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

Generated on Sun Oct 26 13:02:03 2003 for rpm by doxygen1.2.18