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