Main Page   Modules   Data Structures   File List   Data Fields   Globals   Related Pages  

rpmio/macro.c

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

Generated on Sun Feb 2 23:32:05 2003 for rpm by doxygen1.2.18