Add librpm-tizen.spec file & Debian packaging.
[tools/librpm-tizen.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 #ifdef WITH_LUA
425     rpmlog(RPMLOG_WARNING, _("Refusing to run shell code: %s\n"), cmd);
426     mbAppendStr(mb, "UNEXPANDEDSHELLSCRIPT");
427 #else
428     if (expandThis(mb, cmd, clen, &buf))
429         goto exit;
430
431     if ((shf = popen(buf, "r")) == NULL) {
432         mb->error = 1;
433         goto exit;
434     }
435
436     size_t tpos = mb->tpos;
437     while ((c = fgetc(shf)) != EOF) {
438         mbAppend(mb, c);
439     }
440     (void) pclose(shf);
441
442     /* Delete trailing \r \n */
443     while (mb->tpos > tpos && iseol(mb->buf[mb->tpos-1])) {
444         mb->buf[--mb->tpos] = '\0';
445         mb->nb++;
446     }
447
448 exit:
449     _free(buf);
450 #endif
451 }
452
453 /**
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
461  */
462 static const char *
463 doDefine(MacroBuf mb, const char * se, size_t slen, int level, int expandbody)
464 {
465     const char *s = se;
466     char *buf = xmalloc(slen + 3); /* Some leeway for termination issues... */
467     char *n = buf, *ne = n;
468     char *o = NULL, *oe;
469     char *b, *be, *ebody = NULL;
470     int c;
471     int oc = ')';
472     const char *sbody; /* as-is body start */
473     int rc = 1; /* assume failure */
474
475     /* Copy name */
476     COPYNAME(ne, s, c);
477
478     /* Copy opts (if present) */
479     oe = ne + 1;
480     if (*s == '(') {
481         s++;    /* skip ( */
482         /* Options must be terminated with ')' */
483         if (strchr(s, ')')) {
484             o = oe;
485             COPYOPTS(oe, s, oc);
486             s++;        /* skip ) */
487         } else {
488             rpmlog(RPMLOG_ERR, _("Macro %%%s has unterminated opts\n"), n);
489             goto exit;
490         }
491     }
492
493     /* Copy body, skipping over escaped newlines */
494     b = be = oe + 1;
495     sbody = s;
496     SKIPBLANK(s, c);
497     if (c == '{') {     /* XXX permit silent {...} grouping */
498         if ((se = matchchar(s, c, '}')) == NULL) {
499             rpmlog(RPMLOG_ERR,
500                 _("Macro %%%s has unterminated body\n"), n);
501             se = s;     /* XXX W2DO? */
502             goto exit;
503         }
504         s++;    /* XXX skip { */
505         strncpy(b, s, (se - s));
506         b[se - s] = '\0';
507         be += strlen(b);
508         se++;   /* XXX skip } */
509         s = se; /* move scan forward */
510     } else {    /* otherwise free-field */
511         int bc = 0, pc = 0;
512         while (*s && (bc || pc || !iseol(*s))) {
513             switch (*s) {
514                 case '\\':
515                     switch (*(s+1)) {
516                         case '\0': break;
517                         default: s++; break;
518                     }
519                     break;
520                 case '%':
521                     switch (*(s+1)) {
522                         case '{': *be++ = *s++; bc++; break;
523                         case '(': *be++ = *s++; pc++; break;
524                         case '%': *be++ = *s++; break;
525                     }
526                     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;
531             }
532             *be++ = *s++;
533         }
534         *be = '\0';
535
536         if (bc || pc) {
537             rpmlog(RPMLOG_ERR,
538                 _("Macro %%%s has unterminated body\n"), n);
539             se = s;     /* XXX W2DO? */
540             goto exit;
541         }
542
543         /* Trim trailing blanks/newlines */
544         while (--be >= b && (c = *be) && (isblank(c) || iseol(c)))
545             {};
546         *(++be) = '\0'; /* one too far */
547     }
548
549     /* Move scan over body */
550     while (iseol(*s))
551         s++;
552     se = s;
553
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");
558         goto exit;
559     }
560
561     if ((be - b) < 1) {
562         rpmlog(RPMLOG_ERR, _("Macro %%%s has empty body\n"), n);
563         goto exit;
564     }
565
566     if (!isblank(*sbody) && !(*sbody == '\\' && iseol(sbody[1])))
567         rpmlog(RPMLOG_WARNING, _("Macro %%%s needs whitespace before body\n"), n);
568
569     if (expandbody) {
570         if (expandThis(mb, b, 0, &ebody)) {
571             rpmlog(RPMLOG_ERR, _("Macro %%%s failed to expand\n"), n);
572             goto exit;
573         }
574         b = ebody;
575     }
576
577     pushMacro(mb->mc, n, o, b, level, ME_NONE);
578     rc = 0;
579
580 exit:
581     if (rc)
582         mb->error = 1;
583     _free(buf);
584     _free(ebody);
585     return se;
586 }
587
588 /**
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
594  */
595 static const char *
596 doUndefine(MacroBuf mb, const char * se, size_t slen)
597 {
598     const char *s = se;
599     char *buf = xmalloc(slen + 1);
600     char *n = buf, *ne = n;
601     int c;
602
603     COPYNAME(ne, s, c);
604
605     /* Move scan over body */
606     while (iseol(*s))
607         s++;
608     se = s;
609
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);
613         mb->error = 1;
614         goto exit;
615     }
616
617     popMacro(mb->mc, n);
618
619 exit:
620     _free(buf);
621     return se;
622 }
623
624 /**
625  * Free parsed arguments for parameterized macro.
626  * @param mb            macro expansion state
627  */
628 static void
629 freeArgs(MacroBuf mb)
630 {
631     rpmMacroContext mc = mb->mc;
632
633     /* Delete dynamic macro definitions */
634     for (int i = 0; i < mc->n; i++) {
635         rpmMacroEntry me = mc->tab[i];
636         assert(me);
637         if (me->level < mb->level)
638             continue;
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"),
643                         me->name);
644             /* Only whine once */
645             me->flags |= ME_USED;
646         }
647
648         /* compensate if the slot is to go away */
649         if (me->prev == NULL)
650             i--;
651         popMacro(mc, me->name);
652     }
653     mb->level--;
654 }
655
656 static void splitQuoted(ARGV_t *av, const char * str, const char * seps)
657 {
658     const int qchar = 0x1f; /* ASCII unit separator */
659     const char *s = str;
660     const char *start = str;
661     int quoted = 0;
662
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 */
667             if (slen > 0) {
668                 char *d, arg[slen + 1];
669                 const char *t;
670                 for (d = arg, t = start; t - start < slen; t++) {
671                     if (*t == qchar)
672                         continue;
673                     *d++ = *t;
674                 }
675                 arg[d - arg] = '\0';
676                 argvAdd(av, arg);
677             }
678             start = s + 1;
679         }
680         if (*s == qchar)
681             quoted = !quoted;
682         else if (*s == '\0')
683             start = NULL;
684         s++;
685     }
686 }
687
688 /**
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
696  */
697 static const char *
698 grabArgs(MacroBuf mb, const rpmMacroEntry me, const char * se,
699                 const char * lastc)
700 {
701     const char *cont = NULL;
702     const char *opts;
703     char *args = NULL;
704     ARGV_t argv = NULL;
705     int argc = 0;
706     int c;
707
708     /* 
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. 
712      */
713     argvAdd(&argv, me->name);
714     if (lastc) {
715         int oescape = mb->escape;
716         char *s = NULL;
717
718         /* Expand possible macros in arguments */
719         mb->escape = 1;
720         expandThis(mb, se, lastc-se, &s);
721         mb->escape = oescape;
722
723         splitQuoted(&argv, s, " \t");
724         free(s);
725
726         cont = ((*lastc == '\0' || *lastc == '\n') && *(lastc-1) != '\\') ?
727                lastc : lastc + 1;
728     }
729
730     /* Bump call depth on entry before first macro define */
731     mb->level++;
732
733     /* Setup macro name as %0 */
734     pushMacro(mb->mc, "0", NULL, me->name, mb->level, ME_AUTO);
735
736     /*
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 %{**} ...
743     */
744     args = argvJoin(argv + 1, " ");
745     pushMacro(mb->mc, "**", NULL, args, mb->level, ME_AUTO);
746     free(args);
747
748     /*
749      * POSIX states optind must be 1 before any call but glibc uses 0
750      * to (re)initialize getopt structures, eww.
751      */
752 #ifdef __GLIBC__
753     optind = 0;
754 #else
755     optind = 1;
756 #endif
757
758     opts = me->opts;
759     argc = argvCount(argv);
760
761     /* Define option macros. */
762     while ((c = getopt(argc, argv, opts)) != -1)
763     {
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);
768             mb->error = 1;
769             goto exit;
770         }
771
772         rasprintf(&name, "-%c", c);
773         if (optarg) {
774             rasprintf(&body, "-%c %s", c, optarg);
775         } else {
776             rasprintf(&body, "-%c", c);
777         }
778         pushMacro(mb->mc, name, NULL, body, mb->level, ME_AUTO);
779         free(name);
780         free(body);
781
782         if (optarg) {
783             rasprintf(&name, "-%c*", c);
784             pushMacro(mb->mc, name, NULL, optarg, mb->level, ME_AUTO);
785             free(name);
786         }
787     }
788
789     /* Add argument count (remaining non-option items) as macro. */
790     {   char *ac = NULL;
791         rasprintf(&ac, "%d", (argc - optind));
792         pushMacro(mb->mc, "#", NULL, ac, mb->level, ME_AUTO);
793         free(ac);
794     }
795
796     /* Add macro for each argument */
797     if (argc - optind) {
798         for (c = optind; c < argc; c++) {
799             char *name = NULL;
800             rasprintf(&name, "%d", (c - optind + 1));
801             pushMacro(mb->mc, name, NULL, argv[c], mb->level, ME_AUTO);
802             free(name);
803         }
804     }
805
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);
809     free(args);
810
811 exit:
812     argvFree(argv);
813     return cont;
814 }
815
816 /**
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
822  */
823 static void
824 doOutput(MacroBuf mb, int loglevel, const char * msg, size_t msglen)
825 {
826     char *buf = NULL;
827
828     (void) expandThis(mb, msg, msglen, &buf);
829     rpmlog(loglevel, "%s\n", buf);
830     _free(buf);
831 }
832
833 static void doLua(MacroBuf mb, const char * f, size_t fn, const char * g, size_t gn)
834 {
835 #ifdef WITH_LUA
836     rpmlua lua = NULL; /* Global state. */
837     char *scriptbuf = xmalloc(gn + 1);
838     char *printbuf;
839     rpmMacroContext mc = mb->mc;
840     int odepth = mc->depth;
841     int olevel = mc->level;
842
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)
850         mb->error = 1;
851     mc->depth = odepth;
852     mc->level = olevel;
853     printbuf = rpmluaPopPrintBuffer(lua);
854     if (printbuf) {
855         mbAppendStr(mb, printbuf);
856         free(printbuf);
857     }
858     free(scriptbuf);
859 #else
860     rpmlog(RPMLOG_ERR, _("<lua> scriptlet support not built in\n"));
861     mb->error = 1;
862 #endif
863 }
864
865 /**
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
873  */
874 static void
875 doFoo(MacroBuf mb, int negate, const char * f, size_t fn,
876                 const char * g, size_t gn)
877 {
878     char *buf = NULL;
879     char *b = NULL, *be;
880     int c;
881     int verbose = (rpmIsVerbose() != 0);
882     int expand = (g != NULL && gn > 0);
883
884     /* Don't expand %{verbose:...} argument on false condition */
885     if (STREQ("verbose", f, fn) && (verbose == negate))
886         expand = 0;
887
888     if (expand) {
889         (void) expandThis(mb, g, gn, &buf);
890     } else {
891         buf = xmalloc(MACROBUFSIZ + fn + gn);
892         buf[0] = '\0';
893     }
894     if (STREQ("basename", f, fn)) {
895         if ((b = strrchr(buf, '/')) == NULL)
896             b = buf;
897         else
898             b++;
899     } else if (STREQ("dirname", f, fn)) {
900         if ((b = strrchr(buf, '/')) != NULL)
901             *b = '\0';
902         b = buf;
903     } else if (STREQ("shrink", f, fn)) {
904         /*
905          * shrink body by removing all leading and trailing whitespaces and
906          * reducing intermediate whitespaces to a single space character.
907          */
908         size_t i = 0, j = 0;
909         size_t buflen = strlen(buf);
910         int was_space = 0;
911         while (i < buflen) {
912             if (risspace(buf[i])) {
913                 was_space = 1;
914                 i++;
915                 continue;
916             } else if (was_space) {
917                 was_space = 0;
918                 if (j > 0) /* remove leading blanks at all */
919                     buf[j++] = ' ';
920             }
921             buf[j++] = buf[i++];
922         }
923         buf[j] = '\0';
924         b = buf;
925     } else if (STREQ("quote", f, fn)) {
926         char *quoted = NULL;
927         rasprintf(&quoted, "%c%s%c", 0x1f, buf, 0x1f);
928         free(buf);
929         b = buf = quoted;
930     } else if (STREQ("suffix", f, fn)) {
931         if ((b = strrchr(buf, '.')) != NULL)
932             b++;
933     } else if (STREQ("expand", f, fn) || STREQ("verbose", f, fn)) {
934         b = buf;
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);)
941             b++;
942         for (be = b; (c = *be) && !isblank(c);)
943             be++;
944         *be++ = '\0';
945         (void) rpmFileIsCompressed(b, &compressed);
946         switch (compressed) {
947         default:
948         case COMPRESSED_NOT:
949             sprintf(be, "%%__cat %s", b);
950             break;
951         case COMPRESSED_OTHER:
952             sprintf(be, "%%__gzip -dc %s", b);
953             break;
954         case COMPRESSED_BZIP2:
955             sprintf(be, "%%__bzip2 -dc %s", b);
956             break;
957         case COMPRESSED_ZIP:
958             sprintf(be, "%%__unzip %s", b);
959             break;
960         case COMPRESSED_LZMA:
961         case COMPRESSED_XZ:
962             sprintf(be, "%%__xz -dc %s", b);
963             break;
964         case COMPRESSED_LZIP:
965             sprintf(be, "%%__lzip -dc %s", b);
966             break;
967         case COMPRESSED_LRZIP:
968             sprintf(be, "%%__lrzip -dqo- %s", b);
969             break;
970         case COMPRESSED_7ZIP:
971             sprintf(be, "%%__7zip x %s", b);
972             break;
973         case COMPRESSED_ZSTD:
974             sprintf(be, "%%__zstd -dc %s", b);
975             break;
976         }
977         b = be;
978     } else if (STREQ("getenv", f, fn)) {
979         b = getenv(buf);
980     } else if (STREQ("getconfdir", f, fn)) {
981         sprintf(buf, "%s", rpmConfigDir());
982         b = buf;
983     } else if (STREQ("S", f, fn)) {
984         for (b = buf; (c = *b) && risdigit(c);)
985             b++;
986         if (!c) {       /* digit index */
987             b++;
988             sprintf(b, "%%SOURCE%s", buf);
989         } else
990             b = buf;
991     } else if (STREQ("P", f, fn)) {
992         for (b = buf; (c = *b) && risdigit(c);)
993             b++;
994         if (!c) {       /* digit index */
995             b++;
996             sprintf(b, "%%PATCH%s", buf);
997         } else
998                         b = buf;
999     } else if (STREQ("F", f, fn)) {
1000         b = buf + strlen(buf) + 1;
1001         sprintf(b, "file%s.file", buf);
1002     }
1003
1004     if (b) {
1005         (void) expandMacro(mb, b, 0);
1006     }
1007     free(buf);
1008 }
1009
1010 /**
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
1016  */
1017 static int
1018 expandMacro(MacroBuf mb, const char *src, size_t slen)
1019 {
1020     rpmMacroEntry *mep;
1021     rpmMacroEntry me;
1022     const char *s = src, *se;
1023     const char *f, *fe;
1024     const char *g, *ge;
1025     size_t fn, gn, tpos;
1026     int c;
1027     int negate;
1028     const char * lastc;
1029     int chkexist;
1030     char *source = NULL;
1031     int store_macro_trace;
1032     int store_expand_trace;
1033
1034     /*
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.
1039      */
1040     if (!slen)
1041         slen = strlen(src);
1042     source = xmalloc(slen + 1);
1043     strncpy(source, src, slen);
1044     source[slen] = '\0';
1045     s = source;
1046
1047     if (mb->buf == NULL) {
1048         size_t blen = MACROBUFSIZ + slen;
1049         mb->buf = xmalloc(blen + 1);
1050         mb->buf[0] = '\0';
1051         mb->tpos = 0;
1052         mb->nb = blen;
1053     }
1054     tpos = mb->tpos; /* save expansion pointer for printExpand */
1055     store_macro_trace = mb->macro_trace;
1056     store_expand_trace = mb->expand_trace;
1057
1058     if (++mb->depth > max_macro_depth) {
1059         rpmlog(RPMLOG_ERR,
1060                 _("Too many levels of recursion in macro expansion. It is likely caused by recursive macro declaration.\n"));
1061         mb->depth--;
1062         mb->expand_trace = 1;
1063         mb->error = 1;
1064         goto exit;
1065     }
1066
1067     while (mb->error == 0 && (c = *s) != '\0') {
1068         s++;
1069         /* Copy text until next macro */
1070         switch (c) {
1071         case '%':
1072             if (*s) {   /* Ensure not end-of-string. */
1073                 if (*s != '%')
1074                     break;
1075                 s++;    /* skip first % in %% */
1076                 if (mb->escape)
1077                     mbAppend(mb, c);
1078             }
1079         default:
1080             mbAppend(mb, c);
1081             continue;
1082             break;
1083         }
1084
1085         /* Expand next macro */
1086         f = fe = NULL;
1087         g = ge = NULL;
1088         if (mb->depth > 1)      /* XXX full expansion for outermost level */
1089             tpos = mb->tpos;    /* save expansion pointer for printExpand */
1090         negate = 0;
1091         lastc = NULL;
1092         chkexist = 0;
1093         switch ((c = *s)) {
1094         default:                /* %name substitution */
1095             while (*s != '\0' && strchr("!?", *s) != NULL) {
1096                 switch (*s++) {
1097                     case '!':
1098                         negate = ((negate + 1) % 2);
1099                         break;
1100                     case '?':
1101                         chkexist++;
1102                         break;
1103                 }
1104             }
1105             f = se = s;
1106             if (*se == '-')
1107                 se++;
1108             while ((c = *se) && (risalnum(c) || c == '_'))
1109                 se++;
1110             /* Recognize non-alnum macros too */
1111             switch (*se) {
1112             case '*':
1113                 se++;
1114                 if (*se == '*') se++;
1115                 break;
1116             case '#':
1117                 se++;
1118                 break;
1119             default:
1120                 break;
1121             }
1122             fe = se;
1123             /* For "%name " macros ... */
1124             if ((c = *fe) && isblank(c))
1125                 if ((lastc = strchr(fe,'\n')) == NULL)
1126                     lastc = strchr(fe, '\0');
1127             break;
1128         case '(':               /* %(...) shell escape */
1129             if ((se = matchchar(s, c, ')')) == NULL) {
1130                 rpmlog(RPMLOG_ERR, _("Unterminated %c: %s\n"), (char)c, s);
1131                 mb->error = 1;
1132                 continue;
1133             }
1134             if (mb->macro_trace)
1135                 printMacro(mb, s, se+1);
1136
1137             s++;        /* skip ( */
1138             doShellEscape(mb, s, (se - s));
1139             se++;       /* skip ) */
1140
1141             s = se;
1142             continue;
1143             break;
1144         case '{':               /* %{...}/%{...:...} substitution */
1145             if ((se = matchchar(s, c, '}')) == NULL) {
1146                 rpmlog(RPMLOG_ERR, _("Unterminated %c: %s\n"), (char)c, s);
1147                 mb->error = 1;
1148                 continue;
1149             }
1150             f = s+1;/* skip { */
1151             se++;       /* skip } */
1152             while (strchr("!?", *f) != NULL) {
1153                 switch (*f++) {
1154                 case '!':
1155                     negate = ((negate + 1) % 2);
1156                     break;
1157                 case '?':
1158                     chkexist++;
1159                     break;
1160                 }
1161             }
1162             for (fe = f; (c = *fe) && !strchr(" :}", c);)
1163                 fe++;
1164             switch (c) {
1165             case ':':
1166                 g = fe + 1;
1167                 ge = se - 1;
1168                 break;
1169             case ' ':
1170                 lastc = se-1;
1171                 break;
1172             default:
1173                 break;
1174             }
1175             break;
1176         }
1177
1178         /* XXX Everything below expects fe > f */
1179         fn = (fe - f);
1180         gn = (ge - g);
1181         if ((fe - f) <= 0) {
1182             /* XXX Process % in unknown context */
1183             c = '%';    /* XXX only need to save % */
1184             mbAppend(mb, c);
1185 #if 0
1186             rpmlog(RPMLOG_ERR,
1187                     _("A %% is followed by an unparseable macro\n"));
1188 #endif
1189             s = se;
1190             continue;
1191         }
1192
1193         if (mb->macro_trace)
1194             printMacro(mb, s, se);
1195
1196         /* Expand builtin macros */
1197         if (STREQ("load", f, fn)) {
1198             char *arg = NULL;
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);
1203                     mb->error = 1;
1204                 }
1205             }
1206             free(arg);
1207             s = se;
1208             continue;
1209         }
1210         if (STREQ("global", f, fn)) {
1211             s = doDefine(mb, se, slen - (se - s), RMIL_GLOBAL, 1);
1212             continue;
1213         }
1214         if (STREQ("define", f, fn)) {
1215             s = doDefine(mb, se, slen - (se - s), mb->level, 0);
1216             continue;
1217         }
1218         if (STREQ("undefine", f, fn)) {
1219             s = doUndefine(mb, se, slen - (se - s));
1220             continue;
1221         }
1222
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;
1229                 mb->error = 0;
1230             } else if (STREQ("warn", f, fn)) {
1231                 loglevel = RPMLOG_WARNING;
1232             }
1233             if (g != NULL && g < ge)
1234                 doOutput(mb, loglevel, g, gn);
1235             else
1236                 doOutput(mb, loglevel, "", 0);
1237             s = se;
1238             continue;
1239         }
1240
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;
1247             }
1248             s = se;
1249             continue;
1250         }
1251
1252         if (STREQ("dump", f, fn)) {
1253             rpmDumpMacroTable(mb->mc, NULL);
1254             while (iseol(*se))
1255                 se++;
1256             s = se;
1257             continue;
1258         }
1259
1260         if (STREQ("lua", f, fn)) {
1261             doLua(mb, f, fn, g, gn);
1262             s = se;
1263             continue;
1264         }
1265
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) ||
1281             STREQ("F", f, fn))
1282         {
1283             /* FIX: verbose may be set */
1284             doFoo(mb, negate, f, fn, g, gn);
1285             s = se;
1286             continue;
1287         }
1288
1289         /* Expand defined macros */
1290         mep = findEntry(mb->mc, f, fn, NULL);
1291         me = (mep ? *mep : NULL);
1292
1293         if (me) {
1294             if ((me->flags & ME_AUTO) && mb->level > me->level) {
1295                 /* Ignore out-of-scope automatic macros */
1296                 me = NULL;
1297             } else {
1298                 /* If we looked up a macro, consider it used */
1299                 me->flags |= ME_USED;
1300             }
1301         }
1302
1303         /* XXX Special processing for flags */
1304         if (*f == '-') {
1305             if ((me == NULL && !negate) ||      /* Without -f, skip %{-f...} */
1306                     (me != NULL && negate)) {   /* With -f, skip %{!-f...} */
1307                 s = se;
1308                 continue;
1309             }
1310
1311             if (g && g < ge) {          /* Expand X in %{-f:X} */
1312                 expandMacro(mb, g, gn);
1313             } else
1314                 if (me && me->body && *me->body) {/* Expand %{-f}/%{-f*} */
1315                     expandMacro(mb, me->body, 0);
1316                 }
1317             s = se;
1318             continue;
1319         }
1320
1321         /* XXX Special processing for macro existence */
1322         if (chkexist) {
1323             if ((me == NULL && !negate) ||      /* Without -f, skip %{?f...} */
1324                     (me != NULL && negate)) {   /* With -f, skip %{!?f...} */
1325                 s = se;
1326                 continue;
1327             }
1328             if (g && g < ge) {          /* Expand X in %{?f:X} */
1329                 expandMacro(mb, g, gn);
1330             } else
1331                 if (me && me->body && *me->body) { /* Expand %{?f}/%{?f*} */
1332                     expandMacro(mb, me->body, 0);
1333                 }
1334             s = se;
1335             continue;
1336         }
1337         
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 % */
1341             mbAppend(mb, c);
1342             continue;
1343         }
1344
1345         /* Setup args for "%name " macros with opts */
1346         if (me && me->opts != NULL) {
1347             const char *xe = grabArgs(mb, me, fe, lastc);
1348             if (xe != NULL)
1349                 se = xe;
1350         }
1351
1352         /* Recursively expand body of macro */
1353         if (me->body && *me->body) {
1354             expandMacro(mb, me->body, 0);
1355         }
1356
1357         /* Free args for "%name " macros with opts */
1358         if (me->opts != NULL)
1359             freeArgs(mb);
1360
1361         s = se;
1362     }
1363
1364     mb->buf[mb->tpos] = '\0';
1365     mb->depth--;
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;
1370 exit:
1371     _free(source);
1372     return mb->error;
1373 }
1374
1375
1376 /* =============================================================== */
1377
1378 static int doExpandMacros(rpmMacroContext mc, const char *src, int flags,
1379                         char **target)
1380 {
1381     MacroBuf mb = xcalloc(1, sizeof(*mb));
1382     int rc = 0;
1383
1384     mb->buf = NULL;
1385     mb->depth = mc->depth;
1386     mb->level = mc->level;
1387     mb->macro_trace = print_macro_trace;
1388     mb->expand_trace = print_expand_trace;
1389     mb->mc = mc;
1390     mb->flags = flags;
1391
1392     rc = expandMacro(mb, src, 0);
1393
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);
1397
1398     _free(mb);
1399     return rc;
1400 }
1401
1402 static void pushMacro(rpmMacroContext mc,
1403         const char * n, const char * o, const char * b, int level, int flags)
1404 {
1405     /* new entry */
1406     rpmMacroEntry me;
1407     /* pointer into me */
1408     char *p;
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);
1413
1414     size_t pos;
1415     rpmMacroEntry *mep = findEntry(mc, n, 0, &pos);
1416     if (mep) {
1417         /* entry with shared name */
1418         me = xmalloc(mesize);
1419         /* copy body */
1420         me->body = p = me->arena;
1421         if (blen)
1422             memcpy(p, b, blen + 1);
1423         else
1424             *p = '\0';
1425         p += blen + 1;
1426         /* set name */
1427         me->name = (*mep)->name;
1428     }
1429     else {
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));
1436         mc->n++;
1437         /* make slot */
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);
1443         /* copy body */
1444         me->body = p = me->arena;
1445         if (blen)
1446             memcpy(p, b, blen + 1);
1447         else
1448             *p = '\0';
1449         p += blen + 1;
1450         /* copy name */
1451         me->name = memcpy(p, n, nlen + 1);
1452         p += nlen + 1;
1453     }
1454
1455     /* copy options */
1456     if (olen)
1457         me->opts = memcpy(p, o, olen + 1);
1458     else
1459         me->opts = o ? "" : NULL;
1460     /* initialize */
1461     me->flags = flags;
1462     me->flags &= ~(ME_USED);
1463     me->level = level;
1464     /* push over previous definition */
1465     me->prev = *mep;
1466     *mep = me;
1467 }
1468
1469 static void popMacro(rpmMacroContext mc, const char * n)
1470 {
1471     size_t pos;
1472     rpmMacroEntry *mep = findEntry(mc, n, 0, &pos);
1473     if (mep == NULL)
1474         return;
1475     /* parting entry */
1476     rpmMacroEntry me = *mep;
1477     assert(me);
1478     /* detach/pop definition */
1479     mc->tab[pos] = me->prev;
1480     /* shrink macro table */
1481     if (me->prev == NULL) {
1482         mc->n--;
1483         /* move pos+ elements to the left */
1484         memmove(mc->tab + pos, mc->tab + pos + 1, sizeof(me) * (mc->n - pos));
1485         /* deallocate */
1486         if (mc->n == 0)
1487             mc->tab = _free(mc->tab);
1488     }
1489     /* comes in a single chunk */
1490     free(me);
1491 }
1492
1493 static int defineMacro(rpmMacroContext mc, const char * macro, int level)
1494 {
1495     MacroBuf mb = xcalloc(1, sizeof(*mb));
1496     int rc;
1497
1498     /* XXX just enough to get by */
1499     mb->mc = mc;
1500     (void) doDefine(mb, macro, strlen(macro), level, 0);
1501     rc = mb->error;
1502     _free(mb);
1503     return rc;
1504 }
1505
1506 static int loadMacroFile(rpmMacroContext mc, const char * fn)
1507 {
1508     FILE *fd = fopen(fn, "r");
1509     size_t blen = MACROBUFSIZ;
1510     char *buf = xmalloc(blen);
1511     int rc = -1;
1512
1513     if (fd == NULL)
1514         goto exit;
1515
1516     buf[0] = '\0';
1517     while (rdcl(buf, blen, fd) != NULL) {
1518         char c, *n;
1519
1520         n = buf;
1521         SKIPBLANK(n, c);
1522
1523         if (c != '%')
1524                 continue;
1525         n++;    /* skip % */
1526         rc = defineMacro(mc, n, RMIL_MACROFILES);
1527     }
1528
1529     rc = fclose(fd);
1530
1531 exit:
1532     _free(buf);
1533     return rc;
1534 }
1535
1536 static void copyMacros(rpmMacroContext src, rpmMacroContext dst, int level)
1537 {
1538     for (int i = 0; i < src->n; i++) {
1539         rpmMacroEntry me = src->tab[i];
1540         assert(me);
1541         pushMacro(dst, me->name, me->opts, me->body, level, me->flags);
1542     }
1543 }
1544
1545 /* External interfaces */
1546
1547 int rpmExpandMacros(rpmMacroContext mc, const char * sbuf, char ** obuf, int flags)
1548 {
1549     char *target = NULL;
1550     int rc;
1551
1552     mc = rpmmctxAcquire(mc);
1553     rc = doExpandMacros(mc, sbuf, flags, &target);
1554     rpmmctxRelease(mc);
1555
1556     if (rc) {
1557         free(target);
1558         return -1;
1559     } else {
1560         *obuf = target;
1561         return 1;
1562     }
1563 }
1564
1565 void
1566 rpmDumpMacroTable(rpmMacroContext mc, FILE * fp)
1567 {
1568     mc = rpmmctxAcquire(mc);
1569     if (fp == NULL) fp = stderr;
1570     
1571     fprintf(fp, "========================\n");
1572     for (int i = 0; i < mc->n; i++) {
1573         rpmMacroEntry me = mc->tab[i];
1574         assert(me);
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);
1581         fprintf(fp, "\n");
1582     }
1583     fprintf(fp, _("======================== active %d empty %d\n"),
1584                 mc->n, 0);
1585     rpmmctxRelease(mc);
1586 }
1587
1588 int rpmPushMacro(rpmMacroContext mc,
1589               const char * n, const char * o, const char * b, int level)
1590 {
1591     mc = rpmmctxAcquire(mc);
1592     pushMacro(mc, n, o, b, level, ME_NONE);
1593     rpmmctxRelease(mc);
1594     return 0;
1595 }
1596
1597 int rpmPopMacro(rpmMacroContext mc, const char * n)
1598 {
1599     mc = rpmmctxAcquire(mc);
1600     popMacro(mc, n);
1601     rpmmctxRelease(mc);
1602     return 0;
1603 }
1604
1605 int
1606 rpmDefineMacro(rpmMacroContext mc, const char * macro, int level)
1607 {
1608     int rc;
1609     mc = rpmmctxAcquire(mc);
1610     rc = defineMacro(mc, macro, level);
1611     rpmmctxRelease(mc);
1612     return rc;
1613 }
1614
1615 void
1616 rpmLoadMacros(rpmMacroContext mc, int level)
1617 {
1618     rpmMacroContext gmc;
1619     if (mc == NULL || mc == rpmGlobalMacroContext)
1620         return;
1621
1622     gmc = rpmmctxAcquire(NULL);
1623     mc = rpmmctxAcquire(mc);
1624
1625     copyMacros(mc, gmc, level);
1626
1627     rpmmctxRelease(mc);
1628     rpmmctxRelease(gmc);
1629 }
1630
1631 int
1632 rpmLoadMacroFile(rpmMacroContext mc, const char * fn)
1633 {
1634     int rc;
1635
1636     mc = rpmmctxAcquire(mc);
1637     rc = loadMacroFile(mc, fn);
1638     rpmmctxRelease(mc);
1639
1640     return rc;
1641 }
1642
1643 void
1644 rpmInitMacros(rpmMacroContext mc, const char * macrofiles)
1645 {
1646     ARGV_t pattern, globs = NULL;
1647     rpmMacroContext climc;
1648
1649     if (macrofiles == NULL)
1650         return;
1651
1652     argvSplit(&globs, macrofiles, ":");
1653     mc = rpmmctxAcquire(mc);
1654     for (pattern = globs; *pattern; pattern++) {
1655         ARGV_t path, files = NULL;
1656     
1657         /* Glob expand the macro file path element, expanding ~ to $HOME. */
1658         if (rpmGlob(*pattern, NULL, &files) != 0) {
1659             continue;
1660         }
1661
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")) {
1667                 continue;
1668             }
1669             (void) loadMacroFile(mc, *path);
1670         }
1671         argvFree(files);
1672     }
1673     argvFree(globs);
1674
1675     /* Reload cmdline macros */
1676     climc = rpmmctxAcquire(rpmCLIMacroContext);
1677     copyMacros(climc, mc, RMIL_CMDLINE);
1678     rpmmctxRelease(climc);
1679
1680     rpmmctxRelease(mc);
1681 }
1682
1683 void
1684 rpmFreeMacros(rpmMacroContext mc)
1685 {
1686     mc = rpmmctxAcquire(mc);
1687     while (mc->n > 0) {
1688         /* remove from the end to avoid memmove */
1689         rpmMacroEntry me = mc->tab[mc->n - 1];
1690         popMacro(mc, me->name);
1691     }
1692     rpmmctxRelease(mc);
1693 }
1694
1695 char * 
1696 rpmExpand(const char *arg, ...)
1697 {
1698     size_t blen = 0;
1699     char *buf = NULL, *ret = NULL;
1700     char *pe;
1701     const char *s;
1702     va_list ap;
1703     rpmMacroContext mc;
1704
1705     if (arg == NULL) {
1706         ret = xstrdup("");
1707         goto exit;
1708     }
1709
1710     /* precalculate unexpanded size */
1711     va_start(ap, arg);
1712     for (s = arg; s != NULL; s = va_arg(ap, const char *))
1713         blen += strlen(s);
1714     va_end(ap);
1715
1716     buf = xmalloc(blen + 1);
1717     buf[0] = '\0';
1718
1719     va_start(ap, arg);
1720     for (pe = buf, s = arg; s != NULL; s = va_arg(ap, const char *))
1721         pe = stpcpy(pe, s);
1722     va_end(ap);
1723
1724     mc = rpmmctxAcquire(NULL);
1725     (void) doExpandMacros(mc, buf, 0, &ret);
1726     rpmmctxRelease(mc);
1727
1728     free(buf);
1729 exit:
1730     return ret;
1731 }
1732
1733 int
1734 rpmExpandNumeric(const char *arg)
1735 {
1736     char *val;
1737     int rc;
1738
1739     if (arg == NULL)
1740         return 0;
1741
1742     val = rpmExpand(arg, NULL);
1743     if (!(val && *val != '%'))
1744         rc = 0;
1745     else if (*val == 'Y' || *val == 'y')
1746         rc = 1;
1747     else if (*val == 'N' || *val == 'n')
1748         rc = 0;
1749     else {
1750         char *end;
1751         rc = strtol(val, &end, 0);
1752         if (!(end && *end == '\0'))
1753             rc = 0;
1754     }
1755     free(val);
1756
1757     return rc;
1758 }