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

Generated on Wed Sep 4 12:49:56 2002 for rpm by doxygen1.2.14 written by Dimitri van Heesch, © 1997-2002