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