• Main Page
  • Related Pages
  • Modules
  • Data Structures
  • Files
  • File List
  • Globals

rpmio/macro.c

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

Generated on Mon Nov 29 2010 05:18:47 for rpm by  doxygen 1.7.2