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

Generated on Fri Apr 16 16:33:55 2004 for rpm by doxygen 1.3.6