rpm  4.5
macro.c
Go to the documentation of this file.
1 /*@-boundsread@*/
6 #include "system.h"
7 #include <stdarg.h>
8 
9 #if !defined(isblank)
10 #define isblank(_c) ((_c) == ' ' || (_c) == '\t')
11 #endif
12 #define iseol(_c) ((_c) == '\n' || (_c) == '\r')
13 
14 #define STREQ(_t, _f, _fn) ((_fn) == (sizeof(_t)-1) && !strncmp((_t), (_f), (_fn)))
15 
16 #ifdef DEBUG_MACROS
17 #undef WITH_LUA /* XXX fixme */
18 #include <sys/types.h>
19 #include <errno.h>
20 #include <fcntl.h>
21 #include <getopt.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <strings.h>
26 #include <ctype.h>
27 #define rpmError fprintf
28 #define rpmIsVerbose() (0)
29 #define RPMERR_BADSPEC 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 
41 typedef FILE * FD_t;
42 #define Fopen(_path, _fmode) fopen(_path, "r");
43 #define Ferror ferror
44 #define Fstrerror(_fd) strerror(errno)
45 #define Fread fread
46 #define Fclose fclose
47 
48 #define fdGetFILE(_fd) (_fd)
49 
50 /*@unused@*/ static inline /*@null@*/ void *
51 _free(/*@only@*/ /*@null@*/ const void * p)
52  /*@modifies p@*/
53 {
54  if (p != NULL) free((void *)p);
55  return NULL;
56 }
57 
58 #else
59 
60 /*@observer@*/ /*@checked@*/
61 const char * rpmMacrofiles = MACROFILES;
62 
63 #include <rpmio_internal.h>
64 #include <rpmmessages.h>
65 #include <rpmerr.h>
66 
67 #ifdef WITH_LUA
68 #include <rpmlua.h>
69 #endif
70 
71 #endif
72 
73 #include <rpmmacro.h>
74 
75 #include "debug.h"
76 
77 #if defined(__LCLINT__)
78 /*@-exportheader@*/
79 extern const unsigned short int **__ctype_b_loc (void) /*@*/;
80 /*@=exportheader@*/
81 #endif
82 
83 /*@access FD_t@*/ /* XXX compared with NULL */
84 /*@access MacroContext@*/
85 /*@access MacroEntry@*/
86 /*@access rpmlua @*/
87 
89 /*@-compmempass@*/
91 /*@=compmempass@*/
92 
94 /*@-compmempass@*/
96 /*@=compmempass@*/
97 
101 typedef /*@abstract@*/ struct MacroBuf_s {
102 /*@kept@*/ /*@exposed@*/
103  const char * s;
104 /*@shared@*/
105  char * t;
106  size_t nb;
107  int depth;
110 /*@kept@*/ /*@exposed@*/ /*@null@*/
111  void * spec;
112 /*@kept@*/ /*@exposed@*/
114 } * MacroBuf;
115 
116 #define SAVECHAR(_mb, _c) { *(_mb)->t = (_c), (_mb)->t++, (_mb)->nb--; }
117 
118 /*@-exportlocal -exportheadervar@*/
119 
120 #define _MAX_MACRO_DEPTH 16
121 /*@unchecked@*/
123 
124 #define _PRINT_MACRO_TRACE 0
125 /*@unchecked@*/
127 
128 #define _PRINT_EXPAND_TRACE 0
129 /*@unchecked@*/
131 /*@=exportlocal =exportheadervar@*/
132 
133 #define MACRO_CHUNK_SIZE 16
134 
135 /* Size of expansion buffers. */
136 static size_t _macro_BUFSIZ = 4 * BUFSIZ;
137 
138 /* forward ref */
139 static int expandMacro(MacroBuf mb)
140  /*@globals rpmGlobalMacroContext,
141  print_macro_trace, print_expand_trace, h_errno, fileSystem @*/
142  /*@modifies mb, rpmGlobalMacroContext,
143  print_macro_trace, print_expand_trace, fileSystem @*/;
144 
145 /* =============================================================== */
146 
153 static int
154 compareMacroName(const void * ap, const void * bp)
155  /*@*/
156 {
157  MacroEntry ame = *((MacroEntry *)ap);
158  MacroEntry bme = *((MacroEntry *)bp);
159 
160  if (ame == NULL && bme == NULL)
161  return 0;
162  if (ame == NULL)
163  return 1;
164  if (bme == NULL)
165  return -1;
166  return strcmp(ame->name, bme->name);
167 }
168 
173 /*@-boundswrite@*/
174 static void
176  /*@modifies mc @*/
177 {
178  if (mc->macroTable == NULL) {
180  mc->macroTable = (MacroEntry *)
181  xmalloc(sizeof(*(mc->macroTable)) * mc->macrosAllocated);
182  mc->firstFree = 0;
183  } else {
185  mc->macroTable = (MacroEntry *)
186  xrealloc(mc->macroTable, sizeof(*(mc->macroTable)) *
187  mc->macrosAllocated);
188  }
189  memset(&mc->macroTable[mc->firstFree], 0, MACRO_CHUNK_SIZE * sizeof(*(mc->macroTable)));
190 }
191 /*@=boundswrite@*/
192 
197 static void
199  /*@modifies mc @*/
200 {
201  int i;
202 
203  if (mc == NULL || mc->macroTable == NULL)
204  return;
205 
206  qsort(mc->macroTable, mc->firstFree, sizeof(*(mc->macroTable)),
208 
209  /* Empty pointers are now at end of table. Reset first free index. */
210  for (i = 0; i < mc->firstFree; i++) {
211  if (mc->macroTable[i] != NULL)
212  continue;
213  mc->firstFree = i;
214  break;
215  }
216 }
217 
218 void
220 {
221  int nempty = 0;
222  int nactive = 0;
223 
224  if (mc == NULL) mc = rpmGlobalMacroContext;
225  if (fp == NULL) fp = stderr;
226 
227  fprintf(fp, "========================\n");
228  if (mc->macroTable != NULL) {
229  int i;
230  for (i = 0; i < mc->firstFree; i++) {
231  MacroEntry me;
232  if ((me = mc->macroTable[i]) == NULL) {
233  /* XXX this should never happen */
234  nempty++;
235  continue;
236  }
237  fprintf(fp, "%3d%c %s", me->level,
238  (me->used > 0 ? '=' : ':'), me->name);
239  if (me->opts && *me->opts)
240  fprintf(fp, "(%s)", me->opts);
241  if (me->body && *me->body)
242  fprintf(fp, "\t%s", me->body);
243  fprintf(fp, "\n");
244  nactive++;
245  }
246  }
247  fprintf(fp, _("======================== active %d empty %d\n"),
248  nactive, nempty);
249 }
250 
258 /*@-boundswrite@*/
259 /*@dependent@*/ /*@null@*/
260 static MacroEntry *
261 findEntry(MacroContext mc, const char * name, size_t namelen)
262  /*@*/
263 {
264  MacroEntry key, *ret;
265 
266 /*@-globs@*/
267  if (mc == NULL) mc = rpmGlobalMacroContext;
268 /*@=globs@*/
269  if (mc->macroTable == NULL || mc->firstFree == 0)
270  return NULL;
271 
272 /*@-branchstate@*/
273  if (namelen > 0) {
274  char * t = strncpy(alloca(namelen + 1), name, namelen);
275  t[namelen] = '\0';
276  name = t;
277  }
278 /*@=branchstate@*/
279 
280  key = memset(alloca(sizeof(*key)), 0, sizeof(*key));
281  /*@-temptrans -assignexpose@*/
282  key->name = (char *)name;
283  /*@=temptrans =assignexpose@*/
284  ret = (MacroEntry *) bsearch(&key, mc->macroTable, mc->firstFree,
285  sizeof(*(mc->macroTable)), compareMacroName);
286  /* XXX TODO: find 1st empty slot and return that */
287  return ret;
288 }
289 /*@=boundswrite@*/
290 
291 /* =============================================================== */
292 
300 /*@-boundswrite@*/
301 /*@null@*/
302 static char *
303 rdcl(/*@returned@*/ char * buf, size_t size, FD_t fd)
304  /*@globals fileSystem @*/
305  /*@modifies buf, fileSystem @*/
306 {
307  char *q = buf - 1; /* initialize just before buffer. */
308  size_t nb = 0;
309  size_t nread = 0;
310  FILE * f = fdGetFILE(fd);
311  int pc = 0, bc = 0;
312  char *p = buf;
313 
314  if (f != NULL)
315  do {
316  *(++q) = '\0'; /* terminate and move forward. */
317  if (fgets(q, size, f) == NULL) /* read next line. */
318  break;
319  nb = strlen(q);
320  nread += nb; /* trim trailing \r and \n */
321  for (q += nb - 1; nb > 0 && iseol(*q); q--)
322  nb--;
323  for (; p <= q; p++) {
324  switch (*p) {
325  case '\\':
326  switch (*(p+1)) {
327  case '\0': /*@switchbreak@*/ break;
328  default: p++; /*@switchbreak@*/ break;
329  }
330  /*@switchbreak@*/ break;
331  case '%':
332  switch (*(p+1)) {
333  case '{': p++, bc++; /*@switchbreak@*/ break;
334  case '(': p++, pc++; /*@switchbreak@*/ break;
335  case '%': p++; /*@switchbreak@*/ break;
336  }
337  /*@switchbreak@*/ break;
338  case '{': if (bc > 0) bc++; /*@switchbreak@*/ break;
339  case '}': if (bc > 0) bc--; /*@switchbreak@*/ break;
340  case '(': if (pc > 0) pc++; /*@switchbreak@*/ break;
341  case ')': if (pc > 0) pc--; /*@switchbreak@*/ break;
342  }
343  }
344  if (nb == 0 || (*q != '\\' && !bc && !pc) || *(q+1) == '\0') {
345  *(++q) = '\0'; /* trim trailing \r, \n */
346  break;
347  }
348  q++; p++; nb++; /* copy newline too */
349  size -= nb;
350  if (*q == '\r') /* XXX avoid \r madness */
351  *q = '\n';
352  } while (size > 0);
353  return (nread > 0 ? buf : NULL);
354 }
355 /*@=boundswrite@*/
356 
364 /*@null@*/
365 static const char *
366 matchchar(const char * p, char pl, char pr)
367  /*@*/
368 {
369  int lvl = 0;
370  char c;
371 
372  while ((c = *p++) != '\0') {
373  if (c == '\\') { /* Ignore escaped chars */
374  p++;
375  continue;
376  }
377  if (c == pr) {
378  if (--lvl <= 0) return --p;
379  } else if (c == pl)
380  lvl++;
381  }
382  return (const char *)NULL;
383 }
384 
391 static void
392 printMacro(MacroBuf mb, const char * s, const char * se)
393  /*@globals fileSystem @*/
394  /*@modifies fileSystem @*/
395 {
396  const char *senl;
397  const char *ellipsis;
398  int choplen;
399 
400  if (s >= se) { /* XXX just in case */
401  fprintf(stderr, _("%3d>%*s(empty)"), mb->depth,
402  (2 * mb->depth + 1), "");
403  return;
404  }
405 
406  if (s[-1] == '{')
407  s--;
408 
409  /* Print only to first end-of-line (or end-of-string). */
410  for (senl = se; *senl && !iseol(*senl); senl++)
411  {};
412 
413  /* Limit trailing non-trace output */
414  choplen = 61 - (2 * mb->depth);
415  if ((senl - s) > choplen) {
416  senl = s + choplen;
417  ellipsis = "...";
418  } else
419  ellipsis = "";
420 
421  /* Substitute caret at end-of-macro position */
422  fprintf(stderr, "%3d>%*s%%%.*s^", mb->depth,
423  (2 * mb->depth + 1), "", (int)(se - s), s);
424  if (se[1] != '\0' && (senl - (se+1)) > 0)
425  fprintf(stderr, "%-.*s%s", (int)(senl - (se+1)), se+1, ellipsis);
426  fprintf(stderr, "\n");
427 }
428 
435 static void
436 printExpansion(MacroBuf mb, const char * t, const char * te)
437  /*@globals fileSystem @*/
438  /*@modifies fileSystem @*/
439 {
440  const char *ellipsis;
441  int choplen;
442 
443  if (!(te > t)) {
444  fprintf(stderr, _("%3d<%*s(empty)\n"), mb->depth, (2 * mb->depth + 1), "");
445  return;
446  }
447 
448  /* Shorten output which contains newlines */
449  while (te > t && iseol(te[-1]))
450  te--;
451  ellipsis = "";
452  if (mb->depth > 0) {
453  const char *tenl;
454 
455  /* Skip to last line of expansion */
456  while ((tenl = strchr(t, '\n')) && tenl < te)
457  t = ++tenl;
458 
459  /* Limit expand output */
460  choplen = 61 - (2 * mb->depth);
461  if ((te - t) > choplen) {
462  te = t + choplen;
463  ellipsis = "...";
464  }
465  }
466 
467  fprintf(stderr, "%3d<%*s", mb->depth, (2 * mb->depth + 1), "");
468  if (te > t)
469  fprintf(stderr, "%.*s%s", (int)(te - t), t, ellipsis);
470  fprintf(stderr, "\n");
471 }
472 
473 #define SKIPBLANK(_s, _c) \
474  /*@-globs@*/ /* FIX: __ctype_b */ \
475  while (((_c) = *(_s)) && isblank(_c)) \
476  (_s)++; \
477  /*@=globs@*/
478 
479 #define SKIPNONBLANK(_s, _c) \
480  /*@-globs@*/ /* FIX: __ctype_b */ \
481  while (((_c) = *(_s)) && !(isblank(_c) || iseol(_c))) \
482  (_s)++; \
483  /*@=globs@*/
484 
485 #define COPYNAME(_ne, _s, _c) \
486  { SKIPBLANK(_s,_c); \
487  /*@-boundswrite@*/ \
488  while(((_c) = *(_s)) && (xisalnum(_c) || (_c) == '_')) \
489  *(_ne)++ = *(_s)++; \
490  *(_ne) = '\0'; \
491  /*@=boundswrite@*/ \
492  }
493 
494 #define COPYOPTS(_oe, _s, _c) \
495  { /*@-boundswrite@*/ \
496  while(((_c) = *(_s)) && (_c) != ')') \
497  *(_oe)++ = *(_s)++; \
498  *(_oe) = '\0'; \
499  /*@=boundswrite@*/ \
500  }
501 
509 static int
510 expandT(MacroBuf mb, const char * f, size_t flen)
511  /*@globals rpmGlobalMacroContext, h_errno, fileSystem@*/
512  /*@modifies mb, rpmGlobalMacroContext, fileSystem @*/
513 {
514  char *sbuf;
515  const char *s = mb->s;
516  int rc;
517 
518  sbuf = alloca(flen + 1);
519  memset(sbuf, 0, (flen + 1));
520 
521  strncpy(sbuf, f, flen);
522  sbuf[flen] = '\0';
523  mb->s = sbuf;
524  rc = expandMacro(mb);
525  mb->s = s;
526  return rc;
527 }
528 
529 #if 0
530 
537 static int
538 expandS(MacroBuf mb, char * tbuf, size_t tbuflen)
539  /*@globals rpmGlobalMacroContext, fileSystem@*/
540  /*@modifies mb, *tbuf, rpmGlobalMacroContext, fileSystem @*/
541 {
542  const char *t = mb->t;
543  size_t nb = mb->nb;
544  int rc;
545 
546  mb->t = tbuf;
547  mb->nb = tbuflen;
548  rc = expandMacro(mb);
549  mb->t = t;
550  mb->nb = nb;
551  return rc;
552 }
553 #endif
554 
562 /*@-boundswrite@*/
563 static int
564 expandU(MacroBuf mb, char * u, size_t ulen)
565  /*@globals rpmGlobalMacroContext, h_errno, fileSystem@*/
566  /*@modifies mb, *u, rpmGlobalMacroContext, fileSystem @*/
567 {
568  const char *s = mb->s;
569  char *t = mb->t;
570  size_t nb = mb->nb;
571  char *tbuf;
572  int rc;
573 
574  tbuf = alloca(ulen + 1);
575  memset(tbuf, 0, (ulen + 1));
576 
577  mb->s = u;
578  mb->t = tbuf;
579  mb->nb = ulen;
580  rc = expandMacro(mb);
581 
582  tbuf[ulen] = '\0'; /* XXX just in case */
583  if (ulen > mb->nb)
584  strncpy(u, tbuf, (ulen - mb->nb + 1));
585 
586  mb->s = s;
587  mb->t = t;
588  mb->nb = nb;
589 
590  return rc;
591 }
592 /*@=boundswrite@*/
593 
601 /*@-boundswrite@*/
602 static int
603 doShellEscape(MacroBuf mb, const char * cmd, size_t clen)
604  /*@globals rpmGlobalMacroContext, h_errno, fileSystem @*/
605  /*@modifies mb, rpmGlobalMacroContext, fileSystem @*/
606 {
607  size_t bufn = _macro_BUFSIZ + clen;
608  char * buf = alloca(bufn);
609  FILE *shf;
610  int rc;
611  int c;
612 
613  strncpy(buf, cmd, clen);
614  buf[clen] = '\0';
615  rc = expandU(mb, buf, bufn);
616  if (rc)
617  return rc;
618 
619  if ((shf = popen(buf, "r")) == NULL)
620  return 1;
621  while(mb->nb > 0 && (c = fgetc(shf)) != EOF)
622  SAVECHAR(mb, c);
623  (void) pclose(shf);
624 
625  /* XXX delete trailing \r \n */
626  while (iseol(mb->t[-1])) {
627  *(mb->t--) = '\0';
628  mb->nb++;
629  }
630  return 0;
631 }
632 /*@=boundswrite@*/
633 
642 /*@dependent@*/ static const char *
643 doDefine(MacroBuf mb, /*@returned@*/ const char * se, int level, int expandbody)
644  /*@globals rpmGlobalMacroContext, h_errno @*/
645  /*@modifies mb, rpmGlobalMacroContext @*/
646 {
647  const char *s = se;
648  size_t bufn = _macro_BUFSIZ;
649  char *buf = alloca(bufn);
650  char *n = buf, *ne;
651  char *o = NULL, *oe;
652  char *b, *be;
653  int c;
654  int oc = ')';
655 
656  SKIPBLANK(s, c);
657  if (c == '.') /* XXX readonly macros */
658  *n++ = c = *s++;
659  if (c == '.') /* XXX readonly macros */
660  *n++ = c = *s++;
661  ne = n;
662 
663  /* Copy name */
664  COPYNAME(ne, s, c);
665 
666  /* Copy opts (if present) */
667  oe = ne + 1;
668  if (*s == '(') {
669  s++; /* skip ( */
670  o = oe;
671  COPYOPTS(oe, s, oc);
672  s++; /* skip ) */
673  }
674 
675  /* Copy body, skipping over escaped newlines */
676  b = be = oe + 1;
677  SKIPBLANK(s, c);
678  if (c == '{') { /* XXX permit silent {...} grouping */
679  if ((se = matchchar(s, c, '}')) == NULL) {
681  _("Macro %%%s has unterminated body\n"), n);
682  se = s; /* XXX W2DO? */
683  return se;
684  }
685  s++; /* XXX skip { */
686 /*@-boundswrite@*/
687  strncpy(b, s, (se - s));
688  b[se - s] = '\0';
689 /*@=boundswrite@*/
690  be += strlen(b);
691  se++; /* XXX skip } */
692  s = se; /* move scan forward */
693  } else { /* otherwise free-field */
694 /*@-boundswrite@*/
695  int bc = 0, pc = 0;
696  while (*s && (bc || pc || !iseol(*s))) {
697  switch (*s) {
698  case '\\':
699  switch (*(s+1)) {
700  case '\0': /*@switchbreak@*/ break;
701  default: s++; /*@switchbreak@*/ break;
702  }
703  /*@switchbreak@*/ break;
704  case '%':
705  switch (*(s+1)) {
706  case '{': *be++ = *s++; bc++; /*@switchbreak@*/ break;
707  case '(': *be++ = *s++; pc++; /*@switchbreak@*/ break;
708  case '%': *be++ = *s++; /*@switchbreak@*/ break;
709  }
710  /*@switchbreak@*/ break;
711  case '{': if (bc > 0) bc++; /*@switchbreak@*/ break;
712  case '}': if (bc > 0) bc--; /*@switchbreak@*/ break;
713  case '(': if (pc > 0) pc++; /*@switchbreak@*/ break;
714  case ')': if (pc > 0) pc--; /*@switchbreak@*/ break;
715  }
716  *be++ = *s++;
717  }
718  *be = '\0';
719 
720  if (bc || pc) {
722  _("Macro %%%s has unterminated body\n"), n);
723  se = s; /* XXX W2DO? */
724  return se;
725  }
726 
727  /* Trim trailing blanks/newlines */
728 /*@-globs@*/
729  while (--be >= b && (c = *be) && (isblank(c) || iseol(c)))
730  {};
731 /*@=globs@*/
732  *(++be) = '\0'; /* one too far */
733 /*@=boundswrite@*/
734  }
735 
736  /* Move scan over body */
737  while (iseol(*s))
738  s++;
739  se = s;
740 
741  /* Names must start with alphabetic or _ and be at least 3 chars */
742  if (!((c = *n) && (xisalpha(c) || c == '_') && (ne - n) > 2)) {
744  _("Macro %%%s has illegal name (%%define)\n"), n);
745  return se;
746  }
747 
748  /* Options must be terminated with ')' */
749  if (o && oc != ')') {
750  rpmError(RPMERR_BADSPEC, _("Macro %%%s has unterminated opts\n"), n);
751  return se;
752  }
753 
754  if ((be - b) < 1) {
755  rpmError(RPMERR_BADSPEC, _("Macro %%%s has empty body\n"), n);
756  return se;
757  }
758 
759 /*@-modfilesys@*/
760  if (expandbody && expandU(mb, b, (&buf[bufn] - b))) {
761  rpmError(RPMERR_BADSPEC, _("Macro %%%s failed to expand\n"), n);
762  return se;
763  }
764 /*@=modfilesys@*/
765 
766  if (n != buf) /* XXX readonly macros */
767  n--;
768  if (n != buf) /* XXX readonly macros */
769  n--;
770  addMacro(mb->mc, n, o, b, (level - 1));
771 
772  return se;
773 }
774 
781 /*@dependent@*/ static const char *
782 doUndefine(MacroContext mc, /*@returned@*/ const char * se)
783  /*@globals rpmGlobalMacroContext @*/
784  /*@modifies mc, rpmGlobalMacroContext @*/
785 {
786  const char *s = se;
787  char *buf = alloca(_macro_BUFSIZ);
788  char *n = buf, *ne = n;
789  int c;
790 
791  COPYNAME(ne, s, c);
792 
793  /* Move scan over body */
794  while (iseol(*s))
795  s++;
796  se = s;
797 
798  /* Names must start with alphabetic or _ and be at least 3 chars */
799  if (!((c = *n) && (xisalpha(c) || c == '_') && (ne - n) > 2)) {
801  _("Macro %%%s has illegal name (%%undefine)\n"), n);
802  return se;
803  }
804 
805  delMacro(mc, n);
806 
807  return se;
808 }
809 
810 void delMacroAll(MacroContext mc, const char * n);
817 /*@dependent@*/ static const char *
818 doUnglobal(MacroContext mc, /*@returned@*/ const char * se)
819  /*@globals rpmGlobalMacroContext @*/
820  /*@modifies mc, rpmGlobalMacroContext @*/
821 {
822  const char *s = se;
823  char *buf = alloca(_macro_BUFSIZ);
824  char *n = buf, *ne = n;
825  int c;
826 
827  COPYNAME(ne, s, c);
828 
829  /* Move scan over body */
830  while (iseol(*s))
831  s++;
832  se = s;
833 
834  /* Names must start with alphabetic or _ and be at least 3 chars */
835  if (!((c = *n) && (xisalpha(c) || c == '_') && (ne - n) > 2)) {
837  _("Macro %%%s has illegal name (%%unglobal)\n"), n);
838  return se;
839  }
840 
841  delMacroAll(mc, n);
842 
843  return se;
844 }
845 
846 #ifdef DYING
847 static void
848 dumpME(const char * msg, MacroEntry me)
849  /*@globals fileSystem @*/
850  /*@modifies fileSystem @*/
851 {
852  if (msg)
853  fprintf(stderr, "%s", msg);
854  fprintf(stderr, "\tme %p", me);
855  if (me)
856  fprintf(stderr,"\tname %p(%s) prev %p",
857  me->name, me->name, me->prev);
858  fprintf(stderr, "\n");
859 }
860 #endif
861 
870 static void
871 pushMacro(/*@out@*/ MacroEntry * mep, const char * n, /*@null@*/ const char * o,
872  /*@null@*/ const char * b, int level)
873  /*@modifies *mep @*/
874 {
875  MacroEntry prev = (mep && *mep ? *mep : NULL);
876  MacroEntry me = (MacroEntry) xmalloc(sizeof(*me));
877  const char *name = n;
878 
879  if (*name == '.') /* XXX readonly macros */
880  name++;
881  if (*name == '.') /* XXX readonly macros */
882  name++;
883 
884  /*@-assignexpose@*/
885  me->prev = prev;
886  /*@=assignexpose@*/
887  me->name = (prev ? prev->name : xstrdup(name));
888  me->opts = (o ? xstrdup(o) : NULL);
889  me->body = xstrdup(b ? b : "");
890  me->used = 0;
891  me->level = level;
892  me->flags = (name != n);
893 /*@-boundswrite@*/
894 /*@-branchstate@*/
895  if (mep)
896  *mep = me;
897  else
898  me = _free(me);
899 /*@=branchstate@*/
900 /*@=boundswrite@*/
901 }
902 
907 static void
909  /*@modifies *mep @*/
910 {
911  MacroEntry me = (*mep ? *mep : NULL);
912 
913 /*@-branchstate@*/
914  if (me) {
915  /* XXX cast to workaround const */
916  /*@-onlytrans@*/
917 /*@-boundswrite@*/
918  if ((*mep = me->prev) == NULL)
919  me->name = _free(me->name);
920 /*@=boundswrite@*/
921  me->opts = _free(me->opts);
922  me->body = _free(me->body);
923  me = _free(me);
924  /*@=onlytrans@*/
925  }
926 /*@=branchstate@*/
927 }
928 
933 static void
935  /*@modifies mb @*/
936 {
937  MacroContext mc = mb->mc;
938  int ndeleted = 0;
939  int i;
940 
941  if (mc == NULL || mc->macroTable == NULL)
942  return;
943 
944  /* Delete dynamic macro definitions */
945  for (i = 0; i < mc->firstFree; i++) {
946  MacroEntry *mep, me;
947  int skiptest = 0;
948  mep = &mc->macroTable[i];
949  me = *mep;
950 
951  if (me == NULL) /* XXX this should never happen */
952  continue;
953  if (me->level < mb->depth)
954  continue;
955  if (strlen(me->name) == 1 && strchr("#*0", *me->name)) {
956  if (*me->name == '*' && me->used > 0)
957  skiptest = 1; /* XXX skip test for %# %* %0 */
958  } else if (!skiptest && me->used <= 0) {
959 #if NOTYET
961  _("Macro %%%s (%s) was not used below level %d\n"),
962  me->name, me->body, me->level);
963 #endif
964  }
965  popMacro(mep);
966  if (!(mep && *mep))
967  ndeleted++;
968  }
969 
970  /* If any deleted macros, sort macro table */
971  if (ndeleted)
972  sortMacroTable(mc);
973 }
974 
984 /*@-bounds@*/
985 /*@dependent@*/ static const char *
986 grabArgs(MacroBuf mb, const MacroEntry me, /*@returned@*/ const char * se,
987  const char * lastc)
988  /*@globals rpmGlobalMacroContext @*/
989  /*@modifies mb, rpmGlobalMacroContext @*/
990 {
991  size_t bufn = _macro_BUFSIZ;
992  char *buf = alloca(bufn);
993  char *b, *be;
994  char aname[16];
995  const char *opts, *o;
996  int argc = 0;
997  const char **argv;
998  int c;
999 
1000  /* Copy macro name as argv[0], save beginning of args. */
1001  buf[0] = '\0';
1002  b = be = stpcpy(buf, me->name);
1003 
1004  addMacro(mb->mc, "0", NULL, buf, mb->depth);
1005 
1006  argc = 1; /* XXX count argv[0] */
1007 
1008  /* Copy args into buf until lastc */
1009  *be++ = ' ';
1010  while ((c = *se++) != '\0' && (se-1) != lastc) {
1011 /*@-globs@*/
1012  if (!isblank(c)) {
1013  *be++ = c;
1014  continue;
1015  }
1016 /*@=globs@*/
1017  /* c is blank */
1018  if (be[-1] == ' ')
1019  continue;
1020  /* a word has ended */
1021  *be++ = ' ';
1022  argc++;
1023  }
1024  if (c == '\0') se--; /* one too far */
1025  if (be[-1] != ' ')
1026  argc++, be++; /* last word has not trailing ' ' */
1027  be[-1] = '\0';
1028  if (*b == ' ') b++; /* skip the leading ' ' */
1029 
1030 /*
1031  * The macro %* analoguous to the shell's $* means "Pass all non-macro
1032  * parameters." Consequently, there needs to be a macro that means "Pass all
1033  * (including macro parameters) options". This is useful for verifying
1034  * parameters during expansion and yet transparently passing all parameters
1035  * through for higher level processing (e.g. %description and/or %setup).
1036  * This is the (potential) justification for %{**} ...
1037  */
1038  /* Add unexpanded args as macro */
1039  addMacro(mb->mc, "**", NULL, b, mb->depth);
1040 
1041 #ifdef NOTYET
1042  /* XXX if macros can be passed as args ... */
1043  expandU(mb, buf, bufn);
1044 #endif
1045 
1046  /* Build argv array */
1047  argv = (const char **) alloca((argc + 1) * sizeof(*argv));
1048  be[-1] = ' '; /* assert((be - 1) == (b + strlen(b) == buf + strlen(buf))) */
1049  be[0] = '\0';
1050  b = buf;
1051  for (c = 0; c < argc; c++) {
1052  argv[c] = b;
1053  b = strchr(b, ' ');
1054  *b++ = '\0';
1055  }
1056  /* assert(b == be); */
1057  argv[argc] = NULL;
1058 
1059  /* Citation from glibc/posix/getopt.c:
1060  * Index in ARGV of the next element to be scanned.
1061  * This is used for communication to and from the caller
1062  * and for communication between successive calls to `getopt'.
1063  *
1064  * On entry to `getopt', zero means this is the first call; initialize.
1065  *
1066  * When `getopt' returns -1, this is the index of the first of the
1067  * non-option elements that the caller should itself scan.
1068  *
1069  * Otherwise, `optind' communicates from one call to the next
1070  * how much of ARGV has been scanned so far.
1071  */
1072  /* 1003.2 says this must be 1 before any call. */
1073 
1074 #ifdef __GLIBC__
1075  /*@-mods@*/
1076  optind = 0; /* XXX but posix != glibc */
1077  /*@=mods@*/
1078 #else
1079  optind = 1;
1080 #endif
1081 
1082  opts = me->opts;
1083 
1084  /* Define option macros. */
1085 /*@-nullstate@*/ /* FIX: argv[] can be NULL */
1086  while((c = getopt(argc, (char **)argv, opts)) != -1)
1087 /*@=nullstate@*/
1088  {
1089  if (c == '?' || (o = strchr(opts, c)) == NULL) {
1090  rpmError(RPMERR_BADSPEC, _("Unknown option %c in %s(%s)\n"),
1091  (char)c, me->name, opts);
1092  return se;
1093  }
1094  *be++ = '-';
1095  *be++ = c;
1096  if (o[1] == ':') {
1097  *be++ = ' ';
1098  be = stpcpy(be, optarg);
1099  }
1100  *be++ = '\0';
1101  aname[0] = '-'; aname[1] = c; aname[2] = '\0';
1102  addMacro(mb->mc, aname, NULL, b, mb->depth);
1103  if (o[1] == ':') {
1104  aname[0] = '-'; aname[1] = c; aname[2] = '*'; aname[3] = '\0';
1105  addMacro(mb->mc, aname, NULL, optarg, mb->depth);
1106  }
1107  be = b; /* reuse the space */
1108  }
1109 
1110  /* Add arg count as macro. */
1111  sprintf(aname, "%d", (argc - optind));
1112  addMacro(mb->mc, "#", NULL, aname, mb->depth);
1113 
1114  /* Add macro for each arg. Concatenate args for %*. */
1115  if (be) {
1116  *be = '\0';
1117  for (c = optind; c < argc; c++) {
1118  sprintf(aname, "%d", (c - optind + 1));
1119  addMacro(mb->mc, aname, NULL, argv[c], mb->depth);
1120  if (be != b) *be++ = ' '; /* Add space between args */
1121 /*@-nullpass@*/ /* FIX: argv[] can be NULL */
1122  be = stpcpy(be, argv[c]);
1123 /*@=nullpass@*/
1124  }
1125  }
1126 
1127  /* Add unexpanded args as macro. */
1128  addMacro(mb->mc, "*", NULL, b, mb->depth);
1129 
1130  return se;
1131 }
1132 /*@=bounds@*/
1133 
1141 static void
1142 doOutput(MacroBuf mb, int waserror, const char * msg, size_t msglen)
1143  /*@globals rpmGlobalMacroContext, h_errno, fileSystem @*/
1144  /*@modifies mb, rpmGlobalMacroContext, fileSystem @*/
1145 {
1146  size_t bufn = _macro_BUFSIZ + msglen;
1147  char *buf = alloca(bufn);
1148 
1149  strncpy(buf, msg, msglen);
1150  buf[msglen] = '\0';
1151  (void) expandU(mb, buf, bufn);
1152  if (waserror)
1153  rpmError(RPMERR_BADSPEC, "%s\n", buf);
1154  else
1155  fprintf(stderr, "%s", buf);
1156 }
1157 
1167 static void
1168 doFoo(MacroBuf mb, int negate, const char * f, size_t fn,
1169  /*@null@*/ const char * g, size_t gn)
1170  /*@globals rpmGlobalMacroContext, h_errno, fileSystem, internalState @*/
1171  /*@modifies mb, rpmGlobalMacroContext, fileSystem, internalState @*/
1172 {
1173  size_t bufn = _macro_BUFSIZ + fn + gn;
1174  char * buf = alloca(bufn);
1175  char *b = NULL, *be;
1176  int c;
1177 
1178  buf[0] = '\0';
1179  if (g != NULL) {
1180  strncpy(buf, g, gn);
1181  buf[gn] = '\0';
1182  (void) expandU(mb, buf, bufn);
1183  }
1184 #if defined(NOTYET) /* XXX change needs parsePrep and macros changes too */
1185  if (fn > 5 && STREQ("patch", f, 5) && xisdigit(f[5])) {
1186  /* Skip leading zeros */
1187  for (c = 5; c < fn-1 && f[c] == '0' && xisdigit(f[c+1]);)
1188  c++;
1189  b = buf;
1190  be = stpncpy( stpcpy(b, "%patch -P "), f+c, fn-c);
1191  *be = '\0';
1192  } else
1193 #endif
1194  if (STREQ("basename", f, fn)) {
1195  if ((b = strrchr(buf, '/')) == NULL)
1196  b = buf;
1197  else
1198  b++;
1199  } else if (STREQ("dirname", f, fn)) {
1200  if ((b = strrchr(buf, '/')) != NULL)
1201  *b = '\0';
1202  b = buf;
1203  } else if (STREQ("suffix", f, fn)) {
1204  if ((b = strrchr(buf, '.')) != NULL)
1205  b++;
1206  } else if (STREQ("expand", f, fn)) {
1207  b = buf;
1208  } else if (STREQ("verbose", f, fn)) {
1209  if (negate)
1210  b = (rpmIsVerbose() ? NULL : buf);
1211  else
1212  b = (rpmIsVerbose() ? buf : NULL);
1213  } else if (STREQ("url2path", f, fn) || STREQ("u2p", f, fn)) {
1214  int ut = urlPath(buf, (const char **)&b);
1215  ut = ut; /* XXX quiet gcc */
1216 /*@-branchstate@*/
1217  if (*b == '\0') b = "/";
1218 /*@=branchstate@*/
1219  } else if (STREQ("uncompress", f, fn)) {
1220  rpmCompressedMagic compressed = COMPRESSED_OTHER;
1221 /*@-globs@*/
1222  for (b = buf; (c = *b) && isblank(c);)
1223  b++;
1224  for (be = b; (c = *be) && !isblank(c);)
1225  be++;
1226 /*@=globs@*/
1227  *be++ = '\0';
1228  (void) isCompressed(b, &compressed);
1229  switch(compressed) {
1230  default:
1231  case 0: /* COMPRESSED_NOT */
1232  sprintf(be, "%%__cat %s", b);
1233  break;
1234  case 1: /* COMPRESSED_OTHER */
1235  sprintf(be, "%%__gzip -dc %s", b);
1236  break;
1237  case 2: /* COMPRESSED_BZIP2 */
1238  sprintf(be, "%%__bzip2 -dc %s", b);
1239  break;
1240  case 3: /* COMPRESSED_ZIP */
1241  sprintf(be, "%%__unzip -qq %s", b);
1242  break;
1243  case 4: /* COMPRESSED_LZOP */
1244  sprintf(be, "%%__lzop -dc %s", b);
1245  break;
1246  case 5: /* COMPRESSED_LZMA */
1247  sprintf(be, "%%__lzma -dc %s", b);
1248  break;
1249  case 6: /* COMPRESSED_XZ */
1250  sprintf(be, "%%__xz -dc %s", b);
1251  break;
1252  }
1253  b = be;
1254  } else if (STREQ("S", f, fn)) {
1255  for (b = buf; (c = *b) && xisdigit(c);)
1256  b++;
1257  if (!c) { /* digit index */
1258  b++;
1259  sprintf(b, "%%SOURCE%s", buf);
1260  } else
1261  b = buf;
1262  } else if (STREQ("P", f, fn)) {
1263  for (b = buf; (c = *b) && xisdigit(c);)
1264  b++;
1265  if (!c) { /* digit index */
1266  b++;
1267  sprintf(b, "%%PATCH%s", buf);
1268  } else
1269  b = buf;
1270  } else if (STREQ("F", f, fn)) {
1271  b = buf + strlen(buf) + 1;
1272  sprintf(b, "file%s.file", buf);
1273  }
1274 
1275  if (b) {
1276  (void) expandT(mb, b, strlen(b));
1277  }
1278 }
1279 
1286 static int
1288  /*@globals rpmGlobalMacroContext,
1289  print_macro_trace, print_expand_trace, h_errno, fileSystem @*/
1290  /*@modifies mb, rpmGlobalMacroContext,
1291  print_macro_trace, print_expand_trace, fileSystem @*/
1292 {
1293  MacroEntry *mep;
1294  MacroEntry me;
1295  const char *s = mb->s, *se;
1296  const char *f, *fe;
1297  const char *g, *ge;
1298  size_t fn, gn;
1299  char *t = mb->t; /* save expansion pointer for printExpand */
1300  int c;
1301  int rc = 0;
1302  int negate;
1303  const char * lastc;
1304  int chkexist;
1305 
1306  if (++mb->depth > max_macro_depth) {
1308  _("Recursion depth(%d) greater than max(%d)\n"),
1309  mb->depth, max_macro_depth);
1310  mb->depth--;
1311  mb->expand_trace = 1;
1312  return 1;
1313  }
1314 
1315 /*@-branchstate@*/
1316  while (rc == 0 && mb->nb > 0 && (c = *s) != '\0') {
1317  s++;
1318  /* Copy text until next macro */
1319  switch(c) {
1320  case '%':
1321  if (*s) { /* Ensure not end-of-string. */
1322  if (*s != '%')
1323  /*@switchbreak@*/ break;
1324  s++; /* skip first % in %% */
1325  }
1326  /*@fallthrough@*/
1327  default:
1328  SAVECHAR(mb, c);
1329  continue;
1330  /*@notreached@*/ /*@switchbreak@*/ break;
1331  }
1332 
1333  /* Expand next macro */
1334  f = fe = NULL;
1335  g = ge = NULL;
1336  if (mb->depth > 1) /* XXX full expansion for outermost level */
1337  t = mb->t; /* save expansion pointer for printExpand */
1338  negate = 0;
1339  lastc = NULL;
1340  chkexist = 0;
1341  switch ((c = *s)) {
1342  default: /* %name substitution */
1343  while (*s != '\0' && strchr("!?", *s) != NULL) {
1344  switch(*s++) {
1345  case '!':
1346  negate = ((negate + 1) % 2);
1347  /*@switchbreak@*/ break;
1348  case '?':
1349  chkexist++;
1350  /*@switchbreak@*/ break;
1351  }
1352  }
1353  f = se = s;
1354  if (*se == '-')
1355  se++;
1356  while((c = *se) && (xisalnum(c) || c == '_'))
1357  se++;
1358  /* Recognize non-alnum macros too */
1359  switch (*se) {
1360  case '*':
1361  se++;
1362  if (*se == '*') se++;
1363  /*@innerbreak@*/ break;
1364  case '#':
1365  se++;
1366  /*@innerbreak@*/ break;
1367  default:
1368  /*@innerbreak@*/ break;
1369  }
1370  fe = se;
1371  /* For "%name " macros ... */
1372 /*@-globs@*/
1373  if ((c = *fe) && isblank(c))
1374  if ((lastc = strchr(fe,'\n')) == NULL)
1375  lastc = strchr(fe, '\0');
1376 /*@=globs@*/
1377  /*@switchbreak@*/ break;
1378  case '(': /* %(...) shell escape */
1379  if ((se = matchchar(s, c, ')')) == NULL) {
1381  _("Unterminated %c: %s\n"), (char)c, s);
1382  rc = 1;
1383  continue;
1384  }
1385  if (mb->macro_trace)
1386  printMacro(mb, s, se+1);
1387 
1388  s++; /* skip ( */
1389  rc = doShellEscape(mb, s, (se - s));
1390  se++; /* skip ) */
1391 
1392  s = se;
1393  continue;
1394  /*@notreached@*/ /*@switchbreak@*/ break;
1395  case '{': /* %{...}/%{...:...} substitution */
1396  if ((se = matchchar(s, c, '}')) == NULL) {
1398  _("Unterminated %c: %s\n"), (char)c, s);
1399  rc = 1;
1400  continue;
1401  }
1402  f = s+1;/* skip { */
1403  se++; /* skip } */
1404  while (strchr("!?", *f) != NULL) {
1405  switch(*f++) {
1406  case '!':
1407  negate = ((negate + 1) % 2);
1408  /*@switchbreak@*/ break;
1409  case '?':
1410  chkexist++;
1411  /*@switchbreak@*/ break;
1412  }
1413  }
1414  /* Find end-of-expansion, handle %{foo:bar} expansions. */
1415  for (fe = f; (c = *fe) && !strchr(" :}", c);)
1416  fe++;
1417  switch (c) {
1418  case ':':
1419  g = fe + 1;
1420  ge = se - 1;
1421  /*@innerbreak@*/ break;
1422  case ' ':
1423  lastc = se-1;
1424  /*@innerbreak@*/ break;
1425  default:
1426  /*@innerbreak@*/ break;
1427  }
1428  /*@switchbreak@*/ break;
1429  }
1430 
1431  /* XXX Everything below expects fe > f */
1432  fn = (fe - f);
1433  gn = (ge - g);
1434  if ((fe - f) <= 0) {
1435 /* XXX Process % in unknown context */
1436  c = '%'; /* XXX only need to save % */
1437  SAVECHAR(mb, c);
1438 #if 0
1440  _("A %% is followed by an unparseable macro\n"));
1441 #endif
1442  s = se;
1443  continue;
1444  }
1445 
1446  if (mb->macro_trace)
1447  printMacro(mb, s, se);
1448 
1449  /* Expand builtin macros */
1450  if (STREQ("load", f, fn)) {
1451  if (g != NULL) {
1452  char * mfn = strncpy(alloca(gn + 1), g, gn);
1453  int xx;
1454  mfn[gn] = '\0';
1455  xx = rpmLoadMacroFile(NULL, mfn);
1456  }
1457  s = se;
1458  continue;
1459  }
1460  if (STREQ("global", f, fn)) {
1461  s = doDefine(mb, se, RMIL_GLOBAL, 1);
1462  continue;
1463  }
1464  if (STREQ("define", f, fn)) {
1465  s = doDefine(mb, se, mb->depth, 0);
1466  continue;
1467  }
1468  if (STREQ("undefine", f, fn)) {
1469  s = doUndefine(mb->mc, se);
1470  continue;
1471  }
1472  if (STREQ("unglobal", f, fn)) {
1473  s = doUnglobal(mb->mc, se);
1474  continue;
1475  }
1476 
1477  if (STREQ("echo", f, fn) ||
1478  STREQ("warn", f, fn) ||
1479  STREQ("error", f, fn)) {
1480  int waserror = 0;
1481  if (STREQ("error", f, fn))
1482  waserror = 1;
1483  if (g != NULL && g < ge)
1484  doOutput(mb, waserror, g, gn);
1485  else
1486  doOutput(mb, waserror, f, fn);
1487  s = se;
1488  continue;
1489  }
1490 
1491  if (STREQ("trace", f, fn)) {
1492  /* XXX TODO restore expand_trace/macro_trace to 0 on return */
1493  mb->expand_trace = mb->macro_trace = (negate ? 0 : mb->depth);
1494  if (mb->depth == 1) {
1497  }
1498  s = se;
1499  continue;
1500  }
1501 
1502  if (STREQ("dump", f, fn)) {
1503  rpmDumpMacroTable(mb->mc, NULL);
1504  while (iseol(*se))
1505  se++;
1506  s = se;
1507  continue;
1508  }
1509 
1510 #ifdef WITH_LUA
1511  if (STREQ("lua", f, fn)) {
1512  rpmlua lua = NULL; /* Global state. */
1513  const char *ls = s+sizeof("{lua:")-1;
1514  const char *lse = se-sizeof("}")+1;
1515  char *scriptbuf = (char *)xmalloc((lse-ls)+1);
1516  const char *printbuf;
1517  memcpy(scriptbuf, ls, lse-ls);
1518  scriptbuf[lse-ls] = '\0';
1519  rpmluaSetPrintBuffer(lua, 1);
1520  if (rpmluaRunScript(lua, scriptbuf, NULL) == -1)
1521  rc = 1;
1522  printbuf = rpmluaGetPrintBuffer(lua);
1523  if (printbuf) {
1524  int len = strlen(printbuf);
1525  if (len > mb->nb)
1526  len = mb->nb;
1527  memcpy(mb->t, printbuf, len);
1528  mb->t += len;
1529  mb->nb -= len;
1530  }
1531  rpmluaSetPrintBuffer(lua, 0);
1532  free(scriptbuf);
1533  s = se;
1534  continue;
1535  }
1536 #endif
1537 
1538 #if defined(NOTYET) /* XXX change needs parsePrep and macros changes too */
1539  /* Rewrite "%patchNN ..." as "%patch -P NN ..." and expand. */
1540  if (lastc != NULL && fn > 5 && STREQ("patch", f, 5) && xisdigit(f[5])) {
1541  /*@-internalglobs@*/ /* FIX: verbose may be set */
1542  doFoo(mb, negate, f, (lastc - f), NULL, 0);
1543  /*@=internalglobs@*/
1544  s = lastc;
1545  continue;
1546  }
1547 #endif
1548 
1549  /* XXX necessary but clunky */
1550  if (STREQ("basename", f, fn) ||
1551  STREQ("dirname", f, fn) ||
1552  STREQ("suffix", f, fn) ||
1553  STREQ("expand", f, fn) ||
1554  STREQ("verbose", f, fn) ||
1555  STREQ("uncompress", f, fn) ||
1556  STREQ("url2path", f, fn) ||
1557  STREQ("u2p", f, fn) ||
1558  STREQ("S", f, fn) ||
1559  STREQ("P", f, fn) ||
1560  STREQ("F", f, fn)) {
1561  /*@-internalglobs@*/ /* FIX: verbose may be set */
1562  doFoo(mb, negate, f, fn, g, gn);
1563  /*@=internalglobs@*/
1564  s = se;
1565  continue;
1566  }
1567 
1568  /* Expand defined macros */
1569  mep = findEntry(mb->mc, f, fn);
1570  me = (mep ? *mep : NULL);
1571 
1572  /* XXX Special processing for flags */
1573  if (*f == '-') {
1574  if (me)
1575  me->used++; /* Mark macro as used */
1576  if ((me == NULL && !negate) || /* Without -f, skip %{-f...} */
1577  (me != NULL && negate)) { /* With -f, skip %{!-f...} */
1578  s = se;
1579  continue;
1580  }
1581 
1582  if (g && g < ge) { /* Expand X in %{-f:X} */
1583  rc = expandT(mb, g, gn);
1584  } else
1585  if (me && me->body && *me->body) {/* Expand %{-f}/%{-f*} */
1586  rc = expandT(mb, me->body, strlen(me->body));
1587  }
1588  s = se;
1589  continue;
1590  }
1591 
1592  /* XXX Special processing for macro existence */
1593  if (chkexist) {
1594  if ((me == NULL && !negate) || /* Without -f, skip %{?f...} */
1595  (me != NULL && negate)) { /* With -f, skip %{!?f...} */
1596  s = se;
1597  continue;
1598  }
1599  if (g && g < ge) { /* Expand X in %{?f:X} */
1600  rc = expandT(mb, g, gn);
1601  } else
1602  if (me && me->body && *me->body) { /* Expand %{?f}/%{?f*} */
1603  rc = expandT(mb, me->body, strlen(me->body));
1604  }
1605  s = se;
1606  continue;
1607  }
1608 
1609  if (me == NULL) { /* leave unknown %... as is */
1610 #ifndef HACK
1611 #if DEAD
1612  /* XXX hack to skip over empty arg list */
1613  if (fn == 1 && *f == '*') {
1614  s = se;
1615  continue;
1616  }
1617 #endif
1618  /* XXX hack to permit non-overloaded %foo to be passed */
1619  c = '%'; /* XXX only need to save % */
1620  SAVECHAR(mb, c);
1621 #else
1622  if (!strncmp(f, "if", fn) ||
1623  !strncmp(f, "else", fn) ||
1624  !strncmp(f, "endif", fn)) {
1625  c = '%'; /* XXX only need to save % */
1626  SAVECHAR(mb, c);
1627  } else {
1629  _("Macro %%%.*s not found, skipping\n"), fn, f);
1630  s = se;
1631  }
1632 #endif
1633  continue;
1634  }
1635 
1636  /* Setup args for "%name " macros with opts */
1637  if (me && me->opts != NULL) {
1638  if (lastc != NULL) {
1639  se = grabArgs(mb, me, fe, lastc);
1640  } else {
1641  addMacro(mb->mc, "**", NULL, "", mb->depth);
1642  addMacro(mb->mc, "*", NULL, "", mb->depth);
1643  addMacro(mb->mc, "#", NULL, "0", mb->depth);
1644  addMacro(mb->mc, "0", NULL, me->name, mb->depth);
1645  }
1646  }
1647 
1648  /* Recursively expand body of macro */
1649  if (me->body && *me->body) {
1650  mb->s = me->body;
1651  rc = expandMacro(mb);
1652  if (rc == 0)
1653  me->used++; /* Mark macro as used */
1654  }
1655 
1656  /* Free args for "%name " macros with opts */
1657  if (me->opts != NULL)
1658  freeArgs(mb);
1659 
1660  s = se;
1661  }
1662 /*@=branchstate@*/
1663 
1664  *mb->t = '\0';
1665  mb->s = s;
1666  mb->depth--;
1667  if (rc != 0 || mb->expand_trace)
1668  printExpansion(mb, t, mb->t);
1669  return rc;
1670 }
1671 
1672 #if !defined(DEBUG_MACROS)
1673 /* =============================================================== */
1674 /* XXX dupe'd to avoid change in linkage conventions. */
1675 
1676 #define POPT_ERROR_NOARG -10
1677 #define POPT_ERROR_BADQUOTE -15
1678 #define POPT_ERROR_MALLOC -21
1680 #define POPT_ARGV_ARRAY_GROW_DELTA 5
1681 
1682 /*@-boundswrite@*/
1683 static int XpoptDupArgv(int argc, const char **argv,
1684  int * argcPtr, const char *** argvPtr)
1685  /*@modifies *argcPtr, *argvPtr @*/
1686 {
1687  size_t nb = (argc + 1) * sizeof(*argv);
1688  const char ** argv2;
1689  char * dst;
1690  int i;
1691 
1692  if (argc <= 0 || argv == NULL) /* XXX can't happen */
1693  return POPT_ERROR_NOARG;
1694  for (i = 0; i < argc; i++) {
1695  if (argv[i] == NULL)
1696  return POPT_ERROR_NOARG;
1697  nb += strlen(argv[i]) + 1;
1698  }
1699 
1700  dst = malloc(nb);
1701  if (dst == NULL) /* XXX can't happen */
1702  return POPT_ERROR_MALLOC;
1703  argv2 = (void *) dst;
1704  dst += (argc + 1) * sizeof(*argv);
1705 
1706  /*@-branchstate@*/
1707  for (i = 0; i < argc; i++) {
1708  argv2[i] = dst;
1709  dst += strlen(strcpy(dst, argv[i])) + 1;
1710  }
1711  /*@=branchstate@*/
1712  argv2[argc] = NULL;
1713 
1714  if (argvPtr) {
1715  *argvPtr = argv2;
1716  } else {
1717  free(argv2);
1718  argv2 = NULL;
1719  }
1720  if (argcPtr)
1721  *argcPtr = argc;
1722  return 0;
1723 }
1724 /*@=boundswrite@*/
1725 
1726 /*@-bounds@*/
1727 static int XpoptParseArgvString(const char * s, int * argcPtr, const char *** argvPtr)
1728  /*@modifies *argcPtr, *argvPtr @*/
1729 {
1730  const char * src;
1731  char quote = '\0';
1732  int argvAlloced = POPT_ARGV_ARRAY_GROW_DELTA;
1733  const char ** argv = malloc(sizeof(*argv) * argvAlloced);
1734  int argc = 0;
1735  int buflen = strlen(s) + 1;
1736  char * buf = memset(alloca(buflen), 0, buflen);
1737  int rc = POPT_ERROR_MALLOC;
1738 
1739  if (argv == NULL) return rc;
1740  argv[argc] = buf;
1741 
1742  for (src = s; *src != '\0'; src++) {
1743  if (quote == *src) {
1744  quote = '\0';
1745  } else if (quote != '\0') {
1746  if (*src == '\\') {
1747  src++;
1748  if (!*src) {
1749  rc = POPT_ERROR_BADQUOTE;
1750  goto exit;
1751  }
1752  if (*src != quote) *buf++ = '\\';
1753  }
1754  *buf++ = *src;
1755  } else if (isspace(*src)) {
1756  if (*argv[argc] != '\0') {
1757  buf++, argc++;
1758  if (argc == argvAlloced) {
1759  argvAlloced += POPT_ARGV_ARRAY_GROW_DELTA;
1760  argv = realloc(argv, sizeof(*argv) * argvAlloced);
1761  if (argv == NULL) goto exit;
1762  }
1763  argv[argc] = buf;
1764  }
1765  } else switch (*src) {
1766  case '"':
1767  case '\'':
1768  quote = *src;
1769  /*@switchbreak@*/ break;
1770  case '\\':
1771  src++;
1772  if (!*src) {
1773  rc = POPT_ERROR_BADQUOTE;
1774  goto exit;
1775  }
1776  /*@fallthrough@*/
1777  default:
1778  *buf++ = *src;
1779  /*@switchbreak@*/ break;
1780  }
1781  }
1782 
1783  if (strlen(argv[argc])) {
1784  argc++, buf++;
1785  }
1786 
1787  rc = XpoptDupArgv(argc, argv, argcPtr, argvPtr);
1788 
1789 exit:
1790  if (argv) free(argv);
1791  return rc;
1792 }
1793 /*@=bounds@*/
1794 /* =============================================================== */
1795 /*@unchecked@*/
1796 static int _debug = 0;
1797 
1798 int rpmGlob(const char * patterns, int * argcPtr, const char *** argvPtr)
1799 {
1800  int ac = 0;
1801  const char ** av = NULL;
1802  int argc = 0;
1803  const char ** argv = NULL;
1804  char * globRoot = NULL;
1805 #ifdef ENABLE_NLS
1806  const char * old_collate = NULL;
1807  const char * old_ctype = NULL;
1808  const char * t;
1809 #endif
1810  size_t maxb, nb;
1811  int i, j;
1812  int rc;
1813 
1814  rc = XpoptParseArgvString(patterns, &ac, &av);
1815  if (rc)
1816  return rc;
1817 #ifdef ENABLE_NLS
1818 /*@-branchstate@*/
1819  t = setlocale(LC_COLLATE, NULL);
1820  if (t)
1821  old_collate = xstrdup(t);
1822  t = setlocale(LC_CTYPE, NULL);
1823  if (t)
1824  old_ctype = xstrdup(t);
1825 /*@=branchstate@*/
1826  (void) setlocale(LC_COLLATE, "C");
1827  (void) setlocale(LC_CTYPE, "C");
1828 #endif
1829 
1830  if (av != NULL)
1831  for (j = 0; j < ac; j++) {
1832  const char * globURL;
1833  const char * path;
1834  int ut = urlPath(av[j], &path);
1835  glob_t gl;
1836 
1837  if (!Glob_pattern_p(av[j], 0) && strchr(path, '~') == NULL) {
1838  argv = xrealloc(argv, (argc+2) * sizeof(*argv));
1839  argv[argc] = xstrdup(av[j]);
1840 if (_debug)
1841 fprintf(stderr, "*** rpmGlob argv[%d] \"%s\"\n", argc, argv[argc]);
1842  argc++;
1843  continue;
1844  }
1845 
1846  gl.gl_pathc = 0;
1847  gl.gl_pathv = NULL;
1848  rc = Glob(av[j], GLOB_TILDE, Glob_error, &gl);
1849  if (rc)
1850  goto exit;
1851 
1852  /* XXX Prepend the URL leader for globs that have stripped it off */
1853  maxb = 0;
1854  for (i = 0; i < gl.gl_pathc; i++) {
1855  if ((nb = strlen(&(gl.gl_pathv[i][0]))) > maxb)
1856  maxb = nb;
1857  }
1858 
1859  nb = ((ut == URL_IS_PATH) ? (path - av[j]) : 0);
1860  maxb += nb;
1861  maxb += 1;
1862  globURL = globRoot = xmalloc(maxb);
1863 
1864  switch (ut) {
1865  case URL_IS_PATH:
1866  case URL_IS_DASH:
1867  strncpy(globRoot, av[j], nb);
1868  /*@switchbreak@*/ break;
1869  case URL_IS_HTTPS:
1870  case URL_IS_HTTP:
1871  case URL_IS_FTP:
1872  case URL_IS_HKP:
1873  case URL_IS_UNKNOWN:
1874  default:
1875  /*@switchbreak@*/ break;
1876  }
1877  globRoot += nb;
1878  *globRoot = '\0';
1879 if (_debug)
1880 fprintf(stderr, "*** GLOB maxb %d diskURL %d %*s globURL %p %s\n", (int)maxb, (int)nb, (int)nb, av[j], globURL, globURL);
1881 
1882  argv = xrealloc(argv, (argc+gl.gl_pathc+1) * sizeof(*argv));
1883 
1884  if (argv != NULL)
1885  for (i = 0; i < gl.gl_pathc; i++) {
1886  const char * globFile = &(gl.gl_pathv[i][0]);
1887  if (globRoot > globURL && globRoot[-1] == '/')
1888  while (*globFile == '/') globFile++;
1889  strcpy(globRoot, globFile);
1890 if (_debug)
1891 fprintf(stderr, "*** rpmGlob argv[%d] \"%s\"\n", argc, globURL);
1892  argv[argc++] = xstrdup(globURL);
1893  }
1894  /*@-immediatetrans@*/
1895  Globfree(&gl);
1896  /*@=immediatetrans@*/
1897  globURL = _free(globURL);
1898  }
1899 
1900  if (argv != NULL && argc > 0) {
1901  argv[argc] = NULL;
1902  if (argvPtr)
1903  *argvPtr = argv;
1904  if (argcPtr)
1905  *argcPtr = argc;
1906  rc = 0;
1907  } else
1908  rc = 1;
1909 
1910 
1911 exit:
1912 #ifdef ENABLE_NLS
1913 /*@-branchstate@*/
1914  if (old_collate) {
1915  (void) setlocale(LC_COLLATE, old_collate);
1916  old_collate = _free(old_collate);
1917  }
1918  if (old_ctype) {
1919  (void) setlocale(LC_CTYPE, old_ctype);
1920  old_ctype = _free(old_ctype);
1921  }
1922 /*@=branchstate@*/
1923 #endif
1924  av = _free(av);
1925 /*@-branchstate@*/
1926  if (rc || argvPtr == NULL) {
1927 /*@-dependenttrans -unqualifiedtrans@*/
1928  if (argv != NULL)
1929  for (i = 0; i < argc; i++)
1930  argv[i] = _free(argv[i]);
1931  argv = _free(argv);
1932 /*@=dependenttrans =unqualifiedtrans@*/
1933  }
1934 /*@=branchstate@*/
1935  return rc;
1936 }
1937 #endif /* !defined(DEBUG_MACROS) */
1938 
1939 /* =============================================================== */
1940 
1941 int
1942 expandMacros(void * spec, MacroContext mc, char * sbuf, size_t slen)
1943 {
1944  MacroBuf mb = alloca(sizeof(*mb));
1945  char *tbuf;
1946  int rc;
1947 
1948  if (sbuf == NULL || slen == 0)
1949  return 0;
1950  if (mc == NULL) mc = rpmGlobalMacroContext;
1951 
1952  tbuf = alloca(slen + 1);
1953  memset(tbuf, 0, (slen + 1));
1954 
1955  mb->s = sbuf;
1956  mb->t = tbuf;
1957  mb->nb = slen;
1958  mb->depth = 0;
1961 
1962  mb->spec = spec; /* (future) %file expansion info */
1963  mb->mc = mc;
1964 
1965  rc = expandMacro(mb);
1966 
1967  tbuf[slen] = '\0';
1968  if (mb->nb == 0)
1969  rpmError(RPMERR_BADSPEC, _("Macro expansion too big for target buffer\n"));
1970  else
1971  strncpy(sbuf, tbuf, (slen - mb->nb + 1));
1972 
1973  return rc;
1974 }
1975 
1976 void
1978  const char * n, const char * o, const char * b, int level)
1979 {
1980  MacroEntry * mep;
1981  const char * name = n;
1982 
1983  if (*name == '.') /* XXX readonly macros */
1984  name++;
1985  if (*name == '.') /* XXX readonly macros */
1986  name++;
1987 
1988  if (mc == NULL) mc = rpmGlobalMacroContext;
1989 
1990  /* If new name, expand macro table */
1991  if ((mep = findEntry(mc, name, 0)) == NULL) {
1992  if (mc->firstFree == mc->macrosAllocated)
1993  expandMacroTable(mc);
1994  if (mc->macroTable != NULL)
1995  mep = mc->macroTable + mc->firstFree++;
1996  }
1997 
1998  if (mep != NULL) {
1999  /* XXX permit "..foo" to be pushed over ".foo" */
2000  if (*mep && (*mep)->flags && !(n[0] == '.' && n[1] == '.')) {
2001  /* XXX avoid error message for %buildroot */
2002  if (strcmp((*mep)->name, "buildroot"))
2003  rpmError(RPMERR_BADSPEC, _("Macro '%s' is readonly and cannot be changed.\n"), n);
2004  return;
2005  }
2006  /* Push macro over previous definition */
2007  pushMacro(mep, n, o, b, level);
2008 
2009  /* If new name, sort macro table */
2010  if ((*mep)->prev == NULL)
2011  sortMacroTable(mc);
2012  }
2013 }
2014 
2015 void
2016 delMacro(MacroContext mc, const char * n)
2017 {
2018  MacroEntry * mep;
2019 
2020  if (mc == NULL) mc = rpmGlobalMacroContext;
2021  /* If name exists, pop entry */
2022  if ((mep = findEntry(mc, n, 0)) != NULL) {
2023  popMacro(mep);
2024  /* If deleted name, sort macro table */
2025  if (!(mep && *mep))
2026  sortMacroTable(mc);
2027  }
2028 }
2029 
2030 void
2031 delMacroAll(MacroContext mc, const char * n)
2032 {
2033  MacroEntry * mep;
2034 
2035  if (mc == NULL) mc = rpmGlobalMacroContext;
2036  /* If name exists, pop entry */
2037  while ((mep = findEntry(mc, n, 0)) != NULL) {
2038  delMacro(mc, n);
2039  }
2040 }
2041 
2042 /*@-mustmod@*/ /* LCL: mc is modified through mb->mc, mb is abstract */
2043 int
2044 rpmDefineMacro(MacroContext mc, const char * macro, int level)
2045 {
2046  MacroBuf mb = alloca(sizeof(*mb));
2047 
2048  memset(mb, 0, sizeof(*mb));
2049  /* XXX just enough to get by */
2050  mb->mc = (mc ? mc : rpmGlobalMacroContext);
2051  (void) doDefine(mb, macro, level, 0);
2052  return 0;
2053 }
2054 /*@=mustmod@*/
2055 
2056 void
2058 {
2059 
2060  if (mc == NULL || mc == rpmGlobalMacroContext)
2061  return;
2062 
2063  if (mc->macroTable != NULL) {
2064  int i;
2065  for (i = 0; i < mc->firstFree; i++) {
2066  MacroEntry *mep, me;
2067  mep = &mc->macroTable[i];
2068  me = *mep;
2069 
2070  if (me == NULL) /* XXX this should never happen */
2071  continue;
2072  addMacro(NULL, me->name, me->opts, me->body, (level - 1));
2073  }
2074  }
2075 }
2076 
2077 int
2078 rpmLoadMacroFile(MacroContext mc, const char * fn)
2079 {
2080  FD_t fd = Fopen(fn, "r.fpio");
2081  size_t bufn = _macro_BUFSIZ;
2082  char *buf = alloca(bufn);
2083  int rc = -1;
2084 
2085  if (fd == NULL || Ferror(fd)) {
2086  if (fd) (void) Fclose(fd);
2087  return rc;
2088  }
2089 
2090  /* XXX Assume new fangled macro expansion */
2091  /*@-mods@*/
2093  /*@=mods@*/
2094 
2095  buf[0] = '\0';
2096  while(rdcl(buf, bufn, fd) != NULL) {
2097  char c, *n;
2098 
2099  n = buf;
2100  SKIPBLANK(n, c);
2101 
2102  if (c != '%')
2103  continue;
2104  n++; /* skip % */
2105  rc = rpmDefineMacro(mc, n, RMIL_MACROFILES);
2106  }
2107  rc = Fclose(fd);
2108  return rc;
2109 }
2110 
2111 void
2112 rpmInitMacros(MacroContext mc, const char * macrofiles)
2113 {
2114  char *mfiles, *m, *me;
2115 
2116  if (macrofiles == NULL)
2117  return;
2118 #ifdef DYING
2119  if (mc == NULL) mc = rpmGlobalMacroContext;
2120 #endif
2121 
2122  mfiles = xstrdup(macrofiles);
2123  for (m = mfiles; m && *m != '\0'; m = me) {
2124  const char ** av;
2125  int ac;
2126  int i;
2127 
2128  for (me = m; (me = strchr(me, ':')) != NULL; me++) {
2129  /* Skip over URI's. */
2130  if (!(me[1] == '/' && me[2] == '/'))
2131  /*@innerbreak@*/ break;
2132  }
2133 
2134  if (me && *me == ':')
2135  *me++ = '\0';
2136  else
2137  me = m + strlen(m);
2138 
2139  /* Glob expand the macro file path element, expanding ~ to $HOME. */
2140  ac = 0;
2141  av = NULL;
2142 #if defined(DEBUG_MACROS)
2143  ac = 1;
2144  av = xmalloc((ac + 1) * sizeof(*av));
2145  av[0] = strdup(m);
2146  av[1] = NULL;
2147 #else
2148  i = rpmGlob(m, &ac, &av);
2149  if (i != 0)
2150  continue;
2151 #endif
2152 
2153  /* Read macros from each file. */
2154 
2155  for (i = 0; i < ac; i++) {
2156  size_t slen = strlen(av[i]);
2157 
2158  /* Skip backup files and %config leftovers. */
2159 #define _suffix(_s, _x) \
2160  (slen >= sizeof(_x) && !strcmp((_s)+slen-(sizeof(_x)-1), (_x)))
2161  if (!(_suffix(av[i], "~")
2162  || _suffix(av[i], ".rpmnew")
2163  || _suffix(av[i], ".rpmorig")
2164  || _suffix(av[i], ".rpmsave"))
2165  )
2166  (void) rpmLoadMacroFile(mc, av[i]);
2167 #undef _suffix
2168 
2169  av[i] = _free(av[i]);
2170  }
2171  av = _free(av);
2172  }
2173  mfiles = _free(mfiles);
2174 
2175  /* Reload cmdline macros */
2176  /*@-mods@*/
2177  rpmLoadMacros(rpmCLIMacroContext, RMIL_CMDLINE);
2178  /*@=mods@*/
2179 }
2180 
2181 /*@-globstate@*/
2182 void
2184 {
2185 
2186  if (mc == NULL) mc = rpmGlobalMacroContext;
2187 
2188  if (mc->macroTable != NULL) {
2189  int i;
2190  for (i = 0; i < mc->firstFree; i++) {
2191  MacroEntry me;
2192  while ((me = mc->macroTable[i]) != NULL) {
2193  /* XXX cast to workaround const */
2194  /*@-onlytrans@*/
2195  if ((mc->macroTable[i] = me->prev) == NULL)
2196  me->name = _free(me->name);
2197  /*@=onlytrans@*/
2198  me->opts = _free(me->opts);
2199  me->body = _free(me->body);
2200  me = _free(me);
2201  }
2202  }
2203  mc->macroTable = _free(mc->macroTable);
2204  }
2205  memset(mc, 0, sizeof(*mc));
2206 }
2207 /*@=globstate@*/
2208 
2209 /* =============================================================== */
2210 int isCompressed(const char * file, rpmCompressedMagic * compressed)
2211 {
2212  FD_t fd;
2213  ssize_t nb;
2214  int rc = -1;
2215  unsigned char magic[13];
2216  char *end, *ext;
2217 
2218  *compressed = COMPRESSED_NOT;
2219 
2220  fd = Fopen(file, "r");
2221  if (fd == NULL || Ferror(fd)) {
2222  /* XXX Fstrerror */
2223  rpmError(RPMERR_BADSPEC, _("File %s: %s\n"), file, Fstrerror(fd));
2224  if (fd) (void) Fclose(fd);
2225  return 1;
2226  }
2227  nb = Fread(magic, sizeof(magic[0]), sizeof(magic), fd);
2228  if (nb < 0) {
2229  rpmError(RPMERR_BADSPEC, _("File %s: %s\n"), file, Fstrerror(fd));
2230  rc = 1;
2231  } else if (nb < sizeof(magic)) {
2232  rpmError(RPMERR_BADSPEC, _("File %s is smaller than %u bytes\n"),
2233  file, (unsigned)sizeof(magic));
2234  rc = 0;
2235  }
2236  (void) Fclose(fd);
2237  if (rc >= 0)
2238  return rc;
2239 
2240  rc = 0;
2241 
2242  /* Tar archives will be recognized by filename. */
2243  end = strchr(file, '\0');
2244  ext = end - 4;
2245  if (ext > file && !strcasecmp(ext, ".tar")) return rc;
2246 
2247  if (magic[0] == 'B' && magic[1] == 'Z')
2248  *compressed = COMPRESSED_BZIP2;
2249  else
2250  if (magic[0] == 0120 && magic[1] == 0113
2251  && magic[2] == 0003 && magic[3] == 0004) /* pkzip */
2252  *compressed = COMPRESSED_ZIP;
2253  else
2254  if (magic[0] == 0x89 && magic[1] == 'L'
2255  && magic[2] == 'Z' && magic[3] == 'O') /* lzop */
2256  *compressed = COMPRESSED_LZOP;
2257  else
2258  /* XXX Ick, LZMA has no magic. See http://lkml.org/lkml/2005/6/13/285 */
2259  if (magic[ 9] == 0x00 && magic[10] == 0x00 &&
2260  magic[11] == 0x00 && magic[12] == 0x00) /* lzmash */
2261  *compressed = COMPRESSED_LZMA;
2262  else
2263  if (magic[0] == 0135 && magic[1] == 0 && magic[2] == 0) /* lzma */
2264  *compressed = COMPRESSED_LZMA;
2265  else
2266  if (magic[0] == 0xFD && magic[1] == 0x37 && magic[2] == 0x7A
2267  && magic[3] == 0x58 && magic[4] == 0x5A && magic[5] == 0x00) /* xz */
2268  *compressed = COMPRESSED_XZ;
2269  else
2270  if ((magic[0] == 0037 && magic[1] == 0213) /* gzip */
2271  || (magic[0] == 0037 && magic[1] == 0236) /* old gzip */
2272  || (magic[0] == 0037 && magic[1] == 0036) /* pack */
2273  || (magic[0] == 0037 && magic[1] == 0240) /* SCO lzh */
2274  || (magic[0] == 0037 && magic[1] == 0235)) /* compress */
2275  *compressed = COMPRESSED_OTHER;
2276 
2277  return rc;
2278 }
2279 
2280 /* =============================================================== */
2281 
2282 /*@-modfilesys@*/
2283 char *
2284 rpmExpand(const char *arg, ...)
2285 {
2286  const char *s;
2287  char *t, *te;
2288  size_t sn, tn;
2289  size_t bufn = 8 * _macro_BUFSIZ;
2290 
2291  va_list ap;
2292 
2293  if (arg == NULL)
2294  return xstrdup("");
2295 
2296  t = xmalloc(bufn + strlen(arg) + 1);
2297  *t = '\0';
2298  te = stpcpy(t, arg);
2299 
2300 /*@-branchstate@*/
2301  va_start(ap, arg);
2302  while ((s = va_arg(ap, const char *)) != NULL) {
2303  sn = strlen(s);
2304  tn = (te - t);
2305  t = xrealloc(t, tn + sn + bufn + 1);
2306  te = t + tn;
2307  te = stpcpy(te, s);
2308  }
2309  va_end(ap);
2310 /*@=branchstate@*/
2311 
2312  *te = '\0';
2313  tn = (te - t);
2314  (void) expandMacros(NULL, NULL, t, tn + bufn + 1);
2315  t[tn + bufn] = '\0';
2316  t = xrealloc(t, strlen(t) + 1);
2317 
2318  return t;
2319 }
2320 /*@=modfilesys@*/
2321 
2322 int
2323 rpmExpandNumeric(const char *arg)
2324 {
2325  const char *val;
2326  int rc;
2327 
2328  if (arg == NULL)
2329  return 0;
2330 
2331  val = rpmExpand(arg, NULL);
2332  if (!(val && *val != '%'))
2333  rc = 0;
2334  else if (*val == 'Y' || *val == 'y')
2335  rc = 1;
2336  else if (*val == 'N' || *val == 'n')
2337  rc = 0;
2338  else {
2339  char *end;
2340  rc = strtol(val, &end, 0);
2341  if (!(end && *end == '\0'))
2342  rc = 0;
2343  }
2344  val = _free(val);
2345 
2346  return rc;
2347 }
2348 
2349 /* @todo "../sbin/./../bin/" not correct. */
2350 char *rpmCleanPath(char * path)
2351 {
2352  const char *s;
2353  char *se, *t, *te;
2354  int begin = 1;
2355 
2356  if (path == NULL)
2357  return NULL;
2358 
2359 /*fprintf(stderr, "*** RCP %s ->\n", path); */
2360  s = t = te = path;
2361  while (*s != '\0') {
2362 /*fprintf(stderr, "*** got \"%.*s\"\trest \"%s\"\n", (t-path), path, s); */
2363  switch(*s) {
2364  case ':': /* handle url's */
2365  if (s[1] == '/' && s[2] == '/') {
2366  *t++ = *s++;
2367  *t++ = *s++;
2368  /* XXX handle "file:///" */
2369  if (s[0] == '/') *t++ = *s++;
2370  te = t;
2371  /*@switchbreak@*/ break;
2372  }
2373  begin=1;
2374  /*@switchbreak@*/ break;
2375  case '/':
2376  /* Move parent dir forward */
2377  for (se = te + 1; se < t && *se != '/'; se++)
2378  {};
2379  if (se < t && *se == '/') {
2380  te = se;
2381 /*fprintf(stderr, "*** next pdir \"%.*s\"\n", (te-path), path); */
2382  }
2383  while (s[1] == '/')
2384  s++;
2385  while (t > te && t[-1] == '/')
2386  t--;
2387  /*@switchbreak@*/ break;
2388  case '.':
2389  /* Leading .. is special */
2390  /* Check that it is ../, so that we don't interpret */
2391  /* ..?(i.e. "...") or ..* (i.e. "..bogus") as "..". */
2392  /* in the case of "...", this ends up being processed*/
2393  /* as "../.", and the last '.' is stripped. This */
2394  /* would not be correct processing. */
2395  if (begin && s[1] == '.' && (s[2] == '/' || s[2] == '\0')) {
2396 /*fprintf(stderr, " leading \"..\"\n"); */
2397  *t++ = *s++;
2398  /*@switchbreak@*/ break;
2399  }
2400  /* Single . is special */
2401  if (begin && s[1] == '\0') {
2402  /*@switchbreak@*/ break;
2403  }
2404  /* Trim embedded ./ , trailing /. */
2405  if ((t[-1] == '/' && s[1] == '\0') || (t > path && t[-1] == '/' && s[1] == '/')) {
2406  s++;
2407  continue;
2408  }
2409  /* Trim embedded /../ and trailing /.. */
2410  if (!begin && t > path && t[-1] == '/' && s[1] == '.' && (s[2] == '/' || s[2] == '\0')) {
2411  t = te;
2412  /* Move parent dir forward */
2413  if (te > path)
2414  for (--te; te > path && *te != '/'; te--)
2415  {};
2416 /*fprintf(stderr, "*** prev pdir \"%.*s\"\n", (te-path), path); */
2417  s++;
2418  s++;
2419  continue;
2420  }
2421  /*@switchbreak@*/ break;
2422  default:
2423  begin = 0;
2424  /*@switchbreak@*/ break;
2425  }
2426  *t++ = *s++;
2427  }
2428 
2429  /* Trim trailing / (but leave single / alone) */
2430  if (t > &path[1] && t[-1] == '/')
2431  t--;
2432  *t = '\0';
2433 
2434 /*fprintf(stderr, "\t%s\n", path); */
2435  return path;
2436 }
2437 
2438 /* Return concatenated and expanded canonical path. */
2439 
2440 const char *
2441 rpmGetPath(const char *path, ...)
2442 {
2443  size_t bufn = _macro_BUFSIZ;
2444  char *buf = alloca(bufn);
2445  const char * s;
2446  char * t, * te;
2447  va_list ap;
2448 
2449  if (path == NULL)
2450  return xstrdup("");
2451 
2452  buf[0] = '\0';
2453  t = buf;
2454  te = stpcpy(t, path);
2455  *te = '\0';
2456 
2457  va_start(ap, path);
2458  while ((s = va_arg(ap, const char *)) != NULL) {
2459  te = stpcpy(te, s);
2460  *te = '\0';
2461  }
2462  va_end(ap);
2463 /*@-modfilesys@*/
2464  (void) expandMacros(NULL, NULL, buf, bufn);
2465 /*@=modfilesys@*/
2466 
2467  (void) rpmCleanPath(buf);
2468  return xstrdup(buf); /* XXX xstrdup has side effects. */
2469 }
2470 
2471 /* Merge 3 args into path, any or all of which may be a url. */
2472 
2473 const char * rpmGenPath(const char * urlroot, const char * urlmdir,
2474  const char *urlfile)
2475 {
2476 /*@owned@*/ const char * xroot = rpmGetPath(urlroot, NULL);
2477 /*@dependent@*/ const char * root = xroot;
2478 /*@owned@*/ const char * xmdir = rpmGetPath(urlmdir, NULL);
2479 /*@dependent@*/ const char * mdir = xmdir;
2480 /*@owned@*/ const char * xfile = rpmGetPath(urlfile, NULL);
2481 /*@dependent@*/ const char * file = xfile;
2482  const char * result;
2483  const char * url = NULL;
2484  int nurl = 0;
2485  int ut;
2486 
2487 #if 0
2488 if (_debug) fprintf(stderr, "*** RGP xroot %s xmdir %s xfile %s\n", xroot, xmdir, xfile);
2489 #endif
2490  ut = urlPath(xroot, &root);
2491  if (url == NULL && ut > URL_IS_DASH) {
2492  url = xroot;
2493  nurl = root - xroot;
2494 #if 0
2495 if (_debug) fprintf(stderr, "*** RGP ut %d root %s nurl %d\n", ut, root, nurl);
2496 #endif
2497  }
2498  if (root == NULL || *root == '\0') root = "/";
2499 
2500  ut = urlPath(xmdir, &mdir);
2501  if (url == NULL && ut > URL_IS_DASH) {
2502  url = xmdir;
2503  nurl = mdir - xmdir;
2504 #if 0
2505 if (_debug) fprintf(stderr, "*** RGP ut %d mdir %s nurl %d\n", ut, mdir, nurl);
2506 #endif
2507  }
2508  if (mdir == NULL || *mdir == '\0') mdir = "/";
2509 
2510  ut = urlPath(xfile, &file);
2511  if (url == NULL && ut > URL_IS_DASH) {
2512  url = xfile;
2513  nurl = file - xfile;
2514 #if 0
2515 if (_debug) fprintf(stderr, "*** RGP ut %d file %s nurl %d\n", ut, file, nurl);
2516 #endif
2517  }
2518 
2519 /*@-branchstate@*/
2520  if (url && nurl > 0) {
2521  char *t = strncpy(alloca(nurl+1), url, nurl);
2522  t[nurl] = '\0';
2523  url = t;
2524  } else
2525  url = "";
2526 /*@=branchstate@*/
2527 
2528  result = rpmGetPath(url, root, "/", mdir, "/", file, NULL);
2529 
2530  xroot = _free(xroot);
2531  xmdir = _free(xmdir);
2532  xfile = _free(xfile);
2533 #if 0
2534 if (_debug) fprintf(stderr, "*** RGP result %s\n", result);
2535 #endif
2536  return result;
2537 }
2538 
2539 /* =============================================================== */
2540 
2541 #if defined(DEBUG_MACROS)
2542 
2543 #if defined(EVAL_MACROS)
2544 
2545 const char *rpmMacrofiles = MACROFILES;
2546 
2547 int
2548 main(int argc, char *argv[])
2549 {
2550  int c;
2551  int errflg = 0;
2552  extern char *optarg;
2553  extern int optind;
2554 
2555  while ((c = getopt(argc, argv, "f:")) != EOF ) {
2556  switch (c) {
2557  case 'f':
2558  rpmMacrofiles = optarg;
2559  break;
2560  case '?':
2561  default:
2562  errflg++;
2563  break;
2564  }
2565  }
2566  if (errflg || optind >= argc) {
2567  fprintf(stderr, "Usage: %s [-f macropath ] macro ...\n", argv[0]);
2568  exit(1);
2569  }
2570 
2571  rpmInitMacros(NULL, rpmMacrofiles);
2572  /* XXX getopt(3) also used for parametrized macros, expect scwewiness. */
2573  for ( ; optind < argc; optind++) {
2574  const char *val;
2575 
2576  val = rpmExpand(argv[optind], NULL);
2577  if (val) {
2578  fprintf(stdout, "%s:\t%s\n", argv[optind], val);
2579  val = _free(val);
2580  }
2581  }
2582  rpmFreeMacros(NULL);
2583  return 0;
2584 }
2585 
2586 #else /* !EVAL_MACROS */
2587 
2588 const char *rpmMacrofiles = "../macros:./testmacros";
2589 const char *testfile = "./test";
2590 
2591 int
2592 main(int argc, char *argv[])
2593 {
2594  size_t bufn = _macro_BUFSIZ;
2595  char *buf = alloca(bufn);
2596  FILE *fp;
2597  int x;
2598 
2599  rpmInitMacros(NULL, rpmMacrofiles);
2600 
2601  if ((fp = fopen(testfile, "r")) != NULL) {
2602  while(rdcl(buf, bufn, fp)) {
2603  x = expandMacros(NULL, NULL, buf, bufn);
2604  fprintf(stderr, "%d->%s\n", x, buf);
2605  memset(buf, 0, bufn);
2606  }
2607  fclose(fp);
2608  }
2609 
2610  while(rdcl(buf, bufn, stdin)) {
2611  x = expandMacros(NULL, NULL, buf, bufn);
2612  fprintf(stderr, "%d->%s\n <-\n", x, buf);
2613  memset(buf, 0, bufn);
2614  }
2615  rpmFreeMacros(NULL);
2616 
2617  return 0;
2618 }
2619 #endif /* EVAL_MACROS */
2620 #endif /* DEBUG_MACROS */
2621 /*@=boundsread@*/