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