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