csomh / source-git / rpm

Forked from source-git/rpm 4 years ago
Clone
2ff057
/** \ingroup header
2ff057
 * \file lib/headerfmt.c
2ff057
 */
2ff057
2ff057
#include "system.h"
2ff057
2ff057
#include <stdarg.h>
2ff057
#include <rpm/header.h>
2ff057
#include <rpm/rpmtag.h>
2ff057
#include <rpm/rpmstring.h>
2ff057
#include <rpm/rpmpgp.h>
2ff057
#include "lib/misc.h"		/* format function protos */
2ff057
2ff057
#include "debug.h"
2ff057
2ff057
#define PARSER_BEGIN 	0
2ff057
#define PARSER_IN_ARRAY 1
2ff057
#define PARSER_IN_EXPR  2
2ff057
2ff057
/** \ingroup header
2ff057
 */
2ff057
typedef struct sprintfTag_s * sprintfTag;
2ff057
struct sprintfTag_s {
2ff057
    headerFmt fmt;
2ff057
    rpmTagVal tag;
2ff057
    int justOne;
2ff057
    char * format;
2ff057
    char * type;
2ff057
};
2ff057
2ff057
typedef enum {
2ff057
    PTOK_NONE = 0,
2ff057
    PTOK_TAG,
2ff057
    PTOK_ARRAY,
2ff057
    PTOK_STRING,
2ff057
    PTOK_COND
2ff057
} ptokType;
2ff057
2ff057
/** \ingroup header
2ff057
 */
2ff057
typedef struct sprintfToken_s * sprintfToken;
2ff057
struct sprintfToken_s {
2ff057
    ptokType type;
2ff057
    union {
2ff057
	struct sprintfTag_s tag;	/*!< PTOK_TAG */
2ff057
	struct {
2ff057
	    sprintfToken format;
2ff057
	    int i;
2ff057
	    int numTokens;
2ff057
	} array;			/*!< PTOK_ARRAY */
2ff057
	struct {
2ff057
	    char * string;
2ff057
	    int len;
2ff057
	} string;			/*!< PTOK_STRING */
2ff057
	struct {
2ff057
	    sprintfToken ifFormat;
2ff057
	    int numIfTokens;
2ff057
	    sprintfToken elseFormat;
2ff057
	    int numElseTokens;
2ff057
	    struct sprintfTag_s tag;
2ff057
	} cond;				/*!< PTOK_COND */
2ff057
    } u;
2ff057
};
2ff057
2ff057
#define HASHTYPE tagCache
2ff057
#define HTKEYTYPE rpmTagVal
2ff057
#define HTDATATYPE rpmtd
2ff057
#include "lib/rpmhash.H"
2ff057
#include "lib/rpmhash.C"
2ff057
#undef HASHTYPE
2ff057
#undef HTKEYTYPE
2ff057
#undef HTDATATYPE
2ff057
2ff057
/**
2ff057
 */
2ff057
typedef struct headerSprintfArgs_s {
2ff057
    Header h;
2ff057
    char * fmt;
2ff057
    const char * errmsg;
2ff057
    tagCache cache;
2ff057
    sprintfToken format;
2ff057
    HeaderIterator hi;
2ff057
    char * val;
2ff057
    size_t vallen;
2ff057
    size_t alloced;
2ff057
    int numTokens;
2ff057
    int i;
2ff057
    headerGetFlags hgflags;
2ff057
} * headerSprintfArgs;
2ff057
2ff057
2ff057
static char escapedChar(const char ch)	
2ff057
{
2ff057
    switch (ch) {
2ff057
    case 'a': 	return '\a';
2ff057
    case 'b': 	return '\b';
2ff057
    case 'f': 	return '\f';
2ff057
    case 'n': 	return '\n';
2ff057
    case 'r': 	return '\r';
2ff057
    case 't': 	return '\t';
2ff057
    case 'v': 	return '\v';
2ff057
    default:	return ch;
2ff057
    }
2ff057
}
2ff057
2ff057
/**
2ff057
 * Destroy headerSprintf format array.
2ff057
 * @param format	sprintf format array
2ff057
 * @param num		number of elements
2ff057
 * @return		NULL always
2ff057
 */
2ff057
static sprintfToken
2ff057
freeFormat( sprintfToken format, int num)
2ff057
{
2ff057
    int i;
2ff057
2ff057
    if (format == NULL) return NULL;
2ff057
2ff057
    for (i = 0; i < num; i++) {
2ff057
	switch (format[i].type) {
2ff057
	case PTOK_ARRAY:
2ff057
	    format[i].u.array.format =
2ff057
		freeFormat(format[i].u.array.format,
2ff057
			format[i].u.array.numTokens);
2ff057
	    break;
2ff057
	case PTOK_COND:
2ff057
	    format[i].u.cond.ifFormat =
2ff057
		freeFormat(format[i].u.cond.ifFormat, 
2ff057
			format[i].u.cond.numIfTokens);
2ff057
	    format[i].u.cond.elseFormat =
2ff057
		freeFormat(format[i].u.cond.elseFormat, 
2ff057
			format[i].u.cond.numElseTokens);
2ff057
	    break;
2ff057
	case PTOK_NONE:
2ff057
	case PTOK_TAG:
2ff057
	case PTOK_STRING:
2ff057
	default:
2ff057
	    break;
2ff057
	}
2ff057
    }
2ff057
    free(format);
2ff057
    return NULL;
2ff057
}
2ff057
2ff057
/**
2ff057
 * Initialize an hsa iteration.
2ff057
 * @param hsa		headerSprintf args
2ff057
 */
2ff057
static void hsaInit(headerSprintfArgs hsa)
2ff057
{
2ff057
    sprintfTag tag =
2ff057
	(hsa->format->type == PTOK_TAG
2ff057
	    ? &hsa->format->u.tag :
2ff057
	(hsa->format->type == PTOK_ARRAY
2ff057
	    ? &hsa->format->u.array.format->u.tag :
2ff057
	NULL));
2ff057
2ff057
    hsa->i = 0;
2ff057
    if (tag != NULL && tag->tag == -2)
2ff057
	hsa->hi = headerInitIterator(hsa->h);
2ff057
    /* Normally with bells and whistles enabled, but raw dump on iteration. */
2ff057
    hsa->hgflags = (hsa->hi == NULL) ? HEADERGET_EXT : HEADERGET_RAW;
2ff057
}
2ff057
2ff057
/**
2ff057
 * Return next hsa iteration item.
2ff057
 * @param hsa		headerSprintf args
2ff057
 * @return		next sprintfToken (or NULL)
2ff057
 */
2ff057
static sprintfToken hsaNext(headerSprintfArgs hsa)
2ff057
{
2ff057
    sprintfToken fmt = NULL;
2ff057
    sprintfTag tag =
2ff057
	(hsa->format->type == PTOK_TAG
2ff057
	    ? &hsa->format->u.tag :
2ff057
	(hsa->format->type == PTOK_ARRAY
2ff057
	    ? &hsa->format->u.array.format->u.tag :
2ff057
	NULL));
2ff057
2ff057
    if (hsa->i >= 0 && hsa->i < hsa->numTokens) {
2ff057
	fmt = hsa->format + hsa->i;
2ff057
	if (hsa->hi == NULL) {
2ff057
	    hsa->i++;
2ff057
	} else {
2ff057
	    tag->tag = headerNextTag(hsa->hi);
2ff057
	    if (tag->tag == RPMTAG_NOT_FOUND)
2ff057
		fmt = NULL;
2ff057
	}
2ff057
    }
2ff057
2ff057
    return fmt;
2ff057
}
2ff057
2ff057
/**
2ff057
 * Finish an hsa iteration.
2ff057
 * @param hsa		headerSprintf args
2ff057
 */
2ff057
static void hsaFini(headerSprintfArgs hsa)
2ff057
{
2ff057
    hsa->hi = headerFreeIterator(hsa->hi);
2ff057
    hsa->i = 0;
2ff057
}
2ff057
2ff057
/**
2ff057
 * Reserve sufficient buffer space for next output value.
2ff057
 * @param hsa		headerSprintf args
2ff057
 * @param need		no. of bytes to reserve
2ff057
 * @return		pointer to reserved space
2ff057
 */
2ff057
static char * hsaReserve(headerSprintfArgs hsa, size_t need)
2ff057
{
2ff057
    if ((hsa->vallen + need) >= hsa->alloced) {
2ff057
	if (hsa->alloced <= need)
2ff057
	    hsa->alloced += need;
2ff057
	hsa->alloced <<= 1;
2ff057
	hsa->val = xrealloc(hsa->val, hsa->alloced+1);	
2ff057
    }
2ff057
    return hsa->val + hsa->vallen;
2ff057
}
2ff057
2ff057
RPM_GNUC_PRINTF(2, 3)
2ff057
static void hsaError(headerSprintfArgs hsa, const char *fmt, ...)
2ff057
{
2ff057
    /* Use thread local static buffer as headerFormat() errmsg arg is const */
2ff057
    static __thread char errbuf[BUFSIZ];
2ff057
2ff057
    if (fmt == NULL) {
2ff057
	hsa->errmsg = NULL;
2ff057
    } else {
2ff057
	va_list ap;
2ff057
2ff057
	va_start(ap, fmt);
2ff057
	vsnprintf(errbuf, sizeof(errbuf), fmt, ap);
2ff057
	va_end(ap);
2ff057
2ff057
	hsa->errmsg = errbuf;
2ff057
    }
2ff057
}
2ff057
2ff057
/**
2ff057
 * Search tags for a name.
2ff057
 * @param hsa		headerSprintf args
2ff057
 * @param token		parsed fields
2ff057
 * @param name		name to find
2ff057
 * @return		0 on success, 1 on not found
2ff057
 */
2ff057
static int findTag(headerSprintfArgs hsa, sprintfToken token, const char * name)
2ff057
{
2ff057
    const char *tagname = name;
2ff057
    sprintfTag stag = (token->type == PTOK_COND
2ff057
	? &token->u.cond.tag : &token->u.tag);
2ff057
2ff057
    stag->fmt = NULL;
2ff057
    stag->tag = RPMTAG_NOT_FOUND;
2ff057
2ff057
    if (!rstreq(tagname, "*")) {
2ff057
	if (rstreqn("RPMTAG_", tagname, sizeof("RPMTAG_")-1)) {
2ff057
	    tagname += sizeof("RPMTAG");
2ff057
	}
2ff057
2ff057
	/* Search tag names. */
2ff057
	stag->tag = rpmTagGetValue(tagname);
2ff057
	if (stag->tag == RPMTAG_NOT_FOUND) return 1;
2ff057
2ff057
    } else stag->tag = -2;
2ff057
2ff057
    /* Search extensions for specific format. */
2ff057
    if (stag->type != NULL)
2ff057
	stag->fmt = rpmHeaderFormatByName(stag->type);
2ff057
2ff057
    return stag->fmt ? 0 : 1;
2ff057
}
2ff057
2ff057
/* forward ref */
2ff057
/**
2ff057
 * Parse an expression.
2ff057
 * @param hsa		headerSprintf args
2ff057
 * @param token		token
2ff057
 * @param str		string
2ff057
 * @param[out] *endPtr
2ff057
 * @return		0 on success
2ff057
 */
2ff057
static int parseExpression(headerSprintfArgs hsa, sprintfToken token,
2ff057
		char * str,char ** endPtr);
2ff057
2ff057
/**
2ff057
 * Parse a headerSprintf term.
2ff057
 * @param hsa		headerSprintf args
2ff057
 * @param str
2ff057
 * @retval *formatPtr
2ff057
 * @retval *numTokensPtr
2ff057
 * @retval *endPtr
2ff057
 * @param state
2ff057
 * @return		0 on success
2ff057
 */
2ff057
static int parseFormat(headerSprintfArgs hsa, char * str,
2ff057
	sprintfToken * formatPtr,int * numTokensPtr,
2ff057
	char ** endPtr, int state)
2ff057
{
2ff057
    char * chptr, * start, * next, * dst;
2ff057
    sprintfToken format;
2ff057
    sprintfToken token;
2ff057
    int numTokens;
2ff057
    int done = 0;
2ff057
2ff057
    /* upper limit on number of individual formats */
2ff057
    numTokens = 0;
2ff057
    if (str != NULL)
2ff057
    for (chptr = str; *chptr != '\0'; chptr++)
2ff057
	if (*chptr == '%' || *chptr == '[') numTokens++;
2ff057
    numTokens = numTokens * 2 + 1;
2ff057
2ff057
    format = xcalloc(numTokens, sizeof(*format));
2ff057
    if (endPtr) *endPtr = NULL;
2ff057
2ff057
    dst = start = str;
2ff057
    numTokens = 0;
2ff057
    token = NULL;
2ff057
    if (start != NULL)
2ff057
    while (*start != '\0') {
2ff057
	switch (*start) {
2ff057
	case '%':
2ff057
	    /* handle %% */
2ff057
	    if (*(start + 1) == '%') {
2ff057
		if (token == NULL || token->type != PTOK_STRING) {
2ff057
		    token = format + numTokens++;
2ff057
		    token->type = PTOK_STRING;
2ff057
		    dst = token->u.string.string = start;
2ff057
		}
2ff057
		start++;
2ff057
		*dst++ = *start++;
2ff057
		break;
2ff057
	    } 
2ff057
2ff057
	    token = format + numTokens++;
2ff057
	    *dst++ = '\0';
2ff057
	    start++;
2ff057
2ff057
	    if (*start == '|') {
2ff057
		char * newEnd;
2ff057
2ff057
		start++;
2ff057
		if (parseExpression(hsa, token, start, &newEnd)) {
2ff057
		    goto errxit;
2ff057
		}
2ff057
		start = newEnd;
2ff057
		break;
2ff057
	    }
2ff057
2ff057
	    token->u.tag.format = start;
2ff057
	    token->u.tag.justOne = 0;
2ff057
2ff057
	    chptr = start;
2ff057
	    while (*chptr && *chptr != '{' && *chptr != '%') {
2ff057
		if (!risdigit(*chptr) && *chptr != '-') {
2ff057
		    hsaError(hsa, _("invalid field width"));
2ff057
		    goto errxit;
2ff057
		}
2ff057
		chptr++;
2ff057
	    }
2ff057
	    if (!*chptr || *chptr == '%') {
2ff057
		hsaError(hsa, _("missing { after %%"));
2ff057
		goto errxit;
2ff057
	    }
2ff057
2ff057
	    *chptr++ = '\0';
2ff057
2ff057
	    while (start < chptr) {
2ff057
		start++;
2ff057
	    }
2ff057
2ff057
	    if (*start == '=') {
2ff057
		token->u.tag.justOne = 1;
2ff057
		start++;
2ff057
	    } else if (*start == '#') {
2ff057
		token->u.tag.justOne = 1;
2ff057
		token->u.tag.type = "arraysize";
2ff057
		start++;
2ff057
	    }
2ff057
2ff057
	    dst = next = start;
2ff057
	    while (*next && *next != '}') next++;
2ff057
	    if (!*next) {
2ff057
		hsaError(hsa, _("missing } after %%{"));
2ff057
		goto errxit;
2ff057
	    }
2ff057
	    *next++ = '\0';
2ff057
2ff057
	    chptr = start;
2ff057
	    while (*chptr && *chptr != ':') chptr++;
2ff057
2ff057
	    if (*chptr != '\0') {
2ff057
		*chptr++ = '\0';
2ff057
		if (!*chptr) {
2ff057
		    hsaError(hsa, _("empty tag format"));
2ff057
		    goto errxit;
2ff057
		}
2ff057
		token->u.tag.type = chptr;
2ff057
	    } 
2ff057
	    /* default to string conversion if no formats found by now */
2ff057
	    if (!token->u.tag.type) {
2ff057
		token->u.tag.type = "string";
2ff057
	    }
2ff057
	    
2ff057
	    if (!*start) {
2ff057
		hsaError(hsa, _("empty tag name"));
2ff057
		goto errxit;
2ff057
	    }
2ff057
2ff057
	    token->type = PTOK_TAG;
2ff057
2ff057
	    if (findTag(hsa, token, start)) {
2ff057
		hsaError(hsa, _("unknown tag: \"%s\""), start);
2ff057
		goto errxit;
2ff057
	    }
2ff057
2ff057
	    start = next;
2ff057
	    break;
2ff057
2ff057
	case '[':
2ff057
	    *dst++ = '\0';
2ff057
	    *start++ = '\0';
2ff057
	    token = format + numTokens++;
2ff057
2ff057
	    if (parseFormat(hsa, start,
2ff057
			    &token->u.array.format,
2ff057
			    &token->u.array.numTokens,
2ff057
			    &start, PARSER_IN_ARRAY)) {
2ff057
		goto errxit;
2ff057
	    }
2ff057
2ff057
	    if (!start) {
2ff057
		hsaError(hsa, _("] expected at end of array"));
2ff057
		goto errxit;
2ff057
	    }
2ff057
2ff057
	    dst = start;
2ff057
2ff057
	    token->type = PTOK_ARRAY;
2ff057
2ff057
	    break;
2ff057
2ff057
	case ']':
2ff057
	    if (state != PARSER_IN_ARRAY) {
2ff057
		hsaError(hsa, _("unexpected ]"));
2ff057
		goto errxit;
2ff057
	    }
2ff057
	    *start++ = '\0';
2ff057
	    if (endPtr) *endPtr = start;
2ff057
	    done = 1;
2ff057
	    break;
2ff057
2ff057
	case '}':
2ff057
	    if (state != PARSER_IN_EXPR) {
2ff057
		hsaError(hsa, _("unexpected }"));
2ff057
		goto errxit;
2ff057
	    }
2ff057
	    *start++ = '\0';
2ff057
	    if (endPtr) *endPtr = start;
2ff057
	    done = 1;
2ff057
	    break;
2ff057
2ff057
	default:
2ff057
	    if (token == NULL || token->type != PTOK_STRING) {
2ff057
		token = format + numTokens++;
2ff057
		token->type = PTOK_STRING;
2ff057
		dst = token->u.string.string = start;
2ff057
	    }
2ff057
2ff057
	    if (*start == '\\') {
2ff057
		start++;
2ff057
		*dst++ = escapedChar(*start++);
2ff057
	    } else {
2ff057
		*dst++ = *start++;
2ff057
	    }
2ff057
	    break;
2ff057
	}
2ff057
	if (done)
2ff057
	    break;
2ff057
    }
2ff057
2ff057
    if (dst != NULL)
2ff057
        *dst = '\0';
2ff057
2ff057
    for (int i = 0; i < numTokens; i++) {
2ff057
	token = format + i;
2ff057
	if (token->type == PTOK_STRING)
2ff057
	    token->u.string.len = strlen(token->u.string.string);
2ff057
    }
2ff057
2ff057
    *numTokensPtr = numTokens;
2ff057
    *formatPtr = format;
2ff057
    return 0;
2ff057
2ff057
errxit:
2ff057
    freeFormat(format, numTokens);
2ff057
    return 1;
2ff057
}
2ff057
2ff057
static int parseExpression(headerSprintfArgs hsa, sprintfToken token,
2ff057
		char * str, char ** endPtr)
2ff057
{
2ff057
    char * chptr;
2ff057
    char * end;
2ff057
2ff057
    hsaError(hsa, NULL);
2ff057
    chptr = str;
2ff057
    while (*chptr && *chptr != '?') chptr++;
2ff057
2ff057
    if (*chptr != '?') {
2ff057
	hsaError(hsa, _("? expected in expression"));
2ff057
	return 1;
2ff057
    }
2ff057
2ff057
    *chptr++ = '\0';;
2ff057
2ff057
    if (*chptr != '{') {
2ff057
	hsaError(hsa, _("{ expected after ? in expression"));
2ff057
	return 1;
2ff057
    }
2ff057
2ff057
    chptr++;
2ff057
2ff057
    if (parseFormat(hsa, chptr, &token->u.cond.ifFormat, 
2ff057
		    &token->u.cond.numIfTokens, &end, PARSER_IN_EXPR)) 
2ff057
	return 1;
2ff057
2ff057
    /* XXX fix segfault on "rpm -q rpm --qf='%|NAME?{%}:{NAME}|\n'"*/
2ff057
    if (!(end && *end)) {
2ff057
	hsaError(hsa, _("} expected in expression"));
2ff057
	token->u.cond.ifFormat =
2ff057
		freeFormat(token->u.cond.ifFormat, token->u.cond.numIfTokens);
2ff057
	return 1;
2ff057
    }
2ff057
2ff057
    chptr = end;
2ff057
    if (*chptr != ':' && *chptr != '|') {
2ff057
	hsaError(hsa, _(": expected following ? subexpression"));
2ff057
	token->u.cond.ifFormat =
2ff057
		freeFormat(token->u.cond.ifFormat, token->u.cond.numIfTokens);
2ff057
	return 1;
2ff057
    }
2ff057
2ff057
    if (*chptr == '|') {
2ff057
	if (parseFormat(hsa, NULL, &token->u.cond.elseFormat, 
2ff057
		&token->u.cond.numElseTokens, &end, PARSER_IN_EXPR))
2ff057
	{
2ff057
	    token->u.cond.ifFormat =
2ff057
		freeFormat(token->u.cond.ifFormat, token->u.cond.numIfTokens);
2ff057
	    return 1;
2ff057
	}
2ff057
    } else {
2ff057
	chptr++;
2ff057
2ff057
	if (*chptr != '{') {
2ff057
	    hsaError(hsa, _("{ expected after : in expression"));
2ff057
	    token->u.cond.ifFormat =
2ff057
		freeFormat(token->u.cond.ifFormat, token->u.cond.numIfTokens);
2ff057
	    return 1;
2ff057
	}
2ff057
2ff057
	chptr++;
2ff057
2ff057
	if (parseFormat(hsa, chptr, &token->u.cond.elseFormat, 
2ff057
			&token->u.cond.numElseTokens, &end, PARSER_IN_EXPR)) 
2ff057
	    return 1;
2ff057
2ff057
	/* XXX fix segfault on "rpm -q rpm --qf='%|NAME?{a}:{%}|{NAME}\n'" */
2ff057
	if (!(end && *end)) {
2ff057
	    hsaError(hsa, _("} expected in expression"));
2ff057
	    token->u.cond.ifFormat =
2ff057
		freeFormat(token->u.cond.ifFormat, token->u.cond.numIfTokens);
2ff057
	    return 1;
2ff057
	}
2ff057
2ff057
	chptr = end;
2ff057
	if (*chptr != '|') {
2ff057
	    hsaError(hsa, _("| expected at end of expression"));
2ff057
	    token->u.cond.ifFormat =
2ff057
		freeFormat(token->u.cond.ifFormat, token->u.cond.numIfTokens);
2ff057
	    token->u.cond.elseFormat =
2ff057
		freeFormat(token->u.cond.elseFormat, token->u.cond.numElseTokens);
2ff057
	    return 1;
2ff057
	}
2ff057
    }
2ff057
	
2ff057
    chptr++;
2ff057
2ff057
    *endPtr = chptr;
2ff057
2ff057
    token->type = PTOK_COND;
2ff057
2ff057
    (void) findTag(hsa, token, str);
2ff057
2ff057
    return 0;
2ff057
}
2ff057
2ff057
static rpmtd getCached(tagCache cache, rpmTagVal tag)
2ff057
{
2ff057
    rpmtd *res = NULL;
2ff057
    return tagCacheGetEntry(cache, tag, &res, NULL, NULL) ? res[0] : NULL;
2ff057
}
2ff057
2ff057
/**
2ff057
 * Do headerGet() just once for given tag, cache results.
2ff057
 * @param hsa		headerSprintf args
2ff057
 * @param tag
2ff057
 * @retval *typeptr
2ff057
 * @retval *data
2ff057
 * @retval *countptr
2ff057
 * @return		1 on success, 0 on failure
2ff057
 */
2ff057
static rpmtd getData(headerSprintfArgs hsa, rpmTagVal tag)
2ff057
{
2ff057
    rpmtd td = NULL;
2ff057
2ff057
    if (!(td = getCached(hsa->cache, tag))) {
2ff057
	td = rpmtdNew();
2ff057
	if (!headerGet(hsa->h, tag, td, hsa->hgflags)) {
2ff057
	    rpmtdFree(td);
2ff057
	    return NULL;
2ff057
	}
2ff057
	tagCacheAddEntry(hsa->cache, tag, td);
2ff057
    }
2ff057
2ff057
    return td;
2ff057
}
2ff057
2ff057
/**
2ff057
 * formatValue
2ff057
 * @param hsa		headerSprintf args
2ff057
 * @param tag
2ff057
 * @param element
2ff057
 * @return		end of formatted string (NULL on error)
2ff057
 */
2ff057
static char * formatValue(headerSprintfArgs hsa, sprintfTag tag, int element)
2ff057
{
2ff057
    char * val = NULL;
2ff057
    size_t need = 0;
2ff057
    char * t, * te;
2ff057
    rpmtd td;
2ff057
2ff057
    if ((td = getData(hsa, tag->tag)) && td->count > element) {
2ff057
	td->ix = element; /* Ick, use iterators instead */
2ff057
	val = rpmHeaderFormatCall(tag->fmt, td);
2ff057
    } else {
2ff057
	val = xstrdup("(none)");
2ff057
    }
2ff057
2ff057
    /* Handle field width + justification formatting if specified */
2ff057
    if (tag->format && *tag->format) {
2ff057
	char *tval = NULL;
2ff057
	/* user string + extra for '%', format char and trailing '\0' */
2ff057
	char fmtbuf[strlen(tag->format) + 3];
2ff057
2ff057
	sprintf(fmtbuf, "%%%ss", tag->format);
2ff057
	rasprintf(&tval, fmtbuf, val);
2ff057
	free(val);
2ff057
	val = tval;
2ff057
    }
2ff057
	
2ff057
    need = strlen(val);
2ff057
2ff057
    if (val && need > 0) {
2ff057
	t = hsaReserve(hsa, need);
2ff057
	te = stpcpy(t, val);
2ff057
	hsa->vallen += (te - t);
2ff057
    }
2ff057
    free(val);
2ff057
2ff057
    return (hsa->val + hsa->vallen);
2ff057
}
2ff057
2ff057
/**
2ff057
 * Format a single headerSprintf item.
2ff057
 * @param hsa		headerSprintf args
2ff057
 * @param token
2ff057
 * @param element
2ff057
 * @return		end of formatted string (NULL on error)
2ff057
 */
2ff057
static char * singleSprintf(headerSprintfArgs hsa, sprintfToken token,
2ff057
		int element)
2ff057
{
2ff057
    char * t, * te;
2ff057
    int i, j, found;
2ff057
    rpm_count_t count, numElements;
2ff057
    sprintfToken spft;
2ff057
    int condNumFormats;
2ff057
    size_t need;
2ff057
2ff057
    /* we assume the token and header have been validated already! */
2ff057
2ff057
    switch (token->type) {
2ff057
    case PTOK_NONE:
2ff057
	break;
2ff057
2ff057
    case PTOK_STRING:
2ff057
	need = token->u.string.len;
2ff057
	if (need == 0) break;
2ff057
	t = hsaReserve(hsa, need);
2ff057
	te = stpcpy(t, token->u.string.string);
2ff057
	hsa->vallen += (te - t);
2ff057
	break;
2ff057
2ff057
    case PTOK_TAG:
2ff057
	t = hsa->val + hsa->vallen;
2ff057
	te = formatValue(hsa, &token->u.tag,
2ff057
			(token->u.tag.justOne ? 0 : element));
2ff057
	if (te == NULL)
2ff057
	    return NULL;
2ff057
	break;
2ff057
2ff057
    case PTOK_COND:
2ff057
	if (getData(hsa, token->u.cond.tag.tag) ||
2ff057
		      headerIsEntry(hsa->h, token->u.cond.tag.tag)) {
2ff057
	    spft = token->u.cond.ifFormat;
2ff057
	    condNumFormats = token->u.cond.numIfTokens;
2ff057
	} else {
2ff057
	    spft = token->u.cond.elseFormat;
2ff057
	    condNumFormats = token->u.cond.numElseTokens;
2ff057
	}
2ff057
2ff057
	need = condNumFormats * 20;
2ff057
	if (spft == NULL || need == 0) break;
2ff057
2ff057
	t = hsaReserve(hsa, need);
2ff057
	for (i = 0; i < condNumFormats; i++, spft++) {
2ff057
	    te = singleSprintf(hsa, spft, element);
2ff057
	    if (te == NULL)
2ff057
		return NULL;
2ff057
	}
2ff057
	break;
2ff057
2ff057
    case PTOK_ARRAY:
2ff057
	numElements = 0;
2ff057
	found = 0;
2ff057
	spft = token->u.array.format;
2ff057
	for (i = 0; i < token->u.array.numTokens; i++, spft++)
2ff057
	{
2ff057
	    rpmtd td = NULL;
2ff057
	    if (spft->type != PTOK_TAG ||
2ff057
		spft->u.tag.justOne) continue;
2ff057
2ff057
	    if (!(td = getData(hsa, spft->u.tag.tag))) {
2ff057
		continue;
2ff057
	    }
2ff057
2ff057
	    found = 1;
2ff057
	    count = rpmtdCount(td);
2ff057
2ff057
	    if (numElements > 0 && count != numElements) {
2ff057
		hsaError(hsa,
2ff057
			_("array iterator used with different sized arrays"));
2ff057
		return NULL;
2ff057
	    }
2ff057
	    if (count > numElements)
2ff057
		numElements = count;
2ff057
	}
2ff057
2ff057
	if (found) {
2ff057
	    int isxml;
2ff057
2ff057
	    need = numElements * token->u.array.numTokens * 10;
2ff057
	    if (need == 0) break;
2ff057
2ff057
	    spft = token->u.array.format;
2ff057
	    isxml = (spft->type == PTOK_TAG && spft->u.tag.type != NULL &&
2ff057
		    rstreq(spft->u.tag.type, "xml"));
2ff057
2ff057
	    if (isxml) {
2ff057
		const char * tagN = rpmTagGetName(spft->u.tag.tag);
2ff057
2ff057
		need = sizeof("  <rpmTag name=\"\">\n") - 1;
2ff057
		if (tagN != NULL)
2ff057
		    need += strlen(tagN);
2ff057
		t = hsaReserve(hsa, need);
2ff057
		te = stpcpy(t, "  
2ff057
		if (tagN != NULL)
2ff057
		    te = stpcpy(te, tagN);
2ff057
		te = stpcpy(te, "\">\n");
2ff057
		hsa->vallen += (te - t);
2ff057
	    }
2ff057
2ff057
	    t = hsaReserve(hsa, need);
2ff057
	    for (j = 0; j < numElements; j++) {
2ff057
		spft = token->u.array.format;
2ff057
		for (i = 0; i < token->u.array.numTokens; i++, spft++) {
2ff057
		    te = singleSprintf(hsa, spft, j);
2ff057
		    if (te == NULL)
2ff057
			return NULL;
2ff057
		}
2ff057
	    }
2ff057
2ff057
	    if (isxml) {
2ff057
		need = sizeof("  </rpmTag>\n") - 1;
2ff057
		t = hsaReserve(hsa, need);
2ff057
		te = stpcpy(t, "  </rpmTag>\n");
2ff057
		hsa->vallen += (te - t);
2ff057
	    }
2ff057
2ff057
	}
2ff057
	break;
2ff057
    }
2ff057
2ff057
    return (hsa->val + hsa->vallen);
2ff057
}
2ff057
2ff057
static int tagCmp(rpmTagVal a, rpmTagVal b)
2ff057
{
2ff057
    return (a != b);
2ff057
}
2ff057
2ff057
static unsigned int tagId(rpmTagVal tag)
2ff057
{
2ff057
    return tag;
2ff057
}
2ff057
2ff057
char * headerFormat(Header h, const char * fmt, errmsg_t * errmsg) 
2ff057
{
2ff057
    struct headerSprintfArgs_s hsa;
2ff057
    sprintfToken nextfmt;
2ff057
    sprintfTag tag;
2ff057
    char * t, * te;
2ff057
    int isxml;
2ff057
    size_t need;
2ff057
 
2ff057
    memset(&hsa, 0, sizeof(hsa));
2ff057
    hsa.h = headerLink(h);
2ff057
    hsa.fmt = xstrdup(fmt);
2ff057
    hsa.errmsg = NULL;
2ff057
2ff057
    if (parseFormat(&hsa, hsa.fmt, &hsa.format, &hsa.numTokens, NULL, PARSER_BEGIN))
2ff057
	goto exit;
2ff057
2ff057
    hsa.cache = tagCacheCreate(128, tagId, tagCmp, NULL, rpmtdFree);
2ff057
    hsa.val = xstrdup("");
2ff057
2ff057
    tag =
2ff057
	(hsa.format->type == PTOK_TAG
2ff057
	    ? &hsa.format->u.tag :
2ff057
	(hsa.format->type == PTOK_ARRAY
2ff057
	    ? &hsa.format->u.array.format->u.tag :
2ff057
	NULL));
2ff057
    isxml = (tag != NULL && tag->tag == -2 && tag->type != NULL && rstreq(tag->type, "xml"));
2ff057
2ff057
    if (isxml) {
2ff057
	need = sizeof("<rpmHeader>\n") - 1;
2ff057
	t = hsaReserve(&hsa, need);
2ff057
	te = stpcpy(t, "<rpmHeader>\n");
2ff057
	hsa.vallen += (te - t);
2ff057
    }
2ff057
2ff057
    hsaInit(&hsa;;
2ff057
    while ((nextfmt = hsaNext(&hsa)) != NULL) {
2ff057
	te = singleSprintf(&hsa, nextfmt, 0);
2ff057
	if (te == NULL) {
2ff057
	    hsa.val = _free(hsa.val);
2ff057
	    break;
2ff057
	}
2ff057
    }
2ff057
    hsaFini(&hsa;;
2ff057
2ff057
    if (isxml) {
2ff057
	need = sizeof("</rpmHeader>\n") - 1;
2ff057
	t = hsaReserve(&hsa, need);
2ff057
	te = stpcpy(t, "</rpmHeader>\n");
2ff057
	hsa.vallen += (te - t);
2ff057
    }
2ff057
2ff057
    if (hsa.val != NULL && hsa.vallen < hsa.alloced)
2ff057
	hsa.val = xrealloc(hsa.val, hsa.vallen+1);	
2ff057
2ff057
    hsa.cache = tagCacheFree(hsa.cache);
2ff057
    hsa.format = freeFormat(hsa.format, hsa.numTokens);
2ff057
2ff057
exit:
2ff057
    if (errmsg)
2ff057
	*errmsg = hsa.errmsg;
2ff057
    hsa.h = headerFree(hsa.h);
2ff057
    hsa.fmt = _free(hsa.fmt);
2ff057
    return hsa.val;
2ff057
}
2ff057