|
|
2ff057 |
/** \ingroup header
|
|
|
2ff057 |
* \file lib/headerfmt.c
|
|
|
2ff057 |
*/
|
|
|
2ff057 |
|
|
|
2ff057 |
#include "system.h"
|
|
|
2ff057 |
|
|
|
2ff057 |
#include <stdarg.h>
|
|
|
2ff057 |
#include <rpm/header.h>
|
|
|
2ff057 |
#include <rpm/rpmtag.h>
|
|
|
2ff057 |
#include <rpm/rpmstring.h>
|
|
|
2ff057 |
#include <rpm/rpmpgp.h>
|
|
|
2ff057 |
#include "lib/misc.h" /* format function protos */
|
|
|
2ff057 |
|
|
|
2ff057 |
#include "debug.h"
|
|
|
2ff057 |
|
|
|
2ff057 |
#define PARSER_BEGIN 0
|
|
|
2ff057 |
#define PARSER_IN_ARRAY 1
|
|
|
2ff057 |
#define PARSER_IN_EXPR 2
|
|
|
2ff057 |
|
|
|
2ff057 |
/** \ingroup header
|
|
|
2ff057 |
*/
|
|
|
2ff057 |
typedef struct sprintfTag_s * sprintfTag;
|
|
|
2ff057 |
struct sprintfTag_s {
|
|
|
2ff057 |
headerFmt fmt;
|
|
|
2ff057 |
rpmTagVal tag;
|
|
|
2ff057 |
int justOne;
|
|
|
2ff057 |
char * format;
|
|
|
2ff057 |
char * type;
|
|
|
2ff057 |
};
|
|
|
2ff057 |
|
|
|
2ff057 |
typedef enum {
|
|
|
2ff057 |
PTOK_NONE = 0,
|
|
|
2ff057 |
PTOK_TAG,
|
|
|
2ff057 |
PTOK_ARRAY,
|
|
|
2ff057 |
PTOK_STRING,
|
|
|
2ff057 |
PTOK_COND
|
|
|
2ff057 |
} ptokType;
|
|
|
2ff057 |
|
|
|
2ff057 |
/** \ingroup header
|
|
|
2ff057 |
*/
|
|
|
2ff057 |
typedef struct sprintfToken_s * sprintfToken;
|
|
|
2ff057 |
struct sprintfToken_s {
|
|
|
2ff057 |
ptokType type;
|
|
|
2ff057 |
union {
|
|
|
2ff057 |
struct sprintfTag_s tag; /*!< PTOK_TAG */
|
|
|
2ff057 |
struct {
|
|
|
2ff057 |
sprintfToken format;
|
|
|
2ff057 |
int i;
|
|
|
2ff057 |
int numTokens;
|
|
|
2ff057 |
} array; /*!< PTOK_ARRAY */
|
|
|
2ff057 |
struct {
|
|
|
2ff057 |
char * string;
|
|
|
2ff057 |
int len;
|
|
|
2ff057 |
} string; /*!< PTOK_STRING */
|
|
|
2ff057 |
struct {
|
|
|
2ff057 |
sprintfToken ifFormat;
|
|
|
2ff057 |
int numIfTokens;
|
|
|
2ff057 |
sprintfToken elseFormat;
|
|
|
2ff057 |
int numElseTokens;
|
|
|
2ff057 |
struct sprintfTag_s tag;
|
|
|
2ff057 |
} cond; /*!< PTOK_COND */
|
|
|
2ff057 |
} u;
|
|
|
2ff057 |
};
|
|
|
2ff057 |
|
|
|
2ff057 |
#define HASHTYPE tagCache
|
|
|
2ff057 |
#define HTKEYTYPE rpmTagVal
|
|
|
2ff057 |
#define HTDATATYPE rpmtd
|
|
|
2ff057 |
#include "lib/rpmhash.H"
|
|
|
2ff057 |
#include "lib/rpmhash.C"
|
|
|
2ff057 |
#undef HASHTYPE
|
|
|
2ff057 |
#undef HTKEYTYPE
|
|
|
2ff057 |
#undef HTDATATYPE
|
|
|
2ff057 |
|
|
|
2ff057 |
/**
|
|
|
2ff057 |
*/
|
|
|
2ff057 |
typedef struct headerSprintfArgs_s {
|
|
|
2ff057 |
Header h;
|
|
|
2ff057 |
char * fmt;
|
|
|
2ff057 |
const char * errmsg;
|
|
|
2ff057 |
tagCache cache;
|
|
|
2ff057 |
sprintfToken format;
|
|
|
2ff057 |
HeaderIterator hi;
|
|
|
2ff057 |
char * val;
|
|
|
2ff057 |
size_t vallen;
|
|
|
2ff057 |
size_t alloced;
|
|
|
2ff057 |
int numTokens;
|
|
|
2ff057 |
int i;
|
|
|
2ff057 |
headerGetFlags hgflags;
|
|
|
2ff057 |
} * headerSprintfArgs;
|
|
|
2ff057 |
|
|
|
2ff057 |
|
|
|
2ff057 |
static char escapedChar(const char ch)
|
|
|
2ff057 |
{
|
|
|
2ff057 |
switch (ch) {
|
|
|
2ff057 |
case 'a': return '\a';
|
|
|
2ff057 |
case 'b': return '\b';
|
|
|
2ff057 |
case 'f': return '\f';
|
|
|
2ff057 |
case 'n': return '\n';
|
|
|
2ff057 |
case 'r': return '\r';
|
|
|
2ff057 |
case 't': return '\t';
|
|
|
2ff057 |
case 'v': return '\v';
|
|
|
2ff057 |
default: return ch;
|
|
|
2ff057 |
}
|
|
|
2ff057 |
}
|
|
|
2ff057 |
|
|
|
2ff057 |
/**
|
|
|
2ff057 |
* Destroy headerSprintf format array.
|
|
|
2ff057 |
* @param format sprintf format array
|
|
|
2ff057 |
* @param num number of elements
|
|
|
2ff057 |
* @return NULL always
|
|
|
2ff057 |
*/
|
|
|
2ff057 |
static sprintfToken
|
|
|
2ff057 |
freeFormat( sprintfToken format, int num)
|
|
|
2ff057 |
{
|
|
|
2ff057 |
int i;
|
|
|
2ff057 |
|
|
|
2ff057 |
if (format == NULL) return NULL;
|
|
|
2ff057 |
|
|
|
2ff057 |
for (i = 0; i < num; i++) {
|
|
|
2ff057 |
switch (format[i].type) {
|
|
|
2ff057 |
case PTOK_ARRAY:
|
|
|
2ff057 |
format[i].u.array.format =
|
|
|
2ff057 |
freeFormat(format[i].u.array.format,
|
|
|
2ff057 |
format[i].u.array.numTokens);
|
|
|
2ff057 |
break;
|
|
|
2ff057 |
case PTOK_COND:
|
|
|
2ff057 |
format[i].u.cond.ifFormat =
|
|
|
2ff057 |
freeFormat(format[i].u.cond.ifFormat,
|
|
|
2ff057 |
format[i].u.cond.numIfTokens);
|
|
|
2ff057 |
format[i].u.cond.elseFormat =
|
|
|
2ff057 |
freeFormat(format[i].u.cond.elseFormat,
|
|
|
2ff057 |
format[i].u.cond.numElseTokens);
|
|
|
2ff057 |
break;
|
|
|
2ff057 |
case PTOK_NONE:
|
|
|
2ff057 |
case PTOK_TAG:
|
|
|
2ff057 |
case PTOK_STRING:
|
|
|
2ff057 |
default:
|
|
|
2ff057 |
break;
|
|
|
2ff057 |
}
|
|
|
2ff057 |
}
|
|
|
2ff057 |
free(format);
|
|
|
2ff057 |
return NULL;
|
|
|
2ff057 |
}
|
|
|
2ff057 |
|
|
|
2ff057 |
/**
|
|
|
2ff057 |
* Initialize an hsa iteration.
|
|
|
2ff057 |
* @param hsa headerSprintf args
|
|
|
2ff057 |
*/
|
|
|
2ff057 |
static void hsaInit(headerSprintfArgs hsa)
|
|
|
2ff057 |
{
|
|
|
2ff057 |
sprintfTag tag =
|
|
|
2ff057 |
(hsa->format->type == PTOK_TAG
|
|
|
2ff057 |
? &hsa->format->u.tag :
|
|
|
2ff057 |
(hsa->format->type == PTOK_ARRAY
|
|
|
2ff057 |
? &hsa->format->u.array.format->u.tag :
|
|
|
2ff057 |
NULL));
|
|
|
2ff057 |
|
|
|
2ff057 |
hsa->i = 0;
|
|
|
2ff057 |
if (tag != NULL && tag->tag == -2)
|
|
|
2ff057 |
hsa->hi = headerInitIterator(hsa->h);
|
|
|
2ff057 |
/* Normally with bells and whistles enabled, but raw dump on iteration. */
|
|
|
2ff057 |
hsa->hgflags = (hsa->hi == NULL) ? HEADERGET_EXT : HEADERGET_RAW;
|
|
|
2ff057 |
}
|
|
|
2ff057 |
|
|
|
2ff057 |
/**
|
|
|
2ff057 |
* Return next hsa iteration item.
|
|
|
2ff057 |
* @param hsa headerSprintf args
|
|
|
2ff057 |
* @return next sprintfToken (or NULL)
|
|
|
2ff057 |
*/
|
|
|
2ff057 |
static sprintfToken hsaNext(headerSprintfArgs hsa)
|
|
|
2ff057 |
{
|
|
|
2ff057 |
sprintfToken fmt = NULL;
|
|
|
2ff057 |
sprintfTag tag =
|
|
|
2ff057 |
(hsa->format->type == PTOK_TAG
|
|
|
2ff057 |
? &hsa->format->u.tag :
|
|
|
2ff057 |
(hsa->format->type == PTOK_ARRAY
|
|
|
2ff057 |
? &hsa->format->u.array.format->u.tag :
|
|
|
2ff057 |
NULL));
|
|
|
2ff057 |
|
|
|
2ff057 |
if (hsa->i >= 0 && hsa->i < hsa->numTokens) {
|
|
|
2ff057 |
fmt = hsa->format + hsa->i;
|
|
|
2ff057 |
if (hsa->hi == NULL) {
|
|
|
2ff057 |
hsa->i++;
|
|
|
2ff057 |
} else {
|
|
|
2ff057 |
tag->tag = headerNextTag(hsa->hi);
|
|
|
2ff057 |
if (tag->tag == RPMTAG_NOT_FOUND)
|
|
|
2ff057 |
fmt = NULL;
|
|
|
2ff057 |
}
|
|
|
2ff057 |
}
|
|
|
2ff057 |
|
|
|
2ff057 |
return fmt;
|
|
|
2ff057 |
}
|
|
|
2ff057 |
|
|
|
2ff057 |
/**
|
|
|
2ff057 |
* Finish an hsa iteration.
|
|
|
2ff057 |
* @param hsa headerSprintf args
|
|
|
2ff057 |
*/
|
|
|
2ff057 |
static void hsaFini(headerSprintfArgs hsa)
|
|
|
2ff057 |
{
|
|
|
2ff057 |
hsa->hi = headerFreeIterator(hsa->hi);
|
|
|
2ff057 |
hsa->i = 0;
|
|
|
2ff057 |
}
|
|
|
2ff057 |
|
|
|
2ff057 |
/**
|
|
|
2ff057 |
* Reserve sufficient buffer space for next output value.
|
|
|
2ff057 |
* @param hsa headerSprintf args
|
|
|
2ff057 |
* @param need no. of bytes to reserve
|
|
|
2ff057 |
* @return pointer to reserved space
|
|
|
2ff057 |
*/
|
|
|
2ff057 |
static char * hsaReserve(headerSprintfArgs hsa, size_t need)
|
|
|
2ff057 |
{
|
|
|
2ff057 |
if ((hsa->vallen + need) >= hsa->alloced) {
|
|
|
2ff057 |
if (hsa->alloced <= need)
|
|
|
2ff057 |
hsa->alloced += need;
|
|
|
2ff057 |
hsa->alloced <<= 1;
|
|
|
2ff057 |
hsa->val = xrealloc(hsa->val, hsa->alloced+1);
|
|
|
2ff057 |
}
|
|
|
2ff057 |
return hsa->val + hsa->vallen;
|
|
|
2ff057 |
}
|
|
|
2ff057 |
|
|
|
2ff057 |
RPM_GNUC_PRINTF(2, 3)
|
|
|
2ff057 |
static void hsaError(headerSprintfArgs hsa, const char *fmt, ...)
|
|
|
2ff057 |
{
|
|
|
2ff057 |
/* Use thread local static buffer as headerFormat() errmsg arg is const */
|
|
|
2ff057 |
static __thread char errbuf[BUFSIZ];
|
|
|
2ff057 |
|
|
|
2ff057 |
if (fmt == NULL) {
|
|
|
2ff057 |
hsa->errmsg = NULL;
|
|
|
2ff057 |
} else {
|
|
|
2ff057 |
va_list ap;
|
|
|
2ff057 |
|
|
|
2ff057 |
va_start(ap, fmt);
|
|
|
2ff057 |
vsnprintf(errbuf, sizeof(errbuf), fmt, ap);
|
|
|
2ff057 |
va_end(ap);
|
|
|
2ff057 |
|
|
|
2ff057 |
hsa->errmsg = errbuf;
|
|
|
2ff057 |
}
|
|
|
2ff057 |
}
|
|
|
2ff057 |
|
|
|
2ff057 |
/**
|
|
|
2ff057 |
* Search tags for a name.
|
|
|
2ff057 |
* @param hsa headerSprintf args
|
|
|
2ff057 |
* @param token parsed fields
|
|
|
2ff057 |
* @param name name to find
|
|
|
2ff057 |
* @return 0 on success, 1 on not found
|
|
|
2ff057 |
*/
|
|
|
2ff057 |
static int findTag(headerSprintfArgs hsa, sprintfToken token, const char * name)
|
|
|
2ff057 |
{
|
|
|
2ff057 |
const char *tagname = name;
|
|
|
2ff057 |
sprintfTag stag = (token->type == PTOK_COND
|
|
|
2ff057 |
? &token->u.cond.tag : &token->u.tag);
|
|
|
2ff057 |
|
|
|
2ff057 |
stag->fmt = NULL;
|
|
|
2ff057 |
stag->tag = RPMTAG_NOT_FOUND;
|
|
|
2ff057 |
|
|
|
2ff057 |
if (!rstreq(tagname, "*")) {
|
|
|
2ff057 |
if (rstreqn("RPMTAG_", tagname, sizeof("RPMTAG_")-1)) {
|
|
|
2ff057 |
tagname += sizeof("RPMTAG");
|
|
|
2ff057 |
}
|
|
|
2ff057 |
|
|
|
2ff057 |
/* Search tag names. */
|
|
|
2ff057 |
stag->tag = rpmTagGetValue(tagname);
|
|
|
2ff057 |
if (stag->tag == RPMTAG_NOT_FOUND) return 1;
|
|
|
2ff057 |
|
|
|
2ff057 |
} else stag->tag = -2;
|
|
|
2ff057 |
|
|
|
2ff057 |
/* Search extensions for specific format. */
|
|
|
2ff057 |
if (stag->type != NULL)
|
|
|
2ff057 |
stag->fmt = rpmHeaderFormatByName(stag->type);
|
|
|
2ff057 |
|
|
|
2ff057 |
return stag->fmt ? 0 : 1;
|
|
|
2ff057 |
}
|
|
|
2ff057 |
|
|
|
2ff057 |
/* forward ref */
|
|
|
2ff057 |
/**
|
|
|
2ff057 |
* Parse an expression.
|
|
|
2ff057 |
* @param hsa headerSprintf args
|
|
|
2ff057 |
* @param token token
|
|
|
2ff057 |
* @param str string
|
|
|
2ff057 |
* @param[out] *endPtr
|
|
|
2ff057 |
* @return 0 on success
|
|
|
2ff057 |
*/
|
|
|
2ff057 |
static int parseExpression(headerSprintfArgs hsa, sprintfToken token,
|
|
|
2ff057 |
char * str,char ** endPtr);
|
|
|
2ff057 |
|
|
|
2ff057 |
/**
|
|
|
2ff057 |
* Parse a headerSprintf term.
|
|
|
2ff057 |
* @param hsa headerSprintf args
|
|
|
2ff057 |
* @param str
|
|
|
2ff057 |
* @retval *formatPtr
|
|
|
2ff057 |
* @retval *numTokensPtr
|
|
|
2ff057 |
* @retval *endPtr
|
|
|
2ff057 |
* @param state
|
|
|
2ff057 |
* @return 0 on success
|
|
|
2ff057 |
*/
|
|
|
2ff057 |
static int parseFormat(headerSprintfArgs hsa, char * str,
|
|
|
2ff057 |
sprintfToken * formatPtr,int * numTokensPtr,
|
|
|
2ff057 |
char ** endPtr, int state)
|
|
|
2ff057 |
{
|
|
|
2ff057 |
char * chptr, * start, * next, * dst;
|
|
|
2ff057 |
sprintfToken format;
|
|
|
2ff057 |
sprintfToken token;
|
|
|
2ff057 |
int numTokens;
|
|
|
2ff057 |
int done = 0;
|
|
|
2ff057 |
|
|
|
2ff057 |
/* upper limit on number of individual formats */
|
|
|
2ff057 |
numTokens = 0;
|
|
|
2ff057 |
if (str != NULL)
|
|
|
2ff057 |
for (chptr = str; *chptr != '\0'; chptr++)
|
|
|
2ff057 |
if (*chptr == '%' || *chptr == '[') numTokens++;
|
|
|
2ff057 |
numTokens = numTokens * 2 + 1;
|
|
|
2ff057 |
|
|
|
2ff057 |
format = xcalloc(numTokens, sizeof(*format));
|
|
|
2ff057 |
if (endPtr) *endPtr = NULL;
|
|
|
2ff057 |
|
|
|
2ff057 |
dst = start = str;
|
|
|
2ff057 |
numTokens = 0;
|
|
|
2ff057 |
token = NULL;
|
|
|
2ff057 |
if (start != NULL)
|
|
|
2ff057 |
while (*start != '\0') {
|
|
|
2ff057 |
switch (*start) {
|
|
|
2ff057 |
case '%':
|
|
|
2ff057 |
/* handle %% */
|
|
|
2ff057 |
if (*(start + 1) == '%') {
|
|
|
2ff057 |
if (token == NULL || token->type != PTOK_STRING) {
|
|
|
2ff057 |
token = format + numTokens++;
|
|
|
2ff057 |
token->type = PTOK_STRING;
|
|
|
2ff057 |
dst = token->u.string.string = start;
|
|
|
2ff057 |
}
|
|
|
2ff057 |
start++;
|
|
|
2ff057 |
*dst++ = *start++;
|
|
|
2ff057 |
break;
|
|
|
2ff057 |
}
|
|
|
2ff057 |
|
|
|
2ff057 |
token = format + numTokens++;
|
|
|
2ff057 |
*dst++ = '\0';
|
|
|
2ff057 |
start++;
|
|
|
2ff057 |
|
|
|
2ff057 |
if (*start == '|') {
|
|
|
2ff057 |
char * newEnd;
|
|
|
2ff057 |
|
|
|
2ff057 |
start++;
|
|
|
2ff057 |
if (parseExpression(hsa, token, start, &newEnd)) {
|
|
|
2ff057 |
goto errxit;
|
|
|
2ff057 |
}
|
|
|
2ff057 |
start = newEnd;
|
|
|
2ff057 |
break;
|
|
|
2ff057 |
}
|
|
|
2ff057 |
|
|
|
2ff057 |
token->u.tag.format = start;
|
|
|
2ff057 |
token->u.tag.justOne = 0;
|
|
|
2ff057 |
|
|
|
2ff057 |
chptr = start;
|
|
|
2ff057 |
while (*chptr && *chptr != '{' && *chptr != '%') {
|
|
|
2ff057 |
if (!risdigit(*chptr) && *chptr != '-') {
|
|
|
2ff057 |
hsaError(hsa, _("invalid field width"));
|
|
|
2ff057 |
goto errxit;
|
|
|
2ff057 |
}
|
|
|
2ff057 |
chptr++;
|
|
|
2ff057 |
}
|
|
|
2ff057 |
if (!*chptr || *chptr == '%') {
|
|
|
2ff057 |
hsaError(hsa, _("missing { after %%"));
|
|
|
2ff057 |
goto errxit;
|
|
|
2ff057 |
}
|
|
|
2ff057 |
|
|
|
2ff057 |
*chptr++ = '\0';
|
|
|
2ff057 |
|
|
|
2ff057 |
while (start < chptr) {
|
|
|
2ff057 |
start++;
|
|
|
2ff057 |
}
|
|
|
2ff057 |
|
|
|
2ff057 |
if (*start == '=') {
|
|
|
2ff057 |
token->u.tag.justOne = 1;
|
|
|
2ff057 |
start++;
|
|
|
2ff057 |
} else if (*start == '#') {
|
|
|
2ff057 |
token->u.tag.justOne = 1;
|
|
|
2ff057 |
token->u.tag.type = "arraysize";
|
|
|
2ff057 |
start++;
|
|
|
2ff057 |
}
|
|
|
2ff057 |
|
|
|
2ff057 |
dst = next = start;
|
|
|
2ff057 |
while (*next && *next != '}') next++;
|
|
|
2ff057 |
if (!*next) {
|
|
|
2ff057 |
hsaError(hsa, _("missing } after %%{"));
|
|
|
2ff057 |
goto errxit;
|
|
|
2ff057 |
}
|
|
|
2ff057 |
*next++ = '\0';
|
|
|
2ff057 |
|
|
|
2ff057 |
chptr = start;
|
|
|
2ff057 |
while (*chptr && *chptr != ':') chptr++;
|
|
|
2ff057 |
|
|
|
2ff057 |
if (*chptr != '\0') {
|
|
|
2ff057 |
*chptr++ = '\0';
|
|
|
2ff057 |
if (!*chptr) {
|
|
|
2ff057 |
hsaError(hsa, _("empty tag format"));
|
|
|
2ff057 |
goto errxit;
|
|
|
2ff057 |
}
|
|
|
2ff057 |
token->u.tag.type = chptr;
|
|
|
2ff057 |
}
|
|
|
2ff057 |
/* default to string conversion if no formats found by now */
|
|
|
2ff057 |
if (!token->u.tag.type) {
|
|
|
2ff057 |
token->u.tag.type = "string";
|
|
|
2ff057 |
}
|
|
|
2ff057 |
|
|
|
2ff057 |
if (!*start) {
|
|
|
2ff057 |
hsaError(hsa, _("empty tag name"));
|
|
|
2ff057 |
goto errxit;
|
|
|
2ff057 |
}
|
|
|
2ff057 |
|
|
|
2ff057 |
token->type = PTOK_TAG;
|
|
|
2ff057 |
|
|
|
2ff057 |
if (findTag(hsa, token, start)) {
|
|
|
2ff057 |
hsaError(hsa, _("unknown tag: \"%s\""), start);
|
|
|
2ff057 |
goto errxit;
|
|
|
2ff057 |
}
|
|
|
2ff057 |
|
|
|
2ff057 |
start = next;
|
|
|
2ff057 |
break;
|
|
|
2ff057 |
|
|
|
2ff057 |
case '[':
|
|
|
2ff057 |
*dst++ = '\0';
|
|
|
2ff057 |
*start++ = '\0';
|
|
|
2ff057 |
token = format + numTokens++;
|
|
|
2ff057 |
|
|
|
2ff057 |
if (parseFormat(hsa, start,
|
|
|
2ff057 |
&token->u.array.format,
|
|
|
2ff057 |
&token->u.array.numTokens,
|
|
|
2ff057 |
&start, PARSER_IN_ARRAY)) {
|
|
|
2ff057 |
goto errxit;
|
|
|
2ff057 |
}
|
|
|
2ff057 |
|
|
|
2ff057 |
if (!start) {
|
|
|
2ff057 |
hsaError(hsa, _("] expected at end of array"));
|
|
|
2ff057 |
goto errxit;
|
|
|
2ff057 |
}
|
|
|
2ff057 |
|
|
|
2ff057 |
dst = start;
|
|
|
2ff057 |
|
|
|
2ff057 |
token->type = PTOK_ARRAY;
|
|
|
2ff057 |
|
|
|
2ff057 |
break;
|
|
|
2ff057 |
|
|
|
2ff057 |
case ']':
|
|
|
2ff057 |
if (state != PARSER_IN_ARRAY) {
|
|
|
2ff057 |
hsaError(hsa, _("unexpected ]"));
|
|
|
2ff057 |
goto errxit;
|
|
|
2ff057 |
}
|
|
|
2ff057 |
*start++ = '\0';
|
|
|
2ff057 |
if (endPtr) *endPtr = start;
|
|
|
2ff057 |
done = 1;
|
|
|
2ff057 |
break;
|
|
|
2ff057 |
|
|
|
2ff057 |
case '}':
|
|
|
2ff057 |
if (state != PARSER_IN_EXPR) {
|
|
|
2ff057 |
hsaError(hsa, _("unexpected }"));
|
|
|
2ff057 |
goto errxit;
|
|
|
2ff057 |
}
|
|
|
2ff057 |
*start++ = '\0';
|
|
|
2ff057 |
if (endPtr) *endPtr = start;
|
|
|
2ff057 |
done = 1;
|
|
|
2ff057 |
break;
|
|
|
2ff057 |
|
|
|
2ff057 |
default:
|
|
|
2ff057 |
if (token == NULL || token->type != PTOK_STRING) {
|
|
|
2ff057 |
token = format + numTokens++;
|
|
|
2ff057 |
token->type = PTOK_STRING;
|
|
|
2ff057 |
dst = token->u.string.string = start;
|
|
|
2ff057 |
}
|
|
|
2ff057 |
|
|
|
2ff057 |
if (*start == '\\') {
|
|
|
2ff057 |
start++;
|
|
|
2ff057 |
*dst++ = escapedChar(*start++);
|
|
|
2ff057 |
} else {
|
|
|
2ff057 |
*dst++ = *start++;
|
|
|
2ff057 |
}
|
|
|
2ff057 |
break;
|
|
|
2ff057 |
}
|
|
|
2ff057 |
if (done)
|
|
|
2ff057 |
break;
|
|
|
2ff057 |
}
|
|
|
2ff057 |
|
|
|
2ff057 |
if (dst != NULL)
|
|
|
2ff057 |
*dst = '\0';
|
|
|
2ff057 |
|
|
|
2ff057 |
for (int i = 0; i < numTokens; i++) {
|
|
|
2ff057 |
token = format + i;
|
|
|
2ff057 |
if (token->type == PTOK_STRING)
|
|
|
2ff057 |
token->u.string.len = strlen(token->u.string.string);
|
|
|
2ff057 |
}
|
|
|
2ff057 |
|
|
|
2ff057 |
*numTokensPtr = numTokens;
|
|
|
2ff057 |
*formatPtr = format;
|
|
|
2ff057 |
return 0;
|
|
|
2ff057 |
|
|
|
2ff057 |
errxit:
|
|
|
2ff057 |
freeFormat(format, numTokens);
|
|
|
2ff057 |
return 1;
|
|
|
2ff057 |
}
|
|
|
2ff057 |
|
|
|
2ff057 |
static int parseExpression(headerSprintfArgs hsa, sprintfToken token,
|
|
|
2ff057 |
char * str, char ** endPtr)
|
|
|
2ff057 |
{
|
|
|
2ff057 |
char * chptr;
|
|
|
2ff057 |
char * end;
|
|
|
2ff057 |
|
|
|
2ff057 |
hsaError(hsa, NULL);
|
|
|
2ff057 |
chptr = str;
|
|
|
2ff057 |
while (*chptr && *chptr != '?') chptr++;
|
|
|
2ff057 |
|
|
|
2ff057 |
if (*chptr != '?') {
|
|
|
2ff057 |
hsaError(hsa, _("? expected in expression"));
|
|
|
2ff057 |
return 1;
|
|
|
2ff057 |
}
|
|
|
2ff057 |
|
|
|
2ff057 |
*chptr++ = '\0';;
|
|
|
2ff057 |
|
|
|
2ff057 |
if (*chptr != '{') {
|
|
|
2ff057 |
hsaError(hsa, _("{ expected after ? in expression"));
|
|
|
2ff057 |
return 1;
|
|
|
2ff057 |
}
|
|
|
2ff057 |
|
|
|
2ff057 |
chptr++;
|
|
|
2ff057 |
|
|
|
2ff057 |
if (parseFormat(hsa, chptr, &token->u.cond.ifFormat,
|
|
|
2ff057 |
&token->u.cond.numIfTokens, &end, PARSER_IN_EXPR))
|
|
|
2ff057 |
return 1;
|
|
|
2ff057 |
|
|
|
2ff057 |
/* XXX fix segfault on "rpm -q rpm --qf='%|NAME?{%}:{NAME}|\n'"*/
|
|
|
2ff057 |
if (!(end && *end)) {
|
|
|
2ff057 |
hsaError(hsa, _("} expected in expression"));
|
|
|
2ff057 |
token->u.cond.ifFormat =
|
|
|
2ff057 |
freeFormat(token->u.cond.ifFormat, token->u.cond.numIfTokens);
|
|
|
2ff057 |
return 1;
|
|
|
2ff057 |
}
|
|
|
2ff057 |
|
|
|
2ff057 |
chptr = end;
|
|
|
2ff057 |
if (*chptr != ':' && *chptr != '|') {
|
|
|
2ff057 |
hsaError(hsa, _(": expected following ? subexpression"));
|
|
|
2ff057 |
token->u.cond.ifFormat =
|
|
|
2ff057 |
freeFormat(token->u.cond.ifFormat, token->u.cond.numIfTokens);
|
|
|
2ff057 |
return 1;
|
|
|
2ff057 |
}
|
|
|
2ff057 |
|
|
|
2ff057 |
if (*chptr == '|') {
|
|
|
2ff057 |
if (parseFormat(hsa, NULL, &token->u.cond.elseFormat,
|
|
|
2ff057 |
&token->u.cond.numElseTokens, &end, PARSER_IN_EXPR))
|
|
|
2ff057 |
{
|
|
|
2ff057 |
token->u.cond.ifFormat =
|
|
|
2ff057 |
freeFormat(token->u.cond.ifFormat, token->u.cond.numIfTokens);
|
|
|
2ff057 |
return 1;
|
|
|
2ff057 |
}
|
|
|
2ff057 |
} else {
|
|
|
2ff057 |
chptr++;
|
|
|
2ff057 |
|
|
|
2ff057 |
if (*chptr != '{') {
|
|
|
2ff057 |
hsaError(hsa, _("{ expected after : in expression"));
|
|
|
2ff057 |
token->u.cond.ifFormat =
|
|
|
2ff057 |
freeFormat(token->u.cond.ifFormat, token->u.cond.numIfTokens);
|
|
|
2ff057 |
return 1;
|
|
|
2ff057 |
}
|
|
|
2ff057 |
|
|
|
2ff057 |
chptr++;
|
|
|
2ff057 |
|
|
|
2ff057 |
if (parseFormat(hsa, chptr, &token->u.cond.elseFormat,
|
|
|
2ff057 |
&token->u.cond.numElseTokens, &end, PARSER_IN_EXPR))
|
|
|
2ff057 |
return 1;
|
|
|
2ff057 |
|
|
|
2ff057 |
/* XXX fix segfault on "rpm -q rpm --qf='%|NAME?{a}:{%}|{NAME}\n'" */
|
|
|
2ff057 |
if (!(end && *end)) {
|
|
|
2ff057 |
hsaError(hsa, _("} expected in expression"));
|
|
|
2ff057 |
token->u.cond.ifFormat =
|
|
|
2ff057 |
freeFormat(token->u.cond.ifFormat, token->u.cond.numIfTokens);
|
|
|
2ff057 |
return 1;
|
|
|
2ff057 |
}
|
|
|
2ff057 |
|
|
|
2ff057 |
chptr = end;
|
|
|
2ff057 |
if (*chptr != '|') {
|
|
|
2ff057 |
hsaError(hsa, _("| expected at end of expression"));
|
|
|
2ff057 |
token->u.cond.ifFormat =
|
|
|
2ff057 |
freeFormat(token->u.cond.ifFormat, token->u.cond.numIfTokens);
|
|
|
2ff057 |
token->u.cond.elseFormat =
|
|
|
2ff057 |
freeFormat(token->u.cond.elseFormat, token->u.cond.numElseTokens);
|
|
|
2ff057 |
return 1;
|
|
|
2ff057 |
}
|
|
|
2ff057 |
}
|
|
|
2ff057 |
|
|
|
2ff057 |
chptr++;
|
|
|
2ff057 |
|
|
|
2ff057 |
*endPtr = chptr;
|
|
|
2ff057 |
|
|
|
2ff057 |
token->type = PTOK_COND;
|
|
|
2ff057 |
|
|
|
2ff057 |
(void) findTag(hsa, token, str);
|
|
|
2ff057 |
|
|
|
2ff057 |
return 0;
|
|
|
2ff057 |
}
|
|
|
2ff057 |
|
|
|
2ff057 |
static rpmtd getCached(tagCache cache, rpmTagVal tag)
|
|
|
2ff057 |
{
|
|
|
2ff057 |
rpmtd *res = NULL;
|
|
|
2ff057 |
return tagCacheGetEntry(cache, tag, &res, NULL, NULL) ? res[0] : NULL;
|
|
|
2ff057 |
}
|
|
|
2ff057 |
|
|
|
2ff057 |
/**
|
|
|
2ff057 |
* Do headerGet() just once for given tag, cache results.
|
|
|
2ff057 |
* @param hsa headerSprintf args
|
|
|
2ff057 |
* @param tag
|
|
|
2ff057 |
* @retval *typeptr
|
|
|
2ff057 |
* @retval *data
|
|
|
2ff057 |
* @retval *countptr
|
|
|
2ff057 |
* @return 1 on success, 0 on failure
|
|
|
2ff057 |
*/
|
|
|
2ff057 |
static rpmtd getData(headerSprintfArgs hsa, rpmTagVal tag)
|
|
|
2ff057 |
{
|
|
|
2ff057 |
rpmtd td = NULL;
|
|
|
2ff057 |
|
|
|
2ff057 |
if (!(td = getCached(hsa->cache, tag))) {
|
|
|
2ff057 |
td = rpmtdNew();
|
|
|
2ff057 |
if (!headerGet(hsa->h, tag, td, hsa->hgflags)) {
|
|
|
2ff057 |
rpmtdFree(td);
|
|
|
2ff057 |
return NULL;
|
|
|
2ff057 |
}
|
|
|
2ff057 |
tagCacheAddEntry(hsa->cache, tag, td);
|
|
|
2ff057 |
}
|
|
|
2ff057 |
|
|
|
2ff057 |
return td;
|
|
|
2ff057 |
}
|
|
|
2ff057 |
|
|
|
2ff057 |
/**
|
|
|
2ff057 |
* formatValue
|
|
|
2ff057 |
* @param hsa headerSprintf args
|
|
|
2ff057 |
* @param tag
|
|
|
2ff057 |
* @param element
|
|
|
2ff057 |
* @return end of formatted string (NULL on error)
|
|
|
2ff057 |
*/
|
|
|
2ff057 |
static char * formatValue(headerSprintfArgs hsa, sprintfTag tag, int element)
|
|
|
2ff057 |
{
|
|
|
2ff057 |
char * val = NULL;
|
|
|
2ff057 |
size_t need = 0;
|
|
|
2ff057 |
char * t, * te;
|
|
|
2ff057 |
rpmtd td;
|
|
|
2ff057 |
|
|
|
2ff057 |
if ((td = getData(hsa, tag->tag)) && td->count > element) {
|
|
|
2ff057 |
td->ix = element; /* Ick, use iterators instead */
|
|
|
2ff057 |
val = rpmHeaderFormatCall(tag->fmt, td);
|
|
|
2ff057 |
} else {
|
|
|
2ff057 |
val = xstrdup("(none)");
|
|
|
2ff057 |
}
|
|
|
2ff057 |
|
|
|
2ff057 |
/* Handle field width + justification formatting if specified */
|
|
|
2ff057 |
if (tag->format && *tag->format) {
|
|
|
2ff057 |
char *tval = NULL;
|
|
|
2ff057 |
/* user string + extra for '%', format char and trailing '\0' */
|
|
|
2ff057 |
char fmtbuf[strlen(tag->format) + 3];
|
|
|
2ff057 |
|
|
|
2ff057 |
sprintf(fmtbuf, "%%%ss", tag->format);
|
|
|
2ff057 |
rasprintf(&tval, fmtbuf, val);
|
|
|
2ff057 |
free(val);
|
|
|
2ff057 |
val = tval;
|
|
|
2ff057 |
}
|
|
|
2ff057 |
|
|
|
2ff057 |
need = strlen(val);
|
|
|
2ff057 |
|
|
|
2ff057 |
if (val && need > 0) {
|
|
|
2ff057 |
t = hsaReserve(hsa, need);
|
|
|
2ff057 |
te = stpcpy(t, val);
|
|
|
2ff057 |
hsa->vallen += (te - t);
|
|
|
2ff057 |
}
|
|
|
2ff057 |
free(val);
|
|
|
2ff057 |
|
|
|
2ff057 |
return (hsa->val + hsa->vallen);
|
|
|
2ff057 |
}
|
|
|
2ff057 |
|
|
|
2ff057 |
/**
|
|
|
2ff057 |
* Format a single headerSprintf item.
|
|
|
2ff057 |
* @param hsa headerSprintf args
|
|
|
2ff057 |
* @param token
|
|
|
2ff057 |
* @param element
|
|
|
2ff057 |
* @return end of formatted string (NULL on error)
|
|
|
2ff057 |
*/
|
|
|
2ff057 |
static char * singleSprintf(headerSprintfArgs hsa, sprintfToken token,
|
|
|
2ff057 |
int element)
|
|
|
2ff057 |
{
|
|
|
2ff057 |
char * t, * te;
|
|
|
2ff057 |
int i, j, found;
|
|
|
2ff057 |
rpm_count_t count, numElements;
|
|
|
2ff057 |
sprintfToken spft;
|
|
|
2ff057 |
int condNumFormats;
|
|
|
2ff057 |
size_t need;
|
|
|
2ff057 |
|
|
|
2ff057 |
/* we assume the token and header have been validated already! */
|
|
|
2ff057 |
|
|
|
2ff057 |
switch (token->type) {
|
|
|
2ff057 |
case PTOK_NONE:
|
|
|
2ff057 |
break;
|
|
|
2ff057 |
|
|
|
2ff057 |
case PTOK_STRING:
|
|
|
2ff057 |
need = token->u.string.len;
|
|
|
2ff057 |
if (need == 0) break;
|
|
|
2ff057 |
t = hsaReserve(hsa, need);
|
|
|
2ff057 |
te = stpcpy(t, token->u.string.string);
|
|
|
2ff057 |
hsa->vallen += (te - t);
|
|
|
2ff057 |
break;
|
|
|
2ff057 |
|
|
|
2ff057 |
case PTOK_TAG:
|
|
|
2ff057 |
t = hsa->val + hsa->vallen;
|
|
|
2ff057 |
te = formatValue(hsa, &token->u.tag,
|
|
|
2ff057 |
(token->u.tag.justOne ? 0 : element));
|
|
|
2ff057 |
if (te == NULL)
|
|
|
2ff057 |
return NULL;
|
|
|
2ff057 |
break;
|
|
|
2ff057 |
|
|
|
2ff057 |
case PTOK_COND:
|
|
|
2ff057 |
if (getData(hsa, token->u.cond.tag.tag) ||
|
|
|
2ff057 |
headerIsEntry(hsa->h, token->u.cond.tag.tag)) {
|
|
|
2ff057 |
spft = token->u.cond.ifFormat;
|
|
|
2ff057 |
condNumFormats = token->u.cond.numIfTokens;
|
|
|
2ff057 |
} else {
|
|
|
2ff057 |
spft = token->u.cond.elseFormat;
|
|
|
2ff057 |
condNumFormats = token->u.cond.numElseTokens;
|
|
|
2ff057 |
}
|
|
|
2ff057 |
|
|
|
2ff057 |
need = condNumFormats * 20;
|
|
|
2ff057 |
if (spft == NULL || need == 0) break;
|
|
|
2ff057 |
|
|
|
2ff057 |
t = hsaReserve(hsa, need);
|
|
|
2ff057 |
for (i = 0; i < condNumFormats; i++, spft++) {
|
|
|
2ff057 |
te = singleSprintf(hsa, spft, element);
|
|
|
2ff057 |
if (te == NULL)
|
|
|
2ff057 |
return NULL;
|
|
|
2ff057 |
}
|
|
|
2ff057 |
break;
|
|
|
2ff057 |
|
|
|
2ff057 |
case PTOK_ARRAY:
|
|
|
2ff057 |
numElements = 0;
|
|
|
2ff057 |
found = 0;
|
|
|
2ff057 |
spft = token->u.array.format;
|
|
|
2ff057 |
for (i = 0; i < token->u.array.numTokens; i++, spft++)
|
|
|
2ff057 |
{
|
|
|
2ff057 |
rpmtd td = NULL;
|
|
|
2ff057 |
if (spft->type != PTOK_TAG ||
|
|
|
2ff057 |
spft->u.tag.justOne) continue;
|
|
|
2ff057 |
|
|
|
2ff057 |
if (!(td = getData(hsa, spft->u.tag.tag))) {
|
|
|
2ff057 |
continue;
|
|
|
2ff057 |
}
|
|
|
2ff057 |
|
|
|
2ff057 |
found = 1;
|
|
|
2ff057 |
count = rpmtdCount(td);
|
|
|
2ff057 |
|
|
|
2ff057 |
if (numElements > 0 && count != numElements) {
|
|
|
2ff057 |
hsaError(hsa,
|
|
|
2ff057 |
_("array iterator used with different sized arrays"));
|
|
|
2ff057 |
return NULL;
|
|
|
2ff057 |
}
|
|
|
2ff057 |
if (count > numElements)
|
|
|
2ff057 |
numElements = count;
|
|
|
2ff057 |
}
|
|
|
2ff057 |
|
|
|
2ff057 |
if (found) {
|
|
|
2ff057 |
int isxml;
|
|
|
2ff057 |
|
|
|
2ff057 |
need = numElements * token->u.array.numTokens * 10;
|
|
|
2ff057 |
if (need == 0) break;
|
|
|
2ff057 |
|
|
|
2ff057 |
spft = token->u.array.format;
|
|
|
2ff057 |
isxml = (spft->type == PTOK_TAG && spft->u.tag.type != NULL &&
|
|
|
2ff057 |
rstreq(spft->u.tag.type, "xml"));
|
|
|
2ff057 |
|
|
|
2ff057 |
if (isxml) {
|
|
|
2ff057 |
const char * tagN = rpmTagGetName(spft->u.tag.tag);
|
|
|
2ff057 |
|
|
|
2ff057 |
need = sizeof(" <rpmTag name=\"\">\n") - 1;
|
|
|
2ff057 |
if (tagN != NULL)
|
|
|
2ff057 |
need += strlen(tagN);
|
|
|
2ff057 |
t = hsaReserve(hsa, need);
|
|
|
2ff057 |
te = stpcpy(t, "
|
|
|
2ff057 |
if (tagN != NULL)
|
|
|
2ff057 |
te = stpcpy(te, tagN);
|
|
|
2ff057 |
te = stpcpy(te, "\">\n");
|
|
|
2ff057 |
hsa->vallen += (te - t);
|
|
|
2ff057 |
}
|
|
|
2ff057 |
|
|
|
2ff057 |
t = hsaReserve(hsa, need);
|
|
|
2ff057 |
for (j = 0; j < numElements; j++) {
|
|
|
2ff057 |
spft = token->u.array.format;
|
|
|
2ff057 |
for (i = 0; i < token->u.array.numTokens; i++, spft++) {
|
|
|
2ff057 |
te = singleSprintf(hsa, spft, j);
|
|
|
2ff057 |
if (te == NULL)
|
|
|
2ff057 |
return NULL;
|
|
|
2ff057 |
}
|
|
|
2ff057 |
}
|
|
|
2ff057 |
|
|
|
2ff057 |
if (isxml) {
|
|
|
2ff057 |
need = sizeof(" </rpmTag>\n") - 1;
|
|
|
2ff057 |
t = hsaReserve(hsa, need);
|
|
|
2ff057 |
te = stpcpy(t, " </rpmTag>\n");
|
|
|
2ff057 |
hsa->vallen += (te - t);
|
|
|
2ff057 |
}
|
|
|
2ff057 |
|
|
|
2ff057 |
}
|
|
|
2ff057 |
break;
|
|
|
2ff057 |
}
|
|
|
2ff057 |
|
|
|
2ff057 |
return (hsa->val + hsa->vallen);
|
|
|
2ff057 |
}
|
|
|
2ff057 |
|
|
|
2ff057 |
static int tagCmp(rpmTagVal a, rpmTagVal b)
|
|
|
2ff057 |
{
|
|
|
2ff057 |
return (a != b);
|
|
|
2ff057 |
}
|
|
|
2ff057 |
|
|
|
2ff057 |
static unsigned int tagId(rpmTagVal tag)
|
|
|
2ff057 |
{
|
|
|
2ff057 |
return tag;
|
|
|
2ff057 |
}
|
|
|
2ff057 |
|
|
|
2ff057 |
char * headerFormat(Header h, const char * fmt, errmsg_t * errmsg)
|
|
|
2ff057 |
{
|
|
|
2ff057 |
struct headerSprintfArgs_s hsa;
|
|
|
2ff057 |
sprintfToken nextfmt;
|
|
|
2ff057 |
sprintfTag tag;
|
|
|
2ff057 |
char * t, * te;
|
|
|
2ff057 |
int isxml;
|
|
|
2ff057 |
size_t need;
|
|
|
2ff057 |
|
|
|
2ff057 |
memset(&hsa, 0, sizeof(hsa));
|
|
|
2ff057 |
hsa.h = headerLink(h);
|
|
|
2ff057 |
hsa.fmt = xstrdup(fmt);
|
|
|
2ff057 |
hsa.errmsg = NULL;
|
|
|
2ff057 |
|
|
|
2ff057 |
if (parseFormat(&hsa, hsa.fmt, &hsa.format, &hsa.numTokens, NULL, PARSER_BEGIN))
|
|
|
2ff057 |
goto exit;
|
|
|
2ff057 |
|
|
|
2ff057 |
hsa.cache = tagCacheCreate(128, tagId, tagCmp, NULL, rpmtdFree);
|
|
|
2ff057 |
hsa.val = xstrdup("");
|
|
|
2ff057 |
|
|
|
2ff057 |
tag =
|
|
|
2ff057 |
(hsa.format->type == PTOK_TAG
|
|
|
2ff057 |
? &hsa.format->u.tag :
|
|
|
2ff057 |
(hsa.format->type == PTOK_ARRAY
|
|
|
2ff057 |
? &hsa.format->u.array.format->u.tag :
|
|
|
2ff057 |
NULL));
|
|
|
2ff057 |
isxml = (tag != NULL && tag->tag == -2 && tag->type != NULL && rstreq(tag->type, "xml"));
|
|
|
2ff057 |
|
|
|
2ff057 |
if (isxml) {
|
|
|
2ff057 |
need = sizeof("<rpmHeader>\n") - 1;
|
|
|
2ff057 |
t = hsaReserve(&hsa, need);
|
|
|
2ff057 |
te = stpcpy(t, "<rpmHeader>\n");
|
|
|
2ff057 |
hsa.vallen += (te - t);
|
|
|
2ff057 |
}
|
|
|
2ff057 |
|
|
|
2ff057 |
hsaInit(&hsa;;
|
|
|
2ff057 |
while ((nextfmt = hsaNext(&hsa)) != NULL) {
|
|
|
2ff057 |
te = singleSprintf(&hsa, nextfmt, 0);
|
|
|
2ff057 |
if (te == NULL) {
|
|
|
2ff057 |
hsa.val = _free(hsa.val);
|
|
|
2ff057 |
break;
|
|
|
2ff057 |
}
|
|
|
2ff057 |
}
|
|
|
2ff057 |
hsaFini(&hsa;;
|
|
|
2ff057 |
|
|
|
2ff057 |
if (isxml) {
|
|
|
2ff057 |
need = sizeof("</rpmHeader>\n") - 1;
|
|
|
2ff057 |
t = hsaReserve(&hsa, need);
|
|
|
2ff057 |
te = stpcpy(t, "</rpmHeader>\n");
|
|
|
2ff057 |
hsa.vallen += (te - t);
|
|
|
2ff057 |
}
|
|
|
2ff057 |
|
|
|
2ff057 |
if (hsa.val != NULL && hsa.vallen < hsa.alloced)
|
|
|
2ff057 |
hsa.val = xrealloc(hsa.val, hsa.vallen+1);
|
|
|
2ff057 |
|
|
|
2ff057 |
hsa.cache = tagCacheFree(hsa.cache);
|
|
|
2ff057 |
hsa.format = freeFormat(hsa.format, hsa.numTokens);
|
|
|
2ff057 |
|
|
|
2ff057 |
exit:
|
|
|
2ff057 |
if (errmsg)
|
|
|
2ff057 |
*errmsg = hsa.errmsg;
|
|
|
2ff057 |
hsa.h = headerFree(hsa.h);
|
|
|
2ff057 |
hsa.fmt = _free(hsa.fmt);
|
|
|
2ff057 |
return hsa.val;
|
|
|
2ff057 |
}
|
|
|
2ff057 |
|