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