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