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