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