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