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