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