• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*	$OpenBSD: misc.c,v 1.41 2015/09/10 22:48:58 nicm Exp $	*/
2 /*	$OpenBSD: path.c,v 1.13 2015/09/05 09:47:08 jsg Exp $	*/
3 
4 /*-
5  * Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
6  *		 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2019,
7  *		 2020
8  *	mirabilos <m@mirbsd.org>
9  * Copyright (c) 2015
10  *	Daniel Richard G. <skunk@iSKUNK.ORG>
11  *
12  * Provided that these terms and disclaimer and all copyright notices
13  * are retained or reproduced in an accompanying document, permission
14  * is granted to deal in this work without restriction, including un-
15  * limited rights to use, publicly perform, distribute, sell, modify,
16  * merge, give away, or sublicence.
17  *
18  * This work is provided "AS IS" and WITHOUT WARRANTY of any kind, to
19  * the utmost extent permitted by applicable law, neither express nor
20  * implied; without malicious intent or gross negligence. In no event
21  * may a licensor, author or contributor be held liable for indirect,
22  * direct, other damage, loss, or other issues arising in any way out
23  * of dealing in the work, even if advised of the possibility of such
24  * damage or existence of a defect, except proven that it results out
25  * of said person's immediate fault when using the work as intended.
26  */
27 
28 #include "sh.h"
29 #if !HAVE_GETRUSAGE
30 #include <sys/times.h>
31 #endif
32 #if HAVE_GRP_H
33 #include <grp.h>
34 #endif
35 
36 __RCSID("$MirOS: src/bin/mksh/misc.c,v 1.302 2020/08/27 19:52:45 tg Exp $");
37 
38 #define KSH_CHVT_FLAG
39 #ifdef MKSH_SMALL
40 #undef KSH_CHVT_FLAG
41 #endif
42 #ifdef TIOCSCTTY
43 #define KSH_CHVT_CODE
44 #define KSH_CHVT_FLAG
45 #endif
46 
47 /* type bits for unsigned char */
48 unsigned char chtypes[UCHAR_MAX + 1];
49 
50 static const unsigned char *pat_scan(const unsigned char *,
51     const unsigned char *, bool) MKSH_A_PURE;
52 static int do_gmatch(const unsigned char *, const unsigned char *,
53     const unsigned char *, const unsigned char *,
54     const unsigned char *) MKSH_A_PURE;
55 static const unsigned char *gmatch_cclass(const unsigned char *, unsigned char)
56     MKSH_A_PURE;
57 #ifdef KSH_CHVT_CODE
58 static void chvt(const Getopt *);
59 #endif
60 
61 /*XXX this should go away */
62 static int make_path(const char *, const char *, char **, XString *, int *);
63 
64 #ifdef SETUID_CAN_FAIL_WITH_EAGAIN
65 /* we don't need to check for other codes, EPERM won't happen */
66 #define DO_SETUID(func,argvec) do {					\
67 	if ((func argvec) && errno == EAGAIN)				\
68 		errorf("%s failed with EAGAIN, probably due to a"	\
69 		    " too low process limit; aborting", #func);		\
70 } while (/* CONSTCOND */ 0)
71 #else
72 #define DO_SETUID(func,argvec) func argvec
73 #endif
74 
75 
76 /* called from XcheckN() to grow buffer */
77 char *
Xcheck_grow(XString * xsp,const char * xp,size_t more)78 Xcheck_grow(XString *xsp, const char *xp, size_t more)
79 {
80 	const char *old_beg = xsp->beg;
81 
82 	if (more < xsp->len)
83 		more = xsp->len;
84 	/* (xsp->len + X_EXTRA) never overflows */
85 	checkoktoadd(more, xsp->len + X_EXTRA);
86 	xsp->beg = aresize(xsp->beg, (xsp->len += more) + X_EXTRA, xsp->areap);
87 	xsp->end = xsp->beg + xsp->len;
88 	return (xsp->beg + (xp - old_beg));
89 }
90 
91 
92 #define SHFLAGS_DEFNS
93 #define FN(sname,cname,flags,ochar)		\
94 	static const struct {			\
95 		/* character flag (if any) */	\
96 		char c;				\
97 		/* OF_* */			\
98 		unsigned char optflags;		\
99 		/* long name of option */	\
100 		char name[sizeof(sname)];	\
101 	} shoptione_ ## cname = {		\
102 		ochar, flags, sname		\
103 	};
104 #include "sh_flags.gen"
105 
106 #define OFC(i) (options[i][-2])
107 #define OFF(i) (((const unsigned char *)options[i])[-1])
108 #define OFN(i) (options[i])
109 
110 const char * const options[] = {
111 #define SHFLAGS_ITEMS
112 #include "sh_flags.gen"
113 };
114 
115 /*
116  * translate -o option into F* constant (also used for test -o option)
117  */
118 size_t
option(const char * n)119 option(const char *n)
120 {
121 	size_t i = 0;
122 
123 	if (ctype(n[0], C_MINUS | C_PLUS) && n[1] && !n[2])
124 		while (i < NELEM(options)) {
125 			if (OFC(i) == n[1])
126 				return (i);
127 			++i;
128 		}
129 	else
130 		while (i < NELEM(options)) {
131 			if (!strcmp(OFN(i), n))
132 				return (i);
133 			++i;
134 		}
135 
136 	return ((size_t)-1);
137 }
138 
139 struct options_info {
140 	int opt_width;
141 	int opts[NELEM(options)];
142 };
143 
144 static void options_fmt_entry(char *, size_t, unsigned int, const void *);
145 static int printoptions(bool);
146 static int printoption(size_t);
147 
148 /* format a single select menu item */
149 static void
options_fmt_entry(char * buf,size_t buflen,unsigned int i,const void * arg)150 options_fmt_entry(char *buf, size_t buflen, unsigned int i, const void *arg)
151 {
152 	const struct options_info *oi = (const struct options_info *)arg;
153 
154 	shf_snprintf(buf, buflen, "%-*s %s",
155 	    oi->opt_width, OFN(oi->opts[i]),
156 	    Flag(oi->opts[i]) ? "on" : "off");
157 }
158 
159 static int
printoption(size_t i)160 printoption(size_t i)
161 {
162 	if (Flag(i) == baseline_flags[i])
163 		return (0);
164 	if (!OFN(i)[0]) {
165 #if !defined(MKSH_SMALL) || defined(DEBUG)
166 		bi_errorf(Tf_sd, "change in unnamed option", (int)i);
167 #endif
168 		return (1);
169 	}
170 	if (Flag(i) != 0 && Flag(i) != 1) {
171 #if !defined(MKSH_SMALL) || defined(DEBUG)
172 		bi_errorf(Tf_s_sD_s, Tdo, OFN(i), "not 0 or 1");
173 #endif
174 		return (1);
175 	}
176 	shprintf(Tf__s_s, Flag(i) ? Tdo : Tpo, OFN(i));
177 	return (0);
178 }
179 
180 static int
printoptions(bool verbose)181 printoptions(bool verbose)
182 {
183 	size_t i = 0;
184 	int rv = 0;
185 
186 	if (verbose) {
187 		size_t n = 0, len, octs = 0;
188 		struct options_info oi;
189 		struct columnise_opts co;
190 
191 		/* verbose version */
192 		shf_puts("Current option settings\n", shl_stdout);
193 
194 		oi.opt_width = 0;
195 		while (i < NELEM(options)) {
196 			if ((len = strlen(OFN(i)))) {
197 				oi.opts[n++] = i;
198 				if (len > octs)
199 					octs = len;
200 				len = utf_mbswidth(OFN(i));
201 				if ((int)len > oi.opt_width)
202 					oi.opt_width = (int)len;
203 			}
204 			++i;
205 		}
206 		co.shf = shl_stdout;
207 		co.linesep = '\n';
208 		co.prefcol = co.do_last = true;
209 		print_columns(&co, n, options_fmt_entry, &oi,
210 		    octs + 4, oi.opt_width + 4);
211 	} else {
212 		/* short version like AT&T ksh93 */
213 		shf_puts(Tset, shl_stdout);
214 		shf_puts(To_o_reset, shl_stdout);
215 		printoption(FSH);
216 		printoption(FPOSIX);
217 		while (i < FNFLAGS) {
218 			if (i != FSH && i != FPOSIX)
219 				rv |= printoption(i);
220 			++i;
221 		}
222 		shf_putc('\n', shl_stdout);
223 	}
224 	return (rv);
225 }
226 
227 char *
getoptions(void)228 getoptions(void)
229 {
230 	size_t i = 0;
231 	char c, m[(int)FNFLAGS + 1];
232 	char *cp = m;
233 
234 	while (i < NELEM(options)) {
235 		if ((c = OFC(i)) && Flag(i))
236 			*cp++ = c;
237 		++i;
238 	}
239 	strndupx(cp, m, cp - m, ATEMP);
240 	return (cp);
241 }
242 
243 /* change a Flag(*) value; takes care of special actions */
244 void
change_flag(enum sh_flag f,int what,bool newset)245 change_flag(enum sh_flag f, int what, bool newset)
246 {
247 	unsigned char oldval = Flag(f);
248 	unsigned char newval = (newset ? 1 : 0);
249 
250 	if (f == FXTRACE) {
251 		change_xtrace(newval, true);
252 		return;
253 	} else if (f == FPRIVILEGED) {
254 		if (!oldval)
255 			/* no getting back dropped privs */
256 			return;
257 		else if (!newval) {
258 			/* turning off -p */
259 			kshegid = kshgid;
260 			ksheuid = kshuid;
261 		} else if (oldval != 3)
262 			/* nor going full sugid */
263 			goto change_flag;
264 
265 		/* +++ set group IDs +++ */
266 #if HAVE_SETRESUGID
267 		DO_SETUID(setresgid, (kshegid, kshegid, kshgid));
268 #else /* !HAVE_SETRESUGID */
269 		/* setgid, setegid don't EAGAIN on Linux */
270 		setgid(kshegid);
271 #ifndef MKSH__NO_SETEUGID
272 		setegid(kshegid);
273 #endif /* !MKSH__NO_SETEUGID */
274 #endif /* !HAVE_SETRESUGID */
275 
276 		/* +++ wipe groups vector +++ */
277 #if HAVE_SETGROUPS
278 		/* setgroups doesn't EAGAIN on Linux */
279 		setgroups(0, NULL);
280 #endif /* HAVE_SETGROUPS */
281 
282 		/* +++ set user IDs +++ */
283 #if HAVE_SETRESUGID
284 		DO_SETUID(setresuid, (ksheuid, ksheuid, kshuid));
285 #else /* !HAVE_SETRESUGID */
286 		/* seteuid doesn't EAGAIN on Linux */
287 		DO_SETUID(setuid, (ksheuid));
288 #ifndef MKSH__NO_SETEUGID
289 		seteuid(ksheuid);
290 #endif /* !MKSH__NO_SETEUGID */
291 #endif /* !HAVE_SETRESUGID */
292 
293 		/* +++ privs changed +++ */
294 	} else if ((f == FPOSIX || f == FSH) && newval) {
295 		/* Turning on -o posix? */
296 		if (f == FPOSIX)
297 			/* C locale required for compliance */
298 			UTFMODE = 0;
299 		/* Turning on -o posix or -o sh? */
300 		Flag(FBRACEEXPAND) = 0;
301 #ifndef MKSH_NO_CMDLINE_EDITING
302 	} else if ((f == FEMACS ||
303 #if !MKSH_S_NOVI
304 	    f == FVI ||
305 #endif
306 	    f == FGMACS) && newval) {
307 #if !MKSH_S_NOVI
308 		Flag(FVI) = 0;
309 #endif
310 		Flag(FEMACS) = Flag(FGMACS) = 0;
311 #endif
312 	}
313 
314  change_flag:
315 	Flag(f) = newval;
316 
317 	if (f == FTALKING) {
318 		/* Changing interactive flag? */
319 		if ((what == OF_CMDLINE || what == OF_SET) && procpid == kshpid)
320 			Flag(FTALKING_I) = newval;
321 #ifndef MKSH_UNEMPLOYED
322 	} else if (f == FMONITOR) {
323 		if (what != OF_CMDLINE && newval != oldval)
324 			j_change();
325 #endif
326 	}
327 }
328 
329 void
change_xtrace(unsigned char newval,bool dosnapshot)330 change_xtrace(unsigned char newval, bool dosnapshot)
331 {
332 	static bool in_xtrace;
333 
334 	if (in_xtrace)
335 		return;
336 
337 	if (!dosnapshot && newval == Flag(FXTRACE))
338 		return;
339 
340 	if (Flag(FXTRACE) == 2) {
341 		shf_putc('\n', shl_xtrace);
342 		Flag(FXTRACE) = 1;
343 		shf_flush(shl_xtrace);
344 	}
345 
346 	if (!dosnapshot && Flag(FXTRACE) == 1)
347 		switch (newval) {
348 		case 1:
349 			return;
350 		case 2:
351 			goto changed_xtrace;
352 		}
353 
354 	shf_flush(shl_xtrace);
355 	if (shl_xtrace->fd != 2)
356 		close(shl_xtrace->fd);
357 	if (!newval || (shl_xtrace->fd = savefd(2)) == -1)
358 		shl_xtrace->fd = 2;
359 
360  changed_xtrace:
361 	if ((Flag(FXTRACE) = newval) == 2) {
362 		in_xtrace = true;
363 		Flag(FXTRACE) = 0;
364 		shf_puts(substitute(str_val(global("PS4")), 0), shl_xtrace);
365 		Flag(FXTRACE) = 2;
366 		in_xtrace = false;
367 	}
368 }
369 
370 /*
371  * Parse command line and set command arguments. Returns the index of
372  * non-option arguments, -1 if there is an error.
373  */
374 int
parse_args(const char ** argv,int what,bool * setargsp)375 parse_args(const char **argv,
376     /* OF_FIRSTTIME, OF_CMDLINE, or OF_SET */
377     int what,
378     bool *setargsp)
379 {
380 	static const char cmd_opts[] =
381 #define SHFLAGS_NOT_SET
382 #define SHFLAGS_OPTCS
383 #include "sh_flags.gen"
384 #undef SHFLAGS_NOT_SET
385 	    ;
386 	static const char set_opts[] =
387 #define SHFLAGS_NOT_CMD
388 #define SHFLAGS_OPTCS
389 #include "sh_flags.gen"
390 #undef SHFLAGS_NOT_CMD
391 	    ;
392 	bool set;
393 	const char *opts = what == OF_CMDLINE || what == OF_FIRSTTIME ?
394 	    cmd_opts : set_opts;
395 	const char *array = NULL;
396 	Getopt go;
397 	size_t i;
398 	int optc, arrayset = 0;
399 	bool sortargs = false;
400 	bool fcompatseen = false;
401 
402 	ksh_getopt_reset(&go, GF_ERROR|GF_PLUSOPT);
403 	while ((optc = ksh_getopt(argv, &go, opts)) != -1) {
404 		set = tobool(!(go.info & GI_PLUS));
405 		switch (optc) {
406 		case 'A':
407 			if (what == OF_FIRSTTIME)
408 				break;
409 			arrayset = set ? 1 : -1;
410 			array = go.optarg;
411 			break;
412 
413 		case 'o':
414 			if (what == OF_FIRSTTIME)
415 				break;
416 			if (go.optarg == NULL) {
417 				/*
418 				 * lone -o: print options
419 				 *
420 				 * Note that on the command line, -o requires
421 				 * an option (ie, can't get here if what is
422 				 * OF_CMDLINE).
423 				 */
424 #if !defined(MKSH_SMALL) || defined(DEBUG)
425 				if (!set && !baseline_flags[(int)FNFLAGS]) {
426 					bi_errorf(Tf_s_s, "too early",
427 					    Tset_po);
428 					return (-1);
429 				}
430 #endif
431 				if (printoptions(set))
432 					return (-1);
433 				break;
434 			}
435 			i = option(go.optarg);
436 			if ((i == FPOSIX || i == FSH) && set && !fcompatseen) {
437 				/*
438 				 * If running 'set -o posix' or
439 				 * 'set -o sh', turn off the other;
440 				 * if running 'set -o posix -o sh'
441 				 * allow both to be set though.
442 				 */
443 				Flag(FPOSIX) = 0;
444 				Flag(FSH) = 0;
445 				fcompatseen = true;
446 			}
447 			if ((i != (size_t)-1) && (set ? 1U : 0U) == Flag(i))
448 				/*
449 				 * Don't check the context if the flag
450 				 * isn't changing - makes "set -o interactive"
451 				 * work if you're already interactive. Needed
452 				 * if the output of "set +o" is to be used.
453 				 */
454 				;
455 			else if ((i != (size_t)-1) && (OFF(i) & what))
456 				change_flag((enum sh_flag)i, what, set);
457 			else if (!strcmp(go.optarg, To_reset)) {
458 #if !defined(MKSH_SMALL) || defined(DEBUG)
459 				if (!baseline_flags[(int)FNFLAGS]) {
460 					bi_errorf(Tf_ss, "too early",
461 					    To_o_reset);
462 					return (-1);
463 				}
464 #endif
465 				/*
466 				 * ordering, with respect to side effects,
467 				 * was ensured above by printoptions
468 				 */
469 				for (i = 0; i < FNFLAGS; ++i)
470 					if (Flag(i) != baseline_flags[i])
471 						change_flag((enum sh_flag)i,
472 						    what, baseline_flags[i]);
473 			} else {
474 				bi_errorf(Tf_sD_s, go.optarg,
475 				    Tunknown_option);
476 				return (-1);
477 			}
478 			break;
479 
480 #ifdef KSH_CHVT_FLAG
481 		case 'T':
482 			if (what != OF_FIRSTTIME)
483 				break;
484 #ifndef KSH_CHVT_CODE
485 			errorf("no TIOCSCTTY ioctl");
486 #else
487 			change_flag(FTALKING, OF_CMDLINE, true);
488 			chvt(&go);
489 			break;
490 #endif
491 #endif
492 
493 		case '?':
494 			return (-1);
495 
496 		default:
497 			if (what == OF_FIRSTTIME)
498 				break;
499 			/* -s: sort positional params (AT&T ksh stupidity) */
500 			if (what == OF_SET && optc == 's') {
501 				sortargs = true;
502 				break;
503 			}
504 			for (i = 0; i < NELEM(options); i++)
505 				if (optc == OFC(i) &&
506 				    (what & OFF(i))) {
507 					change_flag((enum sh_flag)i, what, set);
508 					break;
509 				}
510 			if (i == NELEM(options))
511 				internal_errorf("parse_args: '%c'", optc);
512 		}
513 	}
514 	if (!(go.info & GI_MINUSMINUS) && argv[go.optind] &&
515 	    ctype(argv[go.optind][0], C_MINUS | C_PLUS) &&
516 	    argv[go.optind][1] == '\0') {
517 		/* lone - clears -v and -x flags */
518 		if (argv[go.optind][0] == '-') {
519 			Flag(FVERBOSE) = 0;
520 			change_xtrace(0, false);
521 		}
522 		/* set skips lone - or + option */
523 		go.optind++;
524 	}
525 	if (setargsp)
526 		/* -- means set $#/$* even if there are no arguments */
527 		*setargsp = !arrayset && ((go.info & GI_MINUSMINUS) ||
528 		    argv[go.optind]);
529 
530 	if (arrayset) {
531 		const char *ccp = NULL;
532 
533 		if (array && *array)
534 			ccp = skip_varname(array, false);
535 		if (!ccp || !(!ccp[0] || (ccp[0] == '+' && !ccp[1]))) {
536 			bi_errorf(Tf_sD_s, array, Tnot_ident);
537 			return (-1);
538 		}
539 	}
540 	if (sortargs) {
541 		for (i = go.optind; argv[i]; i++)
542 			;
543 		qsort(&argv[go.optind], i - go.optind, sizeof(void *),
544 		    ascpstrcmp);
545 	}
546 	if (arrayset)
547 		go.optind += set_array(array, tobool(arrayset > 0),
548 		    argv + go.optind);
549 
550 	return (go.optind);
551 }
552 
553 /* parse a decimal number: returns 0 if string isn't a number, 1 otherwise */
554 int
getn(const char * s,int * ai)555 getn(const char *s, int *ai)
556 {
557 	char c;
558 	mksh_ari_u num;
559 	bool neg = false;
560 
561 	num.u = 0;
562 
563 	do {
564 		c = *s++;
565 	} while (ctype(c, C_SPACE));
566 
567 	switch (c) {
568 	case '-':
569 		neg = true;
570 		/* FALLTHROUGH */
571 	case '+':
572 		c = *s++;
573 		break;
574 	}
575 
576 	do {
577 		if (!ctype(c, C_DIGIT))
578 			/* not numeric */
579 			return (0);
580 		if (num.u > 214748364U)
581 			/* overflow on multiplication */
582 			return (0);
583 		num.u = num.u * 10U + (unsigned int)ksh_numdig(c);
584 		/* now: num.u <= 2147483649U */
585 	} while ((c = *s++));
586 
587 	if (num.u > (neg ? 2147483648U : 2147483647U))
588 		/* overflow for signed 32-bit int */
589 		return (0);
590 
591 	if (neg)
592 		num.u = -num.u;
593 	*ai = num.i;
594 	return (1);
595 }
596 
597 /**
598  * pattern simplifications:
599  * - @(x) -> x (not @(x|y) though)
600  * - ** -> *
601  */
602 static void *
simplify_gmatch_pattern(const unsigned char * sp)603 simplify_gmatch_pattern(const unsigned char *sp)
604 {
605 	uint8_t c;
606 	unsigned char *cp, *dp;
607 	const unsigned char *ps, *se;
608 
609 	cp = alloc(strlen((const void *)sp) + 1, ATEMP);
610 	goto simplify_gmatch_pat1a;
611 
612 	/* foo@(b@(a)r)b@(a|a)z -> foobarb@(a|a)z */
613  simplify_gmatch_pat1:
614 	sp = cp;
615  simplify_gmatch_pat1a:
616 	dp = cp;
617 	se = strnul(sp);
618 	while ((c = *sp++)) {
619 		if (!ISMAGIC(c)) {
620 			*dp++ = c;
621 			continue;
622 		}
623 		switch ((c = *sp++)) {
624 		case 0x80|'@':
625 		/* simile for @ */
626 		case 0x80|' ':
627 			/* check whether it has only one clause */
628 			ps = pat_scan(sp, se, true);
629 			if (!ps || ps[-1] != /*(*/ ')')
630 				/* nope */
631 				break;
632 			/* copy inner clause until matching close */
633 			ps -= 2;
634 			while ((const unsigned char *)sp < ps)
635 				*dp++ = *sp++;
636 			/* skip MAGIC and closing parenthesis */
637 			sp += 2;
638 			/* copy the rest of the pattern */
639 			memmove(dp, sp, strlen((const void *)sp) + 1);
640 			/* redo from start */
641 			goto simplify_gmatch_pat1;
642 		}
643 		*dp++ = MAGIC;
644 		*dp++ = c;
645 	}
646 	*dp = '\0';
647 
648 	/* collapse adjacent asterisk wildcards */
649 	sp = dp = cp;
650 	while ((c = *sp++)) {
651 		if (!ISMAGIC(c)) {
652 			*dp++ = c;
653 			continue;
654 		}
655 		switch ((c = *sp++)) {
656 		case '*':
657 			while (ISMAGIC(sp[0]) && sp[1] == c)
658 				sp += 2;
659 			break;
660 		}
661 		*dp++ = MAGIC;
662 		*dp++ = c;
663 	}
664 	*dp = '\0';
665 
666 	/* return the result, allocated from ATEMP */
667 	return (cp);
668 }
669 
670 /* -------- gmatch.c -------- */
671 
672 /*
673  * int gmatch(string, pattern)
674  * char *string, *pattern;
675  *
676  * Match a pattern as in sh(1).
677  * pattern character are prefixed with MAGIC by expand.
678  */
679 int
gmatchx(const char * s,const char * p,bool isfile)680 gmatchx(const char *s, const char *p, bool isfile)
681 {
682 	const char *se, *pe;
683 	char *pnew;
684 	int rv;
685 
686 	if (s == NULL || p == NULL)
687 		return (0);
688 
689 	pe = strnul(p);
690 	/*
691 	 * isfile is false iff no syntax check has been done on
692 	 * the pattern. If check fails, just do a strcmp().
693 	 */
694 	if (!isfile && !has_globbing(p)) {
695 		size_t len = pe - p + 1;
696 		char tbuf[64];
697 		char *t = len <= sizeof(tbuf) ? tbuf : alloc(len, ATEMP);
698 		debunk(t, p, len);
699 		return (!strcmp(t, s));
700 	}
701 	se = strnul(s);
702 
703 	/*
704 	 * since the do_gmatch() engine sucks so much, we must do some
705 	 * pattern simplifications
706 	 */
707 	pnew = simplify_gmatch_pattern((const unsigned char *)p);
708 	pe = strnul(pnew);
709 
710 	rv = do_gmatch((const unsigned char *)s, (const unsigned char *)se,
711 	    (const unsigned char *)pnew, (const unsigned char *)pe,
712 	    (const unsigned char *)s);
713 	afree(pnew, ATEMP);
714 	return (rv);
715 }
716 
717 /**
718  * Returns if p is a syntacticly correct globbing pattern, false
719  * if it contains no pattern characters or if there is a syntax error.
720  * Syntax errors are:
721  *	- [ with no closing ]
722  *	- imbalanced $(...) expression
723  *	- [...] and *(...) not nested (eg, @(a[b|)c], *(a[b|c]d))
724  */
725 /*XXX
726  * - if no magic,
727  *	if dest given, copy to dst
728  *	return ?
729  * - if magic && (no globbing || syntax error)
730  *	debunk to dst
731  *	return ?
732  * - return ?
733  */
734 bool
has_globbing(const char * pat)735 has_globbing(const char *pat)
736 {
737 	unsigned char c, subc;
738 	bool saw_glob = false;
739 	unsigned int nest = 0;
740 	const unsigned char *p = (const unsigned char *)pat;
741 	const unsigned char *s;
742 
743 	while ((c = *p++)) {
744 		/* regular character? ok. */
745 		if (!ISMAGIC(c))
746 			continue;
747 		/* MAGIC + NUL? abort. */
748 		if (!(c = *p++))
749 			return (false);
750 		/* some specials */
751 		if (ord(c) == ORD('*') || ord(c) == ORD('?')) {
752 			/* easy glob, accept */
753 			saw_glob = true;
754 		} else if (ord(c) == ORD('[')) {
755 			/* bracket expression; eat negation and initial ] */
756 			if (ISMAGIC(p[0]) && ord(p[1]) == ORD('!'))
757 				p += 2;
758 			if (ISMAGIC(p[0]) && ord(p[1]) == ORD(']'))
759 				p += 2;
760 			/* check next string part */
761 			s = p;
762 			while ((c = *s++)) {
763 				/* regular chars are ok */
764 				if (!ISMAGIC(c))
765 					continue;
766 				/* MAGIC + NUL cannot happen */
767 				if (!(c = *s++))
768 					return (false);
769 				/* terminating bracket? */
770 				if (ord(c) == ORD(']')) {
771 					/* accept and continue */
772 					p = s;
773 					saw_glob = true;
774 					break;
775 				}
776 				/* sub-bracket expressions */
777 				if (ord(c) == ORD('[') && (
778 				    /* collating element? */
779 				    ord(*s) == ORD('.') ||
780 				    /* equivalence class? */
781 				    ord(*s) == ORD('=') ||
782 				    /* character class? */
783 				    ord(*s) == ORD(':'))) {
784 					/* must stop with exactly the same c */
785 					subc = *s++;
786 					/* arbitrarily many chars in betwixt */
787 					while ((c = *s++))
788 						/* but only this sequence... */
789 						if (c == subc && ISMAGIC(*s) &&
790 						    ord(s[1]) == ORD(']')) {
791 							/* accept, terminate */
792 							s += 2;
793 							break;
794 						}
795 					/* EOS without: reject bracket expr */
796 					if (!c)
797 						break;
798 					/* continue; */
799 				}
800 				/* anything else just goes on */
801 			}
802 		} else if ((c & 0x80) && ctype(c & 0x7F, C_PATMO | C_SPC)) {
803 			/* opening pattern */
804 			saw_glob = true;
805 			++nest;
806 		} else if (ord(c) == ORD(/*(*/ ')')) {
807 			/* closing pattern */
808 			if (nest)
809 				--nest;
810 		}
811 	}
812 	return (saw_glob && !nest);
813 }
814 
815 /* Function must return either 0 or 1 (assumed by code for 0x80|'!') */
816 static int
do_gmatch(const unsigned char * s,const unsigned char * se,const unsigned char * p,const unsigned char * pe,const unsigned char * smin)817 do_gmatch(const unsigned char *s, const unsigned char *se,
818     const unsigned char *p, const unsigned char *pe,
819     const unsigned char *smin)
820 {
821 	unsigned char sc, pc, sl = 0;
822 	const unsigned char *prest, *psub, *pnext;
823 	const unsigned char *srest;
824 
825 	if (s == NULL || p == NULL)
826 		return (0);
827 	if (s > smin && s <= se)
828 		sl = s[-1];
829 	while (p < pe) {
830 		pc = *p++;
831 		sc = s < se ? *s : '\0';
832 		s++;
833 		if (!ISMAGIC(pc)) {
834 			if (sc != pc)
835 				return (0);
836 			sl = sc;
837 			continue;
838 		}
839 		switch (ord(*p++)) {
840 		case ORD('['):
841 			/* BSD cclass extension? */
842 			if (ISMAGIC(p[0]) && ord(p[1]) == ORD('[') &&
843 			    ord(p[2]) == ORD(':') &&
844 			    ctype((pc = p[3]), C_ANGLE) &&
845 			    ord(p[4]) == ORD(':') &&
846 			    ISMAGIC(p[5]) && ord(p[6]) == ORD(']') &&
847 			    ISMAGIC(p[7]) && ord(p[8]) == ORD(']')) {
848 				/* zero-length match */
849 				--s;
850 				p += 9;
851 				/* word begin? */
852 				if (ord(pc) == ORD('<') &&
853 				    !ctype(sl, C_ALNUX) &&
854 				    ctype(sc, C_ALNUX))
855 					break;
856 				/* word end? */
857 				if (ord(pc) == ORD('>') &&
858 				    ctype(sl, C_ALNUX) &&
859 				    !ctype(sc, C_ALNUX))
860 					break;
861 				/* neither */
862 				return (0);
863 			}
864 			if (sc == 0 || (p = gmatch_cclass(p, sc)) == NULL)
865 				return (0);
866 			break;
867 
868 		case ORD('?'):
869 			if (sc == 0)
870 				return (0);
871 			if (UTFMODE) {
872 				--s;
873 				s += utf_ptradj((const void *)s);
874 			}
875 			break;
876 
877 		case ORD('*'):
878 			if (p == pe)
879 				return (1);
880 			s--;
881 			do {
882 				if (do_gmatch(s, se, p, pe, smin))
883 					return (1);
884 			} while (s++ < se);
885 			return (0);
886 
887 		/**
888 		 * [+*?@!](pattern|pattern|..)
889 		 * This is also needed for ${..%..}, etc.
890 		 */
891 
892 		/* matches one or more times */
893 		case ORD('+') | 0x80:
894 		/* matches zero or more times */
895 		case ORD('*') | 0x80:
896 			if (!(prest = pat_scan(p, pe, false)))
897 				return (0);
898 			s--;
899 			/* take care of zero matches */
900 			if (ord(p[-1]) == (0x80 | ORD('*')) &&
901 			    do_gmatch(s, se, prest, pe, smin))
902 				return (1);
903 			for (psub = p; ; psub = pnext) {
904 				pnext = pat_scan(psub, pe, true);
905 				for (srest = s; srest <= se; srest++) {
906 					if (do_gmatch(s, srest, psub, pnext - 2, smin) &&
907 					    (do_gmatch(srest, se, prest, pe, smin) ||
908 					    (s != srest &&
909 					    do_gmatch(srest, se, p - 2, pe, smin))))
910 						return (1);
911 				}
912 				if (pnext == prest)
913 					break;
914 			}
915 			return (0);
916 
917 		/* matches zero or once */
918 		case ORD('?') | 0x80:
919 		/* matches one of the patterns */
920 		case ORD('@') | 0x80:
921 		/* simile for @ */
922 		case ORD(' ') | 0x80:
923 			if (!(prest = pat_scan(p, pe, false)))
924 				return (0);
925 			s--;
926 			/* Take care of zero matches */
927 			if (ord(p[-1]) == (0x80 | ORD('?')) &&
928 			    do_gmatch(s, se, prest, pe, smin))
929 				return (1);
930 			for (psub = p; ; psub = pnext) {
931 				pnext = pat_scan(psub, pe, true);
932 				srest = prest == pe ? se : s;
933 				for (; srest <= se; srest++) {
934 					if (do_gmatch(s, srest, psub, pnext - 2, smin) &&
935 					    do_gmatch(srest, se, prest, pe, smin))
936 						return (1);
937 				}
938 				if (pnext == prest)
939 					break;
940 			}
941 			return (0);
942 
943 		/* matches none of the patterns */
944 		case ORD('!') | 0x80:
945 			if (!(prest = pat_scan(p, pe, false)))
946 				return (0);
947 			s--;
948 			for (srest = s; srest <= se; srest++) {
949 				int matched = 0;
950 
951 				for (psub = p; ; psub = pnext) {
952 					pnext = pat_scan(psub, pe, true);
953 					if (do_gmatch(s, srest, psub,
954 					    pnext - 2, smin)) {
955 						matched = 1;
956 						break;
957 					}
958 					if (pnext == prest)
959 						break;
960 				}
961 				if (!matched &&
962 				    do_gmatch(srest, se, prest, pe, smin))
963 					return (1);
964 			}
965 			return (0);
966 
967 		default:
968 			if (sc != p[-1])
969 				return (0);
970 			break;
971 		}
972 		sl = sc;
973 	}
974 	return (s == se);
975 }
976 
977 /*XXX this is a prime example for bsearch or a const hashtable */
978 static const struct cclass {
979 	const char *name;
980 	uint32_t value;
981 } cclasses[] = {
982 	/* POSIX */
983 	{ "alnum",	C_ALNUM	},
984 	{ "alpha",	C_ALPHA	},
985 	{ "blank",	C_BLANK	},
986 	{ "cntrl",	C_CNTRL	},
987 	{ "digit",	C_DIGIT	},
988 	{ "graph",	C_GRAPH	},
989 	{ "lower",	C_LOWER	},
990 	{ "print",	C_PRINT	},
991 	{ "punct",	C_PUNCT	},
992 	{ "space",	C_SPACE	},
993 	{ "upper",	C_UPPER	},
994 	{ "xdigit",	C_SEDEC	},
995 	/* BSD */
996 	/* "<" and ">" are handled inline */
997 	/* GNU bash */
998 	{ "ascii",	C_ASCII	},
999 	{ "word",	C_ALNUX	},
1000 	/* mksh */
1001 	{ "sh_alias",	C_ALIAS	},
1002 	{ "sh_edq",	C_EDQ	},
1003 	{ "sh_ifs",	C_IFS	},
1004 	{ "sh_ifsws",	C_IFSWS	},
1005 	{ "sh_nl",	C_NL	},
1006 	{ "sh_quote",	C_QUOTE	},
1007 	/* sentinel */
1008 	{ NULL,		0	}
1009 };
1010 
1011 static const unsigned char *
gmatch_cclass(const unsigned char * pat,unsigned char sc)1012 gmatch_cclass(const unsigned char *pat, unsigned char sc)
1013 {
1014 	unsigned char c, subc, lc;
1015 	const unsigned char *p = pat, *s;
1016 	bool found = false;
1017 	bool negated = false;
1018 	char *subp;
1019 
1020 	/* check for negation */
1021 	if (ISMAGIC(p[0]) && ord(p[1]) == ORD('!')) {
1022 		p += 2;
1023 		negated = true;
1024 	}
1025 	/* make initial ] non-MAGIC */
1026 	if (ISMAGIC(p[0]) && ord(p[1]) == ORD(']'))
1027 		++p;
1028 	/* iterate over bracket expression, debunk()ing on the fly */
1029 	while ((c = *p++)) {
1030  nextc:
1031 		/* non-regular character? */
1032 		if (ISMAGIC(c)) {
1033 			/* MAGIC + NUL cannot happen */
1034 			if (!(c = *p++))
1035 				break;
1036 			/* terminating bracket? */
1037 			if (ord(c) == ORD(']')) {
1038 				/* accept and return */
1039 				return (found != negated ? p : NULL);
1040 			}
1041 			/* sub-bracket expressions */
1042 			if (ord(c) == ORD('[') && (
1043 			    /* collating element? */
1044 			    ord(*p) == ORD('.') ||
1045 			    /* equivalence class? */
1046 			    ord(*p) == ORD('=') ||
1047 			    /* character class? */
1048 			    ord(*p) == ORD(':'))) {
1049 				/* must stop with exactly the same c */
1050 				subc = *p++;
1051 				/* save away start of substring */
1052 				s = p;
1053 				/* arbitrarily many chars in betwixt */
1054 				while ((c = *p++))
1055 					/* but only this sequence... */
1056 					if (c == subc && ISMAGIC(*p) &&
1057 					    ord(p[1]) == ORD(']')) {
1058 						/* accept, terminate */
1059 						p += 2;
1060 						break;
1061 					}
1062 				/* EOS without: reject bracket expr */
1063 				if (!c)
1064 					break;
1065 				/* debunk substring */
1066 				strndupx(subp, s, p - s - 3, ATEMP);
1067 				debunk(subp, subp, p - s - 3 + 1);
1068  cclass_common:
1069 				/* whither subexpression */
1070 				if (ord(subc) == ORD(':')) {
1071 					const struct cclass *cls = cclasses;
1072 
1073 					/* search for name in cclass list */
1074 					while (cls->name)
1075 						if (!strcmp(subp, cls->name)) {
1076 							/* found, match? */
1077 							if (ctype(sc,
1078 							    cls->value))
1079 								found = true;
1080 							/* break either way */
1081 							break;
1082 						} else
1083 							++cls;
1084 					/* that's all here */
1085 					afree(subp, ATEMP);
1086 					continue;
1087 				}
1088 				/* collating element or equivalence class */
1089 				/* Note: latter are treated as former */
1090 				if (ctype(subp[0], C_ASCII) && !subp[1])
1091 					/* [.a.] where a is one ASCII char */
1092 					c = subp[0];
1093 				else
1094 					/* force no match */
1095 					c = 0;
1096 				/* no longer needed */
1097 				afree(subp, ATEMP);
1098 			} else if (!ISMAGIC(c) && (c & 0x80)) {
1099 				/* 0x80|' ' is plain (...) */
1100 				if ((c &= 0x7F) != ' ') {
1101 					/* check single match NOW */
1102 					if (sc == c)
1103 						found = true;
1104 					/* next character is (...) */
1105 				}
1106 				c = '(' /*)*/;
1107 			}
1108 		}
1109 		/* range expression? */
1110 		if (!(ISMAGIC(p[0]) && ord(p[1]) == ORD('-') &&
1111 		    /* not terminating bracket? */
1112 		    (!ISMAGIC(p[2]) || ord(p[3]) != ORD(']')))) {
1113 			/* no, check single match */
1114 			if (sc == c)
1115 				/* note: sc is never NUL */
1116 				found = true;
1117 			/* do the next "first" character */
1118 			continue;
1119 		}
1120 		/* save lower range bound */
1121 		lc = c;
1122 		/* skip over the range operator */
1123 		p += 2;
1124 		/* do the same shit as above... almost */
1125 		subc = 0;
1126 		if (!(c = *p++))
1127 			break;
1128 		/* non-regular character? */
1129 		if (ISMAGIC(c)) {
1130 			/* MAGIC + NUL cannot happen */
1131 			if (!(c = *p++))
1132 				break;
1133 			/* sub-bracket expressions */
1134 			if (ord(c) == ORD('[') && (
1135 			    /* collating element? */
1136 			    ord(*p) == ORD('.') ||
1137 			    /* equivalence class? */
1138 			    ord(*p) == ORD('=') ||
1139 			    /* character class? */
1140 			    ord(*p) == ORD(':'))) {
1141 				/* must stop with exactly the same c */
1142 				subc = *p++;
1143 				/* save away start of substring */
1144 				s = p;
1145 				/* arbitrarily many chars in betwixt */
1146 				while ((c = *p++))
1147 					/* but only this sequence... */
1148 					if (c == subc && ISMAGIC(*p) &&
1149 					    ord(p[1]) == ORD(']')) {
1150 						/* accept, terminate */
1151 						p += 2;
1152 						break;
1153 					}
1154 				/* EOS without: reject bracket expr */
1155 				if (!c)
1156 					break;
1157 				/* debunk substring */
1158 				strndupx(subp, s, p - s - 3, ATEMP);
1159 				debunk(subp, subp, p - s - 3 + 1);
1160 				/* whither subexpression */
1161 				if (ord(subc) == ORD(':')) {
1162 					/* oops, not a range */
1163 
1164 					/* match single previous char */
1165 					if (lc && (sc == lc))
1166 						found = true;
1167 					/* match hyphen-minus */
1168 					if (ord(sc) == ORD('-'))
1169 						found = true;
1170 					/* handle cclass common part */
1171 					goto cclass_common;
1172 				}
1173 				/* collating element or equivalence class */
1174 				/* Note: latter are treated as former */
1175 				if (ctype(subp[0], C_ASCII) && !subp[1])
1176 					/* [.a.] where a is one ASCII char */
1177 					c = subp[0];
1178 				else
1179 					/* force no match */
1180 					c = 0;
1181 				/* no longer needed */
1182 				afree(subp, ATEMP);
1183 				/* other meaning below */
1184 				subc = 0;
1185 			} else if (c == (0x80 | ' ')) {
1186 				/* 0x80|' ' is plain (...) */
1187 				c = '(' /*)*/;
1188 			} else if (!ISMAGIC(c) && (c & 0x80)) {
1189 				c &= 0x7F;
1190 				subc = '(' /*)*/;
1191 			}
1192 		}
1193 		/* now do the actual range match check */
1194 		if (lc != 0 /* && c != 0 */ &&
1195 		    asciibetical(lc) <= asciibetical(sc) &&
1196 		    asciibetical(sc) <= asciibetical(c))
1197 			found = true;
1198 		/* forced next character? */
1199 		if (subc) {
1200 			c = subc;
1201 			goto nextc;
1202 		}
1203 		/* otherwise, just go on with the pattern string */
1204 	}
1205 	/* if we broke here, the bracket expression was invalid */
1206 	if (ord(sc) == ORD('['))
1207 		/* initial opening bracket as literal match */
1208 		return (pat);
1209 	/* or rather no match */
1210 	return (NULL);
1211 }
1212 
1213 /* Look for next ) or | (if match_sep) in *(foo|bar) pattern */
1214 static const unsigned char *
pat_scan(const unsigned char * p,const unsigned char * pe,bool match_sep)1215 pat_scan(const unsigned char *p, const unsigned char *pe, bool match_sep)
1216 {
1217 	int nest = 0;
1218 
1219 	for (; p < pe; p++) {
1220 		if (!ISMAGIC(*p))
1221 			continue;
1222 		if ((*++p == /*(*/ ')' && nest-- == 0) ||
1223 		    (*p == '|' && match_sep && nest == 0))
1224 			return (p + 1);
1225 		if ((*p & 0x80) && ctype(*p & 0x7F, C_PATMO | C_SPC))
1226 			nest++;
1227 	}
1228 	return (NULL);
1229 }
1230 
1231 int
ascstrcmp(const void * s1,const void * s2)1232 ascstrcmp(const void *s1, const void *s2)
1233 {
1234 	const uint8_t *cp1 = s1, *cp2 = s2;
1235 
1236 	while (*cp1 == *cp2) {
1237 		if (*cp1++ == '\0')
1238 			return (0);
1239 		++cp2;
1240 	}
1241 	return ((int)asciibetical(*cp1) - (int)asciibetical(*cp2));
1242 }
1243 
1244 int
ascpstrcmp(const void * pstr1,const void * pstr2)1245 ascpstrcmp(const void *pstr1, const void *pstr2)
1246 {
1247 	return (ascstrcmp(*(const char * const *)pstr1,
1248 	    *(const char * const *)pstr2));
1249 }
1250 
1251 /* Initialise a Getopt structure */
1252 void
ksh_getopt_reset(Getopt * go,int flags)1253 ksh_getopt_reset(Getopt *go, int flags)
1254 {
1255 	go->optind = 1;
1256 	go->optarg = NULL;
1257 	go->p = 0;
1258 	go->flags = flags;
1259 	go->info = 0;
1260 	go->buf[1] = '\0';
1261 }
1262 
1263 
1264 /**
1265  * getopt() used for shell built-in commands, the getopts command, and
1266  * command line options.
1267  * A leading ':' in options means don't print errors, instead return '?'
1268  * or ':' and set go->optarg to the offending option character.
1269  * If GF_ERROR is set (and option doesn't start with :), errors result in
1270  * a call to bi_errorf().
1271  *
1272  * Non-standard features:
1273  *	- ';' is like ':' in options, except the argument is optional
1274  *	  (if it isn't present, optarg is set to 0).
1275  *	  Used for 'set -o'.
1276  *	- ',' is like ':' in options, except the argument always immediately
1277  *	  follows the option character (optarg is set to the null string if
1278  *	  the option is missing).
1279  *	  Used for 'read -u2', 'print -u2' and fc -40.
1280  *	- '#' is like ':' in options, expect that the argument is optional
1281  *	  and must start with a digit. If the argument doesn't start with a
1282  *	  digit, it is assumed to be missing and normal option processing
1283  *	  continues (optarg is set to 0 if the option is missing).
1284  *	  Used for 'typeset -LZ4'.
1285  *	- accepts +c as well as -c IF the GF_PLUSOPT flag is present. If an
1286  *	  option starting with + is accepted, the GI_PLUS flag will be set
1287  *	  in go->info.
1288  */
1289 int
ksh_getopt(const char ** argv,Getopt * go,const char * optionsp)1290 ksh_getopt(const char **argv, Getopt *go, const char *optionsp)
1291 {
1292 	char c;
1293 	const char *o;
1294 
1295 	if (go->p == 0 || (c = argv[go->optind - 1][go->p]) == '\0') {
1296 		const char *arg = argv[go->optind], flag = arg ? *arg : '\0';
1297 
1298 		go->p = 1;
1299 		if (flag == '-' && ksh_isdash(arg + 1)) {
1300 			go->optind++;
1301 			go->p = 0;
1302 			go->info |= GI_MINUSMINUS;
1303 			return (-1);
1304 		}
1305 		if (arg == NULL ||
1306 		    ((flag != '-' ) &&
1307 		    /* neither a - nor a + (if + allowed) */
1308 		    (!(go->flags & GF_PLUSOPT) || flag != '+')) ||
1309 		    (c = arg[1]) == '\0') {
1310 			go->p = 0;
1311 			return (-1);
1312 		}
1313 		go->optind++;
1314 		go->info &= ~(GI_MINUS|GI_PLUS);
1315 		go->info |= flag == '-' ? GI_MINUS : GI_PLUS;
1316 	}
1317 	go->p++;
1318 	if (ctype(c, C_QUEST | C_COLON | C_HASH) || c == ';' || c == ',' ||
1319 	    !(o = cstrchr(optionsp, c))) {
1320 		if (optionsp[0] == ':') {
1321 			go->buf[0] = c;
1322 			go->optarg = go->buf;
1323 		} else {
1324 			warningf(true, Tf_optfoo,
1325 			    (go->flags & GF_NONAME) ? "" : argv[0],
1326 			    (go->flags & GF_NONAME) ? "" : Tcolsp,
1327 			    c, Tunknown_option);
1328 			if (go->flags & GF_ERROR)
1329 				bi_errorfz();
1330 		}
1331 		return (ORD('?'));
1332 	}
1333 	/**
1334 	 * : means argument must be present, may be part of option argument
1335 	 *   or the next argument
1336 	 * ; same as : but argument may be missing
1337 	 * , means argument is part of option argument, and may be null.
1338 	 */
1339 	if (*++o == ':' || *o == ';') {
1340 		if (argv[go->optind - 1][go->p])
1341 			go->optarg = argv[go->optind - 1] + go->p;
1342 		else if (argv[go->optind])
1343 			go->optarg = argv[go->optind++];
1344 		else if (*o == ';')
1345 			go->optarg = NULL;
1346 		else {
1347 			if (optionsp[0] == ':') {
1348 				go->buf[0] = c;
1349 				go->optarg = go->buf;
1350 				return (ORD(':'));
1351 			}
1352 			warningf(true, Tf_optfoo,
1353 			    (go->flags & GF_NONAME) ? "" : argv[0],
1354 			    (go->flags & GF_NONAME) ? "" : Tcolsp,
1355 			    c, Treq_arg);
1356 			if (go->flags & GF_ERROR)
1357 				bi_errorfz();
1358 			return (ORD('?'));
1359 		}
1360 		go->p = 0;
1361 	} else if (*o == ',') {
1362 		/* argument is attached to option character, even if null */
1363 		go->optarg = argv[go->optind - 1] + go->p;
1364 		go->p = 0;
1365 	} else if (*o == '#') {
1366 		/*
1367 		 * argument is optional and may be attached or unattached
1368 		 * but must start with a digit. optarg is set to 0 if the
1369 		 * argument is missing.
1370 		 */
1371 		if (argv[go->optind - 1][go->p]) {
1372 			if (ctype(argv[go->optind - 1][go->p], C_DIGIT)) {
1373 				go->optarg = argv[go->optind - 1] + go->p;
1374 				go->p = 0;
1375 			} else
1376 				go->optarg = NULL;
1377 		} else {
1378 			if (argv[go->optind] &&
1379 			    ctype(argv[go->optind][0], C_DIGIT)) {
1380 				go->optarg = argv[go->optind++];
1381 				go->p = 0;
1382 			} else
1383 				go->optarg = NULL;
1384 		}
1385 	}
1386 	return (ord(c));
1387 }
1388 
1389 /*
1390  * print variable/alias value using necessary quotes
1391  * (POSIX says they should be suitable for re-entry...)
1392  * No trailing newline is printed.
1393  */
1394 void
print_value_quoted(struct shf * shf,const char * s)1395 print_value_quoted(struct shf *shf, const char *s)
1396 {
1397 	unsigned char c;
1398 	const unsigned char *p = (const unsigned char *)s;
1399 	bool inquote = true;
1400 
1401 	/* first, special-case empty strings (for re-entrancy) */
1402 	if (!*s) {
1403 		shf_putc('\'', shf);
1404 		shf_putc('\'', shf);
1405 		return;
1406 	}
1407 
1408 	/* non-empty; check whether any quotes are needed */
1409 	while (rtt2asc(c = *p++) >= 32)
1410 		if (ctype(c, C_QUOTE | C_SPC))
1411 			inquote = false;
1412 
1413 	p = (const unsigned char *)s;
1414 	if (c == 0) {
1415 		if (inquote) {
1416 			/* nope, use the shortcut */
1417 			shf_puts(s, shf);
1418 			return;
1419 		}
1420 
1421 		/* otherwise, quote nicely via state machine */
1422 		while ((c = *p++) != 0) {
1423 			if (c == '\'') {
1424 				/*
1425 				 * multiple single quotes or any of them
1426 				 * at the beginning of a string look nicer
1427 				 * this way than when simply substituting
1428 				 */
1429 				if (inquote) {
1430 					shf_putc('\'', shf);
1431 					inquote = false;
1432 				}
1433 				shf_putc('\\', shf);
1434 			} else if (!inquote) {
1435 				shf_putc('\'', shf);
1436 				inquote = true;
1437 			}
1438 			shf_putc(c, shf);
1439 		}
1440 	} else {
1441 		unsigned int wc;
1442 		size_t n;
1443 
1444 		/* use $'...' quote format */
1445 		shf_putc('$', shf);
1446 		shf_putc('\'', shf);
1447 		while ((c = *p) != 0) {
1448 #ifndef MKSH_EBCDIC
1449 			if (c >= 0xC2) {
1450 				n = utf_mbtowc(&wc, (const char *)p);
1451 				if (n != (size_t)-1) {
1452 					p += n;
1453 					shf_fprintf(shf, "\\u%04X", wc);
1454 					continue;
1455 				}
1456 			}
1457 #endif
1458 			++p;
1459 			switch (c) {
1460 			/* see unbksl() in this file for comments */
1461 			case KSH_BEL:
1462 				c = 'a';
1463 				if (0)
1464 					/* FALLTHROUGH */
1465 			case '\b':
1466 				  c = 'b';
1467 				if (0)
1468 					/* FALLTHROUGH */
1469 			case '\f':
1470 				  c = 'f';
1471 				if (0)
1472 					/* FALLTHROUGH */
1473 			case '\n':
1474 				  c = 'n';
1475 				if (0)
1476 					/* FALLTHROUGH */
1477 			case '\r':
1478 				  c = 'r';
1479 				if (0)
1480 					/* FALLTHROUGH */
1481 			case '\t':
1482 				  c = 't';
1483 				if (0)
1484 					/* FALLTHROUGH */
1485 			case KSH_VTAB:
1486 				  c = 'v';
1487 				if (0)
1488 					/* FALLTHROUGH */
1489 			case KSH_ESC:
1490 				/* take E not e because \e is \ in *roff */
1491 				  c = 'E';
1492 				/* FALLTHROUGH */
1493 			case '\\':
1494 				shf_putc('\\', shf);
1495 
1496 				if (0)
1497 					/* FALLTHROUGH */
1498 			default:
1499 #if defined(MKSH_EBCDIC) || defined(MKSH_FAUX_EBCDIC)
1500 				  if (ksh_isctrl(c))
1501 #else
1502 				  if (!ctype(c, C_PRINT))
1503 #endif
1504 				    {
1505 					/* FALLTHROUGH */
1506 			case '\'':
1507 					shf_fprintf(shf, "\\%03o", c);
1508 					break;
1509 				}
1510 
1511 				shf_putc(c, shf);
1512 				break;
1513 			}
1514 		}
1515 		inquote = true;
1516 	}
1517 	if (inquote)
1518 		shf_putc('\'', shf);
1519 }
1520 
1521 /*
1522  * Print things in columns and rows - func() is called to format
1523  * the i-th element
1524  */
1525 void
print_columns(struct columnise_opts * opts,unsigned int n,void (* func)(char *,size_t,unsigned int,const void *),const void * arg,size_t max_oct,size_t max_colz)1526 print_columns(struct columnise_opts *opts, unsigned int n,
1527     void (*func)(char *, size_t, unsigned int, const void *),
1528     const void *arg, size_t max_oct, size_t max_colz)
1529 {
1530 	unsigned int i, r = 0, c, rows, cols, nspace, max_col;
1531 	char *str;
1532 
1533 	if (!n)
1534 		return;
1535 
1536 	if (max_colz > 2147483646) {
1537 #ifndef MKSH_SMALL
1538 		internal_warningf("print_columns called with %s=%zu >= INT_MAX",
1539 		    "max_col", max_colz);
1540 #endif
1541 		return;
1542 	}
1543 	max_col = (unsigned int)max_colz;
1544 
1545 	if (max_oct > 2147483646) {
1546 #ifndef MKSH_SMALL
1547 		internal_warningf("print_columns called with %s=%zu >= INT_MAX",
1548 		    "max_oct", max_oct);
1549 #endif
1550 		return;
1551 	}
1552 	++max_oct;
1553 	str = alloc(max_oct, ATEMP);
1554 
1555 	/*
1556 	 * We use (max_col + 2) to consider the separator space.
1557 	 * Note that no spaces are printed after the last column
1558 	 * to avoid problems with terminals that have auto-wrap,
1559 	 * but we need to also take this into account in x_cols.
1560 	 */
1561 	cols = (x_cols + 1) / (max_col + 2);
1562 
1563 	/* if we can only print one column anyway, skip the goo */
1564 	if (cols < 2) {
1565 		goto prcols_easy;
1566 		while (r < n) {
1567 			shf_putc(opts->linesep, opts->shf);
1568  prcols_easy:
1569 			(*func)(str, max_oct, r++, arg);
1570 			shf_puts(str, opts->shf);
1571 		}
1572 		goto out;
1573 	}
1574 
1575 	rows = (n + cols - 1) / cols;
1576 	if (opts->prefcol && cols > rows) {
1577 		cols = rows;
1578 		rows = (n + cols - 1) / cols;
1579 	}
1580 
1581 	nspace = (x_cols - max_col * cols) / cols;
1582 	if (nspace < 2)
1583 		nspace = 2;
1584 	max_col = -max_col;
1585 	goto prcols_hard;
1586 	while (r < rows) {
1587 		shf_putchar(opts->linesep, opts->shf);
1588  prcols_hard:
1589 		for (c = 0; c < cols; c++) {
1590 			if ((i = c * rows + r) >= n)
1591 				break;
1592 			(*func)(str, max_oct, i, arg);
1593 			if (i + rows >= n)
1594 				shf_puts(str, opts->shf);
1595 			else
1596 				shf_fprintf(opts->shf, "%*s%*s",
1597 				    (int)max_col, str, (int)nspace, null);
1598 		}
1599 		++r;
1600 	}
1601  out:
1602 	if (opts->do_last)
1603 		shf_putchar(opts->linesep, opts->shf);
1604 	afree(str, ATEMP);
1605 }
1606 
1607 /* strip all NUL bytes from buf; output is NUL-terminated if stripped */
1608 void
strip_nuls(char * buf,size_t len)1609 strip_nuls(char *buf, size_t len)
1610 {
1611 	char *cp, *dp, *ep;
1612 
1613 	if (!len || !(dp = memchr(buf, '\0', len)))
1614 		return;
1615 
1616 	ep = buf + len;
1617 	cp = dp;
1618 
1619  cp_has_nul_byte:
1620 	while (cp++ < ep && *cp == '\0')
1621 		;	/* nothing */
1622 	while (cp < ep && *cp != '\0')
1623 		*dp++ = *cp++;
1624 	if (cp < ep)
1625 		goto cp_has_nul_byte;
1626 
1627 	*dp = '\0';
1628 }
1629 
1630 /*
1631  * Like read(2), but if read fails due to non-blocking flag,
1632  * resets flag and restarts read.
1633  */
1634 ssize_t
blocking_read(int fd,char * buf,size_t nbytes)1635 blocking_read(int fd, char *buf, size_t nbytes)
1636 {
1637 	ssize_t ret;
1638 	bool tried_reset = false;
1639 
1640 	while ((ret = read(fd, buf, nbytes)) < 0) {
1641 		if (!tried_reset && errno == EAGAIN) {
1642 			if (reset_nonblock(fd) > 0) {
1643 				tried_reset = true;
1644 				continue;
1645 			}
1646 			errno = EAGAIN;
1647 		}
1648 		break;
1649 	}
1650 	return (ret);
1651 }
1652 
1653 /*
1654  * Reset the non-blocking flag on the specified file descriptor.
1655  * Returns -1 if there was an error, 0 if non-blocking wasn't set,
1656  * 1 if it was.
1657  */
1658 int
reset_nonblock(int fd)1659 reset_nonblock(int fd)
1660 {
1661 	int flags;
1662 
1663 	if ((flags = fcntl(fd, F_GETFL, 0)) < 0)
1664 		return (-1);
1665 	if (!(flags & O_NONBLOCK))
1666 		return (0);
1667 	flags &= ~O_NONBLOCK;
1668 	if (fcntl(fd, F_SETFL, flags) < 0)
1669 		return (-1);
1670 	return (1);
1671 }
1672 
1673 /* getcwd(3) equivalent, allocates from ATEMP but doesn't resize */
1674 char *
ksh_get_wd(void)1675 ksh_get_wd(void)
1676 {
1677 #ifdef MKSH__NO_PATH_MAX
1678 	char *rv, *cp;
1679 
1680 	if ((cp = get_current_dir_name())) {
1681 		strdupx(rv, cp, ATEMP);
1682 		free_gnu_gcdn(cp);
1683 	} else
1684 		rv = NULL;
1685 #else
1686 	char *rv;
1687 
1688 	if (!getcwd((rv = alloc(PATH_MAX + 1, ATEMP)), PATH_MAX)) {
1689 		afree(rv, ATEMP);
1690 		rv = NULL;
1691 	}
1692 #endif
1693 
1694 	return (rv);
1695 }
1696 
1697 #ifndef ELOOP
1698 #define ELOOP		E2BIG
1699 #endif
1700 
1701 char *
do_realpath(const char * upath)1702 do_realpath(const char *upath)
1703 {
1704 	char *xp, *ip, *tp, *ipath, *ldest = NULL;
1705 	XString xs;
1706 	size_t pos, len;
1707 	int llen;
1708 	struct stat sb;
1709 #ifdef MKSH__NO_PATH_MAX
1710 	size_t ldestlen = 0;
1711 #define pathlen sb.st_size
1712 #define pathcnd (ldestlen < (pathlen + 1))
1713 #else
1714 #define pathlen PATH_MAX
1715 #define pathcnd (!ldest)
1716 #endif
1717 	/* max. recursion depth */
1718 	int symlinks = 32;
1719 
1720 	if (mksh_abspath(upath)) {
1721 		/* upath is an absolute pathname */
1722 		strdupx(ipath, upath, ATEMP);
1723 #ifdef MKSH_DOSPATH
1724 	} else if (mksh_drvltr(upath)) {
1725 		/* upath is a drive-relative pathname */
1726 		if (getdrvwd(&ldest, ord(*upath)))
1727 			return (NULL);
1728 		/* A:foo -> A:/cwd/foo; A: -> A:/cwd */
1729 		strpathx(ipath, ldest, upath + 2, 0);
1730 #endif
1731 	} else {
1732 		/* upath is a relative pathname, prepend cwd */
1733 		if ((tp = ksh_get_wd()) == NULL || !mksh_abspath(tp))
1734 			return (NULL);
1735 		strpathx(ipath, tp, upath, 1);
1736 		afree(tp, ATEMP);
1737 	}
1738 
1739 	/* ipath and upath are in memory at the same time -> unchecked */
1740 	Xinit(xs, xp, strlen(ip = ipath) + 1, ATEMP);
1741 
1742 	/* now jump into the deep of the loop */
1743 	goto beginning_of_a_pathname;
1744 
1745 	while (*ip) {
1746 		/* skip slashes in input */
1747 		while (mksh_cdirsep(*ip))
1748 			++ip;
1749 		if (!*ip)
1750 			break;
1751 
1752 		/* get next pathname component from input */
1753 		tp = ip;
1754 		while (*ip && !mksh_cdirsep(*ip))
1755 			++ip;
1756 		len = ip - tp;
1757 
1758 		/* check input for "." and ".." */
1759 		if (tp[0] == '.') {
1760 			if (len == 1)
1761 				/* just continue with the next one */
1762 				continue;
1763 			else if (len == 2 && tp[1] == '.') {
1764 				/* strip off last pathname component */
1765 				/*XXX consider a rooted pathname */
1766 				while (xp > Xstring(xs, xp))
1767 					if (mksh_cdirsep(*--xp))
1768 						break;
1769 				/* then continue with the next one */
1770 				continue;
1771 			}
1772 		}
1773 
1774 		/* store output position away, then append slash to output */
1775 		pos = Xsavepos(xs, xp);
1776 		/* 1 for the '/' and len + 1 for tp and the NUL from below */
1777 		XcheckN(xs, xp, 1 + len + 1);
1778 		Xput(xs, xp, '/');
1779 
1780 		/* append next pathname component to output */
1781 		memcpy(xp, tp, len);
1782 		xp += len;
1783 		*xp = '\0';
1784 
1785 		/* lstat the current output, see if it's a symlink */
1786 		if (mksh_lstat(Xstring(xs, xp), &sb)) {
1787 			/* lstat failed */
1788 			if (errno == ENOENT) {
1789 				/* because the pathname does not exist */
1790 				while (mksh_cdirsep(*ip))
1791 					/* skip any trailing slashes */
1792 					++ip;
1793 				/* no more components left? */
1794 				if (!*ip)
1795 					/* we can still return successfully */
1796 					break;
1797 				/* more components left? fall through */
1798 			}
1799 			/* not ENOENT or not at the end of ipath */
1800 			goto notfound;
1801 		}
1802 
1803 		/* check if we encountered a symlink? */
1804 		if (S_ISLNK(sb.st_mode)) {
1805 #ifndef MKSH__NO_SYMLINK
1806 			/* reached maximum recursion depth? */
1807 			if (!symlinks--) {
1808 				/* yep, prevent infinite loops */
1809 				errno = ELOOP;
1810 				goto notfound;
1811 			}
1812 
1813 			/* get symlink(7) target */
1814 			if (pathcnd) {
1815 #ifdef MKSH__NO_PATH_MAX
1816 				if (notoktoadd(pathlen, 1)) {
1817 					errno = ENAMETOOLONG;
1818 					goto notfound;
1819 				}
1820 #endif
1821 				ldest = aresize(ldest, pathlen + 1, ATEMP);
1822 			}
1823 			llen = readlink(Xstring(xs, xp), ldest, pathlen);
1824 			if (llen < 0)
1825 				/* oops... */
1826 				goto notfound;
1827 			ldest[llen] = '\0';
1828 
1829 			/*
1830 			 * restart if symlink target is an absolute path,
1831 			 * otherwise continue with currently resolved prefix
1832 			 */
1833 #ifdef MKSH_DOSPATH
1834  assemble_symlink:
1835 #endif
1836 			/* append rest of current input path to link target */
1837 			strpathx(tp, ldest, ip, 0);
1838 			afree(ipath, ATEMP);
1839 			ip = ipath = tp;
1840 			if (!mksh_abspath(ipath)) {
1841 #ifdef MKSH_DOSPATH
1842 				/* symlink target might be drive-relative */
1843 				if (mksh_drvltr(ipath)) {
1844 					if (getdrvwd(&ldest, ord(*ipath)))
1845 						goto notfound;
1846 					ip += 2;
1847 					goto assemble_symlink;
1848 				}
1849 #endif
1850 				/* symlink target is a relative path */
1851 				xp = Xrestpos(xs, xp, pos);
1852 			} else
1853 #endif
1854 			  {
1855 				/* symlink target is an absolute path */
1856 				xp = Xstring(xs, xp);
1857  beginning_of_a_pathname:
1858 				/* assert: mksh_abspath(ip == ipath) */
1859 				/* assert: xp == xs.beg => start of path */
1860 
1861 				/* exactly two leading slashes? (SUSv4 3.266) */
1862 				if (ip[1] == ip[0] && !mksh_cdirsep(ip[2])) {
1863 					/* keep them, e.g. for UNC pathnames */
1864 					Xput(xs, xp, '/');
1865 				}
1866 #ifdef MKSH_DOSPATH
1867 				/* drive letter? */
1868 				if (mksh_drvltr(ip)) {
1869 					/* keep it */
1870 					Xput(xs, xp, *ip++);
1871 					Xput(xs, xp, *ip++);
1872 				}
1873 #endif
1874 			}
1875 		}
1876 		/* otherwise (no symlink) merely go on */
1877 	}
1878 
1879 	/*
1880 	 * either found the target and successfully resolved it,
1881 	 * or found its parent directory and may create it
1882 	 */
1883 	if (Xlength(xs, xp) == 0)
1884 		/*
1885 		 * if the resolved pathname is "", make it "/",
1886 		 * otherwise do not add a trailing slash
1887 		 */
1888 		Xput(xs, xp, '/');
1889 	Xput(xs, xp, '\0');
1890 
1891 	/*
1892 	 * if source path had a trailing slash, check if target path
1893 	 * is not a non-directory existing file
1894 	 */
1895 	if (ip > ipath && mksh_cdirsep(ip[-1])) {
1896 		if (stat(Xstring(xs, xp), &sb)) {
1897 			if (errno != ENOENT)
1898 				goto notfound;
1899 		} else if (!S_ISDIR(sb.st_mode)) {
1900 			errno = ENOTDIR;
1901 			goto notfound;
1902 		}
1903 		/* target now either does not exist or is a directory */
1904 	}
1905 
1906 	/* return target path */
1907 	afree(ldest, ATEMP);
1908 	afree(ipath, ATEMP);
1909 	return (Xclose(xs, xp));
1910 
1911  notfound:
1912 	/* save; freeing memory might trash it */
1913 	llen = errno;
1914 	afree(ldest, ATEMP);
1915 	afree(ipath, ATEMP);
1916 	Xfree(xs, xp);
1917 	errno = llen;
1918 	return (NULL);
1919 
1920 #undef pathlen
1921 #undef pathcnd
1922 }
1923 
1924 /**
1925  *	Makes a filename into result using the following algorithm.
1926  *	- make result NULL
1927  *	- if file starts with '/', append file to result & set cdpathp to NULL
1928  *	- if file starts with ./ or ../ append cwd and file to result
1929  *	  and set cdpathp to NULL
1930  *	- if the first element of cdpathp doesn't start with a '/' xx or '.' xx
1931  *	  then cwd is appended to result.
1932  *	- the first element of cdpathp is appended to result
1933  *	- file is appended to result
1934  *	- cdpathp is set to the start of the next element in cdpathp (or NULL
1935  *	  if there are no more elements.
1936  *	The return value indicates whether a non-null element from cdpathp
1937  *	was appended to result.
1938  */
1939 static int
make_path(const char * cwd,const char * file,char ** cdpathp,XString * xsp,int * phys_pathp)1940 make_path(const char *cwd, const char *file,
1941     /* pointer to colon-separated list */
1942     char **cdpathp,
1943     XString *xsp,
1944     int *phys_pathp)
1945 {
1946 	int rval = 0;
1947 	bool use_cdpath = true;
1948 	char *plist;
1949 	size_t len, plen = 0;
1950 	char *xp = Xstring(*xsp, xp);
1951 
1952 	if (!file)
1953 		file = null;
1954 
1955 	if (mksh_abspath(file)) {
1956 		*phys_pathp = 0;
1957 		use_cdpath = false;
1958 	} else {
1959 		if (file[0] == '.') {
1960 			char c = file[1];
1961 
1962 			if (c == '.')
1963 				c = file[2];
1964 			if (mksh_cdirsep(c) || c == '\0')
1965 				use_cdpath = false;
1966 		}
1967 
1968 		plist = *cdpathp;
1969 		if (!plist)
1970 			use_cdpath = false;
1971 		else if (use_cdpath) {
1972 			char *pend = plist;
1973 
1974 			while (*pend && *pend != MKSH_PATHSEPC)
1975 				++pend;
1976 			plen = pend - plist;
1977 			*cdpathp = *pend ? pend + 1 : NULL;
1978 		}
1979 
1980 		if ((!use_cdpath || !plen || !mksh_abspath(plist)) &&
1981 		    (cwd && *cwd)) {
1982 			len = strlen(cwd);
1983 			XcheckN(*xsp, xp, len);
1984 			memcpy(xp, cwd, len);
1985 			xp += len;
1986 			if (mksh_cdirsep(xp[-1]))
1987 				xp--;
1988 			*xp++ = '/';
1989 		}
1990 		*phys_pathp = Xlength(*xsp, xp);
1991 		if (use_cdpath && plen) {
1992 			XcheckN(*xsp, xp, plen);
1993 			memcpy(xp, plist, plen);
1994 			xp += plen;
1995 			if (mksh_cdirsep(xp[-1]))
1996 				xp--;
1997 			*xp++ = '/';
1998 			rval = 1;
1999 		}
2000 	}
2001 
2002 	len = strlen(file) + 1;
2003 	XcheckN(*xsp, xp, len);
2004 	memcpy(xp, file, len);
2005 
2006 	if (!use_cdpath)
2007 		*cdpathp = NULL;
2008 
2009 	return (rval);
2010 }
2011 
2012 /*-
2013  * Simplify pathnames containing "." and ".." entries.
2014  *
2015  * simplify_path(this)			= that
2016  * /a/b/c/./../d/..			/a/b
2017  * //./C/foo/bar/../baz			//C/foo/baz
2018  * /foo/				/foo
2019  * /foo/../../bar			/bar
2020  * /foo/./blah/..			/foo
2021  * .					.
2022  * ..					..
2023  * ./foo				foo
2024  * foo/../../../bar			../../bar
2025  * C:/foo/../..				C:/
2026  * C:.					C:
2027  * C:..					C:..
2028  * C:foo/../../blah			C:../blah
2029  *
2030  * XXX consider a rooted pathname: we cannot really 'cd ..' for
2031  * pathnames like: '/', 'c:/', '//foo', '//foo/', '/@unixroot/'
2032  * (no effect), 'c:', 'c:.' (effect is retaining the '../') but
2033  * we need to honour this throughout the shell
2034  */
2035 void
simplify_path(char * p)2036 simplify_path(char *p)
2037 {
2038 	char *dp, *ip, *sp, *tp;
2039 	size_t len;
2040 	bool needslash;
2041 #ifdef MKSH_DOSPATH
2042 	bool needdot = true;
2043 
2044 	/* keep drive letter */
2045 	if (mksh_drvltr(p)) {
2046 		p += 2;
2047 		needdot = false;
2048 	}
2049 #else
2050 #define needdot true
2051 #endif
2052 
2053 	switch (*p) {
2054 	case 0:
2055 		return;
2056 	case '/':
2057 #ifdef MKSH_DOSPATH
2058 	case '\\':
2059 #endif
2060 		/* exactly two leading slashes? (SUSv4 3.266) */
2061 		if (p[1] == p[0] && !mksh_cdirsep(p[2])) {
2062 			/* keep them, e.g. for UNC pathnames */
2063 #ifdef MKSH_DOSPATH
2064 			*p++ = '/';
2065 #else
2066 			++p;
2067 #endif
2068 		}
2069 		needslash = true;
2070 		break;
2071 	default:
2072 		needslash = false;
2073 	}
2074 	dp = ip = sp = p;
2075 
2076 	while (*ip) {
2077 		/* skip slashes in input */
2078 		while (mksh_cdirsep(*ip))
2079 			++ip;
2080 		if (!*ip)
2081 			break;
2082 
2083 		/* get next pathname component from input */
2084 		tp = ip;
2085 		while (*ip && !mksh_cdirsep(*ip))
2086 			++ip;
2087 		len = ip - tp;
2088 
2089 		/* check input for "." and ".." */
2090 		if (tp[0] == '.') {
2091 			if (len == 1)
2092 				/* just continue with the next one */
2093 				continue;
2094 			else if (len == 2 && tp[1] == '.') {
2095 				/* parent level, but how? (see above) */
2096 				if (mksh_abspath(p))
2097 					/* absolute path, only one way */
2098 					goto strip_last_component;
2099 				else if (dp > sp) {
2100 					/* relative path, with subpaths */
2101 					needslash = false;
2102  strip_last_component:
2103 					/* strip off last pathname component */
2104 					while (dp > sp)
2105 						if (mksh_cdirsep(*--dp))
2106 							break;
2107 				} else {
2108 					/* relative path, at its beginning */
2109 					if (needslash)
2110 						/* or already dotdot-slash'd */
2111 						*dp++ = '/';
2112 					/* keep dotdot-slash if not absolute */
2113 					*dp++ = '.';
2114 					*dp++ = '.';
2115 					needslash = true;
2116 					sp = dp;
2117 				}
2118 				/* then continue with the next one */
2119 				continue;
2120 			}
2121 		}
2122 
2123 		if (needslash)
2124 			*dp++ = '/';
2125 
2126 		/* append next pathname component to output */
2127 		memmove(dp, tp, len);
2128 		dp += len;
2129 
2130 		/* append slash if we continue */
2131 		needslash = true;
2132 		/* try next component */
2133 	}
2134 	if (dp == p) {
2135 		/* empty path -> dot (or slash, when absolute) */
2136 		if (needslash)
2137 			*dp++ = '/';
2138 		else if (needdot)
2139 			*dp++ = '.';
2140 	}
2141 	*dp = '\0';
2142 #undef needdot
2143 }
2144 
2145 void
set_current_wd(const char * nwd)2146 set_current_wd(const char *nwd)
2147 {
2148 	char *allocd = NULL;
2149 
2150 	if (nwd == NULL) {
2151 		allocd = ksh_get_wd();
2152 		nwd = allocd ? allocd : null;
2153 	}
2154 
2155 	afree(current_wd, APERM);
2156 	strdupx(current_wd, nwd, APERM);
2157 
2158 	afree(allocd, ATEMP);
2159 }
2160 
2161 int
c_cd(const char ** wp)2162 c_cd(const char **wp)
2163 {
2164 	int optc, rv, phys_path;
2165 	bool physical = tobool(Flag(FPHYSICAL));
2166 	/* was a node from cdpath added in? */
2167 	int cdnode;
2168 	/* show where we went?, error for $PWD */
2169 	bool printpath = false, eflag = false;
2170 	struct tbl *pwd_s, *oldpwd_s;
2171 	XString xs;
2172 	char *dir, *allocd = NULL, *tryp, *pwd, *cdpath;
2173 
2174 	while ((optc = ksh_getopt(wp, &builtin_opt, "eLP")) != -1)
2175 		switch (optc) {
2176 		case 'e':
2177 			eflag = true;
2178 			break;
2179 		case 'L':
2180 			physical = false;
2181 			break;
2182 		case 'P':
2183 			physical = true;
2184 			break;
2185 		case '?':
2186 			return (2);
2187 		}
2188 	wp += builtin_opt.optind;
2189 
2190 	if (Flag(FRESTRICTED)) {
2191 		bi_errorf(Tcant_cd);
2192 		return (2);
2193 	}
2194 
2195 	pwd_s = global(TPWD);
2196 	oldpwd_s = global(TOLDPWD);
2197 
2198 	if (!wp[0]) {
2199 		/* no arguments; go home */
2200 		if ((dir = str_val(global("HOME"))) == null) {
2201 			bi_errorf("no home directory (HOME not set)");
2202 			return (2);
2203 		}
2204 	} else if (!wp[1]) {
2205 		/* one argument: - or dir */
2206 		if (ksh_isdash(wp[0])) {
2207 			dir = str_val(oldpwd_s);
2208 			if (dir == null) {
2209 				bi_errorf(Tno_OLDPWD);
2210 				return (2);
2211 			}
2212 			printpath = true;
2213 		} else {
2214 			strdupx(allocd, wp[0], ATEMP);
2215 			dir = allocd;
2216 		}
2217 	} else if (!wp[2]) {
2218 		/* two arguments; substitute arg1 in PWD for arg2 */
2219 		size_t ilen, olen, nlen, elen;
2220 		char *cp;
2221 
2222 		if (!current_wd[0]) {
2223 			bi_errorf("can't determine current directory");
2224 			return (2);
2225 		}
2226 		/*
2227 		 * Substitute arg1 for arg2 in current path. If the first
2228 		 * substitution fails because the cd fails we could try to
2229 		 * find another substitution. For now, we don't.
2230 		 */
2231 		if ((cp = strstr(current_wd, wp[0])) == NULL) {
2232 			bi_errorf(Tbadsubst);
2233 			return (2);
2234 		}
2235 		/*-
2236 		 * ilen = part of current_wd before wp[0]
2237 		 * elen = part of current_wd after wp[0]
2238 		 * because current_wd and wp[1] need to be in memory at the
2239 		 * same time beforehand the addition can stay unchecked
2240 		 */
2241 		ilen = cp - current_wd;
2242 		olen = strlen(wp[0]);
2243 		nlen = strlen(wp[1]);
2244 		elen = strlen(current_wd + ilen + olen) + 1;
2245 		dir = allocd = alloc(ilen + nlen + elen, ATEMP);
2246 		memcpy(dir, current_wd, ilen);
2247 		memcpy(dir + ilen, wp[1], nlen);
2248 		memcpy(dir + ilen + nlen, current_wd + ilen + olen, elen);
2249 		printpath = true;
2250 	} else {
2251 		bi_errorf(Ttoo_many_args);
2252 		return (2);
2253 	}
2254 
2255 #ifdef MKSH_DOSPATH
2256 	tryp = NULL;
2257 	if (mksh_drvltr(dir) && !mksh_cdirsep(dir[2]) &&
2258 	    !getdrvwd(&tryp, ord(*dir))) {
2259 		strpathx(dir, tryp, dir + 2, 0);
2260 		afree(tryp, ATEMP);
2261 		afree(allocd, ATEMP);
2262 		allocd = dir;
2263 	}
2264 #endif
2265 
2266 #ifdef MKSH__NO_PATH_MAX
2267 	/* only a first guess; make_path will enlarge xs if necessary */
2268 	XinitN(xs, 1024, ATEMP);
2269 #else
2270 	XinitN(xs, PATH_MAX, ATEMP);
2271 #endif
2272 
2273 	cdpath = str_val(global("CDPATH"));
2274 	do {
2275 		cdnode = make_path(current_wd, dir, &cdpath, &xs, &phys_path);
2276 		if (physical)
2277 			rv = chdir(tryp = Xstring(xs, xp) + phys_path);
2278 		else {
2279 			simplify_path(Xstring(xs, xp));
2280 			rv = chdir(tryp = Xstring(xs, xp));
2281 		}
2282 	} while (rv < 0 && cdpath != NULL);
2283 
2284 	if (rv < 0) {
2285 		if (cdnode)
2286 			bi_errorf(Tf_sD_s, dir, "bad directory");
2287 		else
2288 			bi_errorf(Tf_sD_s, tryp, cstrerror(errno));
2289 		afree(allocd, ATEMP);
2290 		Xfree(xs, xp);
2291 		return (2);
2292 	}
2293 
2294 	rv = 0;
2295 
2296 	/* allocd (above) => dir, which is no longer used */
2297 	afree(allocd, ATEMP);
2298 	allocd = NULL;
2299 
2300 	/* Clear out tracked aliases with relative paths */
2301 	flushcom(false);
2302 
2303 	/*
2304 	 * Set OLDPWD (note: unsetting OLDPWD does not disable this
2305 	 * setting in AT&T ksh)
2306 	 */
2307 	if (current_wd[0])
2308 		/* Ignore failure (happens if readonly or integer) */
2309 		setstr(oldpwd_s, current_wd, KSH_RETURN_ERROR);
2310 
2311 	if (!mksh_abspath(Xstring(xs, xp))) {
2312 		pwd = NULL;
2313 	} else if (!physical) {
2314 		goto norealpath_PWD;
2315 	} else if ((pwd = allocd = do_realpath(Xstring(xs, xp))) == NULL) {
2316 		if (eflag)
2317 			rv = 1;
2318  norealpath_PWD:
2319 		pwd = Xstring(xs, xp);
2320 	}
2321 
2322 	/* Set PWD */
2323 	if (pwd) {
2324 		char *ptmp = pwd;
2325 
2326 		set_current_wd(ptmp);
2327 		/* Ignore failure (happens if readonly or integer) */
2328 		setstr(pwd_s, ptmp, KSH_RETURN_ERROR);
2329 	} else {
2330 		set_current_wd(null);
2331 		pwd = Xstring(xs, xp);
2332 		/* XXX unset $PWD? */
2333 		if (eflag)
2334 			rv = 1;
2335 	}
2336 	if (printpath || cdnode)
2337 		shprintf(Tf_sN, pwd);
2338 
2339 	afree(allocd, ATEMP);
2340 	Xfree(xs, xp);
2341 	return (rv);
2342 }
2343 
2344 
2345 #ifdef KSH_CHVT_CODE
2346 extern void chvt_reinit(void);
2347 
2348 static void
chvt(const Getopt * go)2349 chvt(const Getopt *go)
2350 {
2351 	const char *dv = go->optarg;
2352 	char *cp = NULL;
2353 	int fd;
2354 
2355 	switch (*dv) {
2356 	case '-':
2357 		dv = "/dev/null";
2358 		break;
2359 	case '!':
2360 		++dv;
2361 		/* FALLTHROUGH */
2362 	default: {
2363 		struct stat sb;
2364 
2365 		if (stat(dv, &sb)) {
2366 			cp = shf_smprintf("/dev/ttyC%s", dv);
2367 			dv = cp;
2368 			if (stat(dv, &sb)) {
2369 				memmove(cp + 1, cp, /* /dev/tty */ 8);
2370 				dv = cp + 1;
2371 				if (stat(dv, &sb)) {
2372 					errorf(Tf_sD_sD_s, "chvt",
2373 					    "can't find tty", go->optarg);
2374 				}
2375 			}
2376 		}
2377 		if (!(sb.st_mode & S_IFCHR))
2378 			errorf(Tf_sD_sD_s, "chvt", "not a char device", dv);
2379 #ifndef MKSH_DISABLE_REVOKE_WARNING
2380 #if HAVE_REVOKE
2381 		if (revoke(dv))
2382 #endif
2383 			warningf(false, Tf_sD_s_s, "chvt",
2384 			    "new shell is potentially insecure, can't revoke",
2385 			    dv);
2386 #endif
2387 	    }
2388 	}
2389 	if ((fd = binopen2(dv, O_RDWR)) < 0) {
2390 		sleep(1);
2391 		if ((fd = binopen2(dv, O_RDWR)) < 0) {
2392 			errorf(Tf_sD_s_s, "chvt", Tcant_open, dv);
2393 		}
2394 	}
2395 	if (go->optarg[0] != '!') {
2396 		switch (fork()) {
2397 		case -1:
2398 			errorf(Tf_sD_s_s, "chvt", "fork", "failed");
2399 		case 0:
2400 			break;
2401 		default:
2402 			exit(0);
2403 		}
2404 	}
2405 	if (setsid() == -1)
2406 		errorf(Tf_sD_s_s, "chvt", "setsid", "failed");
2407 	if (go->optarg[0] != '-') {
2408 		if (ioctl(fd, TIOCSCTTY, NULL) == -1)
2409 			errorf(Tf_sD_s_s, "chvt", "TIOCSCTTY", "failed");
2410 		if (tcflush(fd, TCIOFLUSH))
2411 			errorf(Tf_sD_s_s, "chvt", "TCIOFLUSH", "failed");
2412 	}
2413 	ksh_dup2(fd, 0, false);
2414 	ksh_dup2(fd, 1, false);
2415 	ksh_dup2(fd, 2, false);
2416 	if (fd > 2)
2417 		close(fd);
2418 	rndset((unsigned long)chvt_rndsetup(go, sizeof(Getopt)));
2419 	chvt_reinit();
2420 }
2421 #endif
2422 
2423 #ifdef DEBUG
2424 char *
strchr(char * p,int ch)2425 strchr(char *p, int ch)
2426 {
2427 	for (;; ++p) {
2428 		if (*p == ch)
2429 			return (p);
2430 		if (!*p)
2431 			return (NULL);
2432 	}
2433 	/* NOTREACHED */
2434 }
2435 
2436 char *
strstr(char * b,const char * l)2437 strstr(char *b, const char *l)
2438 {
2439 	char first, c;
2440 	size_t n;
2441 
2442 	if ((first = *l++) == '\0')
2443 		return (b);
2444 	n = strlen(l);
2445  strstr_look:
2446 	while ((c = *b++) != first)
2447 		if (c == '\0')
2448 			return (NULL);
2449 	if (strncmp(b, l, n))
2450 		goto strstr_look;
2451 	return (b - 1);
2452 }
2453 #endif
2454 
2455 #if defined(MKSH_SMALL) && !defined(MKSH_SMALL_BUT_FAST)
2456 char *
strndup_i(const char * src,size_t len,Area * ap)2457 strndup_i(const char *src, size_t len, Area *ap)
2458 {
2459 	char *dst = NULL;
2460 
2461 	if (src != NULL) {
2462 		dst = alloc(len + 1, ap);
2463 		memcpy(dst, src, len);
2464 		dst[len] = '\0';
2465 	}
2466 	return (dst);
2467 }
2468 
2469 char *
strdup_i(const char * src,Area * ap)2470 strdup_i(const char *src, Area *ap)
2471 {
2472 	return (src == NULL ? NULL : strndup_i(src, strlen(src), ap));
2473 }
2474 #endif
2475 
2476 #if !HAVE_GETRUSAGE
2477 #define INVTCK(r,t)	do {						\
2478 	r.tv_usec = ((t) % (1000000 / CLK_TCK)) * (1000000 / CLK_TCK);	\
2479 	r.tv_sec = (t) / CLK_TCK;					\
2480 } while (/* CONSTCOND */ 0)
2481 
2482 int
getrusage(int what,struct rusage * ru)2483 getrusage(int what, struct rusage *ru)
2484 {
2485 	struct tms tms;
2486 	clock_t u, s;
2487 
2488 	if (/* ru == NULL || */ times(&tms) == (clock_t)-1)
2489 		return (-1);
2490 
2491 	switch (what) {
2492 	case RUSAGE_SELF:
2493 		u = tms.tms_utime;
2494 		s = tms.tms_stime;
2495 		break;
2496 	case RUSAGE_CHILDREN:
2497 		u = tms.tms_cutime;
2498 		s = tms.tms_cstime;
2499 		break;
2500 	default:
2501 		errno = EINVAL;
2502 		return (-1);
2503 	}
2504 	INVTCK(ru->ru_utime, u);
2505 	INVTCK(ru->ru_stime, s);
2506 	return (0);
2507 }
2508 #endif
2509 
2510 /*
2511  * process the string available via fg (get a char)
2512  * and fp (put back a char) for backslash escapes,
2513  * assuming the first call to *fg gets the char di-
2514  * rectly after the backslash; return the character
2515  * (0..0xFF), UCS (wc + 0x100), or -1 if no known
2516  * escape sequence was found
2517  */
2518 int
unbksl(bool cstyle,int (* fg)(void),void (* fp)(int))2519 unbksl(bool cstyle, int (*fg)(void), void (*fp)(int))
2520 {
2521 	int wc, i, c, fc, n;
2522 
2523 	fc = (*fg)();
2524 	switch (fc) {
2525 	case 'a':
2526 		wc = KSH_BEL;
2527 		break;
2528 	case 'b':
2529 		wc = '\b';
2530 		break;
2531 	case 'c':
2532 		if (!cstyle)
2533 			goto unknown_escape;
2534 		c = (*fg)();
2535 		wc = ksh_toctrl(c);
2536 		break;
2537 	case 'E':
2538 	case 'e':
2539 		wc = KSH_ESC;
2540 		break;
2541 	case 'f':
2542 		wc = '\f';
2543 		break;
2544 	case 'n':
2545 		wc = '\n';
2546 		break;
2547 	case 'r':
2548 		wc = '\r';
2549 		break;
2550 	case 't':
2551 		wc = '\t';
2552 		break;
2553 	case 'v':
2554 		wc = KSH_VTAB;
2555 		break;
2556 	case '1':
2557 	case '2':
2558 	case '3':
2559 	case '4':
2560 	case '5':
2561 	case '6':
2562 	case '7':
2563 		if (!cstyle)
2564 			goto unknown_escape;
2565 		/* FALLTHROUGH */
2566 	case '0':
2567 		if (cstyle)
2568 			(*fp)(fc);
2569 		/*
2570 		 * look for an octal number with up to three
2571 		 * digits, not counting the leading zero;
2572 		 * convert it to a raw octet
2573 		 */
2574 		wc = 0;
2575 		i = 3;
2576 		while (i--)
2577 			if (ctype((c = (*fg)()), C_OCTAL))
2578 				wc = (wc << 3) + ksh_numdig(c);
2579 			else {
2580 				(*fp)(c);
2581 				break;
2582 			}
2583 		break;
2584 	case 'U':
2585 		i = 8;
2586 		if (/* CONSTCOND */ 0)
2587 			/* FALLTHROUGH */
2588 	case 'u':
2589 		  i = 4;
2590 		if (/* CONSTCOND */ 0)
2591 			/* FALLTHROUGH */
2592 	case 'x':
2593 		  i = cstyle ? -1 : 2;
2594 		/**
2595 		 * x:	look for a hexadecimal number with up to
2596 		 *	two (C style: arbitrary) digits; convert
2597 		 *	to raw octet (C style: UCS if >0xFF)
2598 		 * u/U:	look for a hexadecimal number with up to
2599 		 *	four (U: eight) digits; convert to UCS
2600 		 */
2601 		wc = 0;
2602 		n = 0;
2603 		while (n < i || i == -1) {
2604 			wc <<= 4;
2605 			if (!ctype((c = (*fg)()), C_SEDEC)) {
2606 				wc >>= 4;
2607 				(*fp)(c);
2608 				break;
2609 			}
2610 			if (ctype(c, C_DIGIT))
2611 				wc += ksh_numdig(c);
2612 			else if (ctype(c, C_UPPER))
2613 				wc += ksh_numuc(c) + 10;
2614 			else
2615 				wc += ksh_numlc(c) + 10;
2616 			++n;
2617 		}
2618 		if (!n)
2619 			goto unknown_escape;
2620 		if ((cstyle && wc > 0xFF) || fc != 'x')
2621 			/* UCS marker */
2622 			wc += 0x100;
2623 		break;
2624 	case '\'':
2625 		if (!cstyle)
2626 			goto unknown_escape;
2627 		wc = '\'';
2628 		break;
2629 	case '\\':
2630 		wc = '\\';
2631 		break;
2632 	default:
2633  unknown_escape:
2634 		(*fp)(fc);
2635 		return (-1);
2636 	}
2637 
2638 	return (wc);
2639 }
2640