Imported Upstream version 4.14.1
[platform/upstream/rpm.git] / build / parseReqs.c
index 36db460..705d4cb 100644 (file)
 #include "build/rpmbuild_misc.h"
 #include "debug.h"
 
-/**
- */
-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)++;}
+
+static rpmRC checkSep(const char *s, char c, char **emsg)
+{
+    const char *sep = strchr(s, c);
+    if (sep && strchr(sep + 1, c)) {
+       rasprintf(emsg, "Invalid version (double separator '%c'): %s", c, s);
+       return RPMRC_FAIL;
+    }
+    return RPMRC_OK;
+}
+
+static rpmRC checkEpoch(const char *s, char **emsg)
+{
+    const char *si, *sep = strchr(s, ':');
+
+    if (!sep)
+       return RPMRC_OK;
+
+    for (si = s; si != sep; si++) {
+       if (!risdigit(*si)) {
+           rasprintf(emsg, "Invalid version (epoch must be unsigned integer): %s", s);
+           return RPMRC_FAIL;
+       }
+    }
+    return RPMRC_OK;
+}
+
+static rpmRC checkDep(rpmSpec spec, char *N, char *EVR, char **emsg)
+{
+    /* 
+     * Tokens must begin with alphanumeric, _, or /, but we don't know
+     * the spec's encoding so we only check what we can: plain ascii.
+     */
+    if (isascii(N[0]) && !(risalnum(N[0]) || N[0] == '_' || N[0] == '/')) {
+        rasprintf(emsg, _("Dependency tokens must begin with alpha-numeric, '_' or '/'"));
+        return RPMRC_FAIL;
+    }
+    if (EVR) {
+        if (N[0] == '/') {
+            rasprintf(emsg, _("Versioned file name not permitted"));
+            return RPMRC_FAIL;
+        }
+        if (rpmCharCheck(spec, EVR, ".-_+:%{}~"))
+            return RPMRC_FAIL;
+       if (checkSep(EVR, '-', emsg) != RPMRC_OK ||
+           checkSep(EVR, ':', emsg) != RPMRC_OK ||
+           checkEpoch(EVR, emsg) != RPMRC_OK) {
+
+           if (rpmExpandNumeric("%{?_wrong_version_format_terminate_build}"))
+               return RPMRC_FAIL;
+       }
+    }
+    return RPMRC_OK;
+}
+
+struct parseRCPOTRichData {
+    rpmSpec spec;
+    StringBuf sb;
 };
 
-#define        SKIPWHITE(_x)   {while(*(_x) && (risspace(*_x) || *(_x) == ',')) (_x)++;}
-#define        SKIPNONWHITE(_x){while(*(_x) &&!(risspace(*_x) || *(_x) == ',')) (_x)++;}
+/* 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, rpmrichParseType 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, ")");
+    } 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) {
+       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)
+              int index, rpmsenseFlags tagflags, addReqProvFunction cb, void *cbdata)
 {
     const char *r, *re, *v, *ve;
-    const char *emsg = NULL;
+    char *emsg = NULL;
     char * N = NULL, * EVR = NULL;
     rpmTagVal nametag = RPMTAG_NOT_FOUND;
     rpmsenseFlags Flags;
     rpmRC rc = RPMRC_FAIL; /* assume failure */
+    int allow_richdeps = 0;
+
+    if (!cbdata)
+       cbdata = pkg;
 
     switch (tagN) {
     default:
-    case RPMTAG_REQUIREFLAGS:
-       nametag = RPMTAG_REQUIRENAME;
+    case RPMTAG_REQUIRENAME:
        tagflags |= RPMSENSE_ANY;
-       break;
-    case RPMTAG_PROVIDEFLAGS:
-       nametag = RPMTAG_PROVIDENAME;
-       break;
-    case RPMTAG_OBSOLETEFLAGS:
-       nametag = RPMTAG_OBSOLETENAME;
-       break;
-    case RPMTAG_CONFLICTFLAGS:
-       nametag = RPMTAG_CONFLICTNAME;
-       break;
-    case RPMTAG_ORDERFLAGS:
-       nametag = RPMTAG_ORDERNAME;
+       /* fall through */
+    case RPMTAG_RECOMMENDNAME:
+    case RPMTAG_SUGGESTNAME:
+    case RPMTAG_SUPPLEMENTNAME:
+    case RPMTAG_ENHANCENAME:
+    case RPMTAG_CONFLICTNAME:
+       allow_richdeps = 1;
+       /* fall through */
+    case RPMTAG_PROVIDENAME:
+    case RPMTAG_OBSOLETENAME:
+    case RPMTAG_ORDERNAME:
+       nametag = tagN;
        break;
     case RPMTAG_PREREQ:
        /* XXX map legacy PreReq into Requires(pre,preun) */
        nametag = RPMTAG_REQUIRENAME;
        tagflags |= (RPMSENSE_SCRIPT_PRE|RPMSENSE_SCRIPT_PREUN);
+       allow_richdeps = 1;
        break;
     case RPMTAG_TRIGGERPREIN:
        nametag = RPMTAG_TRIGGERNAME;
@@ -88,9 +177,35 @@ rpmRC parseRCPOT(rpmSpec spec, Package pkg, const char *field, rpmTagVal tagN,
     case RPMTAG_BUILDREQUIRES:
        nametag = RPMTAG_REQUIRENAME;
        tagflags |= RPMSENSE_ANY;
+       allow_richdeps = 1;
        break;
     case RPMTAG_BUILDCONFLICTS:
        nametag = RPMTAG_CONFLICTNAME;
+       allow_richdeps = 1;
+       break;
+    case RPMTAG_FILETRIGGERIN:
+       nametag = RPMTAG_FILETRIGGERNAME;
+       tagflags |= RPMSENSE_TRIGGERIN;
+       break;
+    case RPMTAG_FILETRIGGERUN:
+       nametag = RPMTAG_FILETRIGGERNAME;
+       tagflags |= RPMSENSE_TRIGGERUN;
+       break;
+    case RPMTAG_FILETRIGGERPOSTUN:
+       nametag = RPMTAG_FILETRIGGERNAME;
+       tagflags |= RPMSENSE_TRIGGERPOSTUN;
+       break;
+    case RPMTAG_TRANSFILETRIGGERIN:
+       nametag = RPMTAG_TRANSFILETRIGGERNAME;
+       tagflags |= RPMSENSE_TRIGGERIN;
+       break;
+    case RPMTAG_TRANSFILETRIGGERUN:
+       nametag = RPMTAG_TRANSFILETRIGGERNAME;
+       tagflags |= RPMSENSE_TRIGGERUN;
+       break;
+    case RPMTAG_TRANSFILETRIGGERPOSTUN:
+       nametag = RPMTAG_TRANSFILETRIGGERNAME;
+       tagflags |= RPMSENSE_TRIGGERPOSTUN;
        break;
     }
 
@@ -101,13 +216,26 @@ rpmRC parseRCPOT(rpmSpec spec, Package pkg, const char *field, rpmTagVal tagN,
 
        Flags = (tagflags & ~RPMSENSE_SENSEMASK);
 
-       /* 
-        * Tokens must begin with alphanumeric, _, or /, but we don't know
-        * the spec's encoding so we only check what we can: plain ascii.
-        */
-       if (isascii(r[0]) && !(risalnum(r[0]) || r[0] == '_' || r[0] == '/')) {
-           emsg = _("Dependency tokens must begin with alpha-numeric, '_' or '/'");
-           goto exit;
+       if (r[0] == '(') {
+           struct parseRCPOTRichData data;
+           if (!allow_richdeps) {
+               rasprintf(&emsg, _("No rich dependencies allowed for this type"));
+               goto exit;
+           }
+           data.spec = spec;
+           data.sb = newStringBuf();
+           if (rpmrichParseForTag(&r, &emsg, parseRCPOTRichCB, &data, nametag) != RPMRC_OK) {
+               freeStringBuf(data.sb);
+               goto exit;
+           }
+           if (cb && cb(cbdata, nametag, getStringBuf(data.sb), NULL, Flags, index) != RPMRC_OK) {
+               rasprintf(&emsg, _("invalid dependency"));
+               freeStringBuf(data.sb);
+               goto exit;
+           }
+           freeStringBuf(data.sb);
+           re = r;
+           continue;
        }
 
        re = r;
@@ -116,6 +244,7 @@ rpmRC parseRCPOT(rpmSpec spec, Package pkg, const char *field, rpmTagVal tagN,
        rstrlcpy(N, r, (re-r) + 1);
 
        /* Parse EVR */
+       EVR = NULL;
        v = re;
        SKIPWHITE(v);
        ve = v;
@@ -125,41 +254,57 @@ rpmRC parseRCPOT(rpmSpec spec, Package pkg, const char *field, rpmTagVal tagN,
 
        /* 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;
+           rpmsenseFlags sense = rpmParseDSFlags(v, ve - v);
+           if (sense) {
+               Flags |= sense;
 
-           if (r[0] == '/') {
-               emsg = _("Versioned file name not permitted");
-               goto exit;
+               /* now parse EVR */
+               v = ve;
+               SKIPWHITE(v);
+               ve = v;
+               SKIPNONWHITE(ve);
+               if (*v == '\0' || ve == v) {
+                   rasprintf(&emsg, _("Version required"));
+                   goto exit;
+               }
+               EVR = xmalloc((ve-v) + 1);
+               rstrlcpy(EVR, v, (ve-v) + 1);
+               re = ve;        /* ==> next token after EVR string starts here */
            }
+       }
 
-           Flags |= rc->sense;
+       /* check that dependency is well-formed */
+       if (checkDep(spec, N, EVR, &emsg))
+           goto exit;
 
-           /* now parse EVR */
-           v = ve;
-           SKIPWHITE(v);
-           ve = v;
-           SKIPNONWHITE(ve);
-           break;
-         }
+       if (nametag == RPMTAG_FILETRIGGERNAME ||
+           nametag == RPMTAG_TRANSFILETRIGGERNAME) {
+           if (N[0] != '/') {
+               rasprintf(&emsg, _("Only absolute paths are allowed in "
+                                   "file triggers"));
+           }
        }
 
-       if (Flags & RPMSENSE_SENSEMASK) {
-           if (*v == '\0' || ve == v) {
-               emsg = _("Version required");
-               goto exit;
+
+       /* Deny more "normal" triggers fired by the same pakage. File triggers are ok */
+       if (nametag == RPMTAG_TRIGGERNAME) {
+           rpmds *pdsp = packageDependencies(pkg, nametag);
+           rpmds newds = rpmdsSingle(nametag, N, EVR, Flags);
+           rpmdsInit(*pdsp);
+           while (rpmdsNext(*pdsp) >= 0) {
+               if (rpmdsCompare(*pdsp, newds) && (rpmdsFlags(*pdsp) & tagflags )) {
+                   rasprintf(&emsg, _("Trigger fired by the same package "
+                       "is already defined in spec file"));
+                   break;
+               }
            }
-           EVR = xmalloc((ve-v) + 1);
-           rstrlcpy(EVR, v, (ve-v) + 1);
-           if (rpmCharCheck(spec, EVR, ve-v, ".-_+:%{}~")) goto exit;
-           re = ve;    /* ==> next token after EVR string starts here */
-       } else
-           EVR = NULL;
-
-       if (addReqProv(pkg, nametag, N, EVR, Flags, index)) {
-           emsg = _("invalid dependency");
+           rpmdsFree(newds);
+           if (emsg)
+               goto exit;
+       }
+
+       if (cb && cb(cbdata, nametag, N, EVR, Flags, index) != RPMRC_OK) {
+           rasprintf(&emsg, _("invalid dependency"));
            goto exit;
        }
 
@@ -171,16 +316,18 @@ rpmRC parseRCPOT(rpmSpec spec, Package pkg, const char *field, rpmTagVal tagN,
 
 exit:
     if (emsg) {
+       int lvl = (rc == RPMRC_OK) ? RPMLOG_WARNING : RPMLOG_ERR;
        /* Automatic dependencies don't relate to spec lines */
        if (tagflags & (RPMSENSE_FIND_REQUIRES|RPMSENSE_FIND_PROVIDES)) {
-           rpmlog(RPMLOG_ERR, "%s: %s\n", emsg, r);
+           rpmlog(lvl, "%s: %s\n", emsg, r);
        } else {
-           rpmlog(RPMLOG_ERR, _("line %d: %s: %s\n"),
+           rpmlog(lvl, _("line %d: %s: %s\n"),
                   spec->lineNum, emsg, spec->line);
        }
+       free(emsg);
     }
-    free(N);
-    free(EVR);
+    _free(N);
+    _free(EVR);
 
     return rc;
 }