rpmio/macro.c

Go to the documentation of this file.
00001 /*@-boundsread@*/
00006 #include "system.h"
00007 #include <stdarg.h>
00008 
00009 #if !defined(isblank)
00010 #define isblank(_c)     ((_c) == ' ' || (_c) == '\t')
00011 #endif
00012 #define iseol(_c)       ((_c) == '\n' || (_c) == '\r')
00013 
00014 #define STREQ(_t, _f, _fn)      ((_fn) == (sizeof(_t)-1) && !strncmp((_t), (_f), (_fn)))
00015 
00016 #ifdef DEBUG_MACROS
00017 #undef  WITH_LUA        /* XXX fixme */
00018 #include <sys/types.h>
00019 #include <errno.h>
00020 #include <fcntl.h>
00021 #include <getopt.h>
00022 #include <stdio.h>
00023 #include <stdlib.h>
00024 #include <string.h>
00025 #include <strings.h>
00026 #include <ctype.h>
00027 #define rpmError fprintf
00028 #define rpmIsVerbose()  (0)
00029 #define RPMERR_BADSPEC 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 
00041 typedef FILE * FD_t;
00042 #define Fopen(_path, _fmode)    fopen(_path, "r");
00043 #define Ferror                  ferror
00044 #define Fstrerror(_fd)          strerror(errno)
00045 #define Fread                   fread
00046 #define Fclose                  fclose
00047 
00048 #define fdGetFILE(_fd)          (_fd)
00049 
00050 /*@unused@*/ static inline /*@null@*/ void *
00051 _free(/*@only@*/ /*@null@*/ const void * p)
00052         /*@modifies p@*/
00053 {
00054     if (p != NULL)      free((void *)p);
00055     return NULL;
00056 }
00057 
00058 #else
00059 
00060 /*@observer@*/ /*@checked@*/
00061 const char * rpmMacrofiles = MACROFILES;
00062 
00063 #include <rpmio_internal.h>
00064 #include <rpmmessages.h>
00065 #include <rpmerr.h>
00066 
00067 #ifdef  WITH_LUA
00068 #include <rpmlua.h>
00069 #endif
00070 
00071 #endif
00072 
00073 #include <rpmmacro.h>
00074 
00075 #include "debug.h"
00076 
00077 #if defined(__LCLINT__)
00078 /*@-exportheader@*/
00079 extern const unsigned short int **__ctype_b_loc (void) /*@*/;
00080 /*@=exportheader@*/
00081 #endif
00082 
00083 /*@access FD_t@*/               /* XXX compared with NULL */
00084 /*@access MacroContext@*/
00085 /*@access MacroEntry@*/
00086 /*@access rpmlua @*/
00087 
00088 static struct MacroContext_s rpmGlobalMacroContext_s;
00089 /*@-compmempass@*/
00090 MacroContext rpmGlobalMacroContext = &rpmGlobalMacroContext_s;
00091 /*@=compmempass@*/
00092 
00093 static struct MacroContext_s rpmCLIMacroContext_s;
00094 /*@-compmempass@*/
00095 MacroContext rpmCLIMacroContext = &rpmCLIMacroContext_s;
00096 /*@=compmempass@*/
00097 
00101 typedef /*@abstract@*/ struct MacroBuf_s {
00102 /*@kept@*/ /*@exposed@*/
00103     const char * s;             
00104 /*@shared@*/
00105     char * t;                   
00106     size_t nb;                  
00107     int depth;                  
00108     int macro_trace;            
00109     int expand_trace;           
00110 /*@kept@*/ /*@exposed@*/ /*@null@*/
00111     void * spec;                
00112 /*@kept@*/ /*@exposed@*/
00113     MacroContext mc;
00114 } * MacroBuf;
00115 
00116 #define SAVECHAR(_mb, _c) { *(_mb)->t = (_c), (_mb)->t++, (_mb)->nb--; }
00117 
00118 /*@-exportlocal -exportheadervar@*/
00119 
00120 #define _MAX_MACRO_DEPTH        16
00121 /*@unchecked@*/
00122 int max_macro_depth = _MAX_MACRO_DEPTH;
00123 
00124 #define _PRINT_MACRO_TRACE      0
00125 /*@unchecked@*/
00126 int print_macro_trace = _PRINT_MACRO_TRACE;
00127 
00128 #define _PRINT_EXPAND_TRACE     0
00129 /*@unchecked@*/
00130 int print_expand_trace = _PRINT_EXPAND_TRACE;
00131 /*@=exportlocal =exportheadervar@*/
00132 
00133 #define MACRO_CHUNK_SIZE        16
00134 
00135 /* Size of expansion buffers. */
00136 static size_t _macro_BUFSIZ = 4 * BUFSIZ;
00137 
00138 /* forward ref */
00139 static int expandMacro(MacroBuf mb)
00140         /*@globals rpmGlobalMacroContext,
00141                 print_macro_trace, print_expand_trace, h_errno, fileSystem @*/
00142         /*@modifies mb, rpmGlobalMacroContext,
00143                 print_macro_trace, print_expand_trace, fileSystem @*/;
00144 
00145 /* =============================================================== */
00146 
00153 static int
00154 compareMacroName(const void * ap, const void * bp)
00155         /*@*/
00156 {
00157     MacroEntry ame = *((MacroEntry *)ap);
00158     MacroEntry bme = *((MacroEntry *)bp);
00159 
00160     if (ame == NULL && bme == NULL)
00161         return 0;
00162     if (ame == NULL)
00163         return 1;
00164     if (bme == NULL)
00165         return -1;
00166     return strcmp(ame->name, bme->name);
00167 }
00168 
00173 /*@-boundswrite@*/
00174 static void
00175 expandMacroTable(MacroContext mc)
00176         /*@modifies mc @*/
00177 {
00178     if (mc->macroTable == NULL) {
00179         mc->macrosAllocated = MACRO_CHUNK_SIZE;
00180         mc->macroTable = (MacroEntry *)
00181             xmalloc(sizeof(*(mc->macroTable)) * mc->macrosAllocated);
00182         mc->firstFree = 0;
00183     } else {
00184         mc->macrosAllocated += MACRO_CHUNK_SIZE;
00185         mc->macroTable = (MacroEntry *)
00186             xrealloc(mc->macroTable, sizeof(*(mc->macroTable)) *
00187                         mc->macrosAllocated);
00188     }
00189     memset(&mc->macroTable[mc->firstFree], 0, MACRO_CHUNK_SIZE * sizeof(*(mc->macroTable)));
00190 }
00191 /*@=boundswrite@*/
00192 
00197 static void
00198 sortMacroTable(MacroContext mc)
00199         /*@modifies mc @*/
00200 {
00201     int i;
00202 
00203     if (mc == NULL || mc->macroTable == NULL)
00204         return;
00205 
00206     qsort(mc->macroTable, mc->firstFree, sizeof(*(mc->macroTable)),
00207                 compareMacroName);
00208 
00209     /* Empty pointers are now at end of table. Reset first free index. */
00210     for (i = 0; i < mc->firstFree; i++) {
00211         if (mc->macroTable[i] != NULL)
00212             continue;
00213         mc->firstFree = i;
00214         break;
00215     }
00216 }
00217 
00218 void
00219 rpmDumpMacroTable(MacroContext mc, FILE * fp)
00220 {
00221     int nempty = 0;
00222     int nactive = 0;
00223 
00224     if (mc == NULL) mc = rpmGlobalMacroContext;
00225     if (fp == NULL) fp = stderr;
00226     
00227     fprintf(fp, "========================\n");
00228     if (mc->macroTable != NULL) {
00229         int i;
00230         for (i = 0; i < mc->firstFree; i++) {
00231             MacroEntry me;
00232             if ((me = mc->macroTable[i]) == NULL) {
00233                 /* XXX this should never happen */
00234                 nempty++;
00235                 continue;
00236             }
00237             fprintf(fp, "%3d%c %s", me->level,
00238                         (me->used > 0 ? '=' : ':'), me->name);
00239             if (me->opts && *me->opts)
00240                     fprintf(fp, "(%s)", me->opts);
00241             if (me->body && *me->body)
00242                     fprintf(fp, "\t%s", me->body);
00243             fprintf(fp, "\n");
00244             nactive++;
00245         }
00246     }
00247     fprintf(fp, _("======================== active %d empty %d\n"),
00248                 nactive, nempty);
00249 }
00250 
00258 /*@-boundswrite@*/
00259 /*@dependent@*/ /*@null@*/
00260 static MacroEntry *
00261 findEntry(MacroContext mc, const char * name, size_t namelen)
00262         /*@*/
00263 {
00264     MacroEntry key, *ret;
00265 
00266 /*@-globs@*/
00267     if (mc == NULL) mc = rpmGlobalMacroContext;
00268 /*@=globs@*/
00269     if (mc->macroTable == NULL || mc->firstFree == 0)
00270         return NULL;
00271 
00272 /*@-branchstate@*/
00273     if (namelen > 0) {
00274         char * t = strncpy(alloca(namelen + 1), name, namelen);
00275         t[namelen] = '\0';
00276         name = t;
00277     }
00278 /*@=branchstate@*/
00279     
00280     key = memset(alloca(sizeof(*key)), 0, sizeof(*key));
00281     /*@-temptrans -assignexpose@*/
00282     key->name = (char *)name;
00283     /*@=temptrans =assignexpose@*/
00284     ret = (MacroEntry *) bsearch(&key, mc->macroTable, mc->firstFree,
00285                         sizeof(*(mc->macroTable)), compareMacroName);
00286     /* XXX TODO: find 1st empty slot and return that */
00287     return ret;
00288 }
00289 /*@=boundswrite@*/
00290 
00291 /* =============================================================== */
00292 
00300 /*@-boundswrite@*/
00301 /*@null@*/
00302 static char *
00303 rdcl(/*@returned@*/ char * buf, size_t size, FD_t fd)
00304         /*@globals fileSystem @*/
00305         /*@modifies buf, fileSystem @*/
00306 {
00307     char *q = buf - 1;          /* initialize just before buffer. */
00308     size_t nb = 0;
00309     size_t nread = 0;
00310     FILE * f = fdGetFILE(fd);
00311     int pc = 0, bc = 0;
00312     char *p = buf;
00313 
00314     if (f != NULL)
00315     do {
00316         *(++q) = '\0';                  /* terminate and move forward. */
00317         if (fgets(q, size, f) == NULL)  /* read next line. */
00318             break;
00319         nb = strlen(q);
00320         nread += nb;                    /* trim trailing \r and \n */
00321         for (q += nb - 1; nb > 0 && iseol(*q); q--)
00322             nb--;
00323         for (; p <= q; p++) {
00324             switch (*p) {
00325                 case '\\':
00326                     switch (*(p+1)) {
00327                         case '\0': /*@switchbreak@*/ break;
00328                         default: p++; /*@switchbreak@*/ break;
00329                     }
00330                     /*@switchbreak@*/ break;
00331                 case '%':
00332                     switch (*(p+1)) {
00333                         case '{': p++, bc++; /*@switchbreak@*/ break;
00334                         case '(': p++, pc++; /*@switchbreak@*/ break;
00335                         case '%': p++; /*@switchbreak@*/ break;
00336                     }
00337                     /*@switchbreak@*/ break;
00338                 case '{': if (bc > 0) bc++; /*@switchbreak@*/ break;
00339                 case '}': if (bc > 0) bc--; /*@switchbreak@*/ break;
00340                 case '(': if (pc > 0) pc++; /*@switchbreak@*/ break;
00341                 case ')': if (pc > 0) pc--; /*@switchbreak@*/ break;
00342             }
00343         }
00344         if (nb == 0 || (*q != '\\' && !bc && !pc) || *(q+1) == '\0') {
00345             *(++q) = '\0';              /* trim trailing \r, \n */
00346             break;
00347         }
00348         q++; p++; nb++;                 /* copy newline too */
00349         size -= nb;
00350         if (*q == '\r')                 /* XXX avoid \r madness */
00351             *q = '\n';
00352     } while (size > 0);
00353     return (nread > 0 ? buf : NULL);
00354 }
00355 /*@=boundswrite@*/
00356 
00364 /*@null@*/
00365 static const char *
00366 matchchar(const char * p, char pl, char pr)
00367         /*@*/
00368 {
00369     int lvl = 0;
00370     char c;
00371 
00372     while ((c = *p++) != '\0') {
00373         if (c == '\\') {                /* Ignore escaped chars */
00374             p++;
00375             continue;
00376         }
00377         if (c == pr) {
00378             if (--lvl <= 0)     return --p;
00379         } else if (c == pl)
00380             lvl++;
00381     }
00382     return (const char *)NULL;
00383 }
00384 
00391 static void
00392 printMacro(MacroBuf mb, const char * s, const char * se)
00393         /*@globals fileSystem @*/
00394         /*@modifies fileSystem @*/
00395 {
00396     const char *senl;
00397     const char *ellipsis;
00398     int choplen;
00399 
00400     if (s >= se) {      /* XXX just in case */
00401         fprintf(stderr, _("%3d>%*s(empty)"), mb->depth,
00402                 (2 * mb->depth + 1), "");
00403         return;
00404     }
00405 
00406     if (s[-1] == '{')
00407         s--;
00408 
00409     /* Print only to first end-of-line (or end-of-string). */
00410     for (senl = se; *senl && !iseol(*senl); senl++)
00411         {};
00412 
00413     /* Limit trailing non-trace output */
00414     choplen = 61 - (2 * mb->depth);
00415     if ((senl - s) > choplen) {
00416         senl = s + choplen;
00417         ellipsis = "...";
00418     } else
00419         ellipsis = "";
00420 
00421     /* Substitute caret at end-of-macro position */
00422     fprintf(stderr, "%3d>%*s%%%.*s^", mb->depth,
00423         (2 * mb->depth + 1), "", (int)(se - s), s);
00424     if (se[1] != '\0' && (senl - (se+1)) > 0)
00425         fprintf(stderr, "%-.*s%s", (int)(senl - (se+1)), se+1, ellipsis);
00426     fprintf(stderr, "\n");
00427 }
00428 
00435 static void
00436 printExpansion(MacroBuf mb, const char * t, const char * te)
00437         /*@globals fileSystem @*/
00438         /*@modifies fileSystem @*/
00439 {
00440     const char *ellipsis;
00441     int choplen;
00442 
00443     if (!(te > t)) {
00444         fprintf(stderr, _("%3d<%*s(empty)\n"), mb->depth, (2 * mb->depth + 1), "");
00445         return;
00446     }
00447 
00448     /* Shorten output which contains newlines */
00449     while (te > t && iseol(te[-1]))
00450         te--;
00451     ellipsis = "";
00452     if (mb->depth > 0) {
00453         const char *tenl;
00454 
00455         /* Skip to last line of expansion */
00456         while ((tenl = strchr(t, '\n')) && tenl < te)
00457             t = ++tenl;
00458 
00459         /* Limit expand output */
00460         choplen = 61 - (2 * mb->depth);
00461         if ((te - t) > choplen) {
00462             te = t + choplen;
00463             ellipsis = "...";
00464         }
00465     }
00466 
00467     fprintf(stderr, "%3d<%*s", mb->depth, (2 * mb->depth + 1), "");
00468     if (te > t)
00469         fprintf(stderr, "%.*s%s", (int)(te - t), t, ellipsis);
00470     fprintf(stderr, "\n");
00471 }
00472 
00473 #define SKIPBLANK(_s, _c)       \
00474         /*@-globs@*/    /* FIX: __ctype_b */ \
00475         while (((_c) = *(_s)) && isblank(_c)) \
00476                 (_s)++;         \
00477         /*@=globs@*/
00478 
00479 #define SKIPNONBLANK(_s, _c)    \
00480         /*@-globs@*/    /* FIX: __ctype_b */ \
00481         while (((_c) = *(_s)) && !(isblank(_c) || iseol(_c))) \
00482                 (_s)++;         \
00483         /*@=globs@*/
00484 
00485 #define COPYNAME(_ne, _s, _c)   \
00486     {   SKIPBLANK(_s,_c);       \
00487         /*@-boundswrite@*/      \
00488         while(((_c) = *(_s)) && (xisalnum(_c) || (_c) == '_')) \
00489                 *(_ne)++ = *(_s)++; \
00490         *(_ne) = '\0';          \
00491         /*@=boundswrite@*/      \
00492     }
00493 
00494 #define COPYOPTS(_oe, _s, _c)   \
00495     {   /*@-boundswrite@*/      \
00496         while(((_c) = *(_s)) && (_c) != ')') \
00497                 *(_oe)++ = *(_s)++; \
00498         *(_oe) = '\0';          \
00499         /*@=boundswrite@*/      \
00500     }
00501 
00509 static int
00510 expandT(MacroBuf mb, const char * f, size_t flen)
00511         /*@globals rpmGlobalMacroContext, h_errno, fileSystem@*/
00512         /*@modifies mb, rpmGlobalMacroContext, fileSystem @*/
00513 {
00514     char *sbuf;
00515     const char *s = mb->s;
00516     int rc;
00517 
00518     sbuf = alloca(flen + 1);
00519     memset(sbuf, 0, (flen + 1));
00520 
00521     strncpy(sbuf, f, flen);
00522     sbuf[flen] = '\0';
00523     mb->s = sbuf;
00524     rc = expandMacro(mb);
00525     mb->s = s;
00526     return rc;
00527 }
00528 
00529 #if 0
00530 
00537 static int
00538 expandS(MacroBuf mb, char * tbuf, size_t tbuflen)
00539         /*@globals rpmGlobalMacroContext, fileSystem@*/
00540         /*@modifies mb, *tbuf, rpmGlobalMacroContext, fileSystem @*/
00541 {
00542     const char *t = mb->t;
00543     size_t nb = mb->nb;
00544     int rc;
00545 
00546     mb->t = tbuf;
00547     mb->nb = tbuflen;
00548     rc = expandMacro(mb);
00549     mb->t = t;
00550     mb->nb = nb;
00551     return rc;
00552 }
00553 #endif
00554 
00562 /*@-boundswrite@*/
00563 static int
00564 expandU(MacroBuf mb, char * u, size_t ulen)
00565         /*@globals rpmGlobalMacroContext, h_errno, fileSystem@*/
00566         /*@modifies mb, *u, rpmGlobalMacroContext, fileSystem @*/
00567 {
00568     const char *s = mb->s;
00569     char *t = mb->t;
00570     size_t nb = mb->nb;
00571     char *tbuf;
00572     int rc;
00573 
00574     tbuf = alloca(ulen + 1);
00575     memset(tbuf, 0, (ulen + 1));
00576 
00577     mb->s = u;
00578     mb->t = tbuf;
00579     mb->nb = ulen;
00580     rc = expandMacro(mb);
00581 
00582     tbuf[ulen] = '\0';  /* XXX just in case */
00583     if (ulen > mb->nb)
00584         strncpy(u, tbuf, (ulen - mb->nb + 1));
00585 
00586     mb->s = s;
00587     mb->t = t;
00588     mb->nb = nb;
00589 
00590     return rc;
00591 }
00592 /*@=boundswrite@*/
00593 
00601 /*@-boundswrite@*/
00602 static int
00603 doShellEscape(MacroBuf mb, const char * cmd, size_t clen)
00604         /*@globals rpmGlobalMacroContext, h_errno, fileSystem @*/
00605         /*@modifies mb, rpmGlobalMacroContext, fileSystem @*/
00606 {
00607     size_t bufn = _macro_BUFSIZ + clen;
00608     char * buf = alloca(bufn);
00609     FILE *shf;
00610     int rc;
00611     int c;
00612 
00613     strncpy(buf, cmd, clen);
00614     buf[clen] = '\0';
00615     rc = expandU(mb, buf, bufn);
00616     if (rc)
00617         return rc;
00618 
00619     if ((shf = popen(buf, "r")) == NULL)
00620         return 1;
00621     while(mb->nb > 0 && (c = fgetc(shf)) != EOF)
00622         SAVECHAR(mb, c);
00623     (void) pclose(shf);
00624 
00625     /* XXX delete trailing \r \n */
00626     while (iseol(mb->t[-1])) {
00627         *(mb->t--) = '\0';
00628         mb->nb++;
00629     }
00630     return 0;
00631 }
00632 /*@=boundswrite@*/
00633 
00642 /*@dependent@*/ static const char *
00643 doDefine(MacroBuf mb, /*@returned@*/ const char * se, int level, int expandbody)
00644         /*@globals rpmGlobalMacroContext, h_errno @*/
00645         /*@modifies mb, rpmGlobalMacroContext @*/
00646 {
00647     const char *s = se;
00648     size_t bufn = _macro_BUFSIZ;
00649     char *buf = alloca(bufn);
00650     char *n = buf, *ne;
00651     char *o = NULL, *oe;
00652     char *b, *be;
00653     int c;
00654     int oc = ')';
00655 
00656     SKIPBLANK(s, c);
00657     if (c == '.')               /* XXX readonly macros */
00658         *n++ = c = *s++;
00659     if (c == '.')               /* XXX readonly macros */
00660         *n++ = c = *s++;
00661     ne = n;
00662 
00663     /* Copy name */
00664     COPYNAME(ne, s, c);
00665 
00666     /* Copy opts (if present) */
00667     oe = ne + 1;
00668     if (*s == '(') {
00669         s++;    /* skip ( */
00670         o = oe;
00671         COPYOPTS(oe, s, oc);
00672         s++;    /* skip ) */
00673     }
00674 
00675     /* Copy body, skipping over escaped newlines */
00676     b = be = oe + 1;
00677     SKIPBLANK(s, c);
00678     if (c == '{') {     /* XXX permit silent {...} grouping */
00679         if ((se = matchchar(s, c, '}')) == NULL) {
00680             rpmError(RPMERR_BADSPEC,
00681                 _("Macro %%%s has unterminated body\n"), n);
00682             se = s;     /* XXX W2DO? */
00683             return se;
00684         }
00685         s++;    /* XXX skip { */
00686 /*@-boundswrite@*/
00687         strncpy(b, s, (se - s));
00688         b[se - s] = '\0';
00689 /*@=boundswrite@*/
00690         be += strlen(b);
00691         se++;   /* XXX skip } */
00692         s = se; /* move scan forward */
00693     } else {    /* otherwise free-field */
00694 /*@-boundswrite@*/
00695         int bc = 0, pc = 0;
00696         while (*s && (bc || pc || !iseol(*s))) {
00697             switch (*s) {
00698                 case '\\':
00699                     switch (*(s+1)) {
00700                         case '\0': /*@switchbreak@*/ break;
00701                         default: s++; /*@switchbreak@*/ break;
00702                     }
00703                     /*@switchbreak@*/ break;
00704                 case '%':
00705                     switch (*(s+1)) {
00706                         case '{': *be++ = *s++; bc++; /*@switchbreak@*/ break;
00707                         case '(': *be++ = *s++; pc++; /*@switchbreak@*/ break;
00708                         case '%': *be++ = *s++; /*@switchbreak@*/ break;
00709                     }
00710                     /*@switchbreak@*/ break;
00711                 case '{': if (bc > 0) bc++; /*@switchbreak@*/ break;
00712                 case '}': if (bc > 0) bc--; /*@switchbreak@*/ break;
00713                 case '(': if (pc > 0) pc++; /*@switchbreak@*/ break;
00714                 case ')': if (pc > 0) pc--; /*@switchbreak@*/ break;
00715             }
00716             *be++ = *s++;
00717         }
00718         *be = '\0';
00719 
00720         if (bc || pc) {
00721             rpmError(RPMERR_BADSPEC,
00722                 _("Macro %%%s has unterminated body\n"), n);
00723             se = s;     /* XXX W2DO? */
00724             return se;
00725         }
00726 
00727         /* Trim trailing blanks/newlines */
00728 /*@-globs@*/
00729         while (--be >= b && (c = *be) && (isblank(c) || iseol(c)))
00730             {};
00731 /*@=globs@*/
00732         *(++be) = '\0'; /* one too far */
00733 /*@=boundswrite@*/
00734     }
00735 
00736     /* Move scan over body */
00737     while (iseol(*s))
00738         s++;
00739     se = s;
00740 
00741     /* Names must start with alphabetic or _ and be at least 3 chars */
00742     if (!((c = *n) && (xisalpha(c) || c == '_') && (ne - n) > 2)) {
00743         rpmError(RPMERR_BADSPEC,
00744                 _("Macro %%%s has illegal name (%%define)\n"), n);
00745         return se;
00746     }
00747 
00748     /* Options must be terminated with ')' */
00749     if (o && oc != ')') {
00750         rpmError(RPMERR_BADSPEC, _("Macro %%%s has unterminated opts\n"), n);
00751         return se;
00752     }
00753 
00754     if ((be - b) < 1) {
00755         rpmError(RPMERR_BADSPEC, _("Macro %%%s has empty body\n"), n);
00756         return se;
00757     }
00758 
00759 /*@-modfilesys@*/
00760     if (expandbody && expandU(mb, b, (&buf[bufn] - b))) {
00761         rpmError(RPMERR_BADSPEC, _("Macro %%%s failed to expand\n"), n);
00762         return se;
00763     }
00764 /*@=modfilesys@*/
00765 
00766     if (n != buf)               /* XXX readonly macros */
00767         n--;
00768     if (n != buf)               /* XXX readonly macros */
00769         n--;
00770     addMacro(mb->mc, n, o, b, (level - 1));
00771 
00772     return se;
00773 }
00774 
00781 /*@dependent@*/ static const char *
00782 doUndefine(MacroContext mc, /*@returned@*/ const char * se)
00783         /*@globals rpmGlobalMacroContext @*/
00784         /*@modifies mc, rpmGlobalMacroContext @*/
00785 {
00786     const char *s = se;
00787     char *buf = alloca(_macro_BUFSIZ);
00788     char *n = buf, *ne = n;
00789     int c;
00790 
00791     COPYNAME(ne, s, c);
00792 
00793     /* Move scan over body */
00794     while (iseol(*s))
00795         s++;
00796     se = s;
00797 
00798     /* Names must start with alphabetic or _ and be at least 3 chars */
00799     if (!((c = *n) && (xisalpha(c) || c == '_') && (ne - n) > 2)) {
00800         rpmError(RPMERR_BADSPEC,
00801                 _("Macro %%%s has illegal name (%%undefine)\n"), n);
00802         return se;
00803     }
00804 
00805     delMacro(mc, n);
00806 
00807     return se;
00808 }
00809 
00810 #ifdef  DYING
00811 static void
00812 dumpME(const char * msg, MacroEntry me)
00813         /*@globals fileSystem @*/
00814         /*@modifies fileSystem @*/
00815 {
00816     if (msg)
00817         fprintf(stderr, "%s", msg);
00818     fprintf(stderr, "\tme %p", me);
00819     if (me)
00820         fprintf(stderr,"\tname %p(%s) prev %p",
00821                 me->name, me->name, me->prev);
00822     fprintf(stderr, "\n");
00823 }
00824 #endif
00825 
00834 static void
00835 pushMacro(/*@out@*/ MacroEntry * mep, const char * n, /*@null@*/ const char * o,
00836                 /*@null@*/ const char * b, int level)
00837         /*@modifies *mep @*/
00838 {
00839     MacroEntry prev = (mep && *mep ? *mep : NULL);
00840     MacroEntry me = (MacroEntry) xmalloc(sizeof(*me));
00841     const char *name = n;
00842 
00843     if (*name == '.')           /* XXX readonly macros */
00844         name++;
00845     if (*name == '.')           /* XXX readonly macros */
00846         name++;
00847 
00848     /*@-assignexpose@*/
00849     me->prev = prev;
00850     /*@=assignexpose@*/
00851     me->name = (prev ? prev->name : xstrdup(name));
00852     me->opts = (o ? xstrdup(o) : NULL);
00853     me->body = xstrdup(b ? b : "");
00854     me->used = 0;
00855     me->level = level;
00856     me->flags = (name != n);
00857 /*@-boundswrite@*/
00858 /*@-branchstate@*/
00859     if (mep)
00860         *mep = me;
00861     else
00862         me = _free(me);
00863 /*@=branchstate@*/
00864 /*@=boundswrite@*/
00865 }
00866 
00871 static void
00872 popMacro(MacroEntry * mep)
00873         /*@modifies *mep @*/
00874 {
00875         MacroEntry me = (*mep ? *mep : NULL);
00876 
00877 /*@-branchstate@*/
00878         if (me) {
00879                 /* XXX cast to workaround const */
00880                 /*@-onlytrans@*/
00881 /*@-boundswrite@*/
00882                 if ((*mep = me->prev) == NULL)
00883                         me->name = _free(me->name);
00884 /*@=boundswrite@*/
00885                 me->opts = _free(me->opts);
00886                 me->body = _free(me->body);
00887                 me = _free(me);
00888                 /*@=onlytrans@*/
00889         }
00890 /*@=branchstate@*/
00891 }
00892 
00897 static void
00898 freeArgs(MacroBuf mb)
00899         /*@modifies mb @*/
00900 {
00901     MacroContext mc = mb->mc;
00902     int ndeleted = 0;
00903     int i;
00904 
00905     if (mc == NULL || mc->macroTable == NULL)
00906         return;
00907 
00908     /* Delete dynamic macro definitions */
00909     for (i = 0; i < mc->firstFree; i++) {
00910         MacroEntry *mep, me;
00911         int skiptest = 0;
00912         mep = &mc->macroTable[i];
00913         me = *mep;
00914 
00915         if (me == NULL)         /* XXX this should never happen */
00916             continue;
00917         if (me->level < mb->depth)
00918             continue;
00919         if (strlen(me->name) == 1 && strchr("#*0", *me->name)) {
00920             if (*me->name == '*' && me->used > 0)
00921                 skiptest = 1; /* XXX skip test for %# %* %0 */
00922         } else if (!skiptest && me->used <= 0) {
00923 #if NOTYET
00924             rpmError(RPMERR_BADSPEC,
00925                         _("Macro %%%s (%s) was not used below level %d\n"),
00926                         me->name, me->body, me->level);
00927 #endif
00928         }
00929         popMacro(mep);
00930         if (!(mep && *mep))
00931             ndeleted++;
00932     }
00933 
00934     /* If any deleted macros, sort macro table */
00935     if (ndeleted)
00936         sortMacroTable(mc);
00937 }
00938 
00948 /*@-bounds@*/
00949 /*@dependent@*/ static const char *
00950 grabArgs(MacroBuf mb, const MacroEntry me, /*@returned@*/ const char * se,
00951                 const char * lastc)
00952         /*@globals rpmGlobalMacroContext @*/
00953         /*@modifies mb, rpmGlobalMacroContext @*/
00954 {
00955     size_t bufn = _macro_BUFSIZ;
00956     char *buf = alloca(bufn);
00957     char *b, *be;
00958     char aname[16];
00959     const char *opts, *o;
00960     int argc = 0;
00961     const char **argv;
00962     int c;
00963 
00964     /* Copy macro name as argv[0], save beginning of args.  */
00965     buf[0] = '\0';
00966     b = be = stpcpy(buf, me->name);
00967 
00968     addMacro(mb->mc, "0", NULL, buf, mb->depth);
00969     
00970     argc = 1;   /* XXX count argv[0] */
00971 
00972     /* Copy args into buf until lastc */
00973     *be++ = ' ';
00974     while ((c = *se++) != '\0' && (se-1) != lastc) {
00975 /*@-globs@*/
00976         if (!isblank(c)) {
00977             *be++ = c;
00978             continue;
00979         }
00980 /*@=globs@*/
00981         /* c is blank */
00982         if (be[-1] == ' ')
00983             continue;
00984         /* a word has ended */
00985         *be++ = ' ';
00986         argc++;
00987     }
00988     if (c == '\0') se--;        /* one too far */
00989     if (be[-1] != ' ')
00990         argc++, be++;           /* last word has not trailing ' ' */
00991     be[-1] = '\0';
00992     if (*b == ' ') b++;         /* skip the leading ' ' */
00993 
00994 /*
00995  * The macro %* analoguous to the shell's $* means "Pass all non-macro
00996  * parameters." Consequently, there needs to be a macro that means "Pass all
00997  * (including macro parameters) options". This is useful for verifying
00998  * parameters during expansion and yet transparently passing all parameters
00999  * through for higher level processing (e.g. %description and/or %setup).
01000  * This is the (potential) justification for %{**} ...
01001  */
01002     /* Add unexpanded args as macro */
01003     addMacro(mb->mc, "**", NULL, b, mb->depth);
01004 
01005 #ifdef NOTYET
01006     /* XXX if macros can be passed as args ... */
01007     expandU(mb, buf, bufn);
01008 #endif
01009 
01010     /* Build argv array */
01011     argv = (const char **) alloca((argc + 1) * sizeof(*argv));
01012     be[-1] = ' '; /* assert((be - 1) == (b + strlen(b) == buf + strlen(buf))) */
01013     be[0] = '\0';
01014     b = buf;
01015     for (c = 0; c < argc; c++) {
01016         argv[c] = b;
01017         b = strchr(b, ' ');
01018         *b++ = '\0';
01019     }
01020     /* assert(b == be);  */
01021     argv[argc] = NULL;
01022 
01023     /* Citation from glibc/posix/getopt.c:
01024      *    Index in ARGV of the next element to be scanned.
01025      *    This is used for communication to and from the caller
01026      *    and for communication between successive calls to `getopt'.
01027      *
01028      *    On entry to `getopt', zero means this is the first call; initialize.
01029      *
01030      *    When `getopt' returns -1, this is the index of the first of the
01031      *    non-option elements that the caller should itself scan.
01032      *
01033      *    Otherwise, `optind' communicates from one call to the next
01034      *    how much of ARGV has been scanned so far.
01035      */
01036     /* 1003.2 says this must be 1 before any call.  */
01037 
01038 #ifdef __GLIBC__
01039     /*@-mods@*/
01040     optind = 0;         /* XXX but posix != glibc */
01041     /*@=mods@*/
01042 #else
01043     optind = 1;
01044 #endif
01045 
01046     opts = me->opts;
01047 
01048     /* Define option macros. */
01049 /*@-nullstate@*/ /* FIX: argv[] can be NULL */
01050     while((c = getopt(argc, (char **)argv, opts)) != -1)
01051 /*@=nullstate@*/
01052     {
01053         if (c == '?' || (o = strchr(opts, c)) == NULL) {
01054             rpmError(RPMERR_BADSPEC, _("Unknown option %c in %s(%s)\n"),
01055                         (char)c, me->name, opts);
01056             return se;
01057         }
01058         *be++ = '-';
01059         *be++ = c;
01060         if (o[1] == ':') {
01061             *be++ = ' ';
01062             be = stpcpy(be, optarg);
01063         }
01064         *be++ = '\0';
01065         aname[0] = '-'; aname[1] = c; aname[2] = '\0';
01066         addMacro(mb->mc, aname, NULL, b, mb->depth);
01067         if (o[1] == ':') {
01068             aname[0] = '-'; aname[1] = c; aname[2] = '*'; aname[3] = '\0';
01069             addMacro(mb->mc, aname, NULL, optarg, mb->depth);
01070         }
01071         be = b; /* reuse the space */
01072     }
01073 
01074     /* Add arg count as macro. */
01075     sprintf(aname, "%d", (argc - optind));
01076     addMacro(mb->mc, "#", NULL, aname, mb->depth);
01077 
01078     /* Add macro for each arg. Concatenate args for %*. */
01079     if (be) {
01080         *be = '\0';
01081         for (c = optind; c < argc; c++) {
01082             sprintf(aname, "%d", (c - optind + 1));
01083             addMacro(mb->mc, aname, NULL, argv[c], mb->depth);
01084             if (be != b) *be++ = ' '; /* Add space between args */
01085 /*@-nullpass@*/ /* FIX: argv[] can be NULL */
01086             be = stpcpy(be, argv[c]);
01087 /*@=nullpass@*/
01088         }
01089     }
01090 
01091     /* Add unexpanded args as macro. */
01092     addMacro(mb->mc, "*", NULL, b, mb->depth);
01093 
01094     return se;
01095 }
01096 /*@=bounds@*/
01097 
01105 static void
01106 doOutput(MacroBuf mb, int waserror, const char * msg, size_t msglen)
01107         /*@globals rpmGlobalMacroContext, h_errno, fileSystem @*/
01108         /*@modifies mb, rpmGlobalMacroContext, fileSystem @*/
01109 {
01110     size_t bufn = _macro_BUFSIZ + msglen;
01111     char *buf = alloca(bufn);
01112 
01113     strncpy(buf, msg, msglen);
01114     buf[msglen] = '\0';
01115     (void) expandU(mb, buf, bufn);
01116     if (waserror)
01117         rpmError(RPMERR_BADSPEC, "%s\n", buf);
01118     else
01119         fprintf(stderr, "%s", buf);
01120 }
01121 
01131 static void
01132 doFoo(MacroBuf mb, int negate, const char * f, size_t fn,
01133                 /*@null@*/ const char * g, size_t gn)
01134         /*@globals rpmGlobalMacroContext, h_errno, fileSystem, internalState @*/
01135         /*@modifies mb, rpmGlobalMacroContext, fileSystem, internalState @*/
01136 {
01137      size_t bufn = _macro_BUFSIZ + fn + gn;
01138      char * buf = alloca(bufn);
01139      char *b = NULL, *be;
01140     int c;
01141 
01142     buf[0] = '\0';
01143     if (g != NULL) {
01144         strncpy(buf, g, gn);
01145         buf[gn] = '\0';
01146         (void) expandU(mb, buf, bufn);
01147     }
01148 #if defined(NOTYET)     /* XXX change needs parsePrep and macros changes too */
01149     if (fn > 5 && STREQ("patch", f, 5) && xisdigit(f[5])) {
01150         /* Skip leading zeros */
01151         for (c = 5; c < fn-1 && f[c] == '0' && xisdigit(f[c+1]);)
01152             c++;
01153         b = buf;
01154         be = stpncpy( stpcpy(b, "%patch -P "), f+c, fn-c);
01155         *be = '\0';
01156     } else
01157 #endif
01158     if (STREQ("basename", f, fn)) {
01159         if ((b = strrchr(buf, '/')) == NULL)
01160             b = buf;
01161         else
01162             b++;
01163     } else if (STREQ("dirname", f, fn)) {
01164         if ((b = strrchr(buf, '/')) != NULL)
01165             *b = '\0';
01166         b = buf;
01167     } else if (STREQ("suffix", f, fn)) {
01168         if ((b = strrchr(buf, '.')) != NULL)
01169             b++;
01170     } else if (STREQ("expand", f, fn)) {
01171         b = buf;
01172     } else if (STREQ("verbose", f, fn)) {
01173         if (negate)
01174             b = (rpmIsVerbose() ? NULL : buf);
01175         else
01176             b = (rpmIsVerbose() ? buf : NULL);
01177     } else if (STREQ("url2path", f, fn) || STREQ("u2p", f, fn)) {
01178         int ut = urlPath(buf, (const char **)&b);
01179         ut = ut;        /* XXX quiet gcc */
01180 /*@-branchstate@*/
01181         if (*b == '\0') b = "/";
01182 /*@=branchstate@*/
01183     } else if (STREQ("uncompress", f, fn)) {
01184         rpmCompressedMagic compressed = COMPRESSED_OTHER;
01185 /*@-globs@*/
01186         for (b = buf; (c = *b) && isblank(c);)
01187             b++;
01188         for (be = b; (c = *be) && !isblank(c);)
01189             be++;
01190 /*@=globs@*/
01191         *be++ = '\0';
01192         (void) isCompressed(b, &compressed);
01193         switch(compressed) {
01194         default:
01195         case 0: /* COMPRESSED_NOT */
01196             sprintf(be, "%%__cat %s", b);
01197             break;
01198         case 1: /* COMPRESSED_OTHER */
01199             sprintf(be, "%%__gzip -dc %s", b);
01200             break;
01201         case 2: /* COMPRESSED_BZIP2 */
01202             sprintf(be, "%%__bzip2 -dc %s", b);
01203             break;
01204         case 3: /* COMPRESSED_ZIP */
01205             sprintf(be, "%%__unzip -qq %s", b);
01206             break;
01207         case 4: /* COMPRESSED_LZOP */
01208             sprintf(be, "%%__lzop %s", b);
01209             break;
01210         case 5: /* COMPRESSED_LZMA */
01211             sprintf(be, "%%__lzma %s", b);
01212             break;
01213         }
01214         b = be;
01215     } else if (STREQ("S", f, fn)) {
01216         for (b = buf; (c = *b) && xisdigit(c);)
01217             b++;
01218         if (!c) {       /* digit index */
01219             b++;
01220             sprintf(b, "%%SOURCE%s", buf);
01221         } else
01222             b = buf;
01223     } else if (STREQ("P", f, fn)) {
01224         for (b = buf; (c = *b) && xisdigit(c);)
01225             b++;
01226         if (!c) {       /* digit index */
01227             b++;
01228             sprintf(b, "%%PATCH%s", buf);
01229         } else
01230             b = buf;
01231     } else if (STREQ("F", f, fn)) {
01232         b = buf + strlen(buf) + 1;
01233         sprintf(b, "file%s.file", buf);
01234     }
01235 
01236     if (b) {
01237         (void) expandT(mb, b, strlen(b));
01238     }
01239 }
01240 
01247 static int
01248 expandMacro(MacroBuf mb)
01249         /*@globals rpmGlobalMacroContext,
01250                 print_macro_trace, print_expand_trace, h_errno, fileSystem @*/
01251         /*@modifies mb, rpmGlobalMacroContext,
01252                 print_macro_trace, print_expand_trace, fileSystem @*/
01253 {
01254     MacroEntry *mep;
01255     MacroEntry me;
01256     const char *s = mb->s, *se;
01257     const char *f, *fe;
01258     const char *g, *ge;
01259     size_t fn, gn;
01260     char *t = mb->t;    /* save expansion pointer for printExpand */
01261     int c;
01262     int rc = 0;
01263     int negate;
01264     const char * lastc;
01265     int chkexist;
01266 
01267     if (++mb->depth > max_macro_depth) {
01268         rpmError(RPMERR_BADSPEC,
01269                 _("Recursion depth(%d) greater than max(%d)\n"),
01270                 mb->depth, max_macro_depth);
01271         mb->depth--;
01272         mb->expand_trace = 1;
01273         return 1;
01274     }
01275 
01276 /*@-branchstate@*/
01277     while (rc == 0 && mb->nb > 0 && (c = *s) != '\0') {
01278         s++;
01279         /* Copy text until next macro */
01280         switch(c) {
01281         case '%':
01282                 if (*s) {       /* Ensure not end-of-string. */
01283                     if (*s != '%')
01284                         /*@switchbreak@*/ break;
01285                     s++;        /* skip first % in %% */
01286                 }
01287                 /*@fallthrough@*/
01288         default:
01289                 SAVECHAR(mb, c);
01290                 continue;
01291                 /*@notreached@*/ /*@switchbreak@*/ break;
01292         }
01293 
01294         /* Expand next macro */
01295         f = fe = NULL;
01296         g = ge = NULL;
01297         if (mb->depth > 1)      /* XXX full expansion for outermost level */
01298                 t = mb->t;      /* save expansion pointer for printExpand */
01299         negate = 0;
01300         lastc = NULL;
01301         chkexist = 0;
01302         switch ((c = *s)) {
01303         default:                /* %name substitution */
01304                 while (*s != '\0' && strchr("!?", *s) != NULL) {
01305                         switch(*s++) {
01306                         case '!':
01307                                 negate = ((negate + 1) % 2);
01308                                 /*@switchbreak@*/ break;
01309                         case '?':
01310                                 chkexist++;
01311                                 /*@switchbreak@*/ break;
01312                         }
01313                 }
01314                 f = se = s;
01315                 if (*se == '-')
01316                         se++;
01317                 while((c = *se) && (xisalnum(c) || c == '_'))
01318                         se++;
01319                 /* Recognize non-alnum macros too */
01320                 switch (*se) {
01321                 case '*':
01322                         se++;
01323                         if (*se == '*') se++;
01324                         /*@innerbreak@*/ break;
01325                 case '#':
01326                         se++;
01327                         /*@innerbreak@*/ break;
01328                 default:
01329                         /*@innerbreak@*/ break;
01330                 }
01331                 fe = se;
01332                 /* For "%name " macros ... */
01333 /*@-globs@*/
01334                 if ((c = *fe) && isblank(c))
01335                         if ((lastc = strchr(fe,'\n')) == NULL)
01336                                 lastc = strchr(fe, '\0');
01337 /*@=globs@*/
01338                 /*@switchbreak@*/ break;
01339         case '(':               /* %(...) shell escape */
01340                 if ((se = matchchar(s, c, ')')) == NULL) {
01341                         rpmError(RPMERR_BADSPEC,
01342                                 _("Unterminated %c: %s\n"), (char)c, s);
01343                         rc = 1;
01344                         continue;
01345                 }
01346                 if (mb->macro_trace)
01347                         printMacro(mb, s, se+1);
01348 
01349                 s++;    /* skip ( */
01350                 rc = doShellEscape(mb, s, (se - s));
01351                 se++;   /* skip ) */
01352 
01353                 s = se;
01354                 continue;
01355                 /*@notreached@*/ /*@switchbreak@*/ break;
01356         case '{':               /* %{...}/%{...:...} substitution */
01357                 if ((se = matchchar(s, c, '}')) == NULL) {
01358                         rpmError(RPMERR_BADSPEC,
01359                                 _("Unterminated %c: %s\n"), (char)c, s);
01360                         rc = 1;
01361                         continue;
01362                 }
01363                 f = s+1;/* skip { */
01364                 se++;   /* skip } */
01365                 while (strchr("!?", *f) != NULL) {
01366                         switch(*f++) {
01367                         case '!':
01368                                 negate = ((negate + 1) % 2);
01369                                 /*@switchbreak@*/ break;
01370                         case '?':
01371                                 chkexist++;
01372                                 /*@switchbreak@*/ break;
01373                         }
01374                 }
01375                 /* Find end-of-expansion, handle %{foo:bar} expansions. */
01376                 for (fe = f; (c = *fe) && !strchr(" :}", c);)
01377                         fe++;
01378                 switch (c) {
01379                 case ':':
01380                         g = fe + 1;
01381                         ge = se - 1;
01382                         /*@innerbreak@*/ break;
01383                 case ' ':
01384                         lastc = se-1;
01385                         /*@innerbreak@*/ break;
01386                 default:
01387                         /*@innerbreak@*/ break;
01388                 }
01389                 /*@switchbreak@*/ break;
01390         }
01391 
01392         /* XXX Everything below expects fe > f */
01393         fn = (fe - f);
01394         gn = (ge - g);
01395         if ((fe - f) <= 0) {
01396 /* XXX Process % in unknown context */
01397                 c = '%';        /* XXX only need to save % */
01398                 SAVECHAR(mb, c);
01399 #if 0
01400                 rpmError(RPMERR_BADSPEC,
01401                         _("A %% is followed by an unparseable macro\n"));
01402 #endif
01403                 s = se;
01404                 continue;
01405         }
01406 
01407         if (mb->macro_trace)
01408                 printMacro(mb, s, se);
01409 
01410         /* Expand builtin macros */
01411         if (STREQ("load", f, fn)) {
01412                 if (g != NULL) {
01413                     char * mfn = strncpy(alloca(gn + 1), g, gn);
01414                     int xx;
01415                     mfn[gn] = '\0';
01416                     xx = rpmLoadMacroFile(NULL, mfn);
01417                 }
01418                 s = se;
01419                 continue;
01420         }
01421         if (STREQ("global", f, fn)) {
01422                 s = doDefine(mb, se, RMIL_GLOBAL, 1);
01423                 continue;
01424         }
01425         if (STREQ("define", f, fn)) {
01426                 s = doDefine(mb, se, mb->depth, 0);
01427                 continue;
01428         }
01429         if (STREQ("undefine", f, fn)) {
01430                 s = doUndefine(mb->mc, se);
01431                 continue;
01432         }
01433 
01434         if (STREQ("echo", f, fn) ||
01435             STREQ("warn", f, fn) ||
01436             STREQ("error", f, fn)) {
01437                 int waserror = 0;
01438                 if (STREQ("error", f, fn))
01439                         waserror = 1;
01440                 if (g != NULL && g < ge)
01441                         doOutput(mb, waserror, g, gn);
01442                 else
01443                         doOutput(mb, waserror, f, fn);
01444                 s = se;
01445                 continue;
01446         }
01447 
01448         if (STREQ("trace", f, fn)) {
01449                 /* XXX TODO restore expand_trace/macro_trace to 0 on return */
01450                 mb->expand_trace = mb->macro_trace = (negate ? 0 : mb->depth);
01451                 if (mb->depth == 1) {
01452                         print_macro_trace = mb->macro_trace;
01453                         print_expand_trace = mb->expand_trace;
01454                 }
01455                 s = se;
01456                 continue;
01457         }
01458 
01459         if (STREQ("dump", f, fn)) {
01460                 rpmDumpMacroTable(mb->mc, NULL);
01461                 while (iseol(*se))
01462                         se++;
01463                 s = se;
01464                 continue;
01465         }
01466 
01467 #ifdef  WITH_LUA
01468         if (STREQ("lua", f, fn)) {
01469                 rpmlua lua = NULL; /* Global state. */
01470                 const char *ls = s+sizeof("{lua:")-1;
01471                 const char *lse = se-sizeof("}")+1;
01472                 char *scriptbuf = (char *)xmalloc((lse-ls)+1);
01473                 const char *printbuf;
01474                 memcpy(scriptbuf, ls, lse-ls);
01475                 scriptbuf[lse-ls] = '\0';
01476                 rpmluaSetPrintBuffer(lua, 1);
01477                 if (rpmluaRunScript(lua, scriptbuf, NULL) == -1)
01478                     rc = 1;
01479                 printbuf = rpmluaGetPrintBuffer(lua);
01480                 if (printbuf) {
01481                     int len = strlen(printbuf);
01482                     if (len > mb->nb)
01483                         len = mb->nb;
01484                     memcpy(mb->t, printbuf, len);
01485                     mb->t += len;
01486                     mb->nb -= len;
01487                 }
01488                 rpmluaSetPrintBuffer(lua, 0);
01489                 free(scriptbuf);
01490                 s = se;
01491                 continue;
01492         }
01493 #endif
01494 
01495 #if defined(NOTYET)     /* XXX change needs parsePrep and macros changes too */
01496         /* Rewrite "%patchNN ..." as "%patch -P NN ..." and expand. */
01497         if (lastc != NULL && fn > 5 && STREQ("patch", f, 5) && xisdigit(f[5])) {
01498                 /*@-internalglobs@*/ /* FIX: verbose may be set */
01499                 doFoo(mb, negate, f, (lastc - f), NULL, 0);
01500                 /*@=internalglobs@*/
01501                 s = lastc;
01502                 continue;
01503         }
01504 #endif
01505 
01506         /* XXX necessary but clunky */
01507         if (STREQ("basename", f, fn) ||
01508             STREQ("dirname", f, fn) ||
01509             STREQ("suffix", f, fn) ||
01510             STREQ("expand", f, fn) ||
01511             STREQ("verbose", f, fn) ||
01512             STREQ("uncompress", f, fn) ||
01513             STREQ("url2path", f, fn) ||
01514             STREQ("u2p", f, fn) ||
01515             STREQ("S", f, fn) ||
01516             STREQ("P", f, fn) ||
01517             STREQ("F", f, fn)) {
01518                 /*@-internalglobs@*/ /* FIX: verbose may be set */
01519                 doFoo(mb, negate, f, fn, g, gn);
01520                 /*@=internalglobs@*/
01521                 s = se;
01522                 continue;
01523         }
01524 
01525         /* Expand defined macros */
01526         mep = findEntry(mb->mc, f, fn);
01527         me = (mep ? *mep : NULL);
01528 
01529         /* XXX Special processing for flags */
01530         if (*f == '-') {
01531                 if (me)
01532                         me->used++;     /* Mark macro as used */
01533                 if ((me == NULL && !negate) ||  /* Without -f, skip %{-f...} */
01534                     (me != NULL && negate)) {   /* With -f, skip %{!-f...} */
01535                         s = se;
01536                         continue;
01537                 }
01538 
01539                 if (g && g < ge) {              /* Expand X in %{-f:X} */
01540                         rc = expandT(mb, g, gn);
01541                 } else
01542                 if (me && me->body && *me->body) {/* Expand %{-f}/%{-f*} */
01543                         rc = expandT(mb, me->body, strlen(me->body));
01544                 }
01545                 s = se;
01546                 continue;
01547         }
01548 
01549         /* XXX Special processing for macro existence */
01550         if (chkexist) {
01551                 if ((me == NULL && !negate) ||  /* Without -f, skip %{?f...} */
01552                     (me != NULL && negate)) {   /* With -f, skip %{!?f...} */
01553                         s = se;
01554                         continue;
01555                 }
01556                 if (g && g < ge) {              /* Expand X in %{?f:X} */
01557                         rc = expandT(mb, g, gn);
01558                 } else
01559                 if (me && me->body && *me->body) { /* Expand %{?f}/%{?f*} */
01560                         rc = expandT(mb, me->body, strlen(me->body));
01561                 }
01562                 s = se;
01563                 continue;
01564         }
01565         
01566         if (me == NULL) {       /* leave unknown %... as is */
01567 #ifndef HACK
01568 #if DEAD
01569                 /* XXX hack to skip over empty arg list */
01570                 if (fn == 1 && *f == '*') {
01571                         s = se;
01572                         continue;
01573                 }
01574 #endif
01575                 /* XXX hack to permit non-overloaded %foo to be passed */
01576                 c = '%';        /* XXX only need to save % */
01577                 SAVECHAR(mb, c);
01578 #else
01579                 if (!strncmp(f, "if", fn) ||
01580                     !strncmp(f, "else", fn) ||
01581                     !strncmp(f, "endif", fn)) {
01582                         c = '%';        /* XXX only need to save % */
01583                         SAVECHAR(mb, c);
01584                 } else {
01585                         rpmError(RPMERR_BADSPEC,
01586                                 _("Macro %%%.*s not found, skipping\n"), fn, f);
01587                         s = se;
01588                 }
01589 #endif
01590                 continue;
01591         }
01592 
01593         /* Setup args for "%name " macros with opts */
01594         if (me && me->opts != NULL) {
01595                 if (lastc != NULL) {
01596                         se = grabArgs(mb, me, fe, lastc);
01597                 } else {
01598                         addMacro(mb->mc, "**", NULL, "", mb->depth);
01599                         addMacro(mb->mc, "*", NULL, "", mb->depth);
01600                         addMacro(mb->mc, "#", NULL, "0", mb->depth);
01601                         addMacro(mb->mc, "0", NULL, me->name, mb->depth);
01602                 }
01603         }
01604 
01605         /* Recursively expand body of macro */
01606         if (me->body && *me->body) {
01607                 mb->s = me->body;
01608                 rc = expandMacro(mb);
01609                 if (rc == 0)
01610                         me->used++;     /* Mark macro as used */
01611         }
01612 
01613         /* Free args for "%name " macros with opts */
01614         if (me->opts != NULL)
01615                 freeArgs(mb);
01616 
01617         s = se;
01618     }
01619 /*@=branchstate@*/
01620 
01621     *mb->t = '\0';
01622     mb->s = s;
01623     mb->depth--;
01624     if (rc != 0 || mb->expand_trace)
01625         printExpansion(mb, t, mb->t);
01626     return rc;
01627 }
01628 
01629 #if !defined(DEBUG_MACROS)
01630 /* =============================================================== */
01631 /* XXX dupe'd to avoid change in linkage conventions. */
01632 
01633 #define POPT_ERROR_NOARG        -10     
01634 #define POPT_ERROR_BADQUOTE     -15     
01635 #define POPT_ERROR_MALLOC       -21     
01637 #define POPT_ARGV_ARRAY_GROW_DELTA 5
01638 
01639 /*@-boundswrite@*/
01640 static int XpoptDupArgv(int argc, const char **argv,
01641                 int * argcPtr, const char *** argvPtr)
01642         /*@modifies *argcPtr, *argvPtr @*/
01643 {
01644     size_t nb = (argc + 1) * sizeof(*argv);
01645     const char ** argv2;
01646     char * dst;
01647     int i;
01648 
01649     if (argc <= 0 || argv == NULL)      /* XXX can't happen */
01650         return POPT_ERROR_NOARG;
01651     for (i = 0; i < argc; i++) {
01652         if (argv[i] == NULL)
01653             return POPT_ERROR_NOARG;
01654         nb += strlen(argv[i]) + 1;
01655     }
01656         
01657     dst = malloc(nb);
01658     if (dst == NULL)                    /* XXX can't happen */
01659         return POPT_ERROR_MALLOC;
01660     argv2 = (void *) dst;
01661     dst += (argc + 1) * sizeof(*argv);
01662 
01663     /*@-branchstate@*/
01664     for (i = 0; i < argc; i++) {
01665         argv2[i] = dst;
01666         dst += strlen(strcpy(dst, argv[i])) + 1;
01667     }
01668     /*@=branchstate@*/
01669     argv2[argc] = NULL;
01670 
01671     if (argvPtr) {
01672         *argvPtr = argv2;
01673     } else {
01674         free(argv2);
01675         argv2 = NULL;
01676     }
01677     if (argcPtr)
01678         *argcPtr = argc;
01679     return 0;
01680 }
01681 /*@=boundswrite@*/
01682 
01683 /*@-bounds@*/
01684 static int XpoptParseArgvString(const char * s, int * argcPtr, const char *** argvPtr)
01685         /*@modifies *argcPtr, *argvPtr @*/
01686 {
01687     const char * src;
01688     char quote = '\0';
01689     int argvAlloced = POPT_ARGV_ARRAY_GROW_DELTA;
01690     const char ** argv = malloc(sizeof(*argv) * argvAlloced);
01691     int argc = 0;
01692     int buflen = strlen(s) + 1;
01693     char * buf = memset(alloca(buflen), 0, buflen);
01694     int rc = POPT_ERROR_MALLOC;
01695 
01696     if (argv == NULL) return rc;
01697     argv[argc] = buf;
01698 
01699     for (src = s; *src != '\0'; src++) {
01700         if (quote == *src) {
01701             quote = '\0';
01702         } else if (quote != '\0') {
01703             if (*src == '\\') {
01704                 src++;
01705                 if (!*src) {
01706                     rc = POPT_ERROR_BADQUOTE;
01707                     goto exit;
01708                 }
01709                 if (*src != quote) *buf++ = '\\';
01710             }
01711             *buf++ = *src;
01712         } else if (isspace(*src)) {
01713             if (*argv[argc] != '\0') {
01714                 buf++, argc++;
01715                 if (argc == argvAlloced) {
01716                     argvAlloced += POPT_ARGV_ARRAY_GROW_DELTA;
01717                     argv = realloc(argv, sizeof(*argv) * argvAlloced);
01718                     if (argv == NULL) goto exit;
01719                 }
01720                 argv[argc] = buf;
01721             }
01722         } else switch (*src) {
01723           case '"':
01724           case '\'':
01725             quote = *src;
01726             /*@switchbreak@*/ break;
01727           case '\\':
01728             src++;
01729             if (!*src) {
01730                 rc = POPT_ERROR_BADQUOTE;
01731                 goto exit;
01732             }
01733             /*@fallthrough@*/
01734           default:
01735             *buf++ = *src;
01736             /*@switchbreak@*/ break;
01737         }
01738     }
01739 
01740     if (strlen(argv[argc])) {
01741         argc++, buf++;
01742     }
01743 
01744     rc = XpoptDupArgv(argc, argv, argcPtr, argvPtr);
01745 
01746 exit:
01747     if (argv) free(argv);
01748     return rc;
01749 }
01750 /*@=bounds@*/
01751 /* =============================================================== */
01752 /*@unchecked@*/
01753 static int _debug = 0;
01754 
01755 int rpmGlob(const char * patterns, int * argcPtr, const char *** argvPtr)
01756 {
01757     int ac = 0;
01758     const char ** av = NULL;
01759     int argc = 0;
01760     const char ** argv = NULL;
01761     char * globRoot = NULL;
01762 #ifdef ENABLE_NLS
01763     const char * old_collate = NULL;
01764     const char * old_ctype = NULL;
01765     const char * t;
01766 #endif
01767     size_t maxb, nb;
01768     int i, j;
01769     int rc;
01770 
01771     rc = XpoptParseArgvString(patterns, &ac, &av);
01772     if (rc)
01773         return rc;
01774 #ifdef ENABLE_NLS
01775 /*@-branchstate@*/
01776         t = setlocale(LC_COLLATE, NULL);
01777         if (t)
01778             old_collate = xstrdup(t);
01779         t = setlocale(LC_CTYPE, NULL);
01780         if (t)
01781             old_ctype = xstrdup(t);
01782 /*@=branchstate@*/
01783         (void) setlocale(LC_COLLATE, "C");
01784         (void) setlocale(LC_CTYPE, "C");
01785 #endif
01786         
01787     if (av != NULL)
01788     for (j = 0; j < ac; j++) {
01789         const char * globURL;
01790         const char * path;
01791         int ut = urlPath(av[j], &path);
01792         glob_t gl;
01793 
01794         if (!Glob_pattern_p(av[j], 0) && strchr(path, '~') == NULL) {
01795             argv = xrealloc(argv, (argc+2) * sizeof(*argv));
01796             argv[argc] = xstrdup(av[j]);
01797 if (_debug)
01798 fprintf(stderr, "*** rpmGlob argv[%d] \"%s\"\n", argc, argv[argc]);
01799             argc++;
01800             continue;
01801         }
01802         
01803         gl.gl_pathc = 0;
01804         gl.gl_pathv = NULL;
01805         rc = Glob(av[j], GLOB_TILDE, Glob_error, &gl);
01806         if (rc)
01807             goto exit;
01808 
01809         /* XXX Prepend the URL leader for globs that have stripped it off */
01810         maxb = 0;
01811         for (i = 0; i < gl.gl_pathc; i++) {
01812             if ((nb = strlen(&(gl.gl_pathv[i][0]))) > maxb)
01813                 maxb = nb;
01814         }
01815         
01816         nb = ((ut == URL_IS_PATH) ? (path - av[j]) : 0);
01817         maxb += nb;
01818         maxb += 1;
01819         globURL = globRoot = xmalloc(maxb);
01820 
01821         switch (ut) {
01822         case URL_IS_PATH:
01823         case URL_IS_DASH:
01824             strncpy(globRoot, av[j], nb);
01825             /*@switchbreak@*/ break;
01826         case URL_IS_HTTPS:
01827         case URL_IS_HTTP:
01828         case URL_IS_FTP:
01829         case URL_IS_HKP:
01830         case URL_IS_UNKNOWN:
01831         default:
01832             /*@switchbreak@*/ break;
01833         }
01834         globRoot += nb;
01835         *globRoot = '\0';
01836 if (_debug)
01837 fprintf(stderr, "*** GLOB maxb %d diskURL %d %*s globURL %p %s\n", (int)maxb, (int)nb, (int)nb, av[j], globURL, globURL);
01838         
01839         argv = xrealloc(argv, (argc+gl.gl_pathc+1) * sizeof(*argv));
01840 
01841         if (argv != NULL)
01842         for (i = 0; i < gl.gl_pathc; i++) {
01843             const char * globFile = &(gl.gl_pathv[i][0]);
01844             if (globRoot > globURL && globRoot[-1] == '/')
01845                 while (*globFile == '/') globFile++;
01846             strcpy(globRoot, globFile);
01847 if (_debug)
01848 fprintf(stderr, "*** rpmGlob argv[%d] \"%s\"\n", argc, globURL);
01849             argv[argc++] = xstrdup(globURL);
01850         }
01851         /*@-immediatetrans@*/
01852         Globfree(&gl);
01853         /*@=immediatetrans@*/
01854         globURL = _free(globURL);
01855     }
01856 
01857     if (argv != NULL && argc > 0) {
01858         argv[argc] = NULL;
01859         if (argvPtr)
01860             *argvPtr = argv;
01861         if (argcPtr)
01862             *argcPtr = argc;
01863         rc = 0;
01864     } else
01865         rc = 1;
01866 
01867 
01868 exit:
01869 #ifdef ENABLE_NLS       
01870 /*@-branchstate@*/
01871     if (old_collate) {
01872         (void) setlocale(LC_COLLATE, old_collate);
01873         old_collate = _free(old_collate);
01874     }
01875     if (old_ctype) {
01876         (void) setlocale(LC_CTYPE, old_ctype);
01877         old_ctype = _free(old_ctype);
01878     }
01879 /*@=branchstate@*/
01880 #endif
01881     av = _free(av);
01882 /*@-branchstate@*/
01883     if (rc || argvPtr == NULL) {
01884 /*@-dependenttrans -unqualifiedtrans@*/
01885         if (argv != NULL)
01886         for (i = 0; i < argc; i++)
01887             argv[i] = _free(argv[i]);
01888         argv = _free(argv);
01889 /*@=dependenttrans =unqualifiedtrans@*/
01890     }
01891 /*@=branchstate@*/
01892     return rc;
01893 }
01894 #endif  /* !defined(DEBUG_MACROS) */
01895 
01896 /* =============================================================== */
01897 
01898 int
01899 expandMacros(void * spec, MacroContext mc, char * sbuf, size_t slen)
01900 {
01901     MacroBuf mb = alloca(sizeof(*mb));
01902     char *tbuf;
01903     int rc;
01904 
01905     if (sbuf == NULL || slen == 0)
01906         return 0;
01907     if (mc == NULL) mc = rpmGlobalMacroContext;
01908 
01909     tbuf = alloca(slen + 1);
01910     memset(tbuf, 0, (slen + 1));
01911 
01912     mb->s = sbuf;
01913     mb->t = tbuf;
01914     mb->nb = slen;
01915     mb->depth = 0;
01916     mb->macro_trace = print_macro_trace;
01917     mb->expand_trace = print_expand_trace;
01918 
01919     mb->spec = spec;    /* (future) %file expansion info */
01920     mb->mc = mc;
01921 
01922     rc = expandMacro(mb);
01923 
01924     tbuf[slen] = '\0';
01925     if (mb->nb == 0)
01926         rpmError(RPMERR_BADSPEC, _("Macro expansion too big for target buffer\n"));
01927     else
01928         strncpy(sbuf, tbuf, (slen - mb->nb + 1));
01929 
01930     return rc;
01931 }
01932 
01933 void
01934 addMacro(MacroContext mc,
01935         const char * n, const char * o, const char * b, int level)
01936 {
01937     MacroEntry * mep;
01938     const char * name = n;
01939 
01940     if (*name == '.')           /* XXX readonly macros */
01941         name++;
01942     if (*name == '.')           /* XXX readonly macros */
01943         name++;
01944 
01945     if (mc == NULL) mc = rpmGlobalMacroContext;
01946 
01947     /* If new name, expand macro table */
01948     if ((mep = findEntry(mc, name, 0)) == NULL) {
01949         if (mc->firstFree == mc->macrosAllocated)
01950             expandMacroTable(mc);
01951         if (mc->macroTable != NULL)
01952             mep = mc->macroTable + mc->firstFree++;
01953     }
01954 
01955     if (mep != NULL) {
01956         /* XXX permit "..foo" to be pushed over ".foo" */
01957         if (*mep && (*mep)->flags && !(n[0] == '.' && n[1] == '.')) {
01958             /* XXX avoid error message for %buildroot */
01959             if (strcmp((*mep)->name, "buildroot"))
01960                 rpmError(RPMERR_BADSPEC, _("Macro '%s' is readonly and cannot be changed.\n"), n);
01961             return;
01962         }
01963         /* Push macro over previous definition */
01964         pushMacro(mep, n, o, b, level);
01965 
01966         /* If new name, sort macro table */
01967         if ((*mep)->prev == NULL)
01968             sortMacroTable(mc);
01969     }
01970 }
01971 
01972 void
01973 delMacro(MacroContext mc, const char * n)
01974 {
01975     MacroEntry * mep;
01976 
01977     if (mc == NULL) mc = rpmGlobalMacroContext;
01978     /* If name exists, pop entry */
01979     if ((mep = findEntry(mc, n, 0)) != NULL) {
01980         popMacro(mep);
01981         /* If deleted name, sort macro table */
01982         if (!(mep && *mep))
01983             sortMacroTable(mc);
01984     }
01985 }
01986 
01987 /*@-mustmod@*/ /* LCL: mc is modified through mb->mc, mb is abstract */
01988 int
01989 rpmDefineMacro(MacroContext mc, const char * macro, int level)
01990 {
01991     MacroBuf mb = alloca(sizeof(*mb));
01992 
01993     memset(mb, 0, sizeof(*mb));
01994     /* XXX just enough to get by */
01995     mb->mc = (mc ? mc : rpmGlobalMacroContext);
01996     (void) doDefine(mb, macro, level, 0);
01997     return 0;
01998 }
01999 /*@=mustmod@*/
02000 
02001 void
02002 rpmLoadMacros(MacroContext mc, int level)
02003 {
02004 
02005     if (mc == NULL || mc == rpmGlobalMacroContext)
02006         return;
02007 
02008     if (mc->macroTable != NULL) {
02009         int i;
02010         for (i = 0; i < mc->firstFree; i++) {
02011             MacroEntry *mep, me;
02012             mep = &mc->macroTable[i];
02013             me = *mep;
02014 
02015             if (me == NULL)             /* XXX this should never happen */
02016                 continue;
02017             addMacro(NULL, me->name, me->opts, me->body, (level - 1));
02018         }
02019     }
02020 }
02021 
02022 int
02023 rpmLoadMacroFile(MacroContext mc, const char * fn)
02024 {
02025     FD_t fd = Fopen(fn, "r.fpio");
02026     size_t bufn = _macro_BUFSIZ;
02027     char *buf = alloca(bufn);
02028     int rc = -1;
02029 
02030     if (fd == NULL || Ferror(fd)) {
02031         if (fd) (void) Fclose(fd);
02032         return rc;
02033     }
02034 
02035     /* XXX Assume new fangled macro expansion */
02036     /*@-mods@*/
02037     max_macro_depth = _MAX_MACRO_DEPTH;
02038     /*@=mods@*/
02039 
02040     buf[0] = '\0';
02041     while(rdcl(buf, bufn, fd) != NULL) {
02042         char c, *n;
02043 
02044         n = buf;
02045         SKIPBLANK(n, c);
02046 
02047         if (c != '%')
02048                 continue;
02049         n++;    /* skip % */
02050         rc = rpmDefineMacro(mc, n, RMIL_MACROFILES);
02051     }
02052     rc = Fclose(fd);
02053     return rc;
02054 }
02055 
02056 void
02057 rpmInitMacros(MacroContext mc, const char * macrofiles)
02058 {
02059     char *mfiles, *m, *me;
02060 
02061     if (macrofiles == NULL)
02062         return;
02063 #ifdef  DYING
02064     if (mc == NULL) mc = rpmGlobalMacroContext;
02065 #endif
02066 
02067     mfiles = xstrdup(macrofiles);
02068     for (m = mfiles; m && *m != '\0'; m = me) {
02069         const char ** av;
02070         int ac;
02071         int i;
02072 
02073         for (me = m; (me = strchr(me, ':')) != NULL; me++) {
02074             /* Skip over URI's. */
02075             if (!(me[1] == '/' && me[2] == '/'))
02076                 /*@innerbreak@*/ break;
02077         }
02078 
02079         if (me && *me == ':')
02080             *me++ = '\0';
02081         else
02082             me = m + strlen(m);
02083 
02084         /* Glob expand the macro file path element, expanding ~ to $HOME. */
02085         ac = 0;
02086         av = NULL;
02087 #if defined(DEBUG_MACROS)
02088         ac = 1;
02089         av = xmalloc((ac + 1) * sizeof(*av));
02090         av[0] = strdup(m);
02091         av[1] = NULL;
02092 #else
02093         i = rpmGlob(m, &ac, &av);
02094         if (i != 0)
02095             continue;
02096 #endif
02097 
02098         /* Read macros from each file. */
02099 
02100         for (i = 0; i < ac; i++) {
02101             size_t slen = strlen(av[i]);
02102 
02103         /* Skip backup files and %config leftovers. */
02104 #define _suffix(_s, _x) \
02105     (slen >= sizeof(_x) && !strcmp((_s)+slen-(sizeof(_x)-1), (_x)))
02106             if (!(_suffix(av[i], "~")
02107                || _suffix(av[i], ".rpmnew")
02108                || _suffix(av[i], ".rpmorig")
02109                || _suffix(av[i], ".rpmsave"))
02110                )
02111                    (void) rpmLoadMacroFile(mc, av[i]);
02112 #undef _suffix
02113 
02114             av[i] = _free(av[i]);
02115         }
02116         av = _free(av);
02117     }
02118     mfiles = _free(mfiles);
02119 
02120     /* Reload cmdline macros */
02121     /*@-mods@*/
02122     rpmLoadMacros(rpmCLIMacroContext, RMIL_CMDLINE);
02123     /*@=mods@*/
02124 }
02125 
02126 /*@-globstate@*/
02127 void
02128 rpmFreeMacros(MacroContext mc)
02129 {
02130     
02131     if (mc == NULL) mc = rpmGlobalMacroContext;
02132 
02133     if (mc->macroTable != NULL) {
02134         int i;
02135         for (i = 0; i < mc->firstFree; i++) {
02136             MacroEntry me;
02137             while ((me = mc->macroTable[i]) != NULL) {
02138                 /* XXX cast to workaround const */
02139                 /*@-onlytrans@*/
02140                 if ((mc->macroTable[i] = me->prev) == NULL)
02141                     me->name = _free(me->name);
02142                 /*@=onlytrans@*/
02143                 me->opts = _free(me->opts);
02144                 me->body = _free(me->body);
02145                 me = _free(me);
02146             }
02147         }
02148         mc->macroTable = _free(mc->macroTable);
02149     }
02150     memset(mc, 0, sizeof(*mc));
02151 }
02152 /*@=globstate@*/
02153 
02154 /* =============================================================== */
02155 int isCompressed(const char * file, rpmCompressedMagic * compressed)
02156 {
02157     FD_t fd;
02158     ssize_t nb;
02159     int rc = -1;
02160     unsigned char magic[13];
02161     char *end, *ext;
02162 
02163     *compressed = COMPRESSED_NOT;
02164 
02165     fd = Fopen(file, "r");
02166     if (fd == NULL || Ferror(fd)) {
02167         /* XXX Fstrerror */
02168         rpmError(RPMERR_BADSPEC, _("File %s: %s\n"), file, Fstrerror(fd));
02169         if (fd) (void) Fclose(fd);
02170         return 1;
02171     }
02172     nb = Fread(magic, sizeof(magic[0]), sizeof(magic), fd);
02173     if (nb < 0) {
02174         rpmError(RPMERR_BADSPEC, _("File %s: %s\n"), file, Fstrerror(fd));
02175         rc = 1;
02176     } else if (nb < sizeof(magic)) {
02177         rpmError(RPMERR_BADSPEC, _("File %s is smaller than %u bytes\n"),
02178                 file, (unsigned)sizeof(magic));
02179         rc = 0;
02180     }
02181     (void) Fclose(fd);
02182     if (rc >= 0)
02183         return rc;
02184 
02185     rc = 0;
02186 
02187     /* Tar archives will be recognized by filename. */
02188     end = strchr(file, '\0');
02189     ext = end - 4;
02190     if (ext > file && !strcasecmp(ext, ".tar")) return rc;
02191  
02192     if (magic[0] == 'B' && magic[1] == 'Z')
02193         *compressed = COMPRESSED_BZIP2;
02194     else
02195     if (magic[0] == 0120 && magic[1] == 0113
02196      && magic[2] == 0003 && magic[3] == 0004)   /* pkzip */
02197         *compressed = COMPRESSED_ZIP;
02198     else
02199     if (magic[0] == 0x89 && magic[1] == 'L'
02200      && magic[2] == 'Z' && magic[3] == 'O')     /* lzop */
02201         *compressed = COMPRESSED_LZOP;
02202     else
02203     /* XXX Ick, LZMA has no magic. See http://lkml.org/lkml/2005/6/13/285 */
02204     if (magic[ 9] == 0x00 && magic[10] == 0x00 &&
02205         magic[11] == 0x00 && magic[12] == 0x00) /* lzmash */
02206         *compressed = COMPRESSED_LZMA;
02207     else
02208     if ((magic[0] == 0037 && magic[1] == 0213)  /* gzip */
02209      || (magic[0] == 0037 && magic[1] == 0236)  /* old gzip */
02210      || (magic[0] == 0037 && magic[1] == 0036)  /* pack */
02211      || (magic[0] == 0037 && magic[1] == 0240)  /* SCO lzh */
02212      || (magic[0] == 0037 && magic[1] == 0235)) /* compress */
02213         *compressed = COMPRESSED_OTHER;
02214 
02215     return rc;
02216 }
02217 
02218 /* =============================================================== */
02219 
02220 /*@-modfilesys@*/
02221 char * 
02222 rpmExpand(const char *arg, ...)
02223 {
02224     const char *s;
02225     char *t, *te;
02226     size_t sn, tn;
02227     size_t bufn = 8 * _macro_BUFSIZ;
02228 
02229     va_list ap;
02230 
02231     if (arg == NULL)
02232         return xstrdup("");
02233 
02234     t = xmalloc(bufn + strlen(arg) + 1);
02235     *t = '\0';
02236     te = stpcpy(t, arg);
02237 
02238 /*@-branchstate@*/
02239     va_start(ap, arg);
02240     while ((s = va_arg(ap, const char *)) != NULL) {
02241         sn = strlen(s);
02242         tn = (te - t);
02243         t = xrealloc(t, tn + sn + bufn + 1);
02244         te = t + tn;
02245         te = stpcpy(te, s);
02246     }
02247     va_end(ap);
02248 /*@=branchstate@*/
02249 
02250     *te = '\0';
02251     tn = (te - t);
02252     (void) expandMacros(NULL, NULL, t, tn + bufn + 1);
02253     t[tn + bufn] = '\0';
02254     t = xrealloc(t, strlen(t) + 1);
02255     
02256     return t;
02257 }
02258 /*@=modfilesys@*/
02259 
02260 int
02261 rpmExpandNumeric(const char *arg)
02262 {
02263     const char *val;
02264     int rc;
02265 
02266     if (arg == NULL)
02267         return 0;
02268 
02269     val = rpmExpand(arg, NULL);
02270     if (!(val && *val != '%'))
02271         rc = 0;
02272     else if (*val == 'Y' || *val == 'y')
02273         rc = 1;
02274     else if (*val == 'N' || *val == 'n')
02275         rc = 0;
02276     else {
02277         char *end;
02278         rc = strtol(val, &end, 0);
02279         if (!(end && *end == '\0'))
02280             rc = 0;
02281     }
02282     val = _free(val);
02283 
02284     return rc;
02285 }
02286 
02287 /* @todo "../sbin/./../bin/" not correct. */
02288 char *rpmCleanPath(char * path)
02289 {
02290     const char *s;
02291     char *se, *t, *te;
02292     int begin = 1;
02293 
02294     if (path == NULL)
02295         return NULL;
02296 
02297 /*fprintf(stderr, "*** RCP %s ->\n", path); */
02298     s = t = te = path;
02299     while (*s != '\0') {
02300 /*fprintf(stderr, "*** got \"%.*s\"\trest \"%s\"\n", (t-path), path, s); */
02301         switch(*s) {
02302         case ':':                       /* handle url's */
02303             if (s[1] == '/' && s[2] == '/') {
02304                 *t++ = *s++;
02305                 *t++ = *s++;
02306                 /* XXX handle "file:///" */
02307                 if (s[0] == '/') *t++ = *s++;
02308                 te = t;
02309                 /*@switchbreak@*/ break;
02310             }
02311             begin=1;
02312             /*@switchbreak@*/ break;
02313         case '/':
02314             /* Move parent dir forward */
02315             for (se = te + 1; se < t && *se != '/'; se++)
02316                 {};
02317             if (se < t && *se == '/') {
02318                 te = se;
02319 /*fprintf(stderr, "*** next pdir \"%.*s\"\n", (te-path), path); */
02320             }
02321             while (s[1] == '/')
02322                 s++;
02323             while (t > te && t[-1] == '/')
02324                 t--;
02325             /*@switchbreak@*/ break;
02326         case '.':
02327             /* Leading .. is special */
02328             /* Check that it is ../, so that we don't interpret */
02329             /* ..?(i.e. "...") or ..* (i.e. "..bogus") as "..". */
02330             /* in the case of "...", this ends up being processed*/
02331             /* as "../.", and the last '.' is stripped.  This   */
02332             /* would not be correct processing.                 */
02333             if (begin && s[1] == '.' && (s[2] == '/' || s[2] == '\0')) {
02334 /*fprintf(stderr, "    leading \"..\"\n"); */
02335                 *t++ = *s++;
02336                 /*@switchbreak@*/ break;
02337             }
02338             /* Single . is special */
02339             if (begin && s[1] == '\0') {
02340                 /*@switchbreak@*/ break;
02341             }
02342             /* Trim embedded ./ , trailing /. */
02343             if ((t[-1] == '/' && s[1] == '\0') || (t > path && t[-1] == '/' && s[1] == '/')) {
02344                 s++;
02345                 continue;
02346             }
02347             /* Trim embedded /../ and trailing /.. */
02348             if (!begin && t > path && t[-1] == '/' && s[1] == '.' && (s[2] == '/' || s[2] == '\0')) {
02349                 t = te;
02350                 /* Move parent dir forward */
02351                 if (te > path)
02352                     for (--te; te > path && *te != '/'; te--)
02353                         {};
02354 /*fprintf(stderr, "*** prev pdir \"%.*s\"\n", (te-path), path); */
02355                 s++;
02356                 s++;
02357                 continue;
02358             }
02359             /*@switchbreak@*/ break;
02360         default:
02361             begin = 0;
02362             /*@switchbreak@*/ break;
02363         }
02364         *t++ = *s++;
02365     }
02366 
02367     /* Trim trailing / (but leave single / alone) */
02368     if (t > &path[1] && t[-1] == '/')
02369         t--;
02370     *t = '\0';
02371 
02372 /*fprintf(stderr, "\t%s\n", path); */
02373     return path;
02374 }
02375 
02376 /* Return concatenated and expanded canonical path. */
02377 
02378 const char *
02379 rpmGetPath(const char *path, ...)
02380 {
02381     size_t bufn = _macro_BUFSIZ;
02382     char *buf = alloca(bufn);
02383     const char * s;
02384     char * t, * te;
02385     va_list ap;
02386 
02387     if (path == NULL)
02388         return xstrdup("");
02389 
02390     buf[0] = '\0';
02391     t = buf;
02392     te = stpcpy(t, path);
02393     *te = '\0';
02394 
02395     va_start(ap, path);
02396     while ((s = va_arg(ap, const char *)) != NULL) {
02397         te = stpcpy(te, s);
02398         *te = '\0';
02399     }
02400     va_end(ap);
02401 /*@-modfilesys@*/
02402     (void) expandMacros(NULL, NULL, buf, bufn);
02403 /*@=modfilesys@*/
02404 
02405     (void) rpmCleanPath(buf);
02406     return xstrdup(buf);        /* XXX xstrdup has side effects. */
02407 }
02408 
02409 /* Merge 3 args into path, any or all of which may be a url. */
02410 
02411 const char * rpmGenPath(const char * urlroot, const char * urlmdir,
02412                 const char *urlfile)
02413 {
02414 /*@owned@*/ const char * xroot = rpmGetPath(urlroot, NULL);
02415 /*@dependent@*/ const char * root = xroot;
02416 /*@owned@*/ const char * xmdir = rpmGetPath(urlmdir, NULL);
02417 /*@dependent@*/ const char * mdir = xmdir;
02418 /*@owned@*/ const char * xfile = rpmGetPath(urlfile, NULL);
02419 /*@dependent@*/ const char * file = xfile;
02420     const char * result;
02421     const char * url = NULL;
02422     int nurl = 0;
02423     int ut;
02424 
02425 #if 0
02426 if (_debug) fprintf(stderr, "*** RGP xroot %s xmdir %s xfile %s\n", xroot, xmdir, xfile);
02427 #endif
02428     ut = urlPath(xroot, &root);
02429     if (url == NULL && ut > URL_IS_DASH) {
02430         url = xroot;
02431         nurl = root - xroot;
02432 #if 0
02433 if (_debug) fprintf(stderr, "*** RGP ut %d root %s nurl %d\n", ut, root, nurl);
02434 #endif
02435     }
02436     if (root == NULL || *root == '\0') root = "/";
02437 
02438     ut = urlPath(xmdir, &mdir);
02439     if (url == NULL && ut > URL_IS_DASH) {
02440         url = xmdir;
02441         nurl = mdir - xmdir;
02442 #if 0
02443 if (_debug) fprintf(stderr, "*** RGP ut %d mdir %s nurl %d\n", ut, mdir, nurl);
02444 #endif
02445     }
02446     if (mdir == NULL || *mdir == '\0') mdir = "/";
02447 
02448     ut = urlPath(xfile, &file);
02449     if (url == NULL && ut > URL_IS_DASH) {
02450         url = xfile;
02451         nurl = file - xfile;
02452 #if 0
02453 if (_debug) fprintf(stderr, "*** RGP ut %d file %s nurl %d\n", ut, file, nurl);
02454 #endif
02455     }
02456 
02457 /*@-branchstate@*/
02458     if (url && nurl > 0) {
02459         char *t = strncpy(alloca(nurl+1), url, nurl);
02460         t[nurl] = '\0';
02461         url = t;
02462     } else
02463         url = "";
02464 /*@=branchstate@*/
02465 
02466     result = rpmGetPath(url, root, "/", mdir, "/", file, NULL);
02467 
02468     xroot = _free(xroot);
02469     xmdir = _free(xmdir);
02470     xfile = _free(xfile);
02471 #if 0
02472 if (_debug) fprintf(stderr, "*** RGP result %s\n", result);
02473 #endif
02474     return result;
02475 }
02476 
02477 /* =============================================================== */
02478 
02479 #if defined(DEBUG_MACROS)
02480 
02481 #if defined(EVAL_MACROS)
02482 
02483 const char *rpmMacrofiles = MACROFILES;
02484 
02485 int
02486 main(int argc, char *argv[])
02487 {
02488     int c;
02489     int errflg = 0;
02490     extern char *optarg;
02491     extern int optind;
02492 
02493     while ((c = getopt(argc, argv, "f:")) != EOF ) {
02494         switch (c) {
02495         case 'f':
02496             rpmMacrofiles = optarg;
02497             break;
02498         case '?':
02499         default:
02500             errflg++;
02501             break;
02502         }
02503     }
02504     if (errflg || optind >= argc) {
02505         fprintf(stderr, "Usage: %s [-f macropath ] macro ...\n", argv[0]);
02506         exit(1);
02507     }
02508 
02509     rpmInitMacros(NULL, rpmMacrofiles);
02510     /* XXX getopt(3) also used for parametrized macros, expect scwewiness. */
02511     for ( ; optind < argc; optind++) {
02512         const char *val;
02513 
02514         val = rpmExpand(argv[optind], NULL);
02515         if (val) {
02516             fprintf(stdout, "%s:\t%s\n", argv[optind], val);
02517             val = _free(val);
02518         }
02519     }
02520     rpmFreeMacros(NULL);
02521     return 0;
02522 }
02523 
02524 #else   /* !EVAL_MACROS */
02525 
02526 const char *rpmMacrofiles = "../macros:./testmacros";
02527 const char *testfile = "./test";
02528 
02529 int
02530 main(int argc, char *argv[])
02531 {
02532     size_t bufn = _macro_BUFSIZ;
02533     char *buf = alloca(bufn);
02534     FILE *fp;
02535     int x;
02536 
02537     rpmInitMacros(NULL, rpmMacrofiles);
02538 
02539     if ((fp = fopen(testfile, "r")) != NULL) {
02540         while(rdcl(buf, bufn, fp)) {
02541             x = expandMacros(NULL, NULL, buf, bufn);
02542             fprintf(stderr, "%d->%s\n", x, buf);
02543             memset(buf, 0, bufn);
02544         }
02545         fclose(fp);
02546     }
02547 
02548     while(rdcl(buf, bufn, stdin)) {
02549         x = expandMacros(NULL, NULL, buf, bufn);
02550         fprintf(stderr, "%d->%s\n <-\n", x, buf);
02551         memset(buf, 0, bufn);
02552     }
02553     rpmFreeMacros(NULL);
02554 
02555     return 0;
02556 }
02557 #endif  /* EVAL_MACROS */
02558 #endif  /* DEBUG_MACROS */
02559 /*@=boundsread@*/

Generated on Wed Oct 29 02:15:21 2008 for rpm by  doxygen 1.5.1