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

Generated at Sun Apr 8 18:42:59 2001 for rpm by doxygen1.2.3 written by Dimitri van Heesch, © 1997-2000