Main Page Modules Data Structures File List Data Fields Globals Related Pages

# rpmio/macro.c

Go to the documentation of this file.

    00001 /*@-boundsread@*/
    00006 #include "system.h"
    00007 #include <stdarg.h>
    00008
    00009 #if !defined(isblank)
    00010 #define isblank(_c)     ((_c) == ' ' || (_c) == '\t')
    00011 #endif
    00012 #define iseol(_c)       ((_c) == '\n' || (_c) == '\r')
    00013
    00014 #define STREQ(_t, _f, _fn)      ((_fn) == (sizeof(_t)-1) && !strncmp((_t), (_f), (_fn)))
    00015
    00016 #ifdef DEBUG_MACROS
    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 #define rpmError fprintf
    00025 #define RPMERR_BADSPEC stderr
    00026 #undef  _
    00027 #define _(x)    x
    00028
    00029 #define vmefail()               (exit(1), NULL)
    00030 #define urlPath(_xr, _r)        *(_r) = (_xr)
    00031
    00032 typedef FILE * FD_t;
    00033 #define Fopen(_path, _fmode)    fopen(_path, "r");
    00034 #define Ferror                  ferror
    00035 #define Fstrerror(_fd)          strerror(errno)
    00036 #define Fread                   fread
    00037 #define Fclose                  fclose
    00038
    00039 #define fdGetFILE(_fd)          (_fd)
    00040
    00041 #else
    00042
    00043 #include <rpmio_internal.h>
    00044 #include <rpmmessages.h>
    00045 #include <rpmerr.h>
    00046
    00047 #endif
    00048
    00049 #include <rpmmacro.h>
    00050
    00051 #include "debug.h"
    00052
    00053 /*@access FD_t@*/               /* XXX compared with NULL */
    00054 /*@access MacroContext@*/
    00055 /*@access MacroEntry@*/
    00056
    00057 static struct MacroContext_s rpmGlobalMacroContext_s;
    00058 /*@-compmempass@*/
    00059 MacroContext rpmGlobalMacroContext = &rpmGlobalMacroContext_s;
    00060 /*@=compmempass@*/
    00061
    00062 static struct MacroContext_s rpmCLIMacroContext_s;
    00063 /*@-compmempass@*/
    00064 MacroContext rpmCLIMacroContext = &rpmCLIMacroContext_s;
    00065 /*@=compmempass@*/
    00066
    00070 typedef /*@abstract@*/ struct MacroBuf_s {
    00071 /*@kept@*/ /*@exposed@*/
    00072     const char * s;
    00073 /*@shared@*/
    00074     char * t;
    00075     size_t nb;
    00076     int depth;
    00077     int macro_trace;
    00078     int expand_trace;
    00079 /*@kept@*/ /*@exposed@*/ /*@null@*/
    00080     void * spec;
    00081 /*@kept@*/ /*@exposed@*/
    00082     MacroContext mc;
    00083 } * MacroBuf;
    00084
    00085 #define SAVECHAR(_mb, _c) { *(_mb)->t = (_c), (_mb)->t++, (_mb)->nb--; }
    00086
    00087 /*@-exportlocal -exportheadervar@*/
    00088
    00089 #define MAX_MACRO_DEPTH 16
    00090 /*@unchecked@*/
    00091 int max_macro_depth = MAX_MACRO_DEPTH;
    00092
    00093 #ifdef  DEBUG_MACROS
    00094 /*@unchecked@*/
    00095 int print_macro_trace = 0;
    00096 /*@unchecked@*/
    00097 int print_expand_trace = 0;
    00098 #else
    00099 /*@unchecked@*/
    00100 int print_macro_trace = 0;
    00101 /*@unchecked@*/
    00102 int print_expand_trace = 0;
    00103 #endif
    00104 /*@=exportlocal =exportheadervar@*/
    00105
    00106 #define MACRO_CHUNK_SIZE        16
    00107
    00108 /* forward ref */
    00109 static int expandMacro(MacroBuf mb)
    00110         /*@globals rpmGlobalMacroContext,
    00111                 print_macro_trace, print_expand_trace, fileSystem @*/
    00112         /*@modifies mb, rpmGlobalMacroContext,
    00113                 print_macro_trace, print_expand_trace, fileSystem @*/;
    00114
    00120 /*@unused@*/ static inline /*@null@*/ void *
    00121 _free(/*@only@*/ /*@null@*/ const void * p)
    00122         /*@modifies p@*/
    00123 {
    00124     if (p != NULL)      free((void *)p);
    00125     return NULL;
    00126 }
    00127
    00128 /* =============================================================== */
    00129
    00136 static int
    00137 compareMacroName(const void * ap, const void * bp)
    00138         /*@*/
    00139 {
    00140     MacroEntry ame = *((MacroEntry *)ap);
    00141     MacroEntry bme = *((MacroEntry *)bp);
    00142
    00143     if (ame == NULL && bme == NULL)
    00144         return 0;
    00145     if (ame == NULL)
    00146         return 1;
    00147     if (bme == NULL)
    00148         return -1;
    00149     return strcmp(ame->name, bme->name);
    00150 }
    00151
    00156 /*@-boundswrite@*/
    00157 static void
    00158 expandMacroTable(MacroContext mc)
    00159         /*@modifies mc @*/
    00160 {
    00161     if (mc->macroTable == NULL) {
    00162         mc->macrosAllocated = MACRO_CHUNK_SIZE;
    00163         mc->macroTable = (MacroEntry *)
    00164             xmalloc(sizeof(*(mc->macroTable)) * mc->macrosAllocated);
    00165         mc->firstFree = 0;
    00166     } else {
    00167         mc->macrosAllocated += MACRO_CHUNK_SIZE;
    00168         mc->macroTable = (MacroEntry *)
    00169             xrealloc(mc->macroTable, sizeof(*(mc->macroTable)) *
    00170                         mc->macrosAllocated);
    00171     }
    00172     memset(&mc->macroTable[mc->firstFree], 0, MACRO_CHUNK_SIZE * sizeof(*(mc->macroTable)));
    00173 }
    00174 /*@=boundswrite@*/
    00175
    00180 static void
    00181 sortMacroTable(MacroContext mc)
    00182         /*@modifies mc @*/
    00183 {
    00184     int i;
    00185
    00186     if (mc == NULL || mc->macroTable == NULL)
    00187         return;
    00188
    00189     qsort(mc->macroTable, mc->firstFree, sizeof(*(mc->macroTable)),
    00190                 compareMacroName);
    00191
    00192     /* Empty pointers are now at end of table. Reset first free index. */
    00193     for (i = 0; i < mc->firstFree; i++) {
    00194         if (mc->macroTable[i] != NULL)
    00195             continue;
    00196         mc->firstFree = i;
    00197         break;
    00198     }
    00199 }
    00200
    00201 void
    00202 rpmDumpMacroTable(MacroContext mc, FILE * fp)
    00203 {
    00204     int nempty = 0;
    00205     int nactive = 0;
    00206
    00207     if (mc == NULL) mc = rpmGlobalMacroContext;
    00208     if (fp == NULL) fp = stderr;
    00209
    00210     fprintf(fp, "========================\n");
    00211     if (mc->macroTable != NULL) {
    00212         int i;
    00213         for (i = 0; i < mc->firstFree; i++) {
    00214             MacroEntry me;
    00215             if ((me = mc->macroTable[i]) == NULL) {
    00216                 /* XXX this should never happen */
    00217                 nempty++;
    00218                 continue;
    00219             }
    00220             fprintf(fp, "%3d%c %s", me->level,
    00221                         (me->used > 0 ? '=' : ':'), me->name);
    00222             if (me->opts && *me->opts)
    00223                     fprintf(fp, "(%s)", me->opts);
    00224             if (me->body && *me->body)
    00225                     fprintf(fp, "\t%s", me->body);
    00226             fprintf(fp, "\n");
    00227             nactive++;
    00228         }
    00229     }
    00230     fprintf(fp, _("======================== active %d empty %d\n"),
    00231                 nactive, nempty);
    00232 }
    00233
    00241 /*@-boundswrite@*/
    00242 /*@dependent@*/ /*@null@*/
    00243 static MacroEntry *
    00244 findEntry(MacroContext mc, const char * name, size_t namelen)
    00245         /*@*/
    00246 {
    00247     MacroEntry key, *ret;
    00248     struct MacroEntry_s keybuf;
    00249     char namebuf[1024];
    00250
    00251 /*@-globs@*/
    00252     if (mc == NULL) mc = rpmGlobalMacroContext;
    00253 /*@=globs@*/
    00254     if (mc->macroTable == NULL || mc->firstFree == 0)
    00255         return NULL;
    00256
    00257 /*@-branchstate@*/
    00258     if (namelen > 0) {
    00259         strncpy(namebuf, name, namelen);
    00260         namebuf[namelen] = '\0';
    00261         name = namebuf;
    00262     }
    00263 /*@=branchstate@*/
    00264
    00265     key = &keybuf;
    00266     memset(key, 0, sizeof(*key));
    00267     /*@-temptrans -assignexpose@*/
    00268     key->name = (char *)name;
    00269     /*@=temptrans =assignexpose@*/
    00270     ret = (MacroEntry *) bsearch(&key, mc->macroTable, mc->firstFree,
    00271                         sizeof(*(mc->macroTable)), compareMacroName);
    00272     /* XXX TODO: find 1st empty slot and return that */
    00273     return ret;
    00274 }
    00275 /*@=boundswrite@*/
    00276
    00277 /* =============================================================== */
    00278
    00287 /*@-boundswrite@*/
    00288 /*@null@*/
    00289 static char *
    00290 rdcl(/*@returned@*/ char * buf, size_t size, FD_t fd, int escapes)
    00291         /*@globals fileSystem @*/
    00292         /*@modifies buf, fileSystem @*/
    00293 {
    00294     char *q = buf - 1;          /* initialize just before buffer. */
    00295     size_t nb = 0;
    00296     size_t nread = 0;
    00297     FILE * f = fdGetFILE(fd);
    00298
    00299     if (f != NULL)
    00300     do {
    00301         *(++q) = '\0';                  /* terminate and move forward. */
    00302         if (fgets(q, size, f) == NULL)  /* read next line. */
    00303             break;
    00304         nb = strlen(q);
    00305         nread += nb;                    /* trim trailing \r and \n */
    00306         for (q += nb - 1; nb > 0 && iseol(*q); q--)
    00307             nb--;
    00308         if (!(nb > 0 && *q == '\\')) {  /* continue? */
    00309             *(++q) = '\0';              /* trim trailing \r, \n */
    00310             break;
    00311         }
    00312         if (escapes) {                  /* copy escape too */
    00313             q++;
    00314             nb++;
    00315         }
    00316         size -= nb;
    00317         if (*q == '\r')                 /* XXX avoid \r madness */
    00318             *q = '\n';
    00319     } while (size > 0);
    00320     return (nread > 0 ? buf : NULL);
    00321 }
    00322 /*@=boundswrite@*/
    00323
    00331 static const char *
    00332 matchchar(const char * p, char pl, char pr)
    00333         /*@*/
    00334 {
    00335     int lvl = 0;
    00336     char c;
    00337
    00338     while ((c = *p++) != '\0') {
    00339         if (c == '\\') {                /* Ignore escaped chars */
    00340             p++;
    00341             continue;
    00342         }
    00343         if (c == pr) {
    00344             if (--lvl <= 0)     return --p;
    00345         } else if (c == pl)
    00346             lvl++;
    00347     }
    00348     return (const char *)NULL;
    00349 }
    00350
    00357 static void
    00358 printMacro(MacroBuf mb, const char * s, const char * se)
    00359         /*@globals fileSystem @*/
    00360         /*@modifies fileSystem @*/
    00361 {
    00362     const char *senl;
    00363     const char *ellipsis;
    00364     int choplen;
    00365
    00366     if (s >= se) {      /* XXX just in case */
    00367         fprintf(stderr, _("%3d>%*s(empty)"), mb->depth,
    00368                 (2 * mb->depth + 1), "");
    00369         return;
    00370     }
    00371
    00372     if (s[-1] == '{')
    00373         s--;
    00374
    00375     /* Print only to first end-of-line (or end-of-string). */
    00376     for (senl = se; *senl && !iseol(*senl); senl++)
    00377         {};
    00378
    00379     /* Limit trailing non-trace output */
    00380     choplen = 61 - (2 * mb->depth);
    00381     if ((senl - s) > choplen) {
    00382         senl = s + choplen;
    00383         ellipsis = "...";
    00384     } else
    00385         ellipsis = "";
    00386
    00387     /* Substitute caret at end-of-macro position */
    00388     fprintf(stderr, "%3d>%*s%%%.*s^", mb->depth,
    00389         (2 * mb->depth + 1), "", (int)(se - s), s);
    00390     if (se[1] != '\0' && (senl - (se+1)) > 0)
    00391         fprintf(stderr, "%-.*s%s", (int)(senl - (se+1)), se+1, ellipsis);
    00392     fprintf(stderr, "\n");
    00393 }
    00394
    00401 static void
    00402 printExpansion(MacroBuf mb, const char * t, const char * te)
    00403         /*@globals fileSystem @*/
    00404         /*@modifies fileSystem @*/
    00405 {
    00406     const char *ellipsis;
    00407     int choplen;
    00408
    00409     if (!(te > t)) {
    00410         fprintf(stderr, _("%3d<%*s(empty)\n"), mb->depth, (2 * mb->depth + 1), "");
    00411         return;
    00412     }
    00413
    00414     /* Shorten output which contains newlines */
    00415     while (te > t && iseol(te[-1]))
    00416         te--;
    00417     ellipsis = "";
    00418     if (mb->depth > 0) {
    00419         const char *tenl;
    00420
    00421         /* Skip to last line of expansion */
    00422         while ((tenl = strchr(t, '\n')) && tenl < te)
    00423             t = ++tenl;
    00424
    00425         /* Limit expand output */
    00426         choplen = 61 - (2 * mb->depth);
    00427         if ((te - t) > choplen) {
    00428             te = t + choplen;
    00429             ellipsis = "...";
    00430         }
    00431     }
    00432
    00433     fprintf(stderr, "%3d<%*s", mb->depth, (2 * mb->depth + 1), "");
    00434     if (te > t)
    00435         fprintf(stderr, "%.*s%s", (int)(te - t), t, ellipsis);
    00436     fprintf(stderr, "\n");
    00437 }
    00438
    00439 #define SKIPBLANK(_s, _c)       \
    00440         /*@-globs@*/    /* FIX: __ctype_b */ \
    00441         while (((_c) = *(_s)) && isblank(_c)) \
    00442                 (_s)++;         \
    00443         /*@=globs@*/
    00444
    00445 #define SKIPNONBLANK(_s, _c)    \
    00446         /*@-globs@*/    /* FIX: __ctype_b */ \
    00447         while (((_c) = *(_s)) && !(isblank(_c) || iseol(_c))) \
    00448                 (_s)++;         \
    00449         /*@=globs@*/
    00450
    00451 #define COPYNAME(_ne, _s, _c)   \
    00452     {   SKIPBLANK(_s,_c);       \
    00453         /*@-boundswrite@*/      \
    00454         while(((_c) = *(_s)) && (xisalnum(_c) || (_c) == '_')) \
    00455                 *(_ne)++ = *(_s)++; \
    00456         *(_ne) = '\0';          \
    00457         /*@=boundswrite@*/      \
    00458     }
    00459
    00460 #define COPYOPTS(_oe, _s, _c)   \
    00461     {   /*@-boundswrite@*/      \
    00462         while(((_c) = *(_s)) && (_c) != ')') \
    00463                 *(_oe)++ = *(_s)++; \
    00464         *(_oe) = '\0';          \
    00465         /*@=boundswrite@*/      \
    00466     }
    00467
    00468 #define COPYBODY(_be, _s, _c)   \
    00469     {   /*@-boundswrite@*/      \
    00470         while(((_c) = *(_s)) && !iseol(_c)) { \
    00471                 if ((_c) == '\\') \
    00472                         (_s)++; \
    00473                 *(_be)++ = *(_s)++; \
    00474         }                       \
    00475         *(_be) = '\0';          \
    00476         /*@=boundswrite@*/      \
    00477     }
    00478
    00486 static int
    00487 expandT(MacroBuf mb, const char * f, size_t flen)
    00488         /*@globals rpmGlobalMacroContext, fileSystem@*/
    00489         /*@modifies mb, rpmGlobalMacroContext, fileSystem @*/
    00490 {
    00491     char *sbuf;
    00492     const char *s = mb->s;
    00493     int rc;
    00494
    00495     sbuf = alloca(flen + 1);
    00496     memset(sbuf, 0, (flen + 1));
    00497
    00498     strncpy(sbuf, f, flen);
    00499     sbuf[flen] = '\0';
    00500     mb->s = sbuf;
    00501     rc = expandMacro(mb);
    00502     mb->s = s;
    00503     return rc;
    00504 }
    00505
    00506 #if 0
    00507
    00514 static int
    00515 expandS(MacroBuf mb, char * tbuf, size_t tbuflen)
    00516         /*@globals rpmGlobalMacroContext, fileSystem@*/
    00517         /*@modifies mb, *tbuf, rpmGlobalMacroContext, fileSystem @*/
    00518 {
    00519     const char *t = mb->t;
    00520     size_t nb = mb->nb;
    00521     int rc;
    00522
    00523     mb->t = tbuf;
    00524     mb->nb = tbuflen;
    00525     rc = expandMacro(mb);
    00526     mb->t = t;
    00527     mb->nb = nb;
    00528     return rc;
    00529 }
    00530 #endif
    00531
    00539 /*@-boundswrite@*/
    00540 static int
    00541 expandU(MacroBuf mb, char * u, size_t ulen)
    00542         /*@globals rpmGlobalMacroContext, fileSystem@*/
    00543         /*@modifies mb, *u, rpmGlobalMacroContext, fileSystem @*/
    00544 {
    00545     const char *s = mb->s;
    00546     char *t = mb->t;
    00547     size_t nb = mb->nb;
    00548     char *tbuf;
    00549     int rc;
    00550
    00551     tbuf = alloca(ulen + 1);
    00552     memset(tbuf, 0, (ulen + 1));
    00553
    00554     mb->s = u;
    00555     mb->t = tbuf;
    00556     mb->nb = ulen;
    00557     rc = expandMacro(mb);
    00558
    00559     tbuf[ulen] = '\0';  /* XXX just in case */
    00560     if (ulen > mb->nb)
    00561         strncpy(u, tbuf, (ulen - mb->nb + 1));
    00562
    00563     mb->s = s;
    00564     mb->t = t;
    00565     mb->nb = nb;
    00566
    00567     return rc;
    00568 }
    00569 /*@=boundswrite@*/
    00570
    00578 /*@-boundswrite@*/
    00579 static int
    00580 doShellEscape(MacroBuf mb, const char * cmd, size_t clen)
    00581         /*@globals rpmGlobalMacroContext, fileSystem @*/
    00582         /*@modifies mb, rpmGlobalMacroContext, fileSystem @*/
    00583 {
    00584     char pcmd[BUFSIZ];
    00585     FILE *shf;
    00586     int rc;
    00587     int c;
    00588
    00589     strncpy(pcmd, cmd, clen);
    00590     pcmd[clen] = '\0';
    00591     rc = expandU(mb, pcmd, sizeof(pcmd));
    00592     if (rc)
    00593         return rc;
    00594
    00595     if ((shf = popen(pcmd, "r")) == NULL)
    00596         return 1;
    00597     while(mb->nb > 0 && (c = fgetc(shf)) != EOF)
    00598         SAVECHAR(mb, c);
    00599     (void) pclose(shf);
    00600
    00601     /* XXX delete trailing \r \n */
    00602     while (iseol(mb->t[-1])) {
    00603         *(mb->t--) = '\0';
    00604         mb->nb++;
    00605     }
    00606     return 0;
    00607 }
    00608 /*@=boundswrite@*/
    00609
    00618 /*@dependent@*/ static const char *
    00619 doDefine(MacroBuf mb, /*@returned@*/ const char * se, int level, int expandbody)
    00620         /*@globals rpmGlobalMacroContext @*/
    00621         /*@modifies mb, rpmGlobalMacroContext @*/
    00622 {
    00623     const char *s = se;
    00624     char buf[BUFSIZ], *n = buf, *ne = n;
    00625     char *o = NULL, *oe;
    00626     char *b, *be;
    00627     int c;
    00628     int oc = ')';
    00629
    00630     /* Copy name */
    00631     COPYNAME(ne, s, c);
    00632
    00633     /* Copy opts (if present) */
    00634     oe = ne + 1;
    00635     if (*s == '(') {
    00636         s++;    /* skip ( */
    00637         o = oe;
    00638         COPYOPTS(oe, s, oc);
    00639         s++;    /* skip ) */
    00640     }
    00641
    00642     /* Copy body, skipping over escaped newlines */
    00643     b = be = oe + 1;
    00644     SKIPBLANK(s, c);
    00645     if (c == '{') {     /* XXX permit silent {...} grouping */
    00646         if ((se = matchchar(s, c, '}')) == NULL) {
    00647             rpmError(RPMERR_BADSPEC,
    00648                 _("Macro %%%s has unterminated body\n"), n);
    00649             se = s;     /* XXX W2DO? */
    00650             return se;
    00651         }
    00652         s++;    /* XXX skip { */
    00653 /*@-boundswrite@*/
    00654         strncpy(b, s, (se - s));
    00655         b[se - s] = '\0';
    00656 /*@=boundswrite@*/
    00657         be += strlen(b);
    00658         se++;   /* XXX skip } */
    00659         s = se; /* move scan forward */
    00660     } else {    /* otherwise free-field */
    00661         COPYBODY(be, s, c);
    00662
    00663 /*@-boundswrite@*/
    00664         /* Trim trailing blanks/newlines */
    00665 /*@-globs@*/
    00666         while (--be >= b && (c = *be) && (isblank(c) || iseol(c)))
    00667             {};
    00668 /*@=globs@*/
    00669         *(++be) = '\0'; /* one too far */
    00670 /*@=boundswrite@*/
    00671     }
    00672
    00673     /* Move scan over body */
    00674     while (iseol(*s))
    00675         s++;
    00676     se = s;
    00677
    00678     /* Names must start with alphabetic or _ and be at least 3 chars */
    00679     if (!((c = *n) && (xisalpha(c) || c == '_') && (ne - n) > 2)) {
    00680         rpmError(RPMERR_BADSPEC,
    00681                 _("Macro %%%s has illegal name (%%define)\n"), n);
    00682         return se;
    00683     }
    00684
    00685     /* Options must be terminated with ')' */
    00686     if (o && oc != ')') {
    00687         rpmError(RPMERR_BADSPEC, _("Macro %%%s has unterminated opts\n"), n);
    00688         return se;
    00689     }
    00690
    00691     if ((be - b) < 1) {
    00692         rpmError(RPMERR_BADSPEC, _("Macro %%%s has empty body\n"), n);
    00693         return se;
    00694     }
    00695
    00696 /*@-modfilesys@*/
    00697     if (expandbody && expandU(mb, b, (&buf[sizeof(buf)] - b))) {
    00698         rpmError(RPMERR_BADSPEC, _("Macro %%%s failed to expand\n"), n);
    00699         return se;
    00700     }
    00701 /*@=modfilesys@*/
    00702
    00703     addMacro(mb->mc, n, o, b, (level - 1));
    00704
    00705     return se;
    00706 }
    00707
    00714 /*@dependent@*/ static const char *
    00715 doUndefine(MacroContext mc, /*@returned@*/ const char * se)
    00716         /*@globals rpmGlobalMacroContext @*/
    00717         /*@modifies mc, rpmGlobalMacroContext @*/
    00718 {
    00719     const char *s = se;
    00720     char buf[BUFSIZ], *n = buf, *ne = n;
    00721     int c;
    00722
    00723     COPYNAME(ne, s, c);
    00724
    00725     /* Move scan over body */
    00726     while (iseol(*s))
    00727         s++;
    00728     se = s;
    00729
    00730     /* Names must start with alphabetic or _ and be at least 3 chars */
    00731     if (!((c = *n) && (xisalpha(c) || c == '_') && (ne - n) > 2)) {
    00732         rpmError(RPMERR_BADSPEC,
    00733                 _("Macro %%%s has illegal name (%%undefine)\n"), n);
    00734         return se;
    00735     }
    00736
    00737     delMacro(mc, n);
    00738
    00739     return se;
    00740 }
    00741
    00742 #ifdef  DYING
    00743 static void
    00744 dumpME(const char * msg, MacroEntry me)
    00745         /*@globals fileSystem @*/
    00746         /*@modifies fileSystem @*/
    00747 {
    00748     if (msg)
    00749         fprintf(stderr, "%s", msg);
    00750     fprintf(stderr, "\tme %p", me);
    00751     if (me)
    00752         fprintf(stderr,"\tname %p(%s) prev %p",
    00753                 me->name, me->name, me->prev);
    00754     fprintf(stderr, "\n");
    00755 }
    00756 #endif
    00757
    00766 static void
    00767 pushMacro(/*@out@*/ MacroEntry * mep,
    00768                 const char * n, /*@null@*/ const char * o,
    00769                 /*@null@*/ const char * b, int level)
    00770         /*@modifies *mep @*/
    00771 {
    00772     MacroEntry prev = (mep && *mep ? *mep : NULL);
    00773     MacroEntry me = (MacroEntry) xmalloc(sizeof(*me));
    00774
    00775     /*@-assignexpose@*/
    00776     me->prev = prev;
    00777     /*@=assignexpose@*/
    00778     me->name = (prev ? prev->name : xstrdup(n));
    00779     me->opts = (o ? xstrdup(o) : NULL);
    00780     me->body = xstrdup(b ? b : "");
    00781     me->used = 0;
    00782     me->level = level;
    00783 /*@-boundswrite@*/
    00784 /*@-branchstate@*/
    00785     if (mep)
    00786         *mep = me;
    00787     else
    00788         me = _free(me);
    00789 /*@=branchstate@*/
    00790 /*@=boundswrite@*/
    00791 }
    00792
    00797 static void
    00798 popMacro(MacroEntry * mep)
    00799         /*@modifies *mep @*/
    00800 {
    00801         MacroEntry me = (*mep ? *mep : NULL);
    00802
    00803 /*@-branchstate@*/
    00804         if (me) {
    00805                 /* XXX cast to workaround const */
    00806                 /*@-onlytrans@*/
    00807 /*@-boundswrite@*/
    00808                 if ((*mep = me->prev) == NULL)
    00809                         me->name = _free(me->name);
    00810 /*@=boundswrite@*/
    00811                 me->opts = _free(me->opts);
    00812                 me->body = _free(me->body);
    00813                 me = _free(me);
    00814                 /*@=onlytrans@*/
    00815         }
    00816 /*@=branchstate@*/
    00817 }
    00818
    00823 static void
    00824 freeArgs(MacroBuf mb)
    00825         /*@modifies mb @*/
    00826 {
    00827     MacroContext mc = mb->mc;
    00828     int ndeleted = 0;
    00829     int i;
    00830
    00831     if (mc == NULL || mc->macroTable == NULL)
    00832         return;
    00833
    00834     /* Delete dynamic macro definitions */
    00835     for (i = 0; i < mc->firstFree; i++) {
    00836         MacroEntry *mep, me;
    00837         int skiptest = 0;
    00838         mep = &mc->macroTable[i];
    00839         me = *mep;
    00840
    00841         if (me == NULL)         /* XXX this should never happen */
    00842             continue;
    00843         if (me->level < mb->depth)
    00844             continue;
    00845         if (strlen(me->name) == 1 && strchr("#*0", *me->name)) {
    00846             if (*me->name == '*' && me->used > 0)
    00847                 skiptest = 1; /* XXX skip test for %# %* %0 */
    00848         } else if (!skiptest && me->used <= 0) {
    00849 #if NOTYET
    00850             rpmError(RPMERR_BADSPEC,
    00851                         _("Macro %%%s (%s) was not used below level %d\n"),
    00852                         me->name, me->body, me->level);
    00853 #endif
    00854         }
    00855         popMacro(mep);
    00856         if (!(mep && *mep))
    00857             ndeleted++;
    00858     }
    00859
    00860     /* If any deleted macros, sort macro table */
    00861     if (ndeleted)
    00862         sortMacroTable(mc);
    00863 }
    00864
    00874 /*@-bounds@*/
    00875 /*@dependent@*/ static const char *
    00876 grabArgs(MacroBuf mb, const MacroEntry me, /*@returned@*/ const char * se, char lastc)
    00877         /*@globals rpmGlobalMacroContext @*/
    00878         /*@modifies mb, rpmGlobalMacroContext @*/
    00879 {
    00880     char buf[BUFSIZ], *b, *be;
    00881     char aname[16];
    00882     const char *opts, *o;
    00883     int argc = 0;
    00884     const char **argv;
    00885     int c;
    00886
    00887     /* Copy macro name as argv[0], save beginning of args.  */
    00888     buf[0] = '\0';
    00889     b = be = stpcpy(buf, me->name);
    00890
    00891     addMacro(mb->mc, "0", NULL, buf, mb->depth);
    00892
    00893     argc = 1;   /* XXX count argv[0] */
    00894
    00895     /* Copy args into buf until lastc */
    00896     *be++ = ' ';
    00897     while ((c = *se++) != '\0' && c != lastc) {
    00898 /*@-globs@*/
    00899         if (!isblank(c)) {
    00900             *be++ = c;
    00901             continue;
    00902         }
    00903 /*@=globs@*/
    00904         /* c is blank */
    00905         if (be[-1] == ' ')
    00906             continue;
    00907         /* a word has ended */
    00908         *be++ = ' ';
    00909         argc++;
    00910     }
    00911     if (c == '\0') se--;        /* one too far */
    00912     if (be[-1] != ' ')
    00913         argc++, be++;           /* last word has not trailing ' ' */
    00914     be[-1] = '\0';
    00915     if (*b == ' ') b++;         /* skip the leading ' ' */
    00916
    00917 /*
    00918  * The macro %* analoguous to the shell's $* means "Pass all non-macro
    00919  * parameters." Consequently, there needs to be a macro that means "Pass all
    00920  * (including macro parameters) options". This is useful for verifying
    00921  * parameters during expansion and yet transparently passing all parameters
    00922  * through for higher level processing (e.g. %description and/or %setup).
    00923  * This is the (potential) justification for %{**} ...
    00924  */
    00925     /* Add unexpanded args as macro */
    00926     addMacro(mb->mc, "**", NULL, b, mb->depth);
    00927
    00928 #ifdef NOTYET
    00929     /* XXX if macros can be passed as args ... */
    00930     expandU(mb, buf, sizeof(buf));
    00931 #endif
    00932
    00933     /* Build argv array */
    00934     argv = (const char **) alloca((argc + 1) * sizeof(*argv));
    00935     be[-1] = ' '; /* assert((be - 1) == (b + strlen(b) == buf + strlen(buf))) */
    00936     be[0] = '\0';
    00937     b = buf;
    00938     for (c = 0; c < argc; c++) {
    00939         argv[c] = b;
    00940         b = strchr(b, ' ');
    00941         *b++ = '\0';
    00942     }
    00943     /* assert(b == be);  */
    00944     argv[argc] = NULL;
    00945
    00946     /* Citation from glibc/posix/getopt.c:
    00947      *    Index in ARGV of the next element to be scanned.
    00948      *    This is used for communication to and from the caller
    00949      *    and for communication between successive calls to `getopt'.
    00950      *
    00951      *    On entry to `getopt', zero means this is the first call; initialize.
    00952      *
    00953      *    When `getopt' returns -1, this is the index of the first of the
    00954      *    non-option elements that the caller should itself scan.
    00955      *
    00956      *    Otherwise, `optind' communicates from one call to the next
    00957      *    how much of ARGV has been scanned so far.
    00958      */
    00959     /* 1003.2 says this must be 1 before any call.  */
    00960
    00961 #ifdef __GLIBC__
    00962     /*@-mods@*/
    00963     optind = 1;
    00964     /*@=mods@*/
    00965 #endif
    00966
    00967     opts = me->opts;
    00968
    00969     /* Define option macros. */
    00970     while((c = getopt(argc, (char **)argv, opts)) != -1) {
    00971         if (c == '?' || (o = strchr(opts, c)) == NULL) {
    00972             rpmError(RPMERR_BADSPEC, _("Unknown option %c in %s(%s)\n"),
    00973                         (char)c, me->name, opts);
    00974             return se;
    00975         }
    00976         *be++ = '-';
    00977         *be++ = c;
    00978         if (o[1] == ':') {
    00979             *be++ = ' ';
    00980             be = stpcpy(be, optarg);
    00981         }
    00982         *be++ = '\0';
    00983         aname[0] = '-'; aname[1] = c; aname[2] = '\0';
    00984         addMacro(mb->mc, aname, NULL, b, mb->depth);
    00985         if (o[1] == ':') {
    00986             aname[0] = '-'; aname[1] = c; aname[2] = '*'; aname[3] = '\0';
    00987             addMacro(mb->mc, aname, NULL, optarg, mb->depth);
    00988         }
    00989         be = b; /* reuse the space */
    00990     }
    00991
    00992     /* Add arg count as macro. */
    00993     sprintf(aname, "%d", (argc - optind));
    00994     addMacro(mb->mc, "#", NULL, aname, mb->depth);
    00995
    00996     /* Add macro for each arg. Concatenate args for %*. */
    00997     if (be) {
    00998         *be = '\0';
    00999         for (c = optind; c < argc; c++) {
    01000             sprintf(aname, "%d", (c - optind + 1));
    01001             addMacro(mb->mc, aname, NULL, argv[c], mb->depth);
    01002             *be++ = ' ';
    01003             be = stpcpy(be, argv[c]);
    01004         }
    01005     }
    01006
    01007     /* Add unexpanded args as macro. */
    01008     addMacro(mb->mc, "*", NULL, b, mb->depth);
    01009
    01010     return se;
    01011 }
    01012 /*@=bounds@*/
    01013
    01021 static void
    01022 doOutput(MacroBuf mb, int waserror, const char * msg, size_t msglen)
    01023         /*@globals rpmGlobalMacroContext, fileSystem @*/
    01024         /*@modifies mb, rpmGlobalMacroContext, fileSystem @*/
    01025 {
    01026     char buf[BUFSIZ];
    01027
    01028     strncpy(buf, msg, msglen);
    01029     buf[msglen] = '\0';
    01030     (void) expandU(mb, buf, sizeof(buf));
    01031     if (waserror)
    01032         rpmError(RPMERR_BADSPEC, "%s\n", buf);
    01033     else
    01034         fprintf(stderr, "%s", buf);
    01035 }
    01036
    01046 static void
    01047 doFoo(MacroBuf mb, int negate, const char * f, size_t fn,
    01048                 const char * g, size_t gn)
    01049         /*@globals rpmGlobalMacroContext, fileSystem, internalState @*/
    01050         /*@modifies mb, rpmGlobalMacroContext, fileSystem, internalState @*/
    01051 {
    01052     char buf[BUFSIZ], *b = NULL, *be;
    01053     int c;
    01054
    01055     buf[0] = '\0';
    01056     if (g) {
    01057         strncpy(buf, g, gn);
    01058         buf[gn] = '\0';
    01059         (void) expandU(mb, buf, sizeof(buf));
    01060     }
    01061     if (STREQ("basename", f, fn)) {
    01062         if ((b = strrchr(buf, '/')) == NULL)
    01063             b = buf;
    01064         else
    01065             b++;
    01066 #if NOTYET
    01067     /* XXX watchout for conflict with %dir */
    01068     } else if (STREQ("dirname", f, fn)) {
    01069         if ((b = strrchr(buf, '/')) != NULL)
    01070             *b = '\0';
    01071         b = buf;
    01072 #endif
    01073     } else if (STREQ("suffix", f, fn)) {
    01074         if ((b = strrchr(buf, '.')) != NULL)
    01075             b++;
    01076     } else if (STREQ("expand", f, fn)) {
    01077         b = buf;
    01078     } else if (STREQ("verbose", f, fn)) {
    01079         if (negate)
    01080             b = (rpmIsVerbose() ? NULL : buf);
    01081         else
    01082             b = (rpmIsVerbose() ? buf : NULL);
    01083     } else if (STREQ("url2path", f, fn) || STREQ("u2p", f, fn)) {
    01084         (void)urlPath(buf, (const char **)&b);
    01085 /*@-branchstate@*/
    01086         if (*b == '\0') b = "/";
    01087 /*@=branchstate@*/
    01088     } else if (STREQ("uncompress", f, fn)) {
    01089         rpmCompressedMagic compressed = COMPRESSED_OTHER;
    01090 /*@-globs@*/
    01091         for (b = buf; (c = *b) && isblank(c);)
    01092             b++;
    01093         for (be = b; (c = *be) && !isblank(c);)
    01094             be++;
    01095 /*@=globs@*/
    01096         *be++ = '\0';
    01097 #ifndef DEBUG_MACROS
    01098         (void) isCompressed(b, &compressed);
    01099 #endif
    01100         switch(compressed) {
    01101         default:
    01102         case 0: /* COMPRESSED_NOT */
    01103             sprintf(be, "%%_cat %s", b);
    01104             break;
    01105         case 1: /* COMPRESSED_OTHER */
    01106             sprintf(be, "%%_gzip -dc %s", b);
    01107             break;
    01108         case 2: /* COMPRESSED_BZIP2 */
    01109             sprintf(be, "%%_bzip2 %s", b);
    01110             break;
    01111         case 3: /* COMPRESSED_ZIP */
    01112             sprintf(be, "%%_unzip %s", b);
    01113             break;
    01114         }
    01115         b = be;
    01116     } else if (STREQ("S", f, fn)) {
    01117         for (b = buf; (c = *b) && xisdigit(c);)
    01118             b++;
    01119         if (!c) {       /* digit index */
    01120             b++;
    01121             sprintf(b, "%%SOURCE%s", buf);
    01122         } else
    01123             b = buf;
    01124     } else if (STREQ("P", f, fn)) {
    01125         for (b = buf; (c = *b) && xisdigit(c);)
    01126             b++;
    01127         if (!c) {       /* digit index */
    01128             b++;
    01129             sprintf(b, "%%PATCH%s", buf);
    01130         } else
    01131                         b = buf;
    01132     } else if (STREQ("F", f, fn)) {
    01133         b = buf + strlen(buf) + 1;
    01134         sprintf(b, "file%s.file", buf);
    01135     }
    01136
    01137     if (b) {
    01138         (void) expandT(mb, b, strlen(b));
    01139     }
    01140 }
    01141
    01148 static int
    01149 expandMacro(MacroBuf mb)
    01150         /*@globals rpmGlobalMacroContext,
    01151                 print_macro_trace, print_expand_trace, fileSystem @*/
    01152         /*@modifies mb, rpmGlobalMacroContext,
    01153                 print_macro_trace, print_expand_trace, fileSystem @*/
    01154 {
    01155     MacroEntry *mep;
    01156     MacroEntry me;
    01157     const char *s = mb->s, *se;
    01158     const char *f, *fe;
    01159     const char *g, *ge;
    01160     size_t fn, gn;
    01161     char *t = mb->t;    /* save expansion pointer for printExpand */
    01162     int c;
    01163     int rc = 0;
    01164     int negate;
    01165     char grab;
    01166     int chkexist;
    01167
    01168     if (++mb->depth > max_macro_depth) {
    01169         rpmError(RPMERR_BADSPEC,
    01170                 _("Recursion depth(%d) greater than max(%d)\n"),
    01171                 mb->depth, max_macro_depth);
    01172         mb->depth--;
    01173         mb->expand_trace = 1;
    01174         return 1;
    01175     }
    01176
    01177 /*@-branchstate@*/
    01178     while (rc == 0 && mb->nb > 0 && (c = *s) != '\0') {
    01179         s++;
    01180         /* Copy text until next macro */
    01181         switch(c) {
    01182         case '%':
    01183                 if (*s != '%')
    01184                         /*@switchbreak@*/ break;
    01185                 s++;    /* skip first % in %% */
    01186                 /*@fallthrough@*/
    01187         default:
    01188                 SAVECHAR(mb, c);
    01189                 continue;
    01190                 /*@notreached@*/ /*@switchbreak@*/ break;
    01191         }
    01192
    01193         /* Expand next macro */
    01194         f = fe = NULL;
    01195         g = ge = NULL;
    01196         if (mb->depth > 1)      /* XXX full expansion for outermost level */
    01197                 t = mb->t;      /* save expansion pointer for printExpand */
    01198         negate = 0;
    01199         grab = '\0';
    01200         chkexist = 0;
    01201         switch ((c = *s)) {
    01202         default:                /* %name substitution */
    01203                 while (strchr("!?", *s) != NULL) {
    01204                         switch(*s++) {
    01205                         case '!':
    01206                                 negate = ((negate + 1) % 2);
    01207                                 /*@switchbreak@*/ break;
    01208                         case '?':
    01209                                 chkexist++;
    01210                                 /*@switchbreak@*/ break;
    01211                         }
    01212                 }
    01213                 f = se = s;
    01214                 if (*se == '-')
    01215                         se++;
    01216                 while((c = *se) && (xisalnum(c) || c == '_'))
    01217                         se++;
    01218                 /* Recognize non-alnum macros too */
    01219                 switch (*se) {
    01220                 case '*':
    01221                         se++;
    01222                         if (*se == '*') se++;
    01223                         /*@innerbreak@*/ break;
    01224                 case '#':
    01225                         se++;
    01226                         /*@innerbreak@*/ break;
    01227                 default:
    01228                         /*@innerbreak@*/ break;
    01229                 }
    01230                 fe = se;
    01231                 /* For "%name " macros ... */
    01232 /*@-globs@*/
    01233                 if ((c = *fe) && isblank(c))
    01234                         grab = '\n';
    01235 /*@=globs@*/
    01236                 /*@switchbreak@*/ break;
    01237         case '(':               /* %(...) shell escape */
    01238                 if ((se = matchchar(s, c, ')')) == NULL) {
    01239                         rpmError(RPMERR_BADSPEC,
    01240                                 _("Unterminated %c: %s\n"), (char)c, s);
    01241                         rc = 1;
    01242                         continue;
    01243                 }
    01244                 if (mb->macro_trace)
    01245                         printMacro(mb, s, se+1);
    01246
    01247                 s++;    /* skip ( */
    01248                 rc = doShellEscape(mb, s, (se - s));
    01249                 se++;   /* skip ) */
    01250
    01251                 s = se;
    01252                 continue;
    01253                 /*@notreached@*/ /*@switchbreak@*/ break;
    01254         case '{':               /* %{...}/%{...:...} substitution */
    01255                 if ((se = matchchar(s, c, '}')) == NULL) {
    01256                         rpmError(RPMERR_BADSPEC,
    01257                                 _("Unterminated %c: %s\n"), (char)c, s);
    01258                         rc = 1;
    01259                         continue;
    01260                 }
    01261                 f = s+1;/* skip { */
    01262                 se++;   /* skip } */
    01263                 while (strchr("!?", *f) != NULL) {
    01264                         switch(*f++) {
    01265                         case '!':
    01266                                 negate = ((negate + 1) % 2);
    01267                                 /*@switchbreak@*/ break;
    01268                         case '?':
    01269                                 chkexist++;
    01270                                 /*@switchbreak@*/ break;
    01271                         }
    01272                 }
    01273                 for (fe = f; (c = *fe) && !strchr(" :}", c);)
    01274                         fe++;
    01275                 switch (c) {
    01276                 case ':':
    01277                         g = fe + 1;
    01278                         ge = se - 1;
    01279                         /*@innerbreak@*/ break;
    01280                 case ' ':
    01281                         grab = se[-1];
    01282                         /*@innerbreak@*/ break;
    01283                 default:
    01284                         /*@innerbreak@*/ break;
    01285                 }
    01286                 /*@switchbreak@*/ break;
    01287         }
    01288
    01289         /* XXX Everything below expects fe > f */
    01290         fn = (fe - f);
    01291         gn = (ge - g);
    01292         if ((fe - f) <= 0) {
    01293 /* XXX Process % in unknown context */
    01294                 c = '%';        /* XXX only need to save % */
    01295                 SAVECHAR(mb, c);
    01296 #if 0
    01297                 rpmError(RPMERR_BADSPEC,
    01298                         _("A %% is followed by an unparseable macro\n"));
    01299 #endif
    01300                 s = se;
    01301                 continue;
    01302         }
    01303
    01304         if (mb->macro_trace)
    01305                 printMacro(mb, s, se);
    01306
    01307         /* Expand builtin macros */
    01308         if (STREQ("global", f, fn)) {
    01309                 s = doDefine(mb, se, RMIL_GLOBAL, 1);
    01310                 continue;
    01311         }
    01312         if (STREQ("define", f, fn)) {
    01313                 s = doDefine(mb, se, mb->depth, 0);
    01314                 continue;
    01315         }
    01316         if (STREQ("undefine", f, fn)) {
    01317                 s = doUndefine(mb->mc, se);
    01318                 continue;
    01319         }
    01320
    01321         if (STREQ("echo", f, fn) ||
    01322             STREQ("warn", f, fn) ||
    01323             STREQ("error", f, fn)) {
    01324                 int waserror = 0;
    01325                 if (STREQ("error", f, fn))
    01326                         waserror = 1;
    01327                 if (g < ge)
    01328                         doOutput(mb, waserror, g, gn);
    01329                 else
    01330                         doOutput(mb, waserror, f, fn);
    01331                 s = se;
    01332                 continue;
    01333         }
    01334
    01335         if (STREQ("trace", f, fn)) {
    01336                 /* XXX TODO restore expand_trace/macro_trace to 0 on return */
    01337                 mb->expand_trace = mb->macro_trace = (negate ? 0 : mb->depth);
    01338                 if (mb->depth == 1) {
    01339                         print_macro_trace = mb->macro_trace;
    01340                         print_expand_trace = mb->expand_trace;
    01341                 }
    01342                 s = se;
    01343                 continue;
    01344         }
    01345
    01346         if (STREQ("dump", f, fn)) {
    01347                 rpmDumpMacroTable(mb->mc, NULL);
    01348                 while (iseol(*se))
    01349                         se++;
    01350                 s = se;
    01351                 continue;
    01352         }
    01353
    01354         /* XXX necessary but clunky */
    01355         if (STREQ("basename", f, fn) ||
    01356             STREQ("suffix", f, fn) ||
    01357             STREQ("expand", f, fn) ||
    01358             STREQ("verbose", f, fn) ||
    01359             STREQ("uncompress", f, fn) ||
    01360             STREQ("url2path", f, fn) ||
    01361             STREQ("u2p", f, fn) ||
    01362             STREQ("S", f, fn) ||
    01363             STREQ("P", f, fn) ||
    01364             STREQ("F", f, fn)) {
    01365                 /*@-internalglobs@*/ /* FIX: verbose may be set */
    01366                 doFoo(mb, negate, f, fn, g, gn);
    01367                 /*@=internalglobs@*/
    01368                 s = se;
    01369                 continue;
    01370         }
    01371
    01372         /* Expand defined macros */
    01373         mep = findEntry(mb->mc, f, fn);
    01374         me = (mep ? *mep : NULL);
    01375
    01376         /* XXX Special processing for flags */
    01377         if (*f == '-') {
    01378                 if (me)
    01379                         me->used++;     /* Mark macro as used */
    01380                 if ((me == NULL && !negate) ||  /* Without -f, skip %{-f...} */
    01381                     (me != NULL && negate)) {   /* With -f, skip %{!-f...} */
    01382                         s = se;
    01383                         continue;
    01384                 }
    01385
    01386                 if (g && g < ge) {              /* Expand X in %{-f:X} */
    01387                         rc = expandT(mb, g, gn);
    01388                 } else
    01389                 if (me && me->body && *me->body) {/* Expand %{-f}/%{-f*} */
    01390                         rc = expandT(mb, me->body, strlen(me->body));
    01391                 }
    01392                 s = se;
    01393                 continue;
    01394         }
    01395
    01396         /* XXX Special processing for macro existence */
    01397         if (chkexist) {
    01398                 if ((me == NULL && !negate) ||  /* Without -f, skip %{?f...} */
    01399                     (me != NULL && negate)) {   /* With -f, skip %{!?f...} */
    01400                         s = se;
    01401                         continue;
    01402                 }
    01403                 if (g && g < ge) {              /* Expand X in %{?f:X} */
    01404                         rc = expandT(mb, g, gn);
    01405                 } else
    01406                 if (me && me->body && *me->body) { /* Expand %{?f}/%{?f*} */
    01407                         rc = expandT(mb, me->body, strlen(me->body));
    01408                 }
    01409                 s = se;
    01410                 continue;
    01411         }
    01412
    01413         if (me == NULL) {       /* leave unknown %... as is */
    01414 #ifndef HACK
    01415 #if DEAD
    01416                 /* XXX hack to skip over empty arg list */
    01417                 if (fn == 1 && *f == '*') {
    01418                         s = se;
    01419                         continue;
    01420                 }
    01421 #endif
    01422                 /* XXX hack to permit non-overloaded %foo to be passed */
    01423                 c = '%';        /* XXX only need to save % */
    01424                 SAVECHAR(mb, c);
    01425 #else
    01426                 rpmError(RPMERR_BADSPEC,
    01427                         _("Macro %%%.*s not found, skipping\n"), fn, f);
    01428                 s = se;
    01429 #endif
    01430                 continue;
    01431         }
    01432
    01433         /* Setup args for "%name " macros with opts */
    01434         if (me && me->opts != NULL) {
    01435                 if (grab != '\0') {
    01436                         se = grabArgs(mb, me, fe, grab);
    01437                 } else {
    01438                         addMacro(mb->mc, "**", NULL, "", mb->depth);
    01439                         addMacro(mb->mc, "*", NULL, "", mb->depth);
    01440                         addMacro(mb->mc, "#", NULL, "0", mb->depth);
    01441                         addMacro(mb->mc, "0", NULL, me->name, mb->depth);
    01442                 }
    01443         }
    01444
    01445         /* Recursively expand body of macro */
    01446         if (me->body && *me->body) {
    01447                 mb->s = me->body;
    01448                 rc = expandMacro(mb);
    01449                 if (rc == 0)
    01450                         me->used++;     /* Mark macro as used */
    01451         }
    01452
    01453         /* Free args for "%name " macros with opts */
    01454         if (me->opts != NULL)
    01455                 freeArgs(mb);
    01456
    01457         s = se;
    01458     }
    01459 /*@=branchstate@*/
    01460
    01461     *mb->t = '\0';
    01462     mb->s = s;
    01463     mb->depth--;
    01464     if (rc != 0 || mb->expand_trace)
    01465         printExpansion(mb, t, mb->t);
    01466     return rc;
    01467 }
    01468
    01469 /* =============================================================== */
    01470
    01471 int
    01472 expandMacros(void * spec, MacroContext mc, char * sbuf, size_t slen)
    01473 {
    01474     MacroBuf mb = alloca(sizeof(*mb));
    01475     char *tbuf;
    01476     int rc;
    01477
    01478     if (sbuf == NULL || slen == 0)
    01479         return 0;
    01480     if (mc == NULL) mc = rpmGlobalMacroContext;
    01481
    01482     tbuf = alloca(slen + 1);
    01483     memset(tbuf, 0, (slen + 1));
    01484
    01485     mb->s = sbuf;
    01486     mb->t = tbuf;
    01487     mb->nb = slen;
    01488     mb->depth = 0;
    01489     mb->macro_trace = print_macro_trace;
    01490     mb->expand_trace = print_expand_trace;
    01491
    01492     mb->spec = spec;    /* (future) %file expansion info */
    01493     mb->mc = mc;
    01494
    01495     rc = expandMacro(mb);
    01496
    01497     if (mb->nb == 0)
    01498         rpmError(RPMERR_BADSPEC, _("Target buffer overflow\n"));
    01499
    01500     tbuf[slen] = '\0';  /* XXX just in case */
    01501     strncpy(sbuf, tbuf, (slen - mb->nb + 1));
    01502
    01503     return rc;
    01504 }
    01505
    01506 void
    01507 addMacro(MacroContext mc,
    01508         const char * n, const char * o, const char * b, int level)
    01509 {
    01510     MacroEntry * mep;
    01511
    01512     if (mc == NULL) mc = rpmGlobalMacroContext;
    01513
    01514     /* If new name, expand macro table */
    01515     if ((mep = findEntry(mc, n, 0)) == NULL) {
    01516         if (mc->firstFree == mc->macrosAllocated)
    01517             expandMacroTable(mc);
    01518         if (mc->macroTable != NULL)
    01519             mep = mc->macroTable + mc->firstFree++;
    01520     }
    01521
    01522     if (mep != NULL) {
    01523         /* Push macro over previous definition */
    01524         pushMacro(mep, n, o, b, level);
    01525
    01526         /* If new name, sort macro table */
    01527         if ((*mep)->prev == NULL)
    01528             sortMacroTable(mc);
    01529     }
    01530 }
    01531
    01532 void
    01533 delMacro(MacroContext mc, const char * n)
    01534 {
    01535     MacroEntry * mep;
    01536
    01537     if (mc == NULL) mc = rpmGlobalMacroContext;
    01538     /* If name exists, pop entry */
    01539     if ((mep = findEntry(mc, n, 0)) != NULL) {
    01540         popMacro(mep);
    01541         /* If deleted name, sort macro table */
    01542         if (!(mep && *mep))
    01543             sortMacroTable(mc);
    01544     }
    01545 }
    01546
    01547 /*@-mustmod@*/ /* LCL: mc is modified through mb->mc, mb is abstract */
    01548 int
    01549 rpmDefineMacro(MacroContext mc, const char * macro, int level)
    01550 {
    01551     MacroBuf mb = alloca(sizeof(*mb));
    01552
    01553     memset(mb, 0, sizeof(*mb));
    01554     /* XXX just enough to get by */
    01555     mb->mc = (mc ? mc : rpmGlobalMacroContext);
    01556     (void) doDefine(mb, macro, level, 0);
    01557     return 0;
    01558 }
    01559 /*@=mustmod@*/
    01560
    01561 void
    01562 rpmLoadMacros(MacroContext mc, int level)
    01563 {
    01564
    01565     if (mc == NULL || mc == rpmGlobalMacroContext)
    01566         return;
    01567
    01568     if (mc->macroTable != NULL) {
    01569         int i;
    01570         for (i = 0; i < mc->firstFree; i++) {
    01571             MacroEntry *mep, me;
    01572             mep = &mc->macroTable[i];
    01573             me = *mep;
    01574
    01575             if (me == NULL)             /* XXX this should never happen */
    01576                 continue;
    01577             addMacro(NULL, me->name, me->opts, me->body, (level - 1));
    01578         }
    01579     }
    01580 }
    01581
    01582 void
    01583 rpmInitMacros(/*@unused@*/ MacroContext mc, const char *macrofiles)
    01584 {
    01585     char *m, *mfile, *me;
    01586
    01587     if (macrofiles == NULL)
    01588         return;
    01589 #ifdef  DYING
    01590     if (mc == NULL) mc = rpmGlobalMacroContext;
    01591 #endif
    01592
    01593     for (mfile = m = xstrdup(macrofiles); mfile && *mfile != '\0'; mfile = me) {
    01594         FD_t fd;
    01595         char buf[BUFSIZ];
    01596
    01597         for (me = mfile; (me = strchr(me, ':')) != NULL; me++) {
    01598             if (!(me[1] == '/' && me[2] == '/'))
    01599                 /*@innerbreak@*/ break;
    01600         }
    01601
    01602         if (me && *me == ':')
    01603             *me++ = '\0';
    01604         else
    01605             me = mfile + strlen(mfile);
    01606
    01607         /* Expand ~/ to $HOME */
    01608         buf[0] = '\0';
    01609         if (mfile[0] == '~' && mfile[1] == '/') {
    01610             char *home;
    01611             if ((home = getenv("HOME")) != NULL) {
    01612                 mfile += 2;
    01613                 strncpy(buf, home, sizeof(buf));
    01614                 strncat(buf, "/", sizeof(buf) - strlen(buf));
    01615             }
    01616         }
    01617         strncat(buf, mfile, sizeof(buf) - strlen(buf));
    01618         buf[sizeof(buf)-1] = '\0';
    01619
    01620         fd = Fopen(buf, "r.fpio");
    01621         if (fd == NULL || Ferror(fd)) {
    01622             if (fd) (void) Fclose(fd);
    01623             continue;
    01624         }
    01625
    01626         /* XXX Assume new fangled macro expansion */
    01627         /*@-mods@*/
    01628         max_macro_depth = 16;
    01629         /*@=mods@*/
    01630
    01631         while(rdcl(buf, sizeof(buf), fd, 1) != NULL) {
    01632             char c, *n;
    01633
    01634             n = buf;
    01635             SKIPBLANK(n, c);
    01636
    01637             if (c != '%')
    01638                 /*@innercontinue@*/ continue;
    01639             n++;        /* skip % */
    01640             (void) rpmDefineMacro(NULL, n, RMIL_MACROFILES);
    01641         }
    01642         (void) Fclose(fd);
    01643     }
    01644     m = _free(m);
    01645
    01646     /* Reload cmdline macros */
    01647     /*@-mods@*/
    01648     rpmLoadMacros(rpmCLIMacroContext, RMIL_CMDLINE);
    01649     /*@=mods@*/
    01650 }
    01651
    01652 /*@-globstate@*/
    01653 void
    01654 rpmFreeMacros(MacroContext mc)
    01655 {
    01656
    01657     if (mc == NULL) mc = rpmGlobalMacroContext;
    01658
    01659     if (mc->macroTable != NULL) {
    01660         int i;
    01661         for (i = 0; i < mc->firstFree; i++) {
    01662             MacroEntry me;
    01663             while ((me = mc->macroTable[i]) != NULL) {
    01664                 /* XXX cast to workaround const */
    01665                 /*@-onlytrans@*/
    01666                 if ((mc->macroTable[i] = me->prev) == NULL)
    01667                     me->name = _free(me->name);
    01668                 /*@=onlytrans@*/
    01669                 me->opts = _free(me->opts);
    01670                 me->body = _free(me->body);
    01671                 me = _free(me);
    01672             }
    01673         }
    01674         mc->macroTable = _free(mc->macroTable);
    01675     }
    01676     memset(mc, 0, sizeof(*mc));
    01677 }
    01678 /*@=globstate@*/
    01679
    01680 /* =============================================================== */
    01681 int isCompressed(const char * file, rpmCompressedMagic * compressed)
    01682 {
    01683     FD_t fd;
    01684     ssize_t nb;
    01685     int rc = -1;
    01686     unsigned char magic[4];
    01687
    01688     *compressed = COMPRESSED_NOT;
    01689
    01690     fd = Fopen(file, "r.ufdio");
    01691     if (fd == NULL || Ferror(fd)) {
    01692         /* XXX Fstrerror */
    01693         rpmError(RPMERR_BADSPEC, _("File %s: %s\n"), file, Fstrerror(fd));
    01694         if (fd) (void) Fclose(fd);
    01695         return 1;
    01696     }
    01697     nb = Fread(magic, sizeof(magic[0]), sizeof(magic), fd);
    01698     if (nb < 0) {
    01699         rpmError(RPMERR_BADSPEC, _("File %s: %s\n"), file, Fstrerror(fd));
    01700         rc = 1;
    01701     } else if (nb < sizeof(magic)) {
    01702         rpmError(RPMERR_BADSPEC, _("File %s is smaller than %u bytes\n"),
    01703                 file, (unsigned)sizeof(magic));
    01704         rc = 0;
    01705     }
    01706     (void) Fclose(fd);
    01707     if (rc >= 0)
    01708         return rc;
    01709
    01710     rc = 0;
    01711
    01712     if ((magic[0] == 'B') && (magic[1] == 'Z')) {
    01713         *compressed = COMPRESSED_BZIP2;
    01714     } else if ((magic[0] == 0120) && (magic[1] == 0113) &&
    01715          (magic[2] == 0003) && (magic[3] == 0004)) {    /* pkzip */
    01716         *compressed = COMPRESSED_ZIP;
    01717     } else if (((magic[0] == 0037) && (magic[1] == 0213)) || /* gzip */
    01718         ((magic[0] == 0037) && (magic[1] == 0236)) ||   /* old gzip */
    01719         ((magic[0] == 0037) && (magic[1] == 0036)) ||   /* pack */
    01720         ((magic[0] == 0037) && (magic[1] == 0240)) ||   /* SCO lzh */
    01721         ((magic[0] == 0037) && (magic[1] == 0235))      /* compress */
    01722         ) {
    01723         *compressed = COMPRESSED_OTHER;
    01724     }
    01725
    01726     return rc;
    01727 }
    01728
    01729 /* =============================================================== */
    01730
    01731 /*@-modfilesys@*/
    01732 char *
    01733 rpmExpand(const char *arg, ...)
    01734 {
    01735     char buf[BUFSIZ], *p, *pe;
    01736     const char *s;
    01737     va_list ap;
    01738
    01739     if (arg == NULL)
    01740         return xstrdup("");
    01741
    01742     buf[0] = '\0';
    01743     p = buf;
    01744     pe = stpcpy(p, arg);
    01745
    01746     va_start(ap, arg);
    01747     while ((s = va_arg(ap, const char *)) != NULL)
    01748         pe = stpcpy(pe, s);
    01749     va_end(ap);
    01750     (void) expandMacros(NULL, NULL, buf, sizeof(buf));
    01751     return xstrdup(buf);
    01752 }
    01753 /*@=modfilesys@*/
    01754
    01755 int
    01756 rpmExpandNumeric(const char *arg)
    01757 {
    01758     const char *val;
    01759     int rc;
    01760
    01761     if (arg == NULL)
    01762         return 0;
    01763
    01764     val = rpmExpand(arg, NULL);
    01765     if (!(val && *val != '%'))
    01766         rc = 0;
    01767     else if (*val == 'Y' || *val == 'y')
    01768         rc = 1;
    01769     else if (*val == 'N' || *val == 'n')
    01770         rc = 0;
    01771     else {
    01772         char *end;
    01773         rc = strtol(val, &end, 0);
    01774         if (!(end && *end == '\0'))
    01775             rc = 0;
    01776     }
    01777     val = _free(val);
    01778
    01779     return rc;
    01780 }
    01781
    01782 /* @todo "../sbin/./../bin/" not correct. */
    01783 char *rpmCleanPath(char * path)
    01784 {
    01785     const char *s;
    01786     char *se, *t, *te;
    01787     int begin = 1;
    01788
    01789     if (path == NULL)
    01790         return NULL;
    01791
    01792 /*fprintf(stderr, "*** RCP %s ->\n", path); */
    01793     s = t = te = path;
    01794     while (*s != '\0') {
    01795 /*fprintf(stderr, "*** got \"%.*s\"\trest \"%s\"\n", (t-path), path, s); */
    01796         switch(*s) {
    01797         case ':':                       /* handle url's */
    01798             if (s[1] == '/' && s[2] == '/') {
    01799                 *t++ = *s++;
    01800                 *t++ = *s++;
    01801                 /*@switchbreak@*/ break;
    01802             }
    01803             begin=1;
    01804             /*@switchbreak@*/ break;
    01805         case '/':
    01806             /* Move parent dir forward */
    01807             for (se = te + 1; se < t && *se != '/'; se++)
    01808                 {};
    01809             if (se < t && *se == '/') {
    01810                 te = se;
    01811 /*fprintf(stderr, "*** next pdir \"%.*s\"\n", (te-path), path); */
    01812             }
    01813             while (s[1] == '/')
    01814                 s++;
    01815             while (t > path && t[-1] == '/')
    01816                 t--;
    01817             /*@switchbreak@*/ break;
    01818         case '.':
    01819             /* Leading .. is special */
    01820             /* Check that it is ../, so that we don't interpret */
    01821             /* ..?(i.e. "...") or ..* (i.e. "..bogus") as "..". */
    01822             /* in the case of "...", this ends up being processed*/
    01823             /* as "../.", and the last '.' is stripped.  This   */
    01824             /* would not be correct processing.                 */
    01825             if (begin && s[1] == '.' && (s[2] == '/' || s[2] == '\0')) {
    01826 /*fprintf(stderr, "    leading \"..\"\n"); */
    01827                 *t++ = *s++;
    01828                 /*@switchbreak@*/ break;
    01829             }
    01830             /* Single . is special */
    01831             if (begin && s[1] == '\0') {
    01832                 /*@switchbreak@*/ break;
    01833             }
    01834             /* Trim embedded ./ , trailing /. */
    01835             if ((t[-1] == '/' && s[1] == '\0') || (t != path && s[1] == '/')) {
    01836                 s++;
    01837                 continue;
    01838             }
    01839             /* Trim embedded /../ and trailing /.. */
    01840             if (!begin && t > path && t[-1] == '/' && s[1] == '.' && (s[2] == '/' || s[2] == '\0')) {
    01841                 t = te;
    01842                 /* Move parent dir forward */
    01843                 if (te > path)
    01844                     for (--te; te > path && *te != '/'; te--)
    01845                         {};
    01846 /*fprintf(stderr, "*** prev pdir \"%.*s\"\n", (te-path), path); */
    01847                 s++;
    01848                 s++;
    01849                 continue;
    01850             }
    01851             /*@switchbreak@*/ break;
    01852         default:
    01853             begin = 0;
    01854             /*@switchbreak@*/ break;
    01855         }
    01856         *t++ = *s++;
    01857     }
    01858
    01859     /* Trim trailing / (but leave single / alone) */
    01860     if (t > &path[1] && t[-1] == '/')
    01861         t--;
    01862     *t = '\0';
    01863
    01864 /*fprintf(stderr, "\t%s\n", path); */
    01865     return path;
    01866 }
    01867
    01868 /* Return concatenated and expanded canonical path. */
    01869
    01870 const char *
    01871 rpmGetPath(const char *path, ...)
    01872 {
    01873     char buf[BUFSIZ];
    01874     const char * s;
    01875     char * t, * te;
    01876     va_list ap;
    01877
    01878     if (path == NULL)
    01879         return xstrdup("");
    01880
    01881     buf[0] = '\0';
    01882     t = buf;
    01883     te = stpcpy(t, path);
    01884     *te = '\0';
    01885
    01886     va_start(ap, path);
    01887     while ((s = va_arg(ap, const char *)) != NULL) {
    01888         te = stpcpy(te, s);
    01889         *te = '\0';
    01890     }
    01891     va_end(ap);
    01892 /*@-modfilesys@*/
    01893     (void) expandMacros(NULL, NULL, buf, sizeof(buf));
    01894 /*@=modfilesys@*/
    01895
    01896     (void) rpmCleanPath(buf);
    01897     return xstrdup(buf);        /* XXX xstrdup has side effects. */
    01898 }
    01899
    01900 /* Merge 3 args into path, any or all of which may be a url. */
    01901
    01902 const char * rpmGenPath(const char * urlroot, const char * urlmdir,
    01903                 const char *urlfile)
    01904 {
    01905 /*@owned@*/ const char * xroot = rpmGetPath(urlroot, NULL);
    01906 /*@dependent@*/ const char * root = xroot;
    01907 /*@owned@*/ const char * xmdir = rpmGetPath(urlmdir, NULL);
    01908 /*@dependent@*/ const char * mdir = xmdir;
    01909 /*@owned@*/ const char * xfile = rpmGetPath(urlfile, NULL);
    01910 /*@dependent@*/ const char * file = xfile;
    01911     const char * result;
    01912     const char * url = NULL;
    01913     int nurl = 0;
    01914     int ut;
    01915
    01916 #if 0
    01917 if (_debug) fprintf(stderr, "*** RGP xroot %s xmdir %s xfile %s\n", xroot, xmdir, xfile);
    01918 #endif
    01919     ut = urlPath(xroot, &root);
    01920     if (url == NULL && ut > URL_IS_DASH) {
    01921         url = xroot;
    01922         nurl = root - xroot;
    01923 #if 0
    01924 if (_debug) fprintf(stderr, "*** RGP ut %d root %s nurl %d\n", ut, root, nurl);
    01925 #endif
    01926     }
    01927     if (root == NULL || *root == '\0') root = "/";
    01928
    01929     ut = urlPath(xmdir, &mdir);
    01930     if (url == NULL && ut > URL_IS_DASH) {
    01931         url = xmdir;
    01932         nurl = mdir - xmdir;
    01933 #if 0
    01934 if (_debug) fprintf(stderr, "*** RGP ut %d mdir %s nurl %d\n", ut, mdir, nurl);
    01935 #endif
    01936     }
    01937     if (mdir == NULL || *mdir == '\0') mdir = "/";
    01938
    01939     ut = urlPath(xfile, &file);
    01940     if (url == NULL && ut > URL_IS_DASH) {
    01941         url = xfile;
    01942         nurl = file - xfile;
    01943 #if 0
    01944 if (_debug) fprintf(stderr, "*** RGP ut %d file %s nurl %d\n", ut, file, nurl);
    01945 #endif
    01946     }
    01947
    01948 /*@-branchstate@*/
    01949     if (url && nurl > 0) {
    01950         char *t = strncpy(alloca(nurl+1), url, nurl);
    01951         t[nurl] = '\0';
    01952         url = t;
    01953     } else
    01954         url = "";
    01955 /*@=branchstate@*/
    01956
    01957     result = rpmGetPath(url, root, "/", mdir, "/", file, NULL);
    01958
    01959     xroot = _free(xroot);
    01960     xmdir = _free(xmdir);
    01961     xfile = _free(xfile);
    01962 #if 0
    01963 if (_debug) fprintf(stderr, "*** RGP result %s\n", result);
    01964 #endif
    01965     return result;
    01966 }
    01967
    01968 /* =============================================================== */
    01969
    01970 #if defined(DEBUG_MACROS)
    01971
    01972 #if defined(EVAL_MACROS)
    01973
    01974 char *macrofiles = "/usr/lib/rpm/macros:/etc/rpm/macros:~/.rpmmacros";
    01975
    01976 int
    01977 main(int argc, char *argv[])
    01978 {
    01979     int c;
    01980     int errflg = 0;
    01981     extern char *optarg;
    01982     extern int optind;
    01983
    01984     while ((c = getopt(argc, argv, "f:")) != EOF ) {
    01985         switch (c) {
    01986         case 'f':
    01987             macrofiles = optarg;
    01988             break;
    01989         case '?':
    01990         default:
    01991             errflg++;
    01992             break;
    01993         }
    01994     }
    01995     if (errflg || optind >= argc) {
    01996         fprintf(stderr, "Usage: %s [-f macropath ] macro ...\n", argv[0]);
    01997         exit(1);
    01998     }
    01999
    02000     rpmInitMacros(NULL, macrofiles);
    02001     for ( ; optind < argc; optind++) {
    02002         const char *val;
    02003
    02004         val = rpmGetPath(argv[optind], NULL);
    02005         if (val) {
    02006             fprintf(stdout, "%s:\t%s\n", argv[optind], val);
    02007             val = _free(val);
    02008         }
    02009     }
    02010     rpmFreeMacros(NULL);
    02011     return 0;
    02012 }
    02013
    02014 #else   /* !EVAL_MACROS */
    02015
    02016 char *macrofiles = "../macros:./testmacros";
    02017 char *testfile = "./test";
    02018
    02019 int
    02020 main(int argc, char *argv[])
    02021 {
    02022     char buf[BUFSIZ];
    02023     FILE *fp;
    02024     int x;
    02025
    02026     rpmInitMacros(NULL, macrofiles);
    02027     rpmDumpMacroTable(NULL, NULL);
    02028
    02029     if ((fp = fopen(testfile, "r")) != NULL) {
    02030         while(rdcl(buf, sizeof(buf), fp, 1)) {
    02031             x = expandMacros(NULL, NULL, buf, sizeof(buf));
    02032             fprintf(stderr, "%d->%s\n", x, buf);
    02033             memset(buf, 0, sizeof(buf));
    02034         }
    02035         fclose(fp);
    02036     }
    02037
    02038     while(rdcl(buf, sizeof(buf), stdin, 1)) {
    02039         x = expandMacros(NULL, NULL, buf, sizeof(buf));
    02040         fprintf(stderr, "%d->%s\n <-\n", x, buf);
    02041         memset(buf, 0, sizeof(buf));
    02042     }
    02043     rpmFreeMacros(NULL);
    02044
    02045     return 0;
    02046 }
    02047 #endif  /* EVAL_MACROS */
    02048 #endif  /* DEBUG_MACROS */
    02049 /*@=boundsread@*/