/** * Check for inappropriate characters. All alphanums are considered sane. * @param spec spec (or NULL) * @param field string to check * @param whitelist string of permitted characters * @return RPMRC_OK if OK */ rpmRC rpmCharCheck(rpmSpec spec, const char *field, const char *whitelist) { const char *ch; char *err = NULL; rpmRC rc = RPMRC_OK; for (ch=field; *ch; ch++) { if (risalnum(*ch) || strchr(whitelist, *ch)) continue; rasprintf(&err, _("Illegal char '%c' (0x%x)"), isprint(*ch) ? *ch : '?', *ch); } for (ch=field; *ch; ch++) { if (strchr("%{}", *ch)) { specLog(spec, RPMLOG_WARNING, field, _("Possible unexpanded macro")); break; } } if (err == NULL && strstr(field, "..") != NULL) { rasprintf(&err, _("Illegal sequence \"..\"")); } if (err) { specLog(spec, RPMLOG_ERR, field, err); free(err); rc = RPMRC_FAIL; } return rc; }
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) return RPMRC_FAIL; } return RPMRC_OK; }
/** * The main macro recursion loop. * @todo Dynamically reallocate target buffer. * @param mb macro expansion state * @return 0 on success, 1 on failure */ static int expandMacro(MacroBuf mb) { rpmMacroEntry *mep; rpmMacroEntry me; const char *s = mb->s, *se; const char *f, *fe; const char *g, *ge; size_t fn, gn; char *t = mb->t; /* save expansion pointer for printExpand */ int c; int rc = 0; int negate; const char * lastc; int chkexist; if (++mb->depth > max_macro_depth) { rpmlog(RPMLOG_ERR, _("Recursion depth(%d) greater than max(%d)\n"), mb->depth, max_macro_depth); mb->depth--; mb->expand_trace = 1; return 1; } while (rc == 0 && mb->nb > 0 && (c = *s) != '\0') { s++; /* Copy text until next macro */ switch(c) { case '%': if (*s) { /* Ensure not end-of-string. */ if (*s != '%') break; s++; /* skip first % in %% */ } default: SAVECHAR(mb, c); continue; break; } /* Expand next macro */ f = fe = NULL; g = ge = NULL; if (mb->depth > 1) /* XXX full expansion for outermost level */ t = mb->t; /* save expansion pointer for printExpand */ negate = 0; lastc = NULL; chkexist = 0; switch ((c = *s)) { default: /* %name substitution */ while (strchr("!?", *s) != NULL) { switch(*s++) { case '!': negate = ((negate + 1) % 2); break; case '?': chkexist++; break; } } f = se = s; if (*se == '-') se++; while((c = *se) && (risalnum(c) || c == '_')) se++; /* Recognize non-alnum macros too */ switch (*se) { case '*': se++; if (*se == '*') se++; break; case '#': se++; break; default: break; } fe = se; /* For "%name " macros ... */ if ((c = *fe) && isblank(c)) if ((lastc = strchr(fe,'\n')) == NULL) lastc = strchr(fe, '\0'); break; case '(': /* %(...) shell escape */ if ((se = matchchar(s, c, ')')) == NULL) { rpmlog(RPMLOG_ERR, _("Unterminated %c: %s\n"), (char)c, s); rc = 1; continue; } if (mb->macro_trace) printMacro(mb, s, se+1); s++; /* skip ( */ rc = doShellEscape(mb, s, (se - s)); se++; /* skip ) */ s = se; continue; break; case '{': /* %{...}/%{...:...} substitution */ if ((se = matchchar(s, c, '}')) == NULL) { rpmlog(RPMLOG_ERR, _("Unterminated %c: %s\n"), (char)c, s); rc = 1; continue; } f = s+1;/* skip { */ se++; /* skip } */ while (strchr("!?", *f) != NULL) { switch(*f++) { case '!': negate = ((negate + 1) % 2); break; case '?': chkexist++; break; } } for (fe = f; (c = *fe) && !strchr(" :}", c);) fe++; switch (c) { case ':': g = fe + 1; ge = se - 1; break; case ' ': lastc = se-1; break; default: break; } break; } /* XXX Everything below expects fe > f */ fn = (fe - f); gn = (ge - g); if ((fe - f) <= 0) { /* XXX Process % in unknown context */ c = '%'; /* XXX only need to save % */ SAVECHAR(mb, c); #if 0 rpmlog(RPMLOG_ERR, _("A %% is followed by an unparseable macro\n")); #endif s = se; continue; } if (mb->macro_trace) printMacro(mb, s, se); /* Expand builtin macros */ if (STREQ("global", f, fn)) { s = doDefine(mb, se, RMIL_GLOBAL, 1); continue; } if (STREQ("define", f, fn)) { s = doDefine(mb, se, mb->depth, 0); continue; } if (STREQ("undefine", f, fn)) { s = doUndefine(mb->mc, se); continue; } if (STREQ("echo", f, fn) || STREQ("warn", f, fn) || STREQ("error", f, fn)) { int waserror = 0; if (STREQ("error", f, fn)) waserror = 1; if (g != NULL && g < ge) doOutput(mb, waserror, g, gn); else doOutput(mb, waserror, f, fn); s = se; continue; } if (STREQ("trace", f, fn)) { /* XXX TODO restore expand_trace/macro_trace to 0 on return */ mb->expand_trace = mb->macro_trace = (negate ? 0 : mb->depth); if (mb->depth == 1) { print_macro_trace = mb->macro_trace; print_expand_trace = mb->expand_trace; } s = se; continue; } if (STREQ("dump", f, fn)) { rpmDumpMacroTable(mb->mc, NULL); while (iseol(*se)) se++; s = se; continue; } #ifdef WITH_LUA if (STREQ("lua", f, fn)) { rpmlua lua = NULL; /* Global state. */ const char *ls = s+sizeof("{lua:")-1; const char *lse = se-sizeof("}")+1; char *scriptbuf = (char *)xmalloc((lse-ls)+1); const char *printbuf; memcpy(scriptbuf, ls, lse-ls); scriptbuf[lse-ls] = '\0'; rpmluaSetPrintBuffer(lua, 1); if (rpmluaRunScript(lua, scriptbuf, NULL) == -1) rc = 1; printbuf = rpmluaGetPrintBuffer(lua); if (printbuf) { size_t len = strlen(printbuf); if (len > mb->nb) len = mb->nb; memcpy(mb->t, printbuf, len); mb->t += len; mb->nb -= len; } rpmluaSetPrintBuffer(lua, 0); free(scriptbuf); s = se; continue; } #endif /* XXX necessary but clunky */ if (STREQ("basename", f, fn) || STREQ("suffix", f, fn) || STREQ("expand", f, fn) || STREQ("verbose", f, fn) || STREQ("uncompress", f, fn) || STREQ("url2path", f, fn) || STREQ("u2p", f, fn) || STREQ("getenv", f, fn) || STREQ("S", f, fn) || STREQ("P", f, fn) || STREQ("F", f, fn)) { /* FIX: verbose may be set */ doFoo(mb, negate, f, fn, g, gn); s = se; continue; } /* Expand defined macros */ mep = findEntry(mb->mc, f, fn); me = (mep ? *mep : NULL); /* XXX Special processing for flags */ if (*f == '-') { if (me) me->used++; /* Mark macro as used */ if ((me == NULL && !negate) || /* Without -f, skip %{-f...} */ (me != NULL && negate)) { /* With -f, skip %{!-f...} */ s = se; continue; } if (g && g < ge) { /* Expand X in %{-f:X} */ rc = expandT(mb, g, gn); } else if (me && me->body && *me->body) {/* Expand %{-f}/%{-f*} */ rc = expandT(mb, me->body, strlen(me->body)); } s = se; continue; } /* XXX Special processing for macro existence */ if (chkexist) { if ((me == NULL && !negate) || /* Without -f, skip %{?f...} */ (me != NULL && negate)) { /* With -f, skip %{!?f...} */ s = se; continue; } if (g && g < ge) { /* Expand X in %{?f:X} */ rc = expandT(mb, g, gn); } else if (me && me->body && *me->body) { /* Expand %{?f}/%{?f*} */ rc = expandT(mb, me->body, strlen(me->body)); } s = se; continue; } if (me == NULL) { /* leave unknown %... as is */ #ifndef HACK #if DEAD /* XXX hack to skip over empty arg list */ if (fn == 1 && *f == '*') { s = se; continue; } #endif /* XXX hack to permit non-overloaded %foo to be passed */ c = '%'; /* XXX only need to save % */ SAVECHAR(mb, c); #else rpmlog(RPMLOG_ERR, _("Macro %%%.*s not found, skipping\n"), fn, f); s = se; #endif continue; } /* Setup args for "%name " macros with opts */ if (me && me->opts != NULL) { if (lastc != NULL) { se = grabArgs(mb, me, fe, lastc); } else { addMacro(mb->mc, "**", NULL, "", mb->depth); addMacro(mb->mc, "*", NULL, "", mb->depth); addMacro(mb->mc, "#", NULL, "0", mb->depth); addMacro(mb->mc, "0", NULL, me->name, mb->depth); } } /* Recursively expand body of macro */ if (me->body && *me->body) { mb->s = me->body; rc = expandMacro(mb); if (rc == 0) me->used++; /* Mark macro as used */ } /* Free args for "%name " macros with opts */ if (me->opts != NULL) freeArgs(mb); s = se; } *mb->t = '\0'; mb->s = s; mb->depth--; if (rc != 0 || mb->expand_trace) printExpansion(mb, t, mb->t); return rc; }
/* -1: b is newer than a */ int rpmvercmp(const char * a, const char * b) { char oldch1, oldch2; char abuf[strlen(a)+1], bbuf[strlen(b)+1]; char *str1 = abuf, *str2 = bbuf; char * one, * two; int rc; int isnum; /* easy comparison to see if versions are identical */ if (rstreq(a, b)) return 0; strcpy(str1, a); strcpy(str2, b); one = str1; two = str2; /* loop through each version segment of str1 and str2 and compare them */ while (*one || *two) { while (*one && !risalnum(*one) && *one != '~') one++; while (*two && !risalnum(*two) && *two != '~') two++; /* handle the tilde separator, it sorts before everthing else */ if (*one == '~' || *two == '~') { if (*one != '~') return 1; if (*two != '~') return -1; one++; two++; continue; } /* If we ran to the end of either, we are finished with the loop */ if (!(*one && *two)) break; str1 = one; str2 = two; /* grab first completely alpha or completely numeric segment */ /* leave one and two pointing to the start of the alpha or numeric */ /* segment and walk str1 and str2 to end of segment */ if (risdigit(*str1)) { while (*str1 && risdigit(*str1)) str1++; while (*str2 && risdigit(*str2)) str2++; isnum = 1; } else { while (*str1 && risalpha(*str1)) str1++; while (*str2 && risalpha(*str2)) str2++; isnum = 0; } /* save character at the end of the alpha or numeric segment */ /* so that they can be restored after the comparison */ oldch1 = *str1; *str1 = '\0'; oldch2 = *str2; *str2 = '\0'; /* this cannot happen, as we previously tested to make sure that */ /* the first string has a non-null segment */ if (one == str1) return -1; /* arbitrary */ /* take care of the case where the two version segments are */ /* different types: one numeric, the other alpha (i.e. empty) */ /* numeric segments are always newer than alpha segments */ /* XXX See patch #60884 (and details) from bugzilla #50977. */ if (two == str2) return (isnum ? 1 : -1); if (isnum) { /* this used to be done by converting the digit segments */ /* to ints using atoi() - it's changed because long */ /* digit segments can overflow an int - this should fix that. */ /* throw away any leading zeros - it's a number, right? */ while (*one == '0') one++; while (*two == '0') two++; /* whichever number has more digits wins */ if (strlen(one) > strlen(two)) return 1; if (strlen(two) > strlen(one)) return -1; } /* strcmp will return which one is greater - even if the two */ /* segments are alpha or if they are numeric. don't return */ /* if they are equal because there might be more segments to */ /* compare */ rc = strcmp(one, two); if (rc) return (rc < 1 ? -1 : 1); /* restore character that was replaced by null above */ *str1 = oldch1; one = str1; *str2 = oldch2; two = str2; } /* this catches the case where all numeric and alpha segments have */ /* compared identically but the segment sepparating characters were */ /* different */ if ((!*one) && (!*two)) return 0; /* whichever version still has characters left over wins */ if (!*one) return -1; else return 1; }
/** * @param state expression parser state */ static int rdToken(ParseState state) { int token; Value v = NULL; char *p = state->p; /* Skip whitespace before the next token. */ while (*p && risspace(*p)) p++; switch (*p) { case '\0': token = TOK_EOF; p--; break; case '+': token = TOK_ADD; break; case '-': token = TOK_MINUS; break; case '*': token = TOK_MULTIPLY; break; case '/': token = TOK_DIVIDE; break; case '(': token = TOK_OPEN_P; break; case ')': token = TOK_CLOSE_P; break; case '=': if (p[1] == '=') { token = TOK_EQ; p++; } else { rpmlog(RPMLOG_ERR, _("syntax error while parsing ==\n")); return -1; } break; case '!': if (p[1] == '=') { token = TOK_NEQ; p++; } else token = TOK_NOT; break; case '<': if (p[1] == '=') { token = TOK_LE; p++; } else token = TOK_LT; break; case '>': if (p[1] == '=') { token = TOK_GE; p++; } else token = TOK_GT; break; case '&': if (p[1] == '&') { token = TOK_LOGICAL_AND; p++; } else { rpmlog(RPMLOG_ERR, _("syntax error while parsing &&\n")); return -1; } break; case '|': if (p[1] == '|') { token = TOK_LOGICAL_OR; p++; } else { rpmlog(RPMLOG_ERR, _("syntax error while parsing ||\n")); return -1; } break; default: if (risdigit(*p)) { char *temp; size_t ts; for (ts=1; p[ts] && risdigit(p[ts]); ts++); temp = xmalloc(ts+1); memcpy(temp, p, ts); p += ts-1; temp[ts] = '\0'; token = TOK_INTEGER; v = valueMakeInteger(atoi(temp)); free(temp); } else if (risalpha(*p)) { char *temp; size_t ts; for (ts=1; p[ts] && (risalnum(p[ts]) || p[ts] == '_'); ts++); temp = xmalloc(ts+1); memcpy(temp, p, ts); p += ts-1; temp[ts] = '\0'; token = TOK_IDENTIFIER; v = valueMakeString(temp); } else if (*p == '\"') { char *temp; size_t ts; p++; for (ts=0; p[ts] && p[ts] != '\"'; ts++); temp = xmalloc(ts+1); memcpy(temp, p, ts); p += ts-1; temp[ts] = '\0'; p++; token = TOK_STRING; v = valueMakeString( rpmExpand(temp, NULL) ); free(temp); } else { rpmlog(RPMLOG_ERR, _("parse error in expression\n")); return -1; } } state->p = p + 1; state->nextToken = token; state->tokenValue = v; DEBUG(printf("rdToken: \"%s\" (%d)\n", prToken(token), token)); DEBUG(valueDump("rdToken:", state->tokenValue, stdout)); return 0; }
rpmRC parseRCPOT(rpmSpec spec, Package pkg, const char *field, rpmTagVal tagN, int index, rpmsenseFlags tagflags) { const char *r, *re, *v, *ve; const char *emsg = NULL; char * N = NULL, * EVR = NULL; rpmTagVal nametag = RPMTAG_NOT_FOUND; rpmsenseFlags Flags; Header h = pkg->header; /* everything except buildrequires go here */ rpmRC rc = RPMRC_FAIL; /* assume failure */ switch (tagN) { default: case RPMTAG_REQUIREFLAGS: nametag = 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; break; case RPMTAG_PREREQ: /* XXX map legacy PreReq into Requires(pre,preun) */ nametag = RPMTAG_REQUIRENAME; tagflags |= (RPMSENSE_SCRIPT_PRE|RPMSENSE_SCRIPT_PREUN); break; case RPMTAG_TRIGGERPREIN: nametag = RPMTAG_TRIGGERNAME; tagflags |= RPMSENSE_TRIGGERPREIN; break; case RPMTAG_TRIGGERIN: nametag = RPMTAG_TRIGGERNAME; tagflags |= RPMSENSE_TRIGGERIN; break; case RPMTAG_TRIGGERPOSTUN: nametag = RPMTAG_TRIGGERNAME; tagflags |= RPMSENSE_TRIGGERPOSTUN; break; case RPMTAG_TRIGGERUN: nametag = RPMTAG_TRIGGERNAME; tagflags |= RPMSENSE_TRIGGERUN; break; case RPMTAG_BUILDPREREQ: case RPMTAG_BUILDREQUIRES: nametag = RPMTAG_REQUIRENAME; tagflags |= RPMSENSE_ANY; h = spec->buildRestrictions; break; case RPMTAG_BUILDCONFLICTS: nametag = RPMTAG_CONFLICTNAME; h = spec->buildRestrictions; break; } for (r = field; *r != '\0'; r = re) { SKIPWHITE(r); if (*r == '\0') 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; } re = r; SKIPNONWHITE(re); N = xmalloc((re-r) + 1); rstrlcpy(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] == '/') { emsg = _("Versioned file name not permitted"); goto exit; } Flags |= rc->sense; /* now parse EVR */ v = ve; SKIPWHITE(v); ve = v; SKIPNONWHITE(ve); break; } } if (Flags & RPMSENSE_SENSEMASK) { if (*v == '\0' || ve == v) { emsg = _("Version required"); goto exit; } 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(h, nametag, N, EVR, Flags, index)) { emsg = _("invalid dependency"); goto exit; } N = _free(N); EVR = _free(EVR); } rc = RPMRC_OK; exit: if (emsg) { /* Automatic dependencies don't relate to spec lines */ if (tagflags & (RPMSENSE_FIND_REQUIRES|RPMSENSE_FIND_PROVIDES)) { rpmlog(RPMLOG_ERR, "%s: %s\n", emsg, r); } else { rpmlog(RPMLOG_ERR, _("line %d: %s: %s\n"), spec->lineNum, emsg, spec->line); } } free(N); free(EVR); return rc; }