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