rpmdb/header.c

Go to the documentation of this file.
00001 
00005 /* RPM - Copyright (C) 1995-2002 Red Hat Software */
00006 
00007 /* Data written to file descriptors is in network byte order.    */
00008 /* Data read from file descriptors is expected to be in          */
00009 /* network byte order and is converted on the fly to host order. */
00010 
00011 #include "system.h"
00012 
00013 #define __HEADER_PROTOTYPES__
00014 
00015 #include <rpmio_internal.h>     /* XXX for fdGetOPath() */
00016 #include <header_internal.h>
00017 
00018 #include "debug.h"
00019 
00020 /*@unchecked@*/
00021 int _hdr_debug = 0;
00022 
00023 /*@access entryInfo @*/
00024 /*@access indexEntry @*/
00025 
00026 /*@access rpmec @*/
00027 /*@access sprintfTag @*/
00028 /*@access sprintfToken @*/
00029 /*@access HV_t @*/
00030 
00031 #define PARSER_BEGIN    0
00032 #define PARSER_IN_ARRAY 1
00033 #define PARSER_IN_EXPR  2
00034 
00037 /*@observer@*/ /*@unchecked@*/
00038 static unsigned char header_magic[8] = {
00039         0x8e, 0xad, 0xe8, 0x01, 0x00, 0x00, 0x00, 0x00
00040 };
00041 
00045 /*@observer@*/ /*@unchecked@*/
00046 static int typeAlign[16] =  {
00047     1,  
00048     1,  
00049     1,  
00050     2,  
00051     4,  
00052     8,  
00053     1,  
00054     1,  
00055     1,  
00056     1,  
00057     1,  
00058     1,  
00059     0,
00060     0,
00061     0,
00062     0
00063 };
00064 
00068 /*@observer@*/ /*@unchecked@*/
00069 static int typeSizes[16] =  { 
00070     0,  
00071     1,  
00072     1,  
00073     2,  
00074     4,  
00075     8,  
00076     -1, 
00077     1,  
00078     -1, 
00079     -1, 
00080     1,  
00081     1,  
00082     0,
00083     0,
00084     0,
00085     0
00086 };
00087 
00091 /*@unchecked@*/
00092 static size_t headerMaxbytes = (32*1024*1024);
00093 
00098 #define hdrchkTags(_ntags)      ((_ntags) & 0xffff0000)
00099 
00103 #define hdrchkType(_type) ((_type) < RPM_MIN_TYPE || (_type) > RPM_MAX_TYPE)
00104 
00109 #define hdrchkData(_nbytes)     ((_nbytes) & 0xff000000)
00110 
00114 #define hdrchkAlign(_type, _off)        ((_off) & (typeAlign[_type]-1))
00115 
00119 #define hdrchkRange(_dl, _off)          ((_off) < 0 || (_off) > (_dl))
00120 
00121 /*@observer@*/ /*@unchecked@*/
00122 HV_t hdrVec;    /* forward reference */
00123 
00129 /*@unused@*/ static inline /*@null@*/ void *
00130 _free(/*@only@*/ /*@null@*/ /*@out@*/ const void * p) /*@modifies *p @*/
00131 {
00132     if (p != NULL)      free((void *)p);
00133     return NULL;
00134 }
00135 
00141 static
00142 Header headerLink(Header h)
00143         /*@modifies h @*/
00144 {
00145 /*@-nullret@*/
00146     if (h == NULL) return NULL;
00147 /*@=nullret@*/
00148 
00149     h->nrefs++;
00150 /*@-modfilesys@*/
00151 if (_hdr_debug)
00152 fprintf(stderr, "--> h  %p ++ %d at %s:%u\n", h, h->nrefs, __FILE__, __LINE__);
00153 /*@=modfilesys@*/
00154 
00155     /*@-refcounttrans @*/
00156     return h;
00157     /*@=refcounttrans @*/
00158 }
00159 
00165 static /*@null@*/
00166 Header headerUnlink(/*@killref@*/ /*@null@*/ Header h)
00167         /*@modifies h @*/
00168 {
00169     if (h == NULL) return NULL;
00170 /*@-modfilesys@*/
00171 if (_hdr_debug)
00172 fprintf(stderr, "--> h  %p -- %d at %s:%u\n", h, h->nrefs, __FILE__, __LINE__);
00173 /*@=modfilesys@*/
00174     h->nrefs--;
00175     return NULL;
00176 }
00177 
00183 static /*@null@*/
00184 Header headerFree(/*@killref@*/ /*@null@*/ Header h)
00185         /*@modifies h @*/
00186 {
00187     (void) headerUnlink(h);
00188 
00189     /*@-usereleased@*/
00190     if (h == NULL || h->nrefs > 0)
00191         return NULL;    /* XXX return previous header? */
00192 
00193     if (h->index) {
00194         indexEntry entry = h->index;
00195         int i;
00196         for (i = 0; i < h->indexUsed; i++, entry++) {
00197             if ((h->flags & HEADERFLAG_ALLOCATED) && ENTRY_IS_REGION(entry)) {
00198                 if (entry->length > 0) {
00199                     int_32 * ei = entry->data;
00200                     if ((ei - 2) == h->blob) h->blob = _free(h->blob);
00201                     entry->data = NULL;
00202                 }
00203             } else if (!ENTRY_IN_REGION(entry)) {
00204                 entry->data = _free(entry->data);
00205             }
00206             entry->data = NULL;
00207         }
00208         h->index = _free(h->index);
00209     }
00210     h->origin = _free(h->origin);
00211 
00212     /*@-refcounttrans@*/ h = _free(h); /*@=refcounttrans@*/
00213     return h;
00214     /*@=usereleased@*/
00215 }
00216 
00221 static
00222 Header headerNew(void)
00223         /*@*/
00224 {
00225     Header h = xcalloc(1, sizeof(*h));
00226 
00227 /*@-boundsread@*/
00228     /*@-assignexpose@*/
00229     h->hv = *hdrVec;            /* structure assignment */
00230     /*@=assignexpose@*/
00231 /*@=boundsread@*/
00232     h->blob = NULL;
00233     h->origin = NULL;
00234     h->instance = 0;
00235     h->indexAlloced = INDEX_MALLOC_SIZE;
00236     h->indexUsed = 0;
00237     h->flags |= HEADERFLAG_SORTED;
00238 
00239     h->index = (h->indexAlloced
00240         ? xcalloc(h->indexAlloced, sizeof(*h->index))
00241         : NULL);
00242 
00243     h->nrefs = 0;
00244     /*@-globstate -observertrans @*/
00245     return headerLink(h);
00246     /*@=globstate =observertrans @*/
00247 }
00248 
00251 static int indexCmp(const void * avp, const void * bvp)
00252         /*@*/
00253 {
00254     /*@-castexpose@*/
00255     indexEntry ap = (indexEntry) avp, bp = (indexEntry) bvp;
00256     /*@=castexpose@*/
00257     return (ap->info.tag - bp->info.tag);
00258 }
00259 
00264 static
00265 void headerSort(Header h)
00266         /*@modifies h @*/
00267 {
00268     if (!(h->flags & HEADERFLAG_SORTED)) {
00269 /*@-boundsread@*/
00270         qsort(h->index, h->indexUsed, sizeof(*h->index), indexCmp);
00271 /*@=boundsread@*/
00272         h->flags |= HEADERFLAG_SORTED;
00273     }
00274 }
00275 
00278 static int offsetCmp(const void * avp, const void * bvp) /*@*/
00279 {
00280     /*@-castexpose@*/
00281     indexEntry ap = (indexEntry) avp, bp = (indexEntry) bvp;
00282     /*@=castexpose@*/
00283     int rc = (ap->info.offset - bp->info.offset);
00284 
00285     if (rc == 0) {
00286         /* Within a region, entries sort by address. Added drips sort by tag. */
00287         if (ap->info.offset < 0)
00288             rc = (((char *)ap->data) - ((char *)bp->data));
00289         else
00290             rc = (ap->info.tag - bp->info.tag);
00291     }
00292     return rc;
00293 }
00294 
00299 static
00300 void headerUnsort(Header h)
00301         /*@modifies h @*/
00302 {
00303 /*@-boundsread@*/
00304     qsort(h->index, h->indexUsed, sizeof(*h->index), offsetCmp);
00305 /*@=boundsread@*/
00306 }
00307 
00314 static
00315 unsigned int headerSizeof(/*@null@*/ Header h, enum hMagic magicp)
00316         /*@modifies h @*/
00317 {
00318     indexEntry entry;
00319     unsigned int size = 0;
00320     unsigned int pad = 0;
00321     int i;
00322 
00323     if (h == NULL)
00324         return size;
00325 
00326     headerSort(h);
00327 
00328     switch (magicp) {
00329     case HEADER_MAGIC_YES:
00330         size += sizeof(header_magic);
00331         break;
00332     case HEADER_MAGIC_NO:
00333         break;
00334     }
00335 
00336     /*@-sizeoftype@*/
00337     size += 2 * sizeof(int_32); /* count of index entries */
00338     /*@=sizeoftype@*/
00339 
00340     for (i = 0, entry = h->index; i < h->indexUsed; i++, entry++) {
00341         unsigned diff;
00342         int_32 type;
00343 
00344         /* Regions go in as is ... */
00345         if (ENTRY_IS_REGION(entry)) {
00346             size += entry->length;
00347             /* XXX Legacy regions do not include the region tag and data. */
00348             /*@-sizeoftype@*/
00349             if (i == 0 && (h->flags & HEADERFLAG_LEGACY))
00350                 size += sizeof(struct entryInfo_s) + entry->info.count;
00351             /*@=sizeoftype@*/
00352             continue;
00353         }
00354 
00355         /* ... and region elements are skipped. */
00356         if (entry->info.offset < 0)
00357             continue;
00358 
00359         /* Alignment */
00360         type = entry->info.type;
00361 /*@-boundsread@*/
00362         if (typeSizes[type] > 1) {
00363             diff = typeSizes[type] - (size % typeSizes[type]);
00364             if (diff != typeSizes[type]) {
00365                 size += diff;
00366                 pad += diff;
00367             }
00368         }
00369 /*@=boundsread@*/
00370 
00371         /*@-sizeoftype@*/
00372         size += sizeof(struct entryInfo_s) + entry->length;
00373         /*@=sizeoftype@*/
00374     }
00375 
00376     return size;
00377 }
00378 
00388 static int dataLength(int_32 type, hPTR_t p, int_32 count, int onDisk,
00389                 /*@null@*/ hPTR_t pend)
00390         /*@*/
00391 {
00392     const unsigned char * s = p;
00393     const unsigned char * se = pend;
00394     int length = 0;
00395 
00396     switch (type) {
00397     case RPM_STRING_TYPE:
00398         if (count != 1)
00399             return -1;
00400 /*@-boundsread@*/
00401         while (*s++) {
00402             if (se && s > se)
00403                 return -1;
00404             length++;
00405         }
00406 /*@=boundsread@*/
00407         length++;       /* count nul terminator too. */
00408         break;
00409 
00410     case RPM_STRING_ARRAY_TYPE:
00411     case RPM_I18NSTRING_TYPE:
00412         /* These are like RPM_STRING_TYPE, except they're *always* an array */
00413         /* Compute sum of length of all strings, including nul terminators */
00414 
00415         if (onDisk) {
00416             while (count--) {
00417                 length++;       /* count nul terminator too */
00418 /*@-boundsread@*/
00419                while (*s++) {
00420                     if (se && s > se)
00421                         return -1;
00422                     length++;
00423                 }
00424 /*@=boundsread@*/
00425             }
00426         } else {
00427             const char ** av = (const char **)p;
00428 /*@-boundsread@*/
00429             while (count--) {
00430                 /* add one for null termination */
00431                 length += strlen(*av++) + 1;
00432             }
00433 /*@=boundsread@*/
00434         }
00435         break;
00436 
00437     default:
00438 /*@-boundsread@*/
00439         if (typeSizes[type] == -1)
00440             return -1;
00441         length = typeSizes[(type & 0xf)] * count;
00442 /*@=boundsread@*/
00443         if (length < 0 || (se && (s + length) > se))
00444             return -1;
00445         break;
00446     }
00447 
00448     return length;
00449 }
00450 
00477 static int regionSwab(/*@null@*/ indexEntry entry, int il, int dl,
00478                 entryInfo pe,
00479                 unsigned char * dataStart,
00480                 /*@null@*/ const unsigned char * dataEnd,
00481                 int regionid)
00482         /*@modifies *entry, *dataStart @*/
00483 {
00484     unsigned char * tprev = NULL;
00485     unsigned char * t = NULL;
00486     int tdel = 0;
00487     int tl = dl;
00488     struct indexEntry_s ieprev;
00489 
00490 /*@-boundswrite@*/
00491     memset(&ieprev, 0, sizeof(ieprev));
00492 /*@=boundswrite@*/
00493     for (; il > 0; il--, pe++) {
00494         struct indexEntry_s ie;
00495         int_32 type;
00496 
00497         ie.info.tag = ntohl(pe->tag);
00498         ie.info.type = ntohl(pe->type);
00499         ie.info.count = ntohl(pe->count);
00500         ie.info.offset = ntohl(pe->offset);
00501 
00502         if (hdrchkType(ie.info.type))
00503             return -1;
00504         if (hdrchkData(ie.info.count))
00505             return -1;
00506         if (hdrchkData(ie.info.offset))
00507             return -1;
00508 /*@-boundsread@*/
00509         if (hdrchkAlign(ie.info.type, ie.info.offset))
00510             return -1;
00511 /*@=boundsread@*/
00512 
00513         ie.data = t = dataStart + ie.info.offset;
00514         if (dataEnd && t >= dataEnd)
00515             return -1;
00516 
00517         ie.length = dataLength(ie.info.type, ie.data, ie.info.count, 1, dataEnd);
00518         if (ie.length < 0 || hdrchkData(ie.length))
00519             return -1;
00520 
00521         ie.rdlen = 0;
00522 
00523         if (entry) {
00524             ie.info.offset = regionid;
00525 /*@-boundswrite@*/
00526             *entry = ie;        /* structure assignment */
00527 /*@=boundswrite@*/
00528             entry++;
00529         }
00530 
00531         /* Alignment */
00532         type = ie.info.type;
00533 /*@-boundsread@*/
00534         if (typeSizes[type] > 1) {
00535             unsigned diff;
00536             diff = typeSizes[type] - (dl % typeSizes[type]);
00537             if (diff != typeSizes[type]) {
00538                 dl += diff;
00539                 if (ieprev.info.type == RPM_I18NSTRING_TYPE)
00540                     ieprev.length += diff;
00541             }
00542         }
00543 /*@=boundsread@*/
00544         tdel = (tprev ? (t - tprev) : 0);
00545         if (ieprev.info.type == RPM_I18NSTRING_TYPE)
00546             tdel = ieprev.length;
00547 
00548         if (ie.info.tag >= HEADER_I18NTABLE) {
00549             tprev = t;
00550         } else {
00551             tprev = dataStart;
00552             /* XXX HEADER_IMAGE tags don't include region sub-tag. */
00553             /*@-sizeoftype@*/
00554             if (ie.info.tag == HEADER_IMAGE)
00555                 tprev -= REGION_TAG_COUNT;
00556             /*@=sizeoftype@*/
00557         }
00558 
00559         /* Perform endian conversions */
00560         switch (ntohl(pe->type)) {
00561 /*@-bounds@*/
00562         case RPM_INT64_TYPE:
00563         {   int_64 * it = (int_64 *)t;
00564             int_32 b[2];
00565             for (; ie.info.count > 0; ie.info.count--, it += 1) {
00566                 if (dataEnd && ((unsigned char *)it) >= dataEnd)
00567                     return -1;
00568                 b[1] = htonl(((int_32 *)it)[0]);
00569                 b[0] = htonl(((int_32 *)it)[1]);
00570                 if (b[1] != ((int_32 *)it)[0])
00571                     memcpy(it, b, sizeof(b));
00572             }
00573             t = (char *) it;
00574         }   /*@switchbreak@*/ break;
00575         case RPM_INT32_TYPE:
00576         {   int_32 * it = (int_32 *)t;
00577             for (; ie.info.count > 0; ie.info.count--, it += 1) {
00578                 if (dataEnd && ((unsigned char *)it) >= dataEnd)
00579                     return -1;
00580                 *it = htonl(*it);
00581             }
00582             t = (char *) it;
00583         }   /*@switchbreak@*/ break;
00584         case RPM_INT16_TYPE:
00585         {   int_16 * it = (int_16 *) t;
00586             for (; ie.info.count > 0; ie.info.count--, it += 1) {
00587                 if (dataEnd && ((unsigned char *)it) >= dataEnd)
00588                     return -1;
00589                 *it = htons(*it);
00590             }
00591             t = (char *) it;
00592         }   /*@switchbreak@*/ break;
00593 /*@=bounds@*/
00594         default:
00595             t += ie.length;
00596             /*@switchbreak@*/ break;
00597         }
00598 
00599         dl += ie.length;
00600         if (dataEnd && dataStart + dl > dataEnd) return -1;
00601         tl += tdel;
00602         ieprev = ie;    /* structure assignment */
00603 
00604     }
00605     tdel = (tprev ? (t - tprev) : 0);
00606     tl += tdel;
00607 
00608     /* XXX
00609      * There are two hacks here:
00610      *  1) tl is 16b (i.e. REGION_TAG_COUNT) short while doing headerReload().
00611      *  2) the 8/98 rpm bug with inserting i18n tags needs to use tl, not dl.
00612      */
00613     /*@-sizeoftype@*/
00614     if (tl+REGION_TAG_COUNT == dl)
00615         tl += REGION_TAG_COUNT;
00616     /*@=sizeoftype@*/
00617 
00618     return dl;
00619 }
00620 
00626 static /*@only@*/ /*@null@*/ void * doHeaderUnload(Header h,
00627                 /*@out@*/ int * lengthPtr)
00628         /*@modifies h, *lengthPtr @*/
00629         /*@requires maxSet(lengthPtr) >= 0 @*/
00630         /*@ensures maxRead(result) == (*lengthPtr) @*/
00631 {
00632     int_32 * ei = NULL;
00633     entryInfo pe;
00634     char * dataStart;
00635     char * te;
00636     unsigned pad;
00637     unsigned len;
00638     int_32 il = 0;
00639     int_32 dl = 0;
00640     indexEntry entry; 
00641     int_32 type;
00642     int i;
00643     int drlen, ndribbles;
00644     int driplen, ndrips;
00645     int legacy = 0;
00646 
00647     /* Sort entries by (offset,tag). */
00648     headerUnsort(h);
00649 
00650     /* Compute (il,dl) for all tags, including those deleted in region. */
00651     pad = 0;
00652     drlen = ndribbles = driplen = ndrips = 0;
00653     for (i = 0, entry = h->index; i < h->indexUsed; i++, entry++) {
00654         if (ENTRY_IS_REGION(entry)) {
00655             int_32 rdl = -entry->info.offset;   /* negative offset */
00656             int_32 ril = rdl/sizeof(*pe);
00657             int rid = entry->info.offset;
00658 
00659             il += ril;
00660             dl += entry->rdlen + entry->info.count;
00661             /* XXX Legacy regions do not include the region tag and data. */
00662             if (i == 0 && (h->flags & HEADERFLAG_LEGACY))
00663                 il += 1;
00664 
00665             /* Skip rest of entries in region, but account for dribbles. */
00666             for (; i < h->indexUsed && entry->info.offset <= rid+1; i++, entry++) {
00667                 if (entry->info.offset <= rid)
00668                     /*@innercontinue@*/ continue;
00669 
00670                 /* Alignment */
00671                 type = entry->info.type;
00672                 if (typeSizes[type] > 1) {
00673                     unsigned diff;
00674                     diff = typeSizes[type] - (dl % typeSizes[type]);
00675                     if (diff != typeSizes[type]) {
00676                         drlen += diff;
00677                         pad += diff;
00678                         dl += diff;
00679                     }
00680                 }
00681 
00682                 ndribbles++;
00683                 il++;
00684                 drlen += entry->length;
00685                 dl += entry->length;
00686             }
00687             i--;
00688             entry--;
00689             continue;
00690         }
00691 
00692         /* Ignore deleted drips. */
00693         if (entry->data == NULL || entry->length <= 0)
00694             continue;
00695 
00696         /* Alignment */
00697         type = entry->info.type;
00698         if (typeSizes[type] > 1) {
00699             unsigned diff;
00700             diff = typeSizes[type] - (dl % typeSizes[type]);
00701             if (diff != typeSizes[type]) {
00702                 driplen += diff;
00703                 pad += diff;
00704                 dl += diff;
00705             } else
00706                 diff = 0;
00707         }
00708 
00709         ndrips++;
00710         il++;
00711         driplen += entry->length;
00712         dl += entry->length;
00713     }
00714 
00715     /* Sanity checks on header intro. */
00716     if (hdrchkTags(il) || hdrchkData(dl))
00717         goto errxit;
00718 
00719     len = sizeof(il) + sizeof(dl) + (il * sizeof(*pe)) + dl;
00720 
00721 /*@-boundswrite@*/
00722     ei = xmalloc(len);
00723     ei[0] = htonl(il);
00724     ei[1] = htonl(dl);
00725 /*@=boundswrite@*/
00726 
00727     pe = (entryInfo) &ei[2];
00728     dataStart = te = (char *) (pe + il);
00729 
00730     pad = 0;
00731     for (i = 0, entry = h->index; i < h->indexUsed; i++, entry++) {
00732         const char * src;
00733 char *t;
00734         int count;
00735         int rdlen;
00736 
00737         if (entry->data == NULL || entry->length <= 0)
00738             continue;
00739 
00740 t = te;
00741         pe->tag = htonl(entry->info.tag);
00742         pe->type = htonl(entry->info.type);
00743         pe->count = htonl(entry->info.count);
00744 
00745         if (ENTRY_IS_REGION(entry)) {
00746             int_32 rdl = -entry->info.offset;   /* negative offset */
00747             int_32 ril = rdl/sizeof(*pe) + ndribbles;
00748             int rid = entry->info.offset;
00749 
00750             src = (char *)entry->data;
00751             rdlen = entry->rdlen;
00752 
00753             /* XXX Legacy regions do not include the region tag and data. */
00754             if (i == 0 && (h->flags & HEADERFLAG_LEGACY)) {
00755                 int_32 stei[4];
00756 
00757                 legacy = 1;
00758 /*@-boundswrite@*/
00759                 memcpy(pe+1, src, rdl);
00760                 memcpy(te, src + rdl, rdlen);
00761 /*@=boundswrite@*/
00762                 te += rdlen;
00763 
00764                 pe->offset = htonl(te - dataStart);
00765                 stei[0] = pe->tag;
00766                 stei[1] = pe->type;
00767                 stei[2] = htonl(-rdl-entry->info.count);
00768                 stei[3] = pe->count;
00769 /*@-boundswrite@*/
00770                 memcpy(te, stei, entry->info.count);
00771 /*@=boundswrite@*/
00772                 te += entry->info.count;
00773                 ril++;
00774                 rdlen += entry->info.count;
00775 
00776                 count = regionSwab(NULL, ril, 0, pe, t, NULL, 0);
00777                 if (count != rdlen)
00778                     goto errxit;
00779 
00780             } else {
00781 
00782 /*@-boundswrite@*/
00783                 memcpy(pe+1, src + sizeof(*pe), ((ril-1) * sizeof(*pe)));
00784                 memcpy(te, src + (ril * sizeof(*pe)), rdlen+entry->info.count+drlen);
00785 /*@=boundswrite@*/
00786                 te += rdlen;
00787                 {   /*@-castexpose@*/
00788                     entryInfo se = (entryInfo)src;
00789                     /*@=castexpose@*/
00790                     int off = ntohl(se->offset);
00791                     pe->offset = (off) ? htonl(te - dataStart) : htonl(off);
00792                 }
00793                 te += entry->info.count + drlen;
00794 
00795                 count = regionSwab(NULL, ril, 0, pe, t, NULL, 0);
00796                 if (count != (rdlen + entry->info.count + drlen))
00797                     goto errxit;
00798             }
00799 
00800             /* Skip rest of entries in region. */
00801             while (i < h->indexUsed && entry->info.offset <= rid+1) {
00802                 i++;
00803                 entry++;
00804             }
00805             i--;
00806             entry--;
00807             pe += ril;
00808             continue;
00809         }
00810 
00811         /* Ignore deleted drips. */
00812         if (entry->data == NULL || entry->length <= 0)
00813             continue;
00814 
00815         /* Alignment */
00816         type = entry->info.type;
00817         if (typeSizes[type] > 1) {
00818             unsigned diff;
00819             diff = typeSizes[type] - ((te - dataStart) % typeSizes[type]);
00820             if (diff != typeSizes[type]) {
00821 /*@-boundswrite@*/
00822                 memset(te, 0, diff);
00823 /*@=boundswrite@*/
00824                 te += diff;
00825                 pad += diff;
00826             }
00827         }
00828 
00829         pe->offset = htonl(te - dataStart);
00830 
00831         /* copy data w/ endian conversions */
00832 /*@-boundswrite@*/
00833         switch (entry->info.type) {
00834         case RPM_INT64_TYPE:
00835         {   int_32 b[2];
00836             count = entry->info.count;
00837             src = entry->data;
00838             while (count--) {
00839                 b[1] = htonl(((int_32 *)src)[0]);
00840                 b[0] = htonl(((int_32 *)src)[1]);
00841                 if (b[1] == ((int_32 *)src)[0])
00842                     memcpy(te, src, sizeof(b));
00843                 else
00844                     memcpy(te, b, sizeof(b));
00845                 te += sizeof(b);
00846                 src += sizeof(b);
00847             }
00848         }   /*@switchbreak@*/ break;
00849 
00850         case RPM_INT32_TYPE:
00851             count = entry->info.count;
00852             src = entry->data;
00853             while (count--) {
00854                 *((int_32 *)te) = htonl(*((int_32 *)src));
00855                 /*@-sizeoftype@*/
00856                 te += sizeof(int_32);
00857                 src += sizeof(int_32);
00858                 /*@=sizeoftype@*/
00859             }
00860             /*@switchbreak@*/ break;
00861 
00862         case RPM_INT16_TYPE:
00863             count = entry->info.count;
00864             src = entry->data;
00865             while (count--) {
00866                 *((int_16 *)te) = htons(*((int_16 *)src));
00867                 /*@-sizeoftype@*/
00868                 te += sizeof(int_16);
00869                 src += sizeof(int_16);
00870                 /*@=sizeoftype@*/
00871             }
00872             /*@switchbreak@*/ break;
00873 
00874         default:
00875             memcpy(te, entry->data, entry->length);
00876             te += entry->length;
00877             /*@switchbreak@*/ break;
00878         }
00879 /*@=boundswrite@*/
00880         pe++;
00881     }
00882    
00883     /* Insure that there are no memcpy underruns/overruns. */
00884     if (((char *)pe) != dataStart)
00885         goto errxit;
00886     if ((((char *)ei)+len) != te)
00887         goto errxit;
00888 
00889     if (lengthPtr)
00890         *lengthPtr = len;
00891 
00892     h->flags &= ~HEADERFLAG_SORTED;
00893     headerSort(h);
00894 
00895     return (void *) ei;
00896 
00897 errxit:
00898     /*@-usereleased@*/
00899     ei = _free(ei);
00900     /*@=usereleased@*/
00901     return (void *) ei;
00902 }
00903 
00909 static /*@only@*/ /*@null@*/
00910 void * headerUnload(Header h)
00911         /*@modifies h @*/
00912 {
00913     int length;
00914 /*@-boundswrite@*/
00915     void * uh = doHeaderUnload(h, &length);
00916 /*@=boundswrite@*/
00917     return uh;
00918 }
00919 
00927 static /*@null@*/
00928 indexEntry findEntry(/*@null@*/ Header h, int_32 tag, int_32 type)
00929         /*@modifies h @*/
00930 {
00931     indexEntry entry, entry2, last;
00932     struct indexEntry_s key;
00933 
00934     if (h == NULL) return NULL;
00935     if (!(h->flags & HEADERFLAG_SORTED)) headerSort(h);
00936 
00937     key.info.tag = tag;
00938 
00939 /*@-boundswrite@*/
00940     entry2 = entry = 
00941         bsearch(&key, h->index, h->indexUsed, sizeof(*h->index), indexCmp);
00942 /*@=boundswrite@*/
00943     if (entry == NULL)
00944         return NULL;
00945 
00946     if (type == RPM_NULL_TYPE)
00947         return entry;
00948 
00949     /* look backwards */
00950     while (entry->info.tag == tag && entry->info.type != type &&
00951            entry > h->index) entry--;
00952 
00953     if (entry->info.tag == tag && entry->info.type == type)
00954         return entry;
00955 
00956     last = h->index + h->indexUsed;
00957     /*@-usereleased@*/ /* FIX: entry2 = entry. Code looks bogus as well. */
00958     while (entry2->info.tag == tag && entry2->info.type != type &&
00959            entry2 < last) entry2++;
00960     /*@=usereleased@*/
00961 
00962     if (entry->info.tag == tag && entry->info.type == type)
00963         return entry;
00964 
00965     return NULL;
00966 }
00967 
00977 static
00978 int headerRemoveEntry(Header h, int_32 tag)
00979         /*@modifies h @*/
00980 {
00981     indexEntry last = h->index + h->indexUsed;
00982     indexEntry entry, first;
00983     int ne;
00984 
00985     entry = findEntry(h, tag, RPM_NULL_TYPE);
00986     if (!entry) return 1;
00987 
00988     /* Make sure entry points to the first occurence of this tag. */
00989     while (entry > h->index && (entry - 1)->info.tag == tag)  
00990         entry--;
00991 
00992     /* Free data for tags being removed. */
00993     for (first = entry; first < last; first++) {
00994         void * data;
00995         if (first->info.tag != tag)
00996             break;
00997         data = first->data;
00998         first->data = NULL;
00999         first->length = 0;
01000         if (ENTRY_IN_REGION(first))
01001             continue;
01002         data = _free(data);
01003     }
01004 
01005     ne = (first - entry);
01006     if (ne > 0) {
01007         h->indexUsed -= ne;
01008         ne = last - first;
01009 /*@-boundswrite@*/
01010         if (ne > 0)
01011             memmove(entry, first, (ne * sizeof(*entry)));
01012 /*@=boundswrite@*/
01013     }
01014 
01015     return 0;
01016 }
01017 
01023 static /*@null@*/
01024 Header headerLoad(/*@kept@*/ void * uh)
01025         /*@modifies uh @*/
01026 {
01027     int_32 * ei = (int_32 *) uh;
01028     int_32 il = ntohl(ei[0]);           /* index length */
01029     int_32 dl = ntohl(ei[1]);           /* data length */
01030     /*@-sizeoftype@*/
01031     size_t pvlen = sizeof(il) + sizeof(dl) +
01032                (il * sizeof(struct entryInfo_s)) + dl;
01033     /*@=sizeoftype@*/
01034     void * pv = uh;
01035     Header h = NULL;
01036     entryInfo pe;
01037     unsigned char * dataStart;
01038     unsigned char * dataEnd;
01039     indexEntry entry; 
01040     int rdlen;
01041     int i;
01042 
01043     /* Sanity checks on header intro. */
01044     if (hdrchkTags(il) || hdrchkData(dl))
01045         goto errxit;
01046 
01047     ei = (int_32 *) pv;
01048     /*@-castexpose@*/
01049     pe = (entryInfo) &ei[2];
01050     /*@=castexpose@*/
01051     dataStart = (unsigned char *) (pe + il);
01052     dataEnd = dataStart + dl;
01053 
01054     h = xcalloc(1, sizeof(*h));
01055     /*@-assignexpose@*/
01056     h->hv = *hdrVec;            /* structure assignment */
01057     /*@=assignexpose@*/
01058     /*@-assignexpose -kepttrans@*/
01059     h->blob = uh;
01060     /*@=assignexpose =kepttrans@*/
01061     h->indexAlloced = il + 1;
01062     h->indexUsed = il;
01063     h->index = xcalloc(h->indexAlloced, sizeof(*h->index));
01064     h->flags |= HEADERFLAG_SORTED;
01065     h->nrefs = 0;
01066     h = headerLink(h);
01067 
01068     /*
01069      * XXX XFree86-libs, ash, and pdksh from Red Hat 5.2 have bogus
01070      * %verifyscript tag that needs to be diddled.
01071      */
01072     if (ntohl(pe->tag) == 15 &&
01073         ntohl(pe->type) == RPM_STRING_TYPE &&
01074         ntohl(pe->count) == 1)
01075     {
01076         pe->tag = htonl(1079);
01077     }
01078 
01079     entry = h->index;
01080     i = 0;
01081     if (!(htonl(pe->tag) < HEADER_I18NTABLE)) {
01082         h->flags |= HEADERFLAG_LEGACY;
01083         entry->info.type = REGION_TAG_TYPE;
01084         entry->info.tag = HEADER_IMAGE;
01085         /*@-sizeoftype@*/
01086         entry->info.count = REGION_TAG_COUNT;
01087         /*@=sizeoftype@*/
01088         entry->info.offset = ((unsigned char *)pe - dataStart); /* negative offset */
01089 
01090         /*@-assignexpose@*/
01091         entry->data = pe;
01092         /*@=assignexpose@*/
01093         entry->length = pvlen - sizeof(il) - sizeof(dl);
01094         rdlen = regionSwab(entry+1, il, 0, pe, dataStart, dataEnd, entry->info.offset);
01095 #if 0   /* XXX don't check, the 8/98 i18n bug fails here. */
01096         if (rdlen != dl)
01097             goto errxit;
01098 #endif
01099         entry->rdlen = rdlen;
01100         entry++;
01101         h->indexUsed++;
01102     } else {
01103         int_32 rdl;
01104         int_32 ril;
01105 
01106         h->flags &= ~HEADERFLAG_LEGACY;
01107 
01108         entry->info.type = htonl(pe->type);
01109         entry->info.count = htonl(pe->count);
01110 
01111         if (hdrchkType(entry->info.type))
01112             goto errxit;
01113         if (hdrchkTags(entry->info.count))
01114             goto errxit;
01115 
01116         {   int off = ntohl(pe->offset);
01117 
01118             if (hdrchkData(off))
01119                 goto errxit;
01120             if (off) {
01121 /*@-sizeoftype@*/
01122                 size_t nb = REGION_TAG_COUNT;
01123 /*@=sizeoftype@*/
01124                 int_32 * stei = memcpy(alloca(nb), dataStart + off, nb);
01125                 rdl = -ntohl(stei[2]);  /* negative offset */
01126                 ril = rdl/sizeof(*pe);
01127                 if (hdrchkTags(ril) || hdrchkData(rdl))
01128                     goto errxit;
01129                 entry->info.tag = htonl(pe->tag);
01130             } else {
01131                 ril = il;
01132                 /*@-sizeoftype@*/
01133                 rdl = (ril * sizeof(struct entryInfo_s));
01134                 /*@=sizeoftype@*/
01135                 entry->info.tag = HEADER_IMAGE;
01136             }
01137         }
01138         entry->info.offset = -rdl;      /* negative offset */
01139 
01140         /*@-assignexpose@*/
01141         entry->data = pe;
01142         /*@=assignexpose@*/
01143         entry->length = pvlen - sizeof(il) - sizeof(dl);
01144         rdlen = regionSwab(entry+1, ril-1, 0, pe+1, dataStart, dataEnd, entry->info.offset);
01145         if (rdlen < 0)
01146             goto errxit;
01147         entry->rdlen = rdlen;
01148 
01149         if (ril < h->indexUsed) {
01150             indexEntry newEntry = entry + ril;
01151             int ne = (h->indexUsed - ril);
01152             int rid = entry->info.offset+1;
01153             int rc;
01154 
01155             /* Load dribble entries from region. */
01156             rc = regionSwab(newEntry, ne, 0, pe+ril, dataStart, dataEnd, rid);
01157             if (rc < 0)
01158                 goto errxit;
01159             rdlen += rc;
01160 
01161           { indexEntry firstEntry = newEntry;
01162             int save = h->indexUsed;
01163             int j;
01164 
01165             /* Dribble entries replace duplicate region entries. */
01166             h->indexUsed -= ne;
01167             for (j = 0; j < ne; j++, newEntry++) {
01168                 (void) headerRemoveEntry(h, newEntry->info.tag);
01169                 if (newEntry->info.tag == HEADER_BASENAMES)
01170                     (void) headerRemoveEntry(h, HEADER_OLDFILENAMES);
01171             }
01172 
01173             /* If any duplicate entries were replaced, move new entries down. */
01174 /*@-boundswrite@*/
01175             if (h->indexUsed < (save - ne)) {
01176                 memmove(h->index + h->indexUsed, firstEntry,
01177                         (ne * sizeof(*entry)));
01178             }
01179 /*@=boundswrite@*/
01180             h->indexUsed += ne;
01181           }
01182         }
01183     }
01184 
01185     h->flags &= ~HEADERFLAG_SORTED;
01186     headerSort(h);
01187 
01188     /*@-globstate -observertrans @*/
01189     return h;
01190     /*@=globstate =observertrans @*/
01191 
01192 errxit:
01193     /*@-usereleased@*/
01194     if (h) {
01195         h->index = _free(h->index);
01196         /*@-refcounttrans@*/
01197         h = _free(h);
01198         /*@=refcounttrans@*/
01199     }
01200     /*@=usereleased@*/
01201     /*@-refcounttrans -globstate@*/
01202     return h;
01203     /*@=refcounttrans =globstate@*/
01204 }
01205 
01211 static /*@observer@*/ /*@null@*/
01212 const char * headerGetOrigin(/*@null@*/ Header h)
01213         /*@*/
01214 {
01215     return (h != NULL ? h->origin : NULL);
01216 }
01217 
01224 static
01225 int headerSetOrigin(/*@null@*/ Header h, const char * origin)
01226         /*@modifies h @*/
01227 {
01228     if (h != NULL) {
01229         h->origin = _free(h->origin);
01230         h->origin = xstrdup(origin);
01231     }
01232     return 0;
01233 }
01234 
01240 static
01241 int headerGetInstance(/*@null@*/ Header h)
01242         /*@*/
01243 {
01244     return (h != NULL ? h->instance : 0);
01245 }
01246 
01253 static
01254 int headerSetInstance(/*@null@*/ Header h, int instance)
01255         /*@modifies h @*/
01256 {
01257     if (h != NULL)
01258         h->instance = instance;
01259     return 0;
01260 }
01261 
01269 static /*@null@*/
01270 Header headerReload(/*@only@*/ Header h, int tag)
01271         /*@modifies h @*/
01272 {
01273     Header nh;
01274     int length;
01275     /*@-onlytrans@*/
01276 /*@-boundswrite@*/
01277     void * uh = doHeaderUnload(h, &length);
01278 /*@=boundswrite@*/
01279     const char * origin;
01280     int_32 instance = h->instance;
01281     int xx;
01282 
01283     origin = (h->origin != NULL ? xstrdup(h->origin) : NULL);
01284     h = headerFree(h);
01285     /*@=onlytrans@*/
01286     if (uh == NULL)
01287         return NULL;
01288     nh = headerLoad(uh);
01289     if (nh == NULL) {
01290         uh = _free(uh);
01291         return NULL;
01292     }
01293     if (nh->flags & HEADERFLAG_ALLOCATED)
01294         uh = _free(uh);
01295     nh->flags |= HEADERFLAG_ALLOCATED;
01296     if (ENTRY_IS_REGION(nh->index)) {
01297 /*@-boundswrite@*/
01298         if (tag == HEADER_SIGNATURES || tag == HEADER_IMMUTABLE)
01299             nh->index[0].info.tag = tag;
01300 /*@=boundswrite@*/
01301     }
01302     if (origin != NULL) {
01303         xx = headerSetOrigin(nh, origin);
01304         origin = _free(origin);
01305     }
01306     xx = headerSetInstance(nh, instance);
01307     return nh;
01308 }
01309 
01315 static /*@null@*/
01316 Header headerCopyLoad(const void * uh)
01317         /*@*/
01318 {
01319     int_32 * ei = (int_32 *) uh;
01320 /*@-boundsread@*/
01321     int_32 il = ntohl(ei[0]);           /* index length */
01322     int_32 dl = ntohl(ei[1]);           /* data length */
01323 /*@=boundsread@*/
01324     /*@-sizeoftype@*/
01325     size_t pvlen = sizeof(il) + sizeof(dl) +
01326                         (il * sizeof(struct entryInfo_s)) + dl;
01327     /*@=sizeoftype@*/
01328     void * nuh = NULL;
01329     Header h = NULL;
01330 
01331     /* Sanity checks on header intro. */
01332     /*@-branchstate@*/
01333     if (!(hdrchkTags(il) || hdrchkData(dl)) && pvlen < headerMaxbytes) {
01334 /*@-boundsread@*/
01335         nuh = memcpy(xmalloc(pvlen), uh, pvlen);
01336 /*@=boundsread@*/
01337         if ((h = headerLoad(nuh)) != NULL)
01338             h->flags |= HEADERFLAG_ALLOCATED;
01339     }
01340     /*@=branchstate@*/
01341     /*@-branchstate@*/
01342     if (h == NULL)
01343         nuh = _free(nuh);
01344     /*@=branchstate@*/
01345     return h;
01346 }
01347 
01354 static /*@null@*/
01355 Header headerRead(FD_t fd, enum hMagic magicp)
01356         /*@modifies fd @*/
01357 {
01358     int_32 block[4];
01359     int_32 reserved;
01360     int_32 * ei = NULL;
01361     int_32 il;
01362     int_32 dl;
01363     int_32 magic;
01364     Header h = NULL;
01365     size_t len;
01366     int i;
01367 
01368     memset(block, 0, sizeof(block));
01369     i = 2;
01370     if (magicp == HEADER_MAGIC_YES)
01371         i += 2;
01372 
01373     /*@-type@*/ /* FIX: cast? */
01374     if (timedRead(fd, (char *)block, i*sizeof(*block)) != (i * sizeof(*block)))
01375         goto exit;
01376     /*@=type@*/
01377 
01378     i = 0;
01379 
01380 /*@-boundsread@*/
01381     if (magicp == HEADER_MAGIC_YES) {
01382         magic = block[i++];
01383         if (memcmp(&magic, header_magic, sizeof(magic)))
01384             goto exit;
01385         reserved = block[i++];
01386     }
01387     
01388     il = ntohl(block[i]);       i++;
01389     dl = ntohl(block[i]);       i++;
01390 /*@=boundsread@*/
01391 
01392     /*@-sizeoftype@*/
01393     len = sizeof(il) + sizeof(dl) + (il * sizeof(struct entryInfo_s)) + dl;
01394     /*@=sizeoftype@*/
01395 
01396     /* Sanity checks on header intro. */
01397     if (hdrchkTags(il) || hdrchkData(dl) || len > headerMaxbytes)
01398         goto exit;
01399 
01400 /*@-boundswrite@*/
01401     ei = xmalloc(len);
01402     ei[0] = htonl(il);
01403     ei[1] = htonl(dl);
01404     len -= sizeof(il) + sizeof(dl);
01405 /*@=boundswrite@*/
01406 
01407 /*@-boundsread@*/
01408     /*@-type@*/ /* FIX: cast? */
01409     if (timedRead(fd, (char *)&ei[2], len) != len)
01410         goto exit;
01411     /*@=type@*/
01412 /*@=boundsread@*/
01413     
01414     h = headerLoad(ei);
01415 
01416     {   const char * origin = fdGetOPath(fd);
01417         if (origin != NULL)
01418             (void) headerSetOrigin(h, origin);
01419     }
01420 
01421 exit:
01422     if (h) {
01423         if (h->flags & HEADERFLAG_ALLOCATED)
01424             ei = _free(ei);
01425         h->flags |= HEADERFLAG_ALLOCATED;
01426     } else if (ei)
01427         ei = _free(ei);
01428     /*@-mustmod@*/      /* FIX: timedRead macro obscures annotation */
01429     return h;
01430     /*@-mustmod@*/
01431 }
01432 
01440 static
01441 int headerWrite(FD_t fd, /*@null@*/ Header h, enum hMagic magicp)
01442         /*@globals fileSystem @*/
01443         /*@modifies fd, h, fileSystem @*/
01444 {
01445     ssize_t nb;
01446     int length;
01447     const void * uh;
01448 
01449     if (h == NULL)
01450         return 1;
01451 /*@-boundswrite@*/
01452     uh = doHeaderUnload(h, &length);
01453 /*@=boundswrite@*/
01454     if (uh == NULL)
01455         return 1;
01456     switch (magicp) {
01457     case HEADER_MAGIC_YES:
01458 /*@-boundsread@*/
01459         /*@-sizeoftype@*/
01460         nb = Fwrite(header_magic, sizeof(char), sizeof(header_magic), fd);
01461         /*@=sizeoftype@*/
01462 /*@=boundsread@*/
01463         if (nb != sizeof(header_magic))
01464             goto exit;
01465         break;
01466     case HEADER_MAGIC_NO:
01467         break;
01468     }
01469 
01470     /*@-sizeoftype@*/
01471     nb = Fwrite(uh, sizeof(char), length, fd);
01472     /*@=sizeoftype@*/
01473 
01474 exit:
01475     uh = _free(uh);
01476     return (nb == length ? 0 : 1);
01477 }
01478 
01485 static
01486 int headerIsEntry(/*@null@*/Header h, int_32 tag)
01487         /*@*/
01488 {
01489     /*@-mods@*/         /*@ FIX: h modified by sort. */
01490     return (findEntry(h, tag, RPM_NULL_TYPE) ? 1 : 0);
01491     /*@=mods@*/ 
01492 }
01493 
01504 static int copyEntry(const indexEntry entry,
01505                 /*@null@*/ /*@out@*/ hTYP_t type,
01506                 /*@null@*/ /*@out@*/ hPTR_t * p,
01507                 /*@null@*/ /*@out@*/ hCNT_t c,
01508                 int minMem)
01509         /*@modifies *type, *p, *c @*/
01510         /*@requires maxSet(type) >= 0 /\ maxSet(p) >= 0 /\ maxSet(c) >= 0 @*/
01511 {
01512     int_32 count = entry->info.count;
01513     int rc = 1;         /* XXX 1 on success. */
01514 
01515     if (p)
01516     switch (entry->info.type) {
01517     case RPM_BIN_TYPE:
01518         /*
01519          * XXX This only works for
01520          * XXX  "sealed" HEADER_IMMUTABLE/HEADER_SIGNATURES/HEADER_IMAGE.
01521          * XXX This will *not* work for unsealed legacy HEADER_IMAGE (i.e.
01522          * XXX a legacy header freshly read, but not yet unloaded to the rpmdb).
01523          */
01524         if (ENTRY_IS_REGION(entry)) {
01525             int_32 * ei = ((int_32 *)entry->data) - 2;
01526             /*@-castexpose@*/
01527             entryInfo pe = (entryInfo) (ei + 2);
01528             /*@=castexpose@*/
01529 /*@-boundsread@*/
01530             char * dataStart = (char *) (pe + ntohl(ei[0]));
01531 /*@=boundsread@*/
01532             int_32 rdl = -entry->info.offset;   /* negative offset */
01533             int_32 ril = rdl/sizeof(*pe);
01534 
01535             /*@-sizeoftype@*/
01536             rdl = entry->rdlen;
01537             count = 2 * sizeof(*ei) + (ril * sizeof(*pe)) + rdl;
01538             if (entry->info.tag == HEADER_IMAGE) {
01539                 ril -= 1;
01540                 pe += 1;
01541             } else {
01542                 count += REGION_TAG_COUNT;
01543                 rdl += REGION_TAG_COUNT;
01544             }
01545 
01546 /*@-bounds@*/
01547             *p = xmalloc(count);
01548             ei = (int_32 *) *p;
01549             ei[0] = htonl(ril);
01550             ei[1] = htonl(rdl);
01551 
01552             /*@-castexpose@*/
01553             pe = (entryInfo) memcpy(ei + 2, pe, (ril * sizeof(*pe)));
01554             /*@=castexpose@*/
01555 
01556             dataStart = (char *) memcpy(pe + ril, dataStart, rdl);
01557             /*@=sizeoftype@*/
01558 /*@=bounds@*/
01559 
01560             rc = regionSwab(NULL, ril, 0, pe, dataStart, NULL, 0);
01561             /* XXX 1 on success. */
01562             rc = (rc < 0) ? 0 : 1;
01563         } else {
01564             count = entry->length;
01565             *p = (!minMem
01566                 ? memcpy(xmalloc(count), entry->data, count)
01567                 : entry->data);
01568         }
01569         break;
01570     case RPM_STRING_TYPE:
01571         if (count == 1) {
01572             *p = entry->data;
01573             break;
01574         }
01575         /*@fallthrough@*/
01576     case RPM_STRING_ARRAY_TYPE:
01577     case RPM_I18NSTRING_TYPE:
01578     {   const char ** ptrEntry;
01579         /*@-sizeoftype@*/
01580         int tableSize = count * sizeof(char *);
01581         /*@=sizeoftype@*/
01582         char * t;
01583         int i;
01584 
01585 /*@-bounds@*/
01586         /*@-mods@*/
01587         if (minMem) {
01588             *p = xmalloc(tableSize);
01589             ptrEntry = (const char **) *p;
01590             t = entry->data;
01591         } else {
01592             t = xmalloc(tableSize + entry->length);
01593             *p = (void *)t;
01594             ptrEntry = (const char **) *p;
01595             t += tableSize;
01596             memcpy(t, entry->data, entry->length);
01597         }
01598         /*@=mods@*/
01599 /*@=bounds@*/
01600         for (i = 0; i < count; i++) {
01601 /*@-boundswrite@*/
01602             *ptrEntry++ = t;
01603 /*@=boundswrite@*/
01604             t = strchr(t, 0);
01605             t++;
01606         }
01607     }   break;
01608 
01609     case RPM_OPENPGP_TYPE:      /* XXX W2DO? */
01610     case RPM_ASN1_TYPE:         /* XXX W2DO? */
01611     default:
01612         *p = entry->data;
01613         break;
01614     }
01615     if (type) *type = entry->info.type;
01616     if (c) *c = count;
01617     return rc;
01618 }
01619 
01638 static int headerMatchLocale(const char *td, const char *l, const char *le)
01639         /*@*/
01640 {
01641     const char *fe;
01642 
01643 
01644 #if 0
01645   { const char *s, *ll, *CC, *EE, *dd;
01646     char *lbuf, *t.
01647 
01648     /* Copy the buffer and parse out components on the fly. */
01649     lbuf = alloca(le - l + 1);
01650     for (s = l, ll = t = lbuf; *s; s++, t++) {
01651         switch (*s) {
01652         case '_':
01653             *t = '\0';
01654             CC = t + 1;
01655             break;
01656         case '.':
01657             *t = '\0';
01658             EE = t + 1;
01659             break;
01660         case '@':
01661             *t = '\0';
01662             dd = t + 1;
01663             break;
01664         default:
01665             *t = *s;
01666             break;
01667         }
01668     }
01669 
01670     if (ll)     /* ISO language should be lower case */
01671         for (t = ll; *t; t++)   *t = tolower(*t);
01672     if (CC)     /* ISO country code should be upper case */
01673         for (t = CC; *t; t++)   *t = toupper(*t);
01674 
01675     /* There are a total of 16 cases to attempt to match. */
01676   }
01677 #endif
01678 
01679     /* First try a complete match. */
01680     if (strlen(td) == (le-l) && !strncmp(td, l, (le - l)))
01681         return 1;
01682 
01683     /* Next, try stripping optional dialect and matching.  */
01684     for (fe = l; fe < le && *fe != '@'; fe++)
01685         {};
01686     if (fe < le && !strncmp(td, l, (fe - l)))
01687         return 1;
01688 
01689     /* Next, try stripping optional codeset and matching.  */
01690     for (fe = l; fe < le && *fe != '.'; fe++)
01691         {};
01692     if (fe < le && !strncmp(td, l, (fe - l)))
01693         return 1;
01694 
01695     /* Finally, try stripping optional country code and matching. */
01696     for (fe = l; fe < le && *fe != '_'; fe++)
01697         {};
01698     if (fe < le && !strncmp(td, l, (fe - l)))
01699         return 1;
01700 
01701     return 0;
01702 }
01703 
01710 /*@dependent@*/ /*@exposed@*/ static char *
01711 headerFindI18NString(Header h, indexEntry entry)
01712         /*@*/
01713 {
01714     const char *lang, *l, *le;
01715     indexEntry table;
01716 
01717     /* XXX Drepper sez' this is the order. */
01718     if ((lang = getenv("LANGUAGE")) == NULL &&
01719         (lang = getenv("LC_ALL")) == NULL &&
01720         (lang = getenv("LC_MESSAGES")) == NULL &&
01721         (lang = getenv("LANG")) == NULL)
01722             return entry->data;
01723     
01724     /*@-mods@*/
01725     if ((table = findEntry(h, HEADER_I18NTABLE, RPM_STRING_ARRAY_TYPE)) == NULL)
01726         return entry->data;
01727     /*@=mods@*/
01728 
01729 /*@-boundsread@*/
01730     for (l = lang; *l != '\0'; l = le) {
01731         const char *td;
01732         char *ed;
01733         int langNum;
01734 
01735         while (*l && *l == ':')                 /* skip leading colons */
01736             l++;
01737         if (*l == '\0')
01738             break;
01739         for (le = l; *le && *le != ':'; le++)   /* find end of this locale */
01740             {};
01741 
01742         /* For each entry in the header ... */
01743         for (langNum = 0, td = table->data, ed = entry->data;
01744              langNum < entry->info.count;
01745              langNum++, td += strlen(td) + 1, ed += strlen(ed) + 1) {
01746 
01747                 if (headerMatchLocale(td, l, le))
01748                     return ed;
01749 
01750         }
01751     }
01752 /*@=boundsread@*/
01753 
01754 /* when everything fail, try gettext */
01755     return ((entry->data != NULL) && *(char*)(entry->data)) ? _(entry->data) : entry->data;
01756 }
01757 
01768 static int intGetEntry(Header h, int_32 tag,
01769                 /*@null@*/ /*@out@*/ hTAG_t type,
01770                 /*@null@*/ /*@out@*/ hPTR_t * p,
01771                 /*@null@*/ /*@out@*/ hCNT_t c,
01772                 int minMem)
01773         /*@modifies *type, *p, *c @*/
01774         /*@requires maxSet(type) >= 0 /\ maxSet(p) >= 0 /\ maxSet(c) >= 0 @*/
01775 {
01776     indexEntry entry;
01777     int rc;
01778 
01779     /* First find the tag */
01780     /*@-mods@*/         /*@ FIX: h modified by sort. */
01781     entry = findEntry(h, tag, RPM_NULL_TYPE);
01782     /*@mods@*/
01783     if (entry == NULL) {
01784         if (type) type = 0;
01785         if (p) *p = NULL;
01786         if (c) *c = 0;
01787         return 0;
01788     }
01789 
01790     switch (entry->info.type) {
01791     case RPM_I18NSTRING_TYPE:
01792         rc = 1;
01793         if (type) *type = RPM_STRING_TYPE;
01794         if (c) *c = 1;
01795         /*@-dependenttrans@*/
01796         if (p) *p = headerFindI18NString(h, entry);
01797         /*@=dependenttrans@*/
01798         break;
01799     default:
01800         rc = copyEntry(entry, type, p, c, minMem);
01801         break;
01802     }
01803 
01804     /* XXX 1 on success */
01805     return ((rc == 1) ? 1 : 0);
01806 }
01807 
01815 static /*@null@*/ void * headerFreeTag(/*@unused@*/ Header h,
01816                 /*@only@*/ /*@null@*/ const void * data, rpmTagType type)
01817         /*@modifies data @*/
01818 {
01819     if (data) {
01820         /*@-branchstate@*/
01821         if (type == -1 ||
01822             type == RPM_STRING_ARRAY_TYPE ||
01823             type == RPM_I18NSTRING_TYPE ||
01824             type == RPM_BIN_TYPE ||
01825             type == RPM_ASN1_TYPE ||
01826             type == RPM_OPENPGP_TYPE)
01827                 data = _free(data);
01828         /*@=branchstate@*/
01829     }
01830     return NULL;
01831 }
01832 
01846 static
01847 int headerGetEntry(Header h, int_32 tag,
01848                         /*@null@*/ /*@out@*/ hTYP_t type,
01849                         /*@null@*/ /*@out@*/ void ** p,
01850                         /*@null@*/ /*@out@*/ hCNT_t c)
01851         /*@modifies *type, *p, *c @*/
01852         /*@requires maxSet(type) >= 0 /\ maxSet(p) >= 0 /\ maxSet(c) >= 0 @*/
01853 {
01854     return intGetEntry(h, tag, type, (hPTR_t *)p, c, 0);
01855 }
01856 
01869 static
01870 int headerGetEntryMinMemory(Header h, int_32 tag,
01871                         /*@null@*/ /*@out@*/ hTYP_t type,
01872                         /*@null@*/ /*@out@*/ hPTR_t * p,
01873                         /*@null@*/ /*@out@*/ hCNT_t c)
01874         /*@modifies *type, *p, *c @*/
01875         /*@requires maxSet(type) >= 0 /\ maxSet(p) >= 0 /\ maxSet(c) >= 0 @*/
01876 {
01877     return intGetEntry(h, tag, type, p, c, 1);
01878 }
01879 
01880 int headerGetRawEntry(Header h, int_32 tag, int_32 * type, hPTR_t * p,
01881                 int_32 * c)
01882 {
01883     indexEntry entry;
01884     int rc;
01885 
01886     if (p == NULL) return headerIsEntry(h, tag);
01887 
01888     /* First find the tag */
01889     /*@-mods@*/         /*@ FIX: h modified by sort. */
01890     entry = findEntry(h, tag, RPM_NULL_TYPE);
01891     /*@=mods@*/
01892     if (!entry) {
01893         if (p) *p = NULL;
01894         if (c) *c = 0;
01895         return 0;
01896     }
01897 
01898     rc = copyEntry(entry, type, p, c, 0);
01899 
01900     /* XXX 1 on success */
01901     return ((rc == 1) ? 1 : 0);
01902 }
01903 
01906 static void copyData(int_32 type, /*@out@*/ void * dstPtr, const void * srcPtr,
01907                 int_32 cnt, int dataLength)
01908         /*@modifies *dstPtr @*/
01909 {
01910     switch (type) {
01911     case RPM_STRING_ARRAY_TYPE:
01912     case RPM_I18NSTRING_TYPE:
01913     {   const char ** av = (const char **) srcPtr;
01914         char * t = dstPtr;
01915 
01916 /*@-bounds@*/
01917         while (cnt-- > 0 && dataLength > 0) {
01918             const char * s;
01919             if ((s = *av++) == NULL)
01920                 continue;
01921             do {
01922                 *t++ = *s++;
01923             } while (s[-1] && --dataLength > 0);
01924         }
01925 /*@=bounds@*/
01926     }   break;
01927 
01928     default:
01929 /*@-boundswrite@*/
01930         memmove(dstPtr, srcPtr, dataLength);
01931 /*@=boundswrite@*/
01932         break;
01933     }
01934 }
01935 
01944 /*@null@*/
01945 static void *
01946 grabData(int_32 type, hPTR_t p, int_32 c, /*@out@*/ int * lengthPtr)
01947         /*@modifies *lengthPtr @*/
01948         /*@requires maxSet(lengthPtr) >= 0 @*/
01949 {
01950     void * data = NULL;
01951     int length;
01952 
01953     length = dataLength(type, p, c, 0, NULL);
01954 /*@-branchstate@*/
01955     if (length > 0) {
01956         data = xmalloc(length);
01957         copyData(type, data, p, c, length);
01958     }
01959 /*@=branchstate@*/
01960 
01961     if (lengthPtr)
01962         *lengthPtr = length;
01963     return data;
01964 }
01965 
01980 static
01981 int headerAddEntry(Header h, int_32 tag, int_32 type, const void * p, int_32 c)
01982         /*@modifies h @*/
01983 {
01984     indexEntry entry;
01985     void * data;
01986     int length;
01987 
01988     /* Count must always be >= 1 for headerAddEntry. */
01989     if (c <= 0)
01990         return 0;
01991 
01992     if (hdrchkType(type))
01993         return 0;
01994     if (hdrchkData(c))
01995         return 0;
01996 
01997     length = 0;
01998 /*@-boundswrite@*/
01999     data = grabData(type, p, c, &length);
02000 /*@=boundswrite@*/
02001     if (data == NULL || length <= 0)
02002         return 0;
02003 
02004     /* Allocate more index space if necessary */
02005     if (h->indexUsed == h->indexAlloced) {
02006         h->indexAlloced += INDEX_MALLOC_SIZE;
02007         h->index = xrealloc(h->index, h->indexAlloced * sizeof(*h->index));
02008     }
02009 
02010     /* Fill in the index */
02011     entry = h->index + h->indexUsed;
02012     entry->info.tag = tag;
02013     entry->info.type = type;
02014     entry->info.count = c;
02015     entry->info.offset = 0;
02016     entry->data = data;
02017     entry->length = length;
02018 
02019 /*@-boundsread@*/
02020     if (h->indexUsed > 0 && tag < h->index[h->indexUsed-1].info.tag)
02021         h->flags &= ~HEADERFLAG_SORTED;
02022 /*@=boundsread@*/
02023     h->indexUsed++;
02024 
02025     return 1;
02026 }
02027 
02042 static
02043 int headerAppendEntry(Header h, int_32 tag, int_32 type,
02044                 const void * p, int_32 c)
02045         /*@modifies h @*/
02046 {
02047     indexEntry entry;
02048     int length;
02049 
02050     if (type == RPM_STRING_TYPE || type == RPM_I18NSTRING_TYPE) {
02051         /* we can't do this */
02052         return 0;
02053     }
02054 
02055     /* Find the tag entry in the header. */
02056     entry = findEntry(h, tag, type);
02057     if (!entry)
02058         return 0;
02059 
02060     length = dataLength(type, p, c, 0, NULL);
02061     if (length < 0)
02062         return 0;
02063 
02064     if (ENTRY_IN_REGION(entry)) {
02065         char * t = xmalloc(entry->length + length);
02066 /*@-bounds@*/
02067         memcpy(t, entry->data, entry->length);
02068 /*@=bounds@*/
02069         entry->data = t;
02070         entry->info.offset = 0;
02071     } else
02072         entry->data = xrealloc(entry->data, entry->length + length);
02073 
02074     copyData(type, ((char *) entry->data) + entry->length, p, c, length);
02075 
02076     entry->length += length;
02077 
02078     entry->info.count += c;
02079 
02080     return 1;
02081 }
02082 
02092 static
02093 int headerAddOrAppendEntry(Header h, int_32 tag, int_32 type,
02094                 const void * p, int_32 c)
02095         /*@modifies h @*/
02096 {
02097     return (findEntry(h, tag, type)
02098         ? headerAppendEntry(h, tag, type, p, c)
02099         : headerAddEntry(h, tag, type, p, c));
02100 }
02101 
02122 static
02123 int headerAddI18NString(Header h, int_32 tag, const char * string,
02124                 const char * lang)
02125         /*@modifies h @*/
02126 {
02127     indexEntry table, entry;
02128     const char ** strArray;
02129     int length;
02130     int ghosts;
02131     int i, langNum;
02132     char * buf;
02133 
02134     table = findEntry(h, HEADER_I18NTABLE, RPM_STRING_ARRAY_TYPE);
02135     entry = findEntry(h, tag, RPM_I18NSTRING_TYPE);
02136 
02137     if (!table && entry)
02138         return 0;               /* this shouldn't ever happen!! */
02139 
02140     if (!table && !entry) {
02141         const char * charArray[2];
02142         int count = 0;
02143         if (!lang || (lang[0] == 'C' && lang[1] == '\0')) {
02144             /*@-observertrans -readonlytrans@*/
02145             charArray[count++] = "C";
02146             /*@=observertrans =readonlytrans@*/
02147         } else {
02148             /*@-observertrans -readonlytrans@*/
02149             charArray[count++] = "C";
02150             /*@=observertrans =readonlytrans@*/
02151             charArray[count++] = lang;
02152         }
02153         if (!headerAddEntry(h, HEADER_I18NTABLE, RPM_STRING_ARRAY_TYPE, 
02154                         &charArray, count))
02155             return 0;
02156         table = findEntry(h, HEADER_I18NTABLE, RPM_STRING_ARRAY_TYPE);
02157     }
02158 
02159     if (!table)
02160         return 0;
02161     /*@-branchstate@*/
02162     if (!lang) lang = "C";
02163     /*@=branchstate@*/
02164 
02165     {   const char * l = table->data;
02166         for (langNum = 0; langNum < table->info.count; langNum++) {
02167             if (!strcmp(l, lang)) break;
02168             l += strlen(l) + 1;
02169         }
02170     }
02171 
02172     if (langNum >= table->info.count) {
02173         length = strlen(lang) + 1;
02174         if (ENTRY_IN_REGION(table)) {
02175             char * t = xmalloc(table->length + length);
02176             memcpy(t, table->data, table->length);
02177             table->data = t;
02178             table->info.offset = 0;
02179         } else
02180             table->data = xrealloc(table->data, table->length + length);
02181         memmove(((char *)table->data) + table->length, lang, length);
02182         table->length += length;
02183         table->info.count++;
02184     }
02185 
02186     if (!entry) {
02187         strArray = alloca(sizeof(*strArray) * (langNum + 1));
02188         for (i = 0; i < langNum; i++)
02189             strArray[i] = "";
02190         strArray[langNum] = string;
02191         return headerAddEntry(h, tag, RPM_I18NSTRING_TYPE, strArray, 
02192                                 langNum + 1);
02193     } else if (langNum >= entry->info.count) {
02194         ghosts = langNum - entry->info.count;
02195         
02196         length = strlen(string) + 1 + ghosts;
02197         if (ENTRY_IN_REGION(entry)) {
02198             char * t = xmalloc(entry->length + length);
02199             memcpy(t, entry->data, entry->length);
02200             entry->data = t;
02201             entry->info.offset = 0;
02202         } else
02203             entry->data = xrealloc(entry->data, entry->length + length);
02204 
02205         memset(((char *)entry->data) + entry->length, '\0', ghosts);
02206         memmove(((char *)entry->data) + entry->length + ghosts, string, strlen(string)+1);
02207 
02208         entry->length += length;
02209         entry->info.count = langNum + 1;
02210     } else {
02211         char *b, *be, *e, *ee, *t;
02212         size_t bn, sn, en;
02213 
02214         /* Set beginning/end pointers to previous data */
02215         b = be = e = ee = entry->data;
02216         for (i = 0; i < table->info.count; i++) {
02217             if (i == langNum)
02218                 be = ee;
02219             ee += strlen(ee) + 1;
02220             if (i == langNum)
02221                 e  = ee;
02222         }
02223 
02224         /* Get storage for new buffer */
02225         bn = (be-b);
02226         sn = strlen(string) + 1;
02227         en = (ee-e);
02228         length = bn + sn + en;
02229         t = buf = xmalloc(length);
02230 
02231         /* Copy values into new storage */
02232         memcpy(t, b, bn);
02233         t += bn;
02234 /*@-mayaliasunique@*/
02235         memcpy(t, string, sn);
02236         t += sn;
02237         memcpy(t, e, en);
02238         t += en;
02239 /*@=mayaliasunique@*/
02240 
02241         /* Replace i18N string array */
02242         entry->length -= strlen(be) + 1;
02243         entry->length += sn;
02244         
02245         if (ENTRY_IN_REGION(entry)) {
02246             entry->info.offset = 0;
02247         } else
02248             entry->data = _free(entry->data);
02249         /*@-dependenttrans@*/
02250         entry->data = buf;
02251         /*@=dependenttrans@*/
02252     }
02253 
02254     return 0;
02255 }
02256 
02267 static
02268 int headerModifyEntry(Header h, int_32 tag, int_32 type,
02269                         const void * p, int_32 c)
02270         /*@modifies h @*/
02271 {
02272     indexEntry entry;
02273     void * oldData;
02274     void * data;
02275     int length;
02276 
02277     /* First find the tag */
02278     entry = findEntry(h, tag, type);
02279     if (!entry)
02280         return 0;
02281 
02282     length = 0;
02283     data = grabData(type, p, c, &length);
02284     if (data == NULL || length <= 0)
02285         return 0;
02286 
02287     /* make sure entry points to the first occurence of this tag */
02288     while (entry > h->index && (entry - 1)->info.tag == tag)  
02289         entry--;
02290 
02291     /* free after we've grabbed the new data in case the two are intertwined;
02292        that's a bad idea but at least we won't break */
02293     oldData = entry->data;
02294 
02295     entry->info.count = c;
02296     entry->info.type = type;
02297     entry->data = data;
02298     entry->length = length;
02299 
02300     /*@-branchstate@*/
02301     if (ENTRY_IN_REGION(entry)) {
02302         entry->info.offset = 0;
02303     } else
02304         oldData = _free(oldData);
02305     /*@=branchstate@*/
02306 
02307     return 1;
02308 }
02309 
02312 static char escapedChar(const char ch)  /*@*/
02313 {
02314     switch (ch) {
02315     case 'a':   return '\a';
02316     case 'b':   return '\b';
02317     case 'f':   return '\f';
02318     case 'n':   return '\n';
02319     case 'r':   return '\r';
02320     case 't':   return '\t';
02321     case 'v':   return '\v';
02322     default:    return ch;
02323     }
02324 }
02325 
02332 static /*@null@*/ sprintfToken
02333 freeFormat( /*@only@*/ /*@null@*/ sprintfToken format, int num)
02334         /*@modifies *format @*/
02335 {
02336     int i;
02337 
02338     if (format == NULL) return NULL;
02339 
02340     for (i = 0; i < num; i++) {
02341         switch (format[i].type) {
02342         case PTOK_ARRAY:
02343 /*@-boundswrite@*/
02344             format[i].u.array.format =
02345                 freeFormat(format[i].u.array.format,
02346                         format[i].u.array.numTokens);
02347 /*@=boundswrite@*/
02348             /*@switchbreak@*/ break;
02349         case PTOK_COND:
02350 /*@-boundswrite@*/
02351             format[i].u.cond.ifFormat =
02352                 freeFormat(format[i].u.cond.ifFormat, 
02353                         format[i].u.cond.numIfTokens);
02354             format[i].u.cond.elseFormat =
02355                 freeFormat(format[i].u.cond.elseFormat, 
02356                         format[i].u.cond.numElseTokens);
02357 /*@=boundswrite@*/
02358             /*@switchbreak@*/ break;
02359         case PTOK_NONE:
02360         case PTOK_TAG:
02361         case PTOK_STRING:
02362         default:
02363             /*@switchbreak@*/ break;
02364         }
02365     }
02366     format = _free(format);
02367     return NULL;
02368 }
02369 
02373 struct headerIterator_s {
02374 /*@unused@*/
02375     Header h;           
02376 /*@unused@*/
02377     int next_index;     
02378 };
02379 
02385 static /*@null@*/
02386 HeaderIterator headerFreeIterator(/*@only@*/ HeaderIterator hi)
02387         /*@modifies hi @*/
02388 {
02389     if (hi != NULL) {
02390         hi->h = headerFree(hi->h);
02391         hi = _free(hi);
02392     }
02393     return hi;
02394 }
02395 
02401 static
02402 HeaderIterator headerInitIterator(Header h)
02403         /*@modifies h */
02404 {
02405     HeaderIterator hi = xmalloc(sizeof(*hi));
02406 
02407     headerSort(h);
02408 
02409     hi->h = headerLink(h);
02410     hi->next_index = 0;
02411     return hi;
02412 }
02413 
02423 static
02424 int headerNextIterator(HeaderIterator hi,
02425                 /*@null@*/ /*@out@*/ hTAG_t tag,
02426                 /*@null@*/ /*@out@*/ hTYP_t type,
02427                 /*@null@*/ /*@out@*/ hPTR_t * p,
02428                 /*@null@*/ /*@out@*/ hCNT_t c)
02429         /*@modifies hi, *tag, *type, *p, *c @*/
02430         /*@requires maxSet(tag) >= 0 /\ maxSet(type) >= 0
02431                 /\ maxSet(p) >= 0 /\ maxSet(c) >= 0 @*/
02432 {
02433     Header h = hi->h;
02434     int slot = hi->next_index;
02435     indexEntry entry = NULL;
02436     int rc;
02437 
02438     for (slot = hi->next_index; slot < h->indexUsed; slot++) {
02439         entry = h->index + slot;
02440         if (!ENTRY_IS_REGION(entry))
02441             break;
02442     }
02443     hi->next_index = slot;
02444     if (entry == NULL || slot >= h->indexUsed)
02445         return 0;
02446 
02447     /*@-noeffect@*/     /* LCL: no clue */
02448     hi->next_index++;
02449     /*@=noeffect@*/
02450 
02451     if (tag)
02452         *tag = entry->info.tag;
02453 
02454     rc = copyEntry(entry, type, p, c, 0);
02455 
02456     /* XXX 1 on success */
02457     return ((rc == 1) ? 1 : 0);
02458 }
02459 
02465 static /*@null@*/
02466 Header headerCopy(Header h)
02467         /*@modifies h @*/
02468 {
02469     Header nh = headerNew();
02470     HeaderIterator hi;
02471     int_32 tag, type, count;
02472     hPTR_t ptr;
02473    
02474     /*@-branchstate@*/
02475     for (hi = headerInitIterator(h);
02476         headerNextIterator(hi, &tag, &type, &ptr, &count);
02477         ptr = headerFreeData((void *)ptr, type))
02478     {
02479         if (ptr) (void) headerAddEntry(nh, tag, type, ptr, count);
02480     }
02481     hi = headerFreeIterator(hi);
02482     /*@=branchstate@*/
02483 
02484     return headerReload(nh, HEADER_IMAGE);
02485 }
02486 
02489 typedef struct headerSprintfArgs_s {
02490     Header h;
02491     char * fmt;
02492 /*@temp@*/
02493     headerTagTableEntry tags;
02494 /*@temp@*/
02495     headerSprintfExtension exts;
02496 /*@observer@*/ /*@null@*/
02497     const char * errmsg;
02498     rpmec ec;
02499     sprintfToken format;
02500 /*@relnull@*/
02501     HeaderIterator hi;
02502 /*@owned@*/
02503     char * val;
02504     size_t vallen;
02505     size_t alloced;
02506     int numTokens;
02507     int i;
02508 } * headerSprintfArgs;
02509 
02515 static headerSprintfArgs hsaInit(/*@returned@*/ headerSprintfArgs hsa)
02516         /*@modifies hsa */
02517 {
02518     sprintfTag tag =
02519         (hsa->format->type == PTOK_TAG
02520             ? &hsa->format->u.tag :
02521         (hsa->format->type == PTOK_ARRAY
02522             ? &hsa->format->u.array.format->u.tag :
02523         NULL));
02524 
02525     if (hsa != NULL) {
02526         hsa->i = 0;
02527         if (tag != NULL && tag->tag == -2)
02528             hsa->hi = headerInitIterator(hsa->h);
02529     }
02530 /*@-nullret@*/
02531     return hsa;
02532 /*@=nullret@*/
02533 }
02534 
02540 /*@null@*/
02541 static sprintfToken hsaNext(/*@returned@*/ headerSprintfArgs hsa)
02542         /*@modifies hsa */
02543 {
02544     sprintfToken fmt = NULL;
02545     sprintfTag tag =
02546         (hsa->format->type == PTOK_TAG
02547             ? &hsa->format->u.tag :
02548         (hsa->format->type == PTOK_ARRAY
02549             ? &hsa->format->u.array.format->u.tag :
02550         NULL));
02551 
02552     if (hsa != NULL && hsa->i >= 0 && hsa->i < hsa->numTokens) {
02553         fmt = hsa->format + hsa->i;
02554         if (hsa->hi == NULL) {
02555             hsa->i++;
02556         } else {
02557             int_32 tagno;
02558             int_32 type;
02559             int_32 count;
02560 
02561 /*@-boundswrite@*/
02562             if (!headerNextIterator(hsa->hi, &tagno, &type, NULL, &count))
02563                 fmt = NULL;
02564             tag->tag = tagno;
02565 /*@=boundswrite@*/
02566         }
02567     }
02568 
02569 /*@-dependenttrans -onlytrans@*/
02570     return fmt;
02571 /*@=dependenttrans =onlytrans@*/
02572 }
02573 
02579 static headerSprintfArgs hsaFini(/*@returned@*/ headerSprintfArgs hsa)
02580         /*@modifies hsa */
02581 {
02582     if (hsa != NULL) {
02583         hsa->hi = headerFreeIterator(hsa->hi);
02584         hsa->i = 0;
02585     }
02586 /*@-nullret@*/
02587     return hsa;
02588 /*@=nullret@*/
02589 }
02590 
02597 /*@dependent@*/ /*@exposed@*/
02598 static char * hsaReserve(headerSprintfArgs hsa, size_t need)
02599         /*@modifies hsa */
02600 {
02601     if ((hsa->vallen + need) >= hsa->alloced) {
02602         if (hsa->alloced <= need)
02603             hsa->alloced += need;
02604         hsa->alloced <<= 1;
02605         hsa->val = xrealloc(hsa->val, hsa->alloced+1);  
02606     }
02607     return hsa->val + hsa->vallen;
02608 }
02609 
02618 /*@observer@*/ /*@null@*/
02619 static const char * myTagName(headerTagTableEntry tbl, int val,
02620                 /*@null@*/ int *typep)
02621         /*@*/
02622 {
02623     static char name[128];
02624     const char * s;
02625     char *t;
02626 
02627     for (; tbl->name != NULL; tbl++) {
02628         if (tbl->val == val)
02629             break;
02630     }
02631     if ((s = tbl->name) == NULL)
02632         return NULL;
02633     s += sizeof("RPMTAG_") - 1;
02634     t = name;
02635     *t++ = *s++;
02636     while (*s != '\0')
02637         *t++ = xtolower(*s++);
02638     *t = '\0';
02639     if (typep)
02640         *typep = tbl->type;
02641     return name;
02642 }
02643 
02651 static int myTagValue(headerTagTableEntry tbl, const char * name)
02652         /*@*/
02653 {
02654     for (; tbl->name != NULL; tbl++) {
02655         if (!xstrcasecmp(tbl->name, name))
02656             return tbl->val;
02657     }
02658     return 0;
02659 }
02660 
02668 static int findTag(headerSprintfArgs hsa, sprintfToken token, const char * name)
02669         /*@modifies token @*/
02670 {
02671     headerSprintfExtension ext;
02672     sprintfTag stag = (token->type == PTOK_COND
02673         ? &token->u.cond.tag : &token->u.tag);
02674 
02675     stag->fmt = NULL;
02676     stag->ext = NULL;
02677     stag->extNum = 0;
02678     stag->tag = -1;
02679 
02680     if (!strcmp(name, "*")) {
02681         stag->tag = -2;
02682         goto bingo;
02683     }
02684 
02685 /*@-branchstate@*/
02686     if (strncmp("RPMTAG_", name, sizeof("RPMTAG_")-1)) {
02687 /*@-boundswrite@*/
02688         char * t = alloca(strlen(name) + sizeof("RPMTAG_"));
02689         (void) stpcpy( stpcpy(t, "RPMTAG_"), name);
02690         name = t;
02691 /*@=boundswrite@*/
02692     }
02693 /*@=branchstate@*/
02694 
02695     /* Search extensions for specific tag override. */
02696     for (ext = hsa->exts; ext != NULL && ext->type != HEADER_EXT_LAST;
02697         ext = (ext->type == HEADER_EXT_MORE ? ext->u.more : ext+1))
02698     {
02699         if (ext->name == NULL || ext->type != HEADER_EXT_TAG)
02700             continue;
02701         if (!xstrcasecmp(ext->name, name)) {
02702             stag->ext = ext->u.tagFunction;
02703             stag->extNum = ext - hsa->exts;
02704             goto bingo;
02705         }
02706     }
02707 
02708     /* Search tag names. */
02709     stag->tag = myTagValue(hsa->tags, name);
02710     if (stag->tag != 0)
02711         goto bingo;
02712 
02713     return 1;
02714 
02715 bingo:
02716     /* Search extensions for specific format. */
02717     if (stag->type != NULL)
02718     for (ext = hsa->exts; ext != NULL && ext->type != HEADER_EXT_LAST;
02719             ext = (ext->type == HEADER_EXT_MORE ? ext->u.more : ext+1))
02720     {
02721         if (ext->name == NULL || ext->type != HEADER_EXT_FORMAT)
02722             continue;
02723         if (!strcmp(ext->name, stag->type)) {
02724             stag->fmt = ext->u.formatFunction;
02725             break;
02726         }
02727     }
02728     return 0;
02729 }
02730 
02731 /* forward ref */
02740 static int parseExpression(headerSprintfArgs hsa, sprintfToken token,
02741                 char * str, /*@out@*/char ** endPtr)
02742         /*@modifies hsa, str, token, *endPtr @*/
02743         /*@requires maxSet(endPtr) >= 0 @*/;
02744 
02755 static int parseFormat(headerSprintfArgs hsa, /*@null@*/ char * str,
02756                 /*@out@*/sprintfToken * formatPtr, /*@out@*/int * numTokensPtr,
02757                 /*@null@*/ /*@out@*/ char ** endPtr, int state)
02758         /*@modifies hsa, str, *formatPtr, *numTokensPtr, *endPtr @*/
02759         /*@requires maxSet(formatPtr) >= 0 /\ maxSet(numTokensPtr) >= 0
02760                 /\ maxSet(endPtr) >= 0 @*/
02761 {
02762     char * chptr, * start, * next, * dst;
02763     sprintfToken format;
02764     sprintfToken token;
02765     int numTokens;
02766     int i;
02767     int done = 0;
02768 
02769     /* upper limit on number of individual formats */
02770     numTokens = 0;
02771     if (str != NULL)
02772     for (chptr = str; *chptr != '\0'; chptr++)
02773         if (*chptr == '%') numTokens++;
02774     numTokens = numTokens * 2 + 1;
02775 
02776     format = xcalloc(numTokens, sizeof(*format));
02777     if (endPtr) *endPtr = NULL;
02778 
02779     /*@-infloops@*/ /* LCL: can't detect done termination */
02780     dst = start = str;
02781     numTokens = 0;
02782     token = NULL;
02783     if (start != NULL)
02784     while (*start != '\0') {
02785         switch (*start) {
02786         case '%':
02787             /* handle %% */
02788             if (*(start + 1) == '%') {
02789                 if (token == NULL || token->type != PTOK_STRING) {
02790                     token = format + numTokens++;
02791                     token->type = PTOK_STRING;
02792                     /*@-temptrans -assignexpose@*/
02793                     dst = token->u.string.string = start;
02794                     /*@=temptrans =assignexpose@*/
02795                 }
02796                 start++;
02797 /*@-boundswrite@*/
02798                 *dst++ = *start++;
02799 /*@=boundswrite@*/
02800                 /*@switchbreak@*/ break;
02801             } 
02802 
02803             token = format + numTokens++;
02804 /*@-boundswrite@*/
02805             *dst++ = '\0';
02806 /*@=boundswrite@*/
02807             start++;
02808 
02809             if (*start == '|') {
02810                 char * newEnd;
02811 
02812                 start++;
02813 /*@-boundswrite@*/
02814                 if (parseExpression(hsa, token, start, &newEnd))
02815                 {
02816                     format = freeFormat(format, numTokens);
02817                     return 1;
02818                 }
02819 /*@=boundswrite@*/
02820                 start = newEnd;
02821                 /*@switchbreak@*/ break;
02822             }
02823 
02824             /*@-assignexpose@*/
02825             token->u.tag.format = start;
02826             /*@=assignexpose@*/
02827             token->u.tag.pad = 0;
02828             token->u.tag.justOne = 0;
02829             token->u.tag.arrayCount = 0;
02830 
02831             chptr = start;
02832             while (*chptr && *chptr != '{' && *chptr != '%') chptr++;
02833             if (!*chptr || *chptr == '%') {
02834                 hsa->errmsg = _("missing { after %");
02835                 format = freeFormat(format, numTokens);
02836                 return 1;
02837             }
02838 
02839 /*@-boundswrite@*/
02840             *chptr++ = '\0';
02841 /*@=boundswrite@*/
02842 
02843             while (start < chptr) {
02844                 if (xisdigit(*start)) {
02845                     i = strtoul(start, &start, 10);
02846                     token->u.tag.pad += i;
02847                 } else {
02848                     start++;
02849                 }
02850             }
02851 
02852             if (*start == '=') {
02853                 token->u.tag.justOne = 1;
02854                 start++;
02855             } else if (*start == '#') {
02856                 token->u.tag.justOne = 1;
02857                 token->u.tag.arrayCount = 1;
02858                 start++;
02859             }
02860 
02861             next = start;
02862             while (*next && *next != '}') next++;
02863             if (!*next) {
02864                 hsa->errmsg = _("missing } after %{");
02865                 format = freeFormat(format, numTokens);
02866                 return 1;
02867             }
02868 /*@-boundswrite@*/
02869             *next++ = '\0';
02870 /*@=boundswrite@*/
02871 
02872             chptr = start;
02873             while (*chptr && *chptr != ':') chptr++;
02874 
02875             if (*chptr != '\0') {
02876 /*@-boundswrite@*/
02877                 *chptr++ = '\0';
02878 /*@=boundswrite@*/
02879                 if (!*chptr) {
02880                     hsa->errmsg = _("empty tag format");
02881                     format = freeFormat(format, numTokens);
02882                     return 1;
02883                 }
02884                 /*@-assignexpose@*/
02885                 token->u.tag.type = chptr;
02886                 /*@=assignexpose@*/
02887             } else {
02888                 token->u.tag.type = NULL;
02889             }
02890             
02891             if (!*start) {
02892                 hsa->errmsg = _("empty tag name");
02893                 format = freeFormat(format, numTokens);
02894                 return 1;
02895             }
02896 
02897             i = 0;
02898             token->type = PTOK_TAG;
02899 
02900             if (findTag(hsa, token, start)) {
02901                 hsa->errmsg = _("unknown tag");
02902                 format = freeFormat(format, numTokens);
02903                 return 1;
02904             }
02905 
02906             start = next;
02907             /*@switchbreak@*/ break;
02908 
02909         case '[':
02910 /*@-boundswrite@*/
02911             *dst++ = '\0';
02912             *start++ = '\0';
02913 /*@=boundswrite@*/
02914             token = format + numTokens++;
02915 
02916 /*@-boundswrite@*/
02917             if (parseFormat(hsa, start,
02918                             &token->u.array.format,
02919                             &token->u.array.numTokens,
02920                             &start, PARSER_IN_ARRAY))
02921             {
02922                 format = freeFormat(format, numTokens);
02923                 return 1;
02924             }
02925 /*@=boundswrite@*/
02926 
02927             if (!start) {
02928                 hsa->errmsg = _("] expected at end of array");
02929                 format = freeFormat(format, numTokens);
02930                 return 1;
02931             }
02932 
02933             dst = start;
02934 
02935             token->type = PTOK_ARRAY;
02936 
02937             /*@switchbreak@*/ break;
02938 
02939         case ']':
02940             if (state != PARSER_IN_ARRAY) {
02941                 hsa->errmsg = _("unexpected ]");
02942                 format = freeFormat(format, numTokens);
02943                 return 1;
02944             }
02945 /*@-boundswrite@*/
02946             *start++ = '\0';
02947 /*@=boundswrite@*/
02948             if (endPtr) *endPtr = start;
02949             done = 1;
02950             /*@switchbreak@*/ break;
02951 
02952         case '}':
02953             if (state != PARSER_IN_EXPR) {
02954                 hsa->errmsg = _("unexpected }");
02955                 format = freeFormat(format, numTokens);
02956                 return 1;
02957             }
02958 /*@-boundswrite@*/
02959             *start++ = '\0';
02960 /*@=boundswrite@*/
02961             if (endPtr) *endPtr = start;
02962             done = 1;
02963             /*@switchbreak@*/ break;
02964 
02965         default:
02966             if (token == NULL || token->type != PTOK_STRING) {
02967                 token = format + numTokens++;
02968                 token->type = PTOK_STRING;
02969                 /*@-temptrans -assignexpose@*/
02970                 dst = token->u.string.string = start;
02971                 /*@=temptrans =assignexpose@*/
02972             }
02973 
02974 /*@-boundswrite@*/
02975             if (*start == '\\') {
02976                 start++;
02977                 *dst++ = escapedChar(*start++);
02978             } else {
02979                 *dst++ = *start++;
02980             }
02981 /*@=boundswrite@*/
02982             /*@switchbreak@*/ break;
02983         }
02984         if (done)
02985             break;
02986     }
02987     /*@=infloops@*/
02988 
02989 /*@-boundswrite@*/
02990     if (dst != NULL)
02991         *dst = '\0';
02992 /*@=boundswrite@*/
02993 
02994     for (i = 0; i < numTokens; i++) {
02995         token = format + i;
02996         if (token->type == PTOK_STRING)
02997             token->u.string.len = strlen(token->u.string.string);
02998     }
02999 
03000     *numTokensPtr = numTokens;
03001     *formatPtr = format;
03002 
03003     return 0;
03004 }
03005 
03006 /*@-boundswrite@*/
03007 static int parseExpression(headerSprintfArgs hsa, sprintfToken token,
03008                 char * str, /*@out@*/ char ** endPtr)
03009 {
03010     char * chptr;
03011     char * end;
03012 
03013     hsa->errmsg = NULL;
03014     chptr = str;
03015     while (*chptr && *chptr != '?') chptr++;
03016 
03017     if (*chptr != '?') {
03018         hsa->errmsg = _("? expected in expression");
03019         return 1;
03020     }
03021 
03022     *chptr++ = '\0';;
03023 
03024     if (*chptr != '{') {
03025         hsa->errmsg = _("{ expected after ? in expression");
03026         return 1;
03027     }
03028 
03029     chptr++;
03030 
03031     if (parseFormat(hsa, chptr, &token->u.cond.ifFormat, 
03032                     &token->u.cond.numIfTokens, &end, PARSER_IN_EXPR)) 
03033         return 1;
03034 
03035     /* XXX fix segfault on "rpm -q rpm --qf='%|NAME?{%}:{NAME}|\n'"*/
03036     if (!(end && *end)) {
03037         hsa->errmsg = _("} expected in expression");
03038         token->u.cond.ifFormat =
03039                 freeFormat(token->u.cond.ifFormat, token->u.cond.numIfTokens);
03040         return 1;
03041     }
03042 
03043     chptr = end;
03044     if (*chptr != ':' && *chptr != '|') {
03045         hsa->errmsg = _(": expected following ? subexpression");
03046         token->u.cond.ifFormat =
03047                 freeFormat(token->u.cond.ifFormat, token->u.cond.numIfTokens);
03048         return 1;
03049     }
03050 
03051     if (*chptr == '|') {
03052         if (parseFormat(hsa, NULL, &token->u.cond.elseFormat, 
03053                 &token->u.cond.numElseTokens, &end, PARSER_IN_EXPR))
03054         {
03055             token->u.cond.ifFormat =
03056                 freeFormat(token->u.cond.ifFormat, token->u.cond.numIfTokens);
03057             return 1;
03058         }
03059     } else {
03060         chptr++;
03061 
03062         if (*chptr != '{') {
03063             hsa->errmsg = _("{ expected after : in expression");
03064             token->u.cond.ifFormat =
03065                 freeFormat(token->u.cond.ifFormat, token->u.cond.numIfTokens);
03066             return 1;
03067         }
03068 
03069         chptr++;
03070 
03071         if (parseFormat(hsa, chptr, &token->u.cond.elseFormat, 
03072                         &token->u.cond.numElseTokens, &end, PARSER_IN_EXPR)) 
03073             return 1;
03074 
03075         /* XXX fix segfault on "rpm -q rpm --qf='%|NAME?{a}:{%}|{NAME}\n'" */
03076         if (!(end && *end)) {
03077             hsa->errmsg = _("} expected in expression");
03078             token->u.cond.ifFormat =
03079                 freeFormat(token->u.cond.ifFormat, token->u.cond.numIfTokens);
03080             return 1;
03081         }
03082 
03083         chptr = end;
03084         if (*chptr != '|') {
03085             hsa->errmsg = _("| expected at end of expression");
03086             token->u.cond.ifFormat =
03087                 freeFormat(token->u.cond.ifFormat, token->u.cond.numIfTokens);
03088             token->u.cond.elseFormat =
03089                 freeFormat(token->u.cond.elseFormat, token->u.cond.numElseTokens);
03090             return 1;
03091         }
03092     }
03093         
03094     chptr++;
03095 
03096     *endPtr = chptr;
03097 
03098     token->type = PTOK_COND;
03099 
03100     (void) findTag(hsa, token, str);
03101 
03102     return 0;
03103 }
03104 /*@=boundswrite@*/
03105 
03116 static int getExtension(headerSprintfArgs hsa, headerTagTagFunction fn,
03117                 /*@out@*/ hTYP_t typeptr,
03118                 /*@out@*/ hPTR_t * data,
03119                 /*@out@*/ hCNT_t countptr,
03120                 rpmec ec)
03121         /*@modifies *typeptr, *data, *countptr, ec @*/
03122         /*@requires maxSet(typeptr) >= 0 /\ maxSet(data) >= 0
03123                 /\ maxSet(countptr) >= 0 @*/
03124 {
03125     if (!ec->avail) {
03126         if (fn(hsa->h, &ec->type, &ec->data, &ec->count, &ec->freeit))
03127             return 1;
03128         ec->avail = 1;
03129     }
03130 
03131     if (typeptr) *typeptr = ec->type;
03132     if (data) *data = ec->data;
03133     if (countptr) *countptr = ec->count;
03134 
03135     return 0;
03136 }
03137 
03145 /*@observer@*/ /*@null@*/
03146 static char * formatValue(headerSprintfArgs hsa, sprintfTag tag, int element)
03147         /*@modifies hsa @*/
03148 {
03149     char * val = NULL;
03150     size_t need = 0;
03151     char * t, * te;
03152     char buf[20];
03153     int_32 count, type;
03154     hPTR_t data;
03155     unsigned int intVal;
03156     uint_64 llVal;
03157     const char ** strarray;
03158     int datafree = 0;
03159     int countBuf;
03160 
03161     memset(buf, 0, sizeof(buf));
03162     if (tag->ext) {
03163 /*@-boundswrite -branchstate @*/
03164         if (getExtension(hsa, tag->ext, &type, &data, &count, hsa->ec + tag->extNum))
03165         {
03166             count = 1;
03167             type = RPM_STRING_TYPE;     
03168             data = "(none)";
03169         }
03170 /*@=boundswrite =branchstate @*/
03171     } else {
03172 /*@-boundswrite -branchstate @*/
03173         if (!headerGetEntry(hsa->h, tag->tag, &type, (void **)&data, &count)) {
03174             count = 1;
03175             type = RPM_STRING_TYPE;     
03176             data = "(none)";
03177         }
03178 /*@=boundswrite =branchstate @*/
03179 
03180         /* XXX this test is unnecessary, array sizes are checked */
03181         switch (type) {
03182         default:
03183             if (element >= count) {
03184                 /*@-modobserver -observertrans@*/
03185                 data = headerFreeData(data, type);
03186                 /*@=modobserver =observertrans@*/
03187 
03188                 hsa->errmsg = _("(index out of range)");
03189                 return NULL;
03190             }
03191             break;
03192         case RPM_OPENPGP_TYPE:
03193         case RPM_ASN1_TYPE:
03194         case RPM_BIN_TYPE:
03195         case RPM_STRING_TYPE:
03196             break;
03197         }
03198         datafree = 1;
03199     }
03200 
03201     if (tag->arrayCount) {
03202         /*@-branchstate -observertrans -modobserver@*/
03203         if (datafree)
03204             data = headerFreeData(data, type);
03205         /*@=branchstate =observertrans =modobserver@*/
03206 
03207         countBuf = count;
03208         data = &countBuf;
03209         count = 1;
03210         type = RPM_INT32_TYPE;
03211     }
03212 
03213 /*@-boundswrite@*/
03214     (void) stpcpy( stpcpy(buf, "%"), tag->format);
03215 /*@=boundswrite@*/
03216 
03217     /*@-branchstate@*/
03218     if (data)
03219     switch (type) {
03220     case RPM_STRING_ARRAY_TYPE:
03221         strarray = (const char **)data;
03222 
03223         if (tag->fmt)
03224             val = tag->fmt(RPM_STRING_TYPE, strarray[element], buf, tag->pad, (count > 1 ? element : -1));
03225 
03226         if (val) {
03227             need = strlen(val);
03228         } else {
03229             need = strlen(strarray[element]) + tag->pad + 20;
03230             val = xmalloc(need+1);
03231             strcat(buf, "s");
03232             /*@-formatconst@*/
03233             sprintf(val, buf, strarray[element]);
03234             /*@=formatconst@*/
03235         }
03236 
03237         break;
03238 
03239     case RPM_STRING_TYPE:
03240         if (tag->fmt)
03241             val = tag->fmt(RPM_STRING_TYPE, data, buf, tag->pad,  -1);
03242 
03243         if (val) {
03244             need = strlen(val);
03245         } else {
03246             need = strlen(data) + tag->pad + 20;
03247             val = xmalloc(need+1);
03248             strcat(buf, "s");
03249             /*@-formatconst@*/
03250             sprintf(val, buf, data);
03251             /*@=formatconst@*/
03252         }
03253         break;
03254 
03255     case RPM_INT64_TYPE:
03256         llVal = *(((int_64 *) data) + element);
03257         if (tag->fmt)
03258             val = tag->fmt(RPM_INT64_TYPE, &llVal, buf, tag->pad, (count > 1 ? element : -1));
03259         if (val) {
03260             need = strlen(val);
03261         } else {
03262             need = 10 + tag->pad + 40;
03263             val = xmalloc(need+1);
03264             strcat(buf, "lld");
03265             /*@-formatconst@*/
03266             sprintf(val, buf, llVal);
03267             /*@=formatconst@*/
03268         }
03269         break;
03270 
03271     case RPM_CHAR_TYPE:
03272     case RPM_INT8_TYPE:
03273     case RPM_INT16_TYPE:
03274     case RPM_INT32_TYPE:
03275         switch (type) {
03276         case RPM_CHAR_TYPE:     
03277         case RPM_INT8_TYPE:
03278             intVal = *(((int_8 *) data) + element);
03279             /*@innerbreak@*/ break;
03280         case RPM_INT16_TYPE:
03281             intVal = *(((uint_16 *) data) + element);
03282             /*@innerbreak@*/ break;
03283         default:                /* keep -Wall quiet */
03284         case RPM_INT32_TYPE:
03285             intVal = *(((int_32 *) data) + element);
03286             /*@innerbreak@*/ break;
03287         }
03288 
03289         if (tag->fmt)
03290             val = tag->fmt(RPM_INT32_TYPE, &intVal, buf, tag->pad, (count > 1 ? element : -1));
03291 
03292         if (val) {
03293             need = strlen(val);
03294         } else {
03295             need = 10 + tag->pad + 20;
03296             val = xmalloc(need+1);
03297             strcat(buf, "d");
03298             /*@-formatconst@*/
03299             sprintf(val, buf, intVal);
03300             /*@=formatconst@*/
03301         }
03302         break;
03303 
03304     case RPM_OPENPGP_TYPE:      /* XXX W2DO? */
03305     case RPM_ASN1_TYPE:         /* XXX W2DO? */
03306     case RPM_BIN_TYPE:
03307         /* XXX HACK ALERT: element field abused as no. bytes of binary data. */
03308         if (tag->fmt)
03309             val = tag->fmt(RPM_BIN_TYPE, data, buf, tag->pad, count);
03310 
03311         if (val) {
03312             need = strlen(val);
03313         } else {
03314 #ifdef  NOTYET
03315             val = memcpy(xmalloc(count), data, count);
03316 #else
03317             /* XXX format string not used */
03318             static char hex[] = "0123456789abcdef";
03319             const char * s = data;
03320 
03321 /*@-boundswrite@*/
03322             need = 2*count + tag->pad;
03323             val = t = xmalloc(need+1);
03324             while (count-- > 0) {
03325                 unsigned int i;
03326                 i = *s++;
03327                 *t++ = hex[ (i >> 4) & 0xf ];
03328                 *t++ = hex[ (i     ) & 0xf ];
03329             }
03330             *t = '\0';
03331 /*@=boundswrite@*/
03332 #endif
03333         }
03334         break;
03335 
03336     default:
03337         need = sizeof("(unknown type)") - 1;
03338         val = xstrdup("(unknown type)");
03339         break;
03340     }
03341     /*@=branchstate@*/
03342 
03343     /*@-branchstate -observertrans -modobserver@*/
03344     if (datafree)
03345         data = headerFreeData(data, type);
03346     /*@=branchstate =observertrans =modobserver@*/
03347 
03348     /*@-branchstate@*/
03349     if (val && need > 0) {
03350         t = hsaReserve(hsa, need);
03351 /*@-boundswrite@*/
03352         te = stpcpy(t, val);
03353 /*@=boundswrite@*/
03354         hsa->vallen += (te - t);
03355         val = _free(val);
03356     }
03357     /*@=branchstate@*/
03358 
03359     return (hsa->val + hsa->vallen);
03360 }
03361 
03369 /*@observer@*/
03370 static char * singleSprintf(headerSprintfArgs hsa, sprintfToken token,
03371                 int element)
03372         /*@modifies hsa @*/
03373 {
03374     char numbuf[64];    /* XXX big enuf for "Tag_0x01234567" */
03375     char * t, * te;
03376     int i, j;
03377     int numElements;
03378     int_32 type;
03379     int_32 count;
03380     sprintfToken spft;
03381     int condNumFormats;
03382     size_t need;
03383 
03384     /* we assume the token and header have been validated already! */
03385 
03386     switch (token->type) {
03387     case PTOK_NONE:
03388         break;
03389 
03390     case PTOK_STRING:
03391         need = token->u.string.len;
03392         if (need == 0) break;
03393         t = hsaReserve(hsa, need);
03394 /*@-boundswrite@*/
03395         te = stpcpy(t, token->u.string.string);
03396 /*@=boundswrite@*/
03397         hsa->vallen += (te - t);
03398         break;
03399 
03400     case PTOK_TAG:
03401         t = hsa->val + hsa->vallen;
03402         te = formatValue(hsa, &token->u.tag,
03403                         (token->u.tag.justOne ? 0 : element));
03404         if (te == NULL)
03405             return NULL;
03406         break;
03407 
03408     case PTOK_COND:
03409         if (token->u.cond.tag.ext || headerIsEntry(hsa->h, token->u.cond.tag.tag)) {
03410             spft = token->u.cond.ifFormat;
03411             condNumFormats = token->u.cond.numIfTokens;
03412         } else {
03413             spft = token->u.cond.elseFormat;
03414             condNumFormats = token->u.cond.numElseTokens;
03415         }
03416 
03417         need = condNumFormats * 20;
03418         if (spft == NULL || need == 0) break;
03419 
03420         t = hsaReserve(hsa, need);
03421         for (i = 0; i < condNumFormats; i++, spft++) {
03422             te = singleSprintf(hsa, spft, element);
03423             if (te == NULL)
03424                 return NULL;
03425         }
03426         break;
03427 
03428     case PTOK_ARRAY:
03429         numElements = -1;
03430         spft = token->u.array.format;
03431         for (i = 0; i < token->u.array.numTokens; i++, spft++)
03432         {
03433             if (spft->type != PTOK_TAG ||
03434                 spft->u.tag.arrayCount ||
03435                 spft->u.tag.justOne) continue;
03436 
03437             if (spft->u.tag.ext) {
03438 /*@-boundswrite@*/
03439                 if (getExtension(hsa, spft->u.tag.ext, &type, NULL, &count, 
03440                                  hsa->ec + spft->u.tag.extNum))
03441                      continue;
03442 /*@=boundswrite@*/
03443             } else {
03444 /*@-boundswrite@*/
03445                 if (!headerGetEntry(hsa->h, spft->u.tag.tag, &type, NULL, &count))
03446                     continue;
03447 /*@=boundswrite@*/
03448             } 
03449 
03450             if (type == RPM_BIN_TYPE || type == RPM_ASN1_TYPE || type == RPM_OPENPGP_TYPE)
03451                 count = 1;      /* XXX count abused as no. of bytes. */
03452 
03453             if (numElements > 1 && count != numElements)
03454             switch (type) {
03455             default:
03456                 hsa->errmsg =
03457                         _("array iterator used with different sized arrays");
03458                 return NULL;
03459                 /*@notreached@*/ /*@switchbreak@*/ break;
03460             case RPM_OPENPGP_TYPE:
03461             case RPM_ASN1_TYPE:
03462             case RPM_BIN_TYPE:
03463             case RPM_STRING_TYPE:
03464                 /*@switchbreak@*/ break;
03465             }
03466             if (count > numElements)
03467                 numElements = count;
03468         }
03469 
03470         if (numElements == -1) {
03471 #ifdef  DYING   /* XXX lots of pugly "(none)" lines with --conflicts. */
03472             need = sizeof("(none)\n") - 1;
03473             t = hsaReserve(hsa, need);
03474 /*@-boundswrite@*/
03475             te = stpcpy(t, "(none)\n");
03476 /*@=boundswrite@*/
03477             hsa->vallen += (te - t);
03478 #endif
03479         } else {
03480             int isxml;
03481             int isyaml;
03482 
03483             need = numElements * token->u.array.numTokens;
03484             if (need == 0) break;
03485 
03486             spft = token->u.array.format;
03487             isxml = (spft->type == PTOK_TAG && spft->u.tag.type != NULL &&
03488                 !strcmp(spft->u.tag.type, "xml"));
03489             isyaml = (spft->type == PTOK_TAG && spft->u.tag.type != NULL &&
03490                 !strcmp(spft->u.tag.type, "yaml"));
03491 
03492             if (isxml) {
03493                 const char * tagN = myTagName(hsa->tags, spft->u.tag.tag, NULL);
03494                 /* XXX display "Tag_0x01234567" for unknown tags. */
03495                 if (tagN == NULL) {
03496                     (void) snprintf(numbuf, sizeof(numbuf), "Tag_0x%08x",
03497                                 spft->u.tag.tag);
03498                     numbuf[sizeof(numbuf)-1] = '\0';
03499                     tagN = numbuf;
03500                 }
03501                 need = sizeof("  <rpmTag name=\"\">\n") + strlen(tagN);
03502                 te = t = hsaReserve(hsa, need);
03503 /*@-boundswrite@*/
03504                 te = stpcpy( stpcpy( stpcpy(te, "  <rpmTag name=\""), tagN), "\">\n");
03505 /*@=boundswrite@*/
03506                 hsa->vallen += (te - t);
03507             }
03508             if (isyaml) {
03509                 int tagT = -1;
03510                 const char * tagN = myTagName(hsa->tags, spft->u.tag.tag, &tagT);
03511                 /* XXX display "Tag_0x01234567" for unknown tags. */
03512                 if (tagN == NULL) {
03513                     (void) snprintf(numbuf, sizeof(numbuf), "Tag_0x%08x",
03514                                 spft->u.tag.tag);
03515                     numbuf[sizeof(numbuf)-1] = '\0';
03516                     tagN = numbuf;
03517                     tagT = numElements > 1
03518                         ?  RPM_ARRAY_RETURN_TYPE : RPM_SCALAR_RETURN_TYPE;
03519                 }
03520                 need = sizeof("  :     - ") + strlen(tagN);
03521                 te = t = hsaReserve(hsa, need);
03522 /*@-boundswrite@*/
03523                 *te++ = ' ';
03524                 *te++ = ' ';
03525                 te = stpcpy(te, tagN);
03526                 *te++ = ':';
03527                 *te++ = (((tagT & RPM_MASK_RETURN_TYPE) == RPM_ARRAY_RETURN_TYPE)
03528                         ? '\n' : ' ');
03529                 /* XXX Dirnames: in srpms need "    " indent */
03530                 if (((tagT & RPM_MASK_RETURN_TYPE) == RPM_ARRAY_RETURN_TYPE)
03531                  && numElements == 1) {
03532                     te = stpcpy(te, "    ");
03533                     if (spft->u.tag.tag != 1118)
03534                         te = stpcpy(te, "- ");
03535                 }
03536                 *te = '\0';
03537 /*@=boundswrite@*/
03538                 hsa->vallen += (te - t);
03539             }
03540 
03541             need = numElements * token->u.array.numTokens * 10;
03542             t = hsaReserve(hsa, need);
03543             for (j = 0; j < numElements; j++) {
03544                 spft = token->u.array.format;
03545                 for (i = 0; i < token->u.array.numTokens; i++, spft++) {
03546                     te = singleSprintf(hsa, spft, j);
03547                     if (te == NULL)
03548                         return NULL;
03549                 }
03550             }
03551 
03552             if (isxml) {
03553                 need = sizeof("  </rpmTag>\n") - 1;
03554                 te = t = hsaReserve(hsa, need);
03555 /*@-boundswrite@*/
03556                 te = stpcpy(te, "  </rpmTag>\n");
03557 /*@=boundswrite@*/
03558                 hsa->vallen += (te - t);
03559             }
03560             if (isyaml) {
03561 #if 0
03562                 need = sizeof("\n") - 1;
03563                 te = t = hsaReserve(hsa, need);
03564 /*@-boundswrite@*/
03565                 te = stpcpy(te, "\n");
03566 /*@=boundswrite@*/
03567                 hsa->vallen += (te - t);
03568 #endif
03569             }
03570 
03571         }
03572         break;
03573     }
03574 
03575     return (hsa->val + hsa->vallen);
03576 }
03577 
03583 static /*@only@*/ rpmec
03584 rpmecNew(const headerSprintfExtension exts)
03585         /*@*/
03586 {
03587     headerSprintfExtension ext;
03588     rpmec ec;
03589     int i = 0;
03590 
03591     for (ext = exts; ext != NULL && ext->type != HEADER_EXT_LAST;
03592         ext = (ext->type == HEADER_EXT_MORE ? ext->u.more : ext+1))
03593     {
03594         i++;
03595     }
03596 
03597     ec = xcalloc(i, sizeof(*ec));
03598     return ec;
03599 }
03600 
03607 static /*@null@*/ rpmec
03608 rpmecFree(const headerSprintfExtension exts, /*@only@*/ rpmec ec)
03609         /*@modifies ec @*/
03610 {
03611     headerSprintfExtension ext;
03612     int i = 0;
03613 
03614     for (ext = exts; ext != NULL && ext->type != HEADER_EXT_LAST;
03615         ext = (ext->type == HEADER_EXT_MORE ? ext->u.more : ext+1))
03616     {
03617 /*@-boundswrite@*/
03618         if (ec[i].freeit) ec[i].data = _free(ec[i].data);
03619 /*@=boundswrite@*/
03620         i++;
03621     }
03622 
03623     ec = _free(ec);
03624     return NULL;
03625 }
03626 
03638 static /*@only@*/ /*@null@*/
03639 char * headerSprintf(Header h, const char * fmt,
03640                      const struct headerTagTableEntry_s * tbltags,
03641                      const struct headerSprintfExtension_s * extensions,
03642                      /*@null@*/ /*@out@*/ errmsg_t * errmsg)
03643         /*@modifies h, *errmsg @*/
03644         /*@requires maxSet(errmsg) >= 0 @*/
03645 {
03646     headerSprintfArgs hsa = memset(alloca(sizeof(*hsa)), 0, sizeof(*hsa));
03647     sprintfToken nextfmt;
03648     sprintfTag tag;
03649     char * t, * te;
03650     int isxml;
03651     int isyaml;
03652     int need;
03653  
03654     hsa->h = headerLink(h);
03655     hsa->fmt = xstrdup(fmt);
03656 /*@-castexpose@*/       /* FIX: legacy API shouldn't change. */
03657     hsa->exts = (headerSprintfExtension) extensions;
03658     hsa->tags = (headerTagTableEntry) tbltags;
03659 /*@=castexpose@*/
03660     hsa->errmsg = NULL;
03661 
03662 /*@-boundswrite@*/
03663     if (parseFormat(hsa, hsa->fmt, &hsa->format, &hsa->numTokens, NULL, PARSER_BEGIN))
03664         goto exit;
03665 /*@=boundswrite@*/
03666 
03667     hsa->ec = rpmecNew(hsa->exts);
03668     hsa->val = xstrdup("");
03669 
03670     tag =
03671         (hsa->format->type == PTOK_TAG
03672             ? &hsa->format->u.tag :
03673         (hsa->format->type == PTOK_ARRAY
03674             ? &hsa->format->u.array.format->u.tag :
03675         NULL));
03676     isxml = (tag != NULL && tag->tag == -2 && tag->type != NULL && !strcmp(tag->type, "xml"));
03677     isyaml = (tag != NULL && tag->tag == -2 && tag->type != NULL && !strcmp(tag->type, "yaml"));
03678 
03679     if (isxml) {
03680         need = sizeof("<rpmHeader>\n") - 1;
03681         t = hsaReserve(hsa, need);
03682 /*@-boundswrite@*/
03683         te = stpcpy(t, "<rpmHeader>\n");
03684 /*@=boundswrite@*/
03685         hsa->vallen += (te - t);
03686     }
03687     if (isyaml) {
03688         need = sizeof("- !!omap\n") - 1;
03689         t = hsaReserve(hsa, need);
03690 /*@-boundswrite@*/
03691         te = stpcpy(t, "- !!omap\n");
03692 /*@=boundswrite@*/
03693         hsa->vallen += (te - t);
03694     }
03695 
03696     hsa = hsaInit(hsa);
03697     while ((nextfmt = hsaNext(hsa)) != NULL) {
03698         te = singleSprintf(hsa, nextfmt, 0);
03699         if (te == NULL) {
03700             hsa->val = _free(hsa->val);
03701             break;
03702         }
03703     }
03704     hsa = hsaFini(hsa);
03705 
03706     if (isxml) {
03707         need = sizeof("</rpmHeader>\n") - 1;
03708         t = hsaReserve(hsa, need);
03709 /*@-boundswrite@*/
03710         te = stpcpy(t, "</rpmHeader>\n");
03711 /*@=boundswrite@*/
03712         hsa->vallen += (te - t);
03713     }
03714     if (isyaml) {
03715         need = sizeof("\n") - 1;
03716         t = hsaReserve(hsa, need);
03717 /*@-boundswrite@*/
03718         te = stpcpy(t, "\n");
03719 /*@=boundswrite@*/
03720         hsa->vallen += (te - t);
03721     }
03722 
03723     if (hsa->val != NULL && hsa->vallen < hsa->alloced)
03724         hsa->val = xrealloc(hsa->val, hsa->vallen+1);   
03725 
03726     hsa->ec = rpmecFree(hsa->exts, hsa->ec);
03727     hsa->format = freeFormat(hsa->format, hsa->numTokens);
03728 
03729 exit:
03730 /*@-dependenttrans -observertrans @*/
03731     if (errmsg)
03732         *errmsg = hsa->errmsg;
03733 /*@=dependenttrans =observertrans @*/
03734     hsa->h = headerFree(hsa->h);
03735     hsa->fmt = _free(hsa->fmt);
03736     return hsa->val;
03737 }
03738 
03748 static char * octalFormat(int_32 type, hPTR_t data, 
03749                 char * formatPrefix, int padding, /*@unused@*/int element)
03750         /*@modifies formatPrefix @*/
03751 {
03752     char * val;
03753 
03754     if (type == RPM_INT32_TYPE) {
03755         val = xmalloc(20 + padding);
03756 /*@-boundswrite@*/
03757         strcat(formatPrefix, "o");
03758 /*@=boundswrite@*/
03759         /*@-formatconst@*/
03760         sprintf(val, formatPrefix, *((int_32 *) data));
03761         /*@=formatconst@*/
03762     } else if (type == RPM_INT64_TYPE) {
03763         val = xmalloc(40 + padding);
03764 /*@-boundswrite@*/
03765         strcat(formatPrefix, "llo");
03766 /*@=boundswrite@*/
03767         /*@-formatconst@*/
03768         sprintf(val, formatPrefix, *((int_64 *) data));
03769         /*@=formatconst@*/
03770     } else
03771         val = xstrdup(_("(not a number)"));
03772 
03773     return val;
03774 }
03775 
03785 static char * hexFormat(int_32 type, hPTR_t data, 
03786                 char * formatPrefix, int padding, /*@unused@*/int element)
03787         /*@modifies formatPrefix @*/
03788 {
03789     char * val;
03790 
03791     if (type == RPM_INT32_TYPE) {
03792         val = xmalloc(20 + padding);
03793 /*@-boundswrite@*/
03794         strcat(formatPrefix, "x");
03795 /*@=boundswrite@*/
03796         /*@-formatconst@*/
03797         sprintf(val, formatPrefix, *((int_32 *) data));
03798         /*@=formatconst@*/
03799     } else if (type == RPM_INT64_TYPE) {
03800         val = xmalloc(40 + padding);
03801 /*@-boundswrite@*/
03802         strcat(formatPrefix, "llx");
03803 /*@=boundswrite@*/
03804         /*@-formatconst@*/
03805         sprintf(val, formatPrefix, *((int_64 *) data));
03806         /*@=formatconst@*/
03807     } else
03808         val = xstrdup(_("(not a number)"));
03809 
03810     return val;
03811 }
03812 
03823 static char * realDateFormat(int_32 type, hPTR_t data, 
03824                 char * formatPrefix, int padding, /*@unused@*/int element,
03825                 const char * strftimeFormat)
03826         /*@modifies formatPrefix @*/
03827 {
03828     char * val;
03829 
03830     if (type != RPM_INT32_TYPE) {
03831         val = xstrdup(_("(not a number)"));
03832     } else {
03833         struct tm * tstruct;
03834         char buf[50];
03835 
03836         val = xmalloc(50 + padding);
03837 /*@-boundswrite@*/
03838         strcat(formatPrefix, "s");
03839 /*@=boundswrite@*/
03840 
03841         /* this is important if sizeof(int_32) ! sizeof(time_t) */
03842         {   time_t dateint = *((int_32 *) data);
03843             tstruct = localtime(&dateint);
03844         }
03845         buf[0] = '\0';
03846         if (tstruct)
03847             (void) strftime(buf, sizeof(buf) - 1, strftimeFormat, tstruct);
03848         /*@-formatconst@*/
03849         sprintf(val, formatPrefix, buf);
03850         /*@=formatconst@*/
03851     }
03852 
03853     return val;
03854 }
03855 
03865 static char * dateFormat(int_32 type, hPTR_t data, 
03866                          char * formatPrefix, int padding, int element)
03867         /*@modifies formatPrefix @*/
03868 {
03869     return realDateFormat(type, data, formatPrefix, padding, element,
03870                         _("%c"));
03871 }
03872 
03882 static char * dayFormat(int_32 type, hPTR_t data, 
03883                          char * formatPrefix, int padding, int element)
03884         /*@modifies formatPrefix @*/
03885 {
03886     return realDateFormat(type, data, formatPrefix, padding, element, 
03887                           _("%a %b %d %Y"));
03888 }
03889 
03899 static char * shescapeFormat(int_32 type, hPTR_t data, 
03900                 char * formatPrefix, int padding, /*@unused@*/int element)
03901         /*@modifies formatPrefix @*/
03902 {
03903     char * result, * dst, * src, * buf;
03904 
03905     if (type == RPM_INT32_TYPE) {
03906         result = xmalloc(padding + 20);
03907 /*@-boundswrite@*/
03908         strcat(formatPrefix, "d");
03909 /*@=boundswrite@*/
03910         /*@-formatconst@*/
03911         sprintf(result, formatPrefix, *((int_32 *) data));
03912         /*@=formatconst@*/
03913     } else if (type == RPM_INT64_TYPE) {
03914         result = xmalloc(padding + 40);
03915 /*@-boundswrite@*/
03916         strcat(formatPrefix, "lld");
03917 /*@=boundswrite@*/
03918         /*@-formatconst@*/
03919         sprintf(result, formatPrefix, *((int_64 *) data));
03920         /*@=formatconst@*/
03921     } else {
03922         buf = alloca(strlen(data) + padding + 2);
03923 /*@-boundswrite@*/
03924         strcat(formatPrefix, "s");
03925 /*@=boundswrite@*/
03926         /*@-formatconst@*/
03927         sprintf(buf, formatPrefix, data);
03928         /*@=formatconst@*/
03929 
03930 /*@-boundswrite@*/
03931         result = dst = xmalloc(strlen(buf) * 4 + 3);
03932         *dst++ = '\'';
03933         for (src = buf; *src != '\0'; src++) {
03934             if (*src == '\'') {
03935                 *dst++ = '\'';
03936                 *dst++ = '\\';
03937                 *dst++ = '\'';
03938                 *dst++ = '\'';
03939             } else {
03940                 *dst++ = *src;
03941             }
03942         }
03943         *dst++ = '\'';
03944         *dst = '\0';
03945 /*@=boundswrite@*/
03946 
03947     }
03948 
03949     return result;
03950 }
03951 
03952 /*@-type@*/ /* FIX: cast? */
03953 const struct headerSprintfExtension_s headerDefaultFormats[] = {
03954     { HEADER_EXT_FORMAT, "octal", { octalFormat } },
03955     { HEADER_EXT_FORMAT, "hex", { hexFormat } },
03956     { HEADER_EXT_FORMAT, "date", { dateFormat } },
03957     { HEADER_EXT_FORMAT, "day", { dayFormat } },
03958     { HEADER_EXT_FORMAT, "shescape", { shescapeFormat } },
03959     { HEADER_EXT_LAST, NULL, { NULL } }
03960 };
03961 /*@=type@*/
03962 
03969 static
03970 void headerCopyTags(Header headerFrom, Header headerTo, hTAG_t tagstocopy)
03971         /*@modifies headerTo @*/
03972 {
03973     int * p;
03974 
03975     if (headerFrom == headerTo)
03976         return;
03977 
03978     for (p = tagstocopy; *p != 0; p++) {
03979         char *s;
03980         int_32 type;
03981         int_32 count;
03982         if (headerIsEntry(headerTo, *p))
03983             continue;
03984 /*@-boundswrite@*/
03985         if (!headerGetEntryMinMemory(headerFrom, *p, &type,
03986                                 (hPTR_t *) &s, &count))
03987             continue;
03988 /*@=boundswrite@*/
03989         (void) headerAddEntry(headerTo, *p, type, s, count);
03990         s = headerFreeData(s, type);
03991     }
03992 }
03993 
03994 /*@observer@*/ /*@unchecked@*/
03995 static struct HV_s hdrVec1 = {
03996     headerLink,
03997     headerUnlink,
03998     headerFree,
03999     headerNew,
04000     headerSort,
04001     headerUnsort,
04002     headerSizeof,
04003     headerUnload,
04004     headerReload,
04005     headerCopy,
04006     headerLoad,
04007     headerCopyLoad,
04008     headerRead,
04009     headerWrite,
04010     headerIsEntry,
04011     headerFreeTag,
04012     headerGetEntry,
04013     headerGetEntryMinMemory,
04014     headerAddEntry,
04015     headerAppendEntry,
04016     headerAddOrAppendEntry,
04017     headerAddI18NString,
04018     headerModifyEntry,
04019     headerRemoveEntry,
04020     headerSprintf,
04021     headerCopyTags,
04022     headerFreeIterator,
04023     headerInitIterator,
04024     headerNextIterator,
04025     headerGetOrigin,
04026     headerSetOrigin,
04027     headerGetInstance,
04028     headerSetInstance,
04029     NULL, NULL,
04030     1
04031 };
04032 
04033 /*@-compmempass -redef@*/
04034 /*@observer@*/ /*@unchecked@*/
04035 HV_t hdrVec = &hdrVec1;
04036 /*@=compmempass =redef@*/

Generated on Mon Aug 3 16:23:17 2009 for rpm by  doxygen 1.5.1