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

Generated on Fri Oct 12 08:44:54 2007 for rpm by  doxygen 1.5.2