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

Generated on Fri Aug 31 10:38:35 2007 for rpm by  doxygen 1.5.1