Main Page   Modules   Data Structures   File List   Data Fields   Globals   Related Pages  

lib/header.c

Go to the documentation of this file.
00001 
00005 #undef  REMALLOC_HEADER_REGION
00006 #define _DEBUG_SWAB 1
00007 #define _DEBUG_INDEX 1
00008 
00009 /* RPM - Copyright (C) 1995-2000 Red Hat Software */
00010 
00011 /* Data written to file descriptors is in network byte order.    */
00012 /* Data read from file descriptors is expected to be in          */
00013 /* network byte order and is converted on the fly to host order. */
00014 
00015 #include "system.h"
00016 
00017 #if !defined(__LCLINT__)
00018 #include <netinet/in.h>
00019 #endif  /* __LCLINT__ */
00020 
00021 #include <header.h>
00022 
00023 #include "debug.h"
00024 
00025 /* XXX avoid rpmlib.h, need for debugging. */
00026 /*@observer@*/ const char *const tagName(int tag)       /*@*/;
00027 
00028 /*
00029  * Teach header.c about legacy tags.
00030  */
00031 #define HEADER_OLDFILENAMES     1027
00032 #define HEADER_BASENAMES        1117
00033 
00034 #define INDEX_MALLOC_SIZE 8
00035 
00036 #define PARSER_BEGIN    0
00037 #define PARSER_IN_ARRAY 1
00038 #define PARSER_IN_EXPR  2
00039 
00040 static unsigned char header_magic[8] = {
00041         0x8e, 0xad, 0xe8, 0x01, 0x00, 0x00, 0x00, 0x00
00042 };
00043 
00047 static int typeSizes[] =  { 
00048         0,      
00049         1,      
00050         1,      
00051         2,      
00052         4,      
00053         -1,     
00054         -1,     
00055         1,      
00056         -1,     
00057         -1      
00058 };
00059 
00063 struct entryInfo {
00064     int_32 tag;                 
00065     int_32 type;                
00066     int_32 offset;              
00067     int_32 count;               
00068 };
00069 
00070 #define REGION_TAG_TYPE         RPM_BIN_TYPE
00071 #define REGION_TAG_COUNT        sizeof(struct entryInfo)
00072 
00073 #define ENTRY_IS_REGION(_e)     ((_e)->info.tag < HEADER_I18NTABLE)
00074 #define ENTRY_IN_REGION(_e)     ((_e)->info.offset < 0)
00075 
00079 struct indexEntry {
00080     struct entryInfo info;      
00081 /*@owned@*/ void * data;        
00082     int length;                 
00083     int rdlen;                  
00084 };
00085 
00089 struct headerToken {
00090 /*@owned@*/ struct indexEntry *index;   
00091     int indexUsed;              
00092     int indexAlloced;           
00093     int region_allocated;       
00094     int sorted;                 
00095     int legacy;                 
00096 /*@refs@*/ int nrefs;   
00097 };
00098 
00101 struct sprintfTag {
00102     headerTagTagFunction ext;   
00103     int extNum;
00104     int_32 tag;
00105     int justOne;
00106     int arrayCount;
00107 /*@kept@*/ char * format;
00108 /*@kept@*/ char * type;
00109     int pad;
00110 };
00111 
00114 struct extensionCache {
00115     int_32 type;
00116     int_32 count;
00117     int avail;
00118     int freeit;
00119 /*@owned@*/ const void * data;
00120 };
00121 
00124 struct sprintfToken {
00125     enum {
00126         PTOK_NONE = 0,
00127         PTOK_TAG,
00128         PTOK_ARRAY,
00129         PTOK_STRING,
00130         PTOK_COND
00131     } type;
00132     union {
00133         struct {
00134             /*@only@*/ struct sprintfToken * format;
00135             int numTokens;
00136         } array;
00137         struct sprintfTag tag;
00138         struct {
00139             /*@dependent@*/ char * string;
00140             int len;
00141         } string;
00142         struct {
00143             /*@only@*/ struct sprintfToken * ifFormat;
00144             int numIfTokens;
00145             /*@only@*/ struct sprintfToken * elseFormat;
00146             int numElseTokens;
00147             struct sprintfTag tag;
00148         } cond;
00149     } u;
00150 };
00151 
00160 static int dataLength(int_32 type, const void * p, int_32 count, int onDisk)
00161         /*@*/
00162 {
00163     int length = 0;
00164 
00165     switch (type) {
00166     case RPM_STRING_TYPE:
00167         if (count == 1) {       /* Special case -- p is just the string */
00168             length = strlen(p) + 1;
00169             break;
00170         }
00171         /* This should not be allowed */
00172         fprintf(stderr, _("dataLength() RPM_STRING_TYPE count must be 1.\n"));
00173         exit(EXIT_FAILURE);
00174         /*@notreached@*/ break;
00175 
00176     case RPM_STRING_ARRAY_TYPE:
00177     case RPM_I18NSTRING_TYPE:
00178     {   int i;
00179 
00180         /* This is like RPM_STRING_TYPE, except it's *always* an array */
00181         /* Compute sum of length of all strings, including null terminators */
00182         i = count;
00183 
00184         if (onDisk) {
00185             const char * chptr = p;
00186             int thisLen;
00187 
00188             while (i--) {
00189                 thisLen = strlen(chptr) + 1;
00190                 length += thisLen;
00191                 chptr += thisLen;
00192             }
00193         } else {
00194             const char ** src = (const char **)p;
00195             while (i--) {
00196                 /* add one for null termination */
00197                 length += strlen(*src++) + 1;
00198             }
00199         }
00200     }   break;
00201 
00202     default:
00203         if (typeSizes[type] != -1) {
00204             length = typeSizes[type] * count;
00205             break;
00206         }
00207         fprintf(stderr, _("Data type %d not supported\n"), (int) type);
00208         exit(EXIT_FAILURE);
00209         /*@notreached@*/ break;
00210     }
00211 
00212     return length;
00213 }
00214 
00240 static int regionSwab(struct indexEntry * entry, int il, int dl,
00241                 const struct entryInfo * pe, char * dataStart, int regionid)
00242 {
00243     char * tprev = NULL;
00244     char * t = NULL;
00245     int tdel, tl = dl;
00246 
00247     for (; il > 0; il--, pe++) {
00248         struct indexEntry ie;
00249         int_32 type;
00250 
00251         ie.info.tag = ntohl(pe->tag);
00252         ie.info.type = ntohl(pe->type);
00253         ie.info.count = ntohl(pe->count);
00254         ie.info.offset = ntohl(pe->offset);
00255         ie.data = t = dataStart + ie.info.offset;
00256         ie.length = dataLength(ie.info.type, ie.data, ie.info.count, 1);
00257         ie.rdlen = 0;
00258 
00259 assert(ie.info.type >= RPM_MIN_TYPE && ie.info.type <= RPM_MAX_TYPE);
00260 
00261         if (entry) {
00262             ie.info.offset = regionid;
00263             *entry = ie;        /* structure assignment */
00264             entry++;
00265         }
00266 
00267         /* Alignment */
00268         type = ie.info.type;
00269         if (typeSizes[type] > 1) {
00270             unsigned diff;
00271             diff = typeSizes[type] - (dl % typeSizes[type]);
00272             if (diff != typeSizes[type]) {
00273                 dl += diff;
00274             }
00275         }
00276         tdel = (tprev ? (t - tprev) : 0);
00277         dl += ie.length;
00278         tl += tdel;
00279         tprev = (ie.info.tag < HEADER_I18NTABLE)
00280                 ? dataStart : t;
00281 
00282         /* Perform endian conversions */
00283         switch (ntohl(pe->type)) {
00284         case RPM_INT32_TYPE:
00285         {   int_32 * it = (int_32 *)t;
00286             for (; ie.info.count > 0; ie.info.count--, it += 1)
00287                 *it = htonl(*it);
00288             t = (char *) it;
00289         }   break;
00290         case RPM_INT16_TYPE:
00291         {   int_16 * it = (int_16 *) t;
00292             for (; ie.info.count > 0; ie.info.count--, it += 1)
00293                 *it = htons(*it);
00294             t = (char *) it;
00295         }   break;
00296         default:
00297             t += ie.length;
00298             break;
00299         }
00300     }
00301     tdel = (tprev ? (t - tprev) : 0);
00302     tl += tdel;
00303     if (tl > dl)
00304         dl = tl;
00305     return dl;
00306 }
00307 
00317 static void copyEntry(const struct indexEntry * entry, /*@out@*/ int_32 * type,
00318         /*@out@*/ const void ** p, /*@out@*/ int_32 * c, int minMem)
00319                 /*@modifies *type, *p, *c @*/
00320 {
00321     int_32 count = entry->info.count;
00322 
00323     if (p)
00324     switch (entry->info.type) {
00325     case RPM_BIN_TYPE:
00326         /* XXX this only works for HEADER_IMMUTABLE */
00327         if (ENTRY_IS_REGION(entry)) {
00328             int_32 * ei = ((int_32 *)entry->data) - 2;
00329             struct entryInfo * pe = (struct entryInfo *) (ei + 2);
00330             char * dataStart = (char *) (pe + ntohl(ei[0]));
00331             int_32 rdl = -entry->info.offset;   /* negative offset */
00332             int_32 ril = rdl/sizeof(*pe);
00333 
00334             count = 2 * sizeof(*ei) + (ril * sizeof(*pe)) +
00335                         entry->rdlen + REGION_TAG_COUNT;
00336             *p = xmalloc(count);
00337             ei = (int_32 *) *p;
00338             ei[0] = htonl(ril);
00339             ei[1] = htonl(entry->rdlen + REGION_TAG_COUNT);
00340             pe = (struct entryInfo *) memcpy(ei + 2, pe, (ril * sizeof(*pe)));
00341             dataStart = (char *) memcpy(pe + ril, dataStart,
00342                                         (entry->rdlen + REGION_TAG_COUNT));
00343 
00344             (void) regionSwab(NULL, ril, 0, pe, dataStart, 0);
00345         } else {
00346             count = entry->length;
00347             *p = (!minMem
00348                 ? memcpy(xmalloc(count), entry->data, count)
00349                 : entry->data);
00350         }
00351         break;
00352     case RPM_STRING_TYPE:
00353         if (count == 1) {
00354             *p = entry->data;
00355             break;
00356         }
00357         /*@fallthrough@*/
00358     case RPM_STRING_ARRAY_TYPE:
00359     case RPM_I18NSTRING_TYPE:
00360     {   const char ** ptrEntry;
00361         int tableSize = count * sizeof(char *);
00362         char * t;
00363         int i;
00364 
00365         if (minMem) {
00366             *p = xmalloc(tableSize);
00367             ptrEntry = (const char **) *p;
00368             t = entry->data;
00369         } else {
00370             t = xmalloc(tableSize + entry->length);
00371             *p = (void *)t;
00372             ptrEntry = (const char **) *p;
00373             t += tableSize;
00374             memcpy(t, entry->data, entry->length);
00375         }
00376         for (i = 0; i < count; i++) {
00377             *ptrEntry++ = t;
00378             t = strchr(t, 0);
00379             t++;
00380         }
00381     }   break;
00382 
00383     default:
00384         *p = entry->data;
00385         break;
00386     }
00387     if (type) *type = entry->info.type;
00388     if (c) *c = count;
00389 }
00390 
00394 struct headerIteratorS {
00395     Header h;           
00396     int next_index;     
00397 };
00398 
00399 HeaderIterator headerInitIterator(Header h)
00400 {
00401     HeaderIterator hi = xmalloc(sizeof(struct headerIteratorS));
00402 
00403     headerSort(h);
00404 
00405     hi->h = headerLink(h);
00406     hi->next_index = 0;
00407     return hi;
00408 }
00409 
00410 void headerFreeIterator(HeaderIterator iter)
00411 {
00412     headerFree(iter->h);
00413     free(iter);
00414 }
00415 
00416 int headerNextIterator(HeaderIterator hi,
00417                  int_32 * tag, int_32 * type, const void ** p, int_32 * c)
00418 {
00419     Header h = hi->h;
00420     int slot = hi->next_index;
00421     struct indexEntry * entry = NULL;;
00422 
00423     for (slot = hi->next_index; slot < h->indexUsed; slot++) {
00424         entry = h->index + slot;
00425         if (!ENTRY_IS_REGION(entry))
00426             break;
00427     }
00428     hi->next_index = slot;
00429     if (entry == NULL || slot >= h->indexUsed)
00430         return 0;
00431     hi->next_index++;
00432 
00433     if (tag)
00434         *tag = entry->info.tag;
00435 
00436     copyEntry(entry, type, p, c, 0);
00437 
00438     return 1;
00439 }
00440 
00441 static int indexCmp(const void *avp, const void *bvp)   /*@*/
00442 {
00443     const struct indexEntry * ap = avp, * bp = bvp;
00444     return (ap->info.tag - bp->info.tag);
00445 }
00446 
00447 void headerSort(Header h)
00448 {
00449     if (!h->sorted) {
00450         qsort(h->index, h->indexUsed, sizeof(*h->index), indexCmp);
00451         h->sorted = 1;
00452     }
00453 }
00454 
00455 static int offsetCmp(const void *avp, const void *bvp) /*@*/
00456 {
00457     const struct indexEntry * ap = avp, * bp = bvp;
00458     int rc = (ap->info.offset - bp->info.offset);
00459 
00460     if (rc == 0)
00461         rc = (ap->info.tag - bp->info.tag);
00462     return rc;
00463 }
00464 
00465 void headerUnsort(Header h)
00466 {
00467     qsort(h->index, h->indexUsed, sizeof(*h->index), offsetCmp);
00468 }
00469 
00470 Header headerCopy(Header h)
00471 {
00472     Header nh = headerNew();
00473     HeaderIterator hi;
00474     int_32 tag, type, count;
00475     const void *ptr;
00476    
00477     for (hi = headerInitIterator(h);
00478         headerNextIterator(hi, &tag, &type, &ptr, &count);
00479         ptr = headerFreeData((void *)ptr, type))
00480     {
00481         headerAddEntry(nh, tag, type, ptr, count);
00482     }
00483     headerFreeIterator(hi);
00484 
00485     return headerReload(nh, HEADER_IMAGE);
00486 }
00487 
00488 Header headerLoad(void *uh)
00489 {
00490     int_32 *ei = (int_32 *) uh;
00491     int_32 il = ntohl(ei[0]);           /* index length */
00492     int_32 dl = ntohl(ei[1]);           /* data length */
00493     int pvlen = sizeof(il) + sizeof(dl) +
00494                (il * sizeof(struct entryInfo)) + dl;
00495 #ifdef  REMALLOC_HEADER_REGION
00496     void * pv = memcpy(xmalloc(pvlen), uh, pvlen);
00497 #else
00498     void * pv = uh;
00499 #endif
00500     Header h = xcalloc(1, sizeof(*h));
00501     struct entryInfo * pe;
00502     char * dataStart;
00503     struct indexEntry * entry; 
00504     int rdlen;
00505     int i;
00506 
00507     ei = (int_32 *) pv;
00508     pe = (struct entryInfo *) &ei[2];
00509     dataStart = (char *) (pe + il);
00510 
00511     h->indexAlloced = il + 1;
00512     h->indexUsed = il;
00513     h->index = xcalloc(h->indexAlloced, sizeof(*h->index));
00514     h->sorted = 1;
00515 #ifdef  REMALLOC_HEADER_REGION
00516     h->region_allocated = 1;
00517 #else
00518     h->region_allocated = 0;
00519 #endif
00520     h->nrefs = 1;
00521 
00522     /*
00523      * XXX XFree86-libs, ash, and pdksh from Red Hat 5.2 have bogus
00524      * %verifyscript tag that needs to be diddled.
00525      */
00526     if (ntohl(pe->tag) == 15 &&
00527         ntohl(pe->type) == RPM_STRING_TYPE &&
00528         ntohl(pe->count) == 1)
00529     {
00530         pe->tag = htonl(1079);
00531     }
00532 
00533     entry = h->index;
00534     i = 0;
00535     if (!(htonl(pe->tag) < HEADER_I18NTABLE)) {
00536         h->legacy = 1;
00537         entry->info.type = REGION_TAG_TYPE;
00538         entry->info.tag = HEADER_IMAGE;
00539         entry->info.count = REGION_TAG_COUNT;
00540         entry->info.offset = ((char *)pe - dataStart); /* negative offset */
00541 
00542         entry->data = pe;
00543         entry->length = pvlen - sizeof(il) - sizeof(dl);
00544         rdlen = regionSwab(entry+1, il, 0, pe, dataStart, entry->info.offset);
00545         entry->rdlen = rdlen;
00546 assert(rdlen == dl);
00547 
00548         entry++;
00549         h->indexUsed++;
00550     } else {
00551         int nb = ntohl(pe->count);
00552         int_32 rdl;
00553         int_32 ril;
00554 
00555         h->legacy = 0;
00556         entry->info.type = htonl(pe->type);
00557         if (entry->info.type < RPM_MIN_TYPE || entry->info.type > RPM_MAX_TYPE)
00558             return NULL;
00559         entry->info.count = htonl(pe->count);
00560 
00561         {  int off = ntohl(pe->offset);
00562            if (off) {
00563                 int_32 * stei = memcpy(alloca(nb), dataStart + off, nb);
00564                 rdl = -ntohl(stei[2]);  /* negative offset */
00565                 ril = rdl/sizeof(*pe);
00566                 entry->info.tag = htonl(pe->tag);
00567             } else {
00568                 ril = il;
00569                 rdl = (ril * sizeof(struct entryInfo));
00570                 entry->info.tag = HEADER_IMAGE;
00571             }
00572         }
00573         entry->info.offset = -rdl;      /* negative offset */
00574 
00575         entry->data = pe;
00576         entry->length = pvlen - sizeof(il) - sizeof(dl);
00577         rdlen = regionSwab(entry+1, ril-1, 0, pe+1, dataStart, entry->info.offset);
00578         entry->rdlen = rdlen;
00579 
00580         if (ril < h->indexUsed) {
00581             struct indexEntry * newEntry = entry + ril;
00582             int ne = (h->indexUsed - ril);
00583             int rid = entry->info.offset+1;
00584 
00585             /* Load dribble entries from region. */
00586             rdlen += regionSwab(newEntry, ne, 0, pe+ril, dataStart, rid);
00587 
00588           { struct indexEntry * firstEntry = newEntry;
00589             int save = h->indexUsed;
00590             int j;
00591 
00592             /* Dribble entries replace duplicate region entries. */
00593             h->indexUsed -= ne;
00594             for (j = 0; j < ne; j++, newEntry++) {
00595                 headerRemoveEntry(h, newEntry->info.tag);
00596                 if (newEntry->info.tag == HEADER_BASENAMES)
00597                     headerRemoveEntry(h, HEADER_OLDFILENAMES);
00598             }
00599 
00600             /* If any duplicate entries were replaced, move new entries down. */
00601             if (h->indexUsed < (save - ne)) {
00602                 memmove(h->index + h->indexUsed, firstEntry,
00603                         (ne * sizeof(*entry)));
00604             }
00605             h->indexUsed += ne;
00606           }
00607         }
00608     }
00609 
00610     h->sorted = 0;
00611     headerSort(h);
00612 
00613     return h;
00614 }
00615 
00616 Header headerCopyLoad(void *uh)
00617 {
00618     int_32 *ei = (int_32 *) uh;
00619     int_32 il = ntohl(ei[0]);           /* index length */
00620     int_32 dl = ntohl(ei[1]);           /* data length */
00621     int pvlen = sizeof(il) + sizeof(dl) +
00622                (il * sizeof(struct entryInfo)) + dl;
00623     void * nuh = memcpy(xmalloc(pvlen), uh, pvlen);
00624     Header h;
00625 
00626     h = headerLoad(nuh);
00627     if (h == NULL) {
00628         free(nuh);
00629         return h;
00630     }
00631     h->region_allocated = 1;
00632     return h;
00633 }
00634 
00635 #if 0
00636 int headerDrips(const Header h)
00637 {
00638     struct indexEntry * entry; 
00639     int i;
00640 
00641     for (i = 0, entry = h->index; i < h->indexUsed; i++, entry++) {
00642         if (ENTRY_IS_REGION(entry)) {
00643             int rid = entry->info.offset;
00644 
00645             for (; i < h->indexUsed && entry->info.offset <= rid+1; i++, entry++) {
00646                 if (entry->info.offset <= rid)
00647                     continue;
00648             }
00649             i--;
00650             entry--;
00651             continue;
00652         }
00653 
00654         /* Ignore deleted drips. */
00655         if (entry->data == NULL || entry->length <= 0)
00656             continue;
00657     }
00658     return 0;
00659 }
00660 #endif
00661 
00662 static /*@only@*/ void * doHeaderUnload(Header h, /*@out@*/ int * lengthPtr)
00663         /*@modifies h, *lengthPtr @*/
00664 {
00665     int_32 * ei;
00666     struct entryInfo * pe;
00667     char * dataStart;
00668     char * te;
00669     unsigned pad;
00670     unsigned len;
00671     int_32 il = 0;
00672     int_32 dl = 0;
00673     struct indexEntry * entry; 
00674     int_32 type;
00675     int i;
00676     int drlen, ndribbles;
00677     int driplen, ndrips;
00678     int legacy = 0;
00679 
00680     /* Sort entries by (offset,tag). */
00681     headerUnsort(h);
00682 
00683     /* Compute (il,dl) for all tags, including those deleted in region. */
00684     pad = 0;
00685     drlen = ndribbles = driplen = ndrips = 0;
00686     for (i = 0, entry = h->index; i < h->indexUsed; i++, entry++) {
00687         if (ENTRY_IS_REGION(entry)) {
00688             int_32 rdl = -entry->info.offset;   /* negative offset */
00689             int_32 ril = rdl/sizeof(*pe);
00690             int rid = entry->info.offset;
00691 
00692             il += ril;
00693             dl += entry->rdlen + entry->info.count;
00694             /* XXX Legacy regions do not include the region tag and data. */
00695             if (i == 0 && h->legacy)
00696                 il += 1;
00697 
00698             /* Skip rest of entries in region, but account for dribbles. */
00699             for (; i < h->indexUsed && entry->info.offset <= rid+1; i++, entry++) {
00700                 if (entry->info.offset <= rid)
00701                     continue;
00702 
00703                 /* Alignment */
00704                 type = entry->info.type;
00705                 if (typeSizes[type] > 1) {
00706                     unsigned diff;
00707                     diff = typeSizes[type] - (dl % typeSizes[type]);
00708                     if (diff != typeSizes[type]) {
00709                         drlen += diff;
00710                         pad += diff;
00711                         dl += diff;
00712                     }
00713                 }
00714 
00715                 ndribbles++;
00716                 il++;
00717                 drlen += entry->length;
00718                 dl += entry->length;
00719             }
00720             i--;
00721             entry--;
00722             continue;
00723         }
00724 
00725         /* Ignore deleted drips. */
00726         if (entry->data == NULL || entry->length <= 0)
00727             continue;
00728 
00729         /* Alignment */
00730         type = entry->info.type;
00731         if (typeSizes[type] > 1) {
00732             unsigned diff;
00733             diff = typeSizes[type] - (dl % typeSizes[type]);
00734             if (diff != typeSizes[type]) {
00735                 driplen += diff;
00736                 pad += diff;
00737                 dl += diff;
00738             } else
00739                 diff = 0;
00740         }
00741 
00742         ndrips++;
00743         il++;
00744         driplen += entry->length;
00745         dl += entry->length;
00746     }
00747     len = sizeof(il) + sizeof(dl) + (il * sizeof(*pe)) + dl;
00748 
00749     ei = xmalloc(len);
00750     ei[0] = htonl(il);
00751     ei[1] = htonl(dl);
00752 
00753     pe = (struct entryInfo *) &ei[2];
00754     dataStart = te = (char *) (pe + il);
00755 
00756     pad = 0;
00757     for (i = 0, entry = h->index; i < h->indexUsed; i++, entry++) {
00758         const char * src;
00759 char *t;
00760         int count;
00761         int rdlen;
00762 
00763         if (entry->data == NULL || entry->length <= 0)
00764             continue;
00765 
00766 t = te;
00767         pe->tag = htonl(entry->info.tag);
00768         pe->type = htonl(entry->info.type);
00769         pe->count = htonl(entry->info.count);
00770 
00771         if (ENTRY_IS_REGION(entry)) {
00772             int_32 rdl = -entry->info.offset;   /* negative offset */
00773             int_32 ril = rdl/sizeof(*pe) + ndribbles;
00774             int rid = entry->info.offset;
00775 
00776             src = (char *)entry->data;
00777             rdlen = entry->rdlen;
00778 
00779             /* XXX Legacy regions do not include the region tag and data. */
00780             if (i == 0 && h->legacy) {
00781                 int_32 stei[4];
00782 
00783                 legacy = 1;
00784                 memcpy(pe+1, src, rdl);
00785                 memcpy(te, src + rdl, rdlen);
00786                 te += rdlen;
00787 
00788                 pe->offset = htonl(te - dataStart);
00789                 stei[0] = pe->tag;
00790                 stei[1] = pe->type;
00791                 stei[2] = htonl(-rdl-entry->info.count);
00792                 stei[3] = pe->count;
00793                 memcpy(te, stei, entry->info.count);
00794                 te += entry->info.count;
00795                 ril++;
00796                 rdlen += entry->info.count;
00797 
00798                 count = regionSwab(NULL, ril, 0, pe, t, 0);
00799                 assert(count == rdlen);
00800 
00801             } else {
00802 
00803                 memcpy(pe+1, src + sizeof(*pe), ((ril-1) * sizeof(*pe)));
00804                 memcpy(te, src + (ril * sizeof(*pe)), rdlen+entry->info.count+drlen);
00805                 te += rdlen;
00806                 {   struct entryInfo * se = (struct entryInfo *)src;
00807                     int off = ntohl(se->offset);
00808                     pe->offset = (off) ? htonl(te - dataStart) : htonl(off);
00809                 }
00810                 te += entry->info.count + drlen;
00811 
00812                 count = regionSwab(NULL, ril, 0, pe, t, 0);
00813                 assert(count == rdlen+entry->info.count+drlen);
00814             }
00815 
00816             /* Skip rest of entries in region. */
00817             while (i < h->indexUsed && entry->info.offset <= rid+1) {
00818                 i++;
00819                 entry++;
00820             }
00821             i--;
00822             entry--;
00823             pe += ril;
00824             continue;
00825         }
00826 
00827         /* Ignore deleted drips. */
00828         if (entry->data == NULL || entry->length <= 0)
00829             continue;
00830 
00831         /* Alignment */
00832         type = entry->info.type;
00833         if (typeSizes[type] > 1) {
00834             unsigned diff;
00835             diff = typeSizes[type] - ((te - dataStart) % typeSizes[type]);
00836             if (diff != typeSizes[type]) {
00837                 memset(te, 0, diff);
00838                 te += diff;
00839                 pad += diff;
00840             }
00841         }
00842 
00843         pe->offset = htonl(te - dataStart);
00844 
00845         /* copy data w/ endian conversions */
00846         switch (entry->info.type) {
00847         case RPM_INT32_TYPE:
00848             count = entry->info.count;
00849             src = entry->data;
00850             while (count--) {
00851                 *((int_32 *)te) = htonl(*((int_32 *)src));
00852                 te += sizeof(int_32);
00853                 src += sizeof(int_32);
00854             }
00855             break;
00856 
00857         case RPM_INT16_TYPE:
00858             count = entry->info.count;
00859             src = entry->data;
00860             while (count--) {
00861                 *((int_16 *)te) = htons(*((int_16 *)src));
00862                 te += sizeof(int_16);
00863                 src += sizeof(int_16);
00864             }
00865             break;
00866 
00867         default:
00868             memcpy(te, entry->data, entry->length);
00869             te += entry->length;
00870             break;
00871         }
00872         pe++;
00873     }
00874    
00875     /* Insure that there are no memcpy underruns/overruns. */
00876     assert(((char *)pe) == dataStart);
00877     assert((((char *)ei)+len) == te);
00878 
00879     if (lengthPtr)
00880         *lengthPtr = len;
00881 
00882     h->sorted = 0;
00883     headerSort(h);
00884 
00885     return (void *)ei;
00886 }
00887 
00888 void *headerUnload(Header h)
00889 {
00890     int length;
00891     void * uh = doHeaderUnload(h, &length);
00892     return uh;
00893 }
00894 
00895 Header headerReload(Header h, int tag)
00896 {
00897     Header nh;
00898     int length;
00899     void * uh = doHeaderUnload(h, &length);
00900 
00901     headerFree(h);
00902     nh = headerLoad(uh);
00903     if (nh == NULL) {
00904         free(uh);
00905         return nh;
00906     }
00907     if (nh->region_allocated)
00908         free(uh);
00909     nh->region_allocated = 1;
00910     if (ENTRY_IS_REGION(nh->index)) {
00911         if (tag == HEADER_SIGNATURES || tag == HEADER_IMMUTABLE)
00912             nh->index[0].info.tag = tag;
00913     }
00914     return nh;
00915 }
00916 
00917 int headerWrite(FD_t fd, Header h, enum hMagic magicp)
00918 {
00919     ssize_t nb;
00920     int length;
00921     const void * uh;
00922 
00923     uh = doHeaderUnload(h, &length);
00924     switch (magicp) {
00925     case HEADER_MAGIC_YES:
00926         nb = Fwrite(header_magic, sizeof(char), sizeof(header_magic), fd);
00927         if (nb != sizeof(header_magic))
00928             goto exit;
00929         break;
00930     case HEADER_MAGIC_NO:
00931         break;
00932     }
00933 
00934     nb = Fwrite(uh, sizeof(char), length, fd);
00935 
00936 exit:
00937     free((void *)uh);
00938     return (nb == length ? 0 : 1);
00939 }
00940 
00941 Header headerRead(FD_t fd, enum hMagic magicp)
00942 {
00943     int_32 block[4];
00944     int_32 reserved;
00945     int_32 * ei = NULL;
00946     int_32 il;
00947     int_32 dl;
00948     int_32 magic;
00949     Header h = NULL;
00950     int len;
00951     int i;
00952 
00953     memset(block, 0, sizeof(block));
00954     i = 2;
00955     if (magicp == HEADER_MAGIC_YES)
00956         i += 2;
00957 
00958     if (timedRead(fd, (char *)block, i*sizeof(*block)) != (i * sizeof(*block)))
00959         goto exit;
00960 
00961     i = 0;
00962 
00963     if (magicp == HEADER_MAGIC_YES) {
00964         magic = block[i++];
00965         if (memcmp(&magic, header_magic, sizeof(magic)))
00966             goto exit;
00967         reserved = block[i++];
00968     }
00969     
00970     il = ntohl(block[i++]);
00971     dl = ntohl(block[i++]);
00972 
00973     len = sizeof(il) + sizeof(dl) + (il * sizeof(struct entryInfo)) + dl;
00974 
00975     /*
00976      * XXX Limit total size of header to 32Mb (~16 times largest known size).
00977      */
00978     if (len > (32*1024*1024))
00979         goto exit;
00980 
00981     ei = xmalloc(len);
00982     ei[0] = htonl(il);
00983     ei[1] = htonl(dl);
00984     len -= sizeof(il) + sizeof(dl);
00985 
00986     if (timedRead(fd, (char *)&ei[2], len) != len)
00987         goto exit;
00988     
00989     h = headerLoad(ei);
00990 
00991 exit:
00992     if (h) {
00993         if (h->region_allocated)
00994             free(ei);
00995         h->region_allocated = 1;
00996     } else if (ei)
00997         free(ei);
00998     return h;
00999 }
01000 
01001 void headerDump(Header h, FILE *f, int flags, 
01002                 const struct headerTagTableEntry * tags)
01003 {
01004     int i;
01005     struct indexEntry *p;
01006     const struct headerTagTableEntry * tage;
01007     const char *tag;
01008     char *type;
01009 
01010     /* First write out the length of the index (count of index entries) */
01011     fprintf(f, "Entry count: %d\n", h->indexUsed);
01012 
01013     /* Now write the index */
01014     p = h->index;
01015     fprintf(f, "\n             CT  TAG                  TYPE         "
01016                 "      OFSET      COUNT\n");
01017     for (i = 0; i < h->indexUsed; i++) {
01018         switch (p->info.type) {
01019         case RPM_NULL_TYPE:             type = "NULL_TYPE";     break;
01020         case RPM_CHAR_TYPE:             type = "CHAR_TYPE";     break;
01021         case RPM_BIN_TYPE:              type = "BIN_TYPE";      break;
01022         case RPM_INT8_TYPE:             type = "INT8_TYPE";     break;
01023         case RPM_INT16_TYPE:            type = "INT16_TYPE";    break;
01024         case RPM_INT32_TYPE:            type = "INT32_TYPE";    break;
01025         /*case RPM_INT64_TYPE:          type = "INT64_TYPE";    break;*/
01026         case RPM_STRING_TYPE:           type = "STRING_TYPE";   break;
01027         case RPM_STRING_ARRAY_TYPE:     type = "STRING_ARRAY_TYPE"; break;
01028         case RPM_I18NSTRING_TYPE:       type = "I18N_STRING_TYPE"; break;
01029         default:                        type = "(unknown)";     break;
01030         }
01031 
01032         tage = tags;
01033         while (tage->name && tage->val != p->info.tag) tage++;
01034 
01035         if (!tage->name)
01036             tag = "(unknown)";
01037         else
01038             tag = tage->name;
01039 
01040         fprintf(f, "Entry      : %.3d (%d)%-14s %-18s 0x%.8x %.8d\n", i,
01041                 p->info.tag, tag, type, (unsigned) p->info.offset, (int) 
01042                 p->info.count);
01043 
01044         if (flags & HEADER_DUMP_INLINE) {
01045             char *dp = p->data;
01046             int c = p->info.count;
01047             int ct = 0;
01048 
01049             /* Print the data inline */
01050             switch (p->info.type) {
01051             case RPM_INT32_TYPE:
01052                 while (c--) {
01053                     fprintf(f, "       Data: %.3d 0x%08x (%d)\n", ct++,
01054                             (unsigned) *((int_32 *) dp),
01055                             (int) *((int_32 *) dp));
01056                     dp += sizeof(int_32);
01057                 }
01058                 break;
01059 
01060             case RPM_INT16_TYPE:
01061                 while (c--) {
01062                     fprintf(f, "       Data: %.3d 0x%04x (%d)\n", ct++,
01063                             (unsigned) (*((int_16 *) dp) & 0xffff),
01064                             (int) *((int_16 *) dp));
01065                     dp += sizeof(int_16);
01066                 }
01067                 break;
01068             case RPM_INT8_TYPE:
01069                 while (c--) {
01070                     fprintf(f, "       Data: %.3d 0x%02x (%d)\n", ct++,
01071                             (unsigned) (*((int_8 *) dp) & 0xff),
01072                             (int) *((int_8 *) dp));
01073                     dp += sizeof(int_8);
01074                 }
01075                 break;
01076             case RPM_BIN_TYPE:
01077                 while (c > 0) {
01078                     fprintf(f, "       Data: %.3d ", ct);
01079                     while (c--) {
01080                         fprintf(f, "%02x ", (unsigned) (*(int_8 *)dp & 0xff));
01081                         ct++;
01082                         dp += sizeof(int_8);
01083                         if (! (ct % 8)) {
01084                             break;
01085                         }
01086                     }
01087                     fprintf(f, "\n");
01088                 }
01089                 break;
01090             case RPM_CHAR_TYPE:
01091                 while (c--) {
01092                     char ch = (char) *((char *) dp);
01093                     fprintf(f, "       Data: %.3d 0x%2x %c (%d)\n", ct++,
01094                             (unsigned)(ch & 0xff),
01095                             (isprint(ch) ? ch : ' '),
01096                             (int) *((char *) dp));
01097                     dp += sizeof(char);
01098                 }
01099                 break;
01100             case RPM_STRING_TYPE:
01101             case RPM_STRING_ARRAY_TYPE:
01102             case RPM_I18NSTRING_TYPE:
01103                 while (c--) {
01104                     fprintf(f, "       Data: %.3d %s\n", ct++, (char *) dp);
01105                     dp = strchr(dp, 0);
01106                     dp++;
01107                 }
01108                 break;
01109             default:
01110                 fprintf(stderr, _("Data type %d not supported\n"), 
01111                         (int) p->info.type);
01112                 exit(EXIT_FAILURE);
01113                 /*@notreached@*/ break;
01114             }
01115         }
01116         p++;
01117     }
01118 }
01119 
01127 static struct indexEntry *findEntry(Header h, int_32 tag, int_32 type)
01128 {
01129     struct indexEntry * entry, * entry2, * last;
01130     struct indexEntry key;
01131 
01132     if (!h->sorted) headerSort(h);
01133 
01134     key.info.tag = tag;
01135 
01136     entry2 = entry = 
01137         bsearch(&key, h->index, h->indexUsed, sizeof(*h->index), indexCmp);
01138     if (entry == NULL)
01139         return NULL;
01140 
01141     if (type == RPM_NULL_TYPE)
01142         return entry;
01143 
01144     /* look backwards */
01145     while (entry->info.tag == tag && entry->info.type != type &&
01146            entry > h->index) entry--;
01147 
01148     if (entry->info.tag == tag && entry->info.type == type)
01149         return entry;
01150 
01151     last = h->index + h->indexUsed;
01152     while (entry2->info.tag == tag && entry2->info.type != type &&
01153            entry2 < last) entry2++;
01154 
01155     if (entry->info.tag == tag && entry->info.type == type)
01156         return entry;
01157 
01158     return NULL;
01159 }
01160 
01161 int headerIsEntry(Header h, int_32 tag)
01162 {
01163     return (findEntry(h, tag, RPM_NULL_TYPE) ? 1 : 0);
01164 }
01165 
01166 int headerGetRawEntry(Header h, int_32 tag, int_32 * type, const void ** p,
01167                         int_32 *c)
01168 {
01169     struct indexEntry * entry;
01170 
01171     if (p == NULL) return headerIsEntry(h, tag);
01172 
01173     /* First find the tag */
01174     entry = findEntry(h, tag, RPM_NULL_TYPE);
01175     if (!entry) {
01176         if (p) *p = NULL;
01177         if (c) *c = 0;
01178         return 0;
01179     }
01180 
01181     copyEntry(entry, type, p, c, 0);
01182 
01183     return 1;
01184 }
01185 
01204 static int headerMatchLocale(const char *td, const char *l, const char *le)
01205         /*@*/
01206 {
01207     const char *fe;
01208 
01209 
01210 #if 0
01211   { const char *s, *ll, *CC, *EE, *dd;
01212     char *lbuf, *t.
01213 
01214     /* Copy the buffer and parse out components on the fly. */
01215     lbuf = alloca(le - l + 1);
01216     for (s = l, ll = t = lbuf; *s; s++, t++) {
01217         switch (*s) {
01218         case '_':
01219             *t = '\0';
01220             CC = t + 1;
01221             break;
01222         case '.':
01223             *t = '\0';
01224             EE = t + 1;
01225             break;
01226         case '@':
01227             *t = '\0';
01228             dd = t + 1;
01229             break;
01230         default:
01231             *t = *s;
01232             break;
01233         }
01234     }
01235 
01236     if (ll)     /* ISO language should be lower case */
01237         for (t = ll; *t; t++)   *t = tolower(*t);
01238     if (CC)     /* ISO country code should be upper case */
01239         for (t = CC; *t; t++)   *t = toupper(*t);
01240 
01241     /* There are a total of 16 cases to attempt to match. */
01242   }
01243 #endif
01244 
01245     /* First try a complete match. */
01246     if (strlen(td) == (le-l) && !strncmp(td, l, (le - l)))
01247         return 1;
01248 
01249     /* Next, try stripping optional dialect and matching.  */
01250     for (fe = l; fe < le && *fe != '@'; fe++)
01251         ;
01252     if (fe < le && !strncmp(td, l, (fe - l)))
01253         return 1;
01254 
01255     /* Next, try stripping optional codeset and matching.  */
01256     for (fe = l; fe < le && *fe != '.'; fe++)
01257         ;
01258     if (fe < le && !strncmp(td, l, (fe - l)))
01259         return 1;
01260 
01261     /* Finally, try stripping optional country code and matching. */
01262     for (fe = l; fe < le && *fe != '_'; fe++)
01263         ;
01264     if (fe < le && !strncmp(td, l, (fe - l)))
01265         return 1;
01266 
01267     return 0;
01268 }
01269 
01276 /*@dependent@*/ static char *
01277 headerFindI18NString(Header h, struct indexEntry *entry)
01278 {
01279     const char *lang, *l, *le;
01280     struct indexEntry * table;
01281 
01282     /* XXX Drepper sez' this is the order. */
01283     if ((lang = getenv("LANGUAGE")) == NULL &&
01284         (lang = getenv("LC_ALL")) == NULL &&
01285         (lang = getenv("LC_MESSAGES")) == NULL &&
01286         (lang = getenv("LANG")) == NULL)
01287             return entry->data;
01288     
01289     if ((table = findEntry(h, HEADER_I18NTABLE, RPM_STRING_ARRAY_TYPE)) == NULL)
01290         return entry->data;
01291 
01292     for (l = lang; *l; l = le) {
01293         const char *td;
01294         char *ed;
01295         int langNum;
01296 
01297         while (*l && *l == ':')                 /* skip leading colons */
01298             l++;
01299         if (*l == '\0')
01300             break;
01301         for (le = l; *le && *le != ':'; le++)   /* find end of this locale */
01302             ;
01303 
01304         /* For each entry in the header ... */
01305         for (langNum = 0, td = table->data, ed = entry->data;
01306              langNum < entry->info.count;
01307              langNum++, td += strlen(td) + 1, ed += strlen(ed) + 1) {
01308 
01309                 if (headerMatchLocale(td, l, le))
01310                     return ed;
01311 
01312         }
01313     }
01314 
01315     return entry->data;
01316 }
01317 
01328 static int intGetEntry(Header h, int_32 tag, /*@out@*/ int_32 *type,
01329         /*@out@*/ const void **p, /*@out@*/ int_32 *c, int minMem)
01330                 /*@modifies *type, *p, *c @*/
01331 {
01332     struct indexEntry * entry;
01333 
01334     /* First find the tag */
01335     entry = findEntry(h, tag, RPM_NULL_TYPE);
01336     if (entry == NULL) {
01337         if (type) type = 0;
01338         if (p) *p = NULL;
01339         if (c) *c = 0;
01340         return 0;
01341     }
01342 
01343     switch (entry->info.type) {
01344     case RPM_I18NSTRING_TYPE:
01345         if (type) *type = RPM_STRING_TYPE;
01346         if (c) *c = 1;
01347         /*@-dependenttrans@*/
01348         if (p) *p = headerFindI18NString(h, entry);
01349         /*@=dependenttrans@*/
01350         break;
01351     default:
01352         copyEntry(entry, type, p, c, minMem);
01353         break;
01354     }
01355 
01356     return 1;
01357 }
01358 
01359 int headerGetEntryMinMemory(Header h, int_32 tag, int_32 *type, const void **p, 
01360                             int_32 *c)
01361 {
01362     return intGetEntry(h, tag, type, p, c, 1);
01363 }
01364 
01365 int headerGetEntry(Header h, int_32 tag, int_32 * type, void **p, int_32 * c)
01366 {
01367     return intGetEntry(h, tag, type, (const void **)p, c, 0);
01368 }
01369 
01370 Header headerNew()
01371 {
01372     Header h = xcalloc(1, sizeof(*h));
01373 
01374     h->indexAlloced = INDEX_MALLOC_SIZE;
01375     h->indexUsed = 0;
01376     h->region_allocated = 0;
01377     h->sorted = 1;
01378     h->legacy = 0;
01379     h->nrefs = 1;
01380 
01381     h->index = (h->indexAlloced
01382         ? xcalloc(h->indexAlloced, sizeof(*h->index))
01383         : NULL);
01384 
01385     return h;
01386 }
01387 
01388 void headerFree(Header h)
01389 {
01390     if (h == NULL || --h->nrefs > 0)
01391         return;
01392 
01393     if (h->index) {
01394         struct indexEntry * entry = h->index;
01395         int i;
01396         for (i = 0; i < h->indexUsed; i++, entry++) {
01397             if (h->region_allocated && ENTRY_IS_REGION(entry)) {
01398                 if (entry->length > 0) {
01399                     int_32 * ei = entry->data;
01400                     ei -= 2; /* XXX HACK: adjust to beginning of header. */
01401                     free(ei);
01402                 }
01403             } else if (!ENTRY_IN_REGION(entry)) {
01404                 free(entry->data);
01405             }
01406             entry->data = NULL;
01407         }
01408         free(h->index);
01409         h->index = NULL;
01410     }
01411 
01412     /*@-refcounttrans@*/ free(h); /*@=refcounttrans@*/
01413 }
01414 
01415 Header headerLink(Header h)
01416 {
01417     h->nrefs++;
01418     /*@-refcounttrans@*/ return h; /*@=refcounttrans@*/
01419 }
01420 
01421 int headerUsageCount(Header h)
01422 {
01423     return h->nrefs;
01424 }
01425 
01426 unsigned int headerSizeof(Header h, enum hMagic magicp)
01427 {
01428     struct indexEntry * entry;
01429     unsigned int size = 0, pad = 0;
01430     int i;
01431 
01432     headerSort(h);
01433 
01434     switch (magicp) {
01435     case HEADER_MAGIC_YES:
01436         size += sizeof(header_magic);
01437         break;
01438     case HEADER_MAGIC_NO:
01439         break;
01440     }
01441 
01442     size += 2 * sizeof(int_32); /* count of index entries */
01443 
01444     for (i = 0, entry = h->index; i < h->indexUsed; i++, entry++) {
01445         unsigned diff;
01446         int_32 type;
01447 
01448         /* Regions go in as is ... */
01449         if (ENTRY_IS_REGION(entry)) {
01450             size += entry->length;
01451             /* XXX Legacy regions do not include the region tag and data. */
01452             if (i == 0 && h->legacy)
01453                 size += sizeof(struct entryInfo) + entry->info.count;
01454             continue;
01455         }
01456 
01457         /* ... and region elements are skipped. */
01458         if (entry->info.offset < 0)
01459             continue;
01460 
01461         /* Alignment */
01462         type = entry->info.type;
01463         if (typeSizes[type] > 1) {
01464             diff = typeSizes[type] - (size % typeSizes[type]);
01465             if (diff != typeSizes[type]) {
01466                 size += diff;
01467                 pad += diff;
01468             }
01469         }
01470 
01471         size += sizeof(struct entryInfo) + entry->length;
01472     }
01473 
01474     return size;
01475 }
01476 
01477 static void copyData(int_32 type, /*@out@*/ void * dstPtr, const void * srcPtr,
01478         int_32 c, int dataLength)
01479                 /*@modifies *dstPtr @*/
01480 {
01481     const char ** src;
01482     char * dst;
01483     int i, len;
01484 
01485     switch (type) {
01486     case RPM_STRING_ARRAY_TYPE:
01487     case RPM_I18NSTRING_TYPE:
01488         /* Otherwise, p is char** */
01489         i = c;
01490         src = (const char **) srcPtr;
01491         dst = dstPtr;
01492         while (i--) {
01493             len = *src ? strlen(*src) + 1 : 0;
01494             memcpy(dst, *src, len);
01495             dst += len;
01496             src++;
01497         }
01498         break;
01499 
01500     default:
01501         memcpy(dstPtr, srcPtr, dataLength);
01502         break;
01503     }
01504 }
01505 
01514 static void * grabData(int_32 type, const void * p, int_32 c,
01515         /*@out@*/ int * lengthPtr)
01516                 /*@modifies *lengthPtr @*/
01517 {
01518     int length = dataLength(type, p, c, 0);
01519     void * data = xmalloc(length);
01520 
01521     copyData(type, data, p, c, length);
01522 
01523     if (lengthPtr)
01524         *lengthPtr = length;
01525     return data;
01526 }
01527 
01528 int headerAddEntry(Header h, int_32 tag, int_32 type, const void *p, int_32 c)
01529 {
01530     struct indexEntry *entry;
01531 
01532     if (c <= 0) {
01533         fprintf(stderr, _("Bad count for headerAddEntry(): %d\n"), (int) c);
01534         exit(EXIT_FAILURE);
01535         /*@notreached@*/
01536     }
01537 
01538     /* Allocate more index space if necessary */
01539     if (h->indexUsed == h->indexAlloced) {
01540         h->indexAlloced += INDEX_MALLOC_SIZE;
01541         h->index = xrealloc(h->index,
01542                         h->indexAlloced * sizeof(struct indexEntry));
01543     }
01544 
01545     /* Fill in the index */
01546     entry = h->index + h->indexUsed;
01547     entry->info.tag = tag;
01548     entry->info.type = type;
01549     entry->info.count = c;
01550     entry->info.offset = 0;
01551     entry->data = grabData(type, p, c, &entry->length);
01552 
01553     if (h->indexUsed > 0 && tag < h->index[h->indexUsed-1].info.tag)
01554         h->sorted = 0;
01555     h->indexUsed++;
01556 
01557     return 1;
01558 }
01559 
01560 char **
01561 headerGetLangs(Header h)
01562 {
01563     char **s, *e, **table;
01564     int i, type, count;
01565 
01566     if (!headerGetRawEntry(h, HEADER_I18NTABLE, &type, (const void **)&s, &count))
01567         return NULL;
01568 
01569     if ((table = (char **)xcalloc((count+1), sizeof(char *))) == NULL)
01570         return NULL;
01571 
01572     for (i = 0, e = *s; i < count > 0; i++, e += strlen(e)+1)
01573         table[i] = e;
01574     table[count] = NULL;
01575 
01576     return table;
01577 }
01578 
01579 int headerAddI18NString(Header h, int_32 tag, const char * string, const char * lang)
01580 {
01581     struct indexEntry * table, * entry;
01582     char * chptr;
01583     const char ** strArray;
01584     int length;
01585     int ghosts;
01586     int i, langNum;
01587     char * buf;
01588 
01589     table = findEntry(h, HEADER_I18NTABLE, RPM_STRING_ARRAY_TYPE);
01590     entry = findEntry(h, tag, RPM_I18NSTRING_TYPE);
01591 
01592     if (!table && entry)
01593         return 0;               /* this shouldn't ever happen!! */
01594 
01595     if (!table && !entry) {
01596         const char * charArray[2];
01597         int count = 0;
01598         if (!lang || (lang[0] == 'C' && lang[1] == '\0')) {
01599             /*@-observertrans@*/
01600             charArray[count++] = "C";
01601             /*@=observertrans@*/
01602         } else {
01603             /*@-observertrans@*/
01604             charArray[count++] = "C";
01605             /*@=observertrans@*/
01606             charArray[count++] = lang;
01607         }
01608         if (!headerAddEntry(h, HEADER_I18NTABLE, RPM_STRING_ARRAY_TYPE, 
01609                         &charArray, count))
01610             return 0;
01611         table = findEntry(h, HEADER_I18NTABLE, RPM_STRING_ARRAY_TYPE);
01612     }
01613 
01614     if (!lang) lang = "C";
01615 
01616     chptr = table->data;
01617     for (langNum = 0; langNum < table->info.count; langNum++) {
01618         if (!strcmp(chptr, lang)) break;
01619         chptr += strlen(chptr) + 1;
01620     }
01621 
01622     if (langNum >= table->info.count) {
01623         length = strlen(lang) + 1;
01624         if (ENTRY_IN_REGION(table)) {
01625             char * t = xmalloc(table->length + length);
01626             memcpy(t, table->data, table->length);
01627             table->data = t;
01628             table->info.offset = 0;
01629         } else
01630             table->data = xrealloc(table->data, table->length + length);
01631         memcpy(((char *)table->data) + table->length, lang, length);
01632         table->length += length;
01633         table->info.count++;
01634     }
01635 
01636     if (!entry) {
01637         strArray = alloca(sizeof(*strArray) * (langNum + 1));
01638         for (i = 0; i < langNum; i++)
01639             strArray[i] = "";
01640         strArray[langNum] = string;
01641         return headerAddEntry(h, tag, RPM_I18NSTRING_TYPE, strArray, 
01642                                 langNum + 1);
01643     } else if (langNum >= entry->info.count) {
01644         ghosts = langNum - entry->info.count;
01645         
01646         length = strlen(string) + 1 + ghosts;
01647         if (ENTRY_IN_REGION(entry)) {
01648             char * t = xmalloc(entry->length + length);
01649             memcpy(t, entry->data, entry->length);
01650             entry->data = t;
01651             entry->info.offset = 0;
01652         } else
01653             entry->data = xrealloc(entry->data, entry->length + length);
01654 
01655         memset(((char *)entry->data) + entry->length, '\0', ghosts);
01656         strcpy(((char *)entry->data) + entry->length + ghosts, string);
01657 
01658         entry->length += length;
01659         entry->info.count = langNum + 1;
01660     } else {
01661         char *b, *be, *e, *ee, *t;
01662         size_t bn, sn, en;
01663 
01664         /* Set beginning/end pointers to previous data */
01665         b = be = e = ee = entry->data;
01666         for (i = 0; i < table->info.count; i++) {
01667             if (i == langNum)
01668                 be = ee;
01669             ee += strlen(ee) + 1;
01670             if (i == langNum)
01671                 e  = ee;
01672         }
01673 
01674         /* Get storage for new buffer */
01675         bn = (be-b);
01676         sn = strlen(string) + 1;
01677         en = (ee-e);
01678         length = bn + sn + en;
01679         t = buf = xmalloc(length);
01680 
01681         /* Copy values into new storage */
01682         memcpy(t, b, bn);
01683         t += bn;
01684         memcpy(t, string, sn);
01685         t += sn;
01686         memcpy(t, e, en);
01687         t += en;
01688 
01689         /* Replace I18N string array */
01690         entry->length -= strlen(be) + 1;
01691         entry->length += sn;
01692         
01693         if (ENTRY_IN_REGION(entry)) {
01694             entry->info.offset = 0;
01695         } else
01696             free(entry->data);
01697         entry->data = buf;
01698     }
01699 
01700     return 0;
01701 }
01702 
01703 /* if there are multiple entries with this tag, the first one gets replaced */
01704 int headerModifyEntry(Header h, int_32 tag, int_32 type, void *p, int_32 c)
01705 {
01706     struct indexEntry *entry;
01707     void * oldData;
01708 
01709     /* First find the tag */
01710     entry = findEntry(h, tag, type);
01711     if (!entry)
01712         return 0;
01713 
01714     /* make sure entry points to the first occurence of this tag */
01715     while (entry > h->index && (entry - 1)->info.tag == tag)  
01716         entry--;
01717 
01718     /* free after we've grabbed the new data in case the two are intertwined;
01719        that's a bad idea but at least we won't break */
01720     oldData = entry->data;
01721 
01722     entry->info.count = c;
01723     entry->info.type = type;
01724     entry->data = grabData(type, p, c, &entry->length);
01725 
01726     if (ENTRY_IN_REGION(entry)) {
01727         entry->info.offset = 0;
01728     } else
01729         free(oldData);
01730 
01731     return 1;
01732 }
01733 
01734 int headerAddOrAppendEntry(Header h, int_32 tag, int_32 type,
01735                            void * p, int_32 c)
01736 {
01737     return (findEntry(h, tag, type)
01738         ? headerAppendEntry(h, tag, type, p, c)
01739         : headerAddEntry(h, tag, type, p, c));
01740 }
01741 
01742 int headerAppendEntry(Header h, int_32 tag, int_32 type, void * p, int_32 c)
01743 {
01744     struct indexEntry *entry;
01745     int length;
01746 
01747     /* First find the tag */
01748     entry = findEntry(h, tag, type);
01749     if (!entry)
01750         return 0;
01751 
01752     if (type == RPM_STRING_TYPE || type == RPM_I18NSTRING_TYPE) {
01753         /* we can't do this */
01754         return 0;
01755     }
01756 
01757     length = dataLength(type, p, c, 0);
01758 
01759     if (ENTRY_IN_REGION(entry)) {
01760         char * t = xmalloc(entry->length + length);
01761         memcpy(t, entry->data, entry->length);
01762         entry->data = t;
01763         entry->info.offset = 0;
01764     } else
01765         entry->data = xrealloc(entry->data, entry->length + length);
01766 
01767     copyData(type, ((char *) entry->data) + entry->length, p, c, length);
01768 
01769     entry->length += length;
01770 
01771     entry->info.count += c;
01772 
01773     return 1;
01774 }
01775 
01776 int headerRemoveEntry(Header h, int_32 tag)
01777 {
01778     struct indexEntry * last = h->index + h->indexUsed;
01779     struct indexEntry * entry, * first;
01780     int ne;
01781 
01782     entry = findEntry(h, tag, RPM_NULL_TYPE);
01783     if (!entry) return 1;
01784 
01785     /* Make sure entry points to the first occurence of this tag. */
01786     while (entry > h->index && (entry - 1)->info.tag == tag)  
01787         entry--;
01788 
01789     /* Free data for tags being removed. */
01790     for (first = entry; first < last; first++) {
01791         void * data;
01792         if (first->info.tag != tag)
01793             break;
01794         data = first->data;
01795         first->data = NULL;
01796         first->length = 0;
01797         if (ENTRY_IN_REGION(first))
01798             continue;
01799         free(data);
01800     }
01801 
01802     ne = (first - entry);
01803     if (ne > 0) {
01804         h->indexUsed -= ne;
01805         ne = last - first;
01806         if (ne > 0)
01807             memmove(entry, first, (ne * sizeof(*entry)));
01808     }
01809 
01810     return 0;
01811 }
01812 
01813 static char escapedChar(const char ch)  /*@*/
01814 {
01815     switch (ch) {
01816     case 'a':   return '\a';
01817     case 'b':   return '\b';
01818     case 'f':   return '\f';
01819     case 'n':   return '\n';
01820     case 'r':   return '\r';
01821     case 't':   return '\t';
01822     case 'v':   return '\v';
01823     default:    return ch;
01824     }
01825 }
01826 
01827 static void freeFormat( /*@only@*/ struct sprintfToken * format, int num)
01828 {
01829     int i;
01830 
01831     for (i = 0; i < num; i++) {
01832         switch (format[i].type) {
01833         case PTOK_ARRAY:
01834             freeFormat(format[i].u.array.format, format[i].u.array.numTokens);
01835             break;
01836         case PTOK_COND:
01837             freeFormat(format[i].u.cond.ifFormat, 
01838                         format[i].u.cond.numIfTokens);
01839             freeFormat(format[i].u.cond.elseFormat, 
01840                         format[i].u.cond.numElseTokens);
01841             break;
01842         case PTOK_NONE:
01843         case PTOK_TAG:
01844         case PTOK_STRING:
01845         default:
01846             break;
01847         }
01848     }
01849     free(format);
01850 }
01851 
01852 static void findTag(char * name, const struct headerTagTableEntry * tags, 
01853                     const struct headerSprintfExtension * extensions,
01854                     /*@out@*/const struct headerTagTableEntry ** tagMatch,
01855                     /*@out@*/const struct headerSprintfExtension ** extMatch)
01856         /*@modifies *tagMatch, *extMatch @*/
01857 {
01858     const struct headerTagTableEntry * entry;
01859     const struct headerSprintfExtension * ext;
01860     const char * tagname;
01861 
01862     *tagMatch = NULL;
01863     *extMatch = NULL;
01864 
01865     if (strncmp("RPMTAG_", name, sizeof("RPMTAG_")-1)) {
01866         char * t = alloca(strlen(name) + sizeof("RPMTAG_"));
01867         (void) stpcpy( stpcpy(t, "RPMTAG_"), name);
01868         tagname = t;
01869     } else {
01870         tagname = name;
01871     }
01872 
01873     /* Search extensions first to permit overriding header tags. */
01874     ext = extensions;
01875     while (ext->type != HEADER_EXT_LAST) {
01876         if (ext->type == HEADER_EXT_TAG && !strcasecmp(ext->name, tagname))
01877             break;
01878 
01879         if (ext->type == HEADER_EXT_MORE)
01880             ext = ext->u.more;
01881         else
01882             ext++;
01883     }
01884 
01885     if (ext->type == HEADER_EXT_TAG) {
01886         *extMatch = ext;
01887         return;
01888     }
01889 
01890     /* Search header tags. */
01891     for (entry = tags; entry->name; entry++)
01892         if (!strcasecmp(entry->name, tagname)) break;
01893 
01894     if (entry->name) {
01895         *tagMatch = entry;
01896         return;
01897     }
01898 }
01899 
01900 /* forward ref */
01901 static int parseExpression(struct sprintfToken * token, char * str, 
01902         const struct headerTagTableEntry * tags, 
01903         const struct headerSprintfExtension * extensions,
01904         /*@out@*/char ** endPtr, /*@out@*/const char ** errmsg)
01905                 /*@modifies str, *str, *token, *endPtr, *errmsg @*/;
01906 
01907 static int parseFormat(char * str, const struct headerTagTableEntry * tags,
01908         const struct headerSprintfExtension * extensions,
01909         /*@out@*/struct sprintfToken ** formatPtr, /*@out@*/int * numTokensPtr,
01910         /*@out@*/char ** endPtr, int state, /*@out@*/const char ** errmsg)
01911           /*@modifies str, *str, *formatPtr, *numTokensPtr, *endPtr, *errmsg @*/
01912 {
01913     char * chptr, * start, * next, * dst;
01914     struct sprintfToken * format;
01915     int numTokens;
01916     int currToken;
01917     const struct headerTagTableEntry * tag;
01918     const struct headerSprintfExtension * ext;
01919     int i;
01920     int done = 0;
01921 
01922     /* upper limit on number of individual formats */
01923     numTokens = 0;
01924     for (chptr = str; *chptr; chptr++)
01925         if (*chptr == '%') numTokens++;
01926     numTokens = numTokens * 2 + 1;
01927 
01928     format = xcalloc(numTokens, sizeof(*format));
01929     if (endPtr) *endPtr = NULL;
01930 
01931     /*@-infloops@*/
01932     dst = start = str;
01933     currToken = -1;
01934     while (*start) {
01935         switch (*start) {
01936         case '%':
01937             /* handle %% */
01938             if (*(start + 1) == '%') {
01939                 if (currToken < 0 || format[currToken].type != PTOK_STRING) {
01940                     currToken++;
01941                     format[currToken].type = PTOK_STRING;
01942                     dst = format[currToken].u.string.string = start;
01943                 }
01944 
01945                 start++;
01946 
01947                 *dst++ = *start++;
01948 
01949                 break; /* out of switch */
01950             } 
01951 
01952             currToken++;
01953             *dst++ = '\0';
01954             start++;
01955 
01956             if (*start == '|') {
01957                 char * newEnd;
01958 
01959                 start++;
01960                 if (parseExpression(format + currToken, start, tags, 
01961                                     extensions, &newEnd, errmsg)) {
01962                     freeFormat(format, numTokens);
01963                     return 1;
01964                 }
01965                 start = newEnd;
01966                 break; /* out of switch */
01967             }
01968 
01969             format[currToken].u.tag.format = start;
01970             format[currToken].u.tag.pad = 0;
01971             format[currToken].u.tag.justOne = 0;
01972             format[currToken].u.tag.arrayCount = 0;
01973 
01974             chptr = start;
01975             while (*chptr && *chptr != '{' && *chptr != '%') chptr++;
01976             if (!*chptr || *chptr == '%') {
01977                 /*@-observertrans@*/
01978                 *errmsg = _("missing { after %");
01979                 /*@=observertrans@*/
01980                 freeFormat(format, numTokens);
01981                 return 1;
01982             }
01983 
01984             *chptr++ = '\0';
01985 
01986             while (start < chptr) {
01987                 if (isdigit(*start)) {
01988                     i = strtoul(start, &start, 10);
01989                     format[currToken].u.tag.pad += i;
01990                 } else {
01991                     start++;
01992                 }
01993             }
01994 
01995             if (*start == '=') {
01996                 format[currToken].u.tag.justOne = 1;
01997                 start++;
01998             } else if (*start == '#') {
01999                 format[currToken].u.tag.justOne = 1;
02000                 format[currToken].u.tag.arrayCount = 1;
02001                 start++;
02002             }
02003 
02004             next = start;
02005             while (*next && *next != '}') next++;
02006             if (!*next) {
02007                 /*@-observertrans@*/
02008                 *errmsg = _("missing } after %{");
02009                 /*@=observertrans@*/
02010                 freeFormat(format, numTokens);
02011                 return 1;
02012             }
02013             *next++ = '\0';
02014 
02015             chptr = start;
02016             while (*chptr && *chptr != ':') chptr++;
02017 
02018             if (*chptr) {
02019                 *chptr++ = '\0';
02020                 if (!*chptr) {
02021                     /*@-observertrans@*/
02022                     *errmsg = _("empty tag format");
02023                     /*@=observertrans@*/
02024                     freeFormat(format, numTokens);
02025                     return 1;
02026                 }
02027                 format[currToken].u.tag.type = chptr;
02028             } else {
02029                 format[currToken].u.tag.type = NULL;
02030             }
02031             
02032             if (!*start) {
02033                 /*@-observertrans@*/
02034                 *errmsg = _("empty tag name");
02035                 /*@=observertrans@*/
02036                 freeFormat(format, numTokens);
02037                 return 1;
02038             }
02039 
02040             i = 0;
02041             findTag(start, tags, extensions, &tag, &ext);
02042 
02043             if (tag) {
02044                 format[currToken].u.tag.ext = NULL;
02045                 format[currToken].u.tag.tag = tag->val;
02046             } else if (ext) {
02047                 format[currToken].u.tag.ext = ext->u.tagFunction;
02048                 format[currToken].u.tag.extNum = ext - extensions;
02049             } else {
02050                 /*@-observertrans@*/
02051                 *errmsg = _("unknown tag");
02052                 /*@=observertrans@*/
02053                 freeFormat(format, numTokens);
02054                 return 1;
02055             }
02056 
02057             format[currToken].type = PTOK_TAG;
02058 
02059             start = next;
02060 
02061             break;
02062 
02063         case '[':
02064             *dst++ = '\0';
02065             *start++ = '\0';
02066             currToken++;
02067 
02068             if (parseFormat(start, tags, extensions, 
02069                             &format[currToken].u.array.format,
02070                             &format[currToken].u.array.numTokens,
02071                             &start, PARSER_IN_ARRAY, errmsg)) {
02072                 freeFormat(format, numTokens);
02073                 return 1;
02074             }
02075 
02076             if (!start) {
02077                 /*@-observertrans@*/
02078                 *errmsg = _("] expected at end of array");
02079                 /*@=observertrans@*/
02080                 freeFormat(format, numTokens);
02081                 return 1;
02082             }
02083 
02084             dst = start;
02085 
02086             format[currToken].type = PTOK_ARRAY;
02087 
02088             break;
02089 
02090         case ']':
02091         case '}':
02092             if ((*start == ']' && state != PARSER_IN_ARRAY) ||
02093                 (*start == '}' && state != PARSER_IN_EXPR)) {
02094                 if (*start == ']')
02095                     /*@-observertrans@*/
02096                     *errmsg = _("unexpected ]");
02097                     /*@=observertrans@*/
02098                 else
02099                     /*@-observertrans@*/
02100                     *errmsg = _("unexpected }");
02101                     /*@=observertrans@*/
02102                 freeFormat(format, numTokens);
02103                 return 1;
02104             }
02105             *start++ = '\0';
02106             *endPtr = start;
02107             done = 1;
02108             break;
02109 
02110         default:
02111             if (currToken < 0 || format[currToken].type != PTOK_STRING) {
02112                 currToken++;
02113                 format[currToken].type = PTOK_STRING;
02114                 dst = format[currToken].u.string.string = start;
02115             }
02116 
02117             if (*start == '\\') {
02118                 start++;
02119                 *dst++ = escapedChar(*start++);
02120             } else {
02121                 *dst++ = *start++;
02122             }
02123             break;
02124         }
02125         if (done)
02126             break;
02127     }
02128     /*@=infloops@*/
02129 
02130     *dst = '\0';
02131 
02132     currToken++;
02133     for (i = 0; i < currToken; i++) {
02134         if (format[i].type == PTOK_STRING)
02135             format[i].u.string.len = strlen(format[i].u.string.string);
02136     }
02137 
02138     *numTokensPtr = currToken;
02139     *formatPtr = format;
02140 
02141     return 0;
02142 }
02143 
02144 static int parseExpression(struct sprintfToken * token, char * str, 
02145         const struct headerTagTableEntry * tags, 
02146         const struct headerSprintfExtension * extensions,
02147         /*@out@*/ char ** endPtr, /*@out@*/ const char ** errmsg)
02148 {
02149     const struct headerTagTableEntry * tag;
02150     const struct headerSprintfExtension * ext;
02151     char * chptr;
02152     char * end;
02153 
02154     *errmsg = NULL;
02155     chptr = str;
02156     while (*chptr && *chptr != '?') chptr++;
02157 
02158     if (*chptr != '?') {
02159         /*@-observertrans@*/
02160         *errmsg = _("? expected in expression");
02161         /*@=observertrans@*/
02162         return 1;
02163     }
02164 
02165     *chptr++ = '\0';;
02166 
02167     if (*chptr != '{') {
02168         /*@-observertrans@*/
02169         *errmsg = _("{ expected after ? in expression");
02170         /*@=observertrans@*/
02171         return 1;
02172     }
02173 
02174     chptr++;
02175 
02176     if (parseFormat(chptr, tags, extensions, &token->u.cond.ifFormat, 
02177                     &token->u.cond.numIfTokens, &end, PARSER_IN_EXPR, errmsg)) 
02178         return 1;
02179 
02180     if (!*end) {
02181         /*@-observertrans@*/
02182         *errmsg = _("} expected in expression");
02183         /*@=observertrans@*/
02184         freeFormat(token->u.cond.ifFormat, token->u.cond.numIfTokens);
02185         token->u.cond.ifFormat = NULL;
02186         return 1;
02187     }
02188 
02189     chptr = end;
02190     if (*chptr != ':' && *chptr != '|') {
02191         /*@-observertrans@*/
02192         *errmsg = _(": expected following ? subexpression");
02193         /*@=observertrans@*/
02194         freeFormat(token->u.cond.ifFormat, token->u.cond.numIfTokens);
02195         token->u.cond.ifFormat = NULL;
02196         return 1;
02197     }
02198 
02199     if (*chptr == '|') {
02200         parseFormat(xstrdup(""), tags, extensions, &token->u.cond.elseFormat, 
02201                         &token->u.cond.numElseTokens, &end, PARSER_IN_EXPR, 
02202                         errmsg);
02203     } else {
02204         chptr++;
02205 
02206         if (*chptr != '{') {
02207             /*@-observertrans@*/
02208             *errmsg = _("{ expected after : in expression");
02209             /*@=observertrans@*/
02210             freeFormat(token->u.cond.ifFormat, token->u.cond.numIfTokens);
02211             token->u.cond.ifFormat = NULL;
02212             return 1;
02213         }
02214 
02215         chptr++;
02216 
02217         if (parseFormat(chptr, tags, extensions, &token->u.cond.elseFormat, 
02218                         &token->u.cond.numElseTokens, &end, PARSER_IN_EXPR, 
02219                         errmsg)) 
02220             return 1;
02221         if (!*end) {
02222             /*@-observertrans@*/
02223             *errmsg = _("} expected in expression");
02224             /*@=observertrans@*/
02225             freeFormat(token->u.cond.ifFormat, token->u.cond.numIfTokens);
02226             token->u.cond.ifFormat = NULL;
02227             return 1;
02228         }
02229 
02230         chptr = end;
02231         if (*chptr != '|') {
02232             /*@-observertrans@*/
02233             *errmsg = _("| expected at end of expression");
02234             /*@=observertrans@*/
02235             freeFormat(token->u.cond.ifFormat, token->u.cond.numIfTokens);
02236             token->u.cond.ifFormat = NULL;
02237             freeFormat(token->u.cond.elseFormat, token->u.cond.numElseTokens);
02238             token->u.cond.elseFormat = NULL;
02239             return 1;
02240         }
02241     }
02242         
02243     chptr++;
02244 
02245     *endPtr = chptr;
02246 
02247     findTag(str, tags, extensions, &tag, &ext);
02248 
02249     if (tag) {
02250         token->u.cond.tag.ext = NULL;
02251         token->u.cond.tag.tag = tag->val;
02252     } else if (ext) {
02253         token->u.cond.tag.ext = ext->u.tagFunction;
02254         token->u.cond.tag.extNum = ext - extensions;
02255     } else {
02256         token->u.cond.tag.ext = NULL;
02257         token->u.cond.tag.tag = -1;
02258     }
02259         
02260     token->type = PTOK_COND;
02261 
02262     return 0;
02263 }
02264 
02265 static int getExtension(Header h, headerTagTagFunction fn,
02266         /*@out@*/ int_32 * typeptr, /*@out@*/ const void ** data,
02267         /*@out@*/ int_32 * countptr, struct extensionCache * ext)
02268                 /*@modifies *typeptr, *data, *countptr, ext->avail @*/
02269 {
02270     if (!ext->avail) {
02271         if (fn(h, &ext->type, &ext->data, &ext->count, &ext->freeit))
02272             return 1;
02273         ext->avail = 1;
02274     }
02275 
02276     *typeptr = ext->type;
02277     *data = ext->data;
02278     *countptr = ext->count;
02279 
02280     return 0;
02281 }
02282 
02283 static char * formatValue(struct sprintfTag * tag, Header h, 
02284                           const struct headerSprintfExtension * extensions,
02285                           struct extensionCache * extCache, int element)
02286                 /*@modifies h, extCache->avail @*/
02287 {
02288     int len;
02289     char buf[20];
02290     int_32 count, type;
02291     const void * data;
02292     unsigned int intVal;
02293     char * val = NULL;
02294     const char ** strarray;
02295     int mayfree = 0;
02296     int countBuf;
02297     headerTagFormatFunction tagtype = NULL;
02298     const struct headerSprintfExtension * ext;
02299 
02300     if (tag->ext) {
02301         if (getExtension(h, tag->ext, &type, &data, &count, 
02302                          extCache + tag->extNum)) {
02303             count = 1;
02304             type = RPM_STRING_TYPE;     
02305             data = "(none)";            /* XXX i18n? NO!, sez; gafton */
02306         }
02307     } else {
02308         if (!headerGetEntry(h, tag->tag, &type, (void **)&data, &count)){
02309             count = 1;
02310             type = RPM_STRING_TYPE;     
02311             data = "(none)";            /* XXX i18n? NO!, sez; gafton */
02312         }
02313 
02314         mayfree = 1;
02315     }
02316 
02317     if (tag->arrayCount) {
02318         /*@-observertrans -modobserver@*/
02319         data = headerFreeData(data, type);
02320         /*@=observertrans =modobserver@*/
02321 
02322         countBuf = count;
02323         data = &countBuf;
02324         count = 1;
02325         type = RPM_INT32_TYPE;
02326     }
02327 
02328     (void) stpcpy( stpcpy(buf, "%"), tag->format);
02329 
02330     if (tag->type) {
02331         ext = extensions;
02332         while (ext->type != HEADER_EXT_LAST) {
02333             if (ext->type == HEADER_EXT_FORMAT && 
02334                 !strcmp(ext->name, tag->type)) {
02335                 tagtype = ext->u.formatFunction;
02336                 break;
02337             }
02338 
02339             if (ext->type == HEADER_EXT_MORE)
02340                 ext = ext->u.more;
02341             else
02342                 ext++;
02343         }
02344     }
02345     
02346     switch (type) {
02347     case RPM_STRING_ARRAY_TYPE:
02348         strarray = (const char **)data;
02349 
02350         if (tagtype)
02351             val = tagtype(RPM_STRING_TYPE, strarray[element], buf, tag->pad, 0);
02352 
02353         if (!val) {
02354             strcat(buf, "s");
02355 
02356             len = strlen(strarray[element]) + tag->pad + 20;
02357             val = xmalloc(len);
02358             sprintf(val, buf, strarray[element]);
02359         }
02360 
02361         /*@-observertrans -modobserver@*/
02362         if (mayfree) free((void *)data);
02363         /*@=observertrans =modobserver@*/
02364 
02365         break;
02366 
02367     case RPM_STRING_TYPE:
02368         if (tagtype)
02369             val = tagtype(RPM_STRING_ARRAY_TYPE, data, buf, tag->pad,  0);
02370 
02371         if (!val) {
02372             strcat(buf, "s");
02373 
02374             len = strlen(data) + tag->pad + 20;
02375             val = xmalloc(len);
02376             sprintf(val, buf, data);
02377         }
02378         break;
02379 
02380     case RPM_CHAR_TYPE:
02381     case RPM_INT8_TYPE:
02382     case RPM_INT16_TYPE:
02383     case RPM_INT32_TYPE:
02384         switch (type) {
02385         case RPM_CHAR_TYPE:     
02386         case RPM_INT8_TYPE:     intVal = *(((int_8 *) data) + element); break;
02387         case RPM_INT16_TYPE:    intVal = *(((uint_16 *) data) + element); break;
02388         default:                /* keep -Wall quiet */
02389         case RPM_INT32_TYPE:    intVal = *(((int_32 *) data) + element); break;
02390         }
02391 
02392         if (tagtype)
02393             val = tagtype(RPM_INT32_TYPE, &intVal, buf, tag->pad,  element);
02394 
02395         if (!val) {
02396             strcat(buf, "d");
02397             len = 10 + tag->pad + 20;
02398             val = xmalloc(len);
02399             sprintf(val, buf, intVal);
02400         }
02401         break;
02402 
02403     default:
02404         val = xstrdup(_("(unknown type)"));
02405         break;
02406     }
02407 
02408     return val;
02409 }
02410 
02411 static const char * singleSprintf(Header h, struct sprintfToken * token,
02412                             const struct headerSprintfExtension * extensions,
02413                             struct extensionCache * extCache, int element)
02414                 /*@modifies h, extCache->avail @*/
02415 {
02416     char * val;
02417     const char * thisItem;
02418     int thisItemLen;
02419     int len, alloced;
02420     int i, j;
02421     int numElements;
02422     int type;
02423     struct sprintfToken * condFormat;
02424     int condNumFormats;
02425 
02426     /* we assume the token and header have been validated already! */
02427 
02428     switch (token->type) {
02429     case PTOK_NONE:
02430         break;
02431 
02432     case PTOK_STRING:
02433         val = xmalloc(token->u.string.len + 1);
02434         strcpy(val, token->u.string.string);
02435         break;
02436 
02437     case PTOK_TAG:
02438         val = formatValue(&token->u.tag, h, extensions, extCache,
02439                           token->u.tag.justOne ? 0 : element);
02440         break;
02441 
02442     case PTOK_COND:
02443         if (token->u.cond.tag.ext ||
02444             headerIsEntry(h, token->u.cond.tag.tag)) {
02445             condFormat = token->u.cond.ifFormat;
02446             condNumFormats = token->u.cond.numIfTokens;
02447         } else {
02448             condFormat = token->u.cond.elseFormat;
02449             condNumFormats = token->u.cond.numElseTokens;
02450         }
02451 
02452         alloced = condNumFormats * 20;
02453         val = xmalloc(alloced ? alloced : 1);
02454         *val = '\0';
02455         len = 0;
02456 
02457         for (i = 0; i < condNumFormats; i++) {
02458             thisItem = singleSprintf(h, condFormat + i, 
02459                                      extensions, extCache, element);
02460             thisItemLen = strlen(thisItem);
02461             if ((thisItemLen + len) >= alloced) {
02462                 alloced = (thisItemLen + len) + 200;
02463                 val = xrealloc(val, alloced);   
02464             }
02465             strcat(val, thisItem);
02466             len += thisItemLen;
02467             free((void *)thisItem);
02468         }
02469 
02470         break;
02471 
02472     case PTOK_ARRAY:
02473         numElements = -1;
02474         for (i = 0; i < token->u.array.numTokens; i++) {
02475             if (token->u.array.format[i].type != PTOK_TAG ||
02476                 token->u.array.format[i].u.tag.arrayCount ||
02477                 token->u.array.format[i].u.tag.justOne) continue;
02478 
02479             if (token->u.array.format[i].u.tag.ext) {
02480                 const void * data;
02481                 if (getExtension(h, token->u.array.format[i].u.tag.ext,
02482                                  &type, &data, &numElements, 
02483                                  extCache + 
02484                                    token->u.array.format[i].u.tag.extNum))
02485                      continue;
02486             } else {
02487                 if (!headerGetEntry(h, token->u.array.format[i].u.tag.tag, 
02488                                     &type, (void **) &val, &numElements))
02489                     continue;
02490                 val = headerFreeData(val, type);
02491             } 
02492             break;
02493         }
02494 
02495         if (numElements == -1) {
02496             val = xstrdup("(none)");    /* XXX i18n? NO!, sez; gafton */
02497         } else {
02498             alloced = numElements * token->u.array.numTokens * 20;
02499             val = xmalloc(alloced);
02500             *val = '\0';
02501             len = 0;
02502 
02503             for (j = 0; j < numElements; j++) {
02504                 for (i = 0; i < token->u.array.numTokens; i++) {
02505                     thisItem = singleSprintf(h, token->u.array.format + i, 
02506                                              extensions, extCache, j);
02507                     thisItemLen = strlen(thisItem);
02508                     if ((thisItemLen + len) >= alloced) {
02509                         alloced = (thisItemLen + len) + 200;
02510                         val = xrealloc(val, alloced);   
02511                     }
02512                     strcat(val, thisItem);
02513                     len += thisItemLen;
02514                     free((void *)thisItem);
02515                 }
02516             }
02517         }
02518            
02519         break;
02520     }
02521 
02522     return val;
02523 }
02524 
02525 static struct extensionCache * allocateExtensionCache(
02526                      const struct headerSprintfExtension * extensions)
02527                 /*@*/
02528 {
02529     const struct headerSprintfExtension * ext = extensions;
02530     int i = 0;
02531 
02532     while (ext->type != HEADER_EXT_LAST) {
02533         i++;
02534         if (ext->type == HEADER_EXT_MORE)
02535             ext = ext->u.more;
02536         else
02537             ext++;
02538     }
02539 
02540     return xcalloc(i, sizeof(struct extensionCache));
02541 }
02542 
02543 static void freeExtensionCache(const struct headerSprintfExtension * extensions,
02544                         /*@only@*/struct extensionCache * cache)
02545 {
02546     const struct headerSprintfExtension * ext = extensions;
02547     int i = 0;
02548 
02549     while (ext->type != HEADER_EXT_LAST) {
02550         if (cache[i].freeit) free((void *)cache[i].data);
02551 
02552         i++;
02553         if (ext->type == HEADER_EXT_MORE)
02554             ext = ext->u.more;
02555         else
02556             ext++;
02557     }
02558 
02559     free(cache);
02560 }
02561 
02562 char * headerSprintf(Header h, const char * origFmt, 
02563                      const struct headerTagTableEntry * tags,
02564                      const struct headerSprintfExtension * extensions,
02565                      const char ** errmsg)
02566 {
02567     char * fmtString;
02568     struct sprintfToken * format;
02569     int numTokens;
02570     char * answer;
02571     int answerLength;
02572     int answerAlloced;
02573     int i;
02574     struct extensionCache * extCache;
02575  
02576     /*fmtString = escapeString(origFmt);*/
02577     fmtString = xstrdup(origFmt);
02578    
02579     if (parseFormat(fmtString, tags, extensions, &format, &numTokens, 
02580                     NULL, PARSER_BEGIN, errmsg)) {
02581         free(fmtString);
02582         return NULL;
02583     }
02584 
02585     extCache = allocateExtensionCache(extensions);
02586 
02587     answerAlloced = 1024;
02588     answerLength = 0;
02589     answer = xmalloc(answerAlloced);
02590     *answer = '\0';
02591 
02592     for (i = 0; i < numTokens; i++) {
02593         const char * piece;
02594         int pieceLength;
02595 
02596         piece = singleSprintf(h, format + i, extensions, extCache, 0);
02597         if (piece) {
02598             pieceLength = strlen(piece);
02599             if ((answerLength + pieceLength) >= answerAlloced) {
02600                 while ((answerLength + pieceLength) >= answerAlloced) 
02601                     answerAlloced += 1024;
02602                 answer = xrealloc(answer, answerAlloced);
02603             }
02604 
02605             strcat(answer, piece);
02606             answerLength += pieceLength;
02607             free((void *)piece);
02608         }
02609     }
02610 
02611     free(fmtString);
02612     freeExtensionCache(extensions, extCache);
02613     free(format);
02614 
02615     return answer;
02616 }
02617 
02618 static char * octalFormat(int_32 type, const void * data, 
02619                 char * formatPrefix, int padding, /*@unused@*/int element)
02620                 /*@modifies formatPrefix @*/
02621 {
02622     char * val;
02623 
02624     if (type != RPM_INT32_TYPE) {
02625         val = xstrdup(_("(not a number)"));
02626     } else {
02627         val = xmalloc(20 + padding);
02628         strcat(formatPrefix, "o");
02629         sprintf(val, formatPrefix, *((int_32 *) data));
02630     }
02631 
02632     return val;
02633 }
02634 
02635 static char * hexFormat(int_32 type, const void * data, 
02636                 char * formatPrefix, int padding, /*@unused@*/int element)
02637                 /*@modifies formatPrefix @*/
02638 {
02639     char * val;
02640 
02641     if (type != RPM_INT32_TYPE) {
02642         val = xstrdup(_("(not a number)"));
02643     } else {
02644         val = xmalloc(20 + padding);
02645         strcat(formatPrefix, "x");
02646         sprintf(val, formatPrefix, *((int_32 *) data));
02647     }
02648 
02649     return val;
02650 }
02651 
02652 static char * realDateFormat(int_32 type, const void * data, 
02653                 char * formatPrefix, int padding, /*@unused@*/int element,
02654                 char * strftimeFormat)
02655                 /*@modifies formatPrefix @*/
02656 {
02657     char * val;
02658     struct tm * tstruct;
02659     char buf[50];
02660 
02661     if (type != RPM_INT32_TYPE) {
02662         val = xstrdup(_("(not a number)"));
02663     } else {
02664         val = xmalloc(50 + padding);
02665         strcat(formatPrefix, "s");
02666 
02667         /* this is important if sizeof(int_32) ! sizeof(time_t) */
02668         {   time_t dateint = *((int_32 *) data);
02669             tstruct = localtime(&dateint);
02670         }
02671         (void)strftime(buf, sizeof(buf) - 1, strftimeFormat, tstruct);
02672         sprintf(val, formatPrefix, buf);
02673     }
02674 
02675     return val;
02676 }
02677 
02678 static char * dateFormat(int_32 type, const void * data, 
02679                          char * formatPrefix, int padding, int element)
02680                 /*@modifies formatPrefix @*/
02681 {
02682     return realDateFormat(type, data, formatPrefix, padding, element, "%c");
02683 }
02684 
02685 static char * dayFormat(int_32 type, const void * data, 
02686                          char * formatPrefix, int padding, int element)
02687                 /*@modifies formatPrefix @*/
02688 {
02689     return realDateFormat(type, data, formatPrefix, padding, element, 
02690                           "%a %b %d %Y");
02691 }
02692 
02693 static char * shescapeFormat(int_32 type, const void * data, 
02694                 char * formatPrefix, int padding, /*@unused@*/int element)
02695                 /*@modifies formatPrefix @*/
02696 {
02697     char * result, * dst, * src, * buf;
02698 
02699     if (type == RPM_INT32_TYPE) {
02700         result = xmalloc(padding + 20);
02701         strcat(formatPrefix, "d");
02702         sprintf(result, formatPrefix, *((int_32 *) data));
02703     } else {
02704         buf = alloca(strlen(data) + padding + 2);
02705         strcat(formatPrefix, "s");
02706         sprintf(buf, formatPrefix, data);
02707 
02708         result = dst = xmalloc(strlen(buf) * 4 + 3);
02709         *dst++ = '\'';
02710         for (src = buf; *src; src++) {
02711             if (*src == '\'') {
02712                 *dst++ = '\'';
02713                 *dst++ = '\\';
02714                 *dst++ = '\'';
02715                 *dst++ = '\'';
02716             } else {
02717                 *dst++ = *src;
02718             }
02719         }
02720         *dst++ = '\'';
02721         *dst = '\0';
02722 
02723     }
02724 
02725     return result;
02726 }
02727 
02728 const struct headerSprintfExtension headerDefaultFormats[] = {
02729     { HEADER_EXT_FORMAT, "octal", { octalFormat } },
02730     { HEADER_EXT_FORMAT, "hex", { hexFormat } },
02731     { HEADER_EXT_FORMAT, "date", { dateFormat } },
02732     { HEADER_EXT_FORMAT, "day", { dayFormat } },
02733     { HEADER_EXT_FORMAT, "shescape", { shescapeFormat } },
02734     { HEADER_EXT_LAST, NULL, { NULL } }
02735 };
02736 
02737 void headerCopyTags(Header headerFrom, Header headerTo, int *tagstocopy)
02738 {
02739     int *p;
02740 
02741     if (headerFrom == headerTo)
02742         return;
02743 
02744     for (p = tagstocopy; *p != 0; p++) {
02745         char *s;
02746         int type, count;
02747         if (headerIsEntry(headerTo, *p))
02748             continue;
02749         if (!headerGetEntryMinMemory(headerFrom, *p, &type,
02750                                 (const void **) &s, &count))
02751             continue;
02752         headerAddEntry(headerTo, *p, type, s, count);
02753         s = headerFreeData(s, type);
02754     }
02755 }

Generated at Thu Apr 19 15:29:43 2001 for rpm by doxygen1.2.6-20010408 written by Dimitri van Heesch, © 1997-2001