rpm  5.4.4
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     mode_t mode;
01241 
01242     buf[0] = '\0';
01243     if (g != NULL) {
01244         strncpy(buf, g, gn);
01245         buf[gn] = '\0';
01246         (void) expandU(mb, buf, bufn);
01247     }
01248     if (fn > 5 && STREQ("patch", f, 5) && xisdigit((int)f[5])) {
01249         /* Skip leading zeros */
01250         for (c = 5; c < (int)(fn-1) && f[c] == '0' && xisdigit((int)f[c+1]);)
01251             c++;
01252         b = buf;
01253         be = stpncpy( stpcpy(b, "%patch -P "), f+c, fn-c);
01254         *be = '\0';
01255     } else
01256     if (STREQ("basename", f, fn)) {
01257         if ((b = strrchr(buf, '/')) == NULL)
01258             b = buf;
01259         else
01260             b++;
01261     } else if (STREQ("dirname", f, fn)) {
01262         if ((b = strrchr(buf, '/')) != NULL)
01263             *b = '\0';
01264         b = buf;
01265 #if !defined(__LCLINT__) /* XXX LCL: realpath(3) annotations are buggy. */
01266     } else if (STREQ("realpath", f, fn)) {
01267         char rp[PATH_MAX];
01268         char *cp;
01269         size_t l;
01270         if ((cp = realpath(buf, rp)) != NULL) {
01271             l = strlen(cp);
01272             if ((size_t)(l+1) <= bufn) {
01273                 memcpy(buf, cp, l+1);
01274                 b = buf;
01275             }
01276         }
01277 #endif
01278     } else if (STREQ("getenv", f, fn)) {
01279         char *cp;
01280         if ((cp = getenv(buf)) != NULL)
01281             b = cp;
01282     } else if (STREQ("shrink", f, fn)) {
01283         /*
01284          * shrink body by removing all leading and trailing whitespaces and
01285          * reducing intermediate whitespaces to a single space character.
01286          */
01287         int i, j, k, was_space = 0;
01288         for (i = 0, j = 0, k = (int)strlen(buf); i < k; ) {
01289             if (xisspace((int)(buf[i]))) {
01290                 was_space = 1;
01291                 i++;
01292                 continue;
01293             }
01294             else if (was_space) {
01295                 was_space = 0;
01296                 if (j > 0) /* remove leading blanks at all */
01297                     buf[j++] = ' ';
01298                 /* fallthrough */
01299             }
01300             buf[j++] = buf[i++];
01301         }
01302         buf[j] = '\0';
01303         b = buf;
01304     } else if (STREQ("suffix", f, fn)) {
01305         if ((b = strrchr(buf, '.')) != NULL)
01306             b++;
01307     } else if (STREQ("expand", f, fn)) {
01308         b = buf;
01309     } else if (STREQ("verbose", f, fn)) {
01310 #if defined(RPMLOG_MASK)
01311         if (negate)
01312             b = ((rpmlogSetMask(0) >= RPMLOG_MASK( RPMLOG_INFO )) ? NULL : buf);
01313         else
01314             b = ((rpmlogSetMask(0) >= RPMLOG_MASK( RPMLOG_INFO )) ? buf : NULL);
01315 #else
01316         /* XXX assume always verbose when running standalone */
01317         b = (negate) ? NULL : buf;
01318 #endif
01319     } else if (STREQ("url2path", f, fn) || STREQ("u2p", f, fn)) {
01320         int ut = urlPath(buf, (const char **)&b);
01321         ut = ut;        /* XXX quiet gcc */
01322         if (*b == '\0') b = "/";
01323     } else if (STREQ("uncompress", f, fn)) {
01324         rpmCompressedMagic compressed = COMPRESSED_OTHER;
01325 /*@-globs@*/
01326         for (b = buf; (c = (int)*b) && isblank(c);)
01327             b++;
01328         /* XXX FIXME: file paths with embedded white space needs rework. */
01329         for (be = b; (c = (int)*be) && !isblank(c);)
01330             be++;
01331 /*@=globs@*/
01332         *be++ = '\0';
01333         (void) isCompressed(b, &compressed);
01334         switch(compressed) {
01335         default:
01336         case 0: /* COMPRESSED_NOT */
01337             sprintf(be, "%%__cat %s", b);
01338             break;
01339         case 1: /* COMPRESSED_OTHER */
01340             sprintf(be, "%%__gzip -dc '%s'", b);
01341             break;
01342         case 2: /* COMPRESSED_BZIP2 */
01343             sprintf(be, "%%__bzip2 -dc '%s'", b);
01344             break;
01345         case 3: /* COMPRESSED_ZIP */
01346             sprintf(be, "%%__unzip -qq '%s'", b);
01347             break;
01348         case 4: /* COMPRESSED_LZOP */
01349             sprintf(be, "%%__lzop -dc '%s'", b);
01350             break;
01351         case 5: /* COMPRESSED_LZMA */
01352             sprintf(be, "%%__lzma -dc '%s'", b);
01353             break;
01354         case 6: /* COMPRESSED_XZ */
01355             sprintf(be, "%%__xz -dc '%s'", b);
01356             break;
01357         case 7: /* COMPRESSED_LRZIP */
01358             sprintf(be, "%%__lrzip -dqo- %s", b);
01359             break;
01360         case 8: /* COMPRESSED_LZIP */
01361             sprintf(be, "%%__lzip -dc %s", b);
01362             break;
01363         }
01364         b = be;
01365     } else if (STREQ("mkstemp", f, fn)) {
01366 /*@-globs@*/
01367         for (b = buf; (c = (int)*b) && isblank(c);)
01368             b++;
01369         /* XXX FIXME: file paths with embedded white space needs rework. */
01370         for (be = b; (c = (int)*be) && !isblank(c);)
01371             be++;
01372 /*@=globs@*/
01373 #if defined(HAVE_MKSTEMP)
01374         mode = umask(0077);
01375         (void) close(mkstemp(b));
01376         (void) umask(mode);
01377 #else
01378         (void) mktemp(b);
01379 #endif
01380     } else if (STREQ("mkdtemp", f, fn)) {
01381 /*@-globs@*/
01382         for (b = buf; (c = (int)*b) && isblank(c);)
01383             b++;
01384         /* XXX FIXME: file paths with embedded white space needs rework. */
01385         for (be = b; (c = (int)*be) && !isblank(c);)
01386             be++;
01387 /*@=globs@*/
01388 #if defined(HAVE_MKDTEMP) && !defined(__LCLINT__)
01389         if (mkdtemp(b) == NULL)
01390             perror("mkdtemp");
01391 #else
01392         if ((b = tmpnam(b)) != NULL)
01393             (void) mkdir(b, 0700);      /* XXX S_IWRSXU is not included. */
01394 #endif
01395     } else if (STREQ("uuid", f, fn)) {
01396         int uuid_version;
01397         const char *uuid_ns;
01398         const char *uuid_data;
01399         char *cp;
01400         size_t n;
01401 
01402         uuid_version = 1;
01403         uuid_ns = NULL;
01404         uuid_data = NULL;
01405         cp = buf;
01406         if ((n = strspn(cp, " \t\n")) > 0)
01407             cp += n;
01408         if ((n = strcspn(cp, " \t\n")) > 0) {
01409             uuid_version = (int)strtol(cp, (char **)NULL, 10);
01410             cp += n;
01411             if ((n = strspn(cp, " \t\n")) > 0)
01412                 cp += n;
01413             if ((n = strcspn(cp, " \t\n")) > 0) {
01414                 uuid_ns = cp;
01415                 cp += n;
01416                 *cp++ = '\0';
01417                 if ((n = strspn(cp, " \t\n")) > 0)
01418                     cp += n;
01419                 if ((n = strcspn(cp, " \t\n")) > 0) {
01420                     uuid_data = cp;
01421                     cp += n;
01422                     *cp++ = '\0';
01423                 }
01424             }
01425         }
01426 /*@-nullpass@*/ /* FIX: uuid_ns may be NULL */
01427         if (rpmuuidMake(uuid_version, uuid_ns, uuid_data, buf, NULL))
01428             rpmlog(RPMLOG_ERR, "failed to create UUID\n");
01429         else
01430             b = buf;
01431 /*@=nullpass@*/
01432     } else if (STREQ("S", f, fn)) {
01433         for (b = buf; (c = (int)*b) && xisdigit(c);)
01434             b++;
01435         if (!c) {       /* digit index */
01436             b++;
01437             sprintf(b, "%%SOURCE%s", buf);
01438         } else
01439             b = buf;
01440     } else if (STREQ("P", f, fn)) {
01441         for (b = buf; (c = (int) *b) && xisdigit(c);)
01442             b++;
01443         if (!c) {       /* digit index */
01444             b++;
01445             sprintf(b, "%%PATCH%s", buf);
01446         } else
01447             b = buf;
01448     } else if (STREQ("F", f, fn)) {
01449         b = buf + strlen(buf) + 1;
01450         sprintf(b, "file%s.file", buf);
01451     }
01452 
01453     if (b) {
01454         (void) expandT(mb, b, strlen(b));
01455     }
01456 }
01457 
01458 static int expandFIFO(MacroBuf mb, MacroEntry me, const char *g, size_t gn)
01459         /*@globals rpmGlobalMacroContext, h_errno, fileSystem, internalState @*/
01460         /*@modifies mb, rpmGlobalMacroContext, fileSystem, internalState @*/
01461 {
01462     int rc = 0;
01463 
01464     if (me) {
01465         if (me->prev) {
01466             rc = expandFIFO(mb, me->prev, g, gn);
01467             rc = expandT(mb, g, gn);
01468         }
01469         rc = expandT(mb, me->body, strlen(me->body));
01470     }
01471     return rc;
01472 }
01473 
01474 #if !defined(DEBUG_MACROS)
01475 /* =============================================================== */
01476 /* XXX dupe'd to avoid change in linkage conventions. */
01477 
01478 #define POPT_ERROR_NOARG        -10     
01479 #define POPT_ERROR_BADQUOTE     -15     
01480 #define POPT_ERROR_MALLOC       -21     
01482 #define POPT_ARGV_ARRAY_GROW_DELTA 5
01483 
01484 static int XpoptDupArgv(int argc, char **argv,
01485                 int * argcPtr, char *** argvPtr)
01486         /*@modifies *argcPtr, *argvPtr @*/
01487 {
01488     size_t nb = (argc + 1) * sizeof(*argv);
01489     char ** argv2;
01490     char * dst;
01491     int i;
01492 
01493     if (argc <= 0 || argv == NULL)      /* XXX can't happen */
01494         return POPT_ERROR_NOARG;
01495     for (i = 0; i < argc; i++) {
01496         if (argv[i] == NULL)
01497             return POPT_ERROR_NOARG;
01498         nb += strlen(argv[i]) + 1;
01499     }
01500         
01501     dst = xmalloc(nb);
01502     if (dst == NULL)                    /* XXX can't happen */
01503         return POPT_ERROR_MALLOC;
01504     argv2 = (void *) dst;
01505     dst += (argc + 1) * sizeof(*argv);
01506 
01507     for (i = 0; i < argc; i++) {
01508         argv2[i] = dst;
01509         dst += strlen(strcpy(dst, argv[i])) + 1;
01510     }
01511     argv2[argc] = NULL;
01512 
01513     if (argvPtr) {
01514         *argvPtr = argv2;
01515     } else {
01516         free(argv2);
01517         argv2 = NULL;
01518     }
01519     if (argcPtr)
01520         *argcPtr = argc;
01521     return 0;
01522 }
01523 
01524 static int XpoptParseArgvString(const char * s, int * argcPtr, char *** argvPtr)
01525         /*@modifies *argcPtr, *argvPtr @*/
01526 {
01527     const char * src;
01528     char quote = '\0';
01529     int argvAlloced = POPT_ARGV_ARRAY_GROW_DELTA;
01530     char ** argv = xmalloc(sizeof(*argv) * argvAlloced);
01531     int argc = 0;
01532     size_t buflen = strlen(s) + 1;
01533     char * buf = memset(alloca(buflen), 0, buflen);
01534     int rc = POPT_ERROR_MALLOC;
01535 
01536     if (argv == NULL) return rc;
01537     argv[argc] = buf;
01538 
01539     for (src = s; *src != '\0'; src++) {
01540         if (quote == *src) {
01541             quote = '\0';
01542         } else if (quote != '\0') {
01543             if (*src == '\\') {
01544                 src++;
01545                 if (!*src) {
01546                     rc = POPT_ERROR_BADQUOTE;
01547                     goto exit;
01548                 }
01549                 if (*src != quote) *buf++ = '\\';
01550             }
01551             *buf++ = *src;
01552         } else if (isspace(*src)) {
01553             if (*argv[argc] != '\0') {
01554                 buf++, argc++;
01555                 if (argc == argvAlloced) {
01556                     argvAlloced += POPT_ARGV_ARRAY_GROW_DELTA;
01557                     argv = realloc(argv, sizeof(*argv) * argvAlloced);
01558                     if (argv == NULL) goto exit;
01559                 }
01560                 argv[argc] = buf;
01561             }
01562         } else switch (*src) {
01563           case '"':
01564           case '\'':
01565             quote = *src;
01566             /*@switchbreak@*/ break;
01567           case '\\':
01568             src++;
01569             if (!*src) {
01570                 rc = POPT_ERROR_BADQUOTE;
01571                 goto exit;
01572             }
01573             /*@fallthrough@*/
01574           default:
01575             *buf++ = *src;
01576             /*@switchbreak@*/ break;
01577         }
01578     }
01579 
01580     if (strlen(argv[argc])) {
01581         argc++, buf++;
01582     }
01583 
01584     rc = XpoptDupArgv(argc, argv, argcPtr, argvPtr);
01585 
01586 exit:
01587     if (argv) free(argv);
01588     return rc;
01589 }
01590 #endif  /* !defined(DEBUG_MACROS) */
01591 
01599 #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)
01600 static char * parseEmbedded(const char * s, size_t nb, char *** avp)
01601         /*@*/
01602 {
01603     char * script = NULL;
01604     const char * se;
01605 
01606     /* XXX FIXME: args might have embedded : too. */
01607     for (se = s + 1; se < (s+nb); se++)
01608     switch (*se) {
01609     default:    continue;       /*@notreached@*/ break;
01610     case ':':   goto bingo;     /*@notreached@*/ break;
01611     }
01612 
01613 bingo:
01614     {   size_t na = (size_t)(se-s-1);
01615         char * args = NULL;
01616         int ac;
01617         int rc;
01618 
01619         args = memcpy(xmalloc(na+1), s+1, na);
01620         args[na] = '\0';
01621 
01622         ac = 0;
01623         rc = XpoptParseArgvString(args, &ac, avp);
01624         args = _free(args);
01625         nb -= na;
01626     }
01627 
01628     nb -= 3;
01629     script = memcpy(xmalloc(nb+1), se+1, nb+1);
01630     script[nb] = '\0';
01631     return script;
01632 }
01633 #endif
01634 
01641 static int
01642 expandMacro(MacroBuf mb)
01643         /*@globals rpmGlobalMacroContext,
01644                 print_macro_trace, print_expand_trace, h_errno,
01645                 fileSystem, internalState @*/
01646         /*@modifies mb, rpmGlobalMacroContext,
01647                 print_macro_trace, print_expand_trace,
01648                 fileSystem, internalState @*/
01649 {
01650     MacroEntry *mep;
01651     MacroEntry me;
01652     const char *s = mb->s, *se;
01653     const char *f, *fe;
01654     const char *g, *ge;
01655     size_t fn, gn;
01656     char *t = mb->t;    /* save expansion pointer for printExpand */
01657     int c;
01658     int rc = 0;
01659     int negate;
01660     int stackarray;
01661     const char * lastc;
01662     int chkexist;
01663 
01664     if (++mb->depth > max_macro_depth) {
01665         rpmlog(RPMLOG_ERR,
01666                 _("Recursion depth(%d) greater than max(%d)\n"),
01667                 mb->depth, max_macro_depth);
01668         mb->depth--;
01669         mb->expand_trace = 1;
01670         return 1;
01671     }
01672 
01673     while (rc == 0 && mb->nb > 0 && (c = (int)*s) != (int)'\0') {
01674         s++;
01675         /* Copy text until next macro */
01676         switch(c) {
01677         case '%':
01678                 if (*s != '\0') {       /* Ensure not end-of-string. */
01679                     if (*s != '%')
01680                         /*@switchbreak@*/ break;
01681                     s++;        /* skip first % in %% */
01682                 }
01683                 /*@fallthrough@*/
01684         default:
01685                 SAVECHAR(mb, c);
01686                 continue;
01687                 /*@notreached@*/ /*@switchbreak@*/ break;
01688         }
01689 
01690         /* Expand next macro */
01691         f = fe = NULL;
01692         g = ge = NULL;
01693         if (mb->depth > 1)      /* XXX full expansion for outermost level */
01694                 t = mb->t;      /* save expansion pointer for printExpand */
01695         stackarray = chkexist = negate = 0;
01696         lastc = NULL;
01697         switch ((c = (int) *s)) {
01698         default:                /* %name substitution */
01699                 while (*s != '\0' && strchr("!?@", *s) != NULL) {
01700                         switch(*s++) {
01701                         case '@':
01702                                 stackarray = ((stackarray + 1) % 2);
01703                                 /*@switchbreak@*/ break;
01704                         case '!':
01705                                 negate = ((negate + 1) % 2);
01706                                 /*@switchbreak@*/ break;
01707                         case '?':
01708                                 chkexist++;
01709                                 /*@switchbreak@*/ break;
01710                         }
01711                 }
01712                 f = se = s;
01713                 if (*se == '-')
01714                         se++;
01715                 while((c = (int) *se) && (xisalnum(c) || c == (int) '_'))
01716                         se++;
01717                 /* Recognize non-alnum macros too */
01718                 switch (*se) {
01719                 case '*':
01720                         se++;
01721                         if (*se == '*') se++;
01722                         /*@innerbreak@*/ break;
01723                 case '#':
01724                         se++;
01725                         /*@innerbreak@*/ break;
01726                 default:
01727                         /*@innerbreak@*/ break;
01728                 }
01729                 fe = se;
01730                 /* For "%name " macros ... */
01731 /*@-globs@*/
01732                 if ((c = (int) *fe) && isblank(c))
01733                         if ((lastc = strchr(fe,'\n')) == NULL)
01734                                 lastc = strchr(fe, '\0');
01735 /*@=globs@*/
01736                 /*@switchbreak@*/ break;
01737         case '(':               /* %(...) shell escape */
01738                 if ((se = matchchar(s, (char)c, ')')) == NULL) {
01739                         rpmlog(RPMLOG_ERR,
01740                                 _("Unterminated %c: %s\n"), (char)c, s);
01741                         rc = 1;
01742                         continue;
01743                 }
01744                 if (mb->macro_trace)
01745                         printMacro(mb, s, se+1);
01746 
01747                 s++;    /* skip ( */
01748                 rc = doShellEscape(mb, s, (se - s));
01749                 se++;   /* skip ) */
01750 
01751                 s = se;
01752                 continue;
01753                 /*@notreached@*/ /*@switchbreak@*/ break;
01754         case '{':               /* %{...}/%{...:...} substitution */
01755                 if ((se = matchchar(s, (char)c, '}')) == NULL) {
01756                         rpmlog(RPMLOG_ERR,
01757                                 _("Unterminated %c: %s\n"), (char)c, s);
01758                         rc = 1;
01759                         continue;
01760                 }
01761                 f = s+1;/* skip { */
01762                 se++;   /* skip } */
01763                 while (strchr("!?@", *f) != NULL) {
01764                         switch(*f++) {
01765                         case '@':
01766                                 stackarray = ((stackarray + 1) % 2);
01767                                 /*@switchbreak@*/ break;
01768                         case '!':
01769                                 negate = ((negate + 1) % 2);
01770                                 /*@switchbreak@*/ break;
01771                         case '?':
01772                                 chkexist++;
01773                                 /*@switchbreak@*/ break;
01774                         }
01775                 }
01776                 /* Find end-of-expansion, handle %{foo:bar} expansions. */
01777                 for (fe = f; (c = (int) *fe) && !strchr(" :}", c);)
01778                         fe++;
01779                 switch (c) {
01780                 case ':':
01781                         g = fe + 1;
01782                         ge = se - 1;
01783                         /*@innerbreak@*/ break;
01784                 case ' ':
01785                         lastc = se-1;
01786                         /*@innerbreak@*/ break;
01787                 default:
01788                         /*@innerbreak@*/ break;
01789                 }
01790                 /*@switchbreak@*/ break;
01791         }
01792 
01793         /* XXX Everything below expects fe > f */
01794         fn = (fe - f);
01795         gn = (ge - g);
01796         if ((fe - f) <= 0) {
01797 /* XXX Process % in unknown context */
01798                 c = (int) '%';  /* XXX only need to save % */
01799                 SAVECHAR(mb, c);
01800 #if 0
01801                 rpmlog(RPMLOG_ERR,
01802                         _("A %% is followed by an unparseable macro\n"));
01803 #endif
01804                 s = se;
01805                 continue;
01806         }
01807 
01808         if (mb->macro_trace)
01809                 printMacro(mb, s, se);
01810 
01811         /* Expand builtin macros */
01812         if (STREQ("load", f, fn)) {
01813                 if (g != NULL) {
01814                     char * mfn = strncpy(alloca(gn + 1), g, gn);
01815                     int xx;
01816                     mfn[gn] = '\0';
01817                     xx = rpmLoadMacroFile(NULL, mfn, _max_load_depth);
01818                     /* Print failure iff %{load:...} or %{!?load:...} */
01819                     if (xx != 0 && chkexist == negate)
01820                         rpmlog(RPMLOG_ERR, _("%s: load macros failed\n"), mfn);
01821                 }
01822                 s = se;
01823                 continue;
01824         }
01825         if (STREQ("global", f, fn)) {
01826                 s = doDefine(mb, se, RMIL_GLOBAL, 1);
01827                 continue;
01828         }
01829         if (STREQ("define", f, fn)) {
01830                 s = doDefine(mb, se, mb->depth, 0);
01831                 continue;
01832         }
01833         if (STREQ("undefine", f, fn)) {
01834                 s = doUndefine(mb->mc, se);
01835                 continue;
01836         }
01837 
01838         if (STREQ("echo", f, fn) ||
01839             STREQ("warn", f, fn) ||
01840             STREQ("error", f, fn)) {
01841                 int waserror = 0;
01842                 if (STREQ("error", f, fn))
01843                         waserror = 1, rc = 1;
01844                 if (g != NULL && g < ge)
01845                         doOutput(mb, waserror, g, gn);
01846                 else
01847                         doOutput(mb, waserror, f, fn);
01848                 s = se;
01849                 continue;
01850         }
01851 
01852         if (STREQ("trace", f, fn)) {
01853                 /* XXX TODO restore expand_trace/macro_trace to 0 on return */
01854                 mb->expand_trace = mb->macro_trace = (negate ? 0 : mb->depth);
01855                 if (mb->depth == 1) {
01856                         print_macro_trace = mb->macro_trace;
01857                         print_expand_trace = mb->expand_trace;
01858                 }
01859                 s = se;
01860                 continue;
01861         }
01862 
01863         if (STREQ("dump", f, fn)) {
01864                 rpmDumpMacroTable(mb->mc, NULL);
01865                 while (iseol(*se))
01866                         se++;
01867                 s = se;
01868                 continue;
01869         }
01870 
01871 #ifdef  WITH_LUA
01872         if (STREQ("lua", f, fn)) {
01873                 rpmlua lua = rpmluaGetGlobalState();
01874                 rpmlua olua = memcpy(alloca(sizeof(*olua)), lua, sizeof(*olua));
01875                 const char *ls = s+sizeof("{lua:")-1;
01876                 const char *lse = se-sizeof("}")+1;
01877                 char *scriptbuf = (char *)xmalloc((lse-ls)+1);
01878                 const char *printbuf;
01879 
01880                 /* Reset the stateful output buffer before recursing down. */
01881                 lua->storeprint = 1;
01882                 lua->printbuf = NULL;
01883                 lua->printbufsize = 0;
01884                 lua->printbufused = 0;
01885 
01886                 memcpy(scriptbuf, ls, lse-ls);
01887                 scriptbuf[lse-ls] = '\0';
01888                 if (rpmluaRunScript(lua, scriptbuf, NULL) == -1)
01889                     rc = 1;
01890                 printbuf = rpmluaGetPrintBuffer(lua);
01891                 if (printbuf) {
01892                     size_t len = strlen(printbuf);
01893                     if (len > mb->nb)
01894                         len = mb->nb;
01895                     memcpy(mb->t, printbuf, len);
01896                     mb->t += len;
01897                     mb->nb -= len;
01898                 }
01899 
01900                 /* Restore the stateful output buffer after recursion. */
01901                 lua->storeprint = olua->storeprint;
01902                 lua->printbuf = olua->printbuf;
01903                 lua->printbufsize = olua->printbufsize;
01904                 lua->printbufused = olua->printbufused;
01905 
01906                 free(scriptbuf);
01907                 s = se;
01908                 continue;
01909         }
01910 #endif
01911 
01912 #ifdef  WITH_AUGEAS
01913         if (STREQ("augeas", f, fn)) {
01914                 /* XXX change rpmaugNew() to common embedded interpreter API */
01915 #ifdef  NOTYET
01916                 char ** av = NULL;
01917                 char * script = parseEmbedded(s, (size_t)(se-s), &av);
01918 #else
01919                 char * script = strndup(g, (size_t)(se-g-1));
01920 #endif
01921                 rpmaug aug = (_globalI ? NULL
01922                     : rpmaugNew(_rpmaugRoot, _rpmaugLoadpath, _rpmaugFlags));
01923                 const char * result = NULL;
01924 
01925                 if (rpmaugRun(aug, script, &result) != RPMRC_OK)
01926                     rc = 1;
01927                 else {
01928                   if (result == NULL) result = "FIXME";
01929                   if (result != NULL && *result != '\0') {
01930                     size_t len = strlen(result);
01931                     if (len > mb->nb)
01932                         len = mb->nb;
01933                     memcpy(mb->t, result, len);
01934                     mb->t += len;
01935                     mb->nb -= len;
01936                  }
01937                 }
01938                 aug = rpmaugFree(aug);
01939 #ifdef  NOTYET
01940                 av = _free(av);
01941 #endif
01942                 script = _free(script);
01943                 s = se;
01944                 continue;
01945         }
01946 #endif
01947 
01948 #ifdef  WITH_FICL
01949         if (STREQ("ficl", f, fn)) {
01950                 char ** av = NULL;
01951                 char * script = parseEmbedded(s, (size_t)(se-s), &av);
01952                 rpmficl ficl = rpmficlNew(av, _globalI);
01953                 const char * result = NULL;
01954 
01955                 if (rpmficlRun(ficl, script, &result) != RPMRC_OK)
01956                     rc = 1;
01957                 else {
01958                   if (result == NULL) result = "FIXME";
01959                   if (result != NULL && *result != '\0') {
01960                     size_t len = strlen(result);
01961                     if (len > mb->nb)
01962                         len = mb->nb;
01963                     memcpy(mb->t, result, len);
01964                     mb->t += len;
01965                     mb->nb -= len;
01966                  }
01967                 }
01968                 ficl = rpmficlFree(ficl);
01969                 av = _free(av);
01970                 script = _free(script);
01971                 s = se;
01972                 continue;
01973         }
01974 #endif
01975 
01976 #ifdef  WITH_GPSEE
01977         if (STREQ("js", f, fn)) {
01978                 char ** av = NULL;
01979                 char * script = parseEmbedded(s, (size_t)(se-s), &av);
01980                 rpmjs js = rpmjsNew(av, _globalI);
01981                 const char * result = NULL;
01982 
01983                 if (rpmjsRun(js, script, &result) != RPMRC_OK)
01984                     rc = 1;
01985                 else {
01986                   if (result == NULL) result = "FIXME";
01987                   if (result != NULL && *result != '\0') {
01988                     size_t len = strlen(result);
01989                     if (len > mb->nb)
01990                         len = mb->nb;
01991                     memcpy(mb->t, result, len);
01992                     mb->t += len;
01993                     mb->nb -= len;
01994                  }
01995                 }
01996                 js = rpmjsFree(js);
01997                 av = _free(av);
01998                 script = _free(script);
01999                 s = se;
02000                 continue;
02001         }
02002 #endif
02003 
02004 #ifdef  WITH_NIX
02005         if (STREQ("nix", f, fn)) {
02006                 char ** av = NULL;
02007                 char * script = parseEmbedded(s, (size_t)(se-s), &av);
02008                 int (*_vec) (rpmnix nix) = rpmnixEcho;
02009                 uint32_t _flags = RPMNIX_FLAGS_NONE;
02010                 rpmnix nix;
02011                 const char * result = NULL;
02012                 int xx;
02013 
02014                 if (av == NULL || av[0] == NULL || av[1] == NULL
02015                  || !strcmp(av[0], "echo"))
02016                 {
02017                     _vec = rpmnixEcho;
02018                 } else
02019                 if (!strcmp(av[1], "build")) {
02020                     _vec = rpmnixBuild;
02021                     _flags = RPMNIX_FLAGS_NOOUTLINK;
02022                 } else
02023                 if (!strcmp(av[1], "channel")) {
02024                     _vec = rpmnixChannel;
02025                 } else
02026                 if (!strcmp(av[1], "collect-garbage")) {
02027                     _vec = rpmnixCollectGarbage;
02028                 } else
02029                 if (!strcmp(av[1], "copy-closure")) {
02030                     _vec = rpmnixCopyClosure;
02031                 } else
02032                 if (!strcmp(av[1], "env")) {
02033                     _vec = rpmnixEnv;
02034                 } else
02035                 if (!strcmp(av[1], "hash")) {
02036                     _vec = rpmnixHash;
02037                 } else
02038                 if (!strcmp(av[1], "install-package")) {
02039                     _vec = rpmnixInstallPackage;
02040                     _flags = RPMNIX_FLAGS_INTERACTIVE;
02041                 } else
02042                 if (!strcmp(av[1], "instantiate")) {
02043                     _vec = rpmnixInstantiate;
02044                 } else
02045                 if (!strcmp(av[1], "prefetch-url")) {
02046                     _vec = rpmnixPrefetchURL;
02047                 } else
02048                 if (!strcmp(av[1], "push")) {
02049                     _vec = rpmnixPush;
02050                 } else
02051                 if (!strcmp(av[1], "pull")) {
02052                     _vec = rpmnixPull;
02053                 } else
02054                 if (!strcmp(av[1], "store")) {
02055                     _vec = rpmnixStore;
02056                 } else
02057                 if (!strcmp(av[1], "worker")) {
02058                     _vec = rpmnixWorker;
02059                 } else
02060 assert(0);
02061 
02062                 nix = rpmnixNew(av, _flags, NULL);
02063 
02064 #ifdef  NOTYET
02065                 if (rpmnixRun(nix, script, &result) != RPMRC_OK)
02066                     rc = 1;
02067                 else {
02068                   if (result == NULL) result = "FIXME";
02069                   if (result != NULL && *result != '\0') {
02070                     size_t len = strlen(result);
02071                     if (len > mb->nb)
02072                         len = mb->nb;
02073                     memcpy(mb->t, result, len);
02074                     mb->t += len;
02075                     mb->nb -= len;
02076                  }
02077                 }
02078 #else
02079                 xx = (*_vec) (nix);
02080                 result = xstrdup("");
02081 #endif  /* NOTYET */
02082 
02083                 nix = rpmnixFree(nix);
02084                 av = _free(av);
02085                 script = _free(script);
02086                 s = se;
02087                 continue;
02088         }
02089 #endif
02090 
02091 #ifdef  WITH_PERLEMBED
02092         if (STREQ("perl", f, fn)) {
02093                 char ** av = NULL;
02094                 char * script = parseEmbedded(s, (size_t)(se-s), &av);
02095                 rpmperl perl = rpmperlNew(av, _globalI);
02096                 const char * result = NULL;
02097 
02098                 if (rpmperlRun(perl, script, &result) != RPMRC_OK)
02099                     rc = 1;
02100                 else {
02101                   if (result == NULL) result = "FIXME";
02102                   if (result != NULL && *result != '\0') {
02103                     size_t len = strlen(result);
02104                     if (len > mb->nb)
02105                         len = mb->nb;
02106                     memcpy(mb->t, result, len);
02107                     mb->t += len;
02108                     mb->nb -= len;
02109                  }
02110                 }
02111                 perl = rpmperlFree(perl);
02112                 av = _free(av);
02113                 script = _free(script);
02114                 s = se;
02115                 continue;
02116         }
02117 #endif
02118 
02119 #ifdef  WITH_PYTHONEMBED
02120         if (STREQ("python", f, fn)) {
02121                 char ** av = NULL;
02122                 char * script = parseEmbedded(s, (size_t)(se-s), &av);
02123                 rpmpython python = rpmpythonNew(av, _globalI);
02124                 const char * result = NULL;
02125 
02126                 if (rpmpythonRun(python, script, &result) != RPMRC_OK)
02127                     rc = 1;
02128                 else {
02129                   if (result == NULL) result = "FIXME";
02130                   if (result != NULL && *result != '\0') {
02131                     size_t len = strlen(result);
02132                     if (len > mb->nb)
02133                         len = mb->nb;
02134                     memcpy(mb->t, result, len);
02135                     mb->t += len;
02136                     mb->nb -= len;
02137                   }
02138                 }
02139                 python = rpmpythonFree(python);
02140                 av = _free(av);
02141                 script = _free(script);
02142                 s = se;
02143                 continue;
02144         }
02145 #endif
02146 
02147 #ifdef  WITH_RUBYEMBED
02148         if (STREQ("ruby", f, fn)) {
02149                 char ** av = NULL;
02150                 char * script = parseEmbedded(s, (size_t)(se-s), &av);
02151                 rpmruby ruby = rpmrubyNew(av, _globalI);
02152                 const char * result = NULL;
02153 
02154                 if (rpmrubyRun(ruby, script, &result) != RPMRC_OK)
02155                     rc = 1;
02156                 else {
02157                   if (result == NULL) result = "FIXME";
02158                   if (result != NULL && *result != '\0') {
02159                     size_t len = strlen(result);
02160                     if (len > mb->nb)
02161                         len = mb->nb;
02162                     memcpy(mb->t, result, len);
02163                     mb->t += len;
02164                     mb->nb -= len;
02165                   }
02166                 }
02167                 ruby = rpmrubyFree(ruby);
02168                 av = _free(av);
02169                 script = _free(script);
02170                 s = se;
02171                 continue;
02172         }
02173 #endif
02174 
02175 #ifdef  WITH_SEMANAGE
02176         if (STREQ("spook", f, fn)) {
02177                 /* XXX change rpmsmNew() to common embedded interpreter API */
02178 #ifdef  NOTYET
02179                 char ** av = NULL;
02180                 char * script = parseEmbedded(s, (size_t)(se-s), &av);
02181 #else
02182                 /* XXX use xstrndup (which never returns NULL) instead. */
02183                 char * script = strndup(g, (size_t)(se-g-1));
02184                 char * av[2];
02185                 /* XXX FIXME */
02186                 static const char * _rpmsmStore = "targeted";
02187                 static unsigned int _rpmsmFlags = 0;
02188 #endif
02189                 rpmsm sm = (_globalI ? NULL
02190                     : rpmsmNew(_rpmsmStore, _rpmsmFlags));
02191                 const char * result = NULL;
02192 
02193                 /* XXX HACK: use an argv for now. */
02194                 av[0] = script;
02195                 av[1] = NULL;
02196                 if (rpmsmRun(sm, av, &result) != RPMRC_OK)
02197                     rc = 1;
02198                 else {
02199                   if (result == NULL) result = "FIXME";
02200                   if (result != NULL && *result != '\0') {
02201                     size_t len = strlen(result);
02202                     if (len > mb->nb)
02203                         len = mb->nb;
02204                     memcpy(mb->t, result, len);
02205                     mb->t += len;
02206                     mb->nb -= len;
02207                  }
02208                 }
02209                 sm = rpmsmFree(sm);
02210 #ifdef  NOTYET
02211                 av = _free(av);
02212 #endif
02213                 script = _free(script);
02214                 s = se;
02215                 continue;
02216         }
02217 #endif
02218 
02219 #ifdef  WITH_SQLITE
02220         if (STREQ("sql", f, fn)) {
02221                 char ** av = NULL;
02222                 char * script = parseEmbedded(s, (size_t)(se-s), &av);
02223                 rpmsql sql = rpmsqlNew(av, _globalI);
02224                 const char * result = NULL;
02225 
02226                 if (rpmsqlRun(sql, script, &result) != RPMRC_OK)
02227                     rc = 1;
02228                 else {
02229                   if (result == NULL) result = "FIXME";
02230                   if (result != NULL && *result != '\0') {
02231                     size_t len = strlen(result);
02232                     if (len > mb->nb)
02233                         len = mb->nb;
02234                     memcpy(mb->t, result, len);
02235                     mb->t += len;
02236                     mb->nb -= len;
02237                   }
02238                 }
02239                 sql = rpmsqlFree(sql);
02240                 av = _free(av);
02241                 script = _free(script);
02242                 s = se;
02243                 continue;
02244         }
02245 #endif
02246 
02247 #ifdef  WITH_SQUIRREL
02248         if (STREQ("squirrel", f, fn)) {
02249                 char ** av = NULL;
02250                 char * script = parseEmbedded(s, (size_t)(se-s), &av);
02251                 rpmsquirrel squirrel = rpmsquirrelNew(av, _globalI);
02252                 const char * result = NULL;
02253 
02254                 if (rpmsquirrelRun(squirrel, script, &result) != RPMRC_OK)
02255                     rc = 1;
02256                 else {
02257                   if (result == NULL) result = "FIXME";
02258                   if (result != NULL && *result != '\0') {
02259                     size_t len = strlen(result);
02260                     if (len > mb->nb)
02261                         len = mb->nb;
02262                     memcpy(mb->t, result, len);
02263                     mb->t += len;
02264                     mb->nb -= len;
02265                   }
02266                 }
02267                 squirrel = rpmsquirrelFree(squirrel);
02268                 av = _free(av);
02269                 script = _free(script);
02270                 s = se;
02271                 continue;
02272         }
02273 #endif
02274 
02275 #ifdef  WITH_TCL
02276         if (STREQ("tcl", f, fn)) {
02277                 char ** av = NULL;
02278                 char * script = parseEmbedded(s, (size_t)(se-s), &av);
02279                 rpmtcl tcl = rpmtclNew(av, _globalI);
02280                 const char * result = NULL;
02281 
02282                 if (rpmtclRun(tcl, script, &result) != RPMRC_OK)
02283                     rc = 1;
02284                 else if (result != NULL && *result != '\0') {
02285                     size_t len = strlen(result);
02286                     if (len > mb->nb)
02287                         len = mb->nb;
02288                     memcpy(mb->t, result, len);
02289                     mb->t += len;
02290                     mb->nb -= len;
02291                 }
02292                 tcl = rpmtclFree(tcl);
02293                 av = _free(av);
02294                 script = _free(script);
02295                 s = se;
02296                 continue;
02297         }
02298 #endif
02299 
02300         /* Rewrite "%patchNN ..." as "%patch -P NN ..." and expand. */
02301         if (lastc && fn > 5 && STREQ("patch", f, 5) && xisdigit((int)f[5])) {
02302                 /*@-internalglobs@*/ /* FIX: verbose may be set */
02303                 doFoo(mb, negate, f, (lastc - f), NULL, 0);
02304                 /*@=internalglobs@*/
02305                 s = lastc;
02306                 continue;
02307         }
02308 
02309         /* XXX necessary but clunky */
02310         if (STREQ("basename", f, fn) ||
02311             STREQ("dirname", f, fn) ||
02312             STREQ("realpath", f, fn) ||
02313             STREQ("getenv", f, fn) ||
02314             STREQ("shrink", f, fn) ||
02315             STREQ("suffix", f, fn) ||
02316             STREQ("expand", f, fn) ||
02317             STREQ("verbose", f, fn) ||
02318             STREQ("uncompress", f, fn) ||
02319             STREQ("mkstemp", f, fn) ||
02320             STREQ("mkdtemp", f, fn) ||
02321             STREQ("uuid", f, fn) ||
02322             STREQ("url2path", f, fn) ||
02323             STREQ("u2p", f, fn) ||
02324             STREQ("S", f, fn) ||
02325             STREQ("P", f, fn) ||
02326             STREQ("F", f, fn)) {
02327                 /*@-internalglobs@*/ /* FIX: verbose may be set */
02328                 doFoo(mb, negate, f, fn, g, gn);
02329                 /*@=internalglobs@*/
02330                 s = se;
02331                 continue;
02332         }
02333 
02334         /* Expand defined macros */
02335         mep = findEntry(mb->mc, f, fn);
02336         me = (mep ? *mep : NULL);
02337 
02338         /* XXX Special processing for flags */
02339         if (*f == '-') {
02340                 if (me)
02341                         me->used++;     /* Mark macro as used */
02342                 if ((me == NULL && !negate) ||  /* Without -f, skip %{-f...} */
02343                     (me != NULL && negate)) {   /* With -f, skip %{!-f...} */
02344                         s = se;
02345                         continue;
02346                 }
02347 
02348                 if (g && g < ge) {              /* Expand X in %{-f:X} */
02349                         rc = expandT(mb, g, gn);
02350                 } else
02351                 if (me && me->body && *me->body) {/* Expand %{-f}/%{-f*} */
02352                         rc = expandT(mb, me->body, strlen(me->body));
02353                 }
02354                 s = se;
02355                 continue;
02356         }
02357 
02358         /* XXX Special processing for macro existence */
02359         if (chkexist) {
02360                 if ((me == NULL && !negate) ||  /* Without -f, skip %{?f...} */
02361                     (me != NULL && negate)) {   /* With -f, skip %{!?f...} */
02362                         s = se;
02363                         continue;
02364                 }
02365                 if (g && g < ge) {              /* Expand X in %{?f:X} */
02366                         rc = expandT(mb, g, gn);
02367                 } else
02368                 if (me && me->body && *me->body) { /* Expand %{?f}/%{?f*} */
02369                         rc = expandT(mb, me->body, strlen(me->body));
02370                 }
02371                 s = se;
02372                 continue;
02373         }
02374 
02375         if (me == NULL) {       /* leave unknown %... as is */
02376 #if !defined(RPM_VENDOR_WINDRIVER_DEBUG)        /* XXX usually disabled */
02377 #if DEAD
02378                 /* XXX hack to skip over empty arg list */
02379                 if (fn == 1 && *f == '*') {
02380                         s = se;
02381                         continue;
02382                 }
02383 #endif
02384                 /* XXX hack to permit non-overloaded %foo to be passed */
02385                 c = (int) '%';  /* XXX only need to save % */
02386                 SAVECHAR(mb, c);
02387 #else
02388                 if (!strncmp(f, "if", fn) ||
02389                     !strncmp(f, "else", fn) ||
02390                     !strncmp(f, "endif", fn)) {
02391                         c = '%';        /* XXX only need to save % */
02392                         SAVECHAR(mb, c);
02393                 } else {
02394                         rpmlog(RPMLOG_ERR,
02395                                 _("Macro %%%.*s not found, skipping\n"), fn, f);
02396                         s = se;
02397                 }
02398 #endif
02399                 continue;
02400         }
02401 
02402         /* XXX Special processing to create a tuple from stack'd values. */
02403         if (stackarray) {
02404                 if (!(g && g < ge)) {
02405                         g = "\n";
02406                         gn = strlen(g);
02407                 }
02408                 rc = expandFIFO(mb, me, g, gn);
02409                 s = se;
02410                 continue;
02411         }
02412 
02413         /* Setup args for "%name " macros with opts */
02414         if (me && me->opts != NULL) {
02415                 if (lastc != NULL) {
02416                         se = grabArgs(mb, me, fe, lastc);
02417                 } else {
02418                         addMacro(mb->mc, "**", NULL, "", mb->depth);
02419                         addMacro(mb->mc, "*", NULL, "", mb->depth);
02420                         addMacro(mb->mc, "#", NULL, "0", mb->depth);
02421                         addMacro(mb->mc, "0", NULL, me->name, mb->depth);
02422                 }
02423         }
02424 
02425         /* Recursively expand body of macro */
02426         if (me->body && *me->body) {
02427                 mb->s = me->body;
02428                 rc = expandMacro(mb);
02429                 if (rc == 0)
02430                         me->used++;     /* Mark macro as used */
02431         }
02432 
02433         /* Free args for "%name " macros with opts */
02434         if (me->opts != NULL)
02435                 freeArgs(mb);
02436 
02437         s = se;
02438     }
02439 
02440     *mb->t = '\0';
02441     mb->s = s;
02442     mb->depth--;
02443     if (rc != 0 || mb->expand_trace)
02444         printExpansion(mb, t, mb->t);
02445     return rc;
02446 }
02447 
02448 #if defined(RPM_VENDOR_OPENPKG) /* stick-with-rpm-file-sanity-checking */ || \
02449     !defined(POPT_ERROR_BADCONFIG)      /* XXX POPT 1.15 retrofit */
02450 int rpmSecuritySaneFile(const char *filename)
02451 {
02452     struct stat sb;
02453     uid_t uid;
02454 
02455     if (stat(filename, &sb) == -1)
02456         return 1;
02457     uid = getuid();
02458     if (sb.st_uid != uid)
02459         return 0;
02460     if (!S_ISREG(sb.st_mode))
02461         return 0;
02462     if (sb.st_mode & (S_IWGRP|S_IWOTH))
02463         return 0;
02464     return 1;
02465 }
02466 #endif
02467 
02468 #if !defined(DEBUG_MACROS)
02469 /* =============================================================== */
02470 /*@unchecked@*/
02471 static int _debug = 0;
02472 
02473 int rpmGlob(const char * patterns, int * argcPtr, const char *** argvPtr)
02474 {
02475     int ac = 0;
02476     char ** av = NULL;
02477     int argc = 0;
02478     const char ** argv = NULL;
02479     char * globRoot = NULL;
02480 #ifdef ENABLE_NLS
02481     const char * old_collate = NULL;
02482     const char * old_ctype = NULL;
02483     const char * t;
02484 #endif
02485     size_t maxb, nb;
02486     size_t i;
02487     int j;
02488     int rc;
02489 
02490     rc = XpoptParseArgvString(patterns, &ac, &av);
02491     if (rc)
02492         return rc;
02493 #ifdef ENABLE_NLS
02494     t = setlocale(LC_COLLATE, NULL);
02495     if (t)
02496         old_collate = xstrdup(t);
02497     t = setlocale(LC_CTYPE, NULL);
02498     if (t)
02499         old_ctype = xstrdup(t);
02500     (void) setlocale(LC_COLLATE, "C");
02501     (void) setlocale(LC_CTYPE, "C");
02502 #endif
02503         
02504     if (av != NULL)
02505     for (j = 0; j < ac; j++) {
02506         const char * globURL;
02507         const char * path;
02508         int ut = urlPath(av[j], &path);
02509         glob_t gl;
02510 
02511         if (!Glob_pattern_p(av[j], 0) && strchr(path, '~') == NULL) {
02512             argv = xrealloc(argv, (argc+2) * sizeof(*argv));
02513             argv[argc] = xstrdup(av[j]);
02514 if (_debug)
02515 fprintf(stderr, "*** rpmGlob argv[%d] \"%s\"\n", argc, argv[argc]);
02516             argc++;
02517             continue;
02518         }
02519         
02520         gl.gl_pathc = 0;
02521         gl.gl_pathv = NULL;
02522         rc = Glob(av[j], GLOB_TILDE, Glob_error, &gl);
02523         if (rc)
02524             goto exit;
02525 
02526         /* XXX Prepend the URL leader for globs that have stripped it off */
02527         maxb = 0;
02528         for (i = 0; i < gl.gl_pathc; i++) {
02529             if ((nb = strlen(&(gl.gl_pathv[i][0]))) > maxb)
02530                 maxb = nb;
02531         }
02532         
02533         nb = ((ut == URL_IS_PATH) ? (path - av[j]) : 0);
02534         maxb += nb;
02535         maxb += 1;
02536         globURL = globRoot = xmalloc(maxb);
02537 
02538         switch (ut) {
02539         case URL_IS_PATH:
02540         case URL_IS_DASH:
02541             strncpy(globRoot, av[j], nb);
02542             /*@switchbreak@*/ break;
02543         case URL_IS_HKP:
02544         case URL_IS_FTP:
02545         case URL_IS_HTTP:
02546         case URL_IS_HTTPS:
02547         case URL_IS_MONGO:      /* XXX FIXME */
02548         case URL_IS_UNKNOWN:
02549         default:
02550             /*@switchbreak@*/ break;
02551         }
02552         globRoot += nb;
02553         *globRoot = '\0';
02554 if (_debug)
02555 fprintf(stderr, "*** GLOB maxb %d diskURL %d %*s globURL %p %s\n", (int)maxb, (int)nb, (int)nb, av[j], globURL, globURL);
02556         
02557         argv = xrealloc(argv, (argc+gl.gl_pathc+1) * sizeof(*argv));
02558 
02559         if (argv != NULL)
02560         for (i = 0; i < gl.gl_pathc; i++) {
02561             const char * globFile = &(gl.gl_pathv[i][0]);
02562             if (globRoot > globURL && globRoot[-1] == '/')
02563                 while (*globFile == '/') globFile++;
02564             strcpy(globRoot, globFile);
02565 if (_debug)
02566 fprintf(stderr, "*** rpmGlob argv[%d] \"%s\"\n", argc, globURL);
02567             argv[argc++] = xstrdup(globURL);
02568         }
02569         /*@-immediatetrans@*/
02570         Globfree(&gl);
02571         /*@=immediatetrans@*/
02572         globURL = _free(globURL);
02573     }
02574 
02575     if (argv != NULL && argc > 0) {
02576         argv[argc] = NULL;
02577         if (argvPtr)
02578             *argvPtr = argv;
02579         if (argcPtr)
02580             *argcPtr = argc;
02581         rc = 0;
02582     } else
02583         rc = 1;
02584 
02585 
02586 exit:
02587 #ifdef ENABLE_NLS       
02588     if (old_collate) {
02589         (void) setlocale(LC_COLLATE, old_collate);
02590         old_collate = _free(old_collate);
02591     }
02592     if (old_ctype) {
02593         (void) setlocale(LC_CTYPE, old_ctype);
02594         old_ctype = _free(old_ctype);
02595     }
02596 #endif
02597     av = _free(av);
02598     if (rc || argvPtr == NULL) {
02599 /*@-dependenttrans -unqualifiedtrans@*/
02600         if (argv != NULL)
02601         for (j = 0; j < argc; j++)
02602             argv[j] = _free(argv[j]);
02603         argv = _free(argv);
02604 /*@=dependenttrans =unqualifiedtrans@*/
02605     }
02606     return rc;
02607 }
02608 #endif  /* !defined(DEBUG_MACROS) */
02609 
02610 /* =============================================================== */
02611 
02612 int
02613 expandMacros(void * spec, MacroContext mc, char * sbuf, size_t slen)
02614 {
02615     MacroBuf mb = alloca(sizeof(*mb));
02616     char *tbuf;
02617     int rc;
02618 
02619     if (sbuf == NULL || slen == 0)
02620         return 0;
02621     if (mc == NULL) mc = rpmGlobalMacroContext;
02622 
02623     tbuf = alloca(slen + 1);
02624     tbuf[0] = '\0';
02625 
02626     mb->s = sbuf;
02627     mb->t = tbuf;
02628     mb->nb = slen;
02629     mb->depth = 0;
02630     mb->macro_trace = print_macro_trace;
02631     mb->expand_trace = print_expand_trace;
02632 
02633     mb->spec = spec;    /* (future) %file expansion info */
02634     mb->mc = mc;
02635 
02636     rc = expandMacro(mb);
02637 
02638     tbuf[slen] = '\0';
02639     if (mb->nb == 0)
02640         rpmlog(RPMLOG_ERR, _("Macro expansion too big for target buffer\n"));
02641     else
02642         strncpy(sbuf, tbuf, (slen - mb->nb + 1));
02643 
02644     return rc;
02645 }
02646 
02647 void
02648 addMacro(MacroContext mc,
02649         const char * n, const char * o, const char * b, int level)
02650 {
02651     MacroEntry * mep;
02652     const char * name = n;
02653 
02654     if (*name == '.')           /* XXX readonly macros */
02655         name++;
02656     if (*name == '.')           /* XXX readonly macros */
02657         name++;
02658 
02659     if (mc == NULL) mc = rpmGlobalMacroContext;
02660 
02661     /* If new name, expand macro table */
02662     if ((mep = findEntry(mc, name, 0)) == NULL) {
02663         if (mc->firstFree == mc->macrosAllocated)
02664             expandMacroTable(mc);
02665         if (mc->macroTable != NULL)
02666             mep = mc->macroTable + mc->firstFree++;
02667     }
02668 
02669     if (mep != NULL) {
02670         /* XXX permit "..foo" to be pushed over ".foo" */
02671         if (*mep && (*mep)->flags && !(n[0] == '.' && n[1] == '.')) {
02672             /* XXX avoid error message for %buildroot */
02673             if (strcmp((*mep)->name, "buildroot"))
02674                 rpmlog(RPMLOG_ERR, _("Macro '%s' is readonly and cannot be changed.\n"), n);
02675             return;
02676         }
02677         /* Push macro over previous definition */
02678         pushMacro(mep, n, o, b, level);
02679 
02680         /* If new name, sort macro table */
02681         if ((*mep)->prev == NULL)
02682             sortMacroTable(mc);
02683     }
02684 }
02685 
02686 void
02687 delMacro(MacroContext mc, const char * n)
02688 {
02689     MacroEntry * mep;
02690 
02691     if (mc == NULL) mc = rpmGlobalMacroContext;
02692     /* If name exists, pop entry */
02693     if ((mep = findEntry(mc, n, 0)) != NULL) {
02694         popMacro(mep);
02695         /* If deleted name, sort macro table */
02696         if (!(mep && *mep))
02697             sortMacroTable(mc);
02698     }
02699 }
02700 
02701 /*@-mustmod@*/ /* LCL: mc is modified through mb->mc, mb is abstract */
02702 int
02703 rpmDefineMacro(MacroContext mc, const char * macro, int level)
02704 {
02705     MacroBuf mb = alloca(sizeof(*mb));
02706 
02707     memset(mb, 0, sizeof(*mb));
02708     /* XXX just enough to get by */
02709     mb->mc = (mc ? mc : rpmGlobalMacroContext);
02710     (void) doDefine(mb, macro, level, 0);
02711     return 0;
02712 }
02713 /*@=mustmod@*/
02714 
02715 /*@-mustmod@*/ /* LCL: mc is modified through mb->mc, mb is abstract */
02716 int
02717 rpmUndefineMacro(MacroContext mc, const char * macro)
02718 {
02719     (void) doUndefine(mc ? mc : rpmGlobalMacroContext, macro);
02720     return 0;
02721 }
02722 /*@=mustmod@*/
02723 
02724 void
02725 rpmLoadMacros(MacroContext mc, int level)
02726 {
02727 
02728     if (mc == NULL || mc == rpmGlobalMacroContext)
02729         return;
02730 
02731     if (mc->macroTable != NULL) {
02732         int i;
02733         for (i = 0; i < mc->firstFree; i++) {
02734             MacroEntry *mep, me;
02735             mep = &mc->macroTable[i];
02736             me = *mep;
02737 
02738             if (me == NULL)             /* XXX this should never happen */
02739                 continue;
02740             addMacro(NULL, me->name, me->opts, me->body, (level - 1));
02741         }
02742     }
02743 }
02744 
02745 #if defined(RPM_VENDOR_OPENPKG) /* expand-macrosfile-macro */
02746 static void expand_macrosfile_macro(const char *file_name, const char *buf, size_t bufn)
02747 {
02748     char *cp;
02749     size_t l, k;
02750     static const char *macro_name = "%{macrosfile}";
02751 
02752     l = strlen(macro_name);
02753     k = strlen(file_name);
02754     while ((cp = strstr(buf, macro_name)) != NULL) {
02755         if (((strlen(buf) - l) + k) < bufn) {
02756             memmove(cp+k, cp+l, strlen(cp+l)+1);
02757             memcpy(cp, file_name, k);
02758         }
02759     }
02760     return;
02761 }
02762 #endif
02763 
02764 int
02765 rpmLoadMacroFile(MacroContext mc, const char * fn, int nesting)
02766 {
02767     size_t bufn = _macro_BUFSIZ;
02768     char *buf = alloca(bufn);
02769     int lineno = 0;
02770     int rc = -1;
02771     FD_t fd;
02772     int xx;
02773 
02774     /* XXX TODO: teach rdcl() to read through a URI, eliminate ".fpio". */
02775     fd = Fopen(fn, "r.fpio");
02776     if (fd == NULL || Ferror(fd)) {
02777         if (fd) (void) Fclose(fd);
02778         return rc;
02779     }
02780 
02781     /* XXX Assume new fangled macro expansion */
02782     /*@-mods@*/
02783     max_macro_depth = _MAX_MACRO_DEPTH;
02784     /*@=mods@*/
02785 
02786     buf[0] = '\0';
02787     while(rdcl(buf, bufn, fd) != NULL) {
02788         char * s;
02789         int c;
02790 
02791         lineno++;
02792         s = buf;
02793         SKIPBLANK(s, c);
02794 
02795         if (c != (int) '%')
02796             continue;
02797 
02798         /* Parse %{load:...} immediately recursively. */
02799         if (s[1] == '{' && !strncmp(s+2, "load:", sizeof("load:")-1)) {
02800             char * se = (char *) matchchar(s, '{', '}');
02801             if (se == NULL) {
02802                 rpmlog(RPMLOG_WARNING,
02803                     _("%s:%u Missing '}' in \"%s\", skipping.\n"),
02804                     fn, lineno, buf);
02805                 continue;
02806             }
02807             s += sizeof("%{load:") - 1;
02808             SKIPBLANK(s, c);
02809             *se = '\0';
02810             if (nesting <= 0) {
02811                 rpmlog(RPMLOG_WARNING,
02812                     _("%s:%u load depth exceeded, \"%s\" ignored.\n"),
02813                     fn, lineno, buf);
02814                 continue;
02815             }
02816             se = rpmMCExpand(mc, s, NULL);
02817             rc = rpmLoadMacroFile(mc, se, nesting - 1);
02818             se = _free(se);
02819             if (rc != 0)
02820                 goto exit;
02821         } else {
02822 #if defined(RPM_VENDOR_OPENPKG) /* expand-macro-source */
02823             expand_macrosfile_macro(fn, buf, bufn);
02824 #endif
02825             if (*s == '%') s++;
02826             rc = rpmDefineMacro(mc, s, RMIL_MACROFILES);
02827         }
02828     }
02829     rc = 0;
02830 exit:
02831     xx = Fclose(fd);
02832     return rc;
02833 }
02834 
02835 void
02836 rpmInitMacros(MacroContext mc, const char * macrofiles)
02837 {
02838     char *mfiles, *m, *me;
02839 
02840     if (macrofiles == NULL)
02841         return;
02842 #ifdef  DYING
02843     if (mc == NULL) mc = rpmGlobalMacroContext;
02844 #endif
02845 
02846     mfiles = xstrdup(macrofiles);
02847     for (m = mfiles; m && *m != '\0'; m = me) {
02848         const char ** av;
02849         int ac;
02850         int i;
02851 
02852         for (me = m; (me = strchr(me, ':')) != NULL; me++) {
02853             /* Skip over URI's. */
02854             if (!(me[1] == '/' && me[2] == '/'))
02855                 /*@innerbreak@*/ break;
02856         }
02857 
02858         if (me && *me == ':')
02859             *me++ = '\0';
02860         else
02861             me = m + strlen(m);
02862 
02863         /* Glob expand the macro file path element, expanding ~ to $HOME. */
02864         ac = 0;
02865         av = NULL;
02866 #if defined(DEBUG_MACROS)
02867         ac = 1;
02868         av = xmalloc((ac + 1) * sizeof(*av));
02869         av[0] = strdup(m);
02870         av[1] = NULL;
02871 #else
02872         i = rpmGlob(m, &ac, &av);
02873         if (i != 0)
02874             continue;
02875 #endif
02876 
02877         /* Read macros from each file. */
02878 
02879         for (i = 0; i < ac; i++) {
02880             size_t slen = strlen(av[i]);
02881             const char *fn = av[i];
02882 
02883         if (fn[0] == '@' /* attention */) {
02884             fn++;
02885 #if defined(RPM_VENDOR_OPENPKG) /* stick-with-rpm-file-sanity-checking */ || \
02886     !defined(POPT_ERROR_BADCONFIG)      /* XXX POPT 1.15 retrofit */
02887             if (!rpmSecuritySaneFile(fn))
02888 #else
02889             if (!poptSaneFile(fn))
02890 #endif
02891             {
02892                 rpmlog(RPMLOG_WARNING, "existing RPM macros file \"%s\" considered INSECURE -- not loaded\n", fn);
02893                 /*@innercontinue@*/ continue;
02894             }
02895         }
02896 
02897         /* Skip backup files and %config leftovers. */
02898 #define _suffix(_s, _x) \
02899     (slen >= sizeof(_x) && !strcmp((_s)+slen-(sizeof(_x)-1), (_x)))
02900             if (!(_suffix(fn, "~")
02901                || _suffix(fn, ".rpmnew")
02902                || _suffix(fn, ".rpmorig")
02903                || _suffix(fn, ".rpmsave"))
02904                )
02905                    (void) rpmLoadMacroFile(mc, fn, _max_load_depth);
02906 #undef _suffix
02907 
02908             av[i] = _free(av[i]);
02909         }
02910         av = _free(av);
02911     }
02912     mfiles = _free(mfiles);
02913 
02914     /* Reload cmdline macros */
02915     /*@-mods@*/
02916     rpmLoadMacros(rpmCLIMacroContext, RMIL_CMDLINE);
02917     /*@=mods@*/
02918 }
02919 
02920 /*@-globstate@*/
02921 void
02922 rpmFreeMacros(MacroContext mc)
02923 {
02924     
02925     if (mc == NULL) mc = rpmGlobalMacroContext;
02926 
02927     if (mc->macroTable != NULL) {
02928         int i;
02929         for (i = 0; i < mc->firstFree; i++) {
02930             MacroEntry me;
02931             while ((me = mc->macroTable[i]) != NULL) {
02932                 /* XXX cast to workaround const */
02933                 /*@-onlytrans@*/
02934                 if ((mc->macroTable[i] = me->prev) == NULL)
02935                     me->name = _free(me->name);
02936                 /*@=onlytrans@*/
02937                 me->opts = _free(me->opts);
02938                 me->body = _free(me->body);
02939                 me = _free(me);
02940             }
02941         }
02942         mc->macroTable = _free(mc->macroTable);
02943     }
02944     memset(mc, 0, sizeof(*mc));
02945 }
02946 /*@=globstate@*/
02947 
02948 /* =============================================================== */
02949 int isCompressed(const char * file, rpmCompressedMagic * compressed)
02950 {
02951     FD_t fd;
02952     ssize_t nb;
02953     int rc = -1;
02954     unsigned char magic[13];
02955 #if defined(RPM_VENDOR_OPENPKG) || defined(RPM_VENDOR_FEDORA) || defined(RPM_VENDOR_MANDRIVA) /* extension-based-compression-detection */
02956     size_t file_len;
02957 #endif
02958 
02959     *compressed = COMPRESSED_NOT;
02960 
02961 #if defined(RPM_VENDOR_OPENPKG) || defined(RPM_VENDOR_FEDORA) || defined(RPM_VENDOR_MANDRIVA) /* extension-based-compression-detection */
02962     file_len = strlen(file);
02963     if ((file_len > 4 && strcasecmp(file+file_len-4, ".tbz") == 0)
02964      || (file_len > 4 && strcasecmp(file+file_len-4, ".bz2") == 0)) {
02965         *compressed = COMPRESSED_BZIP2;
02966         return 0;
02967     } else
02968     if (file_len > 4 && strcasecmp(file+file_len-4, ".zip") == 0) {
02969         *compressed = COMPRESSED_ZIP;
02970         return 0;
02971     } else
02972     if ((file_len > 4 && strcasecmp(file+file_len-4, ".tlz") == 0)
02973      || (file_len > 5 && strcasecmp(file+file_len-5, ".lzma") == 0)) {
02974         *compressed = COMPRESSED_LZMA;
02975         return 0;
02976     } else
02977     if (file_len > 4 && strcasecmp(file+file_len-3, ".xz") == 0) {
02978         *compressed = COMPRESSED_XZ;
02979         return 0;
02980     } else
02981     if ((file_len > 4 && strcasecmp(file+file_len-4, ".tgz") == 0)
02982      || (file_len > 3 && strcasecmp(file+file_len-3, ".gz") == 0)
02983      || (file_len > 2 && strcasecmp(file+file_len-2, ".Z") == 0)) {
02984         *compressed = COMPRESSED_OTHER;
02985         return 0;
02986     } else
02987     if (file_len > 5 && strcasecmp(file+file_len-5, ".cpio") == 0) {
02988         *compressed = COMPRESSED_NOT;
02989         return 0;
02990     } else
02991     if (file_len > 4 && strcasecmp(file+file_len-4, ".tar") == 0) {
02992         *compressed = COMPRESSED_NOT;
02993         return 0;
02994     }
02995 #endif
02996 
02997     fd = Fopen(file, "r");
02998     if (fd == NULL || Ferror(fd)) {
02999         /* XXX Fstrerror */
03000         rpmlog(RPMLOG_ERR, _("File %s: %s\n"), file, Fstrerror(fd));
03001         if (fd) (void) Fclose(fd);
03002         return 1;
03003     }
03004     nb = Fread(magic, sizeof(magic[0]), sizeof(magic), fd);
03005     if (nb < (ssize_t)0) {
03006         rpmlog(RPMLOG_ERR, _("File %s: %s\n"), file, Fstrerror(fd));
03007         rc = 1;
03008     } else if (nb < (ssize_t)sizeof(magic)) {
03009         rpmlog(RPMLOG_ERR, _("File %s is smaller than %u bytes\n"),
03010                 file, (unsigned)sizeof(magic));
03011         rc = 0;
03012     }
03013     (void) Fclose(fd);
03014     if (rc >= 0)
03015         return rc;
03016 
03017     rc = 0;
03018 
03019     if (magic[0] == 'B' && magic[1] == 'Z')
03020         *compressed = COMPRESSED_BZIP2;
03021     else
03022     if (magic[0] == (unsigned char) 0120 && magic[1] == (unsigned char) 0113
03023      && magic[2] == (unsigned char) 0003 && magic[3] == (unsigned char) 0004)   /* pkzip */
03024         *compressed = COMPRESSED_ZIP;
03025     else
03026     if (magic[0] == (unsigned char) 0x89 && magic[1] == 'L'
03027      && magic[2] == 'Z' && magic[3] == 'O')     /* lzop */
03028         *compressed = COMPRESSED_LZOP;
03029     else
03030 #if !defined(RPM_VENDOR_OPENPKG) && !defined(RPM_VENDOR_FEDORA) && !defined(RPM_VENDOR_MANDRIVA) /* extension-based-compression-detection */
03031     /* XXX Ick, LZMA has no magic. See http://lkml.org/lkml/2005/6/13/285 */
03032     if (magic[ 9] == (unsigned char) 0x00 && magic[10] == (unsigned char) 0x00 &&
03033         magic[11] == (unsigned char) 0x00 && magic[12] == (unsigned char) 0x00) /* lzmash */
03034         *compressed = COMPRESSED_LZMA;
03035     else
03036 #endif
03037 #if defined(RPM_VENDOR_OPENSUSE)
03038     if (magic[0] == 0135 && magic[1] == 0 && magic[2] == 0)             /* lzma */
03039         *compressed = COMPRESSED_LZMA;
03040     else
03041 #endif
03042     if (magic[0] == (unsigned char) 0xFD && magic[1] == 0x37 && magic[2] == 0x7A
03043      && magic[3] == 0x58 && magic[4] == 0x5A && magic[5] == 0x00)               /* xz */
03044         *compressed = COMPRESSED_XZ;
03045     else if ((magic[0] == 'L') && (magic[1] == 'Z') &&
03046                (magic[2] == 'I') && (magic[3] == 'P'))  /* lzip */
03047         *compressed = COMPRESSED_LZIP;
03048     else if ((magic[0] == 'L') && (magic[1] == 'R') &&
03049                (magic[2] == 'Z') && (magic[3] == 'I'))          /* lrzip */
03050         *compressed = COMPRESSED_LRZIP;
03051     else
03052     if ((magic[0] == (unsigned char) 0037 && magic[1] == (unsigned char) 0213)  /* gzip */
03053      || (magic[0] == (unsigned char) 0037 && magic[1] == (unsigned char) 0236)  /* old gzip */
03054      || (magic[0] == (unsigned char) 0037 && magic[1] == (unsigned char) 0036)  /* pack */
03055      || (magic[0] == (unsigned char) 0037 && magic[1] == (unsigned char) 0240)  /* SCO lzh */
03056      || (magic[0] == (unsigned char) 0037 && magic[1] == (unsigned char) 0235)) /* compress */
03057         *compressed = COMPRESSED_OTHER;
03058 
03059     return rc;
03060 }
03061 
03062 /* =============================================================== */
03063 
03064 /*@-modfilesys@*/
03065 /* XXX TODO: merge rpmExpand and rpmMCExpand. gud enuf for now ... */
03066 char * 
03067 rpmExpand(const char *arg, ...)
03068 {
03069     MacroContext mc = NULL;
03070     const char *s;
03071     char *t, *te;
03072     size_t sn, tn;
03073     size_t bufn = 8 * _macro_BUFSIZ;
03074 
03075     va_list ap;
03076 
03077     if (arg == NULL)
03078         return xstrdup("");
03079 
03080     t = xmalloc(bufn + strlen(arg) + 1);
03081     *t = '\0';
03082     te = stpcpy(t, arg);
03083 
03084     va_start(ap, arg);
03085     while ((s = va_arg(ap, const char *)) != NULL) {
03086         sn = strlen(s);
03087         tn = (te - t);
03088         t = xrealloc(t, tn + sn + bufn + 1);
03089         te = t + tn;
03090         te = stpcpy(te, s);
03091     }
03092     va_end(ap);
03093 
03094     *te = '\0';
03095     tn = (te - t);
03096     (void) expandMacros(NULL, mc, t, tn + bufn + 1);
03097     t[tn + bufn] = '\0';
03098     t = xrealloc(t, strlen(t) + 1);
03099     
03100     return t;
03101 }
03102 
03103 char * 
03104 rpmMCExpand(MacroContext mc, const char *arg, ...)
03105 {
03106     const char *s;
03107     char *t, *te;
03108     size_t sn, tn;
03109     size_t bufn = 8 * _macro_BUFSIZ;
03110 
03111     va_list ap;
03112 
03113     if (arg == NULL)
03114         return xstrdup("");
03115 
03116     t = xmalloc(bufn + strlen(arg) + 1);
03117     *t = '\0';
03118     te = stpcpy(t, arg);
03119 
03120     va_start(ap, arg);
03121     while ((s = va_arg(ap, const char *)) != NULL) {
03122         sn = strlen(s);
03123         tn = (te - t);
03124         t = xrealloc(t, tn + sn + bufn + 1);
03125         te = t + tn;
03126         te = stpcpy(te, s);
03127     }
03128     va_end(ap);
03129 
03130     *te = '\0';
03131     tn = (te - t);
03132     (void) expandMacros(NULL, mc, t, tn + bufn + 1);
03133     t[tn + bufn] = '\0';
03134     t = xrealloc(t, strlen(t) + 1);
03135     
03136     return t;
03137 }
03138 /*@=modfilesys@*/
03139 
03140 int
03141 rpmExpandNumeric(const char *arg)
03142 {
03143     const char *val;
03144     int rc;
03145 
03146     if (arg == NULL)
03147         return 0;
03148 
03149     val = rpmExpand(arg, NULL);
03150     if (!(val && *val != '%'))
03151         rc = 0;
03152     else if (*val == 'Y' || *val == 'y')
03153         rc = 1;
03154     else if (*val == 'N' || *val == 'n')
03155         rc = 0;
03156     else {
03157         char *end;
03158         rc = strtol(val, &end, 0);
03159         if (!(end && *end == '\0'))
03160             rc = 0;
03161     }
03162     val = _free(val);
03163 
03164     return rc;
03165 }
03166 
03167 /* @todo "../sbin/./../bin/" not correct. */
03168 char *rpmCleanPath(char * path)
03169 {
03170     const char *s;
03171     char *se, *t, *te;
03172     int begin = 1;
03173 
03174     if (path == NULL)
03175         return NULL;
03176 
03177 /*fprintf(stderr, "*** RCP %s ->\n", path); */
03178     s = t = te = path;
03179     while (*s != '\0') {
03180 /*fprintf(stderr, "*** got \"%.*s\"\trest \"%s\"\n", (t-path), path, s); */
03181         switch(*s) {
03182         case ':':                       /* handle url's */
03183             if (s[1] == '/' && s[2] == '/') {
03184                 *t++ = *s++;
03185                 *t++ = *s++;
03186                 /* XXX handle "file:///" */
03187                 if (s[0] == '/') *t++ = *s++;
03188                 te = t;
03189                 /*@switchbreak@*/ break;
03190             }
03191             begin=1;
03192             /*@switchbreak@*/ break;
03193         case '/':
03194             /* Move parent dir forward */
03195             for (se = te + 1; se < t && *se != '/'; se++)
03196                 {};
03197             if (se < t && *se == '/') {
03198                 te = se;
03199 /*fprintf(stderr, "*** next pdir \"%.*s\"\n", (te-path), path); */
03200             }
03201             while (s[1] == '/')
03202                 s++;
03203             while (t > te && t[-1] == '/')
03204                 t--;
03205             /*@switchbreak@*/ break;
03206         case '.':
03207             /* Leading .. is special */
03208             /* Check that it is ../, so that we don't interpret */
03209             /* ..?(i.e. "...") or ..* (i.e. "..bogus") as "..". */
03210             /* in the case of "...", this ends up being processed*/
03211             /* as "../.", and the last '.' is stripped.  This   */
03212             /* would not be correct processing.                 */
03213             if (begin && s[1] == '.' && (s[2] == '/' || s[2] == '\0')) {
03214 /*fprintf(stderr, "    leading \"..\"\n"); */
03215                 *t++ = *s++;
03216                 /*@switchbreak@*/ break;
03217             }
03218             /* Single . is special */
03219             if (begin && s[1] == '\0') {
03220                 /*@switchbreak@*/ break;
03221             }
03222             if (t > path && t[-1] == '/')
03223             switch (s[1]) {
03224             case '/':   s++;    /*@fallthrough@*/       /* Trim embedded ./ */
03225             case '\0':  s++;    continue;               /* Trim trailing /. */
03226             default:    /*@innerbreak@*/ break;
03227             }
03228             /* Trim embedded /../ and trailing /.. */
03229             if (!begin && t > path && t[-1] == '/' && s[1] == '.' && (s[2] == '/' || s[2] == '\0')) {
03230                 t = te;
03231                 /* Move parent dir forward */
03232                 if (te > path)
03233                     for (--te; te > path && *te != '/'; te--)
03234                         {};
03235 /*fprintf(stderr, "*** prev pdir \"%.*s\"\n", (te-path), path); */
03236                 s++;
03237                 s++;
03238                 continue;
03239             }
03240             /*@switchbreak@*/ break;
03241         default:
03242             begin = 0;
03243             /*@switchbreak@*/ break;
03244         }
03245         *t++ = *s++;
03246     }
03247 
03248     /* Trim trailing / (but leave single / alone) */
03249     if (t > &path[1] && t[-1] == '/')
03250         t--;
03251     *t = '\0';
03252 
03253 /*fprintf(stderr, "\t%s\n", path); */
03254     return path;
03255 }
03256 
03257 /* Return concatenated and expanded canonical path. */
03258 
03259 char *
03260 rpmGetPath(const char *path, ...)
03261 {
03262     size_t bufn = _macro_BUFSIZ;
03263     char *buf = alloca(bufn);
03264     const char * s;
03265     char * t, * te;
03266     int slashed = 0;
03267     va_list ap;
03268 
03269     if (path == NULL)
03270         return xstrdup("");
03271 
03272     buf[0] = '\0';
03273     t = buf;
03274     te = stpcpy(t, path);
03275     *te = '\0';
03276 
03277     va_start(ap, path);
03278     while ((s = va_arg(ap, const char *)) != NULL) {
03279         /* Specifically requested pesky trailing '/'? */
03280         slashed = (s[0] == '/' && s[1] == '\0');
03281         te = stpcpy(te, s);
03282     }
03283     va_end(ap);
03284     *te = '\0';
03285 
03286 /*@-modfilesys@*/
03287     (void) expandMacros(NULL, NULL, buf, bufn);
03288 /*@=modfilesys@*/
03289 
03290     /* Note: rpmCleanPath will strip pesky trailing '/'. */
03291     (void) rpmCleanPath(buf);
03292 
03293     /* Re-append specifically requested pesky trailing '/'. */
03294     if (slashed) {
03295         size_t nb = strlen(buf);
03296         if (buf[nb-1] != '/')
03297             buf[nb++] = '/';
03298         buf[nb] = '\0';
03299     }
03300 
03301     return DRD_xstrdup(buf);    /* XXX xstrdup has side effects. */
03302 }
03303 
03304 /* Merge 3 args into path, any or all of which may be a url. */
03305 
03306 const char * rpmGenPath(const char * urlroot, const char * urlmdir,
03307                 const char *urlfile)
03308 {
03309 /*@owned@*/ const char * xroot = rpmGetPath(urlroot, NULL);
03310 /*@dependent@*/ const char * root = xroot;
03311 /*@owned@*/ const char * xmdir = rpmGetPath(urlmdir, NULL);
03312 /*@dependent@*/ const char * mdir = xmdir;
03313 /*@owned@*/ const char * xfile = rpmGetPath(urlfile, NULL);
03314 /*@dependent@*/ const char * file = xfile;
03315     const char * result;
03316     const char * url = NULL;
03317     size_t nurl = 0;
03318     int ut;
03319 
03320 #if 0
03321 if (_debug) fprintf(stderr, "*** RGP xroot %s xmdir %s xfile %s\n", xroot, xmdir, xfile);
03322 #endif
03323     ut = urlPath(xroot, &root);
03324     if (url == NULL && ut > URL_IS_DASH) {
03325         url = xroot;
03326         nurl = strlen(url);
03327         if (root >= url && root <= url+nurl)
03328             nurl -= strlen(root);
03329 #if 0
03330 if (_debug) fprintf(stderr, "*** RGP ut %d root %s nurl %u\n", ut, root, (unsigned)nurl);
03331 #endif
03332     }
03333     if (root == NULL || *root == '\0') root = "/";
03334 
03335     ut = urlPath(xmdir, &mdir);
03336     if (url == NULL && ut > URL_IS_DASH) {
03337         url = xmdir;
03338         nurl = strlen(url);
03339         if (mdir >= url && mdir <= url+nurl)
03340             nurl -= strlen(mdir);
03341 #if 0
03342 if (_debug) fprintf(stderr, "*** RGP ut %d mdir %s nurl %u\n", ut, mdir, (unsigned)nurl);
03343 #endif
03344     }
03345     if (mdir == NULL || *mdir == '\0') mdir = "/";
03346 
03347     ut = urlPath(xfile, &file);
03348     if (url == NULL && ut > URL_IS_DASH) {
03349         url = xfile;
03350         nurl = strlen(url);
03351         if (file >= url && file <= url+nurl)
03352             nurl -= strlen(file);
03353 #if 0
03354 if (_debug) fprintf(stderr, "*** RGP ut %d file %s nurl %u\n", ut, file, (unsigned)nurl);
03355 #endif
03356     }
03357 
03358     if (url && nurl > 0) {
03359         char *t = strncpy(alloca(nurl+1), url, nurl);
03360         t[nurl] = '\0';
03361         url = t;
03362     } else
03363         url = "";
03364 
03365     result = rpmGetPath(url, root, "/", mdir, "/", file, NULL);
03366 
03367     xroot = _free(xroot);
03368     xmdir = _free(xmdir);
03369     xfile = _free(xfile);
03370 #if 0
03371 if (_debug) fprintf(stderr, "*** RGP result %s\n", result);
03372 #endif
03373     return result;
03374 }
03375 
03376 /* =============================================================== */
03377 
03378 #if defined(DEBUG_MACROS)
03379 
03380 #if defined(EVAL_MACROS)
03381 
03382 const char *rpmMacrofiles = MACROFILES;
03383 
03384 int
03385 main(int argc, char *argv[])
03386 {
03387     int c;
03388     int errflg = 0;
03389     extern char *optarg;
03390     extern int optind;
03391 
03392     while ((c = getopt(argc, argv, "f:")) != EOF ) {
03393         switch (c) {
03394         case 'f':
03395             rpmMacrofiles = optarg;
03396             break;
03397         case '?':
03398         default:
03399             errflg++;
03400             break;
03401         }
03402     }
03403     if (errflg || optind >= argc) {
03404         fprintf(stderr, "Usage: %s [-f macropath ] macro ...\n", argv[0]);
03405         exit(1);
03406     }
03407 
03408     rpmInitMacros(NULL, rpmMacrofiles);
03409     /* XXX getopt(3) also used for parametrized macros, expect scwewiness. */
03410     for ( ; optind < argc; optind++) {
03411         const char *val;
03412 
03413         val = rpmExpand(argv[optind], NULL);
03414         if (val) {
03415             fprintf(stdout, "%s:\t%s\n", argv[optind], val);
03416             val = _free(val);
03417         }
03418     }
03419     rpmFreeMacros(NULL);
03420     return 0;
03421 }
03422 
03423 #else   /* !EVAL_MACROS */
03424 
03425 const char *rpmMacrofiles = "../macros:./testmacros";
03426 const char *testfile = "./test";
03427 
03428 int
03429 main(int argc, char *argv[])
03430 {
03431     size_t bufn = _macro_BUFSIZ;
03432     char *buf = alloca(bufn);
03433     FILE *fp;
03434     int x;
03435 
03436     rpmInitMacros(NULL, rpmMacrofiles);
03437 
03438     if ((fp = fopen(testfile, "r")) != NULL) {
03439         while(rdcl(buf, bufn, fp)) {
03440             x = expandMacros(NULL, NULL, buf, bufn);
03441             fprintf(stderr, "%d->%s\n", x, buf);
03442             memset(buf, 0, bufn);
03443         }
03444         fclose(fp);
03445     }
03446 
03447     while(rdcl(buf, bufn, stdin)) {
03448         x = expandMacros(NULL, NULL, buf, bufn);
03449         fprintf(stderr, "%d->%s\n <-\n", x, buf);
03450         memset(buf, 0, bufn);
03451     }
03452     rpmFreeMacros(NULL);
03453 
03454     return 0;
03455 }
03456 #endif  /* EVAL_MACROS */
03457 #endif  /* DEBUG_MACROS */