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