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