This change introduces a separate routine to parse for valid macro names. Valid macro names are either regular 3+ character identifiers, or special names: "S", "P", "0", "#", "*", "**", macro options such as "-o" and "-o*", and macro arguments such as "1". Other names are not valid. This fixes a number of bugs seen earlier due to sloppy name parsing: "%_libdir*" and "%01" were not expanded (these are now expanded to e.g. "/usr/lib64*" and "<name>1", as expected). This also fixes bugs in as-is substitution: "%!foo" was expanded to "%foo", and likewise "%!!!" was expanded to "%" (and to "%<garbage>" at EOL). Also, bad names in %name and %{name...} substitutions are now handled differently. In %name form, the name is parsed tentatively; a silent fall-back to as-is substitution is provisioned when no valid name can be obtain. In %{name...} form, a failure to obtain a valid name is now a syntax error. Furthermore, only 3 variants are syntactically valid: %{name} proper, %{name:...}, and %{name ...}. This renders invalid ambiguous macro substitutions such as the one found in FC18 lvm2.spec: Requires: util-linux >= %{util-linux_version} error: Invalid macro syntax: %{util-linux_version} --- rpmio/macro.c | 164 +++++++++++++++++++++++++++++++++++++++------------------- 1 file changed, 111 insertions(+), 53 deletions(-) diff --git a/rpmio/macro.c b/rpmio/macro.c index 9229580..2abdbd4 100644 --- a/rpmio/macro.c +++ b/rpmio/macro.c @@ -1011,6 +1011,93 @@ doFoo(MacroBuf mb, int negate, const char * f, size_t fn, } /** + * Parse for flags before macro name, such as in %{?!name:subst}. + * @param s macro flags start + * @param negate flipped by each '!' flag + * @param chkexist increased by each '?' flag + * @return macro flags end + */ +static const char * +parseMacroFlags(const char *s, int *negate, int *chkexist) +{ + while (1) { + switch (*s) { + case '!': + *negate = !*negate; + s++; + break; + case '?': + (*chkexist)++; + s++; + break; + default: + return s; + } + } + /* not reached */ + return s; +} + +/** + * Parse for valid macro name (during expansion). + * @param s macro name start + * @return macro name end, NULL on error + */ +static const char * +parseMacroName(const char *s) +{ + /* alnum identifiers */ + if (risalpha(*s) || *s == '_') { + const char *se = s + 1; + while (risalnum(*se) || *se == '_') + se++; + switch (se - s) { + case 1: + /* recheck for [SPF] */ + break; + case 2: + return NULL; + default: + return se; + } + } + /* simple special names */ + switch (*s) { + case '0': + case '#': + case 'S': + case 'P': + case 'F': + s++; + return s; + case '*': + s++; + if (*s == '*') + s++; + return s; + /* option names */ + case '-': + s++; + if (risalnum(*s)) + s++; + else + return NULL; + if (*s == '*') + s++; + return s; + } + /* argument names */ + if (risdigit(*s)) { + s++; + while (risdigit(*s)) + s++; + return s; + } + /* invalid macro name */ + return NULL; +} + +/** * The main macro recursion loop. * @param mb macro expansion state * @param src string to expand @@ -1083,34 +1170,14 @@ expandMacro(MacroBuf mb, const char *src, size_t slen) 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; + f = parseMacroFlags(s, &negate, &chkexist); + fe = parseMacroName(f); + /* no valid name? assume as-is substitution */ + if (fe == NULL) { + mbAppend(mb, '%'); + continue; } - fe = se; + se = fe; /* For "%name " macros ... */ if ((c = *fe) && isblank(c)) if ((lastc = strchr(fe,'\n')) == NULL) @@ -1140,48 +1207,39 @@ expandMacro(MacroBuf mb, const char *src, size_t slen) rc = 1; continue; } - f = s+1;/* skip { */ + f = parseMacroFlags(s + 1 /* skip { */, &negate, &chkexist); + fe = parseMacroName(f); se++; /* skip } */ - while (strchr("!?", *f) != NULL) { - switch(*f++) { - case '!': - negate = ((negate + 1) % 2); - break; - case '?': - chkexist++; - break; - } + /* no valid name? syntax error */ + if (fe == NULL) { + rpmlog(RPMLOG_ERR, + _("Invalid macro name: %%%.*s\n"), (int)(se - s), s); + rc = 1; + continue; } - for (fe = f; (c = *fe) && !strchr(" :}", c);) - fe++; - switch (c) { + switch (*fe) { case ':': g = fe + 1; ge = se - 1; break; case ' ': + case '\t': lastc = se-1; break; - default: + case '}': break; + default: + rpmlog(RPMLOG_ERR, + _("Invalid macro syntax: %%%.*s\n"), (int)(se - s), s); + rc = 1; + continue; } break; } - /* XXX Everything below expects fe > f */ + assert(fe > f); fn = (fe - f); gn = (ge - g); - if ((fe - f) <= 0) { -/* XXX Process % in unknown context */ - c = '%'; /* XXX only need to save % */ - mbAppend(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); -- 1.8.1 _______________________________________________ Rpm-list mailing list Rpm-list@xxxxxxxxxxxxx http://lists.rpm.org/mailman/listinfo/rpm-list