rpm 5.2.1
|
00001 00005 #include "system.h" 00006 #include <stdarg.h> 00007 00008 #if !defined(isblank) 00009 #define isblank(_c) ((char)(_c) == ' ' || (char)(_c) == '\t') 00010 #endif 00011 #define iseol(_c) ((char)(_c) == '\n' || (char)(_c) == '\r') 00012 00013 #define STREQ(_t, _f, _fn) ((_fn) == (sizeof(_t)-1) && !strncmp((_t), (_f), (_fn))) 00014 00015 #ifdef DEBUG_MACROS 00016 #undef WITH_LUA /* XXX fixme */ 00017 #include <sys/types.h> 00018 #include <errno.h> 00019 #include <fcntl.h> 00020 #include <getopt.h> 00021 #include <stdio.h> 00022 #include <stdlib.h> 00023 #include <string.h> 00024 #include <ctype.h> 00025 #include <popt.h> 00026 00027 #define rpmlog fprintf 00028 #define RPMLOG_ERR stderr 00029 #define RPMLOG_WARNING stderr 00030 #undef _ 00031 #define _(x) x 00032 00033 #define vmefail(_nb) (exit(1), NULL) 00034 #define URL_IS_DASH 1 00035 #define URL_IS_PATH 2 00036 #define urlPath(_xr, _r) (*(_r) = (_xr), URL_IS_PATH) 00037 #define xisalnum(_c) isalnum(_c) 00038 #define xisalpha(_c) isalpha(_c) 00039 #define xisdigit(_c) isdigit(_c) 00040 #define xisspace(_c) isspace(_c) 00041 00042 typedef FILE * FD_t; 00043 #define Fopen(_path, _fmode) fopen(_path, "r"); 00044 #define Ferror ferror 00045 #define Fstrerror(_fd) strerror(errno) 00046 #define Fread fread 00047 #define Fclose fclose 00048 00049 #define fdGetFILE(_fd) (_fd) 00050 00051 /*@unused@*/ static inline /*@null@*/ void * 00052 _free(/*@only@*/ /*@null@*/ const void * p) 00053 /*@modifies p@*/ 00054 { 00055 if (p != NULL) free((void *)p); 00056 return NULL; 00057 } 00058 00059 #else 00060 00061 /*@observer@*/ /*@checked@*/ 00062 const char * rpmMacrofiles = MACROFILES; 00063 00064 #include <rpmio_internal.h> 00065 #include <rpmlog.h> 00066 #include <mire.h> 00067 00068 #ifdef WITH_LUA 00069 #define _RPMLUA_INTERNAL /* XXX lua->printbuf access */ 00070 #include <rpmlua.h> 00071 #endif 00072 00073 #include <rpmficl.h> 00074 #include <rpmjs.h> 00075 #include <rpmperl.h> 00076 #include <rpmpython.h> 00077 #include <rpmruby.h> 00078 #include <rpmtcl.h> 00079 00080 #endif 00081 00082 #include <rpmuuid.h> 00083 00084 #define _MACRO_INTERNAL 00085 #include <rpmmacro.h> 00086 00087 #include "debug.h" 00088 00089 /*@unchecked@*/ 00090 #if defined(WITH_FICL) || defined(WITH_JS) || defined(WITH_PERLEMBED) || defined(WITH_PYTHONEMBED) || defined(WITH_RUBYEMBED) || defined(WITH_TCL) 00091 static int _globalI = 1; 00092 #endif 00093 00094 #if defined(__LCLINT__) 00095 /*@-exportheader@*/ 00096 extern const unsigned short int **__ctype_b_loc (void) /*@*/; 00097 /*@=exportheader@*/ 00098 #endif 00099 00100 /*@access FD_t @*/ /* XXX compared with NULL */ 00101 /*@access miRE @*/ /* XXX cast */ 00102 /*@access MacroContext @*/ 00103 /*@access MacroEntry@ */ 00104 /*@access rpmlua @*/ 00105 /*@access rpmtcl @*/ 00106 00107 static struct MacroContext_s rpmGlobalMacroContext_s; 00108 /*@-compmempass@*/ 00109 MacroContext rpmGlobalMacroContext = &rpmGlobalMacroContext_s; 00110 /*@=compmempass@*/ 00111 00112 static struct MacroContext_s rpmCLIMacroContext_s; 00113 /*@-compmempass@*/ 00114 MacroContext rpmCLIMacroContext = &rpmCLIMacroContext_s; 00115 /*@=compmempass@*/ 00116 00120 typedef /*@abstract@*/ struct MacroBuf_s { 00121 /*@kept@*/ /*@exposed@*/ 00122 const char * s; 00123 /*@shared@*/ 00124 char * t; 00125 size_t nb; 00126 int depth; 00127 int macro_trace; 00128 int expand_trace; 00129 /*@kept@*/ /*@exposed@*/ /*@null@*/ 00130 void * spec; 00131 /*@kept@*/ /*@exposed@*/ 00132 MacroContext mc; 00133 } * MacroBuf; 00134 00135 #define SAVECHAR(_mb, _c) { *(_mb)->t = (char) (_c), (_mb)->t++, (_mb)->nb--; } 00136 00137 /*@-exportlocal -exportheadervar@*/ 00138 00139 #define _MAX_MACRO_DEPTH 16 00140 /*@unchecked@*/ 00141 int max_macro_depth = _MAX_MACRO_DEPTH; 00142 00143 #define _PRINT_MACRO_TRACE 0 00144 /*@unchecked@*/ 00145 int print_macro_trace = _PRINT_MACRO_TRACE; 00146 00147 #define _PRINT_EXPAND_TRACE 0 00148 /*@unchecked@*/ 00149 int print_expand_trace = _PRINT_EXPAND_TRACE; 00150 /*@=exportlocal =exportheadervar@*/ 00151 00152 #define MACRO_CHUNK_SIZE 16 00153 00154 /* Size of expansion buffers. */ 00155 /*@unchecked@*/ 00156 static size_t _macro_BUFSIZ = 16 * 1024; 00157 00158 /* forward ref */ 00159 static int expandMacro(MacroBuf mb) 00160 /*@globals rpmGlobalMacroContext, 00161 print_macro_trace, print_expand_trace, h_errno, 00162 fileSystem, internalState @*/ 00163 /*@modifies mb, rpmGlobalMacroContext, 00164 print_macro_trace, print_expand_trace, 00165 fileSystem, internalState @*/; 00166 00167 /* =============================================================== */ 00168 00175 static int 00176 compareMacroName(const void * ap, const void * bp) 00177 /*@*/ 00178 { 00179 MacroEntry ame = *((MacroEntry *)ap); 00180 MacroEntry bme = *((MacroEntry *)bp); 00181 00182 if (ame == NULL && bme == NULL) 00183 return 0; 00184 if (ame == NULL) 00185 return 1; 00186 if (bme == NULL) 00187 return -1; 00188 return strcmp(ame->name, bme->name); 00189 } 00190 00195 static void 00196 expandMacroTable(MacroContext mc) 00197 /*@modifies mc @*/ 00198 { 00199 if (mc->macroTable == NULL) { 00200 mc->macrosAllocated = MACRO_CHUNK_SIZE; 00201 mc->macroTable = (MacroEntry *) 00202 xmalloc(sizeof(*(mc->macroTable)) * mc->macrosAllocated); 00203 mc->firstFree = 0; 00204 } else { 00205 mc->macrosAllocated += MACRO_CHUNK_SIZE; 00206 mc->macroTable = (MacroEntry *) 00207 xrealloc(mc->macroTable, sizeof(*(mc->macroTable)) * 00208 mc->macrosAllocated); 00209 } 00210 memset(&mc->macroTable[mc->firstFree], 0, MACRO_CHUNK_SIZE * sizeof(*(mc->macroTable))); 00211 } 00212 00217 static void 00218 sortMacroTable(MacroContext mc) 00219 /*@modifies mc @*/ 00220 { 00221 int i; 00222 00223 if (mc == NULL || mc->macroTable == NULL) 00224 return; 00225 00226 qsort(mc->macroTable, mc->firstFree, sizeof(mc->macroTable[0]), 00227 compareMacroName); 00228 00229 /* Empty pointers are now at end of table. Reset first free index. */ 00230 for (i = 0; i < mc->firstFree; i++) { 00231 if (mc->macroTable[i] != NULL) 00232 continue; 00233 mc->firstFree = i; 00234 break; 00235 } 00236 } 00237 00238 #if !defined(DEBUG_MACROS) 00239 /*@only@*/ 00240 static char * dupMacroEntry(MacroEntry me) 00241 /*@*/ 00242 { 00243 char * t, * te; 00244 size_t nb; 00245 00246 assert(me != NULL); 00247 nb = strlen(me->name) + sizeof("%") - 1; 00248 if (me->opts) 00249 nb += strlen(me->opts) + sizeof("()") - 1; 00250 if (me->body) 00251 nb += strlen(me->body) + sizeof("\t") - 1; 00252 nb++; 00253 00254 t = te = xmalloc(nb); 00255 *te = '\0'; 00256 te = stpcpy( stpcpy(te, "%"), me->name); 00257 if (me->opts) 00258 te = stpcpy( stpcpy( stpcpy(te, "("), me->opts), ")"); 00259 if (me->body) 00260 te = stpcpy( stpcpy(te, "\t"), me->body); 00261 *te = '\0'; 00262 00263 return t; 00264 } 00265 #endif 00266 00267 void 00268 rpmDumpMacroTable(MacroContext mc, FILE * fp) 00269 { 00270 int nempty = 0; 00271 int nactive = 0; 00272 00273 if (mc == NULL) mc = rpmGlobalMacroContext; 00274 if (fp == NULL) fp = stderr; 00275 00276 fprintf(fp, "========================\n"); 00277 if (mc->macroTable != NULL) { 00278 int i; 00279 for (i = 0; i < mc->firstFree; i++) { 00280 MacroEntry me; 00281 if ((me = mc->macroTable[i]) == NULL) { 00282 /* XXX this should never happen */ 00283 nempty++; 00284 continue; 00285 } 00286 fprintf(fp, "%3d%c %s", me->level, 00287 (me->used > 0 ? '=' : ':'), me->name); 00288 if (me->opts && *me->opts) 00289 fprintf(fp, "(%s)", me->opts); 00290 if (me->body && *me->body) 00291 fprintf(fp, "\t%s", me->body); 00292 fprintf(fp, "\n"); 00293 nactive++; 00294 } 00295 } 00296 fprintf(fp, _("======================== active %d empty %d\n"), 00297 nactive, nempty); 00298 } 00299 00300 #if !defined(DEBUG_MACROS) 00301 int 00302 rpmGetMacroEntries(MacroContext mc, void * _mire, int used, 00303 const char *** avp) 00304 { 00305 /*@-assignexpose -castexpose @*/ 00306 miRE mire = (miRE) _mire; 00307 /*@=assignexpose =castexpose @*/ 00308 const char ** av; 00309 int ac = 0; 00310 int i; 00311 00312 if (mc == NULL) 00313 mc = rpmGlobalMacroContext; 00314 00315 if (avp == NULL) 00316 return mc->firstFree; 00317 00318 av = xcalloc( (mc->firstFree+1), sizeof(mc->macroTable[0])); 00319 if (mc->macroTable != NULL) 00320 for (i = 0; i < mc->firstFree; i++) { 00321 MacroEntry me; 00322 me = mc->macroTable[i]; 00323 if (used > 0 && me->used < used) 00324 continue; 00325 if (used == 0 && me->used != 0) 00326 continue; 00327 #if !defined(DEBUG_MACROS) /* XXX preserve standalone build */ 00328 if (mire != NULL && mireRegexec(mire, me->name, 0) < 0) 00329 continue; 00330 #endif 00331 av[ac++] = dupMacroEntry(me); 00332 } 00333 av[ac] = NULL; 00334 *avp = av = xrealloc(av, (ac+1) * sizeof(*av)); 00335 00336 return ac; 00337 } 00338 #endif 00339 00347 /*@dependent@*/ /*@null@*/ 00348 static MacroEntry * 00349 findEntry(MacroContext mc, const char * name, size_t namelen) 00350 /*@*/ 00351 { 00352 MacroEntry key, *ret; 00353 00354 /*@-globs@*/ 00355 if (mc == NULL) mc = rpmGlobalMacroContext; 00356 /*@=globs@*/ 00357 if (mc->macroTable == NULL || mc->firstFree == 0) 00358 return NULL; 00359 00360 if (namelen > 0) { 00361 char * t = strncpy(alloca(namelen + 1), name, namelen); 00362 t[namelen] = '\0'; 00363 name = t; 00364 } 00365 00366 key = memset(alloca(sizeof(*key)), 0, sizeof(*key)); 00367 /*@-temptrans -assignexpose@*/ 00368 key->name = (char *)name; 00369 /*@=temptrans =assignexpose@*/ 00370 ret = (MacroEntry *) bsearch(&key, mc->macroTable, mc->firstFree, 00371 sizeof(*(mc->macroTable)), compareMacroName); 00372 /* XXX TODO: find 1st empty slot and return that */ 00373 return ret; 00374 } 00375 00376 /* =============================================================== */ 00377 00385 /*@null@*/ 00386 static char * 00387 rdcl(/*@returned@*/ char * buf, size_t size, FD_t fd) 00388 /*@globals fileSystem @*/ 00389 /*@modifies buf, fileSystem @*/ 00390 { 00391 char *q = buf - 1; /* initialize just before buffer. */ 00392 size_t nb = 0; 00393 size_t nread = 0; 00394 FILE * f = fdGetFILE(fd); 00395 int pc = 0, bc = 0; 00396 char *p = buf; 00397 00398 if (f != NULL) 00399 do { 00400 *(++q) = '\0'; /* terminate and move forward. */ 00401 if (fgets(q, (int)size, f) == NULL) /* read next line. */ 00402 break; 00403 nb = strlen(q); 00404 nread += nb; /* trim trailing \r and \n */ 00405 for (q += nb - 1; nb > 0 && iseol(*q); q--) 00406 nb--; 00407 for (; p <= q; p++) { 00408 switch (*p) { 00409 case '\\': 00410 switch (*(p+1)) { 00411 case '\r': /*@switchbreak@*/ break; 00412 case '\n': /*@switchbreak@*/ break; 00413 case '\0': /*@switchbreak@*/ break; 00414 default: p++; /*@switchbreak@*/ break; 00415 } 00416 /*@switchbreak@*/ break; 00417 case '%': 00418 switch (*(p+1)) { 00419 case '{': p++, bc++; /*@switchbreak@*/ break; 00420 case '(': p++, pc++; /*@switchbreak@*/ break; 00421 case '%': p++; /*@switchbreak@*/ break; 00422 } 00423 /*@switchbreak@*/ break; 00424 case '{': if (bc > 0) bc++; /*@switchbreak@*/ break; 00425 case '}': if (bc > 0) bc--; /*@switchbreak@*/ break; 00426 case '(': if (pc > 0) pc++; /*@switchbreak@*/ break; 00427 case ')': if (pc > 0) pc--; /*@switchbreak@*/ break; 00428 } 00429 } 00430 if (nb == 0 || (*q != '\\' && !bc && !pc) || *(q+1) == '\0') { 00431 *(++q) = '\0'; /* trim trailing \r, \n */ 00432 break; 00433 } 00434 q++; p++; nb++; /* copy newline too */ 00435 size -= nb; 00436 if (*q == '\r') /* XXX avoid \r madness */ 00437 *q = '\n'; 00438 } while (size > 0); 00439 return (nread > 0 ? buf : NULL); 00440 } 00441 00449 /*@null@*/ 00450 static const char * 00451 matchchar(const char * p, char pl, char pr) 00452 /*@*/ 00453 { 00454 int lvl = 0; 00455 char c; 00456 00457 while ((c = *p++) != '\0') { 00458 if (c == '\\') { /* Ignore escaped chars */ 00459 p++; 00460 continue; 00461 } 00462 if (c == pr) { 00463 if (--lvl <= 0) return --p; 00464 } else if (c == pl) 00465 lvl++; 00466 } 00467 return (const char *)NULL; 00468 } 00469 00476 static void 00477 printMacro(MacroBuf mb, const char * s, const char * se) 00478 /*@globals fileSystem @*/ 00479 /*@modifies fileSystem @*/ 00480 { 00481 const char *senl; 00482 const char *ellipsis; 00483 int choplen; 00484 00485 if (s >= se) { /* XXX just in case */ 00486 fprintf(stderr, _("%3d>%*s(empty)"), mb->depth, 00487 (2 * mb->depth + 1), ""); 00488 return; 00489 } 00490 00491 if (s[-1] == '{') 00492 s--; 00493 00494 /* Print only to first end-of-line (or end-of-string). */ 00495 for (senl = se; *senl && !iseol(*senl); senl++) 00496 {}; 00497 00498 /* Limit trailing non-trace output */ 00499 choplen = 61 - (2 * mb->depth); 00500 if ((senl - s) > choplen) { 00501 senl = s + choplen; 00502 ellipsis = "..."; 00503 } else 00504 ellipsis = ""; 00505 00506 /* Substitute caret at end-of-macro position */ 00507 fprintf(stderr, "%3d>%*s%%%.*s^", mb->depth, 00508 (2 * mb->depth + 1), "", (int)(se - s), s); 00509 if (se[1] != '\0' && (senl - (se+1)) > 0) 00510 fprintf(stderr, "%-.*s%s", (int)(senl - (se+1)), se+1, ellipsis); 00511 fprintf(stderr, "\n"); 00512 } 00513 00520 static void 00521 printExpansion(MacroBuf mb, const char * t, const char * te) 00522 /*@globals fileSystem @*/ 00523 /*@modifies fileSystem @*/ 00524 { 00525 const char *ellipsis; 00526 int choplen; 00527 00528 if (!(te > t)) { 00529 fprintf(stderr, _("%3d<%*s(empty)\n"), mb->depth, (2 * mb->depth + 1), ""); 00530 return; 00531 } 00532 00533 /* Shorten output which contains newlines */ 00534 while (te > t && iseol(te[-1])) 00535 te--; 00536 ellipsis = ""; 00537 if (mb->depth > 0) { 00538 const char *tenl; 00539 00540 /* Skip to last line of expansion */ 00541 while ((tenl = strchr(t, '\n')) && tenl < te) 00542 t = ++tenl; 00543 00544 /* Limit expand output */ 00545 choplen = 61 - (2 * mb->depth); 00546 if ((te - t) > choplen) { 00547 te = t + choplen; 00548 ellipsis = "..."; 00549 } 00550 } 00551 00552 fprintf(stderr, "%3d<%*s", mb->depth, (2 * mb->depth + 1), ""); 00553 if (te > t) 00554 fprintf(stderr, "%.*s%s", (int)(te - t), t, ellipsis); 00555 fprintf(stderr, "\n"); 00556 } 00557 00558 #define SKIPBLANK(_s, _c) \ 00559 /*@-globs@*/ /* FIX: __ctype_b */ \ 00560 while (((_c) = (int) *(_s)) && isblank(_c)) \ 00561 (_s)++; \ 00562 /*@=globs@*/ 00563 00564 #define SKIPNONBLANK(_s, _c) \ 00565 /*@-globs@*/ /* FIX: __ctype_b */ \ 00566 while (((_c) = (int) *(_s)) && !(isblank(_c) || iseol(_c))) \ 00567 (_s)++; \ 00568 /*@=globs@*/ 00569 00570 #define COPYNAME(_ne, _s, _c) \ 00571 { SKIPBLANK(_s,_c); \ 00572 while(((_c) = (int) *(_s)) && (xisalnum(_c) || (_c) == (int) '_')) \ 00573 *(_ne)++ = *(_s)++; \ 00574 *(_ne) = '\0'; \ 00575 } 00576 00577 #define COPYOPTS(_oe, _s, _c) \ 00578 { while(((_c) = (int) *(_s)) && (_c) != (int) ')') \ 00579 *(_oe)++ = *(_s)++; \ 00580 *(_oe) = '\0'; \ 00581 } 00582 00590 static int 00591 expandT(MacroBuf mb, const char * f, size_t flen) 00592 /*@globals rpmGlobalMacroContext, h_errno, fileSystem, internalState @*/ 00593 /*@modifies mb, rpmGlobalMacroContext, fileSystem, internalState @*/ 00594 { 00595 char *sbuf; 00596 const char *s = mb->s; 00597 int rc; 00598 00599 sbuf = alloca(flen + 1); 00600 memset(sbuf, 0, (flen + 1)); 00601 00602 strncpy(sbuf, f, flen); 00603 sbuf[flen] = '\0'; 00604 mb->s = sbuf; 00605 rc = expandMacro(mb); 00606 mb->s = s; 00607 return rc; 00608 } 00609 00610 #if 0 00611 00618 static int 00619 expandS(MacroBuf mb, char * tbuf, size_t tbuflen) 00620 /*@globals rpmGlobalMacroContext, fileSystem, internalState @*/ 00621 /*@modifies mb, *tbuf, rpmGlobalMacroContext, fileSystem, internalState @*/ 00622 { 00623 const char *t = mb->t; 00624 size_t nb = mb->nb; 00625 int rc; 00626 00627 mb->t = tbuf; 00628 mb->nb = tbuflen; 00629 rc = expandMacro(mb); 00630 mb->t = t; 00631 mb->nb = nb; 00632 return rc; 00633 } 00634 #endif 00635 00643 static int 00644 expandU(MacroBuf mb, char * u, size_t ulen) 00645 /*@globals rpmGlobalMacroContext, h_errno, fileSystem, internalState @*/ 00646 /*@modifies mb, *u, rpmGlobalMacroContext, fileSystem, internalState @*/ 00647 { 00648 const char *s = mb->s; 00649 char *t = mb->t; 00650 size_t nb = mb->nb; 00651 char *tbuf; 00652 int rc; 00653 00654 tbuf = alloca(ulen + 1); 00655 memset(tbuf, 0, (ulen + 1)); 00656 00657 mb->s = u; 00658 mb->t = tbuf; 00659 mb->nb = ulen; 00660 rc = expandMacro(mb); 00661 00662 tbuf[ulen] = '\0'; /* XXX just in case */ 00663 if (ulen > mb->nb) 00664 strncpy(u, tbuf, (ulen - mb->nb + 1)); 00665 00666 mb->s = s; 00667 mb->t = t; 00668 mb->nb = nb; 00669 00670 return rc; 00671 } 00672 00680 static int 00681 doShellEscape(MacroBuf mb, const char * cmd, size_t clen) 00682 /*@globals rpmGlobalMacroContext, h_errno, fileSystem, internalState @*/ 00683 /*@modifies mb, rpmGlobalMacroContext, fileSystem, internalState @*/ 00684 { 00685 size_t bufn = _macro_BUFSIZ + clen; 00686 char * buf = alloca(bufn); 00687 FILE *shf; 00688 int rc; 00689 int c; 00690 00691 strncpy(buf, cmd, clen); 00692 buf[clen] = '\0'; 00693 rc = expandU(mb, buf, bufn); 00694 if (rc) 00695 return rc; 00696 00697 if ((shf = popen(buf, "r")) == NULL) 00698 return 1; 00699 while(mb->nb > 0 && (c = fgetc(shf)) != EOF) 00700 SAVECHAR(mb, c); 00701 (void) pclose(shf); 00702 00703 /* XXX delete trailing \r \n */ 00704 while (iseol(mb->t[-1])) { 00705 *(mb->t--) = '\0'; 00706 mb->nb++; 00707 } 00708 return 0; 00709 } 00710 00719 /*@dependent@*/ static const char * 00720 doDefine(MacroBuf mb, /*@returned@*/ const char * se, int level, int expandbody) 00721 /*@globals rpmGlobalMacroContext, h_errno, internalState @*/ 00722 /*@modifies mb, rpmGlobalMacroContext, internalState @*/ 00723 { 00724 const char *s = se; 00725 size_t bufn = _macro_BUFSIZ; 00726 char *buf = alloca(bufn); 00727 char *n = buf, *ne; 00728 char *o = NULL, *oe; 00729 char *b, *be; 00730 int c; 00731 int oc = (int) ')'; 00732 00733 SKIPBLANK(s, c); 00734 if (c == (int) '.') /* XXX readonly macros */ 00735 /*@i@*/ *n++ = c = *s++; 00736 if (c == (int) '.') /* XXX readonly macros */ 00737 /*@i@*/ *n++ = c = *s++; 00738 ne = n; 00739 00740 /* Copy name */ 00741 COPYNAME(ne, s, c); 00742 00743 /* Copy opts (if present) */ 00744 oe = ne + 1; 00745 if (*s == '(') { 00746 s++; /* skip ( */ 00747 o = oe; 00748 COPYOPTS(oe, s, oc); 00749 s++; /* skip ) */ 00750 } 00751 00752 /* Copy body, skipping over escaped newlines */ 00753 b = be = oe + 1; 00754 SKIPBLANK(s, c); 00755 if (c == (int) '{') { /* XXX permit silent {...} grouping */ 00756 if ((se = matchchar(s, (char) c, '}')) == NULL) { 00757 rpmlog(RPMLOG_ERR, 00758 _("Macro %%%s has unterminated body\n"), n); 00759 se = s; /* XXX W2DO? */ 00760 return se; 00761 } 00762 s++; /* XXX skip { */ 00763 strncpy(b, s, (se - s)); 00764 b[se - s] = '\0'; 00765 be += strlen(b); 00766 se++; /* XXX skip } */ 00767 s = se; /* move scan forward */ 00768 } else { /* otherwise free-field */ 00769 int bc = 0, pc = 0; 00770 while (*s && (bc || pc || !iseol(*s))) { 00771 switch (*s) { 00772 case '\\': 00773 switch (*(s+1)) { 00774 case '\0': /*@switchbreak@*/ break; 00775 default: s++; /*@switchbreak@*/ break; 00776 } 00777 /*@switchbreak@*/ break; 00778 case '%': 00779 switch (*(s+1)) { 00780 case '{': *be++ = *s++; bc++; /*@switchbreak@*/ break; 00781 case '(': *be++ = *s++; pc++; /*@switchbreak@*/ break; 00782 case '%': *be++ = *s++; /*@switchbreak@*/ break; 00783 } 00784 /*@switchbreak@*/ break; 00785 case '{': if (bc > 0) bc++; /*@switchbreak@*/ break; 00786 case '}': if (bc > 0) bc--; /*@switchbreak@*/ break; 00787 case '(': if (pc > 0) pc++; /*@switchbreak@*/ break; 00788 case ')': if (pc > 0) pc--; /*@switchbreak@*/ break; 00789 } 00790 *be++ = *s++; 00791 } 00792 *be = '\0'; 00793 00794 if (bc || pc) { 00795 rpmlog(RPMLOG_ERR, 00796 _("Macro %%%s has unterminated body\n"), n); 00797 se = s; /* XXX W2DO? */ 00798 return se; 00799 } 00800 00801 /* Trim trailing blanks/newlines */ 00802 /*@-globs@*/ 00803 while (--be >= b && (c = (int) *be) && (isblank(c) || iseol(c))) 00804 {}; 00805 /*@=globs@*/ 00806 *(++be) = '\0'; /* one too far */ 00807 } 00808 00809 /* Move scan over body */ 00810 while (iseol(*s)) 00811 s++; 00812 se = s; 00813 00814 /* Names must start with alphabetic or _ and be at least 3 chars */ 00815 if (!((c = (int) *n) && (xisalpha(c) || c == (int) '_') && (ne - n) > 2)) { 00816 rpmlog(RPMLOG_ERR, 00817 _("Macro %%%s has illegal name (%%define)\n"), n); 00818 return se; 00819 } 00820 00821 /* Options must be terminated with ')' */ 00822 if (o && oc != (int) ')') { 00823 rpmlog(RPMLOG_ERR, _("Macro %%%s has unterminated opts\n"), n); 00824 return se; 00825 } 00826 00827 if ((be - b) < 1) { 00828 rpmlog(RPMLOG_ERR, _("Macro %%%s has empty body\n"), n); 00829 return se; 00830 } 00831 00832 /*@-modfilesys@*/ 00833 if (expandbody && expandU(mb, b, (&buf[bufn] - b))) { 00834 rpmlog(RPMLOG_ERR, _("Macro %%%s failed to expand\n"), n); 00835 return se; 00836 } 00837 /*@=modfilesys@*/ 00838 00839 if (n != buf) /* XXX readonly macros */ 00840 n--; 00841 if (n != buf) /* XXX readonly macros */ 00842 n--; 00843 addMacro(mb->mc, n, o, b, (level - 1)); 00844 00845 return se; 00846 } 00847 00854 /*@dependent@*/ static const char * 00855 doUndefine(MacroContext mc, /*@returned@*/ const char * se) 00856 /*@globals rpmGlobalMacroContext @*/ 00857 /*@modifies mc, rpmGlobalMacroContext @*/ 00858 { 00859 const char *s = se; 00860 char *buf = alloca(_macro_BUFSIZ); 00861 char *n = buf, *ne = n; 00862 int c; 00863 00864 COPYNAME(ne, s, c); 00865 00866 /* Move scan over body */ 00867 while (iseol(*s)) 00868 s++; 00869 se = s; 00870 00871 /* Names must start with alphabetic or _ and be at least 3 chars */ 00872 if (!((c = (int) *n) && (xisalpha(c) || c == (int) '_') && (ne - n) > 2)) { 00873 rpmlog(RPMLOG_ERR, 00874 _("Macro %%%s has illegal name (%%undefine)\n"), n); 00875 return se; 00876 } 00877 00878 delMacro(mc, n); 00879 00880 return se; 00881 } 00882 00883 #ifdef DYING 00884 static void 00885 dumpME(const char * msg, MacroEntry me) 00886 /*@globals fileSystem @*/ 00887 /*@modifies fileSystem @*/ 00888 { 00889 if (msg) 00890 fprintf(stderr, "%s", msg); 00891 fprintf(stderr, "\tme %p", me); 00892 if (me) 00893 fprintf(stderr,"\tname %p(%s) prev %p", 00894 me->name, me->name, me->prev); 00895 fprintf(stderr, "\n"); 00896 } 00897 #endif 00898 00907 static void 00908 pushMacro(/*@out@*/ MacroEntry * mep, const char * n, /*@null@*/ const char * o, 00909 /*@null@*/ const char * b, int level) 00910 /*@modifies *mep @*/ 00911 { 00912 MacroEntry prev = (mep && *mep ? *mep : NULL); 00913 MacroEntry me = (MacroEntry) xmalloc(sizeof(*me)); 00914 const char *name = n; 00915 00916 if (*name == '.') /* XXX readonly macros */ 00917 name++; 00918 if (*name == '.') /* XXX readonly macros */ 00919 name++; 00920 00921 /*@-assignexpose@*/ 00922 me->prev = prev; 00923 /*@=assignexpose@*/ 00924 me->name = (prev ? prev->name : xstrdup(name)); 00925 me->opts = (o ? xstrdup(o) : NULL); 00926 me->body = xstrdup(b ? b : ""); 00927 me->used = 0; 00928 me->level = level; 00929 me->flags = (name != n); 00930 if (mep) 00931 *mep = me; 00932 else 00933 me = _free(me); 00934 } 00935 00940 static void 00941 popMacro(MacroEntry * mep) 00942 /*@modifies *mep @*/ 00943 { 00944 MacroEntry me = (*mep ? *mep : NULL); 00945 00946 if (me) { 00947 /* XXX cast to workaround const */ 00948 /*@-onlytrans@*/ 00949 if ((*mep = me->prev) == NULL) 00950 me->name = _free(me->name); 00951 me->opts = _free(me->opts); 00952 me->body = _free(me->body); 00953 me = _free(me); 00954 /*@=onlytrans@*/ 00955 } 00956 } 00957 00962 static void 00963 freeArgs(MacroBuf mb) 00964 /*@modifies mb @*/ 00965 { 00966 MacroContext mc = mb->mc; 00967 int ndeleted = 0; 00968 int i; 00969 00970 if (mc == NULL || mc->macroTable == NULL) 00971 return; 00972 00973 /* Delete dynamic macro definitions */ 00974 for (i = 0; i < mc->firstFree; i++) { 00975 MacroEntry *mep, me; 00976 int skiptest = 0; 00977 mep = &mc->macroTable[i]; 00978 me = *mep; 00979 00980 if (me == NULL) /* XXX this should never happen */ 00981 continue; 00982 if (me->level < mb->depth) 00983 continue; 00984 if (strlen(me->name) == 1 && strchr("#*0", *me->name)) { 00985 if (*me->name == '*' && me->used > 0) 00986 skiptest = 1; /* XXX skip test for %# %* %0 */ 00987 } else if (!skiptest && me->used <= 0) { 00988 #if NOTYET 00989 rpmlog(RPMLOG_ERR, 00990 _("Macro %%%s (%s) was not used below level %d\n"), 00991 me->name, me->body, me->level); 00992 #endif 00993 } 00994 popMacro(mep); 00995 if (!(mep && *mep)) 00996 ndeleted++; 00997 } 00998 00999 /* If any deleted macros, sort macro table */ 01000 if (ndeleted) 01001 sortMacroTable(mc); 01002 } 01003 01013 /*@dependent@*/ static const char * 01014 grabArgs(MacroBuf mb, const MacroEntry me, /*@returned@*/ const char * se, 01015 const char * lastc) 01016 /*@globals rpmGlobalMacroContext, fileSystem, internalState @*/ 01017 /*@modifies mb, rpmGlobalMacroContext, fileSystem, internalState @*/ 01018 { 01019 poptContext optCon; 01020 struct poptOption *optTbl; 01021 size_t bufn = _macro_BUFSIZ; 01022 char *buf = alloca(bufn); 01023 char *b, *be; 01024 char aname[16]; 01025 const char *opts; 01026 int argc = 0; 01027 const char **argv; 01028 int c; 01029 unsigned int popt_flags; 01030 01031 /* Copy macro name as argv[0], save beginning of args. */ 01032 buf[0] = '\0'; 01033 b = be = stpcpy(buf, me->name); 01034 01035 addMacro(mb->mc, "0", NULL, buf, mb->depth); 01036 01037 argc = 1; /* XXX count argv[0] */ 01038 01039 /* Copy args into buf until lastc */ 01040 *be++ = ' '; 01041 while ((c = (int) *se++) != (int) '\0' && (se-1) != lastc) { 01042 /*@-globs@*/ 01043 if (!isblank(c)) { 01044 *be++ = (char) c; 01045 continue; 01046 } 01047 /*@=globs@*/ 01048 /* c is blank */ 01049 if (be[-1] == ' ') 01050 continue; 01051 /* a word has ended */ 01052 *be++ = ' '; 01053 argc++; 01054 } 01055 if (c == (int) '\0') se--; /* one too far */ 01056 if (be[-1] != ' ') 01057 argc++, be++; /* last word has not trailing ' ' */ 01058 be[-1] = '\0'; 01059 if (*b == ' ') b++; /* skip the leading ' ' */ 01060 01061 /* 01062 * The macro %* analoguous to the shell's $* means "Pass all non-macro 01063 * parameters." Consequently, there needs to be a macro that means "Pass all 01064 * (including macro parameters) options". This is useful for verifying 01065 * parameters during expansion and yet transparently passing all parameters 01066 * through for higher level processing (e.g. %description and/or %setup). 01067 * This is the (potential) justification for %{**} ... 01068 */ 01069 /* Add unexpanded args as macro */ 01070 addMacro(mb->mc, "**", NULL, b, mb->depth); 01071 01072 #ifdef NOTYET 01073 /* XXX if macros can be passed as args ... */ 01074 expandU(mb, buf, bufn); 01075 #endif 01076 01077 /* Build argv array */ 01078 argv = (const char **) alloca((argc + 1) * sizeof(*argv)); 01079 be[-1] = ' '; /* assert((be - 1) == (b + strlen(b) == buf + strlen(buf))) */ 01080 be[0] = '\0'; 01081 01082 b = buf; 01083 for (c = 0; c < argc; c++) { 01084 argv[c] = b; 01085 b = strchr(b, ' '); 01086 *b++ = '\0'; 01087 } 01088 /* assert(b == be); */ 01089 argv[argc] = NULL; 01090 01091 /* '+' as the first character means that options are recognized 01092 * only before positional arguments, as POSIX requires. 01093 */ 01094 popt_flags = POPT_CONTEXT_NO_EXEC; 01095 #if defined(RPM_VENDOR_OPENPKG) /* always-strict-posix-option-parsing */ 01096 popt_flags |= POPT_CONTEXT_POSIXMEHARDER; 01097 #endif 01098 if (me->opts[0] == '+') popt_flags |= POPT_CONTEXT_POSIXMEHARDER; 01099 01100 /* Count the number of short options. */ 01101 opts = me->opts; 01102 if (*opts == '+') opts++; 01103 for (c = 0; *opts != '\0'; opts++) 01104 if (*opts != ':') c++; 01105 01106 /* Set up popt option table. */ 01107 optTbl = xcalloc(sizeof(*optTbl), (c + 1)); 01108 opts = me->opts; 01109 if (*opts == '+') opts++; 01110 for (c = 0; *opts != '\0'; opts++) { 01111 if (*opts == ':') continue; 01112 optTbl[c].shortName = opts[0]; 01113 optTbl[c].val = (int) opts[0]; 01114 if (opts[1] == ':') 01115 optTbl[c].argInfo = POPT_ARG_STRING; 01116 c++; 01117 } 01118 01119 /* Parse the options, defining option macros. */ 01120 /*@-nullstate@*/ 01121 optCon = poptGetContext(argv[0], argc, argv, optTbl, popt_flags); 01122 /*@=nullstate@*/ 01123 while ((c = poptGetNextOpt(optCon)) > 0) { 01124 const char * optArg = poptGetOptArg(optCon); 01125 *be++ = '-'; 01126 *be++ = (char) c; 01127 if (optArg != NULL) { 01128 *be++ = ' '; 01129 be = stpcpy(be, optArg); 01130 } 01131 *be++ = '\0'; 01132 aname[0] = '-'; aname[1] = (char)c; aname[2] = '\0'; 01133 addMacro(mb->mc, aname, NULL, b, mb->depth); 01134 if (optArg != NULL) { 01135 aname[0] = '-'; aname[1] = (char)c; aname[2] = '*'; aname[3] = '\0'; 01136 addMacro(mb->mc, aname, NULL, optArg, mb->depth); 01137 } 01138 be = b; /* reuse the space */ 01139 /*@-dependenttrans -modobserver -observertrans @*/ 01140 optArg = _free(optArg); 01141 /*@=dependenttrans =modobserver =observertrans @*/ 01142 } 01143 if (c < -1) { 01144 rpmlog(RPMLOG_ERR, _("Unknown option in macro %s(%s): %s: %s\n"), 01145 me->name, me->opts, 01146 poptBadOption(optCon, POPT_BADOPTION_NOALIAS), poptStrerror(c)); 01147 goto exit; 01148 } 01149 01150 argv = poptGetArgs(optCon); 01151 argc = 0; 01152 if (argv != NULL) 01153 for (c = 0; argv[c] != NULL; c++) 01154 argc++; 01155 01156 /* Add arg count as macro. */ 01157 sprintf(aname, "%d", argc); 01158 addMacro(mb->mc, "#", NULL, aname, mb->depth); 01159 01160 /* Add macro for each arg. Concatenate args for %*. */ 01161 if (be) { 01162 *be = '\0'; 01163 if (argv != NULL) 01164 for (c = 0; c < argc; c++) { 01165 sprintf(aname, "%d", (c + 1)); 01166 addMacro(mb->mc, aname, NULL, argv[c], mb->depth); 01167 if (be != b) *be++ = ' '; /* Add space between args */ 01168 be = stpcpy(be, argv[c]); 01169 } 01170 } 01171 01172 /* Add unexpanded args as macro. */ 01173 addMacro(mb->mc, "*", NULL, b, mb->depth); 01174 01175 exit: 01176 optCon = poptFreeContext(optCon); 01177 optTbl = _free(optTbl); 01178 return se; 01179 } 01180 01188 static void 01189 doOutput(MacroBuf mb, int waserror, const char * msg, size_t msglen) 01190 /*@globals rpmGlobalMacroContext, h_errno, fileSystem, internalState @*/ 01191 /*@modifies mb, rpmGlobalMacroContext, fileSystem, internalState @*/ 01192 { 01193 size_t bufn = _macro_BUFSIZ + msglen; 01194 char *buf = alloca(bufn); 01195 01196 strncpy(buf, msg, msglen); 01197 buf[msglen] = '\0'; 01198 (void) expandU(mb, buf, bufn); 01199 if (waserror) 01200 rpmlog(RPMLOG_ERR, "%s\n", buf); 01201 else 01202 fprintf(stderr, "%s", buf); 01203 } 01204 01214 static void 01215 doFoo(MacroBuf mb, int negate, const char * f, size_t fn, 01216 /*@null@*/ const char * g, size_t gn) 01217 /*@globals rpmGlobalMacroContext, h_errno, fileSystem, internalState @*/ 01218 /*@modifies mb, rpmGlobalMacroContext, fileSystem, internalState @*/ 01219 { 01220 size_t bufn = _macro_BUFSIZ + fn + gn; 01221 char * buf = alloca(bufn); 01222 char *b = NULL, *be; 01223 int c; 01224 01225 buf[0] = '\0'; 01226 if (g != NULL) { 01227 strncpy(buf, g, gn); 01228 buf[gn] = '\0'; 01229 (void) expandU(mb, buf, bufn); 01230 } 01231 if (fn > 5 && STREQ("patch", f, 5) && xisdigit((int)f[5])) { 01232 /* Skip leading zeros */ 01233 for (c = 5; c < (int)(fn-1) && f[c] == '0' && xisdigit((int)f[c+1]);) 01234 c++; 01235 b = buf; 01236 be = stpncpy( stpcpy(b, "%patch -P "), f+c, fn-c); 01237 *be = '\0'; 01238 } else 01239 if (STREQ("basename", f, fn)) { 01240 if ((b = strrchr(buf, '/')) == NULL) 01241 b = buf; 01242 else 01243 b++; 01244 } else if (STREQ("dirname", f, fn)) { 01245 if ((b = strrchr(buf, '/')) != NULL) 01246 *b = '\0'; 01247 b = buf; 01248 #if !defined(__LCLINT__) /* XXX LCL: realpath(3) annotations are buggy. */ 01249 } else if (STREQ("realpath", f, fn)) { 01250 char rp[PATH_MAX]; 01251 char *cp; 01252 size_t l; 01253 if ((cp = realpath(buf, rp)) != NULL) { 01254 l = strlen(cp); 01255 if ((size_t)(l+1) <= bufn) { 01256 memcpy(buf, cp, l+1); 01257 b = buf; 01258 } 01259 } 01260 #endif 01261 } else if (STREQ("getenv", f, fn)) { 01262 char *cp; 01263 if ((cp = getenv(buf)) != NULL) 01264 b = cp; 01265 } else if (STREQ("shrink", f, fn)) { 01266 /* 01267 * shrink body by removing all leading and trailing whitespaces and 01268 * reducing intermediate whitespaces to a single space character. 01269 */ 01270 int i, j, k, was_space = 0; 01271 for (i = 0, j = 0, k = (int)strlen(buf); i < k; ) { 01272 if (xisspace((int)(buf[i]))) { 01273 was_space = 1; 01274 i++; 01275 continue; 01276 } 01277 else if (was_space) { 01278 was_space = 0; 01279 if (j > 0) /* remove leading blanks at all */ 01280 buf[j++] = ' '; 01281 /* fallthrough */ 01282 } 01283 buf[j++] = buf[i++]; 01284 } 01285 buf[j] = '\0'; 01286 b = buf; 01287 } else if (STREQ("suffix", f, fn)) { 01288 if ((b = strrchr(buf, '.')) != NULL) 01289 b++; 01290 } else if (STREQ("expand", f, fn)) { 01291 b = buf; 01292 } else if (STREQ("verbose", f, fn)) { 01293 #if defined(RPMLOG_MASK) 01294 if (negate) 01295 b = ((rpmlogSetMask(0) >= RPMLOG_MASK( RPMLOG_INFO )) ? NULL : buf); 01296 else 01297 b = ((rpmlogSetMask(0) >= RPMLOG_MASK( RPMLOG_INFO )) ? buf : NULL); 01298 #else 01299 /* XXX assume always verbose when running standalone */ 01300 b = (negate) ? NULL : buf; 01301 #endif 01302 } else if (STREQ("url2path", f, fn) || STREQ("u2p", f, fn)) { 01303 int ut = urlPath(buf, (const char **)&b); 01304 ut = ut; /* XXX quiet gcc */ 01305 if (*b == '\0') b = "/"; 01306 } else if (STREQ("uncompress", f, fn)) { 01307 rpmCompressedMagic compressed = COMPRESSED_OTHER; 01308 /*@-globs@*/ 01309 for (b = buf; (c = (int)*b) && isblank(c);) 01310 b++; 01311 /* XXX FIXME: file paths with embedded white space needs rework. */ 01312 for (be = b; (c = (int)*be) && !isblank(c);) 01313 be++; 01314 /*@=globs@*/ 01315 *be++ = '\0'; 01316 (void) isCompressed(b, &compressed); 01317 switch(compressed) { 01318 default: 01319 case 0: /* COMPRESSED_NOT */ 01320 sprintf(be, "%%__cat %s", b); 01321 break; 01322 case 1: /* COMPRESSED_OTHER */ 01323 sprintf(be, "%%__gzip -dc '%s'", b); 01324 break; 01325 case 2: /* COMPRESSED_BZIP2 */ 01326 sprintf(be, "%%__bzip2 -dc '%s'", b); 01327 break; 01328 case 3: /* COMPRESSED_ZIP */ 01329 sprintf(be, "%%__unzip -qq '%s'", b); 01330 break; 01331 case 4: /* COMPRESSED_LZOP */ 01332 sprintf(be, "%%__lzop -dc '%s'", b); 01333 break; 01334 case 5: /* COMPRESSED_LZMA */ 01335 sprintf(be, "%%__lzma -dc '%s'", b); 01336 break; 01337 case 6: /* COMPRESSED_XZ */ 01338 sprintf(be, "%%__xz -dc '%s'", b); 01339 break; 01340 } 01341 b = be; 01342 } else if (STREQ("mkstemp", f, fn)) { 01343 /*@-globs@*/ 01344 for (b = buf; (c = (int)*b) && isblank(c);) 01345 b++; 01346 /* XXX FIXME: file paths with embedded white space needs rework. */ 01347 for (be = b; (c = (int)*be) && !isblank(c);) 01348 be++; 01349 /*@=globs@*/ 01350 #if defined(HAVE_MKSTEMP) 01351 (void) close(mkstemp(b)); 01352 #else 01353 (void) mktemp(b); 01354 #endif 01355 } else if (STREQ("mkdtemp", f, fn)) { 01356 /*@-globs@*/ 01357 for (b = buf; (c = (int)*b) && isblank(c);) 01358 b++; 01359 /* XXX FIXME: file paths with embedded white space needs rework. */ 01360 for (be = b; (c = (int)*be) && !isblank(c);) 01361 be++; 01362 /*@=globs@*/ 01363 #if defined(HAVE_MKDTEMP) && !defined(__LCLINT__) 01364 if (mkdtemp(b) == NULL) 01365 perror("mkdtemp"); 01366 #else 01367 if ((b = tmpnam(b)) != NULL) 01368 (void) mkdir(b, 0700); /* XXX S_IWRSXU is not included. */ 01369 #endif 01370 } else if (STREQ("uuid", f, fn)) { 01371 int uuid_version; 01372 const char *uuid_ns; 01373 const char *uuid_data; 01374 char *cp; 01375 size_t n; 01376 01377 uuid_version = 1; 01378 uuid_ns = NULL; 01379 uuid_data = NULL; 01380 cp = buf; 01381 if ((n = strspn(cp, " \t\n")) > 0) 01382 cp += n; 01383 if ((n = strcspn(cp, " \t\n")) > 0) { 01384 uuid_version = (int)strtol(cp, (char **)NULL, 10); 01385 cp += n; 01386 if ((n = strspn(cp, " \t\n")) > 0) 01387 cp += n; 01388 if ((n = strcspn(cp, " \t\n")) > 0) { 01389 uuid_ns = cp; 01390 cp += n; 01391 *cp++ = '\0'; 01392 if ((n = strspn(cp, " \t\n")) > 0) 01393 cp += n; 01394 if ((n = strcspn(cp, " \t\n")) > 0) { 01395 uuid_data = cp; 01396 cp += n; 01397 *cp++ = '\0'; 01398 } 01399 } 01400 } 01401 /*@-nullpass@*/ /* FIX: uuid_ns may be NULL */ 01402 if (rpmuuidMake(uuid_version, uuid_ns, uuid_data, buf, NULL)) 01403 rpmlog(RPMLOG_ERR, "failed to create UUID\n"); 01404 else 01405 b = buf; 01406 /*@=nullpass@*/ 01407 } else if (STREQ("S", f, fn)) { 01408 for (b = buf; (c = (int)*b) && xisdigit(c);) 01409 b++; 01410 if (!c) { /* digit index */ 01411 b++; 01412 sprintf(b, "%%SOURCE%s", buf); 01413 } else 01414 b = buf; 01415 } else if (STREQ("P", f, fn)) { 01416 for (b = buf; (c = (int) *b) && xisdigit(c);) 01417 b++; 01418 if (!c) { /* digit index */ 01419 b++; 01420 sprintf(b, "%%PATCH%s", buf); 01421 } else 01422 b = buf; 01423 } else if (STREQ("F", f, fn)) { 01424 b = buf + strlen(buf) + 1; 01425 sprintf(b, "file%s.file", buf); 01426 } 01427 01428 if (b) { 01429 (void) expandT(mb, b, strlen(b)); 01430 } 01431 } 01432 01433 static int expandFIFO(MacroBuf mb, MacroEntry me, const char *g, size_t gn) 01434 /*@globals rpmGlobalMacroContext, h_errno, fileSystem, internalState @*/ 01435 /*@modifies mb, rpmGlobalMacroContext, fileSystem, internalState @*/ 01436 { 01437 int rc = 0; 01438 01439 if (me) { 01440 if (me->prev) { 01441 rc = expandFIFO(mb, me->prev, g, gn); 01442 rc = expandT(mb, g, gn); 01443 } 01444 rc = expandT(mb, me->body, strlen(me->body)); 01445 } 01446 return rc; 01447 } 01448 01449 #if !defined(DEBUG_MACROS) 01450 /* =============================================================== */ 01451 /* XXX dupe'd to avoid change in linkage conventions. */ 01452 01453 #define POPT_ERROR_NOARG -10 01454 #define POPT_ERROR_BADQUOTE -15 01455 #define POPT_ERROR_MALLOC -21 01457 #define POPT_ARGV_ARRAY_GROW_DELTA 5 01458 01459 static int XpoptDupArgv(int argc, const char **argv, 01460 int * argcPtr, const char *** argvPtr) 01461 /*@modifies *argcPtr, *argvPtr @*/ 01462 { 01463 size_t nb = (argc + 1) * sizeof(*argv); 01464 const char ** argv2; 01465 char * dst; 01466 int i; 01467 01468 if (argc <= 0 || argv == NULL) /* XXX can't happen */ 01469 return POPT_ERROR_NOARG; 01470 for (i = 0; i < argc; i++) { 01471 if (argv[i] == NULL) 01472 return POPT_ERROR_NOARG; 01473 nb += strlen(argv[i]) + 1; 01474 } 01475 01476 dst = xmalloc(nb); 01477 if (dst == NULL) /* XXX can't happen */ 01478 return POPT_ERROR_MALLOC; 01479 argv2 = (void *) dst; 01480 dst += (argc + 1) * sizeof(*argv); 01481 01482 for (i = 0; i < argc; i++) { 01483 argv2[i] = dst; 01484 dst += strlen(strcpy(dst, argv[i])) + 1; 01485 } 01486 argv2[argc] = NULL; 01487 01488 if (argvPtr) { 01489 *argvPtr = argv2; 01490 } else { 01491 free(argv2); 01492 argv2 = NULL; 01493 } 01494 if (argcPtr) 01495 *argcPtr = argc; 01496 return 0; 01497 } 01498 01499 static int XpoptParseArgvString(const char * s, int * argcPtr, const char *** argvPtr) 01500 /*@modifies *argcPtr, *argvPtr @*/ 01501 { 01502 const char * src; 01503 char quote = '\0'; 01504 int argvAlloced = POPT_ARGV_ARRAY_GROW_DELTA; 01505 const char ** argv = xmalloc(sizeof(*argv) * argvAlloced); 01506 int argc = 0; 01507 size_t buflen = strlen(s) + 1; 01508 char * buf = memset(alloca(buflen), 0, buflen); 01509 int rc = POPT_ERROR_MALLOC; 01510 01511 if (argv == NULL) return rc; 01512 argv[argc] = buf; 01513 01514 for (src = s; *src != '\0'; src++) { 01515 if (quote == *src) { 01516 quote = '\0'; 01517 } else if (quote != '\0') { 01518 if (*src == '\\') { 01519 src++; 01520 if (!*src) { 01521 rc = POPT_ERROR_BADQUOTE; 01522 goto exit; 01523 } 01524 if (*src != quote) *buf++ = '\\'; 01525 } 01526 *buf++ = *src; 01527 } else if (isspace(*src)) { 01528 if (*argv[argc] != '\0') { 01529 buf++, argc++; 01530 if (argc == argvAlloced) { 01531 argvAlloced += POPT_ARGV_ARRAY_GROW_DELTA; 01532 argv = realloc(argv, sizeof(*argv) * argvAlloced); 01533 if (argv == NULL) goto exit; 01534 } 01535 argv[argc] = buf; 01536 } 01537 } else switch (*src) { 01538 case '"': 01539 case '\'': 01540 quote = *src; 01541 /*@switchbreak@*/ break; 01542 case '\\': 01543 src++; 01544 if (!*src) { 01545 rc = POPT_ERROR_BADQUOTE; 01546 goto exit; 01547 } 01548 /*@fallthrough@*/ 01549 default: 01550 *buf++ = *src; 01551 /*@switchbreak@*/ break; 01552 } 01553 } 01554 01555 if (strlen(argv[argc])) { 01556 argc++, buf++; 01557 } 01558 01559 rc = XpoptDupArgv(argc, argv, argcPtr, argvPtr); 01560 01561 exit: 01562 if (argv) free(argv); 01563 return rc; 01564 } 01565 #endif /* !defined(DEBUG_MACROS) */ 01566 01574 #if defined(WITH_FICL) || defined(WITH_JS) || defined(WITH_PERLEMBED) || defined(WITH_PYTHONEMBED) || defined(WITH_RUBYEMBED) || defined(WITH_TCL) 01575 static char * parseEmbedded(const char * s, size_t nb, const char *** avp) 01576 /*@*/ 01577 { 01578 char * script = NULL; 01579 const char * se; 01580 01581 /* XXX FIXME: args might have embedded : too. */ 01582 for (se = s + 1; se < (s+nb); se++) 01583 switch (*se) { 01584 default: continue; /*@notreached@*/ break; 01585 case ':': goto bingo; /*@notreached@*/ break; 01586 } 01587 01588 bingo: 01589 { size_t na = (size_t)(se-s-1); 01590 char * args = NULL; 01591 int ac; 01592 int rc; 01593 01594 args = memcpy(xmalloc(na+1), s+1, na); 01595 args[na] = '\0'; 01596 01597 ac = 0; 01598 rc = XpoptParseArgvString(args, &ac, avp); 01599 args = _free(args); 01600 nb -= na; 01601 } 01602 01603 nb -= 3; 01604 script = memcpy(xmalloc(nb+1), se+1, nb+1); 01605 script[nb] = '\0'; 01606 return script; 01607 } 01608 #endif 01609 01616 static int 01617 expandMacro(MacroBuf mb) 01618 /*@globals rpmGlobalMacroContext, 01619 print_macro_trace, print_expand_trace, h_errno, 01620 fileSystem, internalState @*/ 01621 /*@modifies mb, rpmGlobalMacroContext, 01622 print_macro_trace, print_expand_trace, 01623 fileSystem, internalState @*/ 01624 { 01625 MacroEntry *mep; 01626 MacroEntry me; 01627 const char *s = mb->s, *se; 01628 const char *f, *fe; 01629 const char *g, *ge; 01630 size_t fn, gn; 01631 char *t = mb->t; /* save expansion pointer for printExpand */ 01632 int c; 01633 int rc = 0; 01634 int negate; 01635 int stackarray; 01636 const char * lastc; 01637 int chkexist; 01638 01639 if (++mb->depth > max_macro_depth) { 01640 rpmlog(RPMLOG_ERR, 01641 _("Recursion depth(%d) greater than max(%d)\n"), 01642 mb->depth, max_macro_depth); 01643 mb->depth--; 01644 mb->expand_trace = 1; 01645 return 1; 01646 } 01647 01648 while (rc == 0 && mb->nb > 0 && (c = (int)*s) != (int)'\0') { 01649 s++; 01650 /* Copy text until next macro */ 01651 switch(c) { 01652 case '%': 01653 if (*s != '\0') { /* Ensure not end-of-string. */ 01654 if (*s != '%') 01655 /*@switchbreak@*/ break; 01656 s++; /* skip first % in %% */ 01657 } 01658 /*@fallthrough@*/ 01659 default: 01660 SAVECHAR(mb, c); 01661 continue; 01662 /*@notreached@*/ /*@switchbreak@*/ break; 01663 } 01664 01665 /* Expand next macro */ 01666 f = fe = NULL; 01667 g = ge = NULL; 01668 if (mb->depth > 1) /* XXX full expansion for outermost level */ 01669 t = mb->t; /* save expansion pointer for printExpand */ 01670 stackarray = chkexist = negate = 0; 01671 lastc = NULL; 01672 switch ((c = (int) *s)) { 01673 default: /* %name substitution */ 01674 while (*s != '\0' && strchr("!?@", *s) != NULL) { 01675 switch(*s++) { 01676 case '@': 01677 stackarray = ((stackarray + 1) % 2); 01678 /*@switchbreak@*/ break; 01679 case '!': 01680 negate = ((negate + 1) % 2); 01681 /*@switchbreak@*/ break; 01682 case '?': 01683 chkexist++; 01684 /*@switchbreak@*/ break; 01685 } 01686 } 01687 f = se = s; 01688 if (*se == '-') 01689 se++; 01690 while((c = (int) *se) && (xisalnum(c) || c == (int) '_')) 01691 se++; 01692 /* Recognize non-alnum macros too */ 01693 switch (*se) { 01694 case '*': 01695 se++; 01696 if (*se == '*') se++; 01697 /*@innerbreak@*/ break; 01698 case '#': 01699 se++; 01700 /*@innerbreak@*/ break; 01701 default: 01702 /*@innerbreak@*/ break; 01703 } 01704 fe = se; 01705 /* For "%name " macros ... */ 01706 /*@-globs@*/ 01707 if ((c = (int) *fe) && isblank(c)) 01708 if ((lastc = strchr(fe,'\n')) == NULL) 01709 lastc = strchr(fe, '\0'); 01710 /*@=globs@*/ 01711 /*@switchbreak@*/ break; 01712 case '(': /* %(...) shell escape */ 01713 if ((se = matchchar(s, (char)c, ')')) == NULL) { 01714 rpmlog(RPMLOG_ERR, 01715 _("Unterminated %c: %s\n"), (char)c, s); 01716 rc = 1; 01717 continue; 01718 } 01719 if (mb->macro_trace) 01720 printMacro(mb, s, se+1); 01721 01722 s++; /* skip ( */ 01723 rc = doShellEscape(mb, s, (se - s)); 01724 se++; /* skip ) */ 01725 01726 s = se; 01727 continue; 01728 /*@notreached@*/ /*@switchbreak@*/ break; 01729 case '{': /* %{...}/%{...:...} substitution */ 01730 if ((se = matchchar(s, (char)c, '}')) == NULL) { 01731 rpmlog(RPMLOG_ERR, 01732 _("Unterminated %c: %s\n"), (char)c, s); 01733 rc = 1; 01734 continue; 01735 } 01736 f = s+1;/* skip { */ 01737 se++; /* skip } */ 01738 while (strchr("!?@", *f) != NULL) { 01739 switch(*f++) { 01740 case '@': 01741 stackarray = ((stackarray + 1) % 2); 01742 /*@switchbreak@*/ break; 01743 case '!': 01744 negate = ((negate + 1) % 2); 01745 /*@switchbreak@*/ break; 01746 case '?': 01747 chkexist++; 01748 /*@switchbreak@*/ break; 01749 } 01750 } 01751 /* Find end-of-expansion, handle %{foo:bar} expansions. */ 01752 for (fe = f; (c = (int) *fe) && !strchr(" :}", c);) 01753 fe++; 01754 switch (c) { 01755 case ':': 01756 g = fe + 1; 01757 ge = se - 1; 01758 /*@innerbreak@*/ break; 01759 case ' ': 01760 lastc = se-1; 01761 /*@innerbreak@*/ break; 01762 default: 01763 /*@innerbreak@*/ break; 01764 } 01765 /*@switchbreak@*/ break; 01766 } 01767 01768 /* XXX Everything below expects fe > f */ 01769 fn = (fe - f); 01770 gn = (ge - g); 01771 if ((fe - f) <= 0) { 01772 /* XXX Process % in unknown context */ 01773 c = (int) '%'; /* XXX only need to save % */ 01774 SAVECHAR(mb, c); 01775 #if 0 01776 rpmlog(RPMLOG_ERR, 01777 _("A %% is followed by an unparseable macro\n")); 01778 #endif 01779 s = se; 01780 continue; 01781 } 01782 01783 if (mb->macro_trace) 01784 printMacro(mb, s, se); 01785 01786 /* Expand builtin macros */ 01787 if (STREQ("load", f, fn)) { 01788 if (g != NULL) { 01789 char * mfn = strncpy(alloca(gn + 1), g, gn); 01790 int xx; 01791 mfn[gn] = '\0'; 01792 xx = rpmLoadMacroFile(NULL, mfn); 01793 /* Print failure iff %{load:...} or %{!?load:...} */ 01794 if (xx != 0 && chkexist == negate) 01795 rpmlog(RPMLOG_ERR, _("%s: load macros failed\n"), mfn); 01796 } 01797 s = se; 01798 continue; 01799 } 01800 if (STREQ("global", f, fn)) { 01801 s = doDefine(mb, se, RMIL_GLOBAL, 1); 01802 continue; 01803 } 01804 if (STREQ("define", f, fn)) { 01805 s = doDefine(mb, se, mb->depth, 0); 01806 continue; 01807 } 01808 if (STREQ("undefine", f, fn)) { 01809 s = doUndefine(mb->mc, se); 01810 continue; 01811 } 01812 01813 if (STREQ("echo", f, fn) || 01814 STREQ("warn", f, fn) || 01815 STREQ("error", f, fn)) { 01816 int waserror = 0; 01817 if (STREQ("error", f, fn)) 01818 waserror = 1, rc = 1; 01819 if (g != NULL && g < ge) 01820 doOutput(mb, waserror, g, gn); 01821 else 01822 doOutput(mb, waserror, f, fn); 01823 s = se; 01824 continue; 01825 } 01826 01827 if (STREQ("trace", f, fn)) { 01828 /* XXX TODO restore expand_trace/macro_trace to 0 on return */ 01829 mb->expand_trace = mb->macro_trace = (negate ? 0 : mb->depth); 01830 if (mb->depth == 1) { 01831 print_macro_trace = mb->macro_trace; 01832 print_expand_trace = mb->expand_trace; 01833 } 01834 s = se; 01835 continue; 01836 } 01837 01838 if (STREQ("dump", f, fn)) { 01839 rpmDumpMacroTable(mb->mc, NULL); 01840 while (iseol(*se)) 01841 se++; 01842 s = se; 01843 continue; 01844 } 01845 01846 #ifdef WITH_LUA 01847 if (STREQ("lua", f, fn)) { 01848 rpmlua lua = rpmluaGetGlobalState(); 01849 rpmlua olua = memcpy(alloca(sizeof(*olua)), lua, sizeof(*olua)); 01850 const char *ls = s+sizeof("{lua:")-1; 01851 const char *lse = se-sizeof("}")+1; 01852 char *scriptbuf = (char *)xmalloc((lse-ls)+1); 01853 const char *printbuf; 01854 01855 /* Reset the stateful output buffer before recursing down. */ 01856 lua->storeprint = 1; 01857 lua->printbuf = NULL; 01858 lua->printbufsize = 0; 01859 lua->printbufused = 0; 01860 01861 memcpy(scriptbuf, ls, lse-ls); 01862 scriptbuf[lse-ls] = '\0'; 01863 if (rpmluaRunScript(lua, scriptbuf, NULL) == -1) 01864 rc = 1; 01865 printbuf = rpmluaGetPrintBuffer(lua); 01866 if (printbuf) { 01867 size_t len = strlen(printbuf); 01868 if (len > mb->nb) 01869 len = mb->nb; 01870 memcpy(mb->t, printbuf, len); 01871 mb->t += len; 01872 mb->nb -= len; 01873 } 01874 01875 /* Restore the stateful output buffer after recursion. */ 01876 lua->storeprint = olua->storeprint; 01877 lua->printbuf = olua->printbuf; 01878 lua->printbufsize = olua->printbufsize; 01879 lua->printbufused = olua->printbufused; 01880 01881 free(scriptbuf); 01882 s = se; 01883 continue; 01884 } 01885 #endif 01886 01887 #ifdef WITH_FICL 01888 if (STREQ("ficl", f, fn)) { 01889 const char ** av = NULL; 01890 char * script = parseEmbedded(s, (size_t)(se-s), &av); 01891 rpmficl ficl = rpmficlNew(av, _globalI); 01892 const char * result = NULL; 01893 01894 if (rpmficlRun(ficl, script, &result) != RPMRC_OK) 01895 rc = 1; 01896 else { 01897 if (result == NULL) result = "FIXME"; 01898 if (result != NULL && *result != '\0') { 01899 size_t len = strlen(result); 01900 if (len > mb->nb) 01901 len = mb->nb; 01902 memcpy(mb->t, result, len); 01903 mb->t += len; 01904 mb->nb -= len; 01905 } 01906 } 01907 ficl = rpmficlFree(ficl); 01908 av = _free(av); 01909 script = _free(script); 01910 s = se; 01911 continue; 01912 } 01913 #endif 01914 01915 #ifdef WITH_JS 01916 if (STREQ("js", f, fn)) { 01917 const char ** av = NULL; 01918 char * script = parseEmbedded(s, (size_t)(se-s), &av); 01919 rpmjs js = rpmjsNew(av, _globalI); 01920 const char * result = NULL; 01921 01922 if (rpmjsRun(js, script, &result) != RPMRC_OK) 01923 rc = 1; 01924 else { 01925 if (result == NULL) result = "FIXME"; 01926 if (result != NULL && *result != '\0') { 01927 size_t len = strlen(result); 01928 if (len > mb->nb) 01929 len = mb->nb; 01930 memcpy(mb->t, result, len); 01931 mb->t += len; 01932 mb->nb -= len; 01933 } 01934 } 01935 js = rpmjsFree(js); 01936 av = _free(av); 01937 script = _free(script); 01938 s = se; 01939 continue; 01940 } 01941 #endif 01942 01943 #ifdef WITH_PERLEMBED 01944 if (STREQ("perl", f, fn)) { 01945 const char ** av = NULL; 01946 char * script = parseEmbedded(s, (size_t)(se-s), &av); 01947 rpmperl perl = rpmperlNew(av, _globalI); 01948 const char * result = NULL; 01949 01950 if (rpmperlRun(perl, script, &result) != RPMRC_OK) 01951 rc = 1; 01952 else { 01953 if (result == NULL) result = "FIXME"; 01954 if (result != NULL && *result != '\0') { 01955 size_t len = strlen(result); 01956 if (len > mb->nb) 01957 len = mb->nb; 01958 memcpy(mb->t, result, len); 01959 mb->t += len; 01960 mb->nb -= len; 01961 } 01962 } 01963 perl = rpmperlFree(perl); 01964 av = _free(av); 01965 script = _free(script); 01966 s = se; 01967 continue; 01968 } 01969 #endif 01970 01971 #ifdef WITH_PYTHONEMBED 01972 if (STREQ("python", f, fn)) { 01973 const char ** av = NULL; 01974 char * script = parseEmbedded(s, (size_t)(se-s), &av); 01975 rpmpython python = rpmpythonNew(av, _globalI); 01976 const char * result = NULL; 01977 01978 if (rpmpythonRun(python, script, &result) != RPMRC_OK) 01979 rc = 1; 01980 else { 01981 if (result == NULL) result = "FIXME"; 01982 if (result != NULL && *result != '\0') { 01983 size_t len = strlen(result); 01984 if (len > mb->nb) 01985 len = mb->nb; 01986 memcpy(mb->t, result, len); 01987 mb->t += len; 01988 mb->nb -= len; 01989 } 01990 } 01991 python = rpmpythonFree(python); 01992 av = _free(av); 01993 script = _free(script); 01994 s = se; 01995 continue; 01996 } 01997 #endif 01998 01999 #ifdef WITH_RUBYEMBED 02000 if (STREQ("ruby", f, fn)) { 02001 const char ** av = NULL; 02002 char * script = parseEmbedded(s, (size_t)(se-s), &av); 02003 rpmruby ruby = rpmrubyNew(av, _globalI); 02004 const char * result = NULL; 02005 02006 if (rpmrubyRun(ruby, script, &result) != RPMRC_OK) 02007 rc = 1; 02008 else { 02009 if (result == NULL) result = "FIXME"; 02010 if (result != NULL && *result != '\0') { 02011 size_t len = strlen(result); 02012 if (len > mb->nb) 02013 len = mb->nb; 02014 memcpy(mb->t, result, len); 02015 mb->t += len; 02016 mb->nb -= len; 02017 } 02018 } 02019 ruby = rpmrubyFree(ruby); 02020 av = _free(av); 02021 script = _free(script); 02022 s = se; 02023 continue; 02024 } 02025 #endif 02026 02027 #ifdef WITH_TCL 02028 if (STREQ("tcl", f, fn)) { 02029 const char ** av = NULL; 02030 char * script = parseEmbedded(s, (size_t)(se-s), &av); 02031 rpmtcl tcl = rpmtclNew(av, _globalI); 02032 const char * result = NULL; 02033 02034 if (rpmtclRun(tcl, script, &result) != RPMRC_OK) 02035 rc = 1; 02036 else if (result != NULL && *result != '\0') { 02037 size_t len = strlen(result); 02038 if (len > mb->nb) 02039 len = mb->nb; 02040 memcpy(mb->t, result, len); 02041 mb->t += len; 02042 mb->nb -= len; 02043 } 02044 tcl = rpmtclFree(tcl); 02045 av = _free(av); 02046 script = _free(script); 02047 s = se; 02048 continue; 02049 } 02050 #endif 02051 02052 /* Rewrite "%patchNN ..." as "%patch -P NN ..." and expand. */ 02053 if (lastc && fn > 5 && STREQ("patch", f, 5) && xisdigit((int)f[5])) { 02054 /*@-internalglobs@*/ /* FIX: verbose may be set */ 02055 doFoo(mb, negate, f, (lastc - f), NULL, 0); 02056 /*@=internalglobs@*/ 02057 s = lastc; 02058 continue; 02059 } 02060 02061 /* XXX necessary but clunky */ 02062 if (STREQ("basename", f, fn) || 02063 STREQ("dirname", f, fn) || 02064 STREQ("realpath", f, fn) || 02065 STREQ("getenv", f, fn) || 02066 STREQ("shrink", f, fn) || 02067 STREQ("suffix", f, fn) || 02068 STREQ("expand", f, fn) || 02069 STREQ("verbose", f, fn) || 02070 STREQ("uncompress", f, fn) || 02071 STREQ("mkstemp", f, fn) || 02072 STREQ("mkdtemp", f, fn) || 02073 STREQ("uuid", f, fn) || 02074 STREQ("url2path", f, fn) || 02075 STREQ("u2p", f, fn) || 02076 STREQ("S", f, fn) || 02077 STREQ("P", f, fn) || 02078 STREQ("F", f, fn)) { 02079 /*@-internalglobs@*/ /* FIX: verbose may be set */ 02080 doFoo(mb, negate, f, fn, g, gn); 02081 /*@=internalglobs@*/ 02082 s = se; 02083 continue; 02084 } 02085 02086 /* Expand defined macros */ 02087 mep = findEntry(mb->mc, f, fn); 02088 me = (mep ? *mep : NULL); 02089 02090 /* XXX Special processing for flags */ 02091 if (*f == '-') { 02092 if (me) 02093 me->used++; /* Mark macro as used */ 02094 if ((me == NULL && !negate) || /* Without -f, skip %{-f...} */ 02095 (me != NULL && negate)) { /* With -f, skip %{!-f...} */ 02096 s = se; 02097 continue; 02098 } 02099 02100 if (g && g < ge) { /* Expand X in %{-f:X} */ 02101 rc = expandT(mb, g, gn); 02102 } else 02103 if (me && me->body && *me->body) {/* Expand %{-f}/%{-f*} */ 02104 rc = expandT(mb, me->body, strlen(me->body)); 02105 } 02106 s = se; 02107 continue; 02108 } 02109 02110 /* XXX Special processing for macro existence */ 02111 if (chkexist) { 02112 if ((me == NULL && !negate) || /* Without -f, skip %{?f...} */ 02113 (me != NULL && negate)) { /* With -f, skip %{!?f...} */ 02114 s = se; 02115 continue; 02116 } 02117 if (g && g < ge) { /* Expand X in %{?f:X} */ 02118 rc = expandT(mb, g, gn); 02119 } else 02120 if (me && me->body && *me->body) { /* Expand %{?f}/%{?f*} */ 02121 rc = expandT(mb, me->body, strlen(me->body)); 02122 } 02123 s = se; 02124 continue; 02125 } 02126 02127 if (me == NULL) { /* leave unknown %... as is */ 02128 #if !defined(RPM_VENDOR_WINDRIVER_DEBUG) /* XXX usually disabled */ 02129 #if DEAD 02130 /* XXX hack to skip over empty arg list */ 02131 if (fn == 1 && *f == '*') { 02132 s = se; 02133 continue; 02134 } 02135 #endif 02136 /* XXX hack to permit non-overloaded %foo to be passed */ 02137 c = (int) '%'; /* XXX only need to save % */ 02138 SAVECHAR(mb, c); 02139 #else 02140 if (!strncmp(f, "if", fn) || 02141 !strncmp(f, "else", fn) || 02142 !strncmp(f, "endif", fn)) { 02143 c = '%'; /* XXX only need to save % */ 02144 SAVECHAR(mb, c); 02145 } else { 02146 rpmlog(RPMLOG_ERR, 02147 _("Macro %%%.*s not found, skipping\n"), fn, f); 02148 s = se; 02149 } 02150 #endif 02151 continue; 02152 } 02153 02154 /* XXX Special processing to create a tuple from stack'd values. */ 02155 if (stackarray) { 02156 if (!(g && g < ge)) { 02157 g = "\n"; 02158 gn = strlen(g); 02159 } 02160 rc = expandFIFO(mb, me, g, gn); 02161 s = se; 02162 continue; 02163 } 02164 02165 /* Setup args for "%name " macros with opts */ 02166 if (me && me->opts != NULL) { 02167 if (lastc != NULL) { 02168 se = grabArgs(mb, me, fe, lastc); 02169 } else { 02170 addMacro(mb->mc, "**", NULL, "", mb->depth); 02171 addMacro(mb->mc, "*", NULL, "", mb->depth); 02172 addMacro(mb->mc, "#", NULL, "0", mb->depth); 02173 addMacro(mb->mc, "0", NULL, me->name, mb->depth); 02174 } 02175 } 02176 02177 /* Recursively expand body of macro */ 02178 if (me->body && *me->body) { 02179 mb->s = me->body; 02180 rc = expandMacro(mb); 02181 if (rc == 0) 02182 me->used++; /* Mark macro as used */ 02183 } 02184 02185 /* Free args for "%name " macros with opts */ 02186 if (me->opts != NULL) 02187 freeArgs(mb); 02188 02189 s = se; 02190 } 02191 02192 *mb->t = '\0'; 02193 mb->s = s; 02194 mb->depth--; 02195 if (rc != 0 || mb->expand_trace) 02196 printExpansion(mb, t, mb->t); 02197 return rc; 02198 } 02199 02200 #if defined(RPM_VENDOR_OPENPKG) /* stick-with-rpm-file-sanity-checking */ || \ 02201 !defined(POPT_ERROR_BADCONFIG) /* XXX POPT 1.15 retrofit */ 02202 int rpmSecuritySaneFile(const char *filename) 02203 { 02204 struct stat sb; 02205 uid_t uid; 02206 02207 if (stat(filename, &sb) == -1) 02208 return 1; 02209 uid = getuid(); 02210 if (sb.st_uid != uid) 02211 return 0; 02212 if (!S_ISREG(sb.st_mode)) 02213 return 0; 02214 if (sb.st_mode & (S_IWGRP|S_IWOTH)) 02215 return 0; 02216 return 1; 02217 } 02218 #endif 02219 02220 #if !defined(DEBUG_MACROS) 02221 /* =============================================================== */ 02222 /*@unchecked@*/ 02223 static int _debug = 0; 02224 02225 int rpmGlob(const char * patterns, int * argcPtr, const char *** argvPtr) 02226 { 02227 int ac = 0; 02228 const char ** av = NULL; 02229 int argc = 0; 02230 const char ** argv = NULL; 02231 char * globRoot = NULL; 02232 #ifdef ENABLE_NLS 02233 const char * old_collate = NULL; 02234 const char * old_ctype = NULL; 02235 const char * t; 02236 #endif 02237 size_t maxb, nb; 02238 size_t i; 02239 int j; 02240 int rc; 02241 02242 rc = XpoptParseArgvString(patterns, &ac, &av); 02243 if (rc) 02244 return rc; 02245 #ifdef ENABLE_NLS 02246 t = setlocale(LC_COLLATE, NULL); 02247 if (t) 02248 old_collate = xstrdup(t); 02249 t = setlocale(LC_CTYPE, NULL); 02250 if (t) 02251 old_ctype = xstrdup(t); 02252 (void) setlocale(LC_COLLATE, "C"); 02253 (void) setlocale(LC_CTYPE, "C"); 02254 #endif 02255 02256 if (av != NULL) 02257 for (j = 0; j < ac; j++) { 02258 const char * globURL; 02259 const char * path; 02260 int ut = urlPath(av[j], &path); 02261 glob_t gl; 02262 02263 if (!Glob_pattern_p(av[j], 0) && strchr(path, '~') == NULL) { 02264 argv = xrealloc(argv, (argc+2) * sizeof(*argv)); 02265 argv[argc] = xstrdup(av[j]); 02266 if (_debug) 02267 fprintf(stderr, "*** rpmGlob argv[%d] \"%s\"\n", argc, argv[argc]); 02268 argc++; 02269 continue; 02270 } 02271 02272 gl.gl_pathc = 0; 02273 gl.gl_pathv = NULL; 02274 rc = Glob(av[j], GLOB_TILDE, Glob_error, &gl); 02275 if (rc) 02276 goto exit; 02277 02278 /* XXX Prepend the URL leader for globs that have stripped it off */ 02279 maxb = 0; 02280 for (i = 0; i < gl.gl_pathc; i++) { 02281 if ((nb = strlen(&(gl.gl_pathv[i][0]))) > maxb) 02282 maxb = nb; 02283 } 02284 02285 nb = ((ut == URL_IS_PATH) ? (path - av[j]) : 0); 02286 maxb += nb; 02287 maxb += 1; 02288 globURL = globRoot = xmalloc(maxb); 02289 02290 switch (ut) { 02291 case URL_IS_PATH: 02292 case URL_IS_DASH: 02293 strncpy(globRoot, av[j], nb); 02294 /*@switchbreak@*/ break; 02295 case URL_IS_HTTPS: 02296 case URL_IS_HTTP: 02297 case URL_IS_FTP: 02298 case URL_IS_HKP: 02299 case URL_IS_UNKNOWN: 02300 default: 02301 /*@switchbreak@*/ break; 02302 } 02303 globRoot += nb; 02304 *globRoot = '\0'; 02305 if (_debug) 02306 fprintf(stderr, "*** GLOB maxb %d diskURL %d %*s globURL %p %s\n", (int)maxb, (int)nb, (int)nb, av[j], globURL, globURL); 02307 02308 argv = xrealloc(argv, (argc+gl.gl_pathc+1) * sizeof(*argv)); 02309 02310 if (argv != NULL) 02311 for (i = 0; i < gl.gl_pathc; i++) { 02312 const char * globFile = &(gl.gl_pathv[i][0]); 02313 if (globRoot > globURL && globRoot[-1] == '/') 02314 while (*globFile == '/') globFile++; 02315 strcpy(globRoot, globFile); 02316 if (_debug) 02317 fprintf(stderr, "*** rpmGlob argv[%d] \"%s\"\n", argc, globURL); 02318 argv[argc++] = xstrdup(globURL); 02319 } 02320 /*@-immediatetrans@*/ 02321 Globfree(&gl); 02322 /*@=immediatetrans@*/ 02323 globURL = _free(globURL); 02324 } 02325 02326 if (argv != NULL && argc > 0) { 02327 argv[argc] = NULL; 02328 if (argvPtr) 02329 *argvPtr = argv; 02330 if (argcPtr) 02331 *argcPtr = argc; 02332 rc = 0; 02333 } else 02334 rc = 1; 02335 02336 02337 exit: 02338 #ifdef ENABLE_NLS 02339 if (old_collate) { 02340 (void) setlocale(LC_COLLATE, old_collate); 02341 old_collate = _free(old_collate); 02342 } 02343 if (old_ctype) { 02344 (void) setlocale(LC_CTYPE, old_ctype); 02345 old_ctype = _free(old_ctype); 02346 } 02347 #endif 02348 av = _free(av); 02349 if (rc || argvPtr == NULL) { 02350 /*@-dependenttrans -unqualifiedtrans@*/ 02351 if (argv != NULL) 02352 for (j = 0; j < argc; j++) 02353 argv[j] = _free(argv[j]); 02354 argv = _free(argv); 02355 /*@=dependenttrans =unqualifiedtrans@*/ 02356 } 02357 return rc; 02358 } 02359 #endif /* !defined(DEBUG_MACROS) */ 02360 02361 /* =============================================================== */ 02362 02363 int 02364 expandMacros(void * spec, MacroContext mc, char * sbuf, size_t slen) 02365 { 02366 MacroBuf mb = alloca(sizeof(*mb)); 02367 char *tbuf; 02368 int rc; 02369 02370 if (sbuf == NULL || slen == 0) 02371 return 0; 02372 if (mc == NULL) mc = rpmGlobalMacroContext; 02373 02374 tbuf = alloca(slen + 1); 02375 tbuf[0] = '\0'; 02376 02377 mb->s = sbuf; 02378 mb->t = tbuf; 02379 mb->nb = slen; 02380 mb->depth = 0; 02381 mb->macro_trace = print_macro_trace; 02382 mb->expand_trace = print_expand_trace; 02383 02384 mb->spec = spec; /* (future) %file expansion info */ 02385 mb->mc = mc; 02386 02387 rc = expandMacro(mb); 02388 02389 tbuf[slen] = '\0'; 02390 if (mb->nb == 0) 02391 rpmlog(RPMLOG_ERR, _("Macro expansion too big for target buffer\n")); 02392 else 02393 strncpy(sbuf, tbuf, (slen - mb->nb + 1)); 02394 02395 return rc; 02396 } 02397 02398 void 02399 addMacro(MacroContext mc, 02400 const char * n, const char * o, const char * b, int level) 02401 { 02402 MacroEntry * mep; 02403 const char * name = n; 02404 02405 if (*name == '.') /* XXX readonly macros */ 02406 name++; 02407 if (*name == '.') /* XXX readonly macros */ 02408 name++; 02409 02410 if (mc == NULL) mc = rpmGlobalMacroContext; 02411 02412 /* If new name, expand macro table */ 02413 if ((mep = findEntry(mc, name, 0)) == NULL) { 02414 if (mc->firstFree == mc->macrosAllocated) 02415 expandMacroTable(mc); 02416 if (mc->macroTable != NULL) 02417 mep = mc->macroTable + mc->firstFree++; 02418 } 02419 02420 if (mep != NULL) { 02421 /* XXX permit "..foo" to be pushed over ".foo" */ 02422 if (*mep && (*mep)->flags && !(n[0] == '.' && n[1] == '.')) { 02423 /* XXX avoid error message for %buildroot */ 02424 if (strcmp((*mep)->name, "buildroot")) 02425 rpmlog(RPMLOG_ERR, _("Macro '%s' is readonly and cannot be changed.\n"), n); 02426 return; 02427 } 02428 /* Push macro over previous definition */ 02429 pushMacro(mep, n, o, b, level); 02430 02431 /* If new name, sort macro table */ 02432 if ((*mep)->prev == NULL) 02433 sortMacroTable(mc); 02434 } 02435 } 02436 02437 void 02438 delMacro(MacroContext mc, const char * n) 02439 { 02440 MacroEntry * mep; 02441 02442 if (mc == NULL) mc = rpmGlobalMacroContext; 02443 /* If name exists, pop entry */ 02444 if ((mep = findEntry(mc, n, 0)) != NULL) { 02445 popMacro(mep); 02446 /* If deleted name, sort macro table */ 02447 if (!(mep && *mep)) 02448 sortMacroTable(mc); 02449 } 02450 } 02451 02452 /*@-mustmod@*/ /* LCL: mc is modified through mb->mc, mb is abstract */ 02453 int 02454 rpmDefineMacro(MacroContext mc, const char * macro, int level) 02455 { 02456 MacroBuf mb = alloca(sizeof(*mb)); 02457 02458 memset(mb, 0, sizeof(*mb)); 02459 /* XXX just enough to get by */ 02460 mb->mc = (mc ? mc : rpmGlobalMacroContext); 02461 (void) doDefine(mb, macro, level, 0); 02462 return 0; 02463 } 02464 /*@=mustmod@*/ 02465 02466 /*@-mustmod@*/ /* LCL: mc is modified through mb->mc, mb is abstract */ 02467 int 02468 rpmUndefineMacro(MacroContext mc, const char * macro) 02469 { 02470 (void) doUndefine(mc ? mc : rpmGlobalMacroContext, macro); 02471 return 0; 02472 } 02473 /*@=mustmod@*/ 02474 02475 void 02476 rpmLoadMacros(MacroContext mc, int level) 02477 { 02478 02479 if (mc == NULL || mc == rpmGlobalMacroContext) 02480 return; 02481 02482 if (mc->macroTable != NULL) { 02483 int i; 02484 for (i = 0; i < mc->firstFree; i++) { 02485 MacroEntry *mep, me; 02486 mep = &mc->macroTable[i]; 02487 me = *mep; 02488 02489 if (me == NULL) /* XXX this should never happen */ 02490 continue; 02491 addMacro(NULL, me->name, me->opts, me->body, (level - 1)); 02492 } 02493 } 02494 } 02495 02496 #if defined(RPM_VENDOR_OPENPKG) /* expand-macrosfile-macro */ 02497 static void expand_macrosfile_macro(const char *file_name, const char *buf, size_t bufn) 02498 { 02499 char *cp; 02500 size_t l, k; 02501 static const char *macro_name = "%{macrosfile}"; 02502 02503 l = strlen(macro_name); 02504 k = strlen(file_name); 02505 while ((cp = strstr(buf, macro_name)) != NULL) { 02506 if (((strlen(buf) - l) + k) < bufn) { 02507 memmove(cp+k, cp+l, strlen(cp+l)+1); 02508 memcpy(cp, file_name, k); 02509 } 02510 } 02511 return; 02512 } 02513 #endif 02514 02515 int 02516 rpmLoadMacroFile(MacroContext mc, const char * fn) 02517 { 02518 /* XXX TODO: teach rdcl() to read through a URI, eliminate ".fpio". */ 02519 FD_t fd = Fopen(fn, "r.fpio"); 02520 size_t bufn = _macro_BUFSIZ; 02521 char *buf = alloca(bufn); 02522 int rc = -1; 02523 02524 if (fd == NULL || Ferror(fd)) { 02525 if (fd) (void) Fclose(fd); 02526 return rc; 02527 } 02528 02529 /* XXX Assume new fangled macro expansion */ 02530 /*@-mods@*/ 02531 max_macro_depth = _MAX_MACRO_DEPTH; 02532 /*@=mods@*/ 02533 02534 buf[0] = '\0'; 02535 while(rdcl(buf, bufn, fd) != NULL) { 02536 char *n; 02537 int c; 02538 02539 n = buf; 02540 SKIPBLANK(n, c); 02541 02542 if (c != (int) '%') 02543 continue; 02544 n++; /* skip % */ 02545 #if defined(RPM_VENDOR_OPENPKG) /* expand-macro-source */ 02546 expand_macrosfile_macro(fn, buf, bufn); 02547 #endif 02548 rc = rpmDefineMacro(mc, n, RMIL_MACROFILES); 02549 } 02550 rc = Fclose(fd); 02551 return rc; 02552 } 02553 02554 void 02555 rpmInitMacros(MacroContext mc, const char * macrofiles) 02556 { 02557 char *mfiles, *m, *me; 02558 02559 if (macrofiles == NULL) 02560 return; 02561 #ifdef DYING 02562 if (mc == NULL) mc = rpmGlobalMacroContext; 02563 #endif 02564 02565 mfiles = xstrdup(macrofiles); 02566 for (m = mfiles; m && *m != '\0'; m = me) { 02567 const char ** av; 02568 int ac; 02569 int i; 02570 02571 for (me = m; (me = strchr(me, ':')) != NULL; me++) { 02572 /* Skip over URI's. */ 02573 if (!(me[1] == '/' && me[2] == '/')) 02574 /*@innerbreak@*/ break; 02575 } 02576 02577 if (me && *me == ':') 02578 *me++ = '\0'; 02579 else 02580 me = m + strlen(m); 02581 02582 /* Glob expand the macro file path element, expanding ~ to $HOME. */ 02583 ac = 0; 02584 av = NULL; 02585 #if defined(DEBUG_MACROS) 02586 ac = 1; 02587 av = xmalloc((ac + 1) * sizeof(*av)); 02588 av[0] = strdup(m); 02589 av[1] = NULL; 02590 #else 02591 i = rpmGlob(m, &ac, &av); 02592 if (i != 0) 02593 continue; 02594 #endif 02595 02596 /* Read macros from each file. */ 02597 02598 for (i = 0; i < ac; i++) { 02599 size_t slen = strlen(av[i]); 02600 const char *fn = av[i]; 02601 02602 if (fn[0] == '@' /* attention */) { 02603 fn++; 02604 #if defined(RPM_VENDOR_OPENPKG) /* stick-with-rpm-file-sanity-checking */ || \ 02605 !defined(POPT_ERROR_BADCONFIG) /* XXX POPT 1.15 retrofit */ 02606 if (!rpmSecuritySaneFile(fn)) 02607 #else 02608 if (!poptSaneFile(fn)) 02609 #endif 02610 { 02611 rpmlog(RPMLOG_WARNING, "existing RPM macros file \"%s\" considered INSECURE -- not loaded\n", fn); 02612 /*@innercontinue@*/ continue; 02613 } 02614 } 02615 02616 /* Skip backup files and %config leftovers. */ 02617 #define _suffix(_s, _x) \ 02618 (slen >= sizeof(_x) && !strcmp((_s)+slen-(sizeof(_x)-1), (_x))) 02619 if (!(_suffix(fn, "~") 02620 || _suffix(fn, ".rpmnew") 02621 || _suffix(fn, ".rpmorig") 02622 || _suffix(fn, ".rpmsave")) 02623 ) 02624 (void) rpmLoadMacroFile(mc, fn); 02625 #undef _suffix 02626 02627 av[i] = _free(av[i]); 02628 } 02629 av = _free(av); 02630 } 02631 mfiles = _free(mfiles); 02632 02633 /* Reload cmdline macros */ 02634 /*@-mods@*/ 02635 rpmLoadMacros(rpmCLIMacroContext, RMIL_CMDLINE); 02636 /*@=mods@*/ 02637 } 02638 02639 /*@-globstate@*/ 02640 void 02641 rpmFreeMacros(MacroContext mc) 02642 { 02643 02644 if (mc == NULL) mc = rpmGlobalMacroContext; 02645 02646 if (mc->macroTable != NULL) { 02647 int i; 02648 for (i = 0; i < mc->firstFree; i++) { 02649 MacroEntry me; 02650 while ((me = mc->macroTable[i]) != NULL) { 02651 /* XXX cast to workaround const */ 02652 /*@-onlytrans@*/ 02653 if ((mc->macroTable[i] = me->prev) == NULL) 02654 me->name = _free(me->name); 02655 /*@=onlytrans@*/ 02656 me->opts = _free(me->opts); 02657 me->body = _free(me->body); 02658 me = _free(me); 02659 } 02660 } 02661 mc->macroTable = _free(mc->macroTable); 02662 } 02663 memset(mc, 0, sizeof(*mc)); 02664 } 02665 /*@=globstate@*/ 02666 02667 /* =============================================================== */ 02668 int isCompressed(const char * file, rpmCompressedMagic * compressed) 02669 { 02670 FD_t fd; 02671 ssize_t nb; 02672 int rc = -1; 02673 unsigned char magic[13]; 02674 #if defined(RPM_VENDOR_OPENPKG) || defined(RPM_VENDOR_FEDORA) || defined(RPM_VENDOR_MANDRIVA) /* extension-based-compression-detection */ 02675 size_t file_len; 02676 #endif 02677 02678 *compressed = COMPRESSED_NOT; 02679 02680 #if defined(RPM_VENDOR_OPENPKG) || defined(RPM_VENDOR_FEDORA) || defined(RPM_VENDOR_MANDRIVA) /* extension-based-compression-detection */ 02681 file_len = strlen(file); 02682 if ((file_len > 4 && strcasecmp(file+file_len-4, ".tbz") == 0) 02683 || (file_len > 4 && strcasecmp(file+file_len-4, ".bz2") == 0)) { 02684 *compressed = COMPRESSED_BZIP2; 02685 return 0; 02686 } else 02687 if (file_len > 4 && strcasecmp(file+file_len-4, ".zip") == 0) { 02688 *compressed = COMPRESSED_ZIP; 02689 return 0; 02690 } else 02691 if ((file_len > 4 && strcasecmp(file+file_len-4, ".tlz") == 0) 02692 || (file_len > 5 && strcasecmp(file+file_len-5, ".lzma") == 0)) { 02693 *compressed = COMPRESSED_LZMA; 02694 return 0; 02695 } else 02696 if (file_len > 4 && strcasecmp(file+file_len-3, ".xz") == 0) { 02697 *compressed = COMPRESSED_XZ; 02698 return 0; 02699 } else 02700 if ((file_len > 4 && strcasecmp(file+file_len-4, ".tgz") == 0) 02701 || (file_len > 3 && strcasecmp(file+file_len-3, ".gz") == 0) 02702 || (file_len > 2 && strcasecmp(file+file_len-2, ".Z") == 0)) { 02703 *compressed = COMPRESSED_OTHER; 02704 return 0; 02705 } else 02706 if (file_len > 5 && strcasecmp(file+file_len-5, ".cpio") == 0) { 02707 *compressed = COMPRESSED_NOT; 02708 return 0; 02709 } else 02710 if (file_len > 4 && strcasecmp(file+file_len-4, ".tar") == 0) { 02711 *compressed = COMPRESSED_NOT; 02712 return 0; 02713 } 02714 #endif 02715 02716 fd = Fopen(file, "r"); 02717 if (fd == NULL || Ferror(fd)) { 02718 /* XXX Fstrerror */ 02719 rpmlog(RPMLOG_ERR, _("File %s: %s\n"), file, Fstrerror(fd)); 02720 if (fd) (void) Fclose(fd); 02721 return 1; 02722 } 02723 nb = Fread(magic, sizeof(magic[0]), sizeof(magic), fd); 02724 if (nb < (ssize_t)0) { 02725 rpmlog(RPMLOG_ERR, _("File %s: %s\n"), file, Fstrerror(fd)); 02726 rc = 1; 02727 } else if (nb < (ssize_t)sizeof(magic)) { 02728 rpmlog(RPMLOG_ERR, _("File %s is smaller than %u bytes\n"), 02729 file, (unsigned)sizeof(magic)); 02730 rc = 0; 02731 } 02732 (void) Fclose(fd); 02733 if (rc >= 0) 02734 return rc; 02735 02736 rc = 0; 02737 02738 if (magic[0] == 'B' && magic[1] == 'Z') 02739 *compressed = COMPRESSED_BZIP2; 02740 else 02741 if (magic[0] == (unsigned char) 0120 && magic[1] == (unsigned char) 0113 02742 && magic[2] == (unsigned char) 0003 && magic[3] == (unsigned char) 0004) /* pkzip */ 02743 *compressed = COMPRESSED_ZIP; 02744 else 02745 if (magic[0] == (unsigned char) 0x89 && magic[1] == 'L' 02746 && magic[2] == 'Z' && magic[3] == 'O') /* lzop */ 02747 *compressed = COMPRESSED_LZOP; 02748 else 02749 #if !defined(RPM_VENDOR_OPENPKG) && !defined(RPM_VENDOR_FEDORA) && !defined(RPM_VENDOR_MANDRIVA) /* extension-based-compression-detection */ 02750 /* XXX Ick, LZMA has no magic. See http://lkml.org/lkml/2005/6/13/285 */ 02751 if (magic[ 9] == (unsigned char) 0x00 && magic[10] == (unsigned char) 0x00 && 02752 magic[11] == (unsigned char) 0x00 && magic[12] == (unsigned char) 0x00) /* lzmash */ 02753 *compressed = COMPRESSED_LZMA; 02754 else 02755 #endif 02756 #if defined(RPM_VENDOR_OPENSUSE) 02757 if (magic[0] == 0135 && magic[1] == 0 && magic[2] == 0) { /* lzma */ 02758 *compressed = COMPRESSED_LZMA; 02759 else 02760 #endif 02761 if (magic[0] == (unsigned char) 0xFD && magic[1] == 0x37 && magic[2] == 0x7A 02762 && magic[3] == 0x58 && magic[4] == 0x5A && magic[5] == 0x00) /* xz */ 02763 *compressed = COMPRESSED_XZ; 02764 else 02765 if ((magic[0] == (unsigned char) 0037 && magic[1] == (unsigned char) 0213) /* gzip */ 02766 || (magic[0] == (unsigned char) 0037 && magic[1] == (unsigned char) 0236) /* old gzip */ 02767 || (magic[0] == (unsigned char) 0037 && magic[1] == (unsigned char) 0036) /* pack */ 02768 || (magic[0] == (unsigned char) 0037 && magic[1] == (unsigned char) 0240) /* SCO lzh */ 02769 || (magic[0] == (unsigned char) 0037 && magic[1] == (unsigned char) 0235)) /* compress */ 02770 *compressed = COMPRESSED_OTHER; 02771 02772 return rc; 02773 } 02774 02775 /* =============================================================== */ 02776 02777 /*@-modfilesys@*/ 02778 /* XXX TODO: merge rpmExpand and rpmMCExpand. gud enuf for now ... */ 02779 char * 02780 rpmExpand(const char *arg, ...) 02781 { 02782 MacroContext mc = NULL; 02783 const char *s; 02784 char *t, *te; 02785 size_t sn, tn; 02786 size_t bufn = 8 * _macro_BUFSIZ; 02787 02788 va_list ap; 02789 02790 if (arg == NULL) 02791 return xstrdup(""); 02792 02793 t = xmalloc(bufn + strlen(arg) + 1); 02794 *t = '\0'; 02795 te = stpcpy(t, arg); 02796 02797 va_start(ap, arg); 02798 while ((s = va_arg(ap, const char *)) != NULL) { 02799 sn = strlen(s); 02800 tn = (te - t); 02801 t = xrealloc(t, tn + sn + bufn + 1); 02802 te = t + tn; 02803 te = stpcpy(te, s); 02804 } 02805 va_end(ap); 02806 02807 *te = '\0'; 02808 tn = (te - t); 02809 (void) expandMacros(NULL, mc, t, tn + bufn + 1); 02810 t[tn + bufn] = '\0'; 02811 t = xrealloc(t, strlen(t) + 1); 02812 02813 return t; 02814 } 02815 02816 char * 02817 rpmMCExpand(MacroContext mc, const char *arg, ...) 02818 { 02819 const char *s; 02820 char *t, *te; 02821 size_t sn, tn; 02822 size_t bufn = 8 * _macro_BUFSIZ; 02823 02824 va_list ap; 02825 02826 if (arg == NULL) 02827 return xstrdup(""); 02828 02829 t = xmalloc(bufn + strlen(arg) + 1); 02830 *t = '\0'; 02831 te = stpcpy(t, arg); 02832 02833 va_start(ap, arg); 02834 while ((s = va_arg(ap, const char *)) != NULL) { 02835 sn = strlen(s); 02836 tn = (te - t); 02837 t = xrealloc(t, tn + sn + bufn + 1); 02838 te = t + tn; 02839 te = stpcpy(te, s); 02840 } 02841 va_end(ap); 02842 02843 *te = '\0'; 02844 tn = (te - t); 02845 (void) expandMacros(NULL, mc, t, tn + bufn + 1); 02846 t[tn + bufn] = '\0'; 02847 t = xrealloc(t, strlen(t) + 1); 02848 02849 return t; 02850 } 02851 /*@=modfilesys@*/ 02852 02853 int 02854 rpmExpandNumeric(const char *arg) 02855 { 02856 const char *val; 02857 int rc; 02858 02859 if (arg == NULL) 02860 return 0; 02861 02862 val = rpmExpand(arg, NULL); 02863 if (!(val && *val != '%')) 02864 rc = 0; 02865 else if (*val == 'Y' || *val == 'y') 02866 rc = 1; 02867 else if (*val == 'N' || *val == 'n') 02868 rc = 0; 02869 else { 02870 char *end; 02871 rc = strtol(val, &end, 0); 02872 if (!(end && *end == '\0')) 02873 rc = 0; 02874 } 02875 val = _free(val); 02876 02877 return rc; 02878 } 02879 02880 /* @todo "../sbin/./../bin/" not correct. */ 02881 char *rpmCleanPath(char * path) 02882 { 02883 const char *s; 02884 char *se, *t, *te; 02885 int begin = 1; 02886 02887 if (path == NULL) 02888 return NULL; 02889 02890 /*fprintf(stderr, "*** RCP %s ->\n", path); */ 02891 s = t = te = path; 02892 while (*s != '\0') { 02893 /*fprintf(stderr, "*** got \"%.*s\"\trest \"%s\"\n", (t-path), path, s); */ 02894 switch(*s) { 02895 case ':': /* handle url's */ 02896 if (s[1] == '/' && s[2] == '/') { 02897 *t++ = *s++; 02898 *t++ = *s++; 02899 /* XXX handle "file:///" */ 02900 if (s[0] == '/') *t++ = *s++; 02901 te = t; 02902 /*@switchbreak@*/ break; 02903 } 02904 begin=1; 02905 /*@switchbreak@*/ break; 02906 case '/': 02907 /* Move parent dir forward */ 02908 for (se = te + 1; se < t && *se != '/'; se++) 02909 {}; 02910 if (se < t && *se == '/') { 02911 te = se; 02912 /*fprintf(stderr, "*** next pdir \"%.*s\"\n", (te-path), path); */ 02913 } 02914 while (s[1] == '/') 02915 s++; 02916 while (t > te && t[-1] == '/') 02917 t--; 02918 /*@switchbreak@*/ break; 02919 case '.': 02920 /* Leading .. is special */ 02921 /* Check that it is ../, so that we don't interpret */ 02922 /* ..?(i.e. "...") or ..* (i.e. "..bogus") as "..". */ 02923 /* in the case of "...", this ends up being processed*/ 02924 /* as "../.", and the last '.' is stripped. This */ 02925 /* would not be correct processing. */ 02926 if (begin && s[1] == '.' && (s[2] == '/' || s[2] == '\0')) { 02927 /*fprintf(stderr, " leading \"..\"\n"); */ 02928 *t++ = *s++; 02929 /*@switchbreak@*/ break; 02930 } 02931 /* Single . is special */ 02932 if (begin && s[1] == '\0') { 02933 /*@switchbreak@*/ break; 02934 } 02935 if (t > path && t[-1] == '/') 02936 switch (s[1]) { 02937 case '/': s++; /*@fallthrough@*/ /* Trim embedded ./ */ 02938 case '\0': s++; continue; /* Trim trailing /. */ 02939 default: /*@innerbreak@*/ break; 02940 } 02941 /* Trim embedded /../ and trailing /.. */ 02942 if (!begin && t > path && t[-1] == '/' && s[1] == '.' && (s[2] == '/' || s[2] == '\0')) { 02943 t = te; 02944 /* Move parent dir forward */ 02945 if (te > path) 02946 for (--te; te > path && *te != '/'; te--) 02947 {}; 02948 /*fprintf(stderr, "*** prev pdir \"%.*s\"\n", (te-path), path); */ 02949 s++; 02950 s++; 02951 continue; 02952 } 02953 /*@switchbreak@*/ break; 02954 default: 02955 begin = 0; 02956 /*@switchbreak@*/ break; 02957 } 02958 *t++ = *s++; 02959 } 02960 02961 /* Trim trailing / (but leave single / alone) */ 02962 if (t > &path[1] && t[-1] == '/') 02963 t--; 02964 *t = '\0'; 02965 02966 /*fprintf(stderr, "\t%s\n", path); */ 02967 return path; 02968 } 02969 02970 /* Return concatenated and expanded canonical path. */ 02971 02972 const char * 02973 rpmGetPath(const char *path, ...) 02974 { 02975 size_t bufn = _macro_BUFSIZ; 02976 char *buf = alloca(bufn); 02977 const char * s; 02978 char * t, * te; 02979 va_list ap; 02980 02981 if (path == NULL) 02982 return xstrdup(""); 02983 02984 buf[0] = '\0'; 02985 t = buf; 02986 te = stpcpy(t, path); 02987 *te = '\0'; 02988 02989 va_start(ap, path); 02990 while ((s = va_arg(ap, const char *)) != NULL) { 02991 te = stpcpy(te, s); 02992 *te = '\0'; 02993 } 02994 va_end(ap); 02995 /*@-modfilesys@*/ 02996 (void) expandMacros(NULL, NULL, buf, bufn); 02997 /*@=modfilesys@*/ 02998 02999 (void) rpmCleanPath(buf); 03000 return xstrdup(buf); /* XXX xstrdup has side effects. */ 03001 } 03002 03003 /* Merge 3 args into path, any or all of which may be a url. */ 03004 03005 const char * rpmGenPath(const char * urlroot, const char * urlmdir, 03006 const char *urlfile) 03007 { 03008 /*@owned@*/ const char * xroot = rpmGetPath(urlroot, NULL); 03009 /*@dependent@*/ const char * root = xroot; 03010 /*@owned@*/ const char * xmdir = rpmGetPath(urlmdir, NULL); 03011 /*@dependent@*/ const char * mdir = xmdir; 03012 /*@owned@*/ const char * xfile = rpmGetPath(urlfile, NULL); 03013 /*@dependent@*/ const char * file = xfile; 03014 const char * result; 03015 const char * url = NULL; 03016 int nurl = 0; 03017 int ut; 03018 03019 #if 0 03020 if (_debug) fprintf(stderr, "*** RGP xroot %s xmdir %s xfile %s\n", xroot, xmdir, xfile); 03021 #endif 03022 ut = urlPath(xroot, &root); 03023 if (url == NULL && ut > URL_IS_DASH) { 03024 url = xroot; 03025 nurl = root - xroot; 03026 #if 0 03027 if (_debug) fprintf(stderr, "*** RGP ut %d root %s nurl %d\n", ut, root, nurl); 03028 #endif 03029 } 03030 if (root == NULL || *root == '\0') root = "/"; 03031 03032 ut = urlPath(xmdir, &mdir); 03033 if (url == NULL && ut > URL_IS_DASH) { 03034 url = xmdir; 03035 nurl = mdir - xmdir; 03036 #if 0 03037 if (_debug) fprintf(stderr, "*** RGP ut %d mdir %s nurl %d\n", ut, mdir, nurl); 03038 #endif 03039 } 03040 if (mdir == NULL || *mdir == '\0') mdir = "/"; 03041 03042 ut = urlPath(xfile, &file); 03043 if (url == NULL && ut > URL_IS_DASH) { 03044 url = xfile; 03045 nurl = file - xfile; 03046 #if 0 03047 if (_debug) fprintf(stderr, "*** RGP ut %d file %s nurl %d\n", ut, file, nurl); 03048 #endif 03049 } 03050 03051 if (url && nurl > 0) { 03052 char *t = strncpy(alloca(nurl+1), url, nurl); 03053 t[nurl] = '\0'; 03054 url = t; 03055 } else 03056 url = ""; 03057 03058 result = rpmGetPath(url, root, "/", mdir, "/", file, NULL); 03059 03060 xroot = _free(xroot); 03061 xmdir = _free(xmdir); 03062 xfile = _free(xfile); 03063 #if 0 03064 if (_debug) fprintf(stderr, "*** RGP result %s\n", result); 03065 #endif 03066 return result; 03067 } 03068 03069 /* =============================================================== */ 03070 03071 #if defined(DEBUG_MACROS) 03072 03073 #if defined(EVAL_MACROS) 03074 03075 const char *rpmMacrofiles = MACROFILES; 03076 03077 int 03078 main(int argc, char *argv[]) 03079 { 03080 int c; 03081 int errflg = 0; 03082 extern char *optarg; 03083 extern int optind; 03084 03085 while ((c = getopt(argc, argv, "f:")) != EOF ) { 03086 switch (c) { 03087 case 'f': 03088 rpmMacrofiles = optarg; 03089 break; 03090 case '?': 03091 default: 03092 errflg++; 03093 break; 03094 } 03095 } 03096 if (errflg || optind >= argc) { 03097 fprintf(stderr, "Usage: %s [-f macropath ] macro ...\n", argv[0]); 03098 exit(1); 03099 } 03100 03101 rpmInitMacros(NULL, rpmMacrofiles); 03102 /* XXX getopt(3) also used for parametrized macros, expect scwewiness. */ 03103 for ( ; optind < argc; optind++) { 03104 const char *val; 03105 03106 val = rpmExpand(argv[optind], NULL); 03107 if (val) { 03108 fprintf(stdout, "%s:\t%s\n", argv[optind], val); 03109 val = _free(val); 03110 } 03111 } 03112 rpmFreeMacros(NULL); 03113 return 0; 03114 } 03115 03116 #else /* !EVAL_MACROS */ 03117 03118 const char *rpmMacrofiles = "../macros:./testmacros"; 03119 const char *testfile = "./test"; 03120 03121 int 03122 main(int argc, char *argv[]) 03123 { 03124 size_t bufn = _macro_BUFSIZ; 03125 char *buf = alloca(bufn); 03126 FILE *fp; 03127 int x; 03128 03129 rpmInitMacros(NULL, rpmMacrofiles); 03130 03131 if ((fp = fopen(testfile, "r")) != NULL) { 03132 while(rdcl(buf, bufn, fp)) { 03133 x = expandMacros(NULL, NULL, buf, bufn); 03134 fprintf(stderr, "%d->%s\n", x, buf); 03135 memset(buf, 0, bufn); 03136 } 03137 fclose(fp); 03138 } 03139 03140 while(rdcl(buf, bufn, stdin)) { 03141 x = expandMacros(NULL, NULL, buf, bufn); 03142 fprintf(stderr, "%d->%s\n <-\n", x, buf); 03143 memset(buf, 0, bufn); 03144 } 03145 rpmFreeMacros(NULL); 03146 03147 return 0; 03148 } 03149 #endif /* EVAL_MACROS */ 03150 #endif /* DEBUG_MACROS */