2 * \file lib/headerfmt.c
7 #include <rpm/header.h>
8 #include <rpm/rpmtag.h>
9 #include <rpm/rpmstring.h>
10 #include <rpm/rpmpgp.h>
14 #define PARSER_BEGIN 0
15 #define PARSER_IN_ARRAY 1
16 #define PARSER_IN_EXPR 2
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.
23 * @param td tag data container
25 * @return formatted string
27 typedef char * (*headerTagFormatFunction) (rpmtd td, char * formatPrefix);
29 extern void *rpmHeaderFormatFuncByName(const char *fmt);
33 typedef struct sprintfTag_s * sprintfTag;
35 headerTagFormatFunction fmt;
45 typedef struct sprintfToken_s * sprintfToken;
46 struct sprintfToken_s {
55 struct sprintfTag_s tag; /*!< PTOK_TAG */
60 } array; /*!< PTOK_ARRAY */
64 } string; /*!< PTOK_STRING */
66 sprintfToken ifFormat;
68 sprintfToken elseFormat;
70 struct sprintfTag_s tag;
71 } cond; /*!< PTOK_COND */
77 typedef struct headerSprintfArgs_s {
89 } * headerSprintfArgs;
92 static char escapedChar(const char 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';
107 * Destroy headerSprintf format array.
108 * @param format sprintf format array
109 * @param num number of elements
110 * @return NULL always
113 freeFormat( sprintfToken format, int num)
117 if (format == NULL) return NULL;
119 for (i = 0; i < num; i++) {
120 switch (format[i].type) {
122 format[i].u.array.format =
123 freeFormat(format[i].u.array.format,
124 format[i].u.array.numTokens);
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);
141 format = _free(format);
146 * Initialize an hsa iteration.
147 * @param hsa headerSprintf args
149 static void hsaInit(headerSprintfArgs hsa)
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 :
159 if (tag != NULL && tag->tag == -2)
160 hsa->hi = headerInitIterator(hsa->h);
164 * Return next hsa iteration item.
165 * @param hsa headerSprintf args
166 * @return next sprintfToken (or NULL)
168 static sprintfToken hsaNext(headerSprintfArgs hsa)
170 sprintfToken fmt = NULL;
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 :
178 if (hsa->i >= 0 && hsa->i < hsa->numTokens) {
179 fmt = hsa->format + hsa->i;
180 if (hsa->hi == NULL) {
187 if (!headerNextIterator(hsa->hi, &tagno, &type, NULL, &count))
197 * Finish an hsa iteration.
198 * @param hsa headerSprintf args
200 static void hsaFini(headerSprintfArgs hsa)
202 hsa->hi = headerFreeIterator(hsa->hi);
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
212 static char * hsaReserve(headerSprintfArgs hsa, size_t need)
214 if ((hsa->vallen + need) >= hsa->alloced) {
215 if (hsa->alloced <= need)
216 hsa->alloced += need;
218 hsa->val = xrealloc(hsa->val, hsa->alloced+1);
220 return hsa->val + hsa->vallen;
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
230 static int findTag(headerSprintfArgs hsa, sprintfToken token, const char * name)
232 const char *tagname = name;
233 sprintfTag stag = (token->type == PTOK_COND
234 ? &token->u.cond.tag : &token->u.tag);
239 if (!strcmp(tagname, "*")) {
244 if (strncmp("RPMTAG_", tagname, sizeof("RPMTAG_")-1) == 0) {
245 tagname += sizeof("RPMTAG");
248 /* Search tag names. */
249 stag->tag = rpmTagGetValue(tagname);
250 if (stag->tag != RPMTAG_NOT_FOUND)
256 /* Search extensions for specific format. */
257 if (stag->type != NULL)
258 stag->fmt = rpmHeaderFormatFuncByName(stag->type);
265 * Parse an expression.
266 * @param hsa headerSprintf args
269 * @param[out] *endPtr
270 * @return 0 on success
272 static int parseExpression(headerSprintfArgs hsa, sprintfToken token,
273 char * str,char ** endPtr);
276 * Parse a headerSprintf term.
277 * @param hsa headerSprintf args
280 * @retval *numTokensPtr
283 * @return 0 on success
285 static int parseFormat(headerSprintfArgs hsa, char * str,
286 sprintfToken * formatPtr,int * numTokensPtr,
287 char ** endPtr, int state)
289 char * chptr, * start, * next, * dst;
296 /* upper limit on number of individual formats */
299 for (chptr = str; *chptr != '\0'; chptr++)
300 if (*chptr == '%') numTokens++;
301 numTokens = numTokens * 2 + 1;
303 format = xcalloc(numTokens, sizeof(*format));
304 if (endPtr) *endPtr = NULL;
306 /* LCL: can't detect done termination */
311 while (*start != '\0') {
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;
326 token = format + numTokens++;
334 if (parseExpression(hsa, token, start, &newEnd))
336 format = freeFormat(format, numTokens);
343 token->u.tag.format = start;
344 token->u.tag.justOne = 0;
345 token->u.tag.arrayCount = 0;
348 while (*chptr && *chptr != '{' && *chptr != '%') chptr++;
349 if (!*chptr || *chptr == '%') {
350 hsa->errmsg = _("missing { after %");
351 format = freeFormat(format, numTokens);
357 while (start < chptr) {
362 token->u.tag.justOne = 1;
364 } else if (*start == '#') {
365 token->u.tag.justOne = 1;
366 token->u.tag.arrayCount = 1;
371 while (*next && *next != '}') next++;
373 hsa->errmsg = _("missing } after %{");
374 format = freeFormat(format, numTokens);
380 while (*chptr && *chptr != ':') chptr++;
382 if (*chptr != '\0') {
385 hsa->errmsg = _("empty tag format");
386 format = freeFormat(format, numTokens);
389 token->u.tag.type = chptr;
391 token->u.tag.type = "string";
395 hsa->errmsg = _("empty tag name");
396 format = freeFormat(format, numTokens);
401 token->type = PTOK_TAG;
403 if (findTag(hsa, token, start)) {
404 hsa->errmsg = _("unknown tag");
405 format = freeFormat(format, numTokens);
415 token = format + numTokens++;
417 if (parseFormat(hsa, start,
418 &token->u.array.format,
419 &token->u.array.numTokens,
420 &start, PARSER_IN_ARRAY))
422 format = freeFormat(format, numTokens);
427 hsa->errmsg = _("] expected at end of array");
428 format = freeFormat(format, numTokens);
434 token->type = PTOK_ARRAY;
439 if (state != PARSER_IN_ARRAY) {
440 hsa->errmsg = _("unexpected ]");
441 format = freeFormat(format, numTokens);
445 if (endPtr) *endPtr = start;
450 if (state != PARSER_IN_EXPR) {
451 hsa->errmsg = _("unexpected }");
452 format = freeFormat(format, numTokens);
456 if (endPtr) *endPtr = start;
461 if (token == NULL || token->type != PTOK_STRING) {
462 token = format + numTokens++;
463 token->type = PTOK_STRING;
464 dst = token->u.string.string = start;
467 if (*start == '\\') {
469 *dst++ = escapedChar(*start++);
482 for (i = 0; i < numTokens; i++) {
484 if (token->type == PTOK_STRING)
485 token->u.string.len = strlen(token->u.string.string);
488 *numTokensPtr = numTokens;
494 static int parseExpression(headerSprintfArgs hsa, sprintfToken token,
495 char * str, char ** endPtr)
502 while (*chptr && *chptr != '?') chptr++;
505 hsa->errmsg = _("? expected in expression");
512 hsa->errmsg = _("{ expected after ? in expression");
518 if (parseFormat(hsa, chptr, &token->u.cond.ifFormat,
519 &token->u.cond.numIfTokens, &end, PARSER_IN_EXPR))
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);
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);
539 if (parseFormat(hsa, NULL, &token->u.cond.elseFormat,
540 &token->u.cond.numElseTokens, &end, PARSER_IN_EXPR))
542 token->u.cond.ifFormat =
543 freeFormat(token->u.cond.ifFormat, token->u.cond.numIfTokens);
550 hsa->errmsg = _("{ expected after : in expression");
551 token->u.cond.ifFormat =
552 freeFormat(token->u.cond.ifFormat, token->u.cond.numIfTokens);
558 if (parseFormat(hsa, chptr, &token->u.cond.elseFormat,
559 &token->u.cond.numElseTokens, &end, PARSER_IN_EXPR))
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);
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);
585 token->type = PTOK_COND;
587 (void) findTag(hsa, token, str);
592 static rpmtd getCached(rpmtd *cache, rpmTag tag)
596 if (tag >= HEADER_IMAGE && tag < RPMTAG_FIRSTFREE_TAG && cache[tag]) {
603 * Do headerGet() just once for given tag, cache results.
604 * @param hsa headerSprintf args
609 * @return 1 on success, 0 on failure
611 static rpmtd getData(headerSprintfArgs hsa, rpmTag tag)
615 if (!(td = getCached(hsa->cache, tag))) {
617 if (!headerGet(hsa->h, tag, td, HEADERGET_EXT)) {
621 hsa->cache[tag] = td;
629 * @param hsa headerSprintf args
632 * @return end of formatted string (NULL on error)
634 static char * formatValue(headerSprintfArgs hsa, sprintfTag tag, int element)
646 memset(buf, 0, sizeof(buf));
647 if (!(td = getData(hsa, tag->tag))) {
649 tmp.type = RPM_STRING_TYPE;
655 if (tag->arrayCount) {
656 countBuf = td->count;
657 tmp.data = &countBuf;
659 tmp.type = RPM_INT32_TYPE;
663 td->ix = element; /* Ick, use iterators instead */
665 (void) stpcpy( stpcpy(buf, "%"), tag->format);
667 val = tag->fmt(td, buf);
670 if (val && need > 0) {
671 t = hsaReserve(hsa, need);
673 hsa->vallen += (te - t);
677 return (hsa->val + hsa->vallen);
681 * Format a single headerSprintf item.
682 * @param hsa headerSprintf args
685 * @return end of formatted string (NULL on error)
687 static char * singleSprintf(headerSprintfArgs hsa, sprintfToken token,
692 rpm_count_t count, numElements;
697 /* we assume the token and header have been validated already! */
699 switch (token->type) {
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);
712 t = hsa->val + hsa->vallen;
713 te = formatValue(hsa, &token->u.tag,
714 (token->u.tag.justOne ? 0 : element));
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;
725 spft = token->u.cond.elseFormat;
726 condNumFormats = token->u.cond.numElseTokens;
729 need = condNumFormats * 20;
730 if (spft == NULL || need == 0) break;
732 t = hsaReserve(hsa, need);
733 for (i = 0; i < condNumFormats; i++, spft++) {
734 te = singleSprintf(hsa, spft, element);
743 spft = token->u.array.format;
744 for (i = 0; i < token->u.array.numTokens; i++, spft++)
747 if (spft->type != PTOK_TAG ||
748 spft->u.tag.arrayCount ||
749 spft->u.tag.justOne) continue;
751 if (!(td = getData(hsa, spft->u.tag.tag))) {
757 if (td->type == RPM_BIN_TYPE) {
758 count = 1; /* XXX count abused as no. of bytes. */
763 if (numElements > 1 && count != numElements)
767 _("array iterator used with different sized arrays");
771 case RPM_STRING_TYPE:
774 if (count > numElements)
779 need = sizeof("(none)") - 1;
780 t = hsaReserve(hsa, need);
781 te = stpcpy(t, "(none)");
782 hsa->vallen += (te - t);
786 need = numElements * token->u.array.numTokens * 10;
787 if (need == 0) break;
789 spft = token->u.array.format;
790 isxml = (spft->type == PTOK_TAG && spft->u.tag.type != NULL &&
791 !strcmp(spft->u.tag.type, "xml"));
794 const char * tagN = rpmTagGetName(spft->u.tag.tag);
796 need = sizeof(" <rpmTag name=\"\">\n") - 1;
798 need += strlen(tagN);
799 t = hsaReserve(hsa, need);
800 te = stpcpy(t, " <rpmTag name=\"");
802 te = stpcpy(te, tagN);
803 te = stpcpy(te, "\">\n");
804 hsa->vallen += (te - t);
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);
818 need = sizeof(" </rpmTag>\n") - 1;
819 t = hsaReserve(hsa, need);
820 te = stpcpy(t, " </rpmTag>\n");
821 hsa->vallen += (te - t);
828 return (hsa->val + hsa->vallen);
832 * Create tag data cache.
833 * This allocates much more space than necessary but playing it
834 * simple and stupid for now.
836 static rpmtd *cacheCreate(void)
838 rpmtd *cache = xcalloc(RPMTAG_FIRSTFREE_TAG, sizeof(*cache));
843 * Free tag data cache contents and destroy cache.
845 static void *cacheFree(rpmtd *cache)
848 for (int i = 0; i < RPMTAG_FIRSTFREE_TAG; i++, td++) {
858 char * headerFormat(Header h, const char * fmt, errmsg_t * errmsg)
860 struct headerSprintfArgs_s hsa;
861 sprintfToken nextfmt;
867 memset(&hsa, 0, sizeof(hsa));
868 hsa.h = headerLink(h);
869 hsa.fmt = xstrdup(fmt);
872 if (parseFormat(&hsa, hsa.fmt, &hsa.format, &hsa.numTokens, NULL, PARSER_BEGIN))
875 hsa.cache = cacheCreate();
876 hsa.val = xstrdup("");
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 :
884 isxml = (tag != NULL && tag->tag == -2 && tag->type != NULL && !strcmp(tag->type, "xml"));
887 need = sizeof("<rpmHeader>\n") - 1;
888 t = hsaReserve(&hsa, need);
889 te = stpcpy(t, "<rpmHeader>\n");
890 hsa.vallen += (te - t);
894 while ((nextfmt = hsaNext(&hsa)) != NULL) {
895 te = singleSprintf(&hsa, nextfmt, 0);
897 hsa.val = _free(hsa.val);
904 need = sizeof("</rpmHeader>\n") - 1;
905 t = hsaReserve(&hsa, need);
906 te = stpcpy(t, "</rpmHeader>\n");
907 hsa.vallen += (te - t);
910 if (hsa.val != NULL && hsa.vallen < hsa.alloced)
911 hsa.val = xrealloc(hsa.val, hsa.vallen+1);
913 hsa.cache = cacheFree(hsa.cache);
914 hsa.format = freeFormat(hsa.format, hsa.numTokens);
918 *errmsg = hsa.errmsg;
919 hsa.h = headerFree(hsa.h);
920 hsa.fmt = _free(hsa.fmt);
924 char * headerSprintf(Header h, const char * fmt,
929 return headerFormat(h, fmt, errmsg);