[Rpm-maint] First experiments with complex dependencies

Michael Schroeder mls at suse.de
Mon Aug 4 16:47:37 UTC 2014


Hi rpm-maint,

attached is a hackish version that implements complex dependencies
for rpm. It's just meant as starting point for a real implementation,
so don't be too cruel on the code. It seems to kind of work,
although I haven't done excessive testing.

Complex dependencies are encoded as simple string in the name
field, and with RPMSENSE_COMPLEX set. The patch insists on all
complex deps starting with '(' and ending with ')', so that there
can be no compatibility issue with old dependencies.

Word parsing in a complex dependency is slightly different: a ')'
with no matching '(' terminates the word. That's so that people
can write "Requires: (A OR B)" and don't need extra spaces.

I implemented the AND, OR, and IF operator, multiple AND and OR
ops may be chained (but you may not mix ops, e.g. (A AND B AND C)
is ok, but (A AND B OR C) needs extra parens).

I'm not very proud of having three different parsers for complex
deps (in rpmds.c, parseReqs.c, and rpmdb.c), this is something
that really should be unified.

Cheers,
  Michael.

-- 
Michael Schroeder                                   mls at suse.de
SUSE LINUX Products GmbH,  GF Jeff Hawn, HRB 16746 AG Nuernberg
main(_){while(_=~getchar())putchar(~_-1/(~(_|32)/13*2-11)*13);}
-------------- next part --------------
diff --git a/build/parseReqs.c b/build/parseReqs.c
index 1427111..e63c974 100644
--- a/build/parseReqs.c
+++ b/build/parseReqs.c
@@ -32,8 +32,20 @@ const char * token;
     { NULL, 0 },
 };
 
+static struct OpComp {
+const char * token;
+    int op;
+    int chainable;
+} const OpComparisons[] = {
+    { "AND", 1, 1 },
+    { "OR", 2, 1 },
+    { "IF", 3, 0 },
+    { NULL, 0, 0 },
+};
+
 #define	SKIPWHITE(_x)	{while(*(_x) && (risspace(*_x) || *(_x) == ',')) (_x)++;}
 #define	SKIPNONWHITE(_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 int checkSep(const char *s, char c, char **emsg)
 {
@@ -45,6 +57,209 @@ static int checkSep(const char *s, char c, char **emsg)
     return 0;
 }
 
+struct ComplexDep {
+    /* simple literal */
+    char *N;
+    char *EVR;
+    rpmsenseFlags Flags;
+    /* complex literal */
+    struct ComplexDep *lit;
+
+    int op;
+    struct ComplexDep *oparg;
+};
+
+static void freeComplexDep(struct ComplexDep *cd)
+{
+    _free(cd->N);
+    _free(cd->EVR);
+    if (cd->lit)
+        freeComplexDep(cd->lit);
+    if (cd->oparg)
+        freeComplexDep(cd->oparg);
+    free(cd);
+}
+
+static char *strjoin(char *s, char *s2)
+{
+    char *ns = xmalloc(strlen(s) + strlen(s2) + 1);
+    strcpy(ns, s);
+    strcat(ns, s2);
+    free(s);
+    return ns;
+}
+
+static char *formatComplexDep(struct ComplexDep *cd)
+{
+    int oldop = 0;
+    char *s = strdup("(");
+    while (cd) {
+	if (cd->lit) {
+	    char *ns = formatComplexDep(cd->lit);
+	    s = strjoin(s, ns);
+	    free(ns);
+	} else {
+	    s = strjoin(s, cd->N);
+	    if (cd->EVR) {
+		s = strjoin(s, " ");
+		if (cd->Flags & RPMSENSE_LESS)
+		    s = strjoin(s, "<");
+		if (cd->Flags & RPMSENSE_EQUAL)
+		    s = strjoin(s, "=");
+		if (cd->Flags & RPMSENSE_GREATER)
+		    s = strjoin(s, ">");
+		s = strjoin(s, " ");
+		s = strjoin(s, cd->EVR);
+	    }
+	}
+	if (!cd->op)
+	    break;
+	if (cd->op == 1)
+	    s = strjoin(s, " AND ");
+	if (cd->op == 2)
+	    s = strjoin(s, " OR ");
+	if (cd->op == 3)
+	    s = strjoin(s, " IF ");
+	if (!oldop || (oldop != 3 && cd->op == oldop)) {
+	    oldop = cd->op;
+	    cd = cd->oparg;
+	} else {
+	    char *ns = formatComplexDep(cd->oparg);
+	    s = strjoin(s, ns);
+	    free(ns);
+	    cd = 0;
+	}
+    }
+    s = strjoin(s, ")");
+    return s;
+}
+
+struct ComplexDep *parseComplexDep(rpmSpec spec, const char **rp, char **emsg, int oldop)
+{
+    struct ComplexDep *cd;
+    const char *r, *re, *v, *ve;
+
+    cd = xmalloc(sizeof(*cd));
+    memset(cd, 0, sizeof(*cd));
+
+    r = *rp;
+    SKIPWHITE(r);
+    if (*r == '(') {
+        r++;
+        cd->lit = parseComplexDep(spec, &r, emsg, 0);
+	if (!cd->lit) {
+	    freeComplexDep(cd);
+	    return 0;
+	}
+    } else {
+        re = r;
+        SKIPNONWHITEX(re);
+        cd->N = xmalloc((re-r) + 1);
+        rstrlcpy(cd->N, r, (re-r) + 1);
+
+        /* Parse EVR */
+        v = re;
+        SKIPWHITE(v);
+        ve = v;
+        SKIPNONWHITE(ve);
+
+        re = v; /* ==> next token (if no EVR found) starts here */
+
+        /* Check for possible logical operator */
+        if (ve > v) {
+          const struct ReqComp *rc;
+          for (rc = ReqComparisons; rc->token != NULL; rc++) {
+            if ((ve-v) != strlen(rc->token) || !rstreqn(v, rc->token, (ve-v)))
+                continue;
+
+            if (r[0] == '/') {
+                rasprintf(emsg, _("Versioned file name not permitted"));
+                freeComplexDep(cd);
+		return 0;
+            }
+
+            cd->Flags |= rc->sense;
+
+            /* now parse EVR */
+            v = ve;
+            SKIPWHITE(v);
+            ve = v;
+            SKIPNONWHITEX(ve);
+            break;
+          }
+        }
+
+        if (cd->Flags & RPMSENSE_SENSEMASK) {
+            if (*v == '\0' || ve == v) {
+                rasprintf(emsg, _("Version required"));
+                freeComplexDep(cd);
+                return 0;
+            }
+            cd->EVR = xmalloc((ve-v) + 1);
+            rstrlcpy(cd->EVR, v, (ve-v) + 1);
+            if (rpmCharCheck(spec, cd->EVR, ve-v, ".-_+:%{}~")) {
+                freeComplexDep(cd);
+                return 0;
+            }
+
+            /* While ':' and '-' are valid, only one of each is valid. */
+            if (checkSep(cd->EVR, '-', emsg) || checkSep(cd->EVR, ':', emsg)) {
+                freeComplexDep(cd);
+                return 0;
+            }
+
+            re = ve;    /* ==> next token after EVR string starts here */
+        }
+        r = re;
+    }
+    SKIPWHITE(r);
+    if (*r && *r != ')') {
+        const struct OpComp *oc;
+        v = r;
+        ve = v;
+        SKIPNONWHITE(ve);
+        for (oc = OpComparisons; oc->token != NULL; oc++) {
+            if ((ve-v) != strlen(oc->token) || !rstreqn(v, oc->token, (ve-v)))
+                continue;
+            break;
+        }
+        if (oc->token == NULL) {
+            rasprintf(emsg, "unknown op '%.*s'", (int)(ve - v), v);
+            freeComplexDep(cd);
+	    return 0;
+        }
+        if (oldop && oc->op != oldop) {
+            rasprintf(emsg, "cannot chain different ops");
+            freeComplexDep(cd);
+	    return 0;
+        }
+        if (oldop && !oc->chainable) {
+            rasprintf(emsg, "cannot chain op");
+            freeComplexDep(cd);
+	    return 0;
+        }
+        cd->op = oc->op;
+        r = ve;
+        cd->oparg = parseComplexDep(spec, &r, emsg, cd->op);
+	if (!cd->oparg) {
+            freeComplexDep(cd);
+	    return 0;
+	}
+    }
+    SKIPWHITE(r);
+    if (!cd->op) {
+        if (*r != ')') {
+            rasprintf(emsg, "unterminated");
+            freeComplexDep(cd);
+	    return 0;
+        }
+        r++;
+    }
+    *rp = r;
+    return cd;
+}
+
+
 rpmRC parseRCPOT(rpmSpec spec, Package pkg, const char *field, rpmTagVal tagN,
 	       int index, rpmsenseFlags tagflags)
 {
@@ -123,6 +338,23 @@ rpmRC parseRCPOT(rpmSpec spec, Package pkg, const char *field, rpmTagVal tagN,
 
 	Flags = (tagflags & ~RPMSENSE_SENSEMASK);
 
+	if (r[0] == '(') {
+	    struct ComplexDep *cd;
+	    char *cdf;
+	    r++;
+            cd = parseComplexDep(spec, &r, &emsg, 0); 
+            if (!cd) {
+                goto exit;
+            }
+            re = r;
+	    cdf = formatComplexDep(cd);
+	    if (addReqProv(pkg, nametag, cdf, NULL, Flags | RPMSENSE_COMPLEX, index)) {
+		rasprintf(&emsg, _("invalid dependency"));
+		goto exit;
+	    }
+	    _free(cdf);
+            continue;
+	}
 	/* 
 	 * Tokens must begin with alphanumeric, _, or /, but we don't know
 	 * the spec's encoding so we only check what we can: plain ascii.
diff --git a/lib/depends.c b/lib/depends.c
index 7fd3986..b8a6bf1 100644
--- a/lib/depends.c
+++ b/lib/depends.c
@@ -619,6 +619,42 @@ retry:
     if (!adding && isInstallPreReq(dsflags) && !isErasePreReq(dsflags))
 	goto exit;
 
+    if (dsflags & RPMSENSE_COMPLEX) {
+        rpmds ds1, ds2; 
+	char *emsg = 0;
+        int op = rpmdsParseCplxDep(dep, &ds1, &ds2, &emsg);
+	if (op == RPMCPLXDEPOP_ERROR) {
+            if (emsg) {
+                rpmdsNotify(dep, emsg, 1); 
+                free(emsg);
+            }
+            goto unsatisfied;
+        }
+	/* an IF on a conflict is actually just an AND */
+	if (op == RPMCPLXDEPOP_IF && rpmdsTagN(dep) == RPMTAG_CONFLICTNAME)
+	    op = RPMCPLXDEPOP_AND;
+        if (op == RPMCPLXDEPOP_IF) {
+            rc = unsatisfiedDepend(ts, dcache, ds2);
+            if (rc) {
+                ds1 = rpmdsFree(ds1);
+                ds2 = rpmdsFree(ds2);
+                rc = 0;
+		rpmdsNotify(dep, "(complex)", 0);
+                goto exit;
+            }
+        }
+        rc = unsatisfiedDepend(ts, dcache, ds1);
+        if ((rc && op == RPMCPLXDEPOP_OR) || (!rc && op == RPMCPLXDEPOP_AND)) {
+            rc = unsatisfiedDepend(ts, dcache, ds2);
+        }
+        ds1 = rpmdsFree(ds1);
+        ds2 = rpmdsFree(ds2);
+        if (rc) 
+            goto unsatisfied;
+	rpmdsNotify(dep, "(complex)", 0);
+        goto exit;
+    }
+
     /* Pretrans dependencies can't be satisfied by added packages. */
     if (!(dsflags & RPMSENSE_PRETRANS)) {
 	rpmte *matches = rpmalAllSatisfiesDepend(tsmem->addedPackages, dep);
@@ -670,7 +706,10 @@ unsatisfied:
     } else {
 	/* dependency is unsatisfied */
 	rc = 1;
-	rpmdsNotify(dep, NULL, rc);
+	if (dsflags & RPMSENSE_COMPLEX)
+	    rpmdsNotify(dep, "(complex)", rc);
+	else
+	    rpmdsNotify(dep, NULL, rc);
     }
 
 exit:
@@ -689,7 +728,7 @@ static void checkDS(rpmts ts, depCache dcache, rpmte te,
     ds = rpmdsInit(ds);
     while (rpmdsNext(ds) >= 0) {
 	/* Filter out dependencies that came along for the ride. */
-	if (depName != NULL && !rstreq(depName, rpmdsN(ds)))
+	if (depName != NULL && !rstreq(depName, rpmdsN(ds)) && !(rpmdsFlags(ds) & RPMSENSE_COMPLEX))
 	    continue;
 
 	/* Ignore colored dependencies not in our rainbow. */
diff --git a/lib/rpmdb.c b/lib/rpmdb.c
index b6d3247..295ec7f 100644
--- a/lib/rpmdb.c
+++ b/lib/rpmdb.c
@@ -2032,6 +2032,74 @@ int rpmdbRemove(rpmdb db, unsigned int hdrNum)
     return 0;
 }
 
+static const char *collectComplexDep(const char *p, ARGV_t *argvp)
+{
+    p++;
+    for (;;) {
+	while (risspace(*p))
+	    p++;
+	if (!*p)
+	    return p;
+	if (*p == '(') {
+	    p = collectComplexDep(p, argvp);
+	    if (*p != ')')
+		return p;
+	    p++;
+	} else if (*p == ')') {
+	    return p;
+	} else {
+	    const char *ps = p;
+	    int bl = 0;
+	    while ((*p) && !(risspace(*p) || (*p == ')' && bl-- <= 0)))
+		if (*p++ == '(')
+		    bl++;
+	    if (p > ps) {
+		char *name = rstrdup(ps);
+		name[p - ps] = 0;
+		argvAdd(argvp, name);
+		free(name);
+	    }
+	    while (risspace(*p))
+		p++;
+	    if (*p == '>' || *p == '<' || *p == '=') {
+		while (*p == '>' || *p == '<' || *p == '=')
+		    p++;
+		while (risspace(*p))
+		    p++;
+		while ((*p) && !(risspace(*p) || (*p == ')' && bl-- <= 0)))
+		    if (*p++ == '(')
+			bl++;
+	    }
+	    while (risspace(*p))
+		p++;
+	    if (*p && *p != ')') {
+		while (*p && !risspace(*p) && *p != ')')
+		    p++;
+	    }
+	}
+    }
+}
+
+static rpmRC updatecplxdep(dbiCursor dbc, const char *str,
+                           struct dbiIndexItem_s *rec,
+                           idxfunc idxupdate)
+{
+    int n, i, rc = 0;
+    ARGV_t argv = argvNew();
+    collectComplexDep(str, &argv);
+    n = argvCount(argv);
+    if (n) {
+	argvSort(argv, NULL);
+	for (i = 0; i < n; i++) {
+	    if (i && !strcmp(argv[i - 1], argv[i]))
+		continue;	/* ignore dups */
+	    rc += idxupdate(dbc, argv[i], strlen(argv[i]), rec);
+	}
+    }
+    argvFree(argv);
+    return rc;
+}
+
 static rpmRC tag2index(dbiIndex dbi, rpmTagVal rpmtag,
 		       unsigned int hdrNum, Header h,
 		       idxfunc idxupdate)
@@ -2098,6 +2166,13 @@ static rpmRC tag2index(dbiIndex dbi, rpmTagVal rpmtag,
 	    continue;
 
 	rc += idxupdate(dbc, key, keylen, &rec);
+
+	if ((rpmtag == RPMTAG_REQUIRENAME || rpmtag == RPMTAG_CONFLICTNAME) && *(char *)key == '(') {
+	    if (rpmtdType(&tagdata) == RPM_STRING_ARRAY_TYPE) {
+		const char *str = rpmtdGetString(&tagdata);
+		rc += updatecplxdep(dbc, str, &rec, idxupdate);
+	    }
+	}
     }
 
     dbiCursorFree(dbc);
diff --git a/lib/rpmds.c b/lib/rpmds.c
index e1a6315..94967f2 100644
--- a/lib/rpmds.c
+++ b/lib/rpmds.c
@@ -1251,10 +1251,252 @@ rpmFlags rpmSanitizeDSFlags(rpmTagVal tagN, rpmFlags Flags)
     case RPMTAG_SUPPLEMENTNAME:
     case RPMTAG_ENHANCENAME:
     case RPMTAG_REQUIRENAME:
-	extra = Flags & _ALL_REQUIRES_MASK;
+	extra = Flags & (_ALL_REQUIRES_MASK | RPMSENSE_COMPLEX);
+	break;
+    case RPMTAG_CONFLICTNAME:
+	extra = Flags & RPMSENSE_COMPLEX;
 	break;
     default:
 	break;
     }
     return (Flags & RPMSENSE_SENSEMASK) | extra;
 }
+
+static const char *skipComplexDep(const char *p)
+{
+    p++; 
+    for (;;) {
+        while (risspace(*p))
+            p++;
+        if (!*p)
+            return p;
+        if (*p == '(') {
+            p = skipComplexDep(p);
+            if (*p != ')') 
+                return p;
+	    p++;
+        } else if (*p == ')') {
+            return p;
+        } else {
+            int bl = 0; 
+            while ((*p) && !(risspace(*p) || (*p == ')' && bl-- <= 0)))
+                if (*p++ == '(') 
+                    bl++;
+        }
+    }
+}
+
+static const char *makeSimpleDep(rpmds ods, const char *dstr, rpmds *dsp)
+{
+    const char *n, *ne, *e = 0, *ee = 0; 
+    int flags = 0; 
+    rpmds ds;
+    const char *p = dstr;
+    int bl;
+
+    bl = 0; 
+    while ((*p) && !(risspace(*p) || (*p == ')' && bl-- <= 0)))
+        if (*p++ == '(') 
+            bl++;
+    n = dstr;
+    ne = p;
+    while (risspace(*p))
+        p++;
+    for (;; p++) {
+        if (*p == '>')
+            flags |= RPMSENSE_GREATER;
+        else if (*p == '=')
+            flags |= RPMSENSE_EQUAL;
+        else if (*p == '<')
+            flags |= RPMSENSE_LESS;
+        else
+            break;
+    }
+    if (flags) {
+        while (risspace(*p))
+            p++;
+        e = p;
+        while ((*p) && !(risspace(*p) || (*p == ')' && bl-- <= 0)))
+            if (*p++ == '(')
+                bl++;
+        ee = p;
+    }
+    flags |= rpmdsFlags(ods) & ~(RPMSENSE_SENSEMASK | RPMSENSE_COMPLEX);
+    ds = singleDS(ods->pool, ods->tagN, 0, 0, flags, 0, 0);
+    if (ds) {
+	if (!e)
+	    e = ee = n;
+	ds->N[0] = rpmstrPoolIdn(ds->pool, n, ne - n, 1);
+	ds->EVR[0] = rpmstrPoolIdn(ds->pool, e, ee - e, 1);
+	if (ds->pool != ods->pool)
+	    rpmstrPoolFreeze(ds->pool, 0);
+    }
+    *dsp = ds;
+    return p;
+}
+
+
+rpmCplxDepOp rpmdsParseCplxDep(rpmds dep, rpmds *leftds, rpmds *rightds, char **emsg)
+{
+    rpmCplxDepOp op = 0, chainop = 0;
+    const char *chainstart;
+    const char *dstr = rpmdsN(dep);
+    const char *p = dstr;
+    rpmsenseFlags flags = rpmdsFlags(dep);
+    rpmds ds;
+    const char *pe;
+
+    *leftds = *rightds = 0;
+    if (*p++ != '(') {
+	if (emsg)
+	    rasprintf(emsg, "Complex dependency does not start with '('");
+        return RPMCPLXDEPOP_ERROR;
+    }
+    while (risspace(*p))
+        p++;
+    if (*p == '(') {
+        /* sub-dependency */
+        pe = skipComplexDep(p);
+        if (*pe != ')') {
+	    if (emsg)
+		rasprintf(emsg, "Unterminated complex dependency: '%s'", p);
+	    return RPMCPLXDEPOP_ERROR;
+        }
+        pe++;
+	ds = singleDS(dep->pool, dep->tagN, 0, 0, flags, 0, 0);
+	if (ds) {
+	    ds->N[0] = rpmstrPoolIdn(ds->pool, p, pe - p, 1);
+	    ds->EVR[0] = rpmstrPoolId(ds->pool, "", 1);
+	}
+	*leftds = ds;
+	p = pe;
+    } else if (*p == ')') {
+	if (emsg)
+	    rasprintf(emsg, "Empty complex dependency");
+	return RPMCPLXDEPOP_ERROR;
+    } else {
+	p = makeSimpleDep(dep, p, leftds);
+    }
+    while (risspace(*p))
+        p++;
+    if (!*p) {
+	if (emsg)
+	    rasprintf(emsg, "Complex dependency does not end with ')'");
+	*leftds = rpmdsFree(*leftds);
+        return RPMCPLXDEPOP_ERROR;
+    }
+    if (*p == ')') {
+        if (p[1]) {
+	    if (emsg)
+		rasprintf(emsg, "Junk at end of complex dependency");
+	    *leftds = rpmdsFree(*leftds);
+            return RPMCPLXDEPOP_ERROR;
+        }
+	return RPMCPLXDEPOP_SINGLE;
+    }
+    for (;;) {
+        pe = p;
+        while (*pe && !risspace(*pe) && *pe != ')')
+            pe++;
+        op = 0;
+        if (pe - p == 3 && !strncmp(p, "AND", 3))
+            op = RPMCPLXDEPOP_AND;
+        if (pe - p == 2 && !strncmp(p, "OR", 2))
+            op = RPMCPLXDEPOP_OR;
+        if (pe - p == 2 && !strncmp(p, "IF", 2))
+            op = RPMCPLXDEPOP_IF;
+        if (!op) {
+	    if (emsg)
+		rasprintf(emsg, "Unknown dependency op '%.*s'", (int)(pe - p), p);
+            *leftds = rpmdsFree(*leftds);
+            return RPMCPLXDEPOP_ERROR;
+        }
+	p = pe;
+        if (chainop) {
+	    char *chainstr;
+            *rightds = rpmdsFree(*rightds);
+            if (chainop != op) {
+                *leftds = rpmdsFree(*leftds);
+		if (emsg)
+		    rasprintf(emsg, "Cannot chain different ops");
+                return RPMCPLXDEPOP_ERROR;
+            }
+            if (op == RPMCPLXDEPOP_IF) {
+                *leftds = rpmdsFree(*leftds);
+		if (emsg)
+		    rasprintf(emsg, "Cannot chain IF ops");
+                return RPMCPLXDEPOP_ERROR;
+            }
+	    chainstr = strdup(chainstart - 1);
+	    *chainstr = '(';
+	    p = chainstr;
+	    pe = skipComplexDep(p);
+	    if (*pe)
+		pe++;
+	    ds = singleDS(dep->pool, dep->tagN, 0, 0, flags, 0, 0);
+	    if (ds) {
+		ds->N[0] = rpmstrPoolIdn(ds->pool, p, pe - p, 1);
+		ds->EVR[0] = rpmstrPoolId(ds->pool, "", 1);
+	    }
+	    *rightds = ds;
+	    free(chainstr);
+            return chainop;
+        }
+        while (risspace(*p))
+            p++;
+        if (!chainop)
+            chainstart = p;
+        if (*p == '(') {
+            /* sub-dependency */
+            pe = skipComplexDep(p);
+            if (*pe != ')') {
+		if (emsg)
+		    rasprintf(emsg, "Unterminated complex dependency: '%s'", p);
+                return RPMCPLXDEPOP_ERROR;
+            }
+	    pe++;
+	    ds = singleDS(dep->pool, dep->tagN, 0, 0, flags, 0, 0);
+            if (ds) {
+                ds->N[0] = rpmstrPoolIdn(ds->pool, p, pe - p, 1);
+                ds->EVR[0] = rpmstrPoolId(ds->pool, "", 1);
+            }
+            *rightds = ds;
+	    p = pe;
+        } else if (*p == ')') {
+	    if (emsg)
+		rasprintf(emsg, "Missing argument after complex op");
+	    return RPMCPLXDEPOP_ERROR;
+        } else {
+	    p = makeSimpleDep(dep, p, rightds);
+	}
+	while (risspace(*p))
+            p++;
+	if (!*p || *p == ')')
+            break;
+        /* chain */
+        chainop = op;
+    }
+    if (!*p) {
+	if (emsg)
+	    rasprintf(emsg, "Complex dependency does not end with ')'");
+	*leftds = rpmdsFree(*leftds);
+	*rightds = rpmdsFree(*rightds);
+	return RPMCPLXDEPOP_ERROR;
+    }
+    if (*p != ')') {
+	if (emsg)
+	    rasprintf(emsg, "Complex dependency does not end with ')'");
+	*leftds = rpmdsFree(*leftds);
+	*rightds = rpmdsFree(*rightds);
+	return RPMCPLXDEPOP_ERROR;
+    }
+    if (p[1]) {
+	if (emsg)
+	    rasprintf(emsg, "Junk at end of complex dependency");
+	*leftds = rpmdsFree(*leftds);
+	*rightds = rpmdsFree(*rightds);
+	return RPMCPLXDEPOP_ERROR;
+    }
+    return op;
+}
+
diff --git a/lib/rpmds.h b/lib/rpmds.h
index 9b7c908..a30d08e 100644
--- a/lib/rpmds.h
+++ b/lib/rpmds.h
@@ -49,7 +49,8 @@ enum rpmsenseFlags_e {
     RPMSENSE_TRIGGERPREIN = (1 << 25),	/*!< %triggerprein dependency. */
     RPMSENSE_KEYRING	= (1 << 26),
     /* bit 27 unused */
-    RPMSENSE_CONFIG	= (1 << 28)
+    RPMSENSE_CONFIG	= (1 << 28),
+    RPMSENSE_COMPLEX    = (1 << 29)
 };
 
 typedef rpmFlags rpmsenseFlags;
@@ -87,6 +88,14 @@ typedef rpmFlags rpmsenseFlags;
 
 
 
+typedef enum rpmCplxDepOp_e {
+    RPMCPLXDEPOP_ERROR	= 0,
+    RPMCPLXDEPOP_SINGLE	= 1,
+    RPMCPLXDEPOP_AND	= 2,
+    RPMCPLXDEPOP_OR	= 3,
+    RPMCPLXDEPOP_IF	= 4
+} rpmCplxDepOp;
+
 /** \ingroup rpmds
  * Return only those flags allowed for given type of dependencies
  * @param tagN		type of dependency
@@ -454,6 +463,8 @@ rpmds rpmdsSinglePoolTix(rpmstrPool pool, rpmTagVal tagN,
  */
 int rpmdsRpmlibPool(rpmstrPool pool, rpmds * dsp, const void * tblp);
 
+rpmCplxDepOp rpmdsParseCplxDep(rpmds dep, rpmds *leftds, rpmds *rightds, char **emsg);
+
 #ifdef __cplusplus
 }
 #endif


More information about the Rpm-maint mailing list