Bug Summary

File:rpmio/macro.c
Warning:line 1456, column 2
Value stored to 'rc' is never read

Annotated Source Code

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