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)
424 if (expandThis(mb, cmd, clen, &buf))
427 if ((shf = popen(buf, "r")) == NULL) {
432 size_t tpos = mb->tpos;
433 while ((c = fgetc(shf)) != EOF) {
438 /* Delete trailing \r \n */
439 while (mb->tpos > tpos && iseol(mb->buf[mb->tpos-1])) {
440 mb->buf[--mb->tpos] = '\0';
449 * Parse (and execute) new macro definition.
450 * @param mb macro expansion state
451 * @param se macro definition to parse
452 * @param slen length of se argument
453 * @param level macro recursion level
454 * @param expandbody should body be expanded?
455 * @return address to continue parsing
458 doDefine(MacroBuf mb, const char * se, size_t slen, int level, int expandbody)
461 char *buf = xmalloc(slen + 3); /* Some leeway for termination issues... */
462 char *n = buf, *ne = n;
464 char *b, *be, *ebody = NULL;
467 const char *sbody; /* as-is body start */
468 int rc = 1; /* assume failure */
473 /* Copy opts (if present) */
477 /* Options must be terminated with ')' */
478 if (strchr(s, ')')) {
483 rpmlog(RPMLOG_ERR, _("Macro %%%s has unterminated opts\n"), n);
488 /* Copy body, skipping over escaped newlines */
492 if (c == '{') { /* XXX permit silent {...} grouping */
493 if ((se = matchchar(s, c, '}')) == NULL) {
495 _("Macro %%%s has unterminated body\n"), n);
496 se = s; /* XXX W2DO? */
499 s++; /* XXX skip { */
500 strncpy(b, s, (se - s));
503 se++; /* XXX skip } */
504 s = se; /* move scan forward */
505 } else { /* otherwise free-field */
507 while (*s && (bc || pc || !iseol(*s))) {
517 case '{': *be++ = *s++; bc++; break;
518 case '(': *be++ = *s++; pc++; break;
519 case '%': *be++ = *s++; break;
522 case '{': if (bc > 0) bc++; break;
523 case '}': if (bc > 0) bc--; break;
524 case '(': if (pc > 0) pc++; break;
525 case ')': if (pc > 0) pc--; break;
533 _("Macro %%%s has unterminated body\n"), n);
534 se = s; /* XXX W2DO? */
538 /* Trim trailing blanks/newlines */
539 while (--be >= b && (c = *be) && (isblank(c) || iseol(c)))
541 *(++be) = '\0'; /* one too far */
544 /* Move scan over body */
549 /* Names must start with alphabetic or _ and be at least 3 chars */
550 if (!((c = *n) && (risalpha(c) || c == '_') && (ne - n) > 2)) {
551 rpmlog(RPMLOG_ERR, _("Macro %%%s has illegal name (%s)\n"),
552 n, expandbody ? "%global": "%define");
557 rpmlog(RPMLOG_ERR, _("Macro %%%s has empty body\n"), n);
561 if (!isblank(*sbody) && !(*sbody == '\\' && iseol(sbody[1])))
562 rpmlog(RPMLOG_WARNING, _("Macro %%%s needs whitespace before body\n"), n);
565 if (expandThis(mb, b, 0, &ebody)) {
566 rpmlog(RPMLOG_ERR, _("Macro %%%s failed to expand\n"), n);
572 pushMacro(mb->mc, n, o, b, level, ME_NONE);
584 * Parse (and execute) macro undefinition.
585 * @param mb macro expansion state
586 * @param se macro name to undefine
587 * @param slen length of se argument
588 * @return address to continue parsing
591 doUndefine(MacroBuf mb, const char * se, size_t slen)
594 char *buf = xmalloc(slen + 1);
595 char *n = buf, *ne = n;
600 /* Move scan over body */
605 /* Names must start with alphabetic or _ and be at least 3 chars */
606 if (!((c = *n) && (risalpha(c) || c == '_') && (ne - n) > 2)) {
607 rpmlog(RPMLOG_ERR, _("Macro %%%s has illegal name (%%undefine)\n"), n);
620 * Free parsed arguments for parameterized macro.
621 * @param mb macro expansion state
624 freeArgs(MacroBuf mb)
626 rpmMacroContext mc = mb->mc;
628 /* Delete dynamic macro definitions */
629 for (int i = 0; i < mc->n; i++) {
630 rpmMacroEntry me = mc->tab[i];
632 if (me->level < mb->level)
634 /* Warn on defined but unused non-automatic, scoped macros */
635 if (!(me->flags & (ME_AUTO|ME_USED))) {
636 rpmlog(RPMLOG_WARNING,
637 _("Macro %%%s defined but not used within scope\n"),
639 /* Only whine once */
640 me->flags |= ME_USED;
643 /* compensate if the slot is to go away */
644 if (me->prev == NULL)
646 popMacro(mc, me->name);
651 static void splitQuoted(ARGV_t *av, const char * str, const char * seps)
653 const int qchar = 0x1f; /* ASCII unit separator */
655 const char *start = str;
658 while (start != NULL) {
659 if (!quoted && strchr(seps, *s)) {
660 size_t slen = s - start;
661 /* quoted arguments are always kept, otherwise skip empty args */
663 char *d, arg[slen + 1];
665 for (d = arg, t = start; t - start < slen; t++) {
684 * Parse arguments (to next new line) for parameterized macro.
685 * @todo Use popt rather than getopt to parse args.
686 * @param mb macro expansion state
687 * @param me macro entry slot
688 * @param se arguments to parse
689 * @param lastc stop parsing at lastc
690 * @return address to continue parsing
693 grabArgs(MacroBuf mb, const rpmMacroEntry me, const char * se,
696 const char *cont = NULL;
704 * Prepare list of call arguments, starting with macro name as argv[0].
705 * Make a copy of se up to lastc string that we can pass to argvSplit().
706 * Append the results to main argv.
708 argvAdd(&argv, me->name);
710 int oescape = mb->escape;
713 /* Expand possible macros in arguments */
715 expandThis(mb, se, lastc-se, &s);
716 mb->escape = oescape;
718 splitQuoted(&argv, s, " \t");
721 cont = ((*lastc == '\0' || *lastc == '\n') && *(lastc-1) != '\\') ?
725 /* Bump call depth on entry before first macro define */
728 /* Setup macro name as %0 */
729 pushMacro(mb->mc, "0", NULL, me->name, mb->level, ME_AUTO);
732 * The macro %* analoguous to the shell's $* means "Pass all non-macro
733 * parameters." Consequently, there needs to be a macro that means "Pass all
734 * (including macro parameters) options". This is useful for verifying
735 * parameters during expansion and yet transparently passing all parameters
736 * through for higher level processing (e.g. %description and/or %setup).
737 * This is the (potential) justification for %{**} ...
739 args = argvJoin(argv + 1, " ");
740 pushMacro(mb->mc, "**", NULL, args, mb->level, ME_AUTO);
744 * POSIX states optind must be 1 before any call but glibc uses 0
745 * to (re)initialize getopt structures, eww.
754 argc = argvCount(argv);
756 /* Define option macros. */
757 while ((c = getopt(argc, argv, opts)) != -1)
759 char *name = NULL, *body = NULL;
760 if (c == '?' || strchr(opts, c) == NULL) {
761 rpmlog(RPMLOG_ERR, _("Unknown option %c in %s(%s)\n"),
762 (char)optopt, me->name, opts);
767 rasprintf(&name, "-%c", c);
769 rasprintf(&body, "-%c %s", c, optarg);
771 rasprintf(&body, "-%c", c);
773 pushMacro(mb->mc, name, NULL, body, mb->level, ME_AUTO);
778 rasprintf(&name, "-%c*", c);
779 pushMacro(mb->mc, name, NULL, optarg, mb->level, ME_AUTO);
784 /* Add argument count (remaining non-option items) as macro. */
786 rasprintf(&ac, "%d", (argc - optind));
787 pushMacro(mb->mc, "#", NULL, ac, mb->level, ME_AUTO);
791 /* Add macro for each argument */
793 for (c = optind; c < argc; c++) {
795 rasprintf(&name, "%d", (c - optind + 1));
796 pushMacro(mb->mc, name, NULL, argv[c], mb->level, ME_AUTO);
801 /* Add concatenated unexpanded arguments as yet another macro. */
802 args = argvJoin(argv + optind, " ");
803 pushMacro(mb->mc, "*", NULL, args ? args : "", mb->level, ME_AUTO);
812 * Perform macro message output
813 * @param mb macro expansion state
814 * @param waserror use rpmlog()?
815 * @param msg message to output
816 * @param msglen no. of bytes in message
819 doOutput(MacroBuf mb, int loglevel, const char * msg, size_t msglen)
823 (void) expandThis(mb, msg, msglen, &buf);
824 rpmlog(loglevel, "%s\n", buf);
828 static void doLua(MacroBuf mb, const char * f, size_t fn, const char * g, size_t gn)
831 rpmlua lua = NULL; /* Global state. */
832 char *scriptbuf = xmalloc(gn + 1);
834 rpmMacroContext mc = mb->mc;
835 int odepth = mc->depth;
836 int olevel = mc->level;
838 if (g != NULL && gn > 0)
839 memcpy(scriptbuf, g, gn);
840 scriptbuf[gn] = '\0';
841 rpmluaPushPrintBuffer(lua);
842 mc->depth = mb->depth;
843 mc->level = mb->level;
844 if (rpmluaRunScript(lua, scriptbuf, NULL) == -1)
848 printbuf = rpmluaPopPrintBuffer(lua);
850 mbAppendStr(mb, printbuf);
855 rpmlog(RPMLOG_ERR, _("<lua> scriptlet support not built in\n"));
861 * Execute macro primitives.
862 * @param mb macro expansion state
863 * @param negate should logic be inverted?
864 * @param f beginning of field f
865 * @param fn length of field f
866 * @param g beginning of field g
867 * @param gn length of field g
870 doFoo(MacroBuf mb, int negate, const char * f, size_t fn,
871 const char * g, size_t gn)
876 int verbose = (rpmIsVerbose() != 0);
877 int expand = (g != NULL && gn > 0);
879 /* Don't expand %{verbose:...} argument on false condition */
880 if (STREQ("verbose", f, fn) && (verbose == negate))
884 (void) expandThis(mb, g, gn, &buf);
886 buf = xmalloc(MACROBUFSIZ + fn + gn);
889 if (STREQ("basename", f, fn)) {
890 if ((b = strrchr(buf, '/')) == NULL)
894 } else if (STREQ("dirname", f, fn)) {
895 if ((b = strrchr(buf, '/')) != NULL)
898 } else if (STREQ("shrink", f, fn)) {
900 * shrink body by removing all leading and trailing whitespaces and
901 * reducing intermediate whitespaces to a single space character.
904 size_t buflen = strlen(buf);
907 if (risspace(buf[i])) {
911 } else if (was_space) {
913 if (j > 0) /* remove leading blanks at all */
920 } else if (STREQ("quote", f, fn)) {
922 rasprintf("ed, "%c%s%c", 0x1f, buf, 0x1f);
925 } else if (STREQ("suffix", f, fn)) {
926 if ((b = strrchr(buf, '.')) != NULL)
928 } else if (STREQ("expand", f, fn) || STREQ("verbose", f, fn)) {
930 } else if (STREQ("url2path", f, fn) || STREQ("u2p", f, fn)) {
931 (void)urlPath(buf, (const char **)&b);
932 if (*b == '\0') b = "/";
933 } else if (STREQ("uncompress", f, fn)) {
934 rpmCompressedMagic compressed = COMPRESSED_OTHER;
935 for (b = buf; (c = *b) && isblank(c);)
937 for (be = b; (c = *be) && !isblank(c);)
940 (void) rpmFileIsCompressed(b, &compressed);
941 switch (compressed) {
944 sprintf(be, "%%__cat %s", b);
946 case COMPRESSED_OTHER:
947 sprintf(be, "%%__gzip -dc %s", b);
949 case COMPRESSED_BZIP2:
950 sprintf(be, "%%__bzip2 -dc %s", b);
953 sprintf(be, "%%__unzip %s", b);
955 case COMPRESSED_LZMA:
957 sprintf(be, "%%__xz -dc %s", b);
959 case COMPRESSED_LZIP:
960 sprintf(be, "%%__lzip -dc %s", b);
962 case COMPRESSED_LRZIP:
963 sprintf(be, "%%__lrzip -dqo- %s", b);
965 case COMPRESSED_7ZIP:
966 sprintf(be, "%%__7zip x %s", b);
968 case COMPRESSED_ZSTD:
969 sprintf(be, "%%__zstd -dc %s", b);
973 } else if (STREQ("getenv", f, fn)) {
975 } else if (STREQ("getconfdir", f, fn)) {
976 sprintf(buf, "%s", rpmConfigDir());
978 } else if (STREQ("S", f, fn)) {
979 for (b = buf; (c = *b) && risdigit(c);)
981 if (!c) { /* digit index */
983 sprintf(b, "%%SOURCE%s", buf);
986 } else if (STREQ("P", f, fn)) {
987 for (b = buf; (c = *b) && risdigit(c);)
989 if (!c) { /* digit index */
991 sprintf(b, "%%PATCH%s", buf);
994 } else if (STREQ("F", f, fn)) {
995 b = buf + strlen(buf) + 1;
996 sprintf(b, "file%s.file", buf);
1000 (void) expandMacro(mb, b, 0);
1006 * The main macro recursion loop.
1007 * @param mb macro expansion state
1008 * @param src string to expand
1009 * @param slen length of string buffer
1010 * @return 0 on success, 1 on failure
1013 expandMacro(MacroBuf mb, const char *src, size_t slen)
1017 const char *s = src, *se;
1020 size_t fn, gn, tpos;
1025 char *source = NULL;
1026 int store_macro_trace;
1027 int store_expand_trace;
1030 * Always make a (terminated) copy of the source string.
1031 * Beware: avoiding the copy when src is known \0-terminated seems like
1032 * an obvious opportunity for optimization, but doing that breaks
1033 * a special case of macro undefining itself.
1037 source = xmalloc(slen + 1);
1038 strncpy(source, src, slen);
1039 source[slen] = '\0';
1042 if (mb->buf == NULL) {
1043 size_t blen = MACROBUFSIZ + slen;
1044 mb->buf = xmalloc(blen + 1);
1049 tpos = mb->tpos; /* save expansion pointer for printExpand */
1050 store_macro_trace = mb->macro_trace;
1051 store_expand_trace = mb->expand_trace;
1053 if (++mb->depth > max_macro_depth) {
1055 _("Too many levels of recursion in macro expansion. It is likely caused by recursive macro declaration.\n"));
1057 mb->expand_trace = 1;
1062 while (mb->error == 0 && (c = *s) != '\0') {
1064 /* Copy text until next macro */
1067 if (*s) { /* Ensure not end-of-string. */
1070 s++; /* skip first % in %% */
1080 /* Expand next macro */
1083 if (mb->depth > 1) /* XXX full expansion for outermost level */
1084 tpos = mb->tpos; /* save expansion pointer for printExpand */
1089 default: /* %name substitution */
1090 while (*s != '\0' && strchr("!?", *s) != NULL) {
1093 negate = ((negate + 1) % 2);
1103 while ((c = *se) && (risalnum(c) || c == '_'))
1105 /* Recognize non-alnum macros too */
1109 if (*se == '*') se++;
1118 /* For "%name " macros ... */
1119 if ((c = *fe) && isblank(c))
1120 if ((lastc = strchr(fe,'\n')) == NULL)
1121 lastc = strchr(fe, '\0');
1123 case '(': /* %(...) shell escape */
1124 if ((se = matchchar(s, c, ')')) == NULL) {
1125 rpmlog(RPMLOG_ERR, _("Unterminated %c: %s\n"), (char)c, s);
1129 if (mb->macro_trace)
1130 printMacro(mb, s, se+1);
1133 doShellEscape(mb, s, (se - s));
1139 case '{': /* %{...}/%{...:...} substitution */
1140 if ((se = matchchar(s, c, '}')) == NULL) {
1141 rpmlog(RPMLOG_ERR, _("Unterminated %c: %s\n"), (char)c, s);
1145 f = s+1;/* skip { */
1147 while (strchr("!?", *f) != NULL) {
1150 negate = ((negate + 1) % 2);
1157 for (fe = f; (c = *fe) && !strchr(" :}", c);)
1173 /* XXX Everything below expects fe > f */
1176 if ((fe - f) <= 0) {
1177 /* XXX Process % in unknown context */
1178 c = '%'; /* XXX only need to save % */
1182 _("A %% is followed by an unparseable macro\n"));
1188 if (mb->macro_trace)
1189 printMacro(mb, s, se);
1191 /* Expand builtin macros */
1192 if (STREQ("load", f, fn)) {
1194 if (g && gn > 0 && expandThis(mb, g, gn, &arg) == 0) {
1195 /* Print failure iff %{load:...} or %{!?load:...} */
1196 if (loadMacroFile(mb->mc, arg) && chkexist == negate) {
1197 rpmlog(RPMLOG_ERR, _("failed to load macro file %s"), arg);
1205 if (STREQ("global", f, fn)) {
1206 s = doDefine(mb, se, slen - (se - s), RMIL_GLOBAL, 1);
1209 if (STREQ("define", f, fn)) {
1210 s = doDefine(mb, se, slen - (se - s), mb->level, 0);
1213 if (STREQ("undefine", f, fn)) {
1214 s = doUndefine(mb, se, slen - (se - s));
1218 if (STREQ("echo", f, fn) ||
1219 STREQ("warn", f, fn) ||
1220 STREQ("error", f, fn)) {
1221 int loglevel = RPMLOG_NOTICE; /* assume echo */
1222 if (STREQ("error", f, fn)) {
1223 loglevel = RPMLOG_ERR;
1225 } else if (STREQ("warn", f, fn)) {
1226 loglevel = RPMLOG_WARNING;
1228 if (g != NULL && g < ge)
1229 doOutput(mb, loglevel, g, gn);
1231 doOutput(mb, loglevel, "", 0);
1236 if (STREQ("trace", f, fn)) {
1237 /* XXX TODO restore expand_trace/macro_trace to 0 on return */
1238 mb->expand_trace = mb->macro_trace = (negate ? 0 : mb->depth);
1239 if (mb->depth == 1) {
1240 print_macro_trace = mb->macro_trace;
1241 print_expand_trace = mb->expand_trace;
1247 if (STREQ("dump", f, fn)) {
1248 rpmDumpMacroTable(mb->mc, NULL);
1255 if (STREQ("lua", f, fn)) {
1256 doLua(mb, f, fn, g, gn);
1261 /* XXX necessary but clunky */
1262 if (STREQ("basename", f, fn) ||
1263 STREQ("dirname", f, fn) ||
1264 STREQ("shrink", f, fn) ||
1265 STREQ("suffix", f, fn) ||
1266 STREQ("quote", f, fn) ||
1267 STREQ("expand", f, fn) ||
1268 STREQ("verbose", f, fn) ||
1269 STREQ("uncompress", f, fn) ||
1270 STREQ("url2path", f, fn) ||
1271 STREQ("u2p", f, fn) ||
1272 STREQ("getenv", f, fn) ||
1273 STREQ("getconfdir", f, fn) ||
1274 STREQ("S", f, fn) ||
1275 STREQ("P", f, fn) ||
1278 /* FIX: verbose may be set */
1279 doFoo(mb, negate, f, fn, g, gn);
1284 /* Expand defined macros */
1285 mep = findEntry(mb->mc, f, fn, NULL);
1286 me = (mep ? *mep : NULL);
1289 if ((me->flags & ME_AUTO) && mb->level > me->level) {
1290 /* Ignore out-of-scope automatic macros */
1293 /* If we looked up a macro, consider it used */
1294 me->flags |= ME_USED;
1298 /* XXX Special processing for flags */
1300 if ((me == NULL && !negate) || /* Without -f, skip %{-f...} */
1301 (me != NULL && negate)) { /* With -f, skip %{!-f...} */
1306 if (g && g < ge) { /* Expand X in %{-f:X} */
1307 expandMacro(mb, g, gn);
1309 if (me && me->body && *me->body) {/* Expand %{-f}/%{-f*} */
1310 expandMacro(mb, me->body, 0);
1316 /* XXX Special processing for macro existence */
1318 if ((me == NULL && !negate) || /* Without -f, skip %{?f...} */
1319 (me != NULL && negate)) { /* With -f, skip %{!?f...} */
1323 if (g && g < ge) { /* Expand X in %{?f:X} */
1324 expandMacro(mb, g, gn);
1326 if (me && me->body && *me->body) { /* Expand %{?f}/%{?f*} */
1327 expandMacro(mb, me->body, 0);
1333 if (me == NULL) { /* leave unknown %... as is */
1334 /* XXX hack to permit non-overloaded %foo to be passed */
1335 c = '%'; /* XXX only need to save % */
1340 /* Setup args for "%name " macros with opts */
1341 if (me && me->opts != NULL) {
1342 const char *xe = grabArgs(mb, me, fe, lastc);
1347 /* Recursively expand body of macro */
1348 if (me->body && *me->body) {
1349 expandMacro(mb, me->body, 0);
1352 /* Free args for "%name " macros with opts */
1353 if (me->opts != NULL)
1359 mb->buf[mb->tpos] = '\0';
1361 if (mb->error != 0 || mb->expand_trace)
1362 printExpansion(mb, mb->buf+tpos, mb->buf+mb->tpos);
1363 mb->macro_trace = store_macro_trace;
1364 mb->expand_trace = store_expand_trace;
1371 /* =============================================================== */
1373 static int doExpandMacros(rpmMacroContext mc, const char *src, int flags,
1376 MacroBuf mb = xcalloc(1, sizeof(*mb));
1380 mb->depth = mc->depth;
1381 mb->level = mc->level;
1382 mb->macro_trace = print_macro_trace;
1383 mb->expand_trace = print_expand_trace;
1387 rc = expandMacro(mb, src, 0);
1389 mb->buf[mb->tpos] = '\0'; /* XXX just in case */
1390 /* expanded output is usually much less than alloced buffer, downsize */
1391 *target = xrealloc(mb->buf, mb->tpos + 1);
1397 static void pushMacro(rpmMacroContext mc,
1398 const char * n, const char * o, const char * b, int level, int flags)
1402 /* pointer into me */
1404 /* calculate sizes */
1405 size_t olen = o ? strlen(o) : 0;
1406 size_t blen = b ? strlen(b) : 0;
1407 size_t mesize = sizeof(*me) + blen + 1 + (olen ? olen + 1 : 0);
1410 rpmMacroEntry *mep = findEntry(mc, n, 0, &pos);
1412 /* entry with shared name */
1413 me = xmalloc(mesize);
1415 me->body = p = me->arena;
1417 memcpy(p, b, blen + 1);
1422 me->name = (*mep)->name;
1425 /* extend macro table */
1426 const int delta = 256;
1427 if (mc->n % delta == 0)
1428 mc->tab = xrealloc(mc->tab, sizeof(me) * (mc->n + delta));
1429 /* shift pos+ entries to the right */
1430 memmove(mc->tab + pos + 1, mc->tab + pos, sizeof(me) * (mc->n - pos));
1433 mc->tab[pos] = NULL;
1434 mep = &mc->tab[pos];
1435 /* entry with new name */
1436 size_t nlen = strlen(n);
1437 me = xmalloc(mesize + nlen + 1);
1439 me->body = p = me->arena;
1441 memcpy(p, b, blen + 1);
1446 me->name = memcpy(p, n, nlen + 1);
1452 me->opts = memcpy(p, o, olen + 1);
1454 me->opts = o ? "" : NULL;
1457 me->flags &= ~(ME_USED);
1459 /* push over previous definition */
1464 static void popMacro(rpmMacroContext mc, const char * n)
1467 rpmMacroEntry *mep = findEntry(mc, n, 0, &pos);
1471 rpmMacroEntry me = *mep;
1473 /* detach/pop definition */
1474 mc->tab[pos] = me->prev;
1475 /* shrink macro table */
1476 if (me->prev == NULL) {
1478 /* move pos+ elements to the left */
1479 memmove(mc->tab + pos, mc->tab + pos + 1, sizeof(me) * (mc->n - pos));
1482 mc->tab = _free(mc->tab);
1484 /* comes in a single chunk */
1488 static int defineMacro(rpmMacroContext mc, const char * macro, int level)
1490 MacroBuf mb = xcalloc(1, sizeof(*mb));
1493 /* XXX just enough to get by */
1495 (void) doDefine(mb, macro, strlen(macro), level, 0);
1501 static int loadMacroFile(rpmMacroContext mc, const char * fn)
1503 FILE *fd = fopen(fn, "r");
1504 size_t blen = MACROBUFSIZ;
1505 char *buf = xmalloc(blen);
1512 while (rdcl(buf, blen, fd) != NULL) {
1521 rc = defineMacro(mc, n, RMIL_MACROFILES);
1531 static void copyMacros(rpmMacroContext src, rpmMacroContext dst, int level)
1533 for (int i = 0; i < src->n; i++) {
1534 rpmMacroEntry me = src->tab[i];
1536 pushMacro(dst, me->name, me->opts, me->body, level, me->flags);
1540 /* External interfaces */
1542 int rpmExpandMacros(rpmMacroContext mc, const char * sbuf, char ** obuf, int flags)
1544 char *target = NULL;
1547 mc = rpmmctxAcquire(mc);
1548 rc = doExpandMacros(mc, sbuf, flags, &target);
1561 rpmDumpMacroTable(rpmMacroContext mc, FILE * fp)
1563 mc = rpmmctxAcquire(mc);
1564 if (fp == NULL) fp = stderr;
1566 fprintf(fp, "========================\n");
1567 for (int i = 0; i < mc->n; i++) {
1568 rpmMacroEntry me = mc->tab[i];
1570 fprintf(fp, "%3d%c %s", me->level,
1571 ((me->flags & ME_USED) ? '=' : ':'), me->name);
1572 if (me->opts && *me->opts)
1573 fprintf(fp, "(%s)", me->opts);
1574 if (me->body && *me->body)
1575 fprintf(fp, "\t%s", me->body);
1578 fprintf(fp, _("======================== active %d empty %d\n"),
1583 int rpmPushMacro(rpmMacroContext mc,
1584 const char * n, const char * o, const char * b, int level)
1586 mc = rpmmctxAcquire(mc);
1587 pushMacro(mc, n, o, b, level, ME_NONE);
1592 int rpmPopMacro(rpmMacroContext mc, const char * n)
1594 mc = rpmmctxAcquire(mc);
1601 rpmDefineMacro(rpmMacroContext mc, const char * macro, int level)
1604 mc = rpmmctxAcquire(mc);
1605 rc = defineMacro(mc, macro, level);
1611 rpmLoadMacros(rpmMacroContext mc, int level)
1613 rpmMacroContext gmc;
1614 if (mc == NULL || mc == rpmGlobalMacroContext)
1617 gmc = rpmmctxAcquire(NULL);
1618 mc = rpmmctxAcquire(mc);
1620 copyMacros(mc, gmc, level);
1623 rpmmctxRelease(gmc);
1627 rpmLoadMacroFile(rpmMacroContext mc, const char * fn)
1631 mc = rpmmctxAcquire(mc);
1632 rc = loadMacroFile(mc, fn);
1639 rpmInitMacros(rpmMacroContext mc, const char * macrofiles)
1641 ARGV_t pattern, globs = NULL;
1642 rpmMacroContext climc;
1644 if (macrofiles == NULL)
1647 argvSplit(&globs, macrofiles, ":");
1648 mc = rpmmctxAcquire(mc);
1649 for (pattern = globs; *pattern; pattern++) {
1650 ARGV_t path, files = NULL;
1652 /* Glob expand the macro file path element, expanding ~ to $HOME. */
1653 if (rpmGlob(*pattern, NULL, &files) != 0) {
1657 /* Read macros from each file. */
1658 for (path = files; *path; path++) {
1659 if (rpmFileHasSuffix(*path, ".rpmnew") ||
1660 rpmFileHasSuffix(*path, ".rpmsave") ||
1661 rpmFileHasSuffix(*path, ".rpmorig")) {
1664 (void) loadMacroFile(mc, *path);
1670 /* Reload cmdline macros */
1671 climc = rpmmctxAcquire(rpmCLIMacroContext);
1672 copyMacros(climc, mc, RMIL_CMDLINE);
1673 rpmmctxRelease(climc);
1679 rpmFreeMacros(rpmMacroContext mc)
1681 mc = rpmmctxAcquire(mc);
1683 /* remove from the end to avoid memmove */
1684 rpmMacroEntry me = mc->tab[mc->n - 1];
1685 popMacro(mc, me->name);
1691 rpmExpand(const char *arg, ...)
1694 char *buf = NULL, *ret = NULL;
1705 /* precalculate unexpanded size */
1707 for (s = arg; s != NULL; s = va_arg(ap, const char *))
1711 buf = xmalloc(blen + 1);
1715 for (pe = buf, s = arg; s != NULL; s = va_arg(ap, const char *))
1719 mc = rpmmctxAcquire(NULL);
1720 (void) doExpandMacros(mc, buf, 0, &ret);
1729 rpmExpandNumeric(const char *arg)
1737 val = rpmExpand(arg, NULL);
1738 if (!(val && *val != '%'))
1740 else if (*val == 'Y' || *val == 'y')
1742 else if (*val == 'N' || *val == 'n')
1746 rc = strtol(val, &end, 0);
1747 if (!(end && *end == '\0'))