Home | History | Annotate | Download | only in src
      1 /*	$OpenBSD: c_ksh.c,v 1.34 2013/12/17 16:37:05 deraadt Exp $	*/
      2 /*	$OpenBSD: c_sh.c,v 1.45 2014/08/27 08:26:04 jmc Exp $	*/
      3 /*	$OpenBSD: c_test.c,v 1.18 2009/03/01 20:11:06 otto Exp $	*/
      4 /*	$OpenBSD: c_ulimit.c,v 1.19 2013/11/28 10:33:37 sobrado Exp $	*/
      5 
      6 /*-
      7  * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009,
      8  *		 2010, 2011, 2012, 2013, 2014, 2015
      9  *	Thorsten Glaser <tg (at) mirbsd.org>
     10  *
     11  * Provided that these terms and disclaimer and all copyright notices
     12  * are retained or reproduced in an accompanying document, permission
     13  * is granted to deal in this work without restriction, including un-
     14  * limited rights to use, publicly perform, distribute, sell, modify,
     15  * merge, give away, or sublicence.
     16  *
     17  * This work is provided "AS IS" and WITHOUT WARRANTY of any kind, to
     18  * the utmost extent permitted by applicable law, neither express nor
     19  * implied; without malicious intent or gross negligence. In no event
     20  * may a licensor, author or contributor be held liable for indirect,
     21  * direct, other damage, loss, or other issues arising in any way out
     22  * of dealing in the work, even if advised of the possibility of such
     23  * damage or existence of a defect, except proven that it results out
     24  * of said person's immediate fault when using the work as intended.
     25  */
     26 
     27 #include "sh.h"
     28 
     29 #if HAVE_SELECT
     30 #if HAVE_SYS_BSDTYPES_H
     31 #include <sys/bsdtypes.h>
     32 #endif
     33 #if HAVE_SYS_SELECT_H
     34 #include <sys/select.h>
     35 #endif
     36 #if HAVE_BSTRING_H
     37 #include <bstring.h>
     38 #endif
     39 #endif
     40 
     41 __RCSID("$MirOS: src/bin/mksh/funcs.c,v 1.259.2.5 2015/04/19 19:18:16 tg Exp $");
     42 
     43 #if HAVE_KILLPG
     44 /*
     45  * use killpg if < -1 since -1 does special things
     46  * for some non-killpg-endowed kills
     47  */
     48 #define mksh_kill(p,s)	((p) < -1 ? killpg(-(p), (s)) : kill((p), (s)))
     49 #else
     50 /* cross fingers and hope kill is killpg-endowed */
     51 #define mksh_kill	kill
     52 #endif
     53 
     54 /* XXX conditions correct? */
     55 #if !defined(RLIM_INFINITY) && !defined(MKSH_NO_LIMITS)
     56 #define MKSH_NO_LIMITS	1
     57 #endif
     58 
     59 #ifdef MKSH_NO_LIMITS
     60 #define c_ulimit	c_true
     61 #endif
     62 
     63 #if !defined(MKSH_UNEMPLOYED) && HAVE_GETSID
     64 static int c_suspend(const char **);
     65 #endif
     66 
     67 /* getn() that prints error */
     68 static int
     69 bi_getn(const char *as, int *ai)
     70 {
     71 	int rv;
     72 
     73 	if (!(rv = getn(as, ai)))
     74 		bi_errorf("%s: %s", as, "bad number");
     75 	return (rv);
     76 }
     77 
     78 static int
     79 c_true(const char **wp MKSH_A_UNUSED)
     80 {
     81 	return (0);
     82 }
     83 
     84 static int
     85 c_false(const char **wp MKSH_A_UNUSED)
     86 {
     87 	return (1);
     88 }
     89 
     90 /*
     91  * A leading = means assignments before command are kept.
     92  * A leading * means a POSIX special builtin.
     93  */
     94 const struct builtin mkshbuiltins[] = {
     95 	{"*=.", c_dot},
     96 	{"*=:", c_true},
     97 	{"[", c_test},
     98 	/* no =: AT&T manual wrong */
     99 	{Talias, c_alias},
    100 	{"*=break", c_brkcont},
    101 	{Tgbuiltin, c_builtin},
    102 	{"cat", c_cat},
    103 	{"cd", c_cd},
    104 	/* dash compatibility hack */
    105 	{"chdir", c_cd},
    106 	{"command", c_command},
    107 	{"*=continue", c_brkcont},
    108 	{"echo", c_print},
    109 	{"*=eval", c_eval},
    110 	{"*=exec", c_exec},
    111 	{"*=exit", c_exitreturn},
    112 	{Tsgexport, c_typeset},
    113 	{"false", c_false},
    114 	{"fc", c_fc},
    115 	{"getopts", c_getopts},
    116 	{"=global", c_typeset},
    117 	{"jobs", c_jobs},
    118 	{"kill", c_kill},
    119 	{"let", c_let},
    120 	{"let]", c_let},
    121 	{"print", c_print},
    122 	{"pwd", c_pwd},
    123 	{"read", c_read},
    124 	{Tsgreadonly, c_typeset},
    125 	{"realpath", c_realpath},
    126 	{"rename", c_rename},
    127 	{"*=return", c_exitreturn},
    128 	{Tsgset, c_set},
    129 	{"*=shift", c_shift},
    130 #if !defined(MKSH_UNEMPLOYED) && HAVE_GETSID
    131 	{"suspend", c_suspend},
    132 #endif
    133 	{"test", c_test},
    134 	{"*=times", c_times},
    135 	{"*=trap", c_trap},
    136 	{"true", c_true},
    137 	{T_typeset, c_typeset},
    138 	{"ulimit", c_ulimit},
    139 	{"umask", c_umask},
    140 	{Tunalias, c_unalias},
    141 	{Tsgunset, c_unset},
    142 	{"=wait", c_wait},
    143 	{"whence", c_whence},
    144 #ifndef MKSH_UNEMPLOYED
    145 	{"bg", c_fgbg},
    146 	{"fg", c_fgbg},
    147 #endif
    148 #ifndef MKSH_NO_CMDLINE_EDITING
    149 	{"bind", c_bind},
    150 #endif
    151 #if HAVE_MKNOD
    152 	{"mknod", c_mknod},
    153 #endif
    154 #ifdef MKSH_PRINTF_BUILTIN
    155 	{"printf", c_printf},
    156 #endif
    157 #if HAVE_SELECT
    158 	{"sleep", c_sleep},
    159 #endif
    160 #ifdef __MirBSD__
    161 	/* alias to "true" for historical reasons */
    162 	{"domainname", c_true},
    163 #endif
    164 	{NULL, (int (*)(const char **))NULL}
    165 };
    166 
    167 struct kill_info {
    168 	int num_width;
    169 	int name_width;
    170 };
    171 
    172 static const struct t_op {
    173 	char op_text[4];
    174 	Test_op op_num;
    175 } u_ops[] = {
    176 	{"-a",	TO_FILAXST },
    177 	{"-b",	TO_FILBDEV },
    178 	{"-c",	TO_FILCDEV },
    179 	{"-d",	TO_FILID },
    180 	{"-e",	TO_FILEXST },
    181 	{"-f",	TO_FILREG },
    182 	{"-G",	TO_FILGID },
    183 	{"-g",	TO_FILSETG },
    184 	{"-h",	TO_FILSYM },
    185 	{"-H",	TO_FILCDF },
    186 	{"-k",	TO_FILSTCK },
    187 	{"-L",	TO_FILSYM },
    188 	{"-n",	TO_STNZE },
    189 	{"-O",	TO_FILUID },
    190 	{"-o",	TO_OPTION },
    191 	{"-p",	TO_FILFIFO },
    192 	{"-r",	TO_FILRD },
    193 	{"-s",	TO_FILGZ },
    194 	{"-S",	TO_FILSOCK },
    195 	{"-t",	TO_FILTT },
    196 	{"-u",	TO_FILSETU },
    197 	{"-w",	TO_FILWR },
    198 	{"-x",	TO_FILEX },
    199 	{"-z",	TO_STZER },
    200 	{"",	TO_NONOP }
    201 };
    202 static const struct t_op b_ops[] = {
    203 	{"=",	TO_STEQL },
    204 	{"==",	TO_STEQL },
    205 	{"!=",	TO_STNEQ },
    206 	{"<",	TO_STLT },
    207 	{">",	TO_STGT },
    208 	{"-eq",	TO_INTEQ },
    209 	{"-ne",	TO_INTNE },
    210 	{"-gt",	TO_INTGT },
    211 	{"-ge",	TO_INTGE },
    212 	{"-lt",	TO_INTLT },
    213 	{"-le",	TO_INTLE },
    214 	{"-ef",	TO_FILEQ },
    215 	{"-nt",	TO_FILNT },
    216 	{"-ot",	TO_FILOT },
    217 	{"",	TO_NONOP }
    218 };
    219 
    220 static int test_oexpr(Test_env *, bool);
    221 static int test_aexpr(Test_env *, bool);
    222 static int test_nexpr(Test_env *, bool);
    223 static int test_primary(Test_env *, bool);
    224 static Test_op ptest_isa(Test_env *, Test_meta);
    225 static const char *ptest_getopnd(Test_env *, Test_op, bool);
    226 static void ptest_error(Test_env *, int, const char *);
    227 static char *kill_fmt_entry(char *, size_t, unsigned int, const void *);
    228 static void p_time(struct shf *, bool, long, int, int,
    229     const char *, const char *);
    230 
    231 int
    232 c_pwd(const char **wp)
    233 {
    234 	int optc;
    235 	bool physical = tobool(Flag(FPHYSICAL));
    236 	char *p, *allocd = NULL;
    237 
    238 	while ((optc = ksh_getopt(wp, &builtin_opt, "LP")) != -1)
    239 		switch (optc) {
    240 		case 'L':
    241 			physical = false;
    242 			break;
    243 		case 'P':
    244 			physical = true;
    245 			break;
    246 		case '?':
    247 			return (1);
    248 		}
    249 	wp += builtin_opt.optind;
    250 
    251 	if (wp[0]) {
    252 		bi_errorf("too many arguments");
    253 		return (1);
    254 	}
    255 	p = current_wd[0] ? (physical ? allocd = do_realpath(current_wd) :
    256 	    current_wd) : NULL;
    257 	/* LINTED use of access */
    258 	if (p && access(p, R_OK) < 0)
    259 		p = NULL;
    260 	if (!p && !(p = allocd = ksh_get_wd())) {
    261 		bi_errorf("%s: %s", "can't determine current directory",
    262 		    cstrerror(errno));
    263 		return (1);
    264 	}
    265 	shprintf("%s\n", p);
    266 	afree(allocd, ATEMP);
    267 	return (0);
    268 }
    269 
    270 static const char *s_ptr;
    271 static int s_get(void);
    272 static void s_put(int);
    273 
    274 int
    275 c_print(const char **wp)
    276 {
    277 #define PO_NL		BIT(0)	/* print newline */
    278 #define PO_EXPAND	BIT(1)	/* expand backslash sequences */
    279 #define PO_PMINUSMINUS	BIT(2)	/* print a -- argument */
    280 #define PO_HIST		BIT(3)	/* print to history instead of stdout */
    281 #define PO_COPROC	BIT(4)	/* printing to coprocess: block SIGPIPE */
    282 	int fd = 1, c;
    283 	int flags = PO_EXPAND | PO_NL;
    284 	const char *s, *emsg;
    285 	XString xs;
    286 	char *xp;
    287 
    288 	if (wp[0][0] == 'e') {
    289 		/* echo builtin */
    290 		wp++;
    291 #ifdef MKSH_MIDNIGHTBSD01ASH_COMPAT
    292 		if (Flag(FSH)) {
    293 			/*
    294 			 * MidnightBSD /bin/sh needs a BSD echo, that is,
    295 			 * one that supports -e but does not enable it by
    296 			 * default
    297 			 */
    298 			flags = PO_NL;
    299 		}
    300 #endif
    301 		if (Flag(FPOSIX) ||
    302 #ifndef MKSH_MIDNIGHTBSD01ASH_COMPAT
    303 		    Flag(FSH) ||
    304 #endif
    305 		    Flag(FAS_BUILTIN)) {
    306 			/* Debian Policy 10.4 compliant "echo" builtin */
    307 			if (*wp && !strcmp(*wp, "-n")) {
    308 				/* we recognise "-n" only as the first arg */
    309 				flags = 0;
    310 				wp++;
    311 			} else
    312 				/* otherwise, we print everything as-is */
    313 				flags = PO_NL;
    314 		} else {
    315 			int nflags = flags;
    316 
    317 			/**
    318 			 * a compromise between sysV and BSD echo commands:
    319 			 * escape sequences are enabled by default, and -n,
    320 			 * -e and -E are recognised if they appear in argu-
    321 			 * ments with no illegal options (ie, echo -nq will
    322 			 * print -nq).
    323 			 * Different from sysV echo since options are reco-
    324 			 * gnised, different from BSD echo since escape se-
    325 			 * quences are enabled by default.
    326 			 */
    327 
    328 			while ((s = *wp) && *s == '-' && s[1]) {
    329 				while (*++s)
    330 					if (*s == 'n')
    331 						nflags &= ~PO_NL;
    332 					else if (*s == 'e')
    333 						nflags |= PO_EXPAND;
    334 					else if (*s == 'E')
    335 						nflags &= ~PO_EXPAND;
    336 					else
    337 						/*
    338 						 * bad option: don't use
    339 						 * nflags, print argument
    340 						 */
    341 						break;
    342 
    343 				if (*s)
    344 					break;
    345 				wp++;
    346 				flags = nflags;
    347 			}
    348 		}
    349 	} else {
    350 		int optc;
    351 		const char *opts = "Rnprsu,";
    352 
    353 		while ((optc = ksh_getopt(wp, &builtin_opt, opts)) != -1)
    354 			switch (optc) {
    355 			case 'R':
    356 				/* fake BSD echo command */
    357 				flags |= PO_PMINUSMINUS;
    358 				flags &= ~PO_EXPAND;
    359 				opts = "ne";
    360 				break;
    361 			case 'e':
    362 				flags |= PO_EXPAND;
    363 				break;
    364 			case 'n':
    365 				flags &= ~PO_NL;
    366 				break;
    367 			case 'p':
    368 				if ((fd = coproc_getfd(W_OK, &emsg)) < 0) {
    369 					bi_errorf("%s: %s", "-p", emsg);
    370 					return (1);
    371 				}
    372 				break;
    373 			case 'r':
    374 				flags &= ~PO_EXPAND;
    375 				break;
    376 			case 's':
    377 				flags |= PO_HIST;
    378 				break;
    379 			case 'u':
    380 				if (!*(s = builtin_opt.optarg))
    381 					fd = 0;
    382 				else if ((fd = check_fd(s, W_OK, &emsg)) < 0) {
    383 					bi_errorf("%s: %s: %s", "-u", s, emsg);
    384 					return (1);
    385 				}
    386 				break;
    387 			case '?':
    388 				return (1);
    389 			}
    390 
    391 		if (!(builtin_opt.info & GI_MINUSMINUS)) {
    392 			/* treat a lone - like -- */
    393 			if (wp[builtin_opt.optind] &&
    394 			    ksh_isdash(wp[builtin_opt.optind]))
    395 				builtin_opt.optind++;
    396 		} else if (flags & PO_PMINUSMINUS)
    397 			builtin_opt.optind--;
    398 		wp += builtin_opt.optind;
    399 	}
    400 
    401 	Xinit(xs, xp, 128, ATEMP);
    402 
    403 	while (*wp != NULL) {
    404 		s = *wp;
    405 		while ((c = *s++) != '\0') {
    406 			Xcheck(xs, xp);
    407 			if ((flags & PO_EXPAND) && c == '\\') {
    408 				s_ptr = s;
    409 				c = unbksl(false, s_get, s_put);
    410 				s = s_ptr;
    411 				if (c == -1) {
    412 					/* rejected by generic function */
    413 					switch ((c = *s++)) {
    414 					case 'c':
    415 						flags &= ~PO_NL;
    416 						/* AT&T brain damage */
    417 						continue;
    418 					case '\0':
    419 						s--;
    420 						c = '\\';
    421 						break;
    422 					default:
    423 						Xput(xs, xp, '\\');
    424 					}
    425 				} else if ((unsigned int)c > 0xFF) {
    426 					/* generic function returned Unicode */
    427 					char ts[4];
    428 
    429 					ts[utf_wctomb(ts, c - 0x100)] = 0;
    430 					for (c = 0; ts[c]; ++c)
    431 						Xput(xs, xp, ts[c]);
    432 					continue;
    433 				}
    434 			}
    435 			Xput(xs, xp, c);
    436 		}
    437 		if (*++wp != NULL)
    438 			Xput(xs, xp, ' ');
    439 	}
    440 	if (flags & PO_NL)
    441 		Xput(xs, xp, '\n');
    442 
    443 	if (flags & PO_HIST) {
    444 		Xput(xs, xp, '\0');
    445 		histsave(&source->line, Xstring(xs, xp), true, false);
    446 		Xfree(xs, xp);
    447 	} else {
    448 		int len = Xlength(xs, xp);
    449 		int opipe = 0;
    450 
    451 		/*
    452 		 * Ensure we aren't killed by a SIGPIPE while writing to
    453 		 * a coprocess. AT&T ksh doesn't seem to do this (seems
    454 		 * to just check that the co-process is alive which is
    455 		 * not enough).
    456 		 */
    457 		if (coproc.write >= 0 && coproc.write == fd) {
    458 			flags |= PO_COPROC;
    459 			opipe = block_pipe();
    460 		}
    461 		for (s = Xstring(xs, xp); len > 0; ) {
    462 			if ((c = write(fd, s, len)) < 0) {
    463 				if (flags & PO_COPROC)
    464 					restore_pipe(opipe);
    465 				if (errno == EINTR) {
    466 					/* allow user to ^C out */
    467 					intrcheck();
    468 					if (flags & PO_COPROC)
    469 						opipe = block_pipe();
    470 					continue;
    471 				}
    472 				return (1);
    473 			}
    474 			s += c;
    475 			len -= c;
    476 		}
    477 		if (flags & PO_COPROC)
    478 			restore_pipe(opipe);
    479 	}
    480 
    481 	return (0);
    482 }
    483 
    484 static int
    485 s_get(void)
    486 {
    487 	return (*s_ptr++);
    488 }
    489 
    490 static void
    491 s_put(int c MKSH_A_UNUSED)
    492 {
    493 	--s_ptr;
    494 }
    495 
    496 int
    497 c_whence(const char **wp)
    498 {
    499 	struct tbl *tp;
    500 	const char *id;
    501 	bool pflag = false, vflag = false, Vflag = false;
    502 	int rv = 0, optc, fcflags;
    503 	bool iam_whence = wp[0][0] == 'w';
    504 	const char *opts = iam_whence ? "pv" : "pvV";
    505 
    506 	while ((optc = ksh_getopt(wp, &builtin_opt, opts)) != -1)
    507 		switch (optc) {
    508 		case 'p':
    509 			pflag = true;
    510 			break;
    511 		case 'v':
    512 			vflag = true;
    513 			break;
    514 		case 'V':
    515 			Vflag = true;
    516 			break;
    517 		case '?':
    518 			return (1);
    519 		}
    520 	wp += builtin_opt.optind;
    521 
    522 	fcflags = FC_BI | FC_PATH | FC_FUNC;
    523 	if (!iam_whence) {
    524 		/* Note that -p on its own is deal with in comexec() */
    525 		if (pflag)
    526 			fcflags |= FC_DEFPATH;
    527 		/*
    528 		 * Convert command options to whence options - note that
    529 		 * command -pV uses a different path search than whence -v
    530 		 * or whence -pv. This should be considered a feature.
    531 		 */
    532 		vflag = Vflag;
    533 	}
    534 	if (pflag)
    535 		fcflags &= ~(FC_BI | FC_FUNC);
    536 
    537 	while ((vflag || rv == 0) && (id = *wp++) != NULL) {
    538 		uint32_t h = 0;
    539 
    540 		tp = NULL;
    541 		if ((iam_whence || vflag) && !pflag)
    542 			tp = ktsearch(&keywords, id, h = hash(id));
    543 		if (!tp && !pflag) {
    544 			tp = ktsearch(&aliases, id, h ? h : hash(id));
    545 			if (tp && !(tp->flag & ISSET))
    546 				tp = NULL;
    547 		}
    548 		if (!tp)
    549 			tp = findcom(id, fcflags);
    550 		if (vflag || (tp->type != CALIAS && tp->type != CEXEC &&
    551 		    tp->type != CTALIAS))
    552 			shf_puts(id, shl_stdout);
    553 		if (vflag) {
    554 			switch (tp->type) {
    555 			case CKEYWD:
    556 			case CALIAS:
    557 			case CFUNC:
    558 			case CSHELL:
    559 				shf_puts(" is a", shl_stdout);
    560 				break;
    561 			}
    562 			switch (tp->type) {
    563 			case CKEYWD:
    564 			case CSHELL:
    565 			case CTALIAS:
    566 			case CEXEC:
    567 				shf_putc(' ', shl_stdout);
    568 				break;
    569 			}
    570 		}
    571 
    572 		switch (tp->type) {
    573 		case CKEYWD:
    574 			if (vflag)
    575 				shf_puts("reserved word", shl_stdout);
    576 			break;
    577 		case CALIAS:
    578 			if (vflag)
    579 				shprintf("n %s%s for ",
    580 				    (tp->flag & EXPORT) ? "exported " : null,
    581 				    Talias);
    582 			if (!iam_whence && !vflag)
    583 				shprintf("%s %s=", Talias, id);
    584 			print_value_quoted(shl_stdout, tp->val.s);
    585 			break;
    586 		case CFUNC:
    587 			if (vflag) {
    588 				if (tp->flag & EXPORT)
    589 					shf_puts("n exported", shl_stdout);
    590 				if (tp->flag & TRACE)
    591 					shf_puts(" traced", shl_stdout);
    592 				if (!(tp->flag & ISSET)) {
    593 					shf_puts(" undefined", shl_stdout);
    594 					if (tp->u.fpath)
    595 						shprintf(" (autoload from %s)",
    596 						    tp->u.fpath);
    597 				}
    598 				shf_puts(T_function, shl_stdout);
    599 			}
    600 			break;
    601 		case CSHELL:
    602 			if (vflag) {
    603 				if (tp->flag & SPEC_BI)
    604 					shf_puts("special ", shl_stdout);
    605 				shprintf("%s %s", "shell", Tbuiltin);
    606 			}
    607 			break;
    608 		case CTALIAS:
    609 		case CEXEC:
    610 			if (tp->flag & ISSET) {
    611 				if (vflag) {
    612 					shf_puts("is ", shl_stdout);
    613 					if (tp->type == CTALIAS)
    614 						shprintf("a tracked %s%s for ",
    615 						    (tp->flag & EXPORT) ?
    616 						    "exported " : null,
    617 						    Talias);
    618 				}
    619 				shf_puts(tp->val.s, shl_stdout);
    620 			} else {
    621 				if (vflag)
    622 					shf_puts("not found", shl_stdout);
    623 				rv = 1;
    624 			}
    625 			break;
    626 		default:
    627 			shf_puts(" is *GOK*", shl_stdout);
    628 			break;
    629 		}
    630 		if (vflag || !rv)
    631 			shf_putc('\n', shl_stdout);
    632 	}
    633 	return (rv);
    634 }
    635 
    636 /* Deal with command -vV - command -p dealt with in comexec() */
    637 int
    638 c_command(const char **wp)
    639 {
    640 	/*
    641 	 * Let c_whence do the work. Note that c_command() must be
    642 	 * a distinct function from c_whence() (tested in comexec()).
    643 	 */
    644 	return (c_whence(wp));
    645 }
    646 
    647 /* typeset, global, export, and readonly */
    648 static void c_typeset_vardump(struct tbl *, uint32_t, int, bool, bool);
    649 static void c_typeset_vardump_recursive(struct block *, uint32_t, int, bool,
    650     bool);
    651 int
    652 c_typeset(const char **wp)
    653 {
    654 	struct tbl *vp, **p;
    655 	uint32_t fset = 0, fclr = 0, flag;
    656 	int thing = 0, field = 0, base = 0, i;
    657 	struct block *l;
    658 	const char *opts;
    659 	const char *fieldstr = NULL, *basestr = NULL;
    660 	bool localv = false, func = false, pflag = false, istset = true;
    661 	enum namerefflag new_refflag = SRF_NOP;
    662 
    663 	switch (**wp) {
    664 
    665 	/* export */
    666 	case 'e':
    667 		fset |= EXPORT;
    668 		istset = false;
    669 		break;
    670 
    671 	/* readonly */
    672 	case 'r':
    673 		fset |= RDONLY;
    674 		istset = false;
    675 		break;
    676 
    677 	/* set */
    678 	case 's':
    679 		/* called with 'typeset -' */
    680 		break;
    681 
    682 	/* typeset */
    683 	case 't':
    684 		localv = true;
    685 		break;
    686 	}
    687 
    688 	/* see comment below regarding possible opions */
    689 	opts = istset ? "L#R#UZ#afi#lnprtux" : "p";
    690 
    691 	builtin_opt.flags |= GF_PLUSOPT;
    692 	/*
    693 	 * AT&T ksh seems to have 0-9 as options which are multiplied
    694 	 * to get a number that is used with -L, -R, -Z or -i (eg, -1R2
    695 	 * sets right justify in a field of 12). This allows options
    696 	 * to be grouped in an order (eg, -Lu12), but disallows -i8 -L3 and
    697 	 * does not allow the number to be specified as a separate argument
    698 	 * Here, the number must follow the RLZi option, but is optional
    699 	 * (see the # kludge in ksh_getopt()).
    700 	 */
    701 	while ((i = ksh_getopt(wp, &builtin_opt, opts)) != -1) {
    702 		flag = 0;
    703 		switch (i) {
    704 		case 'L':
    705 			flag = LJUST;
    706 			fieldstr = builtin_opt.optarg;
    707 			break;
    708 		case 'R':
    709 			flag = RJUST;
    710 			fieldstr = builtin_opt.optarg;
    711 			break;
    712 		case 'U':
    713 			/*
    714 			 * AT&T ksh uses u, but this conflicts with
    715 			 * upper/lower case. If this option is changed,
    716 			 * need to change the -U below as well
    717 			 */
    718 			flag = INT_U;
    719 			break;
    720 		case 'Z':
    721 			flag = ZEROFIL;
    722 			fieldstr = builtin_opt.optarg;
    723 			break;
    724 		case 'a':
    725 			/*
    726 			 * this is supposed to set (-a) or unset (+a) the
    727 			 * indexed array attribute; it does nothing on an
    728 			 * existing regular string or indexed array though
    729 			 */
    730 			break;
    731 		case 'f':
    732 			func = true;
    733 			break;
    734 		case 'i':
    735 			flag = INTEGER;
    736 			basestr = builtin_opt.optarg;
    737 			break;
    738 		case 'l':
    739 			flag = LCASEV;
    740 			break;
    741 		case 'n':
    742 			new_refflag = (builtin_opt.info & GI_PLUS) ?
    743 			    SRF_DISABLE : SRF_ENABLE;
    744 			break;
    745 		/* export, readonly: POSIX -p flag */
    746 		case 'p':
    747 			/* typeset: show values as well */
    748 			pflag = true;
    749 			if (istset)
    750 				continue;
    751 			break;
    752 		case 'r':
    753 			flag = RDONLY;
    754 			break;
    755 		case 't':
    756 			flag = TRACE;
    757 			break;
    758 		case 'u':
    759 			/* upper case / autoload */
    760 			flag = UCASEV_AL;
    761 			break;
    762 		case 'x':
    763 			flag = EXPORT;
    764 			break;
    765 		case '?':
    766 			return (1);
    767 		}
    768 		if (builtin_opt.info & GI_PLUS) {
    769 			fclr |= flag;
    770 			fset &= ~flag;
    771 			thing = '+';
    772 		} else {
    773 			fset |= flag;
    774 			fclr &= ~flag;
    775 			thing = '-';
    776 		}
    777 	}
    778 
    779 	if (fieldstr && !bi_getn(fieldstr, &field))
    780 		return (1);
    781 	if (basestr && (!getn(basestr, &base) || base < 1 || base > 36)) {
    782 		bi_errorf("%s: %s", "bad integer base", basestr);
    783 		return (1);
    784 	}
    785 
    786 	if (!(builtin_opt.info & GI_MINUSMINUS) && wp[builtin_opt.optind] &&
    787 	    (wp[builtin_opt.optind][0] == '-' ||
    788 	    wp[builtin_opt.optind][0] == '+') &&
    789 	    wp[builtin_opt.optind][1] == '\0') {
    790 		thing = wp[builtin_opt.optind][0];
    791 		builtin_opt.optind++;
    792 	}
    793 
    794 	if (func && (((fset|fclr) & ~(TRACE|UCASEV_AL|EXPORT)) ||
    795 	    new_refflag != SRF_NOP)) {
    796 		bi_errorf("only -t, -u and -x options may be used with -f");
    797 		return (1);
    798 	}
    799 	if (wp[builtin_opt.optind]) {
    800 		/*
    801 		 * Take care of exclusions.
    802 		 * At this point, flags in fset are cleared in fclr and vice
    803 		 * versa. This property should be preserved.
    804 		 */
    805 		if (fset & LCASEV)
    806 			/* LCASEV has priority over UCASEV_AL */
    807 			fset &= ~UCASEV_AL;
    808 		if (fset & LJUST)
    809 			/* LJUST has priority over RJUST */
    810 			fset &= ~RJUST;
    811 		if ((fset & (ZEROFIL|LJUST)) == ZEROFIL) {
    812 			/* -Z implies -ZR */
    813 			fset |= RJUST;
    814 			fclr &= ~RJUST;
    815 		}
    816 		/*
    817 		 * Setting these attributes clears the others, unless they
    818 		 * are also set in this command
    819 		 */
    820 		if ((fset & (LJUST | RJUST | ZEROFIL | UCASEV_AL | LCASEV |
    821 		    INTEGER | INT_U | INT_L)) || new_refflag != SRF_NOP)
    822 			fclr |= ~fset & (LJUST | RJUST | ZEROFIL | UCASEV_AL |
    823 			    LCASEV | INTEGER | INT_U | INT_L);
    824 	}
    825 	if (new_refflag != SRF_NOP) {
    826 		fclr &= ~(ARRAY | ASSOC);
    827 		fset &= ~(ARRAY | ASSOC);
    828 		fclr |= EXPORT;
    829 		fset |= ASSOC;
    830 		if (new_refflag == SRF_DISABLE)
    831 			fclr |= ASSOC;
    832 	}
    833 
    834 	/* set variables and attributes */
    835 	if (wp[builtin_opt.optind] &&
    836 	    /* not "typeset -p varname" */
    837 	    !(!func && pflag && !(fset | fclr))) {
    838 		int rv = 0;
    839 		struct tbl *f;
    840 
    841 		if (localv && !func)
    842 			fset |= LOCAL;
    843 		for (i = builtin_opt.optind; wp[i]; i++) {
    844 			if (func) {
    845 				f = findfunc(wp[i], hash(wp[i]),
    846 				    tobool(fset & UCASEV_AL));
    847 				if (!f) {
    848 					/* AT&T ksh does ++rv: bogus */
    849 					rv = 1;
    850 					continue;
    851 				}
    852 				if (fset | fclr) {
    853 					f->flag |= fset;
    854 					f->flag &= ~fclr;
    855 				} else {
    856 					fpFUNCTf(shl_stdout, 0,
    857 					    tobool(f->flag & FKSH),
    858 					    wp[i], f->val.t);
    859 					shf_putc('\n', shl_stdout);
    860 				}
    861 			} else if (!typeset(wp[i], fset, fclr, field, base)) {
    862 				bi_errorf("%s: %s", wp[i], "is not an identifier");
    863 				return (1);
    864 			}
    865 		}
    866 		return (rv);
    867 	}
    868 
    869 	/* list variables and attributes */
    870 
    871 	/* no difference at this point.. */
    872 	flag = fset | fclr;
    873 	if (func) {
    874 		for (l = e->loc; l; l = l->next) {
    875 			for (p = ktsort(&l->funs); (vp = *p++); ) {
    876 				if (flag && (vp->flag & flag) == 0)
    877 					continue;
    878 				if (thing == '-')
    879 					fpFUNCTf(shl_stdout, 0,
    880 					    tobool(vp->flag & FKSH),
    881 					    vp->name, vp->val.t);
    882 				else
    883 					shf_puts(vp->name, shl_stdout);
    884 				shf_putc('\n', shl_stdout);
    885 			}
    886 		}
    887 	} else if (wp[builtin_opt.optind]) {
    888 		for (i = builtin_opt.optind; wp[i]; i++) {
    889 			varsearch(e->loc, &vp, wp[i], hash(wp[i]));
    890 			c_typeset_vardump(vp, flag, thing, pflag, istset);
    891 		}
    892 	} else
    893 		c_typeset_vardump_recursive(e->loc, flag, thing, pflag, istset);
    894 	return (0);
    895 }
    896 
    897 static void
    898 c_typeset_vardump_recursive(struct block *l, uint32_t flag, int thing,
    899     bool pflag, bool istset)
    900 {
    901 	struct tbl **blockvars, *vp;
    902 
    903 	if (l->next)
    904 		c_typeset_vardump_recursive(l->next, flag, thing, pflag, istset);
    905 	blockvars = ktsort(&l->vars);
    906 	while ((vp = *blockvars++))
    907 		c_typeset_vardump(vp, flag, thing, pflag, istset);
    908 	/*XXX doesnt this leak? */
    909 }
    910 
    911 static void
    912 c_typeset_vardump(struct tbl *vp, uint32_t flag, int thing, bool pflag,
    913     bool istset)
    914 {
    915 	struct tbl *tvp;
    916 	int any_set = 0;
    917 	char *s;
    918 
    919 	if (!vp)
    920 		return;
    921 
    922 	/*
    923 	 * See if the parameter is set (for arrays, if any
    924 	 * element is set).
    925 	 */
    926 	for (tvp = vp; tvp; tvp = tvp->u.array)
    927 		if (tvp->flag & ISSET) {
    928 			any_set = 1;
    929 			break;
    930 		}
    931 
    932 	/*
    933 	 * Check attributes - note that all array elements
    934 	 * have (should have?) the same attributes, so checking
    935 	 * the first is sufficient.
    936 	 *
    937 	 * Report an unset param only if the user has
    938 	 * explicitly given it some attribute (like export);
    939 	 * otherwise, after "echo $FOO", we would report FOO...
    940 	 */
    941 	if (!any_set && !(vp->flag & USERATTRIB))
    942 		return;
    943 	if (flag && (vp->flag & flag) == 0)
    944 		return;
    945 	if (!(vp->flag & ARRAY))
    946 		/* optimise later conditionals */
    947 		any_set = 0;
    948 	do {
    949 		/*
    950 		 * Ignore array elements that aren't set unless there
    951 		 * are no set elements, in which case the first is
    952 		 * reported on
    953 		 */
    954 		if (any_set && !(vp->flag & ISSET))
    955 			continue;
    956 		/* no arguments */
    957 		if (!thing && !flag) {
    958 			if (any_set == 1) {
    959 				shprintf("%s %s %s\n", Tset, "-A", vp->name);
    960 				any_set = 2;
    961 			}
    962 			/*
    963 			 * AT&T ksh prints things like export, integer,
    964 			 * leftadj, zerofill, etc., but POSIX says must
    965 			 * be suitable for re-entry...
    966 			 */
    967 			shprintf("%s %s", Ttypeset, "");
    968 			if (((vp->flag & (ARRAY | ASSOC)) == ASSOC))
    969 				shprintf("%s ", "-n");
    970 			if ((vp->flag & INTEGER))
    971 				shprintf("%s ", "-i");
    972 			if ((vp->flag & EXPORT))
    973 				shprintf("%s ", "-x");
    974 			if ((vp->flag & RDONLY))
    975 				shprintf("%s ", "-r");
    976 			if ((vp->flag & TRACE))
    977 				shprintf("%s ", "-t");
    978 			if ((vp->flag & LJUST))
    979 				shprintf("-L%d ", vp->u2.field);
    980 			if ((vp->flag & RJUST))
    981 				shprintf("-R%d ", vp->u2.field);
    982 			if ((vp->flag & ZEROFIL))
    983 				shprintf("%s ", "-Z");
    984 			if ((vp->flag & LCASEV))
    985 				shprintf("%s ", "-l");
    986 			if ((vp->flag & UCASEV_AL))
    987 				shprintf("%s ", "-u");
    988 			if ((vp->flag & INT_U))
    989 				shprintf("%s ", "-U");
    990 		} else if (pflag) {
    991 			shprintf("%s %s", istset ? Ttypeset :
    992 			    (flag & EXPORT) ? Texport : Treadonly, "");
    993 		}
    994 		if (any_set)
    995 			shprintf("%s[%lu]", vp->name, arrayindex(vp));
    996 		else
    997 			shf_puts(vp->name, shl_stdout);
    998 		if ((!thing && !flag && pflag) ||
    999 		    (thing == '-' && (vp->flag & ISSET))) {
   1000 			s = str_val(vp);
   1001 			shf_putc('=', shl_stdout);
   1002 			/* AT&T ksh can't have justified integers... */
   1003 			if ((vp->flag & (INTEGER | LJUST | RJUST)) == INTEGER)
   1004 				shf_puts(s, shl_stdout);
   1005 			else
   1006 				print_value_quoted(shl_stdout, s);
   1007 		}
   1008 		shf_putc('\n', shl_stdout);
   1009 
   1010 		/*
   1011 		 * Only report first 'element' of an array with
   1012 		 * no set elements.
   1013 		 */
   1014 		if (!any_set)
   1015 			return;
   1016 	} while ((vp = vp->u.array));
   1017 }
   1018 
   1019 int
   1020 c_alias(const char **wp)
   1021 {
   1022 	struct table *t = &aliases;
   1023 	int rv = 0, prefix = 0;
   1024 	bool rflag = false, tflag, Uflag = false, pflag = false;
   1025 	uint32_t xflag = 0;
   1026 	int optc;
   1027 
   1028 	builtin_opt.flags |= GF_PLUSOPT;
   1029 	while ((optc = ksh_getopt(wp, &builtin_opt, "dprtUx")) != -1) {
   1030 		prefix = builtin_opt.info & GI_PLUS ? '+' : '-';
   1031 		switch (optc) {
   1032 		case 'd':
   1033 #ifdef MKSH_NOPWNAM
   1034 			t = NULL;	/* fix "alias -dt" */
   1035 #else
   1036 			t = &homedirs;
   1037 #endif
   1038 			break;
   1039 		case 'p':
   1040 			pflag = true;
   1041 			break;
   1042 		case 'r':
   1043 			rflag = true;
   1044 			break;
   1045 		case 't':
   1046 			t = &taliases;
   1047 			break;
   1048 		case 'U':
   1049 			/*
   1050 			 * kludge for tracked alias initialization
   1051 			 * (don't do a path search, just make an entry)
   1052 			 */
   1053 			Uflag = true;
   1054 			break;
   1055 		case 'x':
   1056 			xflag = EXPORT;
   1057 			break;
   1058 		case '?':
   1059 			return (1);
   1060 		}
   1061 	}
   1062 #ifdef MKSH_NOPWNAM
   1063 	if (t == NULL)
   1064 		return (0);
   1065 #endif
   1066 	wp += builtin_opt.optind;
   1067 
   1068 	if (!(builtin_opt.info & GI_MINUSMINUS) && *wp &&
   1069 	    (wp[0][0] == '-' || wp[0][0] == '+') && wp[0][1] == '\0') {
   1070 		prefix = wp[0][0];
   1071 		wp++;
   1072 	}
   1073 
   1074 	tflag = t == &taliases;
   1075 
   1076 	/* "hash -r" means reset all the tracked aliases.. */
   1077 	if (rflag) {
   1078 		static const char *args[] = {
   1079 			Tunalias, "-ta", NULL
   1080 		};
   1081 
   1082 		if (!tflag || *wp) {
   1083 			shprintf("%s: -r flag can only be used with -t"
   1084 			    " and without arguments\n", Talias);
   1085 			return (1);
   1086 		}
   1087 		ksh_getopt_reset(&builtin_opt, GF_ERROR);
   1088 		return (c_unalias(args));
   1089 	}
   1090 
   1091 	if (*wp == NULL) {
   1092 		struct tbl *ap, **p;
   1093 
   1094 		for (p = ktsort(t); (ap = *p++) != NULL; )
   1095 			if ((ap->flag & (ISSET|xflag)) == (ISSET|xflag)) {
   1096 				if (pflag)
   1097 					shprintf("%s ", Talias);
   1098 				shf_puts(ap->name, shl_stdout);
   1099 				if (prefix != '+') {
   1100 					shf_putc('=', shl_stdout);
   1101 					print_value_quoted(shl_stdout, ap->val.s);
   1102 				}
   1103 				shf_putc('\n', shl_stdout);
   1104 			}
   1105 	}
   1106 
   1107 	for (; *wp != NULL; wp++) {
   1108 		const char *alias = *wp, *val, *newval;
   1109 		char *xalias = NULL;
   1110 		struct tbl *ap;
   1111 		uint32_t h;
   1112 
   1113 		if ((val = cstrchr(alias, '='))) {
   1114 			strndupx(xalias, alias, val++ - alias, ATEMP);
   1115 			alias = xalias;
   1116 		}
   1117 		h = hash(alias);
   1118 		if (val == NULL && !tflag && !xflag) {
   1119 			ap = ktsearch(t, alias, h);
   1120 			if (ap != NULL && (ap->flag&ISSET)) {
   1121 				if (pflag)
   1122 					shprintf("%s ", Talias);
   1123 				shf_puts(ap->name, shl_stdout);
   1124 				if (prefix != '+') {
   1125 					shf_putc('=', shl_stdout);
   1126 					print_value_quoted(shl_stdout, ap->val.s);
   1127 				}
   1128 				shf_putc('\n', shl_stdout);
   1129 			} else {
   1130 				shprintf("%s %s %s\n", alias, Talias,
   1131 				    "not found");
   1132 				rv = 1;
   1133 			}
   1134 			continue;
   1135 		}
   1136 		ap = ktenter(t, alias, h);
   1137 		ap->type = tflag ? CTALIAS : CALIAS;
   1138 		/* Are we setting the value or just some flags? */
   1139 		if ((val && !tflag) || (!val && tflag && !Uflag)) {
   1140 			if (ap->flag&ALLOC) {
   1141 				ap->flag &= ~(ALLOC|ISSET);
   1142 				afree(ap->val.s, APERM);
   1143 			}
   1144 			/* ignore values for -t (AT&T ksh does this) */
   1145 			newval = tflag ?
   1146 			    search_path(alias, path, X_OK, NULL) :
   1147 			    val;
   1148 			if (newval) {
   1149 				strdupx(ap->val.s, newval, APERM);
   1150 				ap->flag |= ALLOC|ISSET;
   1151 			} else
   1152 				ap->flag &= ~ISSET;
   1153 		}
   1154 		ap->flag |= DEFINED;
   1155 		if (prefix == '+')
   1156 			ap->flag &= ~xflag;
   1157 		else
   1158 			ap->flag |= xflag;
   1159 		afree(xalias, ATEMP);
   1160 	}
   1161 
   1162 	return (rv);
   1163 }
   1164 
   1165 int
   1166 c_unalias(const char **wp)
   1167 {
   1168 	struct table *t = &aliases;
   1169 	struct tbl *ap;
   1170 	int optc, rv = 0;
   1171 	bool all = false;
   1172 
   1173 	while ((optc = ksh_getopt(wp, &builtin_opt, "adt")) != -1)
   1174 		switch (optc) {
   1175 		case 'a':
   1176 			all = true;
   1177 			break;
   1178 		case 'd':
   1179 #ifdef MKSH_NOPWNAM
   1180 			/* fix "unalias -dt" */
   1181 			t = NULL;
   1182 #else
   1183 			t = &homedirs;
   1184 #endif
   1185 			break;
   1186 		case 't':
   1187 			t = &taliases;
   1188 			break;
   1189 		case '?':
   1190 			return (1);
   1191 		}
   1192 #ifdef MKSH_NOPWNAM
   1193 	if (t == NULL)
   1194 		return (0);
   1195 #endif
   1196 	wp += builtin_opt.optind;
   1197 
   1198 	for (; *wp != NULL; wp++) {
   1199 		ap = ktsearch(t, *wp, hash(*wp));
   1200 		if (ap == NULL) {
   1201 			/* POSIX */
   1202 			rv = 1;
   1203 			continue;
   1204 		}
   1205 		if (ap->flag&ALLOC) {
   1206 			ap->flag &= ~(ALLOC|ISSET);
   1207 			afree(ap->val.s, APERM);
   1208 		}
   1209 		ap->flag &= ~(DEFINED|ISSET|EXPORT);
   1210 	}
   1211 
   1212 	if (all) {
   1213 		struct tstate ts;
   1214 
   1215 		for (ktwalk(&ts, t); (ap = ktnext(&ts)); ) {
   1216 			if (ap->flag&ALLOC) {
   1217 				ap->flag &= ~(ALLOC|ISSET);
   1218 				afree(ap->val.s, APERM);
   1219 			}
   1220 			ap->flag &= ~(DEFINED|ISSET|EXPORT);
   1221 		}
   1222 	}
   1223 
   1224 	return (rv);
   1225 }
   1226 
   1227 int
   1228 c_let(const char **wp)
   1229 {
   1230 	int rv = 1;
   1231 	mksh_ari_t val;
   1232 
   1233 	if (wp[1] == NULL)
   1234 		/* AT&T ksh does this */
   1235 		bi_errorf("no arguments");
   1236 	else
   1237 		for (wp++; *wp; wp++)
   1238 			if (!evaluate(*wp, &val, KSH_RETURN_ERROR, true)) {
   1239 				/* distinguish error from zero result */
   1240 				rv = 2;
   1241 				break;
   1242 			} else
   1243 				rv = val == 0;
   1244 	return (rv);
   1245 }
   1246 
   1247 int
   1248 c_jobs(const char **wp)
   1249 {
   1250 	int optc, flag = 0, nflag = 0, rv = 0;
   1251 
   1252 	while ((optc = ksh_getopt(wp, &builtin_opt, "lpnz")) != -1)
   1253 		switch (optc) {
   1254 		case 'l':
   1255 			flag = 1;
   1256 			break;
   1257 		case 'p':
   1258 			flag = 2;
   1259 			break;
   1260 		case 'n':
   1261 			nflag = 1;
   1262 			break;
   1263 		case 'z':
   1264 			/* debugging: print zombies */
   1265 			nflag = -1;
   1266 			break;
   1267 		case '?':
   1268 			return (1);
   1269 		}
   1270 	wp += builtin_opt.optind;
   1271 	if (!*wp) {
   1272 		if (j_jobs(NULL, flag, nflag))
   1273 			rv = 1;
   1274 	} else {
   1275 		for (; *wp; wp++)
   1276 			if (j_jobs(*wp, flag, nflag))
   1277 				rv = 1;
   1278 	}
   1279 	return (rv);
   1280 }
   1281 
   1282 #ifndef MKSH_UNEMPLOYED
   1283 int
   1284 c_fgbg(const char **wp)
   1285 {
   1286 	bool bg = strcmp(*wp, "bg") == 0;
   1287 	int rv = 0;
   1288 
   1289 	if (!Flag(FMONITOR)) {
   1290 		bi_errorf("job control not enabled");
   1291 		return (1);
   1292 	}
   1293 	if (ksh_getopt(wp, &builtin_opt, null) == '?')
   1294 		return (1);
   1295 	wp += builtin_opt.optind;
   1296 	if (*wp)
   1297 		for (; *wp; wp++)
   1298 			rv = j_resume(*wp, bg);
   1299 	else
   1300 		rv = j_resume("%%", bg);
   1301 	return (bg ? 0 : rv);
   1302 }
   1303 #endif
   1304 
   1305 /* format a single kill item */
   1306 static char *
   1307 kill_fmt_entry(char *buf, size_t buflen, unsigned int i, const void *arg)
   1308 {
   1309 	const struct kill_info *ki = (const struct kill_info *)arg;
   1310 
   1311 	i++;
   1312 	shf_snprintf(buf, buflen, "%*u %*s %s",
   1313 	    ki->num_width, i,
   1314 	    ki->name_width, sigtraps[i].name,
   1315 	    sigtraps[i].mess);
   1316 	return (buf);
   1317 }
   1318 
   1319 int
   1320 c_kill(const char **wp)
   1321 {
   1322 	Trap *t = NULL;
   1323 	const char *p;
   1324 	bool lflag = false;
   1325 	int i, n, rv, sig;
   1326 
   1327 	/* assume old style options if -digits or -UPPERCASE */
   1328 	if ((p = wp[1]) && *p == '-' && (ksh_isdigit(p[1]) ||
   1329 	    ksh_isupper(p[1]))) {
   1330 		if (!(t = gettrap(p + 1, false))) {
   1331 			bi_errorf("bad signal '%s'", p + 1);
   1332 			return (1);
   1333 		}
   1334 		i = (wp[2] && strcmp(wp[2], "--") == 0) ? 3 : 2;
   1335 	} else {
   1336 		int optc;
   1337 
   1338 		while ((optc = ksh_getopt(wp, &builtin_opt, "ls:")) != -1)
   1339 			switch (optc) {
   1340 			case 'l':
   1341 				lflag = true;
   1342 				break;
   1343 			case 's':
   1344 				if (!(t = gettrap(builtin_opt.optarg, true))) {
   1345 					bi_errorf("bad signal '%s'",
   1346 					    builtin_opt.optarg);
   1347 					return (1);
   1348 				}
   1349 				break;
   1350 			case '?':
   1351 				return (1);
   1352 			}
   1353 		i = builtin_opt.optind;
   1354 	}
   1355 	if ((lflag && t) || (!wp[i] && !lflag)) {
   1356 #ifndef MKSH_SMALL
   1357 		shf_puts("usage:\tkill [-s signame | -signum | -signame]"
   1358 		    " { job | pid | pgrp } ...\n"
   1359 		    "\tkill -l [exit_status ...]\n", shl_out);
   1360 #endif
   1361 		bi_errorfz();
   1362 		return (1);
   1363 	}
   1364 
   1365 	if (lflag) {
   1366 		if (wp[i]) {
   1367 			for (; wp[i]; i++) {
   1368 				if (!bi_getn(wp[i], &n))
   1369 					return (1);
   1370 #if (NSIG < 128)
   1371 				if (n > 128 && n < 128 + NSIG)
   1372 					n -= 128;
   1373 #endif
   1374 				if (n > 0 && n < NSIG)
   1375 					shprintf("%s\n", sigtraps[n].name);
   1376 				else
   1377 					shprintf("%d\n", n);
   1378 			}
   1379 		} else {
   1380 			ssize_t w, mess_cols, mess_octs;
   1381 			int j;
   1382 			struct kill_info ki;
   1383 
   1384 			for (j = NSIG, ki.num_width = 1; j >= 10; j /= 10)
   1385 				ki.num_width++;
   1386 			ki.name_width = mess_cols = mess_octs = 0;
   1387 			for (j = 0; j < NSIG; j++) {
   1388 				w = strlen(sigtraps[j].name);
   1389 				if (w > ki.name_width)
   1390 					ki.name_width = w;
   1391 				w = strlen(sigtraps[j].mess);
   1392 				if (w > mess_octs)
   1393 					mess_octs = w;
   1394 				w = utf_mbswidth(sigtraps[j].mess);
   1395 				if (w > mess_cols)
   1396 					mess_cols = w;
   1397 			}
   1398 
   1399 			print_columns(shl_stdout, (unsigned int)(NSIG - 1),
   1400 			    kill_fmt_entry, (void *)&ki,
   1401 			    ki.num_width + 1 + ki.name_width + 1 + mess_octs,
   1402 			    ki.num_width + 1 + ki.name_width + 1 + mess_cols,
   1403 			    true);
   1404 		}
   1405 		return (0);
   1406 	}
   1407 	rv = 0;
   1408 	sig = t ? t->signal : SIGTERM;
   1409 	for (; (p = wp[i]); i++) {
   1410 		if (*p == '%') {
   1411 			if (j_kill(p, sig))
   1412 				rv = 1;
   1413 		} else if (!getn(p, &n)) {
   1414 			bi_errorf("%s: %s", p,
   1415 			    "arguments must be jobs or process IDs");
   1416 			rv = 1;
   1417 		} else {
   1418 			if (mksh_kill(n, sig) < 0) {
   1419 				bi_errorf("%s: %s", p, cstrerror(errno));
   1420 				rv = 1;
   1421 			}
   1422 		}
   1423 	}
   1424 	return (rv);
   1425 }
   1426 
   1427 void
   1428 getopts_reset(int val)
   1429 {
   1430 	if (val >= 1) {
   1431 		ksh_getopt_reset(&user_opt, GF_NONAME | GF_PLUSOPT);
   1432 		user_opt.optind = user_opt.uoptind = val;
   1433 	}
   1434 }
   1435 
   1436 int
   1437 c_getopts(const char **wp)
   1438 {
   1439 	int argc, optc, rv;
   1440 	const char *opts, *var;
   1441 	char buf[3];
   1442 	struct tbl *vq, *voptarg;
   1443 
   1444 	if (ksh_getopt(wp, &builtin_opt, null) == '?')
   1445 		return (1);
   1446 	wp += builtin_opt.optind;
   1447 
   1448 	opts = *wp++;
   1449 	if (!opts) {
   1450 		bi_errorf("missing %s argument", "options");
   1451 		return (1);
   1452 	}
   1453 
   1454 	var = *wp++;
   1455 	if (!var) {
   1456 		bi_errorf("missing %s argument", "name");
   1457 		return (1);
   1458 	}
   1459 	if (!*var || *skip_varname(var, true)) {
   1460 		bi_errorf("%s: %s", var, "is not an identifier");
   1461 		return (1);
   1462 	}
   1463 
   1464 	if (e->loc->next == NULL) {
   1465 		internal_warningf("%s: %s", "c_getopts", "no argv");
   1466 		return (1);
   1467 	}
   1468 	/* Which arguments are we parsing... */
   1469 	if (*wp == NULL)
   1470 		wp = e->loc->next->argv;
   1471 	else
   1472 		*--wp = e->loc->next->argv[0];
   1473 
   1474 	/* Check that our saved state won't cause a core dump... */
   1475 	for (argc = 0; wp[argc]; argc++)
   1476 		;
   1477 	if (user_opt.optind > argc ||
   1478 	    (user_opt.p != 0 &&
   1479 	    user_opt.p > strlen(wp[user_opt.optind - 1]))) {
   1480 		bi_errorf("arguments changed since last call");
   1481 		return (1);
   1482 	}
   1483 
   1484 	user_opt.optarg = NULL;
   1485 	optc = ksh_getopt(wp, &user_opt, opts);
   1486 
   1487 	if (optc >= 0 && optc != '?' && (user_opt.info & GI_PLUS)) {
   1488 		buf[0] = '+';
   1489 		buf[1] = optc;
   1490 		buf[2] = '\0';
   1491 	} else {
   1492 		/*
   1493 		 * POSIX says var is set to ? at end-of-options, AT&T ksh
   1494 		 * sets it to null - we go with POSIX...
   1495 		 */
   1496 		buf[0] = optc < 0 ? '?' : optc;
   1497 		buf[1] = '\0';
   1498 	}
   1499 
   1500 	/* AT&T ksh93 in fact does change OPTIND for unknown options too */
   1501 	user_opt.uoptind = user_opt.optind;
   1502 
   1503 	voptarg = global("OPTARG");
   1504 	/* AT&T ksh clears ro and int */
   1505 	voptarg->flag &= ~RDONLY;
   1506 	/* Paranoia: ensure no bizarre results. */
   1507 	if (voptarg->flag & INTEGER)
   1508 	    typeset("OPTARG", 0, INTEGER, 0, 0);
   1509 	if (user_opt.optarg == NULL)
   1510 		unset(voptarg, 1);
   1511 	else
   1512 		/* This can't fail (have cleared readonly/integer) */
   1513 		setstr(voptarg, user_opt.optarg, KSH_RETURN_ERROR);
   1514 
   1515 	rv = 0;
   1516 
   1517 	vq = global(var);
   1518 	/* Error message already printed (integer, readonly) */
   1519 	if (!setstr(vq, buf, KSH_RETURN_ERROR))
   1520 		rv = 2;
   1521 	if (Flag(FEXPORT))
   1522 		typeset(var, EXPORT, 0, 0, 0);
   1523 
   1524 	return (optc < 0 ? 1 : rv);
   1525 }
   1526 
   1527 #ifndef MKSH_NO_CMDLINE_EDITING
   1528 int
   1529 c_bind(const char **wp)
   1530 {
   1531 	int optc, rv = 0;
   1532 #ifndef MKSH_SMALL
   1533 	bool macro = false;
   1534 #endif
   1535 	bool list = false;
   1536 	const char *cp;
   1537 	char *up;
   1538 
   1539 	while ((optc = ksh_getopt(wp, &builtin_opt,
   1540 #ifndef MKSH_SMALL
   1541 	    "lm"
   1542 #else
   1543 	    "l"
   1544 #endif
   1545 	    )) != -1)
   1546 		switch (optc) {
   1547 		case 'l':
   1548 			list = true;
   1549 			break;
   1550 #ifndef MKSH_SMALL
   1551 		case 'm':
   1552 			macro = true;
   1553 			break;
   1554 #endif
   1555 		case '?':
   1556 			return (1);
   1557 		}
   1558 	wp += builtin_opt.optind;
   1559 
   1560 	if (*wp == NULL)
   1561 		/* list all */
   1562 		rv = x_bind(NULL, NULL,
   1563 #ifndef MKSH_SMALL
   1564 		    false,
   1565 #endif
   1566 		    list);
   1567 
   1568 	for (; *wp != NULL; wp++) {
   1569 		if ((cp = cstrchr(*wp, '=')) == NULL)
   1570 			up = NULL;
   1571 		else {
   1572 			strdupx(up, *wp, ATEMP);
   1573 			up[cp++ - *wp] = '\0';
   1574 		}
   1575 		if (x_bind(up ? up : *wp, cp,
   1576 #ifndef MKSH_SMALL
   1577 		    macro,
   1578 #endif
   1579 		    false))
   1580 			rv = 1;
   1581 		afree(up, ATEMP);
   1582 	}
   1583 
   1584 	return (rv);
   1585 }
   1586 #endif
   1587 
   1588 int
   1589 c_shift(const char **wp)
   1590 {
   1591 	struct block *l = e->loc;
   1592 	int n;
   1593 	mksh_ari_t val;
   1594 	const char *arg;
   1595 
   1596 	if (ksh_getopt(wp, &builtin_opt, null) == '?')
   1597 		return (1);
   1598 	arg = wp[builtin_opt.optind];
   1599 
   1600 	if (!arg)
   1601 		n = 1;
   1602 	else if (!evaluate(arg, &val, KSH_RETURN_ERROR, false)) {
   1603 		/* error already printed */
   1604 		bi_errorfz();
   1605 		return (1);
   1606 	} else if (!(n = val)) {
   1607 		/* nothing to do */
   1608 		return (0);
   1609 	} else if (n < 0) {
   1610 		bi_errorf("%s: %s", arg, "bad number");
   1611 		return (1);
   1612 	}
   1613 	if (l->argc < n) {
   1614 		bi_errorf("nothing to shift");
   1615 		return (1);
   1616 	}
   1617 	l->argv[n] = l->argv[0];
   1618 	l->argv += n;
   1619 	l->argc -= n;
   1620 	return (0);
   1621 }
   1622 
   1623 int
   1624 c_umask(const char **wp)
   1625 {
   1626 	int i, optc;
   1627 	const char *cp;
   1628 	bool symbolic = false;
   1629 	mode_t old_umask;
   1630 
   1631 	while ((optc = ksh_getopt(wp, &builtin_opt, "S")) != -1)
   1632 		switch (optc) {
   1633 		case 'S':
   1634 			symbolic = true;
   1635 			break;
   1636 		case '?':
   1637 			return (1);
   1638 		}
   1639 	cp = wp[builtin_opt.optind];
   1640 	if (cp == NULL) {
   1641 		old_umask = umask((mode_t)0);
   1642 		umask(old_umask);
   1643 		if (symbolic) {
   1644 			char buf[18], *p;
   1645 			int j;
   1646 
   1647 			old_umask = ~old_umask;
   1648 			p = buf;
   1649 			for (i = 0; i < 3; i++) {
   1650 				*p++ = "ugo"[i];
   1651 				*p++ = '=';
   1652 				for (j = 0; j < 3; j++)
   1653 					if (old_umask & (1 << (8 - (3*i + j))))
   1654 						*p++ = "rwx"[j];
   1655 				*p++ = ',';
   1656 			}
   1657 			p[-1] = '\0';
   1658 			shprintf("%s\n", buf);
   1659 		} else
   1660 			shprintf("%#3.3o\n", (unsigned int)old_umask);
   1661 	} else {
   1662 		mode_t new_umask;
   1663 
   1664 		if (ksh_isdigit(*cp)) {
   1665 			for (new_umask = 0; *cp >= '0' && *cp <= '7'; cp++)
   1666 				new_umask = new_umask * 8 + (*cp - '0');
   1667 			if (*cp) {
   1668 				bi_errorf("bad number");
   1669 				return (1);
   1670 			}
   1671 		} else {
   1672 			/* symbolic format */
   1673 			int positions, new_val;
   1674 			char op;
   1675 
   1676 			old_umask = umask((mode_t)0);
   1677 			/* in case of error */
   1678 			umask(old_umask);
   1679 			old_umask = ~old_umask;
   1680 			new_umask = old_umask;
   1681 			positions = 0;
   1682 			while (*cp) {
   1683 				while (*cp && vstrchr("augo", *cp))
   1684 					switch (*cp++) {
   1685 					case 'a':
   1686 						positions |= 0111;
   1687 						break;
   1688 					case 'u':
   1689 						positions |= 0100;
   1690 						break;
   1691 					case 'g':
   1692 						positions |= 0010;
   1693 						break;
   1694 					case 'o':
   1695 						positions |= 0001;
   1696 						break;
   1697 					}
   1698 				if (!positions)
   1699 					/* default is a */
   1700 					positions = 0111;
   1701 				if (!vstrchr("=+-", op = *cp))
   1702 					break;
   1703 				cp++;
   1704 				new_val = 0;
   1705 				while (*cp && vstrchr("rwxugoXs", *cp))
   1706 					switch (*cp++) {
   1707 					case 'r': new_val |= 04; break;
   1708 					case 'w': new_val |= 02; break;
   1709 					case 'x': new_val |= 01; break;
   1710 					case 'u':
   1711 						new_val |= old_umask >> 6;
   1712 						break;
   1713 					case 'g':
   1714 						new_val |= old_umask >> 3;
   1715 						break;
   1716 					case 'o':
   1717 						new_val |= old_umask >> 0;
   1718 						break;
   1719 					case 'X':
   1720 						if (old_umask & 0111)
   1721 							new_val |= 01;
   1722 						break;
   1723 					case 's':
   1724 						/* ignored */
   1725 						break;
   1726 					}
   1727 				new_val = (new_val & 07) * positions;
   1728 				switch (op) {
   1729 				case '-':
   1730 					new_umask &= ~new_val;
   1731 					break;
   1732 				case '=':
   1733 					new_umask = new_val |
   1734 					    (new_umask & ~(positions * 07));
   1735 					break;
   1736 				case '+':
   1737 					new_umask |= new_val;
   1738 				}
   1739 				if (*cp == ',') {
   1740 					positions = 0;
   1741 					cp++;
   1742 				} else if (!vstrchr("=+-", *cp))
   1743 					break;
   1744 			}
   1745 			if (*cp) {
   1746 				bi_errorf("bad mask");
   1747 				return (1);
   1748 			}
   1749 			new_umask = ~new_umask;
   1750 		}
   1751 		umask(new_umask);
   1752 	}
   1753 	return (0);
   1754 }
   1755 
   1756 int
   1757 c_dot(const char **wp)
   1758 {
   1759 	const char *file, *cp, **argv;
   1760 	int argc, i, errcode;
   1761 
   1762 	if (ksh_getopt(wp, &builtin_opt, null) == '?')
   1763 		return (1);
   1764 
   1765 	if ((cp = wp[builtin_opt.optind]) == NULL) {
   1766 		bi_errorf("missing argument");
   1767 		return (1);
   1768 	}
   1769 	if ((file = search_path(cp, path, R_OK, &errcode)) == NULL) {
   1770 		bi_errorf("%s: %s", cp, cstrerror(errcode));
   1771 		return (1);
   1772 	}
   1773 
   1774 	/* Set positional parameters? */
   1775 	if (wp[builtin_opt.optind + 1]) {
   1776 		argv = wp + builtin_opt.optind;
   1777 		/* preserve $0 */
   1778 		argv[0] = e->loc->argv[0];
   1779 		for (argc = 0; argv[argc + 1]; argc++)
   1780 			;
   1781 	} else {
   1782 		argc = 0;
   1783 		argv = NULL;
   1784 	}
   1785 	if ((i = include(file, argc, argv, false)) < 0) {
   1786 		/* should not happen */
   1787 		bi_errorf("%s: %s", cp, cstrerror(errno));
   1788 		return (1);
   1789 	}
   1790 	return (i);
   1791 }
   1792 
   1793 int
   1794 c_wait(const char **wp)
   1795 {
   1796 	int rv = 0, sig;
   1797 
   1798 	if (ksh_getopt(wp, &builtin_opt, null) == '?')
   1799 		return (1);
   1800 	wp += builtin_opt.optind;
   1801 	if (*wp == NULL) {
   1802 		while (waitfor(NULL, &sig) >= 0)
   1803 			;
   1804 		rv = sig;
   1805 	} else {
   1806 		for (; *wp; wp++)
   1807 			rv = waitfor(*wp, &sig);
   1808 		if (rv < 0)
   1809 			/* magic exit code: bad job-id */
   1810 			rv = sig ? sig : 127;
   1811 	}
   1812 	return (rv);
   1813 }
   1814 
   1815 static char REPLY[] = "REPLY";
   1816 int
   1817 c_read(const char **wp)
   1818 {
   1819 #define is_ifsws(c) (ctype((c), C_IFS) && ctype((c), C_IFSWS))
   1820 	int c, fd = 0, rv = 0;
   1821 	bool savehist = false, intoarray = false, aschars = false;
   1822 	bool rawmode = false, expanding = false;
   1823 	bool lastparmmode = false, lastparmused = false;
   1824 	enum { LINES, BYTES, UPTO, READALL } readmode = LINES;
   1825 	char delim = '\n';
   1826 	size_t bytesleft = 128, bytesread;
   1827 	struct tbl *vp /* FU gcc */ = NULL, *vq;
   1828 	char *cp, *allocd = NULL, *xp;
   1829 	const char *ccp;
   1830 	XString xs;
   1831 	size_t xsave = 0;
   1832 	mksh_ttyst tios;
   1833 	bool restore_tios = false;
   1834 #if HAVE_SELECT
   1835 	bool hastimeout = false;
   1836 	struct timeval tv, tvlim;
   1837 #define c_read_opts "Aad:N:n:prst:u,"
   1838 #else
   1839 #define c_read_opts "Aad:N:n:prsu,"
   1840 #endif
   1841 
   1842 	while ((c = ksh_getopt(wp, &builtin_opt, c_read_opts)) != -1)
   1843 	switch (c) {
   1844 	case 'a':
   1845 		aschars = true;
   1846 		/* FALLTHROUGH */
   1847 	case 'A':
   1848 		intoarray = true;
   1849 		break;
   1850 	case 'd':
   1851 		delim = builtin_opt.optarg[0];
   1852 		break;
   1853 	case 'N':
   1854 	case 'n':
   1855 		readmode = c == 'N' ? BYTES : UPTO;
   1856 		if (!bi_getn(builtin_opt.optarg, &c))
   1857 			return (2);
   1858 		if (c == -1) {
   1859 			readmode = readmode == BYTES ? READALL : UPTO;
   1860 			bytesleft = 1024;
   1861 		} else
   1862 			bytesleft = (unsigned int)c;
   1863 		break;
   1864 	case 'p':
   1865 		if ((fd = coproc_getfd(R_OK, &ccp)) < 0) {
   1866 			bi_errorf("%s: %s", "-p", ccp);
   1867 			return (2);
   1868 		}
   1869 		break;
   1870 	case 'r':
   1871 		rawmode = true;
   1872 		break;
   1873 	case 's':
   1874 		savehist = true;
   1875 		break;
   1876 #if HAVE_SELECT
   1877 	case 't':
   1878 		if (parse_usec(builtin_opt.optarg, &tv)) {
   1879 			bi_errorf("%s: %s '%s'", Tsynerr, cstrerror(errno),
   1880 			    builtin_opt.optarg);
   1881 			return (2);
   1882 		}
   1883 		hastimeout = true;
   1884 		break;
   1885 #endif
   1886 	case 'u':
   1887 		if (!builtin_opt.optarg[0])
   1888 			fd = 0;
   1889 		else if ((fd = check_fd(builtin_opt.optarg, R_OK, &ccp)) < 0) {
   1890 			bi_errorf("%s: %s: %s", "-u", builtin_opt.optarg, ccp);
   1891 			return (2);
   1892 		}
   1893 		break;
   1894 	case '?':
   1895 		return (2);
   1896 	}
   1897 	wp += builtin_opt.optind;
   1898 	if (*wp == NULL)
   1899 		*--wp = REPLY;
   1900 
   1901 	if (intoarray && wp[1] != NULL) {
   1902 		bi_errorf("too many arguments");
   1903 		return (2);
   1904 	}
   1905 
   1906 	if ((ccp = cstrchr(*wp, '?')) != NULL) {
   1907 		strdupx(allocd, *wp, ATEMP);
   1908 		allocd[ccp - *wp] = '\0';
   1909 		*wp = allocd;
   1910 		if (isatty(fd)) {
   1911 			/*
   1912 			 * AT&T ksh says it prints prompt on fd if it's open
   1913 			 * for writing and is a tty, but it doesn't do it
   1914 			 * (it also doesn't check the interactive flag,
   1915 			 * as is indicated in the Korn Shell book).
   1916 			 */
   1917 			shf_puts(ccp + 1, shl_out);
   1918 			shf_flush(shl_out);
   1919 		}
   1920 	}
   1921 
   1922 	Xinit(xs, xp, bytesleft, ATEMP);
   1923 
   1924 	if (readmode == LINES)
   1925 		bytesleft = 1;
   1926 	else if (isatty(fd)) {
   1927 		x_mkraw(fd, &tios, true);
   1928 		restore_tios = true;
   1929 	}
   1930 
   1931 #if HAVE_SELECT
   1932 	if (hastimeout) {
   1933 		mksh_TIME(tvlim);
   1934 		timeradd(&tvlim, &tv, &tvlim);
   1935 	}
   1936 #endif
   1937 
   1938  c_read_readloop:
   1939 #if HAVE_SELECT
   1940 	if (hastimeout) {
   1941 		fd_set fdset;
   1942 
   1943 		FD_ZERO(&fdset);
   1944 		FD_SET((unsigned int)fd, &fdset);
   1945 		mksh_TIME(tv);
   1946 		timersub(&tvlim, &tv, &tv);
   1947 		if (tv.tv_sec < 0) {
   1948 			/* timeout expired globally */
   1949 			rv = 1;
   1950 			goto c_read_out;
   1951 		}
   1952 
   1953 		switch (select(fd + 1, &fdset, NULL, NULL, &tv)) {
   1954 		case 1:
   1955 			break;
   1956 		case 0:
   1957 			/* timeout expired for this call */
   1958 			rv = 1;
   1959 			goto c_read_out;
   1960 		default:
   1961 			bi_errorf("%s: %s", Tselect, cstrerror(errno));
   1962 			rv = 2;
   1963 			goto c_read_out;
   1964 		}
   1965 	}
   1966 #endif
   1967 
   1968 	bytesread = blocking_read(fd, xp, bytesleft);
   1969 	if (bytesread == (size_t)-1) {
   1970 		/* interrupted */
   1971 		if (errno == EINTR && fatal_trap_check()) {
   1972 			/*
   1973 			 * Was the offending signal one that would
   1974 			 * normally kill a process? If so, pretend
   1975 			 * the read was killed.
   1976 			 */
   1977 			rv = 2;
   1978 			goto c_read_out;
   1979 		}
   1980 		/* just ignore the signal */
   1981 		goto c_read_readloop;
   1982 	}
   1983 
   1984 	switch (readmode) {
   1985 	case READALL:
   1986 		if (bytesread == 0) {
   1987 			/* end of file reached */
   1988 			rv = 1;
   1989 			goto c_read_readdone;
   1990 		}
   1991 		xp += bytesread;
   1992 		XcheckN(xs, xp, bytesleft);
   1993 		break;
   1994 
   1995 	case UPTO:
   1996 		if (bytesread == 0)
   1997 			/* end of file reached */
   1998 			rv = 1;
   1999 		xp += bytesread;
   2000 		goto c_read_readdone;
   2001 
   2002 	case BYTES:
   2003 		if (bytesread == 0) {
   2004 			/* end of file reached */
   2005 			rv = 1;
   2006 			xp = Xstring(xs, xp);
   2007 			goto c_read_readdone;
   2008 		}
   2009 		xp += bytesread;
   2010 		if ((bytesleft -= bytesread) == 0)
   2011 			goto c_read_readdone;
   2012 		break;
   2013 	case LINES:
   2014 		if (bytesread == 0) {
   2015 			/* end of file reached */
   2016 			rv = 1;
   2017 			goto c_read_readdone;
   2018 		}
   2019 		if ((c = *xp) == '\0' && !aschars && delim != '\0') {
   2020 			/* skip any read NULs unless delimiter */
   2021 			break;
   2022 		}
   2023 		if (expanding) {
   2024 			expanding = false;
   2025 			if (c == delim) {
   2026 				if (Flag(FTALKING_I) && isatty(fd)) {
   2027 					/*
   2028 					 * set prompt in case this is
   2029 					 * called from .profile or $ENV
   2030 					 */
   2031 					set_prompt(PS2, NULL);
   2032 					pprompt(prompt, 0);
   2033 				}
   2034 				/* drop the backslash */
   2035 				--xp;
   2036 				/* and the delimiter */
   2037 				break;
   2038 			}
   2039 		} else if (c == delim) {
   2040 			goto c_read_readdone;
   2041 		} else if (!rawmode && c == '\\') {
   2042 			expanding = true;
   2043 		}
   2044 		Xcheck(xs, xp);
   2045 		++xp;
   2046 		break;
   2047 	}
   2048 	goto c_read_readloop;
   2049 
   2050  c_read_readdone:
   2051 	bytesread = Xlength(xs, xp);
   2052 	Xput(xs, xp, '\0');
   2053 
   2054 	/*-
   2055 	 * state: we finished reading the input and NUL terminated it
   2056 	 * Xstring(xs, xp) -> xp-1 = input string without trailing delim
   2057 	 * rv = 1 if EOF, 0 otherwise (errors handled already)
   2058 	 */
   2059 
   2060 	if (rv == 1) {
   2061 		/* clean up coprocess if needed, on EOF */
   2062 		coproc_read_close(fd);
   2063 		if (readmode == READALL)
   2064 			/* EOF is no error here */
   2065 			rv = 0;
   2066 	}
   2067 
   2068 	if (savehist)
   2069 		histsave(&source->line, Xstring(xs, xp), true, false);
   2070 
   2071 	ccp = cp = Xclose(xs, xp);
   2072 	expanding = false;
   2073 	XinitN(xs, 128, ATEMP);
   2074 	if (intoarray) {
   2075 		vp = global(*wp);
   2076 		if (vp->flag & RDONLY) {
   2077  c_read_splitro:
   2078 			bi_errorf("read-only: %s", *wp);
   2079  c_read_spliterr:
   2080 			rv = 2;
   2081 			afree(cp, ATEMP);
   2082 			goto c_read_out;
   2083 		}
   2084 		/* exporting an array is currently pointless */
   2085 		unset(vp, 1);
   2086 		/* counter for array index */
   2087 		c = 0;
   2088 	}
   2089 	if (!aschars) {
   2090 		/* skip initial IFS whitespace */
   2091 		while (bytesread && is_ifsws(*ccp)) {
   2092 			++ccp;
   2093 			--bytesread;
   2094 		}
   2095 		/* trim trailing IFS whitespace */
   2096 		while (bytesread && is_ifsws(ccp[bytesread - 1])) {
   2097 			--bytesread;
   2098 		}
   2099 	}
   2100  c_read_splitloop:
   2101 	xp = Xstring(xs, xp);
   2102 	/* generate next word */
   2103 	if (!bytesread) {
   2104 		/* no more input */
   2105 		if (intoarray)
   2106 			goto c_read_splitdone;
   2107 		/* zero out next parameters */
   2108 		goto c_read_gotword;
   2109 	}
   2110 	if (aschars) {
   2111 		Xput(xs, xp, '1');
   2112 		Xput(xs, xp, '#');
   2113 		bytesleft = utf_ptradj(ccp);
   2114 		while (bytesleft && bytesread) {
   2115 			*xp++ = *ccp++;
   2116 			--bytesleft;
   2117 			--bytesread;
   2118 		}
   2119 		if (xp[-1] == '\0') {
   2120 			xp[-1] = '0';
   2121 			xp[-3] = '2';
   2122 		}
   2123 		goto c_read_gotword;
   2124 	}
   2125 
   2126 	if (!intoarray && wp[1] == NULL)
   2127 		lastparmmode = true;
   2128 
   2129  c_read_splitlast:
   2130 	/* copy until IFS character */
   2131 	while (bytesread) {
   2132 		char ch;
   2133 
   2134 		ch = *ccp;
   2135 		if (expanding) {
   2136 			expanding = false;
   2137 			goto c_read_splitcopy;
   2138 		} else if (ctype(ch, C_IFS)) {
   2139 			break;
   2140 		} else if (!rawmode && ch == '\\') {
   2141 			expanding = true;
   2142 		} else {
   2143  c_read_splitcopy:
   2144 			Xcheck(xs, xp);
   2145 			Xput(xs, xp, ch);
   2146 		}
   2147 		++ccp;
   2148 		--bytesread;
   2149 	}
   2150 	xsave = Xsavepos(xs, xp);
   2151 	/* copy word delimiter: IFSWS+IFS,IFSWS */
   2152 	expanding = false;
   2153 	while (bytesread) {
   2154 		char ch;
   2155 
   2156 		ch = *ccp;
   2157 		if (!ctype(ch, C_IFS))
   2158 			break;
   2159 		if (lastparmmode && !expanding && !rawmode && ch == '\\') {
   2160 			expanding = true;
   2161 		} else {
   2162 			Xcheck(xs, xp);
   2163 			Xput(xs, xp, ch);
   2164 		}
   2165 		++ccp;
   2166 		--bytesread;
   2167 		if (expanding)
   2168 			continue;
   2169 		if (!ctype(ch, C_IFSWS))
   2170 			break;
   2171 	}
   2172 	while (bytesread && is_ifsws(*ccp)) {
   2173 		Xcheck(xs, xp);
   2174 		Xput(xs, xp, *ccp);
   2175 		++ccp;
   2176 		--bytesread;
   2177 	}
   2178 	/* if no more parameters, rinse and repeat */
   2179 	if (lastparmmode && bytesread) {
   2180 		lastparmused = true;
   2181 		goto c_read_splitlast;
   2182 	}
   2183 	/* get rid of the delimiter unless we pack the rest */
   2184 	if (!lastparmused)
   2185 		xp = Xrestpos(xs, xp, xsave);
   2186  c_read_gotword:
   2187 	Xput(xs, xp, '\0');
   2188 	if (intoarray) {
   2189 		vq = arraysearch(vp, c++);
   2190 	} else {
   2191 		vq = global(*wp);
   2192 		/* must be checked before exporting */
   2193 		if (vq->flag & RDONLY)
   2194 			goto c_read_splitro;
   2195 		if (Flag(FEXPORT))
   2196 			typeset(*wp, EXPORT, 0, 0, 0);
   2197 	}
   2198 	if (!setstr(vq, Xstring(xs, xp), KSH_RETURN_ERROR))
   2199 		goto c_read_spliterr;
   2200 	if (aschars) {
   2201 		setint_v(vq, vq, false);
   2202 		/* protect from UTFMODE changes */
   2203 		vq->type = 0;
   2204 	}
   2205 	if (intoarray || *++wp != NULL)
   2206 		goto c_read_splitloop;
   2207 
   2208  c_read_splitdone:
   2209 	/* free up */
   2210 	afree(cp, ATEMP);
   2211 
   2212  c_read_out:
   2213 	afree(allocd, ATEMP);
   2214 	Xfree(xs, xp);
   2215 	if (restore_tios)
   2216 		mksh_tcset(fd, &tios);
   2217 	return (rv);
   2218 #undef is_ifsws
   2219 }
   2220 
   2221 int
   2222 c_eval(const char **wp)
   2223 {
   2224 	struct source *s, *saves = source;
   2225 	unsigned char savef;
   2226 	int rv;
   2227 
   2228 	if (ksh_getopt(wp, &builtin_opt, null) == '?')
   2229 		return (1);
   2230 	s = pushs(SWORDS, ATEMP);
   2231 	s->u.strv = wp + builtin_opt.optind;
   2232 
   2233 	/*-
   2234 	 * The following code handles the case where the command is
   2235 	 * empty due to failed command substitution, for example by
   2236 	 *	eval "$(false)"
   2237 	 * This has historically returned 1 by AT&T ksh88. In this
   2238 	 * case, shell() will not set or change exstat because the
   2239 	 * compiled tree is empty, so it will use the value we pass
   2240 	 * from subst_exstat, which is cleared in execute(), so it
   2241 	 * should have been 0 if there were no substitutions.
   2242 	 *
   2243 	 * POSIX however says we don't do this, even though it is
   2244 	 * traditionally done. AT&T ksh93 agrees with POSIX, so we
   2245 	 * do. The following is an excerpt from SUSv4 [1003.2-2008]:
   2246 	 *
   2247 	 * 2.9.1: Simple Commands
   2248 	 *	... If there is a command name, execution shall
   2249 	 *	continue as described in 2.9.1.1 [Command Search
   2250 	 *	and Execution]. If there is no command name, but
   2251 	 *	the command contained a command substitution, the
   2252 	 *	command shall complete with the exit status of the
   2253 	 *	last command substitution performed.
   2254 	 * 2.9.1.1: Command Search and Execution
   2255 	 *	(1) a. If the command name matches the name of a
   2256 	 *	special built-in utility, that special built-in
   2257 	 *	utility shall be invoked.
   2258 	 * 2.14.5: eval
   2259 	 *	If there are no arguments, or only null arguments,
   2260 	 *	eval shall return a zero exit status; ...
   2261 	 */
   2262 	/* AT&T ksh88: use subst_exstat */
   2263 	/* exstat = subst_exstat; */
   2264 	/* SUSv4: OR with a high value never written otherwise */
   2265 	exstat |= 0x4000;
   2266 
   2267 	savef = Flag(FERREXIT);
   2268 	Flag(FERREXIT) |= 0x80;
   2269 	rv = shell(s, false);
   2270 	Flag(FERREXIT) = savef;
   2271 	source = saves;
   2272 	afree(s, ATEMP);
   2273 	if (exstat & 0x4000)
   2274 		/* detect old exstat, use 0 in that case */
   2275 		rv = 0;
   2276 	return (rv);
   2277 }
   2278 
   2279 int
   2280 c_trap(const char **wp)
   2281 {
   2282 	int i;
   2283 	const char *s;
   2284 	Trap *p;
   2285 
   2286 	if (ksh_getopt(wp, &builtin_opt, null) == '?')
   2287 		return (1);
   2288 	wp += builtin_opt.optind;
   2289 
   2290 	if (*wp == NULL) {
   2291 		for (p = sigtraps, i = NSIG + 1; --i >= 0; p++)
   2292 			if (p->trap != NULL) {
   2293 				shf_puts("trap -- ", shl_stdout);
   2294 				print_value_quoted(shl_stdout, p->trap);
   2295 				shprintf(" %s\n", p->name);
   2296 			}
   2297 		return (0);
   2298 	}
   2299 
   2300 	/*
   2301 	 * Use case sensitive lookup for first arg so the
   2302 	 * command 'exit' isn't confused with the pseudo-signal
   2303 	 * 'EXIT'.
   2304 	 */
   2305 	/* get command */
   2306 	s = (gettrap(*wp, false) == NULL) ? *wp++ : NULL;
   2307 	if (s != NULL && s[0] == '-' && s[1] == '\0')
   2308 		s = NULL;
   2309 
   2310 	/* set/clear traps */
   2311 	i = 0;
   2312 	while (*wp != NULL)
   2313 		if ((p = gettrap(*wp++, true)) == NULL) {
   2314 			warningf(true, "%s: %s '%s'", builtin_argv0,
   2315 			    "bad signal", wp[-1]);
   2316 			++i;
   2317 		} else
   2318 			settrap(p, s);
   2319 	return (i);
   2320 }
   2321 
   2322 int
   2323 c_exitreturn(const char **wp)
   2324 {
   2325 	int n, how = LEXIT;
   2326 	const char *arg;
   2327 
   2328 	if (ksh_getopt(wp, &builtin_opt, null) == '?')
   2329 		goto c_exitreturn_err;
   2330 	arg = wp[builtin_opt.optind];
   2331 
   2332 	if (arg)
   2333 		exstat = bi_getn(arg, &n) ? (n & 0xFF) : 1;
   2334 	else if (trap_exstat != -1)
   2335 		exstat = trap_exstat;
   2336 	if (wp[0][0] == 'r') {
   2337 		/* return */
   2338 		struct env *ep;
   2339 
   2340 		/*
   2341 		 * need to tell if this is exit or return so trap exit will
   2342 		 * work right (POSIX)
   2343 		 */
   2344 		for (ep = e; ep; ep = ep->oenv)
   2345 			if (STOP_RETURN(ep->type)) {
   2346 				how = LRETURN;
   2347 				break;
   2348 			}
   2349 	}
   2350 
   2351 	if (how == LEXIT && !really_exit && j_stopped_running()) {
   2352 		really_exit = true;
   2353 		how = LSHELL;
   2354 	}
   2355 
   2356 	/* get rid of any i/o redirections */
   2357 	quitenv(NULL);
   2358 	unwind(how);
   2359 	/* NOTREACHED */
   2360 
   2361  c_exitreturn_err:
   2362 	return (1);
   2363 }
   2364 
   2365 int
   2366 c_brkcont(const char **wp)
   2367 {
   2368 	unsigned int quit;
   2369 	int n;
   2370 	struct env *ep, *last_ep = NULL;
   2371 	const char *arg;
   2372 
   2373 	if (ksh_getopt(wp, &builtin_opt, null) == '?')
   2374 		goto c_brkcont_err;
   2375 	arg = wp[builtin_opt.optind];
   2376 
   2377 	if (!arg)
   2378 		n = 1;
   2379 	else if (!bi_getn(arg, &n))
   2380 		goto c_brkcont_err;
   2381 	if (n <= 0) {
   2382 		/* AT&T ksh does this for non-interactive shells only - weird */
   2383 		bi_errorf("%s: %s", arg, "bad value");
   2384 		goto c_brkcont_err;
   2385 	}
   2386 	quit = (unsigned int)n;
   2387 
   2388 	/* Stop at E_NONE, E_PARSE, E_FUNC, or E_INCL */
   2389 	for (ep = e; ep && !STOP_BRKCONT(ep->type); ep = ep->oenv)
   2390 		if (ep->type == E_LOOP) {
   2391 			if (--quit == 0)
   2392 				break;
   2393 			ep->flags |= EF_BRKCONT_PASS;
   2394 			last_ep = ep;
   2395 		}
   2396 
   2397 	if (quit) {
   2398 		/*
   2399 		 * AT&T ksh doesn't print a message - just does what it
   2400 		 * can. We print a message 'cause it helps in debugging
   2401 		 * scripts, but don't generate an error (ie, keep going).
   2402 		 */
   2403 		if ((unsigned int)n == quit) {
   2404 			warningf(true, "%s: %s %s", wp[0], "can't", wp[0]);
   2405 			return (0);
   2406 		}
   2407 		/*
   2408 		 * POSIX says if n is too big, the last enclosing loop
   2409 		 * shall be used. Doesn't say to print an error but we
   2410 		 * do anyway 'cause the user messed up.
   2411 		 */
   2412 		if (last_ep)
   2413 			last_ep->flags &= ~EF_BRKCONT_PASS;
   2414 		warningf(true, "%s: can only %s %u level(s)",
   2415 		    wp[0], wp[0], (unsigned int)n - quit);
   2416 	}
   2417 
   2418 	unwind(*wp[0] == 'b' ? LBREAK : LCONTIN);
   2419 	/* NOTREACHED */
   2420 
   2421  c_brkcont_err:
   2422 	return (1);
   2423 }
   2424 
   2425 int
   2426 c_set(const char **wp)
   2427 {
   2428 	int argi;
   2429 	bool setargs;
   2430 	struct block *l = e->loc;
   2431 	const char **owp;
   2432 
   2433 	if (wp[1] == NULL) {
   2434 		static const char *args[] = { Tset, "-", NULL };
   2435 		return (c_typeset(args));
   2436 	}
   2437 
   2438 	if ((argi = parse_args(wp, OF_SET, &setargs)) < 0)
   2439 		return (2);
   2440 	/* set $# and $* */
   2441 	if (setargs) {
   2442 		wp += argi - 1;
   2443 		owp = wp;
   2444 		/* save $0 */
   2445 		wp[0] = l->argv[0];
   2446 		while (*++wp != NULL)
   2447 			strdupx(*wp, *wp, &l->area);
   2448 		l->argc = wp - owp - 1;
   2449 		l->argv = alloc2(l->argc + 2, sizeof(char *), &l->area);
   2450 		for (wp = l->argv; (*wp++ = *owp++) != NULL; )
   2451 			;
   2452 	}
   2453 	/*-
   2454 	 * POSIX says set exit status is 0, but old scripts that use
   2455 	 * getopt(1) use the construct
   2456 	 *	set -- $(getopt ab:c "$@")
   2457 	 * which assumes the exit value set will be that of the $()
   2458 	 * (subst_exstat is cleared in execute() so that it will be 0
   2459 	 * if there are no command substitutions).
   2460 	 */
   2461 #ifdef MKSH_LEGACY_MODE
   2462 	/* traditional behaviour, unless set -o posix */
   2463 	return (Flag(FPOSIX) ? 0 : subst_exstat);
   2464 #else
   2465 	/* conformant behaviour, unless set -o sh +o posix */
   2466 	return (Flag(FSH) && !Flag(FPOSIX) ? subst_exstat : 0);
   2467 #endif
   2468 }
   2469 
   2470 int
   2471 c_unset(const char **wp)
   2472 {
   2473 	const char *id;
   2474 	int optc, rv = 0;
   2475 	bool unset_var = true;
   2476 
   2477 	while ((optc = ksh_getopt(wp, &builtin_opt, "fv")) != -1)
   2478 		switch (optc) {
   2479 		case 'f':
   2480 			unset_var = false;
   2481 			break;
   2482 		case 'v':
   2483 			unset_var = true;
   2484 			break;
   2485 		case '?':
   2486 			/*XXX not reached due to GF_ERROR */
   2487 			return (2);
   2488 		}
   2489 	wp += builtin_opt.optind;
   2490 	for (; (id = *wp) != NULL; wp++)
   2491 		if (unset_var) {
   2492 			/* unset variable */
   2493 			struct tbl *vp;
   2494 			char *cp = NULL;
   2495 			size_t n;
   2496 
   2497 			n = strlen(id);
   2498 			if (n > 3 && id[n-3] == '[' && id[n-2] == '*' &&
   2499 			    id[n-1] == ']') {
   2500 				strndupx(cp, id, n - 3, ATEMP);
   2501 				id = cp;
   2502 				optc = 3;
   2503 			} else
   2504 				optc = vstrchr(id, '[') ? 0 : 1;
   2505 
   2506 			vp = global(id);
   2507 			afree(cp, ATEMP);
   2508 
   2509 			if ((vp->flag&RDONLY)) {
   2510 				warningf(true, "read-only: %s", vp->name);
   2511 				rv = 1;
   2512 			} else
   2513 				unset(vp, optc);
   2514 		} else
   2515 			/* unset function */
   2516 			define(id, NULL);
   2517 	return (rv);
   2518 }
   2519 
   2520 static void
   2521 p_time(struct shf *shf, bool posix, long tv_sec, int tv_usec, int width,
   2522     const char *prefix, const char *suffix)
   2523 {
   2524 	tv_usec /= 10000;
   2525 	if (posix)
   2526 		shf_fprintf(shf, "%s%*ld.%02d%s", prefix, width,
   2527 		    tv_sec, tv_usec, suffix);
   2528 	else
   2529 		shf_fprintf(shf, "%s%*ldm%d.%02ds%s", prefix, width,
   2530 		    tv_sec / 60, (int)(tv_sec % 60), tv_usec, suffix);
   2531 }
   2532 
   2533 int
   2534 c_times(const char **wp MKSH_A_UNUSED)
   2535 {
   2536 	struct rusage usage;
   2537 
   2538 	getrusage(RUSAGE_SELF, &usage);
   2539 	p_time(shl_stdout, false, usage.ru_utime.tv_sec,
   2540 	    usage.ru_utime.tv_usec, 0, null, " ");
   2541 	p_time(shl_stdout, false, usage.ru_stime.tv_sec,
   2542 	    usage.ru_stime.tv_usec, 0, null, "\n");
   2543 
   2544 	getrusage(RUSAGE_CHILDREN, &usage);
   2545 	p_time(shl_stdout, false, usage.ru_utime.tv_sec,
   2546 	    usage.ru_utime.tv_usec, 0, null, " ");
   2547 	p_time(shl_stdout, false, usage.ru_stime.tv_sec,
   2548 	    usage.ru_stime.tv_usec, 0, null, "\n");
   2549 
   2550 	return (0);
   2551 }
   2552 
   2553 /*
   2554  * time pipeline (really a statement, not a built-in command)
   2555  */
   2556 int
   2557 timex(struct op *t, int f, volatile int *xerrok)
   2558 {
   2559 #define TF_NOARGS	BIT(0)
   2560 #define TF_NOREAL	BIT(1)		/* don't report real time */
   2561 #define TF_POSIX	BIT(2)		/* report in POSIX format */
   2562 	int rv = 0, tf = 0;
   2563 	struct rusage ru0, ru1, cru0, cru1;
   2564 	struct timeval usrtime, systime, tv0, tv1;
   2565 
   2566 	mksh_TIME(tv0);
   2567 	getrusage(RUSAGE_SELF, &ru0);
   2568 	getrusage(RUSAGE_CHILDREN, &cru0);
   2569 	if (t->left) {
   2570 		/*
   2571 		 * Two ways of getting cpu usage of a command: just use t0
   2572 		 * and t1 (which will get cpu usage from other jobs that
   2573 		 * finish while we are executing t->left), or get the
   2574 		 * cpu usage of t->left. AT&T ksh does the former, while
   2575 		 * pdksh tries to do the later (the j_usrtime hack doesn't
   2576 		 * really work as it only counts the last job).
   2577 		 */
   2578 		timerclear(&j_usrtime);
   2579 		timerclear(&j_systime);
   2580 		rv = execute(t->left, f | XTIME, xerrok);
   2581 		if (t->left->type == TCOM)
   2582 			tf |= t->left->str[0];
   2583 		mksh_TIME(tv1);
   2584 		getrusage(RUSAGE_SELF, &ru1);
   2585 		getrusage(RUSAGE_CHILDREN, &cru1);
   2586 	} else
   2587 		tf = TF_NOARGS;
   2588 
   2589 	if (tf & TF_NOARGS) {
   2590 		/* ksh93 - report shell times (shell+kids) */
   2591 		tf |= TF_NOREAL;
   2592 		timeradd(&ru0.ru_utime, &cru0.ru_utime, &usrtime);
   2593 		timeradd(&ru0.ru_stime, &cru0.ru_stime, &systime);
   2594 	} else {
   2595 		timersub(&ru1.ru_utime, &ru0.ru_utime, &usrtime);
   2596 		timeradd(&usrtime, &j_usrtime, &usrtime);
   2597 		timersub(&ru1.ru_stime, &ru0.ru_stime, &systime);
   2598 		timeradd(&systime, &j_systime, &systime);
   2599 	}
   2600 
   2601 	if (!(tf & TF_NOREAL)) {
   2602 		timersub(&tv1, &tv0, &tv1);
   2603 		if (tf & TF_POSIX)
   2604 			p_time(shl_out, true, tv1.tv_sec, tv1.tv_usec,
   2605 			    5, "real ", "\n");
   2606 		else
   2607 			p_time(shl_out, false, tv1.tv_sec, tv1.tv_usec,
   2608 			    5, null, " real ");
   2609 	}
   2610 	if (tf & TF_POSIX)
   2611 		p_time(shl_out, true, usrtime.tv_sec, usrtime.tv_usec,
   2612 		    5, "user ", "\n");
   2613 	else
   2614 		p_time(shl_out, false, usrtime.tv_sec, usrtime.tv_usec,
   2615 		    5, null, " user ");
   2616 	if (tf & TF_POSIX)
   2617 		p_time(shl_out, true, systime.tv_sec, systime.tv_usec,
   2618 		    5, "sys  ", "\n");
   2619 	else
   2620 		p_time(shl_out, false, systime.tv_sec, systime.tv_usec,
   2621 		    5, null, " system\n");
   2622 	shf_flush(shl_out);
   2623 
   2624 	return (rv);
   2625 }
   2626 
   2627 void
   2628 timex_hook(struct op *t, char **volatile *app)
   2629 {
   2630 	char **wp = *app;
   2631 	int optc, i, j;
   2632 	Getopt opt;
   2633 
   2634 	ksh_getopt_reset(&opt, 0);
   2635 	/* start at the start */
   2636 	opt.optind = 0;
   2637 	while ((optc = ksh_getopt((const char **)wp, &opt, ":p")) != -1)
   2638 		switch (optc) {
   2639 		case 'p':
   2640 			t->str[0] |= TF_POSIX;
   2641 			break;
   2642 		case '?':
   2643 			errorf("time: -%s %s", opt.optarg,
   2644 			    "unknown option");
   2645 		case ':':
   2646 			errorf("time: -%s %s", opt.optarg,
   2647 			    "requires an argument");
   2648 		}
   2649 	/* Copy command words down over options. */
   2650 	if (opt.optind != 0) {
   2651 		for (i = 0; i < opt.optind; i++)
   2652 			afree(wp[i], ATEMP);
   2653 		for (i = 0, j = opt.optind; (wp[i] = wp[j]); i++, j++)
   2654 			;
   2655 	}
   2656 	if (!wp[0])
   2657 		t->str[0] |= TF_NOARGS;
   2658 	*app = wp;
   2659 }
   2660 
   2661 /* exec with no args - args case is taken care of in comexec() */
   2662 int
   2663 c_exec(const char **wp MKSH_A_UNUSED)
   2664 {
   2665 	int i;
   2666 
   2667 	/* make sure redirects stay in place */
   2668 	if (e->savefd != NULL) {
   2669 		for (i = 0; i < NUFILE; i++) {
   2670 			if (e->savefd[i] > 0)
   2671 				close(e->savefd[i]);
   2672 #ifndef MKSH_LEGACY_MODE
   2673 			/*
   2674 			 * keep all file descriptors > 2 private for ksh,
   2675 			 * but not for POSIX or legacy/kludge sh
   2676 			 */
   2677 			if (!Flag(FPOSIX) && !Flag(FSH) && i > 2 &&
   2678 			    e->savefd[i])
   2679 				fcntl(i, F_SETFD, FD_CLOEXEC);
   2680 #endif
   2681 		}
   2682 		e->savefd = NULL;
   2683 	}
   2684 	return (0);
   2685 }
   2686 
   2687 #if HAVE_MKNOD
   2688 int
   2689 c_mknod(const char **wp)
   2690 {
   2691 	int argc, optc, rv = 0;
   2692 	bool ismkfifo = false;
   2693 	const char **argv;
   2694 	void *set = NULL;
   2695 	mode_t mode = 0, oldmode = 0;
   2696 
   2697 	while ((optc = ksh_getopt(wp, &builtin_opt, "m:")) != -1) {
   2698 		switch (optc) {
   2699 		case 'm':
   2700 			set = setmode(builtin_opt.optarg);
   2701 			if (set == NULL) {
   2702 				bi_errorf("invalid file mode");
   2703 				return (1);
   2704 			}
   2705 			mode = getmode(set, (mode_t)(DEFFILEMODE));
   2706 			free_ossetmode(set);
   2707 			break;
   2708 		default:
   2709 			goto c_mknod_usage;
   2710 		}
   2711 	}
   2712 	argv = &wp[builtin_opt.optind];
   2713 	if (argv[0] == NULL)
   2714 		goto c_mknod_usage;
   2715 	for (argc = 0; argv[argc]; argc++)
   2716 		;
   2717 	if (argc == 2 && argv[1][0] == 'p')
   2718 		ismkfifo = true;
   2719 	else if (argc != 4 || (argv[1][0] != 'b' && argv[1][0] != 'c'))
   2720 		goto c_mknod_usage;
   2721 
   2722 	if (set != NULL)
   2723 		oldmode = umask((mode_t)0);
   2724 	else
   2725 		mode = DEFFILEMODE;
   2726 
   2727 	mode |= (argv[1][0] == 'b') ? S_IFBLK :
   2728 	    (argv[1][0] == 'c') ? S_IFCHR : 0;
   2729 
   2730 	if (!ismkfifo) {
   2731 		unsigned long majnum, minnum;
   2732 		dev_t dv;
   2733 		char *c;
   2734 
   2735 		majnum = strtoul(argv[2], &c, 0);
   2736 		if ((c == argv[2]) || (*c != '\0')) {
   2737 			bi_errorf("non-numeric %s %s '%s'", "device", "major", argv[2]);
   2738 			goto c_mknod_err;
   2739 		}
   2740 		minnum = strtoul(argv[3], &c, 0);
   2741 		if ((c == argv[3]) || (*c != '\0')) {
   2742 			bi_errorf("non-numeric %s %s '%s'", "device", "minor", argv[3]);
   2743 			goto c_mknod_err;
   2744 		}
   2745 		dv = makedev(majnum, minnum);
   2746 		if ((unsigned long)(major(dv)) != majnum) {
   2747 			bi_errorf("%s %s too large: %lu", "device", "major", majnum);
   2748 			goto c_mknod_err;
   2749 		}
   2750 		if ((unsigned long)(minor(dv)) != minnum) {
   2751 			bi_errorf("%s %s too large: %lu", "device", "minor", minnum);
   2752 			goto c_mknod_err;
   2753 		}
   2754 		if (mknod(argv[0], mode, dv))
   2755 			goto c_mknod_failed;
   2756 	} else if (mkfifo(argv[0], mode)) {
   2757  c_mknod_failed:
   2758 		bi_errorf("%s: %s", argv[0], cstrerror(errno));
   2759  c_mknod_err:
   2760 		rv = 1;
   2761 	}
   2762 
   2763 	if (set)
   2764 		umask(oldmode);
   2765 	return (rv);
   2766  c_mknod_usage:
   2767 	bi_errorf("%s: %s", "usage", "mknod [-m mode] name b|c major minor");
   2768 	bi_errorf("%s: %s", "usage", "mknod [-m mode] name p");
   2769 	return (1);
   2770 }
   2771 #endif
   2772 
   2773 /*-
   2774    test(1) roughly accepts the following grammar:
   2775 	oexpr	::= aexpr | aexpr "-o" oexpr ;
   2776 	aexpr	::= nexpr | nexpr "-a" aexpr ;
   2777 	nexpr	::= primary | "!" nexpr ;
   2778 	primary	::= unary-operator operand
   2779 		| operand binary-operator operand
   2780 		| operand
   2781 		| "(" oexpr ")"
   2782 		;
   2783 
   2784 	unary-operator ::= "-a"|"-r"|"-w"|"-x"|"-e"|"-f"|"-d"|"-c"|"-b"|"-p"|
   2785 			   "-u"|"-g"|"-k"|"-s"|"-t"|"-z"|"-n"|"-o"|"-O"|"-G"|
   2786 			   "-L"|"-h"|"-S"|"-H";
   2787 
   2788 	binary-operator ::= "="|"=="|"!="|"-eq"|"-ne"|"-ge"|"-gt"|"-le"|"-lt"|
   2789 			    "-nt"|"-ot"|"-ef"|
   2790 			    "<"|">"	# rules used for [[ ... ]] expressions
   2791 			    ;
   2792 	operand ::= <anything>
   2793 */
   2794 
   2795 /* POSIX says > 1 for errors */
   2796 #define T_ERR_EXIT 2
   2797 
   2798 int
   2799 c_test(const char **wp)
   2800 {
   2801 	int argc, rv, invert = 0;
   2802 	Test_env te;
   2803 	Test_op op;
   2804 	const char *lhs, **swp;
   2805 
   2806 	te.flags = 0;
   2807 	te.isa = ptest_isa;
   2808 	te.getopnd = ptest_getopnd;
   2809 	te.eval = test_eval;
   2810 	te.error = ptest_error;
   2811 
   2812 	for (argc = 0; wp[argc]; argc++)
   2813 		;
   2814 
   2815 	if (strcmp(wp[0], "[") == 0) {
   2816 		if (strcmp(wp[--argc], "]") != 0) {
   2817 			bi_errorf("missing ]");
   2818 			return (T_ERR_EXIT);
   2819 		}
   2820 	}
   2821 
   2822 	te.pos.wp = wp + 1;
   2823 	te.wp_end = wp + argc;
   2824 
   2825 	/*
   2826 	 * Attempt to conform to POSIX special cases. This is pretty
   2827 	 * dumb code straight-forward from the 2008 spec, but unless
   2828 	 * the old pdksh code doesn't live from so many assumptions.
   2829 	 * It does, though, inline some calls to '(*te.funcname)()'.
   2830 	 */
   2831 	switch (argc - 1) {
   2832 	case 0:
   2833 		return (1);
   2834 	case 1:
   2835  ptest_one:
   2836 		op = TO_STNZE;
   2837 		goto ptest_unary;
   2838 	case 2:
   2839  ptest_two:
   2840 		if (ptest_isa(&te, TM_NOT)) {
   2841 			++invert;
   2842 			goto ptest_one;
   2843 		}
   2844 		if ((op = ptest_isa(&te, TM_UNOP))) {
   2845  ptest_unary:
   2846 			rv = test_eval(&te, op, *te.pos.wp++, NULL, true);
   2847  ptest_out:
   2848 			return ((invert & 1) ? rv : !rv);
   2849 		}
   2850 		/* let the parser deal with anything else */
   2851 		break;
   2852 	case 3:
   2853  ptest_three:
   2854 		swp = te.pos.wp;
   2855 		/* use inside knowledge of ptest_getopnd inlined below */
   2856 		lhs = *te.pos.wp++;
   2857 		if ((op = ptest_isa(&te, TM_BINOP))) {
   2858 			/* test lhs op rhs */
   2859 			rv = test_eval(&te, op, lhs, *te.pos.wp++, true);
   2860 			goto ptest_out;
   2861 		}
   2862 		/* back up to lhs */
   2863 		te.pos.wp = swp;
   2864 		if (ptest_isa(&te, TM_NOT)) {
   2865 			++invert;
   2866 			goto ptest_two;
   2867 		}
   2868 		if (ptest_isa(&te, TM_OPAREN)) {
   2869 			swp = te.pos.wp;
   2870 			/* skip operand, without evaluation */
   2871 			te.pos.wp++;
   2872 			/* check for closing parenthesis */
   2873 			op = ptest_isa(&te, TM_CPAREN);
   2874 			/* back up to operand */
   2875 			te.pos.wp = swp;
   2876 			/* if there was a closing paren, handle it */
   2877 			if (op)
   2878 				goto ptest_one;
   2879 			/* backing up is done before calling the parser */
   2880 		}
   2881 		/* let the parser deal with it */
   2882 		break;
   2883 	case 4:
   2884 		if (ptest_isa(&te, TM_NOT)) {
   2885 			++invert;
   2886 			goto ptest_three;
   2887 		}
   2888 		if (ptest_isa(&te, TM_OPAREN)) {
   2889 			swp = te.pos.wp;
   2890 			/* skip two operands, without evaluation */
   2891 			te.pos.wp++;
   2892 			te.pos.wp++;
   2893 			/* check for closing parenthesis */
   2894 			op = ptest_isa(&te, TM_CPAREN);
   2895 			/* back up to first operand */
   2896 			te.pos.wp = swp;
   2897 			/* if there was a closing paren, handle it */
   2898 			if (op)
   2899 				goto ptest_two;
   2900 			/* backing up is done before calling the parser */
   2901 		}
   2902 		/* defer this to the parser */
   2903 		break;
   2904 	}
   2905 
   2906 	/* "The results are unspecified." */
   2907 	te.pos.wp = wp + 1;
   2908 	return (test_parse(&te));
   2909 }
   2910 
   2911 /*
   2912  * Generic test routines.
   2913  */
   2914 
   2915 Test_op
   2916 test_isop(Test_meta meta, const char *s)
   2917 {
   2918 	char sc1;
   2919 	const struct t_op *tbl;
   2920 
   2921 	tbl = meta == TM_UNOP ? u_ops : b_ops;
   2922 	if (*s) {
   2923 		sc1 = s[1];
   2924 		for (; tbl->op_text[0]; tbl++)
   2925 			if (sc1 == tbl->op_text[1] && !strcmp(s, tbl->op_text))
   2926 				return (tbl->op_num);
   2927 	}
   2928 	return (TO_NONOP);
   2929 }
   2930 
   2931 int
   2932 test_eval(Test_env *te, Test_op op, const char *opnd1, const char *opnd2,
   2933     bool do_eval)
   2934 {
   2935 	int i, s;
   2936 	size_t k;
   2937 	struct stat b1, b2;
   2938 	mksh_ari_t v1, v2;
   2939 
   2940 	if (!do_eval)
   2941 		return (0);
   2942 
   2943 #ifdef DEBUG
   2944 	switch (op) {
   2945 	/* Binary operators */
   2946 	case TO_STEQL:
   2947 	case TO_STNEQ:
   2948 	case TO_STLT:
   2949 	case TO_STGT:
   2950 	case TO_INTEQ:
   2951 	case TO_INTNE:
   2952 	case TO_INTGT:
   2953 	case TO_INTGE:
   2954 	case TO_INTLT:
   2955 	case TO_INTLE:
   2956 	case TO_FILEQ:
   2957 	case TO_FILNT:
   2958 	case TO_FILOT:
   2959 		/* consistency check, but does not happen in practice */
   2960 		if (!opnd2) {
   2961 			te->flags |= TEF_ERROR;
   2962 			return (1);
   2963 		}
   2964 		break;
   2965 	default:
   2966 		/* for completeness of switch */
   2967 		break;
   2968 	}
   2969 #endif
   2970 
   2971 	switch (op) {
   2972 
   2973 	/*
   2974 	 * Unary Operators
   2975 	 */
   2976 
   2977 	/* -n */
   2978 	case TO_STNZE:
   2979 		return (*opnd1 != '\0');
   2980 
   2981 	/* -z */
   2982 	case TO_STZER:
   2983 		return (*opnd1 == '\0');
   2984 
   2985 	/* -o */
   2986 	case TO_OPTION:
   2987 		if ((i = *opnd1) == '!' || i == '?')
   2988 			opnd1++;
   2989 		if ((k = option(opnd1)) == (size_t)-1)
   2990 			return (0);
   2991 		return (i == '?' ? 1 : i == '!' ? !Flag(k) : Flag(k));
   2992 
   2993 	/* -r */
   2994 	case TO_FILRD:
   2995 		/* LINTED use of access */
   2996 		return (access(opnd1, R_OK) == 0);
   2997 
   2998 	/* -w */
   2999 	case TO_FILWR:
   3000 		/* LINTED use of access */
   3001 		return (access(opnd1, W_OK) == 0);
   3002 
   3003 	/* -x */
   3004 	case TO_FILEX:
   3005 		return (ksh_access(opnd1, X_OK) == 0);
   3006 
   3007 	/* -a */
   3008 	case TO_FILAXST:
   3009 	/* -e */
   3010 	case TO_FILEXST:
   3011 		return (stat(opnd1, &b1) == 0);
   3012 
   3013 	/* -r */
   3014 	case TO_FILREG:
   3015 		return (stat(opnd1, &b1) == 0 && S_ISREG(b1.st_mode));
   3016 
   3017 	/* -d */
   3018 	case TO_FILID:
   3019 		return (stat(opnd1, &b1) == 0 && S_ISDIR(b1.st_mode));
   3020 
   3021 	/* -c */
   3022 	case TO_FILCDEV:
   3023 		return (stat(opnd1, &b1) == 0 && S_ISCHR(b1.st_mode));
   3024 
   3025 	/* -b */
   3026 	case TO_FILBDEV:
   3027 		return (stat(opnd1, &b1) == 0 && S_ISBLK(b1.st_mode));
   3028 
   3029 	/* -p */
   3030 	case TO_FILFIFO:
   3031 		return (stat(opnd1, &b1) == 0 && S_ISFIFO(b1.st_mode));
   3032 
   3033 	/* -h or -L */
   3034 	case TO_FILSYM:
   3035 #ifdef MKSH__NO_SYMLINK
   3036 		return (0);
   3037 #else
   3038 		return (lstat(opnd1, &b1) == 0 && S_ISLNK(b1.st_mode));
   3039 #endif
   3040 
   3041 	/* -S */
   3042 	case TO_FILSOCK:
   3043 		return (stat(opnd1, &b1) == 0 && S_ISSOCK(b1.st_mode));
   3044 
   3045 	/* -H => HP context dependent files (directories) */
   3046 	case TO_FILCDF:
   3047 #ifdef S_ISCDF
   3048 	{
   3049 		char *nv;
   3050 
   3051 		/*
   3052 		 * Append a + to filename and check to see if result is
   3053 		 * a setuid directory. CDF stuff in general is hookey,
   3054 		 * since it breaks for, e.g., the following sequence:
   3055 		 * echo hi >foo+; mkdir foo; echo bye >foo/default;
   3056 		 * chmod u+s foo (foo+ refers to the file with hi in it,
   3057 		 * there is no way to get at the file with bye in it;
   3058 		 * please correct me if I'm wrong about this).
   3059 		 */
   3060 
   3061 		nv = shf_smprintf("%s+", opnd1);
   3062 		i = (stat(nv, &b1) == 0 && S_ISCDF(b1.st_mode));
   3063 		afree(nv, ATEMP);
   3064 		return (i);
   3065 	}
   3066 #else
   3067 		return (0);
   3068 #endif
   3069 
   3070 	/* -u */
   3071 	case TO_FILSETU:
   3072 		return (stat(opnd1, &b1) == 0 &&
   3073 		    (b1.st_mode & S_ISUID) == S_ISUID);
   3074 
   3075 	/* -g */
   3076 	case TO_FILSETG:
   3077 		return (stat(opnd1, &b1) == 0 &&
   3078 		    (b1.st_mode & S_ISGID) == S_ISGID);
   3079 
   3080 	/* -k */
   3081 	case TO_FILSTCK:
   3082 #ifdef S_ISVTX
   3083 		return (stat(opnd1, &b1) == 0 &&
   3084 		    (b1.st_mode & S_ISVTX) == S_ISVTX);
   3085 #else
   3086 		return (0);
   3087 #endif
   3088 
   3089 	/* -s */
   3090 	case TO_FILGZ:
   3091 		return (stat(opnd1, &b1) == 0 && (off_t)b1.st_size > (off_t)0);
   3092 
   3093 	/* -t */
   3094 	case TO_FILTT:
   3095 		if (opnd1 && !bi_getn(opnd1, &i)) {
   3096 			te->flags |= TEF_ERROR;
   3097 			i = 0;
   3098 		} else
   3099 			i = isatty(opnd1 ? i : 0);
   3100 		return (i);
   3101 
   3102 	/* -O */
   3103 	case TO_FILUID:
   3104 		return (stat(opnd1, &b1) == 0 && (uid_t)b1.st_uid == ksheuid);
   3105 
   3106 	/* -G */
   3107 	case TO_FILGID:
   3108 		return (stat(opnd1, &b1) == 0 && (gid_t)b1.st_gid == getegid());
   3109 
   3110 	/*
   3111 	 * Binary Operators
   3112 	 */
   3113 
   3114 	/* = */
   3115 	case TO_STEQL:
   3116 		if (te->flags & TEF_DBRACKET)
   3117 			return (gmatchx(opnd1, opnd2, false));
   3118 		return (strcmp(opnd1, opnd2) == 0);
   3119 
   3120 	/* != */
   3121 	case TO_STNEQ:
   3122 		if (te->flags & TEF_DBRACKET)
   3123 			return (!gmatchx(opnd1, opnd2, false));
   3124 		return (strcmp(opnd1, opnd2) != 0);
   3125 
   3126 	/* < */
   3127 	case TO_STLT:
   3128 		return (strcmp(opnd1, opnd2) < 0);
   3129 
   3130 	/* > */
   3131 	case TO_STGT:
   3132 		return (strcmp(opnd1, opnd2) > 0);
   3133 
   3134 	/* -eq */
   3135 	case TO_INTEQ:
   3136 	/* -ne */
   3137 	case TO_INTNE:
   3138 	/* -ge */
   3139 	case TO_INTGE:
   3140 	/* -gt */
   3141 	case TO_INTGT:
   3142 	/* -le */
   3143 	case TO_INTLE:
   3144 	/* -lt */
   3145 	case TO_INTLT:
   3146 		if (!evaluate(opnd1, &v1, KSH_RETURN_ERROR, false) ||
   3147 		    !evaluate(opnd2, &v2, KSH_RETURN_ERROR, false)) {
   3148 			/* error already printed.. */
   3149 			te->flags |= TEF_ERROR;
   3150 			return (1);
   3151 		}
   3152 		switch (op) {
   3153 		case TO_INTEQ:
   3154 			return (v1 == v2);
   3155 		case TO_INTNE:
   3156 			return (v1 != v2);
   3157 		case TO_INTGE:
   3158 			return (v1 >= v2);
   3159 		case TO_INTGT:
   3160 			return (v1 > v2);
   3161 		case TO_INTLE:
   3162 			return (v1 <= v2);
   3163 		case TO_INTLT:
   3164 			return (v1 < v2);
   3165 		default:
   3166 			/* NOTREACHED */
   3167 			break;
   3168 		}
   3169 		/* NOTREACHED */
   3170 
   3171 	/* -nt */
   3172 	case TO_FILNT:
   3173 		/*
   3174 		 * ksh88/ksh93 succeed if file2 can't be stated
   3175 		 * (subtly different from 'does not exist').
   3176 		 */
   3177 		return (stat(opnd1, &b1) == 0 &&
   3178 		    (((s = stat(opnd2, &b2)) == 0 &&
   3179 		    b1.st_mtime > b2.st_mtime) || s < 0));
   3180 
   3181 	/* -ot */
   3182 	case TO_FILOT:
   3183 		/*
   3184 		 * ksh88/ksh93 succeed if file1 can't be stated
   3185 		 * (subtly different from 'does not exist').
   3186 		 */
   3187 		return (stat(opnd2, &b2) == 0 &&
   3188 		    (((s = stat(opnd1, &b1)) == 0 &&
   3189 		    b1.st_mtime < b2.st_mtime) || s < 0));
   3190 
   3191 	/* -ef */
   3192 	case TO_FILEQ:
   3193 		return (stat (opnd1, &b1) == 0 && stat (opnd2, &b2) == 0 &&
   3194 		    b1.st_dev == b2.st_dev && b1.st_ino == b2.st_ino);
   3195 
   3196 	/* all other cases */
   3197 	case TO_NONOP:
   3198 	case TO_NONNULL:
   3199 		/* throw the error */
   3200 		break;
   3201 	}
   3202 	(*te->error)(te, 0, "internal error: unknown op");
   3203 	return (1);
   3204 }
   3205 
   3206 int
   3207 test_parse(Test_env *te)
   3208 {
   3209 	int rv;
   3210 
   3211 	rv = test_oexpr(te, 1);
   3212 
   3213 	if (!(te->flags & TEF_ERROR) && !(*te->isa)(te, TM_END))
   3214 		(*te->error)(te, 0, "unexpected operator/operand");
   3215 
   3216 	return ((te->flags & TEF_ERROR) ? T_ERR_EXIT : !rv);
   3217 }
   3218 
   3219 static int
   3220 test_oexpr(Test_env *te, bool do_eval)
   3221 {
   3222 	int rv;
   3223 
   3224 	if ((rv = test_aexpr(te, do_eval)))
   3225 		do_eval = false;
   3226 	if (!(te->flags & TEF_ERROR) && (*te->isa)(te, TM_OR))
   3227 		return (test_oexpr(te, do_eval) || rv);
   3228 	return (rv);
   3229 }
   3230 
   3231 static int
   3232 test_aexpr(Test_env *te, bool do_eval)
   3233 {
   3234 	int rv;
   3235 
   3236 	if (!(rv = test_nexpr(te, do_eval)))
   3237 		do_eval = false;
   3238 	if (!(te->flags & TEF_ERROR) && (*te->isa)(te, TM_AND))
   3239 		return (test_aexpr(te, do_eval) && rv);
   3240 	return (rv);
   3241 }
   3242 
   3243 static int
   3244 test_nexpr(Test_env *te, bool do_eval)
   3245 {
   3246 	if (!(te->flags & TEF_ERROR) && (*te->isa)(te, TM_NOT))
   3247 		return (!test_nexpr(te, do_eval));
   3248 	return (test_primary(te, do_eval));
   3249 }
   3250 
   3251 static int
   3252 test_primary(Test_env *te, bool do_eval)
   3253 {
   3254 	const char *opnd1, *opnd2;
   3255 	int rv;
   3256 	Test_op op;
   3257 
   3258 	if (te->flags & TEF_ERROR)
   3259 		return (0);
   3260 	if ((*te->isa)(te, TM_OPAREN)) {
   3261 		rv = test_oexpr(te, do_eval);
   3262 		if (te->flags & TEF_ERROR)
   3263 			return (0);
   3264 		if (!(*te->isa)(te, TM_CPAREN)) {
   3265 			(*te->error)(te, 0, "missing )");
   3266 			return (0);
   3267 		}
   3268 		return (rv);
   3269 	}
   3270 	/*
   3271 	 * Binary should have precedence over unary in this case
   3272 	 * so that something like test \( -f = -f \) is accepted
   3273 	 */
   3274 	if ((te->flags & TEF_DBRACKET) || (&te->pos.wp[1] < te->wp_end &&
   3275 	    !test_isop(TM_BINOP, te->pos.wp[1]))) {
   3276 		if ((op = (*te->isa)(te, TM_UNOP))) {
   3277 			/* unary expression */
   3278 			opnd1 = (*te->getopnd)(te, op, do_eval);
   3279 			if (!opnd1) {
   3280 				(*te->error)(te, -1, "missing argument");
   3281 				return (0);
   3282 			}
   3283 
   3284 			return ((*te->eval)(te, op, opnd1, NULL, do_eval));
   3285 		}
   3286 	}
   3287 	opnd1 = (*te->getopnd)(te, TO_NONOP, do_eval);
   3288 	if (!opnd1) {
   3289 		(*te->error)(te, 0, "expression expected");
   3290 		return (0);
   3291 	}
   3292 	if ((op = (*te->isa)(te, TM_BINOP))) {
   3293 		/* binary expression */
   3294 		opnd2 = (*te->getopnd)(te, op, do_eval);
   3295 		if (!opnd2) {
   3296 			(*te->error)(te, -1, "missing second argument");
   3297 			return (0);
   3298 		}
   3299 
   3300 		return ((*te->eval)(te, op, opnd1, opnd2, do_eval));
   3301 	}
   3302 	return ((*te->eval)(te, TO_STNZE, opnd1, NULL, do_eval));
   3303 }
   3304 
   3305 /*
   3306  * Plain test (test and [ .. ]) specific routines.
   3307  */
   3308 
   3309 /*
   3310  * Test if the current token is a whatever. Accepts the current token if
   3311  * it is. Returns 0 if it is not, non-zero if it is (in the case of
   3312  * TM_UNOP and TM_BINOP, the returned value is a Test_op).
   3313  */
   3314 static Test_op
   3315 ptest_isa(Test_env *te, Test_meta meta)
   3316 {
   3317 	/* Order important - indexed by Test_meta values */
   3318 	static const char * const tokens[] = {
   3319 		"-o", "-a", "!", "(", ")"
   3320 	};
   3321 	Test_op rv;
   3322 
   3323 	if (te->pos.wp >= te->wp_end)
   3324 		return (meta == TM_END ? TO_NONNULL : TO_NONOP);
   3325 
   3326 	if (meta == TM_UNOP || meta == TM_BINOP)
   3327 		rv = test_isop(meta, *te->pos.wp);
   3328 	else if (meta == TM_END)
   3329 		rv = TO_NONOP;
   3330 	else
   3331 		rv = !strcmp(*te->pos.wp, tokens[(int)meta]) ?
   3332 		    TO_NONNULL : TO_NONOP;
   3333 
   3334 	/* Accept the token? */
   3335 	if (rv != TO_NONOP)
   3336 		te->pos.wp++;
   3337 
   3338 	return (rv);
   3339 }
   3340 
   3341 static const char *
   3342 ptest_getopnd(Test_env *te, Test_op op, bool do_eval MKSH_A_UNUSED)
   3343 {
   3344 	if (te->pos.wp >= te->wp_end)
   3345 		return (op == TO_FILTT ? "1" : NULL);
   3346 	return (*te->pos.wp++);
   3347 }
   3348 
   3349 static void
   3350 ptest_error(Test_env *te, int ofs, const char *msg)
   3351 {
   3352 	const char *op;
   3353 
   3354 	te->flags |= TEF_ERROR;
   3355 	if ((op = te->pos.wp + ofs >= te->wp_end ? NULL : te->pos.wp[ofs]))
   3356 		bi_errorf("%s: %s", op, msg);
   3357 	else
   3358 		bi_errorf("%s", msg);
   3359 }
   3360 
   3361 #ifndef MKSH_NO_LIMITS
   3362 #define SOFT	0x1
   3363 #define HARD	0x2
   3364 
   3365 /* Magic to divine the 'm' and 'v' limits */
   3366 
   3367 #ifdef RLIMIT_AS
   3368 #if !defined(RLIMIT_VMEM) || (RLIMIT_VMEM == RLIMIT_AS) || \
   3369     !defined(RLIMIT_RSS) || (RLIMIT_VMEM == RLIMIT_RSS)
   3370 #define ULIMIT_V_IS_AS
   3371 #elif defined(RLIMIT_VMEM)
   3372 #if !defined(RLIMIT_RSS) || (RLIMIT_RSS == RLIMIT_AS)
   3373 #define ULIMIT_V_IS_AS
   3374 #else
   3375 #define ULIMIT_V_IS_VMEM
   3376 #endif
   3377 #endif
   3378 #endif
   3379 
   3380 #ifdef RLIMIT_RSS
   3381 #ifdef ULIMIT_V_IS_VMEM
   3382 #define ULIMIT_M_IS_RSS
   3383 #elif defined(RLIMIT_VMEM) && (RLIMIT_VMEM == RLIMIT_RSS)
   3384 #define ULIMIT_M_IS_VMEM
   3385 #else
   3386 #define ULIMIT_M_IS_RSS
   3387 #endif
   3388 #if defined(ULIMIT_M_IS_RSS) && defined(RLIMIT_AS) && (RLIMIT_RSS == RLIMIT_AS)
   3389 #undef ULIMIT_M_IS_RSS
   3390 #endif
   3391 #endif
   3392 
   3393 #if !defined(RLIMIT_AS) && !defined(ULIMIT_M_IS_VMEM) && defined(RLIMIT_VMEM)
   3394 #define ULIMIT_V_IS_VMEM
   3395 #endif
   3396 
   3397 #if !defined(ULIMIT_V_IS_VMEM) && defined(RLIMIT_VMEM) && \
   3398     (!defined(RLIMIT_RSS) || (defined(RLIMIT_AS) && (RLIMIT_RSS == RLIMIT_AS)))
   3399 #define ULIMIT_M_IS_VMEM
   3400 #endif
   3401 
   3402 #if defined(ULIMIT_M_IS_VMEM) && defined(RLIMIT_AS) && \
   3403     (RLIMIT_VMEM == RLIMIT_AS)
   3404 #undef ULIMIT_M_IS_VMEM
   3405 #endif
   3406 
   3407 #if defined(ULIMIT_M_IS_RSS) && defined(ULIMIT_M_IS_VMEM)
   3408 # error nonsensical m ulimit
   3409 #endif
   3410 
   3411 #if defined(ULIMIT_V_IS_VMEM) && defined(ULIMIT_V_IS_AS)
   3412 # error nonsensical v ulimit
   3413 #endif
   3414 
   3415 #define RLIMITS_DEFNS
   3416 #include "rlimits.gen"
   3417 
   3418 static void print_ulimit(const struct limits *, int);
   3419 static int set_ulimit(const struct limits *, const char *, int);
   3420 
   3421 static const struct limits * const rlimits[] = {
   3422 #define RLIMITS_ITEMS
   3423 #include "rlimits.gen"
   3424 };
   3425 
   3426 static const char rlimits_opts[] =
   3427 #define RLIMITS_OPTCS
   3428 #include "rlimits.gen"
   3429     ;
   3430 
   3431 int
   3432 c_ulimit(const char **wp)
   3433 {
   3434 	size_t i = 0;
   3435 	int how = SOFT | HARD, optc, what = 'f';
   3436 	bool all = false;
   3437 
   3438 	while ((optc = ksh_getopt(wp, &builtin_opt, rlimits_opts)) != -1)
   3439 		switch (optc) {
   3440 		case 'H':
   3441 			how = HARD;
   3442 			break;
   3443 		case 'S':
   3444 			how = SOFT;
   3445 			break;
   3446 		case 'a':
   3447 			all = true;
   3448 			break;
   3449 		case '?':
   3450 			bi_errorf("usage: ulimit [-%s] [value]", rlimits_opts);
   3451 			return (1);
   3452 		default:
   3453 			what = optc;
   3454 		}
   3455 
   3456 	while (i < NELEM(rlimits)) {
   3457 		if (rlimits[i]->optchar == what)
   3458 			goto found;
   3459 		++i;
   3460 	}
   3461 	internal_warningf("ulimit: %c", what);
   3462 	return (1);
   3463  found:
   3464 	if (wp[builtin_opt.optind]) {
   3465 		if (all || wp[builtin_opt.optind + 1]) {
   3466 			bi_errorf("too many arguments");
   3467 			return (1);
   3468 		}
   3469 		return (set_ulimit(rlimits[i], wp[builtin_opt.optind], how));
   3470 	}
   3471 	if (!all)
   3472 		print_ulimit(rlimits[i], how);
   3473 	else for (i = 0; i < NELEM(rlimits); ++i) {
   3474 		shprintf("%-20s ", rlimits[i]->name);
   3475 		print_ulimit(rlimits[i], how);
   3476 	}
   3477 	return (0);
   3478 }
   3479 
   3480 static int
   3481 set_ulimit(const struct limits *l, const char *v, int how)
   3482 {
   3483 	rlim_t val = (rlim_t)0;
   3484 	struct rlimit limit;
   3485 
   3486 	if (strcmp(v, "unlimited") == 0)
   3487 		val = (rlim_t)RLIM_INFINITY;
   3488 	else {
   3489 		mksh_uari_t rval;
   3490 
   3491 		if (!evaluate(v, (mksh_ari_t *)&rval, KSH_RETURN_ERROR, false))
   3492 			return (1);
   3493 		/*
   3494 		 * Avoid problems caused by typos that evaluate misses due
   3495 		 * to evaluating unset parameters to 0...
   3496 		 * If this causes problems, will have to add parameter to
   3497 		 * evaluate() to control if unset params are 0 or an error.
   3498 		 */
   3499 		if (!rval && !ksh_isdigit(v[0])) {
   3500 			bi_errorf("invalid %s limit: %s", l->name, v);
   3501 			return (1);
   3502 		}
   3503 		val = (rlim_t)((rlim_t)rval * l->factor);
   3504 	}
   3505 
   3506 	if (getrlimit(l->resource, &limit) < 0) {
   3507 #ifndef MKSH_SMALL
   3508 		bi_errorf("limit %s could not be read, contact the mksh developers: %s",
   3509 		    l->name, cstrerror(errno));
   3510 #endif
   3511 		/* some can't be read */
   3512 		limit.rlim_cur = RLIM_INFINITY;
   3513 		limit.rlim_max = RLIM_INFINITY;
   3514 	}
   3515 	if (how & SOFT)
   3516 		limit.rlim_cur = val;
   3517 	if (how & HARD)
   3518 		limit.rlim_max = val;
   3519 	if (!setrlimit(l->resource, &limit))
   3520 		return (0);
   3521 	if (errno == EPERM)
   3522 		bi_errorf("%s exceeds allowable %s limit", v, l->name);
   3523 	else
   3524 		bi_errorf("bad %s limit: %s", l->name, cstrerror(errno));
   3525 	return (1);
   3526 }
   3527 
   3528 static void
   3529 print_ulimit(const struct limits *l, int how)
   3530 {
   3531 	rlim_t val = (rlim_t)0;
   3532 	struct rlimit limit;
   3533 
   3534 	if (getrlimit(l->resource, &limit)) {
   3535 		shf_puts("unknown\n", shl_stdout);
   3536 		return;
   3537 	}
   3538 	if (how & SOFT)
   3539 		val = limit.rlim_cur;
   3540 	else if (how & HARD)
   3541 		val = limit.rlim_max;
   3542 	if (val == (rlim_t)RLIM_INFINITY)
   3543 		shf_puts("unlimited\n", shl_stdout);
   3544 	else
   3545 		shprintf("%lu\n", (unsigned long)(val / l->factor));
   3546 }
   3547 #endif
   3548 
   3549 int
   3550 c_rename(const char **wp)
   3551 {
   3552 	int rv = 1;
   3553 
   3554 	/* skip argv[0] */
   3555 	++wp;
   3556 	if (wp[0] && !strcmp(wp[0], "--"))
   3557 		/* skip "--" (options separator) */
   3558 		++wp;
   3559 
   3560 	/* check for exactly two arguments */
   3561 	if (wp[0] == NULL	/* first argument */ ||
   3562 	    wp[1] == NULL	/* second argument */ ||
   3563 	    wp[2] != NULL	/* no further args please */)
   3564 		bi_errorf(Tsynerr);
   3565 	else if ((rv = rename(wp[0], wp[1])) != 0) {
   3566 		rv = errno;
   3567 		bi_errorf("%s: %s", "failed", cstrerror(rv));
   3568 	}
   3569 
   3570 	return (rv);
   3571 }
   3572 
   3573 int
   3574 c_realpath(const char **wp)
   3575 {
   3576 	int rv = 1;
   3577 	char *buf;
   3578 
   3579 	/* skip argv[0] */
   3580 	++wp;
   3581 	if (wp[0] && !strcmp(wp[0], "--"))
   3582 		/* skip "--" (options separator) */
   3583 		++wp;
   3584 
   3585 	/* check for exactly one argument */
   3586 	if (wp[0] == NULL || wp[1] != NULL)
   3587 		bi_errorf(Tsynerr);
   3588 	else if ((buf = do_realpath(wp[0])) == NULL) {
   3589 		rv = errno;
   3590 		bi_errorf("%s: %s", wp[0], cstrerror(rv));
   3591 		if ((unsigned int)rv > 255)
   3592 			rv = 255;
   3593 	} else {
   3594 		shprintf("%s\n", buf);
   3595 		afree(buf, ATEMP);
   3596 		rv = 0;
   3597 	}
   3598 
   3599 	return (rv);
   3600 }
   3601 
   3602 int
   3603 c_cat(const char **wp)
   3604 {
   3605 	int fd = STDIN_FILENO, rv, eno;
   3606 	ssize_t n, w;
   3607 	const char *fn = "<stdin>";
   3608 	char *buf, *cp;
   3609 #define MKSH_CAT_BUFSIZ 4096
   3610 
   3611 	/* parse options: POSIX demands we support "-u" as no-op */
   3612 	while ((rv = ksh_getopt(wp, &builtin_opt, "u")) != -1) {
   3613 		switch (rv) {
   3614 		case 'u':
   3615 			/* we already operate unbuffered */
   3616 			break;
   3617 		default:
   3618 			bi_errorf(Tsynerr);
   3619 			return (1);
   3620 		}
   3621 	}
   3622 	wp += builtin_opt.optind;
   3623 	rv = 0;
   3624 
   3625 	if ((buf = malloc_osfunc(MKSH_CAT_BUFSIZ)) == NULL) {
   3626 		bi_errorf(Toomem, (size_t)MKSH_CAT_BUFSIZ);
   3627 		return (1);
   3628 	}
   3629 
   3630 	do {
   3631 		if (*wp) {
   3632 			fn = *wp++;
   3633 			if (fn[0] == '-' && fn[1] == '\0')
   3634 				fd = STDIN_FILENO;
   3635 			else if ((fd = open(fn, O_RDONLY | O_BINARY)) < 0) {
   3636 				eno = errno;
   3637 				bi_errorf("%s: %s", fn, cstrerror(eno));
   3638 				rv = 1;
   3639 				continue;
   3640 			}
   3641 		}
   3642 		while (/* CONSTCOND */ 1) {
   3643 			n = blocking_read(fd, (cp = buf), MKSH_CAT_BUFSIZ);
   3644 			eno = errno;
   3645 			/* give the user a chance to ^C out */
   3646 			intrcheck();
   3647 			if (n == -1) {
   3648 				if (eno == EINTR) {
   3649 					/* interrupted, try again */
   3650 					continue;
   3651 				}
   3652 				/* an error occured during reading */
   3653 				bi_errorf("%s: %s", fn, cstrerror(eno));
   3654 				rv = 1;
   3655 				break;
   3656 			} else if (n == 0)
   3657 				/* end of file reached */
   3658 				break;
   3659 			while (n) {
   3660 				w = write(STDOUT_FILENO, cp, n);
   3661 				eno = errno;
   3662 				/* give the user a chance to ^C out */
   3663 				intrcheck();
   3664 				if (w == -1) {
   3665 					if (eno == EINTR)
   3666 						/* interrupted, try again */
   3667 						continue;
   3668 					/* an error occured during writing */
   3669 					bi_errorf("%s: %s", "<stdout>",
   3670 					    cstrerror(eno));
   3671 					rv = 1;
   3672 					if (fd != STDIN_FILENO)
   3673 						close(fd);
   3674 					goto out;
   3675 				}
   3676 				n -= w;
   3677 				cp += w;
   3678 			}
   3679 		}
   3680 		if (fd != STDIN_FILENO)
   3681 			close(fd);
   3682 	} while (*wp);
   3683 
   3684  out:
   3685 	free_osfunc(buf);
   3686 	return (rv);
   3687 }
   3688 
   3689 #if HAVE_SELECT
   3690 int
   3691 c_sleep(const char **wp)
   3692 {
   3693 	struct timeval tv;
   3694 	int rv = 1;
   3695 
   3696 	/* skip argv[0] */
   3697 	++wp;
   3698 	if (wp[0] && !strcmp(wp[0], "--"))
   3699 		/* skip "--" (options separator) */
   3700 		++wp;
   3701 
   3702 	if (!wp[0] || wp[1])
   3703 		bi_errorf(Tsynerr);
   3704 	else if (parse_usec(wp[0], &tv))
   3705 		bi_errorf("%s: %s '%s'", Tsynerr, cstrerror(errno), wp[0]);
   3706 	else {
   3707 #ifndef MKSH_NOPROSPECTOFWORK
   3708 		sigset_t omask, bmask;
   3709 
   3710 		/* block a number of signals from interrupting us, though */
   3711 		(void)sigemptyset(&bmask);
   3712 		(void)sigaddset(&bmask, SIGPIPE);
   3713 		(void)sigaddset(&bmask, SIGCHLD);
   3714 #ifdef SIGWINCH
   3715 		(void)sigaddset(&bmask, SIGWINCH);
   3716 #endif
   3717 #ifdef SIGINFO
   3718 		(void)sigaddset(&bmask, SIGINFO);
   3719 #endif
   3720 #ifdef SIGUSR1
   3721 		(void)sigaddset(&bmask, SIGUSR1);
   3722 #endif
   3723 #ifdef SIGUSR2
   3724 		(void)sigaddset(&bmask, SIGUSR2);
   3725 #endif
   3726 		sigprocmask(SIG_BLOCK, &bmask, &omask);
   3727 #endif
   3728 		if (select(1, NULL, NULL, NULL, &tv) == 0 || errno == EINTR)
   3729 			/*
   3730 			 * strictly speaking only for SIGALRM, but the
   3731 			 * execution may be interrupted by other signals
   3732 			 */
   3733 			rv = 0;
   3734 		else
   3735 			bi_errorf("%s: %s", Tselect, cstrerror(errno));
   3736 #ifndef MKSH_NOPROSPECTOFWORK
   3737 		/* this will re-schedule signal delivery */
   3738 		sigprocmask(SIG_SETMASK, &omask, NULL);
   3739 #endif
   3740 	}
   3741 	return (rv);
   3742 }
   3743 #endif
   3744 
   3745 #if !defined(MKSH_UNEMPLOYED) && HAVE_GETSID
   3746 static int
   3747 c_suspend(const char **wp)
   3748 {
   3749 	if (wp[1] != NULL) {
   3750 		bi_errorf("too many arguments");
   3751 		return (1);
   3752 	}
   3753 	if (Flag(FLOGIN)) {
   3754 		/* Can't suspend an orphaned process group. */
   3755 		if (getpgid(kshppid) == getpgid(0) ||
   3756 		    getsid(kshppid) != getsid(0)) {
   3757 			bi_errorf("can't suspend a login shell");
   3758 			return (1);
   3759 		}
   3760 	}
   3761 	j_suspend();
   3762 	return (0);
   3763 }
   3764 #endif
   3765