Imported Upstream version 4.14.1
[platform/upstream/rpm.git] / rpmio / macro.c
1 /** \ingroup rpmrc rpmio
2  * \file rpmio/macro.c
3  */
4
5 #include "system.h"
6 #include <stdarg.h>
7 #include <pthread.h>
8 #include <errno.h>
9 #ifdef HAVE_GETOPT_H
10 #include <getopt.h>
11 #else
12 extern char *optarg;
13 extern int optind;
14 #endif
15
16 #if !defined(isblank)
17 #define isblank(_c)     ((_c) == ' ' || (_c) == '\t')
18 #endif
19 #define iseol(_c)       ((_c) == '\n' || (_c) == '\r')
20
21 #define STREQ(_t, _f, _fn)      ((_fn) == (sizeof(_t)-1) && rstreqn((_t), (_f), (_fn)))
22
23 #define MACROBUFSIZ (BUFSIZ * 2)
24
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>
31 #include <rpm/argv.h>
32
33 #ifdef  WITH_LUA
34 #include "rpmio/rpmlua.h"
35 #endif
36
37 #include "debug.h"
38
39 enum macroFlags_e {
40     ME_NONE     = 0,
41     ME_AUTO     = (1 << 0),
42     ME_USED     = (1 << 1),
43 };
44
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. */
54 };
55
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  */
62     pthread_mutex_t lock;
63     pthread_mutexattr_t lockattr;
64 };
65
66
67 static struct rpmMacroContext_s rpmGlobalMacroContext_s;
68 rpmMacroContext rpmGlobalMacroContext = &rpmGlobalMacroContext_s;
69
70 static struct rpmMacroContext_s rpmCLIMacroContext_s;
71 rpmMacroContext rpmCLIMacroContext = &rpmCLIMacroContext_s;
72
73 /*
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.
81  */
82 static pthread_once_t locksInitialized = PTHREAD_ONCE_INIT;
83
84 static void initLocks(void)
85 {
86     rpmMacroContext mcs[] = { rpmGlobalMacroContext, rpmCLIMacroContext, NULL };
87
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);
93     }
94 }
95
96 /**
97  * Macro expansion state.
98  */
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 */
110     rpmMacroContext mc;
111 } * MacroBuf;
112
113 #define _MAX_MACRO_DEPTH        64
114 static int max_macro_depth = _MAX_MACRO_DEPTH;
115
116 #define _PRINT_MACRO_TRACE      0
117 static int print_macro_trace = _PRINT_MACRO_TRACE;
118
119 #define _PRINT_EXPAND_TRACE     0
120 static int print_expand_trace = _PRINT_EXPAND_TRACE;
121
122 /* forward ref */
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);
128
129 /* =============================================================== */
130
131 static rpmMacroContext rpmmctxAcquire(rpmMacroContext mc)
132 {
133     if (mc == NULL)
134         mc = rpmGlobalMacroContext;
135     pthread_once(&locksInitialized, initLocks);
136     pthread_mutex_lock(&mc->lock);
137     return mc;
138 }
139
140 static rpmMacroContext rpmmctxRelease(rpmMacroContext mc)
141 {
142     pthread_mutex_unlock(&mc->lock);
143     return NULL;
144 }
145
146 /**
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)
153  */
154 static rpmMacroEntry *
155 findEntry(rpmMacroContext mc, const char *name, size_t namelen, size_t *pos)
156 {
157     /* bsearch */
158     int cmp = 1;
159     size_t l = 0;
160     size_t u = mc->n;
161     size_t i = 0;
162     while (l < u) {
163         i = (l + u) / 2;
164         rpmMacroEntry me = mc->tab[i];
165         if (namelen == 0)
166             cmp = strcmp(me->name, name);
167         else {
168             cmp = strncmp(me->name, name, namelen);
169             /* longer me->name compares greater */
170             if (cmp == 0)
171                 cmp = (me->name[namelen] != '\0');
172         }
173         if (cmp < 0)
174             l = i + 1;
175         else if (cmp > 0)
176             u = i;
177         else
178             break;
179     }
180
181     if (pos)
182         *pos = (cmp < 0) ? i + 1 : i;
183     if (cmp == 0)
184         return &mc->tab[i];
185     return NULL;
186 }
187
188 /* =============================================================== */
189
190 /**
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
196  */
197 static char *
198 rdcl(char * buf, size_t size, FILE *f)
199 {
200     char *q = buf - 1;          /* initialize just before buffer. */
201     size_t nb = 0;
202     size_t nread = 0;
203     int pc = 0, bc = 0;
204     char *p = buf;
205
206     if (f != NULL)
207     do {
208         *(++q) = '\0';                  /* terminate and move forward. */
209         if (fgets(q, size, f) == NULL)  /* read next line. */
210             break;
211         nb = strlen(q);
212         nread += nb;                    /* trim trailing \r and \n */
213         for (q += nb - 1; nb > 0 && iseol(*q); q--)
214             nb--;
215         for (; p <= q; p++) {
216             switch (*p) {
217                 case '\\':
218                     switch (*(p+1)) {
219                         case '\0': break;
220                         default: p++; break;
221                     }
222                     break;
223                 case '%':
224                     switch (*(p+1)) {
225                         case '{': p++, bc++; break;
226                         case '(': p++, pc++; break;
227                         case '%': p++; break;
228                     }
229                     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;
234             }
235         }
236         if (nb == 0 || (*q != '\\' && !bc && !pc) || *(q+1) == '\0') {
237             *(++q) = '\0';              /* trim trailing \r, \n */
238             break;
239         }
240         q++; p++; nb++;                 /* copy newline too */
241         size -= nb;
242         if (*q == '\r')                 /* XXX avoid \r madness */
243             *q = '\n';
244     } while (size > 0);
245     return (nread > 0 ? buf : NULL);
246 }
247
248 /**
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)
254  */
255 static const char *
256 matchchar(const char * p, char pl, char pr)
257 {
258     int lvl = 0;
259     char c;
260
261     while ((c = *p++) != '\0') {
262         if (c == '\\') {                /* Ignore escaped chars */
263             p++;
264             continue;
265         }
266         if (c == pr) {
267             if (--lvl <= 0)     return --p;
268         } else if (c == pl)
269             lvl++;
270     }
271     return (const char *)NULL;
272 }
273
274 /**
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
279  */
280 static void
281 printMacro(MacroBuf mb, const char * s, const char * se)
282 {
283     const char *senl;
284
285     if (s >= se) {      /* XXX just in case */
286         fprintf(stderr, _("%3d>%*s(empty)\n"), mb->depth,
287                 (2 * mb->depth + 1), "");
288         return;
289     }
290
291     if (s[-1] == '{')
292         s--;
293
294     /* Print only to first end-of-line (or end-of-string). */
295     for (senl = se; *senl && !iseol(*senl); senl++)
296         {};
297
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");
304 }
305
306 /**
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
311  */
312 static void
313 printExpansion(MacroBuf mb, const char * t, const char * te)
314 {
315     if (!(te > t)) {
316         rpmlog(RPMLOG_DEBUG, _("%3d<%*s(empty)\n"), mb->depth, (2 * mb->depth + 1), "");
317         return;
318     }
319
320     /* Shorten output which contains newlines */
321     while (te > t && iseol(te[-1]))
322         te--;
323     if (mb->depth > 0) {
324         const char *tenl;
325
326         /* Skip to last line of expansion */
327         while ((tenl = strchr(t, '\n')) && tenl < te)
328             t = ++tenl;
329
330     }
331
332     rpmlog(RPMLOG_DEBUG,"%3d<%*s", mb->depth, (2 * mb->depth + 1), "");
333     if (te > t)
334         rpmlog(RPMLOG_DEBUG, "%.*s", (int)(te - t), t);
335     rpmlog(RPMLOG_DEBUG, "\n");
336 }
337
338 #define SKIPBLANK(_s, _c)       \
339         while (((_c) = *(_s)) && isblank(_c)) \
340                 (_s)++;         \
341
342 #define SKIPNONBLANK(_s, _c)    \
343         while (((_c) = *(_s)) && !(isblank(_c) || iseol(_c))) \
344                 (_s)++;         \
345
346 #define COPYNAME(_ne, _s, _c)   \
347     {   SKIPBLANK(_s,_c);       \
348         while (((_c) = *(_s)) && (risalnum(_c) || (_c) == '_')) \
349                 *(_ne)++ = *(_s)++; \
350         *(_ne) = '\0';          \
351     }
352
353 #define COPYOPTS(_oe, _s, _c)   \
354     { \
355         while (((_c) = *(_s)) && (_c) != ')') \
356                 *(_oe)++ = *(_s)++; \
357         *(_oe) = '\0';          \
358     }
359
360 /**
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
367  */
368 static int
369 expandThis(MacroBuf mb, const char * src, size_t slen, char **target)
370 {
371     struct MacroBuf_s umb;
372
373     /* Copy other state from "parent", but we want a buffer of our own */
374     umb = *mb;
375     umb.buf = NULL;
376     umb.error = 0;
377     /* In case of error, flag it in the "parent"... */
378     if (expandMacro(&umb, src, slen))
379         mb->error = 1;
380     *target = umb.buf;
381
382     /* ...but return code for this operation specifically */
383     return umb.error;
384 }
385
386 static void mbAppend(MacroBuf mb, char c)
387 {
388     if (mb->nb < 1) {
389         mb->buf = xrealloc(mb->buf, mb->tpos + MACROBUFSIZ + 1);
390         mb->nb += MACROBUFSIZ;
391     }
392     mb->buf[mb->tpos++] = c;
393     mb->buf[mb->tpos] = '\0';
394     mb->nb--;
395 }
396
397 #ifdef WITH_LUA
398 static void mbAppendStr(MacroBuf mb, const char *str)
399 {
400     size_t len = strlen(str);
401     if (len > mb->nb) {
402         mb->buf = xrealloc(mb->buf, mb->tpos + mb->nb + MACROBUFSIZ + len + 1);
403         mb->nb += MACROBUFSIZ + len;
404     }
405     memcpy(mb->buf+mb->tpos, str, len + 1);
406     mb->tpos += len;
407     mb->nb -= len;
408 }
409 #endif
410
411 /**
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
416  */
417 static void
418 doShellEscape(MacroBuf mb, const char * cmd, size_t clen)
419 {
420     char *buf = NULL;
421     FILE *shf;
422     int c;
423
424     if (expandThis(mb, cmd, clen, &buf))
425         goto exit;
426
427     if ((shf = popen(buf, "r")) == NULL) {
428         mb->error = 1;
429         goto exit;
430     }
431
432     size_t tpos = mb->tpos;
433     while ((c = fgetc(shf)) != EOF) {
434         mbAppend(mb, c);
435     }
436     (void) pclose(shf);
437
438     /* Delete trailing \r \n */
439     while (mb->tpos > tpos && iseol(mb->buf[mb->tpos-1])) {
440         mb->buf[--mb->tpos] = '\0';
441         mb->nb++;
442     }
443
444 exit:
445     _free(buf);
446 }
447
448 /**
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
456  */
457 static const char *
458 doDefine(MacroBuf mb, const char * se, size_t slen, int level, int expandbody)
459 {
460     const char *s = se;
461     char *buf = xmalloc(slen + 3); /* Some leeway for termination issues... */
462     char *n = buf, *ne = n;
463     char *o = NULL, *oe;
464     char *b, *be, *ebody = NULL;
465     int c;
466     int oc = ')';
467     const char *sbody; /* as-is body start */
468     int rc = 1; /* assume failure */
469
470     /* Copy name */
471     COPYNAME(ne, s, c);
472
473     /* Copy opts (if present) */
474     oe = ne + 1;
475     if (*s == '(') {
476         s++;    /* skip ( */
477         /* Options must be terminated with ')' */
478         if (strchr(s, ')')) {
479             o = oe;
480             COPYOPTS(oe, s, oc);
481             s++;        /* skip ) */
482         } else {
483             rpmlog(RPMLOG_ERR, _("Macro %%%s has unterminated opts\n"), n);
484             goto exit;
485         }
486     }
487
488     /* Copy body, skipping over escaped newlines */
489     b = be = oe + 1;
490     sbody = s;
491     SKIPBLANK(s, c);
492     if (c == '{') {     /* XXX permit silent {...} grouping */
493         if ((se = matchchar(s, c, '}')) == NULL) {
494             rpmlog(RPMLOG_ERR,
495                 _("Macro %%%s has unterminated body\n"), n);
496             se = s;     /* XXX W2DO? */
497             goto exit;
498         }
499         s++;    /* XXX skip { */
500         strncpy(b, s, (se - s));
501         b[se - s] = '\0';
502         be += strlen(b);
503         se++;   /* XXX skip } */
504         s = se; /* move scan forward */
505     } else {    /* otherwise free-field */
506         int bc = 0, pc = 0;
507         while (*s && (bc || pc || !iseol(*s))) {
508             switch (*s) {
509                 case '\\':
510                     switch (*(s+1)) {
511                         case '\0': break;
512                         default: s++; break;
513                     }
514                     break;
515                 case '%':
516                     switch (*(s+1)) {
517                         case '{': *be++ = *s++; bc++; break;
518                         case '(': *be++ = *s++; pc++; break;
519                         case '%': *be++ = *s++; break;
520                     }
521                     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;
526             }
527             *be++ = *s++;
528         }
529         *be = '\0';
530
531         if (bc || pc) {
532             rpmlog(RPMLOG_ERR,
533                 _("Macro %%%s has unterminated body\n"), n);
534             se = s;     /* XXX W2DO? */
535             goto exit;
536         }
537
538         /* Trim trailing blanks/newlines */
539         while (--be >= b && (c = *be) && (isblank(c) || iseol(c)))
540             {};
541         *(++be) = '\0'; /* one too far */
542     }
543
544     /* Move scan over body */
545     while (iseol(*s))
546         s++;
547     se = s;
548
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");
553         goto exit;
554     }
555
556     if ((be - b) < 1) {
557         rpmlog(RPMLOG_ERR, _("Macro %%%s has empty body\n"), n);
558         goto exit;
559     }
560
561     if (!isblank(*sbody) && !(*sbody == '\\' && iseol(sbody[1])))
562         rpmlog(RPMLOG_WARNING, _("Macro %%%s needs whitespace before body\n"), n);
563
564     if (expandbody) {
565         if (expandThis(mb, b, 0, &ebody)) {
566             rpmlog(RPMLOG_ERR, _("Macro %%%s failed to expand\n"), n);
567             goto exit;
568         }
569         b = ebody;
570     }
571
572     pushMacro(mb->mc, n, o, b, level, ME_NONE);
573     rc = 0;
574
575 exit:
576     if (rc)
577         mb->error = 1;
578     _free(buf);
579     _free(ebody);
580     return se;
581 }
582
583 /**
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
589  */
590 static const char *
591 doUndefine(MacroBuf mb, const char * se, size_t slen)
592 {
593     const char *s = se;
594     char *buf = xmalloc(slen + 1);
595     char *n = buf, *ne = n;
596     int c;
597
598     COPYNAME(ne, s, c);
599
600     /* Move scan over body */
601     while (iseol(*s))
602         s++;
603     se = s;
604
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);
608         mb->error = 1;
609         goto exit;
610     }
611
612     popMacro(mb->mc, n);
613
614 exit:
615     _free(buf);
616     return se;
617 }
618
619 /**
620  * Free parsed arguments for parameterized macro.
621  * @param mb            macro expansion state
622  */
623 static void
624 freeArgs(MacroBuf mb)
625 {
626     rpmMacroContext mc = mb->mc;
627
628     /* Delete dynamic macro definitions */
629     for (int i = 0; i < mc->n; i++) {
630         rpmMacroEntry me = mc->tab[i];
631         assert(me);
632         if (me->level < mb->level)
633             continue;
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"),
638                         me->name);
639             /* Only whine once */
640             me->flags |= ME_USED;
641         }
642
643         /* compensate if the slot is to go away */
644         if (me->prev == NULL)
645             i--;
646         popMacro(mc, me->name);
647     }
648     mb->level--;
649 }
650
651 static void splitQuoted(ARGV_t *av, const char * str, const char * seps)
652 {
653     const int qchar = 0x1f; /* ASCII unit separator */
654     const char *s = str;
655     const char *start = str;
656     int quoted = 0;
657
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 */
662             if (slen > 0) {
663                 char *d, arg[slen + 1];
664                 const char *t;
665                 for (d = arg, t = start; t - start < slen; t++) {
666                     if (*t == qchar)
667                         continue;
668                     *d++ = *t;
669                 }
670                 arg[d - arg] = '\0';
671                 argvAdd(av, arg);
672             }
673             start = s + 1;
674         }
675         if (*s == qchar)
676             quoted = !quoted;
677         else if (*s == '\0')
678             start = NULL;
679         s++;
680     }
681 }
682
683 /**
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
691  */
692 static const char *
693 grabArgs(MacroBuf mb, const rpmMacroEntry me, const char * se,
694                 const char * lastc)
695 {
696     const char *cont = NULL;
697     const char *opts;
698     char *args = NULL;
699     ARGV_t argv = NULL;
700     int argc = 0;
701     int c;
702
703     /* 
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. 
707      */
708     argvAdd(&argv, me->name);
709     if (lastc) {
710         int oescape = mb->escape;
711         char *s = NULL;
712
713         /* Expand possible macros in arguments */
714         mb->escape = 1;
715         expandThis(mb, se, lastc-se, &s);
716         mb->escape = oescape;
717
718         splitQuoted(&argv, s, " \t");
719         free(s);
720
721         cont = ((*lastc == '\0' || *lastc == '\n') && *(lastc-1) != '\\') ?
722                lastc : lastc + 1;
723     }
724
725     /* Bump call depth on entry before first macro define */
726     mb->level++;
727
728     /* Setup macro name as %0 */
729     pushMacro(mb->mc, "0", NULL, me->name, mb->level, ME_AUTO);
730
731     /*
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 %{**} ...
738     */
739     args = argvJoin(argv + 1, " ");
740     pushMacro(mb->mc, "**", NULL, args, mb->level, ME_AUTO);
741     free(args);
742
743     /*
744      * POSIX states optind must be 1 before any call but glibc uses 0
745      * to (re)initialize getopt structures, eww.
746      */
747 #ifdef __GLIBC__
748     optind = 0;
749 #else
750     optind = 1;
751 #endif
752
753     opts = me->opts;
754     argc = argvCount(argv);
755
756     /* Define option macros. */
757     while ((c = getopt(argc, argv, opts)) != -1)
758     {
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);
763             mb->error = 1;
764             goto exit;
765         }
766
767         rasprintf(&name, "-%c", c);
768         if (optarg) {
769             rasprintf(&body, "-%c %s", c, optarg);
770         } else {
771             rasprintf(&body, "-%c", c);
772         }
773         pushMacro(mb->mc, name, NULL, body, mb->level, ME_AUTO);
774         free(name);
775         free(body);
776
777         if (optarg) {
778             rasprintf(&name, "-%c*", c);
779             pushMacro(mb->mc, name, NULL, optarg, mb->level, ME_AUTO);
780             free(name);
781         }
782     }
783
784     /* Add argument count (remaining non-option items) as macro. */
785     {   char *ac = NULL;
786         rasprintf(&ac, "%d", (argc - optind));
787         pushMacro(mb->mc, "#", NULL, ac, mb->level, ME_AUTO);
788         free(ac);
789     }
790
791     /* Add macro for each argument */
792     if (argc - optind) {
793         for (c = optind; c < argc; c++) {
794             char *name = NULL;
795             rasprintf(&name, "%d", (c - optind + 1));
796             pushMacro(mb->mc, name, NULL, argv[c], mb->level, ME_AUTO);
797             free(name);
798         }
799     }
800
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);
804     free(args);
805
806 exit:
807     argvFree(argv);
808     return cont;
809 }
810
811 /**
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
817  */
818 static void
819 doOutput(MacroBuf mb, int loglevel, const char * msg, size_t msglen)
820 {
821     char *buf = NULL;
822
823     (void) expandThis(mb, msg, msglen, &buf);
824     rpmlog(loglevel, "%s\n", buf);
825     _free(buf);
826 }
827
828 static void doLua(MacroBuf mb, const char * f, size_t fn, const char * g, size_t gn)
829 {
830 #ifdef WITH_LUA
831     rpmlua lua = NULL; /* Global state. */
832     char *scriptbuf = xmalloc(gn + 1);
833     char *printbuf;
834     rpmMacroContext mc = mb->mc;
835     int odepth = mc->depth;
836     int olevel = mc->level;
837
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)
845         mb->error = 1;
846     mc->depth = odepth;
847     mc->level = olevel;
848     printbuf = rpmluaPopPrintBuffer(lua);
849     if (printbuf) {
850         mbAppendStr(mb, printbuf);
851         free(printbuf);
852     }
853     free(scriptbuf);
854 #else
855     rpmlog(RPMLOG_ERR, _("<lua> scriptlet support not built in\n"));
856     mb->error = 1;
857 #endif
858 }
859
860 /**
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
868  */
869 static void
870 doFoo(MacroBuf mb, int negate, const char * f, size_t fn,
871                 const char * g, size_t gn)
872 {
873     char *buf = NULL;
874     char *b = NULL, *be;
875     int c;
876     int verbose = (rpmIsVerbose() != 0);
877     int expand = (g != NULL && gn > 0);
878
879     /* Don't expand %{verbose:...} argument on false condition */
880     if (STREQ("verbose", f, fn) && (verbose == negate))
881         expand = 0;
882
883     if (expand) {
884         (void) expandThis(mb, g, gn, &buf);
885     } else {
886         buf = xmalloc(MACROBUFSIZ + fn + gn);
887         buf[0] = '\0';
888     }
889     if (STREQ("basename", f, fn)) {
890         if ((b = strrchr(buf, '/')) == NULL)
891             b = buf;
892         else
893             b++;
894     } else if (STREQ("dirname", f, fn)) {
895         if ((b = strrchr(buf, '/')) != NULL)
896             *b = '\0';
897         b = buf;
898     } else if (STREQ("shrink", f, fn)) {
899         /*
900          * shrink body by removing all leading and trailing whitespaces and
901          * reducing intermediate whitespaces to a single space character.
902          */
903         size_t i = 0, j = 0;
904         size_t buflen = strlen(buf);
905         int was_space = 0;
906         while (i < buflen) {
907             if (risspace(buf[i])) {
908                 was_space = 1;
909                 i++;
910                 continue;
911             } else if (was_space) {
912                 was_space = 0;
913                 if (j > 0) /* remove leading blanks at all */
914                     buf[j++] = ' ';
915             }
916             buf[j++] = buf[i++];
917         }
918         buf[j] = '\0';
919         b = buf;
920     } else if (STREQ("quote", f, fn)) {
921         char *quoted = NULL;
922         rasprintf(&quoted, "%c%s%c", 0x1f, buf, 0x1f);
923         free(buf);
924         b = buf = quoted;
925     } else if (STREQ("suffix", f, fn)) {
926         if ((b = strrchr(buf, '.')) != NULL)
927             b++;
928     } else if (STREQ("expand", f, fn) || STREQ("verbose", f, fn)) {
929         b = buf;
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);)
936             b++;
937         for (be = b; (c = *be) && !isblank(c);)
938             be++;
939         *be++ = '\0';
940         (void) rpmFileIsCompressed(b, &compressed);
941         switch (compressed) {
942         default:
943         case COMPRESSED_NOT:
944             sprintf(be, "%%__cat %s", b);
945             break;
946         case COMPRESSED_OTHER:
947             sprintf(be, "%%__gzip -dc %s", b);
948             break;
949         case COMPRESSED_BZIP2:
950             sprintf(be, "%%__bzip2 -dc %s", b);
951             break;
952         case COMPRESSED_ZIP:
953             sprintf(be, "%%__unzip %s", b);
954             break;
955         case COMPRESSED_LZMA:
956         case COMPRESSED_XZ:
957             sprintf(be, "%%__xz -dc %s", b);
958             break;
959         case COMPRESSED_LZIP:
960             sprintf(be, "%%__lzip -dc %s", b);
961             break;
962         case COMPRESSED_LRZIP:
963             sprintf(be, "%%__lrzip -dqo- %s", b);
964             break;
965         case COMPRESSED_7ZIP:
966             sprintf(be, "%%__7zip x %s", b);
967             break;
968         case COMPRESSED_ZSTD:
969             sprintf(be, "%%__zstd -dc %s", b);
970             break;
971         }
972         b = be;
973     } else if (STREQ("getenv", f, fn)) {
974         b = getenv(buf);
975     } else if (STREQ("getconfdir", f, fn)) {
976         sprintf(buf, "%s", rpmConfigDir());
977         b = buf;
978     } else if (STREQ("S", f, fn)) {
979         for (b = buf; (c = *b) && risdigit(c);)
980             b++;
981         if (!c) {       /* digit index */
982             b++;
983             sprintf(b, "%%SOURCE%s", buf);
984         } else
985             b = buf;
986     } else if (STREQ("P", f, fn)) {
987         for (b = buf; (c = *b) && risdigit(c);)
988             b++;
989         if (!c) {       /* digit index */
990             b++;
991             sprintf(b, "%%PATCH%s", buf);
992         } else
993                         b = buf;
994     } else if (STREQ("F", f, fn)) {
995         b = buf + strlen(buf) + 1;
996         sprintf(b, "file%s.file", buf);
997     }
998
999     if (b) {
1000         (void) expandMacro(mb, b, 0);
1001     }
1002     free(buf);
1003 }
1004
1005 /**
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
1011  */
1012 static int
1013 expandMacro(MacroBuf mb, const char *src, size_t slen)
1014 {
1015     rpmMacroEntry *mep;
1016     rpmMacroEntry me;
1017     const char *s = src, *se;
1018     const char *f, *fe;
1019     const char *g, *ge;
1020     size_t fn, gn, tpos;
1021     int c;
1022     int negate;
1023     const char * lastc;
1024     int chkexist;
1025     char *source = NULL;
1026     int store_macro_trace;
1027     int store_expand_trace;
1028
1029     /*
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.
1034      */
1035     if (!slen)
1036         slen = strlen(src);
1037     source = xmalloc(slen + 1);
1038     strncpy(source, src, slen);
1039     source[slen] = '\0';
1040     s = source;
1041
1042     if (mb->buf == NULL) {
1043         size_t blen = MACROBUFSIZ + slen;
1044         mb->buf = xmalloc(blen + 1);
1045         mb->buf[0] = '\0';
1046         mb->tpos = 0;
1047         mb->nb = blen;
1048     }
1049     tpos = mb->tpos; /* save expansion pointer for printExpand */
1050     store_macro_trace = mb->macro_trace;
1051     store_expand_trace = mb->expand_trace;
1052
1053     if (++mb->depth > max_macro_depth) {
1054         rpmlog(RPMLOG_ERR,
1055                 _("Too many levels of recursion in macro expansion. It is likely caused by recursive macro declaration.\n"));
1056         mb->depth--;
1057         mb->expand_trace = 1;
1058         mb->error = 1;
1059         goto exit;
1060     }
1061
1062     while (mb->error == 0 && (c = *s) != '\0') {
1063         s++;
1064         /* Copy text until next macro */
1065         switch (c) {
1066         case '%':
1067             if (*s) {   /* Ensure not end-of-string. */
1068                 if (*s != '%')
1069                     break;
1070                 s++;    /* skip first % in %% */
1071                 if (mb->escape)
1072                     mbAppend(mb, c);
1073             }
1074         default:
1075             mbAppend(mb, c);
1076             continue;
1077             break;
1078         }
1079
1080         /* Expand next macro */
1081         f = fe = NULL;
1082         g = ge = NULL;
1083         if (mb->depth > 1)      /* XXX full expansion for outermost level */
1084             tpos = mb->tpos;    /* save expansion pointer for printExpand */
1085         negate = 0;
1086         lastc = NULL;
1087         chkexist = 0;
1088         switch ((c = *s)) {
1089         default:                /* %name substitution */
1090             while (*s != '\0' && strchr("!?", *s) != NULL) {
1091                 switch (*s++) {
1092                     case '!':
1093                         negate = ((negate + 1) % 2);
1094                         break;
1095                     case '?':
1096                         chkexist++;
1097                         break;
1098                 }
1099             }
1100             f = se = s;
1101             if (*se == '-')
1102                 se++;
1103             while ((c = *se) && (risalnum(c) || c == '_'))
1104                 se++;
1105             /* Recognize non-alnum macros too */
1106             switch (*se) {
1107             case '*':
1108                 se++;
1109                 if (*se == '*') se++;
1110                 break;
1111             case '#':
1112                 se++;
1113                 break;
1114             default:
1115                 break;
1116             }
1117             fe = se;
1118             /* For "%name " macros ... */
1119             if ((c = *fe) && isblank(c))
1120                 if ((lastc = strchr(fe,'\n')) == NULL)
1121                     lastc = strchr(fe, '\0');
1122             break;
1123         case '(':               /* %(...) shell escape */
1124             if ((se = matchchar(s, c, ')')) == NULL) {
1125                 rpmlog(RPMLOG_ERR, _("Unterminated %c: %s\n"), (char)c, s);
1126                 mb->error = 1;
1127                 continue;
1128             }
1129             if (mb->macro_trace)
1130                 printMacro(mb, s, se+1);
1131
1132             s++;        /* skip ( */
1133             doShellEscape(mb, s, (se - s));
1134             se++;       /* skip ) */
1135
1136             s = se;
1137             continue;
1138             break;
1139         case '{':               /* %{...}/%{...:...} substitution */
1140             if ((se = matchchar(s, c, '}')) == NULL) {
1141                 rpmlog(RPMLOG_ERR, _("Unterminated %c: %s\n"), (char)c, s);
1142                 mb->error = 1;
1143                 continue;
1144             }
1145             f = s+1;/* skip { */
1146             se++;       /* skip } */
1147             while (strchr("!?", *f) != NULL) {
1148                 switch (*f++) {
1149                 case '!':
1150                     negate = ((negate + 1) % 2);
1151                     break;
1152                 case '?':
1153                     chkexist++;
1154                     break;
1155                 }
1156             }
1157             for (fe = f; (c = *fe) && !strchr(" :}", c);)
1158                 fe++;
1159             switch (c) {
1160             case ':':
1161                 g = fe + 1;
1162                 ge = se - 1;
1163                 break;
1164             case ' ':
1165                 lastc = se-1;
1166                 break;
1167             default:
1168                 break;
1169             }
1170             break;
1171         }
1172
1173         /* XXX Everything below expects fe > f */
1174         fn = (fe - f);
1175         gn = (ge - g);
1176         if ((fe - f) <= 0) {
1177             /* XXX Process % in unknown context */
1178             c = '%';    /* XXX only need to save % */
1179             mbAppend(mb, c);
1180 #if 0
1181             rpmlog(RPMLOG_ERR,
1182                     _("A %% is followed by an unparseable macro\n"));
1183 #endif
1184             s = se;
1185             continue;
1186         }
1187
1188         if (mb->macro_trace)
1189             printMacro(mb, s, se);
1190
1191         /* Expand builtin macros */
1192         if (STREQ("load", f, fn)) {
1193             char *arg = NULL;
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);
1198                     mb->error = 1;
1199                 }
1200             }
1201             free(arg);
1202             s = se;
1203             continue;
1204         }
1205         if (STREQ("global", f, fn)) {
1206             s = doDefine(mb, se, slen - (se - s), RMIL_GLOBAL, 1);
1207             continue;
1208         }
1209         if (STREQ("define", f, fn)) {
1210             s = doDefine(mb, se, slen - (se - s), mb->level, 0);
1211             continue;
1212         }
1213         if (STREQ("undefine", f, fn)) {
1214             s = doUndefine(mb, se, slen - (se - s));
1215             continue;
1216         }
1217
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;
1224                 mb->error = 1;
1225             } else if (STREQ("warn", f, fn)) {
1226                 loglevel = RPMLOG_WARNING;
1227             }
1228             if (g != NULL && g < ge)
1229                 doOutput(mb, loglevel, g, gn);
1230             else
1231                 doOutput(mb, loglevel, "", 0);
1232             s = se;
1233             continue;
1234         }
1235
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;
1242             }
1243             s = se;
1244             continue;
1245         }
1246
1247         if (STREQ("dump", f, fn)) {
1248             rpmDumpMacroTable(mb->mc, NULL);
1249             while (iseol(*se))
1250                 se++;
1251             s = se;
1252             continue;
1253         }
1254
1255         if (STREQ("lua", f, fn)) {
1256             doLua(mb, f, fn, g, gn);
1257             s = se;
1258             continue;
1259         }
1260
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) ||
1276             STREQ("F", f, fn))
1277         {
1278             /* FIX: verbose may be set */
1279             doFoo(mb, negate, f, fn, g, gn);
1280             s = se;
1281             continue;
1282         }
1283
1284         /* Expand defined macros */
1285         mep = findEntry(mb->mc, f, fn, NULL);
1286         me = (mep ? *mep : NULL);
1287
1288         if (me) {
1289             if ((me->flags & ME_AUTO) && mb->level > me->level) {
1290                 /* Ignore out-of-scope automatic macros */
1291                 me = NULL;
1292             } else {
1293                 /* If we looked up a macro, consider it used */
1294                 me->flags |= ME_USED;
1295             }
1296         }
1297
1298         /* XXX Special processing for flags */
1299         if (*f == '-') {
1300             if ((me == NULL && !negate) ||      /* Without -f, skip %{-f...} */
1301                     (me != NULL && negate)) {   /* With -f, skip %{!-f...} */
1302                 s = se;
1303                 continue;
1304             }
1305
1306             if (g && g < ge) {          /* Expand X in %{-f:X} */
1307                 expandMacro(mb, g, gn);
1308             } else
1309                 if (me && me->body && *me->body) {/* Expand %{-f}/%{-f*} */
1310                     expandMacro(mb, me->body, 0);
1311                 }
1312             s = se;
1313             continue;
1314         }
1315
1316         /* XXX Special processing for macro existence */
1317         if (chkexist) {
1318             if ((me == NULL && !negate) ||      /* Without -f, skip %{?f...} */
1319                     (me != NULL && negate)) {   /* With -f, skip %{!?f...} */
1320                 s = se;
1321                 continue;
1322             }
1323             if (g && g < ge) {          /* Expand X in %{?f:X} */
1324                 expandMacro(mb, g, gn);
1325             } else
1326                 if (me && me->body && *me->body) { /* Expand %{?f}/%{?f*} */
1327                     expandMacro(mb, me->body, 0);
1328                 }
1329             s = se;
1330             continue;
1331         }
1332         
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 % */
1336             mbAppend(mb, c);
1337             continue;
1338         }
1339
1340         /* Setup args for "%name " macros with opts */
1341         if (me && me->opts != NULL) {
1342             const char *xe = grabArgs(mb, me, fe, lastc);
1343             if (xe != NULL)
1344                 se = xe;
1345         }
1346
1347         /* Recursively expand body of macro */
1348         if (me->body && *me->body) {
1349             expandMacro(mb, me->body, 0);
1350         }
1351
1352         /* Free args for "%name " macros with opts */
1353         if (me->opts != NULL)
1354             freeArgs(mb);
1355
1356         s = se;
1357     }
1358
1359     mb->buf[mb->tpos] = '\0';
1360     mb->depth--;
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;
1365 exit:
1366     _free(source);
1367     return mb->error;
1368 }
1369
1370
1371 /* =============================================================== */
1372
1373 static int doExpandMacros(rpmMacroContext mc, const char *src, int flags,
1374                         char **target)
1375 {
1376     MacroBuf mb = xcalloc(1, sizeof(*mb));
1377     int rc = 0;
1378
1379     mb->buf = NULL;
1380     mb->depth = mc->depth;
1381     mb->level = mc->level;
1382     mb->macro_trace = print_macro_trace;
1383     mb->expand_trace = print_expand_trace;
1384     mb->mc = mc;
1385     mb->flags = flags;
1386
1387     rc = expandMacro(mb, src, 0);
1388
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);
1392
1393     _free(mb);
1394     return rc;
1395 }
1396
1397 static void pushMacro(rpmMacroContext mc,
1398         const char * n, const char * o, const char * b, int level, int flags)
1399 {
1400     /* new entry */
1401     rpmMacroEntry me;
1402     /* pointer into me */
1403     char *p;
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);
1408
1409     size_t pos;
1410     rpmMacroEntry *mep = findEntry(mc, n, 0, &pos);
1411     if (mep) {
1412         /* entry with shared name */
1413         me = xmalloc(mesize);
1414         /* copy body */
1415         me->body = p = me->arena;
1416         if (blen)
1417             memcpy(p, b, blen + 1);
1418         else
1419             *p = '\0';
1420         p += blen + 1;
1421         /* set name */
1422         me->name = (*mep)->name;
1423     }
1424     else {
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));
1431         mc->n++;
1432         /* make slot */
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);
1438         /* copy body */
1439         me->body = p = me->arena;
1440         if (blen)
1441             memcpy(p, b, blen + 1);
1442         else
1443             *p = '\0';
1444         p += blen + 1;
1445         /* copy name */
1446         me->name = memcpy(p, n, nlen + 1);
1447         p += nlen + 1;
1448     }
1449
1450     /* copy options */
1451     if (olen)
1452         me->opts = memcpy(p, o, olen + 1);
1453     else
1454         me->opts = o ? "" : NULL;
1455     /* initialize */
1456     me->flags = flags;
1457     me->flags &= ~(ME_USED);
1458     me->level = level;
1459     /* push over previous definition */
1460     me->prev = *mep;
1461     *mep = me;
1462 }
1463
1464 static void popMacro(rpmMacroContext mc, const char * n)
1465 {
1466     size_t pos;
1467     rpmMacroEntry *mep = findEntry(mc, n, 0, &pos);
1468     if (mep == NULL)
1469         return;
1470     /* parting entry */
1471     rpmMacroEntry me = *mep;
1472     assert(me);
1473     /* detach/pop definition */
1474     mc->tab[pos] = me->prev;
1475     /* shrink macro table */
1476     if (me->prev == NULL) {
1477         mc->n--;
1478         /* move pos+ elements to the left */
1479         memmove(mc->tab + pos, mc->tab + pos + 1, sizeof(me) * (mc->n - pos));
1480         /* deallocate */
1481         if (mc->n == 0)
1482             mc->tab = _free(mc->tab);
1483     }
1484     /* comes in a single chunk */
1485     free(me);
1486 }
1487
1488 static int defineMacro(rpmMacroContext mc, const char * macro, int level)
1489 {
1490     MacroBuf mb = xcalloc(1, sizeof(*mb));
1491     int rc;
1492
1493     /* XXX just enough to get by */
1494     mb->mc = mc;
1495     (void) doDefine(mb, macro, strlen(macro), level, 0);
1496     rc = mb->error;
1497     _free(mb);
1498     return rc;
1499 }
1500
1501 static int loadMacroFile(rpmMacroContext mc, const char * fn)
1502 {
1503     FILE *fd = fopen(fn, "r");
1504     size_t blen = MACROBUFSIZ;
1505     char *buf = xmalloc(blen);
1506     int rc = -1;
1507
1508     if (fd == NULL)
1509         goto exit;
1510
1511     buf[0] = '\0';
1512     while (rdcl(buf, blen, fd) != NULL) {
1513         char c, *n;
1514
1515         n = buf;
1516         SKIPBLANK(n, c);
1517
1518         if (c != '%')
1519                 continue;
1520         n++;    /* skip % */
1521         rc = defineMacro(mc, n, RMIL_MACROFILES);
1522     }
1523
1524     rc = fclose(fd);
1525
1526 exit:
1527     _free(buf);
1528     return rc;
1529 }
1530
1531 static void copyMacros(rpmMacroContext src, rpmMacroContext dst, int level)
1532 {
1533     for (int i = 0; i < src->n; i++) {
1534         rpmMacroEntry me = src->tab[i];
1535         assert(me);
1536         pushMacro(dst, me->name, me->opts, me->body, level, me->flags);
1537     }
1538 }
1539
1540 /* External interfaces */
1541
1542 int rpmExpandMacros(rpmMacroContext mc, const char * sbuf, char ** obuf, int flags)
1543 {
1544     char *target = NULL;
1545     int rc;
1546
1547     mc = rpmmctxAcquire(mc);
1548     rc = doExpandMacros(mc, sbuf, flags, &target);
1549     rpmmctxRelease(mc);
1550
1551     if (rc) {
1552         free(target);
1553         return -1;
1554     } else {
1555         *obuf = target;
1556         return 1;
1557     }
1558 }
1559
1560 void
1561 rpmDumpMacroTable(rpmMacroContext mc, FILE * fp)
1562 {
1563     mc = rpmmctxAcquire(mc);
1564     if (fp == NULL) fp = stderr;
1565     
1566     fprintf(fp, "========================\n");
1567     for (int i = 0; i < mc->n; i++) {
1568         rpmMacroEntry me = mc->tab[i];
1569         assert(me);
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);
1576         fprintf(fp, "\n");
1577     }
1578     fprintf(fp, _("======================== active %d empty %d\n"),
1579                 mc->n, 0);
1580     rpmmctxRelease(mc);
1581 }
1582
1583 int rpmPushMacro(rpmMacroContext mc,
1584               const char * n, const char * o, const char * b, int level)
1585 {
1586     mc = rpmmctxAcquire(mc);
1587     pushMacro(mc, n, o, b, level, ME_NONE);
1588     rpmmctxRelease(mc);
1589     return 0;
1590 }
1591
1592 int rpmPopMacro(rpmMacroContext mc, const char * n)
1593 {
1594     mc = rpmmctxAcquire(mc);
1595     popMacro(mc, n);
1596     rpmmctxRelease(mc);
1597     return 0;
1598 }
1599
1600 int
1601 rpmDefineMacro(rpmMacroContext mc, const char * macro, int level)
1602 {
1603     int rc;
1604     mc = rpmmctxAcquire(mc);
1605     rc = defineMacro(mc, macro, level);
1606     rpmmctxRelease(mc);
1607     return rc;
1608 }
1609
1610 void
1611 rpmLoadMacros(rpmMacroContext mc, int level)
1612 {
1613     rpmMacroContext gmc;
1614     if (mc == NULL || mc == rpmGlobalMacroContext)
1615         return;
1616
1617     gmc = rpmmctxAcquire(NULL);
1618     mc = rpmmctxAcquire(mc);
1619
1620     copyMacros(mc, gmc, level);
1621
1622     rpmmctxRelease(mc);
1623     rpmmctxRelease(gmc);
1624 }
1625
1626 int
1627 rpmLoadMacroFile(rpmMacroContext mc, const char * fn)
1628 {
1629     int rc;
1630
1631     mc = rpmmctxAcquire(mc);
1632     rc = loadMacroFile(mc, fn);
1633     rpmmctxRelease(mc);
1634
1635     return rc;
1636 }
1637
1638 void
1639 rpmInitMacros(rpmMacroContext mc, const char * macrofiles)
1640 {
1641     ARGV_t pattern, globs = NULL;
1642     rpmMacroContext climc;
1643
1644     if (macrofiles == NULL)
1645         return;
1646
1647     argvSplit(&globs, macrofiles, ":");
1648     mc = rpmmctxAcquire(mc);
1649     for (pattern = globs; *pattern; pattern++) {
1650         ARGV_t path, files = NULL;
1651     
1652         /* Glob expand the macro file path element, expanding ~ to $HOME. */
1653         if (rpmGlob(*pattern, NULL, &files) != 0) {
1654             continue;
1655         }
1656
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")) {
1662                 continue;
1663             }
1664             (void) loadMacroFile(mc, *path);
1665         }
1666         argvFree(files);
1667     }
1668     argvFree(globs);
1669
1670     /* Reload cmdline macros */
1671     climc = rpmmctxAcquire(rpmCLIMacroContext);
1672     copyMacros(climc, mc, RMIL_CMDLINE);
1673     rpmmctxRelease(climc);
1674
1675     rpmmctxRelease(mc);
1676 }
1677
1678 void
1679 rpmFreeMacros(rpmMacroContext mc)
1680 {
1681     mc = rpmmctxAcquire(mc);
1682     while (mc->n > 0) {
1683         /* remove from the end to avoid memmove */
1684         rpmMacroEntry me = mc->tab[mc->n - 1];
1685         popMacro(mc, me->name);
1686     }
1687     rpmmctxRelease(mc);
1688 }
1689
1690 char * 
1691 rpmExpand(const char *arg, ...)
1692 {
1693     size_t blen = 0;
1694     char *buf = NULL, *ret = NULL;
1695     char *pe;
1696     const char *s;
1697     va_list ap;
1698     rpmMacroContext mc;
1699
1700     if (arg == NULL) {
1701         ret = xstrdup("");
1702         goto exit;
1703     }
1704
1705     /* precalculate unexpanded size */
1706     va_start(ap, arg);
1707     for (s = arg; s != NULL; s = va_arg(ap, const char *))
1708         blen += strlen(s);
1709     va_end(ap);
1710
1711     buf = xmalloc(blen + 1);
1712     buf[0] = '\0';
1713
1714     va_start(ap, arg);
1715     for (pe = buf, s = arg; s != NULL; s = va_arg(ap, const char *))
1716         pe = stpcpy(pe, s);
1717     va_end(ap);
1718
1719     mc = rpmmctxAcquire(NULL);
1720     (void) doExpandMacros(mc, buf, 0, &ret);
1721     rpmmctxRelease(mc);
1722
1723     free(buf);
1724 exit:
1725     return ret;
1726 }
1727
1728 int
1729 rpmExpandNumeric(const char *arg)
1730 {
1731     char *val;
1732     int rc;
1733
1734     if (arg == NULL)
1735         return 0;
1736
1737     val = rpmExpand(arg, NULL);
1738     if (!(val && *val != '%'))
1739         rc = 0;
1740     else if (*val == 'Y' || *val == 'y')
1741         rc = 1;
1742     else if (*val == 'N' || *val == 'n')
1743         rc = 0;
1744     else {
1745         char *end;
1746         rc = strtol(val, &end, 0);
1747         if (!(end && *end == '\0'))
1748             rc = 0;
1749     }
1750     free(val);
1751
1752     return rc;
1753 }