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