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