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