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