# rpmio/macro.c

Go to the documentation of this file.

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