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

# 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         }
    01318         b = be;
    01319     } else if (STREQ("mkstemp", f, fn)) {
    01320 /*@-globs@*/
    01321         for (b = buf; (c = (int)*b) && isblank(c);)
    01322             b++;
    01323         /* XXX FIXME: file paths with embedded white space needs rework. */
    01324         for (be = b; (c = (int)*be) && !isblank(c);)
    01325             be++;
    01326 /*@=globs@*/
    01327 #if defined(HAVE_MKSTEMP)
    01328         (void) close(mkstemp(b));
    01329 #else
    01330         (void) mktemp(b);
    01331 #endif
    01332     } else if (STREQ("S", f, fn)) {
    01333         for (b = buf; (c = (int)*b) && xisdigit(c);)
    01334             b++;
    01335         if (!c) {       /* digit index */
    01336             b++;
    01337             sprintf(b, "%%SOURCE%s", buf);
    01338         } else
    01339             b = buf;
    01340     } else if (STREQ("P", f, fn)) {
    01341         for (b = buf; (c = (int) *b) && xisdigit(c);)
    01342             b++;
    01343         if (!c) {       /* digit index */
    01344             b++;
    01345             sprintf(b, "%%PATCH%s", buf);
    01346         } else
    01347             b = buf;
    01348     } else if (STREQ("F", f, fn)) {
    01349         b = buf + strlen(buf) + 1;
    01350         sprintf(b, "file%s.file", buf);
    01351     }
    01352
    01353     if (b) {
    01354         (void) expandT(mb, b, strlen(b));
    01355     }
    01356 }
    01357
    01358 static int expandFIFO(MacroBuf mb, MacroEntry me, const char *g, size_t gn)
    01359         /*@globals rpmGlobalMacroContext, fileSystem, internalState @*/
    01360         /*@modifies mb, rpmGlobalMacroContext, fileSystem, internalState @*/
    01361 {
    01362     int rc = 0;
    01363
    01364     if (me) {
    01365         if (me->prev) {
    01366             rc = expandFIFO(mb, me->prev, g, gn);
    01367             rc = expandT(mb, g, gn);
    01368         }
    01369         rc = expandT(mb, me->body, strlen(me->body));
    01370     }
    01371     return rc;
    01372 }
    01373
    01380 static int
    01381 expandMacro(MacroBuf mb)
    01382         /*@globals rpmGlobalMacroContext,
    01383                 print_macro_trace, print_expand_trace, h_errno,
    01384                 fileSystem, internalState @*/
    01385         /*@modifies mb, rpmGlobalMacroContext,
    01386                 print_macro_trace, print_expand_trace,
    01387                 fileSystem, internalState @*/
    01388 {
    01389     MacroEntry *mep;
    01390     MacroEntry me;
    01391     const char *s = mb->s, *se;
    01392     const char *f, *fe;
    01393     const char *g, *ge;
    01394     size_t fn, gn;
    01395     char *t = mb->t;    /* save expansion pointer for printExpand */
    01396     int c;
    01397     int rc = 0;
    01398     int negate;
    01399     int stackarray;
    01400     const char * lastc;
    01401     int chkexist;
    01402
    01403     if (++mb->depth > max_macro_depth) {
    01404         rpmlog(RPMLOG_ERR,
    01405                 _("Recursion depth(%d) greater than max(%d)\n"),
    01406                 mb->depth, max_macro_depth);
    01407         mb->depth--;
    01408         mb->expand_trace = 1;
    01409         return 1;
    01410     }
    01411
    01412     while (rc == 0 && mb->nb > 0 && (c = (int)*s) != (int)'\0') {
    01413         s++;
    01414         /* Copy text until next macro */
    01415         switch(c) {
    01416         case '%':
    01417                 if (*s != '\0') {       /* Ensure not end-of-string. */
    01418                     if (*s != '%')
    01419                         /*@switchbreak@*/ break;
    01420                     s++;        /* skip first % in %% */
    01421                 }
    01422                 /*@fallthrough@*/
    01423         default:
    01424                 SAVECHAR(mb, c);
    01425                 continue;
    01426                 /*@notreached@*/ /*@switchbreak@*/ break;
    01427         }
    01428
    01429         /* Expand next macro */
    01430         f = fe = NULL;
    01431         g = ge = NULL;
    01432         if (mb->depth > 1)      /* XXX full expansion for outermost level */
    01433                 t = mb->t;      /* save expansion pointer for printExpand */
    01434         stackarray = chkexist = negate = 0;
    01435         lastc = NULL;
    01436         switch ((c = (int) *s)) {
    01437         default:                /* %name substitution */
    01438                 while (*s != '\0' && strchr("!?@", *s) != NULL) {
    01439                         switch(*s++) {
    01440                         case '@':
    01441                                 stackarray = ((stackarray + 1) % 2);
    01442                                 /*@switchbreak@*/ break;
    01443                         case '!':
    01444                                 negate = ((negate + 1) % 2);
    01445                                 /*@switchbreak@*/ break;
    01446                         case '?':
    01447                                 chkexist++;
    01448                                 /*@switchbreak@*/ break;
    01449                         }
    01450                 }
    01451                 f = se = s;
    01452                 if (*se == '-')
    01453                         se++;
    01454                 while((c = (int) *se) && (xisalnum(c) || c == (int) '_'))
    01455                         se++;
    01456                 /* Recognize non-alnum macros too */
    01457                 switch (*se) {
    01458                 case '*':
    01459                         se++;
    01460                         if (*se == '*') se++;
    01461                         /*@innerbreak@*/ break;
    01462                 case '#':
    01463                         se++;
    01464                         /*@innerbreak@*/ break;
    01465                 default:
    01466                         /*@innerbreak@*/ break;
    01467                 }
    01468                 fe = se;
    01469                 /* For "%name " macros ... */
    01470 /*@-globs@*/
    01471                 if ((c = (int) *fe) && isblank(c))
    01472                         if ((lastc = strchr(fe,'\n')) == NULL)
    01473                                 lastc = strchr(fe, '\0');
    01474 /*@=globs@*/
    01475                 /*@switchbreak@*/ break;
    01476         case '(':               /* %(...) shell escape */
    01477                 if ((se = matchchar(s, (char)c, ')')) == NULL) {
    01478                         rpmlog(RPMLOG_ERR,
    01479                                 _("Unterminated %c: %s\n"), (char)c, s);
    01480                         rc = 1;
    01481                         continue;
    01482                 }
    01483                 if (mb->macro_trace)
    01484                         printMacro(mb, s, se+1);
    01485
    01486                 s++;    /* skip ( */
    01487                 rc = doShellEscape(mb, s, (se - s));
    01488                 se++;   /* skip ) */
    01489
    01490                 s = se;
    01491                 continue;
    01492                 /*@notreached@*/ /*@switchbreak@*/ break;
    01493         case '{':               /* %{...}/%{...:...} substitution */
    01494                 if ((se = matchchar(s, (char)c, '}')) == NULL) {
    01495                         rpmlog(RPMLOG_ERR,
    01496                                 _("Unterminated %c: %s\n"), (char)c, s);
    01497                         rc = 1;
    01498                         continue;
    01499                 }
    01500                 f = s+1;/* skip { */
    01501                 se++;   /* skip } */
    01502                 while (strchr("!?@", *f) != NULL) {
    01503                         switch(*f++) {
    01504                         case '@':
    01505                                 stackarray = ((stackarray + 1) % 2);
    01506                                 /*@switchbreak@*/ break;
    01507                         case '!':
    01508                                 negate = ((negate + 1) % 2);
    01509                                 /*@switchbreak@*/ break;
    01510                         case '?':
    01511                                 chkexist++;
    01512                                 /*@switchbreak@*/ break;
    01513                         }
    01514                 }
    01515                 /* Find end-of-expansion, handle %{foo:bar} expansions. */
    01516                 for (fe = f; (c = (int) *fe) && !strchr(" :}", c);)
    01517                         fe++;
    01518                 switch (c) {
    01519                 case ':':
    01520                         g = fe + 1;
    01521                         ge = se - 1;
    01522                         /*@innerbreak@*/ break;
    01523                 case ' ':
    01524                         lastc = se-1;
    01525                         /*@innerbreak@*/ break;
    01526                 default:
    01527                         /*@innerbreak@*/ break;
    01528                 }
    01529                 /*@switchbreak@*/ break;
    01530         }
    01531
    01532         /* XXX Everything below expects fe > f */
    01533         fn = (fe - f);
    01534         gn = (ge - g);
    01535         if ((fe - f) <= 0) {
    01536 /* XXX Process % in unknown context */
    01537                 c = (int) '%';  /* XXX only need to save % */
    01538                 SAVECHAR(mb, c);
    01539 #if 0
    01540                 rpmlog(RPMLOG_ERR,
    01541                         _("A %% is followed by an unparseable macro\n"));
    01542 #endif
    01543                 s = se;
    01544                 continue;
    01545         }
    01546
    01547         if (mb->macro_trace)
    01548                 printMacro(mb, s, se);
    01549
    01550         /* Expand builtin macros */
    01551         if (STREQ("load", f, fn)) {
    01552                 if (g != NULL) {
    01553                     char * mfn = strncpy(alloca(gn + 1), g, gn);
    01554                     int xx;
    01555                     mfn[gn] = '\0';
    01556                     xx = rpmLoadMacroFile(NULL, mfn);
    01557                 }
    01558                 s = se;
    01559                 continue;
    01560         }
    01561         if (STREQ("global", f, fn)) {
    01562                 s = doDefine(mb, se, RMIL_GLOBAL, 1);
    01563                 continue;
    01564         }
    01565         if (STREQ("define", f, fn)) {
    01566                 s = doDefine(mb, se, mb->depth, 0);
    01567                 continue;
    01568         }
    01569         if (STREQ("undefine", f, fn)) {
    01570                 s = doUndefine(mb->mc, se);
    01571                 continue;
    01572         }
    01573
    01574         if (STREQ("echo", f, fn) ||
    01575             STREQ("warn", f, fn) ||
    01576             STREQ("error", f, fn)) {
    01577                 int waserror = 0;
    01578                 if (STREQ("error", f, fn))
    01579                         waserror = 1, rc = 1;
    01580                 if (g != NULL && g < ge)
    01581                         doOutput(mb, waserror, g, gn);
    01582                 else
    01583                         doOutput(mb, waserror, f, fn);
    01584                 s = se;
    01585                 continue;
    01586         }
    01587
    01588         if (STREQ("trace", f, fn)) {
    01589                 /* XXX TODO restore expand_trace/macro_trace to 0 on return */
    01590                 mb->expand_trace = mb->macro_trace = (negate ? 0 : mb->depth);
    01591                 if (mb->depth == 1) {
    01592                         print_macro_trace = mb->macro_trace;
    01593                         print_expand_trace = mb->expand_trace;
    01594                 }
    01595                 s = se;
    01596                 continue;
    01597         }
    01598
    01599         if (STREQ("dump", f, fn)) {
    01600                 rpmDumpMacroTable(mb->mc, NULL);
    01601                 while (iseol(*se))
    01602                         se++;
    01603                 s = se;
    01604                 continue;
    01605         }
    01606
    01607 #ifdef  WITH_LUA
    01608         if (STREQ("lua", f, fn)) {
    01609                 rpmlua lua = rpmluaGetGlobalState();
    01610                 rpmlua olua = memcpy(alloca(sizeof(*olua)), lua, sizeof(*olua));
    01611                 const char *ls = s+sizeof("{lua:")-1;
    01612                 const char *lse = se-sizeof("}")+1;
    01613                 char *scriptbuf = (char *)xmalloc((lse-ls)+1);
    01614                 const char *printbuf;
    01615
    01616                 /* Reset the stateful output buffer before recursing down. */
    01617                 lua->storeprint = 1;
    01618                 lua->printbuf = NULL;
    01619                 lua->printbufsize = 0;
    01620                 lua->printbufused = 0;
    01621
    01622                 memcpy(scriptbuf, ls, lse-ls);
    01623                 scriptbuf[lse-ls] = '\0';
    01624                 if (rpmluaRunScript(lua, scriptbuf, NULL) == -1)
    01625                     rc = 1;
    01626                 printbuf = rpmluaGetPrintBuffer(lua);
    01627                 if (printbuf) {
    01628                     size_t len = strlen(printbuf);
    01629                     if (len > mb->nb)
    01630                         len = mb->nb;
    01631                     memcpy(mb->t, printbuf, len);
    01632                     mb->t += len;
    01633                     mb->nb -= len;
    01634                 }
    01635
    01636                 /* Restore the stateful output buffer after recursion. */
    01637                 lua->storeprint = olua->storeprint;
    01638                 lua->printbuf = olua->printbuf;
    01639                 lua->printbufsize = olua->printbufsize;
    01640                 lua->printbufused = olua->printbufused;
    01641
    01642                 free(scriptbuf);
    01643                 s = se;
    01644                 continue;
    01645         }
    01646 #endif
    01647
    01648         /* Rewrite "%patchNN ..." as "%patch -P NN ..." and expand. */
    01649         if (lastc && fn > 5 && STREQ("patch", f, 5) && xisdigit((int)f[5])) {
    01650                 /*@-internalglobs@*/ /* FIX: verbose may be set */
    01651                 doFoo(mb, negate, f, (lastc - f), NULL, 0);
    01652                 /*@=internalglobs@*/
    01653                 s = lastc;
    01654                 continue;
    01655         }
    01656
    01657         /* XXX necessary but clunky */
    01658         if (STREQ("basename", f, fn) ||
    01659             STREQ("dirname", f, fn) ||
    01660             STREQ("realpath", f, fn) ||
    01661             STREQ("getenv", f, fn) ||
    01662             STREQ("shrink", f, fn) ||
    01663             STREQ("suffix", f, fn) ||
    01664             STREQ("expand", f, fn) ||
    01665             STREQ("verbose", f, fn) ||
    01666             STREQ("uncompress", f, fn) ||
    01667             STREQ("mkstemp", f, fn) ||
    01668             STREQ("url2path", f, fn) ||
    01669             STREQ("u2p", f, fn) ||
    01670             STREQ("S", f, fn) ||
    01671             STREQ("P", f, fn) ||
    01672             STREQ("F", f, fn)) {
    01673                 /*@-internalglobs@*/ /* FIX: verbose may be set */
    01674                 doFoo(mb, negate, f, fn, g, gn);
    01675                 /*@=internalglobs@*/
    01676                 s = se;
    01677                 continue;
    01678         }
    01679
    01680         /* Expand defined macros */
    01681         mep = findEntry(mb->mc, f, fn);
    01682         me = (mep ? *mep : NULL);
    01683
    01684         /* XXX Special processing for flags */
    01685         if (*f == '-') {
    01686                 if (me)
    01687                         me->used++;     /* Mark macro as used */
    01688                 if ((me == NULL && !negate) ||  /* Without -f, skip %{-f...} */
    01689                     (me != NULL && negate)) {   /* With -f, skip %{!-f...} */
    01690                         s = se;
    01691                         continue;
    01692                 }
    01693
    01694                 if (g && g < ge) {              /* Expand X in %{-f:X} */
    01695                         rc = expandT(mb, g, gn);
    01696                 } else
    01697                 if (me && me->body && *me->body) {/* Expand %{-f}/%{-f*} */
    01698                         rc = expandT(mb, me->body, strlen(me->body));
    01699                 }
    01700                 s = se;
    01701                 continue;
    01702         }
    01703
    01704         /* XXX Special processing for macro existence */
    01705         if (chkexist) {
    01706                 if ((me == NULL && !negate) ||  /* Without -f, skip %{?f...} */
    01707                     (me != NULL && negate)) {   /* With -f, skip %{!?f...} */
    01708                         s = se;
    01709                         continue;
    01710                 }
    01711                 if (g && g < ge) {              /* Expand X in %{?f:X} */
    01712                         rc = expandT(mb, g, gn);
    01713                 } else
    01714                 if (me && me->body && *me->body) { /* Expand %{?f}/%{?f*} */
    01715                         rc = expandT(mb, me->body, strlen(me->body));
    01716                 }
    01717                 s = se;
    01718                 continue;
    01719         }
    01720
    01721         if (me == NULL) {       /* leave unknown %... as is */
    01722 #ifndef HACK
    01723 #if DEAD
    01724                 /* XXX hack to skip over empty arg list */
    01725                 if (fn == 1 && *f == '*') {
    01726                         s = se;
    01727                         continue;
    01728                 }
    01729 #endif
    01730                 /* XXX hack to permit non-overloaded %foo to be passed */
    01731                 c = (int) '%';  /* XXX only need to save % */
    01732                 SAVECHAR(mb, c);
    01733 #else
    01734                 if (!strncmp(f, "if", fn) ||
    01735                     !strncmp(f, "else", fn) ||
    01736                     !strncmp(f, "endif", fn)) {
    01737                         c = '%';        /* XXX only need to save % */
    01738                         SAVECHAR(mb, c);
    01739                 } else {
    01740                         rpmlog(RPMLOG_ERR,
    01741                                 _("Macro %%%.*s not found, skipping\n"), fn, f);
    01742                         s = se;
    01743                 }
    01744 #endif
    01745                 continue;
    01746         }
    01747
    01748         /* XXX Special processing to create a tuple from stack'd values. */
    01749         if (stackarray) {
    01750                 if (!(g && g < ge)) {
    01751                         g = "\n";
    01752                         gn = strlen(g);
    01753                 }
    01754                 rc = expandFIFO(mb, me, g, gn);
    01755                 s = se;
    01756                 continue;
    01757         }
    01758
    01759         /* Setup args for "%name " macros with opts */
    01760         if (me && me->opts != NULL) {
    01761                 if (lastc != NULL) {
    01762                         se = grabArgs(mb, me, fe, lastc);
    01763                 } else {
    01764                         addMacro(mb->mc, "**", NULL, "", mb->depth);
    01765                         addMacro(mb->mc, "*", NULL, "", mb->depth);
    01766                         addMacro(mb->mc, "#", NULL, "0", mb->depth);
    01767                         addMacro(mb->mc, "0", NULL, me->name, mb->depth);
    01768                 }
    01769         }
    01770
    01771         /* Recursively expand body of macro */
    01772         if (me->body && *me->body) {
    01773                 mb->s = me->body;
    01774                 rc = expandMacro(mb);
    01775                 if (rc == 0)
    01776                         me->used++;     /* Mark macro as used */
    01777         }
    01778
    01779         /* Free args for "%name " macros with opts */
    01780         if (me->opts != NULL)
    01781                 freeArgs(mb);
    01782
    01783         s = se;
    01784     }
    01785
    01786     *mb->t = '\0';
    01787     mb->s = s;
    01788     mb->depth--;
    01789     if (rc != 0 || mb->expand_trace)
    01790         printExpansion(mb, t, mb->t);
    01791     return rc;
    01792 }
    01793
    01794 #if defined(RPM_VENDOR_OPENPKG) /* security-sanity-check-rpmpopt-and-rpmmacros */
    01795 int rpmSecuritySaneFile(const char *filename)
    01796 {
    01797     struct stat sb;
    01798     uid_t uid;
    01799
    01800     if (stat(filename, &sb) == -1)
    01801         return (errno == ENOENT ? 1 : 0);
    01802     uid = getuid();
    01803     if (sb.st_uid != uid)
    01804         return 0;
    01805     if (!S_ISREG(sb.st_mode))
    01806         return 0;
    01807     if (sb.st_mode & (S_IWGRP|S_IWOTH))
    01808         return 0;
    01809     return 1;
    01810 }
    01811 #endif
    01812
    01813 #if !defined(DEBUG_MACROS)
    01814 /* =============================================================== */
    01815 /* XXX dupe'd to avoid change in linkage conventions. */
    01816
    01817 #define POPT_ERROR_NOARG        -10
    01818 #define POPT_ERROR_BADQUOTE     -15
    01819 #define POPT_ERROR_MALLOC       -21
    01821 #define POPT_ARGV_ARRAY_GROW_DELTA 5
    01822
    01823 static int XpoptDupArgv(int argc, const char **argv,
    01824                 int * argcPtr, const char *** argvPtr)
    01825         /*@modifies *argcPtr, *argvPtr @*/
    01826 {
    01827     size_t nb = (argc + 1) * sizeof(*argv);
    01828     const char ** argv2;
    01829     char * dst;
    01830     int i;
    01831
    01832     if (argc <= 0 || argv == NULL)      /* XXX can't happen */
    01833         return POPT_ERROR_NOARG;
    01834     for (i = 0; i < argc; i++) {
    01835         if (argv[i] == NULL)
    01836             return POPT_ERROR_NOARG;
    01837         nb += strlen(argv[i]) + 1;
    01838     }
    01839
    01840     dst = malloc(nb);
    01841     if (dst == NULL)                    /* XXX can't happen */
    01842         return POPT_ERROR_MALLOC;
    01843     argv2 = (void *) dst;
    01844     dst += (argc + 1) * sizeof(*argv);
    01845
    01846     for (i = 0; i < argc; i++) {
    01847         argv2[i] = dst;
    01848         dst += strlen(strcpy(dst, argv[i])) + 1;
    01849     }
    01850     argv2[argc] = NULL;
    01851
    01852     if (argvPtr) {
    01853         *argvPtr = argv2;
    01854     } else {
    01855         free(argv2);
    01856         argv2 = NULL;
    01857     }
    01858     if (argcPtr)
    01859         *argcPtr = argc;
    01860     return 0;
    01861 }
    01862
    01863 static int XpoptParseArgvString(const char * s, int * argcPtr, const char *** argvPtr)
    01864         /*@modifies *argcPtr, *argvPtr @*/
    01865 {
    01866     const char * src;
    01867     char quote = '\0';
    01868     int argvAlloced = POPT_ARGV_ARRAY_GROW_DELTA;
    01869     const char ** argv = malloc(sizeof(*argv) * argvAlloced);
    01870     int argc = 0;
    01871     size_t buflen = strlen(s) + 1;
    01872     char * buf = memset(alloca(buflen), 0, buflen);
    01873     int rc = POPT_ERROR_MALLOC;
    01874
    01875     if (argv == NULL) return rc;
    01876     argv[argc] = buf;
    01877
    01878     for (src = s; *src != '\0'; src++) {
    01879         if (quote == *src) {
    01880             quote = '\0';
    01881         } else if (quote != '\0') {
    01882             if (*src == '\\') {
    01883                 src++;
    01884                 if (!*src) {
    01885                     rc = POPT_ERROR_BADQUOTE;
    01886                     goto exit;
    01887                 }
    01888                 if (*src != quote) *buf++ = '\\';
    01889             }
    01890             *buf++ = *src;
    01891         } else if (isspace(*src)) {
    01892             if (*argv[argc] != '\0') {
    01893                 buf++, argc++;
    01894                 if (argc == argvAlloced) {
    01895                     argvAlloced += POPT_ARGV_ARRAY_GROW_DELTA;
    01896                     argv = realloc(argv, sizeof(*argv) * argvAlloced);
    01897                     if (argv == NULL) goto exit;
    01898                 }
    01899                 argv[argc] = buf;
    01900             }
    01901         } else switch (*src) {
    01902           case '"':
    01903           case '\'':
    01904             quote = *src;
    01905             /*@switchbreak@*/ break;
    01906           case '\\':
    01907             src++;
    01908             if (!*src) {
    01909                 rc = POPT_ERROR_BADQUOTE;
    01910                 goto exit;
    01911             }
    01912             /*@fallthrough@*/
    01913           default:
    01914             *buf++ = *src;
    01915             /*@switchbreak@*/ break;
    01916         }
    01917     }
    01918
    01919     if (strlen(argv[argc])) {
    01920         argc++, buf++;
    01921     }
    01922
    01923     rc = XpoptDupArgv(argc, argv, argcPtr, argvPtr);
    01924
    01925 exit:
    01926     if (argv) free(argv);
    01927     return rc;
    01928 }
    01929
    01930 /* =============================================================== */
    01931 /*@unchecked@*/
    01932 static int _debug = 0;
    01933
    01934 int rpmGlob(const char * patterns, int * argcPtr, const char *** argvPtr)
    01935 {
    01936     int ac = 0;
    01937     const char ** av = NULL;
    01938     int argc = 0;
    01939     const char ** argv = NULL;
    01940     char * globRoot = NULL;
    01941 #ifdef ENABLE_NLS
    01942     const char * old_collate = NULL;
    01943     const char * old_ctype = NULL;
    01944     const char * t;
    01945 #endif
    01946     size_t maxb, nb;
    01947     int i, j;
    01948     int rc;
    01949
    01950     rc = XpoptParseArgvString(patterns, &ac, &av);
    01951     if (rc)
    01952         return rc;
    01953 #ifdef ENABLE_NLS
    01954     t = setlocale(LC_COLLATE, NULL);
    01955     if (t)
    01956         old_collate = xstrdup(t);
    01957     t = setlocale(LC_CTYPE, NULL);
    01958     if (t)
    01959         old_ctype = xstrdup(t);
    01960     (void) setlocale(LC_COLLATE, "C");
    01961     (void) setlocale(LC_CTYPE, "C");
    01962 #endif
    01963
    01964     if (av != NULL)
    01965     for (j = 0; j < ac; j++) {
    01966         const char * globURL;
    01967         const char * path;
    01968         int ut = urlPath(av[j], &path);
    01969         glob_t gl;
    01970
    01971         if (!Glob_pattern_p(av[j], 0) && strchr(path, '~') == NULL) {
    01972             argv = xrealloc(argv, (argc+2) * sizeof(*argv));
    01973             argv[argc] = xstrdup(av[j]);
    01974 if (_debug)
    01975 fprintf(stderr, "*** rpmGlob argv[%d] \"%s\"\n", argc, argv[argc]);
    01976             argc++;
    01977             continue;
    01978         }
    01979
    01980         gl.gl_pathc = 0;
    01981         gl.gl_pathv = NULL;
    01982         rc = Glob(av[j], GLOB_TILDE, Glob_error, &gl);
    01983         if (rc)
    01984             goto exit;
    01985
    01986         /* XXX Prepend the URL leader for globs that have stripped it off */
    01987         maxb = 0;
    01988         for (i = 0; i < gl.gl_pathc; i++) {
    01989             if ((nb = strlen(&(gl.gl_pathv[i][0]))) > maxb)
    01990                 maxb = nb;
    01991         }
    01992
    01993         nb = ((ut == URL_IS_PATH) ? (path - av[j]) : 0);
    01994         maxb += nb;
    01995         maxb += 1;
    01996         globURL = globRoot = xmalloc(maxb);
    01997
    01998         switch (ut) {
    01999         case URL_IS_PATH:
    02000         case URL_IS_DASH:
    02001             strncpy(globRoot, av[j], nb);
    02002             /*@switchbreak@*/ break;
    02003         case URL_IS_HTTPS:
    02004         case URL_IS_HTTP:
    02005         case URL_IS_FTP:
    02006         case URL_IS_HKP:
    02007         case URL_IS_UNKNOWN:
    02008         default:
    02009             /*@switchbreak@*/ break;
    02010         }
    02011         globRoot += nb;
    02012         *globRoot = '\0';
    02013 if (_debug)
    02014 fprintf(stderr, "*** GLOB maxb %d diskURL %d %*s globURL %p %s\n", (int)maxb, (int)nb, (int)nb, av[j], globURL, globURL);
    02015
    02016         argv = xrealloc(argv, (argc+gl.gl_pathc+1) * sizeof(*argv));
    02017
    02018         if (argv != NULL)
    02019         for (i = 0; i < gl.gl_pathc; i++) {
    02020             const char * globFile = &(gl.gl_pathv[i][0]);
    02021             if (globRoot > globURL && globRoot[-1] == '/')
    02022                 while (*globFile == '/') globFile++;
    02023             strcpy(globRoot, globFile);
    02024 if (_debug)
    02025 fprintf(stderr, "*** rpmGlob argv[%d] \"%s\"\n", argc, globURL);
    02026             argv[argc++] = xstrdup(globURL);
    02027         }
    02028         /*@-immediatetrans@*/
    02029         Globfree(&gl);
    02030         /*@=immediatetrans@*/
    02031         globURL = _free(globURL);
    02032     }
    02033
    02034     if (argv != NULL && argc > 0) {
    02035         argv[argc] = NULL;
    02036         if (argvPtr)
    02037             *argvPtr = argv;
    02038         if (argcPtr)
    02039             *argcPtr = argc;
    02040         rc = 0;
    02041     } else
    02042         rc = 1;
    02043
    02044
    02045 exit:
    02046 #ifdef ENABLE_NLS
    02047     if (old_collate) {
    02048         (void) setlocale(LC_COLLATE, old_collate);
    02049         old_collate = _free(old_collate);
    02050     }
    02051     if (old_ctype) {
    02052         (void) setlocale(LC_CTYPE, old_ctype);
    02053         old_ctype = _free(old_ctype);
    02054     }
    02055 #endif
    02056     av = _free(av);
    02057     if (rc || argvPtr == NULL) {
    02058 /*@-dependenttrans -unqualifiedtrans@*/
    02059         if (argv != NULL)
    02060         for (i = 0; i < argc; i++)
    02061             argv[i] = _free(argv[i]);
    02062         argv = _free(argv);
    02063 /*@=dependenttrans =unqualifiedtrans@*/
    02064     }
    02065     return rc;
    02066 }
    02067 #endif  /* !defined(DEBUG_MACROS) */
    02068
    02069 /* =============================================================== */
    02070
    02071 int
    02072 expandMacros(void * spec, MacroContext mc, char * sbuf, size_t slen)
    02073 {
    02074     MacroBuf mb = alloca(sizeof(*mb));
    02075     char *tbuf;
    02076     int rc;
    02077
    02078     if (sbuf == NULL || slen == 0)
    02079         return 0;
    02080     if (mc == NULL) mc = rpmGlobalMacroContext;
    02081
    02082     tbuf = alloca(slen + 1);
    02083     memset(tbuf, 0, (slen + 1));
    02084
    02085     mb->s = sbuf;
    02086     mb->t = tbuf;
    02087     mb->nb = slen;
    02088     mb->depth = 0;
    02089     mb->macro_trace = print_macro_trace;
    02090     mb->expand_trace = print_expand_trace;
    02091
    02092     mb->spec = spec;    /* (future) %file expansion info */
    02093     mb->mc = mc;
    02094
    02095     rc = expandMacro(mb);
    02096
    02097     tbuf[slen] = '\0';
    02098     if (mb->nb == 0)
    02099         rpmlog(RPMLOG_ERR, _("Macro expansion too big for target buffer\n"));
    02100     else
    02101         strncpy(sbuf, tbuf, (slen - mb->nb + 1));
    02102
    02103     return rc;
    02104 }
    02105
    02106 void
    02107 addMacro(MacroContext mc,
    02108         const char * n, const char * o, const char * b, int level)
    02109 {
    02110     MacroEntry * mep;
    02111     const char * name = n;
    02112
    02113     if (*name == '.')           /* XXX readonly macros */
    02114         name++;
    02115     if (*name == '.')           /* XXX readonly macros */
    02116         name++;
    02117
    02118     if (mc == NULL) mc = rpmGlobalMacroContext;
    02119
    02120     /* If new name, expand macro table */
    02121     if ((mep = findEntry(mc, name, 0)) == NULL) {
    02122         if (mc->firstFree == mc->macrosAllocated)
    02123             expandMacroTable(mc);
    02124         if (mc->macroTable != NULL)
    02125             mep = mc->macroTable + mc->firstFree++;
    02126     }
    02127
    02128     if (mep != NULL) {
    02129         /* XXX permit "..foo" to be pushed over ".foo" */
    02130         if (*mep && (*mep)->flags && !(n[0] == '.' && n[1] == '.')) {
    02131             /* XXX avoid error message for %buildroot */
    02132             if (strcmp((*mep)->name, "buildroot"))
    02133                 rpmlog(RPMLOG_ERR, _("Macro '%s' is readonly and cannot be changed.\n"), n);
    02134             return;
    02135         }
    02136         /* Push macro over previous definition */
    02137         pushMacro(mep, n, o, b, level);
    02138
    02139         /* If new name, sort macro table */
    02140         if ((*mep)->prev == NULL)
    02141             sortMacroTable(mc);
    02142     }
    02143 }
    02144
    02145 void
    02146 delMacro(MacroContext mc, const char * n)
    02147 {
    02148     MacroEntry * mep;
    02149
    02150     if (mc == NULL) mc = rpmGlobalMacroContext;
    02151     /* If name exists, pop entry */
    02152     if ((mep = findEntry(mc, n, 0)) != NULL) {
    02153         popMacro(mep);
    02154         /* If deleted name, sort macro table */
    02155         if (!(mep && *mep))
    02156             sortMacroTable(mc);
    02157     }
    02158 }
    02159
    02160 /*@-mustmod@*/ /* LCL: mc is modified through mb->mc, mb is abstract */
    02161 int
    02162 rpmDefineMacro(MacroContext mc, const char * macro, int level)
    02163 {
    02164     MacroBuf mb = alloca(sizeof(*mb));
    02165
    02166     memset(mb, 0, sizeof(*mb));
    02167     /* XXX just enough to get by */
    02168     mb->mc = (mc ? mc : rpmGlobalMacroContext);
    02169     (void) doDefine(mb, macro, level, 0);
    02170     return 0;
    02171 }
    02172 /*@=mustmod@*/
    02173
    02174 /*@-mustmod@*/ /* LCL: mc is modified through mb->mc, mb is abstract */
    02175 int
    02176 rpmUndefineMacro(MacroContext mc, const char * macro)
    02177 {
    02178     (void) doUndefine(mc ? mc : rpmGlobalMacroContext, macro);
    02179     return 0;
    02180 }
    02181 /*@=mustmod@*/
    02182
    02183 void
    02184 rpmLoadMacros(MacroContext mc, int level)
    02185 {
    02186
    02187     if (mc == NULL || mc == rpmGlobalMacroContext)
    02188         return;
    02189
    02190     if (mc->macroTable != NULL) {
    02191         int i;
    02192         for (i = 0; i < mc->firstFree; i++) {
    02193             MacroEntry *mep, me;
    02194             mep = &mc->macroTable[i];
    02195             me = *mep;
    02196
    02197             if (me == NULL)             /* XXX this should never happen */
    02198                 continue;
    02199             addMacro(NULL, me->name, me->opts, me->body, (level - 1));
    02200         }
    02201     }
    02202 }
    02203
    02204 #if defined(RPM_VENDOR_OPENPKG) /* expand-macrosfile-macro */
    02205 static void expand_macrosfile_macro(const char *file_name, const char *buf, size_t bufn)
    02206 {
    02207     char *cp;
    02208     size_t l, k;
    02209     static const char *macro_name = "%{macrosfile}";
    02210
    02211     l = strlen(macro_name);
    02212     k = strlen(file_name);
    02213     while ((cp = strstr(buf, macro_name)) != NULL) {
    02214         if (((strlen(buf) - l) + k) < bufn) {
    02215             memmove(cp+k, cp+l, strlen(cp+l)+1);
    02216             memcpy(cp, file_name, k);
    02217         }
    02218     }
    02219     return;
    02220 }
    02221 #endif
    02222
    02223 int
    02224 rpmLoadMacroFile(MacroContext mc, const char * fn)
    02225 {
    02226     FD_t fd = Fopen(fn, "r.fpio");
    02227     size_t bufn = _macro_BUFSIZ;
    02228     char *buf = alloca(bufn);
    02229     int rc = -1;
    02230
    02231     if (fd == NULL || Ferror(fd)) {
    02232         if (fd) (void) Fclose(fd);
    02233         return rc;
    02234     }
    02235
    02236     /* XXX Assume new fangled macro expansion */
    02237     /*@-mods@*/
    02238     max_macro_depth = _MAX_MACRO_DEPTH;
    02239     /*@=mods@*/
    02240
    02241     buf[0] = '\0';
    02242     while(rdcl(buf, bufn, fd) != NULL) {
    02243         char *n;
    02244         int c;
    02245
    02246         n = buf;
    02247         SKIPBLANK(n, c);
    02248
    02249         if (c != (int) '%')
    02250                 continue;
    02251         n++;    /* skip % */
    02252 #if defined(RPM_VENDOR_OPENPKG) /* expand-macro-source */
    02253         expand_macrosfile_macro(fn, buf, bufn);
    02254 #endif
    02255         rc = rpmDefineMacro(mc, n, RMIL_MACROFILES);
    02256     }
    02257     rc = Fclose(fd);
    02258     return rc;
    02259 }
    02260
    02261 void
    02262 rpmInitMacros(MacroContext mc, const char * macrofiles)
    02263 {
    02264     char *mfiles, *m, *me;
    02265
    02266     if (macrofiles == NULL)
    02267         return;
    02268 #ifdef  DYING
    02269     if (mc == NULL) mc = rpmGlobalMacroContext;
    02270 #endif
    02271
    02272     mfiles = xstrdup(macrofiles);
    02273     for (m = mfiles; m && *m != '\0'; m = me) {
    02274         const char ** av;
    02275         int ac;
    02276         int i;
    02277
    02278         for (me = m; (me = strchr(me, ':')) != NULL; me++) {
    02279             /* Skip over URI's. */
    02280             if (!(me[1] == '/' && me[2] == '/'))
    02281                 /*@innerbreak@*/ break;
    02282         }
    02283
    02284         if (me && *me == ':')
    02285             *me++ = '\0';
    02286         else
    02287             me = m + strlen(m);
    02288
    02289 #if defined(RPM_VENDOR_OPENPKG) /* security-sanity-check-rpmpopt-and-rpmmacros */
    02290         if (m[0] == '@' /* attention */) {
    02291             m++;
    02292             if (!rpmSecuritySaneFile(m)) {
    02293                 rpmlog(RPMLOG_WARNING, "existing RPM macros file \"%s\" considered INSECURE -- not loaded\n", m);
    02294                 continue;
    02295             }
    02296         }
    02297 #endif
    02298
    02299         /* Glob expand the macro file path element, expanding ~ to $HOME. */
    02300         ac = 0;
    02301         av = NULL;
    02302 #if defined(DEBUG_MACROS)
    02303         ac = 1;
    02304         av = xmalloc((ac + 1) * sizeof(*av));
    02305         av[0] = strdup(m);
    02306         av[1] = NULL;
    02307 #else
    02308         i = rpmGlob(m, &ac, &av);
    02309         if (i != 0)
    02310             continue;
    02311 #endif
    02312
    02313         /* Read macros from each file. */
    02314
    02315         for (i = 0; i < ac; i++) {
    02316             size_t slen = strlen(av[i]);
    02317
    02318         /* Skip backup files and %config leftovers. */
    02319 #define _suffix(_s, _x) \
    02320     (slen >= sizeof(_x) && !strcmp((_s)+slen-(sizeof(_x)-1), (_x)))
    02321             if (!(_suffix(av[i], "~")
    02322                || _suffix(av[i], ".rpmnew")
    02323                || _suffix(av[i], ".rpmorig")
    02324                || _suffix(av[i], ".rpmsave"))
    02325                )
    02326                    (void) rpmLoadMacroFile(mc, av[i]);
    02327 #undef _suffix
    02328
    02329             av[i] = _free(av[i]);
    02330         }
    02331         av = _free(av);
    02332     }
    02333     mfiles = _free(mfiles);
    02334
    02335     /* Reload cmdline macros */
    02336     /*@-mods@*/
    02337     rpmLoadMacros(rpmCLIMacroContext, RMIL_CMDLINE);
    02338     /*@=mods@*/
    02339 }
    02340
    02341 /*@-globstate@*/
    02342 void
    02343 rpmFreeMacros(MacroContext mc)
    02344 {
    02345
    02346     if (mc == NULL) mc = rpmGlobalMacroContext;
    02347
    02348     if (mc->macroTable != NULL) {
    02349         int i;
    02350         for (i = 0; i < mc->firstFree; i++) {
    02351             MacroEntry me;
    02352             while ((me = mc->macroTable[i]) != NULL) {
    02353                 /* XXX cast to workaround const */
    02354                 /*@-onlytrans@*/
    02355                 if ((mc->macroTable[i] = me->prev) == NULL)
    02356                     me->name = _free(me->name);
    02357                 /*@=onlytrans@*/
    02358                 me->opts = _free(me->opts);
    02359                 me->body = _free(me->body);
    02360                 me = _free(me);
    02361             }
    02362         }
    02363         mc->macroTable = _free(mc->macroTable);
    02364     }
    02365     memset(mc, 0, sizeof(*mc));
    02366 }
    02367 /*@=globstate@*/
    02368
    02369 /* =============================================================== */
    02370 int isCompressed(const char * file, rpmCompressedMagic * compressed)
    02371 {
    02372     FD_t fd;
    02373     ssize_t nb;
    02374     int rc = -1;
    02375     unsigned char magic[13];
    02376
    02377     *compressed = COMPRESSED_NOT;
    02378
    02379     fd = Fopen(file, "r");
    02380     if (fd == NULL || Ferror(fd)) {
    02381         /* XXX Fstrerror */
    02382         rpmlog(RPMLOG_ERR, _("File %s: %s\n"), file, Fstrerror(fd));
    02383         if (fd) (void) Fclose(fd);
    02384         return 1;
    02385     }
    02386     nb = Fread(magic, sizeof(magic[0]), sizeof(magic), fd);
    02387     if (nb < 0) {
    02388         rpmlog(RPMLOG_ERR, _("File %s: %s\n"), file, Fstrerror(fd));
    02389         rc = 1;
    02390     } else if (nb < sizeof(magic)) {
    02391         rpmlog(RPMLOG_ERR, _("File %s is smaller than %u bytes\n"),
    02392                 file, (unsigned)sizeof(magic));
    02393         rc = 0;
    02394     }
    02395     (void) Fclose(fd);
    02396     if (rc >= 0)
    02397         return rc;
    02398
    02399     rc = 0;
    02400
    02401     if (magic[0] == 'B' && magic[1] == 'Z')
    02402         *compressed = COMPRESSED_BZIP2;
    02403     else
    02404     if (magic[0] == (unsigned char) 0120 && magic[1] == (unsigned char) 0113
    02405      && magic[2] == (unsigned char) 0003 && magic[3] == (unsigned char) 0004)   /* pkzip */
    02406         *compressed = COMPRESSED_ZIP;
    02407     else
    02408     if (magic[0] == (unsigned char) 0x89 && magic[1] == 'L'
    02409      && magic[2] == 'Z' && magic[3] == 'O')     /* lzop */
    02410         *compressed = COMPRESSED_LZOP;
    02411     else
    02412     /* XXX Ick, LZMA has no magic. See http://lkml.org/lkml/2005/6/13/285 */
    02413     if (magic[ 9] == (unsigned char) 0x00 && magic[10] == (unsigned char) 0x00 &&
    02414         magic[11] == (unsigned char) 0x00 && magic[12] == (unsigned char) 0x00) /* lzmash */
    02415         *compressed = COMPRESSED_LZMA;
    02416     else
    02417     if ((magic[0] == (unsigned char) 0037 && magic[1] == (unsigned char) 0213)  /* gzip */
    02418      || (magic[0] == (unsigned char) 0037 && magic[1] == (unsigned char) 0236)  /* old gzip */
    02419      || (magic[0] == (unsigned char) 0037 && magic[1] == (unsigned char) 0036)  /* pack */
    02420      || (magic[0] == (unsigned char) 0037 && magic[1] == (unsigned char) 0240)  /* SCO lzh */
    02421      || (magic[0] == (unsigned char) 0037 && magic[1] == (unsigned char) 0235)) /* compress */
    02422         *compressed = COMPRESSED_OTHER;
    02423
    02424     return rc;
    02425 }
    02426
    02427 /* =============================================================== */
    02428
    02429 /*@-modfilesys@*/
    02430 char *
    02431 rpmExpand(const char *arg, ...)
    02432 {
    02433     const char *s;
    02434     char *t, *te;
    02435     size_t sn, tn;
    02436     size_t bufn = 8 * _macro_BUFSIZ;
    02437
    02438     va_list ap;
    02439
    02440     if (arg == NULL)
    02441         return xstrdup("");
    02442
    02443     t = xmalloc(bufn + strlen(arg) + 1);
    02444     *t = '\0';
    02445     te = stpcpy(t, arg);
    02446
    02447     va_start(ap, arg);
    02448     while ((s = va_arg(ap, const char *)) != NULL) {
    02449         sn = strlen(s);
    02450         tn = (te - t);
    02451         t = xrealloc(t, tn + sn + bufn + 1);
    02452         te = t + tn;
    02453         te = stpcpy(te, s);
    02454     }
    02455     va_end(ap);
    02456
    02457     *te = '\0';
    02458     tn = (te - t);
    02459     (void) expandMacros(NULL, NULL, t, tn + bufn + 1);
    02460     t[tn + bufn] = '\0';
    02461     t = xrealloc(t, strlen(t) + 1);
    02462
    02463     return t;
    02464 }
    02465 /*@=modfilesys@*/
    02466
    02467 int
    02468 rpmExpandNumeric(const char *arg)
    02469 {
    02470     const char *val;
    02471     int rc;
    02472
    02473     if (arg == NULL)
    02474         return 0;
    02475
    02476     val = rpmExpand(arg, NULL);
    02477     if (!(val && *val != '%'))
    02478         rc = 0;
    02479     else if (*val == 'Y' || *val == 'y')
    02480         rc = 1;
    02481     else if (*val == 'N' || *val == 'n')
    02482         rc = 0;
    02483     else {
    02484         char *end;
    02485         rc = strtol(val, &end, 0);
    02486         if (!(end && *end == '\0'))
    02487             rc = 0;
    02488     }
    02489     val = _free(val);
    02490
    02491     return rc;
    02492 }
    02493
    02494 /* @todo "../sbin/./../bin/" not correct. */
    02495 char *rpmCleanPath(char * path)
    02496 {
    02497     const char *s;
    02498     char *se, *t, *te;
    02499     int begin = 1;
    02500
    02501     if (path == NULL)
    02502         return NULL;
    02503
    02504 /*fprintf(stderr, "*** RCP %s ->\n", path); */
    02505     s = t = te = path;
    02506     while (*s != '\0') {
    02507 /*fprintf(stderr, "*** got \"%.*s\"\trest \"%s\"\n", (t-path), path, s); */
    02508         switch(*s) {
    02509         case ':':                       /* handle url's */
    02510             if (s[1] == '/' && s[2] == '/') {
    02511                 *t++ = *s++;
    02512                 *t++ = *s++;
    02513                 /* XXX handle "file:///" */
    02514                 if (s[0] == '/') *t++ = *s++;
    02515                 te = t;
    02516                 /*@switchbreak@*/ break;
    02517             }
    02518             begin=1;
    02519             /*@switchbreak@*/ break;
    02520         case '/':
    02521             /* Move parent dir forward */
    02522             for (se = te + 1; se < t && *se != '/'; se++)
    02523                 {};
    02524             if (se < t && *se == '/') {
    02525                 te = se;
    02526 /*fprintf(stderr, "*** next pdir \"%.*s\"\n", (te-path), path); */
    02527             }
    02528             while (s[1] == '/')
    02529                 s++;
    02530             while (t > te && t[-1] == '/')
    02531                 t--;
    02532             /*@switchbreak@*/ break;
    02533         case '.':
    02534             /* Leading .. is special */
    02535             /* Check that it is ../, so that we don't interpret */
    02536             /* ..?(i.e. "...") or ..* (i.e. "..bogus") as "..". */
    02537             /* in the case of "...", this ends up being processed*/
    02538             /* as "../.", and the last '.' is stripped.  This   */
    02539             /* would not be correct processing.                 */
    02540             if (begin && s[1] == '.' && (s[2] == '/' || s[2] == '\0')) {
    02541 /*fprintf(stderr, "    leading \"..\"\n"); */
    02542                 *t++ = *s++;
    02543                 /*@switchbreak@*/ break;
    02544             }
    02545             /* Single . is special */
    02546             if (begin && s[1] == '\0') {
    02547                 /*@switchbreak@*/ break;
    02548             }
    02549             /* Trim embedded ./ , trailing /. */
    02550             if ((t[-1] == '/' && s[1] == '\0') || (t > path && t[-1] == '/' && s[1] == '/')) {
    02551                 s++;
    02552                 continue;
    02553             }
    02554             /* Trim embedded /../ and trailing /.. */
    02555             if (!begin && t > path && t[-1] == '/' && s[1] == '.' && (s[2] == '/' || s[2] == '\0')) {
    02556                 t = te;
    02557                 /* Move parent dir forward */
    02558                 if (te > path)
    02559                     for (--te; te > path && *te != '/'; te--)
    02560                         {};
    02561 /*fprintf(stderr, "*** prev pdir \"%.*s\"\n", (te-path), path); */
    02562                 s++;
    02563                 s++;
    02564                 continue;
    02565             }
    02566             /*@switchbreak@*/ break;
    02567         default:
    02568             begin = 0;
    02569             /*@switchbreak@*/ break;
    02570         }
    02571         *t++ = *s++;
    02572     }
    02573
    02574     /* Trim trailing / (but leave single / alone) */
    02575     if (t > &path[1] && t[-1] == '/')
    02576         t--;
    02577     *t = '\0';
    02578
    02579 /*fprintf(stderr, "\t%s\n", path); */
    02580     return path;
    02581 }
    02582
    02583 /* Return concatenated and expanded canonical path. */
    02584
    02585 const char *
    02586 rpmGetPath(const char *path, ...)
    02587 {
    02588     size_t bufn = _macro_BUFSIZ;
    02589     char *buf = alloca(bufn);
    02590     const char * s;
    02591     char * t, * te;
    02592     va_list ap;
    02593
    02594     if (path == NULL)
    02595         return xstrdup("");
    02596
    02597     buf[0] = '\0';
    02598     t = buf;
    02599     te = stpcpy(t, path);
    02600     *te = '\0';
    02601
    02602     va_start(ap, path);
    02603     while ((s = va_arg(ap, const char *)) != NULL) {
    02604         te = stpcpy(te, s);
    02605         *te = '\0';
    02606     }
    02607     va_end(ap);
    02608 /*@-modfilesys@*/
    02609     (void) expandMacros(NULL, NULL, buf, bufn);
    02610 /*@=modfilesys@*/
    02611
    02612     (void) rpmCleanPath(buf);
    02613     return xstrdup(buf);        /* XXX xstrdup has side effects. */
    02614 }
    02615
    02616 /* Merge 3 args into path, any or all of which may be a url. */
    02617
    02618 const char * rpmGenPath(const char * urlroot, const char * urlmdir,
    02619                 const char *urlfile)
    02620 {
    02621 /*@owned@*/ const char * xroot = rpmGetPath(urlroot, NULL);
    02622 /*@dependent@*/ const char * root = xroot;
    02623 /*@owned@*/ const char * xmdir = rpmGetPath(urlmdir, NULL);
    02624 /*@dependent@*/ const char * mdir = xmdir;
    02625 /*@owned@*/ const char * xfile = rpmGetPath(urlfile, NULL);
    02626 /*@dependent@*/ const char * file = xfile;
    02627     const char * result;
    02628     const char * url = NULL;
    02629     int nurl = 0;
    02630     int ut;
    02631
    02632 #if 0
    02633 if (_debug) fprintf(stderr, "*** RGP xroot %s xmdir %s xfile %s\n", xroot, xmdir, xfile);
    02634 #endif
    02635     ut = urlPath(xroot, &root);
    02636     if (url == NULL && ut > URL_IS_DASH) {
    02637         url = xroot;
    02638         nurl = root - xroot;
    02639 #if 0
    02640 if (_debug) fprintf(stderr, "*** RGP ut %d root %s nurl %d\n", ut, root, nurl);
    02641 #endif
    02642     }
    02643     if (root == NULL || *root == '\0') root = "/";
    02644
    02645     ut = urlPath(xmdir, &mdir);
    02646     if (url == NULL && ut > URL_IS_DASH) {
    02647         url = xmdir;
    02648         nurl = mdir - xmdir;
    02649 #if 0
    02650 if (_debug) fprintf(stderr, "*** RGP ut %d mdir %s nurl %d\n", ut, mdir, nurl);
    02651 #endif
    02652     }
    02653     if (mdir == NULL || *mdir == '\0') mdir = "/";
    02654
    02655     ut = urlPath(xfile, &file);
    02656     if (url == NULL && ut > URL_IS_DASH) {
    02657         url = xfile;
    02658         nurl = file - xfile;
    02659 #if 0
    02660 if (_debug) fprintf(stderr, "*** RGP ut %d file %s nurl %d\n", ut, file, nurl);
    02661 #endif
    02662     }
    02663
    02664     if (url && nurl > 0) {
    02665         char *t = strncpy(alloca(nurl+1), url, nurl);
    02666         t[nurl] = '\0';
    02667         url = t;
    02668     } else
    02669         url = "";
    02670
    02671     result = rpmGetPath(url, root, "/", mdir, "/", file, NULL);
    02672
    02673     xroot = _free(xroot);
    02674     xmdir = _free(xmdir);
    02675     xfile = _free(xfile);
    02676 #if 0
    02677 if (_debug) fprintf(stderr, "*** RGP result %s\n", result);
    02678 #endif
    02679     return result;
    02680 }
    02681
    02682 /* =============================================================== */
    02683
    02684 #if defined(DEBUG_MACROS)
    02685
    02686 #if defined(EVAL_MACROS)
    02687
    02688 const char *rpmMacrofiles = MACROFILES;
    02689
    02690 int
    02691 main(int argc, char *argv[])
    02692 {
    02693     int c;
    02694     int errflg = 0;
    02695     extern char *optarg;
    02696     extern int optind;
    02697
    02698     while ((c = getopt(argc, argv, "f:")) != EOF ) {
    02699         switch (c) {
    02700         case 'f':
    02701             rpmMacrofiles = optarg;
    02702             break;
    02703         case '?':
    02704         default:
    02705             errflg++;
    02706             break;
    02707         }
    02708     }
    02709     if (errflg || optind >= argc) {
    02710         fprintf(stderr, "Usage: %s [-f macropath ] macro ...\n", argv[0]);
    02711         exit(1);
    02712     }
    02713
    02714     rpmInitMacros(NULL, rpmMacrofiles);
    02715     /* XXX getopt(3) also used for parametrized macros, expect scwewiness. */
    02716     for ( ; optind < argc; optind++) {
    02717         const char *val;
    02718
    02719         val = rpmExpand(argv[optind], NULL);
    02720         if (val) {
    02721             fprintf(stdout, "%s:\t%s\n", argv[optind], val);
    02722             val = _free(val);
    02723         }
    02724     }
    02725     rpmFreeMacros(NULL);
    02726     return 0;
    02727 }
    02728
    02729 #else   /* !EVAL_MACROS */
    02730
    02731 const char *rpmMacrofiles = "../macros:./testmacros";
    02732 const char *testfile = "./test";
    02733
    02734 int
    02735 main(int argc, char *argv[])
    02736 {
    02737     size_t bufn = _macro_BUFSIZ;
    02738     char *buf = alloca(bufn);
    02739     FILE *fp;
    02740     int x;
    02741
    02742     rpmInitMacros(NULL, rpmMacrofiles);
    02743
    02744     if ((fp = fopen(testfile, "r")) != NULL) {
    02745         while(rdcl(buf, bufn, fp)) {
    02746             x = expandMacros(NULL, NULL, buf, bufn);
    02747             fprintf(stderr, "%d->%s\n", x, buf);
    02748             memset(buf, 0, bufn);
    02749         }
    02750         fclose(fp);
    02751     }
    02752
    02753     while(rdcl(buf, bufn, stdin)) {
    02754         x = expandMacros(NULL, NULL, buf, bufn);
    02755         fprintf(stderr, "%d->%s\n <-\n", x, buf);
    02756         memset(buf, 0, bufn);
    02757     }
    02758     rpmFreeMacros(NULL);
    02759
    02760     return 0;
    02761 }
    02762 #endif  /* EVAL_MACROS */
    02763 #endif  /* DEBUG_MACROS */