RPM Community Forums

Mailing List Message of <rpm-cvs>

[CVS] RPM: rpm/ CHANGES rpm/python/ rpmmodule.c rpm/rpmdb/ hdrfmt.c hd...

From: Jeff Johnson <jbj@rpm5.org>
Date: Sat 24 Nov 2007 - 22:01:45 CET
Message-Id: <20071124210145.E84F734844C@rpm5.org>
  RPM Package Manager, CVS Repository
  http://rpm5.org/cvs/
  ____________________________________________________________________________

  Server: rpm5.org                         Name:   Jeff Johnson
  Root:   /v/rpm/cvs                       Email:  jbj@rpm5.org
  Module: rpm                              Date:   24-Nov-2007 22:01:45
  Branch: HEAD                             Handle: 2007112421014401

  Modified files:
    rpm                     CHANGES
    rpm/python              rpmmodule.c
    rpm/rpmdb               hdrfmt.c hdrinline.h header.c header.h
                            header_internal.h librpmdb.vers rpmtag.h

  Log:
    - re-add headerSprintf to the ABI, removing wrappers/methods.
    - move headerSprintf goop out of header.c into hdrfmt.c.

  Summary:
    Revision    Changes     Path
    1.1884      +2  -0      rpm/CHANGES
    1.158       +1  -2      rpm/python/rpmmodule.c
    1.41        +1585 -0    rpm/rpmdb/hdrfmt.c
    1.40        +0  -21     rpm/rpmdb/hdrinline.h
    1.133       +0  -1532   rpm/rpmdb/header.c
    1.74        +0  -19     rpm/rpmdb/header.h
    1.36        +0  -57     rpm/rpmdb/header_internal.h
    1.31        +1  -3      rpm/rpmdb/librpmdb.vers
    1.12        +20 -0      rpm/rpmdb/rpmtag.h
  ____________________________________________________________________________

  patch -p0 <<'@@ .'
  Index: rpm/CHANGES
  ============================================================================
  $ cvs diff -u -r1.1883 -r1.1884 CHANGES
  --- rpm/CHANGES	24 Nov 2007 20:11:36 -0000	1.1883
  +++ rpm/CHANGES	24 Nov 2007 21:01:44 -0000	1.1884
  @@ -1,4 +1,6 @@
   5.0a2 -> 5.0a3:
  +    - jbj: re-add headerSprintf to the ABI, removing wrappers/methods.
  +    - jbj: move headerSprintf goop out of header.c into hdrfmt.c.
       - jbj: python: hmmm, add header extensions to dictionary correctly.
       - jbj: terminate on headerTagTable sentinel, don't use rpmTagTableSize.
       - jbj: use NULL default, not rpmTagTable, arg to headerSprintf everywhere.
  @@ .
  patch -p0 <<'@@ .'
  Index: rpm/python/rpmmodule.c
  ============================================================================
  $ cvs diff -u -r1.157 -r1.158 rpmmodule.c
  --- rpm/python/rpmmodule.c	24 Nov 2007 20:11:36 -0000	1.157
  +++ rpm/python/rpmmodule.c	24 Nov 2007 21:01:44 -0000	1.158
  @@ -237,8 +237,7 @@
    */
   void init_rpm(void)
   {
  -    PyObject * d, *o, * tag = NULL, * dict;
  -    int i;
  +    PyObject * d, *o, * dict;
       PyObject * m;
   
   #if Py_TPFLAGS_HAVE_ITER        /* XXX backport to python-1.5.2 */
  @@ .
  patch -p0 <<'@@ .'
  Index: rpm/rpmdb/hdrfmt.c
  ============================================================================
  $ cvs diff -u -r1.40 -r1.41 hdrfmt.c
  --- rpm/rpmdb/hdrfmt.c	24 Nov 2007 20:11:36 -0000	1.40
  +++ rpm/rpmdb/hdrfmt.c	24 Nov 2007 21:01:45 -0000	1.41
  @@ -23,7 +23,249 @@
   
   /*@access pgpDig @*/
   /*@access pgpDigParams @*/
  +/*@access headerSprintfExtension @*/
  +/*@access sprintfTag @*/
  +/*@access sprintfToken @*/
   
  +/**
  + * Convert tag data representation.
  + * @param he		tag container
  + * @param fmt		output radix (NULL or "" assumes %d)
  + * @return		formatted string
  + */
  +static char * intFormat(HE_t he, const char *fmt)
  +	/*@*/
  +{
  +    uint32_t ix = (he->ix > 0 ? he->ix : 0);
  +    uint64_t ival = 0;
  +    const char * istr = NULL;
  +    char * b;
  +    size_t nb = 0;
  +    int xx;
  +
  +    if (fmt == NULL || *fmt == '\0')
  +	fmt = "d";
  +
  +    switch (he->t) {
  +    default:
  +	return xstrdup(_("(not a number)"));
  +	/*@notreached@*/ break;
  +    case RPM_UINT8_TYPE:
  +	ival = (uint64_t) he->p.ui8p[ix];
  +	break;
  +    case RPM_UINT16_TYPE:
  +	ival = (uint64_t) he->p.ui16p[ix];
  +	break;
  +    case RPM_UINT32_TYPE:
  +	ival = (uint64_t) he->p.ui32p[ix];
  +	break;
  +    case RPM_UINT64_TYPE:
  +	ival = he->p.ui64p[ix];
  +	break;
  +    case RPM_STRING_TYPE:
  +	istr = he->p.str;
  +	break;
  +    case RPM_STRING_ARRAY_TYPE:
  +	istr = he->p.argv[ix];
  +	break;
  +    case RPM_BIN_TYPE:
  +	{   static char hex[] = "0123456789abcdef";
  +	    const char * s = he->p.str;
  +	    rpmTagCount c = he->c;
  +	    char * t;
  +
  +	    nb = 2 * c + 1;
  +	    t = b = alloca(nb+1);
  +	    while (c-- > 0) {
  +		unsigned i;
  +		i = (unsigned) *s++;
  +		*t++ = hex[ (i >> 4) & 0xf ];
  +		*t++ = hex[ (i     ) & 0xf ];
  +	    }
  +	    *t = '\0';
  +	}   break;
  +    }
  +
  +    if (istr) {		/* string */
  +	b = (char *)istr;	/* NOCAST */
  +    } else
  +    if (nb == 0) {	/* number */
  +	char myfmt[] = "%llX";
  +	myfmt[3] = ((fmt != NULL && *fmt != '\0') ? *fmt : 'd');
  +	nb = 64;
  +	b = alloca(nb);
  +/*@-formatconst@*/
  +	xx = snprintf(b, nb, myfmt, ival);
  +/*@=formatconst@*/
  +	b[nb-1] = '\0';
  +    }
  +
  +    return xstrdup(b);
  +}
  +
  +/**
  + * Return octal formatted data.
  + * @param he		tag container
  + * @return		formatted string
  + */
  +static char * octFormat(HE_t he)
  +	/*@*/
  +{
  +    return intFormat(he, "o");
  +}
  +
  +/**
  + * Return hex formatted data.
  + * @param he		tag container
  + * @return		formatted string
  + */
  +static char * hexFormat(HE_t he)
  +	/*@*/
  +{
  +    return intFormat(he, "x");
  +}
  +
  +/**
  + * Return decimal formatted data.
  + * @param he		tag container
  + * @return		formatted string
  + */
  +static char * decFormat(HE_t he)
  +	/*@*/
  +{
  +    return intFormat(he, "d");
  +}
  +
  +/**
  + * Return strftime formatted data.
  + * @param he		tag container
  + * @param strftimeFormat strftime(3) format
  + * @return		formatted string
  + */
  +static char * realDateFormat(HE_t he, const char * strftimeFormat)
  +	/*@*/
  +{
  +    rpmTagData data = { .ptr = he->p.ptr };
  +    char * val;
  +
  +    if (he->t != RPM_UINT64_TYPE) {
  +	val = xstrdup(_("(not a number)"));
  +    } else {
  +	struct tm * tstruct;
  +	char buf[50];
  +
  +	/* this is important if sizeof(uint64_t) ! sizeof(time_t) */
  +	{   time_t dateint = data.ui64p[0];
  +	    tstruct = localtime(&dateint);
  +	}
  +	buf[0] = '\0';
  +	if (tstruct)
  +	    (void) strftime(buf, sizeof(buf) - 1, strftimeFormat, tstruct);
  +	buf[sizeof(buf) - 1] = '\0';
  +	val = xstrdup(buf);
  +    }
  +
  +    return val;
  +}
  +
  +/**
  + * Return date formatted data.
  + * @param he		tag container
  + * @return		formatted string
  + */
  +static char * dateFormat(HE_t he)
  +	/*@*/
  +{
  +    return realDateFormat(he, _("%c"));
  +}
  +
  +/**
  + * Return day formatted data.
  + * @param he		tag container
  + * @return		formatted string
  + */
  +static char * dayFormat(HE_t he)
  +	/*@*/
  +{
  +    return realDateFormat(he, _("%a %b %d %Y"));
  +}
  +
  +/**
  + * Return shell escape formatted data.
  + * @param he		tag container
  + * @return		formatted string
  + */
  +static char * shescapeFormat(HE_t he)
  +	/*@*/
  +{
  +    rpmTagData data = { .ptr = he->p.ptr };
  +    char * val;
  +    size_t nb;
  +    int xx;
  +
  +    /* XXX one of these integer types is unnecessary. */
  +    if (he->t == RPM_UINT32_TYPE) {
  +	nb = 20;
  +	val = xmalloc(nb);
  +	xx = snprintf(val, nb, "%u", (unsigned) data.ui32p[0]);
  +	val[nb-1] = '\0';
  +    } else if (he->t == RPM_UINT64_TYPE) {
  +	nb = 40;
  +	val = xmalloc(40);
  +/*@-duplicatequals@*/
  +	xx = snprintf(val, nb, "%llu", (unsigned long long)data.ui64p[0]);
  +/*@=duplicatequals@*/
  +	val[nb-1] = '\0';
  +    } else if (he->t == RPM_STRING_TYPE) {
  +	const char * s = data.str;
  +	char * t;
  +	int c;
  +
  +	nb = strlen(data.str) + 1;
  +	/* XXX count no. of escapes instead. */
  +	t = xmalloc(4 * nb + 3);
  +	*t++ = '\'';
  +	while ((c = (int)*s++) != 0) {
  +	    if (c == (int)'\'') {
  +		*t++ = '\'';
  +		*t++ = '\\';
  +		*t++ = '\'';
  +	    }
  +	    *t++ = (char) c;
  +	}
  +	*t++ = '\'';
  +	*t = '\0';
  +	nb = strlen(t) + 1;
  +	val = xrealloc(t, nb);
  +    } else
  +	val = xstrdup(_("invalid type"));
  +
  +    return val;
  +}
  +
  +/*@-type@*/ /* FIX: cast? */
  +static struct headerSprintfExtension_s _headerDefaultFormats[] = {
  +    { HEADER_EXT_FORMAT, "octal",
  +	{ .fmtFunction = octFormat } },
  +    { HEADER_EXT_FORMAT, "oct",
  +	{ .fmtFunction = octFormat } },
  +    { HEADER_EXT_FORMAT, "hex",
  +	{ .fmtFunction = hexFormat } },
  +    { HEADER_EXT_FORMAT, "decimal",
  +	{ .fmtFunction = decFormat } },
  +    { HEADER_EXT_FORMAT, "dec",
  +	{ .fmtFunction = decFormat } },
  +    { HEADER_EXT_FORMAT, "date",
  +	{ .fmtFunction = dateFormat } },
  +    { HEADER_EXT_FORMAT, "day",
  +	{ .fmtFunction = dayFormat } },
  +    { HEADER_EXT_FORMAT, "shescape",
  +	{ .fmtFunction = shescapeFormat } },
  +    { HEADER_EXT_LAST, NULL, { NULL } }
  +};
  +/*@=type@*/
  +
  +headerSprintfExtension headerDefaultFormats = &_headerDefaultFormats[0];
   /* XXX FIXME: static for now, refactor from manifest.c later. */
   static char * rpmPermsString(int mode)
   	/*@*/
  @@ -1391,6 +1633,8 @@
   
   headerSprintfExtension headerCompoundFormats = &_headerCompoundFormats[0];
   
  +/*====================================================================*/
  +
   void rpmDisplayQueryTags(FILE * fp, headerTagTableEntry _rpmTagTable, headerSprintfExtension _rpmHeaderFormats)
   {
       const struct headerTagTableEntry_s * t;
  @@ -1455,3 +1699,1344 @@
   	fprintf(fp, "%s\n", ext->name + 7);
       }
   }
  +
  +/*====================================================================*/
  +
  +#define PARSER_BEGIN 	0
  +#define PARSER_IN_ARRAY 1
  +#define PARSER_IN_EXPR  2
  +
  +/*@unchecked@*/
  +static int _usehge = 1;		/* XXX Use headerGetExtension? */
  +/*@unchecked@*/
  +int _tagcache = 1;		/* XXX Cache tag data persistently? */
  +
  +/** \ingroup header
  + */
  +typedef /*@abstract@*/ struct sprintfTag_s * sprintfTag;
  +
  +/** \ingroup header
  + */
  +struct sprintfTag_s {
  +    HE_s he;
  +/*@null@*/
  +    headerTagFormatFunction fmt;
  +/*@null@*/
  +    headerTagTagFunction ext;   /*!< NULL if tag element is invalid */
  +    int extNum;
  +    rpmTag tagno;
  +    int justOne;
  +    int arrayCount;
  +/*@kept@*/
  +    char * format;
  +/*@kept@*/ /*@null@*/
  +    char * type;
  +    int pad;
  +};
  +
  +/** \ingroup header
  + */
  +typedef /*@abstract@*/ struct sprintfToken_s * sprintfToken;
  +
  +/** \ingroup header
  + */
  +struct sprintfToken_s {
  +    enum {
  +        PTOK_NONE       = 0,
  +        PTOK_TAG        = 1,
  +        PTOK_ARRAY      = 2,
  +        PTOK_STRING     = 3,
  +        PTOK_COND       = 4
  +    } type;
  +    union {
  +	struct sprintfTag_s tag;	/*!< PTOK_TAG */
  +	struct {
  +	/*@only@*/
  +	    sprintfToken format;
  +	    size_t numTokens;
  +	} array;			/*!< PTOK_ARRAY */
  +	struct {
  +	/*@dependent@*/
  +	    char * string;
  +	    size_t len;
  +	} string;			/*!< PTOK_STRING */
  +	struct {
  +	/*@only@*/ /*@null@*/
  +	    sprintfToken ifFormat;
  +	    size_t numIfTokens;
  +	/*@only@*/ /*@null@*/
  +	    sprintfToken elseFormat;
  +	    size_t numElseTokens;
  +	    struct sprintfTag_s tag;
  +	} cond;				/*!< PTOK_COND */
  +    } u;
  +};
  +
  +/** \ingroup header
  + */
  +typedef struct headerSprintfArgs_s * headerSprintfArgs;
  +
  +/** \ingroup header
  + */
  +struct headerSprintfArgs_s {
  +    Header h;
  +    char * fmt;
  +/*@observer@*/ /*@temp@*/
  +    headerTagTableEntry tags;
  +/*@observer@*/ /*@temp@*/
  +    headerSprintfExtension exts;
  +/*@observer@*/ /*@null@*/
  +    const char * errmsg;
  +    HE_t ec;			/*!< Extension data cache. */
  +    int nec;			/*!< No. of extension cache items. */
  +    sprintfToken format;
  +/*@relnull@*/
  +    HeaderIterator hi;
  +/*@owned@*/
  +    char * val;
  +    size_t vallen;
  +    size_t alloced;
  +    size_t numTokens;
  +    size_t i;
  +};
  +
  +/**
  + */
  +static char escapedChar(const char ch)	/*@*/
  +{
  +    switch (ch) {
  +    case 'a': 	return '\a';
  +    case 'b': 	return '\b';
  +    case 'f': 	return '\f';
  +    case 'n': 	return '\n';
  +    case 'r': 	return '\r';
  +    case 't': 	return '\t';
  +    case 'v': 	return '\v';
  +    default:	return ch;
  +    }
  +}
  +
  +/**
  + * Mark a tag container with headerGetEntry() freeData.
  + * @param he		tag container
  + */
  +/*@relnull@*/
  +static HE_t rpmheMark(/*@returned@*/ /*@null@*/ HE_t he)
  +	/*@modifies he @*/
  +{
  +    /* Set he->freeData as appropriate for headerGetEntry() . */
  +    if (he)
  +    switch (he->t) {
  +    default:
  +	he->freeData = 0;
  +	break;
  +    case RPM_I18NSTRING_TYPE:
  +    case RPM_STRING_ARRAY_TYPE:
  +    case RPM_BIN_TYPE:
  +	he->freeData = 1;
  +	break;
  +    }
  +    return he;
  +}
  +
  +/**
  + * Clean a tag container, free'ing attached malloc's.
  + * @param he		tag container
  + */
  +/*@relnull@*/
  +static HE_t rpmheClean(/*@returned@*/ /*@null@*/ HE_t he)
  +	/*@modifies he @*/
  +{
  +    if (he) {
  +	if (he->freeData && he->p.ptr != NULL)
  +	    he->p.ptr = _free(he->p.ptr);
  +	memset(he, 0, sizeof(*he));
  +    }
  +    return he;
  +}
  +
  +/**
  + * Destroy headerSprintf format array.
  + * @param format	sprintf format array
  + * @param num		number of elements
  + * @return		NULL always
  + */
  +static /*@null@*/ sprintfToken
  +freeFormat( /*@only@*/ /*@null@*/ sprintfToken format, size_t num)
  +	/*@modifies *format @*/
  +{
  +    unsigned i;
  +
  +    if (format == NULL) return NULL;
  +
  +    for (i = 0; i < (unsigned) num; i++) {
  +	switch (format[i].type) {
  +	case PTOK_TAG:
  +	    if (_tagcache)
  +		(void) rpmheClean(&format[i].u.tag.he);
  +	    /*@switchbreak@*/ break;
  +	case PTOK_ARRAY:
  +	    format[i].u.array.format =
  +		freeFormat(format[i].u.array.format,
  +			format[i].u.array.numTokens);
  +	    /*@switchbreak@*/ break;
  +	case PTOK_COND:
  +	    format[i].u.cond.ifFormat =
  +		freeFormat(format[i].u.cond.ifFormat, 
  +			format[i].u.cond.numIfTokens);
  +	    format[i].u.cond.elseFormat =
  +		freeFormat(format[i].u.cond.elseFormat, 
  +			format[i].u.cond.numElseTokens);
  +	    if (_tagcache)
  +		(void) rpmheClean(&format[i].u.cond.tag.he);
  +	    /*@switchbreak@*/ break;
  +	case PTOK_NONE:
  +	case PTOK_STRING:
  +	default:
  +	    /*@switchbreak@*/ break;
  +	}
  +    }
  +    format = _free(format);
  +    return NULL;
  +}
  +
  +/**
  + * Initialize an hsa iteration.
  + * @param hsa		headerSprintf args
  + * @return		headerSprintf args
  + */
  +static headerSprintfArgs hsaInit(/*@returned@*/ headerSprintfArgs hsa)
  +	/*@modifies hsa */
  +{
  +    sprintfTag tag =
  +	(hsa->format->type == PTOK_TAG
  +	    ? &hsa->format->u.tag :
  +	(hsa->format->type == PTOK_ARRAY
  +	    ? &hsa->format->u.array.format->u.tag :
  +	NULL));
  +
  +    if (hsa != NULL) {
  +	hsa->i = 0;
  +	if (tag != NULL && tag->tagno == -2)
  +	    hsa->hi = headerInitIterator(hsa->h);
  +    }
  +/*@-nullret@*/
  +    return hsa;
  +/*@=nullret@*/
  +}
  +
  +/**
  + * Return next hsa iteration item.
  + * @param hsa		headerSprintf args
  + * @return		next sprintfToken (or NULL)
  + */
  +/*@null@*/
  +static sprintfToken hsaNext(/*@returned@*/ headerSprintfArgs hsa)
  +	/*@modifies hsa */
  +{
  +    sprintfToken fmt = NULL;
  +    sprintfTag tag =
  +	(hsa->format->type == PTOK_TAG
  +	    ? &hsa->format->u.tag :
  +	(hsa->format->type == PTOK_ARRAY
  +	    ? &hsa->format->u.array.format->u.tag :
  +	NULL));
  +
  +    if (hsa != NULL && hsa->i < hsa->numTokens) {
  +	fmt = hsa->format + hsa->i;
  +	if (hsa->hi == NULL) {
  +	    hsa->i++;
  +	} else {
  +	    HE_t he = rpmheClean(&tag->he);
  +	    if (!headerNextIterator(hsa->hi, &he->tag, &he->t, &he->p, &he->c))
  +	    {
  +		tag->tagno = 0;
  +		return NULL;
  +	    }
  +	    he = rpmheMark(he);
  +	    he->avail = 1;
  +	    tag->tagno = he->tag;
  +	}
  +    }
  +
  +/*@-dependenttrans -onlytrans@*/
  +    return fmt;
  +/*@=dependenttrans =onlytrans@*/
  +}
  +
  +/**
  + * Finish an hsa iteration.
  + * @param hsa		headerSprintf args
  + * @return		headerSprintf args
  + */
  +static headerSprintfArgs hsaFini(/*@returned@*/ headerSprintfArgs hsa)
  +	/*@modifies hsa */
  +{
  +    if (hsa != NULL) {
  +	hsa->hi = headerFreeIterator(hsa->hi);
  +	hsa->i = 0;
  +    }
  +/*@-nullret@*/
  +    return hsa;
  +/*@=nullret@*/
  +}
  +
  +/**
  + * Reserve sufficient buffer space for next output value.
  + * @param hsa		headerSprintf args
  + * @param need		no. of bytes to reserve
  + * @return		pointer to reserved space
  + */
  +/*@dependent@*/ /*@exposed@*/
  +static char * hsaReserve(headerSprintfArgs hsa, size_t need)
  +	/*@modifies hsa */
  +{
  +    if ((hsa->vallen + need) >= hsa->alloced) {
  +	if (hsa->alloced <= need)
  +	    hsa->alloced += need;
  +	hsa->alloced <<= 1;
  +	hsa->val = xrealloc(hsa->val, hsa->alloced+1);	
  +    }
  +    return hsa->val + hsa->vallen;
  +}
  +
  +/**
  + * Return tag name from value.
  + * @todo bsearch on sorted value table.
  + * @param tbl		tag table
  + * @param val		tag value to find
  + * @retval *typep	tag type (or NULL)
  + * @return		tag name, NULL on not found
  + */
  +/*@observer@*/ /*@null@*/
  +static const char * myTagName(headerTagTableEntry tbl, uint32_t val,
  +		/*@null@*/ uint32_t *typep)
  +	/*@modifies *typep @*/
  +{
  +    static char name[128];
  +    const char * s;
  +    char *t;
  +
  +    for (; tbl->name != NULL; tbl++) {
  +	if (tbl->val == val)
  +	    break;
  +    }
  +    if ((s = tbl->name) == NULL)
  +	return NULL;
  +    s += sizeof("RPMTAG_") - 1;
  +    t = name;
  +    *t++ = *s++;
  +    while (*s != '\0')
  +	*t++ = (char)xtolower((int)*s++);
  +    *t = '\0';
  +    if (typep)
  +	*typep = tbl->type;
  +    return name;
  +}
  +
  +/**
  + * Return tag value from name.
  + * @todo bsearch on sorted name table.
  + * @param tbl		tag table
  + * @param name		tag name to find
  + * @return		tag value, 0 on not found
  + */
  +static uint32_t myTagValue(headerTagTableEntry tbl, const char * name)
  +	/*@*/
  +{
  +    for (; tbl->name != NULL; tbl++) {
  +	if (!xstrcasecmp(tbl->name, name))
  +	    return tbl->val;
  +    }
  +    return 0;
  +}
  +
  +/**
  + * Search extensions and tags for a name.
  + * @param hsa		headerSprintf args
  + * @param token		parsed fields
  + * @param name		name to find
  + * @return		0 on success, 1 on not found
  + */
  +static int findTag(headerSprintfArgs hsa, sprintfToken token, const char * name)
  +	/*@modifies token @*/
  +{
  +    headerSprintfExtension exts = hsa->exts;
  +    headerSprintfExtension ext;
  +    sprintfTag stag = (token->type == PTOK_COND
  +	? &token->u.cond.tag : &token->u.tag);
  +    int extNum;
  +
  +    stag->fmt = NULL;
  +    stag->ext = NULL;
  +    stag->extNum = 0;
  +    stag->tagno = -1;
  +
  +    if (!strcmp(name, "*")) {
  +	stag->tagno = -2;
  +	goto bingo;
  +    }
  +
  +    if (strncmp("RPMTAG_", name, sizeof("RPMTAG_")-1)) {
  +	char * t = alloca(strlen(name) + sizeof("RPMTAG_"));
  +	(void) stpcpy( stpcpy(t, "RPMTAG_"), name);
  +	name = t;
  +    }
  +
  +    /* Search extensions for specific tag override. */
  +    for (ext = exts, extNum = 0; ext != NULL && ext->type != HEADER_EXT_LAST;
  +	ext = (ext->type == HEADER_EXT_MORE ? *ext->u.more : ext+1), extNum++)
  +    {
  +	if (ext->name == NULL || ext->type != HEADER_EXT_TAG)
  +	    continue;
  +	if (!xstrcasecmp(ext->name, name)) {
  +	    stag->ext = ext->u.tagFunction;
  +	    stag->extNum = extNum;
  +	    goto bingo;
  +	}
  +    }
  +
  +    /* Search tag names. */
  +    stag->tagno = myTagValue(hsa->tags, name);
  +    if (stag->tagno != 0)
  +	goto bingo;
  +
  +    return 1;
  +
  +bingo:
  +    /* Search extensions for specific format. */
  +    if (stag->type != NULL)
  +    for (ext = exts; ext != NULL && ext->type != HEADER_EXT_LAST;
  +	    ext = (ext->type == HEADER_EXT_MORE ? *ext->u.more : ext+1))
  +    {
  +	if (ext->name == NULL || ext->type != HEADER_EXT_FORMAT)
  +	    continue;
  +	if (!strcmp(ext->name, stag->type)) {
  +	    stag->fmt = ext->u.fmtFunction;
  +	    break;
  +	}
  +    }
  +    return 0;
  +}
  +
  +/* forward ref */
  +/**
  + * Parse a headerSprintf expression.
  + * @param hsa		headerSprintf args
  + * @param token
  + * @param str
  + * @retval *endPtr
  + * @return		0 on success
  + */
  +static int parseExpression(headerSprintfArgs hsa, sprintfToken token,
  +		char * str, /*@out@*/char ** endPtr)
  +	/*@modifies hsa, str, token, *endPtr @*/
  +	/*@requires maxSet(endPtr) >= 0 @*/;
  +
  +/**
  + * Parse a headerSprintf term.
  + * @param hsa		headerSprintf args
  + * @param str
  + * @retval *formatPtr
  + * @retval *numTokensPtr
  + * @retval *endPtr
  + * @param state
  + * @return		0 on success
  + */
  +static int parseFormat(headerSprintfArgs hsa, /*@null@*/ char * str,
  +		/*@out@*/ sprintfToken * formatPtr,
  +		/*@out@*/ size_t * numTokensPtr,
  +		/*@null@*/ /*@out@*/ char ** endPtr, int state)
  +	/*@modifies hsa, str, *formatPtr, *numTokensPtr, *endPtr @*/
  +	/*@requires maxSet(formatPtr) >= 0 /\ maxSet(numTokensPtr) >= 0
  +		/\ maxSet(endPtr) >= 0 @*/
  +{
  +    char * chptr, * start, * next, * dst;
  +    sprintfToken format;
  +    sprintfToken token;
  +    size_t numTokens;
  +    unsigned i;
  +    int done = 0;
  +
  +    /* upper limit on number of individual formats */
  +    numTokens = 0;
  +    if (str != NULL)
  +    for (chptr = str; *chptr != '\0'; chptr++)
  +	if (*chptr == '%') numTokens++;
  +    numTokens = numTokens * 2 + 1;
  +
  +    format = xcalloc(numTokens, sizeof(*format));
  +    if (endPtr) *endPtr = NULL;
  +
  +/*@-infloops@*/ /* LCL: can't detect done termination */
  +    dst = start = str;
  +    numTokens = 0;
  +    token = NULL;
  +    if (start != NULL)
  +    while (*start != '\0') {
  +	switch (*start) {
  +	case '%':
  +	    /* handle %% */
  +	    if (*(start + 1) == '%') {
  +		if (token == NULL || token->type != PTOK_STRING) {
  +		    token = format + numTokens++;
  +		    token->type = PTOK_STRING;
  +		    /*@-temptrans -assignexpose@*/
  +		    dst = token->u.string.string = start;
  +		    /*@=temptrans =assignexpose@*/
  +		}
  +		start++;
  +		*dst++ = *start++;
  +		/*@switchbreak@*/ break;
  +	    } 
  +
  +	    token = format + numTokens++;
  +	    *dst++ = '\0';
  +	    start++;
  +
  +	    if (*start == '|') {
  +		char * newEnd;
  +
  +		start++;
  +		if (parseExpression(hsa, token, start, &newEnd))
  +		{
  +		    format = freeFormat(format, numTokens);
  +		    return 1;
  +		}
  +		start = newEnd;
  +		/*@switchbreak@*/ break;
  +	    }
  +
  +	    /*@-assignexpose@*/
  +	    token->u.tag.format = start;
  +	    /*@=assignexpose@*/
  +	    token->u.tag.pad = 0;
  +	    token->u.tag.justOne = 0;
  +	    token->u.tag.arrayCount = 0;
  +
  +	    chptr = start;
  +	    while (*chptr && *chptr != '{' && *chptr != '%') chptr++;
  +	    if (!*chptr || *chptr == '%') {
  +		hsa->errmsg = _("missing { after %");
  +		format = freeFormat(format, numTokens);
  +		return 1;
  +	    }
  +
  +	    *chptr++ = '\0';
  +
  +	    while (start < chptr) {
  +		if (xisdigit((int)*start)) {
  +		    i = strtoul(start, &start, 10);
  +		    token->u.tag.pad += i;
  +		    start = chptr;
  +		    /*@innerbreak@*/ break;
  +		} else {
  +		    start++;
  +		}
  +	    }
  +
  +	    if (*start == '=') {
  +		token->u.tag.justOne = 1;
  +		start++;
  +	    } else if (*start == '#') {
  +		token->u.tag.justOne = 1;
  +		token->u.tag.arrayCount = 1;
  +		start++;
  +	    }
  +
  +	    next = start;
  +	    while (*next && *next != '}') next++;
  +	    if (!*next) {
  +		hsa->errmsg = _("missing } after %{");
  +		format = freeFormat(format, numTokens);
  +		return 1;
  +	    }
  +	    *next++ = '\0';
  +
  +	    chptr = start;
  +	    while (*chptr && *chptr != ':') chptr++;
  +
  +	    if (*chptr != '\0') {
  +		*chptr++ = '\0';
  +		if (!*chptr) {
  +		    hsa->errmsg = _("empty tag format");
  +		    format = freeFormat(format, numTokens);
  +		    return 1;
  +		}
  +		/*@-assignexpose@*/
  +		token->u.tag.type = chptr;
  +		/*@=assignexpose@*/
  +	    } else {
  +		token->u.tag.type = NULL;
  +	    }
  +	    
  +	    if (!*start) {
  +		hsa->errmsg = _("empty tag name");
  +		format = freeFormat(format, numTokens);
  +		return 1;
  +	    }
  +
  +	    i = 0;
  +	    token->type = PTOK_TAG;
  +
  +	    if (findTag(hsa, token, start)) {
  +		hsa->errmsg = _("unknown tag");
  +		format = freeFormat(format, numTokens);
  +		return 1;
  +	    }
  +
  +	    dst = start = next;
  +	    /*@switchbreak@*/ break;
  +
  +	case '[':
  +	    *start++ = '\0';
  +	    token = format + numTokens++;
  +
  +	    if (parseFormat(hsa, start,
  +			    &token->u.array.format,
  +			    &token->u.array.numTokens,
  +			    &start, PARSER_IN_ARRAY))
  +	    {
  +		format = freeFormat(format, numTokens);
  +		return 1;
  +	    }
  +
  +	    if (!start) {
  +		hsa->errmsg = _("] expected at end of array");
  +		format = freeFormat(format, numTokens);
  +		return 1;
  +	    }
  +
  +	    dst = start;
  +
  +	    token->type = PTOK_ARRAY;
  +
  +	    /*@switchbreak@*/ break;
  +
  +	case ']':
  +	    if (state != PARSER_IN_ARRAY) {
  +		hsa->errmsg = _("unexpected ]");
  +		format = freeFormat(format, numTokens);
  +		return 1;
  +	    }
  +	    *start++ = '\0';
  +	    if (endPtr) *endPtr = start;
  +	    done = 1;
  +	    /*@switchbreak@*/ break;
  +
  +	case '}':
  +	    if (state != PARSER_IN_EXPR) {
  +		hsa->errmsg = _("unexpected }");
  +		format = freeFormat(format, numTokens);
  +		return 1;
  +	    }
  +	    *start++ = '\0';
  +	    if (endPtr) *endPtr = start;
  +	    done = 1;
  +	    /*@switchbreak@*/ break;
  +
  +	default:
  +	    if (token == NULL || token->type != PTOK_STRING) {
  +		token = format + numTokens++;
  +		token->type = PTOK_STRING;
  +		/*@-temptrans -assignexpose@*/
  +		dst = token->u.string.string = start;
  +		/*@=temptrans =assignexpose@*/
  +	    }
  +
  +	    if (*start == '\\') {
  +		start++;
  +		*dst++ = escapedChar(*start++);
  +	    } else {
  +		*dst++ = *start++;
  +	    }
  +	    /*@switchbreak@*/ break;
  +	}
  +	if (done)
  +	    break;
  +    }
  +/*@=infloops@*/
  +
  +    if (dst != NULL)
  +        *dst = '\0';
  +
  +    for (i = 0; i < (unsigned) numTokens; i++) {
  +	token = format + i;
  +	switch(token->type) {
  +	default:
  +	    /*@switchbreak@*/ break;
  +	case PTOK_STRING:
  +	    token->u.string.len = strlen(token->u.string.string);
  +	    /*@switchbreak@*/ break;
  +	}
  +    }
  +
  +    if (numTokensPtr != NULL)
  +	*numTokensPtr = numTokens;
  +    if (formatPtr != NULL)
  +	*formatPtr = format;
  +
  +    return 0;
  +}
  +
  +static int parseExpression(headerSprintfArgs hsa, sprintfToken token,
  +		char * str, /*@out@*/ char ** endPtr)
  +{
  +    char * chptr;
  +    char * end;
  +
  +    hsa->errmsg = NULL;
  +    chptr = str;
  +    while (*chptr && *chptr != '?') chptr++;
  +
  +    if (*chptr != '?') {
  +	hsa->errmsg = _("? expected in expression");
  +	return 1;
  +    }
  +
  +    *chptr++ = '\0';
  +
  +    if (*chptr != '{') {
  +	hsa->errmsg = _("{ expected after ? in expression");
  +	return 1;
  +    }
  +
  +    chptr++;
  +
  +    if (parseFormat(hsa, chptr, &token->u.cond.ifFormat, 
  +		    &token->u.cond.numIfTokens, &end, PARSER_IN_EXPR)) 
  +	return 1;
  +
  +    /* XXX fix segfault on "rpm -q rpm --qf='%|NAME?{%}:{NAME}|\n'"*/
  +    if (!(end && *end)) {
  +	hsa->errmsg = _("} expected in expression");
  +	token->u.cond.ifFormat =
  +		freeFormat(token->u.cond.ifFormat, token->u.cond.numIfTokens);
  +	return 1;
  +    }
  +
  +    chptr = end;
  +    if (*chptr != ':' && *chptr != '|') {
  +	hsa->errmsg = _(": expected following ? subexpression");
  +	token->u.cond.ifFormat =
  +		freeFormat(token->u.cond.ifFormat, token->u.cond.numIfTokens);
  +	return 1;
  +    }
  +
  +    if (*chptr == '|') {
  +	if (parseFormat(hsa, NULL, &token->u.cond.elseFormat, 
  +		&token->u.cond.numElseTokens, &end, PARSER_IN_EXPR))
  +	{
  +	    token->u.cond.ifFormat =
  +		freeFormat(token->u.cond.ifFormat, token->u.cond.numIfTokens);
  +	    return 1;
  +	}
  +    } else {
  +	chptr++;
  +
  +	if (*chptr != '{') {
  +	    hsa->errmsg = _("{ expected after : in expression");
  +	    token->u.cond.ifFormat =
  +		freeFormat(token->u.cond.ifFormat, token->u.cond.numIfTokens);
  +	    return 1;
  +	}
  +
  +	chptr++;
  +
  +	if (parseFormat(hsa, chptr, &token->u.cond.elseFormat, 
  +			&token->u.cond.numElseTokens, &end, PARSER_IN_EXPR)) 
  +	    return 1;
  +
  +	/* XXX fix segfault on "rpm -q rpm --qf='%|NAME?{a}:{%}|{NAME}\n'" */
  +	if (!(end && *end)) {
  +	    hsa->errmsg = _("} expected in expression");
  +	    token->u.cond.ifFormat =
  +		freeFormat(token->u.cond.ifFormat, token->u.cond.numIfTokens);
  +	    return 1;
  +	}
  +
  +	chptr = end;
  +	if (*chptr != '|') {
  +	    hsa->errmsg = _("| expected at end of expression");
  +	    token->u.cond.ifFormat =
  +		freeFormat(token->u.cond.ifFormat, token->u.cond.numIfTokens);
  +	    token->u.cond.elseFormat =
  +		freeFormat(token->u.cond.elseFormat, token->u.cond.numElseTokens);
  +	    return 1;
  +	}
  +    }
  +	
  +    chptr++;
  +
  +    *endPtr = chptr;
  +
  +    token->type = PTOK_COND;
  +
  +    (void) findTag(hsa, token, str);
  +
  +    return 0;
  +}
  +
  +/**
  + * Call a header extension only once, saving results.
  + * @param hsa		headerSprintf args
  + * @param fn		function
  + * @retval he		tag container
  + * @retval ec		extension cache
  + * @return		0 on success, 1 on failure
  + */
  +static int getExtension(headerSprintfArgs hsa, headerTagTagFunction fn,
  +		HE_t he, HE_t ec)
  +	/*@modifies he, ec @*/
  +{
  +    int rc = 0;
  +    if (!ec->avail) {
  +	he = rpmheClean(he);
  +	rc = fn(hsa->h, he);
  +	*ec = *he;	/* structure copy. */
  +	if (!rc)
  +	    ec->avail = 1;
  +    } else
  +	*he = *ec;	/* structure copy. */
  +    he->freeData = 0;
  +    return rc;
  +}
  +
  +/**
  + * Format a single item's value.
  + * @param hsa		headerSprintf args
  + * @param tag		tag
  + * @param element	element index
  + * @return		end of formatted string (NULL on error)
  + */
  +/*@observer@*/ /*@null@*/
  +static char * formatValue(headerSprintfArgs hsa, sprintfTag tag,
  +		uint32_t element)
  +	/*@globals headerCompoundFormats @*/
  +	/*@modifies hsa, tag @*/
  +{
  +    HE_t vhe = memset(alloca(sizeof(*vhe)), 0, sizeof(*vhe));
  +    HE_t he = &tag->he;
  +    char * val = NULL;
  +    size_t need = 0;
  +    char * t, * te;
  +    uint64_t ival = 0;
  +    rpmTagCount countBuf;
  +    int xx;
  +
  +    if (!he->avail) {
  +	if (tag->ext) {
  +	    xx = getExtension(hsa, tag->ext, he, hsa->ec + tag->extNum);
  +	} else {
  +	    he->tag = tag->tagno;	/* XXX necessary? */
  +	    if (_usehge) {
  +		xx = headerGetExtension(hsa->h, he, 0);
  +		if (xx)		/* XXX 1 on success */
  +		    he->freeData = 1;
  +	    } else {
  +		xx = headerGetEntry(hsa->h, he->tag, &he->t, &he->p, &he->c);
  +		if (xx)		/* XXX 1 on success */
  +		    he = rpmheMark(he);
  +	    }
  +	    xx = (xx == 0);	/* XXX invert headerGetEntry return. */
  +	}
  +	if (xx) {
  +	    (void) rpmheClean(he);
  +	    he->t = RPM_STRING_TYPE;	
  +	    he->p.str = xstrdup("(none)");
  +	    he->c = 1;
  +	}
  +	he->avail = 1;
  +    }
  +
  +    if (tag->arrayCount) {
  +	countBuf = he->c;
  +	he = rpmheClean(he);
  +	he->t = RPM_UINT32_TYPE;
  +	he->p.ui32p = &countBuf;
  +	he->c = 1;
  +	he->freeData = 0;
  +    }
  +
  +    if (he->p.ptr)
  +    switch (he->t) {
  +    default:
  +	val = xstrdup("(unknown type)");
  +	need = strlen(val) + 1;
  +	goto exit;
  +	/*@notreached@*/ break;
  +    case RPM_I18NSTRING_TYPE:
  +    case RPM_STRING_ARRAY_TYPE:
  +	vhe->t = RPM_STRING_TYPE;
  +	vhe->p.str = he->p.argv[element];
  +	vhe->c = he->c;
  +	/* XXX TODO: force array representation? */
  +	vhe->ix = (he->c > 1 ? 0 : -1);
  +	break;
  +    case RPM_STRING_TYPE:
  +	vhe->p.str = he->p.str;
  +	vhe->t = RPM_STRING_TYPE;
  +	vhe->c = he->c;
  +	vhe->ix = -1;
  +	break;
  +    case RPM_UINT8_TYPE:
  +    case RPM_UINT16_TYPE:
  +    case RPM_UINT32_TYPE:
  +    case RPM_UINT64_TYPE:
  +	switch (he->t) {
  +	default:
  +assert(0);	/* XXX keep gcc quiet. */
  +	    /*@innerbreak@*/ break;
  +	case RPM_UINT8_TYPE:
  +	    ival = he->p.ui8p[element];
  +	    /*@innerbreak@*/ break;
  +	case RPM_UINT16_TYPE:
  +	    ival = he->p.ui16p[element];	/* XXX note unsigned. */
  +	    /*@innerbreak@*/ break;
  +	case RPM_UINT32_TYPE:
  +	    ival = he->p.ui32p[element];
  +	    /*@innerbreak@*/ break;
  +	case RPM_UINT64_TYPE:
  +	    ival = he->p.ui64p[element];
  +	    /*@innerbreak@*/ break;
  +	}
  +	vhe->t = RPM_UINT64_TYPE;
  +	vhe->p.ui64p = &ival;
  +	vhe->c = he->c;
  +	/* XXX TODO: force array representation? */
  +	vhe->ix = (he->c > 1 ? 0 : -1);
  +	break;
  +
  +    case RPM_BIN_TYPE:
  +	vhe->t = RPM_BIN_TYPE;
  +	vhe->p.ptr = he->p.ptr;
  +	vhe->c = he->c;
  +	vhe->ix = -1;
  +	break;
  +    }
  +
  +/*@-compmempass@*/	/* vhe->p.ui64p is stack, not owned */
  +    if (tag->fmt)
  +	val = tag->fmt(vhe);
  +    else
  +	val = intFormat(vhe, NULL);
  +/*@=compmempass@*/
  +assert(val != NULL);
  +    if (val)
  +	need = strlen(val) + 1;
  +
  +exit:
  +/*@-compmempass@*/	/* he->p.ptr is dependent, not owned @*/
  +    if (!_tagcache)
  +	he = rpmheClean(he);
  +/*@=compmempass@*/
  +
  +    if (val && need > 0) {
  +	if (tag->format && *tag->format && tag->pad) {
  +	    size_t nb;
  +	    nb = strlen(tag->format) + sizeof("%s");
  +	    t = alloca(nb);
  +	    (void) stpcpy( stpcpy( stpcpy(t, "%"), tag->format), "s");
  +	    nb = tag->pad + strlen(val) + 1;
  +	    te = xmalloc(nb);
  +/*@-formatconst@*/
  +	    (void) snprintf(te, nb, t, val);
  +/*@=formatconst@*/
  +	    te[nb-1] = '\0';
  +	    val = _free(val);
  +	    val = te;
  +	    need += tag->pad;
  +	}
  +	t = hsaReserve(hsa, need);
  +	te = stpcpy(t, val);
  +	hsa->vallen += (te - t);
  +	val = _free(val);
  +    }
  +
  +    return (hsa->val + hsa->vallen);
  +}
  +
  +/**
  + * Format a single headerSprintf item.
  + * @param hsa		headerSprintf args
  + * @param token		item to format
  + * @param element	element index
  + * @return		end of formatted string (NULL on error)
  + */
  +/*@observer@*/
  +static char * singleSprintf(headerSprintfArgs hsa, sprintfToken token,
  +		uint32_t element)
  +	/*@globals headerCompoundFormats @*/
  +	/*@modifies hsa, token @*/
  +{
  +    char numbuf[64];	/* XXX big enuf for "Tag_0x01234567" */
  +    char * t, * te;
  +    uint32_t i, j;
  +    uint32_t numElements;
  +    sprintfToken spft;
  +    sprintfTag tag = NULL;
  +    HE_t he = NULL;
  +    uint32_t condNumFormats;
  +    size_t need;
  +    int xx;
  +
  +    /* we assume the token and header have been validated already! */
  +
  +    switch (token->type) {
  +    case PTOK_NONE:
  +	break;
  +
  +    case PTOK_STRING:
  +	need = token->u.string.len;
  +	if (need == 0) break;
  +	t = hsaReserve(hsa, need);
  +	te = stpcpy(t, token->u.string.string);
  +	hsa->vallen += (te - t);
  +	break;
  +
  +    case PTOK_TAG:
  +	t = hsa->val + hsa->vallen;
  +/*@-modobserver@*/	/* headerCompoundFormats not modified. */
  +	te = formatValue(hsa, &token->u.tag,
  +			(token->u.tag.justOne ? 0 : element));
  +/*@=modobserver@*/
  +	if (te == NULL)
  +	    return NULL;
  +	break;
  +
  +    case PTOK_COND:
  +	if (token->u.cond.tag.ext
  +	 || headerIsEntry(hsa->h, token->u.cond.tag.tagno))
  +	{
  +	    spft = token->u.cond.ifFormat;
  +	    condNumFormats = token->u.cond.numIfTokens;
  +	} else {
  +	    spft = token->u.cond.elseFormat;
  +	    condNumFormats = token->u.cond.numElseTokens;
  +	}
  +
  +	need = condNumFormats * 20;
  +	if (spft == NULL || need == 0) break;
  +
  +	t = hsaReserve(hsa, need);
  +	for (i = 0; i < condNumFormats; i++, spft++) {
  +/*@-modobserver@*/	/* headerCompoundFormats not modified. */
  +	    te = singleSprintf(hsa, spft, element);
  +/*@=modobserver@*/
  +	    if (te == NULL)
  +		return NULL;
  +	}
  +	break;
  +
  +    case PTOK_ARRAY:
  +	numElements = 0;
  +	spft = token->u.array.format;
  +	for (i = 0; i < token->u.array.numTokens; i++, spft++)
  +	{
  +	    tag = &spft->u.tag;
  +	    if (spft->type != PTOK_TAG || tag->arrayCount || tag->justOne)
  +		continue;
  +	    he = &tag->he;
  +	    if (!he->avail) {
  +		he->tag = tag->tagno;
  +		if (tag->ext)
  +		    xx = getExtension(hsa, tag->ext, he, hsa->ec + tag->extNum);
  +		else {
  +		    if (_usehge) {
  +			xx = headerGetExtension(hsa->h, he, 0);
  +			if (xx)
  +			    he->freeData = 1;
  +		    } else {
  +			xx = headerGetEntry(hsa->h, he->tag, &he->t, &he->p, &he->c);
  +			if (xx)	/* XXX 1 on success */
  +			    he = rpmheMark(he);
  +		    }
  +		    xx = (xx == 0);     /* XXX invert headerGetEntry return. */
  +		}
  +		if (xx) {
  +		    (void) rpmheClean(he);
  +		    continue;
  +		}
  +		he->avail = 1;
  +	    }
  +
  +	    /* Check iteration arrays are same dimension (or scalar). */
  +	    switch (he->t) {
  +	    default:
  +		if (numElements == 0) {
  +		    numElements = he->c;
  +		    /*@switchbreak@*/ break;
  +		}
  +		if (he->c == numElements)
  +		    /*@switchbreak@*/ break;
  +		hsa->errmsg =
  +			_("array iterator used with different sized arrays");
  +		he = rpmheClean(he);
  +		return NULL;
  +		/*@notreached@*/ /*@switchbreak@*/ break;
  +	    case RPM_BIN_TYPE:
  +	    case RPM_STRING_TYPE:
  +		if (numElements == 0)
  +		    numElements = 1;
  +		/*@switchbreak@*/ break;
  +	    }
  +	    if (!_tagcache)
  +		he = rpmheClean(he);
  +	}
  +	spft = token->u.array.format;
  +
  +	if (numElements == 0) {
  +#ifdef	DYING	/* XXX lots of pugly "(none)" lines with --conflicts. */
  +	    need = sizeof("(none)\n") - 1;
  +	    t = hsaReserve(hsa, need);
  +	    te = stpcpy(t, "(none)\n");
  +	    hsa->vallen += (te - t);
  +#endif
  +	} else {
  +	    int isxml;
  +	    int isyaml;
  +
  +	    need = numElements * token->u.array.numTokens;
  +	    if (need == 0) break;
  +
  +	    tag = &spft->u.tag;
  +
  +	    isxml = (spft->type == PTOK_TAG && tag->type != NULL &&
  +		!strcmp(tag->type, "xml"));
  +	    isyaml = (spft->type == PTOK_TAG && tag->type != NULL &&
  +		!strcmp(tag->type, "yaml"));
  +
  +	    if (isxml) {
  +		const char * tagN = myTagName(hsa->tags, tag->tagno, NULL);
  +		/* XXX display "Tag_0x01234567" for unknown tags. */
  +		if (tagN == NULL) {
  +		    (void) snprintf(numbuf, sizeof(numbuf), "Tag_0x%08x",
  +				(unsigned) tag->tagno);
  +		    numbuf[sizeof(numbuf)-1] = '\0';
  +		    tagN = numbuf;
  +		}
  +		need = sizeof("  <rpmTag name=\"\">\n") + strlen(tagN);
  +		te = t = hsaReserve(hsa, need);
  +		te = stpcpy( stpcpy( stpcpy(te, "  <rpmTag name=\""), tagN), "\">\n");
  +		hsa->vallen += (te - t);
  +	    }
  +	    if (isyaml) {
  +		rpmTag tagT = 0;
  +		const char * tagN = myTagName(hsa->tags, tag->tagno, &tagT);
  +		/* XXX display "Tag_0x01234567" for unknown tags. */
  +		if (tagN == NULL) {
  +		    (void) snprintf(numbuf, sizeof(numbuf), "Tag_0x%08x",
  +				(unsigned) tag->tagno);
  +		    numbuf[sizeof(numbuf)-1] = '\0';
  +		    tagN = numbuf;
  +/*@-type@*/
  +		    tagT = numElements > 1
  +			?  RPM_ARRAY_RETURN_TYPE : RPM_SCALAR_RETURN_TYPE;
  +/*@=type@*/
  +		}
  +		need = sizeof("  :     - ") + strlen(tagN);
  +		te = t = hsaReserve(hsa, need);
  +		*te++ = ' ';
  +		*te++ = ' ';
  +		te = stpcpy(te, tagN);
  +		*te++ = ':';
  +/*@-type@*/
  +		*te++ = (((tagT & RPM_MASK_RETURN_TYPE) == RPM_ARRAY_RETURN_TYPE)
  +			? '\n' : ' ');
  +/*@=type@*/
  +		/* XXX Dirnames: in srpms need "    " indent */
  +/*@-type@*/
  +		if (((tagT & RPM_MASK_RETURN_TYPE) == RPM_ARRAY_RETURN_TYPE)
  +		 && numElements == 1)
  +/*@=type@*/
  +		{
  +		    te = stpcpy(te, "    ");
  +		    if (tag->tagno != 1118)
  +			te = stpcpy(te, "- ");
  +		}
  +		*te = '\0';
  +		hsa->vallen += (te - t);
  +	    }
  +
  +	    need = numElements * token->u.array.numTokens * 10;
  +	    t = hsaReserve(hsa, need);
  +	    for (j = 0; j < numElements; j++) {
  +		spft = token->u.array.format;
  +		for (i = 0; i < token->u.array.numTokens; i++, spft++) {
  +/*@-modobserver@*/	/* headerCompoundFormats not modified. */
  +		    te = singleSprintf(hsa, spft, j);
  +/*@=modobserver@*/
  +		    if (te == NULL)
  +			return NULL;
  +		}
  +	    }
  +
  +	    if (isxml) {
  +		need = sizeof("  </rpmTag>\n") - 1;
  +		te = t = hsaReserve(hsa, need);
  +		te = stpcpy(te, "  </rpmTag>\n");
  +		hsa->vallen += (te - t);
  +	    }
  +	    if (isyaml) {
  +#if 0
  +		need = sizeof("\n") - 1;
  +		te = t = hsaReserve(hsa, need);
  +		te = stpcpy(te, "\n");
  +		hsa->vallen += (te - t);
  +#endif
  +	    }
  +
  +	}
  +	break;
  +    }
  +
  +    return (hsa->val + hsa->vallen);
  +}
  +
  +/**
  + * Create an extension cache.
  + * @param exts		headerSprintf extensions
  + * @retval *necp	no. of elements (or NULL)
  + * @return		new extension cache
  + */
  +static /*@only@*/ HE_t
  +rpmecNew(const headerSprintfExtension exts, /*@null@*/ int * necp)
  +	/*@modifies *necp @*/
  +{
  +    headerSprintfExtension ext;
  +    HE_t ec;
  +    int extNum = 0;
  +
  +    if (exts != NULL)
  +    for (ext = exts, extNum = 0; ext != NULL && ext->type != HEADER_EXT_LAST;
  +	ext = (ext->type == HEADER_EXT_MORE ? *ext->u.more : ext+1), extNum++)
  +    {
  +	;
  +    }
  +    if (necp)
  +	*necp = extNum;
  +    ec = xcalloc(extNum+1, sizeof(*ec));	/* XXX +1 unnecessary */
  +    return ec;
  +}
  +
  +/**
  + * Destroy an extension cache.
  + * @param exts		headerSprintf extensions
  + * @param ec		extension cache
  + * @return		NULL always
  + */
  +static /*@null@*/ HE_t
  +rpmecFree(const headerSprintfExtension exts, /*@only@*/ HE_t ec)
  +	/*@modifies ec @*/
  +{
  +    headerSprintfExtension ext;
  +    int extNum;
  +
  +    for (ext = exts, extNum = 0; ext != NULL && ext->type != HEADER_EXT_LAST;
  +	ext = (ext->type == HEADER_EXT_MORE ? *ext->u.more : ext+1), extNum++)
  +    {
  +	(void) rpmheClean(&ec[extNum]);
  +    }
  +
  +    ec = _free(ec);
  +    return NULL;
  +}
  +
  +char * headerSprintf(Header h, const char * fmt,
  +		headerTagTableEntry tags,
  +		headerSprintfExtension exts,
  +		errmsg_t * errmsg)
  +{
  +    headerSprintfArgs hsa = memset(alloca(sizeof(*hsa)), 0, sizeof(*hsa));
  +    sprintfToken nextfmt;
  +    sprintfTag tag;
  +    char * t, * te;
  +    int isxml;
  +    int isyaml;
  +    int need;
  +
  +    /* Set some reasonable defaults */
  +    if (tags == NULL)
  +	tags = rpmTagTable;
  +    /* XXX this loses the extensions in lib/formats.c. */
  +    if (exts == NULL)
  +	exts = headerCompoundFormats;
  + 
  +    hsa->h = headerLink(h);
  +    hsa->fmt = xstrdup(fmt);
  +/*@-dependenttrans@*/
  +    hsa->exts = exts;
  +    hsa->tags = tags;
  +/*@=dependenttrans@*/
  +    hsa->errmsg = NULL;
  +
  +    if (parseFormat(hsa, hsa->fmt, &hsa->format, &hsa->numTokens, NULL, PARSER_BEGIN))
  +	goto exit;
  +
  +    hsa->nec = 0;
  +    hsa->ec = rpmecNew(hsa->exts, &hsa->nec);
  +    hsa->val = xstrdup("");
  +
  +    tag =
  +	(hsa->format->type == PTOK_TAG
  +	    ? &hsa->format->u.tag :
  +	(hsa->format->type == PTOK_ARRAY
  +	    ? &hsa->format->u.array.format->u.tag :
  +	NULL));
  +    isxml = (tag != NULL && tag->tagno == -2 && tag->type != NULL && !strcmp(tag->type, "xml"));
  +    isyaml = (tag != NULL && tag->tagno == -2 && tag->type != NULL && !strcmp(tag->type, "yaml"));
  +
  +    if (isxml) {
  +	need = sizeof("<rpmHeader>\n") - 1;
  +	t = hsaReserve(hsa, need);
  +	te = stpcpy(t, "<rpmHeader>\n");
  +	hsa->vallen += (te - t);
  +    }
  +    if (isyaml) {
  +	need = sizeof("- !!omap\n") - 1;
  +	t = hsaReserve(hsa, need);
  +	te = stpcpy(t, "- !!omap\n");
  +	hsa->vallen += (te - t);
  +    }
  +
  +    hsa = hsaInit(hsa);
  +    while ((nextfmt = hsaNext(hsa)) != NULL) {
  +/*@-modobserver@*/	/* headerCompoundFormats not modified. */
  +	te = singleSprintf(hsa, nextfmt, 0);
  +/*@=modobserver@*/
  +	if (te == NULL) {
  +	    hsa->val = _free(hsa->val);
  +	    break;
  +	}
  +    }
  +    hsa = hsaFini(hsa);
  +
  +    if (isxml) {
  +	need = sizeof("</rpmHeader>\n") - 1;
  +	t = hsaReserve(hsa, need);
  +	te = stpcpy(t, "</rpmHeader>\n");
  +	hsa->vallen += (te - t);
  +    }
  +    if (isyaml) {
  +	need = sizeof("\n") - 1;
  +	t = hsaReserve(hsa, need);
  +	te = stpcpy(t, "\n");
  +	hsa->vallen += (te - t);
  +    }
  +
  +    if (hsa->val != NULL && hsa->vallen < hsa->alloced)
  +	hsa->val = xrealloc(hsa->val, hsa->vallen+1);	
  +
  +    hsa->ec = rpmecFree(hsa->exts, hsa->ec);
  +    hsa->nec = 0;
  +    hsa->format = freeFormat(hsa->format, hsa->numTokens);
  +
  +exit:
  +/*@-dependenttrans -observertrans @*/
  +    if (errmsg)
  +	*errmsg = hsa->errmsg;
  +/*@=dependenttrans =observertrans @*/
  +    hsa->h = headerFree(hsa->h);
  +    hsa->fmt = _free(hsa->fmt);
  +    return hsa->val;
  +}
  @@ .
  patch -p0 <<'@@ .'
  Index: rpm/rpmdb/hdrinline.h
  ============================================================================
  $ cvs diff -u -r1.39 -r1.40 hdrinline.h
  --- rpm/rpmdb/hdrinline.h	24 Nov 2007 18:48:15 -0000	1.39
  +++ rpm/rpmdb/hdrinline.h	24 Nov 2007 21:01:45 -0000	1.40
  @@ -495,27 +495,6 @@
   }
   
   /** \ingroup header
  - * Return formatted output string from header tags.
  - * The returned string must be free()d.
  - *
  - * @param h		header
  - * @param fmt		format to use
  - * @param tags		array of tag name/value/type triples (NULL uses default)
  - * @param exts		formatting extensions chained table (NULL uses default)
  - * @retval errmsg	error message (if any)
  - * @return		formatted output string (malloc'ed)
  - */
  -/*@unused@*/ static inline
  -/*@only@*/ char * headerSprintf(Header h, const char * fmt,
  -		/*@null@*/ headerTagTableEntry tags,
  -		/*@null@*/ headerSprintfExtension exts,
  -		/*@null@*/ /*@out@*/ errmsg_t * errmsg)
  -	/*@modifies *errmsg @*/
  -{
  -    return (h2hv(h)->hdrsprintf) (h, fmt, tags, exts, errmsg);
  -}
  -
  -/** \ingroup header
    * Duplicate tag values from one header into another.
    * @param headerFrom	source header
    * @param headerTo	destination header
  @@ .
  patch -p0 <<'@@ .'
  Index: rpm/rpmdb/header.c
  ============================================================================
  $ cvs diff -u -r1.132 -r1.133 header.c
  --- rpm/rpmdb/header.c	24 Nov 2007 18:48:15 -0000	1.132
  +++ rpm/rpmdb/header.c	24 Nov 2007 21:01:45 -0000	1.133
  @@ -23,28 +23,17 @@
   
   /*@unchecked@*/
   int _newmagic = 0;		/* XXX Change header magic? */
  -/*@unchecked@*/
  -static int _usehge = 1;		/* XXX Use headerGetExtension? */
  -/*@unchecked@*/
  -int _tagcache = 1;		/* XXX Cache tag data persistently? */
   
   /*@access Header @*/
   /*@access HeaderIterator @*/
  -/*@access headerSprintfExtension @*/
   /*@access headerTagTableEntry @*/
   
   /*@access entryInfo @*/
   /*@access indexEntry @*/
   
  -/*@access sprintfTag @*/
  -/*@access sprintfToken @*/
   /*@access HV_t @*/
   /*@access FD_t @*/		/* XXX void * arg headerRead/headerWrite */
   
  -#define PARSER_BEGIN 	0
  -#define PARSER_IN_ARRAY 1
  -#define PARSER_IN_EXPR  2
  -
   /** \ingroup header
    */
   /*@-type@*/
  @@ -2210,106 +2199,6 @@
   }
   
   /**
  - */
  -static char escapedChar(const char ch)	/*@*/
  -{
  -    switch (ch) {
  -    case 'a': 	return '\a';
  -    case 'b': 	return '\b';
  -    case 'f': 	return '\f';
  -    case 'n': 	return '\n';
  -    case 'r': 	return '\r';
  -    case 't': 	return '\t';
  -    case 'v': 	return '\v';
  -    default:	return ch;
  -    }
  -}
  -
  -/**
  - * Mark a tag container with headerGetEntry() freeData.
  - * @param he		tag container
  - */
  -/*@relnull@*/
  -static HE_t rpmheMark(/*@returned@*/ /*@null@*/ HE_t he)
  -	/*@modifies he @*/
  -{
  -    /* Set he->freeData as appropriate for headerGetEntry() . */
  -    if (he)
  -    switch (he->t) {
  -    default:
  -	he->freeData = 0;
  -	break;
  -    case RPM_I18NSTRING_TYPE:
  -    case RPM_STRING_ARRAY_TYPE:
  -    case RPM_BIN_TYPE:
  -	he->freeData = 1;
  -	break;
  -    }
  -    return he;
  -}
  -
  -/**
  - * Clean a tag container, free'ing attached malloc's..
  - * @param he		tag container
  - */
  -/*@relnull@*/
  -static HE_t rpmheClean(/*@returned@*/ /*@null@*/ HE_t he)
  -	/*@modifies he @*/
  -{
  -    if (he) {
  -	if (he->freeData && he->p.ptr != NULL)
  -	    he->p.ptr = _free(he->p.ptr);
  -	memset(he, 0, sizeof(*he));
  -    }
  -    return he;
  -}
  -
  -/**
  - * Destroy headerSprintf format array.
  - * @param format	sprintf format array
  - * @param num		number of elements
  - * @return		NULL always
  - */
  -static /*@null@*/ sprintfToken
  -freeFormat( /*@only@*/ /*@null@*/ sprintfToken format, size_t num)
  -	/*@modifies *format @*/
  -{
  -    unsigned i;
  -
  -    if (format == NULL) return NULL;
  -
  -    for (i = 0; i < (unsigned) num; i++) {
  -	switch (format[i].type) {
  -	case PTOK_TAG:
  -	    if (_tagcache)
  -		(void) rpmheClean(&format[i].u.tag.he);
  -	    /*@switchbreak@*/ break;
  -	case PTOK_ARRAY:
  -	    format[i].u.array.format =
  -		freeFormat(format[i].u.array.format,
  -			format[i].u.array.numTokens);
  -	    /*@switchbreak@*/ break;
  -	case PTOK_COND:
  -	    format[i].u.cond.ifFormat =
  -		freeFormat(format[i].u.cond.ifFormat, 
  -			format[i].u.cond.numIfTokens);
  -	    format[i].u.cond.elseFormat =
  -		freeFormat(format[i].u.cond.elseFormat, 
  -			format[i].u.cond.numElseTokens);
  -	    if (_tagcache)
  -		(void) rpmheClean(&format[i].u.cond.tag.he);
  -	    /*@switchbreak@*/ break;
  -	case PTOK_NONE:
  -	case PTOK_STRING:
  -	default:
  -	    /*@switchbreak@*/ break;
  -	}
  -    }
  -    format = _free(format);
  -    return NULL;
  -}
  -
  -/**
    * Header tag iterator data structure.
    */
   struct headerIterator_s {
  @@ -2427,1426 +2316,6 @@
       return headerReload(nh, HEADER_IMAGE);
   }
   
  -/**
  - */
  -typedef struct headerSprintfArgs_s {
  -    Header h;
  -    char * fmt;
  -/*@observer@*/ /*@temp@*/
  -    headerTagTableEntry tags;
  -/*@observer@*/ /*@temp@*/
  -    headerSprintfExtension exts;
  -/*@observer@*/ /*@null@*/
  -    const char * errmsg;
  -    HE_t ec;			/*!< Extension data cache. */
  -    int nec;			/*!< No. of extension cache items. */
  -    sprintfToken format;
  -/*@relnull@*/
  -    HeaderIterator hi;
  -/*@owned@*/
  -    char * val;
  -    size_t vallen;
  -    size_t alloced;
  -    size_t numTokens;
  -    size_t i;
  -} * headerSprintfArgs;
  -
  -/**
  - * Initialize an hsa iteration.
  - * @param hsa		headerSprintf args
  - * @return		headerSprintf args
  - */
  -static headerSprintfArgs hsaInit(/*@returned@*/ headerSprintfArgs hsa)
  -	/*@modifies hsa */
  -{
  -    sprintfTag tag =
  -	(hsa->format->type == PTOK_TAG
  -	    ? &hsa->format->u.tag :
  -	(hsa->format->type == PTOK_ARRAY
  -	    ? &hsa->format->u.array.format->u.tag :
  -	NULL));
  -
  -    if (hsa != NULL) {
  -	hsa->i = 0;
  -	if (tag != NULL && tag->tagno == -2)
  -	    hsa->hi = headerInitIterator(hsa->h);
  -    }
  -/*@-nullret@*/
  -    return hsa;
  -/*@=nullret@*/
  -}
  -
  -/**
  - * Return next hsa iteration item.
  - * @param hsa		headerSprintf args
  - * @return		next sprintfToken (or NULL)
  - */
  -/*@null@*/
  -static sprintfToken hsaNext(/*@returned@*/ headerSprintfArgs hsa)
  -	/*@modifies hsa */
  -{
  -    sprintfToken fmt = NULL;
  -    sprintfTag tag =
  -	(hsa->format->type == PTOK_TAG
  -	    ? &hsa->format->u.tag :
  -	(hsa->format->type == PTOK_ARRAY
  -	    ? &hsa->format->u.array.format->u.tag :
  -	NULL));
  -
  -    if (hsa != NULL && hsa->i < hsa->numTokens) {
  -	fmt = hsa->format + hsa->i;
  -	if (hsa->hi == NULL) {
  -	    hsa->i++;
  -	} else {
  -	    HE_t he = rpmheClean(&tag->he);
  -	    if (!headerNextIterator(hsa->hi, &he->tag, &he->t, &he->p, &he->c))
  -	    {
  -		tag->tagno = 0;
  -		return NULL;
  -	    }
  -	    he = rpmheMark(he);
  -	    he->avail = 1;
  -	    tag->tagno = he->tag;
  -	}
  -    }
  -
  -/*@-dependenttrans -onlytrans@*/
  -    return fmt;
  -/*@=dependenttrans =onlytrans@*/
  -}
  -
  -/**
  - * Finish an hsa iteration.
  - * @param hsa		headerSprintf args
  - * @return		headerSprintf args
  - */
  -static headerSprintfArgs hsaFini(/*@returned@*/ headerSprintfArgs hsa)
  -	/*@modifies hsa */
  -{
  -    if (hsa != NULL) {
  -	hsa->hi = headerFreeIterator(hsa->hi);
  -	hsa->i = 0;
  -    }
  -/*@-nullret@*/
  -    return hsa;
  -/*@=nullret@*/
  -}
  -
  -/**
  - * Reserve sufficient buffer space for next output value.
  - * @param hsa		headerSprintf args
  - * @param need		no. of bytes to reserve
  - * @return		pointer to reserved space
  - */
  -/*@dependent@*/ /*@exposed@*/
  -static char * hsaReserve(headerSprintfArgs hsa, size_t need)
  -	/*@modifies hsa */
  -{
  -    if ((hsa->vallen + need) >= hsa->alloced) {
  -	if (hsa->alloced <= need)
  -	    hsa->alloced += need;
  -	hsa->alloced <<= 1;
  -	hsa->val = xrealloc(hsa->val, hsa->alloced+1);	
  -    }
  -    return hsa->val + hsa->vallen;
  -}
  -
  -/**
  - * Return tag name from value.
  - * @todo bsearch on sorted value table.
  - * @param tbl		tag table
  - * @param val		tag value to find
  - * @retval *typep	tag type (or NULL)
  - * @return		tag name, NULL on not found
  - */
  -/*@observer@*/ /*@null@*/
  -static const char * myTagName(headerTagTableEntry tbl, uint32_t val,
  -		/*@null@*/ uint32_t *typep)
  -	/*@modifies *typep @*/
  -{
  -    static char name[128];
  -    const char * s;
  -    char *t;
  -
  -    for (; tbl->name != NULL; tbl++) {
  -	if (tbl->val == val)
  -	    break;
  -    }
  -    if ((s = tbl->name) == NULL)
  -	return NULL;
  -    s += sizeof("RPMTAG_") - 1;
  -    t = name;
  -    *t++ = *s++;
  -    while (*s != '\0')
  -	*t++ = (char)xtolower((int)*s++);
  -    *t = '\0';
  -    if (typep)
  -	*typep = tbl->type;
  -    return name;
  -}
  -
  -/**
  - * Return tag value from name.
  - * @todo bsearch on sorted name table.
  - * @param tbl		tag table
  - * @param name		tag name to find
  - * @return		tag value, 0 on not found
  - */
  -static uint32_t myTagValue(headerTagTableEntry tbl, const char * name)
  -	/*@*/
  -{
  -    for (; tbl->name != NULL; tbl++) {
  -	if (!xstrcasecmp(tbl->name, name))
  -	    return tbl->val;
  -    }
  -    return 0;
  -}
  -
  -/**
  - * Search extensions and tags for a name.
  - * @param hsa		headerSprintf args
  - * @param token		parsed fields
  - * @param name		name to find
  - * @return		0 on success, 1 on not found
  - */
  -static int findTag(headerSprintfArgs hsa, sprintfToken token, const char * name)
  -	/*@modifies token @*/
  -{
  -    headerSprintfExtension exts = hsa->exts;
  -    headerSprintfExtension ext;
  -    sprintfTag stag = (token->type == PTOK_COND
  -	? &token->u.cond.tag : &token->u.tag);
  -    int extNum;
  -
  -    stag->fmt = NULL;
  -    stag->ext = NULL;
  -    stag->extNum = 0;
  -    stag->tagno = -1;
  -
  -    if (!strcmp(name, "*")) {
  -	stag->tagno = -2;
  -	goto bingo;
  -    }
  -
  -    if (strncmp("RPMTAG_", name, sizeof("RPMTAG_")-1)) {
  -	char * t = alloca(strlen(name) + sizeof("RPMTAG_"));
  -	(void) stpcpy( stpcpy(t, "RPMTAG_"), name);
  -	name = t;
  -    }
  -
  -    /* Search extensions for specific tag override. */
  -    for (ext = exts, extNum = 0; ext != NULL && ext->type != HEADER_EXT_LAST;
  -	ext = (ext->type == HEADER_EXT_MORE ? *ext->u.more : ext+1), extNum++)
  -    {
  -	if (ext->name == NULL || ext->type != HEADER_EXT_TAG)
  -	    continue;
  -	if (!xstrcasecmp(ext->name, name)) {
  -	    stag->ext = ext->u.tagFunction;
  -	    stag->extNum = extNum;
  -	    goto bingo;
  -	}
  -    }
  -
  -    /* Search tag names. */
  -    stag->tagno = myTagValue(hsa->tags, name);
  -    if (stag->tagno != 0)
  -	goto bingo;
  -
  -    return 1;
  -
  -bingo:
  -    /* Search extensions for specific format. */
  -    if (stag->type != NULL)
  -    for (ext = exts; ext != NULL && ext->type != HEADER_EXT_LAST;
  -	    ext = (ext->type == HEADER_EXT_MORE ? *ext->u.more : ext+1))
  -    {
  -	if (ext->name == NULL || ext->type != HEADER_EXT_FORMAT)
  -	    continue;
  -	if (!strcmp(ext->name, stag->type)) {
  -	    stag->fmt = ext->u.fmtFunction;
  -	    break;
  -	}
  -    }
  -    return 0;
  -}
  -
  -/**
  - * Convert tag data representation.
  - * @param he		tag container
  - * @param fmt		output radix (NULL or "" assumes %d)
  - * @return		formatted string
  - */
  -static char * intFormat(HE_t he, const char *fmt)
  -	/*@*/
  -{
  -    uint32_t ix = (he->ix > 0 ? he->ix : 0);
  -    uint64_t ival = 0;
  -    const char * istr = NULL;
  -    char * b;
  -    size_t nb = 0;
  -    int xx;
  -
  -    if (fmt == NULL || *fmt == '\0')
  -	fmt = "d";
  -
  -    switch (he->t) {
  -    default:
  -	return xstrdup(_("(not a number)"));
  -	/*@notreached@*/ break;
  -    case RPM_UINT8_TYPE:
  -	ival = (uint64_t) he->p.ui8p[ix];
  -	break;
  -    case RPM_UINT16_TYPE:
  -	ival = (uint64_t) he->p.ui16p[ix];
  -	break;
  -    case RPM_UINT32_TYPE:
  -	ival = (uint64_t) he->p.ui32p[ix];
  -	break;
  -    case RPM_UINT64_TYPE:
  -	ival = he->p.ui64p[ix];
  -	break;
  -    case RPM_STRING_TYPE:
  -	istr = he->p.str;
  -	break;
  -    case RPM_STRING_ARRAY_TYPE:
  -	istr = he->p.argv[ix];
  -	break;
  -    case RPM_BIN_TYPE:
  -	{   static char hex[] = "0123456789abcdef";
  -	    const char * s = he->p.str;
  -	    rpmTagCount c = he->c;
  -	    char * t;
  -
  -	    nb = 2 * c + 1;
  -	    t = b = alloca(nb+1);
  -	    while (c-- > 0) {
  -		unsigned i;
  -		i = (unsigned) *s++;
  -		*t++ = hex[ (i >> 4) & 0xf ];
  -		*t++ = hex[ (i     ) & 0xf ];
  -	    }
  -	    *t = '\0';
  -	}   break;
  -    }
  -
  -    if (istr) {		/* string */
  -	b = (char *)istr;	/* NOCAST */
  -    } else
  -    if (nb == 0) {	/* number */
  -	char myfmt[] = "%llX";
  -	myfmt[3] = ((fmt != NULL && *fmt != '\0') ? *fmt : 'd');
  -	nb = 64;
  -	b = alloca(nb);
  -/*@-formatconst@*/
  -	xx = snprintf(b, nb, myfmt, ival);
  -/*@=formatconst@*/
  -	b[nb-1] = '\0';
  -    }
  -
  -    return xstrdup(b);
  -}
  -
  -/**
  - * Return octal formatted data.
  - * @param he		tag container
  - * @return		formatted string
  - */
  -static char * octFormat(HE_t he)
  -	/*@*/
  -{
  -    return intFormat(he, "o");
  -}
  -
  -/**
  - * Return hex formatted data.
  - * @param he		tag container
  - * @return		formatted string
  - */
  -static char * hexFormat(HE_t he)
  -	/*@*/
  -{
  -    return intFormat(he, "x");
  -}
  -
  -/**
  - * Return decimal formatted data.
  - * @param he		tag container
  - * @return		formatted string
  - */
  -static char * decFormat(HE_t he)
  -	/*@*/
  -{
  -    return intFormat(he, "d");
  -}
  -
  -/**
  - * Return strftime formatted data.
  - * @param he		tag container
  - * @param strftimeFormat strftime(3) format
  - * @return		formatted string
  - */
  -static char * realDateFormat(HE_t he, const char * strftimeFormat)
  -	/*@*/
  -{
  -    rpmTagData data = { .ptr = he->p.ptr };
  -    char * val;
  -
  -    if (he->t != RPM_UINT64_TYPE) {
  -	val = xstrdup(_("(not a number)"));
  -    } else {
  -	struct tm * tstruct;
  -	char buf[50];
  -
  -	/* this is important if sizeof(uint64_t) ! sizeof(time_t) */
  -	{   time_t dateint = data.ui64p[0];
  -	    tstruct = localtime(&dateint);
  -	}
  -	buf[0] = '\0';
  -	if (tstruct)
  -	    (void) strftime(buf, sizeof(buf) - 1, strftimeFormat, tstruct);
  -	buf[sizeof(buf) - 1] = '\0';
  -	val = xstrdup(buf);
  -    }
  -
  -    return val;
  -}
  -
  -/**
  - * Return date formatted data.
  - * @param he		tag container
  - * @return		formatted string
  - */
  -static char * dateFormat(HE_t he)
  -	/*@*/
  -{
  -    return realDateFormat(he, _("%c"));
  -}
  -
  -/**
  - * Return day formatted data.
  - * @param he		tag container
  - * @return		formatted string
  - */
  -static char * dayFormat(HE_t he)
  -	/*@*/
  -{
  -    return realDateFormat(he, _("%a %b %d %Y"));
  -}
  -
  -/**
  - * Return shell escape formatted data.
  - * @param he		tag container
  - * @return		formatted string
  - */
  -static char * shescapeFormat(HE_t he)
  -	/*@*/
  -{
  -    rpmTagData data = { .ptr = he->p.ptr };
  -    char * val;
  -    size_t nb;
  -    int xx;
  -
  -    /* XXX one of these integer types is unnecessary. */
  -    if (he->t == RPM_UINT32_TYPE) {
  -	nb = 20;
  -	val = xmalloc(nb);
  -	xx = snprintf(val, nb, "%u", (unsigned) data.ui32p[0]);
  -	val[nb-1] = '\0';
  -    } else if (he->t == RPM_UINT64_TYPE) {
  -	nb = 40;
  -	val = xmalloc(40);
  -/*@-duplicatequals@*/
  -	xx = snprintf(val, nb, "%llu", (unsigned long long)data.ui64p[0]);
  -/*@=duplicatequals@*/
  -	val[nb-1] = '\0';
  -    } else if (he->t == RPM_STRING_TYPE) {
  -	const char * s = data.str;
  -	char * t;
  -	int c;
  -
  -	nb = strlen(data.str) + 1;
  -	/* XXX count no. of escapes instead. */
  -	t = xmalloc(4 * nb + 3);
  -	*t++ = '\'';
  -	while ((c = (int)*s++) != 0) {
  -	    if (c == (int)'\'') {
  -		*t++ = '\'';
  -		*t++ = '\\';
  -		*t++ = '\'';
  -	    }
  -	    *t++ = (char) c;
  -	}
  -	*t++ = '\'';
  -	*t = '\0';
  -	nb = strlen(t) + 1;
  -	val = xrealloc(t, nb);
  -    } else
  -	val = xstrdup(_("invalid type"));
  -
  -    return val;
  -}
  -
  -/*@-type@*/ /* FIX: cast? */
  -static struct headerSprintfExtension_s _headerDefaultFormats[] = {
  -    { HEADER_EXT_FORMAT, "octal",
  -	{ .fmtFunction = octFormat } },
  -    { HEADER_EXT_FORMAT, "oct",
  -	{ .fmtFunction = octFormat } },
  -    { HEADER_EXT_FORMAT, "hex",
  -	{ .fmtFunction = hexFormat } },
  -    { HEADER_EXT_FORMAT, "decimal",
  -	{ .fmtFunction = decFormat } },
  -    { HEADER_EXT_FORMAT, "dec",
  -	{ .fmtFunction = decFormat } },
  -    { HEADER_EXT_FORMAT, "date",
  -	{ .fmtFunction = dateFormat } },
  -    { HEADER_EXT_FORMAT, "day",
  -	{ .fmtFunction = dayFormat } },
  -    { HEADER_EXT_FORMAT, "shescape",
  -	{ .fmtFunction = shescapeFormat } },
  -    { HEADER_EXT_LAST, NULL, { NULL } }
  -};
  -/*@=type@*/
  -
  -headerSprintfExtension headerDefaultFormats = &_headerDefaultFormats[0];
  -
  -/* forward ref */
  -/**
  - * Parse a headerSprintf expression.
  - * @param hsa		headerSprintf args
  - * @param token
  - * @param str
  - * @retval *endPtr
  - * @return		0 on success
  - */
  -static int parseExpression(headerSprintfArgs hsa, sprintfToken token,
  -		char * str, /*@out@*/char ** endPtr)
  -	/*@modifies hsa, str, token, *endPtr @*/
  -	/*@requires maxSet(endPtr) >= 0 @*/;
  -
  -/**
  - * Parse a headerSprintf term.
  - * @param hsa		headerSprintf args
  - * @param str
  - * @retval *formatPtr
  - * @retval *numTokensPtr
  - * @retval *endPtr
  - * @param state
  - * @return		0 on success
  - */
  -static int parseFormat(headerSprintfArgs hsa, /*@null@*/ char * str,
  -		/*@out@*/ sprintfToken * formatPtr,
  -		/*@out@*/ size_t * numTokensPtr,
  -		/*@null@*/ /*@out@*/ char ** endPtr, int state)
  -	/*@modifies hsa, str, *formatPtr, *numTokensPtr, *endPtr @*/
  -	/*@requires maxSet(formatPtr) >= 0 /\ maxSet(numTokensPtr) >= 0
  -		/\ maxSet(endPtr) >= 0 @*/
  -{
  -    char * chptr, * start, * next, * dst;
  -    sprintfToken format;
  -    sprintfToken token;
  -    size_t numTokens;
  -    unsigned i;
  -    int done = 0;
  -
  -    /* upper limit on number of individual formats */
  -    numTokens = 0;
  -    if (str != NULL)
  -    for (chptr = str; *chptr != '\0'; chptr++)
  -	if (*chptr == '%') numTokens++;
  -    numTokens = numTokens * 2 + 1;
  -
  -    format = xcalloc(numTokens, sizeof(*format));
  -    if (endPtr) *endPtr = NULL;
  -
  -/*@-infloops@*/ /* LCL: can't detect done termination */
  -    dst = start = str;
  -    numTokens = 0;
  -    token = NULL;
  -    if (start != NULL)
  -    while (*start != '\0') {
  -	switch (*start) {
  -	case '%':
  -	    /* handle %% */
  -	    if (*(start + 1) == '%') {
  -		if (token == NULL || token->type != PTOK_STRING) {
  -		    token = format + numTokens++;
  -		    token->type = PTOK_STRING;
  -		    /*@-temptrans -assignexpose@*/
  -		    dst = token->u.string.string = start;
  -		    /*@=temptrans =assignexpose@*/
  -		}
  -		start++;
  -		*dst++ = *start++;
  -		/*@switchbreak@*/ break;
  -	    } 
  -
  -	    token = format + numTokens++;
  -	    *dst++ = '\0';
  -	    start++;
  -
  -	    if (*start == '|') {
  -		char * newEnd;
  -
  -		start++;
  -		if (parseExpression(hsa, token, start, &newEnd))
  -		{
  -		    format = freeFormat(format, numTokens);
  -		    return 1;
  -		}
  -		start = newEnd;
  -		/*@switchbreak@*/ break;
  -	    }
  -
  -	    /*@-assignexpose@*/
  -	    token->u.tag.format = start;
  -	    /*@=assignexpose@*/
  -	    token->u.tag.pad = 0;
  -	    token->u.tag.justOne = 0;
  -	    token->u.tag.arrayCount = 0;
  -
  -	    chptr = start;
  -	    while (*chptr && *chptr != '{' && *chptr != '%') chptr++;
  -	    if (!*chptr || *chptr == '%') {
  -		hsa->errmsg = _("missing { after %");
  -		format = freeFormat(format, numTokens);
  -		return 1;
  -	    }
  -
  -	    *chptr++ = '\0';
  -
  -	    while (start < chptr) {
  -		if (xisdigit((int)*start)) {
  -		    i = strtoul(start, &start, 10);
  -		    token->u.tag.pad += i;
  -		    start = chptr;
  -		    /*@innerbreak@*/ break;
  -		} else {
  -		    start++;
  -		}
  -	    }
  -
  -	    if (*start == '=') {
  -		token->u.tag.justOne = 1;
  -		start++;
  -	    } else if (*start == '#') {
  -		token->u.tag.justOne = 1;
  -		token->u.tag.arrayCount = 1;
  -		start++;
  -	    }
  -
  -	    next = start;
  -	    while (*next && *next != '}') next++;
  -	    if (!*next) {
  -		hsa->errmsg = _("missing } after %{");
  -		format = freeFormat(format, numTokens);
  -		return 1;
  -	    }
  -	    *next++ = '\0';
  -
  -	    chptr = start;
  -	    while (*chptr && *chptr != ':') chptr++;
  -
  -	    if (*chptr != '\0') {
  -		*chptr++ = '\0';
  -		if (!*chptr) {
  -		    hsa->errmsg = _("empty tag format");
  -		    format = freeFormat(format, numTokens);
  -		    return 1;
  -		}
  -		/*@-assignexpose@*/
  -		token->u.tag.type = chptr;
  -		/*@=assignexpose@*/
  -	    } else {
  -		token->u.tag.type = NULL;
  -	    }
  -	    
  -	    if (!*start) {
  -		hsa->errmsg = _("empty tag name");
  -		format = freeFormat(format, numTokens);
  -		return 1;
  -	    }
  -
  -	    i = 0;
  -	    token->type = PTOK_TAG;
  -
  -	    if (findTag(hsa, token, start)) {
  -		hsa->errmsg = _("unknown tag");
  -		format = freeFormat(format, numTokens);
  -		return 1;
  -	    }
  -
  -	    dst = start = next;
  -	    /*@switchbreak@*/ break;
  -
  -	case '[':
  -	    *start++ = '\0';
  -	    token = format + numTokens++;
  -
  -	    if (parseFormat(hsa, start,
  -			    &token->u.array.format,
  -			    &token->u.array.numTokens,
  -			    &start, PARSER_IN_ARRAY))
  -	    {
  -		format = freeFormat(format, numTokens);
  -		return 1;
  -	    }
  -
  -	    if (!start) {
  -		hsa->errmsg = _("] expected at end of array");
  -		format = freeFormat(format, numTokens);
  -		return 1;
  -	    }
  -
  -	    dst = start;
  -
  -	    token->type = PTOK_ARRAY;
  -
  -	    /*@switchbreak@*/ break;
  -
  -	case ']':
  -	    if (state != PARSER_IN_ARRAY) {
  -		hsa->errmsg = _("unexpected ]");
  -		format = freeFormat(format, numTokens);
  -		return 1;
  -	    }
  -	    *start++ = '\0';
  -	    if (endPtr) *endPtr = start;
  -	    done = 1;
  -	    /*@switchbreak@*/ break;
  -
  -	case '}':
  -	    if (state != PARSER_IN_EXPR) {
  -		hsa->errmsg = _("unexpected }");
  -		format = freeFormat(format, numTokens);
  -		return 1;
  -	    }
  -	    *start++ = '\0';
  -	    if (endPtr) *endPtr = start;
  -	    done = 1;
  -	    /*@switchbreak@*/ break;
  -
  -	default:
  -	    if (token == NULL || token->type != PTOK_STRING) {
  -		token = format + numTokens++;
  -		token->type = PTOK_STRING;
  -		/*@-temptrans -assignexpose@*/
  -		dst = token->u.string.string = start;
  -		/*@=temptrans =assignexpose@*/
  -	    }
  -
  -	    if (*start == '\\') {
  -		start++;
  -		*dst++ = escapedChar(*start++);
  -	    } else {
  -		*dst++ = *start++;
  -	    }
  -	    /*@switchbreak@*/ break;
  -	}
  -	if (done)
  -	    break;
  -    }
  -/*@=infloops@*/
  -
  -    if (dst != NULL)
  -        *dst = '\0';
  -
  -    for (i = 0; i < (unsigned) numTokens; i++) {
  -	token = format + i;
  -	switch(token->type) {
  -	default:
  -	    /*@switchbreak@*/ break;
  -	case PTOK_STRING:
  -	    token->u.string.len = strlen(token->u.string.string);
  -	    /*@switchbreak@*/ break;
  -	}
  -    }
  -
  -    if (numTokensPtr != NULL)
  -	*numTokensPtr = numTokens;
  -    if (formatPtr != NULL)
  -	*formatPtr = format;
  -
  -    return 0;
  -}
  -
  -static int parseExpression(headerSprintfArgs hsa, sprintfToken token,
  -		char * str, /*@out@*/ char ** endPtr)
  -{
  -    char * chptr;
  -    char * end;
  -
  -    hsa->errmsg = NULL;
  -    chptr = str;
  -    while (*chptr && *chptr != '?') chptr++;
  -
  -    if (*chptr != '?') {
  -	hsa->errmsg = _("? expected in expression");
  -	return 1;
  -    }
  -
  -    *chptr++ = '\0';
  -
  -    if (*chptr != '{') {
  -	hsa->errmsg = _("{ expected after ? in expression");
  -	return 1;
  -    }
  -
  -    chptr++;
  -
  -    if (parseFormat(hsa, chptr, &token->u.cond.ifFormat, 
  -		    &token->u.cond.numIfTokens, &end, PARSER_IN_EXPR)) 
  -	return 1;
  -
  -    /* XXX fix segfault on "rpm -q rpm --qf='%|NAME?{%}:{NAME}|\n'"*/
  -    if (!(end && *end)) {
  -	hsa->errmsg = _("} expected in expression");
  -	token->u.cond.ifFormat =
  -		freeFormat(token->u.cond.ifFormat, token->u.cond.numIfTokens);
  -	return 1;
  -    }
  -
  -    chptr = end;
  -    if (*chptr != ':' && *chptr != '|') {
  -	hsa->errmsg = _(": expected following ? subexpression");
  -	token->u.cond.ifFormat =
  -		freeFormat(token->u.cond.ifFormat, token->u.cond.numIfTokens);
  -	return 1;
  -    }
  -
  -    if (*chptr == '|') {
  -	if (parseFormat(hsa, NULL, &token->u.cond.elseFormat, 
  -		&token->u.cond.numElseTokens, &end, PARSER_IN_EXPR))
  -	{
  -	    token->u.cond.ifFormat =
  -		freeFormat(token->u.cond.ifFormat, token->u.cond.numIfTokens);
  -	    return 1;
  -	}
  -    } else {
  -	chptr++;
  -
  -	if (*chptr != '{') {
  -	    hsa->errmsg = _("{ expected after : in expression");
  -	    token->u.cond.ifFormat =
  -		freeFormat(token->u.cond.ifFormat, token->u.cond.numIfTokens);
  -	    return 1;
  -	}
  -
  -	chptr++;
  -
  -	if (parseFormat(hsa, chptr, &token->u.cond.elseFormat, 
  -			&token->u.cond.numElseTokens, &end, PARSER_IN_EXPR)) 
  -	    return 1;
  -
  -	/* XXX fix segfault on "rpm -q rpm --qf='%|NAME?{a}:{%}|{NAME}\n'" */
  -	if (!(end && *end)) {
  -	    hsa->errmsg = _("} expected in expression");
  -	    token->u.cond.ifFormat =
  -		freeFormat(token->u.cond.ifFormat, token->u.cond.numIfTokens);
  -	    return 1;
  -	}
  -
  -	chptr = end;
  -	if (*chptr != '|') {
  -	    hsa->errmsg = _("| expected at end of expression");
  -	    token->u.cond.ifFormat =
  -		freeFormat(token->u.cond.ifFormat, token->u.cond.numIfTokens);
  -	    token->u.cond.elseFormat =
  -		freeFormat(token->u.cond.elseFormat, token->u.cond.numElseTokens);
  -	    return 1;
  -	}
  -    }
  -	
  -    chptr++;
  -
  -    *endPtr = chptr;
  -
  -    token->type = PTOK_COND;
  -
  -    (void) findTag(hsa, token, str);
  -
  -    return 0;
  -}
  -
  -/**
  - * Call a header extension only once, saving results.
  - * @param hsa		headerSprintf args
  - * @param fn		function
  - * @retval he		tag container
  - * @retval ec		extension cache
  - * @return		0 on success, 1 on failure
  - */
  -static int getExtension(headerSprintfArgs hsa, headerTagTagFunction fn,
  -		HE_t he, HE_t ec)
  -	/*@modifies he, ec @*/
  -{
  -    int rc = 0;
  -    if (!ec->avail) {
  -	he = rpmheClean(he);
  -	rc = fn(hsa->h, he);
  -	*ec = *he;	/* structure copy. */
  -	if (!rc)
  -	    ec->avail = 1;
  -    } else
  -	*he = *ec;	/* structure copy. */
  -    he->freeData = 0;
  -    return rc;
  -}
  -
  -/**
  - * Format a single item's value.
  - * @param hsa		headerSprintf args
  - * @param tag		tag
  - * @param element	element index
  - * @return		end of formatted string (NULL on error)
  - */
  -/*@observer@*/ /*@null@*/
  -static char * formatValue(headerSprintfArgs hsa, sprintfTag tag,
  -		uint32_t element)
  -	/*@globals headerCompoundFormats @*/
  -	/*@modifies hsa, tag @*/
  -{
  -    HE_t vhe = memset(alloca(sizeof(*vhe)), 0, sizeof(*vhe));
  -    HE_t he = &tag->he;
  -    char * val = NULL;
  -    size_t need = 0;
  -    char * t, * te;
  -    uint64_t ival = 0;
  -    rpmTagCount countBuf;
  -    int xx;
  -
  -    if (!he->avail) {
  -	if (tag->ext) {
  -	    xx = getExtension(hsa, tag->ext, he, hsa->ec + tag->extNum);
  -	} else {
  -	    he->tag = tag->tagno;	/* XXX necessary? */
  -	    if (_usehge) {
  -		xx = headerGetExtension(hsa->h, he->tag, &he->t, &he->p, &he->c);
  -		if (xx)		/* XXX 1 on success */
  -		    he->freeData = 1;
  -	    } else {
  -		xx = headerGetEntry(hsa->h, he->tag, &he->t, &he->p, &he->c);
  -		if (xx)		/* XXX 1 on success */
  -		    he = rpmheMark(he);
  -	    }
  -	    xx = (xx == 0);	/* XXX invert headerGetEntry return. */
  -	}
  -	if (xx) {
  -	    (void) rpmheClean(he);
  -	    he->t = RPM_STRING_TYPE;	
  -	    he->p.str = xstrdup("(none)");
  -	    he->c = 1;
  -	}
  -	he->avail = 1;
  -    }
  -
  -    if (tag->arrayCount) {
  -	countBuf = he->c;
  -	he = rpmheClean(he);
  -	he->t = RPM_UINT32_TYPE;
  -	he->p.ui32p = &countBuf;
  -	he->c = 1;
  -	he->freeData = 0;
  -    }
  -
  -    if (he->p.ptr)
  -    switch (he->t) {
  -    default:
  -	val = xstrdup("(unknown type)");
  -	need = strlen(val) + 1;
  -	goto exit;
  -	/*@notreached@*/ break;
  -    case RPM_I18NSTRING_TYPE:
  -    case RPM_STRING_ARRAY_TYPE:
  -	vhe->t = RPM_STRING_TYPE;
  -	vhe->p.str = he->p.argv[element];
  -	vhe->c = he->c;
  -	/* XXX TODO: force array representation? */
  -	vhe->ix = (he->c > 1 ? 0 : -1);
  -	break;
  -    case RPM_STRING_TYPE:
  -	vhe->p.str = he->p.str;
  -	vhe->t = RPM_STRING_TYPE;
  -	vhe->c = he->c;
  -	vhe->ix = -1;
  -	break;
  -    case RPM_UINT8_TYPE:
  -    case RPM_UINT16_TYPE:
  -    case RPM_UINT32_TYPE:
  -    case RPM_UINT64_TYPE:
  -	switch (he->t) {
  -	default:
  -assert(0);	/* XXX keep gcc quiet. */
  -	    /*@innerbreak@*/ break;
  -	case RPM_UINT8_TYPE:
  -	    ival = he->p.ui8p[element];
  -	    /*@innerbreak@*/ break;
  -	case RPM_UINT16_TYPE:
  -	    ival = he->p.ui16p[element];	/* XXX note unsigned. */
  -	    /*@innerbreak@*/ break;
  -	case RPM_UINT32_TYPE:
  -	    ival = he->p.ui32p[element];
  -	    /*@innerbreak@*/ break;
  -	case RPM_UINT64_TYPE:
  -	    ival = he->p.ui64p[element];
  -	    /*@innerbreak@*/ break;
  -	}
  -	vhe->t = RPM_UINT64_TYPE;
  -	vhe->p.ui64p = &ival;
  -	vhe->c = he->c;
  -	/* XXX TODO: force array representation? */
  -	vhe->ix = (he->c > 1 ? 0 : -1);
  -	break;
  -
  -    case RPM_BIN_TYPE:
  -	vhe->t = RPM_BIN_TYPE;
  -	vhe->p.ptr = he->p.ptr;
  -	vhe->c = he->c;
  -	vhe->ix = -1;
  -	break;
  -    }
  -
  -/*@-compmempass@*/	/* vhe->p.ui64p is stack, not owned */
  -    if (tag->fmt)
  -	val = tag->fmt(vhe);
  -    else
  -	val = intFormat(vhe, NULL);
  -/*@=compmempass@*/
  -assert(val != NULL);
  -    if (val)
  -	need = strlen(val) + 1;
  -
  -exit:
  -/*@-compmempass@*/	/* he->p.ptr is dependent, not owned @*/
  -    if (!_tagcache)
  -	he = rpmheClean(he);
  -/*@=compmempass@*/
  -
  -    if (val && need > 0) {
  -	if (tag->format && *tag->format && tag->pad) {
  -	    size_t nb;
  -	    nb = strlen(tag->format) + sizeof("%s");
  -	    t = alloca(nb);
  -	    (void) stpcpy( stpcpy( stpcpy(t, "%"), tag->format), "s");
  -	    nb = tag->pad + strlen(val) + 1;
  -	    te = xmalloc(nb);
  -/*@-formatconst@*/
  -	    (void) snprintf(te, nb, t, val);
  -/*@=formatconst@*/
  -	    te[nb-1] = '\0';
  -	    val = _free(val);
  -	    val = te;
  -	    need += tag->pad;
  -	}
  -	t = hsaReserve(hsa, need);
  -	te = stpcpy(t, val);
  -	hsa->vallen += (te - t);
  -	val = _free(val);
  -    }
  -
  -    return (hsa->val + hsa->vallen);
  -}
  -
  -/**
  - * Format a single headerSprintf item.
  - * @param hsa		headerSprintf args
  - * @param token		item to format
  - * @param element	element index
  - * @return		end of formatted string (NULL on error)
  - */
  -/*@observer@*/
  -static char * singleSprintf(headerSprintfArgs hsa, sprintfToken token,
  -		uint32_t element)
  -	/*@globals headerCompoundFormats @*/
  -	/*@modifies hsa, token @*/
  -{
  -    char numbuf[64];	/* XXX big enuf for "Tag_0x01234567" */
  -    char * t, * te;
  -    uint32_t i, j;
  -    uint32_t numElements;
  -    sprintfToken spft;
  -    sprintfTag tag = NULL;
  -    HE_t he = NULL;
  -    uint32_t condNumFormats;
  -    size_t need;
  -    int xx;
  -
  -    /* we assume the token and header have been validated already! */
  -
  -    switch (token->type) {
  -    case PTOK_NONE:
  -	break;
  -
  -    case PTOK_STRING:
  -	need = token->u.string.len;
  -	if (need == 0) break;
  -	t = hsaReserve(hsa, need);
  -	te = stpcpy(t, token->u.string.string);
  -	hsa->vallen += (te - t);
  -	break;
  -
  -    case PTOK_TAG:
  -	t = hsa->val + hsa->vallen;
  -/*@-modobserver@*/	/* headerCompoundFormats not modified. */
  -	te = formatValue(hsa, &token->u.tag,
  -			(token->u.tag.justOne ? 0 : element));
  -/*@=modobserver@*/
  -	if (te == NULL)
  -	    return NULL;
  -	break;
  -
  -    case PTOK_COND:
  -	if (token->u.cond.tag.ext
  -	 || headerIsEntry(hsa->h, token->u.cond.tag.tagno))
  -	{
  -	    spft = token->u.cond.ifFormat;
  -	    condNumFormats = token->u.cond.numIfTokens;
  -	} else {
  -	    spft = token->u.cond.elseFormat;
  -	    condNumFormats = token->u.cond.numElseTokens;
  -	}
  -
  -	need = condNumFormats * 20;
  -	if (spft == NULL || need == 0) break;
  -
  -	t = hsaReserve(hsa, need);
  -	for (i = 0; i < condNumFormats; i++, spft++) {
  -/*@-modobserver@*/	/* headerCompoundFormats not modified. */
  -	    te = singleSprintf(hsa, spft, element);
  -/*@=modobserver@*/
  -	    if (te == NULL)
  -		return NULL;
  -	}
  -	break;
  -
  -    case PTOK_ARRAY:
  -	numElements = 0;
  -	spft = token->u.array.format;
  -	for (i = 0; i < token->u.array.numTokens; i++, spft++)
  -	{
  -	    tag = &spft->u.tag;
  -	    if (spft->type != PTOK_TAG || tag->arrayCount || tag->justOne)
  -		continue;
  -	    he = &tag->he;
  -	    if (!he->avail) {
  -		he->tag = tag->tagno;
  -		if (tag->ext)
  -		    xx = getExtension(hsa, tag->ext, he, hsa->ec + tag->extNum);
  -		else {
  -		    if (_usehge) {
  -			xx = headerGetExtension(hsa->h, he->tag, &he->t, &he->p, &he->c);
  -			if (xx)
  -			    he->freeData = 1;
  -		    } else {
  -			xx = headerGetEntry(hsa->h, he->tag, &he->t, &he->p, &he->c);
  -			if (xx)	/* XXX 1 on success */
  -			    he = rpmheMark(he);
  -		    }
  -		    xx = (xx == 0);     /* XXX invert headerGetEntry return. */
  -		}
  -		if (xx) {
  -		    (void) rpmheClean(he);
  -		    continue;
  -		}
  -		he->avail = 1;
  -	    }
  -
  -	    /* Check iteration arrays are same dimension (or scalar). */
  -	    switch (he->t) {
  -	    default:
  -		if (numElements == 0) {
  -		    numElements = he->c;
  -		    /*@switchbreak@*/ break;
  -		}
  -		if (he->c == numElements)
  -		    /*@switchbreak@*/ break;
  -		hsa->errmsg =
  -			_("array iterator used with different sized arrays");
  -		he = rpmheClean(he);
  -		return NULL;
  -		/*@notreached@*/ /*@switchbreak@*/ break;
  -	    case RPM_BIN_TYPE:
  -	    case RPM_STRING_TYPE:
  -		if (numElements == 0)
  -		    numElements = 1;
  -		/*@switchbreak@*/ break;
  -	    }
  -	    if (!_tagcache)
  -		he = rpmheClean(he);
  -	}
  -	spft = token->u.array.format;
  -
  -	if (numElements == 0) {
  -#ifdef	DYING	/* XXX lots of pugly "(none)" lines with --conflicts. */
  -	    need = sizeof("(none)\n") - 1;
  -	    t = hsaReserve(hsa, need);
  -	    te = stpcpy(t, "(none)\n");
  -	    hsa->vallen += (te - t);
  -#endif
  -	} else {
  -	    int isxml;
  -	    int isyaml;
  -
  -	    need = numElements * token->u.array.numTokens;
  -	    if (need == 0) break;
  -
  -	    tag = &spft->u.tag;
  -
  -	    isxml = (spft->type == PTOK_TAG && tag->type != NULL &&
  -		!strcmp(tag->type, "xml"));
  -	    isyaml = (spft->type == PTOK_TAG && tag->type != NULL &&
  -		!strcmp(tag->type, "yaml"));
  -
  -	    if (isxml) {
  -		const char * tagN = myTagName(hsa->tags, tag->tagno, NULL);
  -		/* XXX display "Tag_0x01234567" for unknown tags. */
  -		if (tagN == NULL) {
  -		    (void) snprintf(numbuf, sizeof(numbuf), "Tag_0x%08x",
  -				(unsigned) tag->tagno);
  -		    numbuf[sizeof(numbuf)-1] = '\0';
  -		    tagN = numbuf;
  -		}
  -		need = sizeof("  <rpmTag name=\"\">\n") + strlen(tagN);
  -		te = t = hsaReserve(hsa, need);
  -		te = stpcpy( stpcpy( stpcpy(te, "  <rpmTag name=\""), tagN), "\">\n");
  -		hsa->vallen += (te - t);
  -	    }
  -	    if (isyaml) {
  -		rpmTag tagT = 0;
  -		const char * tagN = myTagName(hsa->tags, tag->tagno, &tagT);
  -		/* XXX display "Tag_0x01234567" for unknown tags. */
  -		if (tagN == NULL) {
  -		    (void) snprintf(numbuf, sizeof(numbuf), "Tag_0x%08x",
  -				(unsigned) tag->tagno);
  -		    numbuf[sizeof(numbuf)-1] = '\0';
  -		    tagN = numbuf;
  -/*@-type@*/
  -		    tagT = numElements > 1
  -			?  RPM_ARRAY_RETURN_TYPE : RPM_SCALAR_RETURN_TYPE;
  -/*@=type@*/
  -		}
  -		need = sizeof("  :     - ") + strlen(tagN);
  -		te = t = hsaReserve(hsa, need);
  -		*te++ = ' ';
  -		*te++ = ' ';
  -		te = stpcpy(te, tagN);
  -		*te++ = ':';
  -/*@-type@*/
  -		*te++ = (((tagT & RPM_MASK_RETURN_TYPE) == RPM_ARRAY_RETURN_TYPE)
  -			? '\n' : ' ');
  -/*@=type@*/
  -		/* XXX Dirnames: in srpms need "    " indent */
  -/*@-type@*/
  -		if (((tagT & RPM_MASK_RETURN_TYPE) == RPM_ARRAY_RETURN_TYPE)
  -		 && numElements == 1)
  -/*@=type@*/
  -		{
  -		    te = stpcpy(te, "    ");
  -		    if (tag->tagno != 1118)
  -			te = stpcpy(te, "- ");
  -		}
  -		*te = '\0';
  -		hsa->vallen += (te - t);
  -	    }
  -
  -	    need = numElements * token->u.array.numTokens * 10;
  -	    t = hsaReserve(hsa, need);
  -	    for (j = 0; j < numElements; j++) {
  -		spft = token->u.array.format;
  -		for (i = 0; i < token->u.array.numTokens; i++, spft++) {
  -/*@-modobserver@*/	/* headerCompoundFormats not modified. */
  -		    te = singleSprintf(hsa, spft, j);
  -/*@=modobserver@*/
  -		    if (te == NULL)
  -			return NULL;
  -		}
  -	    }
  -
  -	    if (isxml) {
  -		need = sizeof("  </rpmTag>\n") - 1;
  -		te = t = hsaReserve(hsa, need);
  -		te = stpcpy(te, "  </rpmTag>\n");
  -		hsa->vallen += (te - t);
  -	    }
  -	    if (isyaml) {
  -#if 0
  -		need = sizeof("\n") - 1;
  -		te = t = hsaReserve(hsa, need);
  -		te = stpcpy(te, "\n");
  -		hsa->vallen += (te - t);
  -#endif
  -	    }
  -
  -	}
  -	break;
  -    }
  -
  -    return (hsa->val + hsa->vallen);
  -}
  -
  -/**
  - * Create an extension cache.
  - * @param exts		headerSprintf extensions
  - * @retval *necp	no. of elements (or NULL)
  - * @return		new extension cache
  - */
  -static /*@only@*/ HE_t
  -rpmecNew(const headerSprintfExtension exts, /*@null@*/ int * necp)
  -	/*@modifies *necp @*/
  -{
  -    headerSprintfExtension ext;
  -    HE_t ec;
  -    int extNum = 0;
  -
  -    if (exts != NULL)
  -    for (ext = exts, extNum = 0; ext != NULL && ext->type != HEADER_EXT_LAST;
  -	ext = (ext->type == HEADER_EXT_MORE ? *ext->u.more : ext+1), extNum++)
  -    {
  -	;
  -    }
  -    if (necp)
  -	*necp = extNum;
  -    ec = xcalloc(extNum+1, sizeof(*ec));	/* XXX +1 unnecessary */
  -    return ec;
  -}
  -
  -/**
  - * Destroy an extension cache.
  - * @param exts		headerSprintf extensions
  - * @param ec		extension cache
  - * @return		NULL always
  - */
  -static /*@null@*/ HE_t
  -rpmecFree(const headerSprintfExtension exts, /*@only@*/ HE_t ec)
  -	/*@modifies ec @*/
  -{
  -    headerSprintfExtension ext;
  -    int extNum;
  -
  -    for (ext = exts, extNum = 0; ext != NULL && ext->type != HEADER_EXT_LAST;
  -	ext = (ext->type == HEADER_EXT_MORE ? *ext->u.more : ext+1), extNum++)
  -    {
  -	(void) rpmheClean(&ec[extNum]);
  -    }
  -
  -    ec = _free(ec);
  -    return NULL;
  -}
  -
  -/** \ingroup header
  - * Return formatted output string from header tags.
  - * The returned string must be free()d.
  - *
  - * @param h		header
  - * @param fmt		format to use
  - * @param tags		array of tag name/value/type triples (NULL uses default)
  - * @param exts		formatting extensions chained table (NULL uses default)
  - * @retval *errmsg	error message (if any)
  - * @return		formatted output string (malloc'ed)
  - */
  -static /*@only@*/ /*@null@*/
  -char * headerSprintf(Header h, const char * fmt,
  -		/*@null@*/ headerTagTableEntry tags,
  -		/*@null@*/ headerSprintfExtension exts,
  -		/*@null@*/ /*@out@*/ errmsg_t * errmsg)
  -	/*@globals headerCompoundFormats @*/
  -	/*@modifies h, *errmsg @*/
  -	/*@requires maxSet(errmsg) >= 0 @*/
  -{
  -    headerSprintfArgs hsa = memset(alloca(sizeof(*hsa)), 0, sizeof(*hsa));
  -    sprintfToken nextfmt;
  -    sprintfTag tag;
  -    char * t, * te;
  -    int isxml;
  -    int isyaml;
  -    int need;
  -
  -    /* Set some reasonable defaults */
  -    if (tags == NULL)
  -	tags = rpmTagTable;
  -    /* XXX this loses the extensions in lib/formats.c. */
  -    if (exts == NULL)
  -	exts = headerCompoundFormats;
  - 
  -    hsa->h = headerLink(h);
  -    hsa->fmt = xstrdup(fmt);
  -/*@-dependenttrans@*/
  -    hsa->exts = exts;
  -    hsa->tags = tags;
  -/*@=dependenttrans@*/
  -    hsa->errmsg = NULL;
  -
  -    if (parseFormat(hsa, hsa->fmt, &hsa->format, &hsa->numTokens, NULL, PARSER_BEGIN))
  -	goto exit;
  -
  -    hsa->nec = 0;
  -    hsa->ec = rpmecNew(hsa->exts, &hsa->nec);
  -    hsa->val = xstrdup("");
  -
  -    tag =
  -	(hsa->format->type == PTOK_TAG
  -	    ? &hsa->format->u.tag :
  -	(hsa->format->type == PTOK_ARRAY
  -	    ? &hsa->format->u.array.format->u.tag :
  -	NULL));
  -    isxml = (tag != NULL && tag->tagno == -2 && tag->type != NULL && !strcmp(tag->type, "xml"));
  -    isyaml = (tag != NULL && tag->tagno == -2 && tag->type != NULL && !strcmp(tag->type, "yaml"));
  -
  -    if (isxml) {
  -	need = sizeof("<rpmHeader>\n") - 1;
  -	t = hsaReserve(hsa, need);
  -	te = stpcpy(t, "<rpmHeader>\n");
  -	hsa->vallen += (te - t);
  -    }
  -    if (isyaml) {
  -	need = sizeof("- !!omap\n") - 1;
  -	t = hsaReserve(hsa, need);
  -	te = stpcpy(t, "- !!omap\n");
  -	hsa->vallen += (te - t);
  -    }
  -
  -    hsa = hsaInit(hsa);
  -    while ((nextfmt = hsaNext(hsa)) != NULL) {
  -/*@-modobserver@*/	/* headerCompoundFormats not modified. */
  -	te = singleSprintf(hsa, nextfmt, 0);
  -/*@=modobserver@*/
  -	if (te == NULL) {
  -	    hsa->val = _free(hsa->val);
  -	    break;
  -	}
  -    }
  -    hsa = hsaFini(hsa);
  -
  -    if (isxml) {
  -	need = sizeof("</rpmHeader>\n") - 1;
  -	t = hsaReserve(hsa, need);
  -	te = stpcpy(t, "</rpmHeader>\n");
  -	hsa->vallen += (te - t);
  -    }
  -    if (isyaml) {
  -	need = sizeof("\n") - 1;
  -	t = hsaReserve(hsa, need);
  -	te = stpcpy(t, "\n");
  -	hsa->vallen += (te - t);
  -    }
  -
  -    if (hsa->val != NULL && hsa->vallen < hsa->alloced)
  -	hsa->val = xrealloc(hsa->val, hsa->vallen+1);	
  -
  -    hsa->ec = rpmecFree(hsa->exts, hsa->ec);
  -    hsa->nec = 0;
  -    hsa->format = freeFormat(hsa->format, hsa->numTokens);
  -
  -exit:
  -/*@-dependenttrans -observertrans @*/
  -    if (errmsg)
  -	*errmsg = hsa->errmsg;
  -/*@=dependenttrans =observertrans @*/
  -    hsa->h = headerFree(hsa->h);
  -    hsa->fmt = _free(hsa->fmt);
  -    return hsa->val;
  -}
  -
   /** \ingroup header
    * Duplicate tag values from one header into another.
    * @param headerFrom	source header
  @@ -3899,7 +2368,6 @@
       headerAddI18NString,
       headerModifyEntry,
       headerRemoveEntry,
  -    headerSprintf,
       headerCopyTags,
       headerFreeIterator,
       headerInitIterator,
  @@ .
  patch -p0 <<'@@ .'
  Index: rpm/rpmdb/header.h
  ============================================================================
  $ cvs diff -u -r1.73 -r1.74 header.h
  --- rpm/rpmdb/header.h	24 Nov 2007 18:48:15 -0000	1.73
  +++ rpm/rpmdb/header.h	24 Nov 2007 21:01:45 -0000	1.74
  @@ -414,24 +414,6 @@
           /*@modifies h @*/;
   
   /** \ingroup header
  - * Return formatted output string from header tags.
  - * The returned string must be free()d.
  - *
  - * @param h		header
  - * @param fmt		format to use
  - * @param tags		array of tag name/value/type triples (NULL uses default)
  - * @param exts		formatting extensions chained table (NULL uses default)
  - * @retval errmsg	error message (if any)
  - * @return		formatted output string (malloc'ed)
  - */
  -typedef
  -/*@only@*/ char * (*HDRsprintf) (Header h, const char * fmt,
  -		/*@null@*/ headerTagTableEntry tags,
  -		/*@null@*/ headerSprintfExtension exts,
  -		/*@null@*/ /*@out@*/ errmsg_t * errmsg)
  -	/*@modifies *errmsg @*/;
  -
  -/** \ingroup header
    * Duplicate tag values from one header into another.
    * @param headerFrom	source header
    * @param headerTo	destination header
  @@ -574,7 +556,6 @@
       HDRaddi18n	hdraddi18n;
       HDRmodify	hdrmodify;
       HDRremove	hdrremove;
  -    HDRsprintf	hdrsprintf;
       HDRcopytags	hdrcopytags;
       HDRfreeiter	hdrfreeiter;
       HDRinititer	hdrinititer;
  @@ .
  patch -p0 <<'@@ .'
  Index: rpm/rpmdb/header_internal.h
  ============================================================================
  $ cvs diff -u -r1.35 -r1.36 header_internal.h
  --- rpm/rpmdb/header_internal.h	23 Nov 2007 00:29:47 -0000	1.35
  +++ rpm/rpmdb/header_internal.h	24 Nov 2007 21:01:45 -0000	1.36
  @@ -106,63 +106,6 @@
       int nrefs;			/*!< Reference count. */
   };
   
  -/** \ingroup header
  - */
  -typedef /*@abstract@*/ struct sprintfTag_s * sprintfTag;
  -struct sprintfTag_s {
  -    HE_s he;
  -/*@null@*/
  -    headerTagFormatFunction fmt;
  -/*@null@*/
  -    headerTagTagFunction ext;   /*!< NULL if tag element is invalid */
  -    int extNum;
  -    rpmTag tagno;
  -    int justOne;
  -    int arrayCount;
  -/*@kept@*/
  -    char * format;
  -/*@kept@*/ /*@null@*/
  -    char * type;
  -    int pad;
  -};
  -
  -/** \ingroup header
  - */
  -typedef /*@abstract@*/ struct sprintfToken_s * sprintfToken;
  -/*@-fielduse@*/
  -struct sprintfToken_s {
  -    enum {
  -        PTOK_NONE       = 0,
  -        PTOK_TAG        = 1,
  -        PTOK_ARRAY      = 2,
  -        PTOK_STRING     = 3,
  -        PTOK_COND       = 4
  -    } type;
  -    union {
  -	struct sprintfTag_s tag;	/*!< PTOK_TAG */
  -	struct {
  -	/*@only@*/
  -	    sprintfToken format;
  -	    size_t numTokens;
  -	} array;			/*!< PTOK_ARRAY */
  -	struct {
  -	/*@dependent@*/
  -	    char * string;
  -	    size_t len;
  -	} string;			/*!< PTOK_STRING */
  -	struct {
  -	/*@only@*/ /*@null@*/
  -	    sprintfToken ifFormat;
  -	    size_t numIfTokens;
  -	/*@only@*/ /*@null@*/
  -	    sprintfToken elseFormat;
  -	    size_t numElseTokens;
  -	    struct sprintfTag_s tag;
  -	} cond;				/*!< PTOK_COND */
  -    } u;
  -};
  -/*@=fielduse@*/
  -
   #ifdef __cplusplus
   extern "C" {
   #endif
  @@ .
  patch -p0 <<'@@ .'
  Index: rpm/rpmdb/librpmdb.vers
  ============================================================================
  $ cvs diff -u -r1.30 -r1.31 librpmdb.vers
  --- rpm/rpmdb/librpmdb.vers	24 Nov 2007 19:46:07 -0000	1.30
  +++ rpm/rpmdb/librpmdb.vers	24 Nov 2007 21:01:45 -0000	1.31
  @@ -30,14 +30,12 @@
       headerCheck;
       headerCompoundFormats;
       headerDefaultFormats;
  -    headerDump;
  -    headerGetLangs;
  -    headerGetRawEntry;
       headerMacrosLoad;
       headerMacrosUnload;
       headerMergeLegacySigs;
       headerNEVRA;
       headerRegenSigHeader;
  +    headerSprintf;
       headerVerifyInfo;
       hGetColor;
       _init;
  @@ .
  patch -p0 <<'@@ .'
  Index: rpm/rpmdb/rpmtag.h
  ============================================================================
  $ cvs diff -u -r1.11 -r1.12 rpmtag.h
  --- rpm/rpmdb/rpmtag.h	24 Nov 2007 19:46:07 -0000	1.11
  +++ rpm/rpmdb/rpmtag.h	24 Nov 2007 21:01:45 -0000	1.12
  @@ -630,6 +630,26 @@
   	/*@globals fileSystem, internalState @*/
   	/*@modifies *fp, fileSystem, internalState @*/;
   
  +/** \ingroup header
  + * Return formatted output string from header tags.
  + * The returned string must be free()d.
  + *
  + * @param h		header
  + * @param fmt		format to use
  + * @param tags		array of tag name/value/type triples (NULL uses default)
  + * @param exts		formatting extensions chained table (NULL uses default)
  + * @retval errmsg	error message (if any)
  + * @return		formatted output string (malloc'ed)
  + */
  +/*@only@*/ /*@null@*/
  +char * headerSprintf(Header h, const char * fmt,
  +		/*@null@*/ headerTagTableEntry tags,
  +		/*@null@*/ headerSprintfExtension exts,
  +		/*@null@*/ /*@out@*/ errmsg_t * errmsg)
  +	/*@globals headerCompoundFormats @*/
  +	/*@modifies h, *errmsg @*/
  +	/*@requires maxSet(errmsg) >= 0 @*/;
  +
   #ifdef __cplusplus
   }
   #endif
  @@ .
Received on Sat Nov 24 22:01:46 2007
Driven by Jeff Johnson and the RPM project team.
Hosted by OpenPKG and Ralf S. Engelschall.
Powered by FreeBSD and OpenPKG.