added subroutine setfile to make changing input files easy;
[unix-history] / usr / src / bin / csh / csh.c
CommitLineData
04d3b78c
BJ
1static char *sccsid = "@(#)csh.c 4.1 %G%";
2
3#include "sh.h"
4#include <sys/ioctl.h>
5/*
6 * C Shell
7 *
8 * Bill Joy, UC Berkeley, California, USA
9 * October 1978, May 1980
10 *
11 * Jim Kulp, IIASA, Laxenburg, Austria
12 * April 1980
13 */
14
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()
561{
562 register char **v;
563
564 if (setintr) {
565 sigrelse(SIGINT);
566 if (pjobs) {
567 pjobs = 0;
568 printf("\n");
569 dojobs(jobargv);
570 bferr("Interrupted");
571 }
572 }
573 if (setintr)
574 sighold(SIGINT);
575 sigrelse(SIGCHLD);
576 draino();
577
578 /*
579 * If we have an active "onintr" then we search for the label.
580 * Note that if one does "onintr -" then we shan't be interruptible
581 * so we needn't worry about that here.
582 */
583 if (gointr) {
584 search(ZGOTO, 0, gointr);
585 timflg = 0;
586 if (v = pargv)
587 pargv = 0, blkfree(v);
588 if (v = gargv)
589 gargv = 0, blkfree(v);
590 reset();
591 } else if (intty)
592 printf("\n"); /* Some like this, others don't */
593 error(NOSTR);
594}
595
596/*
597 * Process is the main driving routine for the shell.
598 * It runs all command processing, except for those within { ... }
599 * in expressions (which is run by a routine evalav in sh.exp.c which
600 * is a stripped down process), and `...` evaluation which is run
601 * also by a subset of this code in sh.glob.c in the routine backeval.
602 *
603 * The code here is a little strange because part of it is interruptible
604 * and hence freeing of structures appears to occur when none is necessary
605 * if this is ignored.
606 *
607 * Note that if catch is not set then we will unwind on any error.
608 * If an end-of-file occurs, we return.
609 */
610process(catch)
611 bool catch;
612{
613 register char *cp;
614 jmp_buf osetexit;
615 struct command *t;
616
617 getexit(osetexit);
618 for (;;) {
619 pendjob();
620 paraml.next = paraml.prev = &paraml;
621 paraml.word = "";
622 t = 0;
623 setexit();
624 justpr = 0; /* A chance to execute */
625
626 /*
627 * Interruptible during interactive reads
628 */
629 if (setintr)
630 sigrelse(SIGINT);
631
632 /*
633 * For the sake of reset()
634 */
635 freelex(&paraml), freesyn(t), t = 0;
636
637 if (haderr) {
638 if (!catch) {
639 /* unwind */
640 doneinp = 0;
641 resexit(osetexit);
642 reset();
643 }
644 haderr = 0;
645 /*
646 * Every error is eventually caught here or
647 * the shell dies. It is at this
648 * point that we clean up any left-over open
649 * files, by closing all but a fixed number
650 * of pre-defined files. Thus routines don't
651 * have to worry about leaving files open due
652 * to deeper errors... they will get closed here.
653 */
654 closem();
655 continue;
656 }
657 if (doneinp) {
658 doneinp = 0;
659 break;
660 }
661 if (chkstop)
662 chkstop--;
663 if (neednote)
664 pnote();
665 if (intty && evalvec == 0) {
666 mailchk();
667 /*
668 * If we are at the end of the input buffer
669 * then we are going to read fresh stuff.
670 * Otherwise, we are rereading input and don't
671 * need or want to prompt.
672 */
673 if (fseekp == feobp)
674 if (!whyles)
675 for (cp = value("prompt"); *cp; cp++)
676 if (*cp == HIST)
677 printf("%d", eventno + 1);
678 else {
679 if (*cp == '\\' && cp[1] == HIST)
680 cp++;
681 putchar(*cp | QUOTE);
682 }
683 else
684 /*
685 * Prompt for forward reading loop
686 * body content.
687 */
688 printf("? ");
689 flush();
690 }
691 err = 0;
692
693 /*
694 * Echo not only on VERBOSE, but also with history expansion.
695 * If there is a lexical error then we forego history echo.
696 */
697 if (lex(&paraml) && !err && intty || adrof("verbose")) {
698 haderr = 1;
699 prlex(&paraml);
700 haderr = 0;
701 }
702
703 /*
704 * The parser may lose space if interrupted.
705 */
706 if (setintr)
707 sighold(SIGINT);
708
709 /*
710 * Save input text on the history list if it
711 * is from the terminal at the top level and not
712 * in a loop.
713 */
714 if (catch && intty && !whyles)
715 savehist(&paraml);
716
717 /*
718 * Print lexical error messages.
719 */
720 if (err)
721 error(err);
722
723 /*
724 * If had a history command :p modifier then
725 * this is as far as we should go
726 */
727 if (justpr)
728 reset();
729
730 alias(&paraml);
731
732 /*
733 * Parse the words of the input into a parse tree.
734 */
735 t = syntax(paraml.next, &paraml, 0);
736 if (err)
737 error(err);
738
739 /*
740 * Execute the parse tree
741 */
742 execute(t, tpgrp);
743
744 /*
745 * Made it!
746 */
747 freelex(&paraml), freesyn(t);
748 }
749 resexit(osetexit);
750}
751
752dosource(t)
753 register char **t;
754{
755 register char *f;
756 register int u;
757
758 t++;
759 f = globone(*t);
760 u = dmove(open(f, 0), -1);
761 xfree(f);
762 if (u < 0)
763 Perror(f);
764 srcunit(u, 0);
765}
766
767/*
768 * Check for mail.
769 * If we are a login shell, then we don't want to tell
770 * about any mail file unless its been modified
771 * after the time we started.
772 * This prevents us from telling the user things he already
773 * knows, since the login program insists on saying
774 * "You have mail."
775 */
776mailchk()
777{
778 register struct varent *v;
779 register char **vp;
780 time_t t;
781 int intvl, cnt;
782 struct stat stb;
783 bool new;
784
785 v = adrof("mail");
786 if (v == 0)
787 return;
788 time(&t);
789 vp = v->vec;
790 cnt = blklen(vp);
791 intvl = (cnt && number(*vp)) ? (--cnt, getn(*vp++)) : MAILINTVL;
792 if (intvl < 1)
793 intvl = 1;
794 if (chktim + intvl > t)
795 return;
796 for (; *vp; vp++) {
797 if (stat(*vp, &stb) < 0)
798 continue;
799 new = stb.st_mtime > time0;
800 if (stb.st_size == 0 || stb.st_atime > stb.st_mtime ||
801 (stb.st_atime < chktim && stb.st_mtime < chktim) ||
802 loginsh && !new)
803 continue;
804 if (cnt == 1)
805 printf("You have %smail.\n", new ? "new " : "");
806 else
807 printf("%s in %s.\n", new ? "New mail" : "Mail", *vp);
808 }
809 chktim = t;
810}
811
812#include <pwd.h>
813/*
814 * Extract a home directory from the password file
815 * The argument points to a buffer where the name of the
816 * user whose home directory is sought is currently.
817 * We write the home directory of the user back there.
818 */
819gethdir(home)
820 char *home;
821{
822 register struct passwd *pp = getpwnam(home);
823
824 if (pp == 0)
825 return (1);
826 strcpy(home, pp->pw_dir);
827 return (0);
828}
829
830/*
831 * Move the initial descriptors to their eventual
832 * resting places, closin all other units.
833 */
834initdesc()
835{
836
837 didcch = 0; /* Havent closed for child */
838 didfds = 0; /* 0, 1, 2 aren't set up */
839 SHIN = dcopy(0, FSHIN);
840 SHOUT = dcopy(1, FSHOUT);
841 SHDIAG = dcopy(2, FSHDIAG);
842 OLDSTD = dcopy(SHIN, FOLDSTD);
843 closem();
844}
845
846exit(i)
847 int i;
848{
849
850 untty();
851#ifdef PROF
852 IEH3exit(i);
853#else
854 _exit(i);
855#endif
856}