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