Bug Summary

File:rpmio/macro.c
Warning:line 1480, 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_DEPTH64 64
114static int max_macro_depth = _MAX_MACRO_DEPTH64;
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, _("Macro %%%s has illegal name (%s)\n")dcgettext ("rpm", "Macro %%%s has illegal name (%s)\n", 5),
549 n, expandbody ? "%global": "%define");
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, 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)((void) sizeof ((me) == 0), __extension__ ({ if (me) ; else __assert_fail
("me", "macro.c", 629, __extension__ __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 loglevel, const char * msg, size_t msglen)
792{
793 char *buf = NULL((void*)0);
794
795 (void) expandThis(mb, msg, msglen, &buf);
796 rpmlog(loglevel, "%s\n", buf);
797 _free(buf)rfree((buf));
798}
799
800static void doLua(MacroBuf mb, const char * f, size_t fn, const char * g, size_t gn)
801{
802#ifdef WITH_LUA1
803 rpmlua lua = NULL((void*)0); /* Global state. */
804 char *scriptbuf = xmalloc(gn + 1)rmalloc((gn + 1));
805 char *printbuf;
806 rpmMacroContext mc = mb->mc;
807 int odepth = mc->depth;
808 int olevel = mc->level;
809
810 if (g != NULL((void*)0) && gn > 0)
811 memcpy(scriptbuf, g, gn);
812 scriptbuf[gn] = '\0';
813 rpmluaPushPrintBuffer(lua);
814 mc->depth = mb->depth;
815 mc->level = mb->level;
816 if (rpmluaRunScript(lua, scriptbuf, NULL((void*)0)) == -1)
817 mb->error = 1;
818 mc->depth = odepth;
819 mc->level = olevel;
820 printbuf = rpmluaPopPrintBuffer(lua);
821 if (printbuf) {
822 mbAppendStr(mb, printbuf);
823 free(printbuf);
824 }
825 free(scriptbuf);
826#else
827 rpmlog(RPMLOG_ERR, _("<lua> scriptlet support not built in\n")dcgettext ("rpm", "<lua> scriptlet support not built in\n"
, 5)
);
828 mb->error = 1;
829#endif
830}
831
832/**
833 * Execute macro primitives.
834 * @param mb macro expansion state
835 * @param negate should logic be inverted?
836 * @param f beginning of field f
837 * @param fn length of field f
838 * @param g beginning of field g
839 * @param gn length of field g
840 */
841static void
842doFoo(MacroBuf mb, int negate, const char * f, size_t fn,
843 const char * g, size_t gn)
844{
845 char *buf = NULL((void*)0);
846 char *b = NULL((void*)0), *be;
847 int c;
848
849 if (g != NULL((void*)0) && gn > 0) {
850 (void) expandThis(mb, g, gn, &buf);
851 } else {
852 buf = xmalloc(MACROBUFSIZ + fn + gn)rmalloc(((8192 * 2) + fn + gn));
853 buf[0] = '\0';
854 }
855 if (STREQ("basename", f, fn)((fn) == (sizeof("basename")-1) && rstreqn(("basename"
), (f), (fn)))
) {
856 if ((b = strrchr(buf, '/')) == NULL((void*)0))
857 b = buf;
858 else
859 b++;
860 } else if (STREQ("dirname", f, fn)((fn) == (sizeof("dirname")-1) && rstreqn(("dirname")
, (f), (fn)))
) {
861 if ((b = strrchr(buf, '/')) != NULL((void*)0))
862 *b = '\0';
863 b = buf;
864 } else if (STREQ("shrink", f, fn)((fn) == (sizeof("shrink")-1) && rstreqn(("shrink"), (
f), (fn)))
) {
865 /*
866 * shrink body by removing all leading and trailing whitespaces and
867 * reducing intermediate whitespaces to a single space character.
868 */
869 size_t i = 0, j = 0;
870 size_t buflen = strlen(buf);
871 int was_space = 0;
872 while (i < buflen) {
873 if (risspace(buf[i])) {
874 was_space = 1;
875 i++;
876 continue;
877 } else if (was_space) {
878 was_space = 0;
879 if (j > 0) /* remove leading blanks at all */
880 buf[j++] = ' ';
881 }
882 buf[j++] = buf[i++];
883 }
884 buf[j] = '\0';
885 b = buf;
886 } else if (STREQ("suffix", f, fn)((fn) == (sizeof("suffix")-1) && rstreqn(("suffix"), (
f), (fn)))
) {
887 if ((b = strrchr(buf, '.')) != NULL((void*)0))
888 b++;
889 } else if (STREQ("expand", f, fn)((fn) == (sizeof("expand")-1) && rstreqn(("expand"), (
f), (fn)))
) {
890 b = buf;
891 } else if (STREQ("verbose", f, fn)((fn) == (sizeof("verbose")-1) && rstreqn(("verbose")
, (f), (fn)))
) {
892 if (negate)
893 b = (rpmIsVerbose()(rpmlogSetMask(0) >= (1 << ((unsigned)(RPMLOG_INFO))
))
? NULL((void*)0) : buf);
894 else
895 b = (rpmIsVerbose()(rpmlogSetMask(0) >= (1 << ((unsigned)(RPMLOG_INFO))
))
? buf : NULL((void*)0));
896 } 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)))
) {
897 (void)urlPath(buf, (const char **)&b);
898 if (*b == '\0') b = "/";
899 } else if (STREQ("uncompress", f, fn)((fn) == (sizeof("uncompress")-1) && rstreqn(("uncompress"
), (f), (fn)))
) {
900 rpmCompressedMagic compressed = COMPRESSED_OTHER;
901 for (b = buf; (c = *b) && isblank(c)((c) == ' ' || (c) == '\t');)
902 b++;
903 for (be = b; (c = *be) && !isblank(c)((c) == ' ' || (c) == '\t');)
904 be++;
905 *be++ = '\0';
906 (void) rpmFileIsCompressed(b, &compressed);
907 switch (compressed) {
908 default:
909 case COMPRESSED_NOT:
910 sprintf(be, "%%__cat %s", b);
911 break;
912 case COMPRESSED_OTHER:
913 sprintf(be, "%%__gzip -dc %s", b);
914 break;
915 case COMPRESSED_BZIP2:
916 sprintf(be, "%%__bzip2 -dc %s", b);
917 break;
918 case COMPRESSED_ZIP:
919 sprintf(be, "%%__unzip %s", b);
920 break;
921 case COMPRESSED_LZMA:
922 case COMPRESSED_XZ:
923 sprintf(be, "%%__xz -dc %s", b);
924 break;
925 case COMPRESSED_LZIP:
926 sprintf(be, "%%__lzip -dc %s", b);
927 break;
928 case COMPRESSED_LRZIP:
929 sprintf(be, "%%__lrzip -dqo- %s", b);
930 break;
931 case COMPRESSED_7ZIP:
932 sprintf(be, "%%__7zip x %s", b);
933 break;
934 case COMPRESSED_ZSTD:
935 sprintf(be, "%%__zstd -dc %s", b);
936 break;
937 }
938 b = be;
939 } else if (STREQ("getenv", f, fn)((fn) == (sizeof("getenv")-1) && rstreqn(("getenv"), (
f), (fn)))
) {
940 b = getenv(buf)secure_getenv(buf);
941 } else if (STREQ("getconfdir", f, fn)((fn) == (sizeof("getconfdir")-1) && rstreqn(("getconfdir"
), (f), (fn)))
) {
942 sprintf(buf, "%s", rpmConfigDir());
943 b = buf;
944 } else if (STREQ("S", f, fn)((fn) == (sizeof("S")-1) && rstreqn(("S"), (f), (fn))
)
) {
945 for (b = buf; (c = *b) && risdigit(c);)
946 b++;
947 if (!c) { /* digit index */
948 b++;
949 sprintf(b, "%%SOURCE%s", buf);
950 } else
951 b = buf;
952 } else if (STREQ("P", f, fn)((fn) == (sizeof("P")-1) && rstreqn(("P"), (f), (fn))
)
) {
953 for (b = buf; (c = *b) && risdigit(c);)
954 b++;
955 if (!c) { /* digit index */
956 b++;
957 sprintf(b, "%%PATCH%s", buf);
958 } else
959 b = buf;
960 } else if (STREQ("F", f, fn)((fn) == (sizeof("F")-1) && rstreqn(("F"), (f), (fn))
)
) {
961 b = buf + strlen(buf) + 1;
962 sprintf(b, "file%s.file", buf);
963 }
964
965 if (b) {
966 (void) expandMacro(mb, b, 0);
967 }
968 free(buf);
969}
970
971/**
972 * The main macro recursion loop.
973 * @param mb macro expansion state
974 * @param src string to expand
975 * @param slen length of string buffer
976 * @return 0 on success, 1 on failure
977 */
978static int
979expandMacro(MacroBuf mb, const char *src, size_t slen)
980{
981 rpmMacroEntry *mep;
982 rpmMacroEntry me;
983 const char *s = src, *se;
984 const char *f, *fe;
985 const char *g, *ge;
986 size_t fn, gn, tpos;
987 int c;
988 int negate;
989 const char * lastc;
990 int chkexist;
991 char *source = NULL((void*)0);
992
993 /*
994 * Always make a (terminated) copy of the source string.
995 * Beware: avoiding the copy when src is known \0-terminated seems like
996 * an obvious opportunity for optimization, but doing that breaks
997 * a special case of macro undefining itself.
998 */
999 if (!slen)
1000 slen = strlen(src);
1001 source = xmalloc(slen + 1)rmalloc((slen + 1));
1002 strncpy(source, src, slen);
1003 source[slen] = '\0';
1004 s = source;
1005
1006 if (mb->buf == NULL((void*)0)) {
1007 size_t blen = MACROBUFSIZ(8192 * 2) + slen;
1008 mb->buf = xmalloc(blen + 1)rmalloc((blen + 1));
1009 mb->buf[0] = '\0';
1010 mb->tpos = 0;
1011 mb->nb = blen;
1012 }
1013 tpos = mb->tpos; /* save expansion pointer for printExpand */
1014
1015 if (++mb->depth > max_macro_depth) {
1016 rpmlog(RPMLOG_ERR,
1017 _("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)
);
1018 mb->depth--;
1019 mb->expand_trace = 1;
1020 mb->error = 1;
1021 goto exit;
1022 }
1023
1024 while (mb->error == 0 && (c = *s) != '\0') {
1025 s++;
1026 /* Copy text until next macro */
1027 switch (c) {
1028 case '%':
1029 if (*s) { /* Ensure not end-of-string. */
1030 if (*s != '%')
1031 break;
1032 s++; /* skip first % in %% */
1033 if (mb->escape)
1034 mbAppend(mb, c);
1035 }
1036 default:
1037 mbAppend(mb, c);
1038 continue;
1039 break;
1040 }
1041
1042 /* Expand next macro */
1043 f = fe = NULL((void*)0);
1044 g = ge = NULL((void*)0);
1045 if (mb->depth > 1) /* XXX full expansion for outermost level */
1046 tpos = mb->tpos; /* save expansion pointer for printExpand */
1047 negate = 0;
1048 lastc = NULL((void*)0);
1049 chkexist = 0;
1050 switch ((c = *s)) {
1051 default: /* %name substitution */
1052 while (*s != '\0' && strchr("!?", *s) != NULL((void*)0)) {
1053 switch (*s++) {
1054 case '!':
1055 negate = ((negate + 1) % 2);
1056 break;
1057 case '?':
1058 chkexist++;
1059 break;
1060 }
1061 }
1062 f = se = s;
1063 if (*se == '-')
1064 se++;
1065 while ((c = *se) && (risalnum(c) || c == '_'))
1066 se++;
1067 /* Recognize non-alnum macros too */
1068 switch (*se) {
1069 case '*':
1070 se++;
1071 if (*se == '*') se++;
1072 break;
1073 case '#':
1074 se++;
1075 break;
1076 default:
1077 break;
1078 }
1079 fe = se;
1080 /* For "%name " macros ... */
1081 if ((c = *fe) && isblank(c)((c) == ' ' || (c) == '\t'))
1082 if ((lastc = strchr(fe,'\n')) == NULL((void*)0))
1083 lastc = strchr(fe, '\0');
1084 break;
1085 case '(': /* %(...) shell escape */
1086 if ((se = matchchar(s, c, ')')) == NULL((void*)0)) {
1087 rpmlog(RPMLOG_ERR, _("Unterminated %c: %s\n")dcgettext ("rpm", "Unterminated %c: %s\n", 5), (char)c, s);
1088 mb->error = 1;
1089 continue;
1090 }
1091 if (mb->macro_trace)
1092 printMacro(mb, s, se+1);
1093
1094 s++; /* skip ( */
1095 doShellEscape(mb, s, (se - s));
1096 se++; /* skip ) */
1097
1098 s = se;
1099 continue;
1100 break;
1101 case '{': /* %{...}/%{...:...} substitution */
1102 if ((se = matchchar(s, c, '}')) == NULL((void*)0)) {
1103 rpmlog(RPMLOG_ERR, _("Unterminated %c: %s\n")dcgettext ("rpm", "Unterminated %c: %s\n", 5), (char)c, s);
1104 mb->error = 1;
1105 continue;
1106 }
1107 f = s+1;/* skip { */
1108 se++; /* skip } */
1109 while (strchr("!?", *f) != NULL((void*)0)) {
1110 switch (*f++) {
1111 case '!':
1112 negate = ((negate + 1) % 2);
1113 break;
1114 case '?':
1115 chkexist++;
1116 break;
1117 }
1118 }
1119 for (fe = f; (c = *fe) && !strchr(" :}", c);)
1120 fe++;
1121 switch (c) {
1122 case ':':
1123 g = fe + 1;
1124 ge = se - 1;
1125 break;
1126 case ' ':
1127 lastc = se-1;
1128 break;
1129 default:
1130 break;
1131 }
1132 break;
1133 }
1134
1135 /* XXX Everything below expects fe > f */
1136 fn = (fe - f);
1137 gn = (ge - g);
1138 if ((fe - f) <= 0) {
1139 /* XXX Process % in unknown context */
1140 c = '%'; /* XXX only need to save % */
1141 mbAppend(mb, c);
1142#if 0
1143 rpmlog(RPMLOG_ERR,
1144 _("A %% is followed by an unparseable macro\n")dcgettext ("rpm", "A %% is followed by an unparseable macro\n"
, 5)
);
1145#endif
1146 s = se;
1147 continue;
1148 }
1149
1150 if (mb->macro_trace)
1151 printMacro(mb, s, se);
1152
1153 /* Expand builtin macros */
1154 if (STREQ("load", f, fn)((fn) == (sizeof("load")-1) && rstreqn(("load"), (f),
(fn)))
) {
1155 char *arg = NULL((void*)0);
1156 if (g && gn > 0 && expandThis(mb, g, gn, &arg) == 0) {
1157 /* Print failure iff %{load:...} or %{!?load:...} */
1158 if (loadMacroFile(mb->mc, arg) && chkexist == negate) {
1159 rpmlog(RPMLOG_ERR, _("failed to load macro file %s")dcgettext ("rpm", "failed to load macro file %s", 5), arg);
1160 mb->error = 1;
1161 }
1162 }
1163 free(arg);
1164 s = se;
1165 continue;
1166 }
1167 if (STREQ("global", f, fn)((fn) == (sizeof("global")-1) && rstreqn(("global"), (
f), (fn)))
) {
1168 s = doDefine(mb, se, slen - (se - s), RMIL_GLOBAL0, 1);
1169 continue;
1170 }
1171 if (STREQ("define", f, fn)((fn) == (sizeof("define")-1) && rstreqn(("define"), (
f), (fn)))
) {
1172 s = doDefine(mb, se, slen - (se - s), mb->level, 0);
1173 continue;
1174 }
1175 if (STREQ("undefine", f, fn)((fn) == (sizeof("undefine")-1) && rstreqn(("undefine"
), (f), (fn)))
) {
1176 s = doUndefine(mb, se, slen - (se - s));
1177 continue;
1178 }
1179
1180 if (STREQ("echo", f, fn)((fn) == (sizeof("echo")-1) && rstreqn(("echo"), (f),
(fn)))
||
1181 STREQ("warn", f, fn)((fn) == (sizeof("warn")-1) && rstreqn(("warn"), (f),
(fn)))
||
1182 STREQ("error", f, fn)((fn) == (sizeof("error")-1) && rstreqn(("error"), (f
), (fn)))
) {
1183 int loglevel = RPMLOG_NOTICE; /* assume echo */
1184 if (STREQ("error", f, fn)((fn) == (sizeof("error")-1) && rstreqn(("error"), (f
), (fn)))
) {
1185 loglevel = RPMLOG_ERR;
1186 mb->error = 1;
1187 } else if (STREQ("warn", f, fn)((fn) == (sizeof("warn")-1) && rstreqn(("warn"), (f),
(fn)))
) {
1188 loglevel = RPMLOG_WARNING;
1189 }
1190 if (g != NULL((void*)0) && g < ge)
1191 doOutput(mb, loglevel, g, gn);
1192 else
1193 doOutput(mb, loglevel, "", 0);
1194 s = se;
1195 continue;
1196 }
1197
1198 if (STREQ("trace", f, fn)((fn) == (sizeof("trace")-1) && rstreqn(("trace"), (f
), (fn)))
) {
1199 /* XXX TODO restore expand_trace/macro_trace to 0 on return */
1200 mb->expand_trace = mb->macro_trace = (negate ? 0 : mb->depth);
1201 if (mb->depth == 1) {
1202 print_macro_trace = mb->macro_trace;
1203 print_expand_trace = mb->expand_trace;
1204 }
1205 s = se;
1206 continue;
1207 }
1208
1209 if (STREQ("dump", f, fn)((fn) == (sizeof("dump")-1) && rstreqn(("dump"), (f),
(fn)))
) {
1210 rpmDumpMacroTable(mb->mc, NULL((void*)0));
1211 while (iseol(*se)((*se) == '\n' || (*se) == '\r'))
1212 se++;
1213 s = se;
1214 continue;
1215 }
1216
1217 if (STREQ("lua", f, fn)((fn) == (sizeof("lua")-1) && rstreqn(("lua"), (f), (
fn)))
) {
1218 doLua(mb, f, fn, g, gn);
1219 s = se;
1220 continue;
1221 }
1222
1223 /* XXX necessary but clunky */
1224 if (STREQ("basename", f, fn)((fn) == (sizeof("basename")-1) && rstreqn(("basename"
), (f), (fn)))
||
1225 STREQ("dirname", f, fn)((fn) == (sizeof("dirname")-1) && rstreqn(("dirname")
, (f), (fn)))
||
1226 STREQ("shrink", f, fn)((fn) == (sizeof("shrink")-1) && rstreqn(("shrink"), (
f), (fn)))
||
1227 STREQ("suffix", f, fn)((fn) == (sizeof("suffix")-1) && rstreqn(("suffix"), (
f), (fn)))
||
1228 STREQ("expand", f, fn)((fn) == (sizeof("expand")-1) && rstreqn(("expand"), (
f), (fn)))
||
1229 STREQ("verbose", f, fn)((fn) == (sizeof("verbose")-1) && rstreqn(("verbose")
, (f), (fn)))
||
1230 STREQ("uncompress", f, fn)((fn) == (sizeof("uncompress")-1) && rstreqn(("uncompress"
), (f), (fn)))
||
1231 STREQ("url2path", f, fn)((fn) == (sizeof("url2path")-1) && rstreqn(("url2path"
), (f), (fn)))
||
1232 STREQ("u2p", f, fn)((fn) == (sizeof("u2p")-1) && rstreqn(("u2p"), (f), (
fn)))
||
1233 STREQ("getenv", f, fn)((fn) == (sizeof("getenv")-1) && rstreqn(("getenv"), (
f), (fn)))
||
1234 STREQ("getconfdir", f, fn)((fn) == (sizeof("getconfdir")-1) && rstreqn(("getconfdir"
), (f), (fn)))
||
1235 STREQ("S", f, fn)((fn) == (sizeof("S")-1) && rstreqn(("S"), (f), (fn))
)
||
1236 STREQ("P", f, fn)((fn) == (sizeof("P")-1) && rstreqn(("P"), (f), (fn))
)
||
1237 STREQ("F", f, fn)((fn) == (sizeof("F")-1) && rstreqn(("F"), (f), (fn))
)
)
1238 {
1239 /* FIX: verbose may be set */
1240 doFoo(mb, negate, f, fn, g, gn);
1241 s = se;
1242 continue;
1243 }
1244
1245 /* Expand defined macros */
1246 mep = findEntry(mb->mc, f, fn, NULL((void*)0));
1247 me = (mep ? *mep : NULL((void*)0));
1248
1249 if (me) {
1250 if ((me->flags & ME_AUTO) && mb->level > me->level) {
1251 /* Ignore out-of-scope automatic macros */
1252 me = NULL((void*)0);
1253 } else {
1254 /* If we looked up a macro, consider it used */
1255 me->flags |= ME_USED;
1256 }
1257 }
1258
1259 /* XXX Special processing for flags */
1260 if (*f == '-') {
1261 if ((me == NULL((void*)0) && !negate) || /* Without -f, skip %{-f...} */
1262 (me != NULL((void*)0) && negate)) { /* With -f, skip %{!-f...} */
1263 s = se;
1264 continue;
1265 }
1266
1267 if (g && g < ge) { /* Expand X in %{-f:X} */
1268 expandMacro(mb, g, gn);
1269 } else
1270 if (me && me->body && *me->body) {/* Expand %{-f}/%{-f*} */
1271 expandMacro(mb, me->body, 0);
1272 }
1273 s = se;
1274 continue;
1275 }
1276
1277 /* XXX Special processing for macro existence */
1278 if (chkexist) {
1279 if ((me == NULL((void*)0) && !negate) || /* Without -f, skip %{?f...} */
1280 (me != NULL((void*)0) && negate)) { /* With -f, skip %{!?f...} */
1281 s = se;
1282 continue;
1283 }
1284 if (g && g < ge) { /* Expand X in %{?f:X} */
1285 expandMacro(mb, g, gn);
1286 } else
1287 if (me && me->body && *me->body) { /* Expand %{?f}/%{?f*} */
1288 expandMacro(mb, me->body, 0);
1289 }
1290 s = se;
1291 continue;
1292 }
1293
1294 if (me == NULL((void*)0)) { /* leave unknown %... as is */
1295 /* XXX hack to permit non-overloaded %foo to be passed */
1296 c = '%'; /* XXX only need to save % */
1297 mbAppend(mb, c);
1298 continue;
1299 }
1300
1301 /* Setup args for "%name " macros with opts */
1302 if (me && me->opts != NULL((void*)0)) {
1303 const char *xe = grabArgs(mb, me, fe, lastc);
1304 if (xe != NULL((void*)0))
1305 se = xe;
1306 }
1307
1308 /* Recursively expand body of macro */
1309 if (me->body && *me->body) {
1310 expandMacro(mb, me->body, 0);
1311 }
1312
1313 /* Free args for "%name " macros with opts */
1314 if (me->opts != NULL((void*)0))
1315 freeArgs(mb, 1);
1316
1317 s = se;
1318 }
1319
1320 mb->buf[mb->tpos] = '\0';
1321 mb->depth--;
1322 if (mb->error != 0 || mb->expand_trace)
1323 printExpansion(mb, mb->buf+tpos, mb->buf+mb->tpos);
1324exit:
1325 _free(source)rfree((source));
1326 return mb->error;
1327}
1328
1329
1330/* =============================================================== */
1331
1332static int doExpandMacros(rpmMacroContext mc, const char *src, int flags,
1333 char **target)
1334{
1335 MacroBuf mb = xcalloc(1, sizeof(*mb))rcalloc((1), (sizeof(*mb)));
1336 int rc = 0;
1337
1338 mb->buf = NULL((void*)0);
1339 mb->depth = mc->depth;
1340 mb->level = mc->level;
1341 mb->macro_trace = print_macro_trace;
1342 mb->expand_trace = print_expand_trace;
1343 mb->mc = mc;
1344 mb->flags = flags;
1345
1346 rc = expandMacro(mb, src, 0);
1347
1348 mb->buf[mb->tpos] = '\0'; /* XXX just in case */
1349 /* expanded output is usually much less than alloced buffer, downsize */
1350 *target = xrealloc(mb->buf, mb->tpos + 1)rrealloc((mb->buf), (mb->tpos + 1));
1351
1352 _free(mb)rfree((mb));
1353 return rc;
1354}
1355
1356static void pushMacro(rpmMacroContext mc,
1357 const char * n, const char * o, const char * b, int level, int flags)
1358{
1359 /* new entry */
1360 rpmMacroEntry me;
1361 /* pointer into me */
1362 char *p;
1363 /* calculate sizes */
1364 size_t olen = o ? strlen(o) : 0;
1365 size_t blen = b ? strlen(b) : 0;
1366 size_t mesize = sizeof(*me) + blen + 1 + (olen ? olen + 1 : 0);
1367
1368 size_t pos;
1369 rpmMacroEntry *mep = findEntry(mc, n, 0, &pos);
1370 if (mep) {
1371 /* entry with shared name */
1372 me = xmalloc(mesize)rmalloc((mesize));
1373 /* copy body */
1374 me->body = p = me->arena;
1375 if (blen)
1376 memcpy(p, b, blen + 1);
1377 else
1378 *p = '\0';
1379 p += blen + 1;
1380 /* set name */
1381 me->name = (*mep)->name;
1382 }
1383 else {
1384 /* extend macro table */
1385 const int delta = 256;
1386 if (mc->n % delta == 0)
1387 mc->tab = xrealloc(mc->tab, sizeof(me) * (mc->n + delta))rrealloc((mc->tab), (sizeof(me) * (mc->n + delta)));
1388 /* shift pos+ entries to the right */
1389 memmove(mc->tab + pos + 1, mc->tab + pos, sizeof(me) * (mc->n - pos));
1390 mc->n++;
1391 /* make slot */
1392 mc->tab[pos] = NULL((void*)0);
1393 mep = &mc->tab[pos];
1394 /* entry with new name */
1395 size_t nlen = strlen(n);
1396 me = xmalloc(mesize + nlen + 1)rmalloc((mesize + nlen + 1));
1397 /* copy body */
1398 me->body = p = me->arena;
1399 if (blen)
1400 memcpy(p, b, blen + 1);
1401 else
1402 *p = '\0';
1403 p += blen + 1;
1404 /* copy name */
1405 me->name = memcpy(p, n, nlen + 1);
1406 p += nlen + 1;
1407 }
1408
1409 /* copy options */
1410 if (olen)
1411 me->opts = memcpy(p, o, olen + 1);
1412 else
1413 me->opts = o ? "" : NULL((void*)0);
1414 /* initialize */
1415 me->flags = flags;
1416 me->flags &= ~(ME_USED);
1417 me->level = level;
1418 /* push over previous definition */
1419 me->prev = *mep;
1420 *mep = me;
1421}
1422
1423static void popMacro(rpmMacroContext mc, const char * n)
1424{
1425 size_t pos;
1426 rpmMacroEntry *mep = findEntry(mc, n, 0, &pos);
1427 if (mep == NULL((void*)0))
1428 return;
1429 /* parting entry */
1430 rpmMacroEntry me = *mep;
1431 assert(me)((void) sizeof ((me) == 0), __extension__ ({ if (me) ; else __assert_fail
("me", "macro.c", 1431, __extension__ __PRETTY_FUNCTION__); }
))
;
1432 /* detach/pop definition */
1433 mc->tab[pos] = me->prev;
1434 /* shrink macro table */
1435 if (me->prev == NULL((void*)0)) {
1436 mc->n--;
1437 /* move pos+ elements to the left */
1438 memmove(mc->tab + pos, mc->tab + pos + 1, sizeof(me) * (mc->n - pos));
1439 /* deallocate */
1440 if (mc->n == 0)
1441 mc->tab = _free(mc->tab)rfree((mc->tab));
1442 }
1443 /* comes in a single chunk */
1444 free(me);
1445}
1446
1447static int defineMacro(rpmMacroContext mc, const char * macro, int level)
1448{
1449 MacroBuf mb = xcalloc(1, sizeof(*mb))rcalloc((1), (sizeof(*mb)));
1450 int rc;
1451
1452 /* XXX just enough to get by */
1453 mb->mc = mc;
1454 (void) doDefine(mb, macro, strlen(macro), level, 0);
1455 rc = mb->error;
1456 _free(mb)rfree((mb));
1457 return rc;
1458}
1459
1460static int loadMacroFile(rpmMacroContext mc, const char * fn)
1461{
1462 FILE *fd = fopen(fn, "r");
1463 size_t blen = MACROBUFSIZ(8192 * 2);
1464 char *buf = xmalloc(blen)rmalloc((blen));
1465 int rc = -1;
1466
1467 if (fd == NULL((void*)0))
1468 goto exit;
1469
1470 buf[0] = '\0';
1471 while (rdcl(buf, blen, fd) != NULL((void*)0)) {
1472 char c, *n;
1473
1474 n = buf;
1475 SKIPBLANK(n, c)while (((c) = *(n)) && ((c) == ' ' || (c) == '\t')) (
n)++;
;
1476
1477 if (c != '%')
1478 continue;
1479 n++; /* skip % */
1480 rc = defineMacro(mc, n, RMIL_MACROFILES-13);
Value stored to 'rc' is never read
1481 }
1482
1483 rc = fclose(fd);
1484
1485exit:
1486 _free(buf)rfree((buf));
1487 return rc;
1488}
1489
1490static void copyMacros(rpmMacroContext src, rpmMacroContext dst, int level)
1491{
1492 for (int i = 0; i < src->n; i++) {
1493 rpmMacroEntry me = src->tab[i];
1494 assert(me)((void) sizeof ((me) == 0), __extension__ ({ if (me) ; else __assert_fail
("me", "macro.c", 1494, __extension__ __PRETTY_FUNCTION__); }
))
;
1495 pushMacro(dst, me->name, me->opts, me->body, level, me->flags);
1496 }
1497}
1498
1499/* External interfaces */
1500
1501int rpmExpandMacros(rpmMacroContext mc, const char * sbuf, char ** obuf, int flags)
1502{
1503 char *target = NULL((void*)0);
1504 int rc;
1505
1506 mc = rpmmctxAcquire(mc);
1507 rc = doExpandMacros(mc, sbuf, flags, &target);
1508 rpmmctxRelease(mc);
1509
1510 if (rc) {
1511 free(target);
1512 return -1;
1513 } else {
1514 *obuf = target;
1515 return 1;
1516 }
1517}
1518
1519void
1520rpmDumpMacroTable(rpmMacroContext mc, FILE * fp)
1521{
1522 mc = rpmmctxAcquire(mc);
1523 if (fp == NULL((void*)0)) fp = stderrstderr;
1524
1525 fprintf(fp, "========================\n");
1526 for (int i = 0; i < mc->n; i++) {
1527 rpmMacroEntry me = mc->tab[i];
1528 assert(me)((void) sizeof ((me) == 0), __extension__ ({ if (me) ; else __assert_fail
("me", "macro.c", 1528, __extension__ __PRETTY_FUNCTION__); }
))
;
1529 fprintf(fp, "%3d%c %s", me->level,
1530 ((me->flags & ME_USED) ? '=' : ':'), me->name);
1531 if (me->opts && *me->opts)
1532 fprintf(fp, "(%s)", me->opts);
1533 if (me->body && *me->body)
1534 fprintf(fp, "\t%s", me->body);
1535 fprintf(fp, "\n");
1536 }
1537 fprintf(fp, _("======================== active %d empty %d\n")dcgettext ("rpm", "======================== active %d empty %d\n"
, 5)
,
1538 mc->n, 0);
1539 rpmmctxRelease(mc);
1540}
1541
1542int rpmPushMacro(rpmMacroContext mc,
1543 const char * n, const char * o, const char * b, int level)
1544{
1545 mc = rpmmctxAcquire(mc);
1546 pushMacro(mc, n, o, b, level, ME_NONE);
1547 rpmmctxRelease(mc);
1548 return 0;
1549}
1550
1551int rpmPopMacro(rpmMacroContext mc, const char * n)
1552{
1553 mc = rpmmctxAcquire(mc);
1554 popMacro(mc, n);
1555 rpmmctxRelease(mc);
1556 return 0;
1557}
1558
1559int
1560rpmDefineMacro(rpmMacroContext mc, const char * macro, int level)
1561{
1562 int rc;
1563 mc = rpmmctxAcquire(mc);
1564 rc = defineMacro(mc, macro, level);
1565 rpmmctxRelease(mc);
1566 return rc;
1567}
1568
1569void
1570rpmLoadMacros(rpmMacroContext mc, int level)
1571{
1572 rpmMacroContext gmc;
1573 if (mc == NULL((void*)0) || mc == rpmGlobalMacroContext)
1574 return;
1575
1576 gmc = rpmmctxAcquire(NULL((void*)0));
1577 mc = rpmmctxAcquire(mc);
1578
1579 copyMacros(mc, gmc, level);
1580
1581 rpmmctxRelease(mc);
1582 rpmmctxRelease(gmc);
1583}
1584
1585int
1586rpmLoadMacroFile(rpmMacroContext mc, const char * fn)
1587{
1588 int rc;
1589
1590 mc = rpmmctxAcquire(mc);
1591 rc = loadMacroFile(mc, fn);
1592 rpmmctxRelease(mc);
1593
1594 return rc;
1595}
1596
1597void
1598rpmInitMacros(rpmMacroContext mc, const char * macrofiles)
1599{
1600 ARGV_t pattern, globs = NULL((void*)0);
1601 rpmMacroContext climc;
1602
1603 if (macrofiles == NULL((void*)0))
1604 return;
1605
1606 argvSplit(&globs, macrofiles, ":");
1607 mc = rpmmctxAcquire(mc);
1608 for (pattern = globs; *pattern; pattern++) {
1609 ARGV_t path, files = NULL((void*)0);
1610
1611 /* Glob expand the macro file path element, expanding ~ to $HOME. */
1612 if (rpmGlob(*pattern, NULL((void*)0), &files) != 0) {
1613 continue;
1614 }
1615
1616 /* Read macros from each file. */
1617 for (path = files; *path; path++) {
1618 if (rpmFileHasSuffix(*path, ".rpmnew") ||
1619 rpmFileHasSuffix(*path, ".rpmsave") ||
1620 rpmFileHasSuffix(*path, ".rpmorig")) {
1621 continue;
1622 }
1623 (void) loadMacroFile(mc, *path);
1624 }
1625 argvFree(files);
1626 }
1627 argvFree(globs);
1628
1629 /* Reload cmdline macros */
1630 climc = rpmmctxAcquire(rpmCLIMacroContext);
1631 copyMacros(climc, mc, RMIL_CMDLINE-7);
1632 rpmmctxRelease(climc);
1633
1634 rpmmctxRelease(mc);
1635}
1636
1637void
1638rpmFreeMacros(rpmMacroContext mc)
1639{
1640 mc = rpmmctxAcquire(mc);
1641 while (mc->n > 0) {
1642 /* remove from the end to avoid memmove */
1643 rpmMacroEntry me = mc->tab[mc->n - 1];
1644 popMacro(mc, me->name);
1645 }
1646 rpmmctxRelease(mc);
1647}
1648
1649char *
1650rpmExpand(const char *arg, ...)
1651{
1652 size_t blen = 0;
1653 char *buf = NULL((void*)0), *ret = NULL((void*)0);
1654 char *pe;
1655 const char *s;
1656 va_list ap;
1657 rpmMacroContext mc;
1658
1659 if (arg == NULL((void*)0)) {
1660 ret = xstrdup("")rstrdup((""));
1661 goto exit;
1662 }
1663
1664 /* precalculate unexpanded size */
1665 va_start(ap, arg)__builtin_va_start(ap, arg);
1666 for (s = arg; s != NULL((void*)0); s = va_arg(ap, const char *)__builtin_va_arg(ap, const char *))
1667 blen += strlen(s);
1668 va_end(ap)__builtin_va_end(ap);
1669
1670 buf = xmalloc(blen + 1)rmalloc((blen + 1));
1671 buf[0] = '\0';
1672
1673 va_start(ap, arg)__builtin_va_start(ap, arg);
1674 for (pe = buf, s = arg; s != NULL((void*)0); s = va_arg(ap, const char *)__builtin_va_arg(ap, const char *))
1675 pe = stpcpy(pe, s);
1676 va_end(ap)__builtin_va_end(ap);
1677
1678 mc = rpmmctxAcquire(NULL((void*)0));
1679 (void) doExpandMacros(mc, buf, 0, &ret);
1680 rpmmctxRelease(mc);
1681
1682 free(buf);
1683exit:
1684 return ret;
1685}
1686
1687int
1688rpmExpandNumeric(const char *arg)
1689{
1690 char *val;
1691 int rc;
1692
1693 if (arg == NULL((void*)0))
1694 return 0;
1695
1696 val = rpmExpand(arg, NULL((void*)0));
1697 if (!(val && *val != '%'))
1698 rc = 0;
1699 else if (*val == 'Y' || *val == 'y')
1700 rc = 1;
1701 else if (*val == 'N' || *val == 'n')
1702 rc = 0;
1703 else {
1704 char *end;
1705 rc = strtol(val, &end, 0);
1706 if (!(end && *end == '\0'))
1707 rc = 0;
1708 }
1709 free(val);
1710
1711 return rc;
1712}