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;
44 typedef struct sprintfToken_s * sprintfToken;
45 struct sprintfToken_s {
54 struct sprintfTag_s tag; /*!< PTOK_TAG */
59 } array; /*!< PTOK_ARRAY */
63 } string; /*!< PTOK_STRING */
65 sprintfToken ifFormat;
67 sprintfToken elseFormat;
69 struct sprintfTag_s tag;
70 } cond; /*!< PTOK_COND */
76 typedef struct headerSprintfArgs_s {
88 headerGetFlags hgflags;
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);
161 /* Normally with bells and whistles enabled, but raw dump on iteration. */
162 hsa->hgflags = (hsa->hi == NULL) ? HEADERGET_EXT : HEADERGET_RAW;
166 * Return next hsa iteration item.
167 * @param hsa headerSprintf args
168 * @return next sprintfToken (or NULL)
170 static sprintfToken hsaNext(headerSprintfArgs hsa)
172 sprintfToken fmt = NULL;
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 :
180 if (hsa->i >= 0 && hsa->i < hsa->numTokens) {
181 fmt = hsa->format + hsa->i;
182 if (hsa->hi == NULL) {
187 /* hmm, cache the data from here too? */
188 if (!headerNext(hsa->hi, &td))
199 * Finish an hsa iteration.
200 * @param hsa headerSprintf args
202 static void hsaFini(headerSprintfArgs hsa)
204 hsa->hi = headerFreeIterator(hsa->hi);
209 * Reserve sufficient buffer space for next output value.
210 * @param hsa headerSprintf args
211 * @param need no. of bytes to reserve
212 * @return pointer to reserved space
214 static char * hsaReserve(headerSprintfArgs hsa, size_t need)
216 if ((hsa->vallen + need) >= hsa->alloced) {
217 if (hsa->alloced <= need)
218 hsa->alloced += need;
220 hsa->val = xrealloc(hsa->val, hsa->alloced+1);
222 return hsa->val + hsa->vallen;
226 * Search tags for a name.
227 * @param hsa headerSprintf args
228 * @param token parsed fields
229 * @param name name to find
230 * @return 0 on success, 1 on not found
232 static int findTag(headerSprintfArgs hsa, sprintfToken token, const char * name)
234 const char *tagname = name;
235 sprintfTag stag = (token->type == PTOK_COND
236 ? &token->u.cond.tag : &token->u.tag);
241 if (rstreq(tagname, "*")) {
246 if (rstreqn("RPMTAG_", tagname, sizeof("RPMTAG_")-1)) {
247 tagname += sizeof("RPMTAG");
250 /* Search tag names. */
251 stag->tag = rpmTagGetValue(tagname);
252 if (stag->tag != RPMTAG_NOT_FOUND)
258 /* Search extensions for specific format. */
259 if (stag->type != NULL)
260 stag->fmt = rpmHeaderFormatFuncByName(stag->type);
262 return stag->fmt ? 0 : 1;
267 * Parse an expression.
268 * @param hsa headerSprintf args
271 * @param[out] *endPtr
272 * @return 0 on success
274 static int parseExpression(headerSprintfArgs hsa, sprintfToken token,
275 char * str,char ** endPtr);
278 * Parse a headerSprintf term.
279 * @param hsa headerSprintf args
282 * @retval *numTokensPtr
285 * @return 0 on success
287 static int parseFormat(headerSprintfArgs hsa, char * str,
288 sprintfToken * formatPtr,int * numTokensPtr,
289 char ** endPtr, int state)
291 char * chptr, * start, * next, * dst;
297 /* upper limit on number of individual formats */
300 for (chptr = str; *chptr != '\0'; chptr++)
301 if (*chptr == '%' || *chptr == '[') numTokens++;
302 numTokens = numTokens * 2 + 1;
304 format = xcalloc(numTokens, sizeof(*format));
305 if (endPtr) *endPtr = NULL;
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)) {
341 token->u.tag.format = start;
342 token->u.tag.justOne = 0;
345 while (*chptr && *chptr != '{' && *chptr != '%') chptr++;
346 if (!*chptr || *chptr == '%') {
347 hsa->errmsg = _("missing { after %");
353 while (start < chptr) {
358 token->u.tag.justOne = 1;
360 } else if (*start == '#') {
361 token->u.tag.justOne = 1;
362 token->u.tag.type = "arraysize";
367 while (*next && *next != '}') next++;
369 hsa->errmsg = _("missing } after %{");
375 while (*chptr && *chptr != ':') chptr++;
377 if (*chptr != '\0') {
380 hsa->errmsg = _("empty tag format");
383 token->u.tag.type = chptr;
385 /* default to string conversion if no formats found by now */
386 if (!token->u.tag.type) {
387 token->u.tag.type = "string";
391 hsa->errmsg = _("empty tag name");
395 token->type = PTOK_TAG;
397 if (findTag(hsa, token, start)) {
398 hsa->errmsg = _("unknown tag");
408 token = format + numTokens++;
410 if (parseFormat(hsa, start,
411 &token->u.array.format,
412 &token->u.array.numTokens,
413 &start, PARSER_IN_ARRAY)) {
418 hsa->errmsg = _("] expected at end of array");
424 token->type = PTOK_ARRAY;
429 if (state != PARSER_IN_ARRAY) {
430 hsa->errmsg = _("unexpected ]");
434 if (endPtr) *endPtr = start;
439 if (state != PARSER_IN_EXPR) {
440 hsa->errmsg = _("unexpected }");
444 if (endPtr) *endPtr = start;
449 if (token == NULL || token->type != PTOK_STRING) {
450 token = format + numTokens++;
451 token->type = PTOK_STRING;
452 dst = token->u.string.string = start;
455 if (*start == '\\') {
457 *dst++ = escapedChar(*start++);
470 for (int i = 0; i < numTokens; i++) {
472 if (token->type == PTOK_STRING)
473 token->u.string.len = strlen(token->u.string.string);
476 *numTokensPtr = numTokens;
481 freeFormat(format, numTokens);
485 static int parseExpression(headerSprintfArgs hsa, sprintfToken token,
486 char * str, char ** endPtr)
493 while (*chptr && *chptr != '?') chptr++;
496 hsa->errmsg = _("? expected in expression");
503 hsa->errmsg = _("{ expected after ? in expression");
509 if (parseFormat(hsa, chptr, &token->u.cond.ifFormat,
510 &token->u.cond.numIfTokens, &end, PARSER_IN_EXPR))
513 /* XXX fix segfault on "rpm -q rpm --qf='%|NAME?{%}:{NAME}|\n'"*/
514 if (!(end && *end)) {
515 hsa->errmsg = _("} expected in expression");
516 token->u.cond.ifFormat =
517 freeFormat(token->u.cond.ifFormat, token->u.cond.numIfTokens);
522 if (*chptr != ':' && *chptr != '|') {
523 hsa->errmsg = _(": expected following ? subexpression");
524 token->u.cond.ifFormat =
525 freeFormat(token->u.cond.ifFormat, token->u.cond.numIfTokens);
530 if (parseFormat(hsa, NULL, &token->u.cond.elseFormat,
531 &token->u.cond.numElseTokens, &end, PARSER_IN_EXPR))
533 token->u.cond.ifFormat =
534 freeFormat(token->u.cond.ifFormat, token->u.cond.numIfTokens);
541 hsa->errmsg = _("{ expected after : in expression");
542 token->u.cond.ifFormat =
543 freeFormat(token->u.cond.ifFormat, token->u.cond.numIfTokens);
549 if (parseFormat(hsa, chptr, &token->u.cond.elseFormat,
550 &token->u.cond.numElseTokens, &end, PARSER_IN_EXPR))
553 /* XXX fix segfault on "rpm -q rpm --qf='%|NAME?{a}:{%}|{NAME}\n'" */
554 if (!(end && *end)) {
555 hsa->errmsg = _("} expected in expression");
556 token->u.cond.ifFormat =
557 freeFormat(token->u.cond.ifFormat, token->u.cond.numIfTokens);
563 hsa->errmsg = _("| expected at end of expression");
564 token->u.cond.ifFormat =
565 freeFormat(token->u.cond.ifFormat, token->u.cond.numIfTokens);
566 token->u.cond.elseFormat =
567 freeFormat(token->u.cond.elseFormat, token->u.cond.numElseTokens);
576 token->type = PTOK_COND;
578 (void) findTag(hsa, token, str);
583 static rpmtd getCached(rpmtd *cache, rpmTag tag)
587 if (tag >= RPMTAG_HEADERIMAGE && tag < RPMTAG_FIRSTFREE_TAG && cache[tag]) {
594 * Do headerGet() just once for given tag, cache results.
595 * @param hsa headerSprintf args
600 * @return 1 on success, 0 on failure
602 static rpmtd getData(headerSprintfArgs hsa, rpmTag tag)
606 if (!(td = getCached(hsa->cache, tag))) {
608 if (!headerGet(hsa->h, tag, td, hsa->hgflags)) {
612 hsa->cache[tag] = td;
620 * @param hsa headerSprintf args
623 * @return end of formatted string (NULL on error)
625 static char * formatValue(headerSprintfArgs hsa, sprintfTag tag, int element)
633 memset(buf, 0, sizeof(buf));
634 if ((td = getData(hsa, tag->tag))) {
635 td->ix = element; /* Ick, use iterators instead */
636 stpcpy(stpcpy(buf, "%"), tag->format);
637 val = tag->fmt(td, buf);
640 val = xstrdup("(none)");
645 if (val && need > 0) {
646 t = hsaReserve(hsa, need);
648 hsa->vallen += (te - t);
652 return (hsa->val + hsa->vallen);
656 * Format a single headerSprintf item.
657 * @param hsa headerSprintf args
660 * @return end of formatted string (NULL on error)
662 static char * singleSprintf(headerSprintfArgs hsa, sprintfToken token,
667 rpm_count_t count, numElements;
672 /* we assume the token and header have been validated already! */
674 switch (token->type) {
679 need = token->u.string.len;
680 if (need == 0) break;
681 t = hsaReserve(hsa, need);
682 te = stpcpy(t, token->u.string.string);
683 hsa->vallen += (te - t);
687 t = hsa->val + hsa->vallen;
688 te = formatValue(hsa, &token->u.tag,
689 (token->u.tag.justOne ? 0 : element));
695 if (getData(hsa, token->u.cond.tag.tag) ||
696 headerIsEntry(hsa->h, token->u.cond.tag.tag)) {
697 spft = token->u.cond.ifFormat;
698 condNumFormats = token->u.cond.numIfTokens;
700 spft = token->u.cond.elseFormat;
701 condNumFormats = token->u.cond.numElseTokens;
704 need = condNumFormats * 20;
705 if (spft == NULL || need == 0) break;
707 t = hsaReserve(hsa, need);
708 for (i = 0; i < condNumFormats; i++, spft++) {
709 te = singleSprintf(hsa, spft, element);
718 spft = token->u.array.format;
719 for (i = 0; i < token->u.array.numTokens; i++, spft++)
722 if (spft->type != PTOK_TAG ||
723 spft->u.tag.justOne) continue;
725 if (!(td = getData(hsa, spft->u.tag.tag))) {
730 count = rpmtdCount(td);
732 if (numElements > 1 && count != numElements)
736 _("array iterator used with different sized arrays");
740 case RPM_STRING_TYPE:
743 if (count > numElements)
750 need = numElements * token->u.array.numTokens * 10;
751 if (need == 0) break;
753 spft = token->u.array.format;
754 isxml = (spft->type == PTOK_TAG && spft->u.tag.type != NULL &&
755 rstreq(spft->u.tag.type, "xml"));
758 const char * tagN = rpmTagGetName(spft->u.tag.tag);
760 need = sizeof(" <rpmTag name=\"\">\n") - 1;
762 need += strlen(tagN);
763 t = hsaReserve(hsa, need);
764 te = stpcpy(t, " <rpmTag name=\"");
766 te = stpcpy(te, tagN);
767 te = stpcpy(te, "\">\n");
768 hsa->vallen += (te - t);
771 t = hsaReserve(hsa, need);
772 for (j = 0; j < numElements; j++) {
773 spft = token->u.array.format;
774 for (i = 0; i < token->u.array.numTokens; i++, spft++) {
775 te = singleSprintf(hsa, spft, j);
782 need = sizeof(" </rpmTag>\n") - 1;
783 t = hsaReserve(hsa, need);
784 te = stpcpy(t, " </rpmTag>\n");
785 hsa->vallen += (te - t);
792 return (hsa->val + hsa->vallen);
796 * Create tag data cache.
797 * This allocates much more space than necessary but playing it
798 * simple and stupid for now.
800 static rpmtd *cacheCreate(void)
802 rpmtd *cache = xcalloc(RPMTAG_FIRSTFREE_TAG, sizeof(*cache));
807 * Free tag data cache contents and destroy cache.
809 static void *cacheFree(rpmtd *cache)
812 for (int i = 0; i < RPMTAG_FIRSTFREE_TAG; i++, td++) {
822 char * headerFormat(Header h, const char * fmt, errmsg_t * errmsg)
824 struct headerSprintfArgs_s hsa;
825 sprintfToken nextfmt;
831 memset(&hsa, 0, sizeof(hsa));
832 hsa.h = headerLink(h);
833 hsa.fmt = xstrdup(fmt);
836 if (parseFormat(&hsa, hsa.fmt, &hsa.format, &hsa.numTokens, NULL, PARSER_BEGIN))
839 hsa.cache = cacheCreate();
840 hsa.val = xstrdup("");
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 :
848 isxml = (tag != NULL && tag->tag == -2 && tag->type != NULL && rstreq(tag->type, "xml"));
851 need = sizeof("<rpmHeader>\n") - 1;
852 t = hsaReserve(&hsa, need);
853 te = stpcpy(t, "<rpmHeader>\n");
854 hsa.vallen += (te - t);
858 while ((nextfmt = hsaNext(&hsa)) != NULL) {
859 te = singleSprintf(&hsa, nextfmt, 0);
861 hsa.val = _free(hsa.val);
868 need = sizeof("</rpmHeader>\n") - 1;
869 t = hsaReserve(&hsa, need);
870 te = stpcpy(t, "</rpmHeader>\n");
871 hsa.vallen += (te - t);
874 if (hsa.val != NULL && hsa.vallen < hsa.alloced)
875 hsa.val = xrealloc(hsa.val, hsa.vallen+1);
877 hsa.cache = cacheFree(hsa.cache);
878 hsa.format = freeFormat(hsa.format, hsa.numTokens);
882 *errmsg = hsa.errmsg;
883 hsa.h = headerFree(hsa.h);
884 hsa.fmt = _free(hsa.fmt);