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