Main Page | Modules | Data Structures | Directories | File List | Data Fields | Globals | Related Pages

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

Generated on Mon Nov 1 21:57:36 2004 for rpm by  doxygen 1.3.9.1