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