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