Main Page   Modules   Compound List   File List   Compound Members   File Members   Related Pages  

rpmio/macro.c

Go to the documentation of this file.
00001 
00005 static int _debug = 0;
00006 
00007 #include "system.h"
00008 #include <stdarg.h>
00009 
00010 #if !defined(isblank)
00011 #define isblank(_c)     ((_c) == ' ' || (_c) == '\t')
00012 #endif
00013 #define iseol(_c)       ((_c) == '\n' || (_c) == '\r')
00014 
00015 #define STREQ(_t, _f, _fn)      ((_fn) == (sizeof(_t)-1) && !strncmp((_t), (_f), (_fn)))
00016 #define FREE(_x)        { if (_x) free((void *)_x); (_x) = NULL; }
00017 
00018 #ifdef DEBUG_MACROS
00019 #include <sys/types.h>
00020 #include <errno.h>
00021 #include <fcntl.h>
00022 #include <getopt.h>
00023 #include <stdio.h>
00024 #include <stdlib.h>
00025 #include <string.h>
00026 #define rpmError fprintf
00027 #define RPMERR_BADSPEC stderr
00028 #undef  _
00029 #define _(x)    x
00030 
00031 #define vmefail()               (exit(1), NULL)
00032 #define urlPath(_xr, _r)        *(_r) = (_xr)
00033 
00034 typedef FILE * FD_t;
00035 #define Fopen(_path, _fmode)    fopen(_path, "r");
00036 #define Ferror                  ferror
00037 #define Fstrerror(_fd)          strerror(errno)
00038 #define Fread                   fread
00039 #define Fclose                  fclose
00040 
00041 #define fdGetFILE(_fd)          (_fd)
00042 
00043 #else
00044 
00045 #include <rpmio_internal.h>
00046 #include <rpmmessages.h>
00047 #include <rpmerr.h>
00048 
00049 #endif
00050 
00051 #include <rpmmacro.h>
00052 
00053 #include "debug.h"
00054 
00055 /*@access FD_t@*/               /* XXX compared with NULL */
00056 /*@access MacroContext@*/
00057 /*@access MacroEntry@*/
00058 
00059 struct MacroContext rpmGlobalMacroContext;
00060 struct MacroContext rpmCLIMacroContext;
00061 
00065 typedef struct MacroBuf {
00066 /*@shared@*/ const char *s;             
00067 /*@shared@*/ char *t;           
00068         size_t nb;              
00069         int depth;              
00070         int macro_trace;        
00071         int expand_trace;       
00072 /*@shared@*/ void *spec;        
00073 /*@dependent@*/ MacroContext *mc;
00074 } MacroBuf;
00075 
00076 #define SAVECHAR(_mb, _c) { *(_mb)->t = (_c), (_mb)->t++, (_mb)->nb--; }
00077 
00078 static int expandMacro(MacroBuf *mb);
00079 
00080 #define MAX_MACRO_DEPTH 16
00081 int max_macro_depth = MAX_MACRO_DEPTH;
00082 
00083 #ifdef  DEBUG_MACROS
00084 int print_macro_trace = 0;
00085 int print_expand_trace = 0;
00086 #else
00087 int print_macro_trace = 0;
00088 int print_expand_trace = 0;
00089 #endif
00090 
00091 #define MACRO_CHUNK_SIZE        16
00092 
00093 /* =============================================================== */
00094 
00101 static int
00102 compareMacroName(const void *ap, const void *bp)
00103 {
00104         MacroEntry *ame = *((MacroEntry **)ap);
00105         MacroEntry *bme = *((MacroEntry **)bp);
00106 
00107         if (ame == NULL && bme == NULL)
00108                 return 0;
00109         if (ame == NULL)
00110                 return 1;
00111         if (bme == NULL)
00112                 return -1;
00113         return strcmp(ame->name, bme->name);
00114 }
00115 
00120 static void
00121 expandMacroTable(MacroContext *mc)
00122 {
00123         if (mc->macroTable == NULL) {
00124                 mc->macrosAllocated = MACRO_CHUNK_SIZE;
00125                 mc->macroTable = (MacroEntry **)
00126                     xmalloc(sizeof(*(mc->macroTable)) * mc->macrosAllocated);
00127                 mc->firstFree = 0;
00128         } else {
00129                 mc->macrosAllocated += MACRO_CHUNK_SIZE;
00130                 mc->macroTable = (MacroEntry **)
00131                     xrealloc(mc->macroTable, sizeof(*(mc->macroTable)) *
00132                                 mc->macrosAllocated);
00133         }
00134         memset(&mc->macroTable[mc->firstFree], 0, MACRO_CHUNK_SIZE * sizeof(*(mc->macroTable)));
00135 }
00136 
00141 static void
00142 sortMacroTable(MacroContext *mc)
00143 {
00144         int i;
00145 
00146         qsort(mc->macroTable, mc->firstFree, sizeof(*(mc->macroTable)),
00147                 compareMacroName);
00148 
00149         /* Empty pointers are now at end of table. Reset first free index. */
00150         for (i = 0; i < mc->firstFree; i++) {
00151                 if (mc->macroTable[i] != NULL)
00152                         continue;
00153                 mc->firstFree = i;
00154                 break;
00155         }
00156 }
00157 
00158 void
00159 rpmDumpMacroTable(MacroContext * mc, FILE * fp)
00160 {
00161         int i;
00162         int nempty = 0;
00163         int nactive = 0;
00164 
00165         if (mc == NULL)
00166                 mc = &rpmGlobalMacroContext;
00167         if (fp == NULL)
00168                 fp = stderr;
00169     
00170         fprintf(fp, "========================\n");
00171         for (i = 0; i < mc->firstFree; i++) {
00172                 MacroEntry *me;
00173                 if ((me = mc->macroTable[i]) == NULL) {
00174                         /* XXX this should never happen */
00175                         nempty++;
00176                         continue;
00177                 }
00178                 fprintf(fp, "%3d%c %s", me->level,
00179                         (me->used > 0 ? '=' : ':'), me->name);
00180                 if (me->opts && *me->opts)
00181                         fprintf(fp, "(%s)", me->opts);
00182                 if (me->body && *me->body)
00183                         fprintf(fp, "\t%s", me->body);
00184                 fprintf(fp, "\n");
00185                 nactive++;
00186         }
00187         fprintf(fp, _("======================== active %d empty %d\n"),
00188                 nactive, nempty);
00189 }
00190 
00198 /*@dependent@*/ static MacroEntry **
00199 findEntry(MacroContext *mc, const char *name, size_t namelen)
00200 {
00201         MacroEntry keybuf, *key, **ret;
00202         char namebuf[1024];
00203 
00204         if (mc == NULL)
00205                 mc = &rpmGlobalMacroContext;
00206         if (! mc->firstFree)
00207                 return NULL;
00208 
00209         if (namelen > 0) {
00210                 strncpy(namebuf, name, namelen);
00211                 namebuf[namelen] = '\0';
00212                 name = namebuf;
00213         }
00214     
00215         key = &keybuf;
00216         memset(key, 0, sizeof(*key));
00217         key->name = (char *)name;
00218         ret = (MacroEntry **)bsearch(&key, mc->macroTable, mc->firstFree,
00219                         sizeof(*(mc->macroTable)), compareMacroName);
00220         /* XXX TODO: find 1st empty slot and return that */
00221         return ret;
00222 }
00223 
00224 /* =============================================================== */
00225 
00229 /*@dependent@*/ static char *
00230 rdcl(char *buf, size_t size, FD_t fd, int escapes)
00231 {
00232         char *q = buf;
00233         size_t nb = 0;
00234         size_t nread = 0;
00235 
00236         *q = '\0';
00237         do {
00238                 /* read next line */
00239                 if (fgets(q, size, fdGetFILE(fd)) == NULL)
00240                         break;
00241                 nb = strlen(q);
00242                 nread += nb;
00243                 for (q += nb - 1; nb > 0 && iseol(*q); q--)
00244                         nb--;
00245                 if (!(nb > 0 && *q == '\\')) {  /* continue? */
00246                         *(++q) = '\0';          /* trim trailing \r, \n */
00247                         break;
00248                 }
00249                 if (escapes) {                  /* copy escape too */
00250                         q++;
00251                         nb++;
00252                 }
00253                 size -= nb;
00254                 if (*q == '\r')                 /* XXX avoid \r madness */
00255                         *q = '\n';
00256                 *(++q) = '\0';                  /* next char in buf */
00257         } while (size > 0);
00258         return (nread > 0 ? buf : NULL);
00259 }
00260 
00268 static const char *
00269 matchchar(const char *p, char pl, char pr)
00270 {
00271         int lvl = 0;
00272         char c;
00273 
00274         while ((c = *p++) != '\0') {
00275                 if (c == '\\') {                /* Ignore escaped chars */
00276                         p++;
00277                         continue;
00278                 }
00279                 if (c == pr) {
00280                         if (--lvl <= 0) return --p;
00281                 } else if (c == pl)
00282                         lvl++;
00283         }
00284         return (const char *)NULL;
00285 }
00286 
00293 static void
00294 printMacro(MacroBuf *mb, const char *s, const char *se)
00295 {
00296         const char *senl;
00297         const char *ellipsis;
00298         int choplen;
00299 
00300         if (s >= se) {  /* XXX just in case */
00301                 fprintf(stderr, _("%3d>%*s(empty)"), mb->depth,
00302                         (2 * mb->depth + 1), "");
00303                 return;
00304         }
00305 
00306         if (s[-1] == '{')
00307                 s--;
00308 
00309         /* Print only to first end-of-line (or end-of-string). */
00310         for (senl = se; *senl && !iseol(*senl); senl++)
00311                 ;
00312 
00313         /* Limit trailing non-trace output */
00314         choplen = 61 - (2 * mb->depth);
00315         if ((senl - s) > choplen) {
00316                 senl = s + choplen;
00317                 ellipsis = "...";
00318         } else
00319                 ellipsis = "";
00320 
00321         /* Substitute caret at end-of-macro position */
00322         fprintf(stderr, "%3d>%*s%%%.*s^", mb->depth,
00323                 (2 * mb->depth + 1), "", (int)(se - s), s);
00324         if (se[1] != '\0' && (senl - (se+1)) > 0)
00325                 fprintf(stderr, "%-.*s%s", (int)(senl - (se+1)), se+1, ellipsis);
00326         fprintf(stderr, "\n");
00327 }
00328 
00335 static void
00336 printExpansion(MacroBuf *mb, const char *t, const char *te)
00337 {
00338         const char *ellipsis;
00339         int choplen;
00340 
00341         if (!(te > t)) {
00342                 fprintf(stderr, _("%3d<%*s(empty)\n"), mb->depth, (2 * mb->depth + 1), "");
00343                 return;
00344         }
00345 
00346         /* Shorten output which contains newlines */
00347         while (te > t && iseol(te[-1]))
00348                 te--;
00349         ellipsis = "";
00350         if (mb->depth > 0) {
00351                 const char *tenl;
00352 
00353                 /* Skip to last line of expansion */
00354                 while ((tenl = strchr(t, '\n')) && tenl < te)
00355                         t = ++tenl;
00356 
00357                 /* Limit expand output */
00358                 choplen = 61 - (2 * mb->depth);
00359                 if ((te - t) > choplen) {
00360                         te = t + choplen;
00361                         ellipsis = "...";
00362                 }
00363         }
00364 
00365         fprintf(stderr, "%3d<%*s", mb->depth, (2 * mb->depth + 1), "");
00366         if (te > t)
00367                 fprintf(stderr, "%.*s%s", (int)(te - t), t, ellipsis);
00368         fprintf(stderr, "\n");
00369 }
00370 
00371 #define SKIPBLANK(_s, _c)       \
00372         while (((_c) = *(_s)) && isblank(_c)) \
00373                 (_s)++;
00374 
00375 #define SKIPNONBLANK(_s, _c)    \
00376         while (((_c) = *(_s)) && !(isblank(_c) || iseol(_c))) \
00377                 (_s)++;
00378 
00379 #define COPYNAME(_ne, _s, _c)   \
00380     {   SKIPBLANK(_s,_c);       \
00381         while(((_c) = *(_s)) && (isalnum(_c) || (_c) == '_')) \
00382                 *(_ne)++ = *(_s)++; \
00383         *(_ne) = '\0';          \
00384     }
00385 
00386 #define COPYOPTS(_oe, _s, _c)   \
00387     {   while(((_c) = *(_s)) && (_c) != ')') \
00388                 *(_oe)++ = *(_s)++; \
00389         *(_oe) = '\0';          \
00390     }
00391 
00392 #define COPYBODY(_be, _s, _c)   \
00393     {   while(((_c) = *(_s)) && !iseol(_c)) { \
00394                 if ((_c) == '\\') \
00395                         (_s)++; \
00396                 *(_be)++ = *(_s)++; \
00397         }                       \
00398         *(_be) = '\0';          \
00399     }
00400 
00408 static int
00409 expandT(MacroBuf *mb, const char *f, size_t flen)
00410 {
00411         char *sbuf;
00412         const char *s = mb->s;
00413         int rc;
00414 
00415         sbuf = alloca(flen + 1);
00416         memset(sbuf, 0, (flen + 1));
00417 
00418         strncpy(sbuf, f, flen);
00419         sbuf[flen] = '\0';
00420         mb->s = sbuf;
00421         rc = expandMacro(mb);
00422         mb->s = s;
00423         return rc;
00424 }
00425 
00426 #if 0
00427 
00434 static int
00435 expandS(MacroBuf *mb, char *tbuf, size_t tbuflen)
00436 {
00437         const char *t = mb->t;
00438         size_t nb = mb->nb;
00439         int rc;
00440 
00441         mb->t = tbuf;
00442         mb->nb = tbuflen;
00443         rc = expandMacro(mb);
00444         mb->t = t;
00445         mb->nb = nb;
00446         return rc;
00447 }
00448 #endif
00449 
00457 static int
00458 expandU(MacroBuf *mb, char *u, size_t ulen)
00459 {
00460         const char *s = mb->s;
00461         char *t = mb->t;
00462         size_t nb = mb->nb;
00463         char *tbuf;
00464         int rc;
00465 
00466         tbuf = alloca(ulen + 1);
00467         memset(tbuf, 0, (ulen + 1));
00468 
00469         mb->s = u;
00470         mb->t = tbuf;
00471         mb->nb = ulen;
00472         rc = expandMacro(mb);
00473 
00474         tbuf[ulen] = '\0';      /* XXX just in case */
00475         if (ulen > mb->nb)
00476                 strncpy(u, tbuf, (ulen - mb->nb + 1));
00477 
00478         mb->s = s;
00479         mb->t = t;
00480         mb->nb = nb;
00481 
00482         return rc;
00483 }
00484 
00492 static int
00493 doShellEscape(MacroBuf *mb, const char *cmd, size_t clen)
00494 {
00495         char pcmd[BUFSIZ];
00496         FILE *shf;
00497         int rc;
00498         int c;
00499 
00500         strncpy(pcmd, cmd, clen);
00501         pcmd[clen] = '\0';
00502         rc = expandU(mb, pcmd, sizeof(pcmd));
00503         if (rc)
00504                 return rc;
00505 
00506         if ((shf = popen(pcmd, "r")) == NULL)
00507                 return 1;
00508         while(mb->nb > 0 && (c = fgetc(shf)) != EOF)
00509                 SAVECHAR(mb, c);
00510         pclose(shf);
00511 
00512         /* XXX delete trailing \r \n */
00513         while (iseol(mb->t[-1])) {
00514                 *(mb->t--) = '\0';
00515                 mb->nb++;
00516         }
00517         return 0;
00518 }
00519 
00528 /*@dependent@*/ static const char *
00529 doDefine(MacroBuf *mb, const char *se, int level, int expandbody)
00530 {
00531         const char *s = se;
00532         char buf[BUFSIZ], *n = buf, *ne = n;
00533         char *o = NULL, *oe;
00534         char *b, *be;
00535         int c;
00536         int oc = ')';
00537 
00538         /* Copy name */
00539         COPYNAME(ne, s, c);
00540 
00541         /* Copy opts (if present) */
00542         oe = ne + 1;
00543         if (*s == '(') {
00544                 s++;    /* skip ( */
00545                 o = oe;
00546                 COPYOPTS(oe, s, oc);
00547                 s++;    /* skip ) */
00548         }
00549 
00550         /* Copy body, skipping over escaped newlines */
00551         b = be = oe + 1;
00552         SKIPBLANK(s, c);
00553         if (c == '{') { /* XXX permit silent {...} grouping */
00554                 if ((se = matchchar(s, c, '}')) == NULL) {
00555                         rpmError(RPMERR_BADSPEC,
00556                                 _("Macro %%%s has unterminated body\n"), n);
00557                         se = s; /* XXX W2DO? */
00558                         return se;
00559                 }
00560                 s++;    /* XXX skip { */
00561                 strncpy(b, s, (se - s));
00562                 b[se - s] = '\0';
00563                 be += strlen(b);
00564                 se++;   /* XXX skip } */
00565                 s = se; /* move scan forward */
00566         } else {        /* otherwise free-field */
00567                 COPYBODY(be, s, c);
00568 
00569                 /* Trim trailing blanks/newlines */
00570                 while (--be >= b && (c = *be) && (isblank(c) || iseol(c)))
00571                         ;
00572                 *(++be) = '\0'; /* one too far */
00573         }
00574 
00575         /* Move scan over body */
00576         while (iseol(*s))
00577                 s++;
00578         se = s;
00579 
00580         /* Names must start with alphabetic or _ and be at least 3 chars */
00581         if (!((c = *n) && (isalpha(c) || c == '_') && (ne - n) > 2)) {
00582                 rpmError(RPMERR_BADSPEC,
00583                         _("Macro %%%s has illegal name (%%define)\n"), n);
00584                 return se;
00585         }
00586 
00587         /* Options must be terminated with ')' */
00588         if (o && oc != ')') {
00589                 rpmError(RPMERR_BADSPEC,
00590                         _("Macro %%%s has unterminated opts\n"), n);
00591                 return se;
00592         }
00593 
00594         if ((be - b) < 1) {
00595                 rpmError(RPMERR_BADSPEC, _("Macro %%%s has empty body\n"), n);
00596                 return se;
00597         }
00598 
00599         if (expandbody && expandU(mb, b, (&buf[sizeof(buf)] - b))) {
00600                 rpmError(RPMERR_BADSPEC, _("Macro %%%s failed to expand\n"), n);
00601                 return se;
00602         }
00603 
00604         addMacro(mb->mc, n, o, b, (level - 1));
00605 
00606         return se;
00607 }
00608 
00615 /*@dependent@*/ static const char *
00616 doUndefine(MacroContext *mc, const char *se)
00617 {
00618         const char *s = se;
00619         char buf[BUFSIZ], *n = buf, *ne = n;
00620         int c;
00621 
00622         COPYNAME(ne, s, c);
00623 
00624         /* Move scan over body */
00625         while (iseol(*s))
00626                 s++;
00627         se = s;
00628 
00629         /* Names must start with alphabetic or _ and be at least 3 chars */
00630         if (!((c = *n) && (isalpha(c) || c == '_') && (ne - n) > 2)) {
00631                 rpmError(RPMERR_BADSPEC,
00632                         _("Macro %%%s has illegal name (%%undefine)\n"), n);
00633                 return se;
00634         }
00635 
00636         delMacro(mc, n);
00637 
00638         return se;
00639 }
00640 
00641 #ifdef  DYING
00642 static void
00643 dumpME(const char *msg, MacroEntry *me)
00644 {
00645         if (msg)
00646                 fprintf(stderr, "%s", msg);
00647         fprintf(stderr, "\tme %p", me);
00648         if (me)
00649                 fprintf(stderr,"\tname %p(%s) prev %p",
00650                         me->name, me->name, me->prev);
00651         fprintf(stderr, "\n");
00652 }
00653 #endif
00654 
00663 static void
00664 pushMacro(MacroEntry **mep, const char *n, const char *o, const char *b, int level)
00665 {
00666         MacroEntry *prev = (*mep ? *mep : NULL);
00667         MacroEntry *me = (MacroEntry *) xmalloc(sizeof(*me));
00668 
00669         me->prev = prev;
00670         me->name = (prev ? prev->name : xstrdup(n));
00671         me->opts = (o ? xstrdup(o) : NULL);
00672         me->body = xstrdup(b ? b : "");
00673         me->used = 0;
00674         me->level = level;
00675         *mep = me;
00676 }
00677 
00682 static void
00683 popMacro(MacroEntry **mep)
00684 {
00685         MacroEntry *me = (*mep ? *mep : NULL);
00686 
00687         if (me) {
00688                 /* XXX cast to workaround const */
00689                 if ((*mep = me->prev) == NULL)
00690                         FREE(me->name);
00691                 FREE(me->opts);
00692                 FREE(me->body);
00693                 FREE(me);
00694         }
00695 }
00696 
00701 static void
00702 freeArgs(MacroBuf *mb)
00703 {
00704         MacroContext *mc = mb->mc;
00705         int ndeleted = 0;
00706         int i;
00707 
00708         /* Delete dynamic macro definitions */
00709         for (i = 0; i < mc->firstFree; i++) {
00710                 MacroEntry **mep, *me;
00711                 int skiptest = 0;
00712                 mep = &mc->macroTable[i];
00713                 me = *mep;
00714 
00715                 if (me == NULL)         /* XXX this should never happen */
00716                         continue;
00717                 if (me->level < mb->depth)
00718                         continue;
00719                 if (strlen(me->name) == 1 && strchr("#*0", *me->name)) {
00720                         if (*me->name == '*' && me->used > 0)
00721                                 skiptest = 1;
00722                         /* XXX skip test for %# %* %0 */
00723                 } else if (!skiptest && me->used <= 0) {
00724 #if NOTYET
00725                         rpmError(RPMERR_BADSPEC,
00726                             _("Macro %%%s (%s) was not used below level %d\n"),
00727                                 me->name, me->body, me->level);
00728 #endif
00729                 }
00730                 popMacro(mep);
00731                 if (!(mep && *mep))
00732                         ndeleted++;
00733         }
00734 
00735         /* If any deleted macros, sort macro table */
00736         if (ndeleted)
00737                 sortMacroTable(mc);
00738 }
00739 
00748 /*@dependent@*/ static const char *
00749 grabArgs(MacroBuf *mb, const MacroEntry *me, const char *se, char lastc)
00750 {
00751     char buf[BUFSIZ], *b, *be;
00752     char aname[16];
00753     const char *opts, *o;
00754     int argc = 0;
00755     const char **argv;
00756     int c;
00757 
00758     /* Copy macro name as argv[0], save beginning of args.  */
00759     b = be = stpcpy(buf, me->name);
00760 
00761     addMacro(mb->mc, "0", NULL, buf, mb->depth);
00762     
00763     argc = 1;   /* XXX count argv[0] */
00764 
00765     /* Copy args into buf until lastc */
00766     *be++ = ' ';
00767     while ((c = *se++) != '\0' && c != lastc) {
00768         if (!isblank(c)) {
00769             *be++ = c;
00770             continue;
00771         }
00772         /* c is blank */
00773         if (be[-1] == ' ')
00774             continue;
00775         /* a word has ended */
00776         *be++ = ' ';
00777         argc++;
00778     }
00779     if (c == '\0') se--;        /* one too far */
00780     if (be[-1] != ' ')
00781         argc++, be++;           /* last word has not trailing ' ' */
00782     be[-1] = '\0';
00783     if (*b == ' ') b++;         /* skip the leading ' ' */
00784 
00785 /*
00786  * The macro %* analoguous to the shell's $* means "Pass all non-macro
00787  * parameters." Consequently, there needs to be a macro that means "Pass all
00788  * (including macro parameters) options". This is useful for verifying
00789  * parameters during expansion and yet transparently passing all parameters
00790  * through for higher level processing (e.g. %description and/or %setup).
00791  * This is the (potential) justification for %{**} ...
00792  */
00793     /* Add unexpanded args as macro */
00794     addMacro(mb->mc, "**", NULL, b, mb->depth);
00795 
00796 #ifdef NOTYET
00797     /* XXX if macros can be passed as args ... */
00798     expandU(mb, buf, sizeof(buf));
00799 #endif
00800 
00801     /* Build argv array */
00802     argv = (const char **) alloca((argc + 1) * sizeof(char *));
00803     be[-1] = ' ';       /*  be - 1 == b + strlen(b) == buf + strlen(buf)  */
00804     buf[0] = '\0';
00805     b = buf;
00806     for (c = 0; c < argc; c++) {
00807         argv[c] = b;
00808         b = strchr(b, ' ');
00809         *b++ = '\0';
00810     }
00811     /* now should be  b == be  */
00812     argv[argc] = NULL;
00813 
00814     opts = me->opts;
00815 
00816     /* Define option macros. */
00817     while((c = getopt(argc, (char **)argv, opts)) != -1) {
00818         if (c == '?' || (o = strchr(opts, c)) == NULL) {
00819             rpmError(RPMERR_BADSPEC, _("Unknown option %c in %s(%s)\n"),
00820                         c, me->name, opts);
00821             return se;
00822         }
00823         *be++ = '-';
00824         *be++ = c;
00825         if (o[1] == ':') {
00826             *be++ = ' ';
00827             be = stpcpy(be, optarg);
00828         }
00829         *be++ = '\0';
00830         aname[0] = '-'; aname[1] = c; aname[2] = '\0';
00831         addMacro(mb->mc, aname, NULL, b, mb->depth);
00832         if (o[1] == ':') {
00833             aname[0] = '-'; aname[1] = c; aname[2] = '*'; aname[3] = '\0';
00834             addMacro(mb->mc, aname, NULL, optarg, mb->depth);
00835         }
00836         be = b; /* reuse the space */
00837     }
00838 
00839     /* Add arg count as macro. */
00840     sprintf(aname, "%d", (argc - optind));
00841     addMacro(mb->mc, "#", NULL, aname, mb->depth);
00842 
00843     /* Add macro for each arg. Concatenate args for %*. */
00844     *be = '\0';
00845     for (c = optind; c < argc; c++) {
00846         sprintf(aname, "%d", (c - optind + 1));
00847         addMacro(mb->mc, aname, NULL, argv[c], mb->depth);
00848         *be++ = ' ';
00849         be = stpcpy(be, argv[c]);
00850     }
00851 
00852     /* Add unexpanded args as macro. */
00853     addMacro(mb->mc, "*", NULL, b, mb->depth);
00854 
00855     return se;
00856 }
00857 
00865 static void
00866 doOutput(MacroBuf *mb, int waserror, const char *msg, size_t msglen)
00867 {
00868         char buf[BUFSIZ];
00869 
00870         strncpy(buf, msg, msglen);
00871         buf[msglen] = '\0';
00872         expandU(mb, buf, sizeof(buf));
00873         if (waserror)
00874                 rpmError(RPMERR_BADSPEC, "%s\n", buf);
00875         else
00876                 fprintf(stderr, "%s", buf);
00877 }
00878 
00888 static void
00889 doFoo(MacroBuf *mb, int negate, const char *f, size_t fn, const char *g, size_t glen)
00890 {
00891         char buf[BUFSIZ], *b = NULL, *be;
00892         int c;
00893 
00894         buf[0] = '\0';
00895         if (g) {
00896                 strncpy(buf, g, glen);
00897                 buf[glen] = '\0';
00898                 expandU(mb, buf, sizeof(buf));
00899         }
00900         if (STREQ("basename", f, fn)) {
00901                 if ((b = strrchr(buf, '/')) == NULL)
00902                         b = buf;
00903 #if NOTYET
00904         /* XXX watchout for conflict with %dir */
00905         } else if (STREQ("dirname", f, fn)) {
00906                 if ((b = strrchr(buf, '/')) != NULL)
00907                         *b = '\0';
00908                 b = buf;
00909 #endif
00910         } else if (STREQ("suffix", f, fn)) {
00911                 if ((b = strrchr(buf, '.')) != NULL)
00912                         b++;
00913         } else if (STREQ("expand", f, fn)) {
00914                 b = buf;
00915         } else if (STREQ("verbose", f, fn)) {
00916                 if (negate)
00917                     b = (rpmIsVerbose() ? NULL : buf);
00918                 else
00919                     b = (rpmIsVerbose() ? buf : NULL);
00920         } else if (STREQ("url2path", f, fn) || STREQ("u2p", f, fn)) {
00921                 (void)urlPath(buf, (const char **)&b);
00922                 if (*b == '\0') b = "/";
00923         } else if (STREQ("uncompress", f, fn)) {
00924                 rpmCompressedMagic compressed = COMPRESSED_OTHER;
00925                 for (b = buf; (c = *b) && isblank(c);)
00926                         b++;
00927                 for (be = b; (c = *be) && !isblank(c);)
00928                         be++;
00929                 *be++ = '\0';
00930 #ifndef DEBUG_MACROS
00931                 isCompressed(b, &compressed);
00932 #endif
00933                 switch(compressed) {
00934                 default:
00935                 case 0: /* COMPRESSED_NOT */
00936                         sprintf(be, "%%_cat %s", b);
00937                         break;
00938                 case 1: /* COMPRESSED_OTHER */
00939                         sprintf(be, "%%_gzip -dc %s", b);
00940                         break;
00941                 case 2: /* COMPRESSED_BZIP2 */
00942                         sprintf(be, "%%_bzip2 %s", b);
00943                         break;
00944                 case 3: /* COMPRESSED_ZIP */
00945                         sprintf(be, "%%_unzip %s", b);
00946                         break;
00947                 }
00948                 b = be;
00949         } else if (STREQ("S", f, fn)) {
00950                 for (b = buf; (c = *b) && isdigit(c);)
00951                         b++;
00952                 if (!c) {       /* digit index */
00953                         b++;
00954                         sprintf(b, "%%SOURCE%s", buf);
00955                 } else
00956                         b = buf;
00957         } else if (STREQ("P", f, fn)) {
00958                 for (b = buf; (c = *b) && isdigit(c);)
00959                         b++;
00960                 if (!c) {       /* digit index */
00961                         b++;
00962                         sprintf(b, "%%PATCH%s", buf);
00963                 } else
00964                         b = buf;
00965         } else if (STREQ("F", f, fn)) {
00966                 b = buf + strlen(buf) + 1;
00967                 sprintf(b, "file%s.file", buf);
00968         }
00969 
00970         if (b) {
00971                 expandT(mb, b, strlen(b));
00972         }
00973 }
00974 
00981 static int
00982 expandMacro(MacroBuf *mb)
00983 {
00984     MacroEntry **mep;
00985     MacroEntry *me;
00986     const char *s = mb->s, *se;
00987     const char *f, *fe;
00988     const char *g, *ge;
00989     size_t fn, gn;
00990     char *t = mb->t;    /* save expansion pointer for printExpand */
00991     int c;
00992     int rc = 0;
00993     int negate;
00994     char grab;
00995     int chkexist;
00996 
00997     if (++mb->depth > max_macro_depth) {
00998         rpmError(RPMERR_BADSPEC,
00999                 _("Recursion depth(%d) greater than max(%d)\n"),
01000                 mb->depth, max_macro_depth);
01001         mb->depth--;
01002         mb->expand_trace = 1;
01003         return 1;
01004     }
01005 
01006     while (rc == 0 && mb->nb > 0 && (c = *s) != '\0') {
01007         s++;
01008         /* Copy text until next macro */
01009         switch(c) {
01010         case '%':
01011                 if (*s != '%')
01012                         break;
01013                 s++;    /* skip first % in %% */
01014                 /*@fallthrough@*/
01015         default:
01016                 SAVECHAR(mb, c);
01017                 continue;
01018                 /*@notreached@*/ break;
01019         }
01020 
01021         /* Expand next macro */
01022         f = fe = NULL;
01023         g = ge = NULL;
01024         if (mb->depth > 1)      /* XXX full expansion for outermost level */
01025                 t = mb->t;      /* save expansion pointer for printExpand */
01026         negate = 0;
01027         grab = '\0';
01028         chkexist = 0;
01029         switch ((c = *s)) {
01030         default:                /* %name substitution */
01031                 while (strchr("!?", *s) != NULL) {
01032                         switch(*s++) {
01033                         case '!':
01034                                 negate = ((negate + 1) % 2);
01035                                 break;
01036                         case '?':
01037                                 chkexist++;
01038                                 break;
01039                         }
01040                 }
01041                 f = se = s;
01042                 if (*se == '-')
01043                         se++;
01044                 while((c = *se) && (isalnum(c) || c == '_'))
01045                         se++;
01046                 /* Recognize non-alnum macros too */
01047                 switch (*se) {
01048                 case '*':
01049                         se++;
01050                         if (*se == '*') se++;
01051                         break;
01052                 case '#':
01053                         se++;
01054                         break;
01055                 default:
01056                         break;
01057                 }
01058                 fe = se;
01059                 /* For "%name " macros ... */
01060                 if ((c = *fe) && isblank(c))
01061                         grab = '\n';
01062                 break;
01063         case '(':               /* %(...) shell escape */
01064                 if ((se = matchchar(s, c, ')')) == NULL) {
01065                         rpmError(RPMERR_BADSPEC,
01066                                 _("Unterminated %c: %s\n"), c, s);
01067                         rc = 1;
01068                         continue;
01069                 }
01070                 if (mb->macro_trace)
01071                         printMacro(mb, s, se+1);
01072 
01073                 s++;    /* skip ( */
01074                 rc = doShellEscape(mb, s, (se - s));
01075                 se++;   /* skip ) */
01076 
01077                 s = se;
01078                 continue;
01079                 /*@notreached@*/ break;
01080         case '{':               /* %{...}/%{...:...} substitution */
01081                 if ((se = matchchar(s, c, '}')) == NULL) {
01082                         rpmError(RPMERR_BADSPEC,
01083                                 _("Unterminated %c: %s\n"), c, s);
01084                         rc = 1;
01085                         continue;
01086                 }
01087                 f = s+1;/* skip { */
01088                 se++;   /* skip } */
01089                 while (strchr("!?", *f) != NULL) {
01090                         switch(*f++) {
01091                         case '!':
01092                                 negate = ((negate + 1) % 2);
01093                                 break;
01094                         case '?':
01095                                 chkexist++;
01096                                 break;
01097                         }
01098                 }
01099                 for (fe = f; (c = *fe) && !strchr(" :}", c);)
01100                         fe++;
01101                 switch (c) {
01102                 case ':':
01103                         g = fe + 1;
01104                         ge = se - 1;
01105                         break;
01106                 case ' ':
01107                         grab = se[-1];
01108                         break;
01109                 default:
01110                         break;
01111                 }
01112                 break;
01113         }
01114 
01115         /* XXX Everything below expects fe > f */
01116         fn = (fe - f);
01117         gn = (ge - g);
01118         if (fn <= 0) {
01119 /* XXX Process % in unknown context */
01120                 c = '%';        /* XXX only need to save % */
01121                 SAVECHAR(mb, c);
01122 #if 0
01123                 rpmError(RPMERR_BADSPEC,
01124                         _("A %% is followed by an unparseable macro\n"));
01125 #endif
01126                 s = se;
01127                 continue;
01128         }
01129 
01130         if (mb->macro_trace)
01131                 printMacro(mb, s, se);
01132 
01133         /* Expand builtin macros */
01134         if (STREQ("global", f, fn)) {
01135                 s = doDefine(mb, se, RMIL_GLOBAL, 1);
01136                 continue;
01137         }
01138         if (STREQ("define", f, fn)) {
01139                 s = doDefine(mb, se, mb->depth, 0);
01140                 continue;
01141         }
01142         if (STREQ("undefine", f, fn)) {
01143                 s = doUndefine(mb->mc, se);
01144                 continue;
01145         }
01146 
01147         if (STREQ("echo", f, fn) ||
01148             STREQ("warn", f, fn) ||
01149             STREQ("error", f, fn)) {
01150                 int waserror = 0;
01151                 if (STREQ("error", f, fn))
01152                         waserror = 1;
01153                 if (g < ge)
01154                         doOutput(mb, waserror, g, gn);
01155                 else
01156                         doOutput(mb, waserror, f, fn);
01157                 s = se;
01158                 continue;
01159         }
01160 
01161         if (STREQ("trace", f, fn)) {
01162                 /* XXX TODO restore expand_trace/macro_trace to 0 on return */
01163                 mb->expand_trace = mb->macro_trace = (negate ? 0 : mb->depth);
01164                 if (mb->depth == 1) {
01165                         print_macro_trace = mb->macro_trace;
01166                         print_expand_trace = mb->expand_trace;
01167                 }
01168                 s = se;
01169                 continue;
01170         }
01171 
01172         if (STREQ("dump", f, fn)) {
01173                 rpmDumpMacroTable(mb->mc, NULL);
01174                 while (iseol(*se))
01175                         se++;
01176                 s = se;
01177                 continue;
01178         }
01179 
01180         /* XXX necessary but clunky */
01181         if (STREQ("basename", f, fn) ||
01182             STREQ("suffix", f, fn) ||
01183             STREQ("expand", f, fn) ||
01184             STREQ("verbose", f, fn) ||
01185             STREQ("uncompress", f, fn) ||
01186             STREQ("url2path", f, fn) ||
01187             STREQ("u2p", f, fn) ||
01188             STREQ("S", f, fn) ||
01189             STREQ("P", f, fn) ||
01190             STREQ("F", f, fn)) {
01191                 doFoo(mb, negate, f, fn, g, gn);
01192                 s = se;
01193                 continue;
01194         }
01195 
01196         /* Expand defined macros */
01197         mep = findEntry(mb->mc, f, fn);
01198         me = (mep ? *mep : NULL);
01199 
01200         /* XXX Special processing for flags */
01201         if (*f == '-') {
01202                 if (me)
01203                         me->used++;     /* Mark macro as used */
01204                 if ((me == NULL && !negate) ||  /* Without -f, skip %{-f...} */
01205                     (me != NULL && negate)) {   /* With -f, skip %{!-f...} */
01206                         s = se;
01207                         continue;
01208                 }
01209 
01210                 if (g && g < ge) {              /* Expand X in %{-f:X} */
01211                         rc = expandT(mb, g, gn);
01212                 } else
01213                 if (me->body && *me->body) {    /* Expand %{-f}/%{-f*} */
01214                         rc = expandT(mb, me->body, strlen(me->body));
01215                 }
01216                 s = se;
01217                 continue;
01218         }
01219 
01220         /* XXX Special processing for macro existence */
01221         if (chkexist) {
01222                 if ((me == NULL && !negate) ||  /* Without -f, skip %{?f...} */
01223                     (me != NULL && negate)) {   /* With -f, skip %{!?f...} */
01224                         s = se;
01225                         continue;
01226                 }
01227                 if (g && g < ge) {              /* Expand X in %{?f:X} */
01228                         rc = expandT(mb, g, gn);
01229                 } else
01230                 if (me && me->body && *me->body) { /* Expand %{?f}/%{?f*} */
01231                         rc = expandT(mb, me->body, strlen(me->body));
01232                 }
01233                 s = se;
01234                 continue;
01235         }
01236         
01237         if (me == NULL) {       /* leave unknown %... as is */
01238 #ifndef HACK
01239 #if DEAD
01240                 /* XXX hack to skip over empty arg list */
01241                 if (fn == 1 && *f == '*') {
01242                         s = se;
01243                         continue;
01244                 }
01245 #endif
01246                 /* XXX hack to permit non-overloaded %foo to be passed */
01247                 c = '%';        /* XXX only need to save % */
01248                 SAVECHAR(mb, c);
01249 #else
01250                 rpmError(RPMERR_BADSPEC,
01251                         _("Macro %%%.*s not found, skipping\n"), fn, f);
01252                 s = se;
01253 #endif
01254                 continue;
01255         }
01256 
01257         /* Setup args for "%name " macros with opts */
01258         if (me && me->opts != NULL) {
01259                 if (grab) {
01260                         se = grabArgs(mb, me, fe, grab);
01261                 } else {
01262                         addMacro(mb->mc, "**", NULL, "", mb->depth);
01263                         addMacro(mb->mc, "*", NULL, "", mb->depth);
01264                         addMacro(mb->mc, "#", NULL, "0", mb->depth);
01265                         addMacro(mb->mc, "0", NULL, me->name, mb->depth);
01266                 }
01267         }
01268 
01269         /* Recursively expand body of macro */
01270         if (me->body && *me->body) {
01271                 mb->s = me->body;
01272                 rc = expandMacro(mb);
01273                 if (rc == 0)
01274                         me->used++;     /* Mark macro as used */
01275         }
01276 
01277         /* Free args for "%name " macros with opts */
01278         if (me->opts != NULL)
01279                 freeArgs(mb);
01280 
01281         s = se;
01282     }
01283 
01284     *mb->t = '\0';
01285     mb->s = s;
01286     mb->depth--;
01287     if (rc != 0 || mb->expand_trace)
01288         printExpansion(mb, t, mb->t);
01289     return rc;
01290 }
01291 
01292 /* =============================================================== */
01293 
01294 int
01295 expandMacros(void *spec, MacroContext *mc, char *s, size_t slen)
01296 {
01297         MacroBuf macrobuf, *mb = &macrobuf;
01298         char *tbuf;
01299         int rc;
01300 
01301         if (s == NULL || slen <= 0)
01302                 return 0;
01303         if (mc == NULL)
01304                 mc = &rpmGlobalMacroContext;
01305 
01306         tbuf = alloca(slen + 1);
01307         memset(tbuf, 0, (slen + 1));
01308 
01309         mb->s = s;
01310         mb->t = tbuf;
01311         mb->nb = slen;
01312         mb->depth = 0;
01313         mb->macro_trace = print_macro_trace;
01314         mb->expand_trace = print_expand_trace;
01315 
01316         mb->spec = spec;        /* (future) %file expansion info */
01317         mb->mc = mc;
01318 
01319         rc = expandMacro(mb);
01320 
01321         if (mb->nb <= 0)
01322                 rpmError(RPMERR_BADSPEC, _("Target buffer overflow\n"));
01323 
01324         tbuf[slen] = '\0';      /* XXX just in case */
01325         strncpy(s, tbuf, (slen - mb->nb + 1));
01326 
01327         return rc;
01328 }
01329 
01330 void
01331 addMacro(MacroContext *mc, const char *n, const char *o, const char *b, int level)
01332 {
01333         MacroEntry **mep;
01334 
01335         if (mc == NULL)
01336                 mc = &rpmGlobalMacroContext;
01337 
01338         /* If new name, expand macro table */
01339         if ((mep = findEntry(mc, n, 0)) == NULL) {
01340                 if (mc->firstFree == mc->macrosAllocated)
01341                         expandMacroTable(mc);
01342                 mep = mc->macroTable + mc->firstFree++;
01343         }
01344 
01345         /* Push macro over previous definition */
01346         pushMacro(mep, n, o, b, level);
01347 
01348         /* If new name, sort macro table */
01349         if ((*mep)->prev == NULL)
01350                 sortMacroTable(mc);
01351 }
01352 
01353 void
01354 delMacro(MacroContext *mc, const char *n)
01355 {
01356         MacroEntry **mep;
01357 
01358         if (mc == NULL)
01359                 mc = &rpmGlobalMacroContext;
01360         /* If name exists, pop entry */
01361         if ((mep = findEntry(mc, n, 0)) != NULL) {
01362                 popMacro(mep);
01363                 /* If deleted name, sort macro table */
01364                 if (!(mep && *mep))
01365                         sortMacroTable(mc);
01366         }
01367 }
01368 
01369 int
01370 rpmDefineMacro(MacroContext *mc, const char *macro, int level)
01371 {
01372         MacroBuf macrobuf, *mb = &macrobuf;
01373 
01374         memset(mb, 0, sizeof(*mb));
01375         /* XXX just enough to get by */
01376         mb->mc = (mc ? mc : &rpmGlobalMacroContext);
01377         (void)doDefine(mb, macro, level, 0);
01378         return 0;
01379 }
01380 
01381 void
01382 rpmLoadMacros(MacroContext * mc, int level)
01383 {
01384         int i;
01385 
01386         if (mc == NULL || mc == &rpmGlobalMacroContext)
01387                 return;
01388 
01389         for (i = 0; i < mc->firstFree; i++) {
01390                 MacroEntry **mep, *me;
01391                 mep = &mc->macroTable[i];
01392                 me = *mep;
01393 
01394                 if (me == NULL)         /* XXX this should never happen */
01395                         continue;
01396                 addMacro(NULL, me->name, me->opts, me->body, (level - 1));
01397         }
01398 }
01399 
01400 void
01401 rpmInitMacros(MacroContext *mc, const char *macrofiles)
01402 {
01403         char *m, *mfile, *me;
01404 
01405         if (macrofiles == NULL)
01406                 return;
01407         if (mc == NULL)
01408                 mc = &rpmGlobalMacroContext;
01409 
01410         for (mfile = m = xstrdup(macrofiles); *mfile; mfile = me) {
01411                 FD_t fd;
01412                 char buf[BUFSIZ];
01413 
01414                 for (me = mfile; (me = strchr(me, ':')) != NULL; me++) {
01415                         if (!(me[1] == '/' && me[2] == '/'))
01416                                 break;
01417                 }
01418 
01419                 if (me && *me == ':')
01420                         *me++ = '\0';
01421                 else
01422                         me = mfile + strlen(mfile);
01423 
01424                 /* Expand ~/ to $HOME */
01425                 buf[0] = '\0';
01426                 if (mfile[0] == '~' && mfile[1] == '/') {
01427                         char *home;
01428                         if ((home = getenv("HOME")) != NULL) {
01429                                 mfile += 2;
01430                                 strncpy(buf, home, sizeof(buf));
01431                                 strncat(buf, "/", sizeof(buf) - strlen(buf));
01432                         }
01433                 }
01434                 strncat(buf, mfile, sizeof(buf) - strlen(buf));
01435                 buf[sizeof(buf)-1] = '\0';
01436 
01437                 fd = Fopen(buf, "r.fpio");
01438                 if (fd == NULL || Ferror(fd)) {
01439                         if (fd) Fclose(fd);
01440                         continue;
01441                 }
01442 
01443                 /* XXX Assume new fangled macro expansion */
01444                 max_macro_depth = 16;
01445 
01446                 while(rdcl(buf, sizeof(buf), fd, 1) != NULL) {
01447                         char c, *n;
01448 
01449                         n = buf;
01450                         SKIPBLANK(n, c);
01451 
01452                         if (c != '%')
01453                                 continue;
01454                         n++;    /* skip % */
01455                         (void)rpmDefineMacro(NULL, n, RMIL_MACROFILES);
01456                 }
01457                 Fclose(fd);
01458         }
01459         if (m)
01460                 free(m);
01461 
01462         /* Reload cmdline macros */
01463         rpmLoadMacros(&rpmCLIMacroContext, RMIL_CMDLINE);
01464 }
01465 
01466 void
01467 rpmFreeMacros(MacroContext *mc)
01468 {
01469         int i;
01470     
01471         if (mc == NULL)
01472                 mc = &rpmGlobalMacroContext;
01473 
01474         for (i = 0; i < mc->firstFree; i++) {
01475                 MacroEntry *me;
01476                 while ((me = mc->macroTable[i]) != NULL) {
01477                         /* XXX cast to workaround const */
01478                         if ((mc->macroTable[i] = me->prev) == NULL)
01479                                 FREE(me->name);
01480                         FREE(me->opts);
01481                         FREE(me->body);
01482                         FREE(me);
01483                 }
01484         }
01485         FREE(mc->macroTable);
01486         memset(mc, 0, sizeof(*mc));
01487 }
01488 
01489 /* =============================================================== */
01490 int isCompressed(const char *file, rpmCompressedMagic *compressed)
01491 {
01492     FD_t fd;
01493     ssize_t nb;
01494     int rc = -1;
01495     unsigned char magic[4];
01496 
01497     *compressed = COMPRESSED_NOT;
01498 
01499     fd = Fopen(file, "r.ufdio");
01500     if (fd == NULL || Ferror(fd)) {
01501         /* XXX Fstrerror */
01502         rpmError(RPMERR_BADSPEC, _("File %s: %s\n"), file, Fstrerror(fd));
01503         if (fd) Fclose(fd);
01504         return 1;
01505     }
01506     nb = Fread(magic, sizeof(char), sizeof(magic), fd);
01507     if (nb < 0) {
01508         rpmError(RPMERR_BADSPEC, _("File %s: %s\n"), file, Fstrerror(fd));
01509         rc = 1;
01510     } else if (nb < sizeof(magic)) {
01511         rpmError(RPMERR_BADSPEC, _("File %s is smaller than %u bytes\n"),
01512                 file, (unsigned)sizeof(magic));
01513         rc = 0;
01514     }
01515     Fclose(fd);
01516     if (rc >= 0)
01517         return rc;
01518 
01519     rc = 0;
01520 
01521     if ((magic[0] == 'B') && (magic[1] == 'Z')) {
01522         *compressed = COMPRESSED_BZIP2;
01523     } else if ((magic[0] == 0120) && (magic[1] == 0113) &&
01524          (magic[2] == 0003) && (magic[3] == 0004)) {    /* pkzip */
01525         *compressed = COMPRESSED_ZIP;
01526     } else if (((magic[0] == 0037) && (magic[1] == 0213)) || /* gzip */
01527         ((magic[0] == 0037) && (magic[1] == 0236)) ||   /* old gzip */
01528         ((magic[0] == 0037) && (magic[1] == 0036)) ||   /* pack */
01529         ((magic[0] == 0037) && (magic[1] == 0240)) ||   /* SCO lzh */
01530         ((magic[0] == 0037) && (magic[1] == 0235))      /* compress */
01531         ) {
01532         *compressed = COMPRESSED_OTHER;
01533     }
01534 
01535     return rc;
01536 }
01537 
01538 /* =============================================================== */
01539 
01540 char * 
01541 rpmExpand(const char *arg, ...)
01542 {
01543     char buf[BUFSIZ], *p, *pe;
01544     const char *s;
01545     va_list ap;
01546 
01547     if (arg == NULL)
01548         return xstrdup("");
01549 
01550     buf[0] = '\0';
01551     p = buf;
01552     pe = stpcpy(p, arg);
01553 
01554     va_start(ap, arg);
01555     while ((s = va_arg(ap, const char *)) != NULL)
01556         pe = stpcpy(pe, s);
01557     va_end(ap);
01558     expandMacros(NULL, NULL, buf, sizeof(buf));
01559     return xstrdup(buf);
01560 }
01561 
01562 int
01563 rpmExpandNumeric(const char *arg)
01564 {
01565     const char *val;
01566     int rc;
01567 
01568     if (arg == NULL)
01569         return 0;
01570 
01571     val = rpmExpand(arg, NULL);
01572     if (!(val && *val != '%'))
01573         rc = 0;
01574     else if (*val == 'Y' || *val == 'y')
01575         rc = 1;
01576     else if (*val == 'N' || *val == 'n')
01577         rc = 0;
01578     else {
01579         char *end;
01580         rc = strtol(val, &end, 0);
01581         if (!(end && *end == '\0'))
01582             rc = 0;
01583     }
01584     free((void *)val);
01585 
01586     return rc;
01587 }
01588 
01589 /* @todo "../sbin/./../bin/" not correct. */
01590 char *rpmCleanPath(char * path)
01591 {
01592     const char *s;
01593     char *se, *t, *te;
01594     int begin = 1;
01595 
01596 /*fprintf(stderr, "*** RCP %s ->\n", path); */
01597     s = t = te = path;
01598     while (*s) {
01599 /*fprintf(stderr, "*** got \"%.*s\"\trest \"%s\"\n", (t-path), path, s); */
01600         switch(*s) {
01601         case ':':                       /* handle url's */
01602             if (s[1] == '/' && s[2] == '/') {
01603                 *t++ = *s++;
01604                 *t++ = *s++;
01605                 break;
01606             }
01607             begin=1;
01608             break;
01609         case '/':
01610             /* Move parent dir forward */
01611             for (se = te + 1; se < t && *se != '/'; se++)
01612                 ;
01613             if (se < t && *se == '/') {
01614                 te = se;
01615 /*fprintf(stderr, "*** next pdir \"%.*s\"\n", (te-path), path); */
01616             }
01617             while (s[1] == '/')
01618                 s++;
01619             while (t > path && t[-1] == '/')
01620                 t--;
01621             break;
01622         case '.':
01623             /* Leading .. is special */
01624             if (begin && s[1] == '.') {
01625 /*fprintf(stderr, "    leading \"..\"\n"); */
01626                 *t++ = *s++;
01627                 break;
01628             }
01629             /* Single . is special */
01630             if (begin && s[1] == '\0') {
01631                 break;
01632             }
01633             /* Trim embedded ./ , trailing /. */
01634             if ((t[-1] == '/' && s[1] == '\0') || (t != path && s[1] == '/')) {
01635                 s++;
01636                 continue;
01637             }
01638             /* Trim embedded /../ and trailing /.. */
01639             if (!begin && t > path && t[-1] == '/' && s[1] == '.' && (s[2] == '/' || s[2] == '\0')) {
01640                 t = te;
01641                 /* Move parent dir forward */
01642                 if (te > path)
01643                     for (--te; te > path && *te != '/'; te--)
01644                         ;
01645 /*fprintf(stderr, "*** prev pdir \"%.*s\"\n", (te-path), path); */
01646                 s++;
01647                 s++;
01648                 continue;
01649             }
01650             break;
01651         default:
01652             begin = 0;
01653             break;
01654         }
01655         *t++ = *s++;
01656     }
01657 
01658     /* Trim trailing / (but leave single / alone) */
01659     if (t > &path[1] && t[-1] == '/')
01660         t--;
01661     *t = '\0';
01662 
01663 /*fprintf(stderr, "\t%s\n", path); */
01664     return path;
01665 }
01666 
01667 /* Return concatenated and expanded canonical path. */
01668 
01669 const char *
01670 rpmGetPath(const char *path, ...)
01671 {
01672     char buf[BUFSIZ];
01673     const char * s;
01674     char * t, * te;
01675     va_list ap;
01676 
01677     if (path == NULL)
01678         return xstrdup("");
01679 
01680     buf[0] = '\0';
01681     t = buf;
01682     te = stpcpy(t, path);
01683     *te = '\0';
01684 
01685     va_start(ap, path);
01686     while ((s = va_arg(ap, const char *)) != NULL) {
01687         te = stpcpy(te, s);
01688         *te = '\0';
01689     }
01690     va_end(ap);
01691     expandMacros(NULL, NULL, buf, sizeof(buf));
01692 
01693     (void) rpmCleanPath(buf);
01694     return xstrdup(buf);        /* XXX xstrdup has side effects. */
01695 }
01696 
01697 /* Merge 3 args into path, any or all of which may be a url. */
01698 
01699 const char * rpmGenPath(const char * urlroot, const char * urlmdir,
01700                 const char *urlfile)
01701 {
01702 /*@owned@*/ const char * xroot = rpmGetPath(urlroot, NULL);
01703 /*@dependent@*/ const char * root = xroot;
01704 /*@owned@*/ const char * xmdir = rpmGetPath(urlmdir, NULL);
01705 /*@dependent@*/ const char * mdir = xmdir;
01706 /*@owned@*/ const char * xfile = rpmGetPath(urlfile, NULL);
01707 /*@dependent@*/ const char * file = xfile;
01708     const char * result;
01709     const char * url = NULL;
01710     int nurl = 0;
01711     int ut;
01712 
01713 if (_debug)
01714 fprintf(stderr, "*** RGP xroot %s xmdir %s xfile %s\n", xroot, xmdir, xfile);
01715     ut = urlPath(xroot, &root);
01716     if (url == NULL && ut > URL_IS_DASH) {
01717         url = xroot;
01718         nurl = root - xroot;
01719 if (_debug)
01720 fprintf(stderr, "*** RGP ut %d root %s nurl %d\n", ut, root, nurl);
01721     }
01722     if (root == NULL || *root == '\0') root = "/";
01723 
01724     ut = urlPath(xmdir, &mdir);
01725     if (url == NULL && ut > URL_IS_DASH) {
01726         url = xmdir;
01727         nurl = mdir - xmdir;
01728 if (_debug)
01729 fprintf(stderr, "*** RGP ut %d mdir %s nurl %d\n", ut, mdir, nurl);
01730     }
01731     if (mdir == NULL || *mdir == '\0') mdir = "/";
01732 
01733     ut = urlPath(xfile, &file);
01734     if (url == NULL && ut > URL_IS_DASH) {
01735         url = xfile;
01736         nurl = file - xfile;
01737 if (_debug)
01738 fprintf(stderr, "*** RGP ut %d file %s nurl %d\n", ut, file, nurl);
01739     }
01740 
01741     if (url && nurl > 0) {
01742         char *t = strncpy(alloca(nurl+1), url, nurl);
01743         t[nurl] = '\0';
01744         url = t;
01745     } else
01746         url = "";
01747 
01748     result = rpmGetPath(url, root, "/", mdir, "/", file, NULL);
01749 
01750     free((void *)xroot);
01751     free((void *)xmdir);
01752     free((void *)xfile);
01753 if (_debug)
01754 fprintf(stderr, "*** RGP result %s\n", result);
01755     return result;
01756 }
01757 
01758 /* =============================================================== */
01759 
01760 #if defined(DEBUG_MACROS)
01761 
01762 #if defined(EVAL_MACROS)
01763 
01764 char *macrofiles = "/usr/lib/rpm/macros:/etc/rpm/macros:~/.rpmmacros";
01765 
01766 int
01767 main(int argc, char *argv[])
01768 {
01769         int c;
01770         int errflg = 0;
01771         extern char *optarg;
01772         extern int optind;
01773 
01774         while ((c = getopt(argc, argv, "f:")) != EOF ) {
01775             switch (c) {
01776             case 'f':
01777                 macrofiles = optarg;
01778                 break;
01779             case '?':
01780             default:
01781                 errflg++;
01782                 break;
01783             }
01784         }
01785         if (errflg || optind >= argc) {
01786             fprintf(stderr, "Usage: %s [-f macropath ] macro ...\n", argv[0]);
01787             exit(1);
01788         }
01789 
01790         rpmInitMacros(NULL, macrofiles);
01791         for ( ; optind < argc; optind++) {
01792             const char *val;
01793 
01794             val = rpmGetPath(argv[optind], NULL);
01795             if (val) {
01796                 fprintf(stdout, "%s:\t%s\n", argv[optind], val);
01797                 free((void *)val);
01798             }
01799         }
01800         rpmFreeMacros(NULL);
01801         return 0;
01802 }
01803 
01804 #else   /* !EVAL_MACROS */
01805 
01806 char *macrofiles = "../macros:./testmacros";
01807 char *testfile = "./test";
01808 
01809 int
01810 main(int argc, char *argv[])
01811 {
01812         char buf[BUFSIZ];
01813         FILE *fp;
01814         int x;
01815 
01816         rpmInitMacros(NULL, macrofiles);
01817         rpmDumpMacroTable(NULL, NULL);
01818 
01819         if ((fp = fopen(testfile, "r")) != NULL) {
01820                 while(rdcl(buf, sizeof(buf), fp, 1)) {
01821                         x = expandMacros(NULL, NULL, buf, sizeof(buf));
01822                         fprintf(stderr, "%d->%s\n", x, buf);
01823                         memset(buf, 0, sizeof(buf));
01824                 }
01825                 fclose(fp);
01826         }
01827 
01828         while(rdcl(buf, sizeof(buf), stdin, 1)) {
01829                 x = expandMacros(NULL, NULL, buf, sizeof(buf));
01830                 fprintf(stderr, "%d->%s\n <-\n", x, buf);
01831                 memset(buf, 0, sizeof(buf));
01832         }
01833         rpmFreeMacros(NULL);
01834 
01835         return 0;
01836 }
01837 #endif  /* EVAL_MACROS */
01838 #endif  /* DEBUG_MACROS */

Generated at Mon May 21 08:53:40 2001 for rpm by doxygen1.2.6 written by Dimitri van Heesch, © 1997-2001