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