#include "system.h"
#include <stdarg.h>
+#include <pthread.h>
+#include <errno.h>
#ifdef HAVE_GETOPT_H
#include <getopt.h>
#else
#include "debug.h"
+enum macroFlags_e {
+ ME_NONE = 0,
+ ME_AUTO = (1 << 0),
+ ME_USED = (1 << 1),
+};
+
/*! The structure used to store a macro. */
struct rpmMacroEntry_s {
struct rpmMacroEntry_s *prev;/*!< Macro entry stack. */
const char *name; /*!< Macro name. */
const char *opts; /*!< Macro parameters (a la getopt) */
const char *body; /*!< Macro body. */
- int used; /*!< No. of expansions. */
+ int flags; /*!< Macro state bits. */
int level; /*!< Scoping level. */
char arena[]; /*!< String arena. */
};
struct rpmMacroContext_s {
rpmMacroEntry *tab; /*!< Macro entry table (array of pointers). */
int n; /*!< No. of macros. */
+ int depth; /*!< Depth tracking when recursing from Lua */
+ int level; /*!< Scope level tracking when recursing from Lua */
+ pthread_mutex_t lock;
+ pthread_mutexattr_t lockattr;
};
static struct rpmMacroContext_s rpmCLIMacroContext_s;
rpmMacroContext rpmCLIMacroContext = &rpmCLIMacroContext_s;
+/*
+ * The macro engine internals do not require recursive mutexes but Lua
+ * macro bindings which can get called from the internals use the external
+ * interfaces which do perform locking. Until that is fixed somehow
+ * we'll just have to settle for recursive mutexes.
+ * Unfortunately POSIX doesn't specify static initializers for recursive
+ * mutexes so we need to have a separate PTHREAD_ONCE initializer just
+ * to initialize the otherwise static macro context mutexes. Pooh.
+ */
+static pthread_once_t locksInitialized = PTHREAD_ONCE_INIT;
+
+static void initLocks(void)
+{
+ rpmMacroContext mcs[] = { rpmGlobalMacroContext, rpmCLIMacroContext, NULL };
+
+ for (rpmMacroContext *mcp = mcs; *mcp; mcp++) {
+ rpmMacroContext mc = *mcp;
+ pthread_mutexattr_init(&mc->lockattr);
+ pthread_mutexattr_settype(&mc->lockattr, PTHREAD_MUTEX_RECURSIVE);
+ pthread_mutex_init(&mc->lock, &mc->lockattr);
+ }
+}
+
/**
* Macro expansion state.
*/
size_t tpos; /*!< Current position in expansion buffer */
size_t nb; /*!< No. bytes remaining in expansion buffer. */
int depth; /*!< Current expansion depth. */
+ int level; /*!< Current scoping level */
+ int error; /*!< Errors encountered during expansion? */
int macro_trace; /*!< Pre-print macro to expand? */
int expand_trace; /*!< Post-print macro expansion? */
+ int escape; /*!< Preserve '%%' during expansion? */
+ int flags; /*!< Flags to control behavior */
rpmMacroContext mc;
} * MacroBuf;
-#define _MAX_MACRO_DEPTH 16
+#define _MAX_MACRO_DEPTH 64
static int max_macro_depth = _MAX_MACRO_DEPTH;
#define _PRINT_MACRO_TRACE 0
/* forward ref */
static int expandMacro(MacroBuf mb, const char *src, size_t slen);
+static void pushMacro(rpmMacroContext mc,
+ const char * n, const char * o, const char * b, int level, int flags);
+static void popMacro(rpmMacroContext mc, const char * n);
+static int loadMacroFile(rpmMacroContext mc, const char * fn);
/* =============================================================== */
-void
-rpmDumpMacroTable(rpmMacroContext mc, FILE * fp)
+static rpmMacroContext rpmmctxAcquire(rpmMacroContext mc)
{
- if (mc == NULL) mc = rpmGlobalMacroContext;
- if (fp == NULL) fp = stderr;
-
- fprintf(fp, "========================\n");
- for (int i = 0; i < mc->n; i++) {
- rpmMacroEntry me = mc->tab[i];
- assert(me);
- fprintf(fp, "%3d%c %s", me->level,
- (me->used > 0 ? '=' : ':'), me->name);
- if (me->opts && *me->opts)
- fprintf(fp, "(%s)", me->opts);
- if (me->body && *me->body)
- fprintf(fp, "\t%s", me->body);
- fprintf(fp, "\n");
- }
- fprintf(fp, _("======================== active %d empty %d\n"),
- mc->n, 0);
+ if (mc == NULL)
+ mc = rpmGlobalMacroContext;
+ pthread_once(&locksInitialized, initLocks);
+ pthread_mutex_lock(&mc->lock);
+ return mc;
+}
+
+static rpmMacroContext rpmmctxRelease(rpmMacroContext mc)
+{
+ pthread_mutex_unlock(&mc->lock);
+ return NULL;
}
/**
* fgets(3) analogue that reads \ continuations. Last newline always trimmed.
* @param buf input buffer
* @param size inbut buffer size (bytes)
- * @param fd file handle
+ * @param f file handle
* @return buffer, or NULL on end-of-file
*/
static char *
printMacro(MacroBuf mb, const char * s, const char * se)
{
const char *senl;
- const char *ellipsis;
- int choplen;
if (s >= se) { /* XXX just in case */
- fprintf(stderr, _("%3d>%*s(empty)"), mb->depth,
+ fprintf(stderr, _("%3d>%*s(empty)\n"), mb->depth,
(2 * mb->depth + 1), "");
return;
}
for (senl = se; *senl && !iseol(*senl); senl++)
{};
- /* Limit trailing non-trace output */
- choplen = 61 - (2 * mb->depth);
- if ((senl - s) > choplen) {
- senl = s + choplen;
- ellipsis = "...";
- } else
- ellipsis = "";
-
/* Substitute caret at end-of-macro position */
fprintf(stderr, "%3d>%*s%%%.*s^", mb->depth,
(2 * mb->depth + 1), "", (int)(se - s), s);
- if (se[1] != '\0' && (senl - (se+1)) > 0)
- fprintf(stderr, "%-.*s%s", (int)(senl - (se+1)), se+1, ellipsis);
+ if (se[0] != '\0' && se[1] != '\0' && (senl - (se+1)) > 0)
+ fprintf(stderr, "%-.*s", (int)(senl - (se+1)), se+1);
fprintf(stderr, "\n");
}
static void
printExpansion(MacroBuf mb, const char * t, const char * te)
{
- const char *ellipsis;
- int choplen;
-
if (!(te > t)) {
rpmlog(RPMLOG_DEBUG, _("%3d<%*s(empty)\n"), mb->depth, (2 * mb->depth + 1), "");
return;
/* Shorten output which contains newlines */
while (te > t && iseol(te[-1]))
te--;
- ellipsis = "";
if (mb->depth > 0) {
const char *tenl;
while ((tenl = strchr(t, '\n')) && tenl < te)
t = ++tenl;
- /* Limit expand output */
- choplen = 61 - (2 * mb->depth);
- if ((te - t) > choplen) {
- te = t + choplen;
- ellipsis = "...";
- }
}
rpmlog(RPMLOG_DEBUG,"%3d<%*s", mb->depth, (2 * mb->depth + 1), "");
if (te > t)
- rpmlog(RPMLOG_DEBUG, "%.*s%s", (int)(te - t), t, ellipsis);
+ rpmlog(RPMLOG_DEBUG, "%.*s", (int)(te - t), t);
rpmlog(RPMLOG_DEBUG, "\n");
}
#define COPYNAME(_ne, _s, _c) \
{ SKIPBLANK(_s,_c); \
- while(((_c) = *(_s)) && (risalnum(_c) || (_c) == '_')) \
+ while (((_c) = *(_s)) && (risalnum(_c) || (_c) == '_')) \
*(_ne)++ = *(_s)++; \
*(_ne) = '\0'; \
}
#define COPYOPTS(_oe, _s, _c) \
{ \
- while(((_c) = *(_s)) && (_c) != ')') \
+ while (((_c) = *(_s)) && (_c) != ')') \
*(_oe)++ = *(_s)++; \
*(_oe) = '\0'; \
}
expandThis(MacroBuf mb, const char * src, size_t slen, char **target)
{
struct MacroBuf_s umb;
- int rc;
/* Copy other state from "parent", but we want a buffer of our own */
umb = *mb;
umb.buf = NULL;
- rc = expandMacro(&umb, src, slen);
+ umb.error = 0;
+ /* In case of error, flag it in the "parent"... */
+ if (expandMacro(&umb, src, slen))
+ mb->error = 1;
*target = umb.buf;
- return rc;
+ /* ...but return code for this operation specifically */
+ return umb.error;
}
static void mbAppend(MacroBuf mb, char c)
mb->nb--;
}
+#ifdef WITH_LUA
static void mbAppendStr(MacroBuf mb, const char *str)
{
size_t len = strlen(str);
mb->tpos += len;
mb->nb -= len;
}
+#endif
+
/**
* Expand output of shell command into target buffer.
* @param mb macro expansion state
* @param cmd shell command
* @param clen no. bytes in shell command
- * @return result of expansion
*/
-static int
+static void
doShellEscape(MacroBuf mb, const char * cmd, size_t clen)
{
char *buf = NULL;
FILE *shf;
- int rc = 0;
int c;
- rc = expandThis(mb, cmd, clen, &buf);
- if (rc)
+ if (expandThis(mb, cmd, clen, &buf))
goto exit;
if ((shf = popen(buf, "r")) == NULL) {
- rc = 1;
+ mb->error = 1;
goto exit;
}
size_t tpos = mb->tpos;
- while((c = fgetc(shf)) != EOF) {
+ while ((c = fgetc(shf)) != EOF) {
mbAppend(mb, c);
}
(void) pclose(shf);
exit:
_free(buf);
- return rc;
}
/**
* Parse (and execute) new macro definition.
* @param mb macro expansion state
* @param se macro definition to parse
+ * @param slen length of se argument
* @param level macro recursion level
* @param expandbody should body be expanded?
* @return address to continue parsing
*/
static const char *
-doDefine(MacroBuf mb, const char * se, int level, int expandbody)
+doDefine(MacroBuf mb, const char * se, size_t slen, int level, int expandbody)
{
const char *s = se;
- size_t blen = MACROBUFSIZ;
- char *buf = xmalloc(blen);
+ char *buf = xmalloc(slen + 3); /* Some leeway for termination issues... */
char *n = buf, *ne = n;
char *o = NULL, *oe;
char *b, *be, *ebody = NULL;
int c;
int oc = ')';
const char *sbody; /* as-is body start */
+ int rc = 1; /* assume failure */
/* Copy name */
COPYNAME(ne, s, c);
oe = ne + 1;
if (*s == '(') {
s++; /* skip ( */
- o = oe;
- COPYOPTS(oe, s, oc);
- s++; /* skip ) */
+ /* Options must be terminated with ')' */
+ if (strchr(s, ')')) {
+ o = oe;
+ COPYOPTS(oe, s, oc);
+ s++; /* skip ) */
+ } else {
+ rpmlog(RPMLOG_ERR, _("Macro %%%s has unterminated opts\n"), n);
+ goto exit;
+ }
}
/* Copy body, skipping over escaped newlines */
/* Names must start with alphabetic or _ and be at least 3 chars */
if (!((c = *n) && (risalpha(c) || c == '_') && (ne - n) > 2)) {
- rpmlog(RPMLOG_ERR,
- _("Macro %%%s has illegal name (%%define)\n"), n);
- goto exit;
- }
-
- /* Options must be terminated with ')' */
- if (o && oc != ')') {
- rpmlog(RPMLOG_ERR, _("Macro %%%s has unterminated opts\n"), n);
+ rpmlog(RPMLOG_ERR, _("Macro %%%s has illegal name (%s)\n"),
+ n, expandbody ? "%global": "%define");
goto exit;
}
b = ebody;
}
- addMacro(mb->mc, n, o, b, (level - 1));
+ pushMacro(mb->mc, n, o, b, level, ME_NONE);
+ rc = 0;
exit:
+ if (rc)
+ mb->error = 1;
_free(buf);
_free(ebody);
return se;
/**
* Parse (and execute) macro undefinition.
- * @param mc macro context
+ * @param mb macro expansion state
* @param se macro name to undefine
+ * @param slen length of se argument
* @return address to continue parsing
*/
static const char *
-doUndefine(rpmMacroContext mc, const char * se)
+doUndefine(MacroBuf mb, const char * se, size_t slen)
{
const char *s = se;
- char *buf = xmalloc(MACROBUFSIZ);
+ char *buf = xmalloc(slen + 1);
char *n = buf, *ne = n;
int c;
/* Names must start with alphabetic or _ and be at least 3 chars */
if (!((c = *n) && (risalpha(c) || c == '_') && (ne - n) > 2)) {
- rpmlog(RPMLOG_ERR,
- _("Macro %%%s has illegal name (%%undefine)\n"), n);
+ rpmlog(RPMLOG_ERR, _("Macro %%%s has illegal name (%%undefine)\n"), n);
+ mb->error = 1;
goto exit;
}
- delMacro(mc, n);
+ popMacro(mb->mc, n);
exit:
_free(buf);
/* Delete dynamic macro definitions */
for (int i = 0; i < mc->n; i++) {
- int skiptest = 0;
rpmMacroEntry me = mc->tab[i];
assert(me);
- if (me->level < mb->depth)
+ if (me->level < mb->level)
continue;
- if (strlen(me->name) == 1 && strchr("#*0", *me->name)) {
- if (*me->name == '*' && me->used > 0)
- skiptest = 1; /* XXX skip test for %# %* %0 */
- } else if (!skiptest && me->used <= 0) {
-#if NOTYET
- rpmlog(RPMLOG_ERR,
- _("Macro %%%s (%s) was not used below level %d\n"),
- me->name, me->body, me->level);
-#endif
+ /* Warn on defined but unused non-automatic, scoped macros */
+ if (!(me->flags & (ME_AUTO|ME_USED))) {
+ rpmlog(RPMLOG_WARNING,
+ _("Macro %%%s defined but not used within scope\n"),
+ me->name);
+ /* Only whine once */
+ me->flags |= ME_USED;
}
+
/* compensate if the slot is to go away */
if (me->prev == NULL)
i--;
- delMacro(mc, me->name);
+ popMacro(mc, me->name);
+ }
+ mb->level--;
+}
+
+static void splitQuoted(ARGV_t *av, const char * str, const char * seps)
+{
+ const int qchar = 0x1f; /* ASCII unit separator */
+ const char *s = str;
+ const char *start = str;
+ int quoted = 0;
+
+ while (start != NULL) {
+ if (!quoted && strchr(seps, *s)) {
+ size_t slen = s - start;
+ /* quoted arguments are always kept, otherwise skip empty args */
+ if (slen > 0) {
+ char *d, arg[slen + 1];
+ const char *t;
+ for (d = arg, t = start; t - start < slen; t++) {
+ if (*t == qchar)
+ continue;
+ *d++ = *t;
+ }
+ arg[d - arg] = '\0';
+ argvAdd(av, arg);
+ }
+ start = s + 1;
+ }
+ if (*s == qchar)
+ quoted = !quoted;
+ else if (*s == '\0')
+ start = NULL;
+ s++;
}
}
grabArgs(MacroBuf mb, const rpmMacroEntry me, const char * se,
const char * lastc)
{
+ const char *cont = NULL;
const char *opts;
char *args = NULL;
ARGV_t argv = NULL;
int argc = 0;
int c;
- /* Copy macro name as argv[0] */
- argvAdd(&argv, me->name);
- addMacro(mb->mc, "0", NULL, me->name, mb->depth);
-
/*
+ * Prepare list of call arguments, starting with macro name as argv[0].
* Make a copy of se up to lastc string that we can pass to argvSplit().
* Append the results to main argv.
*/
- { ARGV_t av = NULL;
- char *s = xcalloc((lastc-se)+1, sizeof(*s));
- memcpy(s, se, (lastc-se));
+ argvAdd(&argv, me->name);
+ if (lastc) {
+ int oescape = mb->escape;
+ char *s = NULL;
- argvSplit(&av, s, " \t");
- argvAppend(&argv, av);
+ /* Expand possible macros in arguments */
+ mb->escape = 1;
+ expandThis(mb, se, lastc-se, &s);
+ mb->escape = oescape;
- argvFree(av);
+ splitQuoted(&argv, s, " \t");
free(s);
+
+ cont = ((*lastc == '\0' || *lastc == '\n') && *(lastc-1) != '\\') ?
+ lastc : lastc + 1;
}
+ /* Bump call depth on entry before first macro define */
+ mb->level++;
+
+ /* Setup macro name as %0 */
+ pushMacro(mb->mc, "0", NULL, me->name, mb->level, ME_AUTO);
+
/*
* The macro %* analoguous to the shell's $* means "Pass all non-macro
* parameters." Consequently, there needs to be a macro that means "Pass all
* This is the (potential) justification for %{**} ...
*/
args = argvJoin(argv + 1, " ");
- addMacro(mb->mc, "**", NULL, args, mb->depth);
+ pushMacro(mb->mc, "**", NULL, args, mb->level, ME_AUTO);
free(args);
/*
argc = argvCount(argv);
/* Define option macros. */
- while((c = getopt(argc, argv, opts)) != -1)
+ while ((c = getopt(argc, argv, opts)) != -1)
{
char *name = NULL, *body = NULL;
if (c == '?' || strchr(opts, c) == NULL) {
rpmlog(RPMLOG_ERR, _("Unknown option %c in %s(%s)\n"),
(char)optopt, me->name, opts);
+ mb->error = 1;
goto exit;
}
} else {
rasprintf(&body, "-%c", c);
}
- addMacro(mb->mc, name, NULL, body, mb->depth);
+ pushMacro(mb->mc, name, NULL, body, mb->level, ME_AUTO);
free(name);
free(body);
if (optarg) {
rasprintf(&name, "-%c*", c);
- addMacro(mb->mc, name, NULL, optarg, mb->depth);
+ pushMacro(mb->mc, name, NULL, optarg, mb->level, ME_AUTO);
free(name);
}
}
/* Add argument count (remaining non-option items) as macro. */
{ char *ac = NULL;
rasprintf(&ac, "%d", (argc - optind));
- addMacro(mb->mc, "#", NULL, ac, mb->depth);
+ pushMacro(mb->mc, "#", NULL, ac, mb->level, ME_AUTO);
free(ac);
}
for (c = optind; c < argc; c++) {
char *name = NULL;
rasprintf(&name, "%d", (c - optind + 1));
- addMacro(mb->mc, name, NULL, argv[c], mb->depth);
+ pushMacro(mb->mc, name, NULL, argv[c], mb->level, ME_AUTO);
free(name);
}
}
/* Add concatenated unexpanded arguments as yet another macro. */
args = argvJoin(argv + optind, " ");
- addMacro(mb->mc, "*", NULL, args ? args : "", mb->depth);
+ pushMacro(mb->mc, "*", NULL, args ? args : "", mb->level, ME_AUTO);
free(args);
exit:
argvFree(argv);
- return *lastc ? lastc + 1 : lastc;
+ return cont;
}
/**
* Perform macro message output
* @param mb macro expansion state
* @param waserror use rpmlog()?
- * @param msg message to ouput
+ * @param msg message to output
* @param msglen no. of bytes in message
*/
static void
-doOutput(MacroBuf mb, int waserror, const char * msg, size_t msglen)
+doOutput(MacroBuf mb, int loglevel, const char * msg, size_t msglen)
{
char *buf = NULL;
(void) expandThis(mb, msg, msglen, &buf);
- if (waserror)
- rpmlog(RPMLOG_ERR, "%s\n", buf);
- else
- fprintf(stderr, "%s", buf);
+ rpmlog(loglevel, "%s\n", buf);
_free(buf);
}
+static void doLua(MacroBuf mb, const char * f, size_t fn, const char * g, size_t gn)
+{
+#ifdef WITH_LUA
+ rpmlua lua = NULL; /* Global state. */
+ char *scriptbuf = xmalloc(gn + 1);
+ char *printbuf;
+ rpmMacroContext mc = mb->mc;
+ int odepth = mc->depth;
+ int olevel = mc->level;
+
+ if (g != NULL && gn > 0)
+ memcpy(scriptbuf, g, gn);
+ scriptbuf[gn] = '\0';
+ rpmluaPushPrintBuffer(lua);
+ mc->depth = mb->depth;
+ mc->level = mb->level;
+ if (rpmluaRunScript(lua, scriptbuf, NULL) == -1)
+ mb->error = 1;
+ mc->depth = odepth;
+ mc->level = olevel;
+ printbuf = rpmluaPopPrintBuffer(lua);
+ if (printbuf) {
+ mbAppendStr(mb, printbuf);
+ free(printbuf);
+ }
+ free(scriptbuf);
+#else
+ rpmlog(RPMLOG_ERR, _("<lua> scriptlet support not built in\n"));
+ mb->error = 1;
+#endif
+}
+
/**
* Execute macro primitives.
* @param mb macro expansion state
char *buf = NULL;
char *b = NULL, *be;
int c;
+ int verbose = (rpmIsVerbose() != 0);
+ int expand = (g != NULL && gn > 0);
+
+ /* Don't expand %{verbose:...} argument on false condition */
+ if (STREQ("verbose", f, fn) && (verbose == negate))
+ expand = 0;
- if (g != NULL && gn > 0) {
+ if (expand) {
(void) expandThis(mb, g, gn, &buf);
} else {
buf = xmalloc(MACROBUFSIZ + fn + gn);
if ((b = strrchr(buf, '/')) != NULL)
*b = '\0';
b = buf;
+ } else if (STREQ("shrink", f, fn)) {
+ /*
+ * shrink body by removing all leading and trailing whitespaces and
+ * reducing intermediate whitespaces to a single space character.
+ */
+ size_t i = 0, j = 0;
+ size_t buflen = strlen(buf);
+ int was_space = 0;
+ while (i < buflen) {
+ if (risspace(buf[i])) {
+ was_space = 1;
+ i++;
+ continue;
+ } else if (was_space) {
+ was_space = 0;
+ if (j > 0) /* remove leading blanks at all */
+ buf[j++] = ' ';
+ }
+ buf[j++] = buf[i++];
+ }
+ buf[j] = '\0';
+ b = buf;
+ } else if (STREQ("quote", f, fn)) {
+ char *quoted = NULL;
+ rasprintf("ed, "%c%s%c", 0x1f, buf, 0x1f);
+ free(buf);
+ b = buf = quoted;
} else if (STREQ("suffix", f, fn)) {
if ((b = strrchr(buf, '.')) != NULL)
b++;
- } else if (STREQ("expand", f, fn)) {
+ } else if (STREQ("expand", f, fn) || STREQ("verbose", f, fn)) {
b = buf;
- } else if (STREQ("verbose", f, fn)) {
- if (negate)
- b = (rpmIsVerbose() ? NULL : buf);
- else
- b = (rpmIsVerbose() ? buf : NULL);
} else if (STREQ("url2path", f, fn) || STREQ("u2p", f, fn)) {
(void)urlPath(buf, (const char **)&b);
if (*b == '\0') b = "/";
be++;
*be++ = '\0';
(void) rpmFileIsCompressed(b, &compressed);
- switch(compressed) {
+ switch (compressed) {
default:
case COMPRESSED_NOT:
sprintf(be, "%%__cat %s", b);
case COMPRESSED_7ZIP:
sprintf(be, "%%__7zip x %s", b);
break;
+ case COMPRESSED_ZSTD:
+ sprintf(be, "%%__zstd -dc %s", b);
+ break;
}
b = be;
} else if (STREQ("getenv", f, fn)) {
* The main macro recursion loop.
* @param mb macro expansion state
* @param src string to expand
+ * @param slen length of string buffer
* @return 0 on success, 1 on failure
*/
static int
const char *g, *ge;
size_t fn, gn, tpos;
int c;
- int rc = 0;
int negate;
const char * lastc;
int chkexist;
char *source = NULL;
+ int store_macro_trace;
+ int store_expand_trace;
/*
* Always make a (terminated) copy of the source string.
mb->nb = blen;
}
tpos = mb->tpos; /* save expansion pointer for printExpand */
+ store_macro_trace = mb->macro_trace;
+ store_expand_trace = mb->expand_trace;
if (++mb->depth > max_macro_depth) {
rpmlog(RPMLOG_ERR,
_("Too many levels of recursion in macro expansion. It is likely caused by recursive macro declaration.\n"));
mb->depth--;
mb->expand_trace = 1;
- _free(source);
- return 1;
+ mb->error = 1;
+ goto exit;
}
- while (rc == 0 && (c = *s) != '\0') {
+ while (mb->error == 0 && (c = *s) != '\0') {
s++;
/* Copy text until next macro */
- switch(c) {
+ switch (c) {
case '%':
- if (*s) { /* Ensure not end-of-string. */
- if (*s != '%')
- break;
- s++; /* skip first % in %% */
- }
+ if (*s) { /* Ensure not end-of-string. */
+ if (*s != '%')
+ break;
+ s++; /* skip first % in %% */
+ if (mb->escape)
+ mbAppend(mb, c);
+ }
default:
- mbAppend(mb, c);
- continue;
- break;
+ mbAppend(mb, c);
+ continue;
+ break;
}
/* Expand next macro */
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++;
+ while (*s != '\0' && strchr("!?", *s) != NULL) {
+ switch (*s++) {
+ case '!':
+ negate = ((negate + 1) % 2);
break;
- default:
+ case '?':
+ chkexist++;
break;
}
- fe = se;
- /* For "%name " macros ... */
- if ((c = *fe) && isblank(c))
- if ((lastc = strchr(fe,'\n')) == NULL)
- lastc = strchr(fe, '\0');
+ }
+ 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);
+ if ((se = matchchar(s, c, ')')) == NULL) {
+ rpmlog(RPMLOG_ERR, _("Unterminated %c: %s\n"), (char)c, s);
+ mb->error = 1;
+ continue;
+ }
+ if (mb->macro_trace)
+ printMacro(mb, s, se+1);
- s++; /* skip ( */
- rc = doShellEscape(mb, s, (se - s));
- se++; /* skip ) */
+ s++; /* skip ( */
+ doShellEscape(mb, s, (se - s));
+ se++; /* skip ) */
- s = se;
- continue;
- break;
+ 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;
+ if ((se = matchchar(s, c, '}')) == NULL) {
+ rpmlog(RPMLOG_ERR, _("Unterminated %c: %s\n"), (char)c, s);
+ mb->error = 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 % */
- mbAppend(mb, c);
+ /* 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"));
+ rpmlog(RPMLOG_ERR,
+ _("A %% is followed by an unparseable macro\n"));
#endif
- s = se;
- continue;
+ s = se;
+ continue;
}
if (mb->macro_trace)
- printMacro(mb, s, se);
+ printMacro(mb, s, se);
/* Expand builtin macros */
+ if (STREQ("load", f, fn)) {
+ char *arg = NULL;
+ if (g && gn > 0 && expandThis(mb, g, gn, &arg) == 0) {
+ /* Print failure iff %{load:...} or %{!?load:...} */
+ if (loadMacroFile(mb->mc, arg) && chkexist == negate) {
+ rpmlog(RPMLOG_ERR, _("failed to load macro file %s"), arg);
+ mb->error = 1;
+ }
+ }
+ free(arg);
+ s = se;
+ continue;
+ }
if (STREQ("global", f, fn)) {
- s = doDefine(mb, se, RMIL_GLOBAL, 1);
- continue;
+ s = doDefine(mb, se, slen - (se - s), RMIL_GLOBAL, 1);
+ continue;
}
if (STREQ("define", f, fn)) {
- s = doDefine(mb, se, mb->depth, 0);
- continue;
+ s = doDefine(mb, se, slen - (se - s), mb->level, 0);
+ continue;
}
if (STREQ("undefine", f, fn)) {
- s = doUndefine(mb->mc, se);
- continue;
+ s = doUndefine(mb, se, slen - (se - s));
+ 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;
+ STREQ("warn", f, fn) ||
+ STREQ("error", f, fn)) {
+ int loglevel = RPMLOG_NOTICE; /* assume echo */
+ if (STREQ("error", f, fn)) {
+ loglevel = RPMLOG_ERR;
+ mb->error = 1;
+ } else if (STREQ("warn", f, fn)) {
+ loglevel = RPMLOG_WARNING;
+ }
+ if (g != NULL && g < ge)
+ doOutput(mb, loglevel, g, gn);
+ else
+ doOutput(mb, loglevel, "", 0);
+ 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;
+ /* 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;
+ rpmDumpMacroTable(mb->mc, NULL);
+ while (iseol(*se))
+ se++;
+ s = se;
+ continue;
}
-#ifdef WITH_LUA
if (STREQ("lua", f, fn)) {
- rpmlua lua = NULL; /* Global state. */
- char *scriptbuf = xmalloc(gn + 1);
- char *printbuf;
- if (g != NULL && gn > 0)
- memcpy(scriptbuf, g, gn);
- scriptbuf[gn] = '\0';
- rpmluaPushPrintBuffer(lua);
- if (rpmluaRunScript(lua, scriptbuf, NULL) == -1)
- rc = 1;
- printbuf = rpmluaPopPrintBuffer(lua);
- if (printbuf) {
- mbAppendStr(mb, printbuf);
- free(printbuf);
- }
- free(scriptbuf);
- s = se;
- continue;
+ doLua(mb, f, fn, g, gn);
+ s = se;
+ continue;
}
-#endif
/* XXX necessary but clunky */
if (STREQ("basename", f, fn) ||
STREQ("dirname", f, fn) ||
+ STREQ("shrink", f, fn) ||
STREQ("suffix", f, fn) ||
+ STREQ("quote", f, fn) ||
STREQ("expand", f, fn) ||
STREQ("verbose", f, fn) ||
STREQ("uncompress", f, fn) ||
STREQ("getconfdir", 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;
+ 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, NULL);
me = (mep ? *mep : NULL);
+ if (me) {
+ if ((me->flags & ME_AUTO) && mb->level > me->level) {
+ /* Ignore out-of-scope automatic macros */
+ me = NULL;
+ } else {
+ /* If we looked up a macro, consider it used */
+ me->flags |= ME_USED;
+ }
+ }
+
/* XXX Special processing for flags */
if (*f == '-') {
- if (me)
- me->used++; /* Mark macro as used */
- if ((me == NULL && !negate) || /* Without -f, skip %{-f...} */
+ if ((me == NULL && !negate) || /* Without -f, skip %{-f...} */
(me != NULL && negate)) { /* With -f, skip %{!-f...} */
- s = se;
- continue;
- }
+ s = se;
+ continue;
+ }
- if (g && g < ge) { /* Expand X in %{-f:X} */
- rc = expandMacro(mb, g, gn);
- } else
+ if (g && g < ge) { /* Expand X in %{-f:X} */
+ expandMacro(mb, g, gn);
+ } else
if (me && me->body && *me->body) {/* Expand %{-f}/%{-f*} */
- rc = expandMacro(mb, me->body, 0);
+ expandMacro(mb, me->body, 0);
}
- s = se;
- continue;
+ s = se;
+ continue;
}
/* XXX Special processing for macro existence */
if (chkexist) {
- if ((me == NULL && !negate) || /* Without -f, skip %{?f...} */
+ 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 = expandMacro(mb, g, gn);
- } else
- if (me && me->body && *me->body) { /* Expand %{?f}/%{?f*} */
- rc = expandMacro(mb, me->body, 0);
- }
s = se;
continue;
+ }
+ if (g && g < ge) { /* Expand X in %{?f:X} */
+ expandMacro(mb, g, gn);
+ } else
+ if (me && me->body && *me->body) { /* Expand %{?f}/%{?f*} */
+ expandMacro(mb, me->body, 0);
+ }
+ s = se;
+ continue;
}
if (me == NULL) { /* leave unknown %... as is */
- /* XXX hack to permit non-overloaded %foo to be passed */
- c = '%'; /* XXX only need to save % */
- mbAppend(mb, c);
- continue;
+ /* XXX hack to permit non-overloaded %foo to be passed */
+ c = '%'; /* XXX only need to save % */
+ mbAppend(mb, c);
+ 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);
- }
+ const char *xe = grabArgs(mb, me, fe, lastc);
+ if (xe != NULL)
+ se = xe;
}
/* Recursively expand body of macro */
if (me->body && *me->body) {
- rc = expandMacro(mb, me->body, 0);
- if (rc == 0)
- me->used++; /* Mark macro as used */
+ expandMacro(mb, me->body, 0);
}
/* Free args for "%name " macros with opts */
if (me->opts != NULL)
- freeArgs(mb);
+ freeArgs(mb);
s = se;
}
mb->buf[mb->tpos] = '\0';
mb->depth--;
- if (rc != 0 || mb->expand_trace)
+ if (mb->error != 0 || mb->expand_trace)
printExpansion(mb, mb->buf+tpos, mb->buf+mb->tpos);
+ mb->macro_trace = store_macro_trace;
+ mb->expand_trace = store_expand_trace;
+exit:
_free(source);
- return rc;
+ return mb->error;
}
/* =============================================================== */
-static int doExpandMacros(rpmMacroContext mc, const char *src, char **target)
+static int doExpandMacros(rpmMacroContext mc, const char *src, int flags,
+ char **target)
{
MacroBuf mb = xcalloc(1, sizeof(*mb));
int rc = 0;
- if (mc == NULL) mc = rpmGlobalMacroContext;
-
mb->buf = NULL;
- mb->depth = 0;
+ mb->depth = mc->depth;
+ mb->level = mc->level;
mb->macro_trace = print_macro_trace;
mb->expand_trace = print_expand_trace;
mb->mc = mc;
+ mb->flags = flags;
rc = expandMacro(mb, src, 0);
return rc;
}
-int expandMacros(void * spec, rpmMacroContext mc, char * sbuf, size_t slen)
-{
- char *target = NULL;
- int rc = doExpandMacros(mc, sbuf, &target);
- rstrlcpy(sbuf, target, slen);
- free(target);
- return rc;
-}
-
-void
-addMacro(rpmMacroContext mc,
- const char * n, const char * o, const char * b, int level)
+static void pushMacro(rpmMacroContext mc,
+ const char * n, const char * o, const char * b, int level, int flags)
{
- if (mc == NULL)
- mc = rpmGlobalMacroContext;
-
/* new entry */
rpmMacroEntry me;
/* pointer into me */
else
me->opts = o ? "" : NULL;
/* initialize */
- me->used = 0;
+ me->flags = flags;
+ me->flags &= ~(ME_USED);
me->level = level;
/* push over previous definition */
me->prev = *mep;
*mep = me;
}
-void
-delMacro(rpmMacroContext mc, const char * n)
+static void popMacro(rpmMacroContext mc, const char * n)
{
- if (mc == NULL)
- mc = rpmGlobalMacroContext;
-
size_t pos;
rpmMacroEntry *mep = findEntry(mc, n, 0, &pos);
if (mep == NULL)
free(me);
}
-int
-rpmDefineMacro(rpmMacroContext mc, const char * macro, int level)
+static int defineMacro(rpmMacroContext mc, const char * macro, int level)
{
MacroBuf mb = xcalloc(1, sizeof(*mb));
+ int rc;
/* XXX just enough to get by */
- mb->mc = (mc ? mc : rpmGlobalMacroContext);
- (void) doDefine(mb, macro, level, 0);
+ mb->mc = mc;
+ (void) doDefine(mb, macro, strlen(macro), level, 0);
+ rc = mb->error;
_free(mb);
- return 0;
-}
-
-void
-rpmLoadMacros(rpmMacroContext mc, int level)
-{
-
- if (mc == NULL || mc == rpmGlobalMacroContext)
- return;
-
- for (int i = 0; i < mc->n; i++) {
- rpmMacroEntry me = mc->tab[i];
- assert(me);
- addMacro(NULL, me->name, me->opts, me->body, (level - 1));
- }
+ return rc;
}
-int
-rpmLoadMacroFile(rpmMacroContext mc, const char * fn)
+static int loadMacroFile(rpmMacroContext mc, const char * fn)
{
FILE *fd = fopen(fn, "r");
size_t blen = MACROBUFSIZ;
if (fd == NULL)
goto exit;
- /* XXX Assume new fangled macro expansion */
- max_macro_depth = 16;
-
buf[0] = '\0';
- while(rdcl(buf, blen, fd) != NULL) {
+ while (rdcl(buf, blen, fd) != NULL) {
char c, *n;
n = buf;
if (c != '%')
continue;
n++; /* skip % */
- rc = rpmDefineMacro(mc, n, RMIL_MACROFILES);
+ rc = defineMacro(mc, n, RMIL_MACROFILES);
}
+
rc = fclose(fd);
exit:
return rc;
}
+static void copyMacros(rpmMacroContext src, rpmMacroContext dst, int level)
+{
+ for (int i = 0; i < src->n; i++) {
+ rpmMacroEntry me = src->tab[i];
+ assert(me);
+ pushMacro(dst, me->name, me->opts, me->body, level, me->flags);
+ }
+}
+
+/* External interfaces */
+
+int rpmExpandMacros(rpmMacroContext mc, const char * sbuf, char ** obuf, int flags)
+{
+ char *target = NULL;
+ int rc;
+
+ mc = rpmmctxAcquire(mc);
+ rc = doExpandMacros(mc, sbuf, flags, &target);
+ rpmmctxRelease(mc);
+
+ if (rc) {
+ free(target);
+ return -1;
+ } else {
+ *obuf = target;
+ return 1;
+ }
+}
+
+void
+rpmDumpMacroTable(rpmMacroContext mc, FILE * fp)
+{
+ mc = rpmmctxAcquire(mc);
+ if (fp == NULL) fp = stderr;
+
+ fprintf(fp, "========================\n");
+ for (int i = 0; i < mc->n; i++) {
+ rpmMacroEntry me = mc->tab[i];
+ assert(me);
+ fprintf(fp, "%3d%c %s", me->level,
+ ((me->flags & ME_USED) ? '=' : ':'), me->name);
+ if (me->opts && *me->opts)
+ fprintf(fp, "(%s)", me->opts);
+ if (me->body && *me->body)
+ fprintf(fp, "\t%s", me->body);
+ fprintf(fp, "\n");
+ }
+ fprintf(fp, _("======================== active %d empty %d\n"),
+ mc->n, 0);
+ rpmmctxRelease(mc);
+}
+
+int rpmPushMacro(rpmMacroContext mc,
+ const char * n, const char * o, const char * b, int level)
+{
+ mc = rpmmctxAcquire(mc);
+ pushMacro(mc, n, o, b, level, ME_NONE);
+ rpmmctxRelease(mc);
+ return 0;
+}
+
+int rpmPopMacro(rpmMacroContext mc, const char * n)
+{
+ mc = rpmmctxAcquire(mc);
+ popMacro(mc, n);
+ rpmmctxRelease(mc);
+ return 0;
+}
+
+int
+rpmDefineMacro(rpmMacroContext mc, const char * macro, int level)
+{
+ int rc;
+ mc = rpmmctxAcquire(mc);
+ rc = defineMacro(mc, macro, level);
+ rpmmctxRelease(mc);
+ return rc;
+}
+
+void
+rpmLoadMacros(rpmMacroContext mc, int level)
+{
+ rpmMacroContext gmc;
+ if (mc == NULL || mc == rpmGlobalMacroContext)
+ return;
+
+ gmc = rpmmctxAcquire(NULL);
+ mc = rpmmctxAcquire(mc);
+
+ copyMacros(mc, gmc, level);
+
+ rpmmctxRelease(mc);
+ rpmmctxRelease(gmc);
+}
+
+int
+rpmLoadMacroFile(rpmMacroContext mc, const char * fn)
+{
+ int rc;
+
+ mc = rpmmctxAcquire(mc);
+ rc = loadMacroFile(mc, fn);
+ rpmmctxRelease(mc);
+
+ return rc;
+}
+
void
rpmInitMacros(rpmMacroContext mc, const char * macrofiles)
{
ARGV_t pattern, globs = NULL;
+ rpmMacroContext climc;
if (macrofiles == NULL)
return;
argvSplit(&globs, macrofiles, ":");
+ mc = rpmmctxAcquire(mc);
for (pattern = globs; *pattern; pattern++) {
ARGV_t path, files = NULL;
rpmFileHasSuffix(*path, ".rpmorig")) {
continue;
}
- (void) rpmLoadMacroFile(mc, *path);
+ (void) loadMacroFile(mc, *path);
}
argvFree(files);
}
argvFree(globs);
/* Reload cmdline macros */
- rpmLoadMacros(rpmCLIMacroContext, RMIL_CMDLINE);
+ climc = rpmmctxAcquire(rpmCLIMacroContext);
+ copyMacros(climc, mc, RMIL_CMDLINE);
+ rpmmctxRelease(climc);
+
+ rpmmctxRelease(mc);
}
void
rpmFreeMacros(rpmMacroContext mc)
{
- if (mc == NULL)
- mc = rpmGlobalMacroContext;
+ mc = rpmmctxAcquire(mc);
while (mc->n > 0) {
/* remove from the end to avoid memmove */
rpmMacroEntry me = mc->tab[mc->n - 1];
- delMacro(mc, me->name);
+ popMacro(mc, me->name);
}
+ rpmmctxRelease(mc);
}
char *
char *pe;
const char *s;
va_list ap;
+ rpmMacroContext mc;
if (arg == NULL) {
ret = xstrdup("");
pe = stpcpy(pe, s);
va_end(ap);
- (void) doExpandMacros(NULL, buf, &ret);
+ mc = rpmmctxAcquire(NULL);
+ (void) doExpandMacros(mc, buf, 0, &ret);
+ rpmmctxRelease(mc);
free(buf);
exit: