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

Generated on Fri Aug 31 11:16:08 2007 for rpm by  doxygen 1.4.6