Main Page | Modules | Data Structures | File List | Data Fields | Globals | Related Pages

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

Generated on Mon Aug 3 15:15:36 2009 for rpm by  doxygen 1.4.4