95c5e074d26980ddf207aaa4c39660fd1916a036
[platform/upstream/rpm.git] / lib / tagexts.c
1 /** \ingroup header
2  * \file lib/formats.c
3  */
4
5 #include <wchar.h>
6 #include "system.h"
7
8 #include <rpm/rpmtypes.h>
9 #include <rpm/rpmlib.h>
10 #include <rpm/rpmmacro.h>       /* XXX for %_i18ndomains */
11 #include <rpm/rpmfi.h>
12 #include <rpm/rpmstring.h>
13 #include <rpm/rpmlog.h>
14 #include "lib/misc.h"           /* tag function proto */
15
16 #include "debug.h"
17
18 struct headerTagFunc_s {
19     rpmTag tag;         /*!< Tag of extension. */
20     headerTagTagFunction func;  /*!< Pointer to formatter function. */  
21 };
22
23 /** \ingroup rpmfi
24  * Retrieve file names from header.
25  *
26  * The representation of file names in package headers changed in rpm-4.0.
27  * Originally, file names were stored as an array of absolute paths.
28  * In rpm-4.0, file names are stored as separate arrays of dirname's and
29  * basename's, * with a dirname index to associate the correct dirname
30  * with each basname.
31  *
32  * This function is used to retrieve file names independent of how the
33  * file names are represented in the package header.
34  * 
35  * @param h             header
36  * @param tagN          RPMTAG_BASENAMES | PMTAG_ORIGBASENAMES
37  * @param withstate     take file state into account?
38  * @retval td           tag data container
39  * @return              1 on success
40  */
41 static int fnTag(Header h, rpmTag tagN, int withstate, rpmtd td)
42 {
43     const char **baseNames, **dirNames;
44     const char *fileStates = NULL;
45     uint32_t *dirIndexes;
46     rpm_count_t count, retcount, dncount;
47     size_t size = 0;
48     rpmTag dirNameTag = RPMTAG_DIRNAMES;
49     rpmTag dirIndexesTag = RPMTAG_DIRINDEXES;
50     int i, j;
51     int rc = 0; /* assume failure */
52     struct rpmtd_s bnames, dnames, dixs, fstates;
53
54     if (tagN == RPMTAG_ORIGBASENAMES) {
55         dirNameTag = RPMTAG_ORIGDIRNAMES;
56         dirIndexesTag = RPMTAG_ORIGDIRINDEXES;
57     }
58
59     if (!headerGet(h, tagN, &bnames, HEADERGET_MINMEM)) {
60         return 0;               /* no file list */
61     }
62
63     (void) headerGet(h, dirNameTag, &dnames, HEADERGET_MINMEM);
64     (void) headerGet(h, dirIndexesTag, &dixs, HEADERGET_MINMEM);
65
66     retcount = count = rpmtdCount(&bnames);
67     dncount = rpmtdCount(&dnames);
68
69     /* Basic sanity checking for our interrelated tags  */
70     if (rpmtdCount(&dixs) != count || dncount < 1 || dncount > count)
71         td->flags |= RPMTD_INVALID;
72
73     if (withstate) {
74         /* no recorded states means no installed files */
75         if (!headerGet(h, RPMTAG_FILESTATES, &fstates, HEADERGET_MINMEM))
76             goto exit;
77         if (rpmtdCount(&fstates) != count)
78             td->flags |= RPMTD_INVALID;
79         fileStates = fstates.data;
80     }
81
82     if (td->flags & RPMTD_INVALID)
83         goto exit;
84
85     baseNames = bnames.data;
86     dirNames = dnames.data;
87     dirIndexes = dixs.data;
88
89     /*
90      * fsm, psm and rpmfi assume the data is stored in a single allocation
91      * block, until those assumptions are removed we need to jump through
92      * a few hoops here and precalculate sizes etc
93      */
94     for (i = 0; i < count; i++) {
95         if (fileStates && !RPMFILE_IS_INSTALLED(fileStates[i])) {
96             retcount--;
97             continue;
98         }
99         /* Sanity check  directory indexes are within bounds */
100         if (dirIndexes[i] >= dncount) {
101             td->flags |= RPMTD_INVALID;
102             break;
103         }
104         size += strlen(baseNames[i]) + strlen(dirNames[dirIndexes[i]]) + 1;
105     }
106
107     if (!(td->flags & RPMTD_INVALID)) {
108         char **fileNames = xmalloc(size + (sizeof(*fileNames) * retcount));
109         char *t = ((char *) fileNames) + (sizeof(*fileNames) * retcount);
110         for (i = 0, j = 0; i < count; i++) {
111             if (fileStates && !RPMFILE_IS_INSTALLED(fileStates[i]))
112                 continue;
113             fileNames[j++] = t;
114             t = stpcpy( stpcpy(t, dirNames[dirIndexes[i]]), baseNames[i]);
115             *t++ = '\0';
116         }
117
118         td->data = fileNames;
119         td->count = retcount;
120         td->type = RPM_STRING_ARRAY_TYPE;
121         td->flags |= RPMTD_ALLOCED;
122         rc = 1;
123     }
124
125 exit:
126     rpmtdFreeData(&bnames);
127     rpmtdFreeData(&dnames);
128     rpmtdFreeData(&dixs);
129     /* only safe if the headerGet() on file states was actually called */
130     if (fileStates)
131         rpmtdFreeData(&fstates);
132
133     return rc;
134 }
135
136 static int filedepTag(Header h, rpmTag tagN, rpmtd td, headerGetFlags hgflags)
137 {
138     rpmfi fi = rpmfiNew(NULL, h, RPMTAG_BASENAMES, RPMFI_NOHEADER);
139     rpmds ds = NULL;
140     char **fdeps = NULL;
141     int numfiles;
142     char deptype = 'R';
143     int fileix;
144     int rc = 0;
145
146     numfiles = rpmfiFC(fi);
147     if (numfiles <= 0) {
148         goto exit;
149     }
150
151     if (tagN == RPMTAG_PROVIDENAME)
152         deptype = 'P';
153     else if (tagN == RPMTAG_REQUIRENAME)
154         deptype = 'R';
155
156     ds = rpmdsNew(h, tagN, 0);
157     fdeps = xmalloc(numfiles * sizeof(*fdeps));
158
159     while ((fileix = rpmfiNext(fi)) >= 0) {
160         ARGV_t deps = NULL;
161         const uint32_t * ddict = NULL;
162         int ndx = rpmfiFDepends(fi, &ddict);
163         if (ddict != NULL) {
164             while (ndx-- > 0) {
165                 const char * DNEVR;
166                 unsigned dix = *ddict++;
167                 char mydt = ((dix >> 24) & 0xff);
168                 if (mydt != deptype)
169                     continue;
170                 dix &= 0x00ffffff;
171                 (void) rpmdsSetIx(ds, dix-1);
172                 if (rpmdsNext(ds) < 0)
173                     continue;
174                 DNEVR = rpmdsDNEVR(ds);
175                 if (DNEVR != NULL) {
176                     argvAdd(&deps, DNEVR + 2);
177                 }
178             }
179         }
180         fdeps[fileix] = deps ? argvJoin(deps, " ") : xstrdup("");
181         argvFree(deps);
182     }
183     td->data = fdeps;
184     td->count = numfiles;
185     td->flags = RPMTD_ALLOCED | RPMTD_PTR_ALLOCED;
186     td->type = RPM_STRING_ARRAY_TYPE;
187     rc = 1;
188
189 exit:
190     rpmfiFree(fi);
191     rpmdsFree(ds);
192     return rc;
193 }
194
195 static char * strtolocale(char *str)
196 {
197     wchar_t *wstr, *wp;
198     const unsigned char *cp;
199     char *cc;
200     int state = 0;
201     int c;
202     int ccl, cca, mb_cur_max;
203     size_t l;
204     mbstate_t ps;
205     int strisutf8 = 1;
206     int locisutf8 = 1;
207
208     if (!str)
209         return 0;
210     if (!*str)
211         return str;
212     wstr = (wchar_t *)xmalloc((strlen(str) + 1) * sizeof(*wstr));
213     wp = wstr;
214     cp = (const unsigned char *)str;
215     while ((c = *cp++) != 0) {
216         if (state) {
217             if ((c & 0xc0) != 0x80) {
218                 /* encoding error */
219                 break;
220             }
221             c = (c & 0x3f) | (state << 6);
222             if (!(state & 0x40000000)) {
223               /* check for overlong sequences */
224                 if ((c & 0x820823e0) == 0x80000000)
225                     c = 0xfdffffff;
226                 else if ((c & 0x020821f0) == 0x02000000)
227                     c = 0xfff7ffff;
228                 else if ((c & 0x000820f8) == 0x00080000)
229                     c = 0xffffd000;
230                 else if ((c & 0x0000207c) == 0x00002000)
231                     c = 0xffffff70;
232             }
233         } else {
234             /* new sequence */
235             if (c >= 0xfe)
236                 c = 0xfffd;
237             else if (c >= 0xfc)
238                 c = (c & 0x01) | 0xbffffffc;    /* 5 bytes to follow */
239             else if (c >= 0xf8)
240                 c = (c & 0x03) | 0xbfffff00;    /* 4 */
241             else if (c >= 0xf0)
242                 c = (c & 0x07) | 0xbfffc000;    /* 3 */
243             else if (c >= 0xe0)
244                 c = (c & 0x0f) | 0xbff00000;    /* 2 */
245             else if (c >= 0xc2)
246                 c = (c & 0x1f) | 0xfc000000;    /* 1 */
247             else if (c >= 0xc0)
248                 c = 0xfdffffff;         /* overlong */
249             else if (c >= 0x80)
250                 c = 0xfffd;
251         }
252         state = (c & 0x80000000) ? c : 0;
253         if (state)
254             continue;
255         *wp++ = (wchar_t)c;
256     }
257     if (state) {
258         /* encoding error, assume latin1 */
259         strisutf8 = 0;
260         cp = (const unsigned char *)str;
261         wp = wstr;
262         while ((c = *cp++) != 0) {
263             *wp++ = (wchar_t)c;
264         }
265     }
266     *wp = 0;
267     mb_cur_max = MB_CUR_MAX;
268     memset(&ps, 0, sizeof(ps));
269     cc = xmalloc(mb_cur_max);
270     /* test locale encoding */
271     if (wcrtomb(cc, 0x20ac, &ps) != 3 || memcmp(cc, "\342\202\254", 3))
272         locisutf8 = 0;
273     if (locisutf8 == strisutf8) {
274         wstr = _free(wstr);
275         cc = _free(cc);
276         return str;
277     }
278     str = _free((char *)str);
279     memset(&ps, 0, sizeof(ps));
280     ccl = cca = 0;
281     for (wp = wstr; ; wp++) {
282         l = wcrtomb(cc + ccl, *wp, &ps);
283         if (*wp == 0)
284             break;
285         if (l == (size_t)-1) {
286             if (*wp < (wchar_t)256 && mbsinit(&ps)) {
287                 cc[ccl] = *wp;
288                 l = 1;
289             } else
290                 l = wcrtomb(cc + ccl, (wchar_t)'?', &ps);
291         }
292         if (l == 0 || l == (size_t)-1)
293             continue;
294         ccl += l;
295         if (ccl > cca) {
296             cca = ccl + 16;
297             cc = xrealloc(cc, cca + mb_cur_max);
298         }
299     }
300     wstr = _free(wstr);
301     return (char *)cc;
302 }
303
304 /**
305  * Retrieve trigger info.
306  * @param h             header
307  * @retval td           tag data container
308  * @return              1 on success
309  */
310 static int triggercondsTag(Header h, rpmtd td, headerGetFlags hgflags)
311 {
312     uint32_t * indices;
313     int i, j;
314     char ** conds;
315     struct rpmtd_s nametd, indextd, flagtd, versiontd, scripttd;
316     int hgeflags = HEADERGET_MINMEM;
317
318     if (!headerGet(h, RPMTAG_TRIGGERNAME, &nametd, hgeflags)) {
319         return 0;
320     }
321
322     headerGet(h, RPMTAG_TRIGGERINDEX, &indextd, hgeflags);
323     headerGet(h, RPMTAG_TRIGGERFLAGS, &flagtd, hgeflags);
324     headerGet(h, RPMTAG_TRIGGERVERSION, &versiontd, hgeflags);
325     headerGet(h, RPMTAG_TRIGGERSCRIPTS, &scripttd, hgeflags);
326
327     td->type = RPM_STRING_ARRAY_TYPE;
328     td->flags = RPMTD_ALLOCED | RPMTD_PTR_ALLOCED;
329     td->data = conds = xmalloc(sizeof(*conds) * rpmtdCount(&scripttd));
330     td->count = rpmtdCount(&scripttd);
331
332     indices = indextd.data;
333
334     while ((i = rpmtdNext(&scripttd)) >= 0) {
335         rpm_flag_t *flag;
336         char *flagStr, *item;
337         ARGV_t items = NULL;
338
339         rpmtdInit(&nametd); rpmtdInit(&flagtd); rpmtdInit(&versiontd);
340         while ((j = rpmtdNext(&nametd)) >= 0) {
341             /* flag and version arrays match name array size always */
342             rpmtdNext(&flagtd); rpmtdNext(&versiontd);
343
344             if (indices[j] != i)
345                 continue;
346
347             flag = rpmtdGetUint32(&flagtd);
348             if (flag && *flag & RPMSENSE_SENSEMASK) {
349                 flagStr = rpmtdFormat(&flagtd, RPMTD_FORMAT_DEPFLAGS, NULL);
350                 rasprintf(&item, "%s %s %s", rpmtdGetString(&nametd),
351                                              flagStr,
352                                              rpmtdGetString(&versiontd));
353                 free(flagStr);
354             } else {
355                 item = xstrdup(rpmtdGetString(&nametd));
356             }
357
358             argvAdd(&items, item);
359             free(item);
360         }
361
362         conds[i] = argvJoin(items, ", ");
363         argvFree(items);
364     }
365
366     rpmtdFreeData(&nametd);
367     rpmtdFreeData(&versiontd);
368     rpmtdFreeData(&flagtd);
369     rpmtdFreeData(&indextd);
370     rpmtdFreeData(&scripttd);
371     return 1;
372 }
373
374 /**
375  * Retrieve trigger type info.
376  * @param h             header
377  * @retval td           tag data container
378  * @return              1 on success
379  */
380 static int triggertypeTag(Header h, rpmtd td, headerGetFlags hgflags)
381 {
382     int i;
383     char ** conds;
384     struct rpmtd_s indices, flags, scripts;
385
386     if (!headerGet(h, RPMTAG_TRIGGERINDEX, &indices, HEADERGET_MINMEM)) {
387         return 0;
388     }
389
390     headerGet(h, RPMTAG_TRIGGERFLAGS, &flags, HEADERGET_MINMEM);
391     headerGet(h, RPMTAG_TRIGGERSCRIPTS, &scripts, HEADERGET_MINMEM);
392
393     td->flags = RPMTD_ALLOCED | RPMTD_PTR_ALLOCED;
394     td->count = rpmtdCount(&scripts);
395     td->data = conds = xmalloc(sizeof(*conds) * td->count);
396     td->type = RPM_STRING_ARRAY_TYPE;
397
398     while ((i = rpmtdNext(&scripts)) >= 0) {
399         rpm_flag_t *flag;
400         rpmtdInit(&indices); rpmtdInit(&flags);
401
402         while (rpmtdNext(&indices) >= 0 && rpmtdNext(&flags) >= 0) {
403             if (*rpmtdGetUint32(&indices) != i) 
404                 continue;
405
406             flag = rpmtdGetUint32(&flags);
407             if (*flag & RPMSENSE_TRIGGERPREIN)
408                 conds[i] = xstrdup("prein");
409             else if (*flag & RPMSENSE_TRIGGERIN)
410                 conds[i] = xstrdup("in");
411             else if (*flag & RPMSENSE_TRIGGERUN)
412                 conds[i] = xstrdup("un");
413             else if (*flag & RPMSENSE_TRIGGERPOSTUN)
414                 conds[i] = xstrdup("postun");
415             else
416                 conds[i] = xstrdup("");
417             break;
418         }
419     }
420     rpmtdFreeData(&indices);
421     rpmtdFreeData(&flags);
422     rpmtdFreeData(&scripts);
423
424     return 1;
425 }
426
427 /**
428  * Retrieve installed file paths.
429  * @param h             header
430  * @retval td           tag data container
431  * @return              1 on success
432  */
433 static int instfilenamesTag(Header h, rpmtd td, headerGetFlags hgflags)
434 {
435     return fnTag(h, RPMTAG_BASENAMES, 1, td);
436 }
437 /**
438  * Retrieve file paths.
439  * @param h             header
440  * @retval td           tag data container
441  * @return              1 on success
442  */
443 static int filenamesTag(Header h, rpmtd td, headerGetFlags hgflags)
444 {
445     return fnTag(h, RPMTAG_BASENAMES, 0, td);
446 }
447
448 /**
449  * Retrieve original file paths (wrt relocation).
450  * @param h             header
451  * @retval td           tag data container
452  * @return              1 on success
453  */
454 static int origfilenamesTag(Header h, rpmtd td, headerGetFlags hgflags)
455 {
456     return fnTag(h, RPMTAG_ORIGBASENAMES, 0, td);
457 }
458
459 /*
460  * Attempt to generate libmagic-style file class if missing from header:
461  * we can easily generate this for symlinks and other special types.
462  * Always return malloced strings to simplify life in fileclassTag().
463  */
464 static char *makeFClass(rpmfi fi)
465 {
466     char *fclass = NULL;
467     const char *hc = rpmfiFClass(fi);
468
469     if (hc != NULL && hc[0] != '\0') {
470         fclass = xstrdup(hc);
471     } else {
472         switch (rpmfiFMode(fi) & S_IFMT) {
473         case S_IFBLK:
474             fclass = xstrdup("block special");
475             break;
476         case S_IFCHR:
477             fclass = xstrdup("character special");
478             break;
479         case S_IFDIR:
480             fclass = xstrdup("directory");
481             break;
482         case S_IFIFO:
483             fclass = xstrdup("fifo (named pipe)");
484             break;
485         case S_IFSOCK:
486             fclass = xstrdup("socket");
487             break;
488         case S_IFLNK:
489             fclass = rstrscat(NULL, "symbolic link to `",
490                               rpmfiFLink(fi), "'", NULL);
491             break;
492         }
493     }
494
495     return (fclass != NULL) ? fclass : xstrdup("");
496 }
497
498 /**
499  * Retrieve/generate file classes.
500  * @param h             header
501  * @retval td           tag data container
502  * @return              1 on success
503  */
504 static int fileclassTag(Header h, rpmtd td, headerGetFlags hgflags)
505 {
506     rpmfi fi = rpmfiNew(NULL, h, RPMTAG_BASENAMES, RPMFI_NOHEADER);
507     int numfiles = rpmfiFC(fi);
508
509     if (numfiles > 0) {
510         char **fclasses = xmalloc(numfiles * sizeof(*fclasses));
511         int ix;
512
513         rpmfiInit(fi, 0);
514         while ((ix = rpmfiNext(fi)) >= 0) {
515             fclasses[ix] = makeFClass(fi);
516         }
517
518         td->data = fclasses;
519         td->count = numfiles;
520         td->flags = RPMTD_ALLOCED | RPMTD_PTR_ALLOCED;
521         td->type = RPM_STRING_ARRAY_TYPE;
522     }
523
524     rpmfiFree(fi);
525     return (numfiles > 0); 
526 }
527
528 /**
529  * Retrieve file provides.
530  * @param h             header
531  * @retval td           tag data container
532  * @return              1 on success
533  */
534 static int fileprovideTag(Header h, rpmtd td, headerGetFlags hgflags)
535 {
536     return filedepTag(h, RPMTAG_PROVIDENAME, td, hgflags);
537 }
538
539 /**
540  * Retrieve file requires.
541  * @param h             header
542  * @retval td           tag data container
543  * @return              1 on success
544  */
545 static int filerequireTag(Header h, rpmtd td, headerGetFlags hgflags)
546 {
547     return filedepTag(h, RPMTAG_REQUIRENAME, td, hgflags);
548 }
549
550 /* I18N look aside diversions */
551
552 #if defined(ENABLE_NLS)
553 extern int _nl_msg_cat_cntr;    /* XXX GNU gettext voodoo */
554 #endif
555 static const char * const language = "LANGUAGE";
556
557 static const char * const _macro_i18ndomains = "%{?_i18ndomains}";
558
559 /**
560  * Retrieve i18n text.
561  * @param h             header
562  * @param tag           tag
563  * @retval td           tag data container
564  * @return              1 on success
565  */
566 static int i18nTag(Header h, rpmTag tag, rpmtd td, headerGetFlags hgflags)
567 {
568     int rc;
569 #if defined(ENABLE_NLS)
570     char * dstring = rpmExpand(_macro_i18ndomains, NULL);
571
572     td->type = RPM_STRING_TYPE;
573     td->data = NULL;
574     td->count = 0;
575
576     if (dstring && *dstring) {
577         char *domain, *de;
578         const char * langval;
579         char * msgkey;
580         const char * msgid;
581
582         rasprintf(&msgkey, "%s(%s)", headerGetString(h, RPMTAG_NAME), 
583                   rpmTagGetName(tag));
584
585         /* change to en_US for msgkey -> msgid resolution */
586         langval = getenv(language);
587         (void) setenv(language, "en_US", 1);
588         ++_nl_msg_cat_cntr;
589
590         msgid = NULL;
591         for (domain = dstring; domain != NULL; domain = de) {
592             de = strchr(domain, ':');
593             if (de) *de++ = '\0';
594             msgid = dgettext(domain, msgkey);
595             if (msgid != msgkey) break;
596         }
597
598         /* restore previous environment for msgid -> msgstr resolution */
599         if (langval)
600             (void) setenv(language, langval, 1);
601         else
602             unsetenv(language);
603         ++_nl_msg_cat_cntr;
604
605         if (domain && msgid) {
606             td->data = dgettext(domain, msgid);
607             td->data = xstrdup(td->data); /* XXX xstrdup has side effects. */
608             td->count = 1;
609             td->flags = RPMTD_ALLOCED;
610         }
611         dstring = _free(dstring);
612         free(msgkey);
613         if (td->data)
614             return 1;
615     }
616
617     free(dstring);
618 #endif
619
620     rc = headerGet(h, tag, td, HEADERGET_ALLOC);
621     if (rc && td->data) {
622         td->data = strtolocale(td->data);
623     }
624     return rc;
625 }
626
627 /**
628  * Retrieve text and convert to locale.
629  */
630 static int localeTag(Header h, rpmTag tag, rpmtd td)
631 {
632     int rc;
633     rc = headerGet(h, tag, td, HEADERGET_ALLOC);
634     if (!rc)
635         return 0;
636     if (td->type == RPM_STRING_TYPE) {
637         td->data = strtolocale(td->data);
638         td->count = 1;
639     } else if (td->type == RPM_STRING_ARRAY_TYPE) {
640         char **arr;
641         int i;
642         arr = xmalloc(td->count * sizeof(*arr));
643         for (i = 0; i < td->count; i++) {
644             arr[i] = xstrdup(((char **)td->data)[i]);
645             arr[i] = strtolocale(arr[i]);
646         }
647         _free(td->data);
648         td->data = arr;
649         td->flags = RPMTD_ALLOCED | RPMTD_PTR_ALLOCED;
650     }
651     return rc; 
652 }
653
654
655 /**
656  * Retrieve summary text.
657  * @param h             header
658  * @retval td           tag data container
659  * @return              1 on success
660  */
661 static int summaryTag(Header h, rpmtd td, headerGetFlags hgflags)
662 {
663     return i18nTag(h, RPMTAG_SUMMARY, td, hgflags);
664 }
665
666 /**
667  * Retrieve description text.
668  * @param h             header
669  * @retval td           tag data container
670  * @return              1 on success
671  */
672 static int descriptionTag(Header h, rpmtd td, headerGetFlags hgflags)
673 {
674     return i18nTag(h, RPMTAG_DESCRIPTION, td, hgflags);
675 }
676
677 static int changelognameTag(Header h, rpmtd td)
678 {
679     return localeTag(h, RPMTAG_CHANGELOGNAME, td);
680 }
681
682 static int changelogtextTag(Header h, rpmtd td)
683 {
684     return localeTag(h, RPMTAG_CHANGELOGTEXT, td);
685 }
686
687 /**
688  * Retrieve group text.
689  * @param h             header
690  * @retval td           tag data container
691  * @return              1 on success
692  */
693 static int groupTag(Header h, rpmtd td, headerGetFlags hgflags)
694 {
695     return i18nTag(h, RPMTAG_GROUP, td, hgflags);
696 }
697
698 /*
699  * Helper to convert 32bit tag to 64bit version.
700  * If header has new 64bit tag then just return the data,
701  * otherwise convert 32bit old tag data to 64bit values.
702  * For consistency, always return malloced data.
703  */
704 static int get64(Header h, rpmtd td, rpmTag newtag, rpmTag oldtag)
705 {
706     int rc;
707
708     if (headerIsEntry(h, newtag)) {
709         rc = headerGet(h, newtag, td, HEADERGET_ALLOC);
710     } else {
711         struct rpmtd_s olddata;
712         uint32_t *d32 = NULL;
713         uint64_t *d64 = NULL;
714
715         headerGet(h, oldtag, &olddata, HEADERGET_MINMEM);
716         if (rpmtdType(&olddata) == RPM_INT32_TYPE) {
717             td->type = RPM_INT64_TYPE;
718             td->count = olddata.count;
719             td->flags = RPMTD_ALLOCED;
720             td->data = xmalloc(sizeof(*d64) * td->count);
721             d64 = td->data;
722             while ((d32 = rpmtdNextUint32(&olddata))) {
723                 *d64++ = *d32;
724             }
725         } 
726         rpmtdFreeData(&olddata);
727         rc = d64 ? 1 : 0;
728     }
729
730     return rc;
731 }
732
733 /**
734  * Retrieve file sizes as 64bit regardless of how they're stored.
735  * @param h             header
736  * @retval td           tag data container
737  * @return              1 on success
738  */
739 static int longfilesizesTag(Header h, rpmtd td, headerGetFlags hgflags)
740 {
741     return get64(h, td, RPMTAG_LONGFILESIZES, RPMTAG_FILESIZES);
742 }
743
744 static int longarchivesizeTag(Header h, rpmtd td, headerGetFlags hgflags)
745 {
746     return get64(h, td, RPMTAG_LONGARCHIVESIZE, RPMTAG_ARCHIVESIZE);
747 }
748
749 static int longsizeTag(Header h, rpmtd td, headerGetFlags hgflags)
750 {
751     return get64(h, td, RPMTAG_LONGSIZE, RPMTAG_SIZE);
752 }
753
754 static int longsigsizeTag(Header h, rpmtd td, headerGetFlags hgflags)
755 {
756     return get64(h, td, RPMTAG_LONGSIGSIZE, RPMTAG_SIGSIZE);
757 }
758
759 static int numberTag(rpmtd td, uint32_t val)
760 {
761     uint32_t *tval = xmalloc(sizeof(*tval));
762
763     tval[0] = val;
764     td->type = RPM_INT32_TYPE;
765     td->count = 1;
766     td->data = tval;
767     td->flags = RPMTD_ALLOCED;
768     return 1; /* this cannot fail */
769 }
770
771 static int dbinstanceTag(Header h, rpmtd td, headerGetFlags hgflags)
772 {
773     return numberTag(td, headerGetInstance(h));
774 }
775
776 static int headercolorTag(Header h, rpmtd td, headerGetFlags hgflags)
777 {
778     rpm_color_t *fcolor, hcolor = 0;
779     struct rpmtd_s fcolors;
780
781     headerGet(h, RPMTAG_FILECOLORS, &fcolors, HEADERGET_MINMEM);
782     while ((fcolor = rpmtdNextUint32(&fcolors)) != NULL) {
783         hcolor |= *fcolor;
784     }
785     rpmtdFreeData(&fcolors);
786     hcolor &= 0x0f;
787
788     return numberTag(td, hcolor);
789 }
790
791 enum nevraFlags_e {
792     NEVRA_NAME          = (1 << 0),
793     NEVRA_EPOCH         = (1 << 1),
794     NEVRA_VERSION       = (1 << 2),
795     NEVRA_RELEASE       = (1 << 3),
796     NEVRA_ARCH          = (1 << 4)
797 };
798 typedef rpmFlags nevraFlags;
799
800 static int getNEVRA(Header h, rpmtd td, nevraFlags flags)
801 {
802     const char *val = NULL;
803     char *res = NULL;
804
805     if ((flags & NEVRA_NAME)) {
806         val = headerGetString(h, RPMTAG_NAME);
807         if (val) rstrscat(&res, val, "-", NULL);
808     }
809     if ((flags & NEVRA_EPOCH)) {
810         char *e = headerGetAsString(h, RPMTAG_EPOCH);
811         if (e) rstrscat(&res, e, ":", NULL);
812         free(e);
813     }
814     if ((flags & NEVRA_VERSION)) {
815         val = headerGetString(h, RPMTAG_VERSION);
816         if (val) rstrscat(&res, val, "-", NULL);
817     }
818     if ((flags & NEVRA_RELEASE)) {
819         val = headerGetString(h, RPMTAG_RELEASE);
820         if (val) rstrscat(&res, val, NULL);
821     }
822     if ((flags & NEVRA_ARCH)) {
823         val = headerGetString(h, RPMTAG_ARCH);
824         if (headerIsSource(h) && val == NULL) val = "src";
825         if (val) rstrscat(&res, ".", val, NULL);
826     }
827
828     td->type = RPM_STRING_TYPE;
829     td->data = res;
830     td->count = 1;
831     td->flags = RPMTD_ALLOCED;
832
833     return 1;
834 }
835
836 static int evrTag(Header h, rpmtd td, headerGetFlags hgflags)
837 {
838     return getNEVRA(h, td, NEVRA_EPOCH|NEVRA_VERSION|NEVRA_RELEASE);
839 }
840
841 static int nvrTag(Header h, rpmtd td, headerGetFlags hgflags)
842 {
843     return getNEVRA(h, td, NEVRA_NAME|NEVRA_VERSION|NEVRA_RELEASE);
844 }
845
846 static int nvraTag(Header h, rpmtd td, headerGetFlags hgflags)
847 {
848     return getNEVRA(h, td, NEVRA_NAME|NEVRA_VERSION|NEVRA_RELEASE|NEVRA_ARCH);
849 }
850
851 static int nevrTag(Header h, rpmtd td, headerGetFlags hgflags)
852 {
853     return getNEVRA(h, td, NEVRA_NAME|NEVRA_EPOCH|NEVRA_VERSION|NEVRA_RELEASE);
854 }
855
856 static int nevraTag(Header h, rpmtd td, headerGetFlags hgflags)
857 {
858     return getNEVRA(h, td, NEVRA_NAME|NEVRA_EPOCH|NEVRA_VERSION|NEVRA_RELEASE|NEVRA_ARCH);
859 }
860
861 static int verboseTag(Header h, rpmtd td, headerGetFlags hgflags)
862 {
863     if (rpmIsVerbose()) {
864         td->type = RPM_INT32_TYPE;
865         td->count = 1;
866         td->data = &(td->count);
867         td->flags = RPMTD_NONE;
868         return 1;
869     } else {
870         return 0;
871     }
872 }
873
874 static int epochnumTag(Header h, rpmtd td, headerGetFlags hgflags)
875 {
876     /* For consistency, always return malloced data */
877     if (!headerGet(h, RPMTAG_EPOCH, td, HEADERGET_ALLOC)) {
878         uint32_t *e = malloc(sizeof(*e));
879         *e = 0;
880         td->data = e;
881         td->type = RPM_INT32_TYPE;
882         td->count = 1;
883         td->flags = RPMTD_ALLOCED;
884     }
885     td->tag = RPMTAG_EPOCHNUM;
886     return 1;
887 }
888
889 static int depnevrsTag(Header h, rpmtd td, headerGetFlags hgflags,
890                         rpmTagVal tag)
891 {
892     rpmds ds = rpmdsNew(h, tag, 0);
893     int ndeps = rpmdsCount(ds);
894
895     if (ndeps > 0) {
896         char **deps = xmalloc(sizeof(*deps) * ndeps);
897         int i;
898         while ((i = rpmdsNext(ds)) >= 0) {
899             deps[i] = rpmdsNewDNEVR(NULL, ds);
900         }
901         td->data = deps;
902         td->type = RPM_STRING_ARRAY_TYPE;
903         td->count = ndeps;
904         td->flags |= (RPMTD_ALLOCED | RPMTD_PTR_ALLOCED);
905     }
906     rpmdsFree(ds);
907     return (ndeps > 0);
908 }
909
910 static int requirenevrsTag(Header h, rpmtd td, headerGetFlags hgflags)
911 {
912     return depnevrsTag(h, td, hgflags, RPMTAG_REQUIRENAME);
913 }
914
915 static int providenevrsTag(Header h, rpmtd td, headerGetFlags hgflags)
916 {
917     return depnevrsTag(h, td, hgflags, RPMTAG_PROVIDENAME);
918 }
919
920 static int obsoletenevrsTag(Header h, rpmtd td, headerGetFlags hgflags)
921 {
922     return depnevrsTag(h, td, hgflags, RPMTAG_OBSOLETENAME);
923 }
924
925 static int conflictnevrsTag(Header h, rpmtd td, headerGetFlags hgflags)
926 {
927     return depnevrsTag(h, td, hgflags, RPMTAG_CONFLICTNAME);
928 }
929
930 static int filenlinksTag(Header h, rpmtd td, headerGetFlags hgflags)
931 {
932     rpmfi fi = rpmfiNew(NULL, h, RPMTAG_BASENAMES, RPMFI_NOHEADER);
933     rpm_count_t fc = rpmfiFC(fi);
934
935     if (fc > 0) {
936         uint32_t *nlinks = xmalloc(fc * sizeof(*nlinks));
937         int ix;
938         while ((ix = rpmfiNext(fi)) >= 0) {
939             nlinks[ix] = rpmfiFNlink(fi);
940         }
941         td->data = nlinks;
942         td->type = RPM_INT32_TYPE;
943         td->count = fc;
944         td->flags = RPMTD_ALLOCED;
945     }
946
947     rpmfiFree(fi);
948     return (fc > 0);
949 }
950
951 static const struct headerTagFunc_s rpmHeaderTagExtensions[] = {
952     { RPMTAG_GROUP,             groupTag },
953     { RPMTAG_DESCRIPTION,       descriptionTag },
954     { RPMTAG_SUMMARY,           summaryTag },
955     { RPMTAG_FILECLASS,         fileclassTag },
956     { RPMTAG_FILENAMES,         filenamesTag },
957     { RPMTAG_ORIGFILENAMES,     origfilenamesTag },
958     { RPMTAG_FILEPROVIDE,       fileprovideTag },
959     { RPMTAG_FILEREQUIRE,       filerequireTag },
960     { RPMTAG_TRIGGERCONDS,      triggercondsTag },
961     { RPMTAG_TRIGGERTYPE,       triggertypeTag },
962     { RPMTAG_LONGFILESIZES,     longfilesizesTag },
963     { RPMTAG_LONGARCHIVESIZE,   longarchivesizeTag },
964     { RPMTAG_LONGSIZE,          longsizeTag },
965     { RPMTAG_LONGSIGSIZE,       longsigsizeTag },
966     { RPMTAG_CHANGELOGNAME,     changelognameTag },
967     { RPMTAG_CHANGELOGTEXT,     changelogtextTag },
968     { RPMTAG_DBINSTANCE,        dbinstanceTag },
969     { RPMTAG_EVR,               evrTag },
970     { RPMTAG_NVR,               nvrTag },
971     { RPMTAG_NEVR,              nevrTag },
972     { RPMTAG_NVRA,              nvraTag },
973     { RPMTAG_NEVRA,             nevraTag },
974     { RPMTAG_HEADERCOLOR,       headercolorTag },
975     { RPMTAG_VERBOSE,           verboseTag },
976     { RPMTAG_EPOCHNUM,          epochnumTag },
977     { RPMTAG_INSTFILENAMES,     instfilenamesTag },
978     { RPMTAG_REQUIRENEVRS,      requirenevrsTag },
979     { RPMTAG_PROVIDENEVRS,      providenevrsTag },
980     { RPMTAG_OBSOLETENEVRS,     obsoletenevrsTag },
981     { RPMTAG_CONFLICTNEVRS,     conflictnevrsTag },
982     { RPMTAG_FILENLINKS,        filenlinksTag },
983     { 0,                        NULL }
984 };
985
986 headerTagTagFunction rpmHeaderTagFunc(rpmTagVal tag)
987 {
988     const struct headerTagFunc_s * ext;
989     headerTagTagFunction func = NULL;
990
991     for (ext = rpmHeaderTagExtensions; ext->func != NULL; ext++) {
992         if (ext->tag == tag) {
993             func = ext->func;
994             break;
995         }
996     }
997     return func;
998 }
999