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