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