main.c revision 1.5 1 /* $NetBSD: main.c,v 1.5 1998/08/19 01:43:23 thorpej 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 /* NOTREACHED */
269 }
270
271 if (Flag(FCOMMAND)) {
272 s = pushs(SSTRING, ATEMP);
273 if (!(s->start = s->str = argv[argi++]))
274 errorf("-c requires an argument");
275 if (argv[argi])
276 kshname = argv[argi++];
277 } else if (argi < argc && !Flag(FSTDIN)) {
278 s = pushs(SFILE, ATEMP);
279 #ifdef OS2
280 /* a bug in os2 extproc shell processing doesn't
281 * pass full pathnames so we have to search for it.
282 * This changes the behavior of 'ksh arg' to search
283 * the users search path but it can't be helped.
284 */
285 s->file = search(argv[argi++], path, R_OK, (int *) 0);
286 if (!s->file || !*s->file)
287 s->file = argv[argi - 1];
288 #else
289 s->file = argv[argi++];
290 #endif /* OS2 */
291 s->u.shf = shf_open(s->file, O_RDONLY, 0, SHF_MAPHI|SHF_CLEXEC);
292 if (s->u.shf == NULL) {
293 exstat = 127; /* POSIX */
294 errorf("%s: %s", s->file, strerror(errno));
295 }
296 kshname = s->file;
297 } else {
298 Flag(FSTDIN) = 1;
299 s = pushs(SSTDIN, ATEMP);
300 s->file = "<stdin>";
301 s->u.shf = shf_fdopen(0, SHF_RD | can_seek(0),
302 (struct shf *) 0);
303 if (isatty(0) && isatty(2)) {
304 Flag(FTALKING) = 1;
305 /* The following only if isatty(0) */
306 s->flags |= SF_TTY;
307 s->u.shf->flags |= SHF_INTERRUPT;
308 s->file = (char *) 0;
309 }
310 }
311
312 /* This bizarreness is mandated by POSIX */
313 {
314 struct stat s_stdin;
315
316 if (fstat(0, &s_stdin) >= 0 && S_ISCHR(s_stdin.st_mode))
317 reset_nonblock(0);
318 }
319
320 /* initialize job control */
321 i = Flag(FMONITOR) != 127;
322 Flag(FMONITOR) = 0;
323 j_init(i);
324 #ifdef EDIT
325 /* Do this after j_init(), as tty_fd is not initialized 'til then */
326 if (Flag(FTALKING))
327 x_init();
328 #endif
329
330 l = e->loc;
331 l->argv = &argv[argi - 1];
332 l->argc = argc - argi;
333 l->argv[0] = (char *) kshname;
334 getopts_reset(1);
335
336 /* Disable during .profile/ENV reading */
337 restricted = Flag(FRESTRICTED);
338 Flag(FRESTRICTED) = 0;
339
340 /* Do this before profile/$ENV so that if it causes problems in them,
341 * user will know why things broke.
342 */
343 if (!current_wd[0] && Flag(FTALKING))
344 warningf(FALSE, "Cannot determine current working directory");
345
346 if (Flag(FLOGIN)) {
347 #ifdef OS2
348 char *profile;
349
350 /* Try to find a profile - first see if $INIT has a value,
351 * then try /etc/profile.ksh, then c:/usr/etc/profile.ksh.
352 */
353 if (!Flag(FPRIVILEGED)
354 && strcmp(profile = substitute("$INIT/profile.ksh", 0),
355 "/profile.ksh"))
356 include(profile, 0, (char **) 0, 1);
357 else if (include("/etc/profile.ksh", 0, (char **) 0, 1) < 0)
358 include("c:/usr/etc/profile.ksh", 0, (char **) 0, 1);
359 if (!Flag(FPRIVILEGED))
360 include(substitute("$HOME/profile.ksh", 0), 0,
361 (char **) 0, 1);
362 #else /* OS2 */
363 include(KSH_SYSTEM_PROFILE, 0, (char **) 0, 1);
364 if (!Flag(FPRIVILEGED))
365 include(substitute("$HOME/.profile", 0), 0,
366 (char **) 0, 1);
367 #endif /* OS2 */
368 }
369
370 if (Flag(FPRIVILEGED))
371 include("/etc/suid_profile", 0, (char **) 0, 1);
372 else {
373 char *env_file;
374
375 #ifndef KSH
376 if (!Flag(FPOSIX))
377 env_file = null;
378 else
379 #endif /* !KSH */
380 /* include $ENV */
381 env_file = str_val(global("ENV"));
382
383 #ifdef DEFAULT_ENV
384 /* If env isn't set, include default environment */
385 if (env_file == null)
386 env_file = DEFAULT_ENV;
387 #endif /* DEFAULT_ENV */
388 env_file = substitute(env_file, DOTILDE);
389 if (*env_file != '\0')
390 include(env_file, 0, (char **) 0, 1);
391 #ifdef OS2
392 else if (Flag(FTALKING))
393 include(substitute("$HOME/kshrc.ksh", 0), 0,
394 (char **) 0, 1);
395 #endif /* OS2 */
396 }
397
398 if (is_restricted(argv[0]) || is_restricted(str_val(global("SHELL"))))
399 restricted = 1;
400 if (restricted) {
401 static const char *const restr_com[] = {
402 "typeset", "-r", "PATH",
403 "ENV", "SHELL",
404 (char *) 0
405 };
406 shcomexec((char **) restr_com);
407 /* After typeset command... */
408 Flag(FRESTRICTED) = 1;
409 }
410
411 if (Flag(FTALKING)) {
412 hist_init(s);
413 #ifdef KSH
414 alarm_init();
415 #endif /* KSH */
416 } else
417 Flag(FTRACKALL) = 1; /* set after ENV */
418
419 shell(s, TRUE); /* doesn't return */
420 return 0;
421 }
422
423 int
424 include(name, argc, argv, intr_ok)
425 const char *name;
426 int argc;
427 char **argv;
428 int intr_ok;
429 {
430 register Source *volatile s = NULL;
431 Source *volatile sold;
432 struct shf *shf;
433 char **volatile old_argv;
434 volatile int old_argc;
435 int i;
436
437 shf = shf_open(name, O_RDONLY, 0, SHF_MAPHI|SHF_CLEXEC);
438 if (shf == NULL)
439 return -1;
440
441 if (argv) {
442 old_argv = e->loc->argv;
443 old_argc = e->loc->argc;
444 } else {
445 old_argv = (char **) 0;
446 old_argc = 0;
447 }
448 sold = source;
449 newenv(E_INCL);
450 i = ksh_sigsetjmp(e->jbuf, 0);
451 if (i) {
452 quitenv();
453 source = sold;
454 if (s)
455 shf_close(s->u.shf);
456 if (old_argv) {
457 e->loc->argv = old_argv;
458 e->loc->argc = old_argc;
459 }
460 switch (i) {
461 case LRETURN:
462 case LERROR:
463 return exstat & 0xff; /* see below */
464 case LINTR:
465 /* intr_ok is set if we are including .profile or $ENV.
466 * If user ^C's out, we don't want to kill the shell...
467 */
468 if (intr_ok && (exstat - 128) != SIGTERM)
469 return 1;
470 /* fall through... */
471 case LEXIT:
472 case LLEAVE:
473 case LSHELL:
474 unwind(i);
475 /*NOREACHED*/
476 default:
477 internal_errorf(1, "include: %d", i);
478 /*NOREACHED*/
479 }
480 }
481 if (argv) {
482 e->loc->argv = argv;
483 e->loc->argc = argc;
484 }
485 s = pushs(SFILE, ATEMP);
486 s->u.shf = shf;
487 s->file = str_save(name, ATEMP);
488 i = shell(s, FALSE);
489 quitenv();
490 source = sold;
491 shf_close(s->u.shf);
492 if (old_argv) {
493 e->loc->argv = old_argv;
494 e->loc->argc = old_argc;
495 }
496 return i & 0xff; /* & 0xff to ensure value not -1 */
497 }
498
499 int
500 command(comm)
501 const char *comm;
502 {
503 register Source *s;
504
505 s = pushs(SSTRING, ATEMP);
506 s->start = s->str = comm;
507 return shell(s, FALSE);
508 }
509
510 /*
511 * run the commands from the input source, returning status.
512 */
513 int
514 shell(s, toplevel)
515 Source *volatile s; /* input source */
516 int volatile toplevel;
517 {
518 struct op *t;
519 volatile int wastty = s->flags & SF_TTY;
520 volatile int attempts = 13;
521 volatile int interactive = Flag(FTALKING) && toplevel;
522 int i;
523
524 newenv(E_PARSE);
525 if (interactive)
526 really_exit = 0;
527 i = ksh_sigsetjmp(e->jbuf, 0);
528 if (i) {
529 s->start = s->str = null;
530 switch (i) {
531 case LINTR: /* we get here if SIGINT not caught or ignored */
532 case LERROR:
533 case LSHELL:
534 if (interactive) {
535 if (i == LINTR)
536 shellf(newline);
537 /* Reset any eof that was read as part of a
538 * multiline command.
539 */
540 if (Flag(FIGNOREEOF) && s->type == SEOF
541 && wastty)
542 s->type = SSTDIN;
543 /* Used by exit command to get back to
544 * top level shell. Kind of strange since
545 * interactive is set if we are reading from
546 * a tty, but to have stopped jobs, one only
547 * needs FMONITOR set (not FTALKING/SF_TTY)...
548 */
549 break;
550 }
551 /* fall through... */
552 case LEXIT:
553 case LLEAVE:
554 case LRETURN:
555 quitenv();
556 unwind(i); /* keep on going */
557 /*NOREACHED*/
558 default:
559 quitenv();
560 internal_errorf(1, "shell: %d", i);
561 /*NOREACHED*/
562 }
563 }
564
565 while (1) {
566 if (trap)
567 runtraps(0);
568
569 if (s->next == NULL) {
570 if (Flag(FVERBOSE))
571 s->flags |= SF_ECHO;
572 else
573 s->flags &= ~SF_ECHO;
574 }
575
576 if (interactive) {
577 j_notify();
578 #ifdef KSH
579 mcheck();
580 #endif /* KSH */
581 set_prompt(PS1, s);
582 }
583
584 t = compile(s);
585 if (t != NULL && t->type == TEOF) {
586 if (wastty && Flag(FIGNOREEOF) && --attempts > 0) {
587 shellf("Use `exit' to leave ksh\n");
588 s->type = SSTDIN;
589 } else if (wastty && !really_exit
590 && j_stopped_running())
591 {
592 really_exit = 1;
593 s->type = SSTDIN;
594 } else {
595 /* this for POSIX, which says EXIT traps
596 * shall be taken in the environment
597 * immediately after the last command
598 * executed.
599 */
600 if (toplevel)
601 unwind(LEXIT);
602 break;
603 }
604 }
605
606 if (t && (!Flag(FNOEXEC) || (s->flags & SF_TTY)))
607 exstat = execute(t, 0);
608
609 if (t != NULL && t->type != TEOF && interactive && really_exit)
610 really_exit = 0;
611
612 reclaim();
613 }
614 quitenv();
615 return exstat;
616 }
617
618 /* return to closest error handler or shell(), exit if none found */
619 void
620 unwind(i)
621 int i;
622 {
623 /* ordering for EXIT vs ERR is a bit odd (this is what at&t ksh does) */
624 if (i == LEXIT || (Flag(FERREXIT) && (i == LERROR || i == LINTR)
625 && sigtraps[SIGEXIT_].trap))
626 {
627 runtrap(&sigtraps[SIGEXIT_]);
628 i = LLEAVE;
629 } else if (Flag(FERREXIT) && (i == LERROR || i == LINTR)) {
630 runtrap(&sigtraps[SIGERR_]);
631 i = LLEAVE;
632 }
633 while (1) {
634 switch (e->type) {
635 case E_PARSE:
636 case E_FUNC:
637 case E_INCL:
638 case E_LOOP:
639 case E_ERRH:
640 ksh_siglongjmp(e->jbuf, i);
641 /*NOTREACHED*/
642
643 case E_NONE: /* bottom of the stack */
644 {
645 if (Flag(FTALKING))
646 hist_finish();
647 j_exit();
648 remove_temps(func_heredocs);
649 if (i == LINTR) {
650 int sig = exstat - 128;
651
652 /* ham up our death a bit (at&t ksh
653 * only seems to do this for SIGTERM)
654 * Don't do it for SIGQUIT, since we'd
655 * dump a core..
656 */
657 if (sig == SIGINT || sig == SIGTERM) {
658 setsig(&sigtraps[sig], SIG_DFL,
659 SS_RESTORE_CURR|SS_FORCE);
660 kill(0, sig);
661 }
662 }
663 exit(exstat);
664 /* NOTREACHED */
665 }
666
667 default:
668 quitenv();
669 }
670 }
671 }
672
673 void
674 newenv(type)
675 int type;
676 {
677 register struct env *ep;
678
679 ep = (struct env *) alloc(sizeof(*ep), ATEMP);
680 ep->type = type;
681 ep->flags = 0;
682 ainit(&ep->area);
683 ep->loc = e->loc;
684 ep->savefd = NULL;
685 ep->oenv = e;
686 ep->temps = NULL;
687 e = ep;
688 }
689
690 void
691 quitenv()
692 {
693 register struct env *ep = e;
694 register int fd;
695
696 if (ep->oenv == NULL) { /* cleanup_parents_env() was called */
697 exit(exstat); /* exit child */
698 /* NOTREACHED */
699 }
700 if (ep->oenv->loc != ep->loc)
701 popblock();
702 if (ep->savefd != NULL) {
703 for (fd = 0; fd < NUFILE; fd++)
704 /* if ep->savefd[fd] < 0, means fd was closed */
705 if (ep->savefd[fd])
706 restfd(fd, ep->savefd[fd]);
707 if (ep->savefd[2]) /* Clear any write errors */
708 shf_reopen(2, SHF_WR, shl_out);
709 }
710 reclaim();
711 e = e->oenv;
712 afree(ep, ATEMP);
713 }
714
715 /* Called after a fork to cleanup stuff left over from parents environment */
716 void
717 cleanup_parents_env()
718 {
719 struct env *ep;
720 int fd;
721
722 /* Don't clean up temporary files - parent will probably need them.
723 * Also, can't easily reclaim memory since variables, etc. could be
724 * anywyere.
725 */
726
727 /* close all file descriptors hiding in savefd */
728 for (ep = e; ep; ep = ep->oenv) {
729 if (ep->savefd)
730 for (fd = 0; fd < NUFILE; fd++)
731 if (ep->savefd[fd] > 0)
732 close(ep->savefd[fd]);
733 }
734 e->oenv = (struct env *) 0;
735 }
736
737 /* Called just before an execve cleanup stuff temporary files */
738 void
739 cleanup_proc_env()
740 {
741 struct env *ep;
742
743 for (ep = e; ep; ep = ep->oenv)
744 remove_temps(ep->temps);
745 remove_temps(func_heredocs);
746 }
747
748 /* remove temp files and free ATEMP Area */
749 static void
750 reclaim()
751 {
752 remove_temps(e->temps);
753 e->temps = NULL;
754 afreeall(&e->area);
755 }
756
757 static void
758 remove_temps(tp)
759 struct temp *tp;
760 {
761 #ifdef OS2
762 static struct temp *delayed_remove;
763 struct temp *t, **tprev;
764
765 if (delayed_remove) {
766 for (tprev = &delayed_remove, t = delayed_remove; t; t = *tprev)
767 /* No need to check t->pid here... */
768 if (unlink(t->name) >= 0 || errno == ENOENT) {
769 *tprev = t->next;
770 afree(t, APERM);
771 } else
772 tprev = &t->next;
773 }
774 #endif /* OS2 */
775
776 for (; tp != NULL; tp = tp->next)
777 if (tp->pid == procpid) {
778 #ifdef OS2
779 /* OS/2 (and dos) do not allow files that are currently
780 * open to be removed, so we cache it away for future
781 * removal.
782 * XXX should only do this if errno
783 * is Efile-still-open-can't-remove
784 * (but I don't know what that is...)
785 */
786 if (unlink(tp->name) < 0 && errno != ENOENT) {
787 t = (struct temp *) alloc(
788 sizeof(struct temp) + strlen(tp->name) + 1,
789 APERM);
790 memset(t, 0, sizeof(struct temp));
791 strcpy(t->name, tp->name);
792 t->next = delayed_remove;
793 delayed_remove = t;
794 }
795 #else /* OS2 */
796 unlink(tp->name);
797 #endif /* OS2 */
798 }
799 }
800
801 /* Returns true if name refers to a restricted shell */
802 static int
803 is_restricted(name)
804 char *name;
805 {
806 char *p;
807
808 if ((p = ksh_strrchr_dirsep(name)))
809 name = p;
810 /* accepts rsh, rksh, rpdksh, pdrksh, etc. */
811 return (p = strchr(name, 'r')) && strstr(p, "sh");
812 }
813
814 void
815 aerror(ap, msg)
816 Area *ap;
817 const char *msg;
818 {
819 internal_errorf(1, "alloc: %s", msg);
820 errorf(null); /* this is never executed - keeps gcc quiet */
821 /*NOTREACHED*/
822 }
823