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     static char buf[BUFSIZ];
00928     char *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) {       /* Ensure not end-of-string. */
01239                     if (*s != '%')
01240                         /*@switchbreak@*/ break;
01241                     s++;        /* skip first % in %% */
01242                 }
01243                 /*@fallthrough@*/
01244         default:
01245                 SAVECHAR(mb, c);
01246                 continue;
01247                 /*@notreached@*/ /*@switchbreak@*/ break;
01248         }
01249 
01250         /* Expand next macro */
01251         f = fe = NULL;
01252         g = ge = NULL;
01253         if (mb->depth > 1)      /* XXX full expansion for outermost level */
01254                 t = mb->t;      /* save expansion pointer for printExpand */
01255         negate = 0;
01256         lastc = NULL;
01257         chkexist = 0;
01258         switch ((c = *s)) {
01259         default:                /* %name substitution */
01260                 while (strchr("!?", *s) != NULL) {
01261                         switch(*s++) {
01262                         case '!':
01263                                 negate = ((negate + 1) % 2);
01264                                 /*@switchbreak@*/ break;
01265                         case '?':
01266                                 chkexist++;
01267                                 /*@switchbreak@*/ break;
01268                         }
01269                 }
01270                 f = se = s;
01271                 if (*se == '-')
01272                         se++;
01273                 while((c = *se) && (xisalnum(c) || c == '_'))
01274                         se++;
01275                 /* Recognize non-alnum macros too */
01276                 switch (*se) {
01277                 case '*':
01278                         se++;
01279                         if (*se == '*') se++;
01280                         /*@innerbreak@*/ break;
01281                 case '#':
01282                         se++;
01283                         /*@innerbreak@*/ break;
01284                 default:
01285                         /*@innerbreak@*/ break;
01286                 }
01287                 fe = se;
01288                 /* For "%name " macros ... */
01289 /*@-globs@*/
01290                 if ((c = *fe) && isblank(c))
01291                         if ((lastc = strchr(fe,'\n')) == NULL)
01292                 lastc = strchr(fe, '\0');
01293 /*@=globs@*/
01294                 /*@switchbreak@*/ break;
01295         case '(':               /* %(...) shell escape */
01296                 if ((se = matchchar(s, c, ')')) == NULL) {
01297                         rpmError(RPMERR_BADSPEC,
01298                                 _("Unterminated %c: %s\n"), (char)c, s);
01299                         rc = 1;
01300                         continue;
01301                 }
01302                 if (mb->macro_trace)
01303                         printMacro(mb, s, se+1);
01304 
01305                 s++;    /* skip ( */
01306                 rc = doShellEscape(mb, s, (se - s));
01307                 se++;   /* skip ) */
01308 
01309                 s = se;
01310                 continue;
01311                 /*@notreached@*/ /*@switchbreak@*/ break;
01312         case '{':               /* %{...}/%{...:...} substitution */
01313                 if ((se = matchchar(s, c, '}')) == NULL) {
01314                         rpmError(RPMERR_BADSPEC,
01315                                 _("Unterminated %c: %s\n"), (char)c, s);
01316                         rc = 1;
01317                         continue;
01318                 }
01319                 f = s+1;/* skip { */
01320                 se++;   /* skip } */
01321                 while (strchr("!?", *f) != NULL) {
01322                         switch(*f++) {
01323                         case '!':
01324                                 negate = ((negate + 1) % 2);
01325                                 /*@switchbreak@*/ break;
01326                         case '?':
01327                                 chkexist++;
01328                                 /*@switchbreak@*/ break;
01329                         }
01330                 }
01331                 for (fe = f; (c = *fe) && !strchr(" :}", c);)
01332                         fe++;
01333                 switch (c) {
01334                 case ':':
01335                         g = fe + 1;
01336                         ge = se - 1;
01337                         /*@innerbreak@*/ break;
01338                 case ' ':
01339                         lastc = se-1;
01340                         /*@innerbreak@*/ break;
01341                 default:
01342                         /*@innerbreak@*/ break;
01343                 }
01344                 /*@switchbreak@*/ break;
01345         }
01346 
01347         /* XXX Everything below expects fe > f */
01348         fn = (fe - f);
01349         gn = (ge - g);
01350         if ((fe - f) <= 0) {
01351 /* XXX Process % in unknown context */
01352                 c = '%';        /* XXX only need to save % */
01353                 SAVECHAR(mb, c);
01354 #if 0
01355                 rpmError(RPMERR_BADSPEC,
01356                         _("A %% is followed by an unparseable macro\n"));
01357 #endif
01358                 s = se;
01359                 continue;
01360         }
01361 
01362         if (mb->macro_trace)
01363                 printMacro(mb, s, se);
01364 
01365         /* Expand builtin macros */
01366         if (STREQ("global", f, fn)) {
01367                 s = doDefine(mb, se, RMIL_GLOBAL, 1);
01368                 continue;
01369         }
01370         if (STREQ("define", f, fn)) {
01371                 s = doDefine(mb, se, mb->depth, 0);
01372                 continue;
01373         }
01374         if (STREQ("undefine", f, fn)) {
01375                 s = doUndefine(mb->mc, se);
01376                 continue;
01377         }
01378 
01379         if (STREQ("echo", f, fn) ||
01380             STREQ("warn", f, fn) ||
01381             STREQ("error", f, fn)) {
01382                 int waserror = 0;
01383                 if (STREQ("error", f, fn))
01384                         waserror = 1;
01385                 if (g != NULL && g < ge)
01386                         doOutput(mb, waserror, g, gn);
01387                 else
01388                         doOutput(mb, waserror, f, fn);
01389                 s = se;
01390                 continue;
01391         }
01392 
01393         if (STREQ("trace", f, fn)) {
01394                 /* XXX TODO restore expand_trace/macro_trace to 0 on return */
01395                 mb->expand_trace = mb->macro_trace = (negate ? 0 : mb->depth);
01396                 if (mb->depth == 1) {
01397                         print_macro_trace = mb->macro_trace;
01398                         print_expand_trace = mb->expand_trace;
01399                 }
01400                 s = se;
01401                 continue;
01402         }
01403 
01404         if (STREQ("dump", f, fn)) {
01405                 rpmDumpMacroTable(mb->mc, NULL);
01406                 while (iseol(*se))
01407                         se++;
01408                 s = se;
01409                 continue;
01410         }
01411 
01412 #ifdef  WITH_LUA
01413         if (STREQ("lua", f, fn)) {
01414                 rpmlua lua = NULL; /* Global state. */
01415                 const char *ls = s+sizeof("{lua:")-1;
01416                 const char *lse = se-sizeof("}")+1;
01417                 char *scriptbuf = (char *)xmalloc((lse-ls)+1);
01418                 const char *printbuf;
01419                 memcpy(scriptbuf, ls, lse-ls);
01420                 scriptbuf[lse-ls] = '\0';
01421                 rpmluaSetPrintBuffer(lua, 1);
01422                 if (rpmluaRunScript(lua, scriptbuf, NULL) == -1)
01423                     rc = 1;
01424                 printbuf = rpmluaGetPrintBuffer(lua);
01425                 if (printbuf) {
01426                     int len = strlen(printbuf);
01427                     if (len > mb->nb)
01428                         len = mb->nb;
01429                     memcpy(mb->t, printbuf, len);
01430                     mb->t += len;
01431                     mb->nb -= len;
01432                 }
01433                 rpmluaSetPrintBuffer(lua, 0);
01434                 free(scriptbuf);
01435                 s = se;
01436                 continue;
01437         }
01438 #endif
01439 
01440         /* XXX necessary but clunky */
01441         if (STREQ("basename", f, fn) ||
01442             STREQ("suffix", f, fn) ||
01443             STREQ("expand", f, fn) ||
01444             STREQ("verbose", f, fn) ||
01445             STREQ("uncompress", f, fn) ||
01446             STREQ("url2path", f, fn) ||
01447             STREQ("u2p", f, fn) ||
01448             STREQ("S", f, fn) ||
01449             STREQ("P", f, fn) ||
01450             STREQ("F", f, fn)) {
01451                 /*@-internalglobs@*/ /* FIX: verbose may be set */
01452                 doFoo(mb, negate, f, fn, g, gn);
01453                 /*@=internalglobs@*/
01454                 s = se;
01455                 continue;
01456         }
01457 
01458         /* Expand defined macros */
01459         mep = findEntry(mb->mc, f, fn);
01460         me = (mep ? *mep : NULL);
01461 
01462         /* XXX Special processing for flags */
01463         if (*f == '-') {
01464                 if (me)
01465                         me->used++;     /* Mark macro as used */
01466                 if ((me == NULL && !negate) ||  /* Without -f, skip %{-f...} */
01467                     (me != NULL && negate)) {   /* With -f, skip %{!-f...} */
01468                         s = se;
01469                         continue;
01470                 }
01471 
01472                 if (g && g < ge) {              /* Expand X in %{-f:X} */
01473                         rc = expandT(mb, g, gn);
01474                 } else
01475                 if (me && me->body && *me->body) {/* Expand %{-f}/%{-f*} */
01476                         rc = expandT(mb, me->body, strlen(me->body));
01477                 }
01478                 s = se;
01479                 continue;
01480         }
01481 
01482         /* XXX Special processing for macro existence */
01483         if (chkexist) {
01484                 if ((me == NULL && !negate) ||  /* Without -f, skip %{?f...} */
01485                     (me != NULL && negate)) {   /* With -f, skip %{!?f...} */
01486                         s = se;
01487                         continue;
01488                 }
01489                 if (g && g < ge) {              /* Expand X in %{?f:X} */
01490                         rc = expandT(mb, g, gn);
01491                 } else
01492                 if (me && me->body && *me->body) { /* Expand %{?f}/%{?f*} */
01493                         rc = expandT(mb, me->body, strlen(me->body));
01494                 }
01495                 s = se;
01496                 continue;
01497         }
01498         
01499         if (me == NULL) {       /* leave unknown %... as is */
01500 #ifndef HACK
01501 #if DEAD
01502                 /* XXX hack to skip over empty arg list */
01503                 if (fn == 1 && *f == '*') {
01504                         s = se;
01505                         continue;
01506                 }
01507 #endif
01508                 /* XXX hack to permit non-overloaded %foo to be passed */
01509                 c = '%';        /* XXX only need to save % */
01510                 SAVECHAR(mb, c);
01511 #else
01512                 rpmError(RPMERR_BADSPEC,
01513                         _("Macro %%%.*s not found, skipping\n"), fn, f);
01514                 s = se;
01515 #endif
01516                 continue;
01517         }
01518 
01519         /* Setup args for "%name " macros with opts */
01520         if (me && me->opts != NULL) {
01521                 if (lastc != NULL) {
01522                         se = grabArgs(mb, me, fe, lastc);
01523                 } else {
01524                         addMacro(mb->mc, "**", NULL, "", mb->depth);
01525                         addMacro(mb->mc, "*", NULL, "", mb->depth);
01526                         addMacro(mb->mc, "#", NULL, "0", mb->depth);
01527                         addMacro(mb->mc, "0", NULL, me->name, mb->depth);
01528                 }
01529         }
01530 
01531         /* Recursively expand body of macro */
01532         if (me->body && *me->body) {
01533                 mb->s = me->body;
01534                 rc = expandMacro(mb);
01535                 if (rc == 0)
01536                         me->used++;     /* Mark macro as used */
01537         }
01538 
01539         /* Free args for "%name " macros with opts */
01540         if (me->opts != NULL)
01541                 freeArgs(mb);
01542 
01543         s = se;
01544     }
01545 /*@=branchstate@*/
01546 
01547     *mb->t = '\0';
01548     mb->s = s;
01549     mb->depth--;
01550     if (rc != 0 || mb->expand_trace)
01551         printExpansion(mb, t, mb->t);
01552     return rc;
01553 }
01554 
01555 /* =============================================================== */
01556 /* XXX dupe'd to avoid change in linkage conventions. */
01557 
01558 #define POPT_ERROR_NOARG        -10     
01559 #define POPT_ERROR_BADQUOTE     -15     
01560 #define POPT_ERROR_MALLOC       -21     
01562 #define POPT_ARGV_ARRAY_GROW_DELTA 5
01563 
01564 /*@-boundswrite@*/
01565 static int XpoptDupArgv(int argc, const char **argv,
01566                 int * argcPtr, const char *** argvPtr)
01567         /*@modifies *argcPtr, *argvPtr @*/
01568 {
01569     size_t nb = (argc + 1) * sizeof(*argv);
01570     const char ** argv2;
01571     char * dst;
01572     int i;
01573 
01574     if (argc <= 0 || argv == NULL)      /* XXX can't happen */
01575         return POPT_ERROR_NOARG;
01576     for (i = 0; i < argc; i++) {
01577         if (argv[i] == NULL)
01578             return POPT_ERROR_NOARG;
01579         nb += strlen(argv[i]) + 1;
01580     }
01581         
01582     dst = malloc(nb);
01583     if (dst == NULL)                    /* XXX can't happen */
01584         return POPT_ERROR_MALLOC;
01585     argv2 = (void *) dst;
01586     dst += (argc + 1) * sizeof(*argv);
01587 
01588     /*@-branchstate@*/
01589     for (i = 0; i < argc; i++) {
01590         argv2[i] = dst;
01591         dst += strlen(strcpy(dst, argv[i])) + 1;
01592     }
01593     /*@=branchstate@*/
01594     argv2[argc] = NULL;
01595 
01596     if (argvPtr) {
01597         *argvPtr = argv2;
01598     } else {
01599         free(argv2);
01600         argv2 = NULL;
01601     }
01602     if (argcPtr)
01603         *argcPtr = argc;
01604     return 0;
01605 }
01606 /*@=boundswrite@*/
01607 
01608 /*@-bounds@*/
01609 static int XpoptParseArgvString(const char * s, int * argcPtr, const char *** argvPtr)
01610         /*@modifies *argcPtr, *argvPtr @*/
01611 {
01612     const char * src;
01613     char quote = '\0';
01614     int argvAlloced = POPT_ARGV_ARRAY_GROW_DELTA;
01615     const char ** argv = malloc(sizeof(*argv) * argvAlloced);
01616     int argc = 0;
01617     int buflen = strlen(s) + 1;
01618     char * buf = memset(alloca(buflen), 0, buflen);
01619     int rc = POPT_ERROR_MALLOC;
01620 
01621     if (argv == NULL) return rc;
01622     argv[argc] = buf;
01623 
01624     for (src = s; *src != '\0'; src++) {
01625         if (quote == *src) {
01626             quote = '\0';
01627         } else if (quote != '\0') {
01628             if (*src == '\\') {
01629                 src++;
01630                 if (!*src) {
01631                     rc = POPT_ERROR_BADQUOTE;
01632                     goto exit;
01633                 }
01634                 if (*src != quote) *buf++ = '\\';
01635             }
01636             *buf++ = *src;
01637         } else if (isspace(*src)) {
01638             if (*argv[argc] != '\0') {
01639                 buf++, argc++;
01640                 if (argc == argvAlloced) {
01641                     argvAlloced += POPT_ARGV_ARRAY_GROW_DELTA;
01642                     argv = realloc(argv, sizeof(*argv) * argvAlloced);
01643                     if (argv == NULL) goto exit;
01644                 }
01645                 argv[argc] = buf;
01646             }
01647         } else switch (*src) {
01648           case '"':
01649           case '\'':
01650             quote = *src;
01651             /*@switchbreak@*/ break;
01652           case '\\':
01653             src++;
01654             if (!*src) {
01655                 rc = POPT_ERROR_BADQUOTE;
01656                 goto exit;
01657             }
01658             /*@fallthrough@*/
01659           default:
01660             *buf++ = *src;
01661             /*@switchbreak@*/ break;
01662         }
01663     }
01664 
01665     if (strlen(argv[argc])) {
01666         argc++, buf++;
01667     }
01668 
01669     rc = XpoptDupArgv(argc, argv, argcPtr, argvPtr);
01670 
01671 exit:
01672     if (argv) free(argv);
01673     return rc;
01674 }
01675 /*@=bounds@*/
01676 /* =============================================================== */
01677 /*@unchecked@*/
01678 static int _debug = 0;
01679 
01680 int rpmGlob(const char * patterns, int * argcPtr, const char *** argvPtr)
01681 {
01682     int ac = 0;
01683     const char ** av = NULL;
01684     int argc = 0;
01685     const char ** argv = NULL;
01686     char * globRoot = NULL;
01687 #ifdef ENABLE_NLS
01688     const char * old_collate = NULL;
01689     const char * old_ctype = NULL;
01690     const char * t;
01691 #endif
01692         size_t maxb, nb;
01693     int i, j;
01694     int rc;
01695 
01696     rc = XpoptParseArgvString(patterns, &ac, &av);
01697     if (rc)
01698         return rc;
01699 #ifdef ENABLE_NLS
01700 /*@-branchstate@*/
01701         t = setlocale(LC_COLLATE, NULL);
01702         if (t)
01703             old_collate = xstrdup(t);
01704         t = setlocale(LC_CTYPE, NULL);
01705         if (t)
01706             old_ctype = xstrdup(t);
01707 /*@=branchstate@*/
01708         (void) setlocale(LC_COLLATE, "C");
01709         (void) setlocale(LC_CTYPE, "C");
01710 #endif
01711         
01712     if (av != NULL)
01713     for (j = 0; j < ac; j++) {
01714         const char * globURL;
01715         const char * path;
01716         int ut = urlPath(av[j], &path);
01717         glob_t gl;
01718 
01719         if (!Glob_pattern_p(av[j], 0) && strchr(path, '~') == NULL) {
01720             argv = xrealloc(argv, (argc+2) * sizeof(*argv));
01721             argv[argc] = xstrdup(av[j]);
01722 if (_debug)
01723 fprintf(stderr, "*** rpmGlob argv[%d] \"%s\"\n", argc, argv[argc]);
01724             argc++;
01725             continue;
01726         }
01727         
01728         gl.gl_pathc = 0;
01729         gl.gl_pathv = NULL;
01730         rc = Glob(av[j], GLOB_TILDE, Glob_error, &gl);
01731         if (rc)
01732             goto exit;
01733 
01734         /* XXX Prepend the URL leader for globs that have stripped it off */
01735         maxb = 0;
01736         for (i = 0; i < gl.gl_pathc; i++) {
01737             if ((nb = strlen(&(gl.gl_pathv[i][0]))) > maxb)
01738                 maxb = nb;
01739         }
01740         
01741         nb = ((ut == URL_IS_PATH) ? (path - av[j]) : 0);
01742         maxb += nb;
01743         maxb += 1;
01744         globURL = globRoot = xmalloc(maxb);
01745 
01746         switch (ut) {
01747         case URL_IS_PATH:
01748         case URL_IS_DASH:
01749             strncpy(globRoot, av[j], nb);
01750             /*@switchbreak@*/ break;
01751         case URL_IS_HTTPS:
01752         case URL_IS_HTTP:
01753         case URL_IS_FTP:
01754         case URL_IS_HKP:
01755         case URL_IS_UNKNOWN:
01756         default:
01757             /*@switchbreak@*/ break;
01758         }
01759         globRoot += nb;
01760         *globRoot = '\0';
01761 if (_debug)
01762 fprintf(stderr, "*** GLOB maxb %d diskURL %d %*s globURL %p %s\n", (int)maxb, (int)nb, (int)nb, av[j], globURL, globURL);
01763         
01764         argv = xrealloc(argv, (argc+gl.gl_pathc+1) * sizeof(*argv));
01765 
01766         if (argv != NULL)
01767         for (i = 0; i < gl.gl_pathc; i++) {
01768             const char * globFile = &(gl.gl_pathv[i][0]);
01769             if (globRoot > globURL && globRoot[-1] == '/')
01770                 while (*globFile == '/') globFile++;
01771             strcpy(globRoot, globFile);
01772 if (_debug)
01773 fprintf(stderr, "*** rpmGlob argv[%d] \"%s\"\n", argc, globURL);
01774             argv[argc++] = xstrdup(globURL);
01775         }
01776         /*@-immediatetrans@*/
01777         Globfree(&gl);
01778         /*@=immediatetrans@*/
01779         globURL = _free(globURL);
01780     }
01781 
01782     if (argv != NULL && argc > 0) {
01783         argv[argc] = NULL;
01784         if (argvPtr)
01785             *argvPtr = argv;
01786         if (argcPtr)
01787             *argcPtr = argc;
01788         rc = 0;
01789     } else
01790         rc = 1;
01791 
01792 
01793 exit:
01794 #ifdef ENABLE_NLS       
01795 /*@-branchstate@*/
01796     if (old_collate) {
01797         (void) setlocale(LC_COLLATE, old_collate);
01798         old_collate = _free(old_collate);
01799     }
01800     if (old_ctype) {
01801         (void) setlocale(LC_CTYPE, old_ctype);
01802         old_ctype = _free(old_ctype);
01803     }
01804 /*@=branchstate@*/
01805 #endif
01806     av = _free(av);
01807 /*@-branchstate@*/
01808     if (rc || argvPtr == NULL) {
01809 /*@-dependenttrans -unqualifiedtrans@*/
01810         if (argv != NULL)
01811         for (i = 0; i < argc; i++)
01812             argv[i] = _free(argv[i]);
01813         argv = _free(argv);
01814 /*@=dependenttrans =unqualifiedtrans@*/
01815     }
01816 /*@=branchstate@*/
01817     return rc;
01818 }
01819 
01820 /* =============================================================== */
01821 
01822 int
01823 expandMacros(void * spec, MacroContext mc, char * sbuf, size_t slen)
01824 {
01825     MacroBuf mb = alloca(sizeof(*mb));
01826     char *tbuf;
01827     int rc;
01828 
01829     if (sbuf == NULL || slen == 0)
01830         return 0;
01831     if (mc == NULL) mc = rpmGlobalMacroContext;
01832 
01833     tbuf = xmalloc(slen + 1);
01834     memset(tbuf, 0, (slen + 1));
01835 
01836     mb->s = sbuf;
01837     mb->t = tbuf;
01838     mb->nb = slen;
01839     mb->depth = 0;
01840     mb->macro_trace = print_macro_trace;
01841     mb->expand_trace = print_expand_trace;
01842 
01843     mb->spec = spec;    /* (future) %file expansion info */
01844     mb->mc = mc;
01845 
01846     rc = expandMacro(mb);
01847 
01848     if (mb->nb == 0)
01849         rpmError(RPMERR_BADSPEC, _("Target buffer overflow\n"));
01850 
01851     tbuf[slen] = '\0';  /* XXX just in case */
01852     strncpy(sbuf, tbuf, (slen - mb->nb + 1));
01853     free(tbuf);
01854 
01855     return rc;
01856 }
01857 
01858 void
01859 addMacro(MacroContext mc,
01860         const char * n, const char * o, const char * b, int level)
01861 {
01862     MacroEntry * mep;
01863 
01864     if (mc == NULL) mc = rpmGlobalMacroContext;
01865 
01866     /* If new name, expand macro table */
01867     if ((mep = findEntry(mc, n, 0)) == NULL) {
01868         if (mc->firstFree == mc->macrosAllocated)
01869             expandMacroTable(mc);
01870         if (mc->macroTable != NULL)
01871             mep = mc->macroTable + mc->firstFree++;
01872     }
01873 
01874     if (mep != NULL) {
01875         /* Push macro over previous definition */
01876         pushMacro(mep, n, o, b, level);
01877 
01878         /* If new name, sort macro table */
01879         if ((*mep)->prev == NULL)
01880             sortMacroTable(mc);
01881     }
01882 }
01883 
01884 void
01885 delMacro(MacroContext mc, const char * n)
01886 {
01887     MacroEntry * mep;
01888 
01889     if (mc == NULL) mc = rpmGlobalMacroContext;
01890     /* If name exists, pop entry */
01891     if ((mep = findEntry(mc, n, 0)) != NULL) {
01892         popMacro(mep);
01893         /* If deleted name, sort macro table */
01894         if (!(mep && *mep))
01895             sortMacroTable(mc);
01896     }
01897 }
01898 
01899 /*@-mustmod@*/ /* LCL: mc is modified through mb->mc, mb is abstract */
01900 int
01901 rpmDefineMacro(MacroContext mc, const char * macro, int level)
01902 {
01903     MacroBuf mb = alloca(sizeof(*mb));
01904 
01905     memset(mb, 0, sizeof(*mb));
01906     /* XXX just enough to get by */
01907     mb->mc = (mc ? mc : rpmGlobalMacroContext);
01908     (void) doDefine(mb, macro, level, 0);
01909     return 0;
01910 }
01911 /*@=mustmod@*/
01912 
01913 void
01914 rpmLoadMacros(MacroContext mc, int level)
01915 {
01916 
01917     if (mc == NULL || mc == rpmGlobalMacroContext)
01918         return;
01919 
01920     if (mc->macroTable != NULL) {
01921         int i;
01922         for (i = 0; i < mc->firstFree; i++) {
01923             MacroEntry *mep, me;
01924             mep = &mc->macroTable[i];
01925             me = *mep;
01926 
01927             if (me == NULL)             /* XXX this should never happen */
01928                 continue;
01929             addMacro(NULL, me->name, me->opts, me->body, (level - 1));
01930         }
01931     }
01932 }
01933 
01934 int
01935 rpmLoadMacroFile(MacroContext mc, const char * fn)
01936 {
01937     FD_t fd = Fopen(fn, "r.fpio");
01938     char buf[BUFSIZ];
01939     int rc = -1;
01940 
01941     if (fd == NULL || Ferror(fd)) {
01942         if (fd) (void) Fclose(fd);
01943         return rc;
01944     }
01945 
01946     /* XXX Assume new fangled macro expansion */
01947     /*@-mods@*/
01948     max_macro_depth = 16;
01949     /*@=mods@*/
01950 
01951     buf[0] = '\0';
01952     while(rdcl(buf, sizeof(buf), fd) != NULL) {
01953         char c, *n;
01954 
01955         n = buf;
01956         SKIPBLANK(n, c);
01957 
01958         if (c != '%')
01959                 continue;
01960         n++;    /* skip % */
01961         rc = rpmDefineMacro(mc, n, RMIL_MACROFILES);
01962     }
01963     rc = Fclose(fd);
01964     return rc;
01965 }
01966 
01967 void
01968 rpmInitMacros(MacroContext mc, const char * macrofiles)
01969 {
01970     char *mfiles, *m, *me;
01971 
01972     if (macrofiles == NULL)
01973         return;
01974 #ifdef  DYING
01975     if (mc == NULL) mc = rpmGlobalMacroContext;
01976 #endif
01977 
01978     mfiles = xstrdup(macrofiles);
01979     for (m = mfiles; m && *m != '\0'; m = me) {
01980         const char ** av;
01981         int ac;
01982         int i;
01983 
01984         for (me = m; (me = strchr(me, ':')) != NULL; me++) {
01985             /* Skip over URI's. */
01986             if (!(me[1] == '/' && me[2] == '/'))
01987                 /*@innerbreak@*/ break;
01988         }
01989 
01990         if (me && *me == ':')
01991             *me++ = '\0';
01992         else
01993             me = m + strlen(m);
01994 
01995         /* Glob expand the macro file path element, expanding ~ to $HOME. */
01996         ac = 0;
01997         av = NULL;
01998         i = rpmGlob(m, &ac, &av);
01999         if (i != 0)
02000             continue;
02001 
02002         /* Read macros from each file. */
02003         for (i = 0; i < ac; i++) {
02004             size_t slen = strlen(av[i]);
02005             if ((av[i])[slen-1] != '~' &&
02006                 (slen < 8 || strcmp((av[i] + slen - 7), ".rpmnew")) &&
02007                 (slen < 9 || (strcmp((av[i] + slen - 8), ".rpmorig") &&
02008                               strcmp((av[i] + slen - 8), ".rpmsave")))) {
02009                 (void) rpmLoadMacroFile(mc, av[i]);
02010             }
02011         }
02012         av = _free(av);
02013     }
02014     mfiles = _free(mfiles);
02015 
02016     /* Reload cmdline macros */
02017     /*@-mods@*/
02018     rpmLoadMacros(rpmCLIMacroContext, RMIL_CMDLINE);
02019     /*@=mods@*/
02020 }
02021 
02022 /*@-globstate@*/
02023 void
02024 rpmFreeMacros(MacroContext mc)
02025 {
02026     
02027     if (mc == NULL) mc = rpmGlobalMacroContext;
02028 
02029     if (mc->macroTable != NULL) {
02030         int i;
02031         for (i = 0; i < mc->firstFree; i++) {
02032             MacroEntry me;
02033             while ((me = mc->macroTable[i]) != NULL) {
02034                 /* XXX cast to workaround const */
02035                 /*@-onlytrans@*/
02036                 if ((mc->macroTable[i] = me->prev) == NULL)
02037                     me->name = _free(me->name);
02038                 /*@=onlytrans@*/
02039                 me->opts = _free(me->opts);
02040                 me->body = _free(me->body);
02041                 me = _free(me);
02042             }
02043         }
02044         mc->macroTable = _free(mc->macroTable);
02045     }
02046     memset(mc, 0, sizeof(*mc));
02047 }
02048 /*@=globstate@*/
02049 
02050 /* =============================================================== */
02051 int isCompressed(const char * file, rpmCompressedMagic * compressed)
02052 {
02053     FD_t fd;
02054     ssize_t nb;
02055     int rc = -1;
02056     unsigned char magic[4];
02057 
02058     *compressed = COMPRESSED_NOT;
02059 
02060     fd = Fopen(file, "r.ufdio");
02061     if (fd == NULL || Ferror(fd)) {
02062         /* XXX Fstrerror */
02063         rpmError(RPMERR_BADSPEC, _("File %s: %s\n"), file, Fstrerror(fd));
02064         if (fd) (void) Fclose(fd);
02065         return 1;
02066     }
02067     nb = Fread(magic, sizeof(magic[0]), sizeof(magic), fd);
02068     if (nb < 0) {
02069         rpmError(RPMERR_BADSPEC, _("File %s: %s\n"), file, Fstrerror(fd));
02070         rc = 1;
02071     } else if (nb < sizeof(magic)) {
02072         rpmError(RPMERR_BADSPEC, _("File %s is smaller than %u bytes\n"),
02073                 file, (unsigned)sizeof(magic));
02074         rc = 0;
02075     }
02076     (void) Fclose(fd);
02077     if (rc >= 0)
02078         return rc;
02079 
02080     rc = 0;
02081 
02082     if ((magic[0] == 'B') && (magic[1] == 'Z')) {
02083         *compressed = COMPRESSED_BZIP2;
02084     } else if ((magic[0] == 0120) && (magic[1] == 0113) &&
02085          (magic[2] == 0003) && (magic[3] == 0004)) {    /* pkzip */
02086         *compressed = COMPRESSED_ZIP;
02087     } else if (((magic[0] == 0037) && (magic[1] == 0213)) || /* gzip */
02088         ((magic[0] == 0037) && (magic[1] == 0236)) ||   /* old gzip */
02089         ((magic[0] == 0037) && (magic[1] == 0036)) ||   /* pack */
02090         ((magic[0] == 0037) && (magic[1] == 0240)) ||   /* SCO lzh */
02091         ((magic[0] == 0037) && (magic[1] == 0235))      /* compress */
02092         ) {
02093         *compressed = COMPRESSED_OTHER;
02094     }
02095 
02096     return rc;
02097 }
02098 
02099 /* =============================================================== */
02100 
02101 /*@-modfilesys@*/
02102 char * 
02103 rpmExpand(const char *arg, ...)
02104 {
02105     static char buf[BUFSIZ];
02106     char *p, *pe;
02107     const char *s;
02108     va_list ap;
02109 
02110     if (arg == NULL)
02111         return xstrdup("");
02112 
02113     buf[0] = '\0';
02114     p = buf;
02115     pe = stpcpy(p, arg);
02116 
02117     va_start(ap, arg);
02118     while ((s = va_arg(ap, const char *)) != NULL)
02119         pe = stpcpy(pe, s);
02120     va_end(ap);
02121     (void) expandMacros(NULL, NULL, buf, sizeof(buf));
02122     return xstrdup(buf);
02123 }
02124 /*@=modfilesys@*/
02125 
02126 int
02127 rpmExpandNumeric(const char *arg)
02128 {
02129     const char *val;
02130     int rc;
02131 
02132     if (arg == NULL)
02133         return 0;
02134 
02135     val = rpmExpand(arg, NULL);
02136     if (!(val && *val != '%'))
02137         rc = 0;
02138     else if (*val == 'Y' || *val == 'y')
02139         rc = 1;
02140     else if (*val == 'N' || *val == 'n')
02141         rc = 0;
02142     else {
02143         char *end;
02144         rc = strtol(val, &end, 0);
02145         if (!(end && *end == '\0'))
02146             rc = 0;
02147     }
02148     val = _free(val);
02149 
02150     return rc;
02151 }
02152 
02153 /* @todo "../sbin/./../bin/" not correct. */
02154 char *rpmCleanPath(char * path)
02155 {
02156     const char *s;
02157     char *se, *t, *te;
02158     int begin = 1;
02159 
02160     if (path == NULL)
02161         return NULL;
02162 
02163 /*fprintf(stderr, "*** RCP %s ->\n", path); */
02164     s = t = te = path;
02165     while (*s != '\0') {
02166 /*fprintf(stderr, "*** got \"%.*s\"\trest \"%s\"\n", (t-path), path, s); */
02167         switch(*s) {
02168         case ':':                       /* handle url's */
02169             if (s[1] == '/' && s[2] == '/') {
02170                 *t++ = *s++;
02171                 *t++ = *s++;
02172                 /*@switchbreak@*/ break;
02173             }
02174             begin=1;
02175             /*@switchbreak@*/ break;
02176         case '/':
02177             /* Move parent dir forward */
02178             for (se = te + 1; se < t && *se != '/'; se++)
02179                 {};
02180             if (se < t && *se == '/') {
02181                 te = se;
02182 /*fprintf(stderr, "*** next pdir \"%.*s\"\n", (te-path), path); */
02183             }
02184             while (s[1] == '/')
02185                 s++;
02186             while (t > path && t[-1] == '/')
02187                 t--;
02188             /*@switchbreak@*/ break;
02189         case '.':
02190             /* Leading .. is special */
02191             /* Check that it is ../, so that we don't interpret */
02192             /* ..?(i.e. "...") or ..* (i.e. "..bogus") as "..". */
02193             /* in the case of "...", this ends up being processed*/
02194             /* as "../.", and the last '.' is stripped.  This   */
02195             /* would not be correct processing.                 */
02196             if (begin && s[1] == '.' && (s[2] == '/' || s[2] == '\0')) {
02197 /*fprintf(stderr, "    leading \"..\"\n"); */
02198                 *t++ = *s++;
02199                 /*@switchbreak@*/ break;
02200             }
02201             /* Single . is special */
02202             if (begin && s[1] == '\0') {
02203                 /*@switchbreak@*/ break;
02204             }
02205             /* Trim embedded ./ , trailing /. */
02206             if ((t[-1] == '/' && s[1] == '\0') || (t > path && t[-1] == '/' && s[1] == '/')) {
02207                 s++;
02208                 continue;
02209             }
02210             /* Trim embedded /../ and trailing /.. */
02211             if (!begin && t > path && t[-1] == '/' && s[1] == '.' && (s[2] == '/' || s[2] == '\0')) {
02212                 t = te;
02213                 /* Move parent dir forward */
02214                 if (te > path)
02215                     for (--te; te > path && *te != '/'; te--)
02216                         {};
02217 /*fprintf(stderr, "*** prev pdir \"%.*s\"\n", (te-path), path); */
02218                 s++;
02219                 s++;
02220                 continue;
02221             }
02222             /*@switchbreak@*/ break;
02223         default:
02224             begin = 0;
02225             /*@switchbreak@*/ break;
02226         }
02227         *t++ = *s++;
02228     }
02229 
02230     /* Trim trailing / (but leave single / alone) */
02231     if (t > &path[1] && t[-1] == '/')
02232         t--;
02233     *t = '\0';
02234 
02235 /*fprintf(stderr, "\t%s\n", path); */
02236     return path;
02237 }
02238 
02239 /* Return concatenated and expanded canonical path. */
02240 
02241 const char *
02242 rpmGetPath(const char *path, ...)
02243 {
02244     static char buf[BUFSIZ];
02245     const char * s;
02246     char * t, * te;
02247     va_list ap;
02248 
02249     if (path == NULL)
02250         return xstrdup("");
02251 
02252     buf[0] = '\0';
02253     t = buf;
02254     te = stpcpy(t, path);
02255     *te = '\0';
02256 
02257     va_start(ap, path);
02258     while ((s = va_arg(ap, const char *)) != NULL) {
02259         te = stpcpy(te, s);
02260         *te = '\0';
02261     }
02262     va_end(ap);
02263 /*@-modfilesys@*/
02264     (void) expandMacros(NULL, NULL, buf, sizeof(buf));
02265 /*@=modfilesys@*/
02266 
02267     (void) rpmCleanPath(buf);
02268     return xstrdup(buf);        /* XXX xstrdup has side effects. */
02269 }
02270 
02271 /* Merge 3 args into path, any or all of which may be a url. */
02272 
02273 const char * rpmGenPath(const char * urlroot, const char * urlmdir,
02274                 const char *urlfile)
02275 {
02276 /*@owned@*/ const char * xroot = rpmGetPath(urlroot, NULL);
02277 /*@dependent@*/ const char * root = xroot;
02278 /*@owned@*/ const char * xmdir = rpmGetPath(urlmdir, NULL);
02279 /*@dependent@*/ const char * mdir = xmdir;
02280 /*@owned@*/ const char * xfile = rpmGetPath(urlfile, NULL);
02281 /*@dependent@*/ const char * file = xfile;
02282     const char * result;
02283     const char * url = NULL;
02284     int nurl = 0;
02285     int ut;
02286 
02287 #if 0
02288 if (_debug) fprintf(stderr, "*** RGP xroot %s xmdir %s xfile %s\n", xroot, xmdir, xfile);
02289 #endif
02290     ut = urlPath(xroot, &root);
02291     if (url == NULL && ut > URL_IS_DASH) {
02292         url = xroot;
02293         nurl = root - xroot;
02294 #if 0
02295 if (_debug) fprintf(stderr, "*** RGP ut %d root %s nurl %d\n", ut, root, nurl);
02296 #endif
02297     }
02298     if (root == NULL || *root == '\0') root = "/";
02299 
02300     ut = urlPath(xmdir, &mdir);
02301     if (url == NULL && ut > URL_IS_DASH) {
02302         url = xmdir;
02303         nurl = mdir - xmdir;
02304 #if 0
02305 if (_debug) fprintf(stderr, "*** RGP ut %d mdir %s nurl %d\n", ut, mdir, nurl);
02306 #endif
02307     }
02308     if (mdir == NULL || *mdir == '\0') mdir = "/";
02309 
02310     ut = urlPath(xfile, &file);
02311     if (url == NULL && ut > URL_IS_DASH) {
02312         url = xfile;
02313         nurl = file - xfile;
02314 #if 0
02315 if (_debug) fprintf(stderr, "*** RGP ut %d file %s nurl %d\n", ut, file, nurl);
02316 #endif
02317     }
02318 
02319 /*@-branchstate@*/
02320     if (url && nurl > 0) {
02321         char *t = strncpy(alloca(nurl+1), url, nurl);
02322         t[nurl] = '\0';
02323         url = t;
02324     } else
02325         url = "";
02326 /*@=branchstate@*/
02327 
02328     result = rpmGetPath(url, root, "/", mdir, "/", file, NULL);
02329 
02330     xroot = _free(xroot);
02331     xmdir = _free(xmdir);
02332     xfile = _free(xfile);
02333 #if 0
02334 if (_debug) fprintf(stderr, "*** RGP result %s\n", result);
02335 #endif
02336     return result;
02337 }
02338 
02339 /* =============================================================== */
02340 
02341 #if defined(DEBUG_MACROS)
02342 
02343 #if defined(EVAL_MACROS)
02344 
02345 char *macrofiles = "/usr/lib/rpm/macros:/etc/rpm/macros:~/.rpmmacros";
02346 
02347 int
02348 main(int argc, char *argv[])
02349 {
02350     int c;
02351     int errflg = 0;
02352     extern char *optarg;
02353     extern int optind;
02354 
02355     while ((c = getopt(argc, argv, "f:")) != EOF ) {
02356         switch (c) {
02357         case 'f':
02358             macrofiles = optarg;
02359             break;
02360         case '?':
02361         default:
02362             errflg++;
02363             break;
02364         }
02365     }
02366     if (errflg || optind >= argc) {
02367         fprintf(stderr, "Usage: %s [-f macropath ] macro ...\n", argv[0]);
02368         exit(1);
02369     }
02370 
02371     rpmInitMacros(NULL, macrofiles);
02372     for ( ; optind < argc; optind++) {
02373         const char *val;
02374 
02375         val = rpmGetPath(argv[optind], NULL);
02376         if (val) {
02377             fprintf(stdout, "%s:\t%s\n", argv[optind], val);
02378             val = _free(val);
02379         }
02380     }
02381     rpmFreeMacros(NULL);
02382     return 0;
02383 }
02384 
02385 #else   /* !EVAL_MACROS */
02386 
02387 char *macrofiles = "../macros:./testmacros";
02388 char *testfile = "./test";
02389 
02390 int
02391 main(int argc, char *argv[])
02392 {
02393     char buf[BUFSIZ];
02394     FILE *fp;
02395     int x;
02396 
02397     rpmInitMacros(NULL, macrofiles);
02398     rpmDumpMacroTable(NULL, NULL);
02399 
02400     if ((fp = fopen(testfile, "r")) != NULL) {
02401         while(rdcl(buf, sizeof(buf), fp)) {
02402             x = expandMacros(NULL, NULL, buf, sizeof(buf));
02403             fprintf(stderr, "%d->%s\n", x, buf);
02404             memset(buf, 0, sizeof(buf));
02405         }
02406         fclose(fp);
02407     }
02408 
02409     while(rdcl(buf, sizeof(buf), stdin)) {
02410         x = expandMacros(NULL, NULL, buf, sizeof(buf));
02411         fprintf(stderr, "%d->%s\n <-\n", x, buf);
02412         memset(buf, 0, sizeof(buf));
02413     }
02414     rpmFreeMacros(NULL);
02415 
02416     return 0;
02417 }
02418 #endif  /* EVAL_MACROS */
02419 #endif  /* DEBUG_MACROS */
02420 /*@=boundsread@*/

Generated on Mon Mar 5 13:44:05 2007 for rpm by  doxygen 1.5.1