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

Generated on Wed Apr 4 09:22:10 2012 for rpm by  doxygen 1.4.7