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

Generated on Wed May 12 21:09:56 2004 for rpm by doxygen1.2.18