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