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