Unify builtin and user-defined parametric macro calling syntax

Accept both %{foo:arg} and %{foo arg} notation for both builtins
and user-defined parametric macros, the former only ever has one
argument whereas the latter can have multiple.

"parse" type builtins such as %define are trickier and only work with
traditional syntax for now.
This commit is contained in:
Panu Matilainen 2020-11-12 12:45:01 +02:00
parent 8901a6be16
commit 1de248d440
2 changed files with 75 additions and 40 deletions

View File

@ -44,6 +44,7 @@ enum macroFlags_e {
#define ME_BUILTIN (ME_PARSE|ME_FUNC) #define ME_BUILTIN (ME_PARSE|ME_FUNC)
#define ME_ARGFUNC (ME_FUNC|ME_HAVEARG) #define ME_ARGFUNC (ME_FUNC|ME_HAVEARG)
#define ME_ARGPARSE (ME_PARSE|ME_HAVEARG)
/*! The structure used to store a macro. */ /*! The structure used to store a macro. */
struct rpmMacroEntry_s { struct rpmMacroEntry_s {
@ -1251,9 +1252,9 @@ static struct builtins_s {
{ "P", doSP, ME_ARGFUNC }, { "P", doSP, ME_ARGFUNC },
{ "S", doSP, ME_ARGFUNC }, { "S", doSP, ME_ARGFUNC },
{ "basename", doFoo, ME_ARGFUNC }, { "basename", doFoo, ME_ARGFUNC },
{ "define", doDef, ME_PARSE }, { "define", doDef, ME_ARGPARSE },
{ "dirname", doFoo, ME_ARGFUNC }, { "dirname", doFoo, ME_ARGFUNC },
{ "dnl", doDnl, ME_PARSE }, { "dnl", doDnl, ME_ARGPARSE },
{ "dump", doDump, ME_PARSE }, { "dump", doDump, ME_PARSE },
{ "echo", doOutput, ME_ARGFUNC }, { "echo", doOutput, ME_ARGFUNC },
{ "error", doOutput, ME_ARGFUNC }, { "error", doOutput, ME_ARGFUNC },
@ -1263,7 +1264,7 @@ static struct builtins_s {
{ "getconfdir", doFoo, ME_FUNC }, { "getconfdir", doFoo, ME_FUNC },
{ "getenv", doFoo, ME_ARGFUNC }, { "getenv", doFoo, ME_ARGFUNC },
{ "getncpus", doFoo, ME_FUNC }, { "getncpus", doFoo, ME_FUNC },
{ "global", doGlobal, ME_PARSE }, { "global", doGlobal, ME_ARGPARSE },
{ "load", doLoad, ME_ARGFUNC }, { "load", doLoad, ME_ARGFUNC },
#ifdef WITH_LUA #ifdef WITH_LUA
{ "lua", doLua, ME_ARGFUNC }, { "lua", doLua, ME_ARGFUNC },
@ -1275,7 +1276,7 @@ static struct builtins_s {
{ "trace", doTrace, ME_FUNC }, { "trace", doTrace, ME_FUNC },
{ "u2p", doFoo, ME_ARGFUNC }, { "u2p", doFoo, ME_ARGFUNC },
{ "uncompress", doUncompress, ME_ARGFUNC }, { "uncompress", doUncompress, ME_ARGFUNC },
{ "undefine", doUndefine, ME_PARSE }, { "undefine", doUndefine, ME_ARGPARSE },
{ "url2path", doFoo, ME_ARGFUNC }, { "url2path", doFoo, ME_ARGFUNC },
{ "verbose", doVerbose, ME_FUNC }, { "verbose", doVerbose, ME_FUNC },
{ "warn", doOutput, ME_ARGFUNC }, { "warn", doOutput, ME_ARGFUNC },
@ -1329,29 +1330,48 @@ const char *findMacroEnd(const char *str)
* @param mb macro expansion state * @param mb macro expansion state
* @param me macro entry slot * @param me macro entry slot
* @param args arguments for parametric macros * @param args arguments for parametric macros
* @param parsed how many characters ME_PARSE parsed (or NULL)
*/ */
static void static void
doExpandThisMacro(MacroBuf mb, rpmMacroEntry me, ARGV_t args) doExpandThisMacro(MacroBuf mb, rpmMacroEntry me, ARGV_t args, size_t *parsed)
{ {
rpmMacroEntry prevme = mb->me; rpmMacroEntry prevme = mb->me;
ARGV_t prevarg = mb->args; ARGV_t prevarg = mb->args;
/* Setup args for "%name " macros with opts */
if (args != NULL)
setupArgs(mb, me, args);
/* Recursively expand body of macro */ /* Recursively expand body of macro */
if (me->flags & ME_BUILTIN) { if (me->flags & ME_BUILTIN) {
/* XXX not yet */ int nargs = argvCount(args) - 1;
mbErr(mb, 1, "tried to expand a builtin: %s\n", me->name); int needarg = (me->flags & ME_HAVEARG) ? 1 : 0;
int havearg = (nargs > 0);
if (needarg != havearg) {
mbErr(mb, 1, "%%%s: %s\n", me->name, needarg ?
_("argument expected") : _("unexpected argument"));
goto exit;
}
const char *arg = havearg ? args[1] : NULL;
if (me->flags & ME_PARSE) {
parseFunc parse = me->func;
const char *s = parse(mb, me, arg);
if (parsed)
*parsed = s-arg;
} else {
macroFunc func = me->func;
func(mb, me, arg, arg ? strlen(arg) : 0);
}
} else if (me->body && *me->body) { } else if (me->body && *me->body) {
/* Setup args for "%name " macros with opts */
if (args != NULL)
setupArgs(mb, me, args);
if ((me->flags & ME_LITERAL) != 0) if ((me->flags & ME_LITERAL) != 0)
mbAppendStr(mb, me->body); mbAppendStr(mb, me->body);
else else
expandMacro(mb, me->body, 0); expandMacro(mb, me->body, 0);
/* Free args for "%name " macros with opts */
if (args != NULL)
freeArgs(mb);
} }
/* Free args for "%name " macros with opts */ exit:
if (args != NULL)
freeArgs(mb);
mb->args = prevarg; mb->args = prevarg;
mb->me = prevme; mb->me = prevme;
} }
@ -1504,7 +1524,7 @@ expandMacro(MacroBuf mb, const char *src, size_t slen)
if (g && g < ge) { /* Expand X in %{...:X} */ if (g && g < ge) { /* Expand X in %{...:X} */
expandMacro(mb, g, gn); expandMacro(mb, g, gn);
} else if (me) { } else if (me) {
doExpandThisMacro(mb, me, NULL); doExpandThisMacro(mb, me, NULL, NULL);
} }
s = se; s = se;
continue; continue;
@ -1517,36 +1537,23 @@ expandMacro(MacroBuf mb, const char *src, size_t slen)
continue; continue;
} }
/* Expand builtin macros */
if (me->flags & ME_BUILTIN) {
int havearg = (me->flags & ME_HAVEARG) ? 1 : 0;
if (havearg != (g != NULL)) {
mbErr(mb, 1, "%%%s: %s\n", me->name, havearg ?
_("argument expected") : _("unexpected argument"));
continue;
}
if (me->flags & ME_PARSE) {
parseFunc parse = me->func;
s = parse(mb, me, se);
} else {
macroFunc func = me->func;
func(mb, me, g, gn);
s = se;
}
continue;
}
/* Grab args for parametric macros */ /* Grab args for parametric macros */
ARGV_t args = NULL; ARGV_t args = NULL;
size_t fwd = 0;
if (me->opts != NULL) { if (me->opts != NULL) {
argvAdd(&args, me->name); argvAdd(&args, me->name);
if (lastc) if (g) {
se = grabArgs(mb, &args, fe, lastc); argvAddN(&args, g, gn);
} else if (me->flags & ME_PARSE) {
argvAdd(&args, se);
} else if (lastc) {
fwd = (grabArgs(mb, &args, fe, lastc) - se);
}
} }
doExpandThisMacro(mb, me, args); doExpandThisMacro(mb, me, args, &fwd);
if (args != NULL) if (args != NULL)
argvFree(args); argvFree(args);
s = se; s = se + (g ? 0 : fwd);
} }
mbFini(mb, &med); mbFini(mb, &med);
@ -1596,7 +1603,7 @@ expandThisMacro(MacroBuf mb, rpmMacroEntry me, ARGV_const_t args, int flags)
} }
} }
doExpandThisMacro(mb, me, optargs); doExpandThisMacro(mb, me, optargs, NULL);
if (optargs) if (optargs)
argvFree(optargs); argvFree(optargs);
@ -1950,7 +1957,7 @@ rpmInitMacros(rpmMacroContext mc, const char * macrofiles)
/* Define built-in macros */ /* Define built-in macros */
for (const struct builtins_s *b = builtinmacros; b->name; b++) { for (const struct builtins_s *b = builtinmacros; b->name; b++) {
pushMacroAny(mc, b->name, NULL, "<builtin>", b->func, pushMacroAny(mc, b->name, "", "<builtin>", b->func,
RMIL_BUILTIN, b->flags); RMIL_BUILTIN, b->flags);
} }

View File

@ -302,29 +302,57 @@ AT_CHECK([
runroot rpm --eval "%{dirname}" runroot rpm --eval "%{dirname}"
runroot rpm --eval "%{dirname:}" runroot rpm --eval "%{dirname:}"
runroot rpm --eval "%{dirname:dir}" runroot rpm --eval "%{dirname:dir}"
runroot rpm --eval "%{dirname dir}"
runroot rpm --define '%xxx /hello/%%%%/world' --eval '%{dirname:%xxx}' runroot rpm --define '%xxx /hello/%%%%/world' --eval '%{dirname:%xxx}'
runroot rpm --eval "%{uncompress}" runroot rpm --eval "%{uncompress}"
runroot rpm --eval "%{uncompress:}" runroot rpm --eval "%{uncompress:}"
runroot rpm --eval "%{getncpus:}" runroot rpm --eval "%{getncpus:}"
runroot rpm --eval "%{getncpus:5}" runroot rpm --eval "%{getncpus:5}"
runroot rpm --eval "%{define:}" runroot rpm --eval "%{define:}"
runroot rpm --eval "%{define:foo}"
runroot rpm --eval "%{define:foo bar}%{foo}"
runroot rpm --eval "%{dump:foo}" runroot rpm --eval "%{dump:foo}"
], ],
[1], [1],
[ [
dir dir
dir
/hello/%% /hello/%%
bar
], ],
[error: %dirname: argument expected [error: %dirname: argument expected
error: %uncompress: argument expected error: %uncompress: argument expected
error: %getncpus: unexpected argument error: %getncpus: unexpected argument
error: %getncpus: unexpected argument error: %getncpus: unexpected argument
error: %define: unexpected argument error: Macro % has illegal name (%define)
error: Macro %foo has empty body
error: %dump: unexpected argument error: %dump: unexpected argument
]) ])
AT_CLEANUP AT_CLEANUP
AT_SETUP([macro arguments])
AT_KEYWORDS([macros])
AT_CHECK([
runroot rpm \
--define 'zzz xxx' \
--eval '%{defined zzz}' \
--eval '%{defined:zzz}' \
--eval '%defined zzz' \
--eval '%{defined aaa}' \
--eval '%{defined:aaa}' \
--eval '%defined aaa'
],
[0],
[1
1
1
0
0
0
])
AT_CLEANUP
AT_SETUP([expr macro 1]) AT_SETUP([expr macro 1])
AT_KEYWORDS([macros]) AT_KEYWORDS([macros])
AT_CHECK([ AT_CHECK([