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