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 
816 /*@dependent@*/ static const char *
817 doUnglobal(MacroContext mc, /*@returned@*/ const char * se)
818  /*@globals rpmGlobalMacroContext @*/
819  /*@modifies mc, rpmGlobalMacroContext @*/
820 {
821  const char *s = se;
822  char *buf = alloca(_macro_BUFSIZ);
823  char *n = buf, *ne = n;
824  int c;
825 
826  COPYNAME(ne, s, c);
827 
828  /* Move scan over body */
829  while (iseol(*s))
830  s++;
831  se = s;
832 
833  /* Names must start with alphabetic or _ and be at least 3 chars */
834  if (!((c = *n) && (xisalpha(c) || c == '_') && (ne - n) > 2)) {
836  _("Macro %%%s has illegal name (%%unglobal)\n"), n);
837  return se;
838  }
839 
840  delMacroAll(mc, n);
841 
842  return se;
843 }
844 
845 #ifdef DYING
846 static void
847 dumpME(const char * msg, MacroEntry me)
848  /*@globals fileSystem @*/
849  /*@modifies fileSystem @*/
850 {
851  if (msg)
852  fprintf(stderr, "%s", msg);
853  fprintf(stderr, "\tme %p", me);
854  if (me)
855  fprintf(stderr,"\tname %p(%s) prev %p",
856  me->name, me->name, me->prev);
857  fprintf(stderr, "\n");
858 }
859 #endif
860 
869 static void
870 pushMacro(/*@out@*/ MacroEntry * mep, const char * n, /*@null@*/ const char * o,
871  /*@null@*/ const char * b, int level)
872  /*@modifies *mep @*/
873 {
874  MacroEntry prev = (mep && *mep ? *mep : NULL);
875  MacroEntry me = (MacroEntry) xmalloc(sizeof(*me));
876  const char *name = n;
877 
878  if (*name == '.') /* XXX readonly macros */
879  name++;
880  if (*name == '.') /* XXX readonly macros */
881  name++;
882 
883  /*@-assignexpose@*/
884  me->prev = prev;
885  /*@=assignexpose@*/
886  me->name = (prev ? prev->name : xstrdup(name));
887  me->opts = (o ? xstrdup(o) : NULL);
888  me->body = xstrdup(b ? b : "");
889  me->used = 0;
890  me->level = level;
891  me->flags = (name != n);
892 /*@-boundswrite@*/
893 /*@-branchstate@*/
894  if (mep)
895  *mep = me;
896  else
897  me = _free(me);
898 /*@=branchstate@*/
899 /*@=boundswrite@*/
900 }
901 
906 static void
908  /*@modifies *mep @*/
909 {
910  MacroEntry me = (*mep ? *mep : NULL);
911 
912 /*@-branchstate@*/
913  if (me) {
914  /* XXX cast to workaround const */
915  /*@-onlytrans@*/
916 /*@-boundswrite@*/
917  if ((*mep = me->prev) == NULL)
918  me->name = _free(me->name);
919 /*@=boundswrite@*/
920  me->opts = _free(me->opts);
921  me->body = _free(me->body);
922  me = _free(me);
923  /*@=onlytrans@*/
924  }
925 /*@=branchstate@*/
926 }
927 
932 static void
934  /*@modifies mb @*/
935 {
936  MacroContext mc = mb->mc;
937  int ndeleted = 0;
938  int i;
939 
940  if (mc == NULL || mc->macroTable == NULL)
941  return;
942 
943  /* Delete dynamic macro definitions */
944  for (i = 0; i < mc->firstFree; i++) {
945  MacroEntry *mep, me;
946  int skiptest = 0;
947  mep = &mc->macroTable[i];
948  me = *mep;
949 
950  if (me == NULL) /* XXX this should never happen */
951  continue;
952  if (me->level < mb->depth)
953  continue;
954  if (strlen(me->name) == 1 && strchr("#*0", *me->name)) {
955  if (*me->name == '*' && me->used > 0)
956  skiptest = 1; /* XXX skip test for %# %* %0 */
957  } else if (!skiptest && me->used <= 0) {
958 #if NOTYET
960  _("Macro %%%s (%s) was not used below level %d\n"),
961  me->name, me->body, me->level);
962 #endif
963  }
964  popMacro(mep);
965  if (!(mep && *mep))
966  ndeleted++;
967  }
968 
969  /* If any deleted macros, sort macro table */
970  if (ndeleted)
971  sortMacroTable(mc);
972 }
973 
983 /*@-bounds@*/
984 /*@dependent@*/ static const char *
985 grabArgs(MacroBuf mb, const MacroEntry me, /*@returned@*/ const char * se,
986  const char * lastc)
987  /*@globals rpmGlobalMacroContext @*/
988  /*@modifies mb, rpmGlobalMacroContext @*/
989 {
990  size_t bufn = _macro_BUFSIZ;
991  char *buf = alloca(bufn);
992  char *b, *be;
993  char aname[16];
994  const char *opts, *o;
995  int argc = 0;
996  const char **argv;
997  int c;
998 
999  /* Copy macro name as argv[0], save beginning of args. */
1000  buf[0] = '\0';
1001  b = be = stpcpy(buf, me->name);
1002 
1003  addMacro(mb->mc, "0", NULL, buf, mb->depth);
1004 
1005  argc = 1; /* XXX count argv[0] */
1006 
1007  /* Copy args into buf until lastc */
1008  *be++ = ' ';
1009  while ((c = *se++) != '\0' && (se-1) != lastc) {
1010 /*@-globs@*/
1011  if (!isblank(c)) {
1012  *be++ = c;
1013  continue;
1014  }
1015 /*@=globs@*/
1016  /* c is blank */
1017  if (be[-1] == ' ')
1018  continue;
1019  /* a word has ended */
1020  *be++ = ' ';
1021  argc++;
1022  }
1023  if (c == '\0') se--; /* one too far */
1024  if (be[-1] != ' ')
1025  argc++, be++; /* last word has not trailing ' ' */
1026  be[-1] = '\0';
1027  if (*b == ' ') b++; /* skip the leading ' ' */
1028 
1029 /*
1030  * The macro %* analoguous to the shell's $* means "Pass all non-macro
1031  * parameters." Consequently, there needs to be a macro that means "Pass all
1032  * (including macro parameters) options". This is useful for verifying
1033  * parameters during expansion and yet transparently passing all parameters
1034  * through for higher level processing (e.g. %description and/or %setup).
1035  * This is the (potential) justification for %{**} ...
1036  */
1037  /* Add unexpanded args as macro */
1038  addMacro(mb->mc, "**", NULL, b, mb->depth);
1039 
1040 #ifdef NOTYET
1041  /* XXX if macros can be passed as args ... */
1042  expandU(mb, buf, bufn);
1043 #endif
1044 
1045  /* Build argv array */
1046  argv = (const char **) alloca((argc + 1) * sizeof(*argv));
1047  be[-1] = ' '; /* assert((be - 1) == (b + strlen(b) == buf + strlen(buf))) */
1048  be[0] = '\0';
1049  b = buf;
1050  for (c = 0; c < argc; c++) {
1051  argv[c] = b;
1052  b = strchr(b, ' ');
1053  *b++ = '\0';
1054  }
1055  /* assert(b == be); */
1056  argv[argc] = NULL;
1057 
1058  /* Citation from glibc/posix/getopt.c:
1059  * Index in ARGV of the next element to be scanned.
1060  * This is used for communication to and from the caller
1061  * and for communication between successive calls to `getopt'.
1062  *
1063  * On entry to `getopt', zero means this is the first call; initialize.
1064  *
1065  * When `getopt' returns -1, this is the index of the first of the
1066  * non-option elements that the caller should itself scan.
1067  *
1068  * Otherwise, `optind' communicates from one call to the next
1069  * how much of ARGV has been scanned so far.
1070  */
1071  /* 1003.2 says this must be 1 before any call. */
1072 
1073 #ifdef __GLIBC__
1074  /*@-mods@*/
1075  optind = 0; /* XXX but posix != glibc */
1076  /*@=mods@*/
1077 #else
1078  optind = 1;
1079 #endif
1080 
1081  opts = me->opts;
1082 
1083  /* Define option macros. */
1084 /*@-nullstate@*/ /* FIX: argv[] can be NULL */
1085  while((c = getopt(argc, (char **)argv, opts)) != -1)
1086 /*@=nullstate@*/
1087  {
1088  if (c == '?' || (o = strchr(opts, c)) == NULL) {
1089  rpmError(RPMERR_BADSPEC, _("Unknown option %c in %s(%s)\n"),
1090  (char)c, me->name, opts);
1091  return se;
1092  }
1093  *be++ = '-';
1094  *be++ = c;
1095  if (o[1] == ':') {
1096  *be++ = ' ';
1097  be = stpcpy(be, optarg);
1098  }
1099  *be++ = '\0';
1100  aname[0] = '-'; aname[1] = c; aname[2] = '\0';
1101  addMacro(mb->mc, aname, NULL, b, mb->depth);
1102  if (o[1] == ':') {
1103  aname[0] = '-'; aname[1] = c; aname[2] = '*'; aname[3] = '\0';
1104  addMacro(mb->mc, aname, NULL, optarg, mb->depth);
1105  }
1106  be = b; /* reuse the space */
1107  }
1108 
1109  /* Add arg count as macro. */
1110  sprintf(aname, "%d", (argc - optind));
1111  addMacro(mb->mc, "#", NULL, aname, mb->depth);
1112 
1113  /* Add macro for each arg. Concatenate args for %*. */
1114  if (be) {
1115  *be = '\0';
1116  for (c = optind; c < argc; c++) {
1117  sprintf(aname, "%d", (c - optind + 1));
1118  addMacro(mb->mc, aname, NULL, argv[c], mb->depth);
1119  if (be != b) *be++ = ' '; /* Add space between args */
1120 /*@-nullpass@*/ /* FIX: argv[] can be NULL */
1121  be = stpcpy(be, argv[c]);
1122 /*@=nullpass@*/
1123  }
1124  }
1125 
1126  /* Add unexpanded args as macro. */
1127  addMacro(mb->mc, "*", NULL, b, mb->depth);
1128 
1129  return se;
1130 }
1131 /*@=bounds@*/
1132 
1140 static void
1141 doOutput(MacroBuf mb, int waserror, const char * msg, size_t msglen)
1142  /*@globals rpmGlobalMacroContext, h_errno, fileSystem @*/
1143  /*@modifies mb, rpmGlobalMacroContext, fileSystem @*/
1144 {
1145  size_t bufn = _macro_BUFSIZ + msglen;
1146  char *buf = alloca(bufn);
1147 
1148  strncpy(buf, msg, msglen);
1149  buf[msglen] = '\0';
1150  (void) expandU(mb, buf, bufn);
1151  if (waserror)
1152  rpmError(RPMERR_BADSPEC, "%s\n", buf);
1153  else
1154  fprintf(stderr, "%s", buf);
1155 }
1156 
1166 static void
1167 doFoo(MacroBuf mb, int negate, const char * f, size_t fn,
1168  /*@null@*/ const char * g, size_t gn)
1169  /*@globals rpmGlobalMacroContext, h_errno, fileSystem, internalState @*/
1170  /*@modifies mb, rpmGlobalMacroContext, fileSystem, internalState @*/
1171 {
1172  size_t bufn = _macro_BUFSIZ + fn + gn;
1173  char * buf = alloca(bufn);
1174  char *b = NULL, *be;
1175  int c;
1176 
1177  buf[0] = '\0';
1178  if (g != NULL) {
1179  strncpy(buf, g, gn);
1180  buf[gn] = '\0';
1181  (void) expandU(mb, buf, bufn);
1182  }
1183 #if defined(NOTYET) /* XXX change needs parsePrep and macros changes too */
1184  if (fn > 5 && STREQ("patch", f, 5) && xisdigit(f[5])) {
1185  /* Skip leading zeros */
1186  for (c = 5; c < fn-1 && f[c] == '0' && xisdigit(f[c+1]);)
1187  c++;
1188  b = buf;
1189  be = stpncpy( stpcpy(b, "%patch -P "), f+c, fn-c);
1190  *be = '\0';
1191  } else
1192 #endif
1193  if (STREQ("basename", f, fn)) {
1194  if ((b = strrchr(buf, '/')) == NULL)
1195  b = buf;
1196  else
1197  b++;
1198  } else if (STREQ("dirname", f, fn)) {
1199  if ((b = strrchr(buf, '/')) != NULL)
1200  *b = '\0';
1201  b = buf;
1202  } else if (STREQ("suffix", f, fn)) {
1203  if ((b = strrchr(buf, '.')) != NULL)
1204  b++;
1205  } else if (STREQ("expand", f, fn)) {
1206  b = buf;
1207  } else if (STREQ("verbose", f, fn)) {
1208  if (negate)
1209  b = (rpmIsVerbose() ? NULL : buf);
1210  else
1211  b = (rpmIsVerbose() ? buf : NULL);
1212  } else if (STREQ("url2path", f, fn) || STREQ("u2p", f, fn)) {
1213  int ut = urlPath(buf, (const char **)&b);
1214  ut = ut; /* XXX quiet gcc */
1215 /*@-branchstate@*/
1216  if (*b == '\0') b = "/";
1217 /*@=branchstate@*/
1218  } else if (STREQ("uncompress", f, fn)) {
1219  rpmCompressedMagic compressed = COMPRESSED_OTHER;
1220 /*@-globs@*/
1221  for (b = buf; (c = *b) && isblank(c);)
1222  b++;
1223  for (be = b; (c = *be) && !isblank(c);)
1224  be++;
1225 /*@=globs@*/
1226  *be++ = '\0';
1227  (void) isCompressed(b, &compressed);
1228  switch(compressed) {
1229  default:
1230  case 0: /* COMPRESSED_NOT */
1231  sprintf(be, "%%__cat %s", b);
1232  break;
1233  case 1: /* COMPRESSED_OTHER */
1234  sprintf(be, "%%__gzip -dc %s", b);
1235  break;
1236  case 2: /* COMPRESSED_BZIP2 */
1237  sprintf(be, "%%__bzip2 -dc %s", b);
1238  break;
1239  case 3: /* COMPRESSED_ZIP */
1240  sprintf(be, "%%__unzip -qq %s", b);
1241  break;
1242  case 4: /* COMPRESSED_LZOP */
1243  sprintf(be, "%%__lzop %s", b);
1244  break;
1245  case 5: /* COMPRESSED_LZMA */
1246  sprintf(be, "%%__lzma %s", b);
1247  break;
1248  }
1249  b = be;
1250  } else if (STREQ("S", f, fn)) {
1251  for (b = buf; (c = *b) && xisdigit(c);)
1252  b++;
1253  if (!c) { /* digit index */
1254  b++;
1255  sprintf(b, "%%SOURCE%s", buf);
1256  } else
1257  b = buf;
1258  } else if (STREQ("P", f, fn)) {
1259  for (b = buf; (c = *b) && xisdigit(c);)
1260  b++;
1261  if (!c) { /* digit index */
1262  b++;
1263  sprintf(b, "%%PATCH%s", buf);
1264  } else
1265  b = buf;
1266  } else if (STREQ("F", f, fn)) {
1267  b = buf + strlen(buf) + 1;
1268  sprintf(b, "file%s.file", buf);
1269  }
1270 
1271  if (b) {
1272  (void) expandT(mb, b, strlen(b));
1273  }
1274 }
1275 
1282 static int
1284  /*@globals rpmGlobalMacroContext,
1285  print_macro_trace, print_expand_trace, h_errno, fileSystem @*/
1286  /*@modifies mb, rpmGlobalMacroContext,
1287  print_macro_trace, print_expand_trace, fileSystem @*/
1288 {
1289  MacroEntry *mep;
1290  MacroEntry me;
1291  const char *s = mb->s, *se;
1292  const char *f, *fe;
1293  const char *g, *ge;
1294  size_t fn, gn;
1295  char *t = mb->t; /* save expansion pointer for printExpand */
1296  int c;
1297  int rc = 0;
1298  int negate;
1299  const char * lastc;
1300  int chkexist;
1301 
1302  if (++mb->depth > max_macro_depth) {
1304  _("Recursion depth(%d) greater than max(%d)\n"),
1305  mb->depth, max_macro_depth);
1306  mb->depth--;
1307  mb->expand_trace = 1;
1308  return 1;
1309  }
1310 
1311 /*@-branchstate@*/
1312  while (rc == 0 && mb->nb > 0 && (c = *s) != '\0') {
1313  s++;
1314  /* Copy text until next macro */
1315  switch(c) {
1316  case '%':
1317  if (*s) { /* Ensure not end-of-string. */
1318  if (*s != '%')
1319  /*@switchbreak@*/ break;
1320  s++; /* skip first % in %% */
1321  }
1322  /*@fallthrough@*/
1323  default:
1324  SAVECHAR(mb, c);
1325  continue;
1326  /*@notreached@*/ /*@switchbreak@*/ break;
1327  }
1328 
1329  /* Expand next macro */
1330  f = fe = NULL;
1331  g = ge = NULL;
1332  if (mb->depth > 1) /* XXX full expansion for outermost level */
1333  t = mb->t; /* save expansion pointer for printExpand */
1334  negate = 0;
1335  lastc = NULL;
1336  chkexist = 0;
1337  switch ((c = *s)) {
1338  default: /* %name substitution */
1339  while (*s != '\0' && strchr("!?", *s) != NULL) {
1340  switch(*s++) {
1341  case '!':
1342  negate = ((negate + 1) % 2);
1343  /*@switchbreak@*/ break;
1344  case '?':
1345  chkexist++;
1346  /*@switchbreak@*/ break;
1347  }
1348  }
1349  f = se = s;
1350  if (*se == '-')
1351  se++;
1352  while((c = *se) && (xisalnum(c) || c == '_'))
1353  se++;
1354  /* Recognize non-alnum macros too */
1355  switch (*se) {
1356  case '*':
1357  se++;
1358  if (*se == '*') se++;
1359  /*@innerbreak@*/ break;
1360  case '#':
1361  se++;
1362  /*@innerbreak@*/ break;
1363  default:
1364  /*@innerbreak@*/ break;
1365  }
1366  fe = se;
1367  /* For "%name " macros ... */
1368 /*@-globs@*/
1369  if ((c = *fe) && isblank(c))
1370  if ((lastc = strchr(fe,'\n')) == NULL)
1371  lastc = strchr(fe, '\0');
1372 /*@=globs@*/
1373  /*@switchbreak@*/ break;
1374  case '(': /* %(...) shell escape */
1375  if ((se = matchchar(s, c, ')')) == NULL) {
1377  _("Unterminated %c: %s\n"), (char)c, s);
1378  rc = 1;
1379  continue;
1380  }
1381  if (mb->macro_trace)
1382  printMacro(mb, s, se+1);
1383 
1384  s++; /* skip ( */
1385  rc = doShellEscape(mb, s, (se - s));
1386  se++; /* skip ) */
1387 
1388  s = se;
1389  continue;
1390  /*@notreached@*/ /*@switchbreak@*/ break;
1391  case '{': /* %{...}/%{...:...} substitution */
1392  if ((se = matchchar(s, c, '}')) == NULL) {
1394  _("Unterminated %c: %s\n"), (char)c, s);
1395  rc = 1;
1396  continue;
1397  }
1398  f = s+1;/* skip { */
1399  se++; /* skip } */
1400  while (strchr("!?", *f) != NULL) {
1401  switch(*f++) {
1402  case '!':
1403  negate = ((negate + 1) % 2);
1404  /*@switchbreak@*/ break;
1405  case '?':
1406  chkexist++;
1407  /*@switchbreak@*/ break;
1408  }
1409  }
1410  /* Find end-of-expansion, handle %{foo:bar} expansions. */
1411  for (fe = f; (c = *fe) && !strchr(" :}", c);)
1412  fe++;
1413  switch (c) {
1414  case ':':
1415  g = fe + 1;
1416  ge = se - 1;
1417  /*@innerbreak@*/ break;
1418  case ' ':
1419  lastc = se-1;
1420  /*@innerbreak@*/ break;
1421  default:
1422  /*@innerbreak@*/ break;
1423  }
1424  /*@switchbreak@*/ break;
1425  }
1426 
1427  /* XXX Everything below expects fe > f */
1428  fn = (fe - f);
1429  gn = (ge - g);
1430  if ((fe - f) <= 0) {
1431 /* XXX Process % in unknown context */
1432  c = '%'; /* XXX only need to save % */
1433  SAVECHAR(mb, c);
1434 #if 0
1436  _("A %% is followed by an unparseable macro\n"));
1437 #endif
1438  s = se;
1439  continue;
1440  }
1441 
1442  if (mb->macro_trace)
1443  printMacro(mb, s, se);
1444 
1445  /* Expand builtin macros */
1446  if (STREQ("load", f, fn)) {
1447  if (g != NULL) {
1448  char * mfn = strncpy(alloca(gn + 1), g, gn);
1449  int xx;
1450  mfn[gn] = '\0';
1451  xx = rpmLoadMacroFile(NULL, mfn);
1452  }
1453  s = se;
1454  continue;
1455  }
1456  if (STREQ("global", f, fn)) {
1457  s = doDefine(mb, se, RMIL_GLOBAL, 1);
1458  continue;
1459  }
1460  if (STREQ("define", f, fn)) {
1461  s = doDefine(mb, se, mb->depth, 0);
1462  continue;
1463  }
1464  if (STREQ("undefine", f, fn)) {
1465  s = doUndefine(mb->mc, se);
1466  continue;
1467  }
1468  if (STREQ("unglobal", f, fn)) {
1469  s = doUnglobal(mb->mc, se);
1470  continue;
1471  }
1472 
1473  if (STREQ("echo", f, fn) ||
1474  STREQ("warn", f, fn) ||
1475  STREQ("error", f, fn)) {
1476  int waserror = 0;
1477  if (STREQ("error", f, fn))
1478  waserror = 1;
1479  if (g != NULL && g < ge)
1480  doOutput(mb, waserror, g, gn);
1481  else
1482  doOutput(mb, waserror, f, fn);
1483  s = se;
1484  continue;
1485  }
1486 
1487  if (STREQ("trace", f, fn)) {
1488  /* XXX TODO restore expand_trace/macro_trace to 0 on return */
1489  mb->expand_trace = mb->macro_trace = (negate ? 0 : mb->depth);
1490  if (mb->depth == 1) {
1493  }
1494  s = se;
1495  continue;
1496  }
1497 
1498  if (STREQ("dump", f, fn)) {
1499  rpmDumpMacroTable(mb->mc, NULL);
1500  while (iseol(*se))
1501  se++;
1502  s = se;
1503  continue;
1504  }
1505 
1506 #ifdef WITH_LUA
1507  if (STREQ("lua", f, fn)) {
1508  rpmlua lua = NULL; /* Global state. */
1509  const char *ls = s+sizeof("{lua:")-1;
1510  const char *lse = se-sizeof("}")+1;
1511  char *scriptbuf = (char *)xmalloc((lse-ls)+1);
1512  const char *printbuf;
1513  memcpy(scriptbuf, ls, lse-ls);
1514  scriptbuf[lse-ls] = '\0';
1515  rpmluaSetPrintBuffer(lua, 1);
1516  if (rpmluaRunScript(lua, scriptbuf, NULL) == -1)
1517  rc = 1;
1518  printbuf = rpmluaGetPrintBuffer(lua);
1519  if (printbuf) {
1520  int len = strlen(printbuf);
1521  if (len > mb->nb)
1522  len = mb->nb;
1523  memcpy(mb->t, printbuf, len);
1524  mb->t += len;
1525  mb->nb -= len;
1526  }
1527  rpmluaSetPrintBuffer(lua, 0);
1528  free(scriptbuf);
1529  s = se;
1530  continue;
1531  }
1532 #endif
1533 
1534 #if defined(NOTYET) /* XXX change needs parsePrep and macros changes too */
1535  /* Rewrite "%patchNN ..." as "%patch -P NN ..." and expand. */
1536  if (lastc != NULL && fn > 5 && STREQ("patch", f, 5) && xisdigit(f[5])) {
1537  /*@-internalglobs@*/ /* FIX: verbose may be set */
1538  doFoo(mb, negate, f, (lastc - f), NULL, 0);
1539  /*@=internalglobs@*/
1540  s = lastc;
1541  continue;
1542  }
1543 #endif
1544 
1545  /* XXX necessary but clunky */
1546  if (STREQ("basename", f, fn) ||
1547  STREQ("dirname", f, fn) ||
1548  STREQ("suffix", f, fn) ||
1549  STREQ("expand", f, fn) ||
1550  STREQ("verbose", f, fn) ||
1551  STREQ("uncompress", f, fn) ||
1552  STREQ("url2path", f, fn) ||
1553  STREQ("u2p", f, fn) ||
1554  STREQ("S", f, fn) ||
1555  STREQ("P", f, fn) ||
1556  STREQ("F", f, fn)) {
1557  /*@-internalglobs@*/ /* FIX: verbose may be set */
1558  doFoo(mb, negate, f, fn, g, gn);
1559  /*@=internalglobs@*/
1560  s = se;
1561  continue;
1562  }
1563 
1564  /* Expand defined macros */
1565  mep = findEntry(mb->mc, f, fn);
1566  me = (mep ? *mep : NULL);
1567 
1568  /* XXX Special processing for flags */
1569  if (*f == '-') {
1570  if (me)
1571  me->used++; /* Mark macro as used */
1572  if ((me == NULL && !negate) || /* Without -f, skip %{-f...} */
1573  (me != NULL && negate)) { /* With -f, skip %{!-f...} */
1574  s = se;
1575  continue;
1576  }
1577 
1578  if (g && g < ge) { /* Expand X in %{-f:X} */
1579  rc = expandT(mb, g, gn);
1580  } else
1581  if (me && me->body && *me->body) {/* Expand %{-f}/%{-f*} */
1582  rc = expandT(mb, me->body, strlen(me->body));
1583  }
1584  s = se;
1585  continue;
1586  }
1587 
1588  /* XXX Special processing for macro existence */
1589  if (chkexist) {
1590  if ((me == NULL && !negate) || /* Without -f, skip %{?f...} */
1591  (me != NULL && negate)) { /* With -f, skip %{!?f...} */
1592  s = se;
1593  continue;
1594  }
1595  if (g && g < ge) { /* Expand X in %{?f:X} */
1596  rc = expandT(mb, g, gn);
1597  } else
1598  if (me && me->body && *me->body) { /* Expand %{?f}/%{?f*} */
1599  rc = expandT(mb, me->body, strlen(me->body));
1600  }
1601  s = se;
1602  continue;
1603  }
1604 
1605  if (me == NULL) { /* leave unknown %... as is */
1606 #ifndef HACK
1607 #if DEAD
1608  /* XXX hack to skip over empty arg list */
1609  if (fn == 1 && *f == '*') {
1610  s = se;
1611  continue;
1612  }
1613 #endif
1614  /* XXX hack to permit non-overloaded %foo to be passed */
1615  c = '%'; /* XXX only need to save % */
1616  SAVECHAR(mb, c);
1617 #else
1618  if (!strncmp(f, "if", fn) ||
1619  !strncmp(f, "else", fn) ||
1620  !strncmp(f, "endif", fn)) {
1621  c = '%'; /* XXX only need to save % */
1622  SAVECHAR(mb, c);
1623  } else {
1625  _("Macro %%%.*s not found, skipping\n"), fn, f);
1626  s = se;
1627  }
1628 #endif
1629  continue;
1630  }
1631 
1632  /* Setup args for "%name " macros with opts */
1633  if (me && me->opts != NULL) {
1634  if (lastc != NULL) {
1635  se = grabArgs(mb, me, fe, lastc);
1636  } else {
1637  addMacro(mb->mc, "**", NULL, "", mb->depth);
1638  addMacro(mb->mc, "*", NULL, "", mb->depth);
1639  addMacro(mb->mc, "#", NULL, "0", mb->depth);
1640  addMacro(mb->mc, "0", NULL, me->name, mb->depth);
1641  }
1642  }
1643 
1644  /* Recursively expand body of macro */
1645  if (me->body && *me->body) {
1646  mb->s = me->body;
1647  rc = expandMacro(mb);
1648  if (rc == 0)
1649  me->used++; /* Mark macro as used */
1650  }
1651 
1652  /* Free args for "%name " macros with opts */
1653  if (me->opts != NULL)
1654  freeArgs(mb);
1655 
1656  s = se;
1657  }
1658 /*@=branchstate@*/
1659 
1660  *mb->t = '\0';
1661  mb->s = s;
1662  mb->depth--;
1663  if (rc != 0 || mb->expand_trace)
1664  printExpansion(mb, t, mb->t);
1665  return rc;
1666 }
1667 
1668 #if !defined(DEBUG_MACROS)
1669 /* =============================================================== */
1670 /* XXX dupe'd to avoid change in linkage conventions. */
1671 
1672 #define POPT_ERROR_NOARG -10
1673 #define POPT_ERROR_BADQUOTE -15
1674 #define POPT_ERROR_MALLOC -21
1676 #define POPT_ARGV_ARRAY_GROW_DELTA 5
1677 
1678 /*@-boundswrite@*/
1679 static int XpoptDupArgv(int argc, const char **argv,
1680  int * argcPtr, const char *** argvPtr)
1681  /*@modifies *argcPtr, *argvPtr @*/
1682 {
1683  size_t nb = (argc + 1) * sizeof(*argv);
1684  const char ** argv2;
1685  char * dst;
1686  int i;
1687 
1688  if (argc <= 0 || argv == NULL) /* XXX can't happen */
1689  return POPT_ERROR_NOARG;
1690  for (i = 0; i < argc; i++) {
1691  if (argv[i] == NULL)
1692  return POPT_ERROR_NOARG;
1693  nb += strlen(argv[i]) + 1;
1694  }
1695 
1696  dst = malloc(nb);
1697  if (dst == NULL) /* XXX can't happen */
1698  return POPT_ERROR_MALLOC;
1699  argv2 = (void *) dst;
1700  dst += (argc + 1) * sizeof(*argv);
1701 
1702  /*@-branchstate@*/
1703  for (i = 0; i < argc; i++) {
1704  argv2[i] = dst;
1705  dst += strlen(strcpy(dst, argv[i])) + 1;
1706  }
1707  /*@=branchstate@*/
1708  argv2[argc] = NULL;
1709 
1710  if (argvPtr) {
1711  *argvPtr = argv2;
1712  } else {
1713  free(argv2);
1714  argv2 = NULL;
1715  }
1716  if (argcPtr)
1717  *argcPtr = argc;
1718  return 0;
1719 }
1720 /*@=boundswrite@*/
1721 
1722 /*@-bounds@*/
1723 static int XpoptParseArgvString(const char * s, int * argcPtr, const char *** argvPtr)
1724  /*@modifies *argcPtr, *argvPtr @*/
1725 {
1726  const char * src;
1727  char quote = '\0';
1728  int argvAlloced = POPT_ARGV_ARRAY_GROW_DELTA;
1729  const char ** argv = malloc(sizeof(*argv) * argvAlloced);
1730  int argc = 0;
1731  int buflen = strlen(s) + 1;
1732  char * buf = memset(alloca(buflen), 0, buflen);
1733  int rc = POPT_ERROR_MALLOC;
1734 
1735  if (argv == NULL) return rc;
1736  argv[argc] = buf;
1737 
1738  for (src = s; *src != '\0'; src++) {
1739  if (quote == *src) {
1740  quote = '\0';
1741  } else if (quote != '\0') {
1742  if (*src == '\\') {
1743  src++;
1744  if (!*src) {
1745  rc = POPT_ERROR_BADQUOTE;
1746  goto exit;
1747  }
1748  if (*src != quote) *buf++ = '\\';
1749  }
1750  *buf++ = *src;
1751  } else if (isspace(*src)) {
1752  if (*argv[argc] != '\0') {
1753  buf++, argc++;
1754  if (argc == argvAlloced) {
1755  argvAlloced += POPT_ARGV_ARRAY_GROW_DELTA;
1756  argv = realloc(argv, sizeof(*argv) * argvAlloced);
1757  if (argv == NULL) goto exit;
1758  }
1759  argv[argc] = buf;
1760  }
1761  } else switch (*src) {
1762  case '"':
1763  case '\'':
1764  quote = *src;
1765  /*@switchbreak@*/ break;
1766  case '\\':
1767  src++;
1768  if (!*src) {
1769  rc = POPT_ERROR_BADQUOTE;
1770  goto exit;
1771  }
1772  /*@fallthrough@*/
1773  default:
1774  *buf++ = *src;
1775  /*@switchbreak@*/ break;
1776  }
1777  }
1778 
1779  if (strlen(argv[argc])) {
1780  argc++, buf++;
1781  }
1782 
1783  rc = XpoptDupArgv(argc, argv, argcPtr, argvPtr);
1784 
1785 exit:
1786  if (argv) free(argv);
1787  return rc;
1788 }
1789 /*@=bounds@*/
1790 /* =============================================================== */
1791 /*@unchecked@*/
1792 static int _debug = 0;
1793 
1794 int rpmGlob(const char * patterns, int * argcPtr, const char *** argvPtr)
1795 {
1796  int ac = 0;
1797  const char ** av = NULL;
1798  int argc = 0;
1799  const char ** argv = NULL;
1800  char * globRoot = NULL;
1801 #ifdef ENABLE_NLS
1802  const char * old_collate = NULL;
1803  const char * old_ctype = NULL;
1804  const char * t;
1805 #endif
1806  size_t maxb, nb;
1807  int i, j;
1808  int rc;
1809 
1810  rc = XpoptParseArgvString(patterns, &ac, &av);
1811  if (rc)
1812  return rc;
1813 #ifdef ENABLE_NLS
1814 /*@-branchstate@*/
1815  t = setlocale(LC_COLLATE, NULL);
1816  if (t)
1817  old_collate = xstrdup(t);
1818  t = setlocale(LC_CTYPE, NULL);
1819  if (t)
1820  old_ctype = xstrdup(t);
1821 /*@=branchstate@*/
1822  (void) setlocale(LC_COLLATE, "C");
1823  (void) setlocale(LC_CTYPE, "C");
1824 #endif
1825 
1826  if (av != NULL)
1827  for (j = 0; j < ac; j++) {
1828  const char * globURL;
1829  const char * path;
1830  int ut = urlPath(av[j], &path);
1831  glob_t gl;
1832 
1833  if (!Glob_pattern_p(av[j], 0) && strchr(path, '~') == NULL) {
1834  argv = xrealloc(argv, (argc+2) * sizeof(*argv));
1835  argv[argc] = xstrdup(av[j]);
1836 if (_debug)
1837 fprintf(stderr, "*** rpmGlob argv[%d] \"%s\"\n", argc, argv[argc]);
1838  argc++;
1839  continue;
1840  }
1841 
1842  gl.gl_pathc = 0;
1843  gl.gl_pathv = NULL;
1844  rc = Glob(av[j], GLOB_TILDE, Glob_error, &gl);
1845  if (rc)
1846  goto exit;
1847 
1848  /* XXX Prepend the URL leader for globs that have stripped it off */
1849  maxb = 0;
1850  for (i = 0; i < gl.gl_pathc; i++) {
1851  if ((nb = strlen(&(gl.gl_pathv[i][0]))) > maxb)
1852  maxb = nb;
1853  }
1854 
1855  nb = ((ut == URL_IS_PATH) ? (path - av[j]) : 0);
1856  maxb += nb;
1857  maxb += 1;
1858  globURL = globRoot = xmalloc(maxb);
1859 
1860  switch (ut) {
1861  case URL_IS_PATH:
1862  case URL_IS_DASH:
1863  strncpy(globRoot, av[j], nb);
1864  /*@switchbreak@*/ break;
1865  case URL_IS_HTTPS:
1866  case URL_IS_HTTP:
1867  case URL_IS_FTP:
1868  case URL_IS_HKP:
1869  case URL_IS_UNKNOWN:
1870  default:
1871  /*@switchbreak@*/ break;
1872  }
1873  globRoot += nb;
1874  *globRoot = '\0';
1875 if (_debug)
1876 fprintf(stderr, "*** GLOB maxb %d diskURL %d %*s globURL %p %s\n", (int)maxb, (int)nb, (int)nb, av[j], globURL, globURL);
1877 
1878  argv = xrealloc(argv, (argc+gl.gl_pathc+1) * sizeof(*argv));
1879 
1880  if (argv != NULL)
1881  for (i = 0; i < gl.gl_pathc; i++) {
1882  const char * globFile = &(gl.gl_pathv[i][0]);
1883  if (globRoot > globURL && globRoot[-1] == '/')
1884  while (*globFile == '/') globFile++;
1885  strcpy(globRoot, globFile);
1886 if (_debug)
1887 fprintf(stderr, "*** rpmGlob argv[%d] \"%s\"\n", argc, globURL);
1888  argv[argc++] = xstrdup(globURL);
1889  }
1890  /*@-immediatetrans@*/
1891  Globfree(&gl);
1892  /*@=immediatetrans@*/
1893  globURL = _free(globURL);
1894  }
1895 
1896  if (argv != NULL && argc > 0) {
1897  argv[argc] = NULL;
1898  if (argvPtr)
1899  *argvPtr = argv;
1900  if (argcPtr)
1901  *argcPtr = argc;
1902  rc = 0;
1903  } else
1904  rc = 1;
1905 
1906 
1907 exit:
1908 #ifdef ENABLE_NLS
1909 /*@-branchstate@*/
1910  if (old_collate) {
1911  (void) setlocale(LC_COLLATE, old_collate);
1912  old_collate = _free(old_collate);
1913  }
1914  if (old_ctype) {
1915  (void) setlocale(LC_CTYPE, old_ctype);
1916  old_ctype = _free(old_ctype);
1917  }
1918 /*@=branchstate@*/
1919 #endif
1920  av = _free(av);
1921 /*@-branchstate@*/
1922  if (rc || argvPtr == NULL) {
1923 /*@-dependenttrans -unqualifiedtrans@*/
1924  if (argv != NULL)
1925  for (i = 0; i < argc; i++)
1926  argv[i] = _free(argv[i]);
1927  argv = _free(argv);
1928 /*@=dependenttrans =unqualifiedtrans@*/
1929  }
1930 /*@=branchstate@*/
1931  return rc;
1932 }
1933 #endif /* !defined(DEBUG_MACROS) */
1934 
1935 /* =============================================================== */
1936 
1937 int
1938 expandMacros(void * spec, MacroContext mc, char * sbuf, size_t slen)
1939 {
1940  MacroBuf mb = alloca(sizeof(*mb));
1941  char *tbuf;
1942  int rc;
1943 
1944  if (sbuf == NULL || slen == 0)
1945  return 0;
1946  if (mc == NULL) mc = rpmGlobalMacroContext;
1947 
1948  tbuf = alloca(slen + 1);
1949  memset(tbuf, 0, (slen + 1));
1950 
1951  mb->s = sbuf;
1952  mb->t = tbuf;
1953  mb->nb = slen;
1954  mb->depth = 0;
1957 
1958  mb->spec = spec; /* (future) %file expansion info */
1959  mb->mc = mc;
1960 
1961  rc = expandMacro(mb);
1962 
1963  tbuf[slen] = '\0';
1964  if (mb->nb == 0)
1965  rpmError(RPMERR_BADSPEC, _("Macro expansion too big for target buffer\n"));
1966  else
1967  strncpy(sbuf, tbuf, (slen - mb->nb + 1));
1968 
1969  return rc;
1970 }
1971 
1972 void
1974  const char * n, const char * o, const char * b, int level)
1975 {
1976  MacroEntry * mep;
1977  const char * name = n;
1978 
1979  if (*name == '.') /* XXX readonly macros */
1980  name++;
1981  if (*name == '.') /* XXX readonly macros */
1982  name++;
1983 
1984  if (mc == NULL) mc = rpmGlobalMacroContext;
1985 
1986  /* If new name, expand macro table */
1987  if ((mep = findEntry(mc, name, 0)) == NULL) {
1988  if (mc->firstFree == mc->macrosAllocated)
1989  expandMacroTable(mc);
1990  if (mc->macroTable != NULL)
1991  mep = mc->macroTable + mc->firstFree++;
1992  }
1993 
1994  if (mep != NULL) {
1995  /* XXX permit "..foo" to be pushed over ".foo" */
1996  if (*mep && (*mep)->flags && !(n[0] == '.' && n[1] == '.')) {
1997  /* XXX avoid error message for %buildroot */
1998  if (strcmp((*mep)->name, "buildroot"))
1999  rpmError(RPMERR_BADSPEC, _("Macro '%s' is readonly and cannot be changed.\n"), n);
2000  return;
2001  }
2002  /* Push macro over previous definition */
2003  pushMacro(mep, n, o, b, level);
2004 
2005  /* If new name, sort macro table */
2006  if ((*mep)->prev == NULL)
2007  sortMacroTable(mc);
2008  }
2009 }
2010 
2011 void
2012 delMacro(MacroContext mc, const char * n)
2013 {
2014  MacroEntry * mep;
2015 
2016  if (mc == NULL) mc = rpmGlobalMacroContext;
2017  /* If name exists, pop entry */
2018  if ((mep = findEntry(mc, n, 0)) != NULL) {
2019  popMacro(mep);
2020  /* If deleted name, sort macro table */
2021  if (!(mep && *mep))
2022  sortMacroTable(mc);
2023  }
2024 }
2025 
2026 void
2027 delMacroAll(MacroContext mc, const char * n)
2028 {
2029  MacroEntry * mep;
2030 
2031  if (mc == NULL) mc = rpmGlobalMacroContext;
2032  /* If name exists, pop entry */
2033  while ((mep = findEntry(mc, n, 0)) != NULL) {
2034  delMacro(mc, n);
2035  }
2036 }
2037 
2038 /*@-mustmod@*/ /* LCL: mc is modified through mb->mc, mb is abstract */
2039 int
2040 rpmDefineMacro(MacroContext mc, const char * macro, int level)
2041 {
2042  MacroBuf mb = alloca(sizeof(*mb));
2043 
2044  memset(mb, 0, sizeof(*mb));
2045  /* XXX just enough to get by */
2046  mb->mc = (mc ? mc : rpmGlobalMacroContext);
2047  (void) doDefine(mb, macro, level, 0);
2048  return 0;
2049 }
2050 /*@=mustmod@*/
2051 
2052 void
2054 {
2055 
2056  if (mc == NULL || mc == rpmGlobalMacroContext)
2057  return;
2058 
2059  if (mc->macroTable != NULL) {
2060  int i;
2061  for (i = 0; i < mc->firstFree; i++) {
2062  MacroEntry *mep, me;
2063  mep = &mc->macroTable[i];
2064  me = *mep;
2065 
2066  if (me == NULL) /* XXX this should never happen */
2067  continue;
2068  addMacro(NULL, me->name, me->opts, me->body, (level - 1));
2069  }
2070  }
2071 }
2072 
2073 int
2074 rpmLoadMacroFile(MacroContext mc, const char * fn)
2075 {
2076  FD_t fd = Fopen(fn, "r.fpio");
2077  size_t bufn = _macro_BUFSIZ;
2078  char *buf = alloca(bufn);
2079  int rc = -1;
2080 
2081  if (fd == NULL || Ferror(fd)) {
2082  if (fd) (void) Fclose(fd);
2083  return rc;
2084  }
2085 
2086  /* XXX Assume new fangled macro expansion */
2087  /*@-mods@*/
2089  /*@=mods@*/
2090 
2091  buf[0] = '\0';
2092  while(rdcl(buf, bufn, fd) != NULL) {
2093  char c, *n;
2094 
2095  n = buf;
2096  SKIPBLANK(n, c);
2097 
2098  if (c != '%')
2099  continue;
2100  n++; /* skip % */
2101  rc = rpmDefineMacro(mc, n, RMIL_MACROFILES);
2102  }
2103  rc = Fclose(fd);
2104  return rc;
2105 }
2106 
2107 void
2108 rpmInitMacros(MacroContext mc, const char * macrofiles)
2109 {
2110  char *mfiles, *m, *me;
2111 
2112  if (macrofiles == NULL)
2113  return;
2114 #ifdef DYING
2115  if (mc == NULL) mc = rpmGlobalMacroContext;
2116 #endif
2117 
2118  mfiles = xstrdup(macrofiles);
2119  for (m = mfiles; m && *m != '\0'; m = me) {
2120  const char ** av;
2121  int ac;
2122  int i;
2123 
2124  for (me = m; (me = strchr(me, ':')) != NULL; me++) {
2125  /* Skip over URI's. */
2126  if (!(me[1] == '/' && me[2] == '/'))
2127  /*@innerbreak@*/ break;
2128  }
2129 
2130  if (me && *me == ':')
2131  *me++ = '\0';
2132  else
2133  me = m + strlen(m);
2134 
2135  /* Glob expand the macro file path element, expanding ~ to $HOME. */
2136  ac = 0;
2137  av = NULL;
2138 #if defined(DEBUG_MACROS)
2139  ac = 1;
2140  av = xmalloc((ac + 1) * sizeof(*av));
2141  av[0] = strdup(m);
2142  av[1] = NULL;
2143 #else
2144  i = rpmGlob(m, &ac, &av);
2145  if (i != 0)
2146  continue;
2147 #endif
2148 
2149  /* Read macros from each file. */
2150 
2151  for (i = 0; i < ac; i++) {
2152  size_t slen = strlen(av[i]);
2153 
2154  /* Skip backup files and %config leftovers. */
2155 #define _suffix(_s, _x) \
2156  (slen >= sizeof(_x) && !strcmp((_s)+slen-(sizeof(_x)-1), (_x)))
2157  if (!(_suffix(av[i], "~")
2158  || _suffix(av[i], ".rpmnew")
2159  || _suffix(av[i], ".rpmorig")
2160  || _suffix(av[i], ".rpmsave"))
2161  )
2162  (void) rpmLoadMacroFile(mc, av[i]);
2163 #undef _suffix
2164 
2165  av[i] = _free(av[i]);
2166  }
2167  av = _free(av);
2168  }
2169  mfiles = _free(mfiles);
2170 
2171  /* Reload cmdline macros */
2172  /*@-mods@*/
2173  rpmLoadMacros(rpmCLIMacroContext, RMIL_CMDLINE);
2174  /*@=mods@*/
2175 }
2176 
2177 /*@-globstate@*/
2178 void
2180 {
2181 
2182  if (mc == NULL) mc = rpmGlobalMacroContext;
2183 
2184  if (mc->macroTable != NULL) {
2185  int i;
2186  for (i = 0; i < mc->firstFree; i++) {
2187  MacroEntry me;
2188  while ((me = mc->macroTable[i]) != NULL) {
2189  /* XXX cast to workaround const */
2190  /*@-onlytrans@*/
2191  if ((mc->macroTable[i] = me->prev) == NULL)
2192  me->name = _free(me->name);
2193  /*@=onlytrans@*/
2194  me->opts = _free(me->opts);
2195  me->body = _free(me->body);
2196  me = _free(me);
2197  }
2198  }
2199  mc->macroTable = _free(mc->macroTable);
2200  }
2201  memset(mc, 0, sizeof(*mc));
2202 }
2203 /*@=globstate@*/
2204 
2205 /* =============================================================== */
2206 int isCompressed(const char * file, rpmCompressedMagic * compressed)
2207 {
2208  FD_t fd;
2209  ssize_t nb;
2210  int rc = -1;
2211  unsigned char magic[13];
2212  char *end, *ext;
2213 
2214  *compressed = COMPRESSED_NOT;
2215 
2216  fd = Fopen(file, "r");
2217  if (fd == NULL || Ferror(fd)) {
2218  /* XXX Fstrerror */
2219  rpmError(RPMERR_BADSPEC, _("File %s: %s\n"), file, Fstrerror(fd));
2220  if (fd) (void) Fclose(fd);
2221  return 1;
2222  }
2223  nb = Fread(magic, sizeof(magic[0]), sizeof(magic), fd);
2224  if (nb < 0) {
2225  rpmError(RPMERR_BADSPEC, _("File %s: %s\n"), file, Fstrerror(fd));
2226  rc = 1;
2227  } else if (nb < sizeof(magic)) {
2228  rpmError(RPMERR_BADSPEC, _("File %s is smaller than %u bytes\n"),
2229  file, (unsigned)sizeof(magic));
2230  rc = 0;
2231  }
2232  (void) Fclose(fd);
2233  if (rc >= 0)
2234  return rc;
2235 
2236  rc = 0;
2237 
2238  /* Tar archives will be recognized by filename. */
2239  end = strchr(file, '\0');
2240  ext = end - 4;
2241  if (ext > file && !strcasecmp(ext, ".tar")) return rc;
2242 
2243  if (magic[0] == 'B' && magic[1] == 'Z')
2244  *compressed = COMPRESSED_BZIP2;
2245  else
2246  if (magic[0] == 0120 && magic[1] == 0113
2247  && magic[2] == 0003 && magic[3] == 0004) /* pkzip */
2248  *compressed = COMPRESSED_ZIP;
2249  else
2250  if (magic[0] == 0x89 && magic[1] == 'L'
2251  && magic[2] == 'Z' && magic[3] == 'O') /* lzop */
2252  *compressed = COMPRESSED_LZOP;
2253  else
2254  /* XXX Ick, LZMA has no magic. See http://lkml.org/lkml/2005/6/13/285 */
2255  if (magic[ 9] == 0x00 && magic[10] == 0x00 &&
2256  magic[11] == 0x00 && magic[12] == 0x00) /* lzmash */
2257  *compressed = COMPRESSED_LZMA;
2258  else
2259  if ((magic[0] == 0037 && magic[1] == 0213) /* gzip */
2260  || (magic[0] == 0037 && magic[1] == 0236) /* old gzip */
2261  || (magic[0] == 0037 && magic[1] == 0036) /* pack */
2262  || (magic[0] == 0037 && magic[1] == 0240) /* SCO lzh */
2263  || (magic[0] == 0037 && magic[1] == 0235)) /* compress */
2264  *compressed = COMPRESSED_OTHER;
2265 
2266  return rc;
2267 }
2268 
2269 /* =============================================================== */
2270 
2271 /*@-modfilesys@*/
2272 char *
2273 rpmExpand(const char *arg, ...)
2274 {
2275  const char *s;
2276  char *t, *te;
2277  size_t sn, tn;
2278  size_t bufn = 8 * _macro_BUFSIZ;
2279 
2280  va_list ap;
2281 
2282  if (arg == NULL)
2283  return xstrdup("");
2284 
2285  t = xmalloc(bufn + strlen(arg) + 1);
2286  *t = '\0';
2287  te = stpcpy(t, arg);
2288 
2289 /*@-branchstate@*/
2290  va_start(ap, arg);
2291  while ((s = va_arg(ap, const char *)) != NULL) {
2292  sn = strlen(s);
2293  tn = (te - t);
2294  t = xrealloc(t, tn + sn + bufn + 1);
2295  te = t + tn;
2296  te = stpcpy(te, s);
2297  }
2298  va_end(ap);
2299 /*@=branchstate@*/
2300 
2301  *te = '\0';
2302  tn = (te - t);
2303  (void) expandMacros(NULL, NULL, t, tn + bufn + 1);
2304  t[tn + bufn] = '\0';
2305  t = xrealloc(t, strlen(t) + 1);
2306 
2307  return t;
2308 }
2309 /*@=modfilesys@*/
2310 
2311 int
2312 rpmExpandNumeric(const char *arg)
2313 {
2314  const char *val;
2315  int rc;
2316 
2317  if (arg == NULL)
2318  return 0;
2319 
2320  val = rpmExpand(arg, NULL);
2321  if (!(val && *val != '%'))
2322  rc = 0;
2323  else if (*val == 'Y' || *val == 'y')
2324  rc = 1;
2325  else if (*val == 'N' || *val == 'n')
2326  rc = 0;
2327  else {
2328  char *end;
2329  rc = strtol(val, &end, 0);
2330  if (!(end && *end == '\0'))
2331  rc = 0;
2332  }
2333  val = _free(val);
2334 
2335  return rc;
2336 }
2337 
2338 /* @todo "../sbin/./../bin/" not correct. */
2339 char *rpmCleanPath(char * path)
2340 {
2341  const char *s;
2342  char *se, *t, *te;
2343  int begin = 1;
2344 
2345  if (path == NULL)
2346  return NULL;
2347 
2348 /*fprintf(stderr, "*** RCP %s ->\n", path); */
2349  s = t = te = path;
2350  while (*s != '\0') {
2351 /*fprintf(stderr, "*** got \"%.*s\"\trest \"%s\"\n", (t-path), path, s); */
2352  switch(*s) {
2353  case ':': /* handle url's */
2354  if (s[1] == '/' && s[2] == '/') {
2355  *t++ = *s++;
2356  *t++ = *s++;
2357  /* XXX handle "file:///" */
2358  if (s[0] == '/') *t++ = *s++;
2359  te = t;
2360  /*@switchbreak@*/ break;
2361  }
2362  begin=1;
2363  /*@switchbreak@*/ break;
2364  case '/':
2365  /* Move parent dir forward */
2366  for (se = te + 1; se < t && *se != '/'; se++)
2367  {};
2368  if (se < t && *se == '/') {
2369  te = se;
2370 /*fprintf(stderr, "*** next pdir \"%.*s\"\n", (te-path), path); */
2371  }
2372  while (s[1] == '/')
2373  s++;
2374  while (t > te && t[-1] == '/')
2375  t--;
2376  /*@switchbreak@*/ break;
2377  case '.':
2378  /* Leading .. is special */
2379  /* Check that it is ../, so that we don't interpret */
2380  /* ..?(i.e. "...") or ..* (i.e. "..bogus") as "..". */
2381  /* in the case of "...", this ends up being processed*/
2382  /* as "../.", and the last '.' is stripped. This */
2383  /* would not be correct processing. */
2384  if (begin && s[1] == '.' && (s[2] == '/' || s[2] == '\0')) {
2385 /*fprintf(stderr, " leading \"..\"\n"); */
2386  *t++ = *s++;
2387  /*@switchbreak@*/ break;
2388  }
2389  /* Single . is special */
2390  if (begin && s[1] == '\0') {
2391  /*@switchbreak@*/ break;
2392  }
2393  /* Trim embedded ./ , trailing /. */
2394  if ((t[-1] == '/' && s[1] == '\0') || (t > path && t[-1] == '/' && s[1] == '/')) {
2395  s++;
2396  continue;
2397  }
2398  /* Trim embedded /../ and trailing /.. */
2399  if (!begin && t > path && t[-1] == '/' && s[1] == '.' && (s[2] == '/' || s[2] == '\0')) {
2400  t = te;
2401  /* Move parent dir forward */
2402  if (te > path)
2403  for (--te; te > path && *te != '/'; te--)
2404  {};
2405 /*fprintf(stderr, "*** prev pdir \"%.*s\"\n", (te-path), path); */
2406  s++;
2407  s++;
2408  continue;
2409  }
2410  /*@switchbreak@*/ break;
2411  default:
2412  begin = 0;
2413  /*@switchbreak@*/ break;
2414  }
2415  *t++ = *s++;
2416  }
2417 
2418  /* Trim trailing / (but leave single / alone) */
2419  if (t > &path[1] && t[-1] == '/')
2420  t--;
2421  *t = '\0';
2422 
2423 /*fprintf(stderr, "\t%s\n", path); */
2424  return path;
2425 }
2426 
2427 /* Return concatenated and expanded canonical path. */
2428 
2429 const char *
2430 rpmGetPath(const char *path, ...)
2431 {
2432  size_t bufn = _macro_BUFSIZ;
2433  char *buf = alloca(bufn);
2434  const char * s;
2435  char * t, * te;
2436  va_list ap;
2437 
2438  if (path == NULL)
2439  return xstrdup("");
2440 
2441  buf[0] = '\0';
2442  t = buf;
2443  te = stpcpy(t, path);
2444  *te = '\0';
2445 
2446  va_start(ap, path);
2447  while ((s = va_arg(ap, const char *)) != NULL) {
2448  te = stpcpy(te, s);
2449  *te = '\0';
2450  }
2451  va_end(ap);
2452 /*@-modfilesys@*/
2453  (void) expandMacros(NULL, NULL, buf, bufn);
2454 /*@=modfilesys@*/
2455 
2456  (void) rpmCleanPath(buf);
2457  return xstrdup(buf); /* XXX xstrdup has side effects. */
2458 }
2459 
2460 /* Merge 3 args into path, any or all of which may be a url. */
2461 
2462 const char * rpmGenPath(const char * urlroot, const char * urlmdir,
2463  const char *urlfile)
2464 {
2465 /*@owned@*/ const char * xroot = rpmGetPath(urlroot, NULL);
2466 /*@dependent@*/ const char * root = xroot;
2467 /*@owned@*/ const char * xmdir = rpmGetPath(urlmdir, NULL);
2468 /*@dependent@*/ const char * mdir = xmdir;
2469 /*@owned@*/ const char * xfile = rpmGetPath(urlfile, NULL);
2470 /*@dependent@*/ const char * file = xfile;
2471  const char * result;
2472  const char * url = NULL;
2473  int nurl = 0;
2474  int ut;
2475 
2476 #if 0
2477 if (_debug) fprintf(stderr, "*** RGP xroot %s xmdir %s xfile %s\n", xroot, xmdir, xfile);
2478 #endif
2479  ut = urlPath(xroot, &root);
2480  if (url == NULL && ut > URL_IS_DASH) {
2481  url = xroot;
2482  nurl = root - xroot;
2483 #if 0
2484 if (_debug) fprintf(stderr, "*** RGP ut %d root %s nurl %d\n", ut, root, nurl);
2485 #endif
2486  }
2487  if (root == NULL || *root == '\0') root = "/";
2488 
2489  ut = urlPath(xmdir, &mdir);
2490  if (url == NULL && ut > URL_IS_DASH) {
2491  url = xmdir;
2492  nurl = mdir - xmdir;
2493 #if 0
2494 if (_debug) fprintf(stderr, "*** RGP ut %d mdir %s nurl %d\n", ut, mdir, nurl);
2495 #endif
2496  }
2497  if (mdir == NULL || *mdir == '\0') mdir = "/";
2498 
2499  ut = urlPath(xfile, &file);
2500  if (url == NULL && ut > URL_IS_DASH) {
2501  url = xfile;
2502  nurl = file - xfile;
2503 #if 0
2504 if (_debug) fprintf(stderr, "*** RGP ut %d file %s nurl %d\n", ut, file, nurl);
2505 #endif
2506  }
2507 
2508 /*@-branchstate@*/
2509  if (url && nurl > 0) {
2510  char *t = strncpy(alloca(nurl+1), url, nurl);
2511  t[nurl] = '\0';
2512  url = t;
2513  } else
2514  url = "";
2515 /*@=branchstate@*/
2516 
2517  result = rpmGetPath(url, root, "/", mdir, "/", file, NULL);
2518 
2519  xroot = _free(xroot);
2520  xmdir = _free(xmdir);
2521  xfile = _free(xfile);
2522 #if 0
2523 if (_debug) fprintf(stderr, "*** RGP result %s\n", result);
2524 #endif
2525  return result;
2526 }
2527 
2528 /* =============================================================== */
2529 
2530 #if defined(DEBUG_MACROS)
2531 
2532 #if defined(EVAL_MACROS)
2533 
2534 const char *rpmMacrofiles = MACROFILES;
2535 
2536 int
2537 main(int argc, char *argv[])
2538 {
2539  int c;
2540  int errflg = 0;
2541  extern char *optarg;
2542  extern int optind;
2543 
2544  while ((c = getopt(argc, argv, "f:")) != EOF ) {
2545  switch (c) {
2546  case 'f':
2547  rpmMacrofiles = optarg;
2548  break;
2549  case '?':
2550  default:
2551  errflg++;
2552  break;
2553  }
2554  }
2555  if (errflg || optind >= argc) {
2556  fprintf(stderr, "Usage: %s [-f macropath ] macro ...\n", argv[0]);
2557  exit(1);
2558  }
2559 
2560  rpmInitMacros(NULL, rpmMacrofiles);
2561  /* XXX getopt(3) also used for parametrized macros, expect scwewiness. */
2562  for ( ; optind < argc; optind++) {
2563  const char *val;
2564 
2565  val = rpmExpand(argv[optind], NULL);
2566  if (val) {
2567  fprintf(stdout, "%s:\t%s\n", argv[optind], val);
2568  val = _free(val);
2569  }
2570  }
2571  rpmFreeMacros(NULL);
2572  return 0;
2573 }
2574 
2575 #else /* !EVAL_MACROS */
2576 
2577 const char *rpmMacrofiles = "../macros:./testmacros";
2578 const char *testfile = "./test";
2579 
2580 int
2581 main(int argc, char *argv[])
2582 {
2583  size_t bufn = _macro_BUFSIZ;
2584  char *buf = alloca(bufn);
2585  FILE *fp;
2586  int x;
2587 
2588  rpmInitMacros(NULL, rpmMacrofiles);
2589 
2590  if ((fp = fopen(testfile, "r")) != NULL) {
2591  while(rdcl(buf, bufn, fp)) {
2592  x = expandMacros(NULL, NULL, buf, bufn);
2593  fprintf(stderr, "%d->%s\n", x, buf);
2594  memset(buf, 0, bufn);
2595  }
2596  fclose(fp);
2597  }
2598 
2599  while(rdcl(buf, bufn, stdin)) {
2600  x = expandMacros(NULL, NULL, buf, bufn);
2601  fprintf(stderr, "%d->%s\n <-\n", x, buf);
2602  memset(buf, 0, bufn);
2603  }
2604  rpmFreeMacros(NULL);
2605 
2606  return 0;
2607 }
2608 #endif /* EVAL_MACROS */
2609 #endif /* DEBUG_MACROS */
2610 /*@=boundsread@*/