Commit | Line | Data |
---|---|---|
ecc449eb KB |
1 | /*- |
2 | * Copyright (c) 1980, 1991 The Regents of the University of California. | |
3 | * All rights reserved. | |
4 | * | |
af359dea C |
5 | * Redistribution and use in source and binary forms, with or without |
6 | * modification, are permitted provided that the following conditions | |
7 | * are met: | |
8 | * 1. Redistributions of source code must retain the above copyright | |
9 | * notice, this list of conditions and the following disclaimer. | |
10 | * 2. Redistributions in binary form must reproduce the above copyright | |
11 | * notice, this list of conditions and the following disclaimer in the | |
12 | * documentation and/or other materials provided with the distribution. | |
13 | * 3. All advertising materials mentioning features or use of this software | |
14 | * must display the following acknowledgement: | |
15 | * This product includes software developed by the University of | |
16 | * California, Berkeley and its contributors. | |
17 | * 4. Neither the name of the University nor the names of its contributors | |
18 | * may be used to endorse or promote products derived from this software | |
19 | * without specific prior written permission. | |
20 | * | |
21 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND | |
22 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
23 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
24 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | |
25 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
26 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |
27 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
28 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
29 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |
30 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
31 | * SUCH DAMAGE. | |
b79f4fa9 DF |
32 | */ |
33 | ||
35371dec | 34 | #ifndef lint |
ecc449eb KB |
35 | char copyright[] = |
36 | "@(#) Copyright (c) 1991 The Regents of the University of California.\n\ | |
37 | All rights reserved.\n"; | |
38 | #endif /* not lint */ | |
39 | ||
40 | #ifndef lint | |
af359dea | 41 | static char sccsid[] = "@(#)csh.c 5.26 (Berkeley) 6/8/91"; |
ecc449eb | 42 | #endif /* not lint */ |
04d3b78c | 43 | |
af359dea | 44 | #include <sys/types.h> |
04d3b78c | 45 | #include <sys/ioctl.h> |
af359dea C |
46 | #include <sys/stat.h> |
47 | #include <fcntl.h> | |
48 | #include <errno.h> | |
49 | #include <pwd.h> | |
50 | #include <stdlib.h> | |
51 | #include <string.h> | |
52 | #include <locale.h> | |
53 | #include <unistd.h> | |
54 | #if __STDC__ | |
55 | # include <stdarg.h> | |
56 | #else | |
57 | # include <varargs.h> | |
58 | #endif | |
59 | ||
60 | #include "csh.h" | |
61 | #include "extern.h" | |
c502712b | 62 | #include "pathnames.h" |
5fe577ad | 63 | |
af359dea C |
64 | extern bool MapsAreInited; |
65 | extern bool NLSMapsAreInited; | |
66 | ||
04d3b78c BJ |
67 | /* |
68 | * C Shell | |
69 | * | |
70 | * Bill Joy, UC Berkeley, California, USA | |
71 | * October 1978, May 1980 | |
72 | * | |
73 | * Jim Kulp, IIASA, Laxenburg, Austria | |
74 | * April 1980 | |
af359dea C |
75 | * |
76 | * Christos Zoulas, Cornell University | |
77 | * June, 1991 | |
04d3b78c BJ |
78 | */ |
79 | ||
af359dea C |
80 | Char *dumphist[] = {STRhistory, STRmh, 0, 0}; |
81 | Char *loadhist[] = {STRsource, STRmh, STRhistfile, 0}; | |
82 | ||
83 | int nofile = 0; | |
84 | bool reenter = 0; | |
85 | bool nverbose = 0; | |
86 | bool nexececho = 0; | |
87 | bool quitit = 0; | |
88 | bool fast = 0; | |
89 | bool batch = 0; | |
90 | bool mflag = 0; | |
91 | bool prompt = 1; | |
92 | bool enterhist = 0; | |
93 | bool tellwhat = 0; | |
94 | ||
95 | extern char **environ; | |
96 | ||
97 | static int srccat __P((Char *, Char *)); | |
98 | static int srcfile __P((char *, bool, bool)); | |
99 | static void phup __P((int)); | |
100 | static void srcunit __P((int, bool, bool)); | |
101 | static void mailchk __P((void)); | |
102 | static Char **defaultpath __P((void)); | |
103 | ||
104 | int | |
7a24a2dc | 105 | main(argc, argv) |
af359dea C |
106 | int argc; |
107 | char **argv; | |
04d3b78c | 108 | { |
af359dea C |
109 | register Char *cp; |
110 | register char *tcp; | |
111 | register int f; | |
112 | register char **tempv; | |
113 | struct sigvec osv; | |
04d3b78c | 114 | |
04d3b78c | 115 | |
af359dea | 116 | settimes(); /* Immed. estab. timing base */ |
04d3b78c | 117 | |
af359dea C |
118 | /* |
119 | * Initialize non constant strings | |
120 | */ | |
121 | #ifdef _PATH_BSHELL | |
122 | STR_BSHELL = SAVE(_PATH_BSHELL); | |
123 | #endif | |
124 | #ifdef _PATH_CSHELL | |
125 | STR_SHELLPATH = SAVE(_PATH_CSHELL); | |
126 | #endif | |
127 | STR_environ = blk2short(environ); | |
128 | environ = short2blk(STR_environ); /* So that we can free it */ | |
129 | STR_WORD_CHARS = SAVE(WORD_CHARS); | |
130 | ||
131 | HIST = '!'; | |
132 | HISTSUB = '^'; | |
133 | word_chars = STR_WORD_CHARS; | |
134 | ||
135 | tempv = argv; | |
136 | if (eq(str2short(tempv[0]), STRaout)) /* A.out's are quittable */ | |
137 | quitit = 1; | |
138 | uid = getuid(); | |
139 | gid = getgid(); | |
140 | /* | |
141 | * We are a login shell if: 1. we were invoked as -<something> and we had | |
142 | * no arguments 2. or we were invoked only with the -l flag | |
143 | */ | |
144 | loginsh = (**tempv == '-' && argc == 1) || | |
145 | (argc == 2 && tempv[1][0] == '-' && tempv[1][1] == 'l' && | |
146 | tempv[1][2] == '\0'); | |
147 | ||
148 | if (loginsh && **tempv != '-') { | |
04d3b78c | 149 | /* |
af359dea | 150 | * Mangle the argv space |
04d3b78c | 151 | */ |
af359dea C |
152 | tempv[1][0] = '\0'; |
153 | tempv[1][1] = '\0'; | |
154 | tempv[1] = NULL; | |
155 | for (tcp = *tempv; *tcp++;); | |
156 | for (tcp--; tcp >= *tempv; tcp--) | |
157 | tcp[1] = tcp[0]; | |
158 | *++tcp = '-'; | |
159 | argc--; | |
160 | } | |
161 | if (loginsh) | |
162 | (void) time(&chktim); | |
163 | ||
164 | AsciiOnly = 1; | |
165 | #ifdef NLS | |
166 | (void) setlocale(LC_ALL, ""); | |
167 | { | |
168 | int k; | |
169 | ||
170 | for (k = 0200; k <= 0377 && !Isprint(k); k++); | |
171 | AsciiOnly = k > 0377; | |
172 | } | |
173 | #else | |
174 | AsciiOnly = getenv("LANG") == NULL && getenv("LC_CTYPE") == NULL; | |
175 | #endif /* NLS */ | |
176 | ||
177 | /* | |
178 | * Move the descriptors to safe places. The variable didfds is 0 while we | |
179 | * have only FSH* to work with. When didfds is true, we have 0,1,2 and | |
180 | * prefer to use these. | |
181 | */ | |
182 | initdesc(); | |
183 | ||
184 | /* | |
185 | * Initialize the shell variables. ARGV and PROMPT are initialized later. | |
186 | * STATUS is also munged in several places. CHILD is munged when | |
187 | * forking/waiting | |
188 | */ | |
189 | set(STRstatus, Strsave(STR0)); | |
190 | ||
191 | if ((tcp = getenv("HOME")) != NULL) | |
192 | cp = SAVE(tcp); | |
193 | else | |
194 | cp = NULL; | |
195 | ||
196 | if (cp == NULL) | |
197 | fast = 1; /* No home -> can't read scripts */ | |
198 | else | |
199 | set(STRhome, cp); | |
200 | dinit(cp); /* dinit thinks that HOME == cwd in a login | |
201 | * shell */ | |
202 | /* | |
203 | * Grab other useful things from the environment. Should we grab | |
204 | * everything?? | |
205 | */ | |
206 | if ((tcp = getenv("LOGNAME")) != NULL || | |
207 | (tcp = getenv("USER")) != NULL) | |
208 | set(STRuser, SAVE(tcp)); | |
209 | if ((tcp = getenv("TERM")) != NULL) | |
210 | set(STRterm, SAVE(tcp)); | |
211 | ||
212 | /* | |
213 | * Re-initialize path if set in environment | |
214 | */ | |
215 | if ((tcp = getenv("PATH")) == NULL) | |
216 | set1(STRpath, defaultpath(), &shvhed); | |
217 | else | |
218 | importpath(SAVE(tcp)); | |
219 | ||
220 | set(STRshell, Strsave(STR_SHELLPATH)); | |
221 | ||
222 | doldol = putn((int) getpid()); /* For $$ */ | |
223 | shtemp = Strspl(STRtmpsh, doldol); /* For << */ | |
224 | ||
225 | /* | |
226 | * Record the interrupt states from the parent process. If the parent is | |
227 | * non-interruptible our hand must be forced or we (and our children) won't | |
228 | * be either. Our children inherit termination from our parent. We catch it | |
229 | * only if we are the login shell. | |
230 | */ | |
231 | /* parents interruptibility */ | |
232 | (void) sigvec(SIGINT, NULL, &osv); | |
233 | parintr = (void (*) ()) osv.sv_handler; | |
234 | (void) sigvec(SIGTERM, NULL, &osv); | |
235 | parterm = (void (*) ()) osv.sv_handler; | |
236 | ||
237 | if (loginsh) { | |
238 | (void) signal(SIGHUP, phup); /* exit processing on HUP */ | |
239 | (void) signal(SIGXCPU, phup); /* ...and on XCPU */ | |
240 | (void) signal(SIGXFSZ, phup); /* ...and on XFSZ */ | |
241 | } | |
242 | ||
243 | /* | |
244 | * Process the arguments. | |
245 | * | |
246 | * Note that processing of -v/-x is actually delayed till after script | |
247 | * processing. | |
248 | * | |
249 | * We set the first character of our name to be '-' if we are a shell running | |
250 | * interruptible commands. Many programs which examine ps'es use this to | |
251 | * filter such shells out. | |
252 | */ | |
253 | argc--, tempv++; | |
254 | while (argc > 0 && (tcp = tempv[0])[0] == '-' && *++tcp != '\0' && !batch) { | |
255 | do | |
256 | switch (*tcp++) { | |
257 | ||
258 | case 0: /* - Interruptible, no prompt */ | |
259 | prompt = 0; | |
260 | setintr = 1; | |
261 | nofile = 1; | |
262 | break; | |
263 | ||
264 | case 'b': /* -b Next arg is input file */ | |
265 | batch = 1; | |
266 | break; | |
267 | ||
268 | case 'c': /* -c Command input from arg */ | |
269 | if (argc == 1) | |
270 | xexit(0); | |
271 | argc--, tempv++; | |
272 | arginp = SAVE(tempv[0]); | |
273 | prompt = 0; | |
274 | nofile = 1; | |
275 | break; | |
04d3b78c | 276 | |
af359dea C |
277 | case 'e': /* -e Exit on any error */ |
278 | exiterr = 1; | |
279 | break; | |
04d3b78c | 280 | |
af359dea C |
281 | case 'f': /* -f Fast start */ |
282 | fast = 1; | |
283 | break; | |
04d3b78c | 284 | |
af359dea C |
285 | case 'i': /* -i Interactive, even if !intty */ |
286 | intact = 1; | |
287 | nofile = 1; | |
288 | break; | |
c5020dd9 | 289 | |
af359dea C |
290 | case 'm': /* -m read .cshrc (from su) */ |
291 | mflag = 1; | |
292 | break; | |
04d3b78c | 293 | |
af359dea C |
294 | case 'n': /* -n Don't execute */ |
295 | noexec = 1; | |
296 | break; | |
04d3b78c | 297 | |
af359dea C |
298 | case 'q': /* -q (Undoc'd) ... die on quit */ |
299 | quitit = 1; | |
300 | break; | |
04d3b78c | 301 | |
af359dea C |
302 | case 's': /* -s Read from std input */ |
303 | nofile = 1; | |
304 | break; | |
305 | ||
306 | case 't': /* -t Read one line from input */ | |
307 | onelflg = 2; | |
04d3b78c | 308 | prompt = 0; |
af359dea C |
309 | nofile = 1; |
310 | break; | |
311 | ||
312 | case 'v': /* -v Echo hist expanded input */ | |
313 | nverbose = 1; /* ... later */ | |
314 | break; | |
315 | ||
316 | case 'x': /* -x Echo just before execution */ | |
317 | nexececho = 1; /* ... later */ | |
318 | break; | |
319 | ||
320 | case 'V': /* -V Echo hist expanded input */ | |
321 | setNS(STRverbose); /* NOW! */ | |
322 | break; | |
323 | ||
324 | case 'X': /* -X Echo just before execution */ | |
325 | setNS(STRecho); /* NOW! */ | |
326 | break; | |
327 | ||
328 | } while (*tcp); | |
329 | tempv++, argc--; | |
330 | } | |
331 | ||
332 | if (quitit) /* With all due haste, for debugging */ | |
333 | (void) signal(SIGQUIT, SIG_DFL); | |
334 | ||
335 | /* | |
336 | * Unless prevented by -, -c, -i, -s, or -t, if there are remaining | |
337 | * arguments the first of them is the name of a shell file from which to | |
338 | * read commands. | |
339 | */ | |
340 | if (nofile == 0 && argc > 0) { | |
341 | nofile = open(tempv[0], O_RDONLY); | |
342 | if (nofile < 0) { | |
343 | child = 1; /* So this doesn't return */ | |
344 | stderror(ERR_SYSTEM, tempv[0], strerror(errno)); | |
04d3b78c | 345 | } |
af359dea C |
346 | ffile = SAVE(tempv[0]); |
347 | /* | |
348 | * Replace FSHIN. Handle /dev/std{in,out,err} specially | |
349 | * since once they are closed we cannot open them again. | |
350 | * In that case we use our own saved descriptors | |
04d3b78c | 351 | */ |
af359dea C |
352 | if ((SHIN = dmove(nofile, FSHIN)) < 0) |
353 | switch(nofile) { | |
354 | case 0: | |
355 | SHIN = FSHIN; | |
356 | break; | |
357 | case 1: | |
358 | SHIN = FSHOUT; | |
359 | break; | |
360 | case 2: | |
361 | SHIN = FSHDIAG; | |
362 | break; | |
363 | default: | |
364 | stderror(ERR_SYSTEM, tempv[0], strerror(errno)); | |
365 | break; | |
366 | } | |
367 | (void) ioctl(SHIN, FIOCLEX, NULL); | |
368 | prompt = 0; | |
369 | /* argc not used any more */ tempv++; | |
370 | } | |
371 | intty = isatty(SHIN); | |
372 | intty |= intact; | |
373 | if (intty || (intact && isatty(SHOUT))) { | |
374 | if (!batch && (uid != geteuid() || gid != getegid())) { | |
375 | errno = EACCES; | |
376 | child = 1; /* So this doesn't return */ | |
377 | stderror(ERR_SYSTEM, "csh", strerror(errno)); | |
04d3b78c | 378 | } |
af359dea C |
379 | } |
380 | /* | |
381 | * Decide whether we should play with signals or not. If we are explicitly | |
382 | * told (via -i, or -) or we are a login shell (arg0 starts with -) or the | |
383 | * input and output are both the ttys("csh", or "csh</dev/ttyx>/dev/ttyx") | |
384 | * Note that in only the login shell is it likely that parent may have set | |
385 | * signals to be ignored | |
386 | */ | |
387 | if (loginsh || intact || intty && isatty(SHOUT)) | |
388 | setintr = 1; | |
389 | settell(); | |
390 | /* | |
391 | * Save the remaining arguments in argv. | |
392 | */ | |
393 | setq(STRargv, blk2short(tempv), &shvhed); | |
394 | ||
395 | /* | |
396 | * Set up the prompt. | |
397 | */ | |
398 | if (prompt) { | |
399 | set(STRprompt, Strsave(uid == 0 ? STRsymhash : STRsymcent)); | |
400 | /* that's a meta-questionmark */ | |
401 | set(STRprompt2, Strsave(STRmquestion)); | |
402 | } | |
403 | ||
404 | /* | |
405 | * If we are an interactive shell, then start fiddling with the signals; | |
406 | * this is a tricky game. | |
407 | */ | |
408 | shpgrp = getpgrp(); | |
409 | opgrp = tpgrp = -1; | |
410 | if (setintr) { | |
411 | **argv = '-'; | |
412 | if (!quitit) /* Wary! */ | |
413 | (void) signal(SIGQUIT, SIG_IGN); | |
414 | (void) signal(SIGINT, pintr); | |
415 | (void) sigblock(sigmask(SIGINT)); | |
416 | (void) signal(SIGTERM, SIG_IGN); | |
417 | if (quitit == 0 && arginp == 0) { | |
418 | (void) signal(SIGTSTP, SIG_IGN); | |
419 | (void) signal(SIGTTIN, SIG_IGN); | |
420 | (void) signal(SIGTTOU, SIG_IGN); | |
421 | /* | |
422 | * Wait till in foreground, in case someone stupidly runs csh & | |
423 | * dont want to try to grab away the tty. | |
424 | */ | |
425 | if (isatty(FSHDIAG)) | |
426 | f = FSHDIAG; | |
427 | else if (isatty(FSHOUT)) | |
428 | f = FSHOUT; | |
429 | else if (isatty(OLDSTD)) | |
430 | f = OLDSTD; | |
431 | else | |
432 | f = -1; | |
433 | retry: | |
434 | if ((tpgrp = tcgetpgrp(f)) != -1) { | |
435 | if (tpgrp != shpgrp) { | |
436 | sig_t old = signal(SIGTTIN, SIG_DFL); | |
437 | (void) kill(0, SIGTTIN); | |
438 | (void) signal(SIGTTIN, old); | |
439 | goto retry; | |
5fe577ad | 440 | } |
af359dea C |
441 | opgrp = shpgrp; |
442 | shpgrp = getpid(); | |
443 | tpgrp = shpgrp; | |
444 | /* | |
445 | * Setpgid will fail if we are a session leader and | |
446 | * mypid == mypgrp (POSIX 4.3.3) | |
447 | */ | |
448 | if (opgrp != shpgrp) | |
449 | if (setpgid(0, shpgrp) == -1) | |
450 | goto notty; | |
451 | /* | |
452 | * We do that after we set our process group, to make sure | |
453 | * that the process group belongs to a process in the same | |
454 | * session as the tty (our process and our group) (POSIX 7.2.4) | |
455 | */ | |
456 | if (tcsetpgrp(f, shpgrp) == -1) | |
457 | goto notty; | |
458 | (void) ioctl(dcopy(f, FSHTTY), FIOCLEX, NULL); | |
459 | } | |
460 | if (tpgrp == -1) { | |
461 | notty: | |
462 | xprintf("Warning: no access to tty (%s).\n", strerror(errno)); | |
463 | xprintf("Thus no job control in this shell.\n"); | |
464 | } | |
04d3b78c | 465 | } |
af359dea C |
466 | } |
467 | if ((setintr == 0) && (parintr == SIG_DFL)) | |
468 | setintr = 1; | |
469 | (void) signal(SIGCHLD, pchild); /* while signals not ready */ | |
470 | ||
471 | /* | |
472 | * Set an exit here in case of an interrupt or error reading the shell | |
473 | * start-up scripts. | |
474 | */ | |
475 | reenter = setexit(); /* PWP */ | |
476 | haderr = 0; /* In case second time through */ | |
477 | if (!fast && reenter == 0) { | |
478 | /* Will have value(STRhome) here because set fast if don't */ | |
479 | { | |
480 | int osetintr = setintr; | |
481 | sigset_t omask = sigblock(sigmask(SIGINT)); | |
482 | ||
483 | setintr = 0; | |
484 | #ifdef _PATH_DOTCSHRC | |
485 | (void) srcfile(_PATH_DOTCSHRC, 0, 0); | |
486 | #endif | |
487 | if (!fast && !arginp && !onelflg) | |
488 | dohash(); | |
489 | #ifdef _PATH_DOTLOGIN | |
490 | if (loginsh) | |
491 | (void) srcfile(_PATH_DOTLOGIN, 0, 0); | |
492 | #endif | |
493 | (void) sigsetmask(omask); | |
494 | setintr = osetintr; | |
495 | } | |
496 | (void) srccat(value(STRhome), STRsldotcshrc); | |
497 | ||
498 | if (!fast && !arginp && !onelflg && !havhash) | |
499 | dohash(); | |
500 | if (loginsh) | |
501 | (void) srccat(value(STRhome), STRsldotlogin); | |
502 | dosource(loadhist); | |
503 | } | |
504 | ||
505 | /* | |
506 | * Now are ready for the -v and -x flags | |
507 | */ | |
508 | if (nverbose) | |
509 | setNS(STRverbose); | |
510 | if (nexececho) | |
511 | setNS(STRecho); | |
512 | ||
513 | /* | |
514 | * All the rest of the world is inside this call. The argument to process | |
515 | * indicates whether it should catch "error unwinds". Thus if we are a | |
516 | * interactive shell our call here will never return by being blown past on | |
517 | * an error. | |
518 | */ | |
519 | process(setintr); | |
520 | ||
521 | /* | |
522 | * Mop-up. | |
523 | */ | |
524 | if (intty) { | |
04d3b78c | 525 | if (loginsh) { |
af359dea C |
526 | xprintf("logout\n"); |
527 | (void) close(SHIN); | |
528 | child = 1; | |
529 | goodbye(); | |
04d3b78c | 530 | } |
af359dea C |
531 | else { |
532 | xprintf("exit\n"); | |
533 | } | |
534 | } | |
535 | rechist(); | |
536 | exitstat(); | |
537 | return (0); | |
04d3b78c BJ |
538 | } |
539 | ||
af359dea | 540 | void |
04d3b78c BJ |
541 | untty() |
542 | { | |
af359dea C |
543 | if (tpgrp > 0) { |
544 | (void) setpgid(0, opgrp); | |
545 | (void) tcsetpgrp(FSHTTY, opgrp); | |
546 | } | |
04d3b78c BJ |
547 | } |
548 | ||
af359dea | 549 | void |
04d3b78c | 550 | importpath(cp) |
af359dea | 551 | Char *cp; |
04d3b78c | 552 | { |
af359dea C |
553 | register int i = 0; |
554 | register Char *dp; | |
555 | register Char **pv; | |
556 | int c; | |
557 | ||
558 | for (dp = cp; *dp; dp++) | |
559 | if (*dp == ':') | |
560 | i++; | |
561 | /* | |
562 | * i+2 where i is the number of colons in the path. There are i+1 | |
563 | * directories in the path plus we need room for a zero terminator. | |
564 | */ | |
565 | pv = (Char **) xcalloc((size_t) (i + 2), sizeof(Char **)); | |
566 | dp = cp; | |
567 | i = 0; | |
568 | if (*dp) | |
04d3b78c | 569 | for (;;) { |
af359dea C |
570 | if ((c = *dp) == ':' || c == 0) { |
571 | *dp = 0; | |
572 | pv[i++] = Strsave(*cp ? cp : STRdot); | |
573 | if (c) { | |
574 | cp = dp + 1; | |
575 | *dp = ':'; | |
04d3b78c | 576 | } |
af359dea C |
577 | else |
578 | break; | |
579 | } | |
580 | dp++; | |
04d3b78c | 581 | } |
af359dea C |
582 | pv[i] = 0; |
583 | set1(STRpath, pv, &shvhed); | |
04d3b78c BJ |
584 | } |
585 | ||
586 | /* | |
587 | * Source to the file which is the catenation of the argument names. | |
588 | */ | |
af359dea | 589 | static int |
04d3b78c | 590 | srccat(cp, dp) |
af359dea C |
591 | Char *cp, *dp; |
592 | { | |
593 | register Char *ep = Strspl(cp, dp); | |
594 | char *ptr = short2str(ep); | |
595 | ||
596 | xfree((ptr_t) ep); | |
597 | return srcfile(ptr, mflag ? 0 : 1, 0); | |
598 | } | |
599 | ||
600 | /* | |
601 | * Source to a file putting the file descriptor in a safe place (> 2). | |
602 | */ | |
603 | static int | |
604 | srcfile(f, onlyown, flag) | |
605 | char *f; | |
606 | bool onlyown, flag; | |
04d3b78c | 607 | { |
af359dea C |
608 | register int unit; |
609 | ||
610 | if ((unit = open(f, O_RDONLY)) == -1) | |
611 | return 0; | |
612 | unit = dmove(unit, -1); | |
04d3b78c | 613 | |
af359dea C |
614 | (void) ioctl(unit, FIOCLEX, NULL); |
615 | srcunit(unit, onlyown, flag); | |
616 | return 1; | |
04d3b78c BJ |
617 | } |
618 | ||
619 | /* | |
af359dea C |
620 | * Source to a unit. If onlyown it must be our file or our group or |
621 | * we don't chance it. This occurs on ".cshrc"s and the like. | |
04d3b78c | 622 | */ |
af359dea | 623 | int insource; |
2400447c | 624 | static void |
d7929fa7 | 625 | srcunit(unit, onlyown, hflg) |
af359dea C |
626 | register int unit; |
627 | bool onlyown, hflg; | |
04d3b78c | 628 | { |
af359dea C |
629 | /* We have to push down a lot of state here */ |
630 | /* All this could go into a structure */ | |
631 | int oSHIN = -1, oldintty = intty, oinsource = insource; | |
632 | struct whyle *oldwhyl = whyles; | |
633 | Char *ogointr = gointr, *oarginp = arginp; | |
634 | Char *oevalp = evalp, **oevalvec = evalvec; | |
635 | int oonelflg = onelflg; | |
636 | bool oenterhist = enterhist; | |
637 | char OHIST = HIST; | |
638 | bool otell = cantell; | |
639 | ||
640 | struct Bin saveB; | |
641 | sigset_t omask; | |
642 | jmp_buf oldexit; | |
643 | ||
644 | /* The (few) real local variables */ | |
645 | int my_reenter; | |
646 | ||
647 | if (unit < 0) | |
648 | return; | |
649 | if (didfds) | |
650 | donefds(); | |
651 | if (onlyown) { | |
652 | struct stat stb; | |
04d3b78c | 653 | |
af359dea C |
654 | if (fstat(unit, &stb) < 0) { |
655 | (void) close(unit); | |
656 | return; | |
04d3b78c | 657 | } |
af359dea C |
658 | } |
659 | ||
660 | /* | |
661 | * There is a critical section here while we are pushing down the input | |
662 | * stream since we have stuff in different structures. If we weren't | |
663 | * careful an interrupt could corrupt SHIN's Bin structure and kill the | |
664 | * shell. | |
665 | * | |
666 | * We could avoid the critical region by grouping all the stuff in a single | |
667 | * structure and pointing at it to move it all at once. This is less | |
668 | * efficient globally on many variable references however. | |
669 | */ | |
670 | insource = 1; | |
671 | getexit(oldexit); | |
672 | omask = 0; | |
673 | ||
674 | if (setintr) | |
675 | omask = sigblock(sigmask(SIGINT)); | |
676 | /* Setup the new values of the state stuff saved above */ | |
677 | bcopy((char *) &B, (char *) &(saveB), sizeof(B)); | |
678 | fbuf = NULL; | |
679 | fseekp = feobp = fblocks = 0; | |
680 | oSHIN = SHIN, SHIN = unit, arginp = 0, onelflg = 0; | |
681 | intty = isatty(SHIN), whyles = 0, gointr = 0; | |
682 | evalvec = 0; | |
683 | evalp = 0; | |
684 | enterhist = hflg; | |
685 | if (enterhist) | |
686 | HIST = '\0'; | |
687 | ||
688 | /* | |
689 | * Now if we are allowing commands to be interrupted, we let ourselves be | |
690 | * interrupted. | |
691 | */ | |
692 | if (setintr) | |
693 | (void) sigsetmask(omask); | |
694 | settell(); | |
695 | ||
696 | if ((my_reenter = setexit()) == 0) | |
697 | process(0); /* 0 -> blow away on errors */ | |
698 | ||
699 | if (setintr) | |
700 | (void) sigsetmask(omask); | |
701 | if (oSHIN >= 0) { | |
702 | register int i; | |
703 | ||
704 | /* We made it to the new state... free up its storage */ | |
705 | /* This code could get run twice but xfree doesn't care */ | |
706 | for (i = 0; i < fblocks; i++) | |
707 | xfree((ptr_t) fbuf[i]); | |
708 | xfree((ptr_t) fbuf); | |
709 | ||
710 | /* Reset input arena */ | |
711 | bcopy((char *) &(saveB), (char *) &B, sizeof(B)); | |
712 | ||
713 | (void) close(SHIN), SHIN = oSHIN; | |
714 | arginp = oarginp, onelflg = oonelflg; | |
715 | evalp = oevalp, evalvec = oevalvec; | |
716 | intty = oldintty, whyles = oldwhyl, gointr = ogointr; | |
717 | if (enterhist) | |
718 | HIST = OHIST; | |
719 | enterhist = oenterhist; | |
720 | cantell = otell; | |
721 | } | |
722 | ||
723 | resexit(oldexit); | |
724 | /* | |
725 | * If process reset() (effectively an unwind) then we must also unwind. | |
726 | */ | |
727 | if (my_reenter) | |
728 | stderror(ERR_SILENT); | |
729 | insource = oinsource; | |
04d3b78c BJ |
730 | } |
731 | ||
af359dea | 732 | void |
d7929fa7 | 733 | rechist() |
04d3b78c | 734 | { |
af359dea C |
735 | Char buf[BUFSIZ]; |
736 | int fp, ftmp, oldidfds; | |
737 | ||
738 | if (!fast) { | |
739 | if (value(STRsavehist)[0] == '\0') | |
740 | return; | |
741 | (void) Strcpy(buf, value(STRhome)); | |
742 | (void) Strcat(buf, STRsldthist); | |
743 | fp = creat(short2str(buf), 0600); | |
744 | if (fp == -1) | |
745 | return; | |
746 | oldidfds = didfds; | |
747 | didfds = 0; | |
748 | ftmp = SHOUT; | |
749 | SHOUT = fp; | |
750 | (void) Strcpy(buf, value(STRsavehist)); | |
751 | dumphist[2] = buf; | |
752 | dohist(dumphist); | |
753 | (void) close(fp); | |
754 | SHOUT = ftmp; | |
755 | didfds = oldidfds; | |
756 | } | |
d7929fa7 KM |
757 | } |
758 | ||
af359dea | 759 | void |
d7929fa7 KM |
760 | goodbye() |
761 | { | |
af359dea C |
762 | rechist(); |
763 | ||
764 | if (loginsh) { | |
765 | (void) signal(SIGQUIT, SIG_IGN); | |
766 | (void) signal(SIGINT, SIG_IGN); | |
767 | (void) signal(SIGTERM, SIG_IGN); | |
768 | setintr = 0; /* No interrupts after "logout" */ | |
769 | if (!(adrof(STRlogout))) | |
770 | set(STRlogout, STRnormal); | |
771 | #ifdef _PATH_DOTLOGOUT | |
772 | (void) srcfile(_PATH_DOTLOGOUT, 0, 0); | |
773 | #endif | |
774 | if (adrof(STRhome)) | |
775 | (void) srccat(value(STRhome), STRsldtlogout); | |
776 | } | |
777 | exitstat(); | |
04d3b78c BJ |
778 | } |
779 | ||
af359dea | 780 | void |
04d3b78c BJ |
781 | exitstat() |
782 | { | |
783 | ||
35371dec | 784 | #ifdef PROF |
af359dea | 785 | monitor(0); |
35371dec | 786 | #endif |
af359dea C |
787 | /* |
788 | * Note that if STATUS is corrupted (i.e. getn bombs) then error will exit | |
789 | * directly because we poke child here. Otherwise we might continue | |
790 | * unwarrantedly (sic). | |
791 | */ | |
792 | child = 1; | |
793 | xexit(getn(value(STRstatus))); | |
04d3b78c BJ |
794 | } |
795 | ||
d7929fa7 KM |
796 | /* |
797 | * in the event of a HUP we want to save the history | |
798 | */ | |
af359dea C |
799 | static void |
800 | phup(sig) | |
801 | int sig; | |
d7929fa7 | 802 | { |
af359dea C |
803 | rechist(); |
804 | xexit(sig); | |
d7929fa7 KM |
805 | } |
806 | ||
af359dea C |
807 | Char *jobargv[2] = {STRjobs, 0}; |
808 | ||
04d3b78c BJ |
809 | /* |
810 | * Catch an interrupt, e.g. during lexical input. | |
811 | * If we are an interactive shell, we reset the interrupt catch | |
812 | * immediately. In any case we drain the shell output, | |
813 | * and finally go through the normal error mechanism, which | |
814 | * gets a chance to make the shell go away. | |
815 | */ | |
af359dea | 816 | /* ARGSUSED */ |
d3ac50fc | 817 | void |
af359dea C |
818 | pintr(notused) |
819 | int notused; | |
401149be | 820 | { |
af359dea | 821 | pintr1(1); |
401149be BJ |
822 | } |
823 | ||
af359dea | 824 | void |
401149be | 825 | pintr1(wantnl) |
af359dea | 826 | bool wantnl; |
04d3b78c | 827 | { |
af359dea C |
828 | register Char **v; |
829 | sigset_t omask; | |
830 | ||
831 | omask = sigblock((sigset_t) 0); | |
832 | if (setintr) { | |
833 | (void) sigsetmask(omask & ~sigmask(SIGINT)); | |
834 | if (pjobs) { | |
835 | pjobs = 0; | |
836 | xprintf("\n"); | |
837 | dojobs(jobargv); | |
838 | stderror(ERR_NAME | ERR_INTR); | |
04d3b78c | 839 | } |
af359dea C |
840 | } |
841 | (void) sigsetmask(omask & ~sigmask(SIGCHLD)); | |
842 | draino(); | |
843 | (void) endpwent(); | |
844 | ||
845 | /* | |
846 | * If we have an active "onintr" then we search for the label. Note that if | |
847 | * one does "onintr -" then we shan't be interruptible so we needn't worry | |
848 | * about that here. | |
849 | */ | |
850 | if (gointr) { | |
851 | search(T_GOTO, 0, gointr); | |
852 | timflg = 0; | |
853 | if (v = pargv) | |
854 | pargv = 0, blkfree(v); | |
855 | if (v = gargv) | |
856 | gargv = 0, blkfree(v); | |
857 | reset(); | |
858 | } | |
859 | else if (intty && wantnl) { | |
860 | (void) putraw('\r'); | |
861 | (void) putraw('\n'); | |
862 | } | |
863 | stderror(ERR_SILENT); | |
04d3b78c BJ |
864 | } |
865 | ||
866 | /* | |
867 | * Process is the main driving routine for the shell. | |
868 | * It runs all command processing, except for those within { ... } | |
869 | * in expressions (which is run by a routine evalav in sh.exp.c which | |
870 | * is a stripped down process), and `...` evaluation which is run | |
871 | * also by a subset of this code in sh.glob.c in the routine backeval. | |
872 | * | |
873 | * The code here is a little strange because part of it is interruptible | |
874 | * and hence freeing of structures appears to occur when none is necessary | |
875 | * if this is ignored. | |
876 | * | |
877 | * Note that if catch is not set then we will unwind on any error. | |
878 | * If an end-of-file occurs, we return. | |
879 | */ | |
af359dea | 880 | void |
04d3b78c | 881 | process(catch) |
af359dea | 882 | bool catch; |
04d3b78c | 883 | { |
af359dea C |
884 | jmp_buf osetexit; |
885 | struct command *t; | |
886 | ||
887 | getexit(osetexit); | |
888 | for (;;) { | |
889 | pendjob(); | |
890 | paraml.next = paraml.prev = ¶ml; | |
891 | paraml.word = STRNULL; | |
892 | t = 0; | |
893 | (void) setexit(); | |
894 | justpr = enterhist; /* execute if not entering history */ | |
04d3b78c | 895 | |
af359dea C |
896 | /* |
897 | * Interruptible during interactive reads | |
898 | */ | |
899 | if (setintr) | |
900 | (void) sigsetmask(sigblock((sigset_t) 0) & ~sigmask(SIGINT)); | |
04d3b78c | 901 | |
af359dea C |
902 | /* |
903 | * For the sake of reset() | |
904 | */ | |
905 | freelex(¶ml); | |
906 | freesyn(t); | |
907 | t = 0; | |
908 | ||
909 | if (haderr) { | |
910 | if (!catch) { | |
911 | /* unwind */ | |
912 | doneinp = 0; | |
913 | resexit(osetexit); | |
914 | reset(); | |
915 | } | |
916 | haderr = 0; | |
917 | /* | |
918 | * Every error is eventually caught here or the shell dies. It is | |
919 | * at this point that we clean up any left-over open files, by | |
920 | * closing all but a fixed number of pre-defined files. Thus | |
921 | * routines don't have to worry about leaving files open due to | |
922 | * deeper errors... they will get closed here. | |
923 | */ | |
924 | closem(); | |
925 | continue; | |
926 | } | |
927 | if (doneinp) { | |
928 | doneinp = 0; | |
929 | break; | |
930 | } | |
931 | if (chkstop) | |
932 | chkstop--; | |
933 | if (neednote) | |
934 | pnote(); | |
935 | if (intty && prompt && evalvec == 0) { | |
936 | mailchk(); | |
937 | /* | |
938 | * If we are at the end of the input buffer then we are going to | |
939 | * read fresh stuff. Otherwise, we are rereading input and don't | |
940 | * need or want to prompt. | |
941 | */ | |
942 | if (fseekp == feobp) | |
943 | printprompt(); | |
944 | flush(); | |
945 | } | |
946 | if (seterr) { | |
947 | xfree((ptr_t) seterr); | |
948 | seterr = NULL; | |
949 | } | |
04d3b78c | 950 | |
af359dea C |
951 | /* |
952 | * Echo not only on VERBOSE, but also with history expansion. If there | |
953 | * is a lexical error then we forego history echo. | |
954 | */ | |
955 | if (lex(¶ml) && !seterr && intty || adrof(STRverbose)) { | |
956 | haderr = 1; | |
957 | prlex(¶ml); | |
958 | haderr = 0; | |
959 | } | |
04d3b78c | 960 | |
af359dea C |
961 | /* |
962 | * The parser may lose space if interrupted. | |
963 | */ | |
964 | if (setintr) | |
965 | (void) sigblock(sigmask(SIGINT)); | |
04d3b78c | 966 | |
af359dea C |
967 | /* |
968 | * Save input text on the history list if reading in old history, or it | |
969 | * is from the terminal at the top level and not in a loop. | |
970 | * | |
971 | * PWP: entry of items in the history list while in a while loop is done | |
972 | * elsewhere... | |
973 | */ | |
974 | if (enterhist || catch && intty && !whyles) | |
975 | savehist(¶ml); | |
04d3b78c | 976 | |
af359dea C |
977 | /* |
978 | * Print lexical error messages, except when sourcing history lists. | |
979 | */ | |
980 | if (!enterhist && seterr) | |
981 | stderror(ERR_OLD); | |
04d3b78c | 982 | |
af359dea C |
983 | /* |
984 | * If had a history command :p modifier then this is as far as we | |
985 | * should go | |
986 | */ | |
987 | if (justpr) | |
988 | reset(); | |
04d3b78c | 989 | |
af359dea | 990 | alias(¶ml); |
04d3b78c | 991 | |
af359dea C |
992 | /* |
993 | * Parse the words of the input into a parse tree. | |
994 | */ | |
995 | t = syntax(paraml.next, ¶ml, 0); | |
996 | if (seterr) | |
997 | stderror(ERR_OLD); | |
04d3b78c | 998 | |
af359dea | 999 | execute(t, (tpgrp > 0 ? tpgrp : -1), NULL, NULL); |
04d3b78c | 1000 | |
af359dea C |
1001 | /* |
1002 | * Made it! | |
1003 | */ | |
1004 | freelex(¶ml); | |
1005 | freesyn(t); | |
1006 | } | |
1007 | resexit(osetexit); | |
04d3b78c BJ |
1008 | } |
1009 | ||
af359dea | 1010 | void |
04d3b78c | 1011 | dosource(t) |
af359dea | 1012 | register Char **t; |
04d3b78c | 1013 | { |
af359dea C |
1014 | register Char *f; |
1015 | bool hflg = 0; | |
1016 | Char buf[BUFSIZ]; | |
1017 | ||
1018 | t++; | |
1019 | if (*t && eq(*t, STRmh)) { | |
1020 | if (*++t == NULL) | |
1021 | stderror(ERR_NAME | ERR_HFLAG); | |
1022 | hflg++; | |
1023 | } | |
1024 | (void) Strcpy(buf, *t); | |
1025 | f = globone(buf, G_ERROR); | |
1026 | (void) strcpy((char *) buf, short2str(f)); | |
1027 | xfree((ptr_t) f); | |
1028 | if (!srcfile((char *) buf, 0, hflg) && !hflg) | |
1029 | stderror(ERR_SYSTEM, (char *) buf, strerror(errno)); | |
04d3b78c BJ |
1030 | } |
1031 | ||
1032 | /* | |
1033 | * Check for mail. | |
1034 | * If we are a login shell, then we don't want to tell | |
1035 | * about any mail file unless its been modified | |
1036 | * after the time we started. | |
1037 | * This prevents us from telling the user things he already | |
1038 | * knows, since the login program insists on saying | |
1039 | * "You have mail." | |
1040 | */ | |
af359dea | 1041 | static void |
04d3b78c BJ |
1042 | mailchk() |
1043 | { | |
af359dea C |
1044 | register struct varent *v; |
1045 | register Char **vp; | |
1046 | time_t t; | |
1047 | int intvl, cnt; | |
1048 | struct stat stb; | |
1049 | bool new; | |
1050 | ||
1051 | v = adrof(STRmail); | |
1052 | if (v == 0) | |
1053 | return; | |
1054 | (void) time(&t); | |
1055 | vp = v->vec; | |
1056 | cnt = blklen(vp); | |
1057 | intvl = (cnt && number(*vp)) ? (--cnt, getn(*vp++)) : MAILINTVL; | |
1058 | if (intvl < 1) | |
1059 | intvl = 1; | |
1060 | if (chktim + intvl > t) | |
1061 | return; | |
1062 | for (; *vp; vp++) { | |
1063 | if (stat(short2str(*vp), &stb) < 0) | |
1064 | continue; | |
1065 | new = stb.st_mtime > time0.tv_sec; | |
1066 | if (stb.st_size == 0 || stb.st_atime > stb.st_mtime || | |
1067 | (stb.st_atime < chktim && stb.st_mtime < chktim) || | |
1068 | loginsh && !new) | |
1069 | continue; | |
1070 | if (cnt == 1) | |
1071 | xprintf("You have %smail.\n", new ? "new " : ""); | |
1072 | else | |
1073 | xprintf("%s in %s.\n", new ? "New mail" : "Mail", short2str(*vp)); | |
1074 | } | |
1075 | chktim = t; | |
04d3b78c BJ |
1076 | } |
1077 | ||
04d3b78c BJ |
1078 | /* |
1079 | * Extract a home directory from the password file | |
1080 | * The argument points to a buffer where the name of the | |
1081 | * user whose home directory is sought is currently. | |
1082 | * We write the home directory of the user back there. | |
1083 | */ | |
af359dea | 1084 | int |
04d3b78c | 1085 | gethdir(home) |
af359dea | 1086 | Char *home; |
04d3b78c | 1087 | { |
af359dea C |
1088 | Char *h; |
1089 | struct passwd *pw; | |
1090 | ||
1091 | /* | |
1092 | * Is it us? | |
1093 | */ | |
1094 | if (*home == '\0') { | |
1095 | if (h = value(STRhome)) { | |
1096 | (void) Strcpy(home, h); | |
1097 | return 0; | |
1098 | } | |
1099 | else | |
1100 | return 1; | |
1101 | } | |
1102 | ||
1103 | if (pw = getpwnam(short2str(home))) { | |
1104 | (void) Strcpy(home, str2short(pw->pw_dir)); | |
1105 | return 0; | |
1106 | } | |
1107 | else | |
1108 | return 1; | |
04d3b78c BJ |
1109 | } |
1110 | ||
1111 | /* | |
1112 | * Move the initial descriptors to their eventual | |
1113 | * resting places, closin all other units. | |
1114 | */ | |
af359dea | 1115 | void |
04d3b78c BJ |
1116 | initdesc() |
1117 | { | |
1118 | ||
af359dea C |
1119 | didfds = 0; /* 0, 1, 2 aren't set up */ |
1120 | (void) ioctl(SHIN = dcopy(0, FSHIN), FIOCLEX, NULL); | |
1121 | (void) ioctl(SHOUT = dcopy(1, FSHOUT), FIOCLEX, NULL); | |
1122 | (void) ioctl(SHDIAG = dcopy(2, FSHDIAG), FIOCLEX, NULL); | |
1123 | (void) ioctl(OLDSTD = dcopy(SHIN, FOLDSTD), FIOCLEX, NULL); | |
1124 | closem(); | |
04d3b78c BJ |
1125 | } |
1126 | ||
af359dea C |
1127 | |
1128 | void | |
e31125a2 SL |
1129 | #ifdef PROF |
1130 | done(i) | |
1131 | #else | |
af359dea | 1132 | xexit(i) |
e31125a2 | 1133 | #endif |
af359dea C |
1134 | int i; |
1135 | { | |
1136 | untty(); | |
1137 | _exit(i); | |
1138 | } | |
1139 | ||
1140 | static Char ** | |
1141 | defaultpath() | |
04d3b78c | 1142 | { |
af359dea C |
1143 | char *ptr; |
1144 | Char **blk, **blkp; | |
1145 | struct stat stb; | |
1146 | ||
1147 | blkp = blk = (Char **) xmalloc((size_t) sizeof(Char *) * 10); | |
1148 | ||
1149 | #define DIRAPPEND(a) \ | |
1150 | if (stat(ptr = a, &stb) == 0 && (stb.st_mode & S_IFMT) == S_IFDIR) \ | |
1151 | *blkp++ = SAVE(ptr) | |
04d3b78c | 1152 | |
af359dea C |
1153 | DIRAPPEND(_PATH_BIN); |
1154 | DIRAPPEND(_PATH_USRBIN); | |
1155 | ||
1156 | #undef DIRAPPEND | |
1157 | ||
1158 | *blkp++ = Strsave(STRdot); | |
1159 | *blkp = NULL; | |
1160 | return (blk); | |
04d3b78c | 1161 | } |
19d39288 | 1162 | |
af359dea | 1163 | void |
544d4ed6 | 1164 | printprompt() |
19d39288 | 1165 | { |
af359dea C |
1166 | register Char *cp; |
1167 | ||
1168 | if (!whyles) { | |
1169 | for (cp = value(STRprompt); *cp; cp++) | |
1170 | if (*cp == HIST) | |
1171 | xprintf("%d", eventno + 1); | |
1172 | else { | |
1173 | if (*cp == '\\' && cp[1] == HIST) | |
1174 | cp++; | |
1175 | xputchar(*cp | QUOTE); | |
1176 | } | |
1177 | } | |
1178 | else | |
1179 | /* | |
1180 | * Prompt for forward reading loop body content. | |
1181 | */ | |
1182 | xprintf("? "); | |
1183 | flush(); | |
19d39288 | 1184 | } |