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 } * headerSprintfArgs;
91 static char escapedChar(const char ch)
94 case 'a': return '\a';
95 case 'b': return '\b';
96 case 'f': return '\f';
97 case 'n': return '\n';
98 case 'r': return '\r';
99 case 't': return '\t';
100 case 'v': return '\v';
106 * Destroy headerSprintf format array.
107 * @param format sprintf format array
108 * @param num number of elements
109 * @return NULL always
112 freeFormat( sprintfToken format, int num)
116 if (format == NULL) return NULL;
118 for (i = 0; i < num; i++) {
119 switch (format[i].type) {
121 format[i].u.array.format =
122 freeFormat(format[i].u.array.format,
123 format[i].u.array.numTokens);
126 format[i].u.cond.ifFormat =
127 freeFormat(format[i].u.cond.ifFormat,
128 format[i].u.cond.numIfTokens);
129 format[i].u.cond.elseFormat =
130 freeFormat(format[i].u.cond.elseFormat,
131 format[i].u.cond.numElseTokens);
140 format = _free(format);
145 * Initialize an hsa iteration.
146 * @param hsa headerSprintf args
148 static void hsaInit(headerSprintfArgs hsa)
151 (hsa->format->type == PTOK_TAG
152 ? &hsa->format->u.tag :
153 (hsa->format->type == PTOK_ARRAY
154 ? &hsa->format->u.array.format->u.tag :
158 if (tag != NULL && tag->tag == -2)
159 hsa->hi = headerInitIterator(hsa->h);
163 * Return next hsa iteration item.
164 * @param hsa headerSprintf args
165 * @return next sprintfToken (or NULL)
167 static sprintfToken hsaNext(headerSprintfArgs hsa)
169 sprintfToken fmt = NULL;
171 (hsa->format->type == PTOK_TAG
172 ? &hsa->format->u.tag :
173 (hsa->format->type == PTOK_ARRAY
174 ? &hsa->format->u.array.format->u.tag :
177 if (hsa->i >= 0 && hsa->i < hsa->numTokens) {
178 fmt = hsa->format + hsa->i;
179 if (hsa->hi == NULL) {
184 /* hmm, cache the data from here too? */
185 if (!headerNext(hsa->hi, &td))
196 * Finish an hsa iteration.
197 * @param hsa headerSprintf args
199 static void hsaFini(headerSprintfArgs hsa)
201 hsa->hi = headerFreeIterator(hsa->hi);
206 * Reserve sufficient buffer space for next output value.
207 * @param hsa headerSprintf args
208 * @param need no. of bytes to reserve
209 * @return pointer to reserved space
211 static char * hsaReserve(headerSprintfArgs hsa, size_t need)
213 if ((hsa->vallen + need) >= hsa->alloced) {
214 if (hsa->alloced <= need)
215 hsa->alloced += need;
217 hsa->val = xrealloc(hsa->val, hsa->alloced+1);
219 return hsa->val + hsa->vallen;
223 * Search tags for a name.
224 * @param hsa headerSprintf args
225 * @param token parsed fields
226 * @param name name to find
227 * @return 0 on success, 1 on not found
229 static int findTag(headerSprintfArgs hsa, sprintfToken token, const char * name)
231 const char *tagname = name;
232 sprintfTag stag = (token->type == PTOK_COND
233 ? &token->u.cond.tag : &token->u.tag);
238 if (rstreq(tagname, "*")) {
243 if (rstreqn("RPMTAG_", tagname, sizeof("RPMTAG_")-1)) {
244 tagname += sizeof("RPMTAG");
247 /* Search tag names. */
248 stag->tag = rpmTagGetValue(tagname);
249 if (stag->tag != RPMTAG_NOT_FOUND)
255 /* Search extensions for specific format. */
256 if (stag->type != NULL)
257 stag->fmt = rpmHeaderFormatFuncByName(stag->type);
259 return stag->fmt ? 0 : 1;
264 * Parse an expression.
265 * @param hsa headerSprintf args
268 * @param[out] *endPtr
269 * @return 0 on success
271 static int parseExpression(headerSprintfArgs hsa, sprintfToken token,
272 char * str,char ** endPtr);
275 * Parse a headerSprintf term.
276 * @param hsa headerSprintf args
279 * @retval *numTokensPtr
282 * @return 0 on success
284 static int parseFormat(headerSprintfArgs hsa, char * str,
285 sprintfToken * formatPtr,int * numTokensPtr,
286 char ** endPtr, int state)
288 char * chptr, * start, * next, * dst;
294 /* upper limit on number of individual formats */
297 for (chptr = str; *chptr != '\0'; chptr++)
298 if (*chptr == '%' || *chptr == '[') numTokens++;
299 numTokens = numTokens * 2 + 1;
301 format = xcalloc(numTokens, sizeof(*format));
302 if (endPtr) *endPtr = NULL;
308 while (*start != '\0') {
312 if (*(start + 1) == '%') {
313 if (token == NULL || token->type != PTOK_STRING) {
314 token = format + numTokens++;
315 token->type = PTOK_STRING;
316 dst = token->u.string.string = start;
323 token = format + numTokens++;
331 if (parseExpression(hsa, token, start, &newEnd)) {
338 token->u.tag.format = start;
339 token->u.tag.justOne = 0;
342 while (*chptr && *chptr != '{' && *chptr != '%') chptr++;
343 if (!*chptr || *chptr == '%') {
344 hsa->errmsg = _("missing { after %");
350 while (start < chptr) {
355 token->u.tag.justOne = 1;
357 } else if (*start == '#') {
358 token->u.tag.justOne = 1;
359 token->u.tag.type = "arraysize";
364 while (*next && *next != '}') next++;
366 hsa->errmsg = _("missing } after %{");
372 while (*chptr && *chptr != ':') chptr++;
374 if (*chptr != '\0') {
377 hsa->errmsg = _("empty tag format");
380 token->u.tag.type = chptr;
382 /* default to string conversion if no formats found by now */
383 if (!token->u.tag.type) {
384 token->u.tag.type = "string";
388 hsa->errmsg = _("empty tag name");
392 token->type = PTOK_TAG;
394 if (findTag(hsa, token, start)) {
395 hsa->errmsg = _("unknown tag");
405 token = format + numTokens++;
407 if (parseFormat(hsa, start,
408 &token->u.array.format,
409 &token->u.array.numTokens,
410 &start, PARSER_IN_ARRAY)) {
415 hsa->errmsg = _("] expected at end of array");
421 token->type = PTOK_ARRAY;
426 if (state != PARSER_IN_ARRAY) {
427 hsa->errmsg = _("unexpected ]");
431 if (endPtr) *endPtr = start;
436 if (state != PARSER_IN_EXPR) {
437 hsa->errmsg = _("unexpected }");
441 if (endPtr) *endPtr = start;
446 if (token == NULL || token->type != PTOK_STRING) {
447 token = format + numTokens++;
448 token->type = PTOK_STRING;
449 dst = token->u.string.string = start;
452 if (*start == '\\') {
454 *dst++ = escapedChar(*start++);
467 for (int i = 0; i < numTokens; i++) {
469 if (token->type == PTOK_STRING)
470 token->u.string.len = strlen(token->u.string.string);
473 *numTokensPtr = numTokens;
478 freeFormat(format, numTokens);
482 static int parseExpression(headerSprintfArgs hsa, sprintfToken token,
483 char * str, char ** endPtr)
490 while (*chptr && *chptr != '?') chptr++;
493 hsa->errmsg = _("? expected in expression");
500 hsa->errmsg = _("{ expected after ? in expression");
506 if (parseFormat(hsa, chptr, &token->u.cond.ifFormat,
507 &token->u.cond.numIfTokens, &end, PARSER_IN_EXPR))
510 /* XXX fix segfault on "rpm -q rpm --qf='%|NAME?{%}:{NAME}|\n'"*/
511 if (!(end && *end)) {
512 hsa->errmsg = _("} expected in expression");
513 token->u.cond.ifFormat =
514 freeFormat(token->u.cond.ifFormat, token->u.cond.numIfTokens);
519 if (*chptr != ':' && *chptr != '|') {
520 hsa->errmsg = _(": expected following ? subexpression");
521 token->u.cond.ifFormat =
522 freeFormat(token->u.cond.ifFormat, token->u.cond.numIfTokens);
527 if (parseFormat(hsa, NULL, &token->u.cond.elseFormat,
528 &token->u.cond.numElseTokens, &end, PARSER_IN_EXPR))
530 token->u.cond.ifFormat =
531 freeFormat(token->u.cond.ifFormat, token->u.cond.numIfTokens);
538 hsa->errmsg = _("{ expected after : in expression");
539 token->u.cond.ifFormat =
540 freeFormat(token->u.cond.ifFormat, token->u.cond.numIfTokens);
546 if (parseFormat(hsa, chptr, &token->u.cond.elseFormat,
547 &token->u.cond.numElseTokens, &end, PARSER_IN_EXPR))
550 /* XXX fix segfault on "rpm -q rpm --qf='%|NAME?{a}:{%}|{NAME}\n'" */
551 if (!(end && *end)) {
552 hsa->errmsg = _("} expected in expression");
553 token->u.cond.ifFormat =
554 freeFormat(token->u.cond.ifFormat, token->u.cond.numIfTokens);
560 hsa->errmsg = _("| expected at end of expression");
561 token->u.cond.ifFormat =
562 freeFormat(token->u.cond.ifFormat, token->u.cond.numIfTokens);
563 token->u.cond.elseFormat =
564 freeFormat(token->u.cond.elseFormat, token->u.cond.numElseTokens);
573 token->type = PTOK_COND;
575 (void) findTag(hsa, token, str);
580 static rpmtd getCached(rpmtd *cache, rpmTag tag)
584 if (tag >= HEADER_IMAGE && tag < RPMTAG_FIRSTFREE_TAG && cache[tag]) {
591 * Do headerGet() just once for given tag, cache results.
592 * @param hsa headerSprintf args
597 * @return 1 on success, 0 on failure
599 static rpmtd getData(headerSprintfArgs hsa, rpmTag tag)
603 if (!(td = getCached(hsa->cache, tag))) {
605 if (!headerGet(hsa->h, tag, td, HEADERGET_EXT)) {
609 hsa->cache[tag] = td;
617 * @param hsa headerSprintf args
620 * @return end of formatted string (NULL on error)
622 static char * formatValue(headerSprintfArgs hsa, sprintfTag tag, int element)
630 memset(buf, 0, sizeof(buf));
631 if ((td = getData(hsa, tag->tag))) {
632 td->ix = element; /* Ick, use iterators instead */
633 stpcpy(stpcpy(buf, "%"), tag->format);
634 val = tag->fmt(td, buf);
637 val = xstrdup("(none)");
642 if (val && need > 0) {
643 t = hsaReserve(hsa, need);
645 hsa->vallen += (te - t);
649 return (hsa->val + hsa->vallen);
653 * Format a single headerSprintf item.
654 * @param hsa headerSprintf args
657 * @return end of formatted string (NULL on error)
659 static char * singleSprintf(headerSprintfArgs hsa, sprintfToken token,
664 rpm_count_t count, numElements;
669 /* we assume the token and header have been validated already! */
671 switch (token->type) {
676 need = token->u.string.len;
677 if (need == 0) break;
678 t = hsaReserve(hsa, need);
679 te = stpcpy(t, token->u.string.string);
680 hsa->vallen += (te - t);
684 t = hsa->val + hsa->vallen;
685 te = formatValue(hsa, &token->u.tag,
686 (token->u.tag.justOne ? 0 : element));
692 if (getCached(hsa->cache, token->u.cond.tag.tag) ||
693 headerIsEntry(hsa->h, token->u.cond.tag.tag)) {
694 spft = token->u.cond.ifFormat;
695 condNumFormats = token->u.cond.numIfTokens;
697 spft = token->u.cond.elseFormat;
698 condNumFormats = token->u.cond.numElseTokens;
701 need = condNumFormats * 20;
702 if (spft == NULL || need == 0) break;
704 t = hsaReserve(hsa, need);
705 for (i = 0; i < condNumFormats; i++, spft++) {
706 te = singleSprintf(hsa, spft, element);
715 spft = token->u.array.format;
716 for (i = 0; i < token->u.array.numTokens; i++, spft++)
719 if (spft->type != PTOK_TAG ||
720 spft->u.tag.justOne) continue;
722 if (!(td = getData(hsa, spft->u.tag.tag))) {
727 count = rpmtdCount(td);
729 if (numElements > 1 && count != numElements)
733 _("array iterator used with different sized arrays");
737 case RPM_STRING_TYPE:
740 if (count > numElements)
747 need = numElements * token->u.array.numTokens * 10;
748 if (need == 0) break;
750 spft = token->u.array.format;
751 isxml = (spft->type == PTOK_TAG && spft->u.tag.type != NULL &&
752 rstreq(spft->u.tag.type, "xml"));
755 const char * tagN = rpmTagGetName(spft->u.tag.tag);
757 need = sizeof(" <rpmTag name=\"\">\n") - 1;
759 need += strlen(tagN);
760 t = hsaReserve(hsa, need);
761 te = stpcpy(t, " <rpmTag name=\"");
763 te = stpcpy(te, tagN);
764 te = stpcpy(te, "\">\n");
765 hsa->vallen += (te - t);
768 t = hsaReserve(hsa, need);
769 for (j = 0; j < numElements; j++) {
770 spft = token->u.array.format;
771 for (i = 0; i < token->u.array.numTokens; i++, spft++) {
772 te = singleSprintf(hsa, spft, j);
779 need = sizeof(" </rpmTag>\n") - 1;
780 t = hsaReserve(hsa, need);
781 te = stpcpy(t, " </rpmTag>\n");
782 hsa->vallen += (te - t);
789 return (hsa->val + hsa->vallen);
793 * Create tag data cache.
794 * This allocates much more space than necessary but playing it
795 * simple and stupid for now.
797 static rpmtd *cacheCreate(void)
799 rpmtd *cache = xcalloc(RPMTAG_FIRSTFREE_TAG, sizeof(*cache));
804 * Free tag data cache contents and destroy cache.
806 static void *cacheFree(rpmtd *cache)
809 for (int i = 0; i < RPMTAG_FIRSTFREE_TAG; i++, td++) {
819 char * headerFormat(Header h, const char * fmt, errmsg_t * errmsg)
821 struct headerSprintfArgs_s hsa;
822 sprintfToken nextfmt;
828 memset(&hsa, 0, sizeof(hsa));
829 hsa.h = headerLink(h);
830 hsa.fmt = xstrdup(fmt);
833 if (parseFormat(&hsa, hsa.fmt, &hsa.format, &hsa.numTokens, NULL, PARSER_BEGIN))
836 hsa.cache = cacheCreate();
837 hsa.val = xstrdup("");
840 (hsa.format->type == PTOK_TAG
841 ? &hsa.format->u.tag :
842 (hsa.format->type == PTOK_ARRAY
843 ? &hsa.format->u.array.format->u.tag :
845 isxml = (tag != NULL && tag->tag == -2 && tag->type != NULL && rstreq(tag->type, "xml"));
848 need = sizeof("<rpmHeader>\n") - 1;
849 t = hsaReserve(&hsa, need);
850 te = stpcpy(t, "<rpmHeader>\n");
851 hsa.vallen += (te - t);
855 while ((nextfmt = hsaNext(&hsa)) != NULL) {
856 te = singleSprintf(&hsa, nextfmt, 0);
858 hsa.val = _free(hsa.val);
865 need = sizeof("</rpmHeader>\n") - 1;
866 t = hsaReserve(&hsa, need);
867 te = stpcpy(t, "</rpmHeader>\n");
868 hsa.vallen += (te - t);
871 if (hsa.val != NULL && hsa.vallen < hsa.alloced)
872 hsa.val = xrealloc(hsa.val, hsa.vallen+1);
874 hsa.cache = cacheFree(hsa.cache);
875 hsa.format = freeFormat(hsa.format, hsa.numTokens);
879 *errmsg = hsa.errmsg;
880 hsa.h = headerFree(hsa.h);
881 hsa.fmt = _free(hsa.fmt);