- globalize _free(3) wrapper in rpmlib.h, consistent usage throughout.
[platform/upstream/rpm.git] / lib / header.c
1 /** \ingroup header
2  * \file lib/header.c
3  */
4
5 #undef  REMALLOC_HEADER_REGION
6 #define _DEBUG_SWAB 1
7 #define _DEBUG_INDEX 1
8
9 /* RPM - Copyright (C) 1995-2000 Red Hat Software */
10
11 /* Data written to file descriptors is in network byte order.    */
12 /* Data read from file descriptors is expected to be in          */
13 /* network byte order and is converted on the fly to host order. */
14
15 #include "system.h"
16
17 #if !defined(__LCLINT__)
18 #include <netinet/in.h>
19 #endif  /* __LCLINT__ */
20
21 #include <header.h>
22
23 #include "debug.h"
24
25 /* XXX avoid rpmlib.h, need for debugging. */
26 /*@observer@*/ const char *const tagName(int tag)       /*@*/;
27
28 /*
29  * Teach header.c about legacy tags.
30  */
31 #define HEADER_OLDFILENAMES     1027
32 #define HEADER_BASENAMES        1117
33
34 #define INDEX_MALLOC_SIZE 8
35
36 #define PARSER_BEGIN    0
37 #define PARSER_IN_ARRAY 1
38 #define PARSER_IN_EXPR  2
39
40 static unsigned char header_magic[8] = {
41         0x8e, 0xad, 0xe8, 0x01, 0x00, 0x00, 0x00, 0x00
42 };
43
44 /** \ingroup header
45  * Alignment needs (and sizeof scalars types) for internal rpm data types.
46  */
47 static int typeSizes[] =  { 
48         0,      /*!< RPM_NULL_TYPE */
49         1,      /*!< RPM_CHAR_TYPE */
50         1,      /*!< RPM_INT8_TYPE */
51         2,      /*!< RPM_INT16_TYPE */
52         4,      /*!< RPM_INT32_TYPE */
53         -1,     /*!< RPM_INT64_TYPE */
54         -1,     /*!< RPM_STRING_TYPE */
55         1,      /*!< RPM_BIN_TYPE */
56         -1,     /*!< RPM_STRING_ARRAY_TYPE */
57         -1      /*!< RPM_I18NSTRING_TYPE */
58 };
59
60 /**
61  * Description of tag data.
62  */
63 struct entryInfo {
64     int_32 tag;                 /*!< Tag identifier. */
65     int_32 type;                /*!< Tag data type. */
66     int_32 offset;              /*!< Offset into data segment (ondisk only). */
67     int_32 count;               /*!< Number of tag elements. */
68 };
69
70 #define REGION_TAG_TYPE         RPM_BIN_TYPE
71 #define REGION_TAG_COUNT        sizeof(struct entryInfo)
72
73 #define ENTRY_IS_REGION(_e)     ((_e)->info.tag < HEADER_I18NTABLE)
74 #define ENTRY_IN_REGION(_e)     ((_e)->info.offset < 0)
75
76 /**
77  * A single tag from a Header.
78  */
79 struct indexEntry {
80     struct entryInfo info;      /*!< Description of tag data. */
81 /*@owned@*/ void * data;        /*!< Location of tag data. */
82     int length;                 /*!< No. bytes of data. */
83     int rdlen;                  /*!< No. bytes of data in region. */
84 };
85
86 /**
87  * The Header data structure.
88  */
89 struct headerToken {
90 /*@owned@*/ struct indexEntry *index;   /*!< Array of tags. */
91     int indexUsed;              /*!< Current size of tag array. */
92     int indexAlloced;           /*!< Allocated size of tag array. */
93     int region_allocated;       /*!< Is 1st header region allocated? */
94     int sorted;                 /*!< Are header entries sorted? */
95     int legacy;                 /*!< Header came from legacy source? */
96 /*@refs@*/ int nrefs;   /*!< Reference count. */
97 };
98
99 /**
100  */
101 struct sprintfTag {
102     headerTagTagFunction ext;   /*!< if NULL tag element is invalid */
103     int extNum;
104     int_32 tag;
105     int justOne;
106     int arrayCount;
107 /*@kept@*/ char * format;
108 /*@kept@*/ char * type;
109     int pad;
110 };
111
112 /**
113  */
114 struct extensionCache {
115     int_32 type;
116     int_32 count;
117     int avail;
118     int freeit;
119 /*@owned@*/ const void * data;
120 };
121
122 /**
123  */
124 struct sprintfToken {
125     enum {
126         PTOK_NONE = 0,
127         PTOK_TAG,
128         PTOK_ARRAY,
129         PTOK_STRING,
130         PTOK_COND
131     } type;
132     union {
133         struct {
134             /*@only@*/ struct sprintfToken * format;
135             int numTokens;
136         } array;
137         struct sprintfTag tag;
138         struct {
139             /*@dependent@*/ char * string;
140             int len;
141         } string;
142         struct {
143             /*@only@*/ struct sprintfToken * ifFormat;
144             int numIfTokens;
145             /*@only@*/ struct sprintfToken * elseFormat;
146             int numElseTokens;
147             struct sprintfTag tag;
148         } cond;
149     } u;
150 };
151
152 /**
153  * Return length of entry data.
154  * @param type          entry data type
155  * @param p             entry data
156  * @param count         entry item count
157  * @param onDisk        data is concatenated strings (with NUL's))?
158  * @return              no. bytes in data
159  */
160 /*@mayexit@*/
161 static int dataLength(int_32 type, const void * p, int_32 count, int onDisk)
162         /*@*/
163 {
164     int length = 0;
165
166     switch (type) {
167     case RPM_STRING_TYPE:
168         if (count == 1) {       /* Special case -- p is just the string */
169             length = strlen(p) + 1;
170             break;
171         }
172         /* This should not be allowed */
173         fprintf(stderr, _("dataLength() RPM_STRING_TYPE count must be 1.\n"));
174         exit(EXIT_FAILURE);
175         /*@notreached@*/ break;
176
177     case RPM_STRING_ARRAY_TYPE:
178     case RPM_I18NSTRING_TYPE:
179     {   int i;
180
181         /* This is like RPM_STRING_TYPE, except it's *always* an array */
182         /* Compute sum of length of all strings, including null terminators */
183         i = count;
184
185         if (onDisk) {
186             const char * chptr = p;
187             int thisLen;
188
189             while (i--) {
190                 thisLen = strlen(chptr) + 1;
191                 length += thisLen;
192                 chptr += thisLen;
193             }
194         } else {
195             const char ** src = (const char **)p;
196             while (i--) {
197                 /* add one for null termination */
198                 length += strlen(*src++) + 1;
199             }
200         }
201     }   break;
202
203     default:
204         if (typeSizes[type] != -1) {
205             length = typeSizes[type] * count;
206             break;
207         }
208         fprintf(stderr, _("Data type %d not supported\n"), (int) type);
209         exit(EXIT_FAILURE);
210         /*@notreached@*/ break;
211     }
212
213     return length;
214 }
215
216 /**
217  * Swap int_32 and int_16 arrays within header region.
218  *
219  * This code is way more twisty than I would like.
220  *
221  * A bug with RPM_I18NSTRING_TYPE in rpm-2.5.x (fixed in August 1998)
222  * causes the offset and length of elements in a header region to disagree
223  * regarding the total length of the region data.
224  *
225  * The "fix" is to compute the size using both offset and length and
226  * return the larger of the two numbers as the size of the region.
227  * Kinda like computing left and right Riemann sums of the data elements
228  * to determine the size of a data structure, go figger :-).
229  *
230  * There's one other twist if a header region tag is in the set to be swabbed,
231  * as the data for a header region is located after all other tag data.
232  *
233  * @param entry         header entry
234  * @param il            no. of entries
235  * @param dl            start no. bytes of data
236  * @param pe            header physical entry pointer (swapped)
237  * @param dataStart     header data
238  * @param regionid      region offset
239  * @return              no. bytes of data in region
240  */
241 static int regionSwab(struct indexEntry * entry, int il, int dl,
242                 const struct entryInfo * pe, char * dataStart, int regionid)
243 {
244     char * tprev = NULL;
245     char * t = NULL;
246     int tdel, tl = dl;
247
248     for (; il > 0; il--, pe++) {
249         struct indexEntry ie;
250         int_32 type;
251
252         ie.info.tag = ntohl(pe->tag);
253         ie.info.type = ntohl(pe->type);
254         ie.info.count = ntohl(pe->count);
255         ie.info.offset = ntohl(pe->offset);
256         ie.data = t = dataStart + ie.info.offset;
257         ie.length = dataLength(ie.info.type, ie.data, ie.info.count, 1);
258         ie.rdlen = 0;
259
260 assert(ie.info.type >= RPM_MIN_TYPE && ie.info.type <= RPM_MAX_TYPE);
261
262         if (entry) {
263             ie.info.offset = regionid;
264             *entry = ie;        /* structure assignment */
265             entry++;
266         }
267
268         /* Alignment */
269         type = ie.info.type;
270         if (typeSizes[type] > 1) {
271             unsigned diff;
272             diff = typeSizes[type] - (dl % typeSizes[type]);
273             if (diff != typeSizes[type]) {
274                 dl += diff;
275             }
276         }
277         tdel = (tprev ? (t - tprev) : 0);
278         dl += ie.length;
279         tl += tdel;
280         tprev = (ie.info.tag < HEADER_I18NTABLE)
281                 ? dataStart : t;
282
283         /* Perform endian conversions */
284         switch (ntohl(pe->type)) {
285         case RPM_INT32_TYPE:
286         {   int_32 * it = (int_32 *)t;
287             for (; ie.info.count > 0; ie.info.count--, it += 1)
288                 *it = htonl(*it);
289             t = (char *) it;
290         }   break;
291         case RPM_INT16_TYPE:
292         {   int_16 * it = (int_16 *) t;
293             for (; ie.info.count > 0; ie.info.count--, it += 1)
294                 *it = htons(*it);
295             t = (char *) it;
296         }   break;
297         default:
298             t += ie.length;
299             break;
300         }
301     }
302     tdel = (tprev ? (t - tprev) : 0);
303     tl += tdel;
304     if (tl > dl)
305         dl = tl;
306     return dl;
307 }
308
309 /**
310  * Retrieve data from header entry.
311  * @todo Permit retrieval of regions other than HEADER_IMUTABLE.
312  * @param entry         header entry
313  * @retval type         address of type (or NULL)
314  * @retval p            address of data (or NULL)
315  * @retval c            address of count (or NULL)
316  * @param minMem        string pointers refer to header memory?
317  */
318 static void copyEntry(const struct indexEntry * entry, /*@out@*/ int_32 * type,
319         /*@out@*/ const void ** p, /*@out@*/ int_32 * c, int minMem)
320                 /*@modifies *type, *p, *c @*/
321 {
322     int_32 count = entry->info.count;
323
324     if (p)
325     switch (entry->info.type) {
326     case RPM_BIN_TYPE:
327         /* XXX this only works for HEADER_IMMUTABLE */
328         if (ENTRY_IS_REGION(entry)) {
329             int_32 * ei = ((int_32 *)entry->data) - 2;
330             struct entryInfo * pe = (struct entryInfo *) (ei + 2);
331             char * dataStart = (char *) (pe + ntohl(ei[0]));
332             int_32 rdl = -entry->info.offset;   /* negative offset */
333             int_32 ril = rdl/sizeof(*pe);
334
335             count = 2 * sizeof(*ei) + (ril * sizeof(*pe)) +
336                         entry->rdlen + REGION_TAG_COUNT;
337             *p = xmalloc(count);
338             ei = (int_32 *) *p;
339             ei[0] = htonl(ril);
340             ei[1] = htonl(entry->rdlen + REGION_TAG_COUNT);
341             pe = (struct entryInfo *) memcpy(ei + 2, pe, (ril * sizeof(*pe)));
342             dataStart = (char *) memcpy(pe + ril, dataStart,
343                                         (entry->rdlen + REGION_TAG_COUNT));
344
345             (void) regionSwab(NULL, ril, 0, pe, dataStart, 0);
346         } else {
347             count = entry->length;
348             *p = (!minMem
349                 ? memcpy(xmalloc(count), entry->data, count)
350                 : entry->data);
351         }
352         break;
353     case RPM_STRING_TYPE:
354         if (count == 1) {
355             *p = entry->data;
356             break;
357         }
358         /*@fallthrough@*/
359     case RPM_STRING_ARRAY_TYPE:
360     case RPM_I18NSTRING_TYPE:
361     {   const char ** ptrEntry;
362         int tableSize = count * sizeof(char *);
363         char * t;
364         int i;
365
366         if (minMem) {
367             *p = xmalloc(tableSize);
368             ptrEntry = (const char **) *p;
369             t = entry->data;
370         } else {
371             t = xmalloc(tableSize + entry->length);
372             *p = (void *)t;
373             ptrEntry = (const char **) *p;
374             t += tableSize;
375             memcpy(t, entry->data, entry->length);
376         }
377         for (i = 0; i < count; i++) {
378             *ptrEntry++ = t;
379             t = strchr(t, 0);
380             t++;
381         }
382     }   break;
383
384     default:
385         *p = entry->data;
386         break;
387     }
388     if (type) *type = entry->info.type;
389     if (c) *c = count;
390 }
391
392 /**
393  * Header tag iterator data structure.
394  */
395 struct headerIteratorS {
396     Header h;           /*!< Header being iterated. */
397     int next_index;     /*!< Next tag index. */
398 };
399
400 HeaderIterator headerInitIterator(Header h)
401 {
402     HeaderIterator hi = xmalloc(sizeof(struct headerIteratorS));
403
404     headerSort(h);
405
406     hi->h = headerLink(h);
407     hi->next_index = 0;
408     return hi;
409 }
410
411 void headerFreeIterator(HeaderIterator iter)
412 {
413     headerFree(iter->h);
414     free(iter);
415 }
416
417 int headerNextIterator(HeaderIterator hi,
418                  int_32 * tag, int_32 * type, const void ** p, int_32 * c)
419 {
420     Header h = hi->h;
421     int slot = hi->next_index;
422     struct indexEntry * entry = NULL;;
423
424     for (slot = hi->next_index; slot < h->indexUsed; slot++) {
425         entry = h->index + slot;
426         if (!ENTRY_IS_REGION(entry))
427             break;
428     }
429     hi->next_index = slot;
430     if (entry == NULL || slot >= h->indexUsed)
431         return 0;
432     hi->next_index++;
433
434     if (tag)
435         *tag = entry->info.tag;
436
437     copyEntry(entry, type, p, c, 0);
438
439     return 1;
440 }
441
442 static int indexCmp(const void *avp, const void *bvp)   /*@*/
443 {
444     const struct indexEntry * ap = avp, * bp = bvp;
445     return (ap->info.tag - bp->info.tag);
446 }
447
448 void headerSort(Header h)
449 {
450     if (!h->sorted) {
451         qsort(h->index, h->indexUsed, sizeof(*h->index), indexCmp);
452         h->sorted = 1;
453     }
454 }
455
456 static int offsetCmp(const void *avp, const void *bvp) /*@*/
457 {
458     const struct indexEntry * ap = avp, * bp = bvp;
459     int rc = (ap->info.offset - bp->info.offset);
460
461     if (rc == 0)
462         rc = (ap->info.tag - bp->info.tag);
463     return rc;
464 }
465
466 void headerUnsort(Header h)
467 {
468     qsort(h->index, h->indexUsed, sizeof(*h->index), offsetCmp);
469 }
470
471 Header headerCopy(Header h)
472 {
473     Header nh = headerNew();
474     HeaderIterator hi;
475     int_32 tag, type, count;
476     const void *ptr;
477    
478     for (hi = headerInitIterator(h);
479         headerNextIterator(hi, &tag, &type, &ptr, &count);
480         ptr = headerFreeData((void *)ptr, type))
481     {
482         headerAddEntry(nh, tag, type, ptr, count);
483     }
484     headerFreeIterator(hi);
485
486     return headerReload(nh, HEADER_IMAGE);
487 }
488
489 Header headerLoad(void *uh)
490 {
491     int_32 *ei = (int_32 *) uh;
492     int_32 il = ntohl(ei[0]);           /* index length */
493     int_32 dl = ntohl(ei[1]);           /* data length */
494     int pvlen = sizeof(il) + sizeof(dl) +
495                (il * sizeof(struct entryInfo)) + dl;
496 #ifdef  REMALLOC_HEADER_REGION
497     void * pv = memcpy(xmalloc(pvlen), uh, pvlen);
498 #else
499     void * pv = uh;
500 #endif
501     Header h = xcalloc(1, sizeof(*h));
502     struct entryInfo * pe;
503     char * dataStart;
504     struct indexEntry * entry; 
505     int rdlen;
506     int i;
507
508     ei = (int_32 *) pv;
509     pe = (struct entryInfo *) &ei[2];
510     dataStart = (char *) (pe + il);
511
512     h->indexAlloced = il + 1;
513     h->indexUsed = il;
514     h->index = xcalloc(h->indexAlloced, sizeof(*h->index));
515     h->sorted = 1;
516 #ifdef  REMALLOC_HEADER_REGION
517     h->region_allocated = 1;
518 #else
519     h->region_allocated = 0;
520 #endif
521     h->nrefs = 1;
522
523     /*
524      * XXX XFree86-libs, ash, and pdksh from Red Hat 5.2 have bogus
525      * %verifyscript tag that needs to be diddled.
526      */
527     if (ntohl(pe->tag) == 15 &&
528         ntohl(pe->type) == RPM_STRING_TYPE &&
529         ntohl(pe->count) == 1)
530     {
531         pe->tag = htonl(1079);
532     }
533
534     entry = h->index;
535     i = 0;
536     if (!(htonl(pe->tag) < HEADER_I18NTABLE)) {
537         h->legacy = 1;
538         entry->info.type = REGION_TAG_TYPE;
539         entry->info.tag = HEADER_IMAGE;
540         entry->info.count = REGION_TAG_COUNT;
541         entry->info.offset = ((char *)pe - dataStart); /* negative offset */
542
543         entry->data = pe;
544         entry->length = pvlen - sizeof(il) - sizeof(dl);
545         rdlen = regionSwab(entry+1, il, 0, pe, dataStart, entry->info.offset);
546         entry->rdlen = rdlen;
547 assert(rdlen == dl);
548
549         entry++;
550         h->indexUsed++;
551     } else {
552         int nb = ntohl(pe->count);
553         int_32 rdl;
554         int_32 ril;
555
556         h->legacy = 0;
557         entry->info.type = htonl(pe->type);
558         if (entry->info.type < RPM_MIN_TYPE || entry->info.type > RPM_MAX_TYPE)
559             return NULL;
560         entry->info.count = htonl(pe->count);
561
562         {  int off = ntohl(pe->offset);
563            if (off) {
564                 int_32 * stei = memcpy(alloca(nb), dataStart + off, nb);
565                 rdl = -ntohl(stei[2]);  /* negative offset */
566                 ril = rdl/sizeof(*pe);
567                 entry->info.tag = htonl(pe->tag);
568             } else {
569                 ril = il;
570                 rdl = (ril * sizeof(struct entryInfo));
571                 entry->info.tag = HEADER_IMAGE;
572             }
573         }
574         entry->info.offset = -rdl;      /* negative offset */
575
576         entry->data = pe;
577         entry->length = pvlen - sizeof(il) - sizeof(dl);
578         rdlen = regionSwab(entry+1, ril-1, 0, pe+1, dataStart, entry->info.offset);
579         entry->rdlen = rdlen;
580
581         if (ril < h->indexUsed) {
582             struct indexEntry * newEntry = entry + ril;
583             int ne = (h->indexUsed - ril);
584             int rid = entry->info.offset+1;
585
586             /* Load dribble entries from region. */
587             rdlen += regionSwab(newEntry, ne, 0, pe+ril, dataStart, rid);
588
589           { struct indexEntry * firstEntry = newEntry;
590             int save = h->indexUsed;
591             int j;
592
593             /* Dribble entries replace duplicate region entries. */
594             h->indexUsed -= ne;
595             for (j = 0; j < ne; j++, newEntry++) {
596                 headerRemoveEntry(h, newEntry->info.tag);
597                 if (newEntry->info.tag == HEADER_BASENAMES)
598                     headerRemoveEntry(h, HEADER_OLDFILENAMES);
599             }
600
601             /* If any duplicate entries were replaced, move new entries down. */
602             if (h->indexUsed < (save - ne)) {
603                 memmove(h->index + h->indexUsed, firstEntry,
604                         (ne * sizeof(*entry)));
605             }
606             h->indexUsed += ne;
607           }
608         }
609     }
610
611     h->sorted = 0;
612     headerSort(h);
613
614     return h;
615 }
616
617 Header headerCopyLoad(void *uh)
618 {
619     int_32 *ei = (int_32 *) uh;
620     int_32 il = ntohl(ei[0]);           /* index length */
621     int_32 dl = ntohl(ei[1]);           /* data length */
622     int pvlen = sizeof(il) + sizeof(dl) +
623                (il * sizeof(struct entryInfo)) + dl;
624     void * nuh = memcpy(xmalloc(pvlen), uh, pvlen);
625     Header h;
626
627     h = headerLoad(nuh);
628     if (h == NULL) {
629         free(nuh);
630         return h;
631     }
632     h->region_allocated = 1;
633     return h;
634 }
635
636 #if 0
637 int headerDrips(const Header h)
638 {
639     struct indexEntry * entry; 
640     int i;
641
642     for (i = 0, entry = h->index; i < h->indexUsed; i++, entry++) {
643         if (ENTRY_IS_REGION(entry)) {
644             int rid = entry->info.offset;
645
646             for (; i < h->indexUsed && entry->info.offset <= rid+1; i++, entry++) {
647                 if (entry->info.offset <= rid)
648                     continue;
649             }
650             i--;
651             entry--;
652             continue;
653         }
654
655         /* Ignore deleted drips. */
656         if (entry->data == NULL || entry->length <= 0)
657             continue;
658     }
659     return 0;
660 }
661 #endif
662
663 static /*@only@*/ void * doHeaderUnload(Header h, /*@out@*/ int * lengthPtr)
664         /*@modifies h, *lengthPtr @*/
665 {
666     int_32 * ei;
667     struct entryInfo * pe;
668     char * dataStart;
669     char * te;
670     unsigned pad;
671     unsigned len;
672     int_32 il = 0;
673     int_32 dl = 0;
674     struct indexEntry * entry; 
675     int_32 type;
676     int i;
677     int drlen, ndribbles;
678     int driplen, ndrips;
679     int legacy = 0;
680
681     /* Sort entries by (offset,tag). */
682     headerUnsort(h);
683
684     /* Compute (il,dl) for all tags, including those deleted in region. */
685     pad = 0;
686     drlen = ndribbles = driplen = ndrips = 0;
687     for (i = 0, entry = h->index; i < h->indexUsed; i++, entry++) {
688         if (ENTRY_IS_REGION(entry)) {
689             int_32 rdl = -entry->info.offset;   /* negative offset */
690             int_32 ril = rdl/sizeof(*pe);
691             int rid = entry->info.offset;
692
693             il += ril;
694             dl += entry->rdlen + entry->info.count;
695             /* XXX Legacy regions do not include the region tag and data. */
696             if (i == 0 && h->legacy)
697                 il += 1;
698
699             /* Skip rest of entries in region, but account for dribbles. */
700             for (; i < h->indexUsed && entry->info.offset <= rid+1; i++, entry++) {
701                 if (entry->info.offset <= rid)
702                     continue;
703
704                 /* Alignment */
705                 type = entry->info.type;
706                 if (typeSizes[type] > 1) {
707                     unsigned diff;
708                     diff = typeSizes[type] - (dl % typeSizes[type]);
709                     if (diff != typeSizes[type]) {
710                         drlen += diff;
711                         pad += diff;
712                         dl += diff;
713                     }
714                 }
715
716                 ndribbles++;
717                 il++;
718                 drlen += entry->length;
719                 dl += entry->length;
720             }
721             i--;
722             entry--;
723             continue;
724         }
725
726         /* Ignore deleted drips. */
727         if (entry->data == NULL || entry->length <= 0)
728             continue;
729
730         /* Alignment */
731         type = entry->info.type;
732         if (typeSizes[type] > 1) {
733             unsigned diff;
734             diff = typeSizes[type] - (dl % typeSizes[type]);
735             if (diff != typeSizes[type]) {
736                 driplen += diff;
737                 pad += diff;
738                 dl += diff;
739             } else
740                 diff = 0;
741         }
742
743         ndrips++;
744         il++;
745         driplen += entry->length;
746         dl += entry->length;
747     }
748     len = sizeof(il) + sizeof(dl) + (il * sizeof(*pe)) + dl;
749
750     ei = xmalloc(len);
751     ei[0] = htonl(il);
752     ei[1] = htonl(dl);
753
754     pe = (struct entryInfo *) &ei[2];
755     dataStart = te = (char *) (pe + il);
756
757     pad = 0;
758     for (i = 0, entry = h->index; i < h->indexUsed; i++, entry++) {
759         const char * src;
760 char *t;
761         int count;
762         int rdlen;
763
764         if (entry->data == NULL || entry->length <= 0)
765             continue;
766
767 t = te;
768         pe->tag = htonl(entry->info.tag);
769         pe->type = htonl(entry->info.type);
770         pe->count = htonl(entry->info.count);
771
772         if (ENTRY_IS_REGION(entry)) {
773             int_32 rdl = -entry->info.offset;   /* negative offset */
774             int_32 ril = rdl/sizeof(*pe) + ndribbles;
775             int rid = entry->info.offset;
776
777             src = (char *)entry->data;
778             rdlen = entry->rdlen;
779
780             /* XXX Legacy regions do not include the region tag and data. */
781             if (i == 0 && h->legacy) {
782                 int_32 stei[4];
783
784                 legacy = 1;
785                 memcpy(pe+1, src, rdl);
786                 memcpy(te, src + rdl, rdlen);
787                 te += rdlen;
788
789                 pe->offset = htonl(te - dataStart);
790                 stei[0] = pe->tag;
791                 stei[1] = pe->type;
792                 stei[2] = htonl(-rdl-entry->info.count);
793                 stei[3] = pe->count;
794                 memcpy(te, stei, entry->info.count);
795                 te += entry->info.count;
796                 ril++;
797                 rdlen += entry->info.count;
798
799                 count = regionSwab(NULL, ril, 0, pe, t, 0);
800                 assert(count == rdlen);
801
802             } else {
803
804                 memcpy(pe+1, src + sizeof(*pe), ((ril-1) * sizeof(*pe)));
805                 memcpy(te, src + (ril * sizeof(*pe)), rdlen+entry->info.count+drlen);
806                 te += rdlen;
807                 {   struct entryInfo * se = (struct entryInfo *)src;
808                     int off = ntohl(se->offset);
809                     pe->offset = (off) ? htonl(te - dataStart) : htonl(off);
810                 }
811                 te += entry->info.count + drlen;
812
813                 count = regionSwab(NULL, ril, 0, pe, t, 0);
814                 assert(count == rdlen+entry->info.count+drlen);
815             }
816
817             /* Skip rest of entries in region. */
818             while (i < h->indexUsed && entry->info.offset <= rid+1) {
819                 i++;
820                 entry++;
821             }
822             i--;
823             entry--;
824             pe += ril;
825             continue;
826         }
827
828         /* Ignore deleted drips. */
829         if (entry->data == NULL || entry->length <= 0)
830             continue;
831
832         /* Alignment */
833         type = entry->info.type;
834         if (typeSizes[type] > 1) {
835             unsigned diff;
836             diff = typeSizes[type] - ((te - dataStart) % typeSizes[type]);
837             if (diff != typeSizes[type]) {
838                 memset(te, 0, diff);
839                 te += diff;
840                 pad += diff;
841             }
842         }
843
844         pe->offset = htonl(te - dataStart);
845
846         /* copy data w/ endian conversions */
847         switch (entry->info.type) {
848         case RPM_INT32_TYPE:
849             count = entry->info.count;
850             src = entry->data;
851             while (count--) {
852                 *((int_32 *)te) = htonl(*((int_32 *)src));
853                 te += sizeof(int_32);
854                 src += sizeof(int_32);
855             }
856             break;
857
858         case RPM_INT16_TYPE:
859             count = entry->info.count;
860             src = entry->data;
861             while (count--) {
862                 *((int_16 *)te) = htons(*((int_16 *)src));
863                 te += sizeof(int_16);
864                 src += sizeof(int_16);
865             }
866             break;
867
868         default:
869             memcpy(te, entry->data, entry->length);
870             te += entry->length;
871             break;
872         }
873         pe++;
874     }
875    
876     /* Insure that there are no memcpy underruns/overruns. */
877     assert(((char *)pe) == dataStart);
878     assert((((char *)ei)+len) == te);
879
880     if (lengthPtr)
881         *lengthPtr = len;
882
883     h->sorted = 0;
884     headerSort(h);
885
886     return (void *)ei;
887 }
888
889 void *headerUnload(Header h)
890 {
891     int length;
892     void * uh = doHeaderUnload(h, &length);
893     return uh;
894 }
895
896 Header headerReload(Header h, int tag)
897 {
898     Header nh;
899     int length;
900     void * uh = doHeaderUnload(h, &length);
901
902     headerFree(h);
903     nh = headerLoad(uh);
904     if (nh == NULL) {
905         free(uh);
906         return nh;
907     }
908     if (nh->region_allocated)
909         free(uh);
910     nh->region_allocated = 1;
911     if (ENTRY_IS_REGION(nh->index)) {
912         if (tag == HEADER_SIGNATURES || tag == HEADER_IMMUTABLE)
913             nh->index[0].info.tag = tag;
914     }
915     return nh;
916 }
917
918 int headerWrite(FD_t fd, Header h, enum hMagic magicp)
919 {
920     ssize_t nb;
921     int length;
922     const void * uh;
923
924     uh = doHeaderUnload(h, &length);
925     switch (magicp) {
926     case HEADER_MAGIC_YES:
927         nb = Fwrite(header_magic, sizeof(char), sizeof(header_magic), fd);
928         if (nb != sizeof(header_magic))
929             goto exit;
930         break;
931     case HEADER_MAGIC_NO:
932         break;
933     }
934
935     nb = Fwrite(uh, sizeof(char), length, fd);
936
937 exit:
938     free((void *)uh);
939     return (nb == length ? 0 : 1);
940 }
941
942 Header headerRead(FD_t fd, enum hMagic magicp)
943 {
944     int_32 block[4];
945     int_32 reserved;
946     int_32 * ei = NULL;
947     int_32 il;
948     int_32 dl;
949     int_32 magic;
950     Header h = NULL;
951     int len;
952     int i;
953
954     memset(block, 0, sizeof(block));
955     i = 2;
956     if (magicp == HEADER_MAGIC_YES)
957         i += 2;
958
959     if (timedRead(fd, (char *)block, i*sizeof(*block)) != (i * sizeof(*block)))
960         goto exit;
961
962     i = 0;
963
964     if (magicp == HEADER_MAGIC_YES) {
965         magic = block[i++];
966         if (memcmp(&magic, header_magic, sizeof(magic)))
967             goto exit;
968         reserved = block[i++];
969     }
970     
971     il = ntohl(block[i++]);
972     dl = ntohl(block[i++]);
973
974     len = sizeof(il) + sizeof(dl) + (il * sizeof(struct entryInfo)) + dl;
975
976     /*
977      * XXX Limit total size of header to 32Mb (~16 times largest known size).
978      */
979     if (len > (32*1024*1024))
980         goto exit;
981
982     ei = xmalloc(len);
983     ei[0] = htonl(il);
984     ei[1] = htonl(dl);
985     len -= sizeof(il) + sizeof(dl);
986
987     if (timedRead(fd, (char *)&ei[2], len) != len)
988         goto exit;
989     
990     h = headerLoad(ei);
991
992 exit:
993     if (h) {
994         if (h->region_allocated)
995             free(ei);
996         h->region_allocated = 1;
997     } else if (ei)
998         free(ei);
999     return h;
1000 }
1001
1002 void headerDump(Header h, FILE *f, int flags, 
1003                 const struct headerTagTableEntry * tags)
1004 {
1005     int i;
1006     struct indexEntry *p;
1007     const struct headerTagTableEntry * tage;
1008     const char *tag;
1009     char *type;
1010
1011     /* First write out the length of the index (count of index entries) */
1012     fprintf(f, "Entry count: %d\n", h->indexUsed);
1013
1014     /* Now write the index */
1015     p = h->index;
1016     fprintf(f, "\n             CT  TAG                  TYPE         "
1017                 "      OFSET      COUNT\n");
1018     for (i = 0; i < h->indexUsed; i++) {
1019         switch (p->info.type) {
1020         case RPM_NULL_TYPE:             type = "NULL_TYPE";     break;
1021         case RPM_CHAR_TYPE:             type = "CHAR_TYPE";     break;
1022         case RPM_BIN_TYPE:              type = "BIN_TYPE";      break;
1023         case RPM_INT8_TYPE:             type = "INT8_TYPE";     break;
1024         case RPM_INT16_TYPE:            type = "INT16_TYPE";    break;
1025         case RPM_INT32_TYPE:            type = "INT32_TYPE";    break;
1026         /*case RPM_INT64_TYPE:          type = "INT64_TYPE";    break;*/
1027         case RPM_STRING_TYPE:           type = "STRING_TYPE";   break;
1028         case RPM_STRING_ARRAY_TYPE:     type = "STRING_ARRAY_TYPE"; break;
1029         case RPM_I18NSTRING_TYPE:       type = "I18N_STRING_TYPE"; break;
1030         default:                        type = "(unknown)";     break;
1031         }
1032
1033         tage = tags;
1034         while (tage->name && tage->val != p->info.tag) tage++;
1035
1036         if (!tage->name)
1037             tag = "(unknown)";
1038         else
1039             tag = tage->name;
1040
1041         fprintf(f, "Entry      : %.3d (%d)%-14s %-18s 0x%.8x %.8d\n", i,
1042                 p->info.tag, tag, type, (unsigned) p->info.offset, (int) 
1043                 p->info.count);
1044
1045         if (flags & HEADER_DUMP_INLINE) {
1046             char *dp = p->data;
1047             int c = p->info.count;
1048             int ct = 0;
1049
1050             /* Print the data inline */
1051             switch (p->info.type) {
1052             case RPM_INT32_TYPE:
1053                 while (c--) {
1054                     fprintf(f, "       Data: %.3d 0x%08x (%d)\n", ct++,
1055                             (unsigned) *((int_32 *) dp),
1056                             (int) *((int_32 *) dp));
1057                     dp += sizeof(int_32);
1058                 }
1059                 break;
1060
1061             case RPM_INT16_TYPE:
1062                 while (c--) {
1063                     fprintf(f, "       Data: %.3d 0x%04x (%d)\n", ct++,
1064                             (unsigned) (*((int_16 *) dp) & 0xffff),
1065                             (int) *((int_16 *) dp));
1066                     dp += sizeof(int_16);
1067                 }
1068                 break;
1069             case RPM_INT8_TYPE:
1070                 while (c--) {
1071                     fprintf(f, "       Data: %.3d 0x%02x (%d)\n", ct++,
1072                             (unsigned) (*((int_8 *) dp) & 0xff),
1073                             (int) *((int_8 *) dp));
1074                     dp += sizeof(int_8);
1075                 }
1076                 break;
1077             case RPM_BIN_TYPE:
1078                 while (c > 0) {
1079                     fprintf(f, "       Data: %.3d ", ct);
1080                     while (c--) {
1081                         fprintf(f, "%02x ", (unsigned) (*(int_8 *)dp & 0xff));
1082                         ct++;
1083                         dp += sizeof(int_8);
1084                         if (! (ct % 8)) {
1085                             break;
1086                         }
1087                     }
1088                     fprintf(f, "\n");
1089                 }
1090                 break;
1091             case RPM_CHAR_TYPE:
1092                 while (c--) {
1093                     char ch = (char) *((char *) dp);
1094                     fprintf(f, "       Data: %.3d 0x%2x %c (%d)\n", ct++,
1095                             (unsigned)(ch & 0xff),
1096                             (isprint(ch) ? ch : ' '),
1097                             (int) *((char *) dp));
1098                     dp += sizeof(char);
1099                 }
1100                 break;
1101             case RPM_STRING_TYPE:
1102             case RPM_STRING_ARRAY_TYPE:
1103             case RPM_I18NSTRING_TYPE:
1104                 while (c--) {
1105                     fprintf(f, "       Data: %.3d %s\n", ct++, (char *) dp);
1106                     dp = strchr(dp, 0);
1107                     dp++;
1108                 }
1109                 break;
1110             default:
1111                 fprintf(stderr, _("Data type %d not supported\n"), 
1112                         (int) p->info.type);
1113                 exit(EXIT_FAILURE);
1114                 /*@notreached@*/ break;
1115             }
1116         }
1117         p++;
1118     }
1119 }
1120
1121 /**
1122  * Find matching (tag,type) entry in header.
1123  * @param h             header
1124  * @param tag           entry tag
1125  * @param type          entry type
1126  * @return              header entry
1127  */
1128 static struct indexEntry *findEntry(Header h, int_32 tag, int_32 type)
1129 {
1130     struct indexEntry * entry, * entry2, * last;
1131     struct indexEntry key;
1132
1133     if (!h->sorted) headerSort(h);
1134
1135     key.info.tag = tag;
1136
1137     entry2 = entry = 
1138         bsearch(&key, h->index, h->indexUsed, sizeof(*h->index), indexCmp);
1139     if (entry == NULL)
1140         return NULL;
1141
1142     if (type == RPM_NULL_TYPE)
1143         return entry;
1144
1145     /* look backwards */
1146     while (entry->info.tag == tag && entry->info.type != type &&
1147            entry > h->index) entry--;
1148
1149     if (entry->info.tag == tag && entry->info.type == type)
1150         return entry;
1151
1152     last = h->index + h->indexUsed;
1153     while (entry2->info.tag == tag && entry2->info.type != type &&
1154            entry2 < last) entry2++;
1155
1156     if (entry->info.tag == tag && entry->info.type == type)
1157         return entry;
1158
1159     return NULL;
1160 }
1161
1162 int headerIsEntry(Header h, int_32 tag)
1163 {
1164     return (findEntry(h, tag, RPM_NULL_TYPE) ? 1 : 0);
1165 }
1166
1167 int headerGetRawEntry(Header h, int_32 tag, int_32 * type, const void ** p,
1168                         int_32 *c)
1169 {
1170     struct indexEntry * entry;
1171
1172     if (p == NULL) return headerIsEntry(h, tag);
1173
1174     /* First find the tag */
1175     entry = findEntry(h, tag, RPM_NULL_TYPE);
1176     if (!entry) {
1177         if (p) *p = NULL;
1178         if (c) *c = 0;
1179         return 0;
1180     }
1181
1182     copyEntry(entry, type, p, c, 0);
1183
1184     return 1;
1185 }
1186
1187 /**
1188  * Does locale match entry in header i18n table?
1189  * 
1190  * \verbatim
1191  * The range [l,le) contains the next locale to match:
1192  *    ll[_CC][.EEEEE][@dddd]
1193  * where
1194  *    ll        ISO language code (in lowercase).
1195  *    CC        (optional) ISO coutnry code (in uppercase).
1196  *    EEEEE     (optional) encoding (not really standardized).
1197  *    dddd      (optional) dialect.
1198  * \endverbatim
1199  *
1200  * @param td            header i18n table data, NUL terminated
1201  * @param l             start of locale to match
1202  * @param le            end of locale to match
1203  * @return              1 on match, 0 on no match
1204  */
1205 static int headerMatchLocale(const char *td, const char *l, const char *le)
1206         /*@*/
1207 {
1208     const char *fe;
1209
1210
1211 #if 0
1212   { const char *s, *ll, *CC, *EE, *dd;
1213     char *lbuf, *t.
1214
1215     /* Copy the buffer and parse out components on the fly. */
1216     lbuf = alloca(le - l + 1);
1217     for (s = l, ll = t = lbuf; *s; s++, t++) {
1218         switch (*s) {
1219         case '_':
1220             *t = '\0';
1221             CC = t + 1;
1222             break;
1223         case '.':
1224             *t = '\0';
1225             EE = t + 1;
1226             break;
1227         case '@':
1228             *t = '\0';
1229             dd = t + 1;
1230             break;
1231         default:
1232             *t = *s;
1233             break;
1234         }
1235     }
1236
1237     if (ll)     /* ISO language should be lower case */
1238         for (t = ll; *t; t++)   *t = tolower(*t);
1239     if (CC)     /* ISO country code should be upper case */
1240         for (t = CC; *t; t++)   *t = toupper(*t);
1241
1242     /* There are a total of 16 cases to attempt to match. */
1243   }
1244 #endif
1245
1246     /* First try a complete match. */
1247     if (strlen(td) == (le-l) && !strncmp(td, l, (le - l)))
1248         return 1;
1249
1250     /* Next, try stripping optional dialect and matching.  */
1251     for (fe = l; fe < le && *fe != '@'; fe++)
1252         ;
1253     if (fe < le && !strncmp(td, l, (fe - l)))
1254         return 1;
1255
1256     /* Next, try stripping optional codeset and matching.  */
1257     for (fe = l; fe < le && *fe != '.'; fe++)
1258         ;
1259     if (fe < le && !strncmp(td, l, (fe - l)))
1260         return 1;
1261
1262     /* Finally, try stripping optional country code and matching. */
1263     for (fe = l; fe < le && *fe != '_'; fe++)
1264         ;
1265     if (fe < le && !strncmp(td, l, (fe - l)))
1266         return 1;
1267
1268     return 0;
1269 }
1270
1271 /**
1272  * Return i18n string from header that matches locale.
1273  * @param h             header
1274  * @param entry         i18n string data
1275  * @return              matching i18n string (or 1st string if no match)
1276  */
1277 /*@dependent@*/ static char *
1278 headerFindI18NString(Header h, struct indexEntry *entry)
1279 {
1280     const char *lang, *l, *le;
1281     struct indexEntry * table;
1282
1283     /* XXX Drepper sez' this is the order. */
1284     if ((lang = getenv("LANGUAGE")) == NULL &&
1285         (lang = getenv("LC_ALL")) == NULL &&
1286         (lang = getenv("LC_MESSAGES")) == NULL &&
1287         (lang = getenv("LANG")) == NULL)
1288             return entry->data;
1289     
1290     if ((table = findEntry(h, HEADER_I18NTABLE, RPM_STRING_ARRAY_TYPE)) == NULL)
1291         return entry->data;
1292
1293     for (l = lang; *l != '\0'; l = le) {
1294         const char *td;
1295         char *ed;
1296         int langNum;
1297
1298         while (*l && *l == ':')                 /* skip leading colons */
1299             l++;
1300         if (*l == '\0')
1301             break;
1302         for (le = l; *le && *le != ':'; le++)   /* find end of this locale */
1303             ;
1304
1305         /* For each entry in the header ... */
1306         for (langNum = 0, td = table->data, ed = entry->data;
1307              langNum < entry->info.count;
1308              langNum++, td += strlen(td) + 1, ed += strlen(ed) + 1) {
1309
1310                 if (headerMatchLocale(td, l, le))
1311                     return ed;
1312
1313         }
1314     }
1315
1316     return entry->data;
1317 }
1318
1319 /**
1320  * Retrieve tag data from header.
1321  * @param h             header
1322  * @param tag           tag to retrieve
1323  * @retval type         address of type (or NULL)
1324  * @retval p            address of data (or NULL)
1325  * @retval c            address of count (or NULL)
1326  * @param minMem        string pointers reference header memory?
1327  * @return              1 on success, 0 on not found
1328  */
1329 static int intGetEntry(Header h, int_32 tag, /*@out@*/ int_32 *type,
1330         /*@out@*/ const void **p, /*@out@*/ int_32 *c, int minMem)
1331                 /*@modifies *type, *p, *c @*/
1332 {
1333     struct indexEntry * entry;
1334
1335     /* First find the tag */
1336     entry = findEntry(h, tag, RPM_NULL_TYPE);
1337     if (entry == NULL) {
1338         if (type) type = 0;
1339         if (p) *p = NULL;
1340         if (c) *c = 0;
1341         return 0;
1342     }
1343
1344     switch (entry->info.type) {
1345     case RPM_I18NSTRING_TYPE:
1346         if (type) *type = RPM_STRING_TYPE;
1347         if (c) *c = 1;
1348         /*@-dependenttrans@*/
1349         if (p) *p = headerFindI18NString(h, entry);
1350         /*@=dependenttrans@*/
1351         break;
1352     default:
1353         copyEntry(entry, type, p, c, minMem);
1354         break;
1355     }
1356
1357     return 1;
1358 }
1359
1360 int headerGetEntryMinMemory(Header h, int_32 tag, int_32 *type, const void **p, 
1361                             int_32 *c)
1362 {
1363     return intGetEntry(h, tag, type, p, c, 1);
1364 }
1365
1366 int headerGetEntry(Header h, int_32 tag, int_32 * type, void **p, int_32 * c)
1367 {
1368     return intGetEntry(h, tag, type, (const void **)p, c, 0);
1369 }
1370
1371 Header headerNew()
1372 {
1373     Header h = xcalloc(1, sizeof(*h));
1374
1375     h->indexAlloced = INDEX_MALLOC_SIZE;
1376     h->indexUsed = 0;
1377     h->region_allocated = 0;
1378     h->sorted = 1;
1379     h->legacy = 0;
1380     h->nrefs = 1;
1381
1382     h->index = (h->indexAlloced
1383         ? xcalloc(h->indexAlloced, sizeof(*h->index))
1384         : NULL);
1385
1386     return h;
1387 }
1388
1389 void headerFree(Header h)
1390 {
1391     if (h == NULL || --h->nrefs > 0)
1392         return;
1393
1394     if (h->index) {
1395         struct indexEntry * entry = h->index;
1396         int i;
1397         for (i = 0; i < h->indexUsed; i++, entry++) {
1398             if (h->region_allocated && ENTRY_IS_REGION(entry)) {
1399                 if (entry->length > 0) {
1400                     int_32 * ei = entry->data;
1401                     ei -= 2; /* XXX HACK: adjust to beginning of header. */
1402                     free(ei);
1403                 }
1404             } else if (!ENTRY_IN_REGION(entry)) {
1405                 free(entry->data);
1406             }
1407             entry->data = NULL;
1408         }
1409         free(h->index);
1410         h->index = NULL;
1411     }
1412
1413     /*@-refcounttrans@*/ free(h); /*@=refcounttrans@*/
1414 }
1415
1416 Header headerLink(Header h)
1417 {
1418     h->nrefs++;
1419     /*@-refcounttrans@*/ return h; /*@=refcounttrans@*/
1420 }
1421
1422 int headerUsageCount(Header h)
1423 {
1424     return h->nrefs;
1425 }
1426
1427 unsigned int headerSizeof(Header h, enum hMagic magicp)
1428 {
1429     struct indexEntry * entry;
1430     unsigned int size = 0, pad = 0;
1431     int i;
1432
1433     headerSort(h);
1434
1435     switch (magicp) {
1436     case HEADER_MAGIC_YES:
1437         size += sizeof(header_magic);
1438         break;
1439     case HEADER_MAGIC_NO:
1440         break;
1441     }
1442
1443     size += 2 * sizeof(int_32); /* count of index entries */
1444
1445     for (i = 0, entry = h->index; i < h->indexUsed; i++, entry++) {
1446         unsigned diff;
1447         int_32 type;
1448
1449         /* Regions go in as is ... */
1450         if (ENTRY_IS_REGION(entry)) {
1451             size += entry->length;
1452             /* XXX Legacy regions do not include the region tag and data. */
1453             if (i == 0 && h->legacy)
1454                 size += sizeof(struct entryInfo) + entry->info.count;
1455             continue;
1456         }
1457
1458         /* ... and region elements are skipped. */
1459         if (entry->info.offset < 0)
1460             continue;
1461
1462         /* Alignment */
1463         type = entry->info.type;
1464         if (typeSizes[type] > 1) {
1465             diff = typeSizes[type] - (size % typeSizes[type]);
1466             if (diff != typeSizes[type]) {
1467                 size += diff;
1468                 pad += diff;
1469             }
1470         }
1471
1472         size += sizeof(struct entryInfo) + entry->length;
1473     }
1474
1475     return size;
1476 }
1477
1478 static void copyData(int_32 type, /*@out@*/ void * dstPtr, const void * srcPtr,
1479         int_32 c, int dataLength)
1480                 /*@modifies *dstPtr @*/
1481 {
1482     const char ** src;
1483     char * dst;
1484     int i, len;
1485
1486     switch (type) {
1487     case RPM_STRING_ARRAY_TYPE:
1488     case RPM_I18NSTRING_TYPE:
1489         /* Otherwise, p is char** */
1490         i = c;
1491         src = (const char **) srcPtr;
1492         dst = dstPtr;
1493         while (i--) {
1494             len = *src ? strlen(*src) + 1 : 0;
1495             memcpy(dst, *src, len);
1496             dst += len;
1497             src++;
1498         }
1499         break;
1500
1501     default:
1502         memcpy(dstPtr, srcPtr, dataLength);
1503         break;
1504     }
1505 }
1506
1507 /**
1508  * Return (malloc'ed) copy of entry data.
1509  * @param type          entry data type
1510  * @param p             entry data
1511  * @param c             entry item count
1512  * @retval lengthPtr    no. bytes in returned data
1513  * @return              (malloc'ed) copy of entry data
1514  */
1515 static void * grabData(int_32 type, const void * p, int_32 c,
1516         /*@out@*/ int * lengthPtr)
1517                 /*@modifies *lengthPtr @*/
1518 {
1519     int length = dataLength(type, p, c, 0);
1520     void * data = xmalloc(length);
1521
1522     copyData(type, data, p, c, length);
1523
1524     if (lengthPtr)
1525         *lengthPtr = length;
1526     return data;
1527 }
1528
1529 int headerAddEntry(Header h, int_32 tag, int_32 type, const void *p, int_32 c)
1530 {
1531     struct indexEntry *entry;
1532
1533     if (c <= 0) {
1534         fprintf(stderr, _("Bad count for headerAddEntry(): %d\n"), (int) c);
1535         exit(EXIT_FAILURE);
1536         /*@notreached@*/
1537     }
1538
1539     /* Allocate more index space if necessary */
1540     if (h->indexUsed == h->indexAlloced) {
1541         h->indexAlloced += INDEX_MALLOC_SIZE;
1542         h->index = xrealloc(h->index,
1543                         h->indexAlloced * sizeof(struct indexEntry));
1544     }
1545
1546     /* Fill in the index */
1547     entry = h->index + h->indexUsed;
1548     entry->info.tag = tag;
1549     entry->info.type = type;
1550     entry->info.count = c;
1551     entry->info.offset = 0;
1552     entry->data = grabData(type, p, c, &entry->length);
1553
1554     if (h->indexUsed > 0 && tag < h->index[h->indexUsed-1].info.tag)
1555         h->sorted = 0;
1556     h->indexUsed++;
1557
1558     return 1;
1559 }
1560
1561 char **
1562 headerGetLangs(Header h)
1563 {
1564     char **s, *e, **table;
1565     int i, type, count;
1566
1567     if (!headerGetRawEntry(h, HEADER_I18NTABLE, &type, (const void **)&s, &count))
1568         return NULL;
1569
1570     if ((table = (char **)xcalloc((count+1), sizeof(char *))) == NULL)
1571         return NULL;
1572
1573     for (i = 0, e = *s; i < count > 0; i++, e += strlen(e)+1)
1574         table[i] = e;
1575     table[count] = NULL;
1576
1577     return table;
1578 }
1579
1580 int headerAddI18NString(Header h, int_32 tag, const char * string, const char * lang)
1581 {
1582     struct indexEntry * table, * entry;
1583     char * chptr;
1584     const char ** strArray;
1585     int length;
1586     int ghosts;
1587     int i, langNum;
1588     char * buf;
1589
1590     table = findEntry(h, HEADER_I18NTABLE, RPM_STRING_ARRAY_TYPE);
1591     entry = findEntry(h, tag, RPM_I18NSTRING_TYPE);
1592
1593     if (!table && entry)
1594         return 0;               /* this shouldn't ever happen!! */
1595
1596     if (!table && !entry) {
1597         const char * charArray[2];
1598         int count = 0;
1599         if (!lang || (lang[0] == 'C' && lang[1] == '\0')) {
1600             /*@-observertrans@*/
1601             charArray[count++] = "C";
1602             /*@=observertrans@*/
1603         } else {
1604             /*@-observertrans@*/
1605             charArray[count++] = "C";
1606             /*@=observertrans@*/
1607             charArray[count++] = lang;
1608         }
1609         if (!headerAddEntry(h, HEADER_I18NTABLE, RPM_STRING_ARRAY_TYPE, 
1610                         &charArray, count))
1611             return 0;
1612         table = findEntry(h, HEADER_I18NTABLE, RPM_STRING_ARRAY_TYPE);
1613     }
1614
1615     if (!lang) lang = "C";
1616
1617     chptr = table->data;
1618     for (langNum = 0; langNum < table->info.count; langNum++) {
1619         if (!strcmp(chptr, lang)) break;
1620         chptr += strlen(chptr) + 1;
1621     }
1622
1623     if (langNum >= table->info.count) {
1624         length = strlen(lang) + 1;
1625         if (ENTRY_IN_REGION(table)) {
1626             char * t = xmalloc(table->length + length);
1627             memcpy(t, table->data, table->length);
1628             table->data = t;
1629             table->info.offset = 0;
1630         } else
1631             table->data = xrealloc(table->data, table->length + length);
1632         memcpy(((char *)table->data) + table->length, lang, length);
1633         table->length += length;
1634         table->info.count++;
1635     }
1636
1637     if (!entry) {
1638         strArray = alloca(sizeof(*strArray) * (langNum + 1));
1639         for (i = 0; i < langNum; i++)
1640             strArray[i] = "";
1641         strArray[langNum] = string;
1642         return headerAddEntry(h, tag, RPM_I18NSTRING_TYPE, strArray, 
1643                                 langNum + 1);
1644     } else if (langNum >= entry->info.count) {
1645         ghosts = langNum - entry->info.count;
1646         
1647         length = strlen(string) + 1 + ghosts;
1648         if (ENTRY_IN_REGION(entry)) {
1649             char * t = xmalloc(entry->length + length);
1650             memcpy(t, entry->data, entry->length);
1651             entry->data = t;
1652             entry->info.offset = 0;
1653         } else
1654             entry->data = xrealloc(entry->data, entry->length + length);
1655
1656         memset(((char *)entry->data) + entry->length, '\0', ghosts);
1657         strcpy(((char *)entry->data) + entry->length + ghosts, string);
1658
1659         entry->length += length;
1660         entry->info.count = langNum + 1;
1661     } else {
1662         char *b, *be, *e, *ee, *t;
1663         size_t bn, sn, en;
1664
1665         /* Set beginning/end pointers to previous data */
1666         b = be = e = ee = entry->data;
1667         for (i = 0; i < table->info.count; i++) {
1668             if (i == langNum)
1669                 be = ee;
1670             ee += strlen(ee) + 1;
1671             if (i == langNum)
1672                 e  = ee;
1673         }
1674
1675         /* Get storage for new buffer */
1676         bn = (be-b);
1677         sn = strlen(string) + 1;
1678         en = (ee-e);
1679         length = bn + sn + en;
1680         t = buf = xmalloc(length);
1681
1682         /* Copy values into new storage */
1683         memcpy(t, b, bn);
1684         t += bn;
1685         memcpy(t, string, sn);
1686         t += sn;
1687         memcpy(t, e, en);
1688         t += en;
1689
1690         /* Replace I18N string array */
1691         entry->length -= strlen(be) + 1;
1692         entry->length += sn;
1693         
1694         if (ENTRY_IN_REGION(entry)) {
1695             entry->info.offset = 0;
1696         } else
1697             free(entry->data);
1698         entry->data = buf;
1699     }
1700
1701     return 0;
1702 }
1703
1704 /* if there are multiple entries with this tag, the first one gets replaced */
1705 int headerModifyEntry(Header h, int_32 tag, int_32 type, void *p, int_32 c)
1706 {
1707     struct indexEntry *entry;
1708     void * oldData;
1709
1710     /* First find the tag */
1711     entry = findEntry(h, tag, type);
1712     if (!entry)
1713         return 0;
1714
1715     /* make sure entry points to the first occurence of this tag */
1716     while (entry > h->index && (entry - 1)->info.tag == tag)  
1717         entry--;
1718
1719     /* free after we've grabbed the new data in case the two are intertwined;
1720        that's a bad idea but at least we won't break */
1721     oldData = entry->data;
1722
1723     entry->info.count = c;
1724     entry->info.type = type;
1725     entry->data = grabData(type, p, c, &entry->length);
1726
1727     if (ENTRY_IN_REGION(entry)) {
1728         entry->info.offset = 0;
1729     } else
1730         free(oldData);
1731
1732     return 1;
1733 }
1734
1735 int headerAddOrAppendEntry(Header h, int_32 tag, int_32 type,
1736                            void * p, int_32 c)
1737 {
1738     return (findEntry(h, tag, type)
1739         ? headerAppendEntry(h, tag, type, p, c)
1740         : headerAddEntry(h, tag, type, p, c));
1741 }
1742
1743 int headerAppendEntry(Header h, int_32 tag, int_32 type, void * p, int_32 c)
1744 {
1745     struct indexEntry *entry;
1746     int length;
1747
1748     /* First find the tag */
1749     entry = findEntry(h, tag, type);
1750     if (!entry)
1751         return 0;
1752
1753     if (type == RPM_STRING_TYPE || type == RPM_I18NSTRING_TYPE) {
1754         /* we can't do this */
1755         return 0;
1756     }
1757
1758     length = dataLength(type, p, c, 0);
1759
1760     if (ENTRY_IN_REGION(entry)) {
1761         char * t = xmalloc(entry->length + length);
1762         memcpy(t, entry->data, entry->length);
1763         entry->data = t;
1764         entry->info.offset = 0;
1765     } else
1766         entry->data = xrealloc(entry->data, entry->length + length);
1767
1768     copyData(type, ((char *) entry->data) + entry->length, p, c, length);
1769
1770     entry->length += length;
1771
1772     entry->info.count += c;
1773
1774     return 1;
1775 }
1776
1777 int headerRemoveEntry(Header h, int_32 tag)
1778 {
1779     struct indexEntry * last = h->index + h->indexUsed;
1780     struct indexEntry * entry, * first;
1781     int ne;
1782
1783     entry = findEntry(h, tag, RPM_NULL_TYPE);
1784     if (!entry) return 1;
1785
1786     /* Make sure entry points to the first occurence of this tag. */
1787     while (entry > h->index && (entry - 1)->info.tag == tag)  
1788         entry--;
1789
1790     /* Free data for tags being removed. */
1791     for (first = entry; first < last; first++) {
1792         void * data;
1793         if (first->info.tag != tag)
1794             break;
1795         data = first->data;
1796         first->data = NULL;
1797         first->length = 0;
1798         if (ENTRY_IN_REGION(first))
1799             continue;
1800         free(data);
1801     }
1802
1803     ne = (first - entry);
1804     if (ne > 0) {
1805         h->indexUsed -= ne;
1806         ne = last - first;
1807         if (ne > 0)
1808             memmove(entry, first, (ne * sizeof(*entry)));
1809     }
1810
1811     return 0;
1812 }
1813
1814 static char escapedChar(const char ch)  /*@*/
1815 {
1816     switch (ch) {
1817     case 'a':   return '\a';
1818     case 'b':   return '\b';
1819     case 'f':   return '\f';
1820     case 'n':   return '\n';
1821     case 'r':   return '\r';
1822     case 't':   return '\t';
1823     case 'v':   return '\v';
1824     default:    return ch;
1825     }
1826 }
1827
1828 static void freeFormat( /*@only@*/ struct sprintfToken * format, int num)
1829 {
1830     int i;
1831
1832     for (i = 0; i < num; i++) {
1833         switch (format[i].type) {
1834         case PTOK_ARRAY:
1835             freeFormat(format[i].u.array.format, format[i].u.array.numTokens);
1836             break;
1837         case PTOK_COND:
1838             freeFormat(format[i].u.cond.ifFormat, 
1839                         format[i].u.cond.numIfTokens);
1840             freeFormat(format[i].u.cond.elseFormat, 
1841                         format[i].u.cond.numElseTokens);
1842             break;
1843         case PTOK_NONE:
1844         case PTOK_TAG:
1845         case PTOK_STRING:
1846         default:
1847             break;
1848         }
1849     }
1850     free(format);
1851 }
1852
1853 static void findTag(char * name, const struct headerTagTableEntry * tags, 
1854                     const struct headerSprintfExtension * extensions,
1855                     /*@out@*/const struct headerTagTableEntry ** tagMatch,
1856                     /*@out@*/const struct headerSprintfExtension ** extMatch)
1857         /*@modifies *tagMatch, *extMatch @*/
1858 {
1859     const struct headerTagTableEntry * entry;
1860     const struct headerSprintfExtension * ext;
1861     const char * tagname;
1862
1863     *tagMatch = NULL;
1864     *extMatch = NULL;
1865
1866     if (strncmp("RPMTAG_", name, sizeof("RPMTAG_")-1)) {
1867         char * t = alloca(strlen(name) + sizeof("RPMTAG_"));
1868         (void) stpcpy( stpcpy(t, "RPMTAG_"), name);
1869         tagname = t;
1870     } else {
1871         tagname = name;
1872     }
1873
1874     /* Search extensions first to permit overriding header tags. */
1875     ext = extensions;
1876     while (ext->type != HEADER_EXT_LAST) {
1877         if (ext->type == HEADER_EXT_TAG && !strcasecmp(ext->name, tagname))
1878             break;
1879
1880         if (ext->type == HEADER_EXT_MORE)
1881             ext = ext->u.more;
1882         else
1883             ext++;
1884     }
1885
1886     if (ext->type == HEADER_EXT_TAG) {
1887         *extMatch = ext;
1888         return;
1889     }
1890
1891     /* Search header tags. */
1892     for (entry = tags; entry->name; entry++)
1893         if (!strcasecmp(entry->name, tagname)) break;
1894
1895     if (entry->name) {
1896         *tagMatch = entry;
1897         return;
1898     }
1899 }
1900
1901 /* forward ref */
1902 static int parseExpression(struct sprintfToken * token, char * str, 
1903         const struct headerTagTableEntry * tags, 
1904         const struct headerSprintfExtension * extensions,
1905         /*@out@*/char ** endPtr, /*@out@*/const char ** errmsg)
1906                 /*@modifies str, *str, *token, *endPtr, *errmsg @*/;
1907
1908 static int parseFormat(char * str, const struct headerTagTableEntry * tags,
1909         const struct headerSprintfExtension * extensions,
1910         /*@out@*/struct sprintfToken ** formatPtr, /*@out@*/int * numTokensPtr,
1911         /*@out@*/char ** endPtr, int state, /*@out@*/const char ** errmsg)
1912           /*@modifies str, *str, *formatPtr, *numTokensPtr, *endPtr, *errmsg @*/
1913 {
1914     char * chptr, * start, * next, * dst;
1915     struct sprintfToken * format;
1916     int numTokens;
1917     int currToken;
1918     const struct headerTagTableEntry * tag;
1919     const struct headerSprintfExtension * ext;
1920     int i;
1921     int done = 0;
1922
1923     /* upper limit on number of individual formats */
1924     numTokens = 0;
1925     for (chptr = str; *chptr != '\0'; chptr++)
1926         if (*chptr == '%') numTokens++;
1927     numTokens = numTokens * 2 + 1;
1928
1929     format = xcalloc(numTokens, sizeof(*format));
1930     if (endPtr) *endPtr = NULL;
1931
1932     /*@-infloops@*/
1933     dst = start = str;
1934     currToken = -1;
1935     while (*start != '\0') {
1936         switch (*start) {
1937         case '%':
1938             /* handle %% */
1939             if (*(start + 1) == '%') {
1940                 if (currToken < 0 || format[currToken].type != PTOK_STRING) {
1941                     currToken++;
1942                     format[currToken].type = PTOK_STRING;
1943                     dst = format[currToken].u.string.string = start;
1944                 }
1945
1946                 start++;
1947
1948                 *dst++ = *start++;
1949
1950                 break; /* out of switch */
1951             } 
1952
1953             currToken++;
1954             *dst++ = '\0';
1955             start++;
1956
1957             if (*start == '|') {
1958                 char * newEnd;
1959
1960                 start++;
1961                 if (parseExpression(format + currToken, start, tags, 
1962                                     extensions, &newEnd, errmsg)) {
1963                     freeFormat(format, numTokens);
1964                     return 1;
1965                 }
1966                 start = newEnd;
1967                 break; /* out of switch */
1968             }
1969
1970             format[currToken].u.tag.format = start;
1971             format[currToken].u.tag.pad = 0;
1972             format[currToken].u.tag.justOne = 0;
1973             format[currToken].u.tag.arrayCount = 0;
1974
1975             chptr = start;
1976             while (*chptr && *chptr != '{' && *chptr != '%') chptr++;
1977             if (!*chptr || *chptr == '%') {
1978                 /*@-observertrans@*/
1979                 *errmsg = _("missing { after %");
1980                 /*@=observertrans@*/
1981                 freeFormat(format, numTokens);
1982                 return 1;
1983             }
1984
1985             *chptr++ = '\0';
1986
1987             while (start < chptr) {
1988                 if (xisdigit(*start)) {
1989                     i = strtoul(start, &start, 10);
1990                     format[currToken].u.tag.pad += i;
1991                 } else {
1992                     start++;
1993                 }
1994             }
1995
1996             if (*start == '=') {
1997                 format[currToken].u.tag.justOne = 1;
1998                 start++;
1999             } else if (*start == '#') {
2000                 format[currToken].u.tag.justOne = 1;
2001                 format[currToken].u.tag.arrayCount = 1;
2002                 start++;
2003             }
2004
2005             next = start;
2006             while (*next && *next != '}') next++;
2007             if (!*next) {
2008                 /*@-observertrans@*/
2009                 *errmsg = _("missing } after %{");
2010                 /*@=observertrans@*/
2011                 freeFormat(format, numTokens);
2012                 return 1;
2013             }
2014             *next++ = '\0';
2015
2016             chptr = start;
2017             while (*chptr && *chptr != ':') chptr++;
2018
2019             if (*chptr != '\0') {
2020                 *chptr++ = '\0';
2021                 if (!*chptr) {
2022                     /*@-observertrans@*/
2023                     *errmsg = _("empty tag format");
2024                     /*@=observertrans@*/
2025                     freeFormat(format, numTokens);
2026                     return 1;
2027                 }
2028                 format[currToken].u.tag.type = chptr;
2029             } else {
2030                 format[currToken].u.tag.type = NULL;
2031             }
2032             
2033             if (!*start) {
2034                 /*@-observertrans@*/
2035                 *errmsg = _("empty tag name");
2036                 /*@=observertrans@*/
2037                 freeFormat(format, numTokens);
2038                 return 1;
2039             }
2040
2041             i = 0;
2042             findTag(start, tags, extensions, &tag, &ext);
2043
2044             if (tag) {
2045                 format[currToken].u.tag.ext = NULL;
2046                 format[currToken].u.tag.tag = tag->val;
2047             } else if (ext) {
2048                 format[currToken].u.tag.ext = ext->u.tagFunction;
2049                 format[currToken].u.tag.extNum = ext - extensions;
2050             } else {
2051                 /*@-observertrans@*/
2052                 *errmsg = _("unknown tag");
2053                 /*@=observertrans@*/
2054                 freeFormat(format, numTokens);
2055                 return 1;
2056             }
2057
2058             format[currToken].type = PTOK_TAG;
2059
2060             start = next;
2061
2062             break;
2063
2064         case '[':
2065             *dst++ = '\0';
2066             *start++ = '\0';
2067             currToken++;
2068
2069             if (parseFormat(start, tags, extensions, 
2070                             &format[currToken].u.array.format,
2071                             &format[currToken].u.array.numTokens,
2072                             &start, PARSER_IN_ARRAY, errmsg)) {
2073                 freeFormat(format, numTokens);
2074                 return 1;
2075             }
2076
2077             if (!start) {
2078                 /*@-observertrans@*/
2079                 *errmsg = _("] expected at end of array");
2080                 /*@=observertrans@*/
2081                 freeFormat(format, numTokens);
2082                 return 1;
2083             }
2084
2085             dst = start;
2086
2087             format[currToken].type = PTOK_ARRAY;
2088
2089             break;
2090
2091         case ']':
2092         case '}':
2093             if ((*start == ']' && state != PARSER_IN_ARRAY) ||
2094                 (*start == '}' && state != PARSER_IN_EXPR)) {
2095                 if (*start == ']')
2096                     /*@-observertrans@*/
2097                     *errmsg = _("unexpected ]");
2098                     /*@=observertrans@*/
2099                 else
2100                     /*@-observertrans@*/
2101                     *errmsg = _("unexpected }");
2102                     /*@=observertrans@*/
2103                 freeFormat(format, numTokens);
2104                 return 1;
2105             }
2106             *start++ = '\0';
2107             *endPtr = start;
2108             done = 1;
2109             break;
2110
2111         default:
2112             if (currToken < 0 || format[currToken].type != PTOK_STRING) {
2113                 currToken++;
2114                 format[currToken].type = PTOK_STRING;
2115                 dst = format[currToken].u.string.string = start;
2116             }
2117
2118             if (*start == '\\') {
2119                 start++;
2120                 *dst++ = escapedChar(*start++);
2121             } else {
2122                 *dst++ = *start++;
2123             }
2124             break;
2125         }
2126         if (done)
2127             break;
2128     }
2129     /*@=infloops@*/
2130
2131     *dst = '\0';
2132
2133     currToken++;
2134     for (i = 0; i < currToken; i++) {
2135         if (format[i].type == PTOK_STRING)
2136             format[i].u.string.len = strlen(format[i].u.string.string);
2137     }
2138
2139     *numTokensPtr = currToken;
2140     *formatPtr = format;
2141
2142     return 0;
2143 }
2144
2145 static int parseExpression(struct sprintfToken * token, char * str, 
2146         const struct headerTagTableEntry * tags, 
2147         const struct headerSprintfExtension * extensions,
2148         /*@out@*/ char ** endPtr, /*@out@*/ const char ** errmsg)
2149 {
2150     const struct headerTagTableEntry * tag;
2151     const struct headerSprintfExtension * ext;
2152     char * chptr;
2153     char * end;
2154
2155     *errmsg = NULL;
2156     chptr = str;
2157     while (*chptr && *chptr != '?') chptr++;
2158
2159     if (*chptr != '?') {
2160         /*@-observertrans@*/
2161         *errmsg = _("? expected in expression");
2162         /*@=observertrans@*/
2163         return 1;
2164     }
2165
2166     *chptr++ = '\0';;
2167
2168     if (*chptr != '{') {
2169         /*@-observertrans@*/
2170         *errmsg = _("{ expected after ? in expression");
2171         /*@=observertrans@*/
2172         return 1;
2173     }
2174
2175     chptr++;
2176
2177     if (parseFormat(chptr, tags, extensions, &token->u.cond.ifFormat, 
2178                     &token->u.cond.numIfTokens, &end, PARSER_IN_EXPR, errmsg)) 
2179         return 1;
2180
2181     if (!*end) {
2182         /*@-observertrans@*/
2183         *errmsg = _("} expected in expression");
2184         /*@=observertrans@*/
2185         freeFormat(token->u.cond.ifFormat, token->u.cond.numIfTokens);
2186         token->u.cond.ifFormat = NULL;
2187         return 1;
2188     }
2189
2190     chptr = end;
2191     if (*chptr != ':' && *chptr != '|') {
2192         /*@-observertrans@*/
2193         *errmsg = _(": expected following ? subexpression");
2194         /*@=observertrans@*/
2195         freeFormat(token->u.cond.ifFormat, token->u.cond.numIfTokens);
2196         token->u.cond.ifFormat = NULL;
2197         return 1;
2198     }
2199
2200     if (*chptr == '|') {
2201         parseFormat(xstrdup(""), tags, extensions, &token->u.cond.elseFormat, 
2202                         &token->u.cond.numElseTokens, &end, PARSER_IN_EXPR, 
2203                         errmsg);
2204     } else {
2205         chptr++;
2206
2207         if (*chptr != '{') {
2208             /*@-observertrans@*/
2209             *errmsg = _("{ expected after : in expression");
2210             /*@=observertrans@*/
2211             freeFormat(token->u.cond.ifFormat, token->u.cond.numIfTokens);
2212             token->u.cond.ifFormat = NULL;
2213             return 1;
2214         }
2215
2216         chptr++;
2217
2218         if (parseFormat(chptr, tags, extensions, &token->u.cond.elseFormat, 
2219                         &token->u.cond.numElseTokens, &end, PARSER_IN_EXPR, 
2220                         errmsg)) 
2221             return 1;
2222         if (!*end) {
2223             /*@-observertrans@*/
2224             *errmsg = _("} expected in expression");
2225             /*@=observertrans@*/
2226             freeFormat(token->u.cond.ifFormat, token->u.cond.numIfTokens);
2227             token->u.cond.ifFormat = NULL;
2228             return 1;
2229         }
2230
2231         chptr = end;
2232         if (*chptr != '|') {
2233             /*@-observertrans@*/
2234             *errmsg = _("| expected at end of expression");
2235             /*@=observertrans@*/
2236             freeFormat(token->u.cond.ifFormat, token->u.cond.numIfTokens);
2237             token->u.cond.ifFormat = NULL;
2238             freeFormat(token->u.cond.elseFormat, token->u.cond.numElseTokens);
2239             token->u.cond.elseFormat = NULL;
2240             return 1;
2241         }
2242     }
2243         
2244     chptr++;
2245
2246     *endPtr = chptr;
2247
2248     findTag(str, tags, extensions, &tag, &ext);
2249
2250     if (tag) {
2251         token->u.cond.tag.ext = NULL;
2252         token->u.cond.tag.tag = tag->val;
2253     } else if (ext) {
2254         token->u.cond.tag.ext = ext->u.tagFunction;
2255         token->u.cond.tag.extNum = ext - extensions;
2256     } else {
2257         token->u.cond.tag.ext = NULL;
2258         token->u.cond.tag.tag = -1;
2259     }
2260         
2261     token->type = PTOK_COND;
2262
2263     return 0;
2264 }
2265
2266 static int getExtension(Header h, headerTagTagFunction fn,
2267         /*@out@*/ int_32 * typeptr, /*@out@*/ const void ** data,
2268         /*@out@*/ int_32 * countptr, struct extensionCache * ext)
2269                 /*@modifies *typeptr, *data, *countptr, ext->avail @*/
2270 {
2271     if (!ext->avail) {
2272         if (fn(h, &ext->type, &ext->data, &ext->count, &ext->freeit))
2273             return 1;
2274         ext->avail = 1;
2275     }
2276
2277     *typeptr = ext->type;
2278     *data = ext->data;
2279     *countptr = ext->count;
2280
2281     return 0;
2282 }
2283
2284 static char * formatValue(struct sprintfTag * tag, Header h, 
2285                           const struct headerSprintfExtension * extensions,
2286                           struct extensionCache * extCache, int element)
2287                 /*@modifies h, extCache->avail @*/
2288 {
2289     int len;
2290     char buf[20];
2291     int_32 count, type;
2292     const void * data;
2293     unsigned int intVal;
2294     char * val = NULL;
2295     const char ** strarray;
2296     int mayfree = 0;
2297     int countBuf;
2298     headerTagFormatFunction tagtype = NULL;
2299     const struct headerSprintfExtension * ext;
2300
2301     if (tag->ext) {
2302         if (getExtension(h, tag->ext, &type, &data, &count, 
2303                          extCache + tag->extNum)) {
2304             count = 1;
2305             type = RPM_STRING_TYPE;     
2306             data = "(none)";            /* XXX i18n? NO!, sez; gafton */
2307         }
2308     } else {
2309         if (!headerGetEntry(h, tag->tag, &type, (void **)&data, &count)){
2310             count = 1;
2311             type = RPM_STRING_TYPE;     
2312             data = "(none)";            /* XXX i18n? NO!, sez; gafton */
2313         }
2314
2315         mayfree = 1;
2316     }
2317
2318     if (tag->arrayCount) {
2319         /*@-observertrans -modobserver@*/
2320         data = headerFreeData(data, type);
2321         /*@=observertrans =modobserver@*/
2322
2323         countBuf = count;
2324         data = &countBuf;
2325         count = 1;
2326         type = RPM_INT32_TYPE;
2327     }
2328
2329     (void) stpcpy( stpcpy(buf, "%"), tag->format);
2330
2331     if (tag->type) {
2332         ext = extensions;
2333         while (ext->type != HEADER_EXT_LAST) {
2334             if (ext->type == HEADER_EXT_FORMAT && 
2335                 !strcmp(ext->name, tag->type)) {
2336                 tagtype = ext->u.formatFunction;
2337                 break;
2338             }
2339
2340             if (ext->type == HEADER_EXT_MORE)
2341                 ext = ext->u.more;
2342             else
2343                 ext++;
2344         }
2345     }
2346     
2347     switch (type) {
2348     case RPM_STRING_ARRAY_TYPE:
2349         strarray = (const char **)data;
2350
2351         if (tagtype)
2352             val = tagtype(RPM_STRING_TYPE, strarray[element], buf, tag->pad, 0);
2353
2354         if (!val) {
2355             strcat(buf, "s");
2356
2357             len = strlen(strarray[element]) + tag->pad + 20;
2358             val = xmalloc(len);
2359             sprintf(val, buf, strarray[element]);
2360         }
2361
2362         /*@-observertrans -modobserver@*/
2363         if (mayfree) free((void *)data);
2364         /*@=observertrans =modobserver@*/
2365
2366         break;
2367
2368     case RPM_STRING_TYPE:
2369         if (tagtype)
2370             val = tagtype(RPM_STRING_ARRAY_TYPE, data, buf, tag->pad,  0);
2371
2372         if (!val) {
2373             strcat(buf, "s");
2374
2375             len = strlen(data) + tag->pad + 20;
2376             val = xmalloc(len);
2377             sprintf(val, buf, data);
2378         }
2379         break;
2380
2381     case RPM_CHAR_TYPE:
2382     case RPM_INT8_TYPE:
2383     case RPM_INT16_TYPE:
2384     case RPM_INT32_TYPE:
2385         switch (type) {
2386         case RPM_CHAR_TYPE:     
2387         case RPM_INT8_TYPE:     intVal = *(((int_8 *) data) + element); break;
2388         case RPM_INT16_TYPE:    intVal = *(((uint_16 *) data) + element); break;
2389         default:                /* keep -Wall quiet */
2390         case RPM_INT32_TYPE:    intVal = *(((int_32 *) data) + element); break;
2391         }
2392
2393         if (tagtype)
2394             val = tagtype(RPM_INT32_TYPE, &intVal, buf, tag->pad,  element);
2395
2396         if (!val) {
2397             strcat(buf, "d");
2398             len = 10 + tag->pad + 20;
2399             val = xmalloc(len);
2400             sprintf(val, buf, intVal);
2401         }
2402         break;
2403
2404     default:
2405         val = xstrdup(_("(unknown type)"));
2406         break;
2407     }
2408
2409     return val;
2410 }
2411
2412 static const char * singleSprintf(Header h, struct sprintfToken * token,
2413                             const struct headerSprintfExtension * extensions,
2414                             struct extensionCache * extCache, int element)
2415                 /*@modifies h, extCache->avail @*/
2416 {
2417     char * val;
2418     const char * thisItem;
2419     int thisItemLen;
2420     int len, alloced;
2421     int i, j;
2422     int numElements;
2423     int type;
2424     struct sprintfToken * condFormat;
2425     int condNumFormats;
2426
2427     /* we assume the token and header have been validated already! */
2428
2429     switch (token->type) {
2430     case PTOK_NONE:
2431         break;
2432
2433     case PTOK_STRING:
2434         val = xmalloc(token->u.string.len + 1);
2435         strcpy(val, token->u.string.string);
2436         break;
2437
2438     case PTOK_TAG:
2439         val = formatValue(&token->u.tag, h, extensions, extCache,
2440                           token->u.tag.justOne ? 0 : element);
2441         break;
2442
2443     case PTOK_COND:
2444         if (token->u.cond.tag.ext ||
2445             headerIsEntry(h, token->u.cond.tag.tag)) {
2446             condFormat = token->u.cond.ifFormat;
2447             condNumFormats = token->u.cond.numIfTokens;
2448         } else {
2449             condFormat = token->u.cond.elseFormat;
2450             condNumFormats = token->u.cond.numElseTokens;
2451         }
2452
2453         alloced = condNumFormats * 20;
2454         val = xmalloc(alloced ? alloced : 1);
2455         *val = '\0';
2456         len = 0;
2457
2458         for (i = 0; i < condNumFormats; i++) {
2459             thisItem = singleSprintf(h, condFormat + i, 
2460                                      extensions, extCache, element);
2461             thisItemLen = strlen(thisItem);
2462             if ((thisItemLen + len) >= alloced) {
2463                 alloced = (thisItemLen + len) + 200;
2464                 val = xrealloc(val, alloced);   
2465             }
2466             strcat(val, thisItem);
2467             len += thisItemLen;
2468             free((void *)thisItem);
2469         }
2470
2471         break;
2472
2473     case PTOK_ARRAY:
2474         numElements = -1;
2475         for (i = 0; i < token->u.array.numTokens; i++) {
2476             if (token->u.array.format[i].type != PTOK_TAG ||
2477                 token->u.array.format[i].u.tag.arrayCount ||
2478                 token->u.array.format[i].u.tag.justOne) continue;
2479
2480             if (token->u.array.format[i].u.tag.ext) {
2481                 const void * data;
2482                 if (getExtension(h, token->u.array.format[i].u.tag.ext,
2483                                  &type, &data, &numElements, 
2484                                  extCache + 
2485                                    token->u.array.format[i].u.tag.extNum))
2486                      continue;
2487             } else {
2488                 if (!headerGetEntry(h, token->u.array.format[i].u.tag.tag, 
2489                                     &type, (void **) &val, &numElements))
2490                     continue;
2491                 val = headerFreeData(val, type);
2492             } 
2493             break;
2494         }
2495
2496         if (numElements == -1) {
2497             val = xstrdup("(none)");    /* XXX i18n? NO!, sez; gafton */
2498         } else {
2499             alloced = numElements * token->u.array.numTokens * 20;
2500             val = xmalloc(alloced);
2501             *val = '\0';
2502             len = 0;
2503
2504             for (j = 0; j < numElements; j++) {
2505                 for (i = 0; i < token->u.array.numTokens; i++) {
2506                     thisItem = singleSprintf(h, token->u.array.format + i, 
2507                                              extensions, extCache, j);
2508                     thisItemLen = strlen(thisItem);
2509                     if ((thisItemLen + len) >= alloced) {
2510                         alloced = (thisItemLen + len) + 200;
2511                         val = xrealloc(val, alloced);   
2512                     }
2513                     strcat(val, thisItem);
2514                     len += thisItemLen;
2515                     free((void *)thisItem);
2516                 }
2517             }
2518         }
2519            
2520         break;
2521     }
2522
2523     return val;
2524 }
2525
2526 static struct extensionCache * allocateExtensionCache(
2527                      const struct headerSprintfExtension * extensions)
2528                 /*@*/
2529 {
2530     const struct headerSprintfExtension * ext = extensions;
2531     int i = 0;
2532
2533     while (ext->type != HEADER_EXT_LAST) {
2534         i++;
2535         if (ext->type == HEADER_EXT_MORE)
2536             ext = ext->u.more;
2537         else
2538             ext++;
2539     }
2540
2541     return xcalloc(i, sizeof(struct extensionCache));
2542 }
2543
2544 static void freeExtensionCache(const struct headerSprintfExtension * extensions,
2545                         /*@only@*/struct extensionCache * cache)
2546 {
2547     const struct headerSprintfExtension * ext = extensions;
2548     int i = 0;
2549
2550     while (ext->type != HEADER_EXT_LAST) {
2551         if (cache[i].freeit) free((void *)cache[i].data);
2552
2553         i++;
2554         if (ext->type == HEADER_EXT_MORE)
2555             ext = ext->u.more;
2556         else
2557             ext++;
2558     }
2559
2560     free(cache);
2561 }
2562
2563 char * headerSprintf(Header h, const char * origFmt, 
2564                      const struct headerTagTableEntry * tags,
2565                      const struct headerSprintfExtension * extensions,
2566                      const char ** errmsg)
2567 {
2568     char * fmtString;
2569     struct sprintfToken * format;
2570     int numTokens;
2571     char * answer;
2572     int answerLength;
2573     int answerAlloced;
2574     int i;
2575     struct extensionCache * extCache;
2576  
2577     /*fmtString = escapeString(origFmt);*/
2578     fmtString = xstrdup(origFmt);
2579    
2580     if (parseFormat(fmtString, tags, extensions, &format, &numTokens, 
2581                     NULL, PARSER_BEGIN, errmsg)) {
2582         free(fmtString);
2583         return NULL;
2584     }
2585
2586     extCache = allocateExtensionCache(extensions);
2587
2588     answerAlloced = 1024;
2589     answerLength = 0;
2590     answer = xmalloc(answerAlloced);
2591     *answer = '\0';
2592
2593     for (i = 0; i < numTokens; i++) {
2594         const char * piece;
2595         int pieceLength;
2596
2597         piece = singleSprintf(h, format + i, extensions, extCache, 0);
2598         if (piece) {
2599             pieceLength = strlen(piece);
2600             if ((answerLength + pieceLength) >= answerAlloced) {
2601                 while ((answerLength + pieceLength) >= answerAlloced) 
2602                     answerAlloced += 1024;
2603                 answer = xrealloc(answer, answerAlloced);
2604             }
2605
2606             strcat(answer, piece);
2607             answerLength += pieceLength;
2608             free((void *)piece);
2609         }
2610     }
2611
2612     free(fmtString);
2613     freeExtensionCache(extensions, extCache);
2614     free(format);
2615
2616     return answer;
2617 }
2618
2619 static char * octalFormat(int_32 type, const void * data, 
2620                 char * formatPrefix, int padding, /*@unused@*/int element)
2621                 /*@modifies formatPrefix @*/
2622 {
2623     char * val;
2624
2625     if (type != RPM_INT32_TYPE) {
2626         val = xstrdup(_("(not a number)"));
2627     } else {
2628         val = xmalloc(20 + padding);
2629         strcat(formatPrefix, "o");
2630         sprintf(val, formatPrefix, *((int_32 *) data));
2631     }
2632
2633     return val;
2634 }
2635
2636 static char * hexFormat(int_32 type, const void * data, 
2637                 char * formatPrefix, int padding, /*@unused@*/int element)
2638                 /*@modifies formatPrefix @*/
2639 {
2640     char * val;
2641
2642     if (type != RPM_INT32_TYPE) {
2643         val = xstrdup(_("(not a number)"));
2644     } else {
2645         val = xmalloc(20 + padding);
2646         strcat(formatPrefix, "x");
2647         sprintf(val, formatPrefix, *((int_32 *) data));
2648     }
2649
2650     return val;
2651 }
2652
2653 static char * realDateFormat(int_32 type, const void * data, 
2654                 char * formatPrefix, int padding, /*@unused@*/int element,
2655                 char * strftimeFormat)
2656                 /*@modifies formatPrefix @*/
2657 {
2658     char * val;
2659     struct tm * tstruct;
2660     char buf[50];
2661
2662     if (type != RPM_INT32_TYPE) {
2663         val = xstrdup(_("(not a number)"));
2664     } else {
2665         val = xmalloc(50 + padding);
2666         strcat(formatPrefix, "s");
2667
2668         /* this is important if sizeof(int_32) ! sizeof(time_t) */
2669         {   time_t dateint = *((int_32 *) data);
2670             tstruct = localtime(&dateint);
2671         }
2672         (void)strftime(buf, sizeof(buf) - 1, strftimeFormat, tstruct);
2673         sprintf(val, formatPrefix, buf);
2674     }
2675
2676     return val;
2677 }
2678
2679 static char * dateFormat(int_32 type, const void * data, 
2680                          char * formatPrefix, int padding, int element)
2681                 /*@modifies formatPrefix @*/
2682 {
2683     return realDateFormat(type, data, formatPrefix, padding, element, "%c");
2684 }
2685
2686 static char * dayFormat(int_32 type, const void * data, 
2687                          char * formatPrefix, int padding, int element)
2688                 /*@modifies formatPrefix @*/
2689 {
2690     return realDateFormat(type, data, formatPrefix, padding, element, 
2691                           "%a %b %d %Y");
2692 }
2693
2694 static char * shescapeFormat(int_32 type, const void * data, 
2695                 char * formatPrefix, int padding, /*@unused@*/int element)
2696                 /*@modifies formatPrefix @*/
2697 {
2698     char * result, * dst, * src, * buf;
2699
2700     if (type == RPM_INT32_TYPE) {
2701         result = xmalloc(padding + 20);
2702         strcat(formatPrefix, "d");
2703         sprintf(result, formatPrefix, *((int_32 *) data));
2704     } else {
2705         buf = alloca(strlen(data) + padding + 2);
2706         strcat(formatPrefix, "s");
2707         sprintf(buf, formatPrefix, data);
2708
2709         result = dst = xmalloc(strlen(buf) * 4 + 3);
2710         *dst++ = '\'';
2711         for (src = buf; *src != '\0'; src++) {
2712             if (*src == '\'') {
2713                 *dst++ = '\'';
2714                 *dst++ = '\\';
2715                 *dst++ = '\'';
2716                 *dst++ = '\'';
2717             } else {
2718                 *dst++ = *src;
2719             }
2720         }
2721         *dst++ = '\'';
2722         *dst = '\0';
2723
2724     }
2725
2726     return result;
2727 }
2728
2729 const struct headerSprintfExtension headerDefaultFormats[] = {
2730     { HEADER_EXT_FORMAT, "octal", { octalFormat } },
2731     { HEADER_EXT_FORMAT, "hex", { hexFormat } },
2732     { HEADER_EXT_FORMAT, "date", { dateFormat } },
2733     { HEADER_EXT_FORMAT, "day", { dayFormat } },
2734     { HEADER_EXT_FORMAT, "shescape", { shescapeFormat } },
2735     { HEADER_EXT_LAST, NULL, { NULL } }
2736 };
2737
2738 void headerCopyTags(Header headerFrom, Header headerTo, int *tagstocopy)
2739 {
2740     int *p;
2741
2742     if (headerFrom == headerTo)
2743         return;
2744
2745     for (p = tagstocopy; *p != 0; p++) {
2746         char *s;
2747         int type, count;
2748         if (headerIsEntry(headerTo, *p))
2749             continue;
2750         if (!headerGetEntryMinMemory(headerFrom, *p, &type,
2751                                 (const void **) &s, &count))
2752             continue;
2753         headerAddEntry(headerTo, *p, type, s, count);
2754         s = headerFreeData(s, type);
2755     }
2756 }