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