File: | rpmio/macro.c |
Warning: | line 1456, column 2 Value stored to 'rc' is never read |
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 |
12 | extern char *optarg; |
13 | extern 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 | |
39 | enum 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. */ |
46 | struct 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. */ |
57 | struct 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 | |
67 | static struct rpmMacroContext_s rpmGlobalMacroContext_s; |
68 | rpmMacroContext rpmGlobalMacroContext = &rpmGlobalMacroContext_s; |
69 | |
70 | static struct rpmMacroContext_s rpmCLIMacroContext_s; |
71 | rpmMacroContext 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 | */ |
82 | static pthread_once_t locksInitialized = PTHREAD_ONCE_INIT0; |
83 | |
84 | static 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 | */ |
99 | typedef 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 |
114 | static int max_macro_depth = _MAX_MACRO_DEPTH16; |
115 | |
116 | #define _PRINT_MACRO_TRACE0 0 |
117 | static int print_macro_trace = _PRINT_MACRO_TRACE0; |
118 | |
119 | #define _PRINT_EXPAND_TRACE0 0 |
120 | static int print_expand_trace = _PRINT_EXPAND_TRACE0; |
121 | |
122 | /* forward ref */ |
123 | static int expandMacro(MacroBuf mb, const char *src, size_t slen); |
124 | static void pushMacro(rpmMacroContext mc, |
125 | const char * n, const char * o, const char * b, int level, int flags); |
126 | static void popMacro(rpmMacroContext mc, const char * n); |
127 | static int loadMacroFile(rpmMacroContext mc, const char * fn); |
128 | |
129 | /* =============================================================== */ |
130 | |
131 | static 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 | |
140 | static 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 | */ |
154 | static rpmMacroEntry * |
155 | findEntry(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 | */ |
197 | static char * |
198 | rdcl(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 | */ |
255 | static const char * |
256 | matchchar(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 | */ |
280 | static void |
281 | printMacro(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 | */ |
312 | static void |
313 | printExpansion(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 | */ |
368 | static int |
369 | expandThis(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 | |
386 | static 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 | |
397 | static 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 | */ |
414 | static void |
415 | doShellEscape(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 | |
441 | exit: |
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 | */ |
454 | static const char * |
455 | doDefine(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 | |
572 | exit: |
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 | */ |
587 | static const char * |
588 | doUndefine(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 | |
611 | exit: |
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 | */ |
621 | static void |
622 | freeArgs(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 | */ |
661 | static const char * |
662 | grabArgs(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 | |
778 | exit: |
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 | */ |
790 | static void |
791 | doOutput(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 | |
803 | static 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 | */ |
844 | static void |
845 | doFoo(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 | */ |
956 | static int |
957 | expandMacro(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); |
1297 | exit: |
1298 | _free(source)rfree((source)); |
1299 | return mb->error; |
1300 | } |
1301 | |
1302 | |
1303 | /* =============================================================== */ |
1304 | |
1305 | static 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 | |
1329 | static 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 | |
1396 | static 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 | |
1420 | static 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 | |
1433 | static 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 | |
1461 | exit: |
1462 | _free(buf)rfree((buf)); |
1463 | return rc; |
1464 | } |
1465 | |
1466 | static 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 | |
1477 | int 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 | |
1495 | void |
1496 | rpmDumpMacroTable(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 | |
1518 | int 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 | |
1527 | int rpmPopMacro(rpmMacroContext mc, const char * n) |
1528 | { |
1529 | mc = rpmmctxAcquire(mc); |
1530 | popMacro(mc, n); |
1531 | rpmmctxRelease(mc); |
1532 | return 0; |
1533 | } |
1534 | |
1535 | int |
1536 | rpmDefineMacro(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 | |
1545 | void |
1546 | rpmLoadMacros(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 | |
1561 | int |
1562 | rpmLoadMacroFile(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 | |
1573 | void |
1574 | rpmInitMacros(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 | |
1613 | void |
1614 | rpmFreeMacros(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 | |
1625 | char * |
1626 | rpmExpand(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); |
1659 | exit: |
1660 | return ret; |
1661 | } |
1662 | |
1663 | int |
1664 | rpmExpandNumeric(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 | } |