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