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

rpmio/macro.c

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

Generated on Mon Aug 23 10:33:20 2010 for rpm by  doxygen 1.4.4