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