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