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

Generated on Wed Jan 28 12:45:25 2009 for rpm by  doxygen 1.4.7