1 /** \ingroup rpmrc rpmio
2 * \file rpmio/macro.c
3 */
4
5 #include "system.h"
6 #include <stdarg.h>
7 #include <pthread.h>
8 #include <errno.h>
9 #ifdef HAVE_GETOPT_H
10 #include <getopt.h>
11 #else
12 extern char *optarg;
13 extern int optind;
14 #endif
15 #if HAVE_SCHED_GETAFFINITY
16 #include <sched.h>
17 #endif
18
19 #if !defined(isblank)
20 #define isblank(_c) ((_c) == ' ' || (_c) == '\t')
21 #endif
22 #define iseol(_c) ((_c) == '\n' || (_c) == '\r')
23
24 #define STREQ(_t, _f, _fn) ((_fn) == (sizeof(_t)-1) && rstreqn((_t), (_f), (_fn)))
25
26 #define MACROBUFSIZ (BUFSIZ * 2)
27
28 #include <rpm/rpmio.h>
29 #include <rpm/rpmstring.h>
30 #include <rpm/rpmfileutil.h>
31 #include <rpm/rpmurl.h>
32 #include <rpm/rpmlog.h>
33 #include <rpm/rpmmacro.h>
34 #include <rpm/argv.h>
35
36 #ifdef WITH_LUA
37 #include "rpmio/rpmlua.h"
38 #endif
39
40 #include "rpmio/rpmmacro_internal.h"
41 #include "debug.h"
42
43 enum macroFlags_e {
44 ME_NONE = 0,
45 ME_AUTO = (1 << 0),
46 ME_USED = (1 << 1),
47 ME_LITERAL = (1 << 2),
48 };
49
50 /*! The structure used to store a macro. */
51 struct rpmMacroEntry_s {
52 struct rpmMacroEntry_s *prev;/*!< Macro entry stack. */
53 const char *name; /*!< Macro name. */
54 const char *opts; /*!< Macro parameters (a la getopt) */
55 const char *body; /*!< Macro body. */
56 int flags; /*!< Macro state bits. */
57 int level; /*!< Scoping level. */
58 char arena[]; /*!< String arena. */
59 };
60
61 /*! The structure used to store the set of macros in a context. */
62 struct rpmMacroContext_s {
63 rpmMacroEntry *tab; /*!< Macro entry table (array of pointers). */
64 int n; /*!< No. of macros. */
65 int depth; /*!< Depth tracking when recursing from Lua */
66 int level; /*!< Scope level tracking when recursing from Lua */
67 pthread_mutex_t lock;
68 pthread_mutexattr_t lockattr;
69 };
70
71
72 static struct rpmMacroContext_s rpmGlobalMacroContext_s;
73 rpmMacroContext rpmGlobalMacroContext = &rpmGlobalMacroContext_s;
74
75 static struct rpmMacroContext_s rpmCLIMacroContext_s;
76 rpmMacroContext rpmCLIMacroContext = &rpmCLIMacroContext_s;
77
78 /*
79 * The macro engine internals do not require recursive mutexes but Lua
80 * macro bindings which can get called from the internals use the external
81 * interfaces which do perform locking. Until that is fixed somehow
82 * we'll just have to settle for recursive mutexes.
83 * Unfortunately POSIX doesn't specify static initializers for recursive
84 * mutexes so we need to have a separate PTHREAD_ONCE initializer just
85 * to initialize the otherwise static macro context mutexes. Pooh.
86 */
87 static pthread_once_t locksInitialized = PTHREAD_ONCE_INIT;
88
initLocks(void)89 static void initLocks(void)
90 {
91 rpmMacroContext mcs[] = { rpmGlobalMacroContext, rpmCLIMacroContext, NULL };
92
93 for (rpmMacroContext *mcp = mcs; *mcp; mcp++) {
94 rpmMacroContext mc = *mcp;
95 pthread_mutexattr_init(&mc->lockattr);
96 pthread_mutexattr_settype(&mc->lockattr, PTHREAD_MUTEX_RECURSIVE);
97 pthread_mutex_init(&mc->lock, &mc->lockattr);
98 }
99 }
100
101 /**
102 * Macro expansion state.
103 */
104 typedef struct MacroBuf_s {
105 char * buf; /*!< Expansion buffer. */
106 size_t tpos; /*!< Current position in expansion buffer */
107 size_t nb; /*!< No. bytes remaining in expansion buffer. */
108 int depth; /*!< Current expansion depth. */
109 int level; /*!< Current scoping level */
110 int error; /*!< Errors encountered during expansion? */
111 int macro_trace; /*!< Pre-print macro to expand? */
112 int expand_trace; /*!< Post-print macro expansion? */
113 int flags; /*!< Flags to control behavior */
114 rpmMacroContext mc;
115 } * MacroBuf;
116
117 #define _MAX_MACRO_DEPTH 64
118 static int max_macro_depth = _MAX_MACRO_DEPTH;
119
120 #define _PRINT_MACRO_TRACE 0
121 static int print_macro_trace = _PRINT_MACRO_TRACE;
122
123 #define _PRINT_EXPAND_TRACE 0
124 static int print_expand_trace = _PRINT_EXPAND_TRACE;
125
126 typedef void (*macroFunc)(MacroBuf mb, int chkexist, int negate,
127 const char * f, size_t fn, const char * g, size_t gn);
128 typedef const char *(*parseFunc)(MacroBuf mb, const char * se);
129
130 /* forward ref */
131 static int expandMacro(MacroBuf mb, const char *src, size_t slen);
132 static void pushMacro(rpmMacroContext mc,
133 const char * n, const char * o, const char * b, int level, int flags);
134 static void popMacro(rpmMacroContext mc, const char * n);
135 static int loadMacroFile(rpmMacroContext mc, const char * fn);
136 static void doBody(MacroBuf mb, int chkexist, int negate,
137 const char * f, size_t fn, const char * g, size_t gn);
138 static void doExpand(MacroBuf mb, int chkexist, int negate,
139 const char * f, size_t fn, const char * g, size_t gn);
140 static void doFoo(MacroBuf mb, int chkexist, int negate,
141 const char * f, size_t fn, const char * g, size_t gn);
142 static void doLoad(MacroBuf mb, int chkexist, int negate,
143 const char * f, size_t fn, const char * g, size_t gn);
144 static void doLua(MacroBuf mb, int chkexist, int negate,
145 const char * f, size_t fn, const char * g, size_t gn);
146 static void doOutput(MacroBuf mb, int chkexist, int negate,
147 const char * f, size_t fn, const char * g, size_t gn);
148 static void doSP(MacroBuf mb, int chkexist, int negate,
149 const char * f, size_t fn, const char * g, size_t gn);
150 static void doTrace(MacroBuf mb, int chkexist, int negate,
151 const char * f, size_t fn, const char * g, size_t gn);
152 static void doUncompress(MacroBuf mb, int chkexist, int negate,
153 const char * f, size_t fn, const char * g, size_t gn);
154 static void doVerbose(MacroBuf mb, int chkexist, int negate,
155 const char * f, size_t fn, const char * g, size_t gn);
156
157 static const char * doDef(MacroBuf mb, const char * se);
158 static const char * doGlobal(MacroBuf mb, const char * se);
159 static const char * doDump(MacroBuf mb, const char * se);
160 static const char * doUndefine(MacroBuf mb, const char * se);
161 /* =============================================================== */
162
rpmmctxAcquire(rpmMacroContext mc)163 static rpmMacroContext rpmmctxAcquire(rpmMacroContext mc)
164 {
165 if (mc == NULL)
166 mc = rpmGlobalMacroContext;
167 pthread_once(&locksInitialized, initLocks);
168 pthread_mutex_lock(&mc->lock);
169 return mc;
170 }
171
rpmmctxRelease(rpmMacroContext mc)172 static rpmMacroContext rpmmctxRelease(rpmMacroContext mc)
173 {
174 pthread_mutex_unlock(&mc->lock);
175 return NULL;
176 }
177
178 /**
179 * Find entry in macro table.
180 * @param mc macro context
181 * @param name macro name
182 * @param namelen no. of bytes
183 * @param pos found/insert position
184 * @return address of slot in macro table with name (or NULL)
185 */
186 static rpmMacroEntry *
findEntry(rpmMacroContext mc,const char * name,size_t namelen,size_t * pos)187 findEntry(rpmMacroContext mc, const char *name, size_t namelen, size_t *pos)
188 {
189 /* bsearch */
190 int cmp = 1;
191 size_t l = 0;
192 size_t u = mc->n;
193 size_t i = 0;
194 while (l < u) {
195 i = (l + u) / 2;
196 rpmMacroEntry me = mc->tab[i];
197 if (namelen == 0)
198 cmp = strcmp(me->name, name);
199 else {
200 cmp = strncmp(me->name, name, namelen);
201 /* longer me->name compares greater */
202 if (cmp == 0)
203 cmp = (me->name[namelen] != '\0');
204 }
205 if (cmp < 0)
206 l = i + 1;
207 else if (cmp > 0)
208 u = i;
209 else
210 break;
211 }
212
213 if (pos)
214 *pos = (cmp < 0) ? i + 1 : i;
215 if (cmp == 0)
216 return &mc->tab[i];
217 return NULL;
218 }
219
220 /* =============================================================== */
221
222 /**
223 * fgets(3) analogue that reads \ continuations. Last newline always trimmed.
224 * @param buf input buffer
225 * @param size inbut buffer size (bytes)
226 * @param f file handle
227 * @return number of lines read, or 0 on end-of-file
228 */
229 static int
rdcl(char * buf,size_t size,FILE * f)230 rdcl(char * buf, size_t size, FILE *f)
231 {
232 char *q = buf - 1; /* initialize just before buffer. */
233 size_t nb = 0;
234 size_t nread = 0;
235 int pc = 0, bc = 0, xc = 0;
236 int nlines = 0;
237 char *p = buf;
238
239 if (f != NULL)
240 do {
241 *(++q) = '\0'; /* terminate and move forward. */
242 if (fgets(q, size, f) == NULL) /* read next line. */
243 break;
244 nlines++;
245 nb = strlen(q);
246 nread += nb; /* trim trailing \r and \n */
247 for (q += nb - 1; nb > 0 && iseol(*q); q--)
248 nb--;
249 for (; p <= q; p++) {
250 switch (*p) {
251 case '\\':
252 switch (*(p+1)) {
253 case '\0': break;
254 default: p++; break;
255 }
256 break;
257 case '%':
258 switch (*(p+1)) {
259 case '{': p++, bc++; break;
260 case '(': p++, pc++; break;
261 case '[': p++, xc++; 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 case '[': if (xc > 0) xc++; break;
270 case ']': if (xc > 0) xc--; break;
271 }
272 }
273 if (nb == 0 || (*q != '\\' && !bc && !pc && !xc) || *(q+1) == '\0') {
274 *(++q) = '\0'; /* trim trailing \r, \n */
275 break;
276 }
277 q++; nb++; /* copy newline too */
278 size -= nb;
279 if (*q == '\r') /* XXX avoid \r madness */
280 *q = '\n';
281 } while (size > 0);
282 return nlines;
283 }
284
285 /**
286 * Return text between pl and matching pr characters.
287 * @param p start of text
288 * @param pl left char, i.e. '[', '(', '{', etc.
289 * @param pr right char, i.e. ']', ')', '}', etc.
290 * @return address of char after pr (or NULL)
291 */
292 static const char *
matchchar(const char * p,char pl,char pr)293 matchchar(const char * p, char pl, char pr)
294 {
295 int lvl = 0;
296 char c;
297
298 while ((c = *p++) != '\0') {
299 if (c == '\\') { /* Ignore escaped chars */
300 p++;
301 continue;
302 }
303 if (c == pr) {
304 if (--lvl <= 0) return p;
305 } else if (c == pl)
306 lvl++;
307 }
308 return (const char *)NULL;
309 }
310
mbErr(MacroBuf mb,int error,const char * fmt,...)311 static void mbErr(MacroBuf mb, int error, const char *fmt, ...)
312 {
313 char *emsg = NULL;
314 int n;
315 va_list ap;
316
317 va_start(ap, fmt);
318 n = rvasprintf(&emsg, fmt, ap);
319 va_end(ap);
320
321 if (n >= -1) {
322 /* XXX should have an non-locking version for this */
323 char *pfx = rpmExpand("%{?__file_name:%{__file_name}: }",
324 "%{?__file_lineno:line %{__file_lineno}: }",
325 NULL);
326 rpmlog(error ? RPMLOG_ERR : RPMLOG_WARNING, "%s%s", pfx, emsg);
327 free(pfx);
328 }
329
330 if (error)
331 mb->error = error;
332
333 free(emsg);
334 }
335
336 /**
337 * Pre-print macro expression to be expanded.
338 * @param mb macro expansion state
339 * @param s current expansion string
340 * @param se end of string
341 */
342 static void
printMacro(MacroBuf mb,const char * s,const char * se)343 printMacro(MacroBuf mb, const char * s, const char * se)
344 {
345 const char *senl;
346
347 if (s >= se) { /* XXX just in case */
348 fprintf(stderr, _("%3d>%*s(empty)\n"), mb->depth,
349 (2 * mb->depth + 1), "");
350 return;
351 }
352
353 if (s[-1] == '{')
354 s--;
355
356 /* Print only to first end-of-line (or end-of-string). */
357 for (senl = se; *senl && !iseol(*senl); senl++)
358 {};
359
360 /* Substitute caret at end-of-macro position */
361 fprintf(stderr, "%3d>%*s%%%.*s^", mb->depth,
362 (2 * mb->depth + 1), "", (int)(se - s), s);
363 if (se[0] != '\0' && se[1] != '\0' && (senl - (se+1)) > 0)
364 fprintf(stderr, "%-.*s", (int)(senl - (se+1)), se+1);
365 fprintf(stderr, "\n");
366 }
367
368 /**
369 * Post-print expanded macro expression.
370 * @param mb macro expansion state
371 * @param t current expansion string result
372 * @param te end of string
373 */
374 static void
printExpansion(MacroBuf mb,const char * t,const char * te)375 printExpansion(MacroBuf mb, const char * t, const char * te)
376 {
377 if (!(te > t)) {
378 rpmlog(RPMLOG_DEBUG, _("%3d<%*s(empty)\n"), mb->depth, (2 * mb->depth + 1), "");
379 return;
380 }
381
382 /* Shorten output which contains newlines */
383 while (te > t && iseol(te[-1]))
384 te--;
385 if (mb->depth > 0) {
386 const char *tenl;
387
388 /* Skip to last line of expansion */
389 while ((tenl = strchr(t, '\n')) && tenl < te)
390 t = ++tenl;
391
392 }
393
394 rpmlog(RPMLOG_DEBUG,"%3d<%*s", mb->depth, (2 * mb->depth + 1), "");
395 if (te > t)
396 rpmlog(RPMLOG_DEBUG, "%.*s", (int)(te - t), t);
397 rpmlog(RPMLOG_DEBUG, "\n");
398 }
399
400 #define SKIPBLANK(_s, _c) \
401 while (((_c) = *(_s)) && isblank(_c)) \
402 (_s)++; \
403
404 #define SKIPNONBLANK(_s, _c) \
405 while (((_c) = *(_s)) && !(isblank(_c) || iseol(_c))) \
406 (_s)++; \
407
408 #define COPYNAME(_ne, _s, _c) \
409 { SKIPBLANK(_s,_c); \
410 while (((_c) = *(_s)) && (risalnum(_c) || (_c) == '_')) \
411 *(_ne)++ = *(_s)++; \
412 *(_ne) = '\0'; \
413 }
414
415 #define COPYOPTS(_oe, _s, _c) \
416 { \
417 while (((_c) = *(_s)) && (_c) != ')') \
418 *(_oe)++ = *(_s)++; \
419 *(_oe) = '\0'; \
420 }
421
422 /**
423 * Macro-expand string src, return result in dynamically allocated buffer.
424 * @param mb macro expansion state
425 * @param src string to expand
426 * @param slen input string length (or 0 for strlen())
427 * @retval target pointer to expanded string (malloced)
428 * @return result of expansion
429 */
430 static int
expandThis(MacroBuf mb,const char * src,size_t slen,char ** target)431 expandThis(MacroBuf mb, const char * src, size_t slen, char **target)
432 {
433 struct MacroBuf_s umb;
434
435 /* Copy other state from "parent", but we want a buffer of our own */
436 umb = *mb;
437 umb.buf = NULL;
438 umb.error = 0;
439 /* In case of error, flag it in the "parent"... */
440 if (expandMacro(&umb, src, slen))
441 mb->error = 1;
442 *target = umb.buf;
443
444 /* ...but return code for this operation specifically */
445 return umb.error;
446 }
447
mbAppend(MacroBuf mb,char c)448 static void mbAppend(MacroBuf mb, char c)
449 {
450 if (mb->nb < 1) {
451 mb->buf = xrealloc(mb->buf, mb->tpos + MACROBUFSIZ + 1);
452 mb->nb += MACROBUFSIZ;
453 }
454 mb->buf[mb->tpos++] = c;
455 mb->buf[mb->tpos] = '\0';
456 mb->nb--;
457 }
458
mbAppendStr(MacroBuf mb,const char * str)459 static void mbAppendStr(MacroBuf mb, const char *str)
460 {
461 size_t len = strlen(str);
462 if (len > mb->nb) {
463 mb->buf = xrealloc(mb->buf, mb->tpos + mb->nb + MACROBUFSIZ + len + 1);
464 mb->nb += MACROBUFSIZ + len;
465 }
466 memcpy(mb->buf+mb->tpos, str, len + 1);
467 mb->tpos += len;
468 mb->nb -= len;
469 }
470
doDnl(MacroBuf mb,const char * se)471 static const char * doDnl(MacroBuf mb, const char * se)
472 {
473 const char *s = se;
474 while (*s && !iseol(*s))
475 s++;
476 return (*s != '\0') ? s + 1 : s;
477 }
478
479 /**
480 * Expand output of shell command into target buffer.
481 * @param mb macro expansion state
482 * @param cmd shell command
483 * @param clen no. bytes in shell command
484 */
485 static void
doShellEscape(MacroBuf mb,const char * cmd,size_t clen)486 doShellEscape(MacroBuf mb, const char * cmd, size_t clen)
487 {
488 char *buf = NULL;
489 FILE *shf;
490 int c;
491
492 if (expandThis(mb, cmd, clen, &buf))
493 goto exit;
494
495 if ((shf = popen(buf, "r")) == NULL) {
496 mbErr(mb, 1, _("Failed to open shell expansion pipe for command: "
497 "%s: %m \n"), buf);
498 goto exit;
499 }
500
501 size_t tpos = mb->tpos;
502 while ((c = fgetc(shf)) != EOF) {
503 mbAppend(mb, c);
504 }
505 (void) pclose(shf);
506
507 /* Delete trailing \r \n */
508 while (mb->tpos > tpos && iseol(mb->buf[mb->tpos-1])) {
509 mb->buf[--mb->tpos] = '\0';
510 mb->nb++;
511 }
512
513 exit:
514 _free(buf);
515 }
516
517 /**
518 * Expand an expression into target buffer.
519 * @param mb macro expansion state
520 * @param expr expression
521 * @param len no. bytes in expression
522 */
doExpressionExpansion(MacroBuf mb,const char * expr,size_t len)523 static void doExpressionExpansion(MacroBuf mb, const char * expr, size_t len)
524 {
525 char *buf = xmalloc(len + 1);
526 char *result;
527 strncpy(buf, expr, len);
528 buf[len] = 0;
529 result = rpmExprStrFlags(buf, RPMEXPR_EXPAND);
530 if (!result) {
531 mb->error = 1;
532 goto exit;
533 }
534 mbAppendStr(mb, result);
535 free(result);
536 exit:
537 _free(buf);
538 }
539
getncpus(void)540 static unsigned int getncpus(void)
541 {
542 unsigned int ncpus = 0;
543 #if HAVE_SCHED_GETAFFINITY
544 cpu_set_t set;
545 if (sched_getaffinity (0, sizeof(set), &set) == 0)
546 ncpus = CPU_COUNT(&set);
547 #endif
548 /* Fallback to sysconf() if the above isn't supported or didn't work */
549 if (ncpus < 1)
550 ncpus = sysconf(_SC_NPROCESSORS_ONLN);
551 /* If all else fails, there's always the one we're running on... */
552 if (ncpus < 1)
553 ncpus = 1;
554 return ncpus;
555 }
556
557 #define STR_AND_LEN(_str) (_str), sizeof((_str))-1
558
559 /* Names in the table must be in ASCII-code order */
560 static struct builtins_s {
561 const char * name;
562 size_t len;
563 macroFunc func;
564 parseFunc parse;
565 int havearg;
566 } const builtinmacros[] = {
567 { STR_AND_LEN("P"), doSP, NULL, 1 },
568 { STR_AND_LEN("S"), doSP, NULL, 1 },
569 { STR_AND_LEN("basename"), doFoo, NULL, 1 },
570 { STR_AND_LEN("define"), NULL, doDef, 0 },
571 { STR_AND_LEN("dirname"), doFoo, NULL, 1 },
572 { STR_AND_LEN("dnl"), NULL, doDnl, 0 },
573 { STR_AND_LEN("dump"), NULL, doDump, 0 },
574 { STR_AND_LEN("echo"), doOutput, NULL, 1 },
575 { STR_AND_LEN("error"), doOutput, NULL, 1 },
576 { STR_AND_LEN("expand"), doExpand, NULL, 1 },
577 { STR_AND_LEN("expr"), doFoo, NULL, 1 },
578 { STR_AND_LEN("getconfdir"),doFoo, NULL, 0 },
579 { STR_AND_LEN("getenv"), doFoo, NULL, 1 },
580 { STR_AND_LEN("getncpus"), doFoo, NULL, 0 },
581 { STR_AND_LEN("global"), NULL, doGlobal, 0 },
582 { STR_AND_LEN("load"), doLoad, NULL, 1 },
583 { STR_AND_LEN("lua"), doLua, NULL, 1 },
584 { STR_AND_LEN("macrobody"), doBody, NULL, 1 },
585 { STR_AND_LEN("quote"), doFoo, NULL, 1 },
586 { STR_AND_LEN("shrink"), doFoo, NULL, 1 },
587 { STR_AND_LEN("suffix"), doFoo, NULL, 1 },
588 { STR_AND_LEN("trace"), doTrace, NULL, 0 },
589 { STR_AND_LEN("u2p"), doFoo, NULL, 1 },
590 { STR_AND_LEN("uncompress"),doUncompress, NULL, 1 },
591 { STR_AND_LEN("undefine"), NULL, doUndefine, 0 },
592 { STR_AND_LEN("url2path"), doFoo, NULL, 1 },
593 { STR_AND_LEN("verbose"), doVerbose, NULL, 1 },
594 { STR_AND_LEN("warn"), doOutput, NULL, 1 },
595 };
596 static const size_t numbuiltins = sizeof(builtinmacros)/sizeof(*builtinmacros);
597
namecmp(const void * name1,const void * name2)598 static int namecmp(const void *name1, const void *name2)
599 {
600 struct builtins_s *n1 = (struct builtins_s *)name1;
601 struct builtins_s *n2 = (struct builtins_s *)name2;
602
603 int rc = strncmp(n1->name, n2->name, n1->len);
604 if (rc == 0)
605 rc = n1->len - n2->len;
606 return rc;
607 }
608
609 /**
610 * Return a pointer to the built-in macro with the given name
611 * @param name macro name
612 * @param nlen name length
613 * @return pointer to the built-in macro or NULL if not found
614 */
lookupBuiltin(const char * name,size_t nlen)615 static const struct builtins_s* lookupBuiltin(const char *name, size_t nlen)
616 {
617 struct builtins_s macro = {
618 .name = name,
619 .len = nlen,
620 };
621
622 return bsearch(¯o, builtinmacros, numbuiltins, sizeof(*builtinmacros),
623 namecmp);
624 }
625
626 static int
validName(MacroBuf mb,const char * name,size_t namelen,const char * action)627 validName(MacroBuf mb, const char *name, size_t namelen, const char *action)
628 {
629 int rc = 0;
630 int c;
631
632 /* Names must start with alphabetic or _ and be at least 3 chars */
633 if (!((c = *name) && (risalpha(c) || c == '_') && (namelen) > 2)) {
634 mbErr(mb, 1, _("Macro %%%s has illegal name (%s)\n"), name, action);
635 goto exit;
636 }
637
638 if (lookupBuiltin(name, namelen)) {
639 mbErr(mb, 1, _("Macro %%%s is a built-in (%s)\n"), name, action);
640 goto exit;
641 }
642
643 rc = 1;
644
645 exit:
646 return rc;
647 }
648
649 /**
650 * Parse (and execute) new macro definition.
651 * @param mb macro expansion state
652 * @param se macro definition to parse
653 * @param level macro recursion level
654 * @param expandbody should body be expanded?
655 * @return address to continue parsing
656 */
657 static const char *
doDefine(MacroBuf mb,const char * se,int level,int expandbody)658 doDefine(MacroBuf mb, const char * se, int level, int expandbody)
659 {
660 const char *s = se;
661 char *buf = xmalloc(strlen(s) + 3); /* Some leeway for termination issues... */
662 char *n = buf, *ne = n;
663 char *o = NULL, *oe;
664 char *b, *be, *ebody = NULL;
665 int c;
666 int oc = ')';
667 const char *sbody; /* as-is body start */
668 int rc = 1; /* assume failure */
669
670 /* Copy name */
671 COPYNAME(ne, s, c);
672
673 /* Copy opts (if present) */
674 oe = ne + 1;
675 if (*s == '(') {
676 s++; /* skip ( */
677 /* Options must be terminated with ')' */
678 if (strchr(s, ')')) {
679 o = oe;
680 COPYOPTS(oe, s, oc);
681 s++; /* skip ) */
682 } else {
683 mbErr(mb, 1, _("Macro %%%s has unterminated opts\n"), n);
684 goto exit;
685 }
686 }
687
688 /* Copy body, skipping over escaped newlines */
689 b = be = oe + 1;
690 sbody = s;
691 SKIPBLANK(s, c);
692 if (c == '{') { /* XXX permit silent {...} grouping */
693 if ((se = matchchar(s, c, '}')) == NULL) {
694 mbErr(mb, 1, _("Macro %%%s has unterminated body\n"), n);
695 se = s; /* XXX W2DO? */
696 goto exit;
697 }
698 s++; /* XXX skip { */
699 strncpy(b, s, (se - 1 - s));
700 b[se - 1 - s] = '\0';
701 be += strlen(b);
702 s = se; /* move scan forward */
703 } else { /* otherwise free-field */
704 int bc = 0, pc = 0, xc = 0;
705 while (*s && (bc || pc || !iseol(*s))) {
706 switch (*s) {
707 case '\\':
708 switch (*(s+1)) {
709 case '\0': break;
710 default: s++; break;
711 }
712 break;
713 case '%':
714 switch (*(s+1)) {
715 case '{': *be++ = *s++; bc++; break;
716 case '(': *be++ = *s++; pc++; break;
717 case '[': *be++ = *s++; xc++; break;
718 case '%': *be++ = *s++; break;
719 }
720 break;
721 case '{': if (bc > 0) bc++; break;
722 case '}': if (bc > 0) bc--; break;
723 case '(': if (pc > 0) pc++; break;
724 case ')': if (pc > 0) pc--; break;
725 case '[': if (xc > 0) xc++; break;
726 case ']': if (xc > 0) xc--; break;
727 }
728 *be++ = *s++;
729 }
730 *be = '\0';
731
732 if (bc || pc || xc) {
733 mbErr(mb, 1, _("Macro %%%s has unterminated body\n"), n);
734 se = s; /* XXX W2DO? */
735 goto exit;
736 }
737
738 /* Trim trailing blanks/newlines */
739 while (--be >= b && (c = *be) && (isblank(c) || iseol(c)))
740 {};
741 *(++be) = '\0'; /* one too far */
742 }
743
744 /* Move scan over body */
745 while (iseol(*s))
746 s++;
747 se = s;
748
749 if (!validName(mb, n, ne - n, expandbody ? "%global": "%define"))
750 goto exit;
751
752 if ((be - b) < 1) {
753 mbErr(mb, 1, _("Macro %%%s has empty body\n"), n);
754 goto exit;
755 }
756
757 if (!isblank(*sbody) && !(*sbody == '\\' && iseol(sbody[1])))
758 mbErr(mb, 0, _("Macro %%%s needs whitespace before body\n"), n);
759
760 if (expandbody) {
761 if (expandThis(mb, b, 0, &ebody)) {
762 mbErr(mb, 1, _("Macro %%%s failed to expand\n"), n);
763 goto exit;
764 }
765 b = ebody;
766 }
767
768 pushMacro(mb->mc, n, o, b, level, ME_NONE);
769 rc = 0;
770
771 exit:
772 if (rc)
773 mb->error = 1;
774 _free(buf);
775 _free(ebody);
776 return se;
777 }
778
779 /**
780 * Parse (and execute) macro undefinition.
781 * @param mb macro expansion state
782 * @param se macro name to undefine
783 * @return address to continue parsing
784 */
785 static const char *
doUndefine(MacroBuf mb,const char * se)786 doUndefine(MacroBuf mb, const char * se)
787 {
788 const char *s = se;
789 char *buf = xmalloc(strlen(s) + 1);
790 char *n = buf, *ne = n;
791 int c;
792
793 COPYNAME(ne, s, c);
794
795 /* Move scan over body */
796 while (iseol(*s))
797 s++;
798 se = s;
799
800 if (!validName(mb, n, ne - n, "%undefine")) {
801 mb->error = 1;
802 goto exit;
803 }
804
805 popMacro(mb->mc, n);
806
807 exit:
808 _free(buf);
809 return se;
810 }
811
doDef(MacroBuf mb,const char * se)812 static const char * doDef(MacroBuf mb, const char * se)
813 {
814 return doDefine(mb, se, mb->level, 0);
815 }
816
doGlobal(MacroBuf mb,const char * se)817 static const char * doGlobal(MacroBuf mb, const char * se)
818 {
819 return doDefine(mb, se, RMIL_GLOBAL, 1);
820 }
821
doDump(MacroBuf mb,const char * se)822 static const char * doDump(MacroBuf mb, const char * se)
823 {
824 rpmDumpMacroTable(mb->mc, NULL);
825 while (iseol(*se))
826 se++;
827 return se;
828 }
829
830
831 /**
832 * Free parsed arguments for parameterized macro.
833 * @param mb macro expansion state
834 */
835 static void
freeArgs(MacroBuf mb)836 freeArgs(MacroBuf mb)
837 {
838 rpmMacroContext mc = mb->mc;
839
840 /* Delete dynamic macro definitions */
841 for (int i = 0; i < mc->n; i++) {
842 rpmMacroEntry me = mc->tab[i];
843 assert(me);
844 if (me->level < mb->level)
845 continue;
846 /* Warn on defined but unused non-automatic, scoped macros */
847 if (!(me->flags & (ME_AUTO|ME_USED))) {
848 mbErr(mb, 0, _("Macro %%%s defined but not used within scope\n"),
849 me->name);
850 /* Only whine once */
851 me->flags |= ME_USED;
852 }
853
854 /* compensate if the slot is to go away */
855 if (me->prev == NULL)
856 i--;
857 popMacro(mc, me->name);
858 }
859 mb->level--;
860 }
861
splitQuoted(ARGV_t * av,const char * str,const char * seps)862 static void splitQuoted(ARGV_t *av, const char * str, const char * seps)
863 {
864 const int qchar = 0x1f; /* ASCII unit separator */
865 const char *s = str;
866 const char *start = str;
867 int quoted = 0;
868
869 while (start != NULL) {
870 if (!quoted && strchr(seps, *s)) {
871 size_t slen = s - start;
872 /* quoted arguments are always kept, otherwise skip empty args */
873 if (slen > 0) {
874 char *d, arg[slen + 1];
875 const char *t;
876 for (d = arg, t = start; t - start < slen; t++) {
877 if (*t == qchar)
878 continue;
879 *d++ = *t;
880 }
881 arg[d - arg] = '\0';
882 argvAdd(av, arg);
883 }
884 start = s + 1;
885 }
886 if (*s == qchar)
887 quoted = !quoted;
888 else if (*s == '\0')
889 start = NULL;
890 s++;
891 }
892 }
893
894 /**
895 * Parse arguments (to next new line) for parameterized macro.
896 * @todo Use popt rather than getopt to parse args.
897 * @param mb macro expansion state
898 * @param me macro entry slot
899 * @param se arguments to parse
900 * @param lastc stop parsing at lastc
901 * @return address to continue parsing
902 */
903 static const char *
grabArgs(MacroBuf mb,const rpmMacroEntry me,const char * se,const char * lastc)904 grabArgs(MacroBuf mb, const rpmMacroEntry me, const char * se,
905 const char * lastc)
906 {
907 const char *cont = NULL;
908 const char *opts;
909 char *args = NULL;
910 ARGV_t argv = NULL;
911 int argc = 0;
912 int c;
913
914 /*
915 * Prepare list of call arguments, starting with macro name as argv[0].
916 * Make a copy of se up to lastc string that we can pass to argvSplit().
917 * Append the results to main argv.
918 */
919 argvAdd(&argv, me->name);
920 if (lastc) {
921 char *s = NULL;
922
923 /* Expand possible macros in arguments */
924 expandThis(mb, se, lastc-se, &s);
925 splitQuoted(&argv, s, " \t");
926 free(s);
927
928 cont = (*lastc == '\0') || (*lastc == '\n' && *(lastc-1) != '\\') ?
929 lastc : lastc + 1;
930 }
931
932 /* Bump call depth on entry before first macro define */
933 mb->level++;
934
935 /* Setup macro name as %0 */
936 pushMacro(mb->mc, "0", NULL, me->name, mb->level, ME_AUTO | ME_LITERAL);
937
938 /*
939 * The macro %* analoguous to the shell's $* means "Pass all non-macro
940 * parameters." Consequently, there needs to be a macro that means "Pass all
941 * (including macro parameters) options". This is useful for verifying
942 * parameters during expansion and yet transparently passing all parameters
943 * through for higher level processing (e.g. %description and/or %setup).
944 * This is the (potential) justification for %{**} ...
945 */
946 args = argvJoin(argv + 1, " ");
947 pushMacro(mb->mc, "**", NULL, args, mb->level, ME_AUTO | ME_LITERAL);
948 free(args);
949
950 /*
951 * POSIX states optind must be 1 before any call but glibc uses 0
952 * to (re)initialize getopt structures, eww.
953 */
954 #ifdef __GLIBC__
955 optind = 0;
956 #else
957 optind = 1;
958 #endif
959
960 opts = me->opts;
961 argc = argvCount(argv);
962
963 /* Define option macros. */
964 while ((c = getopt(argc, argv, opts)) != -1)
965 {
966 char *name = NULL, *body = NULL;
967 if (c == '?' || strchr(opts, c) == NULL) {
968 mbErr(mb, 1, _("Unknown option %c in %s(%s)\n"),
969 (char)optopt, me->name, opts);
970 goto exit;
971 }
972
973 rasprintf(&name, "-%c", c);
974 if (optarg) {
975 rasprintf(&body, "-%c %s", c, optarg);
976 } else {
977 rasprintf(&body, "-%c", c);
978 }
979 pushMacro(mb->mc, name, NULL, body, mb->level, ME_AUTO | ME_LITERAL);
980 free(name);
981 free(body);
982
983 if (optarg) {
984 rasprintf(&name, "-%c*", c);
985 pushMacro(mb->mc, name, NULL, optarg, mb->level, ME_AUTO | ME_LITERAL);
986 free(name);
987 }
988 }
989
990 /* Add argument count (remaining non-option items) as macro. */
991 { char *ac = NULL;
992 rasprintf(&ac, "%d", (argc - optind));
993 pushMacro(mb->mc, "#", NULL, ac, mb->level, ME_AUTO | ME_LITERAL);
994 free(ac);
995 }
996
997 /* Add macro for each argument */
998 if (argc - optind) {
999 for (c = optind; c < argc; c++) {
1000 char *name = NULL;
1001 rasprintf(&name, "%d", (c - optind + 1));
1002 pushMacro(mb->mc, name, NULL, argv[c], mb->level, ME_AUTO | ME_LITERAL);
1003 free(name);
1004 }
1005 }
1006
1007 /* Add concatenated unexpanded arguments as yet another macro. */
1008 args = argvJoin(argv + optind, " ");
1009 pushMacro(mb->mc, "*", NULL, args ? args : "", mb->level, ME_AUTO | ME_LITERAL);
1010 free(args);
1011
1012 exit:
1013 argvFree(argv);
1014 return cont;
1015 }
1016
doBody(MacroBuf mb,int chkexist,int negate,const char * f,size_t fn,const char * g,size_t gn)1017 static void doBody(MacroBuf mb, int chkexist, int negate,
1018 const char * f, size_t fn, const char * g, size_t gn)
1019 {
1020 if (gn > 0) {
1021 char *buf = NULL;
1022 if (expandThis(mb, g, gn, &buf) == 0) {
1023 rpmMacroEntry *mep = findEntry(mb->mc, buf, 0, NULL);
1024 if (mep) {
1025 mbAppendStr(mb, (*mep)->body);
1026 } else {
1027 mbErr(mb, 1, _("no such macro: '%s'\n"), buf);
1028 }
1029 free(buf);
1030 }
1031 }
1032 }
1033
doOutput(MacroBuf mb,int chkexist,int negate,const char * f,size_t fn,const char * g,size_t gn)1034 static void doOutput(MacroBuf mb, int chkexist, int negate, const char * f, size_t fn, const char * g, size_t gn)
1035 {
1036 char *buf = NULL;
1037 int loglevel = RPMLOG_NOTICE; /* assume echo */
1038 if (STREQ("error", f, fn)) {
1039 loglevel = RPMLOG_ERR;
1040 mb->error = 1;
1041 } else if (STREQ("warn", f, fn)) {
1042 loglevel = RPMLOG_WARNING;
1043 }
1044 if (gn == 0)
1045 g = "";
1046
1047 (void) expandThis(mb, g, gn, &buf);
1048 rpmlog(loglevel, "%s\n", buf);
1049 _free(buf);
1050 }
1051
doLua(MacroBuf mb,int chkexist,int negate,const char * f,size_t fn,const char * g,size_t gn)1052 static void doLua(MacroBuf mb, int chkexist, int negate, const char * f, size_t fn, const char * g, size_t gn)
1053 {
1054 #ifdef WITH_LUA
1055 rpmlua lua = NULL; /* Global state. */
1056 char *scriptbuf = xmalloc(gn + 1);
1057 char *printbuf;
1058 rpmMacroContext mc = mb->mc;
1059 int odepth = mc->depth;
1060 int olevel = mc->level;
1061
1062 if (g != NULL && gn > 0)
1063 memcpy(scriptbuf, g, gn);
1064 scriptbuf[gn] = '\0';
1065 rpmluaPushPrintBuffer(lua);
1066 mc->depth = mb->depth;
1067 mc->level = mb->level;
1068 if (rpmluaRunScript(lua, scriptbuf, NULL) == -1)
1069 mb->error = 1;
1070 mc->depth = odepth;
1071 mc->level = olevel;
1072 printbuf = rpmluaPopPrintBuffer(lua);
1073 if (printbuf) {
1074 mbAppendStr(mb, printbuf);
1075 free(printbuf);
1076 }
1077 free(scriptbuf);
1078 #else
1079 mbErr(mb, 1, _("<lua> scriptlet support not built in\n"));
1080 #endif
1081 }
1082
1083 static void
doSP(MacroBuf mb,int chkexist,int negate,const char * f,size_t fn,const char * g,size_t gn)1084 doSP(MacroBuf mb, int chkexist, int negate,
1085 const char * f, size_t fn, const char * g, size_t gn)
1086 {
1087 const char *b = "";
1088 char *buf = NULL;
1089 char *s = NULL;
1090
1091 if (gn > 0) {
1092 expandThis(mb, g, gn, &buf);
1093 b = buf;
1094 }
1095
1096 s = rstrscat(NULL, (*f == 'S') ? "%SOURCE" : "%PATCH", b, NULL);
1097 expandMacro(mb, s, 0);
1098 free(s);
1099 free(buf);
1100 }
1101
doUncompress(MacroBuf mb,int chkexist,int negate,const char * f,size_t fn,const char * g,size_t gn)1102 static void doUncompress(MacroBuf mb, int chkexist, int negate,
1103 const char * f, size_t fn, const char * g, size_t gn)
1104 {
1105 rpmCompressedMagic compressed = COMPRESSED_OTHER;
1106 char *b, *be, *buf = NULL;
1107 int c;
1108
1109 if (gn) {
1110 expandThis(mb, g, gn, &buf);
1111 for (b = buf; (c = *b) && isblank(c);)
1112 b++;
1113 for (be = b; (c = *be) && !isblank(c);)
1114 be++;
1115 *be++ = '\0';
1116 }
1117
1118 if (gn == 0 || *b == '\0')
1119 goto exit;
1120
1121 if (rpmFileIsCompressed(b, &compressed))
1122 mb->error = 1;
1123
1124 switch (compressed) {
1125 default:
1126 case COMPRESSED_NOT:
1127 expandMacro(mb, "%__cat ", 0);
1128 break;
1129 case COMPRESSED_OTHER:
1130 expandMacro(mb, "%__gzip -dc ", 0);
1131 break;
1132 case COMPRESSED_BZIP2:
1133 expandMacro(mb, "%__bzip2 -dc ", 0);
1134 break;
1135 case COMPRESSED_ZIP:
1136 expandMacro(mb, "%__unzip ", 0);
1137 break;
1138 case COMPRESSED_LZMA:
1139 case COMPRESSED_XZ:
1140 expandMacro(mb, "%__xz -dc ", 0);
1141 break;
1142 case COMPRESSED_LZIP:
1143 expandMacro(mb, "%__lzip -dc ", 0);
1144 break;
1145 case COMPRESSED_LRZIP:
1146 expandMacro(mb, "%__lrzip -dqo- ", 0);
1147 break;
1148 case COMPRESSED_7ZIP:
1149 expandMacro(mb, "%__7zip x ", 0);
1150 break;
1151 case COMPRESSED_ZSTD:
1152 expandMacro(mb, "%__zstd -dc ", 0);
1153 break;
1154 }
1155 mbAppendStr(mb, buf);
1156
1157 exit:
1158 free(buf);
1159 }
1160
doExpand(MacroBuf mb,int chkexist,int negate,const char * f,size_t fn,const char * g,size_t gn)1161 static void doExpand(MacroBuf mb, int chkexist, int negate,
1162 const char * f, size_t fn, const char * g, size_t gn)
1163 {
1164 if (gn > 0) {
1165 char *buf;
1166 expandThis(mb, g, gn, &buf);
1167 expandMacro(mb, buf, 0);
1168 free(buf);
1169 }
1170 }
1171
doVerbose(MacroBuf mb,int chkexist,int negate,const char * f,size_t fn,const char * g,size_t gn)1172 static void doVerbose(MacroBuf mb, int chkexist, int negate,
1173 const char * f, size_t fn, const char * g, size_t gn)
1174 {
1175 int verbose = (rpmIsVerbose() != 0);
1176 /* Don't expand %{verbose:...} argument on false condition */
1177 if (verbose != negate) {
1178 char *buf = NULL;
1179 expandThis(mb, g, gn, &buf);
1180 mbAppendStr(mb, buf);
1181 free(buf);
1182 }
1183 }
1184
1185 /**
1186 * Execute macro primitives.
1187 * @param mb macro expansion state
1188 * @param chkexist unused
1189 * @param negate should logic be inverted?
1190 * @param f beginning of field f
1191 * @param fn length of field f
1192 * @param g beginning of field g
1193 * @param gn length of field g
1194 */
1195 static void
doFoo(MacroBuf mb,int chkexist,int negate,const char * f,size_t fn,const char * g,size_t gn)1196 doFoo(MacroBuf mb, int chkexist, int negate, const char * f, size_t fn,
1197 const char * g, size_t gn)
1198 {
1199 char *buf = NULL;
1200 char *b = NULL;
1201 int expand = (g != NULL && gn > 0);
1202
1203 if (expand) {
1204 (void) expandThis(mb, g, gn, &buf);
1205 } else {
1206 buf = xmalloc(MACROBUFSIZ + fn + gn);
1207 buf[0] = '\0';
1208 }
1209 if (STREQ("basename", f, fn)) {
1210 if ((b = strrchr(buf, '/')) == NULL)
1211 b = buf;
1212 else
1213 b++;
1214 } else if (STREQ("dirname", f, fn)) {
1215 if ((b = strrchr(buf, '/')) != NULL)
1216 *b = '\0';
1217 b = buf;
1218 } else if (STREQ("shrink", f, fn)) {
1219 /*
1220 * shrink body by removing all leading and trailing whitespaces and
1221 * reducing intermediate whitespaces to a single space character.
1222 */
1223 size_t i = 0, j = 0;
1224 size_t buflen = strlen(buf);
1225 int was_space = 0;
1226 while (i < buflen) {
1227 if (risspace(buf[i])) {
1228 was_space = 1;
1229 i++;
1230 continue;
1231 } else if (was_space) {
1232 was_space = 0;
1233 if (j > 0) /* remove leading blanks at all */
1234 buf[j++] = ' ';
1235 }
1236 buf[j++] = buf[i++];
1237 }
1238 buf[j] = '\0';
1239 b = buf;
1240 } else if (STREQ("quote", f, fn)) {
1241 char *quoted = NULL;
1242 rasprintf("ed, "%c%s%c", 0x1f, buf, 0x1f);
1243 free(buf);
1244 b = buf = quoted;
1245 } else if (STREQ("suffix", f, fn)) {
1246 if ((b = strrchr(buf, '.')) != NULL)
1247 b++;
1248 } else if (STREQ("expr", f, fn)) {
1249 char *expr = rpmExprStrFlags(buf, 0);
1250 if (expr) {
1251 free(buf);
1252 b = buf = expr;
1253 } else {
1254 mb->error = 1;
1255 }
1256 } else if (STREQ("url2path", f, fn) || STREQ("u2p", f, fn)) {
1257 (void)urlPath(buf, (const char **)&b);
1258 if (*b == '\0') b = "/";
1259 } else if (STREQ("getenv", f, fn)) {
1260 b = getenv(buf);
1261 } else if (STREQ("getconfdir", f, fn)) {
1262 sprintf(buf, "%s", rpmConfigDir());
1263 b = buf;
1264 } else if (STREQ("getncpus", f, fn)) {
1265 sprintf(buf, "%u", getncpus());
1266 b = buf;
1267 }
1268
1269 if (b) {
1270 mbAppendStr(mb, b);
1271 }
1272 free(buf);
1273 }
1274
doLoad(MacroBuf mb,int chkexist,int negate,const char * f,size_t fn,const char * g,size_t gn)1275 static void doLoad(MacroBuf mb, int chkexist, int negate,
1276 const char * f, size_t fn, const char * g, size_t gn)
1277 {
1278 char *arg = NULL;
1279 if (g && gn > 0 && expandThis(mb, g, gn, &arg) == 0) {
1280 /* Print failure iff %{load:...} or %{!?load:...} */
1281 if (loadMacroFile(mb->mc, arg) && chkexist == negate) {
1282 mbErr(mb, 1, _("failed to load macro file %s\n"), arg);
1283 }
1284 }
1285 free(arg);
1286 }
1287
doTrace(MacroBuf mb,int chkexist,int negate,const char * f,size_t fn,const char * g,size_t gn)1288 static void doTrace(MacroBuf mb, int chkexist, int negate,
1289 const char * f, size_t fn, const char * g, size_t gn)
1290 {
1291 mb->expand_trace = mb->macro_trace = (negate ? 0 : mb->depth);
1292 if (mb->depth == 1) {
1293 print_macro_trace = mb->macro_trace;
1294 print_expand_trace = mb->expand_trace;
1295 }
1296 }
1297
setNegateAndCheck(const char * str,int * pnegate,int * pchkexist)1298 static const char *setNegateAndCheck(const char *str, int *pnegate, int *pchkexist) {
1299
1300 *pnegate = 0;
1301 *pchkexist = 0;
1302 while ((*str == '?') || (*str == '!')) {
1303 if (*str == '!')
1304 *pnegate = !*pnegate;
1305 else
1306 (*pchkexist)++;
1307 str++;
1308 }
1309 return str;
1310 }
1311
1312 /**
1313 * Find the end of a macro call
1314 * @param str pointer to the character after the initial '%'
1315 * @return pointer to the next character after the macro
1316 */
1317 RPM_GNUC_INTERNAL
findMacroEnd(const char * str)1318 const char *findMacroEnd(const char *str)
1319 {
1320 int c;
1321 if (*str == '(')
1322 return matchchar(str, *str, ')');
1323 if (*str == '{')
1324 return matchchar(str, *str, '}');
1325 if (*str == '[')
1326 return matchchar(str, *str, ']');
1327 while (*str == '?' || *str == '!')
1328 str++;
1329 if (*str == '-') /* %-f */
1330 str++;
1331 while ((c = *str) && (risalnum(c) || c == '_'))
1332 str++;
1333 if (*str == '*' && str[1] == '*') /* %** */
1334 str += 2;
1335 else if (*str == '*' || *str == '#') /* %* and %# */
1336 str++;
1337 return str;
1338 }
1339
1340 /**
1341 * The main macro recursion loop.
1342 * @param mb macro expansion state
1343 * @param src string to expand
1344 * @param slen length of string buffer
1345 * @return 0 on success, 1 on failure
1346 */
1347 static int
expandMacro(MacroBuf mb,const char * src,size_t slen)1348 expandMacro(MacroBuf mb, const char *src, size_t slen)
1349 {
1350 rpmMacroEntry *mep;
1351 rpmMacroEntry me;
1352 const char *s = src, *se;
1353 const char *f, *fe;
1354 const char *g, *ge;
1355 size_t fn, gn, tpos;
1356 int c;
1357 int negate;
1358 const char * lastc;
1359 int chkexist;
1360 char *source = NULL;
1361 int store_macro_trace;
1362 int store_expand_trace;
1363
1364 /*
1365 * Always make a (terminated) copy of the source string.
1366 * Beware: avoiding the copy when src is known \0-terminated seems like
1367 * an obvious opportunity for optimization, but doing that breaks
1368 * a special case of macro undefining itself.
1369 */
1370 if (!slen)
1371 slen = strlen(src);
1372 source = xmalloc(slen + 1);
1373 strncpy(source, src, slen);
1374 source[slen] = '\0';
1375 s = source;
1376
1377 if (mb->buf == NULL) {
1378 size_t blen = MACROBUFSIZ + slen;
1379 mb->buf = xmalloc(blen + 1);
1380 mb->buf[0] = '\0';
1381 mb->tpos = 0;
1382 mb->nb = blen;
1383 }
1384 tpos = mb->tpos; /* save expansion pointer for printExpand */
1385 store_macro_trace = mb->macro_trace;
1386 store_expand_trace = mb->expand_trace;
1387
1388 if (++mb->depth > max_macro_depth) {
1389 mbErr(mb, 1,
1390 _("Too many levels of recursion in macro expansion. It is likely caused by recursive macro declaration.\n"));
1391 mb->depth--;
1392 mb->expand_trace = 1;
1393 goto exit;
1394 }
1395
1396 while (mb->error == 0 && (c = *s) != '\0') {
1397 const struct builtins_s* builtin = NULL;
1398 s++;
1399 /* Copy text until next macro */
1400 switch (c) {
1401 case '%':
1402 if (*s) { /* Ensure not end-of-string. */
1403 if (*s != '%')
1404 break;
1405 s++; /* skip first % in %% */
1406 }
1407 default:
1408 mbAppend(mb, c);
1409 continue;
1410 break;
1411 }
1412
1413 /* Expand next macro */
1414 f = fe = NULL;
1415 g = ge = NULL;
1416 if (mb->depth > 1) /* XXX full expansion for outermost level */
1417 tpos = mb->tpos; /* save expansion pointer for printExpand */
1418 lastc = NULL;
1419 if ((se = findMacroEnd(s)) == NULL) {
1420 mbErr(mb, 1, _("Unterminated %c: %s\n"), (char)*s, s);
1421 continue;
1422 }
1423
1424 switch (*s) {
1425 default: /* %name substitution */
1426 f = s = setNegateAndCheck(s, &negate, &chkexist);
1427 fe = se;
1428 /* For "%name " macros ... */
1429 if ((c = *fe) && isblank(c))
1430 if ((lastc = strchr(fe,'\n')) == NULL)
1431 lastc = strchr(fe, '\0');
1432 break;
1433 case '(': /* %(...) shell escape */
1434 if (mb->macro_trace)
1435 printMacro(mb, s, se);
1436 s++; /* skip ( */
1437 doShellEscape(mb, s, (se - 1 - s));
1438 s = se;
1439 continue;
1440 case '[': /* %[...] expression expansion */
1441 if (mb->macro_trace)
1442 printMacro(mb, s, se);
1443 s++; /* skip [ */
1444 doExpressionExpansion(mb, s, (se - 1 - s));
1445 s = se;
1446 continue;
1447 case '{': /* %{...}/%{...:...} substitution */
1448 f = s+1; /* skip { */
1449 f = setNegateAndCheck(f, &negate, &chkexist);
1450 for (fe = f; (c = *fe) && !strchr(" :}", c);)
1451 fe++;
1452 switch (c) {
1453 case ':':
1454 g = fe + 1;
1455 ge = se - 1;
1456 break;
1457 case ' ':
1458 lastc = se-1;
1459 break;
1460 default:
1461 break;
1462 }
1463 break;
1464 }
1465
1466 /* XXX Everything below expects fe > f */
1467 fn = (fe - f);
1468 gn = (ge - g);
1469 if ((fe - f) <= 0) {
1470 /* XXX Process % in unknown context */
1471 c = '%'; /* XXX only need to save % */
1472 mbAppend(mb, c);
1473 #if 0
1474 mbErr(mb, 1,
1475 _("A %% is followed by an unparseable macro\n"));
1476 #endif
1477 continue;
1478 }
1479
1480 if (mb->macro_trace)
1481 printMacro(mb, s, se);
1482
1483 /* Expand builtin macros */
1484 if ((builtin = lookupBuiltin(f, fn))) {
1485 if (builtin->havearg != (g != NULL)) {
1486 mbErr(mb, 1, "%%%s: %s\n", builtin->name, builtin->havearg ?
1487 _("argument expected") : _("unexpected argument"));
1488 continue;
1489 }
1490 if (builtin->parse) {
1491 s = builtin->parse(mb, se);
1492 } else {
1493 builtin->func(mb, chkexist, negate, f, fn, g, gn);
1494 s = se;
1495 }
1496 continue;
1497 }
1498
1499 /* Expand defined macros */
1500 mep = findEntry(mb->mc, f, fn, NULL);
1501 me = (mep ? *mep : NULL);
1502
1503 if (me) {
1504 if ((me->flags & ME_AUTO) && mb->level > me->level) {
1505 /* Ignore out-of-scope automatic macros */
1506 me = NULL;
1507 } else {
1508 /* If we looked up a macro, consider it used */
1509 me->flags |= ME_USED;
1510 }
1511 }
1512
1513 /* XXX Special processing for flags and existance test */
1514 if (*f == '-' || chkexist) {
1515 if ((me == NULL && !negate) || /* Without existance, skip %{?...} */
1516 (me != NULL && negate)) { /* With existance, skip %{!?...} */
1517 s = se;
1518 continue;
1519 }
1520
1521 if (g && g < ge) { /* Expand X in %{...:X} */
1522 expandMacro(mb, g, gn);
1523 } else
1524 if (me && me->body && *me->body) {/* Expand macro body */
1525 if ((me->flags & ME_LITERAL) != 0)
1526 mbAppendStr(mb, me->body);
1527 else
1528 expandMacro(mb, me->body, 0);
1529 }
1530 s = se;
1531 continue;
1532 }
1533
1534 if (me == NULL) { /* leave unknown %... as is */
1535 /* XXX hack to permit non-overloaded %foo to be passed */
1536 c = '%'; /* XXX only need to save % */
1537 mbAppend(mb, c);
1538 continue;
1539 }
1540
1541 /* Setup args for "%name " macros with opts */
1542 if (me && me->opts != NULL) {
1543 const char *xe = grabArgs(mb, me, fe, lastc);
1544 if (xe != NULL)
1545 se = xe;
1546 }
1547
1548 /* Recursively expand body of macro */
1549 if (me->body && *me->body) {
1550 if ((me->flags & ME_LITERAL) != 0)
1551 mbAppendStr(mb, me->body);
1552 else
1553 expandMacro(mb, me->body, 0);
1554 }
1555
1556 /* Free args for "%name " macros with opts */
1557 if (me->opts != NULL)
1558 freeArgs(mb);
1559
1560 s = se;
1561 }
1562
1563 mb->buf[mb->tpos] = '\0';
1564 mb->depth--;
1565 if (mb->error != 0 || mb->expand_trace)
1566 printExpansion(mb, mb->buf+tpos, mb->buf+mb->tpos);
1567 mb->macro_trace = store_macro_trace;
1568 mb->expand_trace = store_expand_trace;
1569 exit:
1570 _free(source);
1571 return mb->error;
1572 }
1573
1574
1575 /* =============================================================== */
1576
doExpandMacros(rpmMacroContext mc,const char * src,int flags,char ** target)1577 static int doExpandMacros(rpmMacroContext mc, const char *src, int flags,
1578 char **target)
1579 {
1580 MacroBuf mb = xcalloc(1, sizeof(*mb));
1581 int rc = 0;
1582
1583 mb->buf = NULL;
1584 mb->depth = mc->depth;
1585 mb->level = mc->level;
1586 mb->macro_trace = print_macro_trace;
1587 mb->expand_trace = print_expand_trace;
1588 mb->mc = mc;
1589 mb->flags = flags;
1590
1591 rc = expandMacro(mb, src, 0);
1592
1593 mb->buf[mb->tpos] = '\0'; /* XXX just in case */
1594 /* expanded output is usually much less than alloced buffer, downsize */
1595 *target = xrealloc(mb->buf, mb->tpos + 1);
1596
1597 _free(mb);
1598 return rc;
1599 }
1600
pushMacro(rpmMacroContext mc,const char * n,const char * o,const char * b,int level,int flags)1601 static void pushMacro(rpmMacroContext mc,
1602 const char * n, const char * o, const char * b, int level, int flags)
1603 {
1604 /* new entry */
1605 rpmMacroEntry me;
1606 /* pointer into me */
1607 char *p;
1608 /* calculate sizes */
1609 size_t olen = o ? strlen(o) : 0;
1610 size_t blen = b ? strlen(b) : 0;
1611 size_t mesize = sizeof(*me) + blen + 1 + (olen ? olen + 1 : 0);
1612
1613 size_t pos;
1614 rpmMacroEntry *mep = findEntry(mc, n, 0, &pos);
1615 if (mep) {
1616 /* entry with shared name */
1617 me = xmalloc(mesize);
1618 /* copy body */
1619 me->body = p = me->arena;
1620 if (blen)
1621 memcpy(p, b, blen + 1);
1622 else
1623 *p = '\0';
1624 p += blen + 1;
1625 /* set name */
1626 me->name = (*mep)->name;
1627 }
1628 else {
1629 /* extend macro table */
1630 const int delta = 256;
1631 if (mc->n % delta == 0)
1632 mc->tab = xrealloc(mc->tab, sizeof(me) * (mc->n + delta));
1633 /* shift pos+ entries to the right */
1634 memmove(mc->tab + pos + 1, mc->tab + pos, sizeof(me) * (mc->n - pos));
1635 mc->n++;
1636 /* make slot */
1637 mc->tab[pos] = NULL;
1638 mep = &mc->tab[pos];
1639 /* entry with new name */
1640 size_t nlen = strlen(n);
1641 me = xmalloc(mesize + nlen + 1);
1642 /* copy body */
1643 me->body = p = me->arena;
1644 if (blen)
1645 memcpy(p, b, blen + 1);
1646 else
1647 *p = '\0';
1648 p += blen + 1;
1649 /* copy name */
1650 me->name = memcpy(p, n, nlen + 1);
1651 p += nlen + 1;
1652 }
1653
1654 /* copy options */
1655 if (olen)
1656 me->opts = memcpy(p, o, olen + 1);
1657 else
1658 me->opts = o ? "" : NULL;
1659 /* initialize */
1660 me->flags = flags;
1661 me->flags &= ~(ME_USED);
1662 me->level = level;
1663 /* push over previous definition */
1664 me->prev = *mep;
1665 *mep = me;
1666 }
1667
popMacro(rpmMacroContext mc,const char * n)1668 static void popMacro(rpmMacroContext mc, const char * n)
1669 {
1670 size_t pos;
1671 rpmMacroEntry *mep = findEntry(mc, n, 0, &pos);
1672 if (mep == NULL)
1673 return;
1674 /* parting entry */
1675 rpmMacroEntry me = *mep;
1676 assert(me);
1677 /* detach/pop definition */
1678 mc->tab[pos] = me->prev;
1679 /* shrink macro table */
1680 if (me->prev == NULL) {
1681 mc->n--;
1682 /* move pos+ elements to the left */
1683 memmove(mc->tab + pos, mc->tab + pos + 1, sizeof(me) * (mc->n - pos));
1684 /* deallocate */
1685 if (mc->n == 0)
1686 mc->tab = _free(mc->tab);
1687 }
1688 /* comes in a single chunk */
1689 free(me);
1690 }
1691
defineMacro(rpmMacroContext mc,const char * macro,int level)1692 static int defineMacro(rpmMacroContext mc, const char * macro, int level)
1693 {
1694 MacroBuf mb = xcalloc(1, sizeof(*mb));
1695 int rc;
1696
1697 /* XXX just enough to get by */
1698 mb->mc = mc;
1699 (void) doDefine(mb, macro, level, 0);
1700 rc = mb->error;
1701 _free(mb);
1702 return rc;
1703 }
1704
loadMacroFile(rpmMacroContext mc,const char * fn)1705 static int loadMacroFile(rpmMacroContext mc, const char * fn)
1706 {
1707 FILE *fd = fopen(fn, "r");
1708 size_t blen = MACROBUFSIZ;
1709 char *buf = xmalloc(blen);
1710 int rc = -1;
1711 int nfailed = 0;
1712 int lineno = 0;
1713 int nlines = 0;
1714
1715 if (fd == NULL)
1716 goto exit;
1717
1718 pushMacro(mc, "__file_name", NULL, fn, RMIL_MACROFILES, ME_LITERAL);
1719
1720 buf[0] = '\0';
1721 while ((nlines = rdcl(buf, blen, fd)) > 0) {
1722 char c, *n;
1723 char lnobuf[16];
1724
1725 lineno += nlines;
1726 n = buf;
1727 SKIPBLANK(n, c);
1728
1729 if (c != '%')
1730 continue;
1731 n++; /* skip % */
1732
1733 snprintf(lnobuf, sizeof(lnobuf), "%d", lineno);
1734 pushMacro(mc, "__file_lineno", NULL, lnobuf, RMIL_MACROFILES, ME_LITERAL);
1735 if (defineMacro(mc, n, RMIL_MACROFILES))
1736 nfailed++;
1737 popMacro(mc, "__file_lineno");
1738 }
1739 fclose(fd);
1740 popMacro(mc, "__file_name");
1741
1742 rc = (nfailed > 0);
1743
1744 exit:
1745 _free(buf);
1746 return rc;
1747 }
1748
copyMacros(rpmMacroContext src,rpmMacroContext dst,int level)1749 static void copyMacros(rpmMacroContext src, rpmMacroContext dst, int level)
1750 {
1751 for (int i = 0; i < src->n; i++) {
1752 rpmMacroEntry me = src->tab[i];
1753 assert(me);
1754 pushMacro(dst, me->name, me->opts, me->body, level, me->flags);
1755 }
1756 }
1757
1758 /* External interfaces */
1759
rpmExpandMacros(rpmMacroContext mc,const char * sbuf,char ** obuf,int flags)1760 int rpmExpandMacros(rpmMacroContext mc, const char * sbuf, char ** obuf, int flags)
1761 {
1762 char *target = NULL;
1763 int rc;
1764
1765 mc = rpmmctxAcquire(mc);
1766 rc = doExpandMacros(mc, sbuf, flags, &target);
1767 rpmmctxRelease(mc);
1768
1769 if (rc) {
1770 free(target);
1771 return -1;
1772 } else {
1773 *obuf = target;
1774 return 1;
1775 }
1776 }
1777
1778 void
rpmDumpMacroTable(rpmMacroContext mc,FILE * fp)1779 rpmDumpMacroTable(rpmMacroContext mc, FILE * fp)
1780 {
1781 mc = rpmmctxAcquire(mc);
1782 if (fp == NULL) fp = stderr;
1783
1784 fprintf(fp, "========================\n");
1785 for (int i = 0; i < mc->n; i++) {
1786 rpmMacroEntry me = mc->tab[i];
1787 assert(me);
1788 fprintf(fp, "%3d%c %s", me->level,
1789 ((me->flags & ME_USED) ? '=' : ':'), me->name);
1790 if (me->opts && *me->opts)
1791 fprintf(fp, "(%s)", me->opts);
1792 if (me->body && *me->body)
1793 fprintf(fp, "\t%s", me->body);
1794 fprintf(fp, "\n");
1795 }
1796 fprintf(fp, _("======================== active %d empty %d\n"),
1797 mc->n, 0);
1798 rpmmctxRelease(mc);
1799 }
1800
rpmPushMacroFlags(rpmMacroContext mc,const char * n,const char * o,const char * b,int level,rpmMacroFlags flags)1801 int rpmPushMacroFlags(rpmMacroContext mc,
1802 const char * n, const char * o, const char * b,
1803 int level, rpmMacroFlags flags)
1804 {
1805 mc = rpmmctxAcquire(mc);
1806 pushMacro(mc, n, o, b, level, flags & RPMMACRO_LITERAL ? ME_LITERAL : ME_NONE);
1807 rpmmctxRelease(mc);
1808 return 0;
1809 }
1810
rpmPushMacro(rpmMacroContext mc,const char * n,const char * o,const char * b,int level)1811 int rpmPushMacro(rpmMacroContext mc,
1812 const char * n, const char * o, const char * b, int level)
1813 {
1814 return rpmPushMacroFlags(mc, n, o, b, level, RPMMACRO_DEFAULT);
1815 }
1816
rpmPopMacro(rpmMacroContext mc,const char * n)1817 int rpmPopMacro(rpmMacroContext mc, const char * n)
1818 {
1819 mc = rpmmctxAcquire(mc);
1820 popMacro(mc, n);
1821 rpmmctxRelease(mc);
1822 return 0;
1823 }
1824
1825 int
rpmDefineMacro(rpmMacroContext mc,const char * macro,int level)1826 rpmDefineMacro(rpmMacroContext mc, const char * macro, int level)
1827 {
1828 int rc;
1829 mc = rpmmctxAcquire(mc);
1830 rc = defineMacro(mc, macro, level);
1831 rpmmctxRelease(mc);
1832 return rc;
1833 }
1834
rpmMacroIsDefined(rpmMacroContext mc,const char * n)1835 int rpmMacroIsDefined(rpmMacroContext mc, const char *n)
1836 {
1837 int defined = 0;
1838 if ((mc = rpmmctxAcquire(mc)) != NULL) {
1839 if (findEntry(mc, n, 0, NULL))
1840 defined = 1;
1841 rpmmctxRelease(mc);
1842 }
1843 return defined;
1844 }
1845
rpmMacroIsParametric(rpmMacroContext mc,const char * n)1846 int rpmMacroIsParametric(rpmMacroContext mc, const char *n)
1847 {
1848 int parametric = 0;
1849 if ((mc = rpmmctxAcquire(mc)) != NULL) {
1850 rpmMacroEntry *mep = findEntry(mc, n, 0, NULL);
1851 if (mep && (*mep)->opts)
1852 parametric = 1;
1853 rpmmctxRelease(mc);
1854 }
1855 return parametric;
1856 }
1857
1858 void
rpmLoadMacros(rpmMacroContext mc,int level)1859 rpmLoadMacros(rpmMacroContext mc, int level)
1860 {
1861 rpmMacroContext gmc;
1862 if (mc == NULL || mc == rpmGlobalMacroContext)
1863 return;
1864
1865 gmc = rpmmctxAcquire(NULL);
1866 mc = rpmmctxAcquire(mc);
1867
1868 copyMacros(mc, gmc, level);
1869
1870 rpmmctxRelease(mc);
1871 rpmmctxRelease(gmc);
1872 }
1873
1874 int
rpmLoadMacroFile(rpmMacroContext mc,const char * fn)1875 rpmLoadMacroFile(rpmMacroContext mc, const char * fn)
1876 {
1877 int rc;
1878
1879 mc = rpmmctxAcquire(mc);
1880 rc = loadMacroFile(mc, fn);
1881 rpmmctxRelease(mc);
1882
1883 return rc;
1884 }
1885
1886 void
rpmInitMacros(rpmMacroContext mc,const char * macrofiles)1887 rpmInitMacros(rpmMacroContext mc, const char * macrofiles)
1888 {
1889 ARGV_t pattern, globs = NULL;
1890 rpmMacroContext climc;
1891
1892 if (macrofiles == NULL)
1893 return;
1894
1895 argvSplit(&globs, macrofiles, ":");
1896 mc = rpmmctxAcquire(mc);
1897 for (pattern = globs; *pattern; pattern++) {
1898 ARGV_t path, files = NULL;
1899
1900 /* Glob expand the macro file path element, expanding ~ to $HOME. */
1901 if (rpmGlob(*pattern, NULL, &files) != 0) {
1902 continue;
1903 }
1904
1905 /* Read macros from each file. */
1906 for (path = files; *path; path++) {
1907 if (rpmFileHasSuffix(*path, ".rpmnew") ||
1908 rpmFileHasSuffix(*path, ".rpmsave") ||
1909 rpmFileHasSuffix(*path, ".rpmorig")) {
1910 continue;
1911 }
1912 (void) loadMacroFile(mc, *path);
1913 }
1914 argvFree(files);
1915 }
1916 argvFree(globs);
1917
1918 /* Reload cmdline macros */
1919 climc = rpmmctxAcquire(rpmCLIMacroContext);
1920 copyMacros(climc, mc, RMIL_CMDLINE);
1921 rpmmctxRelease(climc);
1922
1923 rpmmctxRelease(mc);
1924 }
1925
1926 void
rpmFreeMacros(rpmMacroContext mc)1927 rpmFreeMacros(rpmMacroContext mc)
1928 {
1929 mc = rpmmctxAcquire(mc);
1930 while (mc->n > 0) {
1931 /* remove from the end to avoid memmove */
1932 rpmMacroEntry me = mc->tab[mc->n - 1];
1933 popMacro(mc, me->name);
1934 }
1935 rpmmctxRelease(mc);
1936 }
1937
1938 char *
rpmExpand(const char * arg,...)1939 rpmExpand(const char *arg, ...)
1940 {
1941 size_t blen = 0;
1942 char *buf = NULL, *ret = NULL;
1943 char *pe;
1944 const char *s;
1945 va_list ap;
1946 rpmMacroContext mc;
1947
1948 if (arg == NULL) {
1949 ret = xstrdup("");
1950 goto exit;
1951 }
1952
1953 /* precalculate unexpanded size */
1954 va_start(ap, arg);
1955 for (s = arg; s != NULL; s = va_arg(ap, const char *))
1956 blen += strlen(s);
1957 va_end(ap);
1958
1959 buf = xmalloc(blen + 1);
1960 buf[0] = '\0';
1961
1962 va_start(ap, arg);
1963 for (pe = buf, s = arg; s != NULL; s = va_arg(ap, const char *))
1964 pe = stpcpy(pe, s);
1965 va_end(ap);
1966
1967 mc = rpmmctxAcquire(NULL);
1968 (void) doExpandMacros(mc, buf, 0, &ret);
1969 rpmmctxRelease(mc);
1970
1971 free(buf);
1972 exit:
1973 return ret;
1974 }
1975
1976 int
rpmExpandNumeric(const char * arg)1977 rpmExpandNumeric(const char *arg)
1978 {
1979 char *val;
1980 int rc;
1981
1982 if (arg == NULL)
1983 return 0;
1984
1985 val = rpmExpand(arg, NULL);
1986 if (!(val && *val != '%'))
1987 rc = 0;
1988 else if (*val == 'Y' || *val == 'y')
1989 rc = 1;
1990 else if (*val == 'N' || *val == 'n')
1991 rc = 0;
1992 else {
1993 char *end;
1994 rc = strtol(val, &end, 0);
1995 if (!(end && *end == '\0'))
1996 rc = 0;
1997 }
1998 free(val);
1999
2000 return rc;
2001 }
2002