Remove static print buffer from pgpHexStr, return malloc'ed memory instead
[platform/upstream/rpm.git] / lib / formats.c
1 /** \ingroup header
2  * \file lib/formats.c
3  */
4
5 #include "system.h"
6
7 #include <rpm/rpmtag.h>
8 #include <rpm/rpmlib.h>         /* rpmGetFilesystem*() */
9 #include <rpm/rpmds.h>
10 #include <rpm/rpmmacro.h>       /* XXX for %_i18ndomains */
11 #include <rpm/rpmfi.h>
12 #include <rpm/rpmstring.h>
13
14 #include "rpmio/digest.h"
15 #include "lib/manifest.h"
16
17 #include "debug.h"
18
19
20 /**
21  * Identify type of trigger.
22  * @param type          tag type
23  * @param data          tag value
24  * @param formatPrefix  (unused)
25  * @param padding       (unused)
26  * @param element       (unused)
27  * @return              formatted string
28  */
29 static char * triggertypeFormat(rpmTagType type, rpm_constdata_t data, 
30                 char * formatPrefix, size_t padding,
31                 int element)
32 {
33     const int32_t * item = data;
34     char * val;
35
36     if (type != RPM_INT32_TYPE)
37         val = xstrdup(_("(not a number)"));
38     else if (*item & RPMSENSE_TRIGGERPREIN)
39         val = xstrdup("prein");
40     else if (*item & RPMSENSE_TRIGGERIN)
41         val = xstrdup("in");
42     else if (*item & RPMSENSE_TRIGGERUN)
43         val = xstrdup("un");
44     else if (*item & RPMSENSE_TRIGGERPOSTUN)
45         val = xstrdup("postun");
46     else
47         val = xstrdup("");
48     return val;
49 }
50
51 /**
52  * Format file permissions for display.
53  * @param type          tag type
54  * @param data          tag value
55  * @param formatPrefix
56  * @param padding
57  * @param element       (unused)
58  * @return              formatted string
59  */
60 static char * permsFormat(rpmTagType type, rpm_constdata_t data,
61                 char * formatPrefix, size_t padding, int element)
62 {
63     char * val;
64     char * buf;
65
66     if (type != RPM_INT32_TYPE) {
67         val = xstrdup(_("(not a number)"));
68     } else {
69         val = xmalloc(15 + padding);
70         strcat(formatPrefix, "s");
71         buf = rpmPermsString(*((const int32_t *) data));
72         sprintf(val, formatPrefix, buf);
73         buf = _free(buf);
74     }
75
76     return val;
77 }
78
79 /**
80  * Format file flags for display.
81  * @param type          tag type
82  * @param data          tag value
83  * @param formatPrefix
84  * @param padding
85  * @param element       (unused)
86  * @return              formatted string
87  */
88 static char * fflagsFormat(rpmTagType type, rpm_constdata_t data, 
89                 char * formatPrefix, size_t padding, int element)
90 {
91     char * val;
92     char buf[15];
93     rpmfileAttrs anint = *((const rpm_flag_t *) data);
94
95     if (type != RPM_INT32_TYPE) {
96         val = xstrdup(_("(not a number)"));
97     } else {
98         buf[0] = '\0';
99         if (anint & RPMFILE_DOC)
100             strcat(buf, "d");
101         if (anint & RPMFILE_CONFIG)
102             strcat(buf, "c");
103         if (anint & RPMFILE_SPECFILE)
104             strcat(buf, "s");
105         if (anint & RPMFILE_MISSINGOK)
106             strcat(buf, "m");
107         if (anint & RPMFILE_NOREPLACE)
108             strcat(buf, "n");
109         if (anint & RPMFILE_GHOST)
110             strcat(buf, "g");
111         if (anint & RPMFILE_LICENSE)
112             strcat(buf, "l");
113         if (anint & RPMFILE_README)
114             strcat(buf, "r");
115
116         val = xmalloc(5 + padding);
117         strcat(formatPrefix, "s");
118         sprintf(val, formatPrefix, buf);
119     }
120
121     return val;
122 }
123
124 /**
125  * Wrap a pubkey in ascii armor for display.
126  * @todo Permit selectable display formats (i.e. binary).
127  * @param type          tag type
128  * @param data          tag value
129  * @param formatPrefix  (unused)
130  * @param padding       (unused)
131  * @param element       no. bytes of binary data
132  * @return              formatted string
133  */
134 static char * armorFormat(rpmTagType type, rpm_constdata_t data, 
135                 char * formatPrefix, size_t padding,
136                 int element)
137 {
138     const char * enc;
139     const unsigned char * s;
140     unsigned char * bs;
141     size_t ns;
142     int atype;
143
144     switch (type) {
145     case RPM_BIN_TYPE:
146         s = data;
147         /* XXX HACK ALERT: element field abused as no. bytes of binary data. */
148         ns = element;
149         atype = PGPARMOR_SIGNATURE;     /* XXX check pkt for signature */
150         break;
151     case RPM_STRING_TYPE:
152     case RPM_STRING_ARRAY_TYPE:
153         enc = data;
154         if (b64decode(enc, (void **)&bs, &ns))
155             return xstrdup(_("(not base64)"));
156         s = bs;
157         atype = PGPARMOR_PUBKEY;        /* XXX check pkt for pubkey */
158         break;
159     case RPM_NULL_TYPE:
160     case RPM_CHAR_TYPE:
161     case RPM_INT8_TYPE:
162     case RPM_INT16_TYPE:
163     case RPM_INT32_TYPE:
164     case RPM_I18NSTRING_TYPE:
165     default:
166         return xstrdup(_("(invalid type)"));
167         break;
168     }
169
170     /* XXX this doesn't use padding directly, assumes enough slop in retval. */
171     return pgpArmorWrap(atype, s, ns);
172 }
173
174 /**
175  * Encode binary data in base64 for display.
176  * @todo Permit selectable display formats (i.e. binary).
177  * @param type          tag type
178  * @param data          tag value
179  * @param formatPrefix  (unused)
180  * @param padding
181  * @param element
182  * @return              formatted string
183  */
184 static char * base64Format(rpmTagType type, rpm_constdata_t data, 
185                 char * formatPrefix, size_t padding, int element)
186 {
187     char * val;
188
189     if (type != RPM_BIN_TYPE) {
190         val = xstrdup(_("(not a blob)"));
191     } else {
192         char * enc;
193         char * t;
194         /* XXX HACK ALERT: element field abused as no. bytes of binary data. */
195         size_t ns = element;
196         size_t nt = 0;
197
198         if ((enc = b64encode(data, ns, -1)) != NULL) {
199                 nt = strlen(enc);
200         }
201
202         val = t = xmalloc(nt + padding + 1);
203
204         *t = '\0';
205         if (enc != NULL) {
206             t = stpcpy(t, enc);
207             enc = _free(enc);
208         }
209     }
210
211     return val;
212 }
213
214 /**
215  */
216 static size_t xmlstrlen(const char * s)
217 {
218     size_t len = 0;
219     int c;
220
221     while ((c = *s++) != '\0')
222     {
223         switch (c) {
224         case '<': case '>':     len += 4;       break;
225         case '&':               len += 5;       break;
226         default:                len += 1;       break;
227         }
228     }
229     return len;
230 }
231
232 /**
233  */
234 static char * xmlstrcpy(char * t, const char * s)
235 {
236     char * te = t;
237     int c;
238
239     while ((c = *s++) != '\0') {
240         switch (c) {
241         case '<':       te = stpcpy(te, "&lt;");        break;
242         case '>':       te = stpcpy(te, "&gt;");        break;
243         case '&':       te = stpcpy(te, "&amp;");       break;
244         default:        *te++ = c;                      break;
245         }
246     }
247     *te = '\0';
248     return t;
249 }
250
251 /**
252  * Wrap tag data in simple header xml markup.
253  * @param type          tag type
254  * @param data          tag value
255  * @param formatPrefix
256  * @param padding
257  * @param element       (unused)
258  * @return              formatted string
259  */
260 static char * xmlFormat(rpmTagType type, rpm_constdata_t data, 
261                 char * formatPrefix, size_t padding,
262                 int element)
263 {
264     const char * xtag = NULL;
265     size_t nb;
266     char * val, * bs = NULL;
267     const char * s = NULL;
268     char * t, * te;
269     unsigned long anint = 0;
270     int xx;
271
272     switch (type) {
273     case RPM_I18NSTRING_TYPE:
274     case RPM_STRING_TYPE:
275         s = data;
276         xtag = "string";
277         break;
278     case RPM_BIN_TYPE:
279     {   
280         /* XXX HACK ALERT: element field abused as no. bytes of binary data. */
281         size_t ns = element;
282         if ((bs = b64encode(data, ns, 0)) == NULL) {
283                 /* XXX proper error handling would be better. */
284                 bs = xcalloc(1, padding + (ns / 3) * 4 + 1);
285         }
286         s = bs;
287         xtag = "base64";
288     }   break;
289     case RPM_CHAR_TYPE:
290     case RPM_INT8_TYPE:
291         anint = *((const uint8_t *) data);
292         break;
293     case RPM_INT16_TYPE:
294         anint = *((const uint16_t *) data);
295         break;
296     case RPM_INT32_TYPE:
297         anint = *((const uint32_t *) data);
298         break;
299     case RPM_NULL_TYPE:
300     case RPM_STRING_ARRAY_TYPE:
301     default:
302         return xstrdup(_("(invalid xml type)"));
303         break;
304     }
305
306     if (s == NULL) {
307         size_t tlen = 32;
308         t = memset(alloca(tlen+1), 0, tlen+1);
309         if (anint != 0)
310             xx = snprintf(t, tlen, "%lu", anint);
311         s = t;
312         xtag = "integer";
313     }
314
315     nb = xmlstrlen(s);
316     if (nb == 0) {
317         nb += strlen(xtag) + sizeof("\t</>");
318         te = t = alloca(nb);
319         te = stpcpy( stpcpy( stpcpy(te, "\t<"), xtag), "/>");
320     } else {
321         nb += 2 * strlen(xtag) + sizeof("\t<></>");
322         te = t = alloca(nb);
323         te = stpcpy( stpcpy( stpcpy(te, "\t<"), xtag), ">");
324         te = xmlstrcpy(te, s);
325         te += strlen(te);
326         te = stpcpy( stpcpy( stpcpy(te, "</"), xtag), ">");
327     }
328
329     /* XXX s was malloc'd */
330     if (!strcmp(xtag, "base64"))
331         free(bs);
332
333     nb += padding;
334     val = xmalloc(nb+1);
335     strcat(formatPrefix, "s");
336     xx = snprintf(val, nb, formatPrefix, t);
337     val[nb] = '\0';
338
339     return val;
340 }
341
342 /**
343  * Display signature fingerprint and time.
344  * @param type          tag type
345  * @param data          tag value
346  * @param formatPrefix  (unused)
347  * @param padding
348  * @param element       (unused)
349  * @return              formatted string
350  */
351 static char * pgpsigFormat(rpmTagType type, rpm_constdata_t data, 
352                 char * formatPrefix, size_t padding,
353                 int element)
354 {
355     char * val, * t;
356
357     if (type != RPM_BIN_TYPE) {
358         val = xstrdup(_("(not a blob)"));
359     } else {
360         const uint8_t * pkt = (const uint8_t *) data;
361         size_t pktlen = 0;
362         unsigned int v = *pkt;
363         pgpTag tag = 0;
364         size_t plen;
365         size_t hlen = 0;
366
367         if (v & 0x80) {
368             if (v & 0x40) {
369                 tag = (v & 0x3f);
370                 plen = pgpLen(pkt+1, &hlen);
371             } else {
372                 tag = (v >> 2) & 0xf;
373                 plen = (1 << (v & 0x3));
374                 hlen = pgpGrab(pkt+1, plen);
375             }
376         
377             pktlen = 1 + plen + hlen;
378         }
379
380         if (pktlen == 0 || tag != PGPTAG_SIGNATURE) {
381             val = xstrdup(_("(not an OpenPGP signature)"));
382         } else {
383             pgpDig dig = pgpNewDig();
384             pgpDigParams sigp = &dig->signature;
385             size_t nb = 0;
386             char *tempstr = NULL;
387
388             (void) pgpPrtPkts(pkt, pktlen, dig, 0);
389
390             val = NULL;
391         again:
392             nb += 100;
393             val = t = xrealloc(val, nb + 1);
394
395             switch (sigp->pubkey_algo) {
396             case PGPPUBKEYALGO_DSA:
397                 t = stpcpy(t, "DSA");
398                 break;
399             case PGPPUBKEYALGO_RSA:
400                 t = stpcpy(t, "RSA");
401                 break;
402             default:
403                 (void) snprintf(t, nb - (t - val), "%d", sigp->pubkey_algo);
404                 t += strlen(t);
405                 break;
406             }
407             if (t + 5 >= val + nb)
408                 goto again;
409             *t++ = '/';
410             switch (sigp->hash_algo) {
411             case PGPHASHALGO_MD5:
412                 t = stpcpy(t, "MD5");
413                 break;
414             case PGPHASHALGO_SHA1:
415                 t = stpcpy(t, "SHA1");
416                 break;
417             default:
418                 (void) snprintf(t, nb - (t - val), "%d", sigp->hash_algo);
419                 t += strlen(t);
420                 break;
421             }
422             if (t + strlen (", ") + 1 >= val + nb)
423                 goto again;
424
425             t = stpcpy(t, ", ");
426
427             /* this is important if sizeof(int32_t) ! sizeof(time_t) */
428             {   time_t dateint = pgpGrab(sigp->time, sizeof(sigp->time));
429                 struct tm * tstruct = localtime(&dateint);
430                 if (tstruct)
431                     (void) strftime(t, (nb - (t - val)), "%c", tstruct);
432             }
433             t += strlen(t);
434             if (t + strlen (", Key ID ") + 1 >= val + nb)
435                 goto again;
436             t = stpcpy(t, ", Key ID ");
437             tempstr = pgpHexStr(sigp->signid, sizeof(sigp->signid));
438             if (t + strlen (tempstr) > val + nb)
439                 goto again;
440             t = stpcpy(t, tempstr);
441             free(tempstr);
442
443             dig = pgpFreeDig(dig);
444         }
445     }
446
447     return val;
448 }
449
450 /**
451  * Format dependency flags for display.
452  * @param type          tag type
453  * @param data          tag value
454  * @param formatPrefix
455  * @param padding
456  * @param element       (unused)
457  * @return              formatted string
458  */
459 static char * depflagsFormat(rpmTagType type, rpm_constdata_t data, 
460                 char * formatPrefix, size_t padding, int element)
461 {
462     char * val;
463     char buf[10];
464     rpmsenseFlags anint;
465
466     if (type != RPM_INT32_TYPE) {
467         val = xstrdup(_("(not a number)"));
468     } else {
469         anint = *((const rpm_flag_t *) data);
470         buf[0] = '\0';
471
472         if (anint & RPMSENSE_LESS) 
473             strcat(buf, "<");
474         if (anint & RPMSENSE_GREATER)
475             strcat(buf, ">");
476         if (anint & RPMSENSE_EQUAL)
477             strcat(buf, "=");
478
479         val = xmalloc(5 + padding);
480         strcat(formatPrefix, "s");
481         sprintf(val, formatPrefix, buf);
482     }
483
484     return val;
485 }
486
487 /**
488  * Retrieve mounted file system paths.
489  * @param h             header
490  * @retval *type        tag type
491  * @retval *data        tag value
492  * @retval *count       no. of data items
493  * @retval *freeData    data-was-malloc'ed indicator
494  * @return              0 on success
495  */
496 static int fsnamesTag( Header h, int32_t * type,
497                 rpm_data_t * data, rpm_count_t * count,
498                 int * freeData)
499 {
500     const char ** list;
501
502     if (rpmGetFilesystemList(&list, count))
503         return 1;
504
505     if (type) *type = RPM_STRING_ARRAY_TYPE;
506     if (data) *((const char ***) data) = list;
507     if (freeData) *freeData = 0;
508
509     return 0; 
510 }
511
512 /**
513  * Retrieve install prefixes.
514  * @param h             header
515  * @retval *type        tag type
516  * @retval *data        tag value
517  * @retval *count       no. of data items
518  * @retval *freeData    data-was-malloc'ed indicator
519  * @return              0 on success
520  */
521 static int instprefixTag(Header h, rpmTagType* type,
522                 rpm_data_t * data,
523                 rpm_count_t * count,
524                 int * freeData)
525 {
526     HGE_t hge = (HGE_t)headerGetEntryMinMemory;
527     HFD_t hfd = headerFreeData;
528     rpmTagType ipt;
529     char ** array;
530
531     if (hge(h, RPMTAG_INSTALLPREFIX, type, (rpm_data_t *)data, count)) {
532         if (freeData) *freeData = 0;
533         return 0;
534     } else if (hge(h, RPMTAG_INSTPREFIXES, &ipt, (rpm_data_t *) &array, count)) {
535         if (type) *type = RPM_STRING_TYPE;
536         if (data) *data = xstrdup(array[0]);
537         if (freeData) *freeData = 1;
538         array = hfd(array, ipt);
539         return 0;
540     }
541
542     return 1;
543 }
544
545 /**
546  * Retrieve mounted file system space.
547  * @param h             header
548  * @retval *type        tag type
549  * @retval *data        tag value
550  * @retval *count       no. of data items
551  * @retval *freeData    data-was-malloc'ed indicator
552  * @return              0 on success
553  */
554 static int fssizesTag(Header h, rpmTagType* type,
555                 rpm_data_t * data, rpm_count_t * count,
556                 int * freeData)
557 {
558     HGE_t hge = (HGE_t)headerGetEntryMinMemory;
559     const char ** filenames;
560     rpm_off_t * filesizes;
561     rpm_off_t * usages;
562     rpm_count_t numFiles;
563
564     if (!hge(h, RPMTAG_FILESIZES, NULL, (rpm_data_t *) &filesizes, &numFiles)) {
565         filesizes = NULL;
566         numFiles = 0;
567         filenames = NULL;
568     } else {
569         rpmfiBuildFNames(h, RPMTAG_BASENAMES, &filenames, &numFiles);
570     }
571
572     if (rpmGetFilesystemList(NULL, count))
573         return 1;
574
575     *type = RPM_INT32_TYPE;
576     *freeData = 1;
577
578     if (filenames == NULL) {
579         usages = xcalloc((*count), sizeof(usages));
580         *data = usages;
581
582         return 0;
583     }
584
585     if (rpmGetFilesystemUsage(filenames, filesizes, numFiles, &usages, 0))      
586         return 1;
587
588     *data = usages;
589
590     filenames = _free(filenames);
591
592     return 0;
593 }
594
595 /**
596  * Retrieve trigger info.
597  * @param h             header
598  * @retval *type        tag type
599  * @retval *data        tag value
600  * @retval *count       no. of data items
601  * @retval *freeData    data-was-malloc'ed indicator
602  * @return              0 on success
603  */
604 static int triggercondsTag(Header h, rpmTagType* type,
605                 rpm_data_t * data, rpm_count_t * count,
606                 int * freeData)
607 {
608     HGE_t hge = (HGE_t)headerGetEntryMinMemory;
609     HFD_t hfd = headerFreeData;
610     rpmTagType tnt, tvt, tst;
611     int32_t * indices;
612     char ** names, ** versions;
613     rpm_count_t numNames, numScripts;
614     rpm_count_t i, j;
615     rpm_flag_t * flags;
616     char ** conds, ** s;
617     char * item, * flagsStr;
618     char * chptr;
619     int xx;
620     char buf[5];
621
622     if (!hge(h, RPMTAG_TRIGGERNAME, &tnt, (rpm_data_t *) &names, &numNames)) {
623         *freeData = 0;
624         return 0;
625     }
626
627     xx = hge(h, RPMTAG_TRIGGERINDEX, NULL, (rpm_data_t *) &indices, NULL);
628     xx = hge(h, RPMTAG_TRIGGERFLAGS, NULL, (rpm_data_t *) &flags, NULL);
629     xx = hge(h, RPMTAG_TRIGGERVERSION, &tvt, (rpm_data_t *) &versions, NULL);
630     xx = hge(h, RPMTAG_TRIGGERSCRIPTS, &tst, (rpm_data_t *) &s, &numScripts);
631     s = hfd(s, tst);
632
633     *freeData = 1;
634     *data = conds = xmalloc(sizeof(*conds) * numScripts);
635     *count = numScripts;
636     *type = RPM_STRING_ARRAY_TYPE;
637     for (i = 0; i < numScripts; i++) {
638         chptr = xstrdup("");
639
640         for (j = 0; j < numNames; j++) {
641             if (indices[j] != i)
642                 continue;
643
644             item = xmalloc(strlen(names[j]) + strlen(versions[j]) + 20);
645             if (flags[j] & RPMSENSE_SENSEMASK) {
646                 buf[0] = '%', buf[1] = '\0';
647                 flagsStr = depflagsFormat(RPM_INT32_TYPE, flags, buf, 0, 0);
648                 sprintf(item, "%s %s %s", names[j], flagsStr, versions[j]);
649                 flagsStr = _free(flagsStr);
650             } else {
651                 strcpy(item, names[j]);
652             }
653
654             chptr = xrealloc(chptr, strlen(chptr) + strlen(item) + 5);
655             if (*chptr != '\0') strcat(chptr, ", ");
656             strcat(chptr, item);
657             item = _free(item);
658         }
659
660         conds[i] = chptr;
661     }
662
663     names = hfd(names, tnt);
664     versions = hfd(versions, tvt);
665
666     return 0;
667 }
668
669 /**
670  * Retrieve trigger type info.
671  * @param h             header
672  * @retval *type        tag type
673  * @retval *data        tag value
674  * @retval *count       no. of data items
675  * @retval *freeData    data-was-malloc'ed indicator
676  * @return              0 on success
677  */
678 static int triggertypeTag(Header h, rpmTagType* type,
679                 rpm_data_t * data, rpm_count_t * count,
680                 int * freeData)
681 {
682     HGE_t hge = (HGE_t)headerGetEntryMinMemory;
683     HFD_t hfd = headerFreeData;
684     rpmTagType tst;
685     int32_t * indices;
686     const char ** conds;
687     const char ** s;
688     int xx;
689     rpm_count_t numScripts, numNames;
690     rpm_count_t i, j;
691     rpm_flag_t * flags;
692
693     if (!hge(h, RPMTAG_TRIGGERINDEX, NULL, (rpm_data_t *) &indices, &numNames)) {
694         *freeData = 0;
695         return 1;
696     }
697
698     xx = hge(h, RPMTAG_TRIGGERFLAGS, NULL, (rpm_data_t *) &flags, NULL);
699     xx = hge(h, RPMTAG_TRIGGERSCRIPTS, &tst, (rpm_data_t *) &s, &numScripts);
700     s = hfd(s, tst);
701
702     *freeData = 1;
703     *data = conds = xmalloc(sizeof(*conds) * numScripts);
704     *count = numScripts;
705     *type = RPM_STRING_ARRAY_TYPE;
706     for (i = 0; i < numScripts; i++) {
707         for (j = 0; j < numNames; j++) {
708             if (indices[j] != i)
709                 continue;
710
711             if (flags[j] & RPMSENSE_TRIGGERPREIN)
712                 conds[i] = xstrdup("prein");
713             else if (flags[j] & RPMSENSE_TRIGGERIN)
714                 conds[i] = xstrdup("in");
715             else if (flags[j] & RPMSENSE_TRIGGERUN)
716                 conds[i] = xstrdup("un");
717             else if (flags[j] & RPMSENSE_TRIGGERPOSTUN)
718                 conds[i] = xstrdup("postun");
719             else
720                 conds[i] = xstrdup("");
721             break;
722         }
723     }
724
725     return 0;
726 }
727
728 /**
729  * Retrieve file paths.
730  * @param h             header
731  * @retval *type        tag type
732  * @retval *data        tag value
733  * @retval *count       no. of data items
734  * @retval *freeData    data-was-malloc'ed indicator
735  * @return              0 on success
736  */
737 static int filenamesTag(Header h, rpmTagType* type,
738                 rpm_data_t * data, rpm_count_t * count,
739                 int * freeData)
740 {
741     *type = RPM_STRING_ARRAY_TYPE;
742     rpmfiBuildFNames(h, RPMTAG_BASENAMES, (const char ***) data, count);
743     *freeData = 1;
744     return 0; 
745 }
746
747 /**
748  * Retrieve file classes.
749  * @param h             header
750  * @retval *type        tag type
751  * @retval *data        tag value
752  * @retval *count       no. of data items
753  * @retval *freeData    data-was-malloc'ed indicator
754  * @return              0 on success
755  */
756 static int fileclassTag(Header h, rpmTagType* type,
757                 rpm_data_t * data, rpm_count_t * count,
758                 int * freeData)
759 {
760     *type = RPM_STRING_ARRAY_TYPE;
761     rpmfiBuildFClasses(h, (const char ***) data, count);
762     *freeData = 1;
763     return 0; 
764 }
765
766 /**
767  * Retrieve file provides.
768  * @param h             header
769  * @retval *type        tag type
770  * @retval *data        tag value
771  * @retval *count       no. of data items
772  * @retval *freeData    data-was-malloc'ed indicator
773  * @return              0 on success
774  */
775 static int fileprovideTag(Header h, rpmTagType* type,
776                 rpm_data_t * data, rpm_count_t * count,
777                 int * freeData)
778 {
779     *type = RPM_STRING_ARRAY_TYPE;
780     rpmfiBuildFDeps(h, RPMTAG_PROVIDENAME, (const char ***) data, count);
781     *freeData = 1;
782     return 0; 
783 }
784
785 /**
786  * Retrieve file requires.
787  * @param h             header
788  * @retval *type        tag type
789  * @retval *data        tag value
790  * @retval *count       no. of data items
791  * @retval *freeData    data-was-malloc'ed indicator
792  * @return              0 on success
793  */
794 static int filerequireTag(Header h, rpmTagType* type,
795                 rpm_data_t * data, rpm_count_t * count,
796                 int * freeData)
797 {
798     *type = RPM_STRING_ARRAY_TYPE;
799     rpmfiBuildFDeps(h, RPMTAG_REQUIRENAME, (const char ***) data, count);
800     *freeData = 1;
801     return 0; 
802 }
803
804 /* I18N look aside diversions */
805
806 #if defined(ENABLE_NLS)
807 extern int _nl_msg_cat_cntr;    /* XXX GNU gettext voodoo */
808 #endif
809 static const char * const language = "LANGUAGE";
810
811 static const char * const _macro_i18ndomains = "%{?_i18ndomains}";
812
813 /**
814  * Retrieve i18n text.
815  * @param h             header
816  * @param tag           tag
817  * @retval *type        tag type
818  * @retval *data        tag value
819  * @retval *count       no. of data items
820  * @retval *freeData    data-was-malloc'ed indicator
821  * @return              0 on success
822  */
823 static int i18nTag(Header h, rpmTag tag, rpmTagType* type,
824                 rpm_data_t * data, rpm_count_t * count,
825                 int * freeData)
826 {
827     HGE_t hge = (HGE_t)headerGetEntryMinMemory;
828     char * dstring = rpmExpand(_macro_i18ndomains, NULL);
829     int rc;
830
831     *type = RPM_STRING_TYPE;
832     *data = NULL;
833     *count = 0;
834     *freeData = 0;
835
836     if (dstring && *dstring) {
837         char *domain, *de;
838         const char * langval;
839         char * msgkey;
840         const char * msgid;
841         const char * n;
842         int xx;
843
844         xx = headerNVR(h, &n, NULL, NULL);
845         rasprintf(&msgkey, "%s(%s)", n, rpmTagGetName(tag));
846
847         /* change to en_US for msgkey -> msgid resolution */
848         langval = getenv(language);
849         (void) setenv(language, "en_US", 1);
850 #if defined(ENABLE_NLS)
851         ++_nl_msg_cat_cntr;
852 #endif
853
854         msgid = NULL;
855         for (domain = dstring; domain != NULL; domain = de) {
856             de = strchr(domain, ':');
857             if (de) *de++ = '\0';
858             msgid = dgettext(domain, msgkey);
859             if (msgid != msgkey) break;
860         }
861
862         /* restore previous environment for msgid -> msgstr resolution */
863         if (langval)
864             (void) setenv(language, langval, 1);
865         else
866             unsetenv(language);
867 #if defined(ENABLE_NLS)
868         ++_nl_msg_cat_cntr;
869 #endif
870
871         if (domain && msgid) {
872             *data = dgettext(domain, msgid);
873             *data = xstrdup(*data);     /* XXX xstrdup has side effects. */
874             *count = 1;
875             *freeData = 1;
876         }
877         dstring = _free(dstring);
878         free(msgkey);
879         if (*data)
880             return 0;
881     }
882
883     dstring = _free(dstring);
884
885     rc = hge(h, tag, type, data, count);
886
887     if (rc && (*data) != NULL) {
888         *data = xstrdup(*data);
889         *freeData = 1;
890         return 0;
891     }
892
893     *freeData = 0;
894     *data = NULL;
895     *count = 0;
896     return 1;
897 }
898
899 /**
900  * Retrieve summary text.
901  * @param h             header
902  * @retval *type        tag type
903  * @retval *data        tag value
904  * @retval *count       no. of data items
905  * @retval *freeData    data-was-malloc'ed indicator
906  * @return              0 on success
907  */
908 static int summaryTag(Header h, rpmTagType* type,
909                 rpm_data_t * data, rpm_count_t * count,
910                 int * freeData)
911 {
912     return i18nTag(h, RPMTAG_SUMMARY, type, data, count, freeData);
913 }
914
915 /**
916  * Retrieve description text.
917  * @param h             header
918  * @retval *type        tag type
919  * @retval *data        tag value
920  * @retval *count       no. of data items
921  * @retval *freeData    data-was-malloc'ed indicator
922  * @return              0 on success
923  */
924 static int descriptionTag(Header h, rpmTagType* type,
925                 rpm_data_t * data, rpm_count_t * count,
926                 int * freeData)
927 {
928     return i18nTag(h, RPMTAG_DESCRIPTION, type, data, count, freeData);
929 }
930
931 /**
932  * Retrieve group text.
933  * @param h             header
934  * @retval *type        tag type
935  * @retval *data        tag value
936  * @retval *count       no. of data items
937  * @retval *freeData    data-was-malloc'ed indicator
938  * @return              0 on success
939  */
940 static int groupTag(Header h, rpmTagType* type,
941                 rpm_data_t * data, rpm_count_t * count,
942                 int * freeData)
943 {
944     return i18nTag(h, RPMTAG_GROUP, type, data, count, freeData);
945 }
946
947 /* FIX: cast? */
948 const struct headerSprintfExtension_s rpmHeaderFormats[] = {
949     { HEADER_EXT_TAG, "RPMTAG_GROUP",           { groupTag } },
950     { HEADER_EXT_TAG, "RPMTAG_DESCRIPTION",     { descriptionTag } },
951     { HEADER_EXT_TAG, "RPMTAG_SUMMARY",         { summaryTag } },
952     { HEADER_EXT_TAG, "RPMTAG_FILECLASS",       { fileclassTag } },
953     { HEADER_EXT_TAG, "RPMTAG_FILENAMES",       { filenamesTag } },
954     { HEADER_EXT_TAG, "RPMTAG_FILEPROVIDE",     { fileprovideTag } },
955     { HEADER_EXT_TAG, "RPMTAG_FILEREQUIRE",     { filerequireTag } },
956     { HEADER_EXT_TAG, "RPMTAG_FSNAMES",         { fsnamesTag } },
957     { HEADER_EXT_TAG, "RPMTAG_FSSIZES",         { fssizesTag } },
958     { HEADER_EXT_TAG, "RPMTAG_INSTALLPREFIX",   { instprefixTag } },
959     { HEADER_EXT_TAG, "RPMTAG_TRIGGERCONDS",    { triggercondsTag } },
960     { HEADER_EXT_TAG, "RPMTAG_TRIGGERTYPE",     { triggertypeTag } },
961     { HEADER_EXT_FORMAT, "armor",               { armorFormat } },
962     { HEADER_EXT_FORMAT, "base64",              { base64Format } },
963     { HEADER_EXT_FORMAT, "pgpsig",              { pgpsigFormat } },
964     { HEADER_EXT_FORMAT, "depflags",            { depflagsFormat } },
965     { HEADER_EXT_FORMAT, "fflags",              { fflagsFormat } },
966     { HEADER_EXT_FORMAT, "perms",               { permsFormat } },
967     { HEADER_EXT_FORMAT, "permissions",         { permsFormat } },
968     { HEADER_EXT_FORMAT, "triggertype",         { triggertypeFormat } },
969     { HEADER_EXT_FORMAT, "xml",                 { xmlFormat } },
970     { HEADER_EXT_MORE, NULL,            { (void *) headerDefaultFormats } }
971 } ;