Drop unused variables from formatValues()
[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     int countBuf;
641     struct rpmtd_s tmp;
642     rpmtd td;
643
644     memset(buf, 0, sizeof(buf));
645     if (!(td = getData(hsa, tag->tag))) {
646         tmp.count = 1;
647         tmp.type = RPM_STRING_TYPE;
648         tmp.data = "(none)";
649         element = 0;
650         td = &tmp;      
651     }
652
653     if (tag->arrayCount) {
654         countBuf = td->count;
655         tmp.data = &countBuf;
656         tmp.count = 1;
657         tmp.type = RPM_INT32_TYPE;
658         element = 0;
659         td = &tmp;
660     }
661     td->ix = element; /* Ick, use iterators instead */
662
663     (void) stpcpy( stpcpy(buf, "%"), tag->format);
664
665     val = tag->fmt(td, buf);
666     need = strlen(val);
667
668     if (val && need > 0) {
669         t = hsaReserve(hsa, need);
670         te = stpcpy(t, val);
671         hsa->vallen += (te - t);
672         val = _free(val);
673     }
674
675     return (hsa->val + hsa->vallen);
676 }
677
678 /**
679  * Format a single headerSprintf item.
680  * @param hsa           headerSprintf args
681  * @param token
682  * @param element
683  * @return              end of formatted string (NULL on error)
684  */
685 static char * singleSprintf(headerSprintfArgs hsa, sprintfToken token,
686                 int element)
687 {
688     char * t, * te;
689     int i, j, found;
690     rpm_count_t count, numElements;
691     sprintfToken spft;
692     int condNumFormats;
693     size_t need;
694
695     /* we assume the token and header have been validated already! */
696
697     switch (token->type) {
698     case PTOK_NONE:
699         break;
700
701     case PTOK_STRING:
702         need = token->u.string.len;
703         if (need == 0) break;
704         t = hsaReserve(hsa, need);
705         te = stpcpy(t, token->u.string.string);
706         hsa->vallen += (te - t);
707         break;
708
709     case PTOK_TAG:
710         t = hsa->val + hsa->vallen;
711         te = formatValue(hsa, &token->u.tag,
712                         (token->u.tag.justOne ? 0 : element));
713         if (te == NULL)
714             return NULL;
715         break;
716
717     case PTOK_COND:
718         if (getCached(hsa->cache, token->u.cond.tag.tag) ||
719                       headerIsEntry(hsa->h, token->u.cond.tag.tag)) {
720             spft = token->u.cond.ifFormat;
721             condNumFormats = token->u.cond.numIfTokens;
722         } else {
723             spft = token->u.cond.elseFormat;
724             condNumFormats = token->u.cond.numElseTokens;
725         }
726
727         need = condNumFormats * 20;
728         if (spft == NULL || need == 0) break;
729
730         t = hsaReserve(hsa, need);
731         for (i = 0; i < condNumFormats; i++, spft++) {
732             te = singleSprintf(hsa, spft, element);
733             if (te == NULL)
734                 return NULL;
735         }
736         break;
737
738     case PTOK_ARRAY:
739         numElements = 0;
740         found = 0;
741         spft = token->u.array.format;
742         for (i = 0; i < token->u.array.numTokens; i++, spft++)
743         {
744             rpmtd td = NULL;
745             if (spft->type != PTOK_TAG ||
746                 spft->u.tag.arrayCount ||
747                 spft->u.tag.justOne) continue;
748
749             if (!(td = getData(hsa, spft->u.tag.tag))) {
750                 continue;
751             }
752
753             found = 1;
754
755             if (td->type == RPM_BIN_TYPE) {
756                 count = 1;      /* XXX count abused as no. of bytes. */
757             } else {
758                 count = td->count;
759             }
760
761             if (numElements > 1 && count != numElements)
762             switch (td->type) {
763             default:
764                 hsa->errmsg =
765                         _("array iterator used with different sized arrays");
766                 return NULL;
767                 break;
768             case RPM_BIN_TYPE:
769             case RPM_STRING_TYPE:
770                 break;
771             }
772             if (count > numElements)
773                 numElements = count;
774         }
775
776         if (! found) {
777             need = sizeof("(none)") - 1;
778             t = hsaReserve(hsa, need);
779             te = stpcpy(t, "(none)");
780             hsa->vallen += (te - t);
781         } else {
782             int isxml;
783
784             need = numElements * token->u.array.numTokens * 10;
785             if (need == 0) break;
786
787             spft = token->u.array.format;
788             isxml = (spft->type == PTOK_TAG && spft->u.tag.type != NULL &&
789                 !strcmp(spft->u.tag.type, "xml"));
790
791             if (isxml) {
792                 const char * tagN = rpmTagGetName(spft->u.tag.tag);
793
794                 need = sizeof("  <rpmTag name=\"\">\n") - 1;
795                 if (tagN != NULL)
796                     need += strlen(tagN);
797                 t = hsaReserve(hsa, need);
798                 te = stpcpy(t, "  <rpmTag name=\"");
799                 if (tagN != NULL)
800                     te = stpcpy(te, tagN);
801                 te = stpcpy(te, "\">\n");
802                 hsa->vallen += (te - t);
803             }
804
805             t = hsaReserve(hsa, need);
806             for (j = 0; j < numElements; j++) {
807                 spft = token->u.array.format;
808                 for (i = 0; i < token->u.array.numTokens; i++, spft++) {
809                     te = singleSprintf(hsa, spft, j);
810                     if (te == NULL)
811                         return NULL;
812                 }
813             }
814
815             if (isxml) {
816                 need = sizeof("  </rpmTag>\n") - 1;
817                 t = hsaReserve(hsa, need);
818                 te = stpcpy(t, "  </rpmTag>\n");
819                 hsa->vallen += (te - t);
820             }
821
822         }
823         break;
824     }
825
826     return (hsa->val + hsa->vallen);
827 }
828
829 /**
830  * Create tag data cache.
831  * This allocates much more space than necessary but playing it
832  * simple and stupid for now.
833  */
834 static rpmtd *cacheCreate(void)
835 {
836     rpmtd *cache = xcalloc(RPMTAG_FIRSTFREE_TAG, sizeof(*cache));
837     return cache;
838 }
839
840 /**
841  * Free tag data cache contents and destroy cache.
842  */
843 static void *cacheFree(rpmtd *cache)
844 {
845     rpmtd *td = cache;
846     for (int i = 0; i < RPMTAG_FIRSTFREE_TAG; i++, td++) {
847         if (*td) {
848             rpmtdFreeData(*td);
849             rpmtdFree(*td);
850         }
851     }
852     free(cache);
853     return NULL;
854 }
855
856 char * headerFormat(Header h, const char * fmt, errmsg_t * errmsg) 
857 {
858     struct headerSprintfArgs_s hsa;
859     sprintfToken nextfmt;
860     sprintfTag tag;
861     char * t, * te;
862     int isxml;
863     size_t need;
864  
865     memset(&hsa, 0, sizeof(hsa));
866     hsa.h = headerLink(h);
867     hsa.fmt = xstrdup(fmt);
868     hsa.errmsg = NULL;
869
870     if (parseFormat(&hsa, hsa.fmt, &hsa.format, &hsa.numTokens, NULL, PARSER_BEGIN))
871         goto exit;
872
873     hsa.cache = cacheCreate();
874     hsa.val = xstrdup("");
875
876     tag =
877         (hsa.format->type == PTOK_TAG
878             ? &hsa.format->u.tag :
879         (hsa.format->type == PTOK_ARRAY
880             ? &hsa.format->u.array.format->u.tag :
881         NULL));
882     isxml = (tag != NULL && tag->tag == -2 && tag->type != NULL && !strcmp(tag->type, "xml"));
883
884     if (isxml) {
885         need = sizeof("<rpmHeader>\n") - 1;
886         t = hsaReserve(&hsa, need);
887         te = stpcpy(t, "<rpmHeader>\n");
888         hsa.vallen += (te - t);
889     }
890
891     hsaInit(&hsa);
892     while ((nextfmt = hsaNext(&hsa)) != NULL) {
893         te = singleSprintf(&hsa, nextfmt, 0);
894         if (te == NULL) {
895             hsa.val = _free(hsa.val);
896             break;
897         }
898     }
899     hsaFini(&hsa);
900
901     if (isxml) {
902         need = sizeof("</rpmHeader>\n") - 1;
903         t = hsaReserve(&hsa, need);
904         te = stpcpy(t, "</rpmHeader>\n");
905         hsa.vallen += (te - t);
906     }
907
908     if (hsa.val != NULL && hsa.vallen < hsa.alloced)
909         hsa.val = xrealloc(hsa.val, hsa.vallen+1);      
910
911     hsa.cache = cacheFree(hsa.cache);
912     hsa.format = freeFormat(hsa.format, hsa.numTokens);
913
914 exit:
915     if (errmsg)
916         *errmsg = hsa.errmsg;
917     hsa.h = headerFree(hsa.h);
918     hsa.fmt = _free(hsa.fmt);
919     return hsa.val;
920 }
921
922 char * headerSprintf(Header h, const char * fmt,
923                      void * tbltags,
924                      void * extensions,
925                      errmsg_t * errmsg)
926 {
927     return headerFormat(h, fmt, errmsg);
928 }
929