From 99d25e4d5df543b5d90181ebc3a7da44a1a1df6b Mon Sep 17 00:00:00 2001 From: Michael Schroeder Date: Fri, 12 Sep 2014 16:50:28 +0200 Subject: [PATCH] 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. --- lib/rpmds.c | 152 ++++++++++++++++++++++++++++++++++++++++++++++++++++ lib/rpmds.h | 36 +++++++++++++ 2 files changed, 188 insertions(+) diff --git a/lib/rpmds.c b/lib/rpmds.c index 6dcb2a8fa..46ac2e713 100644 --- a/lib/rpmds.c +++ b/lib/rpmds.c @@ -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; +} diff --git a/lib/rpmds.h b/lib/rpmds.h index a362ca12d..2919f7245 100644 --- a/lib/rpmds.h +++ b/lib/rpmds.h @@ -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