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

Generated on Sat Mar 5 22:39:50 2011 for rpm by  doxygen 1.4.7