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>
11 #include "lib/misc.h" /* format function protos */
15 #define PARSER_BEGIN 0
16 #define PARSER_IN_ARRAY 1
17 #define PARSER_IN_EXPR 2
21 typedef struct sprintfTag_s * sprintfTag;
23 headerTagFormatFunction fmt;
40 typedef struct sprintfToken_s * sprintfToken;
41 struct sprintfToken_s {
44 struct sprintfTag_s tag; /*!< PTOK_TAG */
49 } array; /*!< PTOK_ARRAY */
53 } string; /*!< PTOK_STRING */
55 sprintfToken ifFormat;
57 sprintfToken elseFormat;
59 struct sprintfTag_s tag;
60 } cond; /*!< PTOK_COND */
64 #define HASHTYPE tagCache
65 #define HTKEYTYPE rpmTagVal
66 #define HTDATATYPE rpmtd
67 #include "lib/rpmhash.H"
68 #include "lib/rpmhash.C"
75 typedef struct headerSprintfArgs_s {
87 headerGetFlags hgflags;
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);
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);
160 /* Normally with bells and whistles enabled, but raw dump on iteration. */
161 hsa->hgflags = (hsa->hi == NULL) ? HEADERGET_EXT : HEADERGET_RAW;
165 * Return next hsa iteration item.
166 * @param hsa headerSprintf args
167 * @return next sprintfToken (or NULL)
169 static sprintfToken hsaNext(headerSprintfArgs hsa)
171 sprintfToken fmt = NULL;
173 (hsa->format->type == PTOK_TAG
174 ? &hsa->format->u.tag :
175 (hsa->format->type == PTOK_ARRAY
176 ? &hsa->format->u.array.format->u.tag :
179 if (hsa->i >= 0 && hsa->i < hsa->numTokens) {
180 fmt = hsa->format + hsa->i;
181 if (hsa->hi == NULL) {
184 tag->tag = headerNextTag(hsa->hi);
185 if (tag->tag == RPMTAG_NOT_FOUND)
194 * Finish an hsa iteration.
195 * @param hsa headerSprintf args
197 static void hsaFini(headerSprintfArgs hsa)
199 hsa->hi = headerFreeIterator(hsa->hi);
204 * Reserve sufficient buffer space for next output value.
205 * @param hsa headerSprintf args
206 * @param need no. of bytes to reserve
207 * @return pointer to reserved space
209 static char * hsaReserve(headerSprintfArgs hsa, size_t need)
211 if ((hsa->vallen + need) >= hsa->alloced) {
212 if (hsa->alloced <= need)
213 hsa->alloced += need;
215 hsa->val = xrealloc(hsa->val, hsa->alloced+1);
217 return hsa->val + hsa->vallen;
221 * Search tags for a name.
222 * @param hsa headerSprintf args
223 * @param token parsed fields
224 * @param name name to find
225 * @return 0 on success, 1 on not found
227 static int findTag(headerSprintfArgs hsa, sprintfToken token, const char * name)
229 const char *tagname = name;
230 sprintfTag stag = (token->type == PTOK_COND
231 ? &token->u.cond.tag : &token->u.tag);
234 stag->tag = RPMTAG_NOT_FOUND;
236 if (!rstreq(tagname, "*")) {
237 if (rstreqn("RPMTAG_", tagname, sizeof("RPMTAG_")-1)) {
238 tagname += sizeof("RPMTAG");
241 /* Search tag names. */
242 stag->tag = rpmTagGetValue(tagname);
243 if (stag->tag == RPMTAG_NOT_FOUND) return 1;
245 } else stag->tag = -2;
247 /* Search extensions for specific format. */
248 if (stag->type != NULL)
249 stag->fmt = rpmHeaderFormatFuncByName(stag->type);
251 return stag->fmt ? 0 : 1;
256 * Parse an expression.
257 * @param hsa headerSprintf args
260 * @param[out] *endPtr
261 * @return 0 on success
263 static int parseExpression(headerSprintfArgs hsa, sprintfToken token,
264 char * str,char ** endPtr);
267 * Parse a headerSprintf term.
268 * @param hsa headerSprintf args
271 * @retval *numTokensPtr
274 * @return 0 on success
276 static int parseFormat(headerSprintfArgs hsa, char * str,
277 sprintfToken * formatPtr,int * numTokensPtr,
278 char ** endPtr, int state)
280 char * chptr, * start, * next, * dst;
286 /* upper limit on number of individual formats */
289 for (chptr = str; *chptr != '\0'; chptr++)
290 if (*chptr == '%' || *chptr == '[') numTokens++;
291 numTokens = numTokens * 2 + 1;
293 format = xcalloc(numTokens, sizeof(*format));
294 if (endPtr) *endPtr = NULL;
300 while (*start != '\0') {
304 if (*(start + 1) == '%') {
305 if (token == NULL || token->type != PTOK_STRING) {
306 token = format + numTokens++;
307 token->type = PTOK_STRING;
308 dst = token->u.string.string = start;
315 token = format + numTokens++;
323 if (parseExpression(hsa, token, start, &newEnd)) {
330 token->u.tag.format = start;
331 token->u.tag.justOne = 0;
334 while (*chptr && *chptr != '{' && *chptr != '%') {
335 if (!risdigit(*chptr) && *chptr != '-') {
336 hsa->errmsg = _("invalid field width");
341 if (!*chptr || *chptr == '%') {
342 hsa->errmsg = _("missing { after %");
348 while (start < chptr) {
353 token->u.tag.justOne = 1;
355 } else if (*start == '#') {
356 token->u.tag.justOne = 1;
357 token->u.tag.type = "arraysize";
362 while (*next && *next != '}') next++;
364 hsa->errmsg = _("missing } after %{");
370 while (*chptr && *chptr != ':') chptr++;
372 if (*chptr != '\0') {
375 hsa->errmsg = _("empty tag format");
378 token->u.tag.type = chptr;
380 /* default to string conversion if no formats found by now */
381 if (!token->u.tag.type) {
382 token->u.tag.type = "string";
386 hsa->errmsg = _("empty tag name");
390 token->type = PTOK_TAG;
392 if (findTag(hsa, token, start)) {
393 hsa->errmsg = _("unknown tag");
403 token = format + numTokens++;
405 if (parseFormat(hsa, start,
406 &token->u.array.format,
407 &token->u.array.numTokens,
408 &start, PARSER_IN_ARRAY)) {
413 hsa->errmsg = _("] expected at end of array");
419 token->type = PTOK_ARRAY;
424 if (state != PARSER_IN_ARRAY) {
425 hsa->errmsg = _("unexpected ]");
429 if (endPtr) *endPtr = start;
434 if (state != PARSER_IN_EXPR) {
435 hsa->errmsg = _("unexpected }");
439 if (endPtr) *endPtr = start;
444 if (token == NULL || token->type != PTOK_STRING) {
445 token = format + numTokens++;
446 token->type = PTOK_STRING;
447 dst = token->u.string.string = start;
450 if (*start == '\\') {
452 *dst++ = escapedChar(*start++);
465 for (int i = 0; i < numTokens; i++) {
467 if (token->type == PTOK_STRING)
468 token->u.string.len = strlen(token->u.string.string);
471 *numTokensPtr = numTokens;
476 freeFormat(format, numTokens);
480 static int parseExpression(headerSprintfArgs hsa, sprintfToken token,
481 char * str, char ** endPtr)
488 while (*chptr && *chptr != '?') chptr++;
491 hsa->errmsg = _("? expected in expression");
498 hsa->errmsg = _("{ expected after ? in expression");
504 if (parseFormat(hsa, chptr, &token->u.cond.ifFormat,
505 &token->u.cond.numIfTokens, &end, PARSER_IN_EXPR))
508 /* XXX fix segfault on "rpm -q rpm --qf='%|NAME?{%}:{NAME}|\n'"*/
509 if (!(end && *end)) {
510 hsa->errmsg = _("} expected in expression");
511 token->u.cond.ifFormat =
512 freeFormat(token->u.cond.ifFormat, token->u.cond.numIfTokens);
517 if (*chptr != ':' && *chptr != '|') {
518 hsa->errmsg = _(": expected following ? subexpression");
519 token->u.cond.ifFormat =
520 freeFormat(token->u.cond.ifFormat, token->u.cond.numIfTokens);
525 if (parseFormat(hsa, NULL, &token->u.cond.elseFormat,
526 &token->u.cond.numElseTokens, &end, PARSER_IN_EXPR))
528 token->u.cond.ifFormat =
529 freeFormat(token->u.cond.ifFormat, token->u.cond.numIfTokens);
536 hsa->errmsg = _("{ expected after : in expression");
537 token->u.cond.ifFormat =
538 freeFormat(token->u.cond.ifFormat, token->u.cond.numIfTokens);
544 if (parseFormat(hsa, chptr, &token->u.cond.elseFormat,
545 &token->u.cond.numElseTokens, &end, PARSER_IN_EXPR))
548 /* XXX fix segfault on "rpm -q rpm --qf='%|NAME?{a}:{%}|{NAME}\n'" */
549 if (!(end && *end)) {
550 hsa->errmsg = _("} expected in expression");
551 token->u.cond.ifFormat =
552 freeFormat(token->u.cond.ifFormat, token->u.cond.numIfTokens);
558 hsa->errmsg = _("| expected at end of expression");
559 token->u.cond.ifFormat =
560 freeFormat(token->u.cond.ifFormat, token->u.cond.numIfTokens);
561 token->u.cond.elseFormat =
562 freeFormat(token->u.cond.elseFormat, token->u.cond.numElseTokens);
571 token->u.cond.tag.type = NULL;
572 token->u.cond.tag.format = "";
573 token->type = PTOK_COND;
575 if ((token->u.cond.tag.type = strchr(str, ':')) != 0)
576 *token->u.cond.tag.type++ = 0;
577 (void) findTag(hsa, token, str);
582 static rpmtd getCached(tagCache cache, rpmTagVal tag)
585 return tagCacheGetEntry(cache, tag, &res, NULL, NULL) ? res[0] : NULL;
589 * Do headerGet() just once for given tag, cache results.
590 * @param hsa headerSprintf args
595 * @return 1 on success, 0 on failure
597 static rpmtd getData(headerSprintfArgs hsa, rpmTagVal tag)
601 if (!(td = getCached(hsa->cache, tag))) {
603 if (!headerGet(hsa->h, tag, td, hsa->hgflags)) {
607 tagCacheAddEntry(hsa->cache, tag, td);
615 * @param hsa headerSprintf args
618 * @return end of formatted string (NULL on error)
620 static char * formatValue(headerSprintfArgs hsa, sprintfTag tag, int element)
627 if ((td = getData(hsa, tag->tag))) {
628 td->ix = element; /* Ick, use iterators instead */
631 val = xstrdup("(none)");
634 /* Handle field width + justification formatting if specified */
635 if (tag->format && *tag->format) {
637 /* user string + extra for '%', format char and trailing '\0' */
638 char fmtbuf[strlen(tag->format) + 3];
640 sprintf(fmtbuf, "%%%ss", tag->format);
641 rasprintf(&tval, fmtbuf, val);
648 if (val && need > 0) {
649 t = hsaReserve(hsa, need);
651 hsa->vallen += (te - t);
655 return (hsa->val + hsa->vallen);
659 * Format a single headerSprintf item.
660 * @param hsa headerSprintf args
663 * @return end of formatted string (NULL on error)
665 static char * singleSprintf(headerSprintfArgs hsa, sprintfToken token,
670 rpm_count_t count, numElements;
676 /* we assume the token and header have been validated already! */
678 switch (token->type) {
683 need = token->u.string.len;
684 if (need == 0) break;
685 t = hsaReserve(hsa, need);
686 te = stpcpy(t, token->u.string.string);
687 hsa->vallen += (te - t);
691 t = hsa->val + hsa->vallen;
692 te = formatValue(hsa, &token->u.tag,
693 (token->u.tag.justOne ? 0 : element));
699 if (getData(hsa, token->u.cond.tag.tag) ||
700 headerIsEntry(hsa->h, token->u.cond.tag.tag)) {
701 spft = token->u.cond.ifFormat;
702 condNumFormats = token->u.cond.numIfTokens;
703 if (token->u.cond.tag.fmt) {
704 /* check if format creates output */
705 size_t vallen = hsa->vallen;
706 formatValue(hsa, &token->u.cond.tag, element);
707 if (hsa->vallen == vallen) {
708 spft = token->u.cond.elseFormat;
709 condNumFormats = token->u.cond.numElseTokens;
711 hsa->vallen = vallen;
712 hsa->val[hsa->vallen] = 0;
716 spft = token->u.cond.elseFormat;
717 condNumFormats = token->u.cond.numElseTokens;
720 need = condNumFormats * 20;
721 if (spft == NULL || need == 0) break;
723 t = hsaReserve(hsa, need);
724 for (i = 0; i < condNumFormats; i++, spft++) {
725 te = singleSprintf(hsa, spft, element);
734 spft = token->u.array.format;
735 for (i = 0; i < token->u.array.numTokens; i++, spft++)
738 if (spft->type != PTOK_TAG && spft->type != PTOK_COND)
740 stag = (spft->type == PTOK_COND ? &spft->u.cond.tag : &spft->u.tag);
744 if (!(td = getData(hsa, stag->tag))) {
749 count = rpmtdCount(td);
751 if (numElements > 1 && count != numElements)
755 _("array iterator used with different sized arrays");
759 case RPM_STRING_TYPE:
762 if (count > numElements)
769 need = numElements * token->u.array.numTokens * 10;
770 if (need == 0) break;
772 spft = token->u.array.format;
773 isxml = (spft->type == PTOK_TAG && spft->u.tag.type != NULL &&
774 rstreq(spft->u.tag.type, "xml"));
777 const char * tagN = rpmTagGetName(spft->u.tag.tag);
779 need = sizeof(" <rpmTag name=\"\">\n") - 1;
781 need += strlen(tagN);
782 t = hsaReserve(hsa, need);
783 te = stpcpy(t, " <rpmTag name=\"");
785 te = stpcpy(te, tagN);
786 te = stpcpy(te, "\">\n");
787 hsa->vallen += (te - t);
790 t = hsaReserve(hsa, need);
791 for (j = 0; j < numElements; j++) {
792 spft = token->u.array.format;
793 for (i = 0; i < token->u.array.numTokens; i++, spft++) {
794 te = singleSprintf(hsa, spft, j);
801 need = sizeof(" </rpmTag>\n") - 1;
802 t = hsaReserve(hsa, need);
803 te = stpcpy(t, " </rpmTag>\n");
804 hsa->vallen += (te - t);
811 return (hsa->val + hsa->vallen);
814 static int tagCmp(rpmTagVal a, rpmTagVal b)
819 static unsigned int tagId(rpmTagVal tag)
824 static rpmtd tagFree(rpmtd td)
831 char * headerFormat(Header h, const char * fmt, errmsg_t * errmsg)
833 struct headerSprintfArgs_s hsa;
834 sprintfToken nextfmt;
840 memset(&hsa, 0, sizeof(hsa));
841 hsa.h = headerLink(h);
842 hsa.fmt = xstrdup(fmt);
845 if (parseFormat(&hsa, hsa.fmt, &hsa.format, &hsa.numTokens, NULL, PARSER_BEGIN))
848 hsa.cache = tagCacheCreate(128, tagId, tagCmp, NULL, tagFree);
849 hsa.val = xstrdup("");
852 (hsa.format->type == PTOK_TAG
853 ? &hsa.format->u.tag :
854 (hsa.format->type == PTOK_ARRAY
855 ? &hsa.format->u.array.format->u.tag :
857 isxml = (tag != NULL && tag->tag == -2 && tag->type != NULL && rstreq(tag->type, "xml"));
860 need = sizeof("<rpmHeader>\n") - 1;
861 t = hsaReserve(&hsa, need);
862 te = stpcpy(t, "<rpmHeader>\n");
863 hsa.vallen += (te - t);
867 while ((nextfmt = hsaNext(&hsa)) != NULL) {
868 te = singleSprintf(&hsa, nextfmt, 0);
870 hsa.val = _free(hsa.val);
877 need = sizeof("</rpmHeader>\n") - 1;
878 t = hsaReserve(&hsa, need);
879 te = stpcpy(t, "</rpmHeader>\n");
880 hsa.vallen += (te - t);
883 if (hsa.val != NULL && hsa.vallen < hsa.alloced)
884 hsa.val = xrealloc(hsa.val, hsa.vallen+1);
886 hsa.cache = tagCacheFree(hsa.cache);
887 hsa.format = freeFormat(hsa.format, hsa.numTokens);
891 *errmsg = hsa.errmsg;
892 hsa.h = headerFree(hsa.h);
893 hsa.fmt = _free(hsa.fmt);