Permit simple strings to be returned argv-style from headers too
[platform/upstream/rpm.git] / lib / header.c
1 /** \ingroup header
2  * \file lib/header.c
3  */
4
5 /* RPM - Copyright (C) 1995-2002 Red Hat Software */
6
7 /* Data written to file descriptors is in network byte order.    */
8 /* Data read from file descriptors is expected to be in          */
9 /* network byte order and is converted on the fly to host order. */
10
11 #include "system.h"
12 #include <netdb.h>
13 #include <rpm/rpmtypes.h>
14 #include <rpm/rpmstring.h>
15 #include "lib/header_internal.h"
16
17 #include "debug.h"
18
19 int _hdr_debug = 0;
20
21 /** \ingroup header
22  */
23 const unsigned char rpm_header_magic[8] = {
24         0x8e, 0xad, 0xe8, 0x01, 0x00, 0x00, 0x00, 0x00
25 };
26
27 /** \ingroup header
28  * Alignment needed for header data types.
29  */
30 static const int typeAlign[16] =  {
31     1,  /*!< RPM_NULL_TYPE */
32     1,  /*!< RPM_CHAR_TYPE */
33     1,  /*!< RPM_INT8_TYPE */
34     2,  /*!< RPM_INT16_TYPE */
35     4,  /*!< RPM_INT32_TYPE */
36     8,  /*!< RPM_INT64_TYPE */
37     1,  /*!< RPM_STRING_TYPE */
38     1,  /*!< RPM_BIN_TYPE */
39     1,  /*!< RPM_STRING_ARRAY_TYPE */
40     1,  /*!< RPM_I18NSTRING_TYPE */
41     0,
42     0,
43     0,
44     0,
45     0,
46     0
47 };
48
49 /** \ingroup header
50  * Size of header data types.
51  */
52 static const int typeSizes[16] =  { 
53     0,  /*!< RPM_NULL_TYPE */
54     1,  /*!< RPM_CHAR_TYPE */
55     1,  /*!< RPM_INT8_TYPE */
56     2,  /*!< RPM_INT16_TYPE */
57     4,  /*!< RPM_INT32_TYPE */
58     8,  /*!< RPM_INT64_TYPE */
59     -1, /*!< RPM_STRING_TYPE */
60     1,  /*!< RPM_BIN_TYPE */
61     -1, /*!< RPM_STRING_ARRAY_TYPE */
62     -1, /*!< RPM_I18NSTRING_TYPE */
63     0,
64     0,
65     0,
66     0,
67     0,
68     0
69 };
70
71 typedef enum headerFlags_e {
72     HEADERFLAG_SORTED    = (1 << 0), /*!< Are header entries sorted? */
73     HEADERFLAG_ALLOCATED = (1 << 1), /*!< Is 1st header region allocated? */
74     HEADERFLAG_LEGACY    = (1 << 2), /*!< Header came from legacy source? */
75     HEADERFLAG_DEBUG     = (1 << 3), /*!< Debug this header? */
76 } headerFlags;
77
78 /** \ingroup header
79  * The Header data structure.
80  */
81 struct headerToken_s {
82     void * blob;                /*!< Header region blob. */
83     indexEntry index;           /*!< Array of tags. */
84     int indexUsed;              /*!< Current size of tag array. */
85     int indexAlloced;           /*!< Allocated size of tag array. */
86     unsigned int instance;      /*!< Rpmdb instance (offset) */
87     headerFlags flags;
88     int nrefs;                  /*!< Reference count. */
89 };
90
91 /** \ingroup header
92  * Maximum no. of bytes permitted in a header.
93  */
94 static const size_t headerMaxbytes = (32*1024*1024);
95
96 #define INDEX_MALLOC_SIZE       8
97
98 #define ENTRY_IS_REGION(_e) \
99         (((_e)->info.tag >= HEADER_IMAGE) && ((_e)->info.tag < HEADER_REGIONS))
100 #define ENTRY_IN_REGION(_e)     ((_e)->info.offset < 0)
101
102 /** \ingroup header
103  * HEADER_EXT_TAG format function prototype.
104  * This is allowed to fail, which indicates the tag doesn't exist.
105  *
106  * @param h             header
107  * @retval td           tag data container
108  * @param flags         modifier flags
109  * @return              0 on success
110  */
111 typedef int (*headerTagTagFunction) (Header h, rpmtd td, headerGetFlags hgflags);
112
113 extern void *rpmHeaderTagFunc(rpmTag tag);
114
115 /* Convert a 64bit value to network byte order. */
116 static uint64_t htonll( uint64_t n ) {
117     uint32_t *i = (uint32_t*)&n;
118     uint32_t b = i[0];
119     i[0] = htonl(i[1]);
120     i[1] = htonl(b);
121     return n;
122 }
123
124 Header headerLink(Header h)
125 {
126     if (h == NULL) return NULL;
127
128     h->nrefs++;
129 if (_hdr_debug)
130 fprintf(stderr, "--> h  %p ++ %d at %s:%u\n", h, h->nrefs, __FILE__, __LINE__);
131
132     return h;
133 }
134
135 Header headerUnlink(Header h)
136 {
137     if (h == NULL) return NULL;
138 if (_hdr_debug)
139 fprintf(stderr, "--> h  %p -- %d at %s:%u\n", h, h->nrefs, __FILE__, __LINE__);
140     h->nrefs--;
141     return NULL;
142 }
143
144 Header headerFree(Header h)
145 {
146     (void) headerUnlink(h);
147
148     if (h == NULL || h->nrefs > 0)
149         return NULL;    /* XXX return previous header? */
150
151     if (h->index) {
152         indexEntry entry = h->index;
153         int i;
154         for (i = 0; i < h->indexUsed; i++, entry++) {
155             if ((h->flags & HEADERFLAG_ALLOCATED) && ENTRY_IS_REGION(entry)) {
156                 if (entry->length > 0) {
157                     int32_t * ei = entry->data;
158                     if ((ei - 2) == h->blob) h->blob = _free(h->blob);
159                     entry->data = NULL;
160                 }
161             } else if (!ENTRY_IN_REGION(entry)) {
162                 entry->data = _free(entry->data);
163             }
164             entry->data = NULL;
165         }
166         h->index = _free(h->index);
167     }
168
169     h = _free(h);
170     return h;
171 }
172
173 Header headerNew(void)
174 {
175     Header h = xcalloc(1, sizeof(*h));
176
177     h->blob = NULL;
178     h->indexAlloced = INDEX_MALLOC_SIZE;
179     h->indexUsed = 0;
180     h->instance = 0;
181     h->flags |= HEADERFLAG_SORTED;
182
183     h->index = (h->indexAlloced
184         ? xcalloc(h->indexAlloced, sizeof(*h->index))
185         : NULL);
186
187     h->nrefs = 0;
188     return headerLink(h);
189 }
190
191 int headerVerifyInfo(int il, int dl, const void * pev, void * iv, int negate)
192 {
193     entryInfo pe = (entryInfo) pev;
194     entryInfo info = iv;
195     int i;
196
197     for (i = 0; i < il; i++) {
198         info->tag = ntohl(pe[i].tag);
199         info->type = ntohl(pe[i].type);
200         info->offset = ntohl(pe[i].offset);
201         if (negate)
202             info->offset = -info->offset;
203         info->count = ntohl(pe[i].count);
204
205         if (hdrchkType(info->type))
206             return i;
207         if (hdrchkAlign(info->type, info->offset))
208             return i;
209         if (!negate && hdrchkRange(dl, info->offset))
210             return i;
211         if (hdrchkData(info->count))
212             return i;
213
214     }
215     return -1;
216 }
217
218 /**
219  */
220 static int indexCmp(const void * avp, const void * bvp)
221 {
222     indexEntry ap = (indexEntry) avp, bp = (indexEntry) bvp;
223     return (ap->info.tag - bp->info.tag);
224 }
225
226 void headerSort(Header h)
227 {
228     if (!(h->flags & HEADERFLAG_SORTED)) {
229         qsort(h->index, h->indexUsed, sizeof(*h->index), indexCmp);
230         h->flags |= HEADERFLAG_SORTED;
231     }
232 }
233
234 /**
235  */
236 static int offsetCmp(const void * avp, const void * bvp) 
237 {
238     indexEntry ap = (indexEntry) avp, bp = (indexEntry) bvp;
239     int rc = (ap->info.offset - bp->info.offset);
240
241     if (rc == 0) {
242         /* Within a region, entries sort by address. Added drips sort by tag. */
243         if (ap->info.offset < 0)
244             rc = (((char *)ap->data) - ((char *)bp->data));
245         else
246             rc = (ap->info.tag - bp->info.tag);
247     }
248     return rc;
249 }
250
251 /** \ingroup header
252  * Restore tags in header to original ordering.
253  * @param h             header
254  */
255 void headerUnsort(Header h)
256 {
257     qsort(h->index, h->indexUsed, sizeof(*h->index), offsetCmp);
258 }
259
260 unsigned headerSizeof(Header h, enum hMagic magicp)
261 {
262     indexEntry entry;
263     unsigned int size = 0;
264     int i;
265
266     if (h == NULL)
267         return size;
268
269     headerSort(h);
270
271     switch (magicp) {
272     case HEADER_MAGIC_YES:
273         size += sizeof(rpm_header_magic);
274         break;
275     case HEADER_MAGIC_NO:
276         break;
277     }
278
279     size += 2 * sizeof(int32_t);        /* count of index entries */
280
281     for (i = 0, entry = h->index; i < h->indexUsed; i++, entry++) {
282         rpmTagType type;
283
284         /* Regions go in as is ... */
285         if (ENTRY_IS_REGION(entry)) {
286             size += entry->length;
287             /* XXX Legacy regions do not include the region tag and data. */
288             if (i == 0 && (h->flags & HEADERFLAG_LEGACY))
289                 size += sizeof(struct entryInfo_s) + entry->info.count;
290             continue;
291         }
292
293         /* ... and region elements are skipped. */
294         if (entry->info.offset < 0)
295             continue;
296
297         /* Alignment */
298         type = entry->info.type;
299         if (typeSizes[type] > 1) {
300             unsigned diff = typeSizes[type] - (size % typeSizes[type]);
301             if (diff != typeSizes[type]) {
302                 size += diff;
303             }
304         }
305
306         size += sizeof(struct entryInfo_s) + entry->length;
307     }
308
309     return size;
310 }
311
312 /**
313  * Return length of entry data.
314  * @param type          entry data type
315  * @param p             entry data
316  * @param count         entry item count
317  * @param onDisk        data is concatenated strings (with NUL's))?
318  * @param pend          pointer to end of data (or NULL)
319  * @return              no. bytes in data, -1 on failure
320  */
321 static int dataLength(rpmTagType type, rpm_constdata_t p, rpm_count_t count,
322                          int onDisk, rpm_constdata_t pend)
323 {
324     const unsigned char * s = p;
325     const unsigned char * se = pend;
326     int length = 0;
327
328     switch (type) {
329     case RPM_STRING_TYPE:
330         if (count != 1)
331             return -1;
332         while (*s++) {
333             if (se && s > se)
334                 return -1;
335             length++;
336         }
337         length++;       /* count nul terminator too. */
338         break;
339
340     case RPM_STRING_ARRAY_TYPE:
341     case RPM_I18NSTRING_TYPE:
342         /* These are like RPM_STRING_TYPE, except they're *always* an array */
343         /* Compute sum of length of all strings, including nul terminators */
344
345         if (onDisk) {
346             while (count--) {
347                 length++;       /* count nul terminator too */
348                while (*s++) {
349                     if (se && s > se)
350                         return -1;
351                     length++;
352                 }
353             }
354         } else {
355             const char ** av = (const char **)p;
356             while (count--) {
357                 /* add one for null termination */
358                 length += strlen(*av++) + 1;
359             }
360         }
361         break;
362
363     default:
364         if (typeSizes[type] == -1)
365             return -1;
366         length = typeSizes[(type & 0xf)] * count;
367         if (length < 0 || (se && (s + length) > se))
368             return -1;
369         break;
370     }
371
372     return length;
373 }
374
375 /** \ingroup header
376  * Swap int32_t and int16_t arrays within header region.
377  *
378  * If a header region tag is in the set to be swabbed, as the data for a
379  * a header region is located after all other tag data.
380  *
381  * @param entry         header entry
382  * @param il            no. of entries
383  * @param dl            start no. bytes of data
384  * @param pe            header physical entry pointer (swapped)
385  * @param dataStart     header data start
386  * @param dataEnd       header data end
387  * @param regionid      region offset
388  * @return              no. bytes of data in region, -1 on error
389  */
390 static int regionSwab(indexEntry entry, int il, int dl,
391                 entryInfo pe,
392                 unsigned char * dataStart,
393                 const unsigned char * dataEnd,
394                 int regionid)
395 {
396     for (; il > 0; il--, pe++) {
397         struct indexEntry_s ie;
398         rpmTagType type;
399
400         ie.info.tag = ntohl(pe->tag);
401         ie.info.type = ntohl(pe->type);
402         ie.info.count = ntohl(pe->count);
403         ie.info.offset = ntohl(pe->offset);
404
405         if (hdrchkType(ie.info.type))
406             return -1;
407         if (hdrchkData(ie.info.count))
408             return -1;
409         if (hdrchkData(ie.info.offset))
410             return -1;
411         if (hdrchkAlign(ie.info.type, ie.info.offset))
412             return -1;
413
414         ie.data = dataStart + ie.info.offset;
415         if (dataEnd && (unsigned char *)ie.data >= dataEnd)
416             return -1;
417
418         ie.length = dataLength(ie.info.type, ie.data, ie.info.count, 1, dataEnd);
419         if (ie.length < 0 || hdrchkData(ie.length))
420             return -1;
421
422         ie.rdlen = 0;
423
424         if (entry) {
425             ie.info.offset = regionid;
426             *entry = ie;        /* structure assignment */
427             entry++;
428         }
429
430         /* Alignment */
431         type = ie.info.type;
432         if (typeSizes[type] > 1) {
433             unsigned diff = typeSizes[type] - (dl % typeSizes[type]);
434             if (diff != typeSizes[type]) {
435                 dl += diff;
436             }
437         }
438
439         /* Perform endian conversions */
440         switch (ntohl(pe->type)) {
441         case RPM_INT64_TYPE:
442         {   uint64_t * it = ie.data;
443             for (; ie.info.count > 0; ie.info.count--, it += 1) {
444                 if (dataEnd && ((unsigned char *)it) >= dataEnd)
445                     return -1;
446                 *it = htonll(*it);
447             }
448         }   break;
449         case RPM_INT32_TYPE:
450         {   int32_t * it = ie.data;
451             for (; ie.info.count > 0; ie.info.count--, it += 1) {
452                 if (dataEnd && ((unsigned char *)it) >= dataEnd)
453                     return -1;
454                 *it = htonl(*it);
455             }
456         }   break;
457         case RPM_INT16_TYPE:
458         {   int16_t * it = ie.data;
459             for (; ie.info.count > 0; ie.info.count--, it += 1) {
460                 if (dataEnd && ((unsigned char *)it) >= dataEnd)
461                     return -1;
462                 *it = htons(*it);
463             }
464         }   break;
465         }
466
467         dl += ie.length;
468     }
469
470     return dl;
471 }
472
473 /** \ingroup header
474  * doHeaderUnload.
475  * @param h             header
476  * @retval *lengthPtr   no. bytes in unloaded header blob
477  * @return              unloaded header blob (NULL on error)
478  */
479 static void * doHeaderUnload(Header h,
480                 size_t * lengthPtr)
481 {
482     int32_t * ei = NULL;
483     entryInfo pe;
484     char * dataStart;
485     char * te;
486     unsigned len;
487     int32_t il = 0;
488     int32_t dl = 0;
489     indexEntry entry; 
490     rpmTagType type;
491     int i;
492     int drlen, ndribbles;
493
494     if (h == NULL) return NULL;
495
496     /* Sort entries by (offset,tag). */
497     headerUnsort(h);
498
499     /* Compute (il,dl) for all tags, including those deleted in region. */
500     drlen = ndribbles = 0;
501     for (i = 0, entry = h->index; i < h->indexUsed; i++, entry++) {
502         if (ENTRY_IS_REGION(entry)) {
503             int32_t rdl = -entry->info.offset;  /* negative offset */
504             int32_t ril = rdl/sizeof(*pe);
505             int rid = entry->info.offset;
506
507             il += ril;
508             dl += entry->rdlen + entry->info.count;
509             /* XXX Legacy regions do not include the region tag and data. */
510             if (i == 0 && (h->flags & HEADERFLAG_LEGACY))
511                 il += 1;
512
513             /* Skip rest of entries in region, but account for dribbles. */
514             for (; i < h->indexUsed && entry->info.offset <= rid+1; i++, entry++) {
515                 if (entry->info.offset <= rid)
516                     continue;
517
518                 /* Alignment */
519                 type = entry->info.type;
520                 if (typeSizes[type] > 1) {
521                     unsigned diff = typeSizes[type] - (dl % typeSizes[type]);
522                     if (diff != typeSizes[type]) {
523                         drlen += diff;
524                         dl += diff;
525                     }
526                 }
527
528                 ndribbles++;
529                 il++;
530                 drlen += entry->length;
531                 dl += entry->length;
532             }
533             i--;
534             entry--;
535             continue;
536         }
537
538         /* Ignore deleted drips. */
539         if (entry->data == NULL || entry->length <= 0)
540             continue;
541
542         /* Alignment */
543         type = entry->info.type;
544         if (typeSizes[type] > 1) {
545             unsigned diff = typeSizes[type] - (dl % typeSizes[type]);
546             if (diff != typeSizes[type]) {
547                 dl += diff;
548             }
549         }
550
551         il++;
552         dl += entry->length;
553     }
554
555     /* Sanity checks on header intro. */
556     if (hdrchkTags(il) || hdrchkData(dl))
557         goto errxit;
558
559     len = sizeof(il) + sizeof(dl) + (il * sizeof(*pe)) + dl;
560
561     ei = xmalloc(len);
562     ei[0] = htonl(il);
563     ei[1] = htonl(dl);
564
565     pe = (entryInfo) &ei[2];
566     dataStart = te = (char *) (pe + il);
567
568     for (i = 0, entry = h->index; i < h->indexUsed; i++, entry++) {
569         const char * src;
570         unsigned char *t;
571         int count;
572         int rdlen;
573
574         if (entry->data == NULL || entry->length <= 0)
575             continue;
576
577         t = (unsigned char*)te;
578         pe->tag = htonl(entry->info.tag);
579         pe->type = htonl(entry->info.type);
580         pe->count = htonl(entry->info.count);
581
582         if (ENTRY_IS_REGION(entry)) {
583             int32_t rdl = -entry->info.offset;  /* negative offset */
584             int32_t ril = rdl/sizeof(*pe) + ndribbles;
585             int rid = entry->info.offset;
586
587             src = (char *)entry->data;
588             rdlen = entry->rdlen;
589
590             /* XXX Legacy regions do not include the region tag and data. */
591             if (i == 0 && (h->flags & HEADERFLAG_LEGACY)) {
592                 int32_t stei[4];
593
594                 memcpy(pe+1, src, rdl);
595                 memcpy(te, src + rdl, rdlen);
596                 te += rdlen;
597
598                 pe->offset = htonl(te - dataStart);
599                 stei[0] = pe->tag;
600                 stei[1] = pe->type;
601                 stei[2] = htonl(-rdl-entry->info.count);
602                 stei[3] = pe->count;
603                 memcpy(te, stei, entry->info.count);
604                 te += entry->info.count;
605                 ril++;
606                 rdlen += entry->info.count;
607
608                 count = regionSwab(NULL, ril, 0, pe, t, NULL, 0);
609                 if (count != rdlen)
610                     goto errxit;
611
612             } else {
613
614                 memcpy(pe+1, src + sizeof(*pe), ((ril-1) * sizeof(*pe)));
615                 memcpy(te, src + (ril * sizeof(*pe)), rdlen+entry->info.count+drlen);
616                 te += rdlen;
617                 {  
618                     entryInfo se = (entryInfo)src;
619                     int off = ntohl(se->offset);
620                     pe->offset = (off) ? htonl(te - dataStart) : htonl(off);
621                 }
622                 te += entry->info.count + drlen;
623
624                 count = regionSwab(NULL, ril, 0, pe, t, NULL, 0);
625                 if (count != (rdlen + entry->info.count + drlen))
626                     goto errxit;
627             }
628
629             /* Skip rest of entries in region. */
630             while (i < h->indexUsed && entry->info.offset <= rid+1) {
631                 i++;
632                 entry++;
633             }
634             i--;
635             entry--;
636             pe += ril;
637             continue;
638         }
639
640         /* Ignore deleted drips. */
641         if (entry->data == NULL || entry->length <= 0)
642             continue;
643
644         /* Alignment */
645         type = entry->info.type;
646         if (typeSizes[type] > 1) {
647             unsigned diff;
648             diff = typeSizes[type] - ((te - dataStart) % typeSizes[type]);
649             if (diff != typeSizes[type]) {
650                 memset(te, 0, diff);
651                 te += diff;
652             }
653         }
654
655         pe->offset = htonl(te - dataStart);
656
657         /* copy data w/ endian conversions */
658         switch (entry->info.type) {
659         case RPM_INT64_TYPE:
660             count = entry->info.count;
661             src = entry->data;
662             while (count--) {
663                 *((uint64_t *)te) = htonll(*((uint64_t *)src));
664                 te += sizeof(uint64_t);
665                 src += sizeof(uint64_t);
666             }
667             break;
668
669         case RPM_INT32_TYPE:
670             count = entry->info.count;
671             src = entry->data;
672             while (count--) {
673                 *((int32_t *)te) = htonl(*((int32_t *)src));
674                 te += sizeof(int32_t);
675                 src += sizeof(int32_t);
676             }
677             break;
678
679         case RPM_INT16_TYPE:
680             count = entry->info.count;
681             src = entry->data;
682             while (count--) {
683                 *((int16_t *)te) = htons(*((int16_t *)src));
684                 te += sizeof(int16_t);
685                 src += sizeof(int16_t);
686             }
687             break;
688
689         default:
690             memcpy(te, entry->data, entry->length);
691             te += entry->length;
692             break;
693         }
694         pe++;
695     }
696    
697     /* Insure that there are no memcpy underruns/overruns. */
698     if (((char *)pe) != dataStart)
699         goto errxit;
700     if ((((char *)ei)+len) != te)
701         goto errxit;
702
703     if (lengthPtr)
704         *lengthPtr = len;
705
706     h->flags &= ~HEADERFLAG_SORTED;
707     headerSort(h);
708
709     return (void *) ei;
710
711 errxit:
712     ei = _free(ei);
713     return (void *) ei;
714 }
715
716 void * headerUnload(Header h)
717 {
718     size_t length;
719     void * uh = doHeaderUnload(h, &length);
720     return uh;
721 }
722
723 /**
724  * Find matching (tag,type) entry in header.
725  * @param h             header
726  * @param tag           entry tag
727  * @param type          entry type
728  * @return              header entry
729  */
730 static
731 indexEntry findEntry(Header h, rpmTag tag, rpmTagType type)
732 {
733     indexEntry entry;
734     struct indexEntry_s key;
735
736     if (h == NULL) return NULL;
737     if (!(h->flags & HEADERFLAG_SORTED)) headerSort(h);
738
739     key.info.tag = tag;
740
741     entry = bsearch(&key, h->index, h->indexUsed, sizeof(*h->index), indexCmp);
742     if (entry == NULL)
743         return NULL;
744
745     if (type == RPM_NULL_TYPE)
746         return entry;
747
748     /* look backwards */
749     while (entry->info.tag == tag && entry->info.type != type &&
750            entry > h->index) entry--;
751
752     if (entry->info.tag == tag && entry->info.type == type)
753         return entry;
754
755     return NULL;
756 }
757
758 int headerDel(Header h, rpmTag tag)
759 {
760     indexEntry last = h->index + h->indexUsed;
761     indexEntry entry, first;
762     int ne;
763
764     entry = findEntry(h, tag, RPM_NULL_TYPE);
765     if (!entry) return 1;
766
767     /* Make sure entry points to the first occurence of this tag. */
768     while (entry > h->index && (entry - 1)->info.tag == tag)  
769         entry--;
770
771     /* Free data for tags being removed. */
772     for (first = entry; first < last; first++) {
773         rpm_data_t data;
774         if (first->info.tag != tag)
775             break;
776         data = first->data;
777         first->data = NULL;
778         first->length = 0;
779         if (ENTRY_IN_REGION(first))
780             continue;
781         data = _free(data);
782     }
783
784     ne = (first - entry);
785     if (ne > 0) {
786         h->indexUsed -= ne;
787         ne = last - first;
788         if (ne > 0)
789             memmove(entry, first, (ne * sizeof(*entry)));
790     }
791
792     return 0;
793 }
794
795 Header headerLoad(void * uh)
796 {
797     int32_t * ei = (int32_t *) uh;
798     int32_t il = ntohl(ei[0]);          /* index length */
799     int32_t dl = ntohl(ei[1]);          /* data length */
800     size_t pvlen = sizeof(il) + sizeof(dl) +
801                (il * sizeof(struct entryInfo_s)) + dl;
802     void * pv = uh;
803     Header h = NULL;
804     entryInfo pe;
805     unsigned char * dataStart;
806     unsigned char * dataEnd;
807     indexEntry entry; 
808     int rdlen;
809
810     /* Sanity checks on header intro. */
811     if (hdrchkTags(il) || hdrchkData(dl))
812         goto errxit;
813
814     ei = (int32_t *) pv;
815     pe = (entryInfo) &ei[2];
816     dataStart = (unsigned char *) (pe + il);
817     dataEnd = dataStart + dl;
818
819     h = xcalloc(1, sizeof(*h));
820     h->blob = uh;
821     h->indexAlloced = il + 1;
822     h->indexUsed = il;
823     h->instance = 0;
824     h->index = xcalloc(h->indexAlloced, sizeof(*h->index));
825     h->flags |= HEADERFLAG_SORTED;
826     h->nrefs = 0;
827     h = headerLink(h);
828
829     entry = h->index;
830     if (!(htonl(pe->tag) < HEADER_I18NTABLE)) {
831         h->flags |= HEADERFLAG_LEGACY;
832         entry->info.type = REGION_TAG_TYPE;
833         entry->info.tag = HEADER_IMAGE;
834         entry->info.count = REGION_TAG_COUNT;
835         entry->info.offset = ((unsigned char *)pe - dataStart); /* negative offset */
836
837         entry->data = pe;
838         entry->length = pvlen - sizeof(il) - sizeof(dl);
839         rdlen = regionSwab(entry+1, il, 0, pe, dataStart, dataEnd, entry->info.offset);
840         if (rdlen != dl)
841             goto errxit;
842         entry->rdlen = rdlen;
843         h->indexUsed++;
844     } else {
845         int32_t rdl;
846         int32_t ril;
847
848         h->flags &= ~HEADERFLAG_LEGACY;
849
850         entry->info.type = htonl(pe->type);
851         entry->info.count = htonl(pe->count);
852
853         if (hdrchkType(entry->info.type))
854             goto errxit;
855         if (hdrchkTags(entry->info.count))
856             goto errxit;
857
858         {   int off = ntohl(pe->offset);
859
860             if (hdrchkData(off))
861                 goto errxit;
862             if (off) {
863                 size_t nb = REGION_TAG_COUNT;
864                 int32_t stei[nb];
865                 /* XXX Hmm, why the copy? */
866                 memcpy(&stei, dataStart + off, nb);
867                 rdl = -ntohl(stei[2]);  /* negative offset */
868                 ril = rdl/sizeof(*pe);
869                 if (hdrchkTags(ril) || hdrchkData(rdl))
870                     goto errxit;
871                 entry->info.tag = htonl(pe->tag);
872             } else {
873                 ril = il;
874                 rdl = (ril * sizeof(struct entryInfo_s));
875                 entry->info.tag = HEADER_IMAGE;
876             }
877         }
878         entry->info.offset = -rdl;      /* negative offset */
879
880         entry->data = pe;
881         entry->length = pvlen - sizeof(il) - sizeof(dl);
882         rdlen = regionSwab(entry+1, ril-1, 0, pe+1, dataStart, dataEnd, entry->info.offset);
883         if (rdlen < 0)
884             goto errxit;
885         entry->rdlen = rdlen;
886
887         if (ril < h->indexUsed) {
888             indexEntry newEntry = entry + ril;
889             int ne = (h->indexUsed - ril);
890             int rid = entry->info.offset+1;
891             int rc;
892
893             /* Load dribble entries from region. */
894             rc = regionSwab(newEntry, ne, 0, pe+ril, dataStart, dataEnd, rid);
895             if (rc < 0)
896                 goto errxit;
897             rdlen += rc;
898
899           { indexEntry firstEntry = newEntry;
900             int save = h->indexUsed;
901             int j;
902
903             /* Dribble entries replace duplicate region entries. */
904             h->indexUsed -= ne;
905             for (j = 0; j < ne; j++, newEntry++) {
906                 (void) headerDel(h, newEntry->info.tag);
907                 if (newEntry->info.tag == RPMTAG_BASENAMES)
908                     (void) headerDel(h, RPMTAG_OLDFILENAMES);
909             }
910
911             /* If any duplicate entries were replaced, move new entries down. */
912             if (h->indexUsed < (save - ne)) {
913                 memmove(h->index + h->indexUsed, firstEntry,
914                         (ne * sizeof(*entry)));
915             }
916             h->indexUsed += ne;
917           }
918         }
919     }
920
921     h->flags &= ~HEADERFLAG_SORTED;
922     headerSort(h);
923     h->flags |= HEADERFLAG_ALLOCATED;
924
925     return h;
926
927 errxit:
928     if (h) {
929         h->index = _free(h->index);
930         h = _free(h);
931     }
932     return h;
933 }
934
935 Header headerReload(Header h, rpmTag tag)
936 {
937     Header nh;
938     size_t length;
939     void * uh = doHeaderUnload(h, &length);
940
941     h = headerFree(h);
942     if (uh == NULL)
943         return NULL;
944     nh = headerLoad(uh);
945     if (nh == NULL) {
946         uh = _free(uh);
947         return NULL;
948     }
949     if (ENTRY_IS_REGION(nh->index)) {
950         if (tag == HEADER_SIGNATURES || tag == HEADER_IMMUTABLE)
951             nh->index[0].info.tag = tag;
952     }
953     return nh;
954 }
955
956 Header headerCopyLoad(const void * uh)
957 {
958     int32_t * ei = (int32_t *) uh;
959     int32_t il = ntohl(ei[0]);          /* index length */
960     int32_t dl = ntohl(ei[1]);          /* data length */
961     size_t pvlen = sizeof(il) + sizeof(dl) +
962                         (il * sizeof(struct entryInfo_s)) + dl;
963     void * nuh = NULL;
964     Header h = NULL;
965
966     /* Sanity checks on header intro. */
967     if (!(hdrchkTags(il) || hdrchkData(dl)) && pvlen < headerMaxbytes) {
968         nuh = memcpy(xmalloc(pvlen), uh, pvlen);
969         if ((h = headerLoad(nuh)) == NULL)
970             nuh = _free(nuh);
971     }
972     return h;
973 }
974
975 /** \ingroup header
976  * Read (and load) header from file handle.
977  * @param fd            file handle
978  * @param magicp        read (and verify) 8 bytes of (magic, 0)?
979  * @return              header (or NULL on error)
980  */
981 Header headerRead(FD_t fd, enum hMagic magicp)
982 {
983     int32_t block[4];
984     int32_t reserved;
985     int32_t * ei = NULL;
986     int32_t il;
987     int32_t dl;
988     int32_t magic;
989     Header h = NULL;
990     size_t len;
991     int i;
992
993     memset(block, 0, sizeof(block));
994     i = 2;
995     if (magicp == HEADER_MAGIC_YES)
996         i += 2;
997
998     /* FIX: cast? */
999     if (timedRead(fd, (char *)block, i*sizeof(*block)) != (i * sizeof(*block)))
1000         goto exit;
1001
1002     i = 0;
1003
1004     if (magicp == HEADER_MAGIC_YES) {
1005         magic = block[i++];
1006         if (memcmp(&magic, rpm_header_magic, sizeof(magic)))
1007             goto exit;
1008         reserved = block[i++];
1009     }
1010     
1011     il = ntohl(block[i]);       i++;
1012     dl = ntohl(block[i]);       i++;
1013
1014     len = sizeof(il) + sizeof(dl) + (il * sizeof(struct entryInfo_s)) + dl;
1015
1016     /* Sanity checks on header intro. */
1017     if (hdrchkTags(il) || hdrchkData(dl) || len > headerMaxbytes)
1018         goto exit;
1019
1020     ei = xmalloc(len);
1021     ei[0] = htonl(il);
1022     ei[1] = htonl(dl);
1023     len -= sizeof(il) + sizeof(dl);
1024
1025     /* FIX: cast? */
1026     if (timedRead(fd, (char *)&ei[2], len) != len)
1027         goto exit;
1028     
1029     h = headerLoad(ei);
1030
1031 exit:
1032     if (h == NULL && ei != NULL) {
1033         free(ei);
1034     }
1035     return h;
1036 }
1037
1038 int headerWrite(FD_t fd, Header h, enum hMagic magicp)
1039 {
1040     ssize_t nb;
1041     size_t length;
1042     void * uh;
1043
1044     uh = doHeaderUnload(h, &length);
1045     if (uh == NULL)
1046         return 1;
1047     switch (magicp) {
1048     case HEADER_MAGIC_YES:
1049         nb = Fwrite(rpm_header_magic, sizeof(uint8_t), sizeof(rpm_header_magic), fd);
1050         if (nb != sizeof(rpm_header_magic))
1051             goto exit;
1052         break;
1053     case HEADER_MAGIC_NO:
1054         break;
1055     }
1056
1057     nb = Fwrite(uh, sizeof(char), length, fd);
1058
1059 exit:
1060     uh = _free(uh);
1061     return (nb == length ? 0 : 1);
1062 }
1063
1064 int headerIsEntry(Header h, rpmTag tag)
1065 {
1066                 /* FIX: h modified by sort. */
1067     return (findEntry(h, tag, RPM_NULL_TYPE) ? 1 : 0);
1068         
1069 }
1070
1071 /** \ingroup header
1072  * Retrieve data from header entry.
1073  * Relevant flags (others are ignored), if neither is set allocation
1074  * behavior depends on data type(!) 
1075  *     HEADERGET_MINMEM: return pointers to header memory
1076  *     HEADERGET_ALLOC: always return malloced memory, overrides MINMEM
1077  * 
1078  * @todo Permit retrieval of regions other than HEADER_IMUTABLE.
1079  * @param entry         header entry
1080  * @param td            tag data container
1081  * @param minMem        string pointers refer to header memory?
1082  * @param flags         flags to control memory allocation
1083  * @return              1 on success, otherwise error.
1084  */
1085 static int copyTdEntry(const indexEntry entry, rpmtd td, headerGetFlags flags)
1086 {
1087     rpm_count_t count = entry->info.count;
1088     int rc = 1;         /* XXX 1 on success. */
1089     /* ALLOC overrides MINMEM */
1090     int allocMem = flags & HEADERGET_ALLOC;
1091     int minMem = allocMem ? 0 : flags & HEADERGET_MINMEM;
1092     int argvArray = (flags & HEADERGET_ARGV) ? 1 : 0;
1093
1094     assert(td != NULL);
1095     td->flags = RPMTD_IMMUTABLE;
1096     switch (entry->info.type) {
1097     case RPM_BIN_TYPE:
1098         /*
1099          * XXX This only works for
1100          * XXX  "sealed" HEADER_IMMUTABLE/HEADER_SIGNATURES/HEADER_IMAGE.
1101          * XXX This will *not* work for unsealed legacy HEADER_IMAGE (i.e.
1102          * XXX a legacy header freshly read, but not yet unloaded to the rpmdb).
1103          */
1104         if (ENTRY_IS_REGION(entry)) {
1105             int32_t * ei = ((int32_t *)entry->data) - 2;
1106             entryInfo pe = (entryInfo) (ei + 2);
1107             unsigned char * dataStart = (unsigned char *) (pe + ntohl(ei[0]));
1108             int32_t rdl = -entry->info.offset;  /* negative offset */
1109             int32_t ril = rdl/sizeof(*pe);
1110
1111             rdl = entry->rdlen;
1112             count = 2 * sizeof(*ei) + (ril * sizeof(*pe)) + rdl;
1113             if (entry->info.tag == HEADER_IMAGE) {
1114                 ril -= 1;
1115                 pe += 1;
1116             } else {
1117                 count += REGION_TAG_COUNT;
1118                 rdl += REGION_TAG_COUNT;
1119             }
1120
1121             td->data = xmalloc(count);
1122             ei = (int32_t *) td->data;
1123             ei[0] = htonl(ril);
1124             ei[1] = htonl(rdl);
1125
1126             pe = (entryInfo) memcpy(ei + 2, pe, (ril * sizeof(*pe)));
1127
1128             dataStart = (unsigned char *) memcpy(pe + ril, dataStart, rdl);
1129
1130             rc = regionSwab(NULL, ril, 0, pe, dataStart, dataStart + rdl, 0);
1131             /* don't return data on failure */
1132             if (rc < 0) {
1133                 td->data = _free(td->data);
1134             }
1135             /* XXX 1 on success. */
1136             rc = (rc < 0) ? 0 : 1;
1137         } else {
1138             count = entry->length;
1139             td->data = (!minMem
1140                 ? memcpy(xmalloc(count), entry->data, count)
1141                 : entry->data);
1142         }
1143         break;
1144     case RPM_STRING_TYPE:
1145         /* simple string, but fallthrough if its actually an array */
1146         if (count == 1 && !argvArray) {
1147             td->data = allocMem ? xstrdup(entry->data) : entry->data;
1148             break;
1149         }
1150     case RPM_STRING_ARRAY_TYPE:
1151     case RPM_I18NSTRING_TYPE:
1152     {   const char ** ptrEntry;
1153         int tableSize = (count + argvArray) * sizeof(char *);
1154         char * t;
1155         int i;
1156
1157         if (minMem) {
1158             td->data = xmalloc(tableSize);
1159             ptrEntry = (const char **) td->data;
1160             t = entry->data;
1161         } else {
1162             t = xmalloc(tableSize + entry->length);
1163             td->data = (void *)t;
1164             ptrEntry = (const char **) td->data;
1165             t += tableSize;
1166             memcpy(t, entry->data, entry->length);
1167         }
1168         for (i = 0; i < count; i++) {
1169             *ptrEntry++ = t;
1170             t = strchr(t, 0);
1171             t++;
1172         }
1173         if (argvArray) {
1174             *ptrEntry = NULL;
1175             td->flags |= RPMTD_ARGV;
1176         }
1177     }   break;
1178     case RPM_CHAR_TYPE:
1179     case RPM_INT8_TYPE:
1180     case RPM_INT16_TYPE:
1181     case RPM_INT32_TYPE:
1182     case RPM_INT64_TYPE:
1183         if (allocMem) {
1184             td->data = xmalloc(entry->length);
1185             memcpy(td->data, entry->data, entry->length);
1186         } else {
1187             td->data = entry->data;
1188         }
1189         break;
1190     default:
1191         /* WTH? Don't mess with unknown data types... */
1192         rc = 0;
1193         td->data = NULL;
1194         break;
1195     }
1196     td->type = entry->info.type;
1197     td->count = count;
1198
1199     if (td->data && entry->data != td->data) {
1200         td->flags |= RPMTD_ALLOCED;
1201     }
1202
1203     return rc;
1204 }
1205
1206 /**
1207  * Does locale match entry in header i18n table?
1208  * 
1209  * \verbatim
1210  * The range [l,le) contains the next locale to match:
1211  *    ll[_CC][.EEEEE][@dddd]
1212  * where
1213  *    ll        ISO language code (in lowercase).
1214  *    CC        (optional) ISO coutnry code (in uppercase).
1215  *    EEEEE     (optional) encoding (not really standardized).
1216  *    dddd      (optional) dialect.
1217  * \endverbatim
1218  *
1219  * @param td            header i18n table data, NUL terminated
1220  * @param l             start of locale to match
1221  * @param le            end of locale to match
1222  * @return              1 on good match, 2 on weak match, 0 on no match
1223  */
1224 static int headerMatchLocale(const char *td, const char *l, const char *le)
1225 {
1226     const char *fe;
1227
1228     /* First try a complete match. */
1229     if (strlen(td) == (le-l) && rstreqn(td, l, (le - l)))
1230         return 1;
1231
1232     /* Next, try stripping optional dialect and matching.  */
1233     for (fe = l; fe < le && *fe != '@'; fe++)
1234         {};
1235     if (fe < le && rstreqn(td, l, (fe - l)))
1236         return 1;
1237
1238     /* Next, try stripping optional codeset and matching.  */
1239     for (fe = l; fe < le && *fe != '.'; fe++)
1240         {};
1241     if (fe < le && rstreqn(td, l, (fe - l)))
1242         return 1;
1243
1244     /* Finally, try stripping optional country code and matching. */
1245     for (fe = l; fe < le && *fe != '_'; fe++)
1246         {};
1247     if (fe < le && rstreqn(td, l, (fe - l)))
1248         return 2;
1249
1250     return 0;
1251 }
1252
1253 /**
1254  * Return i18n string from header that matches locale.
1255  * @param h             header
1256  * @param entry         i18n string data
1257  * @retval td           tag data container
1258  * @param flags         flags to control allocation
1259  * @return              1 always
1260  */
1261 static int copyI18NEntry(Header h, indexEntry entry, rpmtd td, 
1262                                                 headerGetFlags flags)
1263 {
1264     const char *lang, *l, *le;
1265     indexEntry table;
1266
1267     td->type = RPM_STRING_TYPE;
1268     td->count = 1;
1269     /* if no match, just return the first string */
1270     td->data = entry->data;
1271
1272     /* XXX Drepper sez' this is the order. */
1273     if ((lang = getenv("LANGUAGE")) == NULL &&
1274         (lang = getenv("LC_ALL")) == NULL &&
1275         (lang = getenv("LC_MESSAGES")) == NULL &&
1276         (lang = getenv("LANG")) == NULL)
1277             goto exit;
1278     
1279     if ((table = findEntry(h, HEADER_I18NTABLE, RPM_STRING_ARRAY_TYPE)) == NULL)
1280         goto exit;
1281
1282     for (l = lang; *l != '\0'; l = le) {
1283         const char *t;
1284         char *ed, *ed_weak = NULL;
1285         int langNum;
1286
1287         while (*l && *l == ':')                 /* skip leading colons */
1288             l++;
1289         if (*l == '\0')
1290             break;
1291         for (le = l; *le && *le != ':'; le++)   /* find end of this locale */
1292             {};
1293
1294         /* For each entry in the header ... */
1295         for (langNum = 0, t = table->data, ed = entry->data;
1296              langNum < entry->info.count;
1297              langNum++, t += strlen(t) + 1, ed += strlen(ed) + 1) {
1298
1299             int match = headerMatchLocale(t, l, le);
1300             if (match == 1) {
1301                 td->data = ed;
1302                 goto exit;
1303             } else if (match == 2) { 
1304                 ed_weak = ed;
1305             }
1306         }
1307         if (ed_weak) {
1308             td->data = ed_weak;
1309             goto exit;
1310         }
1311     }
1312
1313 exit:
1314     if (flags & HEADERGET_ALLOC) {
1315         td->data = xstrdup(td->data);
1316         td->flags |= RPMTD_ALLOCED;
1317     }
1318
1319     return 1;
1320 }
1321
1322 /**
1323  * Retrieve tag data from header.
1324  * @param h             header
1325  * @retval td           tag data container
1326  * @param flags         flags to control retrieval
1327  * @return              1 on success, 0 on not found
1328  */
1329 static int intGetTdEntry(Header h, rpmtd td, headerGetFlags flags)
1330 {
1331     indexEntry entry;
1332     int rc;
1333
1334     /* First find the tag */
1335     /* FIX: h modified by sort. */
1336     entry = findEntry(h, td->tag, RPM_NULL_TYPE);
1337     if (entry == NULL) {
1338         /* Td is zeroed above, just return... */
1339         return 0;
1340     }
1341
1342     if (flags & HEADERGET_RAW) {
1343         rc = copyTdEntry(entry, td, flags);
1344     } else {
1345         switch (entry->info.type) {
1346         case RPM_I18NSTRING_TYPE:
1347             rc = copyI18NEntry(h, entry, td, flags);
1348             break;
1349         default:
1350             rc = copyTdEntry(entry, td, flags);
1351             break;
1352         }
1353     }
1354
1355     /* XXX 1 on success */
1356     return ((rc == 1) ? 1 : 0);
1357 }
1358
1359 int headerGet(Header h, rpmTag tag, rpmtd td, headerGetFlags flags)
1360 {
1361     int rc;
1362     headerTagTagFunction tagfunc = intGetTdEntry;
1363
1364     if (td == NULL) return 0;
1365
1366     rpmtdReset(td);
1367     td->tag = tag;
1368
1369     if (flags & HEADERGET_EXT) {
1370         headerTagTagFunction extfunc = rpmHeaderTagFunc(tag);
1371         if (extfunc) tagfunc = extfunc;
1372     }
1373     rc = tagfunc(h, td, flags);
1374
1375     assert(tag == td->tag);
1376     return rc;
1377 }
1378
1379 /**
1380  */
1381 static void copyData(rpmTagType type, rpm_data_t dstPtr, 
1382                 rpm_constdata_t srcPtr, rpm_count_t cnt, int dataLength)
1383 {
1384     switch (type) {
1385     case RPM_STRING_ARRAY_TYPE:
1386     case RPM_I18NSTRING_TYPE:
1387     {   const char ** av = (const char **) srcPtr;
1388         char * t = dstPtr;
1389
1390         while (cnt-- > 0 && dataLength > 0) {
1391             const char * s;
1392             if ((s = *av++) == NULL)
1393                 continue;
1394             do {
1395                 *t++ = *s++;
1396             } while (s[-1] && --dataLength > 0);
1397         }
1398     }   break;
1399
1400     default:
1401         memmove(dstPtr, srcPtr, dataLength);
1402         break;
1403     }
1404 }
1405
1406 /**
1407  * Return (malloc'ed) copy of entry data.
1408  * @param type          entry data type
1409  * @param p             entry data
1410  * @param c             entry item count
1411  * @retval lengthPtr    no. bytes in returned data
1412  * @return              (malloc'ed) copy of entry data, NULL on error
1413  */
1414 static void *
1415 grabData(rpmTagType type, rpm_constdata_t p, rpm_count_t c, int * lengthPtr)
1416 {
1417     rpm_data_t data = NULL;
1418     int length;
1419
1420     length = dataLength(type, p, c, 0, NULL);
1421     if (length > 0) {
1422         data = xmalloc(length);
1423         copyData(type, data, p, c, length);
1424     }
1425
1426     if (lengthPtr)
1427         *lengthPtr = length;
1428     return data;
1429 }
1430
1431 static int intAddEntry(Header h, rpmtd td)
1432 {
1433     indexEntry entry;
1434     rpm_data_t data;
1435     int length;
1436
1437     /* Count must always be >= 1 for headerAddEntry. */
1438     if (td->count <= 0)
1439         return 0;
1440
1441     if (hdrchkType(td->type))
1442         return 0;
1443     if (hdrchkData(td->count))
1444         return 0;
1445
1446     length = 0;
1447     data = grabData(td->type, td->data, td->count, &length);
1448     if (data == NULL || length <= 0)
1449         return 0;
1450
1451     /* Allocate more index space if necessary */
1452     if (h->indexUsed == h->indexAlloced) {
1453         h->indexAlloced += INDEX_MALLOC_SIZE;
1454         h->index = xrealloc(h->index, h->indexAlloced * sizeof(*h->index));
1455     }
1456
1457     /* Fill in the index */
1458     entry = h->index + h->indexUsed;
1459     entry->info.tag = td->tag;
1460     entry->info.type = td->type;
1461     entry->info.count = td->count;
1462     entry->info.offset = 0;
1463     entry->data = data;
1464     entry->length = length;
1465
1466     if (h->indexUsed > 0 && td->tag < h->index[h->indexUsed-1].info.tag)
1467         h->flags &= ~HEADERFLAG_SORTED;
1468     h->indexUsed++;
1469
1470     return 1;
1471 }
1472
1473 static int intAppendEntry(Header h, rpmtd td)
1474 {
1475     indexEntry entry;
1476     int length;
1477
1478     if (td->type == RPM_STRING_TYPE || td->type == RPM_I18NSTRING_TYPE) {
1479         /* we can't do this */
1480         return 0;
1481     }
1482
1483     /* Find the tag entry in the header. */
1484     entry = findEntry(h, td->tag, td->type);
1485     if (!entry)
1486         return 0;
1487
1488     length = dataLength(td->type, td->data, td->count, 0, NULL);
1489     if (length < 0)
1490         return 0;
1491
1492     if (ENTRY_IN_REGION(entry)) {
1493         char * t = xmalloc(entry->length + length);
1494         memcpy(t, entry->data, entry->length);
1495         entry->data = t;
1496         entry->info.offset = 0;
1497     } else
1498         entry->data = xrealloc(entry->data, entry->length + length);
1499
1500     copyData(td->type, ((char *) entry->data) + entry->length, 
1501              td->data, td->count, length);
1502
1503     entry->length += length;
1504
1505     entry->info.count += td->count;
1506
1507     return 1;
1508 }
1509
1510 int headerPut(Header h, rpmtd td, headerPutFlags flags)
1511 {
1512     int rc;
1513     
1514     assert(td != NULL);
1515     if (flags & HEADERPUT_APPEND) {
1516         rc = findEntry(h, td->tag, td->type) ?
1517                 intAppendEntry(h, td) :
1518                 intAddEntry(h, td);
1519     } else {
1520         rc = intAddEntry(h, td);
1521     }
1522     return rc;
1523 }
1524
1525 int headerAddI18NString(Header h, rpmTag tag, const char * string,
1526                 const char * lang)
1527 {
1528     indexEntry table, entry;
1529     const char ** strArray;
1530     int length;
1531     int ghosts;
1532     rpm_count_t i, langNum;
1533     char * buf;
1534
1535     table = findEntry(h, HEADER_I18NTABLE, RPM_STRING_ARRAY_TYPE);
1536     entry = findEntry(h, tag, RPM_I18NSTRING_TYPE);
1537
1538     if (!table && entry)
1539         return 0;               /* this shouldn't ever happen!! */
1540
1541     if (!table && !entry) {
1542         const char * charArray[2];
1543         rpm_count_t count = 0;
1544         struct rpmtd_s td;
1545         if (!lang || (lang[0] == 'C' && lang[1] == '\0')) {
1546             charArray[count++] = "C";
1547         } else {
1548             charArray[count++] = "C";
1549             charArray[count++] = lang;
1550         }
1551         
1552         rpmtdReset(&td);
1553         td.tag = HEADER_I18NTABLE;
1554         td.type = RPM_STRING_ARRAY_TYPE;
1555         td.data = (void *) charArray;
1556         td.count = count;
1557         if (!headerPut(h, &td, HEADERPUT_DEFAULT))
1558             return 0;
1559         table = findEntry(h, HEADER_I18NTABLE, RPM_STRING_ARRAY_TYPE);
1560     }
1561
1562     if (!table)
1563         return 0;
1564     if (!lang) lang = "C";
1565
1566     {   const char * l = table->data;
1567         for (langNum = 0; langNum < table->info.count; langNum++) {
1568             if (rstreq(l, lang)) break;
1569             l += strlen(l) + 1;
1570         }
1571     }
1572
1573     if (langNum >= table->info.count) {
1574         length = strlen(lang) + 1;
1575         if (ENTRY_IN_REGION(table)) {
1576             char * t = xmalloc(table->length + length);
1577             memcpy(t, table->data, table->length);
1578             table->data = t;
1579             table->info.offset = 0;
1580         } else
1581             table->data = xrealloc(table->data, table->length + length);
1582         memmove(((char *)table->data) + table->length, lang, length);
1583         table->length += length;
1584         table->info.count++;
1585     }
1586
1587     if (!entry) {
1588         int rc;
1589         struct rpmtd_s td;
1590         strArray = xmalloc(sizeof(*strArray) * (langNum + 1));
1591         for (i = 0; i < langNum; i++)
1592             strArray[i] = "";
1593         strArray[langNum] = string;
1594
1595         rpmtdReset(&td);
1596         td.tag = tag;
1597         td.type = RPM_I18NSTRING_TYPE;
1598         td.data = strArray;
1599         td.count = langNum + 1;
1600         rc = headerPut(h, &td, HEADERPUT_DEFAULT);
1601         free(strArray);
1602         return rc;
1603     } else if (langNum >= entry->info.count) {
1604         ghosts = langNum - entry->info.count;
1605         
1606         length = strlen(string) + 1 + ghosts;
1607         if (ENTRY_IN_REGION(entry)) {
1608             char * t = xmalloc(entry->length + length);
1609             memcpy(t, entry->data, entry->length);
1610             entry->data = t;
1611             entry->info.offset = 0;
1612         } else
1613             entry->data = xrealloc(entry->data, entry->length + length);
1614
1615         memset(((char *)entry->data) + entry->length, '\0', ghosts);
1616         memmove(((char *)entry->data) + entry->length + ghosts, string, strlen(string)+1);
1617
1618         entry->length += length;
1619         entry->info.count = langNum + 1;
1620     } else {
1621         char *b, *be, *e, *ee, *t;
1622         size_t bn, sn, en;
1623
1624         /* Set beginning/end pointers to previous data */
1625         b = be = e = ee = entry->data;
1626         for (i = 0; i < table->info.count; i++) {
1627             if (i == langNum)
1628                 be = ee;
1629             ee += strlen(ee) + 1;
1630             if (i == langNum)
1631                 e  = ee;
1632         }
1633
1634         /* Get storage for new buffer */
1635         bn = (be-b);
1636         sn = strlen(string) + 1;
1637         en = (ee-e);
1638         length = bn + sn + en;
1639         t = buf = xmalloc(length);
1640
1641         /* Copy values into new storage */
1642         memcpy(t, b, bn);
1643         t += bn;
1644         memcpy(t, string, sn);
1645         t += sn;
1646         memcpy(t, e, en);
1647         t += en;
1648
1649         /* Replace i18N string array */
1650         entry->length -= strlen(be) + 1;
1651         entry->length += sn;
1652         
1653         if (ENTRY_IN_REGION(entry)) {
1654             entry->info.offset = 0;
1655         } else
1656             entry->data = _free(entry->data);
1657         entry->data = buf;
1658     }
1659
1660     return 0;
1661 }
1662
1663 int headerMod(Header h, rpmtd td)
1664 {
1665     indexEntry entry;
1666     rpm_data_t oldData;
1667     rpm_data_t data;
1668     int length;
1669
1670     /* First find the tag */
1671     entry = findEntry(h, td->tag, td->type);
1672     if (!entry)
1673         return 0;
1674
1675     length = 0;
1676     data = grabData(td->type, td->data, td->count, &length);
1677     if (data == NULL || length <= 0)
1678         return 0;
1679
1680     /* make sure entry points to the first occurence of this tag */
1681     while (entry > h->index && (entry - 1)->info.tag == td->tag)  
1682         entry--;
1683
1684     /* free after we've grabbed the new data in case the two are intertwined;
1685        that's a bad idea but at least we won't break */
1686     oldData = entry->data;
1687
1688     entry->info.count = td->count;
1689     entry->info.type = td->type;
1690     entry->data = data;
1691     entry->length = length;
1692
1693     if (ENTRY_IN_REGION(entry)) {
1694         entry->info.offset = 0;
1695     } else
1696         oldData = _free(oldData);
1697
1698     return 1;
1699 }
1700
1701 /**
1702  * Header tag iterator data structure.
1703  */
1704 struct headerIterator_s {
1705     Header h;           /*!< Header being iterated. */
1706     int next_index;     /*!< Next tag index. */
1707 };
1708
1709 HeaderIterator headerFreeIterator(HeaderIterator hi)
1710 {
1711     if (hi != NULL) {
1712         hi->h = headerFree(hi->h);
1713         hi = _free(hi);
1714     }
1715     return hi;
1716 }
1717
1718 HeaderIterator headerInitIterator(Header h)
1719 {
1720     HeaderIterator hi = xmalloc(sizeof(*hi));
1721
1722     headerSort(h);
1723
1724     hi->h = headerLink(h);
1725     hi->next_index = 0;
1726     return hi;
1727 }
1728
1729 static indexEntry nextIndex(HeaderIterator hi)
1730 {
1731     Header h = hi->h;
1732     int slot;
1733     indexEntry entry = NULL;
1734
1735     for (slot = hi->next_index; slot < h->indexUsed; slot++) {
1736         entry = h->index + slot;
1737         if (!ENTRY_IS_REGION(entry))
1738             break;
1739     }
1740     hi->next_index = slot;
1741     if (entry == NULL || slot >= h->indexUsed)
1742         return NULL;
1743
1744     hi->next_index++;
1745     return entry;
1746 }
1747
1748 rpmTag headerNextTag(HeaderIterator hi)
1749 {
1750     indexEntry entry = nextIndex(hi);
1751     return entry ? entry->info.tag : RPMTAG_NOT_FOUND;
1752 }
1753
1754 int headerNext(HeaderIterator hi, rpmtd td)
1755 {
1756     indexEntry entry = nextIndex(hi);
1757     int rc = 0;
1758
1759     rpmtdReset(td);
1760     if (entry) {
1761         td->tag = entry->info.tag;
1762         rc = copyTdEntry(entry, td, HEADERGET_DEFAULT);
1763     }
1764     return ((rc == 1) ? 1 : 0);
1765 }
1766
1767 unsigned int headerGetInstance(Header h)
1768 {
1769     return h ? h->instance : 0;
1770 }
1771
1772 void headerSetInstance(Header h, unsigned int instance)
1773 {
1774     h->instance = instance;
1775 }    
1776