rpm 5.2.1

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

- File List
- Globals

rpmio/macro.c
Go to the documentation of this file.

    00001
    00005 #include "system.h"
    00006 #include <stdarg.h>
    00007
    00008 #if !defined(isblank)
    00009 #define isblank(_c)     ((char)(_c) == ' ' || (char)(_c) == '\t')
    00010 #endif
    00011 #define iseol(_c)       ((char)(_c) == '\n' || (char)(_c) == '\r')
    00012
    00013 #define STREQ(_t, _f, _fn)      ((_fn) == (sizeof(_t)-1) && !strncmp((_t), (_f), (_fn)))
    00014
    00015 #ifdef DEBUG_MACROS
    00016 #undef  WITH_LUA        /* XXX fixme */
    00017 #include <sys/types.h>
    00018 #include <errno.h>
    00019 #include <fcntl.h>
    00020 #include <getopt.h>
    00021 #include <stdio.h>
    00022 #include <stdlib.h>
    00023 #include <string.h>
    00024 #include <ctype.h>
    00025 #include <popt.h>
    00026
    00027 #define rpmlog fprintf
    00028 #define RPMLOG_ERR stderr
    00029 #define RPMLOG_WARNING stderr
    00030 #undef  _
    00031 #define _(x)    x
    00032
    00033 #define vmefail(_nb)            (exit(1), NULL)
    00034 #define URL_IS_DASH             1
    00035 #define URL_IS_PATH             2
    00036 #define urlPath(_xr, _r)        (*(_r) = (_xr), URL_IS_PATH)
    00037 #define xisalnum(_c)            isalnum(_c)
    00038 #define xisalpha(_c)            isalpha(_c)
    00039 #define xisdigit(_c)            isdigit(_c)
    00040 #define xisspace(_c)            isspace(_c)
    00041
    00042 typedef FILE * FD_t;
    00043 #define Fopen(_path, _fmode)    fopen(_path, "r");
    00044 #define Ferror                  ferror
    00045 #define Fstrerror(_fd)          strerror(errno)
    00046 #define Fread                   fread
    00047 #define Fclose                  fclose
    00048
    00049 #define fdGetFILE(_fd)          (_fd)
    00050
    00051 /*@unused@*/ static inline /*@null@*/ void *
    00052 _free(/*@only@*/ /*@null@*/ const void * p)
    00053         /*@modifies p@*/
    00054 {
    00055     if (p != NULL)      free((void *)p);
    00056     return NULL;
    00057 }
    00058
    00059 #else
    00060
    00061 /*@observer@*/ /*@checked@*/
    00062 const char * rpmMacrofiles = MACROFILES;
    00063
    00064 #include <rpmio_internal.h>
    00065 #include <rpmlog.h>
    00066 #include <mire.h>
    00067
    00068 #ifdef  WITH_LUA
    00069 #define _RPMLUA_INTERNAL        /* XXX lua->printbuf access */
    00070 #include <rpmlua.h>
    00071 #endif
    00072
    00073 #include <rpmficl.h>
    00074 #include <rpmjs.h>
    00075 #include <rpmperl.h>
    00076 #include <rpmpython.h>
    00077 #include <rpmruby.h>
    00078 #include <rpmtcl.h>
    00079
    00080 #endif
    00081
    00082 #include <rpmuuid.h>
    00083
    00084 #define _MACRO_INTERNAL
    00085 #include <rpmmacro.h>
    00086
    00087 #include "debug.h"
    00088
    00089 /*@unchecked@*/
    00090 #if defined(WITH_FICL) || defined(WITH_JS) || defined(WITH_PERLEMBED) || defined(WITH_PYTHONEMBED) || defined(WITH_RUBYEMBED) || defined(WITH_TCL)
    00091 static int _globalI = 1;
    00092 #endif
    00093
    00094 #if defined(__LCLINT__)
    00095 /*@-exportheader@*/
    00096 extern const unsigned short int **__ctype_b_loc (void) /*@*/;
    00097 /*@=exportheader@*/
    00098 #endif
    00099
    00100 /*@access FD_t @*/              /* XXX compared with NULL */
    00101 /*@access miRE @*/              /* XXX cast */
    00102 /*@access MacroContext @*/
    00103 /*@access MacroEntry@ */
    00104 /*@access rpmlua @*/
    00105 /*@access rpmtcl @*/
    00106
    00107 static struct MacroContext_s rpmGlobalMacroContext_s;
    00108 /*@-compmempass@*/
    00109 MacroContext rpmGlobalMacroContext = &rpmGlobalMacroContext_s;
    00110 /*@=compmempass@*/
    00111
    00112 static struct MacroContext_s rpmCLIMacroContext_s;
    00113 /*@-compmempass@*/
    00114 MacroContext rpmCLIMacroContext = &rpmCLIMacroContext_s;
    00115 /*@=compmempass@*/
    00116
    00120 typedef /*@abstract@*/ struct MacroBuf_s {
    00121 /*@kept@*/ /*@exposed@*/
    00122     const char * s;
    00123 /*@shared@*/
    00124     char * t;
    00125     size_t nb;
    00126     int depth;
    00127     int macro_trace;
    00128     int expand_trace;
    00129 /*@kept@*/ /*@exposed@*/ /*@null@*/
    00130     void * spec;
    00131 /*@kept@*/ /*@exposed@*/
    00132     MacroContext mc;
    00133 } * MacroBuf;
    00134
    00135 #define SAVECHAR(_mb, _c) { *(_mb)->t = (char) (_c), (_mb)->t++, (_mb)->nb--; }
    00136
    00137 /*@-exportlocal -exportheadervar@*/
    00138
    00139 #define _MAX_MACRO_DEPTH        16
    00140 /*@unchecked@*/
    00141 int max_macro_depth = _MAX_MACRO_DEPTH;
    00142
    00143 #define _PRINT_MACRO_TRACE      0
    00144 /*@unchecked@*/
    00145 int print_macro_trace = _PRINT_MACRO_TRACE;
    00146
    00147 #define _PRINT_EXPAND_TRACE     0
    00148 /*@unchecked@*/
    00149 int print_expand_trace = _PRINT_EXPAND_TRACE;
    00150 /*@=exportlocal =exportheadervar@*/
    00151
    00152 #define MACRO_CHUNK_SIZE        16
    00153
    00154 /* Size of expansion buffers. */
    00155 /*@unchecked@*/
    00156 static size_t _macro_BUFSIZ = 16 * 1024;
    00157
    00158 /* forward ref */
    00159 static int expandMacro(MacroBuf mb)
    00160         /*@globals rpmGlobalMacroContext,
    00161                 print_macro_trace, print_expand_trace, h_errno,
    00162                 fileSystem, internalState @*/
    00163         /*@modifies mb, rpmGlobalMacroContext,
    00164                 print_macro_trace, print_expand_trace,
    00165                 fileSystem, internalState @*/;
    00166
    00167 /* =============================================================== */
    00168
    00175 static int
    00176 compareMacroName(const void * ap, const void * bp)
    00177         /*@*/
    00178 {
    00179     MacroEntry ame = *((MacroEntry *)ap);
    00180     MacroEntry bme = *((MacroEntry *)bp);
    00181
    00182     if (ame == NULL && bme == NULL)
    00183         return 0;
    00184     if (ame == NULL)
    00185         return 1;
    00186     if (bme == NULL)
    00187         return -1;
    00188     return strcmp(ame->name, bme->name);
    00189 }
    00190
    00195 static void
    00196 expandMacroTable(MacroContext mc)
    00197         /*@modifies mc @*/
    00198 {
    00199     if (mc->macroTable == NULL) {
    00200         mc->macrosAllocated = MACRO_CHUNK_SIZE;
    00201         mc->macroTable = (MacroEntry *)
    00202             xmalloc(sizeof(*(mc->macroTable)) * mc->macrosAllocated);
    00203         mc->firstFree = 0;
    00204     } else {
    00205         mc->macrosAllocated += MACRO_CHUNK_SIZE;
    00206         mc->macroTable = (MacroEntry *)
    00207             xrealloc(mc->macroTable, sizeof(*(mc->macroTable)) *
    00208                         mc->macrosAllocated);
    00209     }
    00210     memset(&mc->macroTable[mc->firstFree], 0, MACRO_CHUNK_SIZE * sizeof(*(mc->macroTable)));
    00211 }
    00212
    00217 static void
    00218 sortMacroTable(MacroContext mc)
    00219         /*@modifies mc @*/
    00220 {
    00221     int i;
    00222
    00223     if (mc == NULL || mc->macroTable == NULL)
    00224         return;
    00225
    00226     qsort(mc->macroTable, mc->firstFree, sizeof(mc->macroTable[0]),
    00227                 compareMacroName);
    00228
    00229     /* Empty pointers are now at end of table. Reset first free index. */
    00230     for (i = 0; i < mc->firstFree; i++) {
    00231         if (mc->macroTable[i] != NULL)
    00232             continue;
    00233         mc->firstFree = i;
    00234         break;
    00235     }
    00236 }
    00237
    00238 #if !defined(DEBUG_MACROS)
    00239 /*@only@*/
    00240 static char * dupMacroEntry(MacroEntry me)
    00241         /*@*/
    00242 {
    00243     char * t, * te;
    00244     size_t nb;
    00245
    00246 assert(me != NULL);
    00247     nb = strlen(me->name) + sizeof("%") - 1;
    00248     if (me->opts)
    00249         nb += strlen(me->opts) + sizeof("()") - 1;
    00250     if (me->body)
    00251         nb += strlen(me->body) + sizeof("\t") - 1;
    00252     nb++;
    00253
    00254     t = te = xmalloc(nb);
    00255     *te = '\0';
    00256     te = stpcpy( stpcpy(te, "%"), me->name);
    00257     if (me->opts)
    00258         te = stpcpy( stpcpy( stpcpy(te, "("), me->opts), ")");
    00259     if (me->body)
    00260         te = stpcpy( stpcpy(te, "\t"), me->body);
    00261     *te = '\0';
    00262
    00263     return t;
    00264 }
    00265 #endif
    00266
    00267 void
    00268 rpmDumpMacroTable(MacroContext mc, FILE * fp)
    00269 {
    00270     int nempty = 0;
    00271     int nactive = 0;
    00272
    00273     if (mc == NULL) mc = rpmGlobalMacroContext;
    00274     if (fp == NULL) fp = stderr;
    00275
    00276     fprintf(fp, "========================\n");
    00277     if (mc->macroTable != NULL) {
    00278         int i;
    00279         for (i = 0; i < mc->firstFree; i++) {
    00280             MacroEntry me;
    00281             if ((me = mc->macroTable[i]) == NULL) {
    00282                 /* XXX this should never happen */
    00283                 nempty++;
    00284                 continue;
    00285             }
    00286             fprintf(fp, "%3d%c %s", me->level,
    00287                         (me->used > 0 ? '=' : ':'), me->name);
    00288             if (me->opts && *me->opts)
    00289                     fprintf(fp, "(%s)", me->opts);
    00290             if (me->body && *me->body)
    00291                     fprintf(fp, "\t%s", me->body);
    00292             fprintf(fp, "\n");
    00293             nactive++;
    00294         }
    00295     }
    00296     fprintf(fp, _("======================== active %d empty %d\n"),
    00297                 nactive, nempty);
    00298 }
    00299
    00300 #if !defined(DEBUG_MACROS)
    00301 int
    00302 rpmGetMacroEntries(MacroContext mc, void * _mire, int used,
    00303                 const char *** avp)
    00304 {
    00305 /*@-assignexpose -castexpose @*/
    00306     miRE mire = (miRE) _mire;
    00307 /*@=assignexpose =castexpose @*/
    00308     const char ** av;
    00309     int ac = 0;
    00310     int i;
    00311
    00312     if (mc == NULL)
    00313         mc = rpmGlobalMacroContext;
    00314
    00315     if (avp == NULL)
    00316         return mc->firstFree;
    00317
    00318     av = xcalloc( (mc->firstFree+1), sizeof(mc->macroTable[0]));
    00319     if (mc->macroTable != NULL)
    00320     for (i = 0; i < mc->firstFree; i++) {
    00321         MacroEntry me;
    00322         me = mc->macroTable[i];
    00323         if (used > 0 && me->used < used)
    00324             continue;
    00325         if (used == 0 && me->used != 0)
    00326             continue;
    00327 #if !defined(DEBUG_MACROS)      /* XXX preserve standalone build */
    00328         if (mire != NULL && mireRegexec(mire, me->name, 0) < 0)
    00329             continue;
    00330 #endif
    00331         av[ac++] = dupMacroEntry(me);
    00332     }
    00333     av[ac] = NULL;
    00334     *avp = av = xrealloc(av, (ac+1) * sizeof(*av));
    00335
    00336     return ac;
    00337 }
    00338 #endif
    00339
    00347 /*@dependent@*/ /*@null@*/
    00348 static MacroEntry *
    00349 findEntry(MacroContext mc, const char * name, size_t namelen)
    00350         /*@*/
    00351 {
    00352     MacroEntry key, *ret;
    00353
    00354 /*@-globs@*/
    00355     if (mc == NULL) mc = rpmGlobalMacroContext;
    00356 /*@=globs@*/
    00357     if (mc->macroTable == NULL || mc->firstFree == 0)
    00358         return NULL;
    00359
    00360     if (namelen > 0) {
    00361         char * t = strncpy(alloca(namelen + 1), name, namelen);
    00362         t[namelen] = '\0';
    00363         name = t;
    00364     }
    00365
    00366     key = memset(alloca(sizeof(*key)), 0, sizeof(*key));
    00367     /*@-temptrans -assignexpose@*/
    00368     key->name = (char *)name;
    00369     /*@=temptrans =assignexpose@*/
    00370     ret = (MacroEntry *) bsearch(&key, mc->macroTable, mc->firstFree,
    00371                         sizeof(*(mc->macroTable)), compareMacroName);
    00372     /* XXX TODO: find 1st empty slot and return that */
    00373     return ret;
    00374 }
    00375
    00376 /* =============================================================== */
    00377
    00385 /*@null@*/
    00386 static char *
    00387 rdcl(/*@returned@*/ char * buf, size_t size, FD_t fd)
    00388         /*@globals fileSystem @*/
    00389         /*@modifies buf, fileSystem @*/
    00390 {
    00391     char *q = buf - 1;          /* initialize just before buffer. */
    00392     size_t nb = 0;
    00393     size_t nread = 0;
    00394     FILE * f = fdGetFILE(fd);
    00395     int pc = 0, bc = 0;
    00396     char *p = buf;
    00397
    00398     if (f != NULL)
    00399     do {
    00400         *(++q) = '\0';                  /* terminate and move forward. */
    00401         if (fgets(q, (int)size, f) == NULL)     /* read next line. */
    00402             break;
    00403         nb = strlen(q);
    00404         nread += nb;                    /* trim trailing \r and \n */
    00405         for (q += nb - 1; nb > 0 && iseol(*q); q--)
    00406             nb--;
    00407         for (; p <= q; p++) {
    00408             switch (*p) {
    00409                 case '\\':
    00410                     switch (*(p+1)) {
    00411                         case '\r': /*@switchbreak@*/ break;
    00412                         case '\n': /*@switchbreak@*/ break;
    00413                         case '\0': /*@switchbreak@*/ break;
    00414                         default: p++; /*@switchbreak@*/ break;
    00415                     }
    00416                     /*@switchbreak@*/ break;
    00417                 case '%':
    00418                     switch (*(p+1)) {
    00419                         case '{': p++, bc++; /*@switchbreak@*/ break;
    00420                         case '(': p++, pc++; /*@switchbreak@*/ break;
    00421                         case '%': p++; /*@switchbreak@*/ break;
    00422                     }
    00423                     /*@switchbreak@*/ break;
    00424                 case '{': if (bc > 0) bc++; /*@switchbreak@*/ break;
    00425                 case '}': if (bc > 0) bc--; /*@switchbreak@*/ break;
    00426                 case '(': if (pc > 0) pc++; /*@switchbreak@*/ break;
    00427                 case ')': if (pc > 0) pc--; /*@switchbreak@*/ break;
    00428             }
    00429         }
    00430         if (nb == 0 || (*q != '\\' && !bc && !pc) || *(q+1) == '\0') {
    00431             *(++q) = '\0';              /* trim trailing \r, \n */
    00432             break;
    00433         }
    00434         q++; p++; nb++;                 /* copy newline too */
    00435         size -= nb;
    00436         if (*q == '\r')                 /* XXX avoid \r madness */
    00437             *q = '\n';
    00438     } while (size > 0);
    00439     return (nread > 0 ? buf : NULL);
    00440 }
    00441
    00449 /*@null@*/
    00450 static const char *
    00451 matchchar(const char * p, char pl, char pr)
    00452         /*@*/
    00453 {
    00454     int lvl = 0;
    00455     char c;
    00456
    00457     while ((c = *p++) != '\0') {
    00458         if (c == '\\') {                /* Ignore escaped chars */
    00459             p++;
    00460             continue;
    00461         }
    00462         if (c == pr) {
    00463             if (--lvl <= 0)     return --p;
    00464         } else if (c == pl)
    00465             lvl++;
    00466     }
    00467     return (const char *)NULL;
    00468 }
    00469
    00476 static void
    00477 printMacro(MacroBuf mb, const char * s, const char * se)
    00478         /*@globals fileSystem @*/
    00479         /*@modifies fileSystem @*/
    00480 {
    00481     const char *senl;
    00482     const char *ellipsis;
    00483     int choplen;
    00484
    00485     if (s >= se) {      /* XXX just in case */
    00486         fprintf(stderr, _("%3d>%*s(empty)"), mb->depth,
    00487                 (2 * mb->depth + 1), "");
    00488         return;
    00489     }
    00490
    00491     if (s[-1] == '{')
    00492         s--;
    00493
    00494     /* Print only to first end-of-line (or end-of-string). */
    00495     for (senl = se; *senl && !iseol(*senl); senl++)
    00496         {};
    00497
    00498     /* Limit trailing non-trace output */
    00499     choplen = 61 - (2 * mb->depth);
    00500     if ((senl - s) > choplen) {
    00501         senl = s + choplen;
    00502         ellipsis = "...";
    00503     } else
    00504         ellipsis = "";
    00505
    00506     /* Substitute caret at end-of-macro position */
    00507     fprintf(stderr, "%3d>%*s%%%.*s^", mb->depth,
    00508         (2 * mb->depth + 1), "", (int)(se - s), s);
    00509     if (se[1] != '\0' && (senl - (se+1)) > 0)
    00510         fprintf(stderr, "%-.*s%s", (int)(senl - (se+1)), se+1, ellipsis);
    00511     fprintf(stderr, "\n");
    00512 }
    00513
    00520 static void
    00521 printExpansion(MacroBuf mb, const char * t, const char * te)
    00522         /*@globals fileSystem @*/
    00523         /*@modifies fileSystem @*/
    00524 {
    00525     const char *ellipsis;
    00526     int choplen;
    00527
    00528     if (!(te > t)) {
    00529         fprintf(stderr, _("%3d<%*s(empty)\n"), mb->depth, (2 * mb->depth + 1), "");
    00530         return;
    00531     }
    00532
    00533     /* Shorten output which contains newlines */
    00534     while (te > t && iseol(te[-1]))
    00535         te--;
    00536     ellipsis = "";
    00537     if (mb->depth > 0) {
    00538         const char *tenl;
    00539
    00540         /* Skip to last line of expansion */
    00541         while ((tenl = strchr(t, '\n')) && tenl < te)
    00542             t = ++tenl;
    00543
    00544         /* Limit expand output */
    00545         choplen = 61 - (2 * mb->depth);
    00546         if ((te - t) > choplen) {
    00547             te = t + choplen;
    00548             ellipsis = "...";
    00549         }
    00550     }
    00551
    00552     fprintf(stderr, "%3d<%*s", mb->depth, (2 * mb->depth + 1), "");
    00553     if (te > t)
    00554         fprintf(stderr, "%.*s%s", (int)(te - t), t, ellipsis);
    00555     fprintf(stderr, "\n");
    00556 }
    00557
    00558 #define SKIPBLANK(_s, _c)       \
    00559         /*@-globs@*/    /* FIX: __ctype_b */ \
    00560         while (((_c) = (int) *(_s)) && isblank(_c)) \
    00561                 (_s)++;         \
    00562         /*@=globs@*/
    00563
    00564 #define SKIPNONBLANK(_s, _c)    \
    00565         /*@-globs@*/    /* FIX: __ctype_b */ \
    00566         while (((_c) = (int) *(_s)) && !(isblank(_c) || iseol(_c))) \
    00567                 (_s)++;         \
    00568         /*@=globs@*/
    00569
    00570 #define COPYNAME(_ne, _s, _c)   \
    00571     {   SKIPBLANK(_s,_c);       \
    00572         while(((_c) = (int) *(_s)) && (xisalnum(_c) || (_c) == (int) '_')) \
    00573                 *(_ne)++ = *(_s)++; \
    00574         *(_ne) = '\0';          \
    00575     }
    00576
    00577 #define COPYOPTS(_oe, _s, _c)   \
    00578     {   while(((_c) = (int) *(_s)) && (_c) != (int) ')') \
    00579                 *(_oe)++ = *(_s)++; \
    00580         *(_oe) = '\0';          \
    00581     }
    00582
    00590 static int
    00591 expandT(MacroBuf mb, const char * f, size_t flen)
    00592         /*@globals rpmGlobalMacroContext, h_errno, fileSystem, internalState @*/
    00593         /*@modifies mb, rpmGlobalMacroContext, fileSystem, internalState @*/
    00594 {
    00595     char *sbuf;
    00596     const char *s = mb->s;
    00597     int rc;
    00598
    00599     sbuf = alloca(flen + 1);
    00600     memset(sbuf, 0, (flen + 1));
    00601
    00602     strncpy(sbuf, f, flen);
    00603     sbuf[flen] = '\0';
    00604     mb->s = sbuf;
    00605     rc = expandMacro(mb);
    00606     mb->s = s;
    00607     return rc;
    00608 }
    00609
    00610 #if 0
    00611
    00618 static int
    00619 expandS(MacroBuf mb, char * tbuf, size_t tbuflen)
    00620         /*@globals rpmGlobalMacroContext, fileSystem, internalState @*/
    00621         /*@modifies mb, *tbuf, rpmGlobalMacroContext, fileSystem, internalState @*/
    00622 {
    00623     const char *t = mb->t;
    00624     size_t nb = mb->nb;
    00625     int rc;
    00626
    00627     mb->t = tbuf;
    00628     mb->nb = tbuflen;
    00629     rc = expandMacro(mb);
    00630     mb->t = t;
    00631     mb->nb = nb;
    00632     return rc;
    00633 }
    00634 #endif
    00635
    00643 static int
    00644 expandU(MacroBuf mb, char * u, size_t ulen)
    00645         /*@globals rpmGlobalMacroContext, h_errno, fileSystem, internalState @*/
    00646         /*@modifies mb, *u, rpmGlobalMacroContext, fileSystem, internalState @*/
    00647 {
    00648     const char *s = mb->s;
    00649     char *t = mb->t;
    00650     size_t nb = mb->nb;
    00651     char *tbuf;
    00652     int rc;
    00653
    00654     tbuf = alloca(ulen + 1);
    00655     memset(tbuf, 0, (ulen + 1));
    00656
    00657     mb->s = u;
    00658     mb->t = tbuf;
    00659     mb->nb = ulen;
    00660     rc = expandMacro(mb);
    00661
    00662     tbuf[ulen] = '\0';  /* XXX just in case */
    00663     if (ulen > mb->nb)
    00664         strncpy(u, tbuf, (ulen - mb->nb + 1));
    00665
    00666     mb->s = s;
    00667     mb->t = t;
    00668     mb->nb = nb;
    00669
    00670     return rc;
    00671 }
    00672
    00680 static int
    00681 doShellEscape(MacroBuf mb, const char * cmd, size_t clen)
    00682         /*@globals rpmGlobalMacroContext, h_errno, fileSystem, internalState @*/
    00683         /*@modifies mb, rpmGlobalMacroContext, fileSystem, internalState @*/
    00684 {
    00685     size_t bufn = _macro_BUFSIZ + clen;
    00686     char * buf = alloca(bufn);
    00687     FILE *shf;
    00688     int rc;
    00689     int c;
    00690
    00691     strncpy(buf, cmd, clen);
    00692     buf[clen] = '\0';
    00693     rc = expandU(mb, buf, bufn);
    00694     if (rc)
    00695         return rc;
    00696
    00697     if ((shf = popen(buf, "r")) == NULL)
    00698         return 1;
    00699     while(mb->nb > 0 && (c = fgetc(shf)) != EOF)
    00700         SAVECHAR(mb, c);
    00701     (void) pclose(shf);
    00702
    00703     /* XXX delete trailing \r \n */
    00704     while (iseol(mb->t[-1])) {
    00705         *(mb->t--) = '\0';
    00706         mb->nb++;
    00707     }
    00708     return 0;
    00709 }
    00710
    00719 /*@dependent@*/ static const char *
    00720 doDefine(MacroBuf mb, /*@returned@*/ const char * se, int level, int expandbody)
    00721         /*@globals rpmGlobalMacroContext, h_errno, internalState @*/
    00722         /*@modifies mb, rpmGlobalMacroContext, internalState @*/
    00723 {
    00724     const char *s = se;
    00725     size_t bufn = _macro_BUFSIZ;
    00726     char *buf = alloca(bufn);
    00727     char *n = buf, *ne;
    00728     char *o = NULL, *oe;
    00729     char *b, *be;
    00730     int c;
    00731     int oc = (int) ')';
    00732
    00733     SKIPBLANK(s, c);
    00734     if (c == (int) '.')         /* XXX readonly macros */
    00735 /*@i@*/ *n++ = c = *s++;
    00736     if (c == (int) '.')         /* XXX readonly macros */
    00737 /*@i@*/ *n++ = c = *s++;
    00738     ne = n;
    00739
    00740     /* Copy name */
    00741     COPYNAME(ne, s, c);
    00742
    00743     /* Copy opts (if present) */
    00744     oe = ne + 1;
    00745     if (*s == '(') {
    00746         s++;    /* skip ( */
    00747         o = oe;
    00748         COPYOPTS(oe, s, oc);
    00749         s++;    /* skip ) */
    00750     }
    00751
    00752     /* Copy body, skipping over escaped newlines */
    00753     b = be = oe + 1;
    00754     SKIPBLANK(s, c);
    00755     if (c == (int) '{') {       /* XXX permit silent {...} grouping */
    00756         if ((se = matchchar(s, (char) c, '}')) == NULL) {
    00757             rpmlog(RPMLOG_ERR,
    00758                 _("Macro %%%s has unterminated body\n"), n);
    00759             se = s;     /* XXX W2DO? */
    00760             return se;
    00761         }
    00762         s++;    /* XXX skip { */
    00763         strncpy(b, s, (se - s));
    00764         b[se - s] = '\0';
    00765         be += strlen(b);
    00766         se++;   /* XXX skip } */
    00767         s = se; /* move scan forward */
    00768     } else {    /* otherwise free-field */
    00769         int bc = 0, pc = 0;
    00770         while (*s && (bc || pc || !iseol(*s))) {
    00771             switch (*s) {
    00772                 case '\\':
    00773                     switch (*(s+1)) {
    00774                         case '\0': /*@switchbreak@*/ break;
    00775                         default: s++; /*@switchbreak@*/ break;
    00776                     }
    00777                     /*@switchbreak@*/ break;
    00778                 case '%':
    00779                     switch (*(s+1)) {
    00780                         case '{': *be++ = *s++; bc++; /*@switchbreak@*/ break;
    00781                         case '(': *be++ = *s++; pc++; /*@switchbreak@*/ break;
    00782                         case '%': *be++ = *s++; /*@switchbreak@*/ break;
    00783                     }
    00784                     /*@switchbreak@*/ break;
    00785                 case '{': if (bc > 0) bc++; /*@switchbreak@*/ break;
    00786                 case '}': if (bc > 0) bc--; /*@switchbreak@*/ break;
    00787                 case '(': if (pc > 0) pc++; /*@switchbreak@*/ break;
    00788                 case ')': if (pc > 0) pc--; /*@switchbreak@*/ break;
    00789             }
    00790             *be++ = *s++;
    00791         }
    00792         *be = '\0';
    00793
    00794         if (bc || pc) {
    00795             rpmlog(RPMLOG_ERR,
    00796                 _("Macro %%%s has unterminated body\n"), n);
    00797             se = s;     /* XXX W2DO? */
    00798             return se;
    00799         }
    00800
    00801         /* Trim trailing blanks/newlines */
    00802 /*@-globs@*/
    00803         while (--be >= b && (c = (int) *be) && (isblank(c) || iseol(c)))
    00804             {};
    00805 /*@=globs@*/
    00806         *(++be) = '\0'; /* one too far */
    00807     }
    00808
    00809     /* Move scan over body */
    00810     while (iseol(*s))
    00811         s++;
    00812     se = s;
    00813
    00814     /* Names must start with alphabetic or _ and be at least 3 chars */
    00815     if (!((c = (int) *n) && (xisalpha(c) || c == (int) '_') && (ne - n) > 2)) {
    00816         rpmlog(RPMLOG_ERR,
    00817                 _("Macro %%%s has illegal name (%%define)\n"), n);
    00818         return se;
    00819     }
    00820
    00821     /* Options must be terminated with ')' */
    00822     if (o && oc != (int) ')') {
    00823         rpmlog(RPMLOG_ERR, _("Macro %%%s has unterminated opts\n"), n);
    00824         return se;
    00825     }
    00826
    00827     if ((be - b) < 1) {
    00828         rpmlog(RPMLOG_ERR, _("Macro %%%s has empty body\n"), n);
    00829         return se;
    00830     }
    00831
    00832 /*@-modfilesys@*/
    00833     if (expandbody && expandU(mb, b, (&buf[bufn] - b))) {
    00834         rpmlog(RPMLOG_ERR, _("Macro %%%s failed to expand\n"), n);
    00835         return se;
    00836     }
    00837 /*@=modfilesys@*/
    00838
    00839     if (n != buf)               /* XXX readonly macros */
    00840         n--;
    00841     if (n != buf)               /* XXX readonly macros */
    00842         n--;
    00843     addMacro(mb->mc, n, o, b, (level - 1));
    00844
    00845     return se;
    00846 }
    00847
    00854 /*@dependent@*/ static const char *
    00855 doUndefine(MacroContext mc, /*@returned@*/ const char * se)
    00856         /*@globals rpmGlobalMacroContext @*/
    00857         /*@modifies mc, rpmGlobalMacroContext @*/
    00858 {
    00859     const char *s = se;
    00860     char *buf = alloca(_macro_BUFSIZ);
    00861     char *n = buf, *ne = n;
    00862     int c;
    00863
    00864     COPYNAME(ne, s, c);
    00865
    00866     /* Move scan over body */
    00867     while (iseol(*s))
    00868         s++;
    00869     se = s;
    00870
    00871     /* Names must start with alphabetic or _ and be at least 3 chars */
    00872     if (!((c = (int) *n) && (xisalpha(c) || c == (int) '_') && (ne - n) > 2)) {
    00873         rpmlog(RPMLOG_ERR,
    00874                 _("Macro %%%s has illegal name (%%undefine)\n"), n);
    00875         return se;
    00876     }
    00877
    00878     delMacro(mc, n);
    00879
    00880     return se;
    00881 }
    00882
    00883 #ifdef  DYING
    00884 static void
    00885 dumpME(const char * msg, MacroEntry me)
    00886         /*@globals fileSystem @*/
    00887         /*@modifies fileSystem @*/
    00888 {
    00889     if (msg)
    00890         fprintf(stderr, "%s", msg);
    00891     fprintf(stderr, "\tme %p", me);
    00892     if (me)
    00893         fprintf(stderr,"\tname %p(%s) prev %p",
    00894                 me->name, me->name, me->prev);
    00895     fprintf(stderr, "\n");
    00896 }
    00897 #endif
    00898
    00907 static void
    00908 pushMacro(/*@out@*/ MacroEntry * mep, const char * n, /*@null@*/ const char * o,
    00909                 /*@null@*/ const char * b, int level)
    00910         /*@modifies *mep @*/
    00911 {
    00912     MacroEntry prev = (mep && *mep ? *mep : NULL);
    00913     MacroEntry me = (MacroEntry) xmalloc(sizeof(*me));
    00914     const char *name = n;
    00915
    00916     if (*name == '.')           /* XXX readonly macros */
    00917         name++;
    00918     if (*name == '.')           /* XXX readonly macros */
    00919         name++;
    00920
    00921     /*@-assignexpose@*/
    00922     me->prev = prev;
    00923     /*@=assignexpose@*/
    00924     me->name = (prev ? prev->name : xstrdup(name));
    00925     me->opts = (o ? xstrdup(o) : NULL);
    00926     me->body = xstrdup(b ? b : "");
    00927     me->used = 0;
    00928     me->level = level;
    00929     me->flags = (name != n);
    00930     if (mep)
    00931         *mep = me;
    00932     else
    00933         me = _free(me);
    00934 }
    00935
    00940 static void
    00941 popMacro(MacroEntry * mep)
    00942         /*@modifies *mep @*/
    00943 {
    00944         MacroEntry me = (*mep ? *mep : NULL);
    00945
    00946         if (me) {
    00947                 /* XXX cast to workaround const */
    00948                 /*@-onlytrans@*/
    00949                 if ((*mep = me->prev) == NULL)
    00950                         me->name = _free(me->name);
    00951                 me->opts = _free(me->opts);
    00952                 me->body = _free(me->body);
    00953                 me = _free(me);
    00954                 /*@=onlytrans@*/
    00955         }
    00956 }
    00957
    00962 static void
    00963 freeArgs(MacroBuf mb)
    00964         /*@modifies mb @*/
    00965 {
    00966     MacroContext mc = mb->mc;
    00967     int ndeleted = 0;
    00968     int i;
    00969
    00970     if (mc == NULL || mc->macroTable == NULL)
    00971         return;
    00972
    00973     /* Delete dynamic macro definitions */
    00974     for (i = 0; i < mc->firstFree; i++) {
    00975         MacroEntry *mep, me;
    00976         int skiptest = 0;
    00977         mep = &mc->macroTable[i];
    00978         me = *mep;
    00979
    00980         if (me == NULL)         /* XXX this should never happen */
    00981             continue;
    00982         if (me->level < mb->depth)
    00983             continue;
    00984         if (strlen(me->name) == 1 && strchr("#*0", *me->name)) {
    00985             if (*me->name == '*' && me->used > 0)
    00986                 skiptest = 1; /* XXX skip test for %# %* %0 */
    00987         } else if (!skiptest && me->used <= 0) {
    00988 #if NOTYET
    00989             rpmlog(RPMLOG_ERR,
    00990                         _("Macro %%%s (%s) was not used below level %d\n"),
    00991                         me->name, me->body, me->level);
    00992 #endif
    00993         }
    00994         popMacro(mep);
    00995         if (!(mep && *mep))
    00996             ndeleted++;
    00997     }
    00998
    00999     /* If any deleted macros, sort macro table */
    01000     if (ndeleted)
    01001         sortMacroTable(mc);
    01002 }
    01003
    01013 /*@dependent@*/ static const char *
    01014 grabArgs(MacroBuf mb, const MacroEntry me, /*@returned@*/ const char * se,
    01015                 const char * lastc)
    01016         /*@globals rpmGlobalMacroContext, fileSystem, internalState @*/
    01017         /*@modifies mb, rpmGlobalMacroContext, fileSystem, internalState @*/
    01018 {
    01019     poptContext optCon;
    01020     struct poptOption *optTbl;
    01021     size_t bufn = _macro_BUFSIZ;
    01022     char *buf = alloca(bufn);
    01023     char *b, *be;
    01024     char aname[16];
    01025     const char *opts;
    01026     int argc = 0;
    01027     const char **argv;
    01028     int c;
    01029     unsigned int popt_flags;
    01030
    01031     /* Copy macro name as argv[0], save beginning of args.  */
    01032     buf[0] = '\0';
    01033     b = be = stpcpy(buf, me->name);
    01034
    01035     addMacro(mb->mc, "0", NULL, buf, mb->depth);
    01036
    01037     argc = 1;   /* XXX count argv[0] */
    01038
    01039     /* Copy args into buf until lastc */
    01040     *be++ = ' ';
    01041     while ((c = (int) *se++) != (int) '\0' && (se-1) != lastc) {
    01042 /*@-globs@*/
    01043         if (!isblank(c)) {
    01044             *be++ = (char) c;
    01045             continue;
    01046         }
    01047 /*@=globs@*/
    01048         /* c is blank */
    01049         if (be[-1] == ' ')
    01050             continue;
    01051         /* a word has ended */
    01052         *be++ = ' ';
    01053         argc++;
    01054     }
    01055     if (c == (int) '\0') se--;  /* one too far */
    01056     if (be[-1] != ' ')
    01057         argc++, be++;           /* last word has not trailing ' ' */
    01058     be[-1] = '\0';
    01059     if (*b == ' ') b++;         /* skip the leading ' ' */
    01060
    01061 /*
    01062  * The macro %* analoguous to the shell's $* means "Pass all non-macro
    01063  * parameters." Consequently, there needs to be a macro that means "Pass all
    01064  * (including macro parameters) options". This is useful for verifying
    01065  * parameters during expansion and yet transparently passing all parameters
    01066  * through for higher level processing (e.g. %description and/or %setup).
    01067  * This is the (potential) justification for %{**} ...
    01068  */
    01069     /* Add unexpanded args as macro */
    01070     addMacro(mb->mc, "**", NULL, b, mb->depth);
    01071
    01072 #ifdef NOTYET
    01073     /* XXX if macros can be passed as args ... */
    01074     expandU(mb, buf, bufn);
    01075 #endif
    01076
    01077     /* Build argv array */
    01078     argv = (const char **) alloca((argc + 1) * sizeof(*argv));
    01079     be[-1] = ' '; /* assert((be - 1) == (b + strlen(b) == buf + strlen(buf))) */
    01080     be[0] = '\0';
    01081
    01082     b = buf;
    01083     for (c = 0; c < argc; c++) {
    01084         argv[c] = b;
    01085         b = strchr(b, ' ');
    01086         *b++ = '\0';
    01087     }
    01088     /* assert(b == be);  */
    01089     argv[argc] = NULL;
    01090
    01091     /* '+' as the first character means that options are recognized
    01092      * only before positional arguments, as POSIX requires.
    01093     */
    01094     popt_flags = POPT_CONTEXT_NO_EXEC;
    01095 #if defined(RPM_VENDOR_OPENPKG) /* always-strict-posix-option-parsing */
    01096     popt_flags |= POPT_CONTEXT_POSIXMEHARDER;
    01097 #endif
    01098     if (me->opts[0] == '+') popt_flags |= POPT_CONTEXT_POSIXMEHARDER;
    01099
    01100     /* Count the number of short options. */
    01101     opts = me->opts;
    01102     if (*opts == '+') opts++;
    01103     for (c = 0; *opts != '\0'; opts++)
    01104         if (*opts != ':') c++;
    01105
    01106     /* Set up popt option table. */
    01107     optTbl = xcalloc(sizeof(*optTbl), (c + 1));
    01108     opts = me->opts;
    01109     if (*opts == '+') opts++;
    01110     for (c = 0; *opts != '\0'; opts++) {
    01111         if (*opts == ':') continue;
    01112         optTbl[c].shortName = opts[0];
    01113         optTbl[c].val = (int) opts[0];
    01114         if (opts[1] == ':')
    01115             optTbl[c].argInfo = POPT_ARG_STRING;
    01116         c++;
    01117     }
    01118
    01119     /* Parse the options, defining option macros. */
    01120 /*@-nullstate@*/
    01121     optCon = poptGetContext(argv[0], argc, argv, optTbl, popt_flags);
    01122 /*@=nullstate@*/
    01123     while ((c = poptGetNextOpt(optCon)) > 0) {
    01124         const char * optArg = poptGetOptArg(optCon);
    01125         *be++ = '-';
    01126         *be++ = (char) c;
    01127         if (optArg != NULL) {
    01128             *be++ = ' ';
    01129             be = stpcpy(be, optArg);
    01130         }
    01131         *be++ = '\0';
    01132         aname[0] = '-'; aname[1] = (char)c; aname[2] = '\0';
    01133         addMacro(mb->mc, aname, NULL, b, mb->depth);
    01134         if (optArg != NULL) {
    01135             aname[0] = '-'; aname[1] = (char)c; aname[2] = '*'; aname[3] = '\0';
    01136             addMacro(mb->mc, aname, NULL, optArg, mb->depth);
    01137         }
    01138         be = b; /* reuse the space */
    01139 /*@-dependenttrans -modobserver -observertrans @*/
    01140         optArg = _free(optArg);
    01141 /*@=dependenttrans =modobserver =observertrans @*/
    01142     }
    01143     if (c < -1) {
    01144         rpmlog(RPMLOG_ERR, _("Unknown option in macro %s(%s): %s: %s\n"),
    01145                 me->name, me->opts,
    01146                 poptBadOption(optCon, POPT_BADOPTION_NOALIAS), poptStrerror(c));
    01147         goto exit;
    01148     }
    01149
    01150     argv = poptGetArgs(optCon);
    01151     argc = 0;
    01152     if (argv != NULL)
    01153     for (c = 0; argv[c] != NULL; c++)
    01154         argc++;
    01155
    01156     /* Add arg count as macro. */
    01157     sprintf(aname, "%d", argc);
    01158     addMacro(mb->mc, "#", NULL, aname, mb->depth);
    01159
    01160     /* Add macro for each arg. Concatenate args for %*. */
    01161     if (be) {
    01162         *be = '\0';
    01163         if (argv != NULL)
    01164         for (c = 0; c < argc; c++) {
    01165             sprintf(aname, "%d", (c + 1));
    01166             addMacro(mb->mc, aname, NULL, argv[c], mb->depth);
    01167             if (be != b) *be++ = ' '; /* Add space between args */
    01168             be = stpcpy(be, argv[c]);
    01169         }
    01170     }
    01171
    01172     /* Add unexpanded args as macro. */
    01173     addMacro(mb->mc, "*", NULL, b, mb->depth);
    01174
    01175 exit:
    01176     optCon = poptFreeContext(optCon);
    01177     optTbl = _free(optTbl);
    01178     return se;
    01179 }
    01180
    01188 static void
    01189 doOutput(MacroBuf mb, int waserror, const char * msg, size_t msglen)
    01190         /*@globals rpmGlobalMacroContext, h_errno, fileSystem, internalState @*/
    01191         /*@modifies mb, rpmGlobalMacroContext, fileSystem, internalState @*/
    01192 {
    01193     size_t bufn = _macro_BUFSIZ + msglen;
    01194     char *buf = alloca(bufn);
    01195
    01196     strncpy(buf, msg, msglen);
    01197     buf[msglen] = '\0';
    01198     (void) expandU(mb, buf, bufn);
    01199     if (waserror)
    01200         rpmlog(RPMLOG_ERR, "%s\n", buf);
    01201     else
    01202         fprintf(stderr, "%s", buf);
    01203 }
    01204
    01214 static void
    01215 doFoo(MacroBuf mb, int negate, const char * f, size_t fn,
    01216                 /*@null@*/ const char * g, size_t gn)
    01217         /*@globals rpmGlobalMacroContext, h_errno, fileSystem, internalState @*/
    01218         /*@modifies mb, rpmGlobalMacroContext, fileSystem, internalState @*/
    01219 {
    01220     size_t bufn = _macro_BUFSIZ + fn + gn;
    01221     char * buf = alloca(bufn);
    01222     char *b = NULL, *be;
    01223     int c;
    01224
    01225     buf[0] = '\0';
    01226     if (g != NULL) {
    01227         strncpy(buf, g, gn);
    01228         buf[gn] = '\0';
    01229         (void) expandU(mb, buf, bufn);
    01230     }
    01231     if (fn > 5 && STREQ("patch", f, 5) && xisdigit((int)f[5])) {
    01232         /* Skip leading zeros */
    01233         for (c = 5; c < (int)(fn-1) && f[c] == '0' && xisdigit((int)f[c+1]);)
    01234             c++;
    01235         b = buf;
    01236         be = stpncpy( stpcpy(b, "%patch -P "), f+c, fn-c);
    01237         *be = '\0';
    01238     } else
    01239     if (STREQ("basename", f, fn)) {
    01240         if ((b = strrchr(buf, '/')) == NULL)
    01241             b = buf;
    01242         else
    01243             b++;
    01244     } else if (STREQ("dirname", f, fn)) {
    01245         if ((b = strrchr(buf, '/')) != NULL)
    01246             *b = '\0';
    01247         b = buf;
    01248 #if !defined(__LCLINT__) /* XXX LCL: realpath(3) annotations are buggy. */
    01249     } else if (STREQ("realpath", f, fn)) {
    01250         char rp[PATH_MAX];
    01251         char *cp;
    01252         size_t l;
    01253         if ((cp = realpath(buf, rp)) != NULL) {
    01254             l = strlen(cp);
    01255             if ((size_t)(l+1) <= bufn) {
    01256                 memcpy(buf, cp, l+1);
    01257                 b = buf;
    01258             }
    01259         }
    01260 #endif
    01261     } else if (STREQ("getenv", f, fn)) {
    01262         char *cp;
    01263         if ((cp = getenv(buf)) != NULL)
    01264             b = cp;
    01265     } else if (STREQ("shrink", f, fn)) {
    01266         /*
    01267          * shrink body by removing all leading and trailing whitespaces and
    01268          * reducing intermediate whitespaces to a single space character.
    01269          */
    01270         int i, j, k, was_space = 0;
    01271         for (i = 0, j = 0, k = (int)strlen(buf); i < k; ) {
    01272             if (xisspace((int)(buf[i]))) {
    01273                 was_space = 1;
    01274                 i++;
    01275                 continue;
    01276             }
    01277             else if (was_space) {
    01278                 was_space = 0;
    01279                 if (j > 0) /* remove leading blanks at all */
    01280                     buf[j++] = ' ';
    01281                 /* fallthrough */
    01282             }
    01283             buf[j++] = buf[i++];
    01284         }
    01285         buf[j] = '\0';
    01286         b = buf;
    01287     } else if (STREQ("suffix", f, fn)) {
    01288         if ((b = strrchr(buf, '.')) != NULL)
    01289             b++;
    01290     } else if (STREQ("expand", f, fn)) {
    01291         b = buf;
    01292     } else if (STREQ("verbose", f, fn)) {
    01293 #if defined(RPMLOG_MASK)
    01294         if (negate)
    01295             b = ((rpmlogSetMask(0) >= RPMLOG_MASK( RPMLOG_INFO )) ? NULL : buf);
    01296         else
    01297             b = ((rpmlogSetMask(0) >= RPMLOG_MASK( RPMLOG_INFO )) ? buf : NULL);
    01298 #else
    01299         /* XXX assume always verbose when running standalone */
    01300         b = (negate) ? NULL : buf;
    01301 #endif
    01302     } else if (STREQ("url2path", f, fn) || STREQ("u2p", f, fn)) {
    01303         int ut = urlPath(buf, (const char **)&b);
    01304         ut = ut;        /* XXX quiet gcc */
    01305         if (*b == '\0') b = "/";
    01306     } else if (STREQ("uncompress", f, fn)) {
    01307         rpmCompressedMagic compressed = COMPRESSED_OTHER;
    01308 /*@-globs@*/
    01309         for (b = buf; (c = (int)*b) && isblank(c);)
    01310             b++;
    01311         /* XXX FIXME: file paths with embedded white space needs rework. */
    01312         for (be = b; (c = (int)*be) && !isblank(c);)
    01313             be++;
    01314 /*@=globs@*/
    01315         *be++ = '\0';
    01316         (void) isCompressed(b, &compressed);
    01317         switch(compressed) {
    01318         default:
    01319         case 0: /* COMPRESSED_NOT */
    01320             sprintf(be, "%%__cat %s", b);
    01321             break;
    01322         case 1: /* COMPRESSED_OTHER */
    01323             sprintf(be, "%%__gzip -dc '%s'", b);
    01324             break;
    01325         case 2: /* COMPRESSED_BZIP2 */
    01326             sprintf(be, "%%__bzip2 -dc '%s'", b);
    01327             break;
    01328         case 3: /* COMPRESSED_ZIP */
    01329             sprintf(be, "%%__unzip -qq '%s'", b);
    01330             break;
    01331         case 4: /* COMPRESSED_LZOP */
    01332             sprintf(be, "%%__lzop -dc '%s'", b);
    01333             break;
    01334         case 5: /* COMPRESSED_LZMA */
    01335             sprintf(be, "%%__lzma -dc '%s'", b);
    01336             break;
    01337         case 6: /* COMPRESSED_XZ */
    01338             sprintf(be, "%%__xz -dc '%s'", b);
    01339             break;
    01340         }
    01341         b = be;
    01342     } else if (STREQ("mkstemp", f, fn)) {
    01343 /*@-globs@*/
    01344         for (b = buf; (c = (int)*b) && isblank(c);)
    01345             b++;
    01346         /* XXX FIXME: file paths with embedded white space needs rework. */
    01347         for (be = b; (c = (int)*be) && !isblank(c);)
    01348             be++;
    01349 /*@=globs@*/
    01350 #if defined(HAVE_MKSTEMP)
    01351         (void) close(mkstemp(b));
    01352 #else
    01353         (void) mktemp(b);
    01354 #endif
    01355     } else if (STREQ("mkdtemp", f, fn)) {
    01356 /*@-globs@*/
    01357         for (b = buf; (c = (int)*b) && isblank(c);)
    01358             b++;
    01359         /* XXX FIXME: file paths with embedded white space needs rework. */
    01360         for (be = b; (c = (int)*be) && !isblank(c);)
    01361             be++;
    01362 /*@=globs@*/
    01363 #if defined(HAVE_MKDTEMP) && !defined(__LCLINT__)
    01364         if (mkdtemp(b) == NULL)
    01365             perror("mkdtemp");
    01366 #else
    01367         if ((b = tmpnam(b)) != NULL)
    01368             (void) mkdir(b, 0700);      /* XXX S_IWRSXU is not included. */
    01369 #endif
    01370     } else if (STREQ("uuid", f, fn)) {
    01371         int uuid_version;
    01372         const char *uuid_ns;
    01373         const char *uuid_data;
    01374         char *cp;
    01375         size_t n;
    01376
    01377         uuid_version = 1;
    01378         uuid_ns = NULL;
    01379         uuid_data = NULL;
    01380         cp = buf;
    01381         if ((n = strspn(cp, " \t\n")) > 0)
    01382             cp += n;
    01383         if ((n = strcspn(cp, " \t\n")) > 0) {
    01384             uuid_version = (int)strtol(cp, (char **)NULL, 10);
    01385             cp += n;
    01386             if ((n = strspn(cp, " \t\n")) > 0)
    01387                 cp += n;
    01388             if ((n = strcspn(cp, " \t\n")) > 0) {
    01389                 uuid_ns = cp;
    01390                 cp += n;
    01391                 *cp++ = '\0';
    01392                 if ((n = strspn(cp, " \t\n")) > 0)
    01393                     cp += n;
    01394                 if ((n = strcspn(cp, " \t\n")) > 0) {
    01395                     uuid_data = cp;
    01396                     cp += n;
    01397                     *cp++ = '\0';
    01398                 }
    01399             }
    01400         }
    01401 /*@-nullpass@*/ /* FIX: uuid_ns may be NULL */
    01402         if (rpmuuidMake(uuid_version, uuid_ns, uuid_data, buf, NULL))
    01403             rpmlog(RPMLOG_ERR, "failed to create UUID\n");
    01404         else
    01405             b = buf;
    01406 /*@=nullpass@*/
    01407     } else if (STREQ("S", f, fn)) {
    01408         for (b = buf; (c = (int)*b) && xisdigit(c);)
    01409             b++;
    01410         if (!c) {       /* digit index */
    01411             b++;
    01412             sprintf(b, "%%SOURCE%s", buf);
    01413         } else
    01414             b = buf;
    01415     } else if (STREQ("P", f, fn)) {
    01416         for (b = buf; (c = (int) *b) && xisdigit(c);)
    01417             b++;
    01418         if (!c) {       /* digit index */
    01419             b++;
    01420             sprintf(b, "%%PATCH%s", buf);
    01421         } else
    01422             b = buf;
    01423     } else if (STREQ("F", f, fn)) {
    01424         b = buf + strlen(buf) + 1;
    01425         sprintf(b, "file%s.file", buf);
    01426     }
    01427
    01428     if (b) {
    01429         (void) expandT(mb, b, strlen(b));
    01430     }
    01431 }
    01432
    01433 static int expandFIFO(MacroBuf mb, MacroEntry me, const char *g, size_t gn)
    01434         /*@globals rpmGlobalMacroContext, h_errno, fileSystem, internalState @*/
    01435         /*@modifies mb, rpmGlobalMacroContext, fileSystem, internalState @*/
    01436 {
    01437     int rc = 0;
    01438
    01439     if (me) {
    01440         if (me->prev) {
    01441             rc = expandFIFO(mb, me->prev, g, gn);
    01442             rc = expandT(mb, g, gn);
    01443         }
    01444         rc = expandT(mb, me->body, strlen(me->body));
    01445     }
    01446     return rc;
    01447 }
    01448
    01449 #if !defined(DEBUG_MACROS)
    01450 /* =============================================================== */
    01451 /* XXX dupe'd to avoid change in linkage conventions. */
    01452
    01453 #define POPT_ERROR_NOARG        -10
    01454 #define POPT_ERROR_BADQUOTE     -15
    01455 #define POPT_ERROR_MALLOC       -21
    01457 #define POPT_ARGV_ARRAY_GROW_DELTA 5
    01458
    01459 static int XpoptDupArgv(int argc, const char **argv,
    01460                 int * argcPtr, const char *** argvPtr)
    01461         /*@modifies *argcPtr, *argvPtr @*/
    01462 {
    01463     size_t nb = (argc + 1) * sizeof(*argv);
    01464     const char ** argv2;
    01465     char * dst;
    01466     int i;
    01467
    01468     if (argc <= 0 || argv == NULL)      /* XXX can't happen */
    01469         return POPT_ERROR_NOARG;
    01470     for (i = 0; i < argc; i++) {
    01471         if (argv[i] == NULL)
    01472             return POPT_ERROR_NOARG;
    01473         nb += strlen(argv[i]) + 1;
    01474     }
    01475
    01476     dst = xmalloc(nb);
    01477     if (dst == NULL)                    /* XXX can't happen */
    01478         return POPT_ERROR_MALLOC;
    01479     argv2 = (void *) dst;
    01480     dst += (argc + 1) * sizeof(*argv);
    01481
    01482     for (i = 0; i < argc; i++) {
    01483         argv2[i] = dst;
    01484         dst += strlen(strcpy(dst, argv[i])) + 1;
    01485     }
    01486     argv2[argc] = NULL;
    01487
    01488     if (argvPtr) {
    01489         *argvPtr = argv2;
    01490     } else {
    01491         free(argv2);
    01492         argv2 = NULL;
    01493     }
    01494     if (argcPtr)
    01495         *argcPtr = argc;
    01496     return 0;
    01497 }
    01498
    01499 static int XpoptParseArgvString(const char * s, int * argcPtr, const char *** argvPtr)
    01500         /*@modifies *argcPtr, *argvPtr @*/
    01501 {
    01502     const char * src;
    01503     char quote = '\0';
    01504     int argvAlloced = POPT_ARGV_ARRAY_GROW_DELTA;
    01505     const char ** argv = xmalloc(sizeof(*argv) * argvAlloced);
    01506     int argc = 0;
    01507     size_t buflen = strlen(s) + 1;
    01508     char * buf = memset(alloca(buflen), 0, buflen);
    01509     int rc = POPT_ERROR_MALLOC;
    01510
    01511     if (argv == NULL) return rc;
    01512     argv[argc] = buf;
    01513
    01514     for (src = s; *src != '\0'; src++) {
    01515         if (quote == *src) {
    01516             quote = '\0';
    01517         } else if (quote != '\0') {
    01518             if (*src == '\\') {
    01519                 src++;
    01520                 if (!*src) {
    01521                     rc = POPT_ERROR_BADQUOTE;
    01522                     goto exit;
    01523                 }
    01524                 if (*src != quote) *buf++ = '\\';
    01525             }
    01526             *buf++ = *src;
    01527         } else if (isspace(*src)) {
    01528             if (*argv[argc] != '\0') {
    01529                 buf++, argc++;
    01530                 if (argc == argvAlloced) {
    01531                     argvAlloced += POPT_ARGV_ARRAY_GROW_DELTA;
    01532                     argv = realloc(argv, sizeof(*argv) * argvAlloced);
    01533                     if (argv == NULL) goto exit;
    01534                 }
    01535                 argv[argc] = buf;
    01536             }
    01537         } else switch (*src) {
    01538           case '"':
    01539           case '\'':
    01540             quote = *src;
    01541             /*@switchbreak@*/ break;
    01542           case '\\':
    01543             src++;
    01544             if (!*src) {
    01545                 rc = POPT_ERROR_BADQUOTE;
    01546                 goto exit;
    01547             }
    01548             /*@fallthrough@*/
    01549           default:
    01550             *buf++ = *src;
    01551             /*@switchbreak@*/ break;
    01552         }
    01553     }
    01554
    01555     if (strlen(argv[argc])) {
    01556         argc++, buf++;
    01557     }
    01558
    01559     rc = XpoptDupArgv(argc, argv, argcPtr, argvPtr);
    01560
    01561 exit:
    01562     if (argv) free(argv);
    01563     return rc;
    01564 }
    01565 #endif  /* !defined(DEBUG_MACROS) */
    01566
    01574 #if defined(WITH_FICL) || defined(WITH_JS) || defined(WITH_PERLEMBED) || defined(WITH_PYTHONEMBED) || defined(WITH_RUBYEMBED) || defined(WITH_TCL)
    01575 static char * parseEmbedded(const char * s, size_t nb, const char *** avp)
    01576         /*@*/
    01577 {
    01578     char * script = NULL;
    01579     const char * se;
    01580
    01581     /* XXX FIXME: args might have embedded : too. */
    01582     for (se = s + 1; se < (s+nb); se++)
    01583     switch (*se) {
    01584     default:    continue;       /*@notreached@*/ break;
    01585     case ':':   goto bingo;     /*@notreached@*/ break;
    01586     }
    01587
    01588 bingo:
    01589     {   size_t na = (size_t)(se-s-1);
    01590         char * args = NULL;
    01591         int ac;
    01592         int rc;
    01593
    01594         args = memcpy(xmalloc(na+1), s+1, na);
    01595         args[na] = '\0';
    01596
    01597         ac = 0;
    01598         rc = XpoptParseArgvString(args, &ac, avp);
    01599         args = _free(args);
    01600         nb -= na;
    01601     }
    01602
    01603     nb -= 3;
    01604     script = memcpy(xmalloc(nb+1), se+1, nb+1);
    01605     script[nb] = '\0';
    01606     return script;
    01607 }
    01608 #endif
    01609
    01616 static int
    01617 expandMacro(MacroBuf mb)
    01618         /*@globals rpmGlobalMacroContext,
    01619                 print_macro_trace, print_expand_trace, h_errno,
    01620                 fileSystem, internalState @*/
    01621         /*@modifies mb, rpmGlobalMacroContext,
    01622                 print_macro_trace, print_expand_trace,
    01623                 fileSystem, internalState @*/
    01624 {
    01625     MacroEntry *mep;
    01626     MacroEntry me;
    01627     const char *s = mb->s, *se;
    01628     const char *f, *fe;
    01629     const char *g, *ge;
    01630     size_t fn, gn;
    01631     char *t = mb->t;    /* save expansion pointer for printExpand */
    01632     int c;
    01633     int rc = 0;
    01634     int negate;
    01635     int stackarray;
    01636     const char * lastc;
    01637     int chkexist;
    01638
    01639     if (++mb->depth > max_macro_depth) {
    01640         rpmlog(RPMLOG_ERR,
    01641                 _("Recursion depth(%d) greater than max(%d)\n"),
    01642                 mb->depth, max_macro_depth);
    01643         mb->depth--;
    01644         mb->expand_trace = 1;
    01645         return 1;
    01646     }
    01647
    01648     while (rc == 0 && mb->nb > 0 && (c = (int)*s) != (int)'\0') {
    01649         s++;
    01650         /* Copy text until next macro */
    01651         switch(c) {
    01652         case '%':
    01653                 if (*s != '\0') {       /* Ensure not end-of-string. */
    01654                     if (*s != '%')
    01655                         /*@switchbreak@*/ break;
    01656                     s++;        /* skip first % in %% */
    01657                 }
    01658                 /*@fallthrough@*/
    01659         default:
    01660                 SAVECHAR(mb, c);
    01661                 continue;
    01662                 /*@notreached@*/ /*@switchbreak@*/ break;
    01663         }
    01664
    01665         /* Expand next macro */
    01666         f = fe = NULL;
    01667         g = ge = NULL;
    01668         if (mb->depth > 1)      /* XXX full expansion for outermost level */
    01669                 t = mb->t;      /* save expansion pointer for printExpand */
    01670         stackarray = chkexist = negate = 0;
    01671         lastc = NULL;
    01672         switch ((c = (int) *s)) {
    01673         default:                /* %name substitution */
    01674                 while (*s != '\0' && strchr("!?@", *s) != NULL) {
    01675                         switch(*s++) {
    01676                         case '@':
    01677                                 stackarray = ((stackarray + 1) % 2);
    01678                                 /*@switchbreak@*/ break;
    01679                         case '!':
    01680                                 negate = ((negate + 1) % 2);
    01681                                 /*@switchbreak@*/ break;
    01682                         case '?':
    01683                                 chkexist++;
    01684                                 /*@switchbreak@*/ break;
    01685                         }
    01686                 }
    01687                 f = se = s;
    01688                 if (*se == '-')
    01689                         se++;
    01690                 while((c = (int) *se) && (xisalnum(c) || c == (int) '_'))
    01691                         se++;
    01692                 /* Recognize non-alnum macros too */
    01693                 switch (*se) {
    01694                 case '*':
    01695                         se++;
    01696                         if (*se == '*') se++;
    01697                         /*@innerbreak@*/ break;
    01698                 case '#':
    01699                         se++;
    01700                         /*@innerbreak@*/ break;
    01701                 default:
    01702                         /*@innerbreak@*/ break;
    01703                 }
    01704                 fe = se;
    01705                 /* For "%name " macros ... */
    01706 /*@-globs@*/
    01707                 if ((c = (int) *fe) && isblank(c))
    01708                         if ((lastc = strchr(fe,'\n')) == NULL)
    01709                                 lastc = strchr(fe, '\0');
    01710 /*@=globs@*/
    01711                 /*@switchbreak@*/ break;
    01712         case '(':               /* %(...) shell escape */
    01713                 if ((se = matchchar(s, (char)c, ')')) == NULL) {
    01714                         rpmlog(RPMLOG_ERR,
    01715                                 _("Unterminated %c: %s\n"), (char)c, s);
    01716                         rc = 1;
    01717                         continue;
    01718                 }
    01719                 if (mb->macro_trace)
    01720                         printMacro(mb, s, se+1);
    01721
    01722                 s++;    /* skip ( */
    01723                 rc = doShellEscape(mb, s, (se - s));
    01724                 se++;   /* skip ) */
    01725
    01726                 s = se;
    01727                 continue;
    01728                 /*@notreached@*/ /*@switchbreak@*/ break;
    01729         case '{':               /* %{...}/%{...:...} substitution */
    01730                 if ((se = matchchar(s, (char)c, '}')) == NULL) {
    01731                         rpmlog(RPMLOG_ERR,
    01732                                 _("Unterminated %c: %s\n"), (char)c, s);
    01733                         rc = 1;
    01734                         continue;
    01735                 }
    01736                 f = s+1;/* skip { */
    01737                 se++;   /* skip } */
    01738                 while (strchr("!?@", *f) != NULL) {
    01739                         switch(*f++) {
    01740                         case '@':
    01741                                 stackarray = ((stackarray + 1) % 2);
    01742                                 /*@switchbreak@*/ break;
    01743                         case '!':
    01744                                 negate = ((negate + 1) % 2);
    01745                                 /*@switchbreak@*/ break;
    01746                         case '?':
    01747                                 chkexist++;
    01748                                 /*@switchbreak@*/ break;
    01749                         }
    01750                 }
    01751                 /* Find end-of-expansion, handle %{foo:bar} expansions. */
    01752                 for (fe = f; (c = (int) *fe) && !strchr(" :}", c);)
    01753                         fe++;
    01754                 switch (c) {
    01755                 case ':':
    01756                         g = fe + 1;
    01757                         ge = se - 1;
    01758                         /*@innerbreak@*/ break;
    01759                 case ' ':
    01760                         lastc = se-1;
    01761                         /*@innerbreak@*/ break;
    01762                 default:
    01763                         /*@innerbreak@*/ break;
    01764                 }
    01765                 /*@switchbreak@*/ break;
    01766         }
    01767
    01768         /* XXX Everything below expects fe > f */
    01769         fn = (fe - f);
    01770         gn = (ge - g);
    01771         if ((fe - f) <= 0) {
    01772 /* XXX Process % in unknown context */
    01773                 c = (int) '%';  /* XXX only need to save % */
    01774                 SAVECHAR(mb, c);
    01775 #if 0
    01776                 rpmlog(RPMLOG_ERR,
    01777                         _("A %% is followed by an unparseable macro\n"));
    01778 #endif
    01779                 s = se;
    01780                 continue;
    01781         }
    01782
    01783         if (mb->macro_trace)
    01784                 printMacro(mb, s, se);
    01785
    01786         /* Expand builtin macros */
    01787         if (STREQ("load", f, fn)) {
    01788                 if (g != NULL) {
    01789                     char * mfn = strncpy(alloca(gn + 1), g, gn);
    01790                     int xx;
    01791                     mfn[gn] = '\0';
    01792                     xx = rpmLoadMacroFile(NULL, mfn);
    01793                     /* Print failure iff %{load:...} or %{!?load:...} */
    01794                     if (xx != 0 && chkexist == negate)
    01795                         rpmlog(RPMLOG_ERR, _("%s: load macros failed\n"), mfn);
    01796                 }
    01797                 s = se;
    01798                 continue;
    01799         }
    01800         if (STREQ("global", f, fn)) {
    01801                 s = doDefine(mb, se, RMIL_GLOBAL, 1);
    01802                 continue;
    01803         }
    01804         if (STREQ("define", f, fn)) {
    01805                 s = doDefine(mb, se, mb->depth, 0);
    01806                 continue;
    01807         }
    01808         if (STREQ("undefine", f, fn)) {
    01809                 s = doUndefine(mb->mc, se);
    01810                 continue;
    01811         }
    01812
    01813         if (STREQ("echo", f, fn) ||
    01814             STREQ("warn", f, fn) ||
    01815             STREQ("error", f, fn)) {
    01816                 int waserror = 0;
    01817                 if (STREQ("error", f, fn))
    01818                         waserror = 1, rc = 1;
    01819                 if (g != NULL && g < ge)
    01820                         doOutput(mb, waserror, g, gn);
    01821                 else
    01822                         doOutput(mb, waserror, f, fn);
    01823                 s = se;
    01824                 continue;
    01825         }
    01826
    01827         if (STREQ("trace", f, fn)) {
    01828                 /* XXX TODO restore expand_trace/macro_trace to 0 on return */
    01829                 mb->expand_trace = mb->macro_trace = (negate ? 0 : mb->depth);
    01830                 if (mb->depth == 1) {
    01831                         print_macro_trace = mb->macro_trace;
    01832                         print_expand_trace = mb->expand_trace;
    01833                 }
    01834                 s = se;
    01835                 continue;
    01836         }
    01837
    01838         if (STREQ("dump", f, fn)) {
    01839                 rpmDumpMacroTable(mb->mc, NULL);
    01840                 while (iseol(*se))
    01841                         se++;
    01842                 s = se;
    01843                 continue;
    01844         }
    01845
    01846 #ifdef  WITH_LUA
    01847         if (STREQ("lua", f, fn)) {
    01848                 rpmlua lua = rpmluaGetGlobalState();
    01849                 rpmlua olua = memcpy(alloca(sizeof(*olua)), lua, sizeof(*olua));
    01850                 const char *ls = s+sizeof("{lua:")-1;
    01851                 const char *lse = se-sizeof("}")+1;
    01852                 char *scriptbuf = (char *)xmalloc((lse-ls)+1);
    01853                 const char *printbuf;
    01854
    01855                 /* Reset the stateful output buffer before recursing down. */
    01856                 lua->storeprint = 1;
    01857                 lua->printbuf = NULL;
    01858                 lua->printbufsize = 0;
    01859                 lua->printbufused = 0;
    01860
    01861                 memcpy(scriptbuf, ls, lse-ls);
    01862                 scriptbuf[lse-ls] = '\0';
    01863                 if (rpmluaRunScript(lua, scriptbuf, NULL) == -1)
    01864                     rc = 1;
    01865                 printbuf = rpmluaGetPrintBuffer(lua);
    01866                 if (printbuf) {
    01867                     size_t len = strlen(printbuf);
    01868                     if (len > mb->nb)
    01869                         len = mb->nb;
    01870                     memcpy(mb->t, printbuf, len);
    01871                     mb->t += len;
    01872                     mb->nb -= len;
    01873                 }
    01874
    01875                 /* Restore the stateful output buffer after recursion. */
    01876                 lua->storeprint = olua->storeprint;
    01877                 lua->printbuf = olua->printbuf;
    01878                 lua->printbufsize = olua->printbufsize;
    01879                 lua->printbufused = olua->printbufused;
    01880
    01881                 free(scriptbuf);
    01882                 s = se;
    01883                 continue;
    01884         }
    01885 #endif
    01886
    01887 #ifdef  WITH_FICL
    01888         if (STREQ("ficl", f, fn)) {
    01889                 const char ** av = NULL;
    01890                 char * script = parseEmbedded(s, (size_t)(se-s), &av);
    01891                 rpmficl ficl = rpmficlNew(av, _globalI);
    01892                 const char * result = NULL;
    01893
    01894                 if (rpmficlRun(ficl, script, &result) != RPMRC_OK)
    01895                     rc = 1;
    01896                 else {
    01897                   if (result == NULL) result = "FIXME";
    01898                   if (result != NULL && *result != '\0') {
    01899                     size_t len = strlen(result);
    01900                     if (len > mb->nb)
    01901                         len = mb->nb;
    01902                     memcpy(mb->t, result, len);
    01903                     mb->t += len;
    01904                     mb->nb -= len;
    01905                  }
    01906                 }
    01907                 ficl = rpmficlFree(ficl);
    01908                 av = _free(av);
    01909                 script = _free(script);
    01910                 s = se;
    01911                 continue;
    01912         }
    01913 #endif
    01914
    01915 #ifdef  WITH_JS
    01916         if (STREQ("js", f, fn)) {
    01917                 const char ** av = NULL;
    01918                 char * script = parseEmbedded(s, (size_t)(se-s), &av);
    01919                 rpmjs js = rpmjsNew(av, _globalI);
    01920                 const char * result = NULL;
    01921
    01922                 if (rpmjsRun(js, script, &result) != RPMRC_OK)
    01923                     rc = 1;
    01924                 else {
    01925                   if (result == NULL) result = "FIXME";
    01926                   if (result != NULL && *result != '\0') {
    01927                     size_t len = strlen(result);
    01928                     if (len > mb->nb)
    01929                         len = mb->nb;
    01930                     memcpy(mb->t, result, len);
    01931                     mb->t += len;
    01932                     mb->nb -= len;
    01933                  }
    01934                 }
    01935                 js = rpmjsFree(js);
    01936                 av = _free(av);
    01937                 script = _free(script);
    01938                 s = se;
    01939                 continue;
    01940         }
    01941 #endif
    01942
    01943 #ifdef  WITH_PERLEMBED
    01944         if (STREQ("perl", f, fn)) {
    01945                 const char ** av = NULL;
    01946                 char * script = parseEmbedded(s, (size_t)(se-s), &av);
    01947                 rpmperl perl = rpmperlNew(av, _globalI);
    01948                 const char * result = NULL;
    01949
    01950                 if (rpmperlRun(perl, script, &result) != RPMRC_OK)
    01951                     rc = 1;
    01952                 else {
    01953                   if (result == NULL) result = "FIXME";
    01954                   if (result != NULL && *result != '\0') {
    01955                     size_t len = strlen(result);
    01956                     if (len > mb->nb)
    01957                         len = mb->nb;
    01958                     memcpy(mb->t, result, len);
    01959                     mb->t += len;
    01960                     mb->nb -= len;
    01961                  }
    01962                 }
    01963                 perl = rpmperlFree(perl);
    01964                 av = _free(av);
    01965                 script = _free(script);
    01966                 s = se;
    01967                 continue;
    01968         }
    01969 #endif
    01970
    01971 #ifdef  WITH_PYTHONEMBED
    01972         if (STREQ("python", f, fn)) {
    01973                 const char ** av = NULL;
    01974                 char * script = parseEmbedded(s, (size_t)(se-s), &av);
    01975                 rpmpython python = rpmpythonNew(av, _globalI);
    01976                 const char * result = NULL;
    01977
    01978                 if (rpmpythonRun(python, script, &result) != RPMRC_OK)
    01979                     rc = 1;
    01980                 else {
    01981                   if (result == NULL) result = "FIXME";
    01982                   if (result != NULL && *result != '\0') {
    01983                     size_t len = strlen(result);
    01984                     if (len > mb->nb)
    01985                         len = mb->nb;
    01986                     memcpy(mb->t, result, len);
    01987                     mb->t += len;
    01988                     mb->nb -= len;
    01989                   }
    01990                 }
    01991                 python = rpmpythonFree(python);
    01992                 av = _free(av);
    01993                 script = _free(script);
    01994                 s = se;
    01995                 continue;
    01996         }
    01997 #endif
    01998
    01999 #ifdef  WITH_RUBYEMBED
    02000         if (STREQ("ruby", f, fn)) {
    02001                 const char ** av = NULL;
    02002                 char * script = parseEmbedded(s, (size_t)(se-s), &av);
    02003                 rpmruby ruby = rpmrubyNew(av, _globalI);
    02004                 const char * result = NULL;
    02005
    02006                 if (rpmrubyRun(ruby, script, &result) != RPMRC_OK)
    02007                     rc = 1;
    02008                 else {
    02009                   if (result == NULL) result = "FIXME";
    02010                   if (result != NULL && *result != '\0') {
    02011                     size_t len = strlen(result);
    02012                     if (len > mb->nb)
    02013                         len = mb->nb;
    02014                     memcpy(mb->t, result, len);
    02015                     mb->t += len;
    02016                     mb->nb -= len;
    02017                   }
    02018                 }
    02019                 ruby = rpmrubyFree(ruby);
    02020                 av = _free(av);
    02021                 script = _free(script);
    02022                 s = se;
    02023                 continue;
    02024         }
    02025 #endif
    02026
    02027 #ifdef  WITH_TCL
    02028         if (STREQ("tcl", f, fn)) {
    02029                 const char ** av = NULL;
    02030                 char * script = parseEmbedded(s, (size_t)(se-s), &av);
    02031                 rpmtcl tcl = rpmtclNew(av, _globalI);
    02032                 const char * result = NULL;
    02033
    02034                 if (rpmtclRun(tcl, script, &result) != RPMRC_OK)
    02035                     rc = 1;
    02036                 else if (result != NULL && *result != '\0') {
    02037                     size_t len = strlen(result);
    02038                     if (len > mb->nb)
    02039                         len = mb->nb;
    02040                     memcpy(mb->t, result, len);
    02041                     mb->t += len;
    02042                     mb->nb -= len;
    02043                 }
    02044                 tcl = rpmtclFree(tcl);
    02045                 av = _free(av);
    02046                 script = _free(script);
    02047                 s = se;
    02048                 continue;
    02049         }
    02050 #endif
    02051
    02052         /* Rewrite "%patchNN ..." as "%patch -P NN ..." and expand. */
    02053         if (lastc && fn > 5 && STREQ("patch", f, 5) && xisdigit((int)f[5])) {
    02054                 /*@-internalglobs@*/ /* FIX: verbose may be set */
    02055                 doFoo(mb, negate, f, (lastc - f), NULL, 0);
    02056                 /*@=internalglobs@*/
    02057                 s = lastc;
    02058                 continue;
    02059         }
    02060
    02061         /* XXX necessary but clunky */
    02062         if (STREQ("basename", f, fn) ||
    02063             STREQ("dirname", f, fn) ||
    02064             STREQ("realpath", f, fn) ||
    02065             STREQ("getenv", f, fn) ||
    02066             STREQ("shrink", f, fn) ||
    02067             STREQ("suffix", f, fn) ||
    02068             STREQ("expand", f, fn) ||
    02069             STREQ("verbose", f, fn) ||
    02070             STREQ("uncompress", f, fn) ||
    02071             STREQ("mkstemp", f, fn) ||
    02072             STREQ("mkdtemp", f, fn) ||
    02073             STREQ("uuid", f, fn) ||
    02074             STREQ("url2path", f, fn) ||
    02075             STREQ("u2p", f, fn) ||
    02076             STREQ("S", f, fn) ||
    02077             STREQ("P", f, fn) ||
    02078             STREQ("F", f, fn)) {
    02079                 /*@-internalglobs@*/ /* FIX: verbose may be set */
    02080                 doFoo(mb, negate, f, fn, g, gn);
    02081                 /*@=internalglobs@*/
    02082                 s = se;
    02083                 continue;
    02084         }
    02085
    02086         /* Expand defined macros */
    02087         mep = findEntry(mb->mc, f, fn);
    02088         me = (mep ? *mep : NULL);
    02089
    02090         /* XXX Special processing for flags */
    02091         if (*f == '-') {
    02092                 if (me)
    02093                         me->used++;     /* Mark macro as used */
    02094                 if ((me == NULL && !negate) ||  /* Without -f, skip %{-f...} */
    02095                     (me != NULL && negate)) {   /* With -f, skip %{!-f...} */
    02096                         s = se;
    02097                         continue;
    02098                 }
    02099
    02100                 if (g && g < ge) {              /* Expand X in %{-f:X} */
    02101                         rc = expandT(mb, g, gn);
    02102                 } else
    02103                 if (me && me->body && *me->body) {/* Expand %{-f}/%{-f*} */
    02104                         rc = expandT(mb, me->body, strlen(me->body));
    02105                 }
    02106                 s = se;
    02107                 continue;
    02108         }
    02109
    02110         /* XXX Special processing for macro existence */
    02111         if (chkexist) {
    02112                 if ((me == NULL && !negate) ||  /* Without -f, skip %{?f...} */
    02113                     (me != NULL && negate)) {   /* With -f, skip %{!?f...} */
    02114                         s = se;
    02115                         continue;
    02116                 }
    02117                 if (g && g < ge) {              /* Expand X in %{?f:X} */
    02118                         rc = expandT(mb, g, gn);
    02119                 } else
    02120                 if (me && me->body && *me->body) { /* Expand %{?f}/%{?f*} */
    02121                         rc = expandT(mb, me->body, strlen(me->body));
    02122                 }
    02123                 s = se;
    02124                 continue;
    02125         }
    02126
    02127         if (me == NULL) {       /* leave unknown %... as is */
    02128 #if !defined(RPM_VENDOR_WINDRIVER_DEBUG)        /* XXX usually disabled */
    02129 #if DEAD
    02130                 /* XXX hack to skip over empty arg list */
    02131                 if (fn == 1 && *f == '*') {
    02132                         s = se;
    02133                         continue;
    02134                 }
    02135 #endif
    02136                 /* XXX hack to permit non-overloaded %foo to be passed */
    02137                 c = (int) '%';  /* XXX only need to save % */
    02138                 SAVECHAR(mb, c);
    02139 #else
    02140                 if (!strncmp(f, "if", fn) ||
    02141                     !strncmp(f, "else", fn) ||
    02142                     !strncmp(f, "endif", fn)) {
    02143                         c = '%';        /* XXX only need to save % */
    02144                         SAVECHAR(mb, c);
    02145                 } else {
    02146                         rpmlog(RPMLOG_ERR,
    02147                                 _("Macro %%%.*s not found, skipping\n"), fn, f);
    02148                         s = se;
    02149                 }
    02150 #endif
    02151                 continue;
    02152         }
    02153
    02154         /* XXX Special processing to create a tuple from stack'd values. */
    02155         if (stackarray) {
    02156                 if (!(g && g < ge)) {
    02157                         g = "\n";
    02158                         gn = strlen(g);
    02159                 }
    02160                 rc = expandFIFO(mb, me, g, gn);
    02161                 s = se;
    02162                 continue;
    02163         }
    02164
    02165         /* Setup args for "%name " macros with opts */
    02166         if (me && me->opts != NULL) {
    02167                 if (lastc != NULL) {
    02168                         se = grabArgs(mb, me, fe, lastc);
    02169                 } else {
    02170                         addMacro(mb->mc, "**", NULL, "", mb->depth);
    02171                         addMacro(mb->mc, "*", NULL, "", mb->depth);
    02172                         addMacro(mb->mc, "#", NULL, "0", mb->depth);
    02173                         addMacro(mb->mc, "0", NULL, me->name, mb->depth);
    02174                 }
    02175         }
    02176
    02177         /* Recursively expand body of macro */
    02178         if (me->body && *me->body) {
    02179                 mb->s = me->body;
    02180                 rc = expandMacro(mb);
    02181                 if (rc == 0)
    02182                         me->used++;     /* Mark macro as used */
    02183         }
    02184
    02185         /* Free args for "%name " macros with opts */
    02186         if (me->opts != NULL)
    02187                 freeArgs(mb);
    02188
    02189         s = se;
    02190     }
    02191
    02192     *mb->t = '\0';
    02193     mb->s = s;
    02194     mb->depth--;
    02195     if (rc != 0 || mb->expand_trace)
    02196         printExpansion(mb, t, mb->t);
    02197     return rc;
    02198 }
    02199
    02200 #if defined(RPM_VENDOR_OPENPKG) /* stick-with-rpm-file-sanity-checking */ || \
    02201     !defined(POPT_ERROR_BADCONFIG)      /* XXX POPT 1.15 retrofit */
    02202 int rpmSecuritySaneFile(const char *filename)
    02203 {
    02204     struct stat sb;
    02205     uid_t uid;
    02206
    02207     if (stat(filename, &sb) == -1)
    02208         return 1;
    02209     uid = getuid();
    02210     if (sb.st_uid != uid)
    02211         return 0;
    02212     if (!S_ISREG(sb.st_mode))
    02213         return 0;
    02214     if (sb.st_mode & (S_IWGRP|S_IWOTH))
    02215         return 0;
    02216     return 1;
    02217 }
    02218 #endif
    02219
    02220 #if !defined(DEBUG_MACROS)
    02221 /* =============================================================== */
    02222 /*@unchecked@*/
    02223 static int _debug = 0;
    02224
    02225 int rpmGlob(const char * patterns, int * argcPtr, const char *** argvPtr)
    02226 {
    02227     int ac = 0;
    02228     const char ** av = NULL;
    02229     int argc = 0;
    02230     const char ** argv = NULL;
    02231     char * globRoot = NULL;
    02232 #ifdef ENABLE_NLS
    02233     const char * old_collate = NULL;
    02234     const char * old_ctype = NULL;
    02235     const char * t;
    02236 #endif
    02237     size_t maxb, nb;
    02238     size_t i;
    02239     int j;
    02240     int rc;
    02241
    02242     rc = XpoptParseArgvString(patterns, &ac, &av);
    02243     if (rc)
    02244         return rc;
    02245 #ifdef ENABLE_NLS
    02246     t = setlocale(LC_COLLATE, NULL);
    02247     if (t)
    02248         old_collate = xstrdup(t);
    02249     t = setlocale(LC_CTYPE, NULL);
    02250     if (t)
    02251         old_ctype = xstrdup(t);
    02252     (void) setlocale(LC_COLLATE, "C");
    02253     (void) setlocale(LC_CTYPE, "C");
    02254 #endif
    02255
    02256     if (av != NULL)
    02257     for (j = 0; j < ac; j++) {
    02258         const char * globURL;
    02259         const char * path;
    02260         int ut = urlPath(av[j], &path);
    02261         glob_t gl;
    02262
    02263         if (!Glob_pattern_p(av[j], 0) && strchr(path, '~') == NULL) {
    02264             argv = xrealloc(argv, (argc+2) * sizeof(*argv));
    02265             argv[argc] = xstrdup(av[j]);
    02266 if (_debug)
    02267 fprintf(stderr, "*** rpmGlob argv[%d] \"%s\"\n", argc, argv[argc]);
    02268             argc++;
    02269             continue;
    02270         }
    02271
    02272         gl.gl_pathc = 0;
    02273         gl.gl_pathv = NULL;
    02274         rc = Glob(av[j], GLOB_TILDE, Glob_error, &gl);
    02275         if (rc)
    02276             goto exit;
    02277
    02278         /* XXX Prepend the URL leader for globs that have stripped it off */
    02279         maxb = 0;
    02280         for (i = 0; i < gl.gl_pathc; i++) {
    02281             if ((nb = strlen(&(gl.gl_pathv[i][0]))) > maxb)
    02282                 maxb = nb;
    02283         }
    02284
    02285         nb = ((ut == URL_IS_PATH) ? (path - av[j]) : 0);
    02286         maxb += nb;
    02287         maxb += 1;
    02288         globURL = globRoot = xmalloc(maxb);
    02289
    02290         switch (ut) {
    02291         case URL_IS_PATH:
    02292         case URL_IS_DASH:
    02293             strncpy(globRoot, av[j], nb);
    02294             /*@switchbreak@*/ break;
    02295         case URL_IS_HTTPS:
    02296         case URL_IS_HTTP:
    02297         case URL_IS_FTP:
    02298         case URL_IS_HKP:
    02299         case URL_IS_UNKNOWN:
    02300         default:
    02301             /*@switchbreak@*/ break;
    02302         }
    02303         globRoot += nb;
    02304         *globRoot = '\0';
    02305 if (_debug)
    02306 fprintf(stderr, "*** GLOB maxb %d diskURL %d %*s globURL %p %s\n", (int)maxb, (int)nb, (int)nb, av[j], globURL, globURL);
    02307
    02308         argv = xrealloc(argv, (argc+gl.gl_pathc+1) * sizeof(*argv));
    02309
    02310         if (argv != NULL)
    02311         for (i = 0; i < gl.gl_pathc; i++) {
    02312             const char * globFile = &(gl.gl_pathv[i][0]);
    02313             if (globRoot > globURL && globRoot[-1] == '/')
    02314                 while (*globFile == '/') globFile++;
    02315             strcpy(globRoot, globFile);
    02316 if (_debug)
    02317 fprintf(stderr, "*** rpmGlob argv[%d] \"%s\"\n", argc, globURL);
    02318             argv[argc++] = xstrdup(globURL);
    02319         }
    02320         /*@-immediatetrans@*/
    02321         Globfree(&gl);
    02322         /*@=immediatetrans@*/
    02323         globURL = _free(globURL);
    02324     }
    02325
    02326     if (argv != NULL && argc > 0) {
    02327         argv[argc] = NULL;
    02328         if (argvPtr)
    02329             *argvPtr = argv;
    02330         if (argcPtr)
    02331             *argcPtr = argc;
    02332         rc = 0;
    02333     } else
    02334         rc = 1;
    02335
    02336
    02337 exit:
    02338 #ifdef ENABLE_NLS
    02339     if (old_collate) {
    02340         (void) setlocale(LC_COLLATE, old_collate);
    02341         old_collate = _free(old_collate);
    02342     }
    02343     if (old_ctype) {
    02344         (void) setlocale(LC_CTYPE, old_ctype);
    02345         old_ctype = _free(old_ctype);
    02346     }
    02347 #endif
    02348     av = _free(av);
    02349     if (rc || argvPtr == NULL) {
    02350 /*@-dependenttrans -unqualifiedtrans@*/
    02351         if (argv != NULL)
    02352         for (j = 0; j < argc; j++)
    02353             argv[j] = _free(argv[j]);
    02354         argv = _free(argv);
    02355 /*@=dependenttrans =unqualifiedtrans@*/
    02356     }
    02357     return rc;
    02358 }
    02359 #endif  /* !defined(DEBUG_MACROS) */
    02360
    02361 /* =============================================================== */
    02362
    02363 int
    02364 expandMacros(void * spec, MacroContext mc, char * sbuf, size_t slen)
    02365 {
    02366     MacroBuf mb = alloca(sizeof(*mb));
    02367     char *tbuf;
    02368     int rc;
    02369
    02370     if (sbuf == NULL || slen == 0)
    02371         return 0;
    02372     if (mc == NULL) mc = rpmGlobalMacroContext;
    02373
    02374     tbuf = alloca(slen + 1);
    02375     tbuf[0] = '\0';
    02376
    02377     mb->s = sbuf;
    02378     mb->t = tbuf;
    02379     mb->nb = slen;
    02380     mb->depth = 0;
    02381     mb->macro_trace = print_macro_trace;
    02382     mb->expand_trace = print_expand_trace;
    02383
    02384     mb->spec = spec;    /* (future) %file expansion info */
    02385     mb->mc = mc;
    02386
    02387     rc = expandMacro(mb);
    02388
    02389     tbuf[slen] = '\0';
    02390     if (mb->nb == 0)
    02391         rpmlog(RPMLOG_ERR, _("Macro expansion too big for target buffer\n"));
    02392     else
    02393         strncpy(sbuf, tbuf, (slen - mb->nb + 1));
    02394
    02395     return rc;
    02396 }
    02397
    02398 void
    02399 addMacro(MacroContext mc,
    02400         const char * n, const char * o, const char * b, int level)
    02401 {
    02402     MacroEntry * mep;
    02403     const char * name = n;
    02404
    02405     if (*name == '.')           /* XXX readonly macros */
    02406         name++;
    02407     if (*name == '.')           /* XXX readonly macros */
    02408         name++;
    02409
    02410     if (mc == NULL) mc = rpmGlobalMacroContext;
    02411
    02412     /* If new name, expand macro table */
    02413     if ((mep = findEntry(mc, name, 0)) == NULL) {
    02414         if (mc->firstFree == mc->macrosAllocated)
    02415             expandMacroTable(mc);
    02416         if (mc->macroTable != NULL)
    02417             mep = mc->macroTable + mc->firstFree++;
    02418     }
    02419
    02420     if (mep != NULL) {
    02421         /* XXX permit "..foo" to be pushed over ".foo" */
    02422         if (*mep && (*mep)->flags && !(n[0] == '.' && n[1] == '.')) {
    02423             /* XXX avoid error message for %buildroot */
    02424             if (strcmp((*mep)->name, "buildroot"))
    02425                 rpmlog(RPMLOG_ERR, _("Macro '%s' is readonly and cannot be changed.\n"), n);
    02426             return;
    02427         }
    02428         /* Push macro over previous definition */
    02429         pushMacro(mep, n, o, b, level);
    02430
    02431         /* If new name, sort macro table */
    02432         if ((*mep)->prev == NULL)
    02433             sortMacroTable(mc);
    02434     }
    02435 }
    02436
    02437 void
    02438 delMacro(MacroContext mc, const char * n)
    02439 {
    02440     MacroEntry * mep;
    02441
    02442     if (mc == NULL) mc = rpmGlobalMacroContext;
    02443     /* If name exists, pop entry */
    02444     if ((mep = findEntry(mc, n, 0)) != NULL) {
    02445         popMacro(mep);
    02446         /* If deleted name, sort macro table */
    02447         if (!(mep && *mep))
    02448             sortMacroTable(mc);
    02449     }
    02450 }
    02451
    02452 /*@-mustmod@*/ /* LCL: mc is modified through mb->mc, mb is abstract */
    02453 int
    02454 rpmDefineMacro(MacroContext mc, const char * macro, int level)
    02455 {
    02456     MacroBuf mb = alloca(sizeof(*mb));
    02457
    02458     memset(mb, 0, sizeof(*mb));
    02459     /* XXX just enough to get by */
    02460     mb->mc = (mc ? mc : rpmGlobalMacroContext);
    02461     (void) doDefine(mb, macro, level, 0);
    02462     return 0;
    02463 }
    02464 /*@=mustmod@*/
    02465
    02466 /*@-mustmod@*/ /* LCL: mc is modified through mb->mc, mb is abstract */
    02467 int
    02468 rpmUndefineMacro(MacroContext mc, const char * macro)
    02469 {
    02470     (void) doUndefine(mc ? mc : rpmGlobalMacroContext, macro);
    02471     return 0;
    02472 }
    02473 /*@=mustmod@*/
    02474
    02475 void
    02476 rpmLoadMacros(MacroContext mc, int level)
    02477 {
    02478
    02479     if (mc == NULL || mc == rpmGlobalMacroContext)
    02480         return;
    02481
    02482     if (mc->macroTable != NULL) {
    02483         int i;
    02484         for (i = 0; i < mc->firstFree; i++) {
    02485             MacroEntry *mep, me;
    02486             mep = &mc->macroTable[i];
    02487             me = *mep;
    02488
    02489             if (me == NULL)             /* XXX this should never happen */
    02490                 continue;
    02491             addMacro(NULL, me->name, me->opts, me->body, (level - 1));
    02492         }
    02493     }
    02494 }
    02495
    02496 #if defined(RPM_VENDOR_OPENPKG) /* expand-macrosfile-macro */
    02497 static void expand_macrosfile_macro(const char *file_name, const char *buf, size_t bufn)
    02498 {
    02499     char *cp;
    02500     size_t l, k;
    02501     static const char *macro_name = "%{macrosfile}";
    02502
    02503     l = strlen(macro_name);
    02504     k = strlen(file_name);
    02505     while ((cp = strstr(buf, macro_name)) != NULL) {
    02506         if (((strlen(buf) - l) + k) < bufn) {
    02507             memmove(cp+k, cp+l, strlen(cp+l)+1);
    02508             memcpy(cp, file_name, k);
    02509         }
    02510     }
    02511     return;
    02512 }
    02513 #endif
    02514
    02515 int
    02516 rpmLoadMacroFile(MacroContext mc, const char * fn)
    02517 {
    02518     /* XXX TODO: teach rdcl() to read through a URI, eliminate ".fpio". */
    02519     FD_t fd = Fopen(fn, "r.fpio");
    02520     size_t bufn = _macro_BUFSIZ;
    02521     char *buf = alloca(bufn);
    02522     int rc = -1;
    02523
    02524     if (fd == NULL || Ferror(fd)) {
    02525         if (fd) (void) Fclose(fd);
    02526         return rc;
    02527     }
    02528
    02529     /* XXX Assume new fangled macro expansion */
    02530     /*@-mods@*/
    02531     max_macro_depth = _MAX_MACRO_DEPTH;
    02532     /*@=mods@*/
    02533
    02534     buf[0] = '\0';
    02535     while(rdcl(buf, bufn, fd) != NULL) {
    02536         char *n;
    02537         int c;
    02538
    02539         n = buf;
    02540         SKIPBLANK(n, c);
    02541
    02542         if (c != (int) '%')
    02543                 continue;
    02544         n++;    /* skip % */
    02545 #if defined(RPM_VENDOR_OPENPKG) /* expand-macro-source */
    02546         expand_macrosfile_macro(fn, buf, bufn);
    02547 #endif
    02548         rc = rpmDefineMacro(mc, n, RMIL_MACROFILES);
    02549     }
    02550     rc = Fclose(fd);
    02551     return rc;
    02552 }
    02553
    02554 void
    02555 rpmInitMacros(MacroContext mc, const char * macrofiles)
    02556 {
    02557     char *mfiles, *m, *me;
    02558
    02559     if (macrofiles == NULL)
    02560         return;
    02561 #ifdef  DYING
    02562     if (mc == NULL) mc = rpmGlobalMacroContext;
    02563 #endif
    02564
    02565     mfiles = xstrdup(macrofiles);
    02566     for (m = mfiles; m && *m != '\0'; m = me) {
    02567         const char ** av;
    02568         int ac;
    02569         int i;
    02570
    02571         for (me = m; (me = strchr(me, ':')) != NULL; me++) {
    02572             /* Skip over URI's. */
    02573             if (!(me[1] == '/' && me[2] == '/'))
    02574                 /*@innerbreak@*/ break;
    02575         }
    02576
    02577         if (me && *me == ':')
    02578             *me++ = '\0';
    02579         else
    02580             me = m + strlen(m);
    02581
    02582         /* Glob expand the macro file path element, expanding ~ to $HOME. */
    02583         ac = 0;
    02584         av = NULL;
    02585 #if defined(DEBUG_MACROS)
    02586         ac = 1;
    02587         av = xmalloc((ac + 1) * sizeof(*av));
    02588         av[0] = strdup(m);
    02589         av[1] = NULL;
    02590 #else
    02591         i = rpmGlob(m, &ac, &av);
    02592         if (i != 0)
    02593             continue;
    02594 #endif
    02595
    02596         /* Read macros from each file. */
    02597
    02598         for (i = 0; i < ac; i++) {
    02599             size_t slen = strlen(av[i]);
    02600             const char *fn = av[i];
    02601
    02602         if (fn[0] == '@' /* attention */) {
    02603             fn++;
    02604 #if defined(RPM_VENDOR_OPENPKG) /* stick-with-rpm-file-sanity-checking */ || \
    02605     !defined(POPT_ERROR_BADCONFIG)      /* XXX POPT 1.15 retrofit */
    02606             if (!rpmSecuritySaneFile(fn))
    02607 #else
    02608             if (!poptSaneFile(fn))
    02609 #endif
    02610             {
    02611                 rpmlog(RPMLOG_WARNING, "existing RPM macros file \"%s\" considered INSECURE -- not loaded\n", fn);
    02612                 /*@innercontinue@*/ continue;
    02613             }
    02614         }
    02615
    02616         /* Skip backup files and %config leftovers. */
    02617 #define _suffix(_s, _x) \
    02618     (slen >= sizeof(_x) && !strcmp((_s)+slen-(sizeof(_x)-1), (_x)))
    02619             if (!(_suffix(fn, "~")
    02620                || _suffix(fn, ".rpmnew")
    02621                || _suffix(fn, ".rpmorig")
    02622                || _suffix(fn, ".rpmsave"))
    02623                )
    02624                    (void) rpmLoadMacroFile(mc, fn);
    02625 #undef _suffix
    02626
    02627             av[i] = _free(av[i]);
    02628         }
    02629         av = _free(av);
    02630     }
    02631     mfiles = _free(mfiles);
    02632
    02633     /* Reload cmdline macros */
    02634     /*@-mods@*/
    02635     rpmLoadMacros(rpmCLIMacroContext, RMIL_CMDLINE);
    02636     /*@=mods@*/
    02637 }
    02638
    02639 /*@-globstate@*/
    02640 void
    02641 rpmFreeMacros(MacroContext mc)
    02642 {
    02643
    02644     if (mc == NULL) mc = rpmGlobalMacroContext;
    02645
    02646     if (mc->macroTable != NULL) {
    02647         int i;
    02648         for (i = 0; i < mc->firstFree; i++) {
    02649             MacroEntry me;
    02650             while ((me = mc->macroTable[i]) != NULL) {
    02651                 /* XXX cast to workaround const */
    02652                 /*@-onlytrans@*/
    02653                 if ((mc->macroTable[i] = me->prev) == NULL)
    02654                     me->name = _free(me->name);
    02655                 /*@=onlytrans@*/
    02656                 me->opts = _free(me->opts);
    02657                 me->body = _free(me->body);
    02658                 me = _free(me);
    02659             }
    02660         }
    02661         mc->macroTable = _free(mc->macroTable);
    02662     }
    02663     memset(mc, 0, sizeof(*mc));
    02664 }
    02665 /*@=globstate@*/
    02666
    02667 /* =============================================================== */
    02668 int isCompressed(const char * file, rpmCompressedMagic * compressed)
    02669 {
    02670     FD_t fd;
    02671     ssize_t nb;
    02672     int rc = -1;
    02673     unsigned char magic[13];
    02674 #if defined(RPM_VENDOR_OPENPKG) || defined(RPM_VENDOR_FEDORA) || defined(RPM_VENDOR_MANDRIVA) /* extension-based-compression-detection */
    02675     size_t file_len;
    02676 #endif
    02677
    02678     *compressed = COMPRESSED_NOT;
    02679
    02680 #if defined(RPM_VENDOR_OPENPKG) || defined(RPM_VENDOR_FEDORA) || defined(RPM_VENDOR_MANDRIVA) /* extension-based-compression-detection */
    02681     file_len = strlen(file);
    02682     if ((file_len > 4 && strcasecmp(file+file_len-4, ".tbz") == 0)
    02683      || (file_len > 4 && strcasecmp(file+file_len-4, ".bz2") == 0)) {
    02684         *compressed = COMPRESSED_BZIP2;
    02685         return 0;
    02686     } else
    02687     if (file_len > 4 && strcasecmp(file+file_len-4, ".zip") == 0) {
    02688         *compressed = COMPRESSED_ZIP;
    02689         return 0;
    02690     } else
    02691     if ((file_len > 4 && strcasecmp(file+file_len-4, ".tlz") == 0)
    02692      || (file_len > 5 && strcasecmp(file+file_len-5, ".lzma") == 0)) {
    02693         *compressed = COMPRESSED_LZMA;
    02694         return 0;
    02695     } else
    02696     if (file_len > 4 && strcasecmp(file+file_len-3, ".xz") == 0) {
    02697         *compressed = COMPRESSED_XZ;
    02698         return 0;
    02699     } else
    02700     if ((file_len > 4 && strcasecmp(file+file_len-4, ".tgz") == 0)
    02701      || (file_len > 3 && strcasecmp(file+file_len-3, ".gz") == 0)
    02702      || (file_len > 2 && strcasecmp(file+file_len-2, ".Z") == 0)) {
    02703         *compressed = COMPRESSED_OTHER;
    02704         return 0;
    02705     } else
    02706     if (file_len > 5 && strcasecmp(file+file_len-5, ".cpio") == 0) {
    02707         *compressed = COMPRESSED_NOT;
    02708         return 0;
    02709     } else
    02710     if (file_len > 4 && strcasecmp(file+file_len-4, ".tar") == 0) {
    02711         *compressed = COMPRESSED_NOT;
    02712         return 0;
    02713     }
    02714 #endif
    02715
    02716     fd = Fopen(file, "r");
    02717     if (fd == NULL || Ferror(fd)) {
    02718         /* XXX Fstrerror */
    02719         rpmlog(RPMLOG_ERR, _("File %s: %s\n"), file, Fstrerror(fd));
    02720         if (fd) (void) Fclose(fd);
    02721         return 1;
    02722     }
    02723     nb = Fread(magic, sizeof(magic[0]), sizeof(magic), fd);
    02724     if (nb < (ssize_t)0) {
    02725         rpmlog(RPMLOG_ERR, _("File %s: %s\n"), file, Fstrerror(fd));
    02726         rc = 1;
    02727     } else if (nb < (ssize_t)sizeof(magic)) {
    02728         rpmlog(RPMLOG_ERR, _("File %s is smaller than %u bytes\n"),
    02729                 file, (unsigned)sizeof(magic));
    02730         rc = 0;
    02731     }
    02732     (void) Fclose(fd);
    02733     if (rc >= 0)
    02734         return rc;
    02735
    02736     rc = 0;
    02737
    02738     if (magic[0] == 'B' && magic[1] == 'Z')
    02739         *compressed = COMPRESSED_BZIP2;
    02740     else
    02741     if (magic[0] == (unsigned char) 0120 && magic[1] == (unsigned char) 0113
    02742      && magic[2] == (unsigned char) 0003 && magic[3] == (unsigned char) 0004)   /* pkzip */
    02743         *compressed = COMPRESSED_ZIP;
    02744     else
    02745     if (magic[0] == (unsigned char) 0x89 && magic[1] == 'L'
    02746      && magic[2] == 'Z' && magic[3] == 'O')     /* lzop */
    02747         *compressed = COMPRESSED_LZOP;
    02748     else
    02749 #if !defined(RPM_VENDOR_OPENPKG) && !defined(RPM_VENDOR_FEDORA) && !defined(RPM_VENDOR_MANDRIVA) /* extension-based-compression-detection */
    02750     /* XXX Ick, LZMA has no magic. See http://lkml.org/lkml/2005/6/13/285 */
    02751     if (magic[ 9] == (unsigned char) 0x00 && magic[10] == (unsigned char) 0x00 &&
    02752         magic[11] == (unsigned char) 0x00 && magic[12] == (unsigned char) 0x00) /* lzmash */
    02753         *compressed = COMPRESSED_LZMA;
    02754     else
    02755 #endif
    02756 #if defined(RPM_VENDOR_OPENSUSE)
    02757     if (magic[0] == 0135 && magic[1] == 0 && magic[2] == 0) {           /* lzma */
    02758         *compressed = COMPRESSED_LZMA;
    02759     else
    02760 #endif
    02761     if (magic[0] == (unsigned char) 0xFD && magic[1] == 0x37 && magic[2] == 0x7A
    02762      && magic[3] == 0x58 && magic[4] == 0x5A && magic[5] == 0x00)               /* xz */
    02763         *compressed = COMPRESSED_XZ;
    02764     else
    02765     if ((magic[0] == (unsigned char) 0037 && magic[1] == (unsigned char) 0213)  /* gzip */
    02766      || (magic[0] == (unsigned char) 0037 && magic[1] == (unsigned char) 0236)  /* old gzip */
    02767      || (magic[0] == (unsigned char) 0037 && magic[1] == (unsigned char) 0036)  /* pack */
    02768      || (magic[0] == (unsigned char) 0037 && magic[1] == (unsigned char) 0240)  /* SCO lzh */
    02769      || (magic[0] == (unsigned char) 0037 && magic[1] == (unsigned char) 0235)) /* compress */
    02770         *compressed = COMPRESSED_OTHER;
    02771
    02772     return rc;
    02773 }
    02774
    02775 /* =============================================================== */
    02776
    02777 /*@-modfilesys@*/
    02778 /* XXX TODO: merge rpmExpand and rpmMCExpand. gud enuf for now ... */
    02779 char *
    02780 rpmExpand(const char *arg, ...)
    02781 {
    02782     MacroContext mc = NULL;
    02783     const char *s;
    02784     char *t, *te;
    02785     size_t sn, tn;
    02786     size_t bufn = 8 * _macro_BUFSIZ;
    02787
    02788     va_list ap;
    02789
    02790     if (arg == NULL)
    02791         return xstrdup("");
    02792
    02793     t = xmalloc(bufn + strlen(arg) + 1);
    02794     *t = '\0';
    02795     te = stpcpy(t, arg);
    02796
    02797     va_start(ap, arg);
    02798     while ((s = va_arg(ap, const char *)) != NULL) {
    02799         sn = strlen(s);
    02800         tn = (te - t);
    02801         t = xrealloc(t, tn + sn + bufn + 1);
    02802         te = t + tn;
    02803         te = stpcpy(te, s);
    02804     }
    02805     va_end(ap);
    02806
    02807     *te = '\0';
    02808     tn = (te - t);
    02809     (void) expandMacros(NULL, mc, t, tn + bufn + 1);
    02810     t[tn + bufn] = '\0';
    02811     t = xrealloc(t, strlen(t) + 1);
    02812
    02813     return t;
    02814 }
    02815
    02816 char *
    02817 rpmMCExpand(MacroContext mc, const char *arg, ...)
    02818 {
    02819     const char *s;
    02820     char *t, *te;
    02821     size_t sn, tn;
    02822     size_t bufn = 8 * _macro_BUFSIZ;
    02823
    02824     va_list ap;
    02825
    02826     if (arg == NULL)
    02827         return xstrdup("");
    02828
    02829     t = xmalloc(bufn + strlen(arg) + 1);
    02830     *t = '\0';
    02831     te = stpcpy(t, arg);
    02832
    02833     va_start(ap, arg);
    02834     while ((s = va_arg(ap, const char *)) != NULL) {
    02835         sn = strlen(s);
    02836         tn = (te - t);
    02837         t = xrealloc(t, tn + sn + bufn + 1);
    02838         te = t + tn;
    02839         te = stpcpy(te, s);
    02840     }
    02841     va_end(ap);
    02842
    02843     *te = '\0';
    02844     tn = (te - t);
    02845     (void) expandMacros(NULL, mc, t, tn + bufn + 1);
    02846     t[tn + bufn] = '\0';
    02847     t = xrealloc(t, strlen(t) + 1);
    02848
    02849     return t;
    02850 }
    02851 /*@=modfilesys@*/
    02852
    02853 int
    02854 rpmExpandNumeric(const char *arg)
    02855 {
    02856     const char *val;
    02857     int rc;
    02858
    02859     if (arg == NULL)
    02860         return 0;
    02861
    02862     val = rpmExpand(arg, NULL);
    02863     if (!(val && *val != '%'))
    02864         rc = 0;
    02865     else if (*val == 'Y' || *val == 'y')
    02866         rc = 1;
    02867     else if (*val == 'N' || *val == 'n')
    02868         rc = 0;
    02869     else {
    02870         char *end;
    02871         rc = strtol(val, &end, 0);
    02872         if (!(end && *end == '\0'))
    02873             rc = 0;
    02874     }
    02875     val = _free(val);
    02876
    02877     return rc;
    02878 }
    02879
    02880 /* @todo "../sbin/./../bin/" not correct. */
    02881 char *rpmCleanPath(char * path)
    02882 {
    02883     const char *s;
    02884     char *se, *t, *te;
    02885     int begin = 1;
    02886
    02887     if (path == NULL)
    02888         return NULL;
    02889
    02890 /*fprintf(stderr, "*** RCP %s ->\n", path); */
    02891     s = t = te = path;
    02892     while (*s != '\0') {
    02893 /*fprintf(stderr, "*** got \"%.*s\"\trest \"%s\"\n", (t-path), path, s); */
    02894         switch(*s) {
    02895         case ':':                       /* handle url's */
    02896             if (s[1] == '/' && s[2] == '/') {
    02897                 *t++ = *s++;
    02898                 *t++ = *s++;
    02899                 /* XXX handle "file:///" */
    02900                 if (s[0] == '/') *t++ = *s++;
    02901                 te = t;
    02902                 /*@switchbreak@*/ break;
    02903             }
    02904             begin=1;
    02905             /*@switchbreak@*/ break;
    02906         case '/':
    02907             /* Move parent dir forward */
    02908             for (se = te + 1; se < t && *se != '/'; se++)
    02909                 {};
    02910             if (se < t && *se == '/') {
    02911                 te = se;
    02912 /*fprintf(stderr, "*** next pdir \"%.*s\"\n", (te-path), path); */
    02913             }
    02914             while (s[1] == '/')
    02915                 s++;
    02916             while (t > te && t[-1] == '/')
    02917                 t--;
    02918             /*@switchbreak@*/ break;
    02919         case '.':
    02920             /* Leading .. is special */
    02921             /* Check that it is ../, so that we don't interpret */
    02922             /* ..?(i.e. "...") or ..* (i.e. "..bogus") as "..". */
    02923             /* in the case of "...", this ends up being processed*/
    02924             /* as "../.", and the last '.' is stripped.  This   */
    02925             /* would not be correct processing.                 */
    02926             if (begin && s[1] == '.' && (s[2] == '/' || s[2] == '\0')) {
    02927 /*fprintf(stderr, "    leading \"..\"\n"); */
    02928                 *t++ = *s++;
    02929                 /*@switchbreak@*/ break;
    02930             }
    02931             /* Single . is special */
    02932             if (begin && s[1] == '\0') {
    02933                 /*@switchbreak@*/ break;
    02934             }
    02935             if (t > path && t[-1] == '/')
    02936             switch (s[1]) {
    02937             case '/':   s++;    /*@fallthrough@*/       /* Trim embedded ./ */
    02938             case '\0':  s++;    continue;               /* Trim trailing /. */
    02939             default:    /*@innerbreak@*/ break;
    02940             }
    02941             /* Trim embedded /../ and trailing /.. */
    02942             if (!begin && t > path && t[-1] == '/' && s[1] == '.' && (s[2] == '/' || s[2] == '\0')) {
    02943                 t = te;
    02944                 /* Move parent dir forward */
    02945                 if (te > path)
    02946                     for (--te; te > path && *te != '/'; te--)
    02947                         {};
    02948 /*fprintf(stderr, "*** prev pdir \"%.*s\"\n", (te-path), path); */
    02949                 s++;
    02950                 s++;
    02951                 continue;
    02952             }
    02953             /*@switchbreak@*/ break;
    02954         default:
    02955             begin = 0;
    02956             /*@switchbreak@*/ break;
    02957         }
    02958         *t++ = *s++;
    02959     }
    02960
    02961     /* Trim trailing / (but leave single / alone) */
    02962     if (t > &path[1] && t[-1] == '/')
    02963         t--;
    02964     *t = '\0';
    02965
    02966 /*fprintf(stderr, "\t%s\n", path); */
    02967     return path;
    02968 }
    02969
    02970 /* Return concatenated and expanded canonical path. */
    02971
    02972 const char *
    02973 rpmGetPath(const char *path, ...)
    02974 {
    02975     size_t bufn = _macro_BUFSIZ;
    02976     char *buf = alloca(bufn);
    02977     const char * s;
    02978     char * t, * te;
    02979     va_list ap;
    02980
    02981     if (path == NULL)
    02982         return xstrdup("");
    02983
    02984     buf[0] = '\0';
    02985     t = buf;
    02986     te = stpcpy(t, path);
    02987     *te = '\0';
    02988
    02989     va_start(ap, path);
    02990     while ((s = va_arg(ap, const char *)) != NULL) {
    02991         te = stpcpy(te, s);
    02992         *te = '\0';
    02993     }
    02994     va_end(ap);
    02995 /*@-modfilesys@*/
    02996     (void) expandMacros(NULL, NULL, buf, bufn);
    02997 /*@=modfilesys@*/
    02998
    02999     (void) rpmCleanPath(buf);
    03000     return xstrdup(buf);        /* XXX xstrdup has side effects. */
    03001 }
    03002
    03003 /* Merge 3 args into path, any or all of which may be a url. */
    03004
    03005 const char * rpmGenPath(const char * urlroot, const char * urlmdir,
    03006                 const char *urlfile)
    03007 {
    03008 /*@owned@*/ const char * xroot = rpmGetPath(urlroot, NULL);
    03009 /*@dependent@*/ const char * root = xroot;
    03010 /*@owned@*/ const char * xmdir = rpmGetPath(urlmdir, NULL);
    03011 /*@dependent@*/ const char * mdir = xmdir;
    03012 /*@owned@*/ const char * xfile = rpmGetPath(urlfile, NULL);
    03013 /*@dependent@*/ const char * file = xfile;
    03014     const char * result;
    03015     const char * url = NULL;
    03016     int nurl = 0;
    03017     int ut;
    03018
    03019 #if 0
    03020 if (_debug) fprintf(stderr, "*** RGP xroot %s xmdir %s xfile %s\n", xroot, xmdir, xfile);
    03021 #endif
    03022     ut = urlPath(xroot, &root);
    03023     if (url == NULL && ut > URL_IS_DASH) {
    03024         url = xroot;
    03025         nurl = root - xroot;
    03026 #if 0
    03027 if (_debug) fprintf(stderr, "*** RGP ut %d root %s nurl %d\n", ut, root, nurl);
    03028 #endif
    03029     }
    03030     if (root == NULL || *root == '\0') root = "/";
    03031
    03032     ut = urlPath(xmdir, &mdir);
    03033     if (url == NULL && ut > URL_IS_DASH) {
    03034         url = xmdir;
    03035         nurl = mdir - xmdir;
    03036 #if 0
    03037 if (_debug) fprintf(stderr, "*** RGP ut %d mdir %s nurl %d\n", ut, mdir, nurl);
    03038 #endif
    03039     }
    03040     if (mdir == NULL || *mdir == '\0') mdir = "/";
    03041
    03042     ut = urlPath(xfile, &file);
    03043     if (url == NULL && ut > URL_IS_DASH) {
    03044         url = xfile;
    03045         nurl = file - xfile;
    03046 #if 0
    03047 if (_debug) fprintf(stderr, "*** RGP ut %d file %s nurl %d\n", ut, file, nurl);
    03048 #endif
    03049     }
    03050
    03051     if (url && nurl > 0) {
    03052         char *t = strncpy(alloca(nurl+1), url, nurl);
    03053         t[nurl] = '\0';
    03054         url = t;
    03055     } else
    03056         url = "";
    03057
    03058     result = rpmGetPath(url, root, "/", mdir, "/", file, NULL);
    03059
    03060     xroot = _free(xroot);
    03061     xmdir = _free(xmdir);
    03062     xfile = _free(xfile);
    03063 #if 0
    03064 if (_debug) fprintf(stderr, "*** RGP result %s\n", result);
    03065 #endif
    03066     return result;
    03067 }
    03068
    03069 /* =============================================================== */
    03070
    03071 #if defined(DEBUG_MACROS)
    03072
    03073 #if defined(EVAL_MACROS)
    03074
    03075 const char *rpmMacrofiles = MACROFILES;
    03076
    03077 int
    03078 main(int argc, char *argv[])
    03079 {
    03080     int c;
    03081     int errflg = 0;
    03082     extern char *optarg;
    03083     extern int optind;
    03084
    03085     while ((c = getopt(argc, argv, "f:")) != EOF ) {
    03086         switch (c) {
    03087         case 'f':
    03088             rpmMacrofiles = optarg;
    03089             break;
    03090         case '?':
    03091         default:
    03092             errflg++;
    03093             break;
    03094         }
    03095     }
    03096     if (errflg || optind >= argc) {
    03097         fprintf(stderr, "Usage: %s [-f macropath ] macro ...\n", argv[0]);
    03098         exit(1);
    03099     }
    03100
    03101     rpmInitMacros(NULL, rpmMacrofiles);
    03102     /* XXX getopt(3) also used for parametrized macros, expect scwewiness. */
    03103     for ( ; optind < argc; optind++) {
    03104         const char *val;
    03105
    03106         val = rpmExpand(argv[optind], NULL);
    03107         if (val) {
    03108             fprintf(stdout, "%s:\t%s\n", argv[optind], val);
    03109             val = _free(val);
    03110         }
    03111     }
    03112     rpmFreeMacros(NULL);
    03113     return 0;
    03114 }
    03115
    03116 #else   /* !EVAL_MACROS */
    03117
    03118 const char *rpmMacrofiles = "../macros:./testmacros";
    03119 const char *testfile = "./test";
    03120
    03121 int
    03122 main(int argc, char *argv[])
    03123 {
    03124     size_t bufn = _macro_BUFSIZ;
    03125     char *buf = alloca(bufn);
    03126     FILE *fp;
    03127     int x;
    03128
    03129     rpmInitMacros(NULL, rpmMacrofiles);
    03130
    03131     if ((fp = fopen(testfile, "r")) != NULL) {
    03132         while(rdcl(buf, bufn, fp)) {
    03133             x = expandMacros(NULL, NULL, buf, bufn);
    03134             fprintf(stderr, "%d->%s\n", x, buf);
    03135             memset(buf, 0, bufn);
    03136         }
    03137         fclose(fp);
    03138     }
    03139
    03140     while(rdcl(buf, bufn, stdin)) {
    03141         x = expandMacros(NULL, NULL, buf, bufn);
    03142         fprintf(stderr, "%d->%s\n <-\n", x, buf);
    03143         memset(buf, 0, bufn);
    03144     }
    03145     rpmFreeMacros(NULL);
    03146
    03147     return 0;
    03148 }
    03149 #endif  /* EVAL_MACROS */
    03150 #endif  /* DEBUG_MACROS */