rpm 5.3.7

rpmdb/hdrfmt.c

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