#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;
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;
}
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;
rstrlcpy(N, r, (re-r) + 1);
/* Parse EVR */
+ EVR = NULL;
v = re;
SKIPWHITE(v);
ve = v;
/* 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;
}
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;
}