Add a parser for rich dependencies

The data is returned via a callback, this allows us to use it
for different purposes.

Rich dependencies are for the form "(arg)" or "(arg1 op arg2)",
the arguments can either be simple dependencies, or again
rich dependencies.

We don't want to enforce a space between the argument and the
closing ')', thus we check the number of parens when parsing
the argument and stop when we encounter an unbalanced ')'
character.
This commit is contained in:
Michael Schroeder 2014-09-12 16:50:28 +02:00
parent 21a17daa1b
commit 99d25e4d5d
2 changed files with 188 additions and 0 deletions

View File

@ -1299,3 +1299,155 @@ rpmsenseFlags rpmParseDSFlags(const char *str, size_t len)
return rc->sense;
return 0;
}
static struct RichOpComp {
const char * token;
rpmrichOp op;
} const RichOps[] = {
{ "&", RPMRICHOP_AND},
{ "&&", RPMRICHOP_AND},
{ "AND", RPMRICHOP_AND},
{ "|", RPMRICHOP_OR},
{ "||", RPMRICHOP_OR},
{ "OR", RPMRICHOP_OR},
{ "IF", RPMRICHOP_IF},
{ NULL, 0 },
};
static rpmRC parseRichDepOp(const char **dstrp, rpmrichOp *opp, char **emsg)
{
const char *p = *dstrp, *pe = p;
const struct RichOpComp *ro;
while (*pe && !risspace(*pe) && *pe != ')')
pe++;
for (ro = RichOps; ro->token != NULL; ro++)
if (pe - p == strlen(ro->token) && rstreqn(p, ro->token, pe - p)) {
*opp = ro->op;
*dstrp = pe;
return RPMRC_OK;
}
if (emsg)
rasprintf(emsg, _("Unknown rich dependency op '%.*s'"), (int)(pe - p), p);
return RPMRC_FAIL;
}
const char *rpmrichOpStr(rpmrichOp op)
{
if (op == RPMRICHOP_SINGLE)
return "SINGLE";
if (op == RPMRICHOP_AND)
return "&";
if (op == RPMRICHOP_OR)
return "|";
if (op == RPMRICHOP_IF)
return "IF";
return NULL;
}
#define SKIPWHITE(_x) {while(*(_x) && (risspace(*_x) || *(_x) == ',')) (_x)++;}
#define SKIPNONWHITEX(_x){int bl = 0; while(*(_x) &&!(risspace(*_x) || *(_x) == ',' || (*(_x) == ')' && bl-- <= 0))) if (*(_x)++ == '(') bl++;}
static rpmRC parseSimpleDep(const char **dstrp, char **emsg, rpmrichParseFunction cb, void *cbdata)
{
const char *p = *dstrp;
const char *n, *e = 0;
int nl, el = 0;
rpmsenseFlags sense = 0;
n = p;
SKIPNONWHITEX(p);
nl = p - n;
if (nl == 0) {
if (emsg)
rasprintf(emsg, _("Name required"));
return RPMRC_FAIL;
}
SKIPWHITE(p);
if (*p) {
const char *pe = p;
SKIPNONWHITEX(pe);
sense = rpmParseDSFlags(p, pe - p);
if (sense) {
p = pe;
SKIPWHITE(p);
e = p;
SKIPNONWHITEX(p);
el = p - e;
}
}
if (e && el == 0) {
if (emsg)
rasprintf(emsg, _("Version required"));
return RPMRC_FAIL;
}
if (cb(cbdata, RPMRICH_PARSE_SIMPLE, n, nl, e, el, sense, RPMRICHOP_SINGLE, emsg) != RPMRC_OK)
return RPMRC_FAIL;
*dstrp = p;
return RPMRC_OK;
}
rpmRC rpmrichParse(const char **dstrp, char **emsg, rpmrichParseFunction cb, void *cbdata)
{
const char *p = *dstrp, *pe;
rpmrichOp op = RPMRICHOP_SINGLE, chainop = 0;
if (cb(cbdata, RPMRICH_PARSE_ENTER, p, 0, 0, 0, 0, op, emsg) != RPMRC_OK)
return RPMRC_FAIL;
if (*p++ != '(') {
if (emsg)
rasprintf(emsg, _("Rich dependency does not start with '('"));
return RPMRC_FAIL;
}
for (;;) {
SKIPWHITE(p);
if (*p == ')') {
if (emsg) {
if (chainop)
rasprintf(emsg, _("Missing argument to rich dependency op"));
else
rasprintf(emsg, _("Empty rich dependency"));
}
return RPMRC_FAIL;
}
if (*p == '(') {
if (rpmrichParse(&p, emsg, cb, cbdata) != RPMRC_OK)
return RPMRC_FAIL;
} else {
if (parseSimpleDep(&p, emsg, cb, cbdata) != RPMRC_OK)
return RPMRC_FAIL;
}
SKIPWHITE(p);
if (!*p) {
if (emsg)
rasprintf(emsg, _("Unterminated rich dependency: %s"), *dstrp);
return RPMRC_FAIL;
}
if (*p == ')')
break;
pe = p;
if (parseRichDepOp(&pe, &op, emsg) != RPMRC_OK)
return RPMRC_FAIL;
if (chainop && op != chainop) {
if (emsg)
rasprintf(emsg, _("Cannot chain different ops"));
return RPMRC_FAIL;
}
if (chainop == RPMRICHOP_IF) {
if (emsg)
rasprintf(emsg, _("Cannot chain IF ops"));
return RPMRC_FAIL;
}
chainop = op;
if (cb(cbdata, RPMRICH_PARSE_OP, p, pe - p, 0, 0, 0, op, emsg) != RPMRC_OK)
return RPMRC_FAIL;
p = pe;
}
p++;
if (cb(cbdata, RPMRICH_PARSE_LEAVE, *dstrp, p - *dstrp , 0, 0, 0, op, emsg) != RPMRC_OK)
return RPMRC_FAIL;
*dstrp = p;
return RPMRC_OK;
}

View File

@ -463,6 +463,42 @@ rpmds rpmdsSinglePoolTix(rpmstrPool pool, rpmTagVal tagN,
*/
int rpmdsRpmlibPool(rpmstrPool pool, rpmds * dsp, const void * tblp);
typedef enum rpmrichOp_e {
RPMRICHOP_SINGLE = 1,
RPMRICHOP_AND = 2,
RPMRICHOP_OR = 3,
RPMRICHOP_IF = 4
} rpmrichOp;
typedef enum rpmrichParseType_e {
RPMRICH_PARSE_SIMPLE = 1, /* standard N <=> EVR dep */
RPMRICH_PARSE_ENTER = 2, /* entering sub-dependency */
RPMRICH_PARSE_LEAVE = 3, /* leaving sub-dependency */
RPMRICH_PARSE_OP = 4 /* parsed a rich dependency op */
} rpmrichParseType;
typedef rpmRC (*rpmrichParseFunction) (void *cbdata, rpmrichParseType type,
const char *n, int nl, const char *e, int el, rpmsenseFlags sense,
rpmrichOp op, char **emsg);
/**
* Parse a rich dependency string
* @param dstrp pointer to sting, will be updated
* @param emsg returns the error string, can be NULL
* @param cb callback function
* @param cbdata callback function data
* @return RPMRC_OK on success
*/
rpmRC rpmrichParse(const char **dstrp, char **emsg, rpmrichParseFunction cb, void *cbdata);
/**
* Return a string representation of the rich dependency op
* @param op the dependency op
* @return constant string, do not free
*/
const char *rpmrichOpStr(rpmrichOp op);
#ifdef __cplusplus
}
#endif