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