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