main.c revision 1.3 1 /* $NetBSD: main.c,v 1.3 1997/07/20 17:42:08 christos Exp $ */
2
3 /*
4 * startup, main loop, enviroments and error handling
5 */
6
7 #define EXTERN /* define EXTERNs in sh.h */
8
9 #include "sh.h"
10 #include "ksh_stat.h"
11 #include "ksh_time.h"
12
13 extern char **environ;
14
15 /*
16 * global data
17 */
18
19 static void reclaim ARGS((void));
20 static void remove_temps ARGS((struct temp *tp));
21 static int is_restricted ARGS((char *name));
22
23 /*
24 * shell initialization
25 */
26
27 static const char initifs [] = "IFS= \t\n"; /* must be R/W */
28
29 static const char initsubs [] =
30 "${PS2=> } ${PS3=#? } ${PS4=+ }";
31
32 static const char version_param[] =
33 #ifdef KSH
34 "KSH_VERSION"
35 #else /* KSH */
36 "SH_VERSION"
37 #endif /* KSH */
38 ;
39
40 static const char *const initcoms [] = {
41 "typeset", "-x", "SHELL", "PATH", "HOME", NULL,
42 "typeset", "-r", version_param, NULL,
43 "typeset", "-ri", "PPID", NULL,
44 "typeset", "-i", "OPTIND=1",
45 #ifdef KSH
46 "MAILCHECK=600", "RANDOM", "SECONDS=0", "TMOUT=0",
47 #endif /* KSH */
48 NULL,
49 "alias",
50 /* Standard ksh aliases */
51 "hash=alias -t", /* not "alias -t --": hash -r needs to work */
52 "type=whence -v",
53 #ifdef JOBS
54 "stop=kill -STOP",
55 "suspend=kill -STOP $$",
56 #endif
57 #ifdef KSH
58 "autoload=typeset -fu",
59 "functions=typeset -f",
60 # ifdef HISTORY
61 "history=fc -l",
62 # endif /* HISTORY */
63 "integer=typeset -i",
64 "nohup=nohup ",
65 "local=typeset",
66 "r=fc -e -",
67 #endif /* KSH */
68 #ifdef KSH
69 /* Aliases that are builtin commands in at&t */
70 "login=exec login",
71 "newgrp=exec newgrp",
72 #endif /* KSH */
73 NULL,
74 /* this is what at&t ksh seems to track, with the addition of emacs */
75 "alias", "-tU",
76 "cat", "cc", "chmod", "cp", "date", "ed", "emacs", "grep", "ls",
77 "mail", "make", "mv", "pr", "rm", "sed", "sh", "vi", "who",
78 NULL,
79 #ifdef EXTRA_INITCOMS
80 EXTRA_INITCOMS, NULL,
81 #endif /* EXTRA_INITCOMS */
82 NULL
83 };
84
85 int main __P((int, char **));
86
87 int
88 main(argc, argv)
89 int argc;
90 register char **argv;
91 {
92 register int i;
93 int argi;
94 Source *s;
95 struct block *l;
96 int restricted;
97 char **wp;
98 struct env env;
99 int euid;
100
101 #ifdef MEM_DEBUG
102 chmem_push("+c", 1);
103 /*chmem_push("+cd", 1);*/
104 #endif
105
106 #ifdef OS2
107 setmode (0, O_BINARY);
108 setmode (1, O_TEXT);
109 #endif
110
111 /* make sure argv[] is sane */
112 if (!*argv) {
113 static const char *empty_argv[] = {
114 "pdksh", (char *) 0
115 };
116
117 argv = (char **) empty_argv;
118 argc = 1;
119 }
120 kshname = *argv;
121
122 ainit(&aperm); /* initialize permanent Area */
123
124 /* set up base enviroment */
125 memset(&env, 0, sizeof(env));
126 env.type = E_NONE;
127 ainit(&env.area);
128 e = &env;
129 newblock(); /* set up global l->vars and l->funs */
130
131 /* Do this first so output routines (eg, errorf, shellf) can work */
132 initio();
133
134 initvar();
135
136 initctypes();
137
138 inittraps();
139
140 #ifdef KSH
141 coproc_init();
142 #endif /* KSH */
143
144 /* set up variable and command dictionaries */
145 tinit(&taliases, APERM, 0);
146 tinit(&aliases, APERM, 0);
147 tinit(&homedirs, APERM, 0);
148
149 /* define shell keywords */
150 initkeywords();
151
152 /* define built-in commands */
153 tinit(&builtins, APERM, 64); /* must be 2^n (currently 40 builtins) */
154 for (i = 0; shbuiltins[i].name != NULL; i++)
155 builtin(shbuiltins[i].name, shbuiltins[i].func);
156 for (i = 0; kshbuiltins[i].name != NULL; i++)
157 builtin(kshbuiltins[i].name, kshbuiltins[i].func);
158
159 init_histvec();
160
161 def_path = DEFAULT__PATH;
162 #if defined(HAVE_CONFSTR) && defined(_CS_PATH)
163 {
164 size_t len = confstr(_CS_PATH, (char *) 0, 0);
165 char *new;
166
167 if (len > 0) {
168 confstr(_CS_PATH, new = alloc(len + 1, APERM), len + 1);
169 def_path = new;
170 }
171 }
172 #endif /* HAVE_CONFSTR && _CS_PATH */
173 path = def_path;
174
175
176 /* Turn on nohup by default for how - will change to off
177 * by default once people are aware of its existance
178 * (at&t ksh does not have a nohup option - it always sends
179 * the hup).
180 */
181 Flag(FNOHUP) = 1;
182
183 /* Turn on brace expansion by default. At&t ksh's that have
184 * alternation always have it on. BUT, posix doesn't have
185 * brace expansion, so set this before setting up FPOSIX
186 * (change_flag() clears FBRACEEXPAND when FPOSIX is set).
187 */
188 #ifdef BRACE_EXPAND
189 Flag(FBRACEEXPAND) = 1;
190 #endif /* BRACE_EXPAND */
191
192 /* set posix flag just before environment so that it will have
193 * exactly the same effect as the POSIXLY_CORRECT environment
194 * variable. If this needs to be done sooner to ensure correct posix
195 * operation, an initial scan of the environment will also have
196 * done sooner.
197 */
198 #ifdef POSIXLY_CORRECT
199 change_flag(FPOSIX, OF_SPECIAL, 1);
200 #endif /* POSIXLY_CORRECT */
201
202 /* import enviroment */
203 if (environ != NULL)
204 for (wp = environ; *wp != NULL; wp++)
205 typeset(*wp, IMPORT|EXPORT, 0, 0, 0);
206
207 kshpid = procpid = getpid();
208 typeset(initifs, 0, 0, 0, 0); /* for security */
209
210 /* assign default shell variable values */
211 substitute(initsubs, 0);
212
213 /* Figure out the current working directory and set $PWD */
214 {
215 struct stat s_pwd, s_dot;
216 struct tbl *pwd_v = global("PWD");
217 char *pwd = str_val(pwd_v);
218 char *pwdx = pwd;
219
220 /* Try to use existing $PWD if it is valid */
221 if (!ISABSPATH(pwd)
222 || stat(pwd, &s_pwd) < 0 || stat(".", &s_dot) < 0
223 || s_pwd.st_dev != s_dot.st_dev
224 || s_pwd.st_ino != s_dot.st_ino)
225 pwdx = (char *) 0;
226 set_current_wd(pwdx);
227 if (current_wd[0])
228 simplify_path(current_wd);
229 /* Only set pwd if we know where we are or if it had a
230 * bogus value
231 */
232 if (current_wd[0] || pwd != null)
233 setstr(pwd_v, current_wd);
234 }
235 setint(global("PPID"), (long) getppid());
236 #ifdef KSH
237 setint(global("RANDOM"), (long) time((time_t *)0));
238 #endif /* KSH */
239 setstr(global(version_param), ksh_version);
240
241 /* execute initialization statements */
242 for (wp = (char**) initcoms; *wp != NULL; wp++) {
243 shcomexec(wp);
244 for (; *wp != NULL; wp++)
245 ;
246 }
247
248 euid = geteuid();
249 safe_prompt = euid ? "$ " : "# ";
250 {
251 struct tbl *vp = global("PS1");
252
253 /* Set PS1 if it isn't set, or we are root and prompt doesn't
254 * contain a #.
255 */
256 if (!(vp->flag & ISSET) || (!euid && !strchr(str_val(vp), '#')))
257 setstr(vp, safe_prompt);
258 }
259
260 /* Set this before parsing arguments */
261 Flag(FPRIVILEGED) = getuid() != euid || getgid() != getegid();
262
263 /* this to note if monitor is set on command line (see below) */
264 Flag(FMONITOR) = 127;
265 argi = parse_args(argv, OF_CMDLINE, (int *) 0);
266 if (argi < 0)
267 exit(1);
268
269 if (Flag(FCOMMAND)) {
270 s = pushs(SSTRING, ATEMP);
271 if (!(s->start = s->str = argv[argi++]))
272 errorf("-c requires an argument");
273 if (argv[argi])
274 kshname = argv[argi++];
275 } else if (argi < argc && !Flag(FSTDIN)) {
276 s = pushs(SFILE, ATEMP);
277 #ifdef OS2
278 /* a bug in os2 extproc shell processing doesn't
279 * pass full pathnames so we have to search for it.
280 * This changes the behavior of 'ksh arg' to search
281 * the users search path but it can't be helped.
282 */
283 s->file = search(argv[argi++], path, R_OK, (int *) 0);
284 if (!s->file || !*s->file)
285 s->file = argv[argi - 1];
286 #else
287 s->file = argv[argi++];
288 #endif /* OS2 */
289 s->u.shf = shf_open(s->file, O_RDONLY, 0, SHF_MAPHI|SHF_CLEXEC);
290 if (s->u.shf == NULL) {
291 exstat = 127; /* POSIX */
292 errorf("%s: %s", s->file, strerror(errno));
293 }
294 kshname = s->file;
295 } else {
296 Flag(FSTDIN) = 1;
297 s = pushs(SSTDIN, ATEMP);
298 s->file = "<stdin>";
299 s->u.shf = shf_fdopen(0, SHF_RD | can_seek(0),
300 (struct shf *) 0);
301 if (isatty(0) && isatty(2)) {
302 Flag(FTALKING) = 1;
303 /* The following only if isatty(0) */
304 s->flags |= SF_TTY;
305 s->u.shf->flags |= SHF_INTERRUPT;
306 s->file = (char *) 0;
307 }
308 }
309
310 /* This bizarreness is mandated by POSIX */
311 {
312 struct stat s_stdin;
313
314 if (fstat(0, &s_stdin) >= 0 && S_ISCHR(s_stdin.st_mode))
315 reset_nonblock(0);
316 }
317
318 /* initialize job control */
319 i = Flag(FMONITOR) != 127;
320 Flag(FMONITOR) = 0;
321 j_init(i);
322 #ifdef EDIT
323 /* Do this after j_init(), as tty_fd is not initialized 'til then */
324 if (Flag(FTALKING))
325 x_init();
326 #endif
327
328 l = e->loc;
329 l->argv = &argv[argi - 1];
330 l->argc = argc - argi;
331 l->argv[0] = (char *) kshname;
332 getopts_reset(1);
333
334 /* Disable during .profile/ENV reading */
335 restricted = Flag(FRESTRICTED);
336 Flag(FRESTRICTED) = 0;
337
338 /* Do this before profile/$ENV so that if it causes problems in them,
339 * user will know why things broke.
340 */
341 if (!current_wd[0] && Flag(FTALKING))
342 warningf(FALSE, "Cannot determine current working directory");
343
344 if (Flag(FLOGIN)) {
345 #ifdef OS2
346 char *profile;
347
348 /* Try to find a profile - first see if $INIT has a value,
349 * then try /etc/profile.ksh, then c:/usr/etc/profile.ksh.
350 */
351 if (!Flag(FPRIVILEGED)
352 && strcmp(profile = substitute("$INIT/profile.ksh", 0),
353 "/profile.ksh"))
354 include(profile, 0, (char **) 0, 1);
355 else if (include("/etc/profile.ksh", 0, (char **) 0, 1) < 0)
356 include("c:/usr/etc/profile.ksh", 0, (char **) 0, 1);
357 if (!Flag(FPRIVILEGED))
358 include(substitute("$HOME/profile.ksh", 0), 0,
359 (char **) 0, 1);
360 #else /* OS2 */
361 include(KSH_SYSTEM_PROFILE, 0, (char **) 0, 1);
362 if (!Flag(FPRIVILEGED))
363 include(substitute("$HOME/.profile", 0), 0,
364 (char **) 0, 1);
365 #endif /* OS2 */
366 }
367
368 if (Flag(FPRIVILEGED))
369 include("/etc/suid_profile", 0, (char **) 0, 1);
370 else {
371 char *env_file;
372
373 #ifndef KSH
374 if (!Flag(FPOSIX))
375 env_file = null;
376 else
377 #endif /* !KSH */
378 /* include $ENV */
379 env_file = str_val(global("ENV"));
380
381 #ifdef DEFAULT_ENV
382 /* If env isn't set, include default environment */
383 if (env_file == null)
384 env_file = DEFAULT_ENV;
385 #endif /* DEFAULT_ENV */
386 env_file = substitute(env_file, DOTILDE);
387 if (*env_file != '\0')
388 include(env_file, 0, (char **) 0, 1);
389 #ifdef OS2
390 else if (Flag(FTALKING))
391 include(substitute("$HOME/kshrc.ksh", 0), 0,
392 (char **) 0, 1);
393 #endif /* OS2 */
394 }
395
396 if (is_restricted(argv[0]) || is_restricted(str_val(global("SHELL"))))
397 restricted = 1;
398 if (restricted) {
399 static const char *const restr_com[] = {
400 "typeset", "-r", "PATH",
401 "ENV", "SHELL",
402 (char *) 0
403 };
404 shcomexec((char **) restr_com);
405 /* After typeset command... */
406 Flag(FRESTRICTED) = 1;
407 }
408
409 if (Flag(FTALKING)) {
410 hist_init(s);
411 #ifdef KSH
412 alarm_init();
413 #endif /* KSH */
414 } else
415 Flag(FTRACKALL) = 1; /* set after ENV */
416
417 shell(s, TRUE); /* doesn't return */
418 return 0;
419 }
420
421 int
422 include(name, argc, argv, intr_ok)
423 const char *name;
424 int argc;
425 char **argv;
426 int intr_ok;
427 {
428 register Source *volatile s = NULL;
429 Source *volatile sold;
430 struct shf *shf;
431 char **volatile old_argv;
432 volatile int old_argc;
433 int i;
434
435 shf = shf_open(name, O_RDONLY, 0, SHF_MAPHI|SHF_CLEXEC);
436 if (shf == NULL)
437 return -1;
438
439 if (argv) {
440 old_argv = e->loc->argv;
441 old_argc = e->loc->argc;
442 } else {
443 old_argv = (char **) 0;
444 old_argc = 0;
445 }
446 sold = source;
447 newenv(E_INCL);
448 i = ksh_sigsetjmp(e->jbuf, 0);
449 if (i) {
450 quitenv();
451 source = sold;
452 if (s)
453 shf_close(s->u.shf);
454 if (old_argv) {
455 e->loc->argv = old_argv;
456 e->loc->argc = old_argc;
457 }
458 switch (i) {
459 case LRETURN:
460 case LERROR:
461 return exstat & 0xff; /* see below */
462 case LINTR:
463 /* intr_ok is set if we are including .profile or $ENV.
464 * If user ^C's out, we don't want to kill the shell...
465 */
466 if (intr_ok && (exstat - 128) != SIGTERM)
467 return 1;
468 /* fall through... */
469 case LEXIT:
470 case LLEAVE:
471 case LSHELL:
472 unwind(i);
473 /*NOREACHED*/
474 default:
475 internal_errorf(1, "include: %d", i);
476 /*NOREACHED*/
477 }
478 }
479 if (argv) {
480 e->loc->argv = argv;
481 e->loc->argc = argc;
482 }
483 s = pushs(SFILE, ATEMP);
484 s->u.shf = shf;
485 s->file = str_save(name, ATEMP);
486 i = shell(s, FALSE);
487 quitenv();
488 source = sold;
489 shf_close(s->u.shf);
490 if (old_argv) {
491 e->loc->argv = old_argv;
492 e->loc->argc = old_argc;
493 }
494 return i & 0xff; /* & 0xff to ensure value not -1 */
495 }
496
497 int
498 command(comm)
499 const char *comm;
500 {
501 register Source *s;
502
503 s = pushs(SSTRING, ATEMP);
504 s->start = s->str = comm;
505 return shell(s, FALSE);
506 }
507
508 /*
509 * run the commands from the input source, returning status.
510 */
511 int
512 shell(s, toplevel)
513 Source *volatile s; /* input source */
514 int volatile toplevel;
515 {
516 struct op *t;
517 volatile int wastty = s->flags & SF_TTY;
518 volatile int attempts = 13;
519 volatile int interactive = Flag(FTALKING) && toplevel;
520 int i;
521
522 newenv(E_PARSE);
523 if (interactive)
524 really_exit = 0;
525 i = ksh_sigsetjmp(e->jbuf, 0);
526 if (i) {
527 s->start = s->str = null;
528 switch (i) {
529 case LINTR: /* we get here if SIGINT not caught or ignored */
530 case LERROR:
531 case LSHELL:
532 if (interactive) {
533 if (i == LINTR)
534 shellf(newline);
535 /* Reset any eof that was read as part of a
536 * multiline command.
537 */
538 if (Flag(FIGNOREEOF) && s->type == SEOF
539 && wastty)
540 s->type = SSTDIN;
541 /* Used by exit command to get back to
542 * top level shell. Kind of strange since
543 * interactive is set if we are reading from
544 * a tty, but to have stopped jobs, one only
545 * needs FMONITOR set (not FTALKING/SF_TTY)...
546 */
547 break;
548 }
549 /* fall through... */
550 case LEXIT:
551 case LLEAVE:
552 case LRETURN:
553 quitenv();
554 unwind(i); /* keep on going */
555 /*NOREACHED*/
556 default:
557 quitenv();
558 internal_errorf(1, "shell: %d", i);
559 /*NOREACHED*/
560 }
561 }
562
563 while (1) {
564 if (trap)
565 runtraps(0);
566
567 if (s->next == NULL)
568 if (Flag(FVERBOSE))
569 s->flags |= SF_ECHO;
570 else
571 s->flags &= ~SF_ECHO;
572
573 if (interactive) {
574 j_notify();
575 #ifdef KSH
576 mcheck();
577 #endif /* KSH */
578 set_prompt(PS1, s);
579 }
580
581 t = compile(s);
582 if (t != NULL && t->type == TEOF) {
583 if (wastty && Flag(FIGNOREEOF) && --attempts > 0) {
584 shellf("Use `exit' to leave ksh\n");
585 s->type = SSTDIN;
586 } else if (wastty && !really_exit
587 && j_stopped_running())
588 {
589 really_exit = 1;
590 s->type = SSTDIN;
591 } else {
592 /* this for POSIX, which says EXIT traps
593 * shall be taken in the environment
594 * immediately after the last command
595 * executed.
596 */
597 if (toplevel)
598 unwind(LEXIT);
599 break;
600 }
601 }
602
603 if (t && (!Flag(FNOEXEC) || (s->flags & SF_TTY)))
604 exstat = execute(t, 0);
605
606 if (t != NULL && t->type != TEOF && interactive && really_exit)
607 really_exit = 0;
608
609 reclaim();
610 }
611 quitenv();
612 return exstat;
613 }
614
615 /* return to closest error handler or shell(), exit if none found */
616 void
617 unwind(i)
618 int i;
619 {
620 /* ordering for EXIT vs ERR is a bit odd (this is what at&t ksh does) */
621 if (i == LEXIT || (Flag(FERREXIT) && (i == LERROR || i == LINTR)
622 && sigtraps[SIGEXIT_].trap))
623 {
624 runtrap(&sigtraps[SIGEXIT_]);
625 i = LLEAVE;
626 } else if (Flag(FERREXIT) && (i == LERROR || i == LINTR)) {
627 runtrap(&sigtraps[SIGERR_]);
628 i = LLEAVE;
629 }
630 while (1) {
631 switch (e->type) {
632 case E_PARSE:
633 case E_FUNC:
634 case E_INCL:
635 case E_LOOP:
636 case E_ERRH:
637 ksh_siglongjmp(e->jbuf, i);
638 /*NOTREACHED*/
639
640 case E_NONE: /* bottom of the stack */
641 {
642 if (Flag(FTALKING))
643 hist_finish();
644 j_exit();
645 remove_temps(func_heredocs);
646 if (i == LINTR) {
647 int sig = exstat - 128;
648
649 /* ham up our death a bit (at&t ksh
650 * only seems to do this for SIGTERM)
651 * Don't do it for SIGQUIT, since we'd
652 * dump a core..
653 */
654 if (sig == SIGINT || sig == SIGTERM) {
655 setsig(&sigtraps[sig], SIG_DFL,
656 SS_RESTORE_CURR|SS_FORCE);
657 kill(0, sig);
658 }
659 }
660 exit(exstat);
661 /* NOTREACHED */
662 }
663
664 default:
665 quitenv();
666 }
667 }
668 }
669
670 void
671 newenv(type)
672 int type;
673 {
674 register struct env *ep;
675
676 ep = (struct env *) alloc(sizeof(*ep), ATEMP);
677 ep->type = type;
678 ep->flags = 0;
679 ainit(&ep->area);
680 ep->loc = e->loc;
681 ep->savefd = NULL;
682 ep->oenv = e;
683 ep->temps = NULL;
684 e = ep;
685 }
686
687 void
688 quitenv()
689 {
690 register struct env *ep = e;
691 register int fd;
692
693 if (ep->oenv == NULL) /* cleanup_parents_env() was called */
694 exit(exstat); /* exit child */
695 if (ep->oenv->loc != ep->loc)
696 popblock();
697 if (ep->savefd != NULL) {
698 for (fd = 0; fd < NUFILE; fd++)
699 /* if ep->savefd[fd] < 0, means fd was closed */
700 if (ep->savefd[fd])
701 restfd(fd, ep->savefd[fd]);
702 if (ep->savefd[2]) /* Clear any write errors */
703 shf_reopen(2, SHF_WR, shl_out);
704 }
705 reclaim();
706 e = e->oenv;
707 afree(ep, ATEMP);
708 }
709
710 /* Called after a fork to cleanup stuff left over from parents environment */
711 void
712 cleanup_parents_env()
713 {
714 struct env *ep;
715 int fd;
716
717 /* Don't clean up temporary files - parent will probably need them.
718 * Also, can't easily reclaim memory since variables, etc. could be
719 * anywyere.
720 */
721
722 /* close all file descriptors hiding in savefd */
723 for (ep = e; ep; ep = ep->oenv) {
724 if (ep->savefd)
725 for (fd = 0; fd < NUFILE; fd++)
726 if (ep->savefd[fd] > 0)
727 close(ep->savefd[fd]);
728 }
729 e->oenv = (struct env *) 0;
730 }
731
732 /* Called just before an execve cleanup stuff temporary files */
733 void
734 cleanup_proc_env()
735 {
736 struct env *ep;
737
738 for (ep = e; ep; ep = ep->oenv)
739 remove_temps(ep->temps);
740 remove_temps(func_heredocs);
741 }
742
743 /* remove temp files and free ATEMP Area */
744 static void
745 reclaim()
746 {
747 remove_temps(e->temps);
748 e->temps = NULL;
749 afreeall(&e->area);
750 }
751
752 static void
753 remove_temps(tp)
754 struct temp *tp;
755 {
756 #ifdef OS2
757 static struct temp *delayed_remove;
758 struct temp *t, **tprev;
759
760 if (delayed_remove) {
761 for (tprev = &delayed_remove, t = delayed_remove; t; t = *tprev)
762 /* No need to check t->pid here... */
763 if (unlink(t->name) >= 0 || errno == ENOENT) {
764 *tprev = t->next;
765 afree(t, APERM);
766 } else
767 tprev = &t->next;
768 }
769 #endif /* OS2 */
770
771 for (; tp != NULL; tp = tp->next)
772 if (tp->pid == procpid) {
773 #ifdef OS2
774 /* OS/2 (and dos) do not allow files that are currently
775 * open to be removed, so we cache it away for future
776 * removal.
777 * XXX should only do this if errno
778 * is Efile-still-open-can't-remove
779 * (but I don't know what that is...)
780 */
781 if (unlink(tp->name) < 0 && errno != ENOENT) {
782 t = (struct temp *) alloc(
783 sizeof(struct temp) + strlen(tp->name) + 1,
784 APERM);
785 memset(t, 0, sizeof(struct temp));
786 strcpy(t->name, tp->name);
787 t->next = delayed_remove;
788 delayed_remove = t;
789 }
790 #else /* OS2 */
791 unlink(tp->name);
792 #endif /* OS2 */
793 }
794 }
795
796 /* Returns true if name refers to a restricted shell */
797 static int
798 is_restricted(name)
799 char *name;
800 {
801 char *p;
802
803 if ((p = ksh_strrchr_dirsep(name)))
804 name = p;
805 /* accepts rsh, rksh, rpdksh, pdrksh, etc. */
806 return (p = strchr(name, 'r')) && strstr(p, "sh");
807 }
808
809 void
810 aerror(ap, msg)
811 Area *ap;
812 const char *msg;
813 {
814 internal_errorf(1, "alloc: %s", msg);
815 errorf(null); /* this is never executed - keeps gcc quiet */
816 /*NOTREACHED*/
817 }
818