Use actual rpmTags in place of the old HEADER_FOO defines everywhere
[platform/upstream/rpm.git] / lib / headerfmt.c
1 /** \ingroup header
2  * \file lib/headerfmt.c
3  */
4
5 #include "system.h"
6
7 #include <rpm/header.h>
8 #include <rpm/rpmtag.h>
9 #include <rpm/rpmstring.h>
10 #include <rpm/rpmpgp.h>
11
12 #include "debug.h"
13
14 #define PARSER_BEGIN    0
15 #define PARSER_IN_ARRAY 1
16 #define PARSER_IN_EXPR  2
17
18 /** \ingroup header
19  * HEADER_EXT_FORMAT format function prototype.
20  * This will only ever be passed RPM_INT32_TYPE or RPM_STRING_TYPE to
21  * help keep things simple.
22  *
23  * @param td            tag data container
24  * @param formatPrefix
25  * @return              formatted string
26  */
27 typedef char * (*headerTagFormatFunction) (rpmtd td, char * formatPrefix);
28
29 extern void *rpmHeaderFormatFuncByName(const char *fmt);
30
31 /** \ingroup header
32  */
33 typedef struct sprintfTag_s * sprintfTag;
34 struct sprintfTag_s {
35     headerTagFormatFunction fmt;
36     rpmTag tag;
37     int justOne;
38     char * format;
39     char * type;
40 };
41
42 /** \ingroup header
43  */
44 typedef struct sprintfToken_s * sprintfToken;
45 struct sprintfToken_s {
46     enum {
47         PTOK_NONE = 0,
48         PTOK_TAG,
49         PTOK_ARRAY,
50         PTOK_STRING,
51         PTOK_COND
52     } type;
53     union {
54         struct sprintfTag_s tag;        /*!< PTOK_TAG */
55         struct {
56             sprintfToken format;
57             int i;
58             int numTokens;
59         } array;                        /*!< PTOK_ARRAY */
60         struct {
61             char * string;
62             int len;
63         } string;                       /*!< PTOK_STRING */
64         struct {
65             sprintfToken ifFormat;
66             int numIfTokens;
67             sprintfToken elseFormat;
68             int numElseTokens;
69             struct sprintfTag_s tag;
70         } cond;                         /*!< PTOK_COND */
71     } u;
72 };
73
74 /**
75  */
76 typedef struct headerSprintfArgs_s {
77     Header h;
78     char * fmt;
79     const char * errmsg;
80     rpmtd *cache;
81     sprintfToken format;
82     HeaderIterator hi;
83     char * val;
84     size_t vallen;
85     size_t alloced;
86     int numTokens;
87     int i;
88     headerGetFlags hgflags;
89 } * headerSprintfArgs;
90
91
92 static char escapedChar(const char ch)  
93 {
94     switch (ch) {
95     case 'a':   return '\a';
96     case 'b':   return '\b';
97     case 'f':   return '\f';
98     case 'n':   return '\n';
99     case 'r':   return '\r';
100     case 't':   return '\t';
101     case 'v':   return '\v';
102     default:    return ch;
103     }
104 }
105
106 /**
107  * Destroy headerSprintf format array.
108  * @param format        sprintf format array
109  * @param num           number of elements
110  * @return              NULL always
111  */
112 static sprintfToken
113 freeFormat( sprintfToken format, int num)
114 {
115     int i;
116
117     if (format == NULL) return NULL;
118
119     for (i = 0; i < num; i++) {
120         switch (format[i].type) {
121         case PTOK_ARRAY:
122             format[i].u.array.format =
123                 freeFormat(format[i].u.array.format,
124                         format[i].u.array.numTokens);
125             break;
126         case PTOK_COND:
127             format[i].u.cond.ifFormat =
128                 freeFormat(format[i].u.cond.ifFormat, 
129                         format[i].u.cond.numIfTokens);
130             format[i].u.cond.elseFormat =
131                 freeFormat(format[i].u.cond.elseFormat, 
132                         format[i].u.cond.numElseTokens);
133             break;
134         case PTOK_NONE:
135         case PTOK_TAG:
136         case PTOK_STRING:
137         default:
138             break;
139         }
140     }
141     format = _free(format);
142     return NULL;
143 }
144
145 /**
146  * Initialize an hsa iteration.
147  * @param hsa           headerSprintf args
148  */
149 static void hsaInit(headerSprintfArgs hsa)
150 {
151     sprintfTag tag =
152         (hsa->format->type == PTOK_TAG
153             ? &hsa->format->u.tag :
154         (hsa->format->type == PTOK_ARRAY
155             ? &hsa->format->u.array.format->u.tag :
156         NULL));
157
158     hsa->i = 0;
159     if (tag != NULL && tag->tag == -2)
160         hsa->hi = headerInitIterator(hsa->h);
161     /* Normally with bells and whistles enabled, but raw dump on iteration. */
162     hsa->hgflags = (hsa->hi == NULL) ? HEADERGET_EXT : HEADERGET_RAW;
163 }
164
165 /**
166  * Return next hsa iteration item.
167  * @param hsa           headerSprintf args
168  * @return              next sprintfToken (or NULL)
169  */
170 static sprintfToken hsaNext(headerSprintfArgs hsa)
171 {
172     sprintfToken fmt = NULL;
173     sprintfTag tag =
174         (hsa->format->type == PTOK_TAG
175             ? &hsa->format->u.tag :
176         (hsa->format->type == PTOK_ARRAY
177             ? &hsa->format->u.array.format->u.tag :
178         NULL));
179
180     if (hsa->i >= 0 && hsa->i < hsa->numTokens) {
181         fmt = hsa->format + hsa->i;
182         if (hsa->hi == NULL) {
183             hsa->i++;
184         } else {
185             struct rpmtd_s td;
186
187             /* hmm, cache the data from here too? */
188             if (!headerNext(hsa->hi, &td))
189                 fmt = NULL;
190             tag->tag = td.tag;
191             rpmtdFreeData(&td);
192         }
193     }
194
195     return fmt;
196 }
197
198 /**
199  * Finish an hsa iteration.
200  * @param hsa           headerSprintf args
201  */
202 static void hsaFini(headerSprintfArgs hsa)
203 {
204     hsa->hi = headerFreeIterator(hsa->hi);
205     hsa->i = 0;
206 }
207
208 /**
209  * Reserve sufficient buffer space for next output value.
210  * @param hsa           headerSprintf args
211  * @param need          no. of bytes to reserve
212  * @return              pointer to reserved space
213  */
214 static char * hsaReserve(headerSprintfArgs hsa, size_t need)
215 {
216     if ((hsa->vallen + need) >= hsa->alloced) {
217         if (hsa->alloced <= need)
218             hsa->alloced += need;
219         hsa->alloced <<= 1;
220         hsa->val = xrealloc(hsa->val, hsa->alloced+1);  
221     }
222     return hsa->val + hsa->vallen;
223 }
224
225 /**
226  * Search tags for a name.
227  * @param hsa           headerSprintf args
228  * @param token         parsed fields
229  * @param name          name to find
230  * @return              0 on success, 1 on not found
231  */
232 static int findTag(headerSprintfArgs hsa, sprintfToken token, const char * name)
233 {
234     const char *tagname = name;
235     sprintfTag stag = (token->type == PTOK_COND
236         ? &token->u.cond.tag : &token->u.tag);
237
238     stag->fmt = NULL;
239     stag->tag = -1;
240
241     if (rstreq(tagname, "*")) {
242         stag->tag = -2;
243         goto bingo;
244     }
245
246     if (rstreqn("RPMTAG_", tagname, sizeof("RPMTAG_")-1)) {
247         tagname += sizeof("RPMTAG");
248     }
249
250     /* Search tag names. */
251     stag->tag = rpmTagGetValue(tagname);
252     if (stag->tag != RPMTAG_NOT_FOUND)
253         goto bingo;
254
255     return 1;
256
257 bingo:
258     /* Search extensions for specific format. */
259     if (stag->type != NULL)
260         stag->fmt = rpmHeaderFormatFuncByName(stag->type);
261
262     return stag->fmt ? 0 : 1;
263 }
264
265 /* forward ref */
266 /**
267  * Parse an expression.
268  * @param hsa           headerSprintf args
269  * @param token         token
270  * @param str           string
271  * @param[out] *endPtr
272  * @return              0 on success
273  */
274 static int parseExpression(headerSprintfArgs hsa, sprintfToken token,
275                 char * str,char ** endPtr);
276
277 /**
278  * Parse a headerSprintf term.
279  * @param hsa           headerSprintf args
280  * @param str
281  * @retval *formatPtr
282  * @retval *numTokensPtr
283  * @retval *endPtr
284  * @param state
285  * @return              0 on success
286  */
287 static int parseFormat(headerSprintfArgs hsa, char * str,
288         sprintfToken * formatPtr,int * numTokensPtr,
289         char ** endPtr, int state)
290 {
291     char * chptr, * start, * next, * dst;
292     sprintfToken format;
293     sprintfToken token;
294     int numTokens;
295     int done = 0;
296
297     /* upper limit on number of individual formats */
298     numTokens = 0;
299     if (str != NULL)
300     for (chptr = str; *chptr != '\0'; chptr++)
301         if (*chptr == '%' || *chptr == '[') numTokens++;
302     numTokens = numTokens * 2 + 1;
303
304     format = xcalloc(numTokens, sizeof(*format));
305     if (endPtr) *endPtr = NULL;
306
307     dst = start = str;
308     numTokens = 0;
309     token = NULL;
310     if (start != NULL)
311     while (*start != '\0') {
312         switch (*start) {
313         case '%':
314             /* handle %% */
315             if (*(start + 1) == '%') {
316                 if (token == NULL || token->type != PTOK_STRING) {
317                     token = format + numTokens++;
318                     token->type = PTOK_STRING;
319                     dst = token->u.string.string = start;
320                 }
321                 start++;
322                 *dst++ = *start++;
323                 break;
324             } 
325
326             token = format + numTokens++;
327             *dst++ = '\0';
328             start++;
329
330             if (*start == '|') {
331                 char * newEnd;
332
333                 start++;
334                 if (parseExpression(hsa, token, start, &newEnd)) {
335                     goto errxit;
336                 }
337                 start = newEnd;
338                 break;
339             }
340
341             token->u.tag.format = start;
342             token->u.tag.justOne = 0;
343
344             chptr = start;
345             while (*chptr && *chptr != '{' && *chptr != '%') chptr++;
346             if (!*chptr || *chptr == '%') {
347                 hsa->errmsg = _("missing { after %");
348                 goto errxit;
349             }
350
351             *chptr++ = '\0';
352
353             while (start < chptr) {
354                 start++;
355             }
356
357             if (*start == '=') {
358                 token->u.tag.justOne = 1;
359                 start++;
360             } else if (*start == '#') {
361                 token->u.tag.justOne = 1;
362                 token->u.tag.type = "arraysize";
363                 start++;
364             }
365
366             dst = next = start;
367             while (*next && *next != '}') next++;
368             if (!*next) {
369                 hsa->errmsg = _("missing } after %{");
370                 goto errxit;
371             }
372             *next++ = '\0';
373
374             chptr = start;
375             while (*chptr && *chptr != ':') chptr++;
376
377             if (*chptr != '\0') {
378                 *chptr++ = '\0';
379                 if (!*chptr) {
380                     hsa->errmsg = _("empty tag format");
381                     goto errxit;
382                 }
383                 token->u.tag.type = chptr;
384             } 
385             /* default to string conversion if no formats found by now */
386             if (!token->u.tag.type) {
387                 token->u.tag.type = "string";
388             }
389             
390             if (!*start) {
391                 hsa->errmsg = _("empty tag name");
392                 goto errxit;
393             }
394
395             token->type = PTOK_TAG;
396
397             if (findTag(hsa, token, start)) {
398                 hsa->errmsg = _("unknown tag");
399                 goto errxit;
400             }
401
402             start = next;
403             break;
404
405         case '[':
406             *dst++ = '\0';
407             *start++ = '\0';
408             token = format + numTokens++;
409
410             if (parseFormat(hsa, start,
411                             &token->u.array.format,
412                             &token->u.array.numTokens,
413                             &start, PARSER_IN_ARRAY)) {
414                 goto errxit;
415             }
416
417             if (!start) {
418                 hsa->errmsg = _("] expected at end of array");
419                 goto errxit;
420             }
421
422             dst = start;
423
424             token->type = PTOK_ARRAY;
425
426             break;
427
428         case ']':
429             if (state != PARSER_IN_ARRAY) {
430                 hsa->errmsg = _("unexpected ]");
431                 goto errxit;
432             }
433             *start++ = '\0';
434             if (endPtr) *endPtr = start;
435             done = 1;
436             break;
437
438         case '}':
439             if (state != PARSER_IN_EXPR) {
440                 hsa->errmsg = _("unexpected }");
441                 goto errxit;
442             }
443             *start++ = '\0';
444             if (endPtr) *endPtr = start;
445             done = 1;
446             break;
447
448         default:
449             if (token == NULL || token->type != PTOK_STRING) {
450                 token = format + numTokens++;
451                 token->type = PTOK_STRING;
452                 dst = token->u.string.string = start;
453             }
454
455             if (*start == '\\') {
456                 start++;
457                 *dst++ = escapedChar(*start++);
458             } else {
459                 *dst++ = *start++;
460             }
461             break;
462         }
463         if (done)
464             break;
465     }
466
467     if (dst != NULL)
468         *dst = '\0';
469
470     for (int i = 0; i < numTokens; i++) {
471         token = format + i;
472         if (token->type == PTOK_STRING)
473             token->u.string.len = strlen(token->u.string.string);
474     }
475
476     *numTokensPtr = numTokens;
477     *formatPtr = format;
478     return 0;
479
480 errxit:
481     freeFormat(format, numTokens);
482     return 1;
483 }
484
485 static int parseExpression(headerSprintfArgs hsa, sprintfToken token,
486                 char * str, char ** endPtr)
487 {
488     char * chptr;
489     char * end;
490
491     hsa->errmsg = NULL;
492     chptr = str;
493     while (*chptr && *chptr != '?') chptr++;
494
495     if (*chptr != '?') {
496         hsa->errmsg = _("? expected in expression");
497         return 1;
498     }
499
500     *chptr++ = '\0';;
501
502     if (*chptr != '{') {
503         hsa->errmsg = _("{ expected after ? in expression");
504         return 1;
505     }
506
507     chptr++;
508
509     if (parseFormat(hsa, chptr, &token->u.cond.ifFormat, 
510                     &token->u.cond.numIfTokens, &end, PARSER_IN_EXPR)) 
511         return 1;
512
513     /* XXX fix segfault on "rpm -q rpm --qf='%|NAME?{%}:{NAME}|\n'"*/
514     if (!(end && *end)) {
515         hsa->errmsg = _("} expected in expression");
516         token->u.cond.ifFormat =
517                 freeFormat(token->u.cond.ifFormat, token->u.cond.numIfTokens);
518         return 1;
519     }
520
521     chptr = end;
522     if (*chptr != ':' && *chptr != '|') {
523         hsa->errmsg = _(": expected following ? subexpression");
524         token->u.cond.ifFormat =
525                 freeFormat(token->u.cond.ifFormat, token->u.cond.numIfTokens);
526         return 1;
527     }
528
529     if (*chptr == '|') {
530         if (parseFormat(hsa, NULL, &token->u.cond.elseFormat, 
531                 &token->u.cond.numElseTokens, &end, PARSER_IN_EXPR))
532         {
533             token->u.cond.ifFormat =
534                 freeFormat(token->u.cond.ifFormat, token->u.cond.numIfTokens);
535             return 1;
536         }
537     } else {
538         chptr++;
539
540         if (*chptr != '{') {
541             hsa->errmsg = _("{ expected after : in expression");
542             token->u.cond.ifFormat =
543                 freeFormat(token->u.cond.ifFormat, token->u.cond.numIfTokens);
544             return 1;
545         }
546
547         chptr++;
548
549         if (parseFormat(hsa, chptr, &token->u.cond.elseFormat, 
550                         &token->u.cond.numElseTokens, &end, PARSER_IN_EXPR)) 
551             return 1;
552
553         /* XXX fix segfault on "rpm -q rpm --qf='%|NAME?{a}:{%}|{NAME}\n'" */
554         if (!(end && *end)) {
555             hsa->errmsg = _("} expected in expression");
556             token->u.cond.ifFormat =
557                 freeFormat(token->u.cond.ifFormat, token->u.cond.numIfTokens);
558             return 1;
559         }
560
561         chptr = end;
562         if (*chptr != '|') {
563             hsa->errmsg = _("| expected at end of expression");
564             token->u.cond.ifFormat =
565                 freeFormat(token->u.cond.ifFormat, token->u.cond.numIfTokens);
566             token->u.cond.elseFormat =
567                 freeFormat(token->u.cond.elseFormat, token->u.cond.numElseTokens);
568             return 1;
569         }
570     }
571         
572     chptr++;
573
574     *endPtr = chptr;
575
576     token->type = PTOK_COND;
577
578     (void) findTag(hsa, token, str);
579
580     return 0;
581 }
582
583 static rpmtd getCached(rpmtd *cache, rpmTag tag)
584 {
585     rpmtd td = NULL;
586
587     if (tag >= RPMTAG_HEADERIMAGE && tag < RPMTAG_FIRSTFREE_TAG && cache[tag]) {
588         td = cache[tag];
589     }
590     return td;
591 }
592
593 /**
594  * Do headerGet() just once for given tag, cache results.
595  * @param hsa           headerSprintf args
596  * @param tag
597  * @retval *typeptr
598  * @retval *data
599  * @retval *countptr
600  * @return              1 on success, 0 on failure
601  */
602 static rpmtd getData(headerSprintfArgs hsa, rpmTag tag)
603 {
604     rpmtd td = NULL;
605
606     if (!(td = getCached(hsa->cache, tag))) {
607         td = rpmtdNew();
608         if (!headerGet(hsa->h, tag, td, hsa->hgflags)) {
609             rpmtdFree(td);
610             return NULL;
611         }
612         hsa->cache[tag] = td;
613     }
614
615     return td;
616 }
617
618 /**
619  * formatValue
620  * @param hsa           headerSprintf args
621  * @param tag
622  * @param element
623  * @return              end of formatted string (NULL on error)
624  */
625 static char * formatValue(headerSprintfArgs hsa, sprintfTag tag, int element)
626 {
627     char * val = NULL;
628     size_t need = 0;
629     char * t, * te;
630     char buf[20];
631     rpmtd td;
632
633     memset(buf, 0, sizeof(buf));
634     if ((td = getData(hsa, tag->tag))) {
635         td->ix = element; /* Ick, use iterators instead */
636         stpcpy(stpcpy(buf, "%"), tag->format);
637         val = tag->fmt(td, buf);
638     } else {
639         stpcpy(buf, "%s");
640         val = xstrdup("(none)");
641     }
642
643     need = strlen(val);
644
645     if (val && need > 0) {
646         t = hsaReserve(hsa, need);
647         te = stpcpy(t, val);
648         hsa->vallen += (te - t);
649     }
650     free(val);
651
652     return (hsa->val + hsa->vallen);
653 }
654
655 /**
656  * Format a single headerSprintf item.
657  * @param hsa           headerSprintf args
658  * @param token
659  * @param element
660  * @return              end of formatted string (NULL on error)
661  */
662 static char * singleSprintf(headerSprintfArgs hsa, sprintfToken token,
663                 int element)
664 {
665     char * t, * te;
666     int i, j, found;
667     rpm_count_t count, numElements;
668     sprintfToken spft;
669     int condNumFormats;
670     size_t need;
671
672     /* we assume the token and header have been validated already! */
673
674     switch (token->type) {
675     case PTOK_NONE:
676         break;
677
678     case PTOK_STRING:
679         need = token->u.string.len;
680         if (need == 0) break;
681         t = hsaReserve(hsa, need);
682         te = stpcpy(t, token->u.string.string);
683         hsa->vallen += (te - t);
684         break;
685
686     case PTOK_TAG:
687         t = hsa->val + hsa->vallen;
688         te = formatValue(hsa, &token->u.tag,
689                         (token->u.tag.justOne ? 0 : element));
690         if (te == NULL)
691             return NULL;
692         break;
693
694     case PTOK_COND:
695         if (getData(hsa, token->u.cond.tag.tag) ||
696                       headerIsEntry(hsa->h, token->u.cond.tag.tag)) {
697             spft = token->u.cond.ifFormat;
698             condNumFormats = token->u.cond.numIfTokens;
699         } else {
700             spft = token->u.cond.elseFormat;
701             condNumFormats = token->u.cond.numElseTokens;
702         }
703
704         need = condNumFormats * 20;
705         if (spft == NULL || need == 0) break;
706
707         t = hsaReserve(hsa, need);
708         for (i = 0; i < condNumFormats; i++, spft++) {
709             te = singleSprintf(hsa, spft, element);
710             if (te == NULL)
711                 return NULL;
712         }
713         break;
714
715     case PTOK_ARRAY:
716         numElements = 0;
717         found = 0;
718         spft = token->u.array.format;
719         for (i = 0; i < token->u.array.numTokens; i++, spft++)
720         {
721             rpmtd td = NULL;
722             if (spft->type != PTOK_TAG ||
723                 spft->u.tag.justOne) continue;
724
725             if (!(td = getData(hsa, spft->u.tag.tag))) {
726                 continue;
727             }
728
729             found = 1;
730             count = rpmtdCount(td);
731
732             if (numElements > 1 && count != numElements)
733             switch (td->type) {
734             default:
735                 hsa->errmsg =
736                         _("array iterator used with different sized arrays");
737                 return NULL;
738                 break;
739             case RPM_BIN_TYPE:
740             case RPM_STRING_TYPE:
741                 break;
742             }
743             if (count > numElements)
744                 numElements = count;
745         }
746
747         if (found) {
748             int isxml;
749
750             need = numElements * token->u.array.numTokens * 10;
751             if (need == 0) break;
752
753             spft = token->u.array.format;
754             isxml = (spft->type == PTOK_TAG && spft->u.tag.type != NULL &&
755                     rstreq(spft->u.tag.type, "xml"));
756
757             if (isxml) {
758                 const char * tagN = rpmTagGetName(spft->u.tag.tag);
759
760                 need = sizeof("  <rpmTag name=\"\">\n") - 1;
761                 if (tagN != NULL)
762                     need += strlen(tagN);
763                 t = hsaReserve(hsa, need);
764                 te = stpcpy(t, "  <rpmTag name=\"");
765                 if (tagN != NULL)
766                     te = stpcpy(te, tagN);
767                 te = stpcpy(te, "\">\n");
768                 hsa->vallen += (te - t);
769             }
770
771             t = hsaReserve(hsa, need);
772             for (j = 0; j < numElements; j++) {
773                 spft = token->u.array.format;
774                 for (i = 0; i < token->u.array.numTokens; i++, spft++) {
775                     te = singleSprintf(hsa, spft, j);
776                     if (te == NULL)
777                         return NULL;
778                 }
779             }
780
781             if (isxml) {
782                 need = sizeof("  </rpmTag>\n") - 1;
783                 t = hsaReserve(hsa, need);
784                 te = stpcpy(t, "  </rpmTag>\n");
785                 hsa->vallen += (te - t);
786             }
787
788         }
789         break;
790     }
791
792     return (hsa->val + hsa->vallen);
793 }
794
795 /**
796  * Create tag data cache.
797  * This allocates much more space than necessary but playing it
798  * simple and stupid for now.
799  */
800 static rpmtd *cacheCreate(void)
801 {
802     rpmtd *cache = xcalloc(RPMTAG_FIRSTFREE_TAG, sizeof(*cache));
803     return cache;
804 }
805
806 /**
807  * Free tag data cache contents and destroy cache.
808  */
809 static void *cacheFree(rpmtd *cache)
810 {
811     rpmtd *td = cache;
812     for (int i = 0; i < RPMTAG_FIRSTFREE_TAG; i++, td++) {
813         if (*td) {
814             rpmtdFreeData(*td);
815             rpmtdFree(*td);
816         }
817     }
818     free(cache);
819     return NULL;
820 }
821
822 char * headerFormat(Header h, const char * fmt, errmsg_t * errmsg) 
823 {
824     struct headerSprintfArgs_s hsa;
825     sprintfToken nextfmt;
826     sprintfTag tag;
827     char * t, * te;
828     int isxml;
829     size_t need;
830  
831     memset(&hsa, 0, sizeof(hsa));
832     hsa.h = headerLink(h);
833     hsa.fmt = xstrdup(fmt);
834     hsa.errmsg = NULL;
835
836     if (parseFormat(&hsa, hsa.fmt, &hsa.format, &hsa.numTokens, NULL, PARSER_BEGIN))
837         goto exit;
838
839     hsa.cache = cacheCreate();
840     hsa.val = xstrdup("");
841
842     tag =
843         (hsa.format->type == PTOK_TAG
844             ? &hsa.format->u.tag :
845         (hsa.format->type == PTOK_ARRAY
846             ? &hsa.format->u.array.format->u.tag :
847         NULL));
848     isxml = (tag != NULL && tag->tag == -2 && tag->type != NULL && rstreq(tag->type, "xml"));
849
850     if (isxml) {
851         need = sizeof("<rpmHeader>\n") - 1;
852         t = hsaReserve(&hsa, need);
853         te = stpcpy(t, "<rpmHeader>\n");
854         hsa.vallen += (te - t);
855     }
856
857     hsaInit(&hsa);
858     while ((nextfmt = hsaNext(&hsa)) != NULL) {
859         te = singleSprintf(&hsa, nextfmt, 0);
860         if (te == NULL) {
861             hsa.val = _free(hsa.val);
862             break;
863         }
864     }
865     hsaFini(&hsa);
866
867     if (isxml) {
868         need = sizeof("</rpmHeader>\n") - 1;
869         t = hsaReserve(&hsa, need);
870         te = stpcpy(t, "</rpmHeader>\n");
871         hsa.vallen += (te - t);
872     }
873
874     if (hsa.val != NULL && hsa.vallen < hsa.alloced)
875         hsa.val = xrealloc(hsa.val, hsa.vallen+1);      
876
877     hsa.cache = cacheFree(hsa.cache);
878     hsa.format = freeFormat(hsa.format, hsa.numTokens);
879
880 exit:
881     if (errmsg)
882         *errmsg = hsa.errmsg;
883     hsa.h = headerFree(hsa.h);
884     hsa.fmt = _free(hsa.fmt);
885     return hsa.val;
886 }
887