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