rpm  4.5
rpmdb/header.c
Go to the documentation of this file.
00001 
00005 /* RPM - Copyright (C) 1995-2002 Red Hat Software */
00006 
00007 /* Data written to file descriptors is in network byte order.    */
00008 /* Data read from file descriptors is expected to be in          */
00009 /* network byte order and is converted on the fly to host order. */
00010 
00011 #include "system.h"
00012 
00013 #define __HEADER_PROTOTYPES__
00014 
00015 #include <rpmio_internal.h>     /* XXX for fdGetOPath() */
00016 #include <header_internal.h>
00017 #include <rpmmacro.h>
00018 
00019 #include "debug.h"
00020 
00021 /*@unchecked@*/
00022 int _hdr_debug = 0;
00023 
00024 /*@unchecked@*/
00025 int _tagcache = 1;              /* XXX Cache tag data persistently? */
00026 
00027 /*@access entryInfo @*/
00028 /*@access indexEntry @*/
00029 
00030 /*@access sprintfTag @*/
00031 /*@access sprintfToken @*/
00032 /*@access HV_t @*/
00033 
00034 #define PARSER_BEGIN    0
00035 #define PARSER_IN_ARRAY 1
00036 #define PARSER_IN_EXPR  2
00037 
00040 /*@observer@*/ /*@unchecked@*/
00041 static unsigned char header_magic[8] = {
00042         0x8e, 0xad, 0xe8, 0x01, 0x00, 0x00, 0x00, 0x00
00043 };
00044 
00048 /*@observer@*/ /*@unchecked@*/
00049 static int typeAlign[16] =  {
00050     1,  
00051     1,  
00052     1,  
00053     2,  
00054     4,  
00055     8,  
00056     1,  
00057     1,  
00058     1,  
00059     1,  
00060     1,  
00061     1,  
00062     0,
00063     0,
00064     0,
00065     0
00066 };
00067 
00071 /*@observer@*/ /*@unchecked@*/
00072 static int typeSizes[16] =  { 
00073     0,  
00074     1,  
00075     1,  
00076     2,  
00077     4,  
00078     8,  
00079     -1, 
00080     1,  
00081     -1, 
00082     -1, 
00083     1,  
00084     1,  
00085     0,
00086     0,
00087     0,
00088     0
00089 };
00090 
00094 /*@unchecked@*/
00095 static size_t headerMaxbytes = (32*1024*1024);
00096 
00101 #define hdrchkTags(_ntags)      ((_ntags) & 0xffff0000)
00102 
00106 #define hdrchkType(_type) ((_type) < RPM_MIN_TYPE || (_type) > RPM_MAX_TYPE)
00107 
00112 #define hdrchkData(_nbytes)     ((_nbytes) & 0xff000000)
00113 
00117 #define hdrchkAlign(_type, _off)        ((_off) & (typeAlign[_type]-1))
00118 
00122 #define hdrchkRange(_dl, _off)          ((_off) < 0 || (_off) > (_dl))
00123 
00124 /*@observer@*/ /*@unchecked@*/
00125 HV_t hdrVec;    /* forward reference */
00126 
00133 static /*@null@*/
00134 void * headerGetStats(/*@unused@*/ Header h, /*@unused@*/ int opx)
00135         /*@*/
00136 {
00137     rpmop op = NULL;
00138     return op;
00139 }
00140 
00146 static
00147 Header headerLink(Header h)
00148         /*@modifies h @*/
00149 {
00150 /*@-nullret@*/
00151     if (h == NULL) return NULL;
00152 /*@=nullret@*/
00153 
00154     h->nrefs++;
00155 /*@-modfilesys@*/
00156 if (_hdr_debug)
00157 fprintf(stderr, "--> h  %p ++ %d at %s:%u\n", h, h->nrefs, __FILE__, __LINE__);
00158 /*@=modfilesys@*/
00159 
00160     /*@-refcounttrans @*/
00161     return h;
00162     /*@=refcounttrans @*/
00163 }
00164 
00170 static /*@null@*/
00171 Header headerUnlink(/*@killref@*/ /*@null@*/ Header h)
00172         /*@modifies h @*/
00173 {
00174     if (h == NULL) return NULL;
00175 /*@-modfilesys@*/
00176 if (_hdr_debug)
00177 fprintf(stderr, "--> h  %p -- %d at %s:%u\n", h, h->nrefs, __FILE__, __LINE__);
00178 /*@=modfilesys@*/
00179     h->nrefs--;
00180     return NULL;
00181 }
00182 
00188 static /*@null@*/
00189 Header headerFree(/*@killref@*/ /*@null@*/ Header h)
00190         /*@modifies h @*/
00191 {
00192     (void) headerUnlink(h);
00193 
00194     /*@-usereleased@*/
00195     if (h == NULL || h->nrefs > 0)
00196         return NULL;    /* XXX return previous header? */
00197 
00198     if (h->index) {
00199         indexEntry entry = h->index;
00200         int i;
00201         for (i = 0; i < h->indexUsed; i++, entry++) {
00202             if ((h->flags & HEADERFLAG_ALLOCATED) && ENTRY_IS_REGION(entry)) {
00203                 if (entry->length > 0) {
00204                     int_32 * ei = entry->data;
00205                     if ((ei - 2) == h->blob) h->blob = _free(h->blob);
00206                     entry->data = NULL;
00207                 }
00208             } else if (!ENTRY_IN_REGION(entry)) {
00209                 entry->data = _free(entry->data);
00210             }
00211             entry->data = NULL;
00212         }
00213         h->index = _free(h->index);
00214     }
00215     h->origin = _free(h->origin);
00216     h->baseurl = _free(h->baseurl);
00217     h->digest = _free(h->digest);
00218 
00219     /*@-refcounttrans@*/ h = _free(h); /*@=refcounttrans@*/
00220     return h;
00221     /*@=usereleased@*/
00222 }
00223 
00228 static
00229 Header headerNew(void)
00230         /*@*/
00231 {
00232     Header h = xcalloc(1, sizeof(*h));
00233 
00234     /*@-assignexpose@*/
00235     h->hv = *hdrVec;            /* structure assignment */
00236     /*@=assignexpose@*/
00237     h->blob = NULL;
00238     h->origin = NULL;
00239     h->baseurl = NULL;
00240     h->digest = NULL;
00241     h->instance = 0;
00242     h->indexAlloced = INDEX_MALLOC_SIZE;
00243     h->indexUsed = 0;
00244     h->flags |= HEADERFLAG_SORTED;
00245 
00246     h->index = (h->indexAlloced
00247         ? xcalloc(h->indexAlloced, sizeof(*h->index))
00248         : NULL);
00249 
00250     h->nrefs = 0;
00251     /*@-globstate -observertrans @*/
00252     return headerLink(h);
00253     /*@=globstate =observertrans @*/
00254 }
00255 
00258 static int indexCmp(const void * avp, const void * bvp)
00259         /*@*/
00260 {
00261     /*@-castexpose@*/
00262     indexEntry ap = (indexEntry) avp, bp = (indexEntry) bvp;
00263     /*@=castexpose@*/
00264     return (ap->info.tag - bp->info.tag);
00265 }
00266 
00271 static
00272 void headerSort(Header h)
00273         /*@modifies h @*/
00274 {
00275     if (!(h->flags & HEADERFLAG_SORTED)) {
00276         qsort(h->index, h->indexUsed, sizeof(*h->index), indexCmp);
00277         h->flags |= HEADERFLAG_SORTED;
00278     }
00279 }
00280 
00283 static int offsetCmp(const void * avp, const void * bvp) /*@*/
00284 {
00285     /*@-castexpose@*/
00286     indexEntry ap = (indexEntry) avp, bp = (indexEntry) bvp;
00287     /*@=castexpose@*/
00288     int rc = (ap->info.offset - bp->info.offset);
00289 
00290     if (rc == 0) {
00291         /* Within a region, entries sort by address. Added drips sort by tag. */
00292         if (ap->info.offset < 0)
00293             rc = (((char *)ap->data) - ((char *)bp->data));
00294         else
00295             rc = (ap->info.tag - bp->info.tag);
00296     }
00297     return rc;
00298 }
00299 
00304 static
00305 void headerUnsort(Header h)
00306         /*@modifies h @*/
00307 {
00308     qsort(h->index, h->indexUsed, sizeof(*h->index), offsetCmp);
00309 }
00310 
00317 static
00318 unsigned int headerSizeof(/*@null@*/ Header h, enum hMagic magicp)
00319         /*@modifies h @*/
00320 {
00321     indexEntry entry;
00322     unsigned int size = 0;
00323     unsigned int pad = 0;
00324     int i;
00325 
00326     if (h == NULL)
00327         return size;
00328 
00329     headerSort(h);
00330 
00331     switch (magicp) {
00332     case HEADER_MAGIC_YES:
00333         size += sizeof(header_magic);
00334         break;
00335     case HEADER_MAGIC_NO:
00336         break;
00337     }
00338 
00339     /*@-sizeoftype@*/
00340     size += 2 * sizeof(int_32); /* count of index entries */
00341     /*@=sizeoftype@*/
00342 
00343     for (i = 0, entry = h->index; i < h->indexUsed; i++, entry++) {
00344         unsigned diff;
00345         rpmTagType type;
00346 
00347         /* Regions go in as is ... */
00348         if (ENTRY_IS_REGION(entry)) {
00349             size += entry->length;
00350             /* XXX Legacy regions do not include the region tag and data. */
00351             /*@-sizeoftype@*/
00352             if (i == 0 && (h->flags & HEADERFLAG_LEGACY))
00353                 size += sizeof(struct entryInfo_s) + entry->info.count;
00354             /*@=sizeoftype@*/
00355             continue;
00356         }
00357 
00358         /* ... and region elements are skipped. */
00359         if (entry->info.offset < 0)
00360             continue;
00361 
00362         /* Alignment */
00363         type = entry->info.type;
00364         if (typeSizes[type] > 1) {
00365             diff = typeSizes[type] - (size % typeSizes[type]);
00366             if (diff != typeSizes[type]) {
00367                 size += diff;
00368                 pad += diff;
00369             }
00370         }
00371 
00372         /*@-sizeoftype@*/
00373         size += sizeof(struct entryInfo_s) + entry->length;
00374         /*@=sizeoftype@*/
00375     }
00376 
00377     return size;
00378 }
00379 
00389 static int dataLength(rpmTagType type, rpmTagData * p, rpmTagCount count,
00390                 int onDisk, /*@null@*/ rpmTagData * pend)
00391         /*@*/
00392 {
00393     const unsigned char * s = (*p).ui8p;
00394     const unsigned char * se = (pend ? (*pend).ui8p : NULL);
00395     int length = 0;
00396 
00397     switch (type) {
00398     case RPM_STRING_TYPE:
00399         if (count != 1)
00400             return -1;
00401         while (*s++) {
00402             if (se && s > se)
00403                 return -1;
00404             length++;
00405         }
00406         length++;       /* count nul terminator too. */
00407         break;
00408         /* These are like RPM_STRING_TYPE, except they're *always* an array */
00409         /* Compute sum of length of all strings, including nul terminators */
00410     case RPM_I18NSTRING_TYPE:
00411     case RPM_STRING_ARRAY_TYPE:
00412         if (onDisk) {
00413             while (count--) {
00414                 length++;       /* count nul terminator too */
00415                while (*s++) {
00416                     if (se && s > se)
00417                         return -1;      /* XXX change errret, use size_t */
00418                     length++;
00419                 }
00420             }
00421         } else {
00422             const char ** av = (*p).argv;
00423             while (count--) {
00424                 /* add one for null termination */
00425                 length += strlen(*av++) + 1;
00426             }
00427         }
00428         break;
00429     default:
00430         if (typeSizes[type] == -1)
00431             return -1;          /* XXX change errret, use size_t */
00432         length = typeSizes[(type & 0xf)] * count;
00433         if (length < 0 || (se && (s + length) > se))
00434             return -1;          /* XXX change errret, use size_t */
00435         break;
00436     }
00437 
00438     return length;
00439 }
00440 
00467 static int regionSwab(/*@null@*/ indexEntry entry, int il, int dl,
00468                 entryInfo pe,
00469                 unsigned char * dataStart,
00470                 /*@null@*/ const unsigned char * dataEnd,
00471                 int regionid)
00472         /*@modifies *entry, *dataStart @*/
00473 {
00474     rpmTagData p;
00475     rpmTagData pend;
00476     unsigned char * tprev = NULL;
00477     unsigned char * t = NULL;
00478     int tdel = 0;
00479     int tl = dl;
00480     struct indexEntry_s ieprev;
00481 
00482     memset(&ieprev, 0, sizeof(ieprev));
00483     for (; il > 0; il--, pe++) {
00484         struct indexEntry_s ie;
00485         rpmTagType type;
00486 
00487         ie.info.tag = ntohl(pe->tag);
00488         ie.info.type = ntohl(pe->type);
00489         ie.info.count = ntohl(pe->count);
00490         ie.info.offset = ntohl(pe->offset);
00491 
00492         if (hdrchkType(ie.info.type))
00493             return -1;
00494         if (hdrchkData(ie.info.count))
00495             return -1;
00496         if (hdrchkData(ie.info.offset))
00497             return -1;
00498         if (hdrchkAlign(ie.info.type, ie.info.offset))
00499             return -1;
00500 
00501         ie.data = t = dataStart + ie.info.offset;
00502         if (dataEnd && t >= dataEnd)
00503             return -1;
00504 
00505         p.ptr = ie.data;
00506         pend.ui8p = (unsigned char *) dataEnd;
00507         ie.length = dataLength(ie.info.type, &p, ie.info.count, 1, &pend);
00508         if (ie.length < 0 || hdrchkData(ie.length))
00509             return -1;
00510 
00511         ie.rdlen = 0;
00512 
00513         if (entry) {
00514             ie.info.offset = regionid;
00515             *entry = ie;        /* structure assignment */
00516             entry++;
00517         }
00518 
00519         /* Alignment */
00520         type = ie.info.type;
00521         if (typeSizes[type] > 1) {
00522             unsigned diff;
00523             diff = typeSizes[type] - (dl % typeSizes[type]);
00524             if (diff != typeSizes[type]) {
00525                 dl += diff;
00526                 if (ieprev.info.type == RPM_I18NSTRING_TYPE)
00527                     ieprev.length += diff;
00528             }
00529         }
00530         tdel = (tprev ? (t - tprev) : 0);
00531         if (ieprev.info.type == RPM_I18NSTRING_TYPE)
00532             tdel = ieprev.length;
00533 
00534         if (ie.info.tag >= HEADER_I18NTABLE) {
00535             tprev = t;
00536         } else {
00537             tprev = dataStart;
00538             /* XXX HEADER_IMAGE tags don't include region sub-tag. */
00539             /*@-sizeoftype@*/
00540             if (ie.info.tag == HEADER_IMAGE)
00541                 tprev -= REGION_TAG_COUNT;
00542             /*@=sizeoftype@*/
00543         }
00544 
00545         /* Perform endian conversions */
00546         switch (ntohl(pe->type)) {
00547         case RPM_INT64_TYPE:
00548         {   int_64 * it = (int_64 *)t;
00549             int_32 b[2];
00550             for (; ie.info.count > 0; ie.info.count--, it += 1) {
00551                 if (dataEnd && ((unsigned char *)it) >= dataEnd)
00552                     return -1;
00553                 b[1] = htonl(((int_32 *)it)[0]);
00554                 b[0] = htonl(((int_32 *)it)[1]);
00555                 if (b[1] != ((int_32 *)it)[0])
00556                     memcpy(it, b, sizeof(b));
00557             }
00558             t = (unsigned char *) it;
00559         }   /*@switchbreak@*/ break;
00560         case RPM_INT32_TYPE:
00561         {   int_32 * it = (int_32 *)t;
00562             for (; ie.info.count > 0; ie.info.count--, it += 1) {
00563                 if (dataEnd && ((unsigned char *)it) >= dataEnd)
00564                     return -1;
00565                 *it = htonl(*it);
00566             }
00567             t = (unsigned char *) it;
00568         }   /*@switchbreak@*/ break;
00569         case RPM_INT16_TYPE:
00570         {   int_16 * it = (int_16 *) t;
00571             for (; ie.info.count > 0; ie.info.count--, it += 1) {
00572                 if (dataEnd && ((unsigned char *)it) >= dataEnd)
00573                     return -1;
00574                 *it = htons(*it);
00575             }
00576             t = (unsigned char *) it;
00577         }   /*@switchbreak@*/ break;
00578         default:
00579             t += ie.length;
00580             /*@switchbreak@*/ break;
00581         }
00582 
00583         dl += ie.length;
00584         if (dataEnd && (dataStart + dl) > dataEnd) return -1;
00585         tl += tdel;
00586         ieprev = ie;    /* structure assignment */
00587 
00588     }
00589     tdel = (tprev ? (t - tprev) : 0);
00590     tl += tdel;
00591 
00592     /* XXX
00593      * There are two hacks here:
00594      *  1) tl is 16b (i.e. REGION_TAG_COUNT) short while doing headerReload().
00595      *  2) the 8/98 rpm bug with inserting i18n tags needs to use tl, not dl.
00596      */
00597     /*@-sizeoftype@*/
00598     if (tl+REGION_TAG_COUNT == dl)
00599         tl += REGION_TAG_COUNT;
00600     /*@=sizeoftype@*/
00601 
00602     return dl;
00603 }
00604 
00610 static /*@only@*/ /*@null@*/ void * doHeaderUnload(Header h,
00611                 /*@out@*/ size_t * lenp)
00612         /*@modifies h, *lenp @*/
00613         /*@requires maxSet(lenp) >= 0 @*/
00614         /*@ensures maxRead(result) == (*lenp) @*/
00615 {
00616     void * sw;
00617     int_32 * ei = NULL;
00618     entryInfo pe;
00619     unsigned char * dataStart;
00620     unsigned char * te;
00621     unsigned pad;
00622     unsigned len = 0;
00623     int_32 il = 0;
00624     int_32 dl = 0;
00625     indexEntry entry; 
00626     rpmTagType type;
00627     int i;
00628     int drlen, ndribbles;
00629     int driplen, ndrips;
00630     int legacy = 0;
00631 
00632     if ((sw = headerGetStats(h, 18)) != NULL)   /* RPMTS_OP_HDRLOAD */
00633         (void) rpmswEnter(sw, 0);
00634 
00635     /* Sort entries by (offset,tag). */
00636     headerUnsort(h);
00637 
00638     /* Compute (il,dl) for all tags, including those deleted in region. */
00639     pad = 0;
00640     drlen = ndribbles = driplen = ndrips = 0;
00641     for (i = 0, entry = h->index; i < h->indexUsed; i++, entry++) {
00642         if (ENTRY_IS_REGION(entry)) {
00643             int_32 rdl = -entry->info.offset;   /* negative offset */
00644             int_32 ril = rdl/sizeof(*pe);
00645             int rid = entry->info.offset;
00646 
00647             il += ril;
00648             dl += entry->rdlen + entry->info.count;
00649             /* XXX Legacy regions do not include the region tag and data. */
00650             if (i == 0 && (h->flags & HEADERFLAG_LEGACY))
00651                 il += 1;
00652 
00653             /* Skip rest of entries in region, but account for dribbles. */
00654             for (; i < h->indexUsed && entry->info.offset <= rid+1; i++, entry++) {
00655                 if (entry->info.offset <= rid)
00656                     /*@innercontinue@*/ continue;
00657 
00658                 /* Alignment */
00659                 type = entry->info.type;
00660                 if (typeSizes[type] > 1) {
00661                     unsigned diff;
00662                     diff = typeSizes[type] - (dl % typeSizes[type]);
00663                     if (diff != typeSizes[type]) {
00664                         drlen += diff;
00665                         pad += diff;
00666                         dl += diff;
00667                     }
00668                 }
00669 
00670                 ndribbles++;
00671                 il++;
00672                 drlen += entry->length;
00673                 dl += entry->length;
00674             }
00675             i--;
00676             entry--;
00677             continue;
00678         }
00679 
00680         /* Ignore deleted drips. */
00681         if (entry->data == NULL || entry->length <= 0)
00682             continue;
00683 
00684         /* Alignment */
00685         type = entry->info.type;
00686         if (typeSizes[type] > 1) {
00687             unsigned diff;
00688             diff = typeSizes[type] - (dl % typeSizes[type]);
00689             if (diff != typeSizes[type]) {
00690                 driplen += diff;
00691                 pad += diff;
00692                 dl += diff;
00693             } else
00694                 diff = 0;
00695         }
00696 
00697         ndrips++;
00698         il++;
00699         driplen += entry->length;
00700         dl += entry->length;
00701     }
00702 
00703     /* Sanity checks on header intro. */
00704     if (hdrchkTags(il) || hdrchkData(dl))
00705         goto errxit;
00706 
00707     len = sizeof(il) + sizeof(dl) + (il * sizeof(*pe)) + dl;
00708 
00709     ei = xmalloc(len);
00710     ei[0] = htonl(il);
00711     ei[1] = htonl(dl);
00712 
00713     pe = (entryInfo) &ei[2];
00714     dataStart = te = (unsigned char *) (pe + il);
00715 
00716     pad = 0;
00717     for (i = 0, entry = h->index; i < h->indexUsed; i++, entry++) {
00718         const char * src;
00719         unsigned char *t;
00720         int count;
00721         int rdlen;
00722 
00723         if (entry->data == NULL || entry->length <= 0)
00724             continue;
00725 
00726         t = te;
00727         pe->tag = htonl(entry->info.tag);
00728         pe->type = htonl(entry->info.type);
00729         pe->count = htonl(entry->info.count);
00730 
00731         if (ENTRY_IS_REGION(entry)) {
00732             int_32 rdl = -entry->info.offset;   /* negative offset */
00733             int_32 ril = rdl/sizeof(*pe) + ndribbles;
00734             int rid = entry->info.offset;
00735 
00736             src = (char *)entry->data;
00737             rdlen = entry->rdlen;
00738 
00739             /* XXX Legacy regions do not include the region tag and data. */
00740             if (i == 0 && (h->flags & HEADERFLAG_LEGACY)) {
00741                 int_32 stei[4];
00742 
00743                 legacy = 1;
00744                 memcpy(pe+1, src, rdl);
00745                 memcpy(te, src + rdl, rdlen);
00746                 te += rdlen;
00747 
00748                 pe->offset = htonl(te - dataStart);
00749                 stei[0] = pe->tag;
00750                 stei[1] = pe->type;
00751                 stei[2] = htonl(-rdl-entry->info.count);
00752                 stei[3] = pe->count;
00753                 memcpy(te, stei, entry->info.count);
00754                 te += entry->info.count;
00755                 ril++;
00756                 rdlen += entry->info.count;
00757 
00758                 count = regionSwab(NULL, ril, 0, pe, t, NULL, 0);
00759                 if (count != rdlen)
00760                     goto errxit;
00761 
00762             } else {
00763 
00764                 memcpy(pe+1, src + sizeof(*pe), ((ril-1) * sizeof(*pe)));
00765                 memcpy(te, src + (ril * sizeof(*pe)), rdlen+entry->info.count+drlen);
00766                 te += rdlen;
00767                 {   /*@-castexpose@*/
00768                     entryInfo se = (entryInfo)src;
00769                     /*@=castexpose@*/
00770                     int off = ntohl(se->offset);
00771                     pe->offset = (off) ? htonl(te - dataStart) : htonl(off);
00772                 }
00773                 te += entry->info.count + drlen;
00774 
00775                 count = regionSwab(NULL, ril, 0, pe, t, NULL, 0);
00776                 if (count != (rdlen + entry->info.count + drlen))
00777                     goto errxit;
00778             }
00779 
00780             /* Skip rest of entries in region. */
00781             while (i < h->indexUsed && entry->info.offset <= rid+1) {
00782                 i++;
00783                 entry++;
00784             }
00785             i--;
00786             entry--;
00787             pe += ril;
00788             continue;
00789         }
00790 
00791         /* Ignore deleted drips. */
00792         if (entry->data == NULL || entry->length <= 0)
00793             continue;
00794 
00795         /* Alignment */
00796         type = entry->info.type;
00797         if (typeSizes[type] > 1) {
00798             unsigned diff;
00799             diff = typeSizes[type] - ((te - dataStart) % typeSizes[type]);
00800             if (diff != typeSizes[type]) {
00801                 memset(te, 0, diff);
00802                 te += diff;
00803                 pad += diff;
00804             }
00805         }
00806 
00807         pe->offset = htonl(te - dataStart);
00808 
00809         /* copy data w/ endian conversions */
00810         switch (entry->info.type) {
00811         case RPM_INT64_TYPE:
00812         {   int_32 b[2];
00813             count = entry->info.count;
00814             src = entry->data;
00815             while (count--) {
00816                 b[1] = htonl(((int_32 *)src)[0]);
00817                 b[0] = htonl(((int_32 *)src)[1]);
00818                 if (b[1] == ((int_32 *)src)[0])
00819                     memcpy(te, src, sizeof(b));
00820                 else
00821                     memcpy(te, b, sizeof(b));
00822                 te += sizeof(b);
00823                 src += sizeof(b);
00824             }
00825         }   /*@switchbreak@*/ break;
00826 
00827         case RPM_INT32_TYPE:
00828             count = entry->info.count;
00829             src = entry->data;
00830             while (count--) {
00831                 *((int_32 *)te) = htonl(*((int_32 *)src));
00832                 /*@-sizeoftype@*/
00833                 te += sizeof(int_32);
00834                 src += sizeof(int_32);
00835                 /*@=sizeoftype@*/
00836             }
00837             /*@switchbreak@*/ break;
00838 
00839         case RPM_INT16_TYPE:
00840             count = entry->info.count;
00841             src = entry->data;
00842             while (count--) {
00843                 *((int_16 *)te) = htons(*((int_16 *)src));
00844                 /*@-sizeoftype@*/
00845                 te += sizeof(int_16);
00846                 src += sizeof(int_16);
00847                 /*@=sizeoftype@*/
00848             }
00849             /*@switchbreak@*/ break;
00850 
00851         default:
00852             memcpy(te, entry->data, entry->length);
00853             te += entry->length;
00854             /*@switchbreak@*/ break;
00855         }
00856         pe++;
00857     }
00858    
00859     /* Insure that there are no memcpy underruns/overruns. */
00860     if (((unsigned char *)pe) != dataStart)
00861         goto errxit;
00862     if ((((unsigned char *)ei)+len) != te)
00863         goto errxit;
00864 
00865     if (lenp)
00866         *lenp = len;
00867 
00868     h->flags &= ~HEADERFLAG_SORTED;
00869     headerSort(h);
00870 
00871     if (sw != NULL)     (void) rpmswExit(sw, len);
00872 
00873     return (void *) ei;
00874 
00875 errxit:
00876     if (sw != NULL)     (void) rpmswExit(sw, len);
00877     /*@-usereleased@*/
00878     ei = _free(ei);
00879     /*@=usereleased@*/
00880     return (void *) ei;
00881 }
00882 
00888 static /*@only@*/ /*@null@*/
00889 void * headerUnload(Header h)
00890         /*@modifies h @*/
00891 {
00892     size_t length;
00893     void * uh = doHeaderUnload(h, &length);
00894     return uh;
00895 }
00896 
00904 static /*@null@*/
00905 indexEntry findEntry(/*@null@*/ Header h, int_32 tag, rpmTagType type)
00906         /*@modifies h @*/
00907 {
00908     indexEntry entry, entry2, last;
00909     struct indexEntry_s key;
00910 
00911     if (h == NULL) return NULL;
00912     if (!(h->flags & HEADERFLAG_SORTED)) headerSort(h);
00913 
00914     key.info.tag = tag;
00915 
00916     entry2 = entry = 
00917         bsearch(&key, h->index, h->indexUsed, sizeof(*h->index), indexCmp);
00918     if (entry == NULL)
00919         return NULL;
00920 
00921     if (type == RPM_NULL_TYPE)
00922         return entry;
00923 
00924     /* look backwards */
00925     while (entry->info.tag == tag && entry->info.type != type &&
00926            entry > h->index) entry--;
00927 
00928     if (entry->info.tag == tag && entry->info.type == type)
00929         return entry;
00930 
00931     last = h->index + h->indexUsed;
00932     /*@-usereleased@*/ /* FIX: entry2 = entry. Code looks bogus as well. */
00933     while (entry2->info.tag == tag && entry2->info.type != type &&
00934            entry2 < last) entry2++;
00935     /*@=usereleased@*/
00936 
00937     if (entry->info.tag == tag && entry->info.type == type)
00938         return entry;
00939 
00940     return NULL;
00941 }
00942 
00952 static
00953 int headerRemoveEntry(Header h, int_32 tag)
00954         /*@modifies h @*/
00955 {
00956     indexEntry last = h->index + h->indexUsed;
00957     indexEntry entry, first;
00958     int ne;
00959 
00960     entry = findEntry(h, tag, RPM_NULL_TYPE);
00961     if (!entry) return 1;
00962 
00963     /* Make sure entry points to the first occurence of this tag. */
00964     while (entry > h->index && (entry - 1)->info.tag == tag)  
00965         entry--;
00966 
00967     /* Free data for tags being removed. */
00968     for (first = entry; first < last; first++) {
00969         void * data;
00970         if (first->info.tag != tag)
00971             break;
00972         data = first->data;
00973         first->data = NULL;
00974         first->length = 0;
00975         if (ENTRY_IN_REGION(first))
00976             continue;
00977         data = _free(data);
00978     }
00979 
00980     ne = (first - entry);
00981     if (ne > 0) {
00982         h->indexUsed -= ne;
00983         ne = last - first;
00984         if (ne > 0)
00985             memmove(entry, first, (ne * sizeof(*entry)));
00986     }
00987 
00988     return 0;
00989 }
00990 
00996 static /*@null@*/
00997 Header headerLoad(/*@kept@*/ void * uh)
00998         /*@modifies uh @*/
00999 {
01000     void * sw = NULL;
01001     int_32 * ei = (int_32 *) uh;
01002     int_32 il = ntohl(ei[0]);           /* index length */
01003     int_32 dl = ntohl(ei[1]);           /* data length */
01004     /*@-sizeoftype@*/
01005     size_t pvlen = sizeof(il) + sizeof(dl) +
01006                (il * sizeof(struct entryInfo_s)) + dl;
01007     /*@=sizeoftype@*/
01008     void * pv = uh;
01009     Header h = NULL;
01010     entryInfo pe;
01011     unsigned char * dataStart;
01012     unsigned char * dataEnd;
01013     indexEntry entry; 
01014     int rdlen;
01015     int i;
01016 
01017     /* Sanity checks on header intro. */
01018     if (hdrchkTags(il) || hdrchkData(dl))
01019         goto errxit;
01020 
01021     ei = (int_32 *) pv;
01022     /*@-castexpose@*/
01023     pe = (entryInfo) &ei[2];
01024     /*@=castexpose@*/
01025     dataStart = (unsigned char *) (pe + il);
01026     dataEnd = dataStart + dl;
01027 
01028     h = xcalloc(1, sizeof(*h));
01029     if ((sw = headerGetStats(h, 18)) != NULL)   /* RPMTS_OP_HDRLOAD */
01030         (void) rpmswEnter(sw, 0);
01031     /*@-assignexpose@*/
01032     h->hv = *hdrVec;            /* structure assignment */
01033     /*@=assignexpose@*/
01034     /*@-assignexpose -kepttrans@*/
01035     h->blob = uh;
01036     /*@=assignexpose =kepttrans@*/
01037     h->indexAlloced = il + 1;
01038     h->indexUsed = il;
01039     h->index = xcalloc(h->indexAlloced, sizeof(*h->index));
01040     h->flags |= HEADERFLAG_SORTED;
01041     h->nrefs = 0;
01042     h = headerLink(h);
01043 
01044     entry = h->index;
01045     i = 0;
01046     if (!(htonl(pe->tag) < HEADER_I18NTABLE)) {
01047         h->flags |= HEADERFLAG_LEGACY;
01048         entry->info.type = REGION_TAG_TYPE;
01049         entry->info.tag = HEADER_IMAGE;
01050         /*@-sizeoftype@*/
01051         entry->info.count = REGION_TAG_COUNT;
01052         /*@=sizeoftype@*/
01053         entry->info.offset = ((unsigned char *)pe - dataStart); /* negative offset */
01054 
01055         /*@-assignexpose@*/
01056         entry->data = pe;
01057         /*@=assignexpose@*/
01058         entry->length = pvlen - sizeof(il) - sizeof(dl);
01059         rdlen = regionSwab(entry+1, il, 0, pe, dataStart, dataEnd, entry->info.offset);
01060 #if 0   /* XXX don't check, the 8/98 i18n bug fails here. */
01061         if (rdlen != dl)
01062             goto errxit;
01063 #endif
01064         entry->rdlen = rdlen;
01065         entry++;
01066         h->indexUsed++;
01067     } else {
01068         int_32 rdl;
01069         int_32 ril;
01070 
01071         h->flags &= ~HEADERFLAG_LEGACY;
01072 
01073         entry->info.type = htonl(pe->type);
01074         entry->info.count = htonl(pe->count);
01075 
01076         if (hdrchkType(entry->info.type))
01077             goto errxit;
01078         if (hdrchkTags(entry->info.count))
01079             goto errxit;
01080 
01081         {   int off = ntohl(pe->offset);
01082 
01083             if (hdrchkData(off))
01084                 goto errxit;
01085             if (off) {
01086 /*@-sizeoftype@*/
01087                 size_t nb = REGION_TAG_COUNT;
01088 /*@=sizeoftype@*/
01089                 int_32 * stei = memcpy(alloca(nb), dataStart + off, nb);
01090                 rdl = -ntohl(stei[2]);  /* negative offset */
01091                 ril = rdl/sizeof(*pe);
01092                 if (hdrchkTags(ril) || hdrchkData(rdl))
01093                     goto errxit;
01094                 entry->info.tag = htonl(pe->tag);
01095             } else {
01096                 ril = il;
01097                 /*@-sizeoftype@*/
01098                 rdl = (ril * sizeof(struct entryInfo_s));
01099                 /*@=sizeoftype@*/
01100                 entry->info.tag = HEADER_IMAGE;
01101             }
01102         }
01103         entry->info.offset = -rdl;      /* negative offset */
01104 
01105         /*@-assignexpose@*/
01106         entry->data = pe;
01107         /*@=assignexpose@*/
01108         entry->length = pvlen - sizeof(il) - sizeof(dl);
01109         rdlen = regionSwab(entry+1, ril-1, 0, pe+1, dataStart, dataEnd, entry->info.offset);
01110         if (rdlen < 0)
01111             goto errxit;
01112         entry->rdlen = rdlen;
01113 
01114         if (ril < h->indexUsed) {
01115             indexEntry newEntry = entry + ril;
01116             int ne = (h->indexUsed - ril);
01117             int rid = entry->info.offset+1;
01118             int rc;
01119 
01120             /* Load dribble entries from region. */
01121             rc = regionSwab(newEntry, ne, 0, pe+ril, dataStart, dataEnd, rid);
01122             if (rc < 0)
01123                 goto errxit;
01124             rdlen += rc;
01125 
01126           { indexEntry firstEntry = newEntry;
01127             int save = h->indexUsed;
01128             int j;
01129 
01130             /* Dribble entries replace duplicate region entries. */
01131             h->indexUsed -= ne;
01132             for (j = 0; j < ne; j++, newEntry++) {
01133                 (void) headerRemoveEntry(h, newEntry->info.tag);
01134                 if (newEntry->info.tag == HEADER_BASENAMES)
01135                     (void) headerRemoveEntry(h, HEADER_OLDFILENAMES);
01136             }
01137 
01138             /* If any duplicate entries were replaced, move new entries down. */
01139             if (h->indexUsed < (save - ne)) {
01140                 memmove(h->index + h->indexUsed, firstEntry,
01141                         (ne * sizeof(*entry)));
01142             }
01143             h->indexUsed += ne;
01144           }
01145         }
01146     }
01147 
01148     h->flags &= ~HEADERFLAG_SORTED;
01149     headerSort(h);
01150 
01151     if (sw != NULL)     (void) rpmswExit(sw, pvlen);
01152 
01153     /*@-globstate -observertrans @*/
01154     return h;
01155     /*@=globstate =observertrans @*/
01156 
01157 errxit:
01158     if (sw != NULL)     (void) rpmswExit(sw, pvlen);
01159     /*@-usereleased@*/
01160     if (h) {
01161         h->index = _free(h->index);
01162         /*@-refcounttrans@*/
01163         h = _free(h);
01164         /*@=refcounttrans@*/
01165     }
01166     /*@=usereleased@*/
01167     /*@-refcounttrans -globstate@*/
01168     return h;
01169     /*@=refcounttrans =globstate@*/
01170 }
01171 
01177 static /*@observer@*/ /*@null@*/
01178 const char * headerGetOrigin(/*@null@*/ Header h)
01179         /*@*/
01180 {
01181     return (h != NULL ? h->origin : NULL);
01182 }
01183 
01190 static
01191 int headerSetOrigin(/*@null@*/ Header h, const char * origin)
01192         /*@modifies h @*/
01193 {
01194     if (h != NULL) {
01195         h->origin = _free(h->origin);
01196         h->origin = xstrdup(origin);
01197     }
01198     return 0;
01199 }
01200 
01201 const char * headerGetBaseURL(Header h);        /* XXX keep GCC quiet */
01202 const char * headerGetBaseURL(Header h)
01203 {
01204     return (h != NULL ? h->baseurl : NULL);
01205 }
01206 
01207 int headerSetBaseURL(Header h, const char * baseurl);   /* XXX keep GCC quiet */
01208 int headerSetBaseURL(Header h, const char * baseurl)
01209 {
01210     if (h != NULL) {
01211         h->baseurl = _free(h->baseurl);
01212         h->baseurl = xstrdup(baseurl);
01213     }
01214     return 0;
01215 }
01216 
01217 struct stat * headerGetStatbuf(Header h);       /* XXX keep GCC quiet */
01218 struct stat * headerGetStatbuf(Header h)
01219 {
01220     return &h->sb;
01221 }
01222 
01223 int headerSetStatbuf(Header h, struct stat * st);       /* XXX keep GCC quiet */
01224 int headerSetStatbuf(Header h, struct stat * st)
01225 {
01226     if (h != NULL && st != NULL)
01227         memcpy(&h->sb, st, sizeof(h->sb));
01228     return 0;
01229 }
01230 
01231 const char * headerGetDigest(Header h);         /* XXX keep GCC quiet. */
01232 const char * headerGetDigest(Header h)
01233 {
01234     return (h != NULL ? h->digest : NULL);
01235 }
01236 
01237 int headerSetDigest(Header h, const char * digest);     /* XXX keep GCC quiet */
01238 int headerSetDigest(Header h, const char * digest)
01239 {
01240     if (h != NULL) {
01241         h->digest = _free(h->digest);
01242         h->digest = xstrdup(digest);
01243     }
01244     return 0;
01245 }
01246 
01247 static
01248 uint32_t headerGetInstance(/*@null@*/ Header h)
01249         /*@*/
01250 {
01251     return (h != NULL ? h->instance : 0);
01252 }
01253 
01254 static
01255 uint32_t headerSetInstance(/*@null@*/ Header h, uint32_t instance)
01256         /*@modifies h @*/
01257 {
01258     if (h != NULL)
01259         h->instance = instance;
01260     return 0;
01261 }
01262 
01263 uint32_t headerGetStartOff(Header h);   /* XXX keep GCC quiet */
01264 uint32_t headerGetStartOff(Header h)
01265 {
01266     return (h != NULL ? h->startoff : 0);
01267 }
01268 
01269 uint32_t headerSetStartOff(Header h, uint32_t startoff);        /* XXX keep GCC quiet */
01270 uint32_t headerSetStartOff(Header h, uint32_t startoff)
01271 {
01272     if (h != NULL)
01273         h->startoff = startoff;
01274     return 0;
01275 }
01276 
01277 uint32_t headerGetEndOff(Header h);     /* XXX keep GCC quiet */
01278 uint32_t headerGetEndOff(Header h)
01279 {
01280     return (h != NULL ? h->endoff : 0);
01281 }
01282 
01283 uint32_t headerSetEndOff(Header h, uint32_t endoff);    /* XXX keep GCC quiet. */
01284 uint32_t headerSetEndOff(Header h, uint32_t endoff)
01285 {
01286     if (h != NULL)
01287         h->endoff = endoff;
01288     return 0;
01289 }
01290 
01298 static /*@null@*/
01299 Header headerReload(/*@only@*/ Header h, int tag)
01300         /*@modifies h @*/
01301 {
01302     Header nh;
01303     size_t length;
01304     void * uh;
01305     const char * origin = (h->origin != NULL ? xstrdup(h->origin) : NULL);
01306     const char * baseurl = (h->baseurl != NULL ? xstrdup(h->baseurl) : NULL);
01307     const char * digest = (h->digest != NULL ? xstrdup(h->digest) : NULL);
01308     struct stat sb = h->sb;     /* structure assignment */
01309     int_32 instance = h->instance;
01310     int xx;
01311 
01312 /*@-onlytrans@*/
01313     uh = doHeaderUnload(h, &length);
01314     h = headerFree(h);
01315 /*@=onlytrans@*/
01316     if (uh == NULL)
01317         return NULL;
01318     nh = headerLoad(uh);
01319     if (nh == NULL) {
01320         uh = _free(uh);
01321         return NULL;
01322     }
01323     if (nh->flags & HEADERFLAG_ALLOCATED)
01324         uh = _free(uh);
01325     nh->flags |= HEADERFLAG_ALLOCATED;
01326     if (ENTRY_IS_REGION(nh->index)) {
01327         if (tag == HEADER_SIGNATURES || tag == HEADER_IMMUTABLE)
01328             nh->index[0].info.tag = tag;
01329     }
01330     if (origin != NULL) {
01331         xx = headerSetOrigin(nh, origin);
01332         origin = _free(origin);
01333     }
01334     if (baseurl != NULL) {
01335         xx = headerSetBaseURL(nh, baseurl);
01336         baseurl = _free(baseurl);
01337     }
01338     if (digest != NULL) {
01339         xx = headerSetDigest(nh, digest);
01340         digest = _free(digest);
01341     }
01342     nh->sb = sb;        /* structure assignment */
01343     xx = headerSetInstance(nh, instance);
01344     return nh;
01345 }
01346 
01352 static /*@null@*/
01353 Header headerCopyLoad(const void * uh)
01354         /*@*/
01355 {
01356     int_32 * ei = (int_32 *) uh;
01357     int_32 il = ntohl(ei[0]);           /* index length */
01358     int_32 dl = ntohl(ei[1]);           /* data length */
01359     /*@-sizeoftype@*/
01360     size_t pvlen = sizeof(il) + sizeof(dl) +
01361                         (il * sizeof(struct entryInfo_s)) + dl;
01362     /*@=sizeoftype@*/
01363     void * nuh = NULL;
01364     Header h = NULL;
01365 
01366     /* Sanity checks on header intro. */
01367     if (!(hdrchkTags(il) || hdrchkData(dl)) && pvlen < headerMaxbytes) {
01368         nuh = memcpy(xmalloc(pvlen), uh, pvlen);
01369         if ((h = headerLoad(nuh)) != NULL)
01370             h->flags |= HEADERFLAG_ALLOCATED;
01371     }
01372     if (h == NULL)
01373         nuh = _free(nuh);
01374     return h;
01375 }
01376 
01383 static /*@null@*/
01384 Header headerRead(void * _fd, enum hMagic magicp)
01385         /*@modifies _fd @*/
01386 {
01387     FD_t fd = _fd;
01388     int_32 block[4];
01389     int_32 reserved;
01390     int_32 * ei = NULL;
01391     int_32 il;
01392     int_32 dl;
01393     int_32 magic;
01394     Header h = NULL;
01395     size_t len;
01396     int i;
01397 
01398     memset(block, 0, sizeof(block));
01399     i = 2;
01400     if (magicp == HEADER_MAGIC_YES)
01401         i += 2;
01402 
01403     /*@-type@*/ /* FIX: cast? */
01404     if (timedRead(fd, (char *)block, i*sizeof(*block)) != (i * sizeof(*block)))
01405         goto exit;
01406     /*@=type@*/
01407 
01408     i = 0;
01409 
01410     if (magicp == HEADER_MAGIC_YES) {
01411         magic = block[i++];
01412         if (memcmp(&magic, header_magic, sizeof(magic)))
01413             goto exit;
01414         reserved = block[i++];
01415     }
01416     
01417     il = ntohl(block[i]);       i++;
01418     dl = ntohl(block[i]);       i++;
01419 
01420     /*@-sizeoftype@*/
01421     len = sizeof(il) + sizeof(dl) + (il * sizeof(struct entryInfo_s)) + dl;
01422     /*@=sizeoftype@*/
01423 
01424     /* Sanity checks on header intro. */
01425     if (hdrchkTags(il) || hdrchkData(dl) || len > headerMaxbytes)
01426         goto exit;
01427 
01428     ei = xmalloc(len);
01429     ei[0] = htonl(il);
01430     ei[1] = htonl(dl);
01431     len -= sizeof(il) + sizeof(dl);
01432 
01433     /*@-type@*/ /* FIX: cast? */
01434     if (timedRead(fd, (char *)&ei[2], len) != len)
01435         goto exit;
01436     /*@=type@*/
01437     
01438     h = headerLoad(ei);
01439 
01440     {   const char * origin = fdGetOPath(fd);
01441         if (origin != NULL)
01442             (void) headerSetOrigin(h, origin);
01443     }
01444 
01445 exit:
01446     if (h) {
01447         if (h->flags & HEADERFLAG_ALLOCATED)
01448             ei = _free(ei);
01449         h->flags |= HEADERFLAG_ALLOCATED;
01450     } else if (ei)
01451         ei = _free(ei);
01452     /*@-mustmod@*/      /* FIX: timedRead macro obscures annotation */
01453     return h;
01454     /*@-mustmod@*/
01455 }
01456 
01464 static
01465 int headerWrite(void * _fd, /*@null@*/ Header h, enum hMagic magicp)
01466         /*@globals fileSystem @*/
01467         /*@modifies fd, h, fileSystem @*/
01468 {
01469     FD_t fd = _fd;
01470     ssize_t nb;
01471     size_t length;
01472     const void * uh;
01473 
01474     if (h == NULL)
01475         return 1;
01476     uh = doHeaderUnload(h, &length);
01477     if (uh == NULL)
01478         return 1;
01479     switch (magicp) {
01480     case HEADER_MAGIC_YES:
01481         /*@-sizeoftype@*/
01482         nb = Fwrite(header_magic, sizeof(char), sizeof(header_magic), fd);
01483         /*@=sizeoftype@*/
01484         if (nb != sizeof(header_magic))
01485             goto exit;
01486         break;
01487     case HEADER_MAGIC_NO:
01488         break;
01489     }
01490 
01491     /*@-sizeoftype@*/
01492     nb = Fwrite(uh, sizeof(char), length, fd);
01493     /*@=sizeoftype@*/
01494 
01495 exit:
01496     uh = _free(uh);
01497     return (nb == length ? 0 : 1);
01498 }
01499 
01506 static
01507 int headerIsEntry(/*@null@*/Header h, int_32 tag)
01508         /*@*/
01509 {
01510     /*@-mods@*/         /*@ FIX: h modified by sort. */
01511     return (findEntry(h, tag, RPM_NULL_TYPE) ? 1 : 0);
01512     /*@=mods@*/ 
01513 }
01514 
01525 static int copyEntry(const indexEntry entry,
01526                 /*@null@*/ /*@out@*/ rpmTagType * type,
01527                 /*@null@*/ /*@out@*/ rpmTagData * p,
01528                 /*@null@*/ /*@out@*/ rpmTagCount * c,
01529                 int minMem)
01530         /*@modifies *type, *p, *c @*/
01531         /*@requires maxSet(type) >= 0 /\ maxSet(p) >= 0 /\ maxSet(c) >= 0 @*/
01532 {
01533     rpmTagCount count = entry->info.count;
01534     int rc = 1;         /* XXX 1 on success. */
01535 
01536     if (p)
01537     switch (entry->info.type) {
01538     case RPM_BIN_TYPE:
01539         /*
01540          * XXX This only works for
01541          * XXX  "sealed" HEADER_IMMUTABLE/HEADER_SIGNATURES/HEADER_IMAGE.
01542          * XXX This will *not* work for unsealed legacy HEADER_IMAGE (i.e.
01543          * XXX a legacy header freshly read, but not yet unloaded to the rpmdb).
01544          */
01545         if (ENTRY_IS_REGION(entry)) {
01546             int_32 * ei = ((int_32 *)entry->data) - 2;
01547             /*@-castexpose@*/
01548             entryInfo pe = (entryInfo) (ei + 2);
01549             /*@=castexpose@*/
01550             unsigned char * dataStart = (unsigned char *) (pe + ntohl(ei[0]));
01551             unsigned char * dataEnd;
01552             int_32 rdl = -entry->info.offset;   /* negative offset */
01553             int_32 ril = rdl/sizeof(*pe);
01554 
01555             /*@-sizeoftype@*/
01556             rdl = entry->rdlen;
01557             count = 2 * sizeof(*ei) + (ril * sizeof(*pe)) + rdl;
01558             if (entry->info.tag == HEADER_IMAGE) {
01559                 ril -= 1;
01560                 pe += 1;
01561             } else {
01562                 count += REGION_TAG_COUNT;
01563                 rdl += REGION_TAG_COUNT;
01564             }
01565 
01566             (*p).i32p = ei = xmalloc(count);
01567             ei[0] = htonl(ril);
01568             ei[1] = htonl(rdl);
01569 
01570             /*@-castexpose@*/
01571             pe = (entryInfo) memcpy(ei + 2, pe, (ril * sizeof(*pe)));
01572             /*@=castexpose@*/
01573 
01574             dataStart = (unsigned char *) memcpy(pe + ril, dataStart, rdl);
01575             dataEnd = dataStart + rdl;
01576             /*@=sizeoftype@*/
01577 
01578             rc = regionSwab(NULL, ril, 0, pe, dataStart, dataEnd, 0);
01579             /* XXX 1 on success. */
01580             rc = (rc < 0) ? 0 : 1;
01581         } else {
01582             count = entry->length;
01583             (*p).ptr = (!minMem
01584                 ? memcpy(xmalloc(count), entry->data, count)
01585                 : entry->data);
01586         }
01587         break;
01588     case RPM_STRING_TYPE:
01589         if (count == 1) {
01590             (*p).str = entry->data;
01591             break;
01592         }
01593         /*@fallthrough@*/
01594     case RPM_I18NSTRING_TYPE:
01595     case RPM_STRING_ARRAY_TYPE:
01596     {   const char ** argv;
01597         size_t nb = count * sizeof(*argv);
01598         char * t;
01599         int i;
01600 
01601         /*@-mods@*/
01602         if (minMem) {
01603             (*p).argv = argv = xmalloc(nb);
01604             t = entry->data;
01605         } else {
01606             (*p).argv = argv = xmalloc(nb + entry->length);
01607             t = (char *) &argv[count];
01608             memcpy(t, entry->data, entry->length);
01609         }
01610         /*@=mods@*/
01611         for (i = 0; i < count; i++) {
01612             argv[i] = t;
01613             t = strchr(t, 0);
01614             t++;
01615         }
01616     }   break;
01617 
01618     case RPM_OPENPGP_TYPE:      /* XXX W2DO? */
01619     case RPM_ASN1_TYPE:         /* XXX W2DO? */
01620     default:
01621         (*p).ptr = entry->data;
01622         break;
01623     }
01624     if (type) *type = entry->info.type;
01625     if (c) *c = count;
01626     return rc;
01627 }
01628 
01647 static int headerMatchLocale(const char *td, const char *l, const char *le)
01648         /*@*/
01649 {
01650     const char *fe;
01651 
01652 
01653 #if 0
01654   { const char *s, *ll, *CC, *EE, *dd;
01655     char *lbuf, *t.
01656 
01657     /* Copy the buffer and parse out components on the fly. */
01658     lbuf = alloca(le - l + 1);
01659     for (s = l, ll = t = lbuf; *s; s++, t++) {
01660         switch (*s) {
01661         case '_':
01662             *t = '\0';
01663             CC = t + 1;
01664             break;
01665         case '.':
01666             *t = '\0';
01667             EE = t + 1;
01668             break;
01669         case '@':
01670             *t = '\0';
01671             dd = t + 1;
01672             break;
01673         default:
01674             *t = *s;
01675             break;
01676         }
01677     }
01678 
01679     if (ll)     /* ISO language should be lower case */
01680         for (t = ll; *t; t++)   *t = tolower(*t);
01681     if (CC)     /* ISO country code should be upper case */
01682         for (t = CC; *t; t++)   *t = toupper(*t);
01683 
01684     /* There are a total of 16 cases to attempt to match. */
01685   }
01686 #endif
01687 
01688     /* First try a complete match. */
01689     if (strlen(td) == (le-l) && !strncmp(td, l, (le - l)))
01690         return 1;
01691 
01692     /* Next, try stripping optional dialect and matching.  */
01693     for (fe = l; fe < le && *fe != '@'; fe++)
01694         {};
01695     if (fe < le && !strncmp(td, l, (fe - l)))
01696         return 1;
01697 
01698     /* Next, try stripping optional codeset and matching.  */
01699     for (fe = l; fe < le && *fe != '.'; fe++)
01700         {};
01701     if (fe < le && !strncmp(td, l, (fe - l)))
01702         return 1;
01703 
01704     /* Finally, try stripping optional country code and matching. */
01705     for (fe = l; fe < le && *fe != '_'; fe++)
01706         {};
01707     if (fe < le && !strncmp(td, l, (fe - l)))
01708         return 1;
01709 
01710     return 0;
01711 }
01712 
01719 /*@dependent@*/ /*@exposed@*/ static char *
01720 headerFindI18NString(Header h, indexEntry entry)
01721         /*@*/
01722 {
01723     const char *lang, *l, *le;
01724     indexEntry table;
01725 
01726     /* XXX Drepper sez' this is the order. */
01727     if ((lang = getenv("LANGUAGE")) == NULL &&
01728         (lang = getenv("LC_ALL")) == NULL &&
01729         (lang = getenv("LC_MESSAGES")) == NULL &&
01730         (lang = getenv("LANG")) == NULL)
01731             return entry->data;
01732     
01733     /*@-mods@*/
01734     if ((table = findEntry(h, HEADER_I18NTABLE, RPM_STRING_ARRAY_TYPE)) == NULL)
01735         return entry->data;
01736     /*@=mods@*/
01737 
01738     for (l = lang; *l != '\0'; l = le) {
01739         const char *td;
01740         char *ed;
01741         int langNum;
01742 
01743         while (*l && *l == ':')                 /* skip leading colons */
01744             l++;
01745         if (*l == '\0')
01746             break;
01747         for (le = l; *le && *le != ':'; le++)   /* find end of this locale */
01748             {};
01749 
01750         /* For each entry in the header ... */
01751         for (langNum = 0, td = table->data, ed = entry->data;
01752              langNum < entry->info.count;
01753              langNum++, td += strlen(td) + 1, ed += strlen(ed) + 1) {
01754 
01755                 if (headerMatchLocale(td, l, le))
01756                     return ed;
01757 
01758         }
01759     }
01760 
01761 /* when everything fail, try gettext */
01762     return ((entry->data != NULL) && *(char*)(entry->data)) ? _(entry->data) : entry->data;
01763 }
01764 
01775 static int intGetEntry(Header h, int_32 tag,
01776                 /*@null@*/ /*@out@*/ rpmTagType * type,
01777                 /*@null@*/ /*@out@*/ rpmTagData * p,
01778                 /*@null@*/ /*@out@*/ rpmTagCount * c,
01779                 int minMem)
01780         /*@modifies *type, *p, *c @*/
01781         /*@requires maxSet(type) >= 0 /\ maxSet(p) >= 0 /\ maxSet(c) >= 0 @*/
01782 {
01783     indexEntry entry;
01784     int rc;
01785 
01786     /* First find the tag */
01787 /*@-mods@*/             /*@ FIX: h modified by sort. */
01788     entry = findEntry(h, tag, RPM_NULL_TYPE);
01789 /*@=mods@*/
01790     if (entry == NULL) {
01791         if (type) type = 0;
01792         if (p) (*p).ptr = NULL;
01793         if (c) *c = 0;
01794         return 0;
01795     }
01796 
01797     switch (entry->info.type) {
01798     case RPM_I18NSTRING_TYPE:
01799         rc = 1;
01800         if (type) *type = RPM_STRING_TYPE;
01801         if (c) *c = 1;
01802         /*@-dependenttrans@*/
01803         if (p) (*p).str = headerFindI18NString(h, entry);
01804         /*@=dependenttrans@*/
01805         break;
01806     default:
01807         rc = copyEntry(entry, type, p, c, minMem);
01808         break;
01809     }
01810 
01811     /* XXX 1 on success */
01812     return ((rc == 1) ? 1 : 0);
01813 }
01814 
01822 static /*@null@*/ void * headerFreeTag(/*@unused@*/ Header h,
01823                 /*@only@*/ /*@null@*/ const void * data, rpmTagType type)
01824         /*@modifies data @*/
01825 {
01826     if (data) {
01827         if (type == -1 ||
01828             type == RPM_STRING_ARRAY_TYPE ||
01829             type == RPM_I18NSTRING_TYPE ||
01830             type == RPM_BIN_TYPE ||
01831             type == RPM_ASN1_TYPE ||
01832             type == RPM_OPENPGP_TYPE)
01833                 data = _free(data);
01834     }
01835     return NULL;
01836 }
01837 
01851 static
01852 int headerGetEntry(Header h, int_32 tag,
01853                         /*@null@*/ /*@out@*/ hTYP_t type,
01854                         /*@null@*/ /*@out@*/ void * p,
01855                         /*@null@*/ /*@out@*/ hCNT_t c)
01856         /*@modifies *type, *p, *c @*/
01857         /*@requires maxSet(type) >= 0 /\ maxSet(p) >= 0 /\ maxSet(c) >= 0 @*/
01858 {
01859     void * sw;
01860     int rc;
01861 
01862     if ((sw = headerGetStats(h, 19)) != NULL)   /* RPMTS_OP_HDRGET */
01863         (void) rpmswEnter(sw, 0);
01864     rc = intGetEntry(h, tag, (rpmTagType *)type, (rpmTagData *)p, (rpmTagCount *)c, 0);
01865     if (sw != NULL)     (void) rpmswExit(sw, 0);
01866     return rc;
01867 }
01868 
01881 static
01882 int headerGetEntryMinMemory(Header h, int_32 tag,
01883                         /*@null@*/ /*@out@*/ hTYP_t type,
01884                         /*@null@*/ /*@out@*/ void * p,
01885                         /*@null@*/ /*@out@*/ hCNT_t c)
01886         /*@modifies *type, *p, *c @*/
01887         /*@requires maxSet(type) >= 0 /\ maxSet(p) >= 0 /\ maxSet(c) >= 0 @*/
01888 {
01889     void * sw;
01890     int rc;
01891 
01892     if ((sw = headerGetStats(h, 19)) != NULL)   /* RPMTS_OP_HDRGET */
01893         (void) rpmswEnter(sw, 0);
01894     rc = intGetEntry(h, tag, (rpmTagType *)type, (rpmTagData *)p, (rpmTagCount *)c, 1);
01895     if (sw != NULL)     (void) rpmswExit(sw, 0);
01896     return rc;
01897 }
01898 
01899 int headerGetRawEntry(Header h, int_32 tag, rpmTagType * type, void * p, rpmTagCount * c)
01900 {
01901     indexEntry entry;
01902     int rc;
01903 
01904     if (p == NULL) return headerIsEntry(h, tag);
01905 
01906     /* First find the tag */
01907     /*@-mods@*/         /*@ FIX: h modified by sort. */
01908     entry = findEntry(h, tag, RPM_NULL_TYPE);
01909     /*@=mods@*/
01910     if (!entry) {
01911         if (p) *(void **)p = NULL;
01912         if (c) *c = 0;
01913         return 0;
01914     }
01915 
01916     rc = copyEntry(entry, type, p, c, 0);
01917 
01918     /* XXX 1 on success */
01919     return ((rc == 1) ? 1 : 0);
01920 }
01921 
01924 static void copyData(rpmTagType type, rpmTagData * dest, rpmTagData * src,
01925                 rpmTagCount cnt, size_t len)
01926         /*@modifies *dest @*/
01927 {
01928     switch (type) {
01929     case RPM_I18NSTRING_TYPE:
01930     case RPM_STRING_ARRAY_TYPE:
01931     {   const char ** av = (*src).argv;
01932         char * t = (char *) (*dest).str;
01933 
01934         while (cnt-- > 0 && len > 0) {
01935             const char * s;
01936             if ((s = *av++) == NULL)
01937                 continue;
01938             do {
01939                 *t++ = *s++;
01940             } while (s[-1] && --len > 0);
01941         }
01942     }   break;
01943     default:
01944         memmove((*dest).ptr, (*src).ptr, len);
01945         break;
01946     }
01947 }
01948 
01957 /*@null@*/
01958 static void *
01959 grabData(rpmTagType type, rpmTagData * p, rpmTagCount c, /*@out@*/ int * lenp)
01960         /*@modifies *lenp @*/
01961         /*@requires maxSet(lengthPtr) >= 0 @*/
01962 {
01963     rpmTagData data = { .ptr = NULL };
01964     int length;
01965 
01966     length = dataLength(type, p, c, 0, NULL);
01967     if (length > 0) {
01968         data.ptr = xmalloc(length);
01969         copyData(type, &data, p, c, length);
01970     }
01971 
01972     if (lenp)
01973         *lenp = length;
01974     return data.ptr;
01975 }
01976 
01991 static
01992 int headerAddEntry(Header h, int_32 tag, int_32 type, const void * p, int_32 c)
01993         /*@modifies h @*/
01994 {
01995     indexEntry entry;
01996     rpmTagData q = { .ptr = (void *) p };
01997     rpmTagData data;
01998     int length;
01999 
02000     /* Count must always be >= 1 for headerAddEntry. */
02001     if (c <= 0)
02002         return 0;
02003 
02004     if (hdrchkType(type))
02005         return 0;
02006     if (hdrchkData(c))
02007         return 0;
02008 
02009     length = 0;
02010     data.ptr = grabData(type, &q, c, &length);
02011     if (data.ptr == NULL || length <= 0)
02012         return 0;
02013 
02014     /* Allocate more index space if necessary */
02015     if (h->indexUsed == h->indexAlloced) {
02016         h->indexAlloced += INDEX_MALLOC_SIZE;
02017         h->index = xrealloc(h->index, h->indexAlloced * sizeof(*h->index));
02018     }
02019 
02020     /* Fill in the index */
02021     entry = h->index + h->indexUsed;
02022     entry->info.tag = tag;
02023     entry->info.type = type;
02024     entry->info.count = c;
02025     entry->info.offset = 0;
02026     entry->data = data.ptr;
02027     entry->length = length;
02028 
02029     if (h->indexUsed > 0 && tag < h->index[h->indexUsed-1].info.tag)
02030         h->flags &= ~HEADERFLAG_SORTED;
02031     h->indexUsed++;
02032 
02033     return 1;
02034 }
02035 
02050 static
02051 int headerAppendEntry(Header h, int_32 tag, int_32 type,
02052                 const void * p, int_32 c)
02053         /*@modifies h @*/
02054 {
02055     rpmTagData src = { .ptr = (void *) p };
02056     rpmTagData dest = { .ptr = NULL };
02057     indexEntry entry;
02058     int length;
02059 
02060     if (type == RPM_STRING_TYPE || type == RPM_I18NSTRING_TYPE) {
02061         /* we can't do this */
02062         return 0;
02063     }
02064 
02065     /* Find the tag entry in the header. */
02066     entry = findEntry(h, tag, type);
02067     if (!entry)
02068         return 0;
02069 
02070     length = dataLength(type, &src, c, 0, NULL);
02071     if (length < 0)
02072         return 0;
02073 
02074     if (ENTRY_IN_REGION(entry)) {
02075         char * t = xmalloc(entry->length + length);
02076         memcpy(t, entry->data, entry->length);
02077         entry->data = t;
02078         entry->info.offset = 0;
02079     } else
02080         entry->data = xrealloc(entry->data, entry->length + length);
02081 
02082     dest.ptr = ((char *) entry->data) + entry->length;
02083     copyData(type, &dest, &src, c, length);
02084 
02085     entry->length += length;
02086 
02087     entry->info.count += c;
02088 
02089     return 1;
02090 }
02091 
02101 static
02102 int headerAddOrAppendEntry(Header h, int_32 tag, int_32 type,
02103                 const void * p, int_32 c)
02104         /*@modifies h @*/
02105 {
02106     return (findEntry(h, tag, type)
02107         ? headerAppendEntry(h, tag, type, p, c)
02108         : headerAddEntry(h, tag, type, p, c));
02109 }
02110 
02131 static
02132 int headerAddI18NString(Header h, int_32 tag, const char * string,
02133                 const char * lang)
02134         /*@modifies h @*/
02135 {
02136     indexEntry table, entry;
02137     rpmTagData p;
02138     int length;
02139     int ghosts;
02140     int i, langNum;
02141     char * buf;
02142 
02143     table = findEntry(h, HEADER_I18NTABLE, RPM_STRING_ARRAY_TYPE);
02144     entry = findEntry(h, tag, RPM_I18NSTRING_TYPE);
02145 
02146     if (!table && entry)
02147         return 0;               /* this shouldn't ever happen!! */
02148 
02149     if (!table && !entry) {
02150         const char * argv[2];
02151         int count = 0;
02152         p.argv = argv;
02153         if (!lang || (lang[0] == 'C' && lang[1] == '\0')) {
02154             /*@-observertrans -readonlytrans@*/
02155             p.argv[count++] = "C";
02156             /*@=observertrans =readonlytrans@*/
02157         } else {
02158             /*@-observertrans -readonlytrans@*/
02159             p.argv[count++] = "C";
02160             /*@=observertrans =readonlytrans@*/
02161             p.argv[count++] = lang;
02162         }
02163         if (!headerAddEntry(h, HEADER_I18NTABLE, RPM_STRING_ARRAY_TYPE, 
02164                         p.ptr, count))
02165             return 0;
02166         table = findEntry(h, HEADER_I18NTABLE, RPM_STRING_ARRAY_TYPE);
02167     }
02168 
02169     if (!table)
02170         return 0;
02171     if (!lang) lang = "C";
02172 
02173     {   const char * l = table->data;
02174         for (langNum = 0; langNum < table->info.count; langNum++) {
02175             if (!strcmp(l, lang)) break;
02176             l += strlen(l) + 1;
02177         }
02178     }
02179 
02180     if (langNum >= table->info.count) {
02181         length = strlen(lang) + 1;
02182         if (ENTRY_IN_REGION(table)) {
02183             char * t = xmalloc(table->length + length);
02184             memcpy(t, table->data, table->length);
02185             table->data = t;
02186             table->info.offset = 0;
02187         } else
02188             table->data = xrealloc(table->data, table->length + length);
02189         memmove(((char *)table->data) + table->length, lang, length);
02190         table->length += length;
02191         table->info.count++;
02192     }
02193 
02194     if (!entry) {
02195         p.argv = alloca(sizeof(*p.argv) * (langNum + 1));
02196         for (i = 0; i < langNum; i++)
02197             p.argv[i] = "";
02198         p.argv[langNum] = string;
02199         return headerAddEntry(h, tag, RPM_I18NSTRING_TYPE, p.ptr, langNum + 1);
02200     } else if (langNum >= entry->info.count) {
02201         ghosts = langNum - entry->info.count;
02202         
02203         length = strlen(string) + 1 + ghosts;
02204         if (ENTRY_IN_REGION(entry)) {
02205             char * t = xmalloc(entry->length + length);
02206             memcpy(t, entry->data, entry->length);
02207             entry->data = t;
02208             entry->info.offset = 0;
02209         } else
02210             entry->data = xrealloc(entry->data, entry->length + length);
02211 
02212         memset(((char *)entry->data) + entry->length, '\0', ghosts);
02213         memmove(((char *)entry->data) + entry->length + ghosts, string, strlen(string)+1);
02214 
02215         entry->length += length;
02216         entry->info.count = langNum + 1;
02217     } else {
02218         char *b, *be, *e, *ee, *t;
02219         size_t bn, sn, en;
02220 
02221         /* Set beginning/end pointers to previous data */
02222         b = be = e = ee = entry->data;
02223         for (i = 0; i < table->info.count; i++) {
02224             if (i == langNum)
02225                 be = ee;
02226             ee += strlen(ee) + 1;
02227             if (i == langNum)
02228                 e  = ee;
02229         }
02230 
02231         /* Get storage for new buffer */
02232         bn = (be-b);
02233         sn = strlen(string) + 1;
02234         en = (ee-e);
02235         length = bn + sn + en;
02236         t = buf = xmalloc(length);
02237 
02238         /* Copy values into new storage */
02239         memcpy(t, b, bn);
02240         t += bn;
02241 /*@-mayaliasunique@*/
02242         memcpy(t, string, sn);
02243         t += sn;
02244         memcpy(t, e, en);
02245         t += en;
02246 /*@=mayaliasunique@*/
02247 
02248         /* Replace i18N string array */
02249         entry->length -= strlen(be) + 1;
02250         entry->length += sn;
02251         
02252         if (ENTRY_IN_REGION(entry)) {
02253             entry->info.offset = 0;
02254         } else
02255             entry->data = _free(entry->data);
02256         /*@-dependenttrans@*/
02257         entry->data = buf;
02258         /*@=dependenttrans@*/
02259     }
02260 
02261     return 0;
02262 }
02263 
02274 static
02275 int headerModifyEntry(Header h, int_32 tag, int_32 type,
02276                         const void * p, int_32 c)
02277         /*@modifies h @*/
02278 {
02279     indexEntry entry;
02280     rpmTagData q = { .ptr = (void *) p };
02281     rpmTagData oldData;
02282     rpmTagData newData;
02283     int length;
02284 
02285     /* First find the tag */
02286     entry = findEntry(h, tag, type);
02287     if (!entry)
02288         return 0;
02289 
02290     length = 0;
02291     newData.ptr = grabData(type, &q, c, &length);
02292     if (newData.ptr == NULL || length <= 0)
02293         return 0;
02294 
02295     /* make sure entry points to the first occurence of this tag */
02296     while (entry > h->index && (entry - 1)->info.tag == tag)  
02297         entry--;
02298 
02299     /* free after we've grabbed the new data in case the two are intertwined;
02300        that's a bad idea but at least we won't break */
02301     oldData.ptr = entry->data;
02302 
02303     entry->info.count = c;
02304     entry->info.type = type;
02305     entry->data = newData.ptr;
02306     entry->length = length;
02307 
02308     if (ENTRY_IN_REGION(entry)) {
02309         entry->info.offset = 0;
02310     } else
02311         oldData.ptr = _free(oldData.ptr);
02312 
02313     return 1;
02314 }
02315 
02318 static char escapedChar(const char ch)  /*@*/
02319 {
02320 /*@-modfilesys@*/
02321 if (_hdr_debug)
02322 fprintf(stderr, "\t\t\\%c\n", ch);
02323 /*@=modfilesys@*/
02324     switch (ch) {
02325     case 'a':   return '\a';
02326     case 'b':   return '\b';
02327     case 'f':   return '\f';
02328     case 'n':   return '\n';
02329     case 'r':   return '\r';
02330     case 't':   return '\t';
02331     case 'v':   return '\v';
02332     default:    return ch;
02333     }
02334 }
02335 
02340 static HE_t rpmheMark(/*@null@*/ HE_t he)
02341         /*@modifies he @*/
02342 {
02343     /* Set he->freeData as appropriate for headerGetEntry() . */
02344     if (he)
02345     switch (he->t) {
02346     default:
02347         he->freeData = 0;
02348         break;
02349     case RPM_I18NSTRING_TYPE:
02350     case RPM_STRING_ARRAY_TYPE:
02351     case RPM_BIN_TYPE:
02352         he->freeData = 1;
02353         break;
02354     }
02355     return he;
02356 }
02357 
02362 static HE_t rpmheClean(/*@null@*/ HE_t he)
02363         /*@modifies he @*/
02364 {
02365     if (he) {
02366         if (he->freeData && he->p.ptr != NULL)
02367             he->p.ptr = _free(he->p.ptr);
02368         memset(he, 0, sizeof(*he));
02369     }
02370     return he;
02371 }
02372 
02379 static /*@null@*/ sprintfToken
02380 freeFormat( /*@only@*/ /*@null@*/ sprintfToken format, int num)
02381         /*@modifies *format @*/
02382 {
02383     int i;
02384 
02385     if (format == NULL) return NULL;
02386 
02387     for (i = 0; i < num; i++) {
02388         switch (format[i].type) {
02389         case PTOK_TAG:
02390             if (_tagcache)
02391                 (void) rpmheClean(&format[i].u.tag.he);
02392             format[i].u.tag.av = argvFree(format[i].u.tag.av);
02393             format[i].u.tag.params = argvFree(format[i].u.tag.params);
02394             format[i].u.tag.fmtfuncs = _free(format[i].u.tag.fmtfuncs);
02395             /*@switchbreak@*/ break;
02396         case PTOK_ARRAY:
02397             format[i].u.array.format =
02398                 freeFormat(format[i].u.array.format,
02399                         format[i].u.array.numTokens);
02400             /*@switchbreak@*/ break;
02401         case PTOK_COND:
02402             format[i].u.cond.ifFormat =
02403                 freeFormat(format[i].u.cond.ifFormat, 
02404                         format[i].u.cond.numIfTokens);
02405             format[i].u.cond.elseFormat =
02406                 freeFormat(format[i].u.cond.elseFormat, 
02407                         format[i].u.cond.numElseTokens);
02408             if (_tagcache)
02409                 (void) rpmheClean(&format[i].u.cond.tag.he);
02410             format[i].u.cond.tag.av = argvFree(format[i].u.cond.tag.av);
02411             format[i].u.cond.tag.params = argvFree(format[i].u.cond.tag.params);
02412             format[i].u.cond.tag.fmtfuncs = _free(format[i].u.cond.tag.fmtfuncs);
02413             /*@switchbreak@*/ break;
02414         case PTOK_NONE:
02415         case PTOK_STRING:
02416         default:
02417             /*@switchbreak@*/ break;
02418         }
02419     }
02420     format = _free(format);
02421     return NULL;
02422 }
02423 
02427 struct headerIterator_s {
02428 /*@unused@*/
02429     Header h;           
02430 /*@unused@*/
02431     int next_index;     
02432 };
02433 
02439 static /*@null@*/
02440 HeaderIterator headerFreeIterator(/*@only@*/ HeaderIterator hi)
02441         /*@modifies hi @*/
02442 {
02443     if (hi != NULL) {
02444         hi->h = headerFree(hi->h);
02445         hi = _free(hi);
02446     }
02447     return hi;
02448 }
02449 
02455 static
02456 HeaderIterator headerInitIterator(Header h)
02457         /*@modifies h */
02458 {
02459     HeaderIterator hi = xmalloc(sizeof(*hi));
02460 
02461     headerSort(h);
02462 
02463     hi->h = headerLink(h);
02464     hi->next_index = 0;
02465     return hi;
02466 }
02467 
02477 static
02478 int headerNextIterator(HeaderIterator hi,
02479                 /*@null@*/ /*@out@*/ hTAG_t tag,
02480                 /*@null@*/ /*@out@*/ hTYP_t type,
02481                 /*@null@*/ /*@out@*/ hPTR_t * p,
02482                 /*@null@*/ /*@out@*/ hCNT_t c)
02483         /*@modifies hi, *tag, *type, *p, *c @*/
02484         /*@requires maxSet(tag) >= 0 /\ maxSet(type) >= 0
02485                 /\ maxSet(p) >= 0 /\ maxSet(c) >= 0 @*/
02486 {
02487     void * sw;
02488     Header h = hi->h;
02489     int slot = hi->next_index;
02490     indexEntry entry = NULL;
02491     int rc;
02492 
02493     for (slot = hi->next_index; slot < h->indexUsed; slot++) {
02494         entry = h->index + slot;
02495         if (!ENTRY_IS_REGION(entry))
02496             break;
02497     }
02498     hi->next_index = slot;
02499     if (entry == NULL || slot >= h->indexUsed)
02500         return 0;
02501 
02502     /*@-noeffect@*/     /* LCL: no clue */
02503     hi->next_index++;
02504     /*@=noeffect@*/
02505 
02506     if ((sw = headerGetStats(h, 19)) != NULL)   /* RPMTS_OP_HDRGET */
02507         (void) rpmswEnter(sw, 0);
02508 
02509     if (tag)
02510         *tag = entry->info.tag;
02511 
02512     rc = copyEntry(entry, (rpmTagType *)type, (rpmTagData *)p, (rpmTagCount *)c, 0);
02513 
02514     if (sw != NULL)     (void) rpmswExit(sw, 0);
02515 
02516     /* XXX 1 on success */
02517     return ((rc == 1) ? 1 : 0);
02518 }
02519 
02525 static /*@null@*/
02526 Header headerCopy(Header h)
02527         /*@modifies h @*/
02528 {
02529     HE_t he = memset(alloca(sizeof(*he)), 0, sizeof(*he));
02530     Header nh = headerNew();
02531     HeaderIterator hi;
02532    
02533     for (hi = headerInitIterator(h);
02534         headerNextIterator(hi, &he->tag, (hTYP_t)&he->t, (hPTR_t *)&he->p, &he->c);
02535         he->p.ptr = headerFreeData(he->p.ptr, he->t))
02536     {
02537         if (he->p.ptr) (void) headerAddEntry(nh, he->tag, he->t, he->p.ptr, he->c);
02538     }
02539     hi = headerFreeIterator(hi);
02540 
02541     return headerReload(nh, HEADER_IMAGE);
02542 }
02543 
02546 typedef struct headerSprintfArgs_s {
02547     Header h;
02548     char * fmt;
02549 /*@temp@*/
02550     headerTagTableEntry tags;
02551 /*@temp@*/
02552     headerSprintfExtension exts;
02553 /*@observer@*/ /*@null@*/
02554     const char * errmsg;
02555     HE_t ec;                    
02556     int nec;                    
02557     sprintfToken format;
02558 /*@relnull@*/
02559     HeaderIterator hi;
02560 /*@owned@*/
02561     char * val;
02562     size_t vallen;
02563     size_t alloced;
02564     int numTokens;
02565     int i;
02566 } * headerSprintfArgs;
02567 
02573 static headerSprintfArgs hsaInit(/*@returned@*/ headerSprintfArgs hsa)
02574         /*@modifies hsa */
02575 {
02576     sprintfTag tag =
02577         (hsa->format->type == PTOK_TAG
02578             ? &hsa->format->u.tag :
02579         (hsa->format->type == PTOK_ARRAY
02580             ? &hsa->format->u.array.format->u.tag :
02581         NULL));
02582 
02583     if (hsa != NULL) {
02584         hsa->i = 0;
02585         if (tag != NULL && tag->tagno == -2)
02586             hsa->hi = headerInitIterator(hsa->h);
02587     }
02588 /*@-nullret@*/
02589     return hsa;
02590 /*@=nullret@*/
02591 }
02592 
02598 /*@null@*/
02599 static sprintfToken hsaNext(/*@returned@*/ headerSprintfArgs hsa)
02600         /*@modifies hsa */
02601 {
02602     sprintfToken fmt = NULL;
02603     sprintfTag tag =
02604         (hsa->format->type == PTOK_TAG
02605             ? &hsa->format->u.tag :
02606         (hsa->format->type == PTOK_ARRAY
02607             ? &hsa->format->u.array.format->u.tag :
02608         NULL));
02609 
02610     if (hsa != NULL && hsa->i >= 0 && hsa->i < hsa->numTokens) {
02611         fmt = hsa->format + hsa->i;
02612         if (hsa->hi == NULL) {
02613             hsa->i++;
02614         } else {
02615             HE_t he = rpmheClean(&tag->he);
02616             if (!headerNextIterator(hsa->hi, &he->tag, (hTAG_t)&he->t, (hPTR_t *)&he->p.ptr, &he->c))
02617             {
02618                 tag->tagno = 0;
02619                 return NULL;
02620             }
02621             he = rpmheMark(he);
02622             he->avail = 1;
02623             tag->tagno = he->tag;
02624         }
02625     }
02626 
02627 /*@-dependenttrans -onlytrans@*/
02628     return fmt;
02629 /*@=dependenttrans =onlytrans@*/
02630 }
02631 
02637 static headerSprintfArgs hsaFini(/*@returned@*/ headerSprintfArgs hsa)
02638         /*@modifies hsa */
02639 {
02640     if (hsa != NULL) {
02641         hsa->hi = headerFreeIterator(hsa->hi);
02642         hsa->i = 0;
02643     }
02644 /*@-nullret@*/
02645     return hsa;
02646 /*@=nullret@*/
02647 }
02648 
02655 /*@dependent@*/ /*@exposed@*/
02656 static char * hsaReserve(headerSprintfArgs hsa, size_t need)
02657         /*@modifies hsa */
02658 {
02659     if ((hsa->vallen + need) >= hsa->alloced) {
02660         if (hsa->alloced <= need)
02661             hsa->alloced += need;
02662         hsa->alloced <<= 1;
02663         hsa->val = xrealloc(hsa->val, hsa->alloced+1);  
02664     }
02665     return hsa->val + hsa->vallen;
02666 }
02667 
02676 /*@observer@*/ /*@null@*/
02677 static const char * myTagName(headerTagTableEntry tbl, int val,
02678                 /*@null@*/ int *typep)
02679         /*@modifies *typep @*/
02680 {
02681     static char name[128];
02682     const char * s;
02683     char *t;
02684 
02685     for (; tbl->name != NULL; tbl++) {
02686         if (tbl->val == val)
02687             break;
02688     }
02689     if ((s = tbl->name) == NULL)
02690         return NULL;
02691     s += sizeof("RPMTAG_") - 1;
02692     t = name;
02693     *t++ = *s++;
02694     while (*s != '\0')
02695         *t++ = xtolower(*s++);
02696     *t = '\0';
02697     if (typep)
02698         *typep = tbl->type;
02699     return name;
02700 }
02701 
02702 /*@observer@*/ /*@null@*/
02703 static int myTagType(headerTagTableEntry tbl, int val)
02704 {
02705     for (; tbl->name != NULL; tbl++) {
02706         if (tbl->val == val)
02707             break;
02708     }
02709     return (tbl->name != NULL ? tbl->type : 0);
02710 }
02711 
02719 static int myTagValue(headerTagTableEntry tbl, const char * name)
02720         /*@*/
02721 {
02722     for (; tbl->name != NULL; tbl++) {
02723         if (!xstrcasecmp(tbl->name, name))
02724             return tbl->val;
02725     }
02726     return 0;
02727 }
02728 
02736 static int findTag(headerSprintfArgs hsa, sprintfToken token, const char * name)
02737         /*@modifies token @*/
02738 {
02739     headerSprintfExtension exts = hsa->exts;
02740     headerSprintfExtension ext;
02741     sprintfTag stag = (token->type == PTOK_COND
02742         ? &token->u.cond.tag : &token->u.tag);
02743     int extNum;
02744 
02745     stag->fmtfuncs = NULL;
02746     stag->ext = NULL;
02747     stag->extNum = 0;
02748     stag->tagno = -1;
02749 
02750     if (!strcmp(name, "*")) {
02751         stag->tagno = -2;
02752         goto bingo;
02753     }
02754 
02755     if (strncmp("RPMTAG_", name, sizeof("RPMTAG_")-1)) {
02756         char * t = alloca(strlen(name) + sizeof("RPMTAG_"));
02757         (void) stpcpy( stpcpy(t, "RPMTAG_"), name);
02758         name = t;
02759     }
02760 
02761     /* Search extensions for specific tag override. */
02762     for (ext = exts, extNum = 0; ext != NULL && ext->type != HEADER_EXT_LAST;
02763         ext = (ext->type == HEADER_EXT_MORE ? ext->u.more : ext+1), extNum++)
02764     {
02765         if (ext->name == NULL || ext->type != HEADER_EXT_TAG)
02766             continue;
02767         if (!xstrcasecmp(ext->name, name)) {
02768             stag->ext = ext->u.tagFunction;
02769             stag->extNum = extNum;
02770             goto bingo;
02771         }
02772     }
02773 
02774     /* Search tag names. */
02775     stag->tagno = myTagValue(hsa->tags, name);
02776     if (stag->tagno != 0)
02777         goto bingo;
02778 
02779     return 1;
02780 
02781 bingo:
02782     /* Search extensions for specific format. */
02783     if (stag->av != NULL) {
02784         int i;
02785         stag->fmtfuncs = xcalloc(argvCount(stag->av) + 1, sizeof(*stag->fmtfuncs));
02786         for (i = 0; stag->av[i] != NULL; i++) {
02787             for (ext = exts; ext != NULL && ext->type != HEADER_EXT_LAST;
02788                     ext = (ext->type == HEADER_EXT_MORE ? ext->u.more : ext+1))
02789             {
02790                 if (ext->name == NULL || ext->type != HEADER_EXT_FORMAT)
02791                     continue;
02792                 if (strcmp(ext->name, stag->av[i]+1))
02793                     continue;
02794                 stag->fmtfuncs[i] = ext->u.fmtFunction;
02795                 break;
02796             }
02797         }
02798     }
02799     return 0;
02800 }
02801 
02809 char * intFormat(HE_t he, /*@null@*/ const char ** av, const char * fmt)
02810 {
02811     int ix = (he->ix > 0 ? he->ix : 0);
02812     int_64 ival = 0;
02813     const char * istr = NULL;
02814     char * b;
02815     size_t nb = 0;
02816 
02817     if (fmt == NULL || *fmt == '\0')
02818         fmt = "d";
02819 
02820     switch (he->t) {
02821     default:
02822         return xstrdup(_("(not a number)"));
02823         break;
02824     case RPM_CHAR_TYPE: 
02825     case RPM_INT8_TYPE:
02826         ival = he->p.i8p[ix];
02827         break;
02828     case RPM_INT16_TYPE:
02829         ival = he->p.ui16p[ix]; /* XXX note unsigned. */
02830         break;
02831     case RPM_INT32_TYPE:
02832         ival = he->p.i32p[ix];
02833         break;
02834     case RPM_INT64_TYPE:
02835         ival = he->p.i64p[ix];
02836         break;
02837     case RPM_STRING_TYPE:
02838         istr = he->p.str;
02839         break;
02840     case RPM_STRING_ARRAY_TYPE:
02841         istr = he->p.argv[ix];
02842         break;
02843     case RPM_OPENPGP_TYPE:      /* XXX W2DO? */
02844     case RPM_ASN1_TYPE:         /* XXX W2DO? */
02845     case RPM_BIN_TYPE:
02846         {   static char hex[] = "0123456789abcdef";
02847             const char * s = he->p.str;
02848             int c = he->c;
02849             char * t;
02850 
02851             nb = 2 * c + 1;
02852             t = b = alloca(nb+1);
02853             while (c-- > 0) {
02854                 unsigned int i;
02855                 i = *s++;
02856                 *t++ = hex[ (i >> 4) & 0xf ];
02857                 *t++ = hex[ (i     ) & 0xf ];
02858             }
02859             *t = '\0';
02860         }   break;
02861     }
02862 
02863     if (istr) {         /* string */
02864         b = (char *)istr;       /* NOCAST */
02865     } else
02866     if (nb == 0) {      /* number */
02867         char myfmt[] = "%llX";
02868         myfmt[3] = *fmt;
02869         nb = 64;
02870         b = alloca(nb);
02871         snprintf(b, nb, myfmt, ival);
02872         b[nb-1] = '\0';
02873     }
02874 
02875     return xstrdup(b);
02876 }
02877 
02884 static char * octFormat(HE_t he, /*@null@*/ const char ** av)
02885         /*@*/
02886 {
02887     return intFormat(he, NULL, "o");
02888 }
02889 
02896 static char * hexFormat(HE_t he, /*@null@*/ const char ** av)
02897         /*@*/
02898 {
02899     return intFormat(he, NULL, "x");
02900 }
02901 
02908 static char * decFormat(HE_t he, /*@null@*/ const char ** av)
02909         /*@*/
02910 {
02911     return intFormat(he, NULL, "d");
02912 }
02913 
02921 static char * realDateFormat(HE_t he, /*@null@*/ const char ** av, const char * strftimeFormat)
02922         /*@*/
02923 {
02924     rpmTagData data = { .ptr = he->p.ptr };
02925     char * val;
02926 
02927     if (he->t != RPM_INT64_TYPE) {
02928         val = xstrdup(_("(not a number)"));
02929     } else {
02930         struct tm * tstruct;
02931         char buf[50];
02932 
02933         /* this is important if sizeof(int_64) ! sizeof(time_t) */
02934         {   time_t dateint = data.ui64p[0];
02935             tstruct = localtime(&dateint);
02936         }
02937         buf[0] = '\0';
02938         if (tstruct)
02939             (void) strftime(buf, sizeof(buf) - 1, strftimeFormat, tstruct);
02940         buf[sizeof(buf) - 1] = '\0';
02941         val = xstrdup(buf);
02942     }
02943 
02944     return val;
02945 }
02946 
02953 static char * dateFormat(HE_t he, /*@null@*/ const char ** av)
02954         /*@*/
02955 {
02956     return realDateFormat(he, NULL, _("%c"));
02957 }
02958 
02965 static char * dayFormat(HE_t he, /*@null@*/ const char ** av)
02966         /*@*/
02967 {
02968     return realDateFormat(he, NULL, _("%a %b %d %Y"));
02969 }
02970 
02977 static char * shescapeFormat(HE_t he, /*@null@*/ const char ** av)
02978         /*@*/
02979 {
02980     char * val;
02981     size_t nb;
02982 
02983     /* XXX one of these integer types is unnecessary. */
02984     if (he->t == RPM_INT32_TYPE) {
02985         nb = 20;
02986         val = xmalloc(nb);
02987         snprintf(val, nb, "%d", he->p.i32p[0]);
02988         val[nb-1] = '\0';
02989     } else if (he->t == RPM_INT64_TYPE) {
02990         nb = 40;
02991         val = xmalloc(40);
02992         snprintf(val, nb, "%lld", he->p.i64p[0]);
02993         val[nb-1] = '\0';
02994     } else if (he->t == RPM_STRING_TYPE) {
02995         const char * s = he->p.str;
02996         char * t;
02997         int c;
02998 
02999         nb = 0;
03000         for (s = he->p.str; (c = (int)*s) != 0; s++)  {
03001             nb++;
03002             if (c == (int)'\'')
03003                 nb += 3;
03004         }
03005         nb += 3;
03006         t = val = xmalloc(nb);
03007         *t++ = '\'';
03008         for (s = he->p.str; (c = (int)*s) != 0; s++)  {
03009             if (c == (int)'\'') {
03010                 *t++ = '\'';
03011                 *t++ = '\\';
03012                 *t++ = '\'';
03013             }
03014             *t++ = (char) c;
03015         }
03016         *t++ = '\'';
03017         *t = '\0';
03018     } else
03019         val = xstrdup(_("invalid type"));
03020 
03021     return val;
03022 }
03023 
03024 /*@-type@*/ /* FIX: cast? */
03025 const struct headerSprintfExtension_s headerDefaultFormats[] = {
03026     { HEADER_EXT_FORMAT, "octal",
03027         { .fmtFunction = octFormat } },
03028     { HEADER_EXT_FORMAT, "oct",
03029         { .fmtFunction = octFormat } },
03030     { HEADER_EXT_FORMAT, "hex",
03031         { .fmtFunction = hexFormat } },
03032     { HEADER_EXT_FORMAT, "decimal",
03033         { .fmtFunction = decFormat } },
03034     { HEADER_EXT_FORMAT, "dec",
03035         { .fmtFunction = decFormat } },
03036     { HEADER_EXT_FORMAT, "date",
03037         { .fmtFunction = dateFormat } },
03038     { HEADER_EXT_FORMAT, "day",
03039         { .fmtFunction = dayFormat } },
03040     { HEADER_EXT_FORMAT, "shescape",
03041         { .fmtFunction = shescapeFormat } },
03042     { HEADER_EXT_LAST, NULL, { NULL } }
03043 };
03044 /*@=type@*/
03045 
03046 /* forward ref */
03055 static int parseExpression(headerSprintfArgs hsa, sprintfToken token,
03056                 char * str, /*@out@*/char ** endPtr)
03057         /*@modifies hsa, str, token, *endPtr @*/
03058         /*@requires maxSet(endPtr) >= 0 @*/;
03059 
03070 static int parseFormat(headerSprintfArgs hsa, /*@null@*/ char * str,
03071                 /*@out@*/ sprintfToken * formatPtr,
03072                 /*@out@*/ int * numTokensPtr,
03073                 /*@null@*/ /*@out@*/ char ** endPtr, int state)
03074         /*@modifies hsa, str, *formatPtr, *numTokensPtr, *endPtr @*/
03075         /*@requires maxSet(formatPtr) >= 0 /\ maxSet(numTokensPtr) >= 0
03076                 /\ maxSet(endPtr) >= 0 @*/
03077 {
03078 /*@observer@*/
03079 static const char *pstates[] = {
03080 "NORMAL", "ARRAY", "EXPR", "WTF?"
03081 };
03082     char * chptr, * start, * next, * dst;
03083     sprintfToken format;
03084     sprintfToken token;
03085     unsigned numTokens;
03086     unsigned i;
03087     int done = 0;
03088     int xx;
03089 
03090 /*@-modfilesys@*/
03091 if (_hdr_debug)
03092 fprintf(stderr, "-->     parseFormat(%p, \"%.20s...\", %p, %p, %p, %s)\n", hsa, str, formatPtr, numTokensPtr, endPtr, pstates[(state & 0x3)]);
03093 /*@=modfilesys@*/
03094 
03095     /* upper limit on number of individual formats */
03096     numTokens = 0;
03097     if (str != NULL)
03098     for (chptr = str; *chptr != '\0'; chptr++)
03099         if (*chptr == '%') numTokens++;
03100     numTokens = numTokens * 2 + 1;
03101 
03102     format = xcalloc(numTokens, sizeof(*format));
03103     if (endPtr) *endPtr = NULL;
03104 
03105 /*@-infloops@*/ /* LCL: can't detect done termination */
03106     dst = start = str;
03107     numTokens = 0;
03108     token = NULL;
03109     if (start != NULL)
03110     while (*start != '\0') {
03111         switch (*start) {
03112         case '%':
03113             /* handle %% */
03114             if (*(start + 1) == '%') {
03115                 if (token == NULL || token->type != PTOK_STRING) {
03116                     token = format + numTokens++;
03117                     token->type = PTOK_STRING;
03118                     /*@-temptrans -assignexpose@*/
03119                     dst = token->u.string.string = start;
03120                     /*@=temptrans =assignexpose@*/
03121                 }
03122                 start++;
03123                 *dst++ = *start++;
03124                 /*@switchbreak@*/ break;
03125             } 
03126 
03127             token = format + numTokens++;
03128             *dst++ = '\0';
03129             start++;
03130 
03131             if (*start == '|') {
03132                 char * newEnd;
03133 
03134                 start++;
03135                 if (parseExpression(hsa, token, start, &newEnd))
03136                 {
03137                     format = freeFormat(format, numTokens);
03138                     return 1;
03139                 }
03140                 start = newEnd;
03141                 /*@switchbreak@*/ break;
03142             }
03143 
03144             /*@-assignexpose@*/
03145             token->u.tag.format = start;
03146             /*@=assignexpose@*/
03147             token->u.tag.pad = 0;
03148             token->u.tag.justOne = 0;
03149             token->u.tag.arrayCount = 0;
03150 
03151             chptr = start;
03152             while (*chptr && *chptr != '{' && *chptr != '%') chptr++;
03153             if (!*chptr || *chptr == '%') {
03154                 hsa->errmsg = _("missing { after %");
03155                 format = freeFormat(format, numTokens);
03156                 return 1;
03157             }
03158 
03159 /*@-modfilesys@*/
03160 if (_hdr_debug)
03161 fprintf(stderr, "\tchptr *%p = NUL\n", chptr);
03162 /*@=modfilesys@*/
03163             *chptr++ = '\0';
03164 
03165             while (start < chptr) {
03166                 if (xisdigit((int)*start)) {
03167                     i = strtoul(start, &start, 10);
03168                     token->u.tag.pad += i;
03169                     start = chptr;
03170                     /*@innerbreak@*/ break;
03171                 } else {
03172                     start++;
03173                 }
03174             }
03175 
03176             if (*start == '=') {
03177                 token->u.tag.justOne = 1;
03178                 start++;
03179             } else if (*start == '#') {
03180                 token->u.tag.justOne = 1;
03181                 token->u.tag.arrayCount = 1;
03182                 start++;
03183             }
03184 
03185             next = start;
03186             while (*next && *next != '}') next++;
03187             if (!*next) {
03188                 hsa->errmsg = _("missing } after %{");
03189                 format = freeFormat(format, numTokens);
03190                 return 1;
03191             }
03192 /*@-modfilesys@*/
03193 if (_hdr_debug)
03194 fprintf(stderr, "\tnext *%p = NUL\n", next);
03195 /*@=modfilesys@*/
03196             *next++ = '\0';
03197 
03198 #define isSEP(_c)       ((_c) == ':' || (_c) == '|')
03199             chptr = start;
03200             while (!(*chptr == '\0' || isSEP(*chptr))) chptr++;
03201             /* Split ":bing|bang:boom" --qf pipeline formatters (if any) */
03202             while (isSEP(*chptr)) {
03203                 if (chptr[1] == '\0' || isSEP(chptr[1])) {
03204                     hsa->errmsg = _("empty tag format");
03205                     format = freeFormat(format, numTokens);
03206                     return 1;
03207                 }
03208                 /* Parse the formatter parameter list. */
03209                 {   char * te = chptr + 1;
03210                     char * t = strchr(te, '(');
03211                     char c;
03212 
03213                     while (!(*te == '\0' || isSEP(*te))) {
03214 #ifdef  NOTYET  /* XXX some means of escaping is needed */
03215                         if (te[0] == '\\' && te[1] != '\0') te++;
03216 #endif
03217                         te++;
03218                     }
03219                     c = *te; *te = '\0';
03220                     /* Parse (a,b,c) parameter list. */
03221                     if (t != NULL) {
03222                         *t++ = '\0';
03223                         if (te <= t || te[-1] != ')') {
03224                             hsa->errmsg = _("malformed parameter list");
03225                             format = freeFormat(format, numTokens);
03226                             return 1;
03227                         }
03228                         te[-1] = '\0';
03229                         xx = argvAdd(&token->u.tag.params, t);
03230                     } else
03231                         xx = argvAdd(&token->u.tag.params, "");
03232 /*@-modfilesys@*/
03233 if (_hdr_debug)
03234 fprintf(stderr, "\tformat \"%s\" params \"%s\"\n", chptr, (t ? t : ""));
03235 /*@=modfilesys@*/
03236                     xx = argvAdd(&token->u.tag.av, chptr);
03237                     *te = c;
03238                     *chptr = '\0';
03239                     chptr = te;
03240                 }
03241             }
03242 #undef  isSEP
03243             
03244             if (*start == '\0') {
03245                 hsa->errmsg = _("empty tag name");
03246                 format = freeFormat(format, numTokens);
03247                 return 1;
03248             }
03249 
03250             i = 0;
03251             token->type = PTOK_TAG;
03252 
03253             if (findTag(hsa, token, start)) {
03254                 hsa->errmsg = _("unknown tag");
03255                 format = freeFormat(format, numTokens);
03256                 return 1;
03257             }
03258 
03259             dst = start = next;
03260 /*@-modfilesys@*/
03261 if (_hdr_debug)
03262 fprintf(stderr, "\tdst = start = next %p\n", dst);
03263 /*@=modfilesys@*/
03264             /*@switchbreak@*/ break;
03265 
03266         case '[':
03267 /*@-modfilesys@*/
03268 if (_hdr_debug)
03269 fprintf(stderr, "\t%s => %s *%p = NUL\n", pstates[(state & 0x3)], pstates[PARSER_IN_ARRAY], start);
03270 /*@=modfilesys@*/
03271             *start++ = '\0';
03272             token = format + numTokens++;
03273 
03274             if (parseFormat(hsa, start,
03275                             &token->u.array.format,
03276                             &token->u.array.numTokens,
03277                             &start, PARSER_IN_ARRAY))
03278             {
03279                 format = freeFormat(format, numTokens);
03280                 return 1;
03281             }
03282 
03283             if (!start) {
03284                 hsa->errmsg = _("] expected at end of array");
03285                 format = freeFormat(format, numTokens);
03286                 return 1;
03287             }
03288 
03289             dst = start;
03290 /*@-modfilesys@*/
03291 if (_hdr_debug)
03292 fprintf(stderr, "\tdst = start %p\n", dst);
03293 /*@=modfilesys@*/
03294 
03295             token->type = PTOK_ARRAY;
03296 
03297             /*@switchbreak@*/ break;
03298 
03299         case ']':
03300             if (state != PARSER_IN_ARRAY) {
03301                 hsa->errmsg = _("unexpected ]");
03302                 format = freeFormat(format, numTokens);
03303                 return 1;
03304             }
03305             *start++ = '\0';
03306 /*@-modfilesys@*/
03307 if (_hdr_debug)
03308 fprintf(stderr, "\t<= %s %p[-1] = NUL\n", pstates[(state & 0x3)], start);
03309 /*@=modfilesys@*/
03310             if (endPtr) *endPtr = start;
03311             done = 1;
03312             /*@switchbreak@*/ break;
03313 
03314         case '}':
03315             if (state != PARSER_IN_EXPR) {
03316                 hsa->errmsg = _("unexpected }");
03317                 format = freeFormat(format, numTokens);
03318                 return 1;
03319             }
03320             *start++ = '\0';
03321 /*@-modfilesys@*/
03322 if (_hdr_debug)
03323 fprintf(stderr, "\t<= %s %p[-1] = NUL\n", pstates[(state & 0x3)], start);
03324 /*@=modfilesys@*/
03325             if (endPtr) *endPtr = start;
03326             done = 1;
03327             /*@switchbreak@*/ break;
03328 
03329         default:
03330             if (token == NULL || token->type != PTOK_STRING) {
03331                 token = format + numTokens++;
03332                 token->type = PTOK_STRING;
03333                 /*@-temptrans -assignexpose@*/
03334                 dst = token->u.string.string = start;
03335                 /*@=temptrans =assignexpose@*/
03336             }
03337 
03338 /*@-modfilesys@*/
03339 if (_hdr_debug)
03340 fprintf(stderr, "\t*%p = *%p \"%.30s\"\n", dst, start, start);
03341 /*@=modfilesys@*/
03342             if (start[0] == '\\' && start[1] != '\0') {
03343                 start++;
03344                 *dst++ = escapedChar(*start);
03345                 *start++ = '\0';
03346             } else {
03347                 *dst++ = *start++;
03348             }
03349             if (dst < start) *dst = '\0';
03350             /*@switchbreak@*/ break;
03351         }
03352         if (done)
03353             break;
03354     }
03355 /*@=infloops@*/
03356 
03357     if (dst != NULL)
03358         *dst = '\0';
03359 
03360     for (i = 0; i < (unsigned) numTokens; i++) {
03361         token = format + i;
03362         switch(token->type) {
03363         default:
03364             /*@switchbreak@*/ break;
03365         case PTOK_STRING:
03366             token->u.string.len = strlen(token->u.string.string);
03367             /*@switchbreak@*/ break;
03368         }
03369     }
03370 
03371     if (numTokensPtr != NULL)
03372         *numTokensPtr = numTokens;
03373     if (formatPtr != NULL)
03374         *formatPtr = format;
03375 
03376     return 0;
03377 }
03378 
03379 static int parseExpression(headerSprintfArgs hsa, sprintfToken token,
03380                 char * str, /*@out@*/ char ** endPtr)
03381 {
03382     char * chptr;
03383     char * end;
03384 
03385 /*@-modfilesys@*/
03386 if (_hdr_debug)
03387 fprintf(stderr, "-->   parseExpression(%p, %p, \"%.20s...\", %p)\n", hsa, token, str, endPtr);
03388 /*@=modfilesys@*/
03389 
03390     hsa->errmsg = NULL;
03391     chptr = str;
03392     while (*chptr && *chptr != '?') chptr++;
03393 
03394     if (*chptr != '?') {
03395         hsa->errmsg = _("? expected in expression");
03396         return 1;
03397     }
03398 
03399     *chptr++ = '\0';
03400 
03401     if (*chptr != '{') {
03402         hsa->errmsg = _("{ expected after ? in expression");
03403         return 1;
03404     }
03405 
03406     chptr++;
03407 
03408     if (parseFormat(hsa, chptr, &token->u.cond.ifFormat, 
03409                     &token->u.cond.numIfTokens, &end, PARSER_IN_EXPR)) 
03410         return 1;
03411 
03412     /* XXX fix segfault on "rpm -q rpm --qf='%|NAME?{%}:{NAME}|\n'"*/
03413     if (!(end && *end)) {
03414         hsa->errmsg = _("} expected in expression");
03415         token->u.cond.ifFormat =
03416                 freeFormat(token->u.cond.ifFormat, token->u.cond.numIfTokens);
03417         return 1;
03418     }
03419 
03420     chptr = end;
03421     if (*chptr != ':' && *chptr != '|') {
03422         hsa->errmsg = _(": expected following ? subexpression");
03423         token->u.cond.ifFormat =
03424                 freeFormat(token->u.cond.ifFormat, token->u.cond.numIfTokens);
03425         return 1;
03426     }
03427 
03428     if (*chptr == '|') {
03429         if (parseFormat(hsa, NULL, &token->u.cond.elseFormat, 
03430                 &token->u.cond.numElseTokens, &end, PARSER_IN_EXPR))
03431         {
03432             token->u.cond.ifFormat =
03433                 freeFormat(token->u.cond.ifFormat, token->u.cond.numIfTokens);
03434             return 1;
03435         }
03436     } else {
03437         chptr++;
03438 
03439         if (*chptr != '{') {
03440             hsa->errmsg = _("{ expected after : in expression");
03441             token->u.cond.ifFormat =
03442                 freeFormat(token->u.cond.ifFormat, token->u.cond.numIfTokens);
03443             return 1;
03444         }
03445 
03446         chptr++;
03447 
03448         if (parseFormat(hsa, chptr, &token->u.cond.elseFormat, 
03449                         &token->u.cond.numElseTokens, &end, PARSER_IN_EXPR)) 
03450             return 1;
03451 
03452         /* XXX fix segfault on "rpm -q rpm --qf='%|NAME?{a}:{%}|{NAME}\n'" */
03453         if (!(end && *end)) {
03454             hsa->errmsg = _("} expected in expression");
03455             token->u.cond.ifFormat =
03456                 freeFormat(token->u.cond.ifFormat, token->u.cond.numIfTokens);
03457             return 1;
03458         }
03459 
03460         chptr = end;
03461         if (*chptr != '|') {
03462             hsa->errmsg = _("| expected at end of expression");
03463             token->u.cond.ifFormat =
03464                 freeFormat(token->u.cond.ifFormat, token->u.cond.numIfTokens);
03465             token->u.cond.elseFormat =
03466                 freeFormat(token->u.cond.elseFormat, token->u.cond.numElseTokens);
03467             return 1;
03468         }
03469     }
03470         
03471     chptr++;
03472 
03473     *endPtr = chptr;
03474 
03475     token->type = PTOK_COND;
03476 
03477     (void) findTag(hsa, token, str);
03478 
03479     return 0;
03480 }
03481 
03490 static int getExtension(headerSprintfArgs hsa, headerTagTagFunction fn,
03491                 HE_t he, HE_t ec)
03492         /*@modifies he, ec @*/
03493 {
03494     int rc = 0;
03495     if (!ec->avail) {
03496         he = rpmheClean(he);
03497         rc = fn(hsa->h, he);
03498         *ec = *he;      /* structure copy. */
03499         if (!rc)
03500             ec->avail = 1;
03501     } else
03502         *he = *ec;      /* structure copy. */
03503     he->freeData = 0;
03504     return rc;
03505 }
03506 
03514 /*@observer@*/ /*@null@*/
03515 static char * formatValue(headerSprintfArgs hsa, sprintfTag tag, int element)
03516         /*@modifies hsa @*/
03517 {
03518     HE_t vhe = memset(alloca(sizeof(*vhe)), 0, sizeof(*vhe));
03519     HE_t he = &tag->he;
03520     char * val = NULL;
03521     size_t need = 0;
03522     char * t, * te;
03523     int_64 ival = 0;
03524     rpmTagCount countBuf;
03525     int xx;
03526 
03527     if (!he->avail) {
03528         if (tag->ext) {
03529             xx = getExtension(hsa, tag->ext, he, hsa->ec + tag->extNum);
03530         } else {
03531             he->tag = tag->tagno;
03532 #ifdef  NOTYET
03533             if (_usehge) {
03534                 xx = headerGetExtension(hsa->h, he->tag, &he->t, &he->p, &he->c);
03535                 if (xx)         /* XXX 1 on success */
03536                     he->freeData = 1;
03537             } else
03538 #endif
03539             {
03540                 xx = headerGetEntry(hsa->h, he->tag, (hTYP_t)&he->t, &he->p, &he->c);
03541                 if (xx)         /* XXX 1 on success */
03542                     he = rpmheMark(he);
03543             }
03544             xx = (xx == 0);     /* XXX invert headerGetEntry return. */
03545         }
03546         if (xx) {
03547             (void) rpmheClean(he);
03548             he->t = RPM_STRING_TYPE;    
03549             he->p.str = "(none)";
03550             he->c = 1;
03551         }
03552         he->avail = 1;
03553     }
03554 
03555     if (tag->arrayCount) {
03556         countBuf = he->c;
03557         he = rpmheClean(he);
03558         he->t = RPM_INT32_TYPE;
03559         he->p.i32p = &countBuf;
03560         he->c = 1;
03561         he->freeData = 0;
03562     }
03563 
03564     if (he->p.ptr)
03565     switch (he->t) {
03566     default:
03567         val = xstrdup("(unknown type)");
03568         need = strlen(val) + 1;
03569         goto exit;
03570         /*@notreached@*/ break;
03571     case RPM_I18NSTRING_TYPE:
03572     case RPM_STRING_ARRAY_TYPE:
03573         vhe->t = RPM_STRING_TYPE;
03574         vhe->p.str = he->p.argv[element];
03575         vhe->c = he->c;
03576         vhe->ix = (he->t == RPM_STRING_ARRAY_TYPE || he->c > 1 ? 0 : -1);
03577         break;
03578     case RPM_STRING_TYPE:
03579         vhe->p.str = he->p.str;
03580         vhe->t = RPM_STRING_TYPE;
03581         vhe->c = 0;
03582         vhe->ix = -1;
03583         break;
03584     case RPM_CHAR_TYPE:
03585     case RPM_INT8_TYPE:
03586     case RPM_INT16_TYPE:
03587     case RPM_INT32_TYPE:
03588     case RPM_INT64_TYPE:
03589         switch (he->t) {
03590         default:
03591 assert(0);      /* XXX keep gcc quiet. */
03592             break;
03593         case RPM_CHAR_TYPE:     
03594         case RPM_INT8_TYPE:
03595             ival = he->p.i8p[element];
03596             break;
03597         case RPM_INT16_TYPE:
03598             ival = he->p.ui16p[element];        /* XXX note unsigned. */
03599             break;
03600         case RPM_INT32_TYPE:
03601             ival = he->p.i32p[element];
03602             break;
03603         case RPM_INT64_TYPE:
03604             ival = he->p.i64p[element];
03605             break;
03606         }
03607         vhe->t = RPM_INT64_TYPE;
03608         vhe->p.i64p = &ival;
03609         vhe->c = he->c;
03610         vhe->ix = (he->c > 1 ? 0 : -1);
03611         if ((myTagType(hsa->tags, he->tag) & RPM_MASK_RETURN_TYPE) == RPM_ARRAY_RETURN_TYPE)
03612             vhe->ix = 0;
03613         break;
03614 
03615     case RPM_OPENPGP_TYPE:      /* XXX W2DO? */
03616     case RPM_ASN1_TYPE:         /* XXX W2DO? */
03617     case RPM_BIN_TYPE:
03618         vhe->t = RPM_BIN_TYPE;
03619         vhe->p.ptr = he->p.ptr;
03620         vhe->c = he->c;
03621         vhe->ix = -1;
03622         break;
03623     }
03624 
03625 /*@-compmempass@*/      /* vhe->p.ui64p is stack, not owned */
03626     if (tag->fmtfuncs) {
03627         char * nval;
03628         int i;
03629         for (i = 0; tag->av[i] != NULL; i++) {
03630             headerTagFormatFunction fmt;
03631             ARGV_t av;
03632             if ((fmt = tag->fmtfuncs[i]) == NULL)
03633                 continue;
03634             /* If !1st formatter, and transformer, not extractor, save val. */
03635             if (val != NULL && *tag->av[i] == '|') {
03636                 int ix = vhe->ix;
03637                 vhe = rpmheClean(vhe);
03638                 vhe->tag = he->tag;
03639                 vhe->t = RPM_STRING_TYPE;
03640                 vhe->p.str = xstrdup(val);
03641                 vhe->c = he->c;
03642                 vhe->ix = ix;
03643                 vhe->freeData = 1;
03644             }
03645             av = NULL;
03646             if (tag->params && tag->params[i] && *tag->params[i] != '\0')
03647                 xx = argvSplit(&av, tag->params[i], ",");
03648 
03649             nval = fmt(vhe, av);
03650 
03651 /*@-modfilesys@*/
03652 if (_hdr_debug)
03653 fprintf(stderr, "\t%s(%s) %p(%p,%p) ret \"%s\"\n", tag->av[i], tag->params[i], fmt, vhe, (av ? av : NULL), val);
03654 /*@=modfilesys@*/
03655 
03656             /* Accumulate (by appending) next formmatter's return string. */
03657             if (val == NULL)
03658                 val = xstrdup((nval ? nval : ""));
03659             else {
03660                 char * oval = val;
03661                 /* XXX using ... | ... as separator is feeble. */
03662                 val = rpmExpand(val, (*val ? " | " : ""), nval, NULL);
03663                 oval = _free(oval);
03664             }
03665             nval = _free(nval);
03666             av = argvFree(av);
03667         }
03668     }
03669     if (val == NULL)
03670         val = intFormat(vhe, NULL, NULL);
03671 /*@=compmempass@*/
03672 assert(val != NULL);
03673     if (val)
03674         need = strlen(val) + 1;
03675 
03676 exit:
03677     if (!_tagcache)
03678         he = rpmheClean(he);
03679 
03680     if (val && need > 0) {
03681         if (tag->format && *tag->format && tag->pad) {
03682             size_t nb;
03683             nb = strlen(tag->format) + sizeof("%s");
03684             t = alloca(nb);
03685             (void) stpcpy( stpcpy( stpcpy(t, "%"), tag->format), "s");
03686             nb = tag->pad + strlen(val) + 1;
03687             te = xmalloc(nb);
03688             (void) snprintf(te, nb, t, val);
03689             te[nb-1] = '\0';
03690             val = _free(val);
03691             val = te;
03692             need += tag->pad;
03693         }
03694         t = hsaReserve(hsa, need);
03695         te = stpcpy(t, val);
03696         hsa->vallen += (te - t);
03697         val = _free(val);
03698     }
03699 
03700     return (hsa->val + hsa->vallen);
03701 }
03702 
03710 /*@observer@*/
03711 static char * singleSprintf(headerSprintfArgs hsa, sprintfToken token,
03712                 int element)
03713         /*@modifies hsa @*/
03714 {
03715     char numbuf[64];    /* XXX big enuf for "Tag_0x01234567" */
03716     char * t, * te;
03717     int i, j;
03718     int numElements;
03719     sprintfToken spft;
03720     sprintfTag tag = NULL;
03721     HE_t he = NULL;
03722     int condNumFormats;
03723     size_t need;
03724     int xx;
03725 
03726     /* we assume the token and header have been validated already! */
03727 
03728     switch (token->type) {
03729     case PTOK_NONE:
03730         break;
03731 
03732     case PTOK_STRING:
03733         need = token->u.string.len;
03734         if (need == 0) break;
03735         t = hsaReserve(hsa, need);
03736         te = stpcpy(t, token->u.string.string);
03737         hsa->vallen += (te - t);
03738         break;
03739 
03740     case PTOK_TAG:
03741         t = hsa->val + hsa->vallen;
03742         te = formatValue(hsa, &token->u.tag,
03743                         (token->u.tag.justOne ? 0 : element));
03744         if (te == NULL)
03745             return NULL;
03746         break;
03747 
03748     case PTOK_COND:
03749         if (token->u.cond.tag.ext
03750          || headerIsEntry(hsa->h, token->u.cond.tag.tagno))
03751         {
03752             spft = token->u.cond.ifFormat;
03753             condNumFormats = token->u.cond.numIfTokens;
03754         } else {
03755             spft = token->u.cond.elseFormat;
03756             condNumFormats = token->u.cond.numElseTokens;
03757         }
03758 
03759         need = condNumFormats * 20;
03760         if (spft == NULL || need == 0) break;
03761 
03762         t = hsaReserve(hsa, need);
03763         for (i = 0; i < condNumFormats; i++, spft++) {
03764             te = singleSprintf(hsa, spft, element);
03765             if (te == NULL)
03766                 return NULL;
03767         }
03768         break;
03769 
03770     case PTOK_ARRAY:
03771         numElements = -1;
03772         spft = token->u.array.format;
03773         for (i = 0; i < token->u.array.numTokens; i++, spft++)
03774         {
03775             tag = &spft->u.tag;
03776             if (spft->type != PTOK_TAG || tag->arrayCount || tag->justOne)
03777                 continue;
03778             he = &tag->he;
03779             if (!he->avail) {
03780                 he->tag = tag->tagno;
03781                 if (tag->ext)
03782                     xx = getExtension(hsa, tag->ext, he, hsa->ec + tag->extNum);
03783                 else {
03784 #ifdef  NOTYET
03785                     if (_usehge) {
03786                         xx = headerGetExtension(hsa->h, he->tag, &he->t, &he->p, &he->c);
03787                         if (xx)
03788                             he->freeData = 1;
03789                     } else
03790 #endif
03791                     {
03792                         xx = headerGetEntry(hsa->h, he->tag, (hTYP_t)&he->t, &he->p, &he->c);
03793                         if (xx) /* XXX 1 on success */
03794                             rpmheMark(he);
03795                     }
03796                     xx = (xx == 0);     /* XXX invert headerGetEntry return. */
03797                 }
03798                 if (xx) {
03799                     (void) rpmheClean(he);
03800                     continue;
03801                 }
03802                 he->avail = 1;
03803             }
03804 
03805             /* Check iteration arrays are same dimension (or scalar). */
03806             switch (he->t) {
03807             default:
03808                 if (numElements == -1) {
03809                     numElements = he->c;
03810                     /*@switchbreak@*/ break;
03811                 }
03812                 if (he->c == numElements)
03813                     /*@switchbreak@*/ break;
03814                 hsa->errmsg =
03815                         _("array iterator used with different sized arrays");
03816                 he = rpmheClean(he);
03817                 return NULL;
03818                 /*@notreached@*/ /*@switchbreak@*/ break;
03819             case RPM_OPENPGP_TYPE:
03820             case RPM_ASN1_TYPE:
03821             case RPM_BIN_TYPE:
03822             case RPM_STRING_TYPE:
03823                 if (numElements == -1)
03824                     numElements = 1;
03825                 /*@switchbreak@*/ break;
03826             }
03827             if (!_tagcache)
03828                 he = rpmheClean(he);
03829         }
03830         spft = token->u.array.format;
03831 
03832         if (numElements == -1) {
03833 #ifdef  DYING   /* XXX lots of pugly "(none)" lines with --conflicts. */
03834             need = sizeof("(none)\n") - 1;
03835             t = hsaReserve(hsa, need);
03836             te = stpcpy(t, "(none)\n");
03837             hsa->vallen += (te - t);
03838 #endif
03839         } else {
03840             int isxml;
03841             int isyaml;
03842 
03843             need = numElements * token->u.array.numTokens;
03844             if (need == 0) break;
03845 
03846             tag = &spft->u.tag;
03847 
03848             /* XXX Ick: +1 needed to handle :extractor |transformer marking. */
03849             isxml = (spft->type == PTOK_TAG && tag->av != NULL &&
03850                 tag->av[0] != NULL && !strcmp(tag->av[0]+1, "xml"));
03851             isyaml = (spft->type == PTOK_TAG && tag->av != NULL &&
03852                 tag->av[0] != NULL && !strcmp(tag->av[0]+1, "yaml"));
03853 
03854             if (isxml) {
03855                 const char * tagN = myTagName(hsa->tags, tag->tagno, NULL);
03856                 /* XXX display "Tag_0x01234567" for unknown tags. */
03857                 if (tagN == NULL) {
03858                     (void) snprintf(numbuf, sizeof(numbuf), "Tag_0x%08x",
03859                                 tag->tagno);
03860                     numbuf[sizeof(numbuf)-1] = '\0';
03861                     tagN = numbuf;
03862                 }
03863                 need = sizeof("  <rpmTag name=\"\">\n") + strlen(tagN);
03864                 te = t = hsaReserve(hsa, need);
03865                 te = stpcpy( stpcpy( stpcpy(te, "  <rpmTag name=\""), tagN), "\">\n");
03866                 hsa->vallen += (te - t);
03867             }
03868             if (isyaml) {
03869                 int tagT = -1;
03870                 const char * tagN = myTagName(hsa->tags, tag->tagno, &tagT);
03871                 /* XXX display "Tag_0x01234567" for unknown tags. */
03872                 if (tagN == NULL) {
03873                     (void) snprintf(numbuf, sizeof(numbuf), "Tag_0x%08x",
03874                                 tag->tagno);
03875                     numbuf[sizeof(numbuf)-1] = '\0';
03876                     tagN = numbuf;
03877                     tagT = numElements > 1
03878                         ?  RPM_ARRAY_RETURN_TYPE : RPM_SCALAR_RETURN_TYPE;
03879                 }
03880                 need = sizeof("  :     - ") + strlen(tagN);
03881                 te = t = hsaReserve(hsa, need);
03882                 *te++ = ' ';
03883                 *te++ = ' ';
03884                 te = stpcpy(te, tagN);
03885                 *te++ = ':';
03886                 *te++ = (((tagT & RPM_MASK_RETURN_TYPE) == RPM_ARRAY_RETURN_TYPE)
03887                         ? '\n' : ' ');
03888                 *te = '\0';
03889                 hsa->vallen += (te - t);
03890             }
03891 
03892             need = numElements * token->u.array.numTokens * 10;
03893             t = hsaReserve(hsa, need);
03894             for (j = 0; j < numElements; j++) {
03895                 spft = token->u.array.format;
03896                 for (i = 0; i < token->u.array.numTokens; i++, spft++) {
03897                     te = singleSprintf(hsa, spft, j);
03898                     if (te == NULL)
03899                         return NULL;
03900                 }
03901             }
03902 
03903             if (isxml) {
03904                 need = sizeof("  </rpmTag>\n") - 1;
03905                 te = t = hsaReserve(hsa, need);
03906                 te = stpcpy(te, "  </rpmTag>\n");
03907                 hsa->vallen += (te - t);
03908             }
03909             if (isyaml) {
03910 #if 0
03911                 need = sizeof("\n") - 1;
03912                 te = t = hsaReserve(hsa, need);
03913                 te = stpcpy(te, "\n");
03914                 hsa->vallen += (te - t);
03915 #endif
03916             }
03917 
03918         }
03919         break;
03920     }
03921 
03922     return (hsa->val + hsa->vallen);
03923 }
03924 
03931 static /*@only@*/ HE_t
03932 rpmecNew(const headerSprintfExtension exts, /*@null@*/ int * necp)
03933         /*@*/
03934 {
03935     headerSprintfExtension ext;
03936     HE_t ec;
03937     int extNum = 0;
03938 
03939     if (exts != NULL)
03940     for (ext = exts, extNum = 0; ext != NULL && ext->type != HEADER_EXT_LAST;
03941         ext = (ext->type == HEADER_EXT_MORE ? ext->u.more : ext+1), extNum++)
03942     {
03943         ;
03944     }
03945     if (necp)
03946         *necp = extNum;
03947     ec = xcalloc(extNum+1, sizeof(*ec));        /* XXX +1 unnecessary */
03948     return ec;
03949 }
03950 
03957 static /*@null@*/ HE_t
03958 rpmecFree(const headerSprintfExtension exts, /*@only@*/ HE_t ec)
03959         /*@modifies ec @*/
03960 {
03961     headerSprintfExtension ext;
03962     int extNum;
03963 
03964     for (ext = exts, extNum = 0; ext != NULL && ext->type != HEADER_EXT_LAST;
03965         ext = (ext->type == HEADER_EXT_MORE ? ext->u.more : ext+1), extNum++)
03966     {
03967         (void) rpmheClean(&ec[extNum]);
03968     }
03969 
03970     ec = _free(ec);
03971     return NULL;
03972 }
03973 
03974 extern const struct headerTagTableEntry_s * rpmTagTable;
03975 
03987 static /*@only@*/ /*@null@*/
03988 char * headerSprintf(Header h, const char * fmt,
03989                      const struct headerTagTableEntry_s * tags,
03990                      const struct headerSprintfExtension_s * exts,
03991                      /*@null@*/ /*@out@*/ errmsg_t * errmsg)
03992         /*@modifies h, *errmsg @*/
03993         /*@requires maxSet(errmsg) >= 0 @*/
03994 {
03995     headerSprintfArgs hsa = memset(alloca(sizeof(*hsa)), 0, sizeof(*hsa));
03996     sprintfToken nextfmt;
03997     sprintfTag tag;
03998     char * t, * te;
03999     int isxml;
04000     int isyaml;
04001     int need;
04002  
04003 /*@-modfilesys@*/
04004 if (_hdr_debug)
04005 fprintf(stderr, "==> headerSprintf(%p, \"%s\", %p, %p, %p)\n", h, fmt, tags, exts, errmsg);
04006 /*@=modfilesys@*/
04007 
04008     /* Set some reasonable defaults */
04009     if (tags == NULL)
04010         tags = rpmTagTable;
04011 
04012     hsa->h = headerLink(h);
04013     hsa->fmt = xstrdup(fmt);
04014 /*@-castexpose@*/       /* FIX: legacy API shouldn't change. */
04015     hsa->exts = (headerSprintfExtension) exts;
04016     hsa->tags = (headerTagTableEntry) tags;
04017 /*@=castexpose@*/
04018     hsa->errmsg = NULL;
04019 
04020     if (parseFormat(hsa, hsa->fmt, &hsa->format, &hsa->numTokens, NULL, PARSER_BEGIN))
04021         goto exit;
04022 
04023     hsa->nec = 0;
04024     hsa->ec = rpmecNew(hsa->exts, &hsa->nec);
04025     hsa->val = xstrdup("");
04026 
04027     tag =
04028         (hsa->format->type == PTOK_TAG
04029             ? &hsa->format->u.tag :
04030         (hsa->format->type == PTOK_ARRAY
04031             ? &hsa->format->u.array.format->u.tag :
04032         NULL));
04033     /* XXX Ick: +1 needed to handle :extractor |transformer marking. */
04034     isxml = (tag != NULL && tag->tagno == -2 && tag->av != NULL
04035                 && tag->av[0] != NULL && !strcmp(tag->av[0]+1, "xml"));
04036     isyaml = (tag != NULL && tag->tagno == -2 && tag->av != NULL
04037                 && tag->av[0] != NULL && !strcmp(tag->av[0]+1, "yaml"));
04038 
04039     if (isxml) {
04040         need = sizeof("<rpmHeader>\n") - 1;
04041         t = hsaReserve(hsa, need);
04042         te = stpcpy(t, "<rpmHeader>\n");
04043         hsa->vallen += (te - t);
04044     }
04045     if (isyaml) {
04046         need = sizeof("- !!omap\n") - 1;
04047         t = hsaReserve(hsa, need);
04048         te = stpcpy(t, "- !!omap\n");
04049         hsa->vallen += (te - t);
04050     }
04051 
04052     hsa = hsaInit(hsa);
04053     while ((nextfmt = hsaNext(hsa)) != NULL) {
04054         te = singleSprintf(hsa, nextfmt, 0);
04055         if (te == NULL) {
04056             hsa->val = _free(hsa->val);
04057             break;
04058         }
04059     }
04060     hsa = hsaFini(hsa);
04061 
04062     if (isxml) {
04063         need = sizeof("</rpmHeader>\n") - 1;
04064         t = hsaReserve(hsa, need);
04065         te = stpcpy(t, "</rpmHeader>\n");
04066         hsa->vallen += (te - t);
04067     }
04068     if (isyaml) {
04069         need = sizeof("\n") - 1;
04070         t = hsaReserve(hsa, need);
04071         te = stpcpy(t, "\n");
04072         hsa->vallen += (te - t);
04073     }
04074 
04075     if (hsa->val != NULL && hsa->vallen < hsa->alloced)
04076         hsa->val = xrealloc(hsa->val, hsa->vallen+1);   
04077 
04078     hsa->ec = rpmecFree(hsa->exts, hsa->ec);
04079     hsa->nec = 0;
04080     hsa->format = freeFormat(hsa->format, hsa->numTokens);
04081 
04082 exit:
04083 /*@-dependenttrans -observertrans @*/
04084     if (errmsg)
04085         *errmsg = hsa->errmsg;
04086 /*@=dependenttrans =observertrans @*/
04087     hsa->h = headerFree(hsa->h);
04088     hsa->fmt = _free(hsa->fmt);
04089     return hsa->val;
04090 }
04091 
04098 static
04099 void headerCopyTags(Header headerFrom, Header headerTo, hTAG_t tagstocopy)
04100         /*@modifies headerTo @*/
04101 {
04102     int * tagno;
04103 
04104     if (headerFrom == headerTo)
04105         return;
04106 
04107     for (tagno = tagstocopy; *tagno != 0; tagno++) {
04108         rpmTagType t;
04109         rpmTagData p = { .ptr = NULL };
04110         rpmTagCount c;
04111         if (headerIsEntry(headerTo, *tagno))
04112             continue;
04113         if (!headerGetEntryMinMemory(headerFrom, *tagno, (hTYP_t)&t, &p, &c))
04114             continue;
04115         (void) headerAddEntry(headerTo, *tagno, t, p.ptr, c);
04116         p.ptr = headerFreeData(p.ptr, t);
04117     }
04118 }
04119 
04120 /*@observer@*/ /*@unchecked@*/
04121 static struct HV_s hdrVec1 = {
04122     headerLink,
04123     headerUnlink,
04124     headerFree,
04125     headerNew,
04126     headerSort,
04127     headerUnsort,
04128     headerSizeof,
04129     headerUnload,
04130     headerReload,
04131     headerCopy,
04132     headerLoad,
04133     headerCopyLoad,
04134     headerRead,
04135     headerWrite,
04136     headerIsEntry,
04137     headerFreeTag,
04138     headerGetEntry,
04139     headerGetEntryMinMemory,
04140     headerAddEntry,
04141     headerAppendEntry,
04142     headerAddOrAppendEntry,
04143     headerAddI18NString,
04144     headerModifyEntry,
04145     headerRemoveEntry,
04146     headerSprintf,
04147     headerCopyTags,
04148     headerFreeIterator,
04149     headerInitIterator,
04150     headerNextIterator,
04151     headerGetOrigin,
04152     headerSetOrigin,
04153     headerGetInstance,
04154     headerSetInstance,
04155     NULL, NULL,
04156     1
04157 };
04158 
04159 /*@-compmempass -redef@*/
04160 /*@observer@*/ /*@unchecked@*/
04161 HV_t hdrVec = &hdrVec1;
04162 /*@=compmempass =redef@*/