|
|
1.1 ! root 1: /* ! 2: * Expansion - quoting, separation, substitution, globbing ! 3: */ ! 4: ! 5: static char *RCSid = "$Header: /newbits/usr/bin/korn/eval.c.save,v 1.1 91/04/12 10:48:22 bin Exp $"; ! 6: ! 7: #include <stddef.h> ! 8: #include <stdio.h> ! 9: #include <string.h> ! 10: #include <errno.h> ! 11: #include <setjmp.h> ! 12: #include <unistd.h> ! 13: #include <sys/types.h> ! 14: #include <dirent.h> ! 15: #include <pwd.h> ! 16: #include "sh.h" ! 17: #include "lex.h" ! 18: #include "tree.h" ! 19: #include "table.h" ! 20: #include "expand.h" ! 21: ! 22: /* ! 23: * string expansion ! 24: * ! 25: * first pass: quoting, IFS separation, ${} and $() substitution. ! 26: * second pass: filename expansion (*?[]~). ! 27: */ ! 28: ! 29: /* expansion generator state */ ! 30: typedef struct Expand { ! 31: /* int type; */ /* see expand() */ ! 32: char *str; /* string */ ! 33: union { ! 34: char **strv; /* string[] */ ! 35: FILE *file; /* file */ ! 36: } u; /* source */ ! 37: short split; /* split "$@"*/ ! 38: } Expand; ! 39: ! 40: #define XBASE 0 /* scanning original */ ! 41: #define XSUB 1 /* expanding ${} string */ ! 42: #define XARGSEP 2 /* ifs0 between "$@" */ ! 43: #define XARG 3 /* expanding $*, $@ */ ! 44: #define XCOM 4 /* expanding $() */ ! 45: ! 46: static void expand ARGS((char *, XPtrV *, int)); ! 47: static int comsub ARGS((Expand *, char *comm)); ! 48: static int varsub ARGS((Expand *, char *name, int stype)); ! 49: static void glob ARGS((char *cp, XPtrV *wp)); ! 50: static void globit ARGS((char *ds, char *dp, char *sp, XPtrV *wp, int check)); ! 51: static char *tilde ARGS((char *wp)); ! 52: static char *trimsub ARGS((char *str, char *pat, int how)); ! 53: ! 54: int ifs0 = ' '; /* todo: first char of $IFS */ ! 55: ! 56: /* compile and expand word */ ! 57: char * ! 58: substitute(cp, f) ! 59: char Const *cp; ! 60: int f; ! 61: { ! 62: struct source *s, *sold; ! 63: ! 64: sold = source; ! 65: s = pushs(SWSTR); ! 66: s->str = (char *) cp; ! 67: source = s; ! 68: if (yylex(ONEWORD) != LWORD) ! 69: errorf("eval:substitute error\n"); ! 70: source = sold; ! 71: return evalstr(yylval.cp, f); ! 72: } ! 73: ! 74: /* ! 75: * expand arg-list ! 76: */ ! 77: char ** ! 78: eval(ap, f) ! 79: register char **ap; ! 80: { ! 81: XPtrV w; ! 82: ! 83: if (*ap == NULL) ! 84: return ap; ! 85: XPinit(w, 32); ! 86: XPput(w, NULL); /* space for shell name */ ! 87: while (*ap != NULL) ! 88: expand(*ap++, &w, f); ! 89: XPput(w, NULL); ! 90: return (char **) XPclose(w) + 1; ! 91: } ! 92: ! 93: /* ! 94: * expand string ! 95: */ ! 96: char * ! 97: evalstr(cp, f) ! 98: register char *cp; ! 99: int f; ! 100: { ! 101: XPtrV w; ! 102: ! 103: XPinit(w, 1); ! 104: expand(cp, &w, f); ! 105: cp = (XPsize(w) == 0) ? "" : (char*) *XPptrv(w); ! 106: XPfree(w); ! 107: return cp; ! 108: } ! 109: ! 110: /* for nested substitution: ${var:=$var2} */ ! 111: typedef struct SubType { ! 112: short type; /* [=+-?%#] action after expanded word */ ! 113: short base; /* begin position of expanded word */ ! 114: char *name; /* name for ${var=word} */ ! 115: } SubType; ! 116: ! 117: static void ! 118: expand(cp, wp, f) ! 119: char *cp; /* input word */ ! 120: register XPtrV *wp; /* output words */ ! 121: int f; /* DO* flags */ ! 122: { ! 123: register int c; ! 124: register int type = XBASE; /* expansion type */ ! 125: register int quote = 0; /* quoted */ ! 126: XString ds; /* destination string */ ! 127: register char *dp, *sp; /* dest., source */ ! 128: int fdo, word, combase; /* second pass flags; have word */ ! 129: Expand x; /* expansion variables */ ! 130: SubType subtype [10]; /* substitution type stack */ ! 131: register SubType *st = subtype + 10; ! 132: ! 133: if (cp == NULL) ! 134: errorf("eval:expand(NULL)\n"); ! 135: if (flag[FNOGLOB]) ! 136: f &= ~ DOGLOB; ! 137: ! 138: Xinit(ds, dp, 128); /* init dest. string */ ! 139: type = XBASE; ! 140: sp = cp; ! 141: fdo = 0; ! 142: word = !(f&DOBLANK); ! 143: ! 144: while (1) { ! 145: Xcheck(ds, dp); ! 146: ! 147: switch (type) { ! 148: case XBASE: /* original prefixed string */ ! 149: c = *sp++; ! 150: switch (c) { ! 151: case EOS: ! 152: c = 0; ! 153: break; ! 154: case CHAR: ! 155: c = *sp++; ! 156: break; ! 157: case QCHAR: ! 158: quote |= 2; /* temporary quote */ ! 159: c = *sp++; ! 160: break; ! 161: case OQUOTE: ! 162: word = quote = 1; ! 163: continue; ! 164: case CQUOTE: ! 165: quote = 0; ! 166: continue; ! 167: case COMSUB: ! 168: type = comsub(&x, sp); ! 169: sp = strchr(sp, 0) + 1; ! 170: combase = Xsavepos(ds, dp); ! 171: continue; ! 172: case OSUBST: /* ${var{:}[=+-?]word} */ ! 173: cp = sp; /* variable */ ! 174: sp = strchr(sp, 0) + 1; /* skip variable */ ! 175: c = (*sp == CSUBST) ? 0 : *sp++; ! 176: if ((c&0x7F) == '#' || (c&0x7F) == '%') ! 177: f |= DOPAT; ! 178: type = varsub(&x, cp, c); ! 179: if (type == XBASE) { /* expand? */ ! 180: if (st == subtype) ! 181: errorf("ridiculous ${} nesting\n"); ! 182: --st; ! 183: st->type = c; ! 184: st->base = Xsavepos(ds, dp); ! 185: st->name = cp; ! 186: } else ! 187: /* todo: nested OSUBST/CSUBST */ ! 188: sp = wdscan(sp, CSUBST); /* skip word */ ! 189: continue; ! 190: case CSUBST: /* only get here if expanding word */ ! 191: *dp = 0; ! 192: if (f&DOGLOB) ! 193: f &= ~DOPAT; ! 194: switch (st->type&0x7F) { ! 195: case '#': ! 196: case '%': ! 197: *dp = 0; ! 198: dp = Xrestpos(ds, dp, st->base); ! 199: x.str = trimsub(x.str, dp, st->type); ! 200: type = XSUB; ! 201: continue; ! 202: case '=': ! 203: #if 0 ! 204: if ((x.u.vp->flag&RDONLY)) ! 205: errorf("cannot set readonly %s\n", cp); ! 206: #endif ! 207: setstr(global(st->name), Xrestpos(ds, dp, st->base)); ! 208: break; ! 209: case '?': ! 210: if (dp == Xrestpos(ds, dp, st->base)) ! 211: errorf("missing value for %s\n", cp); ! 212: else ! 213: errorf("%s\n", Xrestpos(ds, dp, st->base)); ! 214: } ! 215: st++; ! 216: type = XBASE; ! 217: continue; ! 218: } ! 219: break; ! 220: ! 221: case XSUB: ! 222: if ((c = *x.str++) == 0) { ! 223: type = XBASE; ! 224: continue; ! 225: } ! 226: break; ! 227: ! 228: case XARGSEP: ! 229: type = XARG; ! 230: quote = 1; ! 231: case XARG: ! 232: if ((c = *x.str++) == 0) { ! 233: if ((x.str = *x.u.strv++) == NULL) { ! 234: type = XBASE; ! 235: continue; ! 236: } else if (quote && x.split) { ! 237: /* terminate word for "$@" */ ! 238: type = XARGSEP; ! 239: quote = 0; ! 240: } ! 241: c = ifs0; ! 242: } ! 243: break; ! 244: ! 245: case XCOM: ! 246: c = getc(x.u.file); ! 247: if (quote) { ! 248: if (c == EOF) { ! 249: cp = Xrestpos(ds, sp, combase); ! 250: for (dp--; dp >= cp && *dp == '\n'; dp--) ! 251: ; ! 252: dp++; ! 253: fclose(x.u.file); ! 254: if (x.split) ! 255: waitlast(); ! 256: type = XBASE; ! 257: continue; ! 258: } ! 259: } else { /* this part is probably redundant */ ! 260: if (c == EOF || c == '\n') { ! 261: while ((c = getc(x.u.file)) == '\n') ! 262: ; ! 263: if (c == EOF) { ! 264: fclose(x.u.file); ! 265: if (x.split) ! 266: waitlast(); ! 267: type = XBASE; ! 268: continue; ! 269: } ! 270: ungetc(c, x.u.file); ! 271: c = ifs0; ! 272: } ! 273: } ! 274: break; ! 275: } ! 276: ! 277: /* check for end of word or IFS separation */ ! 278: if (c == 0 || !quote && (f&DOBLANK) && ctype(c, C_IFS)) { ! 279: if (word) { ! 280: *dp++ = 0; ! 281: cp = Xclose(ds, dp); ! 282: if (fdo&DOTILDE) ! 283: cp = tilde(cp); ! 284: if (fdo&DOGLOB) ! 285: glob(cp, wp); ! 286: else ! 287: {XPput(*wp, cp);} ! 288: fdo = word = 0; ! 289: if (c != 0) ! 290: Xinit(ds, dp, 128); ! 291: } else ! 292: ; /* ignore IFS */ ! 293: if (c == 0) ! 294: return; ! 295: } else { ! 296: /* mark any special second pass chars */ ! 297: if (!quote) ! 298: switch (c) { ! 299: case '*': ! 300: case '?': ! 301: case '[': ! 302: if (f&(DOPAT|DOGLOB)) { ! 303: fdo |= (f&DOGLOB); ! 304: *dp++ = MAGIC; ! 305: } ! 306: break; ! 307: case '~': ! 308: if ((f&DOTILDE) && dp == Xstring(ds, dp) || ! 309: !(f&DOBLANK) && ! 310: (dp[-1] == '=' || dp[-1] == ':')) { ! 311: fdo |= DOTILDE; ! 312: *dp++ = MAGIC; ! 313: } ! 314: break; ! 315: } ! 316: else ! 317: quote &= ~2; /* undo temporary */ ! 318: ! 319: word = 1; ! 320: *dp++ = c; /* save output char */ ! 321: } ! 322: } ! 323: } ! 324: ! 325: /* ! 326: * Prepare to generate the string returned by ${} substitution. ! 327: */ ! 328: static int ! 329: varsub(xp, sp, stype) ! 330: register Expand *xp; ! 331: register char *sp; ! 332: int stype; ! 333: { ! 334: register int c; ! 335: int type; ! 336: ! 337: /* ${#var}, string length or argc */ ! 338: if (sp[0] == '#' && (c = sp[1]) != 0) { ! 339: c = (c == '*' || c == '@') ? e.loc->argc : ! 340: strlen(strval(global(sp+1))); ! 341: xp->str = strsave(ulton((unsigned long)c, 10), ATEMP); ! 342: return XSUB; ! 343: } ! 344: ! 345: c = sp[0]; ! 346: if (c == '*' || c == '@') { ! 347: if (e.loc->argc == 0) { ! 348: xp->str = null; ! 349: type = XSUB; ! 350: } else { ! 351: xp->u.strv = e.loc->argv + 1; ! 352: xp->str = *xp->u.strv++; ! 353: xp->split = c == '@'; /* $@ */ ! 354: type = XARG; ! 355: } ! 356: } else { ! 357: xp->str = strval(global(sp)); ! 358: type = XSUB; ! 359: } ! 360: ! 361: c = stype&0x7F; ! 362: /* test the compiler's code generator */ ! 363: if (c == '%' || c == '#' || ! 364: (((stype&0x80) ? *xp->str=='\0' : xp->str==null) ? /* undef? */ ! 365: c == '=' || c == '-' || c == '?' : c == '+')) ! 366: type = XBASE; /* expand word instead of variable value */ ! 367: if (type != XBASE && flag[FNOUNSET] && xp->str == null) ! 368: errorf("%s: unset variable\n", sp); ! 369: return type; ! 370: } ! 371: ! 372: /* ! 373: * Run the command in $(...) and read its output. ! 374: */ ! 375: static int ! 376: comsub(xp, cp) ! 377: register Expand *xp; ! 378: char *cp; ! 379: { ! 380: Source *s; ! 381: register struct op *t; ! 382: FILE *fi; ! 383: ! 384: s = pushs(SSTRING); ! 385: s->str = cp; ! 386: t = compile(s); ! 387: ! 388: if (t != NULL && t->type == TCOM && /* $(<file) */ ! 389: *t->args == NULL && *t->vars == NULL && t->ioact != NULL) { ! 390: register struct ioword *io = *t->ioact; ! 391: ! 392: if (io->flag != IOREAD) ! 393: errorf("funny $() command\n"); ! 394: fi = fopen(evalstr(io->name, DOTILDE), "r"); ! 395: if (fi != NULL) ! 396: fileno(fi) = savefd(fileno(fi)); ! 397: xp->split = 0; /* no waitlast() */ ! 398: } else { ! 399: int ofd1, pv[2]; ! 400: openpipe(pv); ! 401: fi = fdopen(pv[0], "r"); ! 402: ofd1 = savefd(1); ! 403: dup2(pv[1], 1); ! 404: close(pv[1]); ! 405: #if 0 ! 406: exchild(t, XXCOM|XPIPEO); ! 407: #else ! 408: execute(t, XFORK|XXCOM|XPIPEO); ! 409: #endif ! 410: dup2(ofd1, 1); ! 411: xp->split = 1; /* waitlast() */ ! 412: } ! 413: ! 414: if (fi == NULL) ! 415: errorf("cannot open $() input\n"); ! 416: setvbuf(fi, (char *)NULL, _IOFBF, BUFSIZ); ! 417: xp->u.file = fi; ! 418: return XCOM; ! 419: } ! 420: ! 421: /* ! 422: * perform #pattern and %pattern substitution in ${} ! 423: */ ! 424: ! 425: static char * ! 426: trimsub(str, pat, how) ! 427: register char *str; ! 428: char *pat; ! 429: int how; ! 430: { ! 431: register char *end = strchr(str, 0); ! 432: register char *p, c; ! 433: ! 434: switch (how) { ! 435: case '#': /* shortest at begin */ ! 436: for (p = str; p <= end; p++) { ! 437: c = *p; *p = '\0'; ! 438: if (gmatch(str, pat)) { ! 439: *p = c; ! 440: return p; ! 441: } ! 442: *p = c; ! 443: } ! 444: break; ! 445: case '#'|0x80: /* longest match at begin */ ! 446: for (p = end; p >= str; p--) { ! 447: c = *p; *p = '\0'; ! 448: if (gmatch(str, pat)) { ! 449: *p = c; ! 450: return p; ! 451: } ! 452: *p = c; ! 453: } ! 454: break; ! 455: case '%': /* shortest match at end */ ! 456: for (p = end; p >= str; p--) { ! 457: if (gmatch(p, pat)) { ! 458: *p = '\0'; ! 459: return str; ! 460: } ! 461: } ! 462: break; ! 463: case '%'|0x80: /* longest match at end */ ! 464: for (p = str; p <= end; p++) { ! 465: if (gmatch(p, pat)) { ! 466: *p = '\0'; ! 467: return str; ! 468: } ! 469: } ! 470: break; ! 471: } ! 472: ! 473: return str; /* no match, return string */ ! 474: } ! 475: ! 476: /* ! 477: * glob ! 478: * Name derived from V6's /etc/glob, the program that expanded filenames. ! 479: */ ! 480: ! 481: static char *debunk(); ! 482: ! 483: static void ! 484: glob(cp, wp) ! 485: char *cp; ! 486: register XPtrV *wp; ! 487: { ! 488: char path [PATH]; ! 489: register char *sp = cp; ! 490: int oldsize; ! 491: ! 492: oldsize = XPsize(*wp); ! 493: globit(path, path, sp, wp, 0); ! 494: ! 495: if (XPsize(*wp) == oldsize) ! 496: {XPput(*wp, debunk(cp));} ! 497: else ! 498: qsortp(XPptrv(*wp) + oldsize, (size_t)(XPsize(*wp) - oldsize), xstrcmp); ! 499: } ! 500: ! 501: static void ! 502: globit(ds, dp, sp, wp, check) ! 503: char *ds; /* dest path */ ! 504: char *dp; /* dest end */ ! 505: char *sp; /* source path */ ! 506: register XPtrV *wp; /* output list */ ! 507: int check; /* check dest existence */ ! 508: { ! 509: register char *np; /* next source component */ ! 510: register char *tsp, *tdp; ! 511: ! 512: if (sp == NULL) { /* end of source path */ ! 513: if (check && eaccess(ds, 0) < 0) ! 514: return; ! 515: XPput(*wp, strsave(ds, ATEMP)); ! 516: return; ! 517: } ! 518: ! 519: if (dp > ds) ! 520: *dp++ = '/'; ! 521: while (*sp == '/') ! 522: *dp++ = *sp++; ! 523: np = strchr(sp, '/'); ! 524: if (np != NULL) ! 525: *np++ = 0; ! 526: ! 527: *dp = 0; ! 528: if (strchr(sp, MAGIC) == NULL) { /* contains no pattern? */ ! 529: tdp = dp; tsp = sp; ! 530: while ((*tdp++ = *tsp++) != 0) ! 531: ; ! 532: --tdp; ! 533: globit(ds, tdp, np, wp, check); ! 534: } else { ! 535: DIR *dirp; ! 536: struct dirent *d; ! 537: ! 538: dirp = opendir(ds); ! 539: if (dirp == NULL) ! 540: goto Nodir; ! 541: while ((d = readdir(dirp)) != NULL) { ! 542: tsp = d->d_name; ! 543: if (tsp[0] == '.' && ! 544: (tsp[1] == 0 || tsp[1] == '.' && tsp[2] == 0)) ! 545: continue; /* always ignore . and .. */ ! 546: if (*tsp == '.' && *sp != '.' || !gmatch(tsp, sp)) ! 547: continue; ! 548: ! 549: tdp = dp; ! 550: while ((*tdp++ = *tsp++) != 0) ! 551: ; ! 552: --tdp; ! 553: globit(ds, tdp, np, wp, np != NULL); ! 554: } ! 555: closedir(dirp); ! 556: Nodir:; ! 557: } ! 558: ! 559: if (np != NULL) ! 560: *--np = '/'; ! 561: } ! 562: ! 563: /* remove MAGIC from string */ ! 564: static char * ! 565: debunk(cp) ! 566: char *cp; ! 567: { ! 568: register char *dp, *sp; ! 569: ! 570: for (dp = sp = cp; *sp != 0; sp++) ! 571: if (*sp != MAGIC) ! 572: *dp++ = *sp; ! 573: *dp = 0; ! 574: return cp; ! 575: } ! 576: ! 577: /* ! 578: * tilde expansion ! 579: * ! 580: * based on a version by Arnold Robbins ! 581: */ ! 582: ! 583: static char *homedir(); ! 584: ! 585: static char * ! 586: tilde(acp) ! 587: char *acp; ! 588: { ! 589: register int c; ! 590: char path [PATH+1]; ! 591: register char *cp = acp, *wp = path, *dp; ! 592: char userid [16+1]; ! 593: ! 594: Again: ! 595: while (1) { ! 596: if ((c = *cp++) == 0) { ! 597: *wp = 0; ! 598: afree((Void*)acp, ATEMP); ! 599: return strsave(path, ATEMP); ! 600: } else if (c == MAGIC && *cp == '~') ! 601: break; ! 602: else ! 603: *wp++ = c; ! 604: } ! 605: ! 606: dp = NULL; /* no output substitution */ ! 607: if (cp[1] == 0 || cp[1] == '/' || cp[1] == ':') /* ~ or ~/ */ ! 608: dp = strval(global("HOME")), cp += 1; ! 609: else if (cp[1] == '+' && (cp[2] == '/' || cp[2] == ':' || cp[2] == 0)) ! 610: dp = strval(global("PWD")), cp += 2; ! 611: else if (cp[1] == '-' && (cp[2] == '/' || cp[2] == ':' || cp[2] == 0)) ! 612: dp = strval(global("OLDPWD")), cp += 2; ! 613: else if (letter(cp[1])) { ! 614: char *save = cp; ! 615: for (dp = userid, cp++; letnum(*cp) && dp < userid+16; ) ! 616: *dp++ = *cp++; ! 617: *dp = 0; ! 618: dp = homedir(userid); ! 619: if (dp == NULL) ! 620: cp = save; ! 621: } ! 622: /* substitute */ ! 623: if (dp != NULL) ! 624: while (*dp != 0) ! 625: *wp++ = *dp++; ! 626: goto Again; ! 627: } ! 628: ! 629: /* ! 630: * map userid to user's home directory. ! 631: * todo: implement a cache with the "homedirs" table. ! 632: * note that 4.3's getpw adds more than 6K to the shell, ! 633: * and the YP version probably adds much more. ! 634: * we might consider our own version of getpwnam() to keep the size down. ! 635: */ ! 636: ! 637: static char * ! 638: homedir(name) ! 639: char *name; ! 640: { ! 641: register struct tbl *ap; ! 642: register struct passwd *pw; ! 643: ! 644: ap = tsearch(&homedirs, name, hash(name)); ! 645: if ((ap != NULL && (ap->flag&ISSET))) ! 646: return ap->val.s; ! 647: pw = getpwnam(name); ! 648: if (pw == NULL) ! 649: return NULL; ! 650: return pw->pw_dir; ! 651: } ! 652:
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.