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