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