|
|
1.1 ! root 1: /* Copyright (c) 1979 Regents of the University of California */ ! 2: #include "sh.h" ! 3: ! 4: /* ! 5: * C Shell ! 6: * ! 7: * Bill Joy, UC Berkeley ! 8: * October, 1978 ! 9: */ ! 10: ! 11: char *pathlist[] = { SRCHPATH, 0 }; ! 12: ! 13: main(c, av) ! 14: int c; ! 15: char **av; ! 16: { ! 17: register char **v, *cp; ! 18: int nofile = 0; ! 19: int reenter = 0; ! 20: bool nverbose = 0, nexececho = 0, quitit = 0, fast = 0, prompt = 1; ! 21: char *hp; ! 22: ! 23: settimes(); /* Immed. estab. timing base */ ! 24: hp = getenv("HOME"); ! 25: v = av; ! 26: if (eq(v[0], "a.out")) /* A.out's are quittable */ ! 27: quitit = 1; ! 28: uid = getuid(); ! 29: #ifdef V6 ! 30: loginsh = eq(*v, "-"); /* To do .login/.logout */ ! 31: #else ! 32: loginsh = **v == '-'; ! 33: #endif ! 34: if (loginsh) ! 35: time(&chktim); ! 36: ! 37: /* ! 38: * Move the descriptors to safe places. ! 39: * The variable didfds is 0 while we have only FSH* to work with. ! 40: * When didfds is true, we have 0,1,2 and prefer to use these. ! 41: */ ! 42: initdesc(); ! 43: ! 44: /* ! 45: * Initialize the shell variables. ! 46: * ARGV and PROMPT are initialized later. ! 47: * STATUS is also munged in several places. ! 48: * CHILD is munged when forking/waiting ! 49: */ ! 50: ! 51: set("status", "0"); ! 52: if (hp == 0) ! 53: fast++; /* No home -> can't read scripts */ ! 54: else ! 55: set("home", hp); ! 56: if (uid == 0) ! 57: pathlist[0] = "/etc"; ! 58: set1("path", saveblk(pathlist), &shvhed); ! 59: /* ! 60: * Re-initialize path if set in environment ! 61: * ! 62: cp = getenv("PATH"); ! 63: if (cp != 0) { ! 64: register int i = 0; ! 65: register char *dp; ! 66: register char **pv; ! 67: ! 68: for (dp = cp; *dp; dp++) ! 69: if (*dp == ':') ! 70: i++; ! 71: pv = calloc(i+1, sizeof (char **)); ! 72: dp = cp; ! 73: i = 0; ! 74: while (*dp) { ! 75: if (*dp == ':') { ! 76: *dp = 0; ! 77: pv[i++] = savestr(cp); ! 78: *dp = ':'; ! 79: } else if (*dp == 0) { ! 80: pv[i++] = savestr(cp); ! 81: break; ! 82: } ! 83: dp++; ! 84: } ! 85: pv[i] = 0; ! 86: set1("path", pv, &shvhed); ! 87: } ! 88: */ ! 89: set("shell", SHELLPATH); ! 90: ! 91: doldol = putn(getpid()); /* For $$ */ ! 92: shtemp = strspl("/tmp/sh", doldol); /* For << */ ! 93: ! 94: /* ! 95: * Record the interrupt states from the parent process. ! 96: * If the parent is non-interruptible our hand must be forced ! 97: * or we (and our children) won't be either. ! 98: * Our children inherit termination from our parent. ! 99: * We catch it only if we are the login shell. ! 100: */ ! 101: parintr = signal(SIGINT, SIG_IGN); /* parents interruptibility */ ! 102: signal(SIGINT, parintr); /* ... restore */ ! 103: parterm = signal(SIGTERM, SIG_IGN); /* parents terminability */ ! 104: signal(SIGTERM, parterm); /* ... restore */ ! 105: ! 106: /* ! 107: * Process the arguments. ! 108: * ! 109: * Note that processing of -v/-x is actually delayed till after ! 110: * script processing. ! 111: * ! 112: * We set the first character of our name to be '-' if we are ! 113: * a shell running interruptible commands. Many programs which ! 114: * examine ps'es use this to filter such shells out. ! 115: */ ! 116: c--, v++; ! 117: while (c > 0 && (cp = v[0])[0] == '-') { ! 118: do switch (*cp++) { ! 119: ! 120: case 0: /* - Interruptible, no prompt */ ! 121: prompt = 0; ! 122: **av = '-'; ! 123: nofile++; ! 124: break; ! 125: ! 126: case 'c': /* -c Command input from arg */ ! 127: if (c == 1) ! 128: exit(0); ! 129: c--, v++; ! 130: arginp = v[0]; ! 131: prompt = 0; ! 132: nofile++; ! 133: break; ! 134: ! 135: case 'e': /* -e Exit on any error */ ! 136: exiterr++; ! 137: break; ! 138: ! 139: case 'f': /* -f Fast start */ ! 140: fast++; ! 141: break; ! 142: ! 143: case 'i': /* -i Interactive, even if !intty */ ! 144: intact++; ! 145: **av = '-'; ! 146: nofile++; ! 147: break; ! 148: ! 149: case 'n': /* -n Don't execute */ ! 150: noexec++; ! 151: break; ! 152: ! 153: case 'q': /* -q (Undoc'd) ... die on quit */ ! 154: quitit = 1; ! 155: break; ! 156: ! 157: case 's': /* -s Read from std input */ ! 158: nofile++; ! 159: if (isatty(SHIN)) ! 160: **v = '-'; ! 161: break; ! 162: ! 163: case 't': /* -t Read one line from input */ ! 164: onelflg = 2; ! 165: if (isatty(SHIN)) ! 166: **v = '-'; ! 167: prompt = 0; ! 168: nofile++; ! 169: break; ! 170: ! 171: case 'v': /* -v Echo hist expanded input */ ! 172: nverbose = 1; /* ... later */ ! 173: break; ! 174: ! 175: case 'x': /* -x Echo just before execution */ ! 176: nexececho = 1; /* ... later */ ! 177: break; ! 178: ! 179: case 'V': /* -V Echo hist expanded input */ ! 180: setNS("verbose"); /* NOW! */ ! 181: break; ! 182: ! 183: case 'X': /* -X Echo just before execution */ ! 184: setNS("echo"); /* NOW! */ ! 185: break; ! 186: ! 187: } while (*cp); ! 188: v++, c--; ! 189: } ! 190: ! 191: if (quitit) /* With all due haste, for debugging */ ! 192: signal(SIGQUIT, SIG_DFL); ! 193: ! 194: /* ! 195: * Unless prevented by -, -c, -i, -s, or -t, if there ! 196: * are remaining arguments the first of them is the name ! 197: * of a shell file from which to read commands. ! 198: */ ! 199: if (nofile == 0 && c > 0) { ! 200: nofile = open(v[0], 0); ! 201: if (nofile < 0) { ! 202: child++; /* So this ... */ ! 203: Perror(v[0]); /* ... doesn't return */ ! 204: } ! 205: file = v[0]; ! 206: SHIN = dmove(nofile, FSHIN); /* Replace FSHIN */ ! 207: prompt = 0; ! 208: c--, v++; ! 209: } ! 210: ! 211: /* ! 212: * Consider input a tty if it really is or we are interactive. ! 213: */ ! 214: intty = intact || isatty(SHIN); ! 215: #ifdef TELL ! 216: settell(); ! 217: #endif ! 218: /* ! 219: * Commands are interruptible if we are interactive ! 220: * or the process which created us was. ! 221: */ ! 222: if (intact || parintr == SIG_DFL) ! 223: **av = '-'; ! 224: ! 225: /* ! 226: * Save the remaining arguments in ARGV. ! 227: * Normally the system-supplied argument list is ok as ! 228: * a zero terminated value block. ! 229: * On some version 6 systems, it is -1 terminated and setting it ! 230: * to zero messes up "ps" so we change it to zero, copy ! 231: * the block of pointers, and put it back the way it was. ! 232: */ ! 233: /* ! 234: if (c == 0) ! 235: set("argv", 0); ! 236: else ! 237: */ ! 238: if ((int) v[c] == -1) { ! 239: /* ick */ ! 240: v[c] = 0, setq("argv", copyblk(v), &shvhed), v[c] = (char *) -1; ! 241: } else ! 242: setq("argv", v, &shvhed); ! 243: ! 244: /* ! 245: * Set up the prompt. ! 246: */ ! 247: if (prompt) ! 248: set("prompt", uid == 0 ? "# " : "% "); ! 249: ! 250: /* ! 251: * If we are an interactive shell, then start fiddling ! 252: * with the signals; this is a tricky game. ! 253: */ ! 254: if (**av == '-') { ! 255: setintr++; ! 256: if (!quitit) /* Wary! */ ! 257: signal(SIGQUIT, SIG_IGN); ! 258: signal(SIGINT, SIG_IGN); ! 259: signal(SIGTERM, SIG_IGN); ! 260: } ! 261: ! 262: /* ! 263: * Set an exit here in case of an interrupt or error reading ! 264: * the shell start-up scripts. ! 265: */ ! 266: setexit(); ! 267: haderr = 0; /* In case second time through */ ! 268: if (!fast && reenter == 0) { ! 269: reenter++; ! 270: /* Will have value("home") here because set fast if don't */ ! 271: srccat(value("home"), "/.cshrc"); ! 272: if (!fast && !arginp && !onelflg) ! 273: dohash(); ! 274: if (loginsh) ! 275: #ifdef NOHELP ! 276: srccat("", ".login"); ! 277: #else ! 278: srccat(value("home"), "/.login"); ! 279: #endif ! 280: } ! 281: ! 282: /* ! 283: * Now are ready for the -v and -x flags ! 284: */ ! 285: if (nverbose) ! 286: setNS("verbose"); ! 287: if (nexececho) ! 288: setNS("echo"); ! 289: ! 290: /* ! 291: * All the rest of the world is inside this call. ! 292: * The argument to process indicates whether it should ! 293: * catch "error unwinds". Thus if we are a interactive shell ! 294: * our call here will never return by being blown past on an error. ! 295: */ ! 296: process(setintr); ! 297: ! 298: /* ! 299: * Mop-up. ! 300: */ ! 301: if (loginsh) { ! 302: printf("logout\n"); ! 303: close(SHIN); ! 304: child++; ! 305: goodbye(); ! 306: } ! 307: exitstat(); ! 308: } ! 309: ! 310: /* ! 311: * Source to the file which is the catenation of the argument names. ! 312: */ ! 313: srccat(cp, dp) ! 314: char *cp, *dp; ! 315: { ! 316: register char *ep = strspl(cp, dp); ! 317: register int unit = dmove(open(ep, 0), -1); ! 318: ! 319: /* ioctl(unit, FIOCLEX, NULL); */ ! 320: xfree(ep); ! 321: srcunit(unit, 0); ! 322: } ! 323: ! 324: /* ! 325: * Source to a unit. If onlyown it must be our file or ! 326: * we don't chance it. This occurs on ".cshrc"s and the like. ! 327: */ ! 328: srcunit(unit, onlyown) ! 329: register int unit; ! 330: bool onlyown; ! 331: { ! 332: /* We have to push down a lot of state here */ ! 333: /* All this could go into a structure */ ! 334: int oSHIN = -1, oldintty = intty; ! 335: struct whyle *oldwhyl = whyles; ! 336: char *ogointr = gointr, *oarginp = arginp; ! 337: int oonelflg = onelflg; ! 338: #ifdef TELL ! 339: bool otell = cantell; ! 340: #endif ! 341: struct Bin saveB; ! 342: ! 343: /* The (few) real local variables */ ! 344: jmp_buf oldexit; ! 345: int reenter; ! 346: register int (*oldint)(); ! 347: ! 348: if (unit < 0) ! 349: return; ! 350: if (onlyown) { ! 351: struct stat stb; ! 352: ! 353: #ifdef CC ! 354: if (fstat(unit, &stb) < 0 || (stb.st_uid != uid && stb.st_uid != (uid &~ 0377))) { ! 355: #endif ! 356: #ifdef CORY ! 357: if (fstat(unit, &stb) < 0 || (stb.st_uid != uid && stb.st_uid != (uid &~ 0377))) { ! 358: #endif ! 359: #ifndef CC ! 360: #ifndef CORY ! 361: if (fstat(unit, &stb) < 0 || stb.st_uid != uid) { ! 362: #endif ! 363: #endif ! 364: close(unit); ! 365: return; ! 366: } ! 367: } ! 368: ! 369: /* ! 370: * There is a critical section here while we are pushing down the ! 371: * input stream since we have stuff in different structures. ! 372: * If we weren't careful an interrupt could corrupt SHIN's Bin ! 373: * structure and kill the shell. ! 374: * ! 375: * We could avoid the critical region by grouping all the stuff ! 376: * in a single structure and pointing at it to move it all at ! 377: * once. This is less efficient globally on many variable references ! 378: * however. ! 379: */ ! 380: getexit(oldexit); ! 381: reenter = 0; ! 382: oldint = signal(SIGINT, SIG_IGN); ! 383: setexit(); ! 384: reenter++; ! 385: if (reenter == 1) { ! 386: /* Setup the new values of the state stuff saved above */ ! 387: copy(&saveB, &B, sizeof saveB); ! 388: fbuf = (char **) 0; ! 389: fseekp = feobp = fblocks = 0; ! 390: oSHIN = SHIN, SHIN = unit, arginp = 0, onelflg = 0; ! 391: intty = isatty(SHIN), whyles = 0, gointr = 0; ! 392: /* ! 393: * Now if we are allowing commands to be interrupted, ! 394: * we let ourselves be interrupted. ! 395: */ ! 396: signal(SIGINT, setintr ? pintr : oldint); ! 397: #ifdef TELL ! 398: settell(); ! 399: #endif ! 400: process(0); /* 0 -> blow away on errors */ ! 401: } ! 402: signal(SIGINT, oldint); ! 403: if (oSHIN >= 0) { ! 404: register int i; ! 405: ! 406: /* We made it to the new state... free up its storage */ ! 407: /* This code could get run twice but xfree doesn't care */ ! 408: for (i = 0; i < fblocks; i++) ! 409: xfree(fbuf[i]); ! 410: xfree(fbuf); ! 411: ! 412: /* Reset input arena */ ! 413: copy(&B, &saveB, sizeof B); ! 414: ! 415: close(SHIN), SHIN = oSHIN; ! 416: arginp = oarginp, onelflg = oonelflg; ! 417: intty = oldintty, whyles = oldwhyl, gointr = ogointr; ! 418: #ifdef TELL ! 419: cantell = otell; ! 420: #endif ! 421: } ! 422: ! 423: resexit(oldexit); ! 424: /* ! 425: * If process reset() (effectively an unwind) then ! 426: * we must also unwind. ! 427: */ ! 428: if (reenter >= 2) ! 429: error(0); ! 430: } ! 431: ! 432: goodbye() ! 433: { ! 434: ! 435: if (loginsh) { ! 436: signal(SIGQUIT, SIG_IGN); ! 437: signal(SIGINT, SIG_IGN); ! 438: signal(SIGTERM, SIG_IGN); ! 439: setintr = 0; /* No interrupts after "logout" */ ! 440: if (adrof("home")) ! 441: srccat(value("home"), "/.logout"); ! 442: } ! 443: exitstat(); ! 444: } ! 445: ! 446: exitstat() ! 447: { ! 448: ! 449: /* ! 450: * Note that if STATUS is corrupted (i.e. getn bombs) ! 451: * then error will exit directly because we poke child here. ! 452: * Otherwise we might continue unwarrantedly (sic). ! 453: */ ! 454: child++; ! 455: exit(getn(value("status"))); ! 456: } ! 457: ! 458: /* ! 459: * Catch an interrupt, e.g. during lexical input. ! 460: * If we are an interactive shell, we reset the interrupt catch ! 461: * immediately. In any case we drain the shell output, ! 462: * and finally go through the normal error mechanism, which ! 463: * gets a chance to make the shell go away. ! 464: */ ! 465: pintr() ! 466: { ! 467: register char **v; ! 468: ! 469: if (setintr) ! 470: signal(SIGINT, SIG_IGN); ! 471: draino(); ! 472: ! 473: /* ! 474: * If we have an active "onintr" then we search for the label. ! 475: * Note that if one does "onintr -" then we shan't be interruptible ! 476: * so we needn't worry about that here. ! 477: */ ! 478: if (gointr) { ! 479: search(ZGOTO, 0, gointr); ! 480: timflg = 0; ! 481: if (v = pargv) ! 482: pargv = 0, blkfree(v); ! 483: if (v = gargv) ! 484: gargv = 0, blkfree(v); ! 485: reset(); ! 486: } else if (intty) ! 487: printf("\n"); /* Some like this, others don't */ ! 488: error(0); ! 489: } ! 490: ! 491: /* ! 492: * Process is the main driving routine for the shell. ! 493: * It runs all command processing, except for those within { ... } ! 494: * in expressions (which is run by a routine evalav in sh.exp.c which ! 495: * is a stripped down process), and `...` evaluation which is run ! 496: * also by a subset of this code in sh.glob.c in the routine backeval. ! 497: * ! 498: * The code here is a little strange because part of it is interruptible ! 499: * and hence freeing of structures appears to occur when none is necessary ! 500: * if this is ignored. ! 501: * ! 502: * Note that if catch is not set then we will unwind on any error. ! 503: * In an end-of-file occurs, we return. ! 504: */ ! 505: process(catch) ! 506: bool catch; ! 507: { ! 508: register char *cp; ! 509: jmp_buf osetexit; ! 510: struct wordent paraml; ! 511: struct command *t; ! 512: ! 513: getexit(osetexit); ! 514: for (;;) { ! 515: paraml.next = paraml.prev = ¶ml; ! 516: paraml.word = ""; ! 517: t = 0; ! 518: setexit(); ! 519: justpr = 0; /* A chance to execute */ ! 520: ! 521: /* ! 522: * Interruptible during interactive reads ! 523: */ ! 524: if (setintr) ! 525: signal(SIGINT, pintr); ! 526: ! 527: /* ! 528: * For the sake of reset() ! 529: */ ! 530: freelex(¶ml), freesyn(t), t = 0; ! 531: ! 532: if (haderr) { ! 533: if (!catch) { ! 534: /* unwind */ ! 535: doneinp = 0; ! 536: resexit(osetexit); ! 537: reset(); ! 538: } ! 539: haderr = 0; ! 540: /* ! 541: * Every error is eventually caught here or ! 542: * the shell dies. It is at this ! 543: * point that we clean up any left-over open ! 544: * files, by closing all but a fixed number ! 545: * of pre-defined files. Thus routines don't ! 546: * have to worry about leaving files open due ! 547: * to deeper errors... they will get closed here. ! 548: */ ! 549: closem(); ! 550: continue; ! 551: } ! 552: if (doneinp) { ! 553: doneinp = 0; ! 554: break; ! 555: } ! 556: if (intty) { ! 557: mailchk(); ! 558: /* ! 559: * If we are at the end of the input buffer ! 560: * then we are going to read fresh stuff. ! 561: * Otherwise, we are rereading input and don't ! 562: * need or want to prompt. ! 563: */ ! 564: if (fseekp == feobp) ! 565: if (!whyles) ! 566: for (cp = value("prompt"); *cp; cp++) ! 567: if (*cp == '!') ! 568: printf("%d", eventno + 1); ! 569: else { ! 570: if (*cp == '\\' && cp[1] == '!') ! 571: cp++; ! 572: putchar(*cp | QUOTE); ! 573: } ! 574: else ! 575: /* ! 576: * Prompt for forward reading loop ! 577: * body content. ! 578: */ ! 579: printf("? "); ! 580: flush(); ! 581: } ! 582: err = 0; ! 583: ! 584: /* ! 585: * Echo not only on VERBOSE, but also with history expansion. ! 586: * If there is a lexical error then we forego history echo. ! 587: */ ! 588: if (lex(¶ml) && !err && intty || adrof("verbose")) { ! 589: haderr = 1; ! 590: prlex(¶ml); ! 591: haderr = 0; ! 592: } ! 593: ! 594: /* ! 595: * The parser may lose space if interrupted. ! 596: */ ! 597: if (setintr) ! 598: signal(SIGINT, SIG_IGN); ! 599: ! 600: /* ! 601: * Save input text on the history list if it ! 602: * is from the terminal at the top level and not ! 603: * in a loop. ! 604: */ ! 605: if (catch && intty && !whyles) ! 606: savehist(¶ml); ! 607: ! 608: /* ! 609: * Print lexical error messages. ! 610: */ ! 611: if (err) ! 612: error(err); ! 613: ! 614: /* ! 615: * If had a history command :p modifier then ! 616: * this is as far as we should go ! 617: */ ! 618: if (justpr) ! 619: reset(); ! 620: ! 621: alias(¶ml); ! 622: ! 623: /* ! 624: * Parse the words of the input into a parse tree. ! 625: */ ! 626: t = syntax(paraml.next, ¶ml); ! 627: if (err) ! 628: error(err); ! 629: ! 630: /* ! 631: * Execute the parse tree ! 632: */ ! 633: execute(t); ! 634: ! 635: /* ! 636: * Made it! ! 637: */ ! 638: freelex(¶ml), freesyn(t); ! 639: } ! 640: resexit(osetexit); ! 641: } ! 642: ! 643: dosource(t) ! 644: register char **t; ! 645: { ! 646: register char *f; ! 647: register int u; ! 648: ! 649: t++; ! 650: f = globone(*t); ! 651: u = dmove(open(f, 0), -1); ! 652: xfree(f); ! 653: if (u < 0) ! 654: Perror(f); ! 655: didfds = 0; ! 656: srcunit(u, 0); ! 657: } ! 658: ! 659: /* ! 660: * Check for mail. ! 661: * If we are a login shell, then we don't want to tell ! 662: * about any mail file unless its been modified ! 663: * after the time we started. ! 664: * This prevents us from telling the user things he already ! 665: * knows, since the login program insist on saying ! 666: * "You have mail." ! 667: */ ! 668: mailchk() ! 669: { ! 670: register struct varent *v; ! 671: register char **vp; ! 672: time_t t; ! 673: int intvl, cnt; ! 674: ! 675: v = adrof("mail"); ! 676: if (v == 0) ! 677: return; ! 678: time(&t); ! 679: vp = v->vec; ! 680: cnt = blklen(vp); ! 681: intvl = (cnt && number(*vp)) ? (--cnt, getn(*vp++)) : MAILINTVL; ! 682: if (intvl < 1) ! 683: intvl = 1; ! 684: if (chktim + intvl > t) ! 685: return; ! 686: for (; *vp; vp++) { ! 687: bool new; ! 688: struct stat stb; ! 689: ! 690: if (stat(*vp, &stb) < 0) ! 691: continue; ! 692: /* ! 693: * We assume that a file has been read if the access time is ! 694: * greater than the mod time. ! 695: */ ! 696: #ifndef CORY ! 697: if (stb.st_size == 0) ! 698: continue; ! 699: #endif ! 700: if (stb.st_atime > stb.st_mtime || stb.st_atime < chktim) ! 701: continue; ! 702: new = stb.st_mtime > time0; ! 703: if (loginsh && !new) ! 704: continue; ! 705: if (cnt == 1) ! 706: printf("You have %smail.\n", new ? "new " : ""); ! 707: else ! 708: printf("%s in %s.\n", new ? "New mail" : "Mail", *vp); ! 709: } ! 710: chktim = t; ! 711: } ! 712: ! 713: #include <pwd.h> ! 714: /* ! 715: * Extract a home directory from the password file ! 716: * The argument points to a buffer where the name of the ! 717: * user whose home directory is sought is currently. ! 718: * We write the home directory of the user back there. ! 719: */ ! 720: gethdir(home) ! 721: char *home; ! 722: { ! 723: register struct passwd *pp = getpwnam(home); ! 724: ! 725: if (pp == 0) ! 726: return (1); ! 727: strcpy(home, pp->pw_dir); ! 728: return (0); ! 729: } ! 730: ! 731: /* ! 732: * Move the initial descriptors to their eventual ! 733: * resting places, closin all other units. ! 734: */ ! 735: initdesc() ! 736: { ! 737: ! 738: didcch = 0; /* Havent closed for child */ ! 739: didfds = 0; /* 0, 1, 2 aren't set up */ ! 740: SHIN = dcopy(0, FSHIN); ! 741: SHOUT = dcopy(1, FSHOUT); ! 742: SHDIAG = dcopy(2, FSHDIAG); ! 743: OLDSTD = dcopy(SHIN, FOLDSTD); ! 744: closem(); ! 745: } ! 746: ! 747: #ifndef V6 ! 748: exit(i) ! 749: int i; ! 750: { ! 751: ! 752: _exit(i); ! 753: } ! 754: #endif
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.