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