On Mon, Sep 08, 2014 at 04:51:12PM +0200, Michael Schroeder wrote:
> Hi Panu et al,
> 
> attached is an updated version of my rich dependencies patch.
> I cleanup up the code a bit, now we have only one generic parser
> instead of three specialized ones, and we use a callback function
> to do the needed work.

New version attached: some bugs fixed plus the ordering code
now also understands rich deps.

Cheers,
  Michael.

-- 
Michael Schroeder                                   m...@suse.de
SUSE LINUX Products GmbH,  GF Jeff Hawn, HRB 16746 AG Nuernberg
main(_){while(_=~getchar())putchar(~_-1/(~(_|32)/13*2-11)*13);}
diff --git a/build/pack.c b/build/pack.c
index 74471d9..fee89b5 100644
--- a/build/pack.c
+++ b/build/pack.c
@@ -265,6 +265,29 @@ static int haveTildeDep(Header h)
     return 0;
 }
 
+static int depContainsRich(Header h, rpmTagVal tagFlags)
+{
+    struct rpmtd_s flags;
+    rpm_flag_t *flag = NULL;
+
+    if (headerGet(h, tagFlags, &flags, HEADERGET_MINMEM)) {
+        while ((flag = rpmtdNextUint32(&flags)) != NULL)
+            if (*flag & RPMSENSE_RICH)
+                break;
+        rpmtdFreeData(&flags);
+    }
+    return flag != NULL;
+}
+
+static int haveRichDep(Header h)
+{
+    if (depContainsRich(h, RPMTAG_REQUIREFLAGS))
+	return 1;
+    if (depContainsRich(h, RPMTAG_CONFLICTFLAGS))
+	return 1;
+    return 0;
+}
+
 static rpm_loff_t estimateCpioSize(Package pkg)
 {
     rpmfi fi;
@@ -438,6 +461,10 @@ static rpmRC writeRPM(Package pkg, unsigned char ** pkgidp,
     if (haveTildeDep(pkg->header))
 	(void) rpmlibNeedsFeature(pkg, "TildeInVersions", "4.10.0-1");
 
+    /* check if the package has a rich dependency */
+    if (haveRichDep(pkg->header))
+	(void) rpmlibNeedsFeature(pkg, "RichDependencies", "4.12.0-1");
+
     /* All dependencies added finally, write them into the header */
     for (int i = 0; i < PACKAGE_NUM_DEPS; i++) {
 	/* Nuke any previously added dependencies from the header */
diff --git a/build/parseReqs.c b/build/parseReqs.c
index 37ee1fc..eaf10fb 100644
--- a/build/parseReqs.c
+++ b/build/parseReqs.c
@@ -68,6 +68,70 @@ static rpmRC checkDep(rpmSpec spec, char *N, char *EVR, char **emsg)
     return RPMRC_OK;
 }
 
+struct parseRCPOTRichData {
+    rpmSpec spec;
+    StringBuf sb;
+    int no_if;
+    int stacked_if;
+};
+
+/* Callback for the rich dependency parser. We use this to do check for invalid
+ * characters and to build a normailzed version of the dependency */
+static rpmRC parseRCPOTRichCB(void *cbdata, int type,
+				 const char *n, int nl, const char *e, int el, rpmsenseFlags sense,
+				 rpmrichOp op, char **emsg) {
+    struct parseRCPOTRichData *data = cbdata;
+    StringBuf sb = data->sb;
+    rpmRC rc = RPMRC_OK;
+
+    if (type == RPMRICH_PARSE_ENTER) {
+	appendStringBuf(sb, "(");
+    } else if (type == RPMRICH_PARSE_LEAVE) {
+	appendStringBuf(sb, ")");
+	if (op == RPMRICHOP_IF)
+	    data->stacked_if--;
+    } else if (type == RPMRICH_PARSE_SIMPLE) {
+	char *N = xmalloc(nl + 1);
+	char *EVR = NULL;
+	rstrlcpy(N, n, nl + 1);
+	appendStringBuf(sb, N);
+	if (el) {
+	    char rel[6], *rp = rel;
+	    EVR = xmalloc(el + 1);
+	    rstrlcpy(EVR, e, el + 1);
+	    *rp++ = ' ';
+	    if (sense & RPMSENSE_LESS)
+		*rp++ = '<';
+	    if (sense & RPMSENSE_GREATER)
+		*rp++ = '>';
+	    if (sense & RPMSENSE_EQUAL)
+		*rp++ = '=';
+	    *rp++ = ' ';
+	    *rp = 0;
+	    appendStringBuf(sb, rel);
+	    appendStringBuf(sb, EVR);
+	}
+	rc = checkDep(data->spec, N, EVR, emsg);
+	_free(N);
+	_free(EVR);
+    } else if (type == RPMRICH_PARSE_OP) {
+	if (op == RPMRICHOP_IF) {
+	    if (data->no_if) {
+		rasprintf(emsg, "IF not allowed in conflicts dependencies");
+		rc = RPMRC_FAIL;
+	    } else if (data->stacked_if) {
+		rasprintf(emsg, "Stacked IF is not supported ");
+		rc = RPMRC_FAIL;
+	    }
+	    data->stacked_if++;
+	}
+	appendStringBuf(sb, " ");
+	appendStringBuf(sb, rpmrichOpStr(op));
+	appendStringBuf(sb, " ");
+    }
+    return rc;
+}
+
 rpmRC parseRCPOT(rpmSpec spec, Package pkg, const char *field, rpmTagVal tagN,
 	       int index, rpmsenseFlags tagflags)
 {
@@ -146,6 +210,32 @@ rpmRC parseRCPOT(rpmSpec spec, Package pkg, const char *field, rpmTagVal tagN,
 
 	Flags = (tagflags & ~RPMSENSE_SENSEMASK);
 
+	if (r[0] == '(') {
+	    struct parseRCPOTRichData data;
+	    if (nametag != RPMTAG_REQUIRENAME && nametag != RPMTAG_CONFLICTNAME &&
+			nametag != RPMTAG_RECOMMENDNAME && nametag != RPMTAG_SUPPLEMENTNAME &&
+			nametag != RPMTAG_SUGGESTNAME && nametag != RPMTAG_ENHANCENAME) {
+		rasprintf(&emsg, _("No rich dependencies allowed for this type"));
+		goto exit;
+	    }
+	    data.spec = spec;
+	    data.sb = newStringBuf();
+	    data.no_if = (nametag == RPMTAG_CONFLICTNAME);
+	    data.stacked_if = 0;
+	    if (rpmrichParse(&r, &emsg, parseRCPOTRichCB, &data) != RPMRC_OK) {
+		freeStringBuf(data.sb);
+		goto exit;
+	    }
+	    if (addReqProv(pkg, nametag, getStringBuf(data.sb), NULL, Flags | RPMSENSE_RICH, index)) {
+		rasprintf(&emsg, _("invalid dependency"));
+		freeStringBuf(data.sb);
+		goto exit;
+	    }
+	    freeStringBuf(data.sb);
+	    re = r;
+	    continue;
+	}
+
 	re = r;
 	SKIPNONWHITE(re);
 	N = xmalloc((re-r) + 1);
diff --git a/lib/depends.c b/lib/depends.c
index 2ed0d99..438c178 100644
--- a/lib/depends.c
+++ b/lib/depends.c
@@ -57,6 +57,13 @@ const int rpmFLAGS = RPMSENSE_EQUAL;
 #undef HTKEYTYPE
 #undef HTDATATYPE
 
+#define HASHTYPE depexistsCache
+#define HTKEYTYPE const char *
+#include "lib/rpmhash.H"
+#include "lib/rpmhash.C"
+#undef HASHTYPE
+#undef HTKEYTYPE
+
 enum addOp_e {
     RPMTE_INSTALL	= 0,
     RPMTE_UPGRADE	= 1,
@@ -621,6 +628,32 @@ retry:
     if (!adding && isInstallPreReq(dsflags) && !isErasePreReq(dsflags))
 	goto exit;
 
+    /* Handle rich dependencies */
+    if (dsflags & RPMSENSE_RICH) {
+	rpmds ds1, ds2; 
+	rpmrichOp op;
+	char *emsg = 0; 
+	if (rpmdsParseRichDep(dep, &ds1, &ds2, &op, &emsg) != RPMRC_OK) {
+	    rc = rpmdsTagN(dep) == RPMTAG_CONFLICTNAME ? 0 : 1;
+	    if (rpmdsInstance(dep) != 0)
+		rc = !rc;	/* ignore errors for installed packages */
+	    rpmdsNotify(dep, emsg ? emsg : "(parse error)", rc);  
+	    _free(emsg);
+	    goto exit;
+	}
+	if (op == RPMRICHOP_IF)
+	    rc = !unsatisfiedDepend(ts, dcache, ds2);
+	if (op != RPMRICHOP_IF || rc)
+	    rc = unsatisfiedDepend(ts, dcache, ds1);
+	if ((rc && op == RPMRICHOP_OR) || (!rc && op == RPMRICHOP_AND)) {
+	    rc = unsatisfiedDepend(ts, dcache, ds2);
+	}
+	ds1 = rpmdsFree(ds1);
+	ds2 = rpmdsFree(ds2);
+	rpmdsNotify(dep, "(rich)", rc);
+	goto exit;
+    }
+
     /* Pretrans dependencies can't be satisfied by added packages. */
     if (!(dsflags & RPMSENSE_PRETRANS)) {
 	rpmte *matches = rpmalAllSatisfiesDepend(tsmem->addedPackages, dep);
@@ -714,8 +747,18 @@ static void checkInstDeps(rpmts ts, depCache dcache, rpmte te,
     rpmdbFreeIterator(mi);
 }
 
+static void checkNotInstDeps(rpmts ts, depCache dcache, rpmte te,
+			     rpmTag depTag, const char *dep)
+{
+    char *ndep = rmalloc(strlen(dep) + 2);
+    ndep[0] = '!';
+    strcpy(ndep + 1, dep);
+    checkInstDeps(ts, dcache, te, depTag, ndep);
+    free(ndep);
+}
+
 static void checkInstFileDeps(rpmts ts, depCache dcache, rpmte te,
-			      rpmTag depTag, rpmfi fi,
+			      rpmTag depTag, rpmfi fi, int is_not,
 			      filedepCache cache, fingerPrintCache *fpcp)
 {
     fingerPrintCache fpc = *fpcp;
@@ -734,15 +777,23 @@ static void checkInstFileDeps(rpmts ts, depCache dcache, rpmte te,
     dirname = rpmfiDN(fi);
     fpLookup(fpc, dirname, basename, &fp);
     for (i = 0; i < ndirnames; i++) {
+	char *fpdep = 0;
+	const char *dep;
 	if (!strcmp(dirnames[i], dirname)) {
-	    checkInstDeps(ts, dcache, te, depTag, rpmfiFN(fi));
+	    dep = rpmfiFN(fi);
 	} else if (fpLookupEquals(fpc, fp, dirnames[i], basename)) {
-	    char *dep = rmalloc(strlen(dirnames[i]) + strlen(basename) + 1);
-	    strcpy(dep, dirnames[i]);
-	    strcat(dep, basename);
-	    checkInstDeps(ts, dcache, te, depTag, dep);
-	    free(dep);
+	    fpdep = rmalloc(strlen(dirnames[i]) + strlen(basename) + 1);
+	    strcpy(fpdep, dirnames[i]);
+	    strcat(fpdep, basename);
+	    dep = fpdep;
+	} else {
+	    continue;
 	}
+	if (!is_not)
+	    checkInstDeps(ts, dcache, te, depTag, dep);
+	else
+	    checkNotInstDeps(ts, dcache, te, depTag, dep);
+	_free(fpdep);
     }
     _free(fp);
 }
@@ -773,6 +824,8 @@ int rpmtsCheck(rpmts ts)
     depCache dcache = NULL;
     filedepCache confilecache = NULL;	/* file conflicts of installed packages */
     filedepCache reqfilecache = NULL;	/* file requires of installed packages */
+    filedepCache reqnotfilecache = NULL;	/* file requires of installed packages */
+    depexistsCache reqnotcache = NULL;
     fingerPrintCache fpc = NULL;
     
     (void) rpmswEnter(rpmtsOp(ts, RPMTS_OP_CHECK), 0);
@@ -812,13 +865,35 @@ int rpmtsCheck(rpmts ts)
 					(filedepCacheFreeData)rfree);
     if (reqfilecache) {
 	rpmdbIndexIterator ii = rpmdbIndexIteratorInit(rpmtsGetRdb(ts), RPMTAG_REQUIRENAME);
+	reqnotcache = depexistsCacheCreate(257, rstrhash, strcmp,
+					(filedepCacheFreeKey)rfree);
+	reqnotfilecache = filedepCacheCreate(257, rstrhash, strcmp,
+					(filedepCacheFreeKey)rfree,
+					(filedepCacheFreeData)rfree);
 	if (ii) {
 	    char *key;
 	    size_t keylen;
-	    while ((rpmdbIndexIteratorNext(ii, (const void**)&key, &keylen)) == 0)
+	    while ((rpmdbIndexIteratorNext(ii, (const void**)&key, &keylen)) == 0) {
+		if (key && keylen && key[0] == '!') {
+		    char *keystr;
+		    /* inverted name from some rich dependency with IF */
+		    key++;
+		    keylen--;
+		    keystr = rmalloc(keylen + 1);
+		    strncpy(keystr, key, keylen);
+		    keystr[keylen] = 0;
+		    depexistsCacheAddEntry(reqnotcache, keystr);
+		    addFileDepToCache(reqnotfilecache, key, keylen);
+		    continue;
+		}
 		addFileDepToCache(reqfilecache, key, keylen);
+	    }
 	    rpmdbIndexIteratorFree(ii);
 	}
+	if (reqnotcache && !depexistsCacheNumKeys(reqnotcache))
+	    reqnotcache = depexistsCacheFree(reqnotcache);
+	if (reqnotfilecache && !filedepCacheNumKeys(reqnotfilecache))
+	    reqnotfilecache = filedepCacheFree(reqnotfilecache);
     }
 
     
@@ -842,7 +917,10 @@ int rpmtsCheck(rpmts ts)
 
 	/* Check provides against conflicts in installed packages. */
 	while (rpmdsNext(provides) >= 0) {
-	    checkInstDeps(ts, dcache, p, RPMTAG_CONFLICTNAME, rpmdsN(provides));
+	    const char *dep = rpmdsN(provides);
+	    checkInstDeps(ts, dcache, p, RPMTAG_CONFLICTNAME, dep);
+	    if (reqnotcache && depexistsCacheHasEntry(reqnotcache, dep))
+		checkNotInstDeps(ts, dcache, p, RPMTAG_REQUIRENAME, dep);
 	}
 
 	/* Skip obsoletion checks for source packages (ie build) */
@@ -853,11 +931,14 @@ int rpmtsCheck(rpmts ts)
 	checkInstDeps(ts, dcache, p, RPMTAG_OBSOLETENAME, rpmteN(p));
 
 	/* Check filenames against installed conflicts */
-        if (confilecache) {
+        if (confilecache || reqnotfilecache) {
 	    rpmfiles files = rpmteFiles(p);
 	    rpmfi fi = rpmfilesIter(files, RPMFI_ITER_FWD);
 	    while (rpmfiNext(fi) >= 0) {
-		checkInstFileDeps(ts, dcache, p, RPMTAG_CONFLICTNAME, fi, confilecache, &fpc);
+		if (confilecache)
+		    checkInstFileDeps(ts, dcache, p, RPMTAG_CONFLICTNAME, fi, 0, confilecache, &fpc);
+		if (reqnotfilecache)
+		    checkInstFileDeps(ts, dcache, p, RPMTAG_REQUIRENAME, fi, 1, reqnotfilecache, &fpc);
 	    }
 	    rpmfiFree(fi);
 	    rpmfilesFree(files);
@@ -885,7 +966,7 @@ int rpmtsCheck(rpmts ts)
 	    rpmfi fi = rpmfilesIter(files, RPMFI_ITER_FWD);;
 	    while (rpmfiNext(fi) >= 0) {
 		if (RPMFILE_IS_INSTALLED(rpmfiFState(fi))) {
-		    checkInstFileDeps(ts, dcache, p, RPMTAG_REQUIRENAME, fi, reqfilecache, &fpc);
+		    checkInstFileDeps(ts, dcache, p, RPMTAG_REQUIRENAME, fi, 0, reqfilecache, &fpc);
 		}
 	    }
 	    rpmfiFree(fi);
@@ -898,6 +979,8 @@ exit:
     depCacheFree(dcache);
     filedepCacheFree(confilecache);
     filedepCacheFree(reqfilecache);
+    filedepCacheFree(reqnotfilecache);
+    depexistsCacheFree(reqnotcache);
     fpCacheFree(fpc);
 
     (void) rpmswExit(rpmtsOp(ts, RPMTS_OP_CHECK), 0);
diff --git a/lib/order.c b/lib/order.c
index f09b192..b8c81d7 100644
--- a/lib/order.c
+++ b/lib/order.c
@@ -152,6 +152,18 @@ static inline int addRelation(rpmts ts,
     if (dsflags & (RPMSENSE_RPMLIB|RPMSENSE_CONFIG|RPMSENSE_PRETRANS|RPMSENSE_POSTTRANS))
 	return 0;
 
+    if (dsflags & RPMSENSE_RICH) {
+	rpmds ds1, ds2;
+	rpmrichOp op;
+	if (rpmdsParseRichDep(requires, &ds1, &ds2, &op, NULL) == RPMRC_OK) {
+	    addRelation(ts, al, p, ds1);
+	    if (op == RPMRICHOP_AND || op == RPMRICHOP_OR)
+		addRelation(ts, al, p, ds2);
+	    ds1 = rpmdsFree(ds1);
+	    ds2 = rpmdsFree(ds2);
+	}
+	return 0;
+    }
     q = rpmalSatisfiesDepend(al, p, requires);
 
     /* Avoid deps outside this transaction and self dependencies */
diff --git a/lib/rpmdb.c b/lib/rpmdb.c
index b6d3247..af69ed5 100644
--- a/lib/rpmdb.c
+++ b/lib/rpmdb.c
@@ -2032,6 +2032,50 @@ int rpmdbRemove(rpmdb db, unsigned int hdrNum)
     return 0;
 }
 
+struct updateRichDepData {
+    ARGV_t argv;
+    int neg;
+};
+
+static rpmRC updateRichDepCB(void *cbdata, int type, const char *n, int nl, const char *e, int el, rpmsenseFlags sense, rpmrichOp op, char **emsg) {
+    struct updateRichDepData *data = cbdata;
+    if (type == RPMRICH_PARSE_SIMPLE && nl) {
+	char *name = xmalloc(data->neg + nl + 1);
+	*name = '!';
+	strncpy(name + data->neg, n, nl);
+	name[data->neg + nl] = 0;
+	argvAdd(&data->argv, name);
+	_free(name);
+    } else if ((type == RPMRICH_PARSE_OP || RPMRICH_PARSE_LEAVE) && op == RPMRICHOP_IF) {
+	data->neg ^= 1;
+    }
+    return RPMRC_OK;
+}
+
+static rpmRC updateRichDep(dbiCursor dbc, const char *str,
+                           struct dbiIndexItem_s *rec,
+                           idxfunc idxupdate)
+{
+    int n, i, rc = 0;
+    struct updateRichDepData data;
+
+    data.argv = argvNew();
+    data.neg = 0;
+    if (rpmrichParse(&str, NULL, updateRichDepCB, &data) == RPMRC_OK) {
+	n = argvCount(data.argv);
+	if (n) {
+	    argvSort(data.argv, NULL);
+	    for (i = 0; i < n; i++) {
+		if (i && !strcmp(data.argv[i - 1], data.argv[i]))
+		    continue;       /* ignore dups */
+		rc += idxupdate(dbc, data.argv[i], strlen(data.argv[i]), rec);
+	    }
+	}
+    }
+    argvFree(data.argv);
+    return rc;
+}
+
 static rpmRC tag2index(dbiIndex dbi, rpmTagVal rpmtag,
 		       unsigned int hdrNum, Header h,
 		       idxfunc idxupdate)
@@ -2098,6 +2142,12 @@ static rpmRC tag2index(dbiIndex dbi, rpmTagVal rpmtag,
 	    continue;
 
 	rc += idxupdate(dbc, key, keylen, &rec);
+
+	if (rpmtag == RPMTAG_REQUIRENAME && *(char *)key == '(') {
+	    if (rpmtdType(&tagdata) == RPM_STRING_ARRAY_TYPE) {
+		rc += updateRichDep(dbc, rpmtdGetString(&tagdata), &rec, idxupdate);
+	    }
+	}
     }
 
     dbiCursorFree(dbc);
diff --git a/lib/rpmds.c b/lib/rpmds.c
index 49e33bc..617710c 100644
--- a/lib/rpmds.c
+++ b/lib/rpmds.c
@@ -1209,6 +1209,9 @@ static const struct rpmlibProvides_s rpmlibProvides[] = {
     { "rpmlib(LargeFiles)", 	"4.12.0-1",
 	(		RPMSENSE_EQUAL),
     N_("support files larger than 4GB") },
+    { "rpmlib(RichDependencies)",    "4.12.0-1",
+	(		RPMSENSE_EQUAL),
+    N_("support for rich dependencies.") },
     { NULL,				NULL, 0,	NULL }
 };
 
@@ -1259,10 +1262,264 @@ 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_RICH);
+	break;
+    case RPMTAG_CONFLICTNAME:
+	extra = Flags & RPMSENSE_RICH;
 	break;
     default:
 	break;
     }
     return (Flags & RPMSENSE_SENSEMASK) | extra;
 }
+
+struct rpmdsParseRichDepData {
+    rpmds dep;
+    rpmsenseFlags depflags;
+
+    rpmds leftds;
+    rpmds rightds;
+    rpmrichOp op;
+
+    int depth;
+    const char *rightstart;
+    int dochain;
+};
+
+static rpmRC rpmdsParseRichDepCB(void *cbdata, int type, const char *n, int nl, const char *e, int el, rpmsenseFlags sense, rpmrichOp op, char **emsg) {
+    struct rpmdsParseRichDepData *data = cbdata;
+    rpmds ds = 0;
+
+    if (type == RPMRICH_PARSE_ENTER)
+	data->depth++;
+    else if (type == RPMRICH_PARSE_LEAVE) {
+	if (--data->depth == 0 && data->dochain && data->rightstart) {
+	    /* chain op hack, construct a sub-ds from the right side of the chain */
+	    char *right = xmalloc(n + nl - data->rightstart + 2);
+	    right[0] = '(';
+	    strncpy(right + 1, data->rightstart, n + nl - data->rightstart);
+	    right[n + nl - data->rightstart + 1] = 0;
+	    data->rightds = rpmdsFree(data->rightds);
+	    ds = singleDS(data->dep->pool, data->dep->tagN, 0, 0, RPMSENSE_RICH | data->depflags, 0, 0, 0);
+	    ds->N[0] = rpmstrPoolIdn(ds->pool, n, nl, 1);
+	    ds->EVR[0] = rpmstrPoolIdn(ds->pool, e ? e : "", el, 1);
+	    data->rightds = ds;
+	}
+    }
+    if (data->depth != 1)
+	return RPMRC_OK;	/* we're only interested in top-level parsing */
+    if ((type == RPMRICH_PARSE_SIMPLE || type == RPMRICH_PARSE_LEAVE) && !data->dochain) {
+	if (type == RPMRICH_PARSE_LEAVE)
+	    sense = RPMSENSE_RICH;
+	ds = singleDS(data->dep->pool, data->dep->tagN, 0, 0, sense | data->depflags, 0, 0, 0);
+	ds->N[0] = rpmstrPoolIdn(ds->pool, n, nl, 1);
+	ds->EVR[0] = rpmstrPoolIdn(ds->pool, e ? e : "", el, 1);
+	if (!data->leftds)
+	    data->leftds = ds;
+	else {
+	    data->rightds = ds;
+	    data->rightstart = n;
+	}
+    }
+    if (type == RPMRICH_PARSE_OP) {
+	if (data->op != RPMRICHOP_SINGLE)
+	    data->dochain = 1;	/* this is a chained op */
+	data->op = op;
+    }
+    return RPMRC_OK;
+}
+
+
+rpmRC rpmdsParseRichDep(rpmds dep, rpmds *leftds, rpmds *rightds, rpmrichOp *op, char **emsg)
+{
+    rpmRC rc;
+    struct rpmdsParseRichDepData data;
+    const char *depstr = rpmdsN(dep);
+    memset(&data, 0, sizeof(data));
+    data.dep = dep;
+    data.op = RPMRICHOP_SINGLE;
+    data.depflags = rpmdsFlags(dep) & ~(RPMSENSE_SENSEMASK | RPMSENSE_RICH | RPMSENSE_MISSINGOK);
+    rc = rpmrichParse(&depstr, emsg, rpmdsParseRichDepCB, &data);
+    if (rc == RPMRC_OK && *depstr) {
+	if (emsg)
+	    rasprintf(emsg, "Junk after rich dependency");
+	rc = RPMRC_FAIL;
+    }
+    if (rc != RPMRC_OK) {
+	rpmdsFree(data.leftds);
+	rpmdsFree(data.rightds);
+    } else {
+	*leftds = data.leftds;
+	*rightds = data.rightds;
+	*op = data.op;
+    }
+    return rc;
+}
+
+
+static rpmRC parseRichDepOp(const char **dstrp, rpmrichOp *opp, char **emsg)
+{
+    const char *p = *dstrp, *pe = p;
+
+    while (*pe && !risspace(*pe) && *pe != ')')
+	pe++;
+    if (pe - p == 3 && !strncmp(p, "AND", 3)) 
+	*opp = RPMRICHOP_AND;
+    else if (pe - p == 2 && !strncmp(p, "OR", 2)) 
+	*opp = RPMRICHOP_OR;
+    else if (pe - p == 2 && !strncmp(p, "IF", 2)) 
+	*opp = RPMRICHOP_IF;
+    else {
+	if (emsg)
+	    rasprintf(emsg, "Unknown rich dependency op '%.*s'", (int)(pe - p), p); 
+	return RPMRC_FAIL;
+    }   
+    *dstrp = pe; 
+    return RPMRC_OK;
+}
+
+
+static struct ReqComp {
+const char * token;
+    rpmsenseFlags sense;
+} const ReqComparisons[] = {
+    { "<=", RPMSENSE_LESS | RPMSENSE_EQUAL},
+    { "=<", RPMSENSE_LESS | RPMSENSE_EQUAL},
+    { "<", RPMSENSE_LESS},
+
+    { "==", RPMSENSE_EQUAL},
+    { "=", RPMSENSE_EQUAL},
+
+    { ">=", RPMSENSE_GREATER | RPMSENSE_EQUAL},
+    { "=>", RPMSENSE_GREATER | RPMSENSE_EQUAL},
+    { ">", RPMSENSE_GREATER},
+
+    { NULL, 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 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;
+        int i;
+
+        SKIPNONWHITE(pe);
+        for (i = 0; ReqComparisons[i].token; i++) {
+            if (pe - p == strlen(ReqComparisons[i].token) && !strncmp(p, ReqComparisons[i].token, pe - p)) {
+                sense = ReqComparisons[i].sense;
+                break;
+            }
+        }
+        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 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;
+}
+
+const char *rpmrichOpStr(rpmrichOp op)
+{
+    if (op == RPMRICHOP_SINGLE)
+	return "SINGLE";
+    if (op == RPMRICHOP_AND)
+	return "AND";
+    if (op == RPMRICHOP_OR)
+	return "OR";
+    if (op == RPMRICHOP_IF)
+	return "IF";
+    return NULL;
+}
+
diff --git a/lib/rpmds.h b/lib/rpmds.h
index 9b7c908..bdde57a 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_RICH	= (1 << 29)
 };
 
 typedef rpmFlags rpmsenseFlags;
@@ -454,6 +455,52 @@ 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;
+
+/**
+ * Parse a rich dependency string
+ * @param dep		the dependency
+ * @param leftds	returns the left dependency
+ * @param rightds	returns the right dependency
+ * @param op		returns the rich dep op
+ * @param emsg		returns the error string
+ * @return		RPMRC_OK on success
+ */
+rpmRC rpmdsParseRichDep(rpmds dep, rpmds *leftds, rpmds *rightds, rpmrichOp *op, char **emsg);
+
+
+#define RPMRICH_PARSE_SIMPLE	1	/* standard N <=> EVR dep */
+#define RPMRICH_PARSE_ENTER	2	/* entering sub-dependency */
+#define RPMRICH_PARSE_LEAVE	3	/* leaving sub-dependency */
+#define RPMRICH_PARSE_OP	4	/* parsed a rich dep op */
+
+typedef rpmRC (*rpmrichParseFunction) (void *cbdata, int 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
_______________________________________________
Rpm-maint mailing list
Rpm-maint@lists.rpm.org
http://lists.rpm.org/mailman/listinfo/rpm-maint

Reply via email to