9 #define isblank(_c) ((_c) == ' ' || (_c) == '\t')
11 #define iseol(_c) ((_c) == '\n' || (_c) == '\r')
13 #define STREQ(_t, _f, _fn) ((_fn) == (sizeof(_t)-1) && !strncmp((_t), (_f), (_fn)))
14 #define FREE(_x) { if (_x) free((void *)_x); (_x) = NULL; }
17 #include <sys/types.h>
24 #define rpmError fprintf
25 #define RPMERR_BADSPEC stderr
29 #define vmefail() (exit(1), NULL)
30 #define xfree(_p) free((void *)_p)
31 #define urlPath(_xr, _r) *(_r) = (_xr)
34 #define Fopen(_path, _fmode) fopen(_path, "r");
36 #define Fstrerror(_fd) strerror(errno)
43 #include <rpmio_internal.h>
49 struct MacroContext rpmGlobalMacroContext;
50 struct MacroContext rpmCLIMacroContext;
52 typedef struct MacroBuf {
53 const char *s; /* text to expand */
54 char *t; /* expansion buffer */
55 size_t nb; /* no. bytes remaining in expansion buffer */
56 int depth; /* current expansion depth */
57 int macro_trace; /* pre-print macro to expand? */
58 int expand_trace; /* post-print macro expansion? */
59 void *spec; /* (future) %file expansion info */
63 #define SAVECHAR(_mb, _c) { *(_mb)->t = (_c), (_mb)->t++, (_mb)->nb--; }
65 static int expandMacro(MacroBuf *mb);
67 #define MAX_MACRO_DEPTH 16
68 int max_macro_depth = MAX_MACRO_DEPTH;
71 int print_macro_trace = 0;
72 int print_expand_trace = 0;
74 int print_macro_trace = 0;
75 int print_expand_trace = 0;
78 #define MACRO_CHUNK_SIZE 16
80 /* =============================================================== */
83 compareMacroName(const void *ap, const void *bp)
85 MacroEntry *ame = *((MacroEntry **)ap);
86 MacroEntry *bme = *((MacroEntry **)bp);
88 if (ame == NULL && bme == NULL)
94 return strcmp(ame->name, bme->name);
98 expandMacroTable(MacroContext *mc)
100 if (mc->macroTable == NULL) {
101 mc->macrosAllocated = MACRO_CHUNK_SIZE;
102 mc->macroTable = (MacroEntry **)
103 xmalloc(sizeof(*(mc->macroTable)) * mc->macrosAllocated);
106 mc->macrosAllocated += MACRO_CHUNK_SIZE;
107 mc->macroTable = (MacroEntry **)
108 xrealloc(mc->macroTable, sizeof(*(mc->macroTable)) *
109 mc->macrosAllocated);
111 memset(&mc->macroTable[mc->firstFree], 0, MACRO_CHUNK_SIZE * sizeof(*(mc->macroTable)));
115 sortMacroTable(MacroContext *mc)
119 qsort(mc->macroTable, mc->firstFree, sizeof(*(mc->macroTable)),
122 /* Empty pointers are now at end of table. Reset first free index. */
123 for (i = 0; i < mc->firstFree; i++) {
124 if (mc->macroTable[i] != NULL)
132 rpmDumpMacroTable(MacroContext * mc, FILE * fp)
139 mc = &rpmGlobalMacroContext;
143 fprintf(fp, "========================\n");
144 for (i = 0; i < mc->firstFree; i++) {
146 if ((me = mc->macroTable[i]) == NULL) {
147 /* XXX this should never happen */
151 fprintf(fp, "%3d%c %s", me->level,
152 (me->used > 0 ? '=' : ':'), me->name);
153 if (me->opts && *me->opts)
154 fprintf(fp, "(%s)", me->opts);
155 if (me->body && *me->body)
156 fprintf(fp, "\t%s", me->body);
160 fprintf(fp, _("======================== active %d empty %d\n"),
165 findEntry(MacroContext *mc, const char *name, size_t namelen)
167 MacroEntry keybuf, *key, **ret;
171 mc = &rpmGlobalMacroContext;
176 strncpy(namebuf, name, namelen);
177 namebuf[namelen] = '\0';
182 memset(key, 0, sizeof(*key));
183 key->name = (char *)name;
184 ret = (MacroEntry **)bsearch(&key, mc->macroTable, mc->firstFree,
185 sizeof(*(mc->macroTable)), compareMacroName);
186 /* XXX TODO: find 1st empty slot and return that */
190 /* =============================================================== */
192 /* fgets analogue that reads \ continuations. Last newline always trimmed. */
195 rdcl(char *buf, size_t size, FD_t fd, int escapes)
204 if (fgets(q, size, (FILE *)fdGetFp(fd)) == NULL)
208 for (q += nb - 1; nb > 0 && iseol(*q); q--)
210 if (!(nb > 0 && *q == '\\')) { /* continue? */
211 *(++q) = '\0'; /* trim trailing \r, \n */
214 if (escapes) { /* copy escape too */
219 if (*q == '\r') /* XXX avoid \r madness */
221 *(++q) = '\0'; /* next char in buf */
223 return (nread > 0 ? buf : NULL);
226 /* Return text between pl and matching pr */
229 matchchar(const char *p, char pl, char pr)
234 while ((c = *p++) != '\0') {
235 if (c == '\\') { /* Ignore escaped chars */
240 if (--lvl <= 0) return --p;
244 return (const char *)NULL;
248 printMacro(MacroBuf *mb, const char *s, const char *se)
251 const char *ellipsis;
254 if (s >= se) { /* XXX just in case */
255 fprintf(stderr, _("%3d>%*s(empty)"), mb->depth,
256 (2 * mb->depth + 1), "");
263 /* Print only to first end-of-line (or end-of-string). */
264 for (senl = se; *senl && !iseol(*senl); senl++)
267 /* Limit trailing non-trace output */
268 choplen = 61 - (2 * mb->depth);
269 if ((senl - s) > choplen) {
275 /* Substitute caret at end-of-macro position */
276 fprintf(stderr, "%3d>%*s%%%.*s^", mb->depth,
277 (2 * mb->depth + 1), "", (int)(se - s), s);
278 if (se[1] != '\0' && (senl - (se+1)) > 0)
279 fprintf(stderr, "%-.*s%s", (int)(senl - (se+1)), se+1, ellipsis);
280 fprintf(stderr, "\n");
284 printExpansion(MacroBuf *mb, const char *t, const char *te)
286 const char *ellipsis;
290 fprintf(stderr, _("%3d<%*s(empty)\n"), mb->depth, (2 * mb->depth + 1), "");
294 /* Shorten output which contains newlines */
295 while (te > t && iseol(te[-1]))
301 /* Skip to last line of expansion */
302 while ((tenl = strchr(t, '\n')) && tenl < te)
305 /* Limit expand output */
306 choplen = 61 - (2 * mb->depth);
307 if ((te - t) > choplen) {
313 fprintf(stderr, "%3d<%*s", mb->depth, (2 * mb->depth + 1), "");
315 fprintf(stderr, "%.*s%s", (int)(te - t), t, ellipsis);
316 fprintf(stderr, "\n");
319 #define SKIPBLANK(_s, _c) \
320 while (((_c) = *(_s)) && isblank(_c)) \
323 #define SKIPNONBLANK(_s, _c) \
324 while (((_c) = *(_s)) && !(isblank(_c) || iseol(_c))) \
327 #define COPYNAME(_ne, _s, _c) \
328 { SKIPBLANK(_s,_c); \
329 while(((_c) = *(_s)) && (isalnum(_c) || (_c) == '_')) \
330 *(_ne)++ = *(_s)++; \
334 #define COPYOPTS(_oe, _s, _c) \
335 { while(((_c) = *(_s)) && (_c) != ')') \
336 *(_oe)++ = *(_s)++; \
340 #define COPYBODY(_be, _s, _c) \
341 { while(((_c) = *(_s)) && !iseol(_c)) { \
344 *(_be)++ = *(_s)++; \
349 /* Save source and expand field into target */
351 expandT(MacroBuf *mb, const char *f, size_t flen)
354 const char *s = mb->s;
357 sbuf = alloca(flen + 1);
358 memset(sbuf, 0, (flen + 1));
360 strncpy(sbuf, f, flen);
363 rc = expandMacro(mb);
369 /* Save target and expand sbuf into target */
371 expandS(MacroBuf *mb, char *tbuf, size_t tbuflen)
373 const char *t = mb->t;
379 rc = expandMacro(mb);
387 expandU(MacroBuf *mb, char *u, size_t ulen)
389 const char *s = mb->s;
395 tbuf = alloca(ulen + 1);
396 memset(tbuf, 0, (ulen + 1));
401 rc = expandMacro(mb);
403 tbuf[ulen] = '\0'; /* XXX just in case */
405 strncpy(u, tbuf, (ulen - mb->nb + 1));
415 doShellEscape(MacroBuf *mb, const char *cmd, size_t clen)
422 strncpy(pcmd, cmd, clen);
424 rc = expandU(mb, pcmd, sizeof(pcmd));
428 if ((shf = popen(pcmd, "r")) == NULL)
430 while(mb->nb > 0 && (c = fgetc(shf)) != EOF)
434 /* XXX delete trailing \r \n */
435 while (iseol(mb->t[-1])) {
443 doDefine(MacroBuf *mb, const char *se, int level, int expandbody)
446 char buf[BUFSIZ], *n = buf, *ne = n;
455 /* Copy opts (if present) */
464 /* Copy body, skipping over escaped newlines */
467 if (c == '{') { /* XXX permit silent {...} grouping */
468 if ((se = matchchar(s, c, '}')) == NULL) {
469 rpmError(RPMERR_BADSPEC, _("Macro %%%s has unterminated body"), n);
470 se = s; /* XXX W2DO? */
473 s++; /* XXX skip { */
474 strncpy(b, s, (se - s));
477 se++; /* XXX skip } */
478 s = se; /* move scan forward */
479 } else { /* otherwise free-field */
482 /* Trim trailing blanks/newlines */
483 while (--be >= b && (c = *be) && (isblank(c) || iseol(c)))
485 *(++be) = '\0'; /* one too far */
488 /* Move scan over body */
493 /* Names must start with alphabetic or _ and be at least 3 chars */
494 if (!((c = *n) && (isalpha(c) || c == '_') && (ne - n) > 2)) {
495 rpmError(RPMERR_BADSPEC, _("Macro %%%s has illegal name (%%define)"), n);
499 /* Options must be terminated with ')' */
500 if (o && oc != ')') {
501 rpmError(RPMERR_BADSPEC, _("Macro %%%s has unterminated opts"), n);
506 rpmError(RPMERR_BADSPEC, _("Macro %%%s has empty body"), n);
510 if (expandbody && expandU(mb, b, (&buf[sizeof(buf)] - b))) {
511 rpmError(RPMERR_BADSPEC, _("Macro %%%s failed to expand"), n);
515 addMacro(mb->mc, n, o, b, (level - 1));
521 doUndefine(MacroContext *mc, const char *se)
524 char buf[BUFSIZ], *n = buf, *ne = n;
529 /* Move scan over body */
534 /* Names must start with alphabetic or _ and be at least 3 chars */
535 if (!((c = *n) && (isalpha(c) || c == '_') && (ne - n) > 2)) {
536 rpmError(RPMERR_BADSPEC, _("Macro %%%s has illegal name (%%undefine)"), n);
547 dumpME(const char *msg, MacroEntry *me)
550 fprintf(stderr, "%s", msg);
551 fprintf(stderr, "\tme %p", me);
553 fprintf(stderr,"\tname %p(%s) prev %p",
554 me->name, me->name, me->prev);
555 fprintf(stderr, "\n");
560 pushMacro(MacroEntry **mep, const char *n, const char *o, const char *b, int level)
562 MacroEntry *prev = (*mep ? *mep : NULL);
563 MacroEntry *me = (MacroEntry *) xmalloc(sizeof(*me));
566 me->name = (prev ? prev->name : xstrdup(n));
567 me->opts = (o ? xstrdup(o) : NULL);
568 me->body = xstrdup(b ? b : "");
575 popMacro(MacroEntry **mep)
577 MacroEntry *me = (*mep ? *mep : NULL);
580 /* XXX cast to workaround const */
581 if ((*mep = me->prev) == NULL)
590 freeArgs(MacroBuf *mb)
592 MacroContext *mc = mb->mc;
596 /* Delete dynamic macro definitions */
597 for (i = 0; i < mc->firstFree; i++) {
598 MacroEntry **mep, *me;
600 mep = &mc->macroTable[i];
603 if (me == NULL) /* XXX this should never happen */
605 if (me->level < mb->depth)
607 if (strlen(me->name) == 1 && strchr("#*0", *me->name)) {
608 if (*me->name == '*' && me->used > 0)
610 /* XXX skip test for %# %* %0 */
611 } else if (!skiptest && me->used <= 0) {
613 rpmError(RPMERR_BADSPEC, _("Macro %%%s (%s) was not used below level %d"),
614 me->name, me->body, me->level);
622 /* If any deleted macros, sort macro table */
628 grabArgs(MacroBuf *mb, const MacroEntry *me, const char *se, char lastc)
630 char buf[BUFSIZ], *b, *be;
632 const char *opts, *o;
639 int saveoptind; /* XXX optind must be saved on non-linux */
641 /* Copy macro name as argv[0] */
647 argc++; /* XXX count argv[0] */
649 addMacro(mb->mc, "0", NULL, b, mb->depth);
651 /* Copy args into buf until lastc */
653 b = be; /* Save beginning of args */
654 while ((c = *se) != 0) {
664 while (!(isblank(c) || c == lastc)) {
666 if ((c = *se) == '\0')
675 * The macro %* analoguous to the shell's $* means "Pass all non-macro
676 * parameters." Consequently, there needs to be a macro that means "Pass all
677 * (including macro parameters) options". This is useful for verifying
678 * parameters during expansion and yet transparently passing all parameters
679 * through for higher level processing (e.g. %description and/or %setup).
680 * This is the (potential) justification for %{**} ...
682 /* Add unexpanded args as macro */
683 addMacro(mb->mc, "**", NULL, b, mb->depth);
686 /* XXX if macros can be passed as args ... */
687 expandU(mb, buf, sizeof(buf));
690 /* Build argv array */
691 argv = (const char **)alloca((argc + 1) * sizeof(char *));
693 for (c = 0; c < argc; c++) {
695 if ((be = strchr(b, ' ')) == NULL)
704 /* First count number of options ... */
705 saveoptind = optind; /* XXX optind must be saved on non-linux */
707 optc++; /* XXX count argv[0] too */
708 while((c = getopt(argc, (char **)argv, opts)) != -1) {
709 if (!(c != '?' && (o = strchr(opts, c)))) {
710 rpmError(RPMERR_BADSPEC, _("Unknown option %c in %s(%s)"),
717 /* ... then allocate storage ... */
718 opte = optc + (argc - optind);
719 optv = (const char **)alloca((opte + 1) * sizeof(char *));
723 /* ... and finally define option macros. */
724 optind = saveoptind; /* XXX optind must be restored on non-linux */
726 optc++; /* XXX count optv[0] */
727 while((c = getopt(argc, (char **)argv, opts)) != -1) {
738 sprintf(aname, "-%c", c);
739 addMacro(mb->mc, aname, NULL, b, mb->depth);
741 sprintf(aname, "-%c*", c);
742 addMacro(mb->mc, aname, NULL, optarg, mb->depth);
748 /* Add macro for each arg. Concatenate args for !*. */
750 for (c = optind; c < argc; c++) {
751 sprintf(aname, "%d", (c - optind + 1));
752 addMacro(mb->mc, aname, NULL, argv[c], mb->depth);
754 if (be > b) *be++ = ' ';
759 optv[optc] = argv[c];
763 /* Add arg count as macro. */
764 sprintf(aname, "%d", (argc - optind));
765 addMacro(mb->mc, "#", NULL, aname, mb->depth);
767 /* Add unexpanded args as macro. */
768 addMacro(mb->mc, "*", NULL, b, mb->depth);
774 doOutput(MacroBuf *mb, int waserror, const char *msg, size_t msglen)
778 strncpy(buf, msg, msglen);
780 expandU(mb, buf, sizeof(buf));
782 rpmError(RPMERR_BADSPEC, "%s", buf);
784 fprintf(stderr, "%s", buf);
788 doFoo(MacroBuf *mb, int negate, const char *f, size_t fn, const char *g, size_t glen)
790 char buf[BUFSIZ], *b = NULL, *be;
795 strncpy(buf, g, glen);
797 expandU(mb, buf, sizeof(buf));
799 if (STREQ("basename", f, fn)) {
800 if ((b = strrchr(buf, '/')) == NULL)
803 /* XXX watchout for conflict with %dir */
804 } else if (STREQ("dirname", f, fn)) {
805 if ((b = strrchr(buf, '/')) != NULL)
809 } else if (STREQ("suffix", f, fn)) {
810 if ((b = strrchr(buf, '.')) != NULL)
812 } else if (STREQ("expand", f, fn)) {
814 } else if (STREQ("verbose", f, fn)) {
816 b = (rpmIsVerbose() ? NULL : buf);
818 b = (rpmIsVerbose() ? buf : NULL);
819 } else if (STREQ("url2path", f, fn) || STREQ("u2p", f, fn)) {
820 (void)urlPath(buf, (const char **)&b);
821 if (*b == '\0') b = "/";
822 } else if (STREQ("uncompress", f, fn)) {
824 for (b = buf; (c = *b) && isblank(c);)
826 for (be = b; (c = *be) && !isblank(c);)
830 isCompressed(b, &compressed);
834 case 0: /* COMPRESSED_NOT */
835 sprintf(be, "%%_cat %s", b);
837 case 1: /* COMPRESSED_OTHER */
838 sprintf(be, "%%_gzip -dc %s", b);
840 case 2: /* COMPRESSED_BZIP2 */
841 sprintf(be, "%%_bzip2 %s", b);
845 } else if (STREQ("S", f, fn)) {
846 for (b = buf; (c = *b) && isdigit(c);)
848 if (!c) { /* digit index */
850 sprintf(b, "%%SOURCE%s", buf);
853 } else if (STREQ("P", f, fn)) {
854 for (b = buf; (c = *b) && isdigit(c);)
856 if (!c) { /* digit index */
858 sprintf(b, "%%PATCH%s", buf);
861 } else if (STREQ("F", f, fn)) {
862 b = buf + strlen(buf) + 1;
863 sprintf(b, "file%s.file", buf);
867 expandT(mb, b, strlen(b));
871 /* The main recursion engine */
874 expandMacro(MacroBuf *mb)
878 const char *s = mb->s, *se;
882 char *t = mb->t; /* save expansion pointer for printExpand */
889 if (++mb->depth > max_macro_depth) {
890 rpmError(RPMERR_BADSPEC, _("Recursion depth(%d) greater than max(%d)"),
891 mb->depth, max_macro_depth);
893 mb->expand_trace = 1;
897 while (rc == 0 && mb->nb > 0 && (c = *s) != '\0') {
899 /* Copy text until next macro */
904 s++; /* skip first % in %% */
909 /*@notreached@*/ break;
912 /* Expand next macro */
915 if (mb->depth > 1) /* XXX full expansion for outermost level */
916 t = mb->t; /* save expansion pointer for printExpand */
921 default: /* %name substitution */
922 while (strchr("!?", *s) != NULL) {
925 negate = (++negate % 2);
935 while((c = *se) && (isalnum(c) || c == '_'))
937 /* Recognize non-alnum macros too */
941 if (*se == '*') se++;
950 /* For "%name " macros ... */
951 if ((c = *fe) && isblank(c))
954 case '(': /* %(...) shell escape */
955 if ((se = matchchar(s, c, ')')) == NULL) {
956 rpmError(RPMERR_BADSPEC, _("Unterminated %c: %s"), c, s);
961 printMacro(mb, s, se+1);
964 rc = doShellEscape(mb, s, (se - s));
969 /*@notreached@*/ break;
970 case '{': /* %{...}/%{...:...} substitution */
971 if ((se = matchchar(s, c, '}')) == NULL) {
972 rpmError(RPMERR_BADSPEC, _("Unterminated %c: %s"), c, s);
978 while (strchr("!?", *f) != NULL) {
981 negate = (++negate % 2);
988 for (fe = f; (c = *fe) && !strchr(" :}", c);)
1004 /* XXX Everything below expects fe > f */
1008 /* XXX Process % in unknown context */
1009 c = '%'; /* XXX only need to save % */
1012 rpmError(RPMERR_BADSPEC, _("A %% is followed by an unparseable macro"));
1018 if (mb->macro_trace)
1019 printMacro(mb, s, se);
1021 /* Expand builtin macros */
1022 if (STREQ("global", f, fn)) {
1023 s = doDefine(mb, se, RMIL_GLOBAL, 1);
1026 if (STREQ("define", f, fn)) {
1027 s = doDefine(mb, se, mb->depth, 0);
1030 if (STREQ("undefine", f, fn)) {
1031 s = doUndefine(mb->mc, se);
1035 if (STREQ("echo", f, fn) ||
1036 STREQ("warn", f, fn) ||
1037 STREQ("error", f, fn)) {
1039 if (STREQ("error", f, fn))
1042 doOutput(mb, waserror, g, gn);
1044 doOutput(mb, waserror, f, fn);
1049 if (STREQ("trace", f, fn)) {
1050 /* XXX TODO restore expand_trace/macro_trace to 0 on return */
1051 mb->expand_trace = mb->macro_trace = (negate ? 0 : mb->depth);
1052 if (mb->depth == 1) {
1053 print_macro_trace = mb->macro_trace;
1054 print_expand_trace = mb->expand_trace;
1060 if (STREQ("dump", f, fn)) {
1061 rpmDumpMacroTable(mb->mc, NULL);
1068 /* XXX necessary but clunky */
1069 if (STREQ("basename", f, fn) ||
1070 STREQ("suffix", f, fn) ||
1071 STREQ("expand", f, fn) ||
1072 STREQ("verbose", f, fn) ||
1073 STREQ("uncompress", f, fn) ||
1074 STREQ("url2path", f, fn) ||
1075 STREQ("u2p", f, fn) ||
1076 STREQ("S", f, fn) ||
1077 STREQ("P", f, fn) ||
1078 STREQ("F", f, fn)) {
1079 doFoo(mb, negate, f, fn, g, gn);
1084 /* Expand defined macros */
1085 mep = findEntry(mb->mc, f, fn);
1086 me = (mep ? *mep : NULL);
1088 /* XXX Special processing for flags */
1091 me->used++; /* Mark macro as used */
1092 if ((me == NULL && !negate) || /* Without -f, skip %{-f...} */
1093 (me != NULL && negate)) { /* With -f, skip %{!-f...} */
1098 if (g && g < ge) { /* Expand X in %{-f:X} */
1099 rc = expandT(mb, g, gn);
1101 if (me->body && *me->body) { /* Expand %{-f}/%{-f*} */
1102 rc = expandT(mb, me->body, strlen(me->body));
1108 /* XXX Special processing for macro existence */
1110 if ((me == NULL && !negate) || /* Without -f, skip %{?f...} */
1111 (me != NULL && negate)) { /* With -f, skip %{!?f...} */
1115 if (g && g < ge) { /* Expand X in %{?f:X} */
1116 rc = expandT(mb, g, gn);
1118 if (me && me->body && *me->body) { /* Expand %{?f}/%{?f*} */
1119 rc = expandT(mb, me->body, strlen(me->body));
1125 if (me == NULL) { /* leave unknown %... as is */
1128 /* XXX hack to skip over empty arg list */
1129 if (fn == 1 && *f == '*') {
1134 /* XXX hack to permit non-overloaded %foo to be passed */
1135 c = '%'; /* XXX only need to save % */
1138 rpmError(RPMERR_BADSPEC, _("Macro %%%.*s not found, skipping"), fn, f);
1144 /* Setup args for "%name " macros with opts */
1145 if (me && me->opts != NULL) {
1147 se = grabArgs(mb, me, fe, grab);
1149 addMacro(mb->mc, "**", NULL, "", mb->depth);
1150 addMacro(mb->mc, "*", NULL, "", mb->depth);
1151 addMacro(mb->mc, "#", NULL, "0", mb->depth);
1152 addMacro(mb->mc, "0", NULL, me->name, mb->depth);
1156 /* Recursively expand body of macro */
1157 if (me->body && *me->body) {
1159 rc = expandMacro(mb);
1161 me->used++; /* Mark macro as used */
1164 /* Free args for "%name " macros with opts */
1165 if (me->opts != NULL)
1174 if (rc != 0 || mb->expand_trace)
1175 printExpansion(mb, t, mb->t);
1179 /* =============================================================== */
1180 /* XXX this is used only in build/expression.c and will go away. */
1182 getMacroBody(MacroContext *mc, const char *name)
1184 MacroEntry **mep = findEntry(mc, name, 0);
1185 MacroEntry *me = (mep ? *mep : NULL);
1186 return ( me ? me->body : (const char *)NULL );
1189 /* =============================================================== */
1192 expandMacros(void *spec, MacroContext *mc, char *s, size_t slen)
1194 MacroBuf macrobuf, *mb = ¯obuf;
1198 if (s == NULL || slen <= 0)
1201 mc = &rpmGlobalMacroContext;
1203 tbuf = alloca(slen + 1);
1204 memset(tbuf, 0, (slen + 1));
1210 mb->macro_trace = print_macro_trace;
1211 mb->expand_trace = print_expand_trace;
1213 mb->spec = spec; /* (future) %file expansion info */
1216 rc = expandMacro(mb);
1219 rpmError(RPMERR_BADSPEC, _("Target buffer overflow"));
1221 tbuf[slen] = '\0'; /* XXX just in case */
1222 strncpy(s, tbuf, (slen - mb->nb + 1));
1228 addMacro(MacroContext *mc, const char *n, const char *o, const char *b, int level)
1233 mc = &rpmGlobalMacroContext;
1235 /* If new name, expand macro table */
1236 if ((mep = findEntry(mc, n, 0)) == NULL) {
1237 if (mc->firstFree == mc->macrosAllocated)
1238 expandMacroTable(mc);
1239 mep = mc->macroTable + mc->firstFree++;
1242 /* Push macro over previous definition */
1243 pushMacro(mep, n, o, b, level);
1245 /* If new name, sort macro table */
1246 if ((*mep)->prev == NULL)
1251 delMacro(MacroContext *mc, const char *n)
1256 mc = &rpmGlobalMacroContext;
1257 /* If name exists, pop entry */
1258 if ((mep = findEntry(mc, n, 0)) != NULL) {
1260 /* If deleted name, sort macro table */
1267 rpmDefineMacro(MacroContext *mc, const char *macro, int level)
1269 MacroBuf macrobuf, *mb = ¯obuf;
1271 /* XXX just enough to get by */
1272 mb->mc = (mc ? mc : &rpmGlobalMacroContext);
1273 (void)doDefine(mb, macro, level, 0);
1277 /* Load a macro context into rpmGlobalMacroContext */
1279 rpmLoadMacros(MacroContext * mc, int level)
1283 if (mc == NULL || mc == &rpmGlobalMacroContext)
1286 for (i = 0; i < mc->firstFree; i++) {
1287 MacroEntry **mep, *me;
1288 mep = &mc->macroTable[i];
1291 if (me == NULL) /* XXX this should never happen */
1293 addMacro(NULL, me->name, me->opts, me->body, (level - 1));
1298 rpmInitMacros(MacroContext *mc, const char *macrofiles)
1300 char *m, *mfile, *me;
1302 if (macrofiles == NULL)
1305 mc = &rpmGlobalMacroContext;
1307 for (mfile = m = xstrdup(macrofiles); *mfile; mfile = me) {
1311 for (me = mfile; (me = strchr(me, ':')) != NULL; me++) {
1312 if (!(me[1] == '/' && me[2] == '/'))
1316 if (me && *me == ':')
1319 me = mfile + strlen(mfile);
1321 /* Expand ~/ to $HOME */
1323 if (mfile[0] == '~' && mfile[1] == '/') {
1325 if ((home = getenv("HOME")) != NULL) {
1327 strncpy(buf, home, sizeof(buf));
1328 strncat(buf, "/", sizeof(buf) - strlen(buf));
1331 strncat(buf, mfile, sizeof(buf) - strlen(buf));
1332 buf[sizeof(buf)-1] = '\0';
1334 fd = Fopen(buf, "r.fpio");
1335 if (fd == NULL || Ferror(fd)) {
1340 /* XXX Assume new fangled macro expansion */
1341 max_macro_depth = 16;
1343 while(rdcl(buf, sizeof(buf), fd, 1) != NULL) {
1352 (void)rpmDefineMacro(NULL, n, RMIL_MACROFILES);
1359 /* Reload cmdline macros */
1360 rpmLoadMacros(&rpmCLIMacroContext, RMIL_CMDLINE);
1364 rpmFreeMacros(MacroContext *mc)
1369 mc = &rpmGlobalMacroContext;
1371 for (i = 0; i < mc->firstFree; i++) {
1373 while ((me = mc->macroTable[i]) != NULL) {
1374 /* XXX cast to workaround const */
1375 if ((mc->macroTable[i] = me->prev) == NULL)
1382 FREE(mc->macroTable);
1383 memset(mc, 0, sizeof(*mc));
1386 /* =============================================================== */
1387 int isCompressed(const char *file, int *compressed)
1392 unsigned char magic[4];
1394 *compressed = COMPRESSED_NOT;
1396 fd = Fopen(file, "r.ufdio");
1397 if (fd == NULL || Ferror(fd)) {
1399 rpmError(RPMERR_BADSPEC, _("File %s: %s"), file, Fstrerror(fd));
1403 nb = Fread(magic, sizeof(char), sizeof(magic), fd);
1405 rpmError(RPMERR_BADSPEC, _("File %s: %s"), file, Fstrerror(fd));
1407 } else if (nb < sizeof(magic)) {
1408 rpmError(RPMERR_BADSPEC, _("File %s is smaller than %d bytes"),
1409 file, sizeof(magic));
1418 if (((magic[0] == 0037) && (magic[1] == 0213)) || /* gzip */
1419 ((magic[0] == 0037) && (magic[1] == 0236)) || /* old gzip */
1420 ((magic[0] == 0037) && (magic[1] == 0036)) || /* pack */
1421 ((magic[0] == 0037) && (magic[1] == 0240)) || /* SCO lzh */
1422 ((magic[0] == 0037) && (magic[1] == 0235)) || /* compress */
1423 ((magic[0] == 0120) && (magic[1] == 0113) &&
1424 (magic[2] == 0003) && (magic[3] == 0004)) /* pkzip */
1426 *compressed = COMPRESSED_OTHER;
1427 } else if ((magic[0] == 'B') && (magic[1] == 'Z')) {
1428 *compressed = COMPRESSED_BZIP2;
1434 /* =============================================================== */
1435 /* Return concatenated and expanded macro list */
1437 rpmExpand(const char *arg, ...)
1439 char buf[BUFSIZ], *p, *pe;
1452 while ((s = va_arg(ap, const char *)) != NULL) {
1458 expandMacros(NULL, NULL, buf, sizeof(buf));
1459 return xstrdup(buf);
1463 rpmExpandNumeric(const char *arg)
1471 val = rpmExpand(arg, NULL);
1472 if (!(val && *val != '%'))
1474 else if (*val == 'Y' || *val == 'y')
1476 else if (*val == 'N' || *val == 'n')
1480 rc = strtol(val, &end, 0);
1481 if (!(end && *end == '\0'))
1489 /* XXX FIXME: ../sbin/./../bin/ */
1490 char *rpmCleanPath(char * path)
1497 /*fprintf(stderr, "*** got \"%.*s\"\trest \"%s\"\n", (t-path), path, s); */
1499 case ':': /* handle url's */
1500 if (s[1] == '/' && s[2] == '/') {
1506 /* Move parent dir forward */
1507 for (se = te + 1; se < t && *se != '/'; se++)
1509 if (se < t && *se == '/') {
1511 /*fprintf(stderr, "*** next pdir \"%.*s\"\n", (te-path), path); */
1515 while (t > path && t[-1] == '/')
1519 /* Leading .. is special */
1520 if (t == path && s[1] == '.') {
1524 /* Single . is special */
1525 if (t == path && s[1] == '\0') {
1528 /* Trim leading ./ , embedded ./ , trailing /. */
1529 if ((t == path || t[-1] == '/') && (s[1] == '/' || s[1] == '\0')) {
1530 /*fprintf(stderr, "*** Trim leading ./ , embedded ./ , trailing /.\n"); */
1534 /* Trim embedded /../ and trailing /.. */
1535 if (t > path && t[-1] == '/' && s[1] == '.' && (s[2] == '/' || s[2] == '\0')) {
1537 /* Move parent dir forward */
1539 for (--te; te > path && *te != '/'; te--)
1541 /*fprintf(stderr, "*** prev pdir \"%.*s\"\n", (te-path), path); */
1553 /* Trim trailing / (but leave single / alone) */
1554 if (t > &path[1] && t[-1] == '/')
1561 /* Return concatenated and expanded canonical path. */
1563 rpmGetPath(const char *path, ...)
1574 te = stpcpy(t, path);
1578 while ((s = va_arg(ap, const char *)) != NULL) {
1583 expandMacros(NULL, NULL, buf, sizeof(buf));
1585 return xstrdup( rpmCleanPath(buf) );
1588 /* Merge 3 args into path, any or all of which may be a url. */
1590 const char * rpmGenPath(const char * urlroot, const char * urlmdir,
1591 const char *urlfile)
1593 const char * xroot = rpmGetPath(urlroot, NULL), * root = xroot;
1594 const char * xmdir = rpmGetPath(urlmdir, NULL), * mdir = xmdir;
1595 const char * xfile = rpmGetPath(urlfile, NULL), * file = xfile;
1596 const char * result;
1597 const char * url = NULL;
1602 fprintf(stderr, "*** RGP xroot %s xmdir %s xfile %s\n", xroot, xmdir, xfile);
1603 ut = urlPath(xroot, &root);
1604 if (url == NULL && ut > URL_IS_DASH) {
1606 nurl = root - xroot;
1608 fprintf(stderr, "*** RGP ut %d root %s nurl %d\n", ut, root, nurl);
1610 if (root == NULL || *root == '\0') root = "/";
1612 ut = urlPath(xmdir, &mdir);
1613 if (url == NULL && ut > URL_IS_DASH) {
1615 nurl = mdir - xmdir;
1617 fprintf(stderr, "*** RGP ut %d mdir %s nurl %d\n", ut, mdir, nurl);
1619 if (mdir == NULL || *mdir == '\0') mdir = "/";
1621 ut = urlPath(xfile, &file);
1622 if (url == NULL && ut > URL_IS_DASH) {
1624 nurl = file - xfile;
1626 fprintf(stderr, "*** RGP ut %d file %s nurl %d\n", ut, file, nurl);
1629 if (url && nurl > 0) {
1630 char *t = strncpy(alloca(nurl+1), url, nurl);
1636 result = rpmGetPath(url, root, "/", mdir, "/", file, NULL);
1642 fprintf(stderr, "*** RGP result %s\n", result);
1646 /* =============================================================== */
1648 #if defined(DEBUG_MACROS)
1650 #if defined(EVAL_MACROS)
1652 char *macrofiles = "/usr/lib/rpm/macros:/etc/rpm/macros:~/.rpmmacros";
1655 main(int argc, char *argv[])
1659 extern char *optarg;
1662 while ((c = getopt(argc, argv, "f:")) != EOF ) {
1665 macrofiles = optarg;
1673 if (errflg || optind >= argc) {
1674 fprintf(stderr, "Usage: %s [-f macropath ] macro ...\n", argv[0]);
1678 rpmInitMacros(NULL, macrofiles);
1679 for ( ; optind < argc; optind++) {
1682 val = rpmGetPath(argv[optind], NULL);
1684 fprintf(stdout, "%s:\t%s\n", argv[optind], val);
1688 rpmFreeMacros(NULL);
1692 #else /* !EVAL_MACROS */
1694 char *macrofiles = "../macros:./testmacros";
1695 char *testfile = "./test";
1698 main(int argc, char *argv[])
1704 rpmInitMacros(NULL, macrofiles);
1705 rpmDumpMacroTable(NULL, NULL);
1707 if ((fp = fopen(testfile, "r")) != NULL) {
1708 while(rdcl(buf, sizeof(buf), fp, 1)) {
1709 x = expandMacros(NULL, NULL, buf, sizeof(buf));
1710 fprintf(stderr, "%d->%s\n", x, buf);
1711 memset(buf, 0, sizeof(buf));
1716 while(rdcl(buf, sizeof(buf), stdin, 1)) {
1717 x = expandMacros(NULL, NULL, buf, sizeof(buf));
1718 fprintf(stderr, "%d->%s\n <-\n", x, buf);
1719 memset(buf, 0, sizeof(buf));
1721 rpmFreeMacros(NULL);
1725 #endif /* EVAL_MACROS */
1726 #endif /* DEBUG_MACROS */