Make base64 encoding/decoding part of rpmio public API
[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 #ifdef HAVE_GETOPT_H
8 #include <getopt.h>
9 #else
10 extern char *optarg;
11 extern int optind;
12 #endif
13
14 #if !defined(isblank)
15 #define isblank(_c)     ((_c) == ' ' || (_c) == '\t')
16 #endif
17 #define iseol(_c)       ((_c) == '\n' || (_c) == '\r')
18
19 #define STREQ(_t, _f, _fn)      ((_fn) == (sizeof(_t)-1) && rstreqn((_t), (_f), (_fn)))
20
21 #define MACROBUFSIZ (BUFSIZ * 2)
22
23 #include <rpm/rpmio.h>
24 #include <rpm/rpmstring.h>
25 #include <rpm/rpmfileutil.h>
26 #include <rpm/rpmurl.h>
27 #include <rpm/rpmlog.h>
28 #include <rpm/rpmmacro.h>
29 #include <rpm/argv.h>
30
31 #ifdef  WITH_LUA
32 #include "rpmio/rpmlua.h"
33 #endif
34
35 #include "debug.h"
36
37 /*! The structure used to store a macro. */
38 struct rpmMacroEntry_s {
39     struct rpmMacroEntry_s *prev;/*!< Macro entry stack. */
40     char *name;         /*!< Macro name. */
41     char *opts;         /*!< Macro parameters (a la getopt) */
42     char *body;         /*!< Macro body. */
43     int used;           /*!< No. of expansions. */
44     int level;          /*!< Scoping level. */
45 };
46
47 /*! The structure used to store the set of macros in a context. */
48 struct rpmMacroContext_s {
49     rpmMacroEntry *macroTable;  /*!< Macro entry table for context. */
50     int macrosAllocated;/*!< No. of allocated macros. */
51     int firstFree;      /*!< No. of macros. */
52 };
53
54
55 static struct rpmMacroContext_s rpmGlobalMacroContext_s;
56 rpmMacroContext rpmGlobalMacroContext = &rpmGlobalMacroContext_s;
57
58 static struct rpmMacroContext_s rpmCLIMacroContext_s;
59 rpmMacroContext rpmCLIMacroContext = &rpmCLIMacroContext_s;
60
61 /**
62  * Macro expansion state.
63  */
64 typedef struct MacroBuf_s {
65     char * buf;                 /*!< Expansion buffer. */
66     size_t tpos;                /*!< Current position in expansion buffer */
67     size_t nb;                  /*!< No. bytes remaining in expansion buffer. */
68     int depth;                  /*!< Current expansion depth. */
69     int macro_trace;            /*!< Pre-print macro to expand? */
70     int expand_trace;           /*!< Post-print macro expansion? */
71     rpmMacroContext mc;
72 } * MacroBuf;
73
74 #define _MAX_MACRO_DEPTH        16
75 static int max_macro_depth = _MAX_MACRO_DEPTH;
76
77 #define _PRINT_MACRO_TRACE      0
78 static int print_macro_trace = _PRINT_MACRO_TRACE;
79
80 #define _PRINT_EXPAND_TRACE     0
81 static int print_expand_trace = _PRINT_EXPAND_TRACE;
82
83 #define MACRO_CHUNK_SIZE        16
84
85 /* forward ref */
86 static int expandMacro(MacroBuf mb, const char *src, size_t slen);
87
88 /* =============================================================== */
89
90 /**
91  * Compare macro entries by name (qsort/bsearch).
92  * @param ap            1st macro entry
93  * @param bp            2nd macro entry
94  * @return              result of comparison
95  */
96 static int
97 compareMacroName(const void * ap, const void * bp)
98 {
99     rpmMacroEntry ame = *((const rpmMacroEntry *)ap);
100     rpmMacroEntry bme = *((const rpmMacroEntry *)bp);
101
102     if (ame == NULL && bme == NULL)
103         return 0;
104     if (ame == NULL)
105         return 1;
106     if (bme == NULL)
107         return -1;
108     return strcmp(ame->name, bme->name);
109 }
110
111 /**
112  * Enlarge macro table.
113  * @param mc            macro context
114  */
115 static void
116 expandMacroTable(rpmMacroContext mc)
117 {
118     if (mc->macroTable == NULL) {
119         mc->macrosAllocated = MACRO_CHUNK_SIZE;
120         mc->macroTable = (rpmMacroEntry *)
121             xmalloc(sizeof(*(mc->macroTable)) * mc->macrosAllocated);
122         mc->firstFree = 0;
123     } else {
124         mc->macrosAllocated += MACRO_CHUNK_SIZE;
125         mc->macroTable = (rpmMacroEntry *)
126             xrealloc(mc->macroTable, sizeof(*(mc->macroTable)) *
127                         mc->macrosAllocated);
128     }
129     memset(&mc->macroTable[mc->firstFree], 0, MACRO_CHUNK_SIZE * sizeof(*(mc->macroTable)));
130 }
131
132 /**
133  * Sort entries in macro table.
134  * @param mc            macro context
135  */
136 static void
137 sortMacroTable(rpmMacroContext mc)
138 {
139     int i;
140
141     if (mc == NULL || mc->macroTable == NULL)
142         return;
143
144     qsort(mc->macroTable, mc->firstFree, sizeof(*(mc->macroTable)),
145                 compareMacroName);
146
147     /* Empty pointers are now at end of table. Reset first free index. */
148     for (i = 0; i < mc->firstFree; i++) {
149         if (mc->macroTable[i] != NULL)
150             continue;
151         mc->firstFree = i;
152         break;
153     }
154 }
155
156 void
157 rpmDumpMacroTable(rpmMacroContext mc, FILE * fp)
158 {
159     int nempty = 0;
160     int nactive = 0;
161
162     if (mc == NULL) mc = rpmGlobalMacroContext;
163     if (fp == NULL) fp = stderr;
164     
165     fprintf(fp, "========================\n");
166     if (mc->macroTable != NULL) {
167         int i;
168         for (i = 0; i < mc->firstFree; i++) {
169             rpmMacroEntry me;
170             if ((me = mc->macroTable[i]) == NULL) {
171                 /* XXX this should never happen */
172                 nempty++;
173                 continue;
174             }
175             fprintf(fp, "%3d%c %s", me->level,
176                         (me->used > 0 ? '=' : ':'), me->name);
177             if (me->opts && *me->opts)
178                     fprintf(fp, "(%s)", me->opts);
179             if (me->body && *me->body)
180                     fprintf(fp, "\t%s", me->body);
181             fprintf(fp, "\n");
182             nactive++;
183         }
184     }
185     fprintf(fp, _("======================== active %d empty %d\n"),
186                 nactive, nempty);
187 }
188
189 /**
190  * Find entry in macro table.
191  * @param mc            macro context
192  * @param name          macro name
193  * @param namelen       no. of bytes
194  * @return              address of slot in macro table with name (or NULL)
195  */
196 static rpmMacroEntry *
197 findEntry(rpmMacroContext mc, const char * name, size_t namelen)
198 {
199     rpmMacroEntry key, *ret;
200     struct rpmMacroEntry_s keybuf;
201     char namebuf[namelen+1];
202     const char *mname = name;
203
204     if (mc == NULL) mc = rpmGlobalMacroContext;
205     if (mc->macroTable == NULL || mc->firstFree == 0)
206         return NULL;
207
208     if (namelen > 0) {
209         strncpy(namebuf, name, namelen);
210         namebuf[namelen] = '\0';
211         mname = namebuf;
212     }
213     
214     key = &keybuf;
215     memset(key, 0, sizeof(*key));
216     key->name = (char *)mname;
217     ret = (rpmMacroEntry *) bsearch(&key, mc->macroTable, mc->firstFree,
218                         sizeof(*(mc->macroTable)), compareMacroName);
219     /* XXX TODO: find 1st empty slot and return that */
220     return ret;
221 }
222
223 /* =============================================================== */
224
225 /**
226  * fgets(3) analogue that reads \ continuations. Last newline always trimmed.
227  * @param buf           input buffer
228  * @param size          inbut buffer size (bytes)
229  * @param fd            file handle
230  * @return              buffer, or NULL on end-of-file
231  */
232 static char *
233 rdcl(char * buf, size_t size, FILE *f)
234 {
235     char *q = buf - 1;          /* initialize just before buffer. */
236     size_t nb = 0;
237     size_t nread = 0;
238     int pc = 0, bc = 0;
239     char *p = buf;
240
241     if (f != NULL)
242     do {
243         *(++q) = '\0';                  /* terminate and move forward. */
244         if (fgets(q, size, f) == NULL)  /* read next line. */
245             break;
246         nb = strlen(q);
247         nread += nb;                    /* trim trailing \r and \n */
248         for (q += nb - 1; nb > 0 && iseol(*q); q--)
249             nb--;
250         for (; p <= q; p++) {
251             switch (*p) {
252                 case '\\':
253                     switch (*(p+1)) {
254                         case '\0': break;
255                         default: p++; break;
256                     }
257                     break;
258                 case '%':
259                     switch (*(p+1)) {
260                         case '{': p++, bc++; break;
261                         case '(': p++, pc++; break;
262                         case '%': p++; break;
263                     }
264                     break;
265                 case '{': if (bc > 0) bc++; break;
266                 case '}': if (bc > 0) bc--; break;
267                 case '(': if (pc > 0) pc++; break;
268                 case ')': if (pc > 0) pc--; break;
269             }
270         }
271         if (nb == 0 || (*q != '\\' && !bc && !pc) || *(q+1) == '\0') {
272             *(++q) = '\0';              /* trim trailing \r, \n */
273             break;
274         }
275         q++; p++; nb++;                 /* copy newline too */
276         size -= nb;
277         if (*q == '\r')                 /* XXX avoid \r madness */
278             *q = '\n';
279     } while (size > 0);
280     return (nread > 0 ? buf : NULL);
281 }
282
283 /**
284  * Return text between pl and matching pr characters.
285  * @param p             start of text
286  * @param pl            left char, i.e. '[', '(', '{', etc.
287  * @param pr            right char, i.e. ']', ')', '}', etc.
288  * @return              address of last char before pr (or NULL)
289  */
290 static const char *
291 matchchar(const char * p, char pl, char pr)
292 {
293     int lvl = 0;
294     char c;
295
296     while ((c = *p++) != '\0') {
297         if (c == '\\') {                /* Ignore escaped chars */
298             p++;
299             continue;
300         }
301         if (c == pr) {
302             if (--lvl <= 0)     return --p;
303         } else if (c == pl)
304             lvl++;
305     }
306     return (const char *)NULL;
307 }
308
309 /**
310  * Pre-print macro expression to be expanded.
311  * @param mb            macro expansion state
312  * @param s             current expansion string
313  * @param se            end of string
314  */
315 static void
316 printMacro(MacroBuf mb, const char * s, const char * se)
317 {
318     const char *senl;
319     const char *ellipsis;
320     int choplen;
321
322     if (s >= se) {      /* XXX just in case */
323         fprintf(stderr, _("%3d>%*s(empty)"), mb->depth,
324                 (2 * mb->depth + 1), "");
325         return;
326     }
327
328     if (s[-1] == '{')
329         s--;
330
331     /* Print only to first end-of-line (or end-of-string). */
332     for (senl = se; *senl && !iseol(*senl); senl++)
333         {};
334
335     /* Limit trailing non-trace output */
336     choplen = 61 - (2 * mb->depth);
337     if ((senl - s) > choplen) {
338         senl = s + choplen;
339         ellipsis = "...";
340     } else
341         ellipsis = "";
342
343     /* Substitute caret at end-of-macro position */
344     fprintf(stderr, "%3d>%*s%%%.*s^", mb->depth,
345         (2 * mb->depth + 1), "", (int)(se - s), s);
346     if (se[1] != '\0' && (senl - (se+1)) > 0)
347         fprintf(stderr, "%-.*s%s", (int)(senl - (se+1)), se+1, ellipsis);
348     fprintf(stderr, "\n");
349 }
350
351 /**
352  * Post-print expanded macro expression.
353  * @param mb            macro expansion state
354  * @param t             current expansion string result
355  * @param te            end of string
356  */
357 static void
358 printExpansion(MacroBuf mb, const char * t, const char * te)
359 {
360     const char *ellipsis;
361     int choplen;
362
363     if (!(te > t)) {
364         rpmlog(RPMLOG_DEBUG, _("%3d<%*s(empty)\n"), mb->depth, (2 * mb->depth + 1), "");
365         return;
366     }
367
368     /* Shorten output which contains newlines */
369     while (te > t && iseol(te[-1]))
370         te--;
371     ellipsis = "";
372     if (mb->depth > 0) {
373         const char *tenl;
374
375         /* Skip to last line of expansion */
376         while ((tenl = strchr(t, '\n')) && tenl < te)
377             t = ++tenl;
378
379         /* Limit expand output */
380         choplen = 61 - (2 * mb->depth);
381         if ((te - t) > choplen) {
382             te = t + choplen;
383             ellipsis = "...";
384         }
385     }
386
387     rpmlog(RPMLOG_DEBUG,"%3d<%*s", mb->depth, (2 * mb->depth + 1), "");
388     if (te > t)
389         rpmlog(RPMLOG_DEBUG, "%.*s%s", (int)(te - t), t, ellipsis);
390     rpmlog(RPMLOG_DEBUG, "\n");
391 }
392
393 #define SKIPBLANK(_s, _c)       \
394         while (((_c) = *(_s)) && isblank(_c)) \
395                 (_s)++;         \
396
397 #define SKIPNONBLANK(_s, _c)    \
398         while (((_c) = *(_s)) && !(isblank(_c) || iseol(_c))) \
399                 (_s)++;         \
400
401 #define COPYNAME(_ne, _s, _c)   \
402     {   SKIPBLANK(_s,_c);       \
403         while(((_c) = *(_s)) && (risalnum(_c) || (_c) == '_')) \
404                 *(_ne)++ = *(_s)++; \
405         *(_ne) = '\0';          \
406     }
407
408 #define COPYOPTS(_oe, _s, _c)   \
409     { \
410         while(((_c) = *(_s)) && (_c) != ')') \
411                 *(_oe)++ = *(_s)++; \
412         *(_oe) = '\0';          \
413     }
414
415 /**
416  * Macro-expand string src, return result in dynamically allocated buffer.
417  * @param mb            macro expansion state
418  * @param src           string to expand
419  * @param slen          input string length (or 0 for strlen())
420  * @retval target       pointer to expanded string (malloced)
421  * @return              result of expansion
422  */
423 static int
424 expandThis(MacroBuf mb, const char * src, size_t slen, char **target)
425 {
426     struct MacroBuf_s umb;
427     int rc;
428
429     /* Copy other state from "parent", but we want a buffer of our own */
430     umb = *mb;
431     umb.buf = NULL;
432     rc = expandMacro(&umb, src, slen);
433     *target = umb.buf;
434
435     return rc;
436 }
437
438 static void mbAppend(MacroBuf mb, char c)
439 {
440     if (mb->nb < 1) {
441         mb->buf = xrealloc(mb->buf, mb->tpos + mb->nb + MACROBUFSIZ);
442         mb->nb += MACROBUFSIZ;
443     }
444     mb->buf[mb->tpos++] = c;
445     mb->nb--;
446 }
447
448 static void mbAppendStr(MacroBuf mb, const char *str)
449 {
450     size_t len = strlen(str);
451     if (len > mb->nb) {
452         mb->buf = xrealloc(mb->buf, mb->tpos + mb->nb + MACROBUFSIZ + len);
453         mb->nb += MACROBUFSIZ + len;
454     }
455     memcpy(mb->buf+mb->tpos, str, len);
456     mb->tpos += len;
457     mb->nb -= len;
458 }
459 /**
460  * Expand output of shell command into target buffer.
461  * @param mb            macro expansion state
462  * @param cmd           shell command
463  * @param clen          no. bytes in shell command
464  * @return              result of expansion
465  */
466 static int
467 doShellEscape(MacroBuf mb, const char * cmd, size_t clen)
468 {
469     char *buf = NULL;
470     FILE *shf;
471     int rc = 0;
472     int c;
473
474     rc = expandThis(mb, cmd, clen, &buf);
475     if (rc)
476         goto exit;
477
478     if ((shf = popen(buf, "r")) == NULL) {
479         rc = 1;
480         goto exit;
481     }
482     while((c = fgetc(shf)) != EOF) {
483         mbAppend(mb, c);
484     }
485     (void) pclose(shf);
486
487     /* XXX delete trailing \r \n */
488     while (iseol(mb->buf[mb->tpos-1])) {
489         mb->buf[mb->tpos--] = '\0';
490         mb->nb++;
491     }
492
493 exit:
494     _free(buf);
495     return rc;
496 }
497
498 /**
499  * Parse (and execute) new macro definition.
500  * @param mb            macro expansion state
501  * @param se            macro definition to parse
502  * @param level         macro recursion level
503  * @param expandbody    should body be expanded?
504  * @return              address to continue parsing
505  */
506 static const char *
507 doDefine(MacroBuf mb, const char * se, int level, int expandbody)
508 {
509     const char *s = se;
510     size_t blen = MACROBUFSIZ;
511     char *buf = xmalloc(blen);
512     char *n = buf, *ne = n;
513     char *o = NULL, *oe;
514     char *b, *be, *ebody = NULL;
515     int c;
516     int oc = ')';
517
518     /* Copy name */
519     COPYNAME(ne, s, c);
520
521     /* Copy opts (if present) */
522     oe = ne + 1;
523     if (*s == '(') {
524         s++;    /* skip ( */
525         o = oe;
526         COPYOPTS(oe, s, oc);
527         s++;    /* skip ) */
528     }
529
530     /* Copy body, skipping over escaped newlines */
531     b = be = oe + 1;
532     SKIPBLANK(s, c);
533     if (c == '{') {     /* XXX permit silent {...} grouping */
534         if ((se = matchchar(s, c, '}')) == NULL) {
535             rpmlog(RPMLOG_ERR,
536                 _("Macro %%%s has unterminated body\n"), n);
537             se = s;     /* XXX W2DO? */
538             goto exit;
539         }
540         s++;    /* XXX skip { */
541         strncpy(b, s, (se - s));
542         b[se - s] = '\0';
543         be += strlen(b);
544         se++;   /* XXX skip } */
545         s = se; /* move scan forward */
546     } else {    /* otherwise free-field */
547         int bc = 0, pc = 0;
548         while (*s && (bc || pc || !iseol(*s))) {
549             switch (*s) {
550                 case '\\':
551                     switch (*(s+1)) {
552                         case '\0': break;
553                         default: s++; break;
554                     }
555                     break;
556                 case '%':
557                     switch (*(s+1)) {
558                         case '{': *be++ = *s++; bc++; break;
559                         case '(': *be++ = *s++; pc++; break;
560                         case '%': *be++ = *s++; break;
561                     }
562                     break;
563                 case '{': if (bc > 0) bc++; break;
564                 case '}': if (bc > 0) bc--; break;
565                 case '(': if (pc > 0) pc++; break;
566                 case ')': if (pc > 0) pc--; break;
567             }
568             *be++ = *s++;
569         }
570         *be = '\0';
571
572         if (bc || pc) {
573             rpmlog(RPMLOG_ERR,
574                 _("Macro %%%s has unterminated body\n"), n);
575             se = s;     /* XXX W2DO? */
576             goto exit;
577         }
578
579         /* Trim trailing blanks/newlines */
580         while (--be >= b && (c = *be) && (isblank(c) || iseol(c)))
581             {};
582         *(++be) = '\0'; /* one too far */
583     }
584
585     /* Move scan over body */
586     while (iseol(*s))
587         s++;
588     se = s;
589
590     /* Names must start with alphabetic or _ and be at least 3 chars */
591     if (!((c = *n) && (risalpha(c) || c == '_') && (ne - n) > 2)) {
592         rpmlog(RPMLOG_ERR,
593                 _("Macro %%%s has illegal name (%%define)\n"), n);
594         goto exit;
595     }
596
597     /* Options must be terminated with ')' */
598     if (o && oc != ')') {
599         rpmlog(RPMLOG_ERR, _("Macro %%%s has unterminated opts\n"), n);
600         goto exit;
601     }
602
603     if ((be - b) < 1) {
604         rpmlog(RPMLOG_ERR, _("Macro %%%s has empty body\n"), n);
605         goto exit;
606     }
607
608     if (expandbody) {
609         if (expandThis(mb, b, 0, &ebody)) {
610             rpmlog(RPMLOG_ERR, _("Macro %%%s failed to expand\n"), n);
611             goto exit;
612         }
613         b = ebody;
614     }
615
616     addMacro(mb->mc, n, o, b, (level - 1));
617
618 exit:
619     _free(buf);
620     _free(ebody);
621     return se;
622 }
623
624 /**
625  * Parse (and execute) macro undefinition.
626  * @param mc            macro context
627  * @param se            macro name to undefine
628  * @return              address to continue parsing
629  */
630 static const char *
631 doUndefine(rpmMacroContext mc, const char * se)
632 {
633     const char *s = se;
634     char *buf = xmalloc(MACROBUFSIZ);
635     char *n = buf, *ne = n;
636     int c;
637
638     COPYNAME(ne, s, c);
639
640     /* Move scan over body */
641     while (iseol(*s))
642         s++;
643     se = s;
644
645     /* Names must start with alphabetic or _ and be at least 3 chars */
646     if (!((c = *n) && (risalpha(c) || c == '_') && (ne - n) > 2)) {
647         rpmlog(RPMLOG_ERR,
648                 _("Macro %%%s has illegal name (%%undefine)\n"), n);
649         goto exit;
650     }
651
652     delMacro(mc, n);
653
654 exit:
655     _free(buf);
656     return se;
657 }
658
659 /**
660  * Push new macro definition onto macro entry stack.
661  * @param mep           address of macro entry slot
662  * @param n             macro name
663  * @param o             macro parameters (NULL if none)
664  * @param b             macro body (NULL becomes "")
665  * @param level         macro recursion level
666  */
667 static void
668 pushMacro(rpmMacroEntry * mep,
669                 const char * n, const char * o,
670                 const char * b, int level)
671 {
672     rpmMacroEntry prev = (mep && *mep ? *mep : NULL);
673     rpmMacroEntry me = (rpmMacroEntry) xmalloc(sizeof(*me));
674
675     me->prev = prev;
676     me->name = (prev ? prev->name : xstrdup(n));
677     me->opts = (o ? xstrdup(o) : NULL);
678     me->body = xstrdup(b ? b : "");
679     me->used = 0;
680     me->level = level;
681     if (mep)
682         *mep = me;
683     else
684         me = _free(me);
685 }
686
687 /**
688  * Pop macro definition from macro entry stack.
689  * @param mep           address of macro entry slot
690  */
691 static void
692 popMacro(rpmMacroEntry * mep)
693 {
694     if (mep && *mep) {
695         rpmMacroEntry me = *mep;
696
697         /* restore previous definition of the macro */
698         *mep = me->prev;
699
700         /* name is shared between entries, only free if last of its kind */
701         if (me->prev == NULL)
702             free(me->name);
703         free(me->opts);
704         free(me->body);
705
706         memset(me, 0, sizeof(*me)); /* trash and burn */
707         free(me);
708     }
709 }
710
711 /**
712  * Free parsed arguments for parameterized macro.
713  * @param mb            macro expansion state
714  */
715 static void
716 freeArgs(MacroBuf mb)
717 {
718     rpmMacroContext mc = mb->mc;
719     int ndeleted = 0;
720     int i;
721
722     if (mc == NULL || mc->macroTable == NULL)
723         return;
724
725     /* Delete dynamic macro definitions */
726     for (i = 0; i < mc->firstFree; i++) {
727         rpmMacroEntry *mep, me;
728         int skiptest = 0;
729         mep = &mc->macroTable[i];
730         me = *mep;
731
732         if (me == NULL)         /* XXX this should never happen */
733             continue;
734         if (me->level < mb->depth)
735             continue;
736         if (strlen(me->name) == 1 && strchr("#*0", *me->name)) {
737             if (*me->name == '*' && me->used > 0)
738                 skiptest = 1; /* XXX skip test for %# %* %0 */
739         } else if (!skiptest && me->used <= 0) {
740 #if NOTYET
741             rpmlog(RPMLOG_ERR,
742                         _("Macro %%%s (%s) was not used below level %d\n"),
743                         me->name, me->body, me->level);
744 #endif
745         }
746         popMacro(mep);
747         if (!(mep && *mep))
748             ndeleted++;
749     }
750
751     /* If any deleted macros, sort macro table */
752     if (ndeleted)
753         sortMacroTable(mc);
754 }
755
756 /**
757  * Parse arguments (to next new line) for parameterized macro.
758  * @todo Use popt rather than getopt to parse args.
759  * @param mb            macro expansion state
760  * @param me            macro entry slot
761  * @param se            arguments to parse
762  * @param lastc         stop parsing at lastc
763  * @return              address to continue parsing
764  */
765 static const char *
766 grabArgs(MacroBuf mb, const rpmMacroEntry me, const char * se,
767                 const char * lastc)
768 {
769     const char *opts;
770     char *args = NULL;
771     ARGV_t argv = NULL;
772     int argc = 0;
773     int c;
774
775     /* Copy macro name as argv[0] */
776     argvAdd(&argv, me->name);
777     addMacro(mb->mc, "0", NULL, me->name, mb->depth);
778     
779     /* 
780      * Make a copy of se up to lastc string that we can pass to argvSplit().
781      * Append the results to main argv. 
782      */
783     {   ARGV_t av = NULL;
784         char *s = xcalloc((lastc-se)+1, sizeof(*s));
785         memcpy(s, se, (lastc-se));
786
787         argvSplit(&av, s, " \t");
788         argvAppend(&argv, av);
789
790         argvFree(av);
791         free(s);
792     }
793
794     /*
795      * The macro %* analoguous to the shell's $* means "Pass all non-macro
796      * parameters." Consequently, there needs to be a macro that means "Pass all
797      * (including macro parameters) options". This is useful for verifying
798      * parameters during expansion and yet transparently passing all parameters
799      * through for higher level processing (e.g. %description and/or %setup).
800      * This is the (potential) justification for %{**} ...
801     */
802     args = argvJoin(argv + 1, " ");
803     addMacro(mb->mc, "**", NULL, args, mb->depth);
804     free(args);
805
806     /*
807      * POSIX states optind must be 1 before any call but glibc uses 0
808      * to (re)initialize getopt structures, eww.
809      */
810 #ifdef __GLIBC__
811     optind = 0;
812 #else
813     optind = 1;
814 #endif
815
816     opts = me->opts;
817     argc = argvCount(argv);
818
819     /* Define option macros. */
820     while((c = getopt(argc, argv, opts)) != -1)
821     {
822         char *name = NULL, *body = NULL;
823         if (c == '?' || strchr(opts, c) == NULL) {
824             rpmlog(RPMLOG_ERR, _("Unknown option %c in %s(%s)\n"),
825                         (char)c, me->name, opts);
826             goto exit;
827         }
828
829         rasprintf(&name, "-%c", c);
830         if (optarg) {
831             rasprintf(&body, "-%c %s", c, optarg);
832         } else {
833             rasprintf(&body, "-%c", c);
834         }
835         addMacro(mb->mc, name, NULL, body, mb->depth);
836         free(name);
837         free(body);
838
839         if (optarg) {
840             rasprintf(&name, "-%c*", c);
841             addMacro(mb->mc, name, NULL, optarg, mb->depth);
842             free(name);
843         }
844     }
845
846     /* Add argument count (remaining non-option items) as macro. */
847     {   char *ac = NULL;
848         rasprintf(&ac, "%d", (argc - optind));
849         addMacro(mb->mc, "#", NULL, ac, mb->depth);
850         free(ac);
851     }
852
853     /* Add macro for each argument */
854     if (argc - optind) {
855         for (c = optind; c < argc; c++) {
856             char *name = NULL;
857             rasprintf(&name, "%d", (c - optind + 1));
858             addMacro(mb->mc, name, NULL, argv[c], mb->depth);
859             free(name);
860         }
861     }
862
863     /* Add concatenated unexpanded arguments as yet another macro. */
864     args = argvJoin(argv + optind, " ");
865     addMacro(mb->mc, "*", NULL, args ? args : "", mb->depth);
866     free(args);
867
868 exit:
869     argvFree(argv);
870     return *lastc ? lastc + 1 : lastc; 
871 }
872
873 /**
874  * Perform macro message output
875  * @param mb            macro expansion state
876  * @param waserror      use rpmlog()?
877  * @param msg           message to ouput
878  * @param msglen        no. of bytes in message
879  */
880 static void
881 doOutput(MacroBuf mb, int waserror, const char * msg, size_t msglen)
882 {
883     char *buf = NULL;
884
885     (void) expandThis(mb, msg, msglen, &buf);
886     if (waserror)
887         rpmlog(RPMLOG_ERR, "%s\n", buf);
888     else
889         fprintf(stderr, "%s", buf);
890     _free(buf);
891 }
892
893 /**
894  * Execute macro primitives.
895  * @param mb            macro expansion state
896  * @param negate        should logic be inverted?
897  * @param f             beginning of field f
898  * @param fn            length of field f
899  * @param g             beginning of field g
900  * @param gn            length of field g
901  */
902 static void
903 doFoo(MacroBuf mb, int negate, const char * f, size_t fn,
904                 const char * g, size_t gn)
905 {
906     char *buf = NULL;
907     char *b = NULL, *be;
908     int c;
909
910     if (g != NULL) {
911         (void) expandThis(mb, g, gn, &buf);
912     } else {
913         buf = xmalloc(MACROBUFSIZ + fn + gn);
914         buf[0] = '\0';
915     }
916     if (STREQ("basename", f, fn)) {
917         if ((b = strrchr(buf, '/')) == NULL)
918             b = buf;
919         else
920             b++;
921 #if NOTYET
922     /* XXX watchout for conflict with %dir */
923     } else if (STREQ("dirname", f, fn)) {
924         if ((b = strrchr(buf, '/')) != NULL)
925             *b = '\0';
926         b = buf;
927 #endif
928     } else if (STREQ("suffix", f, fn)) {
929         if ((b = strrchr(buf, '.')) != NULL)
930             b++;
931     } else if (STREQ("expand", f, fn)) {
932         b = buf;
933     } else if (STREQ("verbose", f, fn)) {
934         if (negate)
935             b = (rpmIsVerbose() ? NULL : buf);
936         else
937             b = (rpmIsVerbose() ? buf : NULL);
938     } else if (STREQ("url2path", f, fn) || STREQ("u2p", f, fn)) {
939         (void)urlPath(buf, (const char **)&b);
940         if (*b == '\0') b = "/";
941     } else if (STREQ("uncompress", f, fn)) {
942         rpmCompressedMagic compressed = COMPRESSED_OTHER;
943         for (b = buf; (c = *b) && isblank(c);)
944             b++;
945         for (be = b; (c = *be) && !isblank(c);)
946             be++;
947         *be++ = '\0';
948         (void) rpmFileIsCompressed(b, &compressed);
949         switch(compressed) {
950         default:
951         case COMPRESSED_NOT:
952             sprintf(be, "%%__cat %s", b);
953             break;
954         case COMPRESSED_OTHER:
955             sprintf(be, "%%__gzip -dc %s", b);
956             break;
957         case COMPRESSED_BZIP2:
958             sprintf(be, "%%__bzip2 -dc %s", b);
959             break;
960         case COMPRESSED_ZIP:
961             sprintf(be, "%%__unzip %s", b);
962             break;
963         case COMPRESSED_LZMA:
964         case COMPRESSED_XZ:
965             sprintf(be, "%%__xz -dc %s", b);
966             break;
967         case COMPRESSED_LZIP:
968             sprintf(be, "%%__lzip -dc %s", b);
969             break;
970         case COMPRESSED_LRZIP:
971             sprintf(be, "%%__lrzip -dqo- %s", b);
972             break;
973         }
974         b = be;
975     } else if (STREQ("getenv", f, fn)) {
976         b = getenv(buf);
977     } else if (STREQ("getconfdir", f, fn)) {
978         sprintf(buf, "%s", rpmConfigDir());
979         b = buf;
980     } else if (STREQ("S", f, fn)) {
981         for (b = buf; (c = *b) && risdigit(c);)
982             b++;
983         if (!c) {       /* digit index */
984             b++;
985             sprintf(b, "%%SOURCE%s", buf);
986         } else
987             b = buf;
988     } else if (STREQ("P", f, fn)) {
989         for (b = buf; (c = *b) && risdigit(c);)
990             b++;
991         if (!c) {       /* digit index */
992             b++;
993             sprintf(b, "%%PATCH%s", buf);
994         } else
995                         b = buf;
996     } else if (STREQ("F", f, fn)) {
997         b = buf + strlen(buf) + 1;
998         sprintf(b, "file%s.file", buf);
999     }
1000
1001     if (b) {
1002         (void) expandMacro(mb, b, 0);
1003     }
1004     free(buf);
1005 }
1006
1007 /**
1008  * The main macro recursion loop.
1009  * @param mb            macro expansion state
1010  * @param src           string to expand
1011  * @return              0 on success, 1 on failure
1012  */
1013 static int
1014 expandMacro(MacroBuf mb, const char *src, size_t slen)
1015 {
1016     rpmMacroEntry *mep;
1017     rpmMacroEntry me;
1018     const char *s = src, *se;
1019     const char *f, *fe;
1020     const char *g, *ge;
1021     size_t fn, gn, tpos;
1022     int c;
1023     int rc = 0;
1024     int negate;
1025     const char * lastc;
1026     int chkexist;
1027     char *source = NULL;
1028
1029     /* Handle non-terminated substrings by creating a terminated copy */
1030     if (!slen)
1031         slen = strlen(src);
1032     source = xmalloc(slen + 1);
1033     strncpy(source, src, slen);
1034     source[slen] = '\0';
1035     s = source;
1036
1037     if (mb->buf == NULL) {
1038         size_t blen = MACROBUFSIZ + strlen(s);
1039         mb->buf = xcalloc(blen + 1, sizeof(*mb->buf));
1040         mb->tpos = 0;
1041         mb->nb = blen;
1042     }
1043     tpos = mb->tpos; /* save expansion pointer for printExpand */
1044
1045     if (++mb->depth > max_macro_depth) {
1046         rpmlog(RPMLOG_ERR,
1047                 _("Too many levels of recursion in macro expansion. It is likely caused by recursive macro declaration.\n"));
1048         mb->depth--;
1049         mb->expand_trace = 1;
1050         _free(source);
1051         return 1;
1052     }
1053
1054     while (rc == 0 && (c = *s) != '\0') {
1055         s++;
1056         /* Copy text until next macro */
1057         switch(c) {
1058         case '%':
1059                 if (*s) {       /* Ensure not end-of-string. */
1060                     if (*s != '%')
1061                         break;
1062                     s++;        /* skip first % in %% */
1063                 }
1064         default:
1065                 mbAppend(mb, c);
1066                 continue;
1067                 break;
1068         }
1069
1070         /* Expand next macro */
1071         f = fe = NULL;
1072         g = ge = NULL;
1073         if (mb->depth > 1)      /* XXX full expansion for outermost level */
1074             tpos = mb->tpos;    /* save expansion pointer for printExpand */
1075         negate = 0;
1076         lastc = NULL;
1077         chkexist = 0;
1078         switch ((c = *s)) {
1079         default:                /* %name substitution */
1080                 while (strchr("!?", *s) != NULL) {
1081                         switch(*s++) {
1082                         case '!':
1083                                 negate = ((negate + 1) % 2);
1084                                 break;
1085                         case '?':
1086                                 chkexist++;
1087                                 break;
1088                         }
1089                 }
1090                 f = se = s;
1091                 if (*se == '-')
1092                         se++;
1093                 while((c = *se) && (risalnum(c) || c == '_'))
1094                         se++;
1095                 /* Recognize non-alnum macros too */
1096                 switch (*se) {
1097                 case '*':
1098                         se++;
1099                         if (*se == '*') se++;
1100                         break;
1101                 case '#':
1102                         se++;
1103                         break;
1104                 default:
1105                         break;
1106                 }
1107                 fe = se;
1108                 /* For "%name " macros ... */
1109                 if ((c = *fe) && isblank(c))
1110                         if ((lastc = strchr(fe,'\n')) == NULL)
1111                 lastc = strchr(fe, '\0');
1112                 break;
1113         case '(':               /* %(...) shell escape */
1114                 if ((se = matchchar(s, c, ')')) == NULL) {
1115                         rpmlog(RPMLOG_ERR,
1116                                 _("Unterminated %c: %s\n"), (char)c, s);
1117                         rc = 1;
1118                         continue;
1119                 }
1120                 if (mb->macro_trace)
1121                         printMacro(mb, s, se+1);
1122
1123                 s++;    /* skip ( */
1124                 rc = doShellEscape(mb, s, (se - s));
1125                 se++;   /* skip ) */
1126
1127                 s = se;
1128                 continue;
1129                 break;
1130         case '{':               /* %{...}/%{...:...} substitution */
1131                 if ((se = matchchar(s, c, '}')) == NULL) {
1132                         rpmlog(RPMLOG_ERR,
1133                                 _("Unterminated %c: %s\n"), (char)c, s);
1134                         rc = 1;
1135                         continue;
1136                 }
1137                 f = s+1;/* skip { */
1138                 se++;   /* skip } */
1139                 while (strchr("!?", *f) != NULL) {
1140                         switch(*f++) {
1141                         case '!':
1142                                 negate = ((negate + 1) % 2);
1143                                 break;
1144                         case '?':
1145                                 chkexist++;
1146                                 break;
1147                         }
1148                 }
1149                 for (fe = f; (c = *fe) && !strchr(" :}", c);)
1150                         fe++;
1151                 switch (c) {
1152                 case ':':
1153                         g = fe + 1;
1154                         ge = se - 1;
1155                         break;
1156                 case ' ':
1157                         lastc = se-1;
1158                         break;
1159                 default:
1160                         break;
1161                 }
1162                 break;
1163         }
1164
1165         /* XXX Everything below expects fe > f */
1166         fn = (fe - f);
1167         gn = (ge - g);
1168         if ((fe - f) <= 0) {
1169 /* XXX Process % in unknown context */
1170                 c = '%';        /* XXX only need to save % */
1171                 mbAppend(mb, c);
1172 #if 0
1173                 rpmlog(RPMLOG_ERR,
1174                         _("A %% is followed by an unparseable macro\n"));
1175 #endif
1176                 s = se;
1177                 continue;
1178         }
1179
1180         if (mb->macro_trace)
1181                 printMacro(mb, s, se);
1182
1183         /* Expand builtin macros */
1184         if (STREQ("global", f, fn)) {
1185                 s = doDefine(mb, se, RMIL_GLOBAL, 1);
1186                 continue;
1187         }
1188         if (STREQ("define", f, fn)) {
1189                 s = doDefine(mb, se, mb->depth, 0);
1190                 continue;
1191         }
1192         if (STREQ("undefine", f, fn)) {
1193                 s = doUndefine(mb->mc, se);
1194                 continue;
1195         }
1196
1197         if (STREQ("echo", f, fn) ||
1198             STREQ("warn", f, fn) ||
1199             STREQ("error", f, fn)) {
1200                 int waserror = 0;
1201                 if (STREQ("error", f, fn))
1202                         waserror = 1;
1203                 if (g != NULL && g < ge)
1204                         doOutput(mb, waserror, g, gn);
1205                 else
1206                         doOutput(mb, waserror, f, fn);
1207                 s = se;
1208                 continue;
1209         }
1210
1211         if (STREQ("trace", f, fn)) {
1212                 /* XXX TODO restore expand_trace/macro_trace to 0 on return */
1213                 mb->expand_trace = mb->macro_trace = (negate ? 0 : mb->depth);
1214                 if (mb->depth == 1) {
1215                         print_macro_trace = mb->macro_trace;
1216                         print_expand_trace = mb->expand_trace;
1217                 }
1218                 s = se;
1219                 continue;
1220         }
1221
1222         if (STREQ("dump", f, fn)) {
1223                 rpmDumpMacroTable(mb->mc, NULL);
1224                 while (iseol(*se))
1225                         se++;
1226                 s = se;
1227                 continue;
1228         }
1229
1230 #ifdef  WITH_LUA
1231         if (STREQ("lua", f, fn)) {
1232                 rpmlua lua = NULL; /* Global state. */
1233                 const char *ls = s+sizeof("{lua:")-1;
1234                 const char *lse = se-sizeof("}")+1;
1235                 char *scriptbuf = (char *)xmalloc((lse-ls)+1);
1236                 char *printbuf;
1237                 memcpy(scriptbuf, ls, lse-ls);
1238                 scriptbuf[lse-ls] = '\0';
1239                 rpmluaPushPrintBuffer(lua);
1240                 if (rpmluaRunScript(lua, scriptbuf, NULL) == -1)
1241                     rc = 1;
1242                 printbuf = rpmluaPopPrintBuffer(lua);
1243                 if (printbuf) {
1244                     mbAppendStr(mb, printbuf);
1245                     free(printbuf);
1246                 }
1247                 free(scriptbuf);
1248                 s = se;
1249                 continue;
1250         }
1251 #endif
1252
1253         /* XXX necessary but clunky */
1254         if (STREQ("basename", f, fn) ||
1255             STREQ("suffix", f, fn) ||
1256             STREQ("expand", f, fn) ||
1257             STREQ("verbose", f, fn) ||
1258             STREQ("uncompress", f, fn) ||
1259             STREQ("url2path", f, fn) ||
1260             STREQ("u2p", f, fn) ||
1261             STREQ("getenv", f, fn) ||
1262             STREQ("getconfdir", f, fn) ||
1263             STREQ("S", f, fn) ||
1264             STREQ("P", f, fn) ||
1265             STREQ("F", f, fn)) {
1266                 /* FIX: verbose may be set */
1267                 doFoo(mb, negate, f, fn, g, gn);
1268                 s = se;
1269                 continue;
1270         }
1271
1272         /* Expand defined macros */
1273         mep = findEntry(mb->mc, f, fn);
1274         me = (mep ? *mep : NULL);
1275
1276         /* XXX Special processing for flags */
1277         if (*f == '-') {
1278                 if (me)
1279                         me->used++;     /* Mark macro as used */
1280                 if ((me == NULL && !negate) ||  /* Without -f, skip %{-f...} */
1281                     (me != NULL && negate)) {   /* With -f, skip %{!-f...} */
1282                         s = se;
1283                         continue;
1284                 }
1285
1286                 if (g && g < ge) {              /* Expand X in %{-f:X} */
1287                         rc = expandMacro(mb, g, gn);
1288                 } else
1289                 if (me && me->body && *me->body) {/* Expand %{-f}/%{-f*} */
1290                         rc = expandMacro(mb, me->body, 0);
1291                 }
1292                 s = se;
1293                 continue;
1294         }
1295
1296         /* XXX Special processing for macro existence */
1297         if (chkexist) {
1298                 if ((me == NULL && !negate) ||  /* Without -f, skip %{?f...} */
1299                     (me != NULL && negate)) {   /* With -f, skip %{!?f...} */
1300                         s = se;
1301                         continue;
1302                 }
1303                 if (g && g < ge) {              /* Expand X in %{?f:X} */
1304                         rc = expandMacro(mb, g, gn);
1305                 } else
1306                 if (me && me->body && *me->body) { /* Expand %{?f}/%{?f*} */
1307                         rc = expandMacro(mb, me->body, 0);
1308                 }
1309                 s = se;
1310                 continue;
1311         }
1312         
1313         if (me == NULL) {       /* leave unknown %... as is */
1314                 /* XXX hack to permit non-overloaded %foo to be passed */
1315                 c = '%';        /* XXX only need to save % */
1316                 mbAppend(mb, c);
1317                 continue;
1318         }
1319
1320         /* Setup args for "%name " macros with opts */
1321         if (me && me->opts != NULL) {
1322                 if (lastc != NULL) {
1323                         se = grabArgs(mb, me, fe, lastc);
1324                 } else {
1325                         addMacro(mb->mc, "**", NULL, "", mb->depth);
1326                         addMacro(mb->mc, "*", NULL, "", mb->depth);
1327                         addMacro(mb->mc, "#", NULL, "0", mb->depth);
1328                         addMacro(mb->mc, "0", NULL, me->name, mb->depth);
1329                 }
1330         }
1331
1332         /* Recursively expand body of macro */
1333         if (me->body && *me->body) {
1334                 rc = expandMacro(mb, me->body, 0);
1335                 if (rc == 0)
1336                         me->used++;     /* Mark macro as used */
1337         }
1338
1339         /* Free args for "%name " macros with opts */
1340         if (me->opts != NULL)
1341                 freeArgs(mb);
1342
1343         s = se;
1344     }
1345
1346     mb->buf[mb->tpos] = '\0';
1347     mb->depth--;
1348     if (rc != 0 || mb->expand_trace)
1349         printExpansion(mb, mb->buf+tpos, mb->buf+mb->tpos);
1350     _free(source);
1351     return rc;
1352 }
1353
1354
1355 /* =============================================================== */
1356
1357 static int doExpandMacros(rpmMacroContext mc, const char *src, char **target)
1358 {
1359     MacroBuf mb = xcalloc(1, sizeof(*mb));
1360     int rc = 0;
1361
1362     if (mc == NULL) mc = rpmGlobalMacroContext;
1363
1364     mb->buf = NULL;
1365     mb->depth = 0;
1366     mb->macro_trace = print_macro_trace;
1367     mb->expand_trace = print_expand_trace;
1368     mb->mc = mc;
1369
1370     rc = expandMacro(mb, src, 0);
1371
1372     mb->buf[mb->tpos] = '\0';   /* XXX just in case */
1373     /* expanded output is usually much less than alloced buffer, downsize */
1374     *target = xrealloc(mb->buf, mb->tpos + 1);
1375
1376     _free(mb);
1377     return rc;
1378 }
1379
1380 int expandMacros(void * spec, rpmMacroContext mc, char * sbuf, size_t slen)
1381 {
1382     char *target = NULL;
1383     int rc = doExpandMacros(mc, sbuf, &target);
1384     rstrlcpy(sbuf, target, slen);
1385     free(target);
1386     return rc;
1387 }
1388
1389 void
1390 addMacro(rpmMacroContext mc,
1391         const char * n, const char * o, const char * b, int level)
1392 {
1393     rpmMacroEntry * mep;
1394
1395     if (mc == NULL) mc = rpmGlobalMacroContext;
1396
1397     /* If new name, expand macro table */
1398     if ((mep = findEntry(mc, n, 0)) == NULL) {
1399         if (mc->firstFree == mc->macrosAllocated)
1400             expandMacroTable(mc);
1401         if (mc->macroTable != NULL)
1402             mep = mc->macroTable + mc->firstFree++;
1403     }
1404
1405     if (mep != NULL) {
1406         /* Push macro over previous definition */
1407         pushMacro(mep, n, o, b, level);
1408
1409         /* If new name, sort macro table */
1410         if ((*mep)->prev == NULL)
1411             sortMacroTable(mc);
1412     }
1413 }
1414
1415 void
1416 delMacro(rpmMacroContext mc, const char * n)
1417 {
1418     rpmMacroEntry * mep;
1419
1420     if (mc == NULL) mc = rpmGlobalMacroContext;
1421     /* If name exists, pop entry */
1422     if ((mep = findEntry(mc, n, 0)) != NULL) {
1423         popMacro(mep);
1424         /* If deleted name, sort macro table */
1425         if (!(mep && *mep))
1426             sortMacroTable(mc);
1427     }
1428 }
1429
1430 int
1431 rpmDefineMacro(rpmMacroContext mc, const char * macro, int level)
1432 {
1433     MacroBuf mb = xcalloc(1, sizeof(*mb));
1434
1435     /* XXX just enough to get by */
1436     mb->mc = (mc ? mc : rpmGlobalMacroContext);
1437     (void) doDefine(mb, macro, level, 0);
1438     _free(mb);
1439     return 0;
1440 }
1441
1442 void
1443 rpmLoadMacros(rpmMacroContext mc, int level)
1444 {
1445
1446     if (mc == NULL || mc == rpmGlobalMacroContext)
1447         return;
1448
1449     if (mc->macroTable != NULL) {
1450         int i;
1451         for (i = 0; i < mc->firstFree; i++) {
1452             rpmMacroEntry *mep, me;
1453             mep = &mc->macroTable[i];
1454             me = *mep;
1455
1456             if (me == NULL)             /* XXX this should never happen */
1457                 continue;
1458             addMacro(NULL, me->name, me->opts, me->body, (level - 1));
1459         }
1460     }
1461 }
1462
1463 int
1464 rpmLoadMacroFile(rpmMacroContext mc, const char * fn)
1465 {
1466     FILE *fd = fopen(fn, "r");
1467     size_t blen = MACROBUFSIZ;
1468     char *buf = xmalloc(blen);
1469     int rc = -1;
1470
1471     if (fd == NULL)
1472         goto exit;
1473
1474     /* XXX Assume new fangled macro expansion */
1475     max_macro_depth = 16;
1476
1477     buf[0] = '\0';
1478     while(rdcl(buf, blen, fd) != NULL) {
1479         char c, *n;
1480
1481         n = buf;
1482         SKIPBLANK(n, c);
1483
1484         if (c != '%')
1485                 continue;
1486         n++;    /* skip % */
1487         rc = rpmDefineMacro(mc, n, RMIL_MACROFILES);
1488     }
1489     rc = fclose(fd);
1490
1491 exit:
1492     _free(buf);
1493     return rc;
1494 }
1495
1496 void
1497 rpmInitMacros(rpmMacroContext mc, const char * macrofiles)
1498 {
1499     ARGV_t pattern, globs = NULL;
1500
1501     if (macrofiles == NULL)
1502         return;
1503
1504     argvSplit(&globs, macrofiles, ":");
1505     for (pattern = globs; *pattern; pattern++) {
1506         ARGV_t path, files = NULL;
1507     
1508         /* Glob expand the macro file path element, expanding ~ to $HOME. */
1509         if (rpmGlob(*pattern, NULL, &files) != 0) {
1510             continue;
1511         }
1512
1513         /* Read macros from each file. */
1514         for (path = files; *path; path++) {
1515             if (rpmFileHasSuffix(*path, ".rpmnew") || 
1516                 rpmFileHasSuffix(*path, ".rpmsave") ||
1517                 rpmFileHasSuffix(*path, ".rpmorig")) {
1518                 continue;
1519             }
1520             (void) rpmLoadMacroFile(mc, *path);
1521         }
1522         argvFree(files);
1523     }
1524     argvFree(globs);
1525
1526     /* Reload cmdline macros */
1527     rpmLoadMacros(rpmCLIMacroContext, RMIL_CMDLINE);
1528 }
1529
1530 void
1531 rpmFreeMacros(rpmMacroContext mc)
1532 {
1533     
1534     if (mc == NULL) mc = rpmGlobalMacroContext;
1535
1536     if (mc->macroTable != NULL) {
1537         for (int i = 0; i < mc->firstFree; i++) {
1538             while (mc->macroTable[i] != NULL) {
1539                 popMacro(&mc->macroTable[i]);
1540             }
1541         }
1542         free(mc->macroTable);
1543     }
1544     memset(mc, 0, sizeof(*mc));
1545 }
1546
1547 char * 
1548 rpmExpand(const char *arg, ...)
1549 {
1550     size_t blen = 0;
1551     char *buf = NULL, *ret = NULL;
1552     char *pe;
1553     const char *s;
1554     va_list ap;
1555
1556     if (arg == NULL) {
1557         ret = xstrdup("");
1558         goto exit;
1559     }
1560
1561     /* precalculate unexpanded size */
1562     va_start(ap, arg);
1563     for (s = arg; s != NULL; s = va_arg(ap, const char *))
1564         blen += strlen(s);
1565     va_end(ap);
1566
1567     buf = xmalloc(blen + 1);
1568     buf[0] = '\0';
1569
1570     va_start(ap, arg);
1571     for (pe = buf, s = arg; s != NULL; s = va_arg(ap, const char *))
1572         pe = stpcpy(pe, s);
1573     va_end(ap);
1574
1575     (void) doExpandMacros(NULL, buf, &ret);
1576
1577     free(buf);
1578 exit:
1579     return ret;
1580 }
1581
1582 int
1583 rpmExpandNumeric(const char *arg)
1584 {
1585     char *val;
1586     int rc;
1587
1588     if (arg == NULL)
1589         return 0;
1590
1591     val = rpmExpand(arg, NULL);
1592     if (!(val && *val != '%'))
1593         rc = 0;
1594     else if (*val == 'Y' || *val == 'y')
1595         rc = 1;
1596     else if (*val == 'N' || *val == 'n')
1597         rc = 0;
1598     else {
1599         char *end;
1600         rc = strtol(val, &end, 0);
1601         if (!(end && *end == '\0'))
1602             rc = 0;
1603     }
1604     free(val);
1605
1606     return rc;
1607 }