rpm 5.3.12
rpmdb/hdrfmt.c
Go to the documentation of this file.
00001 
00005 #include "system.h"
00006 
00007 /* XXX todo: these should likely be in "system.h" */
00008 #if defined(HAVE_ICONV)
00009 #include <iconv.h>
00010 #if defined(__LCLINT__)
00011 /*@-declundef -exportheader -incondefs @*/
00012 extern /*@only@*/ iconv_t iconv_open(const char *__tocode, const char *__fromcode)
00013         /*@*/;
00014 
00015 extern size_t iconv(iconv_t __cd, /*@null@*/ char ** __inbuf,
00016                     /*@out@*/ size_t * __inbytesleft,
00017                     /*@out@*/ char ** __outbuf,
00018                     /*@out@*/ size_t * __outbytesleft)
00019         /*@modifies __cd,
00020                 *__inbuf, *__inbytesleft, *__outbuf, *__outbytesleft @*/;
00021 
00022 extern int iconv_close(/*@only@*/ iconv_t __cd)
00023         /*@modifies __cd @*/;
00024 /*@=declundef =exportheader =incondefs @*/
00025 #endif
00026 #endif
00027 
00028 #if defined(HAVE_LANGINFO_H)
00029 #include <langinfo.h>
00030 #if defined(__LCLINT__)
00031 /*@-declundef -exportheader -incondefs @*/
00032 extern char *nl_langinfo (nl_item __item)
00033         /*@*/;
00034 /*@=declundef =exportheader =incondefs @*/
00035 #endif
00036 #endif
00037 
00038 #define _MIRE_INTERNAL
00039 #include "rpmio_internal.h"
00040 #include <rpmbc.h>      /* XXX beecrypt base64 */
00041 #include <rpmcb.h>      /* XXX rpmIsVerbose */
00042 #include <rpmmacro.h>   /* XXX for %_i18ndomains */
00043 #include <rpmuuid.h>
00044 #include <argv.h>
00045 #include <ugid.h>
00046 
00047 #define _RPMTAG_INTERNAL
00048 #include <rpmtag.h>
00049 #define _RPMEVR_INTERNAL
00050 #include <rpmevr.h>     /* XXX RPMSENSE_FOO */
00051 #include <rpmns.h>
00052 #include <rpmdb.h>
00053 
00054 #include <rpmtypes.h>   /* XXX rpmfi */
00055 #include "misc.h"       /* XXX rpmMkdirPath */
00056 #include <rpmfi.h>      /* XXX RPMFILE_FOO */
00057 
00058 #include "legacy.h"
00059 #include "misc.h"
00060 
00061 #include "debug.h"
00062 
00063 /*@unchecked@*/
00064 int _hdrqf_debug;
00065 
00066 /*@access pgpDig @*/
00067 /*@access pgpDigParams @*/
00068 /*@access headerSprintfExtension @*/
00069 /*@access headerTagTableEntry @*/
00070 /*@access Header @*/    /* XXX debugging msgs */
00071 /*@access EVR_t @*/
00072 /*@access rpmdb @*/     /* XXX for casts */
00073 /*@access miRE @*/
00074 
00082 static char * intFormat(HE_t he, /*@unused@*/ /*@null@*/ const char ** av,
00083                 /*@null@*/ const char *fmt)
00084         /*@*/
00085 {
00086     rpmuint32_t ix = (he->ix > 0 ? he->ix : 0);
00087     rpmuint64_t ival = 0;
00088     const char * istr = NULL;
00089     char * b;
00090     size_t nb = 0;
00091     int xx;
00092 
00093     if (fmt == NULL || *fmt == '\0')
00094         fmt = "d";
00095 
00096     switch (he->t) {
00097     default:
00098         return xstrdup(_("(not a number)"));
00099         /*@notreached@*/ break;
00100     case RPM_UINT8_TYPE:
00101         ival = (rpmuint64_t) he->p.ui8p[ix];
00102         break;
00103     case RPM_UINT16_TYPE:
00104         ival = (rpmuint64_t) he->p.ui16p[ix];
00105         break;
00106     case RPM_UINT32_TYPE:
00107         ival = (rpmuint64_t) he->p.ui32p[ix];
00108         break;
00109     case RPM_UINT64_TYPE:
00110         ival = he->p.ui64p[ix];
00111         break;
00112     case RPM_STRING_TYPE:
00113         istr = he->p.str;
00114         break;
00115     case RPM_STRING_ARRAY_TYPE:
00116         istr = he->p.argv[ix];
00117         break;
00118     case RPM_BIN_TYPE:
00119         {   static char hex[] = "0123456789abcdef";
00120             const char * s = he->p.str;
00121             rpmTagCount c = he->c;
00122             char * t;
00123 
00124             nb = 2 * c + 1;
00125             t = b = alloca(nb+1);
00126             while (c-- > 0) {
00127                 unsigned i;
00128                 i = (unsigned) *s++;
00129                 *t++ = hex[ (i >> 4) & 0xf ];
00130                 *t++ = hex[ (i     ) & 0xf ];
00131             }
00132             *t = '\0';
00133         }   break;
00134     }
00135 
00136     if (istr) {         /* string */
00137         b = (char *)istr;       /* NOCAST */
00138     } else
00139     if (nb == 0) {      /* number */
00140         char myfmt[] = "%llX";
00141         myfmt[3] = ((fmt != NULL && *fmt != '\0') ? *fmt : 'd');
00142         nb = 64;
00143         b = alloca(nb);
00144 /*@-formatconst@*/
00145         xx = snprintf(b, nb, myfmt, ival);
00146 /*@=formatconst@*/
00147         b[nb-1] = '\0';
00148     } else
00149         b = "";
00150 
00151     return xstrdup(b);
00152 }
00153 
00160 static char * octFormat(HE_t he, /*@null@*/ const char ** av)
00161         /*@*/
00162 {
00163     return intFormat(he, av, "o");
00164 }
00165 
00172 static char * hexFormat(HE_t he, /*@null@*/ const char ** av)
00173         /*@*/
00174 {
00175     return intFormat(he, av, "x");
00176 }
00177 
00184 static char * decFormat(HE_t he, /*@null@*/ const char ** av)
00185         /*@*/
00186 {
00187     return intFormat(he, av, "d");
00188 }
00189 
00197 static char * realDateFormat(HE_t he, /*@unused@*/ /*@null@*/ const char ** av,
00198                 const char * strftimeFormat)
00199         /*@*/
00200 {
00201     char * val;
00202 
00203     if (he->t != RPM_UINT64_TYPE) {
00204         val = xstrdup(_("(not a number)"));
00205     } else {
00206         struct tm * tstruct;
00207         char buf[50];
00208 
00209         /* this is important if sizeof(rpmuint64_t) ! sizeof(time_t) */
00210         {   time_t dateint = he->p.ui64p[0];
00211             tstruct = localtime(&dateint);
00212         }
00213         buf[0] = '\0';
00214         if (tstruct)
00215             (void) strftime(buf, sizeof(buf) - 1, strftimeFormat, tstruct);
00216         buf[sizeof(buf) - 1] = '\0';
00217         val = xstrdup(buf);
00218     }
00219 
00220     return val;
00221 }
00222 
00229 static char * dateFormat(HE_t he, /*@null@*/ const char ** av)
00230         /*@*/
00231 {
00232     return realDateFormat(he, av, _("%c"));
00233 }
00234 
00241 static char * dayFormat(HE_t he, /*@null@*/ const char ** av)
00242         /*@*/
00243 {
00244     return realDateFormat(he, av, _("%a %b %d %Y"));
00245 }
00246 
00253 static char * shescapeFormat(HE_t he, /*@unused@*/ /*@null@*/ const char ** av)
00254         /*@*/
00255 {
00256     char * val;
00257     size_t nb;
00258     int xx;
00259 
00260     /* XXX one of these integer types is unnecessary. */
00261     if (he->t == RPM_UINT32_TYPE) {
00262         nb = 20;
00263         val = xmalloc(nb);
00264         xx = snprintf(val, nb, "%u", (unsigned) he->p.ui32p[0]);
00265         val[nb-1] = '\0';
00266     } else if (he->t == RPM_UINT64_TYPE) {
00267         nb = 40;
00268         val = xmalloc(40);
00269 /*@-duplicatequals@*/
00270         xx = snprintf(val, nb, "%llu", (unsigned long long)he->p.ui64p[0]);
00271 /*@=duplicatequals@*/
00272         val[nb-1] = '\0';
00273     } else if (he->t == RPM_STRING_TYPE) {
00274         const char * s = he->p.str;
00275         char * t;
00276         int c;
00277 
00278         nb = 0;
00279         for (s = he->p.str; (c = (int)*s) != 0; s++)  {
00280             nb++;
00281             if (c == (int)'\'')
00282                 nb += 3;
00283         }
00284         nb += 3;
00285         t = val = xmalloc(nb);
00286         *t++ = '\'';
00287         for (s = he->p.str; (c = (int)*s) != 0; s++)  {
00288             if (c == (int)'\'') {
00289                 *t++ = '\'';
00290                 *t++ = '\\';
00291                 *t++ = '\'';
00292             }
00293             *t++ = (char) c;
00294         }
00295         *t++ = '\'';
00296         *t = '\0';
00297     } else
00298         val = xstrdup(_("invalid type"));
00299 
00300     return val;
00301 }
00302 
00303 static struct headerSprintfExtension_s _headerDefaultFormats[] = {
00304     { HEADER_EXT_FORMAT, "octal",
00305         { .fmtFunction = octFormat } },
00306     { HEADER_EXT_FORMAT, "oct",
00307         { .fmtFunction = octFormat } },
00308     { HEADER_EXT_FORMAT, "hex",
00309         { .fmtFunction = hexFormat } },
00310     { HEADER_EXT_FORMAT, "decimal",
00311         { .fmtFunction = decFormat } },
00312     { HEADER_EXT_FORMAT, "dec",
00313         { .fmtFunction = decFormat } },
00314     { HEADER_EXT_FORMAT, "date",
00315         { .fmtFunction = dateFormat } },
00316     { HEADER_EXT_FORMAT, "day",
00317         { .fmtFunction = dayFormat } },
00318     { HEADER_EXT_FORMAT, "shescape",
00319         { .fmtFunction = shescapeFormat } },
00320     { HEADER_EXT_LAST, NULL, { NULL } }
00321 };
00322 
00323 headerSprintfExtension headerDefaultFormats = &_headerDefaultFormats[0];
00324 
00325 /*====================================================================*/
00326 typedef const struct spew_s * spew_t;
00327 struct spew_s {
00328 /*@observer@*/
00329     const char * spew_name;
00330     const char * spew_init;
00331     const char * spew_fini;
00332     size_t (*spew_strlen) (const char * s, int lvl)
00333         /*@*/;
00334     char * (*spew_strcpy) (/*@returned@*/ char * t, const char * s, int lvl)
00335         /*@modifies t @*/;
00336 };
00337 
00338 /*====================================================================*/
00345 static size_t xmlstrlen(const char * s, /*@unused@*/ int lvl)
00346         /*@*/
00347 {
00348     size_t len = 0;
00349     int c;
00350 
00351     while ((c = (int) *s++) != (int) '\0') {
00352         switch (c) {
00353         case '<':
00354         case '>':       len += sizeof("&lt;") - 1;      /*@switchbreak@*/ break;
00355         case '&':       len += sizeof("&amp;") - 1;     /*@switchbreak@*/ break;
00356         default:        len += 1;                       /*@switchbreak@*/ break;
00357         }
00358     }
00359     return len;
00360 }
00361 
00369 static char * xmlstrcpy(/*@returned@*/ char * t, const char * s,
00370                 /*@unused@*/ int lvl)
00371         /*@modifies t @*/
00372 {
00373     char * te = t;
00374     int c;
00375 
00376     while ((c = (int) *s++) != (int) '\0') {
00377         switch (c) {
00378         case '<':       te = stpcpy(te, "&lt;");        /*@switchbreak@*/ break;
00379         case '>':       te = stpcpy(te, "&gt;");        /*@switchbreak@*/ break;
00380         case '&':       te = stpcpy(te, "&amp;");       /*@switchbreak@*/ break;
00381         default:        *te++ = (char) c;               /*@switchbreak@*/ break;
00382         }
00383     }
00384     *te = '\0';
00385     return t;
00386 }
00387 
00388 /*@unchecked@*/ /*@observer@*/ 
00389 static const struct spew_s _xml_spew = {
00390     .spew_name          = "xml",
00391     .spew_init          = "<rpmHeader>\n",
00392     .spew_fini          = "</rpmHeader>\n",
00393     .spew_strlen        = xmlstrlen,
00394     .spew_strcpy        = xmlstrcpy
00395 };
00396 
00397 /*====================================================================*/
00398 
00405 static size_t yamlstrlen(const char * s, int lvl)
00406         /*@*/
00407 {
00408     size_t len = 0;
00409     int indent = (lvl > 0);
00410     int c;
00411 
00412     while ((c = (int) *s++) != (int) '\0')
00413     {
00414         if (indent) {
00415             len += 2 * lvl;
00416             indent = 0;
00417         }
00418         if (c == (int) '\n')
00419             indent = (lvl > 0);
00420         len++;
00421     }
00422     return len;
00423 }
00424 
00432 static char * yamlstrcpy(/*@out@*/ /*@returned@*/ char * t, const char * s,
00433                 int lvl)
00434         /*@modifies t @*/
00435 {
00436     char * te = t;
00437     int indent = (lvl > 0);
00438     int c;
00439 
00440     while ((c = (int) *s++) != (int) '\0') {
00441         if (indent) {
00442             int i;
00443             for (i = 0; i < lvl; i++) {
00444                 *te++ = ' ';
00445                 *te++ = ' ';
00446             }
00447             indent = 0;
00448         }
00449         if (c == (int) '\n')
00450             indent = (lvl > 0);
00451         *te++ = (char) c;
00452     }
00453     *te = '\0';
00454     return t;
00455 }
00456 
00457 /*@unchecked@*/ /*@observer@*/ 
00458 static const struct spew_s _yaml_spew = {
00459     .spew_name          = "yaml",
00460     .spew_init          = "- !!omap\n",
00461     .spew_fini          = "\n",
00462     .spew_strlen        = yamlstrlen,
00463     .spew_strcpy        = yamlstrcpy
00464 };
00465 
00466 /*====================================================================*/
00467 
00474 static size_t jsonstrlen(const char * s, /*@unused@*/ int lvl)
00475         /*@*/
00476 {
00477     size_t len = 0;
00478     int c;
00479 
00480     while ((c = (int) *s++) != (int) '\0') {
00481         switch (c) {
00482         case '\b':
00483         case '\t':
00484         case '\n':
00485         case '\v':
00486         case '\f':
00487         case '\r':
00488         case '\"':
00489         case '\'':      len += 1;                       /*@fallthrough@*/
00490         default:        len += 1;                       /*@switchbreak@*/ break;
00491         }
00492     }
00493     return len;
00494 }
00495 
00503 static char * jsonstrcpy(/*@returned@*/ char * t, const char * s,
00504                 /*@unused@*/ int lvl)
00505         /*@modifies t @*/
00506 {
00507     char * te = t;
00508     int c;
00509 
00510     while ((c = (int) *s++) != (int) '\0') {
00511         switch (c) {
00512         case '\b':      *te++ = '\\'; *te++ = 'b';      /*@switchbreak@*/ break;
00513         case '\t':      *te++ = '\\'; *te++ = 't';      /*@switchbreak@*/ break;
00514         case '\n':      *te++ = '\\'; *te++ = 'n';      /*@switchbreak@*/ break;
00515         case '\v':      *te++ = '\\'; *te++ = 'v';      /*@switchbreak@*/ break;
00516         case '\f':      *te++ = '\\'; *te++ = 'f';      /*@switchbreak@*/ break;
00517         case '\r':      *te++ = '\\'; *te++ = 'r';      /*@switchbreak@*/ break;
00518         case '\"':      *te++ = '\\'; *te++ = '"';      /*@switchbreak@*/ break;
00519         case '\'':      *te++ = '\\'; *te++ = '\'';     /*@switchbreak@*/ break;
00520         default:        *te++ = (char) c;               /*@switchbreak@*/ break;
00521         }
00522     }
00523     *te = '\0';
00524     return t;
00525 }
00526 
00527 /*@unchecked@*/ /*@observer@*/ 
00528 static const struct spew_s _json_spew = {
00529     .spew_name          = "json",
00530     .spew_init          = "db.Packages.save({\n",
00531     .spew_fini          = "});\n",
00532     .spew_strlen        = jsonstrlen,
00533     .spew_strcpy        = jsonstrcpy
00534 };
00535 
00536 /*====================================================================*/
00537 
00544 static size_t sqlstrlen(const char * s, /*@unused@*/ int lvl)
00545         /*@*/
00546 {
00547     size_t len = 0;
00548     int c;
00549 
00550     while ((c = (int) *s++) != (int) '\0') {
00551         switch (c) {
00552         case '\'':      len += 1;                       /*@fallthrough@*/
00553         default:        len += 1;                       /*@switchbreak@*/ break;
00554         }
00555     }
00556     return len;
00557 }
00558 
00566 static char * sqlstrcpy(/*@returned@*/ char * t, const char * s,
00567                 /*@unused@*/ int lvl)
00568         /*@modifies t @*/
00569 {
00570     char * te = t;
00571     int c;
00572 
00573     while ((c = (int) *s++) != (int) '\0') {
00574         switch (c) {
00575         case '\'':      *te++ = (char) c;               /*@fallthrough@*/
00576         default:        *te++ = (char) c;               /*@switchbreak@*/ break;
00577         }
00578     }
00579     *te = '\0';
00580     return t;
00581 }
00582 
00583 /*@unchecked@*/ /*@observer@*/ 
00584 static const struct spew_s _sql_spew = {
00585     .spew_name          = "sql",
00586     .spew_init          = "",
00587     .spew_fini          = "",
00588     .spew_strlen        = sqlstrlen,
00589     .spew_strcpy        = sqlstrcpy
00590 };
00591 
00592 /*====================================================================*/
00593 
00594 /* XXX FIXME: static for now, refactor from manifest.c later. */
00595 static char * rpmPermsString(int mode)
00596         /*@*/
00597 {
00598     char *perms = xstrdup("----------");
00599    
00600     if (S_ISREG(mode)) 
00601         perms[0] = '-';
00602     else if (S_ISDIR(mode)) 
00603         perms[0] = 'd';
00604     else if (S_ISLNK(mode))
00605         perms[0] = 'l';
00606     else if (S_ISFIFO(mode)) 
00607         perms[0] = 'p';
00608 /*@-unrecog@*/
00609     else if (S_ISSOCK(mode)) 
00610         perms[0] = 's';
00611 /*@=unrecog@*/
00612     else if (S_ISCHR(mode))
00613         perms[0] = 'c';
00614     else if (S_ISBLK(mode))
00615         perms[0] = 'b';
00616     else
00617         perms[0] = '?';
00618 
00619     if (mode & S_IRUSR) perms[1] = 'r';
00620     if (mode & S_IWUSR) perms[2] = 'w';
00621     if (mode & S_IXUSR) perms[3] = 'x';
00622  
00623     if (mode & S_IRGRP) perms[4] = 'r';
00624     if (mode & S_IWGRP) perms[5] = 'w';
00625     if (mode & S_IXGRP) perms[6] = 'x';
00626 
00627     if (mode & S_IROTH) perms[7] = 'r';
00628     if (mode & S_IWOTH) perms[8] = 'w';
00629     if (mode & S_IXOTH) perms[9] = 'x';
00630 
00631     if (mode & S_ISUID)
00632         perms[3] = ((mode & S_IXUSR) ? 's' : 'S'); 
00633 
00634     if (mode & S_ISGID)
00635         perms[6] = ((mode & S_IXGRP) ? 's' : 'S'); 
00636 
00637     if (mode & S_ISVTX)
00638         perms[9] = ((mode & S_IXOTH) ? 't' : 'T');
00639 
00640     return perms;
00641 }
00642 
00649 static /*@only@*/ char * triggertypeFormat(HE_t he, /*@unused@*/ /*@null@*/ const char ** av)
00650         /*@*/
00651 {
00652     int ix = (he->ix > 0 ? he->ix : 0);
00653     char * val;
00654 
00655 assert(ix == 0);
00656     if (he->t != RPM_UINT64_TYPE)
00657         val = xstrdup(_("(invalid type)"));
00658     else {
00659         rpmuint64_t anint = he->p.ui64p[ix];
00660         if (anint & RPMSENSE_TRIGGERPREIN)
00661             val = xstrdup("prein");
00662         else if (anint & RPMSENSE_TRIGGERIN)
00663             val = xstrdup("in");
00664         else if (anint & RPMSENSE_TRIGGERUN)
00665             val = xstrdup("un");
00666         else if (anint & RPMSENSE_TRIGGERPOSTUN)
00667             val = xstrdup("postun");
00668         else
00669             val = xstrdup("");
00670     }
00671     return val;
00672 }
00673 
00680 static /*@only@*/ char * permsFormat(HE_t he, /*@unused@*/ /*@null@*/ const char ** av)
00681         /*@*/
00682 {
00683     int ix = (he->ix > 0 ? he->ix : 0);
00684     char * val;
00685 
00686 assert(ix == 0);
00687     if (he->t != RPM_UINT64_TYPE) {
00688         val = xstrdup(_("(invalid type)"));
00689     } else {
00690         rpmuint64_t anint = he->p.ui64p[0];
00691         val = rpmPermsString((int)anint);
00692     }
00693 
00694     return val;
00695 }
00696 
00703 static /*@only@*/ char * fflagsFormat(HE_t he, /*@unused@*/ /*@null@*/ const char ** av)
00704         /*@*/
00705 {
00706     int ix = (he->ix >= 0 ? he->ix : 0);
00707     char * val;
00708 
00709 assert(ix == 0);
00710     if (he->t != RPM_UINT64_TYPE) {
00711         val = xstrdup(_("(invalid type)"));
00712     } else {
00713         char buf[15];
00714         rpmuint64_t anint = he->p.ui64p[ix];
00715         buf[0] = '\0';
00716         if (anint & RPMFILE_DOC)
00717             strcat(buf, "d");
00718         if (anint & RPMFILE_CONFIG)
00719             strcat(buf, "c");
00720         if (anint & RPMFILE_SPECFILE)
00721             strcat(buf, "s");
00722         if (anint & RPMFILE_MISSINGOK)
00723             strcat(buf, "m");
00724         if (anint & RPMFILE_NOREPLACE)
00725             strcat(buf, "n");
00726         if (anint & RPMFILE_GHOST)
00727             strcat(buf, "g");
00728         if (anint & RPMFILE_LICENSE)
00729             strcat(buf, "l");
00730         if (anint & RPMFILE_README)
00731             strcat(buf, "r");
00732         val = xstrdup(buf);
00733     }
00734 
00735     return val;
00736 }
00737 
00745 static /*@only@*/ char * armorFormat(HE_t he, /*@unused@*/ /*@null@*/ const char ** av)
00746         /*@*/
00747 {
00748     int ix = (he->ix > 0 ? he->ix : 0);
00749     const char * enc;
00750     const unsigned char * s;
00751     size_t ns;
00752     rpmuint8_t atype;
00753     char * val;
00754 
00755 assert(ix == 0);
00756     switch (he->t) {
00757     case RPM_BIN_TYPE:
00758         s = (unsigned char *) he->p.ui8p;
00759         ns = he->c;
00760         atype = (rpmuint8_t)PGPARMOR_SIGNATURE; /* XXX check pkt for signature */
00761         break;
00762     case RPM_STRING_TYPE:
00763     case RPM_STRING_ARRAY_TYPE:
00764         enc = he->p.str;
00765         s = NULL;
00766         ns = 0;
00767 /*@-moduncon@*/
00768         if (b64decode(enc, (void *)&s, &ns))
00769             return xstrdup(_("(not base64)"));
00770 /*@=moduncon@*/
00771         atype = (rpmuint8_t)PGPARMOR_PUBKEY;    /* XXX check pkt for pubkey */
00772         break;
00773     case RPM_UINT8_TYPE:
00774     case RPM_UINT16_TYPE:
00775     case RPM_UINT32_TYPE:
00776     case RPM_UINT64_TYPE:
00777     case RPM_I18NSTRING_TYPE:
00778     default:
00779         return xstrdup(_("(invalid type)"));
00780         /*@notreached@*/ break;
00781     }
00782 
00783     val = pgpArmorWrap(atype, s, ns);
00784     if (atype == (rpmuint8_t)PGPARMOR_PUBKEY)
00785         s = _free(s);
00786     return val;
00787 }
00788 
00796 static /*@only@*/ char * base64Format(HE_t he, /*@unused@*/ /*@null@*/ const char ** av)
00797         /*@*/
00798 {
00799     int ix = (he->ix > 0 ? he->ix : 0);
00800     char * val;
00801     const char * enc;
00802     char * t;
00803     int lc;
00804     size_t ns;
00805     size_t nt;
00806 
00807 assert(ix == 0);
00808     switch(he->t) {
00809     default:
00810         val = xstrdup(_("(invalid type :base64)"));
00811         goto exit;
00812         /*@notreached@*/ break;
00813     case RPM_UINT64_TYPE:
00814         ns = sizeof(he->p.ui64p[0]);
00815         break;
00816     case RPM_STRING_TYPE:
00817         ns = strlen(he->p.str);
00818         break;
00819     case RPM_BIN_TYPE:
00820         ns = he->c;
00821         break;
00822     }
00823 
00824     nt = ((ns + 2) / 3) * 4;
00825 
00826 /*@-globs@*/
00827     /* Add additional bytes necessary for eol string(s). */
00828     if (b64encode_chars_per_line > 0 && b64encode_eolstr != NULL) {
00829         lc = (nt + b64encode_chars_per_line - 1) / b64encode_chars_per_line;
00830         if (((nt + b64encode_chars_per_line - 1) % b64encode_chars_per_line) != 0)
00831             ++lc;
00832         nt += lc * strlen(b64encode_eolstr);
00833     }
00834 /*@=globs@*/
00835 
00836     val = t = xcalloc(1, nt + 1);
00837     *t = '\0';
00838 
00839     /* XXX b64encode accesses uninitialized memory. */
00840     {   unsigned char * _data = xcalloc(1, ns+1);
00841 assert(he->p.ptr != NULL);
00842         memcpy(_data, he->p.ptr, ns);
00843 /*@-moduncon@*/
00844         if ((enc = b64encode(_data, ns)) != NULL) {
00845             t = stpcpy(t, enc);
00846             enc = _free(enc);
00847         }
00848 /*@=moduncon@*/
00849         _data = _free(_data);
00850     }
00851 
00852 exit:
00853 /*@-globstate@*/        /* b64encode_eolstr annotation */
00854     return val;
00855 /*@=globstate@*/
00856 }
00857 
00858 /*====================================================================*/
00859 
00860 static /*@only@*/ /*@null@*/ char *
00861 strdup_locale_convert (/*@null@*/ const char * buffer,
00862                 /*@null@*/ const char * tocode)
00863         /*@*/
00864 {
00865     char *dest_str;
00866 #if defined(HAVE_ICONV)
00867     char *fromcode = NULL;
00868     iconv_t fd;
00869 
00870     if (buffer == NULL)
00871         return NULL;
00872 
00873     if (tocode == NULL)
00874         tocode = "UTF-8";
00875 
00876 #ifdef HAVE_LANGINFO_H
00877     fromcode = nl_langinfo (CODESET);
00878 #endif
00879 
00880     if (fromcode != NULL && strcmp(tocode, fromcode) != 0
00881      && (fd = iconv_open(tocode, fromcode)) != (iconv_t)-1)
00882     {
00883         const char *pin = buffer;
00884         char *pout = NULL;
00885         size_t ib, ob, dest_size;
00886         int done;
00887         int is_error;
00888         size_t err;
00889         const char *shift_pin = NULL;
00890         int xx;
00891 
00892         err = iconv(fd, NULL, &ib, &pout, &ob);
00893         dest_size = ob = ib = strlen(buffer);
00894         dest_str = pout = malloc((dest_size + 1) * sizeof(*dest_str));
00895         if (dest_str)
00896             *dest_str = '\0';
00897         done = is_error = 0;
00898         if (pout != NULL)
00899         while (done == 0 && is_error == 0) {
00900             err = iconv(fd, (char **)&pin, &ib, &pout, &ob);
00901 
00902             if (err == (size_t)-1) {
00903                 switch (errno) {
00904                 case EINVAL:
00905                     done = 1;
00906                     /*@switchbreak@*/ break;
00907                 case E2BIG:
00908                 {   size_t used = (size_t)(pout - dest_str);
00909                     dest_size *= 2;
00910                     dest_str = realloc(dest_str, (dest_size + 1) * sizeof(*dest_str));
00911                     if (dest_str == NULL) {
00912                         is_error = 1;
00913                         continue;
00914                     }
00915                     pout = dest_str + used;
00916                     ob = dest_size - used;
00917                 }   /*@switchbreak@*/ break;
00918                 case EILSEQ:
00919                     is_error = 1;
00920                     /*@switchbreak@*/ break;
00921                 default:
00922                     is_error = 1;
00923                     /*@switchbreak@*/ break;
00924                 }
00925             } else {
00926                 if (shift_pin == NULL) {
00927                     shift_pin = pin;
00928                     pin = NULL;
00929                     ib = 0;
00930                 } else {
00931                     done = 1;
00932                 }
00933             }
00934         }
00935         xx = iconv_close(fd);
00936         if (pout)
00937             *pout = '\0';
00938         if (dest_str != NULL)
00939             dest_str = xstrdup(dest_str);
00940     } else
00941 #endif
00942     {
00943         dest_str = xstrdup((buffer ? buffer : ""));
00944     }
00945 
00946     return dest_str;
00947 }
00948 
00955 static /*@only@*/ char * cdataFormat(HE_t he, /*@null@*/ const char ** av)
00956         /*@*/
00957 {
00958     int ix = (he->ix > 0 ? he->ix : 0);
00959     char * val;
00960 int lvl = 0;
00961 spew_t spew = &_xml_spew;
00962 
00963 assert(ix == 0);
00964     if (he->t != RPM_STRING_TYPE) {
00965         val = xstrdup(_("(not a string)"));
00966     } else {
00967         const char * s = strdup_locale_convert(he->p.str, (av ? av[0] : NULL));
00968         size_t nb;
00969         char * t;
00970 
00971         if (s == NULL) {
00972             /* XXX better error msg? */
00973             val = xstrdup(_("(not a string)"));
00974             goto exit;
00975         }
00976         nb = spew->spew_strlen(s, lvl);
00977         val = t = xcalloc(1, nb + 1);
00978         t = spew->spew_strcpy(t, s, lvl);       t += strlen(t);
00979         *t = '\0';
00980         s = _free(s);
00981     }
00982 
00983 exit:
00984     return val;
00985 }
00986 
00993 static /*@only@*/ char * iconvFormat(HE_t he, /*@unused@*/ /*@null@*/ const char ** av)
00994         /*@*/
00995 {
00996     int ix = (he->ix > 0 ? he->ix : 0);
00997     char * val = NULL;
00998 
00999 assert(ix == 0);
01000     if (he->t == RPM_STRING_TYPE)
01001         val = strdup_locale_convert(he->p.str, (av ? av[0] : NULL));
01002     if (val == NULL)
01003         val = xstrdup(_("(not a string)"));
01004 
01005     return val;
01006 }
01007 
01014 static /*@only@*/ char * xmlFormat(HE_t he, /*@unused@*/ /*@null@*/ const char ** av)
01015         /*@*/
01016 {
01017     int ix = (he->ix > 0 ? he->ix : 0);
01018     const char * xtag = NULL;
01019     size_t nb;
01020     char * val;
01021     const char * s = NULL;
01022     char * t, * te;
01023     rpmuint64_t anint = 0;
01024     int freeit = 0;
01025     int xx;
01026 int lvl = 0;
01027 spew_t spew = &_xml_spew;
01028 
01029 assert(ix == 0);
01030 assert(he->t == RPM_STRING_TYPE || he->t == RPM_UINT64_TYPE || he->t == RPM_BIN_TYPE);
01031     switch (he->t) {
01032     case RPM_STRING_ARRAY_TYPE: /* XXX currently never happens */
01033         s = he->p.argv[ix];
01034         xtag = "string";
01035         /* XXX Force utf8 strings. */
01036         s = xstrdup(s);
01037         s = xstrtolocale(s);
01038         freeit = 1;
01039         break;
01040     case RPM_I18NSTRING_TYPE:   /* XXX currently never happens */
01041     case RPM_STRING_TYPE:
01042         s = he->p.str;
01043         xtag = "string";
01044         /* XXX Force utf8 strings. */
01045         s = xstrdup(s);
01046         s = xstrtolocale(s);
01047         freeit = 1;
01048         break;
01049     case RPM_BIN_TYPE:
01050 /*@-globs -mods@*/      /* Don't bother annotating beecrypt global mods */
01051     {   int cpl = b64encode_chars_per_line;
01052         b64encode_chars_per_line = 0;
01053 /*@-formatconst@*/
01054         s = base64Format(he, NULL);
01055 /*@=formatconst@*/
01056         b64encode_chars_per_line = cpl;
01057         xtag = "base64";
01058         freeit = 1;
01059     }   break;
01060 /*@=globs =mods@*/
01061     case RPM_UINT8_TYPE:
01062         anint = (rpmuint64_t)he->p.ui8p[ix];
01063         break;
01064     case RPM_UINT16_TYPE:
01065         anint = (rpmuint64_t)he->p.ui16p[ix];
01066         break;
01067     case RPM_UINT32_TYPE:
01068         anint = (rpmuint64_t)he->p.ui32p[ix];
01069         break;
01070     case RPM_UINT64_TYPE:
01071         anint = he->p.ui64p[ix];
01072         break;
01073     default:
01074         return xstrdup(_("(invalid xml type)"));
01075         /*@notreached@*/ break;
01076     }
01077 
01078     if (s == NULL) {
01079         int tlen = 64;
01080         t = memset(alloca(tlen+1), 0, tlen+1);
01081 /*@-duplicatequals@*/
01082         if (anint != 0)
01083             xx = snprintf(t, tlen, "%llu", (unsigned long long)anint);
01084 /*@=duplicatequals@*/
01085         s = t;
01086         xtag = "integer";
01087     }
01088 
01089     nb = spew->spew_strlen(s, lvl);
01090     if (nb == 0) {
01091         nb += strlen(xtag) + sizeof("\t</>");
01092         te = t = alloca(nb);
01093         te = stpcpy( stpcpy( stpcpy(te, "\t<"), xtag), "/>");
01094     } else {
01095         nb += 2 * strlen(xtag) + sizeof("\t<></>");
01096         te = t = alloca(nb);
01097         te = stpcpy( stpcpy( stpcpy(te, "\t<"), xtag), ">");
01098         te = spew->spew_strcpy(te, s, lvl);
01099         te += strlen(te);
01100         te = stpcpy( stpcpy( stpcpy(te, "</"), xtag), ">");
01101     }
01102 
01103     if (freeit)
01104         s = _free(s);
01105 
01106     val = xstrdup(t);
01107 
01108     return val;
01109 }
01110 
01117 static /*@only@*/ char * yamlFormat(HE_t he, /*@unused@*/ /*@null@*/ const char ** av)
01118         /*@*/
01119 {
01120     int element = he->ix;
01121     int ix = (he->ix > 0 ? he->ix : 0);
01122     const char * xtag = NULL;
01123     int freetag = 0;
01124     size_t nb;
01125     char * val;
01126     const char * s = NULL;
01127     char * t, * te;
01128     rpmuint64_t anint = 0;
01129     int freeit = 0;
01130     int xx;
01131     int ls;
01132     int c;
01133 int lvl = 0;
01134 spew_t spew = &_yaml_spew;
01135 
01136 assert(ix == 0);
01137 assert(he->t == RPM_STRING_TYPE || he->t == RPM_UINT64_TYPE || he->t == RPM_BIN_TYPE);
01138     xx = 0;
01139     ls = 0;
01140     switch (he->t) {
01141     case RPM_STRING_ARRAY_TYPE: /* XXX currently never happens */
01142     case RPM_I18NSTRING_TYPE:   /* XXX currently never happens */
01143     case RPM_STRING_TYPE:
01144         s = (he->t == RPM_STRING_ARRAY_TYPE ? he->p.argv[ix] : he->p.str);
01145         if (strchr("[", s[0]))  /* leading [ */
01146             xx = 1;
01147         if (xx == 0)
01148         while ((c = (int) *s++) != (int) '\0') {
01149             switch (c) {
01150             default:
01151                 continue;
01152             case '\n':  /* multiline */
01153                 xx = 1;
01154                 if (s[0] == ' ' || s[0] == '\t') /* leading space */
01155                     ls = 1;
01156                 continue;
01157             case '-':   /* leading "- \"" */
01158             case ':':   /* embedded ": " or ":" at EOL */
01159                 if (s[0] != ' ' && s[0] != '\0' && s[1] != '"')
01160                     continue;
01161                 xx = 1;
01162                 /*@switchbreak@*/ break;
01163             }
01164             /*@loopbreak@*/ break;
01165         }
01166         if (xx) {
01167             if (ls) { /* leading spaces means we need to specify the indent */
01168                 xtag = xmalloc(strlen("- |##-\n") + 1);
01169                 freetag = 1;
01170                 if (element >= 0) {
01171                     lvl = 3;
01172                     sprintf((char *)xtag, "- |%d-\n", lvl);
01173                 } else {
01174                     lvl = 2;
01175                     if (he->ix < 0) lvl++;  /* XXX extra indent for array[1] */
01176                     sprintf((char *)xtag, "|%d-\n", lvl);
01177                 }
01178             } else {
01179                 if (element >= 0) {
01180                     xtag = "- |-\n";
01181                     lvl = 3;
01182                 } else {
01183                     xtag = "|-\n";
01184                     lvl = 2;
01185                     if (he->ix < 0) lvl++;  /* XXX extra indent for array[1] */
01186                 }
01187             }
01188         } else {
01189             xtag = (element >= 0 ? "- " : NULL);
01190         }
01191 
01192         /* XXX Force utf8 strings. */
01193         s = xstrdup(he->p.str);
01194         s = xstrtolocale(s);
01195         freeit = 1;
01196         break;
01197     case RPM_BIN_TYPE:
01198 /*@-globs -mods@*/      /* Don't bother annotating beecrypt global mods */
01199     {   int cpl = b64encode_chars_per_line;
01200         b64encode_chars_per_line = 0;
01201 /*@-formatconst@*/
01202         s = base64Format(he, NULL);
01203         element = -element;     /* XXX skip "    " indent. */
01204 /*@=formatconst@*/
01205         b64encode_chars_per_line = cpl;
01206         xtag = "!!binary ";
01207         freeit = 1;
01208     }   break;
01209 /*@=globs =mods@*/
01210     case RPM_UINT8_TYPE:
01211         anint = (rpmuint64_t)he->p.ui8p[ix];
01212         break;
01213     case RPM_UINT16_TYPE:
01214         anint = (rpmuint64_t)he->p.ui16p[ix];
01215         break;
01216     case RPM_UINT32_TYPE:
01217         anint = (rpmuint64_t)he->p.ui32p[ix];
01218         break;
01219     case RPM_UINT64_TYPE:
01220         anint = he->p.ui64p[ix];
01221         break;
01222     default:
01223         return xstrdup(_("(invalid yaml type)"));
01224         /*@notreached@*/ break;
01225     }
01226 
01227     if (s == NULL) {
01228         int tlen = 64;
01229         t = memset(alloca(tlen+1), 0, tlen+1);
01230 /*@-duplicatequals@*/
01231         xx = snprintf(t, tlen, "%llu", (unsigned long long)anint);
01232 /*@=duplicatequals@*/
01233         s = t;
01234         xtag = (element >= 0 ? "- " : NULL);
01235     }
01236 
01237     nb = spew->spew_strlen(s, lvl);
01238     if (nb == 0) {
01239         if (element >= 0)
01240             nb += sizeof("    ") - 1;
01241         nb += sizeof("- ~") - 1;
01242         nb++;
01243         te = t = alloca(nb);
01244         if (element >= 0)
01245             te = stpcpy(te, "    ");
01246         te = stpcpy(te, "- ~");
01247     } else {
01248         if (element >= 0)
01249             nb += sizeof("    ") - 1;
01250         if (xtag)
01251             nb += strlen(xtag);
01252         nb++;
01253         te = t = alloca(nb);
01254         if (element >= 0)
01255             te = stpcpy(te, "    ");
01256         if (xtag)
01257             te = stpcpy(te, xtag);
01258 /*@-modobserver -observertrans -statictrans @*/ /* XXX LCL: can't see freetag flow */
01259             if (freetag)
01260                 xtag = _free(xtag);
01261 /*@=modobserver =observertrans =statictrans @*/
01262         te = spew->spew_strcpy(te, s, lvl);
01263         te += strlen(te);
01264     }
01265 
01266     /* XXX s was malloc'd */
01267     if (freeit)
01268         s = _free(s);
01269 
01270     val = xstrdup(t);
01271 
01272     return val;
01273 }
01274 
01281 static /*@only@*/ char * jsonFormat(HE_t he, /*@unused@*/ /*@null@*/ const char ** av)
01282         /*@*/
01283 {
01284     int element = he->ix;
01285     int ix = (he->ix > 0 ? he->ix : 0);
01286     size_t nb;
01287     char * val;
01288     const char * s = NULL;
01289     char * t, * te;
01290     rpmuint64_t anint = 0;
01291     int freeit = 0;
01292     int xx = 0;
01293     int c;
01294 int lvl = 0;
01295 spew_t spew = &_json_spew;
01296 
01297 assert(ix == 0);
01298 assert(he->t == RPM_STRING_TYPE || he->t == RPM_UINT64_TYPE || he->t == RPM_BIN_TYPE);
01299     xx = 0;
01300     switch (he->t) {
01301     case RPM_STRING_ARRAY_TYPE: /* XXX currently never happens */
01302     case RPM_I18NSTRING_TYPE:   /* XXX currently never happens */
01303     case RPM_STRING_TYPE:
01304         s = (he->t == RPM_STRING_ARRAY_TYPE ? he->p.argv[ix] : he->p.str);
01305         /* XXX Force utf8 strings. */
01306         s = xstrdup(he->p.str);
01307         s = xstrtolocale(s);
01308         freeit = 1;
01309         break;
01310     case RPM_BIN_TYPE:
01311 /*@-globs -mods@*/      /* Don't bother annotating beecrypt global mods */
01312     {   int cpl = b64encode_chars_per_line;
01313         b64encode_chars_per_line = 0;
01314 /*@-formatconst@*/
01315         s = base64Format(he, NULL);
01316         element = -element;     /* XXX skip "    " indent. */
01317 /*@=formatconst@*/
01318         b64encode_chars_per_line = cpl;
01319         freeit = 1;
01320     }   break;
01321 /*@=globs =mods@*/
01322     case RPM_UINT8_TYPE:
01323         anint = (rpmuint64_t)he->p.ui8p[ix];
01324         break;
01325     case RPM_UINT16_TYPE:
01326         anint = (rpmuint64_t)he->p.ui16p[ix];
01327         break;
01328     case RPM_UINT32_TYPE:
01329         anint = (rpmuint64_t)he->p.ui32p[ix];
01330         break;
01331     case RPM_UINT64_TYPE:
01332         anint = he->p.ui64p[ix];
01333         break;
01334     default:
01335         return xstrdup(_("(invalid json type)"));
01336         /*@notreached@*/ break;
01337     }
01338 
01339     if (s == NULL) {
01340         int tlen = 64;
01341         t = memset(alloca(tlen+1), 0, tlen+1);
01342 /*@-duplicatequals@*/
01343         xx = snprintf(t, tlen, "%llu", (unsigned long long)anint);
01344 /*@=duplicatequals@*/
01345         s = t;
01346         c = '\0';
01347     } else
01348         c = '\'';
01349 
01350     nb = spew->spew_strlen(s, lvl);
01351     if (c != '\0')
01352         nb += 2;
01353     nb += sizeof("\t,") - 1;
01354     te = t = alloca(nb);
01355     *te++ = '\t';
01356     if (c != '\0')      *te++ = c;
01357     if (nb) {
01358         te = spew->spew_strcpy(te, s, lvl);
01359         te += strlen(te);
01360     }
01361     if (c != '\0') *te++ = c;
01362     *te++ = ',';
01363     *te = '\0';
01364 
01365     /* XXX s was malloc'd */
01366     if (freeit)
01367         s = _free(s);
01368 
01369     val = xstrdup(t);
01370 
01371     return val;
01372 }
01373 
01374 /*====================================================================*/
01375 
01382 static /*@only@*/ char * pgpsigFormat(HE_t he, /*@unused@*/ /*@null@*/ const char ** av)
01383         /*@globals fileSystem, internalState @*/
01384         /*@modifies fileSystem, internalState @*/
01385 {
01386     int ix = (he->ix > 0 ? he->ix : 0);
01387     char * val, * t;
01388 
01389 assert(ix == 0);
01390     if (!(he->t == RPM_BIN_TYPE)) {
01391         val = xstrdup(_("(not a blob)"));
01392     } else {
01393         rpmuint8_t * pkt = he->p.ui8p;
01394         unsigned int pktlen = 0;
01395         unsigned int v = (unsigned int) *pkt;
01396         pgpTag tag = 0;
01397         unsigned int plen;
01398         unsigned int hlen = 0;
01399 
01400         if (v & 0x80) {
01401             if (v & 0x40) {
01402                 tag = (v & 0x3f);
01403                 plen = pgpLen(pkt+1, &hlen);
01404             } else {
01405                 tag = (v >> 2) & 0xf;
01406                 plen = (1 << (v & 0x3));
01407                 hlen = pgpGrab(pkt+1, plen);
01408             }
01409         
01410             pktlen = 1 + plen + hlen;
01411         }
01412 
01413         if (pktlen == 0 || tag != PGPTAG_SIGNATURE) {
01414             val = xstrdup(_("(not an OpenPGP signature)"));
01415         } else {
01416             pgpDig dig = pgpDigNew(RPMVSF_DEFAULT, 0);
01417             pgpDigParams sigp = pgpGetSignature(dig);
01418             size_t nb = 0;
01419             const char *tempstr;
01420 
01421             (void) pgpPrtPkts(pkt, pktlen, dig, 0);
01422 
01423             val = NULL;
01424         again:
01425             nb += 100;
01426             val = t = xrealloc(val, nb + 1);
01427 
01428             switch (sigp->pubkey_algo) {
01429             case PGPPUBKEYALGO_DSA:
01430                 t = stpcpy(t, "DSA");
01431                 break;
01432             case PGPPUBKEYALGO_RSA:
01433                 t = stpcpy(t, "RSA");
01434                 break;
01435             default:
01436                 (void) snprintf(t, nb - (t - val), "%u", (unsigned)sigp->pubkey_algo);
01437                 t += strlen(t);
01438                 break;
01439             }
01440             if (t + 5 >= val + nb)
01441                 goto again;
01442             *t++ = '/';
01443             switch (sigp->hash_algo) {
01444             case PGPHASHALGO_MD5:
01445                 t = stpcpy(t, "MD5");
01446                 break;
01447             case PGPHASHALGO_SHA1:
01448                 t = stpcpy(t, "SHA1");
01449                 break;
01450             default:
01451                 (void) snprintf(t, nb - (t - val), "%u", (unsigned)sigp->hash_algo);
01452                 t += strlen(t);
01453                 break;
01454             }
01455             if (t + strlen (", ") + 1 >= val + nb)
01456                 goto again;
01457 
01458             t = stpcpy(t, ", ");
01459 
01460             /* this is important if sizeof(rpmuint32_t) ! sizeof(time_t) */
01461             {   time_t dateint = pgpGrab(sigp->time, sizeof(sigp->time));
01462                 struct tm * tstruct = localtime(&dateint);
01463                 if (tstruct)
01464                     (void) strftime(t, (nb - (t - val)), "%c", tstruct);
01465             }
01466             t += strlen(t);
01467             if (t + strlen (", Key ID ") + 1 >= val + nb)
01468                 goto again;
01469             t = stpcpy(t, ", Key ID ");
01470             tempstr = pgpHexStr(sigp->signid, sizeof(sigp->signid));
01471             if (t + strlen (tempstr) > val + nb)
01472                 goto again;
01473             t = stpcpy(t, tempstr);
01474 
01475             dig = pgpDigFree(dig);
01476         }
01477     }
01478 
01479     return val;
01480 }
01481 
01488 static /*@only@*/
01489 char * depflagsFormat(HE_t he, /*@unused@*/ /*@null@*/ const char ** av)
01490         /*@*/
01491 {
01492     int ix = (he->ix > 0 ? he->ix : 0);
01493     char * val;
01494 
01495 assert(ix == 0);
01496     if (he->t != RPM_UINT64_TYPE) {
01497         val = xstrdup(_("(invalid type)"));
01498     } else {
01499         rpmuint64_t anint = he->p.ui64p[ix];
01500         char *t, *buf;
01501 
01502         t = buf = alloca(32);
01503         *t = '\0';
01504 
01505 #ifdef  NOTYET  /* XXX appending markers breaks :depflags format. */
01506         if (anint & RPMSENSE_SCRIPT_PRE)
01507             t = stpcpy(t, "(pre)");
01508         else if (anint & RPMSENSE_SCRIPT_POST)
01509             t = stpcpy(t, "(post)");
01510         else if (anint & RPMSENSE_SCRIPT_PREUN)
01511             t = stpcpy(t, "(preun)");
01512         else if (anint & RPMSENSE_SCRIPT_POSTUN)
01513             t = stpcpy(t, "(postun)");
01514 #endif
01515         if (anint & RPMSENSE_SENSEMASK)
01516             *t++ = ' ';
01517         if (anint & RPMSENSE_LESS)
01518             *t++ = '<';
01519         if (anint & RPMSENSE_GREATER)
01520             *t++ = '>';
01521         if (anint & RPMSENSE_EQUAL)
01522             *t++ = '=';
01523         if (anint & RPMSENSE_SENSEMASK)
01524             *t++ = ' ';
01525         *t = '\0';
01526 
01527         val = xstrdup(buf);
01528     }
01529 
01530     return val;
01531 }
01532 
01540 static /*@only@*/
01541 char * deptypeFormat(HE_t he, /*@unused@*/ /*@null@*/ const char ** av)
01542         /*@*/
01543 {
01544     int ix = (he->ix > 0 ? he->ix : 0);
01545     char * val;
01546 
01547 assert(ix == 0);
01548     if (he->t != RPM_UINT64_TYPE) {
01549         val = xstrdup(_("(invalid type)"));
01550     } else {
01551         rpmuint64_t anint = he->p.ui64p[ix];
01552         char *t, *buf;
01553 
01554         t = buf = alloca(32);
01555         *t = '\0';
01556 
01557         if (anint & RPMSENSE_SCRIPT_PRE)
01558             t = stpcpy(t, "pre");
01559         else if (anint & RPMSENSE_SCRIPT_POST)
01560             t = stpcpy(t, "post");
01561         else if (anint & RPMSENSE_SCRIPT_PREUN)
01562             t = stpcpy(t, "preun");
01563         else if (anint & RPMSENSE_SCRIPT_POSTUN)
01564             t = stpcpy(t, "postun");
01565         else if (anint & RPMSENSE_SCRIPT_VERIFY)
01566             t = stpcpy(t, "verify");
01567         else if (anint & RPMSENSE_RPMLIB)
01568             t = stpcpy(t, "rpmlib");
01569         else if (anint & RPMSENSE_INTERP)
01570             t = stpcpy(t, "interp");
01571         else if (anint & (RPMSENSE_FIND_PROVIDES | RPMSENSE_FIND_REQUIRES))
01572             t = stpcpy(t, "auto");
01573         else
01574             t = stpcpy(t, "manual");
01575         *t = '\0';
01576 
01577         val = xstrdup(buf);
01578     }
01579 
01580     return val;
01581 }
01582 
01589 static int instprefixTag(Header h, HE_t he)
01590         /*@globals internalState @*/
01591         /*@modifies he, internalState @*/
01592 {
01593     he->tag = RPMTAG_INSTALLPREFIX;
01594     if (headerGet(h, he, 0))
01595         return 0;
01596 
01597     he->tag = RPMTAG_INSTPREFIXES;
01598     if (headerGet(h, he, 0)) {
01599         rpmTagData array = { .argv = he->p.argv };
01600         he->t = RPM_STRING_TYPE;
01601         he->c = 1;
01602         he->p.str = xstrdup(array.argv[0]);
01603         he->freeData = 1;
01604         array.ptr = _free(array.ptr);
01605         return 0;
01606     }
01607     return 1;
01608 }
01609 
01617 static int tv2uuidv1(/*@unused@*/ Header h, HE_t he, struct timeval *tv)
01618         /*@modifies he @*/
01619 {
01620     rpmuint64_t uuid_time = ((rpmuint64_t)tv->tv_sec * 10000000) +
01621                         (tv->tv_usec * 10) + 0x01B21DD213814000ULL;
01622 
01623     he->t = RPM_BIN_TYPE;
01624     he->c = 128/8;
01625     he->p.ptr = xcalloc(1, he->c);
01626     he->freeData = 1;
01627     if (rpmuuidMake(1, NULL, NULL, NULL, (unsigned char *)he->p.ui8p)) {
01628         he->p.ptr = _free(he->p.ptr);
01629         he->freeData = 0;
01630         return 1;
01631     }
01632 
01633     he->p.ui8p[6] &= 0xf0;      /* preserve version, clear time_hi nibble */
01634     he->p.ui8p[8] &= 0xc0;      /* preserve variant, clear clock */
01635     he->p.ui8p[9] &= 0x00;
01636 
01637     he->p.ui8p[3] = (rpmuint8_t)(uuid_time >>  0);
01638     he->p.ui8p[2] = (rpmuint8_t)(uuid_time >>  8);
01639     he->p.ui8p[1] = (rpmuint8_t)(uuid_time >> 16);
01640     he->p.ui8p[0] = (rpmuint8_t)(uuid_time >> 24);
01641     he->p.ui8p[5] = (rpmuint8_t)(uuid_time >> 32);
01642     he->p.ui8p[4] = (rpmuint8_t)(uuid_time >> 40);
01643     he->p.ui8p[6] |= (rpmuint8_t)(uuid_time >> 56) & 0x0f;
01644 
01645 #ifdef  NOTYET
01646     /* XXX Jigger up a non-zero (but constant) clock value. Is this needed? */
01647     he->p.ui8p[8] |= (he->p.ui8p[2] & 0x3f);
01648     he->p.ui8p[9] |= he->p.ui8p[3]
01649 #endif
01650 
01651     return 0;
01652 }
01653 
01660 static int tag2uuidv1(Header h, HE_t he)
01661         /*@globals internalState @*/
01662         /*@modifies he, internalState @*/
01663 {
01664     struct timeval tv;
01665 
01666     if (!headerGet(h, he, 0))
01667         return 1;
01668     tv.tv_sec = (long) he->p.ui32p[0];
01669     tv.tv_usec = (long) (he->c > 1 ? he->p.ui32p[1] : 0);
01670     he->p.ptr = _free(he->p.ptr);
01671     return tv2uuidv1(h, he, &tv);
01672 }
01673 
01680 static int installtime_uuidTag(Header h, HE_t he)
01681         /*@globals internalState @*/
01682         /*@modifies he, internalState @*/
01683 {
01684     he->tag = RPMTAG_INSTALLTIME;
01685     return tag2uuidv1(h, he);
01686 }
01687 
01694 static int buildtime_uuidTag(Header h, HE_t he)
01695         /*@globals internalState @*/
01696         /*@modifies he, internalState @*/
01697 {
01698     he->tag = RPMTAG_BUILDTIME;
01699     return tag2uuidv1(h, he);
01700 }
01701 
01708 static int origintime_uuidTag(Header h, HE_t he)
01709         /*@globals internalState @*/
01710         /*@modifies he, internalState @*/
01711 {
01712     he->tag = RPMTAG_ORIGINTIME;
01713     return tag2uuidv1(h, he);
01714 }
01715 
01722 static int installtid_uuidTag(Header h, HE_t he)
01723         /*@globals internalState @*/
01724         /*@modifies he, internalState @*/
01725 {
01726     he->tag = RPMTAG_INSTALLTID;
01727     return tag2uuidv1(h, he);
01728 }
01729 
01736 static int removetid_uuidTag(Header h, HE_t he)
01737         /*@globals internalState @*/
01738         /*@modifies he, internalState @*/
01739 {
01740     he->tag = RPMTAG_REMOVETID;
01741     return tag2uuidv1(h, he);
01742 }
01743 
01750 static int origintid_uuidTag(Header h, HE_t he)
01751         /*@globals internalState @*/
01752         /*@modifies he, internalState @*/
01753 {
01754     he->tag = RPMTAG_ORIGINTID;
01755     return tag2uuidv1(h, he);
01756 }
01757 
01758 /*@unchecked@*/ /*@observer@*/
01759 static const char uuid_ns[] = "ns:URL";
01760 /*@unchecked@*/ /*@observer@*/
01761 static const char uuid_auth[] = "%{?_uuid_auth}%{!?_uuid_auth:http://rpm5.org}";
01762 /*@unchecked@*/ /*@observer@*/
01763 static const char uuid_path[] = "%{?_uuid_path}%{!?_uuid_path:/package}";
01764 /*@unchecked@*/
01765 static rpmuint32_t uuid_version = 5;
01766 
01775 static int str2uuid(HE_t he, /*@unused@*/ /*@null@*/ const char ** av,
01776                 rpmuint32_t version, char * val)
01777         /*@globals rpmGlobalMacroContext, h_errno, internalState @*/
01778         /*@modifies he, rpmGlobalMacroContext, internalState @*/
01779 {
01780     const char * ns = NULL;
01781     const char * tagn = tagName(he->tag);
01782     const char * s = NULL;
01783 char * t = (val ? val : alloca(40));
01784     int rc;
01785 
01786     /* XXX Substitute Pkgid & Hdrid strings for aliases. */
01787     if (!strcmp("Sigmd5", tagn))
01788         tagn = "Pkgid";
01789     else if (!strcmp("Sha1header", tagn))
01790         tagn = "Hdrid";
01791 
01792     switch (version) {
01793     default:
01794         version = uuid_version;
01795         /*@fallthrough@*/
01796     case 3:
01797     case 5:
01798 assert(he->t == RPM_STRING_TYPE);
01799         ns = uuid_ns;
01800         s = rpmGetPath(uuid_auth, "/", uuid_path, "/", tagn, "/",
01801                         he->p.str, NULL);
01802         /*@fallthrough@*/
01803     case 4:
01804         break;
01805     }
01806     he->p.ptr = _free(he->p.ptr);
01807     he->t = RPM_BIN_TYPE;
01808     he->c = 128/8;
01809     he->p.ptr = xcalloc(1, he->c);
01810     he->freeData = 1;
01811     rc = rpmuuidMake((int)version, ns, s, t, (unsigned char *)he->p.ui8p);
01812     if (rc) {
01813         he->p.ptr = _free(he->p.ptr);
01814         he->freeData = 0;
01815     }
01816     s = _free(s);
01817 
01818     return rc;
01819 }
01820 
01827 static int tag2uuidv5(Header h, HE_t he)
01828         /*@globals rpmGlobalMacroContext, h_errno, internalState @*/
01829         /*@modifies he, rpmGlobalMacroContext, internalState @*/
01830 {
01831     if (!headerGet(h, he, 0))
01832         return 1;
01833     switch (he->t) {
01834     default:
01835 assert(0);
01836         /*@notreached@*/ break;
01837     case RPM_BIN_TYPE:  {       /* Convert RPMTAG_PKGID from binary => hex. */
01838         static const char hex[] = "0123456789abcdef";
01839         char * t;
01840         char * te;
01841         rpmuint32_t i;
01842 
01843         t = te = xmalloc (2*he->c + 1);
01844         for (i = 0; i < he->c; i++) {
01845             *te++ = hex[ (int)((he->p.ui8p[i] >> 4) & 0x0f) ];
01846             *te++ = hex[ (int)((he->p.ui8p[i]     ) & 0x0f) ];
01847         }
01848         *te = '\0';
01849         he->p.ptr = _free(he->p.ptr);
01850         he->t = RPM_STRING_TYPE;
01851         he->p.ptr = t;
01852         he->c = 1;
01853         he->freeData = 1;
01854     }   break;
01855     case RPM_STRING_TYPE:
01856         break;
01857     }
01858     return str2uuid(he, NULL, 0, NULL);
01859 }
01860 
01867 static int pkguuidTag(Header h, HE_t he)
01868         /*@globals rpmGlobalMacroContext, h_errno, internalState @*/
01869         /*@modifies he, rpmGlobalMacroContext, internalState @*/
01870 {
01871     he->tag = RPMTAG_PKGID;
01872     return tag2uuidv5(h, he);
01873 }
01874 
01881 static int sourcepkguuidTag(Header h, HE_t he)
01882         /*@globals rpmGlobalMacroContext, h_errno, internalState @*/
01883         /*@modifies he, rpmGlobalMacroContext, internalState @*/
01884 {
01885     he->tag = RPMTAG_SOURCEPKGID;
01886     return tag2uuidv5(h, he);
01887 }
01888 
01895 static int hdruuidTag(Header h, HE_t he)
01896         /*@globals rpmGlobalMacroContext, h_errno, internalState @*/
01897         /*@modifies he, rpmGlobalMacroContext, internalState @*/
01898 {
01899     he->tag = RPMTAG_HDRID;
01900     return tag2uuidv5(h, he);
01901 }
01902 
01909 static int triggercondsTag(Header h, HE_t he)
01910         /*@globals internalState @*/
01911         /*@modifies he, internalState @*/
01912 {
01913     HE_t _he = memset(alloca(sizeof(*_he)), 0, sizeof(*_he));
01914     HE_t Fhe = memset(alloca(sizeof(*Fhe)), 0, sizeof(*Fhe));
01915     HE_t Ihe = memset(alloca(sizeof(*Ihe)), 0, sizeof(*Ihe));
01916     HE_t Nhe = memset(alloca(sizeof(*Nhe)), 0, sizeof(*Nhe));
01917     HE_t Vhe = memset(alloca(sizeof(*Vhe)), 0, sizeof(*Vhe));
01918     HE_t She = memset(alloca(sizeof(*She)), 0, sizeof(*She));
01919     rpmuint64_t anint;
01920     unsigned i, j;
01921     int rc = 1;         /* assume failure */
01922     int xx;
01923 
01924     he->freeData = 0;
01925 
01926     Nhe->tag = RPMTAG_TRIGGERNAME;
01927     xx = headerGet(h, Nhe, 0);
01928     if (!xx) {          /* no triggers, succeed anyways */
01929         rc = 0;
01930         goto exit;
01931     }
01932 
01933     Ihe->tag = RPMTAG_TRIGGERINDEX;
01934     xx = headerGet(h, Ihe, 0);
01935     if (!xx) goto exit;
01936 
01937     Fhe->tag = RPMTAG_TRIGGERFLAGS;
01938     xx = headerGet(h, Fhe, 0);
01939     if (!xx) goto exit;
01940 
01941     Vhe->tag = RPMTAG_TRIGGERVERSION;
01942     xx = headerGet(h, Vhe, 0);
01943     if (!xx) goto exit;
01944 
01945     She->tag = RPMTAG_TRIGGERSCRIPTS;
01946     xx = headerGet(h, She, 0);
01947     if (!xx) goto exit;
01948 
01949     _he->tag = he->tag;
01950     _he->t = RPM_UINT64_TYPE;
01951     _he->p.ui64p = &anint;
01952     _he->c = 1;
01953     _he->freeData = 0;
01954 
01955     he->t = RPM_STRING_ARRAY_TYPE;
01956     he->c = She->c;
01957 
01958     he->freeData = 1;
01959     he->p.argv = xmalloc(sizeof(*he->p.argv) * he->c);
01960     for (i = 0; i < (unsigned) he->c; i++) {
01961         char * item, * flagsStr;
01962         char * chptr;
01963 
01964         chptr = xstrdup("");
01965 
01966         for (j = 0; j < Nhe->c; j++) {
01967             if (Ihe->p.ui32p[j] != i)
01968                 /*@innercontinue@*/ continue;
01969 
01970             item = xmalloc(strlen(Nhe->p.argv[j]) + strlen(Vhe->p.argv[j]) + 20);
01971 /*@-compmempass@*/      /* use separate HE_t, not rpmTagData, containers. */
01972             if (Fhe->p.ui32p[j] & RPMSENSE_SENSEMASK) {
01973                 anint = Fhe->p.ui32p[j];
01974                 flagsStr = depflagsFormat(_he, NULL);
01975                 sprintf(item, "%s%s%s", Nhe->p.argv[j], flagsStr, Vhe->p.argv[j]);
01976                 flagsStr = _free(flagsStr);
01977             } else
01978                 strcpy(item, Nhe->p.argv[j]);
01979 /*@=compmempass@*/
01980 
01981             chptr = xrealloc(chptr, strlen(chptr) + strlen(item) + 5);
01982             if (*chptr != '\0') strcat(chptr, ", ");
01983             strcat(chptr, item);
01984             item = _free(item);
01985         }
01986 
01987         he->p.argv[i] = chptr;
01988     }
01989     rc = 0;
01990 
01991 exit:
01992     Ihe->p.ptr = _free(Ihe->p.ptr);
01993     Fhe->p.ptr = _free(Fhe->p.ptr);
01994     Nhe->p.ptr = _free(Nhe->p.ptr);
01995     Vhe->p.ptr = _free(Vhe->p.ptr);
01996     She->p.ptr = _free(She->p.ptr);
01997 
01998     return rc;
01999 }
02000 
02007 static int triggertypeTag(Header h, HE_t he)
02008         /*@globals internalState @*/
02009         /*@modifies he, internalState @*/
02010 {
02011     HE_t _he = memset(alloca(sizeof(*_he)), 0, sizeof(*_he));
02012     rpmTagData indices = { .ptr = NULL };
02013     rpmTagData flags = { .ptr = NULL };
02014     rpmTagData s = { .ptr = NULL };
02015     rpmTagCount numNames;
02016     rpmTagCount numScripts;
02017     unsigned i, j;
02018     int rc = 1;         /* assume failure */
02019     int xx;
02020 
02021     he->freeData = 0;
02022 
02023 /*@-compmempass@*/      /* use separate HE_t, not rpmTagData, containers. */
02024     _he->tag = RPMTAG_TRIGGERINDEX;
02025     xx = headerGet(h, _he, 0);
02026     if (!xx) goto exit;
02027     indices.ui32p = _he->p.ui32p;
02028     numNames = _he->c;
02029 
02030     _he->tag = RPMTAG_TRIGGERFLAGS;
02031     xx = headerGet(h, _he, 0);
02032     if (!xx) goto exit;
02033     flags.ui32p = _he->p.ui32p;
02034 
02035     _he->tag = RPMTAG_TRIGGERSCRIPTS;
02036     xx = headerGet(h, _he, 0);
02037     if (!xx) goto exit;
02038     s.argv = _he->p.argv;
02039     numScripts = _he->c;
02040 /*@=compmempass@*/
02041 
02042     he->t = RPM_STRING_ARRAY_TYPE;
02043     he->c = numScripts;
02044 
02045     he->freeData = 1;
02046     he->p.argv = xmalloc(sizeof(*he->p.argv) * he->c);
02047     for (i = 0; i < (unsigned) he->c; i++) {
02048         for (j = 0; j < (unsigned) numNames; j++) {
02049             if (indices.ui32p[j] != i)
02050                 /*@innercontinue@*/ continue;
02051 
02052             /* XXX FIXME: there's memory leaks here. */
02053             if (flags.ui32p[j] & RPMSENSE_TRIGGERPREIN)
02054                 he->p.argv[i] = xstrdup("prein");
02055             else if (flags.ui32p[j] & RPMSENSE_TRIGGERIN)
02056                 he->p.argv[i] = xstrdup("in");
02057             else if (flags.ui32p[j] & RPMSENSE_TRIGGERUN)
02058                 he->p.argv[i] = xstrdup("un");
02059             else if (flags.ui32p[j] & RPMSENSE_TRIGGERPOSTUN)
02060                 he->p.argv[i] = xstrdup("postun");
02061             else
02062                 he->p.argv[i] = xstrdup("");
02063             /*@innerbreak@*/ break;
02064         }
02065     }
02066     rc = 0;
02067 
02068 exit:
02069     indices.ptr = _free(indices.ptr);
02070     flags.ptr = _free(flags.ptr);
02071     s.ptr = _free(s.ptr);
02072     return 0;
02073 }
02074 
02075 /* I18N look aside diversions */
02076 
02077 #if defined(ENABLE_NLS)
02078 /*@-exportlocal -exportheadervar@*/
02079 /*@unchecked@*/
02080 extern int _nl_msg_cat_cntr;    /* XXX GNU gettext voodoo */
02081 /*@=exportlocal =exportheadervar@*/
02082 #endif
02083 /*@observer@*/ /*@unchecked@*/
02084 static const char * language = "LANGUAGE";
02085 
02086 /*@observer@*/ /*@unchecked@*/
02087 static const char * _macro_i18ndomains = "%{?_i18ndomains}";
02088 
02095 static int i18nTag(Header h, HE_t he)
02096         /*@globals rpmGlobalMacroContext, h_errno, internalState @*/
02097         /*@modifies he, rpmGlobalMacroContext, internalState @*/
02098 {
02099     char * dstring = rpmExpand(_macro_i18ndomains, NULL);
02100     int rc = 1;         /* assume failure */
02101 
02102     he->t = RPM_STRING_TYPE;
02103     he->p.str = NULL;
02104     he->c = 0;
02105     he->freeData = 0;
02106 
02107     if (dstring && *dstring) {
02108         char *domain, *de;
02109         const char * langval;
02110         const char * msgkey;
02111         const char * msgid;
02112 
02113         {   HE_t nhe = memset(alloca(sizeof(*nhe)), 0, sizeof(*nhe));
02114             const char * tn;
02115             char * mk;
02116             size_t nb = sizeof("()");
02117             int xx;
02118 
02119             nhe->tag = RPMTAG_NAME;
02120             xx = headerGet(h, nhe, 0);
02121             /*
02122              * XXX Ick, tagName() is called by headerGet(), and the tagName()
02123              * buffer is valid only until next tagName() call.
02124              * For now, do the tagName() lookup after headerGet().
02125              */
02126             tn = tagName(he->tag);
02127             if (tn)     nb += strlen(tn);
02128             if (nhe->p.str)     nb += strlen(nhe->p.str);
02129             mk = alloca(nb);
02130             (void) snprintf(mk, nb, "%s(%s)",
02131                         (nhe->p.str ? nhe->p.str : ""), (tn ? tn : ""));
02132             mk[nb-1] = '\0';
02133             nhe->p.ptr = _free(nhe->p.ptr);
02134             msgkey = mk;
02135         }
02136 
02137         /* change to en_US for msgkey -> msgid resolution */
02138         langval = getenv(language);
02139         (void) setenv(language, "en_US", 1);
02140 #if defined(ENABLE_NLS)
02141 /*@i@*/ ++_nl_msg_cat_cntr;
02142 #endif
02143 
02144         msgid = NULL;
02145         for (domain = dstring; domain != NULL; domain = de) {
02146             de = strchr(domain, ':');
02147             if (de) *de++ = '\0';
02148 /*@-unrecog@*/
02149             msgid = dgettext(domain, msgkey);
02150 /*@=unrecog@*/
02151             if (msgid != msgkey) break;
02152         }
02153 
02154         /* restore previous environment for msgid -> msgstr resolution */
02155         if (langval)
02156             (void) setenv(language, langval, 1);
02157         else
02158             unsetenv(language);
02159 #if defined(ENABLE_NLS)
02160 /*@i@*/ ++_nl_msg_cat_cntr;
02161 #endif
02162 
02163         if (domain && msgid) {
02164 /*@-unrecog@*/
02165             const char * s = dgettext(domain, msgid);
02166 /*@=unrecog@*/
02167             if (s) {
02168                 rc = 0;
02169                 he->p.str = xstrdup(s);
02170                 he->c = 1;
02171                 he->freeData = 1;
02172             }
02173         }
02174     }
02175 
02176 /*@-dependenttrans@*/
02177     dstring = _free(dstring);
02178 /*@=dependenttrans@*/
02179     if (!rc)
02180         return rc;
02181 
02182     rc = headerGet(h, he, HEADERGET_NOEXTENSION);
02183     if (rc) {
02184         rc = 0;
02185         he->p.str = xstrtolocale(he->p.str);
02186         he->freeData = 1;
02187         return rc;
02188     }
02189 
02190     he->t = RPM_STRING_TYPE;
02191     he->p.str = NULL;
02192     he->c = 0;
02193     he->freeData = 0;
02194 
02195     return 1;
02196 }
02197 
02201 static int localeTag(Header h, HE_t he)
02202         /*@globals internalState @*/
02203         /*@modifies he, internalState @*/
02204 {
02205     int rc;
02206 
02207     rc = headerGet(h, he, HEADERGET_NOEXTENSION);
02208     if (!rc || he->p.str == NULL || he->c == 0) {
02209         he->t = RPM_STRING_TYPE;
02210         he->freeData = 0;
02211         return 1;
02212     }
02213 
02214     switch (he->t) {
02215     default:
02216         he->freeData = 0;
02217         break;
02218     case RPM_STRING_TYPE:
02219         he->p.str = xstrtolocale(he->p.str);
02220         he->freeData = 1;
02221         break;
02222     case RPM_STRING_ARRAY_TYPE:
02223     {   const char ** argv;
02224         char * te;
02225         size_t l = 0;
02226         unsigned i;
02227         for (i = 0; i < (unsigned) he->c; i++) {
02228             he->p.argv[i] = xstrdup(he->p.argv[i]);
02229             he->p.argv[i] = xstrtolocale(he->p.argv[i]);
02230 assert(he->p.argv[i] != NULL);
02231             l += strlen(he->p.argv[i]) + 1;
02232         }
02233         argv = xmalloc(he->c * sizeof(*argv) + l);
02234         te = (char *)&argv[he->c];
02235         for (i = 0; i < (unsigned) he->c; i++) {
02236             argv[i] = te;
02237             te = stpcpy(te, he->p.argv[i]);
02238             te++;
02239             he->p.argv[i] = _free(he->p.argv[i]);
02240         }
02241         he->p.ptr = _free(he->p.ptr);
02242         he->p.argv = argv;
02243         he->freeData = 1;
02244     }   break;
02245     }
02246 
02247     return 0;
02248 }
02249 
02256 static int summaryTag(Header h, HE_t he)
02257         /*@globals rpmGlobalMacroContext, h_errno, internalState @*/
02258         /*@modifies he, rpmGlobalMacroContext, internalState @*/
02259 {
02260     he->tag = RPMTAG_SUMMARY;
02261     return i18nTag(h, he);
02262 }
02263 
02270 static int descriptionTag(Header h, HE_t he)
02271         /*@globals rpmGlobalMacroContext, h_errno, internalState @*/
02272         /*@modifies he, rpmGlobalMacroContext, internalState @*/
02273 {
02274     he->tag = RPMTAG_DESCRIPTION;
02275     return i18nTag(h, he);
02276 }
02277 
02278 static int changelognameTag(Header h, HE_t he)
02279         /*@globals internalState @*/
02280         /*@modifies he, internalState @*/
02281 {
02282     he->tag = RPMTAG_CHANGELOGNAME;
02283     return localeTag(h, he);
02284 }
02285 
02286 static int changelogtextTag(Header h, HE_t he)
02287         /*@globals internalState @*/
02288         /*@modifies he, internalState @*/
02289 {
02290     he->tag = RPMTAG_CHANGELOGTEXT;
02291     return localeTag(h, he);
02292 }
02293 
02300 static int groupTag(Header h, HE_t he)
02301         /*@globals rpmGlobalMacroContext, h_errno, internalState @*/
02302         /*@modifies he, rpmGlobalMacroContext, internalState @*/
02303 {
02304     he->tag = RPMTAG_GROUP;
02305     return i18nTag(h, he);
02306 }
02307 
02314 static int dbinstanceTag(Header h, HE_t he)
02315         /*@modifies he @*/
02316 {
02317     he->tag = RPMTAG_DBINSTANCE;
02318     he->t = RPM_UINT32_TYPE;
02319     he->p.ui32p = xmalloc(sizeof(*he->p.ui32p));
02320     he->p.ui32p[0] = headerGetInstance(h);
02321     he->freeData = 1;
02322     he->c = 1;
02323     return 0;
02324 }
02325 
02332 static int headerstartoffTag(Header h, HE_t he)
02333         /*@modifies he @*/
02334 {
02335     he->tag = RPMTAG_HEADERSTARTOFF;
02336     he->t = RPM_UINT64_TYPE;
02337     he->p.ui64p = xmalloc(sizeof(*he->p.ui64p));
02338     he->p.ui64p[0] = headerGetStartOff(h);
02339     he->freeData = 1;
02340     he->c = 1;
02341     return 0;
02342 }
02343 
02350 static int headerendoffTag(Header h, HE_t he)
02351         /*@modifies he @*/
02352 {
02353     he->tag = RPMTAG_HEADERENDOFF;
02354     he->t = RPM_UINT64_TYPE;
02355     he->p.ui64p = xmalloc(sizeof(*he->p.ui64p));
02356     he->p.ui64p[0] = headerGetEndOff(h);
02357     he->freeData = 1;
02358     he->c = 1;
02359     return 0;
02360 }
02361 
02368 static int pkgoriginTag(Header h, HE_t he)
02369         /*@globals internalState @*/
02370         /*@modifies he, internalState @*/
02371 {
02372     const char * origin;
02373     int rc = 1;
02374 
02375     he->tag = RPMTAG_PACKAGEORIGIN;
02376     if (!headerGet(h, he, HEADERGET_NOEXTENSION)
02377      && (origin = headerGetOrigin(h)) != NULL)
02378     {
02379         he->t = RPM_STRING_TYPE;
02380         he->p.str = xstrdup(origin);
02381         he->c = 1;
02382         he->freeData = 1;
02383         rc = 0;
02384     }
02385     return rc;
02386 }
02387 
02394 static int pkgbaseurlTag(Header h, HE_t he)
02395         /*@globals internalState @*/
02396         /*@modifies he, internalState @*/
02397 {
02398     const char * baseurl;
02399     int rc = 1;
02400 
02401     he->tag = RPMTAG_PACKAGEBASEURL;
02402     if (!headerGet(h, he, HEADERGET_NOEXTENSION)
02403      && (baseurl = headerGetBaseURL(h)) != NULL)
02404     {
02405         he->t = RPM_STRING_TYPE;
02406         he->p.str = xstrdup(baseurl);
02407         he->c = 1;
02408         he->freeData = 1;
02409         rc = 0;
02410     }
02411     return rc;
02412 }
02413 
02420 static int pkgdigestTag(Header h, HE_t he)
02421         /*@modifies he @*/
02422 {
02423     const char * digest;
02424     int rc = 1;
02425 
02426     he->tag = RPMTAG_PACKAGEDIGEST;
02427     if ((digest = headerGetDigest(h)) != NULL)
02428     {
02429         he->t = RPM_STRING_TYPE;
02430         he->p.str = xstrdup(digest);
02431         he->c = 1;
02432         he->freeData = 1;
02433         rc = 0;
02434     }
02435     return rc;
02436 }
02437 
02444 static int pkgmtimeTag(Header h, HE_t he)
02445         /*@modifies he @*/
02446 {
02447     struct stat * st = headerGetStatbuf(h);
02448     he->tag = RPMTAG_PACKAGETIME;
02449     he->t = RPM_UINT64_TYPE;
02450     he->p.ui64p = xmalloc(sizeof(*he->p.ui64p));
02451 /*@-type@*/
02452     he->p.ui64p[0] = (rpmuint64_t)st->st_mtime;
02453 /*@=type@*/
02454     he->freeData = 1;
02455     he->c = 1;
02456     return 0;
02457 }
02458 
02465 static int pkgsizeTag(Header h, HE_t he)
02466         /*@modifies he @*/
02467 {
02468     struct stat * st = headerGetStatbuf(h);
02469     he->tag = RPMTAG_PACKAGESIZE;
02470     he->t = RPM_UINT64_TYPE;
02471     he->p.ui64p = xmalloc(sizeof(*he->p.ui64p));
02472     he->p.ui64p[0] = (rpmuint64_t)st->st_size;
02473     he->freeData = 1;
02474     he->c = 1;
02475     return 0;
02476 }
02477 
02483 /*@only@*/
02484 static char * hGetNVRA(Header h)
02485         /*@globals internalState @*/
02486         /*@modifies h, internalState @*/
02487 {
02488     const char * N = NULL;
02489     const char * V = NULL;
02490     const char * R = NULL;
02491     const char * A = NULL;
02492     size_t nb = 0;
02493     char * NVRA, * t;
02494 
02495     (void) headerNEVRA(h, &N, NULL, &V, &R, &A);
02496     if (N)      nb += strlen(N);
02497     if (V)      nb += strlen(V) + 1;
02498     if (R)      nb += strlen(R) + 1;
02499 #if defined(RPM_VENDOR_OPENPKG) /* no-architecture-expose */
02500     /* do not expose the architecture as this is too less
02501        information, as in OpenPKG the "platform" is described by the
02502        architecture+operating-system combination. But as the whole
02503        "platform" information is actually overkill, just revert to the
02504        RPM 4 behaviour and do not expose any such information at all. */
02505 #else
02506     if (A)      nb += strlen(A) + 1;
02507 #endif
02508     nb++;
02509     NVRA = t = xmalloc(nb);
02510     *t = '\0';
02511     if (N)      t = stpcpy(t, N);
02512     if (V)      t = stpcpy( stpcpy(t, "-"), V);
02513     if (R)      t = stpcpy( stpcpy(t, "-"), R);
02514 #if defined(RPM_VENDOR_OPENPKG) /* no-architecture-expose */
02515     /* do not expose the architecture as this is too less
02516        information, as in OpenPKG the "platform" is described by the
02517        architecture+operating-system combination. But as the whole
02518        "platform" information is actually overkill, just revert to the
02519        RPM 4 behaviour and do not expose any such information at all. */
02520 #else
02521     if (A)      t = stpcpy( stpcpy(t, "."), A);
02522 #endif
02523     N = _free(N);
02524     V = _free(V);
02525     R = _free(R);
02526     A = _free(A);
02527     return NVRA;
02528 }
02529 
02536 static int nvraTag(Header h, HE_t he)
02537         /*@globals internalState @*/
02538         /*@modifies h, he, internalState @*/
02539 {
02540     he->t = RPM_STRING_TYPE;
02541     he->p.str = hGetNVRA(h);
02542     he->c = 1;
02543     he->freeData = 1;
02544     return 0;
02545 }
02546 
02564 static void rpmfiBuildFNames(Header h, rpmTag tagN,
02565                 /*@null@*/ /*@out@*/ const char *** fnp,
02566                 /*@null@*/ /*@out@*/ rpmTagCount * fcp)
02567         /*@globals internalState @*/
02568         /*@modifies *fnp, *fcp, internalState @*/
02569 {
02570     HE_t he = memset(alloca(sizeof(*he)), 0, sizeof(*he));
02571     rpmTag dirNameTag = 0;
02572     rpmTag dirIndexesTag = 0;
02573     rpmTagData baseNames = { .ptr = NULL };
02574     rpmTagData dirNames = { .ptr = NULL };
02575     rpmTagData dirIndexes = { .ptr = NULL };
02576     rpmTagData fileNames;
02577     rpmTagCount count;
02578     size_t size;
02579     int isSource =
02580         (headerIsEntry(h, RPMTAG_SOURCERPM) == 0 &&
02581          headerIsEntry(h, RPMTAG_ARCH) != 0);
02582     char * t;
02583     unsigned i;
02584     int xx;
02585 
02586     if (tagN == RPMTAG_BASENAMES) {
02587         dirNameTag = RPMTAG_DIRNAMES;
02588         dirIndexesTag = RPMTAG_DIRINDEXES;
02589     } else if (tagN == RPMTAG_ORIGBASENAMES) {
02590         dirNameTag = RPMTAG_ORIGDIRNAMES;
02591         dirIndexesTag = RPMTAG_ORIGDIRINDEXES;
02592     } else {
02593         if (fnp) *fnp = NULL;
02594         if (fcp) *fcp = 0;
02595         return;         /* programmer error */
02596     }
02597 
02598 /*@-compmempass@*/      /* use separate HE_t, not rpmTagData, containers. */
02599     he->tag = tagN;
02600     xx = headerGet(h, he, 0);
02601     /* XXX 3.0.x SRPM's can be used, relative fn's at RPMTAG_OLDFILENAMES. */
02602     if (xx == 0 && isSource) {
02603         he->tag = RPMTAG_OLDFILENAMES;
02604         xx = headerGet(h, he, 0);
02605         if (xx) {
02606             dirNames.argv = xcalloc(3, sizeof(*dirNames.argv));
02607             dirNames.argv[0] = (const char *)&dirNames.argv[2];
02608             dirIndexes.ui32p  = xcalloc(he->c, sizeof(*dirIndexes.ui32p));
02609         }
02610     }
02611     baseNames.argv = he->p.argv;
02612     count = he->c;
02613 
02614     if (!xx) {
02615         if (fnp) *fnp = NULL;
02616         if (fcp) *fcp = 0;
02617         return;         /* no file list */
02618     }
02619 
02620     he->tag = dirNameTag;
02621     if ((xx = headerGet(h, he, 0)) != 0)
02622         dirNames.argv = he->p.argv;
02623 
02624     he->tag = dirIndexesTag;
02625     if ((xx = headerGet(h, he, 0)) != 0)
02626         dirIndexes.ui32p = he->p.ui32p;
02627 /*@=compmempass@*/
02628 
02629     size = sizeof(*fileNames.argv) * count;
02630     for (i = 0; i < (unsigned)count; i++) {
02631         const char * dn = NULL;
02632         (void) urlPath(dirNames.argv[dirIndexes.ui32p[i]], &dn);
02633         size += strlen(baseNames.argv[i]) + strlen(dn) + 1;
02634     }
02635 
02636     fileNames.argv = xmalloc(size);
02637     t = (char *)&fileNames.argv[count];
02638     for (i = 0; i < (unsigned)count; i++) {
02639         const char * dn = NULL;
02640         (void) urlPath(dirNames.argv[dirIndexes.ui32p[i]], &dn);
02641         fileNames.argv[i] = t;
02642         t = stpcpy( stpcpy(t, dn), baseNames.argv[i]);
02643         *t++ = '\0';
02644     }
02645     baseNames.ptr = _free(baseNames.ptr);
02646     dirNames.ptr = _free(dirNames.ptr);
02647     dirIndexes.ptr = _free(dirIndexes.ptr);
02648 
02649 /*@-onlytrans@*/
02650     if (fnp)
02651         *fnp = fileNames.argv;
02652     else
02653         fileNames.ptr = _free(fileNames.ptr);
02654 /*@=onlytrans@*/
02655     if (fcp) *fcp = count;
02656 }
02657 
02665 static int _fnTag(Header h, HE_t he, rpmTag tag)
02666         /*@globals internalState @*/
02667         /*@modifies he, internalState @*/
02668 {
02669     he->t = RPM_STRING_ARRAY_TYPE;
02670     rpmfiBuildFNames(h, tag, &he->p.argv, &he->c);
02671     he->freeData = 1;
02672     /* XXX headerGet() rc on RPMTAG_FILEPATHS w empty list. */
02673     if (he->p.argv && he->p.argv[0] && he->c > 0)
02674         return 0;
02675     he->p.ptr = _free(he->p.ptr);
02676     he->c = 0;
02677     return 1;
02678 }
02679 
02680 static int filenamesTag(Header h, HE_t he)
02681         /*@globals internalState @*/
02682         /*@modifies he, internalState @*/
02683 {
02684     he->tag = tagValue("Filenames");
02685     return _fnTag(h, he, RPMTAG_BASENAMES);
02686 }
02687 
02688 static int filepathsTag(Header h, HE_t he)
02689         /*@globals internalState @*/
02690         /*@modifies he, internalState @*/
02691 {
02692     he->tag = RPMTAG_FILEPATHS;
02693     return _fnTag(h, he, RPMTAG_BASENAMES);
02694 }
02695 
02696 static int origpathsTag(Header h, HE_t he)
02697         /*@globals internalState @*/
02698         /*@modifies he, internalState @*/
02699 {
02700     he->tag = RPMTAG_ORIGPATHS;
02701     return _fnTag(h, he, RPMTAG_ORIGBASENAMES);
02702 }
02703 
02713 static int debevrfmtTag(/*@unused@*/ Header h, HE_t he,
02714                 HE_t Nhe, HE_t EVRhe, HE_t Fhe)
02715         /*@globals rpmGlobalMacroContext, h_errno, internalState @*/
02716         /*@modifies he, Nhe, rpmGlobalMacroContext, internalState @*/
02717 {
02718     char * t, * te;
02719     size_t nb = 0;
02720     int rc = 1;
02721 
02722     he->t = RPM_STRING_ARRAY_TYPE;
02723     he->c = 0;
02724     he->freeData = 1;
02725     for (Nhe->ix = 0; Nhe->ix < (int)Nhe->c; Nhe->ix++) {
02726         nb += sizeof(*he->p.argv);
02727         nb += strlen(Nhe->p.argv[Nhe->ix]) + 1;
02728         if (*EVRhe->p.argv[Nhe->ix] != '\0')
02729             nb += strlen(EVRhe->p.argv[Nhe->ix]) + (sizeof(" (== )")-1);
02730         he->c++;
02731     }
02732     nb += sizeof(*he->p.argv);
02733 
02734     he->p.argv = xmalloc(nb);
02735     te = (char *) &he->p.argv[he->c+1];
02736 
02737     he->c = 0;
02738     for (Nhe->ix = 0; Nhe->ix < (int)Nhe->c; Nhe->ix++) {
02739         he->p.argv[he->c++] = te;
02740         if (*EVRhe->p.argv[Nhe->ix] != '\0') {
02741             char opstr[4], * op = opstr;
02742             if (Fhe->p.ui32p[Nhe->ix] & RPMSENSE_LESS)
02743                 *op++ = '<';
02744             if (Fhe->p.ui32p[Nhe->ix] & RPMSENSE_GREATER)
02745                 *op++ = '>';
02746             if (Fhe->p.ui32p[Nhe->ix] & RPMSENSE_EQUAL)
02747                 *op++ = '=';
02748             *op = '\0';
02749             t = rpmExpand(Nhe->p.argv[Nhe->ix],
02750                         " (", opstr, " ", EVRhe->p.argv[Nhe->ix], ")", NULL);
02751         } else
02752             t = rpmExpand(Nhe->p.argv[Nhe->ix], NULL);
02753         te = stpcpy(te, t);
02754         te++;
02755         t = _free(t);
02756     }
02757     he->p.argv[he->c] = NULL;
02758     rc = 0;
02759 
02760     return rc;
02761 }
02762 
02772 static int debevrTag(Header h, HE_t he, rpmTag tagN, rpmTag tagEVR, rpmTag tagF)
02773         /*@globals rpmGlobalMacroContext, h_errno, internalState @*/
02774         /*@modifies he, rpmGlobalMacroContext, internalState @*/
02775 {
02776     HE_t Nhe = memset(alloca(sizeof(*Nhe)), 0, sizeof(*Nhe));
02777     HE_t EVRhe = memset(alloca(sizeof(*EVRhe)), 0, sizeof(*EVRhe));
02778     HE_t Fhe = memset(alloca(sizeof(*Fhe)), 0, sizeof(*Fhe));
02779     int rc = 1;
02780     int xx;
02781 
02782     Nhe->tag = tagN;
02783     if (!(xx = headerGet(h, Nhe, 0)))
02784         goto exit;
02785     EVRhe->tag = tagEVR;
02786     if (!(xx = headerGet(h, EVRhe, 0)))
02787         goto exit;
02788 assert(EVRhe->c == Nhe->c);
02789     Fhe->tag = tagF;
02790     if (!(xx = headerGet(h, Fhe, 0)))
02791         goto exit;
02792 assert(Fhe->c == Nhe->c);
02793 
02794     rc = debevrfmtTag(h, he, Nhe, EVRhe, Fhe);
02795 
02796 exit:
02797     Nhe->p.ptr = _free(Nhe->p.ptr);
02798     EVRhe->p.ptr = _free(EVRhe->p.ptr);
02799     Fhe->p.ptr = _free(Fhe->p.ptr);
02800     return rc;
02801 }
02802 
02809 static int debconflictsTag(Header h, HE_t he)
02810         /*@globals rpmGlobalMacroContext, h_errno, internalState @*/
02811         /*@modifies he, rpmGlobalMacroContext, internalState @*/
02812 {
02813     he->tag = tagValue("Debconflicts");
02814     return debevrTag(h, he,
02815         RPMTAG_CONFLICTNAME, RPMTAG_CONFLICTVERSION, RPMTAG_CONFLICTFLAGS);
02816 }
02817 
02818 static int debdependsTag(Header h, HE_t he)
02819         /*@globals rpmGlobalMacroContext, h_errno, internalState @*/
02820         /*@modifies he, rpmGlobalMacroContext, internalState @*/
02821 {
02822     he->tag = tagValue("Debdepends");
02823     return debevrTag(h, he,
02824         RPMTAG_REQUIRENAME, RPMTAG_REQUIREVERSION, RPMTAG_REQUIREFLAGS);
02825 }
02826 
02827 static int debobsoletesTag(Header h, HE_t he)
02828         /*@globals rpmGlobalMacroContext, h_errno, internalState @*/
02829         /*@modifies he, rpmGlobalMacroContext, internalState @*/
02830 {
02831     he->tag = tagValue("Debobsoletes");
02832     return debevrTag(h, he,
02833         RPMTAG_OBSOLETENAME, RPMTAG_OBSOLETEVERSION, RPMTAG_OBSOLETEFLAGS);
02834 }
02835 
02836 static int debprovidesTag(Header h, HE_t he)
02837         /*@globals rpmGlobalMacroContext, h_errno, internalState @*/
02838         /*@modifies he, rpmGlobalMacroContext, internalState @*/
02839 {
02840     he->tag = tagValue("Debprovides");
02841     return debevrTag(h, he,
02842         RPMTAG_PROVIDENAME, RPMTAG_PROVIDEVERSION, RPMTAG_PROVIDEFLAGS);
02843 }
02844 
02851 static int debmd5sumsTag(Header h, HE_t he)
02852         /*@globals rpmGlobalMacroContext, h_errno, internalState @*/
02853         /*@modifies he, rpmGlobalMacroContext, internalState @*/
02854 {
02855     HE_t Nhe = memset(alloca(sizeof(*Nhe)), 0, sizeof(*Nhe));
02856     HE_t Dhe = memset(alloca(sizeof(*Dhe)), 0, sizeof(*Dhe));
02857     char * t, * te;
02858     size_t nb = 0;
02859     int rc = 1;
02860     int xx;
02861 
02862     Nhe->tag = RPMTAG_FILEPATHS;
02863     if (!(xx = headerGet(h, Nhe, 0)))
02864         goto exit;
02865     Dhe->tag = RPMTAG_FILEDIGESTS;
02866     if (!(xx = headerGet(h, Dhe, 0)))
02867         goto exit;
02868 assert(Dhe->c == Nhe->c);
02869 
02870     he->tag = tagValue("Debmd5sums");
02871     he->t = RPM_STRING_ARRAY_TYPE;
02872     he->c = 0;
02873     he->freeData = 1;
02874     for (Dhe->ix = 0; Dhe->ix < (int)Dhe->c; Dhe->ix++) {
02875         if (!(Dhe->p.argv[Dhe->ix] && *Dhe->p.argv[Dhe->ix]))
02876             continue;
02877         nb += sizeof(*he->p.argv);
02878         nb += strlen(Dhe->p.argv[Dhe->ix]) + sizeof("  ") + strlen(Nhe->p.argv[Dhe->ix]) - 1;
02879         he->c++;
02880     }
02881     nb += sizeof(*he->p.argv);
02882 
02883     he->p.argv = xmalloc(nb);
02884     te = (char *) &he->p.argv[he->c+1];
02885 
02886     he->c = 0;
02887     for (Dhe->ix = 0; Dhe->ix < (int)Dhe->c; Dhe->ix++) {
02888         if (!(Dhe->p.argv[Dhe->ix] && *Dhe->p.argv[Dhe->ix]))
02889             continue;
02890         he->p.argv[he->c++] = te;
02891         t = rpmExpand(Dhe->p.argv[Dhe->ix], "  ", Nhe->p.argv[Dhe->ix]+1, NULL);
02892         te = stpcpy(te, t);
02893         te++;
02894         t = _free(t);
02895     }
02896     he->p.argv[he->c] = NULL;
02897     rc = 0;
02898 
02899 exit:
02900     Nhe->p.ptr = _free(Nhe->p.ptr);
02901     Dhe->p.ptr = _free(Dhe->p.ptr);
02902     return rc;
02903 }
02904 
02905 static int filestatTag(Header h, HE_t he)
02906         /*@globals internalState @*/
02907         /*@modifies he, internalState @*/
02908 {
02909     rpmTagData paths = { .ptr = NULL };
02910     /* _dev */
02911     rpmTagData _ino = { .ptr = NULL };
02912     rpmTagData _mode = { .ptr = NULL };
02913     /* _nlink */
02914     /* _uid */
02915     /* _gid */
02916     rpmTagData _rdev = { .ptr = NULL };
02917     rpmTagData _size = { .ptr = NULL };
02918     /* _blksize */
02919     /* _blocks */
02920     /* _atime */
02921     rpmTagData _mtime = { .ptr = NULL };
02922     /* st_ctime */
02923     int rc;
02924 
02925     he->tag = RPMTAG_FILEPATHS;
02926     if ((rc = _fnTag(h, he, RPMTAG_BASENAMES)) != 0 || he->c == 0)
02927         goto exit;
02928 
02929 exit:
02930     paths.ptr = _free(paths.ptr);
02931     _ino.ptr = _free(_ino.ptr);
02932     _mode.ptr = _free(_mode.ptr);
02933     _rdev.ptr = _free(_rdev.ptr);
02934     _size.ptr = _free(_size.ptr);
02935     _mtime.ptr = _free(_mtime.ptr);
02936     return rc;
02937 }
02938 
02939 static int wnlookupTag(Header h, rpmTag tagNVRA, ARGV_t *avp, ARGI_t *hitp,
02940                 HE_t PNhe, /*@null@*/ HE_t PEVRhe, /*@null@*/ HE_t PFhe)
02941         /*@globals rpmGlobalMacroContext, h_errno,
02942                 fileSystem, internalState @*/
02943         /*@modifies *avp, *hitp, rpmGlobalMacroContext,
02944                 fileSystem, internalState @*/
02945 {
02946     HE_t NVRAhe = memset(alloca(sizeof(*NVRAhe)), 0, sizeof(*NVRAhe));
02947     HE_t RNhe = memset(alloca(sizeof(*RNhe)), 0, sizeof(*RNhe));
02948     HE_t REVRhe = memset(alloca(sizeof(*REVRhe)), 0, sizeof(*REVRhe));
02949     HE_t RFhe = memset(alloca(sizeof(*RFhe)), 0, sizeof(*RFhe));
02950     rpmdb _rpmdb = (rpmdb) headerGetRpmdb(h);
02951     const char * key = PNhe->p.argv[PNhe->ix];
02952     size_t keylen = 0;
02953     rpmmi mi;
02954     rpmTag tagN = RPMTAG_REQUIRENAME;
02955     rpmTag tagEVR = RPMTAG_REQUIREVERSION;
02956     rpmTag tagF = RPMTAG_REQUIREFLAGS;
02957     rpmuint32_t PFlags;
02958     rpmuint32_t RFlags;
02959     EVR_t Pevr;
02960     Header oh;
02961     int rc = 0;
02962     int xx;
02963 
02964     if (tagNVRA == 0)
02965         tagNVRA = RPMTAG_NVRA;
02966 
02967     PFlags = (PFhe != NULL ? (PFhe->p.ui32p[PNhe->ix] & RPMSENSE_SENSEMASK) : 0);
02968     Pevr = rpmEVRnew(PFlags, 1);
02969 
02970     if (PEVRhe != NULL)
02971         xx = rpmEVRparse(xstrdup(PEVRhe->p.argv[PNhe->ix]), Pevr);
02972 
02973     RNhe->tag = tagN;
02974     REVRhe->tag = tagEVR;
02975     RFhe->tag = tagF;
02976 
02977     mi = rpmmiInit(_rpmdb, tagN, key, keylen);
02978     if (hitp && *hitp)
02979         xx = rpmmiPrune(mi, (uint32_t *)argiData(*hitp), argiCount(*hitp), 0);
02980     while ((oh = rpmmiNext(mi)) != NULL) {
02981         if (!headerGet(oh, RNhe, 0))
02982             goto bottom;
02983         if (PEVRhe != NULL) {
02984             if (!headerGet(oh, REVRhe, 0))
02985                 goto bottom;
02986 assert(REVRhe->c == RNhe->c);
02987             if (!headerGet(oh, RFhe, 0))
02988                 goto bottom;
02989 assert(RFhe->c == RNhe->c);
02990         }
02991 
02992         for (RNhe->ix = 0; RNhe->ix < (int)RNhe->c; RNhe->ix++) {
02993             if (strcmp(PNhe->p.argv[PNhe->ix], RNhe->p.argv[RNhe->ix]))
02994                 /*@innercontinue@*/ continue;
02995             if (PEVRhe == NULL)
02996                 goto bingo;
02997             RFlags = RFhe->p.ui32p[RNhe->ix] & RPMSENSE_SENSEMASK;
02998             {   EVR_t Revr = rpmEVRnew(RFlags, 1);
02999                 if (!(PFlags && RFlags))
03000                     xx = 1;
03001                 else {
03002                     xx = rpmEVRparse(REVRhe->p.argv[RNhe->ix], Revr);
03003                     xx = rpmEVRoverlap(Pevr, Revr);
03004                 }
03005                 Revr = rpmEVRfree(Revr);
03006             }
03007             if (xx)
03008                 goto bingo;
03009         }
03010         goto bottom;
03011 
03012 bingo:
03013         NVRAhe->tag = tagNVRA;
03014         xx = headerGet(oh, NVRAhe, 0);
03015         if (!(*avp != NULL && argvSearch(*avp, NVRAhe->p.str, NULL) != NULL)) {
03016             xx = argvAdd(avp, NVRAhe->p.str);
03017             xx = argvSort(*avp, NULL);
03018             if (hitp != NULL)
03019                 xx = argiAdd(hitp, -1, rpmmiInstance(mi));
03020             rc++;
03021         }
03022 
03023 bottom:
03024         RNhe->p.ptr = _free(RNhe->p.ptr);
03025         REVRhe->p.ptr = _free(REVRhe->p.ptr);
03026         RFhe->p.ptr = _free(RFhe->p.ptr);
03027         NVRAhe->p.ptr = _free(NVRAhe->p.ptr);
03028     }
03029     mi = rpmmiFree(mi);
03030 
03031     Pevr = rpmEVRfree(Pevr);
03032 
03033     return rc;
03034 }
03035 
03036 static int whatneedsTag(Header h, HE_t he)
03037         /*@globals rpmGlobalMacroContext, h_errno, fileSystem, internalState @*/
03038         /*@modifies he, rpmGlobalMacroContext, fileSystem, internalState @*/
03039 {
03040     HE_t NVRAhe = memset(alloca(sizeof(*NVRAhe)), 0, sizeof(*NVRAhe));
03041     HE_t PNhe = memset(alloca(sizeof(*PNhe)), 0, sizeof(*PNhe));
03042     HE_t PEVRhe = memset(alloca(sizeof(*PEVRhe)), 0, sizeof(*PEVRhe));
03043     HE_t PFhe = memset(alloca(sizeof(*PFhe)), 0, sizeof(*PFhe));
03044     HE_t FNhe = memset(alloca(sizeof(*FNhe)), 0, sizeof(*FNhe));
03045     rpmTag tagNVRA = RPMTAG_NVRA;
03046     ARGV_t pkgs = NULL;
03047     ARGI_t hits = NULL;
03048     int rc = 1;
03049 
03050     PNhe->tag = RPMTAG_PROVIDENAME;
03051     if (!headerGet(h, PNhe, 0))
03052         goto exit;
03053     PEVRhe->tag = RPMTAG_PROVIDEVERSION;
03054     if (!headerGet(h, PEVRhe, 0))
03055         goto exit;
03056 assert(PEVRhe->c == PNhe->c);
03057     PFhe->tag = RPMTAG_PROVIDEFLAGS;
03058     if (!headerGet(h, PFhe, 0))
03059         goto exit;
03060 assert(PFhe->c == PNhe->c);
03061 
03062     FNhe->tag = RPMTAG_FILEPATHS;
03063     if (!headerGet(h, FNhe, 0))
03064         goto exit;
03065 
03066     NVRAhe->tag = tagNVRA;;
03067     if (!headerGet(h, NVRAhe, 0))
03068         goto exit;
03069 
03070     (void) argvAdd(&pkgs, NVRAhe->p.str);
03071 
03072     for (PNhe->ix = 0; PNhe->ix < (int)PNhe->c; PNhe->ix++)
03073         (void) wnlookupTag(h, tagNVRA, &pkgs, &hits, PNhe, PEVRhe, PFhe);
03074     for (FNhe->ix = 0; FNhe->ix < (int)FNhe->c; FNhe->ix++)
03075         (void) wnlookupTag(h, tagNVRA, &pkgs, &hits, FNhe, NULL, NULL);
03076 
03077     /* Convert package NVRA array to Header string array. */
03078     {   size_t nb = 0;
03079         char * te;
03080         rpmuint32_t i;
03081 
03082         he->t = RPM_STRING_ARRAY_TYPE;
03083         he->c = argvCount(pkgs);
03084         nb = 0;
03085         for (i = 0; i < he->c; i++) {
03086             nb += sizeof(*he->p.argv);
03087             nb += strlen(pkgs[i]) + 1;
03088         }
03089         nb += sizeof(*he->p.argv);
03090 
03091         he->p.argv = xmalloc(nb);
03092         te = (char *) &he->p.argv[he->c+1];
03093 
03094         for (i = 0; i < he->c; i++) {
03095             he->p.argv[i] = te;
03096             te = stpcpy(te, pkgs[i]);
03097             te++;
03098         }
03099         he->p.argv[he->c] = NULL;
03100     }
03101 
03102     hits = argiFree(hits);
03103     pkgs = argvFree(pkgs);
03104     rc = 0;
03105 
03106 exit:
03107     NVRAhe->p.ptr = _free(NVRAhe->p.ptr);
03108     PNhe->p.ptr = _free(PNhe->p.ptr);
03109     PEVRhe->p.ptr = _free(PEVRhe->p.ptr);
03110     PFhe->p.ptr = _free(PFhe->p.ptr);
03111     FNhe->p.ptr = _free(FNhe->p.ptr);
03112     return rc;
03113 }
03114 
03115 static int nwlookupTag(Header h, rpmTag tagNVRA, ARGV_t *avp, ARGI_t *hitp,
03116                 HE_t RNhe, /*@null@*/ HE_t REVRhe, /*@null@*/ HE_t RFhe)
03117         /*@globals rpmGlobalMacroContext, h_errno,
03118                 fileSystem, internalState @*/
03119         /*@modifies *avp, *hitp, REVRhe, rpmGlobalMacroContext,
03120                 fileSystem, internalState @*/
03121 {
03122     HE_t NVRAhe = memset(alloca(sizeof(*NVRAhe)), 0, sizeof(*NVRAhe));
03123     HE_t PNhe = memset(alloca(sizeof(*PNhe)), 0, sizeof(*PNhe));
03124     HE_t PEVRhe = memset(alloca(sizeof(*PEVRhe)), 0, sizeof(*PEVRhe));
03125     HE_t PFhe = memset(alloca(sizeof(*PFhe)), 0, sizeof(*PFhe));
03126     rpmdb _rpmdb = (rpmdb) headerGetRpmdb(h);
03127     const char * key = RNhe->p.argv[RNhe->ix];
03128     size_t keylen = 0;
03129     rpmmi mi;
03130     rpmTag tagN = tagN = (*RNhe->p.argv[RNhe->ix] == '/')
03131         ? RPMTAG_BASENAMES : RPMTAG_PROVIDENAME;
03132     rpmTag tagEVR = RPMTAG_PROVIDEVERSION;
03133     rpmTag tagF = RPMTAG_PROVIDEFLAGS;
03134     rpmuint32_t PFlags;
03135     rpmuint32_t RFlags;
03136     EVR_t Revr;
03137     Header oh;
03138     int rc = 0;
03139     int xx;
03140 
03141     if (tagNVRA == 0)
03142         tagNVRA = RPMTAG_NVRA;
03143 
03144     RFlags = (RFhe != NULL ? (RFhe->p.ui32p[RNhe->ix] & RPMSENSE_SENSEMASK) : 0);
03145     Revr = rpmEVRnew(RFlags, 1);
03146 
03147     if (REVRhe != NULL)
03148         xx = rpmEVRparse(REVRhe->p.argv[RNhe->ix], Revr);
03149 
03150     PNhe->tag = tagN;
03151     PEVRhe->tag = tagEVR;
03152     PFhe->tag = tagF;
03153 
03154     mi = rpmmiInit(_rpmdb, tagN, key, keylen);
03155     if (hitp && *hitp)
03156         xx = rpmmiPrune(mi, (uint32_t *)argiData(*hitp), argiCount(*hitp), 0);
03157     while ((oh = rpmmiNext(mi)) != NULL) {
03158         if (!headerGet(oh, PNhe, 0))
03159             goto bottom;
03160         if (REVRhe != NULL) {
03161             if (!headerGet(oh, PEVRhe, 0))
03162                 goto bottom;
03163 assert(PEVRhe->c == PNhe->c);
03164             if (!headerGet(oh, PFhe, 0))
03165                 goto bottom;
03166 assert(PFhe->c == PNhe->c);
03167         }
03168 
03169         for (PNhe->ix = 0; PNhe->ix < (int)PNhe->c; PNhe->ix++) {
03170             if (strcmp(RNhe->p.argv[RNhe->ix], PNhe->p.argv[PNhe->ix]))
03171                 /*@innercontinue@*/ continue;
03172             if (REVRhe == NULL)
03173                 goto bingo;
03174             PFlags = PFhe->p.ui32p[PNhe->ix] & RPMSENSE_SENSEMASK;
03175             {   EVR_t Pevr = rpmEVRnew(PFlags, 1);
03176                 if (!(PFlags && RFlags))
03177                     xx = 1;
03178                 else {
03179                     xx = rpmEVRparse(PEVRhe->p.argv[PNhe->ix], Pevr);
03180                     xx = rpmEVRoverlap(Revr, Pevr);
03181                 }
03182                 Pevr = rpmEVRfree(Pevr);
03183             }
03184             if (xx)
03185                 goto bingo;
03186         }
03187         goto bottom;
03188 
03189 bingo:
03190         NVRAhe->tag = tagNVRA;
03191         xx = headerGet(oh, NVRAhe, 0);
03192         if (!(*avp != NULL && argvSearch(*avp, NVRAhe->p.str, NULL) != NULL)) {
03193             xx = argvAdd(avp, NVRAhe->p.str);
03194             xx = argvSort(*avp, NULL);
03195             if (hitp != NULL)
03196                 xx = argiAdd(hitp, -1, rpmmiInstance(mi));
03197             rc++;
03198         }
03199 
03200 bottom:
03201         PNhe->p.ptr = _free(PNhe->p.ptr);
03202         PEVRhe->p.ptr = _free(PEVRhe->p.ptr);
03203         PFhe->p.ptr = _free(PFhe->p.ptr);
03204         NVRAhe->p.ptr = _free(NVRAhe->p.ptr);
03205     }
03206     mi = rpmmiFree(mi);
03207 
03208     Revr = rpmEVRfree(Revr);
03209 
03210     return rc;
03211 }
03212 
03213 static int needswhatTag(Header h, HE_t he)
03214         /*@globals rpmGlobalMacroContext, h_errno, fileSystem, internalState @*/
03215         /*@modifies he, rpmGlobalMacroContext, fileSystem, internalState @*/
03216 {
03217     HE_t NVRAhe = memset(alloca(sizeof(*NVRAhe)), 0, sizeof(*NVRAhe));
03218     HE_t RNhe = memset(alloca(sizeof(*RNhe)), 0, sizeof(*RNhe));
03219     HE_t REVRhe = memset(alloca(sizeof(*REVRhe)), 0, sizeof(*REVRhe));
03220     HE_t RFhe = memset(alloca(sizeof(*RFhe)), 0, sizeof(*RFhe));
03221     rpmTag tagNVRA = RPMTAG_NVRA;
03222     ARGV_t pkgs = NULL;
03223     ARGI_t hits = NULL;
03224     int rc = 1;
03225 
03226     RNhe->tag = RPMTAG_REQUIRENAME;
03227     if (!headerGet(h, RNhe, 0))
03228         goto exit;
03229     REVRhe->tag = RPMTAG_REQUIREVERSION;
03230     if (!headerGet(h, REVRhe, 0))
03231         goto exit;
03232 assert(REVRhe->c == RNhe->c);
03233     RFhe->tag = RPMTAG_REQUIREFLAGS;
03234     if (!headerGet(h, RFhe, 0))
03235         goto exit;
03236 assert(RFhe->c == RNhe->c);
03237 
03238     NVRAhe->tag = tagNVRA;;
03239     if (!headerGet(h, NVRAhe, 0))
03240         goto exit;
03241 
03242     (void) argvAdd(&pkgs, NVRAhe->p.str);
03243 
03244     for (RNhe->ix = 0; RNhe->ix < (int)RNhe->c; RNhe->ix++) {
03245         if (*RNhe->p.argv[RNhe->ix] == '/' || *REVRhe->p.argv[RNhe->ix] == '\0')
03246             (void) nwlookupTag(h, tagNVRA, &pkgs, &hits, RNhe, NULL, NULL);
03247         else
03248             (void) nwlookupTag(h, tagNVRA, &pkgs, &hits, RNhe, REVRhe, RFhe);
03249     }
03250 
03251     /* Convert package NVRA array to Header string array. */
03252     {   size_t nb = 0;
03253         char * te;
03254         rpmuint32_t i;
03255 
03256         he->t = RPM_STRING_ARRAY_TYPE;
03257         he->c = argvCount(pkgs);
03258         nb = 0;
03259         for (i = 0; i < he->c; i++) {
03260             nb += sizeof(*he->p.argv);
03261             nb += strlen(pkgs[i]) + 1;
03262         }
03263         nb += sizeof(*he->p.argv);
03264 
03265         he->p.argv = xmalloc(nb);
03266         te = (char *) &he->p.argv[he->c+1];
03267 
03268         for (i = 0; i < he->c; i++) {
03269             he->p.argv[i] = te;
03270             te = stpcpy(te, pkgs[i]);
03271             te++;
03272         }
03273         he->p.argv[he->c] = NULL;
03274     }
03275 
03276     hits = argiFree(hits);
03277     pkgs = argvFree(pkgs);
03278     rc = 0;
03279 
03280 exit:
03281     NVRAhe->p.ptr = _free(NVRAhe->p.ptr);
03282     RNhe->p.ptr = _free(RNhe->p.ptr);
03283     REVRhe->p.ptr = _free(REVRhe->p.ptr);
03284     RFhe->p.ptr = _free(RFhe->p.ptr);
03285     return rc;
03286 }
03287 
03288 static int PRCOSkip(rpmTag tag, rpmTagData N, rpmTagData EVR, rpmTagData F,
03289                 rpmuint32_t i)
03290         /*@*/
03291 {
03292     int a = -2, b = -2;
03293 
03294     if (N.argv[i] == NULL || *N.argv[i] == '\0')
03295         return 1;
03296     if (tag == RPMTAG_REQUIRENAME && i > 0
03297      && !(a=strcmp(N.argv[i], N.argv[i-1]))
03298      && !(b=strcmp(EVR.argv[i], EVR.argv[i-1]))
03299      && (F.ui32p[i] & 0x4e) == ((F.ui32p[i-1] & 0x4e)))
03300         return 1;
03301     return 0;
03302 }
03303 
03304 static int PRCOxmlTag(Header h, HE_t he, rpmTag EVRtag, rpmTag Ftag)
03305         /*@globals internalState @*/
03306         /*@modifies he, internalState @*/
03307 {
03308     rpmTag tag = he->tag;
03309     rpmTagData N = { .ptr = NULL };
03310     rpmTagData EVR = { .ptr = NULL };
03311     rpmTagData F = { .ptr = NULL };
03312     size_t nb;
03313     rpmuint32_t ac;
03314     rpmuint32_t c;
03315     rpmuint32_t i;
03316     char *t;
03317     int rc = 1;         /* assume failure */
03318     int xx;
03319 int lvl = 0;
03320 spew_t spew = &_xml_spew;
03321 
03322 /*@-compmempass@*/      /* use separate HE_t, not rpmTagData, containers. */
03323     xx = headerGet(h, he, 0);
03324     if (xx == 0) goto exit;
03325     N.argv = he->p.argv;
03326     c = he->c;
03327 
03328     he->tag = EVRtag;
03329     xx = headerGet(h, he, 0);
03330     if (xx == 0) goto exit;
03331     EVR.argv = he->p.argv;
03332 
03333     he->tag = Ftag;
03334     xx = headerGet(h, he, 0);
03335     if (xx == 0) goto exit;
03336     F.ui32p = he->p.ui32p;
03337 
03338     nb = sizeof(*he->p.argv);
03339     ac = 0;
03340     for (i = 0; i < c; i++) {
03341 /*@-nullstate@*/        /* EVR.argv might be NULL */
03342         if (PRCOSkip(tag, N, EVR, F, i))
03343             continue;
03344 /*@=nullstate@*/
03345         ac++;
03346         nb += sizeof(*he->p.argv);
03347         nb += sizeof("<rpm:entry name=\"\"/>");
03348         if (*N.argv[i] == '/')
03349             nb += spew->spew_strlen(N.argv[i], lvl);
03350         else
03351             nb += strlen(N.argv[i]);
03352         if (EVR.argv != NULL && EVR.argv[i] != NULL && *EVR.argv[i] != '\0') {
03353             nb += sizeof(" flags=\"EQ\" epoch=\"0\" ver=\"\"") - 1;
03354             nb += strlen(EVR.argv[i]);
03355             if (strchr(EVR.argv[i], ':') != NULL)
03356                 nb -= 2;
03357             if (strchr(EVR.argv[i], '-') != NULL)
03358                 nb += sizeof(" rel=\"\"") - 2;
03359         }
03360 #ifdef  NOTNOW
03361         if (tag == RPMTAG_REQUIRENAME && (F.ui32p[i] & 0x40))
03362             nb += sizeof(" pre=\"1\"") - 1;
03363 #endif
03364     }
03365 
03366     he->t = RPM_STRING_ARRAY_TYPE;
03367     he->c = ac;
03368     he->freeData = 1;
03369     he->p.argv = xmalloc(nb + BUFSIZ);  /* XXX hack: leave slop */
03370     t = (char *) &he->p.argv[he->c + 1];
03371     ac = 0;
03372     for (i = 0; i < c; i++) {
03373 /*@-nullstate@*/        /* EVR.argv might be NULL */
03374         if (PRCOSkip(tag, N, EVR, F, i))
03375             continue;
03376 /*@=nullstate@*/
03377         he->p.argv[ac++] = t;
03378         t = stpcpy(t, "<rpm:entry");
03379         t = stpcpy(t, " name=\"");
03380         if (*N.argv[i] == '/') {
03381             t = spew->spew_strcpy(t, N.argv[i], lvl);   t += strlen(t);
03382         } else
03383             t = stpcpy(t, N.argv[i]);
03384         t = stpcpy(t, "\"");
03385 /*@-readonlytrans@*/
03386         if (EVR.argv != NULL && EVR.argv[i] != NULL && *EVR.argv[i] != '\0') {
03387             static char *Fstr[] = { "?0","LT","GT","?3","EQ","LE","GE","?7" };
03388             rpmuint32_t Fx = ((F.ui32p[i] >> 1) & 0x7);
03389             const char *E, *V, *R;
03390             char *f, *fe;
03391             t = stpcpy( stpcpy( stpcpy(t, " flags=\""), Fstr[Fx]), "\"");
03392             f = (char *) EVR.argv[i];
03393             for (fe = f; *fe != '\0' && *fe >= '0' && *fe <= '9'; fe++)
03394                 {};
03395             if (*fe == ':') { *fe++ = '\0'; E = f; f = fe; } else E = NULL;
03396             V = f;
03397             for (fe = f; *fe != '\0' && *fe != '-'; fe++)
03398                 {};
03399             if (*fe == '-') { *fe++ = '\0'; R = fe; } else R = NULL;
03400             t = stpcpy( stpcpy( stpcpy(t, " epoch=\""), (E && *E ? E : "0")), "\"");
03401             t = stpcpy( stpcpy( stpcpy(t, " ver=\""), V), "\"");
03402             if (R != NULL)
03403                 t = stpcpy( stpcpy( stpcpy(t, " rel=\""), R), "\"");
03404         }
03405 /*@=readonlytrans@*/
03406 #ifdef  NOTNOW
03407         if (tag == RPMTAG_REQUIRENAME && (F.ui32p[i] & 0x40))
03408             t = stpcpy(t, " pre=\"1\"");
03409 #endif
03410         t = stpcpy(t, "/>");
03411         *t++ = '\0';
03412     }
03413     he->p.argv[he->c] = NULL;
03414 /*@=compmempass@*/
03415     rc = 0;
03416 
03417 exit:
03418 /*@-kepttrans@*/        /* N.argv may be kept. */
03419     N.argv = _free(N.argv);
03420 /*@=kepttrans@*/
03421 /*@-usereleased@*/      /* EVR.argv may be dead. */
03422     EVR.argv = _free(EVR.argv);
03423 /*@=usereleased@*/
03424     F.ui32p = _free(F.ui32p);
03425     return rc;
03426 }
03427 
03428 static int PxmlTag(Header h, HE_t he)
03429         /*@globals internalState @*/
03430         /*@modifies he, internalState @*/
03431 {
03432     he->tag = RPMTAG_PROVIDENAME;
03433     return PRCOxmlTag(h, he, RPMTAG_PROVIDEVERSION, RPMTAG_PROVIDEFLAGS);
03434 }
03435 
03436 static int RxmlTag(Header h, HE_t he)
03437         /*@globals internalState @*/
03438         /*@modifies he, internalState @*/
03439 {
03440     he->tag = RPMTAG_REQUIRENAME;
03441     return PRCOxmlTag(h, he, RPMTAG_REQUIREVERSION, RPMTAG_REQUIREFLAGS);
03442 }
03443 
03444 static int CxmlTag(Header h, HE_t he)
03445         /*@globals internalState @*/
03446         /*@modifies he, internalState @*/
03447 {
03448     he->tag = RPMTAG_CONFLICTNAME;
03449     return PRCOxmlTag(h, he, RPMTAG_CONFLICTVERSION, RPMTAG_CONFLICTFLAGS);
03450 }
03451 
03452 static int OxmlTag(Header h, HE_t he)
03453         /*@globals internalState @*/
03454         /*@modifies he, internalState @*/
03455 {
03456     he->tag = RPMTAG_OBSOLETENAME;
03457     return PRCOxmlTag(h, he, RPMTAG_OBSOLETEVERSION, RPMTAG_OBSOLETEFLAGS);
03458 }
03459 
03468 static /*@only@*/ char * spewescapeFormat(HE_t he, /*@null@*/ const char ** av,
03469                 spew_t spew, int lvl)
03470         /*@*/
03471 {
03472     int ix = (he->ix > 0 ? he->ix : 0);
03473     char * val;
03474 
03475 assert(ix == 0);
03476     if (he->t != RPM_STRING_TYPE) {
03477         val = xstrdup(_("(not a string)"));
03478     } else {
03479         const char * s = strdup_locale_convert(he->p.str, (av ? av[0] : NULL));
03480         size_t nb;
03481         char * t;
03482 
03483         if (s == NULL) {
03484             /* XXX better error msg? */
03485             val = xstrdup(_("(not a string)"));
03486             goto exit;
03487         }
03488 
03489         nb = spew->spew_strlen(s, lvl);
03490         val = t = xcalloc(1, nb + 1);
03491         t = spew->spew_strcpy(t, s, lvl);       t += strlen(t);
03492         *t = '\0';
03493         s = _free(s);
03494     }
03495 
03496 exit:
03497     return val;
03498 }
03499 
03500 #ifndef DYING   /* XXX is :json gud enuf? there are side effects ... */
03501 static /*@only@*/ char * jsonescapeFormat(HE_t he, /*@null@*/ const char ** av)
03502         /*@*/
03503 {
03504     return spewescapeFormat(he, av, &_json_spew, 0);
03505 }
03506 #endif
03507 
03508 static /*@only@*/ char * sqlescapeFormat(HE_t he, /*@null@*/ const char ** av)
03509         /*@*/
03510 {
03511     return spewescapeFormat(he, av, &_sql_spew, 0);
03512 }
03513 
03514 /*@-compmempass -kepttrans -nullstate -usereleased @*/
03515 static int PRCOsqlTag(Header h, HE_t he, rpmTag EVRtag, rpmTag Ftag)
03516         /*@globals internalState @*/
03517         /*@modifies he, internalState @*/
03518 {
03519     rpmTag tag = he->tag;
03520     rpmTagData N = { .ptr = NULL };
03521     rpmTagData EVR = { .ptr = NULL };
03522     rpmTagData F = { .ptr = NULL };
03523     char instance[64];
03524     size_t nb;
03525     rpmuint32_t ac;
03526     rpmuint32_t c;
03527     rpmuint32_t i;
03528     char *t;
03529     int rc = 1;         /* assume failure */
03530     int xx;
03531 
03532 /*@-compmempass@*/      /* use separate HE_t, not rpmTagData, containers. */
03533     xx = headerGet(h, he, 0);
03534     if (xx == 0) goto exit;
03535     N.argv = he->p.argv;
03536     c = he->c;
03537 
03538     he->tag = EVRtag;
03539     xx = headerGet(h, he, 0);
03540     if (xx == 0) goto exit;
03541     EVR.argv = he->p.argv;
03542 
03543     he->tag = Ftag;
03544     xx = headerGet(h, he, 0);
03545     if (xx == 0) goto exit;
03546     F.ui32p = he->p.ui32p;
03547 
03548     xx = snprintf(instance, sizeof(instance), "'%u'", (unsigned)headerGetInstance(h));
03549     nb = sizeof(*he->p.argv);
03550     ac = 0;
03551     for (i = 0; i < c; i++) {
03552 /*@-nullstate@*/        /* EVR.argv might be NULL */
03553         if (PRCOSkip(tag, N, EVR, F, i))
03554             continue;
03555 /*@=nullstate@*/
03556         ac++;
03557         nb += sizeof(*he->p.argv);
03558         nb += strlen(instance) + sizeof(", '', '', '', '', ''");
03559         if (tag == RPMTAG_REQUIRENAME)
03560             nb += sizeof(", ''") - 1;
03561         nb += strlen(N.argv[i]);
03562         if (EVR.argv != NULL && EVR.argv[i] != NULL && *EVR.argv[i] != '\0') {
03563             nb += strlen(EVR.argv[i]);
03564             nb += sizeof("EQ0") - 1;
03565         }
03566 #ifdef  NOTNOW
03567         if (tag == RPMTAG_REQUIRENAME && (F.ui32p[i] & 0x40))
03568             nb += sizeof("1") - 1;
03569 #endif
03570     }
03571 
03572     he->t = RPM_STRING_ARRAY_TYPE;
03573     he->c = ac;
03574     he->freeData = 1;
03575     he->p.argv = xmalloc(nb + BUFSIZ);  /* XXX hack: leave slop */
03576     t = (char *) &he->p.argv[he->c + 1];
03577     ac = 0;
03578     for (i = 0; i < c; i++) {
03579 /*@-nullstate@*/        /* EVR.argv might be NULL */
03580         if (PRCOSkip(tag, N, EVR, F, i))
03581             continue;
03582 /*@=nullstate@*/
03583         he->p.argv[ac++] = t;
03584         t = stpcpy(t, instance);
03585         t = stpcpy( stpcpy( stpcpy(t, ", '"), N.argv[i]), "'");
03586 /*@-readonlytrans@*/
03587         if (EVR.argv != NULL && EVR.argv[i] != NULL && *EVR.argv[i] != '\0') {
03588             static char *Fstr[] = { "?0","LT","GT","?3","EQ","LE","GE","?7" };
03589             rpmuint32_t Fx = ((F.ui32p[i] >> 1) & 0x7);
03590             const char *E, *V, *R;
03591             char *f, *fe;
03592             t = stpcpy( stpcpy( stpcpy(t, ", '"), Fstr[Fx]), "'");
03593             f = (char *) EVR.argv[i];
03594             for (fe = f; *fe != '\0' && *fe >= '0' && *fe <= '9'; fe++)
03595                 {};
03596             if (*fe == ':') { *fe++ = '\0'; E = f; f = fe; } else E = NULL;
03597             V = f;
03598             for (fe = f; *fe != '\0' && *fe != '-'; fe++)
03599                 {};
03600             if (*fe == '-') { *fe++ = '\0'; R = fe; } else R = NULL;
03601             t = stpcpy( stpcpy( stpcpy(t, ", '"), (E && *E ? E : "0")), "'");
03602             t = stpcpy( stpcpy( stpcpy(t, ", '"), V), "'");
03603             t = stpcpy( stpcpy( stpcpy(t, ", '"), (R ? R : "")), "'");
03604         } else
03605             t = stpcpy(t, ", '', '', '', ''");
03606 /*@=readonlytrans@*/
03607 #ifdef  NOTNOW
03608         if (tag == RPMTAG_REQUIRENAME)
03609             t = stpcpy(stpcpy(stpcpy(t, ", '"),(F.ui32p[i] & 0x40) ? "1" : "0"), "'");
03610 #endif
03611         *t++ = '\0';
03612     }
03613     he->p.argv[he->c] = NULL;
03614 /*@=compmempass@*/
03615     rc = 0;
03616 
03617 exit:
03618 /*@-kepttrans@*/        /* N.argv may be kept. */
03619     N.argv = _free(N.argv);
03620 /*@=kepttrans@*/
03621 /*@-usereleased@*/      /* EVR.argv may be dead. */
03622     EVR.argv = _free(EVR.argv);
03623 /*@=usereleased@*/
03624     F.ui32p = _free(F.ui32p);
03625     return rc;
03626 }
03627 
03628 static int PsqlTag(Header h, HE_t he)
03629         /*@globals internalState @*/
03630         /*@modifies he, internalState @*/
03631 {
03632     he->tag = RPMTAG_PROVIDENAME;
03633     return PRCOsqlTag(h, he, RPMTAG_PROVIDEVERSION, RPMTAG_PROVIDEFLAGS);
03634 }
03635 
03636 static int RsqlTag(Header h, HE_t he)
03637         /*@globals internalState @*/
03638         /*@modifies he, internalState @*/
03639 {
03640     he->tag = RPMTAG_REQUIRENAME;
03641     return PRCOsqlTag(h, he, RPMTAG_REQUIREVERSION, RPMTAG_REQUIREFLAGS);
03642 }
03643 
03644 static int CsqlTag(Header h, HE_t he)
03645         /*@globals internalState @*/
03646         /*@modifies he, internalState @*/
03647 {
03648     he->tag = RPMTAG_CONFLICTNAME;
03649     return PRCOsqlTag(h, he, RPMTAG_CONFLICTVERSION, RPMTAG_CONFLICTFLAGS);
03650 }
03651 
03652 static int OsqlTag(Header h, HE_t he)
03653         /*@globals internalState @*/
03654         /*@modifies he, internalState @*/
03655 {
03656     he->tag = RPMTAG_OBSOLETENAME;
03657     return PRCOsqlTag(h, he, RPMTAG_OBSOLETEVERSION, RPMTAG_OBSOLETEFLAGS);
03658 }
03659 
03660 static int PRCOyamlTag(Header h, HE_t he, rpmTag EVRtag, rpmTag Ftag)
03661         /*@globals internalState @*/
03662         /*@modifies he, internalState @*/
03663 {
03664     rpmTag tag = he->tag;
03665     rpmTagData N = { .ptr = NULL };
03666     rpmTagData EVR = { .ptr = NULL };
03667     rpmTagData F = { .ptr = NULL };
03668     size_t nb;
03669     rpmuint32_t ac;
03670     rpmuint32_t c;
03671     rpmuint32_t i;
03672     char *t;
03673     int rc = 1;         /* assume failure */
03674     int xx;
03675 int indent = 0;
03676 spew_t spew = &_yaml_spew;
03677 
03678 /*@-compmempass@*/      /* use separate HE_t, not rpmTagData, containers. */
03679     xx = headerGet(h, he, 0);
03680     if (xx == 0) goto exit;
03681     N.argv = he->p.argv;
03682     c = he->c;
03683 
03684     he->tag = EVRtag;
03685     xx = headerGet(h, he, 0);
03686     if (xx == 0) goto exit;
03687     EVR.argv = he->p.argv;
03688 
03689     he->tag = Ftag;
03690     xx = headerGet(h, he, 0);
03691     if (xx == 0) goto exit;
03692     F.ui32p = he->p.ui32p;
03693 
03694     nb = sizeof(*he->p.argv);
03695     ac = 0;
03696     for (i = 0; i < c; i++) {
03697 /*@-nullstate@*/        /* EVR.argv might be NULL */
03698         if (PRCOSkip(tag, N, EVR, F, i))
03699             continue;
03700 /*@=nullstate@*/
03701         ac++;
03702         nb += sizeof(*he->p.argv);
03703         nb += sizeof("- ");
03704         if (*N.argv[i] == '/')
03705             nb += spew->spew_strlen(N.argv[i], indent);
03706         else
03707             nb += strlen(N.argv[i]);
03708         if (EVR.argv != NULL && EVR.argv[i] != NULL && *EVR.argv[i] != '\0') {
03709             nb += sizeof(" >= ") - 1;
03710             nb += strlen(EVR.argv[i]);
03711         }
03712     }
03713 
03714     he->t = RPM_STRING_ARRAY_TYPE;
03715     he->c = ac;
03716     he->freeData = 1;
03717     he->p.argv = xmalloc(nb + BUFSIZ);  /* XXX hack: leave slop */
03718     t = (char *) &he->p.argv[he->c + 1];
03719     ac = 0;
03720     for (i = 0; i < c; i++) {
03721 /*@-nullstate@*/        /* EVR.argv might be NULL */
03722         if (PRCOSkip(tag, N, EVR, F, i))
03723             continue;
03724 /*@=nullstate@*/
03725         he->p.argv[ac++] = t;
03726         t = stpcpy(t, "- ");
03727         if (*N.argv[i] == '/') {
03728             t = spew->spew_strcpy(t, N.argv[i], indent);        t += strlen(t);
03729         } else
03730             t = stpcpy(t, N.argv[i]);
03731 /*@-readonlytrans@*/
03732         if (EVR.argv != NULL && EVR.argv[i] != NULL && *EVR.argv[i] != '\0') {
03733             static char *Fstr[] = { "?0","<",">","?3","=","<=",">=","?7" };
03734             rpmuint32_t Fx = ((F.ui32p[i] >> 1) & 0x7);
03735             t = stpcpy( stpcpy( stpcpy(t, " "), Fstr[Fx]), " ");
03736             t = stpcpy(t, EVR.argv[i]);
03737         }
03738 /*@=readonlytrans@*/
03739         *t++ = '\0';
03740     }
03741     he->p.argv[he->c] = NULL;
03742 /*@=compmempass@*/
03743     rc = 0;
03744 
03745 exit:
03746 /*@-kepttrans@*/        /* N.argv may be kept. */
03747     N.argv = _free(N.argv);
03748 /*@=kepttrans@*/
03749 /*@-usereleased@*/      /* EVR.argv may be dead. */
03750     EVR.argv = _free(EVR.argv);
03751 /*@=usereleased@*/
03752     F.ui32p = _free(F.ui32p);
03753     return rc;
03754 }
03755 
03756 static int PyamlTag(Header h, HE_t he)
03757         /*@globals internalState @*/
03758         /*@modifies he, internalState @*/
03759 {
03760     int rc;
03761     he->tag = RPMTAG_PROVIDENAME;
03762     rc = PRCOyamlTag(h, he, RPMTAG_PROVIDEVERSION, RPMTAG_PROVIDEFLAGS);
03763     he->tag = RPMTAG_PROVIDEYAMLENTRY;
03764     return rc;
03765 }
03766 
03767 static int RyamlTag(Header h, HE_t he)
03768         /*@globals internalState @*/
03769         /*@modifies he, internalState @*/
03770 {
03771     int rc;
03772     he->tag = RPMTAG_REQUIRENAME;
03773     rc = PRCOyamlTag(h, he, RPMTAG_REQUIREVERSION, RPMTAG_REQUIREFLAGS);
03774     he->tag = RPMTAG_REQUIREYAMLENTRY;
03775     return rc;
03776 }
03777 
03778 static int CyamlTag(Header h, HE_t he)
03779         /*@globals internalState @*/
03780         /*@modifies he, internalState @*/
03781 {
03782     int rc;
03783     he->tag = RPMTAG_CONFLICTNAME;
03784     rc = PRCOyamlTag(h, he, RPMTAG_CONFLICTVERSION, RPMTAG_CONFLICTFLAGS);
03785     he->tag = RPMTAG_CONFLICTYAMLENTRY;
03786     return rc;
03787 }
03788 
03789 static int OyamlTag(Header h, HE_t he)
03790         /*@globals internalState @*/
03791         /*@modifies he, internalState @*/
03792 {
03793     int rc;
03794     he->tag = RPMTAG_OBSOLETENAME;
03795     rc = PRCOyamlTag(h, he, RPMTAG_OBSOLETEVERSION, RPMTAG_OBSOLETEFLAGS);
03796     he->tag = RPMTAG_OBSOLETEYAMLENTRY;
03797     return rc;
03798 }
03799 
03800 static int FDGSkip(rpmTagData DN, rpmTagData BN, rpmTagData DI, rpmuint32_t i)
03801         /*@*/
03802 {
03803     const char * dn = DN.argv[DI.ui32p[i]];
03804     size_t dnlen = strlen(dn);
03805 
03806 assert(dn != NULL);
03807     if (strstr(dn, "bin/") != NULL)
03808         return 1;
03809     if (dnlen >= sizeof("/etc/")-1 && !strncmp(dn, "/etc/", dnlen))
03810         return 1;
03811     if (!strcmp(dn, "/usr/lib/") && !strcmp(BN.argv[i], "sendmail"))
03812         return 1;
03813     return 2;
03814 }
03815 
03816 static int FDGxmlTag(Header h, HE_t he, int lvl)
03817         /*@globals internalState @*/
03818         /*@modifies he, internalState @*/
03819 {
03820     rpmTagData BN = { .ptr = NULL };
03821     rpmTagData DN = { .ptr = NULL };
03822     rpmTagData DI = { .ptr = NULL };
03823     rpmTagData FMODES = { .ptr = NULL };
03824     rpmTagData FFLAGS = { .ptr = NULL };
03825     size_t nb;
03826     rpmuint32_t ac;
03827     rpmuint32_t c;
03828     rpmuint32_t i;
03829     char *t;
03830     int rc = 1;         /* assume failure */
03831     int xx;
03832 spew_t spew = &_xml_spew;
03833 
03834 /*@-compmempass@*/      /* use separate HE_t, not rpmTagData, containers. */
03835     he->tag = RPMTAG_BASENAMES;
03836     xx = headerGet(h, he, 0);
03837     if (xx == 0) goto exit;
03838     BN.argv = he->p.argv;
03839     c = he->c;
03840 
03841     he->tag = RPMTAG_DIRNAMES;
03842     xx = headerGet(h, he, 0);
03843     if (xx == 0) goto exit;
03844     DN.argv = he->p.argv;
03845 
03846     he->tag = RPMTAG_DIRINDEXES;
03847     xx = headerGet(h, he, 0);
03848     if (xx == 0) goto exit;
03849     DI.ui32p = he->p.ui32p;
03850 
03851     he->tag = RPMTAG_FILEMODES;
03852     xx = headerGet(h, he, 0);
03853     if (xx == 0) goto exit;
03854     FMODES.ui16p = he->p.ui16p;
03855 
03856     he->tag = RPMTAG_FILEFLAGS;
03857     xx = headerGet(h, he, 0);
03858     if (xx == 0) goto exit;
03859     FFLAGS.ui32p = he->p.ui32p;
03860 
03861     nb = sizeof(*he->p.argv);
03862     ac = 0;
03863     for (i = 0; i < c; i++) {
03864         if (lvl > 0 && FDGSkip(DN, BN, DI, i) != lvl)
03865             continue;
03866         ac++;
03867         nb += sizeof(*he->p.argv);
03868         nb += sizeof("<file></file>");
03869         nb += spew->spew_strlen(DN.argv[DI.ui32p[i]], lvl);
03870         nb += spew->spew_strlen(BN.argv[i], lvl);
03871         if (FFLAGS.ui32p[i] & 0x40)     /* XXX RPMFILE_GHOST */
03872             nb += sizeof(" type=\"ghost\"") - 1;
03873         else if (S_ISDIR(FMODES.ui16p[i])) {
03874             nb += sizeof(" type=\"dir\"") - 1;
03875 #ifdef  NOTYET
03876             nb += sizeof("/") - 1;
03877 #endif
03878         }
03879     }
03880 
03881     he->t = RPM_STRING_ARRAY_TYPE;
03882     he->c = ac;
03883     he->freeData = 1;
03884     he->p.argv = xmalloc(nb);
03885     t = (char *) &he->p.argv[he->c + 1];
03886     ac = 0;
03887     /* FIXME: Files, then dirs, finally ghosts breaks sort order.  */
03888     for (i = 0; i < c; i++) {
03889         if (lvl > 0 && FDGSkip(DN, BN, DI, i) != lvl)
03890             continue;
03891         if (FFLAGS.ui32p[i] & 0x40)     /* XXX RPMFILE_GHOST */
03892             continue;
03893         if (S_ISDIR(FMODES.ui16p[i]))
03894             continue;
03895         he->p.argv[ac++] = t;
03896         t = stpcpy(t, "<file>");
03897         t = spew->spew_strcpy(t, DN.argv[DI.ui32p[i]], lvl);    t += strlen(t);
03898         t = spew->spew_strcpy(t, BN.argv[i], lvl);              t += strlen(t);
03899         t = stpcpy(t, "</file>");
03900         *t++ = '\0';
03901     }
03902     for (i = 0; i < c; i++) {
03903         if (lvl > 0 && FDGSkip(DN, BN, DI, i) != lvl)
03904             continue;
03905         if (FFLAGS.ui32p[i] & 0x40)     /* XXX RPMFILE_GHOST */
03906             continue;
03907         if (!S_ISDIR(FMODES.ui16p[i]))
03908             continue;
03909         he->p.argv[ac++] = t;
03910         t = stpcpy(t, "<file type=\"dir\">");
03911         t = spew->spew_strcpy(t, DN.argv[DI.ui32p[i]], lvl);    t += strlen(t);
03912         t = spew->spew_strcpy(t, BN.argv[i], lvl);              t += strlen(t);
03913 #ifdef  NOTYET
03914         /* Append the pesky trailing / to directories. */
03915         if (t[-1] != '/')
03916             t = stpcpy(t, "/");
03917 #endif
03918         t = stpcpy(t, "</file>");
03919         *t++ = '\0';
03920     }
03921     for (i = 0; i < c; i++) {
03922         if (lvl > 0 && FDGSkip(DN, BN, DI, i) != lvl)
03923             continue;
03924         if (!(FFLAGS.ui32p[i] & 0x40))  /* XXX RPMFILE_GHOST */
03925             continue;
03926         he->p.argv[ac++] = t;
03927         t = stpcpy(t, "<file type=\"ghost\">");
03928         t = spew->spew_strcpy(t, DN.argv[DI.ui32p[i]], lvl);    t += strlen(t);
03929         t = spew->spew_strcpy(t, BN.argv[i], lvl);              t += strlen(t);
03930         t = stpcpy(t, "</file>");
03931         *t++ = '\0';
03932     }
03933 
03934     he->p.argv[he->c] = NULL;
03935 /*@=compmempass@*/
03936     rc = 0;
03937 
03938 exit:
03939 /*@-kepttrans@*/        /* {BN,DN,DI}.argv may be kept. */
03940     BN.argv = _free(BN.argv);
03941 /*@-usereleased@*/      /* DN.argv may be dead. */
03942     DN.argv = _free(DN.argv);
03943 /*@=usereleased@*/
03944     DI.ui32p = _free(DI.ui32p);
03945 /*@=kepttrans@*/
03946     FMODES.ui16p = _free(FMODES.ui16p);
03947 /*@-usereleased@*/      /* FFLAGS.argv may be dead. */
03948     FFLAGS.ui32p = _free(FFLAGS.ui32p);
03949 /*@=usereleased@*/
03950     return rc;
03951 }
03952 
03953 static int F1xmlTag(Header h, HE_t he)
03954         /*@globals internalState @*/
03955         /*@modifies he, internalState @*/
03956 {
03957     he->tag = RPMTAG_BASENAMES;
03958     return FDGxmlTag(h, he, 1);
03959 }
03960 
03961 static int F2xmlTag(Header h, HE_t he)
03962         /*@globals internalState @*/
03963         /*@modifies he, internalState @*/
03964 {
03965     he->tag = RPMTAG_BASENAMES;
03966     return FDGxmlTag(h, he, 2);
03967 }
03968 
03969 static int FDGsqlTag(Header h, HE_t he, int lvl)
03970         /*@globals internalState @*/
03971         /*@modifies he, internalState @*/
03972 {
03973     rpmTagData BN = { .ptr = NULL };
03974     rpmTagData DN = { .ptr = NULL };
03975     rpmTagData DI = { .ptr = NULL };
03976     rpmTagData FMODES = { .ptr = NULL };
03977     rpmTagData FFLAGS = { .ptr = NULL };
03978     char instance[64];
03979     size_t nb;
03980     rpmuint32_t ac;
03981     rpmuint32_t c;
03982     rpmuint32_t i;
03983     char *t;
03984     int rc = 1;         /* assume failure */
03985     int xx;
03986 
03987 /*@-compmempass@*/      /* use separate HE_t, not rpmTagData, containers. */
03988     he->tag = RPMTAG_BASENAMES;
03989     xx = headerGet(h, he, 0);
03990     if (xx == 0) goto exit;
03991     BN.argv = he->p.argv;
03992     c = he->c;
03993 
03994     he->tag = RPMTAG_DIRNAMES;
03995     xx = headerGet(h, he, 0);
03996     if (xx == 0) goto exit;
03997     DN.argv = he->p.argv;
03998 
03999     he->tag = RPMTAG_DIRINDEXES;
04000     xx = headerGet(h, he, 0);
04001     if (xx == 0) goto exit;
04002     DI.ui32p = he->p.ui32p;
04003 
04004     he->tag = RPMTAG_FILEMODES;
04005     xx = headerGet(h, he, 0);
04006     if (xx == 0) goto exit;
04007     FMODES.ui16p = he->p.ui16p;
04008 
04009     he->tag = RPMTAG_FILEFLAGS;
04010     xx = headerGet(h, he, 0);
04011     if (xx == 0) goto exit;
04012     FFLAGS.ui32p = he->p.ui32p;
04013 
04014     xx = snprintf(instance, sizeof(instance), "'%u'", (unsigned)headerGetInstance(h));
04015     nb = sizeof(*he->p.argv);
04016     ac = 0;
04017     for (i = 0; i < c; i++) {
04018         if (lvl > 0 && FDGSkip(DN, BN, DI, i) != lvl)
04019             continue;
04020         ac++;
04021         nb += sizeof(*he->p.argv);
04022         nb += strlen(instance) + sizeof(", '', ''");
04023         nb += strlen(DN.argv[DI.ui32p[i]]);
04024         nb += strlen(BN.argv[i]);
04025         if (FFLAGS.ui32p[i] & 0x40)     /* XXX RPMFILE_GHOST */
04026             nb += sizeof("ghost") - 1;
04027         else if (S_ISDIR(FMODES.ui16p[i])) {
04028             nb += sizeof("dir") - 1;
04029 #ifdef  NOTYET
04030             nb += sizeof("/") - 1;
04031 #endif
04032         } else
04033             nb += sizeof("file") - 1;
04034     }
04035 
04036     he->t = RPM_STRING_ARRAY_TYPE;
04037     he->c = ac;
04038     he->freeData = 1;
04039     he->p.argv = xmalloc(nb);
04040     t = (char *) &he->p.argv[he->c + 1];
04041     ac = 0;
04042     /* FIXME: Files, then dirs, finally ghosts breaks sort order.  */
04043     for (i = 0; i < c; i++) {
04044         if (lvl > 0 && FDGSkip(DN, BN, DI, i) != lvl)
04045             continue;
04046         if (FFLAGS.ui32p[i] & 0x40)     /* XXX RPMFILE_GHOST */
04047             continue;
04048         if (S_ISDIR(FMODES.ui16p[i]))
04049             continue;
04050         he->p.argv[ac++] = t;
04051         t = stpcpy( stpcpy(t, instance), ", '");
04052         t = strcpy(t, DN.argv[DI.ui32p[i]]);    t += strlen(t);
04053         t = strcpy(t, BN.argv[i]);              t += strlen(t);
04054         t = stpcpy(t, "', 'file'");
04055         *t++ = '\0';
04056     }
04057     for (i = 0; i < c; i++) {
04058         if (lvl > 0 && FDGSkip(DN, BN, DI, i) != lvl)
04059             continue;
04060         if (FFLAGS.ui32p[i] & 0x40)     /* XXX RPMFILE_GHOST */
04061             continue;
04062         if (!S_ISDIR(FMODES.ui16p[i]))
04063             continue;
04064         he->p.argv[ac++] = t;
04065         t = stpcpy( stpcpy(t, instance), ", '");
04066         t = strcpy(t, DN.argv[DI.ui32p[i]]);    t += strlen(t);
04067         t = strcpy(t, BN.argv[i]);              t += strlen(t);
04068 #ifdef  NOTYET
04069         /* Append the pesky trailing / to directories. */
04070         if (t[-1] != '/')
04071             t = stpcpy(t, "/");
04072 #endif
04073         t = stpcpy(t, "', 'dir'");
04074         *t++ = '\0';
04075     }
04076     for (i = 0; i < c; i++) {
04077         if (lvl > 0 && FDGSkip(DN, BN, DI, i) != lvl)
04078             continue;
04079         if (!(FFLAGS.ui32p[i] & 0x40))  /* XXX RPMFILE_GHOST */
04080             continue;
04081         he->p.argv[ac++] = t;
04082         t = stpcpy( stpcpy(t, instance), ", '");
04083         t = strcpy(t, DN.argv[DI.ui32p[i]]);    t += strlen(t);
04084         t = strcpy(t, BN.argv[i]);              t += strlen(t);
04085         t = stpcpy(t, "', 'ghost'");
04086         *t++ = '\0';
04087     }
04088 
04089     he->p.argv[he->c] = NULL;
04090 /*@=compmempass@*/
04091     rc = 0;
04092 
04093 exit:
04094 /*@-kepttrans@*/        /* {BN,DN,DI}.argv may be kept. */
04095     BN.argv = _free(BN.argv);
04096 /*@-usereleased@*/      /* DN.argv may be dead. */
04097     DN.argv = _free(DN.argv);
04098 /*@=usereleased@*/
04099     DI.ui32p = _free(DI.ui32p);
04100 /*@=kepttrans@*/
04101     FMODES.ui16p = _free(FMODES.ui16p);
04102 /*@-usereleased@*/      /* FFLAGS.argv may be dead. */
04103     FFLAGS.ui32p = _free(FFLAGS.ui32p);
04104 /*@=usereleased@*/
04105     return rc;
04106 }
04107 
04108 static int F1sqlTag(Header h, HE_t he)
04109         /*@globals internalState @*/
04110         /*@modifies he, internalState @*/
04111 {
04112     he->tag = RPMTAG_BASENAMES;
04113     return FDGsqlTag(h, he, 1);
04114 }
04115 
04116 static int F2sqlTag(Header h, HE_t he)
04117         /*@globals internalState @*/
04118         /*@modifies he, internalState @*/
04119 {
04120     he->tag = RPMTAG_BASENAMES;
04121     return FDGsqlTag(h, he, 2);
04122 }
04123 
04124 static int FDGyamlTag(Header h, HE_t he, int lvl)
04125         /*@globals internalState @*/
04126         /*@modifies he, internalState @*/
04127 {
04128     rpmTagData BN = { .ptr = NULL };
04129     rpmTagData DN = { .ptr = NULL };
04130     rpmTagData DI = { .ptr = NULL };
04131     rpmTagData FMODES = { .ptr = NULL };
04132     rpmTagData FFLAGS = { .ptr = NULL };
04133     size_t nb;
04134     rpmuint32_t ac;
04135     rpmuint32_t c;
04136     rpmuint32_t i;
04137     char *t;
04138     int rc = 1;         /* assume failure */
04139     int xx;
04140 int indent = 0;
04141 spew_t spew = &_yaml_spew;
04142 
04143 /*@-compmempass@*/      /* use separate HE_t, not rpmTagData, containers. */
04144     he->tag = RPMTAG_BASENAMES;
04145     xx = headerGet(h, he, 0);
04146     if (xx == 0) goto exit;
04147     BN.argv = he->p.argv;
04148     c = he->c;
04149 
04150     he->tag = RPMTAG_DIRNAMES;
04151     xx = headerGet(h, he, 0);
04152     if (xx == 0) goto exit;
04153     DN.argv = he->p.argv;
04154 
04155     he->tag = RPMTAG_DIRINDEXES;
04156     xx = headerGet(h, he, 0);
04157     if (xx == 0) goto exit;
04158     DI.ui32p = he->p.ui32p;
04159 
04160     he->tag = RPMTAG_FILEMODES;
04161     xx = headerGet(h, he, 0);
04162     if (xx == 0) goto exit;
04163     FMODES.ui16p = he->p.ui16p;
04164 
04165     he->tag = RPMTAG_FILEFLAGS;
04166     xx = headerGet(h, he, 0);
04167     if (xx == 0) goto exit;
04168     FFLAGS.ui32p = he->p.ui32p;
04169 
04170     nb = sizeof(*he->p.argv);
04171     ac = 0;
04172     for (i = 0; i < c; i++) {
04173         if (lvl > 0 && FDGSkip(DN, BN, DI, i) != lvl)
04174             continue;
04175         ac++;
04176         nb += sizeof(*he->p.argv);
04177         nb += sizeof("- ");
04178         nb += spew->spew_strlen(DN.argv[DI.ui32p[i]], indent);
04179         nb += spew->spew_strlen(BN.argv[i], indent);
04180         if (FFLAGS.ui32p[i] & 0x40)     /* XXX RPMFILE_GHOST */
04181             nb += sizeof("") - 1;
04182         else if (S_ISDIR(FMODES.ui16p[i]))
04183             nb += sizeof("/") - 1;
04184     }
04185 
04186     he->t = RPM_STRING_ARRAY_TYPE;
04187     he->c = ac;
04188     he->freeData = 1;
04189     he->p.argv = xmalloc(nb);
04190     t = (char *) &he->p.argv[he->c + 1];
04191     ac = 0;
04192     /* FIXME: Files, then dirs, finally ghosts breaks sort order.  */
04193     for (i = 0; i < c; i++) {
04194         if (lvl > 0 && FDGSkip(DN, BN, DI, i) != lvl)
04195             continue;
04196         if (FFLAGS.ui32p[i] & 0x40)     /* XXX RPMFILE_GHOST */
04197             continue;
04198         if (S_ISDIR(FMODES.ui16p[i]))
04199             continue;
04200         he->p.argv[ac++] = t;
04201         t = stpcpy(t, "- ");
04202         t = spew->spew_strcpy(t, DN.argv[DI.ui32p[i]], indent); t += strlen(t);
04203         t = spew->spew_strcpy(t, BN.argv[i], indent);           t += strlen(t);
04204         t = stpcpy(t, "");
04205         *t++ = '\0';
04206     }
04207     for (i = 0; i < c; i++) {
04208         if (lvl > 0 && FDGSkip(DN, BN, DI, i) != lvl)
04209             continue;
04210         if (FFLAGS.ui32p[i] & 0x40)     /* XXX RPMFILE_GHOST */
04211             continue;
04212         if (!S_ISDIR(FMODES.ui16p[i]))
04213             continue;
04214         he->p.argv[ac++] = t;
04215         t = stpcpy(t, "- ");
04216         t = spew->spew_strcpy(t, DN.argv[DI.ui32p[i]], indent); t += strlen(t);
04217         t = spew->spew_strcpy(t, BN.argv[i], indent);           t += strlen(t);
04218         /* Append the pesky trailing / to directories. */
04219         if (t[-1] != '/')
04220             t = stpcpy(t, "/");
04221         *t++ = '\0';
04222     }
04223     for (i = 0; i < c; i++) {
04224         if (lvl > 0 && FDGSkip(DN, BN, DI, i) != lvl)
04225             continue;
04226         if (!(FFLAGS.ui32p[i] & 0x40))  /* XXX RPMFILE_GHOST */
04227             continue;
04228         he->p.argv[ac++] = t;
04229         t = stpcpy(t, "- ");
04230         t = spew->spew_strcpy(t, DN.argv[DI.ui32p[i]], indent); t += strlen(t);
04231         t = spew->spew_strcpy(t, BN.argv[i], indent);           t += strlen(t);
04232         *t++ = '\0';
04233     }
04234 
04235     he->p.argv[he->c] = NULL;
04236 /*@=compmempass@*/
04237     rc = 0;
04238 
04239 exit:
04240 /*@-kepttrans@*/        /* {BN,DN,DI}.argv may be kept. */
04241     BN.argv = _free(BN.argv);
04242 /*@-usereleased@*/      /* DN.argv may be dead. */
04243     DN.argv = _free(DN.argv);
04244 /*@=usereleased@*/
04245     DI.ui32p = _free(DI.ui32p);
04246 /*@=kepttrans@*/
04247     FMODES.ui16p = _free(FMODES.ui16p);
04248 /*@-usereleased@*/      /* FFLAGS.argv may be dead. */
04249     FFLAGS.ui32p = _free(FFLAGS.ui32p);
04250 /*@=usereleased@*/
04251     return rc;
04252 }
04253 
04254 static int F1yamlTag(Header h, HE_t he)
04255         /*@globals internalState @*/
04256         /*@modifies he, internalState @*/
04257 {
04258     he->tag = RPMTAG_BASENAMES;
04259     return FDGyamlTag(h, he, 1);
04260 }
04261 
04262 static int F2yamlTag(Header h, HE_t he)
04263         /*@globals internalState @*/
04264         /*@modifies he, internalState @*/
04265 {
04266     he->tag = RPMTAG_BASENAMES;
04267     return FDGyamlTag(h, he, 2);
04268 }
04269 
04276 static /*@only@*/ char * bncdataFormat(HE_t he, /*@null@*/ const char ** av)
04277         /*@*/
04278 {
04279     char * val;
04280 
04281     if (he->t != RPM_STRING_TYPE) {
04282         val = xstrdup(_("(not a string)"));
04283     } else {
04284         const char * bn;
04285         const char * s;
04286         size_t nb;
04287         char * t;
04288 int lvl = 0;
04289 spew_t spew = &_xml_spew;
04290 
04291 assert(he->p.str != NULL);
04292         /* Get rightmost '/' in string (i.e. basename(3) behavior). */
04293         if ((bn = strrchr(he->p.str, '/')) != NULL)
04294             bn++;
04295         else
04296             bn = he->p.str;
04297 
04298         /* Convert to utf8, escape for XML CDATA. */
04299         s = strdup_locale_convert(bn, (av ? av[0] : NULL));
04300         if (s == NULL) {
04301             /* XXX better error msg? */
04302             val = xstrdup(_("(not a string)"));
04303             goto exit;
04304         }
04305 
04306         nb = spew->spew_strlen(s, lvl);
04307         val = t = xcalloc(1, nb + 1);
04308         t = spew->spew_strcpy(t, s, lvl);       t += strlen(t);
04309         *t = '\0';
04310         s = _free(s);
04311     }
04312 
04313 exit:
04314     return val;
04315 }
04316 
04317 typedef struct key_s {
04318 /*@observer@*/
04319         const char *name;               /* key name */
04320         rpmuint32_t value;
04321 } KEY;
04322 
04323 /*@unchecked@*/ /*@observer@*/
04324 static KEY keyDigests[] = {
04325     { "adler32",        PGPHASHALGO_ADLER32 },
04326     { "crc32",          PGPHASHALGO_CRC32 },
04327     { "crc64",          PGPHASHALGO_CRC64 },
04328     { "haval160",       PGPHASHALGO_HAVAL_5_160 },
04329     { "jlu32",          PGPHASHALGO_JLU32 },
04330     { "md2",            PGPHASHALGO_MD2 },
04331     { "md4",            PGPHASHALGO_MD4 },
04332     { "md5",            PGPHASHALGO_MD5 },
04333     { "rmd128",         PGPHASHALGO_RIPEMD128 },
04334     { "rmd160",         PGPHASHALGO_RIPEMD160 },
04335     { "rmd256",         PGPHASHALGO_RIPEMD256 },
04336     { "rmd320",         PGPHASHALGO_RIPEMD320 },
04337     { "salsa10",        PGPHASHALGO_SALSA10 },
04338     { "salsa20",        PGPHASHALGO_SALSA20 },
04339     { "sha1",           PGPHASHALGO_SHA1 },
04340     { "sha224",         PGPHASHALGO_SHA224 },
04341     { "sha256",         PGPHASHALGO_SHA256 },
04342     { "sha384",         PGPHASHALGO_SHA384 },
04343     { "sha512",         PGPHASHALGO_SHA512 },
04344     { "tiger192",       PGPHASHALGO_TIGER192 },
04345 };
04346 /*@unchecked@*/
04347 static size_t nkeyDigests = sizeof(keyDigests) / sizeof(keyDigests[0]);
04348 
04352 enum keyStat_e {
04353     STAT_KEYS_NONE      = 0,
04354     STAT_KEYS_DEV       = (1U <<  0),   
04355     STAT_KEYS_INO       = (1U <<  1),   
04356     STAT_KEYS_MODE      = (1U <<  2),   
04357     STAT_KEYS_NLINK     = (1U <<  3),   
04358     STAT_KEYS_UID       = (1U <<  4),   
04359     STAT_KEYS_GID       = (1U <<  5),   
04360     STAT_KEYS_RDEV      = (1U <<  6),   
04361     STAT_KEYS_SIZE      = (1U <<  7),   
04362     STAT_KEYS_BLKSIZE   = (1U <<  8),   
04363     STAT_KEYS_BLOCKS    = (1U <<  9),   
04364     STAT_KEYS_ATIME     = (1U << 10),   
04365     STAT_KEYS_CTIME     = (1U << 11),   
04366     STAT_KEYS_MTIME     = (1U << 12),   
04367 #ifdef  NOTYET
04368     STAT_KEYS_FLAGS     = (1U << 13),   
04369 #endif
04370     STAT_KEYS_SLINK     = (1U << 14),   
04371     STAT_KEYS_DIGEST    = (1U << 15),   
04372 #ifdef  NOTYET
04373     STAT_KEYS_FCONTEXT  = (1U << 16),   
04374 #endif
04375     STAT_KEYS_UNAME     = (1U << 17),   
04376     STAT_KEYS_GNAME     = (1U << 18),   
04377 };
04378 
04379 /*@unchecked@*/ /*@observer@*/
04380 static KEY keyStat[] = {
04381     { "adler32",        STAT_KEYS_DIGEST },
04382     { "atime",          STAT_KEYS_ATIME },
04383     { "ctime",          STAT_KEYS_CTIME },
04384     { "blksize",        STAT_KEYS_BLKSIZE },
04385     { "blocks",         STAT_KEYS_BLOCKS },
04386     { "crc32",          STAT_KEYS_DIGEST },
04387     { "crc64",          STAT_KEYS_DIGEST },
04388     { "dev",            STAT_KEYS_DEV },
04389 #ifdef  NOTYET
04390     { "digest",         STAT_KEYS_DIGEST },
04391     { "fcontext",       STAT_KEYS_FCONTEXT },
04392     { "flags",          STAT_KEYS_FLAGS },
04393 #endif
04394     { "gid",            STAT_KEYS_GID },
04395     { "gname",          STAT_KEYS_GNAME },
04396     { "haval160",       STAT_KEYS_DIGEST },
04397     { "ino",            STAT_KEYS_INO },
04398     { "jlu32",          STAT_KEYS_DIGEST },
04399     { "link",           STAT_KEYS_SLINK },
04400     { "md2",            STAT_KEYS_DIGEST },
04401     { "md4",            STAT_KEYS_DIGEST },
04402     { "md5",            STAT_KEYS_DIGEST },
04403     { "mode",           STAT_KEYS_MODE },
04404     { "mtime",          STAT_KEYS_MTIME },
04405     { "nlink",          STAT_KEYS_NLINK },
04406     { "rdev",           STAT_KEYS_RDEV },
04407     { "rmd128",         STAT_KEYS_DIGEST },
04408     { "rmd160",         STAT_KEYS_DIGEST },
04409     { "rmd256",         STAT_KEYS_DIGEST },
04410     { "rmd320",         STAT_KEYS_DIGEST },
04411     { "salsa10",        STAT_KEYS_DIGEST },
04412     { "salsa20",        STAT_KEYS_DIGEST },
04413     { "sha1",           STAT_KEYS_DIGEST },
04414     { "sha224",         STAT_KEYS_DIGEST },
04415     { "sha256",         STAT_KEYS_DIGEST },
04416     { "sha384",         STAT_KEYS_DIGEST },
04417     { "sha512",         STAT_KEYS_DIGEST },
04418     { "size",           STAT_KEYS_SIZE },
04419     { "tiger192",       STAT_KEYS_DIGEST },
04420     { "uid",            STAT_KEYS_UID },
04421     { "uname",          STAT_KEYS_UNAME },
04422 };
04423 /*@unchecked@*/
04424 static size_t nkeyStat = sizeof(keyStat) / sizeof(keyStat[0]);
04425 
04429 enum keyUuids_e {
04430     UUID_KEYS_NONE      = (0U <<  0),
04431     UUID_KEYS_V1        = (1U <<  0),
04432     UUID_KEYS_V3        = (3U <<  0),
04433     UUID_KEYS_V4        = (4U <<  0),
04434     UUID_KEYS_V5        = (5U <<  0),
04435 #ifdef  NOTYET
04436     UUID_KEYS_STRING    = (0U <<  4),
04437     UUID_KEYS_SIV       = (1U <<  4),
04438     UUID_KEYS_BINARY    = (2U <<  4),
04439     UUID_KEYS_TEXT      = (3U <<  4),
04440 #endif
04441 };
04442 
04443 /*@unchecked@*/ /*@observer@*/
04444 static KEY keyUuids[] = {
04445 #ifdef  NOTYET
04446     { "binary",         UUID_KEYS_BINARY },
04447     { "siv",            UUID_KEYS_SIV },
04448     { "string",         UUID_KEYS_STRING },
04449     { "text",           UUID_KEYS_TEXT },
04450 #endif
04451     { "v1",             UUID_KEYS_V1 },
04452     { "v3",             UUID_KEYS_V3 },
04453     { "v4",             UUID_KEYS_V4 },
04454     { "v5",             UUID_KEYS_V5 },
04455 };
04456 /*@unchecked@*/
04457 static size_t nkeyUuids = sizeof(keyUuids) / sizeof(keyUuids[0]);
04458 
04461 static int
04462 keyCmp(const void * a, const void * b)
04463         /*@*/
04464 {
04465     return strcmp(((KEY *)a)->name, ((KEY *)b)->name);
04466 }
04467 
04470 static rpmuint32_t
04471 keyValue(KEY * keys, size_t nkeys, /*@null@*/ const char *name)
04472         /*@*/
04473 {
04474     rpmuint32_t keyval = 0;
04475 
04476     if (name && * name) {
04477         KEY needle = { .name = name, .value = 0 };
04478         KEY *k = (KEY *)bsearch(&needle, keys, nkeys, sizeof(*keys), keyCmp);
04479         if (k)
04480             keyval = k->value;
04481     }
04482     return keyval;
04483 }
04484 
04491 static /*@only@*/ char * digestFormat(HE_t he, /*@null@*/ const char ** av)
04492         /*@*/
04493 {
04494     int ix = (he->ix > 0 ? he->ix : 0);
04495     char * val = NULL;
04496     size_t ns;
04497 
04498 assert(ix == 0);
04499     switch(he->t) {
04500     default:
04501         val = xstrdup(_("(invalid type :digest)"));
04502         goto exit;
04503         /*@notreached@*/ break;
04504     case RPM_UINT64_TYPE:
04505         ns = sizeof(he->p.ui64p[0]);
04506         break;
04507     case RPM_STRING_TYPE:
04508         ns = strlen(he->p.str);
04509         break;
04510     case RPM_BIN_TYPE:
04511         ns = he->c;
04512         break;
04513     }
04514 
04515 assert(he->p.ptr != NULL);
04516     {   rpmuint32_t keyval = keyValue(keyDigests, nkeyDigests, (av ? av[0] : NULL));
04517         rpmuint32_t algo = (keyval ? keyval : PGPHASHALGO_SHA1);
04518         DIGEST_CTX ctx = rpmDigestInit(algo, 0);
04519         int xx = rpmDigestUpdate(ctx, he->p.ptr, ns);
04520         xx = rpmDigestFinal(ctx, &val, NULL, 1);
04521     }
04522 
04523 exit:
04524     return val;
04525 }
04526 
04533 static /*@only@*/ char * statFormat(HE_t he, /*@null@*/ const char ** av)
04534         /*@globals rpmGlobalMacroContext, h_errno, fileSystem, internalState @*/
04535         /*@modifies rpmGlobalMacroContext, fileSystem, internalState @*/
04536 {
04537 /*@-nullassign@*/
04538     /*@unchecked@*/ /*@observer@*/
04539     static const char *avdefault[] = { "mode", NULL };
04540 /*@=nullassign@*/
04541     const char * fn = NULL;
04542     struct stat sb, *st = &sb;
04543     int ix = (he->ix > 0 ? he->ix : 0);
04544     char * val = NULL;
04545     int xx;
04546     int i;
04547 
04548     memset(st, 0, sizeof(*st));
04549 assert(ix == 0);
04550     switch(he->t) {
04551     case RPM_BIN_TYPE:
04552         /* XXX limit to RPMTAG_PACKAGESTAT ... */
04553         if (he->tag == RPMTAG_PACKAGESTAT)
04554         if ((size_t)he->c == sizeof(*st)) {
04555             st = (struct stat *)he->p.ptr;
04556             break;
04557         }
04558         /*@fallthrough @*/
04559     default:
04560         val = xstrdup(_("(invalid type :stat)"));
04561         goto exit;
04562         /*@notreached@*/ break;
04563     case RPM_STRING_TYPE:
04564         fn = he->p.str;
04565         if (Lstat(fn, st) == 0)
04566             break;
04567 /*@-ownedtrans@*/
04568         val = rpmExpand("(Lstat:", fn, ":", strerror(errno), ")", NULL);
04569 /*@=ownedtrans@*/
04570         goto exit;
04571         /*@notreached@*/ break;
04572     }
04573 
04574     if (!(av && av[0] && *av[0]))
04575         av = avdefault;
04576     for (i = 0; av[i] != NULL; i++) {
04577         char b[BUFSIZ];
04578         size_t nb = sizeof(b);
04579         char * nval;
04580         rpmuint32_t keyval = keyValue(keyStat, nkeyStat, av[i]);
04581 
04582         nval = NULL;
04583         b[0] = '\0';
04584         switch (keyval) {
04585         default:
04586             /*@switchbreak@*/ break;
04587         case STAT_KEYS_NONE:
04588             /*@switchbreak@*/ break;
04589         case STAT_KEYS_DEV:
04590             xx = snprintf(b, nb, "0x%lx", (unsigned long)st->st_dev);
04591             /*@switchbreak@*/ break;
04592         case STAT_KEYS_INO:
04593             xx = snprintf(b, nb, "0x%lx", (unsigned long)st->st_ino);
04594             /*@switchbreak@*/ break;
04595         case STAT_KEYS_MODE:
04596             xx = snprintf(b, nb, "%06o", (unsigned)st->st_mode);
04597             /*@switchbreak@*/ break;
04598         case STAT_KEYS_NLINK:
04599             xx = snprintf(b, nb, "0x%ld", (unsigned long)st->st_nlink);
04600             /*@switchbreak@*/ break;
04601         case STAT_KEYS_UID:
04602             xx = snprintf(b, nb, "%ld", (unsigned long)st->st_uid);
04603             /*@switchbreak@*/ break;
04604         case STAT_KEYS_GID:
04605             xx = snprintf(b, nb, "%ld", (unsigned long)st->st_gid);
04606             /*@switchbreak@*/ break;
04607         case STAT_KEYS_RDEV:
04608             xx = snprintf(b, nb, "0x%lx", (unsigned long)st->st_rdev);
04609             /*@switchbreak@*/ break;
04610         case STAT_KEYS_SIZE:
04611             xx = snprintf(b, nb, "%ld", (unsigned long)st->st_size);
04612             /*@switchbreak@*/ break;
04613         case STAT_KEYS_BLKSIZE:
04614             xx = snprintf(b, nb, "%ld", (unsigned long)st->st_blksize);
04615             /*@switchbreak@*/ break;
04616         case STAT_KEYS_BLOCKS:
04617             xx = snprintf(b, nb, "%ld", (unsigned long)st->st_blocks);
04618             /*@switchbreak@*/ break;
04619         case STAT_KEYS_ATIME:
04620 /*@i@*/     (void) stpcpy(b, ctime((time_t *)&st->st_atime));
04621             /*@switchbreak@*/ break;
04622         case STAT_KEYS_CTIME:
04623 /*@i@*/     (void) stpcpy(b, ctime((time_t *)&st->st_ctime));
04624             /*@switchbreak@*/ break;
04625         case STAT_KEYS_MTIME:
04626 /*@i@*/     (void) stpcpy(b, ctime((time_t *)&st->st_mtime));
04627             /*@switchbreak@*/ break;
04628 #ifdef  NOTYET
04629         case STAT_KEYS_FLAGS:
04630             /*@switchbreak@*/ break;
04631 #endif
04632         case STAT_KEYS_SLINK:
04633             if (fn != NULL && S_ISLNK(st->st_mode)) {
04634                 ssize_t size = Readlink(fn, b, nb);
04635                 if (size == -1) {
04636                     nval = rpmExpand("(Readlink:", fn, ":", strerror(errno), ")", NULL);
04637                     (void) stpcpy(b, nval);
04638                     nval = _free(nval);
04639                 } else
04640                     b[size] = '\0';
04641             }
04642             /*@switchbreak@*/ break;
04643         case STAT_KEYS_DIGEST:
04644             if (fn != NULL && S_ISREG(st->st_mode)) {
04645                 rpmuint32_t digval = keyValue(keyDigests, nkeyDigests, av[i]);
04646                 rpmuint32_t algo = (digval ? digval : PGPHASHALGO_SHA1);
04647                 FD_t fd = Fopen(fn, "r%{?_rpmgio}");
04648                 if (fd == NULL || Ferror(fd)) {
04649                     nval = rpmExpand("(Fopen:", fn, ":", Fstrerror(fd), ")", NULL);
04650                 } else {
04651                     static int asAscii = 1;
04652                     char buffer[16 * 1024];
04653                     fdInitDigest(fd, algo, 0);
04654                     while (Fread(buffer, sizeof(buffer[0]), sizeof(buffer), fd) > 0)
04655                         {};
04656                     if (Ferror(fd))
04657                         nval = rpmExpand("(Fread:", fn, ":", Fstrerror(fd), ")", NULL);
04658                     else
04659                         fdFiniDigest(fd, algo, &nval, NULL, asAscii);
04660             }
04661                 if (nval) {
04662                     (void) stpcpy(b, nval);
04663                     nval = _free(nval);
04664                 }
04665                 if (fd != NULL)
04666                     xx = Fclose(fd);
04667             }
04668             /*@switchbreak@*/ break;
04669         case STAT_KEYS_UNAME:
04670         {   const char * uname = uidToUname(st->st_uid);
04671             if (uname != NULL)
04672                 (void) stpcpy(b, uname);
04673             else
04674                 xx = snprintf(b, nb, "%u", (unsigned)st->st_uid);
04675         }   /*@switchbreak@*/ break;
04676         case STAT_KEYS_GNAME:
04677         {   const char * gname = gidToGname(st->st_gid);
04678             if (gname != NULL)
04679                 (void) stpcpy(b, gname);
04680             else
04681                 xx = snprintf(b, nb, "%u", (unsigned)st->st_gid);
04682         }   /*@switchbreak@*/ break;
04683         }
04684         if (b[0] == '\0')
04685             continue;
04686         b[nb-1] = '\0';
04687 
04688         if (val == NULL)
04689             val = xstrdup(b);
04690         else {
04691             nval = rpmExpand(val, " | ", b, NULL);
04692             val = _free(val);
04693             val = nval;
04694         }
04695     }
04696 
04697 exit:
04698     return val;
04699 }
04700 
04707 static /*@only@*/ char * uuidFormat(HE_t he, /*@null@*/ const char ** av)
04708         /*@globals rpmGlobalMacroContext, h_errno, internalState @*/
04709         /*@modifies rpmGlobalMacroContext, internalState @*/
04710 {
04711     static const char hex[] = "0123456789abcdef";
04712     /* XXX use private tag container to avoid memory issues for now. */
04713     HE_t nhe = memset(alloca(sizeof(*nhe)), 0, sizeof(*nhe));
04714 /*@-nullassign@*/
04715 /*@unchecked@*/ /*@observer@*/
04716     static const char *avdefault[] = { "v5", NULL };
04717 /*@=nullassign@*/
04718     int ix = (he->ix > 0 ? he->ix : 0);
04719     char * val = NULL;
04720     struct timeval tv;
04721     char * t;
04722     char * te;
04723     uint32_t i;
04724 
04725 assert(ix == 0);
04726     val = xmalloc((128/4 + 4) + 1);
04727     *val = '\0';
04728 
04729     nhe->tag = he->tag;
04730     nhe->t = he->t;
04731     switch(he->t) {
04732     default:
04733         val = _free(val);
04734         val = xstrdup(_("(invalid type :uuid)"));
04735         goto exit;
04736         /*@notreached@*/ break;
04737     case RPM_UINT64_TYPE:
04738         /* XXX Limit to tag time stamps with UUIDv1 direct conversion. */
04739         switch (he->tag) {
04740         default:
04741             val = _free(val);
04742             val = xstrdup(_("(invalid tag :uuid)"));
04743             goto exit;
04744             break;
04745         case RPMTAG_INSTALLTIME:
04746         case RPMTAG_BUILDTIME:
04747         case RPMTAG_ORIGINTIME:
04748         case RPMTAG_INSTALLTID:
04749         case RPMTAG_REMOVETID:
04750         case RPMTAG_ORIGINTID:
04751 
04752             /* Convert tag time stamp to UUIDv1. */
04753             tv.tv_sec = (long) he->p.ui64p[0];
04754             tv.tv_usec = (long) (he->c > 1 ? he->p.ui64p[1] : 0);
04755             ix = tv2uuidv1(NULL, nhe, &tv);
04756 
04757             /* Convert UUIDv1 to display string. */
04758             te = val;
04759             for (i = 0; i < nhe->c; i++) {
04760                 *te++ = hex[ (int)((nhe->p.ui8p[i] >> 4) & 0x0f) ];
04761                 *te++ = hex[ (int)((nhe->p.ui8p[i]     ) & 0x0f) ];
04762                 if (i == 3 || i == 5 || i == 7 || i == 9)
04763                     *te++ = '-';
04764             }
04765             *te = '\0';
04766             goto exit;  /* XXX immediate exit for UUIDv1 */
04767             break;
04768         }
04769         break;
04770     case RPM_BIN_TYPE:
04771         /* XXX Limit to tag binary digests with djb formatting in UUIDv5. */
04772         switch (he->tag) {
04773         default:
04774             val = _free(val);
04775             val = xstrdup(_("(invalid tag :uuid)"));
04776             goto exit;
04777             break;
04778         case RPMTAG_PKGID:
04779         case RPMTAG_SOURCEPKGID:
04780             /* Convert RPMTAG_*PKGID from binary => hex. */
04781             t = te = xmalloc(2*he->c + 1);
04782             for (i = 0; i < he->c; i++) {
04783                 *te++ = hex[ (int)((he->p.ui8p[i] >> 4) & 0x0f) ];
04784                 *te++ = hex[ (int)((he->p.ui8p[i]     ) & 0x0f) ];
04785             }
04786             *te = '\0';
04787             nhe->t = RPM_STRING_TYPE;
04788             nhe->p.ptr = t;
04789             nhe->c = 1;
04790             break;
04791         }
04792         break;
04793     case RPM_STRING_TYPE:
04794         nhe->c = 1;
04795         nhe->p.ptr = xstrdup(he->p.str);
04796         break;
04797     }
04798 
04799     if (!(av && av[0] && *av[0]))
04800         av = avdefault;
04801 
04802     for (i = 0; av[i] != NULL; i++) {
04803         uint32_t keyval = keyValue(keyUuids, nkeyUuids, av[i]);
04804 
04805         switch (keyval) {
04806         default:
04807             /*@switchbreak@*/ break;
04808         case UUID_KEYS_V1:
04809         case UUID_KEYS_V3:
04810         case UUID_KEYS_V4:
04811         case UUID_KEYS_V5:
04812             ix = str2uuid(nhe, NULL, keyval, val);
04813             goto exit;  /* XXX exit after first found. */
04814             break;
04815         }
04816     }
04817 
04818 exit:
04819     nhe->p.ptr = _free(nhe->p.ptr);
04820     return val;
04821 }
04822 
04829 static /*@only@*/ char * rpnFormat(HE_t he, /*@null@*/ const char ** av)
04830         /*@*/
04831 {
04832     int ac = argvCount(av) + 1;
04833     int64_t * stack = memset(alloca(ac*sizeof(*stack)), 0, (ac*sizeof(*stack)));
04834     char * end;
04835     char * val = NULL;
04836     int ix = 0;
04837     int i;
04838 
04839     switch(he->t) {
04840     default:
04841         val = xstrdup(_("(invalid type :rpn)"));
04842         goto exit;
04843         /*@notreached@*/ break;
04844     case RPM_UINT64_TYPE:
04845         stack[ix] = he->p.ui64p[0];
04846         break;
04847     case RPM_STRING_TYPE:
04848         end = NULL;
04849 /*@-unrecog@*/  /* Add annotated prototype. */
04850         stack[ix] = strtoll(he->p.str, &end, 0);
04851 /*@=unrecog@*/
04852         if (end && *end != '\0') {
04853             val = xstrdup(_("(invalid string :rpn)"));
04854             goto exit;
04855         }
04856         break;
04857     }
04858 
04859     if (av != NULL)
04860     for (i = 0; av[i] != NULL; i++) {
04861         const char * arg = av[i];
04862         size_t len = strlen(arg);
04863         int c = (int) *arg;
04864 
04865         if (len == 0) {
04866             /* do nothing */
04867         } else if (len > 1) {
04868             if (!(xisdigit(c) || (c == (int)'-' && xisdigit((int) arg[1])))) {
04869                 val = xstrdup(_("(expected number :rpn)"));
04870                 goto exit;
04871             }
04872             if (++ix == ac) {
04873                 val = xstrdup(_("(stack overflow :rpn)"));
04874                 goto exit;
04875             }
04876             end = NULL;
04877             stack[ix] = strtoll(arg, &end, 0);
04878             if (end && *end != '\0') {
04879                 val = xstrdup(_("(invalid number :rpn)"));
04880                 goto exit;
04881             }
04882         } else {
04883             if (ix-- < 1) {
04884                 val = xstrdup(_("(stack underflow :rpn)"));
04885                 goto exit;
04886             }
04887             switch (c) {
04888             case '&':   stack[ix] &= stack[ix+1];       /*@switchbreak@*/ break;
04889             case '|':   stack[ix] |= stack[ix+1];       /*@switchbreak@*/ break;
04890             case '^':   stack[ix] ^= stack[ix+1];       /*@switchbreak@*/ break;
04891             case '+':   stack[ix] += stack[ix+1];       /*@switchbreak@*/ break;
04892             case '-':   stack[ix] -= stack[ix+1];       /*@switchbreak@*/ break;
04893             case '*':   stack[ix] *= stack[ix+1];       /*@switchbreak@*/ break;
04894             case '%':   
04895             case '/':   
04896                 if (stack[ix+1] == 0) {
04897                     val = xstrdup(_("(divide by zero :rpn)"));
04898                     goto exit;
04899                 }
04900                 if (c == (int)'%')
04901                     stack[ix] %= stack[ix+1];
04902                 else
04903                     stack[ix] /= stack[ix+1];
04904                 /*@switchbreak@*/ break;
04905             }
04906         }
04907     }
04908 
04909     {   HE_t nhe = memset(alloca(sizeof(*nhe)), 0, sizeof(*nhe));
04910         nhe->tag = he->tag;
04911         nhe->t = RPM_UINT64_TYPE;
04912         nhe->p.ui64p = (rpmuint64_t *)&stack[ix];
04913         nhe->c = 1;
04914         val = intFormat(nhe, NULL, NULL);
04915     }
04916 
04917 exit:
04918     return val;
04919 }
04920 
04927 static /*@only@*/ char * strsubFormat(HE_t he, /*@null@*/ const char ** av)
04928         /*@globals rpmGlobalMacroContext, h_errno, internalState @*/
04929         /*@modifies rpmGlobalMacroContext, internalState @*/
04930 {
04931     char * val = NULL;
04932     int ac = argvCount(av);
04933     miRE mires = NULL;
04934     int nmires = 0;
04935     int xx;
04936     int i;
04937 
04938     switch(he->t) {
04939     default:
04940         val = xstrdup(_("(invalid type :strsub)"));
04941         goto exit;
04942         /*@notreached@*/ break;
04943     case RPM_STRING_TYPE:
04944         if (ac < 2 || (ac % 2) != 0) {
04945             val = xstrdup(_("(invalid args :strsub)"));
04946             goto exit;
04947         }
04948         break;
04949     }
04950     if (av == NULL)
04951         goto noop;
04952 
04953     /* Create the mire pattern array. */
04954     for (i = 0; av[i] != NULL; i += 2)
04955         xx = mireAppend(RPMMIRE_REGEX, 0, av[i], NULL, &mires, &nmires);
04956 
04957     /* Find-and-replace first pattern that matches. */
04958     if (mires != NULL) {
04959         int noffsets = 3;
04960         int offsets[3];
04961         const char * s, * se;
04962         char * t, * te;
04963         char * nval;
04964         size_t slen;
04965         size_t nb;
04966 
04967         for (i = 0; i < nmires; i++) {
04968             miRE mire = mires + i;
04969 
04970             s = he->p.str;
04971             slen = strlen(s);
04972             if ((xx = mireRegexec(mire, s, slen)) < 0)
04973                 continue;
04974             xx = mireSetEOptions(mire, offsets, noffsets);
04975 
04976             /* Replace the string(s). This is just s/find/replace/g */
04977             val = xstrdup("");
04978             while (*s != '\0') {
04979                 nb = strlen(s);
04980                 if ((se = strchr(s, '\n')) == NULL)
04981                     se = s + nb;
04982                 else
04983                     se++;
04984 
04985                 offsets[0] = offsets[1] = -1;
04986                 xx = mireRegexec(mire, s, nb);
04987 
04988                 nb = 1;
04989                 /* On match, copy lead-in and match string. */
04990                 if (xx == 0)
04991                     nb += offsets[0] + strlen(av[2*i+1]);
04992                 /* Copy up to EOL on nomatch or insertion. */
04993                 if (xx != 0 || offsets[1] == offsets[0])
04994                     nb += (se - (s + offsets[1]));
04995 
04996                 te = t = xmalloc(nb);
04997 
04998                 /* On match, copy lead-in and match string. */
04999                 if (xx == 0) {
05000                     te = stpcpy( stpncpy(te, s, offsets[0]), av[2*i+1]);
05001                     s += offsets[1];
05002                 }
05003                 /* Copy up to EOL on nomatch or insertion. */
05004                 if (xx != 0 || offsets[1] == offsets[0]) {
05005                     s += offsets[1];
05006                     te = stpncpy(te, s, (se - s));
05007                     s = se;
05008                 }
05009                 *te = '\0';
05010 
05011                 nval = rpmExpand(val, t, NULL);
05012                 val = _free(val);
05013                 val = nval;
05014                 t = _free(t);
05015             }
05016         }
05017         mires = mireFreeAll(mires, nmires);
05018     }
05019 
05020 noop:
05021     if (val == NULL)
05022         val = xstrdup(he->p.str);
05023 exit:
05024     return val;
05025 }
05026 
05027 static struct headerSprintfExtension_s _headerCompoundFormats[] = {
05028     { HEADER_EXT_TAG, "RPMTAG_BUILDTIMEUUID",
05029         { .tagFunction = buildtime_uuidTag } },
05030     { HEADER_EXT_TAG, "RPMTAG_CHANGELOGNAME",
05031         { .tagFunction = changelognameTag } },
05032     { HEADER_EXT_TAG, "RPMTAG_CHANGELOGTEXT",
05033         { .tagFunction = changelogtextTag } },
05034     { HEADER_EXT_TAG, "RPMTAG_DESCRIPTION",
05035         { .tagFunction = descriptionTag } },
05036     { HEADER_EXT_TAG, "RPMTAG_GROUP",
05037         { .tagFunction = groupTag } },
05038     { HEADER_EXT_TAG, "RPMTAG_HDRUUID",
05039         { .tagFunction = hdruuidTag } },
05040     { HEADER_EXT_TAG, "RPMTAG_INSTALLPREFIX",
05041         { .tagFunction = instprefixTag } },
05042     { HEADER_EXT_TAG, "RPMTAG_INSTALLTIDUUID",
05043         { .tagFunction = installtid_uuidTag } },
05044     { HEADER_EXT_TAG, "RPMTAG_INSTALLTIMEUUID",
05045         { .tagFunction = installtime_uuidTag } },
05046     { HEADER_EXT_TAG, "RPMTAG_ORIGINTIDUUID",
05047         { .tagFunction = origintid_uuidTag } },
05048     { HEADER_EXT_TAG, "RPMTAG_ORIGINTIMEUUID",
05049         { .tagFunction = origintime_uuidTag } },
05050     { HEADER_EXT_TAG, "RPMTAG_PKGUUID",
05051         { .tagFunction = pkguuidTag } },
05052     { HEADER_EXT_TAG, "RPMTAG_REMOVETIDUUID",
05053         { .tagFunction = removetid_uuidTag } },
05054     { HEADER_EXT_TAG, "RPMTAG_SOURCEPKGUUID",
05055         { .tagFunction = sourcepkguuidTag } },
05056     { HEADER_EXT_TAG, "RPMTAG_SUMMARY",
05057         { .tagFunction = summaryTag } },
05058     { HEADER_EXT_TAG, "RPMTAG_TRIGGERCONDS",
05059         { .tagFunction = triggercondsTag } },
05060     { HEADER_EXT_TAG, "RPMTAG_TRIGGERTYPE",
05061         { .tagFunction = triggertypeTag } },
05062     { HEADER_EXT_TAG, "RPMTAG_DBINSTANCE",
05063         { .tagFunction = dbinstanceTag } },
05064     { HEADER_EXT_TAG, "RPMTAG_HEADERSTARTOFF",
05065         { .tagFunction = headerstartoffTag } },
05066     { HEADER_EXT_TAG, "RPMTAG_HEADERENDOFF",
05067         { .tagFunction = headerendoffTag } },
05068     { HEADER_EXT_TAG, "RPMTAG_PACKAGEBASEURL",
05069         { .tagFunction = pkgbaseurlTag } },
05070     { HEADER_EXT_TAG, "RPMTAG_PACKAGEDIGEST",
05071         { .tagFunction = pkgdigestTag } },
05072     { HEADER_EXT_TAG, "RPMTAG_PACKAGEORIGIN",
05073         { .tagFunction = pkgoriginTag } },
05074     { HEADER_EXT_TAG, "RPMTAG_PACKAGESIZE",
05075         { .tagFunction = pkgsizeTag } },
05076     { HEADER_EXT_TAG, "RPMTAG_PACKAGETIME",
05077         { .tagFunction = pkgmtimeTag } },
05078     { HEADER_EXT_TAG, "RPMTAG_NVRA",
05079         { .tagFunction = nvraTag } },
05080     { HEADER_EXT_TAG, "RPMTAG_FILENAMES",
05081         { .tagFunction = filenamesTag } },
05082     { HEADER_EXT_TAG, "RPMTAG_FILEPATHS",
05083         { .tagFunction = filepathsTag } },
05084     { HEADER_EXT_TAG, "RPMTAG_ORIGPATHS",
05085         { .tagFunction = origpathsTag } },
05086     { HEADER_EXT_TAG, "RPMTAG_FILESTAT",
05087         { .tagFunction = filestatTag } },
05088     { HEADER_EXT_TAG, "RPMTAG_PROVIDEXMLENTRY",
05089         { .tagFunction = PxmlTag } },
05090     { HEADER_EXT_TAG, "RPMTAG_REQUIREXMLENTRY",
05091         { .tagFunction = RxmlTag } },
05092     { HEADER_EXT_TAG, "RPMTAG_CONFLICTXMLENTRY",
05093         { .tagFunction = CxmlTag } },
05094     { HEADER_EXT_TAG, "RPMTAG_OBSOLETEXMLENTRY",
05095         { .tagFunction = OxmlTag } },
05096     { HEADER_EXT_TAG, "RPMTAG_FILESXMLENTRY1",
05097         { .tagFunction = F1xmlTag } },
05098     { HEADER_EXT_TAG, "RPMTAG_FILESXMLENTRY2",
05099         { .tagFunction = F2xmlTag } },
05100     { HEADER_EXT_TAG, "RPMTAG_PROVIDEYAMLENTRY",
05101         { .tagFunction = PyamlTag } },
05102     { HEADER_EXT_TAG, "RPMTAG_REQUIREYAMLENTRY",
05103         { .tagFunction = RyamlTag } },
05104     { HEADER_EXT_TAG, "RPMTAG_CONFLICTYAMLENTRY",
05105         { .tagFunction = CyamlTag } },
05106     { HEADER_EXT_TAG, "RPMTAG_OBSOLETEYAMLENTRY",
05107         { .tagFunction = OyamlTag } },
05108     { HEADER_EXT_TAG, "RPMTAG_FILESYAMLENTRY1",
05109         { .tagFunction = F1yamlTag } },
05110     { HEADER_EXT_TAG, "RPMTAG_FILESYAMLENTRY2",
05111         { .tagFunction = F2yamlTag } },
05112     { HEADER_EXT_TAG, "RPMTAG_PROVIDESQLENTRY",
05113         { .tagFunction = PsqlTag } },
05114     { HEADER_EXT_TAG, "RPMTAG_REQUIRESQLENTRY",
05115         { .tagFunction = RsqlTag } },
05116     { HEADER_EXT_TAG, "RPMTAG_CONFLICTSQLENTRY",
05117         { .tagFunction = CsqlTag } },
05118     { HEADER_EXT_TAG, "RPMTAG_OBSOLETESQLENTRY",
05119         { .tagFunction = OsqlTag } },
05120     { HEADER_EXT_TAG, "RPMTAG_FILESSQLENTRY1",
05121         { .tagFunction = F1sqlTag } },
05122     { HEADER_EXT_TAG, "RPMTAG_FILESSQLENTRY2",
05123         { .tagFunction = F2sqlTag } },
05124     { HEADER_EXT_TAG, "RPMTAG_DEBCONFLICTS",
05125         { .tagFunction = debconflictsTag } },
05126     { HEADER_EXT_TAG, "RPMTAG_DEBDEPENDS",
05127         { .tagFunction = debdependsTag } },
05128     { HEADER_EXT_TAG, "RPMTAG_DEBMD5SUMS",
05129         { .tagFunction = debmd5sumsTag } },
05130     { HEADER_EXT_TAG, "RPMTAG_DEBOBSOLETES",
05131         { .tagFunction = debobsoletesTag } },
05132     { HEADER_EXT_TAG, "RPMTAG_DEBPROVIDES",
05133         { .tagFunction = debprovidesTag } },
05134     { HEADER_EXT_TAG, "RPMTAG_NEEDSWHAT",
05135         { .tagFunction = needswhatTag } },
05136     { HEADER_EXT_TAG, "RPMTAG_WHATNEEDS",
05137         { .tagFunction = whatneedsTag } },
05138     { HEADER_EXT_FORMAT, "armor",
05139         { .fmtFunction = armorFormat } },
05140     { HEADER_EXT_FORMAT, "base64",
05141         { .fmtFunction = base64Format } },
05142     { HEADER_EXT_FORMAT, "bncdata",
05143         { .fmtFunction = bncdataFormat } },
05144     { HEADER_EXT_FORMAT, "cdata",
05145         { .fmtFunction = cdataFormat } },
05146     { HEADER_EXT_FORMAT, "depflags",
05147         { .fmtFunction = depflagsFormat } },
05148     { HEADER_EXT_FORMAT, "deptype",
05149         { .fmtFunction = deptypeFormat } },
05150     { HEADER_EXT_FORMAT, "digest",
05151         { .fmtFunction = digestFormat } },
05152     { HEADER_EXT_FORMAT, "fflags",
05153         { .fmtFunction = fflagsFormat } },
05154     { HEADER_EXT_FORMAT, "iconv",
05155         { .fmtFunction = iconvFormat } },
05156     { HEADER_EXT_FORMAT, "json",
05157         { .fmtFunction = jsonFormat } },
05158 #ifndef DYING   /* XXX is :json gud enuf? there are side effects ... */
05159     { HEADER_EXT_FORMAT, "jsonescape",
05160         { .fmtFunction = jsonescapeFormat } },
05161 #endif
05162     { HEADER_EXT_FORMAT, "perms",
05163         { .fmtFunction = permsFormat } },
05164     { HEADER_EXT_FORMAT, "permissions", 
05165         { .fmtFunction = permsFormat } },
05166     { HEADER_EXT_FORMAT, "pgpsig",
05167         { .fmtFunction = pgpsigFormat } },
05168     { HEADER_EXT_FORMAT, "rpn",
05169         { .fmtFunction = rpnFormat } },
05170     { HEADER_EXT_FORMAT, "sqlescape",
05171         { .fmtFunction = sqlescapeFormat } },
05172     { HEADER_EXT_FORMAT, "stat",
05173         { .fmtFunction = statFormat } },
05174     { HEADER_EXT_FORMAT, "strsub",
05175         { .fmtFunction = strsubFormat } },
05176     { HEADER_EXT_FORMAT, "triggertype", 
05177         { .fmtFunction = triggertypeFormat } },
05178     { HEADER_EXT_FORMAT, "utf8",
05179         { .fmtFunction = iconvFormat } },
05180     { HEADER_EXT_FORMAT, "uuid",
05181         { .fmtFunction = uuidFormat } },
05182     { HEADER_EXT_FORMAT, "xml",
05183         { .fmtFunction = xmlFormat } },
05184     { HEADER_EXT_FORMAT, "yaml",
05185         { .fmtFunction = yamlFormat } },
05186     { HEADER_EXT_MORE, NULL,            { (void *) &headerDefaultFormats } }
05187 } ;
05188 
05189 headerSprintfExtension headerCompoundFormats = &_headerCompoundFormats[0];
05190 
05191 /*====================================================================*/
05192 
05193 void rpmDisplayQueryTags(FILE * fp, headerTagTableEntry _rpmTagTable, headerSprintfExtension _rpmHeaderFormats)
05194 {
05195     const struct headerTagTableEntry_s * t;
05196     headerSprintfExtension exts;
05197     headerSprintfExtension ext;
05198     int extNum;
05199 
05200     if (fp == NULL)
05201         fp = stdout;
05202     if (_rpmTagTable == NULL)
05203         _rpmTagTable = rpmTagTable;
05204 
05205     /* XXX this should use rpmHeaderFormats, but there are linkage problems. */
05206     if (_rpmHeaderFormats == NULL)
05207         _rpmHeaderFormats = headerCompoundFormats;
05208 
05209     for (t = _rpmTagTable; t && t->name; t++) {
05210         /*@observer@*/
05211         static const char * tagtypes[] = {
05212                 "", "char", "uint8", "uint16", "uint32", "uint64",
05213                 "string", "octets", "argv", "i18nstring",
05214         };
05215         rpmuint32_t ttype;
05216 
05217         if (rpmIsVerbose()) {
05218             fprintf(fp, "%-20s %6d", t->name + 7, t->val);
05219             ttype = t->type & RPM_MASK_TYPE;
05220             if (ttype < RPM_MIN_TYPE || ttype > RPM_MAX_TYPE)
05221                 continue;
05222             if (t->type & RPM_OPENPGP_RETURN_TYPE)
05223                 fprintf(fp, " openpgp");
05224             if (t->type & RPM_X509_RETURN_TYPE)
05225                 fprintf(fp, " x509");
05226             if (t->type & RPM_ASN1_RETURN_TYPE)
05227                 fprintf(fp, " asn1");
05228             if (t->type & RPM_OPAQUE_RETURN_TYPE)
05229                 fprintf(fp, " opaque");
05230             fprintf(fp, " %s", tagtypes[ttype]);
05231             if (t->type & RPM_ARRAY_RETURN_TYPE)
05232                 fprintf(fp, " array");
05233             if (t->type & RPM_MAPPING_RETURN_TYPE)
05234                 fprintf(fp, " mapping");
05235             if (t->type & RPM_PROBE_RETURN_TYPE)
05236                 fprintf(fp, " probe");
05237             if (t->type & RPM_TREE_RETURN_TYPE)
05238                 fprintf(fp, " tree");
05239         } else
05240             fprintf(fp, "%s", t->name + 7);
05241         fprintf(fp, "\n");
05242     }
05243 
05244     exts = _rpmHeaderFormats;
05245     for (ext = exts, extNum = 0; ext != NULL && ext->type != HEADER_EXT_LAST;
05246         ext = (ext->type == HEADER_EXT_MORE ? *ext->u.more : ext+1), extNum++)
05247     {
05248         if (ext->name == NULL || ext->type != HEADER_EXT_TAG)
05249             continue;
05250 
05251         /* XXX don't print header tags twice. */
05252         if (tagValue(ext->name) > 0)
05253             continue;
05254         fprintf(fp, "%s\n", ext->name + 7);
05255     }
05256 }
05257 
05258 /*====================================================================*/
05259 
05260 #define PARSER_BEGIN    0
05261 #define PARSER_IN_ARRAY 1
05262 #define PARSER_IN_EXPR  2
05263 
05266 typedef /*@abstract@*/ struct sprintfTag_s * sprintfTag;
05267 
05270 struct sprintfTag_s {
05271     HE_s he;
05272 /*@null@*/
05273     headerTagFormatFunction * fmtfuncs;
05274 /*@null@*/
05275     headerTagTagFunction ext;   
05276     int extNum;
05277 /*@only@*/ /*@relnull@*/
05278     rpmTag * tagno;
05279     int justOne;
05280     int arrayCount;
05281 /*@kept@*/
05282     char * format;
05283 /*@only@*/ /*@relnull@*/
05284     ARGV_t av;
05285 /*@only@*/ /*@relnull@*/
05286     ARGV_t params;
05287     unsigned pad;
05288 };
05289 
05292 typedef /*@abstract@*/ struct sprintfToken_s * sprintfToken;
05293 
05296 struct sprintfToken_s {
05297     enum {
05298         PTOK_NONE       = 0,
05299         PTOK_TAG        = 1,
05300         PTOK_ARRAY      = 2,
05301         PTOK_STRING     = 3,
05302         PTOK_COND       = 4
05303     } type;
05304     union {
05305         struct sprintfTag_s tag;        
05306         struct {
05307         /*@only@*/
05308             sprintfToken format;
05309             size_t numTokens;
05310         } array;                        
05311         struct {
05312         /*@dependent@*/
05313             char * string;
05314             size_t len;
05315         } string;                       
05316         struct {
05317         /*@only@*/ /*@null@*/
05318             sprintfToken ifFormat;
05319             size_t numIfTokens;
05320         /*@only@*/ /*@null@*/
05321             sprintfToken elseFormat;
05322             size_t numElseTokens;
05323             struct sprintfTag_s tag;
05324         } cond;                         
05325     } u;
05326 };
05327 
05330 typedef /*@abstract@*/ struct headerSprintfArgs_s * headerSprintfArgs;
05331 
05334 struct headerSprintfArgs_s {
05335     Header h;
05336     char * fmt;
05337 /*@observer@*/ /*@temp@*/
05338     headerTagTableEntry tags;
05339 /*@observer@*/ /*@temp@*/
05340     headerSprintfExtension exts;
05341 /*@observer@*/ /*@null@*/
05342     const char * errmsg;
05343     HE_t ec;                    
05344     int nec;                    
05345     sprintfToken format;
05346 /*@relnull@*/
05347     HeaderIterator hi;
05348 /*@owned@*/
05349     char * val;
05350     size_t vallen;
05351     size_t alloced;
05352     size_t numTokens;
05353     size_t i;
05354 };
05355 
05356 /*@access sprintfTag @*/
05357 /*@access sprintfToken @*/
05358 /*@access headerSprintfArgs @*/
05359 
05362 static char escapedChar(const char ch)
05363         /*@*/
05364 {
05365 /*@-modfilesys@*/
05366 if (_hdrqf_debug)
05367 fprintf(stderr, "\t\t\\%c\n", ch);
05368 /*@=modfilesys@*/
05369     switch (ch) {
05370     case 'a':   return '\a';
05371     case 'b':   return '\b';
05372     case 'f':   return '\f';
05373     case 'n':   return '\n';
05374     case 'r':   return '\r';
05375     case 't':   return '\t';
05376     case 'v':   return '\v';
05377     default:    return ch;
05378     }
05379 }
05380 
05385 /*@relnull@*/
05386 static HE_t rpmheClean(/*@returned@*/ /*@null@*/ HE_t he)
05387         /*@modifies he @*/
05388 {
05389     if (he) {
05390         if (he->freeData && he->p.ptr != NULL)
05391             he->p.ptr = _free(he->p.ptr);
05392         memset(he, 0, sizeof(*he));
05393     }
05394     return he;
05395 }
05396 
05403 static /*@null@*/ sprintfToken
05404 freeFormat( /*@only@*/ /*@null@*/ sprintfToken format, size_t num)
05405         /*@modifies *format @*/
05406 {
05407     unsigned i;
05408 
05409     if (format == NULL) return NULL;
05410 
05411     for (i = 0; i < (unsigned) num; i++) {
05412         switch (format[i].type) {
05413         case PTOK_TAG:
05414             (void) rpmheClean(&format[i].u.tag.he);
05415             format[i].u.tag.tagno = _free(format[i].u.tag.tagno);
05416             format[i].u.tag.av = argvFree(format[i].u.tag.av);
05417             format[i].u.tag.params = argvFree(format[i].u.tag.params);
05418 /*@-type@*/
05419             format[i].u.tag.fmtfuncs = _free(format[i].u.tag.fmtfuncs);
05420 /*@=type@*/
05421             /*@switchbreak@*/ break;
05422         case PTOK_ARRAY:
05423             format[i].u.array.format =
05424                 freeFormat(format[i].u.array.format,
05425                         format[i].u.array.numTokens);
05426             /*@switchbreak@*/ break;
05427         case PTOK_COND:
05428             format[i].u.cond.ifFormat =
05429                 freeFormat(format[i].u.cond.ifFormat, 
05430                         format[i].u.cond.numIfTokens);
05431             format[i].u.cond.elseFormat =
05432                 freeFormat(format[i].u.cond.elseFormat, 
05433                         format[i].u.cond.numElseTokens);
05434             (void) rpmheClean(&format[i].u.cond.tag.he);
05435             format[i].u.cond.tag.tagno = _free(format[i].u.cond.tag.tagno);
05436             format[i].u.cond.tag.av = argvFree(format[i].u.cond.tag.av);
05437             format[i].u.cond.tag.params = argvFree(format[i].u.cond.tag.params);
05438 /*@-type@*/
05439             format[i].u.cond.tag.fmtfuncs = _free(format[i].u.cond.tag.fmtfuncs);
05440 /*@=type@*/
05441             /*@switchbreak@*/ break;
05442         case PTOK_NONE:
05443         case PTOK_STRING:
05444         default:
05445             /*@switchbreak@*/ break;
05446         }
05447     }
05448     format = _free(format);
05449     return NULL;
05450 }
05451 
05457 static headerSprintfArgs hsaInit(/*@returned@*/ headerSprintfArgs hsa)
05458         /*@globals fileSystem @*/
05459         /*@modifies hsa, fileSystem @*/
05460 {
05461     sprintfTag tag =
05462         (hsa->format->type == PTOK_TAG
05463             ? &hsa->format->u.tag :
05464         (hsa->format->type == PTOK_ARRAY
05465             ? &hsa->format->u.array.format->u.tag :
05466         NULL));
05467 
05468     if (hsa != NULL) {
05469         hsa->i = 0;
05470         if (tag != NULL && tag->tagno != NULL && tag->tagno[0] == (rpmTag)-2)
05471             hsa->hi = headerInit(hsa->h);
05472     }
05473 /*@-nullret@*/
05474     return hsa;
05475 /*@=nullret@*/
05476 }
05477 
05483 /*@null@*/
05484 static sprintfToken hsaNext(/*@returned@*/ headerSprintfArgs hsa)
05485         /*@globals internalState @*/
05486         /*@modifies hsa, internalState @*/
05487 {
05488     sprintfToken fmt = NULL;
05489     sprintfTag tag =
05490         (hsa->format->type == PTOK_TAG
05491             ? &hsa->format->u.tag :
05492         (hsa->format->type == PTOK_ARRAY
05493             ? &hsa->format->u.array.format->u.tag :
05494         NULL));
05495 
05496     if (hsa != NULL && hsa->i < hsa->numTokens) {
05497         fmt = hsa->format + hsa->i;
05498         if (hsa->hi == NULL) {
05499             hsa->i++;
05500         } else {
05501             HE_t he = rpmheClean(&tag->he);
05502             if (!headerNext(hsa->hi, he, 0))
05503             {
05504                 tag->tagno[0] = 0;
05505                 return NULL;
05506             }
05507             he->avail = 1;
05508             tag->tagno[0] = he->tag;
05509         }
05510     }
05511 
05512 /*@-dependenttrans -onlytrans@*/
05513     return fmt;
05514 /*@=dependenttrans =onlytrans@*/
05515 }
05516 
05522 static headerSprintfArgs hsaFini(/*@returned@*/ headerSprintfArgs hsa)
05523         /*@globals fileSystem @*/
05524         /*@modifies hsa, fileSystem @*/
05525 {
05526     if (hsa != NULL) {
05527         hsa->hi = headerFini(hsa->hi);
05528         hsa->i = 0;
05529     }
05530 /*@-nullret@*/
05531     return hsa;
05532 /*@=nullret@*/
05533 }
05534 
05541 /*@dependent@*/ /*@exposed@*/
05542 static char * hsaReserve(headerSprintfArgs hsa, size_t need)
05543         /*@modifies hsa */
05544 {
05545     if ((hsa->vallen + need) >= hsa->alloced) {
05546         if (hsa->alloced <= need)
05547             hsa->alloced += need;
05548         hsa->alloced <<= 1;
05549         hsa->val = xrealloc(hsa->val, hsa->alloced+1);  
05550     }
05551     return hsa->val + hsa->vallen;
05552 }
05553 
05561 /*@observer@*/ /*@null@*/
05562 static const char * myTagName(headerTagTableEntry tbl, rpmuint32_t val,
05563                 /*@null@*/ rpmuint32_t *typep)
05564         /*@modifies *typep @*/
05565 {
05566     static char name[128];      /* XXX Ick. */
05567     const char * s;
05568     char *t;
05569 
05570     /* XXX Use bsearch on the "normal" rpmTagTable lookup. */
05571     if (tbl == NULL || tbl == rpmTagTable) {
05572         s = tagName(val);
05573         if (s != NULL && typep != NULL)
05574             *typep = tagType(val);
05575         return s;
05576     }
05577 
05578     for (; tbl->name != NULL; tbl++) {
05579         if (tbl->val == val)
05580             break;
05581     }
05582     if ((s = tbl->name) == NULL)
05583         return NULL;
05584     s += sizeof("RPMTAG_") - 1;
05585     t = name;
05586     *t++ = *s++;
05587     while (*s != '\0')
05588         *t++ = (char)xtolower((int)*s++);
05589     *t = '\0';
05590     if (typep)
05591         *typep = tbl->type;
05592     return name;
05593 }
05594 
05601 static rpmuint32_t myTagValue(headerTagTableEntry tbl, const char * name)
05602         /*@*/
05603 {
05604     rpmuint32_t val = 0;
05605 
05606     /* XXX Use bsearch on the "normal" rpmTagTable lookup. */
05607     if (tbl == NULL || tbl == rpmTagTable)
05608         val = tagValue(name);
05609     else
05610     for (; tbl->name != NULL; tbl++) {
05611         if (xstrcasecmp(tbl->name, name))
05612             continue;
05613         val = tbl->val;
05614         break;
05615     }
05616     return val;
05617 }
05618 
05626 static int findTag(headerSprintfArgs hsa, sprintfToken token, const char * name)
05627         /*@modifies token @*/
05628 {
05629     headerSprintfExtension exts = hsa->exts;
05630     headerSprintfExtension ext;
05631     sprintfTag stag = (token->type == PTOK_COND
05632         ? &token->u.cond.tag : &token->u.tag);
05633     int extNum;
05634     rpmTag tagno = (rpmTag)-1;
05635 
05636     stag->fmtfuncs = NULL;
05637     stag->ext = NULL;
05638     stag->extNum = 0;
05639 
05640     if (!strcmp(name, "*")) {
05641         tagno = (rpmTag)-2;
05642         goto bingo;
05643     }
05644 
05645     if (strncmp("RPMTAG_", name, sizeof("RPMTAG_")-1)) {
05646         char * t = alloca(strlen(name) + sizeof("RPMTAG_"));
05647         (void) stpcpy( stpcpy(t, "RPMTAG_"), name);
05648         name = t;
05649     }
05650 
05651     /* Search extensions for specific tag override. */
05652     for (ext = exts, extNum = 0; ext != NULL && ext->type != HEADER_EXT_LAST;
05653         ext = (ext->type == HEADER_EXT_MORE ? *ext->u.more : ext+1), extNum++)
05654     {
05655         if (ext->name == NULL || ext->type != HEADER_EXT_TAG)
05656             continue;
05657         if (!xstrcasecmp(ext->name, name)) {
05658             stag->ext = ext->u.tagFunction;
05659             stag->extNum = extNum;
05660             tagno = tagValue(name);
05661             goto bingo;
05662         }
05663     }
05664 
05665     /* Search tag names. */
05666     tagno = myTagValue(hsa->tags, name);
05667     if (tagno != 0)
05668         goto bingo;
05669 
05670     return 1;
05671 
05672 bingo:
05673     stag->tagno = xcalloc(1, sizeof(*stag->tagno));
05674     stag->tagno[0] = tagno;
05675     /* Search extensions for specific format(s). */
05676     if (stag->av != NULL) {
05677         int i;
05678 /*@-type@*/
05679         stag->fmtfuncs = xcalloc(argvCount(stag->av) + 1, sizeof(*stag->fmtfuncs));
05680 /*@=type@*/
05681         for (i = 0; stag->av[i] != NULL; i++) {
05682             for (ext = exts; ext != NULL && ext->type != HEADER_EXT_LAST;
05683                  ext = (ext->type == HEADER_EXT_MORE ? *ext->u.more : ext+1))
05684             {
05685                 if (ext->name == NULL || ext->type != HEADER_EXT_FORMAT)
05686                     /*@innercontinue@*/ continue;
05687                 if (strcmp(ext->name, stag->av[i]+1))
05688                     /*@innercontinue@*/ continue;
05689                 stag->fmtfuncs[i] = ext->u.fmtFunction;
05690                 /*@innerbreak@*/ break;
05691             }
05692         }
05693     }
05694     return 0;
05695 }
05696 
05697 /* forward ref */
05706 static int parseExpression(headerSprintfArgs hsa, sprintfToken token,
05707                 char * str, /*@out@*/char ** endPtr)
05708         /*@modifies hsa, str, token, *endPtr @*/
05709         /*@requires maxSet(endPtr) >= 0 @*/;
05710 
05721 static int parseFormat(headerSprintfArgs hsa, char * str,
05722                 /*@out@*/ sprintfToken * formatPtr,
05723                 /*@out@*/ size_t * numTokensPtr,
05724                 /*@null@*/ /*@out@*/ char ** endPtr, int state)
05725         /*@modifies hsa, str, *formatPtr, *numTokensPtr, *endPtr @*/
05726         /*@requires maxSet(formatPtr) >= 0 /\ maxSet(numTokensPtr) >= 0
05727                 /\ maxSet(endPtr) >= 0 @*/
05728 {
05729 /*@observer@*/
05730 static const char *pstates[] = {
05731 "NORMAL", "ARRAY", "EXPR", "WTF?"
05732 };
05733     char * chptr, * start, * next, * dst;
05734     sprintfToken format;
05735     sprintfToken token;
05736     size_t numTokens;
05737     unsigned i;
05738     int done = 0;
05739     int xx;
05740 
05741 /*@-modfilesys@*/
05742 if (_hdrqf_debug)
05743 fprintf(stderr, "-->     parseFormat(%p, \"%.20s...\", %p, %p, %p, %s)\n", hsa, str, formatPtr, numTokensPtr, endPtr, pstates[(state & 0x3)]);
05744 /*@=modfilesys@*/
05745 
05746     /* upper limit on number of individual formats */
05747     numTokens = 0;
05748     if (str != NULL)
05749     for (chptr = str; *chptr != '\0'; chptr++)
05750         if (*chptr == '%' || *chptr == '[') numTokens++;
05751     numTokens = numTokens * 2 + 1;
05752 
05753     format = xcalloc(numTokens, sizeof(*format));
05754     if (endPtr) *endPtr = NULL;
05755 
05756 /*@-infloops@*/ /* LCL: can't detect (start, *start) termination */
05757     dst = start = str;
05758     numTokens = 0;
05759     token = NULL;
05760     if (start != NULL)
05761     while (*start != '\0') {
05762         switch (*start) {
05763         case '%':
05764             /* handle %% */
05765             if (*(start + 1) == '%') {
05766                 if (token == NULL || token->type != PTOK_STRING) {
05767                     token = format + numTokens++;
05768                     token->type = PTOK_STRING;
05769 /*@-temptrans -assignexpose@*/
05770                     dst = token->u.string.string = start;
05771 /*@=temptrans =assignexpose@*/
05772                 }
05773                 start++;
05774                 *dst++ = *start++;
05775                 /*@switchbreak@*/ break;
05776             } 
05777 
05778             token = format + numTokens++;
05779             *dst++ = '\0';
05780             start++;
05781 
05782             if (*start == '|') {
05783                 char * newEnd;
05784 
05785                 start++;
05786                 if (parseExpression(hsa, token, start, &newEnd))
05787                 {
05788                     format = freeFormat(format, numTokens);
05789                     return 1;
05790                 }
05791                 start = newEnd;
05792                 /*@switchbreak@*/ break;
05793             }
05794 
05795 /*@-assignexpose@*/
05796             token->u.tag.format = start;
05797 /*@=assignexpose@*/
05798             token->u.tag.pad = 0;
05799             token->u.tag.justOne = 0;
05800             token->u.tag.arrayCount = 0;
05801 
05802             chptr = start;
05803             while (*chptr && *chptr != '{' && *chptr != '%') chptr++;
05804             if (!*chptr || *chptr == '%') {
05805                 hsa->errmsg = _("missing { after %");
05806                 format = freeFormat(format, numTokens);
05807                 return 1;
05808             }
05809 
05810 /*@-modfilesys@*/
05811 if (_hdrqf_debug)
05812 fprintf(stderr, "\tchptr *%p = NUL\n", chptr);
05813 /*@=modfilesys@*/
05814             *chptr++ = '\0';
05815 
05816             while (start < chptr) {
05817                 if (xisdigit((int)*start)) {
05818                     i = strtoul(start, &start, 10);
05819                     token->u.tag.pad += i;
05820                     start = chptr;
05821                     /*@innerbreak@*/ break;
05822                 } else {
05823                     start++;
05824                 }
05825             }
05826 
05827             if (*start == '=') {
05828                 token->u.tag.justOne = 1;
05829                 start++;
05830             } else if (*start == '#') {
05831                 token->u.tag.justOne = 1;
05832                 token->u.tag.arrayCount = 1;
05833                 start++;
05834             }
05835 
05836             next = start;
05837             while (*next && *next != '}') next++;
05838             if (!*next) {
05839                 hsa->errmsg = _("missing } after %{");
05840                 format = freeFormat(format, numTokens);
05841                 return 1;
05842             }
05843 /*@-modfilesys@*/
05844 if (_hdrqf_debug)
05845 fprintf(stderr, "\tnext *%p = NUL\n", next);
05846 /*@=modfilesys@*/
05847             *next++ = '\0';
05848 
05849 #define isSEP(_c)       ((_c) == ':' || (_c) == '|')
05850             chptr = start;
05851             while (!(*chptr == '\0' || isSEP(*chptr))) chptr++;
05852             /* Split ":bing|bang:boom" --qf pipeline formatters (if any) */
05853             while (isSEP(*chptr)) {
05854                 if (chptr[1] == '\0' || isSEP(chptr[1])) {
05855                     hsa->errmsg = _("empty tag format");
05856                     format = freeFormat(format, numTokens);
05857                     return 1;
05858                 }
05859                 /* Parse the formatter parameter list. */
05860                 {   char * te = chptr + 1;
05861                     char * t = strchr(te, '(');
05862                     char c;
05863 
05864                     while (!(*te == '\0' || isSEP(*te))) {
05865 #ifdef  NOTYET  /* XXX some means of escaping is needed */
05866                         if (te[0] == '\\' && te[1] != '\0') te++;
05867 #endif
05868                         te++;
05869                     }
05870                     c = *te; *te = '\0';
05871                     /* Parse (a,b,c) parameter list. */
05872                     if (t != NULL) {
05873                         *t++ = '\0';
05874                         if (te <= t || te[-1] != ')') {
05875                             hsa->errmsg = _("malformed parameter list");
05876                             format = freeFormat(format, numTokens);
05877                             return 1;
05878                         }
05879                         te[-1] = '\0';
05880                         xx = argvAdd(&token->u.tag.params, t);
05881                     } else
05882                         xx = argvAdd(&token->u.tag.params, "");
05883 /*@-modfilesys@*/
05884 if (_hdrqf_debug)
05885 fprintf(stderr, "\tformat \"%s\" params \"%s\"\n", chptr, (t ? t : ""));
05886 /*@=modfilesys@*/
05887                     xx = argvAdd(&token->u.tag.av, chptr);
05888                     *te = c;
05889                     *chptr = '\0';
05890                     chptr = te;
05891                 }
05892             }
05893 #undef  isSEP
05894             
05895             if (*start == '\0') {
05896                 hsa->errmsg = _("empty tag name");
05897                 format = freeFormat(format, numTokens);
05898                 return 1;
05899             }
05900 
05901             i = 0;
05902             token->type = PTOK_TAG;
05903 
05904             if (findTag(hsa, token, start)) {
05905                 hsa->errmsg = _("unknown tag");
05906                 format = freeFormat(format, numTokens);
05907                 return 1;
05908             }
05909 
05910             dst = start = next;
05911 /*@-modfilesys@*/
05912 if (_hdrqf_debug)
05913 fprintf(stderr, "\tdst = start = next %p\n", dst);
05914 /*@=modfilesys@*/
05915             /*@switchbreak@*/ break;
05916 
05917         case '[':
05918 /*@-modfilesys@*/
05919 if (_hdrqf_debug)
05920 fprintf(stderr, "\t%s => %s *%p = NUL\n", pstates[(state & 0x3)], pstates[PARSER_IN_ARRAY], start);
05921 /*@=modfilesys@*/
05922             *start++ = '\0';
05923             token = format + numTokens++;
05924 
05925             if (parseFormat(hsa, start,
05926                             &token->u.array.format,
05927                             &token->u.array.numTokens,
05928                             &start, PARSER_IN_ARRAY))
05929             {
05930                 format = freeFormat(format, numTokens);
05931                 return 1;
05932             }
05933 
05934             if (!start) {
05935                 hsa->errmsg = _("] expected at end of array");
05936                 format = freeFormat(format, numTokens);
05937                 return 1;
05938             }
05939 
05940             dst = start;
05941 /*@-modfilesys@*/
05942 if (_hdrqf_debug)
05943 fprintf(stderr, "\tdst = start %p\n", dst);
05944 /*@=modfilesys@*/
05945 
05946             token->type = PTOK_ARRAY;
05947 
05948             /*@switchbreak@*/ break;
05949 
05950         case ']':
05951             if (state != PARSER_IN_ARRAY) {
05952                 hsa->errmsg = _("unexpected ]");
05953                 format = freeFormat(format, numTokens);
05954                 return 1;
05955             }
05956             *start++ = '\0';
05957 /*@-modfilesys@*/
05958 if (_hdrqf_debug)
05959 fprintf(stderr, "\t<= %s %p[-1] = NUL\n", pstates[(state & 0x3)], start);
05960 /*@=modfilesys@*/
05961             if (endPtr) *endPtr = start;
05962             done = 1;
05963             /*@switchbreak@*/ break;
05964 
05965         case '}':
05966             if (state != PARSER_IN_EXPR) {
05967                 hsa->errmsg = _("unexpected }");
05968                 format = freeFormat(format, numTokens);
05969                 return 1;
05970             }
05971             *start++ = '\0';
05972 /*@-modfilesys@*/
05973 if (_hdrqf_debug)
05974 fprintf(stderr, "\t<= %s %p[-1] = NUL\n", pstates[(state & 0x3)], start);
05975 /*@=modfilesys@*/
05976             if (endPtr) *endPtr = start;
05977             done = 1;
05978             /*@switchbreak@*/ break;
05979 
05980         default:
05981             if (token == NULL || token->type != PTOK_STRING) {
05982                 token = format + numTokens++;
05983                 token->type = PTOK_STRING;
05984 /*@-temptrans -assignexpose@*/
05985                 dst = token->u.string.string = start;
05986 /*@=temptrans =assignexpose@*/
05987             }
05988 
05989 /*@-modfilesys@*/
05990 if (_hdrqf_debug)
05991 fprintf(stderr, "\t*%p = *%p \"%.30s\"\n", dst, start, start);
05992 /*@=modfilesys@*/
05993             if (start[0] == '\\' && start[1] != '\0') {
05994                 start++;
05995                 *dst++ = escapedChar(*start);
05996                 *start++ = '\0';
05997             } else {
05998                 *dst++ = *start++;
05999             }
06000             /*@switchbreak@*/ break;
06001         }
06002         if (dst < start) *dst = '\0';
06003         if (done)
06004             break;
06005     }
06006 /*@=infloops@*/
06007 
06008     if (dst != NULL)
06009         *dst = '\0';
06010 
06011     for (i = 0; i < (unsigned) numTokens; i++) {
06012         token = format + i;
06013         switch(token->type) {
06014         default:
06015             /*@switchbreak@*/ break;
06016         case PTOK_STRING:
06017             token->u.string.len = strlen(token->u.string.string);
06018             /*@switchbreak@*/ break;
06019         }
06020     }
06021 
06022     if (numTokensPtr != NULL)
06023         *numTokensPtr = numTokens;
06024     if (formatPtr != NULL)
06025         *formatPtr = format;
06026 
06027     return 0;
06028 }
06029 
06030 static int parseExpression(headerSprintfArgs hsa, sprintfToken token,
06031                 char * str, /*@out@*/ char ** endPtr)
06032 {
06033     char * chptr;
06034     char * end;
06035 
06036 /*@-modfilesys@*/
06037 if (_hdrqf_debug)
06038 fprintf(stderr, "-->   parseExpression(%p, %p, \"%.20s...\", %p)\n", hsa, token, str, endPtr);
06039 /*@=modfilesys@*/
06040 
06041     hsa->errmsg = NULL;
06042     chptr = str;
06043     while (*chptr && *chptr != '?') chptr++;
06044 
06045     if (*chptr != '?') {
06046         hsa->errmsg = _("? expected in expression");
06047         return 1;
06048     }
06049 
06050     *chptr++ = '\0';
06051 
06052     if (*chptr != '{') {
06053         hsa->errmsg = _("{ expected after ? in expression");
06054         return 1;
06055     }
06056 
06057     chptr++;
06058 
06059     if (parseFormat(hsa, chptr, &token->u.cond.ifFormat, 
06060                     &token->u.cond.numIfTokens, &end, PARSER_IN_EXPR)) 
06061         return 1;
06062 
06063     /* XXX fix segfault on "rpm -q rpm --qf='%|NAME?{%}:{NAME}|\n'"*/
06064     if (!(end && *end)) {
06065         hsa->errmsg = _("} expected in expression");
06066         token->u.cond.ifFormat =
06067                 freeFormat(token->u.cond.ifFormat, token->u.cond.numIfTokens);
06068         return 1;
06069     }
06070 
06071     chptr = end;
06072     if (*chptr != ':' && *chptr != '|') {
06073         hsa->errmsg = _(": expected following ? subexpression");
06074         token->u.cond.ifFormat =
06075                 freeFormat(token->u.cond.ifFormat, token->u.cond.numIfTokens);
06076         return 1;
06077     }
06078 
06079     if (*chptr == '|') {
06080         if (parseFormat(hsa, NULL, &token->u.cond.elseFormat, 
06081                 &token->u.cond.numElseTokens, &end, PARSER_IN_EXPR))
06082         {
06083             token->u.cond.ifFormat =
06084                 freeFormat(token->u.cond.ifFormat, token->u.cond.numIfTokens);
06085             return 1;
06086         }
06087     } else {
06088         chptr++;
06089 
06090         if (*chptr != '{') {
06091             hsa->errmsg = _("{ expected after : in expression");
06092             token->u.cond.ifFormat =
06093                 freeFormat(token->u.cond.ifFormat, token->u.cond.numIfTokens);
06094             return 1;
06095         }
06096 
06097         chptr++;
06098 
06099         if (parseFormat(hsa, chptr, &token->u.cond.elseFormat, 
06100                         &token->u.cond.numElseTokens, &end, PARSER_IN_EXPR)) 
06101             return 1;
06102 
06103         /* XXX fix segfault on "rpm -q rpm --qf='%|NAME?{a}:{%}|{NAME}\n'" */
06104         if (!(end && *end)) {
06105             hsa->errmsg = _("} expected in expression");
06106             token->u.cond.ifFormat =
06107                 freeFormat(token->u.cond.ifFormat, token->u.cond.numIfTokens);
06108             return 1;
06109         }
06110 
06111         chptr = end;
06112         if (*chptr != '|') {
06113             hsa->errmsg = _("| expected at end of expression");
06114             token->u.cond.ifFormat =
06115                 freeFormat(token->u.cond.ifFormat, token->u.cond.numIfTokens);
06116             token->u.cond.elseFormat =
06117                 freeFormat(token->u.cond.elseFormat, token->u.cond.numElseTokens);
06118             return 1;
06119         }
06120     }
06121         
06122     chptr++;
06123 
06124     *endPtr = chptr;
06125 
06126     token->type = PTOK_COND;
06127 
06128     (void) findTag(hsa, token, str);
06129 
06130     return 0;
06131 }
06132 
06141 static int getExtension(headerSprintfArgs hsa, headerTagTagFunction fn,
06142                 HE_t he, HE_t ec)
06143         /*@modifies he, ec @*/
06144 {
06145     int rc = 0;
06146     if (!ec->avail) {
06147         he = rpmheClean(he);
06148         rc = fn(hsa->h, he);
06149         *ec = *he;      /* structure copy. */
06150         if (!rc)
06151             ec->avail = 1;
06152     } else
06153         *he = *ec;      /* structure copy. */
06154     he->freeData = 0;
06155     rc = (rc == 0);     /* XXX invert getExtension return. */
06156     return rc;
06157 }
06158 
06166 /*@observer@*/ /*@null@*/
06167 static char * formatValue(headerSprintfArgs hsa, sprintfTag tag,
06168                 size_t element)
06169         /*@globals rpmGlobalMacroContext, h_errno, internalState @*/
06170         /*@modifies hsa, tag, rpmGlobalMacroContext, internalState @*/
06171 {
06172     HE_t vhe = memset(alloca(sizeof(*vhe)), 0, sizeof(*vhe));
06173     HE_t he = &tag->he;
06174     char * val = NULL;
06175     size_t need = 0;
06176     char * t, * te;
06177     rpmuint64_t ival = 0;
06178     rpmTagCount countBuf;
06179     int xx;
06180 
06181     if (!he->avail) {
06182         if (tag->ext)
06183             xx = getExtension(hsa, tag->ext, he, hsa->ec + tag->extNum);
06184         else {
06185             he->tag = tag->tagno[0];    /* XXX necessary? */
06186             xx = headerGet(hsa->h, he, 0);
06187         }
06188         if (!xx) {
06189             (void) rpmheClean(he);
06190             he->t = RPM_STRING_TYPE;    
06191             he->p.str = xstrdup("(none)");
06192             he->c = 1;
06193             he->freeData = 1;
06194         }
06195         he->avail = 1;
06196     }
06197 
06198     if (tag->arrayCount) {
06199         countBuf = he->c;
06200         he = rpmheClean(he);
06201         he->t = RPM_UINT32_TYPE;
06202         he->p.ui32p = &countBuf;
06203         he->c = 1;
06204         he->freeData = 0;
06205     }
06206 
06207     vhe->tag = he->tag;
06208 
06209     if (he->p.ptr)
06210     switch (he->t) {
06211     default:
06212         val = xstrdup("(unknown type)");
06213         need = strlen(val) + 1;
06214         goto exit;
06215         /*@notreached@*/ break;
06216     case RPM_I18NSTRING_TYPE:
06217     case RPM_STRING_ARRAY_TYPE:
06218         vhe->t = RPM_STRING_TYPE;
06219         vhe->p.str = he->p.argv[element];
06220         vhe->c = he->c;
06221         vhe->ix = (he->t == RPM_STRING_ARRAY_TYPE || he->c > 1 ? 0 : -1);
06222         break;
06223     case RPM_STRING_TYPE:
06224         vhe->p.str = he->p.str;
06225         vhe->t = RPM_STRING_TYPE;
06226         vhe->c = 0;
06227         vhe->ix = -1;
06228         break;
06229     case RPM_UINT8_TYPE:
06230     case RPM_UINT16_TYPE:
06231     case RPM_UINT32_TYPE:
06232     case RPM_UINT64_TYPE:
06233         switch (he->t) {
06234         default:
06235 assert(0);      /* XXX keep gcc quiet. */
06236             /*@innerbreak@*/ break;
06237         case RPM_UINT8_TYPE:
06238             ival = (rpmuint64_t)he->p.ui8p[element];
06239             /*@innerbreak@*/ break;
06240         case RPM_UINT16_TYPE:
06241             ival = (rpmuint64_t)he->p.ui16p[element];
06242             /*@innerbreak@*/ break;
06243         case RPM_UINT32_TYPE:
06244             ival = (rpmuint64_t)he->p.ui32p[element];
06245             /*@innerbreak@*/ break;
06246         case RPM_UINT64_TYPE:
06247             ival = he->p.ui64p[element];
06248             /*@innerbreak@*/ break;
06249         }
06250         vhe->t = RPM_UINT64_TYPE;
06251         vhe->p.ui64p = &ival;
06252         vhe->c = he->c;
06253         vhe->ix = (he->c > 1 ? 0 : -1);
06254         if ((tagType(he->tag) & RPM_MASK_RETURN_TYPE) == RPM_ARRAY_RETURN_TYPE)
06255             vhe->ix = 0;
06256         break;
06257 
06258     case RPM_BIN_TYPE:
06259         vhe->t = RPM_BIN_TYPE;
06260         vhe->p.ptr = he->p.ptr;
06261         vhe->c = he->c;
06262         vhe->ix = -1;
06263         break;
06264     }
06265 
06266 /*@-compmempass@*/      /* vhe->p.ui64p is stack, not owned */
06267     if (tag->fmtfuncs) {
06268         char * nval;
06269         int i;
06270         for (i = 0; tag->av[i] != NULL; i++) {
06271             headerTagFormatFunction fmt;
06272             ARGV_t av;
06273             if ((fmt = tag->fmtfuncs[i]) == NULL)
06274                 continue;
06275             /* If !1st formatter, and transformer, not extractor, save val. */
06276             if (val != NULL && *tag->av[i] == '|') {
06277                 int ix = vhe->ix;
06278                 vhe = rpmheClean(vhe);
06279                 vhe->tag = he->tag;
06280                 vhe->t = RPM_STRING_TYPE;
06281                 vhe->p.str = xstrdup(val);
06282                 vhe->c = he->c;
06283                 vhe->ix = ix;
06284                 vhe->freeData = 1;
06285             }
06286             av = NULL;
06287             if (tag->params && tag->params[i] && *tag->params[i] != '\0')
06288                 xx = argvSplit(&av, tag->params[i], ",");
06289 
06290             nval = fmt(vhe, av);
06291 
06292 /*@-castfcnptr -modfilesys@*/
06293 if (_hdrqf_debug)
06294 fprintf(stderr, "\t%s(%s) %p(%p,%p) ret \"%s\"\n", tag->av[i], (tag->params ? tag->params[i] : NULL), (void *)fmt, (void *)vhe, (void *)(av ? av : NULL), (val ? val : "(null)"));
06295 /*@=castfcnptr =modfilesys@*/
06296 
06297             /* Accumulate (by appending) next formmatter's return string. */
06298             if (val == NULL)
06299                 val = xstrdup((nval ? nval : ""));
06300             else {
06301                 char * oval = val;
06302                 /* XXX using ... | ... as separator is feeble. */
06303                 val = rpmExpand(val, (*val != '\0' ? " | " : ""), nval, NULL);
06304                 oval = _free(oval);
06305             }
06306             nval = _free(nval);
06307             av = argvFree(av);
06308         }
06309     }
06310     if (val == NULL)
06311         val = intFormat(vhe, NULL, NULL);
06312 /*@=compmempass@*/
06313 assert(val != NULL);
06314     if (val)
06315         need = strlen(val) + 1;
06316 
06317 exit:
06318     if (val && need > 0) {
06319         if (tag->format && *tag->format && tag->pad > 0) {
06320             size_t nb;
06321             nb = strlen(tag->format) + sizeof("%s");
06322             t = alloca(nb);
06323             (void) stpcpy( stpcpy( stpcpy(t, "%"), tag->format), "s");
06324             nb = tag->pad + strlen(val) + 1;
06325             te = xmalloc(nb);
06326 /*@-formatconst@*/
06327             (void) snprintf(te, nb, t, val);
06328 /*@=formatconst@*/
06329             te[nb-1] = '\0';
06330             val = _free(val);
06331             val = te;
06332             need += tag->pad;
06333         }
06334         t = hsaReserve(hsa, need);
06335         te = stpcpy(t, val);
06336         hsa->vallen += (te - t);
06337         val = _free(val);
06338     }
06339 
06340     return (hsa->val + hsa->vallen);
06341 }
06342 
06350 /*@observer@*/ /*@null@*/
06351 static char * singleSprintf(headerSprintfArgs hsa, sprintfToken token,
06352                 size_t element)
06353         /*@globals rpmGlobalMacroContext, h_errno, internalState @*/
06354         /*@modifies hsa, token, rpmGlobalMacroContext, internalState @*/
06355 {
06356     char * t, * te;
06357     size_t i, j;
06358     size_t numElements;
06359     sprintfToken spft;
06360     sprintfTag tag = NULL;
06361     HE_t he = NULL;
06362     size_t condNumFormats;
06363     size_t need;
06364     int xx;
06365 
06366     /* we assume the token and header have been validated already! */
06367 
06368     switch (token->type) {
06369     case PTOK_NONE:
06370         break;
06371 
06372     case PTOK_STRING:
06373         need = token->u.string.len;
06374         if (need == 0) break;
06375         t = hsaReserve(hsa, need);
06376         te = stpcpy(t, token->u.string.string);
06377         hsa->vallen += (te - t);
06378         break;
06379 
06380     case PTOK_TAG:
06381         t = hsa->val + hsa->vallen;
06382 /*@-modobserver@*/      /* headerCompoundFormats not modified. */
06383         te = formatValue(hsa, &token->u.tag,
06384                         (token->u.tag.justOne ? 0 : element));
06385 /*@=modobserver@*/
06386         if (te == NULL)
06387             return NULL;
06388         break;
06389 
06390     case PTOK_COND:
06391         if (token->u.cond.tag.ext
06392          || headerIsEntry(hsa->h, token->u.cond.tag.tagno[0]))
06393         {
06394             spft = token->u.cond.ifFormat;
06395             condNumFormats = token->u.cond.numIfTokens;
06396         } else {
06397             spft = token->u.cond.elseFormat;
06398             condNumFormats = token->u.cond.numElseTokens;
06399         }
06400 
06401         need = condNumFormats * 20;
06402         if (spft == NULL || need == 0) break;
06403 
06404         t = hsaReserve(hsa, need);
06405         for (i = 0; i < condNumFormats; i++, spft++) {
06406 /*@-modobserver@*/      /* headerCompoundFormats not modified. */
06407             te = singleSprintf(hsa, spft, element);
06408 /*@=modobserver@*/
06409             if (te == NULL)
06410                 return NULL;
06411         }
06412         break;
06413 
06414     case PTOK_ARRAY:
06415         numElements = 0;
06416         spft = token->u.array.format;
06417         for (i = 0; i < token->u.array.numTokens; i++, spft++)
06418         {
06419             tag = &spft->u.tag;
06420             if (spft->type != PTOK_TAG || tag->arrayCount || tag->justOne)
06421                 continue;
06422             he = &tag->he;
06423             if (!he->avail) {
06424                 he->tag = tag->tagno[0];
06425                 if (tag->ext)
06426                     xx = getExtension(hsa, tag->ext, he, hsa->ec + tag->extNum);
06427                 else
06428                     xx = headerGet(hsa->h, he, 0);
06429                 if (!xx) {
06430                     (void) rpmheClean(he);
06431                     continue;
06432                 }
06433                 he->avail = 1;
06434             }
06435 
06436             /* Check iteration arrays are same dimension (or scalar). */
06437             switch (he->t) {
06438             default:
06439                 if (numElements == 0) {
06440                     numElements = he->c;
06441                     /*@switchbreak@*/ break;
06442                 }
06443                 if ((size_t)he->c == numElements)
06444                     /*@switchbreak@*/ break;
06445                 hsa->errmsg =
06446                         _("array iterator used with different sized arrays");
06447                 he = rpmheClean(he);
06448                 return NULL;
06449                 /*@notreached@*/ /*@switchbreak@*/ break;
06450             case RPM_BIN_TYPE:
06451             case RPM_STRING_TYPE:
06452                 if (numElements == 0)
06453                     numElements = 1;
06454                 /*@switchbreak@*/ break;
06455             }
06456         }
06457         spft = token->u.array.format;
06458 
06459         if (numElements == 0) {
06460 #ifdef  DYING   /* XXX lots of pugly "(none)" lines with --conflicts. */
06461             need = sizeof("(none)\n") - 1;
06462             t = hsaReserve(hsa, need);
06463             te = stpcpy(t, "(none)\n");
06464             hsa->vallen += (te - t);
06465 #endif
06466         } else {
06467             rpmTagReturnType tagT = 0;
06468             const char * tagN = NULL;
06469             spew_t spew = NULL;
06470 
06471             need = numElements * token->u.array.numTokens;
06472             if (need == 0) break;
06473 
06474             tag = &spft->u.tag;
06475 
06476 spew = NULL;
06477             /* XXX Ick: +1 needed to handle :extractor |transformer marking. */
06478             if (spft->type == PTOK_TAG && tag->av != NULL
06479              && tag->av[0] != NULL && !strcmp(tag->av[0]+1, "xml"))
06480                 spew = &_xml_spew;
06481             if (spft->type == PTOK_TAG && tag->av != NULL
06482              && tag->av[0] != NULL && !strcmp(tag->av[0]+1, "yaml"))
06483                 spew = &_yaml_spew;
06484             if (spft->type == PTOK_TAG && tag->av != NULL
06485              && tag->av[0] != NULL && !strcmp(tag->av[0]+1, "json"))
06486                 spew = &_json_spew;
06487 
06488             if (spew == &_xml_spew) {
06489 assert(tag->tagno != NULL);
06490                 /* XXX display "Tag_0x01234567" for arbitrary tags. */
06491                 if (tag->tagno[0] & 0x40000000) {
06492                     tagN = myTagName(hsa->tags, tag->tagno[0], NULL);
06493                 } else
06494                     tagN = myTagName(hsa->tags, tag->tagno[0], NULL);
06495                 need = sizeof("  <rpmTag name=\"\">\n") + strlen(tagN);
06496                 te = t = hsaReserve(hsa, need);
06497                 te = stpcpy( stpcpy( stpcpy(te, "  <rpmTag name=\""), tagN), "\">\n");
06498                 hsa->vallen += (te - t);
06499             }
06500             if (spew == &_yaml_spew) {
06501 assert(tag->tagno != NULL);
06502                 /* XXX display "Tag_0x01234567" for arbitrary tags. */
06503                 if (tag->tagno[0] & 0x40000000) {
06504                     tagN = myTagName(hsa->tags, tag->tagno[0], NULL);
06505                     tagT = numElements > 1
06506                         ?  RPM_ARRAY_RETURN_TYPE : RPM_SCALAR_RETURN_TYPE;
06507                 } else
06508                     tagN = myTagName(hsa->tags, tag->tagno[0], &tagT);
06509                 need = sizeof("  :     - ") + strlen(tagN);
06510                 te = t = hsaReserve(hsa, need);
06511                 *te++ = ' ';
06512                 *te++ = ' ';
06513                 te = stpcpy(te, tagN);
06514                 *te++ = ':';
06515                 *te++ = (((tagT & RPM_MASK_RETURN_TYPE) == RPM_ARRAY_RETURN_TYPE)
06516                         ? '\n' : ' ');
06517                 *te = '\0';
06518                 hsa->vallen += (te - t);
06519             }
06520             if (spew == &_json_spew) {
06521 assert(tag->tagno != NULL);
06522                 /* XXX display "Tag_0x01234567" for arbitrary tags. */
06523                 if (tag->tagno[0] & 0x40000000) {
06524                     tagN = myTagName(hsa->tags, tag->tagno[0], NULL);
06525                     tagT = numElements > 1
06526                         ?  RPM_ARRAY_RETURN_TYPE : RPM_SCALAR_RETURN_TYPE;
06527                 } else
06528                 if (tag->tagno[0] == RPMTAG_HDRID) {    /* RPMTAG_SHA1HEADER */
06529                     tagN = "_id";       /* XXX mongo primary key name */
06530                 } else
06531                     tagN = myTagName(hsa->tags, tag->tagno[0], &tagT);
06532                 need = sizeof("  : [\n") + strlen(tagN);
06533                 te = t = hsaReserve(hsa, need);
06534                 te = stpcpy( stpcpy( stpcpy(te, "  "), tagN), ":");
06535                 if ((tagT & RPM_MASK_RETURN_TYPE) == RPM_ARRAY_RETURN_TYPE)
06536                     te = stpcpy(te, " [\n");
06537                 hsa->vallen += (te - t);
06538             }
06539 
06540             need = numElements * token->u.array.numTokens * 10;
06541             t = hsaReserve(hsa, need);
06542             for (j = 0; j < numElements; j++) {
06543                 spft = token->u.array.format;
06544                 for (i = 0; i < token->u.array.numTokens; i++, spft++) {
06545 /*@-modobserver@*/      /* headerCompoundFormats not modified. */
06546                     te = singleSprintf(hsa, spft, j);
06547 /*@=modobserver@*/
06548                     if (te == NULL)
06549                         return NULL;
06550                 }
06551             }
06552 
06553             if (spew == &_xml_spew) {
06554                 need = sizeof("  </rpmTag>\n") - 1;
06555                 te = t = hsaReserve(hsa, need);
06556                 te = stpcpy(te, "  </rpmTag>\n");
06557                 hsa->vallen += (te - t);
06558             }
06559             if (spew == &_json_spew) {
06560                 if ((tagT & RPM_MASK_RETURN_TYPE) == RPM_ARRAY_RETURN_TYPE) {
06561                     need = sizeof("  ],\n") - 1;
06562                     te = t = hsaReserve(hsa, need);
06563                     te = stpcpy(te, "  ],\n");
06564                     hsa->vallen += (te - t);
06565                 }
06566             }
06567 
06568         }
06569         break;
06570     }
06571 
06572     return (hsa->val + hsa->vallen);
06573 }
06574 
06581 static /*@only@*/ HE_t
06582 rpmecNew(const headerSprintfExtension exts, /*@null@*/ int * necp)
06583         /*@modifies *necp @*/
06584 {
06585     headerSprintfExtension ext;
06586     HE_t ec;
06587     int extNum = 0;
06588 
06589     if (exts != NULL)
06590     for (ext = exts, extNum = 0; ext != NULL && ext->type != HEADER_EXT_LAST;
06591         ext = (ext->type == HEADER_EXT_MORE ? *ext->u.more : ext+1), extNum++)
06592     {
06593         ;
06594     }
06595     if (necp)
06596         *necp = extNum;
06597     ec = xcalloc(extNum+1, sizeof(*ec));        /* XXX +1 unnecessary */
06598     return ec;
06599 }
06600 
06607 static /*@null@*/ HE_t
06608 rpmecFree(const headerSprintfExtension exts, /*@only@*/ HE_t ec)
06609         /*@modifies ec @*/
06610 {
06611     headerSprintfExtension ext;
06612     int extNum;
06613 
06614     for (ext = exts, extNum = 0; ext != NULL && ext->type != HEADER_EXT_LAST;
06615         ext = (ext->type == HEADER_EXT_MORE ? *ext->u.more : ext+1), extNum++)
06616     {
06617         (void) rpmheClean(&ec[extNum]);
06618     }
06619 
06620     ec = _free(ec);
06621     return NULL;
06622 }
06623 
06624 char * headerSprintf(Header h, const char * fmt,
06625                 headerTagTableEntry tags,
06626                 headerSprintfExtension exts,
06627                 errmsg_t * errmsg)
06628 {
06629     headerSprintfArgs hsa = memset(alloca(sizeof(*hsa)), 0, sizeof(*hsa));
06630     sprintfToken nextfmt;
06631     sprintfTag tag;
06632     char * t, * te;
06633     int need;
06634 spew_t spew = NULL;
06635 
06636 /*@-modfilesys@*/
06637 if (_hdrqf_debug)
06638 fprintf(stderr, "==> headerSprintf(%p, \"%s\", %p, %p, %p)\n", h, fmt, tags, exts, errmsg);
06639 /*@=modfilesys@*/
06640 
06641     /* Set some reasonable defaults */
06642     if (tags == NULL)
06643         tags = rpmTagTable;
06644     /* XXX this loses the extensions in lib/formats.c. */
06645     if (exts == NULL)
06646         exts = headerCompoundFormats;
06647  
06648 /*@-assignexpose -castexpose @*/
06649     hsa->h = headerLink(h);
06650 /*@=assignexpose =castexpose @*/
06651     hsa->fmt = xstrdup(fmt);
06652 /*@-assignexpose -dependenttrans@*/
06653     hsa->exts = exts;
06654     hsa->tags = tags;
06655 /*@=assignexpose =dependenttrans@*/
06656     hsa->errmsg = NULL;
06657 
06658     if (parseFormat(hsa, hsa->fmt, &hsa->format, &hsa->numTokens, NULL, PARSER_BEGIN))
06659         goto exit;
06660 
06661     hsa->nec = 0;
06662     hsa->ec = rpmecNew(hsa->exts, &hsa->nec);
06663     hsa->val = xstrdup("");
06664 
06665     tag =
06666         (hsa->format->type == PTOK_TAG
06667             ? &hsa->format->u.tag :
06668         (hsa->format->type == PTOK_ARRAY
06669             ? &hsa->format->u.array.format->u.tag :
06670         NULL));
06671 
06672 spew = NULL;
06673     /* XXX Ick: +1 needed to handle :extractor |transformer marking. */
06674     if (tag != NULL && tag->tagno != NULL && tag->tagno[0] == (rpmTag)-2
06675      && tag->av != NULL && tag->av[0] != NULL && !strcmp(tag->av[0]+1, "xml"))
06676         spew = &_xml_spew;
06677     if (tag != NULL && tag->tagno != NULL && tag->tagno[0] == (rpmTag)-2
06678      && tag->av != NULL && tag->av[0] != NULL && !strcmp(tag->av[0]+1, "yaml"))
06679         spew = &_yaml_spew;
06680     if (tag != NULL && tag->tagno != NULL && tag->tagno[0] == (rpmTag)-2
06681      && tag->av != NULL && tag->av[0] != NULL && !strcmp(tag->av[0]+1, "json"))
06682         spew = &_json_spew;
06683 
06684     if (spew && spew->spew_init && spew->spew_init[0]) {
06685         need = strlen(spew->spew_init);
06686         t = hsaReserve(hsa, need);
06687         te = stpcpy(t, spew->spew_init);
06688         hsa->vallen += (te - t);
06689     }
06690 
06691     hsa = hsaInit(hsa);
06692     while ((nextfmt = hsaNext(hsa)) != NULL) {
06693 /*@-globs -mods@*/      /* XXX rpmGlobalMacroContext @*/
06694         te = singleSprintf(hsa, nextfmt, 0);
06695 /*@=globs =mods @*/
06696         if (te == NULL) {
06697             hsa->val = _free(hsa->val);
06698             break;
06699         }
06700     }
06701     hsa = hsaFini(hsa);
06702 
06703     if (spew && spew->spew_fini && spew->spew_fini[0]) {
06704         need = strlen(spew->spew_fini);
06705         t = hsaReserve(hsa, need);
06706         te = stpcpy(t, spew->spew_fini);
06707         hsa->vallen += (te - t);
06708     }
06709 
06710     if (hsa->val != NULL && hsa->vallen < hsa->alloced)
06711         hsa->val = xrealloc(hsa->val, hsa->vallen+1);   
06712 
06713     hsa->ec = rpmecFree(hsa->exts, hsa->ec);
06714     hsa->nec = 0;
06715     hsa->format = freeFormat(hsa->format, hsa->numTokens);
06716 
06717 exit:
06718 /*@-dependenttrans -observertrans @*/
06719     if (errmsg)
06720         *errmsg = hsa->errmsg;
06721 /*@=dependenttrans =observertrans @*/
06722     (void)headerFree(hsa->h);
06723     hsa->h = NULL;
06724     hsa->fmt = _free(hsa->fmt);
06725 /*@-retexpose@*/
06726     return hsa->val;
06727 /*@=retexpose@*/
06728 }