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