aa4aa4cbf60b166c224db9d41709e736152faa9d
[platform/upstream/rpm.git] / rpmio / macro.c
1 #include "system.h"
2
3 #include <assert.h>
4
5 #define isblank(_c)     ((_c) == ' ' || (_c) == '\t')
6 #define STREQ(_t, _f, _fn)      ((_fn) == (sizeof(_t)-1) && !strncmp((_t), (_f), (_fn)))
7 #define FREE(_x)        { if (_x) free(_x); ((void *)(_x)) = NULL; }
8
9 #ifdef DEBUG_MACROS
10 #define rpmError fprintf
11 #define RPMERR_BADSPEC stderr
12 #define _(x)    x
13 #else
14 #include "rpmlib.h"
15 #endif
16
17 #include "rpmmacro.h"
18
19 typedef struct MacroBuf {
20         const char *s;          /* text to expand */
21         char *t;                /* expansion buffer */
22         size_t nb;              /* no. bytes remaining in expansion buffer */
23         int depth;              /* current expansion depth */
24         int macro_trace;        /* pre-print macro to expand? */
25         int expand_trace;       /* post-print macro expansion? */
26         void *spec;             /* (future) %file expansion info */
27         MacroContext *mc;
28 } MacroBuf;
29
30 #define SAVECHAR(_mb, _c) { *(_mb)->t = (_c), (_mb)->t++, (_mb)->nb--; }
31 #define DELECHAR(_mb, _c) { if ((_mb)->t[-1] == (_c)) *((_mb)->t--) = '\0', (_mb)->nb++; }
32
33 static int expandMacro(MacroBuf *mb);
34
35 int max_macro_depth = 2;
36
37 #ifdef  DEBUG_MACROS
38 int print_macro_trace = 0;
39 int print_expand_trace = 0;
40 #else
41 int print_macro_trace = 0;
42 int print_expand_trace = 0;
43 #endif
44
45 #define MACRO_CHUNK_SIZE        16
46
47 /* =============================================================== */
48
49 static int
50 compareMacroName(const void *ap, const void *bp)
51 {
52         MacroEntry *ame = *((MacroEntry **)ap);
53         MacroEntry *bme = *((MacroEntry **)bp);
54
55         if (ame == NULL && bme == NULL) {
56                 return 0;
57         }
58         if (ame == NULL) {
59                 return 1;
60         }
61         if (bme == NULL) {
62                 return -1;
63         }
64         return strcmp(ame->name, bme->name);
65 }
66
67 static void
68 expandMacroTable(MacroContext *mc)
69 {
70         if (mc->macroTable == NULL) {
71                 mc->macrosAllocated = MACRO_CHUNK_SIZE;
72                 mc->macroTable = (MacroEntry **)malloc(sizeof(*(mc->macroTable)) *
73                                 mc->macrosAllocated);
74                 mc->firstFree = 0;
75         } else {
76                 mc->macrosAllocated += MACRO_CHUNK_SIZE;
77                 mc->macroTable = (MacroEntry **)realloc(mc->macroTable, sizeof(*(mc->macroTable)) *
78                                 mc->macrosAllocated);
79         }
80         memset(&mc->macroTable[mc->firstFree], 0, MACRO_CHUNK_SIZE * sizeof(*(mc->macroTable)));
81 }
82
83 static void
84 sortMacroTable(MacroContext *mc)
85 {
86         qsort(mc->macroTable, mc->firstFree, sizeof(*(mc->macroTable)),
87                 compareMacroName);
88 }
89
90 static void
91 dumpMacroTable(MacroContext *mc)
92 {
93         int i;
94         int nempty = 0;
95         int nactive = 0;
96     
97         fprintf(stderr, "========================\n");
98         for (i = 0; i < mc->firstFree; i++) {
99                 MacroEntry *me;
100                 if ((me = mc->macroTable[i]) == NULL) {
101                         nempty++;
102                         continue;
103                 }
104                 fprintf(stderr, "%3d%c %s", me->level,
105                         (me->used > 0 ? '=' : ':'), me->name);
106                 if (me->opts)
107                         fprintf(stderr, "(%s)", me->opts);
108                 if (me->body)
109                         fprintf(stderr, "\t%s", me->body);
110                 fprintf(stderr, "\n");
111                 nactive++;
112         }
113         fprintf(stderr, _("======================== active %d empty %d\n"),
114                 nactive, nempty);
115 }
116
117 static MacroEntry **
118 findEntry(MacroContext *mc, const char *name, size_t namelen)
119 {
120         MacroEntry keybuf, *key, **ret;
121         char namebuf[1024];
122
123         if (! mc->firstFree)
124                 return NULL;
125
126         if (namelen > 0) {
127                 strncpy(namebuf, name, namelen);
128                 namebuf[namelen] = '\0';
129                 name = namebuf;
130         }
131     
132         key = &keybuf;
133         memset(key, 0, sizeof(*key));
134         key->name = (char *)name;
135         ret = (MacroEntry **)bsearch(&key, mc->macroTable, mc->firstFree,
136                         sizeof(*(mc->macroTable)), compareMacroName);
137         /* XXX TODO: find 1st empty slot and return that */
138         return ret;
139 }
140
141 /* =============================================================== */
142
143 /* fgets analogue that reads \ continuations. Last newline always trimmed. */
144
145 static char *
146 rdcl(char *buf, size_t size, FILE *fp, int escapes)
147 {
148         char *q = buf;
149         size_t nb = 0;
150
151         do {
152                 *q = '\0';                      /* next char in buf */
153                 nb = 0;
154                 if (fgets(q, size, fp) == NULL) /* read next line */
155                         break;
156                 nb = strlen(q);
157                 q += nb - 1;                    /* last char in buf */
158                 *q-- = '\0';                    /* trim newline */
159                 if (nb < 2 || *q != '\\')       /* continue? */
160                         break;
161                 if (escapes)                    /* copy escape too */
162                         q++;
163                 else
164                         nb--;
165                 *q++ = '\n';                    /* next char in buf */
166                 size -= nb;
167         } while (size > 0);
168         return (nb > 0 ? buf : NULL);
169 }
170
171 /* Return text between pl and matching pr */
172
173 static const char *
174 matchchar(const char *p, char pl, char pr)
175 {
176         int lvl = 0;
177         char c;
178
179         while ((c = *p++) != '\0') {
180                 if (c == '\\') {                /* Ignore escaped chars */
181                         p++;
182                         continue;
183                 }
184                 if (c == pr) {
185                         if (--lvl <= 0) return --p;
186                 } else if (c == pl)
187                         lvl++;
188         }
189         return (const char *)NULL;
190 }
191
192 static void
193 printMacro(MacroBuf *mb, const char *s, const char *se)
194 {
195         const char *senl;
196         const char *ellipsis;
197         int choplen;
198
199         if (s >= se) {  /* XXX just in case */
200                 fprintf(stderr, _("%3d>%*s(empty)"), mb->depth,
201                         (2 * mb->depth + 1), "");
202                 return;
203         }
204
205         if (s[-1] == '{')
206                 s--;
207
208         /* If not end-of-string, print only to newline or end-of-string ... */
209         if (*(senl = se) != '\0' && (senl = strchr(senl, '\n')) == NULL)
210                 senl = se + strlen(se);
211
212         /* Limit trailing non-trace output */
213         choplen = 61 - (2 * mb->depth);
214         if ((senl - s) > choplen) {
215                 senl = s + choplen;
216                 ellipsis = "...";
217         } else
218                 ellipsis = "";
219
220         /* Substitute caret at end-of-macro position */
221         fprintf(stderr, "%3d>%*s%%%.*s^", mb->depth,
222                 (2 * mb->depth + 1), "", (int)(se - s), s);
223         if (se[1] != '\0' && (senl - (se+1)) > 0)
224                 fprintf(stderr, "%-.*s%s", (int)(senl - (se+1)), se+1, ellipsis);
225         fprintf(stderr, "\n");
226 }
227
228 static void
229 printExpansion(MacroBuf *mb, const char *t, const char *te)
230 {
231         const char *ellipsis;
232         int choplen;
233
234         if (!(te > t)) {
235                 fprintf(stderr, _("%3d<%*s(empty)\n"), mb->depth, (2 * mb->depth + 1), "");
236                 return;
237         }
238
239         /* Shorten output which contains newlines */
240         while (te > t && te[-1] == '\n')
241                 te--;
242         ellipsis = "";
243         if (mb->depth > 0) {
244                 const char *tenl;
245                 while ((tenl = strchr(t, '\n')) && tenl < te)
246                         t = ++tenl;
247
248                 /* Limit expand output */
249                 choplen = 61 - (2 * mb->depth);
250                 if ((te - t) > choplen) {
251                         te = t + choplen;
252                         ellipsis = "...";
253                 }
254         }
255
256         fprintf(stderr, "%3d<%*s", mb->depth, (2 * mb->depth + 1), "");
257         if (te > t)
258                 fprintf(stderr, "%.*s%s", (int)(te - t), t, ellipsis);
259         fprintf(stderr, "\n");
260 }
261
262 #define SKIPBLANK(_s, _c)       \
263         while (((_c) = *(_s)) && isblank(_c)) \
264                 (_s)++;
265
266 #define SKIPNONBLANK(_s, _c)    \
267         while (((_c) = *(_s)) && !(isblank(_c) || c == '\n')) \
268                 (_s)++;
269
270 #define COPYNAME(_ne, _s, _c)   \
271     {   SKIPBLANK(_s,_c);       \
272         while(((_c) = *(_s)) && (isalnum(_c) || (_c) == '_')) \
273                 *(_ne)++ = *(_s)++; \
274         *(_ne) = '\0';          \
275     }
276
277 #define COPYOPTS(_oe, _s, _c)   \
278     {   while(((_c) = *(_s)) && (_c) != ')') \
279                 *(_oe)++ = *(_s)++; \
280         *(_oe) = '\0';          \
281     }
282
283 #define COPYBODY(_be, _s, _c)   \
284     {   while(((_c) = *(_s)) && (_c) != '\n') { \
285                 if ((_c) == '\\') \
286                         (_s)++; \
287                 *(_be)++ = *(_s)++; \
288         }                       \
289         *(_be) = '\0';          \
290     }
291
292 /* Save source and expand field into target */
293 static int
294 expandT(MacroBuf *mb, const char *f, size_t flen)
295 {
296         char *sbuf;
297         const char *s = mb->s;
298         int rc;
299
300         sbuf = alloca(flen + 1);
301         memset(sbuf, 0, (flen + 1));
302
303         strncpy(sbuf, f, flen);
304         sbuf[flen] = '\0';
305         mb->s = sbuf;
306         rc = expandMacro(mb);
307         mb->s = s;
308         return rc;
309 }
310
311 #if 0
312 /* Save target and expand sbuf into target */
313 static int
314 expandS(MacroBuf *mb, char *tbuf, size_t tbuflen)
315 {
316         const char *t = mb->t;
317         size_t nb = mb->nb;
318         int rc;
319
320         mb->t = tbuf;
321         mb->nb = tbuflen;
322         rc = expandMacro(mb);
323         mb->t = t;
324         mb->nb = nb;
325         return rc;
326 }
327 #endif
328
329 static int
330 expandU(MacroBuf *mb, char *u, size_t ulen)
331 {
332         const char *s = mb->s;
333         char *t = mb->t;
334         size_t nb = mb->nb;
335         char *tbuf;
336         int rc;
337
338         tbuf = alloca(ulen + 1);
339         memset(tbuf, 0, (ulen + 1));
340
341         mb->s = u;
342         mb->t = tbuf;
343         mb->nb = ulen;
344         rc = expandMacro(mb);
345
346         tbuf[ulen] = '\0';      /* XXX just in case */
347         if (ulen > mb->nb)
348                 strncpy(u, tbuf, (ulen - mb->nb + 1));
349
350         mb->s = s;
351         mb->t = t;
352         mb->nb = nb;
353
354         return rc;
355 }
356
357 static int
358 doShellEscape(MacroBuf *mb, const char *cmd, size_t clen)
359 {
360         char pcmd[BUFSIZ];
361         FILE *shf;
362         int rc;
363         int c;
364
365         strncpy(pcmd, cmd, clen);
366         pcmd[clen] = '\0';
367         rc = expandU(mb, pcmd, sizeof(pcmd));
368         if (rc)
369                 return rc;
370
371         if ((shf = popen(pcmd, "r")) == NULL)
372                 return 1;
373         while(mb->nb > 0 && (c = fgetc(shf)) != EOF)
374                 SAVECHAR(mb, c);
375         pclose(shf);
376
377         DELECHAR(mb, '\n');     /* XXX delete trailing newline (if any) */
378         return 0;
379 }
380
381 static const char *
382 doDefine(MacroBuf *mb, const char *se, int level, int expandbody)
383 {
384         const char *s = se;
385         char buf[BUFSIZ], *n = buf, *ne = n;
386         char *o = NULL, *oe;
387         char *b, *be;
388         int c;
389         int oc = ')';
390
391         /* Copy name */
392         COPYNAME(ne, s, c);
393
394         /* Copy opts (if present) */
395         oe = ne + 1;
396         if (*s == '(') {
397                 s++;    /* skip ( */
398                 o = oe;
399                 COPYOPTS(oe, s, oc);
400                 s++;    /* skip ) */
401         }
402
403         /* Copy body, skipping over escaped newlines */
404         b = be = oe + 1;
405         SKIPBLANK(s, c);
406         if (c == '{') { /* XXX permit silent {...} grouping */
407                 if ((se = matchchar(s, c, '}')) == NULL) {
408                         rpmError(RPMERR_BADSPEC, _("Macro %%%s has unterminated body"), n);
409                         se = s; /* XXX W2DO? */
410                         return se;
411                 }
412                 s++;    /* XXX skip { */
413                 strncpy(b, s, (se - s));
414                 b[se - s] = '\0';
415                 be += strlen(b);
416                 se++;   /* XXX skip } */
417                 s = se; /* move scan forward */
418         } else {        /* otherwise free-field */
419                 COPYBODY(be, s, c);
420
421                 /* Trim trailing blanks/newlines */
422                 while (--be >= b && (c = *be) && (isblank(c) || c == '\n'))
423                         ;
424                 *(++be) = '\0'; /* one too far */
425         }
426
427         /* Move scan over body */
428         if (*s == '\n')
429                 s++;
430         se = s;
431
432         /* Names must start with alphabetic or _ and be at least 3 chars */
433         if (!((c = *n) && (isalpha(c) || c == '_') && (ne - n) > 2)) {
434                 rpmError(RPMERR_BADSPEC, _("Macro %%%s has illegal name (%%define)"), n);
435                 return se;
436         }
437
438         /* Options must be terminated with ')' */
439         if (o && oc != ')') {
440                 rpmError(RPMERR_BADSPEC, _("Macro %%%s has unterminated opts"), n);
441                 return se;
442         }
443
444         if ((be - b) < 1) {
445                 rpmError(RPMERR_BADSPEC, _("Macro %%%s has empty body"), n);
446                 return se;
447         }
448
449         if (expandbody && expandU(mb, b, (&buf[sizeof(buf)] - b))) {
450                 rpmError(RPMERR_BADSPEC, _("Macro %%%s failed to expand"), n);
451                 return se;
452         }
453
454         addMacro(mb->mc, n, o, b, (level - 1));
455
456         return se;
457 }
458
459 static const char *
460 doUndefine(MacroContext *mc, const char *se)
461 {
462         const char *s = se;
463         char buf[BUFSIZ], *n = buf, *ne = n;
464         int c;
465
466         COPYNAME(ne, s, c);
467
468         /* Move scan over body */
469         if (*s == '\n')
470                 s++;
471         se = s;
472
473         /* Names must start with alphabetic or _ and be at least 3 chars */
474         if (!((c = *n) && (isalpha(c) || c == '_') && (ne - n) > 2)) {
475                 rpmError(RPMERR_BADSPEC, _("Macro %%%s has illegal name (%%undefine)"), n);
476                 return se;
477         }
478
479         delMacro(mc, n);
480
481         return se;
482 }
483
484 #ifdef  DYING
485 static void
486 dumpME(const char *msg, MacroEntry *me)
487 {
488         if (msg)
489                 fprintf(stderr, "%s", msg);
490         fprintf(stderr, "\tme %p", me);
491         if (me)
492                 fprintf(stderr,"\tname %p(%s) prev %p",
493                         me->name, me->name, me->prev);
494         fprintf(stderr, "\n");
495 }
496 #endif
497
498 static void
499 pushMacro(MacroEntry **mep, const char *n, const char *o, const char *b, int level)
500 {
501         MacroEntry *prev = (*mep ? *mep : NULL);
502         MacroEntry *me = malloc(sizeof(*me));
503
504         me->prev = prev;
505         me->name = (prev ? prev->name : strdup(n));
506         me->opts = (o ? strdup(o) : NULL);
507         me->body = (b ? strdup(b) : NULL);
508         me->used = 0;
509         me->level = level;
510         *mep = me;
511 }
512
513 static void
514 popMacro(MacroEntry **mep)
515 {
516         MacroEntry *me = (*mep ? *mep : NULL);
517
518         if (me) {
519                 /* XXX cast to workaround const */
520                 if ((*mep = me->prev) == NULL)
521                         FREE((char *)me->name);
522                 FREE((char *)me->opts);
523                 FREE((char *)me->body);
524                 FREE(me);
525         }
526 }
527
528 static void
529 freeArgs(MacroBuf *mb)
530 {
531         MacroContext *mc = mb->mc;
532         int c;
533
534         /* Delete dynamic macro definitions */
535         for (c = 0; c < mc->firstFree; c++) {
536                 MacroEntry *me;
537                 int skiptest = 0;
538                 if ((me = mc->macroTable[c]) == NULL)
539                         continue;
540                 if (me->level < mb->depth)
541                         continue;
542                 if (strlen(me->name) == 1 && strchr("#*0", *me->name)) {
543                         if (*me->name == '*' && me->used > 0)
544                                 skiptest = 1;
545                         ; /* XXX skip test for %# %* %0 */
546                 } else if (!skiptest && me->used <= 0) {
547 #if NOTYET
548                         rpmError(RPMERR_BADSPEC, _("Macro %%%s (%s) was not used below level %d"),
549                                 me->name, me->body, me->level);
550 #endif
551                 }
552                 popMacro(&mc->macroTable[c]);
553         }
554 }
555
556 static const char *
557 grabArgs(MacroBuf *mb, const MacroEntry *me, const char *se)
558 {
559     char buf[BUFSIZ], *b, *be;
560     char aname[16];
561     const char *opts, *o;
562     int argc = 0;
563     const char **argv;
564     int optc = 0;
565     const char **optv;
566     int opte;
567     int c;
568
569     /* Copy macro name as argv[0] */
570     argc = 0;
571     b = be = buf;
572     strcpy(b, me->name);
573     be += strlen(b);
574     *be = '\0';
575     argc++;     /* XXX count argv[0] */
576
577     addMacro(mb->mc, "0", NULL, b, mb->depth);
578     
579     /* Copy args into buf until newline */
580     *be++ = ' ';
581     b = be;     /* Save beginning of args */
582     while ((c = *se) != 0) {
583         char *a;
584         se++;
585         if (c == '\n')
586                 break;
587         if (isblank(c))
588                 continue;
589         if (argc > 1)
590                 *be++ = ' ';
591         a = be;
592         while (!(isblank(c) || c == '\n')) {
593                 *be++ = c;
594                 if ((c = *se) == '\0')
595                         break;
596                 se++;
597         }
598         *be = '\0';
599         argc++;
600     }
601
602     /* Add unexpanded args as macro */
603     addMacro(mb->mc, "*", NULL, b, mb->depth);
604
605 #ifdef NOTYET
606     /* XXX if macros can be passed as args ... */
607     expandU(mb, buf, sizeof(buf));
608 #endif
609
610     /* Build argv array */
611     argv = (const char **)alloca((argc + 1) * sizeof(char *));
612     b = be = buf;
613     for (c = 0; c < argc; c++) {
614         b = be;
615         if ((be = strchr(b, ' ')) == NULL)
616                 be = b + strlen(b);
617         *be++ = '\0';
618         argv[c] = b;
619     }
620     argv[argc] = NULL;
621
622     opts = me->opts;
623
624     /* First count number of options ... */
625     optind = 0;
626     optc = 0;
627     optc++;     /* XXX count argv[0] too */
628     while((c = getopt(argc, (char **)argv, opts)) != -1) {
629         if (!(c != '?' && (o = strchr(opts, c)))) {
630                 rpmError(RPMERR_BADSPEC, _("Unknown option %c in %s(%s)"),
631                         c, me->name, opts);
632                 return se;
633         }
634         optc++;
635     }
636
637     /* ... then allocate storage ... */
638     opte = optc + (argc - optind);
639     optv = (const char **)alloca((opte + 1) * sizeof(char *));
640     optv[0] = me->name;
641     optv[opte] = NULL;
642
643     /* ... and finally copy the options */
644     optind = 0;
645     optc = 0;
646     optc++;     /* XXX count optv[0] */
647     while((c = getopt(argc, (char **)argv, opts)) != -1) {
648         o = strchr(opts, c);
649         b = be;
650         *be++ = '-';
651         *be++ = c;
652         if (o[1] == ':') {
653                 *be++ = ' ';
654                 strcpy(be, optarg);
655                 be += strlen(be);
656         }
657         *be++ = '\0';
658         sprintf(aname, "-%c", c);
659         addMacro(mb->mc, aname, NULL, b, mb->depth);
660         if (o[1] == ':') {
661                 sprintf(aname, "-%c*", c);
662                 addMacro(mb->mc, aname, NULL, optarg, mb->depth);
663         }
664         optv[optc] = b;
665         optc++;
666     }
667
668     for (c = optind; c < argc; c++) {
669         sprintf(aname, "%d", (c - optind + 1));
670         addMacro(mb->mc, aname, NULL, argv[c], mb->depth);
671         optv[optc] = argv[c];
672         optc++;
673     }
674     sprintf(aname, "%d", (argc - optind + 1));
675     addMacro(mb->mc, "#", NULL, aname, mb->depth);
676
677     return se;
678 }
679
680 static void
681 doOutput(MacroBuf *mb, int waserror, const char *msg, size_t msglen)
682 {
683         char buf[BUFSIZ];
684
685         strncpy(buf, msg, msglen);
686         buf[msglen] = '\0';
687         expandU(mb, buf, sizeof(buf));
688         if (waserror)
689                 rpmError(RPMERR_BADSPEC, "%s", buf);
690         else
691                 fprintf(stderr, "%s", buf);
692 }
693
694 static void
695 doFoo(MacroBuf *mb, const char *f, size_t fn, const char *g, size_t glen)
696 {
697         char buf[BUFSIZ], *b = NULL, *be;
698         int c;
699
700         buf[0] = '\0';
701         if (g) {
702                 strncpy(buf, g, glen);
703                 buf[glen] = '\0';
704                 expandU(mb, buf, sizeof(buf));
705         }
706         if (STREQ("basename", f, fn)) {
707                 if ((b = strrchr(buf, '/')) == NULL)
708                         b = buf;
709 #if NOTYET
710         /* XXX watchout for conflict with %dir */
711         } else if (STREQ("dirname", f, fn)) {
712                 if ((b = strrchr(buf, '/')) != NULL)
713                         *b = '\0';
714                 b = buf;
715 #endif
716         } else if (STREQ("suffix", f, fn)) {
717                 if ((b = strrchr(buf, '.')) != NULL)
718                         b++;
719         } else if (STREQ("expand", f, fn)) {
720                 b = buf;
721         } else if (STREQ("uncompress", f, fn)) {
722                 int compressed = 1;
723                 for (b = buf; (c = *b) && isblank(c);)
724                         b++;
725                 for (be = b; (c = *be) && !isblank(c);)
726                         be++;
727                 *be++ = '\0';
728 #ifndef DEBUG_MACROS
729                 isCompressed(b, &compressed);
730 #endif
731                 switch(compressed) {
732                 default:
733                 case 0: /* COMPRESSED_NOT */
734                         sprintf(be, "%%_cat %s", b);
735                         break;
736                 case 1: /* COMPRESSED_OTHER */
737                         sprintf(be, "%%_gzip -dc %s", b);
738                         break;
739                 case 2: /* COMPRESSED_BZIP2 */
740                         sprintf(be, "%%_bzip2 %s", b);
741                         break;
742                 }
743                 b = be;
744         } else if (STREQ("S", f, fn)) {
745                 for (b = buf; (c = *b) && isdigit(c);)
746                         b++;
747                 if (!c) {       /* digit index */
748                         b++;
749                         sprintf(b, "%%SOURCE%s", buf);
750                 } else
751                         b = buf;
752         } else if (STREQ("P", f, fn)) {
753                 for (b = buf; (c = *b) && isdigit(c);)
754                         b++;
755                 if (!c) {       /* digit index */
756                         b++;
757                         sprintf(b, "%%PATCH%s", buf);
758                 } else
759                         b = buf;
760         } else if (STREQ("F", f, fn)) {
761                 b = buf + strlen(buf) + 1;
762                 sprintf(b, "file%s.file", buf);
763 #if DEAD
764 fprintf(stderr, "FILE: \"%s\"\n", b);
765 #endif
766         }
767
768         if (b) {
769                 expandT(mb, b, strlen(b));
770         }
771 }
772
773 /* The main recursion engine */
774
775 static int
776 expandMacro(MacroBuf *mb)
777 {
778     MacroEntry **mep;
779     MacroEntry *me;
780     const char *s = mb->s, *se;
781     const char *f, *fe;
782     const char *g, *ge;
783     size_t fn, gn;
784     char *t = mb->t;    /* save expansion pointer for printExpand */
785     int c;
786     int rc = 0;
787     int negate;
788     int grab;
789     int chkexist;
790
791     if (++mb->depth > max_macro_depth) {
792         rpmError(RPMERR_BADSPEC, _("Recursion depth(%d) greater than max(%d)"),
793                 mb->depth, max_macro_depth);
794         mb->depth--;
795         mb->expand_trace = 1;
796         return 1;
797     }
798
799     while (rc == 0 && mb->nb > 0 && (c = *s) != '\0') {
800         s++;
801         /* Copy text until next macro */
802         switch(c) {
803         case '%':
804                 if (*s != '%')
805                         break;
806                 s++;    /* skip first % in %% */
807                 /* fall thru */
808         default:
809                 SAVECHAR(mb, c);
810                 continue;
811                 break;
812         }
813
814         /* Expand next macro */
815         f = fe = NULL;
816         g = ge = NULL;
817         if (mb->depth > 1)      /* XXX full expansion for outermost level */
818                 t = mb->t;      /* save expansion pointer for printExpand */
819         negate = 0;
820         grab = 0;
821         chkexist = 0;
822         switch ((c = *s)) {
823         default:                /* %name substitution */
824                 while (strchr("!?", *s) != NULL) {
825                         switch(*s++) {
826                         case '!':
827                                 negate = (++negate % 2);
828                                 break;
829                         case '?':
830                                 chkexist++;
831                                 break;
832                         }
833                 }
834                 f = se = s;
835                 if (*se == '-')
836                         se++;
837                 while((c = *se) && (isalnum(c) || c == '_'))
838                         se++;
839                 if (*se == '*')
840                         se++;
841                 fe = se;
842                 /* For "%name " macros ... */
843                 if ((c = *fe) && isblank(c))
844                         grab = 1;
845                 break;
846         case '(':               /* %(...) shell escape */
847                 if ((se = matchchar(s, c, ')')) == NULL) {
848                         rpmError(RPMERR_BADSPEC, _("Unterminated %c: %s"), c, s);
849                         rc = 1;
850                         continue;
851                 }
852                 if (mb->macro_trace)
853                         printMacro(mb, s, se+1);
854
855                 s++;    /* skip ( */
856                 rc = doShellEscape(mb, s, (se - s));
857                 se++;   /* skip ) */
858
859                 s = se;
860                 continue;
861                 break;
862         case '{':               /* %{...}/%{...:...} substitution */
863                 if ((se = matchchar(s, c, '}')) == NULL) {
864                         rpmError(RPMERR_BADSPEC, _("Unterminated %c: %s"), c, s);
865                         rc = 1;
866                         continue;
867                 }
868                 f = ++s;/* skip { */
869                 se++;   /* skip } */
870                 while (strchr("!?", *f) != NULL) {
871                         switch(*f++) {
872                         case '!':
873                                 negate = (++negate % 2);
874                                 break;
875                         case '?':
876                                 chkexist++;
877                                 break;
878                         }
879                 }
880                 for (fe = f; (c = *fe) && !strchr(":}", c);)
881                         fe++;
882                 if (c == ':') {
883                         g = fe + 1;
884                         ge = se - 1;
885                 }
886                 break;
887         }
888
889         /* XXX Everything below expects fe > f */
890         fn = (fe - f);
891         gn = (ge - g);
892         if (fn <= 0) {
893                 rpmError(RPMERR_BADSPEC, _("Empty token"));
894                 s = se;
895                 continue;
896         }
897
898         if (mb->macro_trace)
899                 printMacro(mb, s, se);
900
901         /* Expand builtin macros */
902         if (STREQ("global", f, fn)) {
903                 s = doDefine(mb, se, RMIL_GLOBAL, 1);
904                 continue;
905         }
906         if (STREQ("define", f, fn)) {
907                 s = doDefine(mb, se, mb->depth, 0);
908                 continue;
909         }
910         if (STREQ("undefine", f, fn)) {
911                 s = doUndefine(mb->mc, se);
912                 continue;
913         }
914
915         if (STREQ("echo", f, fn) ||
916             STREQ("warn", f, fn) ||
917             STREQ("error", f, fn)) {
918                 int waserror = 0;
919                 if (STREQ("error", f, fn))
920                         waserror = 1;
921                 if (g < ge)
922                         doOutput(mb, waserror, g, gn);
923                 else
924                         doOutput(mb, waserror, f, fn);
925                 s = se;
926                 continue;
927         }
928
929         if (STREQ("trace", f, fn)) {
930                 /* XXX TODO restore expand_trace/macro_trace to 0 on return */
931                 mb->expand_trace = mb->macro_trace = (negate ? 0 : mb->depth);
932                 if (mb->depth == 1) {
933                         print_macro_trace = mb->macro_trace;
934                         print_expand_trace = mb->expand_trace;
935                 }
936                 s = se;
937                 continue;
938         }
939
940         if (STREQ("dump", f, fn)) {
941                 dumpMacroTable(mb->mc);
942                 if (*se == '\n')
943                         se++;
944                 s = se;
945                 continue;
946         }
947
948         /* XXX necessary but clunky */
949         if (STREQ("basename", f, fn) ||
950             STREQ("suffix", f, fn) ||
951             STREQ("expand", f, fn) ||
952             STREQ("uncompress", f, fn) ||
953             STREQ("S", f, fn) ||
954             STREQ("P", f, fn) ||
955             STREQ("F", f, fn)) {
956                 doFoo(mb, f, fn, g, gn);
957                 s = se;
958                 continue;
959         }
960
961         /* Expand defined macros */
962         mep = findEntry(mb->mc, f, fn);
963         me = (mep ? *mep : NULL);
964
965         /* XXX Special processing for flags */
966         if (*f == '-') {
967                 if (me)
968                         me->used++;     /* Mark macro as used */
969                 if ((me == NULL && !negate) ||  /* Without -f, skip %{-f...} */
970                     (me != NULL && negate)) {   /* With -f, skip %{!-f...} */
971                         s = se;
972                         continue;
973                 }
974
975                 if (g && g < ge) {              /* Expand X in %{-f:X} */
976                         rc = expandT(mb, g, gn);
977                 } else
978                 if (me->body && *me->body) {    /* Expand %{-f}/%{-f*} */
979                         rc = expandT(mb, me->body, strlen(me->body));
980                 }
981                 s = se;
982                 continue;
983         }
984
985         /* XXX Special processing for macro existence */
986         if (chkexist) {
987                 if ((me == NULL && !negate) ||  /* Without -f, skip %{?f...} */
988                     (me != NULL && negate)) {   /* With -f, skip %{!?f...} */
989                         s = se;
990                         continue;
991                 }
992                 if (g && g < ge) {              /* Expand X in %{?f:X} */
993                         rc = expandT(mb, g, gn);
994                 } else
995                 if (me->body && *me->body) {    /* Expand %{?f}/%{?f*} */
996                         rc = expandT(mb, me->body, strlen(me->body));
997                 }
998                 s = se;
999                 continue;
1000         }
1001         
1002         if (me == NULL) {       /* leave unknown %... as is */
1003 #ifndef HACK
1004 #if DEAD
1005                 /* XXX hack to skip over empty arg list */
1006                 if (fn == 1 && *f == '*') {
1007                         s = se;
1008                         continue;
1009                 }
1010 #endif
1011                 /* XXX hack to permit non-overloaded %foo to be passed */
1012                 c = '%';        /* XXX only need to save % */
1013                 SAVECHAR(mb, c);
1014 #else
1015                 rpmError(RPMERR_BADSPEC, _("Macro %%%.*s not found, skipping"), fn, f);
1016                 s = se;
1017 #endif
1018                 continue;
1019         }
1020
1021         /* Setup args for "%name " macros with opts */
1022         if (me && me->opts != NULL) {
1023                 if (grab)
1024                         se = grabArgs(mb, me, fe);
1025                 else {
1026                     addMacro(mb->mc, "*", NULL, "", mb->depth);
1027                 }
1028         }
1029
1030         /* Recursively expand body of macro */
1031         mb->s = me->body;
1032         rc = expandMacro(mb);
1033         if (rc == 0)
1034                 me->used++;     /* Mark macro as used */
1035
1036         /* Free args for "%name " macros with opts */
1037         if (me->opts != NULL)
1038                 freeArgs(mb);
1039
1040         s = se;
1041     }
1042
1043     *mb->t = '\0';
1044     mb->s = s;
1045     mb->depth--;
1046     if (rc != 0 || mb->expand_trace)
1047         printExpansion(mb, t, mb->t);
1048     return rc;
1049 }
1050
1051 /* =============================================================== */
1052 const char *
1053 getMacroBody(MacroContext *mc, const char *name)
1054 {
1055     MacroEntry **mep = findEntry(mc, name, 0);
1056     MacroEntry *me = (mep ? *mep : NULL);
1057     return ( me ? me->body : (const char *)NULL );
1058 }
1059
1060 /* =============================================================== */
1061 int
1062 expandMacros(void *spec, MacroContext *mc, char *s, size_t slen)
1063 {
1064         MacroBuf macrobuf, *mb = &macrobuf;
1065         char *tbuf;
1066         int rc;
1067
1068         if (s == NULL || slen <= 0)
1069                 return 0;
1070
1071         tbuf = alloca(slen + 1);
1072         memset(tbuf, 0, (slen + 1));
1073
1074         mb->s = s;
1075         mb->t = tbuf;
1076         mb->nb = slen;
1077         mb->depth = 0;
1078         mb->macro_trace = print_macro_trace;
1079         mb->expand_trace = print_expand_trace;
1080
1081         mb->spec = spec;        /* (future) %file expansion info */
1082         mb->mc = mc;
1083
1084         rc = expandMacro(mb);
1085
1086         if (mb->nb <= 0)
1087                 rpmError(RPMERR_BADSPEC, _("Target buffer overflow"));
1088
1089         tbuf[slen] = '\0';      /* XXX just in case */
1090         strncpy(s, tbuf, (slen - mb->nb + 1));
1091
1092         return rc;
1093 }
1094
1095 void
1096 addMacro(MacroContext *mc, const char *n, const char *o, const char *b, int level)
1097 {
1098         MacroEntry **mep;
1099
1100         /* If new name, expand macro table */
1101         if ((mep = findEntry(mc, n, 0)) == NULL) {
1102                 if (mc->firstFree == mc->macrosAllocated)
1103                         expandMacroTable(mc);
1104                 mep = mc->macroTable + mc->firstFree++;
1105         }
1106
1107         /* Push macro over previous definition */
1108         pushMacro(mep, n, o, b, level);
1109
1110         /* If new name, sort macro table */
1111         if ((*mep)->prev == NULL)
1112                 sortMacroTable(mc);
1113 }
1114
1115 void
1116 delMacro(MacroContext *mc, const char *name)
1117 {
1118         MacroEntry **mep = findEntry(mc, name, 0);
1119
1120         /* If name exists, pop entry */
1121         if ((mep = findEntry(mc, name, 0)) != NULL)
1122                 popMacro(mep);
1123 }
1124
1125 void
1126 initMacros(MacroContext *mc, const char *macrofile)
1127 {
1128         char *m, *mfile, *me;
1129         static int first = 1;
1130
1131         /* XXX initialization should be per macro context, not per execution */
1132         if (first) {
1133                 mc->macroTable = NULL;
1134                 expandMacroTable(mc);
1135                 max_macro_depth = 2;    /* XXX Assume good ol' macro expansion */
1136                 first = 0;
1137         }
1138
1139         if (macrofile == NULL)
1140                 return;
1141
1142         for (mfile = m = strdup(macrofile); *mfile; mfile = me) {
1143                 FILE *fp;
1144                 char buf[BUFSIZ];
1145                 MacroBuf macrobuf, *mb = &macrobuf;
1146
1147                 if ((me = strchr(mfile, ':')) != NULL)
1148                         *me++ = '\0';
1149                 else
1150                         me = mfile + strlen(mfile);
1151
1152                 if ((fp=fopen(mfile, "r")) == NULL)
1153                         continue;
1154
1155                 /* XXX Assume new fangled macro expansion */
1156                 max_macro_depth = 16;
1157
1158                 while(rdcl(buf, sizeof(buf), fp, 1) != NULL) {
1159                         char c, *n;
1160
1161                         n = buf;
1162                         SKIPBLANK(n, c);
1163
1164                         if (c != '%')
1165                                 continue;
1166                         n++;
1167                         mb->mc = mc;    /* XXX just enough to get by */
1168                         doDefine(mb, n, RMIL_MACROFILES, 0);
1169                 }
1170                 fclose(fp);
1171         }
1172         if (m)
1173                 free(m);
1174 }
1175
1176 void
1177 freeMacros(MacroContext *mc)
1178 {
1179         int i;
1180     
1181         for (i = 0; i < mc->firstFree; i++) {
1182                 MacroEntry *me;
1183                 while ((me = mc->macroTable[i]) != NULL) {
1184                         /* XXX cast to workaround const */
1185                         if ((mc->macroTable[i] = me->prev) == NULL)
1186                                 FREE((char *)me->name);
1187                         FREE((char *)me->opts);
1188                         FREE((char *)me->body);
1189                         FREE(me);
1190                 }
1191         }
1192         FREE(mc->macroTable);
1193 }
1194
1195 /* =============================================================== */
1196
1197 int isCompressed(char *file, int *compressed)
1198 {
1199     int fd, nb, rderrno;
1200     unsigned char magic[4];
1201
1202     *compressed = COMPRESSED_NOT;
1203
1204     if ((fd = open(file, O_RDONLY)) < 0) {
1205         rpmError(RPMERR_BADSPEC, _("File %s: %s"), file, strerror(errno));
1206         return 1;
1207     }
1208     nb = read(fd, magic, sizeof(magic));
1209     rderrno = errno;
1210     close(fd);
1211
1212     if (nb < 0) {
1213         rpmError(RPMERR_BADSPEC, _("File %s: %s"), file, strerror(rderrno));
1214         return 1;
1215     } else if (nb < sizeof(magic)) {
1216         rpmError(RPMERR_BADSPEC, _("File %s is smaller than %d bytes"),
1217                 file, sizeof(magic));
1218         return 0;
1219     }
1220
1221     if (((magic[0] == 0037) && (magic[1] == 0213)) ||  /* gzip */
1222         ((magic[0] == 0037) && (magic[1] == 0236)) ||  /* old gzip */
1223         ((magic[0] == 0037) && (magic[1] == 0036)) ||  /* pack */
1224         ((magic[0] == 0037) && (magic[1] == 0240)) ||  /* SCO lzh */
1225         ((magic[0] == 0037) && (magic[1] == 0235)) ||  /* compress */
1226         ((magic[0] == 0120) && (magic[1] == 0113) &&
1227          (magic[2] == 0003) && (magic[3] == 0004))     /* pkzip */
1228         ) {
1229         *compressed = COMPRESSED_OTHER;
1230     } else if ((magic[0] == 'B') && (magic[1] == 'Z')) {
1231         *compressed = COMPRESSED_BZIP2;
1232     }
1233
1234     return 0;
1235 }
1236
1237 /* =============================================================== */
1238
1239 #ifdef DEBUG_MACROS
1240
1241 MacroContext mc = { NULL, 0, 0};
1242 char *macrofile = "./paths:./environment:./macros";
1243 char *testfile = "./test";
1244
1245 int
1246 main(int argc, char *argv[])
1247 {
1248         char buf[BUFSIZ];
1249         FILE *fp;
1250         int x;
1251
1252         initMacros(&mc, macrofile);
1253         dumpMacroTable(&mc);
1254
1255         if ((fp = fopen(testfile, "r")) != NULL) {
1256                 while(fgets(buf, sizeof(buf), fp)) {
1257                         buf[strlen(buf)-1] = '\0';
1258                         x = expandMacros(NULL, &mc, buf, sizeof(buf));
1259                         fprintf(stderr, "%d->%s\n", x, buf);
1260                         memset(buf, 0, sizeof(buf));
1261                 }
1262                 fclose(fp);
1263         }
1264
1265         while(fgets(buf, sizeof(buf), stdin)) {
1266                 buf[strlen(buf)-1] = '\0';
1267                 x = expandMacros(NULL, &mc, buf, sizeof(buf));
1268                 fprintf(stderr, "%d->%s\n <-\n", x, buf);
1269                 memset(buf, 0, sizeof(buf));
1270         }
1271
1272         return 0;
1273 }
1274 #endif  /* DEBUG_MACROS */