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