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