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