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 
00810 void delMacroAll(MacroContext mc, const char * n);
00817 /*@dependent@*/ static const char *
00818 doUnglobal(MacroContext mc, /*@returned@*/ const char * se)
00819         /*@globals rpmGlobalMacroContext @*/
00820         /*@modifies mc, rpmGlobalMacroContext @*/
00821 {
00822     const char *s = se;
00823     char *buf = alloca(_macro_BUFSIZ);
00824     char *n = buf, *ne = n;
00825     int c;
00826 
00827     COPYNAME(ne, s, c);
00828 
00829     /* Move scan over body */
00830     while (iseol(*s))
00831         s++;
00832     se = s;
00833 
00834     /* Names must start with alphabetic or _ and be at least 3 chars */
00835     if (!((c = *n) && (xisalpha(c) || c == '_') && (ne - n) > 2)) {
00836         rpmError(RPMERR_BADSPEC,
00837                 _("Macro %%%s has illegal name (%%unglobal)\n"), n);
00838         return se;
00839     }
00840 
00841     delMacroAll(mc, n);
00842 
00843     return se;
00844 }
00845 
00846 #ifdef  DYING
00847 static void
00848 dumpME(const char * msg, MacroEntry me)
00849         /*@globals fileSystem @*/
00850         /*@modifies fileSystem @*/
00851 {
00852     if (msg)
00853         fprintf(stderr, "%s", msg);
00854     fprintf(stderr, "\tme %p", me);
00855     if (me)
00856         fprintf(stderr,"\tname %p(%s) prev %p",
00857                 me->name, me->name, me->prev);
00858     fprintf(stderr, "\n");
00859 }
00860 #endif
00861 
00870 static void
00871 pushMacro(/*@out@*/ MacroEntry * mep, const char * n, /*@null@*/ const char * o,
00872                 /*@null@*/ const char * b, int level)
00873         /*@modifies *mep @*/
00874 {
00875     MacroEntry prev = (mep && *mep ? *mep : NULL);
00876     MacroEntry me = (MacroEntry) xmalloc(sizeof(*me));
00877     const char *name = n;
00878 
00879     if (*name == '.')           /* XXX readonly macros */
00880         name++;
00881     if (*name == '.')           /* XXX readonly macros */
00882         name++;
00883 
00884     /*@-assignexpose@*/
00885     me->prev = prev;
00886     /*@=assignexpose@*/
00887     me->name = (prev ? prev->name : xstrdup(name));
00888     me->opts = (o ? xstrdup(o) : NULL);
00889     me->body = xstrdup(b ? b : "");
00890     me->used = 0;
00891     me->level = level;
00892     me->flags = (name != n);
00893 /*@-boundswrite@*/
00894 /*@-branchstate@*/
00895     if (mep)
00896         *mep = me;
00897     else
00898         me = _free(me);
00899 /*@=branchstate@*/
00900 /*@=boundswrite@*/
00901 }
00902 
00907 static void
00908 popMacro(MacroEntry * mep)
00909         /*@modifies *mep @*/
00910 {
00911         MacroEntry me = (*mep ? *mep : NULL);
00912 
00913 /*@-branchstate@*/
00914         if (me) {
00915                 /* XXX cast to workaround const */
00916                 /*@-onlytrans@*/
00917 /*@-boundswrite@*/
00918                 if ((*mep = me->prev) == NULL)
00919                         me->name = _free(me->name);
00920 /*@=boundswrite@*/
00921                 me->opts = _free(me->opts);
00922                 me->body = _free(me->body);
00923                 me = _free(me);
00924                 /*@=onlytrans@*/
00925         }
00926 /*@=branchstate@*/
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             rpmError(RPMERR_BADSPEC,
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 /*@-bounds@*/
00985 /*@dependent@*/ static const char *
00986 grabArgs(MacroBuf mb, const MacroEntry me, /*@returned@*/ const char * se,
00987                 const char * lastc)
00988         /*@globals rpmGlobalMacroContext @*/
00989         /*@modifies mb, rpmGlobalMacroContext @*/
00990 {
00991     size_t bufn = _macro_BUFSIZ;
00992     char *buf = alloca(bufn);
00993     char *b, *be;
00994     char aname[16];
00995     const char *opts, *o;
00996     int argc = 0;
00997     const char **argv;
00998     int c;
00999 
01000     /* Copy macro name as argv[0], save beginning of args.  */
01001     buf[0] = '\0';
01002     b = be = stpcpy(buf, me->name);
01003 
01004     addMacro(mb->mc, "0", NULL, buf, mb->depth);
01005     
01006     argc = 1;   /* XXX count argv[0] */
01007 
01008     /* Copy args into buf until lastc */
01009     *be++ = ' ';
01010     while ((c = *se++) != '\0' && (se-1) != lastc) {
01011 /*@-globs@*/
01012         if (!isblank(c)) {
01013             *be++ = c;
01014             continue;
01015         }
01016 /*@=globs@*/
01017         /* c is blank */
01018         if (be[-1] == ' ')
01019             continue;
01020         /* a word has ended */
01021         *be++ = ' ';
01022         argc++;
01023     }
01024     if (c == '\0') se--;        /* one too far */
01025     if (be[-1] != ' ')
01026         argc++, be++;           /* last word has not trailing ' ' */
01027     be[-1] = '\0';
01028     if (*b == ' ') b++;         /* skip the leading ' ' */
01029 
01030 /*
01031  * The macro %* analoguous to the shell's $* means "Pass all non-macro
01032  * parameters." Consequently, there needs to be a macro that means "Pass all
01033  * (including macro parameters) options". This is useful for verifying
01034  * parameters during expansion and yet transparently passing all parameters
01035  * through for higher level processing (e.g. %description and/or %setup).
01036  * This is the (potential) justification for %{**} ...
01037  */
01038     /* Add unexpanded args as macro */
01039     addMacro(mb->mc, "**", NULL, b, mb->depth);
01040 
01041 #ifdef NOTYET
01042     /* XXX if macros can be passed as args ... */
01043     expandU(mb, buf, bufn);
01044 #endif
01045 
01046     /* Build argv array */
01047     argv = (const char **) alloca((argc + 1) * sizeof(*argv));
01048     be[-1] = ' '; /* assert((be - 1) == (b + strlen(b) == buf + strlen(buf))) */
01049     be[0] = '\0';
01050     b = buf;
01051     for (c = 0; c < argc; c++) {
01052         argv[c] = b;
01053         b = strchr(b, ' ');
01054         *b++ = '\0';
01055     }
01056     /* assert(b == be);  */
01057     argv[argc] = NULL;
01058 
01059     /* Citation from glibc/posix/getopt.c:
01060      *    Index in ARGV of the next element to be scanned.
01061      *    This is used for communication to and from the caller
01062      *    and for communication between successive calls to `getopt'.
01063      *
01064      *    On entry to `getopt', zero means this is the first call; initialize.
01065      *
01066      *    When `getopt' returns -1, this is the index of the first of the
01067      *    non-option elements that the caller should itself scan.
01068      *
01069      *    Otherwise, `optind' communicates from one call to the next
01070      *    how much of ARGV has been scanned so far.
01071      */
01072     /* 1003.2 says this must be 1 before any call.  */
01073 
01074 #ifdef __GLIBC__
01075     /*@-mods@*/
01076     optind = 0;         /* XXX but posix != glibc */
01077     /*@=mods@*/
01078 #else
01079     optind = 1;
01080 #endif
01081 
01082     opts = me->opts;
01083 
01084     /* Define option macros. */
01085 /*@-nullstate@*/ /* FIX: argv[] can be NULL */
01086     while((c = getopt(argc, (char **)argv, opts)) != -1)
01087 /*@=nullstate@*/
01088     {
01089         if (c == '?' || (o = strchr(opts, c)) == NULL) {
01090             rpmError(RPMERR_BADSPEC, _("Unknown option %c in %s(%s)\n"),
01091                         (char)c, me->name, opts);
01092             return se;
01093         }
01094         *be++ = '-';
01095         *be++ = c;
01096         if (o[1] == ':') {
01097             *be++ = ' ';
01098             be = stpcpy(be, optarg);
01099         }
01100         *be++ = '\0';
01101         aname[0] = '-'; aname[1] = c; aname[2] = '\0';
01102         addMacro(mb->mc, aname, NULL, b, mb->depth);
01103         if (o[1] == ':') {
01104             aname[0] = '-'; aname[1] = c; aname[2] = '*'; aname[3] = '\0';
01105             addMacro(mb->mc, aname, NULL, optarg, mb->depth);
01106         }
01107         be = b; /* reuse the space */
01108     }
01109 
01110     /* Add arg count as macro. */
01111     sprintf(aname, "%d", (argc - optind));
01112     addMacro(mb->mc, "#", NULL, aname, mb->depth);
01113 
01114     /* Add macro for each arg. Concatenate args for %*. */
01115     if (be) {
01116         *be = '\0';
01117         for (c = optind; c < argc; c++) {
01118             sprintf(aname, "%d", (c - optind + 1));
01119             addMacro(mb->mc, aname, NULL, argv[c], mb->depth);
01120             if (be != b) *be++ = ' '; /* Add space between args */
01121 /*@-nullpass@*/ /* FIX: argv[] can be NULL */
01122             be = stpcpy(be, argv[c]);
01123 /*@=nullpass@*/
01124         }
01125     }
01126 
01127     /* Add unexpanded args as macro. */
01128     addMacro(mb->mc, "*", NULL, b, mb->depth);
01129 
01130     return se;
01131 }
01132 /*@=bounds@*/
01133 
01141 static void
01142 doOutput(MacroBuf mb, int waserror, const char * msg, size_t msglen)
01143         /*@globals rpmGlobalMacroContext, h_errno, fileSystem @*/
01144         /*@modifies mb, rpmGlobalMacroContext, fileSystem @*/
01145 {
01146     size_t bufn = _macro_BUFSIZ + msglen;
01147     char *buf = alloca(bufn);
01148 
01149     strncpy(buf, msg, msglen);
01150     buf[msglen] = '\0';
01151     (void) expandU(mb, buf, bufn);
01152     if (waserror)
01153         rpmError(RPMERR_BADSPEC, "%s\n", buf);
01154     else
01155         fprintf(stderr, "%s", buf);
01156 }
01157 
01167 static void
01168 doFoo(MacroBuf mb, int negate, const char * f, size_t fn,
01169                 /*@null@*/ const char * g, size_t gn)
01170         /*@globals rpmGlobalMacroContext, h_errno, fileSystem, internalState @*/
01171         /*@modifies mb, rpmGlobalMacroContext, fileSystem, internalState @*/
01172 {
01173      size_t bufn = _macro_BUFSIZ + fn + gn;
01174      char * buf = alloca(bufn);
01175      char *b = NULL, *be;
01176     int c;
01177 
01178     buf[0] = '\0';
01179     if (g != NULL) {
01180         strncpy(buf, g, gn);
01181         buf[gn] = '\0';
01182         (void) expandU(mb, buf, bufn);
01183     }
01184 #if defined(NOTYET)     /* XXX change needs parsePrep and macros changes too */
01185     if (fn > 5 && STREQ("patch", f, 5) && xisdigit(f[5])) {
01186         /* Skip leading zeros */
01187         for (c = 5; c < fn-1 && f[c] == '0' && xisdigit(f[c+1]);)
01188             c++;
01189         b = buf;
01190         be = stpncpy( stpcpy(b, "%patch -P "), f+c, fn-c);
01191         *be = '\0';
01192     } else
01193 #endif
01194     if (STREQ("basename", f, fn)) {
01195         if ((b = strrchr(buf, '/')) == NULL)
01196             b = buf;
01197         else
01198             b++;
01199     } else if (STREQ("dirname", f, fn)) {
01200         if ((b = strrchr(buf, '/')) != NULL)
01201             *b = '\0';
01202         b = buf;
01203     } else if (STREQ("suffix", f, fn)) {
01204         if ((b = strrchr(buf, '.')) != NULL)
01205             b++;
01206     } else if (STREQ("expand", f, fn)) {
01207         b = buf;
01208     } else if (STREQ("verbose", f, fn)) {
01209         if (negate)
01210             b = (rpmIsVerbose() ? NULL : buf);
01211         else
01212             b = (rpmIsVerbose() ? buf : NULL);
01213     } else if (STREQ("url2path", f, fn) || STREQ("u2p", f, fn)) {
01214         int ut = urlPath(buf, (const char **)&b);
01215         ut = ut;        /* XXX quiet gcc */
01216 /*@-branchstate@*/
01217         if (*b == '\0') b = "/";
01218 /*@=branchstate@*/
01219     } else if (STREQ("uncompress", f, fn)) {
01220         rpmCompressedMagic compressed = COMPRESSED_OTHER;
01221 /*@-globs@*/
01222         for (b = buf; (c = *b) && isblank(c);)
01223             b++;
01224         for (be = b; (c = *be) && !isblank(c);)
01225             be++;
01226 /*@=globs@*/
01227         *be++ = '\0';
01228         (void) isCompressed(b, &compressed);
01229         switch(compressed) {
01230         default:
01231         case 0: /* COMPRESSED_NOT */
01232             sprintf(be, "%%__cat %s", b);
01233             break;
01234         case 1: /* COMPRESSED_OTHER */
01235             sprintf(be, "%%__gzip -dc %s", b);
01236             break;
01237         case 2: /* COMPRESSED_BZIP2 */
01238             sprintf(be, "%%__bzip2 -dc %s", b);
01239             break;
01240         case 3: /* COMPRESSED_ZIP */
01241             sprintf(be, "%%__unzip -qq %s", b);
01242             break;
01243         case 4: /* COMPRESSED_LZOP */
01244             sprintf(be, "%%__lzop %s", b);
01245             break;
01246         case 5: /* COMPRESSED_LZMA */
01247             sprintf(be, "%%__lzma %s", b);
01248             break;
01249         }
01250         b = be;
01251     } else if (STREQ("S", f, fn)) {
01252         for (b = buf; (c = *b) && xisdigit(c);)
01253             b++;
01254         if (!c) {       /* digit index */
01255             b++;
01256             sprintf(b, "%%SOURCE%s", buf);
01257         } else
01258             b = buf;
01259     } else if (STREQ("P", f, fn)) {
01260         for (b = buf; (c = *b) && xisdigit(c);)
01261             b++;
01262         if (!c) {       /* digit index */
01263             b++;
01264             sprintf(b, "%%PATCH%s", buf);
01265         } else
01266             b = buf;
01267     } else if (STREQ("F", f, fn)) {
01268         b = buf + strlen(buf) + 1;
01269         sprintf(b, "file%s.file", buf);
01270     }
01271 
01272     if (b) {
01273         (void) expandT(mb, b, strlen(b));
01274     }
01275 }
01276 
01283 static int
01284 expandMacro(MacroBuf mb)
01285         /*@globals rpmGlobalMacroContext,
01286                 print_macro_trace, print_expand_trace, h_errno, fileSystem @*/
01287         /*@modifies mb, rpmGlobalMacroContext,
01288                 print_macro_trace, print_expand_trace, fileSystem @*/
01289 {
01290     MacroEntry *mep;
01291     MacroEntry me;
01292     const char *s = mb->s, *se;
01293     const char *f, *fe;
01294     const char *g, *ge;
01295     size_t fn, gn;
01296     char *t = mb->t;    /* save expansion pointer for printExpand */
01297     int c;
01298     int rc = 0;
01299     int negate;
01300     const char * lastc;
01301     int chkexist;
01302 
01303     if (++mb->depth > max_macro_depth) {
01304         rpmError(RPMERR_BADSPEC,
01305                 _("Recursion depth(%d) greater than max(%d)\n"),
01306                 mb->depth, max_macro_depth);
01307         mb->depth--;
01308         mb->expand_trace = 1;
01309         return 1;
01310     }
01311 
01312 /*@-branchstate@*/
01313     while (rc == 0 && mb->nb > 0 && (c = *s) != '\0') {
01314         s++;
01315         /* Copy text until next macro */
01316         switch(c) {
01317         case '%':
01318                 if (*s) {       /* Ensure not end-of-string. */
01319                     if (*s != '%')
01320                         /*@switchbreak@*/ break;
01321                     s++;        /* skip first % in %% */
01322                 }
01323                 /*@fallthrough@*/
01324         default:
01325                 SAVECHAR(mb, c);
01326                 continue;
01327                 /*@notreached@*/ /*@switchbreak@*/ break;
01328         }
01329 
01330         /* Expand next macro */
01331         f = fe = NULL;
01332         g = ge = NULL;
01333         if (mb->depth > 1)      /* XXX full expansion for outermost level */
01334                 t = mb->t;      /* save expansion pointer for printExpand */
01335         negate = 0;
01336         lastc = NULL;
01337         chkexist = 0;
01338         switch ((c = *s)) {
01339         default:                /* %name substitution */
01340                 while (*s != '\0' && strchr("!?", *s) != NULL) {
01341                         switch(*s++) {
01342                         case '!':
01343                                 negate = ((negate + 1) % 2);
01344                                 /*@switchbreak@*/ break;
01345                         case '?':
01346                                 chkexist++;
01347                                 /*@switchbreak@*/ break;
01348                         }
01349                 }
01350                 f = se = s;
01351                 if (*se == '-')
01352                         se++;
01353                 while((c = *se) && (xisalnum(c) || c == '_'))
01354                         se++;
01355                 /* Recognize non-alnum macros too */
01356                 switch (*se) {
01357                 case '*':
01358                         se++;
01359                         if (*se == '*') se++;
01360                         /*@innerbreak@*/ break;
01361                 case '#':
01362                         se++;
01363                         /*@innerbreak@*/ break;
01364                 default:
01365                         /*@innerbreak@*/ break;
01366                 }
01367                 fe = se;
01368                 /* For "%name " macros ... */
01369 /*@-globs@*/
01370                 if ((c = *fe) && isblank(c))
01371                         if ((lastc = strchr(fe,'\n')) == NULL)
01372                                 lastc = strchr(fe, '\0');
01373 /*@=globs@*/
01374                 /*@switchbreak@*/ break;
01375         case '(':               /* %(...) shell escape */
01376                 if ((se = matchchar(s, c, ')')) == NULL) {
01377                         rpmError(RPMERR_BADSPEC,
01378                                 _("Unterminated %c: %s\n"), (char)c, s);
01379                         rc = 1;
01380                         continue;
01381                 }
01382                 if (mb->macro_trace)
01383                         printMacro(mb, s, se+1);
01384 
01385                 s++;    /* skip ( */
01386                 rc = doShellEscape(mb, s, (se - s));
01387                 se++;   /* skip ) */
01388 
01389                 s = se;
01390                 continue;
01391                 /*@notreached@*/ /*@switchbreak@*/ break;
01392         case '{':               /* %{...}/%{...:...} substitution */
01393                 if ((se = matchchar(s, c, '}')) == NULL) {
01394                         rpmError(RPMERR_BADSPEC,
01395                                 _("Unterminated %c: %s\n"), (char)c, s);
01396                         rc = 1;
01397                         continue;
01398                 }
01399                 f = s+1;/* skip { */
01400                 se++;   /* skip } */
01401                 while (strchr("!?", *f) != NULL) {
01402                         switch(*f++) {
01403                         case '!':
01404                                 negate = ((negate + 1) % 2);
01405                                 /*@switchbreak@*/ break;
01406                         case '?':
01407                                 chkexist++;
01408                                 /*@switchbreak@*/ break;
01409                         }
01410                 }
01411                 /* Find end-of-expansion, handle %{foo:bar} expansions. */
01412                 for (fe = f; (c = *fe) && !strchr(" :}", c);)
01413                         fe++;
01414                 switch (c) {
01415                 case ':':
01416                         g = fe + 1;
01417                         ge = se - 1;
01418                         /*@innerbreak@*/ break;
01419                 case ' ':
01420                         lastc = se-1;
01421                         /*@innerbreak@*/ break;
01422                 default:
01423                         /*@innerbreak@*/ break;
01424                 }
01425                 /*@switchbreak@*/ break;
01426         }
01427 
01428         /* XXX Everything below expects fe > f */
01429         fn = (fe - f);
01430         gn = (ge - g);
01431         if ((fe - f) <= 0) {
01432 /* XXX Process % in unknown context */
01433                 c = '%';        /* XXX only need to save % */
01434                 SAVECHAR(mb, c);
01435 #if 0
01436                 rpmError(RPMERR_BADSPEC,
01437                         _("A %% is followed by an unparseable macro\n"));
01438 #endif
01439                 s = se;
01440                 continue;
01441         }
01442 
01443         if (mb->macro_trace)
01444                 printMacro(mb, s, se);
01445 
01446         /* Expand builtin macros */
01447         if (STREQ("load", f, fn)) {
01448                 if (g != NULL) {
01449                     char * mfn = strncpy(alloca(gn + 1), g, gn);
01450                     int xx;
01451                     mfn[gn] = '\0';
01452                     xx = rpmLoadMacroFile(NULL, mfn);
01453                 }
01454                 s = se;
01455                 continue;
01456         }
01457         if (STREQ("global", f, fn)) {
01458                 s = doDefine(mb, se, RMIL_GLOBAL, 1);
01459                 continue;
01460         }
01461         if (STREQ("define", f, fn)) {
01462                 s = doDefine(mb, se, mb->depth, 0);
01463                 continue;
01464         }
01465         if (STREQ("undefine", f, fn)) {
01466                 s = doUndefine(mb->mc, se);
01467                 continue;
01468         }
01469         if (STREQ("unglobal", f, fn)) {
01470                 s = doUnglobal(mb->mc, se);
01471                 continue;
01472         }
01473 
01474         if (STREQ("echo", f, fn) ||
01475             STREQ("warn", f, fn) ||
01476             STREQ("error", f, fn)) {
01477                 int waserror = 0;
01478                 if (STREQ("error", f, fn))
01479                         waserror = 1;
01480                 if (g != NULL && g < ge)
01481                         doOutput(mb, waserror, g, gn);
01482                 else
01483                         doOutput(mb, waserror, f, fn);
01484                 s = se;
01485                 continue;
01486         }
01487 
01488         if (STREQ("trace", f, fn)) {
01489                 /* XXX TODO restore expand_trace/macro_trace to 0 on return */
01490                 mb->expand_trace = mb->macro_trace = (negate ? 0 : mb->depth);
01491                 if (mb->depth == 1) {
01492                         print_macro_trace = mb->macro_trace;
01493                         print_expand_trace = mb->expand_trace;
01494                 }
01495                 s = se;
01496                 continue;
01497         }
01498 
01499         if (STREQ("dump", f, fn)) {
01500                 rpmDumpMacroTable(mb->mc, NULL);
01501                 while (iseol(*se))
01502                         se++;
01503                 s = se;
01504                 continue;
01505         }
01506 
01507 #ifdef  WITH_LUA
01508         if (STREQ("lua", f, fn)) {
01509                 rpmlua lua = NULL; /* Global state. */
01510                 const char *ls = s+sizeof("{lua:")-1;
01511                 const char *lse = se-sizeof("}")+1;
01512                 char *scriptbuf = (char *)xmalloc((lse-ls)+1);
01513                 const char *printbuf;
01514                 memcpy(scriptbuf, ls, lse-ls);
01515                 scriptbuf[lse-ls] = '\0';
01516                 rpmluaSetPrintBuffer(lua, 1);
01517                 if (rpmluaRunScript(lua, scriptbuf, NULL) == -1)
01518                     rc = 1;
01519                 printbuf = rpmluaGetPrintBuffer(lua);
01520                 if (printbuf) {
01521                     int len = strlen(printbuf);
01522                     if (len > mb->nb)
01523                         len = mb->nb;
01524                     memcpy(mb->t, printbuf, len);
01525                     mb->t += len;
01526                     mb->nb -= len;
01527                 }
01528                 rpmluaSetPrintBuffer(lua, 0);
01529                 free(scriptbuf);
01530                 s = se;
01531                 continue;
01532         }
01533 #endif
01534 
01535 #if defined(NOTYET)     /* XXX change needs parsePrep and macros changes too */
01536         /* Rewrite "%patchNN ..." as "%patch -P NN ..." and expand. */
01537         if (lastc != NULL && fn > 5 && STREQ("patch", f, 5) && xisdigit(f[5])) {
01538                 /*@-internalglobs@*/ /* FIX: verbose may be set */
01539                 doFoo(mb, negate, f, (lastc - f), NULL, 0);
01540                 /*@=internalglobs@*/
01541                 s = lastc;
01542                 continue;
01543         }
01544 #endif
01545 
01546         /* XXX necessary but clunky */
01547         if (STREQ("basename", f, fn) ||
01548             STREQ("dirname", f, fn) ||
01549             STREQ("suffix", f, fn) ||
01550             STREQ("expand", f, fn) ||
01551             STREQ("verbose", f, fn) ||
01552             STREQ("uncompress", f, fn) ||
01553             STREQ("url2path", f, fn) ||
01554             STREQ("u2p", f, fn) ||
01555             STREQ("S", f, fn) ||
01556             STREQ("P", f, fn) ||
01557             STREQ("F", f, fn)) {
01558                 /*@-internalglobs@*/ /* FIX: verbose may be set */
01559                 doFoo(mb, negate, f, fn, g, gn);
01560                 /*@=internalglobs@*/
01561                 s = se;
01562                 continue;
01563         }
01564 
01565         /* Expand defined macros */
01566         mep = findEntry(mb->mc, f, fn);
01567         me = (mep ? *mep : NULL);
01568 
01569         /* XXX Special processing for flags */
01570         if (*f == '-') {
01571                 if (me)
01572                         me->used++;     /* Mark macro as used */
01573                 if ((me == NULL && !negate) ||  /* Without -f, skip %{-f...} */
01574                     (me != NULL && negate)) {   /* With -f, skip %{!-f...} */
01575                         s = se;
01576                         continue;
01577                 }
01578 
01579                 if (g && g < ge) {              /* Expand X in %{-f:X} */
01580                         rc = expandT(mb, g, gn);
01581                 } else
01582                 if (me && me->body && *me->body) {/* Expand %{-f}/%{-f*} */
01583                         rc = expandT(mb, me->body, strlen(me->body));
01584                 }
01585                 s = se;
01586                 continue;
01587         }
01588 
01589         /* XXX Special processing for macro existence */
01590         if (chkexist) {
01591                 if ((me == NULL && !negate) ||  /* Without -f, skip %{?f...} */
01592                     (me != NULL && negate)) {   /* With -f, skip %{!?f...} */
01593                         s = se;
01594                         continue;
01595                 }
01596                 if (g && g < ge) {              /* Expand X in %{?f:X} */
01597                         rc = expandT(mb, g, gn);
01598                 } else
01599                 if (me && me->body && *me->body) { /* Expand %{?f}/%{?f*} */
01600                         rc = expandT(mb, me->body, strlen(me->body));
01601                 }
01602                 s = se;
01603                 continue;
01604         }
01605         
01606         if (me == NULL) {       /* leave unknown %... as is */
01607 #ifndef HACK
01608 #if DEAD
01609                 /* XXX hack to skip over empty arg list */
01610                 if (fn == 1 && *f == '*') {
01611                         s = se;
01612                         continue;
01613                 }
01614 #endif
01615                 /* XXX hack to permit non-overloaded %foo to be passed */
01616                 c = '%';        /* XXX only need to save % */
01617                 SAVECHAR(mb, c);
01618 #else
01619                 if (!strncmp(f, "if", fn) ||
01620                     !strncmp(f, "else", fn) ||
01621                     !strncmp(f, "endif", fn)) {
01622                         c = '%';        /* XXX only need to save % */
01623                         SAVECHAR(mb, c);
01624                 } else {
01625                         rpmError(RPMERR_BADSPEC,
01626                                 _("Macro %%%.*s not found, skipping\n"), fn, f);
01627                         s = se;
01628                 }
01629 #endif
01630                 continue;
01631         }
01632 
01633         /* Setup args for "%name " macros with opts */
01634         if (me && me->opts != NULL) {
01635                 if (lastc != NULL) {
01636                         se = grabArgs(mb, me, fe, lastc);
01637                 } else {
01638                         addMacro(mb->mc, "**", NULL, "", mb->depth);
01639                         addMacro(mb->mc, "*", NULL, "", mb->depth);
01640                         addMacro(mb->mc, "#", NULL, "0", mb->depth);
01641                         addMacro(mb->mc, "0", NULL, me->name, mb->depth);
01642                 }
01643         }
01644 
01645         /* Recursively expand body of macro */
01646         if (me->body && *me->body) {
01647                 mb->s = me->body;
01648                 rc = expandMacro(mb);
01649                 if (rc == 0)
01650                         me->used++;     /* Mark macro as used */
01651         }
01652 
01653         /* Free args for "%name " macros with opts */
01654         if (me->opts != NULL)
01655                 freeArgs(mb);
01656 
01657         s = se;
01658     }
01659 /*@=branchstate@*/
01660 
01661     *mb->t = '\0';
01662     mb->s = s;
01663     mb->depth--;
01664     if (rc != 0 || mb->expand_trace)
01665         printExpansion(mb, t, mb->t);
01666     return rc;
01667 }
01668 
01669 #if !defined(DEBUG_MACROS)
01670 /* =============================================================== */
01671 /* XXX dupe'd to avoid change in linkage conventions. */
01672 
01673 #define POPT_ERROR_NOARG        -10     
01674 #define POPT_ERROR_BADQUOTE     -15     
01675 #define POPT_ERROR_MALLOC       -21     
01677 #define POPT_ARGV_ARRAY_GROW_DELTA 5
01678 
01679 /*@-boundswrite@*/
01680 static int XpoptDupArgv(int argc, const char **argv,
01681                 int * argcPtr, const char *** argvPtr)
01682         /*@modifies *argcPtr, *argvPtr @*/
01683 {
01684     size_t nb = (argc + 1) * sizeof(*argv);
01685     const char ** argv2;
01686     char * dst;
01687     int i;
01688 
01689     if (argc <= 0 || argv == NULL)      /* XXX can't happen */
01690         return POPT_ERROR_NOARG;
01691     for (i = 0; i < argc; i++) {
01692         if (argv[i] == NULL)
01693             return POPT_ERROR_NOARG;
01694         nb += strlen(argv[i]) + 1;
01695     }
01696         
01697     dst = malloc(nb);
01698     if (dst == NULL)                    /* XXX can't happen */
01699         return POPT_ERROR_MALLOC;
01700     argv2 = (void *) dst;
01701     dst += (argc + 1) * sizeof(*argv);
01702 
01703     /*@-branchstate@*/
01704     for (i = 0; i < argc; i++) {
01705         argv2[i] = dst;
01706         dst += strlen(strcpy(dst, argv[i])) + 1;
01707     }
01708     /*@=branchstate@*/
01709     argv2[argc] = NULL;
01710 
01711     if (argvPtr) {
01712         *argvPtr = argv2;
01713     } else {
01714         free(argv2);
01715         argv2 = NULL;
01716     }
01717     if (argcPtr)
01718         *argcPtr = argc;
01719     return 0;
01720 }
01721 /*@=boundswrite@*/
01722 
01723 /*@-bounds@*/
01724 static int XpoptParseArgvString(const char * s, int * argcPtr, const char *** argvPtr)
01725         /*@modifies *argcPtr, *argvPtr @*/
01726 {
01727     const char * src;
01728     char quote = '\0';
01729     int argvAlloced = POPT_ARGV_ARRAY_GROW_DELTA;
01730     const char ** argv = malloc(sizeof(*argv) * argvAlloced);
01731     int argc = 0;
01732     int buflen = strlen(s) + 1;
01733     char * buf = memset(alloca(buflen), 0, buflen);
01734     int rc = POPT_ERROR_MALLOC;
01735 
01736     if (argv == NULL) return rc;
01737     argv[argc] = buf;
01738 
01739     for (src = s; *src != '\0'; src++) {
01740         if (quote == *src) {
01741             quote = '\0';
01742         } else if (quote != '\0') {
01743             if (*src == '\\') {
01744                 src++;
01745                 if (!*src) {
01746                     rc = POPT_ERROR_BADQUOTE;
01747                     goto exit;
01748                 }
01749                 if (*src != quote) *buf++ = '\\';
01750             }
01751             *buf++ = *src;
01752         } else if (isspace(*src)) {
01753             if (*argv[argc] != '\0') {
01754                 buf++, argc++;
01755                 if (argc == argvAlloced) {
01756                     argvAlloced += POPT_ARGV_ARRAY_GROW_DELTA;
01757                     argv = realloc(argv, sizeof(*argv) * argvAlloced);
01758                     if (argv == NULL) goto exit;
01759                 }
01760                 argv[argc] = buf;
01761             }
01762         } else switch (*src) {
01763           case '"':
01764           case '\'':
01765             quote = *src;
01766             /*@switchbreak@*/ break;
01767           case '\\':
01768             src++;
01769             if (!*src) {
01770                 rc = POPT_ERROR_BADQUOTE;
01771                 goto exit;
01772             }
01773             /*@fallthrough@*/
01774           default:
01775             *buf++ = *src;
01776             /*@switchbreak@*/ break;
01777         }
01778     }
01779 
01780     if (strlen(argv[argc])) {
01781         argc++, buf++;
01782     }
01783 
01784     rc = XpoptDupArgv(argc, argv, argcPtr, argvPtr);
01785 
01786 exit:
01787     if (argv) free(argv);
01788     return rc;
01789 }
01790 /*@=bounds@*/
01791 /* =============================================================== */
01792 /*@unchecked@*/
01793 static int _debug = 0;
01794 
01795 int rpmGlob(const char * patterns, int * argcPtr, const char *** argvPtr)
01796 {
01797     int ac = 0;
01798     const char ** av = NULL;
01799     int argc = 0;
01800     const char ** argv = NULL;
01801     char * globRoot = NULL;
01802 #ifdef ENABLE_NLS
01803     const char * old_collate = NULL;
01804     const char * old_ctype = NULL;
01805     const char * t;
01806 #endif
01807     size_t maxb, nb;
01808     int i, j;
01809     int rc;
01810 
01811     rc = XpoptParseArgvString(patterns, &ac, &av);
01812     if (rc)
01813         return rc;
01814 #ifdef ENABLE_NLS
01815 /*@-branchstate@*/
01816         t = setlocale(LC_COLLATE, NULL);
01817         if (t)
01818             old_collate = xstrdup(t);
01819         t = setlocale(LC_CTYPE, NULL);
01820         if (t)
01821             old_ctype = xstrdup(t);
01822 /*@=branchstate@*/
01823         (void) setlocale(LC_COLLATE, "C");
01824         (void) setlocale(LC_CTYPE, "C");
01825 #endif
01826         
01827     if (av != NULL)
01828     for (j = 0; j < ac; j++) {
01829         const char * globURL;
01830         const char * path;
01831         int ut = urlPath(av[j], &path);
01832         glob_t gl;
01833 
01834         if (!Glob_pattern_p(av[j], 0) && strchr(path, '~') == NULL) {
01835             argv = xrealloc(argv, (argc+2) * sizeof(*argv));
01836             argv[argc] = xstrdup(av[j]);
01837 if (_debug)
01838 fprintf(stderr, "*** rpmGlob argv[%d] \"%s\"\n", argc, argv[argc]);
01839             argc++;
01840             continue;
01841         }
01842         
01843         gl.gl_pathc = 0;
01844         gl.gl_pathv = NULL;
01845         rc = Glob(av[j], GLOB_TILDE, Glob_error, &gl);
01846         if (rc)
01847             goto exit;
01848 
01849         /* XXX Prepend the URL leader for globs that have stripped it off */
01850         maxb = 0;
01851         for (i = 0; i < gl.gl_pathc; i++) {
01852             if ((nb = strlen(&(gl.gl_pathv[i][0]))) > maxb)
01853                 maxb = nb;
01854         }
01855         
01856         nb = ((ut == URL_IS_PATH) ? (path - av[j]) : 0);
01857         maxb += nb;
01858         maxb += 1;
01859         globURL = globRoot = xmalloc(maxb);
01860 
01861         switch (ut) {
01862         case URL_IS_PATH:
01863         case URL_IS_DASH:
01864             strncpy(globRoot, av[j], nb);
01865             /*@switchbreak@*/ break;
01866         case URL_IS_HTTPS:
01867         case URL_IS_HTTP:
01868         case URL_IS_FTP:
01869         case URL_IS_HKP:
01870         case URL_IS_UNKNOWN:
01871         default:
01872             /*@switchbreak@*/ break;
01873         }
01874         globRoot += nb;
01875         *globRoot = '\0';
01876 if (_debug)
01877 fprintf(stderr, "*** GLOB maxb %d diskURL %d %*s globURL %p %s\n", (int)maxb, (int)nb, (int)nb, av[j], globURL, globURL);
01878         
01879         argv = xrealloc(argv, (argc+gl.gl_pathc+1) * sizeof(*argv));
01880 
01881         if (argv != NULL)
01882         for (i = 0; i < gl.gl_pathc; i++) {
01883             const char * globFile = &(gl.gl_pathv[i][0]);
01884             if (globRoot > globURL && globRoot[-1] == '/')
01885                 while (*globFile == '/') globFile++;
01886             strcpy(globRoot, globFile);
01887 if (_debug)
01888 fprintf(stderr, "*** rpmGlob argv[%d] \"%s\"\n", argc, globURL);
01889             argv[argc++] = xstrdup(globURL);
01890         }
01891         /*@-immediatetrans@*/
01892         Globfree(&gl);
01893         /*@=immediatetrans@*/
01894         globURL = _free(globURL);
01895     }
01896 
01897     if (argv != NULL && argc > 0) {
01898         argv[argc] = NULL;
01899         if (argvPtr)
01900             *argvPtr = argv;
01901         if (argcPtr)
01902             *argcPtr = argc;
01903         rc = 0;
01904     } else
01905         rc = 1;
01906 
01907 
01908 exit:
01909 #ifdef ENABLE_NLS       
01910 /*@-branchstate@*/
01911     if (old_collate) {
01912         (void) setlocale(LC_COLLATE, old_collate);
01913         old_collate = _free(old_collate);
01914     }
01915     if (old_ctype) {
01916         (void) setlocale(LC_CTYPE, old_ctype);
01917         old_ctype = _free(old_ctype);
01918     }
01919 /*@=branchstate@*/
01920 #endif
01921     av = _free(av);
01922 /*@-branchstate@*/
01923     if (rc || argvPtr == NULL) {
01924 /*@-dependenttrans -unqualifiedtrans@*/
01925         if (argv != NULL)
01926         for (i = 0; i < argc; i++)
01927             argv[i] = _free(argv[i]);
01928         argv = _free(argv);
01929 /*@=dependenttrans =unqualifiedtrans@*/
01930     }
01931 /*@=branchstate@*/
01932     return rc;
01933 }
01934 #endif  /* !defined(DEBUG_MACROS) */
01935 
01936 /* =============================================================== */
01937 
01938 int
01939 expandMacros(void * spec, MacroContext mc, char * sbuf, size_t slen)
01940 {
01941     MacroBuf mb = alloca(sizeof(*mb));
01942     char *tbuf;
01943     int rc;
01944 
01945     if (sbuf == NULL || slen == 0)
01946         return 0;
01947     if (mc == NULL) mc = rpmGlobalMacroContext;
01948 
01949     tbuf = alloca(slen + 1);
01950     memset(tbuf, 0, (slen + 1));
01951 
01952     mb->s = sbuf;
01953     mb->t = tbuf;
01954     mb->nb = slen;
01955     mb->depth = 0;
01956     mb->macro_trace = print_macro_trace;
01957     mb->expand_trace = print_expand_trace;
01958 
01959     mb->spec = spec;    /* (future) %file expansion info */
01960     mb->mc = mc;
01961 
01962     rc = expandMacro(mb);
01963 
01964     tbuf[slen] = '\0';
01965     if (mb->nb == 0)
01966         rpmError(RPMERR_BADSPEC, _("Macro expansion too big for target buffer\n"));
01967     else
01968         strncpy(sbuf, tbuf, (slen - mb->nb + 1));
01969 
01970     return rc;
01971 }
01972 
01973 void
01974 addMacro(MacroContext mc,
01975         const char * n, const char * o, const char * b, int level)
01976 {
01977     MacroEntry * mep;
01978     const char * name = n;
01979 
01980     if (*name == '.')           /* XXX readonly macros */
01981         name++;
01982     if (*name == '.')           /* XXX readonly macros */
01983         name++;
01984 
01985     if (mc == NULL) mc = rpmGlobalMacroContext;
01986 
01987     /* If new name, expand macro table */
01988     if ((mep = findEntry(mc, name, 0)) == NULL) {
01989         if (mc->firstFree == mc->macrosAllocated)
01990             expandMacroTable(mc);
01991         if (mc->macroTable != NULL)
01992             mep = mc->macroTable + mc->firstFree++;
01993     }
01994 
01995     if (mep != NULL) {
01996         /* XXX permit "..foo" to be pushed over ".foo" */
01997         if (*mep && (*mep)->flags && !(n[0] == '.' && n[1] == '.')) {
01998             /* XXX avoid error message for %buildroot */
01999             if (strcmp((*mep)->name, "buildroot"))
02000                 rpmError(RPMERR_BADSPEC, _("Macro '%s' is readonly and cannot be changed.\n"), n);
02001             return;
02002         }
02003         /* Push macro over previous definition */
02004         pushMacro(mep, n, o, b, level);
02005 
02006         /* If new name, sort macro table */
02007         if ((*mep)->prev == NULL)
02008             sortMacroTable(mc);
02009     }
02010 }
02011 
02012 void
02013 delMacro(MacroContext mc, const char * n)
02014 {
02015     MacroEntry * mep;
02016 
02017     if (mc == NULL) mc = rpmGlobalMacroContext;
02018     /* If name exists, pop entry */
02019     if ((mep = findEntry(mc, n, 0)) != NULL) {
02020         popMacro(mep);
02021         /* If deleted name, sort macro table */
02022         if (!(mep && *mep))
02023             sortMacroTable(mc);
02024     }
02025 }
02026 
02027 void
02028 delMacroAll(MacroContext mc, const char * n)
02029 {
02030         MacroEntry * mep;
02031 
02032         if (mc == NULL) mc = rpmGlobalMacroContext;
02033         /* If name exists, pop entry */
02034         while ((mep = findEntry(mc, n, 0)) != NULL) {
02035                 delMacro(mc, n);
02036         }
02037 }
02038 
02039 /*@-mustmod@*/ /* LCL: mc is modified through mb->mc, mb is abstract */
02040 int
02041 rpmDefineMacro(MacroContext mc, const char * macro, int level)
02042 {
02043     MacroBuf mb = alloca(sizeof(*mb));
02044 
02045     memset(mb, 0, sizeof(*mb));
02046     /* XXX just enough to get by */
02047     mb->mc = (mc ? mc : rpmGlobalMacroContext);
02048     (void) doDefine(mb, macro, level, 0);
02049     return 0;
02050 }
02051 /*@=mustmod@*/
02052 
02053 void
02054 rpmLoadMacros(MacroContext mc, int level)
02055 {
02056 
02057     if (mc == NULL || mc == rpmGlobalMacroContext)
02058         return;
02059 
02060     if (mc->macroTable != NULL) {
02061         int i;
02062         for (i = 0; i < mc->firstFree; i++) {
02063             MacroEntry *mep, me;
02064             mep = &mc->macroTable[i];
02065             me = *mep;
02066 
02067             if (me == NULL)             /* XXX this should never happen */
02068                 continue;
02069             addMacro(NULL, me->name, me->opts, me->body, (level - 1));
02070         }
02071     }
02072 }
02073 
02074 int
02075 rpmLoadMacroFile(MacroContext mc, const char * fn)
02076 {
02077     FD_t fd = Fopen(fn, "r.fpio");
02078     size_t bufn = _macro_BUFSIZ;
02079     char *buf = alloca(bufn);
02080     int rc = -1;
02081 
02082     if (fd == NULL || Ferror(fd)) {
02083         if (fd) (void) Fclose(fd);
02084         return rc;
02085     }
02086 
02087     /* XXX Assume new fangled macro expansion */
02088     /*@-mods@*/
02089     max_macro_depth = _MAX_MACRO_DEPTH;
02090     /*@=mods@*/
02091 
02092     buf[0] = '\0';
02093     while(rdcl(buf, bufn, fd) != NULL) {
02094         char c, *n;
02095 
02096         n = buf;
02097         SKIPBLANK(n, c);
02098 
02099         if (c != '%')
02100                 continue;
02101         n++;    /* skip % */
02102         rc = rpmDefineMacro(mc, n, RMIL_MACROFILES);
02103     }
02104     rc = Fclose(fd);
02105     return rc;
02106 }
02107 
02108 void
02109 rpmInitMacros(MacroContext mc, const char * macrofiles)
02110 {
02111     char *mfiles, *m, *me;
02112 
02113     if (macrofiles == NULL)
02114         return;
02115 #ifdef  DYING
02116     if (mc == NULL) mc = rpmGlobalMacroContext;
02117 #endif
02118 
02119     mfiles = xstrdup(macrofiles);
02120     for (m = mfiles; m && *m != '\0'; m = me) {
02121         const char ** av;
02122         int ac;
02123         int i;
02124 
02125         for (me = m; (me = strchr(me, ':')) != NULL; me++) {
02126             /* Skip over URI's. */
02127             if (!(me[1] == '/' && me[2] == '/'))
02128                 /*@innerbreak@*/ break;
02129         }
02130 
02131         if (me && *me == ':')
02132             *me++ = '\0';
02133         else
02134             me = m + strlen(m);
02135 
02136         /* Glob expand the macro file path element, expanding ~ to $HOME. */
02137         ac = 0;
02138         av = NULL;
02139 #if defined(DEBUG_MACROS)
02140         ac = 1;
02141         av = xmalloc((ac + 1) * sizeof(*av));
02142         av[0] = strdup(m);
02143         av[1] = NULL;
02144 #else
02145         i = rpmGlob(m, &ac, &av);
02146         if (i != 0)
02147             continue;
02148 #endif
02149 
02150         /* Read macros from each file. */
02151 
02152         for (i = 0; i < ac; i++) {
02153             size_t slen = strlen(av[i]);
02154 
02155         /* Skip backup files and %config leftovers. */
02156 #define _suffix(_s, _x) \
02157     (slen >= sizeof(_x) && !strcmp((_s)+slen-(sizeof(_x)-1), (_x)))
02158             if (!(_suffix(av[i], "~")
02159                || _suffix(av[i], ".rpmnew")
02160                || _suffix(av[i], ".rpmorig")
02161                || _suffix(av[i], ".rpmsave"))
02162                )
02163                    (void) rpmLoadMacroFile(mc, av[i]);
02164 #undef _suffix
02165 
02166             av[i] = _free(av[i]);
02167         }
02168         av = _free(av);
02169     }
02170     mfiles = _free(mfiles);
02171 
02172     /* Reload cmdline macros */
02173     /*@-mods@*/
02174     rpmLoadMacros(rpmCLIMacroContext, RMIL_CMDLINE);
02175     /*@=mods@*/
02176 }
02177 
02178 /*@-globstate@*/
02179 void
02180 rpmFreeMacros(MacroContext mc)
02181 {
02182     
02183     if (mc == NULL) mc = rpmGlobalMacroContext;
02184 
02185     if (mc->macroTable != NULL) {
02186         int i;
02187         for (i = 0; i < mc->firstFree; i++) {
02188             MacroEntry me;
02189             while ((me = mc->macroTable[i]) != NULL) {
02190                 /* XXX cast to workaround const */
02191                 /*@-onlytrans@*/
02192                 if ((mc->macroTable[i] = me->prev) == NULL)
02193                     me->name = _free(me->name);
02194                 /*@=onlytrans@*/
02195                 me->opts = _free(me->opts);
02196                 me->body = _free(me->body);
02197                 me = _free(me);
02198             }
02199         }
02200         mc->macroTable = _free(mc->macroTable);
02201     }
02202     memset(mc, 0, sizeof(*mc));
02203 }
02204 /*@=globstate@*/
02205 
02206 /* =============================================================== */
02207 int isCompressed(const char * file, rpmCompressedMagic * compressed)
02208 {
02209     FD_t fd;
02210     ssize_t nb;
02211     int rc = -1;
02212     unsigned char magic[13];
02213     char *end, *ext;
02214 
02215     *compressed = COMPRESSED_NOT;
02216 
02217     fd = Fopen(file, "r");
02218     if (fd == NULL || Ferror(fd)) {
02219         /* XXX Fstrerror */
02220         rpmError(RPMERR_BADSPEC, _("File %s: %s\n"), file, Fstrerror(fd));
02221         if (fd) (void) Fclose(fd);
02222         return 1;
02223     }
02224     nb = Fread(magic, sizeof(magic[0]), sizeof(magic), fd);
02225     if (nb < 0) {
02226         rpmError(RPMERR_BADSPEC, _("File %s: %s\n"), file, Fstrerror(fd));
02227         rc = 1;
02228     } else if (nb < sizeof(magic)) {
02229         rpmError(RPMERR_BADSPEC, _("File %s is smaller than %u bytes\n"),
02230                 file, (unsigned)sizeof(magic));
02231         rc = 0;
02232     }
02233     (void) Fclose(fd);
02234     if (rc >= 0)
02235         return rc;
02236 
02237     rc = 0;
02238 
02239     /* Tar archives will be recognized by filename. */
02240     end = strchr(file, '\0');
02241     ext = end - 4;
02242     if (ext > file && !strcasecmp(ext, ".tar")) return rc;
02243  
02244     if (magic[0] == 'B' && magic[1] == 'Z')
02245         *compressed = COMPRESSED_BZIP2;
02246     else
02247     if (magic[0] == 0120 && magic[1] == 0113
02248      && magic[2] == 0003 && magic[3] == 0004)   /* pkzip */
02249         *compressed = COMPRESSED_ZIP;
02250     else
02251     if (magic[0] == 0x89 && magic[1] == 'L'
02252      && magic[2] == 'Z' && magic[3] == 'O')     /* lzop */
02253         *compressed = COMPRESSED_LZOP;
02254     else
02255     /* XXX Ick, LZMA has no magic. See http://lkml.org/lkml/2005/6/13/285 */
02256     if (magic[ 9] == 0x00 && magic[10] == 0x00 &&
02257         magic[11] == 0x00 && magic[12] == 0x00) /* lzmash */
02258         *compressed = COMPRESSED_LZMA;
02259     else
02260     if ((magic[0] == 0037 && magic[1] == 0213)  /* gzip */
02261      || (magic[0] == 0037 && magic[1] == 0236)  /* old gzip */
02262      || (magic[0] == 0037 && magic[1] == 0036)  /* pack */
02263      || (magic[0] == 0037 && magic[1] == 0240)  /* SCO lzh */
02264      || (magic[0] == 0037 && magic[1] == 0235)) /* compress */
02265         *compressed = COMPRESSED_OTHER;
02266 
02267     return rc;
02268 }
02269 
02270 /* =============================================================== */
02271 
02272 /*@-modfilesys@*/
02273 char * 
02274 rpmExpand(const char *arg, ...)
02275 {
02276     const char *s;
02277     char *t, *te;
02278     size_t sn, tn;
02279     size_t bufn = 8 * _macro_BUFSIZ;
02280 
02281     va_list ap;
02282 
02283     if (arg == NULL)
02284         return xstrdup("");
02285 
02286     t = xmalloc(bufn + strlen(arg) + 1);
02287     *t = '\0';
02288     te = stpcpy(t, arg);
02289 
02290 /*@-branchstate@*/
02291     va_start(ap, arg);
02292     while ((s = va_arg(ap, const char *)) != NULL) {
02293         sn = strlen(s);
02294         tn = (te - t);
02295         t = xrealloc(t, tn + sn + bufn + 1);
02296         te = t + tn;
02297         te = stpcpy(te, s);
02298     }
02299     va_end(ap);
02300 /*@=branchstate@*/
02301 
02302     *te = '\0';
02303     tn = (te - t);
02304     (void) expandMacros(NULL, NULL, t, tn + bufn + 1);
02305     t[tn + bufn] = '\0';
02306     t = xrealloc(t, strlen(t) + 1);
02307     
02308     return t;
02309 }
02310 /*@=modfilesys@*/
02311 
02312 int
02313 rpmExpandNumeric(const char *arg)
02314 {
02315     const char *val;
02316     int rc;
02317 
02318     if (arg == NULL)
02319         return 0;
02320 
02321     val = rpmExpand(arg, NULL);
02322     if (!(val && *val != '%'))
02323         rc = 0;
02324     else if (*val == 'Y' || *val == 'y')
02325         rc = 1;
02326     else if (*val == 'N' || *val == 'n')
02327         rc = 0;
02328     else {
02329         char *end;
02330         rc = strtol(val, &end, 0);
02331         if (!(end && *end == '\0'))
02332             rc = 0;
02333     }
02334     val = _free(val);
02335 
02336     return rc;
02337 }
02338 
02339 /* @todo "../sbin/./../bin/" not correct. */
02340 char *rpmCleanPath(char * path)
02341 {
02342     const char *s;
02343     char *se, *t, *te;
02344     int begin = 1;
02345 
02346     if (path == NULL)
02347         return NULL;
02348 
02349 /*fprintf(stderr, "*** RCP %s ->\n", path); */
02350     s = t = te = path;
02351     while (*s != '\0') {
02352 /*fprintf(stderr, "*** got \"%.*s\"\trest \"%s\"\n", (t-path), path, s); */
02353         switch(*s) {
02354         case ':':                       /* handle url's */
02355             if (s[1] == '/' && s[2] == '/') {
02356                 *t++ = *s++;
02357                 *t++ = *s++;
02358                 /* XXX handle "file:///" */
02359                 if (s[0] == '/') *t++ = *s++;
02360                 te = t;
02361                 /*@switchbreak@*/ break;
02362             }
02363             begin=1;
02364             /*@switchbreak@*/ break;
02365         case '/':
02366             /* Move parent dir forward */
02367             for (se = te + 1; se < t && *se != '/'; se++)
02368                 {};
02369             if (se < t && *se == '/') {
02370                 te = se;
02371 /*fprintf(stderr, "*** next pdir \"%.*s\"\n", (te-path), path); */
02372             }
02373             while (s[1] == '/')
02374                 s++;
02375             while (t > te && t[-1] == '/')
02376                 t--;
02377             /*@switchbreak@*/ break;
02378         case '.':
02379             /* Leading .. is special */
02380             /* Check that it is ../, so that we don't interpret */
02381             /* ..?(i.e. "...") or ..* (i.e. "..bogus") as "..". */
02382             /* in the case of "...", this ends up being processed*/
02383             /* as "../.", and the last '.' is stripped.  This   */
02384             /* would not be correct processing.                 */
02385             if (begin && s[1] == '.' && (s[2] == '/' || s[2] == '\0')) {
02386 /*fprintf(stderr, "    leading \"..\"\n"); */
02387                 *t++ = *s++;
02388                 /*@switchbreak@*/ break;
02389             }
02390             /* Single . is special */
02391             if (begin && s[1] == '\0') {
02392                 /*@switchbreak@*/ break;
02393             }
02394             /* Trim embedded ./ , trailing /. */
02395             if ((t[-1] == '/' && s[1] == '\0') || (t > path && t[-1] == '/' && s[1] == '/')) {
02396                 s++;
02397                 continue;
02398             }
02399             /* Trim embedded /../ and trailing /.. */
02400             if (!begin && t > path && t[-1] == '/' && s[1] == '.' && (s[2] == '/' || s[2] == '\0')) {
02401                 t = te;
02402                 /* Move parent dir forward */
02403                 if (te > path)
02404                     for (--te; te > path && *te != '/'; te--)
02405                         {};
02406 /*fprintf(stderr, "*** prev pdir \"%.*s\"\n", (te-path), path); */
02407                 s++;
02408                 s++;
02409                 continue;
02410             }
02411             /*@switchbreak@*/ break;
02412         default:
02413             begin = 0;
02414             /*@switchbreak@*/ break;
02415         }
02416         *t++ = *s++;
02417     }
02418 
02419     /* Trim trailing / (but leave single / alone) */
02420     if (t > &path[1] && t[-1] == '/')
02421         t--;
02422     *t = '\0';
02423 
02424 /*fprintf(stderr, "\t%s\n", path); */
02425     return path;
02426 }
02427 
02428 /* Return concatenated and expanded canonical path. */
02429 
02430 const char *
02431 rpmGetPath(const char *path, ...)
02432 {
02433     size_t bufn = _macro_BUFSIZ;
02434     char *buf = alloca(bufn);
02435     const char * s;
02436     char * t, * te;
02437     va_list ap;
02438 
02439     if (path == NULL)
02440         return xstrdup("");
02441 
02442     buf[0] = '\0';
02443     t = buf;
02444     te = stpcpy(t, path);
02445     *te = '\0';
02446 
02447     va_start(ap, path);
02448     while ((s = va_arg(ap, const char *)) != NULL) {
02449         te = stpcpy(te, s);
02450         *te = '\0';
02451     }
02452     va_end(ap);
02453 /*@-modfilesys@*/
02454     (void) expandMacros(NULL, NULL, buf, bufn);
02455 /*@=modfilesys@*/
02456 
02457     (void) rpmCleanPath(buf);
02458     return xstrdup(buf);        /* XXX xstrdup has side effects. */
02459 }
02460 
02461 /* Merge 3 args into path, any or all of which may be a url. */
02462 
02463 const char * rpmGenPath(const char * urlroot, const char * urlmdir,
02464                 const char *urlfile)
02465 {
02466 /*@owned@*/ const char * xroot = rpmGetPath(urlroot, NULL);
02467 /*@dependent@*/ const char * root = xroot;
02468 /*@owned@*/ const char * xmdir = rpmGetPath(urlmdir, NULL);
02469 /*@dependent@*/ const char * mdir = xmdir;
02470 /*@owned@*/ const char * xfile = rpmGetPath(urlfile, NULL);
02471 /*@dependent@*/ const char * file = xfile;
02472     const char * result;
02473     const char * url = NULL;
02474     int nurl = 0;
02475     int ut;
02476 
02477 #if 0
02478 if (_debug) fprintf(stderr, "*** RGP xroot %s xmdir %s xfile %s\n", xroot, xmdir, xfile);
02479 #endif
02480     ut = urlPath(xroot, &root);
02481     if (url == NULL && ut > URL_IS_DASH) {
02482         url = xroot;
02483         nurl = root - xroot;
02484 #if 0
02485 if (_debug) fprintf(stderr, "*** RGP ut %d root %s nurl %d\n", ut, root, nurl);
02486 #endif
02487     }
02488     if (root == NULL || *root == '\0') root = "/";
02489 
02490     ut = urlPath(xmdir, &mdir);
02491     if (url == NULL && ut > URL_IS_DASH) {
02492         url = xmdir;
02493         nurl = mdir - xmdir;
02494 #if 0
02495 if (_debug) fprintf(stderr, "*** RGP ut %d mdir %s nurl %d\n", ut, mdir, nurl);
02496 #endif
02497     }
02498     if (mdir == NULL || *mdir == '\0') mdir = "/";
02499 
02500     ut = urlPath(xfile, &file);
02501     if (url == NULL && ut > URL_IS_DASH) {
02502         url = xfile;
02503         nurl = file - xfile;
02504 #if 0
02505 if (_debug) fprintf(stderr, "*** RGP ut %d file %s nurl %d\n", ut, file, nurl);
02506 #endif
02507     }
02508 
02509 /*@-branchstate@*/
02510     if (url && nurl > 0) {
02511         char *t = strncpy(alloca(nurl+1), url, nurl);
02512         t[nurl] = '\0';
02513         url = t;
02514     } else
02515         url = "";
02516 /*@=branchstate@*/
02517 
02518     result = rpmGetPath(url, root, "/", mdir, "/", file, NULL);
02519 
02520     xroot = _free(xroot);
02521     xmdir = _free(xmdir);
02522     xfile = _free(xfile);
02523 #if 0
02524 if (_debug) fprintf(stderr, "*** RGP result %s\n", result);
02525 #endif
02526     return result;
02527 }
02528 
02529 /* =============================================================== */
02530 
02531 #if defined(DEBUG_MACROS)
02532 
02533 #if defined(EVAL_MACROS)
02534 
02535 const char *rpmMacrofiles = MACROFILES;
02536 
02537 int
02538 main(int argc, char *argv[])
02539 {
02540     int c;
02541     int errflg = 0;
02542     extern char *optarg;
02543     extern int optind;
02544 
02545     while ((c = getopt(argc, argv, "f:")) != EOF ) {
02546         switch (c) {
02547         case 'f':
02548             rpmMacrofiles = optarg;
02549             break;
02550         case '?':
02551         default:
02552             errflg++;
02553             break;
02554         }
02555     }
02556     if (errflg || optind >= argc) {
02557         fprintf(stderr, "Usage: %s [-f macropath ] macro ...\n", argv[0]);
02558         exit(1);
02559     }
02560 
02561     rpmInitMacros(NULL, rpmMacrofiles);
02562     /* XXX getopt(3) also used for parametrized macros, expect scwewiness. */
02563     for ( ; optind < argc; optind++) {
02564         const char *val;
02565 
02566         val = rpmExpand(argv[optind], NULL);
02567         if (val) {
02568             fprintf(stdout, "%s:\t%s\n", argv[optind], val);
02569             val = _free(val);
02570         }
02571     }
02572     rpmFreeMacros(NULL);
02573     return 0;
02574 }
02575 
02576 #else   /* !EVAL_MACROS */
02577 
02578 const char *rpmMacrofiles = "../macros:./testmacros";
02579 const char *testfile = "./test";
02580 
02581 int
02582 main(int argc, char *argv[])
02583 {
02584     size_t bufn = _macro_BUFSIZ;
02585     char *buf = alloca(bufn);
02586     FILE *fp;
02587     int x;
02588 
02589     rpmInitMacros(NULL, rpmMacrofiles);
02590 
02591     if ((fp = fopen(testfile, "r")) != NULL) {
02592         while(rdcl(buf, bufn, fp)) {
02593             x = expandMacros(NULL, NULL, buf, bufn);
02594             fprintf(stderr, "%d->%s\n", x, buf);
02595             memset(buf, 0, bufn);
02596         }
02597         fclose(fp);
02598     }
02599 
02600     while(rdcl(buf, bufn, stdin)) {
02601         x = expandMacros(NULL, NULL, buf, bufn);
02602         fprintf(stderr, "%d->%s\n <-\n", x, buf);
02603         memset(buf, 0, bufn);
02604     }
02605     rpmFreeMacros(NULL);
02606 
02607     return 0;
02608 }
02609 #endif  /* EVAL_MACROS */
02610 #endif  /* DEBUG_MACROS */
02611 /*@=boundsread@*/

Generated on Tue Dec 27 22:41:26 2016 for rpm by  doxygen 1.4.4