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