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