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