csomh / source-git / rpm

Forked from source-git/rpm 4 years ago
Clone
2ff057
/** \ingroup rpmbuild
2ff057
 * \file build/parseReqs.c
2ff057
 *  Parse dependency tag from spec file or from auto-dependency generator.
2ff057
 */
2ff057
2ff057
#include "system.h"
2ff057
2ff057
#include <ctype.h>
2ff057
#include <rpm/rpmtypes.h>
2ff057
#include <rpm/rpmlog.h>
2ff057
#include "build/rpmbuild_internal.h"
2ff057
#include "build/rpmbuild_misc.h"
2ff057
#include "debug.h"
2ff057
2ff057
2ff057
#define	SKIPWHITE(_x)	{while (*(_x) && (risspace(*_x) || *(_x) == ',')) (_x)++;}
2ff057
#define	SKIPNONWHITE(_x){while (*(_x) &&!(risspace(*_x) || *(_x) == ',')) (_x)++;}
2ff057
2ff057
static rpmRC checkSep(const char *s, char c, char **emsg)
2ff057
{
2ff057
    const char *sep = strchr(s, c);
2ff057
    if (sep && strchr(sep + 1, c)) {
2ff057
	rasprintf(emsg, "Invalid version (double separator '%c'): %s", c, s);
2ff057
	return RPMRC_FAIL;
2ff057
    }
2ff057
    return RPMRC_OK;
2ff057
}
2ff057
2ff057
static rpmRC checkEpoch(const char *s, char **emsg)
2ff057
{
2ff057
    const char *si, *sep = strchr(s, ':');
2ff057
2ff057
    if (!sep)
2ff057
	return RPMRC_OK;
2ff057
2ff057
    for (si = s; si != sep; si++) {
2ff057
	if (!risdigit(*si)) {
2ff057
	    rasprintf(emsg, "Invalid version (epoch must be unsigned integer): %s", s);
2ff057
	    return RPMRC_FAIL;
2ff057
	}
2ff057
    }
2ff057
    return RPMRC_OK;
2ff057
}
2ff057
2ff057
static rpmRC checkDep(rpmSpec spec, char *N, char *EVR, char **emsg)
2ff057
{
2ff057
    /* 
2ff057
     * Tokens must begin with alphanumeric, _, or /, but we don't know
2ff057
     * the spec's encoding so we only check what we can: plain ascii.
2ff057
     */
2ff057
    if (isascii(N[0]) && !(risalnum(N[0]) || N[0] == '_' || N[0] == '/')) {
2ff057
        rasprintf(emsg, _("Dependency tokens must begin with alpha-numeric, '_' or '/'"));
2ff057
        return RPMRC_FAIL;
2ff057
    }
2ff057
    if (EVR) {
2ff057
        if (N[0] == '/') {
2ff057
            rasprintf(emsg, _("Versioned file name not permitted"));
2ff057
            return RPMRC_FAIL;
2ff057
        }
2ff057
        if (rpmCharCheck(spec, EVR, ".-_+:%{}~"))
2ff057
            return RPMRC_FAIL;
2ff057
	if (checkSep(EVR, '-', emsg) != RPMRC_OK ||
2ff057
	    checkSep(EVR, ':', emsg) != RPMRC_OK ||
2ff057
	    checkEpoch(EVR, emsg) != RPMRC_OK) {
2ff057
2ff057
	    if (rpmExpandNumeric("%{?_wrong_version_format_terminate_build}"))
2ff057
		return RPMRC_FAIL;
2ff057
	}
2ff057
    }
2ff057
    return RPMRC_OK;
2ff057
}
2ff057
2ff057
struct parseRCPOTRichData {
2ff057
    rpmSpec spec;
2ff057
    StringBuf sb;
2ff057
};
2ff057
2ff057
/* Callback for the rich dependency parser. We use this to do check for invalid
2ff057
 * characters and to build a normailzed version of the dependency */
2ff057
static rpmRC parseRCPOTRichCB(void *cbdata, rpmrichParseType type,
2ff057
		const char *n, int nl, const char *e, int el, rpmsenseFlags sense,
2ff057
		rpmrichOp op, char **emsg) {
2ff057
    struct parseRCPOTRichData *data = cbdata;
2ff057
    StringBuf sb = data->sb;
2ff057
    rpmRC rc = RPMRC_OK;
2ff057
2ff057
    if (type == RPMRICH_PARSE_ENTER) {
2ff057
	appendStringBuf(sb, "(");
2ff057
    } else if (type == RPMRICH_PARSE_LEAVE) {
2ff057
	appendStringBuf(sb, ")");
2ff057
    } else if (type == RPMRICH_PARSE_SIMPLE) {
2ff057
	char *N = xmalloc(nl + 1);
2ff057
	char *EVR = NULL;
2ff057
	rstrlcpy(N, n, nl + 1);
2ff057
	appendStringBuf(sb, N);
2ff057
	if (el) {
2ff057
	    char rel[6], *rp = rel;
2ff057
	    EVR = xmalloc(el + 1);
2ff057
	    rstrlcpy(EVR, e, el + 1);
2ff057
	    *rp++ = ' ';
2ff057
	    if (sense & RPMSENSE_LESS)
2ff057
		*rp++ = '<';
2ff057
	    if (sense & RPMSENSE_GREATER)
2ff057
		*rp++ = '>';
2ff057
	    if (sense & RPMSENSE_EQUAL)
2ff057
		*rp++ = '=';
2ff057
	    *rp++ = ' ';
2ff057
	    *rp = 0;
2ff057
	    appendStringBuf(sb, rel);
2ff057
	    appendStringBuf(sb, EVR);
2ff057
	}
2ff057
	rc = checkDep(data->spec, N, EVR, emsg);
2ff057
	_free(N);
2ff057
	_free(EVR);
2ff057
    } else if (type == RPMRICH_PARSE_OP) {
2ff057
	appendStringBuf(sb, " ");
2ff057
	appendStringBuf(sb, rpmrichOpStr(op));
2ff057
	appendStringBuf(sb, " ");
2ff057
    }
2ff057
    return rc;
2ff057
}
2ff057
2ff057
rpmRC parseRCPOT(rpmSpec spec, Package pkg, const char *field, rpmTagVal tagN,
2ff057
	       int index, rpmsenseFlags tagflags, addReqProvFunction cb, void *cbdata)
2ff057
{
2ff057
    const char *r, *re, *v, *ve;
2ff057
    char *emsg = NULL;
2ff057
    char * N = NULL, * EVR = NULL;
2ff057
    rpmTagVal nametag = RPMTAG_NOT_FOUND;
2ff057
    rpmsenseFlags Flags;
2ff057
    rpmRC rc = RPMRC_FAIL; /* assume failure */
2ff057
    int allow_richdeps = 0;
2ff057
2ff057
    if (!cbdata)
2ff057
	cbdata = pkg;
2ff057
2ff057
    switch (tagN) {
2ff057
    default:
2ff057
    case RPMTAG_REQUIRENAME:
2ff057
	tagflags |= RPMSENSE_ANY;
2ff057
	/* fall through */
2ff057
    case RPMTAG_RECOMMENDNAME:
2ff057
    case RPMTAG_SUGGESTNAME:
2ff057
    case RPMTAG_SUPPLEMENTNAME:
2ff057
    case RPMTAG_ENHANCENAME:
2ff057
    case RPMTAG_CONFLICTNAME:
2ff057
	allow_richdeps = 1;
2ff057
	/* fall through */
2ff057
    case RPMTAG_PROVIDENAME:
2ff057
    case RPMTAG_OBSOLETENAME:
2ff057
    case RPMTAG_ORDERNAME:
2ff057
	nametag = tagN;
2ff057
	break;
2ff057
    case RPMTAG_PREREQ:
2ff057
	/* XXX map legacy PreReq into Requires(pre,preun) */
2ff057
	nametag = RPMTAG_REQUIRENAME;
2ff057
	tagflags |= (RPMSENSE_SCRIPT_PRE|RPMSENSE_SCRIPT_PREUN);
2ff057
	allow_richdeps = 1;
2ff057
	break;
2ff057
    case RPMTAG_TRIGGERPREIN:
2ff057
	nametag = RPMTAG_TRIGGERNAME;
2ff057
	tagflags |= RPMSENSE_TRIGGERPREIN;
2ff057
	break;
2ff057
    case RPMTAG_TRIGGERIN:
2ff057
	nametag = RPMTAG_TRIGGERNAME;
2ff057
	tagflags |= RPMSENSE_TRIGGERIN;
2ff057
	break;
2ff057
    case RPMTAG_TRIGGERPOSTUN:
2ff057
	nametag = RPMTAG_TRIGGERNAME;
2ff057
	tagflags |= RPMSENSE_TRIGGERPOSTUN;
2ff057
	break;
2ff057
    case RPMTAG_TRIGGERUN:
2ff057
	nametag = RPMTAG_TRIGGERNAME;
2ff057
	tagflags |= RPMSENSE_TRIGGERUN;
2ff057
	break;
2ff057
    case RPMTAG_BUILDPREREQ:
2ff057
    case RPMTAG_BUILDREQUIRES:
2ff057
	nametag = RPMTAG_REQUIRENAME;
2ff057
	tagflags |= RPMSENSE_ANY;
2ff057
	allow_richdeps = 1;
2ff057
	break;
2ff057
    case RPMTAG_BUILDCONFLICTS:
2ff057
	nametag = RPMTAG_CONFLICTNAME;
2ff057
	allow_richdeps = 1;
2ff057
	break;
2ff057
    case RPMTAG_FILETRIGGERIN:
2ff057
	nametag = RPMTAG_FILETRIGGERNAME;
2ff057
	tagflags |= RPMSENSE_TRIGGERIN;
2ff057
	break;
2ff057
    case RPMTAG_FILETRIGGERUN:
2ff057
	nametag = RPMTAG_FILETRIGGERNAME;
2ff057
	tagflags |= RPMSENSE_TRIGGERUN;
2ff057
	break;
2ff057
    case RPMTAG_FILETRIGGERPOSTUN:
2ff057
	nametag = RPMTAG_FILETRIGGERNAME;
2ff057
	tagflags |= RPMSENSE_TRIGGERPOSTUN;
2ff057
	break;
2ff057
    case RPMTAG_TRANSFILETRIGGERIN:
2ff057
	nametag = RPMTAG_TRANSFILETRIGGERNAME;
2ff057
	tagflags |= RPMSENSE_TRIGGERIN;
2ff057
	break;
2ff057
    case RPMTAG_TRANSFILETRIGGERUN:
2ff057
	nametag = RPMTAG_TRANSFILETRIGGERNAME;
2ff057
	tagflags |= RPMSENSE_TRIGGERUN;
2ff057
	break;
2ff057
    case RPMTAG_TRANSFILETRIGGERPOSTUN:
2ff057
	nametag = RPMTAG_TRANSFILETRIGGERNAME;
2ff057
	tagflags |= RPMSENSE_TRIGGERPOSTUN;
2ff057
	break;
2ff057
    }
2ff057
2ff057
    for (r = field; *r != '\0'; r = re) {
2ff057
	SKIPWHITE(r);
2ff057
	if (*r == '\0')
2ff057
	    break;
2ff057
2ff057
	Flags = (tagflags & ~RPMSENSE_SENSEMASK);
2ff057
2ff057
	if (r[0] == '(') {
2ff057
	    struct parseRCPOTRichData data;
2ff057
	    if (!allow_richdeps) {
2ff057
		rasprintf(&emsg, _("No rich dependencies allowed for this type"));
2ff057
		goto exit;
2ff057
	    }
2ff057
	    data.spec = spec;
2ff057
	    data.sb = newStringBuf();
2ff057
	    if (rpmrichParseForTag(&r, &emsg, parseRCPOTRichCB, &data, nametag) != RPMRC_OK) {
2ff057
		freeStringBuf(data.sb);
2ff057
		goto exit;
2ff057
	    }
2ff057
	    if (cb && cb(cbdata, nametag, getStringBuf(data.sb), NULL, Flags, index) != RPMRC_OK) {
2ff057
		rasprintf(&emsg, _("invalid dependency"));
2ff057
		freeStringBuf(data.sb);
2ff057
		goto exit;
2ff057
	    }
2ff057
	    freeStringBuf(data.sb);
2ff057
	    re = r;
2ff057
	    continue;
2ff057
	}
2ff057
2ff057
	re = r;
2ff057
	SKIPNONWHITE(re);
2ff057
	N = xmalloc((re-r) + 1);
2ff057
	rstrlcpy(N, r, (re-r) + 1);
2ff057
2ff057
	/* Parse EVR */
2ff057
	EVR = NULL;
2ff057
	v = re;
2ff057
	SKIPWHITE(v);
2ff057
	ve = v;
2ff057
	SKIPNONWHITE(ve);
2ff057
2ff057
	re = v;	/* ==> next token (if no EVR found) starts here */
2ff057
2ff057
	/* Check for possible logical operator */
2ff057
	if (ve > v) {
2ff057
	    rpmsenseFlags sense = rpmParseDSFlags(v, ve - v);
2ff057
	    if (sense) {
2ff057
		Flags |= sense;
2ff057
2ff057
		/* now parse EVR */
2ff057
		v = ve;
2ff057
		SKIPWHITE(v);
2ff057
		ve = v;
2ff057
		SKIPNONWHITE(ve);
2ff057
		if (*v == '\0' || ve == v) {
2ff057
		    rasprintf(&emsg, _("Version required"));
2ff057
		    goto exit;
2ff057
		}
2ff057
		EVR = xmalloc((ve-v) + 1);
2ff057
		rstrlcpy(EVR, v, (ve-v) + 1);
2ff057
		re = ve;	/* ==> next token after EVR string starts here */
2ff057
	    }
2ff057
	}
2ff057
2ff057
	/* check that dependency is well-formed */
2ff057
	if (checkDep(spec, N, EVR, &emsg))
2ff057
	    goto exit;
2ff057
2ff057
	if (nametag == RPMTAG_FILETRIGGERNAME ||
2ff057
	    nametag == RPMTAG_TRANSFILETRIGGERNAME) {
2ff057
	    if (N[0] != '/') {
2ff057
		rasprintf(&emsg, _("Only absolute paths are allowed in "
2ff057
				    "file triggers"));
2ff057
	    }
2ff057
	}
2ff057
2ff057
2ff057
	/* Deny more "normal" triggers fired by the same pakage. File triggers are ok */
2ff057
	if (nametag == RPMTAG_TRIGGERNAME) {
2ff057
	    rpmds *pdsp = packageDependencies(pkg, nametag);
2ff057
	    rpmds newds = rpmdsSingle(nametag, N, EVR, Flags);
2ff057
	    rpmdsInit(*pdsp);
2ff057
	    while (rpmdsNext(*pdsp) >= 0) {
2ff057
		if (rpmdsCompare(*pdsp, newds) && (rpmdsFlags(*pdsp) & tagflags )) {
2ff057
		    rasprintf(&emsg, _("Trigger fired by the same package "
2ff057
			"is already defined in spec file"));
2ff057
		    break;
2ff057
		}
2ff057
	    }
2ff057
	    rpmdsFree(newds);
2ff057
	    if (emsg)
2ff057
		goto exit;
2ff057
	}
2ff057
2ff057
	if (cb && cb(cbdata, nametag, N, EVR, Flags, index) != RPMRC_OK) {
2ff057
	    rasprintf(&emsg, _("invalid dependency"));
2ff057
	    goto exit;
2ff057
	}
2ff057
2ff057
	N = _free(N);
2ff057
	EVR = _free(EVR);
2ff057
2ff057
    }
2ff057
    rc = RPMRC_OK;
2ff057
2ff057
exit:
2ff057
    if (emsg) {
2ff057
	int lvl = (rc == RPMRC_OK) ? RPMLOG_WARNING : RPMLOG_ERR;
2ff057
	/* Automatic dependencies don't relate to spec lines */
2ff057
	if (tagflags & (RPMSENSE_FIND_REQUIRES|RPMSENSE_FIND_PROVIDES)) {
2ff057
	    rpmlog(lvl, "%s: %s\n", emsg, r);
2ff057
	} else {
2ff057
	    rpmlog(lvl, _("line %d: %s: %s\n"),
2ff057
		   spec->lineNum, emsg, spec->line);
2ff057
	}
2ff057
	free(emsg);
2ff057
    }
2ff057
    _free(N);
2ff057
    _free(EVR);
2ff057
2ff057
    return rc;
2ff057
}