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