|
|
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.3 (Berkeley) 3/29/86";
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: srcunit(unit, onlyown, hflg)
438: register int unit;
439: bool onlyown;
440: bool hflg;
441: {
442: /* We have to push down a lot of state here */
443: /* All this could go into a structure */
444: int oSHIN = -1, oldintty = intty;
445: struct whyle *oldwhyl = whyles;
446: char *ogointr = gointr, *oarginp = arginp;
447: char *oevalp = evalp, **oevalvec = evalvec;
448: int oonelflg = onelflg;
449: bool oenterhist = enterhist;
450: char OHIST = HIST;
451: #ifdef TELL
452: bool otell = cantell;
453: #endif
454: struct Bin saveB;
455:
456: /* The (few) real local variables */
457: jmp_buf oldexit;
458: int reenter, omask;
459:
460: if (unit < 0)
461: return;
462: if (didfds)
463: donefds();
464: if (onlyown) {
465: struct stat stb;
466:
467: if (fstat(unit, &stb) < 0 ||
468: (stb.st_uid != uid && stb.st_gid != getgid())) {
469: (void) close(unit);
470: return;
471: }
472: }
473:
474: /*
475: * There is a critical section here while we are pushing down the
476: * input stream since we have stuff in different structures.
477: * If we weren't careful an interrupt could corrupt SHIN's Bin
478: * structure and kill the shell.
479: *
480: * We could avoid the critical region by grouping all the stuff
481: * in a single structure and pointing at it to move it all at
482: * once. This is less efficient globally on many variable references
483: * however.
484: */
485: getexit(oldexit);
486: reenter = 0;
487: if (setintr)
488: omask = sigblock(sigmask(SIGINT));
489: setexit();
490: reenter++;
491: if (reenter == 1) {
492: /* Setup the new values of the state stuff saved above */
493: copy((char *)&saveB, (char *)&B, sizeof saveB);
494: fbuf = (char **) 0;
495: fseekp = feobp = fblocks = 0;
496: oSHIN = SHIN, SHIN = unit, arginp = 0, onelflg = 0;
497: intty = isatty(SHIN), whyles = 0, gointr = 0;
498: evalvec = 0; evalp = 0;
499: enterhist = hflg;
500: if (enterhist)
501: HIST = '\0';
502: /*
503: * Now if we are allowing commands to be interrupted,
504: * we let ourselves be interrupted.
505: */
506: if (setintr)
507: (void) sigsetmask(omask);
508: #ifdef TELL
509: settell();
510: #endif
511: process(0); /* 0 -> blow away on errors */
512: }
513: if (setintr)
514: (void) sigsetmask(omask);
515: if (oSHIN >= 0) {
516: register int i;
517:
518: /* We made it to the new state... free up its storage */
519: /* This code could get run twice but xfree doesn't care */
520: for (i = 0; i < fblocks; i++)
521: xfree(fbuf[i]);
522: xfree((char *)fbuf);
523:
524: /* Reset input arena */
525: copy((char *)&B, (char *)&saveB, sizeof B);
526:
527: (void) close(SHIN), SHIN = oSHIN;
528: arginp = oarginp, onelflg = oonelflg;
529: evalp = oevalp, evalvec = oevalvec;
530: intty = oldintty, whyles = oldwhyl, gointr = ogointr;
531: if (enterhist)
532: HIST = OHIST;
533: enterhist = oenterhist;
534: #ifdef TELL
535: cantell = otell;
536: #endif
537: }
538:
539: resexit(oldexit);
540: /*
541: * If process reset() (effectively an unwind) then
542: * we must also unwind.
543: */
544: if (reenter >= 2)
545: error(NOSTR);
546: }
547:
548: rechist()
549: {
550: char buf[BUFSIZ];
551: int fp, ftmp, oldidfds;
552:
553: if (!fast) {
554: if (value("savehist")[0] == '\0')
555: return;
556: (void) strcpy(buf, value("home"));
557: (void) strcat(buf, "/.history");
558: fp = creat(buf, 0666);
559: if (fp == -1)
560: return;
561: oldidfds = didfds;
562: didfds = 0;
563: ftmp = SHOUT;
564: SHOUT = fp;
565: (void) strcpy(buf, value("savehist"));
566: dumphist[2] = buf;
567: dohist(dumphist);
568: (void) close(fp);
569: SHOUT = ftmp;
570: didfds = oldidfds;
571: }
572: }
573:
574: goodbye()
575: {
576: if (loginsh) {
577: (void) signal(SIGQUIT, SIG_IGN);
578: (void) signal(SIGINT, SIG_IGN);
579: (void) signal(SIGTERM, SIG_IGN);
580: setintr = 0; /* No interrupts after "logout" */
581: if (adrof("home"))
582: srccat(value("home"), "/.logout");
583: }
584: rechist();
585: exitstat();
586: }
587:
588: exitstat()
589: {
590:
591: #ifdef PROF
592: monitor(0);
593: #endif
594: /*
595: * Note that if STATUS is corrupted (i.e. getn bombs)
596: * then error will exit directly because we poke child here.
597: * Otherwise we might continue unwarrantedly (sic).
598: */
599: child++;
600: exit(getn(value("status")));
601: }
602:
603: /*
604: * in the event of a HUP we want to save the history
605: */
606: phup()
607: {
608: rechist();
609: exit(1);
610: }
611:
612: char *jobargv[2] = { "jobs", 0 };
613: /*
614: * Catch an interrupt, e.g. during lexical input.
615: * If we are an interactive shell, we reset the interrupt catch
616: * immediately. In any case we drain the shell output,
617: * and finally go through the normal error mechanism, which
618: * gets a chance to make the shell go away.
619: */
620: pintr()
621: {
622: pintr1(1);
623: }
624:
625: pintr1(wantnl)
626: bool wantnl;
627: {
628: register char **v;
629: int omask;
630:
631: omask = sigblock(0);
632: if (setintr) {
633: (void) sigsetmask(omask & ~sigmask(SIGINT));
634: if (pjobs) {
635: pjobs = 0;
636: printf("\n");
637: dojobs(jobargv);
638: bferr("Interrupted");
639: }
640: }
641: (void) sigsetmask(omask & ~sigmask(SIGCHLD));
642: draino();
643:
644: /*
645: * If we have an active "onintr" then we search for the label.
646: * Note that if one does "onintr -" then we shan't be interruptible
647: * so we needn't worry about that here.
648: */
649: if (gointr) {
650: search(ZGOTO, 0, gointr);
651: timflg = 0;
652: if (v = pargv)
653: pargv = 0, blkfree(v);
654: if (v = gargv)
655: gargv = 0, blkfree(v);
656: reset();
657: } else if (intty && wantnl)
658: printf("\n"); /* Some like this, others don't */
659: error(NOSTR);
660: }
661:
662: /*
663: * Process is the main driving routine for the shell.
664: * It runs all command processing, except for those within { ... }
665: * in expressions (which is run by a routine evalav in sh.exp.c which
666: * is a stripped down process), and `...` evaluation which is run
667: * also by a subset of this code in sh.glob.c in the routine backeval.
668: *
669: * The code here is a little strange because part of it is interruptible
670: * and hence freeing of structures appears to occur when none is necessary
671: * if this is ignored.
672: *
673: * Note that if catch is not set then we will unwind on any error.
674: * If an end-of-file occurs, we return.
675: */
676: process(catch)
677: bool catch;
678: {
679: jmp_buf osetexit;
680: register struct command *t;
681:
682: getexit(osetexit);
683: for (;;) {
684: pendjob();
685: paraml.next = paraml.prev = ¶ml;
686: paraml.word = "";
687: t = 0;
688: setexit();
689: justpr = enterhist; /* execute if not entering history */
690:
691: /*
692: * Interruptible during interactive reads
693: */
694: if (setintr)
695: (void) sigsetmask(sigblock(0) & ~sigmask(SIGINT));
696:
697: /*
698: * For the sake of reset()
699: */
700: freelex(¶ml), freesyn(t), t = 0;
701:
702: if (haderr) {
703: if (!catch) {
704: /* unwind */
705: doneinp = 0;
706: resexit(osetexit);
707: reset();
708: }
709: haderr = 0;
710: /*
711: * Every error is eventually caught here or
712: * the shell dies. It is at this
713: * point that we clean up any left-over open
714: * files, by closing all but a fixed number
715: * of pre-defined files. Thus routines don't
716: * have to worry about leaving files open due
717: * to deeper errors... they will get closed here.
718: */
719: closem();
720: continue;
721: }
722: if (doneinp) {
723: doneinp = 0;
724: break;
725: }
726: if (chkstop)
727: chkstop--;
728: if (neednote)
729: pnote();
730: if (intty && prompt && evalvec == 0) {
731: mailchk();
732: /*
733: * If we are at the end of the input buffer
734: * then we are going to read fresh stuff.
735: * Otherwise, we are rereading input and don't
736: * need or want to prompt.
737: */
738: if (fseekp == feobp)
739: printprompt();
740: }
741: err = 0;
742:
743: /*
744: * Echo not only on VERBOSE, but also with history expansion.
745: * If there is a lexical error then we forego history echo.
746: */
747: if (lex(¶ml) && !err && intty ||
748: adrof("verbose")) {
749: haderr = 1;
750: prlex(¶ml);
751: haderr = 0;
752: }
753:
754: /*
755: * The parser may lose space if interrupted.
756: */
757: if (setintr)
758: (void) sigblock(sigmask(SIGINT));
759:
760: /*
761: * Save input text on the history list if
762: * reading in old history, or it
763: * is from the terminal at the top level and not
764: * in a loop.
765: */
766: if (enterhist || catch && intty && !whyles)
767: savehist(¶ml);
768:
769: /*
770: * Print lexical error messages, except when sourcing
771: * history lists.
772: */
773: if (!enterhist && err)
774: error(err);
775:
776: /*
777: * If had a history command :p modifier then
778: * this is as far as we should go
779: */
780: if (justpr)
781: reset();
782:
783: alias(¶ml);
784:
785: /*
786: * Parse the words of the input into a parse tree.
787: */
788: t = syntax(paraml.next, ¶ml, 0);
789: if (err)
790: error(err);
791:
792: /*
793: * Execute the parse tree
794: */
795: execute(t, tpgrp);
796:
797: /*
798: * Made it!
799: */
800: freelex(¶ml), freesyn(t);
801: }
802: resexit(osetexit);
803: }
804:
805: dosource(t)
806: register char **t;
807: {
808: register char *f;
809: register int u;
810: bool hflg = 0;
811: char buf[BUFSIZ];
812:
813: t++;
814: if (*t && eq(*t, "-h")) {
815: t++;
816: hflg++;
817: }
818: (void) strcpy(buf, *t);
819: f = globone(buf);
820: u = dmove(open(f, 0), -1);
821: xfree(f);
822: if (u < 0 && !hflg)
823: Perror(f);
824: (void) ioctl(u, FIOCLEX, (char *)0);
825: srcunit(u, 0, hflg);
826: }
827:
828: /*
829: * Check for mail.
830: * If we are a login shell, then we don't want to tell
831: * about any mail file unless its been modified
832: * after the time we started.
833: * This prevents us from telling the user things he already
834: * knows, since the login program insists on saying
835: * "You have mail."
836: */
837: mailchk()
838: {
839: register struct varent *v;
840: register char **vp;
841: time_t t;
842: int intvl, cnt;
843: struct stat stb;
844: bool new;
845:
846: v = adrof("mail");
847: if (v == 0)
848: return;
849: (void) time(&t);
850: vp = v->vec;
851: cnt = blklen(vp);
852: intvl = (cnt && number(*vp)) ? (--cnt, getn(*vp++)) : MAILINTVL;
853: if (intvl < 1)
854: intvl = 1;
855: if (chktim + intvl > t)
856: return;
857: for (; *vp; vp++) {
858: if (stat(*vp, &stb) < 0)
859: continue;
860: new = stb.st_mtime > time0.tv_sec;
861: if (stb.st_size == 0 || stb.st_atime > stb.st_mtime ||
862: (stb.st_atime < chktim && stb.st_mtime < chktim) ||
863: loginsh && !new)
864: continue;
865: if (cnt == 1)
866: printf("You have %smail.\n", new ? "new " : "");
867: else
868: printf("%s in %s.\n", new ? "New mail" : "Mail", *vp);
869: }
870: chktim = t;
871: }
872:
873: #include <pwd.h>
874: /*
875: * Extract a home directory from the password file
876: * The argument points to a buffer where the name of the
877: * user whose home directory is sought is currently.
878: * We write the home directory of the user back there.
879: */
880: gethdir(home)
881: char *home;
882: {
883: register struct passwd *pp = getpwnam(home);
884:
885: if (pp == 0)
886: return (1);
887: (void) strcpy(home, pp->pw_dir);
888: return (0);
889: }
890:
891: /*
892: * Move the initial descriptors to their eventual
893: * resting places, closin all other units.
894: */
895: initdesc()
896: {
897:
898: didfds = 0; /* 0, 1, 2 aren't set up */
899: (void) ioctl(SHIN = dcopy(0, FSHIN), FIOCLEX, (char *)0);
900: (void) ioctl(SHOUT = dcopy(1, FSHOUT), FIOCLEX, (char *)0);
901: (void) ioctl(SHDIAG = dcopy(2, FSHDIAG), FIOCLEX, (char *)0);
902: (void) ioctl(OLDSTD = dcopy(SHIN, FOLDSTD), FIOCLEX, (char *)0);
903: closem();
904: }
905:
906: #ifdef PROF
907: done(i)
908: #else
909: exit(i)
910: #endif
911: int i;
912: {
913:
914: untty();
915: _exit(i);
916: }
917:
918: printprompt()
919: {
920: register char *cp;
921:
922: if (!whyles) {
923: for (cp = value("prompt"); *cp; cp++)
924: if (*cp == HIST)
925: printf("%d", eventno + 1);
926: else {
927: if (*cp == '\\' && cp[1] == HIST)
928: cp++;
929: putchar(*cp | QUOTE);
930: }
931: } else
932: /*
933: * Prompt for forward reading loop
934: * body content.
935: */
936: printf("? ");
937: flush();
938: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.