lib/header.c

Go to the documentation of this file.
00001 
00005 /* RPM - Copyright (C) 1995-2000 Red Hat Software */
00006 
00007 /* Data written to file descriptors is in network byte order.    */
00008 /* Data read from file descriptors is expected to be in          */
00009 /* network byte order and is converted on the fly to host order. */
00010 
00011 #include "system.h"
00012 
00013 #define __HEADER_PROTOTYPES__
00014 
00015 #include <header_internal.h>
00016 
00017 #include "debug.h"
00018 
00019 /*@-redecl@*/   /* FIX: avoid rpmlib.h, need for debugging. */
00020 /*@observer@*/ const char *const tagName(int tag)       /*@*/;
00021 /*@=redecl@*/
00022 
00023 /*@access entryInfo @*/
00024 /*@access indexEntry @*/
00025 
00026 /*@access extensionCache @*/
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 /*@unchecked@*/
00046 static size_t headerMaxbytes = (32*1024*1024);
00047 
00052 #define hdrchkTags(_ntags)      ((_ntags) & 0xffff0000)
00053 
00058 #define hdrchkData(_nbytes)     ((_nbytes) & 0xff000000)
00059 
00063 /*@observer@*/ /*@unchecked@*/
00064 static int typeSizes[] =  { 
00065         0,      
00066         1,      
00067         1,      
00068         2,      
00069         4,      
00070         -1,     
00071         -1,     
00072         1,      
00073         -1,     
00074         -1      
00075 };
00076 
00077 /*@observer@*/ /*@unchecked@*/
00078 HV_t hdrVec;    /* forward reference */
00079 
00085 /*@unused@*/ static inline /*@null@*/ void *
00086 _free(/*@only@*/ /*@null@*/ /*@out@*/ const void * p) /*@modifies *p @*/
00087 {
00088     if (p != NULL)      free((void *)p);
00089     return NULL;
00090 }
00091 
00097 static
00098 Header headerLink(Header h)
00099         /*@modifies h @*/
00100 {
00101     if (h != NULL) h->nrefs++;
00102     /*@-refcounttrans -nullret @*/
00103     return h;
00104     /*@=refcounttrans =nullret @*/
00105 }
00106 
00112 static /*@null@*/
00113 Header headerUnlink(/*@killref@*/ /*@null@*/ Header h)
00114         /*@modifies h @*/
00115 {
00116     if (h != NULL) h->nrefs--;
00117     return NULL;
00118 }
00119 
00125 static /*@null@*/
00126 Header headerFree(/*@killref@*/ /*@null@*/ Header h)
00127         /*@modifies h @*/
00128 {
00129     (void) headerUnlink(h);
00130 
00131     /*@-usereleased@*/
00132     if (h == NULL || h->nrefs > 0)
00133         return NULL;    /* XXX return previous header? */
00134 
00135     if (h->index) {
00136         indexEntry entry = h->index;
00137         int i;
00138         for (i = 0; i < h->indexUsed; i++, entry++) {
00139             if ((h->flags & HEADERFLAG_ALLOCATED) && ENTRY_IS_REGION(entry)) {
00140                 if (entry->length > 0) {
00141                     int_32 * ei = entry->data;
00142                     if ((ei - 2) == h->blob) h->blob = _free(h->blob);
00143                     entry->data = NULL;
00144                 }
00145             } else if (!ENTRY_IN_REGION(entry)) {
00146                 entry->data = _free(entry->data);
00147             }
00148             entry->data = NULL;
00149         }
00150         h->index = _free(h->index);
00151     }
00152 
00153     /*@-refcounttrans@*/ h = _free(h); /*@=refcounttrans@*/
00154     return h;
00155     /*@=usereleased@*/
00156 }
00157 
00162 static
00163 Header headerNew(void)
00164         /*@*/
00165 {
00166     Header h = xcalloc(1, sizeof(*h));
00167 
00168     /*@-assignexpose@*/
00169     h->hv = *hdrVec;            /* structure assignment */
00170     /*@=assignexpose@*/
00171     h->blob = NULL;
00172     h->indexAlloced = INDEX_MALLOC_SIZE;
00173     h->indexUsed = 0;
00174     h->flags = HEADERFLAG_SORTED;
00175 
00176     h->index = (h->indexAlloced
00177         ? xcalloc(h->indexAlloced, sizeof(*h->index))
00178         : NULL);
00179 
00180     /*@-globstate -observertrans @*/
00181     h->nrefs = 0;
00182     return headerLink(h);
00183     /*@=globstate =observertrans @*/
00184 }
00185 
00188 static int indexCmp(const void * avp, const void * bvp) /*@*/
00189 {
00190     /*@-castexpose@*/
00191     indexEntry ap = (indexEntry) avp, bp = (indexEntry) bvp;
00192     /*@=castexpose@*/
00193     return (ap->info.tag - bp->info.tag);
00194 }
00195 
00200 static
00201 void headerSort(Header h)
00202         /*@modifies h @*/
00203 {
00204     if (!(h->flags & HEADERFLAG_SORTED)) {
00205         qsort(h->index, h->indexUsed, sizeof(*h->index), indexCmp);
00206         h->flags |= HEADERFLAG_SORTED;
00207     }
00208 }
00209 
00212 static int offsetCmp(const void * avp, const void * bvp) /*@*/
00213 {
00214     /*@-castexpose@*/
00215     indexEntry ap = (indexEntry) avp, bp = (indexEntry) bvp;
00216     /*@=castexpose@*/
00217     int rc = (ap->info.offset - bp->info.offset);
00218 
00219     if (rc == 0) {
00220         /* Within a region, entries sort by address. Added drips sort by tag. */
00221         if (ap->info.offset < 0)
00222             rc = (((char *)ap->data) - ((char *)bp->data));
00223         else
00224             rc = (ap->info.tag - bp->info.tag);
00225     }
00226     return rc;
00227 }
00228 
00233 static
00234 void headerUnsort(Header h)
00235         /*@modifies h @*/
00236 {
00237     qsort(h->index, h->indexUsed, sizeof(*h->index), offsetCmp);
00238 }
00239 
00246 static
00247 unsigned int headerSizeof(/*@null@*/ Header h, enum hMagic magicp)
00248         /*@modifies h @*/
00249 {
00250     indexEntry entry;
00251     unsigned int size = 0;
00252     unsigned int pad = 0;
00253     int i;
00254 
00255     if (h == NULL)
00256         return size;
00257 
00258     headerSort(h);
00259 
00260     switch (magicp) {
00261     case HEADER_MAGIC_YES:
00262         size += sizeof(header_magic);
00263         break;
00264     case HEADER_MAGIC_NO:
00265         break;
00266     }
00267 
00268     /*@-sizeoftype@*/
00269     size += 2 * sizeof(int_32); /* count of index entries */
00270     /*@=sizeoftype@*/
00271 
00272     for (i = 0, entry = h->index; i < h->indexUsed; i++, entry++) {
00273         unsigned diff;
00274         int_32 type;
00275 
00276         /* Regions go in as is ... */
00277         if (ENTRY_IS_REGION(entry)) {
00278             size += entry->length;
00279             /* XXX Legacy regions do not include the region tag and data. */
00280             /*@-sizeoftype@*/
00281             if (i == 0 && (h->flags & HEADERFLAG_LEGACY))
00282                 size += sizeof(struct entryInfo) + entry->info.count;
00283             /*@=sizeoftype@*/
00284             continue;
00285         }
00286 
00287         /* ... and region elements are skipped. */
00288         if (entry->info.offset < 0)
00289             continue;
00290 
00291         /* Alignment */
00292         type = entry->info.type;
00293         if (typeSizes[type] > 1) {
00294             diff = typeSizes[type] - (size % typeSizes[type]);
00295             if (diff != typeSizes[type]) {
00296                 size += diff;
00297                 pad += diff;
00298             }
00299         }
00300 
00301         /*@-sizeoftype@*/
00302         size += sizeof(struct entryInfo) + entry->length;
00303         /*@=sizeoftype@*/
00304     }
00305 
00306     return size;
00307 }
00308 
00318 /*@mayexit@*/
00319 static int dataLength(int_32 type, hPTR_t p, int_32 count, int onDisk)
00320         /*@*/
00321 {
00322     int length = 0;
00323 
00324     switch (type) {
00325     case RPM_STRING_TYPE:
00326         if (count == 1) {       /* Special case -- p is just the string */
00327             length = strlen(p) + 1;
00328             break;
00329         }
00330         /* This should not be allowed */
00331         /*@-modfilesys@*/
00332         fprintf(stderr, _("dataLength() RPM_STRING_TYPE count must be 1.\n"));
00333         /*@=modfilesys@*/
00334         exit(EXIT_FAILURE);
00335         /*@notreached@*/ break;
00336 
00337     case RPM_STRING_ARRAY_TYPE:
00338     case RPM_I18NSTRING_TYPE:
00339     {   int i;
00340 
00341         /* This is like RPM_STRING_TYPE, except it's *always* an array */
00342         /* Compute sum of length of all strings, including null terminators */
00343         i = count;
00344 
00345         if (onDisk) {
00346             const char * chptr = p;
00347             int thisLen;
00348 
00349             while (i--) {
00350                 thisLen = strlen(chptr) + 1;
00351                 length += thisLen;
00352                 chptr += thisLen;
00353             }
00354         } else {
00355             const char ** src = (const char **)p;
00356             while (i--) {
00357                 /* add one for null termination */
00358                 length += strlen(*src++) + 1;
00359             }
00360         }
00361     }   break;
00362 
00363     default:
00364         if (typeSizes[type] != -1) {
00365             length = typeSizes[type] * count;
00366             break;
00367         }
00368         /*@-modfilesys@*/
00369         fprintf(stderr, _("Data type %d not supported\n"), (int) type);
00370         /*@=modfilesys@*/
00371         exit(EXIT_FAILURE);
00372         /*@notreached@*/ break;
00373     }
00374 
00375     return length;
00376 }
00377 
00403 static int regionSwab(/*@null@*/ indexEntry entry, int il, int dl,
00404                 entryInfo pe, char * dataStart, int regionid)
00405         /*@modifies *entry, *dataStart @*/
00406 {
00407     char * tprev = NULL;
00408     char * t = NULL;
00409     int tdel, tl = dl;
00410     struct indexEntry ieprev;
00411 
00412     memset(&ieprev, 0, sizeof(ieprev));
00413     for (; il > 0; il--, pe++) {
00414         struct indexEntry ie;
00415         int_32 type;
00416 
00417         ie.info.tag = ntohl(pe->tag);
00418         ie.info.type = ntohl(pe->type);
00419         if (ie.info.type < RPM_MIN_TYPE || ie.info.type > RPM_MAX_TYPE)
00420             return -1;
00421         ie.info.count = ntohl(pe->count);
00422         ie.info.offset = ntohl(pe->offset);
00423         ie.data = t = dataStart + ie.info.offset;
00424         ie.length = dataLength(ie.info.type, ie.data, ie.info.count, 1);
00425         ie.rdlen = 0;
00426 
00427         if (entry) {
00428             ie.info.offset = regionid;
00429             *entry = ie;        /* structure assignment */
00430             entry++;
00431         }
00432 
00433         /* Alignment */
00434         type = ie.info.type;
00435         if (typeSizes[type] > 1) {
00436             unsigned diff;
00437             diff = typeSizes[type] - (dl % typeSizes[type]);
00438             if (diff != typeSizes[type]) {
00439                 dl += diff;
00440                 if (ieprev.info.type == RPM_I18NSTRING_TYPE)
00441                     ieprev.length += diff;
00442             }
00443         }
00444         tdel = (tprev ? (t - tprev) : 0);
00445         if (ieprev.info.type == RPM_I18NSTRING_TYPE)
00446             tdel = ieprev.length;
00447 
00448         if (ie.info.tag >= HEADER_I18NTABLE) {
00449             tprev = t;
00450         } else {
00451             tprev = dataStart;
00452             /* XXX HEADER_IMAGE tags don't include region sub-tag. */
00453             /*@-sizeoftype@*/
00454             if (ie.info.tag == HEADER_IMAGE)
00455                 tprev -= REGION_TAG_COUNT;
00456             /*@=sizeoftype@*/
00457         }
00458 
00459         /* Perform endian conversions */
00460         switch (ntohl(pe->type)) {
00461         case RPM_INT32_TYPE:
00462         {   int_32 * it = (int_32 *)t;
00463             for (; ie.info.count > 0; ie.info.count--, it += 1)
00464                 *it = htonl(*it);
00465             t = (char *) it;
00466         }   /*@switchbreak@*/ break;
00467         case RPM_INT16_TYPE:
00468         {   int_16 * it = (int_16 *) t;
00469             for (; ie.info.count > 0; ie.info.count--, it += 1)
00470                 *it = htons(*it);
00471             t = (char *) it;
00472         }   /*@switchbreak@*/ break;
00473         default:
00474             t += ie.length;
00475             /*@switchbreak@*/ break;
00476         }
00477 
00478         dl += ie.length;
00479         tl += tdel;
00480         ieprev = ie;    /* structure assignment */
00481 
00482     }
00483     tdel = (tprev ? (t - tprev) : 0);
00484     tl += tdel;
00485 
00486     /* XXX
00487      * There are two hacks here:
00488      *  1) tl is 16b (i.e. REGION_TAG_COUNT) short while doing headerReload().
00489      *  2) the 8/98 rpm bug with inserting i18n tags needs to use tl, not dl.
00490      */
00491     /*@-sizeoftype@*/
00492     if (tl+REGION_TAG_COUNT == dl)
00493         tl += REGION_TAG_COUNT;
00494     /*@=sizeoftype@*/
00495 
00496     return dl;
00497 }
00498 
00501 static /*@only@*/ /*@null@*/ void * doHeaderUnload(Header h,
00502                 /*@out@*/ int * lengthPtr)
00503         /*@modifies h, *lengthPtr @*/
00504 {
00505     int_32 * ei = NULL;
00506     entryInfo pe;
00507     char * dataStart;
00508     char * te;
00509     unsigned pad;
00510     unsigned len;
00511     int_32 il = 0;
00512     int_32 dl = 0;
00513     indexEntry entry; 
00514     int_32 type;
00515     int i;
00516     int drlen, ndribbles;
00517     int driplen, ndrips;
00518     int legacy = 0;
00519 
00520     /* Sort entries by (offset,tag). */
00521     headerUnsort(h);
00522 
00523     /* Compute (il,dl) for all tags, including those deleted in region. */
00524     pad = 0;
00525     drlen = ndribbles = driplen = ndrips = 0;
00526     for (i = 0, entry = h->index; i < h->indexUsed; i++, entry++) {
00527         if (ENTRY_IS_REGION(entry)) {
00528             int_32 rdl = -entry->info.offset;   /* negative offset */
00529             int_32 ril = rdl/sizeof(*pe);
00530             int rid = entry->info.offset;
00531 
00532             il += ril;
00533             dl += entry->rdlen + entry->info.count;
00534             /* XXX Legacy regions do not include the region tag and data. */
00535             if (i == 0 && (h->flags & HEADERFLAG_LEGACY))
00536                 il += 1;
00537 
00538             /* Skip rest of entries in region, but account for dribbles. */
00539             for (; i < h->indexUsed && entry->info.offset <= rid+1; i++, entry++) {
00540                 if (entry->info.offset <= rid)
00541                     /*@innercontinue@*/ continue;
00542 
00543                 /* Alignment */
00544                 type = entry->info.type;
00545                 if (typeSizes[type] > 1) {
00546                     unsigned diff;
00547                     diff = typeSizes[type] - (dl % typeSizes[type]);
00548                     if (diff != typeSizes[type]) {
00549                         drlen += diff;
00550                         pad += diff;
00551                         dl += diff;
00552                     }
00553                 }
00554 
00555                 ndribbles++;
00556                 il++;
00557                 drlen += entry->length;
00558                 dl += entry->length;
00559             }
00560             i--;
00561             entry--;
00562             continue;
00563         }
00564 
00565         /* Ignore deleted drips. */
00566         if (entry->data == NULL || entry->length <= 0)
00567             continue;
00568 
00569         /* Alignment */
00570         type = entry->info.type;
00571         if (typeSizes[type] > 1) {
00572             unsigned diff;
00573             diff = typeSizes[type] - (dl % typeSizes[type]);
00574             if (diff != typeSizes[type]) {
00575                 driplen += diff;
00576                 pad += diff;
00577                 dl += diff;
00578             } else
00579                 diff = 0;
00580         }
00581 
00582         ndrips++;
00583         il++;
00584         driplen += entry->length;
00585         dl += entry->length;
00586     }
00587 
00588     /* Sanity checks on header intro. */
00589     if (hdrchkTags(il) || hdrchkData(dl))
00590         goto errxit;
00591 
00592     len = sizeof(il) + sizeof(dl) + (il * sizeof(*pe)) + dl;
00593 
00594     ei = xmalloc(len);
00595     ei[0] = htonl(il);
00596     ei[1] = htonl(dl);
00597 
00598     pe = (entryInfo) &ei[2];
00599     dataStart = te = (char *) (pe + il);
00600 
00601     pad = 0;
00602     for (i = 0, entry = h->index; i < h->indexUsed; i++, entry++) {
00603         const char * src;
00604 char *t;
00605         int count;
00606         int rdlen;
00607 
00608         if (entry->data == NULL || entry->length <= 0)
00609             continue;
00610 
00611 t = te;
00612         pe->tag = htonl(entry->info.tag);
00613         pe->type = htonl(entry->info.type);
00614         pe->count = htonl(entry->info.count);
00615 
00616         if (ENTRY_IS_REGION(entry)) {
00617             int_32 rdl = -entry->info.offset;   /* negative offset */
00618             int_32 ril = rdl/sizeof(*pe) + ndribbles;
00619             int rid = entry->info.offset;
00620 
00621             src = (char *)entry->data;
00622             rdlen = entry->rdlen;
00623 
00624             /* XXX Legacy regions do not include the region tag and data. */
00625             if (i == 0 && (h->flags & HEADERFLAG_LEGACY)) {
00626                 int_32 stei[4];
00627 
00628                 legacy = 1;
00629                 memcpy(pe+1, src, rdl);
00630                 memcpy(te, src + rdl, rdlen);
00631                 te += rdlen;
00632 
00633                 pe->offset = htonl(te - dataStart);
00634                 stei[0] = pe->tag;
00635                 stei[1] = pe->type;
00636                 stei[2] = htonl(-rdl-entry->info.count);
00637                 stei[3] = pe->count;
00638                 memcpy(te, stei, entry->info.count);
00639                 te += entry->info.count;
00640                 ril++;
00641                 rdlen += entry->info.count;
00642 
00643                 count = regionSwab(NULL, ril, 0, pe, t, 0);
00644                 if (count != rdlen)
00645                     goto errxit;
00646 
00647             } else {
00648 
00649                 memcpy(pe+1, src + sizeof(*pe), ((ril-1) * sizeof(*pe)));
00650                 memcpy(te, src + (ril * sizeof(*pe)), rdlen+entry->info.count+drlen);
00651                 te += rdlen;
00652                 {   /*@-castexpose@*/
00653                     entryInfo se = (entryInfo)src;
00654                     /*@=castexpose@*/
00655                     int off = ntohl(se->offset);
00656                     pe->offset = (off) ? htonl(te - dataStart) : htonl(off);
00657                 }
00658                 te += entry->info.count + drlen;
00659 
00660                 count = regionSwab(NULL, ril, 0, pe, t, 0);
00661                 if (count != (rdlen + entry->info.count + drlen))
00662                     goto errxit;
00663             }
00664 
00665             /* Skip rest of entries in region. */
00666             while (i < h->indexUsed && entry->info.offset <= rid+1) {
00667                 i++;
00668                 entry++;
00669             }
00670             i--;
00671             entry--;
00672             pe += ril;
00673             continue;
00674         }
00675 
00676         /* Ignore deleted drips. */
00677         if (entry->data == NULL || entry->length <= 0)
00678             continue;
00679 
00680         /* Alignment */
00681         type = entry->info.type;
00682         if (typeSizes[type] > 1) {
00683             unsigned diff;
00684             diff = typeSizes[type] - ((te - dataStart) % typeSizes[type]);
00685             if (diff != typeSizes[type]) {
00686                 memset(te, 0, diff);
00687                 te += diff;
00688                 pad += diff;
00689             }
00690         }
00691 
00692         pe->offset = htonl(te - dataStart);
00693 
00694         /* copy data w/ endian conversions */
00695         switch (entry->info.type) {
00696         case RPM_INT32_TYPE:
00697             count = entry->info.count;
00698             src = entry->data;
00699             while (count--) {
00700                 *((int_32 *)te) = htonl(*((int_32 *)src));
00701                 /*@-sizeoftype@*/
00702                 te += sizeof(int_32);
00703                 src += sizeof(int_32);
00704                 /*@=sizeoftype@*/
00705             }
00706             /*@switchbreak@*/ break;
00707 
00708         case RPM_INT16_TYPE:
00709             count = entry->info.count;
00710             src = entry->data;
00711             while (count--) {
00712                 *((int_16 *)te) = htons(*((int_16 *)src));
00713                 /*@-sizeoftype@*/
00714                 te += sizeof(int_16);
00715                 src += sizeof(int_16);
00716                 /*@=sizeoftype@*/
00717             }
00718             /*@switchbreak@*/ break;
00719 
00720         default:
00721             memcpy(te, entry->data, entry->length);
00722             te += entry->length;
00723             /*@switchbreak@*/ break;
00724         }
00725         pe++;
00726     }
00727    
00728     /* Insure that there are no memcpy underruns/overruns. */
00729     if (((char *)pe) != dataStart)
00730         goto errxit;
00731     if ((((char *)ei)+len) != te)
00732         goto errxit;
00733 
00734     if (lengthPtr)
00735         *lengthPtr = len;
00736 
00737     h->flags &= ~HEADERFLAG_SORTED;
00738     headerSort(h);
00739 
00740     return (void *) ei;
00741 
00742 errxit:
00743     /*@-usereleased@*/
00744     ei = _free(ei);
00745     /*@=usereleased@*/
00746     return (void *) ei;
00747 }
00748 
00754 static /*@only@*/ /*@null@*/
00755 void * headerUnload(Header h)
00756         /*@modifies h @*/
00757 {
00758     int length;
00759     void * uh = doHeaderUnload(h, &length);
00760     return uh;
00761 }
00762 
00770 static /*@null@*/
00771 indexEntry findEntry(/*@null@*/ Header h, int_32 tag, int_32 type)
00772         /*@modifies h @*/
00773 {
00774     indexEntry entry, entry2, last;
00775     struct indexEntry key;
00776 
00777     if (h == NULL) return NULL;
00778     if (!(h->flags & HEADERFLAG_SORTED)) headerSort(h);
00779 
00780     key.info.tag = tag;
00781 
00782     entry2 = entry = 
00783         bsearch(&key, h->index, h->indexUsed, sizeof(*h->index), indexCmp);
00784     if (entry == NULL)
00785         return NULL;
00786 
00787     if (type == RPM_NULL_TYPE)
00788         return entry;
00789 
00790     /* look backwards */
00791     while (entry->info.tag == tag && entry->info.type != type &&
00792            entry > h->index) entry--;
00793 
00794     if (entry->info.tag == tag && entry->info.type == type)
00795         return entry;
00796 
00797     last = h->index + h->indexUsed;
00798     /*@-usereleased@*/ /* FIX: entry2 = entry. Code looks bogus as well. */
00799     while (entry2->info.tag == tag && entry2->info.type != type &&
00800            entry2 < last) entry2++;
00801     /*@=usereleased@*/
00802 
00803     if (entry->info.tag == tag && entry->info.type == type)
00804         return entry;
00805 
00806     return NULL;
00807 }
00808 
00818 static
00819 int headerRemoveEntry(Header h, int_32 tag)
00820         /*@modifies h @*/
00821 {
00822     indexEntry last = h->index + h->indexUsed;
00823     indexEntry entry, first;
00824     int ne;
00825 
00826     entry = findEntry(h, tag, RPM_NULL_TYPE);
00827     if (!entry) return 1;
00828 
00829     /* Make sure entry points to the first occurence of this tag. */
00830     while (entry > h->index && (entry - 1)->info.tag == tag)  
00831         entry--;
00832 
00833     /* Free data for tags being removed. */
00834     for (first = entry; first < last; first++) {
00835         void * data;
00836         if (first->info.tag != tag)
00837             break;
00838         data = first->data;
00839         first->data = NULL;
00840         first->length = 0;
00841         if (ENTRY_IN_REGION(first))
00842             continue;
00843         data = _free(data);
00844     }
00845 
00846     ne = (first - entry);
00847     if (ne > 0) {
00848         h->indexUsed -= ne;
00849         ne = last - first;
00850         if (ne > 0)
00851             memmove(entry, first, (ne * sizeof(*entry)));
00852     }
00853 
00854     return 0;
00855 }
00856 
00862 static /*@null@*/
00863 Header headerLoad(/*@kept@*/ void * uh)
00864         /*@modifies uh @*/
00865 {
00866     int_32 * ei = (int_32 *) uh;
00867     int_32 il = ntohl(ei[0]);           /* index length */
00868     int_32 dl = ntohl(ei[1]);           /* data length */
00869     /*@-sizeoftype@*/
00870     size_t pvlen = sizeof(il) + sizeof(dl) +
00871                (il * sizeof(struct entryInfo)) + dl;
00872     /*@=sizeoftype@*/
00873     void * pv = uh;
00874     Header h = NULL;
00875     entryInfo pe;
00876     char * dataStart;
00877     indexEntry entry; 
00878     int rdlen;
00879     int i;
00880 
00881     /* Sanity checks on header intro. */
00882     if (hdrchkTags(il) || hdrchkData(dl))
00883         goto errxit;
00884 
00885     ei = (int_32 *) pv;
00886     /*@-castexpose@*/
00887     pe = (entryInfo) &ei[2];
00888     /*@=castexpose@*/
00889     dataStart = (char *) (pe + il);
00890 
00891     h = xcalloc(1, sizeof(*h));
00892     /*@-assignexpose@*/
00893     h->hv = *hdrVec;            /* structure assignment */
00894     /*@=assignexpose@*/
00895     /*@-assignexpose -kepttrans@*/
00896     h->blob = uh;
00897     /*@=assignexpose =kepttrans@*/
00898     h->indexAlloced = il + 1;
00899     h->indexUsed = il;
00900     h->index = xcalloc(h->indexAlloced, sizeof(*h->index));
00901     h->flags = HEADERFLAG_SORTED;
00902     h->nrefs = 0;
00903     h = headerLink(h);
00904 
00905     /*
00906      * XXX XFree86-libs, ash, and pdksh from Red Hat 5.2 have bogus
00907      * %verifyscript tag that needs to be diddled.
00908      */
00909     if (ntohl(pe->tag) == 15 &&
00910         ntohl(pe->type) == RPM_STRING_TYPE &&
00911         ntohl(pe->count) == 1)
00912     {
00913         pe->tag = htonl(1079);
00914     }
00915 
00916     entry = h->index;
00917     i = 0;
00918     if (!(htonl(pe->tag) < HEADER_I18NTABLE)) {
00919         h->flags |= HEADERFLAG_LEGACY;
00920         entry->info.type = REGION_TAG_TYPE;
00921         entry->info.tag = HEADER_IMAGE;
00922         /*@-sizeoftype@*/
00923         entry->info.count = REGION_TAG_COUNT;
00924         /*@=sizeoftype@*/
00925         entry->info.offset = ((char *)pe - dataStart); /* negative offset */
00926 
00927         /*@-assignexpose@*/
00928         entry->data = pe;
00929         /*@=assignexpose@*/
00930         entry->length = pvlen - sizeof(il) - sizeof(dl);
00931         rdlen = regionSwab(entry+1, il, 0, pe, dataStart, entry->info.offset);
00932 #if 0   /* XXX don't check, the 8/98 i18n bug fails here. */
00933         if (rdlen != dl)
00934             goto errxit;
00935 #endif
00936         entry->rdlen = rdlen;
00937         entry++;
00938         h->indexUsed++;
00939     } else {
00940         int nb = ntohl(pe->count);
00941         int_32 rdl;
00942         int_32 ril;
00943 
00944         h->flags &= ~HEADERFLAG_LEGACY;
00945 
00946         entry->info.type = htonl(pe->type);
00947         if (entry->info.type < RPM_MIN_TYPE || entry->info.type > RPM_MAX_TYPE)
00948             goto errxit;
00949         entry->info.count = htonl(pe->count);
00950 
00951         if (hdrchkTags(entry->info.count))
00952             goto errxit;
00953 
00954         {   int off = ntohl(pe->offset);
00955 
00956             if (hdrchkData(off))
00957                 goto errxit;
00958             if (off) {
00959                 int_32 * stei = memcpy(alloca(nb), dataStart + off, nb);
00960                 rdl = -ntohl(stei[2]);  /* negative offset */
00961                 ril = rdl/sizeof(*pe);
00962                 if (hdrchkTags(ril) || hdrchkData(rdl))
00963                     goto errxit;
00964                 entry->info.tag = htonl(pe->tag);
00965             } else {
00966                 ril = il;
00967                 /*@-sizeoftype@*/
00968                 rdl = (ril * sizeof(struct entryInfo));
00969                 /*@=sizeoftype@*/
00970                 entry->info.tag = HEADER_IMAGE;
00971             }
00972         }
00973         entry->info.offset = -rdl;      /* negative offset */
00974 
00975         /*@-assignexpose@*/
00976         entry->data = pe;
00977         /*@=assignexpose@*/
00978         entry->length = pvlen - sizeof(il) - sizeof(dl);
00979         rdlen = regionSwab(entry+1, ril-1, 0, pe+1, dataStart, entry->info.offset);
00980         if (rdlen < 0)
00981             goto errxit;
00982         entry->rdlen = rdlen;
00983 
00984         if (ril < h->indexUsed) {
00985             indexEntry newEntry = entry + ril;
00986             int ne = (h->indexUsed - ril);
00987             int rid = entry->info.offset+1;
00988             int rc;
00989 
00990             /* Load dribble entries from region. */
00991             rc = regionSwab(newEntry, ne, 0, pe+ril, dataStart, rid);
00992             if (rc < 0)
00993                 goto errxit;
00994             rdlen += rc;
00995 
00996           { indexEntry firstEntry = newEntry;
00997             int save = h->indexUsed;
00998             int j;
00999 
01000             /* Dribble entries replace duplicate region entries. */
01001             h->indexUsed -= ne;
01002             for (j = 0; j < ne; j++, newEntry++) {
01003                 (void) headerRemoveEntry(h, newEntry->info.tag);
01004                 if (newEntry->info.tag == HEADER_BASENAMES)
01005                     (void) headerRemoveEntry(h, HEADER_OLDFILENAMES);
01006             }
01007 
01008             /* If any duplicate entries were replaced, move new entries down. */
01009             if (h->indexUsed < (save - ne)) {
01010                 memmove(h->index + h->indexUsed, firstEntry,
01011                         (ne * sizeof(*entry)));
01012             }
01013             h->indexUsed += ne;
01014           }
01015         }
01016     }
01017 
01018     h->flags &= ~HEADERFLAG_SORTED;
01019     headerSort(h);
01020 
01021     /*@-globstate -observertrans @*/
01022     return h;
01023     /*@=globstate =observertrans @*/
01024 
01025 errxit:
01026     /*@-usereleased@*/
01027     if (h) {
01028         h->index = _free(h->index);
01029         /*@-refcounttrans@*/
01030         h = _free(h);
01031         /*@=refcounttrans@*/
01032     }
01033     /*@=usereleased@*/
01034     /*@-refcounttrans -globstate@*/
01035     return h;
01036     /*@=refcounttrans =globstate@*/
01037 }
01038 
01046 static /*@null@*/
01047 Header headerReload(/*@only@*/ Header h, int tag)
01048         /*@modifies h @*/
01049 {
01050     Header nh;
01051     int length;
01052     /*@-onlytrans@*/
01053     void * uh = doHeaderUnload(h, &length);
01054 
01055     h = headerFree(h);
01056     /*@=onlytrans@*/
01057     if (uh == NULL)
01058         return NULL;
01059     nh = headerLoad(uh);
01060     if (nh == NULL) {
01061         uh = _free(uh);
01062         return NULL;
01063     }
01064     if (nh->flags & HEADERFLAG_ALLOCATED)
01065         uh = _free(uh);
01066     nh->flags |= HEADERFLAG_ALLOCATED;
01067     if (ENTRY_IS_REGION(nh->index)) {
01068         if (tag == HEADER_SIGNATURES || tag == HEADER_IMMUTABLE)
01069             nh->index[0].info.tag = tag;
01070     }
01071     return nh;
01072 }
01073 
01079 static /*@null@*/
01080 Header headerCopyLoad(const void * uh)
01081         /*@*/
01082 {
01083     int_32 * ei = (int_32 *) uh;
01084     int_32 il = ntohl(ei[0]);           /* index length */
01085     int_32 dl = ntohl(ei[1]);           /* data length */
01086     /*@-sizeoftype@*/
01087     size_t pvlen = sizeof(il) + sizeof(dl) +
01088                         (il * sizeof(struct entryInfo)) + dl;
01089     /*@=sizeoftype@*/
01090     void * nuh = NULL;
01091     Header h = NULL;
01092 
01093     /* Sanity checks on header intro. */
01094     /*@-branchstate@*/
01095     if (!(hdrchkTags(il) || hdrchkData(dl)) && pvlen < headerMaxbytes) {
01096         nuh = memcpy(xmalloc(pvlen), uh, pvlen);
01097         if ((h = headerLoad(nuh)) != NULL)
01098             h->flags |= HEADERFLAG_ALLOCATED;
01099     }
01100     /*@=branchstate@*/
01101     /*@-branchstate@*/
01102     if (h == NULL)
01103         nuh = _free(nuh);
01104     /*@=branchstate@*/
01105     return h;
01106 }
01107 
01114 static /*@null@*/
01115 Header headerRead(FD_t fd, enum hMagic magicp)
01116         /*@modifies fd @*/
01117 {
01118     int_32 block[4];
01119     int_32 reserved;
01120     int_32 * ei = NULL;
01121     int_32 il;
01122     int_32 dl;
01123     int_32 magic;
01124     Header h = NULL;
01125     size_t len;
01126     int i;
01127 
01128     memset(block, 0, sizeof(block));
01129     i = 2;
01130     if (magicp == HEADER_MAGIC_YES)
01131         i += 2;
01132 
01133     /*@-type@*/ /* FIX: cast? */
01134     if (timedRead(fd, (char *)block, i*sizeof(*block)) != (i * sizeof(*block)))
01135         goto exit;
01136     /*@=type@*/
01137 
01138     i = 0;
01139 
01140     if (magicp == HEADER_MAGIC_YES) {
01141         magic = block[i++];
01142         if (memcmp(&magic, header_magic, sizeof(magic)))
01143             goto exit;
01144         reserved = block[i++];
01145     }
01146     
01147     il = ntohl(block[i]);       i++;
01148     dl = ntohl(block[i]);       i++;
01149 
01150     /*@-sizeoftype@*/
01151     len = sizeof(il) + sizeof(dl) + (il * sizeof(struct entryInfo)) + dl;
01152     /*@=sizeoftype@*/
01153 
01154     /* Sanity checks on header intro. */
01155     if (hdrchkTags(il) || hdrchkData(dl) || len > headerMaxbytes)
01156         goto exit;
01157 
01158     ei = xmalloc(len);
01159     ei[0] = htonl(il);
01160     ei[1] = htonl(dl);
01161     len -= sizeof(il) + sizeof(dl);
01162 
01163     /*@-type@*/ /* FIX: cast? */
01164     if (timedRead(fd, (char *)&ei[2], len) != len)
01165         goto exit;
01166     /*@=type@*/
01167     
01168     h = headerLoad(ei);
01169 
01170 exit:
01171     if (h) {
01172         if (h->flags & HEADERFLAG_ALLOCATED)
01173             ei = _free(ei);
01174         h->flags |= HEADERFLAG_ALLOCATED;
01175     } else if (ei)
01176         ei = _free(ei);
01177     /*@-mustmod@*/      /* FIX: timedRead macro obscures annotation */
01178     return h;
01179     /*@-mustmod@*/
01180 }
01181 
01189 static
01190 int headerWrite(FD_t fd, /*@null@*/ Header h, enum hMagic magicp)
01191         /*@globals fileSystem @*/
01192         /*@modifies fd, h, fileSystem @*/
01193 {
01194     ssize_t nb;
01195     int length;
01196     const void * uh;
01197 
01198     if (h == NULL)
01199         return 1;
01200     uh = doHeaderUnload(h, &length);
01201     if (uh == NULL)
01202         return 1;
01203     switch (magicp) {
01204     case HEADER_MAGIC_YES:
01205         /*@-sizeoftype@*/
01206         nb = Fwrite(header_magic, sizeof(char), sizeof(header_magic), fd);
01207         /*@=sizeoftype@*/
01208         if (nb != sizeof(header_magic))
01209             goto exit;
01210         break;
01211     case HEADER_MAGIC_NO:
01212         break;
01213     }
01214 
01215     /*@-sizeoftype@*/
01216     nb = Fwrite(uh, sizeof(char), length, fd);
01217     /*@=sizeoftype@*/
01218 
01219 exit:
01220     uh = _free(uh);
01221     return (nb == length ? 0 : 1);
01222 }
01223 
01230 static
01231 int headerIsEntry(/*@null@*/Header h, int_32 tag)
01232         /*@*/
01233 {
01234     /*@-mods@*/         /*@ FIX: h modified by sort. */
01235     return (findEntry(h, tag, RPM_NULL_TYPE) ? 1 : 0);
01236     /*@=mods@*/ 
01237 }
01238 
01249 static int copyEntry(const indexEntry entry,
01250                 /*@null@*/ /*@out@*/ hTYP_t type,
01251                 /*@null@*/ /*@out@*/ hPTR_t * p,
01252                 /*@null@*/ /*@out@*/ hCNT_t c,
01253                 int minMem)
01254         /*@modifies *type, *p, *c @*/
01255 {
01256     int_32 count = entry->info.count;
01257     int rc = 1;         /* XXX 1 on success. */
01258 
01259     if (p)
01260     switch (entry->info.type) {
01261     case RPM_BIN_TYPE:
01262         /*
01263          * XXX This only works for
01264          * XXX  "sealed" HEADER_IMMUTABLE/HEADER_SIGNATURES/HEADER_IMAGE.
01265          * XXX This will *not* work for unsealed legacy HEADER_IMAGE (i.e.
01266          * XXX a legacy header freshly read, but not yet unloaded to the rpmdb).
01267          */
01268         if (ENTRY_IS_REGION(entry)) {
01269             int_32 * ei = ((int_32 *)entry->data) - 2;
01270             /*@-castexpose@*/
01271             entryInfo pe = (entryInfo) (ei + 2);
01272             /*@=castexpose@*/
01273             char * dataStart = (char *) (pe + ntohl(ei[0]));
01274             int_32 rdl = -entry->info.offset;   /* negative offset */
01275             int_32 ril = rdl/sizeof(*pe);
01276 
01277             /*@-sizeoftype@*/
01278             rdl = entry->rdlen;
01279             count = 2 * sizeof(*ei) + (ril * sizeof(*pe)) + rdl;
01280             if (entry->info.tag == HEADER_IMAGE) {
01281                 ril -= 1;
01282                 pe += 1;
01283             } else {
01284                 count += REGION_TAG_COUNT;
01285                 rdl += REGION_TAG_COUNT;
01286             }
01287 
01288             *p = xmalloc(count);
01289             ei = (int_32 *) *p;
01290             ei[0] = htonl(ril);
01291             ei[1] = htonl(rdl);
01292 
01293             /*@-castexpose@*/
01294             pe = (entryInfo) memcpy(ei + 2, pe, (ril * sizeof(*pe)));
01295             /*@=castexpose@*/
01296 
01297             dataStart = (char *) memcpy(pe + ril, dataStart, rdl);
01298             /*@=sizeoftype@*/
01299 
01300             rc = regionSwab(NULL, ril, 0, pe, dataStart, 0);
01301             /* XXX 1 on success. */
01302             rc = (rc < 0) ? 0 : 1;
01303         } else {
01304             count = entry->length;
01305             *p = (!minMem
01306                 ? memcpy(xmalloc(count), entry->data, count)
01307                 : entry->data);
01308         }
01309         break;
01310     case RPM_STRING_TYPE:
01311         if (count == 1) {
01312             *p = entry->data;
01313             break;
01314         }
01315         /*@fallthrough@*/
01316     case RPM_STRING_ARRAY_TYPE:
01317     case RPM_I18NSTRING_TYPE:
01318     {   const char ** ptrEntry;
01319         /*@-sizeoftype@*/
01320         int tableSize = count * sizeof(char *);
01321         /*@=sizeoftype@*/
01322         char * t;
01323         int i;
01324 
01325         /*@-mods@*/
01326         if (minMem) {
01327             *p = xmalloc(tableSize);
01328             ptrEntry = (const char **) *p;
01329             t = entry->data;
01330         } else {
01331             t = xmalloc(tableSize + entry->length);
01332             *p = (void *)t;
01333             ptrEntry = (const char **) *p;
01334             t += tableSize;
01335             memcpy(t, entry->data, entry->length);
01336         }
01337         /*@=mods@*/
01338         for (i = 0; i < count; i++) {
01339             *ptrEntry++ = t;
01340             t = strchr(t, 0);
01341             t++;
01342         }
01343     }   break;
01344 
01345     default:
01346         *p = entry->data;
01347         break;
01348     }
01349     if (type) *type = entry->info.type;
01350     if (c) *c = count;
01351     return rc;
01352 }
01353 
01372 static int headerMatchLocale(const char *td, const char *l, const char *le)
01373         /*@*/
01374 {
01375     const char *fe;
01376 
01377 
01378 #if 0
01379   { const char *s, *ll, *CC, *EE, *dd;
01380     char *lbuf, *t.
01381 
01382     /* Copy the buffer and parse out components on the fly. */
01383     lbuf = alloca(le - l + 1);
01384     for (s = l, ll = t = lbuf; *s; s++, t++) {
01385         switch (*s) {
01386         case '_':
01387             *t = '\0';
01388             CC = t + 1;
01389             break;
01390         case '.':
01391             *t = '\0';
01392             EE = t + 1;
01393             break;
01394         case '@':
01395             *t = '\0';
01396             dd = t + 1;
01397             break;
01398         default:
01399             *t = *s;
01400             break;
01401         }
01402     }
01403 
01404     if (ll)     /* ISO language should be lower case */
01405         for (t = ll; *t; t++)   *t = tolower(*t);
01406     if (CC)     /* ISO country code should be upper case */
01407         for (t = CC; *t; t++)   *t = toupper(*t);
01408 
01409     /* There are a total of 16 cases to attempt to match. */
01410   }
01411 #endif
01412 
01413     /* First try a complete match. */
01414     if (strlen(td) == (le-l) && !strncmp(td, l, (le - l)))
01415         return 1;
01416 
01417     /* Next, try stripping optional dialect and matching.  */
01418     for (fe = l; fe < le && *fe != '@'; fe++)
01419         {};
01420     if (fe < le && !strncmp(td, l, (fe - l)))
01421         return 1;
01422 
01423     /* Next, try stripping optional codeset and matching.  */
01424     for (fe = l; fe < le && *fe != '.'; fe++)
01425         {};
01426     if (fe < le && !strncmp(td, l, (fe - l)))
01427         return 1;
01428 
01429     /* Finally, try stripping optional country code and matching. */
01430     for (fe = l; fe < le && *fe != '_'; fe++)
01431         {};
01432     if (fe < le && !strncmp(td, l, (fe - l)))
01433         return 1;
01434 
01435     return 0;
01436 }
01437 
01444 /*@dependent@*/ /*@exposed@*/ static char *
01445 headerFindI18NString(Header h, indexEntry entry)
01446         /*@*/
01447 {
01448     const char *lang, *l, *le;
01449     indexEntry table;
01450 
01451     /* XXX Drepper sez' this is the order. */
01452     if ((lang = getenv("LANGUAGE")) == NULL &&
01453         (lang = getenv("LC_ALL")) == NULL &&
01454         (lang = getenv("LC_MESSAGES")) == NULL &&
01455         (lang = getenv("LANG")) == NULL)
01456             return entry->data;
01457     
01458     /*@-mods@*/
01459     if ((table = findEntry(h, HEADER_I18NTABLE, RPM_STRING_ARRAY_TYPE)) == NULL)
01460         return entry->data;
01461     /*@=mods@*/
01462 
01463     for (l = lang; *l != '\0'; l = le) {
01464         const char *td;
01465         char *ed;
01466         int langNum;
01467 
01468         while (*l && *l == ':')                 /* skip leading colons */
01469             l++;
01470         if (*l == '\0')
01471             break;
01472         for (le = l; *le && *le != ':'; le++)   /* find end of this locale */
01473             {};
01474 
01475         /* For each entry in the header ... */
01476         for (langNum = 0, td = table->data, ed = entry->data;
01477              langNum < entry->info.count;
01478              langNum++, td += strlen(td) + 1, ed += strlen(ed) + 1) {
01479 
01480                 if (headerMatchLocale(td, l, le))
01481                     return ed;
01482 
01483         }
01484     }
01485 
01486     return entry->data;
01487 }
01488 
01499 static int intGetEntry(Header h, int_32 tag,
01500                 /*@null@*/ /*@out@*/ hTAG_t type,
01501                 /*@null@*/ /*@out@*/ hPTR_t * p,
01502                 /*@null@*/ /*@out@*/ hCNT_t c,
01503                 int minMem)
01504         /*@modifies *type, *p, *c @*/
01505 {
01506     indexEntry entry;
01507     int rc;
01508 
01509     /* First find the tag */
01510     /*@-mods@*/         /*@ FIX: h modified by sort. */
01511     entry = findEntry(h, tag, RPM_NULL_TYPE);
01512     /*@mods@*/
01513     if (entry == NULL) {
01514         if (type) type = 0;
01515         if (p) *p = NULL;
01516         if (c) *c = 0;
01517         return 0;
01518     }
01519 
01520     switch (entry->info.type) {
01521     case RPM_I18NSTRING_TYPE:
01522         rc = 1;
01523         if (type) *type = RPM_STRING_TYPE;
01524         if (c) *c = 1;
01525         /*@-dependenttrans@*/
01526         if (p) *p = headerFindI18NString(h, entry);
01527         /*@=dependenttrans@*/
01528         break;
01529     default:
01530         rc = copyEntry(entry, type, p, c, minMem);
01531         break;
01532     }
01533 
01534     /* XXX 1 on success */
01535     return ((rc == 1) ? 1 : 0);
01536 }
01537 
01545 static /*@null@*/ void * headerFreeTag(/*@unused@*/ Header h,
01546                 /*@only@*/ /*@null@*/ const void * data, rpmTagType type)
01547         /*@modifies data @*/
01548 {
01549     if (data) {
01550         /*@-branchstate@*/
01551         if (type == -1 ||
01552             type == RPM_STRING_ARRAY_TYPE ||
01553             type == RPM_I18NSTRING_TYPE ||
01554             type == RPM_BIN_TYPE)
01555                 data = _free(data);
01556         /*@=branchstate@*/
01557     }
01558     return NULL;
01559 }
01560 
01574 static
01575 int headerGetEntry(Header h, int_32 tag,
01576                         /*@null@*/ /*@out@*/ hTYP_t type,
01577                         /*@null@*/ /*@out@*/ void ** p,
01578                         /*@null@*/ /*@out@*/ hCNT_t c)
01579         /*@modifies *type, *p, *c @*/
01580 {
01581     return intGetEntry(h, tag, type, (hPTR_t *)p, c, 0);
01582 }
01583 
01596 static
01597 int headerGetEntryMinMemory(Header h, int_32 tag,
01598                         /*@null@*/ /*@out@*/ hTYP_t type,
01599                         /*@null@*/ /*@out@*/ hPTR_t * p,
01600                         /*@null@*/ /*@out@*/ hCNT_t c)
01601         /*@modifies *type, *p, *c @*/
01602 {
01603     return intGetEntry(h, tag, type, p, c, 1);
01604 }
01605 
01606 int headerGetRawEntry(Header h, int_32 tag, int_32 * type, hPTR_t * p,
01607                 int_32 * c)
01608 {
01609     indexEntry entry;
01610     int rc;
01611 
01612     if (p == NULL) return headerIsEntry(h, tag);
01613 
01614     /* First find the tag */
01615     /*@-mods@*/         /*@ FIX: h modified by sort. */
01616     entry = findEntry(h, tag, RPM_NULL_TYPE);
01617     /*@=mods@*/
01618     if (!entry) {
01619         if (p) *p = NULL;
01620         if (c) *c = 0;
01621         return 0;
01622     }
01623 
01624     rc = copyEntry(entry, type, p, c, 0);
01625 
01626     /* XXX 1 on success */
01627     return ((rc == 1) ? 1 : 0);
01628 }
01629 
01632 static void copyData(int_32 type, /*@out@*/ void * dstPtr, const void * srcPtr,
01633                 int_32 c, int dataLength)
01634         /*@modifies *dstPtr @*/
01635 {
01636     const char ** src;
01637     char * dst;
01638     int i;
01639 
01640     switch (type) {
01641     case RPM_STRING_ARRAY_TYPE:
01642     case RPM_I18NSTRING_TYPE:
01643         /* Otherwise, p is char** */
01644         i = c;
01645         src = (const char **) srcPtr;
01646         dst = dstPtr;
01647         while (i--) {
01648             if (*src) {
01649                 int len = strlen(*src) + 1;
01650                 memcpy(dst, *src, len);
01651                 dst += len;
01652             }
01653             src++;
01654         }
01655         break;
01656 
01657     default:
01658         memmove(dstPtr, srcPtr, dataLength);
01659         break;
01660     }
01661 }
01662 
01671 static void * grabData(int_32 type, hPTR_t p, int_32 c,
01672                 /*@out@*/ int * lengthPtr)
01673         /*@modifies *lengthPtr @*/
01674 {
01675     int length = dataLength(type, p, c, 0);
01676     void * data = xmalloc(length);
01677 
01678     copyData(type, data, p, c, length);
01679 
01680     if (lengthPtr)
01681         *lengthPtr = length;
01682     return data;
01683 }
01684 
01699 static
01700 int headerAddEntry(Header h, int_32 tag, int_32 type, const void * p, int_32 c)
01701         /*@modifies h @*/
01702 {
01703     indexEntry entry;
01704 
01705     /* Count must always be >= 1 for headerAddEntry. */
01706     if (c <= 0)
01707         return 0;
01708 
01709     /* Allocate more index space if necessary */
01710     if (h->indexUsed == h->indexAlloced) {
01711         h->indexAlloced += INDEX_MALLOC_SIZE;
01712         h->index = xrealloc(h->index, h->indexAlloced * sizeof(*h->index));
01713     }
01714 
01715     /* Fill in the index */
01716     entry = h->index + h->indexUsed;
01717     entry->info.tag = tag;
01718     entry->info.type = type;
01719     entry->info.count = c;
01720     entry->info.offset = 0;
01721     entry->data = grabData(type, p, c, &entry->length);
01722 
01723     if (h->indexUsed > 0 && tag < h->index[h->indexUsed-1].info.tag)
01724         h->flags &= ~HEADERFLAG_SORTED;
01725     h->indexUsed++;
01726 
01727     return 1;
01728 }
01729 
01744 static
01745 int headerAppendEntry(Header h, int_32 tag, int_32 type,
01746                 const void * p, int_32 c)
01747         /*@modifies h @*/
01748 {
01749     indexEntry entry;
01750     int length;
01751 
01752     /* First find the tag */
01753     entry = findEntry(h, tag, type);
01754     if (!entry)
01755         return 0;
01756 
01757     if (type == RPM_STRING_TYPE || type == RPM_I18NSTRING_TYPE) {
01758         /* we can't do this */
01759         return 0;
01760     }
01761 
01762     length = dataLength(type, p, c, 0);
01763 
01764     if (ENTRY_IN_REGION(entry)) {
01765         char * t = xmalloc(entry->length + length);
01766         memcpy(t, entry->data, entry->length);
01767         entry->data = t;
01768         entry->info.offset = 0;
01769     } else
01770         entry->data = xrealloc(entry->data, entry->length + length);
01771 
01772     copyData(type, ((char *) entry->data) + entry->length, p, c, length);
01773 
01774     entry->length += length;
01775 
01776     entry->info.count += c;
01777 
01778     return 1;
01779 }
01780 
01791 static
01792 int headerAddOrAppendEntry(Header h, int_32 tag, int_32 type,
01793                 const void * p, int_32 c)
01794         /*@modifies h @*/
01795 {
01796     return (findEntry(h, tag, type)
01797         ? headerAppendEntry(h, tag, type, p, c)
01798         : headerAddEntry(h, tag, type, p, c));
01799 }
01800 
01821 static
01822 int headerAddI18NString(Header h, int_32 tag, const char * string,
01823                 const char * lang)
01824         /*@modifies h @*/
01825 {
01826     indexEntry table, entry;
01827     const char ** strArray;
01828     int length;
01829     int ghosts;
01830     int i, langNum;
01831     char * buf;
01832 
01833     table = findEntry(h, HEADER_I18NTABLE, RPM_STRING_ARRAY_TYPE);
01834     entry = findEntry(h, tag, RPM_I18NSTRING_TYPE);
01835 
01836     if (!table && entry)
01837         return 0;               /* this shouldn't ever happen!! */
01838 
01839     if (!table && !entry) {
01840         const char * charArray[2];
01841         int count = 0;
01842         if (!lang || (lang[0] == 'C' && lang[1] == '\0')) {
01843             /*@-observertrans -readonlytrans@*/
01844             charArray[count++] = "C";
01845             /*@=observertrans =readonlytrans@*/
01846         } else {
01847             /*@-observertrans -readonlytrans@*/
01848             charArray[count++] = "C";
01849             /*@=observertrans =readonlytrans@*/
01850             charArray[count++] = lang;
01851         }
01852         if (!headerAddEntry(h, HEADER_I18NTABLE, RPM_STRING_ARRAY_TYPE, 
01853                         &charArray, count))
01854             return 0;
01855         table = findEntry(h, HEADER_I18NTABLE, RPM_STRING_ARRAY_TYPE);
01856     }
01857 
01858     if (!table)
01859         return 0;
01860     /*@-branchstate@*/
01861     if (!lang) lang = "C";
01862     /*@=branchstate@*/
01863 
01864     {   const char * l = table->data;
01865         for (langNum = 0; langNum < table->info.count; langNum++) {
01866             if (!strcmp(l, lang)) break;
01867             l += strlen(l) + 1;
01868         }
01869     }
01870 
01871     if (langNum >= table->info.count) {
01872         length = strlen(lang) + 1;
01873         if (ENTRY_IN_REGION(table)) {
01874             char * t = xmalloc(table->length + length);
01875             memcpy(t, table->data, table->length);
01876             table->data = t;
01877             table->info.offset = 0;
01878         } else
01879             table->data = xrealloc(table->data, table->length + length);
01880         memmove(((char *)table->data) + table->length, lang, length);
01881         table->length += length;
01882         table->info.count++;
01883     }
01884 
01885     if (!entry) {
01886         strArray = alloca(sizeof(*strArray) * (langNum + 1));
01887         for (i = 0; i < langNum; i++)
01888             strArray[i] = "";
01889         strArray[langNum] = string;
01890         return headerAddEntry(h, tag, RPM_I18NSTRING_TYPE, strArray, 
01891                                 langNum + 1);
01892     } else if (langNum >= entry->info.count) {
01893         ghosts = langNum - entry->info.count;
01894         
01895         length = strlen(string) + 1 + ghosts;
01896         if (ENTRY_IN_REGION(entry)) {
01897             char * t = xmalloc(entry->length + length);
01898             memcpy(t, entry->data, entry->length);
01899             entry->data = t;
01900             entry->info.offset = 0;
01901         } else
01902             entry->data = xrealloc(entry->data, entry->length + length);
01903 
01904         memset(((char *)entry->data) + entry->length, '\0', ghosts);
01905         memmove(((char *)entry->data) + entry->length + ghosts, string, strlen(string)+1);
01906 
01907         entry->length += length;
01908         entry->info.count = langNum + 1;
01909     } else {
01910         char *b, *be, *e, *ee, *t;
01911         size_t bn, sn, en;
01912 
01913         /* Set beginning/end pointers to previous data */
01914         b = be = e = ee = entry->data;
01915         for (i = 0; i < table->info.count; i++) {
01916             if (i == langNum)
01917                 be = ee;
01918             ee += strlen(ee) + 1;
01919             if (i == langNum)
01920                 e  = ee;
01921         }
01922 
01923         /* Get storage for new buffer */
01924         bn = (be-b);
01925         sn = strlen(string) + 1;
01926         en = (ee-e);
01927         length = bn + sn + en;
01928         t = buf = xmalloc(length);
01929 
01930         /* Copy values into new storage */
01931         memcpy(t, b, bn);
01932         t += bn;
01933 /*@-mayaliasunique@*/
01934         memcpy(t, string, sn);
01935         t += sn;
01936         memcpy(t, e, en);
01937         t += en;
01938 /*@=mayaliasunique@*/
01939 
01940         /* Replace I18N string array */
01941         entry->length -= strlen(be) + 1;
01942         entry->length += sn;
01943         
01944         if (ENTRY_IN_REGION(entry)) {
01945             entry->info.offset = 0;
01946         } else
01947             entry->data = _free(entry->data);
01948         /*@-dependenttrans@*/
01949         entry->data = buf;
01950         /*@=dependenttrans@*/
01951     }
01952 
01953     return 0;
01954 }
01955 
01966 static
01967 int headerModifyEntry(Header h, int_32 tag, int_32 type,
01968                         const void * p, int_32 c)
01969         /*@modifies h @*/
01970 {
01971     indexEntry entry;
01972     void * oldData;
01973 
01974     /* First find the tag */
01975     entry = findEntry(h, tag, type);
01976     if (!entry)
01977         return 0;
01978 
01979     /* make sure entry points to the first occurence of this tag */
01980     while (entry > h->index && (entry - 1)->info.tag == tag)  
01981         entry--;
01982 
01983     /* free after we've grabbed the new data in case the two are intertwined;
01984        that's a bad idea but at least we won't break */
01985     oldData = entry->data;
01986 
01987     entry->info.count = c;
01988     entry->info.type = type;
01989     entry->data = grabData(type, p, c, &entry->length);
01990 
01991     /*@-branchstate@*/
01992     if (ENTRY_IN_REGION(entry)) {
01993         entry->info.offset = 0;
01994     } else
01995         oldData = _free(oldData);
01996     /*@=branchstate@*/
01997 
01998     return 1;
01999 }
02000 
02003 static char escapedChar(const char ch)  /*@*/
02004 {
02005     switch (ch) {
02006     case 'a':   return '\a';
02007     case 'b':   return '\b';
02008     case 'f':   return '\f';
02009     case 'n':   return '\n';
02010     case 'r':   return '\r';
02011     case 't':   return '\t';
02012     case 'v':   return '\v';
02013     default:    return ch;
02014     }
02015 }
02016 
02023 static /*@null@*/ sprintfToken
02024 freeFormat( /*@only@*/ /*@null@*/ sprintfToken format, int num)
02025         /*@modifies *format @*/
02026 {
02027     int i;
02028 
02029     if (format == NULL) return NULL;
02030     for (i = 0; i < num; i++) {
02031         switch (format[i].type) {
02032         case PTOK_ARRAY:
02033             format[i].u.array.format =
02034                 freeFormat(format[i].u.array.format,
02035                         format[i].u.array.numTokens);
02036             /*@switchbreak@*/ break;
02037         case PTOK_COND:
02038             format[i].u.cond.ifFormat =
02039                 freeFormat(format[i].u.cond.ifFormat, 
02040                         format[i].u.cond.numIfTokens);
02041             format[i].u.cond.elseFormat =
02042                 freeFormat(format[i].u.cond.elseFormat, 
02043                         format[i].u.cond.numElseTokens);
02044             /*@switchbreak@*/ break;
02045         case PTOK_NONE:
02046         case PTOK_TAG:
02047         case PTOK_STRING:
02048         default:
02049             /*@switchbreak@*/ break;
02050         }
02051     }
02052     format = _free(format);
02053     return NULL;
02054 }
02055 
02058 static void findTag(char * name, const headerTagTableEntry tags, 
02059                     const headerSprintfExtension extensions,
02060                     /*@out@*/ headerTagTableEntry * tagMatch,
02061                     /*@out@*/ headerSprintfExtension * extMatch)
02062         /*@modifies *tagMatch, *extMatch @*/
02063 {
02064     headerTagTableEntry entry;
02065     headerSprintfExtension ext;
02066     const char * tagname;
02067 
02068     *tagMatch = NULL;
02069     *extMatch = NULL;
02070 
02071     if (strncmp("RPMTAG_", name, sizeof("RPMTAG_")-1)) {
02072         char * t = alloca(strlen(name) + sizeof("RPMTAG_"));
02073         (void) stpcpy( stpcpy(t, "RPMTAG_"), name);
02074         tagname = t;
02075     } else {
02076         tagname = name;
02077     }
02078 
02079     /* Search extensions first to permit overriding header tags. */
02080     ext = extensions;
02081     while (ext->type != HEADER_EXT_LAST) {
02082         if (ext->name != NULL && ext->type == HEADER_EXT_TAG
02083         && !xstrcasecmp(ext->name, tagname))
02084             break;
02085 
02086         if (ext->type == HEADER_EXT_MORE)
02087             ext = ext->u.more;
02088         else
02089             ext++;
02090     }
02091 
02092     if (ext->type == HEADER_EXT_TAG) {
02093         *extMatch = ext;
02094         return;
02095     }
02096 
02097     /* Search header tags. */
02098     for (entry = tags; entry->name; entry++)
02099         if (entry->name && !xstrcasecmp(entry->name, tagname))
02100             break;
02101 
02102     if (entry->name) {
02103         *tagMatch = entry;
02104         return;
02105     }
02106 }
02107 
02108 /* forward ref */
02109 static int parseExpression(sprintfToken token, char * str, 
02110                 const headerTagTableEntry tags, 
02111                 const headerSprintfExtension extensions,
02112                 /*@out@*/char ** endPtr, /*@null@*/ /*@out@*/ errmsg_t * errmsg)
02113         /*@modifies str, *str, *token, *endPtr, *errmsg @*/;
02114 
02117 static int parseFormat(char * str, const headerTagTableEntry tags,
02118                 const headerSprintfExtension extensions,
02119                 /*@out@*/sprintfToken * formatPtr, /*@out@*/int * numTokensPtr,
02120                 /*@null@*/ /*@out@*/ char ** endPtr, int state,
02121                 /*@null@*/ /*@out@*/ errmsg_t * errmsg)
02122         /*@modifies str, *str, *formatPtr, *numTokensPtr, *endPtr, *errmsg @*/
02123 {
02124     char * chptr, * start, * next, * dst;
02125     sprintfToken format;
02126     int numTokens;
02127     int currToken;
02128     headerTagTableEntry tag;
02129     headerSprintfExtension ext;
02130     int i;
02131     int done = 0;
02132 
02133     /* upper limit on number of individual formats */
02134     numTokens = 0;
02135     for (chptr = str; *chptr != '\0'; chptr++)
02136         if (*chptr == '%') numTokens++;
02137     numTokens = numTokens * 2 + 1;
02138 
02139     format = xcalloc(numTokens, sizeof(*format));
02140     if (endPtr) *endPtr = NULL;
02141 
02142     /*@-infloops@*/ /* LCL: can't detect done termination */
02143     dst = start = str;
02144     currToken = -1;
02145     while (*start != '\0') {
02146         switch (*start) {
02147         case '%':
02148             /* handle %% */
02149             if (*(start + 1) == '%') {
02150                 if (currToken < 0 || format[currToken].type != PTOK_STRING) {
02151                     currToken++;
02152                     format[currToken].type = PTOK_STRING;
02153                     /*@-temptrans -assignexpose@*/
02154                     dst = format[currToken].u.string.string = start;
02155                     /*@=temptrans =assignexpose@*/
02156                 }
02157 
02158                 start++;
02159 
02160                 *dst++ = *start++;
02161 
02162                 /*@switchbreak@*/ break;
02163             } 
02164 
02165             currToken++;
02166             *dst++ = '\0';
02167             start++;
02168 
02169             if (*start == '|') {
02170                 char * newEnd;
02171 
02172                 start++;
02173                 if (parseExpression(format + currToken, start, tags, 
02174                                     extensions, &newEnd, errmsg))
02175                 {
02176                     format = freeFormat(format, numTokens);
02177                     return 1;
02178                 }
02179                 start = newEnd;
02180                 /*@switchbreak@*/ break;
02181             }
02182 
02183             /*@-assignexpose@*/
02184             format[currToken].u.tag.format = start;
02185             /*@=assignexpose@*/
02186             format[currToken].u.tag.pad = 0;
02187             format[currToken].u.tag.justOne = 0;
02188             format[currToken].u.tag.arrayCount = 0;
02189 
02190             chptr = start;
02191             while (*chptr && *chptr != '{' && *chptr != '%') chptr++;
02192             if (!*chptr || *chptr == '%') {
02193                 /*@-observertrans -readonlytrans@*/
02194                 if (errmsg) *errmsg = _("missing { after %");
02195                 /*@=observertrans =readonlytrans@*/
02196                 format = freeFormat(format, numTokens);
02197                 return 1;
02198             }
02199 
02200             *chptr++ = '\0';
02201 
02202             while (start < chptr) {
02203                 if (xisdigit(*start)) {
02204                     i = strtoul(start, &start, 10);
02205                     format[currToken].u.tag.pad += i;
02206                 } else {
02207                     start++;
02208                 }
02209             }
02210 
02211             if (*start == '=') {
02212                 format[currToken].u.tag.justOne = 1;
02213                 start++;
02214             } else if (*start == '#') {
02215                 format[currToken].u.tag.justOne = 1;
02216                 format[currToken].u.tag.arrayCount = 1;
02217                 start++;
02218             }
02219 
02220             next = start;
02221             while (*next && *next != '}') next++;
02222             if (!*next) {
02223                 /*@-observertrans -readonlytrans@*/
02224                 if (errmsg) *errmsg = _("missing } after %{");
02225                 /*@=observertrans =readonlytrans@*/
02226                 format = freeFormat(format, numTokens);
02227                 return 1;
02228             }
02229             *next++ = '\0';
02230 
02231             chptr = start;
02232             while (*chptr && *chptr != ':') chptr++;
02233 
02234             if (*chptr != '\0') {
02235                 *chptr++ = '\0';
02236                 if (!*chptr) {
02237                     /*@-observertrans -readonlytrans@*/
02238                     if (errmsg) *errmsg = _("empty tag format");
02239                     /*@=observertrans =readonlytrans@*/
02240                     format = freeFormat(format, numTokens);
02241                     return 1;
02242                 }
02243                 /*@-assignexpose@*/
02244                 format[currToken].u.tag.type = chptr;
02245                 /*@=assignexpose@*/
02246             } else {
02247                 format[currToken].u.tag.type = NULL;
02248             }
02249             
02250             if (!*start) {
02251                 /*@-observertrans -readonlytrans@*/
02252                 if (errmsg) *errmsg = _("empty tag name");
02253                 /*@=observertrans =readonlytrans@*/
02254                 format = freeFormat(format, numTokens);
02255                 return 1;
02256             }
02257 
02258             i = 0;
02259             findTag(start, tags, extensions, &tag, &ext);
02260 
02261             if (tag) {
02262                 format[currToken].u.tag.ext = NULL;
02263                 format[currToken].u.tag.tag = tag->val;
02264             } else if (ext) {
02265                 format[currToken].u.tag.ext = ext->u.tagFunction;
02266                 format[currToken].u.tag.extNum = ext - extensions;
02267             } else {
02268                 /*@-observertrans -readonlytrans@*/
02269                 if (errmsg) *errmsg = _("unknown tag");
02270                 /*@=observertrans =readonlytrans@*/
02271                 format = freeFormat(format, numTokens);
02272                 return 1;
02273             }
02274 
02275             format[currToken].type = PTOK_TAG;
02276 
02277             start = next;
02278 
02279             /*@switchbreak@*/ break;
02280 
02281         case '[':
02282             *dst++ = '\0';
02283             *start++ = '\0';
02284             currToken++;
02285 
02286             if (parseFormat(start, tags, extensions, 
02287                             &format[currToken].u.array.format,
02288                             &format[currToken].u.array.numTokens,
02289                             &start, PARSER_IN_ARRAY, errmsg)) {
02290                 format = freeFormat(format, numTokens);
02291                 return 1;
02292             }
02293 
02294             if (!start) {
02295                 /*@-observertrans -readonlytrans@*/
02296                 if (errmsg) *errmsg = _("] expected at end of array");
02297                 /*@=observertrans =readonlytrans@*/
02298                 format = freeFormat(format, numTokens);
02299                 return 1;
02300             }
02301 
02302             dst = start;
02303 
02304             format[currToken].type = PTOK_ARRAY;
02305 
02306             /*@switchbreak@*/ break;
02307 
02308         case ']':
02309         case '}':
02310             if ((*start == ']' && state != PARSER_IN_ARRAY) ||
02311                 (*start == '}' && state != PARSER_IN_EXPR)) {
02312                 if (*start == ']') {
02313                     /*@-observertrans -readonlytrans@*/
02314                     if (errmsg) *errmsg = _("unexpected ]");
02315                     /*@=observertrans =readonlytrans@*/
02316                 } else {
02317                     /*@-observertrans -readonlytrans@*/
02318                     if (errmsg) *errmsg = _("unexpected }");
02319                     /*@=observertrans =readonlytrans@*/
02320                 }
02321                 format = freeFormat(format, numTokens);
02322                 return 1;
02323             }
02324             *start++ = '\0';
02325             if (endPtr) *endPtr = start;
02326             done = 1;
02327             /*@switchbreak@*/ break;
02328 
02329         default:
02330             if (currToken < 0 || format[currToken].type != PTOK_STRING) {
02331                 currToken++;
02332                 format[currToken].type = PTOK_STRING;
02333                 /*@-temptrans -assignexpose@*/
02334                 dst = format[currToken].u.string.string = start;
02335                 /*@=temptrans =assignexpose@*/
02336             }
02337 
02338             if (*start == '\\') {
02339                 start++;
02340                 *dst++ = escapedChar(*start++);
02341             } else {
02342                 *dst++ = *start++;
02343             }
02344             /*@switchbreak@*/ break;
02345         }
02346         if (done)
02347             break;
02348     }
02349     /*@=infloops@*/
02350 
02351     *dst = '\0';
02352 
02353     currToken++;
02354     for (i = 0; i < currToken; i++) {
02355         if (format[i].type == PTOK_STRING)
02356             format[i].u.string.len = strlen(format[i].u.string.string);
02357     }
02358 
02359     *numTokensPtr = currToken;
02360     *formatPtr = format;
02361 
02362     return 0;
02363 }
02364 
02367 static int parseExpression(sprintfToken token, char * str, 
02368                 const headerTagTableEntry tags, 
02369                 const headerSprintfExtension extensions,
02370                 /*@out@*/ char ** endPtr,
02371                 /*@null@*/ /*@out@*/ errmsg_t * errmsg)
02372 {
02373     headerTagTableEntry tag;
02374     headerSprintfExtension ext;
02375     char * chptr;
02376     char * end;
02377 
02378     if (errmsg) *errmsg = NULL;
02379     chptr = str;
02380     while (*chptr && *chptr != '?') chptr++;
02381 
02382     if (*chptr != '?') {
02383         /*@-observertrans -readonlytrans@*/
02384         if (errmsg) *errmsg = _("? expected in expression");
02385         /*@=observertrans =readonlytrans@*/
02386         return 1;
02387     }
02388 
02389     *chptr++ = '\0';;
02390 
02391     if (*chptr != '{') {
02392         /*@-observertrans -readonlytrans@*/
02393         if (errmsg) *errmsg = _("{ expected after ? in expression");
02394         /*@=observertrans =readonlytrans@*/
02395         return 1;
02396     }
02397 
02398     chptr++;
02399 
02400     if (parseFormat(chptr, tags, extensions, &token->u.cond.ifFormat, 
02401                     &token->u.cond.numIfTokens, &end, PARSER_IN_EXPR, errmsg)) 
02402         return 1;
02403 
02404     if (!*end) {
02405         /*@-observertrans -readonlytrans@*/
02406         if (errmsg) *errmsg = _("} expected in expression");
02407         /*@=observertrans =readonlytrans@*/
02408         token->u.cond.ifFormat =
02409                 freeFormat(token->u.cond.ifFormat, token->u.cond.numIfTokens);
02410         return 1;
02411     }
02412 
02413     chptr = end;
02414     if (*chptr != ':' && *chptr != '|') {
02415         /*@-observertrans -readonlytrans@*/
02416         if (errmsg) *errmsg = _(": expected following ? subexpression");
02417         /*@=observertrans =readonlytrans@*/
02418         token->u.cond.ifFormat =
02419                 freeFormat(token->u.cond.ifFormat, token->u.cond.numIfTokens);
02420         return 1;
02421     }
02422 
02423     if (*chptr == '|') {
02424         (void) parseFormat(xstrdup(""), tags, extensions,
02425                         &token->u.cond.elseFormat, 
02426                         &token->u.cond.numElseTokens, &end, PARSER_IN_EXPR, 
02427                         errmsg);
02428     } else {
02429         chptr++;
02430 
02431         if (*chptr != '{') {
02432             /*@-observertrans -readonlytrans@*/
02433             if (errmsg) *errmsg = _("{ expected after : in expression");
02434             /*@=observertrans =readonlytrans@*/
02435             token->u.cond.ifFormat =
02436                 freeFormat(token->u.cond.ifFormat, token->u.cond.numIfTokens);
02437             return 1;
02438         }
02439 
02440         chptr++;
02441 
02442         if (parseFormat(chptr, tags, extensions, &token->u.cond.elseFormat, 
02443                         &token->u.cond.numElseTokens, &end, PARSER_IN_EXPR, 
02444                         errmsg)) 
02445             return 1;
02446         if (!*end) {
02447             /*@-observertrans -readonlytrans@*/
02448             if (errmsg) *errmsg = _("} expected in expression");
02449             /*@=observertrans =readonlytrans@*/
02450             token->u.cond.ifFormat =
02451                 freeFormat(token->u.cond.ifFormat, token->u.cond.numIfTokens);
02452             return 1;
02453         }
02454 
02455         chptr = end;
02456         if (*chptr != '|') {
02457             /*@-observertrans -readonlytrans@*/
02458             if (errmsg) *errmsg = _("| expected at end of expression");
02459             /*@=observertrans =readonlytrans@*/
02460             token->u.cond.ifFormat =
02461                 freeFormat(token->u.cond.ifFormat, token->u.cond.numIfTokens);
02462             token->u.cond.elseFormat =
02463                 freeFormat(token->u.cond.elseFormat, token->u.cond.numElseTokens);
02464             return 1;
02465         }
02466     }
02467         
02468     chptr++;
02469 
02470     *endPtr = chptr;
02471 
02472     findTag(str, tags, extensions, &tag, &ext);
02473 
02474     if (tag) {
02475         token->u.cond.tag.ext = NULL;
02476         token->u.cond.tag.tag = tag->val;
02477     } else if (ext) {
02478         token->u.cond.tag.ext = ext->u.tagFunction;
02479         token->u.cond.tag.extNum = ext - extensions;
02480     } else {
02481         token->u.cond.tag.ext = NULL;
02482         token->u.cond.tag.tag = -1;
02483     }
02484         
02485     token->type = PTOK_COND;
02486 
02487     return 0;
02488 }
02489 
02493 static int getExtension(Header h, headerTagTagFunction fn,
02494                 /*@out@*/ hTYP_t typeptr,
02495                 /*@out@*/ hPTR_t * data,
02496                 /*@out@*/ hCNT_t countptr,
02497                 extensionCache ext)
02498         /*@modifies *typeptr, *data, *countptr, ext @*/
02499 {
02500     if (!ext->avail) {
02501         if (fn(h, &ext->type, &ext->data, &ext->count, &ext->freeit))
02502             return 1;
02503         ext->avail = 1;
02504     }
02505 
02506     if (typeptr) *typeptr = ext->type;
02507     if (data) *data = ext->data;
02508     if (countptr) *countptr = ext->count;
02509 
02510     return 0;
02511 }
02512 
02515 static char * formatValue(sprintfTag tag, Header h, 
02516                 const headerSprintfExtension extensions,
02517                 extensionCache extCache, int element)
02518         /*@modifies extCache @*/
02519 {
02520     int len;
02521     char buf[20];
02522     int_32 count, type;
02523     hPTR_t data;
02524     unsigned int intVal;
02525     char * val = NULL;
02526     const char ** strarray;
02527     int mayfree = 0;
02528     int countBuf;
02529     headerTagFormatFunction tagtype = NULL;
02530     headerSprintfExtension ext;
02531 
02532     memset(buf, 0, sizeof(buf));
02533     /*@-branchstate@*/
02534     if (tag->ext) {
02535         if (getExtension(h, tag->ext, &type, &data, &count, 
02536                          extCache + tag->extNum))
02537         {
02538             count = 1;
02539             type = RPM_STRING_TYPE;     
02540             data = "(none)";            /* XXX i18n? NO!, sez; gafton */
02541         }
02542     } else {
02543         if (!headerGetEntry(h, tag->tag, &type, (void **)&data, &count)) {
02544             count = 1;
02545             type = RPM_STRING_TYPE;     
02546             data = "(none)";            /* XXX i18n? NO!, sez; gafton */
02547         }
02548 
02549         mayfree = 1;
02550     }
02551     /*@=branchstate@*/
02552 
02553     if (tag->arrayCount) {
02554         /*@-observertrans -modobserver@*/
02555         data = headerFreeData(data, type);
02556         /*@=observertrans =modobserver@*/
02557 
02558         countBuf = count;
02559         data = &countBuf;
02560         count = 1;
02561         type = RPM_INT32_TYPE;
02562     }
02563 
02564     (void) stpcpy( stpcpy(buf, "%"), tag->format);
02565 
02566     if (tag->type) {
02567         ext = extensions;
02568         while (ext->type != HEADER_EXT_LAST) {
02569             if (ext->name != NULL && ext->type == HEADER_EXT_FORMAT
02570             && !strcmp(ext->name, tag->type))
02571             {
02572                 tagtype = ext->u.formatFunction;
02573                 break;
02574             }
02575 
02576             if (ext->type == HEADER_EXT_MORE)
02577                 ext = ext->u.more;
02578             else
02579                 ext++;
02580         }
02581     }
02582     
02583     /*@-branchstate@*/
02584     switch (type) {
02585     case RPM_STRING_ARRAY_TYPE:
02586         strarray = (const char **)data;
02587 
02588         if (tagtype)
02589             val = tagtype(RPM_STRING_TYPE, strarray[element], buf, tag->pad, 0);
02590 
02591         if (!val) {
02592             strcat(buf, "s");
02593 
02594             len = strlen(strarray[element]) + tag->pad + 20;
02595             val = xmalloc(len);
02596             /*@-formatconst@*/
02597             sprintf(val, buf, strarray[element]);
02598             /*@=formatconst@*/
02599         }
02600 
02601         /*@-observertrans -modobserver@*/
02602         if (mayfree) data = _free(data);
02603         /*@=observertrans =modobserver@*/
02604 
02605         break;
02606 
02607     case RPM_STRING_TYPE:
02608         if (tagtype)
02609             val = tagtype(RPM_STRING_ARRAY_TYPE, data, buf, tag->pad,  0);
02610 
02611         if (!val) {
02612             strcat(buf, "s");
02613 
02614             len = strlen(data) + tag->pad + 20;
02615             val = xmalloc(len);
02616             /*@-formatconst@*/
02617             sprintf(val, buf, data);
02618             /*@=formatconst@*/
02619         }
02620         break;
02621 
02622     case RPM_CHAR_TYPE:
02623     case RPM_INT8_TYPE:
02624     case RPM_INT16_TYPE:
02625     case RPM_INT32_TYPE:
02626         switch (type) {
02627         case RPM_CHAR_TYPE:     
02628         case RPM_INT8_TYPE:
02629             intVal = *(((int_8 *) data) + element);
02630             /*@innerbreak@*/ break;
02631         case RPM_INT16_TYPE:
02632             intVal = *(((uint_16 *) data) + element);
02633             /*@innerbreak@*/ break;
02634         default:                /* keep -Wall quiet */
02635         case RPM_INT32_TYPE:
02636             intVal = *(((int_32 *) data) + element);
02637             /*@innerbreak@*/ break;
02638         }
02639 
02640         if (tagtype)
02641             val = tagtype(RPM_INT32_TYPE, &intVal, buf, tag->pad,  element);
02642 
02643         if (!val) {
02644             strcat(buf, "d");
02645             len = 10 + tag->pad + 20;
02646             val = xmalloc(len);
02647             /*@-formatconst@*/
02648             sprintf(val, buf, intVal);
02649             /*@=formatconst@*/
02650         }
02651         break;
02652 
02653     case RPM_BIN_TYPE:
02654         if (tagtype)
02655             val = tagtype(RPM_BIN_TYPE, data, buf, tag->pad, count);
02656 
02657         if (!val) {
02658 #ifdef  NOTYET
02659             val = memcpy(xmalloc(count), data, count);
02660 #else
02661             /* XXX format string not used */
02662             static char hex[] = "0123456789abcdef";
02663             const char * s = data;
02664             char * t;
02665 
02666             strcat(buf, "s");
02667             len = 2*count + tag->pad + 1;
02668             val = t = xmalloc(len);
02669             while (count-- > 0) {
02670                 unsigned int i;
02671                 i = *s++;
02672                 *t++ = hex[ (i >> 4) & 0xf ];
02673                 *t++ = hex[ (i     ) & 0xf ];
02674             }
02675             *t = '\0';
02676 #endif
02677         }
02678         break;
02679 
02680     default:
02681         val = xstrdup(_("(unknown type)"));
02682         break;
02683     }
02684     /*@=branchstate@*/
02685 
02686     return val;
02687 }
02688 
02691 static const char * singleSprintf(Header h, sprintfToken token,
02692                 const headerSprintfExtension extensions,
02693                 extensionCache extCache, int element)
02694         /*@modifies h, extCache @*/
02695 {
02696     char * val;
02697     const char * thisItem;
02698     int thisItemLen;
02699     int len, alloced;
02700     int i, j;
02701     int numElements;
02702     int type;
02703     sprintfToken condFormat;
02704     int condNumFormats;
02705 
02706     /* we assume the token and header have been validated already! */
02707 
02708     switch (token->type) {
02709     case PTOK_NONE:
02710         break;
02711 
02712     case PTOK_STRING:
02713         val = xmalloc(token->u.string.len + 1);
02714         strcpy(val, token->u.string.string);
02715         break;
02716 
02717     case PTOK_TAG:
02718         val = formatValue(&token->u.tag, h, extensions, extCache,
02719                           token->u.tag.justOne ? 0 : element);
02720         break;
02721 
02722     case PTOK_COND:
02723         if (token->u.cond.tag.ext ||
02724             headerIsEntry(h, token->u.cond.tag.tag)) {
02725             condFormat = token->u.cond.ifFormat;
02726             condNumFormats = token->u.cond.numIfTokens;
02727         } else {
02728             condFormat = token->u.cond.elseFormat;
02729             condNumFormats = token->u.cond.numElseTokens;
02730         }
02731 
02732         alloced = condNumFormats * 20;
02733         val = xmalloc(alloced ? alloced : 1);
02734         *val = '\0';
02735         len = 0;
02736 
02737         if (condFormat)
02738         for (i = 0; i < condNumFormats; i++) {
02739             thisItem = singleSprintf(h, condFormat + i, 
02740                                      extensions, extCache, element);
02741             thisItemLen = strlen(thisItem);
02742             if ((thisItemLen + len) >= alloced) {
02743                 alloced = (thisItemLen + len) + 200;
02744                 val = xrealloc(val, alloced);   
02745             }
02746             strcat(val, thisItem);
02747             len += thisItemLen;
02748             thisItem = _free(thisItem);
02749         }
02750 
02751         break;
02752 
02753     case PTOK_ARRAY:
02754         numElements = -1;
02755         for (i = 0; i < token->u.array.numTokens; i++) {
02756             if (token->u.array.format[i].type != PTOK_TAG ||
02757                 token->u.array.format[i].u.tag.arrayCount ||
02758                 token->u.array.format[i].u.tag.justOne) continue;
02759 
02760             if (token->u.array.format[i].u.tag.ext) {
02761                 const void * data;
02762                 if (getExtension(h, token->u.array.format[i].u.tag.ext,
02763                                  &type, &data, &numElements, 
02764                                  extCache + 
02765                                    token->u.array.format[i].u.tag.extNum))
02766                      continue;
02767             } else {
02768                 if (!headerGetEntry(h, token->u.array.format[i].u.tag.tag, 
02769                                     &type, (void **) &val, &numElements))
02770                     continue;
02771                 val = headerFreeData(val, type);
02772             } 
02773             /*@loopbreak@*/ break;
02774         }
02775 
02776         if (numElements == -1) {
02777             val = xstrdup("(none)");    /* XXX i18n? NO!, sez; gafton */
02778         } else {
02779             alloced = numElements * token->u.array.numTokens * 20;
02780             val = xmalloc(alloced);
02781             *val = '\0';
02782             len = 0;
02783 
02784             for (j = 0; j < numElements; j++) {
02785                 for (i = 0; i < token->u.array.numTokens; i++) {
02786                     thisItem = singleSprintf(h, token->u.array.format + i, 
02787                                              extensions, extCache, j);
02788                     thisItemLen = strlen(thisItem);
02789                     if ((thisItemLen + len) >= alloced) {
02790                         alloced = (thisItemLen + len) + 200;
02791                         val = xrealloc(val, alloced);   
02792                     }
02793                     strcat(val, thisItem);
02794                     len += thisItemLen;
02795                     thisItem = _free(thisItem);
02796                 }
02797             }
02798         }
02799            
02800         break;
02801     }
02802 
02803     return val;
02804 }
02805 
02808 static /*@only@*/ extensionCache
02809 allocateExtensionCache(const headerSprintfExtension extensions)
02810         /*@*/
02811 {
02812     headerSprintfExtension ext = extensions;
02813     int i = 0;
02814 
02815     while (ext->type != HEADER_EXT_LAST) {
02816         i++;
02817         if (ext->type == HEADER_EXT_MORE)
02818             ext = ext->u.more;
02819         else
02820             ext++;
02821     }
02822 
02823     /*@-sizeoftype@*/
02824     return xcalloc(i, sizeof(struct extensionCache));
02825     /*@=sizeoftype@*/
02826 }
02827 
02831 static /*@null@*/ extensionCache
02832 freeExtensionCache(const headerSprintfExtension extensions,
02833                         /*@only@*/ extensionCache cache)
02834         /*@*/
02835 {
02836     headerSprintfExtension ext = extensions;
02837     int i = 0;
02838 
02839     while (ext->type != HEADER_EXT_LAST) {
02840         if (cache[i].freeit) cache[i].data = _free(cache[i].data);
02841 
02842         i++;
02843         if (ext->type == HEADER_EXT_MORE)
02844             ext = ext->u.more;
02845         else
02846             ext++;
02847     }
02848 
02849     cache = _free(cache);
02850     return NULL;
02851 }
02852 
02864 static /*@only@*/ /*@null@*/
02865 char * headerSprintf(Header h, const char * fmt,
02866                      const struct headerTagTableEntry_s * tbltags,
02867                      const struct headerSprintfExtension_s * extensions,
02868                      /*@null@*/ /*@out@*/ errmsg_t * errmsg)
02869         /*@modifies *errmsg @*/
02870 {
02871     /*@-castexpose@*/   /* FIX: legacy API shouldn't change. */
02872     headerSprintfExtension exts = (headerSprintfExtension) extensions;
02873     headerTagTableEntry tags = (headerTagTableEntry) tbltags;
02874     /*@=castexpose@*/
02875     char * fmtString;
02876     sprintfToken format;
02877     int numTokens;
02878     char * answer;
02879     int answerLength;
02880     int answerAlloced;
02881     int i;
02882     extensionCache extCache;
02883  
02884     /*fmtString = escapeString(fmt);*/
02885     fmtString = xstrdup(fmt);
02886    
02887     if (parseFormat(fmtString, tags, exts, &format, &numTokens, 
02888                     NULL, PARSER_BEGIN, errmsg)) {
02889         fmtString = _free(fmtString);
02890         return NULL;
02891     }
02892 
02893     extCache = allocateExtensionCache(exts);
02894 
02895     answerAlloced = 1024;
02896     answerLength = 0;
02897     answer = xmalloc(answerAlloced);
02898     *answer = '\0';
02899 
02900     for (i = 0; i < numTokens; i++) {
02901         const char * piece;
02902         int pieceLength;
02903 
02904         /*@-mods@*/
02905         piece = singleSprintf(h, format + i, exts, extCache, 0);
02906         /*@=mods@*/
02907         if (piece) {
02908             pieceLength = strlen(piece);
02909             if ((answerLength + pieceLength) >= answerAlloced) {
02910                 while ((answerLength + pieceLength) >= answerAlloced) 
02911                     answerAlloced += 1024;
02912                 answer = xrealloc(answer, answerAlloced);
02913             }
02914 
02915             strcat(answer, piece);
02916             answerLength += pieceLength;
02917             piece = _free(piece);
02918         }
02919     }
02920 
02921     fmtString = _free(fmtString);
02922     extCache = freeExtensionCache(exts, extCache);
02923     format = _free(format);
02924 
02925     return answer;
02926 }
02927 
02930 static char * octalFormat(int_32 type, hPTR_t data, 
02931                 char * formatPrefix, int padding, /*@unused@*/int element)
02932         /*@modifies formatPrefix @*/
02933 {
02934     char * val;
02935 
02936     if (type != RPM_INT32_TYPE) {
02937         val = xstrdup(_("(not a number)"));
02938     } else {
02939         val = xmalloc(20 + padding);
02940         strcat(formatPrefix, "o");
02941         /*@-formatconst@*/
02942         sprintf(val, formatPrefix, *((int_32 *) data));
02943         /*@=formatconst@*/
02944     }
02945 
02946     return val;
02947 }
02948 
02951 static char * hexFormat(int_32 type, hPTR_t data, 
02952                 char * formatPrefix, int padding, /*@unused@*/int element)
02953         /*@modifies formatPrefix @*/
02954 {
02955     char * val;
02956 
02957     if (type != RPM_INT32_TYPE) {
02958         val = xstrdup(_("(not a number)"));
02959     } else {
02960         val = xmalloc(20 + padding);
02961         strcat(formatPrefix, "x");
02962         /*@-formatconst@*/
02963         sprintf(val, formatPrefix, *((int_32 *) data));
02964         /*@=formatconst@*/
02965     }
02966 
02967     return val;
02968 }
02969 
02972 static char * realDateFormat(int_32 type, hPTR_t data, 
02973                 char * formatPrefix, int padding, /*@unused@*/int element,
02974                 const char * strftimeFormat)
02975         /*@modifies formatPrefix @*/
02976 {
02977     char * val;
02978 
02979     if (type != RPM_INT32_TYPE) {
02980         val = xstrdup(_("(not a number)"));
02981     } else {
02982         struct tm * tstruct;
02983         char buf[50];
02984 
02985         val = xmalloc(50 + padding);
02986         strcat(formatPrefix, "s");
02987 
02988         /* this is important if sizeof(int_32) ! sizeof(time_t) */
02989         {   time_t dateint = *((int_32 *) data);
02990             tstruct = localtime(&dateint);
02991         }
02992         buf[0] = '\0';
02993         if (tstruct)
02994             (void) strftime(buf, sizeof(buf) - 1, strftimeFormat, tstruct);
02995         /*@-formatconst@*/
02996         sprintf(val, formatPrefix, buf);
02997         /*@=formatconst@*/
02998     }
02999 
03000     return val;
03001 }
03002 
03005 static char * dateFormat(int_32 type, hPTR_t data, 
03006                          char * formatPrefix, int padding, int element)
03007         /*@modifies formatPrefix @*/
03008 {
03009     return realDateFormat(type, data, formatPrefix, padding, element, "%c");
03010 }
03011 
03014 static char * dayFormat(int_32 type, hPTR_t data, 
03015                          char * formatPrefix, int padding, int element)
03016         /*@modifies formatPrefix @*/
03017 {
03018     return realDateFormat(type, data, formatPrefix, padding, element, 
03019                           "%a %b %d %Y");
03020 }
03021 
03024 static char * shescapeFormat(int_32 type, hPTR_t data, 
03025                 char * formatPrefix, int padding, /*@unused@*/int element)
03026         /*@modifies formatPrefix @*/
03027 {
03028     char * result, * dst, * src, * buf;
03029 
03030     if (type == RPM_INT32_TYPE) {
03031         result = xmalloc(padding + 20);
03032         strcat(formatPrefix, "d");
03033         /*@-formatconst@*/
03034         sprintf(result, formatPrefix, *((int_32 *) data));
03035         /*@=formatconst@*/
03036     } else {
03037         buf = alloca(strlen(data) + padding + 2);
03038         strcat(formatPrefix, "s");
03039         /*@-formatconst@*/
03040         sprintf(buf, formatPrefix, data);
03041         /*@=formatconst@*/
03042 
03043         result = dst = xmalloc(strlen(buf) * 4 + 3);
03044         *dst++ = '\'';
03045         for (src = buf; *src != '\0'; src++) {
03046             if (*src == '\'') {
03047                 *dst++ = '\'';
03048                 *dst++ = '\\';
03049                 *dst++ = '\'';
03050                 *dst++ = '\'';
03051             } else {
03052                 *dst++ = *src;
03053             }
03054         }
03055         *dst++ = '\'';
03056         *dst = '\0';
03057 
03058     }
03059 
03060     return result;
03061 }
03062 
03063 /*@-type@*/ /* FIX: cast? */
03064 const struct headerSprintfExtension_s headerDefaultFormats[] = {
03065     { HEADER_EXT_FORMAT, "octal", { octalFormat } },
03066     { HEADER_EXT_FORMAT, "hex", { hexFormat } },
03067     { HEADER_EXT_FORMAT, "date", { dateFormat } },
03068     { HEADER_EXT_FORMAT, "day", { dayFormat } },
03069     { HEADER_EXT_FORMAT, "shescape", { shescapeFormat } },
03070     { HEADER_EXT_LAST, NULL, { NULL } }
03071 };
03072 /*@=type@*/
03073 
03080 static
03081 void headerCopyTags(Header headerFrom, Header headerTo, hTAG_t tagstocopy)
03082         /*@modifies headerTo @*/
03083 {
03084     int * p;
03085 
03086     if (headerFrom == headerTo)
03087         return;
03088 
03089     for (p = tagstocopy; *p != 0; p++) {
03090         char *s;
03091         int_32 type;
03092         int_32 count;
03093         if (headerIsEntry(headerTo, *p))
03094             continue;
03095         if (!headerGetEntryMinMemory(headerFrom, *p, &type,
03096                                 (hPTR_t *) &s, &count))
03097             continue;
03098         (void) headerAddEntry(headerTo, *p, type, s, count);
03099         s = headerFreeData(s, type);
03100     }
03101 }
03102 
03106 struct headerIteratorS {
03107 /*@unused@*/ Header h;          
03108 /*@unused@*/ int next_index;    
03109 };
03110 
03116 static /*@null@*/
03117 HeaderIterator headerFreeIterator(/*@only@*/ HeaderIterator hi)
03118         /*@modifies hi @*/
03119 {
03120     hi->h = headerFree(hi->h);
03121     hi = _free(hi);
03122     return hi;
03123 }
03124 
03130 static
03131 HeaderIterator headerInitIterator(Header h)
03132         /*@modifies h */
03133 {
03134     HeaderIterator hi = xmalloc(sizeof(*hi));
03135 
03136     headerSort(h);
03137 
03138     hi->h = headerLink(h);
03139     hi->next_index = 0;
03140     return hi;
03141 }
03142 
03152 static
03153 int headerNextIterator(HeaderIterator hi,
03154                 /*@null@*/ /*@out@*/ hTAG_t tag,
03155                 /*@null@*/ /*@out@*/ hTYP_t type,
03156                 /*@null@*/ /*@out@*/ hPTR_t * p,
03157                 /*@null@*/ /*@out@*/ hCNT_t c)
03158         /*@modifies hi, *tag, *type, *p, *c @*/
03159 {
03160     Header h = hi->h;
03161     int slot = hi->next_index;
03162     indexEntry entry = NULL;
03163     int rc;
03164 
03165     for (slot = hi->next_index; slot < h->indexUsed; slot++) {
03166         entry = h->index + slot;
03167         if (!ENTRY_IS_REGION(entry))
03168             break;
03169     }
03170     hi->next_index = slot;
03171     if (entry == NULL || slot >= h->indexUsed)
03172         return 0;
03173     /*@-noeffect@*/     /* LCL: no clue */
03174     hi->next_index++;
03175     /*@=noeffect@*/
03176 
03177     if (tag)
03178         *tag = entry->info.tag;
03179 
03180     rc = copyEntry(entry, type, p, c, 0);
03181 
03182     /* XXX 1 on success */
03183     return ((rc == 1) ? 1 : 0);
03184 }
03185 
03191 static /*@null@*/
03192 Header headerCopy(Header h)
03193         /*@modifies h @*/
03194 {
03195     Header nh = headerNew();
03196     HeaderIterator hi;
03197     int_32 tag, type, count;
03198     hPTR_t ptr;
03199    
03200     /*@-branchstate@*/
03201     for (hi = headerInitIterator(h);
03202         headerNextIterator(hi, &tag, &type, &ptr, &count);
03203         ptr = headerFreeData((void *)ptr, type))
03204     {
03205         if (ptr) (void) headerAddEntry(nh, tag, type, ptr, count);
03206     }
03207     hi = headerFreeIterator(hi);
03208     /*@=branchstate@*/
03209 
03210     return headerReload(nh, HEADER_IMAGE);
03211 }
03212 
03213 /*@observer@*/ /*@unchecked@*/
03214 static struct HV_s hdrVec1 = {
03215     headerNew,
03216     headerFree,
03217     headerLink,
03218     headerSort,
03219     headerUnsort,
03220     headerSizeof,
03221     headerUnload,
03222     headerReload,
03223     headerCopy,
03224     headerLoad,
03225     headerCopyLoad,
03226     headerRead,
03227     headerWrite,
03228     headerIsEntry,
03229     headerFreeTag,
03230     headerGetEntry,
03231     headerGetEntryMinMemory,
03232     headerAddEntry,
03233     headerAppendEntry,
03234     headerAddOrAppendEntry,
03235     headerAddI18NString,
03236     headerModifyEntry,
03237     headerRemoveEntry,
03238     headerSprintf,
03239     headerCopyTags,
03240     headerFreeIterator,
03241     headerInitIterator,
03242     headerNextIterator,
03243     headerUnlink,
03244     NULL, NULL,
03245     1
03246 };
03247 
03248 /*@-compmempass -redef@*/
03249 /*@observer@*/ /*@unchecked@*/
03250 HV_t hdrVec = &hdrVec1;
03251 /*@=compmempass =redef@*/

Generated on Thu Feb 14 07:34:58 2008 for rpm by  doxygen 1.5.2