xref: /third_party/mksh/misc.c (revision c84f3f3c)
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 */
48unsigned char chtypes[UCHAR_MAX + 1];
49
50static const unsigned char *pat_scan(const unsigned char *,
51    const unsigned char *, bool) MKSH_A_PURE;
52static int do_gmatch(const unsigned char *, const unsigned char *,
53    const unsigned char *, const unsigned char *,
54    const unsigned char *) MKSH_A_PURE;
55static const unsigned char *gmatch_cclass(const unsigned char *, unsigned char)
56    MKSH_A_PURE;
57#ifdef KSH_CHVT_CODE
58static void chvt(const Getopt *);
59#endif
60
61/*XXX this should go away */
62static 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 */
77char *
78Xcheck_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
110const 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 */
118size_t
119option(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
139struct options_info {
140	int opt_width;
141	int opts[NELEM(options)];
142};
143
144static void options_fmt_entry(char *, size_t, unsigned int, const void *);
145static int printoptions(bool);
146static int printoption(size_t);
147
148/* format a single select menu item */
149static void
150options_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
159static int
160printoption(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
180static int
181printoptions(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
227char *
228getoptions(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 */
244void
245change_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
329void
330change_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 */
374int
375parse_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 */
554int
555getn(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 */
602static void *
603simplify_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 */
679int
680gmatchx(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 */
734bool
735has_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|'!') */
816static int
817do_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 */
978static 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
1011static const unsigned char *
1012gmatch_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 */
1214static const unsigned char *
1215pat_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
1231int
1232ascstrcmp(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
1244int
1245ascpstrcmp(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 */
1252void
1253ksh_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 */
1289int
1290ksh_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 */
1394void
1395print_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 */
1525void
1526print_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 */
1608void
1609strip_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 */
1634ssize_t
1635blocking_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 */
1658int
1659reset_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 */
1674char *
1675ksh_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
1701char *
1702do_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 */
1939static int
1940make_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 */
2035void
2036simplify_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
2145void
2146set_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
2161int
2162c_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
2346extern void chvt_reinit(void);
2347
2348static void
2349chvt(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
2424char *
2425strchr(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
2436char *
2437strstr(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)
2456char *
2457strndup_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
2469char *
2470strdup_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
2482int
2483getrusage(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 */
2518int
2519unbksl(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