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

rpmio/macro.c

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

Generated at Thu Sep 6 11:32:32 2001 for rpm by doxygen1.2.8.1 written by Dimitri van Heesch, © 1997-2001