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, NULL, 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 1;
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;
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                 if (headerMatchLocale(td, l, le))
01642                     return ed;
01643 
01644         }
01645     }
01646 /*@=boundsread@*/
01647 
01648 /* when everything fail, try gettext */
01649     return ((entry->data != NULL) && *(char*)(entry->data)) ? _(entry->data) : entry->data;
01650 }
01651 
01662 static int intGetEntry(Header h, int_32 tag,
01663                 /*@null@*/ /*@out@*/ hTAG_t type,
01664                 /*@null@*/ /*@out@*/ hPTR_t * p,
01665                 /*@null@*/ /*@out@*/ hCNT_t c,
01666                 int minMem)
01667         /*@modifies *type, *p, *c @*/
01668         /*@requires maxSet(type) >= 0 /\ maxSet(p) >= 0 /\ maxSet(c) >= 0 @*/
01669 {
01670     indexEntry entry;
01671     int rc;
01672 
01673     /* First find the tag */
01674     /*@-mods@*/         /*@ FIX: h modified by sort. */
01675     entry = findEntry(h, tag, RPM_NULL_TYPE);
01676     /*@mods@*/
01677     if (entry == NULL) {
01678         if (type) type = 0;
01679         if (p) *p = NULL;
01680         if (c) *c = 0;
01681         return 0;
01682     }
01683 
01684     switch (entry->info.type) {
01685     case RPM_I18NSTRING_TYPE:
01686         rc = 1;
01687         if (type) *type = RPM_STRING_TYPE;
01688         if (c) *c = 1;
01689         /*@-dependenttrans@*/
01690         if (p) *p = headerFindI18NString(h, entry);
01691         /*@=dependenttrans@*/
01692         break;
01693     default:
01694         rc = copyEntry(entry, type, p, c, minMem);
01695         break;
01696     }
01697 
01698     /* XXX 1 on success */
01699     return ((rc == 1) ? 1 : 0);
01700 }
01701 
01709 static /*@null@*/ void * headerFreeTag(/*@unused@*/ Header h,
01710                 /*@only@*/ /*@null@*/ const void * data, rpmTagType type)
01711         /*@modifies data @*/
01712 {
01713     if (data) {
01714         /*@-branchstate@*/
01715         if (type == -1 ||
01716             type == RPM_STRING_ARRAY_TYPE ||
01717             type == RPM_I18NSTRING_TYPE ||
01718             type == RPM_BIN_TYPE)
01719                 data = _free(data);
01720         /*@=branchstate@*/
01721     }
01722     return NULL;
01723 }
01724 
01738 static
01739 int headerGetEntry(Header h, int_32 tag,
01740                         /*@null@*/ /*@out@*/ hTYP_t type,
01741                         /*@null@*/ /*@out@*/ void ** p,
01742                         /*@null@*/ /*@out@*/ hCNT_t c)
01743         /*@modifies *type, *p, *c @*/
01744         /*@requires maxSet(type) >= 0 /\ maxSet(p) >= 0 /\ maxSet(c) >= 0 @*/
01745 {
01746     return intGetEntry(h, tag, type, (hPTR_t *)p, c, 0);
01747 }
01748 
01761 static
01762 int headerGetEntryMinMemory(Header h, int_32 tag,
01763                         /*@null@*/ /*@out@*/ hTYP_t type,
01764                         /*@null@*/ /*@out@*/ hPTR_t * p,
01765                         /*@null@*/ /*@out@*/ hCNT_t c)
01766         /*@modifies *type, *p, *c @*/
01767         /*@requires maxSet(type) >= 0 /\ maxSet(p) >= 0 /\ maxSet(c) >= 0 @*/
01768 {
01769     return intGetEntry(h, tag, type, p, c, 1);
01770 }
01771 
01772 int headerGetRawEntry(Header h, int_32 tag, int_32 * type, hPTR_t * p,
01773                 int_32 * c)
01774 {
01775     indexEntry entry;
01776     int rc;
01777 
01778     if (p == NULL) return headerIsEntry(h, tag);
01779 
01780     /* First find the tag */
01781     /*@-mods@*/         /*@ FIX: h modified by sort. */
01782     entry = findEntry(h, tag, RPM_NULL_TYPE);
01783     /*@=mods@*/
01784     if (!entry) {
01785         if (p) *p = NULL;
01786         if (c) *c = 0;
01787         return 0;
01788     }
01789 
01790     rc = copyEntry(entry, type, p, c, 0);
01791 
01792     /* XXX 1 on success */
01793     return ((rc == 1) ? 1 : 0);
01794 }
01795 
01798 static void copyData(int_32 type, /*@out@*/ void * dstPtr, const void * srcPtr,
01799                 int_32 cnt, int dataLength)
01800         /*@modifies *dstPtr @*/
01801 {
01802     switch (type) {
01803     case RPM_STRING_ARRAY_TYPE:
01804     case RPM_I18NSTRING_TYPE:
01805     {   const char ** av = (const char **) srcPtr;
01806         char * t = dstPtr;
01807 
01808 /*@-bounds@*/
01809         while (cnt-- > 0 && dataLength > 0) {
01810             const char * s;
01811             if ((s = *av++) == NULL)
01812                 continue;
01813             do {
01814                 *t++ = *s++;
01815             } while (s[-1] && --dataLength > 0);
01816         }
01817 /*@=bounds@*/
01818     }   break;
01819 
01820     default:
01821 /*@-boundswrite@*/
01822         memmove(dstPtr, srcPtr, dataLength);
01823 /*@=boundswrite@*/
01824         break;
01825     }
01826 }
01827 
01836 /*@null@*/
01837 static void *
01838 grabData(int_32 type, hPTR_t p, int_32 c, /*@out@*/ int * lengthPtr)
01839         /*@modifies *lengthPtr @*/
01840         /*@requires maxSet(lengthPtr) >= 0 @*/
01841 {
01842     void * data = NULL;
01843     int length;
01844 
01845     length = dataLength(type, p, c, 0, NULL);
01846 /*@-branchstate@*/
01847     if (length > 0) {
01848         data = xmalloc(length);
01849         copyData(type, data, p, c, length);
01850     }
01851 /*@=branchstate@*/
01852 
01853     if (lengthPtr)
01854         *lengthPtr = length;
01855     return data;
01856 }
01857 
01872 static
01873 int headerAddEntry(Header h, int_32 tag, int_32 type, const void * p, int_32 c)
01874         /*@modifies h @*/
01875 {
01876     indexEntry entry;
01877     void * data;
01878     int length;
01879 
01880     /* Count must always be >= 1 for headerAddEntry. */
01881     if (c <= 0)
01882         return 0;
01883 
01884     if (hdrchkType(type))
01885         return 0;
01886     if (hdrchkData(c))
01887         return 0;
01888 
01889     length = 0;
01890 /*@-boundswrite@*/
01891     data = grabData(type, p, c, &length);
01892 /*@=boundswrite@*/
01893     if (data == NULL || length <= 0)
01894         return 0;
01895 
01896     /* Allocate more index space if necessary */
01897     if (h->indexUsed == h->indexAlloced) {
01898         h->indexAlloced += INDEX_MALLOC_SIZE;
01899         h->index = xrealloc(h->index, h->indexAlloced * sizeof(*h->index));
01900     }
01901 
01902     /* Fill in the index */
01903     entry = h->index + h->indexUsed;
01904     entry->info.tag = tag;
01905     entry->info.type = type;
01906     entry->info.count = c;
01907     entry->info.offset = 0;
01908     entry->data = data;
01909     entry->length = length;
01910 
01911 /*@-boundsread@*/
01912     if (h->indexUsed > 0 && tag < h->index[h->indexUsed-1].info.tag)
01913         h->flags &= ~HEADERFLAG_SORTED;
01914 /*@=boundsread@*/
01915     h->indexUsed++;
01916 
01917     return 1;
01918 }
01919 
01934 static
01935 int headerAppendEntry(Header h, int_32 tag, int_32 type,
01936                 const void * p, int_32 c)
01937         /*@modifies h @*/
01938 {
01939     indexEntry entry;
01940     int length;
01941 
01942     if (type == RPM_STRING_TYPE || type == RPM_I18NSTRING_TYPE) {
01943         /* we can't do this */
01944         return 0;
01945     }
01946 
01947     /* Find the tag entry in the header. */
01948     entry = findEntry(h, tag, type);
01949     if (!entry)
01950         return 0;
01951 
01952     length = dataLength(type, p, c, 0, NULL);
01953     if (length < 0)
01954         return 0;
01955 
01956     if (ENTRY_IN_REGION(entry)) {
01957         char * t = xmalloc(entry->length + length);
01958 /*@-bounds@*/
01959         memcpy(t, entry->data, entry->length);
01960 /*@=bounds@*/
01961         entry->data = t;
01962         entry->info.offset = 0;
01963     } else
01964         entry->data = xrealloc(entry->data, entry->length + length);
01965 
01966     copyData(type, ((char *) entry->data) + entry->length, p, c, length);
01967 
01968     entry->length += length;
01969 
01970     entry->info.count += c;
01971 
01972     return 1;
01973 }
01974 
01985 static
01986 int headerAddOrAppendEntry(Header h, int_32 tag, int_32 type,
01987                 const void * p, int_32 c)
01988         /*@modifies h @*/
01989 {
01990     return (findEntry(h, tag, type)
01991         ? headerAppendEntry(h, tag, type, p, c)
01992         : headerAddEntry(h, tag, type, p, c));
01993 }
01994 
02015 static
02016 int headerAddI18NString(Header h, int_32 tag, const char * string,
02017                 const char * lang)
02018         /*@modifies h @*/
02019 {
02020     indexEntry table, entry;
02021     const char ** strArray;
02022     int length;
02023     int ghosts;
02024     int i, langNum;
02025     char * buf;
02026 
02027     table = findEntry(h, HEADER_I18NTABLE, RPM_STRING_ARRAY_TYPE);
02028     entry = findEntry(h, tag, RPM_I18NSTRING_TYPE);
02029 
02030     if (!table && entry)
02031         return 0;               /* this shouldn't ever happen!! */
02032 
02033     if (!table && !entry) {
02034         const char * charArray[2];
02035         int count = 0;
02036         if (!lang || (lang[0] == 'C' && lang[1] == '\0')) {
02037             /*@-observertrans -readonlytrans@*/
02038             charArray[count++] = "C";
02039             /*@=observertrans =readonlytrans@*/
02040         } else {
02041             /*@-observertrans -readonlytrans@*/
02042             charArray[count++] = "C";
02043             /*@=observertrans =readonlytrans@*/
02044             charArray[count++] = lang;
02045         }
02046         if (!headerAddEntry(h, HEADER_I18NTABLE, RPM_STRING_ARRAY_TYPE, 
02047                         &charArray, count))
02048             return 0;
02049         table = findEntry(h, HEADER_I18NTABLE, RPM_STRING_ARRAY_TYPE);
02050     }
02051 
02052     if (!table)
02053         return 0;
02054     /*@-branchstate@*/
02055     if (!lang) lang = "C";
02056     /*@=branchstate@*/
02057 
02058     {   const char * l = table->data;
02059         for (langNum = 0; langNum < table->info.count; langNum++) {
02060             if (!strcmp(l, lang)) break;
02061             l += strlen(l) + 1;
02062         }
02063     }
02064 
02065     if (langNum >= table->info.count) {
02066         length = strlen(lang) + 1;
02067         if (ENTRY_IN_REGION(table)) {
02068             char * t = xmalloc(table->length + length);
02069             memcpy(t, table->data, table->length);
02070             table->data = t;
02071             table->info.offset = 0;
02072         } else
02073             table->data = xrealloc(table->data, table->length + length);
02074         memmove(((char *)table->data) + table->length, lang, length);
02075         table->length += length;
02076         table->info.count++;
02077     }
02078 
02079     if (!entry) {
02080         strArray = alloca(sizeof(*strArray) * (langNum + 1));
02081         for (i = 0; i < langNum; i++)
02082             strArray[i] = "";
02083         strArray[langNum] = string;
02084         return headerAddEntry(h, tag, RPM_I18NSTRING_TYPE, strArray, 
02085                                 langNum + 1);
02086     } else if (langNum >= entry->info.count) {
02087         ghosts = langNum - entry->info.count;
02088         
02089         length = strlen(string) + 1 + ghosts;
02090         if (ENTRY_IN_REGION(entry)) {
02091             char * t = xmalloc(entry->length + length);
02092             memcpy(t, entry->data, entry->length);
02093             entry->data = t;
02094             entry->info.offset = 0;
02095         } else
02096             entry->data = xrealloc(entry->data, entry->length + length);
02097 
02098         memset(((char *)entry->data) + entry->length, '\0', ghosts);
02099         memmove(((char *)entry->data) + entry->length + ghosts, string, strlen(string)+1);
02100 
02101         entry->length += length;
02102         entry->info.count = langNum + 1;
02103     } else {
02104         char *b, *be, *e, *ee, *t;
02105         size_t bn, sn, en;
02106 
02107         /* Set beginning/end pointers to previous data */
02108         b = be = e = ee = entry->data;
02109         for (i = 0; i < table->info.count; i++) {
02110             if (i == langNum)
02111                 be = ee;
02112             ee += strlen(ee) + 1;
02113             if (i == langNum)
02114                 e  = ee;
02115         }
02116 
02117         /* Get storage for new buffer */
02118         bn = (be-b);
02119         sn = strlen(string) + 1;
02120         en = (ee-e);
02121         length = bn + sn + en;
02122         t = buf = xmalloc(length);
02123 
02124         /* Copy values into new storage */
02125         memcpy(t, b, bn);
02126         t += bn;
02127 /*@-mayaliasunique@*/
02128         memcpy(t, string, sn);
02129         t += sn;
02130         memcpy(t, e, en);
02131         t += en;
02132 /*@=mayaliasunique@*/
02133 
02134         /* Replace i18N string array */
02135         entry->length -= strlen(be) + 1;
02136         entry->length += sn;
02137         
02138         if (ENTRY_IN_REGION(entry)) {
02139             entry->info.offset = 0;
02140         } else
02141             entry->data = _free(entry->data);
02142         /*@-dependenttrans@*/
02143         entry->data = buf;
02144         /*@=dependenttrans@*/
02145     }
02146 
02147     return 0;
02148 }
02149 
02160 static
02161 int headerModifyEntry(Header h, int_32 tag, int_32 type,
02162                         const void * p, int_32 c)
02163         /*@modifies h @*/
02164 {
02165     indexEntry entry;
02166     void * oldData;
02167     void * data;
02168     int length;
02169 
02170     /* First find the tag */
02171     entry = findEntry(h, tag, type);
02172     if (!entry)
02173         return 0;
02174 
02175     length = 0;
02176     data = grabData(type, p, c, &length);
02177     if (data == NULL || length <= 0)
02178         return 0;
02179 
02180     /* make sure entry points to the first occurence of this tag */
02181     while (entry > h->index && (entry - 1)->info.tag == tag)  
02182         entry--;
02183 
02184     /* free after we've grabbed the new data in case the two are intertwined;
02185        that's a bad idea but at least we won't break */
02186     oldData = entry->data;
02187 
02188     entry->info.count = c;
02189     entry->info.type = type;
02190     entry->data = data;
02191     entry->length = length;
02192 
02193     /*@-branchstate@*/
02194     if (ENTRY_IN_REGION(entry)) {
02195         entry->info.offset = 0;
02196     } else
02197         oldData = _free(oldData);
02198     /*@=branchstate@*/
02199 
02200     return 1;
02201 }
02202 
02205 static char escapedChar(const char ch)  /*@*/
02206 {
02207     switch (ch) {
02208     case 'a':   return '\a';
02209     case 'b':   return '\b';
02210     case 'f':   return '\f';
02211     case 'n':   return '\n';
02212     case 'r':   return '\r';
02213     case 't':   return '\t';
02214     case 'v':   return '\v';
02215     default:    return ch;
02216     }
02217 }
02218 
02225 static /*@null@*/ sprintfToken
02226 freeFormat( /*@only@*/ /*@null@*/ sprintfToken format, int num)
02227         /*@modifies *format @*/
02228 {
02229     int i;
02230 
02231     if (format == NULL) return NULL;
02232 
02233     for (i = 0; i < num; i++) {
02234         switch (format[i].type) {
02235         case PTOK_ARRAY:
02236 /*@-boundswrite@*/
02237             format[i].u.array.format =
02238                 freeFormat(format[i].u.array.format,
02239                         format[i].u.array.numTokens);
02240 /*@=boundswrite@*/
02241             /*@switchbreak@*/ break;
02242         case PTOK_COND:
02243 /*@-boundswrite@*/
02244             format[i].u.cond.ifFormat =
02245                 freeFormat(format[i].u.cond.ifFormat, 
02246                         format[i].u.cond.numIfTokens);
02247             format[i].u.cond.elseFormat =
02248                 freeFormat(format[i].u.cond.elseFormat, 
02249                         format[i].u.cond.numElseTokens);
02250 /*@=boundswrite@*/
02251             /*@switchbreak@*/ break;
02252         case PTOK_NONE:
02253         case PTOK_TAG:
02254         case PTOK_STRING:
02255         default:
02256             /*@switchbreak@*/ break;
02257         }
02258     }
02259     format = _free(format);
02260     return NULL;
02261 }
02262 
02266 struct headerIterator_s {
02267 /*@unused@*/
02268     Header h;           
02269 /*@unused@*/
02270     int next_index;     
02271 };
02272 
02278 static /*@null@*/
02279 HeaderIterator headerFreeIterator(/*@only@*/ HeaderIterator hi)
02280         /*@modifies hi @*/
02281 {
02282     if (hi != NULL) {
02283         hi->h = headerFree(hi->h);
02284         hi = _free(hi);
02285     }
02286     return hi;
02287 }
02288 
02294 static
02295 HeaderIterator headerInitIterator(Header h)
02296         /*@modifies h */
02297 {
02298     HeaderIterator hi = xmalloc(sizeof(*hi));
02299 
02300     headerSort(h);
02301 
02302     hi->h = headerLink(h);
02303     hi->next_index = 0;
02304     return hi;
02305 }
02306 
02316 static
02317 int headerNextIterator(HeaderIterator hi,
02318                 /*@null@*/ /*@out@*/ hTAG_t tag,
02319                 /*@null@*/ /*@out@*/ hTYP_t type,
02320                 /*@null@*/ /*@out@*/ hPTR_t * p,
02321                 /*@null@*/ /*@out@*/ hCNT_t c)
02322         /*@modifies hi, *tag, *type, *p, *c @*/
02323         /*@requires maxSet(tag) >= 0 /\ maxSet(type) >= 0
02324                 /\ maxSet(p) >= 0 /\ maxSet(c) >= 0 @*/
02325 {
02326     Header h = hi->h;
02327     int slot = hi->next_index;
02328     indexEntry entry = NULL;
02329     int rc;
02330 
02331     for (slot = hi->next_index; slot < h->indexUsed; slot++) {
02332         entry = h->index + slot;
02333         if (!ENTRY_IS_REGION(entry))
02334             break;
02335     }
02336     hi->next_index = slot;
02337     if (entry == NULL || slot >= h->indexUsed)
02338         return 0;
02339 
02340     /*@-noeffect@*/     /* LCL: no clue */
02341     hi->next_index++;
02342     /*@=noeffect@*/
02343 
02344     if (tag)
02345         *tag = entry->info.tag;
02346 
02347     rc = copyEntry(entry, type, p, c, 0);
02348 
02349     /* XXX 1 on success */
02350     return ((rc == 1) ? 1 : 0);
02351 }
02352 
02358 static /*@null@*/
02359 Header headerCopy(Header h)
02360         /*@modifies h @*/
02361 {
02362     Header nh = headerNew();
02363     HeaderIterator hi;
02364     int_32 tag, type, count;
02365     hPTR_t ptr;
02366    
02367     /*@-branchstate@*/
02368     for (hi = headerInitIterator(h);
02369         headerNextIterator(hi, &tag, &type, &ptr, &count);
02370         ptr = headerFreeData((void *)ptr, type))
02371     {
02372         if (ptr) (void) headerAddEntry(nh, tag, type, ptr, count);
02373     }
02374     hi = headerFreeIterator(hi);
02375     /*@=branchstate@*/
02376 
02377     return headerReload(nh, HEADER_IMAGE);
02378 }
02379 
02382 typedef struct headerSprintfArgs_s {
02383     Header h;
02384     char * fmt;
02385 /*@temp@*/
02386     headerTagTableEntry tags;
02387 /*@temp@*/
02388     headerSprintfExtension exts;
02389 /*@observer@*/ /*@null@*/
02390     const char * errmsg;
02391     rpmec ec;
02392     sprintfToken format;
02393 /*@relnull@*/
02394     HeaderIterator hi;
02395 /*@owned@*/
02396     char * val;
02397     size_t vallen;
02398     size_t alloced;
02399     int numTokens;
02400     int i;
02401 } * headerSprintfArgs;
02402 
02408 static headerSprintfArgs hsaInit(/*@returned@*/ headerSprintfArgs hsa)
02409         /*@modifies hsa */
02410 {
02411     sprintfTag tag =
02412         (hsa->format->type == PTOK_TAG
02413             ? &hsa->format->u.tag :
02414         (hsa->format->type == PTOK_ARRAY
02415             ? &hsa->format->u.array.format->u.tag :
02416         NULL));
02417 
02418     if (hsa != NULL) {
02419         hsa->i = 0;
02420         if (tag != NULL && tag->tag == -2)
02421             hsa->hi = headerInitIterator(hsa->h);
02422     }
02423 /*@-nullret@*/
02424     return hsa;
02425 /*@=nullret@*/
02426 }
02427 
02433 /*@null@*/
02434 static sprintfToken hsaNext(/*@returned@*/ headerSprintfArgs hsa)
02435         /*@modifies hsa */
02436 {
02437     sprintfToken fmt = NULL;
02438     sprintfTag tag =
02439         (hsa->format->type == PTOK_TAG
02440             ? &hsa->format->u.tag :
02441         (hsa->format->type == PTOK_ARRAY
02442             ? &hsa->format->u.array.format->u.tag :
02443         NULL));
02444 
02445     if (hsa != NULL && hsa->i >= 0 && hsa->i < hsa->numTokens) {
02446         fmt = hsa->format + hsa->i;
02447         if (hsa->hi == NULL) {
02448             hsa->i++;
02449         } else {
02450             int_32 tagno;
02451             int_32 type;
02452             int_32 count;
02453 
02454 /*@-boundswrite@*/
02455             if (!headerNextIterator(hsa->hi, &tagno, &type, NULL, &count))
02456                 fmt = NULL;
02457             tag->tag = tagno;
02458 /*@=boundswrite@*/
02459         }
02460     }
02461 
02462 /*@-dependenttrans -onlytrans@*/
02463     return fmt;
02464 /*@=dependenttrans =onlytrans@*/
02465 }
02466 
02472 static headerSprintfArgs hsaFini(/*@returned@*/ headerSprintfArgs hsa)
02473         /*@modifies hsa */
02474 {
02475     if (hsa != NULL) {
02476         hsa->hi = headerFreeIterator(hsa->hi);
02477         hsa->i = 0;
02478     }
02479 /*@-nullret@*/
02480     return hsa;
02481 /*@=nullret@*/
02482 }
02483 
02490 /*@dependent@*/ /*@exposed@*/
02491 static char * hsaReserve(headerSprintfArgs hsa, size_t need)
02492         /*@modifies hsa */
02493 {
02494     if ((hsa->vallen + need) >= hsa->alloced) {
02495         if (hsa->alloced <= need)
02496             hsa->alloced += need;
02497         hsa->alloced <<= 1;
02498         hsa->val = xrealloc(hsa->val, hsa->alloced+1);  
02499     }
02500     return hsa->val + hsa->vallen;
02501 }
02502 
02510 /*@observer@*/ /*@null@*/
02511 static const char * myTagName(headerTagTableEntry tbl, int val)
02512         /*@*/
02513 {
02514     static char name[128];
02515     const char * s;
02516     char *t;
02517 
02518     for (; tbl->name != NULL; tbl++) {
02519         if (tbl->val == val)
02520             break;
02521     }
02522     if ((s = tbl->name) == NULL)
02523         return NULL;
02524     s += sizeof("RPMTAG_") - 1;
02525     t = name;
02526     *t++ = *s++;
02527     while (*s != '\0')
02528         *t++ = xtolower(*s++);
02529     *t = '\0';
02530     return name;
02531 }
02532 
02540 static int myTagValue(headerTagTableEntry tbl, const char * name)
02541         /*@*/
02542 {
02543     for (; tbl->name != NULL; tbl++) {
02544         if (!xstrcasecmp(tbl->name, name))
02545             return tbl->val;
02546     }
02547     return 0;
02548 }
02549 
02556 static int findTag(headerSprintfArgs hsa, sprintfToken token, const char * name)
02557         /*@modifies token @*/
02558 {
02559     headerSprintfExtension ext;
02560     sprintfTag stag = (token->type == PTOK_COND
02561         ? &token->u.cond.tag : &token->u.tag);
02562 
02563     stag->fmt = NULL;
02564     stag->ext = NULL;
02565     stag->extNum = 0;
02566     stag->tag = -1;
02567 
02568     if (!strcmp(name, "*")) {
02569         stag->tag = -2;
02570         goto bingo;
02571     }
02572 
02573 /*@-branchstate@*/
02574     if (strncmp("RPMTAG_", name, sizeof("RPMTAG_")-1)) {
02575 /*@-boundswrite@*/
02576         char * t = alloca(strlen(name) + sizeof("RPMTAG_"));
02577         (void) stpcpy( stpcpy(t, "RPMTAG_"), name);
02578         name = t;
02579 /*@=boundswrite@*/
02580     }
02581 /*@=branchstate@*/
02582 
02583     /* Search extensions for specific tag override. */
02584     for (ext = hsa->exts; ext != NULL && ext->type != HEADER_EXT_LAST;
02585         ext = (ext->type == HEADER_EXT_MORE ? ext->u.more : ext+1))
02586     {
02587         if (ext->name == NULL || ext->type != HEADER_EXT_TAG)
02588             continue;
02589         if (!xstrcasecmp(ext->name, name)) {
02590             stag->ext = ext->u.tagFunction;
02591             stag->extNum = ext - hsa->exts;
02592             goto bingo;
02593         }
02594     }
02595 
02596     /* Search tag names. */
02597     stag->tag = myTagValue(hsa->tags, name);
02598     if (stag->tag != 0)
02599         goto bingo;
02600 
02601     return 1;
02602 
02603 bingo:
02604     /* Search extensions for specific format. */
02605     if (stag->type != NULL)
02606     for (ext = hsa->exts; ext != NULL && ext->type != HEADER_EXT_LAST;
02607             ext = (ext->type == HEADER_EXT_MORE ? ext->u.more : ext+1))
02608     {
02609         if (ext->name == NULL || ext->type != HEADER_EXT_FORMAT)
02610             continue;
02611         if (!strcmp(ext->name, stag->type)) {
02612             stag->fmt = ext->u.formatFunction;
02613             break;
02614         }
02615     }
02616     return 0;
02617 }
02618 
02619 /* forward ref */
02627 static int parseExpression(headerSprintfArgs hsa, sprintfToken token,
02628                 char * str, /*@out@*/char ** endPtr)
02629         /*@modifies hsa, str, token, *endPtr @*/
02630         /*@requires maxSet(endPtr) >= 0 @*/;
02631 
02641 static int parseFormat(headerSprintfArgs hsa, /*@null@*/ char * str,
02642                 /*@out@*/sprintfToken * formatPtr, /*@out@*/int * numTokensPtr,
02643                 /*@null@*/ /*@out@*/ char ** endPtr, int state)
02644         /*@modifies hsa, str, *formatPtr, *numTokensPtr, *endPtr @*/
02645         /*@requires maxSet(formatPtr) >= 0 /\ maxSet(numTokensPtr) >= 0
02646                 /\ maxSet(endPtr) >= 0 @*/
02647 {
02648     char * chptr, * start, * next, * dst;
02649     sprintfToken format;
02650     sprintfToken token;
02651     int numTokens;
02652     int i;
02653     int done = 0;
02654 
02655     /* upper limit on number of individual formats */
02656     numTokens = 0;
02657     if (str != NULL)
02658     for (chptr = str; *chptr != '\0'; chptr++)
02659         if (*chptr == '%') numTokens++;
02660     numTokens = numTokens * 2 + 1;
02661 
02662     format = xcalloc(numTokens, sizeof(*format));
02663     if (endPtr) *endPtr = NULL;
02664 
02665     /*@-infloops@*/ /* LCL: can't detect done termination */
02666     dst = start = str;
02667     numTokens = 0;
02668     token = NULL;
02669     if (start != NULL)
02670     while (*start != '\0') {
02671         switch (*start) {
02672         case '%':
02673             /* handle %% */
02674             if (*(start + 1) == '%') {
02675                 if (token == NULL || token->type != PTOK_STRING) {
02676                     token = format + numTokens++;
02677                     token->type = PTOK_STRING;
02678                     /*@-temptrans -assignexpose@*/
02679                     dst = token->u.string.string = start;
02680                     /*@=temptrans =assignexpose@*/
02681                 }
02682                 start++;
02683 /*@-boundswrite@*/
02684                 *dst++ = *start++;
02685 /*@=boundswrite@*/
02686                 /*@switchbreak@*/ break;
02687             } 
02688 
02689             token = format + numTokens++;
02690 /*@-boundswrite@*/
02691             *dst++ = '\0';
02692 /*@=boundswrite@*/
02693             start++;
02694 
02695             if (*start == '|') {
02696                 char * newEnd;
02697 
02698                 start++;
02699 /*@-boundswrite@*/
02700                 if (parseExpression(hsa, token, start, &newEnd))
02701                 {
02702                     format = freeFormat(format, numTokens);
02703                     return 1;
02704                 }
02705 /*@=boundswrite@*/
02706                 start = newEnd;
02707                 /*@switchbreak@*/ break;
02708             }
02709 
02710             /*@-assignexpose@*/
02711             token->u.tag.format = start;
02712             /*@=assignexpose@*/
02713             token->u.tag.pad = 0;
02714             token->u.tag.justOne = 0;
02715             token->u.tag.arrayCount = 0;
02716 
02717             chptr = start;
02718             while (*chptr && *chptr != '{' && *chptr != '%') chptr++;
02719             if (!*chptr || *chptr == '%') {
02720                 hsa->errmsg = _("missing { after %");
02721                 format = freeFormat(format, numTokens);
02722                 return 1;
02723             }
02724 
02725 /*@-boundswrite@*/
02726             *chptr++ = '\0';
02727 /*@=boundswrite@*/
02728 
02729             while (start < chptr) {
02730                 if (xisdigit(*start)) {
02731                     i = strtoul(start, &start, 10);
02732                     token->u.tag.pad += i;
02733                 } else {
02734                     start++;
02735                 }
02736             }
02737 
02738             if (*start == '=') {
02739                 token->u.tag.justOne = 1;
02740                 start++;
02741             } else if (*start == '#') {
02742                 token->u.tag.justOne = 1;
02743                 token->u.tag.arrayCount = 1;
02744                 start++;
02745             }
02746 
02747             next = start;
02748             while (*next && *next != '}') next++;
02749             if (!*next) {
02750                 hsa->errmsg = _("missing } after %{");
02751                 format = freeFormat(format, numTokens);
02752                 return 1;
02753             }
02754 /*@-boundswrite@*/
02755             *next++ = '\0';
02756 /*@=boundswrite@*/
02757 
02758             chptr = start;
02759             while (*chptr && *chptr != ':') chptr++;
02760 
02761             if (*chptr != '\0') {
02762 /*@-boundswrite@*/
02763                 *chptr++ = '\0';
02764 /*@=boundswrite@*/
02765                 if (!*chptr) {
02766                     hsa->errmsg = _("empty tag format");
02767                     format = freeFormat(format, numTokens);
02768                     return 1;
02769                 }
02770                 /*@-assignexpose@*/
02771                 token->u.tag.type = chptr;
02772                 /*@=assignexpose@*/
02773             } else {
02774                 token->u.tag.type = NULL;
02775             }
02776             
02777             if (!*start) {
02778                 hsa->errmsg = _("empty tag name");
02779                 format = freeFormat(format, numTokens);
02780                 return 1;
02781             }
02782 
02783             i = 0;
02784             token->type = PTOK_TAG;
02785 
02786             if (findTag(hsa, token, start)) {
02787                 hsa->errmsg = _("unknown tag");
02788                 format = freeFormat(format, numTokens);
02789                 return 1;
02790             }
02791 
02792             start = next;
02793             /*@switchbreak@*/ break;
02794 
02795         case '[':
02796 /*@-boundswrite@*/
02797             *dst++ = '\0';
02798             *start++ = '\0';
02799 /*@=boundswrite@*/
02800             token = format + numTokens++;
02801 
02802 /*@-boundswrite@*/
02803             if (parseFormat(hsa, start,
02804                             &token->u.array.format,
02805                             &token->u.array.numTokens,
02806                             &start, PARSER_IN_ARRAY))
02807             {
02808                 format = freeFormat(format, numTokens);
02809                 return 1;
02810             }
02811 /*@=boundswrite@*/
02812 
02813             if (!start) {
02814                 hsa->errmsg = _("] expected at end of array");
02815                 format = freeFormat(format, numTokens);
02816                 return 1;
02817             }
02818 
02819             dst = start;
02820 
02821             token->type = PTOK_ARRAY;
02822 
02823             /*@switchbreak@*/ break;
02824 
02825         case ']':
02826             if (state != PARSER_IN_ARRAY) {
02827                 hsa->errmsg = _("unexpected ]");
02828                 format = freeFormat(format, numTokens);
02829                 return 1;
02830             }
02831 /*@-boundswrite@*/
02832             *start++ = '\0';
02833 /*@=boundswrite@*/
02834             if (endPtr) *endPtr = start;
02835             done = 1;
02836             /*@switchbreak@*/ break;
02837 
02838         case '}':
02839             if (state != PARSER_IN_EXPR) {
02840                 hsa->errmsg = _("unexpected }");
02841                 format = freeFormat(format, numTokens);
02842                 return 1;
02843             }
02844 /*@-boundswrite@*/
02845             *start++ = '\0';
02846 /*@=boundswrite@*/
02847             if (endPtr) *endPtr = start;
02848             done = 1;
02849             /*@switchbreak@*/ break;
02850 
02851         default:
02852             if (token == NULL || token->type != PTOK_STRING) {
02853                 token = format + numTokens++;
02854                 token->type = PTOK_STRING;
02855                 /*@-temptrans -assignexpose@*/
02856                 dst = token->u.string.string = start;
02857                 /*@=temptrans =assignexpose@*/
02858             }
02859 
02860 /*@-boundswrite@*/
02861             if (*start == '\\') {
02862                 start++;
02863                 *dst++ = escapedChar(*start++);
02864             } else {
02865                 *dst++ = *start++;
02866             }
02867 /*@=boundswrite@*/
02868             /*@switchbreak@*/ break;
02869         }
02870         if (done)
02871             break;
02872     }
02873     /*@=infloops@*/
02874 
02875 /*@-boundswrite@*/
02876     if (dst != NULL)
02877         *dst = '\0';
02878 /*@=boundswrite@*/
02879 
02880     for (i = 0; i < numTokens; i++) {
02881         token = format + i;
02882         if (token->type == PTOK_STRING)
02883             token->u.string.len = strlen(token->u.string.string);
02884     }
02885 
02886     *numTokensPtr = numTokens;
02887     *formatPtr = format;
02888 
02889     return 0;
02890 }
02891 
02892 /*@-boundswrite@*/
02893 static int parseExpression(headerSprintfArgs hsa, sprintfToken token,
02894                 char * str, /*@out@*/ char ** endPtr)
02895 {
02896     char * chptr;
02897     char * end;
02898 
02899     hsa->errmsg = NULL;
02900     chptr = str;
02901     while (*chptr && *chptr != '?') chptr++;
02902 
02903     if (*chptr != '?') {
02904         hsa->errmsg = _("? expected in expression");
02905         return 1;
02906     }
02907 
02908     *chptr++ = '\0';;
02909 
02910     if (*chptr != '{') {
02911         hsa->errmsg = _("{ expected after ? in expression");
02912         return 1;
02913     }
02914 
02915     chptr++;
02916 
02917     if (parseFormat(hsa, chptr, &token->u.cond.ifFormat, 
02918                     &token->u.cond.numIfTokens, &end, PARSER_IN_EXPR)) 
02919         return 1;
02920 
02921     /* XXX fix segfault on "rpm -q rpm --qf='%|NAME?{%}:{NAME}|\n'"*/
02922     if (!(end && *end)) {
02923         hsa->errmsg = _("} expected in expression");
02924         token->u.cond.ifFormat =
02925                 freeFormat(token->u.cond.ifFormat, token->u.cond.numIfTokens);
02926         return 1;
02927     }
02928 
02929     chptr = end;
02930     if (*chptr != ':' && *chptr != '|') {
02931         hsa->errmsg = _(": expected following ? subexpression");
02932         token->u.cond.ifFormat =
02933                 freeFormat(token->u.cond.ifFormat, token->u.cond.numIfTokens);
02934         return 1;
02935     }
02936 
02937     if (*chptr == '|') {
02938         if (parseFormat(hsa, NULL, &token->u.cond.elseFormat, 
02939                 &token->u.cond.numElseTokens, &end, PARSER_IN_EXPR))
02940         {
02941             token->u.cond.ifFormat =
02942                 freeFormat(token->u.cond.ifFormat, token->u.cond.numIfTokens);
02943             return 1;
02944         }
02945     } else {
02946         chptr++;
02947 
02948         if (*chptr != '{') {
02949             hsa->errmsg = _("{ expected after : in expression");
02950             token->u.cond.ifFormat =
02951                 freeFormat(token->u.cond.ifFormat, token->u.cond.numIfTokens);
02952             return 1;
02953         }
02954 
02955         chptr++;
02956 
02957         if (parseFormat(hsa, chptr, &token->u.cond.elseFormat, 
02958                         &token->u.cond.numElseTokens, &end, PARSER_IN_EXPR)) 
02959             return 1;
02960 
02961         /* XXX fix segfault on "rpm -q rpm --qf='%|NAME?{a}:{%}|{NAME}\n'" */
02962         if (!(end && *end)) {
02963             hsa->errmsg = _("} expected in expression");
02964             token->u.cond.ifFormat =
02965                 freeFormat(token->u.cond.ifFormat, token->u.cond.numIfTokens);
02966             return 1;
02967         }
02968 
02969         chptr = end;
02970         if (*chptr != '|') {
02971             hsa->errmsg = _("| expected at end of expression");
02972             token->u.cond.ifFormat =
02973                 freeFormat(token->u.cond.ifFormat, token->u.cond.numIfTokens);
02974             token->u.cond.elseFormat =
02975                 freeFormat(token->u.cond.elseFormat, token->u.cond.numElseTokens);
02976             return 1;
02977         }
02978     }
02979         
02980     chptr++;
02981 
02982     *endPtr = chptr;
02983 
02984     token->type = PTOK_COND;
02985 
02986     (void) findTag(hsa, token, str);
02987 
02988     return 0;
02989 }
02990 /*@=boundswrite@*/
02991 
03002 static int getExtension(headerSprintfArgs hsa, headerTagTagFunction fn,
03003                 /*@out@*/ hTYP_t typeptr,
03004                 /*@out@*/ hPTR_t * data,
03005                 /*@out@*/ hCNT_t countptr,
03006                 rpmec ec)
03007         /*@modifies *typeptr, *data, *countptr, ec @*/
03008         /*@requires maxSet(typeptr) >= 0 /\ maxSet(data) >= 0
03009                 /\ maxSet(countptr) >= 0 @*/
03010 {
03011     if (!ec->avail) {
03012         if (fn(hsa->h, &ec->type, &ec->data, &ec->count, &ec->freeit))
03013             return 1;
03014         ec->avail = 1;
03015     }
03016 
03017     if (typeptr) *typeptr = ec->type;
03018     if (data) *data = ec->data;
03019     if (countptr) *countptr = ec->count;
03020 
03021     return 0;
03022 }
03023 
03030 /*@observer@*/ /*@null@*/
03031 static char * formatValue(headerSprintfArgs hsa, sprintfTag tag, int element)
03032         /*@modifies hsa @*/
03033 {
03034     char * val = NULL;
03035     size_t need = 0;
03036     char * t, * te;
03037     char buf[20];
03038     int_32 count, type;
03039     hPTR_t data;
03040     unsigned int intVal;
03041     const char ** strarray;
03042     int datafree = 0;
03043     int countBuf;
03044 
03045     memset(buf, 0, sizeof(buf));
03046     if (tag->ext) {
03047 /*@-boundswrite -branchstate @*/
03048         if (getExtension(hsa, tag->ext, &type, &data, &count, hsa->ec + tag->extNum))
03049         {
03050             count = 1;
03051             type = RPM_STRING_TYPE;     
03052             data = "(none)";
03053         }
03054 /*@=boundswrite =branchstate @*/
03055     } else {
03056 /*@-boundswrite -branchstate @*/
03057         if (!headerGetEntry(hsa->h, tag->tag, &type, (void **)&data, &count)) {
03058             count = 1;
03059             type = RPM_STRING_TYPE;     
03060             data = "(none)";
03061         }
03062 /*@=boundswrite =branchstate @*/
03063 
03064         /* XXX this test is unnecessary, array sizes are checked */
03065         switch (type) {
03066         default:
03067             if (element >= count) {
03068                 /*@-modobserver -observertrans@*/
03069                 data = headerFreeData(data, type);
03070                 /*@=modobserver =observertrans@*/
03071 
03072                 hsa->errmsg = _("(index out of range)");
03073                 return NULL;
03074             }
03075             break;
03076         case RPM_BIN_TYPE:
03077         case RPM_STRING_TYPE:
03078             break;
03079         }
03080         datafree = 1;
03081     }
03082 
03083     if (tag->arrayCount) {
03084         /*@-branchstate -observertrans -modobserver@*/
03085         if (datafree)
03086             data = headerFreeData(data, type);
03087         /*@=branchstate =observertrans =modobserver@*/
03088 
03089         countBuf = count;
03090         data = &countBuf;
03091         count = 1;
03092         type = RPM_INT32_TYPE;
03093     }
03094 
03095 /*@-boundswrite@*/
03096     (void) stpcpy( stpcpy(buf, "%"), tag->format);
03097 /*@=boundswrite@*/
03098 
03099     /*@-branchstate@*/
03100     if (data)
03101     switch (type) {
03102     case RPM_STRING_ARRAY_TYPE:
03103         strarray = (const char **)data;
03104 
03105         if (tag->fmt)
03106             val = tag->fmt(RPM_STRING_TYPE, strarray[element], buf, tag->pad, element);
03107 
03108         if (val) {
03109             need = strlen(val);
03110         } else {
03111             need = strlen(strarray[element]) + tag->pad + 20;
03112             val = xmalloc(need+1);
03113             strcat(buf, "s");
03114             /*@-formatconst@*/
03115             sprintf(val, buf, strarray[element]);
03116             /*@=formatconst@*/
03117         }
03118 
03119         break;
03120 
03121     case RPM_STRING_TYPE:
03122         if (tag->fmt)
03123             val = tag->fmt(RPM_STRING_TYPE, data, buf, tag->pad,  0);
03124 
03125         if (val) {
03126             need = strlen(val);
03127         } else {
03128             need = strlen(data) + tag->pad + 20;
03129             val = xmalloc(need+1);
03130             strcat(buf, "s");
03131             /*@-formatconst@*/
03132             sprintf(val, buf, data);
03133             /*@=formatconst@*/
03134         }
03135         break;
03136 
03137     case RPM_CHAR_TYPE:
03138     case RPM_INT8_TYPE:
03139     case RPM_INT16_TYPE:
03140     case RPM_INT32_TYPE:
03141         switch (type) {
03142         case RPM_CHAR_TYPE:     
03143         case RPM_INT8_TYPE:
03144             intVal = *(((int_8 *) data) + element);
03145             /*@innerbreak@*/ break;
03146         case RPM_INT16_TYPE:
03147             intVal = *(((uint_16 *) data) + element);
03148             /*@innerbreak@*/ break;
03149         default:                /* keep -Wall quiet */
03150         case RPM_INT32_TYPE:
03151             intVal = *(((int_32 *) data) + element);
03152             /*@innerbreak@*/ break;
03153         }
03154 
03155         if (tag->fmt)
03156             val = tag->fmt(RPM_INT32_TYPE, &intVal, buf, tag->pad, element);
03157 
03158         if (val) {
03159             need = strlen(val);
03160         } else {
03161             need = 10 + tag->pad + 20;
03162             val = xmalloc(need+1);
03163             strcat(buf, "d");
03164             /*@-formatconst@*/
03165             sprintf(val, buf, intVal);
03166             /*@=formatconst@*/
03167         }
03168         break;
03169 
03170     case RPM_BIN_TYPE:
03171         /* XXX HACK ALERT: element field abused as no. bytes of binary data. */
03172         if (tag->fmt)
03173             val = tag->fmt(RPM_BIN_TYPE, data, buf, tag->pad, count);
03174 
03175         if (val) {
03176             need = strlen(val);
03177         } else {
03178 #ifdef  NOTYET
03179             val = memcpy(xmalloc(count), data, count);
03180 #else
03181             /* XXX format string not used */
03182             static char hex[] = "0123456789abcdef";
03183             const char * s = data;
03184 
03185 /*@-boundswrite@*/
03186             need = 2*count + tag->pad;
03187             val = t = xmalloc(need+1);
03188             while (count-- > 0) {
03189                 unsigned int i;
03190                 i = *s++;
03191                 *t++ = hex[ (i >> 4) & 0xf ];
03192                 *t++ = hex[ (i     ) & 0xf ];
03193             }
03194             *t = '\0';
03195 /*@=boundswrite@*/
03196 #endif
03197         }
03198         break;
03199 
03200     default:
03201         need = sizeof("(unknown type)") - 1;
03202         val = xstrdup("(unknown type)");
03203         break;
03204     }
03205     /*@=branchstate@*/
03206 
03207     /*@-branchstate -observertrans -modobserver@*/
03208     if (datafree)
03209         data = headerFreeData(data, type);
03210     /*@=branchstate =observertrans =modobserver@*/
03211 
03212     /*@-branchstate@*/
03213     if (val && need > 0) {
03214         t = hsaReserve(hsa, need);
03215 /*@-boundswrite@*/
03216         te = stpcpy(t, val);
03217 /*@=boundswrite@*/
03218         hsa->vallen += (te - t);
03219         val = _free(val);
03220     }
03221     /*@=branchstate@*/
03222 
03223     return (hsa->val + hsa->vallen);
03224 }
03225 
03232 /*@observer@*/
03233 static char * singleSprintf(headerSprintfArgs hsa, sprintfToken token,
03234                 int element)
03235         /*@modifies hsa @*/
03236 {
03237     char * t, * te;
03238     int i, j;
03239     int numElements;
03240     int_32 type;
03241     int_32 count;
03242     sprintfToken spft;
03243     int condNumFormats;
03244     size_t need;
03245 
03246     /* we assume the token and header have been validated already! */
03247 
03248     switch (token->type) {
03249     case PTOK_NONE:
03250         break;
03251 
03252     case PTOK_STRING:
03253         need = token->u.string.len;
03254         if (need == 0) break;
03255         t = hsaReserve(hsa, need);
03256 /*@-boundswrite@*/
03257         te = stpcpy(t, token->u.string.string);
03258 /*@=boundswrite@*/
03259         hsa->vallen += (te - t);
03260         break;
03261 
03262     case PTOK_TAG:
03263         t = hsa->val + hsa->vallen;
03264         te = formatValue(hsa, &token->u.tag,
03265                         (token->u.tag.justOne ? 0 : element));
03266         if (te == NULL)
03267             return NULL;
03268         break;
03269 
03270     case PTOK_COND:
03271         if (token->u.cond.tag.ext || headerIsEntry(hsa->h, token->u.cond.tag.tag)) {
03272             spft = token->u.cond.ifFormat;
03273             condNumFormats = token->u.cond.numIfTokens;
03274         } else {
03275             spft = token->u.cond.elseFormat;
03276             condNumFormats = token->u.cond.numElseTokens;
03277         }
03278 
03279         need = condNumFormats * 20;
03280         if (spft == NULL || need == 0) break;
03281 
03282         t = hsaReserve(hsa, need);
03283         for (i = 0; i < condNumFormats; i++, spft++) {
03284             te = singleSprintf(hsa, spft, element);
03285             if (te == NULL)
03286                 return NULL;
03287         }
03288         break;
03289 
03290     case PTOK_ARRAY:
03291         numElements = -1;
03292         spft = token->u.array.format;
03293         for (i = 0; i < token->u.array.numTokens; i++, spft++)
03294         {
03295             if (spft->type != PTOK_TAG ||
03296                 spft->u.tag.arrayCount ||
03297                 spft->u.tag.justOne) continue;
03298 
03299             if (spft->u.tag.ext) {
03300 /*@-boundswrite@*/
03301                 if (getExtension(hsa, spft->u.tag.ext, &type, NULL, &count, 
03302                                  hsa->ec + spft->u.tag.extNum))
03303                      continue;
03304 /*@=boundswrite@*/
03305             } else {
03306 /*@-boundswrite@*/
03307                 if (!headerGetEntry(hsa->h, spft->u.tag.tag, &type, NULL, &count))
03308                     continue;
03309 /*@=boundswrite@*/
03310             } 
03311 
03312             if (type == RPM_BIN_TYPE)
03313                 count = 1;      /* XXX count abused as no. of bytes. */
03314 
03315             if (numElements > 1 && count != numElements)
03316             switch (type) {
03317             default:
03318                 hsa->errmsg =
03319                         _("array iterator used with different sized arrays");
03320                 return NULL;
03321                 /*@notreached@*/ /*@switchbreak@*/ break;
03322             case RPM_BIN_TYPE:
03323             case RPM_STRING_TYPE:
03324                 /*@switchbreak@*/ break;
03325             }
03326             if (count > numElements)
03327                 numElements = count;
03328         }
03329 
03330         if (numElements == -1) {
03331             need = sizeof("(none)") - 1;
03332             t = hsaReserve(hsa, need);
03333 /*@-boundswrite@*/
03334             te = stpcpy(t, "(none)");
03335 /*@=boundswrite@*/
03336             hsa->vallen += (te - t);
03337         } else {
03338             int isxml;
03339 
03340             need = numElements * token->u.array.numTokens * 10;
03341             if (need == 0) break;
03342 
03343             spft = token->u.array.format;
03344             isxml = (spft->type == PTOK_TAG && spft->u.tag.type != NULL &&
03345                 !strcmp(spft->u.tag.type, "xml"));
03346 
03347             if (isxml) {
03348                 const char * tagN = myTagName(hsa->tags, spft->u.tag.tag);
03349 
03350                 need = sizeof("  <rpmTag name=\"\">\n") - 1;
03351                 if (tagN != NULL)
03352                     need += strlen(tagN);
03353                 t = hsaReserve(hsa, need);
03354 /*@-boundswrite@*/
03355                 te = stpcpy(t, "  <rpmTag name=\"");
03356                 if (tagN != NULL)
03357                     te = stpcpy(te, tagN);
03358                 te = stpcpy(te, "\">\n");
03359 /*@=boundswrite@*/
03360                 hsa->vallen += (te - t);
03361             }
03362 
03363             t = hsaReserve(hsa, need);
03364             for (j = 0; j < numElements; j++) {
03365                 spft = token->u.array.format;
03366                 for (i = 0; i < token->u.array.numTokens; i++, spft++) {
03367                     te = singleSprintf(hsa, spft, j);
03368                     if (te == NULL)
03369                         return NULL;
03370                 }
03371             }
03372 
03373             if (isxml) {
03374                 need = sizeof("  </rpmTag>\n") - 1;
03375                 t = hsaReserve(hsa, need);
03376 /*@-boundswrite@*/
03377                 te = stpcpy(t, "  </rpmTag>\n");
03378 /*@=boundswrite@*/
03379                 hsa->vallen += (te - t);
03380             }
03381 
03382         }
03383         break;
03384     }
03385 
03386     return (hsa->val + hsa->vallen);
03387 }
03388 
03394 static /*@only@*/ rpmec
03395 rpmecNew(const headerSprintfExtension exts)
03396         /*@*/
03397 {
03398     headerSprintfExtension ext;
03399     rpmec ec;
03400     int i = 0;
03401 
03402     for (ext = exts; ext != NULL && ext->type != HEADER_EXT_LAST;
03403         ext = (ext->type == HEADER_EXT_MORE ? ext->u.more : ext+1))
03404     {
03405         i++;
03406     }
03407 
03408     ec = xcalloc(i, sizeof(*ec));
03409     return ec;
03410 }
03411 
03418 static /*@null@*/ rpmec
03419 rpmecFree(const headerSprintfExtension exts, /*@only@*/ rpmec ec)
03420         /*@modifies ec @*/
03421 {
03422     headerSprintfExtension ext;
03423     int i = 0;
03424 
03425     for (ext = exts; ext != NULL && ext->type != HEADER_EXT_LAST;
03426         ext = (ext->type == HEADER_EXT_MORE ? ext->u.more : ext+1))
03427     {
03428 /*@-boundswrite@*/
03429         if (ec[i].freeit) ec[i].data = _free(ec[i].data);
03430 /*@=boundswrite@*/
03431         i++;
03432     }
03433 
03434     ec = _free(ec);
03435     return NULL;
03436 }
03437 
03449 static /*@only@*/ /*@null@*/
03450 char * headerSprintf(Header h, const char * fmt,
03451                      const struct headerTagTableEntry_s * tbltags,
03452                      const struct headerSprintfExtension_s * extensions,
03453                      /*@null@*/ /*@out@*/ errmsg_t * errmsg)
03454         /*@modifies h, *errmsg @*/
03455         /*@requires maxSet(errmsg) >= 0 @*/
03456 {
03457     headerSprintfArgs hsa = memset(alloca(sizeof(*hsa)), 0, sizeof(*hsa));
03458     sprintfToken nextfmt;
03459     sprintfTag tag;
03460     char * t, * te;
03461     int isxml;
03462     int need;
03463  
03464     hsa->h = headerLink(h);
03465     hsa->fmt = xstrdup(fmt);
03466 /*@-castexpose@*/       /* FIX: legacy API shouldn't change. */
03467     hsa->exts = (headerSprintfExtension) extensions;
03468     hsa->tags = (headerTagTableEntry) tbltags;
03469 /*@=castexpose@*/
03470     hsa->errmsg = NULL;
03471 
03472 /*@-boundswrite@*/
03473     if (parseFormat(hsa, hsa->fmt, &hsa->format, &hsa->numTokens, NULL, PARSER_BEGIN))
03474         goto exit;
03475 /*@=boundswrite@*/
03476 
03477     hsa->ec = rpmecNew(hsa->exts);
03478     hsa->val = xstrdup("");
03479 
03480     tag =
03481         (hsa->format->type == PTOK_TAG
03482             ? &hsa->format->u.tag :
03483         (hsa->format->type == PTOK_ARRAY
03484             ? &hsa->format->u.array.format->u.tag :
03485         NULL));
03486     isxml = (tag != NULL && tag->tag == -2 && tag->type != NULL && !strcmp(tag->type, "xml"));
03487 
03488     if (isxml) {
03489         need = sizeof("<rpmHeader>\n") - 1;
03490         t = hsaReserve(hsa, need);
03491 /*@-boundswrite@*/
03492         te = stpcpy(t, "<rpmHeader>\n");
03493 /*@=boundswrite@*/
03494         hsa->vallen += (te - t);
03495     }
03496 
03497     hsa = hsaInit(hsa);
03498     while ((nextfmt = hsaNext(hsa)) != NULL) {
03499         te = singleSprintf(hsa, nextfmt, 0);
03500         if (te == NULL) {
03501             hsa->val = _free(hsa->val);
03502             break;
03503         }
03504     }
03505     hsa = hsaFini(hsa);
03506 
03507     if (isxml) {
03508         need = sizeof("</rpmHeader>\n") - 1;
03509         t = hsaReserve(hsa, need);
03510 /*@-boundswrite@*/
03511         te = stpcpy(t, "</rpmHeader>\n");
03512 /*@=boundswrite@*/
03513         hsa->vallen += (te - t);
03514     }
03515 
03516     if (hsa->val != NULL && hsa->vallen < hsa->alloced)
03517         hsa->val = xrealloc(hsa->val, hsa->vallen+1);   
03518 
03519     hsa->ec = rpmecFree(hsa->exts, hsa->ec);
03520     hsa->format = freeFormat(hsa->format, hsa->numTokens);
03521 
03522 exit:
03523 /*@-dependenttrans -observertrans @*/
03524     if (errmsg)
03525         *errmsg = hsa->errmsg;
03526 /*@=dependenttrans =observertrans @*/
03527     hsa->h = headerFree(hsa->h);
03528     hsa->fmt = _free(hsa->fmt);
03529     return hsa->val;
03530 }
03531 
03540 static char * octalFormat(int_32 type, hPTR_t data, 
03541                 char * formatPrefix, int padding, /*@unused@*/int element)
03542         /*@modifies formatPrefix @*/
03543 {
03544     char * val;
03545 
03546     if (type != RPM_INT32_TYPE) {
03547         val = xstrdup(_("(not a number)"));
03548     } else {
03549         val = xmalloc(20 + padding);
03550 /*@-boundswrite@*/
03551         strcat(formatPrefix, "o");
03552 /*@=boundswrite@*/
03553         /*@-formatconst@*/
03554         sprintf(val, formatPrefix, *((int_32 *) data));
03555         /*@=formatconst@*/
03556     }
03557 
03558     return val;
03559 }
03560 
03569 static char * hexFormat(int_32 type, hPTR_t data, 
03570                 char * formatPrefix, int padding, /*@unused@*/int element)
03571         /*@modifies formatPrefix @*/
03572 {
03573     char * val;
03574 
03575     if (type != RPM_INT32_TYPE) {
03576         val = xstrdup(_("(not a number)"));
03577     } else {
03578         val = xmalloc(20 + padding);
03579 /*@-boundswrite@*/
03580         strcat(formatPrefix, "x");
03581 /*@=boundswrite@*/
03582         /*@-formatconst@*/
03583         sprintf(val, formatPrefix, *((int_32 *) data));
03584         /*@=formatconst@*/
03585     }
03586 
03587     return val;
03588 }
03589 
03592 static char * realDateFormat(int_32 type, hPTR_t data, 
03593                 char * formatPrefix, int padding, /*@unused@*/int element,
03594                 const char * strftimeFormat)
03595         /*@modifies formatPrefix @*/
03596 {
03597     char * val;
03598 
03599     if (type != RPM_INT32_TYPE) {
03600         val = xstrdup(_("(not a number)"));
03601     } else {
03602         struct tm * tstruct;
03603         char buf[50];
03604 
03605         val = xmalloc(50 + padding);
03606 /*@-boundswrite@*/
03607         strcat(formatPrefix, "s");
03608 /*@=boundswrite@*/
03609 
03610         /* this is important if sizeof(int_32) ! sizeof(time_t) */
03611         {   time_t dateint = *((int_32 *) data);
03612             tstruct = localtime(&dateint);
03613         }
03614         buf[0] = '\0';
03615         if (tstruct)
03616             (void) strftime(buf, sizeof(buf) - 1, strftimeFormat, tstruct);
03617         /*@-formatconst@*/
03618         sprintf(val, formatPrefix, buf);
03619         /*@=formatconst@*/
03620     }
03621 
03622     return val;
03623 }
03624 
03633 static char * dateFormat(int_32 type, hPTR_t data, 
03634                          char * formatPrefix, int padding, int element)
03635         /*@modifies formatPrefix @*/
03636 {
03637     return realDateFormat(type, data, formatPrefix, padding, element,
03638                         _("%c"));
03639 }
03640 
03649 static char * dayFormat(int_32 type, hPTR_t data, 
03650                          char * formatPrefix, int padding, int element)
03651         /*@modifies formatPrefix @*/
03652 {
03653     return realDateFormat(type, data, formatPrefix, padding, element, 
03654                           _("%a %b %d %Y"));
03655 }
03656 
03665 static char * shescapeFormat(int_32 type, hPTR_t data, 
03666                 char * formatPrefix, int padding, /*@unused@*/int element)
03667         /*@modifies formatPrefix @*/
03668 {
03669     char * result, * dst, * src, * buf;
03670 
03671     if (type == RPM_INT32_TYPE) {
03672         result = xmalloc(padding + 20);
03673 /*@-boundswrite@*/
03674         strcat(formatPrefix, "d");
03675 /*@=boundswrite@*/
03676         /*@-formatconst@*/
03677         sprintf(result, formatPrefix, *((int_32 *) data));
03678         /*@=formatconst@*/
03679     } else {
03680         buf = alloca(strlen(data) + padding + 2);
03681 /*@-boundswrite@*/
03682         strcat(formatPrefix, "s");
03683 /*@=boundswrite@*/
03684         /*@-formatconst@*/
03685         sprintf(buf, formatPrefix, data);
03686         /*@=formatconst@*/
03687 
03688 /*@-boundswrite@*/
03689         result = dst = xmalloc(strlen(buf) * 4 + 3);
03690         *dst++ = '\'';
03691         for (src = buf; *src != '\0'; src++) {
03692             if (*src == '\'') {
03693                 *dst++ = '\'';
03694                 *dst++ = '\\';
03695                 *dst++ = '\'';
03696                 *dst++ = '\'';
03697             } else {
03698                 *dst++ = *src;
03699             }
03700         }
03701         *dst++ = '\'';
03702         *dst = '\0';
03703 /*@=boundswrite@*/
03704 
03705     }
03706 
03707     return result;
03708 }
03709 
03710 /*@-type@*/ /* FIX: cast? */
03711 const struct headerSprintfExtension_s headerDefaultFormats[] = {
03712     { HEADER_EXT_FORMAT, "octal", { octalFormat } },
03713     { HEADER_EXT_FORMAT, "hex", { hexFormat } },
03714     { HEADER_EXT_FORMAT, "date", { dateFormat } },
03715     { HEADER_EXT_FORMAT, "day", { dayFormat } },
03716     { HEADER_EXT_FORMAT, "shescape", { shescapeFormat } },
03717     { HEADER_EXT_LAST, NULL, { NULL } }
03718 };
03719 /*@=type@*/
03720 
03727 static
03728 void headerCopyTags(Header headerFrom, Header headerTo, hTAG_t tagstocopy)
03729         /*@modifies headerTo @*/
03730 {
03731     int * p;
03732 
03733     if (headerFrom == headerTo)
03734         return;
03735 
03736     for (p = tagstocopy; *p != 0; p++) {
03737         char *s;
03738         int_32 type;
03739         int_32 count;
03740         if (headerIsEntry(headerTo, *p))
03741             continue;
03742 /*@-boundswrite@*/
03743         if (!headerGetEntryMinMemory(headerFrom, *p, &type,
03744                                 (hPTR_t *) &s, &count))
03745             continue;
03746 /*@=boundswrite@*/
03747         (void) headerAddEntry(headerTo, *p, type, s, count);
03748         s = headerFreeData(s, type);
03749     }
03750 }
03751 
03752 /*@observer@*/ /*@unchecked@*/
03753 static struct HV_s hdrVec1 = {
03754     headerLink,
03755     headerUnlink,
03756     headerFree,
03757     headerNew,
03758     headerSort,
03759     headerUnsort,
03760     headerSizeof,
03761     headerUnload,
03762     headerReload,
03763     headerCopy,
03764     headerLoad,
03765     headerCopyLoad,
03766     headerRead,
03767     headerWrite,
03768     headerIsEntry,
03769     headerFreeTag,
03770     headerGetEntry,
03771     headerGetEntryMinMemory,
03772     headerAddEntry,
03773     headerAppendEntry,
03774     headerAddOrAppendEntry,
03775     headerAddI18NString,
03776     headerModifyEntry,
03777     headerRemoveEntry,
03778     headerSprintf,
03779     headerCopyTags,
03780     headerFreeIterator,
03781     headerInitIterator,
03782     headerNextIterator,
03783     NULL, NULL,
03784     1
03785 };
03786 
03787 /*@-compmempass -redef@*/
03788 /*@observer@*/ /*@unchecked@*/
03789 HV_t hdrVec = &hdrVec1;
03790 /*@=compmempass =redef@*/

Generated on Mon Mar 5 14:30:15 2007 for rpm by  doxygen 1.5.1