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 <header_internal.h>
00016 
00017 #include "debug.h"
00018 
00019 /*@unchecked@*/
00020 int _hdr_debug = 0;
00021 
00022 /*@access entryInfo @*/
00023 /*@access indexEntry @*/
00024 
00025 /*@access rpmec @*/
00026 /*@access sprintfTag @*/
00027 /*@access sprintfToken @*/
00028 /*@access HV_t @*/
00029 
00030 #define PARSER_BEGIN    0
00031 #define PARSER_IN_ARRAY 1
00032 #define PARSER_IN_EXPR  2
00033 
00036 /*@observer@*/ /*@unchecked@*/
00037 static unsigned char header_magic[8] = {
00038         0x8e, 0xad, 0xe8, 0x01, 0x00, 0x00, 0x00, 0x00
00039 };
00040 
00044 /*@observer@*/ /*@unchecked@*/
00045 static int typeAlign[16] =  {
00046     1,  
00047     1,  
00048     1,  
00049     2,  
00050     4,  
00051     8,  
00052     1,  
00053     1,  
00054     1,  
00055     1,  
00056     0,
00057     0,
00058     0,
00059     0,
00060     0,
00061     0
00062 };
00063 
00067 /*@observer@*/ /*@unchecked@*/
00068 static int typeSizes[16] =  { 
00069     0,  
00070     1,  
00071     1,  
00072     2,  
00073     4,  
00074     -1, 
00075     -1, 
00076     1,  
00077     -1, 
00078     -1, 
00079     0,
00080     0,
00081     0,
00082     0,
00083     0,
00084     0
00085 };
00086 
00090 /*@unchecked@*/
00091 static size_t headerMaxbytes = (32*1024*1024);
00092 
00097 #define hdrchkTags(_ntags)      ((_ntags) & 0xffff0000)
00098 
00102 #define hdrchkType(_type) ((_type) < RPM_MIN_TYPE || (_type) > RPM_MAX_TYPE)
00103 
00108 #define hdrchkData(_nbytes)     ((_nbytes) & 0xff000000)
00109 
00113 #define hdrchkAlign(_type, _off)        ((_off) & (typeAlign[_type]-1))
00114 
00118 #define hdrchkRange(_dl, _off)          ((_off) < 0 || (_off) > (_dl))
00119 
00120 /*@observer@*/ /*@unchecked@*/
00121 HV_t hdrVec;    /* forward reference */
00122 
00128 /*@unused@*/ static inline /*@null@*/ void *
00129 _free(/*@only@*/ /*@null@*/ /*@out@*/ const void * p) /*@modifies *p @*/
00130 {
00131     if (p != NULL)      free((void *)p);
00132     return NULL;
00133 }
00134 
00140 static
00141 Header headerLink(Header h)
00142         /*@modifies h @*/
00143 {
00144 /*@-nullret@*/
00145     if (h == NULL) return NULL;
00146 /*@=nullret@*/
00147 
00148     h->nrefs++;
00149 /*@-modfilesys@*/
00150 if (_hdr_debug)
00151 fprintf(stderr, "--> h  %p ++ %d at %s:%u\n", h, h->nrefs, __FILE__, __LINE__);
00152 /*@=modfilesys@*/
00153 
00154     /*@-refcounttrans @*/
00155     return h;
00156     /*@=refcounttrans @*/
00157 }
00158 
00164 static /*@null@*/
00165 Header headerUnlink(/*@killref@*/ /*@null@*/ Header h)
00166         /*@modifies h @*/
00167 {
00168     if (h == NULL) return NULL;
00169 /*@-modfilesys@*/
00170 if (_hdr_debug)
00171 fprintf(stderr, "--> h  %p -- %d at %s:%u\n", h, h->nrefs, __FILE__, __LINE__);
00172 /*@=modfilesys@*/
00173     h->nrefs--;
00174     return NULL;
00175 }
00176 
00182 static /*@null@*/
00183 Header headerFree(/*@killref@*/ /*@null@*/ Header h)
00184         /*@modifies h @*/
00185 {
00186     (void) headerUnlink(h);
00187 
00188     /*@-usereleased@*/
00189     if (h == NULL || h->nrefs > 0)
00190         return NULL;    /* XXX return previous header? */
00191 
00192     if (h->index) {
00193         indexEntry entry = h->index;
00194         int i;
00195         for (i = 0; i < h->indexUsed; i++, entry++) {
00196             if ((h->flags & HEADERFLAG_ALLOCATED) && ENTRY_IS_REGION(entry)) {
00197                 if (entry->length > 0) {
00198                     int_32 * ei = entry->data;
00199                     if ((ei - 2) == h->blob) h->blob = _free(h->blob);
00200                     entry->data = NULL;
00201                 }
00202             } else if (!ENTRY_IN_REGION(entry)) {
00203                 entry->data = _free(entry->data);
00204             }
00205             entry->data = NULL;
00206         }
00207         h->index = _free(h->index);
00208     }
00209 
00210     /*@-refcounttrans@*/ h = _free(h); /*@=refcounttrans@*/
00211     return h;
00212     /*@=usereleased@*/
00213 }
00214 
00219 static
00220 Header headerNew(void)
00221         /*@*/
00222 {
00223     Header h = xcalloc(1, sizeof(*h));
00224 
00225 /*@-boundsread@*/
00226     /*@-assignexpose@*/
00227     h->hv = *hdrVec;            /* structure assignment */
00228     /*@=assignexpose@*/
00229 /*@=boundsread@*/
00230     h->blob = NULL;
00231     h->indexAlloced = INDEX_MALLOC_SIZE;
00232     h->indexUsed = 0;
00233     h->flags |= HEADERFLAG_SORTED;
00234 
00235     h->index = (h->indexAlloced
00236         ? xcalloc(h->indexAlloced, sizeof(*h->index))
00237         : NULL);
00238 
00239     h->nrefs = 0;
00240     /*@-globstate -observertrans @*/
00241     return headerLink(h);
00242     /*@=globstate =observertrans @*/
00243 }
00244 
00247 static int indexCmp(const void * avp, const void * bvp)
00248         /*@*/
00249 {
00250     /*@-castexpose@*/
00251     indexEntry ap = (indexEntry) avp, bp = (indexEntry) bvp;
00252     /*@=castexpose@*/
00253     return (ap->info.tag - bp->info.tag);
00254 }
00255 
00260 static
00261 void headerSort(Header h)
00262         /*@modifies h @*/
00263 {
00264     if (!(h->flags & HEADERFLAG_SORTED)) {
00265 /*@-boundsread@*/
00266         qsort(h->index, h->indexUsed, sizeof(*h->index), indexCmp);
00267 /*@=boundsread@*/
00268         h->flags |= HEADERFLAG_SORTED;
00269     }
00270 }
00271 
00274 static int offsetCmp(const void * avp, const void * bvp) /*@*/
00275 {
00276     /*@-castexpose@*/
00277     indexEntry ap = (indexEntry) avp, bp = (indexEntry) bvp;
00278     /*@=castexpose@*/
00279     int rc = (ap->info.offset - bp->info.offset);
00280 
00281     if (rc == 0) {
00282         /* Within a region, entries sort by address. Added drips sort by tag. */
00283         if (ap->info.offset < 0)
00284             rc = (((char *)ap->data) - ((char *)bp->data));
00285         else
00286             rc = (ap->info.tag - bp->info.tag);
00287     }
00288     return rc;
00289 }
00290 
00295 static
00296 void headerUnsort(Header h)
00297         /*@modifies h @*/
00298 {
00299 /*@-boundsread@*/
00300     qsort(h->index, h->indexUsed, sizeof(*h->index), offsetCmp);
00301 /*@=boundsread@*/
00302 }
00303 
00310 static
00311 unsigned int headerSizeof(/*@null@*/ Header h, enum hMagic magicp)
00312         /*@modifies h @*/
00313 {
00314     indexEntry entry;
00315     unsigned int size = 0;
00316     unsigned int pad = 0;
00317     int i;
00318 
00319     if (h == NULL)
00320         return size;
00321 
00322     headerSort(h);
00323 
00324     switch (magicp) {
00325     case HEADER_MAGIC_YES:
00326         size += sizeof(header_magic);
00327         break;
00328     case HEADER_MAGIC_NO:
00329         break;
00330     }
00331 
00332     /*@-sizeoftype@*/
00333     size += 2 * sizeof(int_32); /* count of index entries */
00334     /*@=sizeoftype@*/
00335 
00336     for (i = 0, entry = h->index; i < h->indexUsed; i++, entry++) {
00337         unsigned diff;
00338         int_32 type;
00339 
00340         /* Regions go in as is ... */
00341         if (ENTRY_IS_REGION(entry)) {
00342             size += entry->length;
00343             /* XXX Legacy regions do not include the region tag and data. */
00344             /*@-sizeoftype@*/
00345             if (i == 0 && (h->flags & HEADERFLAG_LEGACY))
00346                 size += sizeof(struct entryInfo_s) + entry->info.count;
00347             /*@=sizeoftype@*/
00348             continue;
00349         }
00350 
00351         /* ... and region elements are skipped. */
00352         if (entry->info.offset < 0)
00353             continue;
00354 
00355         /* Alignment */
00356         type = entry->info.type;
00357 /*@-boundsread@*/
00358         if (typeSizes[type] > 1) {
00359             diff = typeSizes[type] - (size % typeSizes[type]);
00360             if (diff != typeSizes[type]) {
00361                 size += diff;
00362                 pad += diff;
00363             }
00364         }
00365 /*@=boundsread@*/
00366 
00367         /*@-sizeoftype@*/
00368         size += sizeof(struct entryInfo_s) + entry->length;
00369         /*@=sizeoftype@*/
00370     }
00371 
00372     return size;
00373 }
00374 
00384 static int dataLength(int_32 type, hPTR_t p, int_32 count, int onDisk,
00385                 /*@null@*/ hPTR_t pend)
00386         /*@*/
00387 {
00388     const unsigned char * s = p;
00389     const unsigned char * se = pend;
00390     int length = 0;
00391 
00392     switch (type) {
00393     case RPM_STRING_TYPE:
00394         if (count != 1)
00395             return -1;
00396 /*@-boundsread@*/
00397         while (*s++) {
00398             if (se && s > se)
00399                 return -1;
00400             length++;
00401         }
00402 /*@=boundsread@*/
00403         length++;       /* count nul terminator too. */
00404         break;
00405 
00406     case RPM_STRING_ARRAY_TYPE:
00407     case RPM_I18NSTRING_TYPE:
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 
00411         if (onDisk) {
00412             while (count--) {
00413                 length++;       /* count nul terminator too */
00414 /*@-boundsread@*/
00415                while (*s++) {
00416                     if (se && s > se)
00417                         return -1;
00418                     length++;
00419                 }
00420 /*@=boundsread@*/
00421             }
00422         } else {
00423             const char ** av = (const char **)p;
00424 /*@-boundsread@*/
00425             while (count--) {
00426                 /* add one for null termination */
00427                 length += strlen(*av++) + 1;
00428             }
00429 /*@=boundsread@*/
00430         }
00431         break;
00432 
00433     default:
00434 /*@-boundsread@*/
00435         if (typeSizes[type] == -1)
00436             return -1;
00437         length = typeSizes[(type & 0xf)] * count;
00438 /*@=boundsread@*/
00439         if (length < 0 || (se && (s + length) > se))
00440             return -1;
00441         break;
00442     }
00443 
00444     return length;
00445 }
00446 
00473 static int regionSwab(/*@null@*/ indexEntry entry, int il, int dl,
00474                 entryInfo pe,
00475                 unsigned char * dataStart,
00476                 /*@null@*/ const unsigned char * dataEnd,
00477                 int regionid)
00478         /*@modifies *entry, *dataStart @*/
00479 {
00480     unsigned char * tprev = NULL;
00481     unsigned char * t = NULL;
00482     int tdel = 0;
00483     int tl = dl;
00484     struct indexEntry_s ieprev;
00485 
00486 /*@-boundswrite@*/
00487     memset(&ieprev, 0, sizeof(ieprev));
00488 /*@=boundswrite@*/
00489     for (; il > 0; il--, pe++) {
00490         struct indexEntry_s ie;
00491         int_32 type;
00492 
00493         ie.info.tag = ntohl(pe->tag);
00494         ie.info.type = ntohl(pe->type);
00495         ie.info.count = ntohl(pe->count);
00496         ie.info.offset = ntohl(pe->offset);
00497 
00498         if (hdrchkType(ie.info.type))
00499             return -1;
00500         if (hdrchkData(ie.info.count))
00501             return -1;
00502         if (hdrchkData(ie.info.offset))
00503             return -1;
00504 /*@-boundsread@*/
00505         if (hdrchkAlign(ie.info.type, ie.info.offset))
00506             return -1;
00507 /*@=boundsread@*/
00508 
00509         ie.data = t = dataStart + ie.info.offset;
00510         if (dataEnd && t >= dataEnd)
00511             return -1;
00512 
00513         ie.length = dataLength(ie.info.type, ie.data, ie.info.count, 1, dataEnd);
00514         if (ie.length < 0 || hdrchkData(ie.length))
00515             return -1;
00516 
00517         ie.rdlen = 0;
00518 
00519         if (entry) {
00520             ie.info.offset = regionid;
00521 /*@-boundswrite@*/
00522             *entry = ie;        /* structure assignment */
00523 /*@=boundswrite@*/
00524             entry++;
00525         }
00526 
00527         /* Alignment */
00528         type = ie.info.type;
00529 /*@-boundsread@*/
00530         if (typeSizes[type] > 1) {
00531             unsigned diff;
00532             diff = typeSizes[type] - (dl % typeSizes[type]);
00533             if (diff != typeSizes[type]) {
00534                 dl += diff;
00535                 if (ieprev.info.type == RPM_I18NSTRING_TYPE)
00536                     ieprev.length += diff;
00537             }
00538         }
00539 /*@=boundsread@*/
00540         tdel = (tprev ? (t - tprev) : 0);
00541         if (ieprev.info.type == RPM_I18NSTRING_TYPE)
00542             tdel = ieprev.length;
00543 
00544         if (ie.info.tag >= HEADER_I18NTABLE) {
00545             tprev = t;
00546         } else {
00547             tprev = dataStart;
00548             /* XXX HEADER_IMAGE tags don't include region sub-tag. */
00549             /*@-sizeoftype@*/
00550             if (ie.info.tag == HEADER_IMAGE)
00551                 tprev -= REGION_TAG_COUNT;
00552             /*@=sizeoftype@*/
00553         }
00554 
00555         /* Perform endian conversions */
00556         switch (ntohl(pe->type)) {
00557 /*@-bounds@*/
00558         case RPM_INT32_TYPE:
00559         {   int_32 * it = (int_32 *)t;
00560             for (; ie.info.count > 0; ie.info.count--, it += 1) {
00561                 if (dataEnd && ((unsigned char *)it) >= dataEnd)
00562                     return -1;
00563                 *it = htonl(*it);
00564             }
00565             t = (char *) it;
00566         }   /*@switchbreak@*/ break;
00567         case RPM_INT16_TYPE:
00568         {   int_16 * it = (int_16 *) t;
00569             for (; ie.info.count > 0; ie.info.count--, it += 1) {
00570                 if (dataEnd && ((unsigned char *)it) >= dataEnd)
00571                     return -1;
00572                 *it = htons(*it);
00573             }
00574             t = (char *) it;
00575         }   /*@switchbreak@*/ break;
00576 /*@=bounds@*/
00577         default:
00578             t += ie.length;
00579             /*@switchbreak@*/ break;
00580         }
00581 
00582         dl += ie.length;
00583         tl += tdel;
00584         ieprev = ie;    /* structure assignment */
00585 
00586     }
00587     tdel = (tprev ? (t - tprev) : 0);
00588     tl += tdel;
00589 
00590     /* XXX
00591      * There are two hacks here:
00592      *  1) tl is 16b (i.e. REGION_TAG_COUNT) short while doing headerReload().
00593      *  2) the 8/98 rpm bug with inserting i18n tags needs to use tl, not dl.
00594      */
00595     /*@-sizeoftype@*/
00596     if (tl+REGION_TAG_COUNT == dl)
00597         tl += REGION_TAG_COUNT;
00598     /*@=sizeoftype@*/
00599 
00600     return dl;
00601 }
00602 
00608 static /*@only@*/ /*@null@*/ void * doHeaderUnload(Header h,
00609                 /*@out@*/ int * lengthPtr)
00610         /*@modifies h, *lengthPtr @*/
00611         /*@requires maxSet(lengthPtr) >= 0 @*/
00612         /*@ensures maxRead(result) == (*lengthPtr) @*/
00613 {
00614     int_32 * ei = NULL;
00615     entryInfo pe;
00616     char * dataStart;
00617     char * te;
00618     unsigned pad;
00619     unsigned len;
00620     int_32 il = 0;
00621     int_32 dl = 0;
00622     indexEntry entry; 
00623     int_32 type;
00624     int i;
00625     int drlen, ndribbles;
00626     int driplen, ndrips;
00627     int legacy = 0;
00628 
00629     /* Sort entries by (offset,tag). */
00630     headerUnsort(h);
00631 
00632     /* Compute (il,dl) for all tags, including those deleted in region. */
00633     pad = 0;
00634     drlen = ndribbles = driplen = ndrips = 0;
00635     for (i = 0, entry = h->index; i < h->indexUsed; i++, entry++) {
00636         if (ENTRY_IS_REGION(entry)) {
00637             int_32 rdl = -entry->info.offset;   /* negative offset */
00638             int_32 ril = rdl/sizeof(*pe);
00639             int rid = entry->info.offset;
00640 
00641             il += ril;
00642             dl += entry->rdlen + entry->info.count;
00643             /* XXX Legacy regions do not include the region tag and data. */
00644             if (i == 0 && (h->flags & HEADERFLAG_LEGACY))
00645                 il += 1;
00646 
00647             /* Skip rest of entries in region, but account for dribbles. */
00648             for (; i < h->indexUsed && entry->info.offset <= rid+1; i++, entry++) {
00649                 if (entry->info.offset <= rid)
00650                     /*@innercontinue@*/ continue;
00651 
00652                 /* Alignment */
00653                 type = entry->info.type;
00654                 if (typeSizes[type] > 1) {
00655                     unsigned diff;
00656                     diff = typeSizes[type] - (dl % typeSizes[type]);
00657                     if (diff != typeSizes[type]) {
00658                         drlen += diff;
00659                         pad += diff;
00660                         dl += diff;
00661                     }
00662                 }
00663 
00664                 ndribbles++;
00665                 il++;
00666                 drlen += entry->length;
00667                 dl += entry->length;
00668             }
00669             i--;
00670             entry--;
00671             continue;
00672         }
00673 
00674         /* Ignore deleted drips. */
00675         if (entry->data == NULL || entry->length <= 0)
00676             continue;
00677 
00678         /* Alignment */
00679         type = entry->info.type;
00680         if (typeSizes[type] > 1) {
00681             unsigned diff;
00682             diff = typeSizes[type] - (dl % typeSizes[type]);
00683             if (diff != typeSizes[type]) {
00684                 driplen += diff;
00685                 pad += diff;
00686                 dl += diff;
00687             } else
00688                 diff = 0;
00689         }
00690 
00691         ndrips++;
00692         il++;
00693         driplen += entry->length;
00694         dl += entry->length;
00695     }
00696 
00697     /* Sanity checks on header intro. */
00698     if (hdrchkTags(il) || hdrchkData(dl))
00699         goto errxit;
00700 
00701     len = sizeof(il) + sizeof(dl) + (il * sizeof(*pe)) + dl;
00702 
00703 /*@-boundswrite@*/
00704     ei = xmalloc(len);
00705     ei[0] = htonl(il);
00706     ei[1] = htonl(dl);
00707 /*@=boundswrite@*/
00708 
00709     pe = (entryInfo) &ei[2];
00710     dataStart = te = (char *) (pe + il);
00711 
00712     pad = 0;
00713     for (i = 0, entry = h->index; i < h->indexUsed; i++, entry++) {
00714         const char * src;
00715 char *t;
00716         int count;
00717         int rdlen;
00718 
00719         if (entry->data == NULL || entry->length <= 0)
00720             continue;
00721 
00722 t = te;
00723         pe->tag = htonl(entry->info.tag);
00724         pe->type = htonl(entry->info.type);
00725         pe->count = htonl(entry->info.count);
00726 
00727         if (ENTRY_IS_REGION(entry)) {
00728             int_32 rdl = -entry->info.offset;   /* negative offset */
00729             int_32 ril = rdl/sizeof(*pe) + ndribbles;
00730             int rid = entry->info.offset;
00731 
00732             src = (char *)entry->data;
00733             rdlen = entry->rdlen;
00734 
00735             /* XXX Legacy regions do not include the region tag and data. */
00736             if (i == 0 && (h->flags & HEADERFLAG_LEGACY)) {
00737                 int_32 stei[4];
00738 
00739                 legacy = 1;
00740 /*@-boundswrite@*/
00741                 memcpy(pe+1, src, rdl);
00742                 memcpy(te, src + rdl, rdlen);
00743 /*@=boundswrite@*/
00744                 te += rdlen;
00745 
00746                 pe->offset = htonl(te - dataStart);
00747                 stei[0] = pe->tag;
00748                 stei[1] = pe->type;
00749                 stei[2] = htonl(-rdl-entry->info.count);
00750                 stei[3] = pe->count;
00751 /*@-boundswrite@*/
00752                 memcpy(te, stei, entry->info.count);
00753 /*@=boundswrite@*/
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 /*@-boundswrite@*/
00765                 memcpy(pe+1, src + sizeof(*pe), ((ril-1) * sizeof(*pe)));
00766                 memcpy(te, src + (ril * sizeof(*pe)), rdlen+entry->info.count+drlen);
00767 /*@=boundswrite@*/
00768                 te += rdlen;
00769                 {   /*@-castexpose@*/
00770                     entryInfo se = (entryInfo)src;
00771                     /*@=castexpose@*/
00772                     int off = ntohl(se->offset);
00773                     pe->offset = (off) ? htonl(te - dataStart) : htonl(off);
00774                 }
00775                 te += entry->info.count + drlen;
00776 
00777                 count = regionSwab(NULL, ril, 0, pe, t, NULL, 0);
00778                 if (count != (rdlen + entry->info.count + drlen))
00779                     goto errxit;
00780             }
00781 
00782             /* Skip rest of entries in region. */
00783             while (i < h->indexUsed && entry->info.offset <= rid+1) {
00784                 i++;
00785                 entry++;
00786             }
00787             i--;
00788             entry--;
00789             pe += ril;
00790             continue;
00791         }
00792 
00793         /* Ignore deleted drips. */
00794         if (entry->data == NULL || entry->length <= 0)
00795             continue;
00796 
00797         /* Alignment */
00798         type = entry->info.type;
00799         if (typeSizes[type] > 1) {
00800             unsigned diff;
00801             diff = typeSizes[type] - ((te - dataStart) % typeSizes[type]);
00802             if (diff != typeSizes[type]) {
00803 /*@-boundswrite@*/
00804                 memset(te, 0, diff);
00805 /*@=boundswrite@*/
00806                 te += diff;
00807                 pad += diff;
00808             }
00809         }
00810 
00811         pe->offset = htonl(te - dataStart);
00812 
00813         /* copy data w/ endian conversions */
00814 /*@-boundswrite@*/
00815         switch (entry->info.type) {
00816         case RPM_INT32_TYPE:
00817             count = entry->info.count;
00818             src = entry->data;
00819             while (count--) {
00820                 *((int_32 *)te) = htonl(*((int_32 *)src));
00821                 /*@-sizeoftype@*/
00822                 te += sizeof(int_32);
00823                 src += sizeof(int_32);
00824                 /*@=sizeoftype@*/
00825             }
00826             /*@switchbreak@*/ break;
00827 
00828         case RPM_INT16_TYPE:
00829             count = entry->info.count;
00830             src = entry->data;
00831             while (count--) {
00832                 *((int_16 *)te) = htons(*((int_16 *)src));
00833                 /*@-sizeoftype@*/
00834                 te += sizeof(int_16);
00835                 src += sizeof(int_16);
00836                 /*@=sizeoftype@*/
00837             }
00838             /*@switchbreak@*/ break;
00839 
00840         default:
00841             memcpy(te, entry->data, entry->length);
00842             te += entry->length;
00843             /*@switchbreak@*/ break;
00844         }
00845 /*@=boundswrite@*/
00846         pe++;
00847     }
00848    
00849     /* Insure that there are no memcpy underruns/overruns. */
00850     if (((char *)pe) != dataStart)
00851         goto errxit;
00852     if ((((char *)ei)+len) != te)
00853         goto errxit;
00854 
00855     if (lengthPtr)
00856         *lengthPtr = len;
00857 
00858     h->flags &= ~HEADERFLAG_SORTED;
00859     headerSort(h);
00860 
00861     return (void *) ei;
00862 
00863 errxit:
00864     /*@-usereleased@*/
00865     ei = _free(ei);
00866     /*@=usereleased@*/
00867     return (void *) ei;
00868 }
00869 
00875 static /*@only@*/ /*@null@*/
00876 void * headerUnload(Header h)
00877         /*@modifies h @*/
00878 {
00879     int length;
00880 /*@-boundswrite@*/
00881     void * uh = doHeaderUnload(h, &length);
00882 /*@=boundswrite@*/
00883     return uh;
00884 }
00885 
00893 static /*@null@*/
00894 indexEntry findEntry(/*@null@*/ Header h, int_32 tag, int_32 type)
00895         /*@modifies h @*/
00896 {
00897     indexEntry entry, entry2, last;
00898     struct indexEntry_s key;
00899 
00900     if (h == NULL) return NULL;
00901     if (!(h->flags & HEADERFLAG_SORTED)) headerSort(h);
00902 
00903     key.info.tag = tag;
00904 
00905 /*@-boundswrite@*/
00906     entry2 = entry = 
00907         bsearch(&key, h->index, h->indexUsed, sizeof(*h->index), indexCmp);
00908 /*@=boundswrite@*/
00909     if (entry == NULL)
00910         return NULL;
00911 
00912     if (type == RPM_NULL_TYPE)
00913         return entry;
00914 
00915     /* look backwards */
00916     while (entry->info.tag == tag && entry->info.type != type &&
00917            entry > h->index) entry--;
00918 
00919     if (entry->info.tag == tag && entry->info.type == type)
00920         return entry;
00921 
00922     last = h->index + h->indexUsed;
00923     /*@-usereleased@*/ /* FIX: entry2 = entry. Code looks bogus as well. */
00924     while (entry2->info.tag == tag && entry2->info.type != type &&
00925            entry2 < last) entry2++;
00926     /*@=usereleased@*/
00927 
00928     if (entry->info.tag == tag && entry->info.type == type)
00929         return entry;
00930 
00931     return NULL;
00932 }
00933 
00943 static
00944 int headerRemoveEntry(Header h, int_32 tag)
00945         /*@modifies h @*/
00946 {
00947     indexEntry last = h->index + h->indexUsed;
00948     indexEntry entry, first;
00949     int ne;
00950 
00951     entry = findEntry(h, tag, RPM_NULL_TYPE);
00952     if (!entry) return 1;
00953 
00954     /* Make sure entry points to the first occurence of this tag. */
00955     while (entry > h->index && (entry - 1)->info.tag == tag)  
00956         entry--;
00957 
00958     /* Free data for tags being removed. */
00959     for (first = entry; first < last; first++) {
00960         void * data;
00961         if (first->info.tag != tag)
00962             break;
00963         data = first->data;
00964         first->data = NULL;
00965         first->length = 0;
00966         if (ENTRY_IN_REGION(first))
00967             continue;
00968         data = _free(data);
00969     }
00970 
00971     ne = (first - entry);
00972     if (ne > 0) {
00973         h->indexUsed -= ne;
00974         ne = last - first;
00975 /*@-boundswrite@*/
00976         if (ne > 0)
00977             memmove(entry, first, (ne * sizeof(*entry)));
00978 /*@=boundswrite@*/
00979     }
00980 
00981     return 0;
00982 }
00983 
00989 static /*@null@*/
00990 Header headerLoad(/*@kept@*/ void * uh)
00991         /*@modifies uh @*/
00992 {
00993     int_32 * ei = (int_32 *) uh;
00994     int_32 il = ntohl(ei[0]);           /* index length */
00995     int_32 dl = ntohl(ei[1]);           /* data length */
00996     /*@-sizeoftype@*/
00997     size_t pvlen = sizeof(il) + sizeof(dl) +
00998                (il * sizeof(struct entryInfo_s)) + dl;
00999     /*@=sizeoftype@*/
01000     void * pv = uh;
01001     Header h = NULL;
01002     entryInfo pe;
01003     unsigned char * dataStart;
01004     unsigned char * dataEnd;
01005     indexEntry entry; 
01006     int rdlen;
01007     int i;
01008 
01009     /* Sanity checks on header intro. */
01010     if (hdrchkTags(il) || hdrchkData(dl))
01011         goto errxit;
01012 
01013     ei = (int_32 *) pv;
01014     /*@-castexpose@*/
01015     pe = (entryInfo) &ei[2];
01016     /*@=castexpose@*/
01017     dataStart = (unsigned char *) (pe + il);
01018     dataEnd = dataStart + dl;
01019 
01020     h = xcalloc(1, sizeof(*h));
01021     /*@-assignexpose@*/
01022     h->hv = *hdrVec;            /* structure assignment */
01023     /*@=assignexpose@*/
01024     /*@-assignexpose -kepttrans@*/
01025     h->blob = uh;
01026     /*@=assignexpose =kepttrans@*/
01027     h->indexAlloced = il + 1;
01028     h->indexUsed = il;
01029     h->index = xcalloc(h->indexAlloced, sizeof(*h->index));
01030     h->flags |= HEADERFLAG_SORTED;
01031     h->nrefs = 0;
01032     h = headerLink(h);
01033 
01034     /*
01035      * XXX XFree86-libs, ash, and pdksh from Red Hat 5.2 have bogus
01036      * %verifyscript tag that needs to be diddled.
01037      */
01038     if (ntohl(pe->tag) == 15 &&
01039         ntohl(pe->type) == RPM_STRING_TYPE &&
01040         ntohl(pe->count) == 1)
01041     {
01042         pe->tag = htonl(1079);
01043     }
01044 
01045     entry = h->index;
01046     i = 0;
01047     if (!(htonl(pe->tag) < HEADER_I18NTABLE)) {
01048         h->flags |= HEADERFLAG_LEGACY;
01049         entry->info.type = REGION_TAG_TYPE;
01050         entry->info.tag = HEADER_IMAGE;
01051         /*@-sizeoftype@*/
01052         entry->info.count = REGION_TAG_COUNT;
01053         /*@=sizeoftype@*/
01054         entry->info.offset = ((unsigned char *)pe - dataStart); /* negative offset */
01055 
01056         /*@-assignexpose@*/
01057         entry->data = pe;
01058         /*@=assignexpose@*/
01059         entry->length = pvlen - sizeof(il) - sizeof(dl);
01060         rdlen = regionSwab(entry+1, il, 0, pe, dataStart, dataEnd, entry->info.offset);
01061 #if 0   /* XXX don't check, the 8/98 i18n bug fails here. */
01062         if (rdlen != dl)
01063             goto errxit;
01064 #endif
01065         entry->rdlen = rdlen;
01066         entry++;
01067         h->indexUsed++;
01068     } else {
01069         int_32 rdl;
01070         int_32 ril;
01071 
01072         h->flags &= ~HEADERFLAG_LEGACY;
01073 
01074         entry->info.type = htonl(pe->type);
01075         entry->info.count = htonl(pe->count);
01076 
01077         if (hdrchkType(entry->info.type))
01078             goto errxit;
01079         if (hdrchkTags(entry->info.count))
01080             goto errxit;
01081 
01082         {   int off = ntohl(pe->offset);
01083 
01084             if (hdrchkData(off))
01085                 goto errxit;
01086             if (off) {
01087 /*@-sizeoftype@*/
01088                 size_t nb = REGION_TAG_COUNT;
01089 /*@=sizeoftype@*/
01090                 int_32 * stei = memcpy(alloca(nb), dataStart + off, nb);
01091                 rdl = -ntohl(stei[2]);  /* negative offset */
01092                 ril = rdl/sizeof(*pe);
01093                 if (hdrchkTags(ril) || hdrchkData(rdl))
01094                     goto errxit;
01095                 entry->info.tag = htonl(pe->tag);
01096             } else {
01097                 ril = il;
01098                 /*@-sizeoftype@*/
01099                 rdl = (ril * sizeof(struct entryInfo_s));
01100                 /*@=sizeoftype@*/
01101                 entry->info.tag = HEADER_IMAGE;
01102             }
01103         }
01104         entry->info.offset = -rdl;      /* negative offset */
01105 
01106         /*@-assignexpose@*/
01107         entry->data = pe;
01108         /*@=assignexpose@*/
01109         entry->length = pvlen - sizeof(il) - sizeof(dl);
01110         rdlen = regionSwab(entry+1, ril-1, 0, pe+1, dataStart, dataEnd, entry->info.offset);
01111         if (rdlen < 0)
01112             goto errxit;
01113         entry->rdlen = rdlen;
01114 
01115         if (ril < h->indexUsed) {
01116             indexEntry newEntry = entry + ril;
01117             int ne = (h->indexUsed - ril);
01118             int rid = entry->info.offset+1;
01119             int rc;
01120 
01121             /* Load dribble entries from region. */
01122             rc = regionSwab(newEntry, ne, 0, pe+ril, dataStart, dataEnd, rid);
01123             if (rc < 0)
01124                 goto errxit;
01125             rdlen += rc;
01126 
01127           { indexEntry firstEntry = newEntry;
01128             int save = h->indexUsed;
01129             int j;
01130 
01131             /* Dribble entries replace duplicate region entries. */
01132             h->indexUsed -= ne;
01133             for (j = 0; j < ne; j++, newEntry++) {
01134                 (void) headerRemoveEntry(h, newEntry->info.tag);
01135                 if (newEntry->info.tag == HEADER_BASENAMES)
01136                     (void) headerRemoveEntry(h, HEADER_OLDFILENAMES);
01137             }
01138 
01139             /* If any duplicate entries were replaced, move new entries down. */
01140 /*@-boundswrite@*/
01141             if (h->indexUsed < (save - ne)) {
01142                 memmove(h->index + h->indexUsed, firstEntry,
01143                         (ne * sizeof(*entry)));
01144             }
01145 /*@=boundswrite@*/
01146             h->indexUsed += ne;
01147           }
01148         }
01149     }
01150 
01151     h->flags &= ~HEADERFLAG_SORTED;
01152     headerSort(h);
01153 
01154     /*@-globstate -observertrans @*/
01155     return h;
01156     /*@=globstate =observertrans @*/
01157 
01158 errxit:
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 
01179 static /*@null@*/
01180 Header headerReload(/*@only@*/ Header h, int tag)
01181         /*@modifies h @*/
01182 {
01183     Header nh;
01184     int length;
01185     /*@-onlytrans@*/
01186 /*@-boundswrite@*/
01187     void * uh = doHeaderUnload(h, &length);
01188 /*@=boundswrite@*/
01189 
01190     h = headerFree(h);
01191     /*@=onlytrans@*/
01192     if (uh == NULL)
01193         return NULL;
01194     nh = headerLoad(uh);
01195     if (nh == NULL) {
01196         uh = _free(uh);
01197         return NULL;
01198     }
01199     if (nh->flags & HEADERFLAG_ALLOCATED)
01200         uh = _free(uh);
01201     nh->flags |= HEADERFLAG_ALLOCATED;
01202     if (ENTRY_IS_REGION(nh->index)) {
01203 /*@-boundswrite@*/
01204         if (tag == HEADER_SIGNATURES || tag == HEADER_IMMUTABLE)
01205             nh->index[0].info.tag = tag;
01206 /*@=boundswrite@*/
01207     }
01208     return nh;
01209 }
01210 
01216 static /*@null@*/
01217 Header headerCopyLoad(const void * uh)
01218         /*@*/
01219 {
01220     int_32 * ei = (int_32 *) uh;
01221 /*@-boundsread@*/
01222     int_32 il = ntohl(ei[0]);           /* index length */
01223     int_32 dl = ntohl(ei[1]);           /* data length */
01224 /*@=boundsread@*/
01225     /*@-sizeoftype@*/
01226     size_t pvlen = sizeof(il) + sizeof(dl) +
01227                         (il * sizeof(struct entryInfo_s)) + dl;
01228     /*@=sizeoftype@*/
01229     void * nuh = NULL;
01230     Header h = NULL;
01231 
01232     /* Sanity checks on header intro. */
01233     /*@-branchstate@*/
01234     if (!(hdrchkTags(il) || hdrchkData(dl)) && pvlen < headerMaxbytes) {
01235 /*@-boundsread@*/
01236         nuh = memcpy(xmalloc(pvlen), uh, pvlen);
01237 /*@=boundsread@*/
01238         if ((h = headerLoad(nuh)) != NULL)
01239             h->flags |= HEADERFLAG_ALLOCATED;
01240     }
01241     /*@=branchstate@*/
01242     /*@-branchstate@*/
01243     if (h == NULL)
01244         nuh = _free(nuh);
01245     /*@=branchstate@*/
01246     return h;
01247 }
01248 
01255 static /*@null@*/
01256 Header headerRead(FD_t fd, enum hMagic magicp)
01257         /*@modifies fd @*/
01258 {
01259     int_32 block[4];
01260     int_32 reserved;
01261     int_32 * ei = NULL;
01262     int_32 il;
01263     int_32 dl;
01264     int_32 magic;
01265     Header h = NULL;
01266     size_t len;
01267     int i;
01268 
01269     memset(block, 0, sizeof(block));
01270     i = 2;
01271     if (magicp == HEADER_MAGIC_YES)
01272         i += 2;
01273 
01274     /*@-type@*/ /* FIX: cast? */
01275     if (timedRead(fd, (char *)block, i*sizeof(*block)) != (i * sizeof(*block)))
01276         goto exit;
01277     /*@=type@*/
01278 
01279     i = 0;
01280 
01281 /*@-boundsread@*/
01282     if (magicp == HEADER_MAGIC_YES) {
01283         magic = block[i++];
01284         if (memcmp(&magic, header_magic, sizeof(magic)))
01285             goto exit;
01286         reserved = block[i++];
01287     }
01288     
01289     il = ntohl(block[i]);       i++;
01290     dl = ntohl(block[i]);       i++;
01291 /*@=boundsread@*/
01292 
01293     /*@-sizeoftype@*/
01294     len = sizeof(il) + sizeof(dl) + (il * sizeof(struct entryInfo_s)) + dl;
01295     /*@=sizeoftype@*/
01296 
01297     /* Sanity checks on header intro. */
01298     if (hdrchkTags(il) || hdrchkData(dl) || len > headerMaxbytes)
01299         goto exit;
01300 
01301 /*@-boundswrite@*/
01302     ei = xmalloc(len);
01303     ei[0] = htonl(il);
01304     ei[1] = htonl(dl);
01305     len -= sizeof(il) + sizeof(dl);
01306 /*@=boundswrite@*/
01307 
01308 /*@-boundsread@*/
01309     /*@-type@*/ /* FIX: cast? */
01310     if (timedRead(fd, (char *)&ei[2], len) != len)
01311         goto exit;
01312     /*@=type@*/
01313 /*@=boundsread@*/
01314     
01315     h = headerLoad(ei);
01316 
01317 exit:
01318     if (h) {
01319         if (h->flags & HEADERFLAG_ALLOCATED)
01320             ei = _free(ei);
01321         h->flags |= HEADERFLAG_ALLOCATED;
01322     } else if (ei)
01323         ei = _free(ei);
01324     /*@-mustmod@*/      /* FIX: timedRead macro obscures annotation */
01325     return h;
01326     /*@-mustmod@*/
01327 }
01328 
01336 static
01337 int headerWrite(FD_t fd, /*@null@*/ Header h, enum hMagic magicp)
01338         /*@globals fileSystem @*/
01339         /*@modifies fd, h, fileSystem @*/
01340 {
01341     ssize_t nb;
01342     int length;
01343     const void * uh;
01344 
01345     if (h == NULL)
01346         return 1;
01347 /*@-boundswrite@*/
01348     uh = doHeaderUnload(h, &length);
01349 /*@=boundswrite@*/
01350     if (uh == NULL)
01351         return 1;
01352     switch (magicp) {
01353     case HEADER_MAGIC_YES:
01354 /*@-boundsread@*/
01355         /*@-sizeoftype@*/
01356         nb = Fwrite(header_magic, sizeof(char), sizeof(header_magic), fd);
01357         /*@=sizeoftype@*/
01358 /*@=boundsread@*/
01359         if (nb != sizeof(header_magic))
01360             goto exit;
01361         break;
01362     case HEADER_MAGIC_NO:
01363         break;
01364     }
01365 
01366     /*@-sizeoftype@*/
01367     nb = Fwrite(uh, sizeof(char), length, fd);
01368     /*@=sizeoftype@*/
01369 
01370 exit:
01371     uh = _free(uh);
01372     return (nb == length ? 0 : 1);
01373 }
01374 
01381 static
01382 int headerIsEntry(/*@null@*/Header h, int_32 tag)
01383         /*@*/
01384 {
01385     /*@-mods@*/         /*@ FIX: h modified by sort. */
01386     return (findEntry(h, tag, RPM_NULL_TYPE) ? 1 : 0);
01387     /*@=mods@*/ 
01388 }
01389 
01400 static int copyEntry(const indexEntry entry,
01401                 /*@null@*/ /*@out@*/ hTYP_t type,
01402                 /*@null@*/ /*@out@*/ hPTR_t * p,
01403                 /*@null@*/ /*@out@*/ hCNT_t c,
01404                 int minMem)
01405         /*@modifies *type, *p, *c @*/
01406         /*@requires maxSet(type) >= 0 /\ maxSet(p) >= 0 /\ maxSet(c) >= 0 @*/
01407 {
01408     int_32 count = entry->info.count;
01409     int rc = 1;         /* XXX 1 on success. */
01410 
01411     if (p)
01412     switch (entry->info.type) {
01413     case RPM_BIN_TYPE:
01414         /*
01415          * XXX This only works for
01416          * XXX  "sealed" HEADER_IMMUTABLE/HEADER_SIGNATURES/HEADER_IMAGE.
01417          * XXX This will *not* work for unsealed legacy HEADER_IMAGE (i.e.
01418          * XXX a legacy header freshly read, but not yet unloaded to the rpmdb).
01419          */
01420         if (ENTRY_IS_REGION(entry)) {
01421             int_32 * ei = ((int_32 *)entry->data) - 2;
01422             /*@-castexpose@*/
01423             entryInfo pe = (entryInfo) (ei + 2);
01424             /*@=castexpose@*/
01425 /*@-boundsread@*/
01426             char * dataStart = (char *) (pe + ntohl(ei[0]));
01427 /*@=boundsread@*/
01428             int_32 rdl = -entry->info.offset;   /* negative offset */
01429             int_32 ril = rdl/sizeof(*pe);
01430 
01431             /*@-sizeoftype@*/
01432             rdl = entry->rdlen;
01433             count = 2 * sizeof(*ei) + (ril * sizeof(*pe)) + rdl;
01434             if (entry->info.tag == HEADER_IMAGE) {
01435                 ril -= 1;
01436                 pe += 1;
01437             } else {
01438                 count += REGION_TAG_COUNT;
01439                 rdl += REGION_TAG_COUNT;
01440             }
01441 
01442 /*@-bounds@*/
01443             *p = xmalloc(count);
01444             ei = (int_32 *) *p;
01445             ei[0] = htonl(ril);
01446             ei[1] = htonl(rdl);
01447 
01448             /*@-castexpose@*/
01449             pe = (entryInfo) memcpy(ei + 2, pe, (ril * sizeof(*pe)));
01450             /*@=castexpose@*/
01451 
01452             dataStart = (char *) memcpy(pe + ril, dataStart, rdl);
01453             /*@=sizeoftype@*/
01454 /*@=bounds@*/
01455 
01456             rc = regionSwab(NULL, ril, 0, pe, dataStart, dataStart + rdl, 0);
01457             /* XXX 1 on success. */
01458             rc = (rc < 0) ? 0 : 1;
01459         } else {
01460             count = entry->length;
01461             *p = (!minMem
01462                 ? memcpy(xmalloc(count), entry->data, count)
01463                 : entry->data);
01464         }
01465         break;
01466     case RPM_STRING_TYPE:
01467         if (count == 1) {
01468             *p = entry->data;
01469             break;
01470         }
01471         /*@fallthrough@*/
01472     case RPM_STRING_ARRAY_TYPE:
01473     case RPM_I18NSTRING_TYPE:
01474     {   const char ** ptrEntry;
01475         /*@-sizeoftype@*/
01476         int tableSize = count * sizeof(char *);
01477         /*@=sizeoftype@*/
01478         char * t;
01479         int i;
01480 
01481 /*@-bounds@*/
01482         /*@-mods@*/
01483         if (minMem) {
01484             *p = xmalloc(tableSize);
01485             ptrEntry = (const char **) *p;
01486             t = entry->data;
01487         } else {
01488             t = xmalloc(tableSize + entry->length);
01489             *p = (void *)t;
01490             ptrEntry = (const char **) *p;
01491             t += tableSize;
01492             memcpy(t, entry->data, entry->length);
01493         }
01494         /*@=mods@*/
01495 /*@=bounds@*/
01496         for (i = 0; i < count; i++) {
01497 /*@-boundswrite@*/
01498             *ptrEntry++ = t;
01499 /*@=boundswrite@*/
01500             t = strchr(t, 0);
01501             t++;
01502         }
01503     }   break;
01504 
01505     default:
01506         *p = entry->data;
01507         break;
01508     }
01509     if (type) *type = entry->info.type;
01510     if (c) *c = count;
01511     return rc;
01512 }
01513 
01532 static int headerMatchLocale(const char *td, const char *l, const char *le)
01533         /*@*/
01534 {
01535     const char *fe;
01536 
01537 
01538 #if 0
01539   { const char *s, *ll, *CC, *EE, *dd;
01540     char *lbuf, *t.
01541 
01542     /* Copy the buffer and parse out components on the fly. */
01543     lbuf = alloca(le - l + 1);
01544     for (s = l, ll = t = lbuf; *s; s++, t++) {
01545         switch (*s) {
01546         case '_':
01547             *t = '\0';
01548             CC = t + 1;
01549             break;
01550         case '.':
01551             *t = '\0';
01552             EE = t + 1;
01553             break;
01554         case '@':
01555             *t = '\0';
01556             dd = t + 1;
01557             break;
01558         default:
01559             *t = *s;
01560             break;
01561         }
01562     }
01563 
01564     if (ll)     /* ISO language should be lower case */
01565         for (t = ll; *t; t++)   *t = tolower(*t);
01566     if (CC)     /* ISO country code should be upper case */
01567         for (t = CC; *t; t++)   *t = toupper(*t);
01568 
01569     /* There are a total of 16 cases to attempt to match. */
01570   }
01571 #endif
01572 
01573     /* First try a complete match. */
01574     if (strlen(td) == (le-l) && !strncmp(td, l, (le - l)))
01575         return 1;
01576 
01577     /* Next, try stripping optional dialect and matching.  */
01578     for (fe = l; fe < le && *fe != '@'; fe++)
01579         {};
01580     if (fe < le && !strncmp(td, l, (fe - l)))
01581         return 1;
01582 
01583     /* Next, try stripping optional codeset and matching.  */
01584     for (fe = l; fe < le && *fe != '.'; fe++)
01585         {};
01586     if (fe < le && !strncmp(td, l, (fe - l)))
01587         return 1;
01588 
01589     /* Finally, try stripping optional country code and matching. */
01590     for (fe = l; fe < le && *fe != '_'; fe++)
01591         {};
01592     if (fe < le && !strncmp(td, l, (fe - l)))
01593         return 2;
01594 
01595     return 0;
01596 }
01597 
01604 /*@dependent@*/ /*@exposed@*/ static char *
01605 headerFindI18NString(Header h, indexEntry entry)
01606         /*@*/
01607 {
01608     const char *lang, *l, *le;
01609     indexEntry table;
01610 
01611     /* XXX Drepper sez' this is the order. */
01612     if ((lang = getenv("LANGUAGE")) == NULL &&
01613         (lang = getenv("LC_ALL")) == NULL &&
01614         (lang = getenv("LC_MESSAGES")) == NULL &&
01615         (lang = getenv("LANG")) == NULL)
01616             return entry->data;
01617     
01618     /*@-mods@*/
01619     if ((table = findEntry(h, HEADER_I18NTABLE, RPM_STRING_ARRAY_TYPE)) == NULL)
01620         return entry->data;
01621     /*@=mods@*/
01622 
01623 /*@-boundsread@*/
01624     for (l = lang; *l != '\0'; l = le) {
01625         const char *td;
01626         char *ed, *ed_weak = NULL;
01627         int langNum;
01628 
01629         while (*l && *l == ':')                 /* skip leading colons */
01630             l++;
01631         if (*l == '\0')
01632             break;
01633         for (le = l; *le && *le != ':'; le++)   /* find end of this locale */
01634             {};
01635 
01636         /* For each entry in the header ... */
01637         for (langNum = 0, td = table->data, ed = entry->data;
01638              langNum < entry->info.count;
01639              langNum++, td += strlen(td) + 1, ed += strlen(ed) + 1) {
01640 
01641                    int match = headerMatchLocale(td, l, le);
01642                    if (match == 1) return ed;
01643                    else if (match == 2) ed_weak = ed;
01644 
01645         }
01646         if (ed_weak) return ed_weak;
01647     }
01648 /*@=boundsread@*/
01649 
01650     return entry->data;
01651 }
01652 
01663 static int intGetEntry(Header h, int_32 tag,
01664                 /*@null@*/ /*@out@*/ hTAG_t type,
01665                 /*@null@*/ /*@out@*/ hPTR_t * p,
01666                 /*@null@*/ /*@out@*/ hCNT_t c,
01667                 int minMem)
01668         /*@modifies *type, *p, *c @*/
01669         /*@requires maxSet(type) >= 0 /\ maxSet(p) >= 0 /\ maxSet(c) >= 0 @*/
01670 {
01671     indexEntry entry;
01672     int rc;
01673 
01674     /* First find the tag */
01675     /*@-mods@*/         /*@ FIX: h modified by sort. */
01676     entry = findEntry(h, tag, RPM_NULL_TYPE);
01677     /*@mods@*/
01678     if (entry == NULL) {
01679         if (type) type = 0;
01680         if (p) *p = NULL;
01681         if (c) *c = 0;
01682         return 0;
01683     }
01684 
01685     switch (entry->info.type) {
01686     case RPM_I18NSTRING_TYPE:
01687         rc = 1;
01688         if (type) *type = RPM_STRING_TYPE;
01689         if (c) *c = 1;
01690         /*@-dependenttrans@*/
01691         if (p) *p = headerFindI18NString(h, entry);
01692         /*@=dependenttrans@*/
01693         break;
01694     default:
01695         rc = copyEntry(entry, type, p, c, minMem);
01696         break;
01697     }
01698 
01699     /* XXX 1 on success */
01700     return ((rc == 1) ? 1 : 0);
01701 }
01702 
01710 static /*@null@*/ void * headerFreeTag(/*@unused@*/ Header h,
01711                 /*@only@*/ /*@null@*/ const void * data, rpmTagType type)
01712         /*@modifies data @*/
01713 {
01714     if (data) {
01715         /*@-branchstate@*/
01716         if (type == -1 ||
01717             type == RPM_STRING_ARRAY_TYPE ||
01718             type == RPM_I18NSTRING_TYPE ||
01719             type == RPM_BIN_TYPE)
01720                 data = _free(data);
01721         /*@=branchstate@*/
01722     }
01723     return NULL;
01724 }
01725 
01739 static
01740 int headerGetEntry(Header h, int_32 tag,
01741                         /*@null@*/ /*@out@*/ hTYP_t type,
01742                         /*@null@*/ /*@out@*/ void ** p,
01743                         /*@null@*/ /*@out@*/ hCNT_t c)
01744         /*@modifies *type, *p, *c @*/
01745         /*@requires maxSet(type) >= 0 /\ maxSet(p) >= 0 /\ maxSet(c) >= 0 @*/
01746 {
01747     return intGetEntry(h, tag, type, (hPTR_t *)p, c, 0);
01748 }
01749 
01762 static
01763 int headerGetEntryMinMemory(Header h, int_32 tag,
01764                         /*@null@*/ /*@out@*/ hTYP_t type,
01765                         /*@null@*/ /*@out@*/ hPTR_t * p,
01766                         /*@null@*/ /*@out@*/ hCNT_t c)
01767         /*@modifies *type, *p, *c @*/
01768         /*@requires maxSet(type) >= 0 /\ maxSet(p) >= 0 /\ maxSet(c) >= 0 @*/
01769 {
01770     return intGetEntry(h, tag, type, p, c, 1);
01771 }
01772 
01773 int headerGetRawEntry(Header h, int_32 tag, int_32 * type, hPTR_t * p,
01774                 int_32 * c)
01775 {
01776     indexEntry entry;
01777     int rc;
01778 
01779     if (p == NULL) return headerIsEntry(h, tag);
01780 
01781     /* First find the tag */
01782     /*@-mods@*/         /*@ FIX: h modified by sort. */
01783     entry = findEntry(h, tag, RPM_NULL_TYPE);
01784     /*@=mods@*/
01785     if (!entry) {
01786         if (p) *p = NULL;
01787         if (c) *c = 0;
01788         return 0;
01789     }
01790 
01791     rc = copyEntry(entry, type, p, c, 0);
01792 
01793     /* XXX 1 on success */
01794     return ((rc == 1) ? 1 : 0);
01795 }
01796 
01799 static void copyData(int_32 type, /*@out@*/ void * dstPtr, const void * srcPtr,
01800                 int_32 cnt, int dataLength)
01801         /*@modifies *dstPtr @*/
01802 {
01803     switch (type) {
01804     case RPM_STRING_ARRAY_TYPE:
01805     case RPM_I18NSTRING_TYPE:
01806     {   const char ** av = (const char **) srcPtr;
01807         char * t = dstPtr;
01808 
01809 /*@-bounds@*/
01810         while (cnt-- > 0 && dataLength > 0) {
01811             const char * s;
01812             if ((s = *av++) == NULL)
01813                 continue;
01814             do {
01815                 *t++ = *s++;
01816             } while (s[-1] && --dataLength > 0);
01817         }
01818 /*@=bounds@*/
01819     }   break;
01820 
01821     default:
01822 /*@-boundswrite@*/
01823         memmove(dstPtr, srcPtr, dataLength);
01824 /*@=boundswrite@*/
01825         break;
01826     }
01827 }
01828 
01837 /*@null@*/
01838 static void *
01839 grabData(int_32 type, hPTR_t p, int_32 c, /*@out@*/ int * lengthPtr)
01840         /*@modifies *lengthPtr @*/
01841         /*@requires maxSet(lengthPtr) >= 0 @*/
01842 {
01843     void * data = NULL;
01844     int length;
01845 
01846     length = dataLength(type, p, c, 0, NULL);
01847 /*@-branchstate@*/
01848     if (length > 0) {
01849         data = xmalloc(length);
01850         copyData(type, data, p, c, length);
01851     }
01852 /*@=branchstate@*/
01853 
01854     if (lengthPtr)
01855         *lengthPtr = length;
01856     return data;
01857 }
01858 
01873 static
01874 int headerAddEntry(Header h, int_32 tag, int_32 type, const void * p, int_32 c)
01875         /*@modifies h @*/
01876 {
01877     indexEntry entry;
01878     void * data;
01879     int length;
01880 
01881     /* Count must always be >= 1 for headerAddEntry. */
01882     if (c <= 0)
01883         return 0;
01884 
01885     if (hdrchkType(type))
01886         return 0;
01887     if (hdrchkData(c))
01888         return 0;
01889 
01890     length = 0;
01891 /*@-boundswrite@*/
01892     data = grabData(type, p, c, &length);
01893 /*@=boundswrite@*/
01894     if (data == NULL || length <= 0)
01895         return 0;
01896 
01897     /* Allocate more index space if necessary */
01898     if (h->indexUsed == h->indexAlloced) {
01899         h->indexAlloced += INDEX_MALLOC_SIZE;
01900         h->index = xrealloc(h->index, h->indexAlloced * sizeof(*h->index));
01901     }
01902 
01903     /* Fill in the index */
01904     entry = h->index + h->indexUsed;
01905     entry->info.tag = tag;
01906     entry->info.type = type;
01907     entry->info.count = c;
01908     entry->info.offset = 0;
01909     entry->data = data;
01910     entry->length = length;
01911 
01912 /*@-boundsread@*/
01913     if (h->indexUsed > 0 && tag < h->index[h->indexUsed-1].info.tag)
01914         h->flags &= ~HEADERFLAG_SORTED;
01915 /*@=boundsread@*/
01916     h->indexUsed++;
01917 
01918     return 1;
01919 }
01920 
01935 static
01936 int headerAppendEntry(Header h, int_32 tag, int_32 type,
01937                 const void * p, int_32 c)
01938         /*@modifies h @*/
01939 {
01940     indexEntry entry;
01941     int length;
01942 
01943     if (type == RPM_STRING_TYPE || type == RPM_I18NSTRING_TYPE) {
01944         /* we can't do this */
01945         return 0;
01946     }
01947 
01948     /* Find the tag entry in the header. */
01949     entry = findEntry(h, tag, type);
01950     if (!entry)
01951         return 0;
01952 
01953     length = dataLength(type, p, c, 0, NULL);
01954     if (length < 0)
01955         return 0;
01956 
01957     if (ENTRY_IN_REGION(entry)) {
01958         char * t = xmalloc(entry->length + length);
01959 /*@-bounds@*/
01960         memcpy(t, entry->data, entry->length);
01961 /*@=bounds@*/
01962         entry->data = t;
01963         entry->info.offset = 0;
01964     } else
01965         entry->data = xrealloc(entry->data, entry->length + length);
01966 
01967     copyData(type, ((char *) entry->data) + entry->length, p, c, length);
01968 
01969     entry->length += length;
01970 
01971     entry->info.count += c;
01972 
01973     return 1;
01974 }
01975 
01986 static
01987 int headerAddOrAppendEntry(Header h, int_32 tag, int_32 type,
01988                 const void * p, int_32 c)
01989         /*@modifies h @*/
01990 {
01991     return (findEntry(h, tag, type)
01992         ? headerAppendEntry(h, tag, type, p, c)
01993         : headerAddEntry(h, tag, type, p, c));
01994 }
01995 
02016 static
02017 int headerAddI18NString(Header h, int_32 tag, const char * string,
02018                 const char * lang)
02019         /*@modifies h @*/
02020 {
02021     indexEntry table, entry;
02022     const char ** strArray;
02023     int length;
02024     int ghosts;
02025     int i, langNum;
02026     char * buf;
02027 
02028     table = findEntry(h, HEADER_I18NTABLE, RPM_STRING_ARRAY_TYPE);
02029     entry = findEntry(h, tag, RPM_I18NSTRING_TYPE);
02030 
02031     if (!table && entry)
02032         return 0;               /* this shouldn't ever happen!! */
02033 
02034     if (!table && !entry) {
02035         const char * charArray[2];
02036         int count = 0;
02037         if (!lang || (lang[0] == 'C' && lang[1] == '\0')) {
02038             /*@-observertrans -readonlytrans@*/
02039             charArray[count++] = "C";
02040             /*@=observertrans =readonlytrans@*/
02041         } else {
02042             /*@-observertrans -readonlytrans@*/
02043             charArray[count++] = "C";
02044             /*@=observertrans =readonlytrans@*/
02045             charArray[count++] = lang;
02046         }
02047         if (!headerAddEntry(h, HEADER_I18NTABLE, RPM_STRING_ARRAY_TYPE, 
02048                         &charArray, count))
02049             return 0;
02050         table = findEntry(h, HEADER_I18NTABLE, RPM_STRING_ARRAY_TYPE);
02051     }
02052 
02053     if (!table)
02054         return 0;
02055     /*@-branchstate@*/
02056     if (!lang) lang = "C";
02057     /*@=branchstate@*/
02058 
02059     {   const char * l = table->data;
02060         for (langNum = 0; langNum < table->info.count; langNum++) {
02061             if (!strcmp(l, lang)) break;
02062             l += strlen(l) + 1;
02063         }
02064     }
02065 
02066     if (langNum >= table->info.count) {
02067         length = strlen(lang) + 1;
02068         if (ENTRY_IN_REGION(table)) {
02069             char * t = xmalloc(table->length + length);
02070             memcpy(t, table->data, table->length);
02071             table->data = t;
02072             table->info.offset = 0;
02073         } else
02074             table->data = xrealloc(table->data, table->length + length);
02075         memmove(((char *)table->data) + table->length, lang, length);
02076         table->length += length;
02077         table->info.count++;
02078     }
02079 
02080     if (!entry) {
02081         strArray = alloca(sizeof(*strArray) * (langNum + 1));
02082         for (i = 0; i < langNum; i++)
02083             strArray[i] = "";
02084         strArray[langNum] = string;
02085         return headerAddEntry(h, tag, RPM_I18NSTRING_TYPE, strArray, 
02086                                 langNum + 1);
02087     } else if (langNum >= entry->info.count) {
02088         ghosts = langNum - entry->info.count;
02089         
02090         length = strlen(string) + 1 + ghosts;
02091         if (ENTRY_IN_REGION(entry)) {
02092             char * t = xmalloc(entry->length + length);
02093             memcpy(t, entry->data, entry->length);
02094             entry->data = t;
02095             entry->info.offset = 0;
02096         } else
02097             entry->data = xrealloc(entry->data, entry->length + length);
02098 
02099         memset(((char *)entry->data) + entry->length, '\0', ghosts);
02100         memmove(((char *)entry->data) + entry->length + ghosts, string, strlen(string)+1);
02101 
02102         entry->length += length;
02103         entry->info.count = langNum + 1;
02104     } else {
02105         char *b, *be, *e, *ee, *t;
02106         size_t bn, sn, en;
02107 
02108         /* Set beginning/end pointers to previous data */
02109         b = be = e = ee = entry->data;
02110         for (i = 0; i < table->info.count; i++) {
02111             if (i == langNum)
02112                 be = ee;
02113             ee += strlen(ee) + 1;
02114             if (i == langNum)
02115                 e  = ee;
02116         }
02117 
02118         /* Get storage for new buffer */
02119         bn = (be-b);
02120         sn = strlen(string) + 1;
02121         en = (ee-e);
02122         length = bn + sn + en;
02123         t = buf = xmalloc(length);
02124 
02125         /* Copy values into new storage */
02126         memcpy(t, b, bn);
02127         t += bn;
02128 /*@-mayaliasunique@*/
02129         memcpy(t, string, sn);
02130         t += sn;
02131         memcpy(t, e, en);
02132         t += en;
02133 /*@=mayaliasunique@*/
02134 
02135         /* Replace i18N string array */
02136         entry->length -= strlen(be) + 1;
02137         entry->length += sn;
02138         
02139         if (ENTRY_IN_REGION(entry)) {
02140             entry->info.offset = 0;
02141         } else
02142             entry->data = _free(entry->data);
02143         /*@-dependenttrans@*/
02144         entry->data = buf;
02145         /*@=dependenttrans@*/
02146     }
02147 
02148     return 0;
02149 }
02150 
02161 static
02162 int headerModifyEntry(Header h, int_32 tag, int_32 type,
02163                         const void * p, int_32 c)
02164         /*@modifies h @*/
02165 {
02166     indexEntry entry;
02167     void * oldData;
02168     void * data;
02169     int length;
02170 
02171     /* First find the tag */
02172     entry = findEntry(h, tag, type);
02173     if (!entry)
02174         return 0;
02175 
02176     length = 0;
02177     data = grabData(type, p, c, &length);
02178     if (data == NULL || length <= 0)
02179         return 0;
02180 
02181     /* make sure entry points to the first occurence of this tag */
02182     while (entry > h->index && (entry - 1)->info.tag == tag)  
02183         entry--;
02184 
02185     /* free after we've grabbed the new data in case the two are intertwined;
02186        that's a bad idea but at least we won't break */
02187     oldData = entry->data;
02188 
02189     entry->info.count = c;
02190     entry->info.type = type;
02191     entry->data = data;
02192     entry->length = length;
02193 
02194     /*@-branchstate@*/
02195     if (ENTRY_IN_REGION(entry)) {
02196         entry->info.offset = 0;
02197     } else
02198         oldData = _free(oldData);
02199     /*@=branchstate@*/
02200 
02201     return 1;
02202 }
02203 
02206 static char escapedChar(const char ch)  /*@*/
02207 {
02208     switch (ch) {
02209     case 'a':   return '\a';
02210     case 'b':   return '\b';
02211     case 'f':   return '\f';
02212     case 'n':   return '\n';
02213     case 'r':   return '\r';
02214     case 't':   return '\t';
02215     case 'v':   return '\v';
02216     default:    return ch;
02217     }
02218 }
02219 
02226 static /*@null@*/ sprintfToken
02227 freeFormat( /*@only@*/ /*@null@*/ sprintfToken format, int num)
02228         /*@modifies *format @*/
02229 {
02230     int i;
02231 
02232     if (format == NULL) return NULL;
02233 
02234     for (i = 0; i < num; i++) {
02235         switch (format[i].type) {
02236         case PTOK_ARRAY:
02237 /*@-boundswrite@*/
02238             format[i].u.array.format =
02239                 freeFormat(format[i].u.array.format,
02240                         format[i].u.array.numTokens);
02241 /*@=boundswrite@*/
02242             /*@switchbreak@*/ break;
02243         case PTOK_COND:
02244 /*@-boundswrite@*/
02245             format[i].u.cond.ifFormat =
02246                 freeFormat(format[i].u.cond.ifFormat, 
02247                         format[i].u.cond.numIfTokens);
02248             format[i].u.cond.elseFormat =
02249                 freeFormat(format[i].u.cond.elseFormat, 
02250                         format[i].u.cond.numElseTokens);
02251 /*@=boundswrite@*/
02252             /*@switchbreak@*/ break;
02253         case PTOK_NONE:
02254         case PTOK_TAG:
02255         case PTOK_STRING:
02256         default:
02257             /*@switchbreak@*/ break;
02258         }
02259     }
02260     format = _free(format);
02261     return NULL;
02262 }
02263 
02267 struct headerIterator_s {
02268 /*@unused@*/
02269     Header h;           
02270 /*@unused@*/
02271     int next_index;     
02272 };
02273 
02279 static /*@null@*/
02280 HeaderIterator headerFreeIterator(/*@only@*/ HeaderIterator hi)
02281         /*@modifies hi @*/
02282 {
02283     if (hi != NULL) {
02284         hi->h = headerFree(hi->h);
02285         hi = _free(hi);
02286     }
02287     return hi;
02288 }
02289 
02295 static
02296 HeaderIterator headerInitIterator(Header h)
02297         /*@modifies h */
02298 {
02299     HeaderIterator hi = xmalloc(sizeof(*hi));
02300 
02301     headerSort(h);
02302 
02303     hi->h = headerLink(h);
02304     hi->next_index = 0;
02305     return hi;
02306 }
02307 
02317 static
02318 int headerNextIterator(HeaderIterator hi,
02319                 /*@null@*/ /*@out@*/ hTAG_t tag,
02320                 /*@null@*/ /*@out@*/ hTYP_t type,
02321                 /*@null@*/ /*@out@*/ hPTR_t * p,
02322                 /*@null@*/ /*@out@*/ hCNT_t c)
02323         /*@modifies hi, *tag, *type, *p, *c @*/
02324         /*@requires maxSet(tag) >= 0 /\ maxSet(type) >= 0
02325                 /\ maxSet(p) >= 0 /\ maxSet(c) >= 0 @*/
02326 {
02327     Header h = hi->h;
02328     int slot = hi->next_index;
02329     indexEntry entry = NULL;
02330     int rc;
02331 
02332     for (slot = hi->next_index; slot < h->indexUsed; slot++) {
02333         entry = h->index + slot;
02334         if (!ENTRY_IS_REGION(entry))
02335             break;
02336     }
02337     hi->next_index = slot;
02338     if (entry == NULL || slot >= h->indexUsed)
02339         return 0;
02340 
02341     /*@-noeffect@*/     /* LCL: no clue */
02342     hi->next_index++;
02343     /*@=noeffect@*/
02344 
02345     if (tag)
02346         *tag = entry->info.tag;
02347 
02348     rc = copyEntry(entry, type, p, c, 0);
02349 
02350     /* XXX 1 on success */
02351     return ((rc == 1) ? 1 : 0);
02352 }
02353 
02359 static /*@null@*/
02360 Header headerCopy(Header h)
02361         /*@modifies h @*/
02362 {
02363     Header nh = headerNew();
02364     HeaderIterator hi;
02365     int_32 tag, type, count;
02366     hPTR_t ptr;
02367    
02368     /*@-branchstate@*/
02369     for (hi = headerInitIterator(h);
02370         headerNextIterator(hi, &tag, &type, &ptr, &count);
02371         ptr = headerFreeData((void *)ptr, type))
02372     {
02373         if (ptr) (void) headerAddEntry(nh, tag, type, ptr, count);
02374     }
02375     hi = headerFreeIterator(hi);
02376     /*@=branchstate@*/
02377 
02378     return headerReload(nh, HEADER_IMAGE);
02379 }
02380 
02383 typedef struct headerSprintfArgs_s {
02384     Header h;
02385     char * fmt;
02386 /*@temp@*/
02387     headerTagTableEntry tags;
02388 /*@temp@*/
02389     headerSprintfExtension exts;
02390 /*@observer@*/ /*@null@*/
02391     const char * errmsg;
02392     rpmec ec;
02393     sprintfToken format;
02394 /*@relnull@*/
02395     HeaderIterator hi;
02396 /*@owned@*/
02397     char * val;
02398     size_t vallen;
02399     size_t alloced;
02400     int numTokens;
02401     int i;
02402 } * headerSprintfArgs;
02403 
02409 static headerSprintfArgs hsaInit(/*@returned@*/ headerSprintfArgs hsa)
02410         /*@modifies hsa */
02411 {
02412     sprintfTag tag =
02413         (hsa->format->type == PTOK_TAG
02414             ? &hsa->format->u.tag :
02415         (hsa->format->type == PTOK_ARRAY
02416             ? &hsa->format->u.array.format->u.tag :
02417         NULL));
02418 
02419     if (hsa != NULL) {
02420         hsa->i = 0;
02421         if (tag != NULL && tag->tag == -2)
02422             hsa->hi = headerInitIterator(hsa->h);
02423     }
02424 /*@-nullret@*/
02425     return hsa;
02426 /*@=nullret@*/
02427 }
02428 
02434 /*@null@*/
02435 static sprintfToken hsaNext(/*@returned@*/ headerSprintfArgs hsa)
02436         /*@modifies hsa */
02437 {
02438     sprintfToken fmt = NULL;
02439     sprintfTag tag =
02440         (hsa->format->type == PTOK_TAG
02441             ? &hsa->format->u.tag :
02442         (hsa->format->type == PTOK_ARRAY
02443             ? &hsa->format->u.array.format->u.tag :
02444         NULL));
02445 
02446     if (hsa != NULL && hsa->i >= 0 && hsa->i < hsa->numTokens) {
02447         fmt = hsa->format + hsa->i;
02448         if (hsa->hi == NULL) {
02449             hsa->i++;
02450         } else {
02451             int_32 tagno;
02452             int_32 type;
02453             int_32 count;
02454 
02455 /*@-boundswrite@*/
02456             if (!headerNextIterator(hsa->hi, &tagno, &type, NULL, &count))
02457                 fmt = NULL;
02458             tag->tag = tagno;
02459 /*@=boundswrite@*/
02460         }
02461     }
02462 
02463 /*@-dependenttrans -onlytrans@*/
02464     return fmt;
02465 /*@=dependenttrans =onlytrans@*/
02466 }
02467 
02473 static headerSprintfArgs hsaFini(/*@returned@*/ headerSprintfArgs hsa)
02474         /*@modifies hsa */
02475 {
02476     if (hsa != NULL) {
02477         hsa->hi = headerFreeIterator(hsa->hi);
02478         hsa->i = 0;
02479     }
02480 /*@-nullret@*/
02481     return hsa;
02482 /*@=nullret@*/
02483 }
02484 
02491 /*@dependent@*/ /*@exposed@*/
02492 static char * hsaReserve(headerSprintfArgs hsa, size_t need)
02493         /*@modifies hsa */
02494 {
02495     if ((hsa->vallen + need) >= hsa->alloced) {
02496         if (hsa->alloced <= need)
02497             hsa->alloced += need;
02498         hsa->alloced <<= 1;
02499         hsa->val = xrealloc(hsa->val, hsa->alloced+1);  
02500     }
02501     return hsa->val + hsa->vallen;
02502 }
02503 
02511 /*@observer@*/ /*@null@*/
02512 static const char * myTagName(headerTagTableEntry tbl, int val)
02513         /*@*/
02514 {
02515     static char name[128];
02516     const char * s;
02517     char *t;
02518 
02519     for (; tbl->name != NULL; tbl++) {
02520         if (tbl->val == val)
02521             break;
02522     }
02523     if ((s = tbl->name) == NULL)
02524         return NULL;
02525     s += sizeof("RPMTAG_") - 1;
02526     t = name;
02527     *t++ = *s++;
02528     while (*s != '\0')
02529         *t++ = xtolower(*s++);
02530     *t = '\0';
02531     return name;
02532 }
02533 
02541 static int myTagValue(headerTagTableEntry tbl, const char * name)
02542         /*@*/
02543 {
02544     for (; tbl->name != NULL; tbl++) {
02545         if (!xstrcasecmp(tbl->name, name))
02546             return tbl->val;
02547     }
02548     return 0;
02549 }
02550 
02557 static int findTag(headerSprintfArgs hsa, sprintfToken token, const char * name)
02558         /*@modifies token @*/
02559 {
02560     headerSprintfExtension ext;
02561     sprintfTag stag = (token->type == PTOK_COND
02562         ? &token->u.cond.tag : &token->u.tag);
02563 
02564     stag->fmt = NULL;
02565     stag->ext = NULL;
02566     stag->extNum = 0;
02567     stag->tag = -1;
02568 
02569     if (!strcmp(name, "*")) {
02570         stag->tag = -2;
02571         goto bingo;
02572     }
02573 
02574 /*@-branchstate@*/
02575     if (strncmp("RPMTAG_", name, sizeof("RPMTAG_")-1)) {
02576 /*@-boundswrite@*/
02577         char * t = alloca(strlen(name) + sizeof("RPMTAG_"));
02578         (void) stpcpy( stpcpy(t, "RPMTAG_"), name);
02579         name = t;
02580 /*@=boundswrite@*/
02581     }
02582 /*@=branchstate@*/
02583 
02584     /* Search extensions for specific tag override. */
02585     for (ext = hsa->exts; ext != NULL && ext->type != HEADER_EXT_LAST;
02586         ext = (ext->type == HEADER_EXT_MORE ? ext->u.more : ext+1))
02587     {
02588         if (ext->name == NULL || ext->type != HEADER_EXT_TAG)
02589             continue;
02590         if (!xstrcasecmp(ext->name, name)) {
02591             stag->ext = ext->u.tagFunction;
02592             stag->extNum = ext - hsa->exts;
02593             goto bingo;
02594         }
02595     }
02596 
02597     /* Search tag names. */
02598     stag->tag = myTagValue(hsa->tags, name);
02599     if (stag->tag != 0)
02600         goto bingo;
02601 
02602     return 1;
02603 
02604 bingo:
02605     /* Search extensions for specific format. */
02606     if (stag->type != NULL)
02607     for (ext = hsa->exts; ext != NULL && ext->type != HEADER_EXT_LAST;
02608             ext = (ext->type == HEADER_EXT_MORE ? ext->u.more : ext+1))
02609     {
02610         if (ext->name == NULL || ext->type != HEADER_EXT_FORMAT)
02611             continue;
02612         if (!strcmp(ext->name, stag->type)) {
02613             stag->fmt = ext->u.formatFunction;
02614             break;
02615         }
02616     }
02617     return 0;
02618 }
02619 
02620 /* forward ref */
02628 static int parseExpression(headerSprintfArgs hsa, sprintfToken token,
02629                 char * str, /*@out@*/char ** endPtr)
02630         /*@modifies hsa, str, token, *endPtr @*/
02631         /*@requires maxSet(endPtr) >= 0 @*/;
02632 
02642 static int parseFormat(headerSprintfArgs hsa, /*@null@*/ char * str,
02643                 /*@out@*/sprintfToken * formatPtr, /*@out@*/int * numTokensPtr,
02644                 /*@null@*/ /*@out@*/ char ** endPtr, int state)
02645         /*@modifies hsa, str, *formatPtr, *numTokensPtr, *endPtr @*/
02646         /*@requires maxSet(formatPtr) >= 0 /\ maxSet(numTokensPtr) >= 0
02647                 /\ maxSet(endPtr) >= 0 @*/
02648 {
02649     char * chptr, * start, * next, * dst;
02650     sprintfToken format;
02651     sprintfToken token;
02652     int numTokens;
02653     int i;
02654     int done = 0;
02655 
02656     /* upper limit on number of individual formats */
02657     numTokens = 0;
02658     if (str != NULL)
02659     for (chptr = str; *chptr != '\0'; chptr++)
02660         if (*chptr == '%') numTokens++;
02661     numTokens = numTokens * 2 + 1;
02662 
02663     format = xcalloc(numTokens, sizeof(*format));
02664     if (endPtr) *endPtr = NULL;
02665 
02666     /*@-infloops@*/ /* LCL: can't detect done termination */
02667     dst = start = str;
02668     numTokens = 0;
02669     token = NULL;
02670     if (start != NULL)
02671     while (*start != '\0') {
02672         switch (*start) {
02673         case '%':
02674             /* handle %% */
02675             if (*(start + 1) == '%') {
02676                 if (token == NULL || token->type != PTOK_STRING) {
02677                     token = format + numTokens++;
02678                     token->type = PTOK_STRING;
02679                     /*@-temptrans -assignexpose@*/
02680                     dst = token->u.string.string = start;
02681                     /*@=temptrans =assignexpose@*/
02682                 }
02683                 start++;
02684 /*@-boundswrite@*/
02685                 *dst++ = *start++;
02686 /*@=boundswrite@*/
02687                 /*@switchbreak@*/ break;
02688             } 
02689 
02690             token = format + numTokens++;
02691 /*@-boundswrite@*/
02692             *dst++ = '\0';
02693 /*@=boundswrite@*/
02694             start++;
02695 
02696             if (*start == '|') {
02697                 char * newEnd;
02698 
02699                 start++;
02700 /*@-boundswrite@*/
02701                 if (parseExpression(hsa, token, start, &newEnd))
02702                 {
02703                     format = freeFormat(format, numTokens);
02704                     return 1;
02705                 }
02706 /*@=boundswrite@*/
02707                 start = newEnd;
02708                 /*@switchbreak@*/ break;
02709             }
02710 
02711             /*@-assignexpose@*/
02712             token->u.tag.format = start;
02713             /*@=assignexpose@*/
02714             token->u.tag.pad = 0;
02715             token->u.tag.justOne = 0;
02716             token->u.tag.arrayCount = 0;
02717 
02718             chptr = start;
02719             while (*chptr && *chptr != '{' && *chptr != '%') chptr++;
02720             if (!*chptr || *chptr == '%') {
02721                 hsa->errmsg = _("missing { after %");
02722                 format = freeFormat(format, numTokens);
02723                 return 1;
02724             }
02725 
02726 /*@-boundswrite@*/
02727             *chptr++ = '\0';
02728 /*@=boundswrite@*/
02729 
02730             while (start < chptr) {
02731                 if (xisdigit(*start)) {
02732                     i = strtoul(start, &start, 10);
02733                     token->u.tag.pad += i;
02734                     start = chptr;
02735                     break;
02736                 } else {
02737                     start++;
02738                 }
02739             }
02740 
02741             if (*start == '=') {
02742                 token->u.tag.justOne = 1;
02743                 start++;
02744             } else if (*start == '#') {
02745                 token->u.tag.justOne = 1;
02746                 token->u.tag.arrayCount = 1;
02747                 start++;
02748             }
02749 
02750             dst = next = start;
02751             while (*next && *next != '}') next++;
02752             if (!*next) {
02753                 hsa->errmsg = _("missing } after %{");
02754                 format = freeFormat(format, numTokens);
02755                 return 1;
02756             }
02757 /*@-boundswrite@*/
02758             *next++ = '\0';
02759 /*@=boundswrite@*/
02760 
02761             chptr = start;
02762             while (*chptr && *chptr != ':') chptr++;
02763 
02764             if (*chptr != '\0') {
02765 /*@-boundswrite@*/
02766                 *chptr++ = '\0';
02767 /*@=boundswrite@*/
02768                 if (!*chptr) {
02769                     hsa->errmsg = _("empty tag format");
02770                     format = freeFormat(format, numTokens);
02771                     return 1;
02772                 }
02773                 /*@-assignexpose@*/
02774                 token->u.tag.type = chptr;
02775                 /*@=assignexpose@*/
02776             } else {
02777                 token->u.tag.type = NULL;
02778             }
02779             
02780             if (!*start) {
02781                 hsa->errmsg = _("empty tag name");
02782                 format = freeFormat(format, numTokens);
02783                 return 1;
02784             }
02785 
02786             i = 0;
02787             token->type = PTOK_TAG;
02788 
02789             if (findTag(hsa, token, start)) {
02790                 hsa->errmsg = _("unknown tag");
02791                 format = freeFormat(format, numTokens);
02792                 return 1;
02793             }
02794 
02795             start = next;
02796             /*@switchbreak@*/ break;
02797 
02798         case '[':
02799 /*@-boundswrite@*/
02800             *dst++ = '\0';
02801             *start++ = '\0';
02802 /*@=boundswrite@*/
02803             token = format + numTokens++;
02804 
02805 /*@-boundswrite@*/
02806             if (parseFormat(hsa, start,
02807                             &token->u.array.format,
02808                             &token->u.array.numTokens,
02809                             &start, PARSER_IN_ARRAY))
02810             {
02811                 format = freeFormat(format, numTokens);
02812                 return 1;
02813             }
02814 /*@=boundswrite@*/
02815 
02816             if (!start) {
02817                 hsa->errmsg = _("] expected at end of array");
02818                 format = freeFormat(format, numTokens);
02819                 return 1;
02820             }
02821 
02822             dst = start;
02823 
02824             token->type = PTOK_ARRAY;
02825 
02826             /*@switchbreak@*/ break;
02827 
02828         case ']':
02829             if (state != PARSER_IN_ARRAY) {
02830                 hsa->errmsg = _("unexpected ]");
02831                 format = freeFormat(format, numTokens);
02832                 return 1;
02833             }
02834 /*@-boundswrite@*/
02835             *start++ = '\0';
02836 /*@=boundswrite@*/
02837             if (endPtr) *endPtr = start;
02838             done = 1;
02839             /*@switchbreak@*/ break;
02840 
02841         case '}':
02842             if (state != PARSER_IN_EXPR) {
02843                 hsa->errmsg = _("unexpected }");
02844                 format = freeFormat(format, numTokens);
02845                 return 1;
02846             }
02847 /*@-boundswrite@*/
02848             *start++ = '\0';
02849 /*@=boundswrite@*/
02850             if (endPtr) *endPtr = start;
02851             done = 1;
02852             /*@switchbreak@*/ break;
02853 
02854         default:
02855             if (token == NULL || token->type != PTOK_STRING) {
02856                 token = format + numTokens++;
02857                 token->type = PTOK_STRING;
02858                 /*@-temptrans -assignexpose@*/
02859                 dst = token->u.string.string = start;
02860                 /*@=temptrans =assignexpose@*/
02861             }
02862 
02863 /*@-boundswrite@*/
02864             if (*start == '\\') {
02865                 start++;
02866                 *dst++ = escapedChar(*start++);
02867             } else {
02868                 *dst++ = *start++;
02869             }
02870 /*@=boundswrite@*/
02871             /*@switchbreak@*/ break;
02872         }
02873         if (done)
02874             break;
02875     }
02876     /*@=infloops@*/
02877 
02878 /*@-boundswrite@*/
02879     if (dst != NULL)
02880         *dst = '\0';
02881 /*@=boundswrite@*/
02882 
02883     for (i = 0; i < numTokens; i++) {
02884         token = format + i;
02885         if (token->type == PTOK_STRING)
02886             token->u.string.len = strlen(token->u.string.string);
02887     }
02888 
02889     *numTokensPtr = numTokens;
02890     *formatPtr = format;
02891 
02892     return 0;
02893 }
02894 
02895 /*@-boundswrite@*/
02896 static int parseExpression(headerSprintfArgs hsa, sprintfToken token,
02897                 char * str, /*@out@*/ char ** endPtr)
02898 {
02899     char * chptr;
02900     char * end;
02901 
02902     hsa->errmsg = NULL;
02903     chptr = str;
02904     while (*chptr && *chptr != '?') chptr++;
02905 
02906     if (*chptr != '?') {
02907         hsa->errmsg = _("? expected in expression");
02908         return 1;
02909     }
02910 
02911     *chptr++ = '\0';;
02912 
02913     if (*chptr != '{') {
02914         hsa->errmsg = _("{ expected after ? in expression");
02915         return 1;
02916     }
02917 
02918     chptr++;
02919 
02920     if (parseFormat(hsa, chptr, &token->u.cond.ifFormat, 
02921                     &token->u.cond.numIfTokens, &end, PARSER_IN_EXPR)) 
02922         return 1;
02923 
02924     /* XXX fix segfault on "rpm -q rpm --qf='%|NAME?{%}:{NAME}|\n'"*/
02925     if (!(end && *end)) {
02926         hsa->errmsg = _("} expected in expression");
02927         token->u.cond.ifFormat =
02928                 freeFormat(token->u.cond.ifFormat, token->u.cond.numIfTokens);
02929         return 1;
02930     }
02931 
02932     chptr = end;
02933     if (*chptr != ':' && *chptr != '|') {
02934         hsa->errmsg = _(": expected following ? subexpression");
02935         token->u.cond.ifFormat =
02936                 freeFormat(token->u.cond.ifFormat, token->u.cond.numIfTokens);
02937         return 1;
02938     }
02939 
02940     if (*chptr == '|') {
02941         if (parseFormat(hsa, NULL, &token->u.cond.elseFormat, 
02942                 &token->u.cond.numElseTokens, &end, PARSER_IN_EXPR))
02943         {
02944             token->u.cond.ifFormat =
02945                 freeFormat(token->u.cond.ifFormat, token->u.cond.numIfTokens);
02946             return 1;
02947         }
02948     } else {
02949         chptr++;
02950 
02951         if (*chptr != '{') {
02952             hsa->errmsg = _("{ expected after : in expression");
02953             token->u.cond.ifFormat =
02954                 freeFormat(token->u.cond.ifFormat, token->u.cond.numIfTokens);
02955             return 1;
02956         }
02957 
02958         chptr++;
02959 
02960         if (parseFormat(hsa, chptr, &token->u.cond.elseFormat, 
02961                         &token->u.cond.numElseTokens, &end, PARSER_IN_EXPR)) 
02962             return 1;
02963 
02964         /* XXX fix segfault on "rpm -q rpm --qf='%|NAME?{a}:{%}|{NAME}\n'" */
02965         if (!(end && *end)) {
02966             hsa->errmsg = _("} expected in expression");
02967             token->u.cond.ifFormat =
02968                 freeFormat(token->u.cond.ifFormat, token->u.cond.numIfTokens);
02969             return 1;
02970         }
02971 
02972         chptr = end;
02973         if (*chptr != '|') {
02974             hsa->errmsg = _("| expected at end of expression");
02975             token->u.cond.ifFormat =
02976                 freeFormat(token->u.cond.ifFormat, token->u.cond.numIfTokens);
02977             token->u.cond.elseFormat =
02978                 freeFormat(token->u.cond.elseFormat, token->u.cond.numElseTokens);
02979             return 1;
02980         }
02981     }
02982         
02983     chptr++;
02984 
02985     *endPtr = chptr;
02986 
02987     token->type = PTOK_COND;
02988 
02989     (void) findTag(hsa, token, str);
02990 
02991     return 0;
02992 }
02993 /*@=boundswrite@*/
02994 
03005 static int getExtension(headerSprintfArgs hsa, headerTagTagFunction fn,
03006                 /*@out@*/ hTYP_t typeptr,
03007                 /*@out@*/ hPTR_t * data,
03008                 /*@out@*/ hCNT_t countptr,
03009                 rpmec ec)
03010         /*@modifies *typeptr, *data, *countptr, ec @*/
03011         /*@requires maxSet(typeptr) >= 0 /\ maxSet(data) >= 0
03012                 /\ maxSet(countptr) >= 0 @*/
03013 {
03014     if (!ec->avail) {
03015         if (fn(hsa->h, &ec->type, &ec->data, &ec->count, &ec->freeit))
03016             return 1;
03017         ec->avail = 1;
03018     }
03019 
03020     if (typeptr) *typeptr = ec->type;
03021     if (data) *data = ec->data;
03022     if (countptr) *countptr = ec->count;
03023 
03024     return 0;
03025 }
03026 
03033 /*@observer@*/ /*@null@*/
03034 static char * formatValue(headerSprintfArgs hsa, sprintfTag tag, int element)
03035         /*@modifies hsa @*/
03036 {
03037     char * val = NULL;
03038     size_t need = 0;
03039     char * t, * te;
03040     char buf[20];
03041     int_32 count, type;
03042     hPTR_t data;
03043     unsigned int intVal;
03044     const char ** strarray;
03045     int datafree = 0;
03046     int countBuf;
03047 
03048     memset(buf, 0, sizeof(buf));
03049     if (tag->ext) {
03050 /*@-boundswrite -branchstate @*/
03051         if (getExtension(hsa, tag->ext, &type, &data, &count, hsa->ec + tag->extNum))
03052         {
03053             count = 1;
03054             type = RPM_STRING_TYPE;     
03055             data = "(none)";
03056         }
03057 /*@=boundswrite =branchstate @*/
03058     } else {
03059 /*@-boundswrite -branchstate @*/
03060         if (!headerGetEntry(hsa->h, tag->tag, &type, (void **)&data, &count)) {
03061             count = 1;
03062             type = RPM_STRING_TYPE;     
03063             data = "(none)";
03064         }
03065 /*@=boundswrite =branchstate @*/
03066 
03067         /* XXX this test is unnecessary, array sizes are checked */
03068         switch (type) {
03069         default:
03070             if (element >= count) {
03071                 /*@-modobserver -observertrans@*/
03072                 data = headerFreeData(data, type);
03073                 /*@=modobserver =observertrans@*/
03074 
03075                 hsa->errmsg = _("(index out of range)");
03076                 return NULL;
03077             }
03078             break;
03079         case RPM_BIN_TYPE:
03080         case RPM_STRING_TYPE:
03081             break;
03082         }
03083         datafree = 1;
03084     }
03085 
03086     if (tag->arrayCount) {
03087         /*@-branchstate -observertrans -modobserver@*/
03088         if (datafree)
03089             data = headerFreeData(data, type);
03090         /*@=branchstate =observertrans =modobserver@*/
03091 
03092         countBuf = count;
03093         data = &countBuf;
03094         count = 1;
03095         type = RPM_INT32_TYPE;
03096     }
03097 
03098 /*@-boundswrite@*/
03099     (void) stpcpy( stpcpy(buf, "%"), tag->format);
03100 /*@=boundswrite@*/
03101 
03102     /*@-branchstate@*/
03103     if (data)
03104     switch (type) {
03105     case RPM_STRING_ARRAY_TYPE:
03106         strarray = (const char **)data;
03107 
03108         if (tag->fmt)
03109             val = tag->fmt(RPM_STRING_TYPE, strarray[element], buf, tag->pad, element);
03110 
03111         if (val) {
03112             need = strlen(val);
03113         } else {
03114             need = strlen(strarray[element]) + tag->pad + 20;
03115             val = xmalloc(need+1);
03116             strcat(buf, "s");
03117             /*@-formatconst@*/
03118             sprintf(val, buf, strarray[element]);
03119             /*@=formatconst@*/
03120         }
03121 
03122         break;
03123 
03124     case RPM_STRING_TYPE:
03125         if (tag->fmt)
03126             val = tag->fmt(RPM_STRING_TYPE, data, buf, tag->pad,  0);
03127 
03128         if (val) {
03129             need = strlen(val);
03130         } else {
03131             need = strlen(data) + tag->pad + 20;
03132             val = xmalloc(need+1);
03133             strcat(buf, "s");
03134             /*@-formatconst@*/
03135             sprintf(val, buf, data);
03136             /*@=formatconst@*/
03137         }
03138         break;
03139 
03140     case RPM_CHAR_TYPE:
03141     case RPM_INT8_TYPE:
03142     case RPM_INT16_TYPE:
03143     case RPM_INT32_TYPE:
03144         switch (type) {
03145         case RPM_CHAR_TYPE:     
03146         case RPM_INT8_TYPE:
03147             intVal = *(((int_8 *) data) + element);
03148             /*@innerbreak@*/ break;
03149         case RPM_INT16_TYPE:
03150             intVal = *(((uint_16 *) data) + element);
03151             /*@innerbreak@*/ break;
03152         default:                /* keep -Wall quiet */
03153         case RPM_INT32_TYPE:
03154             intVal = *(((int_32 *) data) + element);
03155             /*@innerbreak@*/ break;
03156         }
03157 
03158         if (tag->fmt)
03159             val = tag->fmt(RPM_INT32_TYPE, &intVal, buf, tag->pad, element);
03160 
03161         if (val) {
03162             need = strlen(val);
03163         } else {
03164             need = 10 + tag->pad + 20;
03165             val = xmalloc(need+1);
03166             strcat(buf, "d");
03167             /*@-formatconst@*/
03168             sprintf(val, buf, intVal);
03169             /*@=formatconst@*/
03170         }
03171         break;
03172 
03173     case RPM_BIN_TYPE:
03174         /* XXX HACK ALERT: element field abused as no. bytes of binary data. */
03175         if (tag->fmt)
03176             val = tag->fmt(RPM_BIN_TYPE, data, buf, tag->pad, count);
03177 
03178         if (val) {
03179             need = strlen(val);
03180         } else {
03181             val = bin2hex(data, count);
03182             need = strlen(val) + tag->pad;
03183         }
03184         break;
03185 
03186     default:
03187         need = sizeof("(unknown type)") - 1;
03188         val = xstrdup("(unknown type)");
03189         break;
03190     }
03191     /*@=branchstate@*/
03192 
03193     /*@-branchstate -observertrans -modobserver@*/
03194     if (datafree)
03195         data = headerFreeData(data, type);
03196     /*@=branchstate =observertrans =modobserver@*/
03197 
03198     /*@-branchstate@*/
03199     if (val && need > 0) {
03200         t = hsaReserve(hsa, need);
03201 /*@-boundswrite@*/
03202         te = stpcpy(t, val);
03203 /*@=boundswrite@*/
03204         hsa->vallen += (te - t);
03205         val = _free(val);
03206     }
03207     /*@=branchstate@*/
03208 
03209     return (hsa->val + hsa->vallen);
03210 }
03211 
03218 /*@observer@*/
03219 static char * singleSprintf(headerSprintfArgs hsa, sprintfToken token,
03220                 int element)
03221         /*@modifies hsa @*/
03222 {
03223     char * t, * te;
03224     int i, j;
03225     int numElements;
03226     int_32 type;
03227     int_32 count;
03228     sprintfToken spft;
03229     int condNumFormats;
03230     size_t need;
03231 
03232     /* we assume the token and header have been validated already! */
03233 
03234     switch (token->type) {
03235     case PTOK_NONE:
03236         break;
03237 
03238     case PTOK_STRING:
03239         need = token->u.string.len;
03240         if (need == 0) break;
03241         t = hsaReserve(hsa, need);
03242 /*@-boundswrite@*/
03243         te = stpcpy(t, token->u.string.string);
03244 /*@=boundswrite@*/
03245         hsa->vallen += (te - t);
03246         break;
03247 
03248     case PTOK_TAG:
03249         t = hsa->val + hsa->vallen;
03250         te = formatValue(hsa, &token->u.tag,
03251                         (token->u.tag.justOne ? 0 : element));
03252         if (te == NULL)
03253             return NULL;
03254         break;
03255 
03256     case PTOK_COND:
03257         if (token->u.cond.tag.ext || headerIsEntry(hsa->h, token->u.cond.tag.tag)) {
03258             spft = token->u.cond.ifFormat;
03259             condNumFormats = token->u.cond.numIfTokens;
03260         } else {
03261             spft = token->u.cond.elseFormat;
03262             condNumFormats = token->u.cond.numElseTokens;
03263         }
03264 
03265         need = condNumFormats * 20;
03266         if (spft == NULL || need == 0) break;
03267 
03268         t = hsaReserve(hsa, need);
03269         for (i = 0; i < condNumFormats; i++, spft++) {
03270             te = singleSprintf(hsa, spft, element);
03271             if (te == NULL)
03272                 return NULL;
03273         }
03274         break;
03275 
03276     case PTOK_ARRAY:
03277         numElements = -1;
03278         spft = token->u.array.format;
03279         for (i = 0; i < token->u.array.numTokens; i++, spft++)
03280         {
03281             if (spft->type != PTOK_TAG ||
03282                 spft->u.tag.arrayCount ||
03283                 spft->u.tag.justOne) continue;
03284 
03285             if (spft->u.tag.ext) {
03286 /*@-boundswrite@*/
03287                 if (getExtension(hsa, spft->u.tag.ext, &type, NULL, &count, 
03288                                  hsa->ec + spft->u.tag.extNum))
03289                      continue;
03290 /*@=boundswrite@*/
03291             } else {
03292 /*@-boundswrite@*/
03293                 if (!headerGetEntry(hsa->h, spft->u.tag.tag, &type, NULL, &count))
03294                     continue;
03295 /*@=boundswrite@*/
03296             } 
03297 
03298             if (type == RPM_BIN_TYPE)
03299                 count = 1;      /* XXX count abused as no. of bytes. */
03300 
03301             if (numElements > 1 && count != numElements)
03302             switch (type) {
03303             default:
03304                 hsa->errmsg =
03305                         _("array iterator used with different sized arrays");
03306                 return NULL;
03307                 /*@notreached@*/ /*@switchbreak@*/ break;
03308             case RPM_BIN_TYPE:
03309             case RPM_STRING_TYPE:
03310                 /*@switchbreak@*/ break;
03311             }
03312             if (count > numElements)
03313                 numElements = count;
03314         }
03315 
03316         if (numElements == -1) {
03317             need = sizeof("(none)") - 1;
03318             t = hsaReserve(hsa, need);
03319 /*@-boundswrite@*/
03320             te = stpcpy(t, "(none)");
03321 /*@=boundswrite@*/
03322             hsa->vallen += (te - t);
03323         } else {
03324             int isxml;
03325 
03326             need = numElements * token->u.array.numTokens * 10;
03327             if (need == 0) break;
03328 
03329             spft = token->u.array.format;
03330             isxml = (spft->type == PTOK_TAG && spft->u.tag.type != NULL &&
03331                 !strcmp(spft->u.tag.type, "xml"));
03332 
03333             if (isxml) {
03334                 const char * tagN = myTagName(hsa->tags, spft->u.tag.tag);
03335 
03336                 need = sizeof("  <rpmTag name=\"\">\n") - 1;
03337                 if (tagN != NULL)
03338                     need += strlen(tagN);
03339                 t = hsaReserve(hsa, need);
03340 /*@-boundswrite@*/
03341                 te = stpcpy(t, "  <rpmTag name=\"");
03342                 if (tagN != NULL)
03343                     te = stpcpy(te, tagN);
03344                 te = stpcpy(te, "\">\n");
03345 /*@=boundswrite@*/
03346                 hsa->vallen += (te - t);
03347             }
03348 
03349             t = hsaReserve(hsa, need);
03350             for (j = 0; j < numElements; j++) {
03351                 spft = token->u.array.format;
03352                 for (i = 0; i < token->u.array.numTokens; i++, spft++) {
03353                     te = singleSprintf(hsa, spft, j);
03354                     if (te == NULL)
03355                         return NULL;
03356                 }
03357             }
03358 
03359             if (isxml) {
03360                 need = sizeof("  </rpmTag>\n") - 1;
03361                 t = hsaReserve(hsa, need);
03362 /*@-boundswrite@*/
03363                 te = stpcpy(t, "  </rpmTag>\n");
03364 /*@=boundswrite@*/
03365                 hsa->vallen += (te - t);
03366             }
03367 
03368         }
03369         break;
03370     }
03371 
03372     return (hsa->val + hsa->vallen);
03373 }
03374 
03380 static /*@only@*/ rpmec
03381 rpmecNew(const headerSprintfExtension exts)
03382         /*@*/
03383 {
03384     headerSprintfExtension ext;
03385     rpmec ec;
03386     int i = 0;
03387 
03388     for (ext = exts; ext != NULL && ext->type != HEADER_EXT_LAST;
03389         ext = (ext->type == HEADER_EXT_MORE ? ext->u.more : ext+1))
03390     {
03391         i++;
03392     }
03393 
03394     ec = xcalloc(i, sizeof(*ec));
03395     return ec;
03396 }
03397 
03404 static /*@null@*/ rpmec
03405 rpmecFree(const headerSprintfExtension exts, /*@only@*/ rpmec ec)
03406         /*@modifies ec @*/
03407 {
03408     headerSprintfExtension ext;
03409     int i = 0;
03410 
03411     for (ext = exts; ext != NULL && ext->type != HEADER_EXT_LAST;
03412         ext = (ext->type == HEADER_EXT_MORE ? ext->u.more : ext+1))
03413     {
03414 /*@-boundswrite@*/
03415         if (ec[i].freeit) ec[i].data = _free(ec[i].data);
03416 /*@=boundswrite@*/
03417         i++;
03418     }
03419 
03420     ec = _free(ec);
03421     return NULL;
03422 }
03423 
03435 static /*@only@*/ /*@null@*/
03436 char * headerSprintf(Header h, const char * fmt,
03437                      const struct headerTagTableEntry_s * tbltags,
03438                      const struct headerSprintfExtension_s * extensions,
03439                      /*@null@*/ /*@out@*/ errmsg_t * errmsg)
03440         /*@modifies h, *errmsg @*/
03441         /*@requires maxSet(errmsg) >= 0 @*/
03442 {
03443     headerSprintfArgs hsa = memset(alloca(sizeof(*hsa)), 0, sizeof(*hsa));
03444     sprintfToken nextfmt;
03445     sprintfTag tag;
03446     char * t, * te;
03447     int isxml;
03448     int need;
03449  
03450     hsa->h = headerLink(h);
03451     hsa->fmt = xstrdup(fmt);
03452 /*@-castexpose@*/       /* FIX: legacy API shouldn't change. */
03453     hsa->exts = (headerSprintfExtension) extensions;
03454     hsa->tags = (headerTagTableEntry) tbltags;
03455 /*@=castexpose@*/
03456     hsa->errmsg = NULL;
03457 
03458 /*@-boundswrite@*/
03459     if (parseFormat(hsa, hsa->fmt, &hsa->format, &hsa->numTokens, NULL, PARSER_BEGIN))
03460         goto exit;
03461 /*@=boundswrite@*/
03462 
03463     hsa->ec = rpmecNew(hsa->exts);
03464     hsa->val = xstrdup("");
03465 
03466     tag =
03467         (hsa->format->type == PTOK_TAG
03468             ? &hsa->format->u.tag :
03469         (hsa->format->type == PTOK_ARRAY
03470             ? &hsa->format->u.array.format->u.tag :
03471         NULL));
03472     isxml = (tag != NULL && tag->tag == -2 && tag->type != NULL && !strcmp(tag->type, "xml"));
03473 
03474     if (isxml) {
03475         need = sizeof("<rpmHeader>\n") - 1;
03476         t = hsaReserve(hsa, need);
03477 /*@-boundswrite@*/
03478         te = stpcpy(t, "<rpmHeader>\n");
03479 /*@=boundswrite@*/
03480         hsa->vallen += (te - t);
03481     }
03482 
03483     hsa = hsaInit(hsa);
03484     while ((nextfmt = hsaNext(hsa)) != NULL) {
03485         te = singleSprintf(hsa, nextfmt, 0);
03486         if (te == NULL) {
03487             hsa->val = _free(hsa->val);
03488             break;
03489         }
03490     }
03491     hsa = hsaFini(hsa);
03492 
03493     if (isxml) {
03494         need = sizeof("</rpmHeader>\n") - 1;
03495         t = hsaReserve(hsa, need);
03496 /*@-boundswrite@*/
03497         te = stpcpy(t, "</rpmHeader>\n");
03498 /*@=boundswrite@*/
03499         hsa->vallen += (te - t);
03500     }
03501 
03502     if (hsa->val != NULL && hsa->vallen < hsa->alloced)
03503         hsa->val = xrealloc(hsa->val, hsa->vallen+1);   
03504 
03505     hsa->ec = rpmecFree(hsa->exts, hsa->ec);
03506     hsa->format = freeFormat(hsa->format, hsa->numTokens);
03507 
03508 exit:
03509 /*@-dependenttrans -observertrans @*/
03510     if (errmsg)
03511         *errmsg = hsa->errmsg;
03512 /*@=dependenttrans =observertrans @*/
03513     hsa->h = headerFree(hsa->h);
03514     hsa->fmt = _free(hsa->fmt);
03515     return hsa->val;
03516 }
03517 
03526 static char * octalFormat(int_32 type, hPTR_t data, 
03527                 char * formatPrefix, int padding, /*@unused@*/int element)
03528         /*@modifies formatPrefix @*/
03529 {
03530     char * val;
03531 
03532     if (type != RPM_INT32_TYPE) {
03533         val = xstrdup(_("(not a number)"));
03534     } else {
03535         val = xmalloc(20 + padding);
03536 /*@-boundswrite@*/
03537         strcat(formatPrefix, "o");
03538 /*@=boundswrite@*/
03539         /*@-formatconst@*/
03540         sprintf(val, formatPrefix, *((int_32 *) data));
03541         /*@=formatconst@*/
03542     }
03543 
03544     return val;
03545 }
03546 
03555 static char * hexFormat(int_32 type, hPTR_t data, 
03556                 char * formatPrefix, int padding, /*@unused@*/int element)
03557         /*@modifies formatPrefix @*/
03558 {
03559     char * val;
03560 
03561     if (type != RPM_INT32_TYPE) {
03562         val = xstrdup(_("(not a number)"));
03563     } else {
03564         val = xmalloc(20 + padding);
03565 /*@-boundswrite@*/
03566         strcat(formatPrefix, "x");
03567 /*@=boundswrite@*/
03568         /*@-formatconst@*/
03569         sprintf(val, formatPrefix, *((int_32 *) data));
03570         /*@=formatconst@*/
03571     }
03572 
03573     return val;
03574 }
03575 
03578 static char * realDateFormat(int_32 type, hPTR_t data, 
03579                 char * formatPrefix, int padding, /*@unused@*/int element,
03580                 const char * strftimeFormat)
03581         /*@modifies formatPrefix @*/
03582 {
03583     char * val;
03584 
03585     if (type != RPM_INT32_TYPE) {
03586         val = xstrdup(_("(not a number)"));
03587     } else {
03588         struct tm * tstruct;
03589         char buf[50];
03590 
03591         val = xmalloc(50 + padding);
03592 /*@-boundswrite@*/
03593         strcat(formatPrefix, "s");
03594 /*@=boundswrite@*/
03595 
03596         /* this is important if sizeof(int_32) ! sizeof(time_t) */
03597         {   time_t dateint = *((int_32 *) data);
03598             tstruct = localtime(&dateint);
03599         }
03600         buf[0] = '\0';
03601         if (tstruct)
03602             (void) strftime(buf, sizeof(buf) - 1, strftimeFormat, tstruct);
03603         /*@-formatconst@*/
03604         sprintf(val, formatPrefix, buf);
03605         /*@=formatconst@*/
03606     }
03607 
03608     return val;
03609 }
03610 
03619 static char * dateFormat(int_32 type, hPTR_t data, 
03620                          char * formatPrefix, int padding, int element)
03621         /*@modifies formatPrefix @*/
03622 {
03623     return realDateFormat(type, data, formatPrefix, padding, element,
03624                         _("%c"));
03625 }
03626 
03635 static char * dayFormat(int_32 type, hPTR_t data, 
03636                          char * formatPrefix, int padding, int element)
03637         /*@modifies formatPrefix @*/
03638 {
03639     return realDateFormat(type, data, formatPrefix, padding, element, 
03640                           _("%a %b %d %Y"));
03641 }
03642 
03651 static char * shescapeFormat(int_32 type, hPTR_t data, 
03652                 char * formatPrefix, int padding, /*@unused@*/int element)
03653         /*@modifies formatPrefix @*/
03654 {
03655     char * result, * dst, * src, * buf;
03656 
03657     if (type == RPM_INT32_TYPE) {
03658         result = xmalloc(padding + 20);
03659 /*@-boundswrite@*/
03660         strcat(formatPrefix, "d");
03661 /*@=boundswrite@*/
03662         /*@-formatconst@*/
03663         sprintf(result, formatPrefix, *((int_32 *) data));
03664         /*@=formatconst@*/
03665     } else {
03666         buf = alloca(strlen(data) + padding + 2);
03667 /*@-boundswrite@*/
03668         strcat(formatPrefix, "s");
03669 /*@=boundswrite@*/
03670         /*@-formatconst@*/
03671         sprintf(buf, formatPrefix, data);
03672         /*@=formatconst@*/
03673 
03674 /*@-boundswrite@*/
03675         result = dst = xmalloc(strlen(buf) * 4 + 3);
03676         *dst++ = '\'';
03677         for (src = buf; *src != '\0'; src++) {
03678             if (*src == '\'') {
03679                 *dst++ = '\'';
03680                 *dst++ = '\\';
03681                 *dst++ = '\'';
03682                 *dst++ = '\'';
03683             } else {
03684                 *dst++ = *src;
03685             }
03686         }
03687         *dst++ = '\'';
03688         *dst = '\0';
03689 /*@=boundswrite@*/
03690 
03691     }
03692 
03693     return result;
03694 }
03695 
03696 /*@-type@*/ /* FIX: cast? */
03697 const struct headerSprintfExtension_s headerDefaultFormats[] = {
03698     { HEADER_EXT_FORMAT, "octal", { octalFormat } },
03699     { HEADER_EXT_FORMAT, "hex", { hexFormat } },
03700     { HEADER_EXT_FORMAT, "date", { dateFormat } },
03701     { HEADER_EXT_FORMAT, "day", { dayFormat } },
03702     { HEADER_EXT_FORMAT, "shescape", { shescapeFormat } },
03703     { HEADER_EXT_LAST, NULL, { NULL } }
03704 };
03705 /*@=type@*/
03706 
03713 static
03714 void headerCopyTags(Header headerFrom, Header headerTo, hTAG_t tagstocopy)
03715         /*@modifies headerTo @*/
03716 {
03717     int * p;
03718 
03719     if (headerFrom == headerTo)
03720         return;
03721 
03722     for (p = tagstocopy; *p != 0; p++) {
03723         char *s;
03724         int_32 type;
03725         int_32 count;
03726         if (headerIsEntry(headerTo, *p))
03727             continue;
03728 /*@-boundswrite@*/
03729         if (!headerGetEntryMinMemory(headerFrom, *p, &type,
03730                                 (hPTR_t *) &s, &count))
03731             continue;
03732 /*@=boundswrite@*/
03733         (void) headerAddEntry(headerTo, *p, type, s, count);
03734         s = headerFreeData(s, type);
03735     }
03736 }
03737 
03738 /*@observer@*/ /*@unchecked@*/
03739 static struct HV_s hdrVec1 = {
03740     headerLink,
03741     headerUnlink,
03742     headerFree,
03743     headerNew,
03744     headerSort,
03745     headerUnsort,
03746     headerSizeof,
03747     headerUnload,
03748     headerReload,
03749     headerCopy,
03750     headerLoad,
03751     headerCopyLoad,
03752     headerRead,
03753     headerWrite,
03754     headerIsEntry,
03755     headerFreeTag,
03756     headerGetEntry,
03757     headerGetEntryMinMemory,
03758     headerAddEntry,
03759     headerAppendEntry,
03760     headerAddOrAppendEntry,
03761     headerAddI18NString,
03762     headerModifyEntry,
03763     headerRemoveEntry,
03764     headerSprintf,
03765     headerCopyTags,
03766     headerFreeIterator,
03767     headerInitIterator,
03768     headerNextIterator,
03769     NULL, NULL,
03770     1
03771 };
03772 
03773 /*@-compmempass -redef@*/
03774 /*@observer@*/ /*@unchecked@*/
03775 HV_t hdrVec = &hdrVec1;
03776 /*@=compmempass =redef@*/

Generated on 28 Aug 2013 for rpm by  doxygen 1.4.7