use compressed filenames on install side.
[platform/upstream/rpm.git] / lib / header.c
1 /* RPM - Copyright (C) 1995 Red Hat Software
2  *
3  * header.c - routines for managing rpm headers
4  */
5
6 /* Data written to file descriptors is in network byte order.    */
7 /* Data read from file descriptors is expected to be in          */
8 /* network byte order and is converted on the fly to host order. */
9
10 #include "system.h"
11
12 #ifdef  __LCLINT__
13 #define ntohl(_x)       (_x)
14 #define ntohs(_x)       (_x)
15 #define htonl(_x)       (_x)
16 #define htons(_x)       (_x)
17 #else
18 #include <netinet/in.h>
19 #endif  /* __LCLINT__ */
20
21 #include <rpmio.h>
22 #include <header.h>
23
24 #define INDEX_MALLOC_SIZE 8
25
26 #define PARSER_BEGIN    0
27 #define PARSER_IN_ARRAY 1
28 #define PARSER_IN_EXPR  2
29
30 static unsigned char header_magic[4] = { 0x8e, 0xad, 0xe8, 0x01 };
31
32 /* handy -- this tells us alignments for defined elements as well */
33 static int typeSizes[] =  { 
34         /* RPM_NULL_TYPE */             -1,
35         /* RPM_CHAR_TYPE */             1,
36         /* RPM_INT8_TYPE */             1,
37         /* RPM_INT16_TYPE */            2,
38         /* RPM_INT32_TYPE */            4,
39         /* RPM_INT64_TYPE */            -1,
40         /* RPM_STRING_TYPE */           -1,
41         /* RPM_BIN_TYPE */              1,
42         /* RPM_STRING_ARRAY_TYPE */     -1,
43         /* RPM_I18NSTRING_TYPE */       -1 };
44
45 struct headerToken {
46     struct indexEntry *index;
47     int indexUsed;
48     int indexAlloced;
49
50     int sorted;  
51     /*@refs@*/int usageCount;
52 };
53
54 struct entryInfo {
55     int_32 tag;
56     int_32 type;
57     int_32 offset;              /* Offset from beginning of data segment,
58                                    only defined on disk */
59     int_32 count;
60 };
61
62 struct indexEntry {
63     struct entryInfo info;
64     /*@owned@*/ void * data; 
65     int length;                 /* Computable, but why bother? */
66 };
67
68 struct sprintfTag {
69     /* if NULL tag element is invalid */
70     headerTagTagFunction ext;   
71     int extNum;
72     int_32 tag;
73     int justOne;
74     int arrayCount;
75     char * format;
76     char * type;
77     int pad;
78 };
79
80 struct extensionCache {
81     int_32 type, count;
82     int avail, freeit;
83     void * data;
84 };
85
86 struct sprintfToken {
87     enum { PTOK_NONE = 0, PTOK_TAG, PTOK_ARRAY, PTOK_STRING, PTOK_COND } type;
88     union {
89         struct {
90             /*@only@*/ struct sprintfToken * format;
91             int numTokens;
92         } array;
93         struct sprintfTag tag;
94         struct {
95             /*@dependent@*/ char * string;
96             int len;
97         } string;
98         struct {
99             /*@only@*/ struct sprintfToken * ifFormat;
100             int numIfTokens;
101             /*@only@*/ struct sprintfToken * elseFormat;
102             int numElseTokens;
103             struct sprintfTag tag;
104         } cond;
105     } u;
106 };
107
108 static void copyEntry(struct indexEntry * entry, /*@out@*/ int_32 * type,
109         /*@out@*/ void ** p, /*@out@*/ int_32 * c, int minimizeMemory)
110 {
111     int i, tableSize;
112     char ** ptrEntry;
113     char * chptr;
114
115     if (type) 
116         *type = entry->info.type;
117     if (c) 
118         *c = entry->info.count;
119     if (p == NULL)
120         return;
121
122     /* Now look it up */
123     switch (entry->info.type) {
124       case RPM_STRING_TYPE:
125         if (entry->info.count == 1) {
126             *p = entry->data;
127             break;
128         }
129         /*@fallthrough@*/
130       case RPM_STRING_ARRAY_TYPE:
131       case RPM_I18NSTRING_TYPE:
132         i = entry->info.count;
133         tableSize = i * sizeof(char *);
134         if (minimizeMemory) {
135             ptrEntry = *p = xmalloc(tableSize);
136             chptr = entry->data;
137         } else {
138             ptrEntry = *p = xmalloc(tableSize + entry->length); /* XXX memory leak */
139             chptr = ((char *) *p) + tableSize;
140             memcpy(chptr, entry->data, entry->length);
141         }
142         while (i--) {
143             *ptrEntry++ = chptr;
144             chptr = strchr(chptr, 0);
145             chptr++;
146         }
147         break;
148
149       default:
150         *p = entry->data;
151         break;
152     }
153 }
154
155 static int dataLength(int_32 type, const void * p, int_32 count, int onDisk)
156 {
157     int thisLen, length, i;
158     char ** src, * chptr;
159
160     length = 0;
161     switch (type) {
162       case RPM_STRING_TYPE:
163         if (count == 1) {
164             /* Special case -- p is just the string */
165             length = strlen(p) + 1;
166             break;
167         }
168         /* This should not be allowed */
169         fprintf(stderr, _("grabData() RPM_STRING_TYPE count must be 1.\n"));
170         exit(EXIT_FAILURE);
171         /*@notreached@*/ break;
172
173       case RPM_STRING_ARRAY_TYPE:
174       case RPM_I18NSTRING_TYPE:
175         /* This is like RPM_STRING_TYPE, except it's *always* an array */
176         /* Compute sum of length of all strings, including null terminators */
177         i = count;
178         length = 0;
179
180         if (onDisk) {
181             chptr = (char *) p;
182             while (i--) {
183                 thisLen = strlen(chptr) + 1;
184                 length += thisLen;
185                 chptr += thisLen;
186             }
187         } else {
188             src = (char **) p;
189             while (i--) {
190                 /* add one for null termination */
191                 length += strlen(*src++) + 1;
192             }
193         }
194         break;
195
196       default:
197         if (typeSizes[type] != -1)
198             length = typeSizes[type] * count;
199         else {
200             fprintf(stderr, _("Data type %d not supported\n"), (int) type);
201             exit(EXIT_FAILURE);
202             /*@notreached@*/
203         }
204         break;
205     }
206
207     return length;
208 }
209
210 /********************************************************************/
211 /*                                                                  */
212 /* Header iteration and copying                                     */
213 /*                                                                  */
214 /********************************************************************/
215
216 struct headerIteratorS {
217     Header h;
218     int next_index;
219 };
220
221 HeaderIterator headerInitIterator(Header h)
222 {
223     HeaderIterator hi = xmalloc(sizeof(struct headerIteratorS));
224
225     headerSort(h);
226
227     hi->h = headerLink(h);
228     hi->next_index = 0;
229     return hi;
230 }
231
232 void headerFreeIterator(HeaderIterator iter)
233 {
234     headerFree(iter->h);
235     free(iter);
236 }
237
238 int headerNextIterator(HeaderIterator iter,
239                  int_32 *tag, int_32 *type, void **p, int_32 *c)
240 {
241     Header h = iter->h;
242     int slot = iter->next_index;
243
244     if (slot == h->indexUsed) {
245         return 0;
246     }
247     iter->next_index++;
248
249     if (tag) {
250         *tag = h->index[slot].info.tag;
251     }
252     copyEntry(h->index + slot, type, p, c, 0);
253
254     return 1;
255 }
256
257 static int indexCmp(const void *ap, const void *bp)
258 {
259     int_32 a, b;
260
261     a = ((struct indexEntry *)ap)->info.tag;
262     b = ((struct indexEntry *)bp)->info.tag;
263     
264     if (a > b) {
265         return 1;
266     } else if (a < b) {
267         return -1;
268     } else {
269         return 0;
270     }
271 }
272
273 void headerSort(Header h)
274 {
275     if (!h->sorted) {
276         qsort(h->index, h->indexUsed, sizeof(struct indexEntry), indexCmp);
277         h->sorted = 1;
278     }
279 }
280
281 Header headerCopy(Header h)
282 {
283     int_32 tag, type, count;
284     void *ptr;
285     HeaderIterator headerIter;
286     Header res = headerNew();
287    
288 #if 0   /* XXX harmless, but headerInitIterator() does this anyways */
289     /* Sort the index -- not really necessary but some old apps may depend
290        on this and it certainly won't hurt anything */
291     headerSort(h);
292 #endif
293     headerIter = headerInitIterator(h);
294
295     while (headerNextIterator(headerIter, &tag, &type, &ptr, &count)) {
296         headerAddEntry(res, tag, type, ptr, count);
297         if (type == RPM_STRING_ARRAY_TYPE || 
298             type == RPM_I18NSTRING_TYPE) free(ptr);
299     }
300
301     res->sorted = 1;
302
303     headerFreeIterator(headerIter);
304
305     return res;
306 }
307
308 /********************************************************************/
309 /*                                                                  */
310 /* Header loading and unloading                                     */
311 /*                                                                  */
312 /********************************************************************/
313
314 Header headerLoad(void *pv)
315 {
316     int_32 il;                  /* index length, data length */
317     char *p = pv;
318     const char * dataStart;
319     struct entryInfo * pe;
320     struct indexEntry * entry; 
321     struct headerToken *h = xmalloc(sizeof(struct headerToken));
322     const char * src;
323     char * dst;
324     int i;
325     int count;
326
327     il = ntohl(*((int_32 *) p));
328     p += sizeof(int_32);
329
330     /* we can skip the data length -- we only store this to allow reading
331        from disk */
332     p += sizeof(int_32);
333
334     h->indexAlloced = il;
335     h->indexUsed = il;
336     h->index = xmalloc(sizeof(struct indexEntry) * il);
337     h->usageCount = 1;
338
339     /* This assumes you only headerLoad() something you headerUnload()-ed */
340     h->sorted = 1;
341
342     pe = (struct entryInfo *) p;
343     dataStart = (char *) (pe + h->indexUsed);
344
345     for (i = 0, entry = h->index; i < h->indexUsed; i++, entry++, pe++) {
346         entry->info.type = htonl(pe->type);
347         entry->info.tag = htonl(pe->tag);
348         entry->info.count = htonl(pe->count);
349         entry->info.offset = -1;
350
351         if (entry->info.type < RPM_MIN_TYPE ||
352             entry->info.type > RPM_MAX_TYPE) return NULL;
353
354         src = dataStart + htonl(pe->offset);
355         entry->length = dataLength(entry->info.type, src, 
356                                    entry->info.count, 1);
357         entry->data = dst = xmalloc(entry->length);
358
359         /* copy data w/ endian conversions */
360         switch (entry->info.type) {
361           case RPM_INT32_TYPE:
362             count = entry->info.count;
363             while (count--) {
364                 *((int_32 *)dst) = htonl(*((int_32 *)src));
365                 src += sizeof(int_32);
366                 dst += sizeof(int_32);
367             }
368             break;
369
370           case RPM_INT16_TYPE:
371             count = entry->info.count;
372             while (count--) {
373                 *((int_16 *)dst) = htons(*((int_16 *)src));
374                 src += sizeof(int_16);
375                 dst += sizeof(int_16);
376             }
377             break;
378
379           default:
380             memcpy(dst, src, entry->length);
381             break;
382         }
383     }
384
385     return h;
386 }
387
388 static void *doHeaderUnload(Header h, /*@out@*/int * lengthPtr)
389 {
390     int i;
391     int type, diff;
392     void *p;
393     int_32 *pi;
394     struct entryInfo * pe;
395     struct indexEntry * entry; 
396     char * chptr, * src, * dataStart;
397     int count;
398
399     headerSort(h);
400
401     *lengthPtr = headerSizeof(h, 0);
402     pi = p = xmalloc(*lengthPtr);
403
404     *pi++ = htonl(h->indexUsed);
405
406     /* data length */
407     *pi++ = htonl(*lengthPtr - sizeof(int_32) - sizeof(int_32) -
408                 (sizeof(struct entryInfo) * h->indexUsed));
409
410     pe = (struct entryInfo *) pi;
411     dataStart = chptr = (char *) (pe + h->indexUsed);
412
413     for (i = 0, entry = h->index; i < h->indexUsed; i++, entry++, pe++) {
414         pe->type = htonl(entry->info.type);
415         pe->tag = htonl(entry->info.tag);
416         pe->count = htonl(entry->info.count);
417
418         /* Alignment */
419         type = entry->info.type;
420         if (typeSizes[type] > 1) {
421             diff = typeSizes[type] - ((chptr - dataStart) % typeSizes[type]);
422             if (diff != typeSizes[type]) {
423                 memset(chptr, 0, diff);
424                 chptr += diff;
425             }
426         }
427
428         pe->offset = htonl(chptr - dataStart);
429
430         /* copy data w/ endian conversions */
431         switch (entry->info.type) {
432           case RPM_INT32_TYPE:
433             count = entry->info.count;
434             src = entry->data;
435             while (count--) {
436                 *((int_32 *)chptr) = htonl(*((int_32 *)src));
437                 chptr += sizeof(int_32);
438                 src += sizeof(int_32);
439             }
440             break;
441
442           case RPM_INT16_TYPE:
443             count = entry->info.count;
444             src = entry->data;
445             while (count--) {
446                 *((int_16 *)chptr) = htons(*((int_16 *)src));
447                 chptr += sizeof(int_16);
448                 src += sizeof(int_16);
449             }
450             break;
451
452           default:
453             memcpy(chptr, entry->data, entry->length);
454             chptr += entry->length;
455             break;
456         }
457     }
458    
459     return p;
460 }
461
462 void *headerUnload(Header h)
463 {
464     int length;
465
466     return doHeaderUnload(h, &length);
467 }
468
469 /********************************************************************/
470 /*                                                                  */
471 /* Reading and writing headers                                      */
472 /*                                                                  */
473 /********************************************************************/
474
475 int headerWrite(FD_t fd, Header h, int magicp)
476 {
477     void * p;
478     int length;
479     int_32 l;
480     ssize_t nb;
481
482     p = doHeaderUnload(h, &length);
483
484     if (magicp) {
485         nb = Fwrite(header_magic, sizeof(header_magic), 1, fd);
486         if (nb != sizeof(header_magic)) {
487             free(p);
488             return 1;
489         }
490         l = htonl(0);
491         nb = Fwrite(&l, sizeof(l), 1, fd);
492         if (nb != sizeof(l)) {
493             free(p);
494             return 1;
495         }
496     }
497     
498     nb = Fwrite(p, length, 1, fd);
499     if (nb != length) {
500         free(p);
501         return 1;
502     }
503
504     free(p);
505     return 0;
506 }
507
508 Header headerRead(FD_t fd, int magicp)
509 {
510     int_32 block[40];
511     int_32 reserved;
512     int_32 * p;
513     int_32 il, dl;
514     int_32 magic;
515     Header h;
516     void * dataBlock;
517     int totalSize;
518     int i;
519
520     i = 2;
521     if (magicp == HEADER_MAGIC_YES)
522         i += 2;
523
524     if (timedRead(fd, block, i * sizeof(*block)) != (i * sizeof(*block)))
525         return NULL;
526     i = 0;
527
528     if (magicp == HEADER_MAGIC_YES) {
529         magic = block[i++];
530         if (memcmp(&magic, header_magic, sizeof(magic))) {
531             return NULL;
532         }
533
534         reserved = block[i++];
535     }
536     
537     il = ntohl(block[i++]);
538     dl = ntohl(block[i++]);
539
540     totalSize = sizeof(int_32) + sizeof(int_32) + 
541                 (il * sizeof(struct entryInfo)) + dl;
542
543     /*
544      * XXX Limit total size of header to 32Mb (~16 times largest known size).
545      */
546     if (totalSize > (32*1024*1024))
547         return NULL;
548
549     dataBlock = p = xmalloc(totalSize);
550     *p++ = htonl(il);
551     *p++ = htonl(dl);
552
553     totalSize -= sizeof(int_32) + sizeof(int_32);
554     if (timedRead(fd, p, totalSize) != totalSize)
555         return NULL;
556     
557     h = headerLoad(dataBlock);
558
559     free(dataBlock);
560
561     return h;
562 }
563
564 int headerGzWrite(FD_t fd, Header h, int magicp)
565 {
566     void * p;
567     int length;
568     int_32 l;
569     ssize_t nb;
570
571     p = doHeaderUnload(h, &length);
572
573     if (magicp) {
574         nb = Fwrite(header_magic, sizeof(header_magic), 1, fd);
575         if (nb != sizeof(header_magic)) {
576             free(p);
577             return 1;
578         }
579         l = htonl(0);
580         nb = Fwrite(&l, sizeof(l), 1, fd);
581         if (nb != sizeof(l)) {
582             free(p);
583             return 1;
584         }
585     }
586     
587     nb = Fwrite(p, length, 1, fd);
588     if (nb != length) {
589         free(p);
590         return 1;
591     }
592
593     free(p);
594     return 0;
595 }
596
597 Header headerGzRead(FD_t fd, int magicp)
598 {
599     int_32 reserved;
600     int_32 * p;
601     int_32 il, dl;
602     int_32 magic;
603     Header h;
604     void * block;
605     int totalSize;
606
607     if (magicp == HEADER_MAGIC_YES) {
608         if (Fread(&magic, sizeof(magic), 1, fd) != sizeof(magic))
609             return NULL;
610         if (memcmp(&magic, header_magic, sizeof(magic))) {
611             return NULL;
612         }
613
614         if (Fread(&reserved, sizeof(reserved), 1, fd) != sizeof(reserved))
615             return NULL;
616     }
617     
618     /* First read the index length (count of index entries) */
619     if (Fread(&il, sizeof(il), 1, fd) != sizeof(il)) 
620         return NULL;
621
622     il = ntohl(il);
623
624     /* Then read the data length (number of bytes) */
625     if (Fread(&dl, sizeof(dl), 1, fd) != sizeof(dl)) 
626         return NULL;
627
628     dl = ntohl(dl);
629
630     totalSize = sizeof(int_32) + sizeof(int_32) + 
631                 (il * sizeof(struct entryInfo)) + dl;
632
633     block = p = xmalloc(totalSize);
634     *p++ = htonl(il);
635     *p++ = htonl(dl);
636
637     totalSize -= sizeof(int_32) + sizeof(int_32);
638     if (Fread(p, totalSize, 1, fd) != totalSize)
639         return NULL;
640     
641     h = headerLoad(block);
642
643     free(block);
644
645     return h;
646 }
647
648 /********************************************************************/
649 /*                                                                  */
650 /* Header dumping                                                   */
651 /*                                                                  */
652 /********************************************************************/
653
654 void headerDump(Header h, FILE *f, int flags, 
655                 const struct headerTagTableEntry * tags)
656 {
657     int i;
658     struct indexEntry *p;
659     const struct headerTagTableEntry * tage;
660     const char *tag;
661     char *type;
662
663     /* First write out the length of the index (count of index entries) */
664     fprintf(f, "Entry count: %d\n", h->indexUsed);
665
666     /* Now write the index */
667     p = h->index;
668     fprintf(f, "\n             CT  TAG                  TYPE         "
669                 "      OFSET      COUNT\n");
670     for (i = 0; i < h->indexUsed; i++) {
671         switch (p->info.type) {
672             case RPM_NULL_TYPE:                 type = "NULL_TYPE";     break;
673             case RPM_CHAR_TYPE:                 type = "CHAR_TYPE";     break;
674             case RPM_BIN_TYPE:                  type = "BIN_TYPE";      break;
675             case RPM_INT8_TYPE:                 type = "INT8_TYPE";     break;
676             case RPM_INT16_TYPE:                type = "INT16_TYPE";    break;
677             case RPM_INT32_TYPE:                type = "INT32_TYPE";    break;
678             /*case RPM_INT64_TYPE:              type = "INT64_TYPE";    break;*/
679             case RPM_STRING_TYPE:               type = "STRING_TYPE";   break;
680             case RPM_STRING_ARRAY_TYPE:         type = "STRING_ARRAY_TYPE"; break;
681             case RPM_I18NSTRING_TYPE:           type = "I18N_STRING_TYPE"; break;
682             default:                    type = "(unknown)";     break;
683         }
684
685         tage = tags;
686         while (tage->name && tage->val != p->info.tag) tage++;
687
688         if (!tage->name)
689             tag = "(unknown)";
690         else
691             tag = tage->name;
692
693         fprintf(f, "Entry      : %.3d (%d)%-14s %-18s 0x%.8x %.8d\n", i,
694                 p->info.tag, tag, type, (unsigned) p->info.offset, (int) 
695                 p->info.count);
696
697         if (flags & HEADER_DUMP_INLINE) {
698             char *dp = p->data;
699             int c = p->info.count;
700             int ct = 0;
701
702             /* Print the data inline */
703             switch (p->info.type) {
704             case RPM_INT32_TYPE:
705                 while (c--) {
706                     fprintf(f, "       Data: %.3d 0x%08x (%d)\n", ct++,
707                             (unsigned) *((int_32 *) dp),
708                             (int) *((int_32 *) dp));
709                     dp += sizeof(int_32);
710                 }
711                 break;
712
713             case RPM_INT16_TYPE:
714                 while (c--) {
715                     fprintf(f, "       Data: %.3d 0x%04x (%d)\n", ct++,
716                             (unsigned) (*((int_16 *) dp) & 0xffff),
717                             (int) *((int_16 *) dp));
718                     dp += sizeof(int_16);
719                 }
720                 break;
721             case RPM_INT8_TYPE:
722                 while (c--) {
723                     fprintf(f, "       Data: %.3d 0x%02x (%d)\n", ct++,
724                             (unsigned) (*((int_8 *) dp) & 0xff),
725                             (int) *((int_8 *) dp));
726                     dp += sizeof(int_8);
727                 }
728                 break;
729             case RPM_BIN_TYPE:
730                 while (c > 0) {
731                     fprintf(f, "       Data: %.3d ", ct);
732                     while (c--) {
733                         fprintf(f, "%02x ", (unsigned) (*(int_8 *)dp & 0xff));
734                         ct++;
735                         dp += sizeof(int_8);
736                         if (! (ct % 8)) {
737                             break;
738                         }
739                     }
740                     fprintf(f, "\n");
741                 }
742                 break;
743             case RPM_CHAR_TYPE:
744                 while (c--) {
745                     char ch = (char) *((char *) dp);
746                     fprintf(f, "       Data: %.3d 0x%2x %c (%d)\n", ct++,
747                             (unsigned)(ch & 0xff),
748                             (isprint(ch) ? ch : ' '),
749                             (int) *((char *) dp));
750                     dp += sizeof(char);
751                 }
752                 break;
753             case RPM_STRING_TYPE:
754             case RPM_STRING_ARRAY_TYPE:
755             case RPM_I18NSTRING_TYPE:
756                 while (c--) {
757                     fprintf(f, "       Data: %.3d %s\n", ct++, (char *) dp);
758                     dp = strchr(dp, 0);
759                     dp++;
760                 }
761                 break;
762             default:
763                 fprintf(stderr, _("Data type %d not supprted\n"), 
764                         (int) p->info.type);
765                 exit(EXIT_FAILURE);
766                 /*@notreached@*/ break;
767             }
768         }
769         p++;
770     }
771 }
772
773 /********************************************************************/
774 /*                                                                  */
775 /* Entry lookup                                                     */
776 /*                                                                  */
777 /********************************************************************/
778
779 static struct indexEntry *findEntry(Header h, int_32 tag, int_32 type)
780 {
781     struct indexEntry * entry, * entry2, * last;
782     struct indexEntry key;
783
784     if (!h->sorted) headerSort(h);
785
786     key.info.tag = tag;
787
788     entry2 = entry = 
789         bsearch(&key, h->index, h->indexUsed, sizeof(struct indexEntry), 
790                 indexCmp);
791     if (!entry) return NULL;
792
793     if (type == RPM_NULL_TYPE) return entry;
794
795     /* look backwards */
796     while (entry->info.tag == tag && entry->info.type != type &&
797            entry > h->index) entry--;
798
799     if (entry->info.tag == tag && entry->info.type == type)
800         return entry;
801
802     last = h->index + h->indexUsed;
803     while (entry2->info.tag == tag && entry2->info.type != type &&
804            entry2 < last) entry2++;
805
806     if (entry->info.tag == tag && entry->info.type == type)
807         return entry;
808
809     return NULL;
810 }
811
812 int headerIsEntry(Header h, int_32 tag)
813 {
814     return (findEntry(h, tag, RPM_NULL_TYPE) ? 1 : 0);
815 }
816
817 int headerGetRawEntry(Header h, int_32 tag, int_32 *type, void **p, int_32 *c)
818 {
819     struct indexEntry * entry;
820
821     if (p == NULL) return headerIsEntry(h, tag);
822
823     /* First find the tag */
824     entry = findEntry(h, tag, RPM_NULL_TYPE);
825     if (!entry) {
826         if (p) *p = NULL;
827         if (c) *c = 0;
828         return 0;
829     }
830
831     copyEntry(entry, type, p, c, 0);
832
833     return 1;
834 }
835
836 static int headerMatchLocale(const char *td, const char *l, const char *le)
837 {
838     const char *fe;
839
840     /*
841      * The range [l,le) contains the next locale to match:
842      *    ll[_CC][.EEEEE][@dddd]
843      * where
844      *    ll    ISO language code (in lowercase).
845      *    CC    (optional) ISO coutnry code (in uppercase).
846      *    EEEEE (optional) encoding (not really standardized).
847      *    dddd  (optional) dialect.
848      */
849
850 #if 0
851   { const char *s, *ll, *CC, *EE, *dd;
852     char *lbuf, *t.
853
854     /* Copy the buffer and parse out components on the fly. */
855     lbuf = alloca(le - l + 1);
856     for (s = l, ll = t = lbuf; *s; s++, t++) {
857         switch (*s) {
858         case '_':
859             *t = '\0';
860             CC = t + 1;
861             break;
862         case '.':
863             *t = '\0';
864             EE = t + 1;
865             break;
866         case '@':
867             *t = '\0';
868             dd = t + 1;
869             break;
870         default:
871             *t = *s;
872             break;
873         }
874     }
875
876     if (ll)     /* ISO language should be lower case */
877         for (t = ll; *t; t++)   *t = tolower(*t);
878     if (CC)     /* ISO country code should be upper case */
879         for (t = CC; *t; t++)   *t = toupper(*t);
880
881     /* There are a total of 16 cases to attempt to match. */
882   }
883 #endif
884
885     /* First try a complete match. */
886     if (strlen(td) == (le-l) && !strncmp(td, l, (le - l)))
887         return 1;
888
889     /* Next, try stripping optional dialect and matching.  */
890     for (fe = l; fe < le && *fe != '@'; fe++)
891         ;
892     if (fe < le && !strncmp(td, l, (fe - l)))
893         return 1;
894
895     /* Next, try stripping optional codeset and matching.  */
896     for (fe = l; fe < le && *fe != '.'; fe++)
897         ;
898     if (fe < le && !strncmp(td, l, (fe - l)))
899         return 1;
900
901     /* Finally, try stripping optional country code and matching. */
902     for (fe = l; fe < le && *fe != '_'; fe++)
903         ;
904     if (fe < le && !strncmp(td, l, (fe - l)))
905         return 1;
906
907     return 0;
908 }
909
910 /*@dependent@*/ static char *
911 headerFindI18NString(Header h, struct indexEntry *entry)
912 {
913     const char *lang, *l, *le;
914     struct indexEntry * table;
915
916     /* XXX Drepper sez' this is the order. */
917     if ((lang = getenv("LANGUAGE")) == NULL &&
918         (lang = getenv("LC_ALL")) == NULL &&
919         (lang = getenv("LC_MESSAGES")) == NULL &&
920         (lang = getenv("LANG")) == NULL)
921             return entry->data;
922     
923     if ((table = findEntry(h, HEADER_I18NTABLE, RPM_STRING_ARRAY_TYPE)) == NULL)
924         return entry->data;
925
926     for (l = lang; *l; l = le) {
927         const char *td;
928         char *ed;
929         int langNum;
930
931         while (*l && *l == ':')                 /* skip leading colons */
932             l++;
933         if (*l == '\0')
934             break;
935         for (le = l; *le && *le != ':'; le++)   /* find end of this locale */
936             ;
937
938         /* For each entry in the header ... */
939         for (langNum = 0, td = table->data, ed = entry->data;
940              langNum < entry->info.count;
941              langNum++, td += strlen(td) + 1, ed += strlen(ed) + 1) {
942
943                 if (headerMatchLocale(td, l, le))
944                     return ed;
945
946         }
947     }
948
949     return entry->data;
950 }
951
952 static int intGetEntry(Header h, int_32 tag, /*@out@*/int_32 *type, /*@out@*/void **p, /*@out@*/int_32 *c,
953                        int minMem)
954 {
955     struct indexEntry * entry;
956     char * chptr;
957
958     /* First find the tag */
959     entry = findEntry(h, tag, RPM_NULL_TYPE);
960     if (!entry) {
961         if (p) *p = NULL;
962         if (c) *c = 0;
963         return 0;
964     }
965
966     if (entry->info.type == RPM_I18NSTRING_TYPE) {
967         chptr = headerFindI18NString(h, entry);
968
969         if (type) *type = RPM_STRING_TYPE;
970         if (c) *c = 1;
971
972         *p = chptr;
973     } else {
974         copyEntry(entry, type, p, c, minMem);
975     }
976
977     return 1;
978 }
979
980 int headerGetEntryMinMemory(Header h, int_32 tag, int_32 *type, void **p, 
981                             int_32 *c)
982 {
983     return intGetEntry(h, tag, type, p, c, 1);
984 }
985
986 int headerGetEntry(Header h, int_32 tag, int_32 * type, void **p, int_32 * c)
987 {
988     return intGetEntry(h, tag, type, p, c, 0);
989 }
990
991 /********************************************************************/
992 /*                                                                  */
993 /* Header creation and deletion                                     */
994 /*                                                                  */
995 /********************************************************************/
996
997 Header headerNew()
998 {
999     Header h = xmalloc(sizeof(struct headerToken));
1000
1001     h->indexAlloced = INDEX_MALLOC_SIZE;
1002     h->index = xcalloc(h->indexAlloced, sizeof(struct indexEntry));
1003     h->indexUsed = 0;
1004
1005     h->sorted = 0;
1006     h->usageCount = 1;
1007
1008     return (Header) h;
1009 }
1010
1011 void headerFree(Header h)
1012 {
1013     int i;
1014
1015     if (--h->usageCount) return;
1016     for (i = 0; i < h->indexUsed; i++)
1017         free(h->index[i].data);
1018
1019     free(h->index);
1020     /*@-refcounttrans@*/ free(h); /*@=refcounttrans@*/
1021 }
1022
1023 Header headerLink(Header h)
1024 {
1025     h->usageCount++;
1026     return h;
1027 }
1028
1029 int headerUsageCount(Header h)
1030 {
1031     return h->usageCount;
1032 }
1033
1034 unsigned int headerSizeof(Header h, int magicp)
1035 {
1036     unsigned int size;
1037     int i, diff;
1038     int_32 type;
1039
1040     headerSort(h);
1041
1042     size = sizeof(int_32);      /* count of index entries */
1043     size += sizeof(int_32);     /* length of data */
1044     size += sizeof(struct entryInfo) * h->indexUsed;
1045     if (magicp) {
1046         size += 8;
1047     }
1048
1049     for (i = 0; i < h->indexUsed; i++) {
1050         /* Alignment */
1051         type = h->index[i].info.type;
1052         if (typeSizes[type] > 1) {
1053             diff = typeSizes[type] - (size % typeSizes[type]);
1054             if (diff != typeSizes[type]) {
1055                 size += diff;
1056             }
1057         }
1058
1059         size += h->index[i].length;
1060     }
1061    
1062     return size;
1063 }
1064
1065 static void copyData(int_32 type, /*@out@*/void * dstPtr, const void * srcPtr, int_32 c, 
1066                         int dataLength)
1067 {
1068     const char ** src;
1069     char * dst;
1070     int i, len;
1071
1072     switch (type) {
1073       case RPM_STRING_ARRAY_TYPE:
1074       case RPM_I18NSTRING_TYPE:
1075         /* Otherwise, p is char** */
1076         i = c;
1077         src = (const char **) srcPtr;
1078         dst = dstPtr;
1079         while (i--) {
1080             len = *src ? strlen(*src) + 1 : 0;
1081             memcpy(dst, *src, len);
1082             dst += len;
1083             src++;
1084         }
1085         break;
1086
1087       default:
1088         memcpy(dstPtr, srcPtr, dataLength);
1089         break;
1090     }
1091 }
1092
1093 static void * grabData(int_32 type, /*@out@*/const void * p, int_32 c, int * lengthPtr)
1094 {
1095     int length;
1096     void * data;
1097
1098     length = dataLength(type, p, c, 0);
1099     data = xmalloc(length);
1100
1101     copyData(type, data, p, c, length);
1102
1103     *lengthPtr = length;
1104     return data;
1105 }
1106
1107 /********************************************************************/
1108 /*                                                                  */
1109 /* Adding and modifying entries                                     */
1110 /*                                                                  */
1111 /********************************************************************/
1112
1113 int headerAddEntry(Header h, int_32 tag, int_32 type, const void *p, int_32 c)
1114 {
1115     struct indexEntry *entry;
1116
1117     h->sorted = 0;
1118
1119     if (c <= 0) {
1120         fprintf(stderr, _("Bad count for headerAddEntry(): %d\n"), (int) c);
1121         exit(EXIT_FAILURE);
1122         /*@notreached@*/
1123     }
1124
1125     /* Allocate more index space if necessary */
1126     if (h->indexUsed == h->indexAlloced) {
1127         h->indexAlloced += INDEX_MALLOC_SIZE;
1128         h->index = xrealloc(h->index,
1129                         h->indexAlloced * sizeof(struct indexEntry));
1130     }
1131
1132     /* Fill in the index */
1133     entry = h->index + h->indexUsed++;
1134     entry->info.tag = tag;
1135     entry->info.type = type;
1136     entry->info.count = c;
1137     entry->info.offset = -1;
1138
1139     entry->data = grabData(type, p, c, &entry->length);
1140
1141     h->sorted = 0;
1142
1143     return 1;
1144 }
1145
1146 char **
1147 headerGetLangs(Header h)
1148 {
1149     char **s, *e, **table;
1150     int i, type, count;
1151
1152     if (!headerGetRawEntry(h, HEADER_I18NTABLE, &type, (void **)&s, &count))
1153         return NULL;
1154
1155     if ((table = (char **)xcalloc((count+1), sizeof(char *))) == NULL)
1156         return NULL;
1157
1158     for (i = 0, e = *s; i < count > 0; i++, e += strlen(e)+1) {
1159         table[i] = e;
1160     }
1161     table[count] = NULL;
1162
1163     return table;
1164 }
1165
1166 int headerAddI18NString(Header h, int_32 tag, const char * string, const char * lang)
1167 {
1168     struct indexEntry * table, * entry;
1169     char * chptr;
1170     const char ** strArray;
1171     int length;
1172     int ghosts;
1173     int i, langNum;
1174     char * buf;
1175
1176     table = findEntry(h, HEADER_I18NTABLE, RPM_STRING_ARRAY_TYPE);
1177     entry = findEntry(h, tag, RPM_I18NSTRING_TYPE);
1178
1179     if (!table && entry) {
1180         return 0;               /* this shouldn't ever happen!! */
1181     }
1182
1183     if (!table && !entry) {
1184         const char * charArray[2];
1185         int count = 0;
1186         if (!lang || (lang[0] == 'C' && lang[1] == '\0')) {
1187             charArray[count++] = "C";
1188         } else {
1189             charArray[count++] = "C";
1190             charArray[count++] = lang;
1191         }
1192         if (!headerAddEntry(h, HEADER_I18NTABLE, RPM_STRING_ARRAY_TYPE, 
1193                         &charArray, count))
1194             return 0;
1195         table = findEntry(h, HEADER_I18NTABLE, RPM_STRING_ARRAY_TYPE);
1196     }
1197
1198     if (!lang) lang = "C";
1199
1200     chptr = table->data;
1201     for (langNum = 0; langNum < table->info.count; langNum++) {
1202         if (!strcmp(chptr, lang)) break;
1203         chptr += strlen(chptr) + 1;
1204     }
1205
1206     if (langNum >= table->info.count) {
1207         length = strlen(lang) + 1;
1208         table->data = xrealloc(table->data, table->length + length);
1209         memcpy(((char *)table->data) + table->length, lang, length);
1210         table->length += length;
1211         table->info.count++;
1212     }
1213
1214     if (!entry) {
1215         strArray = alloca(sizeof(*strArray) * (langNum + 1));
1216         for (i = 0; i < langNum; i++)
1217             strArray[i] = "";
1218         strArray[langNum] = string;
1219         return headerAddEntry(h, tag, RPM_I18NSTRING_TYPE, strArray, 
1220                                 langNum + 1);
1221     } else if (langNum >= entry->info.count) {
1222         ghosts = langNum - entry->info.count;
1223         
1224         length = strlen(string) + 1 + ghosts;
1225         entry->data = xrealloc(entry->data, entry->length + length);
1226
1227         memset(((char *)entry->data) + entry->length, '\0', ghosts);
1228         strcpy(((char *)entry->data) + entry->length + ghosts, string);
1229
1230         entry->length += length;
1231         entry->info.count = langNum + 1;
1232     } else {
1233         char *b, *be, *e, *ee, *t;
1234         size_t bn, sn, en;
1235
1236         /* Set beginning/end pointers to previous data */
1237         b = be = e = ee = entry->data;
1238         for (i = 0; i < table->info.count; i++) {
1239             if (i == langNum)
1240                 be = ee;
1241             ee += strlen(ee) + 1;
1242             if (i == langNum)
1243                 e  = ee;
1244         }
1245
1246         /* Get storage for new buffer */
1247         bn = (be-b);
1248         sn = strlen(string) + 1;
1249         en = (ee-e);
1250         length = bn + sn + en;
1251         t = buf = xmalloc(length);
1252
1253         /* Copy values into new storage */
1254         memcpy(t, b, bn);
1255         t += bn;
1256         memcpy(t, string, sn);
1257         t += sn;
1258         memcpy(t, e, en);
1259         t += en;
1260
1261         /* Replace I18N string array */
1262         entry->length -= strlen(be) + 1;
1263         entry->length += sn;
1264         free(entry->data);
1265         entry->data = buf;
1266     }
1267
1268     return 0;
1269 }
1270
1271 /* if there are multiple entries with this tag, the first one gets replaced */
1272 int headerModifyEntry(Header h, int_32 tag, int_32 type, void *p, int_32 c)
1273 {
1274     struct indexEntry *entry;
1275     void * oldData;
1276
1277     /* First find the tag */
1278     entry = findEntry(h, tag, type);
1279     if (!entry) {
1280         return 0;
1281     }
1282
1283     /* make sure entry points to the first occurence of this tag */
1284     while (entry > h->index && (entry - 1)->info.tag == tag)  
1285         entry--;
1286
1287     /* free after we've grabbed the new data in case the two are intertwined;
1288        that's a bad idea but at least we won't break */
1289     oldData = entry->data;
1290
1291     entry->info.count = c;
1292     entry->info.type = type;
1293     entry->data = grabData(type, p, c, &entry->length);
1294
1295     free(oldData);
1296     
1297     return 1;
1298 }
1299
1300 int headerAddOrAppendEntry(Header h, int_32 tag, int_32 type,
1301                            void * p, int_32 c)
1302 {
1303     if (findEntry(h, tag, type)) {
1304         return headerAppendEntry(h, tag, type, p, c);
1305     } else {
1306         return headerAddEntry(h, tag, type, p, c);
1307     }
1308 }
1309
1310 int headerAppendEntry(Header h, int_32 tag, int_32 type, void * p, int_32 c)
1311 {
1312     struct indexEntry *entry;
1313     int length;
1314
1315     /* First find the tag */
1316     entry = findEntry(h, tag, type);
1317     if (!entry) {
1318         return 0;
1319     }
1320
1321     if (type == RPM_STRING_TYPE || type == RPM_I18NSTRING_TYPE) {
1322         /* we can't do this */
1323         return 0;
1324     }
1325
1326     length = dataLength(type, p, c, 0);
1327
1328     entry->data = xrealloc(entry->data, entry->length + length);
1329     copyData(type, ((char *) entry->data) + entry->length, p, c, length);
1330
1331     entry->length += length;
1332
1333     entry->info.count += c;
1334
1335     return 0;
1336 }
1337
1338 int headerRemoveEntry(Header h, int_32 tag)
1339 {
1340     struct indexEntry * entry, * last;
1341
1342     entry = findEntry(h, tag, RPM_NULL_TYPE);
1343     if (!entry) return 1;
1344
1345     /* make sure entry points to the first occurence of this tag */
1346     while (entry > h->index && (entry - 1)->info.tag == tag)  
1347         entry--;
1348
1349     /* We might be better off just counting the number of items off the
1350        end and issuing one big memcpy, but memcpy() doesn't have to work
1351        on overlapping regions thanks to ANSI <sigh>. A alloca() and two
1352        memcpy() would probably still be a win (as our moving from the
1353        end to the middle isn't very nice to the qsort() we'll have to
1354        do to make up for this!), but I'm too lazy to implement it. Just
1355        remember that this repeating this is basically nlogn thanks to this
1356        dumb implementation (but n is the best we'd do anyway) */
1357
1358     last = h->index + h->indexUsed;
1359     while (entry->info.tag == tag && entry < last) {
1360         free(entry->data);
1361         *(entry++) = *(--last);
1362     }
1363     h->indexUsed = last - h->index;
1364
1365     h->sorted = 0;
1366
1367     return 0;
1368 }
1369
1370 static char escapedChar(const char ch)
1371 {
1372     switch (ch) {
1373       case 'a':         return '\a';
1374       case 'b':         return '\b';
1375       case 'f':         return '\f';
1376       case 'n':         return '\n';
1377       case 'r':         return '\r';
1378       case 't':         return '\t';
1379       case 'v':         return '\v';
1380
1381       default:          return ch;
1382     }
1383 }
1384
1385 static void freeFormat( /*@only@*/ struct sprintfToken * format, int num)
1386 {
1387     int i;
1388
1389     for (i = 0; i < num; i++) {
1390         switch (format[i].type) {
1391         case PTOK_ARRAY:
1392             freeFormat(format[i].u.array.format, format[i].u.array.numTokens);
1393             break;
1394         case PTOK_COND:
1395             freeFormat(format[i].u.cond.ifFormat, 
1396                         format[i].u.cond.numIfTokens);
1397             freeFormat(format[i].u.cond.elseFormat, 
1398                         format[i].u.cond.numElseTokens);
1399             break;
1400         case PTOK_NONE:
1401         case PTOK_TAG:
1402         case PTOK_STRING:
1403         default:
1404             break;
1405         }
1406     }
1407     free(format);
1408 }
1409
1410 static void findTag(char * name, const struct headerTagTableEntry * tags, 
1411                     const struct headerSprintfExtension * extensions,
1412                     /*@out@*/const struct headerTagTableEntry ** tagMatch,
1413                     /*@out@*/const struct headerSprintfExtension ** extMatch)
1414 {
1415     const struct headerTagTableEntry * entry;
1416     const struct headerSprintfExtension * ext;
1417     char * tagname;
1418     int i;
1419
1420     *tagMatch = NULL;
1421     *extMatch = NULL;
1422
1423     if (strncmp("RPMTAG_", name, sizeof("RPMTAG_")-1)) {
1424         tagname = alloca(strlen(name) + 10);
1425         strcpy(tagname, "RPMTAG_");
1426         strcat(tagname, name);
1427     } else {
1428         tagname = name;
1429     }
1430
1431     for (entry = tags; entry->name; entry++)
1432         if (!strcasecmp(entry->name, tagname)) break;
1433
1434     if (entry->name) {
1435         *tagMatch = entry;
1436     } else {
1437         ext = extensions, i =0;
1438         while (ext->type != HEADER_EXT_LAST) {
1439             if (ext->type == HEADER_EXT_TAG && 
1440                 !strcasecmp(ext->name, tagname)) {
1441                 break;
1442             }
1443
1444             if (ext->type == HEADER_EXT_MORE)
1445                 ext = ext->u.more;
1446             else
1447                 ext++;
1448             i++;
1449         }
1450
1451         if (ext->type == HEADER_EXT_TAG) {
1452             *extMatch = ext;
1453         }
1454     }
1455 }
1456
1457 /* forward ref */
1458 static int parseExpression(struct sprintfToken * token, char * str, 
1459         const struct headerTagTableEntry * tags, 
1460         const struct headerSprintfExtension * extensions,
1461         /*@out@*/char ** endPtr, /*@out@*/const char ** error);
1462
1463 static int parseFormat(char * str, const struct headerTagTableEntry * tags,
1464         const struct headerSprintfExtension * extensions,
1465         /*@out@*/struct sprintfToken ** formatPtr, /*@out@*/int * numTokensPtr,
1466         /*@out@*/char ** endPtr, int state, /*@out@*/const char ** error)
1467 {
1468     char * chptr, * start, * next, * dst;
1469     struct sprintfToken * format;
1470     int numTokens;
1471     int currToken;
1472     const struct headerTagTableEntry * entry;
1473     const struct headerSprintfExtension * ext;
1474     int i;
1475     int done = 0;
1476
1477     /* upper limit on number of individual formats */
1478     numTokens = 0;
1479     for (chptr = str; *chptr; chptr++)
1480         if (*chptr == '%') numTokens++;
1481     numTokens = numTokens * 2 + 1;
1482
1483     format = xcalloc(numTokens, sizeof(*format));
1484     if (endPtr) *endPtr = NULL;
1485
1486     dst = start = str;
1487     currToken = -1;
1488     while (*start && !done) {
1489         switch (*start) {
1490           case '%':
1491             /* handle %% */
1492             if (*(start + 1) == '%') {
1493                 if (currToken < 0 || format[currToken].type != PTOK_STRING) {
1494                     currToken++;
1495                     format[currToken].type = PTOK_STRING;
1496                     dst = format[currToken].u.string.string = start;
1497                 }
1498
1499                 start++;
1500
1501                 *dst++ = *start++;
1502
1503                 break; /* out of switch */
1504             } 
1505
1506             currToken++;
1507             *dst++ = '\0';
1508             start++;
1509
1510             if (*start == '|') {
1511                 char * newEnd;
1512
1513                 start++;
1514                 if (parseExpression(format + currToken, start, tags, 
1515                                     extensions, &newEnd, error)) {
1516                     freeFormat(format, numTokens);
1517                     return 1;
1518                 }
1519                 start = newEnd;
1520             } else {
1521                 format[currToken].u.tag.format = start;
1522                 format[currToken].u.tag.pad = 0;
1523                 format[currToken].u.tag.justOne = 0;
1524                 format[currToken].u.tag.arrayCount = 0;
1525
1526                 chptr = start;
1527                 while (*chptr && *chptr != '{' && *chptr != '%') chptr++;
1528                 if (!*chptr || *chptr == '%') {
1529                     *error = _("missing { after %");
1530                     freeFormat(format, numTokens);
1531                     return 1;
1532                 }
1533
1534                 *chptr++ = '\0';
1535
1536                 while (start < chptr) {
1537                     if (isdigit(*start)) {
1538                         i = strtoul(start, &start, 10);
1539                         format[currToken].u.tag.pad += i;
1540                     } else {
1541                         start++;
1542                     }
1543                 }
1544
1545                 if (*start == '=') {
1546                     format[currToken].u.tag.justOne = 1;
1547                     start++;
1548                 } else if (*start == '#') {
1549                     format[currToken].u.tag.justOne = 1;
1550                     format[currToken].u.tag.arrayCount = 1;
1551                     start++;
1552                 }
1553
1554                 next = start;
1555                 while (*next && *next != '}') next++;
1556                 if (!*next) {
1557                     *error = _("missing } after %{");
1558                     freeFormat(format, numTokens);
1559                     return 1;
1560                 }
1561                 *next++ = '\0';
1562
1563                 chptr = start;
1564                 while (*chptr && *chptr != ':') chptr++;
1565
1566                 if (*chptr) {
1567                     *chptr++ = '\0';
1568                     if (!*chptr) {
1569                         *error = _("empty tag format");
1570                         freeFormat(format, numTokens);
1571                         return 1;
1572                     }
1573                     format[currToken].u.tag.type = chptr;
1574                 } else {
1575                     format[currToken].u.tag.type = NULL;
1576                 }
1577             
1578                 if (!*start) {
1579                     *error = _("empty tag name");
1580                     freeFormat(format, numTokens);
1581                     return 1;
1582                 }
1583
1584                 i = 0;
1585                 findTag(start, tags, extensions, &entry, &ext);
1586
1587                 if (entry) {
1588                     format[currToken].u.tag.ext = NULL;
1589                     format[currToken].u.tag.tag = entry->val;
1590                 } else if (ext) {
1591                     format[currToken].u.tag.ext = ext->u.tagFunction;
1592                     format[currToken].u.tag.extNum = ext - extensions;
1593                 } else {
1594                     *error = _("unknown tag");
1595                     freeFormat(format, numTokens);
1596                     return 1;
1597                 }
1598
1599                 format[currToken].type = PTOK_TAG;
1600
1601                 start = next;
1602             }
1603
1604             break;
1605
1606           case '[':
1607             *dst++ = '\0';
1608             *start++ = '\0';
1609             currToken++;
1610
1611             if (parseFormat(start, tags, extensions, 
1612                             &format[currToken].u.array.format,
1613                             &format[currToken].u.array.numTokens,
1614                             &start, PARSER_IN_ARRAY, error)) {
1615                 freeFormat(format, numTokens);
1616                 return 1;
1617             }
1618
1619             if (!start) {
1620                 *error = _("] expected at end of array");
1621                 freeFormat(format, numTokens);
1622                 return 1;
1623             }
1624
1625             dst = start;
1626
1627             format[currToken].type = PTOK_ARRAY;
1628
1629             break;
1630
1631           case ']':
1632           case '}':
1633             if ((*start == ']' && state != PARSER_IN_ARRAY) ||
1634                 (*start == '}' && state != PARSER_IN_EXPR)) {
1635                 if (*start == ']')
1636                     *error = _("unexpected ]");
1637                 else
1638                     *error = _("unexpected }");
1639                 freeFormat(format, numTokens);
1640                 return 1;
1641             }
1642             *start++ = '\0';
1643             *endPtr = start;
1644             done = 1;
1645             break;
1646
1647           default:
1648             if (currToken < 0 || format[currToken].type != PTOK_STRING) {
1649                 currToken++;
1650                 format[currToken].type = PTOK_STRING;
1651                 dst = format[currToken].u.string.string = start;
1652             }
1653
1654             if (*start == '\\') {
1655                 start++;
1656                 *dst++ = escapedChar(*start++);
1657             } else {
1658                 *dst++ = *start++;
1659             }
1660             break;
1661         }
1662     }
1663
1664     *dst = '\0';
1665
1666     currToken++;
1667     for (i = 0; i < currToken; i++) {
1668         if (format[i].type == PTOK_STRING)
1669             format[i].u.string.len = strlen(format[i].u.string.string);
1670     }
1671
1672     *numTokensPtr = currToken;
1673     *formatPtr = format;
1674
1675     return 0;
1676 }
1677
1678 static int parseExpression(struct sprintfToken * token, char * str, 
1679         const struct headerTagTableEntry * tags, 
1680         const struct headerSprintfExtension * extensions,
1681         /*@out@*/ char ** endPtr, /*@out@*/ const char ** error)
1682 {
1683     char * chptr, * end;
1684     const struct headerTagTableEntry * tag;
1685     const struct headerSprintfExtension * ext;
1686
1687     *error = NULL;
1688     chptr = str;
1689     while (*chptr && *chptr != '?') chptr++;
1690
1691     if (*chptr != '?') {
1692         *error = _("? expected in expression");
1693         return 1;
1694     }
1695
1696     *chptr++ = '\0';;
1697
1698     if (*chptr != '{') {
1699         *error = _("{ expected after ? in expression");
1700         return 1;
1701     }
1702
1703     chptr++;
1704
1705     if (parseFormat(chptr, tags, extensions, &token->u.cond.ifFormat, 
1706                     &token->u.cond.numIfTokens, &end, PARSER_IN_EXPR, error)) 
1707         return 1;
1708     if (!*end) {
1709         *error = _("} expected in expression");
1710         freeFormat(token->u.cond.ifFormat, token->u.cond.numIfTokens);
1711         token->u.cond.ifFormat = NULL;
1712         return 1;
1713     }
1714
1715     chptr = end;
1716     if (*chptr != ':' && *chptr != '|') {
1717         *error = _(": expected following ? subexpression");
1718         freeFormat(token->u.cond.ifFormat, token->u.cond.numIfTokens);
1719         token->u.cond.ifFormat = NULL;
1720         return 1;
1721     }
1722
1723     if (*chptr == '|') {
1724         parseFormat(xstrdup(""), tags, extensions, &token->u.cond.elseFormat, 
1725                         &token->u.cond.numElseTokens, &end, PARSER_IN_EXPR, 
1726                         error);
1727     } else {
1728         chptr++;
1729
1730         if (*chptr != '{') {
1731             *error = _("{ expected after : in expression");
1732             freeFormat(token->u.cond.ifFormat, token->u.cond.numIfTokens);
1733             token->u.cond.ifFormat = NULL;
1734             return 1;
1735         }
1736
1737         chptr++;
1738
1739         if (parseFormat(chptr, tags, extensions, &token->u.cond.elseFormat, 
1740                         &token->u.cond.numElseTokens, &end, PARSER_IN_EXPR, 
1741                         error)) 
1742             return 1;
1743         if (!*end) {
1744             *error = _("} expected in expression");
1745             freeFormat(token->u.cond.ifFormat, token->u.cond.numIfTokens);
1746             token->u.cond.ifFormat = NULL;
1747             return 1;
1748         }
1749
1750         chptr = end;
1751         if (*chptr != '|') {
1752             *error = _("| expected at end of expression");
1753             freeFormat(token->u.cond.ifFormat, token->u.cond.numIfTokens);
1754             token->u.cond.ifFormat = NULL;
1755             freeFormat(token->u.cond.elseFormat, token->u.cond.numElseTokens);
1756             token->u.cond.elseFormat = NULL;
1757             return 1;
1758         }
1759     }
1760         
1761     chptr++;
1762
1763     *endPtr = chptr;
1764
1765     findTag(str, tags, extensions, &tag, &ext);
1766
1767     if (tag) {
1768         token->u.cond.tag.ext = NULL;
1769         token->u.cond.tag.tag = tag->val;
1770     } else if (ext) {
1771         token->u.cond.tag.ext = ext->u.tagFunction;
1772         token->u.cond.tag.extNum = ext - extensions;
1773     } else {
1774         token->u.cond.tag.ext = NULL;
1775         token->u.cond.tag.tag = -1;
1776     }
1777         
1778     token->type = PTOK_COND;
1779
1780     return 0;
1781 }
1782
1783 static int getExtension(Header h, headerTagTagFunction fn, /*@out@*/int_32 * typeptr,
1784                         /*@out@*/void ** data, /*@out@*/int_32 * countptr, 
1785                         struct extensionCache * ext)
1786 {
1787     if (!ext->avail) {
1788         if (fn(h, &ext->type, &ext->data, &ext->count, &ext->freeit))
1789             return 1;
1790         ext->avail = 1;
1791     }
1792
1793     *typeptr = ext->type;
1794     *data = ext->data;
1795     *countptr = ext->count;
1796
1797     return 0;
1798 }
1799
1800 static char * formatValue(struct sprintfTag * tag, Header h, 
1801                           const struct headerSprintfExtension * extensions,
1802                           struct extensionCache * extCache, int element)
1803 {
1804     int len;
1805     char buf[20];
1806     int_32 count, type;
1807     void * data;
1808     unsigned int intVal;
1809     char * val = NULL;
1810     char ** strarray;
1811     int mayfree = 0;
1812     int countBuf;
1813     headerTagFormatFunction tagtype = NULL;
1814     const struct headerSprintfExtension * ext;
1815
1816     if (tag->ext) {
1817         if (getExtension(h, tag->ext, &type, &data, &count, 
1818                          extCache + tag->extNum)) {
1819             count = 1;
1820             type = RPM_STRING_TYPE;     
1821             data = "(none)";            /* XXX i18n? NO!, sez; gafton */
1822         }
1823     } else {
1824         if (!headerGetEntry(h, tag->tag, &type, &data, &count)){
1825             count = 1;
1826             type = RPM_STRING_TYPE;     
1827             data = "(none)";            /* XXX i18n? NO!, sez; gafton */
1828         }
1829
1830         mayfree = 1;
1831     }
1832
1833     if (tag->arrayCount) {
1834         if (type == RPM_STRING_ARRAY_TYPE) free(data);
1835
1836         countBuf = count;
1837         data = &countBuf;
1838         count = 1;
1839         type = RPM_INT32_TYPE;
1840     }
1841
1842     strcpy(buf, "%");
1843     strcat(buf, tag->format);
1844
1845     if (tag->type) {
1846         ext = extensions;
1847         while (ext->type != HEADER_EXT_LAST) {
1848             if (ext->type == HEADER_EXT_FORMAT && 
1849                 !strcmp(ext->name, tag->type)) {
1850                 tagtype = ext->u.formatFunction;
1851                 break;
1852             }
1853
1854             if (ext->type == HEADER_EXT_MORE)
1855                 ext = ext->u.more;
1856             else
1857                 ext++;
1858         }
1859     }
1860     
1861     switch (type) {
1862       case RPM_STRING_ARRAY_TYPE:
1863         strarray = data;
1864
1865         if (tagtype) {
1866             val = tagtype(RPM_STRING_TYPE, strarray[element], buf, tag->pad, 0);
1867         }
1868
1869         if (!val) {
1870             strcat(buf, "s");
1871
1872             len = strlen(strarray[element]) + tag->pad + 20;
1873             val = xmalloc(len);
1874             sprintf(val, buf, strarray[element]);
1875         }
1876
1877         if (mayfree) free(data);
1878
1879         break;
1880
1881       case RPM_STRING_TYPE:
1882         if (tagtype) {
1883             val = tagtype(RPM_STRING_ARRAY_TYPE, data, buf, tag->pad,  0);
1884         }
1885
1886         if (!val) {
1887             strcat(buf, "s");
1888
1889             len = strlen(data) + tag->pad + 20;
1890             val = xmalloc(len);
1891             sprintf(val, buf, data);
1892         }
1893         break;
1894
1895       case RPM_CHAR_TYPE:
1896       case RPM_INT8_TYPE:
1897       case RPM_INT16_TYPE:
1898       case RPM_INT32_TYPE:
1899         switch (type) {
1900           case RPM_CHAR_TYPE:   
1901           case RPM_INT8_TYPE:   intVal = *(((int_8 *) data) + element); break;
1902           case RPM_INT16_TYPE:  intVal = *(((uint_16 *) data) + element); break;
1903           default:              /* keep -Wall quiet */
1904           case RPM_INT32_TYPE:  intVal = *(((int_32 *) data) + element);
1905         }
1906
1907         if (tagtype) {
1908             val = tagtype(RPM_INT32_TYPE, &intVal, buf, tag->pad,  element);
1909         }
1910
1911         if (!val) {
1912             strcat(buf, "d");
1913             len = 10 + tag->pad + 20;
1914             val = xmalloc(len);
1915             sprintf(val, buf, intVal);
1916         }
1917         break;
1918
1919       default:
1920         val = xmalloc(20);
1921         strcpy(val, _("(unknown type)"));
1922         break;
1923     }
1924
1925     return val;
1926 }
1927
1928 static char * singleSprintf(Header h, struct sprintfToken * token,
1929                             const struct headerSprintfExtension * extensions,
1930                             struct extensionCache * extCache, int element)
1931 {
1932     char * val, * thisItem;
1933     int thisItemLen;
1934     int len, alloced;
1935     int i, j;
1936     int numElements;
1937     int type;
1938     void * data;
1939     struct sprintfToken * condFormat;
1940     int condNumFormats;
1941
1942     /* we assume the token and header have been validated already! */
1943
1944     switch (token->type) {
1945       case PTOK_NONE:
1946         break;
1947
1948       case PTOK_STRING:
1949         val = xmalloc(token->u.string.len + 1);
1950         strcpy(val, token->u.string.string);
1951         break;
1952
1953       case PTOK_TAG:
1954         val = formatValue(&token->u.tag, h, extensions, extCache,
1955                           token->u.tag.justOne ? 0 : element);
1956         break;
1957
1958       case PTOK_COND:
1959         if (token->u.cond.tag.ext ||
1960             headerIsEntry(h, token->u.cond.tag.tag)) {
1961             condFormat = token->u.cond.ifFormat;
1962             condNumFormats = token->u.cond.numIfTokens;
1963         } else {
1964             condFormat = token->u.cond.elseFormat;
1965             condNumFormats = token->u.cond.numElseTokens;
1966         }
1967
1968         alloced = condNumFormats * 20;
1969         val = xmalloc(alloced ? alloced : 1);
1970         *val = '\0';
1971         len = 0;
1972
1973         for (i = 0; i < condNumFormats; i++) {
1974             thisItem = singleSprintf(h, condFormat + i, 
1975                                      extensions, extCache, element);
1976             thisItemLen = strlen(thisItem);
1977             if ((thisItemLen + len) >= alloced) {
1978                 alloced = (thisItemLen + len) + 200;
1979                 val = xrealloc(val, alloced);   
1980             }
1981             strcat(val, thisItem);
1982             len += thisItemLen;
1983             free(thisItem);
1984         }
1985
1986         break;
1987
1988       case PTOK_ARRAY:
1989         numElements = -1;
1990         for (i = 0; i < token->u.array.numTokens; i++) {
1991             if (token->u.array.format[i].type != PTOK_TAG ||
1992                 token->u.array.format[i].u.tag.arrayCount ||
1993                 token->u.array.format[i].u.tag.justOne) continue;
1994
1995             if (token->u.array.format[i].u.tag.ext) {
1996                 if (getExtension(h, token->u.array.format[i].u.tag.ext,
1997                                  &type, &data, &numElements, 
1998                                  extCache + 
1999                                    token->u.array.format[i].u.tag.extNum))
2000                      continue;
2001             } else {
2002                 if (!headerGetEntry(h, token->u.array.format[i].u.tag.tag, 
2003                                     &type, (void **) &val, &numElements))
2004                     continue;
2005                 if (type == RPM_STRING_ARRAY_TYPE) free(val);
2006             } 
2007             break;
2008         }
2009
2010         if (numElements == -1) {
2011             val = xmalloc(20);
2012             strcpy(val, "(none)");      /* XXX i18n? NO!, sez; gafton */
2013         } else {
2014             alloced = numElements * token->u.array.numTokens * 20;
2015             val = xmalloc(alloced);
2016             *val = '\0';
2017             len = 0;
2018
2019             for (j = 0; j < numElements; j++) {
2020                 for (i = 0; i < token->u.array.numTokens; i++) {
2021                     thisItem = singleSprintf(h, token->u.array.format + i, 
2022                                              extensions, extCache, j);
2023                     thisItemLen = strlen(thisItem);
2024                     if ((thisItemLen + len) >= alloced) {
2025                         alloced = (thisItemLen + len) + 200;
2026                         val = xrealloc(val, alloced);   
2027                     }
2028                     strcat(val, thisItem);
2029                     len += thisItemLen;
2030                     free(thisItem);
2031                 }
2032             }
2033         }
2034            
2035         break;
2036     }
2037
2038     return val;
2039 }
2040
2041 static struct extensionCache * allocateExtensionCache(
2042                      const struct headerSprintfExtension * extensions)
2043 {
2044     const struct headerSprintfExtension * ext = extensions;
2045     int i = 0;
2046
2047     while (ext->type != HEADER_EXT_LAST) {
2048         i++;
2049         if (ext->type == HEADER_EXT_MORE)
2050             ext = ext->u.more;
2051         else
2052             ext++;
2053     }
2054
2055     return xcalloc(i, sizeof(struct extensionCache));
2056 }
2057
2058 static void freeExtensionCache(const struct headerSprintfExtension * extensions,
2059                         /*@only@*/struct extensionCache * cache)
2060 {
2061     const struct headerSprintfExtension * ext = extensions;
2062     int i = 0;
2063
2064     while (ext->type != HEADER_EXT_LAST) {
2065         if (cache[i].freeit) free(cache[i].data);
2066
2067         i++;
2068         if (ext->type == HEADER_EXT_MORE)
2069             ext = ext->u.more;
2070         else
2071             ext++;
2072     }
2073
2074     free(cache);
2075 }
2076
2077 char * headerSprintf(Header h, const char * origFmt, 
2078                      const struct headerTagTableEntry * tags,
2079                      const struct headerSprintfExtension * extensions,
2080                      const char ** error)
2081 {
2082     char * fmtString;
2083     struct sprintfToken * format;
2084     int numTokens;
2085     char * answer, * piece;
2086     int answerLength;
2087     int answerAlloced;
2088     int pieceLength;
2089     int i;
2090     struct extensionCache * extCache;
2091  
2092     /*fmtString = escapeString(origFmt);*/
2093     fmtString = xstrdup(origFmt);
2094    
2095     if (parseFormat(fmtString, tags, extensions, &format, &numTokens, 
2096                     NULL, PARSER_BEGIN, error)) {
2097         free(fmtString);
2098         return NULL;
2099     }
2100
2101     extCache = allocateExtensionCache(extensions);
2102
2103     answerAlloced = 1024;
2104     answerLength = 0;
2105     answer = xmalloc(answerAlloced);
2106     *answer = '\0';
2107
2108     for (i = 0; i < numTokens; i++) {
2109         piece = singleSprintf(h, format + i, extensions, extCache, 0);
2110         if (piece) {
2111             pieceLength = strlen(piece);
2112             if ((answerLength + pieceLength) >= answerAlloced) {
2113                 while ((answerLength + pieceLength) >= answerAlloced) 
2114                     answerAlloced += 1024;
2115                 answer = xrealloc(answer, answerAlloced);
2116             }
2117
2118             strcat(answer, piece);
2119             answerLength += pieceLength;
2120             free(piece);
2121         }
2122     }
2123
2124     free(fmtString);
2125     freeExtensionCache(extensions, extCache);
2126     free(format);
2127
2128     return answer;
2129 }
2130
2131 static char * octalFormat(int_32 type, const void * data, 
2132                 char * formatPrefix, int padding, /*@unused@*/int element)
2133 {
2134     char * val;
2135
2136     if (type != RPM_INT32_TYPE) {
2137         val = xmalloc(20);
2138         strcpy(val, _("(not a number)"));
2139     } else {
2140         val = xmalloc(20 + padding);
2141         strcat(formatPrefix, "o");
2142         sprintf(val, formatPrefix, *((int_32 *) data));
2143     }
2144
2145     return val;
2146 }
2147
2148 static char * hexFormat(int_32 type, const void * data, 
2149                 char * formatPrefix, int padding, /*@unused@*/int element)
2150 {
2151     char * val;
2152
2153     if (type != RPM_INT32_TYPE) {
2154         val = xmalloc(20);
2155         strcpy(val, _("(not a number)"));
2156     } else {
2157         val = xmalloc(20 + padding);
2158         strcat(formatPrefix, "x");
2159         sprintf(val, formatPrefix, *((int_32 *) data));
2160     }
2161
2162     return val;
2163 }
2164
2165 static char * realDateFormat(int_32 type, const void * data, 
2166                 char * formatPrefix, int padding, /*@unused@*/int element,
2167                 char * strftimeFormat)
2168 {
2169     char * val;
2170     struct tm * tstruct;
2171     char buf[50];
2172
2173     if (type != RPM_INT32_TYPE) {
2174         val = xmalloc(20);
2175         strcpy(val, _("(not a number)"));
2176     } else {
2177         val = xmalloc(50 + padding);
2178         strcat(formatPrefix, "s");
2179
2180         /* this is important if sizeof(int_32) ! sizeof(time_t) */
2181         {   time_t dateint = *((int_32 *) data);
2182             tstruct = localtime(&dateint);
2183         }
2184         (void)strftime(buf, sizeof(buf) - 1, strftimeFormat, tstruct);
2185         sprintf(val, formatPrefix, buf);
2186     }
2187
2188     return val;
2189 }
2190
2191 static char * dateFormat(int_32 type, const void * data, 
2192                          char * formatPrefix, int padding, int element)
2193 {
2194     return realDateFormat(type, data, formatPrefix, padding, element, "%c");
2195 }
2196
2197 static char * dayFormat(int_32 type, const void * data, 
2198                          char * formatPrefix, int padding, int element)
2199 {
2200     return realDateFormat(type, data, formatPrefix, padding, element, 
2201                           "%a %b %d %Y");
2202 }
2203
2204 static char * shescapeFormat(int_32 type, const void * data, 
2205                 char * formatPrefix, int padding, /*@unused@*/int element)
2206 {
2207     char * result, * dst, * src, * buf;
2208
2209     if (type == RPM_INT32_TYPE) {
2210         result = xmalloc(padding + 20);
2211         strcat(formatPrefix, "d");
2212         sprintf(result, formatPrefix, *((int_32 *) data));
2213     } else {
2214         buf = alloca(strlen(data) + padding + 2);
2215         strcat(formatPrefix, "s");
2216         sprintf(buf, formatPrefix, data);
2217
2218         result = dst = xmalloc(strlen(buf) * 4 + 3);
2219         *dst++ = '\'';
2220         for (src = buf; *src; src++) {
2221             if (*src == '\'') {
2222                 *dst++ = '\'';
2223                 *dst++ = '\\';
2224                 *dst++ = '\'';
2225                 *dst++ = '\'';
2226             } else {
2227                 *dst++ = *src;
2228             }
2229         }
2230         *dst++ = '\'';
2231         *dst = '\0';
2232
2233     }
2234
2235     return result;
2236 }
2237
2238 const struct headerSprintfExtension headerDefaultFormats[] = {
2239     { HEADER_EXT_FORMAT, "octal", { octalFormat } },
2240     { HEADER_EXT_FORMAT, "hex", { hexFormat } },
2241     { HEADER_EXT_FORMAT, "date", { dateFormat } },
2242     { HEADER_EXT_FORMAT, "day", { dayFormat } },
2243     { HEADER_EXT_FORMAT, "shescape", { shescapeFormat } },
2244     { HEADER_EXT_LAST, NULL, { NULL } }
2245 };
2246
2247 void headerCopyTags(Header headerFrom, Header headerTo, int *tagstocopy)
2248 {
2249     int *p;
2250
2251     if (headerFrom == headerTo)
2252         return;
2253
2254     for (p = tagstocopy; *p != 0; p++) {
2255         char *s;
2256         int type, count;
2257         if (headerIsEntry(headerTo, *p))
2258             continue;
2259         if (!headerGetEntry(headerFrom, *p, &type, (void **) &s, &count))
2260             continue;
2261         headerAddEntry(headerTo, *p, type, s, count);
2262         if (s != NULL &&
2263            (type == RPM_STRING_ARRAY_TYPE || type == RPM_I18NSTRING_TYPE))
2264             free(s);
2265     }
2266 }