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