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