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