1 /** \ingroup rpmrc rpmio
17 #define isblank(_c) ((_c) == ' ' || (_c) == '\t')
19 #define iseol(_c) ((_c) == '\n' || (_c) == '\r')
21 #define STREQ(_t, _f, _fn) ((_fn) == (sizeof(_t)-1) && rstreqn((_t), (_f), (_fn)))
23 #define MACROBUFSIZ (BUFSIZ * 2)
25 #include <rpm/rpmio.h>
26 #include <rpm/rpmstring.h>
27 #include <rpm/rpmfileutil.h>
28 #include <rpm/rpmurl.h>
29 #include <rpm/rpmlog.h>
30 #include <rpm/rpmmacro.h>
34 #include "rpmio/rpmlua.h"
45 /*! The structure used to store a macro. */
46 struct rpmMacroEntry_s {
47 struct rpmMacroEntry_s *prev;/*!< Macro entry stack. */
48 const char *name; /*!< Macro name. */
49 const char *opts; /*!< Macro parameters (a la getopt) */
50 const char *body; /*!< Macro body. */
51 int flags; /*!< Macro state bits. */
52 int level; /*!< Scoping level. */
53 char arena[]; /*!< String arena. */
56 /*! The structure used to store the set of macros in a context. */
57 struct rpmMacroContext_s {
58 rpmMacroEntry *tab; /*!< Macro entry table (array of pointers). */
59 int n; /*!< No. of macros. */
60 int depth; /*!< Depth tracking when recursing from Lua */
61 int level; /*!< Scope level tracking when recursing from Lua */
63 pthread_mutexattr_t lockattr;
67 static struct rpmMacroContext_s rpmGlobalMacroContext_s;
68 rpmMacroContext rpmGlobalMacroContext = &rpmGlobalMacroContext_s;
70 static struct rpmMacroContext_s rpmCLIMacroContext_s;
71 rpmMacroContext rpmCLIMacroContext = &rpmCLIMacroContext_s;
74 * The macro engine internals do not require recursive mutexes but Lua
75 * macro bindings which can get called from the internals use the external
76 * interfaces which do perform locking. Until that is fixed somehow
77 * we'll just have to settle for recursive mutexes.
78 * Unfortunately POSIX doesn't specify static initializers for recursive
79 * mutexes so we need to have a separate PTHREAD_ONCE initializer just
80 * to initialize the otherwise static macro context mutexes. Pooh.
82 static pthread_once_t locksInitialized = PTHREAD_ONCE_INIT;
84 static void initLocks(void)
86 rpmMacroContext mcs[] = { rpmGlobalMacroContext, rpmCLIMacroContext, NULL };
88 for (rpmMacroContext *mcp = mcs; *mcp; mcp++) {
89 rpmMacroContext mc = *mcp;
90 pthread_mutexattr_init(&mc->lockattr);
91 pthread_mutexattr_settype(&mc->lockattr, PTHREAD_MUTEX_RECURSIVE);
92 pthread_mutex_init(&mc->lock, &mc->lockattr);
97 * Macro expansion state.
99 typedef struct MacroBuf_s {
100 char * buf; /*!< Expansion buffer. */
101 size_t tpos; /*!< Current position in expansion buffer */
102 size_t nb; /*!< No. bytes remaining in expansion buffer. */
103 int depth; /*!< Current expansion depth. */
104 int level; /*!< Current scoping level */
105 int error; /*!< Errors encountered during expansion? */
106 int macro_trace; /*!< Pre-print macro to expand? */
107 int expand_trace; /*!< Post-print macro expansion? */
108 int escape; /*!< Preserve '%%' during expansion? */
109 int flags; /*!< Flags to control behavior */
113 #define _MAX_MACRO_DEPTH 64
114 static int max_macro_depth = _MAX_MACRO_DEPTH;
116 #define _PRINT_MACRO_TRACE 0
117 static int print_macro_trace = _PRINT_MACRO_TRACE;
119 #define _PRINT_EXPAND_TRACE 0
120 static int print_expand_trace = _PRINT_EXPAND_TRACE;
123 static int expandMacro(MacroBuf mb, const char *src, size_t slen);
124 static void pushMacro(rpmMacroContext mc,
125 const char * n, const char * o, const char * b, int level, int flags);
126 static void popMacro(rpmMacroContext mc, const char * n);
127 static int loadMacroFile(rpmMacroContext mc, const char * fn);
129 /* =============================================================== */
131 static rpmMacroContext rpmmctxAcquire(rpmMacroContext mc)
134 mc = rpmGlobalMacroContext;
135 pthread_once(&locksInitialized, initLocks);
136 pthread_mutex_lock(&mc->lock);
140 static rpmMacroContext rpmmctxRelease(rpmMacroContext mc)
142 pthread_mutex_unlock(&mc->lock);
147 * Find entry in macro table.
148 * @param mc macro context
149 * @param name macro name
150 * @param namelen no. of bytes
151 * @param pos found/insert position
152 * @return address of slot in macro table with name (or NULL)
154 static rpmMacroEntry *
155 findEntry(rpmMacroContext mc, const char *name, size_t namelen, size_t *pos)
164 rpmMacroEntry me = mc->tab[i];
166 cmp = strcmp(me->name, name);
168 cmp = strncmp(me->name, name, namelen);
169 /* longer me->name compares greater */
171 cmp = (me->name[namelen] != '\0');
182 *pos = (cmp < 0) ? i + 1 : i;
188 /* =============================================================== */
191 * fgets(3) analogue that reads \ continuations. Last newline always trimmed.
192 * @param buf input buffer
193 * @param size inbut buffer size (bytes)
194 * @param f file handle
195 * @return buffer, or NULL on end-of-file
198 rdcl(char * buf, size_t size, FILE *f)
200 char *q = buf - 1; /* initialize just before buffer. */
208 *(++q) = '\0'; /* terminate and move forward. */
209 if (fgets(q, size, f) == NULL) /* read next line. */
212 nread += nb; /* trim trailing \r and \n */
213 for (q += nb - 1; nb > 0 && iseol(*q); q--)
215 for (; p <= q; p++) {
225 case '{': p++, bc++; break;
226 case '(': p++, pc++; break;
227 case '%': p++; break;
230 case '{': if (bc > 0) bc++; break;
231 case '}': if (bc > 0) bc--; break;
232 case '(': if (pc > 0) pc++; break;
233 case ')': if (pc > 0) pc--; break;
236 if (nb == 0 || (*q != '\\' && !bc && !pc) || *(q+1) == '\0') {
237 *(++q) = '\0'; /* trim trailing \r, \n */
240 q++; p++; nb++; /* copy newline too */
242 if (*q == '\r') /* XXX avoid \r madness */
245 return (nread > 0 ? buf : NULL);
249 * Return text between pl and matching pr characters.
250 * @param p start of text
251 * @param pl left char, i.e. '[', '(', '{', etc.
252 * @param pr right char, i.e. ']', ')', '}', etc.
253 * @return address of last char before pr (or NULL)
256 matchchar(const char * p, char pl, char pr)
261 while ((c = *p++) != '\0') {
262 if (c == '\\') { /* Ignore escaped chars */
267 if (--lvl <= 0) return --p;
271 return (const char *)NULL;
275 * Pre-print macro expression to be expanded.
276 * @param mb macro expansion state
277 * @param s current expansion string
278 * @param se end of string
281 printMacro(MacroBuf mb, const char * s, const char * se)
285 if (s >= se) { /* XXX just in case */
286 fprintf(stderr, _("%3d>%*s(empty)\n"), mb->depth,
287 (2 * mb->depth + 1), "");
294 /* Print only to first end-of-line (or end-of-string). */
295 for (senl = se; *senl && !iseol(*senl); senl++)
298 /* Substitute caret at end-of-macro position */
299 fprintf(stderr, "%3d>%*s%%%.*s^", mb->depth,
300 (2 * mb->depth + 1), "", (int)(se - s), s);
301 if (se[0] != '\0' && se[1] != '\0' && (senl - (se+1)) > 0)
302 fprintf(stderr, "%-.*s", (int)(senl - (se+1)), se+1);
303 fprintf(stderr, "\n");
307 * Post-print expanded macro expression.
308 * @param mb macro expansion state
309 * @param t current expansion string result
310 * @param te end of string
313 printExpansion(MacroBuf mb, const char * t, const char * te)
316 rpmlog(RPMLOG_DEBUG, _("%3d<%*s(empty)\n"), mb->depth, (2 * mb->depth + 1), "");
320 /* Shorten output which contains newlines */
321 while (te > t && iseol(te[-1]))
326 /* Skip to last line of expansion */
327 while ((tenl = strchr(t, '\n')) && tenl < te)
332 rpmlog(RPMLOG_DEBUG,"%3d<%*s", mb->depth, (2 * mb->depth + 1), "");
334 rpmlog(RPMLOG_DEBUG, "%.*s", (int)(te - t), t);
335 rpmlog(RPMLOG_DEBUG, "\n");
338 #define SKIPBLANK(_s, _c) \
339 while (((_c) = *(_s)) && isblank(_c)) \
342 #define SKIPNONBLANK(_s, _c) \
343 while (((_c) = *(_s)) && !(isblank(_c) || iseol(_c))) \
346 #define COPYNAME(_ne, _s, _c) \
347 { SKIPBLANK(_s,_c); \
348 while (((_c) = *(_s)) && (risalnum(_c) || (_c) == '_')) \
349 *(_ne)++ = *(_s)++; \
353 #define COPYOPTS(_oe, _s, _c) \
355 while (((_c) = *(_s)) && (_c) != ')') \
356 *(_oe)++ = *(_s)++; \
361 * Macro-expand string src, return result in dynamically allocated buffer.
362 * @param mb macro expansion state
363 * @param src string to expand
364 * @param slen input string length (or 0 for strlen())
365 * @retval target pointer to expanded string (malloced)
366 * @return result of expansion
369 expandThis(MacroBuf mb, const char * src, size_t slen, char **target)
371 struct MacroBuf_s umb;
373 /* Copy other state from "parent", but we want a buffer of our own */
377 /* In case of error, flag it in the "parent"... */
378 if (expandMacro(&umb, src, slen))
382 /* ...but return code for this operation specifically */
386 static void mbAppend(MacroBuf mb, char c)
389 mb->buf = xrealloc(mb->buf, mb->tpos + MACROBUFSIZ + 1);
390 mb->nb += MACROBUFSIZ;
392 mb->buf[mb->tpos++] = c;
393 mb->buf[mb->tpos] = '\0';
398 static void mbAppendStr(MacroBuf mb, const char *str)
400 size_t len = strlen(str);
402 mb->buf = xrealloc(mb->buf, mb->tpos + mb->nb + MACROBUFSIZ + len + 1);
403 mb->nb += MACROBUFSIZ + len;
405 memcpy(mb->buf+mb->tpos, str, len + 1);
412 * Expand output of shell command into target buffer.
413 * @param mb macro expansion state
414 * @param cmd shell command
415 * @param clen no. bytes in shell command
418 doShellEscape(MacroBuf mb, const char * cmd, size_t clen)
425 rpmlog(RPMLOG_WARNING, _("Refusing to run shell code: %s\n"), cmd);
426 mbAppendStr(mb, "UNEXPANDEDSHELLSCRIPT");
428 if (expandThis(mb, cmd, clen, &buf))
431 if ((shf = popen(buf, "r")) == NULL) {
436 size_t tpos = mb->tpos;
437 while ((c = fgetc(shf)) != EOF) {
442 /* Delete trailing \r \n */
443 while (mb->tpos > tpos && iseol(mb->buf[mb->tpos-1])) {
444 mb->buf[--mb->tpos] = '\0';
454 * Parse (and execute) new macro definition.
455 * @param mb macro expansion state
456 * @param se macro definition to parse
457 * @param slen length of se argument
458 * @param level macro recursion level
459 * @param expandbody should body be expanded?
460 * @return address to continue parsing
463 doDefine(MacroBuf mb, const char * se, size_t slen, int level, int expandbody)
466 char *buf = xmalloc(slen + 3); /* Some leeway for termination issues... */
467 char *n = buf, *ne = n;
469 char *b, *be, *ebody = NULL;
472 const char *sbody; /* as-is body start */
473 int rc = 1; /* assume failure */
478 /* Copy opts (if present) */
482 /* Options must be terminated with ')' */
483 if (strchr(s, ')')) {
488 rpmlog(RPMLOG_ERR, _("Macro %%%s has unterminated opts\n"), n);
493 /* Copy body, skipping over escaped newlines */
497 if (c == '{') { /* XXX permit silent {...} grouping */
498 if ((se = matchchar(s, c, '}')) == NULL) {
500 _("Macro %%%s has unterminated body\n"), n);
501 se = s; /* XXX W2DO? */
504 s++; /* XXX skip { */
505 strncpy(b, s, (se - s));
508 se++; /* XXX skip } */
509 s = se; /* move scan forward */
510 } else { /* otherwise free-field */
512 while (*s && (bc || pc || !iseol(*s))) {
522 case '{': *be++ = *s++; bc++; break;
523 case '(': *be++ = *s++; pc++; break;
524 case '%': *be++ = *s++; break;
527 case '{': if (bc > 0) bc++; break;
528 case '}': if (bc > 0) bc--; break;
529 case '(': if (pc > 0) pc++; break;
530 case ')': if (pc > 0) pc--; break;
538 _("Macro %%%s has unterminated body\n"), n);
539 se = s; /* XXX W2DO? */
543 /* Trim trailing blanks/newlines */
544 while (--be >= b && (c = *be) && (isblank(c) || iseol(c)))
546 *(++be) = '\0'; /* one too far */
549 /* Move scan over body */
554 /* Names must start with alphabetic or _ and be at least 3 chars */
555 if (!((c = *n) && (risalpha(c) || c == '_') && (ne - n) > 2)) {
556 rpmlog(RPMLOG_ERR, _("Macro %%%s has illegal name (%s)\n"),
557 n, expandbody ? "%global": "%define");
562 rpmlog(RPMLOG_ERR, _("Macro %%%s has empty body\n"), n);
566 if (!isblank(*sbody) && !(*sbody == '\\' && iseol(sbody[1])))
567 rpmlog(RPMLOG_WARNING, _("Macro %%%s needs whitespace before body\n"), n);
570 if (expandThis(mb, b, 0, &ebody)) {
571 rpmlog(RPMLOG_ERR, _("Macro %%%s failed to expand\n"), n);
577 pushMacro(mb->mc, n, o, b, level, ME_NONE);
589 * Parse (and execute) macro undefinition.
590 * @param mb macro expansion state
591 * @param se macro name to undefine
592 * @param slen length of se argument
593 * @return address to continue parsing
596 doUndefine(MacroBuf mb, const char * se, size_t slen)
599 char *buf = xmalloc(slen + 1);
600 char *n = buf, *ne = n;
605 /* Move scan over body */
610 /* Names must start with alphabetic or _ and be at least 3 chars */
611 if (!((c = *n) && (risalpha(c) || c == '_') && (ne - n) > 2)) {
612 rpmlog(RPMLOG_ERR, _("Macro %%%s has illegal name (%%undefine)\n"), n);
625 * Free parsed arguments for parameterized macro.
626 * @param mb macro expansion state
629 freeArgs(MacroBuf mb)
631 rpmMacroContext mc = mb->mc;
633 /* Delete dynamic macro definitions */
634 for (int i = 0; i < mc->n; i++) {
635 rpmMacroEntry me = mc->tab[i];
637 if (me->level < mb->level)
639 /* Warn on defined but unused non-automatic, scoped macros */
640 if (!(me->flags & (ME_AUTO|ME_USED))) {
641 rpmlog(RPMLOG_WARNING,
642 _("Macro %%%s defined but not used within scope\n"),
644 /* Only whine once */
645 me->flags |= ME_USED;
648 /* compensate if the slot is to go away */
649 if (me->prev == NULL)
651 popMacro(mc, me->name);
656 static void splitQuoted(ARGV_t *av, const char * str, const char * seps)
658 const int qchar = 0x1f; /* ASCII unit separator */
660 const char *start = str;
663 while (start != NULL) {
664 if (!quoted && strchr(seps, *s)) {
665 size_t slen = s - start;
666 /* quoted arguments are always kept, otherwise skip empty args */
668 char *d, arg[slen + 1];
670 for (d = arg, t = start; t - start < slen; t++) {
689 * Parse arguments (to next new line) for parameterized macro.
690 * @todo Use popt rather than getopt to parse args.
691 * @param mb macro expansion state
692 * @param me macro entry slot
693 * @param se arguments to parse
694 * @param lastc stop parsing at lastc
695 * @return address to continue parsing
698 grabArgs(MacroBuf mb, const rpmMacroEntry me, const char * se,
701 const char *cont = NULL;
709 * Prepare list of call arguments, starting with macro name as argv[0].
710 * Make a copy of se up to lastc string that we can pass to argvSplit().
711 * Append the results to main argv.
713 argvAdd(&argv, me->name);
715 int oescape = mb->escape;
718 /* Expand possible macros in arguments */
720 expandThis(mb, se, lastc-se, &s);
721 mb->escape = oescape;
723 splitQuoted(&argv, s, " \t");
726 cont = ((*lastc == '\0' || *lastc == '\n') && *(lastc-1) != '\\') ?
730 /* Bump call depth on entry before first macro define */
733 /* Setup macro name as %0 */
734 pushMacro(mb->mc, "0", NULL, me->name, mb->level, ME_AUTO);
737 * The macro %* analoguous to the shell's $* means "Pass all non-macro
738 * parameters." Consequently, there needs to be a macro that means "Pass all
739 * (including macro parameters) options". This is useful for verifying
740 * parameters during expansion and yet transparently passing all parameters
741 * through for higher level processing (e.g. %description and/or %setup).
742 * This is the (potential) justification for %{**} ...
744 args = argvJoin(argv + 1, " ");
745 pushMacro(mb->mc, "**", NULL, args, mb->level, ME_AUTO);
749 * POSIX states optind must be 1 before any call but glibc uses 0
750 * to (re)initialize getopt structures, eww.
759 argc = argvCount(argv);
761 /* Define option macros. */
762 while ((c = getopt(argc, argv, opts)) != -1)
764 char *name = NULL, *body = NULL;
765 if (c == '?' || strchr(opts, c) == NULL) {
766 rpmlog(RPMLOG_ERR, _("Unknown option %c in %s(%s)\n"),
767 (char)optopt, me->name, opts);
772 rasprintf(&name, "-%c", c);
774 rasprintf(&body, "-%c %s", c, optarg);
776 rasprintf(&body, "-%c", c);
778 pushMacro(mb->mc, name, NULL, body, mb->level, ME_AUTO);
783 rasprintf(&name, "-%c*", c);
784 pushMacro(mb->mc, name, NULL, optarg, mb->level, ME_AUTO);
789 /* Add argument count (remaining non-option items) as macro. */
791 rasprintf(&ac, "%d", (argc - optind));
792 pushMacro(mb->mc, "#", NULL, ac, mb->level, ME_AUTO);
796 /* Add macro for each argument */
798 for (c = optind; c < argc; c++) {
800 rasprintf(&name, "%d", (c - optind + 1));
801 pushMacro(mb->mc, name, NULL, argv[c], mb->level, ME_AUTO);
806 /* Add concatenated unexpanded arguments as yet another macro. */
807 args = argvJoin(argv + optind, " ");
808 pushMacro(mb->mc, "*", NULL, args ? args : "", mb->level, ME_AUTO);
817 * Perform macro message output
818 * @param mb macro expansion state
819 * @param waserror use rpmlog()?
820 * @param msg message to output
821 * @param msglen no. of bytes in message
824 doOutput(MacroBuf mb, int loglevel, const char * msg, size_t msglen)
828 (void) expandThis(mb, msg, msglen, &buf);
829 rpmlog(loglevel, "%s\n", buf);
833 static void doLua(MacroBuf mb, const char * f, size_t fn, const char * g, size_t gn)
836 rpmlua lua = NULL; /* Global state. */
837 char *scriptbuf = xmalloc(gn + 1);
839 rpmMacroContext mc = mb->mc;
840 int odepth = mc->depth;
841 int olevel = mc->level;
843 if (g != NULL && gn > 0)
844 memcpy(scriptbuf, g, gn);
845 scriptbuf[gn] = '\0';
846 rpmluaPushPrintBuffer(lua);
847 mc->depth = mb->depth;
848 mc->level = mb->level;
849 if (rpmluaRunScript(lua, scriptbuf, NULL) == -1)
853 printbuf = rpmluaPopPrintBuffer(lua);
855 mbAppendStr(mb, printbuf);
860 rpmlog(RPMLOG_ERR, _("<lua> scriptlet support not built in\n"));
866 * Execute macro primitives.
867 * @param mb macro expansion state
868 * @param negate should logic be inverted?
869 * @param f beginning of field f
870 * @param fn length of field f
871 * @param g beginning of field g
872 * @param gn length of field g
875 doFoo(MacroBuf mb, int negate, const char * f, size_t fn,
876 const char * g, size_t gn)
881 int verbose = (rpmIsVerbose() != 0);
882 int expand = (g != NULL && gn > 0);
884 /* Don't expand %{verbose:...} argument on false condition */
885 if (STREQ("verbose", f, fn) && (verbose == negate))
889 (void) expandThis(mb, g, gn, &buf);
891 buf = xmalloc(MACROBUFSIZ + fn + gn);
894 if (STREQ("basename", f, fn)) {
895 if ((b = strrchr(buf, '/')) == NULL)
899 } else if (STREQ("dirname", f, fn)) {
900 if ((b = strrchr(buf, '/')) != NULL)
903 } else if (STREQ("shrink", f, fn)) {
905 * shrink body by removing all leading and trailing whitespaces and
906 * reducing intermediate whitespaces to a single space character.
909 size_t buflen = strlen(buf);
912 if (risspace(buf[i])) {
916 } else if (was_space) {
918 if (j > 0) /* remove leading blanks at all */
925 } else if (STREQ("quote", f, fn)) {
927 rasprintf("ed, "%c%s%c", 0x1f, buf, 0x1f);
930 } else if (STREQ("suffix", f, fn)) {
931 if ((b = strrchr(buf, '.')) != NULL)
933 } else if (STREQ("expand", f, fn) || STREQ("verbose", f, fn)) {
935 } else if (STREQ("url2path", f, fn) || STREQ("u2p", f, fn)) {
936 (void)urlPath(buf, (const char **)&b);
937 if (*b == '\0') b = "/";
938 } else if (STREQ("uncompress", f, fn)) {
939 rpmCompressedMagic compressed = COMPRESSED_OTHER;
940 for (b = buf; (c = *b) && isblank(c);)
942 for (be = b; (c = *be) && !isblank(c);)
945 (void) rpmFileIsCompressed(b, &compressed);
946 switch (compressed) {
949 sprintf(be, "%%__cat %s", b);
951 case COMPRESSED_OTHER:
952 sprintf(be, "%%__gzip -dc %s", b);
954 case COMPRESSED_BZIP2:
955 sprintf(be, "%%__bzip2 -dc %s", b);
958 sprintf(be, "%%__unzip %s", b);
960 case COMPRESSED_LZMA:
962 sprintf(be, "%%__xz -dc %s", b);
964 case COMPRESSED_LZIP:
965 sprintf(be, "%%__lzip -dc %s", b);
967 case COMPRESSED_LRZIP:
968 sprintf(be, "%%__lrzip -dqo- %s", b);
970 case COMPRESSED_7ZIP:
971 sprintf(be, "%%__7zip x %s", b);
973 case COMPRESSED_ZSTD:
974 sprintf(be, "%%__zstd -dc %s", b);
978 } else if (STREQ("getenv", f, fn)) {
980 } else if (STREQ("getconfdir", f, fn)) {
981 sprintf(buf, "%s", rpmConfigDir());
983 } else if (STREQ("S", f, fn)) {
984 for (b = buf; (c = *b) && risdigit(c);)
986 if (!c) { /* digit index */
988 sprintf(b, "%%SOURCE%s", buf);
991 } else if (STREQ("P", f, fn)) {
992 for (b = buf; (c = *b) && risdigit(c);)
994 if (!c) { /* digit index */
996 sprintf(b, "%%PATCH%s", buf);
999 } else if (STREQ("F", f, fn)) {
1000 b = buf + strlen(buf) + 1;
1001 sprintf(b, "file%s.file", buf);
1005 (void) expandMacro(mb, b, 0);
1011 * The main macro recursion loop.
1012 * @param mb macro expansion state
1013 * @param src string to expand
1014 * @param slen length of string buffer
1015 * @return 0 on success, 1 on failure
1018 expandMacro(MacroBuf mb, const char *src, size_t slen)
1022 const char *s = src, *se;
1025 size_t fn, gn, tpos;
1030 char *source = NULL;
1031 int store_macro_trace;
1032 int store_expand_trace;
1035 * Always make a (terminated) copy of the source string.
1036 * Beware: avoiding the copy when src is known \0-terminated seems like
1037 * an obvious opportunity for optimization, but doing that breaks
1038 * a special case of macro undefining itself.
1042 source = xmalloc(slen + 1);
1043 strncpy(source, src, slen);
1044 source[slen] = '\0';
1047 if (mb->buf == NULL) {
1048 size_t blen = MACROBUFSIZ + slen;
1049 mb->buf = xmalloc(blen + 1);
1054 tpos = mb->tpos; /* save expansion pointer for printExpand */
1055 store_macro_trace = mb->macro_trace;
1056 store_expand_trace = mb->expand_trace;
1058 if (++mb->depth > max_macro_depth) {
1060 _("Too many levels of recursion in macro expansion. It is likely caused by recursive macro declaration.\n"));
1062 mb->expand_trace = 1;
1067 while (mb->error == 0 && (c = *s) != '\0') {
1069 /* Copy text until next macro */
1072 if (*s) { /* Ensure not end-of-string. */
1075 s++; /* skip first % in %% */
1085 /* Expand next macro */
1088 if (mb->depth > 1) /* XXX full expansion for outermost level */
1089 tpos = mb->tpos; /* save expansion pointer for printExpand */
1094 default: /* %name substitution */
1095 while (*s != '\0' && strchr("!?", *s) != NULL) {
1098 negate = ((negate + 1) % 2);
1108 while ((c = *se) && (risalnum(c) || c == '_'))
1110 /* Recognize non-alnum macros too */
1114 if (*se == '*') se++;
1123 /* For "%name " macros ... */
1124 if ((c = *fe) && isblank(c))
1125 if ((lastc = strchr(fe,'\n')) == NULL)
1126 lastc = strchr(fe, '\0');
1128 case '(': /* %(...) shell escape */
1129 if ((se = matchchar(s, c, ')')) == NULL) {
1130 rpmlog(RPMLOG_ERR, _("Unterminated %c: %s\n"), (char)c, s);
1134 if (mb->macro_trace)
1135 printMacro(mb, s, se+1);
1138 doShellEscape(mb, s, (se - s));
1144 case '{': /* %{...}/%{...:...} substitution */
1145 if ((se = matchchar(s, c, '}')) == NULL) {
1146 rpmlog(RPMLOG_ERR, _("Unterminated %c: %s\n"), (char)c, s);
1150 f = s+1;/* skip { */
1152 while (strchr("!?", *f) != NULL) {
1155 negate = ((negate + 1) % 2);
1162 for (fe = f; (c = *fe) && !strchr(" :}", c);)
1178 /* XXX Everything below expects fe > f */
1181 if ((fe - f) <= 0) {
1182 /* XXX Process % in unknown context */
1183 c = '%'; /* XXX only need to save % */
1187 _("A %% is followed by an unparseable macro\n"));
1193 if (mb->macro_trace)
1194 printMacro(mb, s, se);
1196 /* Expand builtin macros */
1197 if (STREQ("load", f, fn)) {
1199 if (g && gn > 0 && expandThis(mb, g, gn, &arg) == 0) {
1200 /* Print failure iff %{load:...} or %{!?load:...} */
1201 if (loadMacroFile(mb->mc, arg) && chkexist == negate) {
1202 rpmlog(RPMLOG_ERR, _("failed to load macro file %s"), arg);
1210 if (STREQ("global", f, fn)) {
1211 s = doDefine(mb, se, slen - (se - s), RMIL_GLOBAL, 1);
1214 if (STREQ("define", f, fn)) {
1215 s = doDefine(mb, se, slen - (se - s), mb->level, 0);
1218 if (STREQ("undefine", f, fn)) {
1219 s = doUndefine(mb, se, slen - (se - s));
1223 if (STREQ("echo", f, fn) ||
1224 STREQ("warn", f, fn) ||
1225 STREQ("error", f, fn)) {
1226 int loglevel = RPMLOG_NOTICE; /* assume echo */
1227 if (STREQ("error", f, fn)) {
1228 loglevel = RPMLOG_ERR;
1230 } else if (STREQ("warn", f, fn)) {
1231 loglevel = RPMLOG_WARNING;
1233 if (g != NULL && g < ge)
1234 doOutput(mb, loglevel, g, gn);
1236 doOutput(mb, loglevel, "", 0);
1241 if (STREQ("trace", f, fn)) {
1242 /* XXX TODO restore expand_trace/macro_trace to 0 on return */
1243 mb->expand_trace = mb->macro_trace = (negate ? 0 : mb->depth);
1244 if (mb->depth == 1) {
1245 print_macro_trace = mb->macro_trace;
1246 print_expand_trace = mb->expand_trace;
1252 if (STREQ("dump", f, fn)) {
1253 rpmDumpMacroTable(mb->mc, NULL);
1260 if (STREQ("lua", f, fn)) {
1261 doLua(mb, f, fn, g, gn);
1266 /* XXX necessary but clunky */
1267 if (STREQ("basename", f, fn) ||
1268 STREQ("dirname", f, fn) ||
1269 STREQ("shrink", f, fn) ||
1270 STREQ("suffix", f, fn) ||
1271 STREQ("quote", f, fn) ||
1272 STREQ("expand", f, fn) ||
1273 STREQ("verbose", f, fn) ||
1274 STREQ("uncompress", f, fn) ||
1275 STREQ("url2path", f, fn) ||
1276 STREQ("u2p", f, fn) ||
1277 STREQ("getenv", f, fn) ||
1278 STREQ("getconfdir", f, fn) ||
1279 STREQ("S", f, fn) ||
1280 STREQ("P", f, fn) ||
1283 /* FIX: verbose may be set */
1284 doFoo(mb, negate, f, fn, g, gn);
1289 /* Expand defined macros */
1290 mep = findEntry(mb->mc, f, fn, NULL);
1291 me = (mep ? *mep : NULL);
1294 if ((me->flags & ME_AUTO) && mb->level > me->level) {
1295 /* Ignore out-of-scope automatic macros */
1298 /* If we looked up a macro, consider it used */
1299 me->flags |= ME_USED;
1303 /* XXX Special processing for flags */
1305 if ((me == NULL && !negate) || /* Without -f, skip %{-f...} */
1306 (me != NULL && negate)) { /* With -f, skip %{!-f...} */
1311 if (g && g < ge) { /* Expand X in %{-f:X} */
1312 expandMacro(mb, g, gn);
1314 if (me && me->body && *me->body) {/* Expand %{-f}/%{-f*} */
1315 expandMacro(mb, me->body, 0);
1321 /* XXX Special processing for macro existence */
1323 if ((me == NULL && !negate) || /* Without -f, skip %{?f...} */
1324 (me != NULL && negate)) { /* With -f, skip %{!?f...} */
1328 if (g && g < ge) { /* Expand X in %{?f:X} */
1329 expandMacro(mb, g, gn);
1331 if (me && me->body && *me->body) { /* Expand %{?f}/%{?f*} */
1332 expandMacro(mb, me->body, 0);
1338 if (me == NULL) { /* leave unknown %... as is */
1339 /* XXX hack to permit non-overloaded %foo to be passed */
1340 c = '%'; /* XXX only need to save % */
1345 /* Setup args for "%name " macros with opts */
1346 if (me && me->opts != NULL) {
1347 const char *xe = grabArgs(mb, me, fe, lastc);
1352 /* Recursively expand body of macro */
1353 if (me->body && *me->body) {
1354 expandMacro(mb, me->body, 0);
1357 /* Free args for "%name " macros with opts */
1358 if (me->opts != NULL)
1364 mb->buf[mb->tpos] = '\0';
1366 if (mb->error != 0 || mb->expand_trace)
1367 printExpansion(mb, mb->buf+tpos, mb->buf+mb->tpos);
1368 mb->macro_trace = store_macro_trace;
1369 mb->expand_trace = store_expand_trace;
1376 /* =============================================================== */
1378 static int doExpandMacros(rpmMacroContext mc, const char *src, int flags,
1381 MacroBuf mb = xcalloc(1, sizeof(*mb));
1385 mb->depth = mc->depth;
1386 mb->level = mc->level;
1387 mb->macro_trace = print_macro_trace;
1388 mb->expand_trace = print_expand_trace;
1392 rc = expandMacro(mb, src, 0);
1394 mb->buf[mb->tpos] = '\0'; /* XXX just in case */
1395 /* expanded output is usually much less than alloced buffer, downsize */
1396 *target = xrealloc(mb->buf, mb->tpos + 1);
1402 static void pushMacro(rpmMacroContext mc,
1403 const char * n, const char * o, const char * b, int level, int flags)
1407 /* pointer into me */
1409 /* calculate sizes */
1410 size_t olen = o ? strlen(o) : 0;
1411 size_t blen = b ? strlen(b) : 0;
1412 size_t mesize = sizeof(*me) + blen + 1 + (olen ? olen + 1 : 0);
1415 rpmMacroEntry *mep = findEntry(mc, n, 0, &pos);
1417 /* entry with shared name */
1418 me = xmalloc(mesize);
1420 me->body = p = me->arena;
1422 memcpy(p, b, blen + 1);
1427 me->name = (*mep)->name;
1430 /* extend macro table */
1431 const int delta = 256;
1432 if (mc->n % delta == 0)
1433 mc->tab = xrealloc(mc->tab, sizeof(me) * (mc->n + delta));
1434 /* shift pos+ entries to the right */
1435 memmove(mc->tab + pos + 1, mc->tab + pos, sizeof(me) * (mc->n - pos));
1438 mc->tab[pos] = NULL;
1439 mep = &mc->tab[pos];
1440 /* entry with new name */
1441 size_t nlen = strlen(n);
1442 me = xmalloc(mesize + nlen + 1);
1444 me->body = p = me->arena;
1446 memcpy(p, b, blen + 1);
1451 me->name = memcpy(p, n, nlen + 1);
1457 me->opts = memcpy(p, o, olen + 1);
1459 me->opts = o ? "" : NULL;
1462 me->flags &= ~(ME_USED);
1464 /* push over previous definition */
1469 static void popMacro(rpmMacroContext mc, const char * n)
1472 rpmMacroEntry *mep = findEntry(mc, n, 0, &pos);
1476 rpmMacroEntry me = *mep;
1478 /* detach/pop definition */
1479 mc->tab[pos] = me->prev;
1480 /* shrink macro table */
1481 if (me->prev == NULL) {
1483 /* move pos+ elements to the left */
1484 memmove(mc->tab + pos, mc->tab + pos + 1, sizeof(me) * (mc->n - pos));
1487 mc->tab = _free(mc->tab);
1489 /* comes in a single chunk */
1493 static int defineMacro(rpmMacroContext mc, const char * macro, int level)
1495 MacroBuf mb = xcalloc(1, sizeof(*mb));
1498 /* XXX just enough to get by */
1500 (void) doDefine(mb, macro, strlen(macro), level, 0);
1506 static int loadMacroFile(rpmMacroContext mc, const char * fn)
1508 FILE *fd = fopen(fn, "r");
1509 size_t blen = MACROBUFSIZ;
1510 char *buf = xmalloc(blen);
1517 while (rdcl(buf, blen, fd) != NULL) {
1526 rc = defineMacro(mc, n, RMIL_MACROFILES);
1536 static void copyMacros(rpmMacroContext src, rpmMacroContext dst, int level)
1538 for (int i = 0; i < src->n; i++) {
1539 rpmMacroEntry me = src->tab[i];
1541 pushMacro(dst, me->name, me->opts, me->body, level, me->flags);
1545 /* External interfaces */
1547 int rpmExpandMacros(rpmMacroContext mc, const char * sbuf, char ** obuf, int flags)
1549 char *target = NULL;
1552 mc = rpmmctxAcquire(mc);
1553 rc = doExpandMacros(mc, sbuf, flags, &target);
1566 rpmDumpMacroTable(rpmMacroContext mc, FILE * fp)
1568 mc = rpmmctxAcquire(mc);
1569 if (fp == NULL) fp = stderr;
1571 fprintf(fp, "========================\n");
1572 for (int i = 0; i < mc->n; i++) {
1573 rpmMacroEntry me = mc->tab[i];
1575 fprintf(fp, "%3d%c %s", me->level,
1576 ((me->flags & ME_USED) ? '=' : ':'), me->name);
1577 if (me->opts && *me->opts)
1578 fprintf(fp, "(%s)", me->opts);
1579 if (me->body && *me->body)
1580 fprintf(fp, "\t%s", me->body);
1583 fprintf(fp, _("======================== active %d empty %d\n"),
1588 int rpmPushMacro(rpmMacroContext mc,
1589 const char * n, const char * o, const char * b, int level)
1591 mc = rpmmctxAcquire(mc);
1592 pushMacro(mc, n, o, b, level, ME_NONE);
1597 int rpmPopMacro(rpmMacroContext mc, const char * n)
1599 mc = rpmmctxAcquire(mc);
1606 rpmDefineMacro(rpmMacroContext mc, const char * macro, int level)
1609 mc = rpmmctxAcquire(mc);
1610 rc = defineMacro(mc, macro, level);
1616 rpmLoadMacros(rpmMacroContext mc, int level)
1618 rpmMacroContext gmc;
1619 if (mc == NULL || mc == rpmGlobalMacroContext)
1622 gmc = rpmmctxAcquire(NULL);
1623 mc = rpmmctxAcquire(mc);
1625 copyMacros(mc, gmc, level);
1628 rpmmctxRelease(gmc);
1632 rpmLoadMacroFile(rpmMacroContext mc, const char * fn)
1636 mc = rpmmctxAcquire(mc);
1637 rc = loadMacroFile(mc, fn);
1644 rpmInitMacros(rpmMacroContext mc, const char * macrofiles)
1646 ARGV_t pattern, globs = NULL;
1647 rpmMacroContext climc;
1649 if (macrofiles == NULL)
1652 argvSplit(&globs, macrofiles, ":");
1653 mc = rpmmctxAcquire(mc);
1654 for (pattern = globs; *pattern; pattern++) {
1655 ARGV_t path, files = NULL;
1657 /* Glob expand the macro file path element, expanding ~ to $HOME. */
1658 if (rpmGlob(*pattern, NULL, &files) != 0) {
1662 /* Read macros from each file. */
1663 for (path = files; *path; path++) {
1664 if (rpmFileHasSuffix(*path, ".rpmnew") ||
1665 rpmFileHasSuffix(*path, ".rpmsave") ||
1666 rpmFileHasSuffix(*path, ".rpmorig")) {
1669 (void) loadMacroFile(mc, *path);
1675 /* Reload cmdline macros */
1676 climc = rpmmctxAcquire(rpmCLIMacroContext);
1677 copyMacros(climc, mc, RMIL_CMDLINE);
1678 rpmmctxRelease(climc);
1684 rpmFreeMacros(rpmMacroContext mc)
1686 mc = rpmmctxAcquire(mc);
1688 /* remove from the end to avoid memmove */
1689 rpmMacroEntry me = mc->tab[mc->n - 1];
1690 popMacro(mc, me->name);
1696 rpmExpand(const char *arg, ...)
1699 char *buf = NULL, *ret = NULL;
1710 /* precalculate unexpanded size */
1712 for (s = arg; s != NULL; s = va_arg(ap, const char *))
1716 buf = xmalloc(blen + 1);
1720 for (pe = buf, s = arg; s != NULL; s = va_arg(ap, const char *))
1724 mc = rpmmctxAcquire(NULL);
1725 (void) doExpandMacros(mc, buf, 0, &ret);
1734 rpmExpandNumeric(const char *arg)
1742 val = rpmExpand(arg, NULL);
1743 if (!(val && *val != '%'))
1745 else if (*val == 'Y' || *val == 'y')
1747 else if (*val == 'N' || *val == 'n')
1751 rc = strtol(val, &end, 0);
1752 if (!(end && *end == '\0'))