Blame misc.c

Packit Service 95ac19
/*	$OpenBSD: misc.c,v 1.41 2015/09/10 22:48:58 nicm Exp $	*/
Packit Service 95ac19
/*	$OpenBSD: path.c,v 1.13 2015/09/05 09:47:08 jsg Exp $	*/
Packit Service 95ac19
Packit Service 95ac19
/*-
Packit Service 95ac19
 * Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
Packit Service 95ac19
 *		 2011, 2012, 2013, 2014, 2015, 2016, 2017
Packit Service 95ac19
 *	mirabilos <m@mirbsd.org>
Packit Service 95ac19
 * Copyright (c) 2015
Packit Service 95ac19
 *	Daniel Richard G. <skunk@iSKUNK.ORG>
Packit Service 95ac19
 *
Packit Service 95ac19
 * Provided that these terms and disclaimer and all copyright notices
Packit Service 95ac19
 * are retained or reproduced in an accompanying document, permission
Packit Service 95ac19
 * is granted to deal in this work without restriction, including un-
Packit Service 95ac19
 * limited rights to use, publicly perform, distribute, sell, modify,
Packit Service 95ac19
 * merge, give away, or sublicence.
Packit Service 95ac19
 *
Packit Service 95ac19
 * This work is provided "AS IS" and WITHOUT WARRANTY of any kind, to
Packit Service 95ac19
 * the utmost extent permitted by applicable law, neither express nor
Packit Service 95ac19
 * implied; without malicious intent or gross negligence. In no event
Packit Service 95ac19
 * may a licensor, author or contributor be held liable for indirect,
Packit Service 95ac19
 * direct, other damage, loss, or other issues arising in any way out
Packit Service 95ac19
 * of dealing in the work, even if advised of the possibility of such
Packit Service 95ac19
 * damage or existence of a defect, except proven that it results out
Packit Service 95ac19
 * of said person's immediate fault when using the work as intended.
Packit Service 95ac19
 */
Packit Service 95ac19
Packit Service 95ac19
#include "sh.h"
Packit Service 95ac19
#if !HAVE_GETRUSAGE
Packit Service 95ac19
#include <sys/times.h>
Packit Service 95ac19
#endif
Packit Service 95ac19
#if HAVE_GRP_H
Packit Service 95ac19
#include <grp.h>
Packit Service 95ac19
#endif
Packit Service 95ac19
Packit Service 95ac19
__RCSID("$MirOS: src/bin/mksh/misc.c,v 1.291 2018/01/14 00:03:03 tg Exp $");
Packit Service 95ac19
Packit Service 95ac19
#define KSH_CHVT_FLAG
Packit Service 95ac19
#ifdef MKSH_SMALL
Packit Service 95ac19
#undef KSH_CHVT_FLAG
Packit Service 95ac19
#endif
Packit Service 95ac19
#ifdef TIOCSCTTY
Packit Service 95ac19
#define KSH_CHVT_CODE
Packit Service 95ac19
#define KSH_CHVT_FLAG
Packit Service 95ac19
#endif
Packit Service 95ac19
Packit Service 95ac19
/* type bits for unsigned char */
Packit Service 95ac19
unsigned char chtypes[UCHAR_MAX + 1];
Packit Service 95ac19
Packit Service 95ac19
static const unsigned char *pat_scan(const unsigned char *,
Packit Service 95ac19
    const unsigned char *, bool) MKSH_A_PURE;
Packit Service 95ac19
static int do_gmatch(const unsigned char *, const unsigned char *,
Packit Service 95ac19
    const unsigned char *, const unsigned char *,
Packit Service 95ac19
    const unsigned char *) MKSH_A_PURE;
Packit Service 95ac19
static const unsigned char *gmatch_cclass(const unsigned char *, unsigned char)
Packit Service 95ac19
    MKSH_A_PURE;
Packit Service 95ac19
#ifdef KSH_CHVT_CODE
Packit Service 95ac19
static void chvt(const Getopt *);
Packit Service 95ac19
#endif
Packit Service 95ac19
Packit Service 95ac19
/*XXX this should go away */
Packit Service 95ac19
static int make_path(const char *, const char *, char **, XString *, int *);
Packit Service 95ac19
Packit Service 95ac19
#ifdef SETUID_CAN_FAIL_WITH_EAGAIN
Packit Service 95ac19
/* we don't need to check for other codes, EPERM won't happen */
Packit Service 95ac19
#define DO_SETUID(func, argvec) do {					\
Packit Service 95ac19
	if ((func argvec) && errno == EAGAIN)				\
Packit Service 95ac19
		errorf("%s failed with EAGAIN, probably due to a"	\
Packit Service 95ac19
		    " too low process limit; aborting", #func);		\
Packit Service 95ac19
} while (/* CONSTCOND */ 0)
Packit Service 95ac19
#else
Packit Service 95ac19
#define DO_SETUID(func, argvec) func argvec
Packit Service 95ac19
#endif
Packit Service 95ac19
Packit Service 95ac19
Packit Service 95ac19
/* called from XcheckN() to grow buffer */
Packit Service 95ac19
char *
Packit Service 95ac19
Xcheck_grow(XString *xsp, const char *xp, size_t more)
Packit Service 95ac19
{
Packit Service 95ac19
	const char *old_beg = xsp->beg;
Packit Service 95ac19
Packit Service 95ac19
	if (more < xsp->len)
Packit Service 95ac19
		more = xsp->len;
Packit Service 95ac19
	/* (xsp->len + X_EXTRA) never overflows */
Packit Service 95ac19
	checkoktoadd(more, xsp->len + X_EXTRA);
Packit Service 95ac19
	xsp->beg = aresize(xsp->beg, (xsp->len += more) + X_EXTRA, xsp->areap);
Packit Service 95ac19
	xsp->end = xsp->beg + xsp->len;
Packit Service 95ac19
	return (xsp->beg + (xp - old_beg));
Packit Service 95ac19
}
Packit Service 95ac19
Packit Service 95ac19
Packit Service 95ac19
#define SHFLAGS_DEFNS
Packit Service 95ac19
#define FN(sname,cname,flags,ochar)		\
Packit Service 95ac19
	static const struct {			\
Packit Service 95ac19
		/* character flag (if any) */	\
Packit Service 95ac19
		char c;				\
Packit Service 95ac19
		/* OF_* */			\
Packit Service 95ac19
		unsigned char optflags;		\
Packit Service 95ac19
		/* long name of option */	\
Packit Service 95ac19
		char name[sizeof(sname)];	\
Packit Service 95ac19
	} shoptione_ ## cname = {		\
Packit Service 95ac19
		ochar, flags, sname		\
Packit Service 95ac19
	};
Packit Service 95ac19
#include "sh_flags.gen"
Packit Service 95ac19
Packit Service 95ac19
#define OFC(i) (options[i][-2])
Packit Service 95ac19
#define OFF(i) (((const unsigned char *)options[i])[-1])
Packit Service 95ac19
#define OFN(i) (options[i])
Packit Service 95ac19
Packit Service 95ac19
const char * const options[] = {
Packit Service 95ac19
#define SHFLAGS_ITEMS
Packit Service 95ac19
#include "sh_flags.gen"
Packit Service 95ac19
};
Packit Service 95ac19
Packit Service 95ac19
/*
Packit Service 95ac19
 * translate -o option into F* constant (also used for test -o option)
Packit Service 95ac19
 */
Packit Service 95ac19
size_t
Packit Service 95ac19
option(const char *n)
Packit Service 95ac19
{
Packit Service 95ac19
	size_t i = 0;
Packit Service 95ac19
Packit Service 95ac19
	if (ctype(n[0], C_MINUS | C_PLUS) && n[1] && !n[2])
Packit Service 95ac19
		while (i < NELEM(options)) {
Packit Service 95ac19
			if (OFC(i) == n[1])
Packit Service 95ac19
				return (i);
Packit Service 95ac19
			++i;
Packit Service 95ac19
		}
Packit Service 95ac19
	else
Packit Service 95ac19
		while (i < NELEM(options)) {
Packit Service 95ac19
			if (!strcmp(OFN(i), n))
Packit Service 95ac19
				return (i);
Packit Service 95ac19
			++i;
Packit Service 95ac19
		}
Packit Service 95ac19
Packit Service 95ac19
	return ((size_t)-1);
Packit Service 95ac19
}
Packit Service 95ac19
Packit Service 95ac19
struct options_info {
Packit Service 95ac19
	int opt_width;
Packit Service 95ac19
	int opts[NELEM(options)];
Packit Service 95ac19
};
Packit Service 95ac19
Packit Service 95ac19
static void options_fmt_entry(char *, size_t, unsigned int, const void *);
Packit Service 95ac19
static void printoptions(bool);
Packit Service 95ac19
Packit Service 95ac19
/* format a single select menu item */
Packit Service 95ac19
static void
Packit Service 95ac19
options_fmt_entry(char *buf, size_t buflen, unsigned int i, const void *arg)
Packit Service 95ac19
{
Packit Service 95ac19
	const struct options_info *oi = (const struct options_info *)arg;
Packit Service 95ac19
Packit Service 95ac19
	shf_snprintf(buf, buflen, "%-*s %s",
Packit Service 95ac19
	    oi->opt_width, OFN(oi->opts[i]),
Packit Service 95ac19
	    Flag(oi->opts[i]) ? "on" : "off");
Packit Service 95ac19
}
Packit Service 95ac19
Packit Service 95ac19
static void
Packit Service 95ac19
printoptions(bool verbose)
Packit Service 95ac19
{
Packit Service 95ac19
	size_t i = 0;
Packit Service 95ac19
Packit Service 95ac19
	if (verbose) {
Packit Service 95ac19
		size_t n = 0, len, octs = 0;
Packit Service 95ac19
		struct options_info oi;
Packit Service 95ac19
		struct columnise_opts co;
Packit Service 95ac19
Packit Service 95ac19
		/* verbose version */
Packit Service 95ac19
		shf_puts("Current option settings\n", shl_stdout);
Packit Service 95ac19
Packit Service 95ac19
		oi.opt_width = 0;
Packit Service 95ac19
		while (i < NELEM(options)) {
Packit Service 95ac19
			if ((len = strlen(OFN(i)))) {
Packit Service 95ac19
				oi.opts[n++] = i;
Packit Service 95ac19
				if (len > octs)
Packit Service 95ac19
					octs = len;
Packit Service 95ac19
				len = utf_mbswidth(OFN(i));
Packit Service 95ac19
				if ((int)len > oi.opt_width)
Packit Service 95ac19
					oi.opt_width = (int)len;
Packit Service 95ac19
			}
Packit Service 95ac19
			++i;
Packit Service 95ac19
		}
Packit Service 95ac19
		co.shf = shl_stdout;
Packit Service 95ac19
		co.linesep = '\n';
Packit Service 95ac19
		co.prefcol = co.do_last = true;
Packit Service 95ac19
		print_columns(&co, n, options_fmt_entry, &oi,
Packit Service 95ac19
		    octs + 4, oi.opt_width + 4);
Packit Service 95ac19
	} else {
Packit Service 95ac19
		/* short version like AT&T ksh93 */
Packit Service 95ac19
		shf_puts(Tset, shl_stdout);
Packit Service 95ac19
		while (i < NELEM(options)) {
Packit Service 95ac19
			if (Flag(i) && OFN(i)[0])
Packit Service 95ac19
				shprintf(" -o %s", OFN(i));
Packit Service 95ac19
			++i;
Packit Service 95ac19
		}
Packit Service 95ac19
		shf_putc('\n', shl_stdout);
Packit Service 95ac19
	}
Packit Service 95ac19
}
Packit Service 95ac19
Packit Service 95ac19
char *
Packit Service 95ac19
getoptions(void)
Packit Service 95ac19
{
Packit Service 95ac19
	size_t i = 0;
Packit Service 95ac19
	char c, m[(int)FNFLAGS + 1];
Packit Service 95ac19
	char *cp = m;
Packit Service 95ac19
Packit Service 95ac19
	while (i < NELEM(options)) {
Packit Service 95ac19
		if ((c = OFC(i)) && Flag(i))
Packit Service 95ac19
			*cp++ = c;
Packit Service 95ac19
		++i;
Packit Service 95ac19
	}
Packit Service 95ac19
	strndupx(cp, m, cp - m, ATEMP);
Packit Service 95ac19
	return (cp);
Packit Service 95ac19
}
Packit Service 95ac19
Packit Service 95ac19
/* change a Flag(*) value; takes care of special actions */
Packit Service 95ac19
void
Packit Service 95ac19
change_flag(enum sh_flag f, int what, bool newset)
Packit Service 95ac19
{
Packit Service 95ac19
	unsigned char oldval;
Packit Service 95ac19
	unsigned char newval = (newset ? 1 : 0);
Packit Service 95ac19
Packit Service 95ac19
	if (f == FXTRACE) {
Packit Service 95ac19
		change_xtrace(newval, true);
Packit Service 95ac19
		return;
Packit Service 95ac19
	}
Packit Service 95ac19
	oldval = Flag(f);
Packit Service 95ac19
	Flag(f) = newval = (newset ? 1 : 0);
Packit Service 95ac19
#ifndef MKSH_UNEMPLOYED
Packit Service 95ac19
	if (f == FMONITOR) {
Packit Service 95ac19
		if (what != OF_CMDLINE && newval != oldval)
Packit Service 95ac19
			j_change();
Packit Service 95ac19
	} else
Packit Service 95ac19
#endif
Packit Service 95ac19
#ifndef MKSH_NO_CMDLINE_EDITING
Packit Service 95ac19
	  if ((
Packit Service 95ac19
#if !MKSH_S_NOVI
Packit Service 95ac19
	    f == FVI ||
Packit Service 95ac19
#endif
Packit Service 95ac19
	    f == FEMACS || f == FGMACS) && newval) {
Packit Service 95ac19
#if !MKSH_S_NOVI
Packit Service 95ac19
		Flag(FVI) =
Packit Service 95ac19
#endif
Packit Service 95ac19
		    Flag(FEMACS) = Flag(FGMACS) = 0;
Packit Service 95ac19
		Flag(f) = newval;
Packit Service 95ac19
	} else
Packit Service 95ac19
#endif
Packit Service 95ac19
	  if (f == FPRIVILEGED && oldval && !newval) {
Packit Service 95ac19
		/* Turning off -p? */
Packit Service 95ac19
Packit Service 95ac19
		/*XXX this can probably be optimised */
Packit Service 95ac19
		kshegid = kshgid = getgid();
Packit Service 95ac19
		ksheuid = kshuid = getuid();
Packit Service 95ac19
#if HAVE_SETRESUGID
Packit Service 95ac19
		DO_SETUID(setresgid, (kshegid, kshegid, kshegid));
Packit Service 95ac19
#if HAVE_SETGROUPS
Packit Service 95ac19
		/* setgroups doesn't EAGAIN on Linux */
Packit Service 95ac19
		setgroups(1, &kshegid);
Packit Service 95ac19
#endif
Packit Service 95ac19
		DO_SETUID(setresuid, (ksheuid, ksheuid, ksheuid));
Packit Service 95ac19
#else /* !HAVE_SETRESUGID */
Packit Service 95ac19
		/* setgid, setegid, seteuid don't EAGAIN on Linux */
Packit Service 95ac19
		setgid(kshegid);
Packit Service 95ac19
#ifndef MKSH__NO_SETEUGID
Packit Service 95ac19
		setegid(kshegid);
Packit Service 95ac19
#endif
Packit Service 95ac19
		DO_SETUID(setuid, (ksheuid));
Packit Service 95ac19
#ifndef MKSH__NO_SETEUGID
Packit Service 95ac19
		seteuid(ksheuid);
Packit Service 95ac19
#endif
Packit Service 95ac19
#endif /* !HAVE_SETRESUGID */
Packit Service 95ac19
	} else if ((f == FPOSIX || f == FSH) && newval) {
Packit Service 95ac19
		/* Turning on -o posix or -o sh? */
Packit Service 95ac19
		Flag(FBRACEEXPAND) = 0;
Packit Service 95ac19
		/* Turning on -o posix? */
Packit Service 95ac19
		if (f == FPOSIX) {
Packit Service 95ac19
			/* C locale required for compliance */
Packit Service 95ac19
			UTFMODE = 0;
Packit Service 95ac19
		}
Packit Service 95ac19
	} else if (f == FTALKING) {
Packit Service 95ac19
		/* Changing interactive flag? */
Packit Service 95ac19
		if ((what == OF_CMDLINE || what == OF_SET) && procpid == kshpid)
Packit Service 95ac19
			Flag(FTALKING_I) = newval;
Packit Service 95ac19
	}
Packit Service 95ac19
}
Packit Service 95ac19
Packit Service 95ac19
void
Packit Service 95ac19
change_xtrace(unsigned char newval, bool dosnapshot)
Packit Service 95ac19
{
Packit Service 95ac19
	static bool in_xtrace;
Packit Service 95ac19
Packit Service 95ac19
	if (in_xtrace)
Packit Service 95ac19
		return;
Packit Service 95ac19
Packit Service 95ac19
	if (!dosnapshot && newval == Flag(FXTRACE))
Packit Service 95ac19
		return;
Packit Service 95ac19
Packit Service 95ac19
	if (Flag(FXTRACE) == 2) {
Packit Service 95ac19
		shf_putc('\n', shl_xtrace);
Packit Service 95ac19
		Flag(FXTRACE) = 1;
Packit Service 95ac19
		shf_flush(shl_xtrace);
Packit Service 95ac19
	}
Packit Service 95ac19
Packit Service 95ac19
	if (!dosnapshot && Flag(FXTRACE) == 1)
Packit Service 95ac19
		switch (newval) {
Packit Service 95ac19
		case 1:
Packit Service 95ac19
			return;
Packit Service 95ac19
		case 2:
Packit Service 95ac19
			goto changed_xtrace;
Packit Service 95ac19
		}
Packit Service 95ac19
Packit Service 95ac19
	shf_flush(shl_xtrace);
Packit Service 95ac19
	if (shl_xtrace->fd != 2)
Packit Service 95ac19
		close(shl_xtrace->fd);
Packit Service 95ac19
	if (!newval || (shl_xtrace->fd = savefd(2)) == -1)
Packit Service 95ac19
		shl_xtrace->fd = 2;
Packit Service 95ac19
Packit Service 95ac19
 changed_xtrace:
Packit Service 95ac19
	if ((Flag(FXTRACE) = newval) == 2) {
Packit Service 95ac19
		in_xtrace = true;
Packit Service 95ac19
		Flag(FXTRACE) = 0;
Packit Service 95ac19
		shf_puts(substitute(str_val(global("PS4")), 0), shl_xtrace);
Packit Service 95ac19
		Flag(FXTRACE) = 2;
Packit Service 95ac19
		in_xtrace = false;
Packit Service 95ac19
	}
Packit Service 95ac19
}
Packit Service 95ac19
Packit Service 95ac19
/*
Packit Service 95ac19
 * Parse command line and set command arguments. Returns the index of
Packit Service 95ac19
 * non-option arguments, -1 if there is an error.
Packit Service 95ac19
 */
Packit Service 95ac19
int
Packit Service 95ac19
parse_args(const char **argv,
Packit Service 95ac19
    /* OF_FIRSTTIME, OF_CMDLINE, or OF_SET */
Packit Service 95ac19
    int what,
Packit Service 95ac19
    bool *setargsp)
Packit Service 95ac19
{
Packit Service 95ac19
	static const char cmd_opts[] =
Packit Service 95ac19
#define SHFLAGS_NOT_SET
Packit Service 95ac19
#define SHFLAGS_OPTCS
Packit Service 95ac19
#include "sh_flags.gen"
Packit Service 95ac19
#undef SHFLAGS_NOT_SET
Packit Service 95ac19
	    ;
Packit Service 95ac19
	static const char set_opts[] =
Packit Service 95ac19
#define SHFLAGS_NOT_CMD
Packit Service 95ac19
#define SHFLAGS_OPTCS
Packit Service 95ac19
#include "sh_flags.gen"
Packit Service 95ac19
#undef SHFLAGS_NOT_CMD
Packit Service 95ac19
	    ;
Packit Service 95ac19
	bool set;
Packit Service 95ac19
	const char *opts;
Packit Service 95ac19
	const char *array = NULL;
Packit Service 95ac19
	Getopt go;
Packit Service 95ac19
	size_t i;
Packit Service 95ac19
	int optc, arrayset = 0;
Packit Service 95ac19
	bool sortargs = false;
Packit Service 95ac19
	bool fcompatseen = false;
Packit Service 95ac19
Packit Service 95ac19
	if (what == OF_CMDLINE) {
Packit Service 95ac19
		const char *p = argv[0], *q;
Packit Service 95ac19
		/*
Packit Service 95ac19
		 * Set FLOGIN before parsing options so user can clear
Packit Service 95ac19
		 * flag using +l.
Packit Service 95ac19
		 */
Packit Service 95ac19
		if (*p != '-')
Packit Service 95ac19
			for (q = p; *q; )
Packit Service 95ac19
				if (mksh_cdirsep(*q++))
Packit Service 95ac19
					p = q;
Packit Service 95ac19
		Flag(FLOGIN) = (*p == '-');
Packit Service 95ac19
		opts = cmd_opts;
Packit Service 95ac19
	} else if (what == OF_FIRSTTIME) {
Packit Service 95ac19
		opts = cmd_opts;
Packit Service 95ac19
	} else
Packit Service 95ac19
		opts = set_opts;
Packit Service 95ac19
	ksh_getopt_reset(&go, GF_ERROR|GF_PLUSOPT);
Packit Service 95ac19
	while ((optc = ksh_getopt(argv, &go, opts)) != -1) {
Packit Service 95ac19
		set = tobool(!(go.info & GI_PLUS));
Packit Service 95ac19
		switch (optc) {
Packit Service 95ac19
		case 'A':
Packit Service 95ac19
			if (what == OF_FIRSTTIME)
Packit Service 95ac19
				break;
Packit Service 95ac19
			arrayset = set ? 1 : -1;
Packit Service 95ac19
			array = go.optarg;
Packit Service 95ac19
			break;
Packit Service 95ac19
Packit Service 95ac19
		case 'o':
Packit Service 95ac19
			if (what == OF_FIRSTTIME)
Packit Service 95ac19
				break;
Packit Service 95ac19
			if (go.optarg == NULL) {
Packit Service 95ac19
				/*
Packit Service 95ac19
				 * lone -o: print options
Packit Service 95ac19
				 *
Packit Service 95ac19
				 * Note that on the command line, -o requires
Packit Service 95ac19
				 * an option (ie, can't get here if what is
Packit Service 95ac19
				 * OF_CMDLINE).
Packit Service 95ac19
				 */
Packit Service 95ac19
				printoptions(set);
Packit Service 95ac19
				break;
Packit Service 95ac19
			}
Packit Service 95ac19
			i = option(go.optarg);
Packit Service 95ac19
			if ((i == FPOSIX || i == FSH) && set && !fcompatseen) {
Packit Service 95ac19
				/*
Packit Service 95ac19
				 * If running 'set -o posix' or
Packit Service 95ac19
				 * 'set -o sh', turn off the other;
Packit Service 95ac19
				 * if running 'set -o posix -o sh'
Packit Service 95ac19
				 * allow both to be set though.
Packit Service 95ac19
				 */
Packit Service 95ac19
				Flag(FPOSIX) = 0;
Packit Service 95ac19
				Flag(FSH) = 0;
Packit Service 95ac19
				fcompatseen = true;
Packit Service 95ac19
			}
Packit Service 95ac19
			if ((i != (size_t)-1) && (set ? 1U : 0U) == Flag(i))
Packit Service 95ac19
				/*
Packit Service 95ac19
				 * Don't check the context if the flag
Packit Service 95ac19
				 * isn't changing - makes "set -o interactive"
Packit Service 95ac19
				 * work if you're already interactive. Needed
Packit Service 95ac19
				 * if the output of "set +o" is to be used.
Packit Service 95ac19
				 */
Packit Service 95ac19
				;
Packit Service 95ac19
			else if ((i != (size_t)-1) && (OFF(i) & what))
Packit Service 95ac19
				change_flag((enum sh_flag)i, what, set);
Packit Service 95ac19
			else {
Packit Service 95ac19
				bi_errorf(Tf_sD_s, go.optarg,
Packit Service 95ac19
				    Tunknown_option);
Packit Service 95ac19
				return (-1);
Packit Service 95ac19
			}
Packit Service 95ac19
			break;
Packit Service 95ac19
Packit Service 95ac19
#ifdef KSH_CHVT_FLAG
Packit Service 95ac19
		case 'T':
Packit Service 95ac19
			if (what != OF_FIRSTTIME)
Packit Service 95ac19
				break;
Packit Service 95ac19
#ifndef KSH_CHVT_CODE
Packit Service 95ac19
			errorf("no TIOCSCTTY ioctl");
Packit Service 95ac19
#else
Packit Service 95ac19
			change_flag(FTALKING, OF_CMDLINE, true);
Packit Service 95ac19
			chvt(&go);
Packit Service 95ac19
			break;
Packit Service 95ac19
#endif
Packit Service 95ac19
#endif
Packit Service 95ac19
Packit Service 95ac19
		case '?':
Packit Service 95ac19
			return (-1);
Packit Service 95ac19
Packit Service 95ac19
		default:
Packit Service 95ac19
			if (what == OF_FIRSTTIME)
Packit Service 95ac19
				break;
Packit Service 95ac19
			/* -s: sort positional params (AT&T ksh stupidity) */
Packit Service 95ac19
			if (what == OF_SET && optc == 's') {
Packit Service 95ac19
				sortargs = true;
Packit Service 95ac19
				break;
Packit Service 95ac19
			}
Packit Service 95ac19
			for (i = 0; i < NELEM(options); i++)
Packit Service 95ac19
				if (optc == OFC(i) &&
Packit Service 95ac19
				    (what & OFF(i))) {
Packit Service 95ac19
					change_flag((enum sh_flag)i, what, set);
Packit Service 95ac19
					break;
Packit Service 95ac19
				}
Packit Service 95ac19
			if (i == NELEM(options))
Packit Service 95ac19
				internal_errorf("parse_args: '%c'", optc);
Packit Service 95ac19
		}
Packit Service 95ac19
	}
Packit Service 95ac19
	if (!(go.info & GI_MINUSMINUS) && argv[go.optind] &&
Packit Service 95ac19
	    ctype(argv[go.optind][0], C_MINUS | C_PLUS) &&
Packit Service 95ac19
	    argv[go.optind][1] == '\0') {
Packit Service 95ac19
		/* lone - clears -v and -x flags */
Packit Service 95ac19
		if (argv[go.optind][0] == '-') {
Packit Service 95ac19
			Flag(FVERBOSE) = 0;
Packit Service 95ac19
			change_xtrace(0, false);
Packit Service 95ac19
		}
Packit Service 95ac19
		/* set skips lone - or + option */
Packit Service 95ac19
		go.optind++;
Packit Service 95ac19
	}
Packit Service 95ac19
	if (setargsp)
Packit Service 95ac19
		/* -- means set $#/$* even if there are no arguments */
Packit Service 95ac19
		*setargsp = !arrayset && ((go.info & GI_MINUSMINUS) ||
Packit Service 95ac19
		    argv[go.optind]);
Packit Service 95ac19
Packit Service 95ac19
	if (arrayset) {
Packit Service 95ac19
		const char *ccp = NULL;
Packit Service 95ac19
Packit Service 95ac19
		if (array && *array)
Packit Service 95ac19
			ccp = skip_varname(array, false);
Packit Service 95ac19
		if (!ccp || !(!ccp[0] || (ccp[0] == '+' && !ccp[1]))) {
Packit Service 95ac19
			bi_errorf(Tf_sD_s, array, Tnot_ident);
Packit Service 95ac19
			return (-1);
Packit Service 95ac19
		}
Packit Service 95ac19
	}
Packit Service 95ac19
	if (sortargs) {
Packit Service 95ac19
		for (i = go.optind; argv[i]; i++)
Packit Service 95ac19
			;
Packit Service 95ac19
		qsort(&argv[go.optind], i - go.optind, sizeof(void *),
Packit Service 95ac19
		    ascpstrcmp);
Packit Service 95ac19
	}
Packit Service 95ac19
	if (arrayset)
Packit Service 95ac19
		go.optind += set_array(array, tobool(arrayset > 0),
Packit Service 95ac19
		    argv + go.optind);
Packit Service 95ac19
Packit Service 95ac19
	return (go.optind);
Packit Service 95ac19
}
Packit Service 95ac19
Packit Service 95ac19
/* parse a decimal number: returns 0 if string isn't a number, 1 otherwise */
Packit Service 95ac19
int
Packit Service 95ac19
getn(const char *s, int *ai)
Packit Service 95ac19
{
Packit Service 95ac19
	char c;
Packit Service 95ac19
	mksh_ari_u num;
Packit Service 95ac19
	bool neg = false;
Packit Service 95ac19
Packit Service 95ac19
	num.u = 0;
Packit Service 95ac19
Packit Service 95ac19
	do {
Packit Service 95ac19
		c = *s++;
Packit Service 95ac19
	} while (ctype(c, C_SPACE));
Packit Service 95ac19
Packit Service 95ac19
	switch (c) {
Packit Service 95ac19
	case '-':
Packit Service 95ac19
		neg = true;
Packit Service 95ac19
		/* FALLTHROUGH */
Packit Service 95ac19
	case '+':
Packit Service 95ac19
		c = *s++;
Packit Service 95ac19
		break;
Packit Service 95ac19
	}
Packit Service 95ac19
Packit Service 95ac19
	do {
Packit Service 95ac19
		if (!ctype(c, C_DIGIT))
Packit Service 95ac19
			/* not numeric */
Packit Service 95ac19
			return (0);
Packit Service 95ac19
		if (num.u > 214748364U)
Packit Service 95ac19
			/* overflow on multiplication */
Packit Service 95ac19
			return (0);
Packit Service 95ac19
		num.u = num.u * 10U + (unsigned int)ksh_numdig(c);
Packit Service 95ac19
		/* now: num.u <= 2147483649U */
Packit Service 95ac19
	} while ((c = *s++));
Packit Service 95ac19
Packit Service 95ac19
	if (num.u > (neg ? 2147483648U : 2147483647U))
Packit Service 95ac19
		/* overflow for signed 32-bit int */
Packit Service 95ac19
		return (0);
Packit Service 95ac19
Packit Service 95ac19
	if (neg)
Packit Service 95ac19
		num.u = -num.u;
Packit Service 95ac19
	*ai = num.i;
Packit Service 95ac19
	return (1);
Packit Service 95ac19
}
Packit Service 95ac19
Packit Service 95ac19
/**
Packit Service 95ac19
 * pattern simplifications:
Packit Service 95ac19
 * - @(x) -> x (not @(x|y) though)
Packit Service 95ac19
 * - ** -> *
Packit Service 95ac19
 */
Packit Service 95ac19
static void *
Packit Service 95ac19
simplify_gmatch_pattern(const unsigned char *sp)
Packit Service 95ac19
{
Packit Service 95ac19
	uint8_t c;
Packit Service 95ac19
	unsigned char *cp, *dp;
Packit Service 95ac19
	const unsigned char *ps, *se;
Packit Service 95ac19
Packit Service 95ac19
	cp = alloc(strlen((const void *)sp) + 1, ATEMP);
Packit Service 95ac19
	goto simplify_gmatch_pat1a;
Packit Service 95ac19
Packit Service 95ac19
	/* foo@(b@(a)r)b@(a|a)z -> foobarb@(a|a)z */
Packit Service 95ac19
 simplify_gmatch_pat1:
Packit Service 95ac19
	sp = cp;
Packit Service 95ac19
 simplify_gmatch_pat1a:
Packit Service 95ac19
	dp = cp;
Packit Service 95ac19
	se = strnul(sp);
Packit Service 95ac19
	while ((c = *sp++)) {
Packit Service 95ac19
		if (!ISMAGIC(c)) {
Packit Service 95ac19
			*dp++ = c;
Packit Service 95ac19
			continue;
Packit Service 95ac19
		}
Packit Service 95ac19
		switch ((c = *sp++)) {
Packit Service 95ac19
		case 0x80|'@':
Packit Service 95ac19
		/* simile for @ */
Packit Service 95ac19
		case 0x80|' ':
Packit Service 95ac19
			/* check whether it has only one clause */
Packit Service 95ac19
			ps = pat_scan(sp, se, true);
Packit Service 95ac19
			if (!ps || ps[-1] != /*(*/ ')')
Packit Service 95ac19
				/* nope */
Packit Service 95ac19
				break;
Packit Service 95ac19
			/* copy inner clause until matching close */
Packit Service 95ac19
			ps -= 2;
Packit Service 95ac19
			while ((const unsigned char *)sp < ps)
Packit Service 95ac19
				*dp++ = *sp++;
Packit Service 95ac19
			/* skip MAGIC and closing parenthesis */
Packit Service 95ac19
			sp += 2;
Packit Service 95ac19
			/* copy the rest of the pattern */
Packit Service 95ac19
			memmove(dp, sp, strlen((const void *)sp) + 1);
Packit Service 95ac19
			/* redo from start */
Packit Service 95ac19
			goto simplify_gmatch_pat1;
Packit Service 95ac19
		}
Packit Service 95ac19
		*dp++ = MAGIC;
Packit Service 95ac19
		*dp++ = c;
Packit Service 95ac19
	}
Packit Service 95ac19
	*dp = '\0';
Packit Service 95ac19
Packit Service 95ac19
	/* collapse adjacent asterisk wildcards */
Packit Service 95ac19
	sp = dp = cp;
Packit Service 95ac19
	while ((c = *sp++)) {
Packit Service 95ac19
		if (!ISMAGIC(c)) {
Packit Service 95ac19
			*dp++ = c;
Packit Service 95ac19
			continue;
Packit Service 95ac19
		}
Packit Service 95ac19
		switch ((c = *sp++)) {
Packit Service 95ac19
		case '*':
Packit Service 95ac19
			while (ISMAGIC(sp[0]) && sp[1] == c)
Packit Service 95ac19
				sp += 2;
Packit Service 95ac19
			break;
Packit Service 95ac19
		}
Packit Service 95ac19
		*dp++ = MAGIC;
Packit Service 95ac19
		*dp++ = c;
Packit Service 95ac19
	}
Packit Service 95ac19
	*dp = '\0';
Packit Service 95ac19
Packit Service 95ac19
	/* return the result, allocated from ATEMP */
Packit Service 95ac19
	return (cp);
Packit Service 95ac19
}
Packit Service 95ac19
Packit Service 95ac19
/* -------- gmatch.c -------- */
Packit Service 95ac19
Packit Service 95ac19
/*
Packit Service 95ac19
 * int gmatch(string, pattern)
Packit Service 95ac19
 * char *string, *pattern;
Packit Service 95ac19
 *
Packit Service 95ac19
 * Match a pattern as in sh(1).
Packit Service 95ac19
 * pattern character are prefixed with MAGIC by expand.
Packit Service 95ac19
 */
Packit Service 95ac19
int
Packit Service 95ac19
gmatchx(const char *s, const char *p, bool isfile)
Packit Service 95ac19
{
Packit Service 95ac19
	const char *se, *pe;
Packit Service 95ac19
	char *pnew;
Packit Service 95ac19
	int rv;
Packit Service 95ac19
Packit Service 95ac19
	if (s == NULL || p == NULL)
Packit Service 95ac19
		return (0);
Packit Service 95ac19
Packit Service 95ac19
	pe = strnul(p);
Packit Service 95ac19
	/*
Packit Service 95ac19
	 * isfile is false iff no syntax check has been done on
Packit Service 95ac19
	 * the pattern. If check fails, just do a strcmp().
Packit Service 95ac19
	 */
Packit Service 95ac19
	if (!isfile && !has_globbing(p)) {
Packit Service 95ac19
		size_t len = pe - p + 1;
Packit Service 95ac19
		char tbuf[64];
Packit Service 95ac19
		char *t = len <= sizeof(tbuf) ? tbuf : alloc(len, ATEMP);
Packit Service 95ac19
		debunk(t, p, len);
Packit Service 95ac19
		return (!strcmp(t, s));
Packit Service 95ac19
	}
Packit Service 95ac19
	se = strnul(s);
Packit Service 95ac19
Packit Service 95ac19
	/*
Packit Service 95ac19
	 * since the do_gmatch() engine sucks so much, we must do some
Packit Service 95ac19
	 * pattern simplifications
Packit Service 95ac19
	 */
Packit Service 95ac19
	pnew = simplify_gmatch_pattern((const unsigned char *)p);
Packit Service 95ac19
	pe = strnul(pnew);
Packit Service 95ac19
Packit Service 95ac19
	rv = do_gmatch((const unsigned char *)s, (const unsigned char *)se,
Packit Service 95ac19
	    (const unsigned char *)pnew, (const unsigned char *)pe,
Packit Service 95ac19
	    (const unsigned char *)s);
Packit Service 95ac19
	afree(pnew, ATEMP);
Packit Service 95ac19
	return (rv);
Packit Service 95ac19
}
Packit Service 95ac19
Packit Service 95ac19
/**
Packit Service 95ac19
 * Returns if p is a syntacticly correct globbing pattern, false
Packit Service 95ac19
 * if it contains no pattern characters or if there is a syntax error.
Packit Service 95ac19
 * Syntax errors are:
Packit Service 95ac19
 *	- [ with no closing ]
Packit Service 95ac19
 *	- imbalanced $(...) expression
Packit Service 95ac19
 *	- [...] and *(...) not nested (eg, @(a[b|)c], *(a[b|c]d))
Packit Service 95ac19
 */
Packit Service 95ac19
/*XXX
Packit Service 95ac19
 * - if no magic,
Packit Service 95ac19
 *	if dest given, copy to dst
Packit Service 95ac19
 *	return ?
Packit Service 95ac19
 * - if magic && (no globbing || syntax error)
Packit Service 95ac19
 *	debunk to dst
Packit Service 95ac19
 *	return ?
Packit Service 95ac19
 * - return ?
Packit Service 95ac19
 */
Packit Service 95ac19
bool
Packit Service 95ac19
has_globbing(const char *pat)
Packit Service 95ac19
{
Packit Service 95ac19
	unsigned char c, subc;
Packit Service 95ac19
	bool saw_glob = false;
Packit Service 95ac19
	unsigned int nest = 0;
Packit Service 95ac19
	const unsigned char *p = (const unsigned char *)pat;
Packit Service 95ac19
	const unsigned char *s;
Packit Service 95ac19
Packit Service 95ac19
	while ((c = *p++)) {
Packit Service 95ac19
		/* regular character? ok. */
Packit Service 95ac19
		if (!ISMAGIC(c))
Packit Service 95ac19
			continue;
Packit Service 95ac19
		/* MAGIC + NUL? abort. */
Packit Service 95ac19
		if (!(c = *p++))
Packit Service 95ac19
			return (false);
Packit Service 95ac19
		/* some specials */
Packit Service 95ac19
		if (ord(c) == ORD('*') || ord(c) == ORD('?')) {
Packit Service 95ac19
			/* easy glob, accept */
Packit Service 95ac19
			saw_glob = true;
Packit Service 95ac19
		} else if (ord(c) == ORD('[')) {
Packit Service 95ac19
			/* bracket expression; eat negation and initial ] */
Packit Service 95ac19
			if (ISMAGIC(p[0]) && ord(p[1]) == ORD('!'))
Packit Service 95ac19
				p += 2;
Packit Service 95ac19
			if (ISMAGIC(p[0]) && ord(p[1]) == ORD(']'))
Packit Service 95ac19
				p += 2;
Packit Service 95ac19
			/* check next string part */
Packit Service 95ac19
			s = p;
Packit Service 95ac19
			while ((c = *s++)) {
Packit Service 95ac19
				/* regular chars are ok */
Packit Service 95ac19
				if (!ISMAGIC(c))
Packit Service 95ac19
					continue;
Packit Service 95ac19
				/* MAGIC + NUL cannot happen */
Packit Service 95ac19
				if (!(c = *s++))
Packit Service 95ac19
					return (false);
Packit Service 95ac19
				/* terminating bracket? */
Packit Service 95ac19
				if (ord(c) == ORD(']')) {
Packit Service 95ac19
					/* accept and continue */
Packit Service 95ac19
					p = s;
Packit Service 95ac19
					saw_glob = true;
Packit Service 95ac19
					break;
Packit Service 95ac19
				}
Packit Service 95ac19
				/* sub-bracket expressions */
Packit Service 95ac19
				if (ord(c) == ORD('[') && (
Packit Service 95ac19
				    /* collating element? */
Packit Service 95ac19
				    ord(*s) == ORD('.') ||
Packit Service 95ac19
				    /* equivalence class? */
Packit Service 95ac19
				    ord(*s) == ORD('=') ||
Packit Service 95ac19
				    /* character class? */
Packit Service 95ac19
				    ord(*s) == ORD(':'))) {
Packit Service 95ac19
					/* must stop with exactly the same c */
Packit Service 95ac19
					subc = *s++;
Packit Service 95ac19
					/* arbitrarily many chars in betwixt */
Packit Service 95ac19
					while ((c = *s++))
Packit Service 95ac19
						/* but only this sequence... */
Packit Service 95ac19
						if (c == subc && ISMAGIC(*s) &&
Packit Service 95ac19
						    ord(s[1]) == ORD(']')) {
Packit Service 95ac19
							/* accept, terminate */
Packit Service 95ac19
							s += 2;
Packit Service 95ac19
							break;
Packit Service 95ac19
						}
Packit Service 95ac19
					/* EOS without: reject bracket expr */
Packit Service 95ac19
					if (!c)
Packit Service 95ac19
						break;
Packit Service 95ac19
					/* continue; */
Packit Service 95ac19
				}
Packit Service 95ac19
				/* anything else just goes on */
Packit Service 95ac19
			}
Packit Service 95ac19
		} else if ((c & 0x80) && ctype(c & 0x7F, C_PATMO | C_SPC)) {
Packit Service 95ac19
			/* opening pattern */
Packit Service 95ac19
			saw_glob = true;
Packit Service 95ac19
			++nest;
Packit Service 95ac19
		} else if (ord(c) == ORD(/*(*/ ')')) {
Packit Service 95ac19
			/* closing pattern */
Packit Service 95ac19
			if (nest)
Packit Service 95ac19
				--nest;
Packit Service 95ac19
		}
Packit Service 95ac19
	}
Packit Service 95ac19
	return (saw_glob && !nest);
Packit Service 95ac19
}
Packit Service 95ac19
Packit Service 95ac19
/* Function must return either 0 or 1 (assumed by code for 0x80|'!') */
Packit Service 95ac19
static int
Packit Service 95ac19
do_gmatch(const unsigned char *s, const unsigned char *se,
Packit Service 95ac19
    const unsigned char *p, const unsigned char *pe,
Packit Service 95ac19
    const unsigned char *smin)
Packit Service 95ac19
{
Packit Service 95ac19
	unsigned char sc, pc, sl = 0;
Packit Service 95ac19
	const unsigned char *prest, *psub, *pnext;
Packit Service 95ac19
	const unsigned char *srest;
Packit Service 95ac19
Packit Service 95ac19
	if (s == NULL || p == NULL)
Packit Service 95ac19
		return (0);
Packit Service 95ac19
	if (s > smin && s <= se)
Packit Service 95ac19
		sl = s[-1];
Packit Service 95ac19
	while (p < pe) {
Packit Service 95ac19
		pc = *p++;
Packit Service 95ac19
		sc = s < se ? *s : '\0';
Packit Service 95ac19
		s++;
Packit Service 95ac19
		if (!ISMAGIC(pc)) {
Packit Service 95ac19
			if (sc != pc)
Packit Service 95ac19
				return (0);
Packit Service 95ac19
			sl = sc;
Packit Service 95ac19
			continue;
Packit Service 95ac19
		}
Packit Service 95ac19
		switch (ord(*p++)) {
Packit Service 95ac19
		case ORD('['):
Packit Service 95ac19
			/* BSD cclass extension? */
Packit Service 95ac19
			if (ISMAGIC(p[0]) && ord(p[1]) == ORD('[') &&
Packit Service 95ac19
			    ord(p[2]) == ORD(':') &&
Packit Service 95ac19
			    ctype((pc = p[3]), C_ANGLE) &&
Packit Service 95ac19
			    ord(p[4]) == ORD(':') &&
Packit Service 95ac19
			    ISMAGIC(p[5]) && ord(p[6]) == ORD(']') &&
Packit Service 95ac19
			    ISMAGIC(p[7]) && ord(p[8]) == ORD(']')) {
Packit Service 95ac19
				/* zero-length match */
Packit Service 95ac19
				--s;
Packit Service 95ac19
				p += 9;
Packit Service 95ac19
				/* word begin? */
Packit Service 95ac19
				if (ord(pc) == ORD('<') &&
Packit Service 95ac19
				    !ctype(sl, C_ALNUX) &&
Packit Service 95ac19
				    ctype(sc, C_ALNUX))
Packit Service 95ac19
					break;
Packit Service 95ac19
				/* word end? */
Packit Service 95ac19
				if (ord(pc) == ORD('>') &&
Packit Service 95ac19
				    ctype(sl, C_ALNUX) &&
Packit Service 95ac19
				    !ctype(sc, C_ALNUX))
Packit Service 95ac19
					break;
Packit Service 95ac19
				/* neither */
Packit Service 95ac19
				return (0);
Packit Service 95ac19
			}
Packit Service 95ac19
			if (sc == 0 || (p = gmatch_cclass(p, sc)) == NULL)
Packit Service 95ac19
				return (0);
Packit Service 95ac19
			break;
Packit Service 95ac19
Packit Service 95ac19
		case ORD('?'):
Packit Service 95ac19
			if (sc == 0)
Packit Service 95ac19
				return (0);
Packit Service 95ac19
			if (UTFMODE) {
Packit Service 95ac19
				--s;
Packit Service 95ac19
				s += utf_ptradj((const void *)s);
Packit Service 95ac19
			}
Packit Service 95ac19
			break;
Packit Service 95ac19
Packit Service 95ac19
		case ORD('*'):
Packit Service 95ac19
			if (p == pe)
Packit Service 95ac19
				return (1);
Packit Service 95ac19
			s--;
Packit Service 95ac19
			do {
Packit Service 95ac19
				if (do_gmatch(s, se, p, pe, smin))
Packit Service 95ac19
					return (1);
Packit Service 95ac19
			} while (s++ < se);
Packit Service 95ac19
			return (0);
Packit Service 95ac19
Packit Service 95ac19
		/**
Packit Service 95ac19
		 * [+*?@!](pattern|pattern|..)
Packit Service 95ac19
		 * This is also needed for ${..%..}, etc.
Packit Service 95ac19
		 */
Packit Service 95ac19
Packit Service 95ac19
		/* matches one or more times */
Packit Service 95ac19
		case ORD('+') | 0x80:
Packit Service 95ac19
		/* matches zero or more times */
Packit Service 95ac19
		case ORD('*') | 0x80:
Packit Service 95ac19
			if (!(prest = pat_scan(p, pe, false)))
Packit Service 95ac19
				return (0);
Packit Service 95ac19
			s--;
Packit Service 95ac19
			/* take care of zero matches */
Packit Service 95ac19
			if (ord(p[-1]) == (0x80 | ORD('*')) &&
Packit Service 95ac19
			    do_gmatch(s, se, prest, pe, smin))
Packit Service 95ac19
				return (1);
Packit Service 95ac19
			for (psub = p; ; psub = pnext) {
Packit Service 95ac19
				pnext = pat_scan(psub, pe, true);
Packit Service 95ac19
				for (srest = s; srest <= se; srest++) {
Packit Service 95ac19
					if (do_gmatch(s, srest, psub, pnext - 2, smin) &&
Packit Service 95ac19
					    (do_gmatch(srest, se, prest, pe, smin) ||
Packit Service 95ac19
					    (s != srest &&
Packit Service 95ac19
					    do_gmatch(srest, se, p - 2, pe, smin))))
Packit Service 95ac19
						return (1);
Packit Service 95ac19
				}
Packit Service 95ac19
				if (pnext == prest)
Packit Service 95ac19
					break;
Packit Service 95ac19
			}
Packit Service 95ac19
			return (0);
Packit Service 95ac19
Packit Service 95ac19
		/* matches zero or once */
Packit Service 95ac19
		case ORD('?') | 0x80:
Packit Service 95ac19
		/* matches one of the patterns */
Packit Service 95ac19
		case ORD('@') | 0x80:
Packit Service 95ac19
		/* simile for @ */
Packit Service 95ac19
		case ORD(' ') | 0x80:
Packit Service 95ac19
			if (!(prest = pat_scan(p, pe, false)))
Packit Service 95ac19
				return (0);
Packit Service 95ac19
			s--;
Packit Service 95ac19
			/* Take care of zero matches */
Packit Service 95ac19
			if (ord(p[-1]) == (0x80 | ORD('?')) &&
Packit Service 95ac19
			    do_gmatch(s, se, prest, pe, smin))
Packit Service 95ac19
				return (1);
Packit Service 95ac19
			for (psub = p; ; psub = pnext) {
Packit Service 95ac19
				pnext = pat_scan(psub, pe, true);
Packit Service 95ac19
				srest = prest == pe ? se : s;
Packit Service 95ac19
				for (; srest <= se; srest++) {
Packit Service 95ac19
					if (do_gmatch(s, srest, psub, pnext - 2, smin) &&
Packit Service 95ac19
					    do_gmatch(srest, se, prest, pe, smin))
Packit Service 95ac19
						return (1);
Packit Service 95ac19
				}
Packit Service 95ac19
				if (pnext == prest)
Packit Service 95ac19
					break;
Packit Service 95ac19
			}
Packit Service 95ac19
			return (0);
Packit Service 95ac19
Packit Service 95ac19
		/* matches none of the patterns */
Packit Service 95ac19
		case ORD('!') | 0x80:
Packit Service 95ac19
			if (!(prest = pat_scan(p, pe, false)))
Packit Service 95ac19
				return (0);
Packit Service 95ac19
			s--;
Packit Service 95ac19
			for (srest = s; srest <= se; srest++) {
Packit Service 95ac19
				int matched = 0;
Packit Service 95ac19
Packit Service 95ac19
				for (psub = p; ; psub = pnext) {
Packit Service 95ac19
					pnext = pat_scan(psub, pe, true);
Packit Service 95ac19
					if (do_gmatch(s, srest, psub,
Packit Service 95ac19
					    pnext - 2, smin)) {
Packit Service 95ac19
						matched = 1;
Packit Service 95ac19
						break;
Packit Service 95ac19
					}
Packit Service 95ac19
					if (pnext == prest)
Packit Service 95ac19
						break;
Packit Service 95ac19
				}
Packit Service 95ac19
				if (!matched &&
Packit Service 95ac19
				    do_gmatch(srest, se, prest, pe, smin))
Packit Service 95ac19
					return (1);
Packit Service 95ac19
			}
Packit Service 95ac19
			return (0);
Packit Service 95ac19
Packit Service 95ac19
		default:
Packit Service 95ac19
			if (sc != p[-1])
Packit Service 95ac19
				return (0);
Packit Service 95ac19
			break;
Packit Service 95ac19
		}
Packit Service 95ac19
		sl = sc;
Packit Service 95ac19
	}
Packit Service 95ac19
	return (s == se);
Packit Service 95ac19
}
Packit Service 95ac19
Packit Service 95ac19
/*XXX this is a prime example for bsearch or a const hashtable */
Packit Service 95ac19
static const struct cclass {
Packit Service 95ac19
	const char *name;
Packit Service 95ac19
	uint32_t value;
Packit Service 95ac19
} cclasses[] = {
Packit Service 95ac19
	/* POSIX */
Packit Service 95ac19
	{ "alnum",	C_ALNUM	},
Packit Service 95ac19
	{ "alpha",	C_ALPHA	},
Packit Service 95ac19
	{ "blank",	C_BLANK	},
Packit Service 95ac19
	{ "cntrl",	C_CNTRL	},
Packit Service 95ac19
	{ "digit",	C_DIGIT	},
Packit Service 95ac19
	{ "graph",	C_GRAPH	},
Packit Service 95ac19
	{ "lower",	C_LOWER	},
Packit Service 95ac19
	{ "print",	C_PRINT	},
Packit Service 95ac19
	{ "punct",	C_PUNCT	},
Packit Service 95ac19
	{ "space",	C_SPACE	},
Packit Service 95ac19
	{ "upper",	C_UPPER	},
Packit Service 95ac19
	{ "xdigit",	C_SEDEC	},
Packit Service 95ac19
	/* BSD */
Packit Service 95ac19
	/* "<" and ">" are handled inline */
Packit Service 95ac19
	/* GNU bash */
Packit Service 95ac19
	{ "ascii",	C_ASCII	},
Packit Service 95ac19
	{ "word",	C_ALNUX	},
Packit Service 95ac19
	/* mksh */
Packit Service 95ac19
	{ "sh_alias",	C_ALIAS	},
Packit Service 95ac19
	{ "sh_edq",	C_EDQ	},
Packit Service 95ac19
	{ "sh_ifs",	C_IFS	},
Packit Service 95ac19
	{ "sh_ifsws",	C_IFSWS	},
Packit Service 95ac19
	{ "sh_nl",	C_NL	},
Packit Service 95ac19
	{ "sh_quote",	C_QUOTE	},
Packit Service 95ac19
	/* sentinel */
Packit Service 95ac19
	{ NULL,		0	}
Packit Service 95ac19
};
Packit Service 95ac19
Packit Service 95ac19
static const unsigned char *
Packit Service 95ac19
gmatch_cclass(const unsigned char *pat, unsigned char sc)
Packit Service 95ac19
{
Packit Service 95ac19
	unsigned char c, subc, lc;
Packit Service 95ac19
	const unsigned char *p = pat, *s;
Packit Service 95ac19
	bool found = false;
Packit Service 95ac19
	bool negated = false;
Packit Service 95ac19
	char *subp;
Packit Service 95ac19
Packit Service 95ac19
	/* check for negation */
Packit Service 95ac19
	if (ISMAGIC(p[0]) && ord(p[1]) == ORD('!')) {
Packit Service 95ac19
		p += 2;
Packit Service 95ac19
		negated = true;
Packit Service 95ac19
	}
Packit Service 95ac19
	/* make initial ] non-MAGIC */
Packit Service 95ac19
	if (ISMAGIC(p[0]) && ord(p[1]) == ORD(']'))
Packit Service 95ac19
		++p;
Packit Service 95ac19
	/* iterate over bracket expression, debunk()ing on the fly */
Packit Service 95ac19
	while ((c = *p++)) {
Packit Service 95ac19
 nextc:
Packit Service 95ac19
		/* non-regular character? */
Packit Service 95ac19
		if (ISMAGIC(c)) {
Packit Service 95ac19
			/* MAGIC + NUL cannot happen */
Packit Service 95ac19
			if (!(c = *p++))
Packit Service 95ac19
				break;
Packit Service 95ac19
			/* terminating bracket? */
Packit Service 95ac19
			if (ord(c) == ORD(']')) {
Packit Service 95ac19
				/* accept and return */
Packit Service 95ac19
				return (found != negated ? p : NULL);
Packit Service 95ac19
			}
Packit Service 95ac19
			/* sub-bracket expressions */
Packit Service 95ac19
			if (ord(c) == ORD('[') && (
Packit Service 95ac19
			    /* collating element? */
Packit Service 95ac19
			    ord(*p) == ORD('.') ||
Packit Service 95ac19
			    /* equivalence class? */
Packit Service 95ac19
			    ord(*p) == ORD('=') ||
Packit Service 95ac19
			    /* character class? */
Packit Service 95ac19
			    ord(*p) == ORD(':'))) {
Packit Service 95ac19
				/* must stop with exactly the same c */
Packit Service 95ac19
				subc = *p++;
Packit Service 95ac19
				/* save away start of substring */
Packit Service 95ac19
				s = p;
Packit Service 95ac19
				/* arbitrarily many chars in betwixt */
Packit Service 95ac19
				while ((c = *p++))
Packit Service 95ac19
					/* but only this sequence... */
Packit Service 95ac19
					if (c == subc && ISMAGIC(*p) &&
Packit Service 95ac19
					    ord(p[1]) == ORD(']')) {
Packit Service 95ac19
						/* accept, terminate */
Packit Service 95ac19
						p += 2;
Packit Service 95ac19
						break;
Packit Service 95ac19
					}
Packit Service 95ac19
				/* EOS without: reject bracket expr */
Packit Service 95ac19
				if (!c)
Packit Service 95ac19
					break;
Packit Service 95ac19
				/* debunk substring */
Packit Service 95ac19
				strndupx(subp, s, p - s - 3, ATEMP);
Packit Service 95ac19
				debunk(subp, subp, p - s - 3 + 1);
Packit Service 95ac19
 cclass_common:
Packit Service 95ac19
				/* whither subexpression */
Packit Service 95ac19
				if (ord(subc) == ORD(':')) {
Packit Service 95ac19
					const struct cclass *cls = cclasses;
Packit Service 95ac19
Packit Service 95ac19
					/* search for name in cclass list */
Packit Service 95ac19
					while (cls->name)
Packit Service 95ac19
						if (!strcmp(subp, cls->name)) {
Packit Service 95ac19
							/* found, match? */
Packit Service 95ac19
							if (ctype(sc,
Packit Service 95ac19
							    cls->value))
Packit Service 95ac19
								found = true;
Packit Service 95ac19
							/* break either way */
Packit Service 95ac19
							break;
Packit Service 95ac19
						} else
Packit Service 95ac19
							++cls;
Packit Service 95ac19
					/* that's all here */
Packit Service 95ac19
					afree(subp, ATEMP);
Packit Service 95ac19
					continue;
Packit Service 95ac19
				}
Packit Service 95ac19
				/* collating element or equivalence class */
Packit Service 95ac19
				/* Note: latter are treated as former */
Packit Service 95ac19
				if (ctype(subp[0], C_ASCII) && !subp[1])
Packit Service 95ac19
					/* [.a.] where a is one ASCII char */
Packit Service 95ac19
					c = subp[0];
Packit Service 95ac19
				else
Packit Service 95ac19
					/* force no match */
Packit Service 95ac19
					c = 0;
Packit Service 95ac19
				/* no longer needed */
Packit Service 95ac19
				afree(subp, ATEMP);
Packit Service 95ac19
			} else if (!ISMAGIC(c) && (c & 0x80)) {
Packit Service 95ac19
				/* 0x80|' ' is plain (...) */
Packit Service 95ac19
				if ((c &= 0x7F) != ' ') {
Packit Service 95ac19
					/* check single match NOW */
Packit Service 95ac19
					if (sc == c)
Packit Service 95ac19
						found = true;
Packit Service 95ac19
					/* next character is (...) */
Packit Service 95ac19
				}
Packit Service 95ac19
				c = '(' /*)*/;
Packit Service 95ac19
			}
Packit Service 95ac19
		}
Packit Service 95ac19
		/* range expression? */
Packit Service 95ac19
		if (!(ISMAGIC(p[0]) && ord(p[1]) == ORD('-') &&
Packit Service 95ac19
		    /* not terminating bracket? */
Packit Service 95ac19
		    (!ISMAGIC(p[2]) || ord(p[3]) != ORD(']')))) {
Packit Service 95ac19
			/* no, check single match */
Packit Service 95ac19
			if (sc == c)
Packit Service 95ac19
				/* note: sc is never NUL */
Packit Service 95ac19
				found = true;
Packit Service 95ac19
			/* do the next "first" character */
Packit Service 95ac19
			continue;
Packit Service 95ac19
		}
Packit Service 95ac19
		/* save lower range bound */
Packit Service 95ac19
		lc = c;
Packit Service 95ac19
		/* skip over the range operator */
Packit Service 95ac19
		p += 2;
Packit Service 95ac19
		/* do the same shit as above... almost */
Packit Service 95ac19
		subc = 0;
Packit Service 95ac19
		if (!(c = *p++))
Packit Service 95ac19
			break;
Packit Service 95ac19
		/* non-regular character? */
Packit Service 95ac19
		if (ISMAGIC(c)) {
Packit Service 95ac19
			/* MAGIC + NUL cannot happen */
Packit Service 95ac19
			if (!(c = *p++))
Packit Service 95ac19
				break;
Packit Service 95ac19
			/* sub-bracket expressions */
Packit Service 95ac19
			if (ord(c) == ORD('[') && (
Packit Service 95ac19
			    /* collating element? */
Packit Service 95ac19
			    ord(*p) == ORD('.') ||
Packit Service 95ac19
			    /* equivalence class? */
Packit Service 95ac19
			    ord(*p) == ORD('=') ||
Packit Service 95ac19
			    /* character class? */
Packit Service 95ac19
			    ord(*p) == ORD(':'))) {
Packit Service 95ac19
				/* must stop with exactly the same c */
Packit Service 95ac19
				subc = *p++;
Packit Service 95ac19
				/* save away start of substring */
Packit Service 95ac19
				s = p;
Packit Service 95ac19
				/* arbitrarily many chars in betwixt */
Packit Service 95ac19
				while ((c = *p++))
Packit Service 95ac19
					/* but only this sequence... */
Packit Service 95ac19
					if (c == subc && ISMAGIC(*p) &&
Packit Service 95ac19
					    ord(p[1]) == ORD(']')) {
Packit Service 95ac19
						/* accept, terminate */
Packit Service 95ac19
						p += 2;
Packit Service 95ac19
						break;
Packit Service 95ac19
					}
Packit Service 95ac19
				/* EOS without: reject bracket expr */
Packit Service 95ac19
				if (!c)
Packit Service 95ac19
					break;
Packit Service 95ac19
				/* debunk substring */
Packit Service 95ac19
				strndupx(subp, s, p - s - 3, ATEMP);
Packit Service 95ac19
				debunk(subp, subp, p - s - 3 + 1);
Packit Service 95ac19
				/* whither subexpression */
Packit Service 95ac19
				if (ord(subc) == ORD(':')) {
Packit Service 95ac19
					/* oops, not a range */
Packit Service 95ac19
Packit Service 95ac19
					/* match single previous char */
Packit Service 95ac19
					if (lc && (sc == lc))
Packit Service 95ac19
						found = true;
Packit Service 95ac19
					/* match hyphen-minus */
Packit Service 95ac19
					if (ord(sc) == ORD('-'))
Packit Service 95ac19
						found = true;
Packit Service 95ac19
					/* handle cclass common part */
Packit Service 95ac19
					goto cclass_common;
Packit Service 95ac19
				}
Packit Service 95ac19
				/* collating element or equivalence class */
Packit Service 95ac19
				/* Note: latter are treated as former */
Packit Service 95ac19
				if (ctype(subp[0], C_ASCII) && !subp[1])
Packit Service 95ac19
					/* [.a.] where a is one ASCII char */
Packit Service 95ac19
					c = subp[0];
Packit Service 95ac19
				else
Packit Service 95ac19
					/* force no match */
Packit Service 95ac19
					c = 0;
Packit Service 95ac19
				/* no longer needed */
Packit Service 95ac19
				afree(subp, ATEMP);
Packit Service 95ac19
				/* other meaning below */
Packit Service 95ac19
				subc = 0;
Packit Service 95ac19
			} else if (c == (0x80 | ' ')) {
Packit Service 95ac19
				/* 0x80|' ' is plain (...) */
Packit Service 95ac19
				c = '(' /*)*/;
Packit Service 95ac19
			} else if (!ISMAGIC(c) && (c & 0x80)) {
Packit Service 95ac19
				c &= 0x7F;
Packit Service 95ac19
				subc = '(' /*)*/;
Packit Service 95ac19
			}
Packit Service 95ac19
		}
Packit Service 95ac19
		/* now do the actual range match check */
Packit Service 95ac19
		if (lc != 0 /* && c != 0 */ &&
Packit Service 95ac19
		    asciibetical(lc) <= asciibetical(sc) &&
Packit Service 95ac19
		    asciibetical(sc) <= asciibetical(c))
Packit Service 95ac19
			found = true;
Packit Service 95ac19
		/* forced next character? */
Packit Service 95ac19
		if (subc) {
Packit Service 95ac19
			c = subc;
Packit Service 95ac19
			goto nextc;
Packit Service 95ac19
		}
Packit Service 95ac19
		/* otherwise, just go on with the pattern string */
Packit Service 95ac19
	}
Packit Service 95ac19
	/* if we broke here, the bracket expression was invalid */
Packit Service 95ac19
	if (ord(sc) == ORD('['))
Packit Service 95ac19
		/* initial opening bracket as literal match */
Packit Service 95ac19
		return (pat);
Packit Service 95ac19
	/* or rather no match */
Packit Service 95ac19
	return (NULL);
Packit Service 95ac19
}
Packit Service 95ac19
Packit Service 95ac19
/* Look for next ) or | (if match_sep) in *(foo|bar) pattern */
Packit Service 95ac19
static const unsigned char *
Packit Service 95ac19
pat_scan(const unsigned char *p, const unsigned char *pe, bool match_sep)
Packit Service 95ac19
{
Packit Service 95ac19
	int nest = 0;
Packit Service 95ac19
Packit Service 95ac19
	for (; p < pe; p++) {
Packit Service 95ac19
		if (!ISMAGIC(*p))
Packit Service 95ac19
			continue;
Packit Service 95ac19
		if ((*++p == /*(*/ ')' && nest-- == 0) ||
Packit Service 95ac19
		    (*p == '|' && match_sep && nest == 0))
Packit Service 95ac19
			return (p + 1);
Packit Service 95ac19
		if ((*p & 0x80) && ctype(*p & 0x7F, C_PATMO | C_SPC))
Packit Service 95ac19
			nest++;
Packit Service 95ac19
	}
Packit Service 95ac19
	return (NULL);
Packit Service 95ac19
}
Packit Service 95ac19
Packit Service 95ac19
int
Packit Service 95ac19
ascstrcmp(const void *s1, const void *s2)
Packit Service 95ac19
{
Packit Service 95ac19
	const uint8_t *cp1 = s1, *cp2 = s2;
Packit Service 95ac19
Packit Service 95ac19
	while (*cp1 == *cp2) {
Packit Service 95ac19
		if (*cp1++ == '\0')
Packit Service 95ac19
			return (0);
Packit Service 95ac19
		++cp2;
Packit Service 95ac19
	}
Packit Service 95ac19
	return ((int)asciibetical(*cp1) - (int)asciibetical(*cp2));
Packit Service 95ac19
}
Packit Service 95ac19
Packit Service 95ac19
int
Packit Service 95ac19
ascpstrcmp(const void *pstr1, const void *pstr2)
Packit Service 95ac19
{
Packit Service 95ac19
	return (ascstrcmp(*(const char * const *)pstr1,
Packit Service 95ac19
	    *(const char * const *)pstr2));
Packit Service 95ac19
}
Packit Service 95ac19
Packit Service 95ac19
/* Initialise a Getopt structure */
Packit Service 95ac19
void
Packit Service 95ac19
ksh_getopt_reset(Getopt *go, int flags)
Packit Service 95ac19
{
Packit Service 95ac19
	go->optind = 1;
Packit Service 95ac19
	go->optarg = NULL;
Packit Service 95ac19
	go->p = 0;
Packit Service 95ac19
	go->flags = flags;
Packit Service 95ac19
	go->info = 0;
Packit Service 95ac19
	go->buf[1] = '\0';
Packit Service 95ac19
}
Packit Service 95ac19
Packit Service 95ac19
Packit Service 95ac19
/**
Packit Service 95ac19
 * getopt() used for shell built-in commands, the getopts command, and
Packit Service 95ac19
 * command line options.
Packit Service 95ac19
 * A leading ':' in options means don't print errors, instead return '?'
Packit Service 95ac19
 * or ':' and set go->optarg to the offending option character.
Packit Service 95ac19
 * If GF_ERROR is set (and option doesn't start with :), errors result in
Packit Service 95ac19
 * a call to bi_errorf().
Packit Service 95ac19
 *
Packit Service 95ac19
 * Non-standard features:
Packit Service 95ac19
 *	- ';' is like ':' in options, except the argument is optional
Packit Service 95ac19
 *	  (if it isn't present, optarg is set to 0).
Packit Service 95ac19
 *	  Used for 'set -o'.
Packit Service 95ac19
 *	- ',' is like ':' in options, except the argument always immediately
Packit Service 95ac19
 *	  follows the option character (optarg is set to the null string if
Packit Service 95ac19
 *	  the option is missing).
Packit Service 95ac19
 *	  Used for 'read -u2', 'print -u2' and fc -40.
Packit Service 95ac19
 *	- '#' is like ':' in options, expect that the argument is optional
Packit Service 95ac19
 *	  and must start with a digit. If the argument doesn't start with a
Packit Service 95ac19
 *	  digit, it is assumed to be missing and normal option processing
Packit Service 95ac19
 *	  continues (optarg is set to 0 if the option is missing).
Packit Service 95ac19
 *	  Used for 'typeset -LZ4'.
Packit Service 95ac19
 *	- accepts +c as well as -c IF the GF_PLUSOPT flag is present. If an
Packit Service 95ac19
 *	  option starting with + is accepted, the GI_PLUS flag will be set
Packit Service 95ac19
 *	  in go->info.
Packit Service 95ac19
 */
Packit Service 95ac19
int
Packit Service 95ac19
ksh_getopt(const char **argv, Getopt *go, const char *optionsp)
Packit Service 95ac19
{
Packit Service 95ac19
	char c;
Packit Service 95ac19
	const char *o;
Packit Service 95ac19
Packit Service 95ac19
	if (go->p == 0 || (c = argv[go->optind - 1][go->p]) == '\0') {
Packit Service 95ac19
		const char *arg = argv[go->optind], flag = arg ? *arg : '\0';
Packit Service 95ac19
Packit Service 95ac19
		go->p = 1;
Packit Service 95ac19
		if (flag == '-' && ksh_isdash(arg + 1)) {
Packit Service 95ac19
			go->optind++;
Packit Service 95ac19
			go->p = 0;
Packit Service 95ac19
			go->info |= GI_MINUSMINUS;
Packit Service 95ac19
			return (-1);
Packit Service 95ac19
		}
Packit Service 95ac19
		if (arg == NULL ||
Packit Service 95ac19
		    ((flag != '-' ) &&
Packit Service 95ac19
		    /* neither a - nor a + (if + allowed) */
Packit Service 95ac19
		    (!(go->flags & GF_PLUSOPT) || flag != '+')) ||
Packit Service 95ac19
		    (c = arg[1]) == '\0') {
Packit Service 95ac19
			go->p = 0;
Packit Service 95ac19
			return (-1);
Packit Service 95ac19
		}
Packit Service 95ac19
		go->optind++;
Packit Service 95ac19
		go->info &= ~(GI_MINUS|GI_PLUS);
Packit Service 95ac19
		go->info |= flag == '-' ? GI_MINUS : GI_PLUS;
Packit Service 95ac19
	}
Packit Service 95ac19
	go->p++;
Packit Service 95ac19
	if (ctype(c, C_QUEST | C_COLON | C_HASH) || c == ';' || c == ',' ||
Packit Service 95ac19
	    !(o = cstrchr(optionsp, c))) {
Packit Service 95ac19
		if (optionsp[0] == ':') {
Packit Service 95ac19
			go->buf[0] = c;
Packit Service 95ac19
			go->optarg = go->buf;
Packit Service 95ac19
		} else {
Packit Service 95ac19
			warningf(true, Tf_optfoo,
Packit Service 95ac19
			    (go->flags & GF_NONAME) ? "" : argv[0],
Packit Service 95ac19
			    (go->flags & GF_NONAME) ? "" : Tcolsp,
Packit Service 95ac19
			    c, Tunknown_option);
Packit Service 95ac19
			if (go->flags & GF_ERROR)
Packit Service 95ac19
				bi_errorfz();
Packit Service 95ac19
		}
Packit Service 95ac19
		return ('?');
Packit Service 95ac19
	}
Packit Service 95ac19
	/**
Packit Service 95ac19
	 * : means argument must be present, may be part of option argument
Packit Service 95ac19
	 *   or the next argument
Packit Service 95ac19
	 * ; same as : but argument may be missing
Packit Service 95ac19
	 * , means argument is part of option argument, and may be null.
Packit Service 95ac19
	 */
Packit Service 95ac19
	if (*++o == ':' || *o == ';') {
Packit Service 95ac19
		if (argv[go->optind - 1][go->p])
Packit Service 95ac19
			go->optarg = argv[go->optind - 1] + go->p;
Packit Service 95ac19
		else if (argv[go->optind])
Packit Service 95ac19
			go->optarg = argv[go->optind++];
Packit Service 95ac19
		else if (*o == ';')
Packit Service 95ac19
			go->optarg = NULL;
Packit Service 95ac19
		else {
Packit Service 95ac19
			if (optionsp[0] == ':') {
Packit Service 95ac19
				go->buf[0] = c;
Packit Service 95ac19
				go->optarg = go->buf;
Packit Service 95ac19
				return (':');
Packit Service 95ac19
			}
Packit Service 95ac19
			warningf(true, Tf_optfoo,
Packit Service 95ac19
			    (go->flags & GF_NONAME) ? "" : argv[0],
Packit Service 95ac19
			    (go->flags & GF_NONAME) ? "" : Tcolsp,
Packit Service 95ac19
			    c, Treq_arg);
Packit Service 95ac19
			if (go->flags & GF_ERROR)
Packit Service 95ac19
				bi_errorfz();
Packit Service 95ac19
			return ('?');
Packit Service 95ac19
		}
Packit Service 95ac19
		go->p = 0;
Packit Service 95ac19
	} else if (*o == ',') {
Packit Service 95ac19
		/* argument is attached to option character, even if null */
Packit Service 95ac19
		go->optarg = argv[go->optind - 1] + go->p;
Packit Service 95ac19
		go->p = 0;
Packit Service 95ac19
	} else if (*o == '#') {
Packit Service 95ac19
		/*
Packit Service 95ac19
		 * argument is optional and may be attached or unattached
Packit Service 95ac19
		 * but must start with a digit. optarg is set to 0 if the
Packit Service 95ac19
		 * argument is missing.
Packit Service 95ac19
		 */
Packit Service 95ac19
		if (argv[go->optind - 1][go->p]) {
Packit Service 95ac19
			if (ctype(argv[go->optind - 1][go->p], C_DIGIT)) {
Packit Service 95ac19
				go->optarg = argv[go->optind - 1] + go->p;
Packit Service 95ac19
				go->p = 0;
Packit Service 95ac19
			} else
Packit Service 95ac19
				go->optarg = NULL;
Packit Service 95ac19
		} else {
Packit Service 95ac19
			if (argv[go->optind] &&
Packit Service 95ac19
			    ctype(argv[go->optind][0], C_DIGIT)) {
Packit Service 95ac19
				go->optarg = argv[go->optind++];
Packit Service 95ac19
				go->p = 0;
Packit Service 95ac19
			} else
Packit Service 95ac19
				go->optarg = NULL;
Packit Service 95ac19
		}
Packit Service 95ac19
	}
Packit Service 95ac19
	return (c);
Packit Service 95ac19
}
Packit Service 95ac19
Packit Service 95ac19
/*
Packit Service 95ac19
 * print variable/alias value using necessary quotes
Packit Service 95ac19
 * (POSIX says they should be suitable for re-entry...)
Packit Service 95ac19
 * No trailing newline is printed.
Packit Service 95ac19
 */
Packit Service 95ac19
void
Packit Service 95ac19
print_value_quoted(struct shf *shf, const char *s)
Packit Service 95ac19
{
Packit Service 95ac19
	unsigned char c;
Packit Service 95ac19
	const unsigned char *p = (const unsigned char *)s;
Packit Service 95ac19
	bool inquote = true;
Packit Service 95ac19
Packit Service 95ac19
	/* first, check whether any quotes are needed */
Packit Service 95ac19
	while (rtt2asc(c = *p++) >= 32)
Packit Service 95ac19
		if (ctype(c, C_QUOTE | C_SPC))
Packit Service 95ac19
			inquote = false;
Packit Service 95ac19
Packit Service 95ac19
	p = (const unsigned char *)s;
Packit Service 95ac19
	if (c == 0) {
Packit Service 95ac19
		if (inquote) {
Packit Service 95ac19
			/* nope, use the shortcut */
Packit Service 95ac19
			shf_puts(s, shf);
Packit Service 95ac19
			return;
Packit Service 95ac19
		}
Packit Service 95ac19
Packit Service 95ac19
		/* otherwise, quote nicely via state machine */
Packit Service 95ac19
		while ((c = *p++) != 0) {
Packit Service 95ac19
			if (c == '\'') {
Packit Service 95ac19
				/*
Packit Service 95ac19
				 * multiple single quotes or any of them
Packit Service 95ac19
				 * at the beginning of a string look nicer
Packit Service 95ac19
				 * this way than when simply substituting
Packit Service 95ac19
				 */
Packit Service 95ac19
				if (inquote) {
Packit Service 95ac19
					shf_putc('\'', shf);
Packit Service 95ac19
					inquote = false;
Packit Service 95ac19
				}
Packit Service 95ac19
				shf_putc('\\', shf);
Packit Service 95ac19
			} else if (!inquote) {
Packit Service 95ac19
				shf_putc('\'', shf);
Packit Service 95ac19
				inquote = true;
Packit Service 95ac19
			}
Packit Service 95ac19
			shf_putc(c, shf);
Packit Service 95ac19
		}
Packit Service 95ac19
	} else {
Packit Service 95ac19
		unsigned int wc;
Packit Service 95ac19
		size_t n;
Packit Service 95ac19
Packit Service 95ac19
		/* use $'...' quote format */
Packit Service 95ac19
		shf_putc('$', shf);
Packit Service 95ac19
		shf_putc('\'', shf);
Packit Service 95ac19
		while ((c = *p) != 0) {
Packit Service 95ac19
#ifndef MKSH_EBCDIC
Packit Service 95ac19
			if (c >= 0xC2) {
Packit Service 95ac19
				n = utf_mbtowc(&wc, (const char *)p);
Packit Service 95ac19
				if (n != (size_t)-1) {
Packit Service 95ac19
					p += n;
Packit Service 95ac19
					shf_fprintf(shf, "\\u%04X", wc);
Packit Service 95ac19
					continue;
Packit Service 95ac19
				}
Packit Service 95ac19
			}
Packit Service 95ac19
#endif
Packit Service 95ac19
			++p;
Packit Service 95ac19
			switch (c) {
Packit Service 95ac19
			/* see unbksl() in this file for comments */
Packit Service 95ac19
			case KSH_BEL:
Packit Service 95ac19
				c = 'a';
Packit Service 95ac19
				if (0)
Packit Service 95ac19
					/* FALLTHROUGH */
Packit Service 95ac19
			case '\b':
Packit Service 95ac19
				  c = 'b';
Packit Service 95ac19
				if (0)
Packit Service 95ac19
					/* FALLTHROUGH */
Packit Service 95ac19
			case '\f':
Packit Service 95ac19
				  c = 'f';
Packit Service 95ac19
				if (0)
Packit Service 95ac19
					/* FALLTHROUGH */
Packit Service 95ac19
			case '\n':
Packit Service 95ac19
				  c = 'n';
Packit Service 95ac19
				if (0)
Packit Service 95ac19
					/* FALLTHROUGH */
Packit Service 95ac19
			case '\r':
Packit Service 95ac19
				  c = 'r';
Packit Service 95ac19
				if (0)
Packit Service 95ac19
					/* FALLTHROUGH */
Packit Service 95ac19
			case '\t':
Packit Service 95ac19
				  c = 't';
Packit Service 95ac19
				if (0)
Packit Service 95ac19
					/* FALLTHROUGH */
Packit Service 95ac19
			case KSH_VTAB:
Packit Service 95ac19
				  c = 'v';
Packit Service 95ac19
				if (0)
Packit Service 95ac19
					/* FALLTHROUGH */
Packit Service 95ac19
			case KSH_ESC:
Packit Service 95ac19
				/* take E not e because \e is \ in *roff */
Packit Service 95ac19
				  c = 'E';
Packit Service 95ac19
				/* FALLTHROUGH */
Packit Service 95ac19
			case '\\':
Packit Service 95ac19
				shf_putc('\\', shf);
Packit Service 95ac19
Packit Service 95ac19
				if (0)
Packit Service 95ac19
					/* FALLTHROUGH */
Packit Service 95ac19
			default:
Packit Service 95ac19
#if defined(MKSH_EBCDIC) || defined(MKSH_FAUX_EBCDIC)
Packit Service 95ac19
				  if (ksh_isctrl(c))
Packit Service 95ac19
#else
Packit Service 95ac19
				  if (!ctype(c, C_PRINT))
Packit Service 95ac19
#endif
Packit Service 95ac19
				    {
Packit Service 95ac19
					/* FALLTHROUGH */
Packit Service 95ac19
			case '\'':
Packit Service 95ac19
					shf_fprintf(shf, "\\%03o", c);
Packit Service 95ac19
					break;
Packit Service 95ac19
				}
Packit Service 95ac19
Packit Service 95ac19
				shf_putc(c, shf);
Packit Service 95ac19
				break;
Packit Service 95ac19
			}
Packit Service 95ac19
		}
Packit Service 95ac19
		inquote = true;
Packit Service 95ac19
	}
Packit Service 95ac19
	if (inquote)
Packit Service 95ac19
		shf_putc('\'', shf);
Packit Service 95ac19
}
Packit Service 95ac19
Packit Service 95ac19
/*
Packit Service 95ac19
 * Print things in columns and rows - func() is called to format
Packit Service 95ac19
 * the i-th element
Packit Service 95ac19
 */
Packit Service 95ac19
void
Packit Service 95ac19
print_columns(struct columnise_opts *opts, unsigned int n,
Packit Service 95ac19
    void (*func)(char *, size_t, unsigned int, const void *),
Packit Service 95ac19
    const void *arg, size_t max_oct, size_t max_colz)
Packit Service 95ac19
{
Packit Service 95ac19
	unsigned int i, r = 0, c, rows, cols, nspace, max_col;
Packit Service 95ac19
	char *str;
Packit Service 95ac19
Packit Service 95ac19
	if (!n)
Packit Service 95ac19
		return;
Packit Service 95ac19
Packit Service 95ac19
	if (max_colz > 2147483646) {
Packit Service 95ac19
#ifndef MKSH_SMALL
Packit Service 95ac19
		internal_warningf("print_columns called with %s=%zu >= INT_MAX",
Packit Service 95ac19
		    "max_col", max_colz);
Packit Service 95ac19
#endif
Packit Service 95ac19
		return;
Packit Service 95ac19
	}
Packit Service 95ac19
	max_col = (unsigned int)max_colz;
Packit Service 95ac19
Packit Service 95ac19
	if (max_oct > 2147483646) {
Packit Service 95ac19
#ifndef MKSH_SMALL
Packit Service 95ac19
		internal_warningf("print_columns called with %s=%zu >= INT_MAX",
Packit Service 95ac19
		    "max_oct", max_oct);
Packit Service 95ac19
#endif
Packit Service 95ac19
		return;
Packit Service 95ac19
	}
Packit Service 95ac19
	++max_oct;
Packit Service 95ac19
	str = alloc(max_oct, ATEMP);
Packit Service 95ac19
Packit Service 95ac19
	/*
Packit Service 95ac19
	 * We use (max_col + 2) to consider the separator space.
Packit Service 95ac19
	 * Note that no spaces are printed after the last column
Packit Service 95ac19
	 * to avoid problems with terminals that have auto-wrap,
Packit Service 95ac19
	 * but we need to also take this into account in x_cols.
Packit Service 95ac19
	 */
Packit Service 95ac19
	cols = (x_cols + 1) / (max_col + 2);
Packit Service 95ac19
Packit Service 95ac19
	/* if we can only print one column anyway, skip the goo */
Packit Service 95ac19
	if (cols < 2) {
Packit Service 95ac19
		goto prcols_easy;
Packit Service 95ac19
		while (r < n) {
Packit Service 95ac19
			shf_putc(opts->linesep, opts->shf);
Packit Service 95ac19
 prcols_easy:
Packit Service 95ac19
			(*func)(str, max_oct, r++, arg);
Packit Service 95ac19
			shf_puts(str, opts->shf);
Packit Service 95ac19
		}
Packit Service 95ac19
		goto out;
Packit Service 95ac19
	}
Packit Service 95ac19
Packit Service 95ac19
	rows = (n + cols - 1) / cols;
Packit Service 95ac19
	if (opts->prefcol && cols > rows) {
Packit Service 95ac19
		cols = rows;
Packit Service 95ac19
		rows = (n + cols - 1) / cols;
Packit Service 95ac19
	}
Packit Service 95ac19
Packit Service 95ac19
	nspace = (x_cols - max_col * cols) / cols;
Packit Service 95ac19
	if (nspace < 2)
Packit Service 95ac19
		nspace = 2;
Packit Service 95ac19
	max_col = -max_col;
Packit Service 95ac19
	goto prcols_hard;
Packit Service 95ac19
	while (r < rows) {
Packit Service 95ac19
		shf_putchar(opts->linesep, opts->shf);
Packit Service 95ac19
 prcols_hard:
Packit Service 95ac19
		for (c = 0; c < cols; c++) {
Packit Service 95ac19
			if ((i = c * rows + r) >= n)
Packit Service 95ac19
				break;
Packit Service 95ac19
			(*func)(str, max_oct, i, arg);
Packit Service 95ac19
			if (i + rows >= n)
Packit Service 95ac19
				shf_puts(str, opts->shf);
Packit Service 95ac19
			else
Packit Service 95ac19
				shf_fprintf(opts->shf, "%*s%*s",
Packit Service 95ac19
				    (int)max_col, str, (int)nspace, null);
Packit Service 95ac19
		}
Packit Service 95ac19
		++r;
Packit Service 95ac19
	}
Packit Service 95ac19
 out:
Packit Service 95ac19
	if (opts->do_last)
Packit Service 95ac19
		shf_putchar(opts->linesep, opts->shf);
Packit Service 95ac19
	afree(str, ATEMP);
Packit Service 95ac19
}
Packit Service 95ac19
Packit Service 95ac19
/* strip all NUL bytes from buf; output is NUL-terminated if stripped */
Packit Service 95ac19
void
Packit Service 95ac19
strip_nuls(char *buf, size_t len)
Packit Service 95ac19
{
Packit Service 95ac19
	char *cp, *dp, *ep;
Packit Service 95ac19
Packit Service 95ac19
	if (!len || !(dp = memchr(buf, '\0', len)))
Packit Service 95ac19
		return;
Packit Service 95ac19
Packit Service 95ac19
	ep = buf + len;
Packit Service 95ac19
	cp = dp;
Packit Service 95ac19
Packit Service 95ac19
 cp_has_nul_byte:
Packit Service 95ac19
	while (cp++ < ep && *cp == '\0')
Packit Service 95ac19
		;	/* nothing */
Packit Service 95ac19
	while (cp < ep && *cp != '\0')
Packit Service 95ac19
		*dp++ = *cp++;
Packit Service 95ac19
	if (cp < ep)
Packit Service 95ac19
		goto cp_has_nul_byte;
Packit Service 95ac19
Packit Service 95ac19
	*dp = '\0';
Packit Service 95ac19
}
Packit Service 95ac19
Packit Service 95ac19
/*
Packit Service 95ac19
 * Like read(2), but if read fails due to non-blocking flag,
Packit Service 95ac19
 * resets flag and restarts read.
Packit Service 95ac19
 */
Packit Service 95ac19
ssize_t
Packit Service 95ac19
blocking_read(int fd, char *buf, size_t nbytes)
Packit Service 95ac19
{
Packit Service 95ac19
	ssize_t ret;
Packit Service 95ac19
	bool tried_reset = false;
Packit Service 95ac19
Packit Service 95ac19
	while ((ret = read(fd, buf, nbytes)) < 0) {
Packit Service 95ac19
		if (!tried_reset && errno == EAGAIN) {
Packit Service 95ac19
			if (reset_nonblock(fd) > 0) {
Packit Service 95ac19
				tried_reset = true;
Packit Service 95ac19
				continue;
Packit Service 95ac19
			}
Packit Service 95ac19
			errno = EAGAIN;
Packit Service 95ac19
		}
Packit Service 95ac19
		break;
Packit Service 95ac19
	}
Packit Service 95ac19
	return (ret);
Packit Service 95ac19
}
Packit Service 95ac19
Packit Service 95ac19
/*
Packit Service 95ac19
 * Reset the non-blocking flag on the specified file descriptor.
Packit Service 95ac19
 * Returns -1 if there was an error, 0 if non-blocking wasn't set,
Packit Service 95ac19
 * 1 if it was.
Packit Service 95ac19
 */
Packit Service 95ac19
int
Packit Service 95ac19
reset_nonblock(int fd)
Packit Service 95ac19
{
Packit Service 95ac19
	int flags;
Packit Service 95ac19
Packit Service 95ac19
	if ((flags = fcntl(fd, F_GETFL, 0)) < 0)
Packit Service 95ac19
		return (-1);
Packit Service 95ac19
	if (!(flags & O_NONBLOCK))
Packit Service 95ac19
		return (0);
Packit Service 95ac19
	flags &= ~O_NONBLOCK;
Packit Service 95ac19
	if (fcntl(fd, F_SETFL, flags) < 0)
Packit Service 95ac19
		return (-1);
Packit Service 95ac19
	return (1);
Packit Service 95ac19
}
Packit Service 95ac19
Packit Service 95ac19
/* getcwd(3) equivalent, allocates from ATEMP but doesn't resize */
Packit Service 95ac19
char *
Packit Service 95ac19
ksh_get_wd(void)
Packit Service 95ac19
{
Packit Service 95ac19
#ifdef MKSH__NO_PATH_MAX
Packit Service 95ac19
	char *rv, *cp;
Packit Service 95ac19
Packit Service 95ac19
	if ((cp = get_current_dir_name())) {
Packit Service 95ac19
		strdupx(rv, cp, ATEMP);
Packit Service 95ac19
		free_gnu_gcdn(cp);
Packit Service 95ac19
	} else
Packit Service 95ac19
		rv = NULL;
Packit Service 95ac19
#else
Packit Service 95ac19
	char *rv;
Packit Service 95ac19
Packit Service 95ac19
	if (!getcwd((rv = alloc(PATH_MAX + 1, ATEMP)), PATH_MAX)) {
Packit Service 95ac19
		afree(rv, ATEMP);
Packit Service 95ac19
		rv = NULL;
Packit Service 95ac19
	}
Packit Service 95ac19
#endif
Packit Service 95ac19
Packit Service 95ac19
	return (rv);
Packit Service 95ac19
}
Packit Service 95ac19
Packit Service 95ac19
#ifndef ELOOP
Packit Service 95ac19
#define ELOOP		E2BIG
Packit Service 95ac19
#endif
Packit Service 95ac19
Packit Service 95ac19
char *
Packit Service 95ac19
do_realpath(const char *upath)
Packit Service 95ac19
{
Packit Service 95ac19
	char *xp, *ip, *tp, *ipath, *ldest = NULL;
Packit Service 95ac19
	XString xs;
Packit Service 95ac19
	size_t pos, len;
Packit Service 95ac19
	int llen;
Packit Service 95ac19
	struct stat sb;
Packit Service 95ac19
#ifdef MKSH__NO_PATH_MAX
Packit Service 95ac19
	size_t ldestlen = 0;
Packit Service 95ac19
#define pathlen sb.st_size
Packit Service 95ac19
#define pathcnd (ldestlen < (pathlen + 1))
Packit Service 95ac19
#else
Packit Service 95ac19
#define pathlen PATH_MAX
Packit Service 95ac19
#define pathcnd (!ldest)
Packit Service 95ac19
#endif
Packit Service 95ac19
	/* max. recursion depth */
Packit Service 95ac19
	int symlinks = 32;
Packit Service 95ac19
Packit Service 95ac19
	if (mksh_abspath(upath)) {
Packit Service 95ac19
		/* upath is an absolute pathname */
Packit Service 95ac19
		strdupx(ipath, upath, ATEMP);
Packit Service 95ac19
#ifdef MKSH_DOSPATH
Packit Service 95ac19
	} else if (mksh_drvltr(upath)) {
Packit Service 95ac19
		/* upath is a drive-relative pathname */
Packit Service 95ac19
		if (getdrvwd(&ldest, ord(*upath)))
Packit Service 95ac19
			return (NULL);
Packit Service 95ac19
		/* A:foo -> A:/cwd/foo; A: -> A:/cwd */
Packit Service 95ac19
		ipath = shf_smprintf(Tf_sss, ldest,
Packit Service 95ac19
		    upath[2] ? "/" : "", upath + 2);
Packit Service 95ac19
#endif
Packit Service 95ac19
	} else {
Packit Service 95ac19
		/* upath is a relative pathname, prepend cwd */
Packit Service 95ac19
		if ((tp = ksh_get_wd()) == NULL || !mksh_abspath(tp))
Packit Service 95ac19
			return (NULL);
Packit Service 95ac19
		ipath = shf_smprintf(Tf_sss, tp, "/", upath);
Packit Service 95ac19
		afree(tp, ATEMP);
Packit Service 95ac19
	}
Packit Service 95ac19
Packit Service 95ac19
	/* ipath and upath are in memory at the same time -> unchecked */
Packit Service 95ac19
	Xinit(xs, xp, strlen(ip = ipath) + 1, ATEMP);
Packit Service 95ac19
Packit Service 95ac19
	/* now jump into the deep of the loop */
Packit Service 95ac19
	goto beginning_of_a_pathname;
Packit Service 95ac19
Packit Service 95ac19
	while (*ip) {
Packit Service 95ac19
		/* skip slashes in input */
Packit Service 95ac19
		while (mksh_cdirsep(*ip))
Packit Service 95ac19
			++ip;
Packit Service 95ac19
		if (!*ip)
Packit Service 95ac19
			break;
Packit Service 95ac19
Packit Service 95ac19
		/* get next pathname component from input */
Packit Service 95ac19
		tp = ip;
Packit Service 95ac19
		while (*ip && !mksh_cdirsep(*ip))
Packit Service 95ac19
			++ip;
Packit Service 95ac19
		len = ip - tp;
Packit Service 95ac19
Packit Service 95ac19
		/* check input for "." and ".." */
Packit Service 95ac19
		if (tp[0] == '.') {
Packit Service 95ac19
			if (len == 1)
Packit Service 95ac19
				/* just continue with the next one */
Packit Service 95ac19
				continue;
Packit Service 95ac19
			else if (len == 2 && tp[1] == '.') {
Packit Service 95ac19
				/* strip off last pathname component */
Packit Service 95ac19
				/*XXX consider a rooted pathname */
Packit Service 95ac19
				while (xp > Xstring(xs, xp))
Packit Service 95ac19
					if (mksh_cdirsep(*--xp))
Packit Service 95ac19
						break;
Packit Service 95ac19
				/* then continue with the next one */
Packit Service 95ac19
				continue;
Packit Service 95ac19
			}
Packit Service 95ac19
		}
Packit Service 95ac19
Packit Service 95ac19
		/* store output position away, then append slash to output */
Packit Service 95ac19
		pos = Xsavepos(xs, xp);
Packit Service 95ac19
		/* 1 for the '/' and len + 1 for tp and the NUL from below */
Packit Service 95ac19
		XcheckN(xs, xp, 1 + len + 1);
Packit Service 95ac19
		Xput(xs, xp, '/');
Packit Service 95ac19
Packit Service 95ac19
		/* append next pathname component to output */
Packit Service 95ac19
		memcpy(xp, tp, len);
Packit Service 95ac19
		xp += len;
Packit Service 95ac19
		*xp = '\0';
Packit Service 95ac19
Packit Service 95ac19
		/* lstat the current output, see if it's a symlink */
Packit Service 95ac19
		if (mksh_lstat(Xstring(xs, xp), &sb)) {
Packit Service 95ac19
			/* lstat failed */
Packit Service 95ac19
			if (errno == ENOENT) {
Packit Service 95ac19
				/* because the pathname does not exist */
Packit Service 95ac19
				while (mksh_cdirsep(*ip))
Packit Service 95ac19
					/* skip any trailing slashes */
Packit Service 95ac19
					++ip;
Packit Service 95ac19
				/* no more components left? */
Packit Service 95ac19
				if (!*ip)
Packit Service 95ac19
					/* we can still return successfully */
Packit Service 95ac19
					break;
Packit Service 95ac19
				/* more components left? fall through */
Packit Service 95ac19
			}
Packit Service 95ac19
			/* not ENOENT or not at the end of ipath */
Packit Service 95ac19
			goto notfound;
Packit Service 95ac19
		}
Packit Service 95ac19
Packit Service 95ac19
		/* check if we encountered a symlink? */
Packit Service 95ac19
		if (S_ISLNK(sb.st_mode)) {
Packit Service 95ac19
#ifndef MKSH__NO_SYMLINK
Packit Service 95ac19
			/* reached maximum recursion depth? */
Packit Service 95ac19
			if (!symlinks--) {
Packit Service 95ac19
				/* yep, prevent infinite loops */
Packit Service 95ac19
				errno = ELOOP;
Packit Service 95ac19
				goto notfound;
Packit Service 95ac19
			}
Packit Service 95ac19
Packit Service 95ac19
			/* get symlink(7) target */
Packit Service 95ac19
			if (pathcnd) {
Packit Service 95ac19
#ifdef MKSH__NO_PATH_MAX
Packit Service 95ac19
				if (notoktoadd(pathlen, 1)) {
Packit Service 95ac19
					errno = ENAMETOOLONG;
Packit Service 95ac19
					goto notfound;
Packit Service 95ac19
				}
Packit Service 95ac19
#endif
Packit Service 95ac19
				ldest = aresize(ldest, pathlen + 1, ATEMP);
Packit Service 95ac19
			}
Packit Service 95ac19
			llen = readlink(Xstring(xs, xp), ldest, pathlen);
Packit Service 95ac19
			if (llen < 0)
Packit Service 95ac19
				/* oops... */
Packit Service 95ac19
				goto notfound;
Packit Service 95ac19
			ldest[llen] = '\0';
Packit Service 95ac19
Packit Service 95ac19
			/*
Packit Service 95ac19
			 * restart if symlink target is an absolute path,
Packit Service 95ac19
			 * otherwise continue with currently resolved prefix
Packit Service 95ac19
			 */
Packit Service 95ac19
#ifdef MKSH_DOSPATH
Packit Service 95ac19
 assemble_symlink:
Packit Service 95ac19
#endif
Packit Service 95ac19
			/* append rest of current input path to link target */
Packit Service 95ac19
			tp = shf_smprintf(Tf_sss, ldest, *ip ? "/" : "", ip);
Packit Service 95ac19
			afree(ipath, ATEMP);
Packit Service 95ac19
			ip = ipath = tp;
Packit Service 95ac19
			if (!mksh_abspath(ipath)) {
Packit Service 95ac19
#ifdef MKSH_DOSPATH
Packit Service 95ac19
				/* symlink target might be drive-relative */
Packit Service 95ac19
				if (mksh_drvltr(ipath)) {
Packit Service 95ac19
					if (getdrvwd(&ldest, ord(*ipath)))
Packit Service 95ac19
						goto notfound;
Packit Service 95ac19
					ip += 2;
Packit Service 95ac19
					goto assemble_symlink;
Packit Service 95ac19
				}
Packit Service 95ac19
#endif
Packit Service 95ac19
				/* symlink target is a relative path */
Packit Service 95ac19
				xp = Xrestpos(xs, xp, pos);
Packit Service 95ac19
			} else
Packit Service 95ac19
#endif
Packit Service 95ac19
			  {
Packit Service 95ac19
				/* symlink target is an absolute path */
Packit Service 95ac19
				xp = Xstring(xs, xp);
Packit Service 95ac19
 beginning_of_a_pathname:
Packit Service 95ac19
				/* assert: mksh_abspath(ip == ipath) */
Packit Service 95ac19
				/* assert: xp == xs.beg => start of path */
Packit Service 95ac19
Packit Service 95ac19
				/* exactly two leading slashes? (SUSv4 3.266) */
Packit Service 95ac19
				if (ip[1] == ip[0] && !mksh_cdirsep(ip[2])) {
Packit Service 95ac19
					/* keep them, e.g. for UNC pathnames */
Packit Service 95ac19
					Xput(xs, xp, '/');
Packit Service 95ac19
				}
Packit Service 95ac19
#ifdef MKSH_DOSPATH
Packit Service 95ac19
				/* drive letter? */
Packit Service 95ac19
				if (mksh_drvltr(ip)) {
Packit Service 95ac19
					/* keep it */
Packit Service 95ac19
					Xput(xs, xp, *ip++);
Packit Service 95ac19
					Xput(xs, xp, *ip++);
Packit Service 95ac19
				}
Packit Service 95ac19
#endif
Packit Service 95ac19
			}
Packit Service 95ac19
		}
Packit Service 95ac19
		/* otherwise (no symlink) merely go on */
Packit Service 95ac19
	}
Packit Service 95ac19
Packit Service 95ac19
	/*
Packit Service 95ac19
	 * either found the target and successfully resolved it,
Packit Service 95ac19
	 * or found its parent directory and may create it
Packit Service 95ac19
	 */
Packit Service 95ac19
	if (Xlength(xs, xp) == 0)
Packit Service 95ac19
		/*
Packit Service 95ac19
		 * if the resolved pathname is "", make it "/",
Packit Service 95ac19
		 * otherwise do not add a trailing slash
Packit Service 95ac19
		 */
Packit Service 95ac19
		Xput(xs, xp, '/');
Packit Service 95ac19
	Xput(xs, xp, '\0');
Packit Service 95ac19
Packit Service 95ac19
	/*
Packit Service 95ac19
	 * if source path had a trailing slash, check if target path
Packit Service 95ac19
	 * is not a non-directory existing file
Packit Service 95ac19
	 */
Packit Service 95ac19
	if (ip > ipath && mksh_cdirsep(ip[-1])) {
Packit Service 95ac19
		if (stat(Xstring(xs, xp), &sb)) {
Packit Service 95ac19
			if (errno != ENOENT)
Packit Service 95ac19
				goto notfound;
Packit Service 95ac19
		} else if (!S_ISDIR(sb.st_mode)) {
Packit Service 95ac19
			errno = ENOTDIR;
Packit Service 95ac19
			goto notfound;
Packit Service 95ac19
		}
Packit Service 95ac19
		/* target now either does not exist or is a directory */
Packit Service 95ac19
	}
Packit Service 95ac19
Packit Service 95ac19
	/* return target path */
Packit Service 95ac19
	afree(ldest, ATEMP);
Packit Service 95ac19
	afree(ipath, ATEMP);
Packit Service 95ac19
	return (Xclose(xs, xp));
Packit Service 95ac19
Packit Service 95ac19
 notfound:
Packit Service 95ac19
	/* save; freeing memory might trash it */
Packit Service 95ac19
	llen = errno;
Packit Service 95ac19
	afree(ldest, ATEMP);
Packit Service 95ac19
	afree(ipath, ATEMP);
Packit Service 95ac19
	Xfree(xs, xp);
Packit Service 95ac19
	errno = llen;
Packit Service 95ac19
	return (NULL);
Packit Service 95ac19
Packit Service 95ac19
#undef pathlen
Packit Service 95ac19
#undef pathcnd
Packit Service 95ac19
}
Packit Service 95ac19
Packit Service 95ac19
/**
Packit Service 95ac19
 *	Makes a filename into result using the following algorithm.
Packit Service 95ac19
 *	- make result NULL
Packit Service 95ac19
 *	- if file starts with '/', append file to result & set cdpathp to NULL
Packit Service 95ac19
 *	- if file starts with ./ or ../ append cwd and file to result
Packit Service 95ac19
 *	  and set cdpathp to NULL
Packit Service 95ac19
 *	- if the first element of cdpathp doesnt start with a '/' xx or '.' xx
Packit Service 95ac19
 *	  then cwd is appended to result.
Packit Service 95ac19
 *	- the first element of cdpathp is appended to result
Packit Service 95ac19
 *	- file is appended to result
Packit Service 95ac19
 *	- cdpathp is set to the start of the next element in cdpathp (or NULL
Packit Service 95ac19
 *	  if there are no more elements.
Packit Service 95ac19
 *	The return value indicates whether a non-null element from cdpathp
Packit Service 95ac19
 *	was appended to result.
Packit Service 95ac19
 */
Packit Service 95ac19
static int
Packit Service 95ac19
make_path(const char *cwd, const char *file,
Packit Service 95ac19
    /* pointer to colon-separated list */
Packit Service 95ac19
    char **cdpathp,
Packit Service 95ac19
    XString *xsp,
Packit Service 95ac19
    int *phys_pathp)
Packit Service 95ac19
{
Packit Service 95ac19
	int rval = 0;
Packit Service 95ac19
	bool use_cdpath = true;
Packit Service 95ac19
	char *plist;
Packit Service 95ac19
	size_t len, plen = 0;
Packit Service 95ac19
	char *xp = Xstring(*xsp, xp);
Packit Service 95ac19
Packit Service 95ac19
	if (!file)
Packit Service 95ac19
		file = null;
Packit Service 95ac19
Packit Service 95ac19
	if (mksh_abspath(file)) {
Packit Service 95ac19
		*phys_pathp = 0;
Packit Service 95ac19
		use_cdpath = false;
Packit Service 95ac19
	} else {
Packit Service 95ac19
		if (file[0] == '.') {
Packit Service 95ac19
			char c = file[1];
Packit Service 95ac19
Packit Service 95ac19
			if (c == '.')
Packit Service 95ac19
				c = file[2];
Packit Service 95ac19
			if (mksh_cdirsep(c) || c == '\0')
Packit Service 95ac19
				use_cdpath = false;
Packit Service 95ac19
		}
Packit Service 95ac19
Packit Service 95ac19
		plist = *cdpathp;
Packit Service 95ac19
		if (!plist)
Packit Service 95ac19
			use_cdpath = false;
Packit Service 95ac19
		else if (use_cdpath) {
Packit Service 95ac19
			char *pend = plist;
Packit Service 95ac19
Packit Service 95ac19
			while (*pend && *pend != MKSH_PATHSEPC)
Packit Service 95ac19
				++pend;
Packit Service 95ac19
			plen = pend - plist;
Packit Service 95ac19
			*cdpathp = *pend ? pend + 1 : NULL;
Packit Service 95ac19
		}
Packit Service 95ac19
Packit Service 95ac19
		if ((!use_cdpath || !plen || !mksh_abspath(plist)) &&
Packit Service 95ac19
		    (cwd && *cwd)) {
Packit Service 95ac19
			len = strlen(cwd);
Packit Service 95ac19
			XcheckN(*xsp, xp, len);
Packit Service 95ac19
			memcpy(xp, cwd, len);
Packit Service 95ac19
			xp += len;
Packit Service 95ac19
			if (!mksh_cdirsep(cwd[len - 1]))
Packit Service 95ac19
				Xput(*xsp, xp, '/');
Packit Service 95ac19
		}
Packit Service 95ac19
		*phys_pathp = Xlength(*xsp, xp);
Packit Service 95ac19
		if (use_cdpath && plen) {
Packit Service 95ac19
			XcheckN(*xsp, xp, plen);
Packit Service 95ac19
			memcpy(xp, plist, plen);
Packit Service 95ac19
			xp += plen;
Packit Service 95ac19
			if (!mksh_cdirsep(plist[plen - 1]))
Packit Service 95ac19
				Xput(*xsp, xp, '/');
Packit Service 95ac19
			rval = 1;
Packit Service 95ac19
		}
Packit Service 95ac19
	}
Packit Service 95ac19
Packit Service 95ac19
	len = strlen(file) + 1;
Packit Service 95ac19
	XcheckN(*xsp, xp, len);
Packit Service 95ac19
	memcpy(xp, file, len);
Packit Service 95ac19
Packit Service 95ac19
	if (!use_cdpath)
Packit Service 95ac19
		*cdpathp = NULL;
Packit Service 95ac19
Packit Service 95ac19
	return (rval);
Packit Service 95ac19
}
Packit Service 95ac19
Packit Service 95ac19
/*-
Packit Service 95ac19
 * Simplify pathnames containing "." and ".." entries.
Packit Service 95ac19
 *
Packit Service 95ac19
 * simplify_path(this)			= that
Packit Service 95ac19
 * /a/b/c/./../d/..			/a/b
Packit Service 95ac19
 * //./C/foo/bar/../baz			//C/foo/baz
Packit Service 95ac19
 * /foo/				/foo
Packit Service 95ac19
 * /foo/../../bar			/bar
Packit Service 95ac19
 * /foo/./blah/..			/foo
Packit Service 95ac19
 * .					.
Packit Service 95ac19
 * ..					..
Packit Service 95ac19
 * ./foo				foo
Packit Service 95ac19
 * foo/../../../bar			../../bar
Packit Service 95ac19
 * C:/foo/../..				C:/
Packit Service 95ac19
 * C:.					C:
Packit Service 95ac19
 * C:..					C:..
Packit Service 95ac19
 * C:foo/../../blah			C:../blah
Packit Service 95ac19
 *
Packit Service 95ac19
 * XXX consider a rooted pathname: we cannot really 'cd ..' for
Packit Service 95ac19
 * pathnames like: '/', 'c:/', '//foo', '//foo/', '/@unixroot/'
Packit Service 95ac19
 * (no effect), 'c:', 'c:.' (effect is retaining the '../') but
Packit Service 95ac19
 * we need to honour this throughout the shell
Packit Service 95ac19
 */
Packit Service 95ac19
void
Packit Service 95ac19
simplify_path(char *p)
Packit Service 95ac19
{
Packit Service 95ac19
	char *dp, *ip, *sp, *tp;
Packit Service 95ac19
	size_t len;
Packit Service 95ac19
	bool needslash;
Packit Service 95ac19
#ifdef MKSH_DOSPATH
Packit Service 95ac19
	bool needdot = true;
Packit Service 95ac19
Packit Service 95ac19
	/* keep drive letter */
Packit Service 95ac19
	if (mksh_drvltr(p)) {
Packit Service 95ac19
		p += 2;
Packit Service 95ac19
		needdot = false;
Packit Service 95ac19
	}
Packit Service 95ac19
#else
Packit Service 95ac19
#define needdot true
Packit Service 95ac19
#endif
Packit Service 95ac19
Packit Service 95ac19
	switch (*p) {
Packit Service 95ac19
	case 0:
Packit Service 95ac19
		return;
Packit Service 95ac19
	case '/':
Packit Service 95ac19
#ifdef MKSH_DOSPATH
Packit Service 95ac19
	case '\\':
Packit Service 95ac19
#endif
Packit Service 95ac19
		/* exactly two leading slashes? (SUSv4 3.266) */
Packit Service 95ac19
		if (p[1] == p[0] && !mksh_cdirsep(p[2]))
Packit Service 95ac19
			/* keep them, e.g. for UNC pathnames */
Packit Service 95ac19
			++p;
Packit Service 95ac19
		needslash = true;
Packit Service 95ac19
		break;
Packit Service 95ac19
	default:
Packit Service 95ac19
		needslash = false;
Packit Service 95ac19
	}
Packit Service 95ac19
	dp = ip = sp = p;
Packit Service 95ac19
Packit Service 95ac19
	while (*ip) {
Packit Service 95ac19
		/* skip slashes in input */
Packit Service 95ac19
		while (mksh_cdirsep(*ip))
Packit Service 95ac19
			++ip;
Packit Service 95ac19
		if (!*ip)
Packit Service 95ac19
			break;
Packit Service 95ac19
Packit Service 95ac19
		/* get next pathname component from input */
Packit Service 95ac19
		tp = ip;
Packit Service 95ac19
		while (*ip && !mksh_cdirsep(*ip))
Packit Service 95ac19
			++ip;
Packit Service 95ac19
		len = ip - tp;
Packit Service 95ac19
Packit Service 95ac19
		/* check input for "." and ".." */
Packit Service 95ac19
		if (tp[0] == '.') {
Packit Service 95ac19
			if (len == 1)
Packit Service 95ac19
				/* just continue with the next one */
Packit Service 95ac19
				continue;
Packit Service 95ac19
			else if (len == 2 && tp[1] == '.') {
Packit Service 95ac19
				/* parent level, but how? (see above) */
Packit Service 95ac19
				if (mksh_abspath(p))
Packit Service 95ac19
					/* absolute path, only one way */
Packit Service 95ac19
					goto strip_last_component;
Packit Service 95ac19
				else if (dp > sp) {
Packit Service 95ac19
					/* relative path, with subpaths */
Packit Service 95ac19
					needslash = false;
Packit Service 95ac19
 strip_last_component:
Packit Service 95ac19
					/* strip off last pathname component */
Packit Service 95ac19
					while (dp > sp)
Packit Service 95ac19
						if (mksh_cdirsep(*--dp))
Packit Service 95ac19
							break;
Packit Service 95ac19
				} else {
Packit Service 95ac19
					/* relative path, at its beginning */
Packit Service 95ac19
					if (needslash)
Packit Service 95ac19
						/* or already dotdot-slash'd */
Packit Service 95ac19
						*dp++ = '/';
Packit Service 95ac19
					/* keep dotdot-slash if not absolute */
Packit Service 95ac19
					*dp++ = '.';
Packit Service 95ac19
					*dp++ = '.';
Packit Service 95ac19
					needslash = true;
Packit Service 95ac19
					sp = dp;
Packit Service 95ac19
				}
Packit Service 95ac19
				/* then continue with the next one */
Packit Service 95ac19
				continue;
Packit Service 95ac19
			}
Packit Service 95ac19
		}
Packit Service 95ac19
Packit Service 95ac19
		if (needslash)
Packit Service 95ac19
			*dp++ = '/';
Packit Service 95ac19
Packit Service 95ac19
		/* append next pathname component to output */
Packit Service 95ac19
		memmove(dp, tp, len);
Packit Service 95ac19
		dp += len;
Packit Service 95ac19
Packit Service 95ac19
		/* append slash if we continue */
Packit Service 95ac19
		needslash = true;
Packit Service 95ac19
		/* try next component */
Packit Service 95ac19
	}
Packit Service 95ac19
	if (dp == p) {
Packit Service 95ac19
		/* empty path -> dot (or slash, when absolute) */
Packit Service 95ac19
		if (needslash)
Packit Service 95ac19
			*dp++ = '/';
Packit Service 95ac19
		else if (needdot)
Packit Service 95ac19
			*dp++ = '.';
Packit Service 95ac19
	}
Packit Service 95ac19
	*dp = '\0';
Packit Service 95ac19
#undef needdot
Packit Service 95ac19
}
Packit Service 95ac19
Packit Service 95ac19
void
Packit Service 95ac19
set_current_wd(const char *nwd)
Packit Service 95ac19
{
Packit Service 95ac19
	char *allocd = NULL;
Packit Service 95ac19
Packit Service 95ac19
	if (nwd == NULL) {
Packit Service 95ac19
		allocd = ksh_get_wd();
Packit Service 95ac19
		nwd = allocd ? allocd : null;
Packit Service 95ac19
	}
Packit Service 95ac19
Packit Service 95ac19
	afree(current_wd, APERM);
Packit Service 95ac19
	strdupx(current_wd, nwd, APERM);
Packit Service 95ac19
Packit Service 95ac19
	afree(allocd, ATEMP);
Packit Service 95ac19
}
Packit Service 95ac19
Packit Service 95ac19
int
Packit Service 95ac19
c_cd(const char **wp)
Packit Service 95ac19
{
Packit Service 95ac19
	int optc, rv, phys_path;
Packit Service 95ac19
	bool physical = tobool(Flag(FPHYSICAL));
Packit Service 95ac19
	/* was a node from cdpath added in? */
Packit Service 95ac19
	int cdnode;
Packit Service 95ac19
	/* show where we went?, error for $PWD */
Packit Service 95ac19
	bool printpath = false, eflag = false;
Packit Service 95ac19
	struct tbl *pwd_s, *oldpwd_s;
Packit Service 95ac19
	XString xs;
Packit Service 95ac19
	char *dir, *allocd = NULL, *tryp, *pwd, *cdpath;
Packit Service 95ac19
Packit Service 95ac19
	while ((optc = ksh_getopt(wp, &builtin_opt, "eLP")) != -1)
Packit Service 95ac19
		switch (optc) {
Packit Service 95ac19
		case 'e':
Packit Service 95ac19
			eflag = true;
Packit Service 95ac19
			break;
Packit Service 95ac19
		case 'L':
Packit Service 95ac19
			physical = false;
Packit Service 95ac19
			break;
Packit Service 95ac19
		case 'P':
Packit Service 95ac19
			physical = true;
Packit Service 95ac19
			break;
Packit Service 95ac19
		case '?':
Packit Service 95ac19
			return (2);
Packit Service 95ac19
		}
Packit Service 95ac19
	wp += builtin_opt.optind;
Packit Service 95ac19
Packit Service 95ac19
	if (Flag(FRESTRICTED)) {
Packit Service 95ac19
		bi_errorf(Tcant_cd);
Packit Service 95ac19
		return (2);
Packit Service 95ac19
	}
Packit Service 95ac19
Packit Service 95ac19
	pwd_s = global(TPWD);
Packit Service 95ac19
	oldpwd_s = global(TOLDPWD);
Packit Service 95ac19
Packit Service 95ac19
	if (!wp[0]) {
Packit Service 95ac19
		/* No arguments - go home */
Packit Service 95ac19
		if ((dir = str_val(global("HOME"))) == null) {
Packit Service 95ac19
			bi_errorf("no home directory (HOME not set)");
Packit Service 95ac19
			return (2);
Packit Service 95ac19
		}
Packit Service 95ac19
	} else if (!wp[1]) {
Packit Service 95ac19
		/* One argument: - or dir */
Packit Service 95ac19
		strdupx(allocd, wp[0], ATEMP);
Packit Service 95ac19
		if (ksh_isdash((dir = allocd))) {
Packit Service 95ac19
			afree(allocd, ATEMP);
Packit Service 95ac19
			allocd = NULL;
Packit Service 95ac19
			dir = str_val(oldpwd_s);
Packit Service 95ac19
			if (dir == null) {
Packit Service 95ac19
				bi_errorf(Tno_OLDPWD);
Packit Service 95ac19
				return (2);
Packit Service 95ac19
			}
Packit Service 95ac19
			printpath = true;
Packit Service 95ac19
		}
Packit Service 95ac19
	} else if (!wp[2]) {
Packit Service 95ac19
		/* Two arguments - substitute arg1 in PWD for arg2 */
Packit Service 95ac19
		size_t ilen, olen, nlen, elen;
Packit Service 95ac19
		char *cp;
Packit Service 95ac19
Packit Service 95ac19
		if (!current_wd[0]) {
Packit Service 95ac19
			bi_errorf("can't determine current directory");
Packit Service 95ac19
			return (2);
Packit Service 95ac19
		}
Packit Service 95ac19
		/*
Packit Service 95ac19
		 * substitute arg1 for arg2 in current path.
Packit Service 95ac19
		 * if the first substitution fails because the cd fails
Packit Service 95ac19
		 * we could try to find another substitution. For now
Packit Service 95ac19
		 * we don't
Packit Service 95ac19
		 */
Packit Service 95ac19
		if ((cp = strstr(current_wd, wp[0])) == NULL) {
Packit Service 95ac19
			bi_errorf(Tbadsubst);
Packit Service 95ac19
			return (2);
Packit Service 95ac19
		}
Packit Service 95ac19
		/*-
Packit Service 95ac19
		 * ilen = part of current_wd before wp[0]
Packit Service 95ac19
		 * elen = part of current_wd after wp[0]
Packit Service 95ac19
		 * because current_wd and wp[1] need to be in memory at the
Packit Service 95ac19
		 * same time beforehand the addition can stay unchecked
Packit Service 95ac19
		 */
Packit Service 95ac19
		ilen = cp - current_wd;
Packit Service 95ac19
		olen = strlen(wp[0]);
Packit Service 95ac19
		nlen = strlen(wp[1]);
Packit Service 95ac19
		elen = strlen(current_wd + ilen + olen) + 1;
Packit Service 95ac19
		dir = allocd = alloc(ilen + nlen + elen, ATEMP);
Packit Service 95ac19
		memcpy(dir, current_wd, ilen);
Packit Service 95ac19
		memcpy(dir + ilen, wp[1], nlen);
Packit Service 95ac19
		memcpy(dir + ilen + nlen, current_wd + ilen + olen, elen);
Packit Service 95ac19
		printpath = true;
Packit Service 95ac19
	} else {
Packit Service 95ac19
		bi_errorf(Ttoo_many_args);
Packit Service 95ac19
		return (2);
Packit Service 95ac19
	}
Packit Service 95ac19
Packit Service 95ac19
#ifdef MKSH_DOSPATH
Packit Service 95ac19
	tryp = NULL;
Packit Service 95ac19
	if (mksh_drvltr(dir) && !mksh_cdirsep(dir[2]) &&
Packit Service 95ac19
	    !getdrvwd(&tryp, ord(*dir))) {
Packit Service 95ac19
		dir = shf_smprintf(Tf_sss, tryp,
Packit Service 95ac19
		    dir[2] ? "/" : "", dir + 2);
Packit Service 95ac19
		afree(tryp, ATEMP);
Packit Service 95ac19
		afree(allocd, ATEMP);
Packit Service 95ac19
		allocd = dir;
Packit Service 95ac19
	}
Packit Service 95ac19
#endif
Packit Service 95ac19
Packit Service 95ac19
#ifdef MKSH__NO_PATH_MAX
Packit Service 95ac19
	/* only a first guess; make_path will enlarge xs if necessary */
Packit Service 95ac19
	XinitN(xs, 1024, ATEMP);
Packit Service 95ac19
#else
Packit Service 95ac19
	XinitN(xs, PATH_MAX, ATEMP);
Packit Service 95ac19
#endif
Packit Service 95ac19
Packit Service 95ac19
	cdpath = str_val(global("CDPATH"));
Packit Service 95ac19
	do {
Packit Service 95ac19
		cdnode = make_path(current_wd, dir, &cdpath, &xs, &phys_path);
Packit Service 95ac19
		if (physical)
Packit Service 95ac19
			rv = chdir(tryp = Xstring(xs, xp) + phys_path);
Packit Service 95ac19
		else {
Packit Service 95ac19
			simplify_path(Xstring(xs, xp));
Packit Service 95ac19
			rv = chdir(tryp = Xstring(xs, xp));
Packit Service 95ac19
		}
Packit Service 95ac19
	} while (rv < 0 && cdpath != NULL);
Packit Service 95ac19
Packit Service 95ac19
	if (rv < 0) {
Packit Service 95ac19
		if (cdnode)
Packit Service 95ac19
			bi_errorf(Tf_sD_s, dir, "bad directory");
Packit Service 95ac19
		else
Packit Service 95ac19
			bi_errorf(Tf_sD_s, tryp, cstrerror(errno));
Packit Service 95ac19
		afree(allocd, ATEMP);
Packit Service 95ac19
		Xfree(xs, xp);
Packit Service 95ac19
		return (2);
Packit Service 95ac19
	}
Packit Service 95ac19
Packit Service 95ac19
	rv = 0;
Packit Service 95ac19
Packit Service 95ac19
	/* allocd (above) => dir, which is no longer used */
Packit Service 95ac19
	afree(allocd, ATEMP);
Packit Service 95ac19
	allocd = NULL;
Packit Service 95ac19
Packit Service 95ac19
	/* Clear out tracked aliases with relative paths */
Packit Service 95ac19
	flushcom(false);
Packit Service 95ac19
Packit Service 95ac19
	/*
Packit Service 95ac19
	 * Set OLDPWD (note: unsetting OLDPWD does not disable this
Packit Service 95ac19
	 * setting in AT&T ksh)
Packit Service 95ac19
	 */
Packit Service 95ac19
	if (current_wd[0])
Packit Service 95ac19
		/* Ignore failure (happens if readonly or integer) */
Packit Service 95ac19
		setstr(oldpwd_s, current_wd, KSH_RETURN_ERROR);
Packit Service 95ac19
Packit Service 95ac19
	if (!mksh_abspath(Xstring(xs, xp))) {
Packit Service 95ac19
		pwd = NULL;
Packit Service 95ac19
	} else if (!physical) {
Packit Service 95ac19
		goto norealpath_PWD;
Packit Service 95ac19
	} else if ((pwd = allocd = do_realpath(Xstring(xs, xp))) == NULL) {
Packit Service 95ac19
		if (eflag)
Packit Service 95ac19
			rv = 1;
Packit Service 95ac19
 norealpath_PWD:
Packit Service 95ac19
		pwd = Xstring(xs, xp);
Packit Service 95ac19
	}
Packit Service 95ac19
Packit Service 95ac19
	/* Set PWD */
Packit Service 95ac19
	if (pwd) {
Packit Service 95ac19
		char *ptmp = pwd;
Packit Service 95ac19
Packit Service 95ac19
		set_current_wd(ptmp);
Packit Service 95ac19
		/* Ignore failure (happens if readonly or integer) */
Packit Service 95ac19
		setstr(pwd_s, ptmp, KSH_RETURN_ERROR);
Packit Service 95ac19
	} else {
Packit Service 95ac19
		set_current_wd(null);
Packit Service 95ac19
		pwd = Xstring(xs, xp);
Packit Service 95ac19
		/* XXX unset $PWD? */
Packit Service 95ac19
		if (eflag)
Packit Service 95ac19
			rv = 1;
Packit Service 95ac19
	}
Packit Service 95ac19
	if (printpath || cdnode)
Packit Service 95ac19
		shprintf(Tf_sN, pwd);
Packit Service 95ac19
Packit Service 95ac19
	afree(allocd, ATEMP);
Packit Service 95ac19
	Xfree(xs, xp);
Packit Service 95ac19
	return (rv);
Packit Service 95ac19
}
Packit Service 95ac19
Packit Service 95ac19
Packit Service 95ac19
#ifdef KSH_CHVT_CODE
Packit Service 95ac19
extern void chvt_reinit(void);
Packit Service 95ac19
Packit Service 95ac19
static void
Packit Service 95ac19
chvt(const Getopt *go)
Packit Service 95ac19
{
Packit Service 95ac19
	const char *dv = go->optarg;
Packit Service 95ac19
	char *cp = NULL;
Packit Service 95ac19
	int fd;
Packit Service 95ac19
Packit Service 95ac19
	switch (*dv) {
Packit Service 95ac19
	case '-':
Packit Service 95ac19
		dv = "/dev/null";
Packit Service 95ac19
		break;
Packit Service 95ac19
	case '!':
Packit Service 95ac19
		++dv;
Packit Service 95ac19
		/* FALLTHROUGH */
Packit Service 95ac19
	default: {
Packit Service 95ac19
		struct stat sb;
Packit Service 95ac19
Packit Service 95ac19
		if (stat(dv, &sb)) {
Packit Service 95ac19
			cp = shf_smprintf("/dev/ttyC%s", dv);
Packit Service 95ac19
			dv = cp;
Packit Service 95ac19
			if (stat(dv, &sb)) {
Packit Service 95ac19
				memmove(cp + 1, cp, /* /dev/tty */ 8);
Packit Service 95ac19
				dv = cp + 1;
Packit Service 95ac19
				if (stat(dv, &sb)) {
Packit Service 95ac19
					errorf(Tf_sD_sD_s, "chvt",
Packit Service 95ac19
					    "can't find tty", go->optarg);
Packit Service 95ac19
				}
Packit Service 95ac19
			}
Packit Service 95ac19
		}
Packit Service 95ac19
		if (!(sb.st_mode & S_IFCHR))
Packit Service 95ac19
			errorf(Tf_sD_sD_s, "chvt", "not a char device", dv);
Packit Service 95ac19
#ifndef MKSH_DISABLE_REVOKE_WARNING
Packit Service 95ac19
#if HAVE_REVOKE
Packit Service 95ac19
		if (revoke(dv))
Packit Service 95ac19
#endif
Packit Service 95ac19
			warningf(false, Tf_sD_s_s, "chvt",
Packit Service 95ac19
			    "new shell is potentially insecure, can't revoke",
Packit Service 95ac19
			    dv);
Packit Service 95ac19
#endif
Packit Service 95ac19
	    }
Packit Service 95ac19
	}
Packit Service 95ac19
	if ((fd = binopen2(dv, O_RDWR)) < 0) {
Packit Service 95ac19
		sleep(1);
Packit Service 95ac19
		if ((fd = binopen2(dv, O_RDWR)) < 0) {
Packit Service 95ac19
			errorf(Tf_sD_s_s, "chvt", Tcant_open, dv);
Packit Service 95ac19
		}
Packit Service 95ac19
	}
Packit Service 95ac19
	if (go->optarg[0] != '!') {
Packit Service 95ac19
		switch (fork()) {
Packit Service 95ac19
		case -1:
Packit Service 95ac19
			errorf(Tf_sD_s_s, "chvt", "fork", "failed");
Packit Service 95ac19
		case 0:
Packit Service 95ac19
			break;
Packit Service 95ac19
		default:
Packit Service 95ac19
			exit(0);
Packit Service 95ac19
		}
Packit Service 95ac19
	}
Packit Service 95ac19
	if (setsid() == -1)
Packit Service 95ac19
		errorf(Tf_sD_s_s, "chvt", "setsid", "failed");
Packit Service 95ac19
	if (go->optarg[0] != '-') {
Packit Service 95ac19
		if (ioctl(fd, TIOCSCTTY, NULL) == -1)
Packit Service 95ac19
			errorf(Tf_sD_s_s, "chvt", "TIOCSCTTY", "failed");
Packit Service 95ac19
		if (tcflush(fd, TCIOFLUSH))
Packit Service 95ac19
			errorf(Tf_sD_s_s, "chvt", "TCIOFLUSH", "failed");
Packit Service 95ac19
	}
Packit Service 95ac19
	ksh_dup2(fd, 0, false);
Packit Service 95ac19
	ksh_dup2(fd, 1, false);
Packit Service 95ac19
	ksh_dup2(fd, 2, false);
Packit Service 95ac19
	if (fd > 2)
Packit Service 95ac19
		close(fd);
Packit Service 95ac19
	rndset((unsigned long)chvt_rndsetup(go, sizeof(Getopt)));
Packit Service 95ac19
	chvt_reinit();
Packit Service 95ac19
}
Packit Service 95ac19
#endif
Packit Service 95ac19
Packit Service 95ac19
#ifdef DEBUG
Packit Service 95ac19
char *
Packit Service 95ac19
strchr(char *p, int ch)
Packit Service 95ac19
{
Packit Service 95ac19
	for (;; ++p) {
Packit Service 95ac19
		if (*p == ch)
Packit Service 95ac19
			return (p);
Packit Service 95ac19
		if (!*p)
Packit Service 95ac19
			return (NULL);
Packit Service 95ac19
	}
Packit Service 95ac19
	/* NOTREACHED */
Packit Service 95ac19
}
Packit Service 95ac19
Packit Service 95ac19
char *
Packit Service 95ac19
strstr(char *b, const char *l)
Packit Service 95ac19
{
Packit Service 95ac19
	char first, c;
Packit Service 95ac19
	size_t n;
Packit Service 95ac19
Packit Service 95ac19
	if ((first = *l++) == '\0')
Packit Service 95ac19
		return (b);
Packit Service 95ac19
	n = strlen(l);
Packit Service 95ac19
 strstr_look:
Packit Service 95ac19
	while ((c = *b++) != first)
Packit Service 95ac19
		if (c == '\0')
Packit Service 95ac19
			return (NULL);
Packit Service 95ac19
	if (strncmp(b, l, n))
Packit Service 95ac19
		goto strstr_look;
Packit Service 95ac19
	return (b - 1);
Packit Service 95ac19
}
Packit Service 95ac19
#endif
Packit Service 95ac19
Packit Service 95ac19
#if defined(MKSH_SMALL) && !defined(MKSH_SMALL_BUT_FAST)
Packit Service 95ac19
char *
Packit Service 95ac19
strndup_i(const char *src, size_t len, Area *ap)
Packit Service 95ac19
{
Packit Service 95ac19
	char *dst = NULL;
Packit Service 95ac19
Packit Service 95ac19
	if (src != NULL) {
Packit Service 95ac19
		dst = alloc(len + 1, ap);
Packit Service 95ac19
		memcpy(dst, src, len);
Packit Service 95ac19
		dst[len] = '\0';
Packit Service 95ac19
	}
Packit Service 95ac19
	return (dst);
Packit Service 95ac19
}
Packit Service 95ac19
Packit Service 95ac19
char *
Packit Service 95ac19
strdup_i(const char *src, Area *ap)
Packit Service 95ac19
{
Packit Service 95ac19
	return (src == NULL ? NULL : strndup_i(src, strlen(src), ap));
Packit Service 95ac19
}
Packit Service 95ac19
#endif
Packit Service 95ac19
Packit Service 95ac19
#if !HAVE_GETRUSAGE
Packit Service 95ac19
#define INVTCK(r,t)	do {						\
Packit Service 95ac19
	r.tv_usec = ((t) % (1000000 / CLK_TCK)) * (1000000 / CLK_TCK);	\
Packit Service 95ac19
	r.tv_sec = (t) / CLK_TCK;					\
Packit Service 95ac19
} while (/* CONSTCOND */ 0)
Packit Service 95ac19
Packit Service 95ac19
int
Packit Service 95ac19
getrusage(int what, struct rusage *ru)
Packit Service 95ac19
{
Packit Service 95ac19
	struct tms tms;
Packit Service 95ac19
	clock_t u, s;
Packit Service 95ac19
Packit Service 95ac19
	if (/* ru == NULL || */ times(&tms) == (clock_t)-1)
Packit Service 95ac19
		return (-1);
Packit Service 95ac19
Packit Service 95ac19
	switch (what) {
Packit Service 95ac19
	case RUSAGE_SELF:
Packit Service 95ac19
		u = tms.tms_utime;
Packit Service 95ac19
		s = tms.tms_stime;
Packit Service 95ac19
		break;
Packit Service 95ac19
	case RUSAGE_CHILDREN:
Packit Service 95ac19
		u = tms.tms_cutime;
Packit Service 95ac19
		s = tms.tms_cstime;
Packit Service 95ac19
		break;
Packit Service 95ac19
	default:
Packit Service 95ac19
		errno = EINVAL;
Packit Service 95ac19
		return (-1);
Packit Service 95ac19
	}
Packit Service 95ac19
	INVTCK(ru->ru_utime, u);
Packit Service 95ac19
	INVTCK(ru->ru_stime, s);
Packit Service 95ac19
	return (0);
Packit Service 95ac19
}
Packit Service 95ac19
#endif
Packit Service 95ac19
Packit Service 95ac19
/*
Packit Service 95ac19
 * process the string available via fg (get a char)
Packit Service 95ac19
 * and fp (put back a char) for backslash escapes,
Packit Service 95ac19
 * assuming the first call to *fg gets the char di-
Packit Service 95ac19
 * rectly after the backslash; return the character
Packit Service 95ac19
 * (0..0xFF), Unicode (wc + 0x100), or -1 if no known
Packit Service 95ac19
 * escape sequence was found
Packit Service 95ac19
 */
Packit Service 95ac19
int
Packit Service 95ac19
unbksl(bool cstyle, int (*fg)(void), void (*fp)(int))
Packit Service 95ac19
{
Packit Service 95ac19
	int wc, i, c, fc, n;
Packit Service 95ac19
Packit Service 95ac19
	fc = (*fg)();
Packit Service 95ac19
	switch (fc) {
Packit Service 95ac19
	case 'a':
Packit Service 95ac19
		wc = KSH_BEL;
Packit Service 95ac19
		break;
Packit Service 95ac19
	case 'b':
Packit Service 95ac19
		wc = '\b';
Packit Service 95ac19
		break;
Packit Service 95ac19
	case 'c':
Packit Service 95ac19
		if (!cstyle)
Packit Service 95ac19
			goto unknown_escape;
Packit Service 95ac19
		c = (*fg)();
Packit Service 95ac19
		wc = ksh_toctrl(c);
Packit Service 95ac19
		break;
Packit Service 95ac19
	case 'E':
Packit Service 95ac19
	case 'e':
Packit Service 95ac19
		wc = KSH_ESC;
Packit Service 95ac19
		break;
Packit Service 95ac19
	case 'f':
Packit Service 95ac19
		wc = '\f';
Packit Service 95ac19
		break;
Packit Service 95ac19
	case 'n':
Packit Service 95ac19
		wc = '\n';
Packit Service 95ac19
		break;
Packit Service 95ac19
	case 'r':
Packit Service 95ac19
		wc = '\r';
Packit Service 95ac19
		break;
Packit Service 95ac19
	case 't':
Packit Service 95ac19
		wc = '\t';
Packit Service 95ac19
		break;
Packit Service 95ac19
	case 'v':
Packit Service 95ac19
		wc = KSH_VTAB;
Packit Service 95ac19
		break;
Packit Service 95ac19
	case '1':
Packit Service 95ac19
	case '2':
Packit Service 95ac19
	case '3':
Packit Service 95ac19
	case '4':
Packit Service 95ac19
	case '5':
Packit Service 95ac19
	case '6':
Packit Service 95ac19
	case '7':
Packit Service 95ac19
		if (!cstyle)
Packit Service 95ac19
			goto unknown_escape;
Packit Service 95ac19
		/* FALLTHROUGH */
Packit Service 95ac19
	case '0':
Packit Service 95ac19
		if (cstyle)
Packit Service 95ac19
			(*fp)(fc);
Packit Service 95ac19
		/*
Packit Service 95ac19
		 * look for an octal number with up to three
Packit Service 95ac19
		 * digits, not counting the leading zero;
Packit Service 95ac19
		 * convert it to a raw octet
Packit Service 95ac19
		 */
Packit Service 95ac19
		wc = 0;
Packit Service 95ac19
		i = 3;
Packit Service 95ac19
		while (i--)
Packit Service 95ac19
			if (ctype((c = (*fg)()), C_OCTAL))
Packit Service 95ac19
				wc = (wc << 3) + ksh_numdig(c);
Packit Service 95ac19
			else {
Packit Service 95ac19
				(*fp)(c);
Packit Service 95ac19
				break;
Packit Service 95ac19
			}
Packit Service 95ac19
		break;
Packit Service 95ac19
	case 'U':
Packit Service 95ac19
		i = 8;
Packit Service 95ac19
		if (/* CONSTCOND */ 0)
Packit Service 95ac19
			/* FALLTHROUGH */
Packit Service 95ac19
	case 'u':
Packit Service 95ac19
		  i = 4;
Packit Service 95ac19
		if (/* CONSTCOND */ 0)
Packit Service 95ac19
			/* FALLTHROUGH */
Packit Service 95ac19
	case 'x':
Packit Service 95ac19
		  i = cstyle ? -1 : 2;
Packit Service 95ac19
		/**
Packit Service 95ac19
		 * x:	look for a hexadecimal number with up to
Packit Service 95ac19
		 *	two (C style: arbitrary) digits; convert
Packit Service 95ac19
		 *	to raw octet (C style: Unicode if >0xFF)
Packit Service 95ac19
		 * u/U:	look for a hexadecimal number with up to
Packit Service 95ac19
		 *	four (U: eight) digits; convert to Unicode
Packit Service 95ac19
		 */
Packit Service 95ac19
		wc = 0;
Packit Service 95ac19
		n = 0;
Packit Service 95ac19
		while (n < i || i == -1) {
Packit Service 95ac19
			wc <<= 4;
Packit Service 95ac19
			if (!ctype((c = (*fg)()), C_SEDEC)) {
Packit Service 95ac19
				wc >>= 4;
Packit Service 95ac19
				(*fp)(c);
Packit Service 95ac19
				break;
Packit Service 95ac19
			}
Packit Service 95ac19
			if (ctype(c, C_DIGIT))
Packit Service 95ac19
				wc += ksh_numdig(c);
Packit Service 95ac19
			else if (ctype(c, C_UPPER))
Packit Service 95ac19
				wc += ksh_numuc(c) + 10;
Packit Service 95ac19
			else
Packit Service 95ac19
				wc += ksh_numlc(c) + 10;
Packit Service 95ac19
			++n;
Packit Service 95ac19
		}
Packit Service 95ac19
		if (!n)
Packit Service 95ac19
			goto unknown_escape;
Packit Service 95ac19
		if ((cstyle && wc > 0xFF) || fc != 'x')
Packit Service 95ac19
			/* Unicode marker */
Packit Service 95ac19
			wc += 0x100;
Packit Service 95ac19
		break;
Packit Service 95ac19
	case '\'':
Packit Service 95ac19
		if (!cstyle)
Packit Service 95ac19
			goto unknown_escape;
Packit Service 95ac19
		wc = '\'';
Packit Service 95ac19
		break;
Packit Service 95ac19
	case '\\':
Packit Service 95ac19
		wc = '\\';
Packit Service 95ac19
		break;
Packit Service 95ac19
	default:
Packit Service 95ac19
 unknown_escape:
Packit Service 95ac19
		(*fp)(fc);
Packit Service 95ac19
		return (-1);
Packit Service 95ac19
	}
Packit Service 95ac19
Packit Service 95ac19
	return (wc);
Packit Service 95ac19
}