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 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 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 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 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 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 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 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 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 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 754 reclaim() 755 { 756 remove_temps(e->temps); 757 e->temps = NULL; 758 afreeall(&e->area); 759 } 760 761 static void 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 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 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