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