rpm 5.3.7

rpmio/macro.c

Go to the documentation of this file.
00001 
00005 #include "system.h"
00006 #include <stdarg.h>
00007 
00008 #if !defined(isblank)
00009 #define isblank(_c)     ((char)(_c) == ' ' || (char)(_c) == '\t')
00010 #endif
00011 #define iseol(_c)       ((char)(_c) == '\n' || (char)(_c) == '\r')
00012 
00013 #define STREQ(_t, _f, _fn)      ((_fn) == (sizeof(_t)-1) && !strncmp((_t), (_f), (_fn)))
00014 
00015 #ifdef DEBUG_MACROS
00016 #undef  WITH_LUA        /* XXX fixme */
00017 #include <sys/types.h>
00018 #include <errno.h>
00019 #include <fcntl.h>
00020 #include <getopt.h>
00021 #include <stdio.h>
00022 #include <stdlib.h>
00023 #include <string.h>
00024 #include <ctype.h>
00025 #include <popt.h>
00026 
00027 #define rpmlog fprintf
00028 #define RPMLOG_ERR stderr
00029 #define RPMLOG_WARNING stderr
00030 #undef  _
00031 #define _(x)    x
00032 
00033 #define vmefail(_nb)            (exit(1), NULL)
00034 #define URL_IS_DASH             1
00035 #define URL_IS_PATH             2
00036 #define urlPath(_xr, _r)        (*(_r) = (_xr), URL_IS_PATH)
00037 #define xisalnum(_c)            isalnum(_c)
00038 #define xisalpha(_c)            isalpha(_c)
00039 #define xisdigit(_c)            isdigit(_c)
00040 #define xisspace(_c)            isspace(_c)
00041 
00042 typedef FILE * FD_t;
00043 #define Fopen(_path, _fmode)    fopen(_path, "r");
00044 #define Ferror                  ferror
00045 #define Fstrerror(_fd)          strerror(errno)
00046 #define Fread                   fread
00047 #define Fclose                  fclose
00048 
00049 #define fdGetFILE(_fd)          (_fd)
00050 
00051 /*@unused@*/ static inline /*@null@*/ void *
00052 _free(/*@only@*/ /*@null@*/ const void * p)
00053         /*@modifies p@*/
00054 {
00055     if (p != NULL)      free((void *)p);
00056     return NULL;
00057 }
00058 
00059 #else
00060 
00061 /*@observer@*/ /*@checked@*/
00062 const char * rpmMacrofiles = MACROFILES;
00063 
00064 #include <rpmio_internal.h>
00065 #include <rpmlog.h>
00066 #include <mire.h>
00067 
00068 #ifdef  WITH_LUA
00069 #define _RPMLUA_INTERNAL        /* XXX lua->printbuf access */
00070 #include <rpmlua.h>
00071 #endif
00072 
00073 #define _RPMAUG_INTERNAL        /* XXX for _rpmaugFoo globals */
00074 #include <rpmaug.h>
00075 #include <rpmficl.h>
00076 #include <rpmjs.h>
00077 
00078 #if defined(WITH_NIX)
00079 #define _RPMNIX_INTERNAL
00080 #include <rpmnix.h>
00081 #endif
00082 
00083 #include <rpmjs.h>
00084 #include <rpmperl.h>
00085 #include <rpmpython.h>
00086 #include <rpmruby.h>
00087 #include <rpmsm.h>
00088 #include <rpmsquirrel.h>
00089 #include <rpmsql.h>
00090 #include <rpmtcl.h>
00091 
00092 #endif
00093 
00094 #include <rpmuuid.h>
00095 
00096 #define _MACRO_INTERNAL
00097 #include <rpmmacro.h>
00098 
00099 #include "debug.h"
00100 
00101 /*@unchecked@*/
00102 #if defined(WITH_AUGEAS) || defined(WITH_FICL) || defined(WITH_GPSEE) || defined(WITH_NIX) || defined(WITH_PERLEMBED) || defined(WITH_PYTHONEMBED) || defined(WITH_RUBYEMBED) || defined(WITH_SQLITE) || defined(WITH_SQUIRREL) || defined(WITH_TCL)
00103 static int _globalI = 0x80000000;
00104 #endif
00105 
00106 #if defined(__LCLINT__)
00107 /*@-exportheader@*/
00108 extern const unsigned short int **__ctype_b_loc (void) /*@*/;
00109 /*@=exportheader@*/
00110 #endif
00111 
00112 /*@access FD_t @*/              /* XXX compared with NULL */
00113 /*@access miRE @*/              /* XXX cast */
00114 /*@access MacroContext @*/
00115 /*@access MacroEntry@ */
00116 /*@access rpmlua @*/
00117 /*@access rpmtcl @*/
00118 
00119 static struct MacroContext_s rpmGlobalMacroContext_s;
00120 /*@-compmempass@*/
00121 MacroContext rpmGlobalMacroContext = &rpmGlobalMacroContext_s;
00122 /*@=compmempass@*/
00123 
00124 static struct MacroContext_s rpmCLIMacroContext_s;
00125 /*@-compmempass@*/
00126 MacroContext rpmCLIMacroContext = &rpmCLIMacroContext_s;
00127 /*@=compmempass@*/
00128 
00132 typedef /*@abstract@*/ struct MacroBuf_s {
00133 /*@kept@*/ /*@exposed@*/
00134     const char * s;             
00135 /*@shared@*/
00136     char * t;                   
00137     size_t nb;                  
00138     int depth;                  
00139     int macro_trace;            
00140     int expand_trace;           
00141 /*@kept@*/ /*@exposed@*/ /*@null@*/
00142     void * spec;                
00143 /*@kept@*/ /*@exposed@*/
00144     MacroContext mc;
00145 } * MacroBuf;
00146 
00147 #define SAVECHAR(_mb, _c) { *(_mb)->t = (char) (_c), (_mb)->t++, (_mb)->nb--; }
00148 
00149 /*@-exportlocal -exportheadervar@*/
00150 
00151 #define _MAX_MACRO_DEPTH        16
00152 /*@unchecked@*/
00153 int max_macro_depth = _MAX_MACRO_DEPTH;
00154 
00155 #define _PRINT_MACRO_TRACE      0
00156 /*@unchecked@*/
00157 int print_macro_trace = _PRINT_MACRO_TRACE;
00158 
00159 #define _PRINT_EXPAND_TRACE     0
00160 /*@unchecked@*/
00161 int print_expand_trace = _PRINT_EXPAND_TRACE;
00162 
00163 #define _MAX_LOAD_DEPTH         2
00164 /*@unchecked@*/
00165 int _max_load_depth = _MAX_LOAD_DEPTH;
00166 /*@=exportlocal =exportheadervar@*/
00167 
00168 #define MACRO_CHUNK_SIZE        16
00169 
00170 /* Size of expansion buffers. */
00171 /*@unchecked@*/
00172 static size_t _macro_BUFSIZ = 16 * 1024;
00173 
00174 /* forward ref */
00175 static int expandMacro(MacroBuf mb)
00176         /*@globals rpmGlobalMacroContext,
00177                 print_macro_trace, print_expand_trace, h_errno,
00178                 fileSystem, internalState @*/
00179         /*@modifies mb, rpmGlobalMacroContext,
00180                 print_macro_trace, print_expand_trace,
00181                 fileSystem, internalState @*/;
00182 
00183 /* =============================================================== */
00184 
00191 static int
00192 compareMacroName(const void * ap, const void * bp)
00193         /*@*/
00194 {
00195     MacroEntry ame = *((MacroEntry *)ap);
00196     MacroEntry bme = *((MacroEntry *)bp);
00197 
00198     if (ame == NULL && bme == NULL)
00199         return 0;
00200     if (ame == NULL)
00201         return 1;
00202     if (bme == NULL)
00203         return -1;
00204     return strcmp(ame->name, bme->name);
00205 }
00206 
00211 static void
00212 expandMacroTable(MacroContext mc)
00213         /*@modifies mc @*/
00214 {
00215     if (mc->macroTable == NULL) {
00216         mc->macrosAllocated = MACRO_CHUNK_SIZE;
00217         mc->macroTable = (MacroEntry *)
00218             xmalloc(sizeof(*(mc->macroTable)) * mc->macrosAllocated);
00219         mc->firstFree = 0;
00220     } else {
00221         mc->macrosAllocated += MACRO_CHUNK_SIZE;
00222         mc->macroTable = (MacroEntry *)
00223             xrealloc(mc->macroTable, sizeof(*(mc->macroTable)) *
00224                         mc->macrosAllocated);
00225     }
00226     memset(&mc->macroTable[mc->firstFree], 0, MACRO_CHUNK_SIZE * sizeof(*(mc->macroTable)));
00227 }
00228 
00233 static void
00234 sortMacroTable(MacroContext mc)
00235         /*@modifies mc @*/
00236 {
00237     int i;
00238 
00239     if (mc == NULL || mc->macroTable == NULL)
00240         return;
00241 
00242     qsort(mc->macroTable, mc->firstFree, sizeof(mc->macroTable[0]),
00243                 compareMacroName);
00244 
00245     /* Empty pointers are now at end of table. Reset first free index. */
00246     for (i = 0; i < mc->firstFree; i++) {
00247         if (mc->macroTable[i] != NULL)
00248             continue;
00249         mc->firstFree = i;
00250         break;
00251     }
00252 }
00253 
00254 #if !defined(DEBUG_MACROS)
00255 /*@only@*/
00256 static char * dupMacroEntry(MacroEntry me)
00257         /*@*/
00258 {
00259     char * t, * te;
00260     size_t nb;
00261 
00262 assert(me != NULL);
00263     nb = strlen(me->name) + sizeof("%") - 1;
00264     if (me->opts)
00265         nb += strlen(me->opts) + sizeof("()") - 1;
00266     if (me->body)
00267         nb += strlen(me->body) + sizeof("\t") - 1;
00268     nb++;
00269 
00270     t = te = xmalloc(nb);
00271     *te = '\0';
00272     te = stpcpy( stpcpy(te, "%"), me->name);
00273     if (me->opts)
00274         te = stpcpy( stpcpy( stpcpy(te, "("), me->opts), ")");
00275     if (me->body)
00276         te = stpcpy( stpcpy(te, "\t"), me->body);
00277     *te = '\0';
00278 
00279     return t;
00280 }
00281 #endif
00282 
00283 void
00284 rpmDumpMacroTable(MacroContext mc, FILE * fp)
00285 {
00286     int nempty = 0;
00287     int nactive = 0;
00288 
00289     if (mc == NULL) mc = rpmGlobalMacroContext;
00290     if (fp == NULL) fp = stderr;
00291     
00292     fprintf(fp, "========================\n");
00293     if (mc->macroTable != NULL) {
00294         int i;
00295         for (i = 0; i < mc->firstFree; i++) {
00296             MacroEntry me;
00297             if ((me = mc->macroTable[i]) == NULL) {
00298                 /* XXX this should never happen */
00299                 nempty++;
00300                 continue;
00301             }
00302             fprintf(fp, "%3d%c %s", me->level,
00303                         (me->used > 0 ? '=' : ':'), me->name);
00304             if (me->opts && *me->opts)
00305                     fprintf(fp, "(%s)", me->opts);
00306             if (me->body && *me->body)
00307                     fprintf(fp, "\t%s", me->body);
00308             fprintf(fp, "\n");
00309             nactive++;
00310         }
00311     }
00312     fprintf(fp, _("======================== active %d empty %d\n"),
00313                 nactive, nempty);
00314 }
00315 
00316 #if !defined(DEBUG_MACROS)
00317 int
00318 rpmGetMacroEntries(MacroContext mc, void * _mire, int used,
00319                 const char *** avp)
00320 {
00321 /*@-assignexpose -castexpose @*/
00322     miRE mire = (miRE) _mire;
00323 /*@=assignexpose =castexpose @*/
00324     const char ** av;
00325     int ac = 0;
00326     int i;
00327 
00328     if (mc == NULL)
00329         mc = rpmGlobalMacroContext;
00330 
00331     if (avp == NULL)
00332         return mc->firstFree;
00333 
00334     av = xcalloc( (mc->firstFree+1), sizeof(mc->macroTable[0]));
00335     if (mc->macroTable != NULL)
00336     for (i = 0; i < mc->firstFree; i++) {
00337         MacroEntry me;
00338         me = mc->macroTable[i];
00339         if (used > 0 && me->used < used)
00340             continue;
00341         if (used == 0 && me->used != 0)
00342             continue;
00343 #if !defined(DEBUG_MACROS)      /* XXX preserve standalone build */
00344         if (mire != NULL && mireRegexec(mire, me->name, 0) < 0)
00345             continue;
00346 #endif
00347         av[ac++] = dupMacroEntry(me);
00348     }
00349     av[ac] = NULL;
00350     *avp = av = xrealloc(av, (ac+1) * sizeof(*av));
00351     
00352     return ac;
00353 }
00354 #endif
00355 
00363 /*@dependent@*/ /*@null@*/
00364 static MacroEntry *
00365 findEntry(MacroContext mc, const char * name, size_t namelen)
00366         /*@*/
00367 {
00368     MacroEntry key, *ret;
00369 
00370 /*@-globs@*/
00371     if (mc == NULL) mc = rpmGlobalMacroContext;
00372 /*@=globs@*/
00373     if (mc->macroTable == NULL || mc->firstFree == 0)
00374         return NULL;
00375 
00376     if (namelen > 0) {
00377         char * t = strncpy(alloca(namelen + 1), name, namelen);
00378         t[namelen] = '\0';
00379         name = t;
00380     }
00381     
00382     key = memset(alloca(sizeof(*key)), 0, sizeof(*key));
00383     /*@-temptrans -assignexpose@*/
00384     key->name = (char *)name;
00385     /*@=temptrans =assignexpose@*/
00386     ret = (MacroEntry *) bsearch(&key, mc->macroTable, mc->firstFree,
00387                         sizeof(*(mc->macroTable)), compareMacroName);
00388     /* XXX TODO: find 1st empty slot and return that */
00389     return ret;
00390 }
00391 
00392 /* =============================================================== */
00393 
00401 /*@null@*/
00402 static char *
00403 rdcl(/*@returned@*/ char * buf, size_t size, FD_t fd)
00404         /*@globals fileSystem @*/
00405         /*@modifies buf, fileSystem @*/
00406 {
00407     char *q = buf - 1;          /* initialize just before buffer. */
00408     size_t nb = 0;
00409     size_t nread = 0;
00410     FILE * f = fdGetFILE(fd);
00411     int pc = 0, bc = 0;
00412     char *p = buf;
00413 
00414     if (f != NULL)
00415     do {
00416         *(++q) = '\0';                  /* terminate and move forward. */
00417         if (fgets(q, (int)size, f) == NULL)     /* read next line. */
00418             break;
00419         nb = strlen(q);
00420         nread += nb;                    /* trim trailing \r and \n */
00421         for (q += nb - 1; nb > 0 && iseol(*q); q--)
00422             nb--;
00423         for (; p <= q; p++) {
00424             switch (*p) {
00425                 case '\\':
00426                     switch (*(p+1)) {
00427                         case '\r': /*@switchbreak@*/ break;
00428                         case '\n': /*@switchbreak@*/ break;
00429                         case '\0': /*@switchbreak@*/ break;
00430                         default: p++; /*@switchbreak@*/ break;
00431                     }
00432                     /*@switchbreak@*/ break;
00433                 case '%':
00434                     switch (*(p+1)) {
00435                         case '{': p++, bc++; /*@switchbreak@*/ break;
00436                         case '(': p++, pc++; /*@switchbreak@*/ break;
00437                         case '%': p++; /*@switchbreak@*/ break;
00438                     }
00439                     /*@switchbreak@*/ break;
00440                 case '{': if (bc > 0) bc++; /*@switchbreak@*/ break;
00441                 case '}': if (bc > 0) bc--; /*@switchbreak@*/ break;
00442                 case '(': if (pc > 0) pc++; /*@switchbreak@*/ break;
00443                 case ')': if (pc > 0) pc--; /*@switchbreak@*/ break;
00444             }
00445         }
00446         if (nb == 0 || (*q != '\\' && !bc && !pc) || *(q+1) == '\0') {
00447             *(++q) = '\0';              /* trim trailing \r, \n */
00448             break;
00449         }
00450         q++; p++; nb++;                 /* copy newline too */
00451         size -= nb;
00452         if (*q == '\r')                 /* XXX avoid \r madness */
00453             *q = '\n';
00454     } while (size > 0);
00455     return (nread > 0 ? buf : NULL);
00456 }
00457 
00465 /*@null@*/
00466 static const char *
00467 matchchar(const char * p, char pl, char pr)
00468         /*@*/
00469 {
00470     int lvl = 0;
00471     char c;
00472 
00473     while ((c = *p++) != '\0') {
00474         if (c == '\\') {                /* Ignore escaped chars */
00475             p++;
00476             continue;
00477         }
00478         if (c == pr) {
00479             if (--lvl <= 0)     return --p;
00480         } else if (c == pl)
00481             lvl++;
00482     }
00483     return (const char *)NULL;
00484 }
00485 
00492 static void
00493 printMacro(MacroBuf mb, const char * s, const char * se)
00494         /*@globals fileSystem @*/
00495         /*@modifies fileSystem @*/
00496 {
00497     const char *senl;
00498     const char *ellipsis;
00499     int choplen;
00500 
00501     if (s >= se) {      /* XXX just in case */
00502         fprintf(stderr, _("%3d>%*s(empty)"), mb->depth,
00503                 (2 * mb->depth + 1), "");
00504         return;
00505     }
00506 
00507     if (s[-1] == '{')
00508         s--;
00509 
00510     /* Print only to first end-of-line (or end-of-string). */
00511     for (senl = se; *senl && !iseol(*senl); senl++)
00512         {};
00513 
00514     /* Limit trailing non-trace output */
00515     choplen = 61 - (2 * mb->depth);
00516     if ((senl - s) > choplen) {
00517         senl = s + choplen;
00518         ellipsis = "...";
00519     } else
00520         ellipsis = "";
00521 
00522     /* Substitute caret at end-of-macro position */
00523     fprintf(stderr, "%3d>%*s%%%.*s^", mb->depth,
00524         (2 * mb->depth + 1), "", (int)(se - s), s);
00525     if (se[1] != '\0' && (senl - (se+1)) > 0)
00526         fprintf(stderr, "%-.*s%s", (int)(senl - (se+1)), se+1, ellipsis);
00527     fprintf(stderr, "\n");
00528 }
00529 
00536 static void
00537 printExpansion(MacroBuf mb, const char * t, const char * te)
00538         /*@globals fileSystem @*/
00539         /*@modifies fileSystem @*/
00540 {
00541     const char *ellipsis;
00542     int choplen;
00543 
00544     if (!(te > t)) {
00545         fprintf(stderr, _("%3d<%*s(empty)\n"), mb->depth, (2 * mb->depth + 1), "");
00546         return;
00547     }
00548 
00549     /* Shorten output which contains newlines */
00550     while (te > t && iseol(te[-1]))
00551         te--;
00552     ellipsis = "";
00553     if (mb->depth > 0) {
00554         const char *tenl;
00555 
00556         /* Skip to last line of expansion */
00557         while ((tenl = strchr(t, '\n')) && tenl < te)
00558             t = ++tenl;
00559 
00560         /* Limit expand output */
00561         choplen = 61 - (2 * mb->depth);
00562         if ((te - t) > choplen) {
00563             te = t + choplen;
00564             ellipsis = "...";
00565         }
00566     }
00567 
00568     fprintf(stderr, "%3d<%*s", mb->depth, (2 * mb->depth + 1), "");
00569     if (te > t)
00570         fprintf(stderr, "%.*s%s", (int)(te - t), t, ellipsis);
00571     fprintf(stderr, "\n");
00572 }
00573 
00574 #define SKIPBLANK(_s, _c)       \
00575         /*@-globs@*/    /* FIX: __ctype_b */ \
00576         while (((_c) = (int) *(_s)) && isblank(_c)) \
00577                 (_s)++;         \
00578         /*@=globs@*/
00579 
00580 #define SKIPNONBLANK(_s, _c)    \
00581         /*@-globs@*/    /* FIX: __ctype_b */ \
00582         while (((_c) = (int) *(_s)) && !(isblank(_c) || iseol(_c))) \
00583                 (_s)++;         \
00584         /*@=globs@*/
00585 
00586 #define COPYNAME(_ne, _s, _c)   \
00587     {   SKIPBLANK(_s,_c);       \
00588         while(((_c) = (int) *(_s)) && (xisalnum(_c) || (_c) == (int) '_')) \
00589                 *(_ne)++ = *(_s)++; \
00590         *(_ne) = '\0';          \
00591     }
00592 
00593 #define COPYOPTS(_oe, _s, _c)   \
00594     {   while(((_c) = (int) *(_s)) && (_c) != (int) ')') \
00595                 *(_oe)++ = *(_s)++; \
00596         *(_oe) = '\0';          \
00597     }
00598 
00606 static int
00607 expandT(MacroBuf mb, const char * f, size_t flen)
00608         /*@globals rpmGlobalMacroContext, h_errno, fileSystem, internalState @*/
00609         /*@modifies mb, rpmGlobalMacroContext, fileSystem, internalState @*/
00610 {
00611     char *sbuf;
00612     const char *s = mb->s;
00613     int rc;
00614 
00615     sbuf = alloca(flen + 1);
00616     memset(sbuf, 0, (flen + 1));
00617 
00618     strncpy(sbuf, f, flen);
00619     sbuf[flen] = '\0';
00620     mb->s = sbuf;
00621     rc = expandMacro(mb);
00622     mb->s = s;
00623     return rc;
00624 }
00625 
00626 #if 0
00627 
00634 static int
00635 expandS(MacroBuf mb, char * tbuf, size_t tbuflen)
00636         /*@globals rpmGlobalMacroContext, fileSystem, internalState @*/
00637         /*@modifies mb, *tbuf, rpmGlobalMacroContext, fileSystem, internalState @*/
00638 {
00639     const char *t = mb->t;
00640     size_t nb = mb->nb;
00641     int rc;
00642 
00643     mb->t = tbuf;
00644     mb->nb = tbuflen;
00645     rc = expandMacro(mb);
00646     mb->t = t;
00647     mb->nb = nb;
00648     return rc;
00649 }
00650 #endif
00651 
00659 static int
00660 expandU(MacroBuf mb, char * u, size_t ulen)
00661         /*@globals rpmGlobalMacroContext, h_errno, fileSystem, internalState @*/
00662         /*@modifies mb, *u, rpmGlobalMacroContext, fileSystem, internalState @*/
00663 {
00664     const char *s = mb->s;
00665     char *t = mb->t;
00666     size_t nb = mb->nb;
00667     char *tbuf;
00668     int rc;
00669 
00670     tbuf = alloca(ulen + 1);
00671     memset(tbuf, 0, (ulen + 1));
00672 
00673     mb->s = u;
00674     mb->t = tbuf;
00675     mb->nb = ulen;
00676     rc = expandMacro(mb);
00677 
00678     tbuf[ulen] = '\0';  /* XXX just in case */
00679     if (ulen > mb->nb)
00680         strncpy(u, tbuf, (ulen - mb->nb + 1));
00681 
00682     mb->s = s;
00683     mb->t = t;
00684     mb->nb = nb;
00685 
00686     return rc;
00687 }
00688 
00696 static int
00697 doShellEscape(MacroBuf mb, const char * cmd, size_t clen)
00698         /*@globals rpmGlobalMacroContext, h_errno, fileSystem, internalState @*/
00699         /*@modifies mb, rpmGlobalMacroContext, fileSystem, internalState @*/
00700 {
00701     size_t bufn = _macro_BUFSIZ + clen;
00702     char * buf = alloca(bufn);
00703     FILE *shf;
00704     int rc;
00705     int c;
00706 
00707     strncpy(buf, cmd, clen);
00708     buf[clen] = '\0';
00709     rc = expandU(mb, buf, bufn);
00710     if (rc)
00711         return rc;
00712 
00713     if ((shf = popen(buf, "r")) == NULL)
00714         return 1;
00715     while(mb->nb > 0 && (c = fgetc(shf)) != EOF)
00716         SAVECHAR(mb, c);
00717     (void) pclose(shf);
00718 
00719     /* XXX delete trailing \r \n */
00720     while (iseol(mb->t[-1])) {
00721         *(mb->t--) = '\0';
00722         mb->nb++;
00723     }
00724     return 0;
00725 }
00726 
00735 /*@dependent@*/ static const char *
00736 doDefine(MacroBuf mb, /*@returned@*/ const char * se, int level, int expandbody)
00737         /*@globals rpmGlobalMacroContext, h_errno, internalState @*/
00738         /*@modifies mb, rpmGlobalMacroContext, internalState @*/
00739 {
00740     const char *s = se;
00741     size_t bufn = _macro_BUFSIZ;
00742     char *buf = alloca(bufn);
00743     char *n = buf, *ne;
00744     char *o = NULL, *oe;
00745     char *b, *be;
00746     int c;
00747     int oc = (int) ')';
00748 
00749     SKIPBLANK(s, c);
00750     if (c == (int) '.')         /* XXX readonly macros */
00751 /*@i@*/ *n++ = c = *s++;
00752     if (c == (int) '.')         /* XXX readonly macros */
00753 /*@i@*/ *n++ = c = *s++;
00754     ne = n;
00755 
00756     /* Copy name */
00757     COPYNAME(ne, s, c);
00758 
00759     /* Copy opts (if present) */
00760     oe = ne + 1;
00761     if (*s == '(') {
00762         s++;    /* skip ( */
00763         o = oe;
00764         COPYOPTS(oe, s, oc);
00765         s++;    /* skip ) */
00766     }
00767 
00768     /* Copy body, skipping over escaped newlines */
00769     b = be = oe + 1;
00770     SKIPBLANK(s, c);
00771     if (c == (int) '{') {       /* XXX permit silent {...} grouping */
00772         if ((se = matchchar(s, (char) c, '}')) == NULL) {
00773             rpmlog(RPMLOG_ERR,
00774                 _("Macro %%%s has unterminated body\n"), n);
00775             se = s;     /* XXX W2DO? */
00776             return se;
00777         }
00778         s++;    /* XXX skip { */
00779         strncpy(b, s, (se - s));
00780         b[se - s] = '\0';
00781         be += strlen(b);
00782         se++;   /* XXX skip } */
00783         s = se; /* move scan forward */
00784     } else {    /* otherwise free-field */
00785         int bc = 0, pc = 0;
00786         while (*s && (bc || pc || !iseol(*s))) {
00787             switch (*s) {
00788                 case '\\':
00789                     switch (*(s+1)) {
00790                         case '\0': /*@switchbreak@*/ break;
00791                         default: s++; /*@switchbreak@*/ break;
00792                     }
00793                     /*@switchbreak@*/ break;
00794                 case '%':
00795                     switch (*(s+1)) {
00796                         case '{': *be++ = *s++; bc++; /*@switchbreak@*/ break;
00797                         case '(': *be++ = *s++; pc++; /*@switchbreak@*/ break;
00798                         case '%': *be++ = *s++; /*@switchbreak@*/ break;
00799                     }
00800                     /*@switchbreak@*/ break;
00801                 case '{': if (bc > 0) bc++; /*@switchbreak@*/ break;
00802                 case '}': if (bc > 0) bc--; /*@switchbreak@*/ break;
00803                 case '(': if (pc > 0) pc++; /*@switchbreak@*/ break;
00804                 case ')': if (pc > 0) pc--; /*@switchbreak@*/ break;
00805             }
00806             *be++ = *s++;
00807         }
00808         *be = '\0';
00809 
00810         if (bc || pc) {
00811             rpmlog(RPMLOG_ERR,
00812                 _("Macro %%%s has unterminated body\n"), n);
00813             se = s;     /* XXX W2DO? */
00814             return se;
00815         }
00816 
00817         /* Trim trailing blanks/newlines */
00818 /*@-globs@*/
00819         while (--be >= b && (c = (int) *be) && (isblank(c) || iseol(c)))
00820             {};
00821 /*@=globs@*/
00822         *(++be) = '\0'; /* one too far */
00823     }
00824 
00825     /* Move scan over body */
00826     while (iseol(*s))
00827         s++;
00828     se = s;
00829 
00830     /* Names must start with alphabetic or _ and be at least 3 chars */
00831     if (!((c = (int) *n) && (xisalpha(c) || c == (int) '_') && (ne - n) > 2)) {
00832         rpmlog(RPMLOG_ERR,
00833                 _("Macro %%%s has illegal name (%%define)\n"), n);
00834         return se;
00835     }
00836 
00837     /* Options must be terminated with ')' */
00838     if (o && oc != (int) ')') {
00839         rpmlog(RPMLOG_ERR, _("Macro %%%s has unterminated opts\n"), n);
00840         return se;
00841     }
00842 
00843     if ((be - b) < 1) {
00844         rpmlog(RPMLOG_ERR, _("Macro %%%s has empty body\n"), n);
00845         return se;
00846     }
00847 
00848 /*@-modfilesys@*/
00849     if (expandbody && expandU(mb, b, (&buf[bufn] - b))) {
00850         rpmlog(RPMLOG_ERR, _("Macro %%%s failed to expand\n"), n);
00851         return se;
00852     }
00853 /*@=modfilesys@*/
00854 
00855     if (n != buf)               /* XXX readonly macros */
00856         n--;
00857     if (n != buf)               /* XXX readonly macros */
00858         n--;
00859     addMacro(mb->mc, n, o, b, (level - 1));
00860 
00861     return se;
00862 }
00863 
00870 /*@dependent@*/ static const char *
00871 doUndefine(MacroContext mc, /*@returned@*/ const char * se)
00872         /*@globals rpmGlobalMacroContext @*/
00873         /*@modifies mc, rpmGlobalMacroContext @*/
00874 {
00875     const char *s = se;
00876     char *buf = alloca(_macro_BUFSIZ);
00877     char *n = buf, *ne = n;
00878     int c;
00879 
00880     COPYNAME(ne, s, c);
00881 
00882     /* Move scan over body */
00883     while (iseol(*s))
00884         s++;
00885     se = s;
00886 
00887     /* Names must start with alphabetic or _ and be at least 3 chars */
00888     if (!((c = (int) *n) && (xisalpha(c) || c == (int) '_') && (ne - n) > 2)) {
00889         rpmlog(RPMLOG_ERR,
00890                 _("Macro %%%s has illegal name (%%undefine)\n"), n);
00891         return se;
00892     }
00893 
00894     delMacro(mc, n);
00895 
00896     return se;
00897 }
00898 
00899 #ifdef  DYING
00900 static void
00901 dumpME(const char * msg, MacroEntry me)
00902         /*@globals fileSystem @*/
00903         /*@modifies fileSystem @*/
00904 {
00905     if (msg)
00906         fprintf(stderr, "%s", msg);
00907     fprintf(stderr, "\tme %p", me);
00908     if (me)
00909         fprintf(stderr,"\tname %p(%s) prev %p",
00910                 me->name, me->name, me->prev);
00911     fprintf(stderr, "\n");
00912 }
00913 #endif
00914 
00923 static void
00924 pushMacro(/*@out@*/ MacroEntry * mep, const char * n, /*@null@*/ const char * o,
00925                 /*@null@*/ const char * b, int level)
00926         /*@modifies *mep @*/
00927 {
00928     MacroEntry prev = (mep && *mep ? *mep : NULL);
00929     MacroEntry me = (MacroEntry) xmalloc(sizeof(*me));
00930     const char *name = n;
00931 
00932     if (*name == '.')           /* XXX readonly macros */
00933         name++;
00934     if (*name == '.')           /* XXX readonly macros */
00935         name++;
00936 
00937     /*@-assignexpose@*/
00938     me->prev = prev;
00939     /*@=assignexpose@*/
00940     me->name = (prev ? prev->name : xstrdup(name));
00941     me->opts = (o ? xstrdup(o) : NULL);
00942     me->body = xstrdup(b ? b : "");
00943     me->used = 0;
00944     me->level = level;
00945     me->flags = (name != n);
00946     if (mep)
00947         *mep = me;
00948     else
00949         me = _free(me);
00950 }
00951 
00956 static void
00957 popMacro(MacroEntry * mep)
00958         /*@modifies *mep @*/
00959 {
00960         MacroEntry me = (*mep ? *mep : NULL);
00961 
00962         if (me) {
00963                 /* XXX cast to workaround const */
00964                 /*@-onlytrans@*/
00965                 if ((*mep = me->prev) == NULL)
00966                         me->name = _free(me->name);
00967                 me->opts = _free(me->opts);
00968                 me->body = _free(me->body);
00969                 me = _free(me);
00970                 /*@=onlytrans@*/
00971         }
00972 }
00973 
00978 static void
00979 freeArgs(MacroBuf mb)
00980         /*@modifies mb @*/
00981 {
00982     MacroContext mc = mb->mc;
00983     int ndeleted = 0;
00984     int i;
00985 
00986     if (mc == NULL || mc->macroTable == NULL)
00987         return;
00988 
00989     /* Delete dynamic macro definitions */
00990     for (i = 0; i < mc->firstFree; i++) {
00991         MacroEntry *mep, me;
00992         int skiptest = 0;
00993         mep = &mc->macroTable[i];
00994         me = *mep;
00995 
00996         if (me == NULL)         /* XXX this should never happen */
00997             continue;
00998         if (me->level < mb->depth)
00999             continue;
01000         if (strlen(me->name) == 1 && strchr("#*0", *me->name)) {
01001             if (*me->name == '*' && me->used > 0)
01002                 skiptest = 1; /* XXX skip test for %# %* %0 */
01003         } else if (!skiptest && me->used <= 0) {
01004 #if NOTYET
01005             rpmlog(RPMLOG_ERR,
01006                         _("Macro %%%s (%s) was not used below level %d\n"),
01007                         me->name, me->body, me->level);
01008 #endif
01009         }
01010         popMacro(mep);
01011         if (!(mep && *mep))
01012             ndeleted++;
01013     }
01014 
01015     /* If any deleted macros, sort macro table */
01016     if (ndeleted)
01017         sortMacroTable(mc);
01018 }
01019 
01029 /*@dependent@*/ static const char *
01030 grabArgs(MacroBuf mb, const MacroEntry me, /*@returned@*/ const char * se,
01031                 const char * lastc)
01032         /*@globals rpmGlobalMacroContext, fileSystem, internalState @*/
01033         /*@modifies mb, rpmGlobalMacroContext, fileSystem, internalState @*/
01034 {
01035     poptContext optCon;
01036     struct poptOption *optTbl;
01037     size_t bufn = _macro_BUFSIZ;
01038     char *buf = alloca(bufn);
01039     char *b, *be;
01040     char aname[16];
01041     const char *opts;
01042     int argc = 0;
01043     const char **argv;
01044     int c;
01045     unsigned int popt_flags;
01046 
01047     /* Copy macro name as argv[0], save beginning of args.  */
01048     buf[0] = '\0';
01049     b = be = stpcpy(buf, me->name);
01050 
01051     addMacro(mb->mc, "0", NULL, buf, mb->depth);
01052     
01053     argc = 1;   /* XXX count argv[0] */
01054 
01055     /* Copy args into buf until lastc */
01056     *be++ = ' ';
01057     while ((c = (int) *se++) != (int) '\0' && (se-1) != lastc) {
01058 /*@-globs@*/
01059         if (!isblank(c)) {
01060             *be++ = (char) c;
01061             continue;
01062         }
01063 /*@=globs@*/
01064         /* c is blank */
01065         if (be[-1] == ' ')
01066             continue;
01067         /* a word has ended */
01068         *be++ = ' ';
01069         argc++;
01070     }
01071     if (c == (int) '\0') se--;  /* one too far */
01072     if (be[-1] != ' ')
01073         argc++, be++;           /* last word has not trailing ' ' */
01074     be[-1] = '\0';
01075     if (*b == ' ') b++;         /* skip the leading ' ' */
01076 
01077 /*
01078  * The macro %* analoguous to the shell's $* means "Pass all non-macro
01079  * parameters." Consequently, there needs to be a macro that means "Pass all
01080  * (including macro parameters) options". This is useful for verifying
01081  * parameters during expansion and yet transparently passing all parameters
01082  * through for higher level processing (e.g. %description and/or %setup).
01083  * This is the (potential) justification for %{**} ...
01084  */
01085     /* Add unexpanded args as macro */
01086     addMacro(mb->mc, "**", NULL, b, mb->depth);
01087 
01088 #ifdef NOTYET
01089     /* XXX if macros can be passed as args ... */
01090     expandU(mb, buf, bufn);
01091 #endif
01092 
01093     /* Build argv array */
01094     argv = (const char **) alloca((argc + 1) * sizeof(*argv));
01095     be[-1] = ' '; /* assert((be - 1) == (b + strlen(b) == buf + strlen(buf))) */
01096     be[0] = '\0';
01097 
01098     b = buf;
01099     for (c = 0; c < argc; c++) {
01100         argv[c] = b;
01101         b = strchr(b, ' ');
01102         *b++ = '\0';
01103     }
01104     /* assert(b == be);  */
01105     argv[argc] = NULL;
01106 
01107     /* '+' as the first character means that options are recognized
01108      * only before positional arguments, as POSIX requires.
01109     */
01110     popt_flags = POPT_CONTEXT_NO_EXEC;
01111 #if defined(RPM_VENDOR_OPENPKG) /* always-strict-posix-option-parsing */
01112     popt_flags |= POPT_CONTEXT_POSIXMEHARDER;
01113 #endif
01114     if (me->opts[0] == '+') popt_flags |= POPT_CONTEXT_POSIXMEHARDER;
01115 
01116     /* Count the number of short options. */
01117     opts = me->opts;
01118     if (*opts == '+') opts++;
01119     for (c = 0; *opts != '\0'; opts++)
01120         if (*opts != ':') c++;
01121 
01122     /* Set up popt option table. */
01123     optTbl = xcalloc(sizeof(*optTbl), (c + 1));
01124     opts = me->opts;
01125     if (*opts == '+') opts++;
01126     for (c = 0; *opts != '\0'; opts++) {
01127         if (*opts == ':') continue;
01128         optTbl[c].shortName = opts[0];
01129         optTbl[c].val = (int) opts[0];
01130         if (opts[1] == ':')
01131             optTbl[c].argInfo = POPT_ARG_STRING;
01132         c++;
01133     }
01134 
01135     /* Parse the options, defining option macros. */
01136 /*@-nullstate@*/
01137     optCon = poptGetContext(argv[0], argc, argv, optTbl, popt_flags);
01138 /*@=nullstate@*/
01139     while ((c = poptGetNextOpt(optCon)) > 0) {
01140         const char * optArg = poptGetOptArg(optCon);
01141         *be++ = '-';
01142         *be++ = (char) c;
01143         if (optArg != NULL) {
01144             *be++ = ' ';
01145             be = stpcpy(be, optArg);
01146         }
01147         *be++ = '\0';
01148         aname[0] = '-'; aname[1] = (char)c; aname[2] = '\0';
01149         addMacro(mb->mc, aname, NULL, b, mb->depth);
01150         if (optArg != NULL) {
01151             aname[0] = '-'; aname[1] = (char)c; aname[2] = '*'; aname[3] = '\0';
01152             addMacro(mb->mc, aname, NULL, optArg, mb->depth);
01153         }
01154         be = b; /* reuse the space */
01155 /*@-dependenttrans -modobserver -observertrans @*/
01156         optArg = _free(optArg);
01157 /*@=dependenttrans =modobserver =observertrans @*/
01158     }
01159     if (c < -1) {
01160         rpmlog(RPMLOG_ERR, _("Unknown option in macro %s(%s): %s: %s\n"),
01161                 me->name, me->opts,
01162                 poptBadOption(optCon, POPT_BADOPTION_NOALIAS), poptStrerror(c));
01163         goto exit;
01164     }
01165 
01166     argv = poptGetArgs(optCon);
01167     argc = 0;
01168     if (argv != NULL)
01169     for (c = 0; argv[c] != NULL; c++)
01170         argc++;
01171     
01172     /* Add arg count as macro. */
01173     sprintf(aname, "%d", argc);
01174     addMacro(mb->mc, "#", NULL, aname, mb->depth);
01175 
01176     /* Add macro for each arg. Concatenate args for %*. */
01177     if (be) {
01178         *be = '\0';
01179         if (argv != NULL)
01180         for (c = 0; c < argc; c++) {
01181             sprintf(aname, "%d", (c + 1));
01182             addMacro(mb->mc, aname, NULL, argv[c], mb->depth);
01183             if (be != b) *be++ = ' '; /* Add space between args */
01184             be = stpcpy(be, argv[c]);
01185         }
01186     }
01187 
01188     /* Add unexpanded args as macro. */
01189     addMacro(mb->mc, "*", NULL, b, mb->depth);
01190 
01191 exit:
01192     optCon = poptFreeContext(optCon);
01193     optTbl = _free(optTbl);
01194     return se;
01195 }
01196 
01204 static void
01205 doOutput(MacroBuf mb, int waserror, const char * msg, size_t msglen)
01206         /*@globals rpmGlobalMacroContext, h_errno, fileSystem, internalState @*/
01207         /*@modifies mb, rpmGlobalMacroContext, fileSystem, internalState @*/
01208 {
01209     size_t bufn = _macro_BUFSIZ + msglen;
01210     char *buf = alloca(bufn);
01211 
01212     strncpy(buf, msg, msglen);
01213     buf[msglen] = '\0';
01214     (void) expandU(mb, buf, bufn);
01215     if (waserror)
01216         rpmlog(RPMLOG_ERR, "%s\n", buf);
01217     else
01218         fprintf(stderr, "%s", buf);
01219 }
01220 
01230 static void
01231 doFoo(MacroBuf mb, int negate, const char * f, size_t fn,
01232                 /*@null@*/ const char * g, size_t gn)
01233         /*@globals rpmGlobalMacroContext, h_errno, fileSystem, internalState @*/
01234         /*@modifies mb, rpmGlobalMacroContext, fileSystem, internalState @*/
01235 {
01236     size_t bufn = _macro_BUFSIZ + fn + gn;
01237     char * buf = alloca(bufn);
01238     char *b = NULL, *be;
01239     int c;
01240 
01241     buf[0] = '\0';
01242     if (g != NULL) {
01243         strncpy(buf, g, gn);
01244         buf[gn] = '\0';
01245         (void) expandU(mb, buf, bufn);
01246     }
01247     if (fn > 5 && STREQ("patch", f, 5) && xisdigit((int)f[5])) {
01248         /* Skip leading zeros */
01249         for (c = 5; c < (int)(fn-1) && f[c] == '0' && xisdigit((int)f[c+1]);)
01250             c++;
01251         b = buf;
01252         be = stpncpy( stpcpy(b, "%patch -P "), f+c, fn-c);
01253         *be = '\0';
01254     } else
01255     if (STREQ("basename", f, fn)) {
01256         if ((b = strrchr(buf, '/')) == NULL)
01257             b = buf;
01258         else
01259             b++;
01260     } else if (STREQ("dirname", f, fn)) {
01261         if ((b = strrchr(buf, '/')) != NULL)
01262             *b = '\0';
01263         b = buf;
01264 #if !defined(__LCLINT__) /* XXX LCL: realpath(3) annotations are buggy. */
01265     } else if (STREQ("realpath", f, fn)) {
01266         char rp[PATH_MAX];
01267         char *cp;
01268         size_t l;
01269         if ((cp = realpath(buf, rp)) != NULL) {
01270             l = strlen(cp);
01271             if ((size_t)(l+1) <= bufn) {
01272                 memcpy(buf, cp, l+1);
01273                 b = buf;
01274             }
01275         }
01276 #endif
01277     } else if (STREQ("getenv", f, fn)) {
01278         char *cp;
01279         if ((cp = getenv(buf)) != NULL)
01280             b = cp;
01281     } else if (STREQ("shrink", f, fn)) {
01282         /*
01283          * shrink body by removing all leading and trailing whitespaces and
01284          * reducing intermediate whitespaces to a single space character.
01285          */
01286         int i, j, k, was_space = 0;
01287         for (i = 0, j = 0, k = (int)strlen(buf); i < k; ) {
01288             if (xisspace((int)(buf[i]))) {
01289                 was_space = 1;
01290                 i++;
01291                 continue;
01292             }
01293             else if (was_space) {
01294                 was_space = 0;
01295                 if (j > 0) /* remove leading blanks at all */
01296                     buf[j++] = ' ';
01297                 /* fallthrough */
01298             }
01299             buf[j++] = buf[i++];
01300         }
01301         buf[j] = '\0';
01302         b = buf;
01303     } else if (STREQ("suffix", f, fn)) {
01304         if ((b = strrchr(buf, '.')) != NULL)
01305             b++;
01306     } else if (STREQ("expand", f, fn)) {
01307         b = buf;
01308     } else if (STREQ("verbose", f, fn)) {
01309 #if defined(RPMLOG_MASK)
01310         if (negate)
01311             b = ((rpmlogSetMask(0) >= RPMLOG_MASK( RPMLOG_INFO )) ? NULL : buf);
01312         else
01313             b = ((rpmlogSetMask(0) >= RPMLOG_MASK( RPMLOG_INFO )) ? buf : NULL);
01314 #else
01315         /* XXX assume always verbose when running standalone */
01316         b = (negate) ? NULL : buf;
01317 #endif
01318     } else if (STREQ("url2path", f, fn) || STREQ("u2p", f, fn)) {
01319         int ut = urlPath(buf, (const char **)&b);
01320         ut = ut;        /* XXX quiet gcc */
01321         if (*b == '\0') b = "/";
01322     } else if (STREQ("uncompress", f, fn)) {
01323         rpmCompressedMagic compressed = COMPRESSED_OTHER;
01324 /*@-globs@*/
01325         for (b = buf; (c = (int)*b) && isblank(c);)
01326             b++;
01327         /* XXX FIXME: file paths with embedded white space needs rework. */
01328         for (be = b; (c = (int)*be) && !isblank(c);)
01329             be++;
01330 /*@=globs@*/
01331         *be++ = '\0';
01332         (void) isCompressed(b, &compressed);
01333         switch(compressed) {
01334         default:
01335         case 0: /* COMPRESSED_NOT */
01336             sprintf(be, "%%__cat %s", b);
01337             break;
01338         case 1: /* COMPRESSED_OTHER */
01339             sprintf(be, "%%__gzip -dc '%s'", b);
01340             break;
01341         case 2: /* COMPRESSED_BZIP2 */
01342             sprintf(be, "%%__bzip2 -dc '%s'", b);
01343             break;
01344         case 3: /* COMPRESSED_ZIP */
01345             sprintf(be, "%%__unzip -qq '%s'", b);
01346             break;
01347         case 4: /* COMPRESSED_LZOP */
01348             sprintf(be, "%%__lzop -dc '%s'", b);
01349             break;
01350         case 5: /* COMPRESSED_LZMA */
01351             sprintf(be, "%%__lzma -dc '%s'", b);
01352             break;
01353         case 6: /* COMPRESSED_XZ */
01354             sprintf(be, "%%__xz -dc '%s'", b);
01355             break;
01356         }
01357         b = be;
01358     } else if (STREQ("mkstemp", f, fn)) {
01359 /*@-globs@*/
01360         for (b = buf; (c = (int)*b) && isblank(c);)
01361             b++;
01362         /* XXX FIXME: file paths with embedded white space needs rework. */
01363         for (be = b; (c = (int)*be) && !isblank(c);)
01364             be++;
01365 /*@=globs@*/
01366 #if defined(HAVE_MKSTEMP)
01367         (void) close(mkstemp(b));
01368 #else
01369         (void) mktemp(b);
01370 #endif
01371     } else if (STREQ("mkdtemp", f, fn)) {
01372 /*@-globs@*/
01373         for (b = buf; (c = (int)*b) && isblank(c);)
01374             b++;
01375         /* XXX FIXME: file paths with embedded white space needs rework. */
01376         for (be = b; (c = (int)*be) && !isblank(c);)
01377             be++;
01378 /*@=globs@*/
01379 #if defined(HAVE_MKDTEMP) && !defined(__LCLINT__)
01380         if (mkdtemp(b) == NULL)
01381             perror("mkdtemp");
01382 #else
01383         if ((b = tmpnam(b)) != NULL)
01384             (void) mkdir(b, 0700);      /* XXX S_IWRSXU is not included. */
01385 #endif
01386     } else if (STREQ("uuid", f, fn)) {
01387         int uuid_version;
01388         const char *uuid_ns;
01389         const char *uuid_data;
01390         char *cp;
01391         size_t n;
01392 
01393         uuid_version = 1;
01394         uuid_ns = NULL;
01395         uuid_data = NULL;
01396         cp = buf;
01397         if ((n = strspn(cp, " \t\n")) > 0)
01398             cp += n;
01399         if ((n = strcspn(cp, " \t\n")) > 0) {
01400             uuid_version = (int)strtol(cp, (char **)NULL, 10);
01401             cp += n;
01402             if ((n = strspn(cp, " \t\n")) > 0)
01403                 cp += n;
01404             if ((n = strcspn(cp, " \t\n")) > 0) {
01405                 uuid_ns = cp;
01406                 cp += n;
01407                 *cp++ = '\0';
01408                 if ((n = strspn(cp, " \t\n")) > 0)
01409                     cp += n;
01410                 if ((n = strcspn(cp, " \t\n")) > 0) {
01411                     uuid_data = cp;
01412                     cp += n;
01413                     *cp++ = '\0';
01414                 }
01415             }
01416         }
01417 /*@-nullpass@*/ /* FIX: uuid_ns may be NULL */
01418         if (rpmuuidMake(uuid_version, uuid_ns, uuid_data, buf, NULL))
01419             rpmlog(RPMLOG_ERR, "failed to create UUID\n");
01420         else
01421             b = buf;
01422 /*@=nullpass@*/
01423     } else if (STREQ("S", f, fn)) {
01424         for (b = buf; (c = (int)*b) && xisdigit(c);)
01425             b++;
01426         if (!c) {       /* digit index */
01427             b++;
01428             sprintf(b, "%%SOURCE%s", buf);
01429         } else
01430             b = buf;
01431     } else if (STREQ("P", f, fn)) {
01432         for (b = buf; (c = (int) *b) && xisdigit(c);)
01433             b++;
01434         if (!c) {       /* digit index */
01435             b++;
01436             sprintf(b, "%%PATCH%s", buf);
01437         } else
01438             b = buf;
01439     } else if (STREQ("F", f, fn)) {
01440         b = buf + strlen(buf) + 1;
01441         sprintf(b, "file%s.file", buf);
01442     }
01443 
01444     if (b) {
01445         (void) expandT(mb, b, strlen(b));
01446     }
01447 }
01448 
01449 static int expandFIFO(MacroBuf mb, MacroEntry me, const char *g, size_t gn)
01450         /*@globals rpmGlobalMacroContext, h_errno, fileSystem, internalState @*/
01451         /*@modifies mb, rpmGlobalMacroContext, fileSystem, internalState @*/
01452 {
01453     int rc = 0;
01454 
01455     if (me) {
01456         if (me->prev) {
01457             rc = expandFIFO(mb, me->prev, g, gn);
01458             rc = expandT(mb, g, gn);
01459         }
01460         rc = expandT(mb, me->body, strlen(me->body));
01461     }
01462     return rc;
01463 }
01464 
01465 #if !defined(DEBUG_MACROS)
01466 /* =============================================================== */
01467 /* XXX dupe'd to avoid change in linkage conventions. */
01468 
01469 #define POPT_ERROR_NOARG        -10     
01470 #define POPT_ERROR_BADQUOTE     -15     
01471 #define POPT_ERROR_MALLOC       -21     
01473 #define POPT_ARGV_ARRAY_GROW_DELTA 5
01474 
01475 static int XpoptDupArgv(int argc, char **argv,
01476                 int * argcPtr, char *** argvPtr)
01477         /*@modifies *argcPtr, *argvPtr @*/
01478 {
01479     size_t nb = (argc + 1) * sizeof(*argv);
01480     char ** argv2;
01481     char * dst;
01482     int i;
01483 
01484     if (argc <= 0 || argv == NULL)      /* XXX can't happen */
01485         return POPT_ERROR_NOARG;
01486     for (i = 0; i < argc; i++) {
01487         if (argv[i] == NULL)
01488             return POPT_ERROR_NOARG;
01489         nb += strlen(argv[i]) + 1;
01490     }
01491         
01492     dst = xmalloc(nb);
01493     if (dst == NULL)                    /* XXX can't happen */
01494         return POPT_ERROR_MALLOC;
01495     argv2 = (void *) dst;
01496     dst += (argc + 1) * sizeof(*argv);
01497 
01498     for (i = 0; i < argc; i++) {
01499         argv2[i] = dst;
01500         dst += strlen(strcpy(dst, argv[i])) + 1;
01501     }
01502     argv2[argc] = NULL;
01503 
01504     if (argvPtr) {
01505         *argvPtr = argv2;
01506     } else {
01507         free(argv2);
01508         argv2 = NULL;
01509     }
01510     if (argcPtr)
01511         *argcPtr = argc;
01512     return 0;
01513 }
01514 
01515 static int XpoptParseArgvString(const char * s, int * argcPtr, char *** argvPtr)
01516         /*@modifies *argcPtr, *argvPtr @*/
01517 {
01518     const char * src;
01519     char quote = '\0';
01520     int argvAlloced = POPT_ARGV_ARRAY_GROW_DELTA;
01521     char ** argv = xmalloc(sizeof(*argv) * argvAlloced);
01522     int argc = 0;
01523     size_t buflen = strlen(s) + 1;
01524     char * buf = memset(alloca(buflen), 0, buflen);
01525     int rc = POPT_ERROR_MALLOC;
01526 
01527     if (argv == NULL) return rc;
01528     argv[argc] = buf;
01529 
01530     for (src = s; *src != '\0'; src++) {
01531         if (quote == *src) {
01532             quote = '\0';
01533         } else if (quote != '\0') {
01534             if (*src == '\\') {
01535                 src++;
01536                 if (!*src) {
01537                     rc = POPT_ERROR_BADQUOTE;
01538                     goto exit;
01539                 }
01540                 if (*src != quote) *buf++ = '\\';
01541             }
01542             *buf++ = *src;
01543         } else if (isspace(*src)) {
01544             if (*argv[argc] != '\0') {
01545                 buf++, argc++;
01546                 if (argc == argvAlloced) {
01547                     argvAlloced += POPT_ARGV_ARRAY_GROW_DELTA;
01548                     argv = realloc(argv, sizeof(*argv) * argvAlloced);
01549                     if (argv == NULL) goto exit;
01550                 }
01551                 argv[argc] = buf;
01552             }
01553         } else switch (*src) {
01554           case '"':
01555           case '\'':
01556             quote = *src;
01557             /*@switchbreak@*/ break;
01558           case '\\':
01559             src++;
01560             if (!*src) {
01561                 rc = POPT_ERROR_BADQUOTE;
01562                 goto exit;
01563             }
01564             /*@fallthrough@*/
01565           default:
01566             *buf++ = *src;
01567             /*@switchbreak@*/ break;
01568         }
01569     }
01570 
01571     if (strlen(argv[argc])) {
01572         argc++, buf++;
01573     }
01574 
01575     rc = XpoptDupArgv(argc, argv, argcPtr, argvPtr);
01576 
01577 exit:
01578     if (argv) free(argv);
01579     return rc;
01580 }
01581 #endif  /* !defined(DEBUG_MACROS) */
01582 
01590 #if defined(WITH_AUGEAS) || defined(WITH_FICL) || defined(WITH_GPSEE) || defined(WITH_PERLEMBED) || defined(WITH_PYTHONEMBED) || defined(WITH_RUBYEMBED) || defined(WITH_SQLITE) || defined(WITH_SQUIRREL) || defined(WITH_TCL)
01591 static char * parseEmbedded(const char * s, size_t nb, char *** avp)
01592         /*@*/
01593 {
01594     char * script = NULL;
01595     const char * se;
01596 
01597     /* XXX FIXME: args might have embedded : too. */
01598     for (se = s + 1; se < (s+nb); se++)
01599     switch (*se) {
01600     default:    continue;       /*@notreached@*/ break;
01601     case ':':   goto bingo;     /*@notreached@*/ break;
01602     }
01603 
01604 bingo:
01605     {   size_t na = (size_t)(se-s-1);
01606         char * args = NULL;
01607         int ac;
01608         int rc;
01609 
01610         args = memcpy(xmalloc(na+1), s+1, na);
01611         args[na] = '\0';
01612 
01613         ac = 0;
01614         rc = XpoptParseArgvString(args, &ac, avp);
01615         args = _free(args);
01616         nb -= na;
01617     }
01618 
01619     nb -= 3;
01620     script = memcpy(xmalloc(nb+1), se+1, nb+1);
01621     script[nb] = '\0';
01622     return script;
01623 }
01624 #endif
01625 
01632 static int
01633 expandMacro(MacroBuf mb)
01634         /*@globals rpmGlobalMacroContext,
01635                 print_macro_trace, print_expand_trace, h_errno,
01636                 fileSystem, internalState @*/
01637         /*@modifies mb, rpmGlobalMacroContext,
01638                 print_macro_trace, print_expand_trace,
01639                 fileSystem, internalState @*/
01640 {
01641     MacroEntry *mep;
01642     MacroEntry me;
01643     const char *s = mb->s, *se;
01644     const char *f, *fe;
01645     const char *g, *ge;
01646     size_t fn, gn;
01647     char *t = mb->t;    /* save expansion pointer for printExpand */
01648     int c;
01649     int rc = 0;
01650     int negate;
01651     int stackarray;
01652     const char * lastc;
01653     int chkexist;
01654 
01655     if (++mb->depth > max_macro_depth) {
01656         rpmlog(RPMLOG_ERR,
01657                 _("Recursion depth(%d) greater than max(%d)\n"),
01658                 mb->depth, max_macro_depth);
01659         mb->depth--;
01660         mb->expand_trace = 1;
01661         return 1;
01662     }
01663 
01664     while (rc == 0 && mb->nb > 0 && (c = (int)*s) != (int)'\0') {
01665         s++;
01666         /* Copy text until next macro */
01667         switch(c) {
01668         case '%':
01669                 if (*s != '\0') {       /* Ensure not end-of-string. */
01670                     if (*s != '%')
01671                         /*@switchbreak@*/ break;
01672                     s++;        /* skip first % in %% */
01673                 }
01674                 /*@fallthrough@*/
01675         default:
01676                 SAVECHAR(mb, c);
01677                 continue;
01678                 /*@notreached@*/ /*@switchbreak@*/ break;
01679         }
01680 
01681         /* Expand next macro */
01682         f = fe = NULL;
01683         g = ge = NULL;
01684         if (mb->depth > 1)      /* XXX full expansion for outermost level */
01685                 t = mb->t;      /* save expansion pointer for printExpand */
01686         stackarray = chkexist = negate = 0;
01687         lastc = NULL;
01688         switch ((c = (int) *s)) {
01689         default:                /* %name substitution */
01690                 while (*s != '\0' && strchr("!?@", *s) != NULL) {
01691                         switch(*s++) {
01692                         case '@':
01693                                 stackarray = ((stackarray + 1) % 2);
01694                                 /*@switchbreak@*/ break;
01695                         case '!':
01696                                 negate = ((negate + 1) % 2);
01697                                 /*@switchbreak@*/ break;
01698                         case '?':
01699                                 chkexist++;
01700                                 /*@switchbreak@*/ break;
01701                         }
01702                 }
01703                 f = se = s;
01704                 if (*se == '-')
01705                         se++;
01706                 while((c = (int) *se) && (xisalnum(c) || c == (int) '_'))
01707                         se++;
01708                 /* Recognize non-alnum macros too */
01709                 switch (*se) {
01710                 case '*':
01711                         se++;
01712                         if (*se == '*') se++;
01713                         /*@innerbreak@*/ break;
01714                 case '#':
01715                         se++;
01716                         /*@innerbreak@*/ break;
01717                 default:
01718                         /*@innerbreak@*/ break;
01719                 }
01720                 fe = se;
01721                 /* For "%name " macros ... */
01722 /*@-globs@*/
01723                 if ((c = (int) *fe) && isblank(c))
01724                         if ((lastc = strchr(fe,'\n')) == NULL)
01725                                 lastc = strchr(fe, '\0');
01726 /*@=globs@*/
01727                 /*@switchbreak@*/ break;
01728         case '(':               /* %(...) shell escape */
01729                 if ((se = matchchar(s, (char)c, ')')) == NULL) {
01730                         rpmlog(RPMLOG_ERR,
01731                                 _("Unterminated %c: %s\n"), (char)c, s);
01732                         rc = 1;
01733                         continue;
01734                 }
01735                 if (mb->macro_trace)
01736                         printMacro(mb, s, se+1);
01737 
01738                 s++;    /* skip ( */
01739                 rc = doShellEscape(mb, s, (se - s));
01740                 se++;   /* skip ) */
01741 
01742                 s = se;
01743                 continue;
01744                 /*@notreached@*/ /*@switchbreak@*/ break;
01745         case '{':               /* %{...}/%{...:...} substitution */
01746                 if ((se = matchchar(s, (char)c, '}')) == NULL) {
01747                         rpmlog(RPMLOG_ERR,
01748                                 _("Unterminated %c: %s\n"), (char)c, s);
01749                         rc = 1;
01750                         continue;
01751                 }
01752                 f = s+1;/* skip { */
01753                 se++;   /* skip } */
01754                 while (strchr("!?@", *f) != NULL) {
01755                         switch(*f++) {
01756                         case '@':
01757                                 stackarray = ((stackarray + 1) % 2);
01758                                 /*@switchbreak@*/ break;
01759                         case '!':
01760                                 negate = ((negate + 1) % 2);
01761                                 /*@switchbreak@*/ break;
01762                         case '?':
01763                                 chkexist++;
01764                                 /*@switchbreak@*/ break;
01765                         }
01766                 }
01767                 /* Find end-of-expansion, handle %{foo:bar} expansions. */
01768                 for (fe = f; (c = (int) *fe) && !strchr(" :}", c);)
01769                         fe++;
01770                 switch (c) {
01771                 case ':':
01772                         g = fe + 1;
01773                         ge = se - 1;
01774                         /*@innerbreak@*/ break;
01775                 case ' ':
01776                         lastc = se-1;
01777                         /*@innerbreak@*/ break;
01778                 default:
01779                         /*@innerbreak@*/ break;
01780                 }
01781                 /*@switchbreak@*/ break;
01782         }
01783 
01784         /* XXX Everything below expects fe > f */
01785         fn = (fe - f);
01786         gn = (ge - g);
01787         if ((fe - f) <= 0) {
01788 /* XXX Process % in unknown context */
01789                 c = (int) '%';  /* XXX only need to save % */
01790                 SAVECHAR(mb, c);
01791 #if 0
01792                 rpmlog(RPMLOG_ERR,
01793                         _("A %% is followed by an unparseable macro\n"));
01794 #endif
01795                 s = se;
01796                 continue;
01797         }
01798 
01799         if (mb->macro_trace)
01800                 printMacro(mb, s, se);
01801 
01802         /* Expand builtin macros */
01803         if (STREQ("load", f, fn)) {
01804                 if (g != NULL) {
01805                     char * mfn = strncpy(alloca(gn + 1), g, gn);
01806                     int xx;
01807                     mfn[gn] = '\0';
01808                     xx = rpmLoadMacroFile(NULL, mfn, _max_load_depth);
01809                     /* Print failure iff %{load:...} or %{!?load:...} */
01810                     if (xx != 0 && chkexist == negate)
01811                         rpmlog(RPMLOG_ERR, _("%s: load macros failed\n"), mfn);
01812                 }
01813                 s = se;
01814                 continue;
01815         }
01816         if (STREQ("global", f, fn)) {
01817                 s = doDefine(mb, se, RMIL_GLOBAL, 1);
01818                 continue;
01819         }
01820         if (STREQ("define", f, fn)) {
01821                 s = doDefine(mb, se, mb->depth, 0);
01822                 continue;
01823         }
01824         if (STREQ("undefine", f, fn)) {
01825                 s = doUndefine(mb->mc, se);
01826                 continue;
01827         }
01828 
01829         if (STREQ("echo", f, fn) ||
01830             STREQ("warn", f, fn) ||
01831             STREQ("error", f, fn)) {
01832                 int waserror = 0;
01833                 if (STREQ("error", f, fn))
01834                         waserror = 1, rc = 1;
01835                 if (g != NULL && g < ge)
01836                         doOutput(mb, waserror, g, gn);
01837                 else
01838                         doOutput(mb, waserror, f, fn);
01839                 s = se;
01840                 continue;
01841         }
01842 
01843         if (STREQ("trace", f, fn)) {
01844                 /* XXX TODO restore expand_trace/macro_trace to 0 on return */
01845                 mb->expand_trace = mb->macro_trace = (negate ? 0 : mb->depth);
01846                 if (mb->depth == 1) {
01847                         print_macro_trace = mb->macro_trace;
01848                         print_expand_trace = mb->expand_trace;
01849                 }
01850                 s = se;
01851                 continue;
01852         }
01853 
01854         if (STREQ("dump", f, fn)) {
01855                 rpmDumpMacroTable(mb->mc, NULL);
01856                 while (iseol(*se))
01857                         se++;
01858                 s = se;
01859                 continue;
01860         }
01861 
01862 #ifdef  WITH_LUA
01863         if (STREQ("lua", f, fn)) {
01864                 rpmlua lua = rpmluaGetGlobalState();
01865                 rpmlua olua = memcpy(alloca(sizeof(*olua)), lua, sizeof(*olua));
01866                 const char *ls = s+sizeof("{lua:")-1;
01867                 const char *lse = se-sizeof("}")+1;
01868                 char *scriptbuf = (char *)xmalloc((lse-ls)+1);
01869                 const char *printbuf;
01870 
01871                 /* Reset the stateful output buffer before recursing down. */
01872                 lua->storeprint = 1;
01873                 lua->printbuf = NULL;
01874                 lua->printbufsize = 0;
01875                 lua->printbufused = 0;
01876 
01877                 memcpy(scriptbuf, ls, lse-ls);
01878                 scriptbuf[lse-ls] = '\0';
01879                 if (rpmluaRunScript(lua, scriptbuf, NULL) == -1)
01880                     rc = 1;
01881                 printbuf = rpmluaGetPrintBuffer(lua);
01882                 if (printbuf) {
01883                     size_t len = strlen(printbuf);
01884                     if (len > mb->nb)
01885                         len = mb->nb;
01886                     memcpy(mb->t, printbuf, len);
01887                     mb->t += len;
01888                     mb->nb -= len;
01889                 }
01890 
01891                 /* Restore the stateful output buffer after recursion. */
01892                 lua->storeprint = olua->storeprint;
01893                 lua->printbuf = olua->printbuf;
01894                 lua->printbufsize = olua->printbufsize;
01895                 lua->printbufused = olua->printbufused;
01896 
01897                 free(scriptbuf);
01898                 s = se;
01899                 continue;
01900         }
01901 #endif
01902 
01903 #ifdef  WITH_AUGEAS
01904         if (STREQ("augeas", f, fn)) {
01905                 /* XXX change rpmaugNew() to common embedded interpreter API */
01906 #ifdef  NOTYET
01907                 char ** av = NULL;
01908                 char * script = parseEmbedded(s, (size_t)(se-s), &av);
01909 #else
01910                 char * script = strndup(g, (size_t)(se-g-1));
01911 #endif
01912                 rpmaug aug = (_globalI ? NULL
01913                     : rpmaugNew(_rpmaugRoot, _rpmaugLoadpath, _rpmaugFlags));
01914                 const char * result = NULL;
01915 
01916                 if (rpmaugRun(aug, script, &result) != RPMRC_OK)
01917                     rc = 1;
01918                 else {
01919                   if (result == NULL) result = "FIXME";
01920                   if (result != NULL && *result != '\0') {
01921                     size_t len = strlen(result);
01922                     if (len > mb->nb)
01923                         len = mb->nb;
01924                     memcpy(mb->t, result, len);
01925                     mb->t += len;
01926                     mb->nb -= len;
01927                  }
01928                 }
01929                 aug = rpmaugFree(aug);
01930 #ifdef  NOTYET
01931                 av = _free(av);
01932 #endif
01933                 script = _free(script);
01934                 s = se;
01935                 continue;
01936         }
01937 #endif
01938 
01939 #ifdef  WITH_FICL
01940         if (STREQ("ficl", f, fn)) {
01941                 char ** av = NULL;
01942                 char * script = parseEmbedded(s, (size_t)(se-s), &av);
01943                 rpmficl ficl = rpmficlNew(av, _globalI);
01944                 const char * result = NULL;
01945 
01946                 if (rpmficlRun(ficl, script, &result) != RPMRC_OK)
01947                     rc = 1;
01948                 else {
01949                   if (result == NULL) result = "FIXME";
01950                   if (result != NULL && *result != '\0') {
01951                     size_t len = strlen(result);
01952                     if (len > mb->nb)
01953                         len = mb->nb;
01954                     memcpy(mb->t, result, len);
01955                     mb->t += len;
01956                     mb->nb -= len;
01957                  }
01958                 }
01959                 ficl = rpmficlFree(ficl);
01960                 av = _free(av);
01961                 script = _free(script);
01962                 s = se;
01963                 continue;
01964         }
01965 #endif
01966 
01967 #ifdef  WITH_GPSEE
01968         if (STREQ("js", f, fn)) {
01969                 char ** av = NULL;
01970                 char * script = parseEmbedded(s, (size_t)(se-s), &av);
01971                 rpmjs js = rpmjsNew(av, _globalI);
01972                 const char * result = NULL;
01973 
01974                 if (rpmjsRun(js, script, &result) != RPMRC_OK)
01975                     rc = 1;
01976                 else {
01977                   if (result == NULL) result = "FIXME";
01978                   if (result != NULL && *result != '\0') {
01979                     size_t len = strlen(result);
01980                     if (len > mb->nb)
01981                         len = mb->nb;
01982                     memcpy(mb->t, result, len);
01983                     mb->t += len;
01984                     mb->nb -= len;
01985                  }
01986                 }
01987                 js = rpmjsFree(js);
01988                 av = _free(av);
01989                 script = _free(script);
01990                 s = se;
01991                 continue;
01992         }
01993 #endif
01994 
01995 #ifdef  WITH_NIX
01996         if (STREQ("nix", f, fn)) {
01997                 char ** av = NULL;
01998                 char * script = parseEmbedded(s, (size_t)(se-s), &av);
01999                 int (*_vec) (rpmnix nix) = rpmnixEcho;
02000                 uint32_t _flags = RPMNIX_FLAGS_NONE;
02001                 rpmnix nix;
02002                 const char * result = NULL;
02003                 int xx;
02004 
02005                 if (av == NULL || av[0] == NULL || av[1] == NULL
02006                  || !strcmp(av[0], "echo"))
02007                 {
02008                     _vec = rpmnixEcho;
02009                 } else
02010                 if (!strcmp(av[1], "build")) {
02011                     _vec = rpmnixBuild;
02012                     _flags = RPMNIX_FLAGS_NOOUTLINK;
02013                 } else
02014                 if (!strcmp(av[1], "channel")) {
02015                     _vec = rpmnixChannel;
02016                 } else
02017                 if (!strcmp(av[1], "collect-garbage")) {
02018                     _vec = rpmnixCollectGarbage;
02019                 } else
02020                 if (!strcmp(av[1], "copy-closure")) {
02021                     _vec = rpmnixCopyClosure;
02022                 } else
02023                 if (!strcmp(av[1], "env")) {
02024                     _vec = rpmnixEnv;
02025                 } else
02026                 if (!strcmp(av[1], "hash")) {
02027                     _vec = rpmnixHash;
02028                 } else
02029                 if (!strcmp(av[1], "install-package")) {
02030                     _vec = rpmnixInstallPackage;
02031                     _flags = RPMNIX_FLAGS_INTERACTIVE;
02032                 } else
02033                 if (!strcmp(av[1], "instantiate")) {
02034                     _vec = rpmnixInstantiate;
02035                 } else
02036                 if (!strcmp(av[1], "prefetch-url")) {
02037                     _vec = rpmnixPrefetchURL;
02038                 } else
02039                 if (!strcmp(av[1], "push")) {
02040                     _vec = rpmnixPush;
02041                 } else
02042                 if (!strcmp(av[1], "pull")) {
02043                     _vec = rpmnixPull;
02044                 } else
02045                 if (!strcmp(av[1], "store")) {
02046                     _vec = rpmnixStore;
02047                 } else
02048                 if (!strcmp(av[1], "worker")) {
02049                     _vec = rpmnixWorker;
02050                 } else
02051 assert(0);
02052 
02053                 nix = rpmnixNew(av, _flags, NULL);
02054 
02055 #ifdef  NOTYET
02056                 if (rpmnixRun(nix, script, &result) != RPMRC_OK)
02057                     rc = 1;
02058                 else {
02059                   if (result == NULL) result = "FIXME";
02060                   if (result != NULL && *result != '\0') {
02061                     size_t len = strlen(result);
02062                     if (len > mb->nb)
02063                         len = mb->nb;
02064                     memcpy(mb->t, result, len);
02065                     mb->t += len;
02066                     mb->nb -= len;
02067                  }
02068                 }
02069 #else
02070                 xx = (*_vec) (nix);
02071                 result = xstrdup("");
02072 #endif  /* NOTYET */
02073 
02074                 nix = rpmnixFree(nix);
02075                 av = _free(av);
02076                 script = _free(script);
02077                 s = se;
02078                 continue;
02079         }
02080 #endif
02081 
02082 #ifdef  WITH_PERLEMBED
02083         if (STREQ("perl", f, fn)) {
02084                 char ** av = NULL;
02085                 char * script = parseEmbedded(s, (size_t)(se-s), &av);
02086                 rpmperl perl = rpmperlNew(av, _globalI);
02087                 const char * result = NULL;
02088 
02089                 if (rpmperlRun(perl, script, &result) != RPMRC_OK)
02090                     rc = 1;
02091                 else {
02092                   if (result == NULL) result = "FIXME";
02093                   if (result != NULL && *result != '\0') {
02094                     size_t len = strlen(result);
02095                     if (len > mb->nb)
02096                         len = mb->nb;
02097                     memcpy(mb->t, result, len);
02098                     mb->t += len;
02099                     mb->nb -= len;
02100                  }
02101                 }
02102                 perl = rpmperlFree(perl);
02103                 av = _free(av);
02104                 script = _free(script);
02105                 s = se;
02106                 continue;
02107         }
02108 #endif
02109 
02110 #ifdef  WITH_PYTHONEMBED
02111         if (STREQ("python", f, fn)) {
02112                 char ** av = NULL;
02113                 char * script = parseEmbedded(s, (size_t)(se-s), &av);
02114                 rpmpython python = rpmpythonNew(av, _globalI);
02115                 const char * result = NULL;
02116 
02117                 if (rpmpythonRun(python, script, &result) != RPMRC_OK)
02118                     rc = 1;
02119                 else {
02120                   if (result == NULL) result = "FIXME";
02121                   if (result != NULL && *result != '\0') {
02122                     size_t len = strlen(result);
02123                     if (len > mb->nb)
02124                         len = mb->nb;
02125                     memcpy(mb->t, result, len);
02126                     mb->t += len;
02127                     mb->nb -= len;
02128                   }
02129                 }
02130                 python = rpmpythonFree(python);
02131                 av = _free(av);
02132                 script = _free(script);
02133                 s = se;
02134                 continue;
02135         }
02136 #endif
02137 
02138 #ifdef  WITH_RUBYEMBED
02139         if (STREQ("ruby", f, fn)) {
02140                 char ** av = NULL;
02141                 char * script = parseEmbedded(s, (size_t)(se-s), &av);
02142                 rpmruby ruby = rpmrubyNew(av, _globalI);
02143                 const char * result = NULL;
02144 
02145                 if (rpmrubyRun(ruby, script, &result) != RPMRC_OK)
02146                     rc = 1;
02147                 else {
02148                   if (result == NULL) result = "FIXME";
02149                   if (result != NULL && *result != '\0') {
02150                     size_t len = strlen(result);
02151                     if (len > mb->nb)
02152                         len = mb->nb;
02153                     memcpy(mb->t, result, len);
02154                     mb->t += len;
02155                     mb->nb -= len;
02156                   }
02157                 }
02158                 ruby = rpmrubyFree(ruby);
02159                 av = _free(av);
02160                 script = _free(script);
02161                 s = se;
02162                 continue;
02163         }
02164 #endif
02165 
02166 #ifdef  WITH_SEMANAGE
02167         if (STREQ("spook", f, fn)) {
02168                 /* XXX change rpmsmNew() to common embedded interpreter API */
02169 #ifdef  NOTYET
02170                 char ** av = NULL;
02171                 char * script = parseEmbedded(s, (size_t)(se-s), &av);
02172 #else
02173                 /* XXX use xstrndup (which never returns NULL) instead. */
02174                 char * script = strndup(g, (size_t)(se-g-1));
02175                 char * av[2];
02176                 /* XXX FIXME */
02177                 static const char * _rpmsmStore = "targeted";
02178                 static unsigned int _rpmsmFlags = 0;
02179 #endif
02180                 rpmsm sm = (_globalI ? NULL
02181                     : rpmsmNew(_rpmsmStore, _rpmsmFlags));
02182                 const char * result = NULL;
02183 
02184                 /* XXX HACK: use an argv for now. */
02185                 av[0] = script;
02186                 av[1] = NULL;
02187                 if (rpmsmRun(sm, av, &result) != RPMRC_OK)
02188                     rc = 1;
02189                 else {
02190                   if (result == NULL) result = "FIXME";
02191                   if (result != NULL && *result != '\0') {
02192                     size_t len = strlen(result);
02193                     if (len > mb->nb)
02194                         len = mb->nb;
02195                     memcpy(mb->t, result, len);
02196                     mb->t += len;
02197                     mb->nb -= len;
02198                  }
02199                 }
02200                 sm = rpmsmFree(sm);
02201 #ifdef  NOTYET
02202                 av = _free(av);
02203 #endif
02204                 script = _free(script);
02205                 s = se;
02206                 continue;
02207         }
02208 #endif
02209 
02210 #ifdef  WITH_SQLITE
02211         if (STREQ("sql", f, fn)) {
02212                 char ** av = NULL;
02213                 char * script = parseEmbedded(s, (size_t)(se-s), &av);
02214                 rpmsql sql = rpmsqlNew(av, _globalI);
02215                 const char * result = NULL;
02216 
02217                 if (rpmsqlRun(sql, script, &result) != RPMRC_OK)
02218                     rc = 1;
02219                 else {
02220                   if (result == NULL) result = "FIXME";
02221                   if (result != NULL && *result != '\0') {
02222                     size_t len = strlen(result);
02223                     if (len > mb->nb)
02224                         len = mb->nb;
02225                     memcpy(mb->t, result, len);
02226                     mb->t += len;
02227                     mb->nb -= len;
02228                   }
02229                 }
02230                 sql = rpmsqlFree(sql);
02231                 av = _free(av);
02232                 script = _free(script);
02233                 s = se;
02234                 continue;
02235         }
02236 #endif
02237 
02238 #ifdef  WITH_SQUIRREL
02239         if (STREQ("squirrel", f, fn)) {
02240                 char ** av = NULL;
02241                 char * script = parseEmbedded(s, (size_t)(se-s), &av);
02242                 rpmsquirrel squirrel = rpmsquirrelNew(av, _globalI);
02243                 const char * result = NULL;
02244 
02245                 if (rpmsquirrelRun(squirrel, script, &result) != RPMRC_OK)
02246                     rc = 1;
02247                 else {
02248                   if (result == NULL) result = "FIXME";
02249                   if (result != NULL && *result != '\0') {
02250                     size_t len = strlen(result);
02251                     if (len > mb->nb)
02252                         len = mb->nb;
02253                     memcpy(mb->t, result, len);
02254                     mb->t += len;
02255                     mb->nb -= len;
02256                   }
02257                 }
02258                 squirrel = rpmsquirrelFree(squirrel);
02259                 av = _free(av);
02260                 script = _free(script);
02261                 s = se;
02262                 continue;
02263         }
02264 #endif
02265 
02266 #ifdef  WITH_TCL
02267         if (STREQ("tcl", f, fn)) {
02268                 char ** av = NULL;
02269                 char * script = parseEmbedded(s, (size_t)(se-s), &av);
02270                 rpmtcl tcl = rpmtclNew(av, _globalI);
02271                 const char * result = NULL;
02272 
02273                 if (rpmtclRun(tcl, script, &result) != RPMRC_OK)
02274                     rc = 1;
02275                 else if (result != NULL && *result != '\0') {
02276                     size_t len = strlen(result);
02277                     if (len > mb->nb)
02278                         len = mb->nb;
02279                     memcpy(mb->t, result, len);
02280                     mb->t += len;
02281                     mb->nb -= len;
02282                 }
02283                 tcl = rpmtclFree(tcl);
02284                 av = _free(av);
02285                 script = _free(script);
02286                 s = se;
02287                 continue;
02288         }
02289 #endif
02290 
02291         /* Rewrite "%patchNN ..." as "%patch -P NN ..." and expand. */
02292         if (lastc && fn > 5 && STREQ("patch", f, 5) && xisdigit((int)f[5])) {
02293                 /*@-internalglobs@*/ /* FIX: verbose may be set */
02294                 doFoo(mb, negate, f, (lastc - f), NULL, 0);
02295                 /*@=internalglobs@*/
02296                 s = lastc;
02297                 continue;
02298         }
02299 
02300         /* XXX necessary but clunky */
02301         if (STREQ("basename", f, fn) ||
02302             STREQ("dirname", f, fn) ||
02303             STREQ("realpath", f, fn) ||
02304             STREQ("getenv", f, fn) ||
02305             STREQ("shrink", f, fn) ||
02306             STREQ("suffix", f, fn) ||
02307             STREQ("expand", f, fn) ||
02308             STREQ("verbose", f, fn) ||
02309             STREQ("uncompress", f, fn) ||
02310             STREQ("mkstemp", f, fn) ||
02311             STREQ("mkdtemp", f, fn) ||
02312             STREQ("uuid", f, fn) ||
02313             STREQ("url2path", f, fn) ||
02314             STREQ("u2p", f, fn) ||
02315             STREQ("S", f, fn) ||
02316             STREQ("P", f, fn) ||
02317             STREQ("F", f, fn)) {
02318                 /*@-internalglobs@*/ /* FIX: verbose may be set */
02319                 doFoo(mb, negate, f, fn, g, gn);
02320                 /*@=internalglobs@*/
02321                 s = se;
02322                 continue;
02323         }
02324 
02325         /* Expand defined macros */
02326         mep = findEntry(mb->mc, f, fn);
02327         me = (mep ? *mep : NULL);
02328 
02329         /* XXX Special processing for flags */
02330         if (*f == '-') {
02331                 if (me)
02332                         me->used++;     /* Mark macro as used */
02333                 if ((me == NULL && !negate) ||  /* Without -f, skip %{-f...} */
02334                     (me != NULL && negate)) {   /* With -f, skip %{!-f...} */
02335                         s = se;
02336                         continue;
02337                 }
02338 
02339                 if (g && g < ge) {              /* Expand X in %{-f:X} */
02340                         rc = expandT(mb, g, gn);
02341                 } else
02342                 if (me && me->body && *me->body) {/* Expand %{-f}/%{-f*} */
02343                         rc = expandT(mb, me->body, strlen(me->body));
02344                 }
02345                 s = se;
02346                 continue;
02347         }
02348 
02349         /* XXX Special processing for macro existence */
02350         if (chkexist) {
02351                 if ((me == NULL && !negate) ||  /* Without -f, skip %{?f...} */
02352                     (me != NULL && negate)) {   /* With -f, skip %{!?f...} */
02353                         s = se;
02354                         continue;
02355                 }
02356                 if (g && g < ge) {              /* Expand X in %{?f:X} */
02357                         rc = expandT(mb, g, gn);
02358                 } else
02359                 if (me && me->body && *me->body) { /* Expand %{?f}/%{?f*} */
02360                         rc = expandT(mb, me->body, strlen(me->body));
02361                 }
02362                 s = se;
02363                 continue;
02364         }
02365 
02366         if (me == NULL) {       /* leave unknown %... as is */
02367 #if !defined(RPM_VENDOR_WINDRIVER_DEBUG)        /* XXX usually disabled */
02368 #if DEAD
02369                 /* XXX hack to skip over empty arg list */
02370                 if (fn == 1 && *f == '*') {
02371                         s = se;
02372                         continue;
02373                 }
02374 #endif
02375                 /* XXX hack to permit non-overloaded %foo to be passed */
02376                 c = (int) '%';  /* XXX only need to save % */
02377                 SAVECHAR(mb, c);
02378 #else
02379                 if (!strncmp(f, "if", fn) ||
02380                     !strncmp(f, "else", fn) ||
02381                     !strncmp(f, "endif", fn)) {
02382                         c = '%';        /* XXX only need to save % */
02383                         SAVECHAR(mb, c);
02384                 } else {
02385                         rpmlog(RPMLOG_ERR,
02386                                 _("Macro %%%.*s not found, skipping\n"), fn, f);
02387                         s = se;
02388                 }
02389 #endif
02390                 continue;
02391         }
02392 
02393         /* XXX Special processing to create a tuple from stack'd values. */
02394         if (stackarray) {
02395                 if (!(g && g < ge)) {
02396                         g = "\n";
02397                         gn = strlen(g);
02398                 }
02399                 rc = expandFIFO(mb, me, g, gn);
02400                 s = se;
02401                 continue;
02402         }
02403 
02404         /* Setup args for "%name " macros with opts */
02405         if (me && me->opts != NULL) {
02406                 if (lastc != NULL) {
02407                         se = grabArgs(mb, me, fe, lastc);
02408                 } else {
02409                         addMacro(mb->mc, "**", NULL, "", mb->depth);
02410                         addMacro(mb->mc, "*", NULL, "", mb->depth);
02411                         addMacro(mb->mc, "#", NULL, "0", mb->depth);
02412                         addMacro(mb->mc, "0", NULL, me->name, mb->depth);
02413                 }
02414         }
02415 
02416         /* Recursively expand body of macro */
02417         if (me->body && *me->body) {
02418                 mb->s = me->body;
02419                 rc = expandMacro(mb);
02420                 if (rc == 0)
02421                         me->used++;     /* Mark macro as used */
02422         }
02423 
02424         /* Free args for "%name " macros with opts */
02425         if (me->opts != NULL)
02426                 freeArgs(mb);
02427 
02428         s = se;
02429     }
02430 
02431     *mb->t = '\0';
02432     mb->s = s;
02433     mb->depth--;
02434     if (rc != 0 || mb->expand_trace)
02435         printExpansion(mb, t, mb->t);
02436     return rc;
02437 }
02438 
02439 #if defined(RPM_VENDOR_OPENPKG) /* stick-with-rpm-file-sanity-checking */ || \
02440     !defined(POPT_ERROR_BADCONFIG)      /* XXX POPT 1.15 retrofit */
02441 int rpmSecuritySaneFile(const char *filename)
02442 {
02443     struct stat sb;
02444     uid_t uid;
02445 
02446     if (stat(filename, &sb) == -1)
02447         return 1;
02448     uid = getuid();
02449     if (sb.st_uid != uid)
02450         return 0;
02451     if (!S_ISREG(sb.st_mode))
02452         return 0;
02453     if (sb.st_mode & (S_IWGRP|S_IWOTH))
02454         return 0;
02455     return 1;
02456 }
02457 #endif
02458 
02459 #if !defined(DEBUG_MACROS)
02460 /* =============================================================== */
02461 /*@unchecked@*/
02462 static int _debug = 0;
02463 
02464 int rpmGlob(const char * patterns, int * argcPtr, const char *** argvPtr)
02465 {
02466     int ac = 0;
02467     char ** av = NULL;
02468     int argc = 0;
02469     const char ** argv = NULL;
02470     char * globRoot = NULL;
02471 #ifdef ENABLE_NLS
02472     const char * old_collate = NULL;
02473     const char * old_ctype = NULL;
02474     const char * t;
02475 #endif
02476     size_t maxb, nb;
02477     size_t i;
02478     int j;
02479     int rc;
02480 
02481     rc = XpoptParseArgvString(patterns, &ac, &av);
02482     if (rc)
02483         return rc;
02484 #ifdef ENABLE_NLS
02485     t = setlocale(LC_COLLATE, NULL);
02486     if (t)
02487         old_collate = xstrdup(t);
02488     t = setlocale(LC_CTYPE, NULL);
02489     if (t)
02490         old_ctype = xstrdup(t);
02491     (void) setlocale(LC_COLLATE, "C");
02492     (void) setlocale(LC_CTYPE, "C");
02493 #endif
02494         
02495     if (av != NULL)
02496     for (j = 0; j < ac; j++) {
02497         const char * globURL;
02498         const char * path;
02499         int ut = urlPath(av[j], &path);
02500         glob_t gl;
02501 
02502         if (!Glob_pattern_p(av[j], 0) && strchr(path, '~') == NULL) {
02503             argv = xrealloc(argv, (argc+2) * sizeof(*argv));
02504             argv[argc] = xstrdup(av[j]);
02505 if (_debug)
02506 fprintf(stderr, "*** rpmGlob argv[%d] \"%s\"\n", argc, argv[argc]);
02507             argc++;
02508             continue;
02509         }
02510         
02511         gl.gl_pathc = 0;
02512         gl.gl_pathv = NULL;
02513         rc = Glob(av[j], GLOB_TILDE, Glob_error, &gl);
02514         if (rc)
02515             goto exit;
02516 
02517         /* XXX Prepend the URL leader for globs that have stripped it off */
02518         maxb = 0;
02519         for (i = 0; i < gl.gl_pathc; i++) {
02520             if ((nb = strlen(&(gl.gl_pathv[i][0]))) > maxb)
02521                 maxb = nb;
02522         }
02523         
02524         nb = ((ut == URL_IS_PATH) ? (path - av[j]) : 0);
02525         maxb += nb;
02526         maxb += 1;
02527         globURL = globRoot = xmalloc(maxb);
02528 
02529         switch (ut) {
02530         case URL_IS_PATH:
02531         case URL_IS_DASH:
02532             strncpy(globRoot, av[j], nb);
02533             /*@switchbreak@*/ break;
02534         case URL_IS_HTTPS:
02535         case URL_IS_HTTP:
02536         case URL_IS_FTP:
02537         case URL_IS_HKP:
02538         case URL_IS_UNKNOWN:
02539         default:
02540             /*@switchbreak@*/ break;
02541         }
02542         globRoot += nb;
02543         *globRoot = '\0';
02544 if (_debug)
02545 fprintf(stderr, "*** GLOB maxb %d diskURL %d %*s globURL %p %s\n", (int)maxb, (int)nb, (int)nb, av[j], globURL, globURL);
02546         
02547         argv = xrealloc(argv, (argc+gl.gl_pathc+1) * sizeof(*argv));
02548 
02549         if (argv != NULL)
02550         for (i = 0; i < gl.gl_pathc; i++) {
02551             const char * globFile = &(gl.gl_pathv[i][0]);
02552             if (globRoot > globURL && globRoot[-1] == '/')
02553                 while (*globFile == '/') globFile++;
02554             strcpy(globRoot, globFile);
02555 if (_debug)
02556 fprintf(stderr, "*** rpmGlob argv[%d] \"%s\"\n", argc, globURL);
02557             argv[argc++] = xstrdup(globURL);
02558         }
02559         /*@-immediatetrans@*/
02560         Globfree(&gl);
02561         /*@=immediatetrans@*/
02562         globURL = _free(globURL);
02563     }
02564 
02565     if (argv != NULL && argc > 0) {
02566         argv[argc] = NULL;
02567         if (argvPtr)
02568             *argvPtr = argv;
02569         if (argcPtr)
02570             *argcPtr = argc;
02571         rc = 0;
02572     } else
02573         rc = 1;
02574 
02575 
02576 exit:
02577 #ifdef ENABLE_NLS       
02578     if (old_collate) {
02579         (void) setlocale(LC_COLLATE, old_collate);
02580         old_collate = _free(old_collate);
02581     }
02582     if (old_ctype) {
02583         (void) setlocale(LC_CTYPE, old_ctype);
02584         old_ctype = _free(old_ctype);
02585     }
02586 #endif
02587     av = _free(av);
02588     if (rc || argvPtr == NULL) {
02589 /*@-dependenttrans -unqualifiedtrans@*/
02590         if (argv != NULL)
02591         for (j = 0; j < argc; j++)
02592             argv[j] = _free(argv[j]);
02593         argv = _free(argv);
02594 /*@=dependenttrans =unqualifiedtrans@*/
02595     }
02596     return rc;
02597 }
02598 #endif  /* !defined(DEBUG_MACROS) */
02599 
02600 /* =============================================================== */
02601 
02602 int
02603 expandMacros(void * spec, MacroContext mc, char * sbuf, size_t slen)
02604 {
02605     MacroBuf mb = alloca(sizeof(*mb));
02606     char *tbuf;
02607     int rc;
02608 
02609     if (sbuf == NULL || slen == 0)
02610         return 0;
02611     if (mc == NULL) mc = rpmGlobalMacroContext;
02612 
02613     tbuf = alloca(slen + 1);
02614     tbuf[0] = '\0';
02615 
02616     mb->s = sbuf;
02617     mb->t = tbuf;
02618     mb->nb = slen;
02619     mb->depth = 0;
02620     mb->macro_trace = print_macro_trace;
02621     mb->expand_trace = print_expand_trace;
02622 
02623     mb->spec = spec;    /* (future) %file expansion info */
02624     mb->mc = mc;
02625 
02626     rc = expandMacro(mb);
02627 
02628     tbuf[slen] = '\0';
02629     if (mb->nb == 0)
02630         rpmlog(RPMLOG_ERR, _("Macro expansion too big for target buffer\n"));
02631     else
02632         strncpy(sbuf, tbuf, (slen - mb->nb + 1));
02633 
02634     return rc;
02635 }
02636 
02637 void
02638 addMacro(MacroContext mc,
02639         const char * n, const char * o, const char * b, int level)
02640 {
02641     MacroEntry * mep;
02642     const char * name = n;
02643 
02644     if (*name == '.')           /* XXX readonly macros */
02645         name++;
02646     if (*name == '.')           /* XXX readonly macros */
02647         name++;
02648 
02649     if (mc == NULL) mc = rpmGlobalMacroContext;
02650 
02651     /* If new name, expand macro table */
02652     if ((mep = findEntry(mc, name, 0)) == NULL) {
02653         if (mc->firstFree == mc->macrosAllocated)
02654             expandMacroTable(mc);
02655         if (mc->macroTable != NULL)
02656             mep = mc->macroTable + mc->firstFree++;
02657     }
02658 
02659     if (mep != NULL) {
02660         /* XXX permit "..foo" to be pushed over ".foo" */
02661         if (*mep && (*mep)->flags && !(n[0] == '.' && n[1] == '.')) {
02662             /* XXX avoid error message for %buildroot */
02663             if (strcmp((*mep)->name, "buildroot"))
02664                 rpmlog(RPMLOG_ERR, _("Macro '%s' is readonly and cannot be changed.\n"), n);
02665             return;
02666         }
02667         /* Push macro over previous definition */
02668         pushMacro(mep, n, o, b, level);
02669 
02670         /* If new name, sort macro table */
02671         if ((*mep)->prev == NULL)
02672             sortMacroTable(mc);
02673     }
02674 }
02675 
02676 void
02677 delMacro(MacroContext mc, const char * n)
02678 {
02679     MacroEntry * mep;
02680 
02681     if (mc == NULL) mc = rpmGlobalMacroContext;
02682     /* If name exists, pop entry */
02683     if ((mep = findEntry(mc, n, 0)) != NULL) {
02684         popMacro(mep);
02685         /* If deleted name, sort macro table */
02686         if (!(mep && *mep))
02687             sortMacroTable(mc);
02688     }
02689 }
02690 
02691 /*@-mustmod@*/ /* LCL: mc is modified through mb->mc, mb is abstract */
02692 int
02693 rpmDefineMacro(MacroContext mc, const char * macro, int level)
02694 {
02695     MacroBuf mb = alloca(sizeof(*mb));
02696 
02697     memset(mb, 0, sizeof(*mb));
02698     /* XXX just enough to get by */
02699     mb->mc = (mc ? mc : rpmGlobalMacroContext);
02700     (void) doDefine(mb, macro, level, 0);
02701     return 0;
02702 }
02703 /*@=mustmod@*/
02704 
02705 /*@-mustmod@*/ /* LCL: mc is modified through mb->mc, mb is abstract */
02706 int
02707 rpmUndefineMacro(MacroContext mc, const char * macro)
02708 {
02709     (void) doUndefine(mc ? mc : rpmGlobalMacroContext, macro);
02710     return 0;
02711 }
02712 /*@=mustmod@*/
02713 
02714 void
02715 rpmLoadMacros(MacroContext mc, int level)
02716 {
02717 
02718     if (mc == NULL || mc == rpmGlobalMacroContext)
02719         return;
02720 
02721     if (mc->macroTable != NULL) {
02722         int i;
02723         for (i = 0; i < mc->firstFree; i++) {
02724             MacroEntry *mep, me;
02725             mep = &mc->macroTable[i];
02726             me = *mep;
02727 
02728             if (me == NULL)             /* XXX this should never happen */
02729                 continue;
02730             addMacro(NULL, me->name, me->opts, me->body, (level - 1));
02731         }
02732     }
02733 }
02734 
02735 #if defined(RPM_VENDOR_OPENPKG) /* expand-macrosfile-macro */
02736 static void expand_macrosfile_macro(const char *file_name, const char *buf, size_t bufn)
02737 {
02738     char *cp;
02739     size_t l, k;
02740     static const char *macro_name = "%{macrosfile}";
02741 
02742     l = strlen(macro_name);
02743     k = strlen(file_name);
02744     while ((cp = strstr(buf, macro_name)) != NULL) {
02745         if (((strlen(buf) - l) + k) < bufn) {
02746             memmove(cp+k, cp+l, strlen(cp+l)+1);
02747             memcpy(cp, file_name, k);
02748         }
02749     }
02750     return;
02751 }
02752 #endif
02753 
02754 int
02755 rpmLoadMacroFile(MacroContext mc, const char * fn, int nesting)
02756 {
02757     size_t bufn = _macro_BUFSIZ;
02758     char *buf = alloca(bufn);
02759     int lineno = 0;
02760     int rc = -1;
02761     FD_t fd;
02762     int xx;
02763 
02764     /* XXX TODO: teach rdcl() to read through a URI, eliminate ".fpio". */
02765     fd = Fopen(fn, "r.fpio");
02766     if (fd == NULL || Ferror(fd)) {
02767         if (fd) (void) Fclose(fd);
02768         return rc;
02769     }
02770 
02771     /* XXX Assume new fangled macro expansion */
02772     /*@-mods@*/
02773     max_macro_depth = _MAX_MACRO_DEPTH;
02774     /*@=mods@*/
02775 
02776     buf[0] = '\0';
02777     while(rdcl(buf, bufn, fd) != NULL) {
02778         char * s;
02779         int c;
02780 
02781         lineno++;
02782         s = buf;
02783         SKIPBLANK(s, c);
02784 
02785         if (c != (int) '%')
02786             continue;
02787 
02788         /* Parse %{load:...} immediately recursively. */
02789         if (s[1] == '{' && !strncmp(s+2, "load:", sizeof("load:")-1)) {
02790             char * se = (char *) matchchar(s, '{', '}');
02791             if (se == NULL) {
02792                 rpmlog(RPMLOG_WARNING,
02793                     _("%s:%u Missing '}' in \"%s\", skipping.\n"),
02794                     fn, lineno, buf);
02795                 continue;
02796             }
02797             s += sizeof("%{load:") - 1;
02798             SKIPBLANK(s, c);
02799             *se = '\0';
02800             if (nesting <= 0) {
02801                 rpmlog(RPMLOG_WARNING,
02802                     _("%s:%u load depth exceeded, \"%s\" ignored.\n"),
02803                     fn, lineno, buf);
02804                 continue;
02805             }
02806             se = rpmMCExpand(mc, s, NULL);
02807             rc = rpmLoadMacroFile(mc, se, nesting - 1);
02808             se = _free(se);
02809             if (rc != 0)
02810                 goto exit;
02811         } else {
02812 #if defined(RPM_VENDOR_OPENPKG) /* expand-macro-source */
02813             expand_macrosfile_macro(fn, buf, bufn);
02814 #endif
02815             if (*s == '%') s++;
02816             rc = rpmDefineMacro(mc, s, RMIL_MACROFILES);
02817         }
02818     }
02819     rc = 0;
02820 exit:
02821     xx = Fclose(fd);
02822     return rc;
02823 }
02824 
02825 void
02826 rpmInitMacros(MacroContext mc, const char * macrofiles)
02827 {
02828     char *mfiles, *m, *me;
02829 
02830     if (macrofiles == NULL)
02831         return;
02832 #ifdef  DYING
02833     if (mc == NULL) mc = rpmGlobalMacroContext;
02834 #endif
02835 
02836     mfiles = xstrdup(macrofiles);
02837     for (m = mfiles; m && *m != '\0'; m = me) {
02838         const char ** av;
02839         int ac;
02840         int i;
02841 
02842         for (me = m; (me = strchr(me, ':')) != NULL; me++) {
02843             /* Skip over URI's. */
02844             if (!(me[1] == '/' && me[2] == '/'))
02845                 /*@innerbreak@*/ break;
02846         }
02847 
02848         if (me && *me == ':')
02849             *me++ = '\0';
02850         else
02851             me = m + strlen(m);
02852 
02853         /* Glob expand the macro file path element, expanding ~ to $HOME. */
02854         ac = 0;
02855         av = NULL;
02856 #if defined(DEBUG_MACROS)
02857         ac = 1;
02858         av = xmalloc((ac + 1) * sizeof(*av));
02859         av[0] = strdup(m);
02860         av[1] = NULL;
02861 #else
02862         i = rpmGlob(m, &ac, &av);
02863         if (i != 0)
02864             continue;
02865 #endif
02866 
02867         /* Read macros from each file. */
02868 
02869         for (i = 0; i < ac; i++) {
02870             size_t slen = strlen(av[i]);
02871             const char *fn = av[i];
02872 
02873         if (fn[0] == '@' /* attention */) {
02874             fn++;
02875 #if defined(RPM_VENDOR_OPENPKG) /* stick-with-rpm-file-sanity-checking */ || \
02876     !defined(POPT_ERROR_BADCONFIG)      /* XXX POPT 1.15 retrofit */
02877             if (!rpmSecuritySaneFile(fn))
02878 #else
02879             if (!poptSaneFile(fn))
02880 #endif
02881             {
02882                 rpmlog(RPMLOG_WARNING, "existing RPM macros file \"%s\" considered INSECURE -- not loaded\n", fn);
02883                 /*@innercontinue@*/ continue;
02884             }
02885         }
02886 
02887         /* Skip backup files and %config leftovers. */
02888 #define _suffix(_s, _x) \
02889     (slen >= sizeof(_x) && !strcmp((_s)+slen-(sizeof(_x)-1), (_x)))
02890             if (!(_suffix(fn, "~")
02891                || _suffix(fn, ".rpmnew")
02892                || _suffix(fn, ".rpmorig")
02893                || _suffix(fn, ".rpmsave"))
02894                )
02895                    (void) rpmLoadMacroFile(mc, fn, _max_load_depth);
02896 #undef _suffix
02897 
02898             av[i] = _free(av[i]);
02899         }
02900         av = _free(av);
02901     }
02902     mfiles = _free(mfiles);
02903 
02904     /* Reload cmdline macros */
02905     /*@-mods@*/
02906     rpmLoadMacros(rpmCLIMacroContext, RMIL_CMDLINE);
02907     /*@=mods@*/
02908 }
02909 
02910 /*@-globstate@*/
02911 void
02912 rpmFreeMacros(MacroContext mc)
02913 {
02914     
02915     if (mc == NULL) mc = rpmGlobalMacroContext;
02916 
02917     if (mc->macroTable != NULL) {
02918         int i;
02919         for (i = 0; i < mc->firstFree; i++) {
02920             MacroEntry me;
02921             while ((me = mc->macroTable[i]) != NULL) {
02922                 /* XXX cast to workaround const */
02923                 /*@-onlytrans@*/
02924                 if ((mc->macroTable[i] = me->prev) == NULL)
02925                     me->name = _free(me->name);
02926                 /*@=onlytrans@*/
02927                 me->opts = _free(me->opts);
02928                 me->body = _free(me->body);
02929                 me = _free(me);
02930             }
02931         }
02932         mc->macroTable = _free(mc->macroTable);
02933     }
02934     memset(mc, 0, sizeof(*mc));
02935 }
02936 /*@=globstate@*/
02937 
02938 /* =============================================================== */
02939 int isCompressed(const char * file, rpmCompressedMagic * compressed)
02940 {
02941     FD_t fd;
02942     ssize_t nb;
02943     int rc = -1;
02944     unsigned char magic[13];
02945 #if defined(RPM_VENDOR_OPENPKG) || defined(RPM_VENDOR_FEDORA) || defined(RPM_VENDOR_MANDRIVA) /* extension-based-compression-detection */
02946     size_t file_len;
02947 #endif
02948 
02949     *compressed = COMPRESSED_NOT;
02950 
02951 #if defined(RPM_VENDOR_OPENPKG) || defined(RPM_VENDOR_FEDORA) || defined(RPM_VENDOR_MANDRIVA) /* extension-based-compression-detection */
02952     file_len = strlen(file);
02953     if ((file_len > 4 && strcasecmp(file+file_len-4, ".tbz") == 0)
02954      || (file_len > 4 && strcasecmp(file+file_len-4, ".bz2") == 0)) {
02955         *compressed = COMPRESSED_BZIP2;
02956         return 0;
02957     } else
02958     if (file_len > 4 && strcasecmp(file+file_len-4, ".zip") == 0) {
02959         *compressed = COMPRESSED_ZIP;
02960         return 0;
02961     } else
02962     if ((file_len > 4 && strcasecmp(file+file_len-4, ".tlz") == 0)
02963      || (file_len > 5 && strcasecmp(file+file_len-5, ".lzma") == 0)) {
02964         *compressed = COMPRESSED_LZMA;
02965         return 0;
02966     } else
02967     if (file_len > 4 && strcasecmp(file+file_len-3, ".xz") == 0) {
02968         *compressed = COMPRESSED_XZ;
02969         return 0;
02970     } else
02971     if ((file_len > 4 && strcasecmp(file+file_len-4, ".tgz") == 0)
02972      || (file_len > 3 && strcasecmp(file+file_len-3, ".gz") == 0)
02973      || (file_len > 2 && strcasecmp(file+file_len-2, ".Z") == 0)) {
02974         *compressed = COMPRESSED_OTHER;
02975         return 0;
02976     } else
02977     if (file_len > 5 && strcasecmp(file+file_len-5, ".cpio") == 0) {
02978         *compressed = COMPRESSED_NOT;
02979         return 0;
02980     } else
02981     if (file_len > 4 && strcasecmp(file+file_len-4, ".tar") == 0) {
02982         *compressed = COMPRESSED_NOT;
02983         return 0;
02984     }
02985 #endif
02986 
02987     fd = Fopen(file, "r");
02988     if (fd == NULL || Ferror(fd)) {
02989         /* XXX Fstrerror */
02990         rpmlog(RPMLOG_ERR, _("File %s: %s\n"), file, Fstrerror(fd));
02991         if (fd) (void) Fclose(fd);
02992         return 1;
02993     }
02994     nb = Fread(magic, sizeof(magic[0]), sizeof(magic), fd);
02995     if (nb < (ssize_t)0) {
02996         rpmlog(RPMLOG_ERR, _("File %s: %s\n"), file, Fstrerror(fd));
02997         rc = 1;
02998     } else if (nb < (ssize_t)sizeof(magic)) {
02999         rpmlog(RPMLOG_ERR, _("File %s is smaller than %u bytes\n"),
03000                 file, (unsigned)sizeof(magic));
03001         rc = 0;
03002     }
03003     (void) Fclose(fd);
03004     if (rc >= 0)
03005         return rc;
03006 
03007     rc = 0;
03008 
03009     if (magic[0] == 'B' && magic[1] == 'Z')
03010         *compressed = COMPRESSED_BZIP2;
03011     else
03012     if (magic[0] == (unsigned char) 0120 && magic[1] == (unsigned char) 0113
03013      && magic[2] == (unsigned char) 0003 && magic[3] == (unsigned char) 0004)   /* pkzip */
03014         *compressed = COMPRESSED_ZIP;
03015     else
03016     if (magic[0] == (unsigned char) 0x89 && magic[1] == 'L'
03017      && magic[2] == 'Z' && magic[3] == 'O')     /* lzop */
03018         *compressed = COMPRESSED_LZOP;
03019     else
03020 #if !defined(RPM_VENDOR_OPENPKG) && !defined(RPM_VENDOR_FEDORA) && !defined(RPM_VENDOR_MANDRIVA) /* extension-based-compression-detection */
03021     /* XXX Ick, LZMA has no magic. See http://lkml.org/lkml/2005/6/13/285 */
03022     if (magic[ 9] == (unsigned char) 0x00 && magic[10] == (unsigned char) 0x00 &&
03023         magic[11] == (unsigned char) 0x00 && magic[12] == (unsigned char) 0x00) /* lzmash */
03024         *compressed = COMPRESSED_LZMA;
03025     else
03026 #endif
03027 #if defined(RPM_VENDOR_OPENSUSE)
03028     if (magic[0] == 0135 && magic[1] == 0 && magic[2] == 0)             /* lzma */
03029         *compressed = COMPRESSED_LZMA;
03030     else
03031 #endif
03032     if (magic[0] == (unsigned char) 0xFD && magic[1] == 0x37 && magic[2] == 0x7A
03033      && magic[3] == 0x58 && magic[4] == 0x5A && magic[5] == 0x00)               /* xz */
03034         *compressed = COMPRESSED_XZ;
03035     else
03036     if ((magic[0] == (unsigned char) 0037 && magic[1] == (unsigned char) 0213)  /* gzip */
03037      || (magic[0] == (unsigned char) 0037 && magic[1] == (unsigned char) 0236)  /* old gzip */
03038      || (magic[0] == (unsigned char) 0037 && magic[1] == (unsigned char) 0036)  /* pack */
03039      || (magic[0] == (unsigned char) 0037 && magic[1] == (unsigned char) 0240)  /* SCO lzh */
03040      || (magic[0] == (unsigned char) 0037 && magic[1] == (unsigned char) 0235)) /* compress */
03041         *compressed = COMPRESSED_OTHER;
03042 
03043     return rc;
03044 }
03045 
03046 /* =============================================================== */
03047 
03048 /*@-modfilesys@*/
03049 /* XXX TODO: merge rpmExpand and rpmMCExpand. gud enuf for now ... */
03050 char * 
03051 rpmExpand(const char *arg, ...)
03052 {
03053     MacroContext mc = NULL;
03054     const char *s;
03055     char *t, *te;
03056     size_t sn, tn;
03057     size_t bufn = 8 * _macro_BUFSIZ;
03058 
03059     va_list ap;
03060 
03061     if (arg == NULL)
03062         return xstrdup("");
03063 
03064     t = xmalloc(bufn + strlen(arg) + 1);
03065     *t = '\0';
03066     te = stpcpy(t, arg);
03067 
03068     va_start(ap, arg);
03069     while ((s = va_arg(ap, const char *)) != NULL) {
03070         sn = strlen(s);
03071         tn = (te - t);
03072         t = xrealloc(t, tn + sn + bufn + 1);
03073         te = t + tn;
03074         te = stpcpy(te, s);
03075     }
03076     va_end(ap);
03077 
03078     *te = '\0';
03079     tn = (te - t);
03080     (void) expandMacros(NULL, mc, t, tn + bufn + 1);
03081     t[tn + bufn] = '\0';
03082     t = xrealloc(t, strlen(t) + 1);
03083     
03084     return t;
03085 }
03086 
03087 char * 
03088 rpmMCExpand(MacroContext mc, const char *arg, ...)
03089 {
03090     const char *s;
03091     char *t, *te;
03092     size_t sn, tn;
03093     size_t bufn = 8 * _macro_BUFSIZ;
03094 
03095     va_list ap;
03096 
03097     if (arg == NULL)
03098         return xstrdup("");
03099 
03100     t = xmalloc(bufn + strlen(arg) + 1);
03101     *t = '\0';
03102     te = stpcpy(t, arg);
03103 
03104     va_start(ap, arg);
03105     while ((s = va_arg(ap, const char *)) != NULL) {
03106         sn = strlen(s);
03107         tn = (te - t);
03108         t = xrealloc(t, tn + sn + bufn + 1);
03109         te = t + tn;
03110         te = stpcpy(te, s);
03111     }
03112     va_end(ap);
03113 
03114     *te = '\0';
03115     tn = (te - t);
03116     (void) expandMacros(NULL, mc, t, tn + bufn + 1);
03117     t[tn + bufn] = '\0';
03118     t = xrealloc(t, strlen(t) + 1);
03119     
03120     return t;
03121 }
03122 /*@=modfilesys@*/
03123 
03124 int
03125 rpmExpandNumeric(const char *arg)
03126 {
03127     const char *val;
03128     int rc;
03129 
03130     if (arg == NULL)
03131         return 0;
03132 
03133     val = rpmExpand(arg, NULL);
03134     if (!(val && *val != '%'))
03135         rc = 0;
03136     else if (*val == 'Y' || *val == 'y')
03137         rc = 1;
03138     else if (*val == 'N' || *val == 'n')
03139         rc = 0;
03140     else {
03141         char *end;
03142         rc = strtol(val, &end, 0);
03143         if (!(end && *end == '\0'))
03144             rc = 0;
03145     }
03146     val = _free(val);
03147 
03148     return rc;
03149 }
03150 
03151 /* @todo "../sbin/./../bin/" not correct. */
03152 char *rpmCleanPath(char * path)
03153 {
03154     const char *s;
03155     char *se, *t, *te;
03156     int begin = 1;
03157 
03158     if (path == NULL)
03159         return NULL;
03160 
03161 /*fprintf(stderr, "*** RCP %s ->\n", path); */
03162     s = t = te = path;
03163     while (*s != '\0') {
03164 /*fprintf(stderr, "*** got \"%.*s\"\trest \"%s\"\n", (t-path), path, s); */
03165         switch(*s) {
03166         case ':':                       /* handle url's */
03167             if (s[1] == '/' && s[2] == '/') {
03168                 *t++ = *s++;
03169                 *t++ = *s++;
03170                 /* XXX handle "file:///" */
03171                 if (s[0] == '/') *t++ = *s++;
03172                 te = t;
03173                 /*@switchbreak@*/ break;
03174             }
03175             begin=1;
03176             /*@switchbreak@*/ break;
03177         case '/':
03178             /* Move parent dir forward */
03179             for (se = te + 1; se < t && *se != '/'; se++)
03180                 {};
03181             if (se < t && *se == '/') {
03182                 te = se;
03183 /*fprintf(stderr, "*** next pdir \"%.*s\"\n", (te-path), path); */
03184             }
03185             while (s[1] == '/')
03186                 s++;
03187             while (t > te && t[-1] == '/')
03188                 t--;
03189             /*@switchbreak@*/ break;
03190         case '.':
03191             /* Leading .. is special */
03192             /* Check that it is ../, so that we don't interpret */
03193             /* ..?(i.e. "...") or ..* (i.e. "..bogus") as "..". */
03194             /* in the case of "...", this ends up being processed*/
03195             /* as "../.", and the last '.' is stripped.  This   */
03196             /* would not be correct processing.                 */
03197             if (begin && s[1] == '.' && (s[2] == '/' || s[2] == '\0')) {
03198 /*fprintf(stderr, "    leading \"..\"\n"); */
03199                 *t++ = *s++;
03200                 /*@switchbreak@*/ break;
03201             }
03202             /* Single . is special */
03203             if (begin && s[1] == '\0') {
03204                 /*@switchbreak@*/ break;
03205             }
03206             if (t > path && t[-1] == '/')
03207             switch (s[1]) {
03208             case '/':   s++;    /*@fallthrough@*/       /* Trim embedded ./ */
03209             case '\0':  s++;    continue;               /* Trim trailing /. */
03210             default:    /*@innerbreak@*/ break;
03211             }
03212             /* Trim embedded /../ and trailing /.. */
03213             if (!begin && t > path && t[-1] == '/' && s[1] == '.' && (s[2] == '/' || s[2] == '\0')) {
03214                 t = te;
03215                 /* Move parent dir forward */
03216                 if (te > path)
03217                     for (--te; te > path && *te != '/'; te--)
03218                         {};
03219 /*fprintf(stderr, "*** prev pdir \"%.*s\"\n", (te-path), path); */
03220                 s++;
03221                 s++;
03222                 continue;
03223             }
03224             /*@switchbreak@*/ break;
03225         default:
03226             begin = 0;
03227             /*@switchbreak@*/ break;
03228         }
03229         *t++ = *s++;
03230     }
03231 
03232     /* Trim trailing / (but leave single / alone) */
03233     if (t > &path[1] && t[-1] == '/')
03234         t--;
03235     *t = '\0';
03236 
03237 /*fprintf(stderr, "\t%s\n", path); */
03238     return path;
03239 }
03240 
03241 /* Return concatenated and expanded canonical path. */
03242 
03243 char *
03244 rpmGetPath(const char *path, ...)
03245 {
03246     size_t bufn = _macro_BUFSIZ;
03247     char *buf = alloca(bufn);
03248     const char * s;
03249     char * t, * te;
03250     int slashed = 0;
03251     va_list ap;
03252 
03253     if (path == NULL)
03254         return xstrdup("");
03255 
03256     buf[0] = '\0';
03257     t = buf;
03258     te = stpcpy(t, path);
03259     *te = '\0';
03260 
03261     va_start(ap, path);
03262     while ((s = va_arg(ap, const char *)) != NULL) {
03263         /* Specifically requested pesky trailing '/'? */
03264         slashed = (s[0] == '/' && s[1] == '\0');
03265         te = stpcpy(te, s);
03266     }
03267     va_end(ap);
03268     *te = '\0';
03269 
03270 /*@-modfilesys@*/
03271     (void) expandMacros(NULL, NULL, buf, bufn);
03272 /*@=modfilesys@*/
03273 
03274     /* Note: rpmCleanPath will strip pesky trailing '/'. */
03275     (void) rpmCleanPath(buf);
03276 
03277     /* Re-append specifically requested pesky trailing '/'. */
03278     if (slashed) {
03279         size_t nb = strlen(buf);
03280         if (buf[nb-1] != '/')
03281             buf[nb++] = '/';
03282         buf[nb] = '\0';
03283     }
03284 
03285     return xstrdup(buf);        /* XXX xstrdup has side effects. */
03286 }
03287 
03288 /* Merge 3 args into path, any or all of which may be a url. */
03289 
03290 const char * rpmGenPath(const char * urlroot, const char * urlmdir,
03291                 const char *urlfile)
03292 {
03293 /*@owned@*/ const char * xroot = rpmGetPath(urlroot, NULL);
03294 /*@dependent@*/ const char * root = xroot;
03295 /*@owned@*/ const char * xmdir = rpmGetPath(urlmdir, NULL);
03296 /*@dependent@*/ const char * mdir = xmdir;
03297 /*@owned@*/ const char * xfile = rpmGetPath(urlfile, NULL);
03298 /*@dependent@*/ const char * file = xfile;
03299     const char * result;
03300     const char * url = NULL;
03301     size_t nurl = 0;
03302     int ut;
03303 
03304 #if 0
03305 if (_debug) fprintf(stderr, "*** RGP xroot %s xmdir %s xfile %s\n", xroot, xmdir, xfile);
03306 #endif
03307     ut = urlPath(xroot, &root);
03308     if (url == NULL && ut > URL_IS_DASH) {
03309         url = xroot;
03310         nurl = strlen(url);
03311         if (root >= url && root <= url+nurl)
03312             nurl -= strlen(root);
03313 #if 0
03314 if (_debug) fprintf(stderr, "*** RGP ut %d root %s nurl %u\n", ut, root, (unsigned)nurl);
03315 #endif
03316     }
03317     if (root == NULL || *root == '\0') root = "/";
03318 
03319     ut = urlPath(xmdir, &mdir);
03320     if (url == NULL && ut > URL_IS_DASH) {
03321         url = xmdir;
03322         nurl = strlen(url);
03323         if (mdir >= url && mdir <= url+nurl)
03324             nurl -= strlen(mdir);
03325 #if 0
03326 if (_debug) fprintf(stderr, "*** RGP ut %d mdir %s nurl %u\n", ut, mdir, (unsigned)nurl);
03327 #endif
03328     }
03329     if (mdir == NULL || *mdir == '\0') mdir = "/";
03330 
03331     ut = urlPath(xfile, &file);
03332     if (url == NULL && ut > URL_IS_DASH) {
03333         url = xfile;
03334         nurl = strlen(url);
03335         if (file >= url && file <= url+nurl)
03336             nurl -= strlen(file);
03337 #if 0
03338 if (_debug) fprintf(stderr, "*** RGP ut %d file %s nurl %u\n", ut, file, (unsigned)nurl);
03339 #endif
03340     }
03341 
03342     if (url && nurl > 0) {
03343         char *t = strncpy(alloca(nurl+1), url, nurl);
03344         t[nurl] = '\0';
03345         url = t;
03346     } else
03347         url = "";
03348 
03349     result = rpmGetPath(url, root, "/", mdir, "/", file, NULL);
03350 
03351     xroot = _free(xroot);
03352     xmdir = _free(xmdir);
03353     xfile = _free(xfile);
03354 #if 0
03355 if (_debug) fprintf(stderr, "*** RGP result %s\n", result);
03356 #endif
03357     return result;
03358 }
03359 
03360 /* =============================================================== */
03361 
03362 #if defined(DEBUG_MACROS)
03363 
03364 #if defined(EVAL_MACROS)
03365 
03366 const char *rpmMacrofiles = MACROFILES;
03367 
03368 int
03369 main(int argc, char *argv[])
03370 {
03371     int c;
03372     int errflg = 0;
03373     extern char *optarg;
03374     extern int optind;
03375 
03376     while ((c = getopt(argc, argv, "f:")) != EOF ) {
03377         switch (c) {
03378         case 'f':
03379             rpmMacrofiles = optarg;
03380             break;
03381         case '?':
03382         default:
03383             errflg++;
03384             break;
03385         }
03386     }
03387     if (errflg || optind >= argc) {
03388         fprintf(stderr, "Usage: %s [-f macropath ] macro ...\n", argv[0]);
03389         exit(1);
03390     }
03391 
03392     rpmInitMacros(NULL, rpmMacrofiles);
03393     /* XXX getopt(3) also used for parametrized macros, expect scwewiness. */
03394     for ( ; optind < argc; optind++) {
03395         const char *val;
03396 
03397         val = rpmExpand(argv[optind], NULL);
03398         if (val) {
03399             fprintf(stdout, "%s:\t%s\n", argv[optind], val);
03400             val = _free(val);
03401         }
03402     }
03403     rpmFreeMacros(NULL);
03404     return 0;
03405 }
03406 
03407 #else   /* !EVAL_MACROS */
03408 
03409 const char *rpmMacrofiles = "../macros:./testmacros";
03410 const char *testfile = "./test";
03411 
03412 int
03413 main(int argc, char *argv[])
03414 {
03415     size_t bufn = _macro_BUFSIZ;
03416     char *buf = alloca(bufn);
03417     FILE *fp;
03418     int x;
03419 
03420     rpmInitMacros(NULL, rpmMacrofiles);
03421 
03422     if ((fp = fopen(testfile, "r")) != NULL) {
03423         while(rdcl(buf, bufn, fp)) {
03424             x = expandMacros(NULL, NULL, buf, bufn);
03425             fprintf(stderr, "%d->%s\n", x, buf);
03426             memset(buf, 0, bufn);
03427         }
03428         fclose(fp);
03429     }
03430 
03431     while(rdcl(buf, bufn, stdin)) {
03432         x = expandMacros(NULL, NULL, buf, bufn);
03433         fprintf(stderr, "%d->%s\n <-\n", x, buf);
03434         memset(buf, 0, bufn);
03435     }
03436     rpmFreeMacros(NULL);
03437 
03438     return 0;
03439 }
03440 #endif  /* EVAL_MACROS */
03441 #endif  /* DEBUG_MACROS */