rpm 5.2.1

- Main Page
- Related Pages
- Modules
- Data Structures
- Files

- File List
- Globals

macro.c
Go to the documentation of this file.
1
5 #include "system.h"
6 #include <stdarg.h>
7
8 #if !defined(isblank)
9 #define isblank(_c) ((char)(_c) == ' ' || (char)(_c) == '\t')
10 #endif
11 #define iseol(_c) ((char)(_c) == '\n' || (char)(_c) == '\r')
12
13 #define STREQ(_t, _f, _fn) ((_fn) == (sizeof(_t)-1) && !strncmp((_t), (_f), (_fn)))
14
15 #ifdef DEBUG_MACROS
16 #undef WITH_LUA /* XXX fixme */
17 #include <sys/types.h>
18 #include <errno.h>
19 #include <fcntl.h>
20 #include <getopt.h>
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <ctype.h>
25 #include <popt.h>
26
27 #define rpmlog fprintf
28 #define RPMLOG_ERR stderr
29 #define RPMLOG_WARNING stderr
30 #undef _
31 #define _(x) x
32
33 #define vmefail(_nb) (exit(1), NULL)
34 #define URL_IS_DASH 1
35 #define URL_IS_PATH 2
36 #define urlPath(_xr, _r) (*(_r) = (_xr), URL_IS_PATH)
37 #define xisalnum(_c) isalnum(_c)
38 #define xisalpha(_c) isalpha(_c)
39 #define xisdigit(_c) isdigit(_c)
40 #define xisspace(_c) isspace(_c)
41
42 typedef FILE * FD_t;
43 #define Fopen(_path, _fmode) fopen(_path, "r");
44 #define Ferror ferror
45 #define Fstrerror(_fd) strerror(errno)
46 #define Fread fread
47 #define Fclose fclose
48
49 #define fdGetFILE(_fd) (_fd)
50
51 /*@unused@*/ static inline /*@null@*/ void *
52 _free(/*@only@*/ /*@null@*/ const void * p)
53 /*@modifies p@*/
54 {
55 if (p != NULL) free((void *)p);
56 return NULL;
57 }
58
59 #else
60
61 /*@observer@*/ /*@checked@*/
62 const char * rpmMacrofiles = MACROFILES;
63
64 #include <rpmio_internal.h>
65 #include <rpmlog.h>
66 #include <mire.h>
67
68 #ifdef WITH_LUA
69 #define _RPMLUA_INTERNAL /* XXX lua->printbuf access */
70 #include <rpmlua.h>
71 #endif
72
73 #include <rpmficl.h>
74 #include <rpmjs.h>
75 #include <rpmperl.h>
76 #include <rpmpython.h>
77 #include <rpmruby.h>
78 #include <rpmtcl.h>
79
80 #endif
81
82 #include <rpmuuid.h>
83
84 #define _MACRO_INTERNAL
85 #include <rpmmacro.h>
86
87 #include "debug.h"
88
89 /*@unchecked@*/
90 #if defined(WITH_FICL) || defined(WITH_JS) || defined(WITH_PERLEMBED) || defined(WITH_PYTHONEMBED) || defined(WITH_RUBYEMBED) || defined(WITH_TCL)
91 static int _globalI = 1;
92 #endif
93
94 #if defined(__LCLINT__)
95 /*@-exportheader@*/
96 extern const unsigned short int **__ctype_b_loc (void) /*@*/;
97 /*@=exportheader@*/
98 #endif
99
100 /*@access FD_t @*/ /* XXX compared with NULL */
101 /*@access miRE @*/ /* XXX cast */
102 /*@access MacroContext @*/
103 /*@access MacroEntry@ */
104 /*@access rpmlua @*/
105 /*@access rpmtcl @*/
106
107 static struct MacroContext_s rpmGlobalMacroContext_s;
108 /*@-compmempass@*/
109 MacroContext rpmGlobalMacroContext = &rpmGlobalMacroContext_s;
110 /*@=compmempass@*/
111
112 static struct MacroContext_s rpmCLIMacroContext_s;
113 /*@-compmempass@*/
114 MacroContext rpmCLIMacroContext = &rpmCLIMacroContext_s;
115 /*@=compmempass@*/
116
120 typedef /*@abstract@*/ struct MacroBuf_s {
121 /*@kept@*/ /*@exposed@*/
122 const char * s;
123 /*@shared@*/
124 char * t;
125 size_t nb;
126 int depth;
127 int macro_trace;
128 int expand_trace;
129 /*@kept@*/ /*@exposed@*/ /*@null@*/
130 void * spec;
131 /*@kept@*/ /*@exposed@*/
132 MacroContext mc;
133 } * MacroBuf;
134
135 #define SAVECHAR(_mb, _c) { *(_mb)->t = (char) (_c), (_mb)->t++, (_mb)->nb--; }
136
137 /*@-exportlocal -exportheadervar@*/
138
139 #define _MAX_MACRO_DEPTH 16
140 /*@unchecked@*/
141 int max_macro_depth = _MAX_MACRO_DEPTH;
142
143 #define _PRINT_MACRO_TRACE 0
144 /*@unchecked@*/
145 int print_macro_trace = _PRINT_MACRO_TRACE;
146
147 #define _PRINT_EXPAND_TRACE 0
148 /*@unchecked@*/
149 int print_expand_trace = _PRINT_EXPAND_TRACE;
150 /*@=exportlocal =exportheadervar@*/
151
152 #define MACRO_CHUNK_SIZE 16
153
154 /* Size of expansion buffers. */
155 /*@unchecked@*/
156 static size_t _macro_BUFSIZ = 16 * 1024;
157
158 /* forward ref */
159 static int expandMacro(MacroBuf mb)
160 /*@globals rpmGlobalMacroContext,
161 print_macro_trace, print_expand_trace, h_errno,
162 fileSystem, internalState @*/
163 /*@modifies mb, rpmGlobalMacroContext,
164 print_macro_trace, print_expand_trace,
165 fileSystem, internalState @*/;
166
167 /* =============================================================== */
168
175 static int
176 compareMacroName(const void * ap, const void * bp)
177 /*@*/
178 {
179 MacroEntry ame = *((MacroEntry *)ap);
180 MacroEntry bme = *((MacroEntry *)bp);
181
182 if (ame == NULL && bme == NULL)
183 return 0;
184 if (ame == NULL)
185 return 1;
186 if (bme == NULL)
187 return -1;
188 return strcmp(ame->name, bme->name);
189 }
190
195 static void
196 expandMacroTable(MacroContext mc)
197 /*@modifies mc @*/
198 {
199 if (mc->macroTable == NULL) {
200 mc->macrosAllocated = MACRO_CHUNK_SIZE;
201 mc->macroTable = (MacroEntry *)
202 xmalloc(sizeof(*(mc->macroTable)) * mc->macrosAllocated);
203 mc->firstFree = 0;
204 } else {
205 mc->macrosAllocated += MACRO_CHUNK_SIZE;
206 mc->macroTable = (MacroEntry *)
207 xrealloc(mc->macroTable, sizeof(*(mc->macroTable)) *
208 mc->macrosAllocated);
209 }
210 memset(&mc->macroTable[mc->firstFree], 0, MACRO_CHUNK_SIZE * sizeof(*(mc->macroTable)));
211 }
212
217 static void
218 sortMacroTable(MacroContext mc)
219 /*@modifies mc @*/
220 {
221 int i;
222
223 if (mc == NULL || mc->macroTable == NULL)
224 return;
225
226 qsort(mc->macroTable, mc->firstFree, sizeof(mc->macroTable[0]),
227 compareMacroName);
228
229 /* Empty pointers are now at end of table. Reset first free index. */
230 for (i = 0; i < mc->firstFree; i++) {
231 if (mc->macroTable[i] != NULL)
232 continue;
233 mc->firstFree = i;
234 break;
235 }
236 }
237
238 #if !defined(DEBUG_MACROS)
239 /*@only@*/
240 static char * dupMacroEntry(MacroEntry me)
241 /*@*/
242 {
243 char * t, * te;
244 size_t nb;
245
246 assert(me != NULL);
247 nb = strlen(me->name) + sizeof("%") - 1;
248 if (me->opts)
249 nb += strlen(me->opts) + sizeof("()") - 1;
250 if (me->body)
251 nb += strlen(me->body) + sizeof("\t") - 1;
252 nb++;
253
254 t = te = xmalloc(nb);
255 *te = '\0';
256 te = stpcpy( stpcpy(te, "%"), me->name);
257 if (me->opts)
258 te = stpcpy( stpcpy( stpcpy(te, "("), me->opts), ")");
259 if (me->body)
260 te = stpcpy( stpcpy(te, "\t"), me->body);
261 *te = '\0';
262
263 return t;
264 }
265 #endif
266
267 void
268 rpmDumpMacroTable(MacroContext mc, FILE * fp)
269 {
270 int nempty = 0;
271 int nactive = 0;
272
273 if (mc == NULL) mc = rpmGlobalMacroContext;
274 if (fp == NULL) fp = stderr;
275
276 fprintf(fp, "========================\n");
277 if (mc->macroTable != NULL) {
278 int i;
279 for (i = 0; i < mc->firstFree; i++) {
280 MacroEntry me;
281 if ((me = mc->macroTable[i]) == NULL) {
282 /* XXX this should never happen */
283 nempty++;
284 continue;
285 }
286 fprintf(fp, "%3d%c %s", me->level,
287 (me->used > 0 ? '=' : ':'), me->name);
288 if (me->opts && *me->opts)
289 fprintf(fp, "(%s)", me->opts);
290 if (me->body && *me->body)
291 fprintf(fp, "\t%s", me->body);
292 fprintf(fp, "\n");
293 nactive++;
294 }
295 }
296 fprintf(fp, _("======================== active %d empty %d\n"),
297 nactive, nempty);
298 }
299
300 #if !defined(DEBUG_MACROS)
301 int
302 rpmGetMacroEntries(MacroContext mc, void * _mire, int used,
303 const char *** avp)
304 {
305 /*@-assignexpose -castexpose @*/
306 miRE mire = (miRE) _mire;
307 /*@=assignexpose =castexpose @*/
308 const char ** av;
309 int ac = 0;
310 int i;
311
312 if (mc == NULL)
313 mc = rpmGlobalMacroContext;
314
315 if (avp == NULL)
316 return mc->firstFree;
317
318 av = xcalloc( (mc->firstFree+1), sizeof(mc->macroTable[0]));
319 if (mc->macroTable != NULL)
320 for (i = 0; i < mc->firstFree; i++) {
321 MacroEntry me;
322 me = mc->macroTable[i];
323 if (used > 0 && me->used < used)
324 continue;
325 if (used == 0 && me->used != 0)
326 continue;
327 #if !defined(DEBUG_MACROS) /* XXX preserve standalone build */
328 if (mire != NULL && mireRegexec(mire, me->name, 0) < 0)
329 continue;
330 #endif
331 av[ac++] = dupMacroEntry(me);
332 }
333 av[ac] = NULL;
334 *avp = av = xrealloc(av, (ac+1) * sizeof(*av));
335
336 return ac;
337 }
338 #endif
339
347 /*@dependent@*/ /*@null@*/
348 static MacroEntry *
349 findEntry(MacroContext mc, const char * name, size_t namelen)
350 /*@*/
351 {
352 MacroEntry key, *ret;
353
354 /*@-globs@*/
355 if (mc == NULL) mc = rpmGlobalMacroContext;
356 /*@=globs@*/
357 if (mc->macroTable == NULL || mc->firstFree == 0)
358 return NULL;
359
360 if (namelen > 0) {
361 char * t = strncpy(alloca(namelen + 1), name, namelen);
362 t[namelen] = '\0';
363 name = t;
364 }
365
366 key = memset(alloca(sizeof(*key)), 0, sizeof(*key));
367 /*@-temptrans -assignexpose@*/
368 key->name = (char *)name;
369 /*@=temptrans =assignexpose@*/
370 ret = (MacroEntry *) bsearch(&key, mc->macroTable, mc->firstFree,
371 sizeof(*(mc->macroTable)), compareMacroName);
372 /* XXX TODO: find 1st empty slot and return that */
373 return ret;
374 }
375
376 /* =============================================================== */
377
385 /*@null@*/
386 static char *
387 rdcl(/*@returned@*/ char * buf, size_t size, FD_t fd)
388 /*@globals fileSystem @*/
389 /*@modifies buf, fileSystem @*/
390 {
391 char *q = buf - 1; /* initialize just before buffer. */
392 size_t nb = 0;
393 size_t nread = 0;
394 FILE * f = fdGetFILE(fd);
395 int pc = 0, bc = 0;
396 char *p = buf;
397
398 if (f != NULL)
399 do {
400 *(++q) = '\0'; /* terminate and move forward. */
401 if (fgets(q, (int)size, f) == NULL) /* read next line. */
402 break;
403 nb = strlen(q);
404 nread += nb; /* trim trailing \r and \n */
405 for (q += nb - 1; nb > 0 && iseol(*q); q--)
406 nb--;
407 for (; p <= q; p++) {
408 switch (*p) {
409 case '\\':
410 switch (*(p+1)) {
411 case '\r': /*@switchbreak@*/ break;
412 case '\n': /*@switchbreak@*/ break;
413 case '\0': /*@switchbreak@*/ break;
414 default: p++; /*@switchbreak@*/ break;
415 }
416 /*@switchbreak@*/ break;
417 case '%':
418 switch (*(p+1)) {
419 case '{': p++, bc++; /*@switchbreak@*/ break;
420 case '(': p++, pc++; /*@switchbreak@*/ break;
421 case '%': p++; /*@switchbreak@*/ break;
422 }
423 /*@switchbreak@*/ break;
424 case '{': if (bc > 0) bc++; /*@switchbreak@*/ break;
425 case '}': if (bc > 0) bc--; /*@switchbreak@*/ break;
426 case '(': if (pc > 0) pc++; /*@switchbreak@*/ break;
427 case ')': if (pc > 0) pc--; /*@switchbreak@*/ break;
428 }
429 }
430 if (nb == 0 || (*q != '\\' && !bc && !pc) || *(q+1) == '\0') {
431 *(++q) = '\0'; /* trim trailing \r, \n */
432 break;
433 }
434 q++; p++; nb++; /* copy newline too */
435 size -= nb;
436 if (*q == '\r') /* XXX avoid \r madness */
437 *q = '\n';
438 } while (size > 0);
439 return (nread > 0 ? buf : NULL);
440 }
441
449 /*@null@*/
450 static const char *
451 matchchar(const char * p, char pl, char pr)
452 /*@*/
453 {
454 int lvl = 0;
455 char c;
456
457 while ((c = *p++) != '\0') {
458 if (c == '\\') { /* Ignore escaped chars */
459 p++;
460 continue;
461 }
462 if (c == pr) {
463 if (--lvl <= 0) return --p;
464 } else if (c == pl)
465 lvl++;
466 }
467 return (const char *)NULL;
468 }
469
476 static void
477 printMacro(MacroBuf mb, const char * s, const char * se)
478 /*@globals fileSystem @*/
479 /*@modifies fileSystem @*/
480 {
481 const char *senl;
482 const char *ellipsis;
483 int choplen;
484
485 if (s >= se) { /* XXX just in case */
486 fprintf(stderr, _("%3d>%*s(empty)"), mb->depth,
487 (2 * mb->depth + 1), "");
488 return;
489 }
490
491 if (s[-1] == '{')
492 s--;
493
494 /* Print only to first end-of-line (or end-of-string). */
495 for (senl = se; *senl && !iseol(*senl); senl++)
496 {};
497
498 /* Limit trailing non-trace output */
499 choplen = 61 - (2 * mb->depth);
500 if ((senl - s) > choplen) {
501 senl = s + choplen;
502 ellipsis = "...";
503 } else
504 ellipsis = "";
505
506 /* Substitute caret at end-of-macro position */
507 fprintf(stderr, "%3d>%*s%%%.*s^", mb->depth,
508 (2 * mb->depth + 1), "", (int)(se - s), s);
509 if (se[1] != '\0' && (senl - (se+1)) > 0)
510 fprintf(stderr, "%-.*s%s", (int)(senl - (se+1)), se+1, ellipsis);
511 fprintf(stderr, "\n");
512 }
513
520 static void
521 printExpansion(MacroBuf mb, const char * t, const char * te)
522 /*@globals fileSystem @*/
523 /*@modifies fileSystem @*/
524 {
525 const char *ellipsis;
526 int choplen;
527
528 if (!(te > t)) {
529 fprintf(stderr, _("%3d<%*s(empty)\n"), mb->depth, (2 * mb->depth + 1), "");
530 return;
531 }
532
533 /* Shorten output which contains newlines */
534 while (te > t && iseol(te[-1]))
535 te--;
536 ellipsis = "";
537 if (mb->depth > 0) {
538 const char *tenl;
539
540 /* Skip to last line of expansion */
541 while ((tenl = strchr(t, '\n')) && tenl < te)
542 t = ++tenl;
543
544 /* Limit expand output */
545 choplen = 61 - (2 * mb->depth);
546 if ((te - t) > choplen) {
547 te = t + choplen;
548 ellipsis = "...";
549 }
550 }
551
552 fprintf(stderr, "%3d<%*s", mb->depth, (2 * mb->depth + 1), "");
553 if (te > t)
554 fprintf(stderr, "%.*s%s", (int)(te - t), t, ellipsis);
555 fprintf(stderr, "\n");
556 }
557
558 #define SKIPBLANK(_s, _c) \
559 /*@-globs@*/ /* FIX: __ctype_b */ \
560 while (((_c) = (int) *(_s)) && isblank(_c)) \
561 (_s)++; \
562 /*@=globs@*/
563
564 #define SKIPNONBLANK(_s, _c) \
565 /*@-globs@*/ /* FIX: __ctype_b */ \
566 while (((_c) = (int) *(_s)) && !(isblank(_c) || iseol(_c))) \
567 (_s)++; \
568 /*@=globs@*/
569
570 #define COPYNAME(_ne, _s, _c) \
571 { SKIPBLANK(_s,_c); \
572 while(((_c) = (int) *(_s)) && (xisalnum(_c) || (_c) == (int) '_')) \
573 *(_ne)++ = *(_s)++; \
574 *(_ne) = '\0'; \
575 }
576
577 #define COPYOPTS(_oe, _s, _c) \
578 { while(((_c) = (int) *(_s)) && (_c) != (int) ')') \
579 *(_oe)++ = *(_s)++; \
580 *(_oe) = '\0'; \
581 }
582
590 static int
591 expandT(MacroBuf mb, const char * f, size_t flen)
592 /*@globals rpmGlobalMacroContext, h_errno, fileSystem, internalState @*/
593 /*@modifies mb, rpmGlobalMacroContext, fileSystem, internalState @*/
594 {
595 char *sbuf;
596 const char *s = mb->s;
597 int rc;
598
599 sbuf = alloca(flen + 1);
600 memset(sbuf, 0, (flen + 1));
601
602 strncpy(sbuf, f, flen);
603 sbuf[flen] = '\0';
604 mb->s = sbuf;
605 rc = expandMacro(mb);
606 mb->s = s;
607 return rc;
608 }
609
610 #if 0
611
618 static int
619 expandS(MacroBuf mb, char * tbuf, size_t tbuflen)
620 /*@globals rpmGlobalMacroContext, fileSystem, internalState @*/
621 /*@modifies mb, *tbuf, rpmGlobalMacroContext, fileSystem, internalState @*/
622 {
623 const char *t = mb->t;
624 size_t nb = mb->nb;
625 int rc;
626
627 mb->t = tbuf;
628 mb->nb = tbuflen;
629 rc = expandMacro(mb);
630 mb->t = t;
631 mb->nb = nb;
632 return rc;
633 }
634 #endif
635
643 static int
644 expandU(MacroBuf mb, char * u, size_t ulen)
645 /*@globals rpmGlobalMacroContext, h_errno, fileSystem, internalState @*/
646 /*@modifies mb, *u, rpmGlobalMacroContext, fileSystem, internalState @*/
647 {
648 const char *s = mb->s;
649 char *t = mb->t;
650 size_t nb = mb->nb;
651 char *tbuf;
652 int rc;
653
654 tbuf = alloca(ulen + 1);
655 memset(tbuf, 0, (ulen + 1));
656
657 mb->s = u;
658 mb->t = tbuf;
659 mb->nb = ulen;
660 rc = expandMacro(mb);
661
662 tbuf[ulen] = '\0'; /* XXX just in case */
663 if (ulen > mb->nb)
664 strncpy(u, tbuf, (ulen - mb->nb + 1));
665
666 mb->s = s;
667 mb->t = t;
668 mb->nb = nb;
669
670 return rc;
671 }
672
680 static int
681 doShellEscape(MacroBuf mb, const char * cmd, size_t clen)
682 /*@globals rpmGlobalMacroContext, h_errno, fileSystem, internalState @*/
683 /*@modifies mb, rpmGlobalMacroContext, fileSystem, internalState @*/
684 {
685 size_t bufn = _macro_BUFSIZ + clen;
686 char * buf = alloca(bufn);
687 FILE *shf;
688 int rc;
689 int c;
690
691 strncpy(buf, cmd, clen);
692 buf[clen] = '\0';
693 rc = expandU(mb, buf, bufn);
694 if (rc)
695 return rc;
696
697 if ((shf = popen(buf, "r")) == NULL)
698 return 1;
699 while(mb->nb > 0 && (c = fgetc(shf)) != EOF)
700 SAVECHAR(mb, c);
701 (void) pclose(shf);
702
703 /* XXX delete trailing \r \n */
704 while (iseol(mb->t[-1])) {
705 *(mb->t--) = '\0';
706 mb->nb++;
707 }
708 return 0;
709 }
710
719 /*@dependent@*/ static const char *
720 doDefine(MacroBuf mb, /*@returned@*/ const char * se, int level, int expandbody)
721 /*@globals rpmGlobalMacroContext, h_errno, internalState @*/
722 /*@modifies mb, rpmGlobalMacroContext, internalState @*/
723 {
724 const char *s = se;
725 size_t bufn = _macro_BUFSIZ;
726 char *buf = alloca(bufn);
727 char *n = buf, *ne;
728 char *o = NULL, *oe;
729 char *b, *be;
730 int c;
731 int oc = (int) ')';
732
733 SKIPBLANK(s, c);
734 if (c == (int) '.') /* XXX readonly macros */
735 /*@i@*/ *n++ = c = *s++;
736 if (c == (int) '.') /* XXX readonly macros */
737 /*@i@*/ *n++ = c = *s++;
738 ne = n;
739
740 /* Copy name */
741 COPYNAME(ne, s, c);
742
743 /* Copy opts (if present) */
744 oe = ne + 1;
745 if (*s == '(') {
746 s++; /* skip ( */
747 o = oe;
748 COPYOPTS(oe, s, oc);
749 s++; /* skip ) */
750 }
751
752 /* Copy body, skipping over escaped newlines */
753 b = be = oe + 1;
754 SKIPBLANK(s, c);
755 if (c == (int) '{') { /* XXX permit silent {...} grouping */
756 if ((se = matchchar(s, (char) c, '}')) == NULL) {
757 rpmlog(RPMLOG_ERR,
758 _("Macro %%%s has unterminated body\n"), n);
759 se = s; /* XXX W2DO? */
760 return se;
761 }
762 s++; /* XXX skip { */
763 strncpy(b, s, (se - s));
764 b[se - s] = '\0';
765 be += strlen(b);
766 se++; /* XXX skip } */
767 s = se; /* move scan forward */
768 } else { /* otherwise free-field */
769 int bc = 0, pc = 0;
770 while (*s && (bc || pc || !iseol(*s))) {
771 switch (*s) {
772 case '\\':
773 switch (*(s+1)) {
774 case '\0': /*@switchbreak@*/ break;
775 default: s++; /*@switchbreak@*/ break;
776 }
777 /*@switchbreak@*/ break;
778 case '%':
779 switch (*(s+1)) {
780 case '{': *be++ = *s++; bc++; /*@switchbreak@*/ break;
781 case '(': *be++ = *s++; pc++; /*@switchbreak@*/ break;
782 case '%': *be++ = *s++; /*@switchbreak@*/ break;
783 }
784 /*@switchbreak@*/ break;
785 case '{': if (bc > 0) bc++; /*@switchbreak@*/ break;
786 case '}': if (bc > 0) bc--; /*@switchbreak@*/ break;
787 case '(': if (pc > 0) pc++; /*@switchbreak@*/ break;
788 case ')': if (pc > 0) pc--; /*@switchbreak@*/ break;
789 }
790 *be++ = *s++;
791 }
792 *be = '\0';
793
794 if (bc || pc) {
795 rpmlog(RPMLOG_ERR,
796 _("Macro %%%s has unterminated body\n"), n);
797 se = s; /* XXX W2DO? */
798 return se;
799 }
800
801 /* Trim trailing blanks/newlines */
802 /*@-globs@*/
803 while (--be >= b && (c = (int) *be) && (isblank(c) || iseol(c)))
804 {};
805 /*@=globs@*/
806 *(++be) = '\0'; /* one too far */
807 }
808
809 /* Move scan over body */
810 while (iseol(*s))
811 s++;
812 se = s;
813
814 /* Names must start with alphabetic or _ and be at least 3 chars */
815 if (!((c = (int) *n) && (xisalpha(c) || c == (int) '_') && (ne - n) > 2)) {
816 rpmlog(RPMLOG_ERR,
817 _("Macro %%%s has illegal name (%%define)\n"), n);
818 return se;
819 }
820
821 /* Options must be terminated with ')' */
822 if (o && oc != (int) ')') {
823 rpmlog(RPMLOG_ERR, _("Macro %%%s has unterminated opts\n"), n);
824 return se;
825 }
826
827 if ((be - b) < 1) {
828 rpmlog(RPMLOG_ERR, _("Macro %%%s has empty body\n"), n);
829 return se;
830 }
831
832 /*@-modfilesys@*/
833 if (expandbody && expandU(mb, b, (&buf[bufn] - b))) {
834 rpmlog(RPMLOG_ERR, _("Macro %%%s failed to expand\n"), n);
835 return se;
836 }
837 /*@=modfilesys@*/
838
839 if (n != buf) /* XXX readonly macros */
840 n--;
841 if (n != buf) /* XXX readonly macros */
842 n--;
843 addMacro(mb->mc, n, o, b, (level - 1));
844
845 return se;
846 }
847
854 /*@dependent@*/ static const char *
855 doUndefine(MacroContext mc, /*@returned@*/ const char * se)
856 /*@globals rpmGlobalMacroContext @*/
857 /*@modifies mc, rpmGlobalMacroContext @*/
858 {
859 const char *s = se;
860 char *buf = alloca(_macro_BUFSIZ);
861 char *n = buf, *ne = n;
862 int c;
863
864 COPYNAME(ne, s, c);
865
866 /* Move scan over body */
867 while (iseol(*s))
868 s++;
869 se = s;
870
871 /* Names must start with alphabetic or _ and be at least 3 chars */
872 if (!((c = (int) *n) && (xisalpha(c) || c == (int) '_') && (ne - n) > 2)) {
873 rpmlog(RPMLOG_ERR,
874 _("Macro %%%s has illegal name (%%undefine)\n"), n);
875 return se;
876 }
877
878 delMacro(mc, n);
879
880 return se;
881 }
882
883 #ifdef DYING
884 static void
885 dumpME(const char * msg, MacroEntry me)
886 /*@globals fileSystem @*/
887 /*@modifies fileSystem @*/
888 {
889 if (msg)
890 fprintf(stderr, "%s", msg);
891 fprintf(stderr, "\tme %p", me);
892 if (me)
893 fprintf(stderr,"\tname %p(%s) prev %p",
894 me->name, me->name, me->prev);
895 fprintf(stderr, "\n");
896 }
897 #endif
898
907 static void
908 pushMacro(/*@out@*/ MacroEntry * mep, const char * n, /*@null@*/ const char * o,
909 /*@null@*/ const char * b, int level)
910 /*@modifies *mep @*/
911 {
912 MacroEntry prev = (mep && *mep ? *mep : NULL);
913 MacroEntry me = (MacroEntry) xmalloc(sizeof(*me));
914 const char *name = n;
915
916 if (*name == '.') /* XXX readonly macros */
917 name++;
918 if (*name == '.') /* XXX readonly macros */
919 name++;
920
921 /*@-assignexpose@*/
922 me->prev = prev;
923 /*@=assignexpose@*/
924 me->name = (prev ? prev->name : xstrdup(name));
925 me->opts = (o ? xstrdup(o) : NULL);
926 me->body = xstrdup(b ? b : "");
927 me->used = 0;
928 me->level = level;
929 me->flags = (name != n);
930 if (mep)
931 *mep = me;
932 else
933 me = _free(me);
934 }
935
940 static void
941 popMacro(MacroEntry * mep)
942 /*@modifies *mep @*/
943 {
944 MacroEntry me = (*mep ? *mep : NULL);
945
946 if (me) {
947 /* XXX cast to workaround const */
948 /*@-onlytrans@*/
949 if ((*mep = me->prev) == NULL)
950 me->name = _free(me->name);
951 me->opts = _free(me->opts);
952 me->body = _free(me->body);
953 me = _free(me);
954 /*@=onlytrans@*/
955 }
956 }
957
962 static void
963 freeArgs(MacroBuf mb)
964 /*@modifies mb @*/
965 {
966 MacroContext mc = mb->mc;
967 int ndeleted = 0;
968 int i;
969
970 if (mc == NULL || mc->macroTable == NULL)
971 return;
972
973 /* Delete dynamic macro definitions */
974 for (i = 0; i < mc->firstFree; i++) {
975 MacroEntry *mep, me;
976 int skiptest = 0;
977 mep = &mc->macroTable[i];
978 me = *mep;
979
980 if (me == NULL) /* XXX this should never happen */
981 continue;
982 if (me->level < mb->depth)
983 continue;
984 if (strlen(me->name) == 1 && strchr("#*0", *me->name)) {
985 if (*me->name == '*' && me->used > 0)
986 skiptest = 1; /* XXX skip test for %# %* %0 */
987 } else if (!skiptest && me->used <= 0) {
988 #if NOTYET
989 rpmlog(RPMLOG_ERR,
990 _("Macro %%%s (%s) was not used below level %d\n"),
991 me->name, me->body, me->level);
992 #endif
993 }
994 popMacro(mep);
995 if (!(mep && *mep))
996 ndeleted++;
997 }
998
999 /* If any deleted macros, sort macro table */
1000 if (ndeleted)
1001 sortMacroTable(mc);
1002 }
1003
1013 /*@dependent@*/ static const char *
1014 grabArgs(MacroBuf mb, const MacroEntry me, /*@returned@*/ const char * se,
1015 const char * lastc)
1016 /*@globals rpmGlobalMacroContext, fileSystem, internalState @*/
1017 /*@modifies mb, rpmGlobalMacroContext, fileSystem, internalState @*/
1018 {
1019 poptContext optCon;
1020 struct poptOption *optTbl;
1021 size_t bufn = _macro_BUFSIZ;
1022 char *buf = alloca(bufn);
1023 char *b, *be;
1024 char aname[16];
1025 const char *opts;
1026 int argc = 0;
1027 const char **argv;
1028 int c;
1029 unsigned int popt_flags;
1030
1031 /* Copy macro name as argv[0], save beginning of args. */
1032 buf[0] = '\0';
1033 b = be = stpcpy(buf, me->name);
1034
1035 addMacro(mb->mc, "0", NULL, buf, mb->depth);
1036
1037 argc = 1; /* XXX count argv[0] */
1038
1039 /* Copy args into buf until lastc */
1040 *be++ = ' ';
1041 while ((c = (int) *se++) != (int) '\0' && (se-1) != lastc) {
1042 /*@-globs@*/
1043 if (!isblank(c)) {
1044 *be++ = (char) c;
1045 continue;
1046 }
1047 /*@=globs@*/
1048 /* c is blank */
1049 if (be[-1] == ' ')
1050 continue;
1051 /* a word has ended */
1052 *be++ = ' ';
1053 argc++;
1054 }
1055 if (c == (int) '\0') se--; /* one too far */
1056 if (be[-1] != ' ')
1057 argc++, be++; /* last word has not trailing ' ' */
1058 be[-1] = '\0';
1059 if (*b == ' ') b++; /* skip the leading ' ' */
1060
1061 /*
1062 * The macro %* analoguous to the shell's $* means "Pass all non-macro
1063 * parameters." Consequently, there needs to be a macro that means "Pass all
1064 * (including macro parameters) options". This is useful for verifying
1065 * parameters during expansion and yet transparently passing all parameters
1066 * through for higher level processing (e.g. %description and/or %setup).
1067 * This is the (potential) justification for %{**} ...
1068 */
1069 /* Add unexpanded args as macro */
1070 addMacro(mb->mc, "**", NULL, b, mb->depth);
1071
1072 #ifdef NOTYET
1073 /* XXX if macros can be passed as args ... */
1074 expandU(mb, buf, bufn);
1075 #endif
1076
1077 /* Build argv array */
1078 argv = (const char **) alloca((argc + 1) * sizeof(*argv));
1079 be[-1] = ' '; /* assert((be - 1) == (b + strlen(b) == buf + strlen(buf))) */
1080 be[0] = '\0';
1081
1082 b = buf;
1083 for (c = 0; c < argc; c++) {
1084 argv[c] = b;
1085 b = strchr(b, ' ');
1086 *b++ = '\0';
1087 }
1088 /* assert(b == be); */
1089 argv[argc] = NULL;
1090
1091 /* '+' as the first character means that options are recognized
1092 * only before positional arguments, as POSIX requires.
1093 */
1094 popt_flags = POPT_CONTEXT_NO_EXEC;
1095 #if defined(RPM_VENDOR_OPENPKG) /* always-strict-posix-option-parsing */
1096 popt_flags |= POPT_CONTEXT_POSIXMEHARDER;
1097 #endif
1098 if (me->opts[0] == '+') popt_flags |= POPT_CONTEXT_POSIXMEHARDER;
1099
1100 /* Count the number of short options. */
1101 opts = me->opts;
1102 if (*opts == '+') opts++;
1103 for (c = 0; *opts != '\0'; opts++)
1104 if (*opts != ':') c++;
1105
1106 /* Set up popt option table. */
1107 optTbl = xcalloc(sizeof(*optTbl), (c + 1));
1108 opts = me->opts;
1109 if (*opts == '+') opts++;
1110 for (c = 0; *opts != '\0'; opts++) {
1111 if (*opts == ':') continue;
1112 optTbl[c].shortName = opts[0];
1113 optTbl[c].val = (int) opts[0];
1114 if (opts[1] == ':')
1115 optTbl[c].argInfo = POPT_ARG_STRING;
1116 c++;
1117 }
1118
1119 /* Parse the options, defining option macros. */
1120 /*@-nullstate@*/
1121 optCon = poptGetContext(argv[0], argc, argv, optTbl, popt_flags);
1122 /*@=nullstate@*/
1123 while ((c = poptGetNextOpt(optCon)) > 0) {
1124 const char * optArg = poptGetOptArg(optCon);
1125 *be++ = '-';
1126 *be++ = (char) c;
1127 if (optArg != NULL) {
1128 *be++ = ' ';
1129 be = stpcpy(be, optArg);
1130 }
1131 *be++ = '\0';
1132 aname[0] = '-'; aname[1] = (char)c; aname[2] = '\0';
1133 addMacro(mb->mc, aname, NULL, b, mb->depth);
1134 if (optArg != NULL) {
1135 aname[0] = '-'; aname[1] = (char)c; aname[2] = '*'; aname[3] = '\0';
1136 addMacro(mb->mc, aname, NULL, optArg, mb->depth);
1137 }
1138 be = b; /* reuse the space */
1139 /*@-dependenttrans -modobserver -observertrans @*/
1140 optArg = _free(optArg);
1141 /*@=dependenttrans =modobserver =observertrans @*/
1142 }
1143 if (c < -1) {
1144 rpmlog(RPMLOG_ERR, _("Unknown option in macro %s(%s): %s: %s\n"),
1145 me->name, me->opts,
1146 poptBadOption(optCon, POPT_BADOPTION_NOALIAS), poptStrerror(c));
1147 goto exit;
1148 }
1149
1150 argv = poptGetArgs(optCon);
1151 argc = 0;
1152 if (argv != NULL)
1153 for (c = 0; argv[c] != NULL; c++)
1154 argc++;
1155
1156 /* Add arg count as macro. */
1157 sprintf(aname, "%d", argc);
1158 addMacro(mb->mc, "#", NULL, aname, mb->depth);
1159
1160 /* Add macro for each arg. Concatenate args for %*. */
1161 if (be) {
1162 *be = '\0';
1163 if (argv != NULL)
1164 for (c = 0; c < argc; c++) {
1165 sprintf(aname, "%d", (c + 1));
1166 addMacro(mb->mc, aname, NULL, argv[c], mb->depth);
1167 if (be != b) *be++ = ' '; /* Add space between args */
1168 be = stpcpy(be, argv[c]);
1169 }
1170 }
1171
1172 /* Add unexpanded args as macro. */
1173 addMacro(mb->mc, "*", NULL, b, mb->depth);
1174
1175 exit:
1176 optCon = poptFreeContext(optCon);
1177 optTbl = _free(optTbl);
1178 return se;
1179 }
1180
1188 static void
1189 doOutput(MacroBuf mb, int waserror, const char * msg, size_t msglen)
1190 /*@globals rpmGlobalMacroContext, h_errno, fileSystem, internalState @*/
1191 /*@modifies mb, rpmGlobalMacroContext, fileSystem, internalState @*/
1192 {
1193 size_t bufn = _macro_BUFSIZ + msglen;
1194 char *buf = alloca(bufn);
1195
1196 strncpy(buf, msg, msglen);
1197 buf[msglen] = '\0';
1198 (void) expandU(mb, buf, bufn);
1199 if (waserror)
1200 rpmlog(RPMLOG_ERR, "%s\n", buf);
1201 else
1202 fprintf(stderr, "%s", buf);
1203 }
1204
1214 static void
1215 doFoo(MacroBuf mb, int negate, const char * f, size_t fn,
1216 /*@null@*/ const char * g, size_t gn)
1217 /*@globals rpmGlobalMacroContext, h_errno, fileSystem, internalState @*/
1218 /*@modifies mb, rpmGlobalMacroContext, fileSystem, internalState @*/
1219 {
1220 size_t bufn = _macro_BUFSIZ + fn + gn;
1221 char * buf = alloca(bufn);
1222 char *b = NULL, *be;
1223 int c;
1224
1225 buf[0] = '\0';
1226 if (g != NULL) {
1227 strncpy(buf, g, gn);
1228 buf[gn] = '\0';
1229 (void) expandU(mb, buf, bufn);
1230 }
1231 if (fn > 5 && STREQ("patch", f, 5) && xisdigit((int)f[5])) {
1232 /* Skip leading zeros */
1233 for (c = 5; c < (int)(fn-1) && f[c] == '0' && xisdigit((int)f[c+1]);)
1234 c++;
1235 b = buf;
1236 be = stpncpy( stpcpy(b, "%patch -P "), f+c, fn-c);
1237 *be = '\0';
1238 } else
1239 if (STREQ("basename", f, fn)) {
1240 if ((b = strrchr(buf, '/')) == NULL)
1241 b = buf;
1242 else
1243 b++;
1244 } else if (STREQ("dirname", f, fn)) {
1245 if ((b = strrchr(buf, '/')) != NULL)
1246 *b = '\0';
1247 b = buf;
1248 #if !defined(__LCLINT__) /* XXX LCL: realpath(3) annotations are buggy. */
1249 } else if (STREQ("realpath", f, fn)) {
1250 char rp[PATH_MAX];
1251 char *cp;
1252 size_t l;
1253 if ((cp = realpath(buf, rp)) != NULL) {
1254 l = strlen(cp);
1255 if ((size_t)(l+1) <= bufn) {
1256 memcpy(buf, cp, l+1);
1257 b = buf;
1258 }
1259 }
1260 #endif
1261 } else if (STREQ("getenv", f, fn)) {
1262 char *cp;
1263 if ((cp = getenv(buf)) != NULL)
1264 b = cp;
1265 } else if (STREQ("shrink", f, fn)) {
1266 /*
1267 * shrink body by removing all leading and trailing whitespaces and
1268 * reducing intermediate whitespaces to a single space character.
1269 */
1270 int i, j, k, was_space = 0;
1271 for (i = 0, j = 0, k = (int)strlen(buf); i < k; ) {
1272 if (xisspace((int)(buf[i]))) {
1273 was_space = 1;
1274 i++;
1275 continue;
1276 }
1277 else if (was_space) {
1278 was_space = 0;
1279 if (j > 0) /* remove leading blanks at all */
1280 buf[j++] = ' ';
1281 /* fallthrough */
1282 }
1283 buf[j++] = buf[i++];
1284 }
1285 buf[j] = '\0';
1286 b = buf;
1287 } else if (STREQ("suffix", f, fn)) {
1288 if ((b = strrchr(buf, '.')) != NULL)
1289 b++;
1290 } else if (STREQ("expand", f, fn)) {
1291 b = buf;
1292 } else if (STREQ("verbose", f, fn)) {
1293 #if defined(RPMLOG_MASK)
1294 if (negate)
1295 b = ((rpmlogSetMask(0) >= RPMLOG_MASK( RPMLOG_INFO )) ? NULL : buf);
1296 else
1297 b = ((rpmlogSetMask(0) >= RPMLOG_MASK( RPMLOG_INFO )) ? buf : NULL);
1298 #else
1299 /* XXX assume always verbose when running standalone */
1300 b = (negate) ? NULL : buf;
1301 #endif
1302 } else if (STREQ("url2path", f, fn) || STREQ("u2p", f, fn)) {
1303 int ut = urlPath(buf, (const char **)&b);
1304 ut = ut; /* XXX quiet gcc */
1305 if (*b == '\0') b = "/";
1306 } else if (STREQ("uncompress", f, fn)) {
1307 rpmCompressedMagic compressed = COMPRESSED_OTHER;
1308 /*@-globs@*/
1309 for (b = buf; (c = (int)*b) && isblank(c);)
1310 b++;
1311 /* XXX FIXME: file paths with embedded white space needs rework. */
1312 for (be = b; (c = (int)*be) && !isblank(c);)
1313 be++;
1314 /*@=globs@*/
1315 *be++ = '\0';
1316 (void) isCompressed(b, &compressed);
1317 switch(compressed) {
1318 default:
1319 case 0: /* COMPRESSED_NOT */
1320 sprintf(be, "%%__cat %s", b);
1321 break;
1322 case 1: /* COMPRESSED_OTHER */
1323 sprintf(be, "%%__gzip -dc '%s'", b);
1324 break;
1325 case 2: /* COMPRESSED_BZIP2 */
1326 sprintf(be, "%%__bzip2 -dc '%s'", b);
1327 break;
1328 case 3: /* COMPRESSED_ZIP */
1329 sprintf(be, "%%__unzip -qq '%s'", b);
1330 break;
1331 case 4: /* COMPRESSED_LZOP */
1332 sprintf(be, "%%__lzop -dc '%s'", b);
1333 break;
1334 case 5: /* COMPRESSED_LZMA */
1335 sprintf(be, "%%__lzma -dc '%s'", b);
1336 break;
1337 case 6: /* COMPRESSED_XZ */
1338 sprintf(be, "%%__xz -dc '%s'", b);
1339 break;
1340 }
1341 b = be;
1342 } else if (STREQ("mkstemp", f, fn)) {
1343 /*@-globs@*/
1344 for (b = buf; (c = (int)*b) && isblank(c);)
1345 b++;
1346 /* XXX FIXME: file paths with embedded white space needs rework. */
1347 for (be = b; (c = (int)*be) && !isblank(c);)
1348 be++;
1349 /*@=globs@*/
1350 #if defined(HAVE_MKSTEMP)
1351 (void) close(mkstemp(b));
1352 #else
1353 (void) mktemp(b);
1354 #endif
1355 } else if (STREQ("mkdtemp", f, fn)) {
1356 /*@-globs@*/
1357 for (b = buf; (c = (int)*b) && isblank(c);)
1358 b++;
1359 /* XXX FIXME: file paths with embedded white space needs rework. */
1360 for (be = b; (c = (int)*be) && !isblank(c);)
1361 be++;
1362 /*@=globs@*/
1363 #if defined(HAVE_MKDTEMP) && !defined(__LCLINT__)
1364 if (mkdtemp(b) == NULL)
1365 perror("mkdtemp");
1366 #else
1367 if ((b = tmpnam(b)) != NULL)
1368 (void) mkdir(b, 0700); /* XXX S_IWRSXU is not included. */
1369 #endif
1370 } else if (STREQ("uuid", f, fn)) {
1371 int uuid_version;
1372 const char *uuid_ns;
1373 const char *uuid_data;
1374 char *cp;
1375 size_t n;
1376
1377 uuid_version = 1;
1378 uuid_ns = NULL;
1379 uuid_data = NULL;
1380 cp = buf;
1381 if ((n = strspn(cp, " \t\n")) > 0)
1382 cp += n;
1383 if ((n = strcspn(cp, " \t\n")) > 0) {
1384 uuid_version = (int)strtol(cp, (char **)NULL, 10);
1385 cp += n;
1386 if ((n = strspn(cp, " \t\n")) > 0)
1387 cp += n;
1388 if ((n = strcspn(cp, " \t\n")) > 0) {
1389 uuid_ns = cp;
1390 cp += n;
1391 *cp++ = '\0';
1392 if ((n = strspn(cp, " \t\n")) > 0)
1393 cp += n;
1394 if ((n = strcspn(cp, " \t\n")) > 0) {
1395 uuid_data = cp;
1396 cp += n;
1397 *cp++ = '\0';
1398 }
1399 }
1400 }
1401 /*@-nullpass@*/ /* FIX: uuid_ns may be NULL */
1402 if (rpmuuidMake(uuid_version, uuid_ns, uuid_data, buf, NULL))
1403 rpmlog(RPMLOG_ERR, "failed to create UUID\n");
1404 else
1405 b = buf;
1406 /*@=nullpass@*/
1407 } else if (STREQ("S", f, fn)) {
1408 for (b = buf; (c = (int)*b) && xisdigit(c);)
1409 b++;
1410 if (!c) { /* digit index */
1411 b++;
1412 sprintf(b, "%%SOURCE%s", buf);
1413 } else
1414 b = buf;
1415 } else if (STREQ("P", f, fn)) {
1416 for (b = buf; (c = (int) *b) && xisdigit(c);)
1417 b++;
1418 if (!c) { /* digit index */
1419 b++;
1420 sprintf(b, "%%PATCH%s", buf);
1421 } else
1422 b = buf;
1423 } else if (STREQ("F", f, fn)) {
1424 b = buf + strlen(buf) + 1;
1425 sprintf(b, "file%s.file", buf);
1426 }
1427
1428 if (b) {
1429 (void) expandT(mb, b, strlen(b));
1430 }
1431 }
1432
1433 static int expandFIFO(MacroBuf mb, MacroEntry me, const char *g, size_t gn)
1434 /*@globals rpmGlobalMacroContext, h_errno, fileSystem, internalState @*/
1435 /*@modifies mb, rpmGlobalMacroContext, fileSystem, internalState @*/
1436 {
1437 int rc = 0;
1438
1439 if (me) {
1440 if (me->prev) {
1441 rc = expandFIFO(mb, me->prev, g, gn);
1442 rc = expandT(mb, g, gn);
1443 }
1444 rc = expandT(mb, me->body, strlen(me->body));
1445 }
1446 return rc;
1447 }
1448
1449 #if !defined(DEBUG_MACROS)
1450 /* =============================================================== */
1451 /* XXX dupe'd to avoid change in linkage conventions. */
1452
1453 #define POPT_ERROR_NOARG -10
1454 #define POPT_ERROR_BADQUOTE -15
1455 #define POPT_ERROR_MALLOC -21
1457 #define POPT_ARGV_ARRAY_GROW_DELTA 5
1458
1459 static int XpoptDupArgv(int argc, const char **argv,
1460 int * argcPtr, const char *** argvPtr)
1461 /*@modifies *argcPtr, *argvPtr @*/
1462 {
1463 size_t nb = (argc + 1) * sizeof(*argv);
1464 const char ** argv2;
1465 char * dst;
1466 int i;
1467
1468 if (argc <= 0 || argv == NULL) /* XXX can't happen */
1469 return POPT_ERROR_NOARG;
1470 for (i = 0; i < argc; i++) {
1471 if (argv[i] == NULL)
1472 return POPT_ERROR_NOARG;
1473 nb += strlen(argv[i]) + 1;
1474 }
1475
1476 dst = xmalloc(nb);
1477 if (dst == NULL) /* XXX can't happen */
1478 return POPT_ERROR_MALLOC;
1479 argv2 = (void *) dst;
1480 dst += (argc + 1) * sizeof(*argv);
1481
1482 for (i = 0; i < argc; i++) {
1483 argv2[i] = dst;
1484 dst += strlen(strcpy(dst, argv[i])) + 1;
1485 }
1486 argv2[argc] = NULL;
1487
1488 if (argvPtr) {
1489 *argvPtr = argv2;
1490 } else {
1491 free(argv2);
1492 argv2 = NULL;
1493 }
1494 if (argcPtr)
1495 *argcPtr = argc;
1496 return 0;
1497 }
1498
1499 static int XpoptParseArgvString(const char * s, int * argcPtr, const char *** argvPtr)
1500 /*@modifies *argcPtr, *argvPtr @*/
1501 {
1502 const char * src;
1503 char quote = '\0';
1504 int argvAlloced = POPT_ARGV_ARRAY_GROW_DELTA;
1505 const char ** argv = xmalloc(sizeof(*argv) * argvAlloced);
1506 int argc = 0;
1507 size_t buflen = strlen(s) + 1;
1508 char * buf = memset(alloca(buflen), 0, buflen);
1509 int rc = POPT_ERROR_MALLOC;
1510
1511 if (argv == NULL) return rc;
1512 argv[argc] = buf;
1513
1514 for (src = s; *src != '\0'; src++) {
1515 if (quote == *src) {
1516 quote = '\0';
1517 } else if (quote != '\0') {
1518 if (*src == '\\') {
1519 src++;
1520 if (!*src) {
1521 rc = POPT_ERROR_BADQUOTE;
1522 goto exit;
1523 }
1524 if (*src != quote) *buf++ = '\\';
1525 }
1526 *buf++ = *src;
1527 } else if (isspace(*src)) {
1528 if (*argv[argc] != '\0') {
1529 buf++, argc++;
1530 if (argc == argvAlloced) {
1531 argvAlloced += POPT_ARGV_ARRAY_GROW_DELTA;
1532 argv = realloc(argv, sizeof(*argv) * argvAlloced);
1533 if (argv == NULL) goto exit;
1534 }
1535 argv[argc] = buf;
1536 }
1537 } else switch (*src) {
1538 case '"':
1539 case '\'':
1540 quote = *src;
1541 /*@switchbreak@*/ break;
1542 case '\\':
1543 src++;
1544 if (!*src) {
1545 rc = POPT_ERROR_BADQUOTE;
1546 goto exit;
1547 }
1548 /*@fallthrough@*/
1549 default:
1550 *buf++ = *src;
1551 /*@switchbreak@*/ break;
1552 }
1553 }
1554
1555 if (strlen(argv[argc])) {
1556 argc++, buf++;
1557 }
1558
1559 rc = XpoptDupArgv(argc, argv, argcPtr, argvPtr);
1560
1561 exit:
1562 if (argv) free(argv);
1563 return rc;
1564 }
1565 #endif /* !defined(DEBUG_MACROS) */
1566
1574 #if defined(WITH_FICL) || defined(WITH_JS) || defined(WITH_PERLEMBED) || defined(WITH_PYTHONEMBED) || defined(WITH_RUBYEMBED) || defined(WITH_TCL)
1575 static char * parseEmbedded(const char * s, size_t nb, const char *** avp)
1576 /*@*/
1577 {
1578 char * script = NULL;
1579 const char * se;
1580
1581 /* XXX FIXME: args might have embedded : too. */
1582 for (se = s + 1; se < (s+nb); se++)
1583 switch (*se) {
1584 default: continue; /*@notreached@*/ break;
1585 case ':': goto bingo; /*@notreached@*/ break;
1586 }
1587
1588 bingo:
1589 { size_t na = (size_t)(se-s-1);
1590 char * args = NULL;
1591 int ac;
1592 int rc;
1593
1594 args = memcpy(xmalloc(na+1), s+1, na);
1595 args[na] = '\0';
1596
1597 ac = 0;
1598 rc = XpoptParseArgvString(args, &ac, avp);
1599 args = _free(args);
1600 nb -= na;
1601 }
1602
1603 nb -= 3;
1604 script = memcpy(xmalloc(nb+1), se+1, nb+1);
1605 script[nb] = '\0';
1606 return script;
1607 }
1608 #endif
1609
1616 static int
1617 expandMacro(MacroBuf mb)
1618 /*@globals rpmGlobalMacroContext,
1619 print_macro_trace, print_expand_trace, h_errno,
1620 fileSystem, internalState @*/
1621 /*@modifies mb, rpmGlobalMacroContext,
1622 print_macro_trace, print_expand_trace,
1623 fileSystem, internalState @*/
1624 {
1625 MacroEntry *mep;
1626 MacroEntry me;
1627 const char *s = mb->s, *se;
1628 const char *f, *fe;
1629 const char *g, *ge;
1630 size_t fn, gn;
1631 char *t = mb->t; /* save expansion pointer for printExpand */
1632 int c;
1633 int rc = 0;
1634 int negate;
1635 int stackarray;
1636 const char * lastc;
1637 int chkexist;
1638
1639 if (++mb->depth > max_macro_depth) {
1640 rpmlog(RPMLOG_ERR,
1641 _("Recursion depth(%d) greater than max(%d)\n"),
1642 mb->depth, max_macro_depth);
1643 mb->depth--;
1644 mb->expand_trace = 1;
1645 return 1;
1646 }
1647
1648 while (rc == 0 && mb->nb > 0 && (c = (int)*s) != (int)'\0') {
1649 s++;
1650 /* Copy text until next macro */
1651 switch(c) {
1652 case '%':
1653 if (*s != '\0') { /* Ensure not end-of-string. */
1654 if (*s != '%')
1655 /*@switchbreak@*/ break;
1656 s++; /* skip first % in %% */
1657 }
1658 /*@fallthrough@*/
1659 default:
1660 SAVECHAR(mb, c);
1661 continue;
1662 /*@notreached@*/ /*@switchbreak@*/ break;
1663 }
1664
1665 /* Expand next macro */
1666 f = fe = NULL;
1667 g = ge = NULL;
1668 if (mb->depth > 1) /* XXX full expansion for outermost level */
1669 t = mb->t; /* save expansion pointer for printExpand */
1670 stackarray = chkexist = negate = 0;
1671 lastc = NULL;
1672 switch ((c = (int) *s)) {
1673 default: /* %name substitution */
1674 while (*s != '\0' && strchr("!?@", *s) != NULL) {
1675 switch(*s++) {
1676 case '@':
1677 stackarray = ((stackarray + 1) % 2);
1678 /*@switchbreak@*/ break;
1679 case '!':
1680 negate = ((negate + 1) % 2);
1681 /*@switchbreak@*/ break;
1682 case '?':
1683 chkexist++;
1684 /*@switchbreak@*/ break;
1685 }
1686 }
1687 f = se = s;
1688 if (*se == '-')
1689 se++;
1690 while((c = (int) *se) && (xisalnum(c) || c == (int) '_'))
1691 se++;
1692 /* Recognize non-alnum macros too */
1693 switch (*se) {
1694 case '*':
1695 se++;
1696 if (*se == '*') se++;
1697 /*@innerbreak@*/ break;
1698 case '#':
1699 se++;
1700 /*@innerbreak@*/ break;
1701 default:
1702 /*@innerbreak@*/ break;
1703 }
1704 fe = se;
1705 /* For "%name " macros ... */
1706 /*@-globs@*/
1707 if ((c = (int) *fe) && isblank(c))
1708 if ((lastc = strchr(fe,'\n')) == NULL)
1709 lastc = strchr(fe, '\0');
1710 /*@=globs@*/
1711 /*@switchbreak@*/ break;
1712 case '(': /* %(...) shell escape */
1713 if ((se = matchchar(s, (char)c, ')')) == NULL) {
1714 rpmlog(RPMLOG_ERR,
1715 _("Unterminated %c: %s\n"), (char)c, s);
1716 rc = 1;
1717 continue;
1718 }
1719 if (mb->macro_trace)
1720 printMacro(mb, s, se+1);
1721
1722 s++; /* skip ( */
1723 rc = doShellEscape(mb, s, (se - s));
1724 se++; /* skip ) */
1725
1726 s = se;
1727 continue;
1728 /*@notreached@*/ /*@switchbreak@*/ break;
1729 case '{': /* %{...}/%{...:...} substitution */
1730 if ((se = matchchar(s, (char)c, '}')) == NULL) {
1731 rpmlog(RPMLOG_ERR,
1732 _("Unterminated %c: %s\n"), (char)c, s);
1733 rc = 1;
1734 continue;
1735 }
1736 f = s+1;/* skip { */
1737 se++; /* skip } */
1738 while (strchr("!?@", *f) != NULL) {
1739 switch(*f++) {
1740 case '@':
1741 stackarray = ((stackarray + 1) % 2);
1742 /*@switchbreak@*/ break;
1743 case '!':
1744 negate = ((negate + 1) % 2);
1745 /*@switchbreak@*/ break;
1746 case '?':
1747 chkexist++;
1748 /*@switchbreak@*/ break;
1749 }
1750 }
1751 /* Find end-of-expansion, handle %{foo:bar} expansions. */
1752 for (fe = f; (c = (int) *fe) && !strchr(" :}", c);)
1753 fe++;
1754 switch (c) {
1755 case ':':
1756 g = fe + 1;
1757 ge = se - 1;
1758 /*@innerbreak@*/ break;
1759 case ' ':
1760 lastc = se-1;
1761 /*@innerbreak@*/ break;
1762 default:
1763 /*@innerbreak@*/ break;
1764 }
1765 /*@switchbreak@*/ break;
1766 }
1767
1768 /* XXX Everything below expects fe > f */
1769 fn = (fe - f);
1770 gn = (ge - g);
1771 if ((fe - f) <= 0) {
1772 /* XXX Process % in unknown context */
1773 c = (int) '%'; /* XXX only need to save % */
1774 SAVECHAR(mb, c);
1775 #if 0
1776 rpmlog(RPMLOG_ERR,
1777 _("A %% is followed by an unparseable macro\n"));
1778 #endif
1779 s = se;
1780 continue;
1781 }
1782
1783 if (mb->macro_trace)
1784 printMacro(mb, s, se);
1785
1786 /* Expand builtin macros */
1787 if (STREQ("load", f, fn)) {
1788 if (g != NULL) {
1789 char * mfn = strncpy(alloca(gn + 1), g, gn);
1790 int xx;
1791 mfn[gn] = '\0';
1792 xx = rpmLoadMacroFile(NULL, mfn);
1793 /* Print failure iff %{load:...} or %{!?load:...} */
1794 if (xx != 0 && chkexist == negate)
1795 rpmlog(RPMLOG_ERR, _("%s: load macros failed\n"), mfn);
1796 }
1797 s = se;
1798 continue;
1799 }
1800 if (STREQ("global", f, fn)) {
1801 s = doDefine(mb, se, RMIL_GLOBAL, 1);
1802 continue;
1803 }
1804 if (STREQ("define", f, fn)) {
1805 s = doDefine(mb, se, mb->depth, 0);
1806 continue;
1807 }
1808 if (STREQ("undefine", f, fn)) {
1809 s = doUndefine(mb->mc, se);
1810 continue;
1811 }
1812
1813 if (STREQ("echo", f, fn) ||
1814 STREQ("warn", f, fn) ||
1815 STREQ("error", f, fn)) {
1816 int waserror = 0;
1817 if (STREQ("error", f, fn))
1818 waserror = 1, rc = 1;
1819 if (g != NULL && g < ge)
1820 doOutput(mb, waserror, g, gn);
1821 else
1822 doOutput(mb, waserror, f, fn);
1823 s = se;
1824 continue;
1825 }
1826
1827 if (STREQ("trace", f, fn)) {
1828 /* XXX TODO restore expand_trace/macro_trace to 0 on return */
1829 mb->expand_trace = mb->macro_trace = (negate ? 0 : mb->depth);
1830 if (mb->depth == 1) {
1831 print_macro_trace = mb->macro_trace;
1832 print_expand_trace = mb->expand_trace;
1833 }
1834 s = se;
1835 continue;
1836 }
1837
1838 if (STREQ("dump", f, fn)) {
1839 rpmDumpMacroTable(mb->mc, NULL);
1840 while (iseol(*se))
1841 se++;
1842 s = se;
1843 continue;
1844 }
1845
1846 #ifdef WITH_LUA
1847 if (STREQ("lua", f, fn)) {
1848 rpmlua lua = rpmluaGetGlobalState();
1849 rpmlua olua = memcpy(alloca(sizeof(*olua)), lua, sizeof(*olua));
1850 const char *ls = s+sizeof("{lua:")-1;
1851 const char *lse = se-sizeof("}")+1;
1852 char *scriptbuf = (char *)xmalloc((lse-ls)+1);
1853 const char *printbuf;
1854
1855 /* Reset the stateful output buffer before recursing down. */
1856 lua->storeprint = 1;
1857 lua->printbuf = NULL;
1858 lua->printbufsize = 0;
1859 lua->printbufused = 0;
1860
1861 memcpy(scriptbuf, ls, lse-ls);
1862 scriptbuf[lse-ls] = '\0';
1863 if (rpmluaRunScript(lua, scriptbuf, NULL) == -1)
1864 rc = 1;
1865 printbuf = rpmluaGetPrintBuffer(lua);
1866 if (printbuf) {
1867 size_t len = strlen(printbuf);
1868 if (len > mb->nb)
1869 len = mb->nb;
1870 memcpy(mb->t, printbuf, len);
1871 mb->t += len;
1872 mb->nb -= len;
1873 }
1874
1875 /* Restore the stateful output buffer after recursion. */
1876 lua->storeprint = olua->storeprint;
1877 lua->printbuf = olua->printbuf;
1878 lua->printbufsize = olua->printbufsize;
1879 lua->printbufused = olua->printbufused;
1880
1881 free(scriptbuf);
1882 s = se;
1883 continue;
1884 }
1885 #endif
1886
1887 #ifdef WITH_FICL
1888 if (STREQ("ficl", f, fn)) {
1889 const char ** av = NULL;
1890 char * script = parseEmbedded(s, (size_t)(se-s), &av);
1891 rpmficl ficl = rpmficlNew(av, _globalI);
1892 const char * result = NULL;
1893
1894 if (rpmficlRun(ficl, script, &result) != RPMRC_OK)
1895 rc = 1;
1896 else {
1897 if (result == NULL) result = "FIXME";
1898 if (result != NULL && *result != '\0') {
1899 size_t len = strlen(result);
1900 if (len > mb->nb)
1901 len = mb->nb;
1902 memcpy(mb->t, result, len);
1903 mb->t += len;
1904 mb->nb -= len;
1905 }
1906 }
1907 ficl = rpmficlFree(ficl);
1908 av = _free(av);
1909 script = _free(script);
1910 s = se;
1911 continue;
1912 }
1913 #endif
1914
1915 #ifdef WITH_JS
1916 if (STREQ("js", f, fn)) {
1917 const char ** av = NULL;
1918 char * script = parseEmbedded(s, (size_t)(se-s), &av);
1919 rpmjs js = rpmjsNew(av, _globalI);
1920 const char * result = NULL;
1921
1922 if (rpmjsRun(js, script, &result) != RPMRC_OK)
1923 rc = 1;
1924 else {
1925 if (result == NULL) result = "FIXME";
1926 if (result != NULL && *result != '\0') {
1927 size_t len = strlen(result);
1928 if (len > mb->nb)
1929 len = mb->nb;
1930 memcpy(mb->t, result, len);
1931 mb->t += len;
1932 mb->nb -= len;
1933 }
1934 }
1935 js = rpmjsFree(js);
1936 av = _free(av);
1937 script = _free(script);
1938 s = se;
1939 continue;
1940 }
1941 #endif
1942
1943 #ifdef WITH_PERLEMBED
1944 if (STREQ("perl", f, fn)) {
1945 const char ** av = NULL;
1946 char * script = parseEmbedded(s, (size_t)(se-s), &av);
1947 rpmperl perl = rpmperlNew(av, _globalI);
1948 const char * result = NULL;
1949
1950 if (rpmperlRun(perl, script, &result) != RPMRC_OK)
1951 rc = 1;
1952 else {
1953 if (result == NULL) result = "FIXME";
1954 if (result != NULL && *result != '\0') {
1955 size_t len = strlen(result);
1956 if (len > mb->nb)
1957 len = mb->nb;
1958 memcpy(mb->t, result, len);
1959 mb->t += len;
1960 mb->nb -= len;
1961 }
1962 }
1963 perl = rpmperlFree(perl);
1964 av = _free(av);
1965 script = _free(script);
1966 s = se;
1967 continue;
1968 }
1969 #endif
1970
1971 #ifdef WITH_PYTHONEMBED
1972 if (STREQ("python", f, fn)) {
1973 const char ** av = NULL;
1974 char * script = parseEmbedded(s, (size_t)(se-s), &av);
1975 rpmpython python = rpmpythonNew(av, _globalI);
1976 const char * result = NULL;
1977
1978 if (rpmpythonRun(python, script, &result) != RPMRC_OK)
1979 rc = 1;
1980 else {
1981 if (result == NULL) result = "FIXME";
1982 if (result != NULL && *result != '\0') {
1983 size_t len = strlen(result);
1984 if (len > mb->nb)
1985 len = mb->nb;
1986 memcpy(mb->t, result, len);
1987 mb->t += len;
1988 mb->nb -= len;
1989 }
1990 }
1991 python = rpmpythonFree(python);
1992 av = _free(av);
1993 script = _free(script);
1994 s = se;
1995 continue;
1996 }
1997 #endif
1998
1999 #ifdef WITH_RUBYEMBED
2000 if (STREQ("ruby", f, fn)) {
2001 const char ** av = NULL;
2002 char * script = parseEmbedded(s, (size_t)(se-s), &av);
2003 rpmruby ruby = rpmrubyNew(av, _globalI);
2004 const char * result = NULL;
2005
2006 if (rpmrubyRun(ruby, script, &result) != RPMRC_OK)
2007 rc = 1;
2008 else {
2009 if (result == NULL) result = "FIXME";
2010 if (result != NULL && *result != '\0') {
2011 size_t len = strlen(result);
2012 if (len > mb->nb)
2013 len = mb->nb;
2014 memcpy(mb->t, result, len);
2015 mb->t += len;
2016 mb->nb -= len;
2017 }
2018 }
2019 ruby = rpmrubyFree(ruby);
2020 av = _free(av);
2021 script = _free(script);
2022 s = se;
2023 continue;
2024 }
2025 #endif
2026
2027 #ifdef WITH_TCL
2028 if (STREQ("tcl", f, fn)) {
2029 const char ** av = NULL;
2030 char * script = parseEmbedded(s, (size_t)(se-s), &av);
2031 rpmtcl tcl = rpmtclNew(av, _globalI);
2032 const char * result = NULL;
2033
2034 if (rpmtclRun(tcl, script, &result) != RPMRC_OK)
2035 rc = 1;
2036 else if (result != NULL && *result != '\0') {
2037 size_t len = strlen(result);
2038 if (len > mb->nb)
2039 len = mb->nb;
2040 memcpy(mb->t, result, len);
2041 mb->t += len;
2042 mb->nb -= len;
2043 }
2044 tcl = rpmtclFree(tcl);
2045 av = _free(av);
2046 script = _free(script);
2047 s = se;
2048 continue;
2049 }
2050 #endif
2051
2052 /* Rewrite "%patchNN ..." as "%patch -P NN ..." and expand. */
2053 if (lastc && fn > 5 && STREQ("patch", f, 5) && xisdigit((int)f[5])) {
2054 /*@-internalglobs@*/ /* FIX: verbose may be set */
2055 doFoo(mb, negate, f, (lastc - f), NULL, 0);
2056 /*@=internalglobs@*/
2057 s = lastc;
2058 continue;
2059 }
2060
2061 /* XXX necessary but clunky */
2062 if (STREQ("basename", f, fn) ||
2063 STREQ("dirname", f, fn) ||
2064 STREQ("realpath", f, fn) ||
2065 STREQ("getenv", f, fn) ||
2066 STREQ("shrink", f, fn) ||
2067 STREQ("suffix", f, fn) ||
2068 STREQ("expand", f, fn) ||
2069 STREQ("verbose", f, fn) ||
2070 STREQ("uncompress", f, fn) ||
2071 STREQ("mkstemp", f, fn) ||
2072 STREQ("mkdtemp", f, fn) ||
2073 STREQ("uuid", f, fn) ||
2074 STREQ("url2path", f, fn) ||
2075 STREQ("u2p", f, fn) ||
2076 STREQ("S", f, fn) ||
2077 STREQ("P", f, fn) ||
2078 STREQ("F", f, fn)) {
2079 /*@-internalglobs@*/ /* FIX: verbose may be set */
2080 doFoo(mb, negate, f, fn, g, gn);
2081 /*@=internalglobs@*/
2082 s = se;
2083 continue;
2084 }
2085
2086 /* Expand defined macros */
2087 mep = findEntry(mb->mc, f, fn);
2088 me = (mep ? *mep : NULL);
2089
2090 /* XXX Special processing for flags */
2091 if (*f == '-') {
2092 if (me)
2093 me->used++; /* Mark macro as used */
2094 if ((me == NULL && !negate) || /* Without -f, skip %{-f...} */
2095 (me != NULL && negate)) { /* With -f, skip %{!-f...} */
2096 s = se;
2097 continue;
2098 }
2099
2100 if (g && g < ge) { /* Expand X in %{-f:X} */
2101 rc = expandT(mb, g, gn);
2102 } else
2103 if (me && me->body && *me->body) {/* Expand %{-f}/%{-f*} */
2104 rc = expandT(mb, me->body, strlen(me->body));
2105 }
2106 s = se;
2107 continue;
2108 }
2109
2110 /* XXX Special processing for macro existence */
2111 if (chkexist) {
2112 if ((me == NULL && !negate) || /* Without -f, skip %{?f...} */
2113 (me != NULL && negate)) { /* With -f, skip %{!?f...} */
2114 s = se;
2115 continue;
2116 }
2117 if (g && g < ge) { /* Expand X in %{?f:X} */
2118 rc = expandT(mb, g, gn);
2119 } else
2120 if (me && me->body && *me->body) { /* Expand %{?f}/%{?f*} */
2121 rc = expandT(mb, me->body, strlen(me->body));
2122 }
2123 s = se;
2124 continue;
2125 }
2126
2127 if (me == NULL) { /* leave unknown %... as is */
2128 #if !defined(RPM_VENDOR_WINDRIVER_DEBUG) /* XXX usually disabled */
2129 #if DEAD
2130 /* XXX hack to skip over empty arg list */
2131 if (fn == 1 && *f == '*') {
2132 s = se;
2133 continue;
2134 }
2135 #endif
2136 /* XXX hack to permit non-overloaded %foo to be passed */
2137 c = (int) '%'; /* XXX only need to save % */
2138 SAVECHAR(mb, c);
2139 #else
2140 if (!strncmp(f, "if", fn) ||
2141 !strncmp(f, "else", fn) ||
2142 !strncmp(f, "endif", fn)) {
2143 c = '%'; /* XXX only need to save % */
2144 SAVECHAR(mb, c);
2145 } else {
2146 rpmlog(RPMLOG_ERR,
2147 _("Macro %%%.*s not found, skipping\n"), fn, f);
2148 s = se;
2149 }
2150 #endif
2151 continue;
2152 }
2153
2154 /* XXX Special processing to create a tuple from stack'd values. */
2155 if (stackarray) {
2156 if (!(g && g < ge)) {
2157 g = "\n";
2158 gn = strlen(g);
2159 }
2160 rc = expandFIFO(mb, me, g, gn);
2161 s = se;
2162 continue;
2163 }
2164
2165 /* Setup args for "%name " macros with opts */
2166 if (me && me->opts != NULL) {
2167 if (lastc != NULL) {
2168 se = grabArgs(mb, me, fe, lastc);
2169 } else {
2170 addMacro(mb->mc, "**", NULL, "", mb->depth);
2171 addMacro(mb->mc, "*", NULL, "", mb->depth);
2172 addMacro(mb->mc, "#", NULL, "0", mb->depth);
2173 addMacro(mb->mc, "0", NULL, me->name, mb->depth);
2174 }
2175 }
2176
2177 /* Recursively expand body of macro */
2178 if (me->body && *me->body) {
2179 mb->s = me->body;
2180 rc = expandMacro(mb);
2181 if (rc == 0)
2182 me->used++; /* Mark macro as used */
2183 }
2184
2185 /* Free args for "%name " macros with opts */
2186 if (me->opts != NULL)
2187 freeArgs(mb);
2188
2189 s = se;
2190 }
2191
2192 *mb->t = '\0';
2193 mb->s = s;
2194 mb->depth--;
2195 if (rc != 0 || mb->expand_trace)
2196 printExpansion(mb, t, mb->t);
2197 return rc;
2198 }
2199
2200 #if defined(RPM_VENDOR_OPENPKG) /* stick-with-rpm-file-sanity-checking */ || \
2201 !defined(POPT_ERROR_BADCONFIG) /* XXX POPT 1.15 retrofit */
2202 int rpmSecuritySaneFile(const char *filename)
2203 {
2204 struct stat sb;
2205 uid_t uid;
2206
2207 if (stat(filename, &sb) == -1)
2208 return 1;
2209 uid = getuid();
2210 if (sb.st_uid != uid)
2211 return 0;
2212 if (!S_ISREG(sb.st_mode))
2213 return 0;
2214 if (sb.st_mode & (S_IWGRP|S_IWOTH))
2215 return 0;
2216 return 1;
2217 }
2218 #endif
2219
2220 #if !defined(DEBUG_MACROS)
2221 /* =============================================================== */
2222 /*@unchecked@*/
2223 static int _debug = 0;
2224
2225 int rpmGlob(const char * patterns, int * argcPtr, const char *** argvPtr)
2226 {
2227 int ac = 0;
2228 const char ** av = NULL;
2229 int argc = 0;
2230 const char ** argv = NULL;
2231 char * globRoot = NULL;
2232 #ifdef ENABLE_NLS
2233 const char * old_collate = NULL;
2234 const char * old_ctype = NULL;
2235 const char * t;
2236 #endif
2237 size_t maxb, nb;
2238 size_t i;
2239 int j;
2240 int rc;
2241
2242 rc = XpoptParseArgvString(patterns, &ac, &av);
2243 if (rc)
2244 return rc;
2245 #ifdef ENABLE_NLS
2246 t = setlocale(LC_COLLATE, NULL);
2247 if (t)
2248 old_collate = xstrdup(t);
2249 t = setlocale(LC_CTYPE, NULL);
2250 if (t)
2251 old_ctype = xstrdup(t);
2252 (void) setlocale(LC_COLLATE, "C");
2253 (void) setlocale(LC_CTYPE, "C");
2254 #endif
2255
2256 if (av != NULL)
2257 for (j = 0; j < ac; j++) {
2258 const char * globURL;
2259 const char * path;
2260 int ut = urlPath(av[j], &path);
2261 glob_t gl;
2262
2263 if (!Glob_pattern_p(av[j], 0) && strchr(path, '~') == NULL) {
2264 argv = xrealloc(argv, (argc+2) * sizeof(*argv));
2265 argv[argc] = xstrdup(av[j]);
2266 if (_debug)
2267 fprintf(stderr, "*** rpmGlob argv[%d] \"%s\"\n", argc, argv[argc]);
2268 argc++;
2269 continue;
2270 }
2271
2272 gl.gl_pathc = 0;
2273 gl.gl_pathv = NULL;
2274 rc = Glob(av[j], GLOB_TILDE, Glob_error, &gl);
2275 if (rc)
2276 goto exit;
2277
2278 /* XXX Prepend the URL leader for globs that have stripped it off */
2279 maxb = 0;
2280 for (i = 0; i < gl.gl_pathc; i++) {
2281 if ((nb = strlen(&(gl.gl_pathv[i][0]))) > maxb)
2282 maxb = nb;
2283 }
2284
2285 nb = ((ut == URL_IS_PATH) ? (path - av[j]) : 0);
2286 maxb += nb;
2287 maxb += 1;
2288 globURL = globRoot = xmalloc(maxb);
2289
2290 switch (ut) {
2291 case URL_IS_PATH:
2292 case URL_IS_DASH:
2293 strncpy(globRoot, av[j], nb);
2294 /*@switchbreak@*/ break;
2295 case URL_IS_HTTPS:
2296 case URL_IS_HTTP:
2297 case URL_IS_FTP:
2298 case URL_IS_HKP:
2299 case URL_IS_UNKNOWN:
2300 default:
2301 /*@switchbreak@*/ break;
2302 }
2303 globRoot += nb;
2304 *globRoot = '\0';
2305 if (_debug)
2306 fprintf(stderr, "*** GLOB maxb %d diskURL %d %*s globURL %p %s\n", (int)maxb, (int)nb, (int)nb, av[j], globURL, globURL);
2307
2308 argv = xrealloc(argv, (argc+gl.gl_pathc+1) * sizeof(*argv));
2309
2310 if (argv != NULL)
2311 for (i = 0; i < gl.gl_pathc; i++) {
2312 const char * globFile = &(gl.gl_pathv[i][0]);
2313 if (globRoot > globURL && globRoot[-1] == '/')
2314 while (*globFile == '/') globFile++;
2315 strcpy(globRoot, globFile);
2316 if (_debug)
2317 fprintf(stderr, "*** rpmGlob argv[%d] \"%s\"\n", argc, globURL);
2318 argv[argc++] = xstrdup(globURL);
2319 }
2320 /*@-immediatetrans@*/
2321 Globfree(&gl);
2322 /*@=immediatetrans@*/
2323 globURL = _free(globURL);
2324 }
2325
2326 if (argv != NULL && argc > 0) {
2327 argv[argc] = NULL;
2328 if (argvPtr)
2329 *argvPtr = argv;
2330 if (argcPtr)
2331 *argcPtr = argc;
2332 rc = 0;
2333 } else
2334 rc = 1;
2335
2336
2337 exit:
2338 #ifdef ENABLE_NLS
2339 if (old_collate) {
2340 (void) setlocale(LC_COLLATE, old_collate);
2341 old_collate = _free(old_collate);
2342 }
2343 if (old_ctype) {
2344 (void) setlocale(LC_CTYPE, old_ctype);
2345 old_ctype = _free(old_ctype);
2346 }
2347 #endif
2348 av = _free(av);
2349 if (rc || argvPtr == NULL) {
2350 /*@-dependenttrans -unqualifiedtrans@*/
2351 if (argv != NULL)
2352 for (j = 0; j < argc; j++)
2353 argv[j] = _free(argv[j]);
2354 argv = _free(argv);
2355 /*@=dependenttrans =unqualifiedtrans@*/
2356 }
2357 return rc;
2358 }
2359 #endif /* !defined(DEBUG_MACROS) */
2360
2361 /* =============================================================== */
2362
2363 int
2364 expandMacros(void * spec, MacroContext mc, char * sbuf, size_t slen)
2365 {
2366 MacroBuf mb = alloca(sizeof(*mb));
2367 char *tbuf;
2368 int rc;
2369
2370 if (sbuf == NULL || slen == 0)
2371 return 0;
2372 if (mc == NULL) mc = rpmGlobalMacroContext;
2373
2374 tbuf = alloca(slen + 1);
2375 tbuf[0] = '\0';
2376
2377 mb->s = sbuf;
2378 mb->t = tbuf;
2379 mb->nb = slen;
2380 mb->depth = 0;
2381 mb->macro_trace = print_macro_trace;
2382 mb->expand_trace = print_expand_trace;
2383
2384 mb->spec = spec; /* (future) %file expansion info */
2385 mb->mc = mc;
2386
2387 rc = expandMacro(mb);
2388
2389 tbuf[slen] = '\0';
2390 if (mb->nb == 0)
2391 rpmlog(RPMLOG_ERR, _("Macro expansion too big for target buffer\n"));
2392 else
2393 strncpy(sbuf, tbuf, (slen - mb->nb + 1));
2394
2395 return rc;
2396 }
2397
2398 void
2399 addMacro(MacroContext mc,
2400 const char * n, const char * o, const char * b, int level)
2401 {
2402 MacroEntry * mep;
2403 const char * name = n;
2404
2405 if (*name == '.') /* XXX readonly macros */
2406 name++;
2407 if (*name == '.') /* XXX readonly macros */
2408 name++;
2409
2410 if (mc == NULL) mc = rpmGlobalMacroContext;
2411
2412 /* If new name, expand macro table */
2413 if ((mep = findEntry(mc, name, 0)) == NULL) {
2414 if (mc->firstFree == mc->macrosAllocated)
2415 expandMacroTable(mc);
2416 if (mc->macroTable != NULL)
2417 mep = mc->macroTable + mc->firstFree++;
2418 }
2419
2420 if (mep != NULL) {
2421 /* XXX permit "..foo" to be pushed over ".foo" */
2422 if (*mep && (*mep)->flags && !(n[0] == '.' && n[1] == '.')) {
2423 /* XXX avoid error message for %buildroot */
2424 if (strcmp((*mep)->name, "buildroot"))
2425 rpmlog(RPMLOG_ERR, _("Macro '%s' is readonly and cannot be changed.\n"), n);
2426 return;
2427 }
2428 /* Push macro over previous definition */
2429 pushMacro(mep, n, o, b, level);
2430
2431 /* If new name, sort macro table */
2432 if ((*mep)->prev == NULL)
2433 sortMacroTable(mc);
2434 }
2435 }
2436
2437 void
2438 delMacro(MacroContext mc, const char * n)
2439 {
2440 MacroEntry * mep;
2441
2442 if (mc == NULL) mc = rpmGlobalMacroContext;
2443 /* If name exists, pop entry */
2444 if ((mep = findEntry(mc, n, 0)) != NULL) {
2445 popMacro(mep);
2446 /* If deleted name, sort macro table */
2447 if (!(mep && *mep))
2448 sortMacroTable(mc);
2449 }
2450 }
2451
2452 /*@-mustmod@*/ /* LCL: mc is modified through mb->mc, mb is abstract */
2453 int
2454 rpmDefineMacro(MacroContext mc, const char * macro, int level)
2455 {
2456 MacroBuf mb = alloca(sizeof(*mb));
2457
2458 memset(mb, 0, sizeof(*mb));
2459 /* XXX just enough to get by */
2460 mb->mc = (mc ? mc : rpmGlobalMacroContext);
2461 (void) doDefine(mb, macro, level, 0);
2462 return 0;
2463 }
2464 /*@=mustmod@*/
2465
2466 /*@-mustmod@*/ /* LCL: mc is modified through mb->mc, mb is abstract */
2467 int
2468 rpmUndefineMacro(MacroContext mc, const char * macro)
2469 {
2470 (void) doUndefine(mc ? mc : rpmGlobalMacroContext, macro);
2471 return 0;
2472 }
2473 /*@=mustmod@*/
2474
2475 void
2476 rpmLoadMacros(MacroContext mc, int level)
2477 {
2478
2479 if (mc == NULL || mc == rpmGlobalMacroContext)
2480 return;
2481
2482 if (mc->macroTable != NULL) {
2483 int i;
2484 for (i = 0; i < mc->firstFree; i++) {
2485 MacroEntry *mep, me;
2486 mep = &mc->macroTable[i];
2487 me = *mep;
2488
2489 if (me == NULL) /* XXX this should never happen */
2490 continue;
2491 addMacro(NULL, me->name, me->opts, me->body, (level - 1));
2492 }
2493 }
2494 }
2495
2496 #if defined(RPM_VENDOR_OPENPKG) /* expand-macrosfile-macro */
2497 static void expand_macrosfile_macro(const char *file_name, const char *buf, size_t bufn)
2498 {
2499 char *cp;
2500 size_t l, k;
2501 static const char *macro_name = "%{macrosfile}";
2502
2503 l = strlen(macro_name);
2504 k = strlen(file_name);
2505 while ((cp = strstr(buf, macro_name)) != NULL) {
2506 if (((strlen(buf) - l) + k) < bufn) {
2507 memmove(cp+k, cp+l, strlen(cp+l)+1);
2508 memcpy(cp, file_name, k);
2509 }
2510 }
2511 return;
2512 }
2513 #endif
2514
2515 int
2516 rpmLoadMacroFile(MacroContext mc, const char * fn)
2517 {
2518 /* XXX TODO: teach rdcl() to read through a URI, eliminate ".fpio". */
2519 FD_t fd = Fopen(fn, "r.fpio");
2520 size_t bufn = _macro_BUFSIZ;
2521 char *buf = alloca(bufn);
2522 int rc = -1;
2523
2524 if (fd == NULL || Ferror(fd)) {
2525 if (fd) (void) Fclose(fd);
2526 return rc;
2527 }
2528
2529 /* XXX Assume new fangled macro expansion */
2530 /*@-mods@*/
2531 max_macro_depth = _MAX_MACRO_DEPTH;
2532 /*@=mods@*/
2533
2534 buf[0] = '\0';
2535 while(rdcl(buf, bufn, fd) != NULL) {
2536 char *n;
2537 int c;
2538
2539 n = buf;
2540 SKIPBLANK(n, c);
2541
2542 if (c != (int) '%')
2543 continue;
2544 n++; /* skip % */
2545 #if defined(RPM_VENDOR_OPENPKG) /* expand-macro-source */
2546 expand_macrosfile_macro(fn, buf, bufn);
2547 #endif
2548 rc = rpmDefineMacro(mc, n, RMIL_MACROFILES);
2549 }
2550 rc = Fclose(fd);
2551 return rc;
2552 }
2553
2554 void
2555 rpmInitMacros(MacroContext mc, const char * macrofiles)
2556 {
2557 char *mfiles, *m, *me;
2558
2559 if (macrofiles == NULL)
2560 return;
2561 #ifdef DYING
2562 if (mc == NULL) mc = rpmGlobalMacroContext;
2563 #endif
2564
2565 mfiles = xstrdup(macrofiles);
2566 for (m = mfiles; m && *m != '\0'; m = me) {
2567 const char ** av;
2568 int ac;
2569 int i;
2570
2571 for (me = m; (me = strchr(me, ':')) != NULL; me++) {
2572 /* Skip over URI's. */
2573 if (!(me[1] == '/' && me[2] == '/'))
2574 /*@innerbreak@*/ break;
2575 }
2576
2577 if (me && *me == ':')
2578 *me++ = '\0';
2579 else
2580 me = m + strlen(m);
2581
2582 /* Glob expand the macro file path element, expanding ~ to $HOME. */
2583 ac = 0;
2584 av = NULL;
2585 #if defined(DEBUG_MACROS)
2586 ac = 1;
2587 av = xmalloc((ac + 1) * sizeof(*av));
2588 av[0] = strdup(m);
2589 av[1] = NULL;
2590 #else
2591 i = rpmGlob(m, &ac, &av);
2592 if (i != 0)
2593 continue;
2594 #endif
2595
2596 /* Read macros from each file. */
2597
2598 for (i = 0; i < ac; i++) {
2599 size_t slen = strlen(av[i]);
2600 const char *fn = av[i];
2601
2602 if (fn[0] == '@' /* attention */) {
2603 fn++;
2604 #if defined(RPM_VENDOR_OPENPKG) /* stick-with-rpm-file-sanity-checking */ || \
2605 !defined(POPT_ERROR_BADCONFIG) /* XXX POPT 1.15 retrofit */
2606 if (!rpmSecuritySaneFile(fn))
2607 #else
2608 if (!poptSaneFile(fn))
2609 #endif
2610 {
2611 rpmlog(RPMLOG_WARNING, "existing RPM macros file \"%s\" considered INSECURE -- not loaded\n", fn);
2612 /*@innercontinue@*/ continue;
2613 }
2614 }
2615
2616 /* Skip backup files and %config leftovers. */
2617 #define _suffix(_s, _x) \
2618 (slen >= sizeof(_x) && !strcmp((_s)+slen-(sizeof(_x)-1), (_x)))
2619 if (!(_suffix(fn, "~")
2620 || _suffix(fn, ".rpmnew")
2621 || _suffix(fn, ".rpmorig")
2622 || _suffix(fn, ".rpmsave"))
2623 )
2624 (void) rpmLoadMacroFile(mc, fn);
2625 #undef _suffix
2626
2627 av[i] = _free(av[i]);
2628 }
2629 av = _free(av);
2630 }
2631 mfiles = _free(mfiles);
2632
2633 /* Reload cmdline macros */
2634 /*@-mods@*/
2635 rpmLoadMacros(rpmCLIMacroContext, RMIL_CMDLINE);
2636 /*@=mods@*/
2637 }
2638
2639 /*@-globstate@*/
2640 void
2641 rpmFreeMacros(MacroContext mc)
2642 {
2643
2644 if (mc == NULL) mc = rpmGlobalMacroContext;
2645
2646 if (mc->macroTable != NULL) {
2647 int i;
2648 for (i = 0; i < mc->firstFree; i++) {
2649 MacroEntry me;
2650 while ((me = mc->macroTable[i]) != NULL) {
2651 /* XXX cast to workaround const */
2652 /*@-onlytrans@*/
2653 if ((mc->macroTable[i] = me->prev) == NULL)
2654 me->name = _free(me->name);
2655 /*@=onlytrans@*/
2656 me->opts = _free(me->opts);
2657 me->body = _free(me->body);
2658 me = _free(me);
2659 }
2660 }
2661 mc->macroTable = _free(mc->macroTable);
2662 }
2663 memset(mc, 0, sizeof(*mc));
2664 }
2665 /*@=globstate@*/
2666
2667 /* =============================================================== */
2668 int isCompressed(const char * file, rpmCompressedMagic * compressed)
2669 {
2670 FD_t fd;
2671 ssize_t nb;
2672 int rc = -1;
2673 unsigned char magic[13];
2674 #if defined(RPM_VENDOR_OPENPKG) || defined(RPM_VENDOR_FEDORA) || defined(RPM_VENDOR_MANDRIVA) /* extension-based-compression-detection */
2675 size_t file_len;
2676 #endif
2677
2678 *compressed = COMPRESSED_NOT;
2679
2680 #if defined(RPM_VENDOR_OPENPKG) || defined(RPM_VENDOR_FEDORA) || defined(RPM_VENDOR_MANDRIVA) /* extension-based-compression-detection */
2681 file_len = strlen(file);
2682 if ((file_len > 4 && strcasecmp(file+file_len-4, ".tbz") == 0)
2683 || (file_len > 4 && strcasecmp(file+file_len-4, ".bz2") == 0)) {
2684 *compressed = COMPRESSED_BZIP2;
2685 return 0;
2686 } else
2687 if (file_len > 4 && strcasecmp(file+file_len-4, ".zip") == 0) {
2688 *compressed = COMPRESSED_ZIP;
2689 return 0;
2690 } else
2691 if ((file_len > 4 && strcasecmp(file+file_len-4, ".tlz") == 0)
2692 || (file_len > 5 && strcasecmp(file+file_len-5, ".lzma") == 0)) {
2693 *compressed = COMPRESSED_LZMA;
2694 return 0;
2695 } else
2696 if (file_len > 4 && strcasecmp(file+file_len-3, ".xz") == 0) {
2697 *compressed = COMPRESSED_XZ;
2698 return 0;
2699 } else
2700 if ((file_len > 4 && strcasecmp(file+file_len-4, ".tgz") == 0)
2701 || (file_len > 3 && strcasecmp(file+file_len-3, ".gz") == 0)
2702 || (file_len > 2 && strcasecmp(file+file_len-2, ".Z") == 0)) {
2703 *compressed = COMPRESSED_OTHER;
2704 return 0;
2705 } else
2706 if (file_len > 5 && strcasecmp(file+file_len-5, ".cpio") == 0) {
2707 *compressed = COMPRESSED_NOT;
2708 return 0;
2709 } else
2710 if (file_len > 4 && strcasecmp(file+file_len-4, ".tar") == 0) {
2711 *compressed = COMPRESSED_NOT;
2712 return 0;
2713 }
2714 #endif
2715
2716 fd = Fopen(file, "r");
2717 if (fd == NULL || Ferror(fd)) {
2718 /* XXX Fstrerror */
2719 rpmlog(RPMLOG_ERR, _("File %s: %s\n"), file, Fstrerror(fd));
2720 if (fd) (void) Fclose(fd);
2721 return 1;
2722 }
2723 nb = Fread(magic, sizeof(magic[0]), sizeof(magic), fd);
2724 if (nb < (ssize_t)0) {
2725 rpmlog(RPMLOG_ERR, _("File %s: %s\n"), file, Fstrerror(fd));
2726 rc = 1;
2727 } else if (nb < (ssize_t)sizeof(magic)) {
2728 rpmlog(RPMLOG_ERR, _("File %s is smaller than %u bytes\n"),
2729 file, (unsigned)sizeof(magic));
2730 rc = 0;
2731 }
2732 (void) Fclose(fd);
2733 if (rc >= 0)
2734 return rc;
2735
2736 rc = 0;
2737
2738 if (magic[0] == 'B' && magic[1] == 'Z')
2739 *compressed = COMPRESSED_BZIP2;
2740 else
2741 if (magic[0] == (unsigned char) 0120 && magic[1] == (unsigned char) 0113
2742 && magic[2] == (unsigned char) 0003 && magic[3] == (unsigned char) 0004) /* pkzip */
2743 *compressed = COMPRESSED_ZIP;
2744 else
2745 if (magic[0] == (unsigned char) 0x89 && magic[1] == 'L'
2746 && magic[2] == 'Z' && magic[3] == 'O') /* lzop */
2747 *compressed = COMPRESSED_LZOP;
2748 else
2749 #if !defined(RPM_VENDOR_OPENPKG) && !defined(RPM_VENDOR_FEDORA) && !defined(RPM_VENDOR_MANDRIVA) /* extension-based-compression-detection */
2750 /* XXX Ick, LZMA has no magic. See http://lkml.org/lkml/2005/6/13/285 */
2751 if (magic[ 9] == (unsigned char) 0x00 && magic[10] == (unsigned char) 0x00 &&
2752 magic[11] == (unsigned char) 0x00 && magic[12] == (unsigned char) 0x00) /* lzmash */
2753 *compressed = COMPRESSED_LZMA;
2754 else
2755 #endif
2756 #if defined(RPM_VENDOR_OPENSUSE)
2757 if (magic[0] == 0135 && magic[1] == 0 && magic[2] == 0) { /* lzma */
2758 *compressed = COMPRESSED_LZMA;
2759 else
2760 #endif
2761 if (magic[0] == (unsigned char) 0xFD && magic[1] == 0x37 && magic[2] == 0x7A
2762 && magic[3] == 0x58 && magic[4] == 0x5A && magic[5] == 0x00) /* xz */
2763 *compressed = COMPRESSED_XZ;
2764 else
2765 if ((magic[0] == (unsigned char) 0037 && magic[1] == (unsigned char) 0213) /* gzip */
2766 || (magic[0] == (unsigned char) 0037 && magic[1] == (unsigned char) 0236) /* old gzip */
2767 || (magic[0] == (unsigned char) 0037 && magic[1] == (unsigned char) 0036) /* pack */
2768 || (magic[0] == (unsigned char) 0037 && magic[1] == (unsigned char) 0240) /* SCO lzh */
2769 || (magic[0] == (unsigned char) 0037 && magic[1] == (unsigned char) 0235)) /* compress */
2770 *compressed = COMPRESSED_OTHER;
2771
2772 return rc;
2773 }
2774
2775 /* =============================================================== */
2776
2777 /*@-modfilesys@*/
2778 /* XXX TODO: merge rpmExpand and rpmMCExpand. gud enuf for now ... */
2779 char *
2780 rpmExpand(const char *arg, ...)
2781 {
2782 MacroContext mc = NULL;
2783 const char *s;
2784 char *t, *te;
2785 size_t sn, tn;
2786 size_t bufn = 8 * _macro_BUFSIZ;
2787
2788 va_list ap;
2789
2790 if (arg == NULL)
2791 return xstrdup("");
2792
2793 t = xmalloc(bufn + strlen(arg) + 1);
2794 *t = '\0';
2795 te = stpcpy(t, arg);
2796
2797 va_start(ap, arg);
2798 while ((s = va_arg(ap, const char *)) != NULL) {
2799 sn = strlen(s);
2800 tn = (te - t);
2801 t = xrealloc(t, tn + sn + bufn + 1);
2802 te = t + tn;
2803 te = stpcpy(te, s);
2804 }
2805 va_end(ap);
2806
2807 *te = '\0';
2808 tn = (te - t);
2809 (void) expandMacros(NULL, mc, t, tn + bufn + 1);
2810 t[tn + bufn] = '\0';
2811 t = xrealloc(t, strlen(t) + 1);
2812
2813 return t;
2814 }
2815
2816 char *
2817 rpmMCExpand(MacroContext mc, const char *arg, ...)
2818 {
2819 const char *s;
2820 char *t, *te;
2821 size_t sn, tn;
2822 size_t bufn = 8 * _macro_BUFSIZ;
2823
2824 va_list ap;
2825
2826 if (arg == NULL)
2827 return xstrdup("");
2828
2829 t = xmalloc(bufn + strlen(arg) + 1);
2830 *t = '\0';
2831 te = stpcpy(t, arg);
2832
2833 va_start(ap, arg);
2834 while ((s = va_arg(ap, const char *)) != NULL) {
2835 sn = strlen(s);
2836 tn = (te - t);
2837 t = xrealloc(t, tn + sn + bufn + 1);
2838 te = t + tn;
2839 te = stpcpy(te, s);
2840 }
2841 va_end(ap);
2842
2843 *te = '\0';
2844 tn = (te - t);
2845 (void) expandMacros(NULL, mc, t, tn + bufn + 1);
2846 t[tn + bufn] = '\0';
2847 t = xrealloc(t, strlen(t) + 1);
2848
2849 return t;
2850 }
2851 /*@=modfilesys@*/
2852
2853 int
2854 rpmExpandNumeric(const char *arg)
2855 {
2856 const char *val;
2857 int rc;
2858
2859 if (arg == NULL)
2860 return 0;
2861
2862 val = rpmExpand(arg, NULL);
2863 if (!(val && *val != '%'))
2864 rc = 0;
2865 else if (*val == 'Y' || *val == 'y')
2866 rc = 1;
2867 else if (*val == 'N' || *val == 'n')
2868 rc = 0;
2869 else {
2870 char *end;
2871 rc = strtol(val, &end, 0);
2872 if (!(end && *end == '\0'))
2873 rc = 0;
2874 }
2875 val = _free(val);
2876
2877 return rc;
2878 }
2879
2880 /* @todo "../sbin/./../bin/" not correct. */
2881 char *rpmCleanPath(char * path)
2882 {
2883 const char *s;
2884 char *se, *t, *te;
2885 int begin = 1;
2886
2887 if (path == NULL)
2888 return NULL;
2889
2890 /*fprintf(stderr, "*** RCP %s ->\n", path); */
2891 s = t = te = path;
2892 while (*s != '\0') {
2893 /*fprintf(stderr, "*** got \"%.*s\"\trest \"%s\"\n", (t-path), path, s); */
2894 switch(*s) {
2895 case ':': /* handle url's */
2896 if (s[1] == '/' && s[2] == '/') {
2897 *t++ = *s++;
2898 *t++ = *s++;
2899 /* XXX handle "file:///" */
2900 if (s[0] == '/') *t++ = *s++;
2901 te = t;
2902 /*@switchbreak@*/ break;
2903 }
2904 begin=1;
2905 /*@switchbreak@*/ break;
2906 case '/':
2907 /* Move parent dir forward */
2908 for (se = te + 1; se < t && *se != '/'; se++)
2909 {};
2910 if (se < t && *se == '/') {
2911 te = se;
2912 /*fprintf(stderr, "*** next pdir \"%.*s\"\n", (te-path), path); */
2913 }
2914 while (s[1] == '/')
2915 s++;
2916 while (t > te && t[-1] == '/')
2917 t--;
2918 /*@switchbreak@*/ break;
2919 case '.':
2920 /* Leading .. is special */
2921 /* Check that it is ../, so that we don't interpret */
2922 /* ..?(i.e. "...") or ..* (i.e. "..bogus") as "..". */
2923 /* in the case of "...", this ends up being processed*/
2924 /* as "../.", and the last '.' is stripped. This */
2925 /* would not be correct processing. */
2926 if (begin && s[1] == '.' && (s[2] == '/' || s[2] == '\0')) {
2927 /*fprintf(stderr, " leading \"..\"\n"); */
2928 *t++ = *s++;
2929 /*@switchbreak@*/ break;
2930 }
2931 /* Single . is special */
2932 if (begin && s[1] == '\0') {
2933 /*@switchbreak@*/ break;
2934 }
2935 if (t > path && t[-1] == '/')
2936 switch (s[1]) {
2937 case '/': s++; /*@fallthrough@*/ /* Trim embedded ./ */
2938 case '\0': s++; continue; /* Trim trailing /. */
2939 default: /*@innerbreak@*/ break;
2940 }
2941 /* Trim embedded /../ and trailing /.. */
2942 if (!begin && t > path && t[-1] == '/' && s[1] == '.' && (s[2] == '/' || s[2] == '\0')) {
2943 t = te;
2944 /* Move parent dir forward */
2945 if (te > path)
2946 for (--te; te > path && *te != '/'; te--)
2947 {};
2948 /*fprintf(stderr, "*** prev pdir \"%.*s\"\n", (te-path), path); */
2949 s++;
2950 s++;
2951 continue;
2952 }
2953 /*@switchbreak@*/ break;
2954 default:
2955 begin = 0;
2956 /*@switchbreak@*/ break;
2957 }
2958 *t++ = *s++;
2959 }
2960
2961 /* Trim trailing / (but leave single / alone) */
2962 if (t > &path[1] && t[-1] == '/')
2963 t--;
2964 *t = '\0';
2965
2966 /*fprintf(stderr, "\t%s\n", path); */
2967 return path;
2968 }
2969
2970 /* Return concatenated and expanded canonical path. */
2971
2972 const char *
2973 rpmGetPath(const char *path, ...)
2974 {
2975 size_t bufn = _macro_BUFSIZ;
2976 char *buf = alloca(bufn);
2977 const char * s;
2978 char * t, * te;
2979 va_list ap;
2980
2981 if (path == NULL)
2982 return xstrdup("");
2983
2984 buf[0] = '\0';
2985 t = buf;
2986 te = stpcpy(t, path);
2987 *te = '\0';
2988
2989 va_start(ap, path);
2990 while ((s = va_arg(ap, const char *)) != NULL) {
2991 te = stpcpy(te, s);
2992 *te = '\0';
2993 }
2994 va_end(ap);
2995 /*@-modfilesys@*/
2996 (void) expandMacros(NULL, NULL, buf, bufn);
2997 /*@=modfilesys@*/
2998
2999 (void) rpmCleanPath(buf);
3000 return xstrdup(buf); /* XXX xstrdup has side effects. */
3001 }
3002
3003 /* Merge 3 args into path, any or all of which may be a url. */
3004
3005 const char * rpmGenPath(const char * urlroot, const char * urlmdir,
3006 const char *urlfile)
3007 {
3008 /*@owned@*/ const char * xroot = rpmGetPath(urlroot, NULL);
3009 /*@dependent@*/ const char * root = xroot;
3010 /*@owned@*/ const char * xmdir = rpmGetPath(urlmdir, NULL);
3011 /*@dependent@*/ const char * mdir = xmdir;
3012 /*@owned@*/ const char * xfile = rpmGetPath(urlfile, NULL);
3013 /*@dependent@*/ const char * file = xfile;
3014 const char * result;
3015 const char * url = NULL;
3016 int nurl = 0;
3017 int ut;
3018
3019 #if 0
3020 if (_debug) fprintf(stderr, "*** RGP xroot %s xmdir %s xfile %s\n", xroot, xmdir, xfile);
3021 #endif
3022 ut = urlPath(xroot, &root);
3023 if (url == NULL && ut > URL_IS_DASH) {
3024 url = xroot;
3025 nurl = root - xroot;
3026 #if 0
3027 if (_debug) fprintf(stderr, "*** RGP ut %d root %s nurl %d\n", ut, root, nurl);
3028 #endif
3029 }
3030 if (root == NULL || *root == '\0') root = "/";
3031
3032 ut = urlPath(xmdir, &mdir);
3033 if (url == NULL && ut > URL_IS_DASH) {
3034 url = xmdir;
3035 nurl = mdir - xmdir;
3036 #if 0
3037 if (_debug) fprintf(stderr, "*** RGP ut %d mdir %s nurl %d\n", ut, mdir, nurl);
3038 #endif
3039 }
3040 if (mdir == NULL || *mdir == '\0') mdir = "/";
3041
3042 ut = urlPath(xfile, &file);
3043 if (url == NULL && ut > URL_IS_DASH) {
3044 url = xfile;
3045 nurl = file - xfile;
3046 #if 0
3047 if (_debug) fprintf(stderr, "*** RGP ut %d file %s nurl %d\n", ut, file, nurl);
3048 #endif
3049 }
3050
3051 if (url && nurl > 0) {
3052 char *t = strncpy(alloca(nurl+1), url, nurl);
3053 t[nurl] = '\0';
3054 url = t;
3055 } else
3056 url = "";
3057
3058 result = rpmGetPath(url, root, "/", mdir, "/", file, NULL);
3059
3060 xroot = _free(xroot);
3061 xmdir = _free(xmdir);
3062 xfile = _free(xfile);
3063 #if 0
3064 if (_debug) fprintf(stderr, "*** RGP result %s\n", result);
3065 #endif
3066 return result;
3067 }
3068
3069 /* =============================================================== */
3070
3071 #if defined(DEBUG_MACROS)
3072
3073 #if defined(EVAL_MACROS)
3074
3075 const char *rpmMacrofiles = MACROFILES;
3076
3077 int
3078 main(int argc, char *argv[])
3079 {
3080 int c;
3081 int errflg = 0;
3082 extern char *optarg;
3083 extern int optind;
3084
3085 while ((c = getopt(argc, argv, "f:")) != EOF ) {
3086 switch (c) {
3087 case 'f':
3088 rpmMacrofiles = optarg;
3089 break;
3090 case '?':
3091 default:
3092 errflg++;
3093 break;
3094 }
3095 }
3096 if (errflg || optind >= argc) {
3097 fprintf(stderr, "Usage: %s [-f macropath ] macro ...\n", argv[0]);
3098 exit(1);
3099 }
3100
3101 rpmInitMacros(NULL, rpmMacrofiles);
3102 /* XXX getopt(3) also used for parametrized macros, expect scwewiness. */
3103 for ( ; optind < argc; optind++) {
3104 const char *val;
3105
3106 val = rpmExpand(argv[optind], NULL);
3107 if (val) {
3108 fprintf(stdout, "%s:\t%s\n", argv[optind], val);
3109 val = _free(val);
3110 }
3111 }
3112 rpmFreeMacros(NULL);
3113 return 0;
3114 }
3115
3116 #else /* !EVAL_MACROS */
3117
3118 const char *rpmMacrofiles = "../macros:./testmacros";
3119 const char *testfile = "./test";
3120
3121 int
3122 main(int argc, char *argv[])
3123 {
3124 size_t bufn = _macro_BUFSIZ;
3125 char *buf = alloca(bufn);
3126 FILE *fp;
3127 int x;
3128
3129 rpmInitMacros(NULL, rpmMacrofiles);
3130
3131 if ((fp = fopen(testfile, "r")) != NULL) {
3132 while(rdcl(buf, bufn, fp)) {
3133 x = expandMacros(NULL, NULL, buf, bufn);
3134 fprintf(stderr, "%d->%s\n", x, buf);
3135 memset(buf, 0, bufn);
3136 }
3137 fclose(fp);
3138 }
3139
3140 while(rdcl(buf, bufn, stdin)) {
3141 x = expandMacros(NULL, NULL, buf, bufn);
3142 fprintf(stderr, "%d->%s\n <-\n", x, buf);
3143 memset(buf, 0, bufn);
3144 }
3145 rpmFreeMacros(NULL);
3146
3147 return 0;
3148 }
3149 #endif /* EVAL_MACROS */
3150 #endif /* DEBUG_MACROS */