1 /* $OpenBSD: c_sh.c,v 1.13 1999/07/14 13:37:23 millert Exp $ */ 2 3 /* 4 * built-in Bourne commands 5 */ 6 7 #include "sh.h" 8 #include "ksh_stat.h" /* umask() */ 9 #include "ksh_time.h" 10 #include "ksh_times.h" 11 12 static char *clocktos ARGS((clock_t t)); 13 14 15 /* :, false and true */ 16 int 17 c_label(wp) 18 char **wp; 19 { 20 return wp[0][0] == 'f' ? 1 : 0; 21 } 22 23 int 24 c_shift(wp) 25 char **wp; 26 { 27 register struct block *l = e->loc; 28 register int n; 29 long val; 30 char *arg; 31 32 if (ksh_getopt(wp, &builtin_opt, null) == '?') 33 return 1; 34 arg = wp[builtin_opt.optind]; 35 36 if (arg) { 37 evaluate(arg, &val, KSH_UNWIND_ERROR); 38 n = val; 39 } else 40 n = 1; 41 if (n < 0) { 42 bi_errorf("%s: bad number", arg); 43 return (1); 44 } 45 if (l->argc < n) { 46 bi_errorf("nothing to shift"); 47 return (1); 48 } 49 l->argv[n] = l->argv[0]; 50 l->argv += n; 51 l->argc -= n; 52 return 0; 53 } 54 55 int 56 c_umask(wp) 57 char **wp; 58 { 59 register int i; 60 register char *cp; 61 int symbolic = 0; 62 int old_umask; 63 int optc; 64 65 while ((optc = ksh_getopt(wp, &builtin_opt, "S")) != EOF) 66 switch (optc) { 67 case 'S': 68 symbolic = 1; 69 break; 70 case '?': 71 return 1; 72 } 73 cp = wp[builtin_opt.optind]; 74 if (cp == NULL) { 75 old_umask = umask(0); 76 umask(old_umask); 77 if (symbolic) { 78 char buf[18]; 79 int j; 80 81 old_umask = ~old_umask; 82 cp = buf; 83 for (i = 0; i < 3; i++) { 84 *cp++ = "ugo"[i]; 85 *cp++ = '='; 86 for (j = 0; j < 3; j++) 87 if (old_umask & (1 << (8 - (3*i + j)))) 88 *cp++ = "rwx"[j]; 89 *cp++ = ','; 90 } 91 cp[-1] = '\0'; 92 shprintf("%s\n", buf); 93 } else 94 shprintf("%#3.3o\n", old_umask); 95 } else { 96 int new_umask; 97 98 if (digit(*cp)) { 99 for (new_umask = 0; *cp >= '0' && *cp <= '7'; cp++) 100 new_umask = new_umask * 8 + (*cp - '0'); 101 if (*cp) { 102 bi_errorf("bad number"); 103 return 1; 104 } 105 } else { 106 /* symbolic format */ 107 int positions, new_val; 108 char op; 109 110 old_umask = umask(0); 111 umask(old_umask); /* in case of error */ 112 old_umask = ~old_umask; 113 new_umask = old_umask; 114 positions = 0; 115 while (*cp) { 116 while (*cp && strchr("augo", *cp)) 117 switch (*cp++) { 118 case 'a': positions |= 0111; break; 119 case 'u': positions |= 0100; break; 120 case 'g': positions |= 0010; break; 121 case 'o': positions |= 0001; break; 122 } 123 if (!positions) 124 positions = 0111; /* default is a */ 125 if (!strchr("=+-", op = *cp)) 126 break; 127 cp++; 128 new_val = 0; 129 while (*cp && strchr("rwxugoXs", *cp)) 130 switch (*cp++) { 131 case 'r': new_val |= 04; break; 132 case 'w': new_val |= 02; break; 133 case 'x': new_val |= 01; break; 134 case 'u': new_val |= old_umask >> 6; 135 break; 136 case 'g': new_val |= old_umask >> 3; 137 break; 138 case 'o': new_val |= old_umask >> 0; 139 break; 140 case 'X': if (old_umask & 0111) 141 new_val |= 01; 142 break; 143 case 's': /* ignored */ 144 break; 145 } 146 new_val = (new_val & 07) * positions; 147 switch (op) { 148 case '-': 149 new_umask &= ~new_val; 150 break; 151 case '=': 152 new_umask = new_val 153 | (new_umask & ~(positions * 07)); 154 break; 155 case '+': 156 new_umask |= new_val; 157 } 158 if (*cp == ',') { 159 positions = 0; 160 cp++; 161 } else if (!strchr("=+-", *cp)) 162 break; 163 } 164 if (*cp) { 165 bi_errorf("bad mask"); 166 return 1; 167 } 168 new_umask = ~new_umask; 169 } 170 umask(new_umask); 171 } 172 return 0; 173 } 174 175 int 176 c_dot(wp) 177 char **wp; 178 { 179 char *file, *cp; 180 char **argv; 181 int argc; 182 int i; 183 int err; 184 185 if (ksh_getopt(wp, &builtin_opt, null) == '?') 186 return 1; 187 188 if ((cp = wp[builtin_opt.optind]) == NULL) 189 return 0; 190 file = search(cp, path, R_OK, &err); 191 if (file == NULL) { 192 bi_errorf("%s: %s", cp, err ? strerror(err) : "not found"); 193 return 1; 194 } 195 196 /* Set positional parameters? */ 197 if (wp[builtin_opt.optind + 1]) { 198 argv = wp + builtin_opt.optind; 199 argv[0] = e->loc->argv[0]; /* preserve $0 */ 200 for (argc = 0; argv[argc + 1]; argc++) 201 ; 202 } else { 203 argc = 0; 204 argv = (char **) 0; 205 } 206 i = include(file, argc, argv, 0); 207 if (i < 0) { /* should not happen */ 208 bi_errorf("%s: %s", cp, strerror(errno)); 209 return 1; 210 } 211 return i; 212 } 213 214 int 215 c_wait(wp) 216 char **wp; 217 { 218 int UNINITIALIZED(rv); 219 int sig; 220 221 if (ksh_getopt(wp, &builtin_opt, null) == '?') 222 return 1; 223 wp += builtin_opt.optind; 224 if (*wp == (char *) 0) { 225 while (waitfor((char *) 0, &sig) >= 0) 226 ; 227 rv = sig; 228 } else { 229 for (; *wp; wp++) 230 rv = waitfor(*wp, &sig); 231 if (rv < 0) 232 rv = sig ? sig : 127; /* magic exit code: bad job-id */ 233 } 234 return rv; 235 } 236 237 int 238 c_read(wp) 239 char **wp; 240 { 241 register int c = 0; 242 int expand = 1, history = 0; 243 int expanding; 244 int ecode = 0; 245 register char *cp; 246 int fd = 0; 247 struct shf *shf; 248 int optc; 249 const char *emsg; 250 XString cs, xs; 251 struct tbl *vp; 252 char UNINITIALIZED(*xp); 253 254 while ((optc = ksh_getopt(wp, &builtin_opt, "prsu,")) != EOF) 255 switch (optc) { 256 #ifdef KSH 257 case 'p': 258 if ((fd = coproc_getfd(R_OK, &emsg)) < 0) { 259 bi_errorf("-p: %s", emsg); 260 return 1; 261 } 262 break; 263 #endif /* KSH */ 264 case 'r': 265 expand = 0; 266 break; 267 case 's': 268 history = 1; 269 break; 270 case 'u': 271 if (!*(cp = builtin_opt.optarg)) 272 fd = 0; 273 else if ((fd = check_fd(cp, R_OK, &emsg)) < 0) { 274 bi_errorf("-u: %s: %s", cp, emsg); 275 return 1; 276 } 277 break; 278 case '?': 279 return 1; 280 } 281 wp += builtin_opt.optind; 282 283 if (*wp == NULL) 284 *--wp = "REPLY"; 285 286 /* Since we can't necessarily seek backwards on non-regular files, 287 * don't buffer them so we can't read too much. 288 */ 289 shf = shf_reopen(fd, SHF_RD | SHF_INTERRUPT | can_seek(fd), shl_spare); 290 291 if ((cp = strchr(*wp, '?')) != NULL) { 292 *cp = 0; 293 if (isatty(fd)) { 294 /* at&t ksh says it prints prompt on fd if it's open 295 * for writing and is a tty, but it doesn't do it 296 * (it also doesn't check the interactive flag, 297 * as is indicated in the Kornshell book). 298 */ 299 shellf("%s", cp+1); 300 } 301 } 302 303 #ifdef KSH 304 /* If we are reading from the co-process for the first time, 305 * make sure the other side of the pipe is closed first. This allows 306 * the detection of eof. 307 * 308 * This is not compatiable with at&t ksh... the fd is kept so another 309 * coproc can be started with same ouput, however, this means eof 310 * can't be detected... This is why it is closed here. 311 * If this call is removed, remove the eof check below, too. 312 * coproc_readw_close(fd); 313 */ 314 #endif /* KSH */ 315 316 if (history) 317 Xinit(xs, xp, 128, ATEMP); 318 expanding = 0; 319 Xinit(cs, cp, 128, ATEMP); 320 for (; *wp != NULL; wp++) { 321 for (cp = Xstring(cs, cp); ; ) { 322 if (c == '\n' || c == EOF) 323 break; 324 while (1) { 325 c = shf_getc(shf); 326 if (c == '\0' 327 #ifdef OS2 328 || c == '\r' 329 #endif /* OS2 */ 330 ) 331 continue; 332 if (c == EOF && shf_error(shf) 333 && shf_errno(shf) == EINTR) 334 { 335 /* Was the offending signal one that 336 * would normally kill a process? 337 * If so, pretend the read was killed. 338 */ 339 ecode = fatal_trap_check(); 340 341 /* non fatal (eg, CHLD), carry on */ 342 if (!ecode) { 343 shf_clearerr(shf); 344 continue; 345 } 346 } 347 break; 348 } 349 if (history) { 350 Xcheck(xs, xp); 351 Xput(xs, xp, c); 352 } 353 Xcheck(cs, cp); 354 if (expanding) { 355 expanding = 0; 356 if (c == '\n') { 357 c = 0; 358 if (Flag(FTALKING_I) && isatty(fd)) { 359 /* set prompt in case this is 360 * called from .profile or $ENV 361 */ 362 set_prompt(PS2, (Source *) 0); 363 pprompt(prompt, 0); 364 } 365 } else if (c != EOF) 366 Xput(cs, cp, c); 367 continue; 368 } 369 if (expand && c == '\\') { 370 expanding = 1; 371 continue; 372 } 373 if (c == '\n' || c == EOF) 374 break; 375 if (ctype(c, C_IFS)) { 376 if (Xlength(cs, cp) == 0 && ctype(c, C_IFSWS)) 377 continue; 378 if (wp[1]) 379 break; 380 } 381 Xput(cs, cp, c); 382 } 383 /* strip trailing IFS white space from last variable */ 384 if (!wp[1]) 385 while (Xlength(cs, cp) && ctype(cp[-1], C_IFS) 386 && ctype(cp[-1], C_IFSWS)) 387 cp--; 388 Xput(cs, cp, '\0'); 389 vp = global(*wp); 390 /* Must be done before setting export. */ 391 if (vp->flag & RDONLY) { 392 shf_flush(shf); 393 bi_errorf("%s is read only", *wp); 394 return 1; 395 } 396 if (Flag(FEXPORT)) 397 typeset(*wp, EXPORT, 0, 0, 0); 398 if (!setstr(vp, Xstring(cs, cp), KSH_RETURN_ERROR)) { 399 shf_flush(shf); 400 return 1; 401 } 402 } 403 404 shf_flush(shf); 405 if (history) { 406 Xput(xs, xp, '\0'); 407 source->line++; 408 histsave(source->line, Xstring(xs, xp), 1); 409 Xfree(xs, xp); 410 } 411 #ifdef KSH 412 /* if this is the co-process fd, close the file descriptor 413 * (can get eof if and only if all processes are have died, ie, 414 * coproc.njobs is 0 and the pipe is closed). 415 */ 416 if (c == EOF && !ecode) 417 coproc_read_close(fd); 418 #endif /* KSH */ 419 420 return ecode ? ecode : c == EOF; 421 } 422 423 int 424 c_eval(wp) 425 char **wp; 426 { 427 register struct source *s; 428 429 if (ksh_getopt(wp, &builtin_opt, null) == '?') 430 return 1; 431 s = pushs(SWORDS, ATEMP); 432 s->u.strv = wp + builtin_opt.optind; 433 if (!Flag(FPOSIX)) { 434 /* 435 * Handle case where the command is empty due to failed 436 * command substitution, eg, eval "$(false)". 437 * In this case, shell() will not set/change exstat (because 438 * compiled tree is empty), so will use this value. 439 * subst_exstat is cleared in execute(), so should be 0 if 440 * there were no substitutions. 441 * 442 * A strict reading of POSIX says we don't do this (though 443 * it is traditionally done). [from 1003.2-1992] 444 * 3.9.1: Simple Commands 445 * ... If there is a command name, execution shall 446 * continue as described in 3.9.1.1. If there 447 * is no command name, but the command contained a command 448 * substitution, the command shall complete with the exit 449 * status of the last command substitution 450 * 3.9.1.1: Command Search and Execution 451 * ...(1)...(a) If the command name matches the name of 452 * a special built-in utility, that special built-in 453 * utility shall be invoked. 454 * 3.14.5: Eval 455 * ... If there are no arguments, or only null arguments, 456 * eval shall return an exit status of zero. 457 */ 458 exstat = subst_exstat; 459 } 460 461 return shell(s, FALSE); 462 } 463 464 int 465 c_trap(wp) 466 char **wp; 467 { 468 int i; 469 char *s; 470 register Trap *p; 471 472 if (ksh_getopt(wp, &builtin_opt, null) == '?') 473 return 1; 474 wp += builtin_opt.optind; 475 476 if (*wp == NULL) { 477 int anydfl = 0; 478 479 for (p = sigtraps, i = SIGNALS+1; --i >= 0; p++) { 480 if (p->trap == NULL) 481 anydfl = 1; 482 else { 483 shprintf("trap -- "); 484 print_value_quoted(p->trap); 485 shprintf(" %s\n", p->name); 486 } 487 } 488 #if 0 /* this is ugly and not clear POSIX needs it */ 489 /* POSIX may need this so output of trap can be saved and 490 * used to restore trap conditions 491 */ 492 if (anydfl) { 493 shprintf("trap -- -"); 494 for (p = sigtraps, i = SIGNALS+1; --i >= 0; p++) 495 if (p->trap == NULL && p->name) 496 shprintf(" %s", p->name); 497 shprintf(newline); 498 } 499 #endif 500 return 0; 501 } 502 503 /* 504 * Use case sensitive lookup for first arg so the 505 * command 'exit' isn't confused with the pseudo-signal 506 * 'EXIT'. 507 */ 508 s = (gettrap(*wp, FALSE) == NULL) ? *wp++ : NULL; /* get command */ 509 if (s != NULL && s[0] == '-' && s[1] == '\0') 510 s = NULL; 511 512 /* set/clear traps */ 513 while (*wp != NULL) { 514 p = gettrap(*wp++, TRUE); 515 if (p == NULL) { 516 bi_errorf("bad signal %s", wp[-1]); 517 return 1; 518 } 519 settrap(p, s); 520 } 521 return 0; 522 } 523 524 int 525 c_exitreturn(wp) 526 char **wp; 527 { 528 int how = LEXIT; 529 int n; 530 char *arg; 531 532 if (ksh_getopt(wp, &builtin_opt, null) == '?') 533 return 1; 534 arg = wp[builtin_opt.optind]; 535 536 if (arg) { 537 if (!getn(arg, &n)) { 538 exstat = 1; 539 warningf(TRUE, "%s: bad number", arg); 540 } else 541 exstat = n; 542 } 543 if (wp[0][0] == 'r') { /* return */ 544 struct env *ep; 545 546 /* need to tell if this is exit or return so trap exit will 547 * work right (POSIX) 548 */ 549 for (ep = e; ep; ep = ep->oenv) 550 if (STOP_RETURN(ep->type)) { 551 how = LRETURN; 552 break; 553 } 554 } 555 556 if (how == LEXIT && !really_exit && j_stopped_running()) { 557 really_exit = 1; 558 how = LSHELL; 559 } 560 561 quitenv(); /* get rid of any i/o redirections */ 562 unwind(how); 563 /*NOTREACHED*/ 564 return 0; 565 } 566 567 int 568 c_brkcont(wp) 569 char **wp; 570 { 571 int n, quit; 572 struct env *ep, *last_ep = (struct env *) 0; 573 char *arg; 574 575 if (ksh_getopt(wp, &builtin_opt, null) == '?') 576 return 1; 577 arg = wp[builtin_opt.optind]; 578 579 if (!arg) 580 n = 1; 581 else if (!bi_getn(arg, &n)) 582 return 1; 583 quit = n; 584 if (quit <= 0) { 585 /* at&t ksh does this for non-interactive shells only - weird */ 586 bi_errorf("%s: bad value", arg); 587 return 1; 588 } 589 590 /* Stop at E_NONE, E_PARSE, E_FUNC, or E_INCL */ 591 for (ep = e; ep && !STOP_BRKCONT(ep->type); ep = ep->oenv) 592 if (ep->type == E_LOOP) { 593 if (--quit == 0) 594 break; 595 ep->flags |= EF_BRKCONT_PASS; 596 last_ep = ep; 597 } 598 599 if (quit) { 600 /* at&t ksh doesn't print a message - just does what it 601 * can. We print a message 'cause it helps in debugging 602 * scripts, but don't generate an error (ie, keep going). 603 */ 604 if (n == quit) { 605 warningf(TRUE, "%s: cannot %s", wp[0], wp[0]); 606 return 0; 607 } 608 /* POSIX says if n is too big, the last enclosing loop 609 * shall be used. Doesn't say to print an error but we 610 * do anyway 'cause the user messed up. 611 */ 612 last_ep->flags &= ~EF_BRKCONT_PASS; 613 warningf(TRUE, "%s: can only %s %d level(s)", 614 wp[0], wp[0], n - quit); 615 } 616 617 unwind(*wp[0] == 'b' ? LBREAK : LCONTIN); 618 /*NOTREACHED*/ 619 } 620 621 int 622 c_set(wp) 623 char **wp; 624 { 625 int argi, setargs; 626 struct block *l = e->loc; 627 register char **owp = wp; 628 629 if (wp[1] == NULL) { 630 static const char *const args [] = { "set", "-", NULL }; 631 return c_typeset((char **) args); 632 } 633 634 argi = parse_args(wp, OF_SET, &setargs); 635 if (argi < 0) 636 return 1; 637 /* set $# and $* */ 638 if (setargs) { 639 owp = wp += argi - 1; 640 wp[0] = l->argv[0]; /* save $0 */ 641 while (*++wp != NULL) 642 *wp = str_save(*wp, &l->area); 643 l->argc = wp - owp - 1; 644 l->argv = (char **) alloc(sizeofN(char *, l->argc+2), &l->area); 645 for (wp = l->argv; (*wp++ = *owp++) != NULL; ) 646 ; 647 } 648 /* POSIX says set exit status is 0, but old scripts that use 649 * getopt(1), use the construct: set -- `getopt ab:c "$@"` 650 * which assumes the exit value set will be that of the `` 651 * (subst_exstat is cleared in execute() so that it will be 0 652 * if there are no command substitutions). 653 */ 654 return Flag(FPOSIX) ? 0 : subst_exstat; 655 } 656 657 int 658 c_unset(wp) 659 char **wp; 660 { 661 register char *id; 662 int optc, unset_var = 1; 663 int ret = 0; 664 665 while ((optc = ksh_getopt(wp, &builtin_opt, "fv")) != EOF) 666 switch (optc) { 667 case 'f': 668 unset_var = 0; 669 break; 670 case 'v': 671 unset_var = 1; 672 break; 673 case '?': 674 return 1; 675 } 676 wp += builtin_opt.optind; 677 for (; (id = *wp) != NULL; wp++) 678 if (unset_var) { /* unset variable */ 679 struct tbl *vp = global(id); 680 681 if (!(vp->flag & ISSET)) 682 ret = 1; 683 if ((vp->flag&RDONLY)) { 684 bi_errorf("%s is read only", vp->name); 685 return 1; 686 } 687 unset(vp, strchr(id, '[') ? 1 : 0); 688 } else { /* unset function */ 689 if (define(id, (struct op *) NULL)) 690 ret = 1; 691 } 692 return ret; 693 } 694 695 int 696 c_times(wp) 697 char **wp; 698 { 699 struct tms all; 700 701 (void) ksh_times(&all); 702 shprintf("Shell: %8ss user ", clocktos(all.tms_utime)); 703 shprintf("%8ss system\n", clocktos(all.tms_stime)); 704 shprintf("Kids: %8ss user ", clocktos(all.tms_cutime)); 705 shprintf("%8ss system\n", clocktos(all.tms_cstime)); 706 707 return 0; 708 } 709 710 /* 711 * time pipeline (really a statement, not a built-in command) 712 */ 713 int 714 timex(t, f) 715 struct op *t; 716 int f; 717 { 718 #define TF_NOARGS BIT(0) 719 #define TF_NOREAL BIT(1) /* don't report real time */ 720 #define TF_POSIX BIT(2) /* report in posix format */ 721 int rv = 0; 722 struct tms t0, t1, tms; 723 clock_t t0t, t1t = 0; 724 int tf = 0; 725 extern clock_t j_usrtime, j_systime; /* computed by j_wait */ 726 char opts[1]; 727 728 t0t = ksh_times(&t0); 729 if (t->left) { 730 /* 731 * Two ways of getting cpu usage of a command: just use t0 732 * and t1 (which will get cpu usage from other jobs that 733 * finish while we are executing t->left), or get the 734 * cpu usage of t->left. at&t ksh does the former, while 735 * pdksh tries to do the later (the j_usrtime hack doesn't 736 * really work as it only counts the last job). 737 */ 738 j_usrtime = j_systime = 0; 739 if (t->left->type == TCOM) 740 t->left->str = opts; 741 opts[0] = 0; 742 rv = execute(t->left, f | XTIME); 743 tf |= opts[0]; 744 t1t = ksh_times(&t1); 745 } else 746 tf = TF_NOARGS; 747 748 if (tf & TF_NOARGS) { /* ksh93 - report shell times (shell+kids) */ 749 tf |= TF_NOREAL; 750 tms.tms_utime = t0.tms_utime + t0.tms_cutime; 751 tms.tms_stime = t0.tms_stime + t0.tms_cstime; 752 } else { 753 tms.tms_utime = t1.tms_utime - t0.tms_utime + j_usrtime; 754 tms.tms_stime = t1.tms_stime - t0.tms_stime + j_systime; 755 } 756 757 if (!(tf & TF_NOREAL)) 758 shf_fprintf(shl_out, 759 tf & TF_POSIX ? "real %8s\n" : "%8ss real ", 760 clocktos(t1t - t0t)); 761 shf_fprintf(shl_out, tf & TF_POSIX ? "user %8s\n" : "%8ss user ", 762 clocktos(tms.tms_utime)); 763 shf_fprintf(shl_out, tf & TF_POSIX ? "sys %8s\n" : "%8ss system\n", 764 clocktos(tms.tms_stime)); 765 shf_flush(shl_out); 766 767 return rv; 768 } 769 770 void 771 timex_hook(t, app) 772 struct op *t; 773 char ** volatile *app; 774 { 775 char **wp = *app; 776 int optc; 777 int i, j; 778 Getopt opt; 779 780 ksh_getopt_reset(&opt, 0); 781 opt.optind = 0; /* start at the start */ 782 while ((optc = ksh_getopt(wp, &opt, ":p")) != EOF) 783 switch (optc) { 784 case 'p': 785 t->str[0] |= TF_POSIX; 786 break; 787 case '?': 788 errorf("time: -%s unknown option", opt.optarg); 789 case ':': 790 errorf("time: -%s requires an argument", 791 opt.optarg); 792 } 793 /* Copy command words down over options. */ 794 if (opt.optind != 0) { 795 for (i = 0; i < opt.optind; i++) 796 afree(wp[i], ATEMP); 797 for (i = 0, j = opt.optind; (wp[i] = wp[j]); i++, j++) 798 ; 799 } 800 if (!wp[0]) 801 t->str[0] |= TF_NOARGS; 802 *app = wp; 803 } 804 805 static char * 806 clocktos(t) 807 clock_t t; 808 { 809 static char temp[22]; /* enough for 64 bit clock_t */ 810 register int i; 811 register char *cp = temp + sizeof(temp); 812 813 /* note: posix says must use max precision, ie, if clk_tck is 814 * 1000, must print 3 places after decimal (if non-zero, else 1). 815 */ 816 if (CLK_TCK != 100) /* convert to 1/100'ths */ 817 t = (t < 1000000000/CLK_TCK) ? 818 (t * 100) / CLK_TCK : (t / CLK_TCK) * 100; 819 820 *--cp = '\0'; 821 for (i = -2; i <= 0 || t > 0; i++) { 822 if (i == 0) 823 *--cp = '.'; 824 *--cp = '0' + (char)(t%10); 825 t /= 10; 826 } 827 return cp; 828 } 829 830 /* exec with no args - args case is taken care of in comexec() */ 831 int 832 c_exec(wp) 833 char ** wp; 834 { 835 int i; 836 837 /* make sure redirects stay in place */ 838 if (e->savefd != NULL) { 839 for (i = 0; i < NUFILE; i++) { 840 if (e->savefd[i] > 0) 841 close(e->savefd[i]); 842 /* 843 * For ksh keep anything > 2 private, 844 * for sh, let them be (POSIX says what 845 * happens is unspecified and the bourne shell 846 * keeps them open). 847 */ 848 #ifdef KSH 849 if (!Flag(FSH) && i > 2 && e->savefd[i]) 850 fd_clexec(i); 851 #endif /* KSH */ 852 } 853 e->savefd = NULL; 854 } 855 return 0; 856 } 857 858 /* dummy function, special case in comexec() */ 859 int 860 c_builtin(wp) 861 char ** wp; 862 { 863 return 0; 864 } 865 866 extern int c_test ARGS((char **wp)); /* in c_test.c */ 867 extern int c_ulimit ARGS((char **wp)); /* in c_ulimit.c */ 868 869 /* A leading = means assignments before command are kept; 870 * a leading * means a POSIX special builtin; 871 * a leading + means a POSIX regular builtin 872 * (* and + should not be combined). 873 */ 874 const struct builtin shbuiltins [] = { 875 {"*=.", c_dot}, 876 {"*=:", c_label}, 877 {"[", c_test}, 878 {"*=break", c_brkcont}, 879 {"=builtin", c_builtin}, 880 {"*=continue", c_brkcont}, 881 {"*=eval", c_eval}, 882 {"*=exec", c_exec}, 883 {"*=exit", c_exitreturn}, 884 {"+false", c_label}, 885 {"*=return", c_exitreturn}, 886 {"*=set", c_set}, 887 {"*=shift", c_shift}, 888 {"=times", c_times}, 889 {"*=trap", c_trap}, 890 {"+=wait", c_wait}, 891 {"+read", c_read}, 892 {"test", c_test}, 893 {"+true", c_label}, 894 {"ulimit", c_ulimit}, 895 {"+umask", c_umask}, 896 {"*=unset", c_unset}, 897 #ifdef OS2 898 /* In OS2, the first line of a file can be "extproc name", which 899 * tells the command interpreter (cmd.exe) to use name to execute 900 * the file. For this to be useful, ksh must ignore commands 901 * starting with extproc and this does the trick... 902 */ 903 {"extproc", c_label}, 904 #endif /* OS2 */ 905 {NULL, NULL} 906 }; 907