rpmio/macro.c

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

Generated on Wed Mar 8 22:44:25 2006 for rpm by  doxygen 1.4.6