[Midnightbsd-cvs] src [6708] vendor/MirOS/mksh/R50: tag R50

laffer1 at midnightbsd.org laffer1 at midnightbsd.org
Tue Jul 1 08:01:18 EDT 2014


Revision: 6708
          http://svnweb.midnightbsd.org/src/?rev=6708
Author:   laffer1
Date:     2014-07-01 08:01:17 -0400 (Tue, 01 Jul 2014)
Log Message:
-----------
tag R50

Revision Links:
--------------
    http://svnweb.midnightbsd.org/src/?rev=50

Added Paths:
-----------
    vendor/MirOS/mksh/R50/
    vendor/MirOS/mksh/R50/Build.sh
    vendor/MirOS/mksh/R50/check.pl
    vendor/MirOS/mksh/R50/check.t
    vendor/MirOS/mksh/R50/dot.mkshrc
    vendor/MirOS/mksh/R50/edit.c
    vendor/MirOS/mksh/R50/eval.c
    vendor/MirOS/mksh/R50/exec.c
    vendor/MirOS/mksh/R50/expr.c
    vendor/MirOS/mksh/R50/funcs.c
    vendor/MirOS/mksh/R50/histrap.c
    vendor/MirOS/mksh/R50/jobs.c
    vendor/MirOS/mksh/R50/lex.c
    vendor/MirOS/mksh/R50/main.c
    vendor/MirOS/mksh/R50/mirhash.h
    vendor/MirOS/mksh/R50/misc.c
    vendor/MirOS/mksh/R50/mksh.1
    vendor/MirOS/mksh/R50/rlimits.opt
    vendor/MirOS/mksh/R50/sh.h
    vendor/MirOS/mksh/R50/sh_flags.opt
    vendor/MirOS/mksh/R50/shf.c
    vendor/MirOS/mksh/R50/strlcpy.c
    vendor/MirOS/mksh/R50/syn.c
    vendor/MirOS/mksh/R50/tree.c
    vendor/MirOS/mksh/R50/var.c

Removed Paths:
-------------
    vendor/MirOS/mksh/R50/Build.sh
    vendor/MirOS/mksh/R50/check.pl
    vendor/MirOS/mksh/R50/check.t
    vendor/MirOS/mksh/R50/dot.mkshrc
    vendor/MirOS/mksh/R50/edit.c
    vendor/MirOS/mksh/R50/eval.c
    vendor/MirOS/mksh/R50/exec.c
    vendor/MirOS/mksh/R50/expr.c
    vendor/MirOS/mksh/R50/funcs.c
    vendor/MirOS/mksh/R50/histrap.c
    vendor/MirOS/mksh/R50/jobs.c
    vendor/MirOS/mksh/R50/lex.c
    vendor/MirOS/mksh/R50/main.c
    vendor/MirOS/mksh/R50/misc.c
    vendor/MirOS/mksh/R50/mksh.1
    vendor/MirOS/mksh/R50/sh.h
    vendor/MirOS/mksh/R50/shf.c
    vendor/MirOS/mksh/R50/strlcpy.c
    vendor/MirOS/mksh/R50/syn.c
    vendor/MirOS/mksh/R50/tree.c
    vendor/MirOS/mksh/R50/var.c

Deleted: vendor/MirOS/mksh/R50/Build.sh
===================================================================
--- vendor/MirOS/mksh/dist/Build.sh	2014-06-30 23:58:12 UTC (rev 6706)
+++ vendor/MirOS/mksh/R50/Build.sh	2014-07-01 12:01:17 UTC (rev 6708)
@@ -1,2435 +0,0 @@
-#!/bin/sh
-srcversion='$MirOS: src/bin/mksh/Build.sh,v 1.645 2013/08/10 13:44:25 tg Exp $'
-#-
-# Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
-#		2011, 2012, 2013
-#	Thorsten Glaser <tg at mirbsd.org>
-#
-# Provided that these terms and disclaimer and all copyright notices
-# are retained or reproduced in an accompanying document, permission
-# is granted to deal in this work without restriction, including un-
-# limited rights to use, publicly perform, distribute, sell, modify,
-# merge, give away, or sublicence.
-#
-# This work is provided "AS IS" and WITHOUT WARRANTY of any kind, to
-# the utmost extent permitted by applicable law, neither express nor
-# implied; without malicious intent or gross negligence. In no event
-# may a licensor, author or contributor be held liable for indirect,
-# direct, other damage, loss, or other issues arising in any way out
-# of dealing in the work, even if advised of the possibility of such
-# damage or existence of a defect, except proven that it results out
-# of said person's immediate fault when using the work as intended.
-#-
-# People analysing the output must whitelist conftest.c for any kind
-# of compiler warning checks (mirtoconf is by design not quiet).
-#
-# Used environment documentation is at the end of this file.
-
-LC_ALL=C
-export LC_ALL
-
-echo "For the build logs, demonstrate that /dev/null and /dev/tty exist:"
-ls -l /dev/null /dev/tty
-
-case $ZSH_VERSION:$VERSION in
-:zsh*) ZSH_VERSION=2 ;;
-esac
-
-if test -n "${ZSH_VERSION+x}" && (emulate sh) >/dev/null 2>&1; then
-	emulate sh
-	NULLCMD=:
-fi
-
-if test -d /usr/xpg4/bin/. >/dev/null 2>&1; then
-	# Solaris: some of the tools have weird behaviour, use portable ones
-	PATH=/usr/xpg4/bin:$PATH
-	export PATH
-fi
-
-v() {
-	$e "$*"
-	eval "$@"
-}
-
-vv() {
-	_c=$1
-	shift
-	$e "\$ $*" 2>&1
-	eval "$@" >vv.out 2>&1
-	sed "s^${_c} " <vv.out
-}
-
-vq() {
-	eval "$@"
-}
-
-rmf() {
-	for _f in "$@"; do
-		case $_f in
-		Build.sh|check.pl|check.t|dot.mkshrc|*.c|*.h|*.ico|*.1) ;;
-		*) rm -f "$_f" ;;
-		esac
-	done
-}
-
-allu=QWERTYUIOPASDFGHJKLZXCVBNM
-alll=qwertyuiopasdfghjklzxcvbnm
-alln=0123456789
-alls=______________________________________________________________
-nl='
-'
-tcfn=no
-bi=
-ui=
-ao=
-fx=
-me=`basename "$0"`
-orig_CFLAGS=$CFLAGS
-phase=x
-oldish_ed=stdout-ed,no-stderr-ed
-
-if test -t 1; then
-	bi=''
-	ui=''
-	ao=''
-fi
-
-upper() {
-	echo :"$@" | sed 's/^://' | tr $alll $allu
-}
-
-# clean up after ac_testrun()
-ac_testdone() {
-	eval HAVE_$fu=$fv
-	fr=no
-	test 0 = $fv || fr=yes
-	$e "$bi==> $fd...$ao $ui$fr$ao$fx"
-	fx=
-}
-
-# ac_cache label: sets f, fu, fv?=0
-ac_cache() {
-	f=$1
-	fu=`upper $f`
-	eval fv=\$HAVE_$fu
-	case $fv in
-	0|1)
-		fx=' (cached)'
-		return 0
-		;;
-	esac
-	fv=0
-	return 1
-}
-
-# ac_testinit label [!] checkif[!]0 [setlabelifcheckis[!]0] useroutput
-# returns 1 if value was cached/implied, 0 otherwise: call ac_testdone
-ac_testinit() {
-	if ac_cache $1; then
-		test x"$2" = x"!" && shift
-		test x"$2" = x"" || shift
-		fd=${3-$f}
-		ac_testdone
-		return 1
-	fi
-	fc=0
-	if test x"$2" = x""; then
-		ft=1
-	else
-		if test x"$2" = x"!"; then
-			fc=1
-			shift
-		fi
-		eval ft=\$HAVE_`upper $2`
-		shift
-	fi
-	fd=${3-$f}
-	if test $fc = "$ft"; then
-		fv=$2
-		fx=' (implied)'
-		ac_testdone
-		return 1
-	fi
-	$e ... $fd
-	return 0
-}
-
-# pipe .c | ac_test[n] [!] label [!] checkif[!]0 [setlabelifcheckis[!]0] useroutput
-ac_testnnd() {
-	if test x"$1" = x"!"; then
-		fr=1
-		shift
-	else
-		fr=0
-	fi
-	ac_testinit "$@" || return 1
-	cat >conftest.c
-	vv ']' "$CC $CFLAGS $CPPFLAGS $LDFLAGS $NOWARN conftest.c $LIBS $ccpr"
-	test $tcfn = no && test -f a.out && tcfn=a.out
-	test $tcfn = no && test -f a.exe && tcfn=a.exe
-	test $tcfn = no && test -f conftest && tcfn=conftest
-	if test -f $tcfn; then
-		test 1 = $fr || fv=1
-	else
-		test 0 = $fr || fv=1
-	fi
-	vscan=
-	if test $phase = u; then
-		test $ct = gcc && vscan='unrecogni[sz]ed'
-		test $ct = hpcc && vscan='unsupported'
-		test $ct = pcc && vscan='unsupported'
-		test $ct = sunpro && vscan='-e ignored -e turned.off'
-	fi
-	test -n "$vscan" && grep $vscan vv.out >/dev/null 2>&1 && fv=$fr
-	return 0
-}
-ac_testn() {
-	ac_testnnd "$@" || return
-	rmf conftest.c conftest.o ${tcfn}* vv.out
-	ac_testdone
-}
-
-# ac_ifcpp cppexpr [!] label [!] checkif[!]0 [setlabelifcheckis[!]0] useroutput
-ac_ifcpp() {
-	expr=$1; shift
-	ac_testn "$@" <<-EOF
-		extern int thiswillneverbedefinedIhope(void);
-		int main(void) { return (
-		#$expr
-		    0
-		#else
-		/* force a failure: expr is false */
-		    thiswillneverbedefinedIhope()
-		#endif
-		    ); }
-EOF
-	test x"$1" = x"!" && shift
-	f=$1
-	fu=`upper $f`
-	eval fv=\$HAVE_$fu
-	test x"$fv" = x"1"
-}
-
-add_cppflags() {
-	CPPFLAGS="$CPPFLAGS $*"
-}
-
-ac_cppflags() {
-	test x"$1" = x"" || fu=$1
-	fv=$2
-	test x"$2" = x"" && eval fv=\$HAVE_$fu
-	add_cppflags -DHAVE_$fu=$fv
-}
-
-ac_test() {
-	ac_testn "$@"
-	ac_cppflags
-}
-
-# ac_flags [-] add varname cflags [text] [ldflags]
-ac_flags() {
-	if test x"$1" = x"-"; then
-		shift
-		hf=1
-	else
-		hf=0
-	fi
-	fa=$1
-	vn=$2
-	f=$3
-	ft=$4
-	fl=$5
-	test x"$ft" = x"" && ft="if $f can be used"
-	save_CFLAGS=$CFLAGS
-	CFLAGS="$CFLAGS $f"
-	if test -n "$fl"; then
-		save_LDFLAGS=$LDFLAGS
-		LDFLAGS="$LDFLAGS $fl"
-	fi
-	if test 1 = $hf; then
-		ac_testn can_$vn '' "$ft"
-	else
-		ac_testn can_$vn '' "$ft" <<-'EOF'
-			/* evil apo'stroph in comment test */
-			int main(void) { return (0); }
-		EOF
-	fi
-	eval fv=\$HAVE_CAN_`upper $vn`
-	if test -n "$fl"; then
-		test 11 = $fa$fv || LDFLAGS=$save_LDFLAGS
-	fi
-	test 11 = $fa$fv || CFLAGS=$save_CFLAGS
-}
-
-# ac_header [!] header [prereq ...]
-ac_header() {
-	if test x"$1" = x"!"; then
-		na=1
-		shift
-	else
-		na=0
-	fi
-	hf=$1; shift
-	hv=`echo "$hf" | tr -d '\012\015' | tr -c $alll$allu$alln $alls`
-	echo "/* NeXTstep bug workaround */" >x
-	for i
-	do
-		case $i in
-		_time)
-			echo '#if HAVE_BOTH_TIME_H' >>x
-			echo '#include <sys/time.h>' >>x
-			echo '#include <time.h>' >>x
-			echo '#elif HAVE_SYS_TIME_H' >>x
-			echo '#include <sys/time.h>' >>x
-			echo '#elif HAVE_TIME_H' >>x
-			echo '#include <time.h>' >>x
-			echo '#endif' >>x
-			;;
-		*)
-			echo "#include <$i>" >>x
-			;;
-		esac
-	done
-	echo "#include <$hf>" >>x
-	echo 'int main(void) { return (0); }' >>x
-	ac_testn "$hv" "" "<$hf>" <x
-	rmf x
-	test 1 = $na || ac_cppflags
-}
-
-addsrcs() {
-	addsrcs_s=0
-	if test x"$1" = x"-s"; then
-		# optstatic
-		addsrcs_s=1
-		shift
-	fi
-	if test x"$1" = x"!"; then
-		fr=0
-		shift
-	else
-		fr=1
-	fi
-	eval i=\$$1
-	if test $addsrcs_s = 1; then
-		if test -f "$2" || test -f "$srcdir/$2"; then
-			# always add $2, since it exists
-			fr=1
-			i=1
-		fi
-	fi
-	test $fr = "$i" && case " $SRCS " in
-	*\ $2\ *)	;;
-	*)		SRCS="$SRCS $2" ;;
-	esac
-}
-
-
-curdir=`pwd` srcdir=`dirname "$0" 2>/dev/null` check_categories=
-test -n "$srcdir" || srcdir=. # in case dirname does not exist
-dstversion=`sed -n '/define MKSH_VERSION/s/^.*"\([^"]*\)".*$/\1/p' $srcdir/sh.h`
-add_cppflags -DMKSH_BUILDSH
-
-e=echo
-r=0
-eq=0
-pm=0
-cm=normal
-optflags=-std-compile-opts
-last=
-tfn=
-legacy=0
-
-for i
-do
-	case $last:$i in
-	c:combine|c:dragonegg|c:llvm|c:lto)
-		cm=$i
-		last=
-		;;
-	c:*)
-		echo "$me: Unknown option -c '$i'!" >&2
-		exit 1
-		;;
-	o:*)
-		optflags=$i
-		last=
-		;;
-	t:*)
-		tfn=$i
-		last=
-		;;
-	:-c)
-		last=c
-		;;
-	:-g)
-		# checker, debug, valgrind build
-		add_cppflags -DDEBUG
-		CFLAGS="$CFLAGS -g3 -fno-builtin"
-		;;
-	:-j)
-		pm=1
-		;;
-	:-L)
-		legacy=1
-		;;
-	:+L)
-		legacy=0
-		;;
-	:-M)
-		cm=makefile
-		;;
-	:-O)
-		optflags=-std-compile-opts
-		;;
-	:-o)
-		last=o
-		;;
-	:-Q)
-		eq=1
-		;;
-	:-r)
-		r=1
-		;;
-	:-t)
-		last=t
-		;;
-	:-v)
-		echo "Build.sh $srcversion"
-		echo "for mksh $dstversion"
-		exit 0
-		;;
-	:*)
-		echo "$me: Unknown option '$i'!" >&2
-		exit 1
-		;;
-	*)
-		echo "$me: Unknown option -'$last' '$i'!" >&2
-		exit 1
-		;;
-	esac
-done
-if test -n "$last"; then
-	echo "$me: Option -'$last' not followed by argument!" >&2
-	exit 1
-fi
-
-test -z "$tfn" && if test $legacy = 0; then
-	tfn=mksh
-else
-	tfn=lksh
-fi
-if test -d $tfn || test -d $tfn.exe; then
-	echo "$me: Error: ./$tfn is a directory!" >&2
-	exit 1
-fi
-rmf a.exe* a.out* conftest.c *core core.* lft ${tfn}* no *.bc *.ll *.o \
-    Rebuild.sh signames.inc test.sh x vv.out
-
-SRCS="lalloc.c eval.c exec.c expr.c funcs.c histrap.c jobs.c"
-SRCS="$SRCS lex.c main.c misc.c shf.c syn.c tree.c var.c"
-
-if test $legacy = 0; then
-	SRCS="$SRCS edit.c"
-	check_categories="$check_categories shell:legacy-no int:32"
-else
-	check_categories="$check_categories shell:legacy-yes"
-	add_cppflags -DMKSH_LEGACY_MODE
-	HAVE_PERSISTENT_HISTORY=0
-	HAVE_ISSET_MKSH_CONSERVATIVE_FDS=1	# from sh.h
-fi
-
-if test x"$srcdir" = x"."; then
-	CPPFLAGS="-I. $CPPFLAGS"
-else
-	CPPFLAGS="-I. -I'$srcdir' $CPPFLAGS"
-fi
-test -n "$LDSTATIC" && if test -n "$LDFLAGS"; then
-	LDFLAGS="$LDFLAGS $LDSTATIC"
-else
-	LDFLAGS=$LDSTATIC
-fi
-
-if test -z "$TARGET_OS"; then
-	x=`uname -s 2>/dev/null || uname`
-	test x"$x" = x"`uname -n 2>/dev/null`" || TARGET_OS=$x
-fi
-if test -z "$TARGET_OS"; then
-	echo "$me: Set TARGET_OS, your uname is broken!" >&2
-	exit 1
-fi
-oswarn=
-ccpc=-Wc,
-ccpl=-Wl,
-tsts=
-ccpr='|| for _f in ${tcfn}*; do case $_f in Build.sh|check.pl|check.t|dot.mkshrc|*.c|*.h|*.ico|*.1) ;; *) rm -f "$_f" ;; esac; done'
-
-# Evil hack
-if test x"$TARGET_OS" = x"Android"; then
-	check_categories="$check_categories android"
-	TARGET_OS=Linux
-fi
-
-# Evil OS
-if test x"$TARGET_OS" = x"Minix"; then
-	echo >&2 "
-WARNING: additional checks before running Build.sh required!
-You can avoid these by calling Build.sh correctly, see below.
-"
-	cat >conftest.c <<'EOF'
-#include <sys/types.h>
-const char *
-#ifdef _NETBSD_SOURCE
-ct="Ninix3"
-#else
-ct="Minix3"
-#endif
-;
-EOF
-	ct=unknown
-	vv ']' "${CC-cc} -E $CFLAGS $CPPFLAGS $NOWARN conftest.c | grep ct= | tr -d \\\\015 >x"
-	sed 's/^/[ /' x
-	eval `cat x`
-	rmf x vv.out
-	case $ct in
-	Minix3|Ninix3)
-		echo >&2 "
-Warning: you set TARGET_OS to $TARGET_OS but that is ambiguous.
-Please set it to either Minix3 or Ninix3, whereas the latter is
-all versions of Minix with even partial NetBSD(R) userland. The
-value determined from your compiler for the current compilation
-(which may be wrong) is: $ct
-"
-		TARGET_OS=$ct
-		;;
-	*)
-		echo >&2 "
-Warning: you set TARGET_OS to $TARGET_OS but that is ambiguous.
-Please set it to either Minix3 or Ninix3, whereas the latter is
-all versions of Minix with even partial NetBSD(R) userland. The
-proper value couldn't be determined, continue at your own risk.
-"
-		;;
-	esac
-fi
-
-# Configuration depending on OS revision, on OSes that need them
-case $TARGET_OS in
-NEXTSTEP)
-	test x"$TARGET_OSREV" = x"" && TARGET_OSREV=`hostinfo 2>&1 | \
-	    grep 'NeXT Mach [0-9][0-9.]*:' | \
-	    sed 's/^.*NeXT Mach \([0-9][0-9.]*\):.*$/\1/'`
-	;;
-QNX|SCO_SV)
-	test x"$TARGET_OSREV" = x"" && TARGET_OSREV=`uname -r`
-	;;
-esac
-
-# Configuration depending on OS name
-case $TARGET_OS in
-386BSD)
-	: ${HAVE_CAN_OTWO=0}
-	add_cppflags -DMKSH_NO_SIGSETJMP
-	add_cppflags -DMKSH_TYPEDEF_SIG_ATOMIC_T=int
-	add_cppflags -DMKSH_CONSERVATIVE_FDS
-	;;
-AIX)
-	add_cppflags -D_ALL_SOURCE
-	: ${HAVE_SETLOCALE_CTYPE=0}
-	;;
-BeOS)
-	case $KSH_VERSION in
-	*MIRBSD\ KSH*)
-		oswarn="; it has minor issues"
-		;;
-	*)
-		oswarn="; you must recompile mksh with"
-		oswarn="$oswarn${nl}itself in a second stage"
-		;;
-	esac
-	# BeOS has no real tty either
-	add_cppflags -DMKSH_UNEMPLOYED
-	add_cppflags -DMKSH_DISABLE_TTY_WARNING
-	# BeOS doesn't have different UIDs and GIDs
-	add_cppflags -DMKSH__NO_SETEUGID
-	;;
-BSD/OS)
-	: ${HAVE_SETLOCALE_CTYPE=0}
-	;;
-Coherent)
-	oswarn="; it has major issues"
-	add_cppflags -DMKSH__NO_SYMLINK
-	check_categories="$check_categories nosymlink"
-	add_cppflags -DMKSH__NO_SETEUGID
-	add_cppflags -DMKSH_CONSERVATIVE_FDS
-	add_cppflags -DMKSH_DISABLE_TTY_WARNING
-	;;
-CYGWIN*)
-	: ${HAVE_SETLOCALE_CTYPE=0}
-	;;
-Darwin)
-	add_cppflags -D_DARWIN_C_SOURCE
-	;;
-DragonFly)
-	;;
-FreeBSD)
-	;;
-FreeMiNT)
-	oswarn="; it has minor issues"
-	add_cppflags -D_GNU_SOURCE
-	add_cppflags -DMKSH_CONSERVATIVE_FDS
-	: ${HAVE_SETLOCALE_CTYPE=0}
-	;;
-GNU)
-	case $CC in
-	*tendracc*) ;;
-	*) add_cppflags -D_GNU_SOURCE ;;
-	esac
-	# define MKSH__NO_PATH_MAX to use Hurd-only functions
-	add_cppflags -DMKSH__NO_PATH_MAX
-	;;
-GNU/kFreeBSD)
-	case $CC in
-	*tendracc*) ;;
-	*) add_cppflags -D_GNU_SOURCE ;;
-	esac
-	;;
-Haiku)
-	add_cppflags -DMKSH_ASSUME_UTF8; HAVE_ISSET_MKSH_ASSUME_UTF8=1
-	;;
-HP-UX)
-	;;
-Interix)
-	ccpc='-X '
-	ccpl='-Y '
-	add_cppflags -D_ALL_SOURCE
-	: ${LIBS='-lcrypt'}
-	: ${HAVE_SETLOCALE_CTYPE=0}
-	;;
-IRIX*)
-	: ${HAVE_SETLOCALE_CTYPE=0}
-	;;
-Linux)
-	case $CC in
-	*tendracc*) ;;
-	*) add_cppflags -D_GNU_SOURCE ;;
-	esac
-	add_cppflags -DSETUID_CAN_FAIL_WITH_EAGAIN
-	: ${HAVE_REVOKE=0}
-	;;
-LynxOS)
-	oswarn="; it has minor issues"
-	;;
-MidnightBSD)
-	;;
-Minix-vmd)
-	add_cppflags -DMKSH__NO_SETEUGID
-	add_cppflags -DMKSH_UNEMPLOYED
-	add_cppflags -DMKSH_CONSERVATIVE_FDS
-	add_cppflags -D_MINIX_SOURCE
-	oldish_ed=no-stderr-ed		# no /bin/ed, maybe see below
-	: ${HAVE_SETLOCALE_CTYPE=0}
-	;;
-Minix3)
-	add_cppflags -DMKSH_UNEMPLOYED
-	add_cppflags -DMKSH_CONSERVATIVE_FDS
-	add_cppflags -DMKSH_NO_LIMITS
-	add_cppflags -D_POSIX_SOURCE -D_POSIX_1_SOURCE=2 -D_MINIX
-	oldish_ed=no-stderr-ed		# /usr/bin/ed(!) is broken
-	: ${HAVE_SETLOCALE_CTYPE=0}
-	;;
-MirBSD)
-	;;
-MSYS_*)
-	add_cppflags -DMKSH_ASSUME_UTF8=0; HAVE_ISSET_MKSH_ASSUME_UTF8=1
-	# almost same as CYGWIN* (from RT|Chatzilla)
-	: ${HAVE_SETLOCALE_CTYPE=0}
-	# broken on this OE (from ir0nh34d)
-	: ${HAVE_STDINT_H=0}
-	;;
-NetBSD)
-	;;
-NEXTSTEP)
-	add_cppflags -D_NEXT_SOURCE
-	add_cppflags -D_POSIX_SOURCE
-	: ${AWK=gawk} ${CC=cc -posix}
-	add_cppflags -DMKSH_NO_SIGSETJMP
-	# NeXTstep cannot get a controlling tty
-	add_cppflags -DMKSH_UNEMPLOYED
-	case $TARGET_OSREV in
-	4.2*)
-		# OpenStep 4.2 is broken by default
-		oswarn="; it needs libposix.a"
-		;;
-	esac
-	add_cppflags -DMKSH_CONSERVATIVE_FDS
-	;;
-Ninix3)
-	# similar to Minix3
-	add_cppflags -DMKSH_UNEMPLOYED
-	add_cppflags -DMKSH_CONSERVATIVE_FDS
-	add_cppflags -DMKSH_NO_LIMITS
-	# but no idea what else could be needed
-	oswarn="; it has unknown issues"
-	;;
-OpenBSD)
-	: ${HAVE_SETLOCALE_CTYPE=0}
-	;;
-OSF1)
-	HAVE_SIG_T=0	# incompatible
-	add_cppflags -D_OSF_SOURCE
-	add_cppflags -D_POSIX_C_SOURCE=200112L
-	add_cppflags -D_XOPEN_SOURCE=600
-	add_cppflags -D_XOPEN_SOURCE_EXTENDED
-	: ${HAVE_SETLOCALE_CTYPE=0}
-	;;
-Plan9)
-	add_cppflags -D_POSIX_SOURCE
-	add_cppflags -D_LIMITS_EXTENSION
-	add_cppflags -D_BSD_EXTENSION
-	add_cppflags -D_SUSV2_SOURCE
-	add_cppflags -DMKSH_ASSUME_UTF8; HAVE_ISSET_MKSH_ASSUME_UTF8=1
-	add_cppflags -DMKSH_NO_CMDLINE_EDITING
-	add_cppflags -DMKSH__NO_SETEUGID
-	oswarn=' and will currently not work'
-	add_cppflags -DMKSH_UNEMPLOYED
-	# this is for detecting kencc
-	add_cppflags -DMKSH_MAYBE_KENCC
-	;;
-PW32*)
-	HAVE_SIG_T=0	# incompatible
-	oswarn=' and will currently not work'
-	: ${HAVE_SETLOCALE_CTYPE=0}
-	;;
-QNX)
-	add_cppflags -D__NO_EXT_QNX
-	add_cppflags -D__EXT_UNIX_MISC
-	case $TARGET_OSREV in
-	[012345].*|6.[0123].*|6.4.[01])
-		oldish_ed=no-stderr-ed		# oldish /bin/ed is broken
-		;;
-	esac
-	: ${HAVE_SETLOCALE_CTYPE=0}
-	;;
-SCO_SV)
-	case $TARGET_OSREV in
-	3.2*)
-		# SCO OpenServer 5
-		add_cppflags -DMKSH_UNEMPLOYED
-		;;
-	5*)
-		# SCO OpenServer 6
-		;;
-	*)
-		oswarn='; this is an unknown version of'
-		oswarn="$oswarn$nl$TARGET_OS ${TARGET_OSREV}, please tell me what to do"
-		;;
-	esac
-	add_cppflags -DMKSH_CONSERVATIVE_FDS
-	: ${HAVE_SYS_SIGLIST=0} ${HAVE__SYS_SIGLIST=0}
-	;;
-skyos)
-	oswarn="; it has minor issues"
-	;;
-SunOS)
-	add_cppflags -D_BSD_SOURCE
-	add_cppflags -D__EXTENSIONS__
-	;;
-syllable)
-	add_cppflags -D_GNU_SOURCE
-	add_cppflags -DMKSH_NO_SIGSUSPEND
-	oswarn=' and will currently not work'
-	;;
-ULTRIX)
-	: ${CC=cc -YPOSIX}
-	add_cppflags -DMKSH_TYPEDEF_SSIZE_T=int
-	add_cppflags -DMKSH_CONSERVATIVE_FDS
-	: ${HAVE_SETLOCALE_CTYPE=0}
-	;;
-UnixWare|UNIX_SV)
-	# SCO UnixWare
-	add_cppflags -DMKSH_CONSERVATIVE_FDS
-	: ${HAVE_SYS_SIGLIST=0} ${HAVE__SYS_SIGLIST=0}
-	;;
-UWIN*)
-	ccpc='-Yc,'
-	ccpl='-Yl,'
-	tsts=" 3<>/dev/tty"
-	oswarn="; it will compile, but the target"
-	oswarn="$oswarn${nl}platform itself is very flakey/unreliable"
-	: ${HAVE_SETLOCALE_CTYPE=0}
-	;;
-_svr4)
-	# generic target for SVR4 Unix with uname -s = uname -n
-	# this duplicates the * target below
-	oswarn='; it may or may not work'
-	test x"$TARGET_OSREV" = x"" && TARGET_OSREV=`uname -r`
-	;;
-*)
-	oswarn='; it may or may not work'
-	test x"$TARGET_OSREV" = x"" && TARGET_OSREV=`uname -r`
-	;;
-esac
-
-: ${HAVE_MKNOD=0}
-
-: ${AWK=awk} ${CC=cc} ${NROFF=nroff} ${SIZE=size}
-test 0 = $r && echo | $NROFF -v 2>&1 | grep GNU >/dev/null 2>&1 && \
-    NROFF="$NROFF -c"
-
-# this aids me in tracing FTBFSen without access to the buildd
-$e "Hi from$ao $bi$srcversion$ao on:"
-case $TARGET_OS in
-AIX)
-	vv '|' "oslevel >&2"
-	vv '|' "uname -a >&2"
-	;;
-Darwin)
-	vv '|' "hwprefs machine_type os_type os_class >&2"
-	vv '|' "uname -a >&2"
-	;;
-IRIX*)
-	vv '|' "uname -a >&2"
-	vv '|' "hinv -v >&2"
-	;;
-OSF1)
-	vv '|' "uname -a >&2"
-	vv '|' "/usr/sbin/sizer -v >&2"
-	;;
-SCO_SV|UnixWare|UNIX_SV)
-	vv '|' "uname -a >&2"
-	vv '|' "uname -X >&2"
-	;;
-*)
-	vv '|' "uname -a >&2"
-	;;
-esac
-test -z "$oswarn" || echo >&2 "
-Warning: mksh has not yet been ported to or tested on your
-operating system '$TARGET_OS'$oswarn. If you can provide
-a shell account to the developer, this may improve; please
-drop us a success or failure notice or even send in diffs.
-"
-$e "$bi$me: Building the MirBSD Korn Shell$ao $ui$dstversion$ao on $TARGET_OS ${TARGET_OSREV}..."
-
-#
-# Begin of mirtoconf checks
-#
-$e $bi$me: Scanning for functions... please ignore any errors.$ao
-
-#
-# Compiler: which one?
-#
-# notes:
-# - ICC defines __GNUC__ too
-# - GCC defines __hpux too
-# - LLVM+clang defines __GNUC__ too
-# - nwcc defines __GNUC__ too
-CPP="$CC -E"
-$e ... which compiler seems to be used
-cat >conftest.c <<'EOF'
-const char *
-#if defined(__ICC) || defined(__INTEL_COMPILER)
-ct="icc"
-#elif defined(__xlC__) || defined(__IBMC__)
-ct="xlc"
-#elif defined(__SUNPRO_C)
-ct="sunpro"
-#elif defined(__ACK__)
-ct="ack"
-#elif defined(__BORLANDC__)
-ct="bcc"
-#elif defined(__WATCOMC__)
-ct="watcom"
-#elif defined(__MWERKS__)
-ct="metrowerks"
-#elif defined(__HP_cc)
-ct="hpcc"
-#elif defined(__DECC) || (defined(__osf__) && !defined(__GNUC__))
-ct="dec"
-#elif defined(__PGI)
-ct="pgi"
-#elif defined(__DMC__)
-ct="dmc"
-#elif defined(_MSC_VER)
-ct="msc"
-#elif defined(__ADSPBLACKFIN__) || defined(__ADSPTS__) || defined(__ADSP21000__)
-ct="adsp"
-#elif defined(__IAR_SYSTEMS_ICC__)
-ct="iar"
-#elif defined(SDCC)
-ct="sdcc"
-#elif defined(__PCC__)
-ct="pcc"
-#elif defined(__TenDRA__)
-ct="tendra"
-#elif defined(__TINYC__)
-ct="tcc"
-#elif defined(__llvm__) && defined(__clang__)
-ct="clang"
-#elif defined(__NWCC__)
-ct="nwcc"
-#elif defined(__GNUC__)
-ct="gcc"
-#elif defined(_COMPILER_VERSION)
-ct="mipspro"
-#elif defined(__sgi)
-ct="mipspro"
-#elif defined(__hpux) || defined(__hpua)
-ct="hpcc"
-#elif defined(__ultrix)
-ct="ucode"
-#elif defined(__USLC__)
-ct="uslc"
-#elif defined(__LCC__)
-ct="lcc"
-#elif defined(MKSH_MAYBE_KENCC)
-/* and none of the above matches */
-ct="kencc"
-#else
-ct="unknown"
-#endif
-;
-const char *
-#if defined(__KLIBC__)
-et="klibc"
-#else
-et="unknown"
-#endif
-;
-EOF
-ct=untested
-et=untested
-vv ']' "$CPP $CFLAGS $CPPFLAGS $NOWARN conftest.c | \
-    sed -n '/^ *[ce]t *= */s/^ *\([ce]t\) *= */\1=/p' | tr -d \\\\015 >x"
-sed 's/^/[ /' x
-eval `cat x`
-rmf x vv.out
-echo 'int main(void) { return (0); }' >conftest.c
-case $ct in
-ack)
-	# work around "the famous ACK const bug"
-	CPPFLAGS="-Dconst= $CPPFLAGS"
-	;;
-adsp)
-	echo >&2 'Warning: Analog Devices C++ compiler for Blackfin, TigerSHARC
-    and SHARC (21000) DSPs detected. This compiler has not yet
-    been tested for compatibility with mksh. Continue at your
-    own risk, please report success/failure to the developers.'
-	;;
-bcc)
-	echo >&2 "Warning: Borland C++ Builder detected. This compiler might
-    produce broken executables. Continue at your own risk,
-    please report success/failure to the developers."
-	;;
-clang)
-	# does not work with current "ccc" compiler driver
-	vv '|' "$CC $CFLAGS $CPPFLAGS $LDFLAGS $NOWARN $LIBS -version"
-	# one of these two works, for now
-	vv '|' "${CLANG-clang} -version"
-	vv '|' "${CLANG-clang} --version"
-	# ensure compiler and linker are in sync unless overridden
-	case $CCC_CC:$CCC_LD in
-	:*)	;;
-	*:)	CCC_LD=$CCC_CC; export CCC_LD ;;
-	esac
-	;;
-dec)
-	vv '|' "$CC $CFLAGS $CPPFLAGS $LDFLAGS $NOWARN $LIBS -V"
-	vv '|' "$CC $CFLAGS $CPPFLAGS $LDFLAGS $NOWARN -Wl,-V conftest.c $LIBS"
-	;;
-dmc)
-	echo >&2 "Warning: Digital Mars Compiler detected. When running under"
-	echo >&2 "    UWIN, mksh tends to be unstable due to the limitations"
-	echo >&2 "    of this platform. Continue at your own risk,"
-	echo >&2 "    please report success/failure to the developers."
-	;;
-gcc)
-	vv '|' "$CC $CFLAGS $CPPFLAGS $LDFLAGS $NOWARN -v conftest.c $LIBS"
-	vv '|' 'echo `$CC $CFLAGS $CPPFLAGS $LDFLAGS $NOWARN $LIBS \
-	    -dumpmachine` gcc`$CC $CFLAGS $CPPFLAGS $LDFLAGS $NOWARN \
-	    $LIBS -dumpversion`'
-	;;
-hpcc)
-	vv '|' "$CC $CFLAGS $CPPFLAGS $LDFLAGS $NOWARN -V conftest.c $LIBS"
-	;;
-iar)
-	echo >&2 'Warning: IAR Systems (http://www.iar.com) compiler for embedded
-    systems detected. This unsupported compiler has not yet
-    been tested for compatibility with mksh. Continue at your
-    own risk, please report success/failure to the developers.'
-	;;
-icc)
-	vv '|' "$CC $CFLAGS $CPPFLAGS $LDFLAGS $NOWARN $LIBS -V"
-	;;
-kencc)
-	vv '|' "$CC $CFLAGS $CPPFLAGS $LDFLAGS $NOWARN -v conftest.c $LIBS"
-	;;
-lcc)
-	vv '|' "$CC $CFLAGS $CPPFLAGS $LDFLAGS $NOWARN -v conftest.c $LIBS"
-	add_cppflags -D__inline__=__inline
-	;;
-metrowerks)
-	echo >&2 'Warning: Metrowerks C compiler detected. This has not yet
-    been tested for compatibility with mksh. Continue at your
-    own risk, please report success/failure to the developers.'
-	;;
-mipspro)
-	vv '|' "$CC $CFLAGS $CPPFLAGS $LDFLAGS $NOWARN $LIBS -version"
-	;;
-msc)
-	ccpr=		# errorlevels are not reliable
-	case $TARGET_OS in
-	Interix)
-		if [[ -n $C89_COMPILER ]]; then
-			C89_COMPILER=`ntpath2posix -c "$C89_COMPILER"`
-		else
-			C89_COMPILER=CL.EXE
-		fi
-		if [[ -n $C89_LINKER ]]; then
-			C89_LINKER=`ntpath2posix -c "$C89_LINKER"`
-		else
-			C89_LINKER=LINK.EXE
-		fi
-		vv '|' "$C89_COMPILER /HELP >&2"
-		vv '|' "$C89_LINKER /LINK >&2"
-		;;
-	esac
-	;;
-nwcc)
-	vv '|' "$CC $CFLAGS $CPPFLAGS $LDFLAGS $NOWARN $LIBS -version"
-	;;
-pcc)
-	vv '|' "$CC $CFLAGS $CPPFLAGS $LDFLAGS $NOWARN $LIBS -v"
-	;;
-pgi)
-	echo >&2 'Warning: PGI detected. This unknown compiler has not yet
-    been tested for compatibility with mksh. Continue at your
-    own risk, please report success/failure to the developers.'
-	;;
-sdcc)
-	echo >&2 'Warning: sdcc (http://sdcc.sourceforge.net), the small devices
-    C compiler for embedded systems detected. This has not yet
-    been tested for compatibility with mksh. Continue at your
-    own risk, please report success/failure to the developers.'
-	;;
-sunpro)
-	vv '|' "$CC $CFLAGS $CPPFLAGS $LDFLAGS $NOWARN -V conftest.c $LIBS"
-	;;
-tcc)
-	vv '|' "$CC $CFLAGS $CPPFLAGS $LDFLAGS $NOWARN $LIBS -v"
-	;;
-tendra)
-	vv '|' "$CC $CFLAGS $CPPFLAGS $LDFLAGS $NOWARN $LIBS -V 2>&1 | \
-	    fgrep -i -e version -e release"
-	;;
-ucode)
-	vv '|' "$CC $CFLAGS $CPPFLAGS $LDFLAGS $NOWARN $LIBS -V"
-	vv '|' "$CC $CFLAGS $CPPFLAGS $LDFLAGS $NOWARN -Wl,-V conftest.c $LIBS"
-	;;
-uslc)
-	case $TARGET_OS:$TARGET_OSREV in
-	SCO_SV:3.2*)
-		# SCO OpenServer 5
-		CFLAGS="$CFLAGS -g"
-		: ${HAVE_CAN_OTWO=0} ${HAVE_CAN_OPTIMISE=0}
-		;;
-	esac
-	vv '|' "$CC $CFLAGS $CPPFLAGS $LDFLAGS $NOWARN -V conftest.c $LIBS"
-	;;
-watcom)
-	vv '|' "$CC $CFLAGS $CPPFLAGS $LDFLAGS $NOWARN -v conftest.c $LIBS"
-	;;
-xlc)
-	vv '|' "$CC $CFLAGS $CPPFLAGS $LDFLAGS $NOWARN $LIBS -qversion"
-	vv '|' "$CC $CFLAGS $CPPFLAGS $LDFLAGS $NOWARN $LIBS -qversion=verbose"
-	vv '|' "ld -V"
-	;;
-*)
-	test x"$ct" = x"untested" && $e "!!! detecting preprocessor failed"
-	ct=unknown
-	vv "$CC --version"
-	vv '|' "$CC $CFLAGS $CPPFLAGS $LDFLAGS $NOWARN -v conftest.c $LIBS"
-	vv '|' "$CC $CFLAGS $CPPFLAGS $LDFLAGS $NOWARN -V conftest.c $LIBS"
-	;;
-esac
-case $cm in
-dragonegg|llvm)
-	vv '|' "llc -version"
-	;;
-esac
-case $et in
-klibc)
-	add_cppflags -DMKSH_NO_LIMITS
-	;;
-unknown)
-	# nothing special detected, don’t worry
-	unset et
-	;;
-*)
-	# huh?
-	;;
-esac
-$e "$bi==> which compiler seems to be used...$ao $ui$ct${et+ on $et}$ao"
-rmf conftest.c conftest.o conftest a.out* a.exe* vv.out
-
-#
-# Compiler: works as-is, with -Wno-error and -Werror
-#
-save_NOWARN=$NOWARN
-NOWARN=
-DOWARN=
-ac_flags 0 compiler_works '' 'if the compiler works'
-test 1 = $HAVE_CAN_COMPILER_WORKS || exit 1
-HAVE_COMPILER_KNOWN=0
-test $ct = unknown || HAVE_COMPILER_KNOWN=1
-if ac_ifcpp 'if 0' compiler_fails '' \
-    'if the compiler does not fail correctly'; then
-	save_CFLAGS=$CFLAGS
-	: ${HAVE_CAN_DELEXE=x}
-	case $ct in
-	dec)
-		CFLAGS="$CFLAGS ${ccpl}-non_shared"
-		ac_testn can_delexe compiler_fails 0 'for the -non_shared linker option' <<-EOF
-			int main(void) { return (0); }
-		EOF
-		;;
-	dmc)
-		CFLAGS="$CFLAGS ${ccpl}/DELEXECUTABLE"
-		ac_testn can_delexe compiler_fails 0 'for the /DELEXECUTABLE linker option' <<-EOF
-			int main(void) { return (0); }
-		EOF
-		;;
-	*)
-		exit 1
-		;;
-	esac
-	test 1 = $HAVE_CAN_DELEXE || CFLAGS=$save_CFLAGS
-	ac_testn compiler_still_fails '' 'if the compiler still does not fail correctly' <<-EOF
-	EOF
-	test 1 = $HAVE_COMPILER_STILL_FAILS && exit 1
-fi
-if ac_ifcpp 'ifdef __TINYC__' couldbe_tcc '!' compiler_known 0 \
-    'if this could be tcc'; then
-	ct=tcc
-	CPP='cpp -D__TINYC__'
-	HAVE_COMPILER_KNOWN=1
-fi
-
-case $ct in
-bcc)
-	save_NOWARN="${ccpc}-w"
-	DOWARN="${ccpc}-w!"
-	;;
-dec)
-	# -msg_* flags not used yet, or is -w2 correct?
-	;;
-dmc)
-	save_NOWARN="${ccpc}-w"
-	DOWARN="${ccpc}-wx"
-	;;
-hpcc)
-	save_NOWARN=
-	DOWARN=+We
-	;;
-kencc)
-	save_NOWARN=
-	DOWARN=
-	;;
-mipspro)
-	save_NOWARN=
-	DOWARN="-diag_error 1-10000"
-	;;
-msc)
-	save_NOWARN="${ccpc}/w"
-	DOWARN="${ccpc}/WX"
-	;;
-sunpro)
-	test x"$save_NOWARN" = x"" && save_NOWARN='-errwarn=%none'
-	ac_flags 0 errwarnnone "$save_NOWARN"
-	test 1 = $HAVE_CAN_ERRWARNNONE || save_NOWARN=
-	ac_flags 0 errwarnall "-errwarn=%all"
-	test 1 = $HAVE_CAN_ERRWARNALL && DOWARN="-errwarn=%all"
-	;;
-tendra)
-	save_NOWARN=-w
-	;;
-ucode)
-	save_NOWARN=
-	DOWARN=-w2
-	;;
-watcom)
-	save_NOWARN=
-	DOWARN=-Wc,-we
-	;;
-xlc)
-	save_NOWARN=-qflag=i:e
-	DOWARN=-qflag=i:i
-	;;
-*)
-	test x"$save_NOWARN" = x"" && save_NOWARN=-Wno-error
-	ac_flags 0 wnoerror "$save_NOWARN"
-	test 1 = $HAVE_CAN_WNOERROR || save_NOWARN=
-	ac_flags 0 werror -Werror
-	test 1 = $HAVE_CAN_WERROR && DOWARN=-Werror
-	test $ct = icc && DOWARN="$DOWARN -wd1419"
-	;;
-esac
-NOWARN=$save_NOWARN
-
-#
-# Compiler: extra flags (-O2 -f* -W* etc.)
-#
-i=`echo :"$orig_CFLAGS" | sed 's/^://' | tr -c -d $alll$allu$alln`
-# optimisation: only if orig_CFLAGS is empty
-test x"$i" = x"" && case $ct in
-hpcc)
-	phase=u
-	ac_flags 1 otwo +O2
-	phase=x
-	;;
-kencc|tcc|tendra)
-	# no special optimisation
-	;;
-sunpro)
-	cat >x <<-'EOF'
-		int main(void) { return (0); }
-		#define __IDSTRING_CONCAT(l,p)	__LINTED__ ## l ## _ ## p
-		#define __IDSTRING_EXPAND(l,p)	__IDSTRING_CONCAT(l,p)
-		#define pad			void __IDSTRING_EXPAND(__LINE__,x)(void) { }
-	EOF
-	yes pad | head -n 256 >>x
-	ac_flags - 1 otwo -xO2 <x
-	rmf x
-	;;
-xlc)
-	ac_flags 1 othree "-O3 -qstrict"
-	test 1 = $HAVE_CAN_OTHREE || ac_flags 1 otwo -O2
-	;;
-*)
-	ac_flags 1 otwo -O2
-	test 1 = $HAVE_CAN_OTWO || ac_flags 1 optimise -O
-	;;
-esac
-# other flags: just add them if they are supported
-i=0
-case $ct in
-bcc)
-	ac_flags 1 strpool "${ccpc}-d" 'if string pooling can be enabled'
-	;;
-clang)
-	i=1
-	;;
-dec)
-	ac_flags 0 verb -verbose
-	ac_flags 1 rodata -readonly_strings
-	;;
-dmc)
-	ac_flags 1 decl "${ccpc}-r" 'for strict prototype checks'
-	ac_flags 1 schk "${ccpc}-s" 'for stack overflow checking'
-	;;
-gcc)
-	# The following tests run with -Werror (gcc only) if possible
-	NOWARN=$DOWARN; phase=u
-	ac_flags 1 wnodeprecateddecls -Wno-deprecated-declarations
-	# mksh is not written in CFrustFrust!
-	ac_flags 1 no_eh_frame -fno-asynchronous-unwind-tables
-	ac_flags 1 fnostrictaliasing -fno-strict-aliasing
-	ac_flags 1 fstackprotectorall -fstack-protector-all
-	test $cm = dragonegg && case " $CC $CFLAGS $LDFLAGS " in
-	*\ -fplugin=*dragonegg*) ;;
-	*) ac_flags 1 fplugin_dragonegg -fplugin=dragonegg ;;
-	esac
-	case $cm in
-	combine)
-		fv=0
-		checks='7 8'
-		;;
-	lto)
-		fv=0
-		checks='1 2 3 4 5 6 7 8'
-		;;
-	*)
-		fv=1
-		;;
-	esac
-	test $fv = 1 || for what in $checks; do
-		test $fv = 1 && break
-		case $what in
-		1)	t_cflags='-flto=jobserver'
-			t_ldflags='-fuse-linker-plugin'
-			t_use=1 t_name=fltojs_lp ;;
-		2)	t_cflags='-flto=jobserver' t_ldflags=''
-			t_use=1 t_name=fltojs_nn ;;
-		3)	t_cflags='-flto=jobserver'
-			t_ldflags='-fno-use-linker-plugin -fwhole-program'
-			t_use=1 t_name=fltojs_np ;;
-		4)	t_cflags='-flto'
-			t_ldflags='-fuse-linker-plugin'
-			t_use=1 t_name=fltons_lp ;;
-		5)	t_cflags='-flto' t_ldflags=''
-			t_use=1 t_name=fltons_nn ;;
-		6)	t_cflags='-flto'
-			t_ldflags='-fno-use-linker-plugin -fwhole-program'
-			t_use=1 t_name=fltons_np ;;
-		7)	t_cflags='-fwhole-program --combine' t_ldflags=''
-			t_use=0 t_name=combine cm=combine ;;
-		8)	fv=1 cm=normal ;;
-		esac
-		test $fv = 1 && break
-		ac_flags $t_use $t_name "$t_cflags" \
-		    "if gcc supports $t_cflags $t_ldflags" "$t_ldflags"
-	done
-	i=1
-	;;
-hpcc)
-	phase=u
-	# probably not needed
-	#ac_flags 1 agcc -Agcc 'for support of GCC extensions'
-	phase=x
-	;;
-icc)
-	ac_flags 1 fnobuiltinsetmode -fno-builtin-setmode
-	ac_flags 1 fnostrictaliasing -fno-strict-aliasing
-	ac_flags 1 fstacksecuritycheck -fstack-security-check
-	i=1
-	;;
-mipspro)
-	ac_flags 1 fullwarn -fullwarn 'for remark output support'
-	;;
-msc)
-	ac_flags 1 strpool "${ccpc}/GF" 'if string pooling can be enabled'
-	echo 'int main(void) { char test[64] = ""; return (*test); }' >x
-	ac_flags - 1 stackon "${ccpc}/GZ" 'if stack checks can be enabled' <x
-	ac_flags - 1 stckall "${ccpc}/Ge" 'stack checks for all functions' <x
-	ac_flags - 1 secuchk "${ccpc}/GS" 'for compiler security checks' <x
-	rmf x
-	ac_flags 1 wall "${ccpc}/Wall" 'to enable all warnings'
-	ac_flags 1 wp64 "${ccpc}/Wp64" 'to enable 64-bit warnings'
-	;;
-nwcc)
-	i=1
-	#broken# ac_flags 1 ssp -stackprotect
-	;;
-sunpro)
-	phase=u
-	ac_flags 1 v -v
-	ac_flags 1 ipo -xipo 'for cross-module optimisation'
-	phase=x
-	;;
-tcc)
-	: #broken# ac_flags 1 boundschk -b
-	;;
-tendra)
-	ac_flags 0 ysystem -Ysystem
-	test 1 = $HAVE_CAN_YSYSTEM && CPPFLAGS="-Ysystem $CPPFLAGS"
-	ac_flags 1 extansi -Xa
-	;;
-xlc)
-	ac_flags 1 rodata "-qro -qroconst -qroptr"
-	ac_flags 1 rtcheck -qcheck=all
-	#ac_flags 1 rtchkc -qextchk	# reported broken
-	ac_flags 1 wformat "-qformat=all -qformat=nozln"
-	#ac_flags 1 wp64 -qwarn64	# too verbose for now
-	;;
-esac
-# flags common to a subset of compilers (run with -Werror on gcc)
-if test 1 = $i; then
-	ac_flags 1 wall -Wall
-	ac_flags 1 fwrapv -fwrapv
-fi
-
-phase=x
-# The following tests run with -Werror or similar (all compilers) if possible
-NOWARN=$DOWARN
-test $ct = pcc && phase=u
-
-#
-# Compiler: check for stuff that only generates warnings
-#
-ac_test attribute_bounded '' 'for __attribute__((__bounded__))' <<-'EOF'
-	#if defined(__TenDRA__) || (defined(__GNUC__) && (__GNUC__ < 2))
-	extern int thiswillneverbedefinedIhope(void);
-	/* force a failure: TenDRA and gcc 1.42 have false positive here */
-	int main(void) { return (thiswillneverbedefinedIhope()); }
-	#else
-	#include <string.h>
-	#undef __attribute__
-	int xcopy(const void *, void *, size_t)
-	    __attribute__((__bounded__ (__buffer__, 1, 3)))
-	    __attribute__((__bounded__ (__buffer__, 2, 3)));
-	int main(int ac, char *av[]) { return (xcopy(av[0], av[--ac], 1)); }
-	int xcopy(const void *s, void *d, size_t n) {
-		/*
-		 * if memmove does not exist, we are not on a system
-		 * with GCC with __bounded__ attribute either so poo
-		 */
-		memmove(d, s, n); return ((int)n);
-	}
-	#endif
-EOF
-ac_test attribute_format '' 'for __attribute__((__format__))' <<-'EOF'
-	#if defined(__TenDRA__) || (defined(__GNUC__) && (__GNUC__ < 2))
-	extern int thiswillneverbedefinedIhope(void);
-	/* force a failure: TenDRA and gcc 1.42 have false positive here */
-	int main(void) { return (thiswillneverbedefinedIhope()); }
-	#else
-	#define fprintf printfoo
-	#include <stdio.h>
-	#undef __attribute__
-	#undef fprintf
-	extern int fprintf(FILE *, const char *format, ...)
-	    __attribute__((__format__ (__printf__, 2, 3)));
-	int main(int ac, char **av) { return (fprintf(stderr, "%s%d", *av, ac)); }
-	#endif
-EOF
-ac_test attribute_noreturn '' 'for __attribute__((__noreturn__))' <<-'EOF'
-	#if defined(__TenDRA__) || (defined(__GNUC__) && (__GNUC__ < 2))
-	extern int thiswillneverbedefinedIhope(void);
-	/* force a failure: TenDRA and gcc 1.42 have false positive here */
-	int main(void) { return (thiswillneverbedefinedIhope()); }
-	#else
-	#include <stdlib.h>
-	#undef __attribute__
-	void fnord(void) __attribute__((__noreturn__));
-	int main(void) { fnord(); }
-	void fnord(void) { exit(0); }
-	#endif
-EOF
-ac_test attribute_unused '' 'for __attribute__((__unused__))' <<-'EOF'
-	#if defined(__TenDRA__) || (defined(__GNUC__) && (__GNUC__ < 2))
-	extern int thiswillneverbedefinedIhope(void);
-	/* force a failure: TenDRA and gcc 1.42 have false positive here */
-	int main(void) { return (thiswillneverbedefinedIhope()); }
-	#else
-	int main(int ac __attribute__((__unused__)), char **av
-	    __attribute__((__unused__))) { return (0); }
-	#endif
-EOF
-ac_test attribute_used '' 'for __attribute__((__used__))' <<-'EOF'
-	#if defined(__TenDRA__) || (defined(__GNUC__) && (__GNUC__ < 2))
-	extern int thiswillneverbedefinedIhope(void);
-	/* force a failure: TenDRA and gcc 1.42 have false positive here */
-	int main(void) { return (thiswillneverbedefinedIhope()); }
-	#else
-	static const char fnord[] __attribute__((__used__)) = "42";
-	int main(void) { return (0); }
-	#endif
-EOF
-
-# End of tests run with -Werror
-NOWARN=$save_NOWARN
-phase=x
-
-#
-# mksh: flavours (full/small mksh, omit certain stuff)
-#
-if ac_ifcpp 'ifdef MKSH_SMALL' isset_MKSH_SMALL '' \
-    "if a reduced-feature mksh is requested"; then
-	: ${HAVE_NICE=0}
-	: ${HAVE_PERSISTENT_HISTORY=0}
-	check_categories="$check_categories smksh"
-	HAVE_ISSET_MKSH_CONSERVATIVE_FDS=1	# from sh.h
-fi
-ac_ifcpp 'if defined(MKSH_BINSHPOSIX) || defined(MKSH_BINSHREDUCED)' \
-    isset_MKSH_BINSH '' 'if invoking as sh should be handled specially' && \
-    check_categories="$check_categories binsh"
-ac_ifcpp 'ifdef MKSH_UNEMPLOYED' isset_MKSH_UNEMPLOYED '' \
-    "if mksh will be built without job control" && \
-    check_categories="$check_categories arge"
-ac_ifcpp 'ifdef MKSH_NOPROSPECTOFWORK' isset_MKSH_NOPROSPECTOFWORK '' \
-    "if mksh will be built without job signals" && \
-    check_categories="$check_categories arge nojsig"
-ac_ifcpp 'ifdef MKSH_ASSUME_UTF8' isset_MKSH_ASSUME_UTF8 '' \
-    'if the default UTF-8 mode is specified' && : ${HAVE_SETLOCALE_CTYPE=0}
-ac_ifcpp 'ifdef MKSH_CONSERVATIVE_FDS' isset_MKSH_CONSERVATIVE_FDS '' \
-    'if traditional/conservative fd use is requested' && \
-    check_categories="$check_categories convfds"
-#ac_ifcpp 'ifdef MKSH_DISABLE_DEPRECATED' isset_MKSH_DISABLE_DEPRECATED '' \
-#    "if deprecated features are to be omitted" && \
-#    check_categories="$check_categories nodeprecated"
-#ac_ifcpp 'ifdef MKSH_DISABLE_EXPERIMENTAL' isset_MKSH_DISABLE_EXPERIMENTAL '' \
-#    "if experimental features are to be omitted" && \
-#    check_categories="$check_categories noexperimental"
-ac_ifcpp 'ifdef MKSH_MIDNIGHTBSD01ASH_COMPAT' isset_MKSH_MIDNIGHTBSD01ASH_COMPAT '' \
-    'if the MidnightBSD 0.1 ash compatibility mode is requested' && \
-    check_categories="$check_categories mnbsdash"
-
-#
-# Environment: headers
-#
-ac_header sys/time.h sys/types.h
-ac_header time.h sys/types.h
-test "11" = "$HAVE_SYS_TIME_H$HAVE_TIME_H" || HAVE_BOTH_TIME_H=0
-ac_test both_time_h '' 'whether <sys/time.h> and <time.h> can both be included' <<-'EOF'
-	#include <sys/types.h>
-	#include <sys/time.h>
-	#include <time.h>
-	int main(void) { struct tm tm; return ((int)sizeof(tm)); }
-EOF
-ac_header sys/bsdtypes.h
-ac_header sys/file.h sys/types.h
-ac_header sys/mkdev.h sys/types.h
-ac_header sys/mman.h sys/types.h
-ac_header sys/param.h
-ac_header sys/resource.h sys/types.h _time
-ac_header sys/select.h sys/types.h
-ac_header sys/sysmacros.h
-ac_header bstring.h
-ac_header grp.h sys/types.h
-ac_header libgen.h
-ac_header libutil.h sys/types.h
-ac_header paths.h
-ac_header stdint.h stdarg.h
-# include strings.h only if compatible with string.h
-ac_header strings.h sys/types.h string.h
-ac_header termios.h
-ac_header ulimit.h sys/types.h
-ac_header values.h
-
-#
-# Environment: definitions
-#
-echo '#include <sys/types.h>
-/* check that off_t can represent 2^63-1 correctly, thx FSF */
-#define LARGE_OFF_T (((off_t)1 << 62) - 1 + ((off_t)1 << 62))
-int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 &&
-    LARGE_OFF_T % 2147483647 == 1) ? 1 : -1];
-int main(void) { return (0); }' >lft.c
-ac_testn can_lfs '' "for large file support" <lft.c
-save_CPPFLAGS=$CPPFLAGS
-add_cppflags -D_FILE_OFFSET_BITS=64
-ac_testn can_lfs_sus '!' can_lfs 0 "... with -D_FILE_OFFSET_BITS=64" <lft.c
-if test 0 = $HAVE_CAN_LFS_SUS; then
-	CPPFLAGS=$save_CPPFLAGS
-	add_cppflags -D_LARGE_FILES=1
-	ac_testn can_lfs_aix '!' can_lfs 0 "... with -D_LARGE_FILES=1" <lft.c
-	test 1 = $HAVE_CAN_LFS_AIX || CPPFLAGS=$save_CPPFLAGS
-fi
-rmf lft*	# end of large file support test
-
-#
-# Environment: types
-#
-ac_test can_inttypes '!' stdint_h 1 "for standard 32-bit integer types" <<-'EOF'
-	#include <sys/types.h>
-	#include <stddef.h>
-	int main(int ac, char **av) { return ((uint32_t)(ptrdiff_t)*av + (int32_t)ac); }
-EOF
-ac_test can_ucbints '!' can_inttypes 1 "for UCB 32-bit integer types" <<-'EOF'
-	#include <sys/types.h>
-	#include <stddef.h>
-	int main(int ac, char **av) { return ((u_int32_t)(ptrdiff_t)*av + (int32_t)ac); }
-EOF
-ac_test can_int8type '!' stdint_h 1 "for standard 8-bit integer type" <<-'EOF'
-	#include <sys/types.h>
-	#include <stddef.h>
-	int main(int ac, char **av) { return ((uint8_t)(ptrdiff_t)av[ac]); }
-EOF
-ac_test can_ucbint8 '!' can_int8type 1 "for UCB 8-bit integer type" <<-'EOF'
-	#include <sys/types.h>
-	#include <stddef.h>
-	int main(int ac, char **av) { return ((u_int8_t)(ptrdiff_t)av[ac]); }
-EOF
-
-ac_test rlim_t <<-'EOF'
-	#include <sys/types.h>
-	#if HAVE_BOTH_TIME_H
-	#include <sys/time.h>
-	#include <time.h>
-	#elif HAVE_SYS_TIME_H
-	#include <sys/time.h>
-	#elif HAVE_TIME_H
-	#include <time.h>
-	#endif
-	#if HAVE_SYS_RESOURCE_H
-	#include <sys/resource.h>
-	#endif
-	#include <unistd.h>
-	int main(void) { return ((int)(rlim_t)0); }
-EOF
-
-# only testn: added later below
-ac_testn sig_t <<-'EOF'
-	#include <sys/types.h>
-	#include <signal.h>
-	#include <stddef.h>
-	volatile sig_t foo = (sig_t)0;
-	int main(void) { return (foo == (sig_t)0); }
-EOF
-
-ac_testn sighandler_t '!' sig_t 0 <<-'EOF'
-	#include <sys/types.h>
-	#include <signal.h>
-	#include <stddef.h>
-	volatile sighandler_t foo = (sighandler_t)0;
-	int main(void) { return (foo == (sighandler_t)0); }
-EOF
-if test 1 = $HAVE_SIGHANDLER_T; then
-	add_cppflags -Dsig_t=sighandler_t
-	HAVE_SIG_T=1
-fi
-
-ac_testn __sighandler_t '!' sig_t 0 <<-'EOF'
-	#include <sys/types.h>
-	#include <signal.h>
-	#include <stddef.h>
-	volatile __sighandler_t foo = (__sighandler_t)0;
-	int main(void) { return (foo == (__sighandler_t)0); }
-EOF
-if test 1 = $HAVE___SIGHANDLER_T; then
-	add_cppflags -Dsig_t=__sighandler_t
-	HAVE_SIG_T=1
-fi
-
-test 1 = $HAVE_SIG_T || add_cppflags -Dsig_t=nosig_t
-ac_cppflags SIG_T
-
-#
-# check whether whatever we use for the final link will succeed
-#
-if test $cm = makefile; then
-	: nothing to check
-else
-	HAVE_LINK_WORKS=x
-	ac_testinit link_works '' 'checking if the final link command may succeed'
-	fv=1
-	cat >conftest.c <<-'EOF'
-		#define EXTERN
-		#define MKSH_INCLUDES_ONLY
-		#include "sh.h"
-		__RCSID("$MirOS: src/bin/mksh/Build.sh,v 1.645 2013/08/10 13:44:25 tg Exp $");
-		int main(void) { printf("Hello, World!\n"); return (0); }
-EOF
-	case $cm in
-	llvm)
-		v "$CC $CFLAGS $CPPFLAGS $NOWARN -emit-llvm -c conftest.c" || fv=0
-		rmf $tfn.s
-		test $fv = 0 || v "llvm-link -o - conftest.o | opt $optflags | llc -o $tfn.s" || fv=0
-		test $fv = 0 || v "$CC $CFLAGS $LDFLAGS -o $tcfn $tfn.s $LIBS $ccpr"
-		;;
-	dragonegg)
-		v "$CC $CFLAGS $CPPFLAGS $NOWARN -S -flto conftest.c" || fv=0
-		test $fv = 0 || v "mv conftest.s conftest.ll"
-		test $fv = 0 || v "llvm-as conftest.ll" || fv=0
-		rmf $tfn.s
-		test $fv = 0 || v "llvm-link -o - conftest.bc | opt $optflags | llc -o $tfn.s" || fv=0
-		test $fv = 0 || v "$CC $CFLAGS $LDFLAGS -o $tcfn $tfn.s $LIBS $ccpr"
-		;;
-	combine)
-		v "$CC $CFLAGS $CPPFLAGS $LDFLAGS -fwhole-program --combine $NOWARN -o $tcfn conftest.c $LIBS $ccpr"
-		;;
-	lto|normal)
-		cm=normal
-		v "$CC $CFLAGS $CPPFLAGS $NOWARN -c conftest.c" || fv=0
-		test $fv = 0 || v "$CC $CFLAGS $LDFLAGS -o $tcfn conftest.o $LIBS $ccpr"
-		;;
-	esac
-	test -f $tcfn || fv=0
-	ac_testdone
-	test $fv = 1 || exit 1
-fi
-
-#
-# Environment: errors and signals
-#
-test x"NetBSD" = x"$TARGET_OS" && $e Ignore the compatibility warning.
-
-ac_testn sys_errlist '' "the sys_errlist[] array and sys_nerr" <<-'EOF'
-	extern const int sys_nerr;
-	extern const char * const sys_errlist[];
-	int main(void) { return (*sys_errlist[sys_nerr - 1]); }
-EOF
-ac_testn _sys_errlist '!' sys_errlist 0 "the _sys_errlist[] array and _sys_nerr" <<-'EOF'
-	extern const int _sys_nerr;
-	extern const char * const _sys_errlist[];
-	int main(void) { return (*_sys_errlist[_sys_nerr - 1]); }
-EOF
-if test 1 = "$HAVE__SYS_ERRLIST"; then
-	add_cppflags -Dsys_nerr=_sys_nerr
-	add_cppflags -Dsys_errlist=_sys_errlist
-	HAVE_SYS_ERRLIST=1
-fi
-ac_cppflags SYS_ERRLIST
-
-for what in name list; do
-	uwhat=`upper $what`
-	ac_testn sys_sig$what '' "the sys_sig${what}[] array" <<-EOF
-		extern const char * const sys_sig${what}[];
-		int main(void) { return (sys_sig${what}[0][0]); }
-	EOF
-	ac_testn _sys_sig$what '!' sys_sig$what 0 "the _sys_sig${what}[] array" <<-EOF
-		extern const char * const _sys_sig${what}[];
-		int main(void) { return (_sys_sig${what}[0][0]); }
-	EOF
-	eval uwhat_v=\$HAVE__SYS_SIG$uwhat
-	if test 1 = "$uwhat_v"; then
-		add_cppflags -Dsys_sig$what=_sys_sig$what
-		eval HAVE_SYS_SIG$uwhat=1
-	fi
-	ac_cppflags SYS_SIG$uwhat
-done
-
-#
-# Environment: library functions
-#
-ac_test flock <<-'EOF'
-	#include <sys/types.h>
-	#include <fcntl.h>
-	#undef flock
-	#if HAVE_SYS_FILE_H
-	#include <sys/file.h>
-	#endif
-	int main(void) { return (flock(0, LOCK_EX | LOCK_UN)); }
-EOF
-
-ac_test lock_fcntl '!' flock 1 'whether we can lock files with fcntl' <<-'EOF'
-	#include <fcntl.h>
-	#undef flock
-	int main(void) {
-		struct flock lks;
-		lks.l_type = F_WRLCK | F_UNLCK;
-		return (fcntl(0, F_SETLKW, &lks));
-	}
-EOF
-
-ac_test getrusage <<-'EOF'
-	#define MKSH_INCLUDES_ONLY
-	#include "sh.h"
-	int main(void) {
-		struct rusage ru;
-		return (getrusage(RUSAGE_SELF, &ru) +
-		    getrusage(RUSAGE_CHILDREN, &ru));
-	}
-EOF
-
-ac_test gettimeofday <<-'EOF'
-	#define MKSH_INCLUDES_ONLY
-	#include "sh.h"
-	int main(void) { struct timeval tv; return (gettimeofday(&tv, NULL)); }
-EOF
-
-ac_test killpg <<-'EOF'
-	#include <signal.h>
-	int main(int ac, char *av[]) { return (av[0][killpg(123, ac)]); }
-EOF
-
-ac_test memmove <<-'EOF'
-	#include <sys/types.h>
-	#include <stddef.h>
-	#include <string.h>
-	#if HAVE_STRINGS_H
-	#include <strings.h>
-	#endif
-	int main(int ac, char *av[]) {
-		return (*(int *)(void *)memmove(av[0], av[1], ac));
-	}
-EOF
-
-ac_test mknod '' 'if to use mknod(), makedev() and friends' <<-'EOF'
-	#define MKSH_INCLUDES_ONLY
-	#include "sh.h"
-	int main(int ac, char *av[]) {
-		dev_t dv;
-		dv = makedev((unsigned int)ac, (unsigned int)av[0][0]);
-		return (mknod(av[0], (mode_t)0, dv) ? (int)major(dv) :
-		    (int)minor(dv));
-	}
-EOF
-
-ac_test mmap lock_fcntl 0 'for mmap and munmap' <<-'EOF'
-	#include <sys/types.h>
-	#if HAVE_SYS_FILE_H
-	#include <sys/file.h>
-	#endif
-	#if HAVE_SYS_MMAN_H
-	#include <sys/mman.h>
-	#endif
-	#include <stddef.h>
-	#include <stdlib.h>
-	int main(void) { return ((void *)mmap(NULL, (size_t)0,
-	    PROT_READ, MAP_PRIVATE, 0, (off_t)0) == (void *)NULL ? 1 :
-	    munmap(NULL, 0)); }
-EOF
-
-ac_test nice <<-'EOF'
-	#include <unistd.h>
-	int main(void) { return (nice(4)); }
-EOF
-
-ac_test revoke <<-'EOF'
-	#include <sys/types.h>
-	#if HAVE_LIBUTIL_H
-	#include <libutil.h>
-	#endif
-	#include <unistd.h>
-	int main(int ac, char *av[]) { return (ac + revoke(av[0])); }
-EOF
-
-ac_test setlocale_ctype '' 'setlocale(LC_CTYPE, "")' <<-'EOF'
-	#include <locale.h>
-	#include <stddef.h>
-	int main(void) { return ((int)(ptrdiff_t)(void *)setlocale(LC_CTYPE, "")); }
-EOF
-
-ac_test langinfo_codeset setlocale_ctype 0 'nl_langinfo(CODESET)' <<-'EOF'
-	#include <langinfo.h>
-	#include <stddef.h>
-	int main(void) { return ((int)(ptrdiff_t)(void *)nl_langinfo(CODESET)); }
-EOF
-
-ac_test select <<-'EOF'
-	#include <sys/types.h>
-	#if HAVE_BOTH_TIME_H
-	#include <sys/time.h>
-	#include <time.h>
-	#elif HAVE_SYS_TIME_H
-	#include <sys/time.h>
-	#elif HAVE_TIME_H
-	#include <time.h>
-	#endif
-	#if HAVE_SYS_BSDTYPES_H
-	#include <sys/bsdtypes.h>
-	#endif
-	#if HAVE_SYS_SELECT_H
-	#include <sys/select.h>
-	#endif
-	#if HAVE_BSTRING_H
-	#include <bstring.h>
-	#endif
-	#include <stddef.h>
-	#include <stdlib.h>
-	#include <string.h>
-	#if HAVE_STRINGS_H
-	#include <strings.h>
-	#endif
-	#include <unistd.h>
-	int main(void) {
-		struct timeval tv = { 1, 200000 };
-		fd_set fds; FD_ZERO(&fds); FD_SET(0, &fds);
-		return (select(FD_SETSIZE, &fds, NULL, NULL, &tv));
-	}
-EOF
-
-ac_test setresugid <<-'EOF'
-	#include <sys/types.h>
-	#include <unistd.h>
-	int main(void) { return (setresuid(0,0,0) + setresgid(0,0,0)); }
-EOF
-
-ac_test setgroups setresugid 0 <<-'EOF'
-	#include <sys/types.h>
-	#if HAVE_GRP_H
-	#include <grp.h>
-	#endif
-	#include <unistd.h>
-	int main(void) { gid_t gid = 0; return (setgroups(0, &gid)); }
-EOF
-
-if test x"$et" = x"klibc"; then
-
-	ac_testn __rt_sigsuspend '' 'whether klibc uses RT signals' <<-'EOF'
-		#define MKSH_INCLUDES_ONLY
-		#include "sh.h"
-		extern int __rt_sigsuspend(const sigset_t *, size_t);
-		int main(void) { return (__rt_sigsuspend(NULL, 0)); }
-EOF
-
-	# no? damn! legacy crap ahead!
-
-	ac_testn __sigsuspend_s '!' __rt_sigsuspend 1 \
-	    'whether sigsuspend is usable (1/2)' <<-'EOF'
-		#define MKSH_INCLUDES_ONLY
-		#include "sh.h"
-		extern int __sigsuspend_s(sigset_t);
-		int main(void) { return (__sigsuspend_s(0)); }
-EOF
-	ac_testn __sigsuspend_xxs '!' __sigsuspend_s 1 \
-	    'whether sigsuspend is usable (2/2)' <<-'EOF'
-		#define MKSH_INCLUDES_ONLY
-		#include "sh.h"
-		extern int __sigsuspend_xxs(int, int, sigset_t);
-		int main(void) { return (__sigsuspend_xxs(0, 0, 0)); }
-EOF
-
-	if test "000" = "$HAVE___RT_SIGSUSPEND$HAVE___SIGSUSPEND_S$HAVE___SIGSUSPEND_XXS"; then
-		# no usable sigsuspend(), use pause() *ugh*
-		add_cppflags -DMKSH_NO_SIGSUSPEND
-	fi
-fi
-
-ac_test strerror '!' sys_errlist 0 <<-'EOF'
-	extern char *strerror(int);
-	int main(int ac, char *av[]) { return (*strerror(*av[ac])); }
-EOF
-
-ac_test strsignal '!' sys_siglist 0 <<-'EOF'
-	#include <string.h>
-	#include <signal.h>
-	int main(void) { return (strsignal(1)[0]); }
-EOF
-
-ac_test strlcpy <<-'EOF'
-	#include <string.h>
-	int main(int ac, char *av[]) { return (strlcpy(*av, av[1],
-	    (size_t)ac)); }
-EOF
-
-#
-# check headers for declarations
-#
-ac_test flock_decl flock 1 'for declaration of flock()' <<-'EOF'
-	#define MKSH_INCLUDES_ONLY
-	#include "sh.h"
-	#if HAVE_SYS_FILE_H
-	#include <sys/file.h>
-	#endif
-	int main(void) { return ((flock)(0, 0)); }
-EOF
-ac_test revoke_decl revoke 1 'for declaration of revoke()' <<-'EOF'
-	#define MKSH_INCLUDES_ONLY
-	#include "sh.h"
-	int main(void) { return ((revoke)("")); }
-EOF
-ac_test sys_errlist_decl sys_errlist 0 "for declaration of sys_errlist[] and sys_nerr" <<-'EOF'
-	#define MKSH_INCLUDES_ONLY
-	#include "sh.h"
-	int main(void) { return (*sys_errlist[sys_nerr - 1]); }
-EOF
-ac_test sys_siglist_decl sys_siglist 0 'for declaration of sys_siglist[]' <<-'EOF'
-	#define MKSH_INCLUDES_ONLY
-	#include "sh.h"
-	int main(void) { return (sys_siglist[0][0]); }
-EOF
-
-#
-# other checks
-#
-fd='if to use persistent history'
-ac_cache PERSISTENT_HISTORY || case $HAVE_MMAP$HAVE_FLOCK$HAVE_LOCK_FCNTL in
-11*|101) fv=1 ;;
-esac
-test 1 = $fv || check_categories="$check_categories no-histfile"
-ac_testdone
-ac_cppflags
-
-save_CFLAGS=$CFLAGS
-ac_testn compile_time_asserts_$$ '' 'whether compile-time assertions pass' <<-'EOF'
-	#define MKSH_INCLUDES_ONLY
-	#include "sh.h"
-	#ifndef CHAR_BIT
-	#define CHAR_BIT 8	/* defuse this test on really legacy systems */
-	#endif
-	struct ctasserts {
-	#define cta(name, assertion) char name[(assertion) ? 1 : -1]
-/* this one should be defined by the standard */
-cta(char_is_1_char, (sizeof(char) == 1) && (sizeof(signed char) == 1) &&
-    (sizeof(unsigned char) == 1));
-cta(char_is_8_bits, ((CHAR_BIT) == 8) && ((int)(unsigned char)0xFF == 0xFF) &&
-    ((int)(unsigned char)0x100 == 0) && ((int)(unsigned char)(int)-1 == 0xFF));
-/* the next assertion is probably not really needed */
-cta(short_is_2_char, sizeof(short) == 2);
-cta(short_size_no_matter_of_signedness, sizeof(short) == sizeof(unsigned short));
-/* the next assertion is probably not really needed */
-cta(int_is_4_char, sizeof(int) == 4);
-cta(int_size_no_matter_of_signedness, sizeof(int) == sizeof(unsigned int));
-
-cta(long_ge_int, sizeof(long) >= sizeof(int));
-cta(long_size_no_matter_of_signedness, sizeof(long) == sizeof(unsigned long));
-
-#ifndef MKSH_LEGACY_MODE
-/* the next assertion is probably not really needed */
-cta(ari_is_4_char, sizeof(mksh_ari_t) == 4);
-/* but this is */
-cta(ari_has_31_bit, 0 < (mksh_ari_t)(((((mksh_ari_t)1 << 15) << 15) - 1) * 2 + 1));
-/* the next assertion is probably not really needed */
-cta(uari_is_4_char, sizeof(mksh_uari_t) == 4);
-/* but the next three are; we REQUIRE unsigned integer wraparound */
-cta(uari_has_31_bit, 0 < (mksh_uari_t)(((((mksh_uari_t)1 << 15) << 15) - 1) * 2 + 1));
-cta(uari_has_32_bit, 0 < (mksh_uari_t)(((((mksh_uari_t)1 << 15) << 15) - 1) * 4 + 3));
-cta(uari_wrap_32_bit,
-    (mksh_uari_t)(((((mksh_uari_t)1 << 15) << 15) - 1) * 4 + 3) >
-    (mksh_uari_t)(((((mksh_uari_t)1 << 15) << 15) - 1) * 4 + 4));
-#define NUM 22
-#else
-#define NUM 16
-#endif
-/* these are always required */
-cta(ari_is_signed, (mksh_ari_t)-1 < (mksh_ari_t)0);
-cta(uari_is_unsigned, (mksh_uari_t)-1 > (mksh_uari_t)0);
-/* we require these to have the precisely same size and assume 2s complement */
-cta(ari_size_no_matter_of_signedness, sizeof(mksh_ari_t) == sizeof(mksh_uari_t));
-
-cta(sizet_size_no_matter_of_signedness, sizeof(ssize_t) == sizeof(size_t));
-cta(ptrdifft_sizet_same_size, sizeof(ptrdiff_t) == sizeof(size_t));
-cta(ptrdifft_voidptr_same_size, sizeof(ptrdiff_t) == sizeof(void *));
-cta(ptrdifft_funcptr_same_size, sizeof(ptrdiff_t) == sizeof(void (*)(void)));
-/* our formatting routines assume this */
-cta(ptr_fits_in_long, sizeof(ptrdiff_t) <= sizeof(long));
-/* for struct alignment people */
-		char padding[64 - NUM];
-	};
-char ctasserts_dblcheck[sizeof(struct ctasserts) == 64 ? 1 : -1];
-	int main(void) { return (sizeof(ctasserts_dblcheck)); }
-EOF
-CFLAGS=$save_CFLAGS
-eval test 1 = \$HAVE_COMPILE_TIME_ASSERTS_$$ || exit 1
-
-#
-# extra checks for legacy mksh
-#
-if test $legacy = 1; then
-	ac_test long_32bit '' 'whether long is 32 bit wide' <<-'EOF'
-		#define MKSH_INCLUDES_ONLY
-		#include "sh.h"
-		#ifndef CHAR_BIT
-		#define CHAR_BIT 0
-		#endif
-		struct ctasserts {
-		#define cta(name, assertion) char name[(assertion) ? 1 : -1]
-			cta(char_is_8_bits, (CHAR_BIT) == 8);
-			cta(long_is_32_bits, sizeof(long) == 4);
-		};
-		int main(void) { return (sizeof(struct ctasserts)); }
-EOF
-
-	ac_test long_64bit '!' long_32bit 0 'whether long is 64 bit wide' <<-'EOF'
-		#define MKSH_INCLUDES_ONLY
-		#include "sh.h"
-		#ifndef CHAR_BIT
-		#define CHAR_BIT 0
-		#endif
-		struct ctasserts {
-		#define cta(name, assertion) char name[(assertion) ? 1 : -1]
-			cta(char_is_8_bits, (CHAR_BIT) == 8);
-			cta(long_is_64_bits, sizeof(long) == 8);
-		};
-		int main(void) { return (sizeof(struct ctasserts)); }
-EOF
-
-	case $HAVE_LONG_32BIT$HAVE_LONG_64BIT in
-	10) check_categories="$check_categories int:32" ;;
-	01) check_categories="$check_categories int:64" ;;
-	*) check_categories="$check_categories int:u" ;;
-	esac
-fi
-
-#
-# Compiler: Praeprocessor (only if needed)
-#
-test 0 = $HAVE_SYS_SIGNAME && if ac_testinit cpp_dd '' \
-    'checking if the C Preprocessor supports -dD'; then
-	echo '#define foo bar' >conftest.c
-	vv ']' "$CPP $CFLAGS $CPPFLAGS $NOWARN -dD conftest.c >x"
-	grep '#define foo bar' x >/dev/null 2>&1 && fv=1
-	rmf conftest.c x vv.out
-	ac_testdone
-fi
-
-#
-# End of mirtoconf checks
-#
-$e ... done.
-
-# Some operating systems have ancient versions of ed(1) writing
-# the character count to standard output; cope for that
-echo wq >x
-ed x <x 2>/dev/null | grep 3 >/dev/null 2>&1 && \
-    check_categories="$check_categories $oldish_ed"
-rmf x vv.out
-
-if test 0 = $HAVE_SYS_SIGNAME; then
-	if test 1 = $HAVE_CPP_DD; then
-		$e Generating list of signal names...
-	else
-		$e No list of signal names available via cpp. Falling back...
-	fi
-	sigseenone=:
-	sigseentwo=:
-	echo '#include <signal.h>
-#ifndef NSIG
-#if defined(_NSIG)
-#define NSIG _NSIG
-#elif defined(SIGMAX)
-#define NSIG (SIGMAX+1)
-#elif defined(_SIGMAX)
-#define NSIG (_SIGMAX+1)
-#endif
-#endif
-int
-mksh_cfg= NSIG
-;' >conftest.c
-	# GNU sed 2.03 segfaults when optimising this to sed -n
-	NSIG=`vq "$CPP $CFLAGS $CPPFLAGS $NOWARN conftest.c" | \
-	    grep '^ *mksh_cfg *=' | \
-	    sed 's/^ *mksh_cfg *=[	 ]*\([()0-9x+-][()0-9x+	 -]*\).*$/\1/'`
-	case $NSIG in
-	*mksh_cfg*) $e "Error: NSIG='$NSIG'"; NSIG=0 ;;
-	*[\ \(\)+-]*) NSIG=`"$AWK" "BEGIN { print $NSIG }" </dev/null` ;;
-	esac
-	printf=printf
-	(printf hallo) >/dev/null 2>&1 || printf=echo
-	test $printf = echo || test "`printf %d 42`" = 42 || printf=echo
-	test $printf = echo || NSIG=`printf %d "$NSIG" 2>/dev/null`
-	$printf "NSIG=$NSIG ... "
-	sigs="INT SEGV ABRT KILL ALRM BUS CHLD CLD CONT DIL EMT FPE HUP ILL"
-	sigs="$sigs INFO IO IOT LOST PIPE PROF PWR QUIT RESV SAK STOP SYS TERM"
-	sigs="$sigs TRAP TSTP TTIN TTOU URG USR1 USR2 VTALRM WINCH XCPU XFSZ"
-	test 1 = $HAVE_CPP_DD && test $NSIG -gt 1 && sigs="$sigs "`vq \
-	    "$CPP $CFLAGS $CPPFLAGS $NOWARN -dD conftest.c" | \
-	    grep '[	 ]SIG[A-Z0-9][A-Z0-9]*[	 ]' | \
-	    sed 's/^.*[	 ]SIG\([A-Z0-9][A-Z0-9]*\)[	 ].*$/\1/' | sort`
-	test $NSIG -gt 1 || sigs=
-	for name in $sigs; do
-		case $sigseenone in
-		*:$name:*) continue ;;
-		esac
-		sigseenone=$sigseenone$name:
-		echo '#include <signal.h>' >conftest.c
-		echo int >>conftest.c
-		echo mksh_cfg= SIG$name >>conftest.c
-		echo ';' >>conftest.c
-		# GNU sed 2.03 croaks on optimising this, too
-		vq "$CPP $CFLAGS $CPPFLAGS $NOWARN conftest.c" | \
-		    grep '^ *mksh_cfg *=' | \
-		    sed 's/^ *mksh_cfg *=[	 ]*\([0-9][0-9x]*\).*$/:\1 '$name/
-	done | sed -n '/^:[^ ]/s/^://p' | while read nr name; do
-		test $printf = echo || nr=`printf %d "$nr" 2>/dev/null`
-		test $nr -gt 0 && test $nr -le $NSIG || continue
-		case $sigseentwo in
-		*:$nr:*) ;;
-		*)	echo "		{ \"$name\", $nr },"
-			sigseentwo=$sigseentwo$nr:
-			$printf "$name=$nr " >&2
-			;;
-		esac
-	done 2>&1 >signames.inc
-	rmf conftest.c
-	$e done.
-fi
-
-addsrcs -s '!' HAVE_STRLCPY strlcpy.c
-addsrcs USE_PRINTF_BUILTIN printf.c
-test 1 = "$USE_PRINTF_BUILTIN" && add_cppflags -DMKSH_PRINTF_BUILTIN
-test 1 = "$HAVE_CAN_VERB" && CFLAGS="$CFLAGS -verbose"
-test -n "$LDSTATIC" && add_cppflags -DMKSH_OPTSTATIC
-add_cppflags -DMKSH_BUILD_R=481
-
-$e $bi$me: Finished configuration testing, now producing output.$ao
-
-files=
-objs=
-sp=
-case $tcfn in
-a.exe)	mkshexe=$tfn.exe ;;
-*)	mkshexe=$tfn ;;
-esac
-case $curdir in
-*\ *)	mkshshebang="#!./$mkshexe" ;;
-*)	mkshshebang="#!$curdir/$mkshexe" ;;
-esac
-cat >test.sh <<-EOF
-	$mkshshebang
-	LC_ALL=C PATH='$PATH'; export LC_ALL PATH
-	test -n "\$KSH_VERSION" || exit 1
-	set -A check_categories -- $check_categories
-	pflag='$curdir/$mkshexe'
-	sflag='$srcdir/check.t'
-	usee=0 Pflag=0 Sflag=0 uset=0 vflag=1 xflag=0
-	while getopts "C:e:fPp:QSs:t:v" ch; do case \$ch {
-	(C)	check_categories[\${#check_categories[*]}]=\$OPTARG ;;
-	(e)	usee=1; eflag=\$OPTARG ;;
-	(f)	check_categories[\${#check_categories[*]}]=fastbox ;;
-	(P)	Pflag=1 ;;
-	(+P)	Pflag=0 ;;
-	(p)	pflag=\$OPTARG ;;
-	(Q)	vflag=0 ;;
-	(+Q)	vflag=1 ;;
-	(S)	Sflag=1 ;;
-	(+S)	Sflag=0 ;;
-	(s)	sflag=\$OPTARG ;;
-	(t)	uset=1; tflag=\$OPTARG ;;
-	(v)	vflag=1 ;;
-	(+v)	vflag=0 ;;
-	(*)	xflag=1 ;;
-	}
-	done
-	shift \$((OPTIND - 1))
-	set -A args -- '$srcdir/check.pl' -p "\$pflag"
-	x=
-	for y in "\${check_categories[@]}"; do
-		x=\$x,\$y
-	done
-	if [[ -n \$x ]]; then
-		args[\${#args[*]}]=-C
-		args[\${#args[*]}]=\${x#,}
-	fi
-	if (( usee )); then
-		args[\${#args[*]}]=-e
-		args[\${#args[*]}]=\$eflag
-	fi
-	(( Pflag )) && args[\${#args[*]}]=-P
-	if (( uset )); then
-		args[\${#args[*]}]=-t
-		args[\${#args[*]}]=\$tflag
-	fi
-	(( vflag )) && args[\${#args[*]}]=-v
-	(( xflag )) && args[\${#args[*]}]=-x	# force usage by synerr
-	print Testing mksh for conformance:
-	fgrep -e MirOS: -e MIRBSD "\$sflag"
-	print "This shell is actually:\\n\\t\$KSH_VERSION"
-	print 'test.sh built for mksh $dstversion'
-	cstr='\$os = defined \$^O ? \$^O : "unknown";'
-	cstr="\$cstr"'print \$os . ", Perl version " . \$];'
-	for perli in \$PERL perl5 perl no; do
-		if [[ \$perli = no ]]; then
-			print Cannot find a working Perl interpreter, aborting.
-			exit 1
-		fi
-		print "Trying Perl interpreter '\$perli'..."
-		perlos=\$(\$perli -e "\$cstr")
-		rv=\$?
-		print "Errorlevel \$rv, running on '\$perlos'"
-		if (( rv )); then
-			print "=> not using"
-			continue
-		fi
-		if [[ -n \$perlos ]]; then
-			print "=> using it"
-			break
-		fi
-	done
-	(( Sflag )) || echo + \$perli "\${args[@]}" -s "\$sflag" "\$@"
-	(( Sflag )) || exec \$perli "\${args[@]}" -s "\$sflag" "\$@"$tsts
-	# use of the -S option for check.t split into multiple chunks
-	rv=0
-	for s in "\$sflag".*; do
-		echo + \$perli "\${args[@]}" -s "\$s" "\$@"
-		\$perli "\${args[@]}" -s "\$s" "\$@"$tsts
-		rc=\$?
-		(( rv = rv ? rv : rc ))
-	done
-	exit \$rv
-EOF
-chmod 755 test.sh
-case $cm in
-dragonegg)
-	emitbc="-S -flto"
-	;;
-llvm)
-	emitbc="-emit-llvm -c"
-	;;
-*)
-	emitbc=-c
-	;;
-esac
-echo ": # work around NeXTstep bug" >Rebuild.sh
-echo set -x >>Rebuild.sh
-for file in $SRCS; do
-	op=`echo x"$file" | sed 's/^x\(.*\)\.c$/\1./'`
-	test -f $file || file=$srcdir/$file
-	files="$files$sp$file"
-	sp=' '
-	echo "$CC $CFLAGS $CPPFLAGS $emitbc $file || exit 1" >>Rebuild.sh
-	if test $cm = dragonegg; then
-		echo "mv ${op}s ${op}ll" >>Rebuild.sh
-		echo "llvm-as ${op}ll || exit 1" >>Rebuild.sh
-		objs="$objs$sp${op}bc"
-	else
-		objs="$objs$sp${op}o"
-	fi
-done
-case $cm in
-dragonegg|llvm)
-	echo "rm -f $tfn.s" >>Rebuild.sh
-	echo "llvm-link -o - $objs | opt $optflags | llc -o $tfn.s" >>Rebuild.sh
-	lobjs=$tfn.s
-	;;
-*)
-	lobjs=$objs
-	;;
-esac
-echo tcfn=$mkshexe >>Rebuild.sh
-echo "$CC $CFLAGS $LDFLAGS -o \$tcfn $lobjs $LIBS $ccpr" >>Rebuild.sh
-echo "test -f \$tcfn || exit 1; $SIZE \$tcfn" >>Rebuild.sh
-if test $cm = makefile; then
-	extras='emacsfn.h sh.h sh_flags.h var_spec.h'
-	test 0 = $HAVE_SYS_SIGNAME && extras="$extras signames.inc"
-	cat >Makefrag.inc <<EOF
-# Makefile fragment for building mksh $dstversion
-
-PROG=		$mkshexe
-MAN=		mksh.1
-SRCS=		$SRCS
-SRCS_FP=	$files
-OBJS_BP=	$objs
-INDSRCS=	$extras
-NONSRCS_INST=	dot.mkshrc \$(MAN)
-NONSRCS_NOINST=	Build.sh Makefile Rebuild.sh check.pl check.t test.sh
-CC=		$CC
-CFLAGS=		$CFLAGS
-CPPFLAGS=	$CPPFLAGS
-LDFLAGS=	$LDFLAGS
-LIBS=		$LIBS
-
-# not BSD make only:
-#VPATH=		$srcdir
-#all: \$(PROG)
-#\$(PROG): \$(OBJS_BP)
-#	\$(CC) \$(CFLAGS) \$(LDFLAGS) -o \$@ \$(OBJS_BP) \$(LIBS)
-#\$(OBJS_BP): \$(SRCS_FP) \$(NONSRCS)
-#.c.o:
-#	\$(CC) \$(CFLAGS) \$(CPPFLAGS) -c \$<
-
-# for all make variants:
-#REGRESS_FLAGS=	-f
-#regress:
-#	./test.sh \$(REGRESS_FLAGS)
-check_categories=$check_categories
-
-# for BSD make only:
-#.PATH: $srcdir
-#.include <bsd.prog.mk>
-EOF
-	$e
-	$e Generated Makefrag.inc successfully.
-	exit 0
-fi
-if test $cm = combine; then
-	objs="-o $mkshexe"
-	for file in $SRCS; do
-		test -f $file || file=$srcdir/$file
-		objs="$objs $file"
-	done
-	emitbc="-fwhole-program --combine"
-	v "$CC $CFLAGS $CPPFLAGS $LDFLAGS $emitbc $objs $LIBS $ccpr"
-elif test 1 = $pm; then
-	for file in $SRCS; do
-		test -f $file || file=$srcdir/$file
-		v "$CC $CFLAGS $CPPFLAGS $emitbc $file" &
-	done
-	wait
-else
-	for file in $SRCS; do
-		test $cm = dragonegg && \
-		    op=`echo x"$file" | sed 's/^x\(.*\)\.c$/\1./'`
-		test -f $file || file=$srcdir/$file
-		v "$CC $CFLAGS $CPPFLAGS $emitbc $file" || exit 1
-		if test $cm = dragonegg; then
-			v "mv ${op}s ${op}ll"
-			v "llvm-as ${op}ll" || exit 1
-		fi
-	done
-fi
-case $cm in
-dragonegg|llvm)
-	rmf $tfn.s
-	v "llvm-link -o - $objs | opt $optflags | llc -o $tfn.s"
-	;;
-esac
-tcfn=$mkshexe
-test $cm = combine || v "$CC $CFLAGS $LDFLAGS -o $tcfn $lobjs $LIBS $ccpr"
-test -f $tcfn || exit 1
-test 1 = $r || v "$NROFF -mdoc <'$srcdir/mksh.1' >$tfn.cat1" || \
-    rmf $tfn.cat1
-test 0 = $eq && v $SIZE $tcfn
-i=install
-test -f /usr/ucb/$i && i=/usr/ucb/$i
-test 1 = $eq && e=:
-$e
-$e Installing the shell:
-$e "# $i -c -s -o root -g bin -m 555 $tfn /bin/$tfn"
-if test $legacy = 0; then
-	$e "# grep -x /bin/$tfn /etc/shells >/dev/null || echo /bin/$tfn >>/etc/shells"
-	$e "# $i -c -o root -g bin -m 444 dot.mkshrc /usr/share/doc/mksh/examples/"
-fi
-$e
-$e Installing the manual:
-if test -f $tfn.cat1; then
-	$e "# $i -c -o root -g bin -m 444 $tfn.cat1" \
-	    "/usr/share/man/cat1/$tfn.0"
-	$e or
-fi
-$e "# $i -c -o root -g bin -m 444 $tfn.1 /usr/share/man/man1/$tfn.1"
-$e
-$e Run the regression test suite: ./test.sh
-$e Please also read the sample file dot.mkshrc and the fine manual.
-exit 0
-
-: <<'EOD'
-
-=== Environment used ===
-
-==== build environment ====
-AWK				default: awk
-CC				default: cc
-CFLAGS				if empty, defaults to -xO2 or +O2
-				or -O3 -qstrict or -O2, per compiler
-CPPFLAGS			default empty
-LDFLAGS				default empty; added before sources
-LDSTATIC			set this to '-static'; default unset
-LIBS				default empty; added after sources
-				[Interix] default: -lcrypt (XXX still needed?)
-NOWARN				-Wno-error or similar
-NROFF				default: nroff
-TARGET_OS			default: $(uname -s || uname)
-TARGET_OSREV			[QNX] default: $(uname -r)
-
-==== feature selectors ====
-USE_PRINTF_BUILTIN		1 to include (unsupported) printf(1) as builtin
-===== general format =====
-HAVE_STRLEN			ac_test
-HAVE_STRING_H			ac_header
-HAVE_CAN_FSTACKPROTECTORALL	ac_flags
-
-==== cpp definitions ====
-DEBUG				dont use in production, wants gcc, implies:
-DEBUG_LEAKS			enable freeing resources before exiting
-MKSHRC_PATH			"~/.mkshrc" (do not change)
-MKSH_A4PB			force use of arc4random_pushb
-MKSH_ASSUME_UTF8		(0=disabled, 1=enabled; default: unset)
-MKSH_BINSHPOSIX			if */sh or */-sh, enable set -o posix
-MKSH_BINSHREDUCED		if */sh or */-sh, enable set -o sh
-MKSH_CLRTOEOL_STRING		"\033[K"
-MKSH_CLS_STRING			"\033[;H\033[J"
-MKSH_CONSERVATIVE_FDS		fd 0-9 for scripts, shell only up to 31
-MKSH_DEFAULT_EXECSHELL		"/bin/sh" (do not change)
-MKSH_DEFAULT_PROFILEDIR		"/etc" (do not change)
-MKSH_DEFAULT_TMPDIR		"/tmp" (do not change)
-MKSH_DISABLE_DEPRECATED		disable code paths scheduled for later removal
-MKSH_DISABLE_EXPERIMENTAL	disable code not yet comfy for (LTS) snapshots
-MKSH_DISABLE_TTY_WARNING	shut up warning about ctty if OS cant be fixed
-MKSH_DONT_EMIT_IDSTRING		omit RCS IDs from binary
-MKSH_MIDNIGHTBSD01ASH_COMPAT	set -o sh: additional compatibility quirk
-MKSH_NOPROSPECTOFWORK		disable jobs, co-processes, etc. (do not use)
-MKSH_NOPWNAM			skip PAM calls, for -static on eglibc, Solaris
-MKSH_NO_CMDLINE_EDITING		disable command line editing code entirely
-MKSH_NO_DEPRECATED_WARNING	omit warning when deprecated stuff is run
-MKSH_NO_EXTERNAL_CAT		omit hack to skip cat builtin when flags passed
-MKSH_NO_LIMITS			omit ulimit code
-MKSH_NO_SIGSETJMP		define if sigsetjmp is broken or not available
-MKSH_NO_SIGSUSPEND		use sigprocmask+pause instead of sigsuspend
-MKSH_SMALL			omit some code, optimise hard for size (slower)
-MKSH_SMALL_BUT_FAST		disable some hard-for-size optim. (modern sys.)
-MKSH_S_NOVI=1			disable Vi editing mode (default if MKSH_SMALL)
-MKSH_TYPEDEF_SIG_ATOMIC_T	define to e.g. 'int' if sig_atomic_t is missing
-MKSH_TYPEDEF_SSIZE_T		define to e.g. 'long' if your OS has no ssize_t
-MKSH_UNEMPLOYED			disable job control (but not jobs/co-processes)
-
-=== generic installation instructions ===
-
-Set CC and possibly CFLAGS, CPPFLAGS, LDFLAGS, LIBS. If cross-compiling,
-also set TARGET_OS. To disable tests, set e.g. HAVE_STRLCPY=0; to enable
-them, set to a value other than 0 or 1. Ensure /bin/ed is installed. For
-MKSH_SMALL but with Vi mode, add -DMKSH_S_NOVI=0 to CPPFLAGS as well.
-
-Normally, the following command is what you want to run, then:
-$ (sh Build.sh -r -c lto && ./test.sh -f) 2>&1 | tee log
-
-Copy dot.mkshrc to /etc/skel/.mkshrc; install mksh into $prefix/bin; or
-/bin; install the manpage, if omitting the -r flag a catmanpage is made
-using $NROFF. Consider using a forward script as /etc/skel/.mkshrc like
-https://www.mirbsd.org/cvs.cgi/contrib/hosted/tg/deb/mksh/debian/.mkshrc?rev=HEAD
-and put dot.mkshrc as /etc/mkshrc so users need not keep up their HOME.
-
-EOD

Copied: vendor/MirOS/mksh/R50/Build.sh (from rev 6707, vendor/MirOS/mksh/dist/Build.sh)
===================================================================
--- vendor/MirOS/mksh/R50/Build.sh	                        (rev 0)
+++ vendor/MirOS/mksh/R50/Build.sh	2014-07-01 12:01:17 UTC (rev 6708)
@@ -0,0 +1,2653 @@
+#!/bin/sh
+srcversion='$MirOS: src/bin/mksh/Build.sh,v 1.662 2014/06/29 10:56:08 tg Exp $'
+#-
+# Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
+#		2011, 2012, 2013, 2014
+#	Thorsten Glaser <tg at mirbsd.org>
+#
+# Provided that these terms and disclaimer and all copyright notices
+# are retained or reproduced in an accompanying document, permission
+# is granted to deal in this work without restriction, including un-
+# limited rights to use, publicly perform, distribute, sell, modify,
+# merge, give away, or sublicence.
+#
+# This work is provided "AS IS" and WITHOUT WARRANTY of any kind, to
+# the utmost extent permitted by applicable law, neither express nor
+# implied; without malicious intent or gross negligence. In no event
+# may a licensor, author or contributor be held liable for indirect,
+# direct, other damage, loss, or other issues arising in any way out
+# of dealing in the work, even if advised of the possibility of such
+# damage or existence of a defect, except proven that it results out
+# of said person's immediate fault when using the work as intended.
+#-
+# People analysing the output must whitelist conftest.c for any kind
+# of compiler warning checks (mirtoconf is by design not quiet).
+#
+# Used environment documentation is at the end of this file.
+
+LC_ALL=C
+export LC_ALL
+
+case $ZSH_VERSION:$VERSION in
+:zsh*) ZSH_VERSION=2 ;;
+esac
+
+if test -n "${ZSH_VERSION+x}" && (emulate sh) >/dev/null 2>&1; then
+	emulate sh
+	NULLCMD=:
+fi
+
+if test -d /usr/xpg4/bin/. >/dev/null 2>&1; then
+	# Solaris: some of the tools have weird behaviour, use portable ones
+	PATH=/usr/xpg4/bin:$PATH
+	export PATH
+fi
+
+nl='
+'
+safeIFS='	'
+safeIFS=" $safeIFS$nl"
+IFS=$safeIFS
+allu=QWERTYUIOPASDFGHJKLZXCVBNM
+alll=qwertyuiopasdfghjklzxcvbnm
+alln=0123456789
+alls=______________________________________________________________
+
+genopt_die() {
+	if test -n "$1"; then
+		echo >&2 "E: $*"
+		echo >&2 "E: in '$srcfile': '$line'"
+	else
+		echo >&2 "E: invalid input in '$srcfile': '$line'"
+	fi
+	rm -f "$bn.gen"
+	exit 1
+}
+
+genopt_soptc() {
+	optc=`echo "$line" | sed 's/^[<>]\(.\).*$/\1/'`
+	test x"$optc" = x'|' && return
+	optclo=`echo "$optc" | tr $allu $alll`
+	if test x"$optc" = x"$optclo"; then
+		islo=1
+	else
+		islo=0
+	fi
+	sym=`echo "$line" | sed 's/^[<>]/|/'`
+	o_str=$o_str$nl"<$optclo$islo$sym"
+}
+
+genopt_scond() {
+	case x$cond in
+	x)
+		cond=
+		;;
+	x*' '*)
+		cond=`echo "$cond" | sed 's/^ //'`
+		cond="#if $cond"
+		;;
+	x'!'*)
+		cond=`echo "$cond" | sed 's/^!//'`
+		cond="#ifndef $cond"
+		;;
+	x*)
+		cond="#ifdef $cond"
+		;;
+	esac
+}
+
+do_genopt() {
+	srcfile=$1
+	test -f "$srcfile" || genopt_die Source file \$srcfile not set.
+	bn=`basename "$srcfile" | sed 's/.opt$//'`
+	o_gen=
+	o_str=
+	o_sym=
+	ddefs=
+	state=0
+	exec <"$srcfile"
+	IFS=
+	while IFS= read line; do
+		IFS=$safeIFS
+		case $state:$line in
+		2:'|'*)
+			# end of input
+			o_sym=`echo "$line" | sed 's/^.//'`
+			o_gen=$o_gen$nl"#undef F0"
+			o_gen=$o_gen$nl"#undef FN"
+			o_gen=$o_gen$ddefs
+			state=3
+			;;
+		1:@@)
+			# begin of data block
+			o_gen=$o_gen$nl"#endif"
+			o_gen=$o_gen$nl"#ifndef F0"
+			o_gen=$o_gen$nl"#define F0 FN"
+			o_gen=$o_gen$nl"#endif"
+			state=2
+			;;
+		*:@@*)
+			genopt_die ;;
+		0:@*|1:@*)
+			# begin of a definition block
+			sym=`echo "$line" | sed 's/^@//'`
+			if test $state = 0; then
+				o_gen=$o_gen$nl"#if defined($sym)"
+			else
+				o_gen=$o_gen$nl"#elif defined($sym)"
+			fi
+			ddefs="$ddefs$nl#undef $sym"
+			state=1
+			;;
+		0:*|3:*)
+			genopt_die ;;
+		1:*)
+			# definition line
+			o_gen=$o_gen$nl$line
+			;;
+		2:'<'*'|'*)
+			genopt_soptc
+			;;
+		2:'>'*'|'*)
+			genopt_soptc
+			cond=`echo "$line" | sed 's/^[^|]*|//'`
+			genopt_scond
+			case $optc in
+			'|') optc=0 ;;
+			*) optc=\'$optc\' ;;
+			esac
+			IFS= read line || genopt_die Unexpected EOF
+			IFS=$safeIFS
+			test -n "$cond" && o_gen=$o_gen$nl"$cond"
+			o_gen=$o_gen$nl"$line, $optc)"
+			test -n "$cond" && o_gen=$o_gen$nl"#endif"
+			;;
+		esac
+	done
+	case $state:$o_sym in
+	3:) genopt_die Expected optc sym at EOF ;;
+	3:*) ;;
+	*) genopt_die Missing EOF marker ;;
+	esac
+	echo "$o_str" | sort | while IFS='|' read x opts cond; do
+		IFS=$safeIFS
+		test -n "$x" || continue
+		genopt_scond
+		test -n "$cond" && echo "$cond"
+		echo "\"$opts\""
+		test -n "$cond" && echo "#endif"
+	done | {
+		echo "#ifndef $o_sym$o_gen"
+		echo "#else"
+		cat
+		echo "#undef $o_sym"
+		echo "#endif"
+	} >"$bn.gen"
+	IFS=$safeIFS
+	return 0
+}
+
+if test x"$BUILDSH_RUN_GENOPT" = x"1"; then
+	set x -G "$srcfile"
+	shift
+fi
+if test x"$1" = x"-G"; then
+	do_genopt "$2"
+	exit $?
+fi
+
+echo "For the build logs, demonstrate that /dev/null and /dev/tty exist:"
+ls -l /dev/null /dev/tty
+
+v() {
+	$e "$*"
+	eval "$@"
+}
+
+vv() {
+	_c=$1
+	shift
+	$e "\$ $*" 2>&1
+	eval "$@" >vv.out 2>&1
+	sed "s^${_c} " <vv.out
+}
+
+vq() {
+	eval "$@"
+}
+
+rmf() {
+	for _f in "$@"; do
+		case $_f in
+		Build.sh|check.pl|check.t|dot.mkshrc|*.1|*.c|*.h|*.ico|*.opt) ;;
+		*) rm -f "$_f" ;;
+		esac
+	done
+}
+
+tcfn=no
+bi=
+ui=
+ao=
+fx=
+me=`basename "$0"`
+orig_CFLAGS=$CFLAGS
+phase=x
+oldish_ed=stdout-ed,no-stderr-ed
+
+if test -t 1; then
+	bi=''
+	ui=''
+	ao=''
+fi
+
+upper() {
+	echo :"$@" | sed 's/^://' | tr $alll $allu
+}
+
+# clean up after ac_testrun()
+ac_testdone() {
+	eval HAVE_$fu=$fv
+	fr=no
+	test 0 = $fv || fr=yes
+	$e "$bi==> $fd...$ao $ui$fr$ao$fx"
+	fx=
+}
+
+# ac_cache label: sets f, fu, fv?=0
+ac_cache() {
+	f=$1
+	fu=`upper $f`
+	eval fv=\$HAVE_$fu
+	case $fv in
+	0|1)
+		fx=' (cached)'
+		return 0
+		;;
+	esac
+	fv=0
+	return 1
+}
+
+# ac_testinit label [!] checkif[!]0 [setlabelifcheckis[!]0] useroutput
+# returns 1 if value was cached/implied, 0 otherwise: call ac_testdone
+ac_testinit() {
+	if ac_cache $1; then
+		test x"$2" = x"!" && shift
+		test x"$2" = x"" || shift
+		fd=${3-$f}
+		ac_testdone
+		return 1
+	fi
+	fc=0
+	if test x"$2" = x""; then
+		ft=1
+	else
+		if test x"$2" = x"!"; then
+			fc=1
+			shift
+		fi
+		eval ft=\$HAVE_`upper $2`
+		shift
+	fi
+	fd=${3-$f}
+	if test $fc = "$ft"; then
+		fv=$2
+		fx=' (implied)'
+		ac_testdone
+		return 1
+	fi
+	$e ... $fd
+	return 0
+}
+
+# pipe .c | ac_test[n] [!] label [!] checkif[!]0 [setlabelifcheckis[!]0] useroutput
+ac_testnnd() {
+	if test x"$1" = x"!"; then
+		fr=1
+		shift
+	else
+		fr=0
+	fi
+	ac_testinit "$@" || return 1
+	cat >conftest.c
+	vv ']' "$CC $CFLAGS $CPPFLAGS $LDFLAGS $NOWARN conftest.c $LIBS $ccpr"
+	test $tcfn = no && test -f a.out && tcfn=a.out
+	test $tcfn = no && test -f a.exe && tcfn=a.exe
+	test $tcfn = no && test -f conftest && tcfn=conftest
+	if test -f $tcfn; then
+		test 1 = $fr || fv=1
+	else
+		test 0 = $fr || fv=1
+	fi
+	vscan=
+	if test $phase = u; then
+		test $ct = gcc && vscan='unrecogni[sz]ed'
+		test $ct = hpcc && vscan='unsupported'
+		test $ct = pcc && vscan='unsupported'
+		test $ct = sunpro && vscan='-e ignored -e turned.off'
+	fi
+	test -n "$vscan" && grep $vscan vv.out >/dev/null 2>&1 && fv=$fr
+	return 0
+}
+ac_testn() {
+	ac_testnnd "$@" || return
+	rmf conftest.c conftest.o ${tcfn}* vv.out
+	ac_testdone
+}
+
+# ac_ifcpp cppexpr [!] label [!] checkif[!]0 [setlabelifcheckis[!]0] useroutput
+ac_ifcpp() {
+	expr=$1; shift
+	ac_testn "$@" <<-EOF
+		#include <unistd.h>
+		extern int thiswillneverbedefinedIhope(void);
+		int main(void) { return (isatty(0) +
+		#$expr
+		    0
+		#else
+		/* force a failure: expr is false */
+		    thiswillneverbedefinedIhope()
+		#endif
+		    ); }
+EOF
+	test x"$1" = x"!" && shift
+	f=$1
+	fu=`upper $f`
+	eval fv=\$HAVE_$fu
+	test x"$fv" = x"1"
+}
+
+add_cppflags() {
+	CPPFLAGS="$CPPFLAGS $*"
+}
+
+ac_cppflags() {
+	test x"$1" = x"" || fu=$1
+	fv=$2
+	test x"$2" = x"" && eval fv=\$HAVE_$fu
+	add_cppflags -DHAVE_$fu=$fv
+}
+
+ac_test() {
+	ac_testn "$@"
+	ac_cppflags
+}
+
+# ac_flags [-] add varname cflags [text] [ldflags]
+ac_flags() {
+	if test x"$1" = x"-"; then
+		shift
+		hf=1
+	else
+		hf=0
+	fi
+	fa=$1
+	vn=$2
+	f=$3
+	ft=$4
+	fl=$5
+	test x"$ft" = x"" && ft="if $f can be used"
+	save_CFLAGS=$CFLAGS
+	CFLAGS="$CFLAGS $f"
+	if test -n "$fl"; then
+		save_LDFLAGS=$LDFLAGS
+		LDFLAGS="$LDFLAGS $fl"
+	fi
+	if test 1 = $hf; then
+		ac_testn can_$vn '' "$ft"
+	else
+		ac_testn can_$vn '' "$ft" <<-'EOF'
+			/* evil apo'stroph in comment test */
+			#include <unistd.h>
+			int main(void) { return (isatty(0)); }
+		EOF
+	fi
+	eval fv=\$HAVE_CAN_`upper $vn`
+	if test -n "$fl"; then
+		test 11 = $fa$fv || LDFLAGS=$save_LDFLAGS
+	fi
+	test 11 = $fa$fv || CFLAGS=$save_CFLAGS
+}
+
+# ac_header [!] header [prereq ...]
+ac_header() {
+	if test x"$1" = x"!"; then
+		na=1
+		shift
+	else
+		na=0
+	fi
+	hf=$1; shift
+	hv=`echo "$hf" | tr -d '\012\015' | tr -c $alll$allu$alln $alls`
+	echo "/* NeXTstep bug workaround */" >x
+	for i
+	do
+		case $i in
+		_time)
+			echo '#if HAVE_BOTH_TIME_H' >>x
+			echo '#include <sys/time.h>' >>x
+			echo '#include <time.h>' >>x
+			echo '#elif HAVE_SYS_TIME_H' >>x
+			echo '#include <sys/time.h>' >>x
+			echo '#elif HAVE_TIME_H' >>x
+			echo '#include <time.h>' >>x
+			echo '#endif' >>x
+			;;
+		*)
+			echo "#include <$i>" >>x
+			;;
+		esac
+	done
+	echo "#include <$hf>" >>x
+	echo '#include <unistd.h>' >>x
+	echo 'int main(void) { return (isatty(0)); }' >>x
+	ac_testn "$hv" "" "<$hf>" <x
+	rmf x
+	test 1 = $na || ac_cppflags
+}
+
+addsrcs() {
+	if test x"$1" = x"!"; then
+		fr=0
+		shift
+	else
+		fr=1
+	fi
+	eval i=\$$1
+	test $fr = "$i" && case " $SRCS " in
+	*\ $2\ *)	;;
+	*)		SRCS="$SRCS $2" ;;
+	esac
+}
+
+
+curdir=`pwd` srcdir=`dirname "$0" 2>/dev/null`
+case x$srcdir in
+x)
+	srcdir=.
+	;;
+*\ *|*"	"*|*"$nl"*)
+	echo >&2 Source directory should not contain space or tab or newline.
+	echo >&2 Errors may occur.
+	;;
+*"'"*)
+	echo Source directory must not contain single quotes.
+	exit 1
+	;;
+esac
+dstversion=`sed -n '/define MKSH_VERSION/s/^.*"\([^"]*\)".*$/\1/p' "$srcdir/sh.h"`
+add_cppflags -DMKSH_BUILDSH
+
+e=echo
+r=0
+eq=0
+pm=0
+cm=normal
+optflags=-std-compile-opts
+check_categories=
+last=
+tfn=
+legacy=0
+
+for i
+do
+	case $last:$i in
+	c:combine|c:dragonegg|c:llvm|c:lto)
+		cm=$i
+		last=
+		;;
+	c:*)
+		echo "$me: Unknown option -c '$i'!" >&2
+		exit 1
+		;;
+	o:*)
+		optflags=$i
+		last=
+		;;
+	t:*)
+		tfn=$i
+		last=
+		;;
+	:-c)
+		last=c
+		;;
+	:-G)
+		echo "$me: Do not call me with '-G'!" >&2
+		exit 1
+		;;
+	:-g)
+		# checker, debug, valgrind build
+		add_cppflags -DDEBUG
+		CFLAGS="$CFLAGS -g3 -fno-builtin"
+		;;
+	:-j)
+		pm=1
+		;;
+	:-L)
+		legacy=1
+		;;
+	:+L)
+		legacy=0
+		;;
+	:-M)
+		cm=makefile
+		;;
+	:-O)
+		optflags=-std-compile-opts
+		;;
+	:-o)
+		last=o
+		;;
+	:-Q)
+		eq=1
+		;;
+	:-r)
+		r=1
+		;;
+	:-t)
+		last=t
+		;;
+	:-v)
+		echo "Build.sh $srcversion"
+		echo "for mksh $dstversion"
+		exit 0
+		;;
+	:*)
+		echo "$me: Unknown option '$i'!" >&2
+		exit 1
+		;;
+	*)
+		echo "$me: Unknown option -'$last' '$i'!" >&2
+		exit 1
+		;;
+	esac
+done
+if test -n "$last"; then
+	echo "$me: Option -'$last' not followed by argument!" >&2
+	exit 1
+fi
+
+test -z "$tfn" && if test $legacy = 0; then
+	tfn=mksh
+else
+	tfn=lksh
+fi
+if test -d $tfn || test -d $tfn.exe; then
+	echo "$me: Error: ./$tfn is a directory!" >&2
+	exit 1
+fi
+rmf a.exe* a.out* conftest.c *core core.* lft ${tfn}* no *.bc *.ll *.o *.gen \
+    Rebuild.sh signames.inc test.sh x vv.out
+
+SRCS="lalloc.c eval.c exec.c expr.c funcs.c histrap.c jobs.c"
+SRCS="$SRCS lex.c main.c misc.c shf.c syn.c tree.c var.c"
+
+if test $legacy = 0; then
+	SRCS="$SRCS edit.c"
+	check_categories="$check_categories shell:legacy-no int:32"
+else
+	check_categories="$check_categories shell:legacy-yes"
+	add_cppflags -DMKSH_LEGACY_MODE
+	HAVE_PERSISTENT_HISTORY=0
+	HAVE_ISSET_MKSH_CONSERVATIVE_FDS=1	# from sh.h
+fi
+
+if test x"$srcdir" = x"."; then
+	CPPFLAGS="-I. $CPPFLAGS"
+else
+	CPPFLAGS="-I. -I'$srcdir' $CPPFLAGS"
+fi
+test -n "$LDSTATIC" && if test -n "$LDFLAGS"; then
+	LDFLAGS="$LDFLAGS $LDSTATIC"
+else
+	LDFLAGS=$LDSTATIC
+fi
+
+if test -z "$TARGET_OS"; then
+	x=`uname -s 2>/dev/null || uname`
+	test x"$x" = x"`uname -n 2>/dev/null`" || TARGET_OS=$x
+fi
+if test -z "$TARGET_OS"; then
+	echo "$me: Set TARGET_OS, your uname is broken!" >&2
+	exit 1
+fi
+oswarn=
+ccpc=-Wc,
+ccpl=-Wl,
+tsts=
+ccpr='|| for _f in ${tcfn}*; do case $_f in Build.sh|check.pl|check.t|dot.mkshrc|*.1|*.c|*.h|*.ico|*.opt) ;; *) rm -f "$_f" ;; esac; done'
+
+# Evil hack
+if test x"$TARGET_OS" = x"Android"; then
+	check_categories="$check_categories android"
+	TARGET_OS=Linux
+fi
+
+# Evil OS
+if test x"$TARGET_OS" = x"Minix"; then
+	echo >&2 "
+WARNING: additional checks before running Build.sh required!
+You can avoid these by calling Build.sh correctly, see below.
+"
+	cat >conftest.c <<'EOF'
+#include <sys/types.h>
+const char *
+#ifdef _NETBSD_SOURCE
+ct="Ninix3"
+#else
+ct="Minix3"
+#endif
+;
+EOF
+	ct=unknown
+	vv ']' "${CC-cc} -E $CFLAGS $CPPFLAGS $NOWARN conftest.c | grep ct= | tr -d \\\\015 >x"
+	sed 's/^/[ /' x
+	eval `cat x`
+	rmf x vv.out
+	case $ct in
+	Minix3|Ninix3)
+		echo >&2 "
+Warning: you set TARGET_OS to $TARGET_OS but that is ambiguous.
+Please set it to either Minix3 or Ninix3, whereas the latter is
+all versions of Minix with even partial NetBSD(R) userland. The
+value determined from your compiler for the current compilation
+(which may be wrong) is: $ct
+"
+		TARGET_OS=$ct
+		;;
+	*)
+		echo >&2 "
+Warning: you set TARGET_OS to $TARGET_OS but that is ambiguous.
+Please set it to either Minix3 or Ninix3, whereas the latter is
+all versions of Minix with even partial NetBSD(R) userland. The
+proper value couldn't be determined, continue at your own risk.
+"
+		;;
+	esac
+fi
+
+# Configuration depending on OS revision, on OSes that need them
+case $TARGET_OS in
+NEXTSTEP)
+	test x"$TARGET_OSREV" = x"" && TARGET_OSREV=`hostinfo 2>&1 | \
+	    grep 'NeXT Mach [0-9][0-9.]*:' | \
+	    sed 's/^.*NeXT Mach \([0-9][0-9.]*\):.*$/\1/'`
+	;;
+QNX|SCO_SV)
+	test x"$TARGET_OSREV" = x"" && TARGET_OSREV=`uname -r`
+	;;
+esac
+
+# Configuration depending on OS name
+case $TARGET_OS in
+386BSD)
+	: ${HAVE_CAN_OTWO=0}
+	add_cppflags -DMKSH_NO_SIGSETJMP
+	add_cppflags -DMKSH_TYPEDEF_SIG_ATOMIC_T=int
+	add_cppflags -DMKSH_CONSERVATIVE_FDS
+	;;
+AIX)
+	add_cppflags -D_ALL_SOURCE
+	: ${HAVE_SETLOCALE_CTYPE=0}
+	;;
+BeOS)
+	case $KSH_VERSION in
+	*MIRBSD\ KSH*)
+		oswarn="; it has minor issues"
+		;;
+	*)
+		oswarn="; you must recompile mksh with"
+		oswarn="$oswarn${nl}itself in a second stage"
+		;;
+	esac
+	# BeOS has no real tty either
+	add_cppflags -DMKSH_UNEMPLOYED
+	add_cppflags -DMKSH_DISABLE_TTY_WARNING
+	# BeOS doesn't have different UIDs and GIDs
+	add_cppflags -DMKSH__NO_SETEUGID
+	;;
+BSD/OS)
+	: ${HAVE_SETLOCALE_CTYPE=0}
+	;;
+Coherent)
+	oswarn="; it has major issues"
+	add_cppflags -DMKSH__NO_SYMLINK
+	check_categories="$check_categories nosymlink"
+	add_cppflags -DMKSH__NO_SETEUGID
+	add_cppflags -DMKSH_CONSERVATIVE_FDS
+	add_cppflags -DMKSH_DISABLE_TTY_WARNING
+	;;
+CYGWIN*)
+	: ${HAVE_SETLOCALE_CTYPE=0}
+	;;
+Darwin)
+	add_cppflags -D_DARWIN_C_SOURCE
+	;;
+DragonFly)
+	;;
+FreeBSD)
+	;;
+FreeMiNT)
+	oswarn="; it has minor issues"
+	add_cppflags -D_GNU_SOURCE
+	add_cppflags -DMKSH_CONSERVATIVE_FDS
+	: ${HAVE_SETLOCALE_CTYPE=0}
+	;;
+GNU)
+	case $CC in
+	*tendracc*) ;;
+	*) add_cppflags -D_GNU_SOURCE ;;
+	esac
+	# define MKSH__NO_PATH_MAX to use Hurd-only functions
+	add_cppflags -DMKSH__NO_PATH_MAX
+	;;
+GNU/kFreeBSD)
+	case $CC in
+	*tendracc*) ;;
+	*) add_cppflags -D_GNU_SOURCE ;;
+	esac
+	;;
+Haiku)
+	add_cppflags -DMKSH_ASSUME_UTF8; HAVE_ISSET_MKSH_ASSUME_UTF8=1
+	;;
+HP-UX)
+	;;
+Interix)
+	ccpc='-X '
+	ccpl='-Y '
+	add_cppflags -D_ALL_SOURCE
+	: ${LIBS='-lcrypt'}
+	: ${HAVE_SETLOCALE_CTYPE=0}
+	;;
+IRIX*)
+	: ${HAVE_SETLOCALE_CTYPE=0}
+	;;
+Linux)
+	case $CC in
+	*tendracc*) ;;
+	*) add_cppflags -D_GNU_SOURCE ;;
+	esac
+	add_cppflags -DSETUID_CAN_FAIL_WITH_EAGAIN
+	: ${HAVE_REVOKE=0}
+	;;
+LynxOS)
+	oswarn="; it has minor issues"
+	;;
+MidnightBSD)
+	;;
+Minix-vmd)
+	add_cppflags -DMKSH__NO_SETEUGID
+	add_cppflags -DMKSH_UNEMPLOYED
+	add_cppflags -DMKSH_CONSERVATIVE_FDS
+	add_cppflags -D_MINIX_SOURCE
+	oldish_ed=no-stderr-ed		# no /bin/ed, maybe see below
+	: ${HAVE_SETLOCALE_CTYPE=0}
+	;;
+Minix3)
+	add_cppflags -DMKSH_UNEMPLOYED
+	add_cppflags -DMKSH_CONSERVATIVE_FDS
+	add_cppflags -DMKSH_NO_LIMITS
+	add_cppflags -D_POSIX_SOURCE -D_POSIX_1_SOURCE=2 -D_MINIX
+	oldish_ed=no-stderr-ed		# /usr/bin/ed(!) is broken
+	: ${HAVE_SETLOCALE_CTYPE=0}
+	;;
+MirBSD)
+	;;
+MSYS_*)
+	add_cppflags -DMKSH_ASSUME_UTF8=0; HAVE_ISSET_MKSH_ASSUME_UTF8=1
+	# almost same as CYGWIN* (from RT|Chatzilla)
+	: ${HAVE_SETLOCALE_CTYPE=0}
+	# broken on this OE (from ir0nh34d)
+	: ${HAVE_STDINT_H=0}
+	;;
+NetBSD)
+	;;
+NEXTSTEP)
+	add_cppflags -D_NEXT_SOURCE
+	add_cppflags -D_POSIX_SOURCE
+	: ${AWK=gawk} ${CC=cc -posix}
+	add_cppflags -DMKSH_NO_SIGSETJMP
+	# NeXTstep cannot get a controlling tty
+	add_cppflags -DMKSH_UNEMPLOYED
+	case $TARGET_OSREV in
+	4.2*)
+		# OpenStep 4.2 is broken by default
+		oswarn="; it needs libposix.a"
+		;;
+	esac
+	add_cppflags -DMKSH_CONSERVATIVE_FDS
+	;;
+Ninix3)
+	# similar to Minix3
+	add_cppflags -DMKSH_UNEMPLOYED
+	add_cppflags -DMKSH_CONSERVATIVE_FDS
+	add_cppflags -DMKSH_NO_LIMITS
+	# but no idea what else could be needed
+	oswarn="; it has unknown issues"
+	;;
+OpenBSD)
+	: ${HAVE_SETLOCALE_CTYPE=0}
+	;;
+OSF1)
+	HAVE_SIG_T=0	# incompatible
+	add_cppflags -D_OSF_SOURCE
+	add_cppflags -D_POSIX_C_SOURCE=200112L
+	add_cppflags -D_XOPEN_SOURCE=600
+	add_cppflags -D_XOPEN_SOURCE_EXTENDED
+	: ${HAVE_SETLOCALE_CTYPE=0}
+	;;
+Plan9)
+	add_cppflags -D_POSIX_SOURCE
+	add_cppflags -D_LIMITS_EXTENSION
+	add_cppflags -D_BSD_EXTENSION
+	add_cppflags -D_SUSV2_SOURCE
+	add_cppflags -DMKSH_ASSUME_UTF8; HAVE_ISSET_MKSH_ASSUME_UTF8=1
+	add_cppflags -DMKSH_NO_CMDLINE_EDITING
+	add_cppflags -DMKSH__NO_SETEUGID
+	oswarn=' and will currently not work'
+	add_cppflags -DMKSH_UNEMPLOYED
+	# this is for detecting kencc
+	add_cppflags -DMKSH_MAYBE_KENCC
+	;;
+PW32*)
+	HAVE_SIG_T=0	# incompatible
+	oswarn=' and will currently not work'
+	: ${HAVE_SETLOCALE_CTYPE=0}
+	;;
+QNX)
+	add_cppflags -D__NO_EXT_QNX
+	add_cppflags -D__EXT_UNIX_MISC
+	case $TARGET_OSREV in
+	[012345].*|6.[0123].*|6.4.[01])
+		oldish_ed=no-stderr-ed		# oldish /bin/ed is broken
+		;;
+	esac
+	: ${HAVE_SETLOCALE_CTYPE=0}
+	;;
+SCO_SV)
+	case $TARGET_OSREV in
+	3.2*)
+		# SCO OpenServer 5
+		add_cppflags -DMKSH_UNEMPLOYED
+		;;
+	5*)
+		# SCO OpenServer 6
+		;;
+	*)
+		oswarn='; this is an unknown version of'
+		oswarn="$oswarn$nl$TARGET_OS ${TARGET_OSREV}, please tell me what to do"
+		;;
+	esac
+	add_cppflags -DMKSH_CONSERVATIVE_FDS
+	: ${HAVE_SYS_SIGLIST=0} ${HAVE__SYS_SIGLIST=0}
+	;;
+skyos)
+	oswarn="; it has minor issues"
+	;;
+SunOS)
+	add_cppflags -D_BSD_SOURCE
+	add_cppflags -D__EXTENSIONS__
+	;;
+syllable)
+	add_cppflags -D_GNU_SOURCE
+	add_cppflags -DMKSH_NO_SIGSUSPEND
+	oswarn=' and will currently not work'
+	;;
+ULTRIX)
+	: ${CC=cc -YPOSIX}
+	add_cppflags -DMKSH_TYPEDEF_SSIZE_T=int
+	add_cppflags -DMKSH_CONSERVATIVE_FDS
+	: ${HAVE_SETLOCALE_CTYPE=0}
+	;;
+UnixWare|UNIX_SV)
+	# SCO UnixWare
+	add_cppflags -DMKSH_CONSERVATIVE_FDS
+	: ${HAVE_SYS_SIGLIST=0} ${HAVE__SYS_SIGLIST=0}
+	;;
+UWIN*)
+	ccpc='-Yc,'
+	ccpl='-Yl,'
+	tsts=" 3<>/dev/tty"
+	oswarn="; it will compile, but the target"
+	oswarn="$oswarn${nl}platform itself is very flakey/unreliable"
+	: ${HAVE_SETLOCALE_CTYPE=0}
+	;;
+_svr4)
+	# generic target for SVR4 Unix with uname -s = uname -n
+	# this duplicates the * target below
+	oswarn='; it may or may not work'
+	test x"$TARGET_OSREV" = x"" && TARGET_OSREV=`uname -r`
+	;;
+*)
+	oswarn='; it may or may not work'
+	test x"$TARGET_OSREV" = x"" && TARGET_OSREV=`uname -r`
+	;;
+esac
+
+: ${HAVE_MKNOD=0}
+
+: ${AWK=awk} ${CC=cc} ${NROFF=nroff} ${SIZE=size}
+test 0 = $r && echo | $NROFF -v 2>&1 | grep GNU >/dev/null 2>&1 && \
+    NROFF="$NROFF -c"
+
+# this aids me in tracing FTBFSen without access to the buildd
+$e "Hi from$ao $bi$srcversion$ao on:"
+case $TARGET_OS in
+AIX)
+	vv '|' "oslevel >&2"
+	vv '|' "uname -a >&2"
+	;;
+Darwin)
+	vv '|' "hwprefs machine_type os_type os_class >&2"
+	vv '|' "sw_vers >&2"
+	vv '|' "system_profiler SPSoftwareDataType SPHardwareDataType >&2"
+	vv '|' "/bin/sh --version >&2"
+	vv '|' "xcodebuild -version >&2"
+	vv '|' "uname -a >&2"
+	vv '|' "sysctl kern.version hw.machine hw.model hw.memsize hw.availcpu hw.cpufrequency hw.byteorder hw.cpu64bit_capable >&2"
+	;;
+IRIX*)
+	vv '|' "uname -a >&2"
+	vv '|' "hinv -v >&2"
+	;;
+OSF1)
+	vv '|' "uname -a >&2"
+	vv '|' "/usr/sbin/sizer -v >&2"
+	;;
+SCO_SV|UnixWare|UNIX_SV)
+	vv '|' "uname -a >&2"
+	vv '|' "uname -X >&2"
+	;;
+*)
+	vv '|' "uname -a >&2"
+	;;
+esac
+test -z "$oswarn" || echo >&2 "
+Warning: mksh has not yet been ported to or tested on your
+operating system '$TARGET_OS'$oswarn. If you can provide
+a shell account to the developer, this may improve; please
+drop us a success or failure notice or even send in diffs.
+"
+$e "$bi$me: Building the MirBSD Korn Shell$ao $ui$dstversion$ao on $TARGET_OS ${TARGET_OSREV}..."
+
+#
+# Begin of mirtoconf checks
+#
+$e $bi$me: Scanning for functions... please ignore any errors.$ao
+
+#
+# Compiler: which one?
+#
+# notes:
+# - ICC defines __GNUC__ too
+# - GCC defines __hpux too
+# - LLVM+clang defines __GNUC__ too
+# - nwcc defines __GNUC__ too
+CPP="$CC -E"
+$e ... which compiler seems to be used
+cat >conftest.c <<'EOF'
+const char *
+#if defined(__ICC) || defined(__INTEL_COMPILER)
+ct="icc"
+#elif defined(__xlC__) || defined(__IBMC__)
+ct="xlc"
+#elif defined(__SUNPRO_C)
+ct="sunpro"
+#elif defined(__ACK__)
+ct="ack"
+#elif defined(__BORLANDC__)
+ct="bcc"
+#elif defined(__WATCOMC__)
+ct="watcom"
+#elif defined(__MWERKS__)
+ct="metrowerks"
+#elif defined(__HP_cc)
+ct="hpcc"
+#elif defined(__DECC) || (defined(__osf__) && !defined(__GNUC__))
+ct="dec"
+#elif defined(__PGI)
+ct="pgi"
+#elif defined(__DMC__)
+ct="dmc"
+#elif defined(_MSC_VER)
+ct="msc"
+#elif defined(__ADSPBLACKFIN__) || defined(__ADSPTS__) || defined(__ADSP21000__)
+ct="adsp"
+#elif defined(__IAR_SYSTEMS_ICC__)
+ct="iar"
+#elif defined(SDCC)
+ct="sdcc"
+#elif defined(__PCC__)
+ct="pcc"
+#elif defined(__TenDRA__)
+ct="tendra"
+#elif defined(__TINYC__)
+ct="tcc"
+#elif defined(__llvm__) && defined(__clang__)
+ct="clang"
+#elif defined(__NWCC__)
+ct="nwcc"
+#elif defined(__GNUC__)
+ct="gcc"
+#elif defined(_COMPILER_VERSION)
+ct="mipspro"
+#elif defined(__sgi)
+ct="mipspro"
+#elif defined(__hpux) || defined(__hpua)
+ct="hpcc"
+#elif defined(__ultrix)
+ct="ucode"
+#elif defined(__USLC__)
+ct="uslc"
+#elif defined(__LCC__)
+ct="lcc"
+#elif defined(MKSH_MAYBE_KENCC)
+/* and none of the above matches */
+ct="kencc"
+#else
+ct="unknown"
+#endif
+;
+const char *
+#if defined(__KLIBC__)
+et="klibc"
+#else
+et="unknown"
+#endif
+;
+EOF
+ct=untested
+et=untested
+vv ']' "$CPP $CFLAGS $CPPFLAGS $NOWARN conftest.c | \
+    sed -n '/^ *[ce]t *= */s/^ *\([ce]t\) *= */\1=/p' | tr -d \\\\015 >x"
+sed 's/^/[ /' x
+eval `cat x`
+rmf x vv.out
+cat >conftest.c <<'EOF'
+#include <unistd.h>
+int main(void) { return (isatty(0)); }
+EOF
+case $ct in
+ack)
+	# work around "the famous ACK const bug"
+	CPPFLAGS="-Dconst= $CPPFLAGS"
+	;;
+adsp)
+	echo >&2 'Warning: Analog Devices C++ compiler for Blackfin, TigerSHARC
+    and SHARC (21000) DSPs detected. This compiler has not yet
+    been tested for compatibility with mksh. Continue at your
+    own risk, please report success/failure to the developers.'
+	;;
+bcc)
+	echo >&2 "Warning: Borland C++ Builder detected. This compiler might
+    produce broken executables. Continue at your own risk,
+    please report success/failure to the developers."
+	;;
+clang)
+	# does not work with current "ccc" compiler driver
+	vv '|' "$CC $CFLAGS $CPPFLAGS $LDFLAGS $NOWARN $LIBS -version"
+	# one of these two works, for now
+	vv '|' "${CLANG-clang} -version"
+	vv '|' "${CLANG-clang} --version"
+	# ensure compiler and linker are in sync unless overridden
+	case $CCC_CC:$CCC_LD in
+	:*)	;;
+	*:)	CCC_LD=$CCC_CC; export CCC_LD ;;
+	esac
+	;;
+dec)
+	vv '|' "$CC $CFLAGS $CPPFLAGS $LDFLAGS $NOWARN $LIBS -V"
+	vv '|' "$CC $CFLAGS $CPPFLAGS $LDFLAGS $NOWARN -Wl,-V conftest.c $LIBS"
+	;;
+dmc)
+	echo >&2 "Warning: Digital Mars Compiler detected. When running under"
+	echo >&2 "    UWIN, mksh tends to be unstable due to the limitations"
+	echo >&2 "    of this platform. Continue at your own risk,"
+	echo >&2 "    please report success/failure to the developers."
+	;;
+gcc)
+	vv '|' "$CC $CFLAGS $CPPFLAGS $LDFLAGS $NOWARN -v conftest.c $LIBS"
+	vv '|' 'echo `$CC $CFLAGS $CPPFLAGS $LDFLAGS $NOWARN $LIBS \
+	    -dumpmachine` gcc`$CC $CFLAGS $CPPFLAGS $LDFLAGS $NOWARN \
+	    $LIBS -dumpversion`'
+	;;
+hpcc)
+	vv '|' "$CC $CFLAGS $CPPFLAGS $LDFLAGS $NOWARN -V conftest.c $LIBS"
+	;;
+iar)
+	echo >&2 'Warning: IAR Systems (http://www.iar.com) compiler for embedded
+    systems detected. This unsupported compiler has not yet
+    been tested for compatibility with mksh. Continue at your
+    own risk, please report success/failure to the developers.'
+	;;
+icc)
+	vv '|' "$CC $CFLAGS $CPPFLAGS $LDFLAGS $NOWARN $LIBS -V"
+	;;
+kencc)
+	vv '|' "$CC $CFLAGS $CPPFLAGS $LDFLAGS $NOWARN -v conftest.c $LIBS"
+	;;
+lcc)
+	vv '|' "$CC $CFLAGS $CPPFLAGS $LDFLAGS $NOWARN -v conftest.c $LIBS"
+	add_cppflags -D__inline__=__inline
+	;;
+metrowerks)
+	echo >&2 'Warning: Metrowerks C compiler detected. This has not yet
+    been tested for compatibility with mksh. Continue at your
+    own risk, please report success/failure to the developers.'
+	;;
+mipspro)
+	vv '|' "$CC $CFLAGS $CPPFLAGS $LDFLAGS $NOWARN $LIBS -version"
+	;;
+msc)
+	ccpr=		# errorlevels are not reliable
+	case $TARGET_OS in
+	Interix)
+		if [[ -n $C89_COMPILER ]]; then
+			C89_COMPILER=`ntpath2posix -c "$C89_COMPILER"`
+		else
+			C89_COMPILER=CL.EXE
+		fi
+		if [[ -n $C89_LINKER ]]; then
+			C89_LINKER=`ntpath2posix -c "$C89_LINKER"`
+		else
+			C89_LINKER=LINK.EXE
+		fi
+		vv '|' "$C89_COMPILER /HELP >&2"
+		vv '|' "$C89_LINKER /LINK >&2"
+		;;
+	esac
+	;;
+nwcc)
+	vv '|' "$CC $CFLAGS $CPPFLAGS $LDFLAGS $NOWARN $LIBS -version"
+	;;
+pcc)
+	vv '|' "$CC $CFLAGS $CPPFLAGS $LDFLAGS $NOWARN $LIBS -v"
+	;;
+pgi)
+	echo >&2 'Warning: PGI detected. This unknown compiler has not yet
+    been tested for compatibility with mksh. Continue at your
+    own risk, please report success/failure to the developers.'
+	;;
+sdcc)
+	echo >&2 'Warning: sdcc (http://sdcc.sourceforge.net), the small devices
+    C compiler for embedded systems detected. This has not yet
+    been tested for compatibility with mksh. Continue at your
+    own risk, please report success/failure to the developers.'
+	;;
+sunpro)
+	vv '|' "$CC $CFLAGS $CPPFLAGS $LDFLAGS $NOWARN -V conftest.c $LIBS"
+	;;
+tcc)
+	vv '|' "$CC $CFLAGS $CPPFLAGS $LDFLAGS $NOWARN $LIBS -v"
+	;;
+tendra)
+	vv '|' "$CC $CFLAGS $CPPFLAGS $LDFLAGS $NOWARN $LIBS -V 2>&1 | \
+	    fgrep -i -e version -e release"
+	;;
+ucode)
+	vv '|' "$CC $CFLAGS $CPPFLAGS $LDFLAGS $NOWARN $LIBS -V"
+	vv '|' "$CC $CFLAGS $CPPFLAGS $LDFLAGS $NOWARN -Wl,-V conftest.c $LIBS"
+	;;
+uslc)
+	case $TARGET_OS:$TARGET_OSREV in
+	SCO_SV:3.2*)
+		# SCO OpenServer 5
+		CFLAGS="$CFLAGS -g"
+		: ${HAVE_CAN_OTWO=0} ${HAVE_CAN_OPTIMISE=0}
+		;;
+	esac
+	vv '|' "$CC $CFLAGS $CPPFLAGS $LDFLAGS $NOWARN -V conftest.c $LIBS"
+	;;
+watcom)
+	vv '|' "$CC $CFLAGS $CPPFLAGS $LDFLAGS $NOWARN -v conftest.c $LIBS"
+	;;
+xlc)
+	vv '|' "$CC $CFLAGS $CPPFLAGS $LDFLAGS $NOWARN $LIBS -qversion"
+	vv '|' "$CC $CFLAGS $CPPFLAGS $LDFLAGS $NOWARN $LIBS -qversion=verbose"
+	vv '|' "ld -V"
+	;;
+*)
+	test x"$ct" = x"untested" && $e "!!! detecting preprocessor failed"
+	ct=unknown
+	vv "$CC --version"
+	vv '|' "$CC $CFLAGS $CPPFLAGS $LDFLAGS $NOWARN -v conftest.c $LIBS"
+	vv '|' "$CC $CFLAGS $CPPFLAGS $LDFLAGS $NOWARN -V conftest.c $LIBS"
+	;;
+esac
+case $cm in
+dragonegg|llvm)
+	vv '|' "llc -version"
+	;;
+esac
+case $et in
+klibc)
+	add_cppflags -DMKSH_NO_LIMITS
+	;;
+unknown)
+	# nothing special detected, don’t worry
+	unset et
+	;;
+*)
+	# huh?
+	;;
+esac
+$e "$bi==> which compiler seems to be used...$ao $ui$ct${et+ on $et}$ao"
+rmf conftest.c conftest.o conftest a.out* a.exe* vv.out
+
+#
+# Compiler: works as-is, with -Wno-error and -Werror
+#
+save_NOWARN=$NOWARN
+NOWARN=
+DOWARN=
+ac_flags 0 compiler_works '' 'if the compiler works'
+test 1 = $HAVE_CAN_COMPILER_WORKS || exit 1
+HAVE_COMPILER_KNOWN=0
+test $ct = unknown || HAVE_COMPILER_KNOWN=1
+if ac_ifcpp 'if 0' compiler_fails '' \
+    'if the compiler does not fail correctly'; then
+	save_CFLAGS=$CFLAGS
+	: ${HAVE_CAN_DELEXE=x}
+	case $ct in
+	dec)
+		CFLAGS="$CFLAGS ${ccpl}-non_shared"
+		ac_testn can_delexe compiler_fails 0 'for the -non_shared linker option' <<-EOF
+			#include <unistd.h>
+			int main(void) { return (isatty(0)); }
+		EOF
+		;;
+	dmc)
+		CFLAGS="$CFLAGS ${ccpl}/DELEXECUTABLE"
+		ac_testn can_delexe compiler_fails 0 'for the /DELEXECUTABLE linker option' <<-EOF
+			#include <unistd.h>
+			int main(void) { return (isatty(0)); }
+		EOF
+		;;
+	*)
+		exit 1
+		;;
+	esac
+	test 1 = $HAVE_CAN_DELEXE || CFLAGS=$save_CFLAGS
+	ac_testn compiler_still_fails '' 'if the compiler still does not fail correctly' <<-EOF
+	EOF
+	test 1 = $HAVE_COMPILER_STILL_FAILS && exit 1
+fi
+if ac_ifcpp 'ifdef __TINYC__' couldbe_tcc '!' compiler_known 0 \
+    'if this could be tcc'; then
+	ct=tcc
+	CPP='cpp -D__TINYC__'
+	HAVE_COMPILER_KNOWN=1
+fi
+
+case $ct in
+bcc)
+	save_NOWARN="${ccpc}-w"
+	DOWARN="${ccpc}-w!"
+	;;
+dec)
+	# -msg_* flags not used yet, or is -w2 correct?
+	;;
+dmc)
+	save_NOWARN="${ccpc}-w"
+	DOWARN="${ccpc}-wx"
+	;;
+hpcc)
+	save_NOWARN=
+	DOWARN=+We
+	;;
+kencc)
+	save_NOWARN=
+	DOWARN=
+	;;
+mipspro)
+	save_NOWARN=
+	DOWARN="-diag_error 1-10000"
+	;;
+msc)
+	save_NOWARN="${ccpc}/w"
+	DOWARN="${ccpc}/WX"
+	;;
+sunpro)
+	test x"$save_NOWARN" = x"" && save_NOWARN='-errwarn=%none'
+	ac_flags 0 errwarnnone "$save_NOWARN"
+	test 1 = $HAVE_CAN_ERRWARNNONE || save_NOWARN=
+	ac_flags 0 errwarnall "-errwarn=%all"
+	test 1 = $HAVE_CAN_ERRWARNALL && DOWARN="-errwarn=%all"
+	;;
+tendra)
+	save_NOWARN=-w
+	;;
+ucode)
+	save_NOWARN=
+	DOWARN=-w2
+	;;
+watcom)
+	save_NOWARN=
+	DOWARN=-Wc,-we
+	;;
+xlc)
+	save_NOWARN=-qflag=i:e
+	DOWARN=-qflag=i:i
+	;;
+*)
+	test x"$save_NOWARN" = x"" && save_NOWARN=-Wno-error
+	ac_flags 0 wnoerror "$save_NOWARN"
+	test 1 = $HAVE_CAN_WNOERROR || save_NOWARN=
+	ac_flags 0 werror -Werror
+	test 1 = $HAVE_CAN_WERROR && DOWARN=-Werror
+	test $ct = icc && DOWARN="$DOWARN -wd1419"
+	;;
+esac
+NOWARN=$save_NOWARN
+
+#
+# Compiler: extra flags (-O2 -f* -W* etc.)
+#
+i=`echo :"$orig_CFLAGS" | sed 's/^://' | tr -c -d $alll$allu$alln`
+# optimisation: only if orig_CFLAGS is empty
+test x"$i" = x"" && case $ct in
+hpcc)
+	phase=u
+	ac_flags 1 otwo +O2
+	phase=x
+	;;
+kencc|tcc|tendra)
+	# no special optimisation
+	;;
+sunpro)
+	cat >x <<-'EOF'
+		#include <unistd.h>
+		int main(void) { return (isatty(0)); }
+		#define __IDSTRING_CONCAT(l,p)	__LINTED__ ## l ## _ ## p
+		#define __IDSTRING_EXPAND(l,p)	__IDSTRING_CONCAT(l,p)
+		#define pad			void __IDSTRING_EXPAND(__LINE__,x)(void) { }
+	EOF
+	yes pad | head -n 256 >>x
+	ac_flags - 1 otwo -xO2 <x
+	rmf x
+	;;
+xlc)
+	ac_flags 1 othree "-O3 -qstrict"
+	test 1 = $HAVE_CAN_OTHREE || ac_flags 1 otwo -O2
+	;;
+*)
+	ac_flags 1 otwo -O2
+	test 1 = $HAVE_CAN_OTWO || ac_flags 1 optimise -O
+	;;
+esac
+# other flags: just add them if they are supported
+i=0
+case $ct in
+bcc)
+	ac_flags 1 strpool "${ccpc}-d" 'if string pooling can be enabled'
+	;;
+clang)
+	i=1
+	;;
+dec)
+	ac_flags 0 verb -verbose
+	ac_flags 1 rodata -readonly_strings
+	;;
+dmc)
+	ac_flags 1 decl "${ccpc}-r" 'for strict prototype checks'
+	ac_flags 1 schk "${ccpc}-s" 'for stack overflow checking'
+	;;
+gcc)
+	# The following tests run with -Werror (gcc only) if possible
+	NOWARN=$DOWARN; phase=u
+	ac_flags 1 wnodeprecateddecls -Wno-deprecated-declarations
+	# mksh is not written in CFrustFrust!
+	ac_flags 1 no_eh_frame -fno-asynchronous-unwind-tables
+	ac_flags 1 fnostrictaliasing -fno-strict-aliasing
+	ac_flags 1 fstackprotectorstrong -fstack-protector-strong
+	test 1 = $HAVE_CAN_FSTACKPROTECTORSTRONG || \
+	    ac_flags 1 fstackprotectorall -fstack-protector-all
+	test $cm = dragonegg && case " $CC $CFLAGS $LDFLAGS " in
+	*\ -fplugin=*dragonegg*) ;;
+	*) ac_flags 1 fplugin_dragonegg -fplugin=dragonegg ;;
+	esac
+	case $cm in
+	combine)
+		fv=0
+		checks='7 8'
+		;;
+	lto)
+		fv=0
+		checks='1 2 3 4 5 6 7 8'
+		;;
+	*)
+		fv=1
+		;;
+	esac
+	test $fv = 1 || for what in $checks; do
+		test $fv = 1 && break
+		case $what in
+		1)	t_cflags='-flto=jobserver'
+			t_ldflags='-fuse-linker-plugin'
+			t_use=1 t_name=fltojs_lp ;;
+		2)	t_cflags='-flto=jobserver' t_ldflags=''
+			t_use=1 t_name=fltojs_nn ;;
+		3)	t_cflags='-flto=jobserver'
+			t_ldflags='-fno-use-linker-plugin -fwhole-program'
+			t_use=1 t_name=fltojs_np ;;
+		4)	t_cflags='-flto'
+			t_ldflags='-fuse-linker-plugin'
+			t_use=1 t_name=fltons_lp ;;
+		5)	t_cflags='-flto' t_ldflags=''
+			t_use=1 t_name=fltons_nn ;;
+		6)	t_cflags='-flto'
+			t_ldflags='-fno-use-linker-plugin -fwhole-program'
+			t_use=1 t_name=fltons_np ;;
+		7)	t_cflags='-fwhole-program --combine' t_ldflags=''
+			t_use=0 t_name=combine cm=combine ;;
+		8)	fv=1 cm=normal ;;
+		esac
+		test $fv = 1 && break
+		ac_flags $t_use $t_name "$t_cflags" \
+		    "if gcc supports $t_cflags $t_ldflags" "$t_ldflags"
+	done
+	i=1
+	;;
+hpcc)
+	phase=u
+	# probably not needed
+	#ac_flags 1 agcc -Agcc 'for support of GCC extensions'
+	phase=x
+	;;
+icc)
+	ac_flags 1 fnobuiltinsetmode -fno-builtin-setmode
+	ac_flags 1 fnostrictaliasing -fno-strict-aliasing
+	ac_flags 1 fstacksecuritycheck -fstack-security-check
+	i=1
+	;;
+mipspro)
+	ac_flags 1 fullwarn -fullwarn 'for remark output support'
+	;;
+msc)
+	ac_flags 1 strpool "${ccpc}/GF" 'if string pooling can be enabled'
+	echo 'int main(void) { char test[64] = ""; return (*test); }' >x
+	ac_flags - 1 stackon "${ccpc}/GZ" 'if stack checks can be enabled' <x
+	ac_flags - 1 stckall "${ccpc}/Ge" 'stack checks for all functions' <x
+	ac_flags - 1 secuchk "${ccpc}/GS" 'for compiler security checks' <x
+	rmf x
+	ac_flags 1 wall "${ccpc}/Wall" 'to enable all warnings'
+	ac_flags 1 wp64 "${ccpc}/Wp64" 'to enable 64-bit warnings'
+	;;
+nwcc)
+	i=1
+	#broken# ac_flags 1 ssp -stackprotect
+	;;
+sunpro)
+	phase=u
+	ac_flags 1 v -v
+	ac_flags 1 ipo -xipo 'for cross-module optimisation'
+	phase=x
+	;;
+tcc)
+	: #broken# ac_flags 1 boundschk -b
+	;;
+tendra)
+	ac_flags 0 ysystem -Ysystem
+	test 1 = $HAVE_CAN_YSYSTEM && CPPFLAGS="-Ysystem $CPPFLAGS"
+	ac_flags 1 extansi -Xa
+	;;
+xlc)
+	ac_flags 1 rodata "-qro -qroconst -qroptr"
+	ac_flags 1 rtcheck -qcheck=all
+	#ac_flags 1 rtchkc -qextchk	# reported broken
+	ac_flags 1 wformat "-qformat=all -qformat=nozln"
+	#ac_flags 1 wp64 -qwarn64	# too verbose for now
+	;;
+esac
+# flags common to a subset of compilers (run with -Werror on gcc)
+if test 1 = $i; then
+	ac_flags 1 wall -Wall
+	ac_flags 1 fwrapv -fwrapv
+fi
+
+phase=x
+# The following tests run with -Werror or similar (all compilers) if possible
+NOWARN=$DOWARN
+test $ct = pcc && phase=u
+
+#
+# Compiler: check for stuff that only generates warnings
+#
+ac_test attribute_bounded '' 'for __attribute__((__bounded__))' <<-'EOF'
+	#if defined(__TenDRA__) || (defined(__GNUC__) && (__GNUC__ < 2))
+	extern int thiswillneverbedefinedIhope(void);
+	/* force a failure: TenDRA and gcc 1.42 have false positive here */
+	int main(void) { return (thiswillneverbedefinedIhope()); }
+	#else
+	#include <string.h>
+	#undef __attribute__
+	int xcopy(const void *, void *, size_t)
+	    __attribute__((__bounded__(__buffer__, 1, 3)))
+	    __attribute__((__bounded__(__buffer__, 2, 3)));
+	int main(int ac, char *av[]) { return (xcopy(av[0], av[--ac], 1)); }
+	int xcopy(const void *s, void *d, size_t n) {
+		/*
+		 * if memmove does not exist, we are not on a system
+		 * with GCC with __bounded__ attribute either so poo
+		 */
+		memmove(d, s, n); return ((int)n);
+	}
+	#endif
+EOF
+ac_test attribute_format '' 'for __attribute__((__format__))' <<-'EOF'
+	#if defined(__TenDRA__) || (defined(__GNUC__) && (__GNUC__ < 2))
+	extern int thiswillneverbedefinedIhope(void);
+	/* force a failure: TenDRA and gcc 1.42 have false positive here */
+	int main(void) { return (thiswillneverbedefinedIhope()); }
+	#else
+	#define fprintf printfoo
+	#include <stdio.h>
+	#undef __attribute__
+	#undef fprintf
+	extern int fprintf(FILE *, const char *format, ...)
+	    __attribute__((__format__(__printf__, 2, 3)));
+	int main(int ac, char **av) { return (fprintf(stderr, "%s%d", *av, ac)); }
+	#endif
+EOF
+ac_test attribute_noreturn '' 'for __attribute__((__noreturn__))' <<-'EOF'
+	#if defined(__TenDRA__) || (defined(__GNUC__) && (__GNUC__ < 2))
+	extern int thiswillneverbedefinedIhope(void);
+	/* force a failure: TenDRA and gcc 1.42 have false positive here */
+	int main(void) { return (thiswillneverbedefinedIhope()); }
+	#else
+	#include <stdlib.h>
+	#undef __attribute__
+	void fnord(void) __attribute__((__noreturn__));
+	int main(void) { fnord(); }
+	void fnord(void) { exit(0); }
+	#endif
+EOF
+ac_test attribute_pure '' 'for __attribute__((__pure__))' <<-'EOF'
+	#if defined(__TenDRA__) || (defined(__GNUC__) && (__GNUC__ < 2))
+	extern int thiswillneverbedefinedIhope(void);
+	/* force a failure: TenDRA and gcc 1.42 have false positive here */
+	int main(void) { return (thiswillneverbedefinedIhope()); }
+	#else
+	#include <unistd.h>
+	#undef __attribute__
+	int foo(const char *) __attribute__((__pure__));
+	int main(int ac, char **av) { return (foo(av[ac - 1]) + isatty(0)); }
+	int foo(const char *s) { return ((int)s[0]); }
+	#endif
+EOF
+ac_test attribute_unused '' 'for __attribute__((__unused__))' <<-'EOF'
+	#if defined(__TenDRA__) || (defined(__GNUC__) && (__GNUC__ < 2))
+	extern int thiswillneverbedefinedIhope(void);
+	/* force a failure: TenDRA and gcc 1.42 have false positive here */
+	int main(void) { return (thiswillneverbedefinedIhope()); }
+	#else
+	#include <unistd.h>
+	#undef __attribute__
+	int main(int ac __attribute__((__unused__)), char **av
+	    __attribute__((__unused__))) { return (isatty(0)); }
+	#endif
+EOF
+ac_test attribute_used '' 'for __attribute__((__used__))' <<-'EOF'
+	#if defined(__TenDRA__) || (defined(__GNUC__) && (__GNUC__ < 2))
+	extern int thiswillneverbedefinedIhope(void);
+	/* force a failure: TenDRA and gcc 1.42 have false positive here */
+	int main(void) { return (thiswillneverbedefinedIhope()); }
+	#else
+	#include <unistd.h>
+	#undef __attribute__
+	static const char fnord[] __attribute__((__used__)) = "42";
+	int main(void) { return (isatty(0)); }
+	#endif
+EOF
+
+# End of tests run with -Werror
+NOWARN=$save_NOWARN
+phase=x
+
+#
+# mksh: flavours (full/small mksh, omit certain stuff)
+#
+if ac_ifcpp 'ifdef MKSH_SMALL' isset_MKSH_SMALL '' \
+    "if a reduced-feature mksh is requested"; then
+	: ${HAVE_NICE=0}
+	: ${HAVE_PERSISTENT_HISTORY=0}
+	check_categories="$check_categories smksh"
+	HAVE_ISSET_MKSH_CONSERVATIVE_FDS=1	# from sh.h
+fi
+ac_ifcpp 'if defined(MKSH_BINSHPOSIX) || defined(MKSH_BINSHREDUCED)' \
+    isset_MKSH_BINSH '' 'if invoking as sh should be handled specially' && \
+    check_categories="$check_categories binsh"
+ac_ifcpp 'ifdef MKSH_UNEMPLOYED' isset_MKSH_UNEMPLOYED '' \
+    "if mksh will be built without job control" && \
+    check_categories="$check_categories arge"
+ac_ifcpp 'ifdef MKSH_NOPROSPECTOFWORK' isset_MKSH_NOPROSPECTOFWORK '' \
+    "if mksh will be built without job signals" && \
+    check_categories="$check_categories arge nojsig"
+ac_ifcpp 'ifdef MKSH_ASSUME_UTF8' isset_MKSH_ASSUME_UTF8 '' \
+    'if the default UTF-8 mode is specified' && : ${HAVE_SETLOCALE_CTYPE=0}
+ac_ifcpp 'ifdef MKSH_CONSERVATIVE_FDS' isset_MKSH_CONSERVATIVE_FDS '' \
+    'if traditional/conservative fd use is requested' && \
+    check_categories="$check_categories convfds"
+#ac_ifcpp 'ifdef MKSH_DISABLE_DEPRECATED' isset_MKSH_DISABLE_DEPRECATED '' \
+#    "if deprecated features are to be omitted" && \
+#    check_categories="$check_categories nodeprecated"
+#ac_ifcpp 'ifdef MKSH_DISABLE_EXPERIMENTAL' isset_MKSH_DISABLE_EXPERIMENTAL '' \
+#    "if experimental features are to be omitted" && \
+#    check_categories="$check_categories noexperimental"
+ac_ifcpp 'ifdef MKSH_MIDNIGHTBSD01ASH_COMPAT' isset_MKSH_MIDNIGHTBSD01ASH_COMPAT '' \
+    'if the MidnightBSD 0.1 ash compatibility mode is requested' && \
+    check_categories="$check_categories mnbsdash"
+
+#
+# Environment: headers
+#
+ac_header sys/time.h sys/types.h
+ac_header time.h sys/types.h
+test "11" = "$HAVE_SYS_TIME_H$HAVE_TIME_H" || HAVE_BOTH_TIME_H=0
+ac_test both_time_h '' 'whether <sys/time.h> and <time.h> can both be included' <<-'EOF'
+	#include <sys/types.h>
+	#include <sys/time.h>
+	#include <time.h>
+	#include <unistd.h>
+	int main(void) { struct tm tm; return ((int)sizeof(tm) + isatty(0)); }
+EOF
+ac_header sys/bsdtypes.h
+ac_header sys/file.h sys/types.h
+ac_header sys/mkdev.h sys/types.h
+ac_header sys/mman.h sys/types.h
+ac_header sys/param.h
+ac_header sys/resource.h sys/types.h _time
+ac_header sys/select.h sys/types.h
+ac_header sys/sysmacros.h
+ac_header bstring.h
+ac_header grp.h sys/types.h
+ac_header libgen.h
+ac_header libutil.h sys/types.h
+ac_header paths.h
+ac_header stdint.h stdarg.h
+# include strings.h only if compatible with string.h
+ac_header strings.h sys/types.h string.h
+ac_header termios.h
+ac_header ulimit.h sys/types.h
+ac_header values.h
+
+#
+# Environment: definitions
+#
+echo '#include <sys/types.h>
+#include <unistd.h>
+/* check that off_t can represent 2^63-1 correctly, thx FSF */
+#define LARGE_OFF_T ((((off_t)1 << 31) << 31) - 1 + (((off_t)1 << 31) << 31))
+int off_t_is_large[(LARGE_OFF_T % 2147483629 == 721 &&
+    LARGE_OFF_T % 2147483647 == 1) ? 1 : -1];
+int main(void) { return (isatty(0)); }' >lft.c
+ac_testn can_lfs '' "for large file support" <lft.c
+save_CPPFLAGS=$CPPFLAGS
+add_cppflags -D_FILE_OFFSET_BITS=64
+ac_testn can_lfs_sus '!' can_lfs 0 "... with -D_FILE_OFFSET_BITS=64" <lft.c
+if test 0 = $HAVE_CAN_LFS_SUS; then
+	CPPFLAGS=$save_CPPFLAGS
+	add_cppflags -D_LARGE_FILES=1
+	ac_testn can_lfs_aix '!' can_lfs 0 "... with -D_LARGE_FILES=1" <lft.c
+	test 1 = $HAVE_CAN_LFS_AIX || CPPFLAGS=$save_CPPFLAGS
+fi
+rmf lft*	# end of large file support test
+
+#
+# Environment: types
+#
+ac_test can_inttypes '!' stdint_h 1 "for standard 32-bit integer types" <<-'EOF'
+	#include <sys/types.h>
+	#include <stddef.h>
+	int main(int ac, char **av) { return ((uint32_t)(ptrdiff_t)*av + (int32_t)ac); }
+EOF
+ac_test can_ucbints '!' can_inttypes 1 "for UCB 32-bit integer types" <<-'EOF'
+	#include <sys/types.h>
+	#include <stddef.h>
+	int main(int ac, char **av) { return ((u_int32_t)(ptrdiff_t)*av + (int32_t)ac); }
+EOF
+ac_test can_int8type '!' stdint_h 1 "for standard 8-bit integer type" <<-'EOF'
+	#include <sys/types.h>
+	#include <stddef.h>
+	int main(int ac, char **av) { return ((uint8_t)(ptrdiff_t)av[ac]); }
+EOF
+ac_test can_ucbint8 '!' can_int8type 1 "for UCB 8-bit integer type" <<-'EOF'
+	#include <sys/types.h>
+	#include <stddef.h>
+	int main(int ac, char **av) { return ((u_int8_t)(ptrdiff_t)av[ac]); }
+EOF
+
+ac_test rlim_t <<-'EOF'
+	#include <sys/types.h>
+	#if HAVE_BOTH_TIME_H
+	#include <sys/time.h>
+	#include <time.h>
+	#elif HAVE_SYS_TIME_H
+	#include <sys/time.h>
+	#elif HAVE_TIME_H
+	#include <time.h>
+	#endif
+	#if HAVE_SYS_RESOURCE_H
+	#include <sys/resource.h>
+	#endif
+	#include <unistd.h>
+	int main(void) { return (((int)(rlim_t)0) + isatty(0)); }
+EOF
+
+# only testn: added later below
+ac_testn sig_t <<-'EOF'
+	#include <sys/types.h>
+	#include <signal.h>
+	#include <stddef.h>
+	volatile sig_t foo = (sig_t)0;
+	int main(void) { return (foo == (sig_t)0); }
+EOF
+
+ac_testn sighandler_t '!' sig_t 0 <<-'EOF'
+	#include <sys/types.h>
+	#include <signal.h>
+	#include <stddef.h>
+	volatile sighandler_t foo = (sighandler_t)0;
+	int main(void) { return (foo == (sighandler_t)0); }
+EOF
+if test 1 = $HAVE_SIGHANDLER_T; then
+	add_cppflags -Dsig_t=sighandler_t
+	HAVE_SIG_T=1
+fi
+
+ac_testn __sighandler_t '!' sig_t 0 <<-'EOF'
+	#include <sys/types.h>
+	#include <signal.h>
+	#include <stddef.h>
+	volatile __sighandler_t foo = (__sighandler_t)0;
+	int main(void) { return (foo == (__sighandler_t)0); }
+EOF
+if test 1 = $HAVE___SIGHANDLER_T; then
+	add_cppflags -Dsig_t=__sighandler_t
+	HAVE_SIG_T=1
+fi
+
+test 1 = $HAVE_SIG_T || add_cppflags -Dsig_t=nosig_t
+ac_cppflags SIG_T
+
+#
+# check whether whatever we use for the final link will succeed
+#
+if test $cm = makefile; then
+	: nothing to check
+else
+	HAVE_LINK_WORKS=x
+	ac_testinit link_works '' 'checking if the final link command may succeed'
+	fv=1
+	cat >conftest.c <<-'EOF'
+		#define EXTERN
+		#define MKSH_INCLUDES_ONLY
+		#include "sh.h"
+		__RCSID("$MirOS: src/bin/mksh/Build.sh,v 1.662 2014/06/29 10:56:08 tg Exp $");
+		int main(void) { printf("Hello, World!\n"); return (isatty(0)); }
+EOF
+	case $cm in
+	llvm)
+		v "$CC $CFLAGS $CPPFLAGS $NOWARN -emit-llvm -c conftest.c" || fv=0
+		rmf $tfn.s
+		test $fv = 0 || v "llvm-link -o - conftest.o | opt $optflags | llc -o $tfn.s" || fv=0
+		test $fv = 0 || v "$CC $CFLAGS $LDFLAGS -o $tcfn $tfn.s $LIBS $ccpr"
+		;;
+	dragonegg)
+		v "$CC $CFLAGS $CPPFLAGS $NOWARN -S -flto conftest.c" || fv=0
+		test $fv = 0 || v "mv conftest.s conftest.ll"
+		test $fv = 0 || v "llvm-as conftest.ll" || fv=0
+		rmf $tfn.s
+		test $fv = 0 || v "llvm-link -o - conftest.bc | opt $optflags | llc -o $tfn.s" || fv=0
+		test $fv = 0 || v "$CC $CFLAGS $LDFLAGS -o $tcfn $tfn.s $LIBS $ccpr"
+		;;
+	combine)
+		v "$CC $CFLAGS $CPPFLAGS $LDFLAGS -fwhole-program --combine $NOWARN -o $tcfn conftest.c $LIBS $ccpr"
+		;;
+	lto|normal)
+		cm=normal
+		v "$CC $CFLAGS $CPPFLAGS $NOWARN -c conftest.c" || fv=0
+		test $fv = 0 || v "$CC $CFLAGS $LDFLAGS -o $tcfn conftest.o $LIBS $ccpr"
+		;;
+	esac
+	test -f $tcfn || fv=0
+	ac_testdone
+	test $fv = 1 || exit 1
+fi
+
+#
+# Environment: errors and signals
+#
+test x"NetBSD" = x"$TARGET_OS" && $e Ignore the compatibility warning.
+
+ac_testn sys_errlist '' "the sys_errlist[] array and sys_nerr" <<-'EOF'
+	extern const int sys_nerr;
+	extern const char * const sys_errlist[];
+	extern int isatty(int);
+	int main(void) { return (*sys_errlist[sys_nerr - 1] + isatty(0)); }
+EOF
+ac_testn _sys_errlist '!' sys_errlist 0 "the _sys_errlist[] array and _sys_nerr" <<-'EOF'
+	extern const int _sys_nerr;
+	extern const char * const _sys_errlist[];
+	extern int isatty(int);
+	int main(void) { return (*_sys_errlist[_sys_nerr - 1] + isatty(0)); }
+EOF
+if test 1 = "$HAVE__SYS_ERRLIST"; then
+	add_cppflags -Dsys_nerr=_sys_nerr
+	add_cppflags -Dsys_errlist=_sys_errlist
+	HAVE_SYS_ERRLIST=1
+fi
+ac_cppflags SYS_ERRLIST
+
+for what in name list; do
+	uwhat=`upper $what`
+	ac_testn sys_sig$what '' "the sys_sig${what}[] array" <<-EOF
+		extern const char * const sys_sig${what}[];
+		extern int isatty(int);
+		int main(void) { return (sys_sig${what}[0][0] + isatty(0)); }
+	EOF
+	ac_testn _sys_sig$what '!' sys_sig$what 0 "the _sys_sig${what}[] array" <<-EOF
+		extern const char * const _sys_sig${what}[];
+		extern int isatty(int);
+		int main(void) { return (_sys_sig${what}[0][0] + isatty(0)); }
+	EOF
+	eval uwhat_v=\$HAVE__SYS_SIG$uwhat
+	if test 1 = "$uwhat_v"; then
+		add_cppflags -Dsys_sig$what=_sys_sig$what
+		eval HAVE_SYS_SIG$uwhat=1
+	fi
+	ac_cppflags SYS_SIG$uwhat
+done
+
+#
+# Environment: library functions
+#
+ac_test flock <<-'EOF'
+	#include <sys/types.h>
+	#include <fcntl.h>
+	#undef flock
+	#if HAVE_SYS_FILE_H
+	#include <sys/file.h>
+	#endif
+	int main(void) { return (flock(0, LOCK_EX | LOCK_UN)); }
+EOF
+
+ac_test lock_fcntl '!' flock 1 'whether we can lock files with fcntl' <<-'EOF'
+	#include <fcntl.h>
+	#undef flock
+	int main(void) {
+		struct flock lks;
+		lks.l_type = F_WRLCK | F_UNLCK;
+		return (fcntl(0, F_SETLKW, &lks));
+	}
+EOF
+
+ac_test getrusage <<-'EOF'
+	#define MKSH_INCLUDES_ONLY
+	#include "sh.h"
+	int main(void) {
+		struct rusage ru;
+		return (getrusage(RUSAGE_SELF, &ru) +
+		    getrusage(RUSAGE_CHILDREN, &ru));
+	}
+EOF
+
+ac_test getsid <<-'EOF'
+	#include <unistd.h>
+	int main(void) { return ((int)getsid(0)); }
+EOF
+
+ac_test gettimeofday <<-'EOF'
+	#define MKSH_INCLUDES_ONLY
+	#include "sh.h"
+	int main(void) { struct timeval tv; return (gettimeofday(&tv, NULL)); }
+EOF
+
+ac_test killpg <<-'EOF'
+	#include <signal.h>
+	int main(int ac, char *av[]) { return (av[0][killpg(123, ac)]); }
+EOF
+
+ac_test memmove <<-'EOF'
+	#include <sys/types.h>
+	#include <stddef.h>
+	#include <string.h>
+	#if HAVE_STRINGS_H
+	#include <strings.h>
+	#endif
+	int main(int ac, char *av[]) {
+		return (*(int *)(void *)memmove(av[0], av[1], (size_t)ac));
+	}
+EOF
+
+ac_test mknod '' 'if to use mknod(), makedev() and friends' <<-'EOF'
+	#define MKSH_INCLUDES_ONLY
+	#include "sh.h"
+	int main(int ac, char *av[]) {
+		dev_t dv;
+		dv = makedev((unsigned int)ac, (unsigned int)av[0][0]);
+		return (mknod(av[0], (mode_t)0, dv) ? (int)major(dv) :
+		    (int)minor(dv));
+	}
+EOF
+
+ac_test mmap lock_fcntl 0 'for mmap and munmap' <<-'EOF'
+	#include <sys/types.h>
+	#if HAVE_SYS_FILE_H
+	#include <sys/file.h>
+	#endif
+	#if HAVE_SYS_MMAN_H
+	#include <sys/mman.h>
+	#endif
+	#include <stddef.h>
+	#include <stdlib.h>
+	int main(void) { return ((void *)mmap(NULL, (size_t)0,
+	    PROT_READ, MAP_PRIVATE, 0, (off_t)0) == (void *)NULL ? 1 :
+	    munmap(NULL, 0)); }
+EOF
+
+ac_test nice <<-'EOF'
+	#include <unistd.h>
+	int main(void) { return (nice(4)); }
+EOF
+
+ac_test revoke <<-'EOF'
+	#include <sys/types.h>
+	#if HAVE_LIBUTIL_H
+	#include <libutil.h>
+	#endif
+	#include <unistd.h>
+	int main(int ac, char *av[]) { return (ac + revoke(av[0])); }
+EOF
+
+ac_test setlocale_ctype '' 'setlocale(LC_CTYPE, "")' <<-'EOF'
+	#include <locale.h>
+	#include <stddef.h>
+	int main(void) { return ((int)(ptrdiff_t)(void *)setlocale(LC_CTYPE, "")); }
+EOF
+
+ac_test langinfo_codeset setlocale_ctype 0 'nl_langinfo(CODESET)' <<-'EOF'
+	#include <langinfo.h>
+	#include <stddef.h>
+	int main(void) { return ((int)(ptrdiff_t)(void *)nl_langinfo(CODESET)); }
+EOF
+
+ac_test select <<-'EOF'
+	#include <sys/types.h>
+	#if HAVE_BOTH_TIME_H
+	#include <sys/time.h>
+	#include <time.h>
+	#elif HAVE_SYS_TIME_H
+	#include <sys/time.h>
+	#elif HAVE_TIME_H
+	#include <time.h>
+	#endif
+	#if HAVE_SYS_BSDTYPES_H
+	#include <sys/bsdtypes.h>
+	#endif
+	#if HAVE_SYS_SELECT_H
+	#include <sys/select.h>
+	#endif
+	#if HAVE_BSTRING_H
+	#include <bstring.h>
+	#endif
+	#include <stddef.h>
+	#include <stdlib.h>
+	#include <string.h>
+	#if HAVE_STRINGS_H
+	#include <strings.h>
+	#endif
+	#include <unistd.h>
+	int main(void) {
+		struct timeval tv = { 1, 200000 };
+		fd_set fds; FD_ZERO(&fds); FD_SET(0, &fds);
+		return (select(FD_SETSIZE, &fds, NULL, NULL, &tv));
+	}
+EOF
+
+ac_test setresugid <<-'EOF'
+	#include <sys/types.h>
+	#include <unistd.h>
+	int main(void) { return (setresuid(0,0,0) + setresgid(0,0,0)); }
+EOF
+
+ac_test setgroups setresugid 0 <<-'EOF'
+	#include <sys/types.h>
+	#if HAVE_GRP_H
+	#include <grp.h>
+	#endif
+	#include <unistd.h>
+	int main(void) { gid_t gid = 0; return (setgroups(0, &gid)); }
+EOF
+
+if test x"$et" = x"klibc"; then
+
+	ac_testn __rt_sigsuspend '' 'whether klibc uses RT signals' <<-'EOF'
+		#define MKSH_INCLUDES_ONLY
+		#include "sh.h"
+		extern int __rt_sigsuspend(const sigset_t *, size_t);
+		int main(void) { return (__rt_sigsuspend(NULL, 0)); }
+EOF
+
+	# no? damn! legacy crap ahead!
+
+	ac_testn __sigsuspend_s '!' __rt_sigsuspend 1 \
+	    'whether sigsuspend is usable (1/2)' <<-'EOF'
+		#define MKSH_INCLUDES_ONLY
+		#include "sh.h"
+		extern int __sigsuspend_s(sigset_t);
+		int main(void) { return (__sigsuspend_s(0)); }
+EOF
+	ac_testn __sigsuspend_xxs '!' __sigsuspend_s 1 \
+	    'whether sigsuspend is usable (2/2)' <<-'EOF'
+		#define MKSH_INCLUDES_ONLY
+		#include "sh.h"
+		extern int __sigsuspend_xxs(int, int, sigset_t);
+		int main(void) { return (__sigsuspend_xxs(0, 0, 0)); }
+EOF
+
+	if test "000" = "$HAVE___RT_SIGSUSPEND$HAVE___SIGSUSPEND_S$HAVE___SIGSUSPEND_XXS"; then
+		# no usable sigsuspend(), use pause() *ugh*
+		add_cppflags -DMKSH_NO_SIGSUSPEND
+	fi
+fi
+
+ac_test strerror '!' sys_errlist 0 <<-'EOF'
+	extern char *strerror(int);
+	int main(int ac, char *av[]) { return (*strerror(*av[ac])); }
+EOF
+
+ac_test strsignal '!' sys_siglist 0 <<-'EOF'
+	#include <string.h>
+	#include <signal.h>
+	int main(void) { return (strsignal(1)[0]); }
+EOF
+
+ac_test strlcpy <<-'EOF'
+	#include <string.h>
+	int main(int ac, char *av[]) { return (strlcpy(*av, av[1],
+	    (size_t)ac)); }
+EOF
+
+#
+# check headers for declarations
+#
+ac_test flock_decl flock 1 'for declaration of flock()' <<-'EOF'
+	#define MKSH_INCLUDES_ONLY
+	#include "sh.h"
+	#if HAVE_SYS_FILE_H
+	#include <sys/file.h>
+	#endif
+	int main(void) { return ((flock)(0, 0)); }
+EOF
+ac_test revoke_decl revoke 1 'for declaration of revoke()' <<-'EOF'
+	#define MKSH_INCLUDES_ONLY
+	#include "sh.h"
+	int main(void) { return ((revoke)("")); }
+EOF
+ac_test sys_errlist_decl sys_errlist 0 "for declaration of sys_errlist[] and sys_nerr" <<-'EOF'
+	#define MKSH_INCLUDES_ONLY
+	#include "sh.h"
+	int main(void) { return (*sys_errlist[sys_nerr - 1] + isatty(0)); }
+EOF
+ac_test sys_siglist_decl sys_siglist 0 'for declaration of sys_siglist[]' <<-'EOF'
+	#define MKSH_INCLUDES_ONLY
+	#include "sh.h"
+	int main(void) { return (sys_siglist[0][0] + isatty(0)); }
+EOF
+
+#
+# other checks
+#
+fd='if to use persistent history'
+ac_cache PERSISTENT_HISTORY || case $HAVE_MMAP$HAVE_FLOCK$HAVE_LOCK_FCNTL in
+11*|101) fv=1 ;;
+esac
+test 1 = $fv || check_categories="$check_categories no-histfile"
+ac_testdone
+ac_cppflags
+
+save_CFLAGS=$CFLAGS
+ac_testn compile_time_asserts_$$ '' 'whether compile-time assertions pass' <<-'EOF'
+	#define MKSH_INCLUDES_ONLY
+	#include "sh.h"
+	#ifndef CHAR_BIT
+	#define CHAR_BIT 8	/* defuse this test on really legacy systems */
+	#endif
+	struct ctasserts {
+	#define cta(name, assertion) char name[(assertion) ? 1 : -1]
+/* this one should be defined by the standard */
+cta(char_is_1_char, (sizeof(char) == 1) && (sizeof(signed char) == 1) &&
+    (sizeof(unsigned char) == 1));
+cta(char_is_8_bits, ((CHAR_BIT) == 8) && ((int)(unsigned char)0xFF == 0xFF) &&
+    ((int)(unsigned char)0x100 == 0) && ((int)(unsigned char)(int)-1 == 0xFF));
+/* the next assertion is probably not really needed */
+cta(short_is_2_char, sizeof(short) == 2);
+cta(short_size_no_matter_of_signedness, sizeof(short) == sizeof(unsigned short));
+/* the next assertion is probably not really needed */
+cta(int_is_4_char, sizeof(int) == 4);
+cta(int_size_no_matter_of_signedness, sizeof(int) == sizeof(unsigned int));
+
+cta(long_ge_int, sizeof(long) >= sizeof(int));
+cta(long_size_no_matter_of_signedness, sizeof(long) == sizeof(unsigned long));
+
+#ifndef MKSH_LEGACY_MODE
+/* the next assertion is probably not really needed */
+cta(ari_is_4_char, sizeof(mksh_ari_t) == 4);
+/* but this is */
+cta(ari_has_31_bit, 0 < (mksh_ari_t)(((((mksh_ari_t)1 << 15) << 15) - 1) * 2 + 1));
+/* the next assertion is probably not really needed */
+cta(uari_is_4_char, sizeof(mksh_uari_t) == 4);
+/* but the next three are; we REQUIRE unsigned integer wraparound */
+cta(uari_has_31_bit, 0 < (mksh_uari_t)(((((mksh_uari_t)1 << 15) << 15) - 1) * 2 + 1));
+cta(uari_has_32_bit, 0 < (mksh_uari_t)(((((mksh_uari_t)1 << 15) << 15) - 1) * 4 + 3));
+cta(uari_wrap_32_bit,
+    (mksh_uari_t)(((((mksh_uari_t)1 << 15) << 15) - 1) * 4 + 3) >
+    (mksh_uari_t)(((((mksh_uari_t)1 << 15) << 15) - 1) * 4 + 4));
+#define NUM 22
+#else
+#define NUM 16
+#endif
+/* these are always required */
+cta(ari_is_signed, (mksh_ari_t)-1 < (mksh_ari_t)0);
+cta(uari_is_unsigned, (mksh_uari_t)-1 > (mksh_uari_t)0);
+/* we require these to have the precisely same size and assume 2s complement */
+cta(ari_size_no_matter_of_signedness, sizeof(mksh_ari_t) == sizeof(mksh_uari_t));
+
+cta(sizet_size_no_matter_of_signedness, sizeof(ssize_t) == sizeof(size_t));
+cta(ptrdifft_sizet_same_size, sizeof(ptrdiff_t) == sizeof(size_t));
+cta(ptrdifft_voidptr_same_size, sizeof(ptrdiff_t) == sizeof(void *));
+cta(ptrdifft_funcptr_same_size, sizeof(ptrdiff_t) == sizeof(void (*)(void)));
+/* our formatting routines assume this */
+cta(ptr_fits_in_long, sizeof(ptrdiff_t) <= sizeof(long));
+/* for struct alignment people */
+		char padding[64 - NUM];
+	};
+char ctasserts_dblcheck[sizeof(struct ctasserts) == 64 ? 1 : -1];
+	int main(void) { return (sizeof(ctasserts_dblcheck) + isatty(0)); }
+EOF
+CFLAGS=$save_CFLAGS
+eval test 1 = \$HAVE_COMPILE_TIME_ASSERTS_$$ || exit 1
+
+#
+# extra checks for legacy mksh
+#
+if test $legacy = 1; then
+	ac_test long_32bit '' 'whether long is 32 bit wide' <<-'EOF'
+		#define MKSH_INCLUDES_ONLY
+		#include "sh.h"
+		#ifndef CHAR_BIT
+		#define CHAR_BIT 0
+		#endif
+		struct ctasserts {
+		#define cta(name, assertion) char name[(assertion) ? 1 : -1]
+			cta(char_is_8_bits, (CHAR_BIT) == 8);
+			cta(long_is_32_bits, sizeof(long) == 4);
+		};
+		int main(void) { return (sizeof(struct ctasserts)); }
+EOF
+
+	ac_test long_64bit '!' long_32bit 0 'whether long is 64 bit wide' <<-'EOF'
+		#define MKSH_INCLUDES_ONLY
+		#include "sh.h"
+		#ifndef CHAR_BIT
+		#define CHAR_BIT 0
+		#endif
+		struct ctasserts {
+		#define cta(name, assertion) char name[(assertion) ? 1 : -1]
+			cta(char_is_8_bits, (CHAR_BIT) == 8);
+			cta(long_is_64_bits, sizeof(long) == 8);
+		};
+		int main(void) { return (sizeof(struct ctasserts)); }
+EOF
+
+	case $HAVE_LONG_32BIT$HAVE_LONG_64BIT in
+	10) check_categories="$check_categories int:32" ;;
+	01) check_categories="$check_categories int:64" ;;
+	*) check_categories="$check_categories int:u" ;;
+	esac
+fi
+
+#
+# Compiler: Praeprocessor (only if needed)
+#
+test 0 = $HAVE_SYS_SIGNAME && if ac_testinit cpp_dd '' \
+    'checking if the C Preprocessor supports -dD'; then
+	echo '#define foo bar' >conftest.c
+	vv ']' "$CPP $CFLAGS $CPPFLAGS $NOWARN -dD conftest.c >x"
+	grep '#define foo bar' x >/dev/null 2>&1 && fv=1
+	rmf conftest.c x vv.out
+	ac_testdone
+fi
+
+#
+# End of mirtoconf checks
+#
+$e ... done.
+
+# Some operating systems have ancient versions of ed(1) writing
+# the character count to standard output; cope for that
+echo wq >x
+ed x <x 2>/dev/null | grep 3 >/dev/null 2>&1 && \
+    check_categories="$check_categories $oldish_ed"
+rmf x vv.out
+
+if test 0 = $HAVE_SYS_SIGNAME; then
+	if test 1 = $HAVE_CPP_DD; then
+		$e Generating list of signal names...
+	else
+		$e No list of signal names available via cpp. Falling back...
+	fi
+	sigseenone=:
+	sigseentwo=:
+	echo '#include <signal.h>
+#ifndef NSIG
+#if defined(_NSIG)
+#define NSIG _NSIG
+#elif defined(SIGMAX)
+#define NSIG (SIGMAX+1)
+#elif defined(_SIGMAX)
+#define NSIG (_SIGMAX+1)
+#else
+/* XXX better error out, see sh.h */
+#define NSIG 64
+#endif
+#endif
+int
+mksh_cfg= NSIG
+;' >conftest.c
+	# GNU sed 2.03 segfaults when optimising this to sed -n
+	NSIG=`vq "$CPP $CFLAGS $CPPFLAGS $NOWARN conftest.c" | \
+	    grep '^ *mksh_cfg *=' | \
+	    sed 's/^ *mksh_cfg *=[	 ]*\([()0-9x+-][()0-9x+	 -]*\).*$/\1/'`
+	case $NSIG in
+	*mksh_cfg*) $e "Error: NSIG='$NSIG'"; NSIG=0 ;;
+	*[\ \(\)+-]*) NSIG=`"$AWK" "BEGIN { print $NSIG }" </dev/null` ;;
+	esac
+	printf=printf
+	(printf hallo) >/dev/null 2>&1 || printf=echo
+	test $printf = echo || test "`printf %d 42`" = 42 || printf=echo
+	test $printf = echo || NSIG=`printf %d "$NSIG" 2>/dev/null`
+	$printf "NSIG=$NSIG ... "
+	sigs="ABRT FPE ILL INT SEGV TERM ALRM BUS CHLD CONT HUP KILL PIPE QUIT"
+	sigs="$sigs STOP TSTP TTIN TTOU USR1 USR2 POLL PROF SYS TRAP URG VTALRM"
+	sigs="$sigs XCPU XFSZ INFO WINCH EMT IO DIL LOST PWR SAK CLD IOT RESV"
+	test 1 = $HAVE_CPP_DD && test $NSIG -gt 1 && sigs="$sigs "`vq \
+	    "$CPP $CFLAGS $CPPFLAGS $NOWARN -dD conftest.c" | \
+	    grep '[	 ]SIG[A-Z0-9][A-Z0-9]*[	 ]' | \
+	    sed 's/^.*[	 ]SIG\([A-Z0-9][A-Z0-9]*\)[	 ].*$/\1/' | sort`
+	test $NSIG -gt 1 || sigs=
+	for name in $sigs; do
+		case $sigseenone in
+		*:$name:*) continue ;;
+		esac
+		sigseenone=$sigseenone$name:
+		echo '#include <signal.h>' >conftest.c
+		echo int >>conftest.c
+		echo mksh_cfg= SIG$name >>conftest.c
+		echo ';' >>conftest.c
+		# GNU sed 2.03 croaks on optimising this, too
+		vq "$CPP $CFLAGS $CPPFLAGS $NOWARN conftest.c" | \
+		    grep '^ *mksh_cfg *=' | \
+		    sed 's/^ *mksh_cfg *=[	 ]*\([0-9][0-9x]*\).*$/:\1 '$name/
+	done | sed -n '/^:[^ ]/s/^://p' | while read nr name; do
+		test $printf = echo || nr=`printf %d "$nr" 2>/dev/null`
+		test $nr -gt 0 && test $nr -le $NSIG || continue
+		case $sigseentwo in
+		*:$nr:*) ;;
+		*)	echo "		{ \"$name\", $nr },"
+			sigseentwo=$sigseentwo$nr:
+			$printf "$name=$nr " >&2
+			;;
+		esac
+	done 2>&1 >signames.inc
+	rmf conftest.c
+	$e done.
+fi
+
+addsrcs '!' HAVE_STRLCPY strlcpy.c
+addsrcs USE_PRINTF_BUILTIN printf.c
+test 1 = "$USE_PRINTF_BUILTIN" && add_cppflags -DMKSH_PRINTF_BUILTIN
+test 1 = "$HAVE_CAN_VERB" && CFLAGS="$CFLAGS -verbose"
+add_cppflags -DMKSH_BUILD_R=501
+
+$e $bi$me: Finished configuration testing, now producing output.$ao
+
+files=
+objs=
+sp=
+case $tcfn in
+a.exe)	mkshexe=$tfn.exe ;;
+*)	mkshexe=$tfn ;;
+esac
+case $curdir in
+*\ *)	mkshshebang="#!./$mkshexe" ;;
+*)	mkshshebang="#!$curdir/$mkshexe" ;;
+esac
+cat >test.sh <<-EOF
+	$mkshshebang
+	LC_ALL=C PATH='$PATH'; export LC_ALL PATH
+	test -n "\$KSH_VERSION" || exit 1
+	set -A check_categories -- $check_categories
+	pflag='$curdir/$mkshexe'
+	sflag='$srcdir/check.t'
+	usee=0 Pflag=0 Sflag=0 uset=0 vflag=1 xflag=0
+	while getopts "C:e:fPp:QSs:t:v" ch; do case \$ch {
+	(C)	check_categories[\${#check_categories[*]}]=\$OPTARG ;;
+	(e)	usee=1; eflag=\$OPTARG ;;
+	(f)	check_categories[\${#check_categories[*]}]=fastbox ;;
+	(P)	Pflag=1 ;;
+	(+P)	Pflag=0 ;;
+	(p)	pflag=\$OPTARG ;;
+	(Q)	vflag=0 ;;
+	(+Q)	vflag=1 ;;
+	(S)	Sflag=1 ;;
+	(+S)	Sflag=0 ;;
+	(s)	sflag=\$OPTARG ;;
+	(t)	uset=1; tflag=\$OPTARG ;;
+	(v)	vflag=1 ;;
+	(+v)	vflag=0 ;;
+	(*)	xflag=1 ;;
+	}
+	done
+	shift \$((OPTIND - 1))
+	set -A args -- '$srcdir/check.pl' -p "\$pflag"
+	x=
+	for y in "\${check_categories[@]}"; do
+		x=\$x,\$y
+	done
+	if [[ -n \$x ]]; then
+		args[\${#args[*]}]=-C
+		args[\${#args[*]}]=\${x#,}
+	fi
+	if (( usee )); then
+		args[\${#args[*]}]=-e
+		args[\${#args[*]}]=\$eflag
+	fi
+	(( Pflag )) && args[\${#args[*]}]=-P
+	if (( uset )); then
+		args[\${#args[*]}]=-t
+		args[\${#args[*]}]=\$tflag
+	fi
+	(( vflag )) && args[\${#args[*]}]=-v
+	(( xflag )) && args[\${#args[*]}]=-x	# force usage by synerr
+	if [[ -n \$TMPDIR && -d \$TMPDIR/. ]]; then
+		args[\${#args[*]}]=-T
+		args[\${#args[*]}]=\$TMPDIR
+	fi
+	print Testing mksh for conformance:
+	fgrep -e MirOS: -e MIRBSD "\$sflag"
+	print "This shell is actually:\\n\\t\$KSH_VERSION"
+	print 'test.sh built for mksh $dstversion'
+	cstr='\$os = defined \$^O ? \$^O : "unknown";'
+	cstr="\$cstr"'print \$os . ", Perl version " . \$];'
+	for perli in \$PERL perl5 perl no; do
+		if [[ \$perli = no ]]; then
+			print Cannot find a working Perl interpreter, aborting.
+			exit 1
+		fi
+		print "Trying Perl interpreter '\$perli'..."
+		perlos=\$(\$perli -e "\$cstr")
+		rv=\$?
+		print "Errorlevel \$rv, running on '\$perlos'"
+		if (( rv )); then
+			print "=> not using"
+			continue
+		fi
+		if [[ -n \$perlos ]]; then
+			print "=> using it"
+			break
+		fi
+	done
+	(( Sflag )) || echo + \$perli "\${args[@]}" -s "\$sflag" "\$@"
+	(( Sflag )) || exec \$perli "\${args[@]}" -s "\$sflag" "\$@"$tsts
+	# use of the -S option for check.t split into multiple chunks
+	rv=0
+	for s in "\$sflag".*; do
+		echo + \$perli "\${args[@]}" -s "\$s" "\$@"
+		\$perli "\${args[@]}" -s "\$s" "\$@"$tsts
+		rc=\$?
+		(( rv = rv ? rv : rc ))
+	done
+	exit \$rv
+EOF
+chmod 755 test.sh
+case $cm in
+dragonegg)
+	emitbc="-S -flto"
+	;;
+llvm)
+	emitbc="-emit-llvm -c"
+	;;
+*)
+	emitbc=-c
+	;;
+esac
+echo ": # work around NeXTstep bug" >Rebuild.sh
+for file in "$srcdir"/*.opt; do
+	echo "echo + Running genopt on '$file'..."
+	echo "(srcfile='$file'; BUILDSH_RUN_GENOPT=1; . '$srcdir/Build.sh')"
+done >>Rebuild.sh
+echo set -x >>Rebuild.sh
+for file in $SRCS; do
+	op=`echo x"$file" | sed 's/^x\(.*\)\.c$/\1./'`
+	test -f $file || file=$srcdir/$file
+	files="$files$sp$file"
+	sp=' '
+	echo "$CC $CFLAGS $CPPFLAGS $emitbc $file || exit 1" >>Rebuild.sh
+	if test $cm = dragonegg; then
+		echo "mv ${op}s ${op}ll" >>Rebuild.sh
+		echo "llvm-as ${op}ll || exit 1" >>Rebuild.sh
+		objs="$objs$sp${op}bc"
+	else
+		objs="$objs$sp${op}o"
+	fi
+done
+case $cm in
+dragonegg|llvm)
+	echo "rm -f $tfn.s" >>Rebuild.sh
+	echo "llvm-link -o - $objs | opt $optflags | llc -o $tfn.s" >>Rebuild.sh
+	lobjs=$tfn.s
+	;;
+*)
+	lobjs=$objs
+	;;
+esac
+echo tcfn=$mkshexe >>Rebuild.sh
+echo "$CC $CFLAGS $LDFLAGS -o \$tcfn $lobjs $LIBS $ccpr" >>Rebuild.sh
+echo "test -f \$tcfn || exit 1; $SIZE \$tcfn" >>Rebuild.sh
+if test $cm = makefile; then
+	extras='emacsfn.h rlimits.opt sh.h sh_flags.opt var_spec.h'
+	test 0 = $HAVE_SYS_SIGNAME && extras="$extras signames.inc"
+	gens= genq=
+	for file in "$srcdir"/*.opt; do
+		genf=`basename "$file" | sed 's/.opt$/.gen/'`
+		gens="$gens $genf"
+		genq="$genq$nl$genf: $srcdir/Build.sh $file
+			srcfile=$file; BUILDSH_RUN_GENOPT=1; . $srcdir/Build.sh"
+	done
+	cat >Makefrag.inc <<EOF
+# Makefile fragment for building mksh $dstversion
+
+PROG=		$mkshexe
+MAN=		mksh.1
+SRCS=		$SRCS
+SRCS_FP=	$files
+OBJS_BP=	$objs
+INDSRCS=	$extras
+NONSRCS_INST=	dot.mkshrc \$(MAN)
+NONSRCS_NOINST=	Build.sh Makefile Rebuild.sh check.pl check.t test.sh
+CC=		$CC
+CFLAGS=		$CFLAGS
+CPPFLAGS=	$CPPFLAGS
+LDFLAGS=	$LDFLAGS
+LIBS=		$LIBS
+
+.depend \$(OBJS_BP):$gens$genq
+
+# not BSD make only:
+#VPATH=		$srcdir
+#all: \$(PROG)
+#\$(PROG): \$(OBJS_BP)
+#	\$(CC) \$(CFLAGS) \$(LDFLAGS) -o \$@ \$(OBJS_BP) \$(LIBS)
+#\$(OBJS_BP): \$(SRCS_FP) \$(NONSRCS)
+#.c.o:
+#	\$(CC) \$(CFLAGS) \$(CPPFLAGS) -c \$<
+
+# for all make variants:
+#REGRESS_FLAGS=	-f
+#regress:
+#	./test.sh \$(REGRESS_FLAGS)
+check_categories=$check_categories
+
+# for BSD make only:
+#.PATH: $srcdir
+#.include <bsd.prog.mk>
+EOF
+	$e
+	$e Generated Makefrag.inc successfully.
+	exit 0
+fi
+for file in "$srcdir"/*.opt; do
+	$e "+ Running genopt on '$file'..."
+	do_genopt "$file" || exit 1
+done
+if test $cm = combine; then
+	objs="-o $mkshexe"
+	for file in $SRCS; do
+		test -f $file || file=$srcdir/$file
+		objs="$objs $file"
+	done
+	emitbc="-fwhole-program --combine"
+	v "$CC $CFLAGS $CPPFLAGS $LDFLAGS $emitbc $objs $LIBS $ccpr"
+elif test 1 = $pm; then
+	for file in $SRCS; do
+		test -f $file || file=$srcdir/$file
+		v "$CC $CFLAGS $CPPFLAGS $emitbc $file" &
+	done
+	wait
+else
+	for file in $SRCS; do
+		test $cm = dragonegg && \
+		    op=`echo x"$file" | sed 's/^x\(.*\)\.c$/\1./'`
+		test -f $file || file=$srcdir/$file
+		v "$CC $CFLAGS $CPPFLAGS $emitbc $file" || exit 1
+		if test $cm = dragonegg; then
+			v "mv ${op}s ${op}ll"
+			v "llvm-as ${op}ll" || exit 1
+		fi
+	done
+fi
+case $cm in
+dragonegg|llvm)
+	rmf $tfn.s
+	v "llvm-link -o - $objs | opt $optflags | llc -o $tfn.s"
+	;;
+esac
+tcfn=$mkshexe
+test $cm = combine || v "$CC $CFLAGS $LDFLAGS -o $tcfn $lobjs $LIBS $ccpr"
+test -f $tcfn || exit 1
+test 1 = $r || v "$NROFF -mdoc <'$srcdir/mksh.1' >$tfn.cat1" || \
+    rmf $tfn.cat1
+test 0 = $eq && v $SIZE $tcfn
+i=install
+test -f /usr/ucb/$i && i=/usr/ucb/$i
+test 1 = $eq && e=:
+$e
+$e Installing the shell:
+$e "# $i -c -s -o root -g bin -m 555 $tfn /bin/$tfn"
+if test $legacy = 0; then
+	$e "# grep -x /bin/$tfn /etc/shells >/dev/null || echo /bin/$tfn >>/etc/shells"
+	$e "# $i -c -o root -g bin -m 444 dot.mkshrc /usr/share/doc/mksh/examples/"
+fi
+$e
+$e Installing the manual:
+if test -f $tfn.cat1; then
+	$e "# $i -c -o root -g bin -m 444 $tfn.cat1" \
+	    "/usr/share/man/cat1/$tfn.0"
+	$e or
+fi
+$e "# $i -c -o root -g bin -m 444 $tfn.1 /usr/share/man/man1/$tfn.1"
+$e
+$e Run the regression test suite: ./test.sh
+$e Please also read the sample file dot.mkshrc and the fine manual.
+exit 0
+
+: <<'EOD'
+
+=== Environment used ===
+
+==== build environment ====
+AWK				default: awk
+CC				default: cc
+CFLAGS				if empty, defaults to -xO2 or +O2
+				or -O3 -qstrict or -O2, per compiler
+CPPFLAGS			default empty
+LDFLAGS				default empty; added before sources
+LDSTATIC			set this to '-static'; default unset
+LIBS				default empty; added after sources
+				[Interix] default: -lcrypt (XXX still needed?)
+NOWARN				-Wno-error or similar
+NROFF				default: nroff
+TARGET_OS			default: $(uname -s || uname)
+TARGET_OSREV			[QNX] default: $(uname -r)
+
+==== feature selectors ====
+USE_PRINTF_BUILTIN		1 to include (unsupported) printf(1) as builtin
+===== general format =====
+HAVE_STRLEN			ac_test
+HAVE_STRING_H			ac_header
+HAVE_CAN_FSTACKPROTECTORALL	ac_flags
+
+==== cpp definitions ====
+DEBUG				dont use in production, wants gcc, implies:
+DEBUG_LEAKS			enable freeing resources before exiting
+MKSHRC_PATH			"~/.mkshrc" (do not change)
+MKSH_A4PB			force use of arc4random_pushb
+MKSH_ASSUME_UTF8		(0=disabled, 1=enabled; default: unset)
+MKSH_BINSHPOSIX			if */sh or */-sh, enable set -o posix
+MKSH_BINSHREDUCED		if */sh or */-sh, enable set -o sh
+MKSH_CLRTOEOL_STRING		"\033[K"
+MKSH_CLS_STRING			"\033[;H\033[J"
+MKSH_CONSERVATIVE_FDS		fd 0-9 for scripts, shell only up to 31
+MKSH_DEFAULT_EXECSHELL		"/bin/sh" (do not change)
+MKSH_DEFAULT_PROFILEDIR		"/etc" (do not change)
+MKSH_DEFAULT_TMPDIR		"/tmp" (do not change)
+MKSH_DISABLE_DEPRECATED		disable code paths scheduled for later removal
+MKSH_DISABLE_EXPERIMENTAL	disable code not yet comfy for (LTS) snapshots
+MKSH_DISABLE_TTY_WARNING	shut up warning about ctty if OS cant be fixed
+MKSH_DONT_EMIT_IDSTRING		omit RCS IDs from binary
+MKSH_MIDNIGHTBSD01ASH_COMPAT	set -o sh: additional compatibility quirk
+MKSH_NOPROSPECTOFWORK		disable jobs, co-processes, etc. (do not use)
+MKSH_NOPWNAM			skip PAM calls, for -static on eglibc, Solaris
+MKSH_NO_CMDLINE_EDITING		disable command line editing code entirely
+MKSH_NO_DEPRECATED_WARNING	omit warning when deprecated stuff is run
+MKSH_NO_EXTERNAL_CAT		omit hack to skip cat builtin when flags passed
+MKSH_NO_LIMITS			omit ulimit code
+MKSH_NO_SIGSETJMP		define if sigsetjmp is broken or not available
+MKSH_NO_SIGSUSPEND		use sigprocmask+pause instead of sigsuspend
+MKSH_SMALL			omit some code, optimise hard for size (slower)
+MKSH_SMALL_BUT_FAST		disable some hard-for-size optim. (modern sys.)
+MKSH_S_NOVI=1			disable Vi editing mode (default if MKSH_SMALL)
+MKSH_TYPEDEF_SIG_ATOMIC_T	define to e.g. 'int' if sig_atomic_t is missing
+MKSH_TYPEDEF_SSIZE_T		define to e.g. 'long' if your OS has no ssize_t
+MKSH_UNEMPLOYED			disable job control (but not jobs/co-processes)
+
+=== generic installation instructions ===
+
+Set CC and possibly CFLAGS, CPPFLAGS, LDFLAGS, LIBS. If cross-compiling,
+also set TARGET_OS. To disable tests, set e.g. HAVE_STRLCPY=0; to enable
+them, set to a value other than 0 or 1. Ensure /bin/ed is installed. For
+MKSH_SMALL but with Vi mode, add -DMKSH_S_NOVI=0 to CPPFLAGS as well.
+
+Normally, the following command is what you want to run, then:
+$ (sh Build.sh -r -c lto && ./test.sh -f) 2>&1 | tee log
+
+Copy dot.mkshrc to /etc/skel/.mkshrc; install mksh into $prefix/bin; or
+/bin; install the manpage, if omitting the -r flag a catmanpage is made
+using $NROFF. Consider using a forward script as /etc/skel/.mkshrc like
+https://www.mirbsd.org/cvs.cgi/contrib/hosted/tg/deb/mksh/debian/.mkshrc?rev=HEAD
+and put dot.mkshrc as /etc/mkshrc so users need not keep up their HOME.
+
+EOD

Deleted: vendor/MirOS/mksh/R50/check.pl
===================================================================
--- vendor/MirOS/mksh/dist/check.pl	2014-06-30 23:58:12 UTC (rev 6706)
+++ vendor/MirOS/mksh/R50/check.pl	2014-07-01 12:01:17 UTC (rev 6708)
@@ -1,1307 +0,0 @@
-# $MirOS: src/bin/mksh/check.pl,v 1.32 2013/07/21 18:35:56 tg Exp $
-# $OpenBSD: th,v 1.16 2013/06/14 20:52:08 millert Exp $
-#-
-# Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2011,
-#		2012, 2013
-#	Thorsten Glaser <tg at mirbsd.org>
-#
-# Provided that these terms and disclaimer and all copyright notices
-# are retained or reproduced in an accompanying document, permission
-# is granted to deal in this work without restriction, including un-
-# limited rights to use, publicly perform, distribute, sell, modify,
-# merge, give away, or sublicence.
-#
-# This work is provided "AS IS" and WITHOUT WARRANTY of any kind, to
-# the utmost extent permitted by applicable law, neither express nor
-# implied; without malicious intent or gross negligence. In no event
-# may a licensor, author or contributor be held liable for indirect,
-# direct, other damage, loss, or other issues arising in any way out
-# of dealing in the work, even if advised of the possibility of such
-# damage or existence of a defect, except proven that it results out
-# of said person's immediate fault when using the work as intended.
-#-
-# Example test:
-#		name: a-test
-#		description:
-#			a test to show how tests are done
-#		arguments: !-x!-f!
-#		stdin:
-#			echo -n *
-#			false
-#		expected-stdout: !
-#			*
-#		expected-stderr:
-#			+ echo -n *
-#			+ false
-#		expected-exit: 1
-#		---
-#	This runs the test-program (eg, mksh) with the arguments -x and -f,
-#	standard input is a file containing "echo hi*\nfalse\n". The program
-#	is expected to produce "hi*" (no trailing newline) on standard output,
-#	"+ echo hi*\n+false\n" on standard error, and an exit code of 1.
-#
-#
-# Format of test files:
-# - blank lines and lines starting with # are ignored
-# - a test file contains a series of tests
-# - a test is a series of tag:value pairs ended with a "---" line
-#   (leading/trailing spaces are stripped from the first line of value)
-# - test tags are:
-#	Tag			Flag	Description
-#	-----			----	-----------
-#	name			r	The name of the test; should be unique
-#	description		m	What test does
-#	arguments		M	Arguments to pass to the program;
-#					default is no arguments.
-#	script			m	Value is written to a file which
-#					is passed as an argument to the program
-#					(after the arguments arguments)
-#	stdin			m	Value is written to a file which is
-#					used as standard-input for the program;
-#					default is to use /dev/null.
-#	perl-setup		m	Value is a perl script which is executed
-#					just before the test is run. Try to
-#					avoid using this...
-#	perl-cleanup		m	Value is a perl script which is executed
-#					just after the test is run. Try to
-#					avoid using this...
-#	env-setup		M	Value is a list of NAME=VALUE elements
-#					which are put in the environment before
-#					the test is run. If the =VALUE is
-#					missing, NAME is removed from the
-#					environment. Programs are run with
-#					the following minimal environment:
-#					    HOME, LD_LIBRARY_PATH, LOCPATH,
-#					    LOGNAME, PATH, SHELL, UNIXMODE,
-#					    USER
-#					(values taken from the environment of
-#					the test harness).
-#					CYGWIN is set to nodosfilewarning.
-#					ENV is set to /nonexistant.
-#					__progname is set to the -p argument.
-#					__perlname is set to $^X (perlexe).
-#	file-setup		mps	Used to create files, directories
-#					and symlinks. First word is either
-#					file, dir or symlink; second word is
-#					permissions; this is followed by a
-#					quoted word that is the name of the
-#					file; the end-quote should be followed
-#					by a newline, then the file data
-#					(if any). The first word may be
-#					preceded by a ! to strip the trailing
-#					newline in a symlink.
-#	file-result		mps	Used to verify a file, symlink or
-#					directory is created correctly.
-#					The first word is either
-#					file, dir or symlink; second word is
-#					expected permissions; third word
-#					is user-id; fourth is group-id;
-#					fifth is "exact" or "pattern"
-#					indicating whether the file contents
-#					which follow is to be matched exactly
-#					or if it is a regular expression.
-#					The fifth argument is the quoted name
-#					of the file that should be created.
-#					The end-quote should be followed
-#					by a newline, then the file data
-#					(if any). The first word may be
-#					preceded by a ! to strip the trailing
-#					newline in the file contents.
-#					The permissions, user and group fields
-#					may be * meaning accept any value.
-#	time-limit			Time limit - the program is sent a
-#					SIGKILL N seconds. Default is no
-#					limit.
-#	expected-fail			'yes' if the test is expected to fail.
-#	expected-exit			expected exit code. Can be a number,
-#					or a C expression using the variables
-#					e, s and w (exit code, termination
-#					signal, and status code).
-#	expected-stdout		m	What the test should generate on stdout;
-#					default is to expect no output.
-#	expected-stdout-pattern	m	A perl pattern which matches the
-#					expected output.
-#	expected-stderr		m	What the test should generate on stderr;
-#					default is to expect no output.
-#	expected-stderr-pattern	m	A perl pattern which matches the
-#					expected standard error.
-#	category		m	Specify a comma separated list of
-#					'categories' of program that the test
-#					is to be run for. A category can be
-#					negated by prefixing the name with a !.
-#					The idea is that some tests in a
-#					test suite may apply to a particular
-#					program version and shouldn't be run
-#					on other versions. The category(s) of
-#					the program being tested can be
-#					specified on the command line.
-#					One category os:XXX is predefined
-#					(XXX is the operating system name,
-#					eg, linux, dec_osf).
-#	need-ctty			'yes' if the test needs a ctty, run
-#					with -C regress:no-ctty to disable.
-# Flag meanings:
-#	r	tag is required (eg, a test must have a name tag).
-#	m	value can be multiple lines. Lines must be prefixed with
-#		a tab. If the value part of the initial tag:value line is
-#			- empty: the initial blank line is stripped.
-#			- a lone !: the last newline in the value is stripped;
-#	M	value can be multiple lines (prefixed by a tab) and consists
-#		of multiple fields, delimited by a field separator character.
-#		The value must start and end with the f-s-c.
-#	p	tag takes parameters (used with m).
-#	s	tag can be used several times.
-
-# pull EINTR from POSIX.pm or Errno.pm if they exist
-# otherwise just skip it
-BEGIN {
-	$EINTR = 0;
-	eval {
-		require POSIX;
-		$EINTR = POSIX::EINTR();
-	};
-	if ($@) {
-		eval {
-			require Errno;
-			$EINTR = Errno::EINTR();
-		} or do {
-			$EINTR = 0;
-		};
-	}
-};
-
-use Getopt::Std;
-use Config;
-use File::Temp qw/ :mktemp /;
-
-$os = defined $^O ? $^O : 'unknown';
-
-($prog = $0) =~ s#.*/##;
-
-$Usage = <<EOF ;
-Usage: $prog [-Pv] [-C cat] [-e e=v] [-p prog] [-s fn] [-T dir] \
-       [-t tmo] name ...
-	-C c	Specify the comma separated list of categories the program
-		belongs to (see category field).
-	-e e=v	Set the environment variable e to v for all tests
-		(if no =v is given, the current value is used)
-		Only one -e option can be given at the moment, sadly.
-	-P	program (-p) string has multiple words, and the program is in
-		the path (kludge option)
-	-p p	Use p as the program to test
-	-s s	Read tests from file s; if s is a directory, it is recursively
-		scaned for test files (which end in .t).
-	-T dir	Use dir instead of /tmp to hold temporary files
-	-t t	Use t as default time limit for tests (default is unlimited)
-	-v	Verbose mode: print reason test failed.
-	name	specifies the name of the test(s) to run; if none are
-		specified, all tests are run.
-EOF
-
-# See comment above for flag meanings
-%test_fields = (
-	'name',				'r',
-	'description',			'm',
-	'arguments',			'M',
-	'script',			'm',
-	'stdin',			'm',
-	'perl-setup',			'm',
-	'perl-cleanup',			'm',
-	'env-setup',			'M',
-	'file-setup',			'mps',
-	'file-result',			'mps',
-	'time-limit',			'',
-	'expected-fail',		'',
-	'expected-exit',		'',
-	'expected-stdout',		'm',
-	'expected-stdout-pattern',	'm',
-	'expected-stderr',		'm',
-	'expected-stderr-pattern',	'm',
-	'category',			'm',
-	'need-ctty',			'',
-	'need-pass',			'',
-	);
-# Filled in by read_test()
-%internal_test_fields = (
-	':full-name', 1,		# file:name
-	':long-name', 1,		# dir/file:lineno:name
-	);
-
-# Categories of the program under test. Provide the current
-# os by default.
-%categories = (
-	"os:$os", '1'
-	);
-
-$nfailed = 0;
-$nifailed = 0;
-$nxfailed = 0;
-$npassed = 0;
-$nxpassed = 0;
-
-%known_tests = ();
-
-if (!getopts('C:e:Pp:s:T:t:v')) {
-    print STDERR $Usage;
-    exit 1;
-}
-
-die "$prog: no program specified (use -p)\n" if !defined $opt_p;
-die "$prog: no test set specified (use -s)\n" if !defined $opt_s;
-$test_prog = $opt_p;
-$verbose = defined $opt_v && $opt_v;
-$test_set = $opt_s;
-$temp_dir = $opt_T || "/tmp";
-if (defined $opt_t) {
-    die "$prog: bad -t argument (should be number > 0): $opt_t\n"
-	if $opt_t !~ /^\d+$/ || $opt_t <= 0;
-    $default_time_limit = $opt_t;
-}
-$program_kludge = defined $opt_P ? $opt_P : 0;
-
-if (defined $opt_C) {
-    foreach $c (split(',', $opt_C)) {
-	$c =~ s/\s+//;
-	die "$prog: categories can't be negated on the command line\n"
-	    if ($c =~ /^!/);
-	$categories{$c} = 1;
-    }
-}
-
-# Note which tests are to be run.
-%do_test = ();
-grep($do_test{$_} = 1, @ARGV);
-$all_tests = @ARGV == 0;
-
-# Set up a very minimal environment
-%new_env = ();
-foreach $env (('HOME', 'LD_LIBRARY_PATH', 'LOCPATH', 'LOGNAME',
-  'PATH', 'SHELL', 'UNIXMODE', 'USER')) {
-    $new_env{$env} = $ENV{$env} if defined $ENV{$env};
-}
-$new_env{'CYGWIN'} = 'nodosfilewarning';
-$new_env{'ENV'} = '/nonexistant';
-if (($os eq 'VMS') || ($Config{perlpath} =~ m/$Config{_exe}$/i)) {
-	$new_env{'__perlname'} = $Config{perlpath};
-} else {
-	$new_env{'__perlname'} = $Config{perlpath} . $Config{_exe};
-}
-if (defined $opt_e) {
-    # XXX need a way to allow many -e arguments...
-    if ($opt_e =~ /^([a-zA-Z_]\w*)(|=(.*))$/) {
-	$new_env{$1} = $2 eq '' ? $ENV{$1} : $3;
-    } else {
-	die "$0: bad -e argument: $opt_e\n";
-    }
-}
-%old_env = %ENV;
-
-chop($pwd = `pwd 2>/dev/null`);
-die "$prog: couldn't get current working directory\n" if $pwd eq '';
-die "$prog: couldn't cd to $pwd - $!\n" if !chdir($pwd);
-
-if (!$program_kludge) {
-    $test_prog = "$pwd/$test_prog" if substr($test_prog, 0, 1) ne '/';
-    die "$prog: $test_prog is not executable - bye\n"
-	if (! -x $test_prog && $os ne 'os2');
-}
-
- at trap_sigs = ('TERM', 'QUIT', 'INT', 'PIPE', 'HUP');
- at SIG{@trap_sigs} = ('cleanup_exit') x @trap_sigs;
-$child_kill_ok = 0;
-$SIG{'ALRM'} = 'catch_sigalrm';
-
-$| = 1;
-
-# Create temp files
-($fh, $temps) = mkstemp("${temp_dir}/rts.XXXXXXXX");
-close($fh);
-($fh, $tempi) = mkstemp("${temp_dir}/rti.XXXXXXXX");
-close($fh);
-($fh, $tempo) = mkstemp("${temp_dir}/rto.XXXXXXXX");
-close($fh);
-($fh, $tempe) = mkstemp("${temp_dir}/rte.XXXXXXXX");
-close($fh);
-$tempdir = mkdtemp("${temp_dir}/rtd.XXXXXXXX");
-
-if (-d $test_set) {
-    $file_prefix_skip = length($test_set) + 1;
-    $ret = &process_test_dir($test_set);
-} else {
-    $file_prefix_skip = 0;
-    $ret = &process_test_file($test_set);
-}
-&cleanup_exit() if !defined $ret;
-
-$tot_failed = $nfailed + $nifailed + $nxfailed;
-$tot_passed = $npassed + $nxpassed;
-if ($tot_failed || $tot_passed) {
-    print "Total failed: $tot_failed";
-    print " ($nifailed ignored)" if $nifailed;
-    print " ($nxfailed unexpected)" if $nxfailed;
-    print " (as expected)" if $nfailed && !$nxfailed && !$nifailed;
-    print "\nTotal passed: $tot_passed";
-    print " ($nxpassed unexpected)" if $nxpassed;
-    print "\n";
-}
-
-&cleanup_exit('ok');
-
-sub
-cleanup_exit
-{
-    local($sig, $exitcode) = ('', 1);
-
-    if ($_[0] eq 'ok') {
-	unless ($nxfailed) {
-		$exitcode = 0;
-	} else {
-		$exitcode = 1;
-	}
-    } elsif ($_[0] ne '') {
-	$sig = $_[0];
-    }
-
-    unlink($tempi, $tempo, $tempe, $temps);
-    &scrub_dir($tempdir) if defined $tempdir;
-    rmdir($tempdir) if defined $tempdir;
-
-    if ($sig) {
-	$SIG{$sig} = 'DEFAULT';
-	kill $sig, $$;
-	return;
-    }
-    exit $exitcode;
-}
-
-sub
-catch_sigalrm
-{
-    $SIG{'ALRM'} = 'catch_sigalrm';
-    kill(9, $child_pid) if $child_kill_ok;
-    $child_killed = 1;
-}
-
-sub
-process_test_dir
-{
-    local($dir) = @_;
-    local($ret, $file);
-    local(@todo) = ();
-
-    if (!opendir(DIR, $dir)) {
-	print STDERR "$prog: can't open directory $dir - $!\n";
-	return undef;
-    }
-    while (defined ($file = readdir(DIR))) {
-	push(@todo, $file) if $file =~ /^[^.].*\.t$/;
-    }
-    closedir(DIR);
-
-    foreach $file (@todo) {
-	$file = "$dir/$file";
-	if (-d $file) {
-	    $ret = &process_test_dir($file);
-	} elsif (-f _) {
-	    $ret = &process_test_file($file);
-	}
-	last if !defined $ret;
-    }
-
-    return $ret;
-}
-
-sub
-process_test_file
-{
-    local($file) = @_;
-    local($ret);
-
-    if (!open(IN, $file)) {
-	print STDERR "$prog: can't open $file - $!\n";
-	return undef;
-    }
-    binmode(IN);
-    while (1) {
-	$ret = &read_test($file, IN, *test);
-	last if !defined $ret || !$ret;
-	next if !$all_tests && !$do_test{$test{'name'}};
-	next if !&category_check(*test);
-	$ret = &run_test(*test);
-	last if !defined $ret;
-    }
-    close(IN);
-
-    return $ret;
-}
-
-sub
-run_test
-{
-    local(*test) = @_;
-    local($name) = $test{':full-name'};
-
-    return undef if !&scrub_dir($tempdir);
-
-    if (defined $test{'stdin'}) {
-	return undef if !&write_file($tempi, $test{'stdin'});
-	$ifile = $tempi;
-    } else {
-	$ifile = '/dev/null';
-    }
-
-    if (defined $test{'script'}) {
-	return undef if !&write_file($temps, $test{'script'});
-    }
-
-    if (!chdir($tempdir)) {
-	print STDERR "$prog: couldn't cd to $tempdir - $!\n";
-	return undef;
-    }
-
-    if (defined $test{'file-setup'}) {
-	local($i);
-	local($type, $perm, $rest, $c, $len, $name);
-
-	for ($i = 0; $i < $test{'file-setup'}; $i++) {
-	    $val = $test{"file-setup:$i"};
-
-	    # format is: type perm "name"
-	    ($type, $perm, $rest) =
-		split(' ', $val, 3);
-	    $c = substr($rest, 0, 1);
-	    $len = index($rest, $c, 1) - 1;
-	    $name = substr($rest, 1, $len);
-	    $rest = substr($rest, 2 + $len);
-	    $perm = oct($perm) if $perm =~ /^\d+$/;
-	    if ($type eq 'file') {
-		return undef if !&write_file($name, $rest);
-		if (!chmod($perm, $name)) {
-		    print STDERR
-		  "$prog:$test{':long-name'}: can't chmod $perm $name - $!\n";
-		    return undef;
-		}
-	    } elsif ($type eq 'dir') {
-		if (!mkdir($name, $perm)) {
-		    print STDERR
-		  "$prog:$test{':long-name'}: can't mkdir $perm $name - $!\n";
-		    return undef;
-		}
-	    } elsif ($type eq 'symlink') {
-		local($oumask) = umask($perm);
-		local($ret) = symlink($rest, $name);
-		umask($oumask);
-		if (!$ret) {
-		    print STDERR
-	    "$prog:$test{':long-name'}: couldn't create symlink $name - $!\n";
-		    return undef;
-		}
-	    }
-	}
-    }
-
-    if (defined $test{'perl-setup'}) {
-	eval $test{'perl-setup'};
-	if ($@ ne '') {
-	    print STDERR "$prog:$test{':long-name'}: error running perl-setup - $@\n";
-	    return undef;
-	}
-    }
-
-    $pid = fork;
-    if (!defined $pid) {
-	print STDERR "$prog: can't fork - $!\n";
-	return undef;
-    }
-    if (!$pid) {
-	@SIG{@trap_sigs} = ('DEFAULT') x @trap_sigs;
-	$SIG{'ALRM'} = 'DEFAULT';
-	if (defined $test{'env-setup'}) {
-	    local($var, $val, $i);
-
-	    foreach $var (split(substr($test{'env-setup'}, 0, 1),
-		$test{'env-setup'}))
-	    {
-		$i = index($var, '=');
-		next if $i == 0 || $var eq '';
-		if ($i < 0) {
-		    delete $new_env{$var};
-		} else {
-		    $new_env{substr($var, 0, $i)} = substr($var, $i + 1);
-		}
-	    }
-	}
-	if (!open(STDIN, "< $ifile")) {
-		print STDERR "$prog: couldn't open $ifile in child - $!\n";
-		kill('TERM', $$);
-	}
-	binmode(STDIN);
-	if (!open(STDOUT, "> $tempo")) {
-		print STDERR "$prog: couldn't open $tempo in child - $!\n";
-		kill('TERM', $$);
-	}
-	binmode(STDOUT);
-	if (!open(STDERR, "> $tempe")) {
-		print STDOUT "$prog: couldn't open $tempe in child - $!\n";
-		kill('TERM', $$);
-	}
-	binmode(STDERR);
-	if ($program_kludge) {
-	    @argv = split(' ', $test_prog);
-	} else {
-	    @argv = ($test_prog);
-	}
-	if (defined $test{'arguments'}) {
-		push(@argv,
-		     split(substr($test{'arguments'}, 0, 1),
-			   substr($test{'arguments'}, 1)));
-	}
-	push(@argv, $temps) if defined $test{'script'};
-
-	#XXX realpathise, use which/whence -p, or sth. like that
-	#XXX if !$program_kludge, we get by with not doing it for now tho
-	$new_env{'__progname'} = $argv[0];
-
-	# The following doesn't work with perl5...  Need to do it explicitly - yuck.
-	#%ENV = %new_env;
-	foreach $k (keys(%ENV)) {
-	    delete $ENV{$k};
-	}
-	$ENV{$k} = $v while ($k,$v) = each %new_env;
-
-	exec { $argv[0] } @argv;
-	print STDERR "$prog: couldn't execute $test_prog - $!\n";
-	kill('TERM', $$);
-	exit(95);
-    }
-    $child_pid = $pid;
-    $child_killed = 0;
-    $child_kill_ok = 1;
-    alarm($test{'time-limit'}) if defined $test{'time-limit'};
-    while (1) {
-	$xpid = waitpid($pid, 0);
-	$child_kill_ok = 0;
-	if ($xpid < 0) {
-	    if ($EINTR) {
-		next if $! == $EINTR;
-	    }
-	    print STDERR "$prog: error waiting for child - $!\n";
-	    return undef;
-	}
-	last;
-    }
-    $status = $?;
-    alarm(0) if defined $test{'time-limit'};
-
-    $failed = 0;
-    $why = '';
-
-    if ($child_killed) {
-	$failed = 1;
-	$why .= "\ttest timed out (limit of $test{'time-limit'} seconds)\n";
-    }
-
-    $ret = &eval_exit($test{'long-name'}, $status, $test{'expected-exit'});
-    return undef if !defined $ret;
-    if (!$ret) {
-	local($expl);
-
-	$failed = 1;
-	if (($status & 0xff) == 0x7f) {
-	    $expl = "stopped";
-	} elsif (($status & 0xff)) {
-	    $expl = "signal " . ($status & 0x7f);
-	} else {
-	    $expl = "exit-code " . (($status >> 8) & 0xff);
-	}
-	$why .=
-	"\tunexpected exit status $status ($expl), expected $test{'expected-exit'}\n";
-    }
-
-    $tmp = &check_output($test{'long-name'}, $tempo, 'stdout',
-		$test{'expected-stdout'}, $test{'expected-stdout-pattern'});
-    return undef if !defined $tmp;
-    if ($tmp ne '') {
-	$failed = 1;
-	$why .= $tmp;
-    }
-
-    $tmp = &check_output($test{'long-name'}, $tempe, 'stderr',
-		$test{'expected-stderr'}, $test{'expected-stderr-pattern'});
-    return undef if !defined $tmp;
-    if ($tmp ne '') {
-	$failed = 1;
-	$why .= $tmp;
-    }
-
-    $tmp = &check_file_result(*test);
-    return undef if !defined $tmp;
-    if ($tmp ne '') {
-	$failed = 1;
-	$why .= $tmp;
-    }
-
-    if (defined $test{'perl-cleanup'}) {
-	eval $test{'perl-cleanup'};
-	if ($@ ne '') {
-	    print STDERR "$prog:$test{':long-name'}: error running perl-cleanup - $@\n";
-	    return undef;
-	}
-    }
-
-    if (!chdir($pwd)) {
-	print STDERR "$prog: couldn't cd to $pwd - $!\n";
-	return undef;
-    }
-
-    if ($failed) {
-	if (!$test{'expected-fail'}) {
-	    if ($test{'need-pass'}) {
-		print "FAIL $name\n";
-		$nxfailed++;
-	    } else {
-		print "FAIL $name (ignored)\n";
-		$nifailed++;
-	    }
-	} else {
-	    print "fail $name (as expected)\n";
-	    $nfailed++;
-	}
-	$why = "\tDescription"
-		. &wrap_lines($test{'description'}, " (missing)\n")
-		. $why;
-    } elsif ($test{'expected-fail'}) {
-	print "PASS $name (unexpectedly)\n";
-	$nxpassed++;
-    } else {
-	print "pass $name\n";
-	$npassed++;
-    }
-    print $why if $verbose;
-    return 0;
-}
-
-sub
-category_check
-{
-    local(*test) = @_;
-    local($c);
-
-    return 0 if ($test{'need-ctty'} && defined $categories{'regress:no-ctty'});
-    return 1 if (!defined $test{'category'});
-    local($ok) = 0;
-    foreach $c (split(',', $test{'category'})) {
-	$c =~ s/\s+//;
-	if ($c =~ /^!/) {
-	    $c = $';
-	    return 0 if (defined $categories{$c});
-	    $ok = 1;
-	} else {
-	    $ok = 1 if (defined $categories{$c});
-	}
-    }
-    return $ok;
-}
-
-sub
-scrub_dir
-{
-    local($dir) = @_;
-    local(@todo) = ();
-    local($file);
-
-    if (!opendir(DIR, $dir)) {
-	print STDERR "$prog: couldn't open directory $dir - $!\n";
-	return undef;
-    }
-    while (defined ($file = readdir(DIR))) {
-	push(@todo, $file) if $file ne '.' && $file ne '..';
-    }
-    closedir(DIR);
-    foreach $file (@todo) {
-	$file = "$dir/$file";
-	if (-d $file) {
-	    return undef if !&scrub_dir($file);
-	    if (!rmdir($file)) {
-		print STDERR "$prog: couldn't rmdir $file - $!\n";
-		return undef;
-	    }
-	} else {
-	    if (!unlink($file)) {
-		print STDERR "$prog: couldn't unlink $file - $!\n";
-		return undef;
-	    }
-	}
-    }
-    return 1;
-}
-
-sub
-write_file
-{
-    local($file, $str) = @_;
-
-    if (!open(TEMP, "> $file")) {
-	print STDERR "$prog: can't open $file - $!\n";
-	return undef;
-    }
-    binmode(TEMP);
-    print TEMP $str;
-    if (!close(TEMP)) {
-	print STDERR "$prog: error writing $file - $!\n";
-	return undef;
-    }
-    return 1;
-}
-
-sub
-check_output
-{
-    local($name, $file, $what, $expect, $expect_pat) = @_;
-    local($got) = '';
-    local($why) = '';
-    local($ret);
-
-    if (!open(TEMP, "< $file")) {
-	print STDERR "$prog:$name($what): couldn't open $file after running program - $!\n";
-	return undef;
-    }
-    binmode(TEMP);
-    while (<TEMP>) {
-	$got .= $_;
-    }
-    close(TEMP);
-    return compare_output($name, $what, $expect, $expect_pat, $got);
-}
-
-sub
-compare_output
-{
-    local($name, $what, $expect, $expect_pat, $got) = @_;
-    local($why) = '';
-
-    if (defined $expect_pat) {
-	$_ = $got;
-	$ret = eval "$expect_pat";
-	if ($@ ne '') {
-	    print STDERR "$prog:$name($what): error evaluating $what pattern: $expect_pat - $@\n";
-	    return undef;
-	}
-	if (!$ret) {
-	    $why = "\tunexpected $what - wanted pattern";
-	    $why .= &wrap_lines($expect_pat);
-	    $why .= "\tgot";
-	    $why .= &wrap_lines($got);
-	}
-    } else {
-	$expect = '' if !defined $expect;
-	if ($got ne $expect) {
-	    $why .= "\tunexpected $what - " . &first_diff($expect, $got) . "\n";
-	    $why .= "\twanted";
-	    $why .= &wrap_lines($expect);
-	    $why .= "\tgot";
-	    $why .= &wrap_lines($got);
-	}
-    }
-    return $why;
-}
-
-sub
-wrap_lines
-{
-    local($str, $empty) = @_;
-    local($nonl) = substr($str, -1, 1) ne "\n";
-
-    return (defined $empty ? $empty : " nothing\n") if $str eq '';
-    substr($str, 0, 0) = ":\n";
-    $str =~ s/\n/\n\t\t/g;
-    if ($nonl) {
-	$str .= "\n\t[incomplete last line]\n";
-    } else {
-	chop($str);
-	chop($str);
-    }
-    return $str;
-}
-
-sub
-first_diff
-{
-    local($exp, $got) = @_;
-    local($lineno, $char) = (1, 1);
-    local($i, $exp_len, $got_len);
-    local($ce, $cg);
-
-    $exp_len = length($exp);
-    $got_len = length($got);
-    if ($exp_len != $got_len) {
-	if ($exp_len < $got_len) {
-	    if (substr($got, 0, $exp_len) eq $exp) {
-		return "got too much output";
-	    }
-	} elsif (substr($exp, 0, $got_len) eq $got) {
-	    return "got too little output";
-	}
-    }
-    for ($i = 0; $i < $exp_len; $i++) {
-	$ce = substr($exp, $i, 1);
-	$cg = substr($got, $i, 1);
-	last if $ce ne $cg;
-	$char++;
-	if ($ce eq "\n") {
-	    $lineno++;
-	    $char = 1;
-	}
-    }
-    return "first difference: line $lineno, char $char (wanted '"
-	. &format_char($ce) . "', got '"
-	. &format_char($cg) . "'";
-}
-
-sub
-format_char
-{
-    local($ch, $s);
-
-    $ch = ord($_[0]);
-    if ($ch == 10) {
-	return '\n';
-    } elsif ($ch == 13) {
-	return '\r';
-    } elsif ($ch == 8) {
-	return '\b';
-    } elsif ($ch == 9) {
-	return '\t';
-    } elsif ($ch > 127) {
-	$ch -= 127;
-	$s = "M-";
-    } else {
-	$s = '';
-    }
-    if ($ch < 32) {
-	$s .= '^';
-	$ch += ord('@');
-    } elsif ($ch == 127) {
-	return $s . "^?";
-    }
-    return $s . sprintf("%c", $ch);
-}
-
-sub
-eval_exit
-{
-    local($name, $status, $expect) = @_;
-    local($expr);
-    local($w, $e, $s) = ($status, ($status >> 8) & 0xff, $status & 0x7f);
-
-    $e = -1000 if $status & 0xff;
-    $s = -1000 if $s == 0x7f;
-    if (!defined $expect) {
-	$expr = '$w == 0';
-    } elsif ($expect =~ /^(|-)\d+$/) {
-	$expr = "\$e == $expect";
-    } else {
-	$expr = $expect;
-	$expr =~ s/\b([wse])\b/\$$1/g;
-	$expr =~ s/\b(SIG[A-Z0-9]+)\b/&$1/g;
-    }
-    $w = eval $expr;
-    if ($@ ne '') {
-	print STDERR "$prog:$test{':long-name'}: bad expected-exit expression: $expect ($@)\n";
-	return undef;
-    }
-    return $w;
-}
-
-sub
-read_test
-{
-    local($file, $in, *test) = @_;
-    local($field, $val, $flags, $do_chop, $need_redo, $start_lineno);
-    local(%cnt, $sfield);
-
-    %test = ();
-    %cnt = ();
-    while (<$in>) {
-	next if /^\s*$/;
-	next if /^ *#/;
-	last if /^\s*---\s*$/;
-	$start_lineno = $. if !defined $start_lineno;
-	if (!/^([-\w]+):\s*(|\S|\S.*\S)\s*$/) {
-	    print STDERR "$prog:$file:$.: unrecognised line\n";
-	    return undef;
-	}
-	($field, $val) = ($1, $2);
-	$sfield = $field;
-	$flags = $test_fields{$field};
-	if (!defined $flags) {
-	    print STDERR "$prog:$file:$.: unrecognised field \"$field\"\n";
-	    return undef;
-	}
-	if ($flags =~ /s/) {
-	    local($cnt) = $cnt{$field}++;
-	    $test{$field} = $cnt{$field};
-	    $cnt = 0 if $cnt eq '';
-	    $sfield .= ":$cnt";
-	} elsif (defined $test{$field}) {
-	    print STDERR "$prog:$file:$.: multiple \"$field\" fields\n";
-	    return undef;
-	}
-	$do_chop = $flags !~ /m/;
-	$need_redo = 0;
-	if ($val eq '' || $val eq '!' || $flags =~ /p/) {
-	    if ($flags =~ /[Mm]/) {
-		if ($flags =~ /p/) {
-		    if ($val =~ /^!/) {
-			$do_chop = 1;
-			$val = $';
-		    } else {
-			$do_chop = 0;
-		    }
-		    if ($val eq '') {
-			print STDERR
-		"$prog:$file:$.: no parameters given for field \"$field\"\n";
-			return undef;
-		    }
-		} else {
-		    if ($val eq '!') {
-			$do_chop = 1;
-		    }
-		    $val = '';
-		}
-		while (<$in>) {
-		    last if !/^\t/;
-		    $val .= $';
-		}
-		chop $val if $do_chop;
-		$do_chop = 1;
-		$need_redo = 1;
-
-		# Syntax check on fields that can several instances
-		# (can give useful line numbers this way)
-
-		if ($field eq 'file-setup') {
-		    local($type, $perm, $rest, $c, $len, $name);
-
-		    # format is: type perm "name"
-		    if ($val !~ /^[ \t]*(\S+)[ \t]+(\S+)[ \t]+([^ \t].*)/) {
-			print STDERR
-		    "$prog:$file:$.: bad parameter line for file-setup field\n";
-			return undef;
-		    }
-		    ($type, $perm, $rest) = ($1, $2, $3);
-		    if ($type !~ /^(file|dir|symlink)$/) {
-			print STDERR
-		    "$prog:$file:$.: bad file type for file-setup: $type\n";
-			return undef;
-		    }
-		    if ($perm !~ /^\d+$/) {
-			print STDERR
-		    "$prog:$file:$.: bad permissions for file-setup: $type\n";
-			return undef;
-		    }
-		    $c = substr($rest, 0, 1);
-		    if (($len = index($rest, $c, 1) - 1) <= 0) {
-			print STDERR
-    "$prog:$file:$.: missing end quote for file name in file-setup: $rest\n";
-			return undef;
-		    }
-		    $name = substr($rest, 1, $len);
-		    if ($name =~ /^\// || $name =~ /(^|\/)\.\.(\/|$)/) {
-			# Note: this is not a security thing - just a sanity
-			# check - a test can still use symlinks to get at files
-			# outside the test directory.
-			print STDERR
-"$prog:$file:$.: file name in file-setup is absolute or contains ..: $name\n";
-			return undef;
-		    }
-		}
-		if ($field eq 'file-result') {
-		    local($type, $perm, $uid, $gid, $matchType,
-			  $rest, $c, $len, $name);
-
-		    # format is: type perm uid gid matchType "name"
-		    if ($val !~ /^\s*(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S.*)/) {
-			print STDERR
-		    "$prog:$file:$.: bad parameter line for file-result field\n";
-			return undef;
-		    }
-		    ($type, $perm, $uid, $gid, $matchType, $rest)
-			= ($1, $2, $3, $4, $5, $6);
-		    if ($type !~ /^(file|dir|symlink)$/) {
-			print STDERR
-		    "$prog:$file:$.: bad file type for file-result: $type\n";
-			return undef;
-		    }
-		    if ($perm !~ /^\d+$/ && $perm ne '*') {
-			print STDERR
-		    "$prog:$file:$.: bad permissions for file-result: $perm\n";
-			return undef;
-		    }
-		    if ($uid !~ /^\d+$/ && $uid ne '*') {
-			print STDERR
-		    "$prog:$file:$.: bad user-id for file-result: $uid\n";
-			return undef;
-		    }
-		    if ($gid !~ /^\d+$/ && $gid ne '*') {
-			print STDERR
-		    "$prog:$file:$.: bad group-id for file-result: $gid\n";
-			return undef;
-		    }
-		    if ($matchType !~ /^(exact|pattern)$/) {
-			print STDERR
-		"$prog:$file:$.: bad match type for file-result: $matchType\n";
-			return undef;
-		    }
-		    $c = substr($rest, 0, 1);
-		    if (($len = index($rest, $c, 1) - 1) <= 0) {
-			print STDERR
-    "$prog:$file:$.: missing end quote for file name in file-result: $rest\n";
-			return undef;
-		    }
-		    $name = substr($rest, 1, $len);
-		    if ($name =~ /^\// || $name =~ /(^|\/)\.\.(\/|$)/) {
-			# Note: this is not a security thing - just a sanity
-			# check - a test can still use symlinks to get at files
-			# outside the test directory.
-			print STDERR
-"$prog:$file:$.: file name in file-result is absolute or contains ..: $name\n";
-			return undef;
-		    }
-		}
-	    } elsif ($val eq '') {
-		print STDERR
-		    "$prog:$file:$.: no value given for field \"$field\"\n";
-		return undef;
-	    }
-	}
-	$val .= "\n" if !$do_chop;
-	$test{$sfield} = $val;
-	redo if $need_redo;
-    }
-    if ($_ eq '') {
-	if (%test) {
-	    print STDERR
-	      "$prog:$file:$start_lineno: end-of-file while reading test\n";
-	    return undef;
-	}
-	return 0;
-    }
-
-    while (($field, $val) = each %test_fields) {
-	if ($val =~ /r/ && !defined $test{$field}) {
-	    print STDERR
-	      "$prog:$file:$start_lineno: required field \"$field\" missing\n";
-	    return undef;
-	}
-    }
-
-    $test{':full-name'} = substr($file, $file_prefix_skip) . ":$test{'name'}";
-    $test{':long-name'} = "$file:$start_lineno:$test{'name'}";
-
-    # Syntax check on specific fields
-    if (defined $test{'expected-fail'}) {
-	if ($test{'expected-fail'} !~ /^(yes|no)$/) {
-	    print STDERR
-	      "$prog:$test{':long-name'}: bad value for expected-fail field\n";
-	    return undef;
-	}
-	$test{'expected-fail'} = $1 eq 'yes';
-    } else {
-	$test{'expected-fail'} = 0;
-    }
-    if (defined $test{'need-ctty'}) {
-	if ($test{'need-ctty'} !~ /^(yes|no)$/) {
-	    print STDERR
-	      "$prog:$test{':long-name'}: bad value for need-ctty field\n";
-	    return undef;
-	}
-	$test{'need-ctty'} = $1 eq 'yes';
-    } else {
-	$test{'need-ctty'} = 0;
-    }
-    if (defined $test{'need-pass'}) {
-	if ($test{'need-pass'} !~ /^(yes|no)$/) {
-	    print STDERR
-	      "$prog:$test{':long-name'}: bad value for need-pass field\n";
-	    return undef;
-	}
-	$test{'need-pass'} = $1 eq 'yes';
-    } else {
-	$test{'need-pass'} = 1;
-    }
-    if (defined $test{'arguments'}) {
-	local($firstc) = substr($test{'arguments'}, 0, 1);
-
-	if (substr($test{'arguments'}, -1, 1) ne $firstc) {
-	    print STDERR "$prog:$test{':long-name'}: arguments field doesn't start and end with the same character\n";
-	    return undef;
-	}
-    }
-    if (defined $test{'env-setup'}) {
-	local($firstc) = substr($test{'env-setup'}, 0, 1);
-
-	if (substr($test{'env-setup'}, -1, 1) ne $firstc) {
-	    print STDERR "$prog:$test{':long-name'}: env-setup field doesn't start and end with the same character\n";
-	    return undef;
-	}
-    }
-    if (defined $test{'expected-exit'}) {
-	local($val) = $test{'expected-exit'};
-
-	if ($val =~ /^(|-)\d+$/) {
-	    if ($val < 0 || $val > 255) {
-		print STDERR "$prog:$test{':long-name'}: expected-exit value $val not in 0..255\n";
-		return undef;
-	    }
-	} elsif ($val !~ /^([\s<>+-=*%\/&|!()]|\b[wse]\b|\bSIG[A-Z0-9]+\b)+$/) {
-	    print STDERR "$prog:$test{':long-name'}: bad expected-exit expression: $val\n";
-	    return undef;
-	}
-    } else {
-	$test{'expected-exit'} = 0;
-    }
-    if (defined $test{'expected-stdout'}
-	&& defined $test{'expected-stdout-pattern'})
-    {
-	print STDERR "$prog:$test{':long-name'}: can't use both expected-stdout and expected-stdout-pattern\n";
-	return undef;
-    }
-    if (defined $test{'expected-stderr'}
-	&& defined $test{'expected-stderr-pattern'})
-    {
-	print STDERR "$prog:$test{':long-name'}: can't use both expected-stderr and expected-stderr-pattern\n";
-	return undef;
-    }
-    if (defined $test{'time-limit'}) {
-	if ($test{'time-limit'} !~ /^\d+$/ || $test{'time-limit'} == 0) {
-	    print STDERR
-	      "$prog:$test{':long-name'}: bad value for time-limit field\n";
-	    return undef;
-	}
-    } elsif (defined $default_time_limit) {
-	$test{'time-limit'} = $default_time_limit;
-    }
-
-    if (defined $known_tests{$test{'name'}}) {
-	print STDERR "$prog:$test{':long-name'}: warning: duplicate test name ${test{'name'}}\n";
-    }
-    $known_tests{$test{'name'}} = 1;
-
-    return 1;
-}
-
-sub
-tty_msg
-{
-    local($msg) = @_;
-
-    open(TTY, "> /dev/tty") || return 0;
-    print TTY $msg;
-    close(TTY);
-    return 1;
-}
-
-sub
-never_called_funcs
-{
-	return 0;
-	&tty_msg("hi\n");
-	&never_called_funcs();
-	&catch_sigalrm();
-	$old_env{'foo'} = 'bar';
-	$internal_test_fields{'foo'} = 'bar';
-}
-
-sub
-check_file_result
-{
-    local(*test) = @_;
-
-    return '' if (!defined $test{'file-result'});
-
-    local($why) = '';
-    local($i);
-    local($type, $perm, $uid, $gid, $rest, $c, $len, $name);
-    local(@stbuf);
-
-    for ($i = 0; $i < $test{'file-result'}; $i++) {
-	$val = $test{"file-result:$i"};
-
-	# format is: type perm "name"
-	($type, $perm, $uid, $gid, $matchType, $rest) =
-	    split(' ', $val, 6);
-	$c = substr($rest, 0, 1);
-	$len = index($rest, $c, 1) - 1;
-	$name = substr($rest, 1, $len);
-	$rest = substr($rest, 2 + $len);
-	$perm = oct($perm) if $perm =~ /^\d+$/;
-
-	@stbuf = lstat($name);
-	if (!@stbuf) {
-	    $why .= "\texpected $type \"$name\" not created\n";
-	    next;
-	}
-	if ($perm ne '*' && ($stbuf[2] & 07777) != $perm) {
-	    $why .= "\t$type \"$name\" has unexpected permissions\n";
-	    $why .= sprintf("\t\texpected 0%o, found 0%o\n",
-		    $perm, $stbuf[2] & 07777);
-	}
-	if ($uid ne '*' && $stbuf[4] != $uid) {
-	    $why .= "\t$type \"$name\" has unexpected user-id\n";
-	    $why .= sprintf("\t\texpected %d, found %d\n",
-		    $uid, $stbuf[4]);
-	}
-	if ($gid ne '*' && $stbuf[5] != $gid) {
-	    $why .= "\t$type \"$name\" has unexpected group-id\n";
-	    $why .= sprintf("\t\texpected %d, found %d\n",
-		    $gid, $stbuf[5]);
-	}
-
-	if ($type eq 'file') {
-	    if (-l _ || ! -f _) {
-		$why .= "\t$type \"$name\" is not a regular file\n";
-	    } else {
-		local $tmp = &check_output($test{'long-name'}, $name,
-			    "$type contents in \"$name\"",
-			    $matchType eq 'exact' ? $rest : undef
-			    $matchType eq 'pattern' ? $rest : undef);
-		return undef if (!defined $tmp);
-		$why .= $tmp;
-	    }
-	} elsif ($type eq 'dir') {
-	    if ($rest !~ /^\s*$/) {
-		print STDERR "$prog:$test{':long-name'}: file-result test for directory $name should not have content specified\n";
-		return undef;
-	    }
-	    if (-l _ || ! -d _) {
-		$why .= "\t$type \"$name\" is not a directory\n";
-	    }
-	} elsif ($type eq 'symlink') {
-	    if (!-l _) {
-		$why .= "\t$type \"$name\" is not a symlink\n";
-	    } else {
-		local $content = readlink($name);
-		if (!defined $content) {
-		    print STDERR "$prog:$test{':long-name'}: file-result test for $type $name failed - could not readlink - $!\n";
-		    return undef;
-		}
-		local $tmp = &compare_output($test{'long-name'},
-			    "$type contents in \"$name\"",
-			    $matchType eq 'exact' ? $rest : undef
-			    $matchType eq 'pattern' ? $rest : undef);
-		return undef if (!defined $tmp);
-		$why .= $tmp;
-	    }
-	}
-    }
-
-    return $why;
-}
-
-sub
-HELP_MESSAGE
-{
-    print STDERR $Usage;
-    exit 0;
-}

Copied: vendor/MirOS/mksh/R50/check.pl (from rev 6707, vendor/MirOS/mksh/dist/check.pl)
===================================================================
--- vendor/MirOS/mksh/R50/check.pl	                        (rev 0)
+++ vendor/MirOS/mksh/R50/check.pl	2014-07-01 12:01:17 UTC (rev 6708)
@@ -0,0 +1,1317 @@
+# $MirOS: src/bin/mksh/check.pl,v 1.36 2014/06/09 13:25:50 tg Exp $
+# $OpenBSD: th,v 1.1 2013/12/02 20:39:44 millert Exp $
+#-
+# Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2011,
+#		2012, 2013, 2014
+#	Thorsten Glaser <tg at mirbsd.org>
+#
+# Provided that these terms and disclaimer and all copyright notices
+# are retained or reproduced in an accompanying document, permission
+# is granted to deal in this work without restriction, including un-
+# limited rights to use, publicly perform, distribute, sell, modify,
+# merge, give away, or sublicence.
+#
+# This work is provided "AS IS" and WITHOUT WARRANTY of any kind, to
+# the utmost extent permitted by applicable law, neither express nor
+# implied; without malicious intent or gross negligence. In no event
+# may a licensor, author or contributor be held liable for indirect,
+# direct, other damage, loss, or other issues arising in any way out
+# of dealing in the work, even if advised of the possibility of such
+# damage or existence of a defect, except proven that it results out
+# of said person's immediate fault when using the work as intended.
+#-
+# Example test:
+#		name: a-test
+#		description:
+#			a test to show how tests are done
+#		arguments: !-x!-f!
+#		stdin:
+#			echo -n *
+#			false
+#		expected-stdout: !
+#			*
+#		expected-stderr:
+#			+ echo -n *
+#			+ false
+#		expected-exit: 1
+#		---
+#	This runs the test-program (eg, mksh) with the arguments -x and -f,
+#	standard input is a file containing "echo hi*\nfalse\n". The program
+#	is expected to produce "hi*" (no trailing newline) on standard output,
+#	"+ echo hi*\n+false\n" on standard error, and an exit code of 1.
+#
+#
+# Format of test files:
+# - blank lines and lines starting with # are ignored
+# - a test file contains a series of tests
+# - a test is a series of tag:value pairs ended with a "---" line
+#   (leading/trailing spaces are stripped from the first line of value)
+# - test tags are:
+#	Tag			Flag	Description
+#	-----			----	-----------
+#	name			r	The name of the test; should be unique
+#	description		m	What test does
+#	arguments		M	Arguments to pass to the program;
+#					default is no arguments.
+#	script			m	Value is written to a file which
+#					is passed as an argument to the program
+#					(after the arguments arguments)
+#	stdin			m	Value is written to a file which is
+#					used as standard-input for the program;
+#					default is to use /dev/null.
+#	perl-setup		m	Value is a perl script which is executed
+#					just before the test is run. Try to
+#					avoid using this...
+#	perl-cleanup		m	Value is a perl script which is executed
+#					just after the test is run. Try to
+#					avoid using this...
+#	env-setup		M	Value is a list of NAME=VALUE elements
+#					which are put in the environment before
+#					the test is run. If the =VALUE is
+#					missing, NAME is removed from the
+#					environment. Programs are run with
+#					the following minimal environment:
+#					    HOME, LD_LIBRARY_PATH, LOCPATH,
+#					    LOGNAME, PATH, SHELL, UNIXMODE,
+#					    USER
+#					(values taken from the environment of
+#					the test harness).
+#					CYGWIN is set to nodosfilewarning.
+#					ENV is set to /nonexistant.
+#					__progname is set to the -p argument.
+#					__perlname is set to $^X (perlexe).
+#	file-setup		mps	Used to create files, directories
+#					and symlinks. First word is either
+#					file, dir or symlink; second word is
+#					permissions; this is followed by a
+#					quoted word that is the name of the
+#					file; the end-quote should be followed
+#					by a newline, then the file data
+#					(if any). The first word may be
+#					preceded by a ! to strip the trailing
+#					newline in a symlink.
+#	file-result		mps	Used to verify a file, symlink or
+#					directory is created correctly.
+#					The first word is either
+#					file, dir or symlink; second word is
+#					expected permissions; third word
+#					is user-id; fourth is group-id;
+#					fifth is "exact" or "pattern"
+#					indicating whether the file contents
+#					which follow is to be matched exactly
+#					or if it is a regular expression.
+#					The fifth argument is the quoted name
+#					of the file that should be created.
+#					The end-quote should be followed
+#					by a newline, then the file data
+#					(if any). The first word may be
+#					preceded by a ! to strip the trailing
+#					newline in the file contents.
+#					The permissions, user and group fields
+#					may be * meaning accept any value.
+#	time-limit			Time limit - the program is sent a
+#					SIGKILL N seconds. Default is no
+#					limit.
+#	expected-fail			'yes' if the test is expected to fail.
+#	expected-exit			expected exit code. Can be a number,
+#					or a C expression using the variables
+#					e, s and w (exit code, termination
+#					signal, and status code).
+#	expected-stdout		m	What the test should generate on stdout;
+#					default is to expect no output.
+#	expected-stdout-pattern	m	A perl pattern which matches the
+#					expected output.
+#	expected-stderr		m	What the test should generate on stderr;
+#					default is to expect no output.
+#	expected-stderr-pattern	m	A perl pattern which matches the
+#					expected standard error.
+#	category		m	Specify a comma separated list of
+#					'categories' of program that the test
+#					is to be run for. A category can be
+#					negated by prefixing the name with a !.
+#					The idea is that some tests in a
+#					test suite may apply to a particular
+#					program version and shouldn't be run
+#					on other versions. The category(s) of
+#					the program being tested can be
+#					specified on the command line.
+#					One category os:XXX is predefined
+#					(XXX is the operating system name,
+#					eg, linux, dec_osf).
+#	need-ctty			'yes' if the test needs a ctty, run
+#					with -C regress:no-ctty to disable.
+# Flag meanings:
+#	r	tag is required (eg, a test must have a name tag).
+#	m	value can be multiple lines. Lines must be prefixed with
+#		a tab. If the value part of the initial tag:value line is
+#			- empty: the initial blank line is stripped.
+#			- a lone !: the last newline in the value is stripped;
+#	M	value can be multiple lines (prefixed by a tab) and consists
+#		of multiple fields, delimited by a field separator character.
+#		The value must start and end with the f-s-c.
+#	p	tag takes parameters (used with m).
+#	s	tag can be used several times.
+
+# pull EINTR from POSIX.pm or Errno.pm if they exist
+# otherwise just skip it
+BEGIN {
+	$EINTR = 0;
+	eval {
+		require POSIX;
+		$EINTR = POSIX::EINTR();
+	};
+	if ($@) {
+		eval {
+			require Errno;
+			$EINTR = Errno::EINTR();
+		} or do {
+			$EINTR = 0;
+		};
+	}
+};
+
+use Getopt::Std;
+use Config;
+
+$os = defined $^O ? $^O : 'unknown';
+
+($prog = $0) =~ s#.*/##;
+
+$Usage = <<EOF ;
+Usage: $prog [-Pv] [-C cat] [-e e=v] [-p prog] [-s fn] [-T dir] \
+       [-t tmo] name ...
+	-C c	Specify the comma separated list of categories the program
+		belongs to (see category field).
+	-e e=v	Set the environment variable e to v for all tests
+		(if no =v is given, the current value is used)
+		Only one -e option can be given at the moment, sadly.
+	-P	program (-p) string has multiple words, and the program is in
+		the path (kludge option)
+	-p p	Use p as the program to test
+	-s s	Read tests from file s; if s is a directory, it is recursively
+		scaned for test files (which end in .t).
+	-T dir	Use dir instead of /tmp to hold temporary files
+	-t t	Use t as default time limit for tests (default is unlimited)
+	-v	Verbose mode: print reason test failed.
+	name	specifies the name of the test(s) to run; if none are
+		specified, all tests are run.
+EOF
+
+# See comment above for flag meanings
+%test_fields = (
+	'name',				'r',
+	'description',			'm',
+	'arguments',			'M',
+	'script',			'm',
+	'stdin',			'm',
+	'perl-setup',			'm',
+	'perl-cleanup',			'm',
+	'env-setup',			'M',
+	'file-setup',			'mps',
+	'file-result',			'mps',
+	'time-limit',			'',
+	'expected-fail',		'',
+	'expected-exit',		'',
+	'expected-stdout',		'm',
+	'expected-stdout-pattern',	'm',
+	'expected-stderr',		'm',
+	'expected-stderr-pattern',	'm',
+	'category',			'm',
+	'need-ctty',			'',
+	'need-pass',			'',
+	);
+# Filled in by read_test()
+%internal_test_fields = (
+	':full-name', 1,		# file:name
+	':long-name', 1,		# dir/file:lineno:name
+	);
+
+# Categories of the program under test. Provide the current
+# os by default.
+%categories = (
+	"os:$os", '1'
+	);
+
+$nfailed = 0;
+$nifailed = 0;
+$nxfailed = 0;
+$npassed = 0;
+$nxpassed = 0;
+
+%known_tests = ();
+
+if (!getopts('C:e:Pp:s:T:t:v')) {
+    print STDERR $Usage;
+    exit 1;
+}
+
+die "$prog: no program specified (use -p)\n" if !defined $opt_p;
+die "$prog: no test set specified (use -s)\n" if !defined $opt_s;
+$test_prog = $opt_p;
+$verbose = defined $opt_v && $opt_v;
+$test_set = $opt_s;
+$temp_base = $opt_T || "/tmp";
+if (defined $opt_t) {
+    die "$prog: bad -t argument (should be number > 0): $opt_t\n"
+	if $opt_t !~ /^\d+$/ || $opt_t <= 0;
+    $default_time_limit = $opt_t;
+}
+$program_kludge = defined $opt_P ? $opt_P : 0;
+
+if (defined $opt_C) {
+    foreach $c (split(',', $opt_C)) {
+	$c =~ s/\s+//;
+	die "$prog: categories can't be negated on the command line\n"
+	    if ($c =~ /^!/);
+	$categories{$c} = 1;
+    }
+}
+
+# Note which tests are to be run.
+%do_test = ();
+grep($do_test{$_} = 1, @ARGV);
+$all_tests = @ARGV == 0;
+
+# Set up a very minimal environment
+%new_env = ();
+foreach $env (('HOME', 'LD_LIBRARY_PATH', 'LOCPATH', 'LOGNAME',
+  'PATH', 'SHELL', 'UNIXMODE', 'USER')) {
+    $new_env{$env} = $ENV{$env} if defined $ENV{$env};
+}
+$new_env{'CYGWIN'} = 'nodosfilewarning';
+$new_env{'ENV'} = '/nonexistant';
+if (($os eq 'VMS') || ($Config{perlpath} =~ m/$Config{_exe}$/i)) {
+	$new_env{'__perlname'} = $Config{perlpath};
+} else {
+	$new_env{'__perlname'} = $Config{perlpath} . $Config{_exe};
+}
+if (defined $opt_e) {
+    # XXX need a way to allow many -e arguments...
+    if ($opt_e =~ /^([a-zA-Z_]\w*)(|=(.*))$/) {
+	$new_env{$1} = $2 eq '' ? $ENV{$1} : $3;
+    } else {
+	die "$0: bad -e argument: $opt_e\n";
+    }
+}
+%old_env = %ENV;
+
+chop($pwd = `pwd 2>/dev/null`);
+die "$prog: couldn't get current working directory\n" if $pwd eq '';
+die "$prog: couldn't cd to $pwd - $!\n" if !chdir($pwd);
+
+die "$prog: couldn't cd to $temp_base - $!\n" if !chdir($temp_base);
+die "$prog: couldn't get temporary directory base\n" unless -d '.';
+$temps = sprintf("chk%d-%d.", $$, time());
+$tempi = 0;
+until (mkdir(($tempdir = sprintf("%s%03d", $temps, $tempi)), 0700)) {
+    die "$prog: couldn't get temporary directory\n" if $tempi++ >= 999;
+}
+die "$prog: couldn't cd to $tempdir - $!\n" if !chdir($tempdir);
+chop($temp_dir = `pwd 2>/dev/null`);
+die "$prog: couldn't get temporary directory\n" if $temp_dir eq '';
+die "$prog: couldn't cd to $pwd - $!\n" if !chdir($pwd);
+
+if (!$program_kludge) {
+    $test_prog = "$pwd/$test_prog" if substr($test_prog, 0, 1) ne '/';
+    die "$prog: $test_prog is not executable - bye\n"
+	if (! -x $test_prog && $os ne 'os2');
+}
+
+ at trap_sigs = ('TERM', 'QUIT', 'INT', 'PIPE', 'HUP');
+ at SIG{@trap_sigs} = ('cleanup_exit') x @trap_sigs;
+$child_kill_ok = 0;
+$SIG{'ALRM'} = 'catch_sigalrm';
+
+$| = 1;
+
+# Create temp files
+$temps = "${temp_dir}/rts";
+$tempi = "${temp_dir}/rti";
+$tempo = "${temp_dir}/rto";
+$tempe = "${temp_dir}/rte";
+$tempdir = "${temp_dir}/rtd";
+mkdir($tempdir, 0700) or die "$prog: couldn't mkdir $tempdir - $!\n";
+
+if (-d $test_set) {
+    $file_prefix_skip = length($test_set) + 1;
+    $ret = &process_test_dir($test_set);
+} else {
+    $file_prefix_skip = 0;
+    $ret = &process_test_file($test_set);
+}
+&cleanup_exit() if !defined $ret;
+
+$tot_failed = $nfailed + $nifailed + $nxfailed;
+$tot_passed = $npassed + $nxpassed;
+if ($tot_failed || $tot_passed) {
+    print "Total failed: $tot_failed";
+    print " ($nifailed ignored)" if $nifailed;
+    print " ($nxfailed unexpected)" if $nxfailed;
+    print " (as expected)" if $nfailed && !$nxfailed && !$nifailed;
+    print "\nTotal passed: $tot_passed";
+    print " ($nxpassed unexpected)" if $nxpassed;
+    print "\n";
+}
+
+&cleanup_exit('ok');
+
+sub
+cleanup_exit
+{
+    local($sig, $exitcode) = ('', 1);
+
+    if ($_[0] eq 'ok') {
+	unless ($nxfailed) {
+		$exitcode = 0;
+	} else {
+		$exitcode = 1;
+	}
+    } elsif ($_[0] ne '') {
+	$sig = $_[0];
+    }
+
+    unlink($tempi, $tempo, $tempe, $temps);
+    &scrub_dir($tempdir) if defined $tempdir;
+    rmdir($tempdir) if defined $tempdir;
+    rmdir($temp_dir) if defined $temp_dir;
+
+    if ($sig) {
+	$SIG{$sig} = 'DEFAULT';
+	kill $sig, $$;
+	return;
+    }
+    exit $exitcode;
+}
+
+sub
+catch_sigalrm
+{
+    $SIG{'ALRM'} = 'catch_sigalrm';
+    kill(9, $child_pid) if $child_kill_ok;
+    $child_killed = 1;
+}
+
+sub
+process_test_dir
+{
+    local($dir) = @_;
+    local($ret, $file);
+    local(@todo) = ();
+
+    if (!opendir(DIR, $dir)) {
+	print STDERR "$prog: can't open directory $dir - $!\n";
+	return undef;
+    }
+    while (defined ($file = readdir(DIR))) {
+	push(@todo, $file) if $file =~ /^[^.].*\.t$/;
+    }
+    closedir(DIR);
+
+    foreach $file (@todo) {
+	$file = "$dir/$file";
+	if (-d $file) {
+	    $ret = &process_test_dir($file);
+	} elsif (-f _) {
+	    $ret = &process_test_file($file);
+	}
+	last if !defined $ret;
+    }
+
+    return $ret;
+}
+
+sub
+process_test_file
+{
+    local($file) = @_;
+    local($ret);
+
+    if (!open(IN, $file)) {
+	print STDERR "$prog: can't open $file - $!\n";
+	return undef;
+    }
+    binmode(IN);
+    while (1) {
+	$ret = &read_test($file, IN, *test);
+	last if !defined $ret || !$ret;
+	next if !$all_tests && !$do_test{$test{'name'}};
+	next if !&category_check(*test);
+	$ret = &run_test(*test);
+	last if !defined $ret;
+    }
+    close(IN);
+
+    return $ret;
+}
+
+sub
+run_test
+{
+    local(*test) = @_;
+    local($name) = $test{':full-name'};
+
+    return undef if !&scrub_dir($tempdir);
+
+    if (defined $test{'stdin'}) {
+	return undef if !&write_file($tempi, $test{'stdin'});
+	$ifile = $tempi;
+    } else {
+	$ifile = '/dev/null';
+    }
+
+    if (defined $test{'script'}) {
+	return undef if !&write_file($temps, $test{'script'});
+    }
+
+    if (!chdir($tempdir)) {
+	print STDERR "$prog: couldn't cd to $tempdir - $!\n";
+	return undef;
+    }
+
+    if (defined $test{'file-setup'}) {
+	local($i);
+	local($type, $perm, $rest, $c, $len, $name);
+
+	for ($i = 0; $i < $test{'file-setup'}; $i++) {
+	    $val = $test{"file-setup:$i"};
+
+	    # format is: type perm "name"
+	    ($type, $perm, $rest) =
+		split(' ', $val, 3);
+	    $c = substr($rest, 0, 1);
+	    $len = index($rest, $c, 1) - 1;
+	    $name = substr($rest, 1, $len);
+	    $rest = substr($rest, 2 + $len);
+	    $perm = oct($perm) if $perm =~ /^\d+$/;
+	    if ($type eq 'file') {
+		return undef if !&write_file($name, $rest);
+		if (!chmod($perm, $name)) {
+		    print STDERR
+		  "$prog:$test{':long-name'}: can't chmod $perm $name - $!\n";
+		    return undef;
+		}
+	    } elsif ($type eq 'dir') {
+		if (!mkdir($name, $perm)) {
+		    print STDERR
+		  "$prog:$test{':long-name'}: can't mkdir $perm $name - $!\n";
+		    return undef;
+		}
+	    } elsif ($type eq 'symlink') {
+		local($oumask) = umask($perm);
+		local($ret) = symlink($rest, $name);
+		umask($oumask);
+		if (!$ret) {
+		    print STDERR
+	    "$prog:$test{':long-name'}: couldn't create symlink $name - $!\n";
+		    return undef;
+		}
+	    }
+	}
+    }
+
+    if (defined $test{'perl-setup'}) {
+	eval $test{'perl-setup'};
+	if ($@ ne '') {
+	    print STDERR "$prog:$test{':long-name'}: error running perl-setup - $@\n";
+	    return undef;
+	}
+    }
+
+    $pid = fork;
+    if (!defined $pid) {
+	print STDERR "$prog: can't fork - $!\n";
+	return undef;
+    }
+    if (!$pid) {
+	@SIG{@trap_sigs} = ('DEFAULT') x @trap_sigs;
+	$SIG{'ALRM'} = 'DEFAULT';
+	if (defined $test{'env-setup'}) {
+	    local($var, $val, $i);
+
+	    foreach $var (split(substr($test{'env-setup'}, 0, 1),
+		$test{'env-setup'}))
+	    {
+		$i = index($var, '=');
+		next if $i == 0 || $var eq '';
+		if ($i < 0) {
+		    delete $new_env{$var};
+		} else {
+		    $new_env{substr($var, 0, $i)} = substr($var, $i + 1);
+		}
+	    }
+	}
+	if (!open(STDIN, "< $ifile")) {
+		print STDERR "$prog: couldn't open $ifile in child - $!\n";
+		kill('TERM', $$);
+	}
+	binmode(STDIN);
+	if (!open(STDOUT, "> $tempo")) {
+		print STDERR "$prog: couldn't open $tempo in child - $!\n";
+		kill('TERM', $$);
+	}
+	binmode(STDOUT);
+	if (!open(STDERR, "> $tempe")) {
+		print STDOUT "$prog: couldn't open $tempe in child - $!\n";
+		kill('TERM', $$);
+	}
+	binmode(STDERR);
+	if ($program_kludge) {
+	    @argv = split(' ', $test_prog);
+	} else {
+	    @argv = ($test_prog);
+	}
+	if (defined $test{'arguments'}) {
+		push(@argv,
+		     split(substr($test{'arguments'}, 0, 1),
+			   substr($test{'arguments'}, 1)));
+	}
+	push(@argv, $temps) if defined $test{'script'};
+
+	#XXX realpathise, use which/whence -p, or sth. like that
+	#XXX if !$program_kludge, we get by with not doing it for now tho
+	$new_env{'__progname'} = $argv[0];
+
+	# The following doesn't work with perl5...  Need to do it explicitly - yuck.
+	#%ENV = %new_env;
+	foreach $k (keys(%ENV)) {
+	    delete $ENV{$k};
+	}
+	$ENV{$k} = $v while ($k,$v) = each %new_env;
+
+	exec { $argv[0] } @argv;
+	print STDERR "$prog: couldn't execute $test_prog - $!\n";
+	kill('TERM', $$);
+	exit(95);
+    }
+    $child_pid = $pid;
+    $child_killed = 0;
+    $child_kill_ok = 1;
+    alarm($test{'time-limit'}) if defined $test{'time-limit'};
+    while (1) {
+	$xpid = waitpid($pid, 0);
+	$child_kill_ok = 0;
+	if ($xpid < 0) {
+	    if ($EINTR) {
+		next if $! == $EINTR;
+	    }
+	    print STDERR "$prog: error waiting for child - $!\n";
+	    return undef;
+	}
+	last;
+    }
+    $status = $?;
+    alarm(0) if defined $test{'time-limit'};
+
+    $failed = 0;
+    $why = '';
+
+    if ($child_killed) {
+	$failed = 1;
+	$why .= "\ttest timed out (limit of $test{'time-limit'} seconds)\n";
+    }
+
+    $ret = &eval_exit($test{'long-name'}, $status, $test{'expected-exit'});
+    return undef if !defined $ret;
+    if (!$ret) {
+	local($expl);
+
+	$failed = 1;
+	if (($status & 0xff) == 0x7f) {
+	    $expl = "stopped";
+	} elsif (($status & 0xff)) {
+	    $expl = "signal " . ($status & 0x7f);
+	} else {
+	    $expl = "exit-code " . (($status >> 8) & 0xff);
+	}
+	$why .=
+	"\tunexpected exit status $status ($expl), expected $test{'expected-exit'}\n";
+    }
+
+    $tmp = &check_output($test{'long-name'}, $tempo, 'stdout',
+		$test{'expected-stdout'}, $test{'expected-stdout-pattern'});
+    return undef if !defined $tmp;
+    if ($tmp ne '') {
+	$failed = 1;
+	$why .= $tmp;
+    }
+
+    $tmp = &check_output($test{'long-name'}, $tempe, 'stderr',
+		$test{'expected-stderr'}, $test{'expected-stderr-pattern'});
+    return undef if !defined $tmp;
+    if ($tmp ne '') {
+	$failed = 1;
+	$why .= $tmp;
+    }
+
+    $tmp = &check_file_result(*test);
+    return undef if !defined $tmp;
+    if ($tmp ne '') {
+	$failed = 1;
+	$why .= $tmp;
+    }
+
+    if (defined $test{'perl-cleanup'}) {
+	eval $test{'perl-cleanup'};
+	if ($@ ne '') {
+	    print STDERR "$prog:$test{':long-name'}: error running perl-cleanup - $@\n";
+	    return undef;
+	}
+    }
+
+    if (!chdir($pwd)) {
+	print STDERR "$prog: couldn't cd to $pwd - $!\n";
+	return undef;
+    }
+
+    if ($failed) {
+	if (!$test{'expected-fail'}) {
+	    if ($test{'need-pass'}) {
+		print "FAIL $name\n";
+		$nxfailed++;
+	    } else {
+		print "FAIL $name (ignored)\n";
+		$nifailed++;
+	    }
+	} else {
+	    print "fail $name (as expected)\n";
+	    $nfailed++;
+	}
+	$why = "\tDescription"
+		. &wrap_lines($test{'description'}, " (missing)\n")
+		. $why;
+    } elsif ($test{'expected-fail'}) {
+	print "PASS $name (unexpectedly)\n";
+	$nxpassed++;
+    } else {
+	print "pass $name\n";
+	$npassed++;
+    }
+    print $why if $verbose;
+    return 0;
+}
+
+sub
+category_check
+{
+    local(*test) = @_;
+    local($c);
+
+    return 0 if ($test{'need-ctty'} && defined $categories{'regress:no-ctty'});
+    return 1 if (!defined $test{'category'});
+    local($ok) = 0;
+    foreach $c (split(',', $test{'category'})) {
+	$c =~ s/\s+//;
+	if ($c =~ /^!/) {
+	    $c = $';
+	    return 0 if (defined $categories{$c});
+	    $ok = 1;
+	} else {
+	    $ok = 1 if (defined $categories{$c});
+	}
+    }
+    return $ok;
+}
+
+sub
+scrub_dir
+{
+    local($dir) = @_;
+    local(@todo) = ();
+    local($file);
+
+    if (!opendir(DIR, $dir)) {
+	print STDERR "$prog: couldn't open directory $dir - $!\n";
+	return undef;
+    }
+    while (defined ($file = readdir(DIR))) {
+	push(@todo, $file) if $file ne '.' && $file ne '..';
+    }
+    closedir(DIR);
+    foreach $file (@todo) {
+	$file = "$dir/$file";
+	if (-d $file) {
+	    return undef if !&scrub_dir($file);
+	    if (!rmdir($file)) {
+		print STDERR "$prog: couldn't rmdir $file - $!\n";
+		return undef;
+	    }
+	} else {
+	    if (!unlink($file)) {
+		print STDERR "$prog: couldn't unlink $file - $!\n";
+		return undef;
+	    }
+	}
+    }
+    return 1;
+}
+
+sub
+write_file
+{
+    local($file, $str) = @_;
+
+    if (!open(TEMP, "> $file")) {
+	print STDERR "$prog: can't open $file - $!\n";
+	return undef;
+    }
+    binmode(TEMP);
+    print TEMP $str;
+    if (!close(TEMP)) {
+	print STDERR "$prog: error writing $file - $!\n";
+	return undef;
+    }
+    return 1;
+}
+
+sub
+check_output
+{
+    local($name, $file, $what, $expect, $expect_pat) = @_;
+    local($got) = '';
+    local($why) = '';
+    local($ret);
+
+    if (!open(TEMP, "< $file")) {
+	print STDERR "$prog:$name($what): couldn't open $file after running program - $!\n";
+	return undef;
+    }
+    binmode(TEMP);
+    while (<TEMP>) {
+	$got .= $_;
+    }
+    close(TEMP);
+    return compare_output($name, $what, $expect, $expect_pat, $got);
+}
+
+sub
+compare_output
+{
+    local($name, $what, $expect, $expect_pat, $got) = @_;
+    local($why) = '';
+
+    if (defined $expect_pat) {
+	$_ = $got;
+	$ret = eval "$expect_pat";
+	if ($@ ne '') {
+	    print STDERR "$prog:$name($what): error evaluating $what pattern: $expect_pat - $@\n";
+	    return undef;
+	}
+	if (!$ret) {
+	    $why = "\tunexpected $what - wanted pattern";
+	    $why .= &wrap_lines($expect_pat);
+	    $why .= "\tgot";
+	    $why .= &wrap_lines($got);
+	}
+    } else {
+	$expect = '' if !defined $expect;
+	if ($got ne $expect) {
+	    $why .= "\tunexpected $what - " . &first_diff($expect, $got) . "\n";
+	    $why .= "\twanted";
+	    $why .= &wrap_lines($expect);
+	    $why .= "\tgot";
+	    $why .= &wrap_lines($got);
+	}
+    }
+    return $why;
+}
+
+sub
+wrap_lines
+{
+    local($str, $empty) = @_;
+    local($nonl) = substr($str, -1, 1) ne "\n";
+
+    return (defined $empty ? $empty : " nothing\n") if $str eq '';
+    substr($str, 0, 0) = ":\n";
+    $str =~ s/\n/\n\t\t/g;
+    if ($nonl) {
+	$str .= "\n\t[incomplete last line]\n";
+    } else {
+	chop($str);
+	chop($str);
+    }
+    return $str;
+}
+
+sub
+first_diff
+{
+    local($exp, $got) = @_;
+    local($lineno, $char) = (1, 1);
+    local($i, $exp_len, $got_len);
+    local($ce, $cg);
+
+    $exp_len = length($exp);
+    $got_len = length($got);
+    if ($exp_len != $got_len) {
+	if ($exp_len < $got_len) {
+	    if (substr($got, 0, $exp_len) eq $exp) {
+		return "got too much output";
+	    }
+	} elsif (substr($exp, 0, $got_len) eq $got) {
+	    return "got too little output";
+	}
+    }
+    for ($i = 0; $i < $exp_len; $i++) {
+	$ce = substr($exp, $i, 1);
+	$cg = substr($got, $i, 1);
+	last if $ce ne $cg;
+	$char++;
+	if ($ce eq "\n") {
+	    $lineno++;
+	    $char = 1;
+	}
+    }
+    return "first difference: line $lineno, char $char (wanted '"
+	. &format_char($ce) . "', got '"
+	. &format_char($cg) . "'";
+}
+
+sub
+format_char
+{
+    local($ch, $s);
+
+    $ch = ord($_[0]);
+    if ($ch == 10) {
+	return '\n';
+    } elsif ($ch == 13) {
+	return '\r';
+    } elsif ($ch == 8) {
+	return '\b';
+    } elsif ($ch == 9) {
+	return '\t';
+    } elsif ($ch > 127) {
+	$ch -= 127;
+	$s = "M-";
+    } else {
+	$s = '';
+    }
+    if ($ch < 32) {
+	$s .= '^';
+	$ch += ord('@');
+    } elsif ($ch == 127) {
+	return $s . "^?";
+    }
+    return $s . sprintf("%c", $ch);
+}
+
+sub
+eval_exit
+{
+    local($name, $status, $expect) = @_;
+    local($expr);
+    local($w, $e, $s) = ($status, ($status >> 8) & 0xff, $status & 0x7f);
+
+    $e = -1000 if $status & 0xff;
+    $s = -1000 if $s == 0x7f;
+    if (!defined $expect) {
+	$expr = '$w == 0';
+    } elsif ($expect =~ /^(|-)\d+$/) {
+	$expr = "\$e == $expect";
+    } else {
+	$expr = $expect;
+	$expr =~ s/\b([wse])\b/\$$1/g;
+	$expr =~ s/\b(SIG[A-Z][A-Z0-9]*)\b/&$1/g;
+    }
+    $w = eval $expr;
+    if ($@ ne '') {
+	print STDERR "$prog:$test{':long-name'}: bad expected-exit expression: $expect ($@)\n";
+	return undef;
+    }
+    return $w;
+}
+
+sub
+read_test
+{
+    local($file, $in, *test) = @_;
+    local($field, $val, $flags, $do_chop, $need_redo, $start_lineno);
+    local(%cnt, $sfield);
+
+    %test = ();
+    %cnt = ();
+    while (<$in>) {
+	chop;
+	next if /^\s*$/;
+	next if /^ *#/;
+	last if /^\s*---\s*$/;
+	$start_lineno = $. if !defined $start_lineno;
+	if (!/^([-\w]+):\s*(|\S|\S.*\S)\s*$/) {
+	    print STDERR "$prog:$file:$.: unrecognised line \"$_\"\n";
+	    return undef;
+	}
+	($field, $val) = ($1, $2);
+	$sfield = $field;
+	$flags = $test_fields{$field};
+	if (!defined $flags) {
+	    print STDERR "$prog:$file:$.: unrecognised field \"$field\"\n";
+	    return undef;
+	}
+	if ($flags =~ /s/) {
+	    local($cnt) = $cnt{$field}++;
+	    $test{$field} = $cnt{$field};
+	    $cnt = 0 if $cnt eq '';
+	    $sfield .= ":$cnt";
+	} elsif (defined $test{$field}) {
+	    print STDERR "$prog:$file:$.: multiple \"$field\" fields\n";
+	    return undef;
+	}
+	$do_chop = $flags !~ /m/;
+	$need_redo = 0;
+	if ($val eq '' || $val eq '!' || $flags =~ /p/) {
+	    if ($flags =~ /[Mm]/) {
+		if ($flags =~ /p/) {
+		    if ($val =~ /^!/) {
+			$do_chop = 1;
+			$val = $';
+		    } else {
+			$do_chop = 0;
+		    }
+		    if ($val eq '') {
+			print STDERR
+		"$prog:$file:$.: no parameters given for field \"$field\"\n";
+			return undef;
+		    }
+		} else {
+		    if ($val eq '!') {
+			$do_chop = 1;
+		    }
+		    $val = '';
+		}
+		while (<$in>) {
+		    last if !/^\t/;
+		    $val .= $';
+		}
+		chop $val if $do_chop;
+		$do_chop = 1;
+		$need_redo = 1;
+
+		# Syntax check on fields that can several instances
+		# (can give useful line numbers this way)
+
+		if ($field eq 'file-setup') {
+		    local($type, $perm, $rest, $c, $len, $name);
+
+		    # format is: type perm "name"
+		    if ($val !~ /^[ \t]*(\S+)[ \t]+(\S+)[ \t]+([^ \t].*)/) {
+			print STDERR
+		    "$prog:$file:$.: bad parameter line for file-setup field\n";
+			return undef;
+		    }
+		    ($type, $perm, $rest) = ($1, $2, $3);
+		    if ($type !~ /^(file|dir|symlink)$/) {
+			print STDERR
+		    "$prog:$file:$.: bad file type for file-setup: $type\n";
+			return undef;
+		    }
+		    if ($perm !~ /^\d+$/) {
+			print STDERR
+		    "$prog:$file:$.: bad permissions for file-setup: $type\n";
+			return undef;
+		    }
+		    $c = substr($rest, 0, 1);
+		    if (($len = index($rest, $c, 1) - 1) <= 0) {
+			print STDERR
+    "$prog:$file:$.: missing end quote for file name in file-setup: $rest\n";
+			return undef;
+		    }
+		    $name = substr($rest, 1, $len);
+		    if ($name =~ /^\// || $name =~ /(^|\/)\.\.(\/|$)/) {
+			# Note: this is not a security thing - just a sanity
+			# check - a test can still use symlinks to get at files
+			# outside the test directory.
+			print STDERR
+"$prog:$file:$.: file name in file-setup is absolute or contains ..: $name\n";
+			return undef;
+		    }
+		}
+		if ($field eq 'file-result') {
+		    local($type, $perm, $uid, $gid, $matchType,
+			  $rest, $c, $len, $name);
+
+		    # format is: type perm uid gid matchType "name"
+		    if ($val !~ /^\s*(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S.*)/) {
+			print STDERR
+		    "$prog:$file:$.: bad parameter line for file-result field\n";
+			return undef;
+		    }
+		    ($type, $perm, $uid, $gid, $matchType, $rest)
+			= ($1, $2, $3, $4, $5, $6);
+		    if ($type !~ /^(file|dir|symlink)$/) {
+			print STDERR
+		    "$prog:$file:$.: bad file type for file-result: $type\n";
+			return undef;
+		    }
+		    if ($perm !~ /^\d+$/ && $perm ne '*') {
+			print STDERR
+		    "$prog:$file:$.: bad permissions for file-result: $perm\n";
+			return undef;
+		    }
+		    if ($uid !~ /^\d+$/ && $uid ne '*') {
+			print STDERR
+		    "$prog:$file:$.: bad user-id for file-result: $uid\n";
+			return undef;
+		    }
+		    if ($gid !~ /^\d+$/ && $gid ne '*') {
+			print STDERR
+		    "$prog:$file:$.: bad group-id for file-result: $gid\n";
+			return undef;
+		    }
+		    if ($matchType !~ /^(exact|pattern)$/) {
+			print STDERR
+		"$prog:$file:$.: bad match type for file-result: $matchType\n";
+			return undef;
+		    }
+		    $c = substr($rest, 0, 1);
+		    if (($len = index($rest, $c, 1) - 1) <= 0) {
+			print STDERR
+    "$prog:$file:$.: missing end quote for file name in file-result: $rest\n";
+			return undef;
+		    }
+		    $name = substr($rest, 1, $len);
+		    if ($name =~ /^\// || $name =~ /(^|\/)\.\.(\/|$)/) {
+			# Note: this is not a security thing - just a sanity
+			# check - a test can still use symlinks to get at files
+			# outside the test directory.
+			print STDERR
+"$prog:$file:$.: file name in file-result is absolute or contains ..: $name\n";
+			return undef;
+		    }
+		}
+	    } elsif ($val eq '') {
+		print STDERR
+		    "$prog:$file:$.: no value given for field \"$field\"\n";
+		return undef;
+	    }
+	}
+	$val .= "\n" if !$do_chop;
+	$test{$sfield} = $val;
+	redo if $need_redo;
+    }
+    if ($_ eq '') {
+	if (%test) {
+	    print STDERR
+	      "$prog:$file:$start_lineno: end-of-file while reading test\n";
+	    return undef;
+	}
+	return 0;
+    }
+
+    while (($field, $val) = each %test_fields) {
+	if ($val =~ /r/ && !defined $test{$field}) {
+	    print STDERR
+	      "$prog:$file:$start_lineno: required field \"$field\" missing\n";
+	    return undef;
+	}
+    }
+
+    $test{':full-name'} = substr($file, $file_prefix_skip) . ":$test{'name'}";
+    $test{':long-name'} = "$file:$start_lineno:$test{'name'}";
+
+    # Syntax check on specific fields
+    if (defined $test{'expected-fail'}) {
+	if ($test{'expected-fail'} !~ /^(yes|no)$/) {
+	    print STDERR
+	      "$prog:$test{':long-name'}: bad value for expected-fail field\n";
+	    return undef;
+	}
+	$test{'expected-fail'} = $1 eq 'yes';
+    } else {
+	$test{'expected-fail'} = 0;
+    }
+    if (defined $test{'need-ctty'}) {
+	if ($test{'need-ctty'} !~ /^(yes|no)$/) {
+	    print STDERR
+	      "$prog:$test{':long-name'}: bad value for need-ctty field\n";
+	    return undef;
+	}
+	$test{'need-ctty'} = $1 eq 'yes';
+    } else {
+	$test{'need-ctty'} = 0;
+    }
+    if (defined $test{'need-pass'}) {
+	if ($test{'need-pass'} !~ /^(yes|no)$/) {
+	    print STDERR
+	      "$prog:$test{':long-name'}: bad value for need-pass field\n";
+	    return undef;
+	}
+	$test{'need-pass'} = $1 eq 'yes';
+    } else {
+	$test{'need-pass'} = 1;
+    }
+    if (defined $test{'arguments'}) {
+	local($firstc) = substr($test{'arguments'}, 0, 1);
+
+	if (substr($test{'arguments'}, -1, 1) ne $firstc) {
+	    print STDERR "$prog:$test{':long-name'}: arguments field doesn't start and end with the same character\n";
+	    return undef;
+	}
+    }
+    if (defined $test{'env-setup'}) {
+	local($firstc) = substr($test{'env-setup'}, 0, 1);
+
+	if (substr($test{'env-setup'}, -1, 1) ne $firstc) {
+	    print STDERR "$prog:$test{':long-name'}: env-setup field doesn't start and end with the same character\n";
+	    return undef;
+	}
+    }
+    if (defined $test{'expected-exit'}) {
+	local($val) = $test{'expected-exit'};
+
+	if ($val =~ /^(|-)\d+$/) {
+	    if ($val < 0 || $val > 255) {
+		print STDERR "$prog:$test{':long-name'}: expected-exit value $val not in 0..255\n";
+		return undef;
+	    }
+	} elsif ($val !~ /^([\s<>+-=*%\/&|!()]|\b[wse]\b|\bSIG[A-Z][A-Z0-9]*\b)+$/) {
+	    print STDERR "$prog:$test{':long-name'}: bad expected-exit expression: $val\n";
+	    return undef;
+	}
+    } else {
+	$test{'expected-exit'} = 0;
+    }
+    if (defined $test{'expected-stdout'}
+	&& defined $test{'expected-stdout-pattern'})
+    {
+	print STDERR "$prog:$test{':long-name'}: can't use both expected-stdout and expected-stdout-pattern\n";
+	return undef;
+    }
+    if (defined $test{'expected-stderr'}
+	&& defined $test{'expected-stderr-pattern'})
+    {
+	print STDERR "$prog:$test{':long-name'}: can't use both expected-stderr and expected-stderr-pattern\n";
+	return undef;
+    }
+    if (defined $test{'time-limit'}) {
+	if ($test{'time-limit'} !~ /^\d+$/ || $test{'time-limit'} == 0) {
+	    print STDERR
+	      "$prog:$test{':long-name'}: bad value for time-limit field\n";
+	    return undef;
+	}
+    } elsif (defined $default_time_limit) {
+	$test{'time-limit'} = $default_time_limit;
+    }
+
+    if (defined $known_tests{$test{'name'}}) {
+	print STDERR "$prog:$test{':long-name'}: warning: duplicate test name ${test{'name'}}\n";
+    }
+    $known_tests{$test{'name'}} = 1;
+
+    return 1;
+}
+
+sub
+tty_msg
+{
+    local($msg) = @_;
+
+    open(TTY, "> /dev/tty") || return 0;
+    print TTY $msg;
+    close(TTY);
+    return 1;
+}
+
+sub
+never_called_funcs
+{
+	return 0;
+	&tty_msg("hi\n");
+	&never_called_funcs();
+	&catch_sigalrm();
+	$old_env{'foo'} = 'bar';
+	$internal_test_fields{'foo'} = 'bar';
+}
+
+sub
+check_file_result
+{
+    local(*test) = @_;
+
+    return '' if (!defined $test{'file-result'});
+
+    local($why) = '';
+    local($i);
+    local($type, $perm, $uid, $gid, $rest, $c, $len, $name);
+    local(@stbuf);
+
+    for ($i = 0; $i < $test{'file-result'}; $i++) {
+	$val = $test{"file-result:$i"};
+
+	# format is: type perm "name"
+	($type, $perm, $uid, $gid, $matchType, $rest) =
+	    split(' ', $val, 6);
+	$c = substr($rest, 0, 1);
+	$len = index($rest, $c, 1) - 1;
+	$name = substr($rest, 1, $len);
+	$rest = substr($rest, 2 + $len);
+	$perm = oct($perm) if $perm =~ /^\d+$/;
+
+	@stbuf = lstat($name);
+	if (!@stbuf) {
+	    $why .= "\texpected $type \"$name\" not created\n";
+	    next;
+	}
+	if ($perm ne '*' && ($stbuf[2] & 07777) != $perm) {
+	    $why .= "\t$type \"$name\" has unexpected permissions\n";
+	    $why .= sprintf("\t\texpected 0%o, found 0%o\n",
+		    $perm, $stbuf[2] & 07777);
+	}
+	if ($uid ne '*' && $stbuf[4] != $uid) {
+	    $why .= "\t$type \"$name\" has unexpected user-id\n";
+	    $why .= sprintf("\t\texpected %d, found %d\n",
+		    $uid, $stbuf[4]);
+	}
+	if ($gid ne '*' && $stbuf[5] != $gid) {
+	    $why .= "\t$type \"$name\" has unexpected group-id\n";
+	    $why .= sprintf("\t\texpected %d, found %d\n",
+		    $gid, $stbuf[5]);
+	}
+
+	if ($type eq 'file') {
+	    if (-l _ || ! -f _) {
+		$why .= "\t$type \"$name\" is not a regular file\n";
+	    } else {
+		local $tmp = &check_output($test{'long-name'}, $name,
+			    "$type contents in \"$name\"",
+			    $matchType eq 'exact' ? $rest : undef
+			    $matchType eq 'pattern' ? $rest : undef);
+		return undef if (!defined $tmp);
+		$why .= $tmp;
+	    }
+	} elsif ($type eq 'dir') {
+	    if ($rest !~ /^\s*$/) {
+		print STDERR "$prog:$test{':long-name'}: file-result test for directory $name should not have content specified\n";
+		return undef;
+	    }
+	    if (-l _ || ! -d _) {
+		$why .= "\t$type \"$name\" is not a directory\n";
+	    }
+	} elsif ($type eq 'symlink') {
+	    if (!-l _) {
+		$why .= "\t$type \"$name\" is not a symlink\n";
+	    } else {
+		local $content = readlink($name);
+		if (!defined $content) {
+		    print STDERR "$prog:$test{':long-name'}: file-result test for $type $name failed - could not readlink - $!\n";
+		    return undef;
+		}
+		local $tmp = &compare_output($test{'long-name'},
+			    "$type contents in \"$name\"",
+			    $matchType eq 'exact' ? $rest : undef
+			    $matchType eq 'pattern' ? $rest : undef);
+		return undef if (!defined $tmp);
+		$why .= $tmp;
+	    }
+	}
+    }
+
+    return $why;
+}
+
+sub
+HELP_MESSAGE
+{
+    print STDERR $Usage;
+    exit 0;
+}

Deleted: vendor/MirOS/mksh/R50/check.t
===================================================================
--- vendor/MirOS/mksh/dist/check.t	2014-06-30 23:58:12 UTC (rev 6706)
+++ vendor/MirOS/mksh/R50/check.t	2014-07-01 12:01:17 UTC (rev 6708)
@@ -1,11177 +0,0 @@
-# $MirOS: src/bin/mksh/check.t,v 1.629 2013/08/14 20:26:15 tg Exp $
-# $OpenBSD: bksl-nl.t,v 1.2 2001/01/28 23:04:56 niklas Exp $
-# $OpenBSD: history.t,v 1.5 2001/01/28 23:04:56 niklas Exp $
-# $OpenBSD: read.t,v 1.3 2003/03/10 03:48:16 david Exp $
-# $OpenBSD: regress.t,v 1.15 2013/07/01 17:25:27 jca Exp $
-# $OpenBSD: obsd-regress.t,v 1.5 2013/07/01 17:25:27 jca Exp $
-#-
-# Copyright © 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
-#	      2011, 2012, 2013
-#	Thorsten Glaser <tg at mirbsd.org>
-#
-# Provided that these terms and disclaimer and all copyright notices
-# are retained or reproduced in an accompanying document, permission
-# is granted to deal in this work without restriction, including un‐
-# limited rights to use, publicly perform, distribute, sell, modify,
-# merge, give away, or sublicence.
-#
-# This work is provided “AS IS” and WITHOUT WARRANTY of any kind, to
-# the utmost extent permitted by applicable law, neither express nor
-# implied; without malicious intent or gross negligence. In no event
-# may a licensor, author or contributor be held liable for indirect,
-# direct, other damage, loss, or other issues arising in any way out
-# of dealing in the work, even if advised of the possibility of such
-# damage or existence of a defect, except proven that it results out
-# of said person’s immediate fault when using the work as intended.
-#-
-# You may also want to test IFS with the script at
-# http://www.research.att.com/~gsf/public/ifs.sh
-#
-# More testsuites at:
-# http://www.freebsd.org/cgi/cvsweb.cgi/src/tools/regression/bin/test/regress.sh?rev=HEAD
-
-expected-stdout:
-	@(#)MIRBSD KSH R48 2013/08/14
-description:
-	Check version of shell.
-stdin:
-	echo $KSH_VERSION
-name: KSH_VERSION
-category: shell:legacy-no
----
-expected-stdout:
-	@(#)LEGACY KSH R48 2013/08/14
-description:
-	Check version of legacy shell.
-stdin:
-	echo $KSH_VERSION
-name: KSH_VERSION-legacy
-category: shell:legacy-yes
----
-name: selftest-1
-description:
-	Regression test self-testing
-stdin:
-	echo ${foo:-baz}
-expected-stdout:
-	baz
----
-name: selftest-2
-description:
-	Regression test self-testing
-env-setup: !foo=bar!
-stdin:
-	echo ${foo:-baz}
-expected-stdout:
-	bar
----
-name: selftest-3
-description:
-	Regression test self-testing
-env-setup: !ENV=fnord!
-stdin:
-	echo "<$ENV>"
-expected-stdout:
-	<fnord>
----
-name: selftest-env
-description:
-	Just output the environment variables set (always fails)
-category: disabled
-stdin:
-	set
----
-name: selftest-legacy
-description:
-	Check some things in the LEGACY KSH
-category: shell:legacy-yes
-stdin:
-	set +o emacs
-	set +o vi
-	[[ "$(set +o) -o" = *"-o emacs -o"* ]] && echo 1=emacs
-	[[ "$(set +o) -o" = *"-o vi -o"* ]] && echo 1=vi
-	set -o emacs
-	set -o vi
-	[[ "$(set +o) -o" = *"-o emacs -o"* ]] && echo 2=emacs
-	[[ "$(set +o) -o" = *"-o vi -o"* ]] && echo 2=vi
-expected-stdout:
-	2=emacs
-	2=vi
----
-name: selftest-direct-builtin-call
-description:
-	Check that direct builtin calls work
-stdin:
-	ln -s "$__progname" cat || cp "$__progname" cat
-	ln -s "$__progname" echo || cp "$__progname" echo
-	./echo -c 'echo  foo' | ./cat -u
-expected-stdout:
-	-c echo  foo
----
-name: alias-1
-description:
-	Check that recursion is detected/avoided in aliases.
-stdin:
-	alias fooBar=fooBar
-	fooBar
-	exit 0
-expected-stderr-pattern:
-	/fooBar.*not found.*/
----
-name: alias-2
-description:
-	Check that recursion is detected/avoided in aliases.
-stdin:
-	alias fooBar=barFoo
-	alias barFoo=fooBar
-	fooBar
-	barFoo
-	exit 0
-expected-stderr-pattern:
-	/fooBar.*not found.*\n.*barFoo.*not found/
----
-name: alias-3
-description:
-	Check that recursion is detected/avoided in aliases.
-stdin:
-	alias Echo='echo '
-	alias fooBar=barFoo
-	alias barFoo=fooBar
-	Echo fooBar
-	unalias barFoo
-	Echo fooBar
-expected-stdout:
-	fooBar
-	barFoo
----
-name: alias-4
-description:
-	Check that alias expansion isn't done on keywords (in keyword
-	postitions).
-stdin:
-	alias Echo='echo '
-	alias while=While
-	while false; do echo hi ; done
-	Echo while
-expected-stdout:
-	While
----
-name: alias-5
-description:
-	Check that alias expansion done after alias with trailing space.
-stdin:
-	alias Echo='echo '
-	alias foo='bar stuff '
-	alias bar='Bar1 Bar2 '
-	alias stuff='Stuff'
-	alias blah='Blah'
-	Echo foo blah
-expected-stdout:
-	Bar1 Bar2 Stuff Blah
----
-name: alias-6
-description:
-	Check that alias expansion done after alias with trailing space.
-stdin:
-	alias Echo='echo '
-	alias foo='bar bar'
-	alias bar='Bar '
-	alias blah=Blah
-	Echo foo blah
-expected-stdout:
-	Bar Bar Blah
----
-name: alias-7
-description:
-	Check that alias expansion done after alias with trailing space
-	after a keyword.
-stdin:
-	alias X='case '
-	alias Y=Z
-	X Y in 'Y') echo is y ;; Z) echo is z ; esac
-expected-stdout:
-	is z
----
-name: alias-8
-description:
-	Check that newlines in an alias don't cause the command to be lost.
-stdin:
-	alias foo='
-	
-	
-	echo hi
-	
-	
-	
-	echo there
-	
-	
-	'
-	foo
-expected-stdout:
-	hi
-	there
----
-name: alias-9
-description:
-	Check that recursion is detected/avoided in aliases.
-	This check fails for slow machines or Cygwin, raise
-	the time-limit clause (e.g. to 7) if this occurs.
-time-limit: 3
-stdin:
-	print '#!'"$__progname"'\necho tf' >lq
-	chmod +x lq
-	PATH=$PWD:$PATH
-	alias lq=lq
-	lq
-	echo = now
-	i=`lq`
-	print -r -- $i
-	echo = out
-	exit 0
-expected-stdout:
-	tf
-	= now
-	tf
-	= out
----
-name: alias-10
-description:
-	Check that recursion is detected/avoided in aliases.
-	Regression, introduced during an old bugfix.
-stdin:
-	alias foo='print hello '
-	alias bar='foo world'
-	echo $(bar)
-expected-stdout:
-	hello world
----
-name: arith-lazy-1
-description:
-	Check that only one side of ternary operator is evaluated
-stdin:
-	x=i+=2
-	y=j+=2
-	typeset -i i=1 j=1
-	echo $((1 ? 20 : (x+=2)))
-	echo $i,$x
-	echo $((0 ? (y+=2) : 30))
-	echo $j,$y
-expected-stdout:
-	20
-	1,i+=2
-	30
-	1,j+=2
----
-name: arith-lazy-2
-description:
-	Check that assignments not done on non-evaluated side of ternary
-	operator
-stdin:
-	x=i+=2
-	y=j+=2
-	typeset -i i=1 j=1
-	echo $((1 ? 20 : (x+=2)))
-	echo $i,$x
-	echo $((0 ? (y+=2) : 30))
-	echo $i,$y
-expected-stdout:
-	20
-	1,i+=2
-	30
-	1,j+=2
----
-name: arith-lazy-3
-description:
-	Check that assignments not done on non-evaluated side of ternary
-	operator and this construct is parsed correctly (Debian #445651)
-stdin:
-	x=4
-	y=$((0 ? x=1 : 2))
-	echo = $x $y =
-expected-stdout:
-	= 4 2 =
----
-name: arith-lazy-4
-description:
-	Check that preun/postun not done on non-evaluated side of ternary
-	operator
-stdin:
-	(( m = n = 0, 1 ? n++ : m++ ? 2 : 3 ))
-	echo "($n, $m)"
-	m=0; echo $(( 0 ? ++m : 2 )); echo $m
-	m=0; echo $(( 0 ? m++ : 2 )); echo $m
-expected-stdout:
-	(1, 0)
-	2
-	0
-	2
-	0
----
-name: arith-ternary-prec-1
-description:
-	Check precedence of ternary operator vs assignment
-stdin:
-	typeset -i x=2
-	y=$((1 ? 20 : x+=2))
-expected-exit: e != 0
-expected-stderr-pattern:
-	/.*:.*1 \? 20 : x\+=2.*lvalue.*\n$/
----
-name: arith-ternary-prec-2
-description:
-	Check precedence of ternary operator vs assignment
-stdin:
-	typeset -i x=2
-	echo $((0 ? x+=2 : 20))
-expected-stdout:
-	20
----
-name: arith-div-assoc-1
-description:
-	Check associativity of division operator
-stdin:
-	echo $((20 / 2 / 2))
-expected-stdout:
-	5
----
-name: arith-div-byzero
-description:
-	Check division by zero errors out
-stdin:
-	x=$(echo $((1 / 0)))
-	echo =$?:$x.
-expected-stdout:
-	=1:.
-expected-stderr-pattern:
-	/.*divisor/
----
-name: arith-div-intmin-by-minusone
-description:
-	Check division overflow wraps around silently
-category: int:32
-stdin:
-	echo signed:$((-2147483648 / -1))r$((-2147483648 % -1)).
-	echo unsigned:$((# -2147483648 / -1))r$((# -2147483648 % -1)).
-expected-stdout:
-	signed:-2147483648r0.
-	unsigned:0r2147483648.
----
-name: arith-div-intmin-by-minusone-64
-description:
-	Check division overflow wraps around silently
-category: int:64
-stdin:
-	echo signed:$((-9223372036854775808 / -1))r$((-9223372036854775808 % -1)).
-	echo unsigned:$((# -9223372036854775808 / -1))r$((# -9223372036854775808 % -1)).
-expected-stdout:
-	signed:-9223372036854775808r0.
-	unsigned:0r9223372036854775808.
----
-name: arith-assop-assoc-1
-description:
-	Check associativity of assignment-operator operator
-stdin:
-	typeset -i i=1 j=2 k=3
-	echo $((i += j += k))
-	echo $i,$j,$k
-expected-stdout:
-	6
-	6,5,3
----
-name: arith-mandatory
-description:
-	Passing of this test is *mandatory* for a valid mksh executable!
-category: shell:legacy-no
-stdin:
-	typeset -i sari=0
-	typeset -Ui uari=0
-	typeset -i x=0
-	print -r -- $((x++)):$sari=$uari. #0
-	let --sari --uari
-	print -r -- $((x++)):$sari=$uari. #1
-	sari=2147483647 uari=2147483647
-	print -r -- $((x++)):$sari=$uari. #2
-	let ++sari ++uari
-	print -r -- $((x++)):$sari=$uari. #3
-	let --sari --uari
-	let 'sari *= 2' 'uari *= 2'
-	let ++sari ++uari
-	print -r -- $((x++)):$sari=$uari. #4
-	let ++sari ++uari
-	print -r -- $((x++)):$sari=$uari. #5
-	sari=-2147483648 uari=-2147483648
-	print -r -- $((x++)):$sari=$uari. #6
-	let --sari --uari
-	print -r -- $((x++)):$sari=$uari. #7
-	(( sari = -5 >> 1 ))
-	((# uari = -5 >> 1 ))
-	print -r -- $((x++)):$sari=$uari. #8
-	(( sari = -2 ))
-	((# uari = sari ))
-	print -r -- $((x++)):$sari=$uari. #9
-expected-stdout:
-	0:0=0.
-	1:-1=4294967295.
-	2:2147483647=2147483647.
-	3:-2147483648=2147483648.
-	4:-1=4294967295.
-	5:0=0.
-	6:-2147483648=2147483648.
-	7:2147483647=2147483647.
-	8:-3=2147483645.
-	9:-2=4294967294.
----
-name: arith-unsigned-1
-description:
-	Check if unsigned arithmetics work
-category: int:32
-stdin:
-	# signed vs unsigned
-	echo x1 $((-1)) $((#-1))
-	# calculating
-	typeset -i vs
-	typeset -Ui vu
-	vs=4123456789; vu=4123456789
-	echo x2 $vs $vu
-	(( vs %= 2147483647 ))
-	(( vu %= 2147483647 ))
-	echo x3 $vs $vu
-	vs=4123456789; vu=4123456789
-	(( # vs %= 2147483647 ))
-	(( # vu %= 2147483647 ))
-	echo x4 $vs $vu
-	# make sure the calculation does not change unsigned flag
-	vs=4123456789; vu=4123456789
-	echo x5 $vs $vu
-	# short form
-	echo x6 $((# vs % 2147483647)) $((# vu % 2147483647))
-	# array refs
-	set -A va
-	va[1975973142]=right
-	va[4123456789]=wrong
-	echo x7 ${va[#4123456789%2147483647]}
-expected-stdout:
-	x1 -1 4294967295
-	x2 -171510507 4123456789
-	x3 -171510507 4123456789
-	x4 1975973142 1975973142
-	x5 -171510507 4123456789
-	x6 1975973142 1975973142
-	x7 right
----
-name: arith-limit32-1
-description:
-	Check if arithmetics are 32 bit
-category: int:32
-stdin:
-	# signed vs unsigned
-	echo x1 $((-1)) $((#-1))
-	# calculating
-	typeset -i vs
-	typeset -Ui vu
-	vs=2147483647; vu=2147483647
-	echo x2 $vs $vu
-	let vs++ vu++
-	echo x3 $vs $vu
-	vs=4294967295; vu=4294967295
-	echo x4 $vs $vu
-	let vs++ vu++
-	echo x5 $vs $vu
-	let vs++ vu++
-	echo x6 $vs $vu
-expected-stdout:
-	x1 -1 4294967295
-	x2 2147483647 2147483647
-	x3 -2147483648 2147483648
-	x4 -1 4294967295
-	x5 0 0
-	x6 1 1
----
-name: arith-limit64-1
-description:
-	Check if arithmetics are 64 bit
-category: int:64
-stdin:
-	# signed vs unsigned
-	echo x1 $((-1)) $((#-1))
-	# calculating
-	typeset -i vs
-	typeset -Ui vu
-	vs=9223372036854775807; vu=9223372036854775807
-	echo x2 $vs $vu
-	let vs++ vu++
-	echo x3 $vs $vu
-	vs=18446744073709551615; vu=18446744073709551615
-	echo x4 $vs $vu
-	let vs++ vu++
-	echo x5 $vs $vu
-	let vs++ vu++
-	echo x6 $vs $vu
-expected-stdout:
-	x1 -1 18446744073709551615
-	x2 9223372036854775807 9223372036854775807
-	x3 -9223372036854775808 9223372036854775808
-	x4 -1 18446744073709551615
-	x5 0 0
-	x6 1 1
----
-name: bksl-nl-ign-1
-description:
-	Check that \newline is not collapsed after #
-stdin:
-	echo hi #there \
-	echo folks
-expected-stdout:
-	hi
-	folks
----
-name: bksl-nl-ign-2
-description:
-	Check that \newline is not collapsed inside single quotes
-stdin:
-	echo 'hi \
-	there'
-	echo folks
-expected-stdout:
-	hi \
-	there
-	folks
----
-name: bksl-nl-ign-3
-description:
-	Check that \newline is not collapsed inside single quotes
-stdin:
-	cat << \EOF
-	hi \
-	there
-	EOF
-expected-stdout:
-	hi \
-	there
----
-name: bksl-nl-ign-4
-description:
-	Check interaction of aliases, single quotes and here-documents
-	with backslash-newline
-	(don't know what POSIX has to say about this)
-stdin:
-	a=2
-	alias x='echo hi
-	cat << "EOF"
-	foo\
-	bar
-	some'
-	x
-	more\
-	stuff$a
-	EOF
-expected-stdout:
-	hi
-	foo\
-	bar
-	some
-	more\
-	stuff$a
----
-name: bksl-nl-ign-5
-description:
-	Check what happens with backslash at end of input
-	(the old Bourne shell trashes them; so do we)
-stdin: !
-	echo `echo foo\\`bar
-	echo hi\
-expected-stdout:
-	foobar
-	hi
----
-#
-# Places \newline should be collapsed
-#
-name: bksl-nl-1
-description:
-	Check that \newline is collapsed before, in the middle of, and
-	after words
-stdin:
-	 	 	\
-			 echo hi\
-	There, \
-	folks
-expected-stdout:
-	hiThere, folks
----
-name: bksl-nl-2
-description:
-	Check that \newline is collapsed in $ sequences
-	(ksh93 fails this)
-stdin:
-	a=12
-	ab=19
-	echo $\
-	a
-	echo $a\
-	b
-	echo $\
-	{a}
-	echo ${a\
-	b}
-	echo ${ab\
-	}
-expected-stdout:
-	12
-	19
-	12
-	19
-	19
----
-name: bksl-nl-3
-description:
-	Check that \newline is collapsed in $(..) and `...` sequences
-	(ksh93 fails this)
-stdin:
-	echo $\
-	(echo foobar1)
-	echo $(\
-	echo foobar2)
-	echo $(echo foo\
-	bar3)
-	echo $(echo foobar4\
-	)
-	echo `
-	echo stuff1`
-	echo `echo st\
-	uff2`
-expected-stdout:
-	foobar1
-	foobar2
-	foobar3
-	foobar4
-	stuff1
-	stuff2
----
-name: bksl-nl-4
-description:
-	Check that \newline is collapsed in $((..)) sequences
-	(ksh93 fails this)
-stdin:
-	echo $\
-	((1+2))
-	echo $(\
-	(1+2+3))
-	echo $((\
-	1+2+3+4))
-	echo $((1+\
-	2+3+4+5))
-	echo $((1+2+3+4+5+6)\
-	)
-expected-stdout:
-	3
-	6
-	10
-	15
-	21
----
-name: bksl-nl-5
-description:
-	Check that \newline is collapsed in double quoted strings
-stdin:
-	echo "\
-	hi"
-	echo "foo\
-	bar"
-	echo "folks\
-	"
-expected-stdout:
-	hi
-	foobar
-	folks
----
-name: bksl-nl-6
-description:
-	Check that \newline is collapsed in here document delimiters
-	(ksh93 fails second part of this)
-stdin:
-	a=12
-	cat << EO\
-	F
-	a=$a
-	foo\
-	bar
-	EOF
-	cat << E_O_F
-	foo
-	E_O_\
-	F
-	echo done
-expected-stdout:
-	a=12
-	foobar
-	foo
-	done
----
-name: bksl-nl-7
-description:
-	Check that \newline is collapsed in double-quoted here-document
-	delimiter.
-stdin:
-	a=12
-	cat << "EO\
-	F"
-	a=$a
-	foo\
-	bar
-	EOF
-	echo done
-expected-stdout:
-	a=$a
-	foo\
-	bar
-	done
----
-name: bksl-nl-8
-description:
-	Check that \newline is collapsed in various 2+ character tokens
-	delimiter.
-	(ksh93 fails this)
-stdin:
-	echo hi &\
-	& echo there
-	echo foo |\
-	| echo bar
-	cat <\
-	< EOF
-	stuff
-	EOF
-	cat <\
-	<\
-	- EOF
-		more stuff
-	EOF
-	cat <<\
-	EOF
-	abcdef
-	EOF
-	echo hi >\
-	> /dev/null
-	echo $?
-	i=1
-	case $i in
-	(\
-	x|\
-	1\
-	) echo hi;\
-	;
-	(*) echo oops
-	esac
-expected-stdout:
-	hi
-	there
-	foo
-	stuff
-	more stuff
-	abcdef
-	0
-	hi
----
-name: bksl-nl-9
-description:
-	Check that \ at the end of an alias is collapsed when followed
-	by a newline
-	(don't know what POSIX has to say about this)
-stdin:
-	alias x='echo hi\'
-	x
-	echo there
-expected-stdout:
-	hiecho there
----
-name: bksl-nl-10
-description:
-	Check that \newline in a keyword is collapsed
-stdin:
-	i\
-	f true; then\
-	 echo pass; el\
-	se echo fail; fi
-expected-stdout:
-	pass
----
-#
-# Places \newline should be collapsed (ksh extensions)
-#
-name: bksl-nl-ksh-1
-description:
-	Check that \newline is collapsed in extended globbing
-	(ksh93 fails this)
-stdin:
-	xxx=foo
-	case $xxx in
-	(f*\
-	(\
-	o\
-	)\
-	) echo ok ;;
-	*) echo bad
-	esac
-expected-stdout:
-	ok
----
-name: bksl-nl-ksh-2
-description:
-	Check that \newline is collapsed in ((...)) expressions
-	(ksh93 fails this)
-stdin:
-	i=1
-	(\
-	(\
-	i=i+2\
-	)\
-	)
-	echo $i
-expected-stdout:
-	3
----
-name: break-1
-description:
-	See if break breaks out of loops
-stdin:
-	for i in a b c; do echo $i; break; echo bad-$i; done
-	echo end-1
-	for i in a b c; do echo $i; break 1; echo bad-$i; done
-	echo end-2
-	for i in a b c; do
-	    for j in x y z; do
-		echo $i:$j
-		break
-		echo bad-$i
-	    done
-	    echo end-$i
-	done
-	echo end-3
-expected-stdout:
-	a
-	end-1
-	a
-	end-2
-	a:x
-	end-a
-	b:x
-	end-b
-	c:x
-	end-c
-	end-3
----
-name: break-2
-description:
-	See if break breaks out of nested loops
-stdin:
-	for i in a b c; do
-	    for j in x y z; do
-		echo $i:$j
-		break 2
-		echo bad-$i
-	    done
-	    echo end-$i
-	done
-	echo end
-expected-stdout:
-	a:x
-	end
----
-name: break-3
-description:
-	What if break used outside of any loops
-	(ksh88,ksh93 don't print error messages here)
-stdin:
-	break
-expected-stderr-pattern:
-	/.*break.*/
----
-name: break-4
-description:
-	What if break N used when only N-1 loops
-	(ksh88,ksh93 don't print error messages here)
-stdin:
-	for i in a b c; do echo $i; break 2; echo bad-$i; done
-	echo end
-expected-stdout:
-	a
-	end
-expected-stderr-pattern:
-	/.*break.*/
----
-name: break-5
-description:
-	Error if break argument isn't a number
-stdin:
-	for i in a b c; do echo $i; break abc; echo more-$i; done
-	echo end
-expected-stdout:
-	a
-expected-exit: e != 0
-expected-stderr-pattern:
-	/.*break.*/
----
-name: continue-1
-description:
-	See if continue continues loops
-stdin:
-	for i in a b c; do echo $i; continue; echo bad-$i ; done
-	echo end-1
-	for i in a b c; do echo $i; continue 1; echo bad-$i; done
-	echo end-2
-	for i in a b c; do
-	    for j in x y z; do
-		echo $i:$j
-		continue
-		echo bad-$i-$j
-	    done
-	    echo end-$i
-	done
-	echo end-3
-expected-stdout:
-	a
-	b
-	c
-	end-1
-	a
-	b
-	c
-	end-2
-	a:x
-	a:y
-	a:z
-	end-a
-	b:x
-	b:y
-	b:z
-	end-b
-	c:x
-	c:y
-	c:z
-	end-c
-	end-3
----
-name: continue-2
-description:
-	See if continue breaks out of nested loops
-stdin:
-	for i in a b c; do
-	    for j in x y z; do
-		echo $i:$j
-		continue 2
-		echo bad-$i-$j
-	    done
-	    echo end-$i
-	done
-	echo end
-expected-stdout:
-	a:x
-	b:x
-	c:x
-	end
----
-name: continue-3
-description:
-	What if continue used outside of any loops
-	(ksh88,ksh93 don't print error messages here)
-stdin:
-	continue
-expected-stderr-pattern:
-	/.*continue.*/
----
-name: continue-4
-description:
-	What if continue N used when only N-1 loops
-	(ksh88,ksh93 don't print error messages here)
-stdin:
-	for i in a b c; do echo $i; continue 2; echo bad-$i; done
-	echo end
-expected-stdout:
-	a
-	b
-	c
-	end
-expected-stderr-pattern:
-	/.*continue.*/
----
-name: continue-5
-description:
-	Error if continue argument isn't a number
-stdin:
-	for i in a b c; do echo $i; continue abc; echo more-$i; done
-	echo end
-expected-stdout:
-	a
-expected-exit: e != 0
-expected-stderr-pattern:
-	/.*continue.*/
----
-name: cd-history
-description:
-	Test someone's CD history package (uses arrays)
-stdin:
-	# go to known place before doing anything
-	cd /
-	
-	alias cd=_cd
-	function _cd
-	{
-		typeset -i cdlen i
-		typeset t
-	
-		if [ $# -eq 0 ]
-		then
-			set -- $HOME
-		fi
-	
-		if [ "$CDHISTFILE" -a -r "$CDHISTFILE" ] # if directory history exists
-		then
-			typeset CDHIST
-			i=-1
-			while read -r t			# read directory history file
-			do
-				CDHIST[i=i+1]=$t
-			done <$CDHISTFILE
-		fi
-	
-		if [ "${CDHIST[0]}" != "$PWD" -a "$PWD" != "" ]
-		then
-			_cdins				# insert $PWD into cd history
-		fi
-	
-		cdlen=${#CDHIST[*]}			# number of elements in history
-	
-		case "$@" in
-		-)					# cd to new dir
-			if [ "$OLDPWD" = "" ] && ((cdlen>1))
-			then
-				'print' ${CDHIST[1]}
-				'cd' ${CDHIST[1]}
-				_pwd
-			else
-				'cd' $@
-				_pwd
-			fi
-			;;
-		-l)					# print directory list
-			typeset -R3 num
-			((i=cdlen))
-			while (((i=i-1)>=0))
-			do
-				num=$i
-				'print' "$num ${CDHIST[i]}"
-			done
-			return
-			;;
-		-[0-9]|-[0-9][0-9])			# cd to dir in list
-			if (((i=${1#-})<cdlen))
-			then
-				'print' ${CDHIST[i]}
-				'cd' ${CDHIST[i]}
-				_pwd
-			else
-				'cd' $@
-				_pwd
-			fi
-			;;
-		-*)					# cd to matched dir in list
-			t=${1#-}
-			i=1
-			while ((i<cdlen))
-			do
-				case ${CDHIST[i]} in
-				*$t*)
-					'print' ${CDHIST[i]}
-					'cd' ${CDHIST[i]}
-					_pwd
-					break
-					;;
-				esac
-				((i=i+1))
-			done
-			if ((i>=cdlen))
-			then
-				'cd' $@
-				_pwd
-			fi
-			;;
-		*)					# cd to new dir
-			'cd' $@
-			_pwd
-			;;
-		esac
-	
-		_cdins					# insert $PWD into cd history
-	
-		if [ "$CDHISTFILE" ]
-		then
-			cdlen=${#CDHIST[*]}		# number of elements in history
-	
-			i=0
-			while ((i<cdlen))
-			do
-				'print' -r ${CDHIST[i]}	# update directory history
-				((i=i+1))
-			done >$CDHISTFILE
-		fi
-	}
-	
-	function _cdins					# insert $PWD into cd history
-	{						# meant to be called only by _cd
-		typeset -i i
-	
-		((i=0))
-		while ((i<${#CDHIST[*]}))		# see if dir is already in list
-		do
-			if [ "${CDHIST[$i]}" = "$PWD" ]
-			then
-				break
-			fi
-			((i=i+1))
-		done
-	
-		if ((i>22))				# limit max size of list
-		then
-			i=22
-		fi
-	
-		while (((i=i-1)>=0))			# bump old dirs in list
-		do
-			CDHIST[i+1]=${CDHIST[i]}
-		done
-	
-		CDHIST[0]=$PWD				# insert new directory in list
-	}
-	
-	
-	function _pwd
-	{
-		if [ -n "$ECD" ]
-		then
-			pwd 1>&6
-		fi
-	}
-	# Start of test
-	cd /tmp
-	cd /bin
-	cd /etc
-	cd -
-	cd -2
-	cd -l
-expected-stdout:
-	/bin
-	/tmp
-	  3 /
-	  2 /etc
-	  1 /bin
-	  0 /tmp
----
-name: cd-pe
-description:
-	Check package for cd -Pe
-need-pass: no
-# the mv command fails on Cygwin
-# Hurd aborts the testsuite (permission denied)
-# QNX does not find subdir to cd into
-category: !os:cygwin,!os:gnu,!os:msys,!os:nto,!nosymlink
-file-setup: file 644 "x"
-	mkdir noread noread/target noread/target/subdir
-	ln -s noread link
-	chmod 311 noread
-	cd -P$1 .
-	echo 0=$?
-	bwd=$PWD
-	cd -P$1 link/target
-	echo 1=$?,${PWD#$bwd/}
-	epwd=$($TSHELL -c pwd 2>/dev/null)
-	# This unexpectedly succeeds on GNU/Linux and MidnightBSD
-	#echo pwd=$?,$epwd
-	# expect:	pwd=1,
-	mv ../../noread ../../renamed
-	cd -P$1 subdir
-	echo 2=$?,${PWD#$bwd/}
-	cd $bwd
-	chmod 755 renamed
-	rm -rf noread link renamed
-stdin:
-	export TSHELL="$__progname"
-	"$__progname" x
-	echo "now with -e:"
-	"$__progname" x e
-expected-stdout:
-	0=0
-	1=0,noread/target
-	2=0,noread/target/subdir
-	now with -e:
-	0=0
-	1=0,noread/target
-	2=1,noread/target/subdir
----
-name: env-prompt
-description:
-	Check that prompt not printed when processing ENV
-env-setup: !ENV=./foo!
-file-setup: file 644 "foo"
-	XXX=_
-	PS1=X
-	false && echo hmmm
-need-ctty: yes
-arguments: !-i!
-stdin:
-	echo hi${XXX}there
-expected-stdout:
-	hi_there
-expected-stderr: !
-	XX
----
-name: expand-ugly
-description:
-	Check that weird ${foo+bar} constructs are parsed correctly
-stdin:
-	print '#!'"$__progname"'\nfor x in "$@"; do print -r -- "$x"; done' >pfn
-	print '#!'"$__progname"'\nfor x in "$@"; do print -nr -- "<$x> "; done' >pfs
-	chmod +x pfn pfs
-	(echo 1 ${IFS+'}'z}) 2>/dev/null || echo failed in 1
-	(echo 2 "${IFS+'}'z}") 2>/dev/null || echo failed in 2
-	(echo 3 "foo ${IFS+'bar} baz") 2>/dev/null || echo failed in 3
-	(echo -n '4 '; ./pfn "foo ${IFS+"b   c"} baz") 2>/dev/null || echo failed in 4
-	(echo -n '5 '; ./pfn "foo ${IFS+b   c} baz") 2>/dev/null || echo failed in 5
-	(echo 6 ${IFS+"}"z}) 2>/dev/null || echo failed in 6
-	(echo 7 "${IFS+"}"z}") 2>/dev/null || echo failed in 7
-	(echo 8 "${IFS+\"}\"z}") 2>/dev/null || echo failed in 8
-	(echo 9 "${IFS+\"\}\"z}") 2>/dev/null || echo failed in 9
-	(echo 10 foo ${IFS+'bar} baz'}) 2>/dev/null || echo failed in 10
-	(echo 11 "$(echo "${IFS+'}'z}")") 2>/dev/null || echo failed in 11
-	(echo 12 "$(echo ${IFS+'}'z})") 2>/dev/null || echo failed in 12
-	(echo 13 ${IFS+\}z}) 2>/dev/null || echo failed in 13
-	(echo 14 "${IFS+\}z}") 2>/dev/null || echo failed in 14
-	u=x; (echo -n '15 '; ./pfs "foo ${IFS+a"b$u{ {"{{\}b} c ${IFS+d{}} bar" ${IFS-e{}} baz; echo .) 2>/dev/null || echo failed in 15
-	l=t; (echo 16 ${IFS+h`echo -n i ${IFS+$l}h`ere}) 2>/dev/null || echo failed in 16
-	l=t; (echo 17 ${IFS+h$(echo -n i ${IFS+$l}h)ere}) 2>/dev/null || echo failed in 17
-	l=t; (echo 18 "${IFS+h`echo -n i ${IFS+$l}h`ere}") 2>/dev/null || echo failed in 18
-	l=t; (echo 19 "${IFS+h$(echo -n i ${IFS+$l}h)ere}") 2>/dev/null || echo failed in 19
-	l=t; (echo 20 ${IFS+h`echo -n i "${IFS+$l}"h`ere}) 2>/dev/null || echo failed in 20
-	l=t; (echo 21 ${IFS+h$(echo -n i "${IFS+$l}"h)ere}) 2>/dev/null || echo failed in 21
-	l=t; (echo 22 "${IFS+h`echo -n i "${IFS+$l}"h`ere}") 2>/dev/null || echo failed in 22
-	l=t; (echo 23 "${IFS+h$(echo -n i "${IFS+$l}"h)ere}") 2>/dev/null || echo failed in 23
-	key=value; (echo -n '24 '; ./pfn "${IFS+'$key'}") 2>/dev/null || echo failed in 24
-	key=value; (echo -n '25 '; ./pfn "${IFS+"'$key'"}") 2>/dev/null || echo failed in 25	# ksh93: “'$key'”
-	key=value; (echo -n '26 '; ./pfn ${IFS+'$key'}) 2>/dev/null || echo failed in 26
-	key=value; (echo -n '27 '; ./pfn ${IFS+"'$key'"}) 2>/dev/null || echo failed in 27
-	(echo -n '28 '; ./pfn "${IFS+"'"x ~ x'}'x"'}"x}" #') 2>/dev/null || echo failed in 28
-	u=x; (echo -n '29 '; ./pfs foo ${IFS+a"b$u{ {"{ {\}b} c ${IFS+d{}} bar ${IFS-e{}} baz; echo .) 2>/dev/null || echo failed in 29
-	(echo -n '30 '; ./pfs ${IFS+foo 'b\
-	ar' baz}; echo .) 2>/dev/null || (echo failed in 30; echo failed in 31)
-	(echo -n '32 '; ./pfs ${IFS+foo "b\
-	ar" baz}; echo .) 2>/dev/null || echo failed in 32
-	(echo -n '33 '; ./pfs "${IFS+foo 'b\
-	ar' baz}"; echo .) 2>/dev/null || echo failed in 33
-	(echo -n '34 '; ./pfs "${IFS+foo "b\
-	ar" baz}"; echo .) 2>/dev/null || echo failed in 34
-	(echo -n '35 '; ./pfs ${v=a\ b} x ${v=c\ d}; echo .) 2>/dev/null || echo failed in 35
-	(echo -n '36 '; ./pfs "${v=a\ b}" x "${v=c\ d}"; echo .) 2>/dev/null || echo failed in 36
-	(echo -n '37 '; ./pfs ${v-a\ b} x ${v-c\ d}; echo .) 2>/dev/null || echo failed in 37
-	(echo 38 ${IFS+x'a'y} / "${IFS+x'a'y}" .) 2>/dev/null || echo failed in 38
-	foo="x'a'y"; (echo 39 ${foo%*'a'*} / "${foo%*'a'*}" .) 2>/dev/null || echo failed in 39
-	foo="a b c"; (echo -n '40 '; ./pfs "${foo#a}"; echo .) 2>/dev/null || echo failed in 40
-expected-stdout:
-	1 }z
-	2 ''z}
-	3 foo 'bar baz
-	4 foo b   c baz
-	5 foo b   c baz
-	6 }z
-	7 }z
-	8 ""z}
-	9 "}"z
-	10 foo bar} baz
-	11 ''z}
-	12 }z
-	13 }z
-	14 }z
-	15 <foo abx{ {{{}b c d{} bar> <}> <baz> .
-	16 hi there
-	17 hi there
-	18 hi there
-	19 hi there
-	20 hi there
-	21 hi there
-	22 hi there
-	23 hi there
-	24 'value'
-	25 'value'
-	26 $key
-	27 'value'
-	28 'x ~ x''x}"x}" #
-	29 <foo> <abx{ {{> <{}b> <c> <d{}> <bar> <}> <baz> .
-	30 <foo> <b\
-	ar> <baz> .
-	32 <foo> <bar> <baz> .
-	33 <foo 'bar' baz> .
-	34 <foo bar baz> .
-	35 <a> <b> <x> <a> <b> .
-	36 <a\ b> <x> <a\ b> .
-	37 <a b> <x> <c d> .
-	38 xay / x'a'y .
-	39 x' / x' .
-	40 < b c> .
----
-name: expand-unglob-dblq
-description:
-	Check that regular "${foo+bar}" constructs are parsed correctly
-stdin:
-	u=x
-	tl_norm() {
-		v=$2
-		test x"$v" = x"-" && unset v
-		(echo "$1 plus norm foo ${v+'bar'} baz")
-		(echo "$1 dash norm foo ${v-'bar'} baz")
-		(echo "$1 eqal norm foo ${v='bar'} baz")
-		(echo "$1 qstn norm foo ${v?'bar'} baz") 2>/dev/null || \
-		    echo "$1 qstn norm -> error"
-		(echo "$1 PLUS norm foo ${v:+'bar'} baz")
-		(echo "$1 DASH norm foo ${v:-'bar'} baz")
-		(echo "$1 EQAL norm foo ${v:='bar'} baz")
-		(echo "$1 QSTN norm foo ${v:?'bar'} baz") 2>/dev/null || \
-		    echo "$1 QSTN norm -> error"
-	}
-	tl_paren() {
-		v=$2
-		test x"$v" = x"-" && unset v
-		(echo "$1 plus parn foo ${v+(bar)} baz")
-		(echo "$1 dash parn foo ${v-(bar)} baz")
-		(echo "$1 eqal parn foo ${v=(bar)} baz")
-		(echo "$1 qstn parn foo ${v?(bar)} baz") 2>/dev/null || \
-		    echo "$1 qstn parn -> error"
-		(echo "$1 PLUS parn foo ${v:+(bar)} baz")
-		(echo "$1 DASH parn foo ${v:-(bar)} baz")
-		(echo "$1 EQAL parn foo ${v:=(bar)} baz")
-		(echo "$1 QSTN parn foo ${v:?(bar)} baz") 2>/dev/null || \
-		    echo "$1 QSTN parn -> error"
-	}
-	tl_brace() {
-		v=$2
-		test x"$v" = x"-" && unset v
-		(echo "$1 plus brac foo ${v+a$u{{{\}b} c ${v+d{}} baz")
-		(echo "$1 dash brac foo ${v-a$u{{{\}b} c ${v-d{}} baz")
-		(echo "$1 eqal brac foo ${v=a$u{{{\}b} c ${v=d{}} baz")
-		(echo "$1 qstn brac foo ${v?a$u{{{\}b} c ${v?d{}} baz") 2>/dev/null || \
-		    echo "$1 qstn brac -> error"
-		(echo "$1 PLUS brac foo ${v:+a$u{{{\}b} c ${v:+d{}} baz")
-		(echo "$1 DASH brac foo ${v:-a$u{{{\}b} c ${v:-d{}} baz")
-		(echo "$1 EQAL brac foo ${v:=a$u{{{\}b} c ${v:=d{}} baz")
-		(echo "$1 QSTN brac foo ${v:?a$u{{{\}b} c ${v:?d{}} baz") 2>/dev/null || \
-		    echo "$1 QSTN brac -> error"
-	}
-	tl_norm 1 -
-	tl_norm 2 ''
-	tl_norm 3 x
-	tl_paren 4 -
-	tl_paren 5 ''
-	tl_paren 6 x
-	tl_brace 7 -
-	tl_brace 8 ''
-	tl_brace 9 x
-expected-stdout:
-	1 plus norm foo  baz
-	1 dash norm foo 'bar' baz
-	1 eqal norm foo 'bar' baz
-	1 qstn norm -> error
-	1 PLUS norm foo  baz
-	1 DASH norm foo 'bar' baz
-	1 EQAL norm foo 'bar' baz
-	1 QSTN norm -> error
-	2 plus norm foo 'bar' baz
-	2 dash norm foo  baz
-	2 eqal norm foo  baz
-	2 qstn norm foo  baz
-	2 PLUS norm foo  baz
-	2 DASH norm foo 'bar' baz
-	2 EQAL norm foo 'bar' baz
-	2 QSTN norm -> error
-	3 plus norm foo 'bar' baz
-	3 dash norm foo x baz
-	3 eqal norm foo x baz
-	3 qstn norm foo x baz
-	3 PLUS norm foo 'bar' baz
-	3 DASH norm foo x baz
-	3 EQAL norm foo x baz
-	3 QSTN norm foo x baz
-	4 plus parn foo  baz
-	4 dash parn foo (bar) baz
-	4 eqal parn foo (bar) baz
-	4 qstn parn -> error
-	4 PLUS parn foo  baz
-	4 DASH parn foo (bar) baz
-	4 EQAL parn foo (bar) baz
-	4 QSTN parn -> error
-	5 plus parn foo (bar) baz
-	5 dash parn foo  baz
-	5 eqal parn foo  baz
-	5 qstn parn foo  baz
-	5 PLUS parn foo  baz
-	5 DASH parn foo (bar) baz
-	5 EQAL parn foo (bar) baz
-	5 QSTN parn -> error
-	6 plus parn foo (bar) baz
-	6 dash parn foo x baz
-	6 eqal parn foo x baz
-	6 qstn parn foo x baz
-	6 PLUS parn foo (bar) baz
-	6 DASH parn foo x baz
-	6 EQAL parn foo x baz
-	6 QSTN parn foo x baz
-	7 plus brac foo  c } baz
-	7 dash brac foo ax{{{}b c d{} baz
-	7 eqal brac foo ax{{{}b c ax{{{}b} baz
-	7 qstn brac -> error
-	7 PLUS brac foo  c } baz
-	7 DASH brac foo ax{{{}b c d{} baz
-	7 EQAL brac foo ax{{{}b c ax{{{}b} baz
-	7 QSTN brac -> error
-	8 plus brac foo ax{{{}b c d{} baz
-	8 dash brac foo  c } baz
-	8 eqal brac foo  c } baz
-	8 qstn brac foo  c } baz
-	8 PLUS brac foo  c } baz
-	8 DASH brac foo ax{{{}b c d{} baz
-	8 EQAL brac foo ax{{{}b c ax{{{}b} baz
-	8 QSTN brac -> error
-	9 plus brac foo ax{{{}b c d{} baz
-	9 dash brac foo x c x} baz
-	9 eqal brac foo x c x} baz
-	9 qstn brac foo x c x} baz
-	9 PLUS brac foo ax{{{}b c d{} baz
-	9 DASH brac foo x c x} baz
-	9 EQAL brac foo x c x} baz
-	9 QSTN brac foo x c x} baz
----
-name: expand-unglob-unq
-description:
-	Check that regular ${foo+bar} constructs are parsed correctly
-stdin:
-	u=x
-	tl_norm() {
-		v=$2
-		test x"$v" = x"-" && unset v
-		(echo $1 plus norm foo ${v+'bar'} baz)
-		(echo $1 dash norm foo ${v-'bar'} baz)
-		(echo $1 eqal norm foo ${v='bar'} baz)
-		(echo $1 qstn norm foo ${v?'bar'} baz) 2>/dev/null || \
-		    echo "$1 qstn norm -> error"
-		(echo $1 PLUS norm foo ${v:+'bar'} baz)
-		(echo $1 DASH norm foo ${v:-'bar'} baz)
-		(echo $1 EQAL norm foo ${v:='bar'} baz)
-		(echo $1 QSTN norm foo ${v:?'bar'} baz) 2>/dev/null || \
-		    echo "$1 QSTN norm -> error"
-	}
-	tl_paren() {
-		v=$2
-		test x"$v" = x"-" && unset v
-		(echo $1 plus parn foo ${v+\(bar')'} baz)
-		(echo $1 dash parn foo ${v-\(bar')'} baz)
-		(echo $1 eqal parn foo ${v=\(bar')'} baz)
-		(echo $1 qstn parn foo ${v?\(bar')'} baz) 2>/dev/null || \
-		    echo "$1 qstn parn -> error"
-		(echo $1 PLUS parn foo ${v:+\(bar')'} baz)
-		(echo $1 DASH parn foo ${v:-\(bar')'} baz)
-		(echo $1 EQAL parn foo ${v:=\(bar')'} baz)
-		(echo $1 QSTN parn foo ${v:?\(bar')'} baz) 2>/dev/null || \
-		    echo "$1 QSTN parn -> error"
-	}
-	tl_brace() {
-		v=$2
-		test x"$v" = x"-" && unset v
-		(echo $1 plus brac foo ${v+a$u{{{\}b} c ${v+d{}} baz)
-		(echo $1 dash brac foo ${v-a$u{{{\}b} c ${v-d{}} baz)
-		(echo $1 eqal brac foo ${v=a$u{{{\}b} c ${v=d{}} baz)
-		(echo $1 qstn brac foo ${v?a$u{{{\}b} c ${v?d{}} baz) 2>/dev/null || \
-		    echo "$1 qstn brac -> error"
-		(echo $1 PLUS brac foo ${v:+a$u{{{\}b} c ${v:+d{}} baz)
-		(echo $1 DASH brac foo ${v:-a$u{{{\}b} c ${v:-d{}} baz)
-		(echo $1 EQAL brac foo ${v:=a$u{{{\}b} c ${v:=d{}} baz)
-		(echo $1 QSTN brac foo ${v:?a$u{{{\}b} c ${v:?d{}} baz) 2>/dev/null || \
-		    echo "$1 QSTN brac -> error"
-	}
-	tl_norm 1 -
-	tl_norm 2 ''
-	tl_norm 3 x
-	tl_paren 4 -
-	tl_paren 5 ''
-	tl_paren 6 x
-	tl_brace 7 -
-	tl_brace 8 ''
-	tl_brace 9 x
-expected-stdout:
-	1 plus norm foo baz
-	1 dash norm foo bar baz
-	1 eqal norm foo bar baz
-	1 qstn norm -> error
-	1 PLUS norm foo baz
-	1 DASH norm foo bar baz
-	1 EQAL norm foo bar baz
-	1 QSTN norm -> error
-	2 plus norm foo bar baz
-	2 dash norm foo baz
-	2 eqal norm foo baz
-	2 qstn norm foo baz
-	2 PLUS norm foo baz
-	2 DASH norm foo bar baz
-	2 EQAL norm foo bar baz
-	2 QSTN norm -> error
-	3 plus norm foo bar baz
-	3 dash norm foo x baz
-	3 eqal norm foo x baz
-	3 qstn norm foo x baz
-	3 PLUS norm foo bar baz
-	3 DASH norm foo x baz
-	3 EQAL norm foo x baz
-	3 QSTN norm foo x baz
-	4 plus parn foo baz
-	4 dash parn foo (bar) baz
-	4 eqal parn foo (bar) baz
-	4 qstn parn -> error
-	4 PLUS parn foo baz
-	4 DASH parn foo (bar) baz
-	4 EQAL parn foo (bar) baz
-	4 QSTN parn -> error
-	5 plus parn foo (bar) baz
-	5 dash parn foo baz
-	5 eqal parn foo baz
-	5 qstn parn foo baz
-	5 PLUS parn foo baz
-	5 DASH parn foo (bar) baz
-	5 EQAL parn foo (bar) baz
-	5 QSTN parn -> error
-	6 plus parn foo (bar) baz
-	6 dash parn foo x baz
-	6 eqal parn foo x baz
-	6 qstn parn foo x baz
-	6 PLUS parn foo (bar) baz
-	6 DASH parn foo x baz
-	6 EQAL parn foo x baz
-	6 QSTN parn foo x baz
-	7 plus brac foo c } baz
-	7 dash brac foo ax{{{}b c d{} baz
-	7 eqal brac foo ax{{{}b c ax{{{}b} baz
-	7 qstn brac -> error
-	7 PLUS brac foo c } baz
-	7 DASH brac foo ax{{{}b c d{} baz
-	7 EQAL brac foo ax{{{}b c ax{{{}b} baz
-	7 QSTN brac -> error
-	8 plus brac foo ax{{{}b c d{} baz
-	8 dash brac foo c } baz
-	8 eqal brac foo c } baz
-	8 qstn brac foo c } baz
-	8 PLUS brac foo c } baz
-	8 DASH brac foo ax{{{}b c d{} baz
-	8 EQAL brac foo ax{{{}b c ax{{{}b} baz
-	8 QSTN brac -> error
-	9 plus brac foo ax{{{}b c d{} baz
-	9 dash brac foo x c x} baz
-	9 eqal brac foo x c x} baz
-	9 qstn brac foo x c x} baz
-	9 PLUS brac foo ax{{{}b c d{} baz
-	9 DASH brac foo x c x} baz
-	9 EQAL brac foo x c x} baz
-	9 QSTN brac foo x c x} baz
----
-name: expand-threecolons-dblq
-description:
-	Check for a particular thing that used to segfault
-stdin:
-	TEST=1234
-	echo "${TEST:1:2:3}"
-	echo $? but still living
-expected-stderr-pattern:
-	/bad substitution/
-expected-exit: 1
----
-name: expand-threecolons-unq
-description:
-	Check for a particular thing that used to not error out
-stdin:
-	TEST=1234
-	echo ${TEST:1:2:3}
-	echo $? but still living
-expected-stderr-pattern:
-	/bad substitution/
-expected-exit: 1
----
-name: expand-weird-1
-description:
-	Check corner case of trim expansion vs. $# vs. ${#var}
-stdin:
-	set 1 2 3 4 5 6 7 8 9 10 11
-	echo ${#}	# value of $#
-	echo ${##}	# length of $#
-	echo ${##1}	# $# trimmed 1
-	set 1 2 3 4 5 6 7 8 9 10 11 12
-	echo ${##1}
-expected-stdout:
-	11
-	2
-	1
-	2
----
-name: expand-weird-2
-description:
-	Check corner case of ${var?} vs. ${#var}
-stdin:
-	(exit 0)
-	echo $? = ${#?} .
-	(exit 111)
-	echo $? = ${#?} .
-expected-stdout:
-	0 = 1 .
-	111 = 3 .
----
-name: expand-weird-3
-description:
-	Check that trimming works with positional parameters (Debian #48453)
-stdin:
-	A=9999-02
-	B=9999
-	echo 1=${A#$B?}.
-	set -- $A $B
-	echo 2=${1#$2?}.
-expected-stdout:
-	1=02.
-	2=02.
----
-name: eglob-bad-1
-description:
-	Check that globbing isn't done when glob has syntax error
-file-setup: file 644 "abcx"
-file-setup: file 644 "abcz"
-file-setup: file 644 "bbc"
-stdin:
-	echo !([*)*
-	echo +(a|b[)*
-expected-stdout:
-	!([*)*
-	+(a|b[)*
----
-name: eglob-bad-2
-description:
-	Check that globbing isn't done when glob has syntax error
-	(AT&T ksh fails this test)
-file-setup: file 644 "abcx"
-file-setup: file 644 "abcz"
-file-setup: file 644 "bbc"
-stdin:
-	echo [a*(]*)z
-expected-stdout:
-	[a*(]*)z
----
-name: eglob-infinite-plus
-description:
-	Check that shell doesn't go into infinite loop expanding +(...)
-	expressions.
-file-setup: file 644 "abc"
-time-limit: 3
-stdin:
-	echo +()c
-	echo +()x
-	echo +(*)c
-	echo +(*)x
-expected-stdout:
-	+()c
-	+()x
-	abc
-	+(*)x
----
-name: eglob-subst-1
-description:
-	Check that eglobbing isn't done on substitution results
-file-setup: file 644 "abc"
-stdin:
-	x='@(*)'
-	echo $x
-expected-stdout:
-	@(*)
----
-name: eglob-nomatch-1
-description:
-	Check that the pattern doesn't match
-stdin:
-	echo 1: no-file+(a|b)stuff
-	echo 2: no-file+(a*(c)|b)stuff
-	echo 3: no-file+((((c)))|b)stuff
-expected-stdout:
-	1: no-file+(a|b)stuff
-	2: no-file+(a*(c)|b)stuff
-	3: no-file+((((c)))|b)stuff
----
-name: eglob-match-1
-description:
-	Check that the pattern matches correctly
-file-setup: file 644 "abd"
-file-setup: file 644 "acd"
-file-setup: file 644 "abac"
-stdin:
-	echo 1: a+(b|c)d
-	echo 2: a!(@(b|B))d
-	echo 3: *(a(b|c))		# (...|...) can be used within X(..)
-	echo 4: a[b*(foo|bar)]d		# patterns not special inside [...]
-expected-stdout:
-	1: abd acd
-	2: acd
-	3: abac
-	4: abd
----
-name: eglob-case-1
-description:
-	Simple negation tests
-stdin:
-	case foo in !(foo|bar)) echo yes;; *) echo no;; esac
-	case bar in !(foo|bar)) echo yes;; *) echo no;; esac
-expected-stdout:
-	no
-	no
----
-name: eglob-case-2
-description:
-	Simple kleene tests
-stdin:
-	case foo in *(a|b[)) echo yes;; *) echo no;; esac
-	case foo in *(a|b[)|f*) echo yes;; *) echo no;; esac
-	case '*(a|b[)' in *(a|b[)) echo yes;; *) echo no;; esac
-expected-stdout:
-	no
-	yes
-	yes
----
-name: eglob-trim-1
-description:
-	Eglobbing in trim expressions...
-	(AT&T ksh fails this - docs say # matches shortest string, ## matches
-	longest...)
-stdin:
-	x=abcdef
-	echo 1: ${x#a|abc}
-	echo 2: ${x##a|abc}
-	echo 3: ${x%def|f}
-	echo 4: ${x%%f|def}
-expected-stdout:
-	1: bcdef
-	2: def
-	3: abcde
-	4: abc
----
-name: eglob-trim-2
-description:
-	Check eglobbing works in trims...
-stdin:
-	x=abcdef
-	echo 1: ${x#*(a|b)cd}
-	echo 2: "${x#*(a|b)cd}"
-	echo 3: ${x#"*(a|b)cd"}
-	echo 4: ${x#a(b|c)}
-expected-stdout:
-	1: ef
-	2: ef
-	3: abcdef
-	4: cdef
----
-name: eglob-trim-3
-description:
-	Check eglobbing works in trims, for Korn Shell
-	Ensure eglobbing does not work for reduced-feature /bin/sh
-stdin:
-	set +o sh
-	x=foobar
-	y=foobaz
-	z=fooba\?
-	echo "<${x%bar|baz},${y%bar|baz},${z%\?}>"
-	echo "<${x%ba(r|z)},${y%ba(r|z)}>"
-	set -o sh
-	echo "<${x%bar|baz},${y%bar|baz},${z%\?}>"
-	z='foo(bar'
-	echo "<${z%(*}>"
-expected-stdout:
-	<foo,foo,fooba>
-	<foo,foo>
-	<foobar,foobaz,fooba>
-	<foo>
----
-name: eglob-substrpl-1
-description:
-	Check eglobbing works in substs... and they work at all
-stdin:
-	[[ -n $BASH_VERSION ]] && shopt -s extglob
-	x=1222321_ab/cde_b/c_1221
-	y=xyz
-	echo 1: ${x/2}
-	echo 2: ${x//2}
-	echo 3: ${x/+(2)}
-	echo 4: ${x//+(2)}
-	echo 5: ${x/2/4}
-	echo 6: ${x//2/4}
-	echo 7: ${x/+(2)/4}
-	echo 8: ${x//+(2)/4}
-	echo 9: ${x/b/c/e/f}
-	echo 10: ${x/b\/c/e/f}
-	echo 11: ${x/b\/c/e\/f}
-	echo 12: ${x/b\/c/e\\/f}
-	echo 13: ${x/b\\/c/e\\/f}
-	echo 14: ${x//b/c/e/f}
-	echo 15: ${x//b\/c/e/f}
-	echo 16: ${x//b\/c/e\/f}
-	echo 17: ${x//b\/c/e\\/f}
-	echo 18: ${x//b\\/c/e\\/f}
-	echo 19: ${x/b\/*\/c/x}
-	echo 20: ${x/\//.}
-	echo 21: ${x//\//.}
-	echo 22: ${x///.}
-	echo 23: ${x//#1/9}
-	echo 24: ${x//%1/9}
-	echo 25: ${x//\%1/9}
-	echo 26: ${x//\\%1/9}
-	echo 27: ${x//\a/9}
-	echo 28: ${x//\\a/9}
-	echo 29: ${x/2/$y}
-expected-stdout:
-	1: 122321_ab/cde_b/c_1221
-	2: 131_ab/cde_b/c_11
-	3: 1321_ab/cde_b/c_1221
-	4: 131_ab/cde_b/c_11
-	5: 1422321_ab/cde_b/c_1221
-	6: 1444341_ab/cde_b/c_1441
-	7: 14321_ab/cde_b/c_1221
-	8: 14341_ab/cde_b/c_141
-	9: 1222321_ac/e/f/cde_b/c_1221
-	10: 1222321_ae/fde_b/c_1221
-	11: 1222321_ae/fde_b/c_1221
-	12: 1222321_ae\/fde_b/c_1221
-	13: 1222321_ab/cde_b/c_1221
-	14: 1222321_ac/e/f/cde_c/e/f/c_1221
-	15: 1222321_ae/fde_e/f_1221
-	16: 1222321_ae/fde_e/f_1221
-	17: 1222321_ae\/fde_e\/f_1221
-	18: 1222321_ab/cde_b/c_1221
-	19: 1222321_ax_1221
-	20: 1222321_ab.cde_b/c_1221
-	21: 1222321_ab.cde_b.c_1221
-	22: 1222321_ab/cde_b/c_1221
-	23: 9222321_ab/cde_b/c_1221
-	24: 1222321_ab/cde_b/c_1229
-	25: 1222321_ab/cde_b/c_1229
-	26: 1222321_ab/cde_b/c_1221
-	27: 1222321_9b/cde_b/c_1221
-	28: 1222321_9b/cde_b/c_1221
-	29: 1xyz22321_ab/cde_b/c_1221
----
-name: eglob-substrpl-2
-description:
-	Check anchored substring replacement works, corner cases
-stdin:
-	foo=123
-	echo 1: ${foo/#/x}
-	echo 2: ${foo/%/x}
-	echo 3: ${foo/#/}
-	echo 4: ${foo/#}
-	echo 5: ${foo/%/}
-	echo 6: ${foo/%}
-expected-stdout:
-	1: x123
-	2: 123x
-	3: 123
-	4: 123
-	5: 123
-	6: 123
----
-name: eglob-substrpl-3a
-description:
-	Check substring replacement works with variables and slashes, too
-stdin:
-	pfx=/home/user
-	wd=/home/user/tmp
-	echo "${wd/#$pfx/~}"
-	echo "${wd/#\$pfx/~}"
-	echo "${wd/#"$pfx"/~}"
-	echo "${wd/#'$pfx'/~}"
-	echo "${wd/#"\$pfx"/~}"
-	echo "${wd/#'\$pfx'/~}"
-expected-stdout:
-	~/tmp
-	/home/user/tmp
-	~/tmp
-	/home/user/tmp
-	/home/user/tmp
-	/home/user/tmp
----
-name: eglob-substrpl-3b
-description:
-	More of this, bash fails it (bash4 passes)
-stdin:
-	pfx=/home/user
-	wd=/home/user/tmp
-	echo "${wd/#$(echo /home/user)/~}"
-	echo "${wd/#"$(echo /home/user)"/~}"
-	echo "${wd/#'$(echo /home/user)'/~}"
-expected-stdout:
-	~/tmp
-	~/tmp
-	/home/user/tmp
----
-name: eglob-substrpl-3c
-description:
-	Even more weird cases
-stdin:
-	pfx=/home/user
-	wd='$pfx/tmp'
-	echo 1: ${wd/#$pfx/~}
-	echo 2: ${wd/#\$pfx/~}
-	echo 3: ${wd/#"$pfx"/~}
-	echo 4: ${wd/#'$pfx'/~}
-	echo 5: ${wd/#"\$pfx"/~}
-	echo 6: ${wd/#'\$pfx'/~}
-	ts='a/ba/b$tp$tp_a/b$tp_*(a/b)_*($tp)'
-	tp=a/b
-	tr=c/d
-	[[ -n $BASH_VERSION ]] && shopt -s extglob
-	echo 7: ${ts/a\/b/$tr}
-	echo 8: ${ts/a\/b/\$tr}
-	echo 9: ${ts/$tp/$tr}
-	echo 10: ${ts/\$tp/$tr}
-	echo 11: ${ts/\\$tp/$tr}
-	echo 12: ${ts/$tp/c/d}
-	echo 13: ${ts/$tp/c\/d}
-	echo 14: ${ts/$tp/c\\/d}
-	echo 15: ${ts/+(a\/b)/$tr}
-	echo 16: ${ts/+(a\/b)/\$tr}
-	echo 17: ${ts/+($tp)/$tr}
-	echo 18: ${ts/+($tp)/c/d}
-	echo 19: ${ts/+($tp)/c\/d}
-	echo 25: ${ts//a\/b/$tr}
-	echo 26: ${ts//a\/b/\$tr}
-	echo 27: ${ts//$tp/$tr}
-	echo 28: ${ts//$tp/c/d}
-	echo 29: ${ts//$tp/c\/d}
-	echo 30: ${ts//+(a\/b)/$tr}
-	echo 31: ${ts//+(a\/b)/\$tr}
-	echo 32: ${ts//+($tp)/$tr}
-	echo 33: ${ts//+($tp)/c/d}
-	echo 34: ${ts//+($tp)/c\/d}
-	tp="+($tp)"
-	echo 40: ${ts/$tp/$tr}
-	echo 41: ${ts//$tp/$tr}
-expected-stdout:
-	1: $pfx/tmp
-	2: ~/tmp
-	3: $pfx/tmp
-	4: ~/tmp
-	5: ~/tmp
-	6: ~/tmp
-	7: c/da/b$tp$tp_a/b$tp_*(a/b)_*($tp)
-	8: $tra/b$tp$tp_a/b$tp_*(a/b)_*($tp)
-	9: c/da/b$tp$tp_a/b$tp_*(a/b)_*($tp)
-	10: a/ba/bc/d$tp_a/b$tp_*(a/b)_*($tp)
-	11: c/da/b$tp$tp_a/b$tp_*(a/b)_*($tp)
-	12: c/da/b$tp$tp_a/b$tp_*(a/b)_*($tp)
-	13: c/da/b$tp$tp_a/b$tp_*(a/b)_*($tp)
-	14: c\/da/b$tp$tp_a/b$tp_*(a/b)_*($tp)
-	15: c/d$tp$tp_a/b$tp_*(a/b)_*($tp)
-	16: $tr$tp$tp_a/b$tp_*(a/b)_*($tp)
-	17: c/d$tp$tp_a/b$tp_*(a/b)_*($tp)
-	18: c/d$tp$tp_a/b$tp_*(a/b)_*($tp)
-	19: c/d$tp$tp_a/b$tp_*(a/b)_*($tp)
-	25: c/dc/d$tp$tp_c/d$tp_*(c/d)_*($tp)
-	26: $tr$tr$tp$tp_$tr$tp_*($tr)_*($tp)
-	27: c/dc/d$tp$tp_c/d$tp_*(c/d)_*($tp)
-	28: c/dc/d$tp$tp_c/d$tp_*(c/d)_*($tp)
-	29: c/dc/d$tp$tp_c/d$tp_*(c/d)_*($tp)
-	30: c/d$tp$tp_c/d$tp_*(c/d)_*($tp)
-	31: $tr$tp$tp_$tr$tp_*($tr)_*($tp)
-	32: c/d$tp$tp_c/d$tp_*(c/d)_*($tp)
-	33: c/d$tp$tp_c/d$tp_*(c/d)_*($tp)
-	34: c/d$tp$tp_c/d$tp_*(c/d)_*($tp)
-	40: a/ba/b$tp$tp_a/b$tp_*(a/b)_*($tp)
-	41: a/ba/b$tp$tp_a/b$tp_*(a/b)_*($tp)
-#	This is what GNU bash does:
-#	40: c/d$tp$tp_a/b$tp_*(a/b)_*($tp)
-#	41: c/d$tp$tp_c/d$tp_*(c/d)_*($tp)
----
-name: eglob-utf8-1
-description:
-	UTF-8 mode differences for eglobbing
-stdin:
-	s=blöd
-	set +U
-	print 1: ${s%???} .
-	print 2: ${s/b???d/x} .
-	set -U
-	print 3: ${s%???} .
-	print 4: ${s/b??d/x} .
-	x=nö
-	print 5: ${x%?} ${x%%?} .
-	x=äh
-	print 6: ${x#?} ${x##?} .
-	x=\x81\x82
-	print 7: ${x%?} ${x%%?} .
-	x=mä\x80
-	print 8: ${x%?} ${x%%?} .
-	x=何
-	print 9: ${x%?} ${x%%?} .
-expected-stdout:
-	1: bl .
-	2: x .
-	3: b .
-	4: x .
-	5: n n .
-	6: h h .
-	7: \x81 \x81 .
-	8: mä mä .
-	9: .
----
-name: glob-bad-1
-description:
-	Check that globbing isn't done when glob has syntax error
-file-setup: dir 755 "[x"
-file-setup: file 644 "[x/foo"
-stdin:
-	echo [*
-	echo *[x
-	echo [x/*
-expected-stdout:
-	[*
-	*[x
-	[x/foo
----
-name: glob-bad-2
-description:
-	Check that symbolic links aren't stat()'d
-# breaks on FreeMiNT (cannot unlink dangling symlinks)
-# breaks on MSYS (does not support symlinks)
-# breaks on Dell UNIX 4.0 R2.2 (SVR4) where unlink also fails
-category: !os:mint,!os:msys,!os:svr4.0,!nosymlink
-file-setup: dir 755 "dir"
-file-setup: symlink 644 "dir/abc"
-	non-existent-file
-stdin:
-	echo d*/*
-	echo d*/abc
-expected-stdout:
-	dir/abc
-	dir/abc
----
-name: glob-range-1
-description:
-	Test range matching
-file-setup: file 644 ".bc"
-file-setup: file 644 "abc"
-file-setup: file 644 "bbc"
-file-setup: file 644 "cbc"
-file-setup: file 644 "-bc"
-stdin:
-	echo [ab-]*
-	echo [-ab]*
-	echo [!-ab]*
-	echo [!ab]*
-	echo []ab]*
-expected-stdout:
-	-bc abc bbc
-	-bc abc bbc
-	cbc
-	-bc cbc
-	abc bbc
----
-name: glob-range-2
-description:
-	Test range matching
-	(AT&T ksh fails this; POSIX says invalid)
-file-setup: file 644 "abc"
-stdin:
-	echo [a--]*
-expected-stdout:
-	[a--]*
----
-name: glob-range-3
-description:
-	Check that globbing matches the right things...
-# breaks on Mac OSX (HFS+ non-standard Unicode canonical decomposition)
-# breaks on Cygwin 1.7 (files are now UTF-16 or something)
-# breaks on QNX 6.4.1 (says RT)
-category: !os:cygwin,!os:darwin,!os:msys,!os:nto
-need-pass: no
-file-setup: file 644 "a\xC2c"
-stdin:
-	echo a[\xC1-\xDA]*
-expected-stdout:
-	a\xC2c
----
-name: glob-range-4
-description:
-	Results unspecified according to POSIX
-file-setup: file 644 ".bc"
-stdin:
-	echo [a.]*
-expected-stdout:
-	[a.]*
----
-name: glob-range-5
-description:
-	Results unspecified according to POSIX
-	(AT&T ksh treats this like [a-cc-e]*)
-file-setup: file 644 "abc"
-file-setup: file 644 "bbc"
-file-setup: file 644 "cbc"
-file-setup: file 644 "dbc"
-file-setup: file 644 "ebc"
-file-setup: file 644 "-bc"
-stdin:
-	echo [a-c-e]*
-expected-stdout:
-	-bc abc bbc cbc ebc
----
-name: heredoc-1
-description:
-	Check ordering/content of redundent here documents.
-stdin:
-	cat << EOF1 << EOF2
-	hi
-	EOF1
-	there
-	EOF2
-expected-stdout:
-	there
----
-name: heredoc-2
-description:
-	Check quoted here-doc is protected.
-stdin:
-	a=foo
-	cat << 'EOF'
-	hi\
-	there$a
-	stuff
-	EO\
-	F
-	EOF
-expected-stdout:
-	hi\
-	there$a
-	stuff
-	EO\
-	F
----
-name: heredoc-3
-description:
-	Check that newline isn't needed after heredoc-delimiter marker.
-stdin: !
-	cat << EOF
-	hi
-	there
-	EOF
-expected-stdout:
-	hi
-	there
----
-name: heredoc-4
-description:
-	Check that an error occurs if the heredoc-delimiter is missing.
-stdin: !
-	cat << EOF
-	hi
-	there
-expected-exit: e > 0
-expected-stderr-pattern: /.*/
----
-name: heredoc-5
-description:
-	Check that backslash quotes a $, ` and \ and kills a \newline
-stdin:
-	a=BAD
-	b=ok
-	cat << EOF
-	h\${a}i
-	h\\${b}i
-	th\`echo not-run\`ere
-	th\\`echo is-run`ere
-	fol\\ks
-	more\\
-	last \
-	line
-	EOF
-expected-stdout:
-	h${a}i
-	h\oki
-	th`echo not-run`ere
-	th\is-runere
-	fol\ks
-	more\
-	last line
----
-name: heredoc-6
-description:
-	Check that \newline in initial here-delim word doesn't imply
-	a quoted here-doc.
-stdin:
-	a=i
-	cat << EO\
-	F
-	h$a
-	there
-	EOF
-expected-stdout:
-	hi
-	there
----
-name: heredoc-7
-description:
-	Check that double quoted $ expressions in here delimiters are
-	not expanded and match the delimiter.
-	POSIX says only quote removal is applied to the delimiter.
-stdin:
-	a=b
-	cat << "E$a"
-	hi
-	h$a
-	hb
-	E$a
-	echo done
-expected-stdout:
-	hi
-	h$a
-	hb
-	done
----
-name: heredoc-8
-description:
-	Check that double quoted escaped $ expressions in here
-	delimiters are not expanded and match the delimiter.
-	POSIX says only quote removal is applied to the delimiter
-	(\ counts as a quote).
-stdin:
-	a=b
-	cat << "E\$a"
-	hi
-	h$a
-	h\$a
-	hb
-	h\b
-	E$a
-	echo done
-expected-stdout:
-	hi
-	h$a
-	h\$a
-	hb
-	h\b
-	done
----
-name: heredoc-9a
-description:
-	Check that here strings work.
-stdin:
-	bar="bar
-		baz"
-	tr abcdefghijklmnopqrstuvwxyz nopqrstuvwxyzabcdefghijklm <<<foo
-	"$__progname" -c "tr abcdefghijklmnopqrstuvwxyz nopqrstuvwxyzabcdefghijklm <<<foo"
-	tr abcdefghijklmnopqrstuvwxyz nopqrstuvwxyzabcdefghijklm <<<"$bar"
-	tr abcdefghijklmnopqrstuvwxyz nopqrstuvwxyzabcdefghijklm <<<'$bar'
-	tr abcdefghijklmnopqrstuvwxyz nopqrstuvwxyzabcdefghijklm <<<\$bar
-	tr abcdefghijklmnopqrstuvwxyz nopqrstuvwxyzabcdefghijklm <<<-foo
-	tr abcdefghijklmnopqrstuvwxyz nopqrstuvwxyzabcdefghijklm <<<"$(echo "foo bar")"
-expected-stdout:
-	sbb
-	sbb
-	one
-		onm
-	$one
-	$one
-	-sbb
-	sbb one
----
-name: heredoc-9b
-description:
-	Check that a corner case of here strings works like bash
-stdin:
-	fnord=42
-	bar="bar
-		 \$fnord baz"
-	tr abcdefghijklmnopqrstuvwxyz nopqrstuvwxyzabcdefghijklm <<<$bar
-expected-stdout:
-	one $sabeq onm
-category: bash
----
-name: heredoc-9c
-description:
-	Check that a corner case of here strings works like ksh93, zsh
-stdin:
-	fnord=42
-	bar="bar
-		 \$fnord baz"
-	tr abcdefghijklmnopqrstuvwxyz nopqrstuvwxyzabcdefghijklm <<<$bar
-expected-stdout:
-	one
-		 $sabeq onm
----
-name: heredoc-9d
-description:
-	Check another corner case of here strings
-stdin:
-	tr abcdefghijklmnopqrstuvwxyz nopqrstuvwxyzabcdefghijklm <<< bar
-expected-stdout:
-	one
----
-name: heredoc-9e
-description:
-	Check here string related regression with multiple iops
-stdin:
-	echo $(tr r z <<<'bar' 2>/dev/null)
-expected-stdout:
-	baz
----
-name: heredoc-10
-description:
-	Check direct here document assignment
-stdin:
-	x=u
-	va=<<EOF
-	=a $x \x40=
-	EOF
-	vb=<<'EOF'
-	=b $x \x40=
-	EOF
-	function foo {
-		vc=<<-EOF
-			=c $x \x40=
-		EOF
-	}
-	fnd=$(typeset -f foo)
-	print -r -- "$fnd"
-	function foo {
-		echo blub
-	}
-	foo
-	eval "$fnd"
-	foo
-	# rather nonsensical, but…
-	vd=<<<"=d $x \x40="
-	ve=<<<'=e $x \x40='
-	vf=<<<$'=f $x \x40='
-	# now check
-	print -r -- "| va={$va} vb={$vb} vc={$vc} vd={$vd} ve={$ve} vf={$vf} |"
-expected-stdout:
-	function foo {
-		vc=<<-EOF
-	=c $x \x40=
-	EOF
-	
-	} 
-	blub
-	| va={=a u \x40=
-	} vb={=b $x \x40=
-	} vc={=c u \x40=
-	} vd={=d u \x40=
-	} ve={=e $x \x40=
-	} vf={=f $x @=
-	} |
----
-name: heredoc-11
-description:
-	Check here documents with no or empty delimiter
-stdin:
-	x=u
-	va=<<
-	=a $x \x40=
-	<<
-	vb=<<''
-	=b $x \x40=
-	
-	function foo {
-		vc=<<-
-			=c $x \x40=
-		<<
-		vd=<<-''
-			=d $x \x40=
-	
-	}
-	fnd=$(typeset -f foo)
-	print -r -- "$fnd"
-	function foo {
-		echo blub
-	}
-	foo
-	eval "$fnd"
-	foo
-	print -r -- "| va={$va} vb={$vb} vc={$vc} vd={$vd} |"
-expected-stdout:
-	function foo {
-		vc=<<-
-	=c $x \x40=
-	<<
-	
-		vd=<<-""
-	=d $x \x40=
-	
-	
-	} 
-	blub
-	| va={=a u \x40=
-	} vb={=b $x \x40=
-	} vc={=c u \x40=
-	} vd={=d $x \x40=
-	} |
----
-name: heredoc-comsub-1
-description:
-	Tests for here documents in COMSUB, taken from Austin ML
-stdin:
-	text=$(cat <<EOF
-	here is the text
-	EOF)
-	echo = $text =
-expected-stdout:
-	= here is the text =
----
-name: heredoc-comsub-2
-description:
-	Tests for here documents in COMSUB, taken from Austin ML
-stdin:
-	unbalanced=$(cat <<EOF
-	this paren ) is a problem
-	EOF)
-	echo = $unbalanced =
-expected-stdout:
-	= this paren ) is a problem =
----
-name: heredoc-comsub-3
-description:
-	Tests for here documents in COMSUB, taken from Austin ML
-stdin:
-	balanced=$(cat <<EOF
-	these parens ( ) are not a problem
-	EOF)
-	echo = $balanced =
-expected-stdout:
-	= these parens ( ) are not a problem =
----
-name: heredoc-comsub-4
-description:
-	Tests for here documents in COMSUB, taken from Austin ML
-stdin:
-	balanced=$(cat <<EOF
-	these parens \( ) are a problem
-	EOF)
-	echo = $balanced =
-expected-stdout:
-	= these parens \( ) are a problem =
----
-name: heredoc-subshell-1
-description:
-	Tests for here documents in subshells, taken from Austin ML
-stdin:
-	(cat <<EOF
-	some text
-	EOF)
-	echo end
-expected-stdout:
-	some text
-	end
----
-name: heredoc-subshell-2
-description:
-	Tests for here documents in subshells, taken from Austin ML
-stdin:
-	(cat <<EOF
-	some text
-	EOF
-	)
-	echo end
-expected-stdout:
-	some text
-	end
----
-name: heredoc-subshell-3
-description:
-	Tests for here documents in subshells, taken from Austin ML
-stdin:
-	(cat <<EOF; )
-	some text
-	EOF
-	echo end
-expected-stdout:
-	some text
-	end
----
-name: heredoc-weird-1
-description:
-	Tests for here documents, taken from Austin ML
-	Documents current state in mksh, *NOT* necessarily correct!
-stdin:
-	cat <<END
-	hello
-	END\
-	END
-	END
-	echo end
-expected-stdout:
-	hello
-	ENDEND
-	end
----
-name: heredoc-weird-2
-description:
-	Tests for here documents, taken from Austin ML
-stdin:
-	cat <<'    END    '
-	hello
-	    END    
-	echo end
-expected-stdout:
-	hello
-	end
----
-name: heredoc-weird-4
-description:
-	Tests for here documents, taken from Austin ML
-	Documents current state in mksh, *NOT* necessarily correct!
-stdin:
-	cat <<END
-	hello\
-	END
-	END
-	echo end
-expected-stdout:
-	helloEND
-	end
----
-name: heredoc-weird-5
-description:
-	Tests for here documents, taken from Austin ML
-	Documents current state in mksh, *NOT* necessarily correct!
-stdin:
-	cat <<END
-	hello
-	\END
-	END
-	echo end
-expected-stdout:
-	hello
-	\END
-	end
----
-name: heredoc-tmpfile-1
-description:
-	Check that heredoc temp files aren't removed too soon or too late.
-	Heredoc in simple command.
-stdin:
-	TMPDIR=$PWD
-	eval '
-		cat <<- EOF
-		hi
-		EOF
-		for i in a b ; do
-			cat <<- EOF
-			more
-			EOF
-		done
-	    ' &
-	sleep 1
-	echo Left overs: *
-expected-stdout:
-	hi
-	more
-	more
-	Left overs: *
----
-name: heredoc-tmpfile-2
-description:
-	Check that heredoc temp files aren't removed too soon or too late.
-	Heredoc in function, multiple calls to function.
-stdin:
-	TMPDIR=$PWD
-	eval '
-		foo() {
-			cat <<- EOF
-			hi
-			EOF
-		}
-		foo
-		foo
-	    ' &
-	sleep 1
-	echo Left overs: *
-expected-stdout:
-	hi
-	hi
-	Left overs: *
----
-name: heredoc-tmpfile-3
-description:
-	Check that heredoc temp files aren't removed too soon or too late.
-	Heredoc in function in loop, multiple calls to function.
-stdin:
-	TMPDIR=$PWD
-	eval '
-		foo() {
-			cat <<- EOF
-			hi
-			EOF
-		}
-		for i in a b; do
-			foo
-			foo() {
-				cat <<- EOF
-				folks $i
-				EOF
-			}
-		done
-		foo
-	    ' &
-	sleep 1
-	echo Left overs: *
-expected-stdout:
-	hi
-	folks b
-	folks b
-	Left overs: *
----
-name: heredoc-tmpfile-4
-description:
-	Check that heredoc temp files aren't removed too soon or too late.
-	Backgrounded simple command with here doc
-stdin:
-	TMPDIR=$PWD
-	eval '
-		cat <<- EOF &
-		hi
-		EOF
-	    ' &
-	sleep 1
-	echo Left overs: *
-expected-stdout:
-	hi
-	Left overs: *
----
-name: heredoc-tmpfile-5
-description:
-	Check that heredoc temp files aren't removed too soon or too late.
-	Backgrounded subshell command with here doc
-stdin:
-	TMPDIR=$PWD
-	eval '
-	      (
-		sleep 1	# so parent exits
-		echo A
-		cat <<- EOF
-		hi
-		EOF
-		echo B
-	      ) &
-	    ' &
-	sleep 2
-	echo Left overs: *
-expected-stdout:
-	A
-	hi
-	B
-	Left overs: *
----
-name: heredoc-tmpfile-6
-description:
-	Check that heredoc temp files aren't removed too soon or too late.
-	Heredoc in pipeline.
-stdin:
-	TMPDIR=$PWD
-	eval '
-		cat <<- EOF | sed "s/hi/HI/"
-		hi
-		EOF
-	    ' &
-	sleep 1
-	echo Left overs: *
-expected-stdout:
-	HI
-	Left overs: *
----
-name: heredoc-tmpfile-7
-description:
-	Check that heredoc temp files aren't removed too soon or too late.
-	Heredoc in backgrounded pipeline.
-stdin:
-	TMPDIR=$PWD
-	eval '
-		cat <<- EOF | sed 's/hi/HI/' &
-		hi
-		EOF
-	    ' &
-	sleep 1
-	echo Left overs: *
-expected-stdout:
-	HI
-	Left overs: *
----
-name: heredoc-tmpfile-8
-description:
-	Check that heredoc temp files aren't removed too soon or too
-	late. Heredoc in function, backgrounded call to function.
-	This check can fail on slow machines (<100 MHz), or Cygwin,
-	that's normal.
-need-pass: no
-stdin:
-	TMPDIR=$PWD
-	# Background eval so main shell doesn't do parsing
-	eval '
-		foo() {
-			cat <<- EOF
-			hi
-			EOF
-		}
-		foo
-		# sleep so eval can die
-		(sleep 1; foo) &
-		(sleep 1; foo) &
-		foo
-	    ' &
-	sleep 2
-	echo Left overs: *
-expected-stdout:
-	hi
-	hi
-	hi
-	hi
-	Left overs: *
----
-name: heredoc-quoting-unsubst
-description:
-	Check for correct handling of quoted characters in
-	here documents without substitution (marker is quoted).
-stdin:
-	foo=bar
-	cat <<-'EOF'
-		x " \" \ \\ $ \$ `echo baz` \`echo baz\` $foo \$foo x
-	EOF
-expected-stdout:
-	x " \" \ \\ $ \$ `echo baz` \`echo baz\` $foo \$foo x
----
-name: heredoc-quoting-subst
-description:
-	Check for correct handling of quoted characters in
-	here documents with substitution (marker is not quoted).
-stdin:
-	foo=bar
-	cat <<-EOF
-		x " \" \ \\ $ \$ `echo baz` \`echo baz\` $foo \$foo x
-	EOF
-expected-stdout:
-	x " \" \ \ $ $ baz `echo baz` bar $foo x
----
-name: single-quotes-in-braces
-description:
-	Check that single quotes inside unquoted {} are treated as quotes
-stdin:
-	foo=1
-	echo ${foo:+'blah  $foo'}
-expected-stdout:
-	blah  $foo
----
-name: single-quotes-in-quoted-braces
-description:
-	Check that single quotes inside quoted {} are treated as
-	normal char
-stdin:
-	foo=1
-	echo "${foo:+'blah  $foo'}"
-expected-stdout:
-	'blah  1'
----
-name: single-quotes-in-braces-nested
-description:
-	Check that single quotes inside unquoted {} are treated as quotes,
-	even if that's inside a double-quoted command expansion
-stdin:
-	foo=1
-	echo "$( echo ${foo:+'blah  $foo'})"
-expected-stdout:
-	blah  $foo
----
-name: single-quotes-in-brace-pattern
-description:
-	Check that single quotes inside {} pattern are treated as quotes
-stdin:
-	foo=1234
-	echo ${foo%'2'*} "${foo%'2'*}" ${foo%2'*'} "${foo%2'*'}"
-expected-stdout:
-	1 1 1234 1234
----
-name: single-quotes-in-heredoc-braces
-description:
-	Check that single quotes inside {} in heredoc are treated
-	as normal char
-stdin:
-	foo=1
-	cat <<EOM
-	${foo:+'blah  $foo'}
-	EOM
-expected-stdout:
-	'blah  1'
----
-name: single-quotes-in-nested-braces
-description:
-	Check that single quotes inside nested unquoted {} are
-	treated as quotes
-stdin:
-	foo=1
-	echo ${foo:+${foo:+'blah  $foo'}}
-expected-stdout:
-	blah  $foo
----
-name: single-quotes-in-nested-quoted-braces
-description:
-	Check that single quotes inside nested quoted {} are treated
-	as normal char
-stdin:
-	foo=1
-	echo "${foo:+${foo:+'blah  $foo'}}"
-expected-stdout:
-	'blah  1'
----
-name: single-quotes-in-nested-braces-nested
-description:
-	Check that single quotes inside nested unquoted {} are treated
-	as quotes, even if that's inside a double-quoted command expansion
-stdin:
-	foo=1
-	echo "$( echo ${foo:+${foo:+'blah  $foo'}})"
-expected-stdout:
-	blah  $foo
----
-name: single-quotes-in-nested-brace-pattern
-description:
-	Check that single quotes inside nested {} pattern are treated as quotes
-stdin:
-	foo=1234
-	echo ${foo:+${foo%'2'*}} "${foo:+${foo%'2'*}}" ${foo:+${foo%2'*'}} "${foo:+${foo%2'*'}}"
-expected-stdout:
-	1 1 1234 1234
----
-name: single-quotes-in-heredoc-nested-braces
-description:
-	Check that single quotes inside nested {} in heredoc are treated
-	as normal char
-stdin:
-	foo=1
-	cat <<EOM
-	${foo:+${foo:+'blah  $foo'}}
-	EOM
-expected-stdout:
-	'blah  1'
----
-name: history-basic
-description:
-	See if we can test history at all
-need-ctty: yes
-arguments: !-i!
-env-setup: !ENV=./Env!HISTFILE=hist.file!
-file-setup: file 644 "Env"
-	PS1=X
-stdin:
-	echo hi
-	fc -l
-expected-stdout:
-	hi
-	1	echo hi
-expected-stderr-pattern:
-	/^X*$/
----
-name: history-dups
-description:
-	Verify duplicates and spaces are not entered
-need-ctty: yes
-arguments: !-i!
-env-setup: !ENV=./Env!HISTFILE=hist.file!
-file-setup: file 644 "Env"
-	PS1=X
-stdin:
-	echo hi
-	 echo yo
-	echo hi
-	fc -l
-expected-stdout:
-	hi
-	yo
-	hi
-	1	echo hi
-expected-stderr-pattern:
-	/^X*$/
----
-name: history-unlink
-description:
-	Check if broken HISTFILEs do not cause trouble
-need-ctty: yes
-arguments: !-i!
-env-setup: !ENV=./Env!HISTFILE=foo/hist.file!
-file-setup: file 644 "Env"
-	PS1=X
-file-setup: dir 755 "foo"
-file-setup: file 644 "foo/hist.file"
-	sometext
-time-limit: 5
-perl-setup: chmod(0555, "foo");
-stdin:
-	echo hi
-	fc -l
-	chmod 0755 foo
-expected-stdout:
-	hi
-	1	echo hi
-expected-stderr-pattern:
-	/(.*can't unlink HISTFILE.*\n)?X*$/
----
-name: history-e-minus-1
-description:
-	Check if more recent command is executed
-need-ctty: yes
-arguments: !-i!
-env-setup: !ENV=./Env!HISTFILE=hist.file!
-file-setup: file 644 "Env"
-	PS1=X
-stdin:
-	echo hi
-	echo there
-	fc -e -
-expected-stdout:
-	hi
-	there
-	there
-expected-stderr-pattern:
-	/^X*echo there\nX*$/
----
-name: history-e-minus-2
-description:
-	Check that repeated command is printed before command
-	is re-executed.
-need-ctty: yes
-arguments: !-i!
-env-setup: !ENV=./Env!HISTFILE=hist.file!
-file-setup: file 644 "Env"
-	PS1=X
-stdin:
-	exec 2>&1
-	echo hi
-	echo there
-	fc -e -
-expected-stdout-pattern:
-	/X*hi\nX*there\nX*echo there\nthere\nX*/
-expected-stderr-pattern:
-	/^X*$/
----
-name: history-e-minus-3
-description:
-	fc -e - fails when there is no history
-	(ksh93 has a bug that causes this to fail)
-	(ksh88 loops on this)
-need-ctty: yes
-arguments: !-i!
-env-setup: !ENV=./Env!HISTFILE=hist.file!
-file-setup: file 644 "Env"
-	PS1=X
-stdin:
-	fc -e -
-	echo ok
-expected-stdout:
-	ok
-expected-stderr-pattern:
-	/^X*.*:.*history.*\nX*$/
----
-name: history-e-minus-4
-description:
-	Check if "fc -e -" command output goes to stdout.
-need-ctty: yes
-arguments: !-i!
-env-setup: !ENV=./Env!HISTFILE=hist.file!
-file-setup: file 644 "Env"
-	PS1=X
-stdin:
-	echo abc
-	fc -e - | (read x; echo "A $x")
-	echo ok
-expected-stdout:
-	abc
-	A abc
-	ok
-expected-stderr-pattern:
-	/^X*echo abc\nX*/
----
-name: history-e-minus-5
-description:
-	fc is replaced in history by new command.
-need-ctty: yes
-arguments: !-i!
-env-setup: !ENV=./Env!HISTFILE=hist.file!
-file-setup: file 644 "Env"
-	PS1=X
-stdin:
-	echo abc def
-	echo ghi jkl
-	:
-	fc -e - echo
-	fc -l 2 5
-expected-stdout:
-	abc def
-	ghi jkl
-	ghi jkl
-	2	echo ghi jkl
-	3	:
-	4	echo ghi jkl
-	5	fc -l 2 5
-expected-stderr-pattern:
-	/^X*echo ghi jkl\nX*$/
----
-name: history-list-1
-description:
-	List lists correct range
-	(ksh88 fails 'cause it lists the fc command)
-need-ctty: yes
-arguments: !-i!
-env-setup: !ENV=./Env!HISTFILE=hist.file!
-file-setup: file 644 "Env"
-	PS1=X
-stdin:
-	echo line 1
-	echo line 2
-	echo line 3
-	fc -l -- -2
-expected-stdout:
-	line 1
-	line 2
-	line 3
-	2	echo line 2
-	3	echo line 3
-expected-stderr-pattern:
-	/^X*$/
----
-name: history-list-2
-description:
-	Lists oldest history if given pre-historic number
-	(ksh93 has a bug that causes this to fail)
-	(ksh88 fails 'cause it lists the fc command)
-need-ctty: yes
-arguments: !-i!
-env-setup: !ENV=./Env!HISTFILE=hist.file!
-file-setup: file 644 "Env"
-	PS1=X
-stdin:
-	echo line 1
-	echo line 2
-	echo line 3
-	fc -l -- -40
-expected-stdout:
-	line 1
-	line 2
-	line 3
-	1	echo line 1
-	2	echo line 2
-	3	echo line 3
-expected-stderr-pattern:
-	/^X*$/
----
-name: history-list-3
-description:
-	Can give number 'options' to fc
-need-ctty: yes
-arguments: !-i!
-env-setup: !ENV=./Env!HISTFILE=hist.file!
-file-setup: file 644 "Env"
-	PS1=X
-stdin:
-	echo line 1
-	echo line 2
-	echo line 3
-	echo line 4
-	fc -l -3 -2
-expected-stdout:
-	line 1
-	line 2
-	line 3
-	line 4
-	2	echo line 2
-	3	echo line 3
-expected-stderr-pattern:
-	/^X*$/
----
-name: history-list-4
-description:
-	-1 refers to previous command
-need-ctty: yes
-arguments: !-i!
-env-setup: !ENV=./Env!HISTFILE=hist.file!
-file-setup: file 644 "Env"
-	PS1=X
-stdin:
-	echo line 1
-	echo line 2
-	echo line 3
-	echo line 4
-	fc -l -1 -1
-expected-stdout:
-	line 1
-	line 2
-	line 3
-	line 4
-	4	echo line 4
-expected-stderr-pattern:
-	/^X*$/
----
-name: history-list-5
-description:
-	List command stays in history
-need-ctty: yes
-arguments: !-i!
-env-setup: !ENV=./Env!HISTFILE=hist.file!
-file-setup: file 644 "Env"
-	PS1=X
-stdin:
-	echo line 1
-	echo line 2
-	echo line 3
-	echo line 4
-	fc -l -1 -1
-	fc -l -2 -1
-expected-stdout:
-	line 1
-	line 2
-	line 3
-	line 4
-	4	echo line 4
-	4	echo line 4
-	5	fc -l -1 -1
-expected-stderr-pattern:
-	/^X*$/
----
-name: history-list-6
-description:
-	HISTSIZE limits about of history kept.
-	(ksh88 fails 'cause it lists the fc command)
-need-ctty: yes
-arguments: !-i!
-env-setup: !ENV=./Env!HISTFILE=hist.file!HISTSIZE=3!
-file-setup: file 644 "Env"
-	PS1=X
-stdin:
-	echo line 1
-	echo line 2
-	echo line 3
-	echo line 4
-	echo line 5
-	fc -l
-expected-stdout:
-	line 1
-	line 2
-	line 3
-	line 4
-	line 5
-	4	echo line 4
-	5	echo line 5
-expected-stderr-pattern:
-	/^X*$/
----
-name: history-list-7
-description:
-	fc allows too old/new errors in range specification
-need-ctty: yes
-arguments: !-i!
-env-setup: !ENV=./Env!HISTFILE=hist.file!HISTSIZE=3!
-file-setup: file 644 "Env"
-	PS1=X
-stdin:
-	echo line 1
-	echo line 2
-	echo line 3
-	echo line 4
-	echo line 5
-	fc -l 1 30
-expected-stdout:
-	line 1
-	line 2
-	line 3
-	line 4
-	line 5
-	4	echo line 4
-	5	echo line 5
-	6	fc -l 1 30
-expected-stderr-pattern:
-	/^X*$/
----
-name: history-list-r-1
-description:
-	test -r flag in history
-need-ctty: yes
-arguments: !-i!
-env-setup: !ENV=./Env!HISTFILE=hist.file!
-file-setup: file 644 "Env"
-	PS1=X
-stdin:
-	echo line 1
-	echo line 2
-	echo line 3
-	echo line 4
-	echo line 5
-	fc -l -r 2 4
-expected-stdout:
-	line 1
-	line 2
-	line 3
-	line 4
-	line 5
-	4	echo line 4
-	3	echo line 3
-	2	echo line 2
-expected-stderr-pattern:
-	/^X*$/
----
-name: history-list-r-2
-description:
-	If first is newer than last, -r is implied.
-need-ctty: yes
-arguments: !-i!
-env-setup: !ENV=./Env!HISTFILE=hist.file!
-file-setup: file 644 "Env"
-	PS1=X
-stdin:
-	echo line 1
-	echo line 2
-	echo line 3
-	echo line 4
-	echo line 5
-	fc -l 4 2
-expected-stdout:
-	line 1
-	line 2
-	line 3
-	line 4
-	line 5
-	4	echo line 4
-	3	echo line 3
-	2	echo line 2
-expected-stderr-pattern:
-	/^X*$/
----
-name: history-list-r-3
-description:
-	If first is newer than last, -r is cancelled.
-need-ctty: yes
-arguments: !-i!
-env-setup: !ENV=./Env!HISTFILE=hist.file!
-file-setup: file 644 "Env"
-	PS1=X
-stdin:
-	echo line 1
-	echo line 2
-	echo line 3
-	echo line 4
-	echo line 5
-	fc -l -r 4 2
-expected-stdout:
-	line 1
-	line 2
-	line 3
-	line 4
-	line 5
-	2	echo line 2
-	3	echo line 3
-	4	echo line 4
-expected-stderr-pattern:
-	/^X*$/
----
-name: history-subst-1
-description:
-	Basic substitution
-need-ctty: yes
-arguments: !-i!
-env-setup: !ENV=./Env!HISTFILE=hist.file!
-file-setup: file 644 "Env"
-	PS1=X
-stdin:
-	echo abc def
-	echo ghi jkl
-	fc -e - abc=AB 'echo a'
-expected-stdout:
-	abc def
-	ghi jkl
-	AB def
-expected-stderr-pattern:
-	/^X*echo AB def\nX*$/
----
-name: history-subst-2
-description:
-	Does subst find previous command?
-need-ctty: yes
-arguments: !-i!
-env-setup: !ENV=./Env!HISTFILE=hist.file!
-file-setup: file 644 "Env"
-	PS1=X
-stdin:
-	echo abc def
-	echo ghi jkl
-	fc -e - jkl=XYZQRT 'echo g'
-expected-stdout:
-	abc def
-	ghi jkl
-	ghi XYZQRT
-expected-stderr-pattern:
-	/^X*echo ghi XYZQRT\nX*$/
----
-name: history-subst-3
-description:
-	Does subst find previous command when no arguments given
-need-ctty: yes
-arguments: !-i!
-env-setup: !ENV=./Env!HISTFILE=hist.file!
-file-setup: file 644 "Env"
-	PS1=X
-stdin:
-	echo abc def
-	echo ghi jkl
-	fc -e - jkl=XYZQRT
-expected-stdout:
-	abc def
-	ghi jkl
-	ghi XYZQRT
-expected-stderr-pattern:
-	/^X*echo ghi XYZQRT\nX*$/
----
-name: history-subst-4
-description:
-	Global substitutions work
-	(ksh88 and ksh93 do not have -g option)
-need-ctty: yes
-arguments: !-i!
-env-setup: !ENV=./Env!HISTFILE=hist.file!
-file-setup: file 644 "Env"
-	PS1=X
-stdin:
-	echo abc def asjj sadjhasdjh asdjhasd
-	fc -e - -g a=FooBAR
-expected-stdout:
-	abc def asjj sadjhasdjh asdjhasd
-	FooBARbc def FooBARsjj sFooBARdjhFooBARsdjh FooBARsdjhFooBARsd
-expected-stderr-pattern:
-	/^X*echo FooBARbc def FooBARsjj sFooBARdjhFooBARsdjh FooBARsdjhFooBARsd\nX*$/
----
-name: history-subst-5
-description:
-	Make sure searches don't find current (fc) command
-	(ksh88/ksh93 don't have the ? prefix thing so they fail this test)
-need-ctty: yes
-arguments: !-i!
-env-setup: !ENV=./Env!HISTFILE=hist.file!
-file-setup: file 644 "Env"
-	PS1=X
-stdin:
-	echo abc def
-	echo ghi jkl
-	fc -e - abc=AB \?abc
-expected-stdout:
-	abc def
-	ghi jkl
-	AB def
-expected-stderr-pattern:
-	/^X*echo AB def\nX*$/
----
-name: history-ed-1-old
-description:
-	Basic (ed) editing works (assumes you have generic ed editor
-	that prints no prompts). This is for oldish ed(1) which write
-	the character count to stdout.
-category: stdout-ed
-need-ctty: yes
-need-pass: no
-arguments: !-i!
-env-setup: !ENV=./Env!HISTFILE=hist.file!
-file-setup: file 644 "Env"
-	PS1=X
-stdin:
-	echo abc def
-	fc echo
-	s/abc/FOOBAR/
-	w
-	q
-expected-stdout:
-	abc def
-	13
-	16
-	FOOBAR def
-expected-stderr-pattern:
-	/^X*echo FOOBAR def\nX*$/
----
-name: history-ed-2-old
-description:
-	Correct command is edited when number given
-category: stdout-ed
-need-ctty: yes
-need-pass: no
-arguments: !-i!
-env-setup: !ENV=./Env!HISTFILE=hist.file!
-file-setup: file 644 "Env"
-	PS1=X
-stdin:
-	echo line 1
-	echo line 2 is here
-	echo line 3
-	echo line 4
-	fc 2
-	s/is here/is changed/
-	w
-	q
-expected-stdout:
-	line 1
-	line 2 is here
-	line 3
-	line 4
-	20
-	23
-	line 2 is changed
-expected-stderr-pattern:
-	/^X*echo line 2 is changed\nX*$/
----
-name: history-ed-3-old
-description:
-	Newly created multi line commands show up as single command
-	in history.
-	(NOTE: adjusted for COMPLEX HISTORY compile time option)
-	(ksh88 fails 'cause it lists the fc command)
-category: stdout-ed
-need-ctty: yes
-need-pass: no
-arguments: !-i!
-env-setup: !ENV=./Env!HISTFILE=hist.file!
-file-setup: file 644 "Env"
-	PS1=X
-stdin:
-	echo abc def
-	fc echo
-	s/abc/FOOBAR/
-	$a
-	echo a new line
-	.
-	w
-	q
-	fc -l
-expected-stdout:
-	abc def
-	13
-	32
-	FOOBAR def
-	a new line
-	1	echo abc def
-	2	echo FOOBAR def
-	3	echo a new line
-expected-stderr-pattern:
-	/^X*echo FOOBAR def\necho a new line\nX*$/
----
-name: history-ed-1
-description:
-	Basic (ed) editing works (assumes you have generic ed editor
-	that prints no prompts). This is for newish ed(1) and stderr.
-category: !no-stderr-ed
-need-ctty: yes
-need-pass: no
-arguments: !-i!
-env-setup: !ENV=./Env!HISTFILE=hist.file!
-file-setup: file 644 "Env"
-	PS1=X
-stdin:
-	echo abc def
-	fc echo
-	s/abc/FOOBAR/
-	w
-	q
-expected-stdout:
-	abc def
-	FOOBAR def
-expected-stderr-pattern:
-	/^X*13\n16\necho FOOBAR def\nX*$/
----
-name: history-ed-2
-description:
-	Correct command is edited when number given
-category: !no-stderr-ed
-need-ctty: yes
-need-pass: no
-arguments: !-i!
-env-setup: !ENV=./Env!HISTFILE=hist.file!
-file-setup: file 644 "Env"
-	PS1=X
-stdin:
-	echo line 1
-	echo line 2 is here
-	echo line 3
-	echo line 4
-	fc 2
-	s/is here/is changed/
-	w
-	q
-expected-stdout:
-	line 1
-	line 2 is here
-	line 3
-	line 4
-	line 2 is changed
-expected-stderr-pattern:
-	/^X*20\n23\necho line 2 is changed\nX*$/
----
-name: history-ed-3
-description:
-	Newly created multi line commands show up as single command
-	in history.
-category: !no-stderr-ed
-need-ctty: yes
-need-pass: no
-arguments: !-i!
-env-setup: !ENV=./Env!HISTFILE=hist.file!
-file-setup: file 644 "Env"
-	PS1=X
-stdin:
-	echo abc def
-	fc echo
-	s/abc/FOOBAR/
-	$a
-	echo a new line
-	.
-	w
-	q
-	fc -l
-expected-stdout:
-	abc def
-	FOOBAR def
-	a new line
-	1	echo abc def
-	2	echo FOOBAR def
-	3	echo a new line
-expected-stderr-pattern:
-	/^X*13\n32\necho FOOBAR def\necho a new line\nX*$/
----
-name: IFS-space-1
-description:
-	Simple test, default IFS
-stdin:
-	showargs() { for i; do echo -n " <$i>"; done; echo; }
-	set -- A B C
-	showargs 1 $*
-	showargs 2 "$*"
-	showargs 3 $@
-	showargs 4 "$@"
-expected-stdout:
-	 <1> <A> <B> <C>
-	 <2> <A B C>
-	 <3> <A> <B> <C>
-	 <4> <A> <B> <C>
----
-name: IFS-colon-1
-description:
-	Simple test, IFS=:
-stdin:
-	showargs() { for i; do echo -n " <$i>"; done; echo; }
-	IFS=:
-	set -- A B C
-	showargs 1 $*
-	showargs 2 "$*"
-	showargs 3 $@
-	showargs 4 "$@"
-expected-stdout:
-	 <1> <A> <B> <C>
-	 <2> <A:B:C>
-	 <3> <A> <B> <C>
-	 <4> <A> <B> <C>
----
-name: IFS-null-1
-description:
-	Simple test, IFS=""
-stdin:
-	showargs() { for i; do echo -n " <$i>"; done; echo; }
-	IFS=""
-	set -- A B C
-	showargs 1 $*
-	showargs 2 "$*"
-	showargs 3 $@
-	showargs 4 "$@"
-expected-stdout:
-	 <1> <A> <B> <C>
-	 <2> <ABC>
-	 <3> <A> <B> <C>
-	 <4> <A> <B> <C>
----
-name: IFS-space-colon-1
-description:
-	Simple test, IFS=<white-space>:
-stdin:
-	showargs() { for i; do echo -n " <$i>"; done; echo; }
-	IFS="$IFS:"
-	set --
-	showargs 1 $*
-	showargs 2 "$*"
-	showargs 3 $@
-	showargs 4 "$@"
-	showargs 5 : "$@"
-expected-stdout:
-	 <1>
-	 <2> <>
-	 <3>
-	 <4>
-	 <5> <:>
----
-name: IFS-space-colon-2
-description:
-	Simple test, IFS=<white-space>:
-	AT&T ksh fails this, POSIX says the test is correct.
-stdin:
-	showargs() { for i; do echo -n " <$i>"; done; echo; }
-	IFS="$IFS:"
-	set --
-	showargs :"$@"
-expected-stdout:
-	 <:>
----
-name: IFS-space-colon-4
-description:
-	Simple test, IFS=<white-space>:
-stdin:
-	showargs() { for i; do echo -n " <$i>"; done; echo; }
-	IFS="$IFS:"
-	set --
-	showargs "$@$@"
-expected-stdout:
-	
----
-name: IFS-space-colon-5
-description:
-	Simple test, IFS=<white-space>:
-	Don't know what POSIX thinks of this.  AT&T ksh does not do this.
-stdin:
-	showargs() { for i; do echo -n " <$i>"; done; echo; }
-	IFS="$IFS:"
-	set --
-	showargs "${@:-}"
-expected-stdout:
-	 <>
----
-name: IFS-subst-1
-description:
-	Simple test, IFS=<white-space>:
-stdin:
-	showargs() { for i; do echo -n " <$i>"; done; echo; }
-	IFS="$IFS:"
-	x=":b: :"
-	echo -n '1:'; for i in $x ; do echo -n " [$i]" ; done ; echo
-	echo -n '2:'; for i in :b:: ; do echo -n " [$i]" ; done ; echo
-	showargs 3 $x
-	showargs 4 :b::
-	x="a:b:"
-	echo -n '5:'; for i in $x ; do echo -n " [$i]" ; done ; echo
-	showargs 6 $x
-	x="a::c"
-	echo -n '7:'; for i in $x ; do echo -n " [$i]" ; done ; echo
-	showargs 8 $x
-	echo -n '9:'; for i in ${FOO-`echo -n h:i`th:ere} ; do echo -n " [$i]" ; done ; echo
-	showargs 10 ${FOO-`echo -n h:i`th:ere}
-	showargs 11 "${FOO-`echo -n h:i`th:ere}"
-	x=" A :  B::D"
-	echo -n '12:'; for i in $x ; do echo -n " [$i]" ; done ; echo
-	showargs 13 $x
-expected-stdout:
-	1: [] [b] []
-	2: [:b::]
-	 <3> <> <b> <>
-	 <4> <:b::>
-	5: [a] [b]
-	 <6> <a> <b>
-	7: [a] [] [c]
-	 <8> <a> <> <c>
-	9: [h] [ith] [ere]
-	 <10> <h> <ith> <ere>
-	 <11> <h:ith:ere>
-	12: [A] [B] [] [D]
-	 <13> <A> <B> <> <D>
----
-name: integer-base-err-1
-description:
-	Can't have 0 base (causes shell to exit)
-expected-exit: e != 0
-stdin:
-	typeset -i i
-	i=3
-	i=0#4
-	echo $i
-expected-stderr-pattern:
-	/^.*:.*0#4.*\n$/
----
-name: integer-base-err-2
-description:
-	Can't have multiple bases in a 'constant' (causes shell to exit)
-	(ksh88 fails this test)
-expected-exit: e != 0
-stdin:
-	typeset -i i
-	i=3
-	i=2#110#11
-	echo $i
-expected-stderr-pattern:
-	/^.*:.*2#110#11.*\n$/
----
-name: integer-base-err-3
-description:
-	Syntax errors in expressions and effects on bases
-	(interactive so errors don't cause exits)
-	(ksh88 fails this test - shell exits, even with -i)
-need-ctty: yes
-arguments: !-i!
-stdin:
-	PS1= # minimise prompt hassles
-	typeset -i4 a=10
-	typeset -i a=2+
-	echo $a
-	typeset -i4 a=10
-	typeset -i2 a=2+
-	echo $a
-expected-stderr-pattern:
-	/^([#\$] )?.*:.*2+.*\n.*:.*2+.*\n$/
-expected-stdout:
-	4#22
-	4#22
----
-name: integer-base-err-4
-description:
-	Are invalid digits (according to base) errors?
-	(ksh93 fails this test)
-expected-exit: e != 0
-stdin:
-	typeset -i i;
-	i=3#4
-expected-stderr-pattern:
-	/^([#\$] )?.*:.*3#4.*\n$/
----
-name: integer-base-1
-description:
-	Missing number after base is treated as 0.
-stdin:
-	typeset -i i
-	i=3
-	i=2#
-	echo $i
-expected-stdout:
-	0
----
-name: integer-base-2
-description:
-	Check 'stickyness' of base in various situations
-stdin:
-	typeset -i i=8
-	echo $i
-	echo ---------- A
-	typeset -i4 j=8
-	echo $j
-	echo ---------- B
-	typeset -i k=8
-	typeset -i4 k=8
-	echo $k
-	echo ---------- C
-	typeset -i4 l
-	l=3#10
-	echo $l
-	echo ---------- D
-	typeset -i m
-	m=3#10
-	echo $m
-	echo ---------- E
-	n=2#11
-	typeset -i n
-	echo $n
-	n=10
-	echo $n
-	echo ---------- F
-	typeset -i8 o=12
-	typeset -i4 o
-	echo $o
-	echo ---------- G
-	typeset -i p
-	let p=8#12
-	echo $p
-expected-stdout:
-	8
-	---------- A
-	4#20
-	---------- B
-	4#20
-	---------- C
-	4#3
-	---------- D
-	3#10
-	---------- E
-	2#11
-	2#1010
-	---------- F
-	4#30
-	---------- G
-	8#12
----
-name: integer-base-3
-description:
-	More base parsing (hmm doesn't test much..)
-stdin:
-	typeset -i aa
-	aa=1+12#10+2
-	echo $aa
-	typeset -i bb
-	bb=1+$aa
-	echo $bb
-	typeset -i bb
-	bb=$aa
-	echo $bb
-	typeset -i cc
-	cc=$aa
-	echo $cc
-expected-stdout:
-	15
-	16
-	15
-	15
----
-name: integer-base-4
-description:
-	Check that things not declared as integers are not made integers,
-	also, check if base is not reset by -i with no arguments.
-	(ksh93 fails - prints 10#20 - go figure)
-stdin:
-	xx=20
-	let xx=10
-	typeset -i | grep '^xx='
-	typeset -i4 a=10
-	typeset -i a=20
-	echo $a
-expected-stdout:
-	4#110
----
-name: integer-base-5
-description:
-	More base stuff
-stdin:
-	typeset -i4 a=3#10
-	echo $a
-	echo --
-	typeset -i j=3
-	j='~3'
-	echo $j
-	echo --
-	typeset -i k=1
-	x[k=k+1]=3
-	echo $k
-	echo --
-	typeset -i l
-	for l in 1 2+3 4; do echo $l; done
-expected-stdout:
-	4#3
-	--
-	-4
-	--
-	2
-	--
-	1
-	5
-	4
----
-name: integer-base-6
-description:
-	Even more base stuff
-	(ksh93 fails this test - prints 0)
-stdin:
-	typeset -i7 i
-	i=
-	echo $i
-expected-stdout:
-	7#0
----
-name: integer-base-7
-description:
-	Check that non-integer parameters don't get bases assigned
-stdin:
-	echo $(( zz = 8#100 ))
-	echo $zz
-expected-stdout:
-	64
-	64
----
-name: integer-base-check-flat
-description:
-	Check behaviour does not match POSuX (except if set -o posix),
-	because a not type-safe scripting language has *no* business
-	interpreting the string "010" as octal numer eight (dangerous).
-stdin:
-	echo 1 "$("$__progname" -c 'echo :$((10))/$((010)),$((0x10)):')" .
-	echo 2 "$("$__progname" -o posix -c 'echo :$((10))/$((010)),$((0x10)):')" .
-	echo 3 "$("$__progname" -o sh -c 'echo :$((10))/$((010)),$((0x10)):')" .
-expected-stdout:
-	1 :10/10,16: .
-	2 :10/8,16: .
-	3 :10/10,16: .
----
-name: integer-base-check-numeric-from
-description:
-	Check behaviour for base one to 36, and that 37 errors out
-stdin:
-	echo 1:$((1#1))0.
-	i=1
-	while (( ++i <= 36 )); do
-		eval 'echo '$i':$(('$i'#10)).'
-	done
-	echo 37:$($__progname -c 'echo $((37#10))').$?:
-expected-stdout:
-	1:490.
-	2:2.
-	3:3.
-	4:4.
-	5:5.
-	6:6.
-	7:7.
-	8:8.
-	9:9.
-	10:10.
-	11:11.
-	12:12.
-	13:13.
-	14:14.
-	15:15.
-	16:16.
-	17:17.
-	18:18.
-	19:19.
-	20:20.
-	21:21.
-	22:22.
-	23:23.
-	24:24.
-	25:25.
-	26:26.
-	27:27.
-	28:28.
-	29:29.
-	30:30.
-	31:31.
-	32:32.
-	33:33.
-	34:34.
-	35:35.
-	36:36.
-	37:.0:
-expected-stderr-pattern:
-	/.*bad number '37#10'/
----
-name: integer-base-check-numeric-to
-description:
-	Check behaviour for base one to 36, and that 37 errors out
-stdin:
-	i=0
-	while (( ++i <= 37 )); do
-		typeset -Uui$i x=0x40
-		eval "typeset -i10 y=$x"
-		print $i:$x.$y.
-	done
-expected-stdout:
-	1:1#@.64.
-	2:2#1000000.64.
-	3:3#2101.64.
-	4:4#1000.64.
-	5:5#224.64.
-	6:6#144.64.
-	7:7#121.64.
-	8:8#100.64.
-	9:9#71.64.
-	10:64.64.
-	11:11#59.64.
-	12:12#54.64.
-	13:13#4C.64.
-	14:14#48.64.
-	15:15#44.64.
-	16:16#40.64.
-	17:17#3D.64.
-	18:18#3A.64.
-	19:19#37.64.
-	20:20#34.64.
-	21:21#31.64.
-	22:22#2K.64.
-	23:23#2I.64.
-	24:24#2G.64.
-	25:25#2E.64.
-	26:26#2C.64.
-	27:27#2A.64.
-	28:28#28.64.
-	29:29#26.64.
-	30:30#24.64.
-	31:31#22.64.
-	32:32#20.64.
-	33:33#1V.64.
-	34:34#1U.64.
-	35:35#1T.64.
-	36:36#1S.64.
-	37:36#1S.64.
-expected-stderr-pattern:
-	/.*bad integer base: 37/
----
-name: integer-arithmetic-span
-description:
-	Check wraparound and size that is defined in mksh
-category: int:32
-stdin:
-	echo s:$((2147483647+1)).$(((2147483647*2)+1)).$(((2147483647*2)+2)).
-	echo u:$((#2147483647+1)).$((#(2147483647*2)+1)).$((#(2147483647*2)+2)).
-expected-stdout:
-	s:-2147483648.-1.0.
-	u:2147483648.4294967295.0.
----
-name: integer-arithmetic-span-64
-description:
-	Check wraparound and size that is defined in mksh
-category: int:64
-stdin:
-	echo s:$((9223372036854775807+1)).$(((9223372036854775807*2)+1)).$(((9223372036854775807*2)+2)).
-	echo u:$((#9223372036854775807+1)).$((#(9223372036854775807*2)+1)).$((#(9223372036854775807*2)+2)).
-expected-stdout:
-	s:-9223372036854775808.-1.0.
-	u:9223372036854775808.18446744073709551615.0.
----
-name: integer-size-FAIL-to-detect
-description:
-	Notify the user that their ints are not 32 or 64 bit
-category: int:u
-stdin:
-	:
----
-name: lineno-stdin
-description:
-	See if $LINENO is updated and can be modified.
-stdin:
-	echo A $LINENO
-	echo B $LINENO
-	LINENO=20
-	echo C $LINENO
-expected-stdout:
-	A 1
-	B 2
-	C 20
----
-name: lineno-inc
-description:
-	See if $LINENO is set for .'d files.
-file-setup: file 644 "dotfile"
-	echo dot A $LINENO
-	echo dot B $LINENO
-	LINENO=20
-	echo dot C $LINENO
-stdin:
-	echo A $LINENO
-	echo B $LINENO
-	. ./dotfile
-expected-stdout:
-	A 1
-	B 2
-	dot A 1
-	dot B 2
-	dot C 20
----
-name: lineno-func
-description:
-	See if $LINENO is set for commands in a function.
-stdin:
-	echo A $LINENO
-	echo B $LINENO
-	bar() {
-	    echo func A $LINENO
-	    echo func B $LINENO
-	}
-	bar
-	echo C $LINENO
-expected-stdout:
-	A 1
-	B 2
-	func A 4
-	func B 5
-	C 8
----
-name: lineno-unset
-description:
-	See if unsetting LINENO makes it non-magic.
-file-setup: file 644 "dotfile"
-	echo dot A $LINENO
-	echo dot B $LINENO
-stdin:
-	unset LINENO
-	echo A $LINENO
-	echo B $LINENO
-	bar() {
-	    echo func A $LINENO
-	    echo func B $LINENO
-	}
-	bar
-	. ./dotfile
-	echo C $LINENO
-expected-stdout:
-	A
-	B
-	func A
-	func B
-	dot A
-	dot B
-	C
----
-name: lineno-unset-use
-description:
-	See if unsetting LINENO makes it non-magic even
-	when it is re-used.
-file-setup: file 644 "dotfile"
-	echo dot A $LINENO
-	echo dot B $LINENO
-stdin:
-	unset LINENO
-	LINENO=3
-	echo A $LINENO
-	echo B $LINENO
-	bar() {
-	    echo func A $LINENO
-	    echo func B $LINENO
-	}
-	bar
-	. ./dotfile
-	echo C $LINENO
-expected-stdout:
-	A 3
-	B 3
-	func A 3
-	func B 3
-	dot A 3
-	dot B 3
-	C 3
----
-name: lineno-trap
-description:
-	Check if LINENO is tracked in traps
-stdin:
-	fail() {
-		echo "line <$1>"
-		exit 1
-	}
-	trap 'fail $LINENO' INT ERR
-	false
-expected-stdout:
-	line <6>
-expected-exit: 1
----
-name: unknown-trap
-description:
-	Ensure unknown traps are not a syntax error
-stdin:
-	(
-	trap "echo trap 1 executed" UNKNOWNSIGNAL || echo "foo"
-	echo =1
-	trap "echo trap 2 executed" UNKNOWNSIGNAL EXIT 999999 FNORD
-	echo = $?
-	) 2>&1 | sed "s^${__progname%.exe}\.*e*x*e*: <stdin>\[[0-9]*]PROG"
-expected-stdout:
-	PROG: trap: bad signal 'UNKNOWNSIGNAL'
-	foo
-	=1
-	PROG: trap: bad signal 'UNKNOWNSIGNAL'
-	PROG: trap: bad signal '999999'
-	PROG: trap: bad signal 'FNORD'
-	= 3
-	trap 2 executed
----
-name: read-IFS-1
-description:
-	Simple test, default IFS
-stdin:
-	echo "A B " > IN
-	unset x y z
-	read x y z < IN
-	echo 1: "x[$x] y[$y] z[$z]"
-	echo 1a: ${z-z not set}
-	read x < IN
-	echo 2: "x[$x]"
-expected-stdout:
-	1: x[A] y[B] z[]
-	1a:
-	2: x[A B]
----
-name: read-ksh-1
-description:
-	If no var specified, REPLY is used
-stdin:
-	echo "abc" > IN
-	read < IN
-	echo "[$REPLY]";
-expected-stdout:
-	[abc]
----
-name: read-regress-1
-description:
-	Check a regression of read
-file-setup: file 644 "foo"
-	foo bar
-	baz
-	blah
-stdin:
-	while read a b c; do
-		read d
-		break
-	done <foo
-	echo "<$a|$b|$c><$d>"
-expected-stdout:
-	<foo|bar|><baz>
----
-name: read-delim-1
-description:
-	Check read with delimiters
-stdin:
-	emit() {
-		print -n 'foo bar\tbaz\nblah \0blub\tblech\nmyok meck \0'
-	}
-	emit | while IFS= read -d "" foo; do print -r -- "<$foo>"; done
-	emit | while read -d "" foo; do print -r -- "<$foo>"; done
-	emit | while read -d "eh?" foo; do print -r -- "<$foo>"; done
-expected-stdout:
-	<foo bar	baz
-	blah >
-	<blub	blech
-	myok meck >
-	<foo bar	baz
-	blah>
-	<blub	blech
-	myok meck>
-	<foo bar	baz
-	blah blub	bl>
-	<ch
-	myok m>
----
-name: read-ext-1
-description:
-	Check read with number of bytes specified, and -A
-stdin:
-	print 'foo\nbar' >x1
-	print -n x >x2
-	print 'foo\\ bar baz' >x3
-	x1a=u; read x1a <x1
-	x1b=u; read -N-1 x1b <x1
-	x2a=u; read x2a <x2; r2a=$?
-	x2b=u; read -N2 x2c <x2; r2b=$?
-	x2c=u; read -n2 x2c <x2; r2c=$?
-	x3a=u; read -A x3a <x3
-	print -r "x1a=<$x1a>"
-	print -r "x1b=<$x1b>"
-	print -r "x2a=$r2a<$x2a>"
-	print -r "x2b=$r2b<$x2b>"
-	print -r "x2c=$r2c<$x2c>"
-	print -r "x3a=<${x3a[0]}|${x3a[1]}|${x3a[2]}>"
-expected-stdout:
-	x1a=<foo>
-	x1b=<foo
-	bar>
-	x2a=1<x>
-	x2b=1<u>
-	x2c=0<x>
-	x3a=<foo bar|baz|>
----
-name: regression-1
-description:
-	Lex array code had problems with this.
-stdin:
-	echo foo[
-	n=bar
-	echo "hi[ $n ]=1"
-expected-stdout:
-	foo[
-	hi[ bar ]=1
----
-name: regression-2
-description:
-	When PATH is set before running a command, the new path is
-	not used in doing the path search
-		$ echo echo hi > /tmp/q ; chmod a+rx /tmp/q
-		$ PATH=/tmp q
-		q: not found
-		$
-	in comexec() the two lines
-		while (*vp != NULL)
-			(void) typeset(*vp++, xxx, 0);
-	need to be moved out of the switch to before findcom() is
-	called - I don't know what this will break.
-stdin:
-	: ${PWD:-`pwd 2> /dev/null`}
-	: ${PWD:?"PWD not set - can't do test"}
-	mkdir Y
-	cat > Y/xxxscript << EOF
-	#!/bin/sh
-	# Need to restore path so echo can be found (some shells don't have
-	# it as a built-in)
-	PATH=\$OLDPATH
-	echo hi
-	exit 0
-	EOF
-	chmod a+rx Y/xxxscript
-	export OLDPATH="$PATH"
-	PATH=$PWD/Y xxxscript
-	exit $?
-expected-stdout:
-	hi
----
-name: regression-6
-description:
-	Parsing of $(..) expressions is non-optimal.  It is
-	impossible to have any parentheses inside the expression.
-	I.e.,
-		$ ksh -c 'echo $(echo \( )'
-		no closing quote
-		$ ksh -c 'echo $(echo "(" )'
-		no closing quote
-		$
-	The solution is to hack the parsing clode in lex.c, the
-	question is how to hack it: should any parentheses be
-	escaped by a backslash, or should recursive parsing be done
-	(so quotes could also be used to hide hem).  The former is
-	easier, the later better...
-stdin:
-	echo $(echo \( )
-	echo $(echo "(" )
-expected-stdout:
-	(
-	(
----
-name: regression-9
-description:
-	Continue in a for loop does not work right:
-		for i in a b c ; do
-			if [ $i = b ] ; then
-				continue
-			fi
-			echo $i
-		done
-	Prints a forever...
-stdin:
-	first=yes
-	for i in a b c ; do
-		if [ $i = b ] ; then
-			if [ $first = no ] ; then
-				echo 'continue in for loop broken'
-				break	# hope break isn't broken too :-)
-			fi
-			first=no
-			continue
-		fi
-	done
-	echo bye
-expected-stdout:
-	bye
----
-name: regression-10
-description:
-	The following:
-		set -- `false`
-		echo $?
-	should print 0 according to POSIX (dash, bash, ksh93, posh)
-	but not 0 according to the getopt(1) manual page, ksh88, and
-	Bourne sh (such as /bin/sh on Solaris).
-	We honour POSIX except when -o sh is set.
-category: shell:legacy-no
-stdin:
-	showf() {
-		[[ -o posix ]]; FPOSIX=$((1-$?))
-		[[ -o sh ]]; FSH=$((1-$?))
-		echo -n "FPOSIX=$FPOSIX FSH=$FSH "
-	}
-	set +o posix +o sh
-	showf
-	set -- `false`
-	echo rv=$?
-	set -o sh
-	showf
-	set -- `false`
-	echo rv=$?
-	set -o posix
-	showf
-	set -- `false`
-	echo rv=$?
-	set -o posix -o sh
-	showf
-	set -- `false`
-	echo rv=$?
-expected-stdout:
-	FPOSIX=0 FSH=0 rv=0
-	FPOSIX=0 FSH=1 rv=1
-	FPOSIX=1 FSH=0 rv=0
-	FPOSIX=1 FSH=1 rv=0
----
-name: regression-10-legacy
-description:
-	The following:
-		set -- `false`
-		echo $?
-	should print 0 according to POSIX (dash, bash, ksh93, posh)
-	but not 0 according to the getopt(1) manual page, ksh88, and
-	Bourne sh (such as /bin/sh on Solaris).
-category: shell:legacy-yes
-stdin:
-	showf() {
-		[[ -o posix ]]; FPOSIX=$((1-$?))
-		[[ -o sh ]]; FSH=$((1-$?))
-		echo -n "FPOSIX=$FPOSIX FSH=$FSH "
-	}
-	set +o posix +o sh
-	showf
-	set -- `false`
-	echo rv=$?
-	set -o sh
-	showf
-	set -- `false`
-	echo rv=$?
-	set -o posix
-	showf
-	set -- `false`
-	echo rv=$?
-	set -o posix -o sh
-	showf
-	set -- `false`
-	echo rv=$?
-expected-stdout:
-	FPOSIX=0 FSH=0 rv=1
-	FPOSIX=0 FSH=1 rv=1
-	FPOSIX=1 FSH=0 rv=0
-	FPOSIX=1 FSH=1 rv=0
----
-name: regression-11
-description:
-	The following:
-		x=/foo/bar/blah
-		echo ${x##*/}
-	should echo blah but on some machines echos /foo/bar/blah.
-stdin:
-	x=/foo/bar/blah
-	echo ${x##*/}
-expected-stdout:
-	blah
----
-name: regression-12
-description:
-	Both of the following echos produce the same output under sh/ksh.att:
-		#!/bin/sh
-		x="foo	bar"
-		echo "`echo \"$x\"`"
-		echo "`echo "$x"`"
-	pdksh produces different output for the former (foo instead of foo\tbar)
-stdin:
-	x="foo	bar"
-	echo "`echo \"$x\"`"
-	echo "`echo "$x"`"
-expected-stdout:
-	foo	bar
-	foo	bar
----
-name: regression-13
-description:
-	The following command hangs forever:
-		$ (: ; cat /etc/termcap) | sleep 2
-	This is because the shell forks a shell to run the (..) command
-	and this shell has the pipe open.  When the sleep dies, the cat
-	doesn't get a SIGPIPE 'cause a process (ie, the second shell)
-	still has the pipe open.
-	
-	NOTE: this test provokes a bizarre bug in ksh93 (shell starts reading
-	      commands from /etc/termcap..)
-time-limit: 10
-stdin:
-	echo A line of text that will be duplicated quite a number of times.> t1
-	cat t1 t1 t1 t1  t1 t1 t1 t1  t1 t1 t1 t1  t1 t1 t1 t1  > t2
-	cat t2 t2 t2 t2  t2 t2 t2 t2  t2 t2 t2 t2  t2 t2 t2 t2  > t1
-	cat t1 t1 t1 t1 > t2
-	(: ; cat t2 2>/dev/null) | sleep 1
----
-name: regression-14
-description:
-	The command
-		$ (foobar) 2> /dev/null
-	generates no output under /bin/sh, but pdksh produces the error
-		foobar: not found
-	Also, the command
-		$ foobar 2> /dev/null
-	generates an error under /bin/sh and pdksh, but AT&T ksh88 produces
-	no error (redirected to /dev/null).
-stdin:
-	(you/should/not/see/this/error/1) 2> /dev/null
-	you/should/not/see/this/error/2 2> /dev/null
-	true
----
-name: regression-15
-description:
-	The command
-		$ whence foobar
-	generates a blank line under pdksh and sets the exit status to 0.
-	AT&T ksh88 generates no output and sets the exit status to 1.  Also,
-	the command
-		$ whence foobar cat
-	generates no output under AT&T ksh88 (pdksh generates a blank line
-	and /bin/cat).
-stdin:
-	whence does/not/exist > /dev/null
-	echo 1: $?
-	echo 2: $(whence does/not/exist | wc -l)
-	echo 3: $(whence does/not/exist cat | wc -l)
-expected-stdout:
-	1: 1
-	2: 0
-	3: 0
----
-name: regression-16
-description:
-	${var%%expr} seems to be broken in many places.  On the mips
-	the commands
-		$ read line < /etc/passwd
-		$ echo $line
-		root:0:1:...
-		$ echo ${line%%:*}
-		root
-		$ echo $line
-		root
-		$
-	change the value of line.  On sun4s & pas, the echo ${line%%:*} doesn't
-	work.  Haven't checked elsewhere...
-script:
-	read x
-	y=$x
-	echo ${x%%:*}
-	echo $x
-stdin:
-	root:asdjhasdasjhs:0:1:Root:/:/bin/sh
-expected-stdout:
-	root
-	root:asdjhasdasjhs:0:1:Root:/:/bin/sh
----
-name: regression-17
-description:
-	The command
-		. /foo/bar
-	should set the exit status to non-zero (sh and AT&T ksh88 do).
-	XXX doting a non existent file is a fatal error for a script
-stdin:
-	. does/not/exist
-expected-exit: e != 0
-expected-stderr-pattern: /.?/
----
-name: regression-19
-description:
-	Both of the following echos should produce the same thing, but don't:
-		$ x=foo/bar
-		$ echo ${x%/*}
-		foo
-		$ echo "${x%/*}"
-		foo/bar
-stdin:
-	x=foo/bar
-	echo "${x%/*}"
-expected-stdout:
-	foo
----
-name: regression-21
-description:
-	backslash does not work as expected in case labels:
-	$ x='-x'
-	$ case $x in
-	-\?) echo hi
-	esac
-	hi
-	$ x='-?'
-	$ case $x in
-	-\\?) echo hi
-	esac
-	hi
-	$
-stdin:
-	case -x in
-	-\?)	echo fail
-	esac
----
-name: regression-22
-description:
-	Quoting backquotes inside backquotes doesn't work:
-	$ echo `echo hi \`echo there\` folks`
-	asks for more info.  sh and AT&T ksh88 both echo
-	hi there folks
-stdin:
-	echo `echo hi \`echo there\` folks`
-expected-stdout:
-	hi there folks
----
-name: regression-23
-description:
-	)) is not treated `correctly':
-	    $ (echo hi ; (echo there ; echo folks))
-	    missing ((
-	    $
-	instead of (as sh and ksh.att)
-	    $ (echo hi ; (echo there ; echo folks))
-	    hi
-	    there
-	    folks
-	    $
-stdin:
-	( : ; ( : ; echo hi))
-expected-stdout:
-	hi
----
-name: regression-25
-description:
-	Check reading stdin in a while loop.  The read should only read
-	a single line, not a whole stdio buffer; the cat should get
-	the rest.
-stdin:
-	(echo a; echo b) | while read x ; do
-	    echo $x
-	    cat > /dev/null
-	done
-expected-stdout:
-	a
----
-name: regression-26
-description:
-	Check reading stdin in a while loop.  The read should read both
-	lines, not just the first.
-script:
-	a=
-	while [ "$a" != xxx ] ; do
-	    last=$x
-	    read x
-	    cat /dev/null | sed 's/x/y/'
-	    a=x$a
-	done
-	echo $last
-stdin:
-	a
-	b
-expected-stdout:
-	b
----
-name: regression-27
-description:
-	The command
-		. /does/not/exist
-	should cause a script to exit.
-stdin:
-	. does/not/exist
-	echo hi
-expected-exit: e != 0
-expected-stderr-pattern: /does\/not\/exist/
----
-name: regression-28
-description:
-	variable assignements not detected well
-stdin:
-	a.x=1 echo hi
-expected-exit: e != 0
-expected-stderr-pattern: /a\.x=1/
----
-name: regression-29
-description:
-	alias expansion different from AT&T ksh88
-stdin:
-	alias a='for ' b='i in'
-	a b hi ; do echo $i ; done
-expected-stdout:
-	hi
----
-name: regression-30
-description:
-	strange characters allowed inside ${...}
-stdin:
-	echo ${a{b}}
-expected-exit: e != 0
-expected-stderr-pattern: /.?/
----
-name: regression-31
-description:
-	Does read handle partial lines correctly
-script:
-	a= ret=
-	while [ "$a" != xxx ] ; do
-	    read x y z
-	    ret=$?
-	    a=x$a
-	done
-	echo "[$x]"
-	echo $ret
-stdin: !
-	a A aA
-	b B Bb
-	c
-expected-stdout:
-	[c]
-	1
----
-name: regression-32
-description:
-	Does read set variables to null at eof?
-script:
-	a=
-	while [ "$a" != xxx ] ; do
-	    read x y z
-	    a=x$a
-	done
-	echo 1: ${x-x not set} ${y-y not set} ${z-z not set}
-	echo 2: ${x:+x not null} ${y:+y not null} ${z:+z not null}
-stdin:
-	a A Aa
-	b B Bb
-expected-stdout:
-	1:
-	2:
----
-name: regression-33
-description:
-	Does umask print a leading 0 when umask is 3 digits?
-stdin:
-	# on MiNT, the first umask call seems to fail
-	umask 022
-	# now, the test proper
-	umask 222
-	umask
-expected-stdout:
-	0222
----
-name: regression-35
-description:
-	Tempory files used for here-docs in functions get trashed after
-	the function is parsed (before it is executed)
-stdin:
-	f1() {
-		cat <<- EOF
-			F1
-		EOF
-		f2() {
-			cat <<- EOF
-				F2
-			EOF
-		}
-	}
-	f1
-	f2
-	unset -f f1
-	f2
-expected-stdout:
-	F1
-	F2
-	F2
----
-name: regression-36
-description:
-	Command substitution breaks reading in while loop
-	(test from <sjg at void.zen.oz.au>)
-stdin:
-	(echo abcdef; echo; echo 123) |
-	    while read line
-	    do
-	      # the following line breaks it
-	      c=`echo $line | wc -c`
-	      echo $c
-	    done
-expected-stdout:
-	7
-	1
-	4
----
-name: regression-37
-description:
-	Machines with broken times() (reported by <sjg at void.zen.oz.au>)
-	time does not report correct real time
-stdin:
-	time sleep 1
-expected-stderr-pattern: !/^\s*0\.0[\s\d]+real|^\s*real[\s]+0+\.0/
----
-name: regression-38
-description:
-	set -e doesn't ignore exit codes for if/while/until/&&/||/!.
-arguments: !-e!
-stdin:
-	if false; then echo hi ; fi
-	false || true
-	false && true
-	while false; do echo hi; done
-	echo ok
-expected-stdout:
-	ok
----
-name: regression-39
-description:
-	Only posh and oksh(2013-07) say “hi” below; FreeBSD sh,
-	GNU bash in POSIX mode, dash, ksh93, mksh don’t. All of
-	them exit 0. The POSIX behaviour is needed by BSD make.
-stdin:
-	set -e
-	echo `false; echo hi`
-	echo $?
-expected-stdout:
-	
-	0
----
-name: regression-40
-description:
-	This used to cause a core dump
-env-setup: !RANDOM=12!
-stdin:
-	echo hi
-expected-stdout:
-	hi
----
-name: regression-41
-description:
-	foo should be set to bar (should not be empty)
-stdin:
-	foo=`
-	echo bar`
-	echo "($foo)"
-expected-stdout:
-	(bar)
----
-name: regression-42
-description:
-	Can't use command line assignments to assign readonly parameters.
-stdin:
-	print '#!'"$__progname"'\nunset RANDOM\nexport | while IFS= read -r' \
-	    'RANDOM; do eval '\''print -r -- "$RANDOM=$'\''"$RANDOM"'\'\"\'\; \
-	    done >env; chmod +x env; PATH=.:$PATH
-	foo=bar
-	readonly foo
-	foo=stuff env | grep '^foo'
-expected-exit: e != 0
-expected-stderr-pattern:
-	/read-only/
----
-name: regression-43
-description:
-	Can subshells be prefixed by redirections (historical shells allow
-	this)
-stdin:
-	< /dev/null (sed 's/^/X/')
----
-name: regression-45
-description:
-	Parameter assignments with [] recognised correctly
-stdin:
-	FOO=*[12]
-	BAR=abc[
-	MORE=[abc]
-	JUNK=a[bc
-	echo "<$FOO>"
-	echo "<$BAR>"
-	echo "<$MORE>"
-	echo "<$JUNK>"
-expected-stdout:
-	<*[12]>
-	<abc[>
-	<[abc]>
-	<a[bc>
----
-name: regression-46
-description:
-	Check that alias expansion works in command substitutions and
-	at the end of file.
-stdin:
-	alias x='echo hi'
-	FOO="`x` "
-	echo "[$FOO]"
-	x
-expected-stdout:
-	[hi ]
-	hi
----
-name: regression-47
-description:
-	Check that aliases are fully read.
-stdin:
-	alias x='echo hi;
-	echo there'
-	x
-	echo done
-expected-stdout:
-	hi
-	there
-	done
----
-name: regression-48
-description:
-	Check that (here doc) temp files are not left behind after an exec.
-stdin:
-	mkdir foo || exit 1
-	TMPDIR=$PWD/foo "$__progname" <<- 'EOF'
-		x() {
-			sed 's/^/X /' << E_O_F
-			hi
-			there
-			folks
-			E_O_F
-			echo "done ($?)"
-		}
-		echo=echo; [ -x /bin/echo ] && echo=/bin/echo
-		exec $echo subtest-1 hi
-	EOF
-	echo subtest-1 foo/*
-	TMPDIR=$PWD/foo "$__progname" <<- 'EOF'
-		echo=echo; [ -x /bin/echo ] && echo=/bin/echo
-		sed 's/^/X /' << E_O_F; exec $echo subtest-2 hi
-		a
-		few
-		lines
-		E_O_F
-	EOF
-	echo subtest-2 foo/*
-expected-stdout:
-	subtest-1 hi
-	subtest-1 foo/*
-	X a
-	X few
-	X lines
-	subtest-2 hi
-	subtest-2 foo/*
----
-name: regression-49
-description:
-	Check that unset params with attributes are reported by set, those
-	sans attributes are not.
-stdin:
-	unset FOO BAR
-	echo X$FOO
-	export BAR
-	typeset -i BLAH
-	set | grep FOO
-	set | grep BAR
-	set | grep BLAH
-expected-stdout:
-	X
-	BAR
-	BLAH
----
-name: regression-50
-description:
-	Check that aliases do not use continuation prompt after trailing
-	semi-colon.
-file-setup: file 644 "envf"
-	PS1=Y
-	PS2=X
-env-setup: !ENV=./envf!
-need-ctty: yes
-arguments: !-i!
-stdin:
-	alias foo='echo hi ; '
-	foo
-	foo echo there
-expected-stdout:
-	hi
-	hi
-	there
-expected-stderr: !
-	YYYY
----
-name: regression-51
-description:
-	Check that set allows both +o and -o options on same command line.
-stdin:
-	set a b c
-	set -o noglob +o allexport
-	echo A: $*, *
-expected-stdout:
-	A: a b c, *
----
-name: regression-52
-description:
-	Check that globbing works in pipelined commands
-file-setup: file 644 "envf"
-	PS1=P
-file-setup: file 644 "abc"
-	stuff
-env-setup: !ENV=./envf!
-need-ctty: yes
-arguments: !-i!
-stdin:
-	sed 's/^/X /' < ab*
-	echo mark 1
-	sed 's/^/X /' < ab* | sed 's/^/Y /'
-	echo mark 2
-expected-stdout:
-	X stuff
-	mark 1
-	Y X stuff
-	mark 2
-expected-stderr: !
-	PPPPP
----
-name: regression-53
-description:
-	Check that getopts works in functions
-stdin:
-	bfunc() {
-	    echo bfunc: enter "(args: $*; OPTIND=$OPTIND)"
-	    while getopts B oc; do
-		case $oc in
-		  (B)
-		    echo bfunc: B option
-		    ;;
-		  (*)
-		    echo bfunc: odd option "($oc)"
-		    ;;
-		esac
-	    done
-	    echo bfunc: leave
-	}
-	
-	function kfunc {
-	    echo kfunc: enter "(args: $*; OPTIND=$OPTIND)"
-	    while getopts K oc; do
-		case $oc in
-		  (K)
-		    echo kfunc: K option
-		    ;;
-		  (*)
-		    echo bfunc: odd option "($oc)"
-		    ;;
-		esac
-	    done
-	    echo kfunc: leave
-	}
-	
-	set -- -f -b -k -l
-	echo "line 1: OPTIND=$OPTIND"
-	getopts kbfl optc
-	echo "line 2: ret=$?, optc=$optc, OPTIND=$OPTIND"
-	bfunc -BBB blah
-	echo "line 3: OPTIND=$OPTIND"
-	getopts kbfl optc
-	echo "line 4: ret=$?, optc=$optc, OPTIND=$OPTIND"
-	kfunc -KKK blah
-	echo "line 5: OPTIND=$OPTIND"
-	getopts kbfl optc
-	echo "line 6: ret=$?, optc=$optc, OPTIND=$OPTIND"
-	echo
-	
-	OPTIND=1
-	set -- -fbkl
-	echo "line 10: OPTIND=$OPTIND"
-	getopts kbfl optc
-	echo "line 20: ret=$?, optc=$optc, OPTIND=$OPTIND"
-	bfunc -BBB blah
-	echo "line 30: OPTIND=$OPTIND"
-	getopts kbfl optc
-	echo "line 40: ret=$?, optc=$optc, OPTIND=$OPTIND"
-	kfunc -KKK blah
-	echo "line 50: OPTIND=$OPTIND"
-	getopts kbfl optc
-	echo "line 60: ret=$?, optc=$optc, OPTIND=$OPTIND"
-expected-stdout:
-	line 1: OPTIND=1
-	line 2: ret=0, optc=f, OPTIND=2
-	bfunc: enter (args: -BBB blah; OPTIND=2)
-	bfunc: B option
-	bfunc: B option
-	bfunc: leave
-	line 3: OPTIND=2
-	line 4: ret=0, optc=b, OPTIND=3
-	kfunc: enter (args: -KKK blah; OPTIND=1)
-	kfunc: K option
-	kfunc: K option
-	kfunc: K option
-	kfunc: leave
-	line 5: OPTIND=3
-	line 6: ret=0, optc=k, OPTIND=4
-	
-	line 10: OPTIND=1
-	line 20: ret=0, optc=f, OPTIND=2
-	bfunc: enter (args: -BBB blah; OPTIND=2)
-	bfunc: B option
-	bfunc: B option
-	bfunc: leave
-	line 30: OPTIND=2
-	line 40: ret=1, optc=?, OPTIND=2
-	kfunc: enter (args: -KKK blah; OPTIND=1)
-	kfunc: K option
-	kfunc: K option
-	kfunc: K option
-	kfunc: leave
-	line 50: OPTIND=2
-	line 60: ret=1, optc=?, OPTIND=2
----
-name: regression-54
-description:
-	Check that ; is not required before the then in if (( ... )) then ...
-stdin:
-	if (( 1 )) then
-	    echo ok dparen
-	fi
-	if [[ -n 1 ]] then
-	    echo ok dbrackets
-	fi
-expected-stdout:
-	ok dparen
-	ok dbrackets
----
-name: regression-55
-description:
-	Check ${foo:%bar} is allowed (ksh88 allows it...)
-stdin:
-	x=fooXbarXblah
-	echo 1 ${x%X*}
-	echo 2 ${x:%X*}
-	echo 3 ${x%%X*}
-	echo 4 ${x:%%X*}
-	echo 5 ${x#*X}
-	echo 6 ${x:#*X}
-	echo 7 ${x##*X}
-	echo 8 ${x:##*X}
-expected-stdout:
-	1 fooXbar
-	2 fooXbar
-	3 foo
-	4 foo
-	5 barXblah
-	6 barXblah
-	7 blah
-	8 blah
----
-name: regression-57
-description:
-	Check if typeset output is correct for
-	uninitialised array elements.
-stdin:
-	typeset -i xxx[4]
-	echo A
-	typeset -i | grep xxx | sed 's/^/    /'
-	echo B
-	typeset | grep xxx | sed 's/^/    /'
-	
-	xxx[1]=2+5
-	echo M
-	typeset -i | grep xxx | sed 's/^/    /'
-	echo N
-	typeset | grep xxx | sed 's/^/    /'
-expected-stdout:
-	A
-	    xxx
-	B
-	    typeset -i xxx
-	M
-	    xxx[1]=7
-	N
-	    set -A xxx
-	    typeset -i xxx[1]
----
-name: regression-58
-description:
-	Check if trap exit is ok (exit not mistaken for signal name)
-stdin:
-	trap 'echo hi' exit
-	trap exit 1
-expected-stdout:
-	hi
----
-name: regression-59
-description:
-	Check if ${#array[*]} is calculated correctly.
-stdin:
-	a[12]=hi
-	a[8]=there
-	echo ${#a[*]}
-expected-stdout:
-	2
----
-name: regression-60
-description:
-	Check if default exit status is previous command
-stdin:
-	(true; exit)
-	echo A $?
-	(false; exit)
-	echo B $?
-	( (exit 103) ; exit)
-	echo C $?
-expected-stdout:
-	A 0
-	B 1
-	C 103
----
-name: regression-61
-description:
-	Check if EXIT trap is executed for sub shells.
-stdin:
-	trap 'echo parent exit' EXIT
-	echo start
-	(echo A; echo A last)
-	echo B
-	(echo C; trap 'echo sub exit' EXIT; echo C last)
-	echo parent last
-expected-stdout:
-	start
-	A
-	A last
-	B
-	C
-	C last
-	sub exit
-	parent last
-	parent exit
----
-name: regression-62
-description:
-	Check if test -nt/-ot succeeds if second(first) file is missing.
-stdin:
-	touch a
-	test a -nt b && echo nt OK || echo nt BAD
-	test b -ot a && echo ot OK || echo ot BAD
-expected-stdout:
-	nt OK
-	ot OK
----
-name: regression-63
-description:
-	Check if typeset, export, and readonly work
-stdin:
-	{
-		echo FNORD-0
-		FNORD_A=1
-		FNORD_B=2
-		FNORD_C=3
-		FNORD_D=4
-		FNORD_E=5
-		FNORD_F=6
-		FNORD_G=7
-		FNORD_H=8
-		integer FNORD_E FNORD_F FNORD_G FNORD_H
-		export FNORD_C FNORD_D FNORD_G FNORD_H
-		readonly FNORD_B FNORD_D FNORD_F FNORD_H
-		echo FNORD-1
-		export
-		echo FNORD-2
-		export -p
-		echo FNORD-3
-		readonly
-		echo FNORD-4
-		readonly -p
-		echo FNORD-5
-		typeset
-		echo FNORD-6
-		typeset -p
-		echo FNORD-7
-		typeset -
-		echo FNORD-8
-	} | fgrep FNORD
-	fnord=(42 23)
-	typeset -p fnord
-	echo FNORD-9
-expected-stdout:
-	FNORD-0
-	FNORD-1
-	FNORD_C
-	FNORD_D
-	FNORD_G
-	FNORD_H
-	FNORD-2
-	export FNORD_C=3
-	export FNORD_D=4
-	export FNORD_G=7
-	export FNORD_H=8
-	FNORD-3
-	FNORD_B
-	FNORD_D
-	FNORD_F
-	FNORD_H
-	FNORD-4
-	readonly FNORD_B=2
-	readonly FNORD_D=4
-	readonly FNORD_F=6
-	readonly FNORD_H=8
-	FNORD-5
-	typeset FNORD_A
-	typeset -r FNORD_B
-	typeset -x FNORD_C
-	typeset -x -r FNORD_D
-	typeset -i FNORD_E
-	typeset -i -r FNORD_F
-	typeset -i -x FNORD_G
-	typeset -i -x -r FNORD_H
-	FNORD-6
-	typeset FNORD_A=1
-	typeset -r FNORD_B=2
-	typeset -x FNORD_C=3
-	typeset -x -r FNORD_D=4
-	typeset -i FNORD_E=5
-	typeset -i -r FNORD_F=6
-	typeset -i -x FNORD_G=7
-	typeset -i -x -r FNORD_H=8
-	FNORD-7
-	FNORD_A=1
-	FNORD_B=2
-	FNORD_C=3
-	FNORD_D=4
-	FNORD_E=5
-	FNORD_F=6
-	FNORD_G=7
-	FNORD_H=8
-	FNORD-8
-	set -A fnord
-	typeset fnord[0]=42
-	typeset fnord[1]=23
-	FNORD-9
----
-name: regression-64
-description:
-	Check that we can redefine functions calling time builtin
-stdin:
-	t() {
-		time >/dev/null
-	}
-	t 2>/dev/null
-	t() {
-		time
-	}
----
-name: regression-65
-description:
-	check for a regression with sleep builtin and signal mask
-category: !nojsig
-time-limit: 3
-stdin:
-	sleep 1
-	echo blub |&
-	while read -p line; do :; done
-	echo ok
-expected-stdout:
-	ok
----
-name: regression-66
-description:
-	Check that quoting is sane
-category: !nojsig
-stdin:
-	ac_space=' '
-	ac_newline='
-	'
-	set | grep ^ac_ |&
-	set -A lines
-	while IFS= read -pr line; do
-		if [[ $line = *space* ]]; then
-			lines[0]=$line
-		else
-			lines[1]=$line
-		fi
-	done
-	for line in "${lines[@]}"; do
-		print -r -- "$line"
-	done
-expected-stdout:
-	ac_space=' '
-	ac_newline=$'\n'
----
-name: readonly-0
-description:
-	Ensure readonly is honoured for assignments and unset
-stdin:
-	"$__progname" -c 'u=x; echo $? $u .' || echo aborted, $?
-	echo =
-	"$__progname" -c 'readonly u; u=x; echo $? $u .' || echo aborted, $?
-	echo =
-	"$__progname" -c 'u=x; readonly u; unset u; echo $? $u .' || echo aborted, $?
-expected-stdout:
-	0 x .
-	=
-	aborted, 2
-	=
-	1 x .
-expected-stderr-pattern:
-	/read-only/
----
-name: readonly-1
-description:
-	http://austingroupbugs.net/view.php?id=367 for export
-stdin:
-	"$__progname" -c 'readonly foo; export foo=a; echo $?' || echo aborted, $?
-expected-stdout:
-	aborted, 2
-expected-stderr-pattern:
-	/read-only/
----
-name: readonly-2a
-description:
-	Check that getopts works as intended, for readonly-2b to be valid
-stdin:
-	"$__progname" -c 'set -- -a b; getopts a c; echo $? $c .; getopts a c; echo $? $c .' || echo aborted, $?
-expected-stdout:
-	0 a .
-	1 ? .
----
-name: readonly-2b
-description:
-	http://austingroupbugs.net/view.php?id=367 for getopts
-stdin:
-	"$__progname" -c 'readonly c; set -- -a b; getopts a c; echo $? $c .' || echo aborted, $?
-expected-stdout:
-	2 .
-expected-stderr-pattern:
-	/read-only/
----
-name: readonly-3
-description:
-	http://austingroupbugs.net/view.php?id=367 for read
-stdin:
-	echo x | "$__progname" -c 'read s; echo $? $s .' || echo aborted, $?
-	echo y | "$__progname" -c 'readonly s; read s; echo $? $s .' || echo aborted, $?
-expected-stdout:
-	0 x .
-	2 .
-expected-stderr-pattern:
-	/read-only/
----
-name: syntax-1
-description:
-	Check that lone ampersand is a syntax error
-stdin:
-	 &
-expected-exit: e != 0
-expected-stderr-pattern:
-	/syntax error/
----
-name: xxx-quoted-newline-1
-description:
-	Check that \<newline> works inside of ${}
-stdin:
-	abc=2
-	echo ${ab\
-	c}
-expected-stdout:
-	2
----
-name: xxx-quoted-newline-2
-description:
-	Check that \<newline> works at the start of a here document
-stdin:
-	cat << EO\
-	F
-	hi
-	EOF
-expected-stdout:
-	hi
----
-name: xxx-quoted-newline-3
-description:
-	Check that \<newline> works at the end of a here document
-stdin:
-	cat << EOF
-	hi
-	EO\
-	F
-expected-stdout:
-	hi
----
-name: xxx-multi-assignment-cmd
-description:
-	Check that assignments in a command affect subsequent assignments
-	in the same command
-stdin:
-	FOO=abc
-	FOO=123 BAR=$FOO
-	echo $BAR
-expected-stdout:
-	123
----
-name: xxx-multi-assignment-posix-cmd
-description:
-	Check that the behaviour for multiple assignments with a
-	command name matches POSIX. See:
-	http://thread.gmane.org/gmane.comp.standards.posix.austin.general/1925
-stdin:
-	X=a Y=b; X=$Y Y=$X "$__progname" -c 'echo 1 $X $Y .'; echo 2 $X $Y .
-	unset X Y Z
-	X=a Y=${X=b} Z=$X "$__progname" -c 'echo 3 $Z .'
-	unset X Y Z
-	X=a Y=${X=b} Z=$X; echo 4 $Z .
-expected-stdout:
-	1 b a .
-	2 a b .
-	3 b .
-	4 a .
----
-name: xxx-multi-assignment-posix-nocmd
-description:
-	Check that the behaviour for multiple assignments with no
-	command name matches POSIX (Debian #334182). See:
-	http://thread.gmane.org/gmane.comp.standards.posix.austin.general/1925
-stdin:
-	X=a Y=b; X=$Y Y=$X; echo 1 $X $Y .
-expected-stdout:
-	1 b b .
----
-name: xxx-multi-assignment-posix-subassign
-description:
-	Check that the behaviour for multiple assignments matches POSIX:
-	- The assignment words shall be expanded in the current execution
-	  environment.
-	- The assignments happen in the temporary execution environment.
-stdin:
-	unset X Y Z
-	Z=a Y=${X:=b} sh -c 'echo +$X+ +$Y+ +$Z+'
-	echo /$X/
-	# Now for the special case:
-	unset X Y Z
-	X= Y=${X:=b} sh -c 'echo +$X+ +$Y+'
-	echo /$X/
-expected-stdout:
-	++ +b+ +a+
-	/b/
-	++ +b+
-	/b/
----
-name: xxx-exec-environment-1
-description:
-	Check to see if exec sets it's environment correctly
-stdin:
-	print '#!'"$__progname"'\nunset RANDOM\nexport | while IFS= read -r' \
-	    'RANDOM; do eval '\''print -r -- "$RANDOM=$'\''"$RANDOM"'\'\"\'\; \
-	    done >env; chmod +x env; PATH=.:$PATH
-	FOO=bar exec env
-expected-stdout-pattern:
-	/(^|.*\n)FOO=bar\n/
----
-name: xxx-exec-environment-2
-description:
-	Check to make sure exec doesn't change environment if a program
-	isn't exec-ed
-stdin:
-	print '#!'"$__progname"'\nunset RANDOM\nexport | while IFS= read -r' \
-	    'RANDOM; do eval '\''print -r -- "$RANDOM=$'\''"$RANDOM"'\'\"\'\; \
-	    done >env; chmod +x env; PATH=.:$PATH
-	env >bar1
-	FOO=bar exec; env >bar2
-	cmp -s bar1 bar2
----
-name: exec-function-environment-1
-description:
-	Check assignments in function calls and whether they affect
-	the current execution environment (ksh93, SUSv4)
-stdin:
-	f() { a=2; }; g() { b=3; echo y$c-; }; a=1 f; b=2; c=1 g
-	echo x$a-$b- z$c-
-expected-stdout:
-	y1-
-	x2-3- z1-
----
-name: xxx-what-do-you-call-this-1
-stdin:
-	echo "${foo:-"a"}*"
-expected-stdout:
-	a*
----
-name: xxx-prefix-strip-1
-stdin:
-	foo='a cdef'
-	echo ${foo#a c}
-expected-stdout:
-	def
----
-name: xxx-prefix-strip-2
-stdin:
-	set a c
-	x='a cdef'
-	echo ${x#$*}
-expected-stdout:
-	def
----
-name: xxx-variable-syntax-1
-stdin:
-	echo ${:}
-expected-stderr-pattern:
-	/bad substitution/
-expected-exit: 1
----
-name: xxx-variable-syntax-2
-stdin:
-	set 0
-	echo ${*:0}
-expected-stderr-pattern:
-	/bad substitution/
-expected-exit: 1
----
-name: xxx-variable-syntax-3
-stdin:
-	set -A foo 0
-	echo ${foo[*]:0}
-expected-stderr-pattern:
-	/bad substitution/
-expected-exit: 1
----
-name: xxx-substitution-eval-order
-description:
-	Check order of evaluation of expressions
-stdin:
-	i=1 x= y=
-	set -A A abc def GHI j G k
-	echo ${A[x=(i+=1)]#${A[y=(i+=2)]}}
-	echo $x $y
-expected-stdout:
-	HI
-	2 4
----
-name: xxx-set-option-1
-description:
-	Check option parsing in set
-stdin:
-	set -vsA foo -- A 1 3 2
-	echo ${foo[*]}
-expected-stderr:
-	echo ${foo[*]}
-expected-stdout:
-	1 2 3 A
----
-name: xxx-exec-1
-description:
-	Check that exec exits for built-ins
-need-ctty: yes
-arguments: !-i!
-stdin:
-	exec echo hi
-	echo still herre
-expected-stdout:
-	hi
-expected-stderr-pattern: /.*/
----
-name: xxx-while-1
-description:
-	Check the return value of while loops
-	XXX need to do same for for/select/until loops
-stdin:
-	i=x
-	while [ $i != xxx ] ; do
-	    i=x$i
-	    if [ $i = xxx ] ; then
-		false
-		continue
-	    fi
-	done
-	echo loop1=$?
-	
-	i=x
-	while [ $i != xxx ] ; do
-	    i=x$i
-	    if [ $i = xxx ] ; then
-		false
-		break
-	    fi
-	done
-	echo loop2=$?
-	
-	i=x
-	while [ $i != xxx ] ; do
-	    i=x$i
-	    false
-	done
-	echo loop3=$?
-expected-stdout:
-	loop1=0
-	loop2=0
-	loop3=1
----
-name: xxx-status-1
-description:
-	Check that blank lines don't clear $?
-need-ctty: yes
-arguments: !-i!
-stdin:
-	(exit 1)
-	echo $?
-	(exit 1)
-	
-	echo $?
-	true
-expected-stdout:
-	1
-	1
-expected-stderr-pattern: /.*/
----
-name: xxx-status-2
-description:
-	Check that $? is preserved in subshells, includes, traps.
-stdin:
-	(exit 1)
-	
-	echo blank: $?
-	
-	(exit 2)
-	(echo subshell: $?)
-	
-	echo 'echo include: $?' > foo
-	(exit 3)
-	. ./foo
-	
-	trap 'echo trap: $?' ERR
-	(exit 4)
-	echo exit: $?
-expected-stdout:
-	blank: 1
-	subshell: 2
-	include: 3
-	trap: 4
-	exit: 4
----
-name: xxx-clean-chars-1
-description:
-	Check MAGIC character is stuffed correctly
-stdin:
-	echo `echo [\xA3`
-expected-stdout:
-	[\xA3
----
-name: xxx-param-subst-qmark-1
-description:
-	Check suppresion of error message with null string.  According to
-	POSIX, it shouldn't print the error as 'word' isn't ommitted.
-	ksh88/93, Solaris /bin/sh and /usr/xpg4/bin/sh all print the error,
-	that's why the condition is reversed.
-stdin:
-	unset foo
-	x=
-	echo x${foo?$x}
-expected-exit: 1
-# POSIX
-#expected-fail: yes
-#expected-stderr-pattern: !/not set/
-# common use
-expected-stderr-pattern: /parameter null or not set/
----
-name: xxx-param-_-1
-# fails due to weirdness of execv stuff
-category: !os:uwin-nt
-description:
-	Check c flag is set.
-arguments: !-c!echo "[$-]"!
-expected-stdout-pattern: /^\[.*c.*\]$/
----
-name: tilde-expand-1
-description:
-	Check tilde expansion after equal signs
-env-setup: !HOME=/sweet!
-stdin:
-	echo ${A=a=}~ b=~ c=d~ ~
-	set +o braceexpand
-	unset A
-	echo ${A=a=}~ b=~ c=d~ ~
-expected-stdout:
-	a=/sweet b=/sweet c=d~ /sweet
-	a=~ b=~ c=d~ /sweet
----
-name: tilde-expand-2
-description:
-	Check tilde expansion works
-env-setup: !HOME=/sweet!
-stdin:
-	wd=$PWD
-	cd /
-	plus=$(print -r -- ~+)
-	minus=$(print -r -- ~-)
-	nix=$(print -r -- ~)
-	[[ $plus = / ]]; echo one $? .
-	[[ $minus = "$wd" ]]; echo two $? .
-	[[ $nix = /sweet ]]; echo nix $? .
-expected-stdout:
-	one 0 .
-	two 0 .
-	nix 0 .
----
-name: exit-err-1
-description:
-	Check some "exit on error" conditions
-stdin:
-	print '#!'"$__progname"'\nexec "$1"' >env
-	print '#!'"$__progname"'\nexit 1' >false
-	chmod +x env false
-	PATH=.:$PATH
-	set -ex
-	env false && echo something
-	echo END
-expected-stdout:
-	END
-expected-stderr:
-	+ env false
-	+ echo END
----
-name: exit-err-2
-description:
-	Check some "exit on error" edge conditions (POSIXly)
-stdin:
-	print '#!'"$__progname"'\nexec "$1"' >env
-	print '#!'"$__progname"'\nexit 1' >false
-	print '#!'"$__progname"'\nexit 0' >true
-	chmod +x env false
-	PATH=.:$PATH
-	set -ex
-	if env true; then
-		env false && echo something
-	fi
-	echo END
-expected-stdout:
-	END
-expected-stderr:
-	+ env true
-	+ env false
-	+ echo END
----
-name: exit-err-3
-description:
-	pdksh regression which AT&T ksh does right
-	TFM says: [set] -e | errexit
-		Exit (after executing the ERR trap) ...
-stdin:
-	trap 'echo EXIT' EXIT
-	trap 'echo ERR' ERR
-	set -e
-	cd /XXXXX 2>/dev/null
-	echo DONE
-	exit 0
-expected-stdout:
-	ERR
-	EXIT
-expected-exit: e != 0
----
-name: exit-err-4
-description:
-	"set -e" test suite (POSIX)
-stdin:
-	set -e
-	echo pre
-	if true ; then
-		false && echo foo
-	fi
-	echo bar
-expected-stdout:
-	pre
-	bar
----
-name: exit-err-5
-description:
-	"set -e" test suite (POSIX)
-stdin:
-	set -e
-	foo() {
-		while [ "$1" ]; do
-			for E in $x; do
-				[ "$1" = "$E" ] && { shift ; continue 2 ; }
-			done
-			x="$x $1"
-			shift
-		done
-		echo $x
-	}
-	echo pre
-	foo a b b c
-	echo post
-expected-stdout:
-	pre
-	a b c
-	post
----
-name: exit-err-6
-description:
-	"set -e" test suite (BSD make)
-category: os:mirbsd
-stdin:
-	mkdir zd zd/a zd/b
-	print 'all:\n\t at echo eins\n\t at exit 42\n' >zd/a/Makefile
-	print 'all:\n\t at echo zwei\n' >zd/b/Makefile
-	wd=$(pwd)
-	set -e
-	for entry in a b; do (  set -e;  if [[ -d $wd/zd/$entry.i386 ]]; then  _newdir_="$entry.i386";  else  _newdir_="$entry";  fi;  if [[ -z $_THISDIR_ ]]; then  _nextdir_="$_newdir_";  else  _nextdir_="$_THISDIR_/$_newdir_";  fi;  _makefile_spec_=;  [[ ! -f $wd/zd/$_newdir_/Makefile.bsd-wrapper ]]  || _makefile_spec_="-f Makefile.bsd-wrapper";  subskipdir=;  for skipdir in ; do  subentry=${skipdir#$entry};  if [[ $subentry != $skipdir ]]; then  if [[ -z $subentry ]]; then  echo "($_nextdir_ skipped)";  break;  fi;  subskipdir="$subskipdir ${subentry#/}";  fi;  done;  if [[ -z $skipdir || -n $subentry ]]; then  echo "===> $_nextdir_";  cd $wd/zd/$_newdir_;  make SKIPDIR="$subskipdir" $_makefile_spec_  _THISDIR_="$_nextdir_"   all;  fi;  ) done 2>&1 | sed "s!$wd!WD!g"
-expected-stdout:
-	===> a
-	eins
-	*** Error code 42
-	
-	Stop in WD/zd/a (line 2 of Makefile).
----
-name: exit-err-7
-description:
-	"set -e" regression (LP#1104543)
-stdin:
-	set -e
-	bla() {
-		[ -x $PWD/nonexistant ] && $PWD/nonexistant
-	}
-	echo x
-	bla
-	echo y$?
-expected-stdout:
-	x
-expected-exit: 1
----
-name: exit-err-8
-description:
-	"set -e" regression (Debian #700526)
-stdin:
-	set -e
-	_db_cmd() { return $1; }
-	db_input() { _db_cmd 30; }
-	db_go() { _db_cmd 0; }
-	db_input || :
-	db_go
-	exit 0
----
-name: exit-enoent-1
-description:
-	SUSv4 says that the shell should exit with 126/127 in some situations
-stdin:
-	i=0
-	(echo; echo :) >x
-	"$__progname" ./x >/dev/null 2>&1; r=$?; echo $((i++)) $r .
-	"$__progname" -c ./x >/dev/null 2>&1; r=$?; echo $((i++)) $r .
-	echo exit 42 >x
-	"$__progname" ./x >/dev/null 2>&1; r=$?; echo $((i++)) $r .
-	"$__progname" -c ./x >/dev/null 2>&1; r=$?; echo $((i++)) $r .
-	rm -f x
-	"$__progname" ./x >/dev/null 2>&1; r=$?; echo $((i++)) $r .
-	"$__progname" -c ./x >/dev/null 2>&1; r=$?; echo $((i++)) $r .
-expected-stdout:
-	0 0 .
-	1 126 .
-	2 42 .
-	3 126 .
-	4 127 .
-	5 127 .
----
-name: exit-eval-1
-description:
-	Check eval vs substitution exit codes (ksh93 alike)
-stdin:
-	(exit 12)
-	eval $(false)
-	echo A $?
-	(exit 12)
-	eval ' $(false)'
-	echo B $?
-	(exit 12)
-	eval " $(false)"
-	echo C $?
-	(exit 12)
-	eval "eval $(false)"
-	echo D $?
-	(exit 12)
-	eval 'eval '"$(false)"
-	echo E $?
-	IFS="$IFS:"
-	(exit 12)
-	eval $(echo :; false)
-	echo F $?
-	echo -n "G "
-	(exit 12)
-	eval 'echo $?'
-	echo H $?
-expected-stdout:
-	A 0
-	B 1
-	C 0
-	D 0
-	E 0
-	F 0
-	G 12
-	H 0
----
-name: exit-trap-1
-description:
-	Check that "exit" with no arguments behaves SUSv4 conformant.
-stdin:
-	trap 'echo hi; exit' EXIT
-	exit 9
-expected-stdout:
-	hi
-expected-exit: 9
----
-name: exit-trap-2
-description:
-	Check that ERR and EXIT traps are run just like ksh93 does.
-	GNU bash does not run ERtrap in ±e eval-undef but runs it
-	twice (bug?) in +e eval-false, so does ksh93 (bug?), which
-	also has a bug to continue execution (echoing "and out" and
-	returning 0) in +e eval-undef.
-file-setup: file 644 "x"
-	v=; unset v
-	trap 'echo EXtrap' EXIT
-	trap 'echo ERtrap' ERR
-	set $1
-	echo "and run $2"
-	eval $2
-	echo and out
-file-setup: file 644 "xt"
-	v=; unset v
-	trap 'echo EXtrap' EXIT
-	trap 'echo ERtrap' ERR
-	set $1
-	echo 'and run true'
-	true
-	echo and out
-file-setup: file 644 "xf"
-	v=; unset v
-	trap 'echo EXtrap' EXIT
-	trap 'echo ERtrap' ERR
-	set $1
-	echo 'and run false'
-	false
-	echo and out
-file-setup: file 644 "xu"
-	v=; unset v
-	trap 'echo EXtrap' EXIT
-	trap 'echo ERtrap' ERR
-	set $1
-	echo 'and run ${v?}'
-	${v?}
-	echo and out
-stdin:
-	runtest() {
-		rm -f rc
-		(
-			"$__progname" "$@"
-			echo $? >rc
-		) 2>&1 | sed \
-		    -e 's/parameter not set/parameter null or not set/' \
-		    -e 's/[[]6]//' -e 's/: eval: line 1//' -e 's/: line 6//' \
-		    -e "s^${__progname%.exe}\.*e*x*e*: <stdin>\[[0-9]*]PROG"
-	}
-	xe=-e
-	echo : $xe
-	runtest x $xe true
-	echo = eval-true $(<rc) .
-	runtest x $xe false
-	echo = eval-false $(<rc) .
-	runtest x $xe '${v?}'
-	echo = eval-undef $(<rc) .
-	runtest xt $xe
-	echo = noeval-true $(<rc) .
-	runtest xf $xe
-	echo = noeval-false $(<rc) .
-	runtest xu $xe
-	echo = noeval-undef $(<rc) .
-	xe=+e
-	echo : $xe
-	runtest x $xe true
-	echo = eval-true $(<rc) .
-	runtest x $xe false
-	echo = eval-false $(<rc) .
-	runtest x $xe '${v?}'
-	echo = eval-undef $(<rc) .
-	runtest xt $xe
-	echo = noeval-true $(<rc) .
-	runtest xf $xe
-	echo = noeval-false $(<rc) .
-	runtest xu $xe
-	echo = noeval-undef $(<rc) .
-expected-stdout:
-	: -e
-	and run true
-	and out
-	EXtrap
-	= eval-true 0 .
-	and run false
-	ERtrap
-	EXtrap
-	= eval-false 1 .
-	and run ${v?}
-	x: v: parameter null or not set
-	ERtrap
-	EXtrap
-	= eval-undef 1 .
-	and run true
-	and out
-	EXtrap
-	= noeval-true 0 .
-	and run false
-	ERtrap
-	EXtrap
-	= noeval-false 1 .
-	and run ${v?}
-	xu: v: parameter null or not set
-	EXtrap
-	= noeval-undef 1 .
-	: +e
-	and run true
-	and out
-	EXtrap
-	= eval-true 0 .
-	and run false
-	ERtrap
-	and out
-	EXtrap
-	= eval-false 0 .
-	and run ${v?}
-	x: v: parameter null or not set
-	ERtrap
-	EXtrap
-	= eval-undef 1 .
-	and run true
-	and out
-	EXtrap
-	= noeval-true 0 .
-	and run false
-	ERtrap
-	and out
-	EXtrap
-	= noeval-false 0 .
-	and run ${v?}
-	xu: v: parameter null or not set
-	EXtrap
-	= noeval-undef 1 .
----
-name: exit-trap-interactive
-description:
-	Check that interactive shell doesn't exit via EXIT trap on syntax error
-arguments: !-i!
-stdin:
-	trap -- EXIT
-	echo Syntax error <
-	echo 'After error 1'
-	trap 'echo Exit trap' EXIT
-	echo Syntax error <
-	echo 'After error 2'
-	trap 'echo Exit trap' EXIT
-	exit
-	echo 'After exit'
-expected-stdout:
-	After error 1
-	After error 2
-	Exit trap
-expected-stderr-pattern:
-	/syntax error: 'newline' unexpected/
----
-name: test-stlt-1
-description:
-	Check that test also can handle string1 < string2 etc.
-stdin:
-	test 2005/10/08 '<' 2005/08/21 && echo ja || echo nein
-	test 2005/08/21 \< 2005/10/08 && echo ja || echo nein
-	test 2005/10/08 '>' 2005/08/21 && echo ja || echo nein
-	test 2005/08/21 \> 2005/10/08 && echo ja || echo nein
-expected-stdout:
-	nein
-	ja
-	ja
-	nein
-expected-stderr-pattern: !/unexpected op/
----
-name: test-precedence-1
-description:
-	Check a weird precedence case (and POSIX echo)
-stdin:
-	test \( -f = -f \)
-	rv=$?
-	echo $rv
-expected-stdout:
-	0
----
-name: test-option-1
-description:
-	Test the test -o operator
-stdin:
-	runtest() {
-		test -o $1; echo $?
-		[ -o $1 ]; echo $?
-		[[ -o $1 ]]; echo $?
-	}
-	if_test() {
-		test -o $1 -o -o !$1; echo $?
-		[ -o $1 -o -o !$1 ]; echo $?
-		[[ -o $1 || -o !$1 ]]; echo $?
-		test -o ?$1; echo $?
-	}
-	echo 0y $(if_test utf8-mode) =
-	echo 0n $(if_test utf8-hack) =
-	echo 1= $(runtest utf8-hack) =
-	echo 2= $(runtest !utf8-hack) =
-	echo 3= $(runtest ?utf8-hack) =
-	set +U
-	echo 1+ $(runtest utf8-mode) =
-	echo 2+ $(runtest !utf8-mode) =
-	echo 3+ $(runtest ?utf8-mode) =
-	set -U
-	echo 1- $(runtest utf8-mode) =
-	echo 2- $(runtest !utf8-mode) =
-	echo 3- $(runtest ?utf8-mode) =
-	echo = short flags =
-	echo 0y $(if_test -U) =
-	echo 0y $(if_test +U) =
-	echo 0n $(if_test -_) =
-	echo 0n $(if_test -U-) =
-	echo 1= $(runtest -_) =
-	echo 2= $(runtest !-_) =
-	echo 3= $(runtest ?-_) =
-	set +U
-	echo 1+ $(runtest -U) =
-	echo 2+ $(runtest !-U) =
-	echo 3+ $(runtest ?-U) =
-	echo 1+ $(runtest +U) =
-	echo 2+ $(runtest !+U) =
-	echo 3+ $(runtest ?+U) =
-	set -U
-	echo 1- $(runtest -U) =
-	echo 2- $(runtest !-U) =
-	echo 3- $(runtest ?-U) =
-	echo 1- $(runtest +U) =
-	echo 2- $(runtest !+U) =
-	echo 3- $(runtest ?+U) =
-expected-stdout:
-	0y 0 0 0 0 =
-	0n 1 1 1 1 =
-	1= 1 1 1 =
-	2= 1 1 1 =
-	3= 1 1 1 =
-	1+ 1 1 1 =
-	2+ 0 0 0 =
-	3+ 0 0 0 =
-	1- 0 0 0 =
-	2- 1 1 1 =
-	3- 0 0 0 =
-	= short flags =
-	0y 0 0 0 0 =
-	0y 0 0 0 0 =
-	0n 1 1 1 1 =
-	0n 1 1 1 1 =
-	1= 1 1 1 =
-	2= 1 1 1 =
-	3= 1 1 1 =
-	1+ 1 1 1 =
-	2+ 0 0 0 =
-	3+ 0 0 0 =
-	1+ 1 1 1 =
-	2+ 0 0 0 =
-	3+ 0 0 0 =
-	1- 0 0 0 =
-	2- 1 1 1 =
-	3- 0 0 0 =
-	1- 0 0 0 =
-	2- 1 1 1 =
-	3- 0 0 0 =
----
-name: mkshrc-1
-description:
-	Check that ~/.mkshrc works correctly.
-	Part 1: verify user environment is not read (internal)
-stdin:
-	echo x $FNORD
-expected-stdout:
-	x
----
-name: mkshrc-2a
-description:
-	Check that ~/.mkshrc works correctly.
-	Part 2: verify mkshrc is not read (non-interactive shells)
-file-setup: file 644 ".mkshrc"
-	FNORD=42
-env-setup: !HOME=.!ENV=!
-stdin:
-	echo x $FNORD
-expected-stdout:
-	x
----
-name: mkshrc-2b
-description:
-	Check that ~/.mkshrc works correctly.
-	Part 2: verify mkshrc can be read (interactive shells)
-file-setup: file 644 ".mkshrc"
-	FNORD=42
-need-ctty: yes
-arguments: !-i!
-env-setup: !HOME=.!ENV=!PS1=!
-stdin:
-	echo x $FNORD
-expected-stdout:
-	x 42
-expected-stderr-pattern:
-	/(# )*/
----
-name: mkshrc-3
-description:
-	Check that ~/.mkshrc works correctly.
-	Part 3: verify mkshrc can be turned off
-file-setup: file 644 ".mkshrc"
-	FNORD=42
-env-setup: !HOME=.!ENV=nonexistant!
-stdin:
-	echo x $FNORD
-expected-stdout:
-	x
----
-name: sh-mode-1
-description:
-	Check that sh mode turns braceexpand off
-	and that that works correctly
-stdin:
-	set -o braceexpand
-	set +o sh
-	[[ $(set +o) == *@(-o sh)@(| *) ]] && echo sh || echo nosh
-	[[ $(set +o) == *@(-o braceexpand)@(| *) ]] && echo brex || echo nobrex
-	echo {a,b,c}
-	set +o braceexpand
-	echo {a,b,c}
-	set -o braceexpand
-	echo {a,b,c}
-	set -o sh
-	echo {a,b,c}
-	[[ $(set +o) == *@(-o sh)@(| *) ]] && echo sh || echo nosh
-	[[ $(set +o) == *@(-o braceexpand)@(| *) ]] && echo brex || echo nobrex
-	set -o braceexpand
-	echo {a,b,c}
-	[[ $(set +o) == *@(-o sh)@(| *) ]] && echo sh || echo nosh
-	[[ $(set +o) == *@(-o braceexpand)@(| *) ]] && echo brex || echo nobrex
-expected-stdout:
-	nosh
-	brex
-	a b c
-	{a,b,c}
-	a b c
-	{a,b,c}
-	sh
-	nobrex
-	a b c
-	sh
-	brex
----
-name: sh-mode-2a
-description:
-	Check that posix or sh mode is *not* automatically turned on
-category: !binsh
-stdin:
-	ln -s "$__progname" ksh || cp "$__progname" ksh
-	ln -s "$__progname" sh || cp "$__progname" sh
-	ln -s "$__progname" ./-ksh || cp "$__progname" ./-ksh
-	ln -s "$__progname" ./-sh || cp "$__progname" ./-sh
-	for shell in {,-}{,k}sh; do
-		print -- $shell $(./$shell +l -c \
-		    '[[ $(set +o) == *"-o "@(sh|posix)@(| *) ]] && echo sh || echo nosh')
-	done
-expected-stdout:
-	sh nosh
-	ksh nosh
-	-sh nosh
-	-ksh nosh
----
-name: sh-mode-2b
-description:
-	Check that posix or sh mode *is* automatically turned on
-category: binsh
-stdin:
-	ln -s "$__progname" ksh || cp "$__progname" ksh
-	ln -s "$__progname" sh || cp "$__progname" sh
-	ln -s "$__progname" ./-ksh || cp "$__progname" ./-ksh
-	ln -s "$__progname" ./-sh || cp "$__progname" ./-sh
-	for shell in {,-}{,k}sh; do
-		print -- $shell $(./$shell +l -c \
-		    '[[ $(set +o) == *"-o "@(sh|posix)@(| *) ]] && echo sh || echo nosh')
-	done
-expected-stdout:
-	sh sh
-	ksh nosh
-	-sh sh
-	-ksh nosh
----
-name: pipeline-1
-description:
-	pdksh bug: last command of a pipeline is executed in a
-	subshell - make sure it still is, scripts depend on it
-file-setup: file 644 "abcx"
-file-setup: file 644 "abcy"
-stdin:
-	echo *
-	echo a | while read d; do
-		echo $d
-		echo $d*
-		echo *
-		set -o noglob
-		echo $d*
-		echo *
-	done
-	echo *
-expected-stdout:
-	abcx abcy
-	a
-	abcx abcy
-	abcx abcy
-	a*
-	*
-	abcx abcy
----
-name: pipeline-2
-description:
-	check that co-processes work with TCOMs, TPIPEs and TPARENs
-category: !nojsig
-stdin:
-	"$__progname" -c 'i=100; echo hi |& while read -p line; do echo "$((i++)) $line"; done'
-	"$__progname" -c 'i=200; echo hi | cat |& while read -p line; do echo "$((i++)) $line"; done'
-	"$__progname" -c 'i=300; (echo hi | cat) |& while read -p line; do echo "$((i++)) $line"; done'
-expected-stdout:
-	100 hi
-	200 hi
-	300 hi
----
-name: pipeline-3
-description:
-	Check that PIPESTATUS does what it's supposed to
-stdin:
-	echo 1 $PIPESTATUS .
-	echo 2 ${PIPESTATUS[0]} .
-	echo 3 ${PIPESTATUS[1]} .
-	(echo x; exit 12) | (cat; exit 23) | (cat; exit 42)
-	echo 5 $? , $PIPESTATUS , ${PIPESTATUS[0]} , ${PIPESTATUS[1]} , ${PIPESTATUS[2]} , ${PIPESTATUS[3]} .
-	echo 6 ${PIPESTATUS[0]} .
-	set | fgrep PIPESTATUS
-	echo 8 $(set | fgrep PIPESTATUS) .
-expected-stdout:
-	1 0 .
-	2 0 .
-	3 .
-	x
-	5 42 , 12 , 12 , 23 , 42 , .
-	6 0 .
-	PIPESTATUS[0]=0
-	8 PIPESTATUS[0]=0 PIPESTATUS[1]=0 .
----
-name: pipeline-4
-description:
-	Check that "set -o pipefail" does what it's supposed to
-stdin:
-	echo 1 "$("$__progname" -c '(exit 12) | (exit 23) | (exit 42); echo $?')" .
-	echo 2 "$("$__progname" -c '! (exit 12) | (exit 23) | (exit 42); echo $?')" .
-	echo 3 "$("$__progname" -o pipefail -c '(exit 12) | (exit 23) | (exit 42); echo $?')" .
-	echo 4 "$("$__progname" -o pipefail -c '! (exit 12) | (exit 23) | (exit 42); echo $?')" .
-	echo 5 "$("$__progname" -c '(exit 23) | (exit 42) | :; echo $?')" .
-	echo 6 "$("$__progname" -c '! (exit 23) | (exit 42) | :; echo $?')" .
-	echo 7 "$("$__progname" -o pipefail -c '(exit 23) | (exit 42) | :; echo $?')" .
-	echo 8 "$("$__progname" -o pipefail -c '! (exit 23) | (exit 42) | :; echo $?')" .
-expected-stdout:
-	1 42 .
-	2 0 .
-	3 42 .
-	4 0 .
-	5 0 .
-	6 1 .
-	7 42 .
-	8 0 .
----
-name: persist-history-1
-description:
-	Check if persistent history saving works
-category: !no-histfile
-need-ctty: yes
-arguments: !-i!
-env-setup: !ENV=./Env!HISTFILE=hist.file!
-file-setup: file 644 "Env"
-	PS1=X
-stdin:
-	cat hist.file
-expected-stdout-pattern:
-	/cat hist.file/
-expected-stderr-pattern:
-	/^X*$/
----
-name: typeset-1
-description:
-	Check that global does what typeset is supposed to do
-stdin:
-	set -A arrfoo 65
-	foo() {
-		global -Uui16 arrfoo[*]
-	}
-	echo before ${arrfoo[0]} .
-	foo
-	echo after ${arrfoo[0]} .
-	set -A arrbar 65
-	bar() {
-		echo inside before ${arrbar[0]} .
-		arrbar[0]=97
-		echo inside changed ${arrbar[0]} .
-		global -Uui16 arrbar[*]
-		echo inside typeset ${arrbar[0]} .
-		arrbar[0]=48
-		echo inside changed ${arrbar[0]} .
-	}
-	echo before ${arrbar[0]} .
-	bar
-	echo after ${arrbar[0]} .
-expected-stdout:
-	before 65 .
-	after 16#41 .
-	before 65 .
-	inside before 65 .
-	inside changed 97 .
-	inside typeset 16#61 .
-	inside changed 16#30 .
-	after 16#30 .
----
-name: typeset-padding-1
-description:
-	Check if left/right justification works as per TFM
-stdin:
-	typeset -L10 ln=0hall0
-	typeset -R10 rn=0hall0
-	typeset -ZL10 lz=0hall0
-	typeset -ZR10 rz=0hall0
-	typeset -Z10 rx=" hallo "
-	echo "<$ln> <$rn> <$lz> <$rz> <$rx>"
-expected-stdout:
-	<0hall0    > <    0hall0> <hall0     > <00000hall0> <0000 hallo>
----
-name: typeset-padding-2
-description:
-	Check if base-!10 integers are padded right
-stdin:
-	typeset -Uui16 -L9 ln=16#1
-	typeset -Uui16 -R9 rn=16#1
-	typeset -Uui16 -Z9 zn=16#1
-	typeset -L9 ls=16#1
-	typeset -R9 rs=16#1
-	typeset -Z9 zs=16#1
-	echo "<$ln> <$rn> <$zn> <$ls> <$rs> <$zs>"
-expected-stdout:
-	<16#1     > <     16#1> <16#000001> <16#1     > <     16#1> <0000016#1>
----
-name: utf8bom-1
-description:
-	Check that the UTF-8 Byte Order Mark is ignored as the first
-	multibyte character of the shell input (with -c, from standard
-	input, as file, or as eval argument), but nowhere else
-# breaks on Mac OSX (HFS+ non-standard Unicode canonical decomposition)
-category: !os:darwin
-stdin:
-	mkdir foo
-	print '#!/bin/sh\necho ohne' >foo/fnord
-	print '#!/bin/sh\necho mit' >foo/fnord
-	print 'fnord\nfnord\nfnord\nfnord' >foo/bar
-	print eval \''fnord\nfnord\nfnord\nfnord'\' >foo/zoo
-	set -A anzahl -- foo/*
-	echo got ${#anzahl[*]} files
-	chmod +x foo/*
-	export PATH=$(pwd)/foo:$PATH
-	"$__progname" -c 'fnord'
-	echo =
-	"$__progname" -c 'fnord; fnord; fnord; fnord'
-	echo =
-	"$__progname" foo/bar
-	echo =
-	"$__progname" <foo/bar
-	echo =
-	"$__progname" foo/zoo
-	echo =
-	"$__progname" -c 'echo : $(fnord)'
-	rm -rf foo
-expected-stdout:
-	got 4 files
-	ohne
-	=
-	ohne
-	ohne
-	mit
-	ohne
-	=
-	ohne
-	ohne
-	mit
-	ohne
-	=
-	ohne
-	ohne
-	mit
-	ohne
-	=
-	ohne
-	ohne
-	mit
-	ohne
-	=
-	: ohne
----
-name: utf8bom-2
-description:
-	Check that we can execute BOM-shebangs (failures not fatal)
-	XXX if the OS can already execute them, we lose
-	note: cygwin execve(2) doesn't return to us with ENOEXEC, we lose
-	note: Ultrix perl5 t4 returns 65280 (exit-code 255) and no text
-need-pass: no
-category: !os:cygwin,!os:msys,!os:ultrix,!os:uwin-nt,!smksh
-env-setup: !FOO=BAR!
-stdin:
-	print '#!'"$__progname"'\nprint "1 a=$ENV{FOO}";' >t1
-	print '#!'"$__progname"'\nprint "2 a=$ENV{FOO}";' >t2
-	print '#!'"$__perlname"'\nprint "3 a=$ENV{FOO}\n";' >t3
-	print '#!'"$__perlname"'\nprint "4 a=$ENV{FOO}\n";' >t4
-	chmod +x t?
-	./t1
-	./t2
-	./t3
-	./t4
-expected-stdout:
-	1 a=/nonexistant{FOO}
-	2 a=/nonexistant{FOO}
-	3 a=BAR
-	4 a=BAR
-expected-stderr-pattern:
-	/(Unrecognized character .... ignored at \..t4 line 1)*/
----
-name: utf8opt-1a
-description:
-	Check that the utf8-mode flag is not set at non-interactive startup
-category: !os:hpux
-env-setup: !PS1=!PS2=!LC_CTYPE=en_US.UTF-8!
-stdin:
-	if [[ $- = *U* ]]; then
-		echo is set
-	else
-		echo is not set
-	fi
-expected-stdout:
-	is not set
----
-name: utf8opt-1b
-description:
-	Check that the utf8-mode flag is not set at non-interactive startup
-category: os:hpux
-env-setup: !PS1=!PS2=!LC_CTYPE=en_US.utf8!
-stdin:
-	if [[ $- = *U* ]]; then
-		echo is set
-	else
-		echo is not set
-	fi
-expected-stdout:
-	is not set
----
-name: utf8opt-2a
-description:
-	Check that the utf8-mode flag is set at interactive startup.
-	-DMKSH_ASSUME_UTF8=0 => expected failure, please ignore
-	-DMKSH_ASSUME_UTF8=1 => not expected, please investigate
-	-UMKSH_ASSUME_UTF8 => not expected, but if your OS is old,
-	 try passing HAVE_SETLOCALE_CTYPE=0 to Build.sh
-need-pass: no
-category: !os:hpux,!os:msys
-need-ctty: yes
-arguments: !-i!
-env-setup: !PS1=!PS2=!LC_CTYPE=en_US.UTF-8!
-stdin:
-	if [[ $- = *U* ]]; then
-		echo is set
-	else
-		echo is not set
-	fi
-expected-stdout:
-	is set
-expected-stderr-pattern:
-	/(# )*/
----
-name: utf8opt-2b
-description:
-	Check that the utf8-mode flag is set at interactive startup
-	Expected failure if -DMKSH_ASSUME_UTF8=0
-category: os:hpux
-need-ctty: yes
-arguments: !-i!
-env-setup: !PS1=!PS2=!LC_CTYPE=en_US.utf8!
-stdin:
-	if [[ $- = *U* ]]; then
-		echo is set
-	else
-		echo is not set
-	fi
-expected-stdout:
-	is set
-expected-stderr-pattern:
-	/(# )*/
----
-name: utf8opt-3a
-description:
-	Ensure ±U on the command line is honoured
-	(these two tests may pass falsely depending on CPPFLAGS)
-stdin:
-	export i=0
-	code='if [[ $- = *U* ]]; then echo $i on; else echo $i off; fi'
-	let i++; "$__progname" -U -c "$code"
-	let i++; "$__progname" +U -c "$code"
-	echo $((++i)) done
-expected-stdout:
-	1 on
-	2 off
-	3 done
----
-name: utf8opt-3b
-description:
-	Ensure ±U on the command line is honoured, interactive shells
-need-ctty: yes
-stdin:
-	export i=0
-	code='if [[ $- = *U* ]]; then echo $i on; else echo $i off; fi'
-	let i++; "$__progname" -U -ic "$code"
-	let i++; "$__progname" +U -ic "$code"
-	echo $((++i)) done
-expected-stdout:
-	1 on
-	2 off
-	3 done
----
-name: aliases-1
-description:
-	Check if built-in shell aliases are okay
-category: !android,!arge
-stdin:
-	alias
-	typeset -f
-expected-stdout:
-	autoload='typeset -fu'
-	functions='typeset -f'
-	hash='alias -t'
-	history='fc -l'
-	integer='typeset -i'
-	local=typeset
-	login='exec login'
-	nameref='typeset -n'
-	nohup='nohup '
-	r='fc -e -'
-	source='PATH=$PATH:. command .'
-	stop='kill -STOP'
-	suspend='kill -STOP $$'
-	type='whence -v'
----
-name: aliases-1-hartz4
-description:
-	Check if built-in shell aliases are okay
-category: android,arge
-stdin:
-	alias
-	typeset -f
-expected-stdout:
-	autoload='typeset -fu'
-	functions='typeset -f'
-	hash='alias -t'
-	history='fc -l'
-	integer='typeset -i'
-	local=typeset
-	login='exec login'
-	nameref='typeset -n'
-	nohup='nohup '
-	r='fc -e -'
-	source='PATH=$PATH:. command .'
-	type='whence -v'
----
-name: aliases-2a
-description:
-	Check if “set -o sh” disables built-in aliases (except a few)
-category: disabled
-arguments: !-o!sh!
-stdin:
-	alias
-	typeset -f
-expected-stdout:
-	integer='typeset -i'
-	local=typeset
----
-name: aliases-3a
-description:
-	Check if running as sh disables built-in aliases (except a few)
-category: disabled
-stdin:
-	cp "$__progname" sh
-	./sh -c 'alias; typeset -f'
-	rm -f sh
-expected-stdout:
-	integer='typeset -i'
-	local=typeset
----
-name: aliases-2b
-description:
-	Check if “set -o sh” does not influence built-in aliases
-category: !android,!arge
-arguments: !-o!sh!
-stdin:
-	alias
-	typeset -f
-expected-stdout:
-	autoload='typeset -fu'
-	functions='typeset -f'
-	hash='alias -t'
-	history='fc -l'
-	integer='typeset -i'
-	local=typeset
-	login='exec login'
-	nameref='typeset -n'
-	nohup='nohup '
-	r='fc -e -'
-	source='PATH=$PATH:. command .'
-	stop='kill -STOP'
-	suspend='kill -STOP $$'
-	type='whence -v'
----
-name: aliases-3b
-description:
-	Check if running as sh does not influence built-in aliases
-category: !android,!arge
-stdin:
-	cp "$__progname" sh
-	./sh -c 'alias; typeset -f'
-	rm -f sh
-expected-stdout:
-	autoload='typeset -fu'
-	functions='typeset -f'
-	hash='alias -t'
-	history='fc -l'
-	integer='typeset -i'
-	local=typeset
-	login='exec login'
-	nameref='typeset -n'
-	nohup='nohup '
-	r='fc -e -'
-	source='PATH=$PATH:. command .'
-	stop='kill -STOP'
-	suspend='kill -STOP $$'
-	type='whence -v'
----
-name: aliases-2b-hartz4
-description:
-	Check if “set -o sh” does not influence built-in aliases
-category: android,arge
-arguments: !-o!sh!
-stdin:
-	alias
-	typeset -f
-expected-stdout:
-	autoload='typeset -fu'
-	functions='typeset -f'
-	hash='alias -t'
-	history='fc -l'
-	integer='typeset -i'
-	local=typeset
-	login='exec login'
-	nameref='typeset -n'
-	nohup='nohup '
-	r='fc -e -'
-	source='PATH=$PATH:. command .'
-	type='whence -v'
----
-name: aliases-3b-hartz4
-description:
-	Check if running as sh does not influence built-in aliases
-category: android,arge
-stdin:
-	cp "$__progname" sh
-	./sh -c 'alias; typeset -f'
-	rm -f sh
-expected-stdout:
-	autoload='typeset -fu'
-	functions='typeset -f'
-	hash='alias -t'
-	history='fc -l'
-	integer='typeset -i'
-	local=typeset
-	login='exec login'
-	nameref='typeset -n'
-	nohup='nohup '
-	r='fc -e -'
-	source='PATH=$PATH:. command .'
-	type='whence -v'
----
-name: aliases-cmdline
-description:
-	Check that aliases work from the command line (Debian #517009)
-	Note that due to the nature of the lexing process, defining
-	aliases in COMSUBs then immediately using them, and things
-	like 'alias foo=bar && foo', still fail.
-stdin:
-	"$__progname" -c $'alias a="echo OK"\na'
-expected-stdout:
-	OK
----
-name: aliases-funcdef-1
-description:
-	Check if POSIX functions take precedences over aliases
-stdin:
-	alias foo='echo makro'
-	foo() {
-		echo funktion
-	}
-	foo
-expected-stdout:
-	funktion
----
-name: aliases-funcdef-2
-description:
-	Check if POSIX functions take precedences over aliases
-stdin:
-	alias foo='echo makro'
-	foo () {
-		echo funktion
-	}
-	foo
-expected-stdout:
-	funktion
----
-name: aliases-funcdef-3
-description:
-	Check if aliases take precedences over Korn functions
-stdin:
-	alias foo='echo makro'
-	function foo {
-		echo funktion
-	}
-	foo
-expected-stdout:
-	makro
----
-name: aliases-funcdef-4
-description:
-	Functions should only take over if actually being defined
-stdin:
-	alias local
-	:|| local() { :; }
-	alias local
-expected-stdout:
-	local=typeset
-	local=typeset
----
-name: arrays-1
-description:
-	Check if Korn Shell arrays work as expected
-stdin:
-	v="c d"
-	set -A foo -- a \$v "$v" '$v' b
-	echo "${#foo[*]}|${foo[0]}|${foo[1]}|${foo[2]}|${foo[3]}|${foo[4]}|"
-expected-stdout:
-	5|a|$v|c d|$v|b|
----
-name: arrays-2a
-description:
-	Check if bash-style arrays work as expected
-stdin:
-	v="c d"
-	foo=(a \$v "$v" '$v' b)
-	echo "${#foo[*]}|${foo[0]}|${foo[1]}|${foo[2]}|${foo[3]}|${foo[4]}|"
-expected-stdout:
-	5|a|$v|c d|$v|b|
----
-name: arrays-2b
-description:
-	Check if bash-style arrays work as expected, with newlines
-stdin:
-	print '#!'"$__progname"'\nfor x in "$@"; do print -nr -- "$x|"; done' >pfp
-	chmod +x pfp
-	test -n "$ZSH_VERSION" && setopt KSH_ARRAYS
-	v="e f"
-	foo=(a
-		bc
-		d \$v "$v" '$v' g
-	)
-	./pfp "${#foo[*]}" "${foo[0]}" "${foo[1]}" "${foo[2]}" "${foo[3]}" "${foo[4]}" "${foo[5]}" "${foo[6]}"; echo
-	foo=(a\
-		bc
-		d \$v "$v" '$v' g
-	)
-	./pfp "${#foo[*]}" "${foo[0]}" "${foo[1]}" "${foo[2]}" "${foo[3]}" "${foo[4]}" "${foo[5]}" "${foo[6]}"; echo
-	foo=(a\
-	bc\\
-		d \$v "$v" '$v'
-	g)
-	./pfp "${#foo[*]}" "${foo[0]}" "${foo[1]}" "${foo[2]}" "${foo[3]}" "${foo[4]}" "${foo[5]}" "${foo[6]}"; echo
-expected-stdout:
-	7|a|bc|d|$v|e f|$v|g|
-	7|a|bc|d|$v|e f|$v|g|
-	6|abc\|d|$v|e f|$v|g||
----
-name: arrays-3
-description:
-	Check if array bounds are uint32_t
-stdin:
-	set -A foo a b c
-	foo[4097]=d
-	foo[2147483637]=e
-	echo ${foo[*]}
-	foo[-1]=f
-	echo ${foo[4294967295]} g ${foo[*]}
-expected-stdout:
-	a b c d e
-	f g a b c d e f
----
-name: arrays-4
-description:
-	Check if Korn Shell arrays with specified indices work as expected
-stdin:
-	v="c d"
-	set -A foo -- [1]=\$v [2]="$v" [4]='$v' [0]=a [5]=b
-	echo "${#foo[*]}|${foo[0]}|${foo[1]}|${foo[2]}|${foo[3]}|${foo[4]}|${foo[5]}|"
-expected-stdout:
-	5|a|$v|c d||$v|b|
----
-name: arrays-5
-description:
-	Check if bash-style arrays with specified indices work as expected
-stdin:
-	v="c d"
-	foo=([1]=\$v [2]="$v" [4]='$v' [0]=a [5]=b)
-	echo "${#foo[*]}|${foo[0]}|${foo[1]}|${foo[2]}|${foo[3]}|${foo[4]}|${foo[5]}|"
-	x=([128]=foo bar baz)
-	echo k= ${!x[*]} .
-	echo v= ${x[*]} .
-expected-stdout:
-	5|a|$v|c d||$v|b|
-	k= 128 129 130 .
-	v= foo bar baz .
----
-name: arrays-6
-description:
-	Check if we can get the array keys (indices) for indexed arrays,
-	Korn shell style
-stdin:
-	of() {
-		i=0
-		for x in "$@"; do
-			echo -n "$((i++))<$x>"
-		done
-		echo
-	}
-	foo[1]=eins
-	set | grep '^foo'
-	echo =
-	foo[0]=zwei
-	foo[4]=drei
-	set | grep '^foo'
-	echo =
-	echo a $(of ${foo[*]}) = $(of ${bar[*]}) a
-	echo b $(of "${foo[*]}") = $(of "${bar[*]}") b
-	echo c $(of ${foo[@]}) = $(of ${bar[@]}) c
-	echo d $(of "${foo[@]}") = $(of "${bar[@]}") d
-	echo e $(of ${!foo[*]}) = $(of ${!bar[*]}) e
-	echo f $(of "${!foo[*]}") = $(of "${!bar[*]}") f
-	echo g $(of ${!foo[@]}) = $(of ${!bar[@]}) g
-	echo h $(of "${!foo[@]}") = $(of "${!bar[@]}") h
-expected-stdout:
-	foo[1]=eins
-	=
-	foo[0]=zwei
-	foo[1]=eins
-	foo[4]=drei
-	=
-	a 0<zwei>1<eins>2<drei> = a
-	b 0<zwei eins drei> = 0<> b
-	c 0<zwei>1<eins>2<drei> = c
-	d 0<zwei>1<eins>2<drei> = d
-	e 0<0>1<1>2<4> = e
-	f 0<0 1 4> = 0<> f
-	g 0<0>1<1>2<4> = g
-	h 0<0>1<1>2<4> = h
----
-name: arrays-7
-description:
-	Check if we can get the array keys (indices) for indexed arrays,
-	Korn shell style, in some corner cases
-stdin:
-	echo !arz: ${!arz}
-	echo !arz[0]: ${!arz[0]}
-	echo !arz[1]: ${!arz[1]}
-	arz=foo
-	echo !arz: ${!arz}
-	echo !arz[0]: ${!arz[0]}
-	echo !arz[1]: ${!arz[1]}
-	unset arz
-	echo !arz: ${!arz}
-	echo !arz[0]: ${!arz[0]}
-	echo !arz[1]: ${!arz[1]}
-expected-stdout:
-	!arz: 0
-	!arz[0]:
-	!arz[1]:
-	!arz: arz
-	!arz[0]: 0
-	!arz[1]:
-	!arz: 0
-	!arz[0]:
-	!arz[1]:
----
-name: arrays-8
-description:
-	Check some behavioural rules for arrays.
-stdin:
-	fna() {
-		set -A aa 9
-	}
-	fnb() {
-		typeset ab
-		set -A ab 9
-	}
-	fnc() {
-		typeset ac
-		set -A ac 91
-		unset ac
-		set -A ac 92
-	}
-	fnd() {
-		set +A ad 9
-	}
-	fne() {
-		unset ae
-		set +A ae 9
-	}
-	fnf() {
-		unset af[0]
-		set +A af 9
-	}
-	fng() {
-		unset ag[*]
-		set +A ag 9
-	}
-	set -A aa 1 2
-	set -A ab 1 2
-	set -A ac 1 2
-	set -A ad 1 2
-	set -A ae 1 2
-	set -A af 1 2
-	set -A ag 1 2
-	set -A ah 1 2
-	typeset -Z3 aa ab ac ad ae af ag
-	print 1a ${aa[*]} .
-	print 1b ${ab[*]} .
-	print 1c ${ac[*]} .
-	print 1d ${ad[*]} .
-	print 1e ${ae[*]} .
-	print 1f ${af[*]} .
-	print 1g ${ag[*]} .
-	print 1h ${ah[*]} .
-	fna
-	fnb
-	fnc
-	fnd
-	fne
-	fnf
-	fng
-	typeset -Z5 ah[*]
-	print 2a ${aa[*]} .
-	print 2b ${ab[*]} .
-	print 2c ${ac[*]} .
-	print 2d ${ad[*]} .
-	print 2e ${ae[*]} .
-	print 2f ${af[*]} .
-	print 2g ${ag[*]} .
-	print 2h ${ah[*]} .
-expected-stdout:
-	1a 001 002 .
-	1b 001 002 .
-	1c 001 002 .
-	1d 001 002 .
-	1e 001 002 .
-	1f 001 002 .
-	1g 001 002 .
-	1h 1 2 .
-	2a 9 .
-	2b 001 002 .
-	2c 92 .
-	2d 009 002 .
-	2e 9 .
-	2f 9 002 .
-	2g 009 .
-	2h 00001 00002 .
----
-name: arrays-9a
-description:
-	Check that we can concatenate arrays
-stdin:
-	unset foo; foo=(bar); foo+=(baz); echo 1 ${!foo[*]} : ${foo[*]} .
-	unset foo; foo=(foo bar); foo+=(baz); echo 2 ${!foo[*]} : ${foo[*]} .
-	unset foo; foo=([2]=foo [0]=bar); foo+=(baz [5]=quux); echo 3 ${!foo[*]} : ${foo[*]} .
-expected-stdout:
-	1 0 1 : bar baz .
-	2 0 1 2 : foo bar baz .
-	3 0 2 3 5 : bar foo baz quux .
----
-name: arrays-9b
-description:
-	Check that we can concatenate parameters too
-stdin:
-	unset foo; foo=bar; foo+=baz; echo 1 $foo .
-	unset foo; typeset -i16 foo=10; foo+=20; echo 2 $foo .
-expected-stdout:
-	1 barbaz .
-	2 16#a20 .
----
-name: arrassign-basic
-description:
-	Check basic whitespace conserving properties of wdarrassign
-stdin:
-	a=($(echo a  b))
-	b=($(echo "a  b"))
-	c=("$(echo "a  b")")
-	d=("$(echo a  b)")
-	a+=($(echo c  d))
-	b+=($(echo "c  d"))
-	c+=("$(echo "c  d")")
-	d+=("$(echo c  d)")
-	echo ".a:${a[0]}.${a[1]}.${a[2]}.${a[3]}:"
-	echo ".b:${b[0]}.${b[1]}.${b[2]}.${b[3]}:"
-	echo ".c:${c[0]}.${c[1]}.${c[2]}.${c[3]}:"
-	echo ".d:${d[0]}.${d[1]}.${d[2]}.${d[3]}:"
-expected-stdout:
-	.a:a.b.c.d:
-	.b:a.b.c.d:
-	.c:a  b.c  d..:
-	.d:a b.c d..:
----
-name: arrassign-fnc-none
-description:
-	Check locality of array access inside a function
-stdin:
-	function fn {
-		x+=(f)
-		echo ".fn:${x[0]}.${x[1]}.${x[2]}.${x[3]}:"
-	}
-	function rfn {
-		if [[ -n $BASH_VERSION ]]; then
-			y=()
-		else
-			set -A y
-		fi
-		y+=(f)
-		echo ".rfn:${y[0]}.${y[1]}.${y[2]}.${y[3]}:"
-	}
-	x=(m m)
-	y=(m m)
-	echo ".f0:${x[0]}.${x[1]}.${x[2]}.${x[3]}:"
-	fn
-	echo ".f1:${x[0]}.${x[1]}.${x[2]}.${x[3]}:"
-	fn
-	echo ".f2:${x[0]}.${x[1]}.${x[2]}.${x[3]}:"
-	echo ".rf0:${y[0]}.${y[1]}.${y[2]}.${y[3]}:"
-	rfn
-	echo ".rf1:${y[0]}.${y[1]}.${y[2]}.${y[3]}:"
-	rfn
-	echo ".rf2:${y[0]}.${y[1]}.${y[2]}.${y[3]}:"
-expected-stdout:
-	.f0:m.m..:
-	.fn:m.m.f.:
-	.f1:m.m.f.:
-	.fn:m.m.f.f:
-	.f2:m.m.f.f:
-	.rf0:m.m..:
-	.rfn:f...:
-	.rf1:f...:
-	.rfn:f...:
-	.rf2:f...:
----
-name: arrassign-fnc-local
-description:
-	Check locality of array access inside a function
-	with the bash/mksh/ksh93 local/typeset keyword
-	(note: ksh93 has no local; typeset works only in FKSH)
-stdin:
-	function fn {
-		typeset x
-		x+=(f)
-		echo ".fn:${x[0]}.${x[1]}.${x[2]}.${x[3]}:"
-	}
-	function rfn {
-		if [[ -n $BASH_VERSION ]]; then
-			y=()
-		else
-			set -A y
-		fi
-		typeset y
-		y+=(f)
-		echo ".rfn:${y[0]}.${y[1]}.${y[2]}.${y[3]}:"
-	}
-	function fnr {
-		typeset z
-		if [[ -n $BASH_VERSION ]]; then
-			z=()
-		else
-			set -A z
-		fi
-		z+=(f)
-		echo ".fnr:${z[0]}.${z[1]}.${z[2]}.${z[3]}:"
-	}
-	x=(m m)
-	y=(m m)
-	z=(m m)
-	echo ".f0:${x[0]}.${x[1]}.${x[2]}.${x[3]}:"
-	fn
-	echo ".f1:${x[0]}.${x[1]}.${x[2]}.${x[3]}:"
-	fn
-	echo ".f2:${x[0]}.${x[1]}.${x[2]}.${x[3]}:"
-	echo ".rf0:${y[0]}.${y[1]}.${y[2]}.${y[3]}:"
-	rfn
-	echo ".rf1:${y[0]}.${y[1]}.${y[2]}.${y[3]}:"
-	rfn
-	echo ".rf2:${y[0]}.${y[1]}.${y[2]}.${y[3]}:"
-	echo ".f0r:${z[0]}.${z[1]}.${z[2]}.${z[3]}:"
-	fnr
-	echo ".f1r:${z[0]}.${z[1]}.${z[2]}.${z[3]}:"
-	fnr
-	echo ".f2r:${z[0]}.${z[1]}.${z[2]}.${z[3]}:"
-expected-stdout:
-	.f0:m.m..:
-	.fn:f...:
-	.f1:m.m..:
-	.fn:f...:
-	.f2:m.m..:
-	.rf0:m.m..:
-	.rfn:f...:
-	.rf1:...:
-	.rfn:f...:
-	.rf2:...:
-	.f0r:m.m..:
-	.fnr:f...:
-	.f1r:m.m..:
-	.fnr:f...:
-	.f2r:m.m..:
----
-name: arrassign-fnc-global
-description:
-	Check locality of array access inside a function
-	with the mksh-specific global keyword
-stdin:
-	function fn {
-		global x
-		x+=(f)
-		echo ".fn:${x[0]}.${x[1]}.${x[2]}.${x[3]}:"
-	}
-	function rfn {
-		set -A y
-		global y
-		y+=(f)
-		echo ".rfn:${y[0]}.${y[1]}.${y[2]}.${y[3]}:"
-	}
-	function fnr {
-		global z
-		set -A z
-		z+=(f)
-		echo ".fnr:${z[0]}.${z[1]}.${z[2]}.${z[3]}:"
-	}
-	x=(m m)
-	y=(m m)
-	z=(m m)
-	echo ".f0:${x[0]}.${x[1]}.${x[2]}.${x[3]}:"
-	fn
-	echo ".f1:${x[0]}.${x[1]}.${x[2]}.${x[3]}:"
-	fn
-	echo ".f2:${x[0]}.${x[1]}.${x[2]}.${x[3]}:"
-	echo ".rf0:${y[0]}.${y[1]}.${y[2]}.${y[3]}:"
-	rfn
-	echo ".rf1:${y[0]}.${y[1]}.${y[2]}.${y[3]}:"
-	rfn
-	echo ".rf2:${y[0]}.${y[1]}.${y[2]}.${y[3]}:"
-	echo ".f0r:${z[0]}.${z[1]}.${z[2]}.${z[3]}:"
-	fnr
-	echo ".f1r:${z[0]}.${z[1]}.${z[2]}.${z[3]}:"
-	fnr
-	echo ".f2r:${z[0]}.${z[1]}.${z[2]}.${z[3]}:"
-expected-stdout:
-	.f0:m.m..:
-	.fn:m.m.f.:
-	.f1:m.m.f.:
-	.fn:m.m.f.f:
-	.f2:m.m.f.f:
-	.rf0:m.m..:
-	.rfn:f...:
-	.rf1:f...:
-	.rfn:f...:
-	.rf2:f...:
-	.f0r:m.m..:
-	.fnr:f...:
-	.f1r:f...:
-	.fnr:f...:
-	.f2r:f...:
----
-name: strassign-fnc-none
-description:
-	Check locality of string access inside a function
-stdin:
-	function fn {
-		x+=f
-		echo ".fn:$x:"
-	}
-	function rfn {
-		y=
-		y+=f
-		echo ".rfn:$y:"
-	}
-	x=m
-	y=m
-	echo ".f0:$x:"
-	fn
-	echo ".f1:$x:"
-	fn
-	echo ".f2:$x:"
-	echo ".rf0:$y:"
-	rfn
-	echo ".rf1:$y:"
-	rfn
-	echo ".rf2:$y:"
-expected-stdout:
-	.f0:m:
-	.fn:mf:
-	.f1:mf:
-	.fn:mff:
-	.f2:mff:
-	.rf0:m:
-	.rfn:f:
-	.rf1:f:
-	.rfn:f:
-	.rf2:f:
----
-name: strassign-fnc-local
-description:
-	Check locality of string access inside a function
-	with the bash/mksh/ksh93 local/typeset keyword
-	(note: ksh93 has no local; typeset works only in FKSH)
-stdin:
-	function fn {
-		typeset x
-		x+=f
-		echo ".fn:$x:"
-	}
-	function rfn {
-		y=
-		typeset y
-		y+=f
-		echo ".rfn:$y:"
-	}
-	function fnr {
-		typeset z
-		z=
-		z+=f
-		echo ".fnr:$z:"
-	}
-	x=m
-	y=m
-	z=m
-	echo ".f0:$x:"
-	fn
-	echo ".f1:$x:"
-	fn
-	echo ".f2:$x:"
-	echo ".rf0:$y:"
-	rfn
-	echo ".rf1:$y:"
-	rfn
-	echo ".rf2:$y:"
-	echo ".f0r:$z:"
-	fnr
-	echo ".f1r:$z:"
-	fnr
-	echo ".f2r:$z:"
-expected-stdout:
-	.f0:m:
-	.fn:f:
-	.f1:m:
-	.fn:f:
-	.f2:m:
-	.rf0:m:
-	.rfn:f:
-	.rf1::
-	.rfn:f:
-	.rf2::
-	.f0r:m:
-	.fnr:f:
-	.f1r:m:
-	.fnr:f:
-	.f2r:m:
----
-name: strassign-fnc-global
-description:
-	Check locality of string access inside a function
-	with the mksh-specific global keyword
-stdin:
-	function fn {
-		global x
-		x+=f
-		echo ".fn:$x:"
-	}
-	function rfn {
-		y=
-		global y
-		y+=f
-		echo ".rfn:$y:"
-	}
-	function fnr {
-		global z
-		z=
-		z+=f
-		echo ".fnr:$z:"
-	}
-	x=m
-	y=m
-	z=m
-	echo ".f0:$x:"
-	fn
-	echo ".f1:$x:"
-	fn
-	echo ".f2:$x:"
-	echo ".rf0:$y:"
-	rfn
-	echo ".rf1:$y:"
-	rfn
-	echo ".rf2:$y:"
-	echo ".f0r:$z:"
-	fnr
-	echo ".f1r:$z:"
-	fnr
-	echo ".f2r:$z:"
-expected-stdout:
-	.f0:m:
-	.fn:mf:
-	.f1:mf:
-	.fn:mff:
-	.f2:mff:
-	.rf0:m:
-	.rfn:f:
-	.rf1:f:
-	.rfn:f:
-	.rf2:f:
-	.f0r:m:
-	.fnr:f:
-	.f1r:f:
-	.fnr:f:
-	.f2r:f:
----
-name: varexpand-substr-1
-description:
-	Check if bash-style substring expansion works
-	when using positive numerics
-stdin:
-	x=abcdefghi
-	typeset -i y=123456789
-	typeset -i 16 z=123456789	# 16#75bcd15
-	echo a t${x:2:2} ${y:2:3} ${z:2:3} a
-	echo b ${x::3} ${y::3} ${z::3} b
-	echo c ${x:2:} ${y:2:} ${z:2:} c
-	echo d ${x:2} ${y:2} ${z:2} d
-	echo e ${x:2:6} ${y:2:6} ${z:2:7} e
-	echo f ${x:2:7} ${y:2:7} ${z:2:8} f
-	echo g ${x:2:8} ${y:2:8} ${z:2:9} g
-expected-stdout:
-	a tcd 345 #75 a
-	b abc 123 16# b
-	c c
-	d cdefghi 3456789 #75bcd15 d
-	e cdefgh 345678 #75bcd1 e
-	f cdefghi 3456789 #75bcd15 f
-	g cdefghi 3456789 #75bcd15 g
----
-name: varexpand-substr-2
-description:
-	Check if bash-style substring expansion works
-	when using negative numerics or expressions
-stdin:
-	x=abcdefghi
-	typeset -i y=123456789
-	typeset -i 16 z=123456789	# 16#75bcd15
-	n=2
-	echo a ${x:$n:3} ${y:$n:3} ${z:$n:3} a
-	echo b ${x:(n):3} ${y:(n):3} ${z:(n):3} b
-	echo c ${x:(-2):1} ${y:(-2):1} ${z:(-2):1} c
-	echo d t${x: n:2} ${y: n:3} ${z: n:3} d
-expected-stdout:
-	a cde 345 #75 a
-	b cde 345 #75 b
-	c h 8 1 c
-	d tcd 345 #75 d
----
-name: varexpand-substr-3
-description:
-	Check that some things that work in bash fail.
-	This is by design. And that some things fail in both.
-stdin:
-	export x=abcdefghi n=2
-	"$__progname" -c 'echo v${x:(n)}x'
-	"$__progname" -c 'echo w${x: n}x'
-	"$__progname" -c 'echo x${x:n}x'
-	"$__progname" -c 'echo y${x:}x'
-	"$__progname" -c 'echo z${x}x'
-	"$__progname" -c 'x=abcdef;y=123;echo ${x:${y:2:1}:2}' >/dev/null 2>&1; echo $?
-expected-stdout:
-	vcdefghix
-	wcdefghix
-	zabcdefghix
-	1
-expected-stderr-pattern:
-	/x:n.*bad substitution.*\n.*bad substitution/
----
-name: varexpand-substr-4
-description:
-	Check corner cases for substring expansion
-stdin:
-	x=abcdefghi
-	integer y=2
-	echo a ${x:(y == 1 ? 2 : 3):4} a
-expected-stdout:
-	a defg a
----
-name: varexpand-substr-5A
-description:
-	Check that substring expansions work on characters
-stdin:
-	set +U
-	x=mäh
-	echo a ${x::1} ${x: -1} a
-	echo b ${x::3} ${x: -3} b
-	echo c ${x:1:2} ${x: -3:2} c
-	echo d ${#x} d
-expected-stdout:
-	a m h a
-	b mä äh b
-	c ä ä c
-	d 4 d
----
-name: varexpand-substr-5W
-description:
-	Check that substring expansions work on characters
-stdin:
-	set -U
-	x=mäh
-	echo a ${x::1} ${x: -1} a
-	echo b ${x::2} ${x: -2} b
-	echo c ${x:1:1} ${x: -2:1} c
-	echo d ${#x} d
-expected-stdout:
-	a m h a
-	b mä äh b
-	c ä ä c
-	d 3 d
----
-name: varexpand-substr-6
-description:
-	Check that string substitution works correctly
-stdin:
-	foo=1
-	bar=2
-	baz=qwertyuiop
-	echo a ${baz: foo: bar}
-	echo b ${baz: foo: $bar}
-	echo c ${baz: $foo: bar}
-	echo d ${baz: $foo: $bar}
-expected-stdout:
-	a we
-	b we
-	c we
-	d we
----
-name: varexpand-special-hash
-description:
-	Check special ${var at x} expansion for x=hash
-stdin:
-	typeset -i8 foo=10
-	bar=baz
-	unset baz
-	bla=foo
-	print ${foo@#} ${bar@#} ${baz@#} .
-	print ${foo@#123} ${bar@#456} ${baz@#789} .
-	print ${foo@#bla} ${bar@#bar} ${baz@#OPTIND} .
-expected-stdout:
-	D50219A0 20E5DB5B 00000000 .
-	554A1C76 004A212E CB209562 .
-	6B21CF91 20E5DB5B 124EA49D .
----
-name: varexpand-special-quote
-description:
-	Check special ${var at Q} expansion for quoted strings
-stdin:
-	set +U
-	i=x
-	j=a\ b
-	k=$'c
-	d\xA0''e€f'
-	print -r -- "<i=$i j=$j k=$k>"
-	s="u=${i at Q} v=${j at Q} w=${k at Q}"
-	print -r -- "s=\"$s\""
-	eval "$s"
-	typeset -p u v w
-expected-stdout:
-	<i=x j=a b k=c
-	d\xA0e€f>
-	s="u=x v='a b' w=$'c\nd\240e\u20ACf'"
-	typeset u=x
-	typeset v='a b'
-	typeset w=$'c\nd\240e\u20ACf'
----
-name: varexpand-null-1
-description:
-	Ensure empty strings expand emptily
-stdin:
-	print x ${a} ${b} y
-	print z ${a#?} ${b%?} w
-	print v ${a=} ${b/c/d} u
-expected-stdout:
-	x y
-	z w
-	v u
----
-name: varexpand-null-2
-description:
-	Ensure empty strings, when quoted, are expanded as empty strings
-stdin:
-	print '#!'"$__progname"'\nfor x in "$@"; do print -nr -- "<$x> "; done' >pfs
-	chmod +x pfs
-	./pfs 1 "${a}" 2 "${a#?}" + "${b%?}" 3 "${a=}" + "${b/c/d}"
-	echo .
-expected-stdout:
-	<1> <> <2> <> <+> <> <3> <> <+> <> .
----
-name: print-funny-chars
-description:
-	Check print builtin's capability to output designated characters
-stdin:
-	print '<\0144\0344\xDB\u00DB\u20AC\uDB\x40>'
-expected-stdout:
-	<d\xE4\xDBÛ€Û@>
----
-name: print-bksl-c
-description:
-	Check print builtin's \c escape
-stdin:
-	print '\ca'; print b
-expected-stdout:
-	ab
----
-name: print-cr
-description:
-	Check that CR+LF is not collapsed into LF as some MSYS shells wrongly do
-stdin:
-	echo '#!'"$__progname" >foo
-	cat >>foo <<-'EOF'
-		print -n -- '220-blau.mirbsd.org ESMTP ready at Thu, 25 Jul 2013 15:57:57 GMT\r\n220->> Bitte keine Werbung einwerfen! <<\r\r\n220 Who do you wanna pretend to be today'
-		print \?
-	EOF
-	chmod +x foo
-	echo "[$(./foo)]"
-	./foo | while IFS= read -r line; do
-		print -r -- "{$line}"
-	done
-expected-stdout:
-	[220-blau.mirbsd.org ESMTP ready at Thu, 25 Jul 2013 15:57:57 GMT
-	220->> Bitte keine Werbung einwerfen! <<
-
-	220 Who do you wanna pretend to be today?
-]
-	{220-blau.mirbsd.org ESMTP ready at Thu, 25 Jul 2013 15:57:57 GMT
-}
-	{220->> Bitte keine Werbung einwerfen! <<
-
-}
-	{220 Who do you wanna pretend to be today?
-}
----
-name: print-nul-chars
-description:
-	Check handling of NUL characters for print and COMSUB
-stdin:
-	x=$(print '<\0>')
-	print $(($(print '<\0>' | wc -c))) $(($(print "$x" | wc -c))) \
-	    ${#x} "$x" '<\0>'
-expected-stdout-pattern:
-	/^4 3 2 <> <\0>$/
----
-name: print-escapes
-description:
-	Check backslash expansion by the print builtin
-stdin:
-	print '\ \!\"\#\$\%\&'\\\''\(\)\*\+\,\-\.\/\0\1\2\3\4\5\6\7\8' \
-	    '\9\:\;\<\=\>\?\@\A\B\C\D\E\F\G\H\I\J\K\L\M\N\O\P\Q\R\S\T' \
-	    '\U\V\W\X\Y\Z\[\\\]\^\_\`\a\b  \d\e\f\g\h\i\j\k\l\m\n\o\p' \
-	    '\q\r\s\t\u\v\w\x\y\z\{\|\}\~' '\u20acd' '\U20acd' '\x123' \
-	    '\0x' '\0123' '\01234' | {
-		# integer-base-one-3As
-		typeset -Uui16 -Z11 pos=0
-		typeset -Uui16 -Z5 hv=2147483647
-		typeset -i1 wc=0x0A
-		dasc=
-		nl=${wc#1#}
-		while IFS= read -r line; do
-			line=$line$nl
-			while [[ -n $line ]]; do
-				hv=1#${line::1}
-				if (( (pos & 15) == 0 )); then
-					(( pos )) && print "$dasc|"
-					print -n "${pos#16#}  "
-					dasc=' |'
-				fi
-				print -n "${hv#16#} "
-				if (( (hv < 32) || (hv > 126) )); then
-					dasc=$dasc.
-				else
-					dasc=$dasc${line::1}
-				fi
-				(( (pos++ & 15) == 7 )) && print -n -- '- '
-				line=${line:1}
-			done
-		done
-		while (( pos & 15 )); do
-			print -n '   '
-			(( (pos++ & 15) == 7 )) && print -n -- '- '
-		done
-		(( hv == 2147483647 )) || print "$dasc|"
-	}
-expected-stdout:
-	00000000  5C 20 5C 21 5C 22 5C 23 - 5C 24 5C 25 5C 26 5C 27  |\ \!\"\#\$\%\&\'|
-	00000010  5C 28 5C 29 5C 2A 5C 2B - 5C 2C 5C 2D 5C 2E 5C 2F  |\(\)\*\+\,\-\.\/|
-	00000020  5C 31 5C 32 5C 33 5C 34 - 5C 35 5C 36 5C 37 5C 38  |\1\2\3\4\5\6\7\8|
-	00000030  20 5C 39 5C 3A 5C 3B 5C - 3C 5C 3D 5C 3E 5C 3F 5C  | \9\:\;\<\=\>\?\|
-	00000040  40 5C 41 5C 42 5C 43 5C - 44 1B 5C 46 5C 47 5C 48  |@\A\B\C\D.\F\G\H|
-	00000050  5C 49 5C 4A 5C 4B 5C 4C - 5C 4D 5C 4E 5C 4F 5C 50  |\I\J\K\L\M\N\O\P|
-	00000060  5C 51 5C 52 5C 53 5C 54 - 20 5C 56 5C 57 5C 58 5C  |\Q\R\S\T \V\W\X\|
-	00000070  59 5C 5A 5C 5B 5C 5C 5D - 5C 5E 5C 5F 5C 60 07 08  |Y\Z\[\]\^\_\`..|
-	00000080  20 20 5C 64 1B 0C 5C 67 - 5C 68 5C 69 5C 6A 5C 6B  |  \d..\g\h\i\j\k|
-	00000090  5C 6C 5C 6D 0A 5C 6F 5C - 70 20 5C 71 0D 5C 73 09  |\l\m.\o\p \q.\s.|
-	000000A0  0B 5C 77 5C 79 5C 7A 5C - 7B 5C 7C 5C 7D 5C 7E 20  |.\w\y\z\{\|\}\~ |
-	000000B0  E2 82 AC 64 20 EF BF BD - 20 12 33 20 78 20 53 20  |...d ... .3 x S |
-	000000C0  53 34 0A                -                          |S4.|
----
-name: dollar-doublequoted-strings
-description:
-	Check that a $ preceding "…" is ignored
-stdin:
-	echo $"Localise me!"
-	cat <<<$"Me too!"
-	V=X
-	aol=aol
-	cat <<-$"aol"
-		I do not take a $V for a V!
-	aol
-expected-stdout:
-	Localise me!
-	Me too!
-	I do not take a $V for a V!
----
-name: dollar-quoted-strings
-description:
-	Check backslash expansion by $'…' strings
-stdin:
-	print '#!'"$__progname"'\nfor x in "$@"; do print -r -- "$x"; done' >pfn
-	chmod +x pfn
-	./pfn $'\ \!\"\#\$\%\&\'\(\)\*\+\,\-\.\/ \1\2\3\4\5\6' \
-	    $'a\0b' $'a\01b' $'\7\8\9\:\;\<\=\>\?\@\A\B\C\D\E\F\G\H\I' \
-	    $'\J\K\L\M\N\O\P\Q\R\S\T\U1\V\W\X\Y\Z\[\\\]\^\_\`\a\b\d\e' \
-	    $'\f\g\h\i\j\k\l\m\n\o\p\q\r\s\t\u1\v\w\x1\y\z\{\|\}\~ $x' \
-	    $'\u20acd' $'\U20acd' $'\x123' $'fn\x0rd' $'\0234' $'\234' \
-	    $'\2345' $'\ca' $'\c!' $'\c?' $'\c€' $'a\
-	b' | {
-		# integer-base-one-3As
-		typeset -Uui16 -Z11 pos=0
-		typeset -Uui16 -Z5 hv=2147483647
-		typeset -i1 wc=0x0A
-		dasc=
-		nl=${wc#1#}
-		while IFS= read -r line; do
-			line=$line$nl
-			while [[ -n $line ]]; do
-				hv=1#${line::1}
-				if (( (pos & 15) == 0 )); then
-					(( pos )) && print "$dasc|"
-					print -n "${pos#16#}  "
-					dasc=' |'
-				fi
-				print -n "${hv#16#} "
-				if (( (hv < 32) || (hv > 126) )); then
-					dasc=$dasc.
-				else
-					dasc=$dasc${line::1}
-				fi
-				(( (pos++ & 15) == 7 )) && print -n -- '- '
-				line=${line:1}
-			done
-		done
-		while (( pos & 15 )); do
-			print -n '   '
-			(( (pos++ & 15) == 7 )) && print -n -- '- '
-		done
-		(( hv == 2147483647 )) || print "$dasc|"
-	}
-expected-stdout:
-	00000000  20 21 22 23 24 25 26 27 - 28 29 2A 2B 2C 2D 2E 2F  | !"#$%&'()*+,-./|
-	00000010  20 01 02 03 04 05 06 0A - 61 0A 61 01 62 0A 07 38  | .......a.a.b..8|
-	00000020  39 3A 3B 3C 3D 3E 3F 40 - 41 42 43 44 1B 46 47 48  |9:;<=>?@ABCD.FGH|
-	00000030  49 0A 4A 4B 4C 4D 4E 4F - 50 51 52 53 54 01 56 57  |I.JKLMNOPQRST.VW|
-	00000040  58 59 5A 5B 5C 5D 5E 5F - 60 07 08 64 1B 0A 0C 67  |XYZ[\]^_`..d...g|
-	00000050  68 69 6A 6B 6C 6D 0A 6F - 70 71 0D 73 09 01 0B 77  |hijklm.opq.s...w|
-	00000060  01 79 7A 7B 7C 7D 7E 20 - 24 78 0A E2 82 AC 64 0A  |.yz{|}~ $x....d.|
-	00000070  EF BF BD 0A C4 A3 0A 66 - 6E 0A 13 34 0A 9C 0A 9C  |.......fn..4....|
-	00000080  35 0A 01 0A 01 0A 7F 0A - 02 82 AC 0A 61 0A 62 0A  |5...........a.b.|
----
-name: dollar-quotes-in-heredocs-strings
-description:
-	They are, however, not parsed in here documents, here strings
-	(outside of string delimiters) or regular strings, but in
-	parameter substitutions.
-stdin:
-	cat <<EOF
-		dollar = strchr(s, '$');	/* ' */
-		foo " bar \" baz
-	EOF
-	cat <<$'a\tb'
-	a\tb
-	a	b
-	cat <<<"dollar = strchr(s, '$');	/* ' */"
-	cat <<<'dollar = strchr(s, '\''$'\'');	/* '\'' */'
-	x="dollar = strchr(s, '$');	/* ' */"
-	cat <<<"$x"
-	cat <<<$'a\E[0m\tb'
-	unset nl; print -r -- "x${nl:=$'\n'}y"
-	echo "1 foo\"bar"
-	# cf & HEREDOC
-	cat <<EOF
-	2 foo\"bar
-	EOF
-	# probably never reached for here strings?
-	cat <<<"3 foo\"bar"
-	cat <<<"4 foo\\\"bar"
-	cat <<<'5 foo\"bar'
-	# old scripts use this (e.g. ncurses)
-	echo "^$"
-	# make sure this works, outside of quotes
-	cat <<<'7'$'\t''.'
-expected-stdout:
-		dollar = strchr(s, '$');	/* ' */
-		foo " bar \" baz
-	a\tb
-	dollar = strchr(s, '$');	/* ' */
-	dollar = strchr(s, '$');	/* ' */
-	dollar = strchr(s, '$');	/* ' */
-	a	b
-	x
-	y
-	1 foo"bar
-	2 foo\"bar
-	3 foo"bar
-	4 foo\"bar
-	5 foo\"bar
-	^$
-	7	.
----
-name: dot-needs-argument
-description:
-	check Debian #415167 solution: '.' without arguments should fail
-stdin:
-	"$__progname" -c .
-	"$__progname" -c source
-expected-exit: e != 0
-expected-stderr-pattern:
-	/\.: missing argument.*\n.*\.: missing argument/
----
-name: alias-function-no-conflict
-description:
-	make aliases not conflict with functions
-	note: for ksh-like functions, the order of preference is
-	different; bash outputs baz instead of bar in line 2 below
-stdin:
-	alias foo='echo bar'
-	foo() {
-		echo baz
-	}
-	alias korn='echo bar'
-	function korn {
-		echo baz
-	}
-	foo
-	korn
-	unset -f foo
-	foo 2>/dev/null || echo rab
-expected-stdout:
-	baz
-	bar
-	rab
----
-name: bash-function-parens
-description:
-	ensure the keyword function is ignored when preceding
-	POSIX style function declarations (bashism)
-stdin:
-	mk() {
-		echo '#!'"$__progname"
-		echo "$1 {"
-		echo '	echo "bar='\''$0'\'\"
-		echo '}'
-		echo ${2:-foo}
-	}
-	mk 'function foo' >f-korn
-	mk 'foo ()' >f-dash
-	mk 'function foo ()' >f-bash
-	mk 'function stop ()' stop >f-stop
-	print '#!'"$__progname"'\nprint -r -- "${0%/f-argh}"' >f-argh
-	chmod +x f-*
-	u=$(./f-argh)
-	x="korn: $(./f-korn)"; echo "${x/@("$u")/.}"
-	x="dash: $(./f-dash)"; echo "${x/@("$u")/.}"
-	x="bash: $(./f-bash)"; echo "${x/@("$u")/.}"
-	x="stop: $(./f-stop)"; echo "${x/@("$u")/.}"
-expected-stdout:
-	korn: bar='foo'
-	dash: bar='./f-dash'
-	bash: bar='./f-bash'
-	stop: bar='./f-stop'
----
-name: integer-base-one-1
-description:
-	check if the use of fake integer base 1 works
-stdin:
-	set -U
-	typeset -Uui16 i0=1#\xEF i1=1#€
-	typeset -i1 o0a=64
-	typeset -i1 o1a=0x263A
-	typeset -Uui1 o0b=0x7E
-	typeset -Uui1 o1b=0xFDD0
-	integer px=0xCAFE 'p0=1# ' p1=1#… pl=1#f
-	echo "in <$i0> <$i1>"
-	echo "out <${o0a#1#}|${o0b#1#}> <${o1a#1#}|${o1b#1#}>"
-	typeset -Uui1 i0 i1
-	echo "pass <$px> <$p0> <$p1> <$pl> <${i0#1#}|${i1#1#}>"
-	typeset -Uui16 tv1=1#~ tv2=1# tv3=1#\x80 tv4=1#\x81 tv5=1#\xC0 tv6=1#\xC1 tv7=1#  tv8=1#€
-	echo "specX <${tv1#16#}> <${tv2#16#}> <${tv3#16#}> <${tv4#16#}> <${tv5#16#}> <${tv6#16#}> <${tv7#16#}> <${tv8#16#}>"
-	typeset -i1 tv1 tv2 tv3 tv4 tv5 tv6 tv7 tv8
-	echo "specW <${tv1#1#}> <${tv2#1#}> <${tv3#1#}> <${tv4#1#}> <${tv5#1#}> <${tv6#1#}> <${tv7#1#}> <${tv8#1#}>"
-	typeset -i1 xs1=0xEF7F xs2=0xEF80 xs3=0xFDD0
-	echo "specU <${xs1#1#}> <${xs2#1#}> <${xs3#1#}>"
-expected-stdout:
-	in <16#EFEF> <16#20AC>
-	out <@|~> <☺|\x{FDD0}>
-	pass <16#cafe> <1# > <1#…> <1#f> <\xEF|€>
-	specX <7E> <7F> <EF80> <EF81> <EFC0> <EFC1> <A0> <80>
-	specW <~> <> <\x80> <\x81> <\xC0> <\xC1> < > <€>
-	specU <> <\x80> <\x{FDD0}>
----
-name: integer-base-one-2a
-description:
-	check if the use of fake integer base 1 stops at correct characters
-stdin:
-	set -U
-	integer x=1#foo
-	echo /$x/
-expected-stderr-pattern:
-	/1#foo: unexpected 'oo'/
-expected-exit: e != 0
----
-name: integer-base-one-2b
-description:
-	check if the use of fake integer base 1 stops at correct characters
-stdin:
-	set -U
-	integer x=1#\xC0\x80
-	echo /$x/
-expected-stderr-pattern:
-	/1#\xC0\x80: unexpected '\x80'/
-expected-exit: e != 0
----
-name: integer-base-one-2c1
-description:
-	check if the use of fake integer base 1 stops at correct characters
-stdin:
-	set -U
-	integer x=1#…
-	echo /$x/
-expected-stdout:
-	/1#…/
----
-name: integer-base-one-2c2
-description:
-	check if the use of fake integer base 1 stops at correct characters
-stdin:
-	set +U
-	integer x=1#…
-	echo /$x/
-expected-stderr-pattern:
-	/1#…: unexpected '\x80'/
-expected-exit: e != 0
----
-name: integer-base-one-2d1
-description:
-	check if the use of fake integer base 1 handles octets okay
-stdin:
-	set -U
-	typeset -i16 x=1#\xFF
-	echo /$x/	# invalid utf-8
-expected-stdout:
-	/16#efff/
----
-name: integer-base-one-2d2
-description:
-	check if the use of fake integer base 1 handles octets
-stdin:
-	set -U
-	typeset -i16 x=1#\xC2
-	echo /$x/	# invalid 2-byte
-expected-stdout:
-	/16#efc2/
----
-name: integer-base-one-2d3
-description:
-	check if the use of fake integer base 1 handles octets
-stdin:
-	set -U
-	typeset -i16 x=1#\xEF
-	echo /$x/	# invalid 2-byte
-expected-stdout:
-	/16#efef/
----
-name: integer-base-one-2d4
-description:
-	check if the use of fake integer base 1 stops at invalid input
-stdin:
-	set -U
-	typeset -i16 x=1#\xEF\xBF\xC0
-	echo /$x/	# invalid 3-byte
-expected-stderr-pattern:
-	/1#\xEF\xBF\xC0: unexpected '\xBF'/
-expected-exit: e != 0
----
-name: integer-base-one-2d5
-description:
-	check if the use of fake integer base 1 stops at invalid input
-stdin:
-	set -U
-	typeset -i16 x=1#\xC0\x80
-	echo /$x/	# non-minimalistic
-expected-stderr-pattern:
-	/1#\xC0\x80: unexpected '\x80'/
-expected-exit: e != 0
----
-name: integer-base-one-2d6
-description:
-	check if the use of fake integer base 1 stops at invalid input
-stdin:
-	set -U
-	typeset -i16 x=1#\xE0\x80\x80
-	echo /$x/	# non-minimalistic
-expected-stderr-pattern:
-	/1#\xE0\x80\x80: unexpected '\x80'/
-expected-exit: e != 0
----
-name: integer-base-one-3As
-description:
-	some sample code for hexdumping
-	not NUL safe; input lines must be NL terminated
-stdin:
-	{
-		print 'Hello, World!\\\nこんにちは!'
-		typeset -Uui16 i=0x100
-		# change that to 0xFF once we can handle embedded
-		# NUL characters in strings / here documents
-		while (( i++ < 0x1FF )); do
-			print -n "\x${i#16#1}"
-		done
-		print '\0z'
-	} | {
-		# integer-base-one-3As
-		typeset -Uui16 -Z11 pos=0
-		typeset -Uui16 -Z5 hv=2147483647
-		typeset -i1 wc=0x0A
-		dasc=
-		nl=${wc#1#}
-		while IFS= read -r line; do
-			line=$line$nl
-			while [[ -n $line ]]; do
-				hv=1#${line::1}
-				if (( (pos & 15) == 0 )); then
-					(( pos )) && print "$dasc|"
-					print -n "${pos#16#}  "
-					dasc=' |'
-				fi
-				print -n "${hv#16#} "
-				if (( (hv < 32) || (hv > 126) )); then
-					dasc=$dasc.
-				else
-					dasc=$dasc${line::1}
-				fi
-				(( (pos++ & 15) == 7 )) && print -n -- '- '
-				line=${line:1}
-			done
-		done
-		while (( pos & 15 )); do
-			print -n '   '
-			(( (pos++ & 15) == 7 )) && print -n -- '- '
-		done
-		(( hv == 2147483647 )) || print "$dasc|"
-	}
-expected-stdout:
-	00000000  48 65 6C 6C 6F 2C 20 57 - 6F 72 6C 64 21 5C 0A E3  |Hello, World!\..|
-	00000010  81 93 E3 82 93 E3 81 AB - E3 81 A1 E3 81 AF EF BC  |................|
-	00000020  81 0A 01 02 03 04 05 06 - 07 08 09 0A 0B 0C 0D 0E  |................|
-	00000030  0F 10 11 12 13 14 15 16 - 17 18 19 1A 1B 1C 1D 1E  |................|
-	00000040  1F 20 21 22 23 24 25 26 - 27 28 29 2A 2B 2C 2D 2E  |. !"#$%&'()*+,-.|
-	00000050  2F 30 31 32 33 34 35 36 - 37 38 39 3A 3B 3C 3D 3E  |/0123456789:;<=>|
-	00000060  3F 40 41 42 43 44 45 46 - 47 48 49 4A 4B 4C 4D 4E  |?@ABCDEFGHIJKLMN|
-	00000070  4F 50 51 52 53 54 55 56 - 57 58 59 5A 5B 5C 5D 5E  |OPQRSTUVWXYZ[\]^|
-	00000080  5F 60 61 62 63 64 65 66 - 67 68 69 6A 6B 6C 6D 6E  |_`abcdefghijklmn|
-	00000090  6F 70 71 72 73 74 75 76 - 77 78 79 7A 7B 7C 7D 7E  |opqrstuvwxyz{|}~|
-	000000A0  7F 80 81 82 83 84 85 86 - 87 88 89 8A 8B 8C 8D 8E  |................|
-	000000B0  8F 90 91 92 93 94 95 96 - 97 98 99 9A 9B 9C 9D 9E  |................|
-	000000C0  9F A0 A1 A2 A3 A4 A5 A6 - A7 A8 A9 AA AB AC AD AE  |................|
-	000000D0  AF B0 B1 B2 B3 B4 B5 B6 - B7 B8 B9 BA BB BC BD BE  |................|
-	000000E0  BF C0 C1 C2 C3 C4 C5 C6 - C7 C8 C9 CA CB CC CD CE  |................|
-	000000F0  CF D0 D1 D2 D3 D4 D5 D6 - D7 D8 D9 DA DB DC DD DE  |................|
-	00000100  DF E0 E1 E2 E3 E4 E5 E6 - E7 E8 E9 EA EB EC ED EE  |................|
-	00000110  EF F0 F1 F2 F3 F4 F5 F6 - F7 F8 F9 FA FB FC FD FE  |................|
-	00000120  FF 7A 0A                -                          |.z.|
----
-name: integer-base-one-3Ws
-description:
-	some sample code for hexdumping Unicode
-	not NUL safe; input lines must be NL terminated
-stdin:
-	set -U
-	{
-		print 'Hello, World!\\\nこんにちは!'
-		typeset -Uui16 i=0x100
-		# change that to 0xFF once we can handle embedded
-		# NUL characters in strings / here documents
-		while (( i++ < 0x1FF )); do
-			print -n "\u${i#16#1}"
-		done
-		print
-		print \\xff		# invalid utf-8
-		print \\xc2		# invalid 2-byte
-		print \\xef\\xbf\\xc0	# invalid 3-byte
-		print \\xc0\\x80	# non-minimalistic
-		print \\xe0\\x80\\x80	# non-minimalistic
-		print '�\x{FFFE}\x{FFFF}'	# end of range
-		print '\0z'		# embedded NUL
-	} | {
-		# integer-base-one-3Ws
-		typeset -Uui16 -Z11 pos=0
-		typeset -Uui16 -Z7 hv
-		typeset -i1 wc=0x0A
-		typeset -i lpos
-		dasc=
-		nl=${wc#1#}
-		while IFS= read -r line; do
-			line=$line$nl
-			lpos=0
-			while (( lpos < ${#line} )); do
-				wc=1#${line:(lpos++):1}
-				if (( (wc < 32) || \
-				    ((wc > 126) && (wc < 160)) )); then
-					dch=.
-				elif (( (wc & 0xFF80) == 0xEF80 )); then
-					dch=�
-				else
-					dch=${wc#1#}
-				fi
-				if (( (pos & 7) == 7 )); then
-					dasc=$dasc$dch
-					dch=
-				elif (( (pos & 7) == 0 )); then
-					(( pos )) && print "$dasc|"
-					print -n "${pos#16#}  "
-					dasc=' |'
-				fi
-				let hv=wc
-				print -n "${hv#16#} "
-				(( (pos++ & 7) == 3 )) && \
-				    print -n -- '- '
-				dasc=$dasc$dch
-			done
-		done
-		while (( pos & 7 )); do
-			print -n '     '
-			(( (pos++ & 7) == 3 )) && print -n -- '- '
-		done
-		(( hv == 2147483647 )) || print "$dasc|"
-	}
-expected-stdout:
-	00000000  0048 0065 006C 006C - 006F 002C 0020 0057  |Hello, W|
-	00000008  006F 0072 006C 0064 - 0021 005C 000A 3053  |orld!\.こ|
-	00000010  3093 306B 3061 306F - FF01 000A 0001 0002  |んにちは!...|
-	00000018  0003 0004 0005 0006 - 0007 0008 0009 000A  |........|
-	00000020  000B 000C 000D 000E - 000F 0010 0011 0012  |........|
-	00000028  0013 0014 0015 0016 - 0017 0018 0019 001A  |........|
-	00000030  001B 001C 001D 001E - 001F 0020 0021 0022  |..... !"|
-	00000038  0023 0024 0025 0026 - 0027 0028 0029 002A  |#$%&'()*|
-	00000040  002B 002C 002D 002E - 002F 0030 0031 0032  |+,-./012|
-	00000048  0033 0034 0035 0036 - 0037 0038 0039 003A  |3456789:|
-	00000050  003B 003C 003D 003E - 003F 0040 0041 0042  |;<=>?@AB|
-	00000058  0043 0044 0045 0046 - 0047 0048 0049 004A  |CDEFGHIJ|
-	00000060  004B 004C 004D 004E - 004F 0050 0051 0052  |KLMNOPQR|
-	00000068  0053 0054 0055 0056 - 0057 0058 0059 005A  |STUVWXYZ|
-	00000070  005B 005C 005D 005E - 005F 0060 0061 0062  |[\]^_`ab|
-	00000078  0063 0064 0065 0066 - 0067 0068 0069 006A  |cdefghij|
-	00000080  006B 006C 006D 006E - 006F 0070 0071 0072  |klmnopqr|
-	00000088  0073 0074 0075 0076 - 0077 0078 0079 007A  |stuvwxyz|
-	00000090  007B 007C 007D 007E - 007F 0080 0081 0082  |{|}~....|
-	00000098  0083 0084 0085 0086 - 0087 0088 0089 008A  |........|
-	000000A0  008B 008C 008D 008E - 008F 0090 0091 0092  |........|
-	000000A8  0093 0094 0095 0096 - 0097 0098 0099 009A  |........|
-	000000B0  009B 009C 009D 009E - 009F 00A0 00A1 00A2  |..... ¡¢|
-	000000B8  00A3 00A4 00A5 00A6 - 00A7 00A8 00A9 00AA  |£¤¥¦§¨©ª|
-	000000C0  00AB 00AC 00AD 00AE - 00AF 00B0 00B1 00B2  |«¬­®¯°±²|
-	000000C8  00B3 00B4 00B5 00B6 - 00B7 00B8 00B9 00BA  |³´µ¶·¸¹º|
-	000000D0  00BB 00BC 00BD 00BE - 00BF 00C0 00C1 00C2  |»¼½¾¿ÀÁÂ|
-	000000D8  00C3 00C4 00C5 00C6 - 00C7 00C8 00C9 00CA  |ÃÄÅÆÇÈÉÊ|
-	000000E0  00CB 00CC 00CD 00CE - 00CF 00D0 00D1 00D2  |ËÌÍÎÏÐÑÒ|
-	000000E8  00D3 00D4 00D5 00D6 - 00D7 00D8 00D9 00DA  |ÓÔÕÖ×ØÙÚ|
-	000000F0  00DB 00DC 00DD 00DE - 00DF 00E0 00E1 00E2  |ÛÜÝÞßàáâ|
-	000000F8  00E3 00E4 00E5 00E6 - 00E7 00E8 00E9 00EA  |ãäåæçèéê|
-	00000100  00EB 00EC 00ED 00EE - 00EF 00F0 00F1 00F2  |ëìíîïðñò|
-	00000108  00F3 00F4 00F5 00F6 - 00F7 00F8 00F9 00FA  |óôõö÷øùú|
-	00000110  00FB 00FC 00FD 00FE - 00FF 000A EFFF 000A  |ûüýþÿ.�.|
-	00000118  EFC2 000A EFEF EFBF - EFC0 000A EFC0 EF80  |�.���.��|
-	00000120  000A EFE0 EF80 EF80 - 000A FFFD EFEF EFBF  |.���.���|
-	00000128  EFBE EFEF EFBF EFBF - 000A 007A 000A       |����.z.|
----
-name: integer-base-one-3Ar
-description:
-	some sample code for hexdumping; NUL and binary safe
-stdin:
-	{
-		print 'Hello, World!\\\nこんにちは!'
-		typeset -Uui16 i=0x100
-		# change that to 0xFF once we can handle embedded
-		# NUL characters in strings / here documents
-		while (( i++ < 0x1FF )); do
-			print -n "\x${i#16#1}"
-		done
-		print '\0z'
-	} | {
-		# integer-base-one-3Ar
-		typeset -Uui16 -Z11 pos=0
-		typeset -Uui16 -Z5 hv=2147483647
-		dasc=
-		if read -arN -1 line; then
-			typeset -i1 line
-			i=0
-			while (( i < ${#line[*]} )); do
-				hv=${line[i++]}
-				if (( (pos & 15) == 0 )); then
-					(( pos )) && print "$dasc|"
-					print -n "${pos#16#}  "
-					dasc=' |'
-				fi
-				print -n "${hv#16#} "
-				if (( (hv < 32) || (hv > 126) )); then
-					dasc=$dasc.
-				else
-					dasc=$dasc${line[i-1]#1#}
-				fi
-				(( (pos++ & 15) == 7 )) && print -n -- '- '
-			done
-		fi
-		while (( pos & 15 )); do
-			print -n '   '
-			(( (pos++ & 15) == 7 )) && print -n -- '- '
-		done
-		(( hv == 2147483647 )) || print "$dasc|"
-	}
-expected-stdout:
-	00000000  48 65 6C 6C 6F 2C 20 57 - 6F 72 6C 64 21 5C 0A E3  |Hello, World!\..|
-	00000010  81 93 E3 82 93 E3 81 AB - E3 81 A1 E3 81 AF EF BC  |................|
-	00000020  81 0A 01 02 03 04 05 06 - 07 08 09 0A 0B 0C 0D 0E  |................|
-	00000030  0F 10 11 12 13 14 15 16 - 17 18 19 1A 1B 1C 1D 1E  |................|
-	00000040  1F 20 21 22 23 24 25 26 - 27 28 29 2A 2B 2C 2D 2E  |. !"#$%&'()*+,-.|
-	00000050  2F 30 31 32 33 34 35 36 - 37 38 39 3A 3B 3C 3D 3E  |/0123456789:;<=>|
-	00000060  3F 40 41 42 43 44 45 46 - 47 48 49 4A 4B 4C 4D 4E  |?@ABCDEFGHIJKLMN|
-	00000070  4F 50 51 52 53 54 55 56 - 57 58 59 5A 5B 5C 5D 5E  |OPQRSTUVWXYZ[\]^|
-	00000080  5F 60 61 62 63 64 65 66 - 67 68 69 6A 6B 6C 6D 6E  |_`abcdefghijklmn|
-	00000090  6F 70 71 72 73 74 75 76 - 77 78 79 7A 7B 7C 7D 7E  |opqrstuvwxyz{|}~|
-	000000A0  7F 80 81 82 83 84 85 86 - 87 88 89 8A 8B 8C 8D 8E  |................|
-	000000B0  8F 90 91 92 93 94 95 96 - 97 98 99 9A 9B 9C 9D 9E  |................|
-	000000C0  9F A0 A1 A2 A3 A4 A5 A6 - A7 A8 A9 AA AB AC AD AE  |................|
-	000000D0  AF B0 B1 B2 B3 B4 B5 B6 - B7 B8 B9 BA BB BC BD BE  |................|
-	000000E0  BF C0 C1 C2 C3 C4 C5 C6 - C7 C8 C9 CA CB CC CD CE  |................|
-	000000F0  CF D0 D1 D2 D3 D4 D5 D6 - D7 D8 D9 DA DB DC DD DE  |................|
-	00000100  DF E0 E1 E2 E3 E4 E5 E6 - E7 E8 E9 EA EB EC ED EE  |................|
-	00000110  EF F0 F1 F2 F3 F4 F5 F6 - F7 F8 F9 FA FB FC FD FE  |................|
-	00000120  FF 00 7A 0A             -                          |..z.|
----
-name: integer-base-one-3Wr
-description:
-	some sample code for hexdumping Unicode; NUL and binary safe
-stdin:
-	set -U
-	{
-		print 'Hello, World!\\\nこんにちは!'
-		typeset -Uui16 i=0x100
-		# change that to 0xFF once we can handle embedded
-		# NUL characters in strings / here documents
-		while (( i++ < 0x1FF )); do
-			print -n "\u${i#16#1}"
-		done
-		print
-		print \\xff		# invalid utf-8
-		print \\xc2		# invalid 2-byte
-		print \\xef\\xbf\\xc0	# invalid 3-byte
-		print \\xc0\\x80	# non-minimalistic
-		print \\xe0\\x80\\x80	# non-minimalistic
-		print '�\x{FFFE}\x{FFFF}'	# end of range
-		print '\0z'		# embedded NUL
-	} | {
-		# integer-base-one-3Wr
-		typeset -Uui16 -Z11 pos=0
-		typeset -Uui16 -Z7 hv=2147483647
-		dasc=
-		if read -arN -1 line; then
-			typeset -i1 line
-			i=0
-			while (( i < ${#line[*]} )); do
-				hv=${line[i++]}
-				if (( (hv < 32) || \
-				    ((hv > 126) && (hv < 160)) )); then
-					dch=.
-				elif (( (hv & 0xFF80) == 0xEF80 )); then
-					dch=�
-				else
-					dch=${line[i-1]#1#}
-				fi
-				if (( (pos & 7) == 7 )); then
-					dasc=$dasc$dch
-					dch=
-				elif (( (pos & 7) == 0 )); then
-					(( pos )) && print "$dasc|"
-					print -n "${pos#16#}  "
-					dasc=' |'
-				fi
-				print -n "${hv#16#} "
-				(( (pos++ & 7) == 3 )) && \
-				    print -n -- '- '
-				dasc=$dasc$dch
-			done
-		fi
-		while (( pos & 7 )); do
-			print -n '     '
-			(( (pos++ & 7) == 3 )) && print -n -- '- '
-		done
-		(( hv == 2147483647 )) || print "$dasc|"
-	}
-expected-stdout:
-	00000000  0048 0065 006C 006C - 006F 002C 0020 0057  |Hello, W|
-	00000008  006F 0072 006C 0064 - 0021 005C 000A 3053  |orld!\.こ|
-	00000010  3093 306B 3061 306F - FF01 000A 0001 0002  |んにちは!...|
-	00000018  0003 0004 0005 0006 - 0007 0008 0009 000A  |........|
-	00000020  000B 000C 000D 000E - 000F 0010 0011 0012  |........|
-	00000028  0013 0014 0015 0016 - 0017 0018 0019 001A  |........|
-	00000030  001B 001C 001D 001E - 001F 0020 0021 0022  |..... !"|
-	00000038  0023 0024 0025 0026 - 0027 0028 0029 002A  |#$%&'()*|
-	00000040  002B 002C 002D 002E - 002F 0030 0031 0032  |+,-./012|
-	00000048  0033 0034 0035 0036 - 0037 0038 0039 003A  |3456789:|
-	00000050  003B 003C 003D 003E - 003F 0040 0041 0042  |;<=>?@AB|
-	00000058  0043 0044 0045 0046 - 0047 0048 0049 004A  |CDEFGHIJ|
-	00000060  004B 004C 004D 004E - 004F 0050 0051 0052  |KLMNOPQR|
-	00000068  0053 0054 0055 0056 - 0057 0058 0059 005A  |STUVWXYZ|
-	00000070  005B 005C 005D 005E - 005F 0060 0061 0062  |[\]^_`ab|
-	00000078  0063 0064 0065 0066 - 0067 0068 0069 006A  |cdefghij|
-	00000080  006B 006C 006D 006E - 006F 0070 0071 0072  |klmnopqr|
-	00000088  0073 0074 0075 0076 - 0077 0078 0079 007A  |stuvwxyz|
-	00000090  007B 007C 007D 007E - 007F 0080 0081 0082  |{|}~....|
-	00000098  0083 0084 0085 0086 - 0087 0088 0089 008A  |........|
-	000000A0  008B 008C 008D 008E - 008F 0090 0091 0092  |........|
-	000000A8  0093 0094 0095 0096 - 0097 0098 0099 009A  |........|
-	000000B0  009B 009C 009D 009E - 009F 00A0 00A1 00A2  |..... ¡¢|
-	000000B8  00A3 00A4 00A5 00A6 - 00A7 00A8 00A9 00AA  |£¤¥¦§¨©ª|
-	000000C0  00AB 00AC 00AD 00AE - 00AF 00B0 00B1 00B2  |«¬­®¯°±²|
-	000000C8  00B3 00B4 00B5 00B6 - 00B7 00B8 00B9 00BA  |³´µ¶·¸¹º|
-	000000D0  00BB 00BC 00BD 00BE - 00BF 00C0 00C1 00C2  |»¼½¾¿ÀÁÂ|
-	000000D8  00C3 00C4 00C5 00C6 - 00C7 00C8 00C9 00CA  |ÃÄÅÆÇÈÉÊ|
-	000000E0  00CB 00CC 00CD 00CE - 00CF 00D0 00D1 00D2  |ËÌÍÎÏÐÑÒ|
-	000000E8  00D3 00D4 00D5 00D6 - 00D7 00D8 00D9 00DA  |ÓÔÕÖ×ØÙÚ|
-	000000F0  00DB 00DC 00DD 00DE - 00DF 00E0 00E1 00E2  |ÛÜÝÞßàáâ|
-	000000F8  00E3 00E4 00E5 00E6 - 00E7 00E8 00E9 00EA  |ãäåæçèéê|
-	00000100  00EB 00EC 00ED 00EE - 00EF 00F0 00F1 00F2  |ëìíîïðñò|
-	00000108  00F3 00F4 00F5 00F6 - 00F7 00F8 00F9 00FA  |óôõö÷øùú|
-	00000110  00FB 00FC 00FD 00FE - 00FF 000A EFFF 000A  |ûüýþÿ.�.|
-	00000118  EFC2 000A EFEF EFBF - EFC0 000A EFC0 EF80  |�.���.��|
-	00000120  000A EFE0 EF80 EF80 - 000A FFFD EFEF EFBF  |.���.���|
-	00000128  EFBE EFEF EFBF EFBF - 000A 0000 007A 000A  |����..z.|
----
-name: integer-base-one-4
-description:
-	Check if ksh93-style base-one integers work
-category: !smksh
-stdin:
-	set -U
-	echo 1 $(('a'))
-	(echo 2f $(('aa'))) 2>&1 | sed "s/^[^']*'/2p '/"
-	echo 3 $(('…'))
-	x="'a'"
-	echo "4 <$x>"
-	echo 5 $(($x))
-	echo 6 $((x))
-expected-stdout:
-	1 97
-	2p 'aa': multi-character character constant
-	3 8230
-	4 <'a'>
-	5 97
-	6 97
----
-name: integer-base-one-5A
-description:
-	Check to see that we’re NUL and Unicode safe
-stdin:
-	set +U
-	print 'a\0b\xfdz' >x
-	read -a y <x
-	set -U
-	typeset -Uui16 y
-	print ${y[*]} .
-expected-stdout:
-	16#61 16#0 16#62 16#FD 16#7A .
----
-name: integer-base-one-5W
-description:
-	Check to see that we’re NUL and Unicode safe
-stdin:
-	set -U
-	print 'a\0b€c' >x
-	read -a y <x
-	set +U
-	typeset -Uui16 y
-	print ${y[*]} .
-expected-stdout:
-	16#61 16#0 16#62 16#20AC 16#63 .
----
-name: ulimit-1
-description:
-	Check if we can use a specific syntax idiom for ulimit
-category: !os:syllable
-stdin:
-	if ! x=$(ulimit -d) || [[ $x = unknown ]]; then
-		#echo expected to fail on this OS
-		echo okay
-	else
-		ulimit -dS $x && echo okay
-	fi
-expected-stdout:
-	okay
----
-name: redir-1
-description:
-	Check some of the most basic invariants of I/O redirection
-stdin:
-	i=0
-	function d {
-		print o$i.
-		print -u2 e$((i++)).
-	}
-	d >a 2>b
-	echo =1=
-	cat a
-	echo =2=
-	cat b
-	echo =3=
-	d 2>&1 >c
-	echo =4=
-	cat c
-	echo =5=
-expected-stdout:
-	=1=
-	o0.
-	=2=
-	e0.
-	=3=
-	e1.
-	=4=
-	o1.
-	=5=
----
-name: bashiop-1
-description:
-	Check if GNU bash-like I/O redirection works
-	Part 1: this is also supported by GNU bash
-category: shell:legacy-no
-stdin:
-	exec 3>&1
-	function threeout {
-		echo ras
-		echo dwa >&2
-		echo tri >&3
-	}
-	threeout &>foo
-	echo ===
-	cat foo
-expected-stdout:
-	tri
-	===
-	ras
-	dwa
----
-name: bashiop-2a
-description:
-	Check if GNU bash-like I/O redirection works
-	Part 2: this is *not* supported by GNU bash
-category: shell:legacy-no
-stdin:
-	exec 3>&1
-	function threeout {
-		echo ras
-		echo dwa >&2
-		echo tri >&3
-	}
-	threeout 3&>foo
-	echo ===
-	cat foo
-expected-stdout:
-	ras
-	===
-	dwa
-	tri
----
-name: bashiop-2b
-description:
-	Check if GNU bash-like I/O redirection works
-	Part 2: this is *not* supported by GNU bash
-category: shell:legacy-no
-stdin:
-	exec 3>&1
-	function threeout {
-		echo ras
-		echo dwa >&2
-		echo tri >&3
-	}
-	threeout 3>foo &>&3
-	echo ===
-	cat foo
-expected-stdout:
-	===
-	ras
-	dwa
-	tri
----
-name: bashiop-2c
-description:
-	Check if GNU bash-like I/O redirection works
-	Part 2: this is supported by GNU bash 4 only
-category: shell:legacy-no
-stdin:
-	echo mir >foo
-	set -o noclobber
-	exec 3>&1
-	function threeout {
-		echo ras
-		echo dwa >&2
-		echo tri >&3
-	}
-	threeout &>>foo
-	echo ===
-	cat foo
-expected-stdout:
-	tri
-	===
-	mir
-	ras
-	dwa
----
-name: bashiop-3a
-description:
-	Check if GNU bash-like I/O redirection fails correctly
-	Part 1: this is also supported by GNU bash
-category: shell:legacy-no
-stdin:
-	echo mir >foo
-	set -o noclobber
-	exec 3>&1
-	function threeout {
-		echo ras
-		echo dwa >&2
-		echo tri >&3
-	}
-	threeout &>foo
-	echo ===
-	cat foo
-expected-stdout:
-	===
-	mir
-expected-stderr-pattern: /.*: can't (create|overwrite) .*/
----
-name: bashiop-3b
-description:
-	Check if GNU bash-like I/O redirection fails correctly
-	Part 2: this is *not* supported by GNU bash
-category: shell:legacy-no
-stdin:
-	echo mir >foo
-	set -o noclobber
-	exec 3>&1
-	function threeout {
-		echo ras
-		echo dwa >&2
-		echo tri >&3
-	}
-	threeout &>|foo
-	echo ===
-	cat foo
-expected-stdout:
-	tri
-	===
-	ras
-	dwa
----
-name: bashiop-4
-description:
-	Check if GNU bash-like I/O redirection works
-	Part 4: this is also supported by GNU bash,
-	but failed in some mksh versions
-category: shell:legacy-no
-stdin:
-	exec 3>&1
-	function threeout {
-		echo ras
-		echo dwa >&2
-		echo tri >&3
-	}
-	function blubb {
-		[[ -e bar ]] && threeout "$bf" &>foo
-	}
-	blubb
-	echo -n >bar
-	blubb
-	echo ===
-	cat foo
-expected-stdout:
-	tri
-	===
-	ras
-	dwa
----
-name: bashiop-5-normal
-description:
-	Check if GNU bash-like I/O redirection is only supported
-	in !POSIX !sh mode as it breaks existing scripts' syntax
-category: shell:legacy-no
-stdin:
-	:>x; echo 1 "$("$__progname" -c 'echo foo>/dev/null&>x echo bar')" = "$(<x)" .
-	:>x; echo 2 "$("$__progname" -o posix -c 'echo foo>/dev/null&>x echo bar')" = "$(<x)" .
-	:>x; echo 3 "$("$__progname" -o sh -c 'echo foo>/dev/null&>x echo bar')" = "$(<x)" .
-expected-stdout:
-	1  = foo echo bar .
-	2  = bar .
-	3  = bar .
----
-name: bashiop-5-legacy
-description:
-	Check if GNU bash-like I/O redirection is not parsed
-	in lksh as it breaks existing scripts' syntax
-category: shell:legacy-yes
-stdin:
-	:>x; echo 1 "$("$__progname" -c 'echo foo>/dev/null&>x echo bar')" = "$(<x)" .
-	:>x; echo 2 "$("$__progname" -o posix -c 'echo foo>/dev/null&>x echo bar')" = "$(<x)" .
-	:>x; echo 3 "$("$__progname" -o sh -c 'echo foo>/dev/null&>x echo bar')" = "$(<x)" .
-expected-stdout:
-	1  = bar .
-	2  = bar .
-	3  = bar .
----
-name: mkshiop-1
-description:
-	Check for support of more than 9 file descriptors
-category: !convfds
-stdin:
-	read -u10 foo 10<<< bar
-	echo x$foo
-expected-stdout:
-	xbar
----
-name: mkshiop-2
-description:
-	Check for support of more than 9 file descriptors
-category: !convfds
-stdin:
-	exec 12>foo
-	print -u12 bar
-	echo baz >&12
-	cat foo
-expected-stdout:
-	bar
-	baz
----
-name: oksh-eval
-description:
-	$OpenBSD: eval.sh,v 1.1 2010/03/24 08:29:44 fgsch Exp $
-stdin:
-	a=
-	for n in ${a#*=}; do echo 1hu ${n} .; done
-	for n in "${a#*=}"; do echo 1hq ${n} .; done
-	for n in ${a##*=}; do echo 2hu ${n} .; done
-	for n in "${a##*=}"; do echo 2hq ${n} .; done
-	for n in ${a%=*}; do echo 1pu ${n} .; done
-	for n in "${a%=*}"; do echo 1pq ${n} .; done
-	for n in ${a%%=*}; do echo 2pu ${n} .; done
-	for n in "${a%%=*}"; do echo 2pq ${n} .; done
-expected-stdout:
-	1hq .
-	2hq .
-	1pq .
-	2pq .
----
-name: oksh-and-list-error-1
-description:
-	Test exit status of rightmost element in 2 element && list in -e mode
-stdin:
-	true && false
-	echo "should not print"
-arguments: !-e!
-expected-exit: e != 0
----
-name: oksh-and-list-error-2
-description:
-	Test exit status of rightmost element in 3 element && list in -e mode
-stdin:
-	true && true && false
-	echo "should not print"
-arguments: !-e!
-expected-exit: e != 0
----
-name: oksh-or-list-error-1
-description:
-	Test exit status of || list in -e mode
-stdin:
-	false || false
-	echo "should not print"
-arguments: !-e!
-expected-exit: e != 0
----
-name: oksh-longline-crash
-description:
-	This used to cause a core dump
-stdin:
-	ulimit -c 0
-	deplibs="-lz -lpng /usr/local/lib/libjpeg.la -ltiff -lm -lX11 -lXext /usr/local/lib/libiconv.la -L/usr/local/lib -L/usr/ports/devel/gettext/w-gettext-0.10.40/gettext-0.10.40/intl/.libs /usr/local/lib/libintl.la /usr/local/lib/libglib.la /usr/local/lib/libgmodule.la -lintl -lm -lX11 -lXext -L/usr/X11R6/lib -lglib -lgmodule -L/usr/local/lib /usr/local/lib/libgdk.la -lintl -lm -lX11 -lXext -L/usr/X11R6/lib -lglib -lgmodule -L/usr/local/lib /usr/local/lib/libgtk.la -ltiff -ljpeg -lz -lpng -lm -lX11 -lXext -lintl -lglib -lgmodule -lgdk -lgtk -L/usr/X11R6/lib -lglib -lgmodule -L/usr/local/lib /usr/local/lib/libgdk_pixbuf.la -lz -lpng /usr/local/lib/libiconv.la -L/usr/local/lib -L/usr/ports/devel/gettext/w-gettext-0.10.40/gettext-0.10.40/intl/.libs /usr/local/lib/libintl.la /usr/local/lib/libglib.la -lm -lm /usr/local/lib/libaudiofile.la -lm -lm -laudiofile -L/usr/local/lib /usr/local/lib/libesd.la -lm -lz -L/usr/local/lib /usr/local/lib/libgnomesupport.la -lm -lz -lm -lglib -L/usr/local/lib /usr/local/lib/libgnome.la -lX11 -lXext /usr/local/lib/libiconv.la -L/usr/local/lib -L/usr/ports/devel/gettext/w-gettext-0.10.40/gettext-0.10.40/intl/.libs /usr/local/lib/libintl.la /usr/local/lib/libgmodule.la -lintl -lm -lX11 -lXext -L/usr/X11R6/lib -lglib -lgmodule -L/usr/local/lib /usr/local/lib/libgdk.la -lintl -lm -lX11 -lXext -L/usr/X11R6/lib -lglib -lgmodule -L/usr/local/lib /usr/local/lib/libgtk.la -lICE -lSM -lz -lpng /usr/local/lib/libungif.la /usr/local/lib/libjpeg.la -ltiff -lm -lz -lpng /usr/local/lib/libungif.la -lz /usr/local/lib/libjpeg.la -ltiff -L/usr/local/lib -L/usr/X11R6/lib /usr/local/lib/libgdk_imlib.la -lm -L/usr/local/lib /usr/local/lib/libart_lgpl.la -lm -lz -lm -lX11 -lXext -lintl -lglib -lgmodule -lgdk -lgtk -lICE -lSM -lm -lX11 -lXext -lintl -lglib -lgmodule -lgdk -lgtk -L/usr/X11R6/lib -lm -lz -lpng -lungif -lz -ljpeg -ltiff -ljpeg -lgdk_imlib -lglib -lm -laudiofile -lm -laudiofile -lesd -L/usr/local/lib /usr/local/lib/libgnomeui.la -lz -lz /usr/local/lib/libxml.la -lz -lz -lz /usr/local/lib!
 /libxml.la -lm -lX11 -lXext /usr/local/lib/libiconv.la -L/usr/ports/devel/gettext/w-gettext-0.10.40/gettext-0.10.40/intl/.libs /usr/local/lib/libintl.la /usr/local/lib/libglib.la /usr/local/lib/libgmodule.la -lintl -lglib -lgmodule /usr/local/lib/libgdk.la /usr/local/lib/libgtk.la -L/usr/X11R6/lib -L/usr/local/lib /usr/local/lib/libglade.la -lz -lz -lz /usr/local/lib/libxml.la /usr/local/lib/libglib.la -lm -lm /usr/local/lib/libaudiofile.la -lm -lm -laudiofile /usr/local/lib/libesd.la -lm -lz /usr/local/lib/libgnomesupport.la -lm -lz -lm -lglib /usr/local/lib/libgnome.la -lX11 -lXext /usr/local/lib/libiconv.la -L/usr/ports/devel/gettext/w-gettext-0.10.40/gettext-0.10.40/intl/.libs /usr/local/lib/libintl.la /usr/local/lib/libgmodule.la -lintl -lm -lX11 -lXext -lglib -lgmodule /usr/local/lib/libgdk.la -lintl -lm -lX11 -lXext -lglib -lgmodule /usr/local/lib/libgtk.la -lICE -lSM -lz -lpng /usr/local/lib/libungif.la /usr/local/lib/libjpeg.la -ltiff -lm -lz -lz /usr/local/lib/libgdk_imlib.la /usr/local/lib/libart_lgpl.la -lm -lz -lm -lX11 -lXext -lintl -lglib -lgmodule -lgdk -lgtk -lm -lX11 -lXext -lintl -lglib -lgmodule -lgdk -lgtk -lm -lz -lungif -lz -ljpeg -ljpeg -lgdk_imlib -lglib -lm -laudiofile -lm -laudiofile -lesd /usr/local/lib/libgnomeui.la -L/usr/X11R6/lib -L/usr/local/lib /usr/local/lib/libglade-gnome.la /usr/local/lib/libglib.la -lm -lm /usr/local/lib/libaudiofile.la -lm -lm -laudiofile -L/usr/local/lib /usr/local/lib/libesd.la -lm -lz -L/usr/local/lib /usr/local/lib/libgnomesupport.la -lm -lz -lm -lglib -L/usr/local/lib /usr/local/lib/libgnome.la -lX11 -lXext /usr/local/lib/libiconv.la -L/usr/local/lib -L/usr/ports/devel/gettext/w-gettext-0.10.40/gettext-0.10.40/intl/.libs /usr/local/lib/libintl.la /usr/local/lib/libgmodule.la -lintl -lm -lX11 -lXext -L/usr/X11R6/lib -lglib -lgmodule -L/usr/local/lib /usr/local/lib/libgdk.la -lintl -lm -lX11 -lXext -L/usr/X11R6/lib -lglib -lgmodule -L/usr/local/lib /usr/local/lib/libgtk.la -lICE -lSM -lz -lpng /usr/local/lib/libungif.la /usr/local/lib/libjpeg.la -ltiff -!
 lm -lz -lpng /usr/local/lib/libungif.la -lz /usr/local/lib/libjpeg.la -ltiff -L/usr/local/lib -L/usr/X11R6/lib /usr/local/lib/libgdk_imlib.la -lm -L/usr/local/lib /usr/local/lib/libart_lgpl.la -lm -lz -lm -lX11 -lXext -lintl -lglib -lgmodule -lgdk -lgtk -lICE -lSM -lm -lX11 -lXext -lintl -lglib -lgmodule -lgdk -lgtk -L/usr/X11R6/lib -lm -lz -lpng -lungif -lz -ljpeg -ltiff -ljpeg -lgdk_imlib -lglib -lm -laudiofile -lm -laudiofile -lesd -L/usr/local/lib /usr/local/lib/libgnomeui.la -L/usr/X11R6/lib -L/usr/local/lib"
-	specialdeplibs="-lgnomeui -lart_lgpl -lgdk_imlib -ltiff -ljpeg -lungif -lpng -lz -lSM -lICE -lgtk -lgdk -lgmodule -lintl -lXext -lX11 -lgnome -lgnomesupport -lesd -laudiofile -lm -lglib"
-	for deplib in $deplibs; do
-		case $deplib in
-		-L*)
-			new_libs="$deplib $new_libs"
-			;;
-		*)
-			case " $specialdeplibs " in
-			*" $deplib "*)
-				new_libs="$deplib $new_libs";;
-			esac
-			;;
-		esac
-	done
----
-name: oksh-seterror-1
-description:
-	The -e flag should be ignored when executing a compound list
-	followed by an if statement.
-stdin:
-	if true; then false && false; fi
-	true
-arguments: !-e!
-expected-exit: e == 0
----
-name: oksh-seterror-2
-description:
-	The -e flag should be ignored when executing a compound list
-	followed by an if statement.
-stdin:
-	if true; then if true; then false && false; fi; fi
-	true
-arguments: !-e!
-expected-exit: e == 0
----
-name: oksh-seterror-3
-description:
-	The -e flag should be ignored when executing a compound list
-	followed by an elif statement.
-stdin:
-	if true; then :; elif true; then false && false; fi
-arguments: !-e!
-expected-exit: e == 0
----
-name: oksh-seterror-4
-description:
-	The -e flag should be ignored when executing a pipeline
-	beginning with '!'
-stdin:
-	for i in 1 2 3
-	do
-		false && false
-		true || false
-	done
-arguments: !-e!
-expected-exit: e == 0
----
-name: oksh-seterror-5
-description:
-	The -e flag should be ignored when executing a pipeline
-	beginning with '!'
-stdin:
-	! true | false
-	true
-arguments: !-e!
-expected-exit: e == 0
----
-name: oksh-seterror-6
-description:
-	When trapping ERR and EXIT, both traps should run in -e mode
-	when an error occurs.
-stdin:
-	trap 'echo EXIT' EXIT
-	trap 'echo ERR' ERR
-	set -e
-	false
-	echo DONE
-	exit 0
-arguments: !-e!
-expected-exit: e != 0
-expected-stdout:
-	ERR
-	EXIT
----
-name: oksh-seterror-7
-description:
-	The -e flag within a command substitution should be honored
-stdin:
-	echo $( set -e; false; echo foo )
-arguments: !-e!
-expected-stdout:
-	
----
-name: oksh-input-comsub
-description:
-	A command substitution using input redirection should exit with
-	failure if the input file does not exist.
-stdin:
-	var=$(< non-existent)
-expected-exit: e != 0
-expected-stderr-pattern: /non-existent/
----
-name: oksh-empty-for-list
-description:
-	A for list which expands to zero items should not execute the body.
-stdin:
-	set foo bar baz ; for out in ; do echo $out ; done
----
-name: oksh-varfunction-mod1
-description:
-	$OpenBSD: varfunction.sh,v 1.1 2003/12/15 05:28:40 otto Exp $
-	Calling
-		FOO=bar f
-	where f is a ksh style function, should not set FOO in the current
-	env. If f is a Bourne style function, FOO should be set. Furthermore,
-	the function should receive a correct value of FOO. However, differing
-	from oksh, setting FOO in the function itself must change the value in
-	setting FOO in the function itself should not change the value in
-	global environment.
-	Inspired by PR 2450.
-stdin:
-	print '#!'"$__progname"'\nunset RANDOM\nexport | while IFS= read -r' \
-	    'RANDOM; do eval '\''print -r -- "$RANDOM=$'\''"$RANDOM"'\'\"\'\; \
-	    done >env; chmod +x env; PATH=.:$PATH
-	function k {
-		if [ x$FOO != xbar ]; then
-			echo 1
-			return 1
-		fi
-		x=$(env | grep FOO)
-		if [ "x$x" != "xFOO=bar" ]; then
-			echo 2
-			return 1;
-		fi
-		FOO=foo
-		return 0
-	}
-	b () {
-		if [ x$FOO != xbar ]; then
-			echo 3
-			return 1
-		fi
-		x=$(env | grep FOO)
-		if [ "x$x" != "xFOO=bar" ]; then
-			echo 4
-			return 1;
-		fi
-		FOO=foo
-		return 0
-	}
-	FOO=bar k
-	if [ $? != 0 ]; then
-		exit 1
-	fi
-	if [ x$FOO != x ]; then
-		exit 1
-	fi
-	FOO=bar b
-	if [ $? != 0 ]; then
-		exit 1
-	fi
-	if [ x$FOO != xfoo ]; then
-		exit 1
-	fi
-	FOO=barbar
-	FOO=bar k
-	if [ $? != 0 ]; then
-		exit 1
-	fi
-	if [ x$FOO != xbarbar ]; then
-		exit 1
-	fi
-	FOO=bar b
-	if [ $? != 0 ]; then
-		exit 1
-	fi
-	if [ x$FOO != xfoo ]; then
-		exit 1
-	fi
----
-name: fd-cloexec-1
-description:
-	Verify that file descriptors > 2 are private for Korn shells
-	AT&T ksh93 does this still, which means we must keep it as well
-category: shell:legacy-no
-file-setup: file 644 "test.sh"
-	echo >&3 Fowl
-stdin:
-	exec 3>&1
-	"$__progname" test.sh
-expected-exit: e != 0
-expected-stderr-pattern:
-	/bad file descriptor/
----
-name: fd-cloexec-2
-description:
-	Verify that file descriptors > 2 are not private for POSIX shells
-	See Debian Bug #154540, Closes: #499139
-file-setup: file 644 "test.sh"
-	echo >&3 Fowl
-stdin:
-	test -n "$POSH_VERSION" || set -o sh
-	exec 3>&1
-	"$__progname" test.sh
-expected-stdout:
-	Fowl
----
-name: fd-cloexec-3
-description:
-	Verify that file descriptors > 2 are not private for LEGACY KSH
-category: shell:legacy-yes
-file-setup: file 644 "test.sh"
-	echo >&3 Fowl
-stdin:
-	exec 3>&1
-	"$__progname" test.sh
-expected-stdout:
-	Fowl
----
-name: comsub-1a
-description:
-	COMSUB are now parsed recursively, so this works
-	see also regression-6: matching parenthesēs bug
-	Fails on: pdksh bash2 bash3 zsh
-	Passes on: bash4 ksh93 mksh(20110313+)
-stdin:
-	echo 1 $(case 1 in (1) echo yes;; (2) echo no;; esac) .
-	echo 2 $(case 1 in 1) echo yes;; 2) echo no;; esac) .
-	TEST=1234; echo 3 ${TEST: $(case 1 in (1) echo 1;; (*) echo 2;; esac)} .
-	TEST=5678; echo 4 ${TEST: $(case 1 in 1) echo 1;; *) echo 2;; esac)} .
-	a=($(case 1 in (1) echo 1;; (*) echo 2;; esac)); echo 5 ${a[0]} .
-	a=($(case 1 in 1) echo 1;; *) echo 2;; esac)); echo 6 ${a[0]} .
-expected-stdout:
-	1 yes .
-	2 yes .
-	3 234 .
-	4 678 .
-	5 1 .
-	6 1 .
----
-name: comsub-1b
-description:
-	COMSUB are now parsed recursively, so this works
-	Fails on: pdksh bash2 bash3 bash4 zsh
-	Passes on: ksh93 mksh(20110313+)
-stdin:
-	echo 1 $(($(case 1 in (1) echo 1;; (*) echo 2;; esac)+10)) .
-	echo 2 $(($(case 1 in 1) echo 1;; *) echo 2;; esac)+20)) .
-	(( a = $(case 1 in (1) echo 1;; (*) echo 2;; esac) )); echo 3 $a .
-	(( a = $(case 1 in 1) echo 1;; *) echo 2;; esac) )); echo 4 $a .
-	a=($(($(case 1 in (1) echo 1;; (*) echo 2;; esac)+10))); echo 5 ${a[0]} .
-	a=($(($(case 1 in 1) echo 1;; *) echo 2;; esac)+20))); echo 6 ${a[0]} .
-expected-stdout:
-	1 11 .
-	2 21 .
-	3 1 .
-	4 1 .
-	5 11 .
-	6 21 .
----
-name: comsub-2
-description:
-	RedHat BZ#496791 – another case of missing recursion
-	in parsing COMSUB expressions
-	Fails on: pdksh bash2 bash3¹ bash4¹ zsh
-	Passes on: ksh93 mksh(20110305+)
-	① bash[34] seem to choke on comment ending with backslash-newline
-stdin:
-	# a comment with " ' \
-	x=$(
-	echo yes
-	# a comment with " ' \
-	)
-	echo $x
-expected-stdout:
-	yes
----
-name: comsub-3
-description:
-	Extended test for COMSUB explaining why a recursive parser
-	is a must (a non-recursive parser cannot pass all three of
-	these test cases, especially the ‘#’ is difficult)
-stdin:
-	print '#!'"$__progname"'\necho 1234' >id; chmod +x id; PATH=.:$PATH
-	echo $(typeset -i10 x=16#20; echo $x)
-	echo $(typeset -Uui16 x=16#$(id -u)
-	) .
-	echo $(c=1; d=1
-	typeset -Uui16 a=36#foo; c=2
-	typeset -Uui16 b=36 #foo; d=2
-	echo $a $b $c $d)
-expected-stdout:
-	32
-	.
-	16#4F68 16#24 2 1
----
-name: comsub-4
-description:
-	Check the tree dump functions for !MKSH_SMALL functionality
-category: !smksh
-stdin:
-	x() { case $1 in u) echo x ;;& *) echo $1 ;; esac; }
-	typeset -f x
-expected-stdout:
-	x() {
-		case $1 in
-		(u)
-			echo x 
-			;|
-		(*)
-			echo $1 
-			;;
-		esac 
-	} 
----
-name: comsub-5
-description:
-	Check COMSUB works with aliases (does not expand them twice)
-stdin:
-	print '#!'"$__progname"'\nfor x in "$@"; do print -r -- "$x"; done' >pfn
-	chmod +x pfn
-	alias echo='echo a'
-	foo() {
-		./pfn "$(echo foo)"
-	}
-	./pfn "$(echo b)"
-	typeset -f foo
-expected-stdout:
-	a b
-	foo() {
-		./pfn "$(echo foo )" 
-	} 
----
-name: comsub-torture
-description:
-	Check the tree dump functions work correctly
-stdin:
-	if [[ -z $__progname ]]; then echo >&2 call me with __progname; exit 1; fi
-	while IFS= read -r line; do
-		if [[ $line = '#1' ]]; then
-			lastf=0
-			continue
-		elif [[ $line = EOFN* ]]; then
-			fbody=$fbody$'\n'$line
-			continue
-		elif [[ $line != '#'* ]]; then
-			fbody=$fbody$'\n\t'$line
-			continue
-		fi
-		if (( lastf )); then
-			x="inline_${nextf}() {"$fbody$'\n}\n'
-			print -nr -- "$x"
-			print -r -- "${x}typeset -f inline_$nextf" | "$__progname"
-			x="function comsub_$nextf { x=\$("$fbody$'\n); }\n'
-			print -nr -- "$x"
-			print -r -- "${x}typeset -f comsub_$nextf" | "$__progname"
-			x="function reread_$nextf { x=\$(("$fbody$'\n)|tr u x); }\n'
-			print -nr -- "$x"
-			print -r -- "${x}typeset -f reread_$nextf" | "$__progname"
-		fi
-		lastf=1
-		fbody=
-		nextf=${line#?}
-	done <<'EOD'
-	#1
-	#TCOM
-	vara=1  varb='2  3'  cmd  arg1  $arg2  "$arg3  4"
-	#TPAREN_TPIPE_TLIST
-	(echo $foo  |  tr -dc 0-9; echo)
-	#TAND_TOR
-	cmd  &&  echo ja  ||  echo nein
-	#TSELECT
-	select  file  in  *;  do  echo  "<$file>" ;  break ;  done
-	#TFOR_TTIME
-	time  for  i  in  {1,2,3}  ;  do  echo  $i ;  done
-	#TCASE
-	case  $foo  in  1)  echo eins;& 2) echo zwei  ;| *) echo kann net bis drei zählen;;  esac
-	#TIF_TBANG_TDBRACKET_TELIF
-	if  !  [[  1  =  1  ]]  ;  then  echo eins;  elif [[ 1 = 2 ]]; then echo zwei  ;else echo drei; fi
-	#TWHILE
-	i=1; while (( i < 10 )); do echo $i; let ++i; done
-	#TUNTIL
-	i=10; until  (( !--i )) ; do echo $i; done
-	#TCOPROC
-	cat  *  |&  ls
-	#TFUNCT_TBRACE_TASYNC
-	function  korn  {  echo eins; echo zwei ;  }
-	bourne  ()  {  logger *  &  }
-	#IOREAD_IOCAT
-	tr  x  u  0<foo  >>bar
-	#IOWRITE_IOCLOB_IOHERE_noIOSKIP
-	cat  >|bar  <<'EOFN'
-	foo
-	EOFN
-	#IOWRITE_noIOCLOB_IOHERE_IOSKIP
-	cat  1>bar  <<-EOFI
-	foo
-	EOFI
-	#IORDWR_IODUP
-	sh  1<>/dev/console  0<&1  2>&1
-	#COMSUB_EXPRSUB_FUNSUB_VALSUB
-	echo $(true) $((1+ 2)) ${  :;} ${| REPLY=x;}
-	#QCHAR_OQUOTE_CQUOTE
-	echo fo\ob\"a\`r\'b\$az
-	echo "fo\ob\"a\`r\'b\$az"
-	echo 'fo\ob\"a\`r'\''b\$az'
-	#OSUBST_CSUBST_OPAT_SPAT_CPAT
-	[[ ${foo#bl\(u\)b} = @(bar|baz) ]]
-	#heredoc_closed
-	x=$(cat <<EOFN
-	note there must be no space between EOFN and )
-	EOFN); echo $x
-	#heredoc_space
-	x=$(cat <<EOFN\ 
-	note the space between EOFN and ) is actually part of the here document marker
-	EOFN ); echo $x
-	#patch_motd
-	x=$(sysctl -n kern.version | sed 1q)
-	[[ -s /etc/motd && "$([[ "$(head -1 /etc/motd)" != $x ]] && \
-	    ed -s /etc/motd 2>&1 <<-EOF
-		1,/^\$/d
-		0a
-			$x
-	
-		.
-		wq
-	EOF)" = @(?) ]] && rm -f /etc/motd
-	if [[ ! -s /etc/motd ]]; then
-		install -c -o root -g wheel -m 664 /dev/null /etc/motd
-		print -- "$x\n" >/etc/motd
-	fi
-	#wdarrassign
-	case x in
-	x) a+=b; c+=(d e)
-	esac
-	#0
-	EOD
-expected-stdout:
-	inline_TCOM() {
-		vara=1  varb='2  3'  cmd  arg1  $arg2  "$arg3  4"
-	}
-	inline_TCOM() {
-		vara=1 varb="2  3" cmd arg1 $arg2 "$arg3  4" 
-	} 
-	function comsub_TCOM { x=$(
-		vara=1  varb='2  3'  cmd  arg1  $arg2  "$arg3  4"
-	); }
-	function comsub_TCOM {
-		x=$(vara=1 varb="2  3" cmd arg1 $arg2 "$arg3  4" ) 
-	} 
-	function reread_TCOM { x=$((
-		vara=1  varb='2  3'  cmd  arg1  $arg2  "$arg3  4"
-	)|tr u x); }
-	function reread_TCOM {
-		x=$(( vara=1 varb="2  3" cmd arg1 $arg2 "$arg3  4" ) | tr u x ) 
-	} 
-	inline_TPAREN_TPIPE_TLIST() {
-		(echo $foo  |  tr -dc 0-9; echo)
-	}
-	inline_TPAREN_TPIPE_TLIST() {
-		( echo $foo | tr -dc 0-9 
-		  echo ) 
-	} 
-	function comsub_TPAREN_TPIPE_TLIST { x=$(
-		(echo $foo  |  tr -dc 0-9; echo)
-	); }
-	function comsub_TPAREN_TPIPE_TLIST {
-		x=$(( echo $foo | tr -dc 0-9 ; echo ) ) 
-	} 
-	function reread_TPAREN_TPIPE_TLIST { x=$((
-		(echo $foo  |  tr -dc 0-9; echo)
-	)|tr u x); }
-	function reread_TPAREN_TPIPE_TLIST {
-		x=$(( ( echo $foo | tr -dc 0-9 ; echo ) ) | tr u x ) 
-	} 
-	inline_TAND_TOR() {
-		cmd  &&  echo ja  ||  echo nein
-	}
-	inline_TAND_TOR() {
-		cmd && echo ja || echo nein 
-	} 
-	function comsub_TAND_TOR { x=$(
-		cmd  &&  echo ja  ||  echo nein
-	); }
-	function comsub_TAND_TOR {
-		x=$(cmd && echo ja || echo nein ) 
-	} 
-	function reread_TAND_TOR { x=$((
-		cmd  &&  echo ja  ||  echo nein
-	)|tr u x); }
-	function reread_TAND_TOR {
-		x=$(( cmd && echo ja || echo nein ) | tr u x ) 
-	} 
-	inline_TSELECT() {
-		select  file  in  *;  do  echo  "<$file>" ;  break ;  done
-	}
-	inline_TSELECT() {
-		select file in * 
-		do
-			echo "<$file>" 
-			break 
-		done 
-	} 
-	function comsub_TSELECT { x=$(
-		select  file  in  *;  do  echo  "<$file>" ;  break ;  done
-	); }
-	function comsub_TSELECT {
-		x=$(select file in * ; do echo "<$file>" ; break ; done ) 
-	} 
-	function reread_TSELECT { x=$((
-		select  file  in  *;  do  echo  "<$file>" ;  break ;  done
-	)|tr u x); }
-	function reread_TSELECT {
-		x=$(( select file in * ; do echo "<$file>" ; break ; done ) | tr u x ) 
-	} 
-	inline_TFOR_TTIME() {
-		time  for  i  in  {1,2,3}  ;  do  echo  $i ;  done
-	}
-	inline_TFOR_TTIME() {
-		time for i in {1,2,3} 
-		do
-			echo $i 
-		done 
-	} 
-	function comsub_TFOR_TTIME { x=$(
-		time  for  i  in  {1,2,3}  ;  do  echo  $i ;  done
-	); }
-	function comsub_TFOR_TTIME {
-		x=$(time for i in {1,2,3} ; do echo $i ; done ) 
-	} 
-	function reread_TFOR_TTIME { x=$((
-		time  for  i  in  {1,2,3}  ;  do  echo  $i ;  done
-	)|tr u x); }
-	function reread_TFOR_TTIME {
-		x=$(( time for i in {1,2,3} ; do echo $i ; done ) | tr u x ) 
-	} 
-	inline_TCASE() {
-		case  $foo  in  1)  echo eins;& 2) echo zwei  ;| *) echo kann net bis drei zählen;;  esac
-	}
-	inline_TCASE() {
-		case $foo in
-		(1)
-			echo eins 
-			;&
-		(2)
-			echo zwei 
-			;|
-		(*)
-			echo kann net bis drei zählen 
-			;;
-		esac 
-	} 
-	function comsub_TCASE { x=$(
-		case  $foo  in  1)  echo eins;& 2) echo zwei  ;| *) echo kann net bis drei zählen;;  esac
-	); }
-	function comsub_TCASE {
-		x=$(case $foo in (1) echo eins  ;& (2) echo zwei  ;| (*) echo kann net bis drei zählen  ;; esac ) 
-	} 
-	function reread_TCASE { x=$((
-		case  $foo  in  1)  echo eins;& 2) echo zwei  ;| *) echo kann net bis drei zählen;;  esac
-	)|tr u x); }
-	function reread_TCASE {
-		x=$(( case $foo in (1) echo eins  ;& (2) echo zwei  ;| (*) echo kann net bis drei zählen  ;; esac ) | tr u x ) 
-	} 
-	inline_TIF_TBANG_TDBRACKET_TELIF() {
-		if  !  [[  1  =  1  ]]  ;  then  echo eins;  elif [[ 1 = 2 ]]; then echo zwei  ;else echo drei; fi
-	}
-	inline_TIF_TBANG_TDBRACKET_TELIF() {
-		if ! [[ 1 = 1 ]] 
-		then
-			echo eins 
-		elif [[ 1 = 2 ]] 
-		then
-			echo zwei 
-		else
-			echo drei 
-		fi 
-	} 
-	function comsub_TIF_TBANG_TDBRACKET_TELIF { x=$(
-		if  !  [[  1  =  1  ]]  ;  then  echo eins;  elif [[ 1 = 2 ]]; then echo zwei  ;else echo drei; fi
-	); }
-	function comsub_TIF_TBANG_TDBRACKET_TELIF {
-		x=$(if ! [[ 1 = 1 ]] ; then echo eins ; elif [[ 1 = 2 ]] ; then echo zwei ; else echo drei ; fi ) 
-	} 
-	function reread_TIF_TBANG_TDBRACKET_TELIF { x=$((
-		if  !  [[  1  =  1  ]]  ;  then  echo eins;  elif [[ 1 = 2 ]]; then echo zwei  ;else echo drei; fi
-	)|tr u x); }
-	function reread_TIF_TBANG_TDBRACKET_TELIF {
-		x=$(( if ! [[ 1 = 1 ]] ; then echo eins ; elif [[ 1 = 2 ]] ; then echo zwei ; else echo drei ; fi ) | tr u x ) 
-	} 
-	inline_TWHILE() {
-		i=1; while (( i < 10 )); do echo $i; let ++i; done
-	}
-	inline_TWHILE() {
-		i=1 
-		while let] " i < 10 " 
-		do
-			echo $i 
-			let ++i 
-		done 
-	} 
-	function comsub_TWHILE { x=$(
-		i=1; while (( i < 10 )); do echo $i; let ++i; done
-	); }
-	function comsub_TWHILE {
-		x=$(i=1 ; while let] " i < 10 " ; do echo $i ; let ++i ; done ) 
-	} 
-	function reread_TWHILE { x=$((
-		i=1; while (( i < 10 )); do echo $i; let ++i; done
-	)|tr u x); }
-	function reread_TWHILE {
-		x=$(( i=1 ; while let] " i < 10 " ; do echo $i ; let ++i ; done ) | tr u x ) 
-	} 
-	inline_TUNTIL() {
-		i=10; until  (( !--i )) ; do echo $i; done
-	}
-	inline_TUNTIL() {
-		i=10 
-		until let] " !--i " 
-		do
-			echo $i 
-		done 
-	} 
-	function comsub_TUNTIL { x=$(
-		i=10; until  (( !--i )) ; do echo $i; done
-	); }
-	function comsub_TUNTIL {
-		x=$(i=10 ; until let] " !--i " ; do echo $i ; done ) 
-	} 
-	function reread_TUNTIL { x=$((
-		i=10; until  (( !--i )) ; do echo $i; done
-	)|tr u x); }
-	function reread_TUNTIL {
-		x=$(( i=10 ; until let] " !--i " ; do echo $i ; done ) | tr u x ) 
-	} 
-	inline_TCOPROC() {
-		cat  *  |&  ls
-	}
-	inline_TCOPROC() {
-		cat * |& 
-		ls 
-	} 
-	function comsub_TCOPROC { x=$(
-		cat  *  |&  ls
-	); }
-	function comsub_TCOPROC {
-		x=$(cat * |&  ls ) 
-	} 
-	function reread_TCOPROC { x=$((
-		cat  *  |&  ls
-	)|tr u x); }
-	function reread_TCOPROC {
-		x=$(( cat * |&  ls ) | tr u x ) 
-	} 
-	inline_TFUNCT_TBRACE_TASYNC() {
-		function  korn  {  echo eins; echo zwei ;  }
-		bourne  ()  {  logger *  &  }
-	}
-	inline_TFUNCT_TBRACE_TASYNC() {
-		function korn {
-			echo eins 
-			echo zwei 
-		} 
-		bourne() {
-			logger * & 
-		} 
-	} 
-	function comsub_TFUNCT_TBRACE_TASYNC { x=$(
-		function  korn  {  echo eins; echo zwei ;  }
-		bourne  ()  {  logger *  &  }
-	); }
-	function comsub_TFUNCT_TBRACE_TASYNC {
-		x=$(function korn { echo eins ; echo zwei ; } ; bourne() { logger * &  } ) 
-	} 
-	function reread_TFUNCT_TBRACE_TASYNC { x=$((
-		function  korn  {  echo eins; echo zwei ;  }
-		bourne  ()  {  logger *  &  }
-	)|tr u x); }
-	function reread_TFUNCT_TBRACE_TASYNC {
-		x=$(( function korn { echo eins ; echo zwei ; } ; bourne() { logger * &  } ) | tr u x ) 
-	} 
-	inline_IOREAD_IOCAT() {
-		tr  x  u  0<foo  >>bar
-	}
-	inline_IOREAD_IOCAT() {
-		tr x u <foo >>bar 
-	} 
-	function comsub_IOREAD_IOCAT { x=$(
-		tr  x  u  0<foo  >>bar
-	); }
-	function comsub_IOREAD_IOCAT {
-		x=$(tr x u <foo >>bar ) 
-	} 
-	function reread_IOREAD_IOCAT { x=$((
-		tr  x  u  0<foo  >>bar
-	)|tr u x); }
-	function reread_IOREAD_IOCAT {
-		x=$(( tr x u <foo >>bar ) | tr u x ) 
-	} 
-	inline_IOWRITE_IOCLOB_IOHERE_noIOSKIP() {
-		cat  >|bar  <<'EOFN'
-		foo
-	EOFN
-	}
-	inline_IOWRITE_IOCLOB_IOHERE_noIOSKIP() {
-		cat >|bar <<"EOFN"
-		foo
-	EOFN
-	
-	} 
-	function comsub_IOWRITE_IOCLOB_IOHERE_noIOSKIP { x=$(
-		cat  >|bar  <<'EOFN'
-		foo
-	EOFN
-	); }
-	function comsub_IOWRITE_IOCLOB_IOHERE_noIOSKIP {
-		x=$(cat >|bar <<"EOFN"
-		foo
-	EOFN
-	) 
-	} 
-	function reread_IOWRITE_IOCLOB_IOHERE_noIOSKIP { x=$((
-		cat  >|bar  <<'EOFN'
-		foo
-	EOFN
-	)|tr u x); }
-	function reread_IOWRITE_IOCLOB_IOHERE_noIOSKIP {
-		x=$(( cat >|bar <<"EOFN"
-		foo
-	EOFN
-	) | tr u x ) 
-	} 
-	inline_IOWRITE_noIOCLOB_IOHERE_IOSKIP() {
-		cat  1>bar  <<-EOFI
-		foo
-		EOFI
-	}
-	inline_IOWRITE_noIOCLOB_IOHERE_IOSKIP() {
-		cat >bar <<-EOFI
-	foo
-	EOFI
-	
-	} 
-	function comsub_IOWRITE_noIOCLOB_IOHERE_IOSKIP { x=$(
-		cat  1>bar  <<-EOFI
-		foo
-		EOFI
-	); }
-	function comsub_IOWRITE_noIOCLOB_IOHERE_IOSKIP {
-		x=$(cat >bar <<-EOFI
-	foo
-	EOFI
-	) 
-	} 
-	function reread_IOWRITE_noIOCLOB_IOHERE_IOSKIP { x=$((
-		cat  1>bar  <<-EOFI
-		foo
-		EOFI
-	)|tr u x); }
-	function reread_IOWRITE_noIOCLOB_IOHERE_IOSKIP {
-		x=$(( cat >bar <<-EOFI
-	foo
-	EOFI
-	) | tr u x ) 
-	} 
-	inline_IORDWR_IODUP() {
-		sh  1<>/dev/console  0<&1  2>&1
-	}
-	inline_IORDWR_IODUP() {
-		sh 1<>/dev/console <&1 2>&1 
-	} 
-	function comsub_IORDWR_IODUP { x=$(
-		sh  1<>/dev/console  0<&1  2>&1
-	); }
-	function comsub_IORDWR_IODUP {
-		x=$(sh 1<>/dev/console <&1 2>&1 ) 
-	} 
-	function reread_IORDWR_IODUP { x=$((
-		sh  1<>/dev/console  0<&1  2>&1
-	)|tr u x); }
-	function reread_IORDWR_IODUP {
-		x=$(( sh 1<>/dev/console <&1 2>&1 ) | tr u x ) 
-	} 
-	inline_COMSUB_EXPRSUB_FUNSUB_VALSUB() {
-		echo $(true) $((1+ 2)) ${  :;} ${| REPLY=x;}
-	}
-	inline_COMSUB_EXPRSUB_FUNSUB_VALSUB() {
-		echo $(true ) $((1+ 2)) ${ : ;} ${|REPLY=x ;} 
-	} 
-	function comsub_COMSUB_EXPRSUB_FUNSUB_VALSUB { x=$(
-		echo $(true) $((1+ 2)) ${  :;} ${| REPLY=x;}
-	); }
-	function comsub_COMSUB_EXPRSUB_FUNSUB_VALSUB {
-		x=$(echo $(true ) $((1+ 2)) ${ : ;} ${|REPLY=x ;} ) 
-	} 
-	function reread_COMSUB_EXPRSUB_FUNSUB_VALSUB { x=$((
-		echo $(true) $((1+ 2)) ${  :;} ${| REPLY=x;}
-	)|tr u x); }
-	function reread_COMSUB_EXPRSUB_FUNSUB_VALSUB {
-		x=$(( echo $(true ) $((1+ 2)) ${ : ;} ${|REPLY=x ;} ) | tr u x ) 
-	} 
-	inline_QCHAR_OQUOTE_CQUOTE() {
-		echo fo\ob\"a\`r\'b\$az
-		echo "fo\ob\"a\`r\'b\$az"
-		echo 'fo\ob\"a\`r'\''b\$az'
-	}
-	inline_QCHAR_OQUOTE_CQUOTE() {
-		echo fo\ob\"a\`r\'b\$az 
-		echo "fo\ob\"a\`r\'b\$az" 
-		echo "fo\\ob\\\"a\\\`r"\'"b\\\$az" 
-	} 
-	function comsub_QCHAR_OQUOTE_CQUOTE { x=$(
-		echo fo\ob\"a\`r\'b\$az
-		echo "fo\ob\"a\`r\'b\$az"
-		echo 'fo\ob\"a\`r'\''b\$az'
-	); }
-	function comsub_QCHAR_OQUOTE_CQUOTE {
-		x=$(echo fo\ob\"a\`r\'b\$az ; echo "fo\ob\"a\`r\'b\$az" ; echo "fo\\ob\\\"a\\\`r"\'"b\\\$az" ) 
-	} 
-	function reread_QCHAR_OQUOTE_CQUOTE { x=$((
-		echo fo\ob\"a\`r\'b\$az
-		echo "fo\ob\"a\`r\'b\$az"
-		echo 'fo\ob\"a\`r'\''b\$az'
-	)|tr u x); }
-	function reread_QCHAR_OQUOTE_CQUOTE {
-		x=$(( echo fo\ob\"a\`r\'b\$az ; echo "fo\ob\"a\`r\'b\$az" ; echo "fo\\ob\\\"a\\\`r"\'"b\\\$az" ) | tr u x ) 
-	} 
-	inline_OSUBST_CSUBST_OPAT_SPAT_CPAT() {
-		[[ ${foo#bl\(u\)b} = @(bar|baz) ]]
-	}
-	inline_OSUBST_CSUBST_OPAT_SPAT_CPAT() {
-		[[ ${foo#bl\(u\)b} = @(bar|baz) ]] 
-	} 
-	function comsub_OSUBST_CSUBST_OPAT_SPAT_CPAT { x=$(
-		[[ ${foo#bl\(u\)b} = @(bar|baz) ]]
-	); }
-	function comsub_OSUBST_CSUBST_OPAT_SPAT_CPAT {
-		x=$([[ ${foo#bl\(u\)b} = @(bar|baz) ]] ) 
-	} 
-	function reread_OSUBST_CSUBST_OPAT_SPAT_CPAT { x=$((
-		[[ ${foo#bl\(u\)b} = @(bar|baz) ]]
-	)|tr u x); }
-	function reread_OSUBST_CSUBST_OPAT_SPAT_CPAT {
-		x=$(( [[ ${foo#bl\(u\)b} = @(bar|baz) ]] ) | tr u x ) 
-	} 
-	inline_heredoc_closed() {
-		x=$(cat <<EOFN
-		note there must be no space between EOFN and )
-	EOFN); echo $x
-	}
-	inline_heredoc_closed() {
-		x=$(cat <<EOFN
-		note there must be no space between EOFN and )
-	EOFN
-	) 
-		echo $x 
-	} 
-	function comsub_heredoc_closed { x=$(
-		x=$(cat <<EOFN
-		note there must be no space between EOFN and )
-	EOFN); echo $x
-	); }
-	function comsub_heredoc_closed {
-		x=$(x=$(cat <<EOFN
-		note there must be no space between EOFN and )
-	EOFN
-	) ; echo $x ) 
-	} 
-	function reread_heredoc_closed { x=$((
-		x=$(cat <<EOFN
-		note there must be no space between EOFN and )
-	EOFN); echo $x
-	)|tr u x); }
-	function reread_heredoc_closed {
-		x=$(( x=$(cat <<EOFN
-		note there must be no space between EOFN and )
-	EOFN
-	) ; echo $x ) | tr u x ) 
-	} 
-	inline_heredoc_space() {
-		x=$(cat <<EOFN\ 
-		note the space between EOFN and ) is actually part of the here document marker
-	EOFN ); echo $x
-	}
-	inline_heredoc_space() {
-		x=$(cat <<EOFN\ 
-		note the space between EOFN and ) is actually part of the here document marker
-	EOFN 
-	) 
-		echo $x 
-	} 
-	function comsub_heredoc_space { x=$(
-		x=$(cat <<EOFN\ 
-		note the space between EOFN and ) is actually part of the here document marker
-	EOFN ); echo $x
-	); }
-	function comsub_heredoc_space {
-		x=$(x=$(cat <<EOFN\ 
-		note the space between EOFN and ) is actually part of the here document marker
-	EOFN 
-	) ; echo $x ) 
-	} 
-	function reread_heredoc_space { x=$((
-		x=$(cat <<EOFN\ 
-		note the space between EOFN and ) is actually part of the here document marker
-	EOFN ); echo $x
-	)|tr u x); }
-	function reread_heredoc_space {
-		x=$(( x=$(cat <<EOFN\ 
-		note the space between EOFN and ) is actually part of the here document marker
-	EOFN 
-	) ; echo $x ) | tr u x ) 
-	} 
-	inline_patch_motd() {
-		x=$(sysctl -n kern.version | sed 1q)
-		[[ -s /etc/motd && "$([[ "$(head -1 /etc/motd)" != $x ]] && \
-		    ed -s /etc/motd 2>&1 <<-EOF
-			1,/^\$/d
-			0a
-				$x
-		
-			.
-			wq
-		EOF)" = @(?) ]] && rm -f /etc/motd
-		if [[ ! -s /etc/motd ]]; then
-			install -c -o root -g wheel -m 664 /dev/null /etc/motd
-			print -- "$x\n" >/etc/motd
-		fi
-	}
-	inline_patch_motd() {
-		x=$(sysctl -n kern.version | sed 1q ) 
-		[[ -s /etc/motd && "$([[ "$(head -1 /etc/motd )" != $x ]] && ed -s /etc/motd 2>&1 <<-EOF
-	1,/^\$/d
-	0a
-	$x
-	
-	.
-	wq
-	EOF
-	)" = @(?) ]] && rm -f /etc/motd 
-		if [[ ! -s /etc/motd ]] 
-		then
-			install -c -o root -g wheel -m 664 /dev/null /etc/motd 
-			print -- "$x\n" >/etc/motd 
-		fi 
-	} 
-	function comsub_patch_motd { x=$(
-		x=$(sysctl -n kern.version | sed 1q)
-		[[ -s /etc/motd && "$([[ "$(head -1 /etc/motd)" != $x ]] && \
-		    ed -s /etc/motd 2>&1 <<-EOF
-			1,/^\$/d
-			0a
-				$x
-		
-			.
-			wq
-		EOF)" = @(?) ]] && rm -f /etc/motd
-		if [[ ! -s /etc/motd ]]; then
-			install -c -o root -g wheel -m 664 /dev/null /etc/motd
-			print -- "$x\n" >/etc/motd
-		fi
-	); }
-	function comsub_patch_motd {
-		x=$(x=$(sysctl -n kern.version | sed 1q ) ; [[ -s /etc/motd && "$([[ "$(head -1 /etc/motd )" != $x ]] && ed -s /etc/motd 2>&1 <<-EOF
-	1,/^\$/d
-	0a
-	$x
-	
-	.
-	wq
-	EOF
-	)" = @(?) ]] && rm -f /etc/motd ; if [[ ! -s /etc/motd ]] ; then install -c -o root -g wheel -m 664 /dev/null /etc/motd ; print -- "$x\n" >/etc/motd ; fi ) 
-	} 
-	function reread_patch_motd { x=$((
-		x=$(sysctl -n kern.version | sed 1q)
-		[[ -s /etc/motd && "$([[ "$(head -1 /etc/motd)" != $x ]] && \
-		    ed -s /etc/motd 2>&1 <<-EOF
-			1,/^\$/d
-			0a
-				$x
-		
-			.
-			wq
-		EOF)" = @(?) ]] && rm -f /etc/motd
-		if [[ ! -s /etc/motd ]]; then
-			install -c -o root -g wheel -m 664 /dev/null /etc/motd
-			print -- "$x\n" >/etc/motd
-		fi
-	)|tr u x); }
-	function reread_patch_motd {
-		x=$(( x=$(sysctl -n kern.version | sed 1q ) ; [[ -s /etc/motd && "$([[ "$(head -1 /etc/motd )" != $x ]] && ed -s /etc/motd 2>&1 <<-EOF
-	1,/^\$/d
-	0a
-	$x
-	
-	.
-	wq
-	EOF
-	)" = @(?) ]] && rm -f /etc/motd ; if [[ ! -s /etc/motd ]] ; then install -c -o root -g wheel -m 664 /dev/null /etc/motd ; print -- "$x\n" >/etc/motd ; fi ) | tr u x ) 
-	} 
-	inline_wdarrassign() {
-		case x in
-		x) a+=b; c+=(d e)
-		esac
-	}
-	inline_wdarrassign() {
-		case x in
-		(x)
-			a+=b 
-			set -A c+ -- d e 
-			;;
-		esac 
-	} 
-	function comsub_wdarrassign { x=$(
-		case x in
-		x) a+=b; c+=(d e)
-		esac
-	); }
-	function comsub_wdarrassign {
-		x=$(case x in (x) a+=b ; set -A c+ -- d e  ;; esac ) 
-	} 
-	function reread_wdarrassign { x=$((
-		case x in
-		x) a+=b; c+=(d e)
-		esac
-	)|tr u x); }
-	function reread_wdarrassign {
-		x=$(( case x in (x) a+=b ; set -A c+ -- d e  ;; esac ) | tr u x ) 
-	} 
----
-name: comsub-torture-io
-description:
-	Check the tree dump functions work correctly with I/O redirection
-stdin:
-	if [[ -z $__progname ]]; then echo >&2 call me with __progname; exit 1; fi
-	while IFS= read -r line; do
-		if [[ $line = '#1' ]]; then
-			lastf=0
-			continue
-		elif [[ $line = EOFN* ]]; then
-			fbody=$fbody$'\n'$line
-			continue
-		elif [[ $line != '#'* ]]; then
-			fbody=$fbody$'\n\t'$line
-			continue
-		fi
-		if (( lastf )); then
-			x="inline_${nextf}() {"$fbody$'\n}\n'
-			print -nr -- "$x"
-			print -r -- "${x}typeset -f inline_$nextf" | "$__progname"
-			x="function comsub_$nextf { x=\$("$fbody$'\n); }\n'
-			print -nr -- "$x"
-			print -r -- "${x}typeset -f comsub_$nextf" | "$__progname"
-			x="function reread_$nextf { x=\$(("$fbody$'\n)|tr u x); }\n'
-			print -nr -- "$x"
-			print -r -- "${x}typeset -f reread_$nextf" | "$__progname"
-		fi
-		lastf=1
-		fbody=
-		nextf=${line#?}
-	done <<'EOD'
-	#1
-	#TCOM
-	vara=1  varb='2  3'  cmd  arg1  $arg2  "$arg3  4" >&3
-	#TPAREN_TPIPE_TLIST
-	(echo $foo  |  tr -dc 0-9 >&3; echo >&3) >&3
-	#TAND_TOR
-	cmd  >&3 &&  >&3 echo ja  ||  echo >&3 nein
-	#TSELECT
-	select  file  in  *;  do  echo  "<$file>" ;  break >&3 ;  done >&3
-	#TFOR_TTIME
-	for  i  in  {1,2,3}  ;  do  time  >&3 echo  $i ;  done >&3
-	#TCASE
-	case  $foo  in  1)  echo eins >&3;& 2) echo zwei >&3  ;| *) echo kann net bis drei zählen >&3;;  esac >&3
-	#TIF_TBANG_TDBRACKET_TELIF
-	if  !  [[  1  =  1  ]]  >&3 ;  then  echo eins;  elif [[ 1 = 2 ]] >&3; then echo zwei  ;else echo drei; fi >&3
-	#TWHILE
-	i=1; while (( i < 10 )) >&3; do echo $i; let ++i; done >&3
-	#TUNTIL
-	i=10; until  (( !--i )) >&3 ; do echo $i; done >&3
-	#TCOPROC
-	cat  *  >&3 |&  >&3 ls
-	#TFUNCT_TBRACE_TASYNC
-	function  korn  {  echo eins; echo >&3 zwei ;  }
-	bourne  ()  {  logger *  >&3 &  }
-	#COMSUB_EXPRSUB
-	echo $(true >&3) $((1+ 2))
-	#0
-	EOD
-expected-stdout:
-	inline_TCOM() {
-		vara=1  varb='2  3'  cmd  arg1  $arg2  "$arg3  4" >&3
-	}
-	inline_TCOM() {
-		vara=1 varb="2  3" cmd arg1 $arg2 "$arg3  4" >&3 
-	} 
-	function comsub_TCOM { x=$(
-		vara=1  varb='2  3'  cmd  arg1  $arg2  "$arg3  4" >&3
-	); }
-	function comsub_TCOM {
-		x=$(vara=1 varb="2  3" cmd arg1 $arg2 "$arg3  4" >&3 ) 
-	} 
-	function reread_TCOM { x=$((
-		vara=1  varb='2  3'  cmd  arg1  $arg2  "$arg3  4" >&3
-	)|tr u x); }
-	function reread_TCOM {
-		x=$(( vara=1 varb="2  3" cmd arg1 $arg2 "$arg3  4" >&3 ) | tr u x ) 
-	} 
-	inline_TPAREN_TPIPE_TLIST() {
-		(echo $foo  |  tr -dc 0-9 >&3; echo >&3) >&3
-	}
-	inline_TPAREN_TPIPE_TLIST() {
-		( echo $foo | tr -dc 0-9 >&3 
-		  echo >&3 ) >&3 
-	} 
-	function comsub_TPAREN_TPIPE_TLIST { x=$(
-		(echo $foo  |  tr -dc 0-9 >&3; echo >&3) >&3
-	); }
-	function comsub_TPAREN_TPIPE_TLIST {
-		x=$(( echo $foo | tr -dc 0-9 >&3 ; echo >&3 ) >&3 ) 
-	} 
-	function reread_TPAREN_TPIPE_TLIST { x=$((
-		(echo $foo  |  tr -dc 0-9 >&3; echo >&3) >&3
-	)|tr u x); }
-	function reread_TPAREN_TPIPE_TLIST {
-		x=$(( ( echo $foo | tr -dc 0-9 >&3 ; echo >&3 ) >&3 ) | tr u x ) 
-	} 
-	inline_TAND_TOR() {
-		cmd  >&3 &&  >&3 echo ja  ||  echo >&3 nein
-	}
-	inline_TAND_TOR() {
-		cmd >&3 && echo ja >&3 || echo nein >&3 
-	} 
-	function comsub_TAND_TOR { x=$(
-		cmd  >&3 &&  >&3 echo ja  ||  echo >&3 nein
-	); }
-	function comsub_TAND_TOR {
-		x=$(cmd >&3 && echo ja >&3 || echo nein >&3 ) 
-	} 
-	function reread_TAND_TOR { x=$((
-		cmd  >&3 &&  >&3 echo ja  ||  echo >&3 nein
-	)|tr u x); }
-	function reread_TAND_TOR {
-		x=$(( cmd >&3 && echo ja >&3 || echo nein >&3 ) | tr u x ) 
-	} 
-	inline_TSELECT() {
-		select  file  in  *;  do  echo  "<$file>" ;  break >&3 ;  done >&3
-	}
-	inline_TSELECT() {
-		select file in * 
-		do
-			echo "<$file>" 
-			break >&3 
-		done >&3 
-	} 
-	function comsub_TSELECT { x=$(
-		select  file  in  *;  do  echo  "<$file>" ;  break >&3 ;  done >&3
-	); }
-	function comsub_TSELECT {
-		x=$(select file in * ; do echo "<$file>" ; break >&3 ; done >&3 ) 
-	} 
-	function reread_TSELECT { x=$((
-		select  file  in  *;  do  echo  "<$file>" ;  break >&3 ;  done >&3
-	)|tr u x); }
-	function reread_TSELECT {
-		x=$(( select file in * ; do echo "<$file>" ; break >&3 ; done >&3 ) | tr u x ) 
-	} 
-	inline_TFOR_TTIME() {
-		for  i  in  {1,2,3}  ;  do  time  >&3 echo  $i ;  done >&3
-	}
-	inline_TFOR_TTIME() {
-		for i in {1,2,3} 
-		do
-			time echo $i >&3 
-		done >&3 
-	} 
-	function comsub_TFOR_TTIME { x=$(
-		for  i  in  {1,2,3}  ;  do  time  >&3 echo  $i ;  done >&3
-	); }
-	function comsub_TFOR_TTIME {
-		x=$(for i in {1,2,3} ; do time echo $i >&3 ; done >&3 ) 
-	} 
-	function reread_TFOR_TTIME { x=$((
-		for  i  in  {1,2,3}  ;  do  time  >&3 echo  $i ;  done >&3
-	)|tr u x); }
-	function reread_TFOR_TTIME {
-		x=$(( for i in {1,2,3} ; do time echo $i >&3 ; done >&3 ) | tr u x ) 
-	} 
-	inline_TCASE() {
-		case  $foo  in  1)  echo eins >&3;& 2) echo zwei >&3  ;| *) echo kann net bis drei zählen >&3;;  esac >&3
-	}
-	inline_TCASE() {
-		case $foo in
-		(1)
-			echo eins >&3 
-			;&
-		(2)
-			echo zwei >&3 
-			;|
-		(*)
-			echo kann net bis drei zählen >&3 
-			;;
-		esac >&3 
-	} 
-	function comsub_TCASE { x=$(
-		case  $foo  in  1)  echo eins >&3;& 2) echo zwei >&3  ;| *) echo kann net bis drei zählen >&3;;  esac >&3
-	); }
-	function comsub_TCASE {
-		x=$(case $foo in (1) echo eins >&3  ;& (2) echo zwei >&3  ;| (*) echo kann net bis drei zählen >&3  ;; esac >&3 ) 
-	} 
-	function reread_TCASE { x=$((
-		case  $foo  in  1)  echo eins >&3;& 2) echo zwei >&3  ;| *) echo kann net bis drei zählen >&3;;  esac >&3
-	)|tr u x); }
-	function reread_TCASE {
-		x=$(( case $foo in (1) echo eins >&3  ;& (2) echo zwei >&3  ;| (*) echo kann net bis drei zählen >&3  ;; esac >&3 ) | tr u x ) 
-	} 
-	inline_TIF_TBANG_TDBRACKET_TELIF() {
-		if  !  [[  1  =  1  ]]  >&3 ;  then  echo eins;  elif [[ 1 = 2 ]] >&3; then echo zwei  ;else echo drei; fi >&3
-	}
-	inline_TIF_TBANG_TDBRACKET_TELIF() {
-		if ! [[ 1 = 1 ]] >&3 
-		then
-			echo eins 
-		elif [[ 1 = 2 ]] >&3 
-		then
-			echo zwei 
-		else
-			echo drei 
-		fi >&3 
-	} 
-	function comsub_TIF_TBANG_TDBRACKET_TELIF { x=$(
-		if  !  [[  1  =  1  ]]  >&3 ;  then  echo eins;  elif [[ 1 = 2 ]] >&3; then echo zwei  ;else echo drei; fi >&3
-	); }
-	function comsub_TIF_TBANG_TDBRACKET_TELIF {
-		x=$(if ! [[ 1 = 1 ]] >&3 ; then echo eins ; elif [[ 1 = 2 ]] >&3 ; then echo zwei ; else echo drei ; fi >&3 ) 
-	} 
-	function reread_TIF_TBANG_TDBRACKET_TELIF { x=$((
-		if  !  [[  1  =  1  ]]  >&3 ;  then  echo eins;  elif [[ 1 = 2 ]] >&3; then echo zwei  ;else echo drei; fi >&3
-	)|tr u x); }
-	function reread_TIF_TBANG_TDBRACKET_TELIF {
-		x=$(( if ! [[ 1 = 1 ]] >&3 ; then echo eins ; elif [[ 1 = 2 ]] >&3 ; then echo zwei ; else echo drei ; fi >&3 ) | tr u x ) 
-	} 
-	inline_TWHILE() {
-		i=1; while (( i < 10 )) >&3; do echo $i; let ++i; done >&3
-	}
-	inline_TWHILE() {
-		i=1 
-		while let] " i < 10 " >&3 
-		do
-			echo $i 
-			let ++i 
-		done >&3 
-	} 
-	function comsub_TWHILE { x=$(
-		i=1; while (( i < 10 )) >&3; do echo $i; let ++i; done >&3
-	); }
-	function comsub_TWHILE {
-		x=$(i=1 ; while let] " i < 10 " >&3 ; do echo $i ; let ++i ; done >&3 ) 
-	} 
-	function reread_TWHILE { x=$((
-		i=1; while (( i < 10 )) >&3; do echo $i; let ++i; done >&3
-	)|tr u x); }
-	function reread_TWHILE {
-		x=$(( i=1 ; while let] " i < 10 " >&3 ; do echo $i ; let ++i ; done >&3 ) | tr u x ) 
-	} 
-	inline_TUNTIL() {
-		i=10; until  (( !--i )) >&3 ; do echo $i; done >&3
-	}
-	inline_TUNTIL() {
-		i=10 
-		until let] " !--i " >&3 
-		do
-			echo $i 
-		done >&3 
-	} 
-	function comsub_TUNTIL { x=$(
-		i=10; until  (( !--i )) >&3 ; do echo $i; done >&3
-	); }
-	function comsub_TUNTIL {
-		x=$(i=10 ; until let] " !--i " >&3 ; do echo $i ; done >&3 ) 
-	} 
-	function reread_TUNTIL { x=$((
-		i=10; until  (( !--i )) >&3 ; do echo $i; done >&3
-	)|tr u x); }
-	function reread_TUNTIL {
-		x=$(( i=10 ; until let] " !--i " >&3 ; do echo $i ; done >&3 ) | tr u x ) 
-	} 
-	inline_TCOPROC() {
-		cat  *  >&3 |&  >&3 ls
-	}
-	inline_TCOPROC() {
-		cat * >&3 |& 
-		ls >&3 
-	} 
-	function comsub_TCOPROC { x=$(
-		cat  *  >&3 |&  >&3 ls
-	); }
-	function comsub_TCOPROC {
-		x=$(cat * >&3 |&  ls >&3 ) 
-	} 
-	function reread_TCOPROC { x=$((
-		cat  *  >&3 |&  >&3 ls
-	)|tr u x); }
-	function reread_TCOPROC {
-		x=$(( cat * >&3 |&  ls >&3 ) | tr u x ) 
-	} 
-	inline_TFUNCT_TBRACE_TASYNC() {
-		function  korn  {  echo eins; echo >&3 zwei ;  }
-		bourne  ()  {  logger *  >&3 &  }
-	}
-	inline_TFUNCT_TBRACE_TASYNC() {
-		function korn {
-			echo eins 
-			echo zwei >&3 
-		} 
-		bourne() {
-			logger * >&3 & 
-		} 
-	} 
-	function comsub_TFUNCT_TBRACE_TASYNC { x=$(
-		function  korn  {  echo eins; echo >&3 zwei ;  }
-		bourne  ()  {  logger *  >&3 &  }
-	); }
-	function comsub_TFUNCT_TBRACE_TASYNC {
-		x=$(function korn { echo eins ; echo zwei >&3 ; } ; bourne() { logger * >&3 &  } ) 
-	} 
-	function reread_TFUNCT_TBRACE_TASYNC { x=$((
-		function  korn  {  echo eins; echo >&3 zwei ;  }
-		bourne  ()  {  logger *  >&3 &  }
-	)|tr u x); }
-	function reread_TFUNCT_TBRACE_TASYNC {
-		x=$(( function korn { echo eins ; echo zwei >&3 ; } ; bourne() { logger * >&3 &  } ) | tr u x ) 
-	} 
-	inline_COMSUB_EXPRSUB() {
-		echo $(true >&3) $((1+ 2))
-	}
-	inline_COMSUB_EXPRSUB() {
-		echo $(true >&3 ) $((1+ 2)) 
-	} 
-	function comsub_COMSUB_EXPRSUB { x=$(
-		echo $(true >&3) $((1+ 2))
-	); }
-	function comsub_COMSUB_EXPRSUB {
-		x=$(echo $(true >&3 ) $((1+ 2)) ) 
-	} 
-	function reread_COMSUB_EXPRSUB { x=$((
-		echo $(true >&3) $((1+ 2))
-	)|tr u x); }
-	function reread_COMSUB_EXPRSUB {
-		x=$(( echo $(true >&3 ) $((1+ 2)) ) | tr u x ) 
-	} 
----
-name: funsub-1
-description:
-	Check that non-subenvironment command substitution works
-stdin:
-	set -e
-	foo=bar
-	echo "ob $foo ."
-	echo "${
-		echo "ib $foo :"
-		foo=baz
-		echo "ia $foo :"
-		false
-	}" .
-	echo "oa $foo ."
-expected-stdout:
-	ob bar .
-	ib bar :
-	ia baz : .
-	oa baz .
----
-name: funsub-2
-description:
-	You can now reliably use local and return in funsubs
-	(not exit though)
-stdin:
-	x=q; e=1; x=${ echo a; e=2; echo x$e;}; echo 1:y$x,$e,$?.
-	x=q; e=1; x=${ echo a; typeset e=2; echo x$e;}; echo 2:y$x,$e,$?.
-	x=q; e=1; x=${ echo a; typeset e=2; return 3; echo x$e;}; echo 3:y$x,$e,$?.
-expected-stdout:
-	1:ya x2,2,0.
-	2:ya x2,1,0.
-	3:ya,1,3.
----
-name: valsub-1
-description:
-	Check that "value substitutions" work as advertised
-stdin:
-	x=1
-	y=2
-	z=3
-	REPLY=4
-	echo "before:	x<$x> y<$y> z<$z> R<$REPLY>"
-	x=${|
-		local y
-		echo "begin:	x<$x> y<$y> z<$z> R<$REPLY>"
-		x=5
-		y=6
-		z=7
-		REPLY=8
-		echo "end:	x<$x> y<$y> z<$z> R<$REPLY>"
-	}
-	echo "after:	x<$x> y<$y> z<$z> R<$REPLY>"
-	# ensure trailing newlines are kept
-	t=${|REPLY=$'foo\n\n';}
-	typeset -p t
-	echo -n this used to segfault
-	echo ${|true;}$(true).
-expected-stdout:
-	before:	x<1> y<2> z<3> R<4>
-	begin:	x<1> y<> z<3> R<>
-	end:	x<5> y<6> z<7> R<8>
-	after:	x<8> y<2> z<7> R<4>
-	typeset t=$'foo\n\n'
-	this used to segfault.
----
-name: test-stnze-1
-description:
-	Check that the short form [ $x ] works
-stdin:
-	i=0
-	[ -n $x ]
-	rv=$?; echo $((++i)) $rv
-	[ $x ]
-	rv=$?; echo $((++i)) $rv
-	[ -n "$x" ]
-	rv=$?; echo $((++i)) $rv
-	[ "$x" ]
-	rv=$?; echo $((++i)) $rv
-	x=0
-	[ -n $x ]
-	rv=$?; echo $((++i)) $rv
-	[ $x ]
-	rv=$?; echo $((++i)) $rv
-	[ -n "$x" ]
-	rv=$?; echo $((++i)) $rv
-	[ "$x" ]
-	rv=$?; echo $((++i)) $rv
-	x='1 -a 1 = 2'
-	[ -n $x ]
-	rv=$?; echo $((++i)) $rv
-	[ $x ]
-	rv=$?; echo $((++i)) $rv
-	[ -n "$x" ]
-	rv=$?; echo $((++i)) $rv
-	[ "$x" ]
-	rv=$?; echo $((++i)) $rv
-expected-stdout:
-	1 0
-	2 1
-	3 1
-	4 1
-	5 0
-	6 0
-	7 0
-	8 0
-	9 1
-	10 1
-	11 0
-	12 0
----
-name: test-stnze-2
-description:
-	Check that the short form [[ $x ]] works (ksh93 extension)
-stdin:
-	i=0
-	[[ -n $x ]]
-	rv=$?; echo $((++i)) $rv
-	[[ $x ]]
-	rv=$?; echo $((++i)) $rv
-	[[ -n "$x" ]]
-	rv=$?; echo $((++i)) $rv
-	[[ "$x" ]]
-	rv=$?; echo $((++i)) $rv
-	x=0
-	[[ -n $x ]]
-	rv=$?; echo $((++i)) $rv
-	[[ $x ]]
-	rv=$?; echo $((++i)) $rv
-	[[ -n "$x" ]]
-	rv=$?; echo $((++i)) $rv
-	[[ "$x" ]]
-	rv=$?; echo $((++i)) $rv
-	x='1 -a 1 = 2'
-	[[ -n $x ]]
-	rv=$?; echo $((++i)) $rv
-	[[ $x ]]
-	rv=$?; echo $((++i)) $rv
-	[[ -n "$x" ]]
-	rv=$?; echo $((++i)) $rv
-	[[ "$x" ]]
-	rv=$?; echo $((++i)) $rv
-expected-stdout:
-	1 1
-	2 1
-	3 1
-	4 1
-	5 0
-	6 0
-	7 0
-	8 0
-	9 0
-	10 0
-	11 0
-	12 0
----
-name: event-subst-3
-description:
-	Check that '!' substitution in noninteractive mode is ignored
-file-setup: file 755 "falsetto"
-	#! /bin/sh
-	echo molto bene
-	exit 42
-file-setup: file 755 "!false"
-	#! /bin/sh
-	echo si
-stdin:
-	export PATH=.:$PATH
-	falsetto
-	echo yeap
-	!false
-	echo meow
-	! false
-	echo = $?
-	if
-	! false; then echo foo; else echo bar; fi
-expected-stdout:
-	molto bene
-	yeap
-	si
-	meow
-	= 0
-	foo
----
-name: event-subst-0
-description:
-	Check that '!' substitution in interactive mode is ignored
-need-ctty: yes
-arguments: !-i!
-file-setup: file 755 "falsetto"
-	#! /bin/sh
-	echo molto bene
-	exit 42
-file-setup: file 755 "!false"
-	#! /bin/sh
-	echo si
-stdin:
-	export PATH=.:$PATH
-	falsetto
-	echo yeap
-	!false
-	echo meow
-	! false
-	echo = $?
-	if
-	! false; then echo foo; else echo bar; fi
-expected-stdout:
-	molto bene
-	yeap
-	si
-	meow
-	= 0
-	foo
-expected-stderr-pattern:
-	/.*/
----
-name: nounset-1
-description:
-	Check that "set -u" matches (future) SUSv4 requirement
-stdin:
-	(set -u
-	try() {
-		local v
-		eval v=\$$1
-		if [[ -n $v ]]; then
-			echo $1=nz
-		else
-			echo $1=zf
-		fi
-	}
-	x=y
-	(echo $x)
-	echo =1
-	(echo $y)
-	echo =2
-	(try x)
-	echo =3
-	(try y)
-	echo =4
-	(try 0)
-	echo =5
-	(try 2)
-	echo =6
-	(try)
-	echo =7
-	(echo at=$@)
-	echo =8
-	(echo asterisk=$*)
-	echo =9
-	(echo $?)
-	echo =10
-	(echo $!)
-	echo =11
-	(echo $-)
-	echo =12
-	#(echo $_)
-	#echo =13
-	(echo $#)
-	echo =14
-	(mypid=$$; try mypid)
-	echo =15
-	) 2>&1 | sed -e 's/^[^]]*]//' -e 's/^[^:]*: *//'
-expected-stdout:
-	y
-	=1
-	y: parameter not set
-	=2
-	x=nz
-	=3
-	y: parameter not set
-	=4
-	0=nz
-	=5
-	2: parameter not set
-	=6
-	1: parameter not set
-	=7
-	at=
-	=8
-	asterisk=
-	=9
-	0
-	=10
-	!: parameter not set
-	=11
-	ush
-	=12
-	0
-	=14
-	mypid=nz
-	=15
----
-name: nameref-1
-description:
-	Testsuite for nameref (bound variables)
-stdin:
-	bar=global
-	typeset -n ir2=bar
-	typeset -n ind=ir2
-	echo !ind: ${!ind}
-	echo ind: $ind
-	echo !ir2: ${!ir2}
-	echo ir2: $ir2
-	typeset +n ind
-	echo !ind: ${!ind}
-	echo ind: $ind
-	typeset -n ir2=ind
-	echo !ir2: ${!ir2}
-	echo ir2: $ir2
-	set|grep ^ir2|sed 's/^/s1: /'
-	typeset|grep ' ir2'|sed -e 's/^/s2: /' -e 's/nameref/typeset -n/'
-	set -A blub -- e1 e2 e3
-	typeset -n ind=blub
-	typeset -n ir2=blub[2]
-	echo !ind[1]: ${!ind[1]}
-	echo !ir2: $!ir2
-	echo ind[1]: ${ind[1]}
-	echo ir2: $ir2
-expected-stdout:
-	!ind: bar
-	ind: global
-	!ir2: bar
-	ir2: global
-	!ind: ind
-	ind: ir2
-	!ir2: ind
-	ir2: ir2
-	s1: ir2=ind
-	s2: typeset -n ir2
-	!ind[1]: 1
-	!ir2: ir2
-	ind[1]: e2
-	ir2: e3
----
-name: nameref-2da
-description:
-	Testsuite for nameref (bound variables)
-	Functions, argument given directly, after local
-stdin:
-	function foo {
-		typeset bar=lokal baz=auch
-		typeset -n v=bar
-		echo entering
-		echo !v: ${!v}
-		echo !bar: ${!bar}
-		echo !baz: ${!baz}
-		echo bar: $bar
-		echo v: $v
-		v=123
-		echo bar: $bar
-		echo v: $v
-		echo exiting
-	}
-	bar=global
-	echo bar: $bar
-	foo bar
-	echo bar: $bar
-expected-stdout:
-	bar: global
-	entering
-	!v: bar
-	!bar: bar
-	!baz: baz
-	bar: lokal
-	v: lokal
-	bar: 123
-	v: 123
-	exiting
-	bar: global
----
-name: nameref-3
-description:
-	Advanced testsuite for bound variables (ksh93 fails this)
-stdin:
-	typeset -n foo=bar[i]
-	set -A bar -- b c a
-	for i in 0 1 2 3; do
-		print $i $foo .
-	done
-expected-stdout:
-	0 b .
-	1 c .
-	2 a .
-	3 .
----
-name: nameref-4
-description:
-	Ensure we don't run in an infinite loop
-time-limit: 3
-stdin:
-	baz() {
-		typeset -n foo=fnord fnord=foo
-		foo[0]=bar
-	}
-	set -A foo bad
-	echo sind $foo .
-	baz
-	echo blah $foo .
-expected-stdout:
-	sind bad .
-	blah bad .
-expected-stderr-pattern:
-	/fnord: expression recurses on parameter/
----
-name: better-parens-1a
-description:
-	Check support for ((…)) and $((…)) vs (…) and $(…)
-stdin:
-	if ( (echo fubar)|tr u x); then
-		echo ja
-	else
-		echo nein
-	fi
-expected-stdout:
-	fxbar
-	ja
----
-name: better-parens-1b
-description:
-	Check support for ((…)) and $((…)) vs (…) and $(…)
-stdin:
-	echo $( (echo fubar)|tr u x) $?
-expected-stdout:
-	fxbar 0
----
-name: better-parens-1c
-description:
-	Check support for ((…)) and $((…)) vs (…) and $(…)
-stdin:
-	x=$( (echo fubar)|tr u x); echo $x $?
-expected-stdout:
-	fxbar 0
----
-name: better-parens-2a
-description:
-	Check support for ((…)) and $((…)) vs (…) and $(…)
-stdin:
-	if ((echo fubar)|tr u x); then
-		echo ja
-	else
-		echo nein
-	fi
-expected-stdout:
-	fxbar
-	ja
----
-name: better-parens-2b
-description:
-	Check support for ((…)) and $((…)) vs (…) and $(…)
-stdin:
-	echo $((echo fubar)|tr u x) $?
-expected-stdout:
-	fxbar 0
----
-name: better-parens-2c
-description:
-	Check support for ((…)) and $((…)) vs (…) and $(…)
-stdin:
-	x=$((echo fubar)|tr u x); echo $x $?
-expected-stdout:
-	fxbar 0
----
-name: better-parens-3a
-description:
-	Check support for ((…)) and $((…)) vs (…) and $(…)
-stdin:
-	if ( (echo fubar)|(tr u x)); then
-		echo ja
-	else
-		echo nein
-	fi
-expected-stdout:
-	fxbar
-	ja
----
-name: better-parens-3b
-description:
-	Check support for ((…)) and $((…)) vs (…) and $(…)
-stdin:
-	echo $( (echo fubar)|(tr u x)) $?
-expected-stdout:
-	fxbar 0
----
-name: better-parens-3c
-description:
-	Check support for ((…)) and $((…)) vs (…) and $(…)
-stdin:
-	x=$( (echo fubar)|(tr u x)); echo $x $?
-expected-stdout:
-	fxbar 0
----
-name: better-parens-4a
-description:
-	Check support for ((…)) and $((…)) vs (…) and $(…)
-stdin:
-	if ((echo fubar)|(tr u x)); then
-		echo ja
-	else
-		echo nein
-	fi
-expected-stdout:
-	fxbar
-	ja
----
-name: better-parens-4b
-description:
-	Check support for ((…)) and $((…)) vs (…) and $(…)
-stdin:
-	echo $((echo fubar)|(tr u x)) $?
-expected-stdout:
-	fxbar 0
----
-name: better-parens-4c
-description:
-	Check support for ((…)) and $((…)) vs (…) and $(…)
-stdin:
-	x=$((echo fubar)|(tr u x)); echo $x $?
-expected-stdout:
-	fxbar 0
----
-name: echo-test-1
-description:
-	Test what the echo builtin does (mksh)
-stdin:
-	echo -n 'foo\x40bar'
-	echo -e '\tbaz'
-expected-stdout:
-	foo at bar	baz
----
-name: echo-test-2
-description:
-	Test what the echo builtin does (POSIX)
-	Note: this follows Debian Policy 10.4 which mandates
-	that -n shall be treated as an option, not XSI which
-	mandates it shall be treated as string but escapes
-	shall be expanded.
-stdin:
-	test -n "$POSH_VERSION" || set -o posix
-	echo -n 'foo\x40bar'
-	echo -e '\tbaz'
-expected-stdout:
-	foo\x40bar-e \tbaz
----
-name: echo-test-3-mnbsd
-description:
-	Test what the echo builtin does, and test a compatibility flag.
-category: mnbsdash
-stdin:
-	"$__progname" -c 'echo -n 1=\\x40$1; echo -e \\x2E' -- foo bar
-	"$__progname" -o posix -c 'echo -n 2=\\x40$1; echo -e \\x2E' -- foo bar
-	"$__progname" -o sh -c 'echo -n 3=\\x40$1; echo -e \\x2E' -- foo bar
-expected-stdout:
-	1=@foo.
-	2=\x40foo-e \x2E
-	3=\x40bar.
----
-name: echo-test-3-normal
-description:
-	Test what the echo builtin does, and test a compatibility flag.
-category: !mnbsdash
-stdin:
-	"$__progname" -c 'echo -n 1=\\x40$1; echo -e \\x2E' -- foo bar
-	"$__progname" -o posix -c 'echo -n 2=\\x40$1; echo -e \\x2E' -- foo bar
-	"$__progname" -o sh -c 'echo -n 3=\\x40$1; echo -e \\x2E' -- foo bar
-expected-stdout:
-	1=@foo.
-	2=\x40foo-e \x2E
-	3=\x40foo-e \x2E
----
-name: utilities-getopts-1
-description:
-	getopts sets OPTIND correctly for unparsed option
-stdin:
-	set -- -a -a -x
-	while getopts :a optc; do
-	    echo "OPTARG=$OPTARG, OPTIND=$OPTIND, optc=$optc."
-	done
-	echo done
-expected-stdout:
-	OPTARG=, OPTIND=2, optc=a.
-	OPTARG=, OPTIND=3, optc=a.
-	OPTARG=x, OPTIND=4, optc=?.
-	done
----
-name: utilities-getopts-2
-description:
-	Check OPTARG
-stdin:
-	set -- -a Mary -x
-	while getopts a: optc; do
-	    echo "OPTARG=$OPTARG, OPTIND=$OPTIND, optc=$optc."
-	done
-	echo done
-expected-stdout:
-	OPTARG=Mary, OPTIND=3, optc=a.
-	OPTARG=, OPTIND=4, optc=?.
-	done
-expected-stderr-pattern: /.*-x.*option/
----
-name: wcswidth-1
-description:
-	Check the new wcswidth feature
-stdin:
-	s=何
-	set +U
-	print octets: ${#s} .
-	print 8-bit width: ${%s} .
-	set -U
-	print characters: ${#s} .
-	print columns: ${%s} .
-	s=�
-	set +U
-	print octets: ${#s} .
-	print 8-bit width: ${%s} .
-	set -U
-	print characters: ${#s} .
-	print columns: ${%s} .
-expected-stdout:
-	octets: 3 .
-	8-bit width: -1 .
-	characters: 1 .
-	columns: 2 .
-	octets: 3 .
-	8-bit width: 3 .
-	characters: 1 .
-	columns: 1 .
----
-name: wcswidth-2
-description:
-	Check some corner cases
-stdin:
-	print % $% .
-	set -U
-	x='a	b'
-	print c ${%x} .
-	set +U
-	x='a	b'
-	print d ${%x} .
-expected-stdout:
-	% $% .
-	c -1 .
-	d -1 .
----
-name: wcswidth-3
-description:
-	Check some corner cases
-stdin:
-	print ${%} .
-expected-stderr-pattern:
-	/bad substitution/
-expected-exit: 1
----
-name: wcswidth-4a
-description:
-	Check some corner cases
-stdin:
-	print ${%*} .
-expected-stderr-pattern:
-	/bad substitution/
-expected-exit: 1
----
-name: wcswidth-4b
-description:
-	Check some corner cases
-stdin:
-	print ${%@} .
-expected-stderr-pattern:
-	/bad substitution/
-expected-exit: 1
----
-name: wcswidth-4c
-description:
-	Check some corner cases
-stdin:
-	:
-	print ${%?} .
-expected-stdout:
-	1 .
----
-name: realpath-1
-description:
-	Check proper return values for realpath
-category: os:mirbsd
-stdin:
-	wd=$(realpath .)
-	mkdir dir
-	:>file
-	:>dir/file
-	ln -s dir lndir
-	ln -s file lnfile
-	ln -s nix lnnix
-	ln -s . lnself
-	i=0
-	chk() {
-		typeset x y
-		x=$(realpath "$wd/$1" 2>&1); y=$?
-		print $((++i)) "?$1" =${x##*$wd/} !$y
-	}
-	chk dir
-	chk dir/
-	chk dir/file
-	chk dir/nix
-	chk file
-	chk file/
-	chk file/file
-	chk file/nix
-	chk nix
-	chk nix/
-	chk nix/file
-	chk nix/nix
-	chk lndir
-	chk lndir/
-	chk lndir/file
-	chk lndir/nix
-	chk lnfile
-	chk lnfile/
-	chk lnfile/file
-	chk lnfile/nix
-	chk lnnix
-	chk lnnix/
-	chk lnnix/file
-	chk lnnix/nix
-	chk lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself
-	rm lnself
-expected-stdout:
-	1 ?dir =dir !0
-	2 ?dir/ =dir !0
-	3 ?dir/file =dir/file !0
-	4 ?dir/nix =dir/nix !0
-	5 ?file =file !0
-	6 ?file/ =file/: Not a directory !20
-	7 ?file/file =file/file: Not a directory !20
-	8 ?file/nix =file/nix: Not a directory !20
-	9 ?nix =nix !0
-	10 ?nix/ =nix !0
-	11 ?nix/file =nix/file: No such file or directory !2
-	12 ?nix/nix =nix/nix: No such file or directory !2
-	13 ?lndir =dir !0
-	14 ?lndir/ =dir !0
-	15 ?lndir/file =dir/file !0
-	16 ?lndir/nix =dir/nix !0
-	17 ?lnfile =file !0
-	18 ?lnfile/ =lnfile/: Not a directory !20
-	19 ?lnfile/file =lnfile/file: Not a directory !20
-	20 ?lnfile/nix =lnfile/nix: Not a directory !20
-	21 ?lnnix =nix !0
-	22 ?lnnix/ =nix !0
-	23 ?lnnix/file =lnnix/file: No such file or directory !2
-	24 ?lnnix/nix =lnnix/nix: No such file or directory !2
-	25 ?lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself =lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself: Too many levels of symbolic links !62
----
-name: realpath-2
-description:
-	Ensure that exactly two leading slashes are not collapsed
-	POSIX guarantees this exception, e.g. for UNC paths on Cygwin
-category: os:mirbsd
-stdin:
-	ln -s /bin t1
-	ln -s //bin t2
-	ln -s ///bin t3
-	realpath /bin
-	realpath //bin
-	realpath ///bin
-	realpath /usr/bin
-	realpath /usr//bin
-	realpath /usr///bin
-	realpath t1
-	realpath t2
-	realpath t3
-	rm -f t1 t2 t3
-	cd //usr/bin
-	pwd
-	cd ../lib
-	pwd
-	realpath //usr/include/../bin
-expected-stdout:
-	/bin
-	//bin
-	/bin
-	/usr/bin
-	/usr/bin
-	/usr/bin
-	/bin
-	//bin
-	/bin
-	//usr/bin
-	//usr/lib
-	//usr/bin
----
-name: crash-1
-description:
-	Crashed during March 2011, fixed on vernal equinōx ☺
-category: os:mirbsd,os:openbsd
-stdin:
-	export MALLOC_OPTIONS=FGJPRSX
-	"$__progname" -c 'x=$(tr z r <<<baz); echo $x'
-expected-stdout:
-	bar
----
-name: debian-117-1
-description:
-	Check test - bug#465250
-stdin:
-	test \( ! -e \) ; echo $?
-expected-stdout:
-	1
----
-name: debian-117-2
-description:
-	Check test - bug#465250
-stdin:
-	test \(  -e \) ; echo $?
-expected-stdout:
-	0
----
-name: debian-117-3
-description:
-	Check test - bug#465250
-stdin:
-	test ! -e  ; echo $?
-expected-stdout:
-	1
----
-name: debian-117-4
-description:
-	Check test - bug#465250
-stdin:
-	test  -e  ; echo $?
-expected-stdout:
-	0
----
-name: case-zsh
-description:
-	Check that zsh case variants work
-stdin:
-	case 'b' in
-	  a) echo a ;;
-	  b) echo b ;;
-	  c) echo c ;;
-	  *) echo x ;;
-	esac
-	echo =
-	case 'b' in
-	  a) echo a ;&
-	  b) echo b ;&
-	  c) echo c ;&
-	  *) echo x ;&
-	esac
-	echo =
-	case 'b' in
-	  a) echo a ;|
-	  b) echo b ;|
-	  c) echo c ;|
-	  *) echo x ;|
-	esac
-expected-stdout:
-	b
-	=
-	b
-	c
-	x
-	=
-	b
-	x
----
-name: case-braces
-description:
-	Check that case end tokens are not mixed up (Debian #220272)
-stdin:
-	i=0
-	for value in 'x' '}' 'esac'; do
-		print -n "$((++i))($value)bourne "
-		case $value in
-		}) echo brace ;;
-		*) echo no ;;
-		esac
-		print -n "$((++i))($value)korn "
-		case $value {
-		esac) echo esac ;;
-		*) echo no ;;
-		}
-	done
-expected-stdout:
-	1(x)bourne no
-	2(x)korn no
-	3(})bourne brace
-	4(})korn no
-	5(esac)bourne no
-	6(esac)korn esac
----
-name: command-shift
-description:
-	Check that 'command shift' works
-stdin:
-	function snc {
-		echo "before	0='$0' 1='$1' 2='$2'"
-		shift
-		echo "after	0='$0' 1='$1' 2='$2'"
-	}
-	function swc {
-		echo "before	0='$0' 1='$1' 2='$2'"
-		command shift
-		echo "after	0='$0' 1='$1' 2='$2'"
-	}
-	echo = without command
-	snc 一 二
-	echo = with command
-	swc 一 二
-	echo = done
-expected-stdout:
-	= without command
-	before	0='snc' 1='一' 2='二'
-	after	0='snc' 1='二' 2=''
-	= with command
-	before	0='swc' 1='一' 2='二'
-	after	0='swc' 1='二' 2=''
-	= done
----
-name: duffs-device
-description:
-	Check that the compiler did not optimise-break them
-	(lex.c has got a similar one in SHEREDELIM)
-stdin:
-	set +U
-	s=
-	typeset -i1 i=0
-	while (( ++i < 256 )); do
-		s+=${i#1#}
-	done
-	s+=$'\xC2\xA0\xE2\x82\xAC\xEF\xBF\xBD\xEF\xBF\xBE\xEF\xBF\xBF\xF0\x90\x80\x80.'
-	typeset -p s
-expected-stdout:
-	typeset s=$'\001\002\003\004\005\006\a\b\t\n\v\f\r\016\017\020\021\022\023\024\025\026\027\030\031\032\E\034\035\036\037 !"#$%&\047()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\177\200\201\202\203\204\205\206\207\210\211\212\213\214\215\216\217\220\221\222\223\224\225\226\227\230\231\232\233\234\235\236\237\240\241\242\243\244\245\246\247\250\251\252\253\254\255\256\257\260\261\262\263\264\265\266\267\270\271\272\273\274\275\276\277\300\301\302\303\304\305\306\307\310\311\312\313\314\315\316\317\320\321\322\323\324\325\326\327\330\331\332\333\334\335\336\337\340\341\342\343\344\345\346\347\350\351\352\353\354\355\356\357\360\361\362\363\364\365\366\367\370\371\372\373\374\375\376\377\u00A0\u20AC\uFFFD\357\277\276\357\277\277\360\220\200\200.'
----
-name: stateptr-underflow
-description:
-	This check overflows an Xrestpos stored in a short in R40
-category: fastbox
-stdin:
-	function Lb64decode {
-		[[ -o utf8-mode ]]; local u=$?
-		set +U
-		local c s="$*" t=
-		[[ -n $s ]] || { s=$(cat;print x); s=${s%x}; }
-		local -i i=0 n=${#s} p=0 v x
-		local -i16 o
-	
-		while (( i < n )); do
-			c=${s:(i++):1}
-			case $c {
-			(=)	break ;;
-			([A-Z])	(( v = 1#$c - 65 )) ;;
-			([a-z])	(( v = 1#$c - 71 )) ;;
-			([0-9])	(( v = 1#$c + 4 )) ;;
-			(+)	v=62 ;;
-			(/)	v=63 ;;
-			(*)	continue ;;
-			}
-			(( x = (x << 6) | v ))
-			case $((p++)) {
-			(0)	continue ;;
-			(1)	(( o = (x >> 4) & 255 )) ;;
-			(2)	(( o = (x >> 2) & 255 )) ;;
-			(3)	(( o = x & 255 ))
-				p=0
-				;;
-			}
-			t=$t\\x${o#16#}
-		done
-		print -n $t
-		(( u )) || set -U
-	}
-	
-	i=-1
-	s=
-	while (( ++i < 12120 )); do
-		s+=a
-	done
-	Lb64decode $s >/dev/null
----
-name: xtrace-1
-description:
-	Check that "set -x" doesn't redirect too quickly
-stdin:
-	print '#!'"$__progname" >bash
-	cat >>bash <<'EOF'
-	echo 'GNU bash, version 2.05b.0(1)-release (i386-ecce-mirbsd10)
-	Copyright (C) 2002 Free Software Foundation, Inc.'
-	EOF
-	chmod +x bash
-	"$__progname" -xc 'foo=$(./bash --version 2>&1 | head -1); echo "=$foo="'
-expected-stdout:
-	=GNU bash, version 2.05b.0(1)-release (i386-ecce-mirbsd10)=
-expected-stderr-pattern:
-	/.*/
----

Copied: vendor/MirOS/mksh/R50/check.t (from rev 6707, vendor/MirOS/mksh/dist/check.t)
===================================================================
--- vendor/MirOS/mksh/R50/check.t	                        (rev 0)
+++ vendor/MirOS/mksh/R50/check.t	2014-07-01 12:01:17 UTC (rev 6708)
@@ -0,0 +1,11252 @@
+# $MirOS: src/bin/mksh/check.t,v 1.654 2014/06/29 11:28:26 tg Exp $
+# OpenBSD src/regress/bin/ksh updated: 2013/12/02 20:39:44
+#-
+# Copyright © 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
+#	      2011, 2012, 2013, 2014
+#	Thorsten Glaser <tg at mirbsd.org>
+#
+# Provided that these terms and disclaimer and all copyright notices
+# are retained or reproduced in an accompanying document, permission
+# is granted to deal in this work without restriction, including un‐
+# limited rights to use, publicly perform, distribute, sell, modify,
+# merge, give away, or sublicence.
+#
+# This work is provided “AS IS” and WITHOUT WARRANTY of any kind, to
+# the utmost extent permitted by applicable law, neither express nor
+# implied; without malicious intent or gross negligence. In no event
+# may a licensor, author or contributor be held liable for indirect,
+# direct, other damage, loss, or other issues arising in any way out
+# of dealing in the work, even if advised of the possibility of such
+# damage or existence of a defect, except proven that it results out
+# of said person’s immediate fault when using the work as intended.
+#-
+# You may also want to test IFS with the script at
+# http://www.research.att.com/~gsf/public/ifs.sh
+#
+# More testsuites at:
+# http://www.freebsd.org/cgi/cvsweb.cgi/src/tools/regression/bin/test/regress.sh?rev=HEAD
+
+expected-stdout:
+	@(#)MIRBSD KSH R50 2014/06/29
+description:
+	Check version of shell.
+stdin:
+	echo $KSH_VERSION
+name: KSH_VERSION
+category: shell:legacy-no
+---
+expected-stdout:
+	@(#)LEGACY KSH R50 2014/06/29
+description:
+	Check version of legacy shell.
+stdin:
+	echo $KSH_VERSION
+name: KSH_VERSION-legacy
+category: shell:legacy-yes
+---
+name: selftest-1
+description:
+	Regression test self-testing
+stdin:
+	echo ${foo:-baz}
+expected-stdout:
+	baz
+---
+name: selftest-2
+description:
+	Regression test self-testing
+env-setup: !foo=bar!
+stdin:
+	echo ${foo:-baz}
+expected-stdout:
+	bar
+---
+name: selftest-3
+description:
+	Regression test self-testing
+env-setup: !ENV=fnord!
+stdin:
+	echo "<$ENV>"
+expected-stdout:
+	<fnord>
+---
+name: selftest-exec
+description:
+	Ensure that the test run directory (default /tmp but can be changed
+	with check.pl flag -T or test.sh $TMPDIR) is not mounted noexec, as
+	we execute scripts from the scratch directory during several tests.
+stdin:
+	print '#!'"$__progname"'\necho tf' >lq
+	chmod +x lq
+	./lq
+expected-stdout:
+	tf
+---
+name: selftest-env
+description:
+	Just output the environment variables set (always fails)
+category: disabled
+stdin:
+	set
+---
+name: selftest-legacy
+description:
+	Check some things in the LEGACY KSH
+category: shell:legacy-yes
+stdin:
+	set +o emacs
+	set +o vi
+	[[ "$(set +o) -o" = *"-o emacs -o"* ]] && echo 1=emacs
+	[[ "$(set +o) -o" = *"-o vi -o"* ]] && echo 1=vi
+	set -o emacs
+	set -o vi
+	[[ "$(set +o) -o" = *"-o emacs -o"* ]] && echo 2=emacs
+	[[ "$(set +o) -o" = *"-o vi -o"* ]] && echo 2=vi
+expected-stdout:
+	2=emacs
+	2=vi
+---
+name: selftest-direct-builtin-call
+description:
+	Check that direct builtin calls work
+stdin:
+	ln -s "$__progname" cat || cp "$__progname" cat
+	ln -s "$__progname" echo || cp "$__progname" echo
+	./echo -c 'echo  foo' | ./cat -u
+expected-stdout:
+	-c echo  foo
+---
+name: alias-1
+description:
+	Check that recursion is detected/avoided in aliases.
+stdin:
+	alias fooBar=fooBar
+	fooBar
+	exit 0
+expected-stderr-pattern:
+	/fooBar.*not found.*/
+---
+name: alias-2
+description:
+	Check that recursion is detected/avoided in aliases.
+stdin:
+	alias fooBar=barFoo
+	alias barFoo=fooBar
+	fooBar
+	barFoo
+	exit 0
+expected-stderr-pattern:
+	/fooBar.*not found.*\n.*barFoo.*not found/
+---
+name: alias-3
+description:
+	Check that recursion is detected/avoided in aliases.
+stdin:
+	alias Echo='echo '
+	alias fooBar=barFoo
+	alias barFoo=fooBar
+	Echo fooBar
+	unalias barFoo
+	Echo fooBar
+expected-stdout:
+	fooBar
+	barFoo
+---
+name: alias-4
+description:
+	Check that alias expansion isn't done on keywords (in keyword
+	postitions).
+stdin:
+	alias Echo='echo '
+	alias while=While
+	while false; do echo hi ; done
+	Echo while
+expected-stdout:
+	While
+---
+name: alias-5
+description:
+	Check that alias expansion done after alias with trailing space.
+stdin:
+	alias Echo='echo '
+	alias foo='bar stuff '
+	alias bar='Bar1 Bar2 '
+	alias stuff='Stuff'
+	alias blah='Blah'
+	Echo foo blah
+expected-stdout:
+	Bar1 Bar2 Stuff Blah
+---
+name: alias-6
+description:
+	Check that alias expansion done after alias with trailing space.
+stdin:
+	alias Echo='echo '
+	alias foo='bar bar'
+	alias bar='Bar '
+	alias blah=Blah
+	Echo foo blah
+expected-stdout:
+	Bar Bar Blah
+---
+name: alias-7
+description:
+	Check that alias expansion done after alias with trailing space
+	after a keyword.
+stdin:
+	alias X='case '
+	alias Y=Z
+	X Y in 'Y') echo is y ;; Z) echo is z ; esac
+expected-stdout:
+	is z
+---
+name: alias-8
+description:
+	Check that newlines in an alias don't cause the command to be lost.
+stdin:
+	alias foo='
+	
+	
+	echo hi
+	
+	
+	
+	echo there
+	
+	
+	'
+	foo
+expected-stdout:
+	hi
+	there
+---
+name: alias-9
+description:
+	Check that recursion is detected/avoided in aliases.
+	This check fails for slow machines or Cygwin, raise
+	the time-limit clause (e.g. to 7) if this occurs.
+time-limit: 3
+stdin:
+	print '#!'"$__progname"'\necho tf' >lq
+	chmod +x lq
+	PATH=$PWD:$PATH
+	alias lq=lq
+	lq
+	echo = now
+	i=`lq`
+	print -r -- $i
+	echo = out
+	exit 0
+expected-stdout:
+	tf
+	= now
+	tf
+	= out
+---
+name: alias-10
+description:
+	Check that recursion is detected/avoided in aliases.
+	Regression, introduced during an old bugfix.
+stdin:
+	alias foo='print hello '
+	alias bar='foo world'
+	echo $(bar)
+expected-stdout:
+	hello world
+---
+name: arith-lazy-1
+description:
+	Check that only one side of ternary operator is evaluated
+stdin:
+	x=i+=2
+	y=j+=2
+	typeset -i i=1 j=1
+	echo $((1 ? 20 : (x+=2)))
+	echo $i,$x
+	echo $((0 ? (y+=2) : 30))
+	echo $j,$y
+expected-stdout:
+	20
+	1,i+=2
+	30
+	1,j+=2
+---
+name: arith-lazy-2
+description:
+	Check that assignments not done on non-evaluated side of ternary
+	operator
+stdin:
+	x=i+=2
+	y=j+=2
+	typeset -i i=1 j=1
+	echo $((1 ? 20 : (x+=2)))
+	echo $i,$x
+	echo $((0 ? (y+=2) : 30))
+	echo $i,$y
+expected-stdout:
+	20
+	1,i+=2
+	30
+	1,j+=2
+---
+name: arith-lazy-3
+description:
+	Check that assignments not done on non-evaluated side of ternary
+	operator and this construct is parsed correctly (Debian #445651)
+stdin:
+	x=4
+	y=$((0 ? x=1 : 2))
+	echo = $x $y =
+expected-stdout:
+	= 4 2 =
+---
+name: arith-lazy-4
+description:
+	Check that preun/postun not done on non-evaluated side of ternary
+	operator
+stdin:
+	(( m = n = 0, 1 ? n++ : m++ ? 2 : 3 ))
+	echo "($n, $m)"
+	m=0; echo $(( 0 ? ++m : 2 )); echo $m
+	m=0; echo $(( 0 ? m++ : 2 )); echo $m
+expected-stdout:
+	(1, 0)
+	2
+	0
+	2
+	0
+---
+name: arith-ternary-prec-1
+description:
+	Check precedence of ternary operator vs assignment
+stdin:
+	typeset -i x=2
+	y=$((1 ? 20 : x+=2))
+expected-exit: e != 0
+expected-stderr-pattern:
+	/.*:.*1 \? 20 : x\+=2.*lvalue.*\n$/
+---
+name: arith-ternary-prec-2
+description:
+	Check precedence of ternary operator vs assignment
+stdin:
+	typeset -i x=2
+	echo $((0 ? x+=2 : 20))
+expected-stdout:
+	20
+---
+name: arith-div-assoc-1
+description:
+	Check associativity of division operator
+stdin:
+	echo $((20 / 2 / 2))
+expected-stdout:
+	5
+---
+name: arith-div-byzero
+description:
+	Check division by zero errors out
+stdin:
+	x=$(echo $((1 / 0)))
+	echo =$?:$x.
+expected-stdout:
+	=1:.
+expected-stderr-pattern:
+	/.*divisor/
+---
+name: arith-div-intmin-by-minusone
+description:
+	Check division overflow wraps around silently
+category: int:32
+stdin:
+	echo signed:$((-2147483648 / -1))r$((-2147483648 % -1)).
+	echo unsigned:$((# -2147483648 / -1))r$((# -2147483648 % -1)).
+expected-stdout:
+	signed:-2147483648r0.
+	unsigned:0r2147483648.
+---
+name: arith-div-intmin-by-minusone-64
+description:
+	Check division overflow wraps around silently
+category: int:64
+stdin:
+	echo signed:$((-9223372036854775808 / -1))r$((-9223372036854775808 % -1)).
+	echo unsigned:$((# -9223372036854775808 / -1))r$((# -9223372036854775808 % -1)).
+expected-stdout:
+	signed:-9223372036854775808r0.
+	unsigned:0r9223372036854775808.
+---
+name: arith-assop-assoc-1
+description:
+	Check associativity of assignment-operator operator
+stdin:
+	typeset -i i=1 j=2 k=3
+	echo $((i += j += k))
+	echo $i,$j,$k
+expected-stdout:
+	6
+	6,5,3
+---
+name: arith-mandatory
+description:
+	Passing of this test is *mandatory* for a valid mksh executable!
+category: shell:legacy-no
+stdin:
+	typeset -i sari=0
+	typeset -Ui uari=0
+	typeset -i x=0
+	print -r -- $((x++)):$sari=$uari. #0
+	let --sari --uari
+	print -r -- $((x++)):$sari=$uari. #1
+	sari=2147483647 uari=2147483647
+	print -r -- $((x++)):$sari=$uari. #2
+	let ++sari ++uari
+	print -r -- $((x++)):$sari=$uari. #3
+	let --sari --uari
+	let 'sari *= 2' 'uari *= 2'
+	let ++sari ++uari
+	print -r -- $((x++)):$sari=$uari. #4
+	let ++sari ++uari
+	print -r -- $((x++)):$sari=$uari. #5
+	sari=-2147483648 uari=-2147483648
+	print -r -- $((x++)):$sari=$uari. #6
+	let --sari --uari
+	print -r -- $((x++)):$sari=$uari. #7
+	(( sari = -5 >> 1 ))
+	((# uari = -5 >> 1 ))
+	print -r -- $((x++)):$sari=$uari. #8
+	(( sari = -2 ))
+	((# uari = sari ))
+	print -r -- $((x++)):$sari=$uari. #9
+expected-stdout:
+	0:0=0.
+	1:-1=4294967295.
+	2:2147483647=2147483647.
+	3:-2147483648=2147483648.
+	4:-1=4294967295.
+	5:0=0.
+	6:-2147483648=2147483648.
+	7:2147483647=2147483647.
+	8:-3=2147483645.
+	9:-2=4294967294.
+---
+name: arith-unsigned-1
+description:
+	Check if unsigned arithmetics work
+category: int:32
+stdin:
+	# signed vs unsigned
+	echo x1 $((-1)) $((#-1))
+	# calculating
+	typeset -i vs
+	typeset -Ui vu
+	vs=4123456789; vu=4123456789
+	echo x2 $vs $vu
+	(( vs %= 2147483647 ))
+	(( vu %= 2147483647 ))
+	echo x3 $vs $vu
+	vs=4123456789; vu=4123456789
+	(( # vs %= 2147483647 ))
+	(( # vu %= 2147483647 ))
+	echo x4 $vs $vu
+	# make sure the calculation does not change unsigned flag
+	vs=4123456789; vu=4123456789
+	echo x5 $vs $vu
+	# short form
+	echo x6 $((# vs % 2147483647)) $((# vu % 2147483647))
+	# array refs
+	set -A va
+	va[1975973142]=right
+	va[4123456789]=wrong
+	echo x7 ${va[#4123456789%2147483647]}
+expected-stdout:
+	x1 -1 4294967295
+	x2 -171510507 4123456789
+	x3 -171510507 4123456789
+	x4 1975973142 1975973142
+	x5 -171510507 4123456789
+	x6 1975973142 1975973142
+	x7 right
+---
+name: arith-limit32-1
+description:
+	Check if arithmetics are 32 bit
+category: int:32
+stdin:
+	# signed vs unsigned
+	echo x1 $((-1)) $((#-1))
+	# calculating
+	typeset -i vs
+	typeset -Ui vu
+	vs=2147483647; vu=2147483647
+	echo x2 $vs $vu
+	let vs++ vu++
+	echo x3 $vs $vu
+	vs=4294967295; vu=4294967295
+	echo x4 $vs $vu
+	let vs++ vu++
+	echo x5 $vs $vu
+	let vs++ vu++
+	echo x6 $vs $vu
+expected-stdout:
+	x1 -1 4294967295
+	x2 2147483647 2147483647
+	x3 -2147483648 2147483648
+	x4 -1 4294967295
+	x5 0 0
+	x6 1 1
+---
+name: arith-limit64-1
+description:
+	Check if arithmetics are 64 bit
+category: int:64
+stdin:
+	# signed vs unsigned
+	echo x1 $((-1)) $((#-1))
+	# calculating
+	typeset -i vs
+	typeset -Ui vu
+	vs=9223372036854775807; vu=9223372036854775807
+	echo x2 $vs $vu
+	let vs++ vu++
+	echo x3 $vs $vu
+	vs=18446744073709551615; vu=18446744073709551615
+	echo x4 $vs $vu
+	let vs++ vu++
+	echo x5 $vs $vu
+	let vs++ vu++
+	echo x6 $vs $vu
+expected-stdout:
+	x1 -1 18446744073709551615
+	x2 9223372036854775807 9223372036854775807
+	x3 -9223372036854775808 9223372036854775808
+	x4 -1 18446744073709551615
+	x5 0 0
+	x6 1 1
+---
+name: bksl-nl-ign-1
+description:
+	Check that \newline is not collapsed after #
+stdin:
+	echo hi #there \
+	echo folks
+expected-stdout:
+	hi
+	folks
+---
+name: bksl-nl-ign-2
+description:
+	Check that \newline is not collapsed inside single quotes
+stdin:
+	echo 'hi \
+	there'
+	echo folks
+expected-stdout:
+	hi \
+	there
+	folks
+---
+name: bksl-nl-ign-3
+description:
+	Check that \newline is not collapsed inside single quotes
+stdin:
+	cat << \EOF
+	hi \
+	there
+	EOF
+expected-stdout:
+	hi \
+	there
+---
+name: bksl-nl-ign-4
+description:
+	Check interaction of aliases, single quotes and here-documents
+	with backslash-newline
+	(don't know what POSIX has to say about this)
+stdin:
+	a=2
+	alias x='echo hi
+	cat << "EOF"
+	foo\
+	bar
+	some'
+	x
+	more\
+	stuff$a
+	EOF
+expected-stdout:
+	hi
+	foo\
+	bar
+	some
+	more\
+	stuff$a
+---
+name: bksl-nl-ign-5
+description:
+	Check what happens with backslash at end of input
+	(the old Bourne shell trashes them; so do we)
+stdin: !
+	echo `echo foo\\`bar
+	echo hi\
+expected-stdout:
+	foobar
+	hi
+---
+#
+# Places \newline should be collapsed
+#
+name: bksl-nl-1
+description:
+	Check that \newline is collapsed before, in the middle of, and
+	after words
+stdin:
+	 	 	\
+			 echo hi\
+	There, \
+	folks
+expected-stdout:
+	hiThere, folks
+---
+name: bksl-nl-2
+description:
+	Check that \newline is collapsed in $ sequences
+	(ksh93 fails this)
+stdin:
+	a=12
+	ab=19
+	echo $\
+	a
+	echo $a\
+	b
+	echo $\
+	{a}
+	echo ${a\
+	b}
+	echo ${ab\
+	}
+expected-stdout:
+	12
+	19
+	12
+	19
+	19
+---
+name: bksl-nl-3
+description:
+	Check that \newline is collapsed in $(..) and `...` sequences
+	(ksh93 fails this)
+stdin:
+	echo $\
+	(echo foobar1)
+	echo $(\
+	echo foobar2)
+	echo $(echo foo\
+	bar3)
+	echo $(echo foobar4\
+	)
+	echo `
+	echo stuff1`
+	echo `echo st\
+	uff2`
+expected-stdout:
+	foobar1
+	foobar2
+	foobar3
+	foobar4
+	stuff1
+	stuff2
+---
+name: bksl-nl-4
+description:
+	Check that \newline is collapsed in $((..)) sequences
+	(ksh93 fails this)
+stdin:
+	echo $\
+	((1+2))
+	echo $(\
+	(1+2+3))
+	echo $((\
+	1+2+3+4))
+	echo $((1+\
+	2+3+4+5))
+	echo $((1+2+3+4+5+6)\
+	)
+expected-stdout:
+	3
+	6
+	10
+	15
+	21
+---
+name: bksl-nl-5
+description:
+	Check that \newline is collapsed in double quoted strings
+stdin:
+	echo "\
+	hi"
+	echo "foo\
+	bar"
+	echo "folks\
+	"
+expected-stdout:
+	hi
+	foobar
+	folks
+---
+name: bksl-nl-6
+description:
+	Check that \newline is collapsed in here document delimiters
+	(ksh93 fails second part of this)
+stdin:
+	a=12
+	cat << EO\
+	F
+	a=$a
+	foo\
+	bar
+	EOF
+	cat << E_O_F
+	foo
+	E_O_\
+	F
+	echo done
+expected-stdout:
+	a=12
+	foobar
+	foo
+	done
+---
+name: bksl-nl-7
+description:
+	Check that \newline is collapsed in double-quoted here-document
+	delimiter.
+stdin:
+	a=12
+	cat << "EO\
+	F"
+	a=$a
+	foo\
+	bar
+	EOF
+	echo done
+expected-stdout:
+	a=$a
+	foo\
+	bar
+	done
+---
+name: bksl-nl-8
+description:
+	Check that \newline is collapsed in various 2+ character tokens
+	delimiter.
+	(ksh93 fails this)
+stdin:
+	echo hi &\
+	& echo there
+	echo foo |\
+	| echo bar
+	cat <\
+	< EOF
+	stuff
+	EOF
+	cat <\
+	<\
+	- EOF
+		more stuff
+	EOF
+	cat <<\
+	EOF
+	abcdef
+	EOF
+	echo hi >\
+	> /dev/null
+	echo $?
+	i=1
+	case $i in
+	(\
+	x|\
+	1\
+	) echo hi;\
+	;
+	(*) echo oops
+	esac
+expected-stdout:
+	hi
+	there
+	foo
+	stuff
+	more stuff
+	abcdef
+	0
+	hi
+---
+name: bksl-nl-9
+description:
+	Check that \ at the end of an alias is collapsed when followed
+	by a newline
+	(don't know what POSIX has to say about this)
+stdin:
+	alias x='echo hi\'
+	x
+	echo there
+expected-stdout:
+	hiecho there
+---
+name: bksl-nl-10
+description:
+	Check that \newline in a keyword is collapsed
+stdin:
+	i\
+	f true; then\
+	 echo pass; el\
+	se echo fail; fi
+expected-stdout:
+	pass
+---
+#
+# Places \newline should be collapsed (ksh extensions)
+#
+name: bksl-nl-ksh-1
+description:
+	Check that \newline is collapsed in extended globbing
+	(ksh93 fails this)
+stdin:
+	xxx=foo
+	case $xxx in
+	(f*\
+	(\
+	o\
+	)\
+	) echo ok ;;
+	*) echo bad
+	esac
+expected-stdout:
+	ok
+---
+name: bksl-nl-ksh-2
+description:
+	Check that \newline is collapsed in ((...)) expressions
+	(ksh93 fails this)
+stdin:
+	i=1
+	(\
+	(\
+	i=i+2\
+	)\
+	)
+	echo $i
+expected-stdout:
+	3
+---
+name: break-1
+description:
+	See if break breaks out of loops
+stdin:
+	for i in a b c; do echo $i; break; echo bad-$i; done
+	echo end-1
+	for i in a b c; do echo $i; break 1; echo bad-$i; done
+	echo end-2
+	for i in a b c; do
+	    for j in x y z; do
+		echo $i:$j
+		break
+		echo bad-$i
+	    done
+	    echo end-$i
+	done
+	echo end-3
+expected-stdout:
+	a
+	end-1
+	a
+	end-2
+	a:x
+	end-a
+	b:x
+	end-b
+	c:x
+	end-c
+	end-3
+---
+name: break-2
+description:
+	See if break breaks out of nested loops
+stdin:
+	for i in a b c; do
+	    for j in x y z; do
+		echo $i:$j
+		break 2
+		echo bad-$i
+	    done
+	    echo end-$i
+	done
+	echo end
+expected-stdout:
+	a:x
+	end
+---
+name: break-3
+description:
+	What if break used outside of any loops
+	(ksh88,ksh93 don't print error messages here)
+stdin:
+	break
+expected-stderr-pattern:
+	/.*break.*/
+---
+name: break-4
+description:
+	What if break N used when only N-1 loops
+	(ksh88,ksh93 don't print error messages here)
+stdin:
+	for i in a b c; do echo $i; break 2; echo bad-$i; done
+	echo end
+expected-stdout:
+	a
+	end
+expected-stderr-pattern:
+	/.*break.*/
+---
+name: break-5
+description:
+	Error if break argument isn't a number
+stdin:
+	for i in a b c; do echo $i; break abc; echo more-$i; done
+	echo end
+expected-stdout:
+	a
+expected-exit: e != 0
+expected-stderr-pattern:
+	/.*break.*/
+---
+name: continue-1
+description:
+	See if continue continues loops
+stdin:
+	for i in a b c; do echo $i; continue; echo bad-$i ; done
+	echo end-1
+	for i in a b c; do echo $i; continue 1; echo bad-$i; done
+	echo end-2
+	for i in a b c; do
+	    for j in x y z; do
+		echo $i:$j
+		continue
+		echo bad-$i-$j
+	    done
+	    echo end-$i
+	done
+	echo end-3
+expected-stdout:
+	a
+	b
+	c
+	end-1
+	a
+	b
+	c
+	end-2
+	a:x
+	a:y
+	a:z
+	end-a
+	b:x
+	b:y
+	b:z
+	end-b
+	c:x
+	c:y
+	c:z
+	end-c
+	end-3
+---
+name: continue-2
+description:
+	See if continue breaks out of nested loops
+stdin:
+	for i in a b c; do
+	    for j in x y z; do
+		echo $i:$j
+		continue 2
+		echo bad-$i-$j
+	    done
+	    echo end-$i
+	done
+	echo end
+expected-stdout:
+	a:x
+	b:x
+	c:x
+	end
+---
+name: continue-3
+description:
+	What if continue used outside of any loops
+	(ksh88,ksh93 don't print error messages here)
+stdin:
+	continue
+expected-stderr-pattern:
+	/.*continue.*/
+---
+name: continue-4
+description:
+	What if continue N used when only N-1 loops
+	(ksh88,ksh93 don't print error messages here)
+stdin:
+	for i in a b c; do echo $i; continue 2; echo bad-$i; done
+	echo end
+expected-stdout:
+	a
+	b
+	c
+	end
+expected-stderr-pattern:
+	/.*continue.*/
+---
+name: continue-5
+description:
+	Error if continue argument isn't a number
+stdin:
+	for i in a b c; do echo $i; continue abc; echo more-$i; done
+	echo end
+expected-stdout:
+	a
+expected-exit: e != 0
+expected-stderr-pattern:
+	/.*continue.*/
+---
+name: cd-history
+description:
+	Test someone's CD history package (uses arrays)
+stdin:
+	# go to known place before doing anything
+	cd /
+	
+	alias cd=_cd
+	function _cd
+	{
+		typeset -i cdlen i
+		typeset t
+	
+		if [ $# -eq 0 ]
+		then
+			set -- $HOME
+		fi
+	
+		if [ "$CDHISTFILE" -a -r "$CDHISTFILE" ] # if directory history exists
+		then
+			typeset CDHIST
+			i=-1
+			while read -r t			# read directory history file
+			do
+				CDHIST[i=i+1]=$t
+			done <$CDHISTFILE
+		fi
+	
+		if [ "${CDHIST[0]}" != "$PWD" -a "$PWD" != "" ]
+		then
+			_cdins				# insert $PWD into cd history
+		fi
+	
+		cdlen=${#CDHIST[*]}			# number of elements in history
+	
+		case "$@" in
+		-)					# cd to new dir
+			if [ "$OLDPWD" = "" ] && ((cdlen>1))
+			then
+				'print' ${CDHIST[1]}
+				'cd' ${CDHIST[1]}
+				_pwd
+			else
+				'cd' $@
+				_pwd
+			fi
+			;;
+		-l)					# print directory list
+			typeset -R3 num
+			((i=cdlen))
+			while (((i=i-1)>=0))
+			do
+				num=$i
+				'print' "$num ${CDHIST[i]}"
+			done
+			return
+			;;
+		-[0-9]|-[0-9][0-9])			# cd to dir in list
+			if (((i=${1#-})<cdlen))
+			then
+				'print' ${CDHIST[i]}
+				'cd' ${CDHIST[i]}
+				_pwd
+			else
+				'cd' $@
+				_pwd
+			fi
+			;;
+		-*)					# cd to matched dir in list
+			t=${1#-}
+			i=1
+			while ((i<cdlen))
+			do
+				case ${CDHIST[i]} in
+				*$t*)
+					'print' ${CDHIST[i]}
+					'cd' ${CDHIST[i]}
+					_pwd
+					break
+					;;
+				esac
+				((i=i+1))
+			done
+			if ((i>=cdlen))
+			then
+				'cd' $@
+				_pwd
+			fi
+			;;
+		*)					# cd to new dir
+			'cd' $@
+			_pwd
+			;;
+		esac
+	
+		_cdins					# insert $PWD into cd history
+	
+		if [ "$CDHISTFILE" ]
+		then
+			cdlen=${#CDHIST[*]}		# number of elements in history
+	
+			i=0
+			while ((i<cdlen))
+			do
+				'print' -r ${CDHIST[i]}	# update directory history
+				((i=i+1))
+			done >$CDHISTFILE
+		fi
+	}
+	
+	function _cdins					# insert $PWD into cd history
+	{						# meant to be called only by _cd
+		typeset -i i
+	
+		((i=0))
+		while ((i<${#CDHIST[*]}))		# see if dir is already in list
+		do
+			if [ "${CDHIST[$i]}" = "$PWD" ]
+			then
+				break
+			fi
+			((i=i+1))
+		done
+	
+		if ((i>22))				# limit max size of list
+		then
+			i=22
+		fi
+	
+		while (((i=i-1)>=0))			# bump old dirs in list
+		do
+			CDHIST[i+1]=${CDHIST[i]}
+		done
+	
+		CDHIST[0]=$PWD				# insert new directory in list
+	}
+	
+	
+	function _pwd
+	{
+		if [ -n "$ECD" ]
+		then
+			pwd 1>&6
+		fi
+	}
+	# Start of test
+	cd /tmp
+	cd /bin
+	cd /etc
+	cd -
+	cd -2
+	cd -l
+expected-stdout:
+	/bin
+	/tmp
+	  3 /
+	  2 /etc
+	  1 /bin
+	  0 /tmp
+---
+name: cd-pe
+description:
+	Check package for cd -Pe
+need-pass: no
+# the mv command fails on Cygwin
+# Hurd aborts the testsuite (permission denied)
+# QNX does not find subdir to cd into
+category: !os:cygwin,!os:gnu,!os:msys,!os:nto,!nosymlink
+file-setup: file 644 "x"
+	mkdir noread noread/target noread/target/subdir
+	ln -s noread link
+	chmod 311 noread
+	cd -P$1 .
+	echo 0=$?
+	bwd=$PWD
+	cd -P$1 link/target
+	echo 1=$?,${PWD#$bwd/}
+	epwd=$($TSHELL -c pwd 2>/dev/null)
+	# This unexpectedly succeeds on GNU/Linux and MidnightBSD
+	#echo pwd=$?,$epwd
+	# expect:	pwd=1,
+	mv ../../noread ../../renamed
+	cd -P$1 subdir
+	echo 2=$?,${PWD#$bwd/}
+	cd $bwd
+	chmod 755 renamed
+	rm -rf noread link renamed
+stdin:
+	export TSHELL="$__progname"
+	"$__progname" x
+	echo "now with -e:"
+	"$__progname" x e
+expected-stdout:
+	0=0
+	1=0,noread/target
+	2=0,noread/target/subdir
+	now with -e:
+	0=0
+	1=0,noread/target
+	2=1,noread/target/subdir
+---
+name: env-prompt
+description:
+	Check that prompt not printed when processing ENV
+env-setup: !ENV=./foo!
+file-setup: file 644 "foo"
+	XXX=_
+	PS1=X
+	false && echo hmmm
+need-ctty: yes
+arguments: !-i!
+stdin:
+	echo hi${XXX}there
+expected-stdout:
+	hi_there
+expected-stderr: !
+	XX
+---
+name: expand-ugly
+description:
+	Check that weird ${foo+bar} constructs are parsed correctly
+stdin:
+	print '#!'"$__progname"'\nfor x in "$@"; do print -r -- "$x"; done' >pfn
+	print '#!'"$__progname"'\nfor x in "$@"; do print -nr -- "<$x> "; done' >pfs
+	chmod +x pfn pfs
+	(echo 1 ${IFS+'}'z}) 2>/dev/null || echo failed in 1
+	(echo 2 "${IFS+'}'z}") 2>/dev/null || echo failed in 2
+	(echo 3 "foo ${IFS+'bar} baz") 2>/dev/null || echo failed in 3
+	(echo -n '4 '; ./pfn "foo ${IFS+"b   c"} baz") 2>/dev/null || echo failed in 4
+	(echo -n '5 '; ./pfn "foo ${IFS+b   c} baz") 2>/dev/null || echo failed in 5
+	(echo 6 ${IFS+"}"z}) 2>/dev/null || echo failed in 6
+	(echo 7 "${IFS+"}"z}") 2>/dev/null || echo failed in 7
+	(echo 8 "${IFS+\"}\"z}") 2>/dev/null || echo failed in 8
+	(echo 9 "${IFS+\"\}\"z}") 2>/dev/null || echo failed in 9
+	(echo 10 foo ${IFS+'bar} baz'}) 2>/dev/null || echo failed in 10
+	(echo 11 "$(echo "${IFS+'}'z}")") 2>/dev/null || echo failed in 11
+	(echo 12 "$(echo ${IFS+'}'z})") 2>/dev/null || echo failed in 12
+	(echo 13 ${IFS+\}z}) 2>/dev/null || echo failed in 13
+	(echo 14 "${IFS+\}z}") 2>/dev/null || echo failed in 14
+	u=x; (echo -n '15 '; ./pfs "foo ${IFS+a"b$u{ {"{{\}b} c ${IFS+d{}} bar" ${IFS-e{}} baz; echo .) 2>/dev/null || echo failed in 15
+	l=t; (echo 16 ${IFS+h`echo -n i ${IFS+$l}h`ere}) 2>/dev/null || echo failed in 16
+	l=t; (echo 17 ${IFS+h$(echo -n i ${IFS+$l}h)ere}) 2>/dev/null || echo failed in 17
+	l=t; (echo 18 "${IFS+h`echo -n i ${IFS+$l}h`ere}") 2>/dev/null || echo failed in 18
+	l=t; (echo 19 "${IFS+h$(echo -n i ${IFS+$l}h)ere}") 2>/dev/null || echo failed in 19
+	l=t; (echo 20 ${IFS+h`echo -n i "${IFS+$l}"h`ere}) 2>/dev/null || echo failed in 20
+	l=t; (echo 21 ${IFS+h$(echo -n i "${IFS+$l}"h)ere}) 2>/dev/null || echo failed in 21
+	l=t; (echo 22 "${IFS+h`echo -n i "${IFS+$l}"h`ere}") 2>/dev/null || echo failed in 22
+	l=t; (echo 23 "${IFS+h$(echo -n i "${IFS+$l}"h)ere}") 2>/dev/null || echo failed in 23
+	key=value; (echo -n '24 '; ./pfn "${IFS+'$key'}") 2>/dev/null || echo failed in 24
+	key=value; (echo -n '25 '; ./pfn "${IFS+"'$key'"}") 2>/dev/null || echo failed in 25	# ksh93: “'$key'”
+	key=value; (echo -n '26 '; ./pfn ${IFS+'$key'}) 2>/dev/null || echo failed in 26
+	key=value; (echo -n '27 '; ./pfn ${IFS+"'$key'"}) 2>/dev/null || echo failed in 27
+	(echo -n '28 '; ./pfn "${IFS+"'"x ~ x'}'x"'}"x}" #') 2>/dev/null || echo failed in 28
+	u=x; (echo -n '29 '; ./pfs foo ${IFS+a"b$u{ {"{ {\}b} c ${IFS+d{}} bar ${IFS-e{}} baz; echo .) 2>/dev/null || echo failed in 29
+	(echo -n '30 '; ./pfs ${IFS+foo 'b\
+	ar' baz}; echo .) 2>/dev/null || (echo failed in 30; echo failed in 31)
+	(echo -n '32 '; ./pfs ${IFS+foo "b\
+	ar" baz}; echo .) 2>/dev/null || echo failed in 32
+	(echo -n '33 '; ./pfs "${IFS+foo 'b\
+	ar' baz}"; echo .) 2>/dev/null || echo failed in 33
+	(echo -n '34 '; ./pfs "${IFS+foo "b\
+	ar" baz}"; echo .) 2>/dev/null || echo failed in 34
+	(echo -n '35 '; ./pfs ${v=a\ b} x ${v=c\ d}; echo .) 2>/dev/null || echo failed in 35
+	(echo -n '36 '; ./pfs "${v=a\ b}" x "${v=c\ d}"; echo .) 2>/dev/null || echo failed in 36
+	(echo -n '37 '; ./pfs ${v-a\ b} x ${v-c\ d}; echo .) 2>/dev/null || echo failed in 37
+	(echo 38 ${IFS+x'a'y} / "${IFS+x'a'y}" .) 2>/dev/null || echo failed in 38
+	foo="x'a'y"; (echo 39 ${foo%*'a'*} / "${foo%*'a'*}" .) 2>/dev/null || echo failed in 39
+	foo="a b c"; (echo -n '40 '; ./pfs "${foo#a}"; echo .) 2>/dev/null || echo failed in 40
+expected-stdout:
+	1 }z
+	2 ''z}
+	3 foo 'bar baz
+	4 foo b   c baz
+	5 foo b   c baz
+	6 }z
+	7 }z
+	8 ""z}
+	9 "}"z
+	10 foo bar} baz
+	11 ''z}
+	12 }z
+	13 }z
+	14 }z
+	15 <foo abx{ {{{}b c d{} bar> <}> <baz> .
+	16 hi there
+	17 hi there
+	18 hi there
+	19 hi there
+	20 hi there
+	21 hi there
+	22 hi there
+	23 hi there
+	24 'value'
+	25 'value'
+	26 $key
+	27 'value'
+	28 'x ~ x''x}"x}" #
+	29 <foo> <abx{ {{> <{}b> <c> <d{}> <bar> <}> <baz> .
+	30 <foo> <b\
+	ar> <baz> .
+	32 <foo> <bar> <baz> .
+	33 <foo 'bar' baz> .
+	34 <foo bar baz> .
+	35 <a> <b> <x> <a> <b> .
+	36 <a\ b> <x> <a\ b> .
+	37 <a b> <x> <c d> .
+	38 xay / x'a'y .
+	39 x' / x' .
+	40 < b c> .
+---
+name: expand-unglob-dblq
+description:
+	Check that regular "${foo+bar}" constructs are parsed correctly
+stdin:
+	u=x
+	tl_norm() {
+		v=$2
+		test x"$v" = x"-" && unset v
+		(echo "$1 plus norm foo ${v+'bar'} baz")
+		(echo "$1 dash norm foo ${v-'bar'} baz")
+		(echo "$1 eqal norm foo ${v='bar'} baz")
+		(echo "$1 qstn norm foo ${v?'bar'} baz") 2>/dev/null || \
+		    echo "$1 qstn norm -> error"
+		(echo "$1 PLUS norm foo ${v:+'bar'} baz")
+		(echo "$1 DASH norm foo ${v:-'bar'} baz")
+		(echo "$1 EQAL norm foo ${v:='bar'} baz")
+		(echo "$1 QSTN norm foo ${v:?'bar'} baz") 2>/dev/null || \
+		    echo "$1 QSTN norm -> error"
+	}
+	tl_paren() {
+		v=$2
+		test x"$v" = x"-" && unset v
+		(echo "$1 plus parn foo ${v+(bar)} baz")
+		(echo "$1 dash parn foo ${v-(bar)} baz")
+		(echo "$1 eqal parn foo ${v=(bar)} baz")
+		(echo "$1 qstn parn foo ${v?(bar)} baz") 2>/dev/null || \
+		    echo "$1 qstn parn -> error"
+		(echo "$1 PLUS parn foo ${v:+(bar)} baz")
+		(echo "$1 DASH parn foo ${v:-(bar)} baz")
+		(echo "$1 EQAL parn foo ${v:=(bar)} baz")
+		(echo "$1 QSTN parn foo ${v:?(bar)} baz") 2>/dev/null || \
+		    echo "$1 QSTN parn -> error"
+	}
+	tl_brace() {
+		v=$2
+		test x"$v" = x"-" && unset v
+		(echo "$1 plus brac foo ${v+a$u{{{\}b} c ${v+d{}} baz")
+		(echo "$1 dash brac foo ${v-a$u{{{\}b} c ${v-d{}} baz")
+		(echo "$1 eqal brac foo ${v=a$u{{{\}b} c ${v=d{}} baz")
+		(echo "$1 qstn brac foo ${v?a$u{{{\}b} c ${v?d{}} baz") 2>/dev/null || \
+		    echo "$1 qstn brac -> error"
+		(echo "$1 PLUS brac foo ${v:+a$u{{{\}b} c ${v:+d{}} baz")
+		(echo "$1 DASH brac foo ${v:-a$u{{{\}b} c ${v:-d{}} baz")
+		(echo "$1 EQAL brac foo ${v:=a$u{{{\}b} c ${v:=d{}} baz")
+		(echo "$1 QSTN brac foo ${v:?a$u{{{\}b} c ${v:?d{}} baz") 2>/dev/null || \
+		    echo "$1 QSTN brac -> error"
+	}
+	tl_norm 1 -
+	tl_norm 2 ''
+	tl_norm 3 x
+	tl_paren 4 -
+	tl_paren 5 ''
+	tl_paren 6 x
+	tl_brace 7 -
+	tl_brace 8 ''
+	tl_brace 9 x
+expected-stdout:
+	1 plus norm foo  baz
+	1 dash norm foo 'bar' baz
+	1 eqal norm foo 'bar' baz
+	1 qstn norm -> error
+	1 PLUS norm foo  baz
+	1 DASH norm foo 'bar' baz
+	1 EQAL norm foo 'bar' baz
+	1 QSTN norm -> error
+	2 plus norm foo 'bar' baz
+	2 dash norm foo  baz
+	2 eqal norm foo  baz
+	2 qstn norm foo  baz
+	2 PLUS norm foo  baz
+	2 DASH norm foo 'bar' baz
+	2 EQAL norm foo 'bar' baz
+	2 QSTN norm -> error
+	3 plus norm foo 'bar' baz
+	3 dash norm foo x baz
+	3 eqal norm foo x baz
+	3 qstn norm foo x baz
+	3 PLUS norm foo 'bar' baz
+	3 DASH norm foo x baz
+	3 EQAL norm foo x baz
+	3 QSTN norm foo x baz
+	4 plus parn foo  baz
+	4 dash parn foo (bar) baz
+	4 eqal parn foo (bar) baz
+	4 qstn parn -> error
+	4 PLUS parn foo  baz
+	4 DASH parn foo (bar) baz
+	4 EQAL parn foo (bar) baz
+	4 QSTN parn -> error
+	5 plus parn foo (bar) baz
+	5 dash parn foo  baz
+	5 eqal parn foo  baz
+	5 qstn parn foo  baz
+	5 PLUS parn foo  baz
+	5 DASH parn foo (bar) baz
+	5 EQAL parn foo (bar) baz
+	5 QSTN parn -> error
+	6 plus parn foo (bar) baz
+	6 dash parn foo x baz
+	6 eqal parn foo x baz
+	6 qstn parn foo x baz
+	6 PLUS parn foo (bar) baz
+	6 DASH parn foo x baz
+	6 EQAL parn foo x baz
+	6 QSTN parn foo x baz
+	7 plus brac foo  c } baz
+	7 dash brac foo ax{{{}b c d{} baz
+	7 eqal brac foo ax{{{}b c ax{{{}b} baz
+	7 qstn brac -> error
+	7 PLUS brac foo  c } baz
+	7 DASH brac foo ax{{{}b c d{} baz
+	7 EQAL brac foo ax{{{}b c ax{{{}b} baz
+	7 QSTN brac -> error
+	8 plus brac foo ax{{{}b c d{} baz
+	8 dash brac foo  c } baz
+	8 eqal brac foo  c } baz
+	8 qstn brac foo  c } baz
+	8 PLUS brac foo  c } baz
+	8 DASH brac foo ax{{{}b c d{} baz
+	8 EQAL brac foo ax{{{}b c ax{{{}b} baz
+	8 QSTN brac -> error
+	9 plus brac foo ax{{{}b c d{} baz
+	9 dash brac foo x c x} baz
+	9 eqal brac foo x c x} baz
+	9 qstn brac foo x c x} baz
+	9 PLUS brac foo ax{{{}b c d{} baz
+	9 DASH brac foo x c x} baz
+	9 EQAL brac foo x c x} baz
+	9 QSTN brac foo x c x} baz
+---
+name: expand-unglob-unq
+description:
+	Check that regular ${foo+bar} constructs are parsed correctly
+stdin:
+	u=x
+	tl_norm() {
+		v=$2
+		test x"$v" = x"-" && unset v
+		(echo $1 plus norm foo ${v+'bar'} baz)
+		(echo $1 dash norm foo ${v-'bar'} baz)
+		(echo $1 eqal norm foo ${v='bar'} baz)
+		(echo $1 qstn norm foo ${v?'bar'} baz) 2>/dev/null || \
+		    echo "$1 qstn norm -> error"
+		(echo $1 PLUS norm foo ${v:+'bar'} baz)
+		(echo $1 DASH norm foo ${v:-'bar'} baz)
+		(echo $1 EQAL norm foo ${v:='bar'} baz)
+		(echo $1 QSTN norm foo ${v:?'bar'} baz) 2>/dev/null || \
+		    echo "$1 QSTN norm -> error"
+	}
+	tl_paren() {
+		v=$2
+		test x"$v" = x"-" && unset v
+		(echo $1 plus parn foo ${v+\(bar')'} baz)
+		(echo $1 dash parn foo ${v-\(bar')'} baz)
+		(echo $1 eqal parn foo ${v=\(bar')'} baz)
+		(echo $1 qstn parn foo ${v?\(bar')'} baz) 2>/dev/null || \
+		    echo "$1 qstn parn -> error"
+		(echo $1 PLUS parn foo ${v:+\(bar')'} baz)
+		(echo $1 DASH parn foo ${v:-\(bar')'} baz)
+		(echo $1 EQAL parn foo ${v:=\(bar')'} baz)
+		(echo $1 QSTN parn foo ${v:?\(bar')'} baz) 2>/dev/null || \
+		    echo "$1 QSTN parn -> error"
+	}
+	tl_brace() {
+		v=$2
+		test x"$v" = x"-" && unset v
+		(echo $1 plus brac foo ${v+a$u{{{\}b} c ${v+d{}} baz)
+		(echo $1 dash brac foo ${v-a$u{{{\}b} c ${v-d{}} baz)
+		(echo $1 eqal brac foo ${v=a$u{{{\}b} c ${v=d{}} baz)
+		(echo $1 qstn brac foo ${v?a$u{{{\}b} c ${v?d{}} baz) 2>/dev/null || \
+		    echo "$1 qstn brac -> error"
+		(echo $1 PLUS brac foo ${v:+a$u{{{\}b} c ${v:+d{}} baz)
+		(echo $1 DASH brac foo ${v:-a$u{{{\}b} c ${v:-d{}} baz)
+		(echo $1 EQAL brac foo ${v:=a$u{{{\}b} c ${v:=d{}} baz)
+		(echo $1 QSTN brac foo ${v:?a$u{{{\}b} c ${v:?d{}} baz) 2>/dev/null || \
+		    echo "$1 QSTN brac -> error"
+	}
+	tl_norm 1 -
+	tl_norm 2 ''
+	tl_norm 3 x
+	tl_paren 4 -
+	tl_paren 5 ''
+	tl_paren 6 x
+	tl_brace 7 -
+	tl_brace 8 ''
+	tl_brace 9 x
+expected-stdout:
+	1 plus norm foo baz
+	1 dash norm foo bar baz
+	1 eqal norm foo bar baz
+	1 qstn norm -> error
+	1 PLUS norm foo baz
+	1 DASH norm foo bar baz
+	1 EQAL norm foo bar baz
+	1 QSTN norm -> error
+	2 plus norm foo bar baz
+	2 dash norm foo baz
+	2 eqal norm foo baz
+	2 qstn norm foo baz
+	2 PLUS norm foo baz
+	2 DASH norm foo bar baz
+	2 EQAL norm foo bar baz
+	2 QSTN norm -> error
+	3 plus norm foo bar baz
+	3 dash norm foo x baz
+	3 eqal norm foo x baz
+	3 qstn norm foo x baz
+	3 PLUS norm foo bar baz
+	3 DASH norm foo x baz
+	3 EQAL norm foo x baz
+	3 QSTN norm foo x baz
+	4 plus parn foo baz
+	4 dash parn foo (bar) baz
+	4 eqal parn foo (bar) baz
+	4 qstn parn -> error
+	4 PLUS parn foo baz
+	4 DASH parn foo (bar) baz
+	4 EQAL parn foo (bar) baz
+	4 QSTN parn -> error
+	5 plus parn foo (bar) baz
+	5 dash parn foo baz
+	5 eqal parn foo baz
+	5 qstn parn foo baz
+	5 PLUS parn foo baz
+	5 DASH parn foo (bar) baz
+	5 EQAL parn foo (bar) baz
+	5 QSTN parn -> error
+	6 plus parn foo (bar) baz
+	6 dash parn foo x baz
+	6 eqal parn foo x baz
+	6 qstn parn foo x baz
+	6 PLUS parn foo (bar) baz
+	6 DASH parn foo x baz
+	6 EQAL parn foo x baz
+	6 QSTN parn foo x baz
+	7 plus brac foo c } baz
+	7 dash brac foo ax{{{}b c d{} baz
+	7 eqal brac foo ax{{{}b c ax{{{}b} baz
+	7 qstn brac -> error
+	7 PLUS brac foo c } baz
+	7 DASH brac foo ax{{{}b c d{} baz
+	7 EQAL brac foo ax{{{}b c ax{{{}b} baz
+	7 QSTN brac -> error
+	8 plus brac foo ax{{{}b c d{} baz
+	8 dash brac foo c } baz
+	8 eqal brac foo c } baz
+	8 qstn brac foo c } baz
+	8 PLUS brac foo c } baz
+	8 DASH brac foo ax{{{}b c d{} baz
+	8 EQAL brac foo ax{{{}b c ax{{{}b} baz
+	8 QSTN brac -> error
+	9 plus brac foo ax{{{}b c d{} baz
+	9 dash brac foo x c x} baz
+	9 eqal brac foo x c x} baz
+	9 qstn brac foo x c x} baz
+	9 PLUS brac foo ax{{{}b c d{} baz
+	9 DASH brac foo x c x} baz
+	9 EQAL brac foo x c x} baz
+	9 QSTN brac foo x c x} baz
+---
+name: expand-threecolons-dblq
+description:
+	Check for a particular thing that used to segfault
+stdin:
+	TEST=1234
+	echo "${TEST:1:2:3}"
+	echo $? but still living
+expected-stderr-pattern:
+	/bad substitution/
+expected-exit: 1
+---
+name: expand-threecolons-unq
+description:
+	Check for a particular thing that used to not error out
+stdin:
+	TEST=1234
+	echo ${TEST:1:2:3}
+	echo $? but still living
+expected-stderr-pattern:
+	/bad substitution/
+expected-exit: 1
+---
+name: expand-weird-1
+description:
+	Check corner case of trim expansion vs. $# vs. ${#var}
+stdin:
+	set 1 2 3 4 5 6 7 8 9 10 11
+	echo ${#}	# value of $#
+	echo ${##}	# length of $#
+	echo ${##1}	# $# trimmed 1
+	set 1 2 3 4 5 6 7 8 9 10 11 12
+	echo ${##1}
+expected-stdout:
+	11
+	2
+	1
+	2
+---
+name: expand-weird-2
+description:
+	Check corner case of ${var?} vs. ${#var}
+stdin:
+	(exit 0)
+	echo $? = ${#?} .
+	(exit 111)
+	echo $? = ${#?} .
+expected-stdout:
+	0 = 1 .
+	111 = 3 .
+---
+name: expand-weird-3
+description:
+	Check that trimming works with positional parameters (Debian #48453)
+stdin:
+	A=9999-02
+	B=9999
+	echo 1=${A#$B?}.
+	set -- $A $B
+	echo 2=${1#$2?}.
+expected-stdout:
+	1=02.
+	2=02.
+---
+name: expand-number-1
+description:
+	Check that positional arguments do not overflow
+stdin:
+	echo "1 ${12345678901234567890} ."
+expected-stdout:
+	1  .
+---
+name: eglob-bad-1
+description:
+	Check that globbing isn't done when glob has syntax error
+file-setup: file 644 "abcx"
+file-setup: file 644 "abcz"
+file-setup: file 644 "bbc"
+stdin:
+	echo !([*)*
+	echo +(a|b[)*
+expected-stdout:
+	!([*)*
+	+(a|b[)*
+---
+name: eglob-bad-2
+description:
+	Check that globbing isn't done when glob has syntax error
+	(AT&T ksh fails this test)
+file-setup: file 644 "abcx"
+file-setup: file 644 "abcz"
+file-setup: file 644 "bbc"
+stdin:
+	echo [a*(]*)z
+expected-stdout:
+	[a*(]*)z
+---
+name: eglob-infinite-plus
+description:
+	Check that shell doesn't go into infinite loop expanding +(...)
+	expressions.
+file-setup: file 644 "abc"
+time-limit: 3
+stdin:
+	echo +()c
+	echo +()x
+	echo +(*)c
+	echo +(*)x
+expected-stdout:
+	+()c
+	+()x
+	abc
+	+(*)x
+---
+name: eglob-subst-1
+description:
+	Check that eglobbing isn't done on substitution results
+file-setup: file 644 "abc"
+stdin:
+	x='@(*)'
+	echo $x
+expected-stdout:
+	@(*)
+---
+name: eglob-nomatch-1
+description:
+	Check that the pattern doesn't match
+stdin:
+	echo 1: no-file+(a|b)stuff
+	echo 2: no-file+(a*(c)|b)stuff
+	echo 3: no-file+((((c)))|b)stuff
+expected-stdout:
+	1: no-file+(a|b)stuff
+	2: no-file+(a*(c)|b)stuff
+	3: no-file+((((c)))|b)stuff
+---
+name: eglob-match-1
+description:
+	Check that the pattern matches correctly
+file-setup: file 644 "abd"
+file-setup: file 644 "acd"
+file-setup: file 644 "abac"
+stdin:
+	echo 1: a+(b|c)d
+	echo 2: a!(@(b|B))d
+	echo 3: *(a(b|c))		# (...|...) can be used within X(..)
+	echo 4: a[b*(foo|bar)]d		# patterns not special inside [...]
+expected-stdout:
+	1: abd acd
+	2: acd
+	3: abac
+	4: abd
+---
+name: eglob-case-1
+description:
+	Simple negation tests
+stdin:
+	case foo in !(foo|bar)) echo yes;; *) echo no;; esac
+	case bar in !(foo|bar)) echo yes;; *) echo no;; esac
+expected-stdout:
+	no
+	no
+---
+name: eglob-case-2
+description:
+	Simple kleene tests
+stdin:
+	case foo in *(a|b[)) echo yes;; *) echo no;; esac
+	case foo in *(a|b[)|f*) echo yes;; *) echo no;; esac
+	case '*(a|b[)' in *(a|b[)) echo yes;; *) echo no;; esac
+expected-stdout:
+	no
+	yes
+	yes
+---
+name: eglob-trim-1
+description:
+	Eglobbing in trim expressions...
+	(AT&T ksh fails this - docs say # matches shortest string, ## matches
+	longest...)
+stdin:
+	x=abcdef
+	echo 1: ${x#a|abc}
+	echo 2: ${x##a|abc}
+	echo 3: ${x%def|f}
+	echo 4: ${x%%f|def}
+expected-stdout:
+	1: bcdef
+	2: def
+	3: abcde
+	4: abc
+---
+name: eglob-trim-2
+description:
+	Check eglobbing works in trims...
+stdin:
+	x=abcdef
+	echo 1: ${x#*(a|b)cd}
+	echo 2: "${x#*(a|b)cd}"
+	echo 3: ${x#"*(a|b)cd"}
+	echo 4: ${x#a(b|c)}
+expected-stdout:
+	1: ef
+	2: ef
+	3: abcdef
+	4: cdef
+---
+name: eglob-trim-3
+description:
+	Check eglobbing works in trims, for Korn Shell
+	Ensure eglobbing does not work for reduced-feature /bin/sh
+stdin:
+	set +o sh
+	x=foobar
+	y=foobaz
+	z=fooba\?
+	echo "<${x%bar|baz},${y%bar|baz},${z%\?}>"
+	echo "<${x%ba(r|z)},${y%ba(r|z)}>"
+	set -o sh
+	echo "<${x%bar|baz},${y%bar|baz},${z%\?}>"
+	z='foo(bar'
+	echo "<${z%(*}>"
+expected-stdout:
+	<foo,foo,fooba>
+	<foo,foo>
+	<foobar,foobaz,fooba>
+	<foo>
+---
+name: eglob-substrpl-1
+description:
+	Check eglobbing works in substs... and they work at all
+stdin:
+	[[ -n $BASH_VERSION ]] && shopt -s extglob
+	x=1222321_ab/cde_b/c_1221
+	y=xyz
+	echo 1: ${x/2}
+	echo 2: ${x//2}
+	echo 3: ${x/+(2)}
+	echo 4: ${x//+(2)}
+	echo 5: ${x/2/4}
+	echo 6: ${x//2/4}
+	echo 7: ${x/+(2)/4}
+	echo 8: ${x//+(2)/4}
+	echo 9: ${x/b/c/e/f}
+	echo 10: ${x/b\/c/e/f}
+	echo 11: ${x/b\/c/e\/f}
+	echo 12: ${x/b\/c/e\\/f}
+	echo 13: ${x/b\\/c/e\\/f}
+	echo 14: ${x//b/c/e/f}
+	echo 15: ${x//b\/c/e/f}
+	echo 16: ${x//b\/c/e\/f}
+	echo 17: ${x//b\/c/e\\/f}
+	echo 18: ${x//b\\/c/e\\/f}
+	echo 19: ${x/b\/*\/c/x}
+	echo 20: ${x/\//.}
+	echo 21: ${x//\//.}
+	echo 22: ${x///.}
+	echo 23: ${x//#1/9}
+	echo 24: ${x//%1/9}
+	echo 25: ${x//\%1/9}
+	echo 26: ${x//\\%1/9}
+	echo 27: ${x//\a/9}
+	echo 28: ${x//\\a/9}
+	echo 29: ${x/2/$y}
+expected-stdout:
+	1: 122321_ab/cde_b/c_1221
+	2: 131_ab/cde_b/c_11
+	3: 1321_ab/cde_b/c_1221
+	4: 131_ab/cde_b/c_11
+	5: 1422321_ab/cde_b/c_1221
+	6: 1444341_ab/cde_b/c_1441
+	7: 14321_ab/cde_b/c_1221
+	8: 14341_ab/cde_b/c_141
+	9: 1222321_ac/e/f/cde_b/c_1221
+	10: 1222321_ae/fde_b/c_1221
+	11: 1222321_ae/fde_b/c_1221
+	12: 1222321_ae\/fde_b/c_1221
+	13: 1222321_ab/cde_b/c_1221
+	14: 1222321_ac/e/f/cde_c/e/f/c_1221
+	15: 1222321_ae/fde_e/f_1221
+	16: 1222321_ae/fde_e/f_1221
+	17: 1222321_ae\/fde_e\/f_1221
+	18: 1222321_ab/cde_b/c_1221
+	19: 1222321_ax_1221
+	20: 1222321_ab.cde_b/c_1221
+	21: 1222321_ab.cde_b.c_1221
+	22: 1222321_ab/cde_b/c_1221
+	23: 9222321_ab/cde_b/c_1221
+	24: 1222321_ab/cde_b/c_1229
+	25: 1222321_ab/cde_b/c_1229
+	26: 1222321_ab/cde_b/c_1221
+	27: 1222321_9b/cde_b/c_1221
+	28: 1222321_9b/cde_b/c_1221
+	29: 1xyz22321_ab/cde_b/c_1221
+---
+name: eglob-substrpl-2
+description:
+	Check anchored substring replacement works, corner cases
+stdin:
+	foo=123
+	echo 1: ${foo/#/x}
+	echo 2: ${foo/%/x}
+	echo 3: ${foo/#/}
+	echo 4: ${foo/#}
+	echo 5: ${foo/%/}
+	echo 6: ${foo/%}
+expected-stdout:
+	1: x123
+	2: 123x
+	3: 123
+	4: 123
+	5: 123
+	6: 123
+---
+name: eglob-substrpl-3a
+description:
+	Check substring replacement works with variables and slashes, too
+stdin:
+	pfx=/home/user
+	wd=/home/user/tmp
+	echo "${wd/#$pfx/~}"
+	echo "${wd/#\$pfx/~}"
+	echo "${wd/#"$pfx"/~}"
+	echo "${wd/#'$pfx'/~}"
+	echo "${wd/#"\$pfx"/~}"
+	echo "${wd/#'\$pfx'/~}"
+expected-stdout:
+	~/tmp
+	/home/user/tmp
+	~/tmp
+	/home/user/tmp
+	/home/user/tmp
+	/home/user/tmp
+---
+name: eglob-substrpl-3b
+description:
+	More of this, bash fails it (bash4 passes)
+stdin:
+	pfx=/home/user
+	wd=/home/user/tmp
+	echo "${wd/#$(echo /home/user)/~}"
+	echo "${wd/#"$(echo /home/user)"/~}"
+	echo "${wd/#'$(echo /home/user)'/~}"
+expected-stdout:
+	~/tmp
+	~/tmp
+	/home/user/tmp
+---
+name: eglob-substrpl-3c
+description:
+	Even more weird cases
+stdin:
+	pfx=/home/user
+	wd='$pfx/tmp'
+	echo 1: ${wd/#$pfx/~}
+	echo 2: ${wd/#\$pfx/~}
+	echo 3: ${wd/#"$pfx"/~}
+	echo 4: ${wd/#'$pfx'/~}
+	echo 5: ${wd/#"\$pfx"/~}
+	echo 6: ${wd/#'\$pfx'/~}
+	ts='a/ba/b$tp$tp_a/b$tp_*(a/b)_*($tp)'
+	tp=a/b
+	tr=c/d
+	[[ -n $BASH_VERSION ]] && shopt -s extglob
+	echo 7: ${ts/a\/b/$tr}
+	echo 8: ${ts/a\/b/\$tr}
+	echo 9: ${ts/$tp/$tr}
+	echo 10: ${ts/\$tp/$tr}
+	echo 11: ${ts/\\$tp/$tr}
+	echo 12: ${ts/$tp/c/d}
+	echo 13: ${ts/$tp/c\/d}
+	echo 14: ${ts/$tp/c\\/d}
+	echo 15: ${ts/+(a\/b)/$tr}
+	echo 16: ${ts/+(a\/b)/\$tr}
+	echo 17: ${ts/+($tp)/$tr}
+	echo 18: ${ts/+($tp)/c/d}
+	echo 19: ${ts/+($tp)/c\/d}
+	echo 25: ${ts//a\/b/$tr}
+	echo 26: ${ts//a\/b/\$tr}
+	echo 27: ${ts//$tp/$tr}
+	echo 28: ${ts//$tp/c/d}
+	echo 29: ${ts//$tp/c\/d}
+	echo 30: ${ts//+(a\/b)/$tr}
+	echo 31: ${ts//+(a\/b)/\$tr}
+	echo 32: ${ts//+($tp)/$tr}
+	echo 33: ${ts//+($tp)/c/d}
+	echo 34: ${ts//+($tp)/c\/d}
+	tp="+($tp)"
+	echo 40: ${ts/$tp/$tr}
+	echo 41: ${ts//$tp/$tr}
+expected-stdout:
+	1: $pfx/tmp
+	2: ~/tmp
+	3: $pfx/tmp
+	4: ~/tmp
+	5: ~/tmp
+	6: ~/tmp
+	7: c/da/b$tp$tp_a/b$tp_*(a/b)_*($tp)
+	8: $tra/b$tp$tp_a/b$tp_*(a/b)_*($tp)
+	9: c/da/b$tp$tp_a/b$tp_*(a/b)_*($tp)
+	10: a/ba/bc/d$tp_a/b$tp_*(a/b)_*($tp)
+	11: c/da/b$tp$tp_a/b$tp_*(a/b)_*($tp)
+	12: c/da/b$tp$tp_a/b$tp_*(a/b)_*($tp)
+	13: c/da/b$tp$tp_a/b$tp_*(a/b)_*($tp)
+	14: c\/da/b$tp$tp_a/b$tp_*(a/b)_*($tp)
+	15: c/d$tp$tp_a/b$tp_*(a/b)_*($tp)
+	16: $tr$tp$tp_a/b$tp_*(a/b)_*($tp)
+	17: c/d$tp$tp_a/b$tp_*(a/b)_*($tp)
+	18: c/d$tp$tp_a/b$tp_*(a/b)_*($tp)
+	19: c/d$tp$tp_a/b$tp_*(a/b)_*($tp)
+	25: c/dc/d$tp$tp_c/d$tp_*(c/d)_*($tp)
+	26: $tr$tr$tp$tp_$tr$tp_*($tr)_*($tp)
+	27: c/dc/d$tp$tp_c/d$tp_*(c/d)_*($tp)
+	28: c/dc/d$tp$tp_c/d$tp_*(c/d)_*($tp)
+	29: c/dc/d$tp$tp_c/d$tp_*(c/d)_*($tp)
+	30: c/d$tp$tp_c/d$tp_*(c/d)_*($tp)
+	31: $tr$tp$tp_$tr$tp_*($tr)_*($tp)
+	32: c/d$tp$tp_c/d$tp_*(c/d)_*($tp)
+	33: c/d$tp$tp_c/d$tp_*(c/d)_*($tp)
+	34: c/d$tp$tp_c/d$tp_*(c/d)_*($tp)
+	40: a/ba/b$tp$tp_a/b$tp_*(a/b)_*($tp)
+	41: a/ba/b$tp$tp_a/b$tp_*(a/b)_*($tp)
+#	This is what GNU bash does:
+#	40: c/d$tp$tp_a/b$tp_*(a/b)_*($tp)
+#	41: c/d$tp$tp_c/d$tp_*(c/d)_*($tp)
+---
+name: eglob-utf8-1
+description:
+	UTF-8 mode differences for eglobbing
+stdin:
+	s=blöd
+	set +U
+	print 1: ${s%???} .
+	print 2: ${s/b???d/x} .
+	set -U
+	print 3: ${s%???} .
+	print 4: ${s/b??d/x} .
+	x=nö
+	print 5: ${x%?} ${x%%?} .
+	x=äh
+	print 6: ${x#?} ${x##?} .
+	x=\x81\x82
+	print 7: ${x%?} ${x%%?} .
+	x=mä\x80
+	print 8: ${x%?} ${x%%?} .
+	x=何
+	print 9: ${x%?} ${x%%?} .
+expected-stdout:
+	1: bl .
+	2: x .
+	3: b .
+	4: x .
+	5: n n .
+	6: h h .
+	7: \x81 \x81 .
+	8: mä mä .
+	9: .
+---
+name: glob-bad-1
+description:
+	Check that globbing isn't done when glob has syntax error
+file-setup: dir 755 "[x"
+file-setup: file 644 "[x/foo"
+stdin:
+	echo [*
+	echo *[x
+	echo [x/*
+expected-stdout:
+	[*
+	*[x
+	[x/foo
+---
+name: glob-bad-2
+description:
+	Check that symbolic links aren't stat()'d
+# breaks on FreeMiNT (cannot unlink dangling symlinks)
+# breaks on MSYS (does not support symlinks)
+# breaks on Dell UNIX 4.0 R2.2 (SVR4) where unlink also fails
+category: !os:mint,!os:msys,!os:svr4.0,!nosymlink
+file-setup: dir 755 "dir"
+file-setup: symlink 644 "dir/abc"
+	non-existent-file
+stdin:
+	echo d*/*
+	echo d*/abc
+expected-stdout:
+	dir/abc
+	dir/abc
+---
+name: glob-range-1
+description:
+	Test range matching
+file-setup: file 644 ".bc"
+file-setup: file 644 "abc"
+file-setup: file 644 "bbc"
+file-setup: file 644 "cbc"
+file-setup: file 644 "-bc"
+stdin:
+	echo [ab-]*
+	echo [-ab]*
+	echo [!-ab]*
+	echo [!ab]*
+	echo []ab]*
+	:>'./!bc'
+	:>'./^bc'
+	echo [^ab]*
+	echo [!ab]*
+expected-stdout:
+	-bc abc bbc
+	-bc abc bbc
+	cbc
+	-bc cbc
+	abc bbc
+	^bc abc bbc
+	!bc -bc ^bc cbc
+---
+name: glob-range-2
+description:
+	Test range matching
+	(AT&T ksh fails this; POSIX says invalid)
+file-setup: file 644 "abc"
+stdin:
+	echo [a--]*
+expected-stdout:
+	[a--]*
+---
+name: glob-range-3
+description:
+	Check that globbing matches the right things...
+# breaks on Mac OSX (HFS+ non-standard Unicode canonical decomposition)
+# breaks on Cygwin 1.7 (files are now UTF-16 or something)
+# breaks on QNX 6.4.1 (says RT)
+category: !os:cygwin,!os:darwin,!os:msys,!os:nto
+need-pass: no
+file-setup: file 644 "a\xC2c"
+stdin:
+	echo a[\xC1-\xDA]*
+expected-stdout:
+	a\xC2c
+---
+name: glob-range-4
+description:
+	Results unspecified according to POSIX
+file-setup: file 644 ".bc"
+stdin:
+	echo [a.]*
+expected-stdout:
+	[a.]*
+---
+name: glob-range-5
+description:
+	Results unspecified according to POSIX
+	(AT&T ksh treats this like [a-cc-e]*)
+file-setup: file 644 "abc"
+file-setup: file 644 "bbc"
+file-setup: file 644 "cbc"
+file-setup: file 644 "dbc"
+file-setup: file 644 "ebc"
+file-setup: file 644 "-bc"
+stdin:
+	echo [a-c-e]*
+expected-stdout:
+	-bc abc bbc cbc ebc
+---
+name: glob-trim-1
+description:
+	Check against a regression from fixing IFS-subst-2
+stdin:
+	x='#foo'
+	print -r "before='$x'"
+	x=${x%%#*}
+	print -r "after ='$x'"
+expected-stdout:
+	before='#foo'
+	after =''
+---
+name: heredoc-1
+description:
+	Check ordering/content of redundent here documents.
+stdin:
+	cat << EOF1 << EOF2
+	hi
+	EOF1
+	there
+	EOF2
+expected-stdout:
+	there
+---
+name: heredoc-2
+description:
+	Check quoted here-doc is protected.
+stdin:
+	a=foo
+	cat << 'EOF'
+	hi\
+	there$a
+	stuff
+	EO\
+	F
+	EOF
+expected-stdout:
+	hi\
+	there$a
+	stuff
+	EO\
+	F
+---
+name: heredoc-3
+description:
+	Check that newline isn't needed after heredoc-delimiter marker.
+stdin: !
+	cat << EOF
+	hi
+	there
+	EOF
+expected-stdout:
+	hi
+	there
+---
+name: heredoc-4
+description:
+	Check that an error occurs if the heredoc-delimiter is missing.
+stdin: !
+	cat << EOF
+	hi
+	there
+expected-exit: e > 0
+expected-stderr-pattern: /.*/
+---
+name: heredoc-5
+description:
+	Check that backslash quotes a $, ` and \ and kills a \newline
+stdin:
+	a=BAD
+	b=ok
+	cat << EOF
+	h\${a}i
+	h\\${b}i
+	th\`echo not-run\`ere
+	th\\`echo is-run`ere
+	fol\\ks
+	more\\
+	last \
+	line
+	EOF
+expected-stdout:
+	h${a}i
+	h\oki
+	th`echo not-run`ere
+	th\is-runere
+	fol\ks
+	more\
+	last line
+---
+name: heredoc-6
+description:
+	Check that \newline in initial here-delim word doesn't imply
+	a quoted here-doc.
+stdin:
+	a=i
+	cat << EO\
+	F
+	h$a
+	there
+	EOF
+expected-stdout:
+	hi
+	there
+---
+name: heredoc-7
+description:
+	Check that double quoted $ expressions in here delimiters are
+	not expanded and match the delimiter.
+	POSIX says only quote removal is applied to the delimiter.
+stdin:
+	a=b
+	cat << "E$a"
+	hi
+	h$a
+	hb
+	E$a
+	echo done
+expected-stdout:
+	hi
+	h$a
+	hb
+	done
+---
+name: heredoc-8
+description:
+	Check that double quoted escaped $ expressions in here
+	delimiters are not expanded and match the delimiter.
+	POSIX says only quote removal is applied to the delimiter
+	(\ counts as a quote).
+stdin:
+	a=b
+	cat << "E\$a"
+	hi
+	h$a
+	h\$a
+	hb
+	h\b
+	E$a
+	echo done
+expected-stdout:
+	hi
+	h$a
+	h\$a
+	hb
+	h\b
+	done
+---
+name: heredoc-9a
+description:
+	Check that here strings work.
+stdin:
+	bar="bar
+		baz"
+	tr abcdefghijklmnopqrstuvwxyz nopqrstuvwxyzabcdefghijklm <<<foo
+	"$__progname" -c "tr abcdefghijklmnopqrstuvwxyz nopqrstuvwxyzabcdefghijklm <<<foo"
+	tr abcdefghijklmnopqrstuvwxyz nopqrstuvwxyzabcdefghijklm <<<"$bar"
+	tr abcdefghijklmnopqrstuvwxyz nopqrstuvwxyzabcdefghijklm <<<'$bar'
+	tr abcdefghijklmnopqrstuvwxyz nopqrstuvwxyzabcdefghijklm <<<\$bar
+	tr abcdefghijklmnopqrstuvwxyz nopqrstuvwxyzabcdefghijklm <<<-foo
+	tr abcdefghijklmnopqrstuvwxyz nopqrstuvwxyzabcdefghijklm <<<"$(echo "foo bar")"
+expected-stdout:
+	sbb
+	sbb
+	one
+		onm
+	$one
+	$one
+	-sbb
+	sbb one
+---
+name: heredoc-9b
+description:
+	Check that a corner case of here strings works like bash
+stdin:
+	fnord=42
+	bar="bar
+		 \$fnord baz"
+	tr abcdefghijklmnopqrstuvwxyz nopqrstuvwxyzabcdefghijklm <<<$bar
+expected-stdout:
+	one $sabeq onm
+category: bash
+---
+name: heredoc-9c
+description:
+	Check that a corner case of here strings works like ksh93, zsh
+stdin:
+	fnord=42
+	bar="bar
+		 \$fnord baz"
+	tr abcdefghijklmnopqrstuvwxyz nopqrstuvwxyzabcdefghijklm <<<$bar
+expected-stdout:
+	one
+		 $sabeq onm
+---
+name: heredoc-9d
+description:
+	Check another corner case of here strings
+stdin:
+	tr abcdefghijklmnopqrstuvwxyz nopqrstuvwxyzabcdefghijklm <<< bar
+expected-stdout:
+	one
+---
+name: heredoc-9e
+description:
+	Check here string related regression with multiple iops
+stdin:
+	echo $(tr r z <<<'bar' 2>/dev/null)
+expected-stdout:
+	baz
+---
+name: heredoc-10
+description:
+	Check direct here document assignment
+stdin:
+	x=u
+	va=<<EOF
+	=a $x \x40=
+	EOF
+	vb=<<'EOF'
+	=b $x \x40=
+	EOF
+	function foo {
+		vc=<<-EOF
+			=c $x \x40=
+		EOF
+	}
+	fnd=$(typeset -f foo)
+	print -r -- "$fnd"
+	function foo {
+		echo blub
+	}
+	foo
+	eval "$fnd"
+	foo
+	# rather nonsensical, but…
+	vd=<<<"=d $x \x40="
+	ve=<<<'=e $x \x40='
+	vf=<<<$'=f $x \x40='
+	# now check
+	print -r -- "| va={$va} vb={$vb} vc={$vc} vd={$vd} ve={$ve} vf={$vf} |"
+expected-stdout:
+	function foo {
+		vc=<<-EOF
+	=c $x \x40=
+	EOF
+	
+	} 
+	blub
+	| va={=a u \x40=
+	} vb={=b $x \x40=
+	} vc={=c u \x40=
+	} vd={=d u \x40=
+	} ve={=e $x \x40=
+	} vf={=f $x @=
+	} |
+---
+name: heredoc-11
+description:
+	Check here documents with no or empty delimiter
+stdin:
+	x=u
+	va=<<
+	=a $x \x40=
+	<<
+	vb=<<''
+	=b $x \x40=
+	
+	function foo {
+		vc=<<-
+			=c $x \x40=
+		<<
+		vd=<<-''
+			=d $x \x40=
+	
+	}
+	fnd=$(typeset -f foo)
+	print -r -- "$fnd"
+	function foo {
+		echo blub
+	}
+	foo
+	eval "$fnd"
+	foo
+	print -r -- "| va={$va} vb={$vb} vc={$vc} vd={$vd} |"
+expected-stdout:
+	function foo {
+		vc=<<-
+	=c $x \x40=
+	<<
+	
+		vd=<<-""
+	=d $x \x40=
+	
+	
+	} 
+	blub
+	| va={=a u \x40=
+	} vb={=b $x \x40=
+	} vc={=c u \x40=
+	} vd={=d $x \x40=
+	} |
+---
+name: heredoc-comsub-1
+description:
+	Tests for here documents in COMSUB, taken from Austin ML
+stdin:
+	text=$(cat <<EOF
+	here is the text
+	EOF)
+	echo = $text =
+expected-stdout:
+	= here is the text =
+---
+name: heredoc-comsub-2
+description:
+	Tests for here documents in COMSUB, taken from Austin ML
+stdin:
+	unbalanced=$(cat <<EOF
+	this paren ) is a problem
+	EOF)
+	echo = $unbalanced =
+expected-stdout:
+	= this paren ) is a problem =
+---
+name: heredoc-comsub-3
+description:
+	Tests for here documents in COMSUB, taken from Austin ML
+stdin:
+	balanced=$(cat <<EOF
+	these parens ( ) are not a problem
+	EOF)
+	echo = $balanced =
+expected-stdout:
+	= these parens ( ) are not a problem =
+---
+name: heredoc-comsub-4
+description:
+	Tests for here documents in COMSUB, taken from Austin ML
+stdin:
+	balanced=$(cat <<EOF
+	these parens \( ) are a problem
+	EOF)
+	echo = $balanced =
+expected-stdout:
+	= these parens \( ) are a problem =
+---
+name: heredoc-subshell-1
+description:
+	Tests for here documents in subshells, taken from Austin ML
+stdin:
+	(cat <<EOF
+	some text
+	EOF)
+	echo end
+expected-stdout:
+	some text
+	end
+---
+name: heredoc-subshell-2
+description:
+	Tests for here documents in subshells, taken from Austin ML
+stdin:
+	(cat <<EOF
+	some text
+	EOF
+	)
+	echo end
+expected-stdout:
+	some text
+	end
+---
+name: heredoc-subshell-3
+description:
+	Tests for here documents in subshells, taken from Austin ML
+stdin:
+	(cat <<EOF; )
+	some text
+	EOF
+	echo end
+expected-stdout:
+	some text
+	end
+---
+name: heredoc-weird-1
+description:
+	Tests for here documents, taken from Austin ML
+	Documents current state in mksh, *NOT* necessarily correct!
+stdin:
+	cat <<END
+	hello
+	END\
+	END
+	END
+	echo end
+expected-stdout:
+	hello
+	ENDEND
+	end
+---
+name: heredoc-weird-2
+description:
+	Tests for here documents, taken from Austin ML
+stdin:
+	cat <<'    END    '
+	hello
+	    END    
+	echo end
+expected-stdout:
+	hello
+	end
+---
+name: heredoc-weird-4
+description:
+	Tests for here documents, taken from Austin ML
+	Documents current state in mksh, *NOT* necessarily correct!
+stdin:
+	cat <<END
+	hello\
+	END
+	END
+	echo end
+expected-stdout:
+	helloEND
+	end
+---
+name: heredoc-weird-5
+description:
+	Tests for here documents, taken from Austin ML
+	Documents current state in mksh, *NOT* necessarily correct!
+stdin:
+	cat <<END
+	hello
+	\END
+	END
+	echo end
+expected-stdout:
+	hello
+	\END
+	end
+---
+name: heredoc-tmpfile-1
+description:
+	Check that heredoc temp files aren't removed too soon or too late.
+	Heredoc in simple command.
+stdin:
+	TMPDIR=$PWD
+	eval '
+		cat <<- EOF
+		hi
+		EOF
+		for i in a b ; do
+			cat <<- EOF
+			more
+			EOF
+		done
+	    ' &
+	sleep 1
+	echo Left overs: *
+expected-stdout:
+	hi
+	more
+	more
+	Left overs: *
+---
+name: heredoc-tmpfile-2
+description:
+	Check that heredoc temp files aren't removed too soon or too late.
+	Heredoc in function, multiple calls to function.
+stdin:
+	TMPDIR=$PWD
+	eval '
+		foo() {
+			cat <<- EOF
+			hi
+			EOF
+		}
+		foo
+		foo
+	    ' &
+	sleep 1
+	echo Left overs: *
+expected-stdout:
+	hi
+	hi
+	Left overs: *
+---
+name: heredoc-tmpfile-3
+description:
+	Check that heredoc temp files aren't removed too soon or too late.
+	Heredoc in function in loop, multiple calls to function.
+stdin:
+	TMPDIR=$PWD
+	eval '
+		foo() {
+			cat <<- EOF
+			hi
+			EOF
+		}
+		for i in a b; do
+			foo
+			foo() {
+				cat <<- EOF
+				folks $i
+				EOF
+			}
+		done
+		foo
+	    ' &
+	sleep 1
+	echo Left overs: *
+expected-stdout:
+	hi
+	folks b
+	folks b
+	Left overs: *
+---
+name: heredoc-tmpfile-4
+description:
+	Check that heredoc temp files aren't removed too soon or too late.
+	Backgrounded simple command with here doc
+stdin:
+	TMPDIR=$PWD
+	eval '
+		cat <<- EOF &
+		hi
+		EOF
+	    ' &
+	sleep 1
+	echo Left overs: *
+expected-stdout:
+	hi
+	Left overs: *
+---
+name: heredoc-tmpfile-5
+description:
+	Check that heredoc temp files aren't removed too soon or too late.
+	Backgrounded subshell command with here doc
+stdin:
+	TMPDIR=$PWD
+	eval '
+	      (
+		sleep 1	# so parent exits
+		echo A
+		cat <<- EOF
+		hi
+		EOF
+		echo B
+	      ) &
+	    ' &
+	sleep 2
+	echo Left overs: *
+expected-stdout:
+	A
+	hi
+	B
+	Left overs: *
+---
+name: heredoc-tmpfile-6
+description:
+	Check that heredoc temp files aren't removed too soon or too late.
+	Heredoc in pipeline.
+stdin:
+	TMPDIR=$PWD
+	eval '
+		cat <<- EOF | sed "s/hi/HI/"
+		hi
+		EOF
+	    ' &
+	sleep 1
+	echo Left overs: *
+expected-stdout:
+	HI
+	Left overs: *
+---
+name: heredoc-tmpfile-7
+description:
+	Check that heredoc temp files aren't removed too soon or too late.
+	Heredoc in backgrounded pipeline.
+stdin:
+	TMPDIR=$PWD
+	eval '
+		cat <<- EOF | sed 's/hi/HI/' &
+		hi
+		EOF
+	    ' &
+	sleep 1
+	echo Left overs: *
+expected-stdout:
+	HI
+	Left overs: *
+---
+name: heredoc-tmpfile-8
+description:
+	Check that heredoc temp files aren't removed too soon or too
+	late. Heredoc in function, backgrounded call to function.
+	This check can fail on slow machines (<100 MHz), or Cygwin,
+	that's normal.
+need-pass: no
+stdin:
+	TMPDIR=$PWD
+	# Background eval so main shell doesn't do parsing
+	eval '
+		foo() {
+			cat <<- EOF
+			hi
+			EOF
+		}
+		foo
+		# sleep so eval can die
+		(sleep 1; foo) &
+		(sleep 1; foo) &
+		foo
+	    ' &
+	sleep 2
+	echo Left overs: *
+expected-stdout:
+	hi
+	hi
+	hi
+	hi
+	Left overs: *
+---
+name: heredoc-quoting-unsubst
+description:
+	Check for correct handling of quoted characters in
+	here documents without substitution (marker is quoted).
+stdin:
+	foo=bar
+	cat <<-'EOF'
+		x " \" \ \\ $ \$ `echo baz` \`echo baz\` $foo \$foo x
+	EOF
+expected-stdout:
+	x " \" \ \\ $ \$ `echo baz` \`echo baz\` $foo \$foo x
+---
+name: heredoc-quoting-subst
+description:
+	Check for correct handling of quoted characters in
+	here documents with substitution (marker is not quoted).
+stdin:
+	foo=bar
+	cat <<-EOF
+		x " \" \ \\ $ \$ `echo baz` \`echo baz\` $foo \$foo x
+	EOF
+expected-stdout:
+	x " \" \ \ $ $ baz `echo baz` bar $foo x
+---
+name: single-quotes-in-braces
+description:
+	Check that single quotes inside unquoted {} are treated as quotes
+stdin:
+	foo=1
+	echo ${foo:+'blah  $foo'}
+expected-stdout:
+	blah  $foo
+---
+name: single-quotes-in-quoted-braces
+description:
+	Check that single quotes inside quoted {} are treated as
+	normal char
+stdin:
+	foo=1
+	echo "${foo:+'blah  $foo'}"
+expected-stdout:
+	'blah  1'
+---
+name: single-quotes-in-braces-nested
+description:
+	Check that single quotes inside unquoted {} are treated as quotes,
+	even if that's inside a double-quoted command expansion
+stdin:
+	foo=1
+	echo "$( echo ${foo:+'blah  $foo'})"
+expected-stdout:
+	blah  $foo
+---
+name: single-quotes-in-brace-pattern
+description:
+	Check that single quotes inside {} pattern are treated as quotes
+stdin:
+	foo=1234
+	echo ${foo%'2'*} "${foo%'2'*}" ${foo%2'*'} "${foo%2'*'}"
+expected-stdout:
+	1 1 1234 1234
+---
+name: single-quotes-in-heredoc-braces
+description:
+	Check that single quotes inside {} in heredoc are treated
+	as normal char
+stdin:
+	foo=1
+	cat <<EOM
+	${foo:+'blah  $foo'}
+	EOM
+expected-stdout:
+	'blah  1'
+---
+name: single-quotes-in-nested-braces
+description:
+	Check that single quotes inside nested unquoted {} are
+	treated as quotes
+stdin:
+	foo=1
+	echo ${foo:+${foo:+'blah  $foo'}}
+expected-stdout:
+	blah  $foo
+---
+name: single-quotes-in-nested-quoted-braces
+description:
+	Check that single quotes inside nested quoted {} are treated
+	as normal char
+stdin:
+	foo=1
+	echo "${foo:+${foo:+'blah  $foo'}}"
+expected-stdout:
+	'blah  1'
+---
+name: single-quotes-in-nested-braces-nested
+description:
+	Check that single quotes inside nested unquoted {} are treated
+	as quotes, even if that's inside a double-quoted command expansion
+stdin:
+	foo=1
+	echo "$( echo ${foo:+${foo:+'blah  $foo'}})"
+expected-stdout:
+	blah  $foo
+---
+name: single-quotes-in-nested-brace-pattern
+description:
+	Check that single quotes inside nested {} pattern are treated as quotes
+stdin:
+	foo=1234
+	echo ${foo:+${foo%'2'*}} "${foo:+${foo%'2'*}}" ${foo:+${foo%2'*'}} "${foo:+${foo%2'*'}}"
+expected-stdout:
+	1 1 1234 1234
+---
+name: single-quotes-in-heredoc-nested-braces
+description:
+	Check that single quotes inside nested {} in heredoc are treated
+	as normal char
+stdin:
+	foo=1
+	cat <<EOM
+	${foo:+${foo:+'blah  $foo'}}
+	EOM
+expected-stdout:
+	'blah  1'
+---
+name: history-basic
+description:
+	See if we can test history at all
+need-ctty: yes
+arguments: !-i!
+env-setup: !ENV=./Env!HISTFILE=hist.file!
+file-setup: file 644 "Env"
+	PS1=X
+stdin:
+	echo hi
+	fc -l
+expected-stdout:
+	hi
+	1	echo hi
+expected-stderr-pattern:
+	/^X*$/
+---
+name: history-dups
+description:
+	Verify duplicates and spaces are not entered
+need-ctty: yes
+arguments: !-i!
+env-setup: !ENV=./Env!HISTFILE=hist.file!
+file-setup: file 644 "Env"
+	PS1=X
+stdin:
+	echo hi
+	 echo yo
+	echo hi
+	fc -l
+expected-stdout:
+	hi
+	yo
+	hi
+	1	echo hi
+expected-stderr-pattern:
+	/^X*$/
+---
+name: history-unlink
+description:
+	Check if broken HISTFILEs do not cause trouble
+need-ctty: yes
+arguments: !-i!
+env-setup: !ENV=./Env!HISTFILE=foo/hist.file!
+file-setup: file 644 "Env"
+	PS1=X
+file-setup: dir 755 "foo"
+file-setup: file 644 "foo/hist.file"
+	sometext
+time-limit: 5
+perl-setup: chmod(0555, "foo");
+stdin:
+	echo hi
+	fc -l
+	chmod 0755 foo
+expected-stdout:
+	hi
+	1	echo hi
+expected-stderr-pattern:
+	/(.*can't unlink HISTFILE.*\n)?X*$/
+---
+name: history-e-minus-1
+description:
+	Check if more recent command is executed
+need-ctty: yes
+arguments: !-i!
+env-setup: !ENV=./Env!HISTFILE=hist.file!
+file-setup: file 644 "Env"
+	PS1=X
+stdin:
+	echo hi
+	echo there
+	fc -e -
+expected-stdout:
+	hi
+	there
+	there
+expected-stderr-pattern:
+	/^X*echo there\nX*$/
+---
+name: history-e-minus-2
+description:
+	Check that repeated command is printed before command
+	is re-executed.
+need-ctty: yes
+arguments: !-i!
+env-setup: !ENV=./Env!HISTFILE=hist.file!
+file-setup: file 644 "Env"
+	PS1=X
+stdin:
+	exec 2>&1
+	echo hi
+	echo there
+	fc -e -
+expected-stdout-pattern:
+	/X*hi\nX*there\nX*echo there\nthere\nX*/
+expected-stderr-pattern:
+	/^X*$/
+---
+name: history-e-minus-3
+description:
+	fc -e - fails when there is no history
+	(ksh93 has a bug that causes this to fail)
+	(ksh88 loops on this)
+need-ctty: yes
+arguments: !-i!
+env-setup: !ENV=./Env!HISTFILE=hist.file!
+file-setup: file 644 "Env"
+	PS1=X
+stdin:
+	fc -e -
+	echo ok
+expected-stdout:
+	ok
+expected-stderr-pattern:
+	/^X*.*:.*history.*\nX*$/
+---
+name: history-e-minus-4
+description:
+	Check if "fc -e -" command output goes to stdout.
+need-ctty: yes
+arguments: !-i!
+env-setup: !ENV=./Env!HISTFILE=hist.file!
+file-setup: file 644 "Env"
+	PS1=X
+stdin:
+	echo abc
+	fc -e - | (read x; echo "A $x")
+	echo ok
+expected-stdout:
+	abc
+	A abc
+	ok
+expected-stderr-pattern:
+	/^X*echo abc\nX*/
+---
+name: history-e-minus-5
+description:
+	fc is replaced in history by new command.
+need-ctty: yes
+arguments: !-i!
+env-setup: !ENV=./Env!HISTFILE=hist.file!
+file-setup: file 644 "Env"
+	PS1=X
+stdin:
+	echo abc def
+	echo ghi jkl
+	:
+	fc -e - echo
+	fc -l 2 5
+expected-stdout:
+	abc def
+	ghi jkl
+	ghi jkl
+	2	echo ghi jkl
+	3	:
+	4	echo ghi jkl
+	5	fc -l 2 5
+expected-stderr-pattern:
+	/^X*echo ghi jkl\nX*$/
+---
+name: history-list-1
+description:
+	List lists correct range
+	(ksh88 fails 'cause it lists the fc command)
+need-ctty: yes
+arguments: !-i!
+env-setup: !ENV=./Env!HISTFILE=hist.file!
+file-setup: file 644 "Env"
+	PS1=X
+stdin:
+	echo line 1
+	echo line 2
+	echo line 3
+	fc -l -- -2
+expected-stdout:
+	line 1
+	line 2
+	line 3
+	2	echo line 2
+	3	echo line 3
+expected-stderr-pattern:
+	/^X*$/
+---
+name: history-list-2
+description:
+	Lists oldest history if given pre-historic number
+	(ksh93 has a bug that causes this to fail)
+	(ksh88 fails 'cause it lists the fc command)
+need-ctty: yes
+arguments: !-i!
+env-setup: !ENV=./Env!HISTFILE=hist.file!
+file-setup: file 644 "Env"
+	PS1=X
+stdin:
+	echo line 1
+	echo line 2
+	echo line 3
+	fc -l -- -40
+expected-stdout:
+	line 1
+	line 2
+	line 3
+	1	echo line 1
+	2	echo line 2
+	3	echo line 3
+expected-stderr-pattern:
+	/^X*$/
+---
+name: history-list-3
+description:
+	Can give number 'options' to fc
+need-ctty: yes
+arguments: !-i!
+env-setup: !ENV=./Env!HISTFILE=hist.file!
+file-setup: file 644 "Env"
+	PS1=X
+stdin:
+	echo line 1
+	echo line 2
+	echo line 3
+	echo line 4
+	fc -l -3 -2
+expected-stdout:
+	line 1
+	line 2
+	line 3
+	line 4
+	2	echo line 2
+	3	echo line 3
+expected-stderr-pattern:
+	/^X*$/
+---
+name: history-list-4
+description:
+	-1 refers to previous command
+need-ctty: yes
+arguments: !-i!
+env-setup: !ENV=./Env!HISTFILE=hist.file!
+file-setup: file 644 "Env"
+	PS1=X
+stdin:
+	echo line 1
+	echo line 2
+	echo line 3
+	echo line 4
+	fc -l -1 -1
+expected-stdout:
+	line 1
+	line 2
+	line 3
+	line 4
+	4	echo line 4
+expected-stderr-pattern:
+	/^X*$/
+---
+name: history-list-5
+description:
+	List command stays in history
+need-ctty: yes
+arguments: !-i!
+env-setup: !ENV=./Env!HISTFILE=hist.file!
+file-setup: file 644 "Env"
+	PS1=X
+stdin:
+	echo line 1
+	echo line 2
+	echo line 3
+	echo line 4
+	fc -l -1 -1
+	fc -l -2 -1
+expected-stdout:
+	line 1
+	line 2
+	line 3
+	line 4
+	4	echo line 4
+	4	echo line 4
+	5	fc -l -1 -1
+expected-stderr-pattern:
+	/^X*$/
+---
+name: history-list-6
+description:
+	HISTSIZE limits about of history kept.
+	(ksh88 fails 'cause it lists the fc command)
+need-ctty: yes
+arguments: !-i!
+env-setup: !ENV=./Env!HISTFILE=hist.file!HISTSIZE=3!
+file-setup: file 644 "Env"
+	PS1=X
+stdin:
+	echo line 1
+	echo line 2
+	echo line 3
+	echo line 4
+	echo line 5
+	fc -l
+expected-stdout:
+	line 1
+	line 2
+	line 3
+	line 4
+	line 5
+	4	echo line 4
+	5	echo line 5
+expected-stderr-pattern:
+	/^X*$/
+---
+name: history-list-7
+description:
+	fc allows too old/new errors in range specification
+need-ctty: yes
+arguments: !-i!
+env-setup: !ENV=./Env!HISTFILE=hist.file!HISTSIZE=3!
+file-setup: file 644 "Env"
+	PS1=X
+stdin:
+	echo line 1
+	echo line 2
+	echo line 3
+	echo line 4
+	echo line 5
+	fc -l 1 30
+expected-stdout:
+	line 1
+	line 2
+	line 3
+	line 4
+	line 5
+	4	echo line 4
+	5	echo line 5
+	6	fc -l 1 30
+expected-stderr-pattern:
+	/^X*$/
+---
+name: history-list-r-1
+description:
+	test -r flag in history
+need-ctty: yes
+arguments: !-i!
+env-setup: !ENV=./Env!HISTFILE=hist.file!
+file-setup: file 644 "Env"
+	PS1=X
+stdin:
+	echo line 1
+	echo line 2
+	echo line 3
+	echo line 4
+	echo line 5
+	fc -l -r 2 4
+expected-stdout:
+	line 1
+	line 2
+	line 3
+	line 4
+	line 5
+	4	echo line 4
+	3	echo line 3
+	2	echo line 2
+expected-stderr-pattern:
+	/^X*$/
+---
+name: history-list-r-2
+description:
+	If first is newer than last, -r is implied.
+need-ctty: yes
+arguments: !-i!
+env-setup: !ENV=./Env!HISTFILE=hist.file!
+file-setup: file 644 "Env"
+	PS1=X
+stdin:
+	echo line 1
+	echo line 2
+	echo line 3
+	echo line 4
+	echo line 5
+	fc -l 4 2
+expected-stdout:
+	line 1
+	line 2
+	line 3
+	line 4
+	line 5
+	4	echo line 4
+	3	echo line 3
+	2	echo line 2
+expected-stderr-pattern:
+	/^X*$/
+---
+name: history-list-r-3
+description:
+	If first is newer than last, -r is cancelled.
+need-ctty: yes
+arguments: !-i!
+env-setup: !ENV=./Env!HISTFILE=hist.file!
+file-setup: file 644 "Env"
+	PS1=X
+stdin:
+	echo line 1
+	echo line 2
+	echo line 3
+	echo line 4
+	echo line 5
+	fc -l -r 4 2
+expected-stdout:
+	line 1
+	line 2
+	line 3
+	line 4
+	line 5
+	2	echo line 2
+	3	echo line 3
+	4	echo line 4
+expected-stderr-pattern:
+	/^X*$/
+---
+name: history-subst-1
+description:
+	Basic substitution
+need-ctty: yes
+arguments: !-i!
+env-setup: !ENV=./Env!HISTFILE=hist.file!
+file-setup: file 644 "Env"
+	PS1=X
+stdin:
+	echo abc def
+	echo ghi jkl
+	fc -e - abc=AB 'echo a'
+expected-stdout:
+	abc def
+	ghi jkl
+	AB def
+expected-stderr-pattern:
+	/^X*echo AB def\nX*$/
+---
+name: history-subst-2
+description:
+	Does subst find previous command?
+need-ctty: yes
+arguments: !-i!
+env-setup: !ENV=./Env!HISTFILE=hist.file!
+file-setup: file 644 "Env"
+	PS1=X
+stdin:
+	echo abc def
+	echo ghi jkl
+	fc -e - jkl=XYZQRT 'echo g'
+expected-stdout:
+	abc def
+	ghi jkl
+	ghi XYZQRT
+expected-stderr-pattern:
+	/^X*echo ghi XYZQRT\nX*$/
+---
+name: history-subst-3
+description:
+	Does subst find previous command when no arguments given
+need-ctty: yes
+arguments: !-i!
+env-setup: !ENV=./Env!HISTFILE=hist.file!
+file-setup: file 644 "Env"
+	PS1=X
+stdin:
+	echo abc def
+	echo ghi jkl
+	fc -e - jkl=XYZQRT
+expected-stdout:
+	abc def
+	ghi jkl
+	ghi XYZQRT
+expected-stderr-pattern:
+	/^X*echo ghi XYZQRT\nX*$/
+---
+name: history-subst-4
+description:
+	Global substitutions work
+	(ksh88 and ksh93 do not have -g option)
+need-ctty: yes
+arguments: !-i!
+env-setup: !ENV=./Env!HISTFILE=hist.file!
+file-setup: file 644 "Env"
+	PS1=X
+stdin:
+	echo abc def asjj sadjhasdjh asdjhasd
+	fc -e - -g a=FooBAR
+expected-stdout:
+	abc def asjj sadjhasdjh asdjhasd
+	FooBARbc def FooBARsjj sFooBARdjhFooBARsdjh FooBARsdjhFooBARsd
+expected-stderr-pattern:
+	/^X*echo FooBARbc def FooBARsjj sFooBARdjhFooBARsdjh FooBARsdjhFooBARsd\nX*$/
+---
+name: history-subst-5
+description:
+	Make sure searches don't find current (fc) command
+	(ksh88/ksh93 don't have the ? prefix thing so they fail this test)
+need-ctty: yes
+arguments: !-i!
+env-setup: !ENV=./Env!HISTFILE=hist.file!
+file-setup: file 644 "Env"
+	PS1=X
+stdin:
+	echo abc def
+	echo ghi jkl
+	fc -e - abc=AB \?abc
+expected-stdout:
+	abc def
+	ghi jkl
+	AB def
+expected-stderr-pattern:
+	/^X*echo AB def\nX*$/
+---
+name: history-ed-1-old
+description:
+	Basic (ed) editing works (assumes you have generic ed editor
+	that prints no prompts). This is for oldish ed(1) which write
+	the character count to stdout.
+category: stdout-ed
+need-ctty: yes
+need-pass: no
+arguments: !-i!
+env-setup: !ENV=./Env!HISTFILE=hist.file!
+file-setup: file 644 "Env"
+	PS1=X
+stdin:
+	echo abc def
+	fc echo
+	s/abc/FOOBAR/
+	w
+	q
+expected-stdout:
+	abc def
+	13
+	16
+	FOOBAR def
+expected-stderr-pattern:
+	/^X*echo FOOBAR def\nX*$/
+---
+name: history-ed-2-old
+description:
+	Correct command is edited when number given
+category: stdout-ed
+need-ctty: yes
+need-pass: no
+arguments: !-i!
+env-setup: !ENV=./Env!HISTFILE=hist.file!
+file-setup: file 644 "Env"
+	PS1=X
+stdin:
+	echo line 1
+	echo line 2 is here
+	echo line 3
+	echo line 4
+	fc 2
+	s/is here/is changed/
+	w
+	q
+expected-stdout:
+	line 1
+	line 2 is here
+	line 3
+	line 4
+	20
+	23
+	line 2 is changed
+expected-stderr-pattern:
+	/^X*echo line 2 is changed\nX*$/
+---
+name: history-ed-3-old
+description:
+	Newly created multi line commands show up as single command
+	in history.
+	(NOTE: adjusted for COMPLEX HISTORY compile time option)
+	(ksh88 fails 'cause it lists the fc command)
+category: stdout-ed
+need-ctty: yes
+need-pass: no
+arguments: !-i!
+env-setup: !ENV=./Env!HISTFILE=hist.file!
+file-setup: file 644 "Env"
+	PS1=X
+stdin:
+	echo abc def
+	fc echo
+	s/abc/FOOBAR/
+	$a
+	echo a new line
+	.
+	w
+	q
+	fc -l
+expected-stdout:
+	abc def
+	13
+	32
+	FOOBAR def
+	a new line
+	1	echo abc def
+	2	echo FOOBAR def
+	3	echo a new line
+expected-stderr-pattern:
+	/^X*echo FOOBAR def\necho a new line\nX*$/
+---
+name: history-ed-1
+description:
+	Basic (ed) editing works (assumes you have generic ed editor
+	that prints no prompts). This is for newish ed(1) and stderr.
+category: !no-stderr-ed
+need-ctty: yes
+need-pass: no
+arguments: !-i!
+env-setup: !ENV=./Env!HISTFILE=hist.file!
+file-setup: file 644 "Env"
+	PS1=X
+stdin:
+	echo abc def
+	fc echo
+	s/abc/FOOBAR/
+	w
+	q
+expected-stdout:
+	abc def
+	FOOBAR def
+expected-stderr-pattern:
+	/^X*13\n16\necho FOOBAR def\nX*$/
+---
+name: history-ed-2
+description:
+	Correct command is edited when number given
+category: !no-stderr-ed
+need-ctty: yes
+need-pass: no
+arguments: !-i!
+env-setup: !ENV=./Env!HISTFILE=hist.file!
+file-setup: file 644 "Env"
+	PS1=X
+stdin:
+	echo line 1
+	echo line 2 is here
+	echo line 3
+	echo line 4
+	fc 2
+	s/is here/is changed/
+	w
+	q
+expected-stdout:
+	line 1
+	line 2 is here
+	line 3
+	line 4
+	line 2 is changed
+expected-stderr-pattern:
+	/^X*20\n23\necho line 2 is changed\nX*$/
+---
+name: history-ed-3
+description:
+	Newly created multi line commands show up as single command
+	in history.
+category: !no-stderr-ed
+need-ctty: yes
+need-pass: no
+arguments: !-i!
+env-setup: !ENV=./Env!HISTFILE=hist.file!
+file-setup: file 644 "Env"
+	PS1=X
+stdin:
+	echo abc def
+	fc echo
+	s/abc/FOOBAR/
+	$a
+	echo a new line
+	.
+	w
+	q
+	fc -l
+expected-stdout:
+	abc def
+	FOOBAR def
+	a new line
+	1	echo abc def
+	2	echo FOOBAR def
+	3	echo a new line
+expected-stderr-pattern:
+	/^X*13\n32\necho FOOBAR def\necho a new line\nX*$/
+---
+name: IFS-space-1
+description:
+	Simple test, default IFS
+stdin:
+	showargs() { for i; do echo -n " <$i>"; done; echo; }
+	set -- A B C
+	showargs 1 $*
+	showargs 2 "$*"
+	showargs 3 $@
+	showargs 4 "$@"
+expected-stdout:
+	 <1> <A> <B> <C>
+	 <2> <A B C>
+	 <3> <A> <B> <C>
+	 <4> <A> <B> <C>
+---
+name: IFS-colon-1
+description:
+	Simple test, IFS=:
+stdin:
+	showargs() { for i; do echo -n " <$i>"; done; echo; }
+	IFS=:
+	set -- A B C
+	showargs 1 $*
+	showargs 2 "$*"
+	showargs 3 $@
+	showargs 4 "$@"
+expected-stdout:
+	 <1> <A> <B> <C>
+	 <2> <A:B:C>
+	 <3> <A> <B> <C>
+	 <4> <A> <B> <C>
+---
+name: IFS-null-1
+description:
+	Simple test, IFS=""
+stdin:
+	showargs() { for i; do echo -n " <$i>"; done; echo; }
+	IFS=""
+	set -- A B C
+	showargs 1 $*
+	showargs 2 "$*"
+	showargs 3 $@
+	showargs 4 "$@"
+expected-stdout:
+	 <1> <A> <B> <C>
+	 <2> <ABC>
+	 <3> <A> <B> <C>
+	 <4> <A> <B> <C>
+---
+name: IFS-space-colon-1
+description:
+	Simple test, IFS=<white-space>:
+stdin:
+	showargs() { for i; do echo -n " <$i>"; done; echo; }
+	IFS="$IFS:"
+	set --
+	showargs 1 $*
+	showargs 2 "$*"
+	showargs 3 $@
+	showargs 4 "$@"
+	showargs 5 : "$@"
+expected-stdout:
+	 <1>
+	 <2> <>
+	 <3>
+	 <4>
+	 <5> <:>
+---
+name: IFS-space-colon-2
+description:
+	Simple test, IFS=<white-space>:
+	AT&T ksh fails this, POSIX says the test is correct.
+stdin:
+	showargs() { for i; do echo -n " <$i>"; done; echo; }
+	IFS="$IFS:"
+	set --
+	showargs :"$@"
+expected-stdout:
+	 <:>
+---
+name: IFS-space-colon-4
+description:
+	Simple test, IFS=<white-space>:
+stdin:
+	showargs() { for i; do echo -n " <$i>"; done; echo; }
+	IFS="$IFS:"
+	set --
+	showargs "$@$@"
+expected-stdout:
+	
+---
+name: IFS-space-colon-5
+description:
+	Simple test, IFS=<white-space>:
+	Don't know what POSIX thinks of this.  AT&T ksh does not do this.
+stdin:
+	showargs() { for i; do echo -n " <$i>"; done; echo; }
+	IFS="$IFS:"
+	set --
+	showargs "${@:-}"
+expected-stdout:
+	 <>
+---
+name: IFS-subst-1
+description:
+	Simple test, IFS=<white-space>:
+stdin:
+	showargs() { for i; do echo -n " <$i>"; done; echo; }
+	IFS="$IFS:"
+	x=":b: :"
+	echo -n '1:'; for i in $x ; do echo -n " [$i]" ; done ; echo
+	echo -n '2:'; for i in :b:: ; do echo -n " [$i]" ; done ; echo
+	showargs 3 $x
+	showargs 4 :b::
+	x="a:b:"
+	echo -n '5:'; for i in $x ; do echo -n " [$i]" ; done ; echo
+	showargs 6 $x
+	x="a::c"
+	echo -n '7:'; for i in $x ; do echo -n " [$i]" ; done ; echo
+	showargs 8 $x
+	echo -n '9:'; for i in ${FOO-`echo -n h:i`th:ere} ; do echo -n " [$i]" ; done ; echo
+	showargs 10 ${FOO-`echo -n h:i`th:ere}
+	showargs 11 "${FOO-`echo -n h:i`th:ere}"
+	x=" A :  B::D"
+	echo -n '12:'; for i in $x ; do echo -n " [$i]" ; done ; echo
+	showargs 13 $x
+expected-stdout:
+	1: [] [b] []
+	2: [:b::]
+	 <3> <> <b> <>
+	 <4> <:b::>
+	5: [a] [b]
+	 <6> <a> <b>
+	7: [a] [] [c]
+	 <8> <a> <> <c>
+	9: [h] [ith] [ere]
+	 <10> <h> <ith> <ere>
+	 <11> <h:ith:ere>
+	12: [A] [B] [] [D]
+	 <13> <A> <B> <> <D>
+---
+name: IFS-subst-2
+description:
+	Check leading whitespace after trim does not make a field
+stdin:
+	showargs() { for i; do echo -n " <$i>"; done; echo; }
+	x="X 1 2"
+	showargs 1 shift ${x#X}
+expected-stdout:
+	 <1> <shift> <1> <2>
+---
+name: IFS-arith-1
+description:
+	http://austingroupbugs.net/view.php?id=832
+stdin:
+	${ZSH_VERSION+false} || emulate sh
+	${BASH_VERSION+set -o posix}
+	showargs() { for x in "$@"; do echo -n "<$x> "; done; echo .; }
+	IFS=0
+	showargs $((1230456))
+expected-stdout:
+	<123> <456> .
+---
+name: integer-base-err-1
+description:
+	Can't have 0 base (causes shell to exit)
+expected-exit: e != 0
+stdin:
+	typeset -i i
+	i=3
+	i=0#4
+	echo $i
+expected-stderr-pattern:
+	/^.*:.*0#4.*\n$/
+---
+name: integer-base-err-2
+description:
+	Can't have multiple bases in a 'constant' (causes shell to exit)
+	(ksh88 fails this test)
+expected-exit: e != 0
+stdin:
+	typeset -i i
+	i=3
+	i=2#110#11
+	echo $i
+expected-stderr-pattern:
+	/^.*:.*2#110#11.*\n$/
+---
+name: integer-base-err-3
+description:
+	Syntax errors in expressions and effects on bases
+	(interactive so errors don't cause exits)
+	(ksh88 fails this test - shell exits, even with -i)
+need-ctty: yes
+arguments: !-i!
+stdin:
+	PS1= # minimise prompt hassles
+	typeset -i4 a=10
+	typeset -i a=2+
+	echo $a
+	typeset -i4 a=10
+	typeset -i2 a=2+
+	echo $a
+expected-stderr-pattern:
+	/^([#\$] )?.*:.*2+.*\n.*:.*2+.*\n$/
+expected-stdout:
+	4#22
+	4#22
+---
+name: integer-base-err-4
+description:
+	Are invalid digits (according to base) errors?
+	(ksh93 fails this test)
+expected-exit: e != 0
+stdin:
+	typeset -i i;
+	i=3#4
+expected-stderr-pattern:
+	/^([#\$] )?.*:.*3#4.*\n$/
+---
+name: integer-base-1
+description:
+	Missing number after base is treated as 0.
+stdin:
+	typeset -i i
+	i=3
+	i=2#
+	echo $i
+expected-stdout:
+	0
+---
+name: integer-base-2
+description:
+	Check 'stickyness' of base in various situations
+stdin:
+	typeset -i i=8
+	echo $i
+	echo ---------- A
+	typeset -i4 j=8
+	echo $j
+	echo ---------- B
+	typeset -i k=8
+	typeset -i4 k=8
+	echo $k
+	echo ---------- C
+	typeset -i4 l
+	l=3#10
+	echo $l
+	echo ---------- D
+	typeset -i m
+	m=3#10
+	echo $m
+	echo ---------- E
+	n=2#11
+	typeset -i n
+	echo $n
+	n=10
+	echo $n
+	echo ---------- F
+	typeset -i8 o=12
+	typeset -i4 o
+	echo $o
+	echo ---------- G
+	typeset -i p
+	let p=8#12
+	echo $p
+expected-stdout:
+	8
+	---------- A
+	4#20
+	---------- B
+	4#20
+	---------- C
+	4#3
+	---------- D
+	3#10
+	---------- E
+	2#11
+	2#1010
+	---------- F
+	4#30
+	---------- G
+	8#12
+---
+name: integer-base-3
+description:
+	More base parsing (hmm doesn't test much..)
+stdin:
+	typeset -i aa
+	aa=1+12#10+2
+	echo $aa
+	typeset -i bb
+	bb=1+$aa
+	echo $bb
+	typeset -i bb
+	bb=$aa
+	echo $bb
+	typeset -i cc
+	cc=$aa
+	echo $cc
+expected-stdout:
+	15
+	16
+	15
+	15
+---
+name: integer-base-4
+description:
+	Check that things not declared as integers are not made integers,
+	also, check if base is not reset by -i with no arguments.
+	(ksh93 fails - prints 10#20 - go figure)
+stdin:
+	xx=20
+	let xx=10
+	typeset -i | grep '^xx='
+	typeset -i4 a=10
+	typeset -i a=20
+	echo $a
+expected-stdout:
+	4#110
+---
+name: integer-base-5
+description:
+	More base stuff
+stdin:
+	typeset -i4 a=3#10
+	echo $a
+	echo --
+	typeset -i j=3
+	j='~3'
+	echo $j
+	echo --
+	typeset -i k=1
+	x[k=k+1]=3
+	echo $k
+	echo --
+	typeset -i l
+	for l in 1 2+3 4; do echo $l; done
+expected-stdout:
+	4#3
+	--
+	-4
+	--
+	2
+	--
+	1
+	5
+	4
+---
+name: integer-base-6
+description:
+	Even more base stuff
+	(ksh93 fails this test - prints 0)
+stdin:
+	typeset -i7 i
+	i=
+	echo $i
+expected-stdout:
+	7#0
+---
+name: integer-base-7
+description:
+	Check that non-integer parameters don't get bases assigned
+stdin:
+	echo $(( zz = 8#100 ))
+	echo $zz
+expected-stdout:
+	64
+	64
+---
+name: integer-base-check-flat
+description:
+	Check behaviour does not match POSuX (except if set -o posix),
+	because a not type-safe scripting language has *no* business
+	interpreting the string "010" as octal numer eight (dangerous).
+stdin:
+	echo 1 "$("$__progname" -c 'echo :$((10))/$((010)),$((0x10)):')" .
+	echo 2 "$("$__progname" -o posix -c 'echo :$((10))/$((010)),$((0x10)):')" .
+	echo 3 "$("$__progname" -o sh -c 'echo :$((10))/$((010)),$((0x10)):')" .
+expected-stdout:
+	1 :10/10,16: .
+	2 :10/8,16: .
+	3 :10/10,16: .
+---
+name: integer-base-check-numeric-from
+description:
+	Check behaviour for base one to 36, and that 37 errors out
+stdin:
+	echo 1:$((1#1))0.
+	i=1
+	while (( ++i <= 36 )); do
+		eval 'echo '$i':$(('$i'#10)).'
+	done
+	echo 37:$($__progname -c 'echo $((37#10))').$?:
+expected-stdout:
+	1:490.
+	2:2.
+	3:3.
+	4:4.
+	5:5.
+	6:6.
+	7:7.
+	8:8.
+	9:9.
+	10:10.
+	11:11.
+	12:12.
+	13:13.
+	14:14.
+	15:15.
+	16:16.
+	17:17.
+	18:18.
+	19:19.
+	20:20.
+	21:21.
+	22:22.
+	23:23.
+	24:24.
+	25:25.
+	26:26.
+	27:27.
+	28:28.
+	29:29.
+	30:30.
+	31:31.
+	32:32.
+	33:33.
+	34:34.
+	35:35.
+	36:36.
+	37:.0:
+expected-stderr-pattern:
+	/.*bad number '37#10'/
+---
+name: integer-base-check-numeric-to
+description:
+	Check behaviour for base one to 36, and that 37 errors out
+stdin:
+	i=0
+	while (( ++i <= 37 )); do
+		typeset -Uui$i x=0x40
+		eval "typeset -i10 y=$x"
+		print $i:$x.$y.
+	done
+expected-stdout:
+	1:1#@.64.
+	2:2#1000000.64.
+	3:3#2101.64.
+	4:4#1000.64.
+	5:5#224.64.
+	6:6#144.64.
+	7:7#121.64.
+	8:8#100.64.
+	9:9#71.64.
+	10:64.64.
+	11:11#59.64.
+	12:12#54.64.
+	13:13#4C.64.
+	14:14#48.64.
+	15:15#44.64.
+	16:16#40.64.
+	17:17#3D.64.
+	18:18#3A.64.
+	19:19#37.64.
+	20:20#34.64.
+	21:21#31.64.
+	22:22#2K.64.
+	23:23#2I.64.
+	24:24#2G.64.
+	25:25#2E.64.
+	26:26#2C.64.
+	27:27#2A.64.
+	28:28#28.64.
+	29:29#26.64.
+	30:30#24.64.
+	31:31#22.64.
+	32:32#20.64.
+	33:33#1V.64.
+	34:34#1U.64.
+	35:35#1T.64.
+	36:36#1S.64.
+	37:36#1S.64.
+expected-stderr-pattern:
+	/.*bad integer base: 37/
+---
+name: integer-arithmetic-span
+description:
+	Check wraparound and size that is defined in mksh
+category: int:32
+stdin:
+	echo s:$((2147483647+1)).$(((2147483647*2)+1)).$(((2147483647*2)+2)).
+	echo u:$((#2147483647+1)).$((#(2147483647*2)+1)).$((#(2147483647*2)+2)).
+expected-stdout:
+	s:-2147483648.-1.0.
+	u:2147483648.4294967295.0.
+---
+name: integer-arithmetic-span-64
+description:
+	Check wraparound and size that is defined in mksh
+category: int:64
+stdin:
+	echo s:$((9223372036854775807+1)).$(((9223372036854775807*2)+1)).$(((9223372036854775807*2)+2)).
+	echo u:$((#9223372036854775807+1)).$((#(9223372036854775807*2)+1)).$((#(9223372036854775807*2)+2)).
+expected-stdout:
+	s:-9223372036854775808.-1.0.
+	u:9223372036854775808.18446744073709551615.0.
+---
+name: integer-size-FAIL-to-detect
+description:
+	Notify the user that their ints are not 32 or 64 bit
+category: int:u
+stdin:
+	:
+---
+name: lineno-stdin
+description:
+	See if $LINENO is updated and can be modified.
+stdin:
+	echo A $LINENO
+	echo B $LINENO
+	LINENO=20
+	echo C $LINENO
+expected-stdout:
+	A 1
+	B 2
+	C 20
+---
+name: lineno-inc
+description:
+	See if $LINENO is set for .'d files.
+file-setup: file 644 "dotfile"
+	echo dot A $LINENO
+	echo dot B $LINENO
+	LINENO=20
+	echo dot C $LINENO
+stdin:
+	echo A $LINENO
+	echo B $LINENO
+	. ./dotfile
+expected-stdout:
+	A 1
+	B 2
+	dot A 1
+	dot B 2
+	dot C 20
+---
+name: lineno-func
+description:
+	See if $LINENO is set for commands in a function.
+stdin:
+	echo A $LINENO
+	echo B $LINENO
+	bar() {
+	    echo func A $LINENO
+	    echo func B $LINENO
+	}
+	bar
+	echo C $LINENO
+expected-stdout:
+	A 1
+	B 2
+	func A 4
+	func B 5
+	C 8
+---
+name: lineno-unset
+description:
+	See if unsetting LINENO makes it non-magic.
+file-setup: file 644 "dotfile"
+	echo dot A $LINENO
+	echo dot B $LINENO
+stdin:
+	unset LINENO
+	echo A $LINENO
+	echo B $LINENO
+	bar() {
+	    echo func A $LINENO
+	    echo func B $LINENO
+	}
+	bar
+	. ./dotfile
+	echo C $LINENO
+expected-stdout:
+	A
+	B
+	func A
+	func B
+	dot A
+	dot B
+	C
+---
+name: lineno-unset-use
+description:
+	See if unsetting LINENO makes it non-magic even
+	when it is re-used.
+file-setup: file 644 "dotfile"
+	echo dot A $LINENO
+	echo dot B $LINENO
+stdin:
+	unset LINENO
+	LINENO=3
+	echo A $LINENO
+	echo B $LINENO
+	bar() {
+	    echo func A $LINENO
+	    echo func B $LINENO
+	}
+	bar
+	. ./dotfile
+	echo C $LINENO
+expected-stdout:
+	A 3
+	B 3
+	func A 3
+	func B 3
+	dot A 3
+	dot B 3
+	C 3
+---
+name: lineno-trap
+description:
+	Check if LINENO is tracked in traps
+stdin:
+	fail() {
+		echo "line <$1>"
+		exit 1
+	}
+	trap 'fail $LINENO' INT ERR
+	false
+expected-stdout:
+	line <6>
+expected-exit: 1
+---
+name: unknown-trap
+description:
+	Ensure unknown traps are not a syntax error
+stdin:
+	(
+	trap "echo trap 1 executed" UNKNOWNSIGNAL || echo "foo"
+	echo =1
+	trap "echo trap 2 executed" UNKNOWNSIGNAL EXIT 999999 FNORD
+	echo = $?
+	) 2>&1 | sed "s^${__progname%.exe}\.*e*x*e*: <stdin>\[[0-9]*]PROG"
+expected-stdout:
+	PROG: trap: bad signal 'UNKNOWNSIGNAL'
+	foo
+	=1
+	PROG: trap: bad signal 'UNKNOWNSIGNAL'
+	PROG: trap: bad signal '999999'
+	PROG: trap: bad signal 'FNORD'
+	= 3
+	trap 2 executed
+---
+name: read-IFS-1
+description:
+	Simple test, default IFS
+stdin:
+	echo "A B " > IN
+	unset x y z
+	read x y z < IN
+	echo 1: "x[$x] y[$y] z[$z]"
+	echo 1a: ${z-z not set}
+	read x < IN
+	echo 2: "x[$x]"
+expected-stdout:
+	1: x[A] y[B] z[]
+	1a:
+	2: x[A B]
+---
+name: read-ksh-1
+description:
+	If no var specified, REPLY is used
+stdin:
+	echo "abc" > IN
+	read < IN
+	echo "[$REPLY]";
+expected-stdout:
+	[abc]
+---
+name: read-regress-1
+description:
+	Check a regression of read
+file-setup: file 644 "foo"
+	foo bar
+	baz
+	blah
+stdin:
+	while read a b c; do
+		read d
+		break
+	done <foo
+	echo "<$a|$b|$c><$d>"
+expected-stdout:
+	<foo|bar|><baz>
+---
+name: read-delim-1
+description:
+	Check read with delimiters
+stdin:
+	emit() {
+		print -n 'foo bar\tbaz\nblah \0blub\tblech\nmyok meck \0'
+	}
+	emit | while IFS= read -d "" foo; do print -r -- "<$foo>"; done
+	emit | while read -d "" foo; do print -r -- "<$foo>"; done
+	emit | while read -d "eh?" foo; do print -r -- "<$foo>"; done
+expected-stdout:
+	<foo bar	baz
+	blah >
+	<blub	blech
+	myok meck >
+	<foo bar	baz
+	blah>
+	<blub	blech
+	myok meck>
+	<foo bar	baz
+	blah blub	bl>
+	<ch
+	myok m>
+---
+name: read-ext-1
+description:
+	Check read with number of bytes specified, and -A
+stdin:
+	print 'foo\nbar' >x1
+	print -n x >x2
+	print 'foo\\ bar baz' >x3
+	x1a=u; read x1a <x1
+	x1b=u; read -N-1 x1b <x1
+	x2a=u; read x2a <x2; r2a=$?
+	x2b=u; read -N2 x2c <x2; r2b=$?
+	x2c=u; read -n2 x2c <x2; r2c=$?
+	x3a=u; read -A x3a <x3
+	print -r "x1a=<$x1a>"
+	print -r "x1b=<$x1b>"
+	print -r "x2a=$r2a<$x2a>"
+	print -r "x2b=$r2b<$x2b>"
+	print -r "x2c=$r2c<$x2c>"
+	print -r "x3a=<${x3a[0]}|${x3a[1]}|${x3a[2]}>"
+expected-stdout:
+	x1a=<foo>
+	x1b=<foo
+	bar>
+	x2a=1<x>
+	x2b=1<u>
+	x2c=0<x>
+	x3a=<foo bar|baz|>
+---
+name: regression-1
+description:
+	Lex array code had problems with this.
+stdin:
+	echo foo[
+	n=bar
+	echo "hi[ $n ]=1"
+expected-stdout:
+	foo[
+	hi[ bar ]=1
+---
+name: regression-2
+description:
+	When PATH is set before running a command, the new path is
+	not used in doing the path search
+		$ echo echo hi > /tmp/q ; chmod a+rx /tmp/q
+		$ PATH=/tmp q
+		q: not found
+		$
+	in comexec() the two lines
+		while (*vp != NULL)
+			(void) typeset(*vp++, xxx, 0);
+	need to be moved out of the switch to before findcom() is
+	called - I don't know what this will break.
+stdin:
+	: ${PWD:-`pwd 2> /dev/null`}
+	: ${PWD:?"PWD not set - can't do test"}
+	mkdir Y
+	cat > Y/xxxscript << EOF
+	#!/bin/sh
+	# Need to restore path so echo can be found (some shells don't have
+	# it as a built-in)
+	PATH=\$OLDPATH
+	echo hi
+	exit 0
+	EOF
+	chmod a+rx Y/xxxscript
+	export OLDPATH="$PATH"
+	PATH=$PWD/Y xxxscript
+	exit $?
+expected-stdout:
+	hi
+---
+name: regression-6
+description:
+	Parsing of $(..) expressions is non-optimal.  It is
+	impossible to have any parentheses inside the expression.
+	I.e.,
+		$ ksh -c 'echo $(echo \( )'
+		no closing quote
+		$ ksh -c 'echo $(echo "(" )'
+		no closing quote
+		$
+	The solution is to hack the parsing clode in lex.c, the
+	question is how to hack it: should any parentheses be
+	escaped by a backslash, or should recursive parsing be done
+	(so quotes could also be used to hide hem).  The former is
+	easier, the later better...
+stdin:
+	echo $(echo \( )
+	echo $(echo "(" )
+expected-stdout:
+	(
+	(
+---
+name: regression-9
+description:
+	Continue in a for loop does not work right:
+		for i in a b c ; do
+			if [ $i = b ] ; then
+				continue
+			fi
+			echo $i
+		done
+	Prints a forever...
+stdin:
+	first=yes
+	for i in a b c ; do
+		if [ $i = b ] ; then
+			if [ $first = no ] ; then
+				echo 'continue in for loop broken'
+				break	# hope break isn't broken too :-)
+			fi
+			first=no
+			continue
+		fi
+	done
+	echo bye
+expected-stdout:
+	bye
+---
+name: regression-10
+description:
+	The following:
+		set -- `false`
+		echo $?
+	should print 0 according to POSIX (dash, bash, ksh93, posh)
+	but not 0 according to the getopt(1) manual page, ksh88, and
+	Bourne sh (such as /bin/sh on Solaris).
+	We honour POSIX except when -o sh is set.
+category: shell:legacy-no
+stdin:
+	showf() {
+		[[ -o posix ]]; FPOSIX=$((1-$?))
+		[[ -o sh ]]; FSH=$((1-$?))
+		echo -n "FPOSIX=$FPOSIX FSH=$FSH "
+	}
+	set +o posix +o sh
+	showf
+	set -- `false`
+	echo rv=$?
+	set -o sh
+	showf
+	set -- `false`
+	echo rv=$?
+	set -o posix
+	showf
+	set -- `false`
+	echo rv=$?
+	set -o posix -o sh
+	showf
+	set -- `false`
+	echo rv=$?
+expected-stdout:
+	FPOSIX=0 FSH=0 rv=0
+	FPOSIX=0 FSH=1 rv=1
+	FPOSIX=1 FSH=0 rv=0
+	FPOSIX=1 FSH=1 rv=0
+---
+name: regression-10-legacy
+description:
+	The following:
+		set -- `false`
+		echo $?
+	should print 0 according to POSIX (dash, bash, ksh93, posh)
+	but not 0 according to the getopt(1) manual page, ksh88, and
+	Bourne sh (such as /bin/sh on Solaris).
+category: shell:legacy-yes
+stdin:
+	showf() {
+		[[ -o posix ]]; FPOSIX=$((1-$?))
+		[[ -o sh ]]; FSH=$((1-$?))
+		echo -n "FPOSIX=$FPOSIX FSH=$FSH "
+	}
+	set +o posix +o sh
+	showf
+	set -- `false`
+	echo rv=$?
+	set -o sh
+	showf
+	set -- `false`
+	echo rv=$?
+	set -o posix
+	showf
+	set -- `false`
+	echo rv=$?
+	set -o posix -o sh
+	showf
+	set -- `false`
+	echo rv=$?
+expected-stdout:
+	FPOSIX=0 FSH=0 rv=1
+	FPOSIX=0 FSH=1 rv=1
+	FPOSIX=1 FSH=0 rv=0
+	FPOSIX=1 FSH=1 rv=0
+---
+name: regression-11
+description:
+	The following:
+		x=/foo/bar/blah
+		echo ${x##*/}
+	should echo blah but on some machines echos /foo/bar/blah.
+stdin:
+	x=/foo/bar/blah
+	echo ${x##*/}
+expected-stdout:
+	blah
+---
+name: regression-12
+description:
+	Both of the following echos produce the same output under sh/ksh.att:
+		#!/bin/sh
+		x="foo	bar"
+		echo "`echo \"$x\"`"
+		echo "`echo "$x"`"
+	pdksh produces different output for the former (foo instead of foo\tbar)
+stdin:
+	x="foo	bar"
+	echo "`echo \"$x\"`"
+	echo "`echo "$x"`"
+expected-stdout:
+	foo	bar
+	foo	bar
+---
+name: regression-13
+description:
+	The following command hangs forever:
+		$ (: ; cat /etc/termcap) | sleep 2
+	This is because the shell forks a shell to run the (..) command
+	and this shell has the pipe open.  When the sleep dies, the cat
+	doesn't get a SIGPIPE 'cause a process (ie, the second shell)
+	still has the pipe open.
+	
+	NOTE: this test provokes a bizarre bug in ksh93 (shell starts reading
+	      commands from /etc/termcap..)
+time-limit: 10
+stdin:
+	echo A line of text that will be duplicated quite a number of times.> t1
+	cat t1 t1 t1 t1  t1 t1 t1 t1  t1 t1 t1 t1  t1 t1 t1 t1  > t2
+	cat t2 t2 t2 t2  t2 t2 t2 t2  t2 t2 t2 t2  t2 t2 t2 t2  > t1
+	cat t1 t1 t1 t1 > t2
+	(: ; cat t2 2>/dev/null) | sleep 1
+---
+name: regression-14
+description:
+	The command
+		$ (foobar) 2> /dev/null
+	generates no output under /bin/sh, but pdksh produces the error
+		foobar: not found
+	Also, the command
+		$ foobar 2> /dev/null
+	generates an error under /bin/sh and pdksh, but AT&T ksh88 produces
+	no error (redirected to /dev/null).
+stdin:
+	(you/should/not/see/this/error/1) 2> /dev/null
+	you/should/not/see/this/error/2 2> /dev/null
+	true
+---
+name: regression-15
+description:
+	The command
+		$ whence foobar
+	generates a blank line under pdksh and sets the exit status to 0.
+	AT&T ksh88 generates no output and sets the exit status to 1.  Also,
+	the command
+		$ whence foobar cat
+	generates no output under AT&T ksh88 (pdksh generates a blank line
+	and /bin/cat).
+stdin:
+	whence does/not/exist > /dev/null
+	echo 1: $?
+	echo 2: $(whence does/not/exist | wc -l)
+	echo 3: $(whence does/not/exist cat | wc -l)
+expected-stdout:
+	1: 1
+	2: 0
+	3: 0
+---
+name: regression-16
+description:
+	${var%%expr} seems to be broken in many places.  On the mips
+	the commands
+		$ read line < /etc/passwd
+		$ echo $line
+		root:0:1:...
+		$ echo ${line%%:*}
+		root
+		$ echo $line
+		root
+		$
+	change the value of line.  On sun4s & pas, the echo ${line%%:*} doesn't
+	work.  Haven't checked elsewhere...
+script:
+	read x
+	y=$x
+	echo ${x%%:*}
+	echo $x
+stdin:
+	root:asdjhasdasjhs:0:1:Root:/:/bin/sh
+expected-stdout:
+	root
+	root:asdjhasdasjhs:0:1:Root:/:/bin/sh
+---
+name: regression-17
+description:
+	The command
+		. /foo/bar
+	should set the exit status to non-zero (sh and AT&T ksh88 do).
+	XXX doting a non existent file is a fatal error for a script
+stdin:
+	. does/not/exist
+expected-exit: e != 0
+expected-stderr-pattern: /.?/
+---
+name: regression-19
+description:
+	Both of the following echos should produce the same thing, but don't:
+		$ x=foo/bar
+		$ echo ${x%/*}
+		foo
+		$ echo "${x%/*}"
+		foo/bar
+stdin:
+	x=foo/bar
+	echo "${x%/*}"
+expected-stdout:
+	foo
+---
+name: regression-21
+description:
+	backslash does not work as expected in case labels:
+	$ x='-x'
+	$ case $x in
+	-\?) echo hi
+	esac
+	hi
+	$ x='-?'
+	$ case $x in
+	-\\?) echo hi
+	esac
+	hi
+	$
+stdin:
+	case -x in
+	-\?)	echo fail
+	esac
+---
+name: regression-22
+description:
+	Quoting backquotes inside backquotes doesn't work:
+	$ echo `echo hi \`echo there\` folks`
+	asks for more info.  sh and AT&T ksh88 both echo
+	hi there folks
+stdin:
+	echo `echo hi \`echo there\` folks`
+expected-stdout:
+	hi there folks
+---
+name: regression-23
+description:
+	)) is not treated `correctly':
+	    $ (echo hi ; (echo there ; echo folks))
+	    missing ((
+	    $
+	instead of (as sh and ksh.att)
+	    $ (echo hi ; (echo there ; echo folks))
+	    hi
+	    there
+	    folks
+	    $
+stdin:
+	( : ; ( : ; echo hi))
+expected-stdout:
+	hi
+---
+name: regression-25
+description:
+	Check reading stdin in a while loop.  The read should only read
+	a single line, not a whole stdio buffer; the cat should get
+	the rest.
+stdin:
+	(echo a; echo b) | while read x ; do
+	    echo $x
+	    cat > /dev/null
+	done
+expected-stdout:
+	a
+---
+name: regression-26
+description:
+	Check reading stdin in a while loop.  The read should read both
+	lines, not just the first.
+script:
+	a=
+	while [ "$a" != xxx ] ; do
+	    last=$x
+	    read x
+	    cat /dev/null | sed 's/x/y/'
+	    a=x$a
+	done
+	echo $last
+stdin:
+	a
+	b
+expected-stdout:
+	b
+---
+name: regression-27
+description:
+	The command
+		. /does/not/exist
+	should cause a script to exit.
+stdin:
+	. does/not/exist
+	echo hi
+expected-exit: e != 0
+expected-stderr-pattern: /does\/not\/exist/
+---
+name: regression-28
+description:
+	variable assignements not detected well
+stdin:
+	a.x=1 echo hi
+expected-exit: e != 0
+expected-stderr-pattern: /a\.x=1/
+---
+name: regression-29
+description:
+	alias expansion different from AT&T ksh88
+stdin:
+	alias a='for ' b='i in'
+	a b hi ; do echo $i ; done
+expected-stdout:
+	hi
+---
+name: regression-30
+description:
+	strange characters allowed inside ${...}
+stdin:
+	echo ${a{b}}
+expected-exit: e != 0
+expected-stderr-pattern: /.?/
+---
+name: regression-31
+description:
+	Does read handle partial lines correctly
+script:
+	a= ret=
+	while [ "$a" != xxx ] ; do
+	    read x y z
+	    ret=$?
+	    a=x$a
+	done
+	echo "[$x]"
+	echo $ret
+stdin: !
+	a A aA
+	b B Bb
+	c
+expected-stdout:
+	[c]
+	1
+---
+name: regression-32
+description:
+	Does read set variables to null at eof?
+script:
+	a=
+	while [ "$a" != xxx ] ; do
+	    read x y z
+	    a=x$a
+	done
+	echo 1: ${x-x not set} ${y-y not set} ${z-z not set}
+	echo 2: ${x:+x not null} ${y:+y not null} ${z:+z not null}
+stdin:
+	a A Aa
+	b B Bb
+expected-stdout:
+	1:
+	2:
+---
+name: regression-33
+description:
+	Does umask print a leading 0 when umask is 3 digits?
+stdin:
+	# on MiNT, the first umask call seems to fail
+	umask 022
+	# now, the test proper
+	umask 222
+	umask
+expected-stdout:
+	0222
+---
+name: regression-35
+description:
+	Tempory files used for here-docs in functions get trashed after
+	the function is parsed (before it is executed)
+stdin:
+	f1() {
+		cat <<- EOF
+			F1
+		EOF
+		f2() {
+			cat <<- EOF
+				F2
+			EOF
+		}
+	}
+	f1
+	f2
+	unset -f f1
+	f2
+expected-stdout:
+	F1
+	F2
+	F2
+---
+name: regression-36
+description:
+	Command substitution breaks reading in while loop
+	(test from <sjg at void.zen.oz.au>)
+stdin:
+	(echo abcdef; echo; echo 123) |
+	    while read line
+	    do
+	      # the following line breaks it
+	      c=`echo $line | wc -c`
+	      echo $c
+	    done
+expected-stdout:
+	7
+	1
+	4
+---
+name: regression-37
+description:
+	Machines with broken times() (reported by <sjg at void.zen.oz.au>)
+	time does not report correct real time
+stdin:
+	time sleep 1
+expected-stderr-pattern: !/^\s*0\.0[\s\d]+real|^\s*real[\s]+0+\.0/
+---
+name: regression-38
+description:
+	set -e doesn't ignore exit codes for if/while/until/&&/||/!.
+arguments: !-e!
+stdin:
+	if false; then echo hi ; fi
+	false || true
+	false && true
+	while false; do echo hi; done
+	echo ok
+expected-stdout:
+	ok
+---
+name: regression-39
+description:
+	Only posh and oksh(2013-07) say “hi” below; FreeBSD sh,
+	GNU bash in POSIX mode, dash, ksh93, mksh don’t. All of
+	them exit 0. The POSIX behaviour is needed by BSD make.
+stdin:
+	set -e
+	echo `false; echo hi` $(<this-file-does-not-exist)
+	echo $?
+expected-stdout:
+	
+	0
+expected-stderr-pattern: /this-file-does-not-exist/
+---
+name: regression-40
+description:
+	This used to cause a core dump
+env-setup: !RANDOM=12!
+stdin:
+	echo hi
+expected-stdout:
+	hi
+---
+name: regression-41
+description:
+	foo should be set to bar (should not be empty)
+stdin:
+	foo=`
+	echo bar`
+	echo "($foo)"
+expected-stdout:
+	(bar)
+---
+name: regression-42
+description:
+	Can't use command line assignments to assign readonly parameters.
+stdin:
+	print '#!'"$__progname"'\nunset RANDOM\nexport | while IFS= read -r' \
+	    'RANDOM; do eval '\''print -r -- "$RANDOM=$'\''"$RANDOM"'\'\"\'\; \
+	    done >env; chmod +x env; PATH=.:$PATH
+	foo=bar
+	readonly foo
+	foo=stuff env | grep '^foo'
+expected-exit: e != 0
+expected-stderr-pattern:
+	/read-only/
+---
+name: regression-43
+description:
+	Can subshells be prefixed by redirections (historical shells allow
+	this)
+stdin:
+	< /dev/null (sed 's/^/X/')
+---
+name: regression-45
+description:
+	Parameter assignments with [] recognised correctly
+stdin:
+	FOO=*[12]
+	BAR=abc[
+	MORE=[abc]
+	JUNK=a[bc
+	echo "<$FOO>"
+	echo "<$BAR>"
+	echo "<$MORE>"
+	echo "<$JUNK>"
+expected-stdout:
+	<*[12]>
+	<abc[>
+	<[abc]>
+	<a[bc>
+---
+name: regression-46
+description:
+	Check that alias expansion works in command substitutions and
+	at the end of file.
+stdin:
+	alias x='echo hi'
+	FOO="`x` "
+	echo "[$FOO]"
+	x
+expected-stdout:
+	[hi ]
+	hi
+---
+name: regression-47
+description:
+	Check that aliases are fully read.
+stdin:
+	alias x='echo hi;
+	echo there'
+	x
+	echo done
+expected-stdout:
+	hi
+	there
+	done
+---
+name: regression-48
+description:
+	Check that (here doc) temp files are not left behind after an exec.
+stdin:
+	mkdir foo || exit 1
+	TMPDIR=$PWD/foo "$__progname" <<- 'EOF'
+		x() {
+			sed 's/^/X /' << E_O_F
+			hi
+			there
+			folks
+			E_O_F
+			echo "done ($?)"
+		}
+		echo=echo; [ -x /bin/echo ] && echo=/bin/echo
+		exec $echo subtest-1 hi
+	EOF
+	echo subtest-1 foo/*
+	TMPDIR=$PWD/foo "$__progname" <<- 'EOF'
+		echo=echo; [ -x /bin/echo ] && echo=/bin/echo
+		sed 's/^/X /' << E_O_F; exec $echo subtest-2 hi
+		a
+		few
+		lines
+		E_O_F
+	EOF
+	echo subtest-2 foo/*
+expected-stdout:
+	subtest-1 hi
+	subtest-1 foo/*
+	X a
+	X few
+	X lines
+	subtest-2 hi
+	subtest-2 foo/*
+---
+name: regression-49
+description:
+	Check that unset params with attributes are reported by set, those
+	sans attributes are not.
+stdin:
+	unset FOO BAR
+	echo X$FOO
+	export BAR
+	typeset -i BLAH
+	set | grep FOO
+	set | grep BAR
+	set | grep BLAH
+expected-stdout:
+	X
+	BAR
+	BLAH
+---
+name: regression-50
+description:
+	Check that aliases do not use continuation prompt after trailing
+	semi-colon.
+file-setup: file 644 "envf"
+	PS1=Y
+	PS2=X
+env-setup: !ENV=./envf!
+need-ctty: yes
+arguments: !-i!
+stdin:
+	alias foo='echo hi ; '
+	foo
+	foo echo there
+expected-stdout:
+	hi
+	hi
+	there
+expected-stderr: !
+	YYYY
+---
+name: regression-51
+description:
+	Check that set allows both +o and -o options on same command line.
+stdin:
+	set a b c
+	set -o noglob +o allexport
+	echo A: $*, *
+expected-stdout:
+	A: a b c, *
+---
+name: regression-52
+description:
+	Check that globbing works in pipelined commands
+file-setup: file 644 "envf"
+	PS1=P
+file-setup: file 644 "abc"
+	stuff
+env-setup: !ENV=./envf!
+need-ctty: yes
+arguments: !-i!
+stdin:
+	sed 's/^/X /' < ab*
+	echo mark 1
+	sed 's/^/X /' < ab* | sed 's/^/Y /'
+	echo mark 2
+expected-stdout:
+	X stuff
+	mark 1
+	Y X stuff
+	mark 2
+expected-stderr: !
+	PPPPP
+---
+name: regression-53
+description:
+	Check that getopts works in functions
+stdin:
+	bfunc() {
+	    echo bfunc: enter "(args: $*; OPTIND=$OPTIND)"
+	    while getopts B oc; do
+		case $oc in
+		  (B)
+		    echo bfunc: B option
+		    ;;
+		  (*)
+		    echo bfunc: odd option "($oc)"
+		    ;;
+		esac
+	    done
+	    echo bfunc: leave
+	}
+	
+	function kfunc {
+	    echo kfunc: enter "(args: $*; OPTIND=$OPTIND)"
+	    while getopts K oc; do
+		case $oc in
+		  (K)
+		    echo kfunc: K option
+		    ;;
+		  (*)
+		    echo bfunc: odd option "($oc)"
+		    ;;
+		esac
+	    done
+	    echo kfunc: leave
+	}
+	
+	set -- -f -b -k -l
+	echo "line 1: OPTIND=$OPTIND"
+	getopts kbfl optc
+	echo "line 2: ret=$?, optc=$optc, OPTIND=$OPTIND"
+	bfunc -BBB blah
+	echo "line 3: OPTIND=$OPTIND"
+	getopts kbfl optc
+	echo "line 4: ret=$?, optc=$optc, OPTIND=$OPTIND"
+	kfunc -KKK blah
+	echo "line 5: OPTIND=$OPTIND"
+	getopts kbfl optc
+	echo "line 6: ret=$?, optc=$optc, OPTIND=$OPTIND"
+	echo
+	
+	OPTIND=1
+	set -- -fbkl
+	echo "line 10: OPTIND=$OPTIND"
+	getopts kbfl optc
+	echo "line 20: ret=$?, optc=$optc, OPTIND=$OPTIND"
+	bfunc -BBB blah
+	echo "line 30: OPTIND=$OPTIND"
+	getopts kbfl optc
+	echo "line 40: ret=$?, optc=$optc, OPTIND=$OPTIND"
+	kfunc -KKK blah
+	echo "line 50: OPTIND=$OPTIND"
+	getopts kbfl optc
+	echo "line 60: ret=$?, optc=$optc, OPTIND=$OPTIND"
+expected-stdout:
+	line 1: OPTIND=1
+	line 2: ret=0, optc=f, OPTIND=2
+	bfunc: enter (args: -BBB blah; OPTIND=2)
+	bfunc: B option
+	bfunc: B option
+	bfunc: leave
+	line 3: OPTIND=2
+	line 4: ret=0, optc=b, OPTIND=3
+	kfunc: enter (args: -KKK blah; OPTIND=1)
+	kfunc: K option
+	kfunc: K option
+	kfunc: K option
+	kfunc: leave
+	line 5: OPTIND=3
+	line 6: ret=0, optc=k, OPTIND=4
+	
+	line 10: OPTIND=1
+	line 20: ret=0, optc=f, OPTIND=2
+	bfunc: enter (args: -BBB blah; OPTIND=2)
+	bfunc: B option
+	bfunc: B option
+	bfunc: leave
+	line 30: OPTIND=2
+	line 40: ret=1, optc=?, OPTIND=2
+	kfunc: enter (args: -KKK blah; OPTIND=1)
+	kfunc: K option
+	kfunc: K option
+	kfunc: K option
+	kfunc: leave
+	line 50: OPTIND=2
+	line 60: ret=1, optc=?, OPTIND=2
+---
+name: regression-54
+description:
+	Check that ; is not required before the then in if (( ... )) then ...
+stdin:
+	if (( 1 )) then
+	    echo ok dparen
+	fi
+	if [[ -n 1 ]] then
+	    echo ok dbrackets
+	fi
+expected-stdout:
+	ok dparen
+	ok dbrackets
+---
+name: regression-55
+description:
+	Check ${foo:%bar} is allowed (ksh88 allows it...)
+stdin:
+	x=fooXbarXblah
+	echo 1 ${x%X*}
+	echo 2 ${x:%X*}
+	echo 3 ${x%%X*}
+	echo 4 ${x:%%X*}
+	echo 5 ${x#*X}
+	echo 6 ${x:#*X}
+	echo 7 ${x##*X}
+	echo 8 ${x:##*X}
+expected-stdout:
+	1 fooXbar
+	2 fooXbar
+	3 foo
+	4 foo
+	5 barXblah
+	6 barXblah
+	7 blah
+	8 blah
+---
+name: regression-57
+description:
+	Check if typeset output is correct for
+	uninitialised array elements.
+stdin:
+	typeset -i xxx[4]
+	echo A
+	typeset -i | grep xxx | sed 's/^/    /'
+	echo B
+	typeset | grep xxx | sed 's/^/    /'
+	
+	xxx[1]=2+5
+	echo M
+	typeset -i | grep xxx | sed 's/^/    /'
+	echo N
+	typeset | grep xxx | sed 's/^/    /'
+expected-stdout:
+	A
+	    xxx
+	B
+	    typeset -i xxx
+	M
+	    xxx[1]=7
+	N
+	    set -A xxx
+	    typeset -i xxx[1]
+---
+name: regression-58
+description:
+	Check if trap exit is ok (exit not mistaken for signal name)
+stdin:
+	trap 'echo hi' exit
+	trap exit 1
+expected-stdout:
+	hi
+---
+name: regression-59
+description:
+	Check if ${#array[*]} is calculated correctly.
+stdin:
+	a[12]=hi
+	a[8]=there
+	echo ${#a[*]}
+expected-stdout:
+	2
+---
+name: regression-60
+description:
+	Check if default exit status is previous command
+stdin:
+	(true; exit)
+	echo A $?
+	(false; exit)
+	echo B $?
+	( (exit 103) ; exit)
+	echo C $?
+expected-stdout:
+	A 0
+	B 1
+	C 103
+---
+name: regression-61
+description:
+	Check if EXIT trap is executed for sub shells.
+stdin:
+	trap 'echo parent exit' EXIT
+	echo start
+	(echo A; echo A last)
+	echo B
+	(echo C; trap 'echo sub exit' EXIT; echo C last)
+	echo parent last
+expected-stdout:
+	start
+	A
+	A last
+	B
+	C
+	C last
+	sub exit
+	parent last
+	parent exit
+---
+name: regression-62
+description:
+	Check if test -nt/-ot succeeds if second(first) file is missing.
+stdin:
+	touch a
+	test a -nt b && echo nt OK || echo nt BAD
+	test b -ot a && echo ot OK || echo ot BAD
+expected-stdout:
+	nt OK
+	ot OK
+---
+name: regression-63
+description:
+	Check if typeset, export, and readonly work
+stdin:
+	{
+		echo FNORD-0
+		FNORD_A=1
+		FNORD_B=2
+		FNORD_C=3
+		FNORD_D=4
+		FNORD_E=5
+		FNORD_F=6
+		FNORD_G=7
+		FNORD_H=8
+		integer FNORD_E FNORD_F FNORD_G FNORD_H
+		export FNORD_C FNORD_D FNORD_G FNORD_H
+		readonly FNORD_B FNORD_D FNORD_F FNORD_H
+		echo FNORD-1
+		export
+		echo FNORD-2
+		export -p
+		echo FNORD-3
+		readonly
+		echo FNORD-4
+		readonly -p
+		echo FNORD-5
+		typeset
+		echo FNORD-6
+		typeset -p
+		echo FNORD-7
+		typeset -
+		echo FNORD-8
+	} | fgrep FNORD
+	fnord=(42 23)
+	typeset -p fnord
+	echo FNORD-9
+expected-stdout:
+	FNORD-0
+	FNORD-1
+	FNORD_C
+	FNORD_D
+	FNORD_G
+	FNORD_H
+	FNORD-2
+	export FNORD_C=3
+	export FNORD_D=4
+	export FNORD_G=7
+	export FNORD_H=8
+	FNORD-3
+	FNORD_B
+	FNORD_D
+	FNORD_F
+	FNORD_H
+	FNORD-4
+	readonly FNORD_B=2
+	readonly FNORD_D=4
+	readonly FNORD_F=6
+	readonly FNORD_H=8
+	FNORD-5
+	typeset FNORD_A
+	typeset -r FNORD_B
+	typeset -x FNORD_C
+	typeset -x -r FNORD_D
+	typeset -i FNORD_E
+	typeset -i -r FNORD_F
+	typeset -i -x FNORD_G
+	typeset -i -x -r FNORD_H
+	FNORD-6
+	typeset FNORD_A=1
+	typeset -r FNORD_B=2
+	typeset -x FNORD_C=3
+	typeset -x -r FNORD_D=4
+	typeset -i FNORD_E=5
+	typeset -i -r FNORD_F=6
+	typeset -i -x FNORD_G=7
+	typeset -i -x -r FNORD_H=8
+	FNORD-7
+	FNORD_A=1
+	FNORD_B=2
+	FNORD_C=3
+	FNORD_D=4
+	FNORD_E=5
+	FNORD_F=6
+	FNORD_G=7
+	FNORD_H=8
+	FNORD-8
+	set -A fnord
+	typeset fnord[0]=42
+	typeset fnord[1]=23
+	FNORD-9
+---
+name: regression-64
+description:
+	Check that we can redefine functions calling time builtin
+stdin:
+	t() {
+		time >/dev/null
+	}
+	t 2>/dev/null
+	t() {
+		time
+	}
+---
+name: regression-65
+description:
+	check for a regression with sleep builtin and signal mask
+category: !nojsig
+time-limit: 3
+stdin:
+	sleep 1
+	echo blub |&
+	while read -p line; do :; done
+	echo ok
+expected-stdout:
+	ok
+---
+name: regression-66
+description:
+	Check that quoting is sane
+category: !nojsig
+stdin:
+	ac_space=' '
+	ac_newline='
+	'
+	set | grep ^ac_ |&
+	set -A lines
+	while IFS= read -pr line; do
+		if [[ $line = *space* ]]; then
+			lines[0]=$line
+		else
+			lines[1]=$line
+		fi
+	done
+	for line in "${lines[@]}"; do
+		print -r -- "$line"
+	done
+expected-stdout:
+	ac_space=' '
+	ac_newline=$'\n'
+---
+name: readonly-0
+description:
+	Ensure readonly is honoured for assignments and unset
+stdin:
+	"$__progname" -c 'u=x; echo $? $u .' || echo aborted, $?
+	echo =
+	"$__progname" -c 'readonly u; u=x; echo $? $u .' || echo aborted, $?
+	echo =
+	"$__progname" -c 'u=x; readonly u; unset u; echo $? $u .' || echo aborted, $?
+expected-stdout:
+	0 x .
+	=
+	aborted, 2
+	=
+	1 x .
+expected-stderr-pattern:
+	/read-only/
+---
+name: readonly-1
+description:
+	http://austingroupbugs.net/view.php?id=367 for export
+stdin:
+	"$__progname" -c 'readonly foo; export foo=a; echo $?' || echo aborted, $?
+expected-stdout:
+	aborted, 2
+expected-stderr-pattern:
+	/read-only/
+---
+name: readonly-2a
+description:
+	Check that getopts works as intended, for readonly-2b to be valid
+stdin:
+	"$__progname" -c 'set -- -a b; getopts a c; echo $? $c .; getopts a c; echo $? $c .' || echo aborted, $?
+expected-stdout:
+	0 a .
+	1 ? .
+---
+name: readonly-2b
+description:
+	http://austingroupbugs.net/view.php?id=367 for getopts
+stdin:
+	"$__progname" -c 'readonly c; set -- -a b; getopts a c; echo $? $c .' || echo aborted, $?
+expected-stdout:
+	2 .
+expected-stderr-pattern:
+	/read-only/
+---
+name: readonly-3
+description:
+	http://austingroupbugs.net/view.php?id=367 for read
+stdin:
+	echo x | "$__progname" -c 'read s; echo $? $s .' || echo aborted, $?
+	echo y | "$__progname" -c 'readonly s; read s; echo $? $s .' || echo aborted, $?
+expected-stdout:
+	0 x .
+	2 .
+expected-stderr-pattern:
+	/read-only/
+---
+name: readonly-4
+description:
+	Do not permit bypassing readonly for first array item
+stdin:
+	set -A arr -- foo bar
+	readonly arr
+	arr=baz
+	print -r -- "${arr[@]}"
+expected-exit: e != 0
+expected-stderr-pattern:
+	/read[ -]?only/
+---
+name: syntax-1
+description:
+	Check that lone ampersand is a syntax error
+stdin:
+	 &
+expected-exit: e != 0
+expected-stderr-pattern:
+	/syntax error/
+---
+name: xxx-quoted-newline-1
+description:
+	Check that \<newline> works inside of ${}
+stdin:
+	abc=2
+	echo ${ab\
+	c}
+expected-stdout:
+	2
+---
+name: xxx-quoted-newline-2
+description:
+	Check that \<newline> works at the start of a here document
+stdin:
+	cat << EO\
+	F
+	hi
+	EOF
+expected-stdout:
+	hi
+---
+name: xxx-quoted-newline-3
+description:
+	Check that \<newline> works at the end of a here document
+stdin:
+	cat << EOF
+	hi
+	EO\
+	F
+expected-stdout:
+	hi
+---
+name: xxx-multi-assignment-cmd
+description:
+	Check that assignments in a command affect subsequent assignments
+	in the same command
+stdin:
+	FOO=abc
+	FOO=123 BAR=$FOO
+	echo $BAR
+expected-stdout:
+	123
+---
+name: xxx-multi-assignment-posix-cmd
+description:
+	Check that the behaviour for multiple assignments with a
+	command name matches POSIX. See:
+	http://thread.gmane.org/gmane.comp.standards.posix.austin.general/1925
+stdin:
+	X=a Y=b; X=$Y Y=$X "$__progname" -c 'echo 1 $X $Y .'; echo 2 $X $Y .
+	unset X Y Z
+	X=a Y=${X=b} Z=$X "$__progname" -c 'echo 3 $Z .'
+	unset X Y Z
+	X=a Y=${X=b} Z=$X; echo 4 $Z .
+expected-stdout:
+	1 b a .
+	2 a b .
+	3 b .
+	4 a .
+---
+name: xxx-multi-assignment-posix-nocmd
+description:
+	Check that the behaviour for multiple assignments with no
+	command name matches POSIX (Debian #334182). See:
+	http://thread.gmane.org/gmane.comp.standards.posix.austin.general/1925
+stdin:
+	X=a Y=b; X=$Y Y=$X; echo 1 $X $Y .
+expected-stdout:
+	1 b b .
+---
+name: xxx-multi-assignment-posix-subassign
+description:
+	Check that the behaviour for multiple assignments matches POSIX:
+	- The assignment words shall be expanded in the current execution
+	  environment.
+	- The assignments happen in the temporary execution environment.
+stdin:
+	unset X Y Z
+	Z=a Y=${X:=b} sh -c 'echo +$X+ +$Y+ +$Z+'
+	echo /$X/
+	# Now for the special case:
+	unset X Y Z
+	X= Y=${X:=b} sh -c 'echo +$X+ +$Y+'
+	echo /$X/
+expected-stdout:
+	++ +b+ +a+
+	/b/
+	++ +b+
+	/b/
+---
+name: xxx-exec-environment-1
+description:
+	Check to see if exec sets it's environment correctly
+stdin:
+	print '#!'"$__progname"'\nunset RANDOM\nexport | while IFS= read -r' \
+	    'RANDOM; do eval '\''print -r -- "$RANDOM=$'\''"$RANDOM"'\'\"\'\; \
+	    done >env; chmod +x env; PATH=.:$PATH
+	FOO=bar exec env
+expected-stdout-pattern:
+	/(^|.*\n)FOO=bar\n/
+---
+name: xxx-exec-environment-2
+description:
+	Check to make sure exec doesn't change environment if a program
+	isn't exec-ed
+stdin:
+	print '#!'"$__progname"'\nunset RANDOM\nexport | while IFS= read -r' \
+	    'RANDOM; do eval '\''print -r -- "$RANDOM=$'\''"$RANDOM"'\'\"\'\; \
+	    done >env; chmod +x env; PATH=.:$PATH
+	env >bar1
+	FOO=bar exec; env >bar2
+	cmp -s bar1 bar2
+---
+name: exec-function-environment-1
+description:
+	Check assignments in function calls and whether they affect
+	the current execution environment (ksh93, SUSv4)
+stdin:
+	f() { a=2; }; g() { b=3; echo y$c-; }; a=1 f; b=2; c=1 g
+	echo x$a-$b- z$c-
+expected-stdout:
+	y1-
+	x2-3- z1-
+---
+name: xxx-what-do-you-call-this-1
+stdin:
+	echo "${foo:-"a"}*"
+expected-stdout:
+	a*
+---
+name: xxx-prefix-strip-1
+stdin:
+	foo='a cdef'
+	echo ${foo#a c}
+expected-stdout:
+	def
+---
+name: xxx-prefix-strip-2
+stdin:
+	set a c
+	x='a cdef'
+	echo ${x#$*}
+expected-stdout:
+	def
+---
+name: xxx-variable-syntax-1
+stdin:
+	echo ${:}
+expected-stderr-pattern:
+	/bad substitution/
+expected-exit: 1
+---
+name: xxx-variable-syntax-2
+stdin:
+	set 0
+	echo ${*:0}
+expected-stderr-pattern:
+	/bad substitution/
+expected-exit: 1
+---
+name: xxx-variable-syntax-3
+stdin:
+	set -A foo 0
+	echo ${foo[*]:0}
+expected-stderr-pattern:
+	/bad substitution/
+expected-exit: 1
+---
+name: xxx-substitution-eval-order
+description:
+	Check order of evaluation of expressions
+stdin:
+	i=1 x= y=
+	set -A A abc def GHI j G k
+	echo ${A[x=(i+=1)]#${A[y=(i+=2)]}}
+	echo $x $y
+expected-stdout:
+	HI
+	2 4
+---
+name: xxx-set-option-1
+description:
+	Check option parsing in set
+stdin:
+	set -vsA foo -- A 1 3 2
+	echo ${foo[*]}
+expected-stderr:
+	echo ${foo[*]}
+expected-stdout:
+	1 2 3 A
+---
+name: xxx-exec-1
+description:
+	Check that exec exits for built-ins
+need-ctty: yes
+arguments: !-i!
+stdin:
+	exec echo hi
+	echo still herre
+expected-stdout:
+	hi
+expected-stderr-pattern: /.*/
+---
+name: xxx-while-1
+description:
+	Check the return value of while loops
+	XXX need to do same for for/select/until loops
+stdin:
+	i=x
+	while [ $i != xxx ] ; do
+	    i=x$i
+	    if [ $i = xxx ] ; then
+		false
+		continue
+	    fi
+	done
+	echo loop1=$?
+	
+	i=x
+	while [ $i != xxx ] ; do
+	    i=x$i
+	    if [ $i = xxx ] ; then
+		false
+		break
+	    fi
+	done
+	echo loop2=$?
+	
+	i=x
+	while [ $i != xxx ] ; do
+	    i=x$i
+	    false
+	done
+	echo loop3=$?
+expected-stdout:
+	loop1=0
+	loop2=0
+	loop3=1
+---
+name: xxx-status-1
+description:
+	Check that blank lines don't clear $?
+need-ctty: yes
+arguments: !-i!
+stdin:
+	(exit 1)
+	echo $?
+	(exit 1)
+	
+	echo $?
+	true
+expected-stdout:
+	1
+	1
+expected-stderr-pattern: /.*/
+---
+name: xxx-status-2
+description:
+	Check that $? is preserved in subshells, includes, traps.
+stdin:
+	(exit 1)
+	
+	echo blank: $?
+	
+	(exit 2)
+	(echo subshell: $?)
+	
+	echo 'echo include: $?' > foo
+	(exit 3)
+	. ./foo
+	
+	trap 'echo trap: $?' ERR
+	(exit 4)
+	echo exit: $?
+expected-stdout:
+	blank: 1
+	subshell: 2
+	include: 3
+	trap: 4
+	exit: 4
+---
+name: xxx-clean-chars-1
+description:
+	Check MAGIC character is stuffed correctly
+stdin:
+	echo `echo [\xA3`
+expected-stdout:
+	[\xA3
+---
+name: xxx-param-subst-qmark-1
+description:
+	Check suppresion of error message with null string.  According to
+	POSIX, it shouldn't print the error as 'word' isn't ommitted.
+	ksh88/93, Solaris /bin/sh and /usr/xpg4/bin/sh all print the error,
+	that's why the condition is reversed.
+stdin:
+	unset foo
+	x=
+	echo x${foo?$x}
+expected-exit: 1
+# POSIX
+#expected-fail: yes
+#expected-stderr-pattern: !/not set/
+# common use
+expected-stderr-pattern: /parameter null or not set/
+---
+name: xxx-param-_-1
+# fails due to weirdness of execv stuff
+category: !os:uwin-nt
+description:
+	Check c flag is set.
+arguments: !-c!echo "[$-]"!
+expected-stdout-pattern: /^\[.*c.*\]$/
+---
+name: tilde-expand-1
+description:
+	Check tilde expansion after equal signs
+env-setup: !HOME=/sweet!
+stdin:
+	echo ${A=a=}~ b=~ c=d~ ~
+	set +o braceexpand
+	unset A
+	echo ${A=a=}~ b=~ c=d~ ~
+expected-stdout:
+	a=/sweet b=/sweet c=d~ /sweet
+	a=~ b=~ c=d~ /sweet
+---
+name: tilde-expand-2
+description:
+	Check tilde expansion works
+env-setup: !HOME=/sweet!
+stdin:
+	wd=$PWD
+	cd /
+	plus=$(print -r -- ~+)
+	minus=$(print -r -- ~-)
+	nix=$(print -r -- ~)
+	[[ $plus = / ]]; echo one $? .
+	[[ $minus = "$wd" ]]; echo two $? .
+	[[ $nix = /sweet ]]; echo nix $? .
+expected-stdout:
+	one 0 .
+	two 0 .
+	nix 0 .
+---
+name: exit-err-1
+description:
+	Check some "exit on error" conditions
+stdin:
+	print '#!'"$__progname"'\nexec "$1"' >env
+	print '#!'"$__progname"'\nexit 1' >false
+	chmod +x env false
+	PATH=.:$PATH
+	set -ex
+	env false && echo something
+	echo END
+expected-stdout:
+	END
+expected-stderr:
+	+ env false
+	+ echo END
+---
+name: exit-err-2
+description:
+	Check some "exit on error" edge conditions (POSIXly)
+stdin:
+	print '#!'"$__progname"'\nexec "$1"' >env
+	print '#!'"$__progname"'\nexit 1' >false
+	print '#!'"$__progname"'\nexit 0' >true
+	chmod +x env false
+	PATH=.:$PATH
+	set -ex
+	if env true; then
+		env false && echo something
+	fi
+	echo END
+expected-stdout:
+	END
+expected-stderr:
+	+ env true
+	+ env false
+	+ echo END
+---
+name: exit-err-3
+description:
+	pdksh regression which AT&T ksh does right
+	TFM says: [set] -e | errexit
+		Exit (after executing the ERR trap) ...
+stdin:
+	trap 'echo EXIT' EXIT
+	trap 'echo ERR' ERR
+	set -e
+	cd /XXXXX 2>/dev/null
+	echo DONE
+	exit 0
+expected-stdout:
+	ERR
+	EXIT
+expected-exit: e != 0
+---
+name: exit-err-4
+description:
+	"set -e" test suite (POSIX)
+stdin:
+	set -e
+	echo pre
+	if true ; then
+		false && echo foo
+	fi
+	echo bar
+expected-stdout:
+	pre
+	bar
+---
+name: exit-err-5
+description:
+	"set -e" test suite (POSIX)
+stdin:
+	set -e
+	foo() {
+		while [ "$1" ]; do
+			for E in $x; do
+				[ "$1" = "$E" ] && { shift ; continue 2 ; }
+			done
+			x="$x $1"
+			shift
+		done
+		echo $x
+	}
+	echo pre
+	foo a b b c
+	echo post
+expected-stdout:
+	pre
+	a b c
+	post
+---
+name: exit-err-6
+description:
+	"set -e" test suite (BSD make)
+category: os:mirbsd
+stdin:
+	mkdir zd zd/a zd/b
+	print 'all:\n\t at echo eins\n\t at exit 42\n' >zd/a/Makefile
+	print 'all:\n\t at echo zwei\n' >zd/b/Makefile
+	wd=$(pwd)
+	set -e
+	for entry in a b; do (  set -e;  if [[ -d $wd/zd/$entry.i386 ]]; then  _newdir_="$entry.i386";  else  _newdir_="$entry";  fi;  if [[ -z $_THISDIR_ ]]; then  _nextdir_="$_newdir_";  else  _nextdir_="$_THISDIR_/$_newdir_";  fi;  _makefile_spec_=;  [[ ! -f $wd/zd/$_newdir_/Makefile.bsd-wrapper ]]  || _makefile_spec_="-f Makefile.bsd-wrapper";  subskipdir=;  for skipdir in ; do  subentry=${skipdir#$entry};  if [[ $subentry != $skipdir ]]; then  if [[ -z $subentry ]]; then  echo "($_nextdir_ skipped)";  break;  fi;  subskipdir="$subskipdir ${subentry#/}";  fi;  done;  if [[ -z $skipdir || -n $subentry ]]; then  echo "===> $_nextdir_";  cd $wd/zd/$_newdir_;  make SKIPDIR="$subskipdir" $_makefile_spec_  _THISDIR_="$_nextdir_"   all;  fi;  ) done 2>&1 | sed "s!$wd!WD!g"
+expected-stdout:
+	===> a
+	eins
+	*** Error code 42
+	
+	Stop in WD/zd/a (line 2 of Makefile).
+---
+name: exit-err-7
+description:
+	"set -e" regression (LP#1104543)
+stdin:
+	set -e
+	bla() {
+		[ -x $PWD/nonexistant ] && $PWD/nonexistant
+	}
+	echo x
+	bla
+	echo y$?
+expected-stdout:
+	x
+expected-exit: 1
+---
+name: exit-err-8
+description:
+	"set -e" regression (Debian #700526)
+stdin:
+	set -e
+	_db_cmd() { return $1; }
+	db_input() { _db_cmd 30; }
+	db_go() { _db_cmd 0; }
+	db_input || :
+	db_go
+	exit 0
+---
+name: exit-enoent-1
+description:
+	SUSv4 says that the shell should exit with 126/127 in some situations
+stdin:
+	i=0
+	(echo; echo :) >x
+	"$__progname" ./x >/dev/null 2>&1; r=$?; echo $((i++)) $r .
+	"$__progname" -c ./x >/dev/null 2>&1; r=$?; echo $((i++)) $r .
+	echo exit 42 >x
+	"$__progname" ./x >/dev/null 2>&1; r=$?; echo $((i++)) $r .
+	"$__progname" -c ./x >/dev/null 2>&1; r=$?; echo $((i++)) $r .
+	rm -f x
+	"$__progname" ./x >/dev/null 2>&1; r=$?; echo $((i++)) $r .
+	"$__progname" -c ./x >/dev/null 2>&1; r=$?; echo $((i++)) $r .
+expected-stdout:
+	0 0 .
+	1 126 .
+	2 42 .
+	3 126 .
+	4 127 .
+	5 127 .
+---
+name: exit-eval-1
+description:
+	Check eval vs substitution exit codes (ksh93 alike)
+stdin:
+	(exit 12)
+	eval $(false)
+	echo A $?
+	(exit 12)
+	eval ' $(false)'
+	echo B $?
+	(exit 12)
+	eval " $(false)"
+	echo C $?
+	(exit 12)
+	eval "eval $(false)"
+	echo D $?
+	(exit 12)
+	eval 'eval '"$(false)"
+	echo E $?
+	IFS="$IFS:"
+	(exit 12)
+	eval $(echo :; false)
+	echo F $?
+	echo -n "G "
+	(exit 12)
+	eval 'echo $?'
+	echo H $?
+expected-stdout:
+	A 0
+	B 1
+	C 0
+	D 0
+	E 0
+	F 0
+	G 12
+	H 0
+---
+name: exit-trap-1
+description:
+	Check that "exit" with no arguments behaves SUSv4 conformant.
+stdin:
+	trap 'echo hi; exit' EXIT
+	exit 9
+expected-stdout:
+	hi
+expected-exit: 9
+---
+name: exit-trap-2
+description:
+	Check that ERR and EXIT traps are run just like ksh93 does.
+	GNU bash does not run ERtrap in ±e eval-undef but runs it
+	twice (bug?) in +e eval-false, so does ksh93 (bug?), which
+	also has a bug to continue execution (echoing "and out" and
+	returning 0) in +e eval-undef.
+file-setup: file 644 "x"
+	v=; unset v
+	trap 'echo EXtrap' EXIT
+	trap 'echo ERtrap' ERR
+	set $1
+	echo "and run $2"
+	eval $2
+	echo and out
+file-setup: file 644 "xt"
+	v=; unset v
+	trap 'echo EXtrap' EXIT
+	trap 'echo ERtrap' ERR
+	set $1
+	echo 'and run true'
+	true
+	echo and out
+file-setup: file 644 "xf"
+	v=; unset v
+	trap 'echo EXtrap' EXIT
+	trap 'echo ERtrap' ERR
+	set $1
+	echo 'and run false'
+	false
+	echo and out
+file-setup: file 644 "xu"
+	v=; unset v
+	trap 'echo EXtrap' EXIT
+	trap 'echo ERtrap' ERR
+	set $1
+	echo 'and run ${v?}'
+	${v?}
+	echo and out
+stdin:
+	runtest() {
+		rm -f rc
+		(
+			"$__progname" "$@"
+			echo $? >rc
+		) 2>&1 | sed \
+		    -e 's/parameter not set/parameter null or not set/' \
+		    -e 's/[[]6]//' -e 's/: eval: line 1//' -e 's/: line 6//' \
+		    -e "s^${__progname%.exe}\.*e*x*e*: <stdin>\[[0-9]*]PROG"
+	}
+	xe=-e
+	echo : $xe
+	runtest x $xe true
+	echo = eval-true $(<rc) .
+	runtest x $xe false
+	echo = eval-false $(<rc) .
+	runtest x $xe '${v?}'
+	echo = eval-undef $(<rc) .
+	runtest xt $xe
+	echo = noeval-true $(<rc) .
+	runtest xf $xe
+	echo = noeval-false $(<rc) .
+	runtest xu $xe
+	echo = noeval-undef $(<rc) .
+	xe=+e
+	echo : $xe
+	runtest x $xe true
+	echo = eval-true $(<rc) .
+	runtest x $xe false
+	echo = eval-false $(<rc) .
+	runtest x $xe '${v?}'
+	echo = eval-undef $(<rc) .
+	runtest xt $xe
+	echo = noeval-true $(<rc) .
+	runtest xf $xe
+	echo = noeval-false $(<rc) .
+	runtest xu $xe
+	echo = noeval-undef $(<rc) .
+expected-stdout:
+	: -e
+	and run true
+	and out
+	EXtrap
+	= eval-true 0 .
+	and run false
+	ERtrap
+	EXtrap
+	= eval-false 1 .
+	and run ${v?}
+	x: v: parameter null or not set
+	ERtrap
+	EXtrap
+	= eval-undef 1 .
+	and run true
+	and out
+	EXtrap
+	= noeval-true 0 .
+	and run false
+	ERtrap
+	EXtrap
+	= noeval-false 1 .
+	and run ${v?}
+	xu: v: parameter null or not set
+	EXtrap
+	= noeval-undef 1 .
+	: +e
+	and run true
+	and out
+	EXtrap
+	= eval-true 0 .
+	and run false
+	ERtrap
+	and out
+	EXtrap
+	= eval-false 0 .
+	and run ${v?}
+	x: v: parameter null or not set
+	ERtrap
+	EXtrap
+	= eval-undef 1 .
+	and run true
+	and out
+	EXtrap
+	= noeval-true 0 .
+	and run false
+	ERtrap
+	and out
+	EXtrap
+	= noeval-false 0 .
+	and run ${v?}
+	xu: v: parameter null or not set
+	EXtrap
+	= noeval-undef 1 .
+---
+name: exit-trap-interactive
+description:
+	Check that interactive shell doesn't exit via EXIT trap on syntax error
+arguments: !-i!
+stdin:
+	trap -- EXIT
+	echo Syntax error <
+	echo 'After error 1'
+	trap 'echo Exit trap' EXIT
+	echo Syntax error <
+	echo 'After error 2'
+	trap 'echo Exit trap' EXIT
+	exit
+	echo 'After exit'
+expected-stdout:
+	After error 1
+	After error 2
+	Exit trap
+expected-stderr-pattern:
+	/syntax error: 'newline' unexpected/
+---
+name: test-stlt-1
+description:
+	Check that test also can handle string1 < string2 etc.
+stdin:
+	test 2005/10/08 '<' 2005/08/21 && echo ja || echo nein
+	test 2005/08/21 \< 2005/10/08 && echo ja || echo nein
+	test 2005/10/08 '>' 2005/08/21 && echo ja || echo nein
+	test 2005/08/21 \> 2005/10/08 && echo ja || echo nein
+expected-stdout:
+	nein
+	ja
+	ja
+	nein
+expected-stderr-pattern: !/unexpected op/
+---
+name: test-precedence-1
+description:
+	Check a weird precedence case (and POSIX echo)
+stdin:
+	test \( -f = -f \)
+	rv=$?
+	echo $rv
+expected-stdout:
+	0
+---
+name: test-option-1
+description:
+	Test the test -o operator
+stdin:
+	runtest() {
+		test -o $1; echo $?
+		[ -o $1 ]; echo $?
+		[[ -o $1 ]]; echo $?
+	}
+	if_test() {
+		test -o $1 -o -o !$1; echo $?
+		[ -o $1 -o -o !$1 ]; echo $?
+		[[ -o $1 || -o !$1 ]]; echo $?
+		test -o ?$1; echo $?
+	}
+	echo 0y $(if_test utf8-mode) =
+	echo 0n $(if_test utf8-hack) =
+	echo 1= $(runtest utf8-hack) =
+	echo 2= $(runtest !utf8-hack) =
+	echo 3= $(runtest ?utf8-hack) =
+	set +U
+	echo 1+ $(runtest utf8-mode) =
+	echo 2+ $(runtest !utf8-mode) =
+	echo 3+ $(runtest ?utf8-mode) =
+	set -U
+	echo 1- $(runtest utf8-mode) =
+	echo 2- $(runtest !utf8-mode) =
+	echo 3- $(runtest ?utf8-mode) =
+	echo = short flags =
+	echo 0y $(if_test -U) =
+	echo 0y $(if_test +U) =
+	echo 0n $(if_test -_) =
+	echo 0n $(if_test -U-) =
+	echo 1= $(runtest -_) =
+	echo 2= $(runtest !-_) =
+	echo 3= $(runtest ?-_) =
+	set +U
+	echo 1+ $(runtest -U) =
+	echo 2+ $(runtest !-U) =
+	echo 3+ $(runtest ?-U) =
+	echo 1+ $(runtest +U) =
+	echo 2+ $(runtest !+U) =
+	echo 3+ $(runtest ?+U) =
+	set -U
+	echo 1- $(runtest -U) =
+	echo 2- $(runtest !-U) =
+	echo 3- $(runtest ?-U) =
+	echo 1- $(runtest +U) =
+	echo 2- $(runtest !+U) =
+	echo 3- $(runtest ?+U) =
+expected-stdout:
+	0y 0 0 0 0 =
+	0n 1 1 1 1 =
+	1= 1 1 1 =
+	2= 1 1 1 =
+	3= 1 1 1 =
+	1+ 1 1 1 =
+	2+ 0 0 0 =
+	3+ 0 0 0 =
+	1- 0 0 0 =
+	2- 1 1 1 =
+	3- 0 0 0 =
+	= short flags =
+	0y 0 0 0 0 =
+	0y 0 0 0 0 =
+	0n 1 1 1 1 =
+	0n 1 1 1 1 =
+	1= 1 1 1 =
+	2= 1 1 1 =
+	3= 1 1 1 =
+	1+ 1 1 1 =
+	2+ 0 0 0 =
+	3+ 0 0 0 =
+	1+ 1 1 1 =
+	2+ 0 0 0 =
+	3+ 0 0 0 =
+	1- 0 0 0 =
+	2- 1 1 1 =
+	3- 0 0 0 =
+	1- 0 0 0 =
+	2- 1 1 1 =
+	3- 0 0 0 =
+---
+name: mkshrc-1
+description:
+	Check that ~/.mkshrc works correctly.
+	Part 1: verify user environment is not read (internal)
+stdin:
+	echo x $FNORD
+expected-stdout:
+	x
+---
+name: mkshrc-2a
+description:
+	Check that ~/.mkshrc works correctly.
+	Part 2: verify mkshrc is not read (non-interactive shells)
+file-setup: file 644 ".mkshrc"
+	FNORD=42
+env-setup: !HOME=.!ENV=!
+stdin:
+	echo x $FNORD
+expected-stdout:
+	x
+---
+name: mkshrc-2b
+description:
+	Check that ~/.mkshrc works correctly.
+	Part 2: verify mkshrc can be read (interactive shells)
+file-setup: file 644 ".mkshrc"
+	FNORD=42
+need-ctty: yes
+arguments: !-i!
+env-setup: !HOME=.!ENV=!PS1=!
+stdin:
+	echo x $FNORD
+expected-stdout:
+	x 42
+expected-stderr-pattern:
+	/(# )*/
+---
+name: mkshrc-3
+description:
+	Check that ~/.mkshrc works correctly.
+	Part 3: verify mkshrc can be turned off
+file-setup: file 644 ".mkshrc"
+	FNORD=42
+env-setup: !HOME=.!ENV=nonexistant!
+stdin:
+	echo x $FNORD
+expected-stdout:
+	x
+---
+name: sh-mode-1
+description:
+	Check that sh mode turns braceexpand off
+	and that that works correctly
+stdin:
+	set -o braceexpand
+	set +o sh
+	[[ $(set +o) == *@(-o sh)@(| *) ]] && echo sh || echo nosh
+	[[ $(set +o) == *@(-o braceexpand)@(| *) ]] && echo brex || echo nobrex
+	echo {a,b,c}
+	set +o braceexpand
+	echo {a,b,c}
+	set -o braceexpand
+	echo {a,b,c}
+	set -o sh
+	echo {a,b,c}
+	[[ $(set +o) == *@(-o sh)@(| *) ]] && echo sh || echo nosh
+	[[ $(set +o) == *@(-o braceexpand)@(| *) ]] && echo brex || echo nobrex
+	set -o braceexpand
+	echo {a,b,c}
+	[[ $(set +o) == *@(-o sh)@(| *) ]] && echo sh || echo nosh
+	[[ $(set +o) == *@(-o braceexpand)@(| *) ]] && echo brex || echo nobrex
+expected-stdout:
+	nosh
+	brex
+	a b c
+	{a,b,c}
+	a b c
+	{a,b,c}
+	sh
+	nobrex
+	a b c
+	sh
+	brex
+---
+name: sh-mode-2a
+description:
+	Check that posix or sh mode is *not* automatically turned on
+category: !binsh
+stdin:
+	ln -s "$__progname" ksh || cp "$__progname" ksh
+	ln -s "$__progname" sh || cp "$__progname" sh
+	ln -s "$__progname" ./-ksh || cp "$__progname" ./-ksh
+	ln -s "$__progname" ./-sh || cp "$__progname" ./-sh
+	for shell in {,-}{,k}sh; do
+		print -- $shell $(./$shell +l -c \
+		    '[[ $(set +o) == *"-o "@(sh|posix)@(| *) ]] && echo sh || echo nosh')
+	done
+expected-stdout:
+	sh nosh
+	ksh nosh
+	-sh nosh
+	-ksh nosh
+---
+name: sh-mode-2b
+description:
+	Check that posix or sh mode *is* automatically turned on
+category: binsh
+stdin:
+	ln -s "$__progname" ksh || cp "$__progname" ksh
+	ln -s "$__progname" sh || cp "$__progname" sh
+	ln -s "$__progname" ./-ksh || cp "$__progname" ./-ksh
+	ln -s "$__progname" ./-sh || cp "$__progname" ./-sh
+	for shell in {,-}{,k}sh; do
+		print -- $shell $(./$shell +l -c \
+		    '[[ $(set +o) == *"-o "@(sh|posix)@(| *) ]] && echo sh || echo nosh')
+	done
+expected-stdout:
+	sh sh
+	ksh nosh
+	-sh sh
+	-ksh nosh
+---
+name: pipeline-1
+description:
+	pdksh bug: last command of a pipeline is executed in a
+	subshell - make sure it still is, scripts depend on it
+file-setup: file 644 "abcx"
+file-setup: file 644 "abcy"
+stdin:
+	echo *
+	echo a | while read d; do
+		echo $d
+		echo $d*
+		echo *
+		set -o noglob
+		echo $d*
+		echo *
+	done
+	echo *
+expected-stdout:
+	abcx abcy
+	a
+	abcx abcy
+	abcx abcy
+	a*
+	*
+	abcx abcy
+---
+name: pipeline-2
+description:
+	check that co-processes work with TCOMs, TPIPEs and TPARENs
+category: !nojsig
+stdin:
+	"$__progname" -c 'i=100; echo hi |& while read -p line; do echo "$((i++)) $line"; done'
+	"$__progname" -c 'i=200; echo hi | cat |& while read -p line; do echo "$((i++)) $line"; done'
+	"$__progname" -c 'i=300; (echo hi | cat) |& while read -p line; do echo "$((i++)) $line"; done'
+expected-stdout:
+	100 hi
+	200 hi
+	300 hi
+---
+name: pipeline-3
+description:
+	Check that PIPESTATUS does what it's supposed to
+stdin:
+	echo 1 $PIPESTATUS .
+	echo 2 ${PIPESTATUS[0]} .
+	echo 3 ${PIPESTATUS[1]} .
+	(echo x; exit 12) | (cat; exit 23) | (cat; exit 42)
+	echo 5 $? , $PIPESTATUS , ${PIPESTATUS[0]} , ${PIPESTATUS[1]} , ${PIPESTATUS[2]} , ${PIPESTATUS[3]} .
+	echo 6 ${PIPESTATUS[0]} .
+	set | fgrep PIPESTATUS
+	echo 8 $(set | fgrep PIPESTATUS) .
+expected-stdout:
+	1 0 .
+	2 0 .
+	3 .
+	x
+	5 42 , 12 , 12 , 23 , 42 , .
+	6 0 .
+	PIPESTATUS[0]=0
+	8 PIPESTATUS[0]=0 PIPESTATUS[1]=0 .
+---
+name: pipeline-4
+description:
+	Check that "set -o pipefail" does what it's supposed to
+stdin:
+	echo 1 "$("$__progname" -c '(exit 12) | (exit 23) | (exit 42); echo $?')" .
+	echo 2 "$("$__progname" -c '! (exit 12) | (exit 23) | (exit 42); echo $?')" .
+	echo 3 "$("$__progname" -o pipefail -c '(exit 12) | (exit 23) | (exit 42); echo $?')" .
+	echo 4 "$("$__progname" -o pipefail -c '! (exit 12) | (exit 23) | (exit 42); echo $?')" .
+	echo 5 "$("$__progname" -c '(exit 23) | (exit 42) | :; echo $?')" .
+	echo 6 "$("$__progname" -c '! (exit 23) | (exit 42) | :; echo $?')" .
+	echo 7 "$("$__progname" -o pipefail -c '(exit 23) | (exit 42) | :; echo $?')" .
+	echo 8 "$("$__progname" -o pipefail -c '! (exit 23) | (exit 42) | :; echo $?')" .
+	echo 9 "$("$__progname" -o pipefail -c 'x=$( (exit 23) | (exit 42) | :); echo $?')" .
+expected-stdout:
+	1 42 .
+	2 0 .
+	3 42 .
+	4 0 .
+	5 0 .
+	6 1 .
+	7 42 .
+	8 0 .
+	9 42 .
+---
+name: persist-history-1
+description:
+	Check if persistent history saving works
+category: !no-histfile
+need-ctty: yes
+arguments: !-i!
+env-setup: !ENV=./Env!HISTFILE=hist.file!
+file-setup: file 644 "Env"
+	PS1=X
+stdin:
+	cat hist.file
+expected-stdout-pattern:
+	/cat hist.file/
+expected-stderr-pattern:
+	/^X*$/
+---
+name: typeset-1
+description:
+	Check that global does what typeset is supposed to do
+stdin:
+	set -A arrfoo 65
+	foo() {
+		global -Uui16 arrfoo[*]
+	}
+	echo before ${arrfoo[0]} .
+	foo
+	echo after ${arrfoo[0]} .
+	set -A arrbar 65
+	bar() {
+		echo inside before ${arrbar[0]} .
+		arrbar[0]=97
+		echo inside changed ${arrbar[0]} .
+		global -Uui16 arrbar[*]
+		echo inside typeset ${arrbar[0]} .
+		arrbar[0]=48
+		echo inside changed ${arrbar[0]} .
+	}
+	echo before ${arrbar[0]} .
+	bar
+	echo after ${arrbar[0]} .
+expected-stdout:
+	before 65 .
+	after 16#41 .
+	before 65 .
+	inside before 65 .
+	inside changed 97 .
+	inside typeset 16#61 .
+	inside changed 16#30 .
+	after 16#30 .
+---
+name: typeset-padding-1
+description:
+	Check if left/right justification works as per TFM
+stdin:
+	typeset -L10 ln=0hall0
+	typeset -R10 rn=0hall0
+	typeset -ZL10 lz=0hall0
+	typeset -ZR10 rz=0hall0
+	typeset -Z10 rx=" hallo "
+	echo "<$ln> <$rn> <$lz> <$rz> <$rx>"
+expected-stdout:
+	<0hall0    > <    0hall0> <hall0     > <00000hall0> <0000 hallo>
+---
+name: typeset-padding-2
+description:
+	Check if base-!10 integers are padded right
+stdin:
+	typeset -Uui16 -L9 ln=16#1
+	typeset -Uui16 -R9 rn=16#1
+	typeset -Uui16 -Z9 zn=16#1
+	typeset -L9 ls=16#1
+	typeset -R9 rs=16#1
+	typeset -Z9 zs=16#1
+	echo "<$ln> <$rn> <$zn> <$ls> <$rs> <$zs>"
+expected-stdout:
+	<16#1     > <     16#1> <16#000001> <16#1     > <     16#1> <0000016#1>
+---
+name: utf8bom-1
+description:
+	Check that the UTF-8 Byte Order Mark is ignored as the first
+	multibyte character of the shell input (with -c, from standard
+	input, as file, or as eval argument), but nowhere else
+# breaks on Mac OSX (HFS+ non-standard Unicode canonical decomposition)
+category: !os:darwin
+stdin:
+	mkdir foo
+	print '#!/bin/sh\necho ohne' >foo/fnord
+	print '#!/bin/sh\necho mit' >foo/fnord
+	print 'fnord\nfnord\nfnord\nfnord' >foo/bar
+	print eval \''fnord\nfnord\nfnord\nfnord'\' >foo/zoo
+	set -A anzahl -- foo/*
+	echo got ${#anzahl[*]} files
+	chmod +x foo/*
+	export PATH=$(pwd)/foo:$PATH
+	"$__progname" -c 'fnord'
+	echo =
+	"$__progname" -c 'fnord; fnord; fnord; fnord'
+	echo =
+	"$__progname" foo/bar
+	echo =
+	"$__progname" <foo/bar
+	echo =
+	"$__progname" foo/zoo
+	echo =
+	"$__progname" -c 'echo : $(fnord)'
+	rm -rf foo
+expected-stdout:
+	got 4 files
+	ohne
+	=
+	ohne
+	ohne
+	mit
+	ohne
+	=
+	ohne
+	ohne
+	mit
+	ohne
+	=
+	ohne
+	ohne
+	mit
+	ohne
+	=
+	ohne
+	ohne
+	mit
+	ohne
+	=
+	: ohne
+---
+name: utf8bom-2
+description:
+	Check that we can execute BOM-shebangs (failures not fatal)
+	XXX if the OS can already execute them, we lose
+	note: cygwin execve(2) doesn't return to us with ENOEXEC, we lose
+	note: Ultrix perl5 t4 returns 65280 (exit-code 255) and no text
+need-pass: no
+category: !os:cygwin,!os:msys,!os:ultrix,!os:uwin-nt,!smksh
+env-setup: !FOO=BAR!
+stdin:
+	print '#!'"$__progname"'\nprint "1 a=$ENV{FOO}";' >t1
+	print '#!'"$__progname"'\nprint "2 a=$ENV{FOO}";' >t2
+	print '#!'"$__perlname"'\nprint "3 a=$ENV{FOO}\n";' >t3
+	print '#!'"$__perlname"'\nprint "4 a=$ENV{FOO}\n";' >t4
+	chmod +x t?
+	./t1
+	./t2
+	./t3
+	./t4
+expected-stdout:
+	1 a=/nonexistant{FOO}
+	2 a=/nonexistant{FOO}
+	3 a=BAR
+	4 a=BAR
+expected-stderr-pattern:
+	/(Unrecognized character .... ignored at \..t4 line 1)*/
+---
+name: utf8opt-1a
+description:
+	Check that the utf8-mode flag is not set at non-interactive startup
+category: !os:hpux
+env-setup: !PS1=!PS2=!LC_CTYPE=en_US.UTF-8!
+stdin:
+	if [[ $- = *U* ]]; then
+		echo is set
+	else
+		echo is not set
+	fi
+expected-stdout:
+	is not set
+---
+name: utf8opt-1b
+description:
+	Check that the utf8-mode flag is not set at non-interactive startup
+category: os:hpux
+env-setup: !PS1=!PS2=!LC_CTYPE=en_US.utf8!
+stdin:
+	if [[ $- = *U* ]]; then
+		echo is set
+	else
+		echo is not set
+	fi
+expected-stdout:
+	is not set
+---
+name: utf8opt-2a
+description:
+	Check that the utf8-mode flag is set at interactive startup.
+	-DMKSH_ASSUME_UTF8=0 => expected failure, please ignore
+	-DMKSH_ASSUME_UTF8=1 => not expected, please investigate
+	-UMKSH_ASSUME_UTF8 => not expected, but if your OS is old,
+	 try passing HAVE_SETLOCALE_CTYPE=0 to Build.sh
+need-pass: no
+category: !os:hpux,!os:msys
+need-ctty: yes
+arguments: !-i!
+env-setup: !PS1=!PS2=!LC_CTYPE=en_US.UTF-8!
+stdin:
+	if [[ $- = *U* ]]; then
+		echo is set
+	else
+		echo is not set
+	fi
+expected-stdout:
+	is set
+expected-stderr-pattern:
+	/(# )*/
+---
+name: utf8opt-2b
+description:
+	Check that the utf8-mode flag is set at interactive startup
+	Expected failure if -DMKSH_ASSUME_UTF8=0
+category: os:hpux
+need-ctty: yes
+arguments: !-i!
+env-setup: !PS1=!PS2=!LC_CTYPE=en_US.utf8!
+stdin:
+	if [[ $- = *U* ]]; then
+		echo is set
+	else
+		echo is not set
+	fi
+expected-stdout:
+	is set
+expected-stderr-pattern:
+	/(# )*/
+---
+name: utf8opt-3a
+description:
+	Ensure ±U on the command line is honoured
+	(these two tests may pass falsely depending on CPPFLAGS)
+stdin:
+	export i=0
+	code='if [[ $- = *U* ]]; then echo $i on; else echo $i off; fi'
+	let i++; "$__progname" -U -c "$code"
+	let i++; "$__progname" +U -c "$code"
+	echo $((++i)) done
+expected-stdout:
+	1 on
+	2 off
+	3 done
+---
+name: utf8opt-3b
+description:
+	Ensure ±U on the command line is honoured, interactive shells
+need-ctty: yes
+stdin:
+	export i=0
+	code='if [[ $- = *U* ]]; then echo $i on; else echo $i off; fi'
+	let i++; "$__progname" -U -ic "$code"
+	let i++; "$__progname" +U -ic "$code"
+	echo $((++i)) done
+expected-stdout:
+	1 on
+	2 off
+	3 done
+---
+name: aliases-1
+description:
+	Check if built-in shell aliases are okay
+category: !android,!arge
+stdin:
+	alias
+	typeset -f
+expected-stdout:
+	autoload='typeset -fu'
+	functions='typeset -f'
+	hash='alias -t'
+	history='fc -l'
+	integer='typeset -i'
+	local=typeset
+	login='exec login'
+	nameref='typeset -n'
+	nohup='nohup '
+	r='fc -e -'
+	source='PATH=$PATH:. command .'
+	stop='kill -STOP'
+	type='whence -v'
+---
+name: aliases-1-hartz4
+description:
+	Check if built-in shell aliases are okay
+category: android,arge
+stdin:
+	alias
+	typeset -f
+expected-stdout:
+	autoload='typeset -fu'
+	functions='typeset -f'
+	hash='alias -t'
+	history='fc -l'
+	integer='typeset -i'
+	local=typeset
+	login='exec login'
+	nameref='typeset -n'
+	nohup='nohup '
+	r='fc -e -'
+	source='PATH=$PATH:. command .'
+	type='whence -v'
+---
+name: aliases-2a
+description:
+	Check if “set -o sh” disables built-in aliases (except a few)
+category: disabled
+arguments: !-o!sh!
+stdin:
+	alias
+	typeset -f
+expected-stdout:
+	integer='typeset -i'
+	local=typeset
+---
+name: aliases-3a
+description:
+	Check if running as sh disables built-in aliases (except a few)
+category: disabled
+stdin:
+	cp "$__progname" sh
+	./sh -c 'alias; typeset -f'
+	rm -f sh
+expected-stdout:
+	integer='typeset -i'
+	local=typeset
+---
+name: aliases-2b
+description:
+	Check if “set -o sh” does not influence built-in aliases
+category: !android,!arge
+arguments: !-o!sh!
+stdin:
+	alias
+	typeset -f
+expected-stdout:
+	autoload='typeset -fu'
+	functions='typeset -f'
+	hash='alias -t'
+	history='fc -l'
+	integer='typeset -i'
+	local=typeset
+	login='exec login'
+	nameref='typeset -n'
+	nohup='nohup '
+	r='fc -e -'
+	source='PATH=$PATH:. command .'
+	stop='kill -STOP'
+	type='whence -v'
+---
+name: aliases-3b
+description:
+	Check if running as sh does not influence built-in aliases
+category: !android,!arge
+stdin:
+	cp "$__progname" sh
+	./sh -c 'alias; typeset -f'
+	rm -f sh
+expected-stdout:
+	autoload='typeset -fu'
+	functions='typeset -f'
+	hash='alias -t'
+	history='fc -l'
+	integer='typeset -i'
+	local=typeset
+	login='exec login'
+	nameref='typeset -n'
+	nohup='nohup '
+	r='fc -e -'
+	source='PATH=$PATH:. command .'
+	stop='kill -STOP'
+	type='whence -v'
+---
+name: aliases-2b-hartz4
+description:
+	Check if “set -o sh” does not influence built-in aliases
+category: android,arge
+arguments: !-o!sh!
+stdin:
+	alias
+	typeset -f
+expected-stdout:
+	autoload='typeset -fu'
+	functions='typeset -f'
+	hash='alias -t'
+	history='fc -l'
+	integer='typeset -i'
+	local=typeset
+	login='exec login'
+	nameref='typeset -n'
+	nohup='nohup '
+	r='fc -e -'
+	source='PATH=$PATH:. command .'
+	type='whence -v'
+---
+name: aliases-3b-hartz4
+description:
+	Check if running as sh does not influence built-in aliases
+category: android,arge
+stdin:
+	cp "$__progname" sh
+	./sh -c 'alias; typeset -f'
+	rm -f sh
+expected-stdout:
+	autoload='typeset -fu'
+	functions='typeset -f'
+	hash='alias -t'
+	history='fc -l'
+	integer='typeset -i'
+	local=typeset
+	login='exec login'
+	nameref='typeset -n'
+	nohup='nohup '
+	r='fc -e -'
+	source='PATH=$PATH:. command .'
+	type='whence -v'
+---
+name: aliases-cmdline
+description:
+	Check that aliases work from the command line (Debian #517009)
+	Note that due to the nature of the lexing process, defining
+	aliases in COMSUBs then immediately using them, and things
+	like 'alias foo=bar && foo', still fail.
+stdin:
+	"$__progname" -c $'alias a="echo OK"\na'
+expected-stdout:
+	OK
+---
+name: aliases-funcdef-1
+description:
+	Check if POSIX functions take precedences over aliases
+stdin:
+	alias foo='echo makro'
+	foo() {
+		echo funktion
+	}
+	foo
+expected-stdout:
+	funktion
+---
+name: aliases-funcdef-2
+description:
+	Check if POSIX functions take precedences over aliases
+stdin:
+	alias foo='echo makro'
+	foo () {
+		echo funktion
+	}
+	foo
+expected-stdout:
+	funktion
+---
+name: aliases-funcdef-3
+description:
+	Check if aliases take precedences over Korn functions
+stdin:
+	alias foo='echo makro'
+	function foo {
+		echo funktion
+	}
+	foo
+expected-stdout:
+	makro
+---
+name: aliases-funcdef-4
+description:
+	Functions should only take over if actually being defined
+stdin:
+	alias local
+	:|| local() { :; }
+	alias local
+expected-stdout:
+	local=typeset
+	local=typeset
+---
+name: arrays-1
+description:
+	Check if Korn Shell arrays work as expected
+stdin:
+	v="c d"
+	set -A foo -- a \$v "$v" '$v' b
+	echo "${#foo[*]}|${foo[0]}|${foo[1]}|${foo[2]}|${foo[3]}|${foo[4]}|"
+expected-stdout:
+	5|a|$v|c d|$v|b|
+---
+name: arrays-2a
+description:
+	Check if bash-style arrays work as expected
+stdin:
+	v="c d"
+	foo=(a \$v "$v" '$v' b)
+	echo "${#foo[*]}|${foo[0]}|${foo[1]}|${foo[2]}|${foo[3]}|${foo[4]}|"
+expected-stdout:
+	5|a|$v|c d|$v|b|
+---
+name: arrays-2b
+description:
+	Check if bash-style arrays work as expected, with newlines
+stdin:
+	print '#!'"$__progname"'\nfor x in "$@"; do print -nr -- "$x|"; done' >pfp
+	chmod +x pfp
+	test -n "$ZSH_VERSION" && setopt KSH_ARRAYS
+	v="e f"
+	foo=(a
+		bc
+		d \$v "$v" '$v' g
+	)
+	./pfp "${#foo[*]}" "${foo[0]}" "${foo[1]}" "${foo[2]}" "${foo[3]}" "${foo[4]}" "${foo[5]}" "${foo[6]}"; echo
+	foo=(a\
+		bc
+		d \$v "$v" '$v' g
+	)
+	./pfp "${#foo[*]}" "${foo[0]}" "${foo[1]}" "${foo[2]}" "${foo[3]}" "${foo[4]}" "${foo[5]}" "${foo[6]}"; echo
+	foo=(a\
+	bc\\
+		d \$v "$v" '$v'
+	g)
+	./pfp "${#foo[*]}" "${foo[0]}" "${foo[1]}" "${foo[2]}" "${foo[3]}" "${foo[4]}" "${foo[5]}" "${foo[6]}"; echo
+expected-stdout:
+	7|a|bc|d|$v|e f|$v|g|
+	7|a|bc|d|$v|e f|$v|g|
+	6|abc\|d|$v|e f|$v|g||
+---
+name: arrays-3
+description:
+	Check if array bounds are uint32_t
+stdin:
+	set -A foo a b c
+	foo[4097]=d
+	foo[2147483637]=e
+	echo ${foo[*]}
+	foo[-1]=f
+	echo ${foo[4294967295]} g ${foo[*]}
+expected-stdout:
+	a b c d e
+	f g a b c d e f
+---
+name: arrays-4
+description:
+	Check if Korn Shell arrays with specified indices work as expected
+stdin:
+	v="c d"
+	set -A foo -- [1]=\$v [2]="$v" [4]='$v' [0]=a [5]=b
+	echo "${#foo[*]}|${foo[0]}|${foo[1]}|${foo[2]}|${foo[3]}|${foo[4]}|${foo[5]}|"
+	# we don't want this at all:
+	#	5|a|$v|c d||$v|b|
+	set -A arr "[5]=meh"
+	echo "<${arr[0]}><${arr[5]}>"
+expected-stdout:
+	5|[1]=$v|[2]=c d|[4]=$v|[0]=a|[5]=b||
+	<[5]=meh><>
+---
+name: arrays-5
+description:
+	Check if bash-style arrays with specified indices work as expected
+	(taken out temporarily to fix arrays-4; see also arrays-9a comment)
+category: disabled
+stdin:
+	v="c d"
+	foo=([1]=\$v [2]="$v" [4]='$v' [0]=a [5]=b)
+	echo "${#foo[*]}|${foo[0]}|${foo[1]}|${foo[2]}|${foo[3]}|${foo[4]}|${foo[5]}|"
+	x=([128]=foo bar baz)
+	echo k= ${!x[*]} .
+	echo v= ${x[*]} .
+	# Check that we do not break this by globbing
+	:>b=blah
+	bleh=5
+	typeset -a arr
+	arr+=([bleh]=blah)
+	echo "<${arr[0]}><${arr[5]}>"
+expected-stdout:
+	5|a|$v|c d||$v|b|
+	k= 128 129 130 .
+	v= foo bar baz .
+	<><blah>
+---
+name: arrays-6
+description:
+	Check if we can get the array keys (indices) for indexed arrays,
+	Korn shell style
+stdin:
+	of() {
+		i=0
+		for x in "$@"; do
+			echo -n "$((i++))<$x>"
+		done
+		echo
+	}
+	foo[1]=eins
+	set | grep '^foo'
+	echo =
+	foo[0]=zwei
+	foo[4]=drei
+	set | grep '^foo'
+	echo =
+	echo a $(of ${foo[*]}) = $(of ${bar[*]}) a
+	echo b $(of "${foo[*]}") = $(of "${bar[*]}") b
+	echo c $(of ${foo[@]}) = $(of ${bar[@]}) c
+	echo d $(of "${foo[@]}") = $(of "${bar[@]}") d
+	echo e $(of ${!foo[*]}) = $(of ${!bar[*]}) e
+	echo f $(of "${!foo[*]}") = $(of "${!bar[*]}") f
+	echo g $(of ${!foo[@]}) = $(of ${!bar[@]}) g
+	echo h $(of "${!foo[@]}") = $(of "${!bar[@]}") h
+expected-stdout:
+	foo[1]=eins
+	=
+	foo[0]=zwei
+	foo[1]=eins
+	foo[4]=drei
+	=
+	a 0<zwei>1<eins>2<drei> = a
+	b 0<zwei eins drei> = 0<> b
+	c 0<zwei>1<eins>2<drei> = c
+	d 0<zwei>1<eins>2<drei> = d
+	e 0<0>1<1>2<4> = e
+	f 0<0 1 4> = 0<> f
+	g 0<0>1<1>2<4> = g
+	h 0<0>1<1>2<4> = h
+---
+name: arrays-7
+description:
+	Check if we can get the array keys (indices) for indexed arrays,
+	Korn shell style, in some corner cases
+stdin:
+	echo !arz: ${!arz}
+	echo !arz[0]: ${!arz[0]}
+	echo !arz[1]: ${!arz[1]}
+	arz=foo
+	echo !arz: ${!arz}
+	echo !arz[0]: ${!arz[0]}
+	echo !arz[1]: ${!arz[1]}
+	unset arz
+	echo !arz: ${!arz}
+	echo !arz[0]: ${!arz[0]}
+	echo !arz[1]: ${!arz[1]}
+expected-stdout:
+	!arz: arz
+	!arz[0]: arz[0]
+	!arz[1]: arz[1]
+	!arz: arz
+	!arz[0]: arz[0]
+	!arz[1]: arz[1]
+	!arz: arz
+	!arz[0]: arz[0]
+	!arz[1]: arz[1]
+---
+name: arrays-8
+description:
+	Check some behavioural rules for arrays.
+stdin:
+	fna() {
+		set -A aa 9
+	}
+	fnb() {
+		typeset ab
+		set -A ab 9
+	}
+	fnc() {
+		typeset ac
+		set -A ac 91
+		unset ac
+		set -A ac 92
+	}
+	fnd() {
+		set +A ad 9
+	}
+	fne() {
+		unset ae
+		set +A ae 9
+	}
+	fnf() {
+		unset af[0]
+		set +A af 9
+	}
+	fng() {
+		unset ag[*]
+		set +A ag 9
+	}
+	set -A aa 1 2
+	set -A ab 1 2
+	set -A ac 1 2
+	set -A ad 1 2
+	set -A ae 1 2
+	set -A af 1 2
+	set -A ag 1 2
+	set -A ah 1 2
+	typeset -Z3 aa ab ac ad ae af ag
+	print 1a ${aa[*]} .
+	print 1b ${ab[*]} .
+	print 1c ${ac[*]} .
+	print 1d ${ad[*]} .
+	print 1e ${ae[*]} .
+	print 1f ${af[*]} .
+	print 1g ${ag[*]} .
+	print 1h ${ah[*]} .
+	fna
+	fnb
+	fnc
+	fnd
+	fne
+	fnf
+	fng
+	typeset -Z5 ah[*]
+	print 2a ${aa[*]} .
+	print 2b ${ab[*]} .
+	print 2c ${ac[*]} .
+	print 2d ${ad[*]} .
+	print 2e ${ae[*]} .
+	print 2f ${af[*]} .
+	print 2g ${ag[*]} .
+	print 2h ${ah[*]} .
+expected-stdout:
+	1a 001 002 .
+	1b 001 002 .
+	1c 001 002 .
+	1d 001 002 .
+	1e 001 002 .
+	1f 001 002 .
+	1g 001 002 .
+	1h 1 2 .
+	2a 9 .
+	2b 001 002 .
+	2c 92 .
+	2d 009 002 .
+	2e 9 .
+	2f 9 002 .
+	2g 009 .
+	2h 00001 00002 .
+---
+name: arrays-9a
+description:
+	Check that we can concatenate arrays
+stdin:
+	unset foo; foo=(bar); foo+=(baz); echo 1 ${!foo[*]} : ${foo[*]} .
+	unset foo; foo=(foo bar); foo+=(baz); echo 2 ${!foo[*]} : ${foo[*]} .
+#	unset foo; foo=([2]=foo [0]=bar); foo+=(baz [5]=quux); echo 3 ${!foo[*]} : ${foo[*]} .
+expected-stdout:
+	1 0 1 : bar baz .
+	2 0 1 2 : foo bar baz .
+#	3 0 2 3 5 : bar foo baz quux .
+---
+name: arrays-9b
+description:
+	Check that we can concatenate parameters too
+stdin:
+	unset foo; foo=bar; foo+=baz; echo 1 $foo .
+	unset foo; typeset -i16 foo=10; foo+=20; echo 2 $foo .
+expected-stdout:
+	1 barbaz .
+	2 16#a20 .
+---
+name: arrassign-basic
+description:
+	Check basic whitespace conserving properties of wdarrassign
+stdin:
+	a=($(echo a  b))
+	b=($(echo "a  b"))
+	c=("$(echo "a  b")")
+	d=("$(echo a  b)")
+	a+=($(echo c  d))
+	b+=($(echo "c  d"))
+	c+=("$(echo "c  d")")
+	d+=("$(echo c  d)")
+	echo ".a:${a[0]}.${a[1]}.${a[2]}.${a[3]}:"
+	echo ".b:${b[0]}.${b[1]}.${b[2]}.${b[3]}:"
+	echo ".c:${c[0]}.${c[1]}.${c[2]}.${c[3]}:"
+	echo ".d:${d[0]}.${d[1]}.${d[2]}.${d[3]}:"
+expected-stdout:
+	.a:a.b.c.d:
+	.b:a.b.c.d:
+	.c:a  b.c  d..:
+	.d:a b.c d..:
+---
+name: arrassign-fnc-none
+description:
+	Check locality of array access inside a function
+stdin:
+	function fn {
+		x+=(f)
+		echo ".fn:${x[0]}.${x[1]}.${x[2]}.${x[3]}:"
+	}
+	function rfn {
+		if [[ -n $BASH_VERSION ]]; then
+			y=()
+		else
+			set -A y
+		fi
+		y+=(f)
+		echo ".rfn:${y[0]}.${y[1]}.${y[2]}.${y[3]}:"
+	}
+	x=(m m)
+	y=(m m)
+	echo ".f0:${x[0]}.${x[1]}.${x[2]}.${x[3]}:"
+	fn
+	echo ".f1:${x[0]}.${x[1]}.${x[2]}.${x[3]}:"
+	fn
+	echo ".f2:${x[0]}.${x[1]}.${x[2]}.${x[3]}:"
+	echo ".rf0:${y[0]}.${y[1]}.${y[2]}.${y[3]}:"
+	rfn
+	echo ".rf1:${y[0]}.${y[1]}.${y[2]}.${y[3]}:"
+	rfn
+	echo ".rf2:${y[0]}.${y[1]}.${y[2]}.${y[3]}:"
+expected-stdout:
+	.f0:m.m..:
+	.fn:m.m.f.:
+	.f1:m.m.f.:
+	.fn:m.m.f.f:
+	.f2:m.m.f.f:
+	.rf0:m.m..:
+	.rfn:f...:
+	.rf1:f...:
+	.rfn:f...:
+	.rf2:f...:
+---
+name: arrassign-fnc-local
+description:
+	Check locality of array access inside a function
+	with the bash/mksh/ksh93 local/typeset keyword
+	(note: ksh93 has no local; typeset works only in FKSH)
+stdin:
+	function fn {
+		typeset x
+		x+=(f)
+		echo ".fn:${x[0]}.${x[1]}.${x[2]}.${x[3]}:"
+	}
+	function rfn {
+		if [[ -n $BASH_VERSION ]]; then
+			y=()
+		else
+			set -A y
+		fi
+		typeset y
+		y+=(f)
+		echo ".rfn:${y[0]}.${y[1]}.${y[2]}.${y[3]}:"
+	}
+	function fnr {
+		typeset z
+		if [[ -n $BASH_VERSION ]]; then
+			z=()
+		else
+			set -A z
+		fi
+		z+=(f)
+		echo ".fnr:${z[0]}.${z[1]}.${z[2]}.${z[3]}:"
+	}
+	x=(m m)
+	y=(m m)
+	z=(m m)
+	echo ".f0:${x[0]}.${x[1]}.${x[2]}.${x[3]}:"
+	fn
+	echo ".f1:${x[0]}.${x[1]}.${x[2]}.${x[3]}:"
+	fn
+	echo ".f2:${x[0]}.${x[1]}.${x[2]}.${x[3]}:"
+	echo ".rf0:${y[0]}.${y[1]}.${y[2]}.${y[3]}:"
+	rfn
+	echo ".rf1:${y[0]}.${y[1]}.${y[2]}.${y[3]}:"
+	rfn
+	echo ".rf2:${y[0]}.${y[1]}.${y[2]}.${y[3]}:"
+	echo ".f0r:${z[0]}.${z[1]}.${z[2]}.${z[3]}:"
+	fnr
+	echo ".f1r:${z[0]}.${z[1]}.${z[2]}.${z[3]}:"
+	fnr
+	echo ".f2r:${z[0]}.${z[1]}.${z[2]}.${z[3]}:"
+expected-stdout:
+	.f0:m.m..:
+	.fn:f...:
+	.f1:m.m..:
+	.fn:f...:
+	.f2:m.m..:
+	.rf0:m.m..:
+	.rfn:f...:
+	.rf1:...:
+	.rfn:f...:
+	.rf2:...:
+	.f0r:m.m..:
+	.fnr:f...:
+	.f1r:m.m..:
+	.fnr:f...:
+	.f2r:m.m..:
+---
+name: arrassign-fnc-global
+description:
+	Check locality of array access inside a function
+	with the mksh-specific global keyword
+stdin:
+	function fn {
+		global x
+		x+=(f)
+		echo ".fn:${x[0]}.${x[1]}.${x[2]}.${x[3]}:"
+	}
+	function rfn {
+		set -A y
+		global y
+		y+=(f)
+		echo ".rfn:${y[0]}.${y[1]}.${y[2]}.${y[3]}:"
+	}
+	function fnr {
+		global z
+		set -A z
+		z+=(f)
+		echo ".fnr:${z[0]}.${z[1]}.${z[2]}.${z[3]}:"
+	}
+	x=(m m)
+	y=(m m)
+	z=(m m)
+	echo ".f0:${x[0]}.${x[1]}.${x[2]}.${x[3]}:"
+	fn
+	echo ".f1:${x[0]}.${x[1]}.${x[2]}.${x[3]}:"
+	fn
+	echo ".f2:${x[0]}.${x[1]}.${x[2]}.${x[3]}:"
+	echo ".rf0:${y[0]}.${y[1]}.${y[2]}.${y[3]}:"
+	rfn
+	echo ".rf1:${y[0]}.${y[1]}.${y[2]}.${y[3]}:"
+	rfn
+	echo ".rf2:${y[0]}.${y[1]}.${y[2]}.${y[3]}:"
+	echo ".f0r:${z[0]}.${z[1]}.${z[2]}.${z[3]}:"
+	fnr
+	echo ".f1r:${z[0]}.${z[1]}.${z[2]}.${z[3]}:"
+	fnr
+	echo ".f2r:${z[0]}.${z[1]}.${z[2]}.${z[3]}:"
+expected-stdout:
+	.f0:m.m..:
+	.fn:m.m.f.:
+	.f1:m.m.f.:
+	.fn:m.m.f.f:
+	.f2:m.m.f.f:
+	.rf0:m.m..:
+	.rfn:f...:
+	.rf1:f...:
+	.rfn:f...:
+	.rf2:f...:
+	.f0r:m.m..:
+	.fnr:f...:
+	.f1r:f...:
+	.fnr:f...:
+	.f2r:f...:
+---
+name: strassign-fnc-none
+description:
+	Check locality of string access inside a function
+stdin:
+	function fn {
+		x+=f
+		echo ".fn:$x:"
+	}
+	function rfn {
+		y=
+		y+=f
+		echo ".rfn:$y:"
+	}
+	x=m
+	y=m
+	echo ".f0:$x:"
+	fn
+	echo ".f1:$x:"
+	fn
+	echo ".f2:$x:"
+	echo ".rf0:$y:"
+	rfn
+	echo ".rf1:$y:"
+	rfn
+	echo ".rf2:$y:"
+expected-stdout:
+	.f0:m:
+	.fn:mf:
+	.f1:mf:
+	.fn:mff:
+	.f2:mff:
+	.rf0:m:
+	.rfn:f:
+	.rf1:f:
+	.rfn:f:
+	.rf2:f:
+---
+name: strassign-fnc-local
+description:
+	Check locality of string access inside a function
+	with the bash/mksh/ksh93 local/typeset keyword
+	(note: ksh93 has no local; typeset works only in FKSH)
+stdin:
+	function fn {
+		typeset x
+		x+=f
+		echo ".fn:$x:"
+	}
+	function rfn {
+		y=
+		typeset y
+		y+=f
+		echo ".rfn:$y:"
+	}
+	function fnr {
+		typeset z
+		z=
+		z+=f
+		echo ".fnr:$z:"
+	}
+	x=m
+	y=m
+	z=m
+	echo ".f0:$x:"
+	fn
+	echo ".f1:$x:"
+	fn
+	echo ".f2:$x:"
+	echo ".rf0:$y:"
+	rfn
+	echo ".rf1:$y:"
+	rfn
+	echo ".rf2:$y:"
+	echo ".f0r:$z:"
+	fnr
+	echo ".f1r:$z:"
+	fnr
+	echo ".f2r:$z:"
+expected-stdout:
+	.f0:m:
+	.fn:f:
+	.f1:m:
+	.fn:f:
+	.f2:m:
+	.rf0:m:
+	.rfn:f:
+	.rf1::
+	.rfn:f:
+	.rf2::
+	.f0r:m:
+	.fnr:f:
+	.f1r:m:
+	.fnr:f:
+	.f2r:m:
+---
+name: strassign-fnc-global
+description:
+	Check locality of string access inside a function
+	with the mksh-specific global keyword
+stdin:
+	function fn {
+		global x
+		x+=f
+		echo ".fn:$x:"
+	}
+	function rfn {
+		y=
+		global y
+		y+=f
+		echo ".rfn:$y:"
+	}
+	function fnr {
+		global z
+		z=
+		z+=f
+		echo ".fnr:$z:"
+	}
+	x=m
+	y=m
+	z=m
+	echo ".f0:$x:"
+	fn
+	echo ".f1:$x:"
+	fn
+	echo ".f2:$x:"
+	echo ".rf0:$y:"
+	rfn
+	echo ".rf1:$y:"
+	rfn
+	echo ".rf2:$y:"
+	echo ".f0r:$z:"
+	fnr
+	echo ".f1r:$z:"
+	fnr
+	echo ".f2r:$z:"
+expected-stdout:
+	.f0:m:
+	.fn:mf:
+	.f1:mf:
+	.fn:mff:
+	.f2:mff:
+	.rf0:m:
+	.rfn:f:
+	.rf1:f:
+	.rfn:f:
+	.rf2:f:
+	.f0r:m:
+	.fnr:f:
+	.f1r:f:
+	.fnr:f:
+	.f2r:f:
+---
+name: varexpand-substr-1
+description:
+	Check if bash-style substring expansion works
+	when using positive numerics
+stdin:
+	x=abcdefghi
+	typeset -i y=123456789
+	typeset -i 16 z=123456789	# 16#75bcd15
+	echo a t${x:2:2} ${y:2:3} ${z:2:3} a
+	echo b ${x::3} ${y::3} ${z::3} b
+	echo c ${x:2:} ${y:2:} ${z:2:} c
+	echo d ${x:2} ${y:2} ${z:2} d
+	echo e ${x:2:6} ${y:2:6} ${z:2:7} e
+	echo f ${x:2:7} ${y:2:7} ${z:2:8} f
+	echo g ${x:2:8} ${y:2:8} ${z:2:9} g
+expected-stdout:
+	a tcd 345 #75 a
+	b abc 123 16# b
+	c c
+	d cdefghi 3456789 #75bcd15 d
+	e cdefgh 345678 #75bcd1 e
+	f cdefghi 3456789 #75bcd15 f
+	g cdefghi 3456789 #75bcd15 g
+---
+name: varexpand-substr-2
+description:
+	Check if bash-style substring expansion works
+	when using negative numerics or expressions
+stdin:
+	x=abcdefghi
+	typeset -i y=123456789
+	typeset -i 16 z=123456789	# 16#75bcd15
+	n=2
+	echo a ${x:$n:3} ${y:$n:3} ${z:$n:3} a
+	echo b ${x:(n):3} ${y:(n):3} ${z:(n):3} b
+	echo c ${x:(-2):1} ${y:(-2):1} ${z:(-2):1} c
+	echo d t${x: n:2} ${y: n:3} ${z: n:3} d
+expected-stdout:
+	a cde 345 #75 a
+	b cde 345 #75 b
+	c h 8 1 c
+	d tcd 345 #75 d
+---
+name: varexpand-substr-3
+description:
+	Check that some things that work in bash fail.
+	This is by design. And that some things fail in both.
+stdin:
+	export x=abcdefghi n=2
+	"$__progname" -c 'echo v${x:(n)}x'
+	"$__progname" -c 'echo w${x: n}x'
+	"$__progname" -c 'echo x${x:n}x'
+	"$__progname" -c 'echo y${x:}x'
+	"$__progname" -c 'echo z${x}x'
+	"$__progname" -c 'x=abcdef;y=123;echo ${x:${y:2:1}:2}' >/dev/null 2>&1; echo $?
+expected-stdout:
+	vcdefghix
+	wcdefghix
+	zabcdefghix
+	1
+expected-stderr-pattern:
+	/x:n.*bad substitution.*\n.*bad substitution/
+---
+name: varexpand-substr-4
+description:
+	Check corner cases for substring expansion
+stdin:
+	x=abcdefghi
+	integer y=2
+	echo a ${x:(y == 1 ? 2 : 3):4} a
+expected-stdout:
+	a defg a
+---
+name: varexpand-substr-5A
+description:
+	Check that substring expansions work on characters
+stdin:
+	set +U
+	x=mäh
+	echo a ${x::1} ${x: -1} a
+	echo b ${x::3} ${x: -3} b
+	echo c ${x:1:2} ${x: -3:2} c
+	echo d ${#x} d
+expected-stdout:
+	a m h a
+	b mä äh b
+	c ä ä c
+	d 4 d
+---
+name: varexpand-substr-5W
+description:
+	Check that substring expansions work on characters
+stdin:
+	set -U
+	x=mäh
+	echo a ${x::1} ${x: -1} a
+	echo b ${x::2} ${x: -2} b
+	echo c ${x:1:1} ${x: -2:1} c
+	echo d ${#x} d
+expected-stdout:
+	a m h a
+	b mä äh b
+	c ä ä c
+	d 3 d
+---
+name: varexpand-substr-6
+description:
+	Check that string substitution works correctly
+stdin:
+	foo=1
+	bar=2
+	baz=qwertyuiop
+	echo a ${baz: foo: bar}
+	echo b ${baz: foo: $bar}
+	echo c ${baz: $foo: bar}
+	echo d ${baz: $foo: $bar}
+expected-stdout:
+	a we
+	b we
+	c we
+	d we
+---
+name: varexpand-special-hash
+description:
+	Check special ${var at x} expansion for x=hash
+stdin:
+	typeset -i8 foo=10
+	bar=baz
+	unset baz
+	print ${foo@#} ${bar@#} ${baz@#} .
+expected-stdout:
+	9B15FBFB CFBDD32B 00000000 .
+---
+name: varexpand-special-quote
+description:
+	Check special ${var at Q} expansion for quoted strings
+stdin:
+	set +U
+	i=x
+	j=a\ b
+	k=$'c
+	d\xA0''e€f'
+	print -r -- "<i=$i j=$j k=$k>"
+	s="u=${i at Q} v=${j at Q} w=${k at Q}"
+	print -r -- "s=\"$s\""
+	eval "$s"
+	typeset -p u v w
+expected-stdout:
+	<i=x j=a b k=c
+	d\xA0e€f>
+	s="u=x v='a b' w=$'c\nd\240e\u20ACf'"
+	typeset u=x
+	typeset v='a b'
+	typeset w=$'c\nd\240e\u20ACf'
+---
+name: varexpand-null-1
+description:
+	Ensure empty strings expand emptily
+stdin:
+	print x ${a} ${b} y
+	print z ${a#?} ${b%?} w
+	print v ${a=} ${b/c/d} u
+expected-stdout:
+	x y
+	z w
+	v u
+---
+name: varexpand-null-2
+description:
+	Ensure empty strings, when quoted, are expanded as empty strings
+stdin:
+	print '#!'"$__progname"'\nfor x in "$@"; do print -nr -- "<$x> "; done' >pfs
+	chmod +x pfs
+	./pfs 1 "${a}" 2 "${a#?}" + "${b%?}" 3 "${a=}" + "${b/c/d}"
+	echo .
+expected-stdout:
+	<1> <> <2> <> <+> <> <3> <> <+> <> .
+---
+name: print-funny-chars
+description:
+	Check print builtin's capability to output designated characters
+stdin:
+	print '<\0144\0344\xDB\u00DB\u20AC\uDB\x40>'
+expected-stdout:
+	<d\xE4\xDBÛ€Û@>
+---
+name: print-bksl-c
+description:
+	Check print builtin's \c escape
+stdin:
+	print '\ca'; print b
+expected-stdout:
+	ab
+---
+name: print-cr
+description:
+	Check that CR+LF is not collapsed into LF as some MSYS shells wrongly do
+stdin:
+	echo '#!'"$__progname" >foo
+	cat >>foo <<-'EOF'
+		print -n -- '220-blau.mirbsd.org ESMTP ready at Thu, 25 Jul 2013 15:57:57 GMT\r\n220->> Bitte keine Werbung einwerfen! <<\r\r\n220 Who do you wanna pretend to be today'
+		print \?
+	EOF
+	chmod +x foo
+	echo "[$(./foo)]"
+	./foo | while IFS= read -r line; do
+		print -r -- "{$line}"
+	done
+expected-stdout:
+	[220-blau.mirbsd.org ESMTP ready at Thu, 25 Jul 2013 15:57:57 GMT
+	220->> Bitte keine Werbung einwerfen! <<
+
+	220 Who do you wanna pretend to be today?
+]
+	{220-blau.mirbsd.org ESMTP ready at Thu, 25 Jul 2013 15:57:57 GMT
+}
+	{220->> Bitte keine Werbung einwerfen! <<
+
+}
+	{220 Who do you wanna pretend to be today?
+}
+---
+name: print-nul-chars
+description:
+	Check handling of NUL characters for print and COMSUB
+stdin:
+	x=$(print '<\0>')
+	print $(($(print '<\0>' | wc -c))) $(($(print "$x" | wc -c))) \
+	    ${#x} "$x" '<\0>'
+expected-stdout-pattern:
+	/^4 3 2 <> <\0>$/
+---
+name: print-escapes
+description:
+	Check backslash expansion by the print builtin
+stdin:
+	print '\ \!\"\#\$\%\&'\\\''\(\)\*\+\,\-\.\/\0\1\2\3\4\5\6\7\8' \
+	    '\9\:\;\<\=\>\?\@\A\B\C\D\E\F\G\H\I\J\K\L\M\N\O\P\Q\R\S\T' \
+	    '\U\V\W\X\Y\Z\[\\\]\^\_\`\a\b  \d\e\f\g\h\i\j\k\l\m\n\o\p' \
+	    '\q\r\s\t\u\v\w\x\y\z\{\|\}\~' '\u20acd' '\U20acd' '\x123' \
+	    '\0x' '\0123' '\01234' | {
+		# integer-base-one-3As
+		typeset -Uui16 -Z11 pos=0
+		typeset -Uui16 -Z5 hv=2147483647
+		typeset -i1 wc=0x0A
+		dasc=
+		nl=${wc#1#}
+		while IFS= read -r line; do
+			line=$line$nl
+			while [[ -n $line ]]; do
+				hv=1#${line::1}
+				if (( (pos & 15) == 0 )); then
+					(( pos )) && print "$dasc|"
+					print -n "${pos#16#}  "
+					dasc=' |'
+				fi
+				print -n "${hv#16#} "
+				if (( (hv < 32) || (hv > 126) )); then
+					dasc=$dasc.
+				else
+					dasc=$dasc${line::1}
+				fi
+				(( (pos++ & 15) == 7 )) && print -n -- '- '
+				line=${line:1}
+			done
+		done
+		while (( pos & 15 )); do
+			print -n '   '
+			(( (pos++ & 15) == 7 )) && print -n -- '- '
+		done
+		(( hv == 2147483647 )) || print "$dasc|"
+	}
+expected-stdout:
+	00000000  5C 20 5C 21 5C 22 5C 23 - 5C 24 5C 25 5C 26 5C 27  |\ \!\"\#\$\%\&\'|
+	00000010  5C 28 5C 29 5C 2A 5C 2B - 5C 2C 5C 2D 5C 2E 5C 2F  |\(\)\*\+\,\-\.\/|
+	00000020  5C 31 5C 32 5C 33 5C 34 - 5C 35 5C 36 5C 37 5C 38  |\1\2\3\4\5\6\7\8|
+	00000030  20 5C 39 5C 3A 5C 3B 5C - 3C 5C 3D 5C 3E 5C 3F 5C  | \9\:\;\<\=\>\?\|
+	00000040  40 5C 41 5C 42 5C 43 5C - 44 1B 5C 46 5C 47 5C 48  |@\A\B\C\D.\F\G\H|
+	00000050  5C 49 5C 4A 5C 4B 5C 4C - 5C 4D 5C 4E 5C 4F 5C 50  |\I\J\K\L\M\N\O\P|
+	00000060  5C 51 5C 52 5C 53 5C 54 - 20 5C 56 5C 57 5C 58 5C  |\Q\R\S\T \V\W\X\|
+	00000070  59 5C 5A 5C 5B 5C 5C 5D - 5C 5E 5C 5F 5C 60 07 08  |Y\Z\[\]\^\_\`..|
+	00000080  20 20 5C 64 1B 0C 5C 67 - 5C 68 5C 69 5C 6A 5C 6B  |  \d..\g\h\i\j\k|
+	00000090  5C 6C 5C 6D 0A 5C 6F 5C - 70 20 5C 71 0D 5C 73 09  |\l\m.\o\p \q.\s.|
+	000000A0  0B 5C 77 5C 79 5C 7A 5C - 7B 5C 7C 5C 7D 5C 7E 20  |.\w\y\z\{\|\}\~ |
+	000000B0  E2 82 AC 64 20 EF BF BD - 20 12 33 20 78 20 53 20  |...d ... .3 x S |
+	000000C0  53 34 0A                -                          |S4.|
+---
+name: dollar-doublequoted-strings
+description:
+	Check that a $ preceding "…" is ignored
+stdin:
+	echo $"Localise me!"
+	cat <<<$"Me too!"
+	V=X
+	aol=aol
+	cat <<-$"aol"
+		I do not take a $V for a V!
+	aol
+expected-stdout:
+	Localise me!
+	Me too!
+	I do not take a $V for a V!
+---
+name: dollar-quoted-strings
+description:
+	Check backslash expansion by $'…' strings
+stdin:
+	print '#!'"$__progname"'\nfor x in "$@"; do print -r -- "$x"; done' >pfn
+	chmod +x pfn
+	./pfn $'\ \!\"\#\$\%\&\'\(\)\*\+\,\-\.\/ \1\2\3\4\5\6' \
+	    $'a\0b' $'a\01b' $'\7\8\9\:\;\<\=\>\?\@\A\B\C\D\E\F\G\H\I' \
+	    $'\J\K\L\M\N\O\P\Q\R\S\T\U1\V\W\X\Y\Z\[\\\]\^\_\`\a\b\d\e' \
+	    $'\f\g\h\i\j\k\l\m\n\o\p\q\r\s\t\u1\v\w\x1\y\z\{\|\}\~ $x' \
+	    $'\u20acd' $'\U20acd' $'\x123' $'fn\x0rd' $'\0234' $'\234' \
+	    $'\2345' $'\ca' $'\c!' $'\c?' $'\c€' $'a\
+	b' | {
+		# integer-base-one-3As
+		typeset -Uui16 -Z11 pos=0
+		typeset -Uui16 -Z5 hv=2147483647
+		typeset -i1 wc=0x0A
+		dasc=
+		nl=${wc#1#}
+		while IFS= read -r line; do
+			line=$line$nl
+			while [[ -n $line ]]; do
+				hv=1#${line::1}
+				if (( (pos & 15) == 0 )); then
+					(( pos )) && print "$dasc|"
+					print -n "${pos#16#}  "
+					dasc=' |'
+				fi
+				print -n "${hv#16#} "
+				if (( (hv < 32) || (hv > 126) )); then
+					dasc=$dasc.
+				else
+					dasc=$dasc${line::1}
+				fi
+				(( (pos++ & 15) == 7 )) && print -n -- '- '
+				line=${line:1}
+			done
+		done
+		while (( pos & 15 )); do
+			print -n '   '
+			(( (pos++ & 15) == 7 )) && print -n -- '- '
+		done
+		(( hv == 2147483647 )) || print "$dasc|"
+	}
+expected-stdout:
+	00000000  20 21 22 23 24 25 26 27 - 28 29 2A 2B 2C 2D 2E 2F  | !"#$%&'()*+,-./|
+	00000010  20 01 02 03 04 05 06 0A - 61 0A 61 01 62 0A 07 38  | .......a.a.b..8|
+	00000020  39 3A 3B 3C 3D 3E 3F 40 - 41 42 43 44 1B 46 47 48  |9:;<=>?@ABCD.FGH|
+	00000030  49 0A 4A 4B 4C 4D 4E 4F - 50 51 52 53 54 01 56 57  |I.JKLMNOPQRST.VW|
+	00000040  58 59 5A 5B 5C 5D 5E 5F - 60 07 08 64 1B 0A 0C 67  |XYZ[\]^_`..d...g|
+	00000050  68 69 6A 6B 6C 6D 0A 6F - 70 71 0D 73 09 01 0B 77  |hijklm.opq.s...w|
+	00000060  01 79 7A 7B 7C 7D 7E 20 - 24 78 0A E2 82 AC 64 0A  |.yz{|}~ $x....d.|
+	00000070  EF BF BD 0A C4 A3 0A 66 - 6E 0A 13 34 0A 9C 0A 9C  |.......fn..4....|
+	00000080  35 0A 01 0A 01 0A 7F 0A - 02 82 AC 0A 61 0A 62 0A  |5...........a.b.|
+---
+name: dollar-quotes-in-heredocs-strings
+description:
+	They are, however, not parsed in here documents, here strings
+	(outside of string delimiters) or regular strings, but in
+	parameter substitutions.
+stdin:
+	cat <<EOF
+		dollar = strchr(s, '$');	/* ' */
+		foo " bar \" baz
+	EOF
+	cat <<$'a\tb'
+	a\tb
+	a	b
+	cat <<<"dollar = strchr(s, '$');	/* ' */"
+	cat <<<'dollar = strchr(s, '\''$'\'');	/* '\'' */'
+	x="dollar = strchr(s, '$');	/* ' */"
+	cat <<<"$x"
+	cat <<<$'a\E[0m\tb'
+	unset nl; print -r -- "x${nl:=$'\n'}y"
+	echo "1 foo\"bar"
+	# cf & HEREDOC
+	cat <<EOF
+	2 foo\"bar
+	EOF
+	# probably never reached for here strings?
+	cat <<<"3 foo\"bar"
+	cat <<<"4 foo\\\"bar"
+	cat <<<'5 foo\"bar'
+	# old scripts use this (e.g. ncurses)
+	echo "^$"
+	# make sure this works, outside of quotes
+	cat <<<'7'$'\t''.'
+expected-stdout:
+		dollar = strchr(s, '$');	/* ' */
+		foo " bar \" baz
+	a\tb
+	dollar = strchr(s, '$');	/* ' */
+	dollar = strchr(s, '$');	/* ' */
+	dollar = strchr(s, '$');	/* ' */
+	a	b
+	x
+	y
+	1 foo"bar
+	2 foo\"bar
+	3 foo"bar
+	4 foo\"bar
+	5 foo\"bar
+	^$
+	7	.
+---
+name: dot-needs-argument
+description:
+	check Debian #415167 solution: '.' without arguments should fail
+stdin:
+	"$__progname" -c .
+	"$__progname" -c source
+expected-exit: e != 0
+expected-stderr-pattern:
+	/\.: missing argument.*\n.*\.: missing argument/
+---
+name: alias-function-no-conflict
+description:
+	make aliases not conflict with functions
+	note: for ksh-like functions, the order of preference is
+	different; bash outputs baz instead of bar in line 2 below
+stdin:
+	alias foo='echo bar'
+	foo() {
+		echo baz
+	}
+	alias korn='echo bar'
+	function korn {
+		echo baz
+	}
+	foo
+	korn
+	unset -f foo
+	foo 2>/dev/null || echo rab
+expected-stdout:
+	baz
+	bar
+	rab
+---
+name: bash-function-parens
+description:
+	ensure the keyword function is ignored when preceding
+	POSIX style function declarations (bashism)
+stdin:
+	mk() {
+		echo '#!'"$__progname"
+		echo "$1 {"
+		echo '	echo "bar='\''$0'\'\"
+		echo '}'
+		echo ${2:-foo}
+	}
+	mk 'function foo' >f-korn
+	mk 'foo ()' >f-dash
+	mk 'function foo ()' >f-bash
+	mk 'function stop ()' stop >f-stop
+	print '#!'"$__progname"'\nprint -r -- "${0%/f-argh}"' >f-argh
+	chmod +x f-*
+	u=$(./f-argh)
+	x="korn: $(./f-korn)"; echo "${x/@("$u")/.}"
+	x="dash: $(./f-dash)"; echo "${x/@("$u")/.}"
+	x="bash: $(./f-bash)"; echo "${x/@("$u")/.}"
+	x="stop: $(./f-stop)"; echo "${x/@("$u")/.}"
+expected-stdout:
+	korn: bar='foo'
+	dash: bar='./f-dash'
+	bash: bar='./f-bash'
+	stop: bar='./f-stop'
+---
+name: integer-base-one-1
+description:
+	check if the use of fake integer base 1 works
+stdin:
+	set -U
+	typeset -Uui16 i0=1#\xEF i1=1#€
+	typeset -i1 o0a=64
+	typeset -i1 o1a=0x263A
+	typeset -Uui1 o0b=0x7E
+	typeset -Uui1 o1b=0xFDD0
+	integer px=0xCAFE 'p0=1# ' p1=1#… pl=1#f
+	echo "in <$i0> <$i1>"
+	echo "out <${o0a#1#}|${o0b#1#}> <${o1a#1#}|${o1b#1#}>"
+	typeset -Uui1 i0 i1
+	echo "pass <$px> <$p0> <$p1> <$pl> <${i0#1#}|${i1#1#}>"
+	typeset -Uui16 tv1=1#~ tv2=1# tv3=1#\x80 tv4=1#\x81 tv5=1#\xC0 tv6=1#\xC1 tv7=1#  tv8=1#€
+	echo "specX <${tv1#16#}> <${tv2#16#}> <${tv3#16#}> <${tv4#16#}> <${tv5#16#}> <${tv6#16#}> <${tv7#16#}> <${tv8#16#}>"
+	typeset -i1 tv1 tv2 tv3 tv4 tv5 tv6 tv7 tv8
+	echo "specW <${tv1#1#}> <${tv2#1#}> <${tv3#1#}> <${tv4#1#}> <${tv5#1#}> <${tv6#1#}> <${tv7#1#}> <${tv8#1#}>"
+	typeset -i1 xs1=0xEF7F xs2=0xEF80 xs3=0xFDD0
+	echo "specU <${xs1#1#}> <${xs2#1#}> <${xs3#1#}>"
+expected-stdout:
+	in <16#EFEF> <16#20AC>
+	out <@|~> <☺|\x{FDD0}>
+	pass <16#cafe> <1# > <1#…> <1#f> <\xEF|€>
+	specX <7E> <7F> <EF80> <EF81> <EFC0> <EFC1> <A0> <80>
+	specW <~> <> <\x80> <\x81> <\xC0> <\xC1> < > <€>
+	specU <> <\x80> <\x{FDD0}>
+---
+name: integer-base-one-2a
+description:
+	check if the use of fake integer base 1 stops at correct characters
+stdin:
+	set -U
+	integer x=1#foo
+	echo /$x/
+expected-stderr-pattern:
+	/1#foo: unexpected 'oo'/
+expected-exit: e != 0
+---
+name: integer-base-one-2b
+description:
+	check if the use of fake integer base 1 stops at correct characters
+stdin:
+	set -U
+	integer x=1#\xC0\x80
+	echo /$x/
+expected-stderr-pattern:
+	/1#\xC0\x80: unexpected '\x80'/
+expected-exit: e != 0
+---
+name: integer-base-one-2c1
+description:
+	check if the use of fake integer base 1 stops at correct characters
+stdin:
+	set -U
+	integer x=1#…
+	echo /$x/
+expected-stdout:
+	/1#…/
+---
+name: integer-base-one-2c2
+description:
+	check if the use of fake integer base 1 stops at correct characters
+stdin:
+	set +U
+	integer x=1#…
+	echo /$x/
+expected-stderr-pattern:
+	/1#…: unexpected '\x80'/
+expected-exit: e != 0
+---
+name: integer-base-one-2d1
+description:
+	check if the use of fake integer base 1 handles octets okay
+stdin:
+	set -U
+	typeset -i16 x=1#\xFF
+	echo /$x/	# invalid utf-8
+expected-stdout:
+	/16#efff/
+---
+name: integer-base-one-2d2
+description:
+	check if the use of fake integer base 1 handles octets
+stdin:
+	set -U
+	typeset -i16 x=1#\xC2
+	echo /$x/	# invalid 2-byte
+expected-stdout:
+	/16#efc2/
+---
+name: integer-base-one-2d3
+description:
+	check if the use of fake integer base 1 handles octets
+stdin:
+	set -U
+	typeset -i16 x=1#\xEF
+	echo /$x/	# invalid 2-byte
+expected-stdout:
+	/16#efef/
+---
+name: integer-base-one-2d4
+description:
+	check if the use of fake integer base 1 stops at invalid input
+stdin:
+	set -U
+	typeset -i16 x=1#\xEF\xBF\xC0
+	echo /$x/	# invalid 3-byte
+expected-stderr-pattern:
+	/1#\xEF\xBF\xC0: unexpected '\xBF'/
+expected-exit: e != 0
+---
+name: integer-base-one-2d5
+description:
+	check if the use of fake integer base 1 stops at invalid input
+stdin:
+	set -U
+	typeset -i16 x=1#\xC0\x80
+	echo /$x/	# non-minimalistic
+expected-stderr-pattern:
+	/1#\xC0\x80: unexpected '\x80'/
+expected-exit: e != 0
+---
+name: integer-base-one-2d6
+description:
+	check if the use of fake integer base 1 stops at invalid input
+stdin:
+	set -U
+	typeset -i16 x=1#\xE0\x80\x80
+	echo /$x/	# non-minimalistic
+expected-stderr-pattern:
+	/1#\xE0\x80\x80: unexpected '\x80'/
+expected-exit: e != 0
+---
+name: integer-base-one-3As
+description:
+	some sample code for hexdumping
+	not NUL safe; input lines must be NL terminated
+stdin:
+	{
+		print 'Hello, World!\\\nこんにちは!'
+		typeset -Uui16 i=0x100
+		# change that to 0xFF once we can handle embedded
+		# NUL characters in strings / here documents
+		while (( i++ < 0x1FF )); do
+			print -n "\x${i#16#1}"
+		done
+		print '\0z'
+	} | {
+		# integer-base-one-3As
+		typeset -Uui16 -Z11 pos=0
+		typeset -Uui16 -Z5 hv=2147483647
+		typeset -i1 wc=0x0A
+		dasc=
+		nl=${wc#1#}
+		while IFS= read -r line; do
+			line=$line$nl
+			while [[ -n $line ]]; do
+				hv=1#${line::1}
+				if (( (pos & 15) == 0 )); then
+					(( pos )) && print "$dasc|"
+					print -n "${pos#16#}  "
+					dasc=' |'
+				fi
+				print -n "${hv#16#} "
+				if (( (hv < 32) || (hv > 126) )); then
+					dasc=$dasc.
+				else
+					dasc=$dasc${line::1}
+				fi
+				(( (pos++ & 15) == 7 )) && print -n -- '- '
+				line=${line:1}
+			done
+		done
+		while (( pos & 15 )); do
+			print -n '   '
+			(( (pos++ & 15) == 7 )) && print -n -- '- '
+		done
+		(( hv == 2147483647 )) || print "$dasc|"
+	}
+expected-stdout:
+	00000000  48 65 6C 6C 6F 2C 20 57 - 6F 72 6C 64 21 5C 0A E3  |Hello, World!\..|
+	00000010  81 93 E3 82 93 E3 81 AB - E3 81 A1 E3 81 AF EF BC  |................|
+	00000020  81 0A 01 02 03 04 05 06 - 07 08 09 0A 0B 0C 0D 0E  |................|
+	00000030  0F 10 11 12 13 14 15 16 - 17 18 19 1A 1B 1C 1D 1E  |................|
+	00000040  1F 20 21 22 23 24 25 26 - 27 28 29 2A 2B 2C 2D 2E  |. !"#$%&'()*+,-.|
+	00000050  2F 30 31 32 33 34 35 36 - 37 38 39 3A 3B 3C 3D 3E  |/0123456789:;<=>|
+	00000060  3F 40 41 42 43 44 45 46 - 47 48 49 4A 4B 4C 4D 4E  |?@ABCDEFGHIJKLMN|
+	00000070  4F 50 51 52 53 54 55 56 - 57 58 59 5A 5B 5C 5D 5E  |OPQRSTUVWXYZ[\]^|
+	00000080  5F 60 61 62 63 64 65 66 - 67 68 69 6A 6B 6C 6D 6E  |_`abcdefghijklmn|
+	00000090  6F 70 71 72 73 74 75 76 - 77 78 79 7A 7B 7C 7D 7E  |opqrstuvwxyz{|}~|
+	000000A0  7F 80 81 82 83 84 85 86 - 87 88 89 8A 8B 8C 8D 8E  |................|
+	000000B0  8F 90 91 92 93 94 95 96 - 97 98 99 9A 9B 9C 9D 9E  |................|
+	000000C0  9F A0 A1 A2 A3 A4 A5 A6 - A7 A8 A9 AA AB AC AD AE  |................|
+	000000D0  AF B0 B1 B2 B3 B4 B5 B6 - B7 B8 B9 BA BB BC BD BE  |................|
+	000000E0  BF C0 C1 C2 C3 C4 C5 C6 - C7 C8 C9 CA CB CC CD CE  |................|
+	000000F0  CF D0 D1 D2 D3 D4 D5 D6 - D7 D8 D9 DA DB DC DD DE  |................|
+	00000100  DF E0 E1 E2 E3 E4 E5 E6 - E7 E8 E9 EA EB EC ED EE  |................|
+	00000110  EF F0 F1 F2 F3 F4 F5 F6 - F7 F8 F9 FA FB FC FD FE  |................|
+	00000120  FF 7A 0A                -                          |.z.|
+---
+name: integer-base-one-3Ws
+description:
+	some sample code for hexdumping Unicode
+	not NUL safe; input lines must be NL terminated
+stdin:
+	set -U
+	{
+		print 'Hello, World!\\\nこんにちは!'
+		typeset -Uui16 i=0x100
+		# change that to 0xFF once we can handle embedded
+		# NUL characters in strings / here documents
+		while (( i++ < 0x1FF )); do
+			print -n "\u${i#16#1}"
+		done
+		print
+		print \\xff		# invalid utf-8
+		print \\xc2		# invalid 2-byte
+		print \\xef\\xbf\\xc0	# invalid 3-byte
+		print \\xc0\\x80	# non-minimalistic
+		print \\xe0\\x80\\x80	# non-minimalistic
+		print '�\x{FFFE}\x{FFFF}'	# end of range
+		print '\0z'		# embedded NUL
+	} | {
+		# integer-base-one-3Ws
+		typeset -Uui16 -Z11 pos=0
+		typeset -Uui16 -Z7 hv
+		typeset -i1 wc=0x0A
+		typeset -i lpos
+		dasc=
+		nl=${wc#1#}
+		while IFS= read -r line; do
+			line=$line$nl
+			lpos=0
+			while (( lpos < ${#line} )); do
+				wc=1#${line:(lpos++):1}
+				if (( (wc < 32) || \
+				    ((wc > 126) && (wc < 160)) )); then
+					dch=.
+				elif (( (wc & 0xFF80) == 0xEF80 )); then
+					dch=�
+				else
+					dch=${wc#1#}
+				fi
+				if (( (pos & 7) == 7 )); then
+					dasc=$dasc$dch
+					dch=
+				elif (( (pos & 7) == 0 )); then
+					(( pos )) && print "$dasc|"
+					print -n "${pos#16#}  "
+					dasc=' |'
+				fi
+				let hv=wc
+				print -n "${hv#16#} "
+				(( (pos++ & 7) == 3 )) && \
+				    print -n -- '- '
+				dasc=$dasc$dch
+			done
+		done
+		while (( pos & 7 )); do
+			print -n '     '
+			(( (pos++ & 7) == 3 )) && print -n -- '- '
+		done
+		(( hv == 2147483647 )) || print "$dasc|"
+	}
+expected-stdout:
+	00000000  0048 0065 006C 006C - 006F 002C 0020 0057  |Hello, W|
+	00000008  006F 0072 006C 0064 - 0021 005C 000A 3053  |orld!\.こ|
+	00000010  3093 306B 3061 306F - FF01 000A 0001 0002  |んにちは!...|
+	00000018  0003 0004 0005 0006 - 0007 0008 0009 000A  |........|
+	00000020  000B 000C 000D 000E - 000F 0010 0011 0012  |........|
+	00000028  0013 0014 0015 0016 - 0017 0018 0019 001A  |........|
+	00000030  001B 001C 001D 001E - 001F 0020 0021 0022  |..... !"|
+	00000038  0023 0024 0025 0026 - 0027 0028 0029 002A  |#$%&'()*|
+	00000040  002B 002C 002D 002E - 002F 0030 0031 0032  |+,-./012|
+	00000048  0033 0034 0035 0036 - 0037 0038 0039 003A  |3456789:|
+	00000050  003B 003C 003D 003E - 003F 0040 0041 0042  |;<=>?@AB|
+	00000058  0043 0044 0045 0046 - 0047 0048 0049 004A  |CDEFGHIJ|
+	00000060  004B 004C 004D 004E - 004F 0050 0051 0052  |KLMNOPQR|
+	00000068  0053 0054 0055 0056 - 0057 0058 0059 005A  |STUVWXYZ|
+	00000070  005B 005C 005D 005E - 005F 0060 0061 0062  |[\]^_`ab|
+	00000078  0063 0064 0065 0066 - 0067 0068 0069 006A  |cdefghij|
+	00000080  006B 006C 006D 006E - 006F 0070 0071 0072  |klmnopqr|
+	00000088  0073 0074 0075 0076 - 0077 0078 0079 007A  |stuvwxyz|
+	00000090  007B 007C 007D 007E - 007F 0080 0081 0082  |{|}~....|
+	00000098  0083 0084 0085 0086 - 0087 0088 0089 008A  |........|
+	000000A0  008B 008C 008D 008E - 008F 0090 0091 0092  |........|
+	000000A8  0093 0094 0095 0096 - 0097 0098 0099 009A  |........|
+	000000B0  009B 009C 009D 009E - 009F 00A0 00A1 00A2  |..... ¡¢|
+	000000B8  00A3 00A4 00A5 00A6 - 00A7 00A8 00A9 00AA  |£¤¥¦§¨©ª|
+	000000C0  00AB 00AC 00AD 00AE - 00AF 00B0 00B1 00B2  |«¬­®¯°±²|
+	000000C8  00B3 00B4 00B5 00B6 - 00B7 00B8 00B9 00BA  |³´µ¶·¸¹º|
+	000000D0  00BB 00BC 00BD 00BE - 00BF 00C0 00C1 00C2  |»¼½¾¿ÀÁÂ|
+	000000D8  00C3 00C4 00C5 00C6 - 00C7 00C8 00C9 00CA  |ÃÄÅÆÇÈÉÊ|
+	000000E0  00CB 00CC 00CD 00CE - 00CF 00D0 00D1 00D2  |ËÌÍÎÏÐÑÒ|
+	000000E8  00D3 00D4 00D5 00D6 - 00D7 00D8 00D9 00DA  |ÓÔÕÖ×ØÙÚ|
+	000000F0  00DB 00DC 00DD 00DE - 00DF 00E0 00E1 00E2  |ÛÜÝÞßàáâ|
+	000000F8  00E3 00E4 00E5 00E6 - 00E7 00E8 00E9 00EA  |ãäåæçèéê|
+	00000100  00EB 00EC 00ED 00EE - 00EF 00F0 00F1 00F2  |ëìíîïðñò|
+	00000108  00F3 00F4 00F5 00F6 - 00F7 00F8 00F9 00FA  |óôõö÷øùú|
+	00000110  00FB 00FC 00FD 00FE - 00FF 000A EFFF 000A  |ûüýþÿ.�.|
+	00000118  EFC2 000A EFEF EFBF - EFC0 000A EFC0 EF80  |�.���.��|
+	00000120  000A EFE0 EF80 EF80 - 000A FFFD EFEF EFBF  |.���.���|
+	00000128  EFBE EFEF EFBF EFBF - 000A 007A 000A       |����.z.|
+---
+name: integer-base-one-3Ar
+description:
+	some sample code for hexdumping; NUL and binary safe
+stdin:
+	{
+		print 'Hello, World!\\\nこんにちは!'
+		typeset -Uui16 i=0x100
+		# change that to 0xFF once we can handle embedded
+		# NUL characters in strings / here documents
+		while (( i++ < 0x1FF )); do
+			print -n "\x${i#16#1}"
+		done
+		print '\0z'
+	} | {
+		# integer-base-one-3Ar
+		typeset -Uui16 -Z11 pos=0
+		typeset -Uui16 -Z5 hv=2147483647
+		dasc=
+		if read -arN -1 line; then
+			typeset -i1 line
+			i=0
+			while (( i < ${#line[*]} )); do
+				hv=${line[i++]}
+				if (( (pos & 15) == 0 )); then
+					(( pos )) && print "$dasc|"
+					print -n "${pos#16#}  "
+					dasc=' |'
+				fi
+				print -n "${hv#16#} "
+				if (( (hv < 32) || (hv > 126) )); then
+					dasc=$dasc.
+				else
+					dasc=$dasc${line[i-1]#1#}
+				fi
+				(( (pos++ & 15) == 7 )) && print -n -- '- '
+			done
+		fi
+		while (( pos & 15 )); do
+			print -n '   '
+			(( (pos++ & 15) == 7 )) && print -n -- '- '
+		done
+		(( hv == 2147483647 )) || print "$dasc|"
+	}
+expected-stdout:
+	00000000  48 65 6C 6C 6F 2C 20 57 - 6F 72 6C 64 21 5C 0A E3  |Hello, World!\..|
+	00000010  81 93 E3 82 93 E3 81 AB - E3 81 A1 E3 81 AF EF BC  |................|
+	00000020  81 0A 01 02 03 04 05 06 - 07 08 09 0A 0B 0C 0D 0E  |................|
+	00000030  0F 10 11 12 13 14 15 16 - 17 18 19 1A 1B 1C 1D 1E  |................|
+	00000040  1F 20 21 22 23 24 25 26 - 27 28 29 2A 2B 2C 2D 2E  |. !"#$%&'()*+,-.|
+	00000050  2F 30 31 32 33 34 35 36 - 37 38 39 3A 3B 3C 3D 3E  |/0123456789:;<=>|
+	00000060  3F 40 41 42 43 44 45 46 - 47 48 49 4A 4B 4C 4D 4E  |?@ABCDEFGHIJKLMN|
+	00000070  4F 50 51 52 53 54 55 56 - 57 58 59 5A 5B 5C 5D 5E  |OPQRSTUVWXYZ[\]^|
+	00000080  5F 60 61 62 63 64 65 66 - 67 68 69 6A 6B 6C 6D 6E  |_`abcdefghijklmn|
+	00000090  6F 70 71 72 73 74 75 76 - 77 78 79 7A 7B 7C 7D 7E  |opqrstuvwxyz{|}~|
+	000000A0  7F 80 81 82 83 84 85 86 - 87 88 89 8A 8B 8C 8D 8E  |................|
+	000000B0  8F 90 91 92 93 94 95 96 - 97 98 99 9A 9B 9C 9D 9E  |................|
+	000000C0  9F A0 A1 A2 A3 A4 A5 A6 - A7 A8 A9 AA AB AC AD AE  |................|
+	000000D0  AF B0 B1 B2 B3 B4 B5 B6 - B7 B8 B9 BA BB BC BD BE  |................|
+	000000E0  BF C0 C1 C2 C3 C4 C5 C6 - C7 C8 C9 CA CB CC CD CE  |................|
+	000000F0  CF D0 D1 D2 D3 D4 D5 D6 - D7 D8 D9 DA DB DC DD DE  |................|
+	00000100  DF E0 E1 E2 E3 E4 E5 E6 - E7 E8 E9 EA EB EC ED EE  |................|
+	00000110  EF F0 F1 F2 F3 F4 F5 F6 - F7 F8 F9 FA FB FC FD FE  |................|
+	00000120  FF 00 7A 0A             -                          |..z.|
+---
+name: integer-base-one-3Wr
+description:
+	some sample code for hexdumping Unicode; NUL and binary safe
+stdin:
+	set -U
+	{
+		print 'Hello, World!\\\nこんにちは!'
+		typeset -Uui16 i=0x100
+		# change that to 0xFF once we can handle embedded
+		# NUL characters in strings / here documents
+		while (( i++ < 0x1FF )); do
+			print -n "\u${i#16#1}"
+		done
+		print
+		print \\xff		# invalid utf-8
+		print \\xc2		# invalid 2-byte
+		print \\xef\\xbf\\xc0	# invalid 3-byte
+		print \\xc0\\x80	# non-minimalistic
+		print \\xe0\\x80\\x80	# non-minimalistic
+		print '�\x{FFFE}\x{FFFF}'	# end of range
+		print '\0z'		# embedded NUL
+	} | {
+		# integer-base-one-3Wr
+		typeset -Uui16 -Z11 pos=0
+		typeset -Uui16 -Z7 hv=2147483647
+		dasc=
+		if read -arN -1 line; then
+			typeset -i1 line
+			i=0
+			while (( i < ${#line[*]} )); do
+				hv=${line[i++]}
+				if (( (hv < 32) || \
+				    ((hv > 126) && (hv < 160)) )); then
+					dch=.
+				elif (( (hv & 0xFF80) == 0xEF80 )); then
+					dch=�
+				else
+					dch=${line[i-1]#1#}
+				fi
+				if (( (pos & 7) == 7 )); then
+					dasc=$dasc$dch
+					dch=
+				elif (( (pos & 7) == 0 )); then
+					(( pos )) && print "$dasc|"
+					print -n "${pos#16#}  "
+					dasc=' |'
+				fi
+				print -n "${hv#16#} "
+				(( (pos++ & 7) == 3 )) && \
+				    print -n -- '- '
+				dasc=$dasc$dch
+			done
+		fi
+		while (( pos & 7 )); do
+			print -n '     '
+			(( (pos++ & 7) == 3 )) && print -n -- '- '
+		done
+		(( hv == 2147483647 )) || print "$dasc|"
+	}
+expected-stdout:
+	00000000  0048 0065 006C 006C - 006F 002C 0020 0057  |Hello, W|
+	00000008  006F 0072 006C 0064 - 0021 005C 000A 3053  |orld!\.こ|
+	00000010  3093 306B 3061 306F - FF01 000A 0001 0002  |んにちは!...|
+	00000018  0003 0004 0005 0006 - 0007 0008 0009 000A  |........|
+	00000020  000B 000C 000D 000E - 000F 0010 0011 0012  |........|
+	00000028  0013 0014 0015 0016 - 0017 0018 0019 001A  |........|
+	00000030  001B 001C 001D 001E - 001F 0020 0021 0022  |..... !"|
+	00000038  0023 0024 0025 0026 - 0027 0028 0029 002A  |#$%&'()*|
+	00000040  002B 002C 002D 002E - 002F 0030 0031 0032  |+,-./012|
+	00000048  0033 0034 0035 0036 - 0037 0038 0039 003A  |3456789:|
+	00000050  003B 003C 003D 003E - 003F 0040 0041 0042  |;<=>?@AB|
+	00000058  0043 0044 0045 0046 - 0047 0048 0049 004A  |CDEFGHIJ|
+	00000060  004B 004C 004D 004E - 004F 0050 0051 0052  |KLMNOPQR|
+	00000068  0053 0054 0055 0056 - 0057 0058 0059 005A  |STUVWXYZ|
+	00000070  005B 005C 005D 005E - 005F 0060 0061 0062  |[\]^_`ab|
+	00000078  0063 0064 0065 0066 - 0067 0068 0069 006A  |cdefghij|
+	00000080  006B 006C 006D 006E - 006F 0070 0071 0072  |klmnopqr|
+	00000088  0073 0074 0075 0076 - 0077 0078 0079 007A  |stuvwxyz|
+	00000090  007B 007C 007D 007E - 007F 0080 0081 0082  |{|}~....|
+	00000098  0083 0084 0085 0086 - 0087 0088 0089 008A  |........|
+	000000A0  008B 008C 008D 008E - 008F 0090 0091 0092  |........|
+	000000A8  0093 0094 0095 0096 - 0097 0098 0099 009A  |........|
+	000000B0  009B 009C 009D 009E - 009F 00A0 00A1 00A2  |..... ¡¢|
+	000000B8  00A3 00A4 00A5 00A6 - 00A7 00A8 00A9 00AA  |£¤¥¦§¨©ª|
+	000000C0  00AB 00AC 00AD 00AE - 00AF 00B0 00B1 00B2  |«¬­®¯°±²|
+	000000C8  00B3 00B4 00B5 00B6 - 00B7 00B8 00B9 00BA  |³´µ¶·¸¹º|
+	000000D0  00BB 00BC 00BD 00BE - 00BF 00C0 00C1 00C2  |»¼½¾¿ÀÁÂ|
+	000000D8  00C3 00C4 00C5 00C6 - 00C7 00C8 00C9 00CA  |ÃÄÅÆÇÈÉÊ|
+	000000E0  00CB 00CC 00CD 00CE - 00CF 00D0 00D1 00D2  |ËÌÍÎÏÐÑÒ|
+	000000E8  00D3 00D4 00D5 00D6 - 00D7 00D8 00D9 00DA  |ÓÔÕÖ×ØÙÚ|
+	000000F0  00DB 00DC 00DD 00DE - 00DF 00E0 00E1 00E2  |ÛÜÝÞßàáâ|
+	000000F8  00E3 00E4 00E5 00E6 - 00E7 00E8 00E9 00EA  |ãäåæçèéê|
+	00000100  00EB 00EC 00ED 00EE - 00EF 00F0 00F1 00F2  |ëìíîïðñò|
+	00000108  00F3 00F4 00F5 00F6 - 00F7 00F8 00F9 00FA  |óôõö÷øùú|
+	00000110  00FB 00FC 00FD 00FE - 00FF 000A EFFF 000A  |ûüýþÿ.�.|
+	00000118  EFC2 000A EFEF EFBF - EFC0 000A EFC0 EF80  |�.���.��|
+	00000120  000A EFE0 EF80 EF80 - 000A FFFD EFEF EFBF  |.���.���|
+	00000128  EFBE EFEF EFBF EFBF - 000A 0000 007A 000A  |����..z.|
+---
+name: integer-base-one-4
+description:
+	Check if ksh93-style base-one integers work
+category: !smksh
+stdin:
+	set -U
+	echo 1 $(('a'))
+	(echo 2f $(('aa'))) 2>&1 | sed "s/^[^']*'/2p '/"
+	echo 3 $(('…'))
+	x="'a'"
+	echo "4 <$x>"
+	echo 5 $(($x))
+	echo 6 $((x))
+expected-stdout:
+	1 97
+	2p 'aa': multi-character character constant
+	3 8230
+	4 <'a'>
+	5 97
+	6 97
+---
+name: integer-base-one-5A
+description:
+	Check to see that we’re NUL and Unicode safe
+stdin:
+	set +U
+	print 'a\0b\xfdz' >x
+	read -a y <x
+	set -U
+	typeset -Uui16 y
+	print ${y[*]} .
+expected-stdout:
+	16#61 16#0 16#62 16#FD 16#7A .
+---
+name: integer-base-one-5W
+description:
+	Check to see that we’re NUL and Unicode safe
+stdin:
+	set -U
+	print 'a\0b€c' >x
+	read -a y <x
+	set +U
+	typeset -Uui16 y
+	print ${y[*]} .
+expected-stdout:
+	16#61 16#0 16#62 16#20AC 16#63 .
+---
+name: ulimit-1
+description:
+	Check if we can use a specific syntax idiom for ulimit
+category: !os:syllable
+stdin:
+	if ! x=$(ulimit -d) || [[ $x = unknown ]]; then
+		#echo expected to fail on this OS
+		echo okay
+	else
+		ulimit -dS $x && echo okay
+	fi
+expected-stdout:
+	okay
+---
+name: redir-1
+description:
+	Check some of the most basic invariants of I/O redirection
+stdin:
+	i=0
+	function d {
+		print o$i.
+		print -u2 e$((i++)).
+	}
+	d >a 2>b
+	echo =1=
+	cat a
+	echo =2=
+	cat b
+	echo =3=
+	d 2>&1 >c
+	echo =4=
+	cat c
+	echo =5=
+expected-stdout:
+	=1=
+	o0.
+	=2=
+	e0.
+	=3=
+	e1.
+	=4=
+	o1.
+	=5=
+---
+name: bashiop-1
+description:
+	Check if GNU bash-like I/O redirection works
+	Part 1: this is also supported by GNU bash
+category: shell:legacy-no
+stdin:
+	exec 3>&1
+	function threeout {
+		echo ras
+		echo dwa >&2
+		echo tri >&3
+	}
+	threeout &>foo
+	echo ===
+	cat foo
+expected-stdout:
+	tri
+	===
+	ras
+	dwa
+---
+name: bashiop-2a
+description:
+	Check if GNU bash-like I/O redirection works
+	Part 2: this is *not* supported by GNU bash
+category: shell:legacy-no
+stdin:
+	exec 3>&1
+	function threeout {
+		echo ras
+		echo dwa >&2
+		echo tri >&3
+	}
+	threeout 3&>foo
+	echo ===
+	cat foo
+expected-stdout:
+	ras
+	===
+	dwa
+	tri
+---
+name: bashiop-2b
+description:
+	Check if GNU bash-like I/O redirection works
+	Part 2: this is *not* supported by GNU bash
+category: shell:legacy-no
+stdin:
+	exec 3>&1
+	function threeout {
+		echo ras
+		echo dwa >&2
+		echo tri >&3
+	}
+	threeout 3>foo &>&3
+	echo ===
+	cat foo
+expected-stdout:
+	===
+	ras
+	dwa
+	tri
+---
+name: bashiop-2c
+description:
+	Check if GNU bash-like I/O redirection works
+	Part 2: this is supported by GNU bash 4 only
+category: shell:legacy-no
+stdin:
+	echo mir >foo
+	set -o noclobber
+	exec 3>&1
+	function threeout {
+		echo ras
+		echo dwa >&2
+		echo tri >&3
+	}
+	threeout &>>foo
+	echo ===
+	cat foo
+expected-stdout:
+	tri
+	===
+	mir
+	ras
+	dwa
+---
+name: bashiop-3a
+description:
+	Check if GNU bash-like I/O redirection fails correctly
+	Part 1: this is also supported by GNU bash
+category: shell:legacy-no
+stdin:
+	echo mir >foo
+	set -o noclobber
+	exec 3>&1
+	function threeout {
+		echo ras
+		echo dwa >&2
+		echo tri >&3
+	}
+	threeout &>foo
+	echo ===
+	cat foo
+expected-stdout:
+	===
+	mir
+expected-stderr-pattern: /.*: can't (create|overwrite) .*/
+---
+name: bashiop-3b
+description:
+	Check if GNU bash-like I/O redirection fails correctly
+	Part 2: this is *not* supported by GNU bash
+category: shell:legacy-no
+stdin:
+	echo mir >foo
+	set -o noclobber
+	exec 3>&1
+	function threeout {
+		echo ras
+		echo dwa >&2
+		echo tri >&3
+	}
+	threeout &>|foo
+	echo ===
+	cat foo
+expected-stdout:
+	tri
+	===
+	ras
+	dwa
+---
+name: bashiop-4
+description:
+	Check if GNU bash-like I/O redirection works
+	Part 4: this is also supported by GNU bash,
+	but failed in some mksh versions
+category: shell:legacy-no
+stdin:
+	exec 3>&1
+	function threeout {
+		echo ras
+		echo dwa >&2
+		echo tri >&3
+	}
+	function blubb {
+		[[ -e bar ]] && threeout "$bf" &>foo
+	}
+	blubb
+	echo -n >bar
+	blubb
+	echo ===
+	cat foo
+expected-stdout:
+	tri
+	===
+	ras
+	dwa
+---
+name: bashiop-5-normal
+description:
+	Check if GNU bash-like I/O redirection is only supported
+	in !POSIX !sh mode as it breaks existing scripts' syntax
+category: shell:legacy-no
+stdin:
+	:>x; echo 1 "$("$__progname" -c 'echo foo>/dev/null&>x echo bar')" = "$(<x)" .
+	:>x; echo 2 "$("$__progname" -o posix -c 'echo foo>/dev/null&>x echo bar')" = "$(<x)" .
+	:>x; echo 3 "$("$__progname" -o sh -c 'echo foo>/dev/null&>x echo bar')" = "$(<x)" .
+expected-stdout:
+	1  = foo echo bar .
+	2  = bar .
+	3  = bar .
+---
+name: bashiop-5-legacy
+description:
+	Check if GNU bash-like I/O redirection is not parsed
+	in lksh as it breaks existing scripts' syntax
+category: shell:legacy-yes
+stdin:
+	:>x; echo 1 "$("$__progname" -c 'echo foo>/dev/null&>x echo bar')" = "$(<x)" .
+	:>x; echo 2 "$("$__progname" -o posix -c 'echo foo>/dev/null&>x echo bar')" = "$(<x)" .
+	:>x; echo 3 "$("$__progname" -o sh -c 'echo foo>/dev/null&>x echo bar')" = "$(<x)" .
+expected-stdout:
+	1  = bar .
+	2  = bar .
+	3  = bar .
+---
+name: mkshiop-1
+description:
+	Check for support of more than 9 file descriptors
+category: !convfds
+stdin:
+	read -u10 foo 10<<< bar
+	echo x$foo
+expected-stdout:
+	xbar
+---
+name: mkshiop-2
+description:
+	Check for support of more than 9 file descriptors
+category: !convfds
+stdin:
+	exec 12>foo
+	print -u12 bar
+	echo baz >&12
+	cat foo
+expected-stdout:
+	bar
+	baz
+---
+name: oksh-eval
+description:
+	Check expansions.
+stdin:
+	a=
+	for n in ${a#*=}; do echo 1hu ${n} .; done
+	for n in "${a#*=}"; do echo 1hq ${n} .; done
+	for n in ${a##*=}; do echo 2hu ${n} .; done
+	for n in "${a##*=}"; do echo 2hq ${n} .; done
+	for n in ${a%=*}; do echo 1pu ${n} .; done
+	for n in "${a%=*}"; do echo 1pq ${n} .; done
+	for n in ${a%%=*}; do echo 2pu ${n} .; done
+	for n in "${a%%=*}"; do echo 2pq ${n} .; done
+expected-stdout:
+	1hq .
+	2hq .
+	1pq .
+	2pq .
+---
+name: oksh-and-list-error-1
+description:
+	Test exit status of rightmost element in 2 element && list in -e mode
+stdin:
+	true && false
+	echo "should not print"
+arguments: !-e!
+expected-exit: e != 0
+---
+name: oksh-and-list-error-2
+description:
+	Test exit status of rightmost element in 3 element && list in -e mode
+stdin:
+	true && true && false
+	echo "should not print"
+arguments: !-e!
+expected-exit: e != 0
+---
+name: oksh-or-list-error-1
+description:
+	Test exit status of || list in -e mode
+stdin:
+	false || false
+	echo "should not print"
+arguments: !-e!
+expected-exit: e != 0
+---
+name: oksh-longline-crash
+description:
+	This used to cause a core dump
+stdin:
+	ulimit -c 0
+	deplibs="-lz -lpng /usr/local/lib/libjpeg.la -ltiff -lm -lX11 -lXext /usr/local/lib/libiconv.la -L/usr/local/lib -L/usr/ports/devel/gettext/w-gettext-0.10.40/gettext-0.10.40/intl/.libs /usr/local/lib/libintl.la /usr/local/lib/libglib.la /usr/local/lib/libgmodule.la -lintl -lm -lX11 -lXext -L/usr/X11R6/lib -lglib -lgmodule -L/usr/local/lib /usr/local/lib/libgdk.la -lintl -lm -lX11 -lXext -L/usr/X11R6/lib -lglib -lgmodule -L/usr/local/lib /usr/local/lib/libgtk.la -ltiff -ljpeg -lz -lpng -lm -lX11 -lXext -lintl -lglib -lgmodule -lgdk -lgtk -L/usr/X11R6/lib -lglib -lgmodule -L/usr/local/lib /usr/local/lib/libgdk_pixbuf.la -lz -lpng /usr/local/lib/libiconv.la -L/usr/local/lib -L/usr/ports/devel/gettext/w-gettext-0.10.40/gettext-0.10.40/intl/.libs /usr/local/lib/libintl.la /usr/local/lib/libglib.la -lm -lm /usr/local/lib/libaudiofile.la -lm -lm -laudiofile -L/usr/local/lib /usr/local/lib/libesd.la -lm -lz -L/usr/local/lib /usr/local/lib/libgnomesupport.la -lm -lz -lm -lglib -L/usr/local/lib /usr/local/lib/libgnome.la -lX11 -lXext /usr/local/lib/libiconv.la -L/usr/local/lib -L/usr/ports/devel/gettext/w-gettext-0.10.40/gettext-0.10.40/intl/.libs /usr/local/lib/libintl.la /usr/local/lib/libgmodule.la -lintl -lm -lX11 -lXext -L/usr/X11R6/lib -lglib -lgmodule -L/usr/local/lib /usr/local/lib/libgdk.la -lintl -lm -lX11 -lXext -L/usr/X11R6/lib -lglib -lgmodule -L/usr/local/lib /usr/local/lib/libgtk.la -lICE -lSM -lz -lpng /usr/local/lib/libungif.la /usr/local/lib/libjpeg.la -ltiff -lm -lz -lpng /usr/local/lib/libungif.la -lz /usr/local/lib/libjpeg.la -ltiff -L/usr/local/lib -L/usr/X11R6/lib /usr/local/lib/libgdk_imlib.la -lm -L/usr/local/lib /usr/local/lib/libart_lgpl.la -lm -lz -lm -lX11 -lXext -lintl -lglib -lgmodule -lgdk -lgtk -lICE -lSM -lm -lX11 -lXext -lintl -lglib -lgmodule -lgdk -lgtk -L/usr/X11R6/lib -lm -lz -lpng -lungif -lz -ljpeg -ltiff -ljpeg -lgdk_imlib -lglib -lm -laudiofile -lm -laudiofile -lesd -L/usr/local/lib /usr/local/lib/libgnomeui.la -lz -lz /usr/local/lib/libxml.la -lz -lz -lz /usr/local/lib!
 /libxml.la -lm -lX11 -lXext /usr/local/lib/libiconv.la -L/usr/ports/devel/gettext/w-gettext-0.10.40/gettext-0.10.40/intl/.libs /usr/local/lib/libintl.la /usr/local/lib/libglib.la /usr/local/lib/libgmodule.la -lintl -lglib -lgmodule /usr/local/lib/libgdk.la /usr/local/lib/libgtk.la -L/usr/X11R6/lib -L/usr/local/lib /usr/local/lib/libglade.la -lz -lz -lz /usr/local/lib/libxml.la /usr/local/lib/libglib.la -lm -lm /usr/local/lib/libaudiofile.la -lm -lm -laudiofile /usr/local/lib/libesd.la -lm -lz /usr/local/lib/libgnomesupport.la -lm -lz -lm -lglib /usr/local/lib/libgnome.la -lX11 -lXext /usr/local/lib/libiconv.la -L/usr/ports/devel/gettext/w-gettext-0.10.40/gettext-0.10.40/intl/.libs /usr/local/lib/libintl.la /usr/local/lib/libgmodule.la -lintl -lm -lX11 -lXext -lglib -lgmodule /usr/local/lib/libgdk.la -lintl -lm -lX11 -lXext -lglib -lgmodule /usr/local/lib/libgtk.la -lICE -lSM -lz -lpng /usr/local/lib/libungif.la /usr/local/lib/libjpeg.la -ltiff -lm -lz -lz /usr/local/lib/libgdk_imlib.la /usr/local/lib/libart_lgpl.la -lm -lz -lm -lX11 -lXext -lintl -lglib -lgmodule -lgdk -lgtk -lm -lX11 -lXext -lintl -lglib -lgmodule -lgdk -lgtk -lm -lz -lungif -lz -ljpeg -ljpeg -lgdk_imlib -lglib -lm -laudiofile -lm -laudiofile -lesd /usr/local/lib/libgnomeui.la -L/usr/X11R6/lib -L/usr/local/lib /usr/local/lib/libglade-gnome.la /usr/local/lib/libglib.la -lm -lm /usr/local/lib/libaudiofile.la -lm -lm -laudiofile -L/usr/local/lib /usr/local/lib/libesd.la -lm -lz -L/usr/local/lib /usr/local/lib/libgnomesupport.la -lm -lz -lm -lglib -L/usr/local/lib /usr/local/lib/libgnome.la -lX11 -lXext /usr/local/lib/libiconv.la -L/usr/local/lib -L/usr/ports/devel/gettext/w-gettext-0.10.40/gettext-0.10.40/intl/.libs /usr/local/lib/libintl.la /usr/local/lib/libgmodule.la -lintl -lm -lX11 -lXext -L/usr/X11R6/lib -lglib -lgmodule -L/usr/local/lib /usr/local/lib/libgdk.la -lintl -lm -lX11 -lXext -L/usr/X11R6/lib -lglib -lgmodule -L/usr/local/lib /usr/local/lib/libgtk.la -lICE -lSM -lz -lpng /usr/local/lib/libungif.la /usr/local/lib/libjpeg.la -ltiff -!
 lm -lz -lpng /usr/local/lib/libungif.la -lz /usr/local/lib/libjpeg.la -ltiff -L/usr/local/lib -L/usr/X11R6/lib /usr/local/lib/libgdk_imlib.la -lm -L/usr/local/lib /usr/local/lib/libart_lgpl.la -lm -lz -lm -lX11 -lXext -lintl -lglib -lgmodule -lgdk -lgtk -lICE -lSM -lm -lX11 -lXext -lintl -lglib -lgmodule -lgdk -lgtk -L/usr/X11R6/lib -lm -lz -lpng -lungif -lz -ljpeg -ltiff -ljpeg -lgdk_imlib -lglib -lm -laudiofile -lm -laudiofile -lesd -L/usr/local/lib /usr/local/lib/libgnomeui.la -L/usr/X11R6/lib -L/usr/local/lib"
+	specialdeplibs="-lgnomeui -lart_lgpl -lgdk_imlib -ltiff -ljpeg -lungif -lpng -lz -lSM -lICE -lgtk -lgdk -lgmodule -lintl -lXext -lX11 -lgnome -lgnomesupport -lesd -laudiofile -lm -lglib"
+	for deplib in $deplibs; do
+		case $deplib in
+		-L*)
+			new_libs="$deplib $new_libs"
+			;;
+		*)
+			case " $specialdeplibs " in
+			*" $deplib "*)
+				new_libs="$deplib $new_libs";;
+			esac
+			;;
+		esac
+	done
+---
+name: oksh-seterror-1
+description:
+	The -e flag should be ignored when executing a compound list
+	followed by an if statement.
+stdin:
+	if true; then false && false; fi
+	true
+arguments: !-e!
+expected-exit: e == 0
+---
+name: oksh-seterror-2
+description:
+	The -e flag should be ignored when executing a compound list
+	followed by an if statement.
+stdin:
+	if true; then if true; then false && false; fi; fi
+	true
+arguments: !-e!
+expected-exit: e == 0
+---
+name: oksh-seterror-3
+description:
+	The -e flag should be ignored when executing a compound list
+	followed by an elif statement.
+stdin:
+	if true; then :; elif true; then false && false; fi
+arguments: !-e!
+expected-exit: e == 0
+---
+name: oksh-seterror-4
+description:
+	The -e flag should be ignored when executing a pipeline
+	beginning with '!'
+stdin:
+	for i in 1 2 3
+	do
+		false && false
+		true || false
+	done
+arguments: !-e!
+expected-exit: e == 0
+---
+name: oksh-seterror-5
+description:
+	The -e flag should be ignored when executing a pipeline
+	beginning with '!'
+stdin:
+	! true | false
+	true
+arguments: !-e!
+expected-exit: e == 0
+---
+name: oksh-seterror-6
+description:
+	When trapping ERR and EXIT, both traps should run in -e mode
+	when an error occurs.
+stdin:
+	trap 'echo EXIT' EXIT
+	trap 'echo ERR' ERR
+	set -e
+	false
+	echo DONE
+	exit 0
+arguments: !-e!
+expected-exit: e != 0
+expected-stdout:
+	ERR
+	EXIT
+---
+name: oksh-seterror-7
+description:
+	The -e flag within a command substitution should be honored
+stdin:
+	echo $( set -e; false; echo foo )
+arguments: !-e!
+expected-stdout:
+	
+---
+name: oksh-input-comsub
+description:
+	A command substitution using input redirection should exit with
+	failure if the input file does not exist.
+stdin:
+	var=$(< non-existent)
+expected-exit: e != 0
+expected-stderr-pattern: /non-existent/
+---
+name: oksh-empty-for-list
+description:
+	A for list which expands to zero items should not execute the body.
+stdin:
+	set foo bar baz ; for out in ; do echo $out ; done
+---
+name: oksh-varfunction-mod1
+description:
+	(Inspired by PR 2450 on OpenBSD.) Calling
+		FOO=bar f
+	where f is a ksh style function, should not set FOO in the current
+	env. If f is a Bourne style function, FOO should be set. Furthermore,
+	the function should receive a correct value of FOO. However, differing
+	from oksh, setting FOO in the function itself must change the value in
+	setting FOO in the function itself should not change the value in
+	global environment.
+stdin:
+	print '#!'"$__progname"'\nunset RANDOM\nexport | while IFS= read -r' \
+	    'RANDOM; do eval '\''print -r -- "$RANDOM=$'\''"$RANDOM"'\'\"\'\; \
+	    done >env; chmod +x env; PATH=.:$PATH
+	function k {
+		if [ x$FOO != xbar ]; then
+			echo 1
+			return 1
+		fi
+		x=$(env | grep FOO)
+		if [ "x$x" != "xFOO=bar" ]; then
+			echo 2
+			return 1;
+		fi
+		FOO=foo
+		return 0
+	}
+	b () {
+		if [ x$FOO != xbar ]; then
+			echo 3
+			return 1
+		fi
+		x=$(env | grep FOO)
+		if [ "x$x" != "xFOO=bar" ]; then
+			echo 4
+			return 1;
+		fi
+		FOO=foo
+		return 0
+	}
+	FOO=bar k
+	if [ $? != 0 ]; then
+		exit 1
+	fi
+	if [ x$FOO != x ]; then
+		exit 1
+	fi
+	FOO=bar b
+	if [ $? != 0 ]; then
+		exit 1
+	fi
+	if [ x$FOO != xfoo ]; then
+		exit 1
+	fi
+	FOO=barbar
+	FOO=bar k
+	if [ $? != 0 ]; then
+		exit 1
+	fi
+	if [ x$FOO != xbarbar ]; then
+		exit 1
+	fi
+	FOO=bar b
+	if [ $? != 0 ]; then
+		exit 1
+	fi
+	if [ x$FOO != xfoo ]; then
+		exit 1
+	fi
+---
+name: fd-cloexec-1
+description:
+	Verify that file descriptors > 2 are private for Korn shells
+	AT&T ksh93 does this still, which means we must keep it as well
+category: shell:legacy-no
+file-setup: file 644 "test.sh"
+	echo >&3 Fowl
+stdin:
+	exec 3>&1
+	"$__progname" test.sh
+expected-exit: e != 0
+expected-stderr-pattern:
+	/bad file descriptor/
+---
+name: fd-cloexec-2
+description:
+	Verify that file descriptors > 2 are not private for POSIX shells
+	See Debian Bug #154540, Closes: #499139
+file-setup: file 644 "test.sh"
+	echo >&3 Fowl
+stdin:
+	test -n "$POSH_VERSION" || set -o sh
+	exec 3>&1
+	"$__progname" test.sh
+expected-stdout:
+	Fowl
+---
+name: fd-cloexec-3
+description:
+	Verify that file descriptors > 2 are not private for LEGACY KSH
+category: shell:legacy-yes
+file-setup: file 644 "test.sh"
+	echo >&3 Fowl
+stdin:
+	exec 3>&1
+	"$__progname" test.sh
+expected-stdout:
+	Fowl
+---
+name: comsub-1a
+description:
+	COMSUB are now parsed recursively, so this works
+	see also regression-6: matching parenthesēs bug
+	Fails on: pdksh bash2 bash3 zsh
+	Passes on: bash4 ksh93 mksh(20110313+)
+stdin:
+	echo 1 $(case 1 in (1) echo yes;; (2) echo no;; esac) .
+	echo 2 $(case 1 in 1) echo yes;; 2) echo no;; esac) .
+	TEST=1234; echo 3 ${TEST: $(case 1 in (1) echo 1;; (*) echo 2;; esac)} .
+	TEST=5678; echo 4 ${TEST: $(case 1 in 1) echo 1;; *) echo 2;; esac)} .
+	a=($(case 1 in (1) echo 1;; (*) echo 2;; esac)); echo 5 ${a[0]} .
+	a=($(case 1 in 1) echo 1;; *) echo 2;; esac)); echo 6 ${a[0]} .
+expected-stdout:
+	1 yes .
+	2 yes .
+	3 234 .
+	4 678 .
+	5 1 .
+	6 1 .
+---
+name: comsub-1b
+description:
+	COMSUB are now parsed recursively, so this works
+	Fails on: pdksh bash2 bash3 bash4 zsh
+	Passes on: ksh93 mksh(20110313+)
+stdin:
+	echo 1 $(($(case 1 in (1) echo 1;; (*) echo 2;; esac)+10)) .
+	echo 2 $(($(case 1 in 1) echo 1;; *) echo 2;; esac)+20)) .
+	(( a = $(case 1 in (1) echo 1;; (*) echo 2;; esac) )); echo 3 $a .
+	(( a = $(case 1 in 1) echo 1;; *) echo 2;; esac) )); echo 4 $a .
+	a=($(($(case 1 in (1) echo 1;; (*) echo 2;; esac)+10))); echo 5 ${a[0]} .
+	a=($(($(case 1 in 1) echo 1;; *) echo 2;; esac)+20))); echo 6 ${a[0]} .
+expected-stdout:
+	1 11 .
+	2 21 .
+	3 1 .
+	4 1 .
+	5 11 .
+	6 21 .
+---
+name: comsub-2
+description:
+	RedHat BZ#496791 – another case of missing recursion
+	in parsing COMSUB expressions
+	Fails on: pdksh bash2 bash3¹ bash4¹ zsh
+	Passes on: ksh93 mksh(20110305+)
+	① bash[34] seem to choke on comment ending with backslash-newline
+stdin:
+	# a comment with " ' \
+	x=$(
+	echo yes
+	# a comment with " ' \
+	)
+	echo $x
+expected-stdout:
+	yes
+---
+name: comsub-3
+description:
+	Extended test for COMSUB explaining why a recursive parser
+	is a must (a non-recursive parser cannot pass all three of
+	these test cases, especially the ‘#’ is difficult)
+stdin:
+	print '#!'"$__progname"'\necho 1234' >id; chmod +x id; PATH=.:$PATH
+	echo $(typeset -i10 x=16#20; echo $x)
+	echo $(typeset -Uui16 x=16#$(id -u)
+	) .
+	echo $(c=1; d=1
+	typeset -Uui16 a=36#foo; c=2
+	typeset -Uui16 b=36 #foo; d=2
+	echo $a $b $c $d)
+expected-stdout:
+	32
+	.
+	16#4F68 16#24 2 1
+---
+name: comsub-4
+description:
+	Check the tree dump functions for !MKSH_SMALL functionality
+category: !smksh
+stdin:
+	x() { case $1 in u) echo x ;;& *) echo $1 ;; esac; }
+	typeset -f x
+expected-stdout:
+	x() {
+		case $1 in
+		(u)
+			echo x 
+			;|
+		(*)
+			echo $1 
+			;;
+		esac 
+	} 
+---
+name: comsub-5
+description:
+	Check COMSUB works with aliases (does not expand them twice)
+stdin:
+	print '#!'"$__progname"'\nfor x in "$@"; do print -r -- "$x"; done' >pfn
+	chmod +x pfn
+	alias echo='echo a'
+	foo() {
+		./pfn "$(echo foo)"
+	}
+	./pfn "$(echo b)"
+	typeset -f foo
+expected-stdout:
+	a b
+	foo() {
+		./pfn "$(echo foo )" 
+	} 
+---
+name: comsub-torture
+description:
+	Check the tree dump functions work correctly
+stdin:
+	if [[ -z $__progname ]]; then echo >&2 call me with __progname; exit 1; fi
+	while IFS= read -r line; do
+		if [[ $line = '#1' ]]; then
+			lastf=0
+			continue
+		elif [[ $line = EOFN* ]]; then
+			fbody=$fbody$'\n'$line
+			continue
+		elif [[ $line != '#'* ]]; then
+			fbody=$fbody$'\n\t'$line
+			continue
+		fi
+		if (( lastf )); then
+			x="inline_${nextf}() {"$fbody$'\n}\n'
+			print -nr -- "$x"
+			print -r -- "${x}typeset -f inline_$nextf" | "$__progname"
+			x="function comsub_$nextf { x=\$("$fbody$'\n); }\n'
+			print -nr -- "$x"
+			print -r -- "${x}typeset -f comsub_$nextf" | "$__progname"
+			x="function reread_$nextf { x=\$(("$fbody$'\n)|tr u x); }\n'
+			print -nr -- "$x"
+			print -r -- "${x}typeset -f reread_$nextf" | "$__progname"
+		fi
+		lastf=1
+		fbody=
+		nextf=${line#?}
+	done <<'EOD'
+	#1
+	#TCOM
+	vara=1  varb='2  3'  cmd  arg1  $arg2  "$arg3  4"
+	#TPAREN_TPIPE_TLIST
+	(echo $foo  |  tr -dc 0-9; echo)
+	#TAND_TOR
+	cmd  &&  echo ja  ||  echo nein
+	#TSELECT
+	select  file  in  *;  do  echo  "<$file>" ;  break ;  done
+	#TFOR_TTIME
+	time  for  i  in  {1,2,3}  ;  do  echo  $i ;  done
+	#TCASE
+	case  $foo  in  1)  echo eins;& 2) echo zwei  ;| *) echo kann net bis drei zählen;;  esac
+	#TIF_TBANG_TDBRACKET_TELIF
+	if  !  [[  1  =  1  ]]  ;  then  echo eins;  elif [[ 1 = 2 ]]; then echo zwei  ;else echo drei; fi
+	#TWHILE
+	i=1; while (( i < 10 )); do echo $i; let ++i; done
+	#TUNTIL
+	i=10; until  (( !--i )) ; do echo $i; done
+	#TCOPROC
+	cat  *  |&  ls
+	#TFUNCT_TBRACE_TASYNC
+	function  korn  {  echo eins; echo zwei ;  }
+	bourne  ()  {  logger *  &  }
+	#IOREAD_IOCAT
+	tr  x  u  0<foo  >>bar
+	#IOWRITE_IOCLOB_IOHERE_noIOSKIP
+	cat  >|bar  <<'EOFN'
+	foo
+	EOFN
+	#IOWRITE_noIOCLOB_IOHERE_IOSKIP
+	cat  1>bar  <<-EOFI
+	foo
+	EOFI
+	#IORDWR_IODUP
+	sh  1<>/dev/console  0<&1  2>&1
+	#COMSUB_EXPRSUB_FUNSUB_VALSUB
+	echo $(true) $((1+ 2)) ${  :;} ${| REPLY=x;}
+	#QCHAR_OQUOTE_CQUOTE
+	echo fo\ob\"a\`r\'b\$az
+	echo "fo\ob\"a\`r\'b\$az"
+	echo 'fo\ob\"a\`r'\''b\$az'
+	#OSUBST_CSUBST_OPAT_SPAT_CPAT
+	[[ ${foo#bl\(u\)b} = @(bar|baz) ]]
+	#heredoc_closed
+	x=$(cat <<EOFN
+	note there must be no space between EOFN and )
+	EOFN); echo $x
+	#heredoc_space
+	x=$(cat <<EOFN\ 
+	note the space between EOFN and ) is actually part of the here document marker
+	EOFN ); echo $x
+	#patch_motd
+	x=$(sysctl -n kern.version | sed 1q)
+	[[ -s /etc/motd && "$([[ "$(head -1 /etc/motd)" != $x ]] && \
+	    ed -s /etc/motd 2>&1 <<-EOF
+		1,/^\$/d
+		0a
+			$x
+	
+		.
+		wq
+	EOF)" = @(?) ]] && rm -f /etc/motd
+	if [[ ! -s /etc/motd ]]; then
+		install -c -o root -g wheel -m 664 /dev/null /etc/motd
+		print -- "$x\n" >/etc/motd
+	fi
+	#wdarrassign
+	case x in
+	x) a+=b; c+=(d e)
+	esac
+	#0
+	EOD
+expected-stdout:
+	inline_TCOM() {
+		vara=1  varb='2  3'  cmd  arg1  $arg2  "$arg3  4"
+	}
+	inline_TCOM() {
+		vara=1 varb="2  3" cmd arg1 $arg2 "$arg3  4" 
+	} 
+	function comsub_TCOM { x=$(
+		vara=1  varb='2  3'  cmd  arg1  $arg2  "$arg3  4"
+	); }
+	function comsub_TCOM {
+		x=$(vara=1 varb="2  3" cmd arg1 $arg2 "$arg3  4" ) 
+	} 
+	function reread_TCOM { x=$((
+		vara=1  varb='2  3'  cmd  arg1  $arg2  "$arg3  4"
+	)|tr u x); }
+	function reread_TCOM {
+		x=$(( vara=1 varb="2  3" cmd arg1 $arg2 "$arg3  4" ) | tr u x ) 
+	} 
+	inline_TPAREN_TPIPE_TLIST() {
+		(echo $foo  |  tr -dc 0-9; echo)
+	}
+	inline_TPAREN_TPIPE_TLIST() {
+		( echo $foo | tr -dc 0-9 
+		  echo ) 
+	} 
+	function comsub_TPAREN_TPIPE_TLIST { x=$(
+		(echo $foo  |  tr -dc 0-9; echo)
+	); }
+	function comsub_TPAREN_TPIPE_TLIST {
+		x=$(( echo $foo | tr -dc 0-9 ; echo ) ) 
+	} 
+	function reread_TPAREN_TPIPE_TLIST { x=$((
+		(echo $foo  |  tr -dc 0-9; echo)
+	)|tr u x); }
+	function reread_TPAREN_TPIPE_TLIST {
+		x=$(( ( echo $foo | tr -dc 0-9 ; echo ) ) | tr u x ) 
+	} 
+	inline_TAND_TOR() {
+		cmd  &&  echo ja  ||  echo nein
+	}
+	inline_TAND_TOR() {
+		cmd && echo ja || echo nein 
+	} 
+	function comsub_TAND_TOR { x=$(
+		cmd  &&  echo ja  ||  echo nein
+	); }
+	function comsub_TAND_TOR {
+		x=$(cmd && echo ja || echo nein ) 
+	} 
+	function reread_TAND_TOR { x=$((
+		cmd  &&  echo ja  ||  echo nein
+	)|tr u x); }
+	function reread_TAND_TOR {
+		x=$(( cmd && echo ja || echo nein ) | tr u x ) 
+	} 
+	inline_TSELECT() {
+		select  file  in  *;  do  echo  "<$file>" ;  break ;  done
+	}
+	inline_TSELECT() {
+		select file in * 
+		do
+			echo "<$file>" 
+			break 
+		done 
+	} 
+	function comsub_TSELECT { x=$(
+		select  file  in  *;  do  echo  "<$file>" ;  break ;  done
+	); }
+	function comsub_TSELECT {
+		x=$(select file in * ; do echo "<$file>" ; break ; done ) 
+	} 
+	function reread_TSELECT { x=$((
+		select  file  in  *;  do  echo  "<$file>" ;  break ;  done
+	)|tr u x); }
+	function reread_TSELECT {
+		x=$(( select file in * ; do echo "<$file>" ; break ; done ) | tr u x ) 
+	} 
+	inline_TFOR_TTIME() {
+		time  for  i  in  {1,2,3}  ;  do  echo  $i ;  done
+	}
+	inline_TFOR_TTIME() {
+		time for i in {1,2,3} 
+		do
+			echo $i 
+		done 
+	} 
+	function comsub_TFOR_TTIME { x=$(
+		time  for  i  in  {1,2,3}  ;  do  echo  $i ;  done
+	); }
+	function comsub_TFOR_TTIME {
+		x=$(time for i in {1,2,3} ; do echo $i ; done ) 
+	} 
+	function reread_TFOR_TTIME { x=$((
+		time  for  i  in  {1,2,3}  ;  do  echo  $i ;  done
+	)|tr u x); }
+	function reread_TFOR_TTIME {
+		x=$(( time for i in {1,2,3} ; do echo $i ; done ) | tr u x ) 
+	} 
+	inline_TCASE() {
+		case  $foo  in  1)  echo eins;& 2) echo zwei  ;| *) echo kann net bis drei zählen;;  esac
+	}
+	inline_TCASE() {
+		case $foo in
+		(1)
+			echo eins 
+			;&
+		(2)
+			echo zwei 
+			;|
+		(*)
+			echo kann net bis drei zählen 
+			;;
+		esac 
+	} 
+	function comsub_TCASE { x=$(
+		case  $foo  in  1)  echo eins;& 2) echo zwei  ;| *) echo kann net bis drei zählen;;  esac
+	); }
+	function comsub_TCASE {
+		x=$(case $foo in (1) echo eins  ;& (2) echo zwei  ;| (*) echo kann net bis drei zählen  ;; esac ) 
+	} 
+	function reread_TCASE { x=$((
+		case  $foo  in  1)  echo eins;& 2) echo zwei  ;| *) echo kann net bis drei zählen;;  esac
+	)|tr u x); }
+	function reread_TCASE {
+		x=$(( case $foo in (1) echo eins  ;& (2) echo zwei  ;| (*) echo kann net bis drei zählen  ;; esac ) | tr u x ) 
+	} 
+	inline_TIF_TBANG_TDBRACKET_TELIF() {
+		if  !  [[  1  =  1  ]]  ;  then  echo eins;  elif [[ 1 = 2 ]]; then echo zwei  ;else echo drei; fi
+	}
+	inline_TIF_TBANG_TDBRACKET_TELIF() {
+		if ! [[ 1 = 1 ]] 
+		then
+			echo eins 
+		elif [[ 1 = 2 ]] 
+		then
+			echo zwei 
+		else
+			echo drei 
+		fi 
+	} 
+	function comsub_TIF_TBANG_TDBRACKET_TELIF { x=$(
+		if  !  [[  1  =  1  ]]  ;  then  echo eins;  elif [[ 1 = 2 ]]; then echo zwei  ;else echo drei; fi
+	); }
+	function comsub_TIF_TBANG_TDBRACKET_TELIF {
+		x=$(if ! [[ 1 = 1 ]] ; then echo eins ; elif [[ 1 = 2 ]] ; then echo zwei ; else echo drei ; fi ) 
+	} 
+	function reread_TIF_TBANG_TDBRACKET_TELIF { x=$((
+		if  !  [[  1  =  1  ]]  ;  then  echo eins;  elif [[ 1 = 2 ]]; then echo zwei  ;else echo drei; fi
+	)|tr u x); }
+	function reread_TIF_TBANG_TDBRACKET_TELIF {
+		x=$(( if ! [[ 1 = 1 ]] ; then echo eins ; elif [[ 1 = 2 ]] ; then echo zwei ; else echo drei ; fi ) | tr u x ) 
+	} 
+	inline_TWHILE() {
+		i=1; while (( i < 10 )); do echo $i; let ++i; done
+	}
+	inline_TWHILE() {
+		i=1 
+		while let] " i < 10 " 
+		do
+			echo $i 
+			let ++i 
+		done 
+	} 
+	function comsub_TWHILE { x=$(
+		i=1; while (( i < 10 )); do echo $i; let ++i; done
+	); }
+	function comsub_TWHILE {
+		x=$(i=1 ; while let] " i < 10 " ; do echo $i ; let ++i ; done ) 
+	} 
+	function reread_TWHILE { x=$((
+		i=1; while (( i < 10 )); do echo $i; let ++i; done
+	)|tr u x); }
+	function reread_TWHILE {
+		x=$(( i=1 ; while let] " i < 10 " ; do echo $i ; let ++i ; done ) | tr u x ) 
+	} 
+	inline_TUNTIL() {
+		i=10; until  (( !--i )) ; do echo $i; done
+	}
+	inline_TUNTIL() {
+		i=10 
+		until let] " !--i " 
+		do
+			echo $i 
+		done 
+	} 
+	function comsub_TUNTIL { x=$(
+		i=10; until  (( !--i )) ; do echo $i; done
+	); }
+	function comsub_TUNTIL {
+		x=$(i=10 ; until let] " !--i " ; do echo $i ; done ) 
+	} 
+	function reread_TUNTIL { x=$((
+		i=10; until  (( !--i )) ; do echo $i; done
+	)|tr u x); }
+	function reread_TUNTIL {
+		x=$(( i=10 ; until let] " !--i " ; do echo $i ; done ) | tr u x ) 
+	} 
+	inline_TCOPROC() {
+		cat  *  |&  ls
+	}
+	inline_TCOPROC() {
+		cat * |& 
+		ls 
+	} 
+	function comsub_TCOPROC { x=$(
+		cat  *  |&  ls
+	); }
+	function comsub_TCOPROC {
+		x=$(cat * |&  ls ) 
+	} 
+	function reread_TCOPROC { x=$((
+		cat  *  |&  ls
+	)|tr u x); }
+	function reread_TCOPROC {
+		x=$(( cat * |&  ls ) | tr u x ) 
+	} 
+	inline_TFUNCT_TBRACE_TASYNC() {
+		function  korn  {  echo eins; echo zwei ;  }
+		bourne  ()  {  logger *  &  }
+	}
+	inline_TFUNCT_TBRACE_TASYNC() {
+		function korn {
+			echo eins 
+			echo zwei 
+		} 
+		bourne() {
+			logger * & 
+		} 
+	} 
+	function comsub_TFUNCT_TBRACE_TASYNC { x=$(
+		function  korn  {  echo eins; echo zwei ;  }
+		bourne  ()  {  logger *  &  }
+	); }
+	function comsub_TFUNCT_TBRACE_TASYNC {
+		x=$(function korn { echo eins ; echo zwei ; } ; bourne() { logger * &  } ) 
+	} 
+	function reread_TFUNCT_TBRACE_TASYNC { x=$((
+		function  korn  {  echo eins; echo zwei ;  }
+		bourne  ()  {  logger *  &  }
+	)|tr u x); }
+	function reread_TFUNCT_TBRACE_TASYNC {
+		x=$(( function korn { echo eins ; echo zwei ; } ; bourne() { logger * &  } ) | tr u x ) 
+	} 
+	inline_IOREAD_IOCAT() {
+		tr  x  u  0<foo  >>bar
+	}
+	inline_IOREAD_IOCAT() {
+		tr x u <foo >>bar 
+	} 
+	function comsub_IOREAD_IOCAT { x=$(
+		tr  x  u  0<foo  >>bar
+	); }
+	function comsub_IOREAD_IOCAT {
+		x=$(tr x u <foo >>bar ) 
+	} 
+	function reread_IOREAD_IOCAT { x=$((
+		tr  x  u  0<foo  >>bar
+	)|tr u x); }
+	function reread_IOREAD_IOCAT {
+		x=$(( tr x u <foo >>bar ) | tr u x ) 
+	} 
+	inline_IOWRITE_IOCLOB_IOHERE_noIOSKIP() {
+		cat  >|bar  <<'EOFN'
+		foo
+	EOFN
+	}
+	inline_IOWRITE_IOCLOB_IOHERE_noIOSKIP() {
+		cat >|bar <<"EOFN"
+		foo
+	EOFN
+	
+	} 
+	function comsub_IOWRITE_IOCLOB_IOHERE_noIOSKIP { x=$(
+		cat  >|bar  <<'EOFN'
+		foo
+	EOFN
+	); }
+	function comsub_IOWRITE_IOCLOB_IOHERE_noIOSKIP {
+		x=$(cat >|bar <<"EOFN"
+		foo
+	EOFN
+	) 
+	} 
+	function reread_IOWRITE_IOCLOB_IOHERE_noIOSKIP { x=$((
+		cat  >|bar  <<'EOFN'
+		foo
+	EOFN
+	)|tr u x); }
+	function reread_IOWRITE_IOCLOB_IOHERE_noIOSKIP {
+		x=$(( cat >|bar <<"EOFN"
+		foo
+	EOFN
+	) | tr u x ) 
+	} 
+	inline_IOWRITE_noIOCLOB_IOHERE_IOSKIP() {
+		cat  1>bar  <<-EOFI
+		foo
+		EOFI
+	}
+	inline_IOWRITE_noIOCLOB_IOHERE_IOSKIP() {
+		cat >bar <<-EOFI
+	foo
+	EOFI
+	
+	} 
+	function comsub_IOWRITE_noIOCLOB_IOHERE_IOSKIP { x=$(
+		cat  1>bar  <<-EOFI
+		foo
+		EOFI
+	); }
+	function comsub_IOWRITE_noIOCLOB_IOHERE_IOSKIP {
+		x=$(cat >bar <<-EOFI
+	foo
+	EOFI
+	) 
+	} 
+	function reread_IOWRITE_noIOCLOB_IOHERE_IOSKIP { x=$((
+		cat  1>bar  <<-EOFI
+		foo
+		EOFI
+	)|tr u x); }
+	function reread_IOWRITE_noIOCLOB_IOHERE_IOSKIP {
+		x=$(( cat >bar <<-EOFI
+	foo
+	EOFI
+	) | tr u x ) 
+	} 
+	inline_IORDWR_IODUP() {
+		sh  1<>/dev/console  0<&1  2>&1
+	}
+	inline_IORDWR_IODUP() {
+		sh 1<>/dev/console <&1 2>&1 
+	} 
+	function comsub_IORDWR_IODUP { x=$(
+		sh  1<>/dev/console  0<&1  2>&1
+	); }
+	function comsub_IORDWR_IODUP {
+		x=$(sh 1<>/dev/console <&1 2>&1 ) 
+	} 
+	function reread_IORDWR_IODUP { x=$((
+		sh  1<>/dev/console  0<&1  2>&1
+	)|tr u x); }
+	function reread_IORDWR_IODUP {
+		x=$(( sh 1<>/dev/console <&1 2>&1 ) | tr u x ) 
+	} 
+	inline_COMSUB_EXPRSUB_FUNSUB_VALSUB() {
+		echo $(true) $((1+ 2)) ${  :;} ${| REPLY=x;}
+	}
+	inline_COMSUB_EXPRSUB_FUNSUB_VALSUB() {
+		echo $(true ) $((1+ 2)) ${ : ;} ${|REPLY=x ;} 
+	} 
+	function comsub_COMSUB_EXPRSUB_FUNSUB_VALSUB { x=$(
+		echo $(true) $((1+ 2)) ${  :;} ${| REPLY=x;}
+	); }
+	function comsub_COMSUB_EXPRSUB_FUNSUB_VALSUB {
+		x=$(echo $(true ) $((1+ 2)) ${ : ;} ${|REPLY=x ;} ) 
+	} 
+	function reread_COMSUB_EXPRSUB_FUNSUB_VALSUB { x=$((
+		echo $(true) $((1+ 2)) ${  :;} ${| REPLY=x;}
+	)|tr u x); }
+	function reread_COMSUB_EXPRSUB_FUNSUB_VALSUB {
+		x=$(( echo $(true ) $((1+ 2)) ${ : ;} ${|REPLY=x ;} ) | tr u x ) 
+	} 
+	inline_QCHAR_OQUOTE_CQUOTE() {
+		echo fo\ob\"a\`r\'b\$az
+		echo "fo\ob\"a\`r\'b\$az"
+		echo 'fo\ob\"a\`r'\''b\$az'
+	}
+	inline_QCHAR_OQUOTE_CQUOTE() {
+		echo fo\ob\"a\`r\'b\$az 
+		echo "fo\ob\"a\`r\'b\$az" 
+		echo "fo\\ob\\\"a\\\`r"\'"b\\\$az" 
+	} 
+	function comsub_QCHAR_OQUOTE_CQUOTE { x=$(
+		echo fo\ob\"a\`r\'b\$az
+		echo "fo\ob\"a\`r\'b\$az"
+		echo 'fo\ob\"a\`r'\''b\$az'
+	); }
+	function comsub_QCHAR_OQUOTE_CQUOTE {
+		x=$(echo fo\ob\"a\`r\'b\$az ; echo "fo\ob\"a\`r\'b\$az" ; echo "fo\\ob\\\"a\\\`r"\'"b\\\$az" ) 
+	} 
+	function reread_QCHAR_OQUOTE_CQUOTE { x=$((
+		echo fo\ob\"a\`r\'b\$az
+		echo "fo\ob\"a\`r\'b\$az"
+		echo 'fo\ob\"a\`r'\''b\$az'
+	)|tr u x); }
+	function reread_QCHAR_OQUOTE_CQUOTE {
+		x=$(( echo fo\ob\"a\`r\'b\$az ; echo "fo\ob\"a\`r\'b\$az" ; echo "fo\\ob\\\"a\\\`r"\'"b\\\$az" ) | tr u x ) 
+	} 
+	inline_OSUBST_CSUBST_OPAT_SPAT_CPAT() {
+		[[ ${foo#bl\(u\)b} = @(bar|baz) ]]
+	}
+	inline_OSUBST_CSUBST_OPAT_SPAT_CPAT() {
+		[[ ${foo#bl\(u\)b} = @(bar|baz) ]] 
+	} 
+	function comsub_OSUBST_CSUBST_OPAT_SPAT_CPAT { x=$(
+		[[ ${foo#bl\(u\)b} = @(bar|baz) ]]
+	); }
+	function comsub_OSUBST_CSUBST_OPAT_SPAT_CPAT {
+		x=$([[ ${foo#bl\(u\)b} = @(bar|baz) ]] ) 
+	} 
+	function reread_OSUBST_CSUBST_OPAT_SPAT_CPAT { x=$((
+		[[ ${foo#bl\(u\)b} = @(bar|baz) ]]
+	)|tr u x); }
+	function reread_OSUBST_CSUBST_OPAT_SPAT_CPAT {
+		x=$(( [[ ${foo#bl\(u\)b} = @(bar|baz) ]] ) | tr u x ) 
+	} 
+	inline_heredoc_closed() {
+		x=$(cat <<EOFN
+		note there must be no space between EOFN and )
+	EOFN); echo $x
+	}
+	inline_heredoc_closed() {
+		x=$(cat <<EOFN
+		note there must be no space between EOFN and )
+	EOFN
+	) 
+		echo $x 
+	} 
+	function comsub_heredoc_closed { x=$(
+		x=$(cat <<EOFN
+		note there must be no space between EOFN and )
+	EOFN); echo $x
+	); }
+	function comsub_heredoc_closed {
+		x=$(x=$(cat <<EOFN
+		note there must be no space between EOFN and )
+	EOFN
+	) ; echo $x ) 
+	} 
+	function reread_heredoc_closed { x=$((
+		x=$(cat <<EOFN
+		note there must be no space between EOFN and )
+	EOFN); echo $x
+	)|tr u x); }
+	function reread_heredoc_closed {
+		x=$(( x=$(cat <<EOFN
+		note there must be no space between EOFN and )
+	EOFN
+	) ; echo $x ) | tr u x ) 
+	} 
+	inline_heredoc_space() {
+		x=$(cat <<EOFN\ 
+		note the space between EOFN and ) is actually part of the here document marker
+	EOFN ); echo $x
+	}
+	inline_heredoc_space() {
+		x=$(cat <<EOFN\ 
+		note the space between EOFN and ) is actually part of the here document marker
+	EOFN 
+	) 
+		echo $x 
+	} 
+	function comsub_heredoc_space { x=$(
+		x=$(cat <<EOFN\ 
+		note the space between EOFN and ) is actually part of the here document marker
+	EOFN ); echo $x
+	); }
+	function comsub_heredoc_space {
+		x=$(x=$(cat <<EOFN\ 
+		note the space between EOFN and ) is actually part of the here document marker
+	EOFN 
+	) ; echo $x ) 
+	} 
+	function reread_heredoc_space { x=$((
+		x=$(cat <<EOFN\ 
+		note the space between EOFN and ) is actually part of the here document marker
+	EOFN ); echo $x
+	)|tr u x); }
+	function reread_heredoc_space {
+		x=$(( x=$(cat <<EOFN\ 
+		note the space between EOFN and ) is actually part of the here document marker
+	EOFN 
+	) ; echo $x ) | tr u x ) 
+	} 
+	inline_patch_motd() {
+		x=$(sysctl -n kern.version | sed 1q)
+		[[ -s /etc/motd && "$([[ "$(head -1 /etc/motd)" != $x ]] && \
+		    ed -s /etc/motd 2>&1 <<-EOF
+			1,/^\$/d
+			0a
+				$x
+		
+			.
+			wq
+		EOF)" = @(?) ]] && rm -f /etc/motd
+		if [[ ! -s /etc/motd ]]; then
+			install -c -o root -g wheel -m 664 /dev/null /etc/motd
+			print -- "$x\n" >/etc/motd
+		fi
+	}
+	inline_patch_motd() {
+		x=$(sysctl -n kern.version | sed 1q ) 
+		[[ -s /etc/motd && "$([[ "$(head -1 /etc/motd )" != $x ]] && ed -s /etc/motd 2>&1 <<-EOF
+	1,/^\$/d
+	0a
+	$x
+	
+	.
+	wq
+	EOF
+	)" = @(?) ]] && rm -f /etc/motd 
+		if [[ ! -s /etc/motd ]] 
+		then
+			install -c -o root -g wheel -m 664 /dev/null /etc/motd 
+			print -- "$x\n" >/etc/motd 
+		fi 
+	} 
+	function comsub_patch_motd { x=$(
+		x=$(sysctl -n kern.version | sed 1q)
+		[[ -s /etc/motd && "$([[ "$(head -1 /etc/motd)" != $x ]] && \
+		    ed -s /etc/motd 2>&1 <<-EOF
+			1,/^\$/d
+			0a
+				$x
+		
+			.
+			wq
+		EOF)" = @(?) ]] && rm -f /etc/motd
+		if [[ ! -s /etc/motd ]]; then
+			install -c -o root -g wheel -m 664 /dev/null /etc/motd
+			print -- "$x\n" >/etc/motd
+		fi
+	); }
+	function comsub_patch_motd {
+		x=$(x=$(sysctl -n kern.version | sed 1q ) ; [[ -s /etc/motd && "$([[ "$(head -1 /etc/motd )" != $x ]] && ed -s /etc/motd 2>&1 <<-EOF
+	1,/^\$/d
+	0a
+	$x
+	
+	.
+	wq
+	EOF
+	)" = @(?) ]] && rm -f /etc/motd ; if [[ ! -s /etc/motd ]] ; then install -c -o root -g wheel -m 664 /dev/null /etc/motd ; print -- "$x\n" >/etc/motd ; fi ) 
+	} 
+	function reread_patch_motd { x=$((
+		x=$(sysctl -n kern.version | sed 1q)
+		[[ -s /etc/motd && "$([[ "$(head -1 /etc/motd)" != $x ]] && \
+		    ed -s /etc/motd 2>&1 <<-EOF
+			1,/^\$/d
+			0a
+				$x
+		
+			.
+			wq
+		EOF)" = @(?) ]] && rm -f /etc/motd
+		if [[ ! -s /etc/motd ]]; then
+			install -c -o root -g wheel -m 664 /dev/null /etc/motd
+			print -- "$x\n" >/etc/motd
+		fi
+	)|tr u x); }
+	function reread_patch_motd {
+		x=$(( x=$(sysctl -n kern.version | sed 1q ) ; [[ -s /etc/motd && "$([[ "$(head -1 /etc/motd )" != $x ]] && ed -s /etc/motd 2>&1 <<-EOF
+	1,/^\$/d
+	0a
+	$x
+	
+	.
+	wq
+	EOF
+	)" = @(?) ]] && rm -f /etc/motd ; if [[ ! -s /etc/motd ]] ; then install -c -o root -g wheel -m 664 /dev/null /etc/motd ; print -- "$x\n" >/etc/motd ; fi ) | tr u x ) 
+	} 
+	inline_wdarrassign() {
+		case x in
+		x) a+=b; c+=(d e)
+		esac
+	}
+	inline_wdarrassign() {
+		case x in
+		(x)
+			a+=b 
+			set -A c+ -- d e 
+			;;
+		esac 
+	} 
+	function comsub_wdarrassign { x=$(
+		case x in
+		x) a+=b; c+=(d e)
+		esac
+	); }
+	function comsub_wdarrassign {
+		x=$(case x in (x) a+=b ; set -A c+ -- d e  ;; esac ) 
+	} 
+	function reread_wdarrassign { x=$((
+		case x in
+		x) a+=b; c+=(d e)
+		esac
+	)|tr u x); }
+	function reread_wdarrassign {
+		x=$(( case x in (x) a+=b ; set -A c+ -- d e  ;; esac ) | tr u x ) 
+	} 
+---
+name: comsub-torture-io
+description:
+	Check the tree dump functions work correctly with I/O redirection
+stdin:
+	if [[ -z $__progname ]]; then echo >&2 call me with __progname; exit 1; fi
+	while IFS= read -r line; do
+		if [[ $line = '#1' ]]; then
+			lastf=0
+			continue
+		elif [[ $line = EOFN* ]]; then
+			fbody=$fbody$'\n'$line
+			continue
+		elif [[ $line != '#'* ]]; then
+			fbody=$fbody$'\n\t'$line
+			continue
+		fi
+		if (( lastf )); then
+			x="inline_${nextf}() {"$fbody$'\n}\n'
+			print -nr -- "$x"
+			print -r -- "${x}typeset -f inline_$nextf" | "$__progname"
+			x="function comsub_$nextf { x=\$("$fbody$'\n); }\n'
+			print -nr -- "$x"
+			print -r -- "${x}typeset -f comsub_$nextf" | "$__progname"
+			x="function reread_$nextf { x=\$(("$fbody$'\n)|tr u x); }\n'
+			print -nr -- "$x"
+			print -r -- "${x}typeset -f reread_$nextf" | "$__progname"
+		fi
+		lastf=1
+		fbody=
+		nextf=${line#?}
+	done <<'EOD'
+	#1
+	#TCOM
+	vara=1  varb='2  3'  cmd  arg1  $arg2  "$arg3  4" >&3
+	#TPAREN_TPIPE_TLIST
+	(echo $foo  |  tr -dc 0-9 >&3; echo >&3) >&3
+	#TAND_TOR
+	cmd  >&3 &&  >&3 echo ja  ||  echo >&3 nein
+	#TSELECT
+	select  file  in  *;  do  echo  "<$file>" ;  break >&3 ;  done >&3
+	#TFOR_TTIME
+	for  i  in  {1,2,3}  ;  do  time  >&3 echo  $i ;  done >&3
+	#TCASE
+	case  $foo  in  1)  echo eins >&3;& 2) echo zwei >&3  ;| *) echo kann net bis drei zählen >&3;;  esac >&3
+	#TIF_TBANG_TDBRACKET_TELIF
+	if  !  [[  1  =  1  ]]  >&3 ;  then  echo eins;  elif [[ 1 = 2 ]] >&3; then echo zwei  ;else echo drei; fi >&3
+	#TWHILE
+	i=1; while (( i < 10 )) >&3; do echo $i; let ++i; done >&3
+	#TUNTIL
+	i=10; until  (( !--i )) >&3 ; do echo $i; done >&3
+	#TCOPROC
+	cat  *  >&3 |&  >&3 ls
+	#TFUNCT_TBRACE_TASYNC
+	function  korn  {  echo eins; echo >&3 zwei ;  }
+	bourne  ()  {  logger *  >&3 &  }
+	#COMSUB_EXPRSUB
+	echo $(true >&3) $((1+ 2))
+	#0
+	EOD
+expected-stdout:
+	inline_TCOM() {
+		vara=1  varb='2  3'  cmd  arg1  $arg2  "$arg3  4" >&3
+	}
+	inline_TCOM() {
+		vara=1 varb="2  3" cmd arg1 $arg2 "$arg3  4" >&3 
+	} 
+	function comsub_TCOM { x=$(
+		vara=1  varb='2  3'  cmd  arg1  $arg2  "$arg3  4" >&3
+	); }
+	function comsub_TCOM {
+		x=$(vara=1 varb="2  3" cmd arg1 $arg2 "$arg3  4" >&3 ) 
+	} 
+	function reread_TCOM { x=$((
+		vara=1  varb='2  3'  cmd  arg1  $arg2  "$arg3  4" >&3
+	)|tr u x); }
+	function reread_TCOM {
+		x=$(( vara=1 varb="2  3" cmd arg1 $arg2 "$arg3  4" >&3 ) | tr u x ) 
+	} 
+	inline_TPAREN_TPIPE_TLIST() {
+		(echo $foo  |  tr -dc 0-9 >&3; echo >&3) >&3
+	}
+	inline_TPAREN_TPIPE_TLIST() {
+		( echo $foo | tr -dc 0-9 >&3 
+		  echo >&3 ) >&3 
+	} 
+	function comsub_TPAREN_TPIPE_TLIST { x=$(
+		(echo $foo  |  tr -dc 0-9 >&3; echo >&3) >&3
+	); }
+	function comsub_TPAREN_TPIPE_TLIST {
+		x=$(( echo $foo | tr -dc 0-9 >&3 ; echo >&3 ) >&3 ) 
+	} 
+	function reread_TPAREN_TPIPE_TLIST { x=$((
+		(echo $foo  |  tr -dc 0-9 >&3; echo >&3) >&3
+	)|tr u x); }
+	function reread_TPAREN_TPIPE_TLIST {
+		x=$(( ( echo $foo | tr -dc 0-9 >&3 ; echo >&3 ) >&3 ) | tr u x ) 
+	} 
+	inline_TAND_TOR() {
+		cmd  >&3 &&  >&3 echo ja  ||  echo >&3 nein
+	}
+	inline_TAND_TOR() {
+		cmd >&3 && echo ja >&3 || echo nein >&3 
+	} 
+	function comsub_TAND_TOR { x=$(
+		cmd  >&3 &&  >&3 echo ja  ||  echo >&3 nein
+	); }
+	function comsub_TAND_TOR {
+		x=$(cmd >&3 && echo ja >&3 || echo nein >&3 ) 
+	} 
+	function reread_TAND_TOR { x=$((
+		cmd  >&3 &&  >&3 echo ja  ||  echo >&3 nein
+	)|tr u x); }
+	function reread_TAND_TOR {
+		x=$(( cmd >&3 && echo ja >&3 || echo nein >&3 ) | tr u x ) 
+	} 
+	inline_TSELECT() {
+		select  file  in  *;  do  echo  "<$file>" ;  break >&3 ;  done >&3
+	}
+	inline_TSELECT() {
+		select file in * 
+		do
+			echo "<$file>" 
+			break >&3 
+		done >&3 
+	} 
+	function comsub_TSELECT { x=$(
+		select  file  in  *;  do  echo  "<$file>" ;  break >&3 ;  done >&3
+	); }
+	function comsub_TSELECT {
+		x=$(select file in * ; do echo "<$file>" ; break >&3 ; done >&3 ) 
+	} 
+	function reread_TSELECT { x=$((
+		select  file  in  *;  do  echo  "<$file>" ;  break >&3 ;  done >&3
+	)|tr u x); }
+	function reread_TSELECT {
+		x=$(( select file in * ; do echo "<$file>" ; break >&3 ; done >&3 ) | tr u x ) 
+	} 
+	inline_TFOR_TTIME() {
+		for  i  in  {1,2,3}  ;  do  time  >&3 echo  $i ;  done >&3
+	}
+	inline_TFOR_TTIME() {
+		for i in {1,2,3} 
+		do
+			time echo $i >&3 
+		done >&3 
+	} 
+	function comsub_TFOR_TTIME { x=$(
+		for  i  in  {1,2,3}  ;  do  time  >&3 echo  $i ;  done >&3
+	); }
+	function comsub_TFOR_TTIME {
+		x=$(for i in {1,2,3} ; do time echo $i >&3 ; done >&3 ) 
+	} 
+	function reread_TFOR_TTIME { x=$((
+		for  i  in  {1,2,3}  ;  do  time  >&3 echo  $i ;  done >&3
+	)|tr u x); }
+	function reread_TFOR_TTIME {
+		x=$(( for i in {1,2,3} ; do time echo $i >&3 ; done >&3 ) | tr u x ) 
+	} 
+	inline_TCASE() {
+		case  $foo  in  1)  echo eins >&3;& 2) echo zwei >&3  ;| *) echo kann net bis drei zählen >&3;;  esac >&3
+	}
+	inline_TCASE() {
+		case $foo in
+		(1)
+			echo eins >&3 
+			;&
+		(2)
+			echo zwei >&3 
+			;|
+		(*)
+			echo kann net bis drei zählen >&3 
+			;;
+		esac >&3 
+	} 
+	function comsub_TCASE { x=$(
+		case  $foo  in  1)  echo eins >&3;& 2) echo zwei >&3  ;| *) echo kann net bis drei zählen >&3;;  esac >&3
+	); }
+	function comsub_TCASE {
+		x=$(case $foo in (1) echo eins >&3  ;& (2) echo zwei >&3  ;| (*) echo kann net bis drei zählen >&3  ;; esac >&3 ) 
+	} 
+	function reread_TCASE { x=$((
+		case  $foo  in  1)  echo eins >&3;& 2) echo zwei >&3  ;| *) echo kann net bis drei zählen >&3;;  esac >&3
+	)|tr u x); }
+	function reread_TCASE {
+		x=$(( case $foo in (1) echo eins >&3  ;& (2) echo zwei >&3  ;| (*) echo kann net bis drei zählen >&3  ;; esac >&3 ) | tr u x ) 
+	} 
+	inline_TIF_TBANG_TDBRACKET_TELIF() {
+		if  !  [[  1  =  1  ]]  >&3 ;  then  echo eins;  elif [[ 1 = 2 ]] >&3; then echo zwei  ;else echo drei; fi >&3
+	}
+	inline_TIF_TBANG_TDBRACKET_TELIF() {
+		if ! [[ 1 = 1 ]] >&3 
+		then
+			echo eins 
+		elif [[ 1 = 2 ]] >&3 
+		then
+			echo zwei 
+		else
+			echo drei 
+		fi >&3 
+	} 
+	function comsub_TIF_TBANG_TDBRACKET_TELIF { x=$(
+		if  !  [[  1  =  1  ]]  >&3 ;  then  echo eins;  elif [[ 1 = 2 ]] >&3; then echo zwei  ;else echo drei; fi >&3
+	); }
+	function comsub_TIF_TBANG_TDBRACKET_TELIF {
+		x=$(if ! [[ 1 = 1 ]] >&3 ; then echo eins ; elif [[ 1 = 2 ]] >&3 ; then echo zwei ; else echo drei ; fi >&3 ) 
+	} 
+	function reread_TIF_TBANG_TDBRACKET_TELIF { x=$((
+		if  !  [[  1  =  1  ]]  >&3 ;  then  echo eins;  elif [[ 1 = 2 ]] >&3; then echo zwei  ;else echo drei; fi >&3
+	)|tr u x); }
+	function reread_TIF_TBANG_TDBRACKET_TELIF {
+		x=$(( if ! [[ 1 = 1 ]] >&3 ; then echo eins ; elif [[ 1 = 2 ]] >&3 ; then echo zwei ; else echo drei ; fi >&3 ) | tr u x ) 
+	} 
+	inline_TWHILE() {
+		i=1; while (( i < 10 )) >&3; do echo $i; let ++i; done >&3
+	}
+	inline_TWHILE() {
+		i=1 
+		while let] " i < 10 " >&3 
+		do
+			echo $i 
+			let ++i 
+		done >&3 
+	} 
+	function comsub_TWHILE { x=$(
+		i=1; while (( i < 10 )) >&3; do echo $i; let ++i; done >&3
+	); }
+	function comsub_TWHILE {
+		x=$(i=1 ; while let] " i < 10 " >&3 ; do echo $i ; let ++i ; done >&3 ) 
+	} 
+	function reread_TWHILE { x=$((
+		i=1; while (( i < 10 )) >&3; do echo $i; let ++i; done >&3
+	)|tr u x); }
+	function reread_TWHILE {
+		x=$(( i=1 ; while let] " i < 10 " >&3 ; do echo $i ; let ++i ; done >&3 ) | tr u x ) 
+	} 
+	inline_TUNTIL() {
+		i=10; until  (( !--i )) >&3 ; do echo $i; done >&3
+	}
+	inline_TUNTIL() {
+		i=10 
+		until let] " !--i " >&3 
+		do
+			echo $i 
+		done >&3 
+	} 
+	function comsub_TUNTIL { x=$(
+		i=10; until  (( !--i )) >&3 ; do echo $i; done >&3
+	); }
+	function comsub_TUNTIL {
+		x=$(i=10 ; until let] " !--i " >&3 ; do echo $i ; done >&3 ) 
+	} 
+	function reread_TUNTIL { x=$((
+		i=10; until  (( !--i )) >&3 ; do echo $i; done >&3
+	)|tr u x); }
+	function reread_TUNTIL {
+		x=$(( i=10 ; until let] " !--i " >&3 ; do echo $i ; done >&3 ) | tr u x ) 
+	} 
+	inline_TCOPROC() {
+		cat  *  >&3 |&  >&3 ls
+	}
+	inline_TCOPROC() {
+		cat * >&3 |& 
+		ls >&3 
+	} 
+	function comsub_TCOPROC { x=$(
+		cat  *  >&3 |&  >&3 ls
+	); }
+	function comsub_TCOPROC {
+		x=$(cat * >&3 |&  ls >&3 ) 
+	} 
+	function reread_TCOPROC { x=$((
+		cat  *  >&3 |&  >&3 ls
+	)|tr u x); }
+	function reread_TCOPROC {
+		x=$(( cat * >&3 |&  ls >&3 ) | tr u x ) 
+	} 
+	inline_TFUNCT_TBRACE_TASYNC() {
+		function  korn  {  echo eins; echo >&3 zwei ;  }
+		bourne  ()  {  logger *  >&3 &  }
+	}
+	inline_TFUNCT_TBRACE_TASYNC() {
+		function korn {
+			echo eins 
+			echo zwei >&3 
+		} 
+		bourne() {
+			logger * >&3 & 
+		} 
+	} 
+	function comsub_TFUNCT_TBRACE_TASYNC { x=$(
+		function  korn  {  echo eins; echo >&3 zwei ;  }
+		bourne  ()  {  logger *  >&3 &  }
+	); }
+	function comsub_TFUNCT_TBRACE_TASYNC {
+		x=$(function korn { echo eins ; echo zwei >&3 ; } ; bourne() { logger * >&3 &  } ) 
+	} 
+	function reread_TFUNCT_TBRACE_TASYNC { x=$((
+		function  korn  {  echo eins; echo >&3 zwei ;  }
+		bourne  ()  {  logger *  >&3 &  }
+	)|tr u x); }
+	function reread_TFUNCT_TBRACE_TASYNC {
+		x=$(( function korn { echo eins ; echo zwei >&3 ; } ; bourne() { logger * >&3 &  } ) | tr u x ) 
+	} 
+	inline_COMSUB_EXPRSUB() {
+		echo $(true >&3) $((1+ 2))
+	}
+	inline_COMSUB_EXPRSUB() {
+		echo $(true >&3 ) $((1+ 2)) 
+	} 
+	function comsub_COMSUB_EXPRSUB { x=$(
+		echo $(true >&3) $((1+ 2))
+	); }
+	function comsub_COMSUB_EXPRSUB {
+		x=$(echo $(true >&3 ) $((1+ 2)) ) 
+	} 
+	function reread_COMSUB_EXPRSUB { x=$((
+		echo $(true >&3) $((1+ 2))
+	)|tr u x); }
+	function reread_COMSUB_EXPRSUB {
+		x=$(( echo $(true >&3 ) $((1+ 2)) ) | tr u x ) 
+	} 
+---
+name: funsub-1
+description:
+	Check that non-subenvironment command substitution works
+stdin:
+	set -e
+	foo=bar
+	echo "ob $foo ."
+	echo "${
+		echo "ib $foo :"
+		foo=baz
+		echo "ia $foo :"
+		false
+	}" .
+	echo "oa $foo ."
+expected-stdout:
+	ob bar .
+	ib bar :
+	ia baz : .
+	oa baz .
+---
+name: funsub-2
+description:
+	You can now reliably use local and return in funsubs
+	(not exit though)
+stdin:
+	x=q; e=1; x=${ echo a; e=2; echo x$e;}; echo 1:y$x,$e,$?.
+	x=q; e=1; x=${ echo a; typeset e=2; echo x$e;}; echo 2:y$x,$e,$?.
+	x=q; e=1; x=${ echo a; typeset e=2; return 3; echo x$e;}; echo 3:y$x,$e,$?.
+expected-stdout:
+	1:ya x2,2,0.
+	2:ya x2,1,0.
+	3:ya,1,3.
+---
+name: valsub-1
+description:
+	Check that "value substitutions" work as advertised
+stdin:
+	x=1
+	y=2
+	z=3
+	REPLY=4
+	echo "before:	x<$x> y<$y> z<$z> R<$REPLY>"
+	x=${|
+		local y
+		echo "begin:	x<$x> y<$y> z<$z> R<$REPLY>"
+		x=5
+		y=6
+		z=7
+		REPLY=8
+		echo "end:	x<$x> y<$y> z<$z> R<$REPLY>"
+	}
+	echo "after:	x<$x> y<$y> z<$z> R<$REPLY>"
+	# ensure trailing newlines are kept
+	t=${|REPLY=$'foo\n\n';}
+	typeset -p t
+	echo -n this used to segfault
+	echo ${|true;}$(true).
+expected-stdout:
+	before:	x<1> y<2> z<3> R<4>
+	begin:	x<1> y<> z<3> R<>
+	end:	x<5> y<6> z<7> R<8>
+	after:	x<8> y<2> z<7> R<4>
+	typeset t=$'foo\n\n'
+	this used to segfault.
+---
+name: test-stnze-1
+description:
+	Check that the short form [ $x ] works
+stdin:
+	i=0
+	[ -n $x ]
+	rv=$?; echo $((++i)) $rv
+	[ $x ]
+	rv=$?; echo $((++i)) $rv
+	[ -n "$x" ]
+	rv=$?; echo $((++i)) $rv
+	[ "$x" ]
+	rv=$?; echo $((++i)) $rv
+	x=0
+	[ -n $x ]
+	rv=$?; echo $((++i)) $rv
+	[ $x ]
+	rv=$?; echo $((++i)) $rv
+	[ -n "$x" ]
+	rv=$?; echo $((++i)) $rv
+	[ "$x" ]
+	rv=$?; echo $((++i)) $rv
+	x='1 -a 1 = 2'
+	[ -n $x ]
+	rv=$?; echo $((++i)) $rv
+	[ $x ]
+	rv=$?; echo $((++i)) $rv
+	[ -n "$x" ]
+	rv=$?; echo $((++i)) $rv
+	[ "$x" ]
+	rv=$?; echo $((++i)) $rv
+expected-stdout:
+	1 0
+	2 1
+	3 1
+	4 1
+	5 0
+	6 0
+	7 0
+	8 0
+	9 1
+	10 1
+	11 0
+	12 0
+---
+name: test-stnze-2
+description:
+	Check that the short form [[ $x ]] works (ksh93 extension)
+stdin:
+	i=0
+	[[ -n $x ]]
+	rv=$?; echo $((++i)) $rv
+	[[ $x ]]
+	rv=$?; echo $((++i)) $rv
+	[[ -n "$x" ]]
+	rv=$?; echo $((++i)) $rv
+	[[ "$x" ]]
+	rv=$?; echo $((++i)) $rv
+	x=0
+	[[ -n $x ]]
+	rv=$?; echo $((++i)) $rv
+	[[ $x ]]
+	rv=$?; echo $((++i)) $rv
+	[[ -n "$x" ]]
+	rv=$?; echo $((++i)) $rv
+	[[ "$x" ]]
+	rv=$?; echo $((++i)) $rv
+	x='1 -a 1 = 2'
+	[[ -n $x ]]
+	rv=$?; echo $((++i)) $rv
+	[[ $x ]]
+	rv=$?; echo $((++i)) $rv
+	[[ -n "$x" ]]
+	rv=$?; echo $((++i)) $rv
+	[[ "$x" ]]
+	rv=$?; echo $((++i)) $rv
+expected-stdout:
+	1 1
+	2 1
+	3 1
+	4 1
+	5 0
+	6 0
+	7 0
+	8 0
+	9 0
+	10 0
+	11 0
+	12 0
+---
+name: event-subst-3
+description:
+	Check that '!' substitution in noninteractive mode is ignored
+file-setup: file 755 "falsetto"
+	#! /bin/sh
+	echo molto bene
+	exit 42
+file-setup: file 755 "!false"
+	#! /bin/sh
+	echo si
+stdin:
+	export PATH=.:$PATH
+	falsetto
+	echo yeap
+	!false
+	echo meow
+	! false
+	echo = $?
+	if
+	! false; then echo foo; else echo bar; fi
+expected-stdout:
+	molto bene
+	yeap
+	si
+	meow
+	= 0
+	foo
+---
+name: event-subst-0
+description:
+	Check that '!' substitution in interactive mode is ignored
+need-ctty: yes
+arguments: !-i!
+file-setup: file 755 "falsetto"
+	#! /bin/sh
+	echo molto bene
+	exit 42
+file-setup: file 755 "!false"
+	#! /bin/sh
+	echo si
+stdin:
+	export PATH=.:$PATH
+	falsetto
+	echo yeap
+	!false
+	echo meow
+	! false
+	echo = $?
+	if
+	! false; then echo foo; else echo bar; fi
+expected-stdout:
+	molto bene
+	yeap
+	si
+	meow
+	= 0
+	foo
+expected-stderr-pattern:
+	/.*/
+---
+name: nounset-1
+description:
+	Check that "set -u" matches (future) SUSv4 requirement
+stdin:
+	(set -u
+	try() {
+		local v
+		eval v=\$$1
+		if [[ -n $v ]]; then
+			echo $1=nz
+		else
+			echo $1=zf
+		fi
+	}
+	x=y
+	(echo $x)
+	echo =1
+	(echo $y)
+	echo =2
+	(try x)
+	echo =3
+	(try y)
+	echo =4
+	(try 0)
+	echo =5
+	(try 2)
+	echo =6
+	(try)
+	echo =7
+	(echo at=$@)
+	echo =8
+	(echo asterisk=$*)
+	echo =9
+	(echo $?)
+	echo =10
+	(echo $!)
+	echo =11
+	(echo $-)
+	echo =12
+	#(echo $_)
+	#echo =13
+	(echo $#)
+	echo =14
+	(mypid=$$; try mypid)
+	echo =15
+	) 2>&1 | sed -e 's/^[^]]*]//' -e 's/^[^:]*: *//'
+expected-stdout:
+	y
+	=1
+	y: parameter not set
+	=2
+	x=nz
+	=3
+	y: parameter not set
+	=4
+	0=nz
+	=5
+	2: parameter not set
+	=6
+	1: parameter not set
+	=7
+	at=
+	=8
+	asterisk=
+	=9
+	0
+	=10
+	!: parameter not set
+	=11
+	ush
+	=12
+	0
+	=14
+	mypid=nz
+	=15
+---
+name: nameref-1
+description:
+	Testsuite for nameref (bound variables)
+stdin:
+	bar=global
+	typeset -n ir2=bar
+	typeset -n ind=ir2
+	echo !ind: ${!ind}
+	echo ind: $ind
+	echo !ir2: ${!ir2}
+	echo ir2: $ir2
+	typeset +n ind
+	echo !ind: ${!ind}
+	echo ind: $ind
+	typeset -n ir2=ind
+	echo !ir2: ${!ir2}
+	echo ir2: $ir2
+	set|grep ^ir2|sed 's/^/s1: /'
+	typeset|grep ' ir2'|sed -e 's/^/s2: /' -e 's/nameref/typeset -n/'
+	set -A blub -- e1 e2 e3
+	typeset -n ind=blub
+	typeset -n ir2=blub[2]
+	echo !ind[1]: ${!ind[1]}
+	echo !ir2: $!ir2
+	echo ind[1]: ${ind[1]}
+	echo ir2: $ir2
+expected-stdout:
+	!ind: bar
+	ind: global
+	!ir2: bar
+	ir2: global
+	!ind: ind
+	ind: ir2
+	!ir2: ind
+	ir2: ir2
+	s1: ir2=ind
+	s2: typeset -n ir2
+	!ind[1]: blub[1]
+	!ir2: ir2
+	ind[1]: e2
+	ir2: e3
+---
+name: nameref-2da
+description:
+	Testsuite for nameref (bound variables)
+	Functions, argument given directly, after local
+stdin:
+	function foo {
+		typeset bar=lokal baz=auch
+		typeset -n v=bar
+		echo entering
+		echo !v: ${!v}
+		echo !bar: ${!bar}
+		echo !baz: ${!baz}
+		echo bar: $bar
+		echo v: $v
+		v=123
+		echo bar: $bar
+		echo v: $v
+		echo exiting
+	}
+	bar=global
+	echo bar: $bar
+	foo bar
+	echo bar: $bar
+expected-stdout:
+	bar: global
+	entering
+	!v: bar
+	!bar: bar
+	!baz: baz
+	bar: lokal
+	v: lokal
+	bar: 123
+	v: 123
+	exiting
+	bar: global
+---
+name: nameref-3
+description:
+	Advanced testsuite for bound variables (ksh93 fails this)
+stdin:
+	typeset -n foo=bar[i]
+	set -A bar -- b c a
+	for i in 0 1 2 3; do
+		print $i $foo .
+	done
+expected-stdout:
+	0 b .
+	1 c .
+	2 a .
+	3 .
+---
+name: nameref-4
+description:
+	Ensure we don't run in an infinite loop
+time-limit: 3
+stdin:
+	baz() {
+		typeset -n foo=fnord fnord=foo
+		foo[0]=bar
+	}
+	set -A foo bad
+	echo sind $foo .
+	baz
+	echo blah $foo .
+expected-stdout:
+	sind bad .
+	blah bad .
+expected-stderr-pattern:
+	/fnord: expression recurses on parameter/
+---
+name: better-parens-1a
+description:
+	Check support for ((…)) and $((…)) vs (…) and $(…)
+stdin:
+	if ( (echo fubar)|tr u x); then
+		echo ja
+	else
+		echo nein
+	fi
+expected-stdout:
+	fxbar
+	ja
+---
+name: better-parens-1b
+description:
+	Check support for ((…)) and $((…)) vs (…) and $(…)
+stdin:
+	echo $( (echo fubar)|tr u x) $?
+expected-stdout:
+	fxbar 0
+---
+name: better-parens-1c
+description:
+	Check support for ((…)) and $((…)) vs (…) and $(…)
+stdin:
+	x=$( (echo fubar)|tr u x); echo $x $?
+expected-stdout:
+	fxbar 0
+---
+name: better-parens-2a
+description:
+	Check support for ((…)) and $((…)) vs (…) and $(…)
+stdin:
+	if ((echo fubar)|tr u x); then
+		echo ja
+	else
+		echo nein
+	fi
+expected-stdout:
+	fxbar
+	ja
+---
+name: better-parens-2b
+description:
+	Check support for ((…)) and $((…)) vs (…) and $(…)
+stdin:
+	echo $((echo fubar)|tr u x) $?
+expected-stdout:
+	fxbar 0
+---
+name: better-parens-2c
+description:
+	Check support for ((…)) and $((…)) vs (…) and $(…)
+stdin:
+	x=$((echo fubar)|tr u x); echo $x $?
+expected-stdout:
+	fxbar 0
+---
+name: better-parens-3a
+description:
+	Check support for ((…)) and $((…)) vs (…) and $(…)
+stdin:
+	if ( (echo fubar)|(tr u x)); then
+		echo ja
+	else
+		echo nein
+	fi
+expected-stdout:
+	fxbar
+	ja
+---
+name: better-parens-3b
+description:
+	Check support for ((…)) and $((…)) vs (…) and $(…)
+stdin:
+	echo $( (echo fubar)|(tr u x)) $?
+expected-stdout:
+	fxbar 0
+---
+name: better-parens-3c
+description:
+	Check support for ((…)) and $((…)) vs (…) and $(…)
+stdin:
+	x=$( (echo fubar)|(tr u x)); echo $x $?
+expected-stdout:
+	fxbar 0
+---
+name: better-parens-4a
+description:
+	Check support for ((…)) and $((…)) vs (…) and $(…)
+stdin:
+	if ((echo fubar)|(tr u x)); then
+		echo ja
+	else
+		echo nein
+	fi
+expected-stdout:
+	fxbar
+	ja
+---
+name: better-parens-4b
+description:
+	Check support for ((…)) and $((…)) vs (…) and $(…)
+stdin:
+	echo $((echo fubar)|(tr u x)) $?
+expected-stdout:
+	fxbar 0
+---
+name: better-parens-4c
+description:
+	Check support for ((…)) and $((…)) vs (…) and $(…)
+stdin:
+	x=$((echo fubar)|(tr u x)); echo $x $?
+expected-stdout:
+	fxbar 0
+---
+name: echo-test-1
+description:
+	Test what the echo builtin does (mksh)
+stdin:
+	echo -n 'foo\x40bar'
+	echo -e '\tbaz'
+expected-stdout:
+	foo at bar	baz
+---
+name: echo-test-2
+description:
+	Test what the echo builtin does (POSIX)
+	Note: this follows Debian Policy 10.4 which mandates
+	that -n shall be treated as an option, not XSI which
+	mandates it shall be treated as string but escapes
+	shall be expanded.
+stdin:
+	test -n "$POSH_VERSION" || set -o posix
+	echo -n 'foo\x40bar'
+	echo -e '\tbaz'
+expected-stdout:
+	foo\x40bar-e \tbaz
+---
+name: echo-test-3-mnbsd
+description:
+	Test what the echo builtin does, and test a compatibility flag.
+category: mnbsdash
+stdin:
+	"$__progname" -c 'echo -n 1=\\x40$1; echo -e \\x2E' -- foo bar
+	"$__progname" -o posix -c 'echo -n 2=\\x40$1; echo -e \\x2E' -- foo bar
+	"$__progname" -o sh -c 'echo -n 3=\\x40$1; echo -e \\x2E' -- foo bar
+expected-stdout:
+	1=@foo.
+	2=\x40foo-e \x2E
+	3=\x40bar.
+---
+name: echo-test-3-normal
+description:
+	Test what the echo builtin does, and test a compatibility flag.
+category: !mnbsdash
+stdin:
+	"$__progname" -c 'echo -n 1=\\x40$1; echo -e \\x2E' -- foo bar
+	"$__progname" -o posix -c 'echo -n 2=\\x40$1; echo -e \\x2E' -- foo bar
+	"$__progname" -o sh -c 'echo -n 3=\\x40$1; echo -e \\x2E' -- foo bar
+expected-stdout:
+	1=@foo.
+	2=\x40foo-e \x2E
+	3=\x40foo-e \x2E
+---
+name: utilities-getopts-1
+description:
+	getopts sets OPTIND correctly for unparsed option
+stdin:
+	set -- -a -a -x
+	while getopts :a optc; do
+	    echo "OPTARG=$OPTARG, OPTIND=$OPTIND, optc=$optc."
+	done
+	echo done
+expected-stdout:
+	OPTARG=, OPTIND=2, optc=a.
+	OPTARG=, OPTIND=3, optc=a.
+	OPTARG=x, OPTIND=4, optc=?.
+	done
+---
+name: utilities-getopts-2
+description:
+	Check OPTARG
+stdin:
+	set -- -a Mary -x
+	while getopts a: optc; do
+	    echo "OPTARG=$OPTARG, OPTIND=$OPTIND, optc=$optc."
+	done
+	echo done
+expected-stdout:
+	OPTARG=Mary, OPTIND=3, optc=a.
+	OPTARG=, OPTIND=4, optc=?.
+	done
+expected-stderr-pattern: /.*-x.*option/
+---
+name: wcswidth-1
+description:
+	Check the new wcswidth feature
+stdin:
+	s=何
+	set +U
+	print octets: ${#s} .
+	print 8-bit width: ${%s} .
+	set -U
+	print characters: ${#s} .
+	print columns: ${%s} .
+	s=�
+	set +U
+	print octets: ${#s} .
+	print 8-bit width: ${%s} .
+	set -U
+	print characters: ${#s} .
+	print columns: ${%s} .
+expected-stdout:
+	octets: 3 .
+	8-bit width: -1 .
+	characters: 1 .
+	columns: 2 .
+	octets: 3 .
+	8-bit width: 3 .
+	characters: 1 .
+	columns: 1 .
+---
+name: wcswidth-2
+description:
+	Check some corner cases
+stdin:
+	print % $% .
+	set -U
+	x='a	b'
+	print c ${%x} .
+	set +U
+	x='a	b'
+	print d ${%x} .
+expected-stdout:
+	% $% .
+	c -1 .
+	d -1 .
+---
+name: wcswidth-3
+description:
+	Check some corner cases
+stdin:
+	print ${%} .
+expected-stderr-pattern:
+	/bad substitution/
+expected-exit: 1
+---
+name: wcswidth-4a
+description:
+	Check some corner cases
+stdin:
+	print ${%*} .
+expected-stderr-pattern:
+	/bad substitution/
+expected-exit: 1
+---
+name: wcswidth-4b
+description:
+	Check some corner cases
+stdin:
+	print ${%@} .
+expected-stderr-pattern:
+	/bad substitution/
+expected-exit: 1
+---
+name: wcswidth-4c
+description:
+	Check some corner cases
+stdin:
+	:
+	print ${%?} .
+expected-stdout:
+	1 .
+---
+name: realpath-1
+description:
+	Check proper return values for realpath
+category: os:mirbsd
+stdin:
+	wd=$(realpath .)
+	mkdir dir
+	:>file
+	:>dir/file
+	ln -s dir lndir
+	ln -s file lnfile
+	ln -s nix lnnix
+	ln -s . lnself
+	i=0
+	chk() {
+		typeset x y
+		x=$(realpath "$wd/$1" 2>&1); y=$?
+		print $((++i)) "?$1" =${x##*$wd/} !$y
+	}
+	chk dir
+	chk dir/
+	chk dir/file
+	chk dir/nix
+	chk file
+	chk file/
+	chk file/file
+	chk file/nix
+	chk nix
+	chk nix/
+	chk nix/file
+	chk nix/nix
+	chk lndir
+	chk lndir/
+	chk lndir/file
+	chk lndir/nix
+	chk lnfile
+	chk lnfile/
+	chk lnfile/file
+	chk lnfile/nix
+	chk lnnix
+	chk lnnix/
+	chk lnnix/file
+	chk lnnix/nix
+	chk lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself
+	rm lnself
+expected-stdout:
+	1 ?dir =dir !0
+	2 ?dir/ =dir !0
+	3 ?dir/file =dir/file !0
+	4 ?dir/nix =dir/nix !0
+	5 ?file =file !0
+	6 ?file/ =file/: Not a directory !20
+	7 ?file/file =file/file: Not a directory !20
+	8 ?file/nix =file/nix: Not a directory !20
+	9 ?nix =nix !0
+	10 ?nix/ =nix !0
+	11 ?nix/file =nix/file: No such file or directory !2
+	12 ?nix/nix =nix/nix: No such file or directory !2
+	13 ?lndir =dir !0
+	14 ?lndir/ =dir !0
+	15 ?lndir/file =dir/file !0
+	16 ?lndir/nix =dir/nix !0
+	17 ?lnfile =file !0
+	18 ?lnfile/ =lnfile/: Not a directory !20
+	19 ?lnfile/file =lnfile/file: Not a directory !20
+	20 ?lnfile/nix =lnfile/nix: Not a directory !20
+	21 ?lnnix =nix !0
+	22 ?lnnix/ =nix !0
+	23 ?lnnix/file =lnnix/file: No such file or directory !2
+	24 ?lnnix/nix =lnnix/nix: No such file or directory !2
+	25 ?lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself =lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself/lnself: Too many levels of symbolic links !62
+---
+name: realpath-2
+description:
+	Ensure that exactly two leading slashes are not collapsed
+	POSIX guarantees this exception, e.g. for UNC paths on Cygwin
+category: os:mirbsd
+stdin:
+	ln -s /bin t1
+	ln -s //bin t2
+	ln -s ///bin t3
+	realpath /bin
+	realpath //bin
+	realpath ///bin
+	realpath /usr/bin
+	realpath /usr//bin
+	realpath /usr///bin
+	realpath t1
+	realpath t2
+	realpath t3
+	rm -f t1 t2 t3
+	cd //usr/bin
+	pwd
+	cd ../lib
+	pwd
+	realpath //usr/include/../bin
+expected-stdout:
+	/bin
+	//bin
+	/bin
+	/usr/bin
+	/usr/bin
+	/usr/bin
+	/bin
+	//bin
+	/bin
+	//usr/bin
+	//usr/lib
+	//usr/bin
+---
+name: crash-1
+description:
+	Crashed during March 2011, fixed on vernal equinōx ☺
+category: os:mirbsd,os:openbsd
+stdin:
+	export MALLOC_OPTIONS=FGJPRSX
+	"$__progname" -c 'x=$(tr z r <<<baz); echo $x'
+expected-stdout:
+	bar
+---
+name: debian-117-1
+description:
+	Check test - bug#465250
+stdin:
+	test \( ! -e \) ; echo $?
+expected-stdout:
+	1
+---
+name: debian-117-2
+description:
+	Check test - bug#465250
+stdin:
+	test \(  -e \) ; echo $?
+expected-stdout:
+	0
+---
+name: debian-117-3
+description:
+	Check test - bug#465250
+stdin:
+	test ! -e  ; echo $?
+expected-stdout:
+	1
+---
+name: debian-117-4
+description:
+	Check test - bug#465250
+stdin:
+	test  -e  ; echo $?
+expected-stdout:
+	0
+---
+name: case-zsh
+description:
+	Check that zsh case variants work
+stdin:
+	case 'b' in
+	  a) echo a ;;
+	  b) echo b ;;
+	  c) echo c ;;
+	  *) echo x ;;
+	esac
+	echo =
+	case 'b' in
+	  a) echo a ;&
+	  b) echo b ;&
+	  c) echo c ;&
+	  *) echo x ;&
+	esac
+	echo =
+	case 'b' in
+	  a) echo a ;|
+	  b) echo b ;|
+	  c) echo c ;|
+	  *) echo x ;|
+	esac
+expected-stdout:
+	b
+	=
+	b
+	c
+	x
+	=
+	b
+	x
+---
+name: case-braces
+description:
+	Check that case end tokens are not mixed up (Debian #220272)
+stdin:
+	i=0
+	for value in 'x' '}' 'esac'; do
+		print -n "$((++i))($value)bourne "
+		case $value in
+		}) echo brace ;;
+		*) echo no ;;
+		esac
+		print -n "$((++i))($value)korn "
+		case $value {
+		esac) echo esac ;;
+		*) echo no ;;
+		}
+	done
+expected-stdout:
+	1(x)bourne no
+	2(x)korn no
+	3(})bourne brace
+	4(})korn no
+	5(esac)bourne no
+	6(esac)korn esac
+---
+name: command-shift
+description:
+	Check that 'command shift' works
+stdin:
+	function snc {
+		echo "before	0='$0' 1='$1' 2='$2'"
+		shift
+		echo "after	0='$0' 1='$1' 2='$2'"
+	}
+	function swc {
+		echo "before	0='$0' 1='$1' 2='$2'"
+		command shift
+		echo "after	0='$0' 1='$1' 2='$2'"
+	}
+	echo = without command
+	snc 一 二
+	echo = with command
+	swc 一 二
+	echo = done
+expected-stdout:
+	= without command
+	before	0='snc' 1='一' 2='二'
+	after	0='snc' 1='二' 2=''
+	= with command
+	before	0='swc' 1='一' 2='二'
+	after	0='swc' 1='二' 2=''
+	= done
+---
+name: duffs-device
+description:
+	Check that the compiler did not optimise-break them
+	(lex.c has got a similar one in SHEREDELIM)
+stdin:
+	set +U
+	s=
+	typeset -i1 i=0
+	while (( ++i < 256 )); do
+		s+=${i#1#}
+	done
+	s+=$'\xC2\xA0\xE2\x82\xAC\xEF\xBF\xBD\xEF\xBF\xBE\xEF\xBF\xBF\xF0\x90\x80\x80.'
+	typeset -p s
+expected-stdout:
+	typeset s=$'\001\002\003\004\005\006\a\b\t\n\v\f\r\016\017\020\021\022\023\024\025\026\027\030\031\032\E\034\035\036\037 !"#$%&\047()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\177\200\201\202\203\204\205\206\207\210\211\212\213\214\215\216\217\220\221\222\223\224\225\226\227\230\231\232\233\234\235\236\237\240\241\242\243\244\245\246\247\250\251\252\253\254\255\256\257\260\261\262\263\264\265\266\267\270\271\272\273\274\275\276\277\300\301\302\303\304\305\306\307\310\311\312\313\314\315\316\317\320\321\322\323\324\325\326\327\330\331\332\333\334\335\336\337\340\341\342\343\344\345\346\347\350\351\352\353\354\355\356\357\360\361\362\363\364\365\366\367\370\371\372\373\374\375\376\377\u00A0\u20AC\uFFFD\357\277\276\357\277\277\360\220\200\200.'
+---
+name: stateptr-underflow
+description:
+	This check overflows an Xrestpos stored in a short in R40
+category: fastbox
+stdin:
+	function Lb64decode {
+		[[ -o utf8-mode ]]; local u=$?
+		set +U
+		local c s="$*" t=
+		[[ -n $s ]] || { s=$(cat;print x); s=${s%x}; }
+		local -i i=0 n=${#s} p=0 v x
+		local -i16 o
+	
+		while (( i < n )); do
+			c=${s:(i++):1}
+			case $c {
+			(=)	break ;;
+			([A-Z])	(( v = 1#$c - 65 )) ;;
+			([a-z])	(( v = 1#$c - 71 )) ;;
+			([0-9])	(( v = 1#$c + 4 )) ;;
+			(+)	v=62 ;;
+			(/)	v=63 ;;
+			(*)	continue ;;
+			}
+			(( x = (x << 6) | v ))
+			case $((p++)) {
+			(0)	continue ;;
+			(1)	(( o = (x >> 4) & 255 )) ;;
+			(2)	(( o = (x >> 2) & 255 )) ;;
+			(3)	(( o = x & 255 ))
+				p=0
+				;;
+			}
+			t=$t\\x${o#16#}
+		done
+		print -n $t
+		(( u )) || set -U
+	}
+	
+	i=-1
+	s=
+	while (( ++i < 12120 )); do
+		s+=a
+	done
+	Lb64decode $s >/dev/null
+---
+name: xtrace-1
+description:
+	Check that "set -x" doesn't redirect too quickly
+stdin:
+	print '#!'"$__progname" >bash
+	cat >>bash <<'EOF'
+	echo 'GNU bash, version 2.05b.0(1)-release (i386-ecce-mirbsd10)
+	Copyright (C) 2002 Free Software Foundation, Inc.'
+	EOF
+	chmod +x bash
+	"$__progname" -xc 'foo=$(./bash --version 2>&1 | head -1); echo "=$foo="'
+expected-stdout:
+	=GNU bash, version 2.05b.0(1)-release (i386-ecce-mirbsd10)=
+expected-stderr-pattern:
+	/.*/
+---

Deleted: vendor/MirOS/mksh/R50/dot.mkshrc
===================================================================
--- vendor/MirOS/mksh/dist/dot.mkshrc	2014-06-30 23:58:12 UTC (rev 6706)
+++ vendor/MirOS/mksh/R50/dot.mkshrc	2014-07-01 12:01:17 UTC (rev 6708)
@@ -1,416 +0,0 @@
-# $Id: dot.mkshrc,v 1.1.1.19 2013-08-14 22:11:50 laffer1 Exp $
-# $MirOS: src/bin/mksh/dot.mkshrc,v 1.84 2013/08/10 13:43:50 tg Exp $
-#-
-# Copyright (c) 2002, 2003, 2004, 2006, 2007, 2008, 2009, 2010,
-#		2011, 2012, 2013
-#	Thorsten Glaser <tg at mirbsd.org>
-#
-# Provided that these terms and disclaimer and all copyright notices
-# are retained or reproduced in an accompanying document, permission
-# is granted to deal in this work without restriction, including un-
-# limited rights to use, publicly perform, distribute, sell, modify,
-# merge, give away, or sublicence.
-#
-# This work is provided "AS IS" and WITHOUT WARRANTY of any kind, to
-# the utmost extent permitted by applicable law, neither express nor
-# implied; without malicious intent or gross negligence. In no event
-# may a licensor, author or contributor be held liable for indirect,
-# direct, other damage, loss, or other issues arising in any way out
-# of dealing in the work, even if advised of the possibility of such
-# damage or existence of a defect, except proven that it results out
-# of said person's immediate fault when using the work as intended.
-#-
-# ${ENV:-~/.mkshrc}: mksh initialisation file for interactive shells
-
-# catch non-mksh (including lksh) trying to shell this file
-case $KSH_VERSION in
-*MIRBSD\ KSH*) ;;
-*) return 0 ;;
-esac
-
-PS1='#'; (( USER_ID )) && PS1='$'; [[ ${HOSTNAME:=$(ulimit -c 0; hostname -s \
-    2>/dev/null)} = *([	 ]|localhost) ]] && HOSTNAME=$(ulimit -c 0; hostname \
-    2>/dev/null); : ${EDITOR:=/bin/ed} ${HOSTNAME:=nil} ${TERM:=vt100}
-: ${MKSH:=$(whence -p mksh)}; PS4='[$EPOCHREALTIME] '; PS1=$'\001\r''${|
-	local e=$?
-
-	(( e )) && REPLY+="$e|"
-	REPLY+=${USER:=$(ulimit -c 0; id -un 2>/dev/null || echo \?)}
-	REPLY+=@${HOSTNAME%%.*}:
-
-	local d=${PWD:-?} p=~; [[ $p = ?(*/) ]] || d=${d/#$p/~}
-	local m=${%d} n p=...; (( m > 0 )) || m=${#d}
-	(( m > (n = (COLUMNS/3 < 7 ? 7 : COLUMNS/3)) )) && d=${d:(-n)} || p=
-	REPLY+=$p$d
-
-	return $e
-} '"$PS1 "; export EDITOR HOSTNAME MKSH TERM USER
-alias ls=ls
-unalias ls
-alias l='ls -F'
-alias la='l -a'
-alias ll='l -l'
-alias lo='l -alo'
-alias doch='sudo mksh -c "$(fc -ln -1)"'
-whence -p rot13 >/dev/null || alias rot13='tr \
-    abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ \
-    nopqrstuvwxyzabcdefghijklmNOPQRSTUVWXYZABCDEFGHIJKLM'
-if whence -p hd >/dev/null; then :; elif whence -p hexdump >/dev/null; then
-	function hd {
-		hexdump -e '"%08.8_ax  " 8/1 "%02X " " - " 8/1 "%02X "' \
-		    -e '"  |" "%_p"' -e '"|\n"' "$@"
-	}
-else
-	function hd {
-		local -Uui16 -Z11 pos=0
-		local -Uui16 -Z5 hv=2147483647
-		local dasc line i
-
-		cat "$@" | { set +U; if read -arN -1 line; then
-			typeset -i1 line
-			i=0
-			while (( i < ${#line[*]} )); do
-				hv=${line[i++]}
-				if (( (pos & 15) == 0 )); then
-					(( pos )) && print -r -- "$dasc|"
-					print -n "${pos#16#}  "
-					dasc=' |'
-				fi
-				print -n "${hv#16#} "
-				if (( (hv < 32) || (hv > 126) )); then
-					dasc+=.
-				else
-					dasc+=${line[i-1]#1#}
-				fi
-				(( (pos++ & 15) == 7 )) && print -n -- '- '
-			done
-			while (( pos & 15 )); do
-				print -n '   '
-				(( (pos++ & 15) == 7 )) && print -n -- '- '
-			done
-			(( hv == 2147483647 )) || print -r -- "$dasc|"
-		fi; }
-	}
-fi
-
-# Berkeley C shell compatible dirs, popd, and pushd functions
-# Z shell compatible chpwd() hook, used to update DIRSTACK[0]
-DIRSTACKBASE=$(realpath ~/. 2>/dev/null || print -nr -- "${HOME:-/}")
-set -A DIRSTACK
-function chpwd {
-	DIRSTACK[0]=$(realpath . 2>/dev/null || print -r -- "$PWD")
-	[[ $DIRSTACKBASE = ?(*/) ]] || \
-	    DIRSTACK[0]=${DIRSTACK[0]/#$DIRSTACKBASE/~}
-	:
-}
-chpwd .
-function cd {
-	builtin cd "$@"
-	chpwd "$@"
-}
-function cd_csh {
-	local d t=${1/#~/$DIRSTACKBASE}
-
-	if ! d=$(builtin cd "$t" 2>&1); then
-		print -u2 "${1}: ${d##*$t - }."
-		return 1
-	fi
-	cd "$t"
-}
-function dirs {
-	local d dwidth
-	local -i fl=0 fv=0 fn=0 cpos=0
-
-	while getopts ":lvn" d; do
-		case $d {
-		(l)	fl=1 ;;
-		(v)	fv=1 ;;
-		(n)	fn=1 ;;
-		(*)	print -u2 'Usage: dirs [-lvn].'
-			return 1 ;;
-		}
-	done
-	shift $((OPTIND - 1))
-	if (( $# > 0 )); then
-		print -u2 'Usage: dirs [-lvn].'
-		return 1
-	fi
-	if (( fv )); then
-		fv=0
-		while (( fv < ${#DIRSTACK[*]} )); do
-			d=${DIRSTACK[fv]}
-			(( fl )) && d=${d/#~/$DIRSTACKBASE}
-			print -r -- "$fv	$d"
-			let fv++
-		done
-	else
-		fv=0
-		while (( fv < ${#DIRSTACK[*]} )); do
-			d=${DIRSTACK[fv]}
-			(( fl )) && d=${d/#~/$DIRSTACKBASE}
-			(( dwidth = (${%d} > 0 ? ${%d} : ${#d}) ))
-			if (( fn && (cpos += dwidth + 1) >= 79 && \
-			    dwidth < 80 )); then
-				print
-				(( cpos = dwidth + 1 ))
-			fi
-			print -nr -- "$d "
-			let fv++
-		done
-		print
-	fi
-	return 0
-}
-function popd {
-	local d fa
-	local -i n=1
-
-	while getopts ":0123456789lvn" d; do
-		case $d {
-		(l|v|n)	fa+=" -$d" ;;
-		(+*)	n=2
-			break ;;
-		(*)	print -u2 'Usage: popd [-lvn] [+<n>].'
-			return 1 ;;
-		}
-	done
-	shift $((OPTIND - n))
-	n=0
-	if (( $# > 1 )); then
-		print -u2 popd: Too many arguments.
-		return 1
-	elif [[ $1 = ++([0-9]) && $1 != +0 ]]; then
-		if (( (n = ${1#+}) >= ${#DIRSTACK[*]} )); then
-			print -u2 popd: Directory stack not that deep.
-			return 1
-		fi
-	elif [[ -n $1 ]]; then
-		print -u2 popd: Bad directory.
-		return 1
-	fi
-	if (( ${#DIRSTACK[*]} < 2 )); then
-		print -u2 popd: Directory stack empty.
-		return 1
-	fi
-	unset DIRSTACK[n]
-	set -A DIRSTACK -- "${DIRSTACK[@]}"
-	cd_csh "${DIRSTACK[0]}" || return 1
-	dirs $fa
-}
-function pushd {
-	local d fa
-	local -i n=1
-
-	while getopts ":0123456789lvn" d; do
-		case $d {
-		(l|v|n)	fa+=" -$d" ;;
-		(+*)	n=2
-			break ;;
-		(*)	print -u2 'Usage: pushd [-lvn] [<dir>|+<n>].'
-			return 1 ;;
-		}
-	done
-	shift $((OPTIND - n))
-	if (( $# == 0 )); then
-		if (( ${#DIRSTACK[*]} < 2 )); then
-			print -u2 pushd: No other directory.
-			return 1
-		fi
-		d=${DIRSTACK[1]}
-		DIRSTACK[1]=${DIRSTACK[0]}
-		cd_csh "$d" || return 1
-	elif (( $# > 1 )); then
-		print -u2 pushd: Too many arguments.
-		return 1
-	elif [[ $1 = ++([0-9]) && $1 != +0 ]]; then
-		if (( (n = ${1#+}) >= ${#DIRSTACK[*]} )); then
-			print -u2 pushd: Directory stack not that deep.
-			return 1
-		fi
-		while (( n-- )); do
-			d=${DIRSTACK[0]}
-			unset DIRSTACK[0]
-			set -A DIRSTACK -- "${DIRSTACK[@]}" "$d"
-		done
-		cd_csh "${DIRSTACK[0]}" || return 1
-	else
-		set -A DIRSTACK -- placeholder "${DIRSTACK[@]}"
-		cd_csh "$1" || return 1
-	fi
-	dirs $fa
-}
-
-# pager (not control character safe)
-function smores {
-	local dummy line llen curlin=0
-
-	cat "$@" | while IFS= read -r line; do
-		llen=${%line}
-		(( llen == -1 )) && llen=${#line}
-		(( llen = llen ? (llen + COLUMNS - 1) / COLUMNS : 1 ))
-		if (( (curlin += llen) >= LINES )); then
-			print -n -- '\033[7m--more--\033[0m'
-			read -u1 dummy
-			[[ $dummy = [Qq]* ]] && return 0
-			curlin=$llen
-		fi
-		print -r -- "$line"
-	done
-}
-
-# base64 encoder and decoder, RFC compliant, NUL safe
-function Lb64decode {
-	[[ -o utf8-mode ]]; local u=$?
-	set +U
-	local c s="$*" t=
-	[[ -n $s ]] || { s=$(cat; print x); s=${s%x}; }
-	local -i i=0 j=0 n=${#s} p=0 v x
-	local -i16 o
-
-	while (( i < n )); do
-		c=${s:(i++):1}
-		case $c {
-		(=)	break ;;
-		([A-Z])	(( v = 1#$c - 65 )) ;;
-		([a-z])	(( v = 1#$c - 71 )) ;;
-		([0-9])	(( v = 1#$c + 4 )) ;;
-		(+)	v=62 ;;
-		(/)	v=63 ;;
-		(*)	continue ;;
-		}
-		(( x = (x << 6) | v ))
-		case $((p++)) {
-		(0)	continue ;;
-		(1)	(( o = (x >> 4) & 255 )) ;;
-		(2)	(( o = (x >> 2) & 255 )) ;;
-		(3)	(( o = x & 255 ))
-			p=0
-			;;
-		}
-		t+=\\x${o#16#}
-		(( ++j & 4095 )) && continue
-		print -n $t
-		t=
-	done
-	print -n $t
-	(( u )) || set -U
-}
-
-set -A Lb64encode_code -- A B C D E F G H I J K L M N O P Q R S T U V W X Y Z \
-    a b c d e f g h i j k l m n o p q r s t u v w x y z 0 1 2 3 4 5 6 7 8 9 + /
-function Lb64encode {
-	[[ -o utf8-mode ]]; local u=$?
-	set +U
-	local c s t
-	if (( $# )); then
-		read -raN-1 s <<<"$*"
-		unset s[${#s[*]}-1]
-	else
-		read -raN-1 s
-	fi
-	local -i i=0 n=${#s[*]} j v
-
-	while (( i < n )); do
-		(( v = s[i++] << 16 ))
-		(( j = i < n ? s[i++] : 0 ))
-		(( v |= j << 8 ))
-		(( j = i < n ? s[i++] : 0 ))
-		(( v |= j ))
-		t+=${Lb64encode_code[v >> 18]}${Lb64encode_code[v >> 12 & 63]}
-		c=${Lb64encode_code[v >> 6 & 63]}
-		if (( i <= n )); then
-			t+=$c${Lb64encode_code[v & 63]}
-		elif (( i == n + 1 )); then
-			t+=$c=
-		else
-			t+===
-		fi
-		if (( ${#t} == 76 || i >= n )); then
-			print $t
-			t=
-		fi
-	done
-	(( u )) || set -U
-}
-
-# mksh NUL counting, never zero
-typeset -Z11 -Uui16 Lnzathash_v
-function Lnzathash_add {
-	[[ -o utf8-mode ]]; local u=$?
-	set +U
-	local s
-	if (( $# )); then
-		read -raN-1 s <<<"$*"
-		unset s[${#s[*]}-1]
-	else
-		read -raN-1 s
-	fi
-	local -i i=0 n=${#s[*]}
-
-	while (( i < n )); do
-		((# Lnzathash_v = (Lnzathash_v + s[i++] + 1) * 1025 ))
-		((# Lnzathash_v ^= Lnzathash_v >> 6 ))
-	done
-
-	(( u )) || set -U
-}
-function Lnzaathash_end {
-	((# Lnzathash_v *= 1025 ))
-	((# Lnzathash_v ^= Lnzathash_v >> 6 ))
-	((# Lnzathash_v += Lnzathash_v << 3 ))
-	((# Lnzathash_v = (Lnzathash_v ^
-	    (Lnzathash_v >> 11)) * 32769 ))
-	print ${Lnzathash_v#16#}
-}
-function Lnzaathash {
-	Lnzathash_v=0
-	Lnzathash_add "$@"
-	Lnzaathash_end
-}
-function Lnzathash {
-	Lnzathash_v=0
-	Lnzathash_add "$@"
-	Lnzathash_end
-}
-function Lnzathash_end {
-	if (( Lnzathash_v )); then
-		Lnzaathash_end
-	else
-		Lnzathash_v=1
-		print ${Lnzathash_v#16#}
-	fi
-}
-
-# strip comments (and leading/trailing whitespace if IFS is set) from
-# any file(s) given as argument, or stdin if none, and spew to stdout
-function Lstripcom {
-	cat "$@" | { set -o noglob; while read _line; do
-		_line=${_line%%#*}
-		[[ -n $_line ]] && print -r -- $_line
-	done; }
-}
-
-# give MidnightBSD's laffer1 a bit of csh feeling
-function setenv {
-	eval export "\"$1\""'="$2"'
-}
-
-: place customisations below this line
-
-for p in ~/.etc/bin ~/bin; do
-	[[ -d $p/. ]] || continue
-	[[ :$PATH: = *:$p:* ]] || PATH=$p:$PATH
-done
-
-export SHELL=$MKSH MANWIDTH=80 LESSHISTFILE=-
-alias cls='print -n \\033c'
-
-#unset LANGUAGE LC_ADDRESS LC_ALL LC_COLLATE LC_IDENTIFICATION LC_MONETARY \
-#    LC_NAME LC_NUMERIC LC_TELEPHONE LC_TIME
-#p=en_GB.UTF-8
-#set -U
-#export LANG=C LC_CTYPE=$p LC_MEASUREMENT=$p LC_MESSAGES=$p LC_PAPER=$p
-
-unset p
-
-: place customisations above this line

Copied: vendor/MirOS/mksh/R50/dot.mkshrc (from rev 6707, vendor/MirOS/mksh/dist/dot.mkshrc)
===================================================================
--- vendor/MirOS/mksh/R50/dot.mkshrc	                        (rev 0)
+++ vendor/MirOS/mksh/R50/dot.mkshrc	2014-07-01 12:01:17 UTC (rev 6708)
@@ -0,0 +1,402 @@
+# $Id$
+# $MirOS: src/bin/mksh/dot.mkshrc,v 1.88 2014/01/11 18:09:39 tg Exp $
+#-
+# Copyright (c) 2002, 2003, 2004, 2006, 2007, 2008, 2009, 2010,
+#		2011, 2012, 2013, 2014
+#	Thorsten Glaser <tg at mirbsd.org>
+#
+# Provided that these terms and disclaimer and all copyright notices
+# are retained or reproduced in an accompanying document, permission
+# is granted to deal in this work without restriction, including un-
+# limited rights to use, publicly perform, distribute, sell, modify,
+# merge, give away, or sublicence.
+#
+# This work is provided "AS IS" and WITHOUT WARRANTY of any kind, to
+# the utmost extent permitted by applicable law, neither express nor
+# implied; without malicious intent or gross negligence. In no event
+# may a licensor, author or contributor be held liable for indirect,
+# direct, other damage, loss, or other issues arising in any way out
+# of dealing in the work, even if advised of the possibility of such
+# damage or existence of a defect, except proven that it results out
+# of said person's immediate fault when using the work as intended.
+#-
+# ${ENV:-~/.mkshrc}: mksh initialisation file for interactive shells
+
+# catch non-mksh (including lksh) trying to shell this file
+case $KSH_VERSION in
+*MIRBSD\ KSH*) ;;
+*) return 0 ;;
+esac
+
+PS1='#'; (( USER_ID )) && PS1='$'; [[ ${HOSTNAME:=$(ulimit -c 0; hostname -s \
+    2>/dev/null)} = *([	 ]|localhost) ]] && HOSTNAME=$(ulimit -c 0; hostname \
+    2>/dev/null); : ${EDITOR:=/bin/ed} ${HOSTNAME:=nil} ${TERM:=vt100}
+: ${MKSH:=$(whence -p mksh)}; PS4='[$EPOCHREALTIME] '; PS1=$'\001\r''${|
+	local e=$?
+
+	(( e )) && REPLY+="$e|"
+	REPLY+=${USER:=$(ulimit -c 0; id -un 2>/dev/null || echo \?)}
+	REPLY+=@${HOSTNAME%%.*}:
+
+	local d=${PWD:-?} p=~; [[ $p = ?(*/) ]] || d=${d/#$p/~}
+	local m=${%d} n p=...; (( m > 0 )) || m=${#d}
+	(( m > (n = (COLUMNS/3 < 7 ? 7 : COLUMNS/3)) )) && d=${d:(-n)} || p=
+	REPLY+=$p$d
+
+	return $e
+} '"$PS1 "; export EDITOR HOSTNAME MKSH TERM USER
+alias ls=ls
+unalias ls
+alias l='ls -F'
+alias la='l -a'
+alias ll='l -l'
+alias lo='l -alo'
+alias doch='sudo mksh -c "$(fc -ln -1)"'
+whence -p rot13 >/dev/null || alias rot13='tr \
+    abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ \
+    nopqrstuvwxyzabcdefghijklmNOPQRSTUVWXYZABCDEFGHIJKLM'
+if whence -p hd >/dev/null; then :; elif whence -p hexdump >/dev/null; then
+	function hd {
+		hexdump -e '"%08.8_ax  " 8/1 "%02X " " - " 8/1 "%02X "' \
+		    -e '"  |" "%_p"' -e '"|\n"' "$@"
+	}
+else
+	function hd {
+		local -Uui16 -Z11 pos=0
+		local -Uui16 -Z5 hv=2147483647
+		local dasc line i
+
+		cat "$@" | { set +U; if read -arN -1 line; then
+			typeset -i1 line
+			i=0
+			while (( i < ${#line[*]} )); do
+				hv=${line[i++]}
+				if (( (pos & 15) == 0 )); then
+					(( pos )) && print -r -- "$dasc|"
+					print -n "${pos#16#}  "
+					dasc=' |'
+				fi
+				print -n "${hv#16#} "
+				if (( (hv < 32) || (hv > 126) )); then
+					dasc+=.
+				else
+					dasc+=${line[i-1]#1#}
+				fi
+				(( (pos++ & 15) == 7 )) && print -n -- '- '
+			done
+			while (( pos & 15 )); do
+				print -n '   '
+				(( (pos++ & 15) == 7 )) && print -n -- '- '
+			done
+			(( hv == 2147483647 )) || print -r -- "$dasc|"
+		fi; }
+	}
+fi
+
+# Berkeley C shell compatible dirs, popd, and pushd functions
+# Z shell compatible chpwd() hook, used to update DIRSTACK[0]
+DIRSTACKBASE=$(realpath ~/. 2>/dev/null || print -nr -- "${HOME:-/}")
+set -A DIRSTACK
+function chpwd {
+	DIRSTACK[0]=$(realpath . 2>/dev/null || print -r -- "$PWD")
+	[[ $DIRSTACKBASE = ?(*/) ]] || \
+	    DIRSTACK[0]=${DIRSTACK[0]/#$DIRSTACKBASE/~}
+	:
+}
+chpwd .
+function cd {
+	builtin cd "$@" || return $?
+	chpwd "$@"
+}
+function cd_csh {
+	local d t=${1/#~/$DIRSTACKBASE}
+
+	if ! d=$(builtin cd "$t" 2>&1); then
+		print -u2 "${1}: ${d##*cd: $t: }."
+		return 1
+	fi
+	cd "$t"
+}
+function dirs {
+	local d dwidth
+	local -i fl=0 fv=0 fn=0 cpos=0
+
+	while getopts ":lvn" d; do
+		case $d {
+		(l)	fl=1 ;;
+		(v)	fv=1 ;;
+		(n)	fn=1 ;;
+		(*)	print -u2 'Usage: dirs [-lvn].'
+			return 1 ;;
+		}
+	done
+	shift $((OPTIND - 1))
+	if (( $# > 0 )); then
+		print -u2 'Usage: dirs [-lvn].'
+		return 1
+	fi
+	if (( fv )); then
+		fv=0
+		while (( fv < ${#DIRSTACK[*]} )); do
+			d=${DIRSTACK[fv]}
+			(( fl )) && d=${d/#~/$DIRSTACKBASE}
+			print -r -- "$fv	$d"
+			let fv++
+		done
+	else
+		fv=0
+		while (( fv < ${#DIRSTACK[*]} )); do
+			d=${DIRSTACK[fv]}
+			(( fl )) && d=${d/#~/$DIRSTACKBASE}
+			(( dwidth = (${%d} > 0 ? ${%d} : ${#d}) ))
+			if (( fn && (cpos += dwidth + 1) >= 79 && \
+			    dwidth < 80 )); then
+				print
+				(( cpos = dwidth + 1 ))
+			fi
+			print -nr -- "$d "
+			let fv++
+		done
+		print
+	fi
+	return 0
+}
+function popd {
+	local d fa
+	local -i n=1
+
+	while getopts ":0123456789lvn" d; do
+		case $d {
+		(l|v|n)	fa+=" -$d" ;;
+		(+*)	n=2
+			break ;;
+		(*)	print -u2 'Usage: popd [-lvn] [+<n>].'
+			return 1 ;;
+		}
+	done
+	shift $((OPTIND - n))
+	n=0
+	if (( $# > 1 )); then
+		print -u2 popd: Too many arguments.
+		return 1
+	elif [[ $1 = ++([0-9]) && $1 != +0 ]]; then
+		if (( (n = ${1#+}) >= ${#DIRSTACK[*]} )); then
+			print -u2 popd: Directory stack not that deep.
+			return 1
+		fi
+	elif [[ -n $1 ]]; then
+		print -u2 popd: Bad directory.
+		return 1
+	fi
+	if (( ${#DIRSTACK[*]} < 2 )); then
+		print -u2 popd: Directory stack empty.
+		return 1
+	fi
+	unset DIRSTACK[n]
+	set -A DIRSTACK -- "${DIRSTACK[@]}"
+	cd_csh "${DIRSTACK[0]}" || return 1
+	dirs $fa
+}
+function pushd {
+	local d fa
+	local -i n=1
+
+	while getopts ":0123456789lvn" d; do
+		case $d {
+		(l|v|n)	fa+=" -$d" ;;
+		(+*)	n=2
+			break ;;
+		(*)	print -u2 'Usage: pushd [-lvn] [<dir>|+<n>].'
+			return 1 ;;
+		}
+	done
+	shift $((OPTIND - n))
+	if (( $# == 0 )); then
+		if (( ${#DIRSTACK[*]} < 2 )); then
+			print -u2 pushd: No other directory.
+			return 1
+		fi
+		d=${DIRSTACK[1]}
+		DIRSTACK[1]=${DIRSTACK[0]}
+		cd_csh "$d" || return 1
+	elif (( $# > 1 )); then
+		print -u2 pushd: Too many arguments.
+		return 1
+	elif [[ $1 = ++([0-9]) && $1 != +0 ]]; then
+		if (( (n = ${1#+}) >= ${#DIRSTACK[*]} )); then
+			print -u2 pushd: Directory stack not that deep.
+			return 1
+		fi
+		while (( n-- )); do
+			d=${DIRSTACK[0]}
+			unset DIRSTACK[0]
+			set -A DIRSTACK -- "${DIRSTACK[@]}" "$d"
+		done
+		cd_csh "${DIRSTACK[0]}" || return 1
+	else
+		set -A DIRSTACK -- placeholder "${DIRSTACK[@]}"
+		cd_csh "$1" || return 1
+	fi
+	dirs $fa
+}
+
+# pager (not control character safe)
+function smores {
+	local dummy line llen curlin=0
+
+	cat "$@" | while IFS= read -r line; do
+		llen=${%line}
+		(( llen == -1 )) && llen=${#line}
+		(( llen = llen ? (llen + COLUMNS - 1) / COLUMNS : 1 ))
+		if (( (curlin += llen) >= LINES )); then
+			print -n -- '\033[7m--more--\033[0m'
+			read -u1 dummy
+			[[ $dummy = [Qq]* ]] && return 0
+			curlin=$llen
+		fi
+		print -r -- "$line"
+	done
+}
+
+# base64 encoder and decoder, RFC compliant, NUL safe
+function Lb64decode {
+	[[ -o utf8-mode ]]; local u=$?
+	set +U
+	local c s="$*" t=
+	[[ -n $s ]] || { s=$(cat; print x); s=${s%x}; }
+	local -i i=0 j=0 n=${#s} p=0 v x
+	local -i16 o
+
+	while (( i < n )); do
+		c=${s:(i++):1}
+		case $c {
+		(=)	break ;;
+		([A-Z])	(( v = 1#$c - 65 )) ;;
+		([a-z])	(( v = 1#$c - 71 )) ;;
+		([0-9])	(( v = 1#$c + 4 )) ;;
+		(+)	v=62 ;;
+		(/)	v=63 ;;
+		(*)	continue ;;
+		}
+		(( x = (x << 6) | v ))
+		case $((p++)) {
+		(0)	continue ;;
+		(1)	(( o = (x >> 4) & 255 )) ;;
+		(2)	(( o = (x >> 2) & 255 )) ;;
+		(3)	(( o = x & 255 ))
+			p=0
+			;;
+		}
+		t+=\\x${o#16#}
+		(( ++j & 4095 )) && continue
+		print -n $t
+		t=
+	done
+	print -n $t
+	(( u )) || set -U
+}
+
+set -A Lb64encode_code -- A B C D E F G H I J K L M N O P Q R S T U V W X Y Z \
+    a b c d e f g h i j k l m n o p q r s t u v w x y z 0 1 2 3 4 5 6 7 8 9 + /
+function Lb64encode {
+	[[ -o utf8-mode ]]; local u=$?
+	set +U
+	local c s t
+	if (( $# )); then
+		read -raN-1 s <<<"$*"
+		unset s[${#s[*]}-1]
+	else
+		read -raN-1 s
+	fi
+	local -i i=0 n=${#s[*]} j v
+
+	while (( i < n )); do
+		(( v = s[i++] << 16 ))
+		(( j = i < n ? s[i++] : 0 ))
+		(( v |= j << 8 ))
+		(( j = i < n ? s[i++] : 0 ))
+		(( v |= j ))
+		t+=${Lb64encode_code[v >> 18]}${Lb64encode_code[v >> 12 & 63]}
+		c=${Lb64encode_code[v >> 6 & 63]}
+		if (( i <= n )); then
+			t+=$c${Lb64encode_code[v & 63]}
+		elif (( i == n + 1 )); then
+			t+=$c=
+		else
+			t+===
+		fi
+		if (( ${#t} == 76 || i >= n )); then
+			print $t
+			t=
+		fi
+	done
+	(( u )) || set -U
+}
+
+# Better Avalanche for the Jenkins Hash
+typeset -Z11 -Uui16 Lbafh_v
+function Lbafh_init {
+	Lbafh_v=0
+}
+function Lbafh_add {
+	[[ -o utf8-mode ]]; local u=$?
+	set +U
+	local s
+	if (( $# )); then
+		read -raN-1 s <<<"$*"
+		unset s[${#s[*]}-1]
+	else
+		read -raN-1 s
+	fi
+	local -i i=0 n=${#s[*]}
+
+	while (( i < n )); do
+		((# Lbafh_v = (Lbafh_v + s[i++] + 1) * 1025 ))
+		((# Lbafh_v ^= Lbafh_v >> 6 ))
+	done
+
+	(( u )) || set -U
+}
+function Lbafh_finish {
+	local -Ui t
+
+	((# t = (((Lbafh_v >> 7) & 0x01010101) * 0x1B) ^ \
+	    ((Lbafh_v << 1) & 0xFEFEFEFE) ))
+	((# Lbafh_v = t ^ (t >>> 8) ^ (Lbafh_v >>> 8) ^ \
+	    (Lbafh_v >>> 16) ^ (Lbafh_v >>> 24) ))
+	:
+}
+
+# strip comments (and leading/trailing whitespace if IFS is set) from
+# any file(s) given as argument, or stdin if none, and spew to stdout
+function Lstripcom {
+	cat "$@" | { set -o noglob; while read _line; do
+		_line=${_line%%#*}
+		[[ -n $_line ]] && print -r -- $_line
+	done; }
+}
+
+# give MidnightBSD's laffer1 a bit of csh feeling
+function setenv {
+	eval export "\"$1\""'="$2"'
+}
+
+: place customisations below this line
+
+for p in ~/.etc/bin ~/bin; do
+	[[ -d $p/. ]] || continue
+	[[ :$PATH: = *:$p:* ]] || PATH=$p:$PATH
+done
+
+export SHELL=$MKSH MANWIDTH=80 LESSHISTFILE=-
+alias cls='print -n \\033c'
+
+#unset LANGUAGE LC_ADDRESS LC_ALL LC_COLLATE LC_IDENTIFICATION LC_MONETARY \
+#    LC_NAME LC_NUMERIC LC_TELEPHONE LC_TIME
+#p=en_GB.UTF-8
+#set -U
+#export LANG=C LC_CTYPE=$p LC_MEASUREMENT=$p LC_MESSAGES=$p LC_PAPER=$p
+
+unset p
+
+: place customisations above this line

Deleted: vendor/MirOS/mksh/R50/edit.c
===================================================================
--- vendor/MirOS/mksh/dist/edit.c	2014-06-30 23:58:12 UTC (rev 6706)
+++ vendor/MirOS/mksh/R50/edit.c	2014-07-01 12:01:17 UTC (rev 6708)
@@ -1,5510 +0,0 @@
-/*	$OpenBSD: edit.c,v 1.38 2013/06/03 15:41:59 tedu Exp $	*/
-/*	$OpenBSD: edit.h,v 1.9 2011/05/30 17:14:35 martynas Exp $	*/
-/*	$OpenBSD: emacs.c,v 1.44 2011/09/05 04:50:33 marco Exp $	*/
-/*	$OpenBSD: vi.c,v 1.26 2009/06/29 22:50:19 martynas Exp $	*/
-
-/*-
- * Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
- *		 2011, 2012, 2013
- *	Thorsten Glaser <tg at mirbsd.org>
- *
- * Provided that these terms and disclaimer and all copyright notices
- * are retained or reproduced in an accompanying document, permission
- * is granted to deal in this work without restriction, including un-
- * limited rights to use, publicly perform, distribute, sell, modify,
- * merge, give away, or sublicence.
- *
- * This work is provided "AS IS" and WITHOUT WARRANTY of any kind, to
- * the utmost extent permitted by applicable law, neither express nor
- * implied; without malicious intent or gross negligence. In no event
- * may a licensor, author or contributor be held liable for indirect,
- * direct, other damage, loss, or other issues arising in any way out
- * of dealing in the work, even if advised of the possibility of such
- * damage or existence of a defect, except proven that it results out
- * of said person's immediate fault when using the work as intended.
- */
-
-#include "sh.h"
-
-#ifndef MKSH_NO_CMDLINE_EDITING
-
-__RCSID("$MirOS: src/bin/mksh/edit.c,v 1.270 2013/08/14 20:26:17 tg Exp $");
-
-/*
- * in later versions we might use libtermcap for this, but since external
- * dependencies are problematic, this has not yet been decided on; another
- * good string is "\033c" except on hardware terminals like the DEC VT420
- * which do a full power cycle then...
- */
-#ifndef MKSH_CLS_STRING
-#define MKSH_CLS_STRING		"\033[;H\033[J"
-#endif
-#ifndef MKSH_CLRTOEOL_STRING
-#define MKSH_CLRTOEOL_STRING	"\033[K"
-#endif
-
-/* tty driver characters we are interested in */
-typedef struct {
-	int erase;
-	int kill;
-	int werase;
-	int intr;
-	int quit;
-	int eof;
-} X_chars;
-
-static X_chars edchars;
-
-/* x_cf_glob() flags */
-#define XCF_COMMAND	BIT(0)	/* Do command completion */
-#define XCF_FILE	BIT(1)	/* Do file completion */
-#define XCF_FULLPATH	BIT(2)	/* command completion: store full path */
-#define XCF_COMMAND_FILE (XCF_COMMAND | XCF_FILE)
-#define XCF_IS_COMMAND	BIT(3)	/* return flag: is command */
-#define XCF_IS_NOSPACE	BIT(4)	/* return flag: do not append a space */
-
-static char editmode;
-static int xx_cols;			/* for Emacs mode */
-static int modified;			/* buffer has been "modified" */
-static char *holdbufp;			/* place to hold last edit buffer */
-
-static int x_getc(void);
-static void x_putcf(int);
-static void x_modified(void);
-static void x_mode(bool);
-static int x_do_comment(char *, ssize_t, ssize_t *);
-static void x_print_expansions(int, char * const *, bool);
-static int x_cf_glob(int *, const char *, int, int, int *, int *, char ***);
-static size_t x_longest_prefix(int, char * const *);
-static void x_glob_hlp_add_qchar(char *);
-static char *x_glob_hlp_tilde_and_rem_qchar(char *, bool);
-static int x_basename(const char *, const char *);
-static void x_free_words(int, char **);
-static int x_escape(const char *, size_t, int (*)(const char *, size_t));
-static int x_emacs(char *);
-static void x_init_prompt(void);
-#if !MKSH_S_NOVI
-static int x_vi(char *);
-#endif
-
-#define x_flush()	shf_flush(shl_out)
-#if defined(MKSH_SMALL) && !defined(MKSH_SMALL_BUT_FAST)
-#define x_putc(c)	x_putcf(c)
-#else
-#define x_putc(c)	shf_putc((c), shl_out)
-#endif
-
-static int path_order_cmp(const void *, const void *);
-static void glob_table(const char *, XPtrV *, struct table *);
-static void glob_path(int, const char *, XPtrV *, const char *);
-static int x_file_glob(int *, char *, char ***);
-static int x_command_glob(int, char *, char ***);
-static int x_locate_word(const char *, int, int, int *, bool *);
-
-static int x_e_getmbc(char *);
-static int x_e_rebuildline(const char *);
-
-/* +++ generic editing functions +++ */
-
-/*
- * read an edited command line
- */
-int
-x_read(char *buf)
-{
-	int i;
-
-	x_mode(true);
-	modified = 1;
-	if (Flag(FEMACS) || Flag(FGMACS))
-		i = x_emacs(buf);
-#if !MKSH_S_NOVI
-	else if (Flag(FVI))
-		i = x_vi(buf);
-#endif
-	else
-		/* internal error */
-		i = -1;
-	editmode = 0;
-	x_mode(false);
-	return (i);
-}
-
-/* tty I/O */
-
-static int
-x_getc(void)
-{
-	char c;
-	ssize_t n;
-
-	while ((n = blocking_read(STDIN_FILENO, &c, 1)) < 0 && errno == EINTR)
-		if (trap) {
-			x_mode(false);
-			runtraps(0);
-#ifdef SIGWINCH
-			if (got_winch) {
-				change_winsz();
-				if (x_cols != xx_cols && editmode == 1) {
-					/* redraw line in Emacs mode */
-					xx_cols = x_cols;
-					x_e_rebuildline(MKSH_CLRTOEOL_STRING);
-				}
-			}
-#endif
-			x_mode(true);
-		}
-	return ((n == 1) ? (int)(unsigned char)c : -1);
-}
-
-static void
-x_putcf(int c)
-{
-	shf_putc(c, shl_out);
-}
-
-/*********************************
- * Misc common code for vi/emacs *
- *********************************/
-
-/*-
- * Handle the commenting/uncommenting of a line.
- * Returns:
- *	1 if a carriage return is indicated (comment added)
- *	0 if no return (comment removed)
- *	-1 if there is an error (not enough room for comment chars)
- * If successful, *lenp contains the new length. Note: cursor should be
- * moved to the start of the line after (un)commenting.
- */
-static int
-x_do_comment(char *buf, ssize_t bsize, ssize_t *lenp)
-{
-	ssize_t i, j, len = *lenp;
-
-	if (len == 0)
-		/* somewhat arbitrary - it's what AT&T ksh does */
-		return (1);
-
-	/* Already commented? */
-	if (buf[0] == '#') {
-		bool saw_nl = false;
-
-		for (j = 0, i = 1; i < len; i++) {
-			if (!saw_nl || buf[i] != '#')
-				buf[j++] = buf[i];
-			saw_nl = buf[i] == '\n';
-		}
-		*lenp = j;
-		return (0);
-	} else {
-		int n = 1;
-
-		/* See if there's room for the #s - 1 per \n */
-		for (i = 0; i < len; i++)
-			if (buf[i] == '\n')
-				n++;
-		if (len + n >= bsize)
-			return (-1);
-		/* Now add them... */
-		for (i = len, j = len + n; --i >= 0; ) {
-			if (buf[i] == '\n')
-				buf[--j] = '#';
-			buf[--j] = buf[i];
-		}
-		buf[0] = '#';
-		*lenp += n;
-		return (1);
-	}
-}
-
-/****************************************************
- * Common file/command completion code for vi/emacs *
- ****************************************************/
-
-static void
-x_print_expansions(int nwords, char * const *words, bool is_command)
-{
-	bool use_copy = false;
-	int prefix_len;
-	XPtrV l = { NULL, 0, 0 };
-
-	/*
-	 * Check if all matches are in the same directory (in this
-	 * case, we want to omit the directory name)
-	 */
-	if (!is_command &&
-	    (prefix_len = x_longest_prefix(nwords, words)) > 0) {
-		int i;
-
-		/* Special case for 1 match (prefix is whole word) */
-		if (nwords == 1)
-			prefix_len = x_basename(words[0], NULL);
-		/* Any (non-trailing) slashes in non-common word suffixes? */
-		for (i = 0; i < nwords; i++)
-			if (x_basename(words[i] + prefix_len, NULL) >
-			    prefix_len)
-				break;
-		/* All in same directory? */
-		if (i == nwords) {
-			while (prefix_len > 0 && words[0][prefix_len - 1] != '/')
-				prefix_len--;
-			use_copy = true;
-			XPinit(l, nwords + 1);
-			for (i = 0; i < nwords; i++)
-				XPput(l, words[i] + prefix_len);
-			XPput(l, NULL);
-		}
-	}
-	/*
-	 * Enumerate expansions
-	 */
-	x_putc('\r');
-	x_putc('\n');
-	pr_list(use_copy ? (char **)XPptrv(l) : words);
-
-	if (use_copy)
-		/* not x_free_words() */
-		XPfree(l);
-}
-
-/*
- * Convert backslash-escaped string to QCHAR-escaped
- * string useful for globbing; loses QCHAR unless it
- * can squeeze in, eg. by previous loss of backslash
- */
-static void
-x_glob_hlp_add_qchar(char *cp)
-{
-	char ch, *dp = cp;
-	bool escaping = false;
-
-	while ((ch = *cp++)) {
-		if (ch == '\\' && !escaping) {
-			escaping = true;
-			continue;
-		}
-		if (escaping || (ch == QCHAR && (cp - dp) > 1)) {
-			/*
-			 * empirically made list of chars to escape
-			 * for globbing as well as QCHAR itself
-			 */
-			switch (ch) {
-			case QCHAR:
-			case '$':
-			case '*':
-			case '?':
-			case '[':
-			case '\\':
-			case '`':
-				*dp++ = QCHAR;
-				break;
-			}
-			escaping = false;
-		}
-		*dp++ = ch;
-	}
-	*dp = '\0';
-}
-
-/*
- * Run tilde expansion on argument string, return the result
- * after unescaping; if the flag is set, the original string
- * is freed if changed and assumed backslash-escaped, if not
- * it is assumed QCHAR-escaped
- */
-static char *
-x_glob_hlp_tilde_and_rem_qchar(char *s, bool magic_flag)
-{
-	char ch, *cp, *dp;
-
-	/*
-	 * On the string, check whether we have a tilde expansion,
-	 * and if so, discern "~foo/bar" and "~/baz" from "~blah";
-	 * if we have a directory part (the former), try to expand
-	 */
-	if (*s == '~' && (cp = strchr(s, '/')) != NULL) {
-		/* ok, so split into "~foo"/"bar" or "~"/"baz" */
-		*cp++ = 0;
-		/* try to expand the tilde */
-		if (!(dp = tilde(s + 1))) {
-			/* nope, revert damage */
-			*--cp = '/';
-		} else {
-			/* ok, expand and replace */
-			cp = shf_smprintf("%s/%s", dp, cp);
-			if (magic_flag)
-				afree(s, ATEMP);
-			s = cp;
-		}
-	}
-
-	/* ... convert it from backslash-escaped via QCHAR-escaped... */
-	if (magic_flag)
-		x_glob_hlp_add_qchar(s);
-	/* ... to unescaped, for comparison with the matches */
-	cp = dp = s;
-
-	while ((ch = *cp++)) {
-		if (ch == QCHAR && !(ch = *cp++))
-			break;
-		*dp++ = ch;
-	}
-	*dp = '\0';
-
-	return (s);
-}
-
-/**
- * Do file globbing:
- *	- does expansion, checks for no match, etc.
- *	- sets *wordsp to array of matching strings
- *	- returns number of matching strings
- */
-static int
-x_file_glob(int *flagsp, char *toglob, char ***wordsp)
-{
-	char **words, *cp;
-	int nwords;
-	XPtrV w;
-	struct source *s, *sold;
-
-	/* remove all escaping backward slashes */
-	x_glob_hlp_add_qchar(toglob);
-
-	/*
-	 * Convert "foo*" (toglob) to an array of strings (words)
-	 */
-	sold = source;
-	s = pushs(SWSTR, ATEMP);
-	s->start = s->str = toglob;
-	source = s;
-	if (yylex(ONEWORD | LQCHAR) != LWORD) {
-		source = sold;
-		internal_warningf("%s: %s", "fileglob", "bad substitution");
-		return (0);
-	}
-	source = sold;
-	afree(s, ATEMP);
-	XPinit(w, 32);
-	cp = yylval.cp;
-	while (*cp == CHAR || *cp == QCHAR)
-		cp += 2;
-	nwords = DOGLOB | DOTILDE | DOMARKDIRS;
-	if (*cp != EOS) {
-		/* probably a $FOO expansion */
-		*flagsp |= XCF_IS_NOSPACE;
-		/* this always results in at most one match */
-		nwords = 0;
-	}
-	expand(yylval.cp, &w, nwords);
-	XPput(w, NULL);
-	words = (char **)XPclose(w);
-
-	for (nwords = 0; words[nwords]; nwords++)
-		;
-	if (nwords == 1) {
-		struct stat statb;
-
-		/* Expand any tilde and drop all QCHAR for comparison */
-		toglob = x_glob_hlp_tilde_and_rem_qchar(toglob, false);
-
-		/*
-		 * Check if globbing failed (returned glob pattern),
-		 * but be careful (e.g. toglob == "ab*" when the file
-		 * "ab*" exists is not an error).
-		 * Also, check for empty result - happens if we tried
-		 * to glob something which evaluated to an empty
-		 * string (e.g., "$FOO" when there is no FOO, etc).
-		 */
-		if ((strcmp(words[0], toglob) == 0 &&
-		    stat(words[0], &statb) < 0) ||
-		    words[0][0] == '\0') {
-			x_free_words(nwords, words);
-			words = NULL;
-			nwords = 0;
-		}
-	}
-
-	if ((*wordsp = nwords ? words : NULL) == NULL && words != NULL)
-		x_free_words(nwords, words);
-
-	return (nwords);
-}
-
-/* Data structure used in x_command_glob() */
-struct path_order_info {
-	char *word;
-	int base;
-	int path_order;
-};
-
-/* Compare routine used in x_command_glob() */
-static int
-path_order_cmp(const void *aa, const void *bb)
-{
-	const struct path_order_info *a = (const struct path_order_info *)aa;
-	const struct path_order_info *b = (const struct path_order_info *)bb;
-	int t;
-
-	t = strcmp(a->word + a->base, b->word + b->base);
-	return (t ? t : a->path_order - b->path_order);
-}
-
-static int
-x_command_glob(int flags, char *toglob, char ***wordsp)
-{
-	char *pat, *fpath;
-	size_t nwords;
-	XPtrV w;
-	struct block *l;
-
-	/* Convert "foo*" (toglob) to a pattern for future use */
-	pat = evalstr(toglob, DOPAT | DOTILDE);
-
-	XPinit(w, 32);
-
-	glob_table(pat, &w, &keywords);
-	glob_table(pat, &w, &aliases);
-	glob_table(pat, &w, &builtins);
-	for (l = e->loc; l; l = l->next)
-		glob_table(pat, &w, &l->funs);
-
-	glob_path(flags, pat, &w, path);
-	if ((fpath = str_val(global("FPATH"))) != null)
-		glob_path(flags, pat, &w, fpath);
-
-	nwords = XPsize(w);
-
-	if (!nwords) {
-		*wordsp = NULL;
-		XPfree(w);
-		return (0);
-	}
-	/* Sort entries */
-	if (flags & XCF_FULLPATH) {
-		/* Sort by basename, then path order */
-		struct path_order_info *info, *last_info = NULL;
-		char **words = (char **)XPptrv(w);
-		size_t i, path_order = 0;
-
-		info = (struct path_order_info *)
-		    alloc2(nwords, sizeof(struct path_order_info), ATEMP);
-		for (i = 0; i < nwords; i++) {
-			info[i].word = words[i];
-			info[i].base = x_basename(words[i], NULL);
-			if (!last_info || info[i].base != last_info->base ||
-			    strncmp(words[i], last_info->word, info[i].base) != 0) {
-				last_info = &info[i];
-				path_order++;
-			}
-			info[i].path_order = path_order;
-		}
-		qsort(info, nwords, sizeof(struct path_order_info),
-		    path_order_cmp);
-		for (i = 0; i < nwords; i++)
-			words[i] = info[i].word;
-		afree(info, ATEMP);
-	} else {
-		/* Sort and remove duplicate entries */
-		char **words = (char **)XPptrv(w);
-		size_t i, j;
-
-		qsort(words, nwords, sizeof(void *), xstrcmp);
-		for (i = j = 0; i < nwords - 1; i++) {
-			if (strcmp(words[i], words[i + 1]))
-				words[j++] = words[i];
-			else
-				afree(words[i], ATEMP);
-		}
-		words[j++] = words[i];
-		w.len = nwords = j;
-	}
-
-	XPput(w, NULL);
-	*wordsp = (char **)XPclose(w);
-
-	return (nwords);
-}
-
-#define IS_WORDC(c)	(!ctype(c, C_LEX1) && (c) != '\'' && (c) != '"' && \
-			    (c) != '`' && (c) != '=' && (c) != ':')
-
-static int
-x_locate_word(const char *buf, int buflen, int pos, int *startp,
-    bool *is_commandp)
-{
-	int start, end;
-
-	/* Bad call? Probably should report error */
-	if (pos < 0 || pos > buflen) {
-		*startp = pos;
-		*is_commandp = false;
-		return (0);
-	}
-	/* The case where pos == buflen happens to take care of itself... */
-
-	start = pos;
-	/*
-	 * Keep going backwards to start of word (has effect of allowing
-	 * one blank after the end of a word)
-	 */
-	for (; (start > 0 && IS_WORDC(buf[start - 1])) ||
-	    (start > 1 && buf[start - 2] == '\\'); start--)
-		;
-	/* Go forwards to end of word */
-	for (end = start; end < buflen && IS_WORDC(buf[end]); end++) {
-		if (buf[end] == '\\' && (end + 1) < buflen)
-			end++;
-	}
-
-	if (is_commandp) {
-		bool iscmd;
-		int p = start - 1;
-
-		/* Figure out if this is a command */
-		while (p >= 0 && ksh_isspace(buf[p]))
-			p--;
-		iscmd = p < 0 || vstrchr(";|&()`", buf[p]);
-		if (iscmd) {
-			/*
-			 * If command has a /, path, etc. is not searched;
-			 * only current directory is searched which is just
-			 * like file globbing.
-			 */
-			for (p = start; p < end; p++)
-				if (buf[p] == '/')
-					break;
-			iscmd = p == end;
-		}
-		*is_commandp = iscmd;
-	}
-	*startp = start;
-
-	return (end - start);
-}
-
-static int
-x_cf_glob(int *flagsp, const char *buf, int buflen, int pos, int *startp,
-    int *endp, char ***wordsp)
-{
-	int len, nwords = 0;
-	char **words = NULL;
-	bool is_command;
-
-	mkssert(buf != NULL);
-
-	len = x_locate_word(buf, buflen, pos, startp, &is_command);
-	if (!((*flagsp) & XCF_COMMAND))
-		is_command = false;
-	/*
-	 * Don't do command globing on zero length strings - it takes too
-	 * long and isn't very useful. File globs are more likely to be
-	 * useful, so allow these.
-	 */
-	if (len == 0 && is_command)
-		return (0);
-
-	if (len >= 0) {
-		char *toglob, *s;
-
-		/*
-		 * Given a string, copy it and possibly add a '*' to the end.
-		 */
-
-		strndupx(toglob, buf + *startp, len + /* the '*' */ 1, ATEMP);
-		toglob[len] = '\0';
-
-		/*
-		 * If the pathname contains a wildcard (an unquoted '*',
-		 * '?', or '[') or an extglob, then it is globbed based
-		 * on that value (i.e., without the appended '*'). Same
-		 * for parameter substitutions (as in “cat $HOME/.ss↹”)
-		 * without appending a trailing space (LP: #710539), as
-		 * well as for “~foo” (but not “~foo/”).
-		 */
-		for (s = toglob; *s; s++) {
-			if (*s == '\\' && s[1])
-				s++;
-			else if (*s == '?' || *s == '*' || *s == '[' ||
-			    *s == '$' ||
-			    /* ?() *() +() @() !() but two already checked */
-			    (s[1] == '(' /*)*/ &&
-			    (*s == '+' || *s == '@' || *s == '!'))) {
-				/*
-				 * just expand based on the extglob
-				 * or parameter
-				 */
-				goto dont_add_glob;
-			}
-		}
-
-		if (*toglob == '~' && !vstrchr(toglob, '/')) {
-			/* neither for '~foo' (but '~foo/bar') */
-			*flagsp |= XCF_IS_NOSPACE;
-			goto dont_add_glob;
-		}
-
-		/* append a glob */
-		toglob[len] = '*';
-		toglob[len + 1] = '\0';
- dont_add_glob:
-		/*
-		 * Expand (glob) it now.
-		 */
-
-		nwords = is_command ?
-		    x_command_glob(*flagsp, toglob, &words) :
-		    x_file_glob(flagsp, toglob, &words);
-		afree(toglob, ATEMP);
-	}
-	if (nwords == 0) {
-		*wordsp = NULL;
-		return (0);
-	}
-	if (is_command)
-		*flagsp |= XCF_IS_COMMAND;
-	*wordsp = words;
-	*endp = *startp + len;
-
-	return (nwords);
-}
-
-/*
- * Find longest common prefix
- */
-static size_t
-x_longest_prefix(int nwords, char * const * words)
-{
-	int i;
-	size_t j, prefix_len;
-	char *p;
-
-	if (nwords <= 0)
-		return (0);
-
-	prefix_len = strlen(words[0]);
-	for (i = 1; i < nwords; i++)
-		for (j = 0, p = words[i]; j < prefix_len; j++)
-			if (p[j] != words[0][j]) {
-				prefix_len = j;
-				break;
-			}
-	/* false for nwords==1 as 0 = words[0][prefix_len] then */
-	if (UTFMODE && prefix_len && (words[0][prefix_len] & 0xC0) == 0x80)
-		while (prefix_len && (words[0][prefix_len] & 0xC0) != 0xC0)
-			--prefix_len;
-	return (prefix_len);
-}
-
-static void
-x_free_words(int nwords, char **words)
-{
-	while (nwords)
-		afree(words[--nwords], ATEMP);
-	afree(words, ATEMP);
-}
-
-/*-
- * Return the offset of the basename of string s (which ends at se - need not
- * be null terminated). Trailing slashes are ignored. If s is just a slash,
- * then the offset is 0 (actually, length - 1).
- *	s		Return
- *	/etc		1
- *	/etc/		1
- *	/etc//		1
- *	/etc/fo		5
- *	foo		0
- *	///		2
- *			0
- */
-static int
-x_basename(const char *s, const char *se)
-{
-	const char *p;
-
-	if (se == NULL)
-		se = s + strlen(s);
-	if (s == se)
-		return (0);
-
-	/* Skip trailing slashes */
-	for (p = se - 1; p > s && *p == '/'; p--)
-		;
-	for (; p > s && *p != '/'; p--)
-		;
-	if (*p == '/' && p + 1 < se)
-		p++;
-
-	return (p - s);
-}
-
-/*
- * Apply pattern matching to a table: all table entries that match a pattern
- * are added to wp.
- */
-static void
-glob_table(const char *pat, XPtrV *wp, struct table *tp)
-{
-	struct tstate ts;
-	struct tbl *te;
-
-	ktwalk(&ts, tp);
-	while ((te = ktnext(&ts)))
-		if (gmatchx(te->name, pat, false)) {
-			char *cp;
-
-			strdupx(cp, te->name, ATEMP);
-			XPput(*wp, cp);
-		}
-}
-
-static void
-glob_path(int flags, const char *pat, XPtrV *wp, const char *lpath)
-{
-	const char *sp = lpath, *p;
-	char *xp, **words;
-	size_t pathlen, patlen, oldsize, newsize, i, j;
-	XString xs;
-
-	patlen = strlen(pat);
-	checkoktoadd(patlen, 129 + X_EXTRA);
-	++patlen;
-	Xinit(xs, xp, patlen + 128, ATEMP);
-	while (sp) {
-		xp = Xstring(xs, xp);
-		if (!(p = cstrchr(sp, ':')))
-			p = sp + strlen(sp);
-		pathlen = p - sp;
-		if (pathlen) {
-			/*
-			 * Copy sp into xp, stuffing any MAGIC characters
-			 * on the way
-			 */
-			const char *s = sp;
-
-			XcheckN(xs, xp, pathlen * 2);
-			while (s < p) {
-				if (ISMAGIC(*s))
-					*xp++ = MAGIC;
-				*xp++ = *s++;
-			}
-			*xp++ = '/';
-			pathlen++;
-		}
-		sp = p;
-		XcheckN(xs, xp, patlen);
-		memcpy(xp, pat, patlen);
-
-		oldsize = XPsize(*wp);
-		/* mark dirs */
-		glob_str(Xstring(xs, xp), wp, true);
-		newsize = XPsize(*wp);
-
-		/* Check that each match is executable... */
-		words = (char **)XPptrv(*wp);
-		for (i = j = oldsize; i < newsize; i++) {
-			if (ksh_access(words[i], X_OK) == 0) {
-				words[j] = words[i];
-				if (!(flags & XCF_FULLPATH))
-					memmove(words[j], words[j] + pathlen,
-					    strlen(words[j] + pathlen) + 1);
-				j++;
-			} else
-				afree(words[i], ATEMP);
-		}
-		wp->len = j;
-
-		if (!*sp++)
-			break;
-	}
-	Xfree(xs, xp);
-}
-
-/*
- * if argument string contains any special characters, they will
- * be escaped and the result will be put into edit buffer by
- * keybinding-specific function
- */
-static int
-x_escape(const char *s, size_t len, int (*putbuf_func)(const char *, size_t))
-{
-	size_t add = 0, wlen = len;
-	const char *ifs = str_val(local("IFS", 0));
-	int rval = 0;
-
-	while (wlen - add > 0)
-		if (vstrchr("\"#$&'()*:;<=>?[\\`{|}", s[add]) ||
-		    vstrchr(ifs, s[add])) {
-			if (putbuf_func(s, add) != 0) {
-				rval = -1;
-				break;
-			}
-			putbuf_func(s[add] == '\n' ? "'" : "\\", 1);
-			putbuf_func(&s[add], 1);
-			if (s[add] == '\n')
-				putbuf_func("'", 1);
-
-			add++;
-			wlen -= add;
-			s += add;
-			add = 0;
-		} else
-			++add;
-	if (wlen > 0 && rval == 0)
-		rval = putbuf_func(s, wlen);
-
-	return (rval);
-}
-
-
-/* +++ emacs editing mode +++ */
-
-static	Area	aedit;
-#define	AEDIT	&aedit		/* area for kill ring and macro defns */
-
-/* values returned by keyboard functions */
-#define	KSTD	0
-#define	KEOL	1		/* ^M, ^J */
-#define	KINTR	2		/* ^G, ^C */
-
-struct x_ftab {
-	int (*xf_func)(int c);
-	const char *xf_name;
-	short xf_flags;
-};
-
-struct x_defbindings {
-	unsigned char xdb_func;	/* XFUNC_* */
-	unsigned char xdb_tab;
-	unsigned char xdb_char;
-};
-
-#define XF_ARG		1	/* command takes number prefix */
-#define	XF_NOBIND	2	/* not allowed to bind to function */
-#define	XF_PREFIX	4	/* function sets prefix */
-
-/* Separator for completion */
-#define	is_cfs(c)	((c) == ' ' || (c) == '\t' || (c) == '"' || (c) == '\'')
-/* Separator for motion */
-#define	is_mfs(c)	(!(ksh_isalnux(c) || (c) == '$' || ((c) & 0x80)))
-
-#define X_NTABS		3			/* normal, meta1, meta2 */
-#define X_TABSZ		256			/* size of keydef tables etc */
-
-/*-
- * Arguments for do_complete()
- * 0 = enumerate	M-=	complete as much as possible and then list
- * 1 = complete		M-Esc
- * 2 = list		M-?
- */
-typedef enum {
-	CT_LIST,	/* list the possible completions */
-	CT_COMPLETE,	/* complete to longest prefix */
-	CT_COMPLIST	/* complete and then list (if non-exact) */
-} Comp_type;
-
-/*
- * The following are used for my horizontal scrolling stuff
- */
-static char *xbuf;		/* beg input buffer */
-static char *xend;		/* end input buffer */
-static char *xcp;		/* current position */
-static char *xep;		/* current end */
-static char *xbp;		/* start of visible portion of input buffer */
-static char *xlp;		/* last char visible on screen */
-static bool x_adj_ok;
-/*
- * we use x_adj_done so that functions can tell
- * whether x_adjust() has been called while they are active.
- */
-static int x_adj_done;		/* is incremented by x_adjust() */
-
-static int x_displen;
-static int x_arg;		/* general purpose arg */
-static bool x_arg_defaulted;	/* x_arg not explicitly set; defaulted to 1 */
-
-static bool xlp_valid;		/* lastvis pointer was recalculated */
-
-static char **x_histp;		/* history position */
-static int x_nextcmd;		/* for newline-and-next */
-static char **x_histncp;	/* saved x_histp for " */
-static char *xmp;		/* mark pointer */
-static unsigned char x_last_command;
-static unsigned char (*x_tab)[X_TABSZ];	/* key definition */
-#ifndef MKSH_SMALL
-static char *(*x_atab)[X_TABSZ];	/* macro definitions */
-#endif
-static unsigned char x_bound[(X_TABSZ * X_NTABS + 7) / 8];
-#define KILLSIZE	20
-static char *killstack[KILLSIZE];
-static int killsp, killtp;
-static int x_curprefix;
-#ifndef MKSH_SMALL
-static char *macroptr;		/* bind key macro active? */
-#endif
-#if !MKSH_S_NOVI
-static int winwidth;		/* width of window */
-static char *wbuf[2];		/* window buffers */
-static int wbuf_len;		/* length of window buffers (x_cols - 3) */
-static int win;			/* window buffer in use */
-static char morec;		/* more character at right of window */
-static int lastref;		/* argument to last refresh() */
-static int holdlen;		/* length of holdbuf */
-#endif
-static int pwidth;		/* width of prompt */
-static int prompt_trunc;	/* how much of prompt to truncate or -1 */
-static int x_col;		/* current column on line */
-
-static int x_ins(const char *);
-static void x_delete(size_t, bool);
-static size_t x_bword(void);
-static size_t x_fword(bool);
-static void x_goto(char *);
-static char *x_bs0(char *, char *);
-static void x_bs3(char **);
-static int x_size_str(char *);
-static int x_size2(char *, char **);
-static void x_zots(char *);
-static void x_zotc2(int);
-static void x_zotc3(char **);
-static void x_load_hist(char **);
-static int x_search(char *, int, int);
-#ifndef MKSH_SMALL
-static int x_search_dir(int);
-#endif
-static int x_match(char *, char *);
-static void x_redraw(int);
-static void x_push(int);
-static char *x_mapin(const char *, Area *);
-static char *x_mapout(int);
-static void x_mapout2(int, char **);
-static void x_print(int, int);
-static void x_adjust(void);
-static void x_e_ungetc(int);
-static int x_e_getc(void);
-static void x_e_putc2(int);
-static void x_e_putc3(const char **);
-static void x_e_puts(const char *);
-#ifndef MKSH_SMALL
-static int x_fold_case(int);
-#endif
-static char *x_lastcp(void);
-static void do_complete(int, Comp_type);
-static size_t x_nb2nc(size_t);
-
-static int unget_char = -1;
-
-static int x_do_ins(const char *, size_t);
-static void bind_if_not_bound(int, int, int);
-
-enum emacs_funcs {
-#define EMACSFN_ENUMS
-#include "emacsfn.h"
-	XFUNC_MAX
-};
-
-#define EMACSFN_DEFNS
-#include "emacsfn.h"
-
-static const struct x_ftab x_ftab[] = {
-#define EMACSFN_ITEMS
-#include "emacsfn.h"
-	{ 0, NULL, 0 }
-};
-
-static struct x_defbindings const x_defbindings[] = {
-	{ XFUNC_del_back,		0, CTRL('?')	},
-	{ XFUNC_del_bword,		1, CTRL('?')	},
-	{ XFUNC_eot_del,		0, CTRL('D')	},
-	{ XFUNC_del_back,		0, CTRL('H')	},
-	{ XFUNC_del_bword,		1, CTRL('H')	},
-	{ XFUNC_del_bword,		1,	'h'	},
-	{ XFUNC_mv_bword,		1,	'b'	},
-	{ XFUNC_mv_fword,		1,	'f'	},
-	{ XFUNC_del_fword,		1,	'd'	},
-	{ XFUNC_mv_back,		0, CTRL('B')	},
-	{ XFUNC_mv_forw,		0, CTRL('F')	},
-	{ XFUNC_search_char_forw,	0, CTRL(']')	},
-	{ XFUNC_search_char_back,	1, CTRL(']')	},
-	{ XFUNC_newline,		0, CTRL('M')	},
-	{ XFUNC_newline,		0, CTRL('J')	},
-	{ XFUNC_end_of_text,		0, CTRL('_')	},
-	{ XFUNC_abort,			0, CTRL('G')	},
-	{ XFUNC_prev_com,		0, CTRL('P')	},
-	{ XFUNC_next_com,		0, CTRL('N')	},
-	{ XFUNC_nl_next_com,		0, CTRL('O')	},
-	{ XFUNC_search_hist,		0, CTRL('R')	},
-	{ XFUNC_beg_hist,		1,	'<'	},
-	{ XFUNC_end_hist,		1,	'>'	},
-	{ XFUNC_goto_hist,		1,	'g'	},
-	{ XFUNC_mv_end,			0, CTRL('E')	},
-	{ XFUNC_mv_begin,		0, CTRL('A')	},
-	{ XFUNC_draw_line,		0, CTRL('L')	},
-	{ XFUNC_cls,			1, CTRL('L')	},
-	{ XFUNC_meta1,			0, CTRL('[')	},
-	{ XFUNC_meta2,			0, CTRL('X')	},
-	{ XFUNC_kill,			0, CTRL('K')	},
-	{ XFUNC_yank,			0, CTRL('Y')	},
-	{ XFUNC_meta_yank,		1,	'y'	},
-	{ XFUNC_literal,		0, CTRL('^')	},
-	{ XFUNC_comment,		1,	'#'	},
-	{ XFUNC_transpose,		0, CTRL('T')	},
-	{ XFUNC_complete,		1, CTRL('[')	},
-	{ XFUNC_comp_list,		0, CTRL('I')	},
-	{ XFUNC_comp_list,		1,	'='	},
-	{ XFUNC_enumerate,		1,	'?'	},
-	{ XFUNC_expand,			1,	'*'	},
-	{ XFUNC_comp_file,		1, CTRL('X')	},
-	{ XFUNC_comp_comm,		2, CTRL('[')	},
-	{ XFUNC_list_comm,		2,	'?'	},
-	{ XFUNC_list_file,		2, CTRL('Y')	},
-	{ XFUNC_set_mark,		1,	' '	},
-	{ XFUNC_kill_region,		0, CTRL('W')	},
-	{ XFUNC_xchg_point_mark,	2, CTRL('X')	},
-	{ XFUNC_literal,		0, CTRL('V')	},
-	{ XFUNC_version,		1, CTRL('V')	},
-	{ XFUNC_prev_histword,		1,	'.'	},
-	{ XFUNC_prev_histword,		1,	'_'	},
-	{ XFUNC_set_arg,		1,	'0'	},
-	{ XFUNC_set_arg,		1,	'1'	},
-	{ XFUNC_set_arg,		1,	'2'	},
-	{ XFUNC_set_arg,		1,	'3'	},
-	{ XFUNC_set_arg,		1,	'4'	},
-	{ XFUNC_set_arg,		1,	'5'	},
-	{ XFUNC_set_arg,		1,	'6'	},
-	{ XFUNC_set_arg,		1,	'7'	},
-	{ XFUNC_set_arg,		1,	'8'	},
-	{ XFUNC_set_arg,		1,	'9'	},
-#ifndef MKSH_SMALL
-	{ XFUNC_fold_upper,		1,	'U'	},
-	{ XFUNC_fold_upper,		1,	'u'	},
-	{ XFUNC_fold_lower,		1,	'L'	},
-	{ XFUNC_fold_lower,		1,	'l'	},
-	{ XFUNC_fold_capitalise,	1,	'C'	},
-	{ XFUNC_fold_capitalise,	1,	'c'	},
-#endif
-	/*
-	 * These for ANSI arrow keys: arguablely shouldn't be here by
-	 * default, but its simpler/faster/smaller than using termcap
-	 * entries.
-	 */
-	{ XFUNC_meta2,			1,	'['	},
-	{ XFUNC_meta2,			1,	'O'	},
-	{ XFUNC_prev_com,		2,	'A'	},
-	{ XFUNC_next_com,		2,	'B'	},
-	{ XFUNC_mv_forw,		2,	'C'	},
-	{ XFUNC_mv_back,		2,	'D'	},
-#ifndef MKSH_SMALL
-	{ XFUNC_vt_hack,		2,	'1'	},
-	{ XFUNC_mv_begin | 0x80,	2,	'7'	},
-	{ XFUNC_mv_begin,		2,	'H'	},
-	{ XFUNC_mv_end | 0x80,		2,	'4'	},
-	{ XFUNC_mv_end | 0x80,		2,	'8'	},
-	{ XFUNC_mv_end,			2,	'F'	},
-	{ XFUNC_del_char | 0x80,	2,	'3'	},
-	{ XFUNC_search_hist_up | 0x80,	2,	'5'	},
-	{ XFUNC_search_hist_dn | 0x80,	2,	'6'	},
-	/* more non-standard ones */
-	{ XFUNC_edit_line,		2,	'e'	}
-#endif
-};
-
-static size_t
-x_nb2nc(size_t nb)
-{
-	char *cp;
-	size_t nc = 0;
-
-	for (cp = xcp; cp < (xcp + nb); ++nc)
-		cp += utf_ptradj(cp);
-	return (nc);
-}
-
-static void
-x_modified(void)
-{
-	if (!modified) {
-		x_histp = histptr + 1;
-		modified = 1;
-	}
-}
-
-#ifdef MKSH_SMALL
-#define XFUNC_VALUE(f) (f)
-#else
-#define XFUNC_VALUE(f) (f & 0x7F)
-#endif
-
-static int
-x_e_getmbc(char *sbuf)
-{
-	int c, pos = 0;
-	unsigned char *buf = (unsigned char *)sbuf;
-
-	memset(buf, 0, 4);
-	buf[pos++] = c = x_e_getc();
-	if (c == -1)
-		return (-1);
-	if (UTFMODE) {
-		if ((buf[0] >= 0xC2) && (buf[0] < 0xF0)) {
-			c = x_e_getc();
-			if (c == -1)
-				return (-1);
-			if ((c & 0xC0) != 0x80) {
-				x_e_ungetc(c);
-				return (1);
-			}
-			buf[pos++] = c;
-		}
-		if ((buf[0] >= 0xE0) && (buf[0] < 0xF0)) {
-			/* XXX x_e_ungetc is one-octet only */
-			buf[pos++] = c = x_e_getc();
-			if (c == -1)
-				return (-1);
-		}
-	}
-	return (pos);
-}
-
-static void
-x_init_prompt(void)
-{
-	prompt_trunc = pprompt(prompt, 0);
-	pwidth = prompt_trunc % x_cols;
-	prompt_trunc -= pwidth;
-	if ((mksh_uari_t)pwidth > ((mksh_uari_t)x_cols - 3 - MIN_EDIT_SPACE)) {
-		/* force newline after prompt */
-		prompt_trunc = -1;
-		pwidth = 0;
-		x_e_putc2('\n');
-	}
-}
-
-static int
-x_emacs(char *buf)
-{
-	int c, i;
-	unsigned char f;
-
-	xbp = xbuf = buf;
-	xend = buf + LINE;
-	xlp = xcp = xep = buf;
-	*xcp = 0;
-	xlp_valid = true;
-	xmp = NULL;
-	x_curprefix = 0;
-	x_histp = histptr + 1;
-	x_last_command = XFUNC_error;
-
-	x_init_prompt();
-	x_displen = (xx_cols = x_cols) - 2 - (x_col = pwidth);
-	x_adj_done = 0;
-	x_adj_ok = true;
-
-	x_histncp = NULL;
-	if (x_nextcmd >= 0) {
-		int off = source->line - x_nextcmd;
-		if (histptr - history >= off) {
-			x_load_hist(histptr - off);
-			x_histncp = x_histp;
-		}
-		x_nextcmd = -1;
-	}
-	editmode = 1;
-	while (/* CONSTCOND */ 1) {
-		x_flush();
-		if ((c = x_e_getc()) < 0)
-			return (0);
-
-		f = x_curprefix == -1 ? XFUNC_insert :
-		    x_tab[x_curprefix][c];
-#ifndef MKSH_SMALL
-		if (f & 0x80) {
-			f &= 0x7F;
-			if ((i = x_e_getc()) != '~')
-				x_e_ungetc(i);
-		}
-
-		/* avoid bind key macro recursion */
-		if (macroptr && f == XFUNC_ins_string)
-			f = XFUNC_insert;
-#endif
-
-		if (!(x_ftab[f].xf_flags & XF_PREFIX) &&
-		    x_last_command != XFUNC_set_arg) {
-			x_arg = 1;
-			x_arg_defaulted = true;
-		}
-		i = c | (x_curprefix << 8);
-		x_curprefix = 0;
-		switch ((*x_ftab[f].xf_func)(i)) {
-		case KSTD:
-			if (!(x_ftab[f].xf_flags & XF_PREFIX))
-				x_last_command = f;
-			break;
-		case KEOL:
-			i = xep - xbuf;
-			return (i);
-		case KINTR:
-			/* special case for interrupt */
-			trapsig(SIGINT);
-			x_mode(false);
-			unwind(LSHELL);
-		}
-		/* ad-hoc hack for fixing the cursor position */
-		x_goto(xcp);
-	}
-}
-
-static int
-x_insert(int c)
-{
-	static int left, pos, save_arg;
-	static char str[4];
-
-	/*
-	 * Should allow tab and control chars.
-	 */
-	if (c == 0) {
- invmbs:
-		left = 0;
-		x_e_putc2(7);
-		return (KSTD);
-	}
-	if (UTFMODE) {
-		if (((c & 0xC0) == 0x80) && left) {
-			str[pos++] = c;
-			if (!--left) {
-				str[pos] = '\0';
-				x_arg = save_arg;
-				while (x_arg--)
-					x_ins(str);
-			}
-			return (KSTD);
-		}
-		if (left) {
-			if (x_curprefix == -1) {
-				/* flush invalid multibyte */
-				str[pos] = '\0';
-				while (save_arg--)
-					x_ins(str);
-			}
-		}
-		if ((c >= 0xC2) && (c < 0xE0))
-			left = 1;
-		else if ((c >= 0xE0) && (c < 0xF0))
-			left = 2;
-		else if (c > 0x7F)
-			goto invmbs;
-		else
-			left = 0;
-		if (left) {
-			save_arg = x_arg;
-			pos = 1;
-			str[0] = c;
-			return (KSTD);
-		}
-	}
-	left = 0;
-	str[0] = c;
-	str[1] = '\0';
-	while (x_arg--)
-		x_ins(str);
-	return (KSTD);
-}
-
-#ifndef MKSH_SMALL
-static int
-x_ins_string(int c)
-{
-	macroptr = x_atab[c >> 8][c & 255];
-	/*
-	 * we no longer need to bother checking if macroptr is
-	 * not NULL but first char is NUL; x_e_getc() does it
-	 */
-	return (KSTD);
-}
-#endif
-
-static int
-x_do_ins(const char *cp, size_t len)
-{
-	if (xep + len >= xend) {
-		x_e_putc2(7);
-		return (-1);
-	}
-	memmove(xcp + len, xcp, xep - xcp + 1);
-	memmove(xcp, cp, len);
-	xcp += len;
-	xep += len;
-	x_modified();
-	return (0);
-}
-
-static int
-x_ins(const char *s)
-{
-	char *cp = xcp;
-	int adj = x_adj_done;
-
-	if (x_do_ins(s, strlen(s)) < 0)
-		return (-1);
-	/*
-	 * x_zots() may result in a call to x_adjust()
-	 * we want xcp to reflect the new position.
-	 */
-	xlp_valid = false;
-	x_lastcp();
-	x_adj_ok = tobool(xcp >= xlp);
-	x_zots(cp);
-	/* has x_adjust() been called? */
-	if (adj == x_adj_done) {
-		/* no */
-		cp = xlp;
-		while (cp > xcp)
-			x_bs3(&cp);
-	}
-	if (xlp == xep - 1)
-		x_redraw(xx_cols);
-	x_adj_ok = true;
-	return (0);
-}
-
-static int
-x_del_back(int c MKSH_A_UNUSED)
-{
-	ssize_t i = 0;
-
-	if (xcp == xbuf) {
-		x_e_putc2(7);
-		return (KSTD);
-	}
-	do {
-		x_goto(xcp - 1);
-	} while ((++i < x_arg) && (xcp != xbuf));
-	x_delete(i, false);
-	return (KSTD);
-}
-
-static int
-x_del_char(int c MKSH_A_UNUSED)
-{
-	char *cp, *cp2;
-	size_t i = 0;
-
-	cp = xcp;
-	while (i < (size_t)x_arg) {
-		utf_ptradjx(cp, cp2);
-		if (cp2 > xep)
-			break;
-		cp = cp2;
-		i++;
-	}
-
-	if (!i) {
-		x_e_putc2(7);
-		return (KSTD);
-	}
-	x_delete(i, false);
-	return (KSTD);
-}
-
-/* Delete nc chars to the right of the cursor (including cursor position) */
-static void
-x_delete(size_t nc, bool push)
-{
-	size_t i, nb, nw;
-	char *cp;
-
-	if (nc == 0)
-		return;
-
-	nw = 0;
-	cp = xcp;
-	for (i = 0; i < nc; ++i) {
-		char *cp2;
-		int j;
-
-		j = x_size2(cp, &cp2);
-		if (cp2 > xep)
-			break;
-		cp = cp2;
-		nw += j;
-	}
-	nb = cp - xcp;
-	/* nc = i; */
-
-	if (xmp != NULL && xmp > xcp) {
-		if (xcp + nb > xmp)
-			xmp = xcp;
-		else
-			xmp -= nb;
-	}
-	/*
-	 * This lets us yank a word we have deleted.
-	 */
-	if (push)
-		x_push(nb);
-
-	xep -= nb;
-	/* Copies the NUL */
-	memmove(xcp, xcp + nb, xep - xcp + 1);
-	/* don't redraw */
-	x_adj_ok = false;
-	xlp_valid = false;
-	x_zots(xcp);
-	/*
-	 * if we are already filling the line,
-	 * there is no need to ' ', '\b'.
-	 * But if we must, make sure we do the minimum.
-	 */
-	if ((i = xx_cols - 2 - x_col) > 0 || xep - xlp == 0) {
-		nw = i = (nw < i) ? nw : i;
-		while (i--)
-			x_e_putc2(' ');
-		if (x_col == xx_cols - 2) {
-			x_e_putc2((xep > xlp) ? '>' : (xbp > xbuf) ? '<' : ' ');
-			++nw;
-		}
-		while (nw--)
-			x_e_putc2('\b');
-	}
-	/*x_goto(xcp);*/
-	x_adj_ok = true;
-	xlp_valid = false;
-	cp = x_lastcp();
-	while (cp > xcp)
-		x_bs3(&cp);
-
-	x_modified();
-	return;
-}
-
-static int
-x_del_bword(int c MKSH_A_UNUSED)
-{
-	x_delete(x_bword(), true);
-	return (KSTD);
-}
-
-static int
-x_mv_bword(int c MKSH_A_UNUSED)
-{
-	x_bword();
-	return (KSTD);
-}
-
-static int
-x_mv_fword(int c MKSH_A_UNUSED)
-{
-	x_fword(true);
-	return (KSTD);
-}
-
-static int
-x_del_fword(int c MKSH_A_UNUSED)
-{
-	x_delete(x_fword(false), true);
-	return (KSTD);
-}
-
-static size_t
-x_bword(void)
-{
-	size_t nb = 0;
-	char *cp = xcp;
-
-	if (cp == xbuf) {
-		x_e_putc2(7);
-		return (0);
-	}
-	while (x_arg--) {
-		while (cp != xbuf && is_mfs(cp[-1])) {
-			cp--;
-			nb++;
-		}
-		while (cp != xbuf && !is_mfs(cp[-1])) {
-			cp--;
-			nb++;
-		}
-	}
-	x_goto(cp);
-	return (x_nb2nc(nb));
-}
-
-static size_t
-x_fword(bool move)
-{
-	size_t nc;
-	char *cp = xcp;
-
-	if (cp == xep) {
-		x_e_putc2(7);
-		return (0);
-	}
-	while (x_arg--) {
-		while (cp != xep && is_mfs(*cp))
-			cp++;
-		while (cp != xep && !is_mfs(*cp))
-			cp++;
-	}
-	nc = x_nb2nc(cp - xcp);
-	if (move)
-		x_goto(cp);
-	return (nc);
-}
-
-static void
-x_goto(char *cp)
-{
-	cp = cp >= xep ? xep : x_bs0(cp, xbuf);
-	if (cp < xbp || cp >= utf_skipcols(xbp, x_displen)) {
-		/* we are heading off screen */
-		xcp = cp;
-		x_adjust();
-	} else if (cp < xcp) {
-		/* move back */
-		while (cp < xcp)
-			x_bs3(&xcp);
-	} else if (cp > xcp) {
-		/* move forward */
-		while (cp > xcp)
-			x_zotc3(&xcp);
-	}
-}
-
-static char *
-x_bs0(char *cp, char *lower_bound)
-{
-	if (UTFMODE)
-		while ((!lower_bound || (cp > lower_bound)) &&
-		    ((*(unsigned char *)cp & 0xC0) == 0x80))
-			--cp;
-	return (cp);
-}
-
-static void
-x_bs3(char **p)
-{
-	int i;
-
-	*p = x_bs0((*p) - 1, NULL);
-	i = x_size2(*p, NULL);
-	while (i--)
-		x_e_putc2('\b');
-}
-
-static int
-x_size_str(char *cp)
-{
-	int size = 0;
-	while (*cp)
-		size += x_size2(cp, &cp);
-	return (size);
-}
-
-static int
-x_size2(char *cp, char **dcp)
-{
-	int c = *(unsigned char *)cp;
-
-	if (UTFMODE && (c > 0x7F))
-		return (utf_widthadj(cp, (const char **)dcp));
-	if (dcp)
-		*dcp = cp + 1;
-	if (c == '\t')
-		/* Kludge, tabs are always four spaces. */
-		return (4);
-	if (c < ' ' || c == 0x7f)
-		/* control unsigned char */
-		return (2);
-	return (1);
-}
-
-static void
-x_zots(char *str)
-{
-	int adj = x_adj_done;
-
-	x_lastcp();
-	while (*str && str < xlp && adj == x_adj_done)
-		x_zotc3(&str);
-}
-
-static void
-x_zotc2(int c)
-{
-	if (c == '\t') {
-		/* Kludge, tabs are always four spaces. */
-		x_e_puts("    ");
-	} else if (c < ' ' || c == 0x7f) {
-		x_e_putc2('^');
-		x_e_putc2(UNCTRL(c));
-	} else
-		x_e_putc2(c);
-}
-
-static void
-x_zotc3(char **cp)
-{
-	unsigned char c = **(unsigned char **)cp;
-
-	if (c == '\t') {
-		/* Kludge, tabs are always four spaces. */
-		x_e_puts("    ");
-		(*cp)++;
-	} else if (c < ' ' || c == 0x7f) {
-		x_e_putc2('^');
-		x_e_putc2(UNCTRL(c));
-		(*cp)++;
-	} else
-		x_e_putc3((const char **)cp);
-}
-
-static int
-x_mv_back(int c MKSH_A_UNUSED)
-{
-	if (xcp == xbuf) {
-		x_e_putc2(7);
-		return (KSTD);
-	}
-	while (x_arg--) {
-		x_goto(xcp - 1);
-		if (xcp == xbuf)
-			break;
-	}
-	return (KSTD);
-}
-
-static int
-x_mv_forw(int c MKSH_A_UNUSED)
-{
-	char *cp = xcp, *cp2;
-
-	if (xcp == xep) {
-		x_e_putc2(7);
-		return (KSTD);
-	}
-	while (x_arg--) {
-		utf_ptradjx(cp, cp2);
-		if (cp2 > xep)
-			break;
-		cp = cp2;
-	}
-	x_goto(cp);
-	return (KSTD);
-}
-
-static int
-x_search_char_forw(int c MKSH_A_UNUSED)
-{
-	char *cp = xcp;
-	char tmp[4];
-
-	*xep = '\0';
-	if (x_e_getmbc(tmp) < 0) {
-		x_e_putc2(7);
-		return (KSTD);
-	}
-	while (x_arg--) {
-		if ((cp = (cp == xep) ? NULL : strstr(cp + 1, tmp)) == NULL &&
-		    (cp = strstr(xbuf, tmp)) == NULL) {
-			x_e_putc2(7);
-			return (KSTD);
-		}
-	}
-	x_goto(cp);
-	return (KSTD);
-}
-
-static int
-x_search_char_back(int c MKSH_A_UNUSED)
-{
-	char *cp = xcp, *p, tmp[4];
-	bool b;
-
-	if (x_e_getmbc(tmp) < 0) {
-		x_e_putc2(7);
-		return (KSTD);
-	}
-	for (; x_arg--; cp = p)
-		for (p = cp; ; ) {
-			if (p-- == xbuf)
-				p = xep;
-			if (p == cp) {
-				x_e_putc2(7);
-				return (KSTD);
-			}
-			if ((tmp[1] && ((p+1) > xep)) ||
-			    (tmp[2] && ((p+2) > xep)))
-				continue;
-			b = true;
-			if (*p != tmp[0])
-				b = false;
-			if (b && tmp[1] && p[1] != tmp[1])
-				b = false;
-			if (b && tmp[2] && p[2] != tmp[2])
-				b = false;
-			if (b)
-				break;
-		}
-	x_goto(cp);
-	return (KSTD);
-}
-
-static int
-x_newline(int c MKSH_A_UNUSED)
-{
-	x_e_putc2('\r');
-	x_e_putc2('\n');
-	x_flush();
-	*xep++ = '\n';
-	return (KEOL);
-}
-
-static int
-x_end_of_text(int c MKSH_A_UNUSED)
-{
-	x_zotc2(edchars.eof);
-	x_putc('\r');
-	x_putc('\n');
-	x_flush();
-	return (KEOL);
-}
-
-static int
-x_beg_hist(int c MKSH_A_UNUSED)
-{
-	x_load_hist(history);
-	return (KSTD);
-}
-
-static int
-x_end_hist(int c MKSH_A_UNUSED)
-{
-	x_load_hist(histptr);
-	return (KSTD);
-}
-
-static int
-x_prev_com(int c MKSH_A_UNUSED)
-{
-	x_load_hist(x_histp - x_arg);
-	return (KSTD);
-}
-
-static int
-x_next_com(int c MKSH_A_UNUSED)
-{
-	x_load_hist(x_histp + x_arg);
-	return (KSTD);
-}
-
-/*
- * Goto a particular history number obtained from argument.
- * If no argument is given history 1 is probably not what you
- * want so we'll simply go to the oldest one.
- */
-static int
-x_goto_hist(int c MKSH_A_UNUSED)
-{
-	if (x_arg_defaulted)
-		x_load_hist(history);
-	else
-		x_load_hist(histptr + x_arg - source->line);
-	return (KSTD);
-}
-
-static void
-x_load_hist(char **hp)
-{
-	int oldsize;
-	char *sp = NULL;
-
-	if (hp == histptr + 1) {
-		sp = holdbufp;
-		modified = 0;
-	} else if (hp < history || hp > histptr) {
-		x_e_putc2(7);
-		return;
-	}
-	if (sp == NULL)
-		sp = *hp;
-	x_histp = hp;
-	oldsize = x_size_str(xbuf);
-	if (modified)
-		strlcpy(holdbufp, xbuf, LINE);
-	strlcpy(xbuf, sp, xend - xbuf);
-	xbp = xbuf;
-	xep = xcp = xbuf + strlen(xbuf);
-	xlp_valid = false;
-	if (xep <= x_lastcp()) {
-		x_redraw(oldsize);
-	}
-	x_goto(xep);
-	modified = 0;
-}
-
-static int
-x_nl_next_com(int c MKSH_A_UNUSED)
-{
-	if (!x_histncp || (x_histp != x_histncp && x_histp != histptr + 1))
-		/* fresh start of ^O */
-		x_histncp = x_histp;
-	x_nextcmd = source->line - (histptr - x_histncp) + 1;
-	return (x_newline('\n'));
-}
-
-static int
-x_eot_del(int c)
-{
-	if (xep == xbuf && x_arg_defaulted)
-		return (x_end_of_text(c));
-	else
-		return (x_del_char(c));
-}
-
-/* reverse incremental history search */
-static int
-x_search_hist(int c)
-{
-	int offset = -1;	/* offset of match in xbuf, else -1 */
-	char pat[80 + 1];	/* pattern buffer */
-	char *p = pat;
-	unsigned char f;
-
-	*p = '\0';
-	while (/* CONSTCOND */ 1) {
-		if (offset < 0) {
-			x_e_puts("\nI-search: ");
-			x_e_puts(pat);
-		}
-		x_flush();
-		if ((c = x_e_getc()) < 0)
-			return (KSTD);
-		f = x_tab[0][c];
-		if (c == CTRL('[')) {
-			if ((f & 0x7F) == XFUNC_meta1) {
-				if ((c = x_e_getc()) < 0)
-					return (KSTD);
-				f = x_tab[1][c] & 0x7F;
-				if (f == XFUNC_meta1 || f == XFUNC_meta2)
-					x_meta1(CTRL('['));
-				x_e_ungetc(c);
-			}
-			break;
-		}
-#ifndef MKSH_SMALL
-		if (f & 0x80) {
-			f &= 0x7F;
-			if ((c = x_e_getc()) != '~')
-				x_e_ungetc(c);
-		}
-#endif
-		if (f == XFUNC_search_hist)
-			offset = x_search(pat, 0, offset);
-		else if (f == XFUNC_del_back) {
-			if (p == pat) {
-				offset = -1;
-				break;
-			}
-			if (p > pat)
-				*--p = '\0';
-			if (p == pat)
-				offset = -1;
-			else
-				offset = x_search(pat, 1, offset);
-			continue;
-		} else if (f == XFUNC_insert) {
-			/* add char to pattern */
-			/* overflow check... */
-			if ((size_t)(p - pat) >= sizeof(pat) - 1) {
-				x_e_putc2(7);
-				continue;
-			}
-			*p++ = c, *p = '\0';
-			if (offset >= 0) {
-				/* already have partial match */
-				offset = x_match(xbuf, pat);
-				if (offset >= 0) {
-					x_goto(xbuf + offset + (p - pat) -
-					    (*pat == '^'));
-					continue;
-				}
-			}
-			offset = x_search(pat, 0, offset);
-		} else if (f == XFUNC_abort) {
-			if (offset >= 0)
-				x_load_hist(histptr + 1);
-			break;
-		} else {
-			/* other command */
-			x_e_ungetc(c);
-			break;
-		}
-	}
-	if (offset < 0)
-		x_redraw(-1);
-	return (KSTD);
-}
-
-/* search backward from current line */
-static int
-x_search(char *pat, int sameline, int offset)
-{
-	char **hp;
-	int i;
-
-	for (hp = x_histp - (sameline ? 0 : 1); hp >= history; --hp) {
-		i = x_match(*hp, pat);
-		if (i >= 0) {
-			if (offset < 0)
-				x_e_putc2('\n');
-			x_load_hist(hp);
-			x_goto(xbuf + i + strlen(pat) - (*pat == '^'));
-			return (i);
-		}
-	}
-	x_e_putc2(7);
-	x_histp = histptr;
-	return (-1);
-}
-
-#ifndef MKSH_SMALL
-/* anchored search up from current line */
-static int
-x_search_hist_up(int c MKSH_A_UNUSED)
-{
-	return (x_search_dir(-1));
-}
-
-/* anchored search down from current line */
-static int
-x_search_hist_dn(int c MKSH_A_UNUSED)
-{
-	return (x_search_dir(1));
-}
-
-/* anchored search in the indicated direction */
-static int
-x_search_dir(int search_dir /* should've been bool */)
-{
-	char **hp = x_histp + search_dir;
-	size_t curs = xcp - xbuf;
-
-	while (histptr >= hp && hp >= history) {
-		if (strncmp(xbuf, *hp, curs) == 0) {
-			x_load_hist(hp);
-			x_goto(xbuf + curs);
-			break;
-		}
-		hp += search_dir;
-	}
-	return (KSTD);
-}
-#endif
-
-/* return position of first match of pattern in string, else -1 */
-static int
-x_match(char *str, char *pat)
-{
-	if (*pat == '^') {
-		return ((strncmp(str, pat + 1, strlen(pat + 1)) == 0) ? 0 : -1);
-	} else {
-		char *q = strstr(str, pat);
-		return ((q == NULL) ? -1 : q - str);
-	}
-}
-
-static int
-x_del_line(int c MKSH_A_UNUSED)
-{
-	int i, j;
-
-	*xep = 0;
-	i = xep - xbuf;
-	j = x_size_str(xbuf);
-	xcp = xbuf;
-	x_push(i);
-	xlp = xbp = xep = xbuf;
-	xlp_valid = true;
-	*xcp = 0;
-	xmp = NULL;
-	x_redraw(j);
-	x_modified();
-	return (KSTD);
-}
-
-static int
-x_mv_end(int c MKSH_A_UNUSED)
-{
-	x_goto(xep);
-	return (KSTD);
-}
-
-static int
-x_mv_begin(int c MKSH_A_UNUSED)
-{
-	x_goto(xbuf);
-	return (KSTD);
-}
-
-static int
-x_draw_line(int c MKSH_A_UNUSED)
-{
-	x_redraw(-1);
-	return (KSTD);
-}
-
-static int
-x_e_rebuildline(const char *clrstr)
-{
-	shf_puts(clrstr, shl_out);
-	x_adjust();
-	return (KSTD);
-}
-
-static int
-x_cls(int c MKSH_A_UNUSED)
-{
-	return (x_e_rebuildline(MKSH_CLS_STRING));
-}
-
-/*
- * Redraw (part of) the line. If limit is < 0, the everything is redrawn
- * on a NEW line, otherwise limit is the screen column up to which needs
- * redrawing.
- */
-static void
-x_redraw(int limit)
-{
-	int i, j;
-	char *cp;
-
-	x_adj_ok = false;
-	if (limit == -1)
-		x_e_putc2('\n');
-	else
-		x_e_putc2('\r');
-	x_flush();
-	if (xbp == xbuf) {
-		if (prompt_trunc != -1)
-			pprompt(prompt, prompt_trunc);
-		x_col = pwidth;
-	}
-	x_displen = xx_cols - 2 - x_col;
-	xlp_valid = false;
-	x_zots(xbp);
-	if (xbp != xbuf || xep > xlp)
-		limit = xx_cols;
-	if (limit >= 0) {
-		if (xep > xlp)
-			/* we fill the line */
-			i = 0;
-		else {
-			char *cpl = xbp;
-
-			i = limit;
-			while (cpl < xlp)
-				i -= x_size2(cpl, &cpl);
-		}
-
-		j = 0;
-		while ((j < i) || (x_col < (xx_cols - 2))) {
-			if (!(x_col < (xx_cols - 2)))
-				break;
-			x_e_putc2(' ');
-			j++;
-		}
-		i = ' ';
-		if (xep > xlp) {
-			/* more off screen */
-			if (xbp > xbuf)
-				i = '*';
-			else
-				i = '>';
-		} else if (xbp > xbuf)
-			i = '<';
-		x_e_putc2(i);
-		j++;
-		while (j--)
-			x_e_putc2('\b');
-	}
-	cp = xlp;
-	while (cp > xcp)
-		x_bs3(&cp);
-	x_adj_ok = true;
-	return;
-}
-
-static int
-x_transpose(int c MKSH_A_UNUSED)
-{
-	unsigned int tmpa, tmpb;
-
-	/*-
-	 * What transpose is meant to do seems to be up for debate. This
-	 * is a general summary of the options; the text is abcd with the
-	 * upper case character or underscore indicating the cursor position:
-	 *	Who			Before	After	Before	After
-	 *	AT&T ksh in emacs mode:	abCd	abdC	abcd_	(bell)
-	 *	AT&T ksh in gmacs mode:	abCd	baCd	abcd_	abdc_
-	 *	gnu emacs:		abCd	acbD	abcd_	abdc_
-	 * Pdksh currently goes with GNU behavior since I believe this is the
-	 * most common version of emacs, unless in gmacs mode, in which case
-	 * it does the AT&T ksh gmacs mode.
-	 * This should really be broken up into 3 functions so users can bind
-	 * to the one they want.
-	 */
-	if (xcp == xbuf) {
-		x_e_putc2(7);
-		return (KSTD);
-	} else if (xcp == xep || Flag(FGMACS)) {
-		if (xcp - xbuf == 1) {
-			x_e_putc2(7);
-			return (KSTD);
-		}
-		/*
-		 * Gosling/Unipress emacs style: Swap two characters before
-		 * the cursor, do not change cursor position
-		 */
-		x_bs3(&xcp);
-		if (utf_mbtowc(&tmpa, xcp) == (size_t)-1) {
-			x_e_putc2(7);
-			return (KSTD);
-		}
-		x_bs3(&xcp);
-		if (utf_mbtowc(&tmpb, xcp) == (size_t)-1) {
-			x_e_putc2(7);
-			return (KSTD);
-		}
-		utf_wctomb(xcp, tmpa);
-		x_zotc3(&xcp);
-		utf_wctomb(xcp, tmpb);
-		x_zotc3(&xcp);
-	} else {
-		/*
-		 * GNU emacs style: Swap the characters before and under the
-		 * cursor, move cursor position along one.
-		 */
-		if (utf_mbtowc(&tmpa, xcp) == (size_t)-1) {
-			x_e_putc2(7);
-			return (KSTD);
-		}
-		x_bs3(&xcp);
-		if (utf_mbtowc(&tmpb, xcp) == (size_t)-1) {
-			x_e_putc2(7);
-			return (KSTD);
-		}
-		utf_wctomb(xcp, tmpa);
-		x_zotc3(&xcp);
-		utf_wctomb(xcp, tmpb);
-		x_zotc3(&xcp);
-	}
-	x_modified();
-	return (KSTD);
-}
-
-static int
-x_literal(int c MKSH_A_UNUSED)
-{
-	x_curprefix = -1;
-	return (KSTD);
-}
-
-static int
-x_meta1(int c MKSH_A_UNUSED)
-{
-	x_curprefix = 1;
-	return (KSTD);
-}
-
-static int
-x_meta2(int c MKSH_A_UNUSED)
-{
-	x_curprefix = 2;
-	return (KSTD);
-}
-
-static int
-x_kill(int c MKSH_A_UNUSED)
-{
-	size_t col = xcp - xbuf;
-	size_t lastcol = xep - xbuf;
-	size_t ndel, narg;
-
-	if (x_arg_defaulted || (narg = x_arg) > lastcol)
-		narg = lastcol;
-	if (narg < col) {
-		x_goto(xbuf + narg);
-		ndel = col - narg;
-	} else
-		ndel = narg - col;
-	x_delete(x_nb2nc(ndel), true);
-	return (KSTD);
-}
-
-static void
-x_push(int nchars)
-{
-	char *cp;
-
-	mkssert(xcp != NULL);
-	strndupx(cp, xcp, nchars, AEDIT);
-	if (killstack[killsp])
-		afree(killstack[killsp], AEDIT);
-	killstack[killsp] = cp;
-	killsp = (killsp + 1) % KILLSIZE;
-}
-
-static int
-x_yank(int c MKSH_A_UNUSED)
-{
-	if (killsp == 0)
-		killtp = KILLSIZE;
-	else
-		killtp = killsp;
-	killtp--;
-	if (killstack[killtp] == 0) {
-		x_e_puts("\nnothing to yank");
-		x_redraw(-1);
-		return (KSTD);
-	}
-	xmp = xcp;
-	x_ins(killstack[killtp]);
-	return (KSTD);
-}
-
-static int
-x_meta_yank(int c MKSH_A_UNUSED)
-{
-	size_t len;
-
-	if ((x_last_command != XFUNC_yank && x_last_command != XFUNC_meta_yank) ||
-	    killstack[killtp] == 0) {
-		killtp = killsp;
-		x_e_puts("\nyank something first");
-		x_redraw(-1);
-		return (KSTD);
-	}
-	len = strlen(killstack[killtp]);
-	x_goto(xcp - len);
-	x_delete(x_nb2nc(len), false);
-	do {
-		if (killtp == 0)
-			killtp = KILLSIZE - 1;
-		else
-			killtp--;
-	} while (killstack[killtp] == 0);
-	x_ins(killstack[killtp]);
-	return (KSTD);
-}
-
-static int
-x_abort(int c MKSH_A_UNUSED)
-{
-	/* x_zotc(c); */
-	xlp = xep = xcp = xbp = xbuf;
-	xlp_valid = true;
-	*xcp = 0;
-	x_modified();
-	return (KINTR);
-}
-
-static int
-x_error(int c MKSH_A_UNUSED)
-{
-	x_e_putc2(7);
-	return (KSTD);
-}
-
-#ifndef MKSH_SMALL
-/* special VT100 style key sequence hack */
-static int
-x_vt_hack(int c)
-{
-	/* we only support PF2-'1' for now */
-	if (c != (2 << 8 | '1'))
-		return (x_error(c));
-
-	/* what's the next character? */
-	switch ((c = x_e_getc())) {
-	case '~':
-		x_arg = 1;
-		x_arg_defaulted = true;
-		return (x_mv_begin(0));
-	case ';':
-		/* "interesting" sequence detected */
-		break;
-	default:
-		goto unwind_err;
-	}
-
-	/* XXX x_e_ungetc is one-octet only */
-	if ((c = x_e_getc()) != '5' && c != '3')
-		goto unwind_err;
-
-	/*-
-	 * At this point, we have read the following octets so far:
-	 * - ESC+[ or ESC+O or Ctrl-X (Prefix 2)
-	 * - 1 (vt_hack)
-	 * - ;
-	 * - 5 (Ctrl key combiner) or 3 (Alt key combiner)
-	 * We can now accept one more octet designating the key.
-	 */
-
-	switch ((c = x_e_getc())) {
-	case 'C':
-		return (x_mv_fword(c));
-	case 'D':
-		return (x_mv_bword(c));
-	}
-
- unwind_err:
-	x_e_ungetc(c);
-	return (x_error(c));
-}
-#endif
-
-static char *
-x_mapin(const char *cp, Area *ap)
-{
-	char *news, *op;
-
-	strdupx(news, cp, ap);
-	op = news;
-	while (*cp) {
-		/* XXX -- should handle \^ escape? */
-		if (*cp == '^') {
-			cp++;
-			if (*cp >= '?')
-				/* includes '?'; ASCII */
-				*op++ = CTRL(*cp);
-			else {
-				*op++ = '^';
-				cp--;
-			}
-		} else
-			*op++ = *cp;
-		cp++;
-	}
-	*op = '\0';
-
-	return (news);
-}
-
-static void
-x_mapout2(int c, char **buf)
-{
-	char *p = *buf;
-
-	if (c < ' ' || c == 0x7f) {
-		*p++ = '^';
-		*p++ = UNCTRL(c);
-	} else
-		*p++ = c;
-	*p = 0;
-	*buf = p;
-}
-
-static char *
-x_mapout(int c)
-{
-	static char buf[8];
-	char *bp = buf;
-
-	x_mapout2(c, &bp);
-	return (buf);
-}
-
-static void
-x_print(int prefix, int key)
-{
-	int f = x_tab[prefix][key];
-
-	if (prefix)
-		/* prefix == 1 || prefix == 2 */
-		shf_puts(x_mapout(prefix == 1 ?
-		    CTRL('[') : CTRL('X')), shl_stdout);
-#ifdef MKSH_SMALL
-	shprintf("%s = ", x_mapout(key));
-#else
-	shprintf("%s%s = ", x_mapout(key), (f & 0x80) ? "~" : "");
-	if (XFUNC_VALUE(f) != XFUNC_ins_string)
-#endif
-		shprintf("%s\n", x_ftab[XFUNC_VALUE(f)].xf_name);
-#ifndef MKSH_SMALL
-	else
-		shprintf("'%s'\n", x_atab[prefix][key]);
-#endif
-}
-
-int
-x_bind(const char *a1, const char *a2,
-#ifndef MKSH_SMALL
-    /* bind -m */
-    bool macro,
-#endif
-    /* bind -l */
-    bool list)
-{
-	unsigned char f;
-	int prefix, key;
-	char *m1, *m2;
-#ifndef MKSH_SMALL
-	char *sp = NULL;
-	bool hastilde;
-#endif
-
-	if (x_tab == NULL) {
-		bi_errorf("can't bind, not a tty");
-		return (1);
-	}
-	/* List function names */
-	if (list) {
-		for (f = 0; f < NELEM(x_ftab); f++)
-			if (x_ftab[f].xf_name &&
-			    !(x_ftab[f].xf_flags & XF_NOBIND))
-				shprintf("%s\n", x_ftab[f].xf_name);
-		return (0);
-	}
-	if (a1 == NULL) {
-		for (prefix = 0; prefix < X_NTABS; prefix++)
-			for (key = 0; key < X_TABSZ; key++) {
-				f = XFUNC_VALUE(x_tab[prefix][key]);
-				if (f == XFUNC_insert || f == XFUNC_error
-#ifndef MKSH_SMALL
-				    || (macro && f != XFUNC_ins_string)
-#endif
-				    )
-					continue;
-				x_print(prefix, key);
-			}
-		return (0);
-	}
-	m2 = m1 = x_mapin(a1, ATEMP);
-	prefix = 0;
-	for (;; m1++) {
-		key = (unsigned char)*m1;
-		f = XFUNC_VALUE(x_tab[prefix][key]);
-		if (f == XFUNC_meta1)
-			prefix = 1;
-		else if (f == XFUNC_meta2)
-			prefix = 2;
-		else
-			break;
-	}
-	if (*++m1
-#ifndef MKSH_SMALL
-	    && ((*m1 != '~') || *(m1 + 1))
-#endif
-	    ) {
-		char msg[256];
-		const char *c = a1;
-		m1 = msg;
-		while (*c && (size_t)(m1 - msg) < sizeof(msg) - 3)
-			x_mapout2(*c++, &m1);
-		bi_errorf("%s: %s", "too long key sequence", msg);
-		return (1);
-	}
-#ifndef MKSH_SMALL
-	hastilde = tobool(*m1);
-#endif
-	afree(m2, ATEMP);
-
-	if (a2 == NULL) {
-		x_print(prefix, key);
-		return (0);
-	}
-	if (*a2 == 0) {
-		f = XFUNC_insert;
-#ifndef MKSH_SMALL
-	} else if (macro) {
-		f = XFUNC_ins_string;
-		sp = x_mapin(a2, AEDIT);
-#endif
-	} else {
-		for (f = 0; f < NELEM(x_ftab); f++)
-			if (x_ftab[f].xf_name &&
-			    strcmp(x_ftab[f].xf_name, a2) == 0)
-				break;
-		if (f == NELEM(x_ftab) || x_ftab[f].xf_flags & XF_NOBIND) {
-			bi_errorf("%s: %s %s", a2, "no such", Tfunction);
-			return (1);
-		}
-	}
-
-#ifndef MKSH_SMALL
-	if (XFUNC_VALUE(x_tab[prefix][key]) == XFUNC_ins_string &&
-	    x_atab[prefix][key])
-		afree(x_atab[prefix][key], AEDIT);
-#endif
-	x_tab[prefix][key] = f
-#ifndef MKSH_SMALL
-	    | (hastilde ? 0x80 : 0)
-#endif
-	    ;
-#ifndef MKSH_SMALL
-	x_atab[prefix][key] = sp;
-#endif
-
-	/* Track what the user has bound so x_mode(true) won't toast things */
-	if (f == XFUNC_insert)
-		x_bound[(prefix * X_TABSZ + key) / 8] &=
-		    ~(1 << ((prefix * X_TABSZ + key) % 8));
-	else
-		x_bound[(prefix * X_TABSZ + key) / 8] |=
-		    (1 << ((prefix * X_TABSZ + key) % 8));
-
-	return (0);
-}
-
-static void
-bind_if_not_bound(int p, int k, int func)
-{
-	int t;
-
-	/*
-	 * Has user already bound this key?
-	 * If so, do not override it.
-	 */
-	t = p * X_TABSZ + k;
-	if (x_bound[t >> 3] & (1 << (t & 7)))
-		return;
-
-	x_tab[p][k] = func;
-}
-
-static int
-x_set_mark(int c MKSH_A_UNUSED)
-{
-	xmp = xcp;
-	return (KSTD);
-}
-
-static int
-x_kill_region(int c MKSH_A_UNUSED)
-{
-	size_t rsize;
-	char *xr;
-
-	if (xmp == NULL) {
-		x_e_putc2(7);
-		return (KSTD);
-	}
-	if (xmp > xcp) {
-		rsize = xmp - xcp;
-		xr = xcp;
-	} else {
-		rsize = xcp - xmp;
-		xr = xmp;
-	}
-	x_goto(xr);
-	x_delete(x_nb2nc(rsize), true);
-	xmp = xr;
-	return (KSTD);
-}
-
-static int
-x_xchg_point_mark(int c MKSH_A_UNUSED)
-{
-	char *tmp;
-
-	if (xmp == NULL) {
-		x_e_putc2(7);
-		return (KSTD);
-	}
-	tmp = xmp;
-	xmp = xcp;
-	x_goto(tmp);
-	return (KSTD);
-}
-
-static int
-x_noop(int c MKSH_A_UNUSED)
-{
-	return (KSTD);
-}
-
-/*
- *	File/command name completion routines
- */
-static int
-x_comp_comm(int c MKSH_A_UNUSED)
-{
-	do_complete(XCF_COMMAND, CT_COMPLETE);
-	return (KSTD);
-}
-
-static int
-x_list_comm(int c MKSH_A_UNUSED)
-{
-	do_complete(XCF_COMMAND, CT_LIST);
-	return (KSTD);
-}
-
-static int
-x_complete(int c MKSH_A_UNUSED)
-{
-	do_complete(XCF_COMMAND_FILE, CT_COMPLETE);
-	return (KSTD);
-}
-
-static int
-x_enumerate(int c MKSH_A_UNUSED)
-{
-	do_complete(XCF_COMMAND_FILE, CT_LIST);
-	return (KSTD);
-}
-
-static int
-x_comp_file(int c MKSH_A_UNUSED)
-{
-	do_complete(XCF_FILE, CT_COMPLETE);
-	return (KSTD);
-}
-
-static int
-x_list_file(int c MKSH_A_UNUSED)
-{
-	do_complete(XCF_FILE, CT_LIST);
-	return (KSTD);
-}
-
-static int
-x_comp_list(int c MKSH_A_UNUSED)
-{
-	do_complete(XCF_COMMAND_FILE, CT_COMPLIST);
-	return (KSTD);
-}
-
-static int
-x_expand(int c MKSH_A_UNUSED)
-{
-	char **words;
-	int start, end, nwords, i;
-
-	i = XCF_FILE;
-	nwords = x_cf_glob(&i, xbuf, xep - xbuf, xcp - xbuf,
-	    &start, &end, &words);
-
-	if (nwords == 0) {
-		x_e_putc2(7);
-		return (KSTD);
-	}
-	x_goto(xbuf + start);
-	x_delete(x_nb2nc(end - start), false);
-
-	i = 0;
-	while (i < nwords) {
-		if (x_escape(words[i], strlen(words[i]), x_do_ins) < 0 ||
-		    (++i < nwords && x_ins(" ") < 0)) {
-			x_e_putc2(7);
-			return (KSTD);
-		}
-	}
-	x_adjust();
-
-	return (KSTD);
-}
-
-static void
-do_complete(
-    /* XCF_{COMMAND,FILE,COMMAND_FILE} */
-    int flags,
-    /* 0 for list, 1 for complete and 2 for complete-list */
-    Comp_type type)
-{
-	char **words;
-	int start, end, nlen, olen, nwords;
-	bool completed;
-
-	nwords = x_cf_glob(&flags, xbuf, xep - xbuf, xcp - xbuf,
-	    &start, &end, &words);
-	/* no match */
-	if (nwords == 0) {
-		x_e_putc2(7);
-		return;
-	}
-	if (type == CT_LIST) {
-		x_print_expansions(nwords, words,
-		    tobool(flags & XCF_IS_COMMAND));
-		x_redraw(0);
-		x_free_words(nwords, words);
-		return;
-	}
-	olen = end - start;
-	nlen = x_longest_prefix(nwords, words);
-	if (nwords == 1) {
-		/*
-		 * always complete single matches;
-		 * any expansion of parameter substitution
-		 * is always at most one result, too
-		 */
-		completed = true;
-	} else {
-		char *unescaped;
-
-		/* make a copy of the original string part */
-		strndupx(unescaped, xbuf + start, olen, ATEMP);
-
-		/* expand any tilde and unescape the string for comparison */
-		unescaped = x_glob_hlp_tilde_and_rem_qchar(unescaped, true);
-
-		/*
-		 * match iff entire original string is part of the
-		 * longest prefix, implying the latter is at least
-		 * the same size (after unescaping)
-		 */
-		completed = !strncmp(words[0], unescaped, strlen(unescaped));
-
-		afree(unescaped, ATEMP);
-	}
-	if (type == CT_COMPLIST && nwords > 1) {
-		/*
-		 * print expansions, since we didn't get back
-		 * just a single match
-		 */
-		x_print_expansions(nwords, words,
-		    tobool(flags & XCF_IS_COMMAND));
-	}
-	if (completed) {
-		/* expand on the command line */
-		xmp = NULL;
-		xcp = xbuf + start;
-		xep -= olen;
-		memmove(xcp, xcp + olen, xep - xcp + 1);
-		x_escape(words[0], nlen, x_do_ins);
-	}
-	x_adjust();
-	/*
-	 * append a space if this is a single non-directory match
-	 * and not a parameter or homedir substitution
-	 */
-	if (nwords == 1 && words[0][nlen - 1] != '/' &&
-	    !(flags & XCF_IS_NOSPACE)) {
-		x_ins(" ");
-	}
-
-	x_free_words(nwords, words);
-}
-
-/*-
- * NAME:
- *	x_adjust - redraw the line adjusting starting point etc.
- *
- * DESCRIPTION:
- *	This function is called when we have exceeded the bounds
- *	of the edit window. It increments x_adj_done so that
- *	functions like x_ins and x_delete know that we have been
- *	called and can skip the x_bs() stuff which has already
- *	been done by x_redraw.
- *
- * RETURN VALUE:
- *	None
- */
-static void
-x_adjust(void)
-{
-	int col_left, n;
-
-	/* flag the fact that we were called */
-	x_adj_done++;
-
-	/*
-	 * calculate the amount of columns we need to "go back"
-	 * from xcp to set xbp to (but never < xbuf) to 2/3 of
-	 * the display width; take care of pwidth though
-	 */
-	if ((col_left = xx_cols * 2 / 3) < MIN_EDIT_SPACE) {
-		/*
-		 * cowardly refuse to do anything
-		 * if the available space is too small;
-		 * fall back to dumb pdksh code
-		 */
-		if ((xbp = xcp - (x_displen / 2)) < xbuf)
-			xbp = xbuf;
-		/* elide UTF-8 fixup as penalty */
-		goto x_adjust_out;
-	}
-
-	/* fix up xbp to just past a character end first */
-	xbp = xcp >= xep ? xep : x_bs0(xcp, xbuf);
-	/* walk backwards */
-	while (xbp > xbuf && col_left > 0) {
-		xbp = x_bs0(xbp - 1, xbuf);
-		col_left -= (n = x_size2(xbp, NULL));
-	}
-	/* check if we hit the prompt */
-	if (xbp == xbuf && xcp != xbuf && col_left >= 0 && col_left < pwidth) {
-		/* so we did; force scrolling occurs */
-		xbp += utf_ptradj(xbp);
-	}
-
- x_adjust_out:
-	xlp_valid = false;
-	x_redraw(xx_cols);
-	x_flush();
-}
-
-static void
-x_e_ungetc(int c)
-{
-	unget_char = c < 0 ? -1 : (c & 255);
-}
-
-static int
-x_e_getc(void)
-{
-	int c;
-
-	if (unget_char >= 0) {
-		c = unget_char;
-		unget_char = -1;
-		return (c);
-	}
-
-#ifndef MKSH_SMALL
-	if (macroptr) {
-		if ((c = (unsigned char)*macroptr++))
-			return (c);
-		macroptr = NULL;
-	}
-#endif
-
-	return (x_getc());
-}
-
-static void
-x_e_putc2(int c)
-{
-	int width = 1;
-
-	if (c == '\r' || c == '\n')
-		x_col = 0;
-	if (x_col < xx_cols) {
-		if (UTFMODE && (c > 0x7F)) {
-			char utf_tmp[3];
-			size_t x;
-
-			if (c < 0xA0)
-				c = 0xFFFD;
-			x = utf_wctomb(utf_tmp, c);
-			x_putc(utf_tmp[0]);
-			if (x > 1)
-				x_putc(utf_tmp[1]);
-			if (x > 2)
-				x_putc(utf_tmp[2]);
-			width = utf_wcwidth(c);
-		} else
-			x_putc(c);
-		switch (c) {
-		case 7:
-			break;
-		case '\r':
-		case '\n':
-			break;
-		case '\b':
-			x_col--;
-			break;
-		default:
-			x_col += width;
-			break;
-		}
-	}
-	if (x_adj_ok && (x_col < 0 || x_col >= (xx_cols - 2)))
-		x_adjust();
-}
-
-static void
-x_e_putc3(const char **cp)
-{
-	int width = 1, c = **(const unsigned char **)cp;
-
-	if (c == '\r' || c == '\n')
-		x_col = 0;
-	if (x_col < xx_cols) {
-		if (UTFMODE && (c > 0x7F)) {
-			char *cp2;
-
-			width = utf_widthadj(*cp, (const char **)&cp2);
-			while (*cp < cp2)
-				x_putcf(*(*cp)++);
-		} else {
-			(*cp)++;
-			x_putc(c);
-		}
-		switch (c) {
-		case 7:
-			break;
-		case '\r':
-		case '\n':
-			break;
-		case '\b':
-			x_col--;
-			break;
-		default:
-			x_col += width;
-			break;
-		}
-	}
-	if (x_adj_ok && (x_col < 0 || x_col >= (xx_cols - 2)))
-		x_adjust();
-}
-
-static void
-x_e_puts(const char *s)
-{
-	int adj = x_adj_done;
-
-	while (*s && adj == x_adj_done)
-		x_e_putc3(&s);
-}
-
-/*-
- * NAME:
- *	x_set_arg - set an arg value for next function
- *
- * DESCRIPTION:
- *	This is a simple implementation of M-[0-9].
- *
- * RETURN VALUE:
- *	KSTD
- */
-static int
-x_set_arg(int c)
-{
-	unsigned int n = 0;
-	bool first = true;
-
-	/* strip command prefix */
-	c &= 255;
-	while (c >= 0 && ksh_isdigit(c)) {
-		n = n * 10 + (c - '0');
-		if (n > LINE)
-			/* upper bound for repeat */
-			goto x_set_arg_too_big;
-		c = x_e_getc();
-		first = false;
-	}
-	if (c < 0 || first) {
- x_set_arg_too_big:
-		x_e_putc2(7);
-		x_arg = 1;
-		x_arg_defaulted = true;
-	} else {
-		x_e_ungetc(c);
-		x_arg = n;
-		x_arg_defaulted = false;
-	}
-	return (KSTD);
-}
-
-/* Comment or uncomment the current line. */
-static int
-x_comment(int c MKSH_A_UNUSED)
-{
-	int oldsize = x_size_str(xbuf);
-	ssize_t len = xep - xbuf;
-	int ret = x_do_comment(xbuf, xend - xbuf, &len);
-
-	if (ret < 0)
-		x_e_putc2(7);
-	else {
-		x_modified();
-		xep = xbuf + len;
-		*xep = '\0';
-		xcp = xbp = xbuf;
-		x_redraw(oldsize);
-		if (ret > 0)
-			return (x_newline('\n'));
-	}
-	return (KSTD);
-}
-
-static int
-x_version(int c MKSH_A_UNUSED)
-{
-	char *o_xbuf = xbuf, *o_xend = xend;
-	char *o_xbp = xbp, *o_xep = xep, *o_xcp = xcp;
-	int lim = x_lastcp() - xbp;
-	size_t vlen;
-	char *v;
-
-	strdupx(v, KSH_VERSION, ATEMP);
-
-	xbuf = xbp = xcp = v;
-	xend = xep = v + (vlen = strlen(v));
-	x_redraw(lim);
-	x_flush();
-
-	c = x_e_getc();
-	xbuf = o_xbuf;
-	xend = o_xend;
-	xbp = o_xbp;
-	xep = o_xep;
-	xcp = o_xcp;
-	x_redraw((int)vlen);
-
-	if (c < 0)
-		return (KSTD);
-	/* This is what AT&T ksh seems to do... Very bizarre */
-	if (c != ' ')
-		x_e_ungetc(c);
-
-	afree(v, ATEMP);
-	return (KSTD);
-}
-
-#ifndef MKSH_SMALL
-static int
-x_edit_line(int c MKSH_A_UNUSED)
-{
-	if (x_arg_defaulted) {
-		if (xep == xbuf) {
-			x_e_putc2(7);
-			return (KSTD);
-		}
-		if (modified) {
-			*xep = '\0';
-			histsave(&source->line, xbuf, true, true);
-			x_arg = 0;
-		} else
-			x_arg = source->line - (histptr - x_histp);
-	}
-	if (x_arg)
-		shf_snprintf(xbuf, xend - xbuf, "%s %d",
-		    "fc -e ${VISUAL:-${EDITOR:-vi}} --", x_arg);
-	else
-		strlcpy(xbuf, "fc -e ${VISUAL:-${EDITOR:-vi}} --", xend - xbuf);
-	xep = xbuf + strlen(xbuf);
-	return (x_newline('\n'));
-}
-#endif
-
-/*-
- * NAME:
- *	x_prev_histword - recover word from prev command
- *
- * DESCRIPTION:
- *	This function recovers the last word from the previous
- *	command and inserts it into the current edit line. If a
- *	numeric arg is supplied then the n'th word from the
- *	start of the previous command is used.
- *	As a side effect, trashes the mark in order to achieve
- *	being called in a repeatable fashion.
- *
- *	Bound to M-.
- *
- * RETURN VALUE:
- *	KSTD
- */
-static int
-x_prev_histword(int c MKSH_A_UNUSED)
-{
-	char *rcp, *cp;
-	char **xhp;
-	int m = 1;
-	/* -1 = defaulted; 0+ = argument */
-	static int last_arg = -1;
-
-	if (x_last_command == XFUNC_prev_histword) {
-		if (xmp && modified > 1)
-			x_kill_region(0);
-		if (modified)
-			m = modified;
-	} else
-		last_arg = x_arg_defaulted ? -1 : x_arg;
-	xhp = histptr - (m - 1);
-	if ((xhp < history) || !(cp = *xhp)) {
-		x_e_putc2(7);
-		x_modified();
-		return (KSTD);
-	}
-	x_set_mark(0);
-	if ((x_arg = last_arg) == -1) {
-		/* x_arg_defaulted */
-
-		rcp = &cp[strlen(cp) - 1];
-		/*
-		 * ignore white-space after the last word
-		 */
-		while (rcp > cp && is_cfs(*rcp))
-			rcp--;
-		while (rcp > cp && !is_cfs(*rcp))
-			rcp--;
-		if (is_cfs(*rcp))
-			rcp++;
-		x_ins(rcp);
-	} else {
-		/* not x_arg_defaulted */
-		char ch;
-
-		rcp = cp;
-		/*
-		 * ignore white-space at start of line
-		 */
-		while (*rcp && is_cfs(*rcp))
-			rcp++;
-		while (x_arg-- > 0) {
-			while (*rcp && !is_cfs(*rcp))
-				rcp++;
-			while (*rcp && is_cfs(*rcp))
-				rcp++;
-		}
-		cp = rcp;
-		while (*rcp && !is_cfs(*rcp))
-			rcp++;
-		ch = *rcp;
-		*rcp = '\0';
-		x_ins(cp);
-		*rcp = ch;
-	}
-	modified = m + 1;
-	return (KSTD);
-}
-
-#ifndef MKSH_SMALL
-/* Uppercase N(1) words */
-static int
-x_fold_upper(int c MKSH_A_UNUSED)
-{
-	return (x_fold_case('U'));
-}
-
-/* Lowercase N(1) words */
-static int
-x_fold_lower(int c MKSH_A_UNUSED)
-{
-	return (x_fold_case('L'));
-}
-
-/* Titlecase N(1) words */
-static int
-x_fold_capitalise(int c MKSH_A_UNUSED)
-{
-	return (x_fold_case('C'));
-}
-
-/*-
- * NAME:
- *	x_fold_case - convert word to UPPER/lower/Capital case
- *
- * DESCRIPTION:
- *	This function is used to implement M-U/M-u, M-L/M-l, M-C/M-c
- *	to UPPER CASE, lower case or Capitalise Words.
- *
- * RETURN VALUE:
- *	None
- */
-static int
-x_fold_case(int c)
-{
-	char *cp = xcp;
-
-	if (cp == xep) {
-		x_e_putc2(7);
-		return (KSTD);
-	}
-	while (x_arg--) {
-		/*
-		 * first skip over any white-space
-		 */
-		while (cp != xep && is_mfs(*cp))
-			cp++;
-		/*
-		 * do the first char on its own since it may be
-		 * a different action than for the rest.
-		 */
-		if (cp != xep) {
-			if (c == 'L')
-				/* lowercase */
-				*cp = ksh_tolower(*cp);
-			else
-				/* uppercase, capitalise */
-				*cp = ksh_toupper(*cp);
-			cp++;
-		}
-		/*
-		 * now for the rest of the word
-		 */
-		while (cp != xep && !is_mfs(*cp)) {
-			if (c == 'U')
-				/* uppercase */
-				*cp = ksh_toupper(*cp);
-			else
-				/* lowercase, capitalise */
-				*cp = ksh_tolower(*cp);
-			cp++;
-		}
-	}
-	x_goto(cp);
-	x_modified();
-	return (KSTD);
-}
-#endif
-
-/*-
- * NAME:
- *	x_lastcp - last visible char
- *
- * SYNOPSIS:
- *	x_lastcp()
- *
- * DESCRIPTION:
- *	This function returns a pointer to that char in the
- *	edit buffer that will be the last displayed on the
- *	screen. The sequence:
- *
- *	cp = x_lastcp();
- *	while (cp > xcp)
- *		x_bs3(&cp);
- *
- *	Will position the cursor correctly on the screen.
- *
- * RETURN VALUE:
- *	cp or NULL
- */
-static char *
-x_lastcp(void)
-{
-	if (!xlp_valid) {
-		int i = 0, j;
-		char *xlp2;
-
-		xlp = xbp;
-		while (xlp < xep) {
-			j = x_size2(xlp, &xlp2);
-			if ((i + j) > x_displen)
-				break;
-			i += j;
-			xlp = xlp2;
-		}
-	}
-	xlp_valid = true;
-	return (xlp);
-}
-
-static void
-x_mode(bool onoff)
-{
-	static bool x_cur_mode;
-
-	if (x_cur_mode == onoff)
-		return;
-	x_cur_mode = onoff;
-
-	if (onoff) {
-		x_mkraw(tty_fd, NULL, false);
-
-		edchars.erase = tty_state.c_cc[VERASE];
-		edchars.kill = tty_state.c_cc[VKILL];
-		edchars.intr = tty_state.c_cc[VINTR];
-		edchars.quit = tty_state.c_cc[VQUIT];
-		edchars.eof = tty_state.c_cc[VEOF];
-#ifdef VWERASE
-		edchars.werase = tty_state.c_cc[VWERASE];
-#endif
-
-#ifdef _POSIX_VDISABLE
-		/* Convert unset values to internal 'unset' value */
-		if (edchars.erase == _POSIX_VDISABLE)
-			edchars.erase = -1;
-		if (edchars.kill == _POSIX_VDISABLE)
-			edchars.kill = -1;
-		if (edchars.intr == _POSIX_VDISABLE)
-			edchars.intr = -1;
-		if (edchars.quit == _POSIX_VDISABLE)
-			edchars.quit = -1;
-		if (edchars.eof == _POSIX_VDISABLE)
-			edchars.eof = -1;
-		if (edchars.werase == _POSIX_VDISABLE)
-			edchars.werase = -1;
-#endif
-
-		if (edchars.erase >= 0) {
-			bind_if_not_bound(0, edchars.erase, XFUNC_del_back);
-			bind_if_not_bound(1, edchars.erase, XFUNC_del_bword);
-		}
-		if (edchars.kill >= 0)
-			bind_if_not_bound(0, edchars.kill, XFUNC_del_line);
-		if (edchars.werase >= 0)
-			bind_if_not_bound(0, edchars.werase, XFUNC_del_bword);
-		if (edchars.intr >= 0)
-			bind_if_not_bound(0, edchars.intr, XFUNC_abort);
-		if (edchars.quit >= 0)
-			bind_if_not_bound(0, edchars.quit, XFUNC_noop);
-	} else
-		mksh_tcset(tty_fd, &tty_state);
-}
-
-#if !MKSH_S_NOVI
-/* +++ vi editing mode +++ */
-
-#define Ctrl(c)		(c&0x1f)
-
-struct edstate {
-	char *cbuf;
-	ssize_t winleft;
-	ssize_t cbufsize;
-	ssize_t linelen;
-	ssize_t cursor;
-};
-
-static int vi_hook(int);
-static int nextstate(int);
-static int vi_insert(int);
-static int vi_cmd(int, const char *);
-static int domove(int, const char *, int);
-static int redo_insert(int);
-static void yank_range(int, int);
-static int bracktype(int);
-static void save_cbuf(void);
-static void restore_cbuf(void);
-static int putbuf(const char *, ssize_t, bool);
-static void del_range(int, int);
-static int findch(int, int, bool, bool);
-static int forwword(int);
-static int backword(int);
-static int endword(int);
-static int Forwword(int);
-static int Backword(int);
-static int Endword(int);
-static int grabhist(int, int);
-static int grabsearch(int, int, int, const char *);
-static void redraw_line(bool);
-static void refresh(int);
-static int outofwin(void);
-static void rewindow(void);
-static int newcol(int, int);
-static void display(char *, char *, int);
-static void ed_mov_opt(int, char *);
-static int expand_word(int);
-static int complete_word(int, int);
-static int print_expansions(struct edstate *, int);
-#define char_len(c)	((c) < ' ' || (c) == 0x7F ? 2 : 1)
-static void x_vi_zotc(int);
-static void vi_error(void);
-static void vi_macro_reset(void);
-static int x_vi_putbuf(const char *, size_t);
-
-#define vC	0x01		/* a valid command that isn't a vM, vE, vU */
-#define vM	0x02		/* movement command (h, l, etc.) */
-#define vE	0x04		/* extended command (c, d, y) */
-#define vX	0x08		/* long command (@, f, F, t, T, etc.) */
-#define vU	0x10		/* an UN-undoable command (that isn't a vM) */
-#define vB	0x20		/* bad command (^@) */
-#define vZ	0x40		/* repeat count defaults to 0 (not 1) */
-#define vS	0x80		/* search (/, ?) */
-
-#define is_bad(c)	(classify[(c)&0x7f]&vB)
-#define is_cmd(c)	(classify[(c)&0x7f]&(vM|vE|vC|vU))
-#define is_move(c)	(classify[(c)&0x7f]&vM)
-#define is_extend(c)	(classify[(c)&0x7f]&vE)
-#define is_long(c)	(classify[(c)&0x7f]&vX)
-#define is_undoable(c)	(!(classify[(c)&0x7f]&vU))
-#define is_srch(c)	(classify[(c)&0x7f]&vS)
-#define is_zerocount(c)	(classify[(c)&0x7f]&vZ)
-
-static const unsigned char classify[128] = {
-/*	 0	1	2	3	4	5	6	7	*/
-/* 0	^@	^A	^B	^C	^D	^E	^F	^G	*/
-	vB,	0,	0,	0,	0,	vC|vU,	vC|vZ,	0,
-/* 1	^H	^I	^J	^K	^L	^M	^N	^O	*/
-	vM,	vC|vZ,	0,	0,	vC|vU,	0,	vC,	0,
-/* 2	^P	^Q	^R	^S	^T	^U	^V	^W	*/
-	vC,	0,	vC|vU,	0,	0,	0,	vC,	0,
-/* 3	^X	^Y	^Z	^[	^\	^]	^^	^_	*/
-	vC,	0,	0,	vC|vZ,	0,	0,	0,	0,
-/* 4	<space>	!	"	#	$	%	&	'	*/
-	vM,	0,	0,	vC,	vM,	vM,	0,	0,
-/* 5	(	)	*	+	,	-	.	/	*/
-	0,	0,	vC,	vC,	vM,	vC,	0,	vC|vS,
-/* 6	0	1	2	3	4	5	6	7	*/
-	vM,	0,	0,	0,	0,	0,	0,	0,
-/* 7	8	9	:	;	<	=	>	?	*/
-	0,	0,	0,	vM,	0,	vC,	0,	vC|vS,
-/* 8	@	A	B	C	D	E	F	G	*/
-	vC|vX,	vC,	vM,	vC,	vC,	vM,	vM|vX,	vC|vU|vZ,
-/* 9	H	I	J	K	L	M	N	O	*/
-	0,	vC,	0,	0,	0,	0,	vC|vU,	vU,
-/* A	P	Q	R	S	T	U	V	W	*/
-	vC,	0,	vC,	vC,	vM|vX,	vC,	0,	vM,
-/* B	X	Y	Z	[	\	]	^	_	*/
-	vC,	vC|vU,	0,	vU,	vC|vZ,	0,	vM,	vC|vZ,
-/* C	`	a	b	c	d	e	f	g	*/
-	0,	vC,	vM,	vE,	vE,	vM,	vM|vX,	vC|vZ,
-/* D	h	i	j	k	l	m	n	o	*/
-	vM,	vC,	vC|vU,	vC|vU,	vM,	0,	vC|vU,	0,
-/* E	p	q	r	s	t	u	v	w	*/
-	vC,	0,	vX,	vC,	vM|vX,	vC|vU,	vC|vU|vZ, vM,
-/* F	x	y	z	{	|	}	~	^?	*/
-	vC,	vE|vU,	0,	0,	vM|vZ,	0,	vC,	0
-};
-
-#define MAXVICMD	3
-#define SRCHLEN		40
-
-#define INSERT		1
-#define REPLACE		2
-
-#define VNORMAL		0		/* command, insert or replace mode */
-#define VARG1		1		/* digit prefix (first, eg, 5l) */
-#define VEXTCMD		2		/* cmd + movement (eg, cl) */
-#define VARG2		3		/* digit prefix (second, eg, 2c3l) */
-#define VXCH		4		/* f, F, t, T, @ */
-#define VFAIL		5		/* bad command */
-#define VCMD		6		/* single char command (eg, X) */
-#define VREDO		7		/* . */
-#define VLIT		8		/* ^V */
-#define VSEARCH		9		/* /, ? */
-#define VVERSION	10		/* <ESC> ^V */
-#define VPREFIX2	11		/* ^[[ and ^[O in insert mode */
-
-static struct edstate	*save_edstate(struct edstate *old);
-static void		restore_edstate(struct edstate *old, struct edstate *news);
-static void		free_edstate(struct edstate *old);
-
-static struct edstate	ebuf;
-static struct edstate	undobuf;
-
-static struct edstate	*es;		/* current editor state */
-static struct edstate	*undo;
-
-static char *ibuf;			/* input buffer */
-static bool first_insert;		/* set when starting in insert mode */
-static int saved_inslen;		/* saved inslen for first insert */
-static int inslen;			/* length of input buffer */
-static int srchlen;			/* length of current search pattern */
-static char *ybuf;			/* yank buffer */
-static int yanklen;			/* length of yank buffer */
-static int fsavecmd = ' ';		/* last find command */
-static int fsavech;			/* character to find */
-static char lastcmd[MAXVICMD];		/* last non-move command */
-static int lastac;			/* argcnt for lastcmd */
-static int lastsearch = ' ';		/* last search command */
-static char srchpat[SRCHLEN];		/* last search pattern */
-static int insert;			/* <>0 in insert mode */
-static int hnum;			/* position in history */
-static int ohnum;			/* history line copied (after mod) */
-static int hlast;			/* 1 past last position in history */
-static int state;
-
-/*
- * Information for keeping track of macros that are being expanded.
- * The format of buf is the alias contents followed by a NUL byte followed
- * by the name (letter) of the alias. The end of the buffer is marked by
- * a double NUL. The name of the alias is stored so recursive macros can
- * be detected.
- */
-struct macro_state {
-	unsigned char *p;	/* current position in buf */
-	unsigned char *buf;	/* pointer to macro(s) being expanded */
-	size_t len;		/* how much data in buffer */
-};
-static struct macro_state macro;
-
-/* last input was expanded */
-static enum expand_mode {
-	NONE = 0, EXPAND, COMPLETE, PRINT
-} expanded;
-
-static int
-x_vi(char *buf)
-{
-	int c;
-
-	state = VNORMAL;
-	ohnum = hnum = hlast = histnum(-1) + 1;
-	insert = INSERT;
-	saved_inslen = inslen;
-	first_insert = true;
-	inslen = 0;
-	vi_macro_reset();
-
-	ebuf.cbuf = buf;
-	if (undobuf.cbuf == NULL) {
-		ibuf = alloc(LINE, AEDIT);
-		ybuf = alloc(LINE, AEDIT);
-		undobuf.cbuf = alloc(LINE, AEDIT);
-	}
-	undobuf.cbufsize = ebuf.cbufsize = LINE;
-	undobuf.linelen = ebuf.linelen = 0;
-	undobuf.cursor = ebuf.cursor = 0;
-	undobuf.winleft = ebuf.winleft = 0;
-	es = &ebuf;
-	undo = &undobuf;
-
-	x_init_prompt();
-	x_col = pwidth;
-
-	if (wbuf_len != x_cols - 3 && ((wbuf_len = x_cols - 3))) {
-		wbuf[0] = aresize(wbuf[0], wbuf_len, AEDIT);
-		wbuf[1] = aresize(wbuf[1], wbuf_len, AEDIT);
-	}
-	if (wbuf_len) {
-		memset(wbuf[0], ' ', wbuf_len);
-		memset(wbuf[1], ' ', wbuf_len);
-	}
-	winwidth = x_cols - pwidth - 3;
-	win = 0;
-	morec = ' ';
-	lastref = 1;
-	holdlen = 0;
-
-	editmode = 2;
-	x_flush();
-	while (/* CONSTCOND */ 1) {
-		if (macro.p) {
-			c = *macro.p++;
-			/* end of current macro? */
-			if (!c) {
-				/* more macros left to finish? */
-				if (*macro.p++)
-					continue;
-				/* must be the end of all the macros */
-				vi_macro_reset();
-				c = x_getc();
-			}
-		} else
-			c = x_getc();
-
-		if (c == -1)
-			break;
-		if (state != VLIT) {
-			if (c == edchars.intr || c == edchars.quit) {
-				/* pretend we got an interrupt */
-				x_vi_zotc(c);
-				x_flush();
-				trapsig(c == edchars.intr ? SIGINT : SIGQUIT);
-				x_mode(false);
-				unwind(LSHELL);
-			} else if (c == edchars.eof && state != VVERSION) {
-				if (es->linelen == 0) {
-					x_vi_zotc(edchars.eof);
-					c = -1;
-					break;
-				}
-				continue;
-			}
-		}
-		if (vi_hook(c))
-			break;
-		x_flush();
-	}
-
-	x_putc('\r');
-	x_putc('\n');
-	x_flush();
-
-	if (c == -1 || (ssize_t)LINE <= es->linelen)
-		return (-1);
-
-	if (es->cbuf != buf)
-		memcpy(buf, es->cbuf, es->linelen);
-
-	buf[es->linelen++] = '\n';
-
-	return (es->linelen);
-}
-
-static int
-vi_hook(int ch)
-{
-	static char curcmd[MAXVICMD], locpat[SRCHLEN];
-	static int cmdlen, argc1, argc2;
-
-	switch (state) {
-
-	case VNORMAL:
-		if (insert != 0) {
-			if (ch == Ctrl('v')) {
-				state = VLIT;
-				ch = '^';
-			}
-			switch (vi_insert(ch)) {
-			case -1:
-				vi_error();
-				state = VNORMAL;
-				break;
-			case 0:
-				if (state == VLIT) {
-					es->cursor--;
-					refresh(0);
-				} else
-					refresh(insert != 0);
-				break;
-			case 1:
-				return (1);
-			}
-		} else {
-			if (ch == '\r' || ch == '\n')
-				return (1);
-			cmdlen = 0;
-			argc1 = 0;
-			if (ch >= '1' && ch <= '9') {
-				argc1 = ch - '0';
-				state = VARG1;
-			} else {
-				curcmd[cmdlen++] = ch;
-				state = nextstate(ch);
-				if (state == VSEARCH) {
-					save_cbuf();
-					es->cursor = 0;
-					es->linelen = 0;
-					if (putbuf(ch == '/' ? "/" : "?", 1,
-					    false) != 0)
-						return (-1);
-					refresh(0);
-				}
-				if (state == VVERSION) {
-					save_cbuf();
-					es->cursor = 0;
-					es->linelen = 0;
-					putbuf(KSH_VERSION,
-					    strlen(KSH_VERSION), false);
-					refresh(0);
-				}
-			}
-		}
-		break;
-
-	case VLIT:
-		if (is_bad(ch)) {
-			del_range(es->cursor, es->cursor + 1);
-			vi_error();
-		} else
-			es->cbuf[es->cursor++] = ch;
-		refresh(1);
-		state = VNORMAL;
-		break;
-
-	case VVERSION:
-		restore_cbuf();
-		state = VNORMAL;
-		refresh(0);
-		break;
-
-	case VARG1:
-		if (ksh_isdigit(ch))
-			argc1 = argc1 * 10 + ch - '0';
-		else {
-			curcmd[cmdlen++] = ch;
-			state = nextstate(ch);
-		}
-		break;
-
-	case VEXTCMD:
-		argc2 = 0;
-		if (ch >= '1' && ch <= '9') {
-			argc2 = ch - '0';
-			state = VARG2;
-			return (0);
-		} else {
-			curcmd[cmdlen++] = ch;
-			if (ch == curcmd[0])
-				state = VCMD;
-			else if (is_move(ch))
-				state = nextstate(ch);
-			else
-				state = VFAIL;
-		}
-		break;
-
-	case VARG2:
-		if (ksh_isdigit(ch))
-			argc2 = argc2 * 10 + ch - '0';
-		else {
-			if (argc1 == 0)
-				argc1 = argc2;
-			else
-				argc1 *= argc2;
-			curcmd[cmdlen++] = ch;
-			if (ch == curcmd[0])
-				state = VCMD;
-			else if (is_move(ch))
-				state = nextstate(ch);
-			else
-				state = VFAIL;
-		}
-		break;
-
-	case VXCH:
-		if (ch == Ctrl('['))
-			state = VNORMAL;
-		else {
-			curcmd[cmdlen++] = ch;
-			state = VCMD;
-		}
-		break;
-
-	case VSEARCH:
-		if (ch == '\r' || ch == '\n' /*|| ch == Ctrl('[')*/ ) {
-			restore_cbuf();
-			/* Repeat last search? */
-			if (srchlen == 0) {
-				if (!srchpat[0]) {
-					vi_error();
-					state = VNORMAL;
-					refresh(0);
-					return (0);
-				}
-			} else {
-				locpat[srchlen] = '\0';
-				memcpy(srchpat, locpat, srchlen + 1);
-			}
-			state = VCMD;
-		} else if (ch == edchars.erase || ch == Ctrl('h')) {
-			if (srchlen != 0) {
-				srchlen--;
-				es->linelen -= char_len((unsigned char)locpat[srchlen]);
-				es->cursor = es->linelen;
-				refresh(0);
-				return (0);
-			}
-			restore_cbuf();
-			state = VNORMAL;
-			refresh(0);
-		} else if (ch == edchars.kill) {
-			srchlen = 0;
-			es->linelen = 1;
-			es->cursor = 1;
-			refresh(0);
-			return (0);
-		} else if (ch == edchars.werase) {
-			int i, n = srchlen;
-			struct edstate new_es, *save_es;
-
-			new_es.cursor = n;
-			new_es.cbuf = locpat;
-
-			save_es = es;
-			es = &new_es;
-			n = backword(1);
-			es = save_es;
-
-			for (i = srchlen; --i >= n; )
-				es->linelen -= char_len((unsigned char)locpat[i]);
-			srchlen = n;
-			es->cursor = es->linelen;
-			refresh(0);
-			return (0);
-		} else {
-			if (srchlen == SRCHLEN - 1)
-				vi_error();
-			else {
-				locpat[srchlen++] = ch;
-				if (ch < ' ' || ch == 0x7f) {
-					if ((size_t)es->linelen + 2 >
-					    (size_t)es->cbufsize)
-						vi_error();
-					es->cbuf[es->linelen++] = '^';
-					es->cbuf[es->linelen++] = ch ^ '@';
-				} else {
-					if (es->linelen >= es->cbufsize)
-						vi_error();
-					es->cbuf[es->linelen++] = ch;
-				}
-				es->cursor = es->linelen;
-				refresh(0);
-			}
-			return (0);
-		}
-		break;
-
-	case VPREFIX2:
-		state = VFAIL;
-		switch (ch) {
-		case 'A':
-			/* the cursor may not be at the BOL */
-			if (!es->cursor)
-				break;
-			/* nor further in the line than we can search for */
-			if ((size_t)es->cursor >= sizeof(srchpat) - 1)
-				es->cursor = sizeof(srchpat) - 2;
-			/* anchor the search pattern */
-			srchpat[0] = '^';
-			/* take the current line up to the cursor */
-			memmove(srchpat + 1, es->cbuf, es->cursor);
-			srchpat[es->cursor + 1] = '\0';
-			/* set a magic flag */
-			argc1 = 2 + (int)es->cursor;
-			/* and emulate a backwards history search */
-			lastsearch = '/';
-			*curcmd = 'n';
-			goto pseudo_VCMD;
-		}
-		break;
-	}
-
-	switch (state) {
-	case VCMD:
- pseudo_VCMD:
-		state = VNORMAL;
-		switch (vi_cmd(argc1, curcmd)) {
-		case -1:
-			vi_error();
-			refresh(0);
-			break;
-		case 0:
-			if (insert != 0)
-				inslen = 0;
-			refresh(insert != 0);
-			break;
-		case 1:
-			refresh(0);
-			return (1);
-		case 2:
-			/* back from a 'v' command - don't redraw the screen */
-			return (1);
-		}
-		break;
-
-	case VREDO:
-		state = VNORMAL;
-		if (argc1 != 0)
-			lastac = argc1;
-		switch (vi_cmd(lastac, lastcmd)) {
-		case -1:
-			vi_error();
-			refresh(0);
-			break;
-		case 0:
-			if (insert != 0) {
-				if (lastcmd[0] == 's' || lastcmd[0] == 'c' ||
-				    lastcmd[0] == 'C') {
-					if (redo_insert(1) != 0)
-						vi_error();
-				} else {
-					if (redo_insert(lastac) != 0)
-						vi_error();
-				}
-			}
-			refresh(0);
-			break;
-		case 1:
-			refresh(0);
-			return (1);
-		case 2:
-			/* back from a 'v' command - can't happen */
-			break;
-		}
-		break;
-
-	case VFAIL:
-		state = VNORMAL;
-		vi_error();
-		break;
-	}
-	return (0);
-}
-
-static int
-nextstate(int ch)
-{
-	if (is_extend(ch))
-		return (VEXTCMD);
-	else if (is_srch(ch))
-		return (VSEARCH);
-	else if (is_long(ch))
-		return (VXCH);
-	else if (ch == '.')
-		return (VREDO);
-	else if (ch == Ctrl('v'))
-		return (VVERSION);
-	else if (is_cmd(ch))
-		return (VCMD);
-	else
-		return (VFAIL);
-}
-
-static int
-vi_insert(int ch)
-{
-	int tcursor;
-
-	if (ch == edchars.erase || ch == Ctrl('h')) {
-		if (insert == REPLACE) {
-			if (es->cursor == undo->cursor) {
-				vi_error();
-				return (0);
-			}
-			if (inslen > 0)
-				inslen--;
-			es->cursor--;
-			if (es->cursor >= undo->linelen)
-				es->linelen--;
-			else
-				es->cbuf[es->cursor] = undo->cbuf[es->cursor];
-		} else {
-			if (es->cursor == 0)
-				return (0);
-			if (inslen > 0)
-				inslen--;
-			es->cursor--;
-			es->linelen--;
-			memmove(&es->cbuf[es->cursor], &es->cbuf[es->cursor + 1],
-			    es->linelen - es->cursor + 1);
-		}
-		expanded = NONE;
-		return (0);
-	}
-	if (ch == edchars.kill) {
-		if (es->cursor != 0) {
-			inslen = 0;
-			memmove(es->cbuf, &es->cbuf[es->cursor],
-			    es->linelen - es->cursor);
-			es->linelen -= es->cursor;
-			es->cursor = 0;
-		}
-		expanded = NONE;
-		return (0);
-	}
-	if (ch == edchars.werase) {
-		if (es->cursor != 0) {
-			tcursor = backword(1);
-			memmove(&es->cbuf[tcursor], &es->cbuf[es->cursor],
-			    es->linelen - es->cursor);
-			es->linelen -= es->cursor - tcursor;
-			if (inslen < es->cursor - tcursor)
-				inslen = 0;
-			else
-				inslen -= es->cursor - tcursor;
-			es->cursor = tcursor;
-		}
-		expanded = NONE;
-		return (0);
-	}
-	/*
-	 * If any chars are entered before escape, trash the saved insert
-	 * buffer (if user inserts & deletes char, ibuf gets trashed and
-	 * we don't want to use it)
-	 */
-	if (first_insert && ch != Ctrl('['))
-		saved_inslen = 0;
-	switch (ch) {
-	case '\0':
-		return (-1);
-
-	case '\r':
-	case '\n':
-		return (1);
-
-	case Ctrl('['):
-		expanded = NONE;
-		if (first_insert) {
-			first_insert = false;
-			if (inslen == 0) {
-				inslen = saved_inslen;
-				return (redo_insert(0));
-			}
-			lastcmd[0] = 'a';
-			lastac = 1;
-		}
-		if (lastcmd[0] == 's' || lastcmd[0] == 'c' ||
-		    lastcmd[0] == 'C')
-			return (redo_insert(0));
-		else
-			return (redo_insert(lastac - 1));
-
-	/* { Begin nonstandard vi commands */
-	case Ctrl('x'):
-		expand_word(0);
-		break;
-
-	case Ctrl('f'):
-		complete_word(0, 0);
-		break;
-
-	case Ctrl('e'):
-		print_expansions(es, 0);
-		break;
-
-	case Ctrl('i'):
-		if (Flag(FVITABCOMPLETE)) {
-			complete_word(0, 0);
-			break;
-		}
-		/* FALLTHROUGH */
-	/* End nonstandard vi commands } */
-
-	default:
-		if (es->linelen >= es->cbufsize - 1)
-			return (-1);
-		ibuf[inslen++] = ch;
-		if (insert == INSERT) {
-			memmove(&es->cbuf[es->cursor + 1], &es->cbuf[es->cursor],
-			    es->linelen - es->cursor);
-			es->linelen++;
-		}
-		es->cbuf[es->cursor++] = ch;
-		if (insert == REPLACE && es->cursor > es->linelen)
-			es->linelen++;
-		expanded = NONE;
-	}
-	return (0);
-}
-
-static int
-vi_cmd(int argcnt, const char *cmd)
-{
-	int ncursor;
-	int cur, c1, c2, c3 = 0;
-	int any;
-	struct edstate *t;
-
-	if (argcnt == 0 && !is_zerocount(*cmd))
-		argcnt = 1;
-
-	if (is_move(*cmd)) {
-		if ((cur = domove(argcnt, cmd, 0)) >= 0) {
-			if (cur == es->linelen && cur != 0)
-				cur--;
-			es->cursor = cur;
-		} else
-			return (-1);
-	} else {
-		/* Don't save state in middle of macro.. */
-		if (is_undoable(*cmd) && !macro.p) {
-			undo->winleft = es->winleft;
-			memmove(undo->cbuf, es->cbuf, es->linelen);
-			undo->linelen = es->linelen;
-			undo->cursor = es->cursor;
-			lastac = argcnt;
-			memmove(lastcmd, cmd, MAXVICMD);
-		}
-		switch (*cmd) {
-
-		case Ctrl('l'):
-		case Ctrl('r'):
-			redraw_line(true);
-			break;
-
-		case '@':
-			{
-				static char alias[] = "_\0";
-				struct tbl *ap;
-				size_t olen, nlen;
-				char *p, *nbuf;
-
-				/* lookup letter in alias list... */
-				alias[1] = cmd[1];
-				ap = ktsearch(&aliases, alias, hash(alias));
-				if (!cmd[1] || !ap || !(ap->flag & ISSET))
-					return (-1);
-				/* check if this is a recursive call... */
-				if ((p = (char *)macro.p))
-					while ((p = strnul(p)) && p[1])
-						if (*++p == cmd[1])
-							return (-1);
-				/* insert alias into macro buffer */
-				nlen = strlen(ap->val.s) + 1;
-				olen = !macro.p ? 2 :
-				    macro.len - (macro.p - macro.buf);
-				/*
-				 * at this point, it's fairly reasonable that
-				 * nlen + olen + 2 doesn't overflow
-				 */
-				nbuf = alloc(nlen + 1 + olen, AEDIT);
-				memcpy(nbuf, ap->val.s, nlen);
-				nbuf[nlen++] = cmd[1];
-				if (macro.p) {
-					memcpy(nbuf + nlen, macro.p, olen);
-					afree(macro.buf, AEDIT);
-					nlen += olen;
-				} else {
-					nbuf[nlen++] = '\0';
-					nbuf[nlen++] = '\0';
-				}
-				macro.p = macro.buf = (unsigned char *)nbuf;
-				macro.len = nlen;
-			}
-			break;
-
-		case 'a':
-			modified = 1;
-			hnum = hlast;
-			if (es->linelen != 0)
-				es->cursor++;
-			insert = INSERT;
-			break;
-
-		case 'A':
-			modified = 1;
-			hnum = hlast;
-			del_range(0, 0);
-			es->cursor = es->linelen;
-			insert = INSERT;
-			break;
-
-		case 'S':
-			es->cursor = domove(1, "^", 1);
-			del_range(es->cursor, es->linelen);
-			modified = 1;
-			hnum = hlast;
-			insert = INSERT;
-			break;
-
-		case 'Y':
-			cmd = "y$";
-			/* ahhhhhh... */
-		case 'c':
-		case 'd':
-		case 'y':
-			if (*cmd == cmd[1]) {
-				c1 = *cmd == 'c' ? domove(1, "^", 1) : 0;
-				c2 = es->linelen;
-			} else if (!is_move(cmd[1]))
-				return (-1);
-			else {
-				if ((ncursor = domove(argcnt, &cmd[1], 1)) < 0)
-					return (-1);
-				if (*cmd == 'c' &&
-				    (cmd[1] == 'w' || cmd[1] == 'W') &&
-				    !ksh_isspace(es->cbuf[es->cursor])) {
-					do {
-						--ncursor;
-					} while (ksh_isspace(es->cbuf[ncursor]));
-					ncursor++;
-				}
-				if (ncursor > es->cursor) {
-					c1 = es->cursor;
-					c2 = ncursor;
-				} else {
-					c1 = ncursor;
-					c2 = es->cursor;
-					if (cmd[1] == '%')
-						c2++;
-				}
-			}
-			if (*cmd != 'c' && c1 != c2)
-				yank_range(c1, c2);
-			if (*cmd != 'y') {
-				del_range(c1, c2);
-				es->cursor = c1;
-			}
-			if (*cmd == 'c') {
-				modified = 1;
-				hnum = hlast;
-				insert = INSERT;
-			}
-			break;
-
-		case 'p':
-			modified = 1;
-			hnum = hlast;
-			if (es->linelen != 0)
-				es->cursor++;
-			while (putbuf(ybuf, yanklen, false) == 0 &&
-			    --argcnt > 0)
-				;
-			if (es->cursor != 0)
-				es->cursor--;
-			if (argcnt != 0)
-				return (-1);
-			break;
-
-		case 'P':
-			modified = 1;
-			hnum = hlast;
-			any = 0;
-			while (putbuf(ybuf, yanklen, false) == 0 &&
-			    --argcnt > 0)
-				any = 1;
-			if (any && es->cursor != 0)
-				es->cursor--;
-			if (argcnt != 0)
-				return (-1);
-			break;
-
-		case 'C':
-			modified = 1;
-			hnum = hlast;
-			del_range(es->cursor, es->linelen);
-			insert = INSERT;
-			break;
-
-		case 'D':
-			yank_range(es->cursor, es->linelen);
-			del_range(es->cursor, es->linelen);
-			if (es->cursor != 0)
-				es->cursor--;
-			break;
-
-		case 'g':
-			if (!argcnt)
-				argcnt = hlast;
-			/* FALLTHROUGH */
-		case 'G':
-			if (!argcnt)
-				argcnt = 1;
-			else
-				argcnt = hlast - (source->line - argcnt);
-			if (grabhist(modified, argcnt - 1) < 0)
-				return (-1);
-			else {
-				modified = 0;
-				hnum = argcnt - 1;
-			}
-			break;
-
-		case 'i':
-			modified = 1;
-			hnum = hlast;
-			insert = INSERT;
-			break;
-
-		case 'I':
-			modified = 1;
-			hnum = hlast;
-			es->cursor = domove(1, "^", 1);
-			insert = INSERT;
-			break;
-
-		case 'j':
-		case '+':
-		case Ctrl('n'):
-			if (grabhist(modified, hnum + argcnt) < 0)
-				return (-1);
-			else {
-				modified = 0;
-				hnum += argcnt;
-			}
-			break;
-
-		case 'k':
-		case '-':
-		case Ctrl('p'):
-			if (grabhist(modified, hnum - argcnt) < 0)
-				return (-1);
-			else {
-				modified = 0;
-				hnum -= argcnt;
-			}
-			break;
-
-		case 'r':
-			if (es->linelen == 0)
-				return (-1);
-			modified = 1;
-			hnum = hlast;
-			if (cmd[1] == 0)
-				vi_error();
-			else {
-				int n;
-
-				if (es->cursor + argcnt > es->linelen)
-					return (-1);
-				for (n = 0; n < argcnt; ++n)
-					es->cbuf[es->cursor + n] = cmd[1];
-				es->cursor += n - 1;
-			}
-			break;
-
-		case 'R':
-			modified = 1;
-			hnum = hlast;
-			insert = REPLACE;
-			break;
-
-		case 's':
-			if (es->linelen == 0)
-				return (-1);
-			modified = 1;
-			hnum = hlast;
-			if (es->cursor + argcnt > es->linelen)
-				argcnt = es->linelen - es->cursor;
-			del_range(es->cursor, es->cursor + argcnt);
-			insert = INSERT;
-			break;
-
-		case 'v':
-			if (!argcnt) {
-				if (es->linelen == 0)
-					return (-1);
-				if (modified) {
-					es->cbuf[es->linelen] = '\0';
-					histsave(&source->line, es->cbuf, true,
-					    true);
-				} else
-					argcnt = source->line + 1 -
-					    (hlast - hnum);
-			}
-			if (argcnt)
-				shf_snprintf(es->cbuf, es->cbufsize, "%s %d",
-				    "fc -e ${VISUAL:-${EDITOR:-vi}} --",
-				    argcnt);
-			else
-				strlcpy(es->cbuf,
-				    "fc -e ${VISUAL:-${EDITOR:-vi}} --",
-				    es->cbufsize);
-			es->linelen = strlen(es->cbuf);
-			return (2);
-
-		case 'x':
-			if (es->linelen == 0)
-				return (-1);
-			modified = 1;
-			hnum = hlast;
-			if (es->cursor + argcnt > es->linelen)
-				argcnt = es->linelen - es->cursor;
-			yank_range(es->cursor, es->cursor + argcnt);
-			del_range(es->cursor, es->cursor + argcnt);
-			break;
-
-		case 'X':
-			if (es->cursor > 0) {
-				modified = 1;
-				hnum = hlast;
-				if (es->cursor < argcnt)
-					argcnt = es->cursor;
-				yank_range(es->cursor - argcnt, es->cursor);
-				del_range(es->cursor - argcnt, es->cursor);
-				es->cursor -= argcnt;
-			} else
-				return (-1);
-			break;
-
-		case 'u':
-			t = es;
-			es = undo;
-			undo = t;
-			break;
-
-		case 'U':
-			if (!modified)
-				return (-1);
-			if (grabhist(modified, ohnum) < 0)
-				return (-1);
-			modified = 0;
-			hnum = ohnum;
-			break;
-
-		case '?':
-			if (hnum == hlast)
-				hnum = -1;
-			/* ahhh */
-		case '/':
-			c3 = 1;
-			srchlen = 0;
-			lastsearch = *cmd;
-			/* FALLTHROUGH */
-		case 'n':
-		case 'N':
-			if (lastsearch == ' ')
-				return (-1);
-			if (lastsearch == '?')
-				c1 = 1;
-			else
-				c1 = 0;
-			if (*cmd == 'N')
-				c1 = !c1;
-			if ((c2 = grabsearch(modified, hnum,
-			    c1, srchpat)) < 0) {
-				if (c3) {
-					restore_cbuf();
-					refresh(0);
-				}
-				return (-1);
-			} else {
-				modified = 0;
-				hnum = c2;
-				ohnum = hnum;
-			}
-			if (argcnt >= 2) {
-				/* flag from cursor-up command */
-				es->cursor = argcnt - 2;
-				return (0);
-			}
-			break;
-		case '_':
-			{
-				bool inspace;
-				char *p, *sp;
-
-				if (histnum(-1) < 0)
-					return (-1);
-				p = *histpos();
-#define issp(c)		(ksh_isspace(c) || (c) == '\n')
-				if (argcnt) {
-					while (*p && issp(*p))
-						p++;
-					while (*p && --argcnt) {
-						while (*p && !issp(*p))
-							p++;
-						while (*p && issp(*p))
-							p++;
-					}
-					if (!*p)
-						return (-1);
-					sp = p;
-				} else {
-					sp = p;
-					inspace = false;
-					while (*p) {
-						if (issp(*p))
-							inspace = true;
-						else if (inspace) {
-							inspace = false;
-							sp = p;
-						}
-						p++;
-					}
-					p = sp;
-				}
-				modified = 1;
-				hnum = hlast;
-				if (es->cursor != es->linelen)
-					es->cursor++;
-				while (*p && !issp(*p)) {
-					argcnt++;
-					p++;
-				}
-				if (putbuf(" ", 1, false) != 0 ||
-				    putbuf(sp, argcnt, false) != 0) {
-					if (es->cursor != 0)
-						es->cursor--;
-					return (-1);
-				}
-				insert = INSERT;
-			}
-			break;
-
-		case '~':
-			{
-				char *p;
-				int i;
-
-				if (es->linelen == 0)
-					return (-1);
-				for (i = 0; i < argcnt; i++) {
-					p = &es->cbuf[es->cursor];
-					if (ksh_islower(*p)) {
-						modified = 1;
-						hnum = hlast;
-						*p = ksh_toupper(*p);
-					} else if (ksh_isupper(*p)) {
-						modified = 1;
-						hnum = hlast;
-						*p = ksh_tolower(*p);
-					}
-					if (es->cursor < es->linelen - 1)
-						es->cursor++;
-				}
-				break;
-			}
-
-		case '#':
-			{
-				int ret = x_do_comment(es->cbuf, es->cbufsize,
-				    &es->linelen);
-				if (ret >= 0)
-					es->cursor = 0;
-				return (ret);
-			}
-
-		/* AT&T ksh */
-		case '=':
-		/* Nonstandard vi/ksh */
-		case Ctrl('e'):
-			print_expansions(es, 1);
-			break;
-
-
-		/* Nonstandard vi/ksh */
-		case Ctrl('i'):
-			if (!Flag(FVITABCOMPLETE))
-				return (-1);
-			complete_word(1, argcnt);
-			break;
-
-		/* some annoying AT&T kshs */
-		case Ctrl('['):
-			if (!Flag(FVIESCCOMPLETE))
-				return (-1);
-		/* AT&T ksh */
-		case '\\':
-		/* Nonstandard vi/ksh */
-		case Ctrl('f'):
-			complete_word(1, argcnt);
-			break;
-
-
-		/* AT&T ksh */
-		case '*':
-		/* Nonstandard vi/ksh */
-		case Ctrl('x'):
-			expand_word(1);
-			break;
-
-
-		/* mksh: cursor movement */
-		case '[':
-		case 'O':
-			state = VPREFIX2;
-			if (es->linelen != 0)
-				es->cursor++;
-			insert = INSERT;
-			return (0);
-		}
-		if (insert == 0 && es->cursor != 0 && es->cursor >= es->linelen)
-			es->cursor--;
-	}
-	return (0);
-}
-
-static int
-domove(int argcnt, const char *cmd, int sub)
-{
-	int bcount, i = 0, t;
-	int ncursor = 0;
-
-	switch (*cmd) {
-	case 'b':
-		if (!sub && es->cursor == 0)
-			return (-1);
-		ncursor = backword(argcnt);
-		break;
-
-	case 'B':
-		if (!sub && es->cursor == 0)
-			return (-1);
-		ncursor = Backword(argcnt);
-		break;
-
-	case 'e':
-		if (!sub && es->cursor + 1 >= es->linelen)
-			return (-1);
-		ncursor = endword(argcnt);
-		if (sub && ncursor < es->linelen)
-			ncursor++;
-		break;
-
-	case 'E':
-		if (!sub && es->cursor + 1 >= es->linelen)
-			return (-1);
-		ncursor = Endword(argcnt);
-		if (sub && ncursor < es->linelen)
-			ncursor++;
-		break;
-
-	case 'f':
-	case 'F':
-	case 't':
-	case 'T':
-		fsavecmd = *cmd;
-		fsavech = cmd[1];
-		/* drop through */
-
-	case ',':
-	case ';':
-		if (fsavecmd == ' ')
-			return (-1);
-		i = fsavecmd == 'f' || fsavecmd == 'F';
-		t = fsavecmd > 'a';
-		if (*cmd == ',')
-			t = !t;
-		if ((ncursor = findch(fsavech, argcnt, tobool(t),
-		    tobool(i))) < 0)
-			return (-1);
-		if (sub && t)
-			ncursor++;
-		break;
-
-	case 'h':
-	case Ctrl('h'):
-		if (!sub && es->cursor == 0)
-			return (-1);
-		ncursor = es->cursor - argcnt;
-		if (ncursor < 0)
-			ncursor = 0;
-		break;
-
-	case ' ':
-	case 'l':
-		if (!sub && es->cursor + 1 >= es->linelen)
-			return (-1);
-		if (es->linelen != 0) {
-			ncursor = es->cursor + argcnt;
-			if (ncursor > es->linelen)
-				ncursor = es->linelen;
-		}
-		break;
-
-	case 'w':
-		if (!sub && es->cursor + 1 >= es->linelen)
-			return (-1);
-		ncursor = forwword(argcnt);
-		break;
-
-	case 'W':
-		if (!sub && es->cursor + 1 >= es->linelen)
-			return (-1);
-		ncursor = Forwword(argcnt);
-		break;
-
-	case '0':
-		ncursor = 0;
-		break;
-
-	case '^':
-		ncursor = 0;
-		while (ncursor < es->linelen - 1 &&
-		    ksh_isspace(es->cbuf[ncursor]))
-			ncursor++;
-		break;
-
-	case '|':
-		ncursor = argcnt;
-		if (ncursor > es->linelen)
-			ncursor = es->linelen;
-		if (ncursor)
-			ncursor--;
-		break;
-
-	case '$':
-		if (es->linelen != 0)
-			ncursor = es->linelen;
-		else
-			ncursor = 0;
-		break;
-
-	case '%':
-		ncursor = es->cursor;
-		while (ncursor < es->linelen &&
-		    (i = bracktype(es->cbuf[ncursor])) == 0)
-			ncursor++;
-		if (ncursor == es->linelen)
-			return (-1);
-		bcount = 1;
-		do {
-			if (i > 0) {
-				if (++ncursor >= es->linelen)
-					return (-1);
-			} else {
-				if (--ncursor < 0)
-					return (-1);
-			}
-			t = bracktype(es->cbuf[ncursor]);
-			if (t == i)
-				bcount++;
-			else if (t == -i)
-				bcount--;
-		} while (bcount != 0);
-		if (sub && i > 0)
-			ncursor++;
-		break;
-
-	default:
-		return (-1);
-	}
-	return (ncursor);
-}
-
-static int
-redo_insert(int count)
-{
-	while (count-- > 0)
-		if (putbuf(ibuf, inslen, tobool(insert == REPLACE)) != 0)
-			return (-1);
-	if (es->cursor > 0)
-		es->cursor--;
-	insert = 0;
-	return (0);
-}
-
-static void
-yank_range(int a, int b)
-{
-	yanklen = b - a;
-	if (yanklen != 0)
-		memmove(ybuf, &es->cbuf[a], yanklen);
-}
-
-static int
-bracktype(int ch)
-{
-	switch (ch) {
-
-	case '(':
-		return (1);
-
-	case '[':
-		return (2);
-
-	case '{':
-		return (3);
-
-	case ')':
-		return (-1);
-
-	case ']':
-		return (-2);
-
-	case '}':
-		return (-3);
-
-	default:
-		return (0);
-	}
-}
-
-/*
- *	Non user interface editor routines below here
- */
-
-static void
-save_cbuf(void)
-{
-	memmove(holdbufp, es->cbuf, es->linelen);
-	holdlen = es->linelen;
-	holdbufp[holdlen] = '\0';
-}
-
-static void
-restore_cbuf(void)
-{
-	es->cursor = 0;
-	es->linelen = holdlen;
-	memmove(es->cbuf, holdbufp, holdlen);
-}
-
-/* return a new edstate */
-static struct edstate *
-save_edstate(struct edstate *old)
-{
-	struct edstate *news;
-
-	news = alloc(sizeof(struct edstate), AEDIT);
-	news->cbuf = alloc(old->cbufsize, AEDIT);
-	memcpy(news->cbuf, old->cbuf, old->linelen);
-	news->cbufsize = old->cbufsize;
-	news->linelen = old->linelen;
-	news->cursor = old->cursor;
-	news->winleft = old->winleft;
-	return (news);
-}
-
-static void
-restore_edstate(struct edstate *news, struct edstate *old)
-{
-	memcpy(news->cbuf, old->cbuf, old->linelen);
-	news->linelen = old->linelen;
-	news->cursor = old->cursor;
-	news->winleft = old->winleft;
-	free_edstate(old);
-}
-
-static void
-free_edstate(struct edstate *old)
-{
-	afree(old->cbuf, AEDIT);
-	afree(old, AEDIT);
-}
-
-/*
- * this is used for calling x_escape() in complete_word()
- */
-static int
-x_vi_putbuf(const char *s, size_t len)
-{
-	return (putbuf(s, len, false));
-}
-
-static int
-putbuf(const char *buf, ssize_t len, bool repl)
-{
-	if (len == 0)
-		return (0);
-	if (repl) {
-		if (es->cursor + len >= es->cbufsize)
-			return (-1);
-		if (es->cursor + len > es->linelen)
-			es->linelen = es->cursor + len;
-	} else {
-		if (es->linelen + len >= es->cbufsize)
-			return (-1);
-		memmove(&es->cbuf[es->cursor + len], &es->cbuf[es->cursor],
-		    es->linelen - es->cursor);
-		es->linelen += len;
-	}
-	memmove(&es->cbuf[es->cursor], buf, len);
-	es->cursor += len;
-	return (0);
-}
-
-static void
-del_range(int a, int b)
-{
-	if (es->linelen != b)
-		memmove(&es->cbuf[a], &es->cbuf[b], es->linelen - b);
-	es->linelen -= b - a;
-}
-
-static int
-findch(int ch, int cnt, bool forw, bool incl)
-{
-	int ncursor;
-
-	if (es->linelen == 0)
-		return (-1);
-	ncursor = es->cursor;
-	while (cnt--) {
-		do {
-			if (forw) {
-				if (++ncursor == es->linelen)
-					return (-1);
-			} else {
-				if (--ncursor < 0)
-					return (-1);
-			}
-		} while (es->cbuf[ncursor] != ch);
-	}
-	if (!incl) {
-		if (forw)
-			ncursor--;
-		else
-			ncursor++;
-	}
-	return (ncursor);
-}
-
-static int
-forwword(int argcnt)
-{
-	int ncursor;
-
-	ncursor = es->cursor;
-	while (ncursor < es->linelen && argcnt--) {
-		if (ksh_isalnux(es->cbuf[ncursor]))
-			while (ksh_isalnux(es->cbuf[ncursor]) &&
-			    ncursor < es->linelen)
-				ncursor++;
-		else if (!ksh_isspace(es->cbuf[ncursor]))
-			while (!ksh_isalnux(es->cbuf[ncursor]) &&
-			    !ksh_isspace(es->cbuf[ncursor]) &&
-			    ncursor < es->linelen)
-				ncursor++;
-		while (ksh_isspace(es->cbuf[ncursor]) &&
-		    ncursor < es->linelen)
-			ncursor++;
-	}
-	return (ncursor);
-}
-
-static int
-backword(int argcnt)
-{
-	int ncursor;
-
-	ncursor = es->cursor;
-	while (ncursor > 0 && argcnt--) {
-		while (--ncursor > 0 && ksh_isspace(es->cbuf[ncursor]))
-			;
-		if (ncursor > 0) {
-			if (ksh_isalnux(es->cbuf[ncursor]))
-				while (--ncursor >= 0 &&
-				    ksh_isalnux(es->cbuf[ncursor]))
-					;
-			else
-				while (--ncursor >= 0 &&
-				    !ksh_isalnux(es->cbuf[ncursor]) &&
-				    !ksh_isspace(es->cbuf[ncursor]))
-					;
-			ncursor++;
-		}
-	}
-	return (ncursor);
-}
-
-static int
-endword(int argcnt)
-{
-	int ncursor;
-
-	ncursor = es->cursor;
-	while (ncursor < es->linelen && argcnt--) {
-		while (++ncursor < es->linelen - 1 &&
-		    ksh_isspace(es->cbuf[ncursor]))
-			;
-		if (ncursor < es->linelen - 1) {
-			if (ksh_isalnux(es->cbuf[ncursor]))
-				while (++ncursor < es->linelen &&
-				    ksh_isalnux(es->cbuf[ncursor]))
-					;
-			else
-				while (++ncursor < es->linelen &&
-				    !ksh_isalnux(es->cbuf[ncursor]) &&
-				    !ksh_isspace(es->cbuf[ncursor]))
-					;
-			ncursor--;
-		}
-	}
-	return (ncursor);
-}
-
-static int
-Forwword(int argcnt)
-{
-	int ncursor;
-
-	ncursor = es->cursor;
-	while (ncursor < es->linelen && argcnt--) {
-		while (!ksh_isspace(es->cbuf[ncursor]) &&
-		    ncursor < es->linelen)
-			ncursor++;
-		while (ksh_isspace(es->cbuf[ncursor]) &&
-		    ncursor < es->linelen)
-			ncursor++;
-	}
-	return (ncursor);
-}
-
-static int
-Backword(int argcnt)
-{
-	int ncursor;
-
-	ncursor = es->cursor;
-	while (ncursor > 0 && argcnt--) {
-		while (--ncursor >= 0 && ksh_isspace(es->cbuf[ncursor]))
-			;
-		while (ncursor >= 0 && !ksh_isspace(es->cbuf[ncursor]))
-			ncursor--;
-		ncursor++;
-	}
-	return (ncursor);
-}
-
-static int
-Endword(int argcnt)
-{
-	int ncursor;
-
-	ncursor = es->cursor;
-	while (ncursor < es->linelen - 1 && argcnt--) {
-		while (++ncursor < es->linelen - 1 &&
-		    ksh_isspace(es->cbuf[ncursor]))
-			;
-		if (ncursor < es->linelen - 1) {
-			while (++ncursor < es->linelen &&
-			    !ksh_isspace(es->cbuf[ncursor]))
-				;
-			ncursor--;
-		}
-	}
-	return (ncursor);
-}
-
-static int
-grabhist(int save, int n)
-{
-	char *hptr;
-
-	if (n < 0 || n > hlast)
-		return (-1);
-	if (n == hlast) {
-		restore_cbuf();
-		ohnum = n;
-		return (0);
-	}
-	(void)histnum(n);
-	if ((hptr = *histpos()) == NULL) {
-		internal_warningf("%s: %s", "grabhist", "bad history array");
-		return (-1);
-	}
-	if (save)
-		save_cbuf();
-	if ((es->linelen = strlen(hptr)) >= es->cbufsize)
-		es->linelen = es->cbufsize - 1;
-	memmove(es->cbuf, hptr, es->linelen);
-	es->cursor = 0;
-	ohnum = n;
-	return (0);
-}
-
-static int
-grabsearch(int save, int start, int fwd, const char *pat)
-{
-	char *hptr;
-	int hist;
-	int anchored;
-
-	if ((start == 0 && fwd == 0) || (start >= hlast - 1 && fwd == 1))
-		return (-1);
-	if (fwd)
-		start++;
-	else
-		start--;
-	anchored = *pat == '^' ? (++pat, 1) : 0;
-	if ((hist = findhist(start, fwd, pat, anchored)) < 0) {
-		/* (start != 0 && fwd && match(holdbufp, pat) >= 0) */
-		if (start != 0 && fwd && strcmp(holdbufp, pat) >= 0) {
-			restore_cbuf();
-			return (0);
-		} else
-			return (-1);
-	}
-	if (save)
-		save_cbuf();
-	histnum(hist);
-	hptr = *histpos();
-	if ((es->linelen = strlen(hptr)) >= es->cbufsize)
-		es->linelen = es->cbufsize - 1;
-	memmove(es->cbuf, hptr, es->linelen);
-	es->cursor = 0;
-	return (hist);
-}
-
-static void
-redraw_line(bool newl)
-{
-	if (wbuf_len)
-		memset(wbuf[win], ' ', wbuf_len);
-	if (newl) {
-		x_putc('\r');
-		x_putc('\n');
-	}
-	if (prompt_trunc != -1)
-		pprompt(prompt, prompt_trunc);
-	x_col = pwidth;
-	morec = ' ';
-}
-
-static void
-refresh(int leftside)
-{
-	if (leftside < 0)
-		leftside = lastref;
-	else
-		lastref = leftside;
-	if (outofwin())
-		rewindow();
-	display(wbuf[1 - win], wbuf[win], leftside);
-	win = 1 - win;
-}
-
-static int
-outofwin(void)
-{
-	int cur, col;
-
-	if (es->cursor < es->winleft)
-		return (1);
-	col = 0;
-	cur = es->winleft;
-	while (cur < es->cursor)
-		col = newcol((unsigned char)es->cbuf[cur++], col);
-	if (col >= winwidth)
-		return (1);
-	return (0);
-}
-
-static void
-rewindow(void)
-{
-	int tcur, tcol;
-	int holdcur1, holdcol1;
-	int holdcur2, holdcol2;
-
-	holdcur1 = holdcur2 = tcur = 0;
-	holdcol1 = holdcol2 = tcol = 0;
-	while (tcur < es->cursor) {
-		if (tcol - holdcol2 > winwidth / 2) {
-			holdcur1 = holdcur2;
-			holdcol1 = holdcol2;
-			holdcur2 = tcur;
-			holdcol2 = tcol;
-		}
-		tcol = newcol((unsigned char)es->cbuf[tcur++], tcol);
-	}
-	while (tcol - holdcol1 > winwidth / 2)
-		holdcol1 = newcol((unsigned char)es->cbuf[holdcur1++],
-		    holdcol1);
-	es->winleft = holdcur1;
-}
-
-static int
-newcol(int ch, int col)
-{
-	if (ch == '\t')
-		return ((col | 7) + 1);
-	return (col + char_len(ch));
-}
-
-static void
-display(char *wb1, char *wb2, int leftside)
-{
-	unsigned char ch;
-	char *twb1, *twb2, mc;
-	int cur, col, cnt;
-	int ncol = 0;
-	int moreright;
-
-	col = 0;
-	cur = es->winleft;
-	moreright = 0;
-	twb1 = wb1;
-	while (col < winwidth && cur < es->linelen) {
-		if (cur == es->cursor && leftside)
-			ncol = col + pwidth;
-		if ((ch = es->cbuf[cur]) == '\t')
-			do {
-				*twb1++ = ' ';
-			} while (++col < winwidth && (col & 7) != 0);
-		else if (col < winwidth) {
-			if (ch < ' ' || ch == 0x7f) {
-				*twb1++ = '^';
-				if (++col < winwidth) {
-					*twb1++ = ch ^ '@';
-					col++;
-				}
-			} else {
-				*twb1++ = ch;
-				col++;
-			}
-		}
-		if (cur == es->cursor && !leftside)
-			ncol = col + pwidth - 1;
-		cur++;
-	}
-	if (cur == es->cursor)
-		ncol = col + pwidth;
-	if (col < winwidth) {
-		while (col < winwidth) {
-			*twb1++ = ' ';
-			col++;
-		}
-	} else
-		moreright++;
-	*twb1 = ' ';
-
-	col = pwidth;
-	cnt = winwidth;
-	twb1 = wb1;
-	twb2 = wb2;
-	while (cnt--) {
-		if (*twb1 != *twb2) {
-			if (x_col != col)
-				ed_mov_opt(col, wb1);
-			x_putc(*twb1);
-			x_col++;
-		}
-		twb1++;
-		twb2++;
-		col++;
-	}
-	if (es->winleft > 0 && moreright)
-		/*
-		 * POSIX says to use * for this but that is a globbing
-		 * character and may confuse people; + is more innocuous
-		 */
-		mc = '+';
-	else if (es->winleft > 0)
-		mc = '<';
-	else if (moreright)
-		mc = '>';
-	else
-		mc = ' ';
-	if (mc != morec) {
-		ed_mov_opt(pwidth + winwidth + 1, wb1);
-		x_putc(mc);
-		x_col++;
-		morec = mc;
-	}
-	if (x_col != ncol)
-		ed_mov_opt(ncol, wb1);
-}
-
-static void
-ed_mov_opt(int col, char *wb)
-{
-	if (col < x_col) {
-		if (col + 1 < x_col - col) {
-			x_putc('\r');
-			if (prompt_trunc != -1)
-				pprompt(prompt, prompt_trunc);
-			x_col = pwidth;
-			while (x_col++ < col)
-				x_putcf(*wb++);
-		} else {
-			while (x_col-- > col)
-				x_putc('\b');
-		}
-	} else {
-		wb = &wb[x_col - pwidth];
-		while (x_col++ < col)
-			x_putcf(*wb++);
-	}
-	x_col = col;
-}
-
-
-/* replace word with all expansions (ie, expand word*) */
-static int
-expand_word(int cmd)
-{
-	static struct edstate *buf;
-	int rval = 0, nwords, start, end, i;
-	char **words;
-
-	/* Undo previous expansion */
-	if (cmd == 0 && expanded == EXPAND && buf) {
-		restore_edstate(es, buf);
-		buf = 0;
-		expanded = NONE;
-		return (0);
-	}
-	if (buf) {
-		free_edstate(buf);
-		buf = 0;
-	}
-
-	i = XCF_COMMAND_FILE | XCF_FULLPATH;
-	nwords = x_cf_glob(&i, es->cbuf, es->linelen, es->cursor,
-	    &start, &end, &words);
-	if (nwords == 0) {
-		vi_error();
-		return (-1);
-	}
-
-	buf = save_edstate(es);
-	expanded = EXPAND;
-	del_range(start, end);
-	es->cursor = start;
-	i = 0;
-	while (i < nwords) {
-		if (x_escape(words[i], strlen(words[i]), x_vi_putbuf) != 0) {
-			rval = -1;
-			break;
-		}
-		if (++i < nwords && putbuf(" ", 1, false) != 0) {
-			rval = -1;
-			break;
-		}
-	}
-	i = buf->cursor - end;
-	if (rval == 0 && i > 0)
-		es->cursor += i;
-	modified = 1;
-	hnum = hlast;
-	insert = INSERT;
-	lastac = 0;
-	refresh(0);
-	return (rval);
-}
-
-static int
-complete_word(int cmd, int count)
-{
-	static struct edstate *buf;
-	int rval, nwords, start, end, flags;
-	size_t match_len;
-	char **words;
-	char *match;
-	bool is_unique;
-
-	/* Undo previous completion */
-	if (cmd == 0 && expanded == COMPLETE && buf) {
-		print_expansions(buf, 0);
-		expanded = PRINT;
-		return (0);
-	}
-	if (cmd == 0 && expanded == PRINT && buf) {
-		restore_edstate(es, buf);
-		buf = 0;
-		expanded = NONE;
-		return (0);
-	}
-	if (buf) {
-		free_edstate(buf);
-		buf = 0;
-	}
-
-	/*
-	 * XCF_FULLPATH for count 'cause the menu printed by
-	 * print_expansions() was done this way.
-	 */
-	flags = XCF_COMMAND_FILE;
-	if (count)
-		flags |= XCF_FULLPATH;
-	nwords = x_cf_glob(&flags, es->cbuf, es->linelen, es->cursor,
-	    &start, &end, &words);
-	if (nwords == 0) {
-		vi_error();
-		return (-1);
-	}
-	if (count) {
-		int i;
-
-		count--;
-		if (count >= nwords) {
-			vi_error();
-			x_print_expansions(nwords, words,
-			    tobool(flags & XCF_IS_COMMAND));
-			x_free_words(nwords, words);
-			redraw_line(false);
-			return (-1);
-		}
-		/*
-		 * Expand the count'th word to its basename
-		 */
-		if (flags & XCF_IS_COMMAND) {
-			match = words[count] +
-			    x_basename(words[count], NULL);
-			/* If more than one possible match, use full path */
-			for (i = 0; i < nwords; i++)
-				if (i != count &&
-				    strcmp(words[i] + x_basename(words[i],
-				    NULL), match) == 0) {
-					match = words[count];
-					break;
-				}
-		} else
-			match = words[count];
-		match_len = strlen(match);
-		is_unique = true;
-		/* expanded = PRINT;	next call undo */
-	} else {
-		match = words[0];
-		match_len = x_longest_prefix(nwords, words);
-		/* next call will list completions */
-		expanded = COMPLETE;
-		is_unique = nwords == 1;
-	}
-
-	buf = save_edstate(es);
-	del_range(start, end);
-	es->cursor = start;
-
-	/*
-	 * escape all shell-sensitive characters and put the result into
-	 * command buffer
-	 */
-	rval = x_escape(match, match_len, x_vi_putbuf);
-
-	if (rval == 0 && is_unique) {
-		/*
-		 * If exact match, don't undo. Allows directory completions
-		 * to be used (ie, complete the next portion of the path).
-		 */
-		expanded = NONE;
-
-		/*
-		 * append a space if this is a non-directory match
-		 * and not a parameter or homedir substitution
-		 */
-		if (match_len > 0 && match[match_len - 1] != '/' &&
-		    !(flags & XCF_IS_NOSPACE))
-			rval = putbuf(" ", 1, false);
-	}
-	x_free_words(nwords, words);
-
-	modified = 1;
-	hnum = hlast;
-	insert = INSERT;
-	/* prevent this from being redone... */
-	lastac = 0;
-	refresh(0);
-
-	return (rval);
-}
-
-static int
-print_expansions(struct edstate *est, int cmd MKSH_A_UNUSED)
-{
-	int start, end, nwords, i;
-	char **words;
-
-	i = XCF_COMMAND_FILE | XCF_FULLPATH;
-	nwords = x_cf_glob(&i, est->cbuf, est->linelen, est->cursor,
-	    &start, &end, &words);
-	if (nwords == 0) {
-		vi_error();
-		return (-1);
-	}
-	x_print_expansions(nwords, words, tobool(i & XCF_IS_COMMAND));
-	x_free_words(nwords, words);
-	redraw_line(false);
-	return (0);
-}
-
-/* Similar to x_zotc(emacs.c), but no tab weirdness */
-static void
-x_vi_zotc(int c)
-{
-	if (c < ' ' || c == 0x7f) {
-		x_putc('^');
-		c ^= '@';
-	}
-	x_putc(c);
-}
-
-static void
-vi_error(void)
-{
-	/* Beem out of any macros as soon as an error occurs */
-	vi_macro_reset();
-	x_putc(7);
-	x_flush();
-}
-
-static void
-vi_macro_reset(void)
-{
-	if (macro.p) {
-		afree(macro.buf, AEDIT);
-		memset((char *)&macro, 0, sizeof(macro));
-	}
-}
-#endif /* !MKSH_S_NOVI */
-
-/* called from main.c */
-void
-x_init(void)
-{
-	int i, j;
-
-	/*
-	 * Set edchars to -2 to force initial binding, except
-	 * we need default values for some deficient systems…
-	 */
-	edchars.erase = edchars.kill = edchars.intr = edchars.quit =
-	    edchars.eof = -2;
-	/* ^W */
-	edchars.werase = 027;
-
-	/* command line editing specific memory allocation */
-	ainit(AEDIT);
-	holdbufp = alloc(LINE, AEDIT);
-
-	/* initialise Emacs command line editing mode */
-	x_nextcmd = -1;
-
-	x_tab = alloc2(X_NTABS, sizeof(*x_tab), AEDIT);
-	for (j = 0; j < X_TABSZ; j++)
-		x_tab[0][j] = XFUNC_insert;
-	for (i = 1; i < X_NTABS; i++)
-		for (j = 0; j < X_TABSZ; j++)
-			x_tab[i][j] = XFUNC_error;
-	for (i = 0; i < (int)NELEM(x_defbindings); i++)
-		x_tab[x_defbindings[i].xdb_tab][x_defbindings[i].xdb_char]
-		    = x_defbindings[i].xdb_func;
-
-#ifndef MKSH_SMALL
-	x_atab = alloc2(X_NTABS, sizeof(*x_atab), AEDIT);
-	for (i = 1; i < X_NTABS; i++)
-		for (j = 0; j < X_TABSZ; j++)
-			x_atab[i][j] = NULL;
-#endif
-}
-
-#ifdef DEBUG_LEAKS
-void
-x_done(void)
-{
-	if (x_tab != NULL)
-		afreeall(AEDIT);
-}
-#endif
-#endif /* !MKSH_NO_CMDLINE_EDITING */

Copied: vendor/MirOS/mksh/R50/edit.c (from rev 6707, vendor/MirOS/mksh/dist/edit.c)
===================================================================
--- vendor/MirOS/mksh/R50/edit.c	                        (rev 0)
+++ vendor/MirOS/mksh/R50/edit.c	2014-07-01 12:01:17 UTC (rev 6708)
@@ -0,0 +1,5502 @@
+/*	$OpenBSD: edit.c,v 1.39 2013/12/17 16:37:05 deraadt Exp $	*/
+/*	$OpenBSD: edit.h,v 1.9 2011/05/30 17:14:35 martynas Exp $	*/
+/*	$OpenBSD: emacs.c,v 1.48 2013/12/17 16:37:05 deraadt Exp $	*/
+/*	$OpenBSD: vi.c,v 1.28 2013/12/18 16:45:46 deraadt Exp $	*/
+
+/*-
+ * Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
+ *		 2011, 2012, 2013, 2014
+ *	Thorsten Glaser <tg at mirbsd.org>
+ *
+ * Provided that these terms and disclaimer and all copyright notices
+ * are retained or reproduced in an accompanying document, permission
+ * is granted to deal in this work without restriction, including un-
+ * limited rights to use, publicly perform, distribute, sell, modify,
+ * merge, give away, or sublicence.
+ *
+ * This work is provided "AS IS" and WITHOUT WARRANTY of any kind, to
+ * the utmost extent permitted by applicable law, neither express nor
+ * implied; without malicious intent or gross negligence. In no event
+ * may a licensor, author or contributor be held liable for indirect,
+ * direct, other damage, loss, or other issues arising in any way out
+ * of dealing in the work, even if advised of the possibility of such
+ * damage or existence of a defect, except proven that it results out
+ * of said person's immediate fault when using the work as intended.
+ */
+
+#include "sh.h"
+
+#ifndef MKSH_NO_CMDLINE_EDITING
+
+__RCSID("$MirOS: src/bin/mksh/edit.c,v 1.275 2014/01/05 21:57:24 tg Exp $");
+
+/*
+ * in later versions we might use libtermcap for this, but since external
+ * dependencies are problematic, this has not yet been decided on; another
+ * good string is "\033c" except on hardware terminals like the DEC VT420
+ * which do a full power cycle then...
+ */
+#ifndef MKSH_CLS_STRING
+#define MKSH_CLS_STRING		"\033[;H\033[J"
+#endif
+#ifndef MKSH_CLRTOEOL_STRING
+#define MKSH_CLRTOEOL_STRING	"\033[K"
+#endif
+
+/* tty driver characters we are interested in */
+typedef struct {
+	int erase;
+	int kill;
+	int werase;
+	int intr;
+	int quit;
+	int eof;
+} X_chars;
+
+static X_chars edchars;
+
+/* x_cf_glob() flags */
+#define XCF_COMMAND	BIT(0)	/* Do command completion */
+#define XCF_FILE	BIT(1)	/* Do file completion */
+#define XCF_FULLPATH	BIT(2)	/* command completion: store full path */
+#define XCF_COMMAND_FILE (XCF_COMMAND | XCF_FILE)
+#define XCF_IS_COMMAND	BIT(3)	/* return flag: is command */
+#define XCF_IS_NOSPACE	BIT(4)	/* return flag: do not append a space */
+
+static char editmode;
+static int xx_cols;			/* for Emacs mode */
+static int modified;			/* buffer has been "modified" */
+static char *holdbufp;			/* place to hold last edit buffer */
+
+static int x_getc(void);
+static void x_putcf(int);
+static void x_modified(void);
+static void x_mode(bool);
+static int x_do_comment(char *, ssize_t, ssize_t *);
+static void x_print_expansions(int, char * const *, bool);
+static int x_cf_glob(int *, const char *, int, int, int *, int *, char ***);
+static size_t x_longest_prefix(int, char * const *);
+static void x_glob_hlp_add_qchar(char *);
+static char *x_glob_hlp_tilde_and_rem_qchar(char *, bool);
+static int x_basename(const char *, const char *);
+static void x_free_words(int, char **);
+static int x_escape(const char *, size_t, int (*)(const char *, size_t));
+static int x_emacs(char *);
+static void x_init_prompt(bool);
+#if !MKSH_S_NOVI
+static int x_vi(char *);
+#endif
+
+#define x_flush()	shf_flush(shl_out)
+#if defined(MKSH_SMALL) && !defined(MKSH_SMALL_BUT_FAST)
+#define x_putc(c)	x_putcf(c)
+#else
+#define x_putc(c)	shf_putc((c), shl_out)
+#endif
+
+static int path_order_cmp(const void *, const void *);
+static void glob_table(const char *, XPtrV *, struct table *);
+static void glob_path(int, const char *, XPtrV *, const char *);
+static int x_file_glob(int *, char *, char ***);
+static int x_command_glob(int, char *, char ***);
+static int x_locate_word(const char *, int, int, int *, bool *);
+
+static int x_e_getmbc(char *);
+static int x_e_rebuildline(const char *);
+
+/* +++ generic editing functions +++ */
+
+/*
+ * read an edited command line
+ */
+int
+x_read(char *buf)
+{
+	int i;
+
+	x_mode(true);
+	modified = 1;
+	if (Flag(FEMACS) || Flag(FGMACS))
+		i = x_emacs(buf);
+#if !MKSH_S_NOVI
+	else if (Flag(FVI))
+		i = x_vi(buf);
+#endif
+	else
+		/* internal error */
+		i = -1;
+	editmode = 0;
+	x_mode(false);
+	return (i);
+}
+
+/* tty I/O */
+
+static int
+x_getc(void)
+{
+	char c;
+	ssize_t n;
+
+	while ((n = blocking_read(STDIN_FILENO, &c, 1)) < 0 && errno == EINTR)
+		if (trap) {
+			x_mode(false);
+			runtraps(0);
+#ifdef SIGWINCH
+			if (got_winch) {
+				change_winsz();
+				if (x_cols != xx_cols && editmode == 1) {
+					/* redraw line in Emacs mode */
+					xx_cols = x_cols;
+					x_init_prompt(false);
+					x_e_rebuildline(MKSH_CLRTOEOL_STRING);
+				}
+			}
+#endif
+			x_mode(true);
+		}
+	return ((n == 1) ? (int)(unsigned char)c : -1);
+}
+
+static void
+x_putcf(int c)
+{
+	shf_putc(c, shl_out);
+}
+
+/*********************************
+ * Misc common code for vi/emacs *
+ *********************************/
+
+/*-
+ * Handle the commenting/uncommenting of a line.
+ * Returns:
+ *	1 if a carriage return is indicated (comment added)
+ *	0 if no return (comment removed)
+ *	-1 if there is an error (not enough room for comment chars)
+ * If successful, *lenp contains the new length. Note: cursor should be
+ * moved to the start of the line after (un)commenting.
+ */
+static int
+x_do_comment(char *buf, ssize_t bsize, ssize_t *lenp)
+{
+	ssize_t i, j, len = *lenp;
+
+	if (len == 0)
+		/* somewhat arbitrary - it's what AT&T ksh does */
+		return (1);
+
+	/* Already commented? */
+	if (buf[0] == '#') {
+		bool saw_nl = false;
+
+		for (j = 0, i = 1; i < len; i++) {
+			if (!saw_nl || buf[i] != '#')
+				buf[j++] = buf[i];
+			saw_nl = buf[i] == '\n';
+		}
+		*lenp = j;
+		return (0);
+	} else {
+		int n = 1;
+
+		/* See if there's room for the #s - 1 per \n */
+		for (i = 0; i < len; i++)
+			if (buf[i] == '\n')
+				n++;
+		if (len + n >= bsize)
+			return (-1);
+		/* Now add them... */
+		for (i = len, j = len + n; --i >= 0; ) {
+			if (buf[i] == '\n')
+				buf[--j] = '#';
+			buf[--j] = buf[i];
+		}
+		buf[0] = '#';
+		*lenp += n;
+		return (1);
+	}
+}
+
+/****************************************************
+ * Common file/command completion code for vi/emacs *
+ ****************************************************/
+
+static void
+x_print_expansions(int nwords, char * const *words, bool is_command)
+{
+	bool use_copy = false;
+	int prefix_len;
+	XPtrV l = { NULL, 0, 0 };
+
+	/*
+	 * Check if all matches are in the same directory (in this
+	 * case, we want to omit the directory name)
+	 */
+	if (!is_command &&
+	    (prefix_len = x_longest_prefix(nwords, words)) > 0) {
+		int i;
+
+		/* Special case for 1 match (prefix is whole word) */
+		if (nwords == 1)
+			prefix_len = x_basename(words[0], NULL);
+		/* Any (non-trailing) slashes in non-common word suffixes? */
+		for (i = 0; i < nwords; i++)
+			if (x_basename(words[i] + prefix_len, NULL) >
+			    prefix_len)
+				break;
+		/* All in same directory? */
+		if (i == nwords) {
+			while (prefix_len > 0 && words[0][prefix_len - 1] != '/')
+				prefix_len--;
+			use_copy = true;
+			XPinit(l, nwords + 1);
+			for (i = 0; i < nwords; i++)
+				XPput(l, words[i] + prefix_len);
+			XPput(l, NULL);
+		}
+	}
+	/*
+	 * Enumerate expansions
+	 */
+	x_putc('\r');
+	x_putc('\n');
+	pr_list(use_copy ? (char **)XPptrv(l) : words);
+
+	if (use_copy)
+		/* not x_free_words() */
+		XPfree(l);
+}
+
+/*
+ * Convert backslash-escaped string to QCHAR-escaped
+ * string useful for globbing; loses QCHAR unless it
+ * can squeeze in, eg. by previous loss of backslash
+ */
+static void
+x_glob_hlp_add_qchar(char *cp)
+{
+	char ch, *dp = cp;
+	bool escaping = false;
+
+	while ((ch = *cp++)) {
+		if (ch == '\\' && !escaping) {
+			escaping = true;
+			continue;
+		}
+		if (escaping || (ch == QCHAR && (cp - dp) > 1)) {
+			/*
+			 * empirically made list of chars to escape
+			 * for globbing as well as QCHAR itself
+			 */
+			switch (ch) {
+			case QCHAR:
+			case '$':
+			case '*':
+			case '?':
+			case '[':
+			case '\\':
+			case '`':
+				*dp++ = QCHAR;
+				break;
+			}
+			escaping = false;
+		}
+		*dp++ = ch;
+	}
+	*dp = '\0';
+}
+
+/*
+ * Run tilde expansion on argument string, return the result
+ * after unescaping; if the flag is set, the original string
+ * is freed if changed and assumed backslash-escaped, if not
+ * it is assumed QCHAR-escaped
+ */
+static char *
+x_glob_hlp_tilde_and_rem_qchar(char *s, bool magic_flag)
+{
+	char ch, *cp, *dp;
+
+	/*
+	 * On the string, check whether we have a tilde expansion,
+	 * and if so, discern "~foo/bar" and "~/baz" from "~blah";
+	 * if we have a directory part (the former), try to expand
+	 */
+	if (*s == '~' && (cp = strchr(s, '/')) != NULL) {
+		/* ok, so split into "~foo"/"bar" or "~"/"baz" */
+		*cp++ = 0;
+		/* try to expand the tilde */
+		if (!(dp = tilde(s + 1))) {
+			/* nope, revert damage */
+			*--cp = '/';
+		} else {
+			/* ok, expand and replace */
+			cp = shf_smprintf("%s/%s", dp, cp);
+			if (magic_flag)
+				afree(s, ATEMP);
+			s = cp;
+		}
+	}
+
+	/* ... convert it from backslash-escaped via QCHAR-escaped... */
+	if (magic_flag)
+		x_glob_hlp_add_qchar(s);
+	/* ... to unescaped, for comparison with the matches */
+	cp = dp = s;
+
+	while ((ch = *cp++)) {
+		if (ch == QCHAR && !(ch = *cp++))
+			break;
+		*dp++ = ch;
+	}
+	*dp = '\0';
+
+	return (s);
+}
+
+/**
+ * Do file globbing:
+ *	- does expansion, checks for no match, etc.
+ *	- sets *wordsp to array of matching strings
+ *	- returns number of matching strings
+ */
+static int
+x_file_glob(int *flagsp, char *toglob, char ***wordsp)
+{
+	char **words, *cp;
+	int nwords;
+	XPtrV w;
+	struct source *s, *sold;
+
+	/* remove all escaping backward slashes */
+	x_glob_hlp_add_qchar(toglob);
+
+	/*
+	 * Convert "foo*" (toglob) to an array of strings (words)
+	 */
+	sold = source;
+	s = pushs(SWSTR, ATEMP);
+	s->start = s->str = toglob;
+	source = s;
+	if (yylex(ONEWORD | LQCHAR) != LWORD) {
+		source = sold;
+		internal_warningf("%s: %s", "fileglob", "bad substitution");
+		return (0);
+	}
+	source = sold;
+	afree(s, ATEMP);
+	XPinit(w, 32);
+	cp = yylval.cp;
+	while (*cp == CHAR || *cp == QCHAR)
+		cp += 2;
+	nwords = DOGLOB | DOTILDE | DOMARKDIRS;
+	if (*cp != EOS) {
+		/* probably a $FOO expansion */
+		*flagsp |= XCF_IS_NOSPACE;
+		/* this always results in at most one match */
+		nwords = 0;
+	}
+	expand(yylval.cp, &w, nwords);
+	XPput(w, NULL);
+	words = (char **)XPclose(w);
+
+	for (nwords = 0; words[nwords]; nwords++)
+		;
+	if (nwords == 1) {
+		struct stat statb;
+
+		/* Expand any tilde and drop all QCHAR for comparison */
+		toglob = x_glob_hlp_tilde_and_rem_qchar(toglob, false);
+
+		/*
+		 * Check if globbing failed (returned glob pattern),
+		 * but be careful (e.g. toglob == "ab*" when the file
+		 * "ab*" exists is not an error).
+		 * Also, check for empty result - happens if we tried
+		 * to glob something which evaluated to an empty
+		 * string (e.g., "$FOO" when there is no FOO, etc).
+		 */
+		if ((strcmp(words[0], toglob) == 0 &&
+		    stat(words[0], &statb) < 0) ||
+		    words[0][0] == '\0') {
+			x_free_words(nwords, words);
+			words = NULL;
+			nwords = 0;
+		}
+	}
+
+	if ((*wordsp = nwords ? words : NULL) == NULL && words != NULL)
+		x_free_words(nwords, words);
+
+	return (nwords);
+}
+
+/* Data structure used in x_command_glob() */
+struct path_order_info {
+	char *word;
+	int base;
+	int path_order;
+};
+
+/* Compare routine used in x_command_glob() */
+static int
+path_order_cmp(const void *aa, const void *bb)
+{
+	const struct path_order_info *a = (const struct path_order_info *)aa;
+	const struct path_order_info *b = (const struct path_order_info *)bb;
+	int t;
+
+	t = strcmp(a->word + a->base, b->word + b->base);
+	return (t ? t : a->path_order - b->path_order);
+}
+
+static int
+x_command_glob(int flags, char *toglob, char ***wordsp)
+{
+	char *pat, *fpath;
+	size_t nwords;
+	XPtrV w;
+	struct block *l;
+
+	/* Convert "foo*" (toglob) to a pattern for future use */
+	pat = evalstr(toglob, DOPAT | DOTILDE);
+
+	XPinit(w, 32);
+
+	glob_table(pat, &w, &keywords);
+	glob_table(pat, &w, &aliases);
+	glob_table(pat, &w, &builtins);
+	for (l = e->loc; l; l = l->next)
+		glob_table(pat, &w, &l->funs);
+
+	glob_path(flags, pat, &w, path);
+	if ((fpath = str_val(global("FPATH"))) != null)
+		glob_path(flags, pat, &w, fpath);
+
+	nwords = XPsize(w);
+
+	if (!nwords) {
+		*wordsp = NULL;
+		XPfree(w);
+		return (0);
+	}
+	/* Sort entries */
+	if (flags & XCF_FULLPATH) {
+		/* Sort by basename, then path order */
+		struct path_order_info *info, *last_info = NULL;
+		char **words = (char **)XPptrv(w);
+		size_t i, path_order = 0;
+
+		info = (struct path_order_info *)
+		    alloc2(nwords, sizeof(struct path_order_info), ATEMP);
+		for (i = 0; i < nwords; i++) {
+			info[i].word = words[i];
+			info[i].base = x_basename(words[i], NULL);
+			if (!last_info || info[i].base != last_info->base ||
+			    strncmp(words[i], last_info->word, info[i].base) != 0) {
+				last_info = &info[i];
+				path_order++;
+			}
+			info[i].path_order = path_order;
+		}
+		qsort(info, nwords, sizeof(struct path_order_info),
+		    path_order_cmp);
+		for (i = 0; i < nwords; i++)
+			words[i] = info[i].word;
+		afree(info, ATEMP);
+	} else {
+		/* Sort and remove duplicate entries */
+		char **words = (char **)XPptrv(w);
+		size_t i, j;
+
+		qsort(words, nwords, sizeof(void *), xstrcmp);
+		for (i = j = 0; i < nwords - 1; i++) {
+			if (strcmp(words[i], words[i + 1]))
+				words[j++] = words[i];
+			else
+				afree(words[i], ATEMP);
+		}
+		words[j++] = words[i];
+		w.len = nwords = j;
+	}
+
+	XPput(w, NULL);
+	*wordsp = (char **)XPclose(w);
+
+	return (nwords);
+}
+
+#define IS_WORDC(c)	(!ctype(c, C_LEX1) && (c) != '\'' && (c) != '"' && \
+			    (c) != '`' && (c) != '=' && (c) != ':')
+
+static int
+x_locate_word(const char *buf, int buflen, int pos, int *startp,
+    bool *is_commandp)
+{
+	int start, end;
+
+	/* Bad call? Probably should report error */
+	if (pos < 0 || pos > buflen) {
+		*startp = pos;
+		*is_commandp = false;
+		return (0);
+	}
+	/* The case where pos == buflen happens to take care of itself... */
+
+	start = pos;
+	/*
+	 * Keep going backwards to start of word (has effect of allowing
+	 * one blank after the end of a word)
+	 */
+	for (; (start > 0 && IS_WORDC(buf[start - 1])) ||
+	    (start > 1 && buf[start - 2] == '\\'); start--)
+		;
+	/* Go forwards to end of word */
+	for (end = start; end < buflen && IS_WORDC(buf[end]); end++) {
+		if (buf[end] == '\\' && (end + 1) < buflen)
+			end++;
+	}
+
+	if (is_commandp) {
+		bool iscmd;
+		int p = start - 1;
+
+		/* Figure out if this is a command */
+		while (p >= 0 && ksh_isspace(buf[p]))
+			p--;
+		iscmd = p < 0 || vstrchr(";|&()`", buf[p]);
+		if (iscmd) {
+			/*
+			 * If command has a /, path, etc. is not searched;
+			 * only current directory is searched which is just
+			 * like file globbing.
+			 */
+			for (p = start; p < end; p++)
+				if (buf[p] == '/')
+					break;
+			iscmd = p == end;
+		}
+		*is_commandp = iscmd;
+	}
+	*startp = start;
+
+	return (end - start);
+}
+
+static int
+x_cf_glob(int *flagsp, const char *buf, int buflen, int pos, int *startp,
+    int *endp, char ***wordsp)
+{
+	int len, nwords = 0;
+	char **words = NULL;
+	bool is_command;
+
+	mkssert(buf != NULL);
+
+	len = x_locate_word(buf, buflen, pos, startp, &is_command);
+	if (!((*flagsp) & XCF_COMMAND))
+		is_command = false;
+	/*
+	 * Don't do command globing on zero length strings - it takes too
+	 * long and isn't very useful. File globs are more likely to be
+	 * useful, so allow these.
+	 */
+	if (len == 0 && is_command)
+		return (0);
+
+	if (len >= 0) {
+		char *toglob, *s;
+
+		/*
+		 * Given a string, copy it and possibly add a '*' to the end.
+		 */
+
+		strndupx(toglob, buf + *startp, len + /* the '*' */ 1, ATEMP);
+		toglob[len] = '\0';
+
+		/*
+		 * If the pathname contains a wildcard (an unquoted '*',
+		 * '?', or '[') or an extglob, then it is globbed based
+		 * on that value (i.e., without the appended '*'). Same
+		 * for parameter substitutions (as in “cat $HOME/.ss↹”)
+		 * without appending a trailing space (LP: #710539), as
+		 * well as for “~foo” (but not “~foo/”).
+		 */
+		for (s = toglob; *s; s++) {
+			if (*s == '\\' && s[1])
+				s++;
+			else if (*s == '?' || *s == '*' || *s == '[' ||
+			    *s == '$' ||
+			    /* ?() *() +() @() !() but two already checked */
+			    (s[1] == '(' /*)*/ &&
+			    (*s == '+' || *s == '@' || *s == '!'))) {
+				/*
+				 * just expand based on the extglob
+				 * or parameter
+				 */
+				goto dont_add_glob;
+			}
+		}
+
+		if (*toglob == '~' && !vstrchr(toglob, '/')) {
+			/* neither for '~foo' (but '~foo/bar') */
+			*flagsp |= XCF_IS_NOSPACE;
+			goto dont_add_glob;
+		}
+
+		/* append a glob */
+		toglob[len] = '*';
+		toglob[len + 1] = '\0';
+ dont_add_glob:
+		/*
+		 * Expand (glob) it now.
+		 */
+
+		nwords = is_command ?
+		    x_command_glob(*flagsp, toglob, &words) :
+		    x_file_glob(flagsp, toglob, &words);
+		afree(toglob, ATEMP);
+	}
+	if (nwords == 0) {
+		*wordsp = NULL;
+		return (0);
+	}
+	if (is_command)
+		*flagsp |= XCF_IS_COMMAND;
+	*wordsp = words;
+	*endp = *startp + len;
+
+	return (nwords);
+}
+
+/*
+ * Find longest common prefix
+ */
+static size_t
+x_longest_prefix(int nwords, char * const * words)
+{
+	int i;
+	size_t j, prefix_len;
+	char *p;
+
+	if (nwords <= 0)
+		return (0);
+
+	prefix_len = strlen(words[0]);
+	for (i = 1; i < nwords; i++)
+		for (j = 0, p = words[i]; j < prefix_len; j++)
+			if (p[j] != words[0][j]) {
+				prefix_len = j;
+				break;
+			}
+	/* false for nwords==1 as 0 = words[0][prefix_len] then */
+	if (UTFMODE && prefix_len && (words[0][prefix_len] & 0xC0) == 0x80)
+		while (prefix_len && (words[0][prefix_len] & 0xC0) != 0xC0)
+			--prefix_len;
+	return (prefix_len);
+}
+
+static void
+x_free_words(int nwords, char **words)
+{
+	while (nwords)
+		afree(words[--nwords], ATEMP);
+	afree(words, ATEMP);
+}
+
+/*-
+ * Return the offset of the basename of string s (which ends at se - need not
+ * be null terminated). Trailing slashes are ignored. If s is just a slash,
+ * then the offset is 0 (actually, length - 1).
+ *	s		Return
+ *	/etc		1
+ *	/etc/		1
+ *	/etc//		1
+ *	/etc/fo		5
+ *	foo		0
+ *	///		2
+ *			0
+ */
+static int
+x_basename(const char *s, const char *se)
+{
+	const char *p;
+
+	if (se == NULL)
+		se = s + strlen(s);
+	if (s == se)
+		return (0);
+
+	/* Skip trailing slashes */
+	for (p = se - 1; p > s && *p == '/'; p--)
+		;
+	for (; p > s && *p != '/'; p--)
+		;
+	if (*p == '/' && p + 1 < se)
+		p++;
+
+	return (p - s);
+}
+
+/*
+ * Apply pattern matching to a table: all table entries that match a pattern
+ * are added to wp.
+ */
+static void
+glob_table(const char *pat, XPtrV *wp, struct table *tp)
+{
+	struct tstate ts;
+	struct tbl *te;
+
+	ktwalk(&ts, tp);
+	while ((te = ktnext(&ts)))
+		if (gmatchx(te->name, pat, false)) {
+			char *cp;
+
+			strdupx(cp, te->name, ATEMP);
+			XPput(*wp, cp);
+		}
+}
+
+static void
+glob_path(int flags, const char *pat, XPtrV *wp, const char *lpath)
+{
+	const char *sp = lpath, *p;
+	char *xp, **words;
+	size_t pathlen, patlen, oldsize, newsize, i, j;
+	XString xs;
+
+	patlen = strlen(pat);
+	checkoktoadd(patlen, 129 + X_EXTRA);
+	++patlen;
+	Xinit(xs, xp, patlen + 128, ATEMP);
+	while (sp) {
+		xp = Xstring(xs, xp);
+		if (!(p = cstrchr(sp, ':')))
+			p = sp + strlen(sp);
+		pathlen = p - sp;
+		if (pathlen) {
+			/*
+			 * Copy sp into xp, stuffing any MAGIC characters
+			 * on the way
+			 */
+			const char *s = sp;
+
+			XcheckN(xs, xp, pathlen * 2);
+			while (s < p) {
+				if (ISMAGIC(*s))
+					*xp++ = MAGIC;
+				*xp++ = *s++;
+			}
+			*xp++ = '/';
+			pathlen++;
+		}
+		sp = p;
+		XcheckN(xs, xp, patlen);
+		memcpy(xp, pat, patlen);
+
+		oldsize = XPsize(*wp);
+		/* mark dirs */
+		glob_str(Xstring(xs, xp), wp, true);
+		newsize = XPsize(*wp);
+
+		/* Check that each match is executable... */
+		words = (char **)XPptrv(*wp);
+		for (i = j = oldsize; i < newsize; i++) {
+			if (ksh_access(words[i], X_OK) == 0) {
+				words[j] = words[i];
+				if (!(flags & XCF_FULLPATH))
+					memmove(words[j], words[j] + pathlen,
+					    strlen(words[j] + pathlen) + 1);
+				j++;
+			} else
+				afree(words[i], ATEMP);
+		}
+		wp->len = j;
+
+		if (!*sp++)
+			break;
+	}
+	Xfree(xs, xp);
+}
+
+/*
+ * if argument string contains any special characters, they will
+ * be escaped and the result will be put into edit buffer by
+ * keybinding-specific function
+ */
+static int
+x_escape(const char *s, size_t len, int (*putbuf_func)(const char *, size_t))
+{
+	size_t add = 0, wlen = len;
+	const char *ifs = str_val(local("IFS", 0));
+	int rval = 0;
+
+	while (wlen - add > 0)
+		if (vstrchr("\"#$&'()*:;<=>?[\\`{|}", s[add]) ||
+		    vstrchr(ifs, s[add])) {
+			if (putbuf_func(s, add) != 0) {
+				rval = -1;
+				break;
+			}
+			putbuf_func(s[add] == '\n' ? "'" : "\\", 1);
+			putbuf_func(&s[add], 1);
+			if (s[add] == '\n')
+				putbuf_func("'", 1);
+
+			add++;
+			wlen -= add;
+			s += add;
+			add = 0;
+		} else
+			++add;
+	if (wlen > 0 && rval == 0)
+		rval = putbuf_func(s, wlen);
+
+	return (rval);
+}
+
+
+/* +++ emacs editing mode +++ */
+
+static	Area	aedit;
+#define	AEDIT	&aedit		/* area for kill ring and macro defns */
+
+/* values returned by keyboard functions */
+#define	KSTD	0
+#define	KEOL	1		/* ^M, ^J */
+#define	KINTR	2		/* ^G, ^C */
+
+struct x_ftab {
+	int (*xf_func)(int c);
+	const char *xf_name;
+	short xf_flags;
+};
+
+struct x_defbindings {
+	unsigned char xdb_func;	/* XFUNC_* */
+	unsigned char xdb_tab;
+	unsigned char xdb_char;
+};
+
+#define XF_ARG		1	/* command takes number prefix */
+#define	XF_NOBIND	2	/* not allowed to bind to function */
+#define	XF_PREFIX	4	/* function sets prefix */
+
+/* Separator for completion */
+#define	is_cfs(c)	((c) == ' ' || (c) == '\t' || (c) == '"' || (c) == '\'')
+/* Separator for motion */
+#define	is_mfs(c)	(!(ksh_isalnux(c) || (c) == '$' || ((c) & 0x80)))
+
+#define X_NTABS		3			/* normal, meta1, meta2 */
+#define X_TABSZ		256			/* size of keydef tables etc */
+
+/*-
+ * Arguments for do_complete()
+ * 0 = enumerate	M-=	complete as much as possible and then list
+ * 1 = complete		M-Esc
+ * 2 = list		M-?
+ */
+typedef enum {
+	CT_LIST,	/* list the possible completions */
+	CT_COMPLETE,	/* complete to longest prefix */
+	CT_COMPLIST	/* complete and then list (if non-exact) */
+} Comp_type;
+
+/*
+ * The following are used for my horizontal scrolling stuff
+ */
+static char *xbuf;		/* beg input buffer */
+static char *xend;		/* end input buffer */
+static char *xcp;		/* current position */
+static char *xep;		/* current end */
+static char *xbp;		/* start of visible portion of input buffer */
+static char *xlp;		/* last char visible on screen */
+static bool x_adj_ok;
+/*
+ * we use x_adj_done so that functions can tell
+ * whether x_adjust() has been called while they are active.
+ */
+static int x_adj_done;		/* is incremented by x_adjust() */
+
+static int x_displen;
+static int x_arg;		/* general purpose arg */
+static bool x_arg_defaulted;	/* x_arg not explicitly set; defaulted to 1 */
+
+static bool xlp_valid;		/* lastvis pointer was recalculated */
+
+static char **x_histp;		/* history position */
+static int x_nextcmd;		/* for newline-and-next */
+static char **x_histncp;	/* saved x_histp for " */
+static char *xmp;		/* mark pointer */
+static unsigned char x_last_command;
+static unsigned char (*x_tab)[X_TABSZ];	/* key definition */
+#ifndef MKSH_SMALL
+static char *(*x_atab)[X_TABSZ];	/* macro definitions */
+#endif
+static unsigned char x_bound[(X_TABSZ * X_NTABS + 7) / 8];
+#define KILLSIZE	20
+static char *killstack[KILLSIZE];
+static int killsp, killtp;
+static int x_curprefix;
+#ifndef MKSH_SMALL
+static char *macroptr;		/* bind key macro active? */
+#endif
+#if !MKSH_S_NOVI
+static int winwidth;		/* width of window */
+static char *wbuf[2];		/* window buffers */
+static int wbuf_len;		/* length of window buffers (x_cols - 3) */
+static int win;			/* window buffer in use */
+static char morec;		/* more character at right of window */
+static int lastref;		/* argument to last refresh() */
+static int holdlen;		/* length of holdbuf */
+#endif
+static int pwidth;		/* width of prompt */
+static int prompt_trunc;	/* how much of prompt to truncate or -1 */
+static int x_col;		/* current column on line */
+
+static int x_ins(const char *);
+static void x_delete(size_t, bool);
+static size_t x_bword(void);
+static size_t x_fword(bool);
+static void x_goto(char *);
+static char *x_bs0(char *, char *) MKSH_A_PURE;
+static void x_bs3(char **);
+static int x_size_str(char *);
+static int x_size2(char *, char **);
+static void x_zots(char *);
+static void x_zotc3(char **);
+static void x_load_hist(char **);
+static int x_search(char *, int, int);
+#ifndef MKSH_SMALL
+static int x_search_dir(int);
+#endif
+static int x_match(char *, char *);
+static void x_redraw(int);
+static void x_push(int);
+static char *x_mapin(const char *, Area *);
+static char *x_mapout(int);
+static void x_mapout2(int, char **);
+static void x_print(int, int);
+static void x_adjust(void);
+static void x_e_ungetc(int);
+static int x_e_getc(void);
+static void x_e_putc2(int);
+static void x_e_putc3(const char **);
+static void x_e_puts(const char *);
+#ifndef MKSH_SMALL
+static int x_fold_case(int);
+#endif
+static char *x_lastcp(void);
+static void do_complete(int, Comp_type);
+static size_t x_nb2nc(size_t) MKSH_A_PURE;
+
+static int unget_char = -1;
+
+static int x_do_ins(const char *, size_t);
+static void bind_if_not_bound(int, int, int);
+
+enum emacs_funcs {
+#define EMACSFN_ENUMS
+#include "emacsfn.h"
+	XFUNC_MAX
+};
+
+#define EMACSFN_DEFNS
+#include "emacsfn.h"
+
+static const struct x_ftab x_ftab[] = {
+#define EMACSFN_ITEMS
+#include "emacsfn.h"
+	{ 0, NULL, 0 }
+};
+
+static struct x_defbindings const x_defbindings[] = {
+	{ XFUNC_del_back,		0, CTRL('?')	},
+	{ XFUNC_del_bword,		1, CTRL('?')	},
+	{ XFUNC_eot_del,		0, CTRL('D')	},
+	{ XFUNC_del_back,		0, CTRL('H')	},
+	{ XFUNC_del_bword,		1, CTRL('H')	},
+	{ XFUNC_del_bword,		1,	'h'	},
+	{ XFUNC_mv_bword,		1,	'b'	},
+	{ XFUNC_mv_fword,		1,	'f'	},
+	{ XFUNC_del_fword,		1,	'd'	},
+	{ XFUNC_mv_back,		0, CTRL('B')	},
+	{ XFUNC_mv_forw,		0, CTRL('F')	},
+	{ XFUNC_search_char_forw,	0, CTRL(']')	},
+	{ XFUNC_search_char_back,	1, CTRL(']')	},
+	{ XFUNC_newline,		0, CTRL('M')	},
+	{ XFUNC_newline,		0, CTRL('J')	},
+	{ XFUNC_end_of_text,		0, CTRL('_')	},
+	{ XFUNC_abort,			0, CTRL('G')	},
+	{ XFUNC_prev_com,		0, CTRL('P')	},
+	{ XFUNC_next_com,		0, CTRL('N')	},
+	{ XFUNC_nl_next_com,		0, CTRL('O')	},
+	{ XFUNC_search_hist,		0, CTRL('R')	},
+	{ XFUNC_beg_hist,		1,	'<'	},
+	{ XFUNC_end_hist,		1,	'>'	},
+	{ XFUNC_goto_hist,		1,	'g'	},
+	{ XFUNC_mv_end,			0, CTRL('E')	},
+	{ XFUNC_mv_begin,		0, CTRL('A')	},
+	{ XFUNC_draw_line,		0, CTRL('L')	},
+	{ XFUNC_cls,			1, CTRL('L')	},
+	{ XFUNC_meta1,			0, CTRL('[')	},
+	{ XFUNC_meta2,			0, CTRL('X')	},
+	{ XFUNC_kill,			0, CTRL('K')	},
+	{ XFUNC_yank,			0, CTRL('Y')	},
+	{ XFUNC_meta_yank,		1,	'y'	},
+	{ XFUNC_literal,		0, CTRL('^')	},
+	{ XFUNC_comment,		1,	'#'	},
+	{ XFUNC_transpose,		0, CTRL('T')	},
+	{ XFUNC_complete,		1, CTRL('[')	},
+	{ XFUNC_comp_list,		0, CTRL('I')	},
+	{ XFUNC_comp_list,		1,	'='	},
+	{ XFUNC_enumerate,		1,	'?'	},
+	{ XFUNC_expand,			1,	'*'	},
+	{ XFUNC_comp_file,		1, CTRL('X')	},
+	{ XFUNC_comp_comm,		2, CTRL('[')	},
+	{ XFUNC_list_comm,		2,	'?'	},
+	{ XFUNC_list_file,		2, CTRL('Y')	},
+	{ XFUNC_set_mark,		1,	' '	},
+	{ XFUNC_kill_region,		0, CTRL('W')	},
+	{ XFUNC_xchg_point_mark,	2, CTRL('X')	},
+	{ XFUNC_literal,		0, CTRL('V')	},
+	{ XFUNC_version,		1, CTRL('V')	},
+	{ XFUNC_prev_histword,		1,	'.'	},
+	{ XFUNC_prev_histword,		1,	'_'	},
+	{ XFUNC_set_arg,		1,	'0'	},
+	{ XFUNC_set_arg,		1,	'1'	},
+	{ XFUNC_set_arg,		1,	'2'	},
+	{ XFUNC_set_arg,		1,	'3'	},
+	{ XFUNC_set_arg,		1,	'4'	},
+	{ XFUNC_set_arg,		1,	'5'	},
+	{ XFUNC_set_arg,		1,	'6'	},
+	{ XFUNC_set_arg,		1,	'7'	},
+	{ XFUNC_set_arg,		1,	'8'	},
+	{ XFUNC_set_arg,		1,	'9'	},
+#ifndef MKSH_SMALL
+	{ XFUNC_fold_upper,		1,	'U'	},
+	{ XFUNC_fold_upper,		1,	'u'	},
+	{ XFUNC_fold_lower,		1,	'L'	},
+	{ XFUNC_fold_lower,		1,	'l'	},
+	{ XFUNC_fold_capitalise,	1,	'C'	},
+	{ XFUNC_fold_capitalise,	1,	'c'	},
+#endif
+	/*
+	 * These for ANSI arrow keys: arguablely shouldn't be here by
+	 * default, but its simpler/faster/smaller than using termcap
+	 * entries.
+	 */
+	{ XFUNC_meta2,			1,	'['	},
+	{ XFUNC_meta2,			1,	'O'	},
+	{ XFUNC_prev_com,		2,	'A'	},
+	{ XFUNC_next_com,		2,	'B'	},
+	{ XFUNC_mv_forw,		2,	'C'	},
+	{ XFUNC_mv_back,		2,	'D'	},
+#ifndef MKSH_SMALL
+	{ XFUNC_vt_hack,		2,	'1'	},
+	{ XFUNC_mv_begin | 0x80,	2,	'7'	},
+	{ XFUNC_mv_begin,		2,	'H'	},
+	{ XFUNC_mv_end | 0x80,		2,	'4'	},
+	{ XFUNC_mv_end | 0x80,		2,	'8'	},
+	{ XFUNC_mv_end,			2,	'F'	},
+	{ XFUNC_del_char | 0x80,	2,	'3'	},
+	{ XFUNC_search_hist_up | 0x80,	2,	'5'	},
+	{ XFUNC_search_hist_dn | 0x80,	2,	'6'	},
+	/* more non-standard ones */
+	{ XFUNC_edit_line,		2,	'e'	}
+#endif
+};
+
+static size_t
+x_nb2nc(size_t nb)
+{
+	char *cp;
+	size_t nc = 0;
+
+	for (cp = xcp; cp < (xcp + nb); ++nc)
+		cp += utf_ptradj(cp);
+	return (nc);
+}
+
+static void
+x_modified(void)
+{
+	if (!modified) {
+		x_histp = histptr + 1;
+		modified = 1;
+	}
+}
+
+#ifdef MKSH_SMALL
+#define XFUNC_VALUE(f) (f)
+#else
+#define XFUNC_VALUE(f) (f & 0x7F)
+#endif
+
+static int
+x_e_getmbc(char *sbuf)
+{
+	int c, pos = 0;
+	unsigned char *buf = (unsigned char *)sbuf;
+
+	memset(buf, 0, 4);
+	buf[pos++] = c = x_e_getc();
+	if (c == -1)
+		return (-1);
+	if (UTFMODE) {
+		if ((buf[0] >= 0xC2) && (buf[0] < 0xF0)) {
+			c = x_e_getc();
+			if (c == -1)
+				return (-1);
+			if ((c & 0xC0) != 0x80) {
+				x_e_ungetc(c);
+				return (1);
+			}
+			buf[pos++] = c;
+		}
+		if ((buf[0] >= 0xE0) && (buf[0] < 0xF0)) {
+			/* XXX x_e_ungetc is one-octet only */
+			buf[pos++] = c = x_e_getc();
+			if (c == -1)
+				return (-1);
+		}
+	}
+	return (pos);
+}
+
+static void
+x_init_prompt(bool doprint)
+{
+	prompt_trunc = pprompt(prompt, doprint ? 0 : -1);
+	pwidth = prompt_trunc % x_cols;
+	prompt_trunc -= pwidth;
+	if ((mksh_uari_t)pwidth > ((mksh_uari_t)x_cols - 3 - MIN_EDIT_SPACE)) {
+		/* force newline after prompt */
+		prompt_trunc = -1;
+		pwidth = 0;
+		if (doprint)
+			x_e_putc2('\n');
+	}
+}
+
+static int
+x_emacs(char *buf)
+{
+	int c, i;
+	unsigned char f;
+
+	xbp = xbuf = buf;
+	xend = buf + LINE;
+	xlp = xcp = xep = buf;
+	*xcp = 0;
+	xlp_valid = true;
+	xmp = NULL;
+	x_curprefix = 0;
+	x_histp = histptr + 1;
+	x_last_command = XFUNC_error;
+
+	x_init_prompt(true);
+	x_displen = (xx_cols = x_cols) - 2 - (x_col = pwidth);
+	x_adj_done = 0;
+	x_adj_ok = true;
+
+	x_histncp = NULL;
+	if (x_nextcmd >= 0) {
+		int off = source->line - x_nextcmd;
+		if (histptr - history >= off) {
+			x_load_hist(histptr - off);
+			x_histncp = x_histp;
+		}
+		x_nextcmd = -1;
+	}
+	editmode = 1;
+	while (/* CONSTCOND */ 1) {
+		x_flush();
+		if ((c = x_e_getc()) < 0)
+			return (0);
+
+		f = x_curprefix == -1 ? XFUNC_insert :
+		    x_tab[x_curprefix][c];
+#ifndef MKSH_SMALL
+		if (f & 0x80) {
+			f &= 0x7F;
+			if ((i = x_e_getc()) != '~')
+				x_e_ungetc(i);
+		}
+
+		/* avoid bind key macro recursion */
+		if (macroptr && f == XFUNC_ins_string)
+			f = XFUNC_insert;
+#endif
+
+		if (!(x_ftab[f].xf_flags & XF_PREFIX) &&
+		    x_last_command != XFUNC_set_arg) {
+			x_arg = 1;
+			x_arg_defaulted = true;
+		}
+		i = c | (x_curprefix << 8);
+		x_curprefix = 0;
+		switch ((*x_ftab[f].xf_func)(i)) {
+		case KSTD:
+			if (!(x_ftab[f].xf_flags & XF_PREFIX))
+				x_last_command = f;
+			break;
+		case KEOL:
+			i = xep - xbuf;
+			return (i);
+		case KINTR:
+			/* special case for interrupt */
+			trapsig(SIGINT);
+			x_mode(false);
+			unwind(LSHELL);
+		}
+		/* ad-hoc hack for fixing the cursor position */
+		x_goto(xcp);
+	}
+}
+
+static int
+x_insert(int c)
+{
+	static int left, pos, save_arg;
+	static char str[4];
+
+	/*
+	 * Should allow tab and control chars.
+	 */
+	if (c == 0) {
+ invmbs:
+		left = 0;
+		x_e_putc2(7);
+		return (KSTD);
+	}
+	if (UTFMODE) {
+		if (((c & 0xC0) == 0x80) && left) {
+			str[pos++] = c;
+			if (!--left) {
+				str[pos] = '\0';
+				x_arg = save_arg;
+				while (x_arg--)
+					x_ins(str);
+			}
+			return (KSTD);
+		}
+		if (left) {
+			if (x_curprefix == -1) {
+				/* flush invalid multibyte */
+				str[pos] = '\0';
+				while (save_arg--)
+					x_ins(str);
+			}
+		}
+		if ((c >= 0xC2) && (c < 0xE0))
+			left = 1;
+		else if ((c >= 0xE0) && (c < 0xF0))
+			left = 2;
+		else if (c > 0x7F)
+			goto invmbs;
+		else
+			left = 0;
+		if (left) {
+			save_arg = x_arg;
+			pos = 1;
+			str[0] = c;
+			return (KSTD);
+		}
+	}
+	left = 0;
+	str[0] = c;
+	str[1] = '\0';
+	while (x_arg--)
+		x_ins(str);
+	return (KSTD);
+}
+
+#ifndef MKSH_SMALL
+static int
+x_ins_string(int c)
+{
+	macroptr = x_atab[c >> 8][c & 255];
+	/*
+	 * we no longer need to bother checking if macroptr is
+	 * not NULL but first char is NUL; x_e_getc() does it
+	 */
+	return (KSTD);
+}
+#endif
+
+static int
+x_do_ins(const char *cp, size_t len)
+{
+	if (xep + len >= xend) {
+		x_e_putc2(7);
+		return (-1);
+	}
+	memmove(xcp + len, xcp, xep - xcp + 1);
+	memmove(xcp, cp, len);
+	xcp += len;
+	xep += len;
+	x_modified();
+	return (0);
+}
+
+static int
+x_ins(const char *s)
+{
+	char *cp = xcp;
+	int adj = x_adj_done;
+
+	if (x_do_ins(s, strlen(s)) < 0)
+		return (-1);
+	/*
+	 * x_zots() may result in a call to x_adjust()
+	 * we want xcp to reflect the new position.
+	 */
+	xlp_valid = false;
+	x_lastcp();
+	x_adj_ok = tobool(xcp >= xlp);
+	x_zots(cp);
+	/* has x_adjust() been called? */
+	if (adj == x_adj_done) {
+		/* no */
+		cp = xlp;
+		while (cp > xcp)
+			x_bs3(&cp);
+	}
+	if (xlp == xep - 1)
+		x_redraw(xx_cols);
+	x_adj_ok = true;
+	return (0);
+}
+
+static int
+x_del_back(int c MKSH_A_UNUSED)
+{
+	ssize_t i = 0;
+
+	if (xcp == xbuf) {
+		x_e_putc2(7);
+		return (KSTD);
+	}
+	do {
+		x_goto(xcp - 1);
+	} while ((++i < x_arg) && (xcp != xbuf));
+	x_delete(i, false);
+	return (KSTD);
+}
+
+static int
+x_del_char(int c MKSH_A_UNUSED)
+{
+	char *cp, *cp2;
+	size_t i = 0;
+
+	cp = xcp;
+	while (i < (size_t)x_arg) {
+		utf_ptradjx(cp, cp2);
+		if (cp2 > xep)
+			break;
+		cp = cp2;
+		i++;
+	}
+
+	if (!i) {
+		x_e_putc2(7);
+		return (KSTD);
+	}
+	x_delete(i, false);
+	return (KSTD);
+}
+
+/* Delete nc chars to the right of the cursor (including cursor position) */
+static void
+x_delete(size_t nc, bool push)
+{
+	size_t i, nb, nw;
+	char *cp;
+
+	if (nc == 0)
+		return;
+
+	nw = 0;
+	cp = xcp;
+	for (i = 0; i < nc; ++i) {
+		char *cp2;
+		int j;
+
+		j = x_size2(cp, &cp2);
+		if (cp2 > xep)
+			break;
+		cp = cp2;
+		nw += j;
+	}
+	nb = cp - xcp;
+	/* nc = i; */
+
+	if (xmp != NULL && xmp > xcp) {
+		if (xcp + nb > xmp)
+			xmp = xcp;
+		else
+			xmp -= nb;
+	}
+	/*
+	 * This lets us yank a word we have deleted.
+	 */
+	if (push)
+		x_push(nb);
+
+	xep -= nb;
+	/* Copies the NUL */
+	memmove(xcp, xcp + nb, xep - xcp + 1);
+	/* don't redraw */
+	x_adj_ok = false;
+	xlp_valid = false;
+	x_zots(xcp);
+	/*
+	 * if we are already filling the line,
+	 * there is no need to ' ', '\b'.
+	 * But if we must, make sure we do the minimum.
+	 */
+	if ((i = xx_cols - 2 - x_col) > 0 || xep - xlp == 0) {
+		nw = i = (nw < i) ? nw : i;
+		while (i--)
+			x_e_putc2(' ');
+		if (x_col == xx_cols - 2) {
+			x_e_putc2((xep > xlp) ? '>' : (xbp > xbuf) ? '<' : ' ');
+			++nw;
+		}
+		while (nw--)
+			x_e_putc2('\b');
+	}
+	/*x_goto(xcp);*/
+	x_adj_ok = true;
+	xlp_valid = false;
+	cp = x_lastcp();
+	while (cp > xcp)
+		x_bs3(&cp);
+
+	x_modified();
+	return;
+}
+
+static int
+x_del_bword(int c MKSH_A_UNUSED)
+{
+	x_delete(x_bword(), true);
+	return (KSTD);
+}
+
+static int
+x_mv_bword(int c MKSH_A_UNUSED)
+{
+	x_bword();
+	return (KSTD);
+}
+
+static int
+x_mv_fword(int c MKSH_A_UNUSED)
+{
+	x_fword(true);
+	return (KSTD);
+}
+
+static int
+x_del_fword(int c MKSH_A_UNUSED)
+{
+	x_delete(x_fword(false), true);
+	return (KSTD);
+}
+
+static size_t
+x_bword(void)
+{
+	size_t nb = 0;
+	char *cp = xcp;
+
+	if (cp == xbuf) {
+		x_e_putc2(7);
+		return (0);
+	}
+	while (x_arg--) {
+		while (cp != xbuf && is_mfs(cp[-1])) {
+			cp--;
+			nb++;
+		}
+		while (cp != xbuf && !is_mfs(cp[-1])) {
+			cp--;
+			nb++;
+		}
+	}
+	x_goto(cp);
+	return (x_nb2nc(nb));
+}
+
+static size_t
+x_fword(bool move)
+{
+	size_t nc;
+	char *cp = xcp;
+
+	if (cp == xep) {
+		x_e_putc2(7);
+		return (0);
+	}
+	while (x_arg--) {
+		while (cp != xep && is_mfs(*cp))
+			cp++;
+		while (cp != xep && !is_mfs(*cp))
+			cp++;
+	}
+	nc = x_nb2nc(cp - xcp);
+	if (move)
+		x_goto(cp);
+	return (nc);
+}
+
+static void
+x_goto(char *cp)
+{
+	cp = cp >= xep ? xep : x_bs0(cp, xbuf);
+	if (cp < xbp || cp >= utf_skipcols(xbp, x_displen)) {
+		/* we are heading off screen */
+		xcp = cp;
+		x_adjust();
+	} else if (cp < xcp) {
+		/* move back */
+		while (cp < xcp)
+			x_bs3(&xcp);
+	} else if (cp > xcp) {
+		/* move forward */
+		while (cp > xcp)
+			x_zotc3(&xcp);
+	}
+}
+
+static char *
+x_bs0(char *cp, char *lower_bound)
+{
+	if (UTFMODE)
+		while ((!lower_bound || (cp > lower_bound)) &&
+		    ((*(unsigned char *)cp & 0xC0) == 0x80))
+			--cp;
+	return (cp);
+}
+
+static void
+x_bs3(char **p)
+{
+	int i;
+
+	*p = x_bs0((*p) - 1, NULL);
+	i = x_size2(*p, NULL);
+	while (i--)
+		x_e_putc2('\b');
+}
+
+static int
+x_size_str(char *cp)
+{
+	int size = 0;
+	while (*cp)
+		size += x_size2(cp, &cp);
+	return (size);
+}
+
+static int
+x_size2(char *cp, char **dcp)
+{
+	uint8_t c = *(unsigned char *)cp;
+
+	if (UTFMODE && (c > 0x7F))
+		return (utf_widthadj(cp, (const char **)dcp));
+	if (dcp)
+		*dcp = cp + 1;
+	if (c == '\t')
+		/* Kludge, tabs are always four spaces. */
+		return (4);
+	if (ISCTRL(c) && /* but not C1 */ c < 0x80)
+		/* control unsigned char */
+		return (2);
+	return (1);
+}
+
+static void
+x_zots(char *str)
+{
+	int adj = x_adj_done;
+
+	x_lastcp();
+	while (*str && str < xlp && adj == x_adj_done)
+		x_zotc3(&str);
+}
+
+static void
+x_zotc3(char **cp)
+{
+	unsigned char c = **(unsigned char **)cp;
+
+	if (c == '\t') {
+		/* Kludge, tabs are always four spaces. */
+		x_e_puts("    ");
+		(*cp)++;
+	} else if (ISCTRL(c) && /* but not C1 */ c < 0x80) {
+		x_e_putc2('^');
+		x_e_putc2(UNCTRL(c));
+		(*cp)++;
+	} else
+		x_e_putc3((const char **)cp);
+}
+
+static int
+x_mv_back(int c MKSH_A_UNUSED)
+{
+	if (xcp == xbuf) {
+		x_e_putc2(7);
+		return (KSTD);
+	}
+	while (x_arg--) {
+		x_goto(xcp - 1);
+		if (xcp == xbuf)
+			break;
+	}
+	return (KSTD);
+}
+
+static int
+x_mv_forw(int c MKSH_A_UNUSED)
+{
+	char *cp = xcp, *cp2;
+
+	if (xcp == xep) {
+		x_e_putc2(7);
+		return (KSTD);
+	}
+	while (x_arg--) {
+		utf_ptradjx(cp, cp2);
+		if (cp2 > xep)
+			break;
+		cp = cp2;
+	}
+	x_goto(cp);
+	return (KSTD);
+}
+
+static int
+x_search_char_forw(int c MKSH_A_UNUSED)
+{
+	char *cp = xcp;
+	char tmp[4];
+
+	*xep = '\0';
+	if (x_e_getmbc(tmp) < 0) {
+		x_e_putc2(7);
+		return (KSTD);
+	}
+	while (x_arg--) {
+		if ((cp = (cp == xep) ? NULL : strstr(cp + 1, tmp)) == NULL &&
+		    (cp = strstr(xbuf, tmp)) == NULL) {
+			x_e_putc2(7);
+			return (KSTD);
+		}
+	}
+	x_goto(cp);
+	return (KSTD);
+}
+
+static int
+x_search_char_back(int c MKSH_A_UNUSED)
+{
+	char *cp = xcp, *p, tmp[4];
+	bool b;
+
+	if (x_e_getmbc(tmp) < 0) {
+		x_e_putc2(7);
+		return (KSTD);
+	}
+	for (; x_arg--; cp = p)
+		for (p = cp; ; ) {
+			if (p-- == xbuf)
+				p = xep;
+			if (p == cp) {
+				x_e_putc2(7);
+				return (KSTD);
+			}
+			if ((tmp[1] && ((p+1) > xep)) ||
+			    (tmp[2] && ((p+2) > xep)))
+				continue;
+			b = true;
+			if (*p != tmp[0])
+				b = false;
+			if (b && tmp[1] && p[1] != tmp[1])
+				b = false;
+			if (b && tmp[2] && p[2] != tmp[2])
+				b = false;
+			if (b)
+				break;
+		}
+	x_goto(cp);
+	return (KSTD);
+}
+
+static int
+x_newline(int c MKSH_A_UNUSED)
+{
+	x_e_putc2('\r');
+	x_e_putc2('\n');
+	x_flush();
+	*xep++ = '\n';
+	return (KEOL);
+}
+
+static int
+x_end_of_text(int c MKSH_A_UNUSED)
+{
+	char tmp = edchars.eof;
+	char *cp = &tmp;
+
+	x_zotc3(&cp);
+	x_putc('\r');
+	x_putc('\n');
+	x_flush();
+	return (KEOL);
+}
+
+static int
+x_beg_hist(int c MKSH_A_UNUSED)
+{
+	x_load_hist(history);
+	return (KSTD);
+}
+
+static int
+x_end_hist(int c MKSH_A_UNUSED)
+{
+	x_load_hist(histptr);
+	return (KSTD);
+}
+
+static int
+x_prev_com(int c MKSH_A_UNUSED)
+{
+	x_load_hist(x_histp - x_arg);
+	return (KSTD);
+}
+
+static int
+x_next_com(int c MKSH_A_UNUSED)
+{
+	x_load_hist(x_histp + x_arg);
+	return (KSTD);
+}
+
+/*
+ * Goto a particular history number obtained from argument.
+ * If no argument is given history 1 is probably not what you
+ * want so we'll simply go to the oldest one.
+ */
+static int
+x_goto_hist(int c MKSH_A_UNUSED)
+{
+	if (x_arg_defaulted)
+		x_load_hist(history);
+	else
+		x_load_hist(histptr + x_arg - source->line);
+	return (KSTD);
+}
+
+static void
+x_load_hist(char **hp)
+{
+	int oldsize;
+	char *sp = NULL;
+
+	if (hp == histptr + 1) {
+		sp = holdbufp;
+		modified = 0;
+	} else if (hp < history || hp > histptr) {
+		x_e_putc2(7);
+		return;
+	}
+	if (sp == NULL)
+		sp = *hp;
+	x_histp = hp;
+	oldsize = x_size_str(xbuf);
+	if (modified)
+		strlcpy(holdbufp, xbuf, LINE);
+	strlcpy(xbuf, sp, xend - xbuf);
+	xbp = xbuf;
+	xep = xcp = xbuf + strlen(xbuf);
+	xlp_valid = false;
+	if (xep <= x_lastcp()) {
+		x_redraw(oldsize);
+	}
+	x_goto(xep);
+	modified = 0;
+}
+
+static int
+x_nl_next_com(int c MKSH_A_UNUSED)
+{
+	if (!x_histncp || (x_histp != x_histncp && x_histp != histptr + 1))
+		/* fresh start of ^O */
+		x_histncp = x_histp;
+	x_nextcmd = source->line - (histptr - x_histncp) + 1;
+	return (x_newline('\n'));
+}
+
+static int
+x_eot_del(int c)
+{
+	if (xep == xbuf && x_arg_defaulted)
+		return (x_end_of_text(c));
+	else
+		return (x_del_char(c));
+}
+
+/* reverse incremental history search */
+static int
+x_search_hist(int c)
+{
+	int offset = -1;	/* offset of match in xbuf, else -1 */
+	char pat[80 + 1];	/* pattern buffer */
+	char *p = pat;
+	unsigned char f;
+
+	*p = '\0';
+	while (/* CONSTCOND */ 1) {
+		if (offset < 0) {
+			x_e_puts("\nI-search: ");
+			x_e_puts(pat);
+		}
+		x_flush();
+		if ((c = x_e_getc()) < 0)
+			return (KSTD);
+		f = x_tab[0][c];
+		if (c == CTRL('[')) {
+			if ((f & 0x7F) == XFUNC_meta1) {
+				if ((c = x_e_getc()) < 0)
+					return (KSTD);
+				f = x_tab[1][c] & 0x7F;
+				if (f == XFUNC_meta1 || f == XFUNC_meta2)
+					x_meta1(CTRL('['));
+				x_e_ungetc(c);
+			}
+			break;
+		}
+#ifndef MKSH_SMALL
+		if (f & 0x80) {
+			f &= 0x7F;
+			if ((c = x_e_getc()) != '~')
+				x_e_ungetc(c);
+		}
+#endif
+		if (f == XFUNC_search_hist)
+			offset = x_search(pat, 0, offset);
+		else if (f == XFUNC_del_back) {
+			if (p == pat) {
+				offset = -1;
+				break;
+			}
+			if (p > pat)
+				*--p = '\0';
+			if (p == pat)
+				offset = -1;
+			else
+				offset = x_search(pat, 1, offset);
+			continue;
+		} else if (f == XFUNC_insert) {
+			/* add char to pattern */
+			/* overflow check... */
+			if ((size_t)(p - pat) >= sizeof(pat) - 1) {
+				x_e_putc2(7);
+				continue;
+			}
+			*p++ = c, *p = '\0';
+			if (offset >= 0) {
+				/* already have partial match */
+				offset = x_match(xbuf, pat);
+				if (offset >= 0) {
+					x_goto(xbuf + offset + (p - pat) -
+					    (*pat == '^'));
+					continue;
+				}
+			}
+			offset = x_search(pat, 0, offset);
+		} else if (f == XFUNC_abort) {
+			if (offset >= 0)
+				x_load_hist(histptr + 1);
+			break;
+		} else {
+			/* other command */
+			x_e_ungetc(c);
+			break;
+		}
+	}
+	if (offset < 0)
+		x_redraw(-1);
+	return (KSTD);
+}
+
+/* search backward from current line */
+static int
+x_search(char *pat, int sameline, int offset)
+{
+	char **hp;
+	int i;
+
+	for (hp = x_histp - (sameline ? 0 : 1); hp >= history; --hp) {
+		i = x_match(*hp, pat);
+		if (i >= 0) {
+			if (offset < 0)
+				x_e_putc2('\n');
+			x_load_hist(hp);
+			x_goto(xbuf + i + strlen(pat) - (*pat == '^'));
+			return (i);
+		}
+	}
+	x_e_putc2(7);
+	x_histp = histptr;
+	return (-1);
+}
+
+#ifndef MKSH_SMALL
+/* anchored search up from current line */
+static int
+x_search_hist_up(int c MKSH_A_UNUSED)
+{
+	return (x_search_dir(-1));
+}
+
+/* anchored search down from current line */
+static int
+x_search_hist_dn(int c MKSH_A_UNUSED)
+{
+	return (x_search_dir(1));
+}
+
+/* anchored search in the indicated direction */
+static int
+x_search_dir(int search_dir /* should've been bool */)
+{
+	char **hp = x_histp + search_dir;
+	size_t curs = xcp - xbuf;
+
+	while (histptr >= hp && hp >= history) {
+		if (strncmp(xbuf, *hp, curs) == 0) {
+			x_load_hist(hp);
+			x_goto(xbuf + curs);
+			break;
+		}
+		hp += search_dir;
+	}
+	return (KSTD);
+}
+#endif
+
+/* return position of first match of pattern in string, else -1 */
+static int
+x_match(char *str, char *pat)
+{
+	if (*pat == '^') {
+		return ((strncmp(str, pat + 1, strlen(pat + 1)) == 0) ? 0 : -1);
+	} else {
+		char *q = strstr(str, pat);
+		return ((q == NULL) ? -1 : q - str);
+	}
+}
+
+static int
+x_del_line(int c MKSH_A_UNUSED)
+{
+	int i, j;
+
+	*xep = 0;
+	i = xep - xbuf;
+	j = x_size_str(xbuf);
+	xcp = xbuf;
+	x_push(i);
+	xlp = xbp = xep = xbuf;
+	xlp_valid = true;
+	*xcp = 0;
+	xmp = NULL;
+	x_redraw(j);
+	x_modified();
+	return (KSTD);
+}
+
+static int
+x_mv_end(int c MKSH_A_UNUSED)
+{
+	x_goto(xep);
+	return (KSTD);
+}
+
+static int
+x_mv_begin(int c MKSH_A_UNUSED)
+{
+	x_goto(xbuf);
+	return (KSTD);
+}
+
+static int
+x_draw_line(int c MKSH_A_UNUSED)
+{
+	x_redraw(-1);
+	return (KSTD);
+}
+
+static int
+x_e_rebuildline(const char *clrstr)
+{
+	shf_puts(clrstr, shl_out);
+	x_adjust();
+	return (KSTD);
+}
+
+static int
+x_cls(int c MKSH_A_UNUSED)
+{
+	return (x_e_rebuildline(MKSH_CLS_STRING));
+}
+
+/*
+ * Redraw (part of) the line. If limit is < 0, the everything is redrawn
+ * on a NEW line, otherwise limit is the screen column up to which needs
+ * redrawing.
+ */
+static void
+x_redraw(int limit)
+{
+	int i, j;
+	char *cp;
+
+	x_adj_ok = false;
+	if (limit == -1)
+		x_e_putc2('\n');
+	else
+		x_e_putc2('\r');
+	x_flush();
+	if (xbp == xbuf) {
+		if (prompt_trunc != -1)
+			pprompt(prompt, prompt_trunc);
+		x_col = pwidth;
+	}
+	x_displen = xx_cols - 2 - x_col;
+	xlp_valid = false;
+	x_zots(xbp);
+	if (xbp != xbuf || xep > xlp)
+		limit = xx_cols;
+	if (limit >= 0) {
+		if (xep > xlp)
+			/* we fill the line */
+			i = 0;
+		else {
+			char *cpl = xbp;
+
+			i = limit;
+			while (cpl < xlp)
+				i -= x_size2(cpl, &cpl);
+		}
+
+		j = 0;
+		while ((j < i) || (x_col < (xx_cols - 2))) {
+			if (!(x_col < (xx_cols - 2)))
+				break;
+			x_e_putc2(' ');
+			j++;
+		}
+		i = ' ';
+		if (xep > xlp) {
+			/* more off screen */
+			if (xbp > xbuf)
+				i = '*';
+			else
+				i = '>';
+		} else if (xbp > xbuf)
+			i = '<';
+		x_e_putc2(i);
+		j++;
+		while (j--)
+			x_e_putc2('\b');
+	}
+	cp = xlp;
+	while (cp > xcp)
+		x_bs3(&cp);
+	x_adj_ok = true;
+	return;
+}
+
+static int
+x_transpose(int c MKSH_A_UNUSED)
+{
+	unsigned int tmpa, tmpb;
+
+	/*-
+	 * What transpose is meant to do seems to be up for debate. This
+	 * is a general summary of the options; the text is abcd with the
+	 * upper case character or underscore indicating the cursor position:
+	 *	Who			Before	After	Before	After
+	 *	AT&T ksh in emacs mode:	abCd	abdC	abcd_	(bell)
+	 *	AT&T ksh in gmacs mode:	abCd	baCd	abcd_	abdc_
+	 *	gnu emacs:		abCd	acbD	abcd_	abdc_
+	 * Pdksh currently goes with GNU behavior since I believe this is the
+	 * most common version of emacs, unless in gmacs mode, in which case
+	 * it does the AT&T ksh gmacs mode.
+	 * This should really be broken up into 3 functions so users can bind
+	 * to the one they want.
+	 */
+	if (xcp == xbuf) {
+		x_e_putc2(7);
+		return (KSTD);
+	} else if (xcp == xep || Flag(FGMACS)) {
+		if (xcp - xbuf == 1) {
+			x_e_putc2(7);
+			return (KSTD);
+		}
+		/*
+		 * Gosling/Unipress emacs style: Swap two characters before
+		 * the cursor, do not change cursor position
+		 */
+		x_bs3(&xcp);
+		if (utf_mbtowc(&tmpa, xcp) == (size_t)-1) {
+			x_e_putc2(7);
+			return (KSTD);
+		}
+		x_bs3(&xcp);
+		if (utf_mbtowc(&tmpb, xcp) == (size_t)-1) {
+			x_e_putc2(7);
+			return (KSTD);
+		}
+		utf_wctomb(xcp, tmpa);
+		x_zotc3(&xcp);
+		utf_wctomb(xcp, tmpb);
+		x_zotc3(&xcp);
+	} else {
+		/*
+		 * GNU emacs style: Swap the characters before and under the
+		 * cursor, move cursor position along one.
+		 */
+		if (utf_mbtowc(&tmpa, xcp) == (size_t)-1) {
+			x_e_putc2(7);
+			return (KSTD);
+		}
+		x_bs3(&xcp);
+		if (utf_mbtowc(&tmpb, xcp) == (size_t)-1) {
+			x_e_putc2(7);
+			return (KSTD);
+		}
+		utf_wctomb(xcp, tmpa);
+		x_zotc3(&xcp);
+		utf_wctomb(xcp, tmpb);
+		x_zotc3(&xcp);
+	}
+	x_modified();
+	return (KSTD);
+}
+
+static int
+x_literal(int c MKSH_A_UNUSED)
+{
+	x_curprefix = -1;
+	return (KSTD);
+}
+
+static int
+x_meta1(int c MKSH_A_UNUSED)
+{
+	x_curprefix = 1;
+	return (KSTD);
+}
+
+static int
+x_meta2(int c MKSH_A_UNUSED)
+{
+	x_curprefix = 2;
+	return (KSTD);
+}
+
+static int
+x_kill(int c MKSH_A_UNUSED)
+{
+	size_t col = xcp - xbuf;
+	size_t lastcol = xep - xbuf;
+	size_t ndel, narg;
+
+	if (x_arg_defaulted || (narg = x_arg) > lastcol)
+		narg = lastcol;
+	if (narg < col) {
+		x_goto(xbuf + narg);
+		ndel = col - narg;
+	} else
+		ndel = narg - col;
+	x_delete(x_nb2nc(ndel), true);
+	return (KSTD);
+}
+
+static void
+x_push(int nchars)
+{
+	char *cp;
+
+	mkssert(xcp != NULL);
+	strndupx(cp, xcp, nchars, AEDIT);
+	if (killstack[killsp])
+		afree(killstack[killsp], AEDIT);
+	killstack[killsp] = cp;
+	killsp = (killsp + 1) % KILLSIZE;
+}
+
+static int
+x_yank(int c MKSH_A_UNUSED)
+{
+	if (killsp == 0)
+		killtp = KILLSIZE;
+	else
+		killtp = killsp;
+	killtp--;
+	if (killstack[killtp] == 0) {
+		x_e_puts("\nnothing to yank");
+		x_redraw(-1);
+		return (KSTD);
+	}
+	xmp = xcp;
+	x_ins(killstack[killtp]);
+	return (KSTD);
+}
+
+static int
+x_meta_yank(int c MKSH_A_UNUSED)
+{
+	size_t len;
+
+	if ((x_last_command != XFUNC_yank && x_last_command != XFUNC_meta_yank) ||
+	    killstack[killtp] == 0) {
+		killtp = killsp;
+		x_e_puts("\nyank something first");
+		x_redraw(-1);
+		return (KSTD);
+	}
+	len = strlen(killstack[killtp]);
+	x_goto(xcp - len);
+	x_delete(x_nb2nc(len), false);
+	do {
+		if (killtp == 0)
+			killtp = KILLSIZE - 1;
+		else
+			killtp--;
+	} while (killstack[killtp] == 0);
+	x_ins(killstack[killtp]);
+	return (KSTD);
+}
+
+static int
+x_abort(int c MKSH_A_UNUSED)
+{
+	/* x_zotc(c); */
+	xlp = xep = xcp = xbp = xbuf;
+	xlp_valid = true;
+	*xcp = 0;
+	x_modified();
+	return (KINTR);
+}
+
+static int
+x_error(int c MKSH_A_UNUSED)
+{
+	x_e_putc2(7);
+	return (KSTD);
+}
+
+#ifndef MKSH_SMALL
+/* special VT100 style key sequence hack */
+static int
+x_vt_hack(int c)
+{
+	/* we only support PF2-'1' for now */
+	if (c != (2 << 8 | '1'))
+		return (x_error(c));
+
+	/* what's the next character? */
+	switch ((c = x_e_getc())) {
+	case '~':
+		x_arg = 1;
+		x_arg_defaulted = true;
+		return (x_mv_begin(0));
+	case ';':
+		/* "interesting" sequence detected */
+		break;
+	default:
+		goto unwind_err;
+	}
+
+	/* XXX x_e_ungetc is one-octet only */
+	if ((c = x_e_getc()) != '5' && c != '3')
+		goto unwind_err;
+
+	/*-
+	 * At this point, we have read the following octets so far:
+	 * - ESC+[ or ESC+O or Ctrl-X (Prefix 2)
+	 * - 1 (vt_hack)
+	 * - ;
+	 * - 5 (Ctrl key combiner) or 3 (Alt key combiner)
+	 * We can now accept one more octet designating the key.
+	 */
+
+	switch ((c = x_e_getc())) {
+	case 'C':
+		return (x_mv_fword(c));
+	case 'D':
+		return (x_mv_bword(c));
+	}
+
+ unwind_err:
+	x_e_ungetc(c);
+	return (x_error(c));
+}
+#endif
+
+static char *
+x_mapin(const char *cp, Area *ap)
+{
+	char *news, *op;
+
+	strdupx(news, cp, ap);
+	op = news;
+	while (*cp) {
+		/* XXX -- should handle \^ escape? */
+		if (*cp == '^') {
+			cp++;
+			/*XXX or ^^ escape? this is ugly. */
+			if (*cp >= '?')
+				/* includes '?'; ASCII */
+				*op++ = CTRL(*cp);
+			else {
+				*op++ = '^';
+				cp--;
+			}
+		} else
+			*op++ = *cp;
+		cp++;
+	}
+	*op = '\0';
+
+	return (news);
+}
+
+static void
+x_mapout2(int c, char **buf)
+{
+	char *p = *buf;
+
+	if (ISCTRL(c)) {
+		*p++ = '^';
+		*p++ = UNCTRL(c);
+	} else
+		*p++ = c;
+	*p = 0;
+	*buf = p;
+}
+
+static char *
+x_mapout(int c)
+{
+	static char buf[8];
+	char *bp = buf;
+
+	x_mapout2(c, &bp);
+	return (buf);
+}
+
+static void
+x_print(int prefix, int key)
+{
+	int f = x_tab[prefix][key];
+
+	if (prefix)
+		/* prefix == 1 || prefix == 2 */
+		shf_puts(x_mapout(prefix == 1 ?
+		    CTRL('[') : CTRL('X')), shl_stdout);
+#ifdef MKSH_SMALL
+	shprintf("%s = ", x_mapout(key));
+#else
+	shprintf("%s%s = ", x_mapout(key), (f & 0x80) ? "~" : "");
+	if (XFUNC_VALUE(f) != XFUNC_ins_string)
+#endif
+		shprintf("%s\n", x_ftab[XFUNC_VALUE(f)].xf_name);
+#ifndef MKSH_SMALL
+	else
+		shprintf("'%s'\n", x_atab[prefix][key]);
+#endif
+}
+
+int
+x_bind(const char *a1, const char *a2,
+#ifndef MKSH_SMALL
+    /* bind -m */
+    bool macro,
+#endif
+    /* bind -l */
+    bool list)
+{
+	unsigned char f;
+	int prefix, key;
+	char *m1, *m2;
+#ifndef MKSH_SMALL
+	char *sp = NULL;
+	bool hastilde;
+#endif
+
+	if (x_tab == NULL) {
+		bi_errorf("can't bind, not a tty");
+		return (1);
+	}
+	/* List function names */
+	if (list) {
+		for (f = 0; f < NELEM(x_ftab); f++)
+			if (x_ftab[f].xf_name &&
+			    !(x_ftab[f].xf_flags & XF_NOBIND))
+				shprintf("%s\n", x_ftab[f].xf_name);
+		return (0);
+	}
+	if (a1 == NULL) {
+		for (prefix = 0; prefix < X_NTABS; prefix++)
+			for (key = 0; key < X_TABSZ; key++) {
+				f = XFUNC_VALUE(x_tab[prefix][key]);
+				if (f == XFUNC_insert || f == XFUNC_error
+#ifndef MKSH_SMALL
+				    || (macro && f != XFUNC_ins_string)
+#endif
+				    )
+					continue;
+				x_print(prefix, key);
+			}
+		return (0);
+	}
+	m2 = m1 = x_mapin(a1, ATEMP);
+	prefix = 0;
+	for (;; m1++) {
+		key = (unsigned char)*m1;
+		f = XFUNC_VALUE(x_tab[prefix][key]);
+		if (f == XFUNC_meta1)
+			prefix = 1;
+		else if (f == XFUNC_meta2)
+			prefix = 2;
+		else
+			break;
+	}
+	if (*++m1
+#ifndef MKSH_SMALL
+	    && ((*m1 != '~') || *(m1 + 1))
+#endif
+	    ) {
+		char msg[256];
+		const char *c = a1;
+		m1 = msg;
+		while (*c && (size_t)(m1 - msg) < sizeof(msg) - 3)
+			x_mapout2(*c++, &m1);
+		bi_errorf("%s: %s", "too long key sequence", msg);
+		return (1);
+	}
+#ifndef MKSH_SMALL
+	hastilde = tobool(*m1);
+#endif
+	afree(m2, ATEMP);
+
+	if (a2 == NULL) {
+		x_print(prefix, key);
+		return (0);
+	}
+	if (*a2 == 0) {
+		f = XFUNC_insert;
+#ifndef MKSH_SMALL
+	} else if (macro) {
+		f = XFUNC_ins_string;
+		sp = x_mapin(a2, AEDIT);
+#endif
+	} else {
+		for (f = 0; f < NELEM(x_ftab); f++)
+			if (x_ftab[f].xf_name &&
+			    strcmp(x_ftab[f].xf_name, a2) == 0)
+				break;
+		if (f == NELEM(x_ftab) || x_ftab[f].xf_flags & XF_NOBIND) {
+			bi_errorf("%s: %s %s", a2, "no such", Tfunction);
+			return (1);
+		}
+	}
+
+#ifndef MKSH_SMALL
+	if (XFUNC_VALUE(x_tab[prefix][key]) == XFUNC_ins_string &&
+	    x_atab[prefix][key])
+		afree(x_atab[prefix][key], AEDIT);
+#endif
+	x_tab[prefix][key] = f
+#ifndef MKSH_SMALL
+	    | (hastilde ? 0x80 : 0)
+#endif
+	    ;
+#ifndef MKSH_SMALL
+	x_atab[prefix][key] = sp;
+#endif
+
+	/* Track what the user has bound so x_mode(true) won't toast things */
+	if (f == XFUNC_insert)
+		x_bound[(prefix * X_TABSZ + key) / 8] &=
+		    ~(1 << ((prefix * X_TABSZ + key) % 8));
+	else
+		x_bound[(prefix * X_TABSZ + key) / 8] |=
+		    (1 << ((prefix * X_TABSZ + key) % 8));
+
+	return (0);
+}
+
+static void
+bind_if_not_bound(int p, int k, int func)
+{
+	int t;
+
+	/*
+	 * Has user already bound this key?
+	 * If so, do not override it.
+	 */
+	t = p * X_TABSZ + k;
+	if (x_bound[t >> 3] & (1 << (t & 7)))
+		return;
+
+	x_tab[p][k] = func;
+}
+
+static int
+x_set_mark(int c MKSH_A_UNUSED)
+{
+	xmp = xcp;
+	return (KSTD);
+}
+
+static int
+x_kill_region(int c MKSH_A_UNUSED)
+{
+	size_t rsize;
+	char *xr;
+
+	if (xmp == NULL) {
+		x_e_putc2(7);
+		return (KSTD);
+	}
+	if (xmp > xcp) {
+		rsize = xmp - xcp;
+		xr = xcp;
+	} else {
+		rsize = xcp - xmp;
+		xr = xmp;
+	}
+	x_goto(xr);
+	x_delete(x_nb2nc(rsize), true);
+	xmp = xr;
+	return (KSTD);
+}
+
+static int
+x_xchg_point_mark(int c MKSH_A_UNUSED)
+{
+	char *tmp;
+
+	if (xmp == NULL) {
+		x_e_putc2(7);
+		return (KSTD);
+	}
+	tmp = xmp;
+	xmp = xcp;
+	x_goto(tmp);
+	return (KSTD);
+}
+
+static int
+x_noop(int c MKSH_A_UNUSED)
+{
+	return (KSTD);
+}
+
+/*
+ *	File/command name completion routines
+ */
+static int
+x_comp_comm(int c MKSH_A_UNUSED)
+{
+	do_complete(XCF_COMMAND, CT_COMPLETE);
+	return (KSTD);
+}
+
+static int
+x_list_comm(int c MKSH_A_UNUSED)
+{
+	do_complete(XCF_COMMAND, CT_LIST);
+	return (KSTD);
+}
+
+static int
+x_complete(int c MKSH_A_UNUSED)
+{
+	do_complete(XCF_COMMAND_FILE, CT_COMPLETE);
+	return (KSTD);
+}
+
+static int
+x_enumerate(int c MKSH_A_UNUSED)
+{
+	do_complete(XCF_COMMAND_FILE, CT_LIST);
+	return (KSTD);
+}
+
+static int
+x_comp_file(int c MKSH_A_UNUSED)
+{
+	do_complete(XCF_FILE, CT_COMPLETE);
+	return (KSTD);
+}
+
+static int
+x_list_file(int c MKSH_A_UNUSED)
+{
+	do_complete(XCF_FILE, CT_LIST);
+	return (KSTD);
+}
+
+static int
+x_comp_list(int c MKSH_A_UNUSED)
+{
+	do_complete(XCF_COMMAND_FILE, CT_COMPLIST);
+	return (KSTD);
+}
+
+static int
+x_expand(int c MKSH_A_UNUSED)
+{
+	char **words;
+	int start, end, nwords, i;
+
+	i = XCF_FILE;
+	nwords = x_cf_glob(&i, xbuf, xep - xbuf, xcp - xbuf,
+	    &start, &end, &words);
+
+	if (nwords == 0) {
+		x_e_putc2(7);
+		return (KSTD);
+	}
+	x_goto(xbuf + start);
+	x_delete(x_nb2nc(end - start), false);
+
+	i = 0;
+	while (i < nwords) {
+		if (x_escape(words[i], strlen(words[i]), x_do_ins) < 0 ||
+		    (++i < nwords && x_ins(" ") < 0)) {
+			x_e_putc2(7);
+			return (KSTD);
+		}
+	}
+	x_adjust();
+
+	return (KSTD);
+}
+
+static void
+do_complete(
+    /* XCF_{COMMAND,FILE,COMMAND_FILE} */
+    int flags,
+    /* 0 for list, 1 for complete and 2 for complete-list */
+    Comp_type type)
+{
+	char **words;
+	int start, end, nlen, olen, nwords;
+	bool completed;
+
+	nwords = x_cf_glob(&flags, xbuf, xep - xbuf, xcp - xbuf,
+	    &start, &end, &words);
+	/* no match */
+	if (nwords == 0) {
+		x_e_putc2(7);
+		return;
+	}
+	if (type == CT_LIST) {
+		x_print_expansions(nwords, words,
+		    tobool(flags & XCF_IS_COMMAND));
+		x_redraw(0);
+		x_free_words(nwords, words);
+		return;
+	}
+	olen = end - start;
+	nlen = x_longest_prefix(nwords, words);
+	if (nwords == 1) {
+		/*
+		 * always complete single matches;
+		 * any expansion of parameter substitution
+		 * is always at most one result, too
+		 */
+		completed = true;
+	} else {
+		char *unescaped;
+
+		/* make a copy of the original string part */
+		strndupx(unescaped, xbuf + start, olen, ATEMP);
+
+		/* expand any tilde and unescape the string for comparison */
+		unescaped = x_glob_hlp_tilde_and_rem_qchar(unescaped, true);
+
+		/*
+		 * match iff entire original string is part of the
+		 * longest prefix, implying the latter is at least
+		 * the same size (after unescaping)
+		 */
+		completed = !strncmp(words[0], unescaped, strlen(unescaped));
+
+		afree(unescaped, ATEMP);
+	}
+	if (type == CT_COMPLIST && nwords > 1) {
+		/*
+		 * print expansions, since we didn't get back
+		 * just a single match
+		 */
+		x_print_expansions(nwords, words,
+		    tobool(flags & XCF_IS_COMMAND));
+	}
+	if (completed) {
+		/* expand on the command line */
+		xmp = NULL;
+		xcp = xbuf + start;
+		xep -= olen;
+		memmove(xcp, xcp + olen, xep - xcp + 1);
+		x_escape(words[0], nlen, x_do_ins);
+	}
+	x_adjust();
+	/*
+	 * append a space if this is a single non-directory match
+	 * and not a parameter or homedir substitution
+	 */
+	if (nwords == 1 && words[0][nlen - 1] != '/' &&
+	    !(flags & XCF_IS_NOSPACE)) {
+		x_ins(" ");
+	}
+
+	x_free_words(nwords, words);
+}
+
+/*-
+ * NAME:
+ *	x_adjust - redraw the line adjusting starting point etc.
+ *
+ * DESCRIPTION:
+ *	This function is called when we have exceeded the bounds
+ *	of the edit window. It increments x_adj_done so that
+ *	functions like x_ins and x_delete know that we have been
+ *	called and can skip the x_bs() stuff which has already
+ *	been done by x_redraw.
+ *
+ * RETURN VALUE:
+ *	None
+ */
+static void
+x_adjust(void)
+{
+	int col_left, n;
+
+	/* flag the fact that we were called */
+	x_adj_done++;
+
+	/*
+	 * calculate the amount of columns we need to "go back"
+	 * from xcp to set xbp to (but never < xbuf) to 2/3 of
+	 * the display width; take care of pwidth though
+	 */
+	if ((col_left = xx_cols * 2 / 3) < MIN_EDIT_SPACE) {
+		/*
+		 * cowardly refuse to do anything
+		 * if the available space is too small;
+		 * fall back to dumb pdksh code
+		 */
+		if ((xbp = xcp - (x_displen / 2)) < xbuf)
+			xbp = xbuf;
+		/* elide UTF-8 fixup as penalty */
+		goto x_adjust_out;
+	}
+
+	/* fix up xbp to just past a character end first */
+	xbp = xcp >= xep ? xep : x_bs0(xcp, xbuf);
+	/* walk backwards */
+	while (xbp > xbuf && col_left > 0) {
+		xbp = x_bs0(xbp - 1, xbuf);
+		col_left -= (n = x_size2(xbp, NULL));
+	}
+	/* check if we hit the prompt */
+	if (xbp == xbuf && xcp != xbuf && col_left >= 0 && col_left < pwidth) {
+		/* so we did; force scrolling occurs */
+		xbp += utf_ptradj(xbp);
+	}
+
+ x_adjust_out:
+	xlp_valid = false;
+	x_redraw(xx_cols);
+	x_flush();
+}
+
+static void
+x_e_ungetc(int c)
+{
+	unget_char = c < 0 ? -1 : (c & 255);
+}
+
+static int
+x_e_getc(void)
+{
+	int c;
+
+	if (unget_char >= 0) {
+		c = unget_char;
+		unget_char = -1;
+		return (c);
+	}
+
+#ifndef MKSH_SMALL
+	if (macroptr) {
+		if ((c = (unsigned char)*macroptr++))
+			return (c);
+		macroptr = NULL;
+	}
+#endif
+
+	return (x_getc());
+}
+
+static void
+x_e_putc2(int c)
+{
+	int width = 1;
+
+	if (c == '\r' || c == '\n')
+		x_col = 0;
+	if (x_col < xx_cols) {
+		if (UTFMODE && (c > 0x7F)) {
+			char utf_tmp[3];
+			size_t x;
+
+			if (c < 0xA0)
+				c = 0xFFFD;
+			x = utf_wctomb(utf_tmp, c);
+			x_putc(utf_tmp[0]);
+			if (x > 1)
+				x_putc(utf_tmp[1]);
+			if (x > 2)
+				x_putc(utf_tmp[2]);
+			width = utf_wcwidth(c);
+		} else
+			x_putc(c);
+		switch (c) {
+		case 7:
+			break;
+		case '\r':
+		case '\n':
+			break;
+		case '\b':
+			x_col--;
+			break;
+		default:
+			x_col += width;
+			break;
+		}
+	}
+	if (x_adj_ok && (x_col < 0 || x_col >= (xx_cols - 2)))
+		x_adjust();
+}
+
+static void
+x_e_putc3(const char **cp)
+{
+	int width = 1, c = **(const unsigned char **)cp;
+
+	if (c == '\r' || c == '\n')
+		x_col = 0;
+	if (x_col < xx_cols) {
+		if (UTFMODE && (c > 0x7F)) {
+			char *cp2;
+
+			width = utf_widthadj(*cp, (const char **)&cp2);
+			while (*cp < cp2)
+				x_putcf(*(*cp)++);
+		} else {
+			(*cp)++;
+			x_putc(c);
+		}
+		switch (c) {
+		case 7:
+			break;
+		case '\r':
+		case '\n':
+			break;
+		case '\b':
+			x_col--;
+			break;
+		default:
+			x_col += width;
+			break;
+		}
+	}
+	if (x_adj_ok && (x_col < 0 || x_col >= (xx_cols - 2)))
+		x_adjust();
+}
+
+static void
+x_e_puts(const char *s)
+{
+	int adj = x_adj_done;
+
+	while (*s && adj == x_adj_done)
+		x_e_putc3(&s);
+}
+
+/*-
+ * NAME:
+ *	x_set_arg - set an arg value for next function
+ *
+ * DESCRIPTION:
+ *	This is a simple implementation of M-[0-9].
+ *
+ * RETURN VALUE:
+ *	KSTD
+ */
+static int
+x_set_arg(int c)
+{
+	unsigned int n = 0;
+	bool first = true;
+
+	/* strip command prefix */
+	c &= 255;
+	while (c >= 0 && ksh_isdigit(c)) {
+		n = n * 10 + (c - '0');
+		if (n > LINE)
+			/* upper bound for repeat */
+			goto x_set_arg_too_big;
+		c = x_e_getc();
+		first = false;
+	}
+	if (c < 0 || first) {
+ x_set_arg_too_big:
+		x_e_putc2(7);
+		x_arg = 1;
+		x_arg_defaulted = true;
+	} else {
+		x_e_ungetc(c);
+		x_arg = n;
+		x_arg_defaulted = false;
+	}
+	return (KSTD);
+}
+
+/* Comment or uncomment the current line. */
+static int
+x_comment(int c MKSH_A_UNUSED)
+{
+	int oldsize = x_size_str(xbuf);
+	ssize_t len = xep - xbuf;
+	int ret = x_do_comment(xbuf, xend - xbuf, &len);
+
+	if (ret < 0)
+		x_e_putc2(7);
+	else {
+		x_modified();
+		xep = xbuf + len;
+		*xep = '\0';
+		xcp = xbp = xbuf;
+		x_redraw(oldsize);
+		if (ret > 0)
+			return (x_newline('\n'));
+	}
+	return (KSTD);
+}
+
+static int
+x_version(int c MKSH_A_UNUSED)
+{
+	char *o_xbuf = xbuf, *o_xend = xend;
+	char *o_xbp = xbp, *o_xep = xep, *o_xcp = xcp;
+	int lim = x_lastcp() - xbp;
+	size_t vlen;
+	char *v;
+
+	strdupx(v, KSH_VERSION, ATEMP);
+
+	xbuf = xbp = xcp = v;
+	xend = xep = v + (vlen = strlen(v));
+	x_redraw(lim);
+	x_flush();
+
+	c = x_e_getc();
+	xbuf = o_xbuf;
+	xend = o_xend;
+	xbp = o_xbp;
+	xep = o_xep;
+	xcp = o_xcp;
+	x_redraw((int)vlen);
+
+	if (c < 0)
+		return (KSTD);
+	/* This is what AT&T ksh seems to do... Very bizarre */
+	if (c != ' ')
+		x_e_ungetc(c);
+
+	afree(v, ATEMP);
+	return (KSTD);
+}
+
+#ifndef MKSH_SMALL
+static int
+x_edit_line(int c MKSH_A_UNUSED)
+{
+	if (x_arg_defaulted) {
+		if (xep == xbuf) {
+			x_e_putc2(7);
+			return (KSTD);
+		}
+		if (modified) {
+			*xep = '\0';
+			histsave(&source->line, xbuf, true, true);
+			x_arg = 0;
+		} else
+			x_arg = source->line - (histptr - x_histp);
+	}
+	if (x_arg)
+		shf_snprintf(xbuf, xend - xbuf, "%s %d",
+		    "fc -e ${VISUAL:-${EDITOR:-vi}} --", x_arg);
+	else
+		strlcpy(xbuf, "fc -e ${VISUAL:-${EDITOR:-vi}} --", xend - xbuf);
+	xep = xbuf + strlen(xbuf);
+	return (x_newline('\n'));
+}
+#endif
+
+/*-
+ * NAME:
+ *	x_prev_histword - recover word from prev command
+ *
+ * DESCRIPTION:
+ *	This function recovers the last word from the previous
+ *	command and inserts it into the current edit line. If a
+ *	numeric arg is supplied then the n'th word from the
+ *	start of the previous command is used.
+ *	As a side effect, trashes the mark in order to achieve
+ *	being called in a repeatable fashion.
+ *
+ *	Bound to M-.
+ *
+ * RETURN VALUE:
+ *	KSTD
+ */
+static int
+x_prev_histword(int c MKSH_A_UNUSED)
+{
+	char *rcp, *cp;
+	char **xhp;
+	int m = 1;
+	/* -1 = defaulted; 0+ = argument */
+	static int last_arg = -1;
+
+	if (x_last_command == XFUNC_prev_histword) {
+		if (xmp && modified > 1)
+			x_kill_region(0);
+		if (modified)
+			m = modified;
+	} else
+		last_arg = x_arg_defaulted ? -1 : x_arg;
+	xhp = histptr - (m - 1);
+	if ((xhp < history) || !(cp = *xhp)) {
+		x_e_putc2(7);
+		x_modified();
+		return (KSTD);
+	}
+	x_set_mark(0);
+	if ((x_arg = last_arg) == -1) {
+		/* x_arg_defaulted */
+
+		rcp = &cp[strlen(cp) - 1];
+		/*
+		 * ignore white-space after the last word
+		 */
+		while (rcp > cp && is_cfs(*rcp))
+			rcp--;
+		while (rcp > cp && !is_cfs(*rcp))
+			rcp--;
+		if (is_cfs(*rcp))
+			rcp++;
+		x_ins(rcp);
+	} else {
+		/* not x_arg_defaulted */
+		char ch;
+
+		rcp = cp;
+		/*
+		 * ignore white-space at start of line
+		 */
+		while (*rcp && is_cfs(*rcp))
+			rcp++;
+		while (x_arg-- > 0) {
+			while (*rcp && !is_cfs(*rcp))
+				rcp++;
+			while (*rcp && is_cfs(*rcp))
+				rcp++;
+		}
+		cp = rcp;
+		while (*rcp && !is_cfs(*rcp))
+			rcp++;
+		ch = *rcp;
+		*rcp = '\0';
+		x_ins(cp);
+		*rcp = ch;
+	}
+	modified = m + 1;
+	return (KSTD);
+}
+
+#ifndef MKSH_SMALL
+/* Uppercase N(1) words */
+static int
+x_fold_upper(int c MKSH_A_UNUSED)
+{
+	return (x_fold_case('U'));
+}
+
+/* Lowercase N(1) words */
+static int
+x_fold_lower(int c MKSH_A_UNUSED)
+{
+	return (x_fold_case('L'));
+}
+
+/* Titlecase N(1) words */
+static int
+x_fold_capitalise(int c MKSH_A_UNUSED)
+{
+	return (x_fold_case('C'));
+}
+
+/*-
+ * NAME:
+ *	x_fold_case - convert word to UPPER/lower/Capital case
+ *
+ * DESCRIPTION:
+ *	This function is used to implement M-U/M-u, M-L/M-l, M-C/M-c
+ *	to UPPER CASE, lower case or Capitalise Words.
+ *
+ * RETURN VALUE:
+ *	None
+ */
+static int
+x_fold_case(int c)
+{
+	char *cp = xcp;
+
+	if (cp == xep) {
+		x_e_putc2(7);
+		return (KSTD);
+	}
+	while (x_arg--) {
+		/*
+		 * first skip over any white-space
+		 */
+		while (cp != xep && is_mfs(*cp))
+			cp++;
+		/*
+		 * do the first char on its own since it may be
+		 * a different action than for the rest.
+		 */
+		if (cp != xep) {
+			if (c == 'L')
+				/* lowercase */
+				*cp = ksh_tolower(*cp);
+			else
+				/* uppercase, capitalise */
+				*cp = ksh_toupper(*cp);
+			cp++;
+		}
+		/*
+		 * now for the rest of the word
+		 */
+		while (cp != xep && !is_mfs(*cp)) {
+			if (c == 'U')
+				/* uppercase */
+				*cp = ksh_toupper(*cp);
+			else
+				/* lowercase, capitalise */
+				*cp = ksh_tolower(*cp);
+			cp++;
+		}
+	}
+	x_goto(cp);
+	x_modified();
+	return (KSTD);
+}
+#endif
+
+/*-
+ * NAME:
+ *	x_lastcp - last visible char
+ *
+ * SYNOPSIS:
+ *	x_lastcp()
+ *
+ * DESCRIPTION:
+ *	This function returns a pointer to that char in the
+ *	edit buffer that will be the last displayed on the
+ *	screen. The sequence:
+ *
+ *	cp = x_lastcp();
+ *	while (cp > xcp)
+ *		x_bs3(&cp);
+ *
+ *	Will position the cursor correctly on the screen.
+ *
+ * RETURN VALUE:
+ *	cp or NULL
+ */
+static char *
+x_lastcp(void)
+{
+	if (!xlp_valid) {
+		int i = 0, j;
+		char *xlp2;
+
+		xlp = xbp;
+		while (xlp < xep) {
+			j = x_size2(xlp, &xlp2);
+			if ((i + j) > x_displen)
+				break;
+			i += j;
+			xlp = xlp2;
+		}
+	}
+	xlp_valid = true;
+	return (xlp);
+}
+
+static void
+x_mode(bool onoff)
+{
+	static bool x_cur_mode;
+
+	if (x_cur_mode == onoff)
+		return;
+	x_cur_mode = onoff;
+
+	if (onoff) {
+		x_mkraw(tty_fd, NULL, false);
+
+		edchars.erase = tty_state.c_cc[VERASE];
+		edchars.kill = tty_state.c_cc[VKILL];
+		edchars.intr = tty_state.c_cc[VINTR];
+		edchars.quit = tty_state.c_cc[VQUIT];
+		edchars.eof = tty_state.c_cc[VEOF];
+#ifdef VWERASE
+		edchars.werase = tty_state.c_cc[VWERASE];
+#endif
+
+#ifdef _POSIX_VDISABLE
+		/* Convert unset values to internal 'unset' value */
+		if (edchars.erase == _POSIX_VDISABLE)
+			edchars.erase = -1;
+		if (edchars.kill == _POSIX_VDISABLE)
+			edchars.kill = -1;
+		if (edchars.intr == _POSIX_VDISABLE)
+			edchars.intr = -1;
+		if (edchars.quit == _POSIX_VDISABLE)
+			edchars.quit = -1;
+		if (edchars.eof == _POSIX_VDISABLE)
+			edchars.eof = -1;
+		if (edchars.werase == _POSIX_VDISABLE)
+			edchars.werase = -1;
+#endif
+
+		if (edchars.erase >= 0) {
+			bind_if_not_bound(0, edchars.erase, XFUNC_del_back);
+			bind_if_not_bound(1, edchars.erase, XFUNC_del_bword);
+		}
+		if (edchars.kill >= 0)
+			bind_if_not_bound(0, edchars.kill, XFUNC_del_line);
+		if (edchars.werase >= 0)
+			bind_if_not_bound(0, edchars.werase, XFUNC_del_bword);
+		if (edchars.intr >= 0)
+			bind_if_not_bound(0, edchars.intr, XFUNC_abort);
+		if (edchars.quit >= 0)
+			bind_if_not_bound(0, edchars.quit, XFUNC_noop);
+	} else
+		mksh_tcset(tty_fd, &tty_state);
+}
+
+#if !MKSH_S_NOVI
+/* +++ vi editing mode +++ */
+
+struct edstate {
+	char *cbuf;
+	ssize_t winleft;
+	ssize_t cbufsize;
+	ssize_t linelen;
+	ssize_t cursor;
+};
+
+static int vi_hook(int);
+static int nextstate(int);
+static int vi_insert(int);
+static int vi_cmd(int, const char *);
+static int domove(int, const char *, int);
+static int redo_insert(int);
+static void yank_range(int, int);
+static int bracktype(int);
+static void save_cbuf(void);
+static void restore_cbuf(void);
+static int putbuf(const char *, ssize_t, bool);
+static void del_range(int, int);
+static int findch(int, int, bool, bool) MKSH_A_PURE;
+static int forwword(int);
+static int backword(int);
+static int endword(int);
+static int Forwword(int);
+static int Backword(int);
+static int Endword(int);
+static int grabhist(int, int);
+static int grabsearch(int, int, int, const char *);
+static void redraw_line(bool);
+static void refresh(int);
+static int outofwin(void);
+static void rewindow(void);
+static int newcol(unsigned char, int);
+static void display(char *, char *, int);
+static void ed_mov_opt(int, char *);
+static int expand_word(int);
+static int complete_word(int, int);
+static int print_expansions(struct edstate *, int);
+#define char_len(c)	((ISCTRL((unsigned char)c) && \
+			/* but not C1 */ (unsigned char)c < 0x80) ? 2 : 1)
+static void x_vi_zotc(int);
+static void vi_error(void);
+static void vi_macro_reset(void);
+static int x_vi_putbuf(const char *, size_t);
+
+#define vC	0x01		/* a valid command that isn't a vM, vE, vU */
+#define vM	0x02		/* movement command (h, l, etc.) */
+#define vE	0x04		/* extended command (c, d, y) */
+#define vX	0x08		/* long command (@, f, F, t, T, etc.) */
+#define vU	0x10		/* an UN-undoable command (that isn't a vM) */
+#define vB	0x20		/* bad command (^@) */
+#define vZ	0x40		/* repeat count defaults to 0 (not 1) */
+#define vS	0x80		/* search (/, ?) */
+
+#define is_bad(c)	(classify[(c)&0x7f]&vB)
+#define is_cmd(c)	(classify[(c)&0x7f]&(vM|vE|vC|vU))
+#define is_move(c)	(classify[(c)&0x7f]&vM)
+#define is_extend(c)	(classify[(c)&0x7f]&vE)
+#define is_long(c)	(classify[(c)&0x7f]&vX)
+#define is_undoable(c)	(!(classify[(c)&0x7f]&vU))
+#define is_srch(c)	(classify[(c)&0x7f]&vS)
+#define is_zerocount(c)	(classify[(c)&0x7f]&vZ)
+
+static const unsigned char classify[128] = {
+/*	 0	1	2	3	4	5	6	7	*/
+/* 0	^@	^A	^B	^C	^D	^E	^F	^G	*/
+	vB,	0,	0,	0,	0,	vC|vU,	vC|vZ,	0,
+/* 1	^H	^I	^J	^K	^L	^M	^N	^O	*/
+	vM,	vC|vZ,	0,	0,	vC|vU,	0,	vC,	0,
+/* 2	^P	^Q	^R	^S	^T	^U	^V	^W	*/
+	vC,	0,	vC|vU,	0,	0,	0,	vC,	0,
+/* 3	^X	^Y	^Z	^[	^\	^]	^^	^_	*/
+	vC,	0,	0,	vC|vZ,	0,	0,	0,	0,
+/* 4	<space>	!	"	#	$	%	&	'	*/
+	vM,	0,	0,	vC,	vM,	vM,	0,	0,
+/* 5	(	)	*	+	,	-	.	/	*/
+	0,	0,	vC,	vC,	vM,	vC,	0,	vC|vS,
+/* 6	0	1	2	3	4	5	6	7	*/
+	vM,	0,	0,	0,	0,	0,	0,	0,
+/* 7	8	9	:	;	<	=	>	?	*/
+	0,	0,	0,	vM,	0,	vC,	0,	vC|vS,
+/* 8	@	A	B	C	D	E	F	G	*/
+	vC|vX,	vC,	vM,	vC,	vC,	vM,	vM|vX,	vC|vU|vZ,
+/* 9	H	I	J	K	L	M	N	O	*/
+	0,	vC,	0,	0,	0,	0,	vC|vU,	vU,
+/* A	P	Q	R	S	T	U	V	W	*/
+	vC,	0,	vC,	vC,	vM|vX,	vC,	0,	vM,
+/* B	X	Y	Z	[	\	]	^	_	*/
+	vC,	vC|vU,	0,	vU,	vC|vZ,	0,	vM,	vC|vZ,
+/* C	`	a	b	c	d	e	f	g	*/
+	0,	vC,	vM,	vE,	vE,	vM,	vM|vX,	vC|vZ,
+/* D	h	i	j	k	l	m	n	o	*/
+	vM,	vC,	vC|vU,	vC|vU,	vM,	0,	vC|vU,	0,
+/* E	p	q	r	s	t	u	v	w	*/
+	vC,	0,	vX,	vC,	vM|vX,	vC|vU,	vC|vU|vZ, vM,
+/* F	x	y	z	{	|	}	~	^?	*/
+	vC,	vE|vU,	0,	0,	vM|vZ,	0,	vC,	0
+};
+
+#define MAXVICMD	3
+#define SRCHLEN		40
+
+#define INSERT		1
+#define REPLACE		2
+
+#define VNORMAL		0		/* command, insert or replace mode */
+#define VARG1		1		/* digit prefix (first, eg, 5l) */
+#define VEXTCMD		2		/* cmd + movement (eg, cl) */
+#define VARG2		3		/* digit prefix (second, eg, 2c3l) */
+#define VXCH		4		/* f, F, t, T, @ */
+#define VFAIL		5		/* bad command */
+#define VCMD		6		/* single char command (eg, X) */
+#define VREDO		7		/* . */
+#define VLIT		8		/* ^V */
+#define VSEARCH		9		/* /, ? */
+#define VVERSION	10		/* <ESC> ^V */
+#define VPREFIX2	11		/* ^[[ and ^[O in insert mode */
+
+static struct edstate	*save_edstate(struct edstate *old);
+static void		restore_edstate(struct edstate *old, struct edstate *news);
+static void		free_edstate(struct edstate *old);
+
+static struct edstate	ebuf;
+static struct edstate	undobuf;
+
+static struct edstate	*es;		/* current editor state */
+static struct edstate	*undo;
+
+static char *ibuf;			/* input buffer */
+static bool first_insert;		/* set when starting in insert mode */
+static int saved_inslen;		/* saved inslen for first insert */
+static int inslen;			/* length of input buffer */
+static int srchlen;			/* length of current search pattern */
+static char *ybuf;			/* yank buffer */
+static int yanklen;			/* length of yank buffer */
+static int fsavecmd = ' ';		/* last find command */
+static int fsavech;			/* character to find */
+static char lastcmd[MAXVICMD];		/* last non-move command */
+static int lastac;			/* argcnt for lastcmd */
+static int lastsearch = ' ';		/* last search command */
+static char srchpat[SRCHLEN];		/* last search pattern */
+static int insert;			/* <>0 in insert mode */
+static int hnum;			/* position in history */
+static int ohnum;			/* history line copied (after mod) */
+static int hlast;			/* 1 past last position in history */
+static int state;
+
+/*
+ * Information for keeping track of macros that are being expanded.
+ * The format of buf is the alias contents followed by a NUL byte followed
+ * by the name (letter) of the alias. The end of the buffer is marked by
+ * a double NUL. The name of the alias is stored so recursive macros can
+ * be detected.
+ */
+struct macro_state {
+	unsigned char *p;	/* current position in buf */
+	unsigned char *buf;	/* pointer to macro(s) being expanded */
+	size_t len;		/* how much data in buffer */
+};
+static struct macro_state macro;
+
+/* last input was expanded */
+static enum expand_mode {
+	NONE = 0, EXPAND, COMPLETE, PRINT
+} expanded;
+
+static int
+x_vi(char *buf)
+{
+	int c;
+
+	state = VNORMAL;
+	ohnum = hnum = hlast = histnum(-1) + 1;
+	insert = INSERT;
+	saved_inslen = inslen;
+	first_insert = true;
+	inslen = 0;
+	vi_macro_reset();
+
+	ebuf.cbuf = buf;
+	if (undobuf.cbuf == NULL) {
+		ibuf = alloc(LINE, AEDIT);
+		ybuf = alloc(LINE, AEDIT);
+		undobuf.cbuf = alloc(LINE, AEDIT);
+	}
+	undobuf.cbufsize = ebuf.cbufsize = LINE;
+	undobuf.linelen = ebuf.linelen = 0;
+	undobuf.cursor = ebuf.cursor = 0;
+	undobuf.winleft = ebuf.winleft = 0;
+	es = &ebuf;
+	undo = &undobuf;
+
+	x_init_prompt(true);
+	x_col = pwidth;
+
+	if (wbuf_len != x_cols - 3 && ((wbuf_len = x_cols - 3))) {
+		wbuf[0] = aresize(wbuf[0], wbuf_len, AEDIT);
+		wbuf[1] = aresize(wbuf[1], wbuf_len, AEDIT);
+	}
+	if (wbuf_len) {
+		memset(wbuf[0], ' ', wbuf_len);
+		memset(wbuf[1], ' ', wbuf_len);
+	}
+	winwidth = x_cols - pwidth - 3;
+	win = 0;
+	morec = ' ';
+	lastref = 1;
+	holdlen = 0;
+
+	editmode = 2;
+	x_flush();
+	while (/* CONSTCOND */ 1) {
+		if (macro.p) {
+			c = (unsigned char)*macro.p++;
+			/* end of current macro? */
+			if (!c) {
+				/* more macros left to finish? */
+				if (*macro.p++)
+					continue;
+				/* must be the end of all the macros */
+				vi_macro_reset();
+				c = x_getc();
+			}
+		} else
+			c = x_getc();
+
+		if (c == -1)
+			break;
+		if (state != VLIT) {
+			if (c == edchars.intr || c == edchars.quit) {
+				/* pretend we got an interrupt */
+				x_vi_zotc(c);
+				x_flush();
+				trapsig(c == edchars.intr ? SIGINT : SIGQUIT);
+				x_mode(false);
+				unwind(LSHELL);
+			} else if (c == edchars.eof && state != VVERSION) {
+				if (es->linelen == 0) {
+					x_vi_zotc(edchars.eof);
+					c = -1;
+					break;
+				}
+				continue;
+			}
+		}
+		if (vi_hook(c))
+			break;
+		x_flush();
+	}
+
+	x_putc('\r');
+	x_putc('\n');
+	x_flush();
+
+	if (c == -1 || (ssize_t)LINE <= es->linelen)
+		return (-1);
+
+	if (es->cbuf != buf)
+		memcpy(buf, es->cbuf, es->linelen);
+
+	buf[es->linelen++] = '\n';
+
+	return (es->linelen);
+}
+
+static int
+vi_hook(int ch)
+{
+	static char curcmd[MAXVICMD], locpat[SRCHLEN];
+	static int cmdlen, argc1, argc2;
+
+	switch (state) {
+
+	case VNORMAL:
+		if (insert != 0) {
+			if (ch == CTRL('v')) {
+				state = VLIT;
+				ch = '^';
+			}
+			switch (vi_insert(ch)) {
+			case -1:
+				vi_error();
+				state = VNORMAL;
+				break;
+			case 0:
+				if (state == VLIT) {
+					es->cursor--;
+					refresh(0);
+				} else
+					refresh(insert != 0);
+				break;
+			case 1:
+				return (1);
+			}
+		} else {
+			if (ch == '\r' || ch == '\n')
+				return (1);
+			cmdlen = 0;
+			argc1 = 0;
+			if (ch >= '1' && ch <= '9') {
+				argc1 = ch - '0';
+				state = VARG1;
+			} else {
+				curcmd[cmdlen++] = ch;
+				state = nextstate(ch);
+				if (state == VSEARCH) {
+					save_cbuf();
+					es->cursor = 0;
+					es->linelen = 0;
+					if (putbuf(ch == '/' ? "/" : "?", 1,
+					    false) != 0)
+						return (-1);
+					refresh(0);
+				}
+				if (state == VVERSION) {
+					save_cbuf();
+					es->cursor = 0;
+					es->linelen = 0;
+					putbuf(KSH_VERSION,
+					    strlen(KSH_VERSION), false);
+					refresh(0);
+				}
+			}
+		}
+		break;
+
+	case VLIT:
+		if (is_bad(ch)) {
+			del_range(es->cursor, es->cursor + 1);
+			vi_error();
+		} else
+			es->cbuf[es->cursor++] = ch;
+		refresh(1);
+		state = VNORMAL;
+		break;
+
+	case VVERSION:
+		restore_cbuf();
+		state = VNORMAL;
+		refresh(0);
+		break;
+
+	case VARG1:
+		if (ksh_isdigit(ch))
+			argc1 = argc1 * 10 + ch - '0';
+		else {
+			curcmd[cmdlen++] = ch;
+			state = nextstate(ch);
+		}
+		break;
+
+	case VEXTCMD:
+		argc2 = 0;
+		if (ch >= '1' && ch <= '9') {
+			argc2 = ch - '0';
+			state = VARG2;
+			return (0);
+		} else {
+			curcmd[cmdlen++] = ch;
+			if (ch == curcmd[0])
+				state = VCMD;
+			else if (is_move(ch))
+				state = nextstate(ch);
+			else
+				state = VFAIL;
+		}
+		break;
+
+	case VARG2:
+		if (ksh_isdigit(ch))
+			argc2 = argc2 * 10 + ch - '0';
+		else {
+			if (argc1 == 0)
+				argc1 = argc2;
+			else
+				argc1 *= argc2;
+			curcmd[cmdlen++] = ch;
+			if (ch == curcmd[0])
+				state = VCMD;
+			else if (is_move(ch))
+				state = nextstate(ch);
+			else
+				state = VFAIL;
+		}
+		break;
+
+	case VXCH:
+		if (ch == CTRL('['))
+			state = VNORMAL;
+		else {
+			curcmd[cmdlen++] = ch;
+			state = VCMD;
+		}
+		break;
+
+	case VSEARCH:
+		if (ch == '\r' || ch == '\n' /*|| ch == CTRL('[')*/ ) {
+			restore_cbuf();
+			/* Repeat last search? */
+			if (srchlen == 0) {
+				if (!srchpat[0]) {
+					vi_error();
+					state = VNORMAL;
+					refresh(0);
+					return (0);
+				}
+			} else {
+				locpat[srchlen] = '\0';
+				memcpy(srchpat, locpat, srchlen + 1);
+			}
+			state = VCMD;
+		} else if (ch == edchars.erase || ch == CTRL('h')) {
+			if (srchlen != 0) {
+				srchlen--;
+				es->linelen -= char_len(locpat[srchlen]);
+				es->cursor = es->linelen;
+				refresh(0);
+				return (0);
+			}
+			restore_cbuf();
+			state = VNORMAL;
+			refresh(0);
+		} else if (ch == edchars.kill) {
+			srchlen = 0;
+			es->linelen = 1;
+			es->cursor = 1;
+			refresh(0);
+			return (0);
+		} else if (ch == edchars.werase) {
+			unsigned int i, n;
+			struct edstate new_es, *save_es;
+
+			new_es.cursor = srchlen;
+			new_es.cbuf = locpat;
+
+			save_es = es;
+			es = &new_es;
+			n = backword(1);
+			es = save_es;
+
+			i = (unsigned)srchlen;
+			while (--i >= n)
+				es->linelen -= char_len(locpat[i]);
+			srchlen = (int)n;
+			es->cursor = es->linelen;
+			refresh(0);
+			return (0);
+		} else {
+			if (srchlen == SRCHLEN - 1)
+				vi_error();
+			else {
+				locpat[srchlen++] = ch;
+				if (ISCTRL(ch) && /* but not C1 */ ch < 0x80) {
+					if ((size_t)es->linelen + 2 >
+					    (size_t)es->cbufsize)
+						vi_error();
+					es->cbuf[es->linelen++] = '^';
+					es->cbuf[es->linelen++] = UNCTRL(ch);
+				} else {
+					if (es->linelen >= es->cbufsize)
+						vi_error();
+					es->cbuf[es->linelen++] = ch;
+				}
+				es->cursor = es->linelen;
+				refresh(0);
+			}
+			return (0);
+		}
+		break;
+
+	case VPREFIX2:
+		state = VFAIL;
+		switch (ch) {
+		case 'A':
+			/* the cursor may not be at the BOL */
+			if (!es->cursor)
+				break;
+			/* nor further in the line than we can search for */
+			if ((size_t)es->cursor >= sizeof(srchpat) - 1)
+				es->cursor = sizeof(srchpat) - 2;
+			/* anchor the search pattern */
+			srchpat[0] = '^';
+			/* take the current line up to the cursor */
+			memmove(srchpat + 1, es->cbuf, es->cursor);
+			srchpat[es->cursor + 1] = '\0';
+			/* set a magic flag */
+			argc1 = 2 + (int)es->cursor;
+			/* and emulate a backwards history search */
+			lastsearch = '/';
+			*curcmd = 'n';
+			goto pseudo_VCMD;
+		}
+		break;
+	}
+
+	switch (state) {
+	case VCMD:
+ pseudo_VCMD:
+		state = VNORMAL;
+		switch (vi_cmd(argc1, curcmd)) {
+		case -1:
+			vi_error();
+			refresh(0);
+			break;
+		case 0:
+			if (insert != 0)
+				inslen = 0;
+			refresh(insert != 0);
+			break;
+		case 1:
+			refresh(0);
+			return (1);
+		case 2:
+			/* back from a 'v' command - don't redraw the screen */
+			return (1);
+		}
+		break;
+
+	case VREDO:
+		state = VNORMAL;
+		if (argc1 != 0)
+			lastac = argc1;
+		switch (vi_cmd(lastac, lastcmd)) {
+		case -1:
+			vi_error();
+			refresh(0);
+			break;
+		case 0:
+			if (insert != 0) {
+				if (lastcmd[0] == 's' || lastcmd[0] == 'c' ||
+				    lastcmd[0] == 'C') {
+					if (redo_insert(1) != 0)
+						vi_error();
+				} else {
+					if (redo_insert(lastac) != 0)
+						vi_error();
+				}
+			}
+			refresh(0);
+			break;
+		case 1:
+			refresh(0);
+			return (1);
+		case 2:
+			/* back from a 'v' command - can't happen */
+			break;
+		}
+		break;
+
+	case VFAIL:
+		state = VNORMAL;
+		vi_error();
+		break;
+	}
+	return (0);
+}
+
+static int
+nextstate(int ch)
+{
+	if (is_extend(ch))
+		return (VEXTCMD);
+	else if (is_srch(ch))
+		return (VSEARCH);
+	else if (is_long(ch))
+		return (VXCH);
+	else if (ch == '.')
+		return (VREDO);
+	else if (ch == CTRL('v'))
+		return (VVERSION);
+	else if (is_cmd(ch))
+		return (VCMD);
+	else
+		return (VFAIL);
+}
+
+static int
+vi_insert(int ch)
+{
+	int tcursor;
+
+	if (ch == edchars.erase || ch == CTRL('h')) {
+		if (insert == REPLACE) {
+			if (es->cursor == undo->cursor) {
+				vi_error();
+				return (0);
+			}
+			if (inslen > 0)
+				inslen--;
+			es->cursor--;
+			if (es->cursor >= undo->linelen)
+				es->linelen--;
+			else
+				es->cbuf[es->cursor] = undo->cbuf[es->cursor];
+		} else {
+			if (es->cursor == 0)
+				return (0);
+			if (inslen > 0)
+				inslen--;
+			es->cursor--;
+			es->linelen--;
+			memmove(&es->cbuf[es->cursor], &es->cbuf[es->cursor + 1],
+			    es->linelen - es->cursor + 1);
+		}
+		expanded = NONE;
+		return (0);
+	}
+	if (ch == edchars.kill) {
+		if (es->cursor != 0) {
+			inslen = 0;
+			memmove(es->cbuf, &es->cbuf[es->cursor],
+			    es->linelen - es->cursor);
+			es->linelen -= es->cursor;
+			es->cursor = 0;
+		}
+		expanded = NONE;
+		return (0);
+	}
+	if (ch == edchars.werase) {
+		if (es->cursor != 0) {
+			tcursor = backword(1);
+			memmove(&es->cbuf[tcursor], &es->cbuf[es->cursor],
+			    es->linelen - es->cursor);
+			es->linelen -= es->cursor - tcursor;
+			if (inslen < es->cursor - tcursor)
+				inslen = 0;
+			else
+				inslen -= es->cursor - tcursor;
+			es->cursor = tcursor;
+		}
+		expanded = NONE;
+		return (0);
+	}
+	/*
+	 * If any chars are entered before escape, trash the saved insert
+	 * buffer (if user inserts & deletes char, ibuf gets trashed and
+	 * we don't want to use it)
+	 */
+	if (first_insert && ch != CTRL('['))
+		saved_inslen = 0;
+	switch (ch) {
+	case '\0':
+		return (-1);
+
+	case '\r':
+	case '\n':
+		return (1);
+
+	case CTRL('['):
+		expanded = NONE;
+		if (first_insert) {
+			first_insert = false;
+			if (inslen == 0) {
+				inslen = saved_inslen;
+				return (redo_insert(0));
+			}
+			lastcmd[0] = 'a';
+			lastac = 1;
+		}
+		if (lastcmd[0] == 's' || lastcmd[0] == 'c' ||
+		    lastcmd[0] == 'C')
+			return (redo_insert(0));
+		else
+			return (redo_insert(lastac - 1));
+
+	/* { Begin nonstandard vi commands */
+	case CTRL('x'):
+		expand_word(0);
+		break;
+
+	case CTRL('f'):
+		complete_word(0, 0);
+		break;
+
+	case CTRL('e'):
+		print_expansions(es, 0);
+		break;
+
+	case CTRL('i'):
+		if (Flag(FVITABCOMPLETE)) {
+			complete_word(0, 0);
+			break;
+		}
+		/* FALLTHROUGH */
+	/* End nonstandard vi commands } */
+
+	default:
+		if (es->linelen >= es->cbufsize - 1)
+			return (-1);
+		ibuf[inslen++] = ch;
+		if (insert == INSERT) {
+			memmove(&es->cbuf[es->cursor + 1], &es->cbuf[es->cursor],
+			    es->linelen - es->cursor);
+			es->linelen++;
+		}
+		es->cbuf[es->cursor++] = ch;
+		if (insert == REPLACE && es->cursor > es->linelen)
+			es->linelen++;
+		expanded = NONE;
+	}
+	return (0);
+}
+
+static int
+vi_cmd(int argcnt, const char *cmd)
+{
+	int ncursor;
+	int cur, c1, c2, c3 = 0;
+	int any;
+	struct edstate *t;
+
+	if (argcnt == 0 && !is_zerocount(*cmd))
+		argcnt = 1;
+
+	if (is_move(*cmd)) {
+		if ((cur = domove(argcnt, cmd, 0)) >= 0) {
+			if (cur == es->linelen && cur != 0)
+				cur--;
+			es->cursor = cur;
+		} else
+			return (-1);
+	} else {
+		/* Don't save state in middle of macro.. */
+		if (is_undoable(*cmd) && !macro.p) {
+			undo->winleft = es->winleft;
+			memmove(undo->cbuf, es->cbuf, es->linelen);
+			undo->linelen = es->linelen;
+			undo->cursor = es->cursor;
+			lastac = argcnt;
+			memmove(lastcmd, cmd, MAXVICMD);
+		}
+		switch (*cmd) {
+
+		case CTRL('l'):
+		case CTRL('r'):
+			redraw_line(true);
+			break;
+
+		case '@':
+			{
+				static char alias[] = "_\0";
+				struct tbl *ap;
+				size_t olen, nlen;
+				char *p, *nbuf;
+
+				/* lookup letter in alias list... */
+				alias[1] = cmd[1];
+				ap = ktsearch(&aliases, alias, hash(alias));
+				if (!cmd[1] || !ap || !(ap->flag & ISSET))
+					return (-1);
+				/* check if this is a recursive call... */
+				if ((p = (char *)macro.p))
+					while ((p = strnul(p)) && p[1])
+						if (*++p == cmd[1])
+							return (-1);
+				/* insert alias into macro buffer */
+				nlen = strlen(ap->val.s) + 1;
+				olen = !macro.p ? 2 :
+				    macro.len - (macro.p - macro.buf);
+				/*
+				 * at this point, it's fairly reasonable that
+				 * nlen + olen + 2 doesn't overflow
+				 */
+				nbuf = alloc(nlen + 1 + olen, AEDIT);
+				memcpy(nbuf, ap->val.s, nlen);
+				nbuf[nlen++] = cmd[1];
+				if (macro.p) {
+					memcpy(nbuf + nlen, macro.p, olen);
+					afree(macro.buf, AEDIT);
+					nlen += olen;
+				} else {
+					nbuf[nlen++] = '\0';
+					nbuf[nlen++] = '\0';
+				}
+				macro.p = macro.buf = (unsigned char *)nbuf;
+				macro.len = nlen;
+			}
+			break;
+
+		case 'a':
+			modified = 1;
+			hnum = hlast;
+			if (es->linelen != 0)
+				es->cursor++;
+			insert = INSERT;
+			break;
+
+		case 'A':
+			modified = 1;
+			hnum = hlast;
+			del_range(0, 0);
+			es->cursor = es->linelen;
+			insert = INSERT;
+			break;
+
+		case 'S':
+			es->cursor = domove(1, "^", 1);
+			del_range(es->cursor, es->linelen);
+			modified = 1;
+			hnum = hlast;
+			insert = INSERT;
+			break;
+
+		case 'Y':
+			cmd = "y$";
+			/* ahhhhhh... */
+		case 'c':
+		case 'd':
+		case 'y':
+			if (*cmd == cmd[1]) {
+				c1 = *cmd == 'c' ? domove(1, "^", 1) : 0;
+				c2 = es->linelen;
+			} else if (!is_move(cmd[1]))
+				return (-1);
+			else {
+				if ((ncursor = domove(argcnt, &cmd[1], 1)) < 0)
+					return (-1);
+				if (*cmd == 'c' &&
+				    (cmd[1] == 'w' || cmd[1] == 'W') &&
+				    !ksh_isspace(es->cbuf[es->cursor])) {
+					do {
+						--ncursor;
+					} while (ksh_isspace(es->cbuf[ncursor]));
+					ncursor++;
+				}
+				if (ncursor > es->cursor) {
+					c1 = es->cursor;
+					c2 = ncursor;
+				} else {
+					c1 = ncursor;
+					c2 = es->cursor;
+					if (cmd[1] == '%')
+						c2++;
+				}
+			}
+			if (*cmd != 'c' && c1 != c2)
+				yank_range(c1, c2);
+			if (*cmd != 'y') {
+				del_range(c1, c2);
+				es->cursor = c1;
+			}
+			if (*cmd == 'c') {
+				modified = 1;
+				hnum = hlast;
+				insert = INSERT;
+			}
+			break;
+
+		case 'p':
+			modified = 1;
+			hnum = hlast;
+			if (es->linelen != 0)
+				es->cursor++;
+			while (putbuf(ybuf, yanklen, false) == 0 &&
+			    --argcnt > 0)
+				;
+			if (es->cursor != 0)
+				es->cursor--;
+			if (argcnt != 0)
+				return (-1);
+			break;
+
+		case 'P':
+			modified = 1;
+			hnum = hlast;
+			any = 0;
+			while (putbuf(ybuf, yanklen, false) == 0 &&
+			    --argcnt > 0)
+				any = 1;
+			if (any && es->cursor != 0)
+				es->cursor--;
+			if (argcnt != 0)
+				return (-1);
+			break;
+
+		case 'C':
+			modified = 1;
+			hnum = hlast;
+			del_range(es->cursor, es->linelen);
+			insert = INSERT;
+			break;
+
+		case 'D':
+			yank_range(es->cursor, es->linelen);
+			del_range(es->cursor, es->linelen);
+			if (es->cursor != 0)
+				es->cursor--;
+			break;
+
+		case 'g':
+			if (!argcnt)
+				argcnt = hlast;
+			/* FALLTHROUGH */
+		case 'G':
+			if (!argcnt)
+				argcnt = 1;
+			else
+				argcnt = hlast - (source->line - argcnt);
+			if (grabhist(modified, argcnt - 1) < 0)
+				return (-1);
+			else {
+				modified = 0;
+				hnum = argcnt - 1;
+			}
+			break;
+
+		case 'i':
+			modified = 1;
+			hnum = hlast;
+			insert = INSERT;
+			break;
+
+		case 'I':
+			modified = 1;
+			hnum = hlast;
+			es->cursor = domove(1, "^", 1);
+			insert = INSERT;
+			break;
+
+		case 'j':
+		case '+':
+		case CTRL('n'):
+			if (grabhist(modified, hnum + argcnt) < 0)
+				return (-1);
+			else {
+				modified = 0;
+				hnum += argcnt;
+			}
+			break;
+
+		case 'k':
+		case '-':
+		case CTRL('p'):
+			if (grabhist(modified, hnum - argcnt) < 0)
+				return (-1);
+			else {
+				modified = 0;
+				hnum -= argcnt;
+			}
+			break;
+
+		case 'r':
+			if (es->linelen == 0)
+				return (-1);
+			modified = 1;
+			hnum = hlast;
+			if (cmd[1] == 0)
+				vi_error();
+			else {
+				int n;
+
+				if (es->cursor + argcnt > es->linelen)
+					return (-1);
+				for (n = 0; n < argcnt; ++n)
+					es->cbuf[es->cursor + n] = cmd[1];
+				es->cursor += n - 1;
+			}
+			break;
+
+		case 'R':
+			modified = 1;
+			hnum = hlast;
+			insert = REPLACE;
+			break;
+
+		case 's':
+			if (es->linelen == 0)
+				return (-1);
+			modified = 1;
+			hnum = hlast;
+			if (es->cursor + argcnt > es->linelen)
+				argcnt = es->linelen - es->cursor;
+			del_range(es->cursor, es->cursor + argcnt);
+			insert = INSERT;
+			break;
+
+		case 'v':
+			if (!argcnt) {
+				if (es->linelen == 0)
+					return (-1);
+				if (modified) {
+					es->cbuf[es->linelen] = '\0';
+					histsave(&source->line, es->cbuf, true,
+					    true);
+				} else
+					argcnt = source->line + 1 -
+					    (hlast - hnum);
+			}
+			if (argcnt)
+				shf_snprintf(es->cbuf, es->cbufsize, "%s %d",
+				    "fc -e ${VISUAL:-${EDITOR:-vi}} --",
+				    argcnt);
+			else
+				strlcpy(es->cbuf,
+				    "fc -e ${VISUAL:-${EDITOR:-vi}} --",
+				    es->cbufsize);
+			es->linelen = strlen(es->cbuf);
+			return (2);
+
+		case 'x':
+			if (es->linelen == 0)
+				return (-1);
+			modified = 1;
+			hnum = hlast;
+			if (es->cursor + argcnt > es->linelen)
+				argcnt = es->linelen - es->cursor;
+			yank_range(es->cursor, es->cursor + argcnt);
+			del_range(es->cursor, es->cursor + argcnt);
+			break;
+
+		case 'X':
+			if (es->cursor > 0) {
+				modified = 1;
+				hnum = hlast;
+				if (es->cursor < argcnt)
+					argcnt = es->cursor;
+				yank_range(es->cursor - argcnt, es->cursor);
+				del_range(es->cursor - argcnt, es->cursor);
+				es->cursor -= argcnt;
+			} else
+				return (-1);
+			break;
+
+		case 'u':
+			t = es;
+			es = undo;
+			undo = t;
+			break;
+
+		case 'U':
+			if (!modified)
+				return (-1);
+			if (grabhist(modified, ohnum) < 0)
+				return (-1);
+			modified = 0;
+			hnum = ohnum;
+			break;
+
+		case '?':
+			if (hnum == hlast)
+				hnum = -1;
+			/* ahhh */
+		case '/':
+			c3 = 1;
+			srchlen = 0;
+			lastsearch = *cmd;
+			/* FALLTHROUGH */
+		case 'n':
+		case 'N':
+			if (lastsearch == ' ')
+				return (-1);
+			if (lastsearch == '?')
+				c1 = 1;
+			else
+				c1 = 0;
+			if (*cmd == 'N')
+				c1 = !c1;
+			if ((c2 = grabsearch(modified, hnum,
+			    c1, srchpat)) < 0) {
+				if (c3) {
+					restore_cbuf();
+					refresh(0);
+				}
+				return (-1);
+			} else {
+				modified = 0;
+				hnum = c2;
+				ohnum = hnum;
+			}
+			if (argcnt >= 2) {
+				/* flag from cursor-up command */
+				es->cursor = argcnt - 2;
+				return (0);
+			}
+			break;
+		case '_':
+			{
+				bool inspace;
+				char *p, *sp;
+
+				if (histnum(-1) < 0)
+					return (-1);
+				p = *histpos();
+#define issp(c)		(ksh_isspace(c) || (c) == '\n')
+				if (argcnt) {
+					while (*p && issp(*p))
+						p++;
+					while (*p && --argcnt) {
+						while (*p && !issp(*p))
+							p++;
+						while (*p && issp(*p))
+							p++;
+					}
+					if (!*p)
+						return (-1);
+					sp = p;
+				} else {
+					sp = p;
+					inspace = false;
+					while (*p) {
+						if (issp(*p))
+							inspace = true;
+						else if (inspace) {
+							inspace = false;
+							sp = p;
+						}
+						p++;
+					}
+					p = sp;
+				}
+				modified = 1;
+				hnum = hlast;
+				if (es->cursor != es->linelen)
+					es->cursor++;
+				while (*p && !issp(*p)) {
+					argcnt++;
+					p++;
+				}
+				if (putbuf(" ", 1, false) != 0 ||
+				    putbuf(sp, argcnt, false) != 0) {
+					if (es->cursor != 0)
+						es->cursor--;
+					return (-1);
+				}
+				insert = INSERT;
+			}
+			break;
+
+		case '~':
+			{
+				char *p;
+				int i;
+
+				if (es->linelen == 0)
+					return (-1);
+				for (i = 0; i < argcnt; i++) {
+					p = &es->cbuf[es->cursor];
+					if (ksh_islower(*p)) {
+						modified = 1;
+						hnum = hlast;
+						*p = ksh_toupper(*p);
+					} else if (ksh_isupper(*p)) {
+						modified = 1;
+						hnum = hlast;
+						*p = ksh_tolower(*p);
+					}
+					if (es->cursor < es->linelen - 1)
+						es->cursor++;
+				}
+				break;
+			}
+
+		case '#':
+			{
+				int ret = x_do_comment(es->cbuf, es->cbufsize,
+				    &es->linelen);
+				if (ret >= 0)
+					es->cursor = 0;
+				return (ret);
+			}
+
+		/* AT&T ksh */
+		case '=':
+		/* Nonstandard vi/ksh */
+		case CTRL('e'):
+			print_expansions(es, 1);
+			break;
+
+
+		/* Nonstandard vi/ksh */
+		case CTRL('i'):
+			if (!Flag(FVITABCOMPLETE))
+				return (-1);
+			complete_word(1, argcnt);
+			break;
+
+		/* some annoying AT&T kshs */
+		case CTRL('['):
+			if (!Flag(FVIESCCOMPLETE))
+				return (-1);
+		/* AT&T ksh */
+		case '\\':
+		/* Nonstandard vi/ksh */
+		case CTRL('f'):
+			complete_word(1, argcnt);
+			break;
+
+
+		/* AT&T ksh */
+		case '*':
+		/* Nonstandard vi/ksh */
+		case CTRL('x'):
+			expand_word(1);
+			break;
+
+
+		/* mksh: cursor movement */
+		case '[':
+		case 'O':
+			state = VPREFIX2;
+			if (es->linelen != 0)
+				es->cursor++;
+			insert = INSERT;
+			return (0);
+		}
+		if (insert == 0 && es->cursor != 0 && es->cursor >= es->linelen)
+			es->cursor--;
+	}
+	return (0);
+}
+
+static int
+domove(int argcnt, const char *cmd, int sub)
+{
+	int bcount, i = 0, t;
+	int ncursor = 0;
+
+	switch (*cmd) {
+	case 'b':
+		if (!sub && es->cursor == 0)
+			return (-1);
+		ncursor = backword(argcnt);
+		break;
+
+	case 'B':
+		if (!sub && es->cursor == 0)
+			return (-1);
+		ncursor = Backword(argcnt);
+		break;
+
+	case 'e':
+		if (!sub && es->cursor + 1 >= es->linelen)
+			return (-1);
+		ncursor = endword(argcnt);
+		if (sub && ncursor < es->linelen)
+			ncursor++;
+		break;
+
+	case 'E':
+		if (!sub && es->cursor + 1 >= es->linelen)
+			return (-1);
+		ncursor = Endword(argcnt);
+		if (sub && ncursor < es->linelen)
+			ncursor++;
+		break;
+
+	case 'f':
+	case 'F':
+	case 't':
+	case 'T':
+		fsavecmd = *cmd;
+		fsavech = cmd[1];
+		/* drop through */
+
+	case ',':
+	case ';':
+		if (fsavecmd == ' ')
+			return (-1);
+		i = fsavecmd == 'f' || fsavecmd == 'F';
+		t = fsavecmd > 'a';
+		if (*cmd == ',')
+			t = !t;
+		if ((ncursor = findch(fsavech, argcnt, tobool(t),
+		    tobool(i))) < 0)
+			return (-1);
+		if (sub && t)
+			ncursor++;
+		break;
+
+	case 'h':
+	case CTRL('h'):
+		if (!sub && es->cursor == 0)
+			return (-1);
+		ncursor = es->cursor - argcnt;
+		if (ncursor < 0)
+			ncursor = 0;
+		break;
+
+	case ' ':
+	case 'l':
+		if (!sub && es->cursor + 1 >= es->linelen)
+			return (-1);
+		if (es->linelen != 0) {
+			ncursor = es->cursor + argcnt;
+			if (ncursor > es->linelen)
+				ncursor = es->linelen;
+		}
+		break;
+
+	case 'w':
+		if (!sub && es->cursor + 1 >= es->linelen)
+			return (-1);
+		ncursor = forwword(argcnt);
+		break;
+
+	case 'W':
+		if (!sub && es->cursor + 1 >= es->linelen)
+			return (-1);
+		ncursor = Forwword(argcnt);
+		break;
+
+	case '0':
+		ncursor = 0;
+		break;
+
+	case '^':
+		ncursor = 0;
+		while (ncursor < es->linelen - 1 &&
+		    ksh_isspace(es->cbuf[ncursor]))
+			ncursor++;
+		break;
+
+	case '|':
+		ncursor = argcnt;
+		if (ncursor > es->linelen)
+			ncursor = es->linelen;
+		if (ncursor)
+			ncursor--;
+		break;
+
+	case '$':
+		if (es->linelen != 0)
+			ncursor = es->linelen;
+		else
+			ncursor = 0;
+		break;
+
+	case '%':
+		ncursor = es->cursor;
+		while (ncursor < es->linelen &&
+		    (i = bracktype(es->cbuf[ncursor])) == 0)
+			ncursor++;
+		if (ncursor == es->linelen)
+			return (-1);
+		bcount = 1;
+		do {
+			if (i > 0) {
+				if (++ncursor >= es->linelen)
+					return (-1);
+			} else {
+				if (--ncursor < 0)
+					return (-1);
+			}
+			t = bracktype(es->cbuf[ncursor]);
+			if (t == i)
+				bcount++;
+			else if (t == -i)
+				bcount--;
+		} while (bcount != 0);
+		if (sub && i > 0)
+			ncursor++;
+		break;
+
+	default:
+		return (-1);
+	}
+	return (ncursor);
+}
+
+static int
+redo_insert(int count)
+{
+	while (count-- > 0)
+		if (putbuf(ibuf, inslen, tobool(insert == REPLACE)) != 0)
+			return (-1);
+	if (es->cursor > 0)
+		es->cursor--;
+	insert = 0;
+	return (0);
+}
+
+static void
+yank_range(int a, int b)
+{
+	yanklen = b - a;
+	if (yanklen != 0)
+		memmove(ybuf, &es->cbuf[a], yanklen);
+}
+
+static int
+bracktype(int ch)
+{
+	switch (ch) {
+
+	case '(':
+		return (1);
+
+	case '[':
+		return (2);
+
+	case '{':
+		return (3);
+
+	case ')':
+		return (-1);
+
+	case ']':
+		return (-2);
+
+	case '}':
+		return (-3);
+
+	default:
+		return (0);
+	}
+}
+
+/*
+ *	Non user interface editor routines below here
+ */
+
+static void
+save_cbuf(void)
+{
+	memmove(holdbufp, es->cbuf, es->linelen);
+	holdlen = es->linelen;
+	holdbufp[holdlen] = '\0';
+}
+
+static void
+restore_cbuf(void)
+{
+	es->cursor = 0;
+	es->linelen = holdlen;
+	memmove(es->cbuf, holdbufp, holdlen);
+}
+
+/* return a new edstate */
+static struct edstate *
+save_edstate(struct edstate *old)
+{
+	struct edstate *news;
+
+	news = alloc(sizeof(struct edstate), AEDIT);
+	news->cbuf = alloc(old->cbufsize, AEDIT);
+	memcpy(news->cbuf, old->cbuf, old->linelen);
+	news->cbufsize = old->cbufsize;
+	news->linelen = old->linelen;
+	news->cursor = old->cursor;
+	news->winleft = old->winleft;
+	return (news);
+}
+
+static void
+restore_edstate(struct edstate *news, struct edstate *old)
+{
+	memcpy(news->cbuf, old->cbuf, old->linelen);
+	news->linelen = old->linelen;
+	news->cursor = old->cursor;
+	news->winleft = old->winleft;
+	free_edstate(old);
+}
+
+static void
+free_edstate(struct edstate *old)
+{
+	afree(old->cbuf, AEDIT);
+	afree(old, AEDIT);
+}
+
+/*
+ * this is used for calling x_escape() in complete_word()
+ */
+static int
+x_vi_putbuf(const char *s, size_t len)
+{
+	return (putbuf(s, len, false));
+}
+
+static int
+putbuf(const char *buf, ssize_t len, bool repl)
+{
+	if (len == 0)
+		return (0);
+	if (repl) {
+		if (es->cursor + len >= es->cbufsize)
+			return (-1);
+		if (es->cursor + len > es->linelen)
+			es->linelen = es->cursor + len;
+	} else {
+		if (es->linelen + len >= es->cbufsize)
+			return (-1);
+		memmove(&es->cbuf[es->cursor + len], &es->cbuf[es->cursor],
+		    es->linelen - es->cursor);
+		es->linelen += len;
+	}
+	memmove(&es->cbuf[es->cursor], buf, len);
+	es->cursor += len;
+	return (0);
+}
+
+static void
+del_range(int a, int b)
+{
+	if (es->linelen != b)
+		memmove(&es->cbuf[a], &es->cbuf[b], es->linelen - b);
+	es->linelen -= b - a;
+}
+
+static int
+findch(int ch, int cnt, bool forw, bool incl)
+{
+	int ncursor;
+
+	if (es->linelen == 0)
+		return (-1);
+	ncursor = es->cursor;
+	while (cnt--) {
+		do {
+			if (forw) {
+				if (++ncursor == es->linelen)
+					return (-1);
+			} else {
+				if (--ncursor < 0)
+					return (-1);
+			}
+		} while (es->cbuf[ncursor] != ch);
+	}
+	if (!incl) {
+		if (forw)
+			ncursor--;
+		else
+			ncursor++;
+	}
+	return (ncursor);
+}
+
+static int
+forwword(int argcnt)
+{
+	int ncursor;
+
+	ncursor = es->cursor;
+	while (ncursor < es->linelen && argcnt--) {
+		if (ksh_isalnux(es->cbuf[ncursor]))
+			while (ksh_isalnux(es->cbuf[ncursor]) &&
+			    ncursor < es->linelen)
+				ncursor++;
+		else if (!ksh_isspace(es->cbuf[ncursor]))
+			while (!ksh_isalnux(es->cbuf[ncursor]) &&
+			    !ksh_isspace(es->cbuf[ncursor]) &&
+			    ncursor < es->linelen)
+				ncursor++;
+		while (ksh_isspace(es->cbuf[ncursor]) &&
+		    ncursor < es->linelen)
+			ncursor++;
+	}
+	return (ncursor);
+}
+
+static int
+backword(int argcnt)
+{
+	int ncursor;
+
+	ncursor = es->cursor;
+	while (ncursor > 0 && argcnt--) {
+		while (--ncursor > 0 && ksh_isspace(es->cbuf[ncursor]))
+			;
+		if (ncursor > 0) {
+			if (ksh_isalnux(es->cbuf[ncursor]))
+				while (--ncursor >= 0 &&
+				    ksh_isalnux(es->cbuf[ncursor]))
+					;
+			else
+				while (--ncursor >= 0 &&
+				    !ksh_isalnux(es->cbuf[ncursor]) &&
+				    !ksh_isspace(es->cbuf[ncursor]))
+					;
+			ncursor++;
+		}
+	}
+	return (ncursor);
+}
+
+static int
+endword(int argcnt)
+{
+	int ncursor;
+
+	ncursor = es->cursor;
+	while (ncursor < es->linelen && argcnt--) {
+		while (++ncursor < es->linelen - 1 &&
+		    ksh_isspace(es->cbuf[ncursor]))
+			;
+		if (ncursor < es->linelen - 1) {
+			if (ksh_isalnux(es->cbuf[ncursor]))
+				while (++ncursor < es->linelen &&
+				    ksh_isalnux(es->cbuf[ncursor]))
+					;
+			else
+				while (++ncursor < es->linelen &&
+				    !ksh_isalnux(es->cbuf[ncursor]) &&
+				    !ksh_isspace(es->cbuf[ncursor]))
+					;
+			ncursor--;
+		}
+	}
+	return (ncursor);
+}
+
+static int
+Forwword(int argcnt)
+{
+	int ncursor;
+
+	ncursor = es->cursor;
+	while (ncursor < es->linelen && argcnt--) {
+		while (!ksh_isspace(es->cbuf[ncursor]) &&
+		    ncursor < es->linelen)
+			ncursor++;
+		while (ksh_isspace(es->cbuf[ncursor]) &&
+		    ncursor < es->linelen)
+			ncursor++;
+	}
+	return (ncursor);
+}
+
+static int
+Backword(int argcnt)
+{
+	int ncursor;
+
+	ncursor = es->cursor;
+	while (ncursor > 0 && argcnt--) {
+		while (--ncursor >= 0 && ksh_isspace(es->cbuf[ncursor]))
+			;
+		while (ncursor >= 0 && !ksh_isspace(es->cbuf[ncursor]))
+			ncursor--;
+		ncursor++;
+	}
+	return (ncursor);
+}
+
+static int
+Endword(int argcnt)
+{
+	int ncursor;
+
+	ncursor = es->cursor;
+	while (ncursor < es->linelen - 1 && argcnt--) {
+		while (++ncursor < es->linelen - 1 &&
+		    ksh_isspace(es->cbuf[ncursor]))
+			;
+		if (ncursor < es->linelen - 1) {
+			while (++ncursor < es->linelen &&
+			    !ksh_isspace(es->cbuf[ncursor]))
+				;
+			ncursor--;
+		}
+	}
+	return (ncursor);
+}
+
+static int
+grabhist(int save, int n)
+{
+	char *hptr;
+
+	if (n < 0 || n > hlast)
+		return (-1);
+	if (n == hlast) {
+		restore_cbuf();
+		ohnum = n;
+		return (0);
+	}
+	(void)histnum(n);
+	if ((hptr = *histpos()) == NULL) {
+		internal_warningf("%s: %s", "grabhist", "bad history array");
+		return (-1);
+	}
+	if (save)
+		save_cbuf();
+	if ((es->linelen = strlen(hptr)) >= es->cbufsize)
+		es->linelen = es->cbufsize - 1;
+	memmove(es->cbuf, hptr, es->linelen);
+	es->cursor = 0;
+	ohnum = n;
+	return (0);
+}
+
+static int
+grabsearch(int save, int start, int fwd, const char *pat)
+{
+	char *hptr;
+	int hist;
+	bool anchored;
+
+	if ((start == 0 && fwd == 0) || (start >= hlast - 1 && fwd == 1))
+		return (-1);
+	if (fwd)
+		start++;
+	else
+		start--;
+	anchored = *pat == '^' ? (++pat, true) : false;
+	if ((hist = findhist(start, fwd, pat, anchored)) < 0) {
+		/* (start != 0 && fwd && match(holdbufp, pat) >= 0) */
+		if (start != 0 && fwd && strcmp(holdbufp, pat) >= 0) {
+			restore_cbuf();
+			return (0);
+		} else
+			return (-1);
+	}
+	if (save)
+		save_cbuf();
+	histnum(hist);
+	hptr = *histpos();
+	if ((es->linelen = strlen(hptr)) >= es->cbufsize)
+		es->linelen = es->cbufsize - 1;
+	memmove(es->cbuf, hptr, es->linelen);
+	es->cursor = 0;
+	return (hist);
+}
+
+static void
+redraw_line(bool newl)
+{
+	if (wbuf_len)
+		memset(wbuf[win], ' ', wbuf_len);
+	if (newl) {
+		x_putc('\r');
+		x_putc('\n');
+	}
+	if (prompt_trunc != -1)
+		pprompt(prompt, prompt_trunc);
+	x_col = pwidth;
+	morec = ' ';
+}
+
+static void
+refresh(int leftside)
+{
+	if (leftside < 0)
+		leftside = lastref;
+	else
+		lastref = leftside;
+	if (outofwin())
+		rewindow();
+	display(wbuf[1 - win], wbuf[win], leftside);
+	win = 1 - win;
+}
+
+static int
+outofwin(void)
+{
+	int cur, col;
+
+	if (es->cursor < es->winleft)
+		return (1);
+	col = 0;
+	cur = es->winleft;
+	while (cur < es->cursor)
+		col = newcol((unsigned char)es->cbuf[cur++], col);
+	if (col >= winwidth)
+		return (1);
+	return (0);
+}
+
+static void
+rewindow(void)
+{
+	int tcur, tcol;
+	int holdcur1, holdcol1;
+	int holdcur2, holdcol2;
+
+	holdcur1 = holdcur2 = tcur = 0;
+	holdcol1 = holdcol2 = tcol = 0;
+	while (tcur < es->cursor) {
+		if (tcol - holdcol2 > winwidth / 2) {
+			holdcur1 = holdcur2;
+			holdcol1 = holdcol2;
+			holdcur2 = tcur;
+			holdcol2 = tcol;
+		}
+		tcol = newcol((unsigned char)es->cbuf[tcur++], tcol);
+	}
+	while (tcol - holdcol1 > winwidth / 2)
+		holdcol1 = newcol((unsigned char)es->cbuf[holdcur1++],
+		    holdcol1);
+	es->winleft = holdcur1;
+}
+
+static int
+newcol(unsigned char ch, int col)
+{
+	if (ch == '\t')
+		return ((col | 7) + 1);
+	return (col + char_len(ch));
+}
+
+static void
+display(char *wb1, char *wb2, int leftside)
+{
+	unsigned char ch;
+	char *twb1, *twb2, mc;
+	int cur, col, cnt;
+	int ncol = 0;
+	int moreright;
+
+	col = 0;
+	cur = es->winleft;
+	moreright = 0;
+	twb1 = wb1;
+	while (col < winwidth && cur < es->linelen) {
+		if (cur == es->cursor && leftside)
+			ncol = col + pwidth;
+		if ((ch = es->cbuf[cur]) == '\t')
+			do {
+				*twb1++ = ' ';
+			} while (++col < winwidth && (col & 7) != 0);
+		else if (col < winwidth) {
+			if (ISCTRL(ch) && /* but not C1 */ ch < 0x80) {
+				*twb1++ = '^';
+				if (++col < winwidth) {
+					*twb1++ = UNCTRL(ch);
+					col++;
+				}
+			} else {
+				*twb1++ = ch;
+				col++;
+			}
+		}
+		if (cur == es->cursor && !leftside)
+			ncol = col + pwidth - 1;
+		cur++;
+	}
+	if (cur == es->cursor)
+		ncol = col + pwidth;
+	if (col < winwidth) {
+		while (col < winwidth) {
+			*twb1++ = ' ';
+			col++;
+		}
+	} else
+		moreright++;
+	*twb1 = ' ';
+
+	col = pwidth;
+	cnt = winwidth;
+	twb1 = wb1;
+	twb2 = wb2;
+	while (cnt--) {
+		if (*twb1 != *twb2) {
+			if (x_col != col)
+				ed_mov_opt(col, wb1);
+			x_putc(*twb1);
+			x_col++;
+		}
+		twb1++;
+		twb2++;
+		col++;
+	}
+	if (es->winleft > 0 && moreright)
+		/*
+		 * POSIX says to use * for this but that is a globbing
+		 * character and may confuse people; + is more innocuous
+		 */
+		mc = '+';
+	else if (es->winleft > 0)
+		mc = '<';
+	else if (moreright)
+		mc = '>';
+	else
+		mc = ' ';
+	if (mc != morec) {
+		ed_mov_opt(pwidth + winwidth + 1, wb1);
+		x_putc(mc);
+		x_col++;
+		morec = mc;
+	}
+	if (x_col != ncol)
+		ed_mov_opt(ncol, wb1);
+}
+
+static void
+ed_mov_opt(int col, char *wb)
+{
+	if (col < x_col) {
+		if (col + 1 < x_col - col) {
+			x_putc('\r');
+			if (prompt_trunc != -1)
+				pprompt(prompt, prompt_trunc);
+			x_col = pwidth;
+			while (x_col++ < col)
+				x_putcf(*wb++);
+		} else {
+			while (x_col-- > col)
+				x_putc('\b');
+		}
+	} else {
+		wb = &wb[x_col - pwidth];
+		while (x_col++ < col)
+			x_putcf(*wb++);
+	}
+	x_col = col;
+}
+
+
+/* replace word with all expansions (ie, expand word*) */
+static int
+expand_word(int cmd)
+{
+	static struct edstate *buf;
+	int rval = 0, nwords, start, end, i;
+	char **words;
+
+	/* Undo previous expansion */
+	if (cmd == 0 && expanded == EXPAND && buf) {
+		restore_edstate(es, buf);
+		buf = 0;
+		expanded = NONE;
+		return (0);
+	}
+	if (buf) {
+		free_edstate(buf);
+		buf = 0;
+	}
+
+	i = XCF_COMMAND_FILE | XCF_FULLPATH;
+	nwords = x_cf_glob(&i, es->cbuf, es->linelen, es->cursor,
+	    &start, &end, &words);
+	if (nwords == 0) {
+		vi_error();
+		return (-1);
+	}
+
+	buf = save_edstate(es);
+	expanded = EXPAND;
+	del_range(start, end);
+	es->cursor = start;
+	i = 0;
+	while (i < nwords) {
+		if (x_escape(words[i], strlen(words[i]), x_vi_putbuf) != 0) {
+			rval = -1;
+			break;
+		}
+		if (++i < nwords && putbuf(" ", 1, false) != 0) {
+			rval = -1;
+			break;
+		}
+	}
+	i = buf->cursor - end;
+	if (rval == 0 && i > 0)
+		es->cursor += i;
+	modified = 1;
+	hnum = hlast;
+	insert = INSERT;
+	lastac = 0;
+	refresh(0);
+	return (rval);
+}
+
+static int
+complete_word(int cmd, int count)
+{
+	static struct edstate *buf;
+	int rval, nwords, start, end, flags;
+	size_t match_len;
+	char **words;
+	char *match;
+	bool is_unique;
+
+	/* Undo previous completion */
+	if (cmd == 0 && expanded == COMPLETE && buf) {
+		print_expansions(buf, 0);
+		expanded = PRINT;
+		return (0);
+	}
+	if (cmd == 0 && expanded == PRINT && buf) {
+		restore_edstate(es, buf);
+		buf = 0;
+		expanded = NONE;
+		return (0);
+	}
+	if (buf) {
+		free_edstate(buf);
+		buf = 0;
+	}
+
+	/*
+	 * XCF_FULLPATH for count 'cause the menu printed by
+	 * print_expansions() was done this way.
+	 */
+	flags = XCF_COMMAND_FILE;
+	if (count)
+		flags |= XCF_FULLPATH;
+	nwords = x_cf_glob(&flags, es->cbuf, es->linelen, es->cursor,
+	    &start, &end, &words);
+	if (nwords == 0) {
+		vi_error();
+		return (-1);
+	}
+	if (count) {
+		int i;
+
+		count--;
+		if (count >= nwords) {
+			vi_error();
+			x_print_expansions(nwords, words,
+			    tobool(flags & XCF_IS_COMMAND));
+			x_free_words(nwords, words);
+			redraw_line(false);
+			return (-1);
+		}
+		/*
+		 * Expand the count'th word to its basename
+		 */
+		if (flags & XCF_IS_COMMAND) {
+			match = words[count] +
+			    x_basename(words[count], NULL);
+			/* If more than one possible match, use full path */
+			for (i = 0; i < nwords; i++)
+				if (i != count &&
+				    strcmp(words[i] + x_basename(words[i],
+				    NULL), match) == 0) {
+					match = words[count];
+					break;
+				}
+		} else
+			match = words[count];
+		match_len = strlen(match);
+		is_unique = true;
+		/* expanded = PRINT;	next call undo */
+	} else {
+		match = words[0];
+		match_len = x_longest_prefix(nwords, words);
+		/* next call will list completions */
+		expanded = COMPLETE;
+		is_unique = nwords == 1;
+	}
+
+	buf = save_edstate(es);
+	del_range(start, end);
+	es->cursor = start;
+
+	/*
+	 * escape all shell-sensitive characters and put the result into
+	 * command buffer
+	 */
+	rval = x_escape(match, match_len, x_vi_putbuf);
+
+	if (rval == 0 && is_unique) {
+		/*
+		 * If exact match, don't undo. Allows directory completions
+		 * to be used (ie, complete the next portion of the path).
+		 */
+		expanded = NONE;
+
+		/*
+		 * append a space if this is a non-directory match
+		 * and not a parameter or homedir substitution
+		 */
+		if (match_len > 0 && match[match_len - 1] != '/' &&
+		    !(flags & XCF_IS_NOSPACE))
+			rval = putbuf(" ", 1, false);
+	}
+	x_free_words(nwords, words);
+
+	modified = 1;
+	hnum = hlast;
+	insert = INSERT;
+	/* prevent this from being redone... */
+	lastac = 0;
+	refresh(0);
+
+	return (rval);
+}
+
+static int
+print_expansions(struct edstate *est, int cmd MKSH_A_UNUSED)
+{
+	int start, end, nwords, i;
+	char **words;
+
+	i = XCF_COMMAND_FILE | XCF_FULLPATH;
+	nwords = x_cf_glob(&i, est->cbuf, est->linelen, est->cursor,
+	    &start, &end, &words);
+	if (nwords == 0) {
+		vi_error();
+		return (-1);
+	}
+	x_print_expansions(nwords, words, tobool(i & XCF_IS_COMMAND));
+	x_free_words(nwords, words);
+	redraw_line(false);
+	return (0);
+}
+
+/* Similar to x_zotc(emacs.c), but no tab weirdness */
+static void
+x_vi_zotc(int c)
+{
+	if (ISCTRL(c)) {
+		x_putc('^');
+		c = UNCTRL(c);
+	}
+	x_putc(c);
+}
+
+static void
+vi_error(void)
+{
+	/* Beem out of any macros as soon as an error occurs */
+	vi_macro_reset();
+	x_putc(7);
+	x_flush();
+}
+
+static void
+vi_macro_reset(void)
+{
+	if (macro.p) {
+		afree(macro.buf, AEDIT);
+		memset((char *)&macro, 0, sizeof(macro));
+	}
+}
+#endif /* !MKSH_S_NOVI */
+
+/* called from main.c */
+void
+x_init(void)
+{
+	int i, j;
+
+	/*
+	 * Set edchars to -2 to force initial binding, except
+	 * we need default values for some deficient systems…
+	 */
+	edchars.erase = edchars.kill = edchars.intr = edchars.quit =
+	    edchars.eof = -2;
+	/* ^W */
+	edchars.werase = 027;
+
+	/* command line editing specific memory allocation */
+	ainit(AEDIT);
+	holdbufp = alloc(LINE, AEDIT);
+
+	/* initialise Emacs command line editing mode */
+	x_nextcmd = -1;
+
+	x_tab = alloc2(X_NTABS, sizeof(*x_tab), AEDIT);
+	for (j = 0; j < X_TABSZ; j++)
+		x_tab[0][j] = XFUNC_insert;
+	for (i = 1; i < X_NTABS; i++)
+		for (j = 0; j < X_TABSZ; j++)
+			x_tab[i][j] = XFUNC_error;
+	for (i = 0; i < (int)NELEM(x_defbindings); i++)
+		x_tab[x_defbindings[i].xdb_tab][x_defbindings[i].xdb_char]
+		    = x_defbindings[i].xdb_func;
+
+#ifndef MKSH_SMALL
+	x_atab = alloc2(X_NTABS, sizeof(*x_atab), AEDIT);
+	for (i = 1; i < X_NTABS; i++)
+		for (j = 0; j < X_TABSZ; j++)
+			x_atab[i][j] = NULL;
+#endif
+}
+
+#ifdef DEBUG_LEAKS
+void
+x_done(void)
+{
+	if (x_tab != NULL)
+		afreeall(AEDIT);
+}
+#endif
+#endif /* !MKSH_NO_CMDLINE_EDITING */

Deleted: vendor/MirOS/mksh/R50/eval.c
===================================================================
--- vendor/MirOS/mksh/dist/eval.c	2014-06-30 23:58:12 UTC (rev 6706)
+++ vendor/MirOS/mksh/R50/eval.c	2014-07-01 12:01:17 UTC (rev 6708)
@@ -1,1853 +0,0 @@
-/*	$OpenBSD: eval.c,v 1.39 2013/07/01 17:25:27 jca Exp $	*/
-
-/*-
- * Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
- *		 2011, 2012, 2013
- *	Thorsten Glaser <tg at mirbsd.org>
- *
- * Provided that these terms and disclaimer and all copyright notices
- * are retained or reproduced in an accompanying document, permission
- * is granted to deal in this work without restriction, including un-
- * limited rights to use, publicly perform, distribute, sell, modify,
- * merge, give away, or sublicence.
- *
- * This work is provided "AS IS" and WITHOUT WARRANTY of any kind, to
- * the utmost extent permitted by applicable law, neither express nor
- * implied; without malicious intent or gross negligence. In no event
- * may a licensor, author or contributor be held liable for indirect,
- * direct, other damage, loss, or other issues arising in any way out
- * of dealing in the work, even if advised of the possibility of such
- * damage or existence of a defect, except proven that it results out
- * of said person's immediate fault when using the work as intended.
- */
-
-#include "sh.h"
-
-__RCSID("$MirOS: src/bin/mksh/eval.c,v 1.142 2013/07/24 18:03:57 tg Exp $");
-
-/*
- * string expansion
- *
- * first pass: quoting, IFS separation, ~, ${}, $() and $(()) substitution.
- * second pass: alternation ({,}), filename expansion (*?[]).
- */
-
-/* expansion generator state */
-typedef struct {
-	/* not including an "int type;" member, see expand() */
-	/* string */
-	const char *str;
-	/* source */
-	union {
-		/* string[] */
-		const char **strv;
-		/* file */
-		struct shf *shf;
-	} u;
-	/* variable in ${var...} */
-	struct tbl *var;
-	/* split "$@" / call waitlast in $() */
-	bool split;
-} Expand;
-
-#define	XBASE		0	/* scanning original */
-#define	XSUB		1	/* expanding ${} string */
-#define	XARGSEP		2	/* ifs0 between "$*" */
-#define	XARG		3	/* expanding $*, $@ */
-#define	XCOM		4	/* expanding $() */
-#define XNULLSUB	5	/* "$@" when $# is 0 (don't generate word) */
-#define XSUBMID		6	/* middle of expanding ${} */
-
-/* States used for field splitting */
-#define IFS_WORD	0	/* word has chars (or quotes) */
-#define IFS_WS		1	/* have seen IFS white-space */
-#define IFS_NWS		2	/* have seen IFS non-white-space */
-
-static int varsub(Expand *, const char *, const char *, int *, int *);
-static int comsub(Expand *, const char *, int);
-static char *valsub(struct op *, Area *);
-static char *trimsub(char *, char *, int);
-static void glob(char *, XPtrV *, bool);
-static void globit(XString *, char **, char *, XPtrV *, int);
-static const char *maybe_expand_tilde(const char *, XString *, char **, int);
-#ifndef MKSH_NOPWNAM
-static char *homedir(char *);
-#endif
-static void alt_expand(XPtrV *, char *, char *, char *, int);
-static int utflen(const char *);
-static void utfincptr(const char *, mksh_ari_t *);
-
-/* UTFMODE functions */
-static int
-utflen(const char *s)
-{
-	size_t n;
-
-	if (UTFMODE) {
-		n = 0;
-		while (*s) {
-			s += utf_ptradj(s);
-			++n;
-		}
-	} else
-		n = strlen(s);
-
-	if (n > 2147483647)
-		n = 2147483647;
-	return ((int)n);
-}
-
-static void
-utfincptr(const char *s, mksh_ari_t *lp)
-{
-	const char *cp = s;
-
-	while ((*lp)--)
-		cp += utf_ptradj(cp);
-	*lp = cp - s;
-}
-
-/* compile and expand word */
-char *
-substitute(const char *cp, int f)
-{
-	struct source *s, *sold;
-
-	sold = source;
-	s = pushs(SWSTR, ATEMP);
-	s->start = s->str = cp;
-	source = s;
-	if (yylex(ONEWORD) != LWORD)
-		internal_errorf("bad substitution");
-	source = sold;
-	afree(s, ATEMP);
-	return (evalstr(yylval.cp, f));
-}
-
-/*
- * expand arg-list
- */
-char **
-eval(const char **ap, int f)
-{
-	XPtrV w;
-
-	if (*ap == NULL) {
-		union mksh_ccphack vap;
-
-		vap.ro = ap;
-		return (vap.rw);
-	}
-	XPinit(w, 32);
-	/* space for shell name */
-	XPput(w, NULL);
-	while (*ap != NULL)
-		expand(*ap++, &w, f);
-	XPput(w, NULL);
-	return ((char **)XPclose(w) + 1);
-}
-
-/*
- * expand string
- */
-char *
-evalstr(const char *cp, int f)
-{
-	XPtrV w;
-	char *dp = null;
-
-	XPinit(w, 1);
-	expand(cp, &w, f);
-	if (XPsize(w))
-		dp = *XPptrv(w);
-	XPfree(w);
-	return (dp);
-}
-
-/*
- * expand string - return only one component
- * used from iosetup to expand redirection files
- */
-char *
-evalonestr(const char *cp, int f)
-{
-	XPtrV w;
-	char *rv;
-
-	XPinit(w, 1);
-	expand(cp, &w, f);
-	switch (XPsize(w)) {
-	case 0:
-		rv = null;
-		break;
-	case 1:
-		rv = (char *) *XPptrv(w);
-		break;
-	default:
-		rv = evalstr(cp, f&~DOGLOB);
-		break;
-	}
-	XPfree(w);
-	return (rv);
-}
-
-/* for nested substitution: ${var:=$var2} */
-typedef struct SubType {
-	struct tbl *var;	/* variable for ${var..} */
-	struct SubType *prev;	/* old type */
-	struct SubType *next;	/* poped type (to avoid re-allocating) */
-	size_t	base;		/* begin position of expanded word */
-	short	stype;		/* [=+-?%#] action after expanded word */
-	short	f;		/* saved value of f (DOPAT, etc) */
-	uint8_t	quotep;		/* saved value of quote (for ${..[%#]..}) */
-	uint8_t	quotew;		/* saved value of quote (for ${..[+-=]..}) */
-} SubType;
-
-void
-expand(
-    /* input word */
-    const char *ccp,
-    /* output words */
-    XPtrV *wp,
-    /* DO* flags */
-    int f)
-{
-	int c = 0;
-	/* expansion type */
-	int type;
-	/* quoted */
-	int quote = 0;
-	/* destination string and live pointer */
-	XString ds;
-	char *dp;
-	/* source */
-	const char *sp;
-	/* second pass flags */
-	int fdo;
-	/* have word */
-	int word;
-	/* field splitting of parameter/command substitution */
-	int doblank;
-	/* expansion variables */
-	Expand x = {
-		NULL, { NULL }, NULL, 0
-	};
-	SubType st_head, *st;
-	/* record number of trailing newlines in COMSUB */
-	int newlines = 0;
-	bool saw_eq, make_magic;
-	int tilde_ok;
-	size_t len;
-	char *cp;
-
-	if (ccp == NULL)
-		internal_errorf("expand(NULL)");
-	/* for alias, readonly, set, typeset commands */
-	if ((f & DOVACHECK) && is_wdvarassign(ccp)) {
-		f &= ~(DOVACHECK|DOBLANK|DOGLOB|DOTILDE);
-		f |= DOASNTILDE;
-	}
-	if (Flag(FNOGLOB))
-		f &= ~DOGLOB;
-	if (Flag(FMARKDIRS))
-		f |= DOMARKDIRS;
-	if (Flag(FBRACEEXPAND) && (f & DOGLOB))
-		f |= DOBRACE;
-
-	/* init destination string */
-	Xinit(ds, dp, 128, ATEMP);
-	type = XBASE;
-	sp = ccp;
-	fdo = 0;
-	saw_eq = false;
-	/* must be 1/0 */
-	tilde_ok = (f & (DOTILDE|DOASNTILDE)) ? 1 : 0;
-	doblank = 0;
-	make_magic = false;
-	word = (f&DOBLANK) ? IFS_WS : IFS_WORD;
-	/* clang doesn't know OSUBST comes before CSUBST */
-	memset(&st_head, 0, sizeof(st_head));
-	st = &st_head;
-
-	while (/* CONSTCOND */ 1) {
-		Xcheck(ds, dp);
-
-		switch (type) {
-		case XBASE:
-			/* original prefixed string */
-			c = *sp++;
-			switch (c) {
-			case EOS:
-				c = 0;
-				break;
-			case CHAR:
-				c = *sp++;
-				break;
-			case QCHAR:
-				/* temporary quote */
-				quote |= 2;
-				c = *sp++;
-				break;
-			case OQUOTE:
-				word = IFS_WORD;
-				tilde_ok = 0;
-				quote = 1;
-				continue;
-			case CQUOTE:
-				quote = st->quotew;
-				continue;
-			case COMSUB:
-			case FUNSUB:
-			case VALSUB:
-				tilde_ok = 0;
-				if (f & DONTRUNCOMMAND) {
-					word = IFS_WORD;
-					*dp++ = '$';
-					*dp++ = c == COMSUB ? '(' : '{';
-					if (c != COMSUB)
-						*dp++ = c == FUNSUB ? ' ' : '|';
-					while (*sp != '\0') {
-						Xcheck(ds, dp);
-						*dp++ = *sp++;
-					}
-					if (c != COMSUB) {
-						*dp++ = ';';
-						*dp++ = '}';
-					} else
-						*dp++ = ')';
-				} else {
-					type = comsub(&x, sp, c);
-					if (type != XBASE && (f & DOBLANK))
-						doblank++;
-					sp = strnul(sp) + 1;
-					newlines = 0;
-				}
-				continue;
-			case EXPRSUB:
-				word = IFS_WORD;
-				tilde_ok = 0;
-				if (f & DONTRUNCOMMAND) {
-					*dp++ = '$'; *dp++ = '('; *dp++ = '(';
-					while (*sp != '\0') {
-						Xcheck(ds, dp);
-						*dp++ = *sp++;
-					}
-					*dp++ = ')'; *dp++ = ')';
-				} else {
-					struct tbl v;
-
-					v.flag = DEFINED|ISSET|INTEGER;
-					/* not default */
-					v.type = 10;
-					v.name[0] = '\0';
-					v_evaluate(&v, substitute(sp, 0),
-					    KSH_UNWIND_ERROR, true);
-					sp = strnul(sp) + 1;
-					cp = str_val(&v);
-					while (*cp) {
-						Xcheck(ds, dp);
-						*dp++ = *cp++;
-					}
-				}
-				continue;
-			case OSUBST: {
-				/* ${{#}var{:}[=+-?#%]word} */
-			/*-
-			 * format is:
-			 *	OSUBST [{x] plain-variable-part \0
-			 *	    compiled-word-part CSUBST [}x]
-			 * This is where all syntax checking gets done...
-			 */
-				/* skip the { or x (}) */
-				const char *varname = ++sp;
-				int stype;
-				int slen = 0;
-
-				/* skip variable */
-				sp = cstrchr(sp, '\0') + 1;
-				type = varsub(&x, varname, sp, &stype, &slen);
-				if (type < 0) {
-					char *beg, *end, *str;
- unwind_substsyn:
-					/* restore sp */
-					sp = varname - 2;
-					end = (beg = wdcopy(sp, ATEMP)) +
-					    (wdscan(sp, CSUBST) - sp);
-					/* ({) the } or x is already skipped */
-					if (end < wdscan(beg, EOS))
-						*end = EOS;
-					str = snptreef(NULL, 64, "%S", beg);
-					afree(beg, ATEMP);
-					errorf("%s: %s", str, "bad substitution");
-				}
-				if (f & DOBLANK)
-					doblank++;
-				tilde_ok = 0;
-				if (type == XBASE) {
-					/* expand? */
-					if (!st->next) {
-						SubType *newst;
-
-						newst = alloc(sizeof(SubType), ATEMP);
-						newst->next = NULL;
-						newst->prev = st;
-						st->next = newst;
-					}
-					st = st->next;
-					st->stype = stype;
-					st->base = Xsavepos(ds, dp);
-					st->f = f;
-					if (x.var == &vtemp) {
-						st->var = tempvar();
-						st->var->flag &= ~INTEGER;
-						/* can't fail here */
-						setstr(st->var,
-						    str_val(x.var),
-						    KSH_RETURN_ERROR | 0x4);
-					} else
-						st->var = x.var;
-
-					st->quotew = st->quotep = quote;
-					/* skip qualifier(s) */
-					if (stype)
-						sp += slen;
-					switch (stype & 0x17F) {
-					case 0x100 | '#': {
-						char *beg, *end;
-						mksh_ari_t seed;
-						register uint32_t h;
-
-						beg = wdcopy(sp, ATEMP);
-						end = beg + (wdscan(sp, CSUBST) - sp);
-						end[-2] = EOS;
-						end = wdstrip(beg, 0);
-						afree(beg, ATEMP);
-						evaluate(substitute(end, 0),
-						    &seed, KSH_UNWIND_ERROR, true);
-						/* hash with seed, for now */
-						h = seed;
-						NZATUpdateString(h,
-						    str_val(st->var));
-						NZAATFinish(h);
-						x.str = shf_smprintf("%08X",
-						    (unsigned int)h);
-						break;
-					}
-					case 0x100 | 'Q': {
-						struct shf shf;
-
-						shf_sopen(NULL, 0, SHF_WR|SHF_DYNAMIC, &shf);
-						print_value_quoted(&shf, str_val(st->var));
-						x.str = shf_sclose(&shf);
-						break;
-					}
-					case '0': {
-						char *beg, *mid, *end, *stg;
-						mksh_ari_t from = 0, num = -1, flen, finc = 0;
-
-						beg = wdcopy(sp, ATEMP);
-						mid = beg + (wdscan(sp, ADELIM) - sp);
-						stg = beg + (wdscan(sp, CSUBST) - sp);
-						if (mid >= stg)
-							goto unwind_substsyn;
-						mid[-2] = EOS;
-						if (mid[-1] == /*{*/'}') {
-							sp += mid - beg - 1;
-							end = NULL;
-						} else {
-							end = mid +
-							    (wdscan(mid, ADELIM) - mid);
-							if (end >= stg ||
-							    /* more than max delimiters */
-							    end[-1] != /*{*/ '}')
-								goto unwind_substsyn;
-							end[-2] = EOS;
-							sp += end - beg - 1;
-						}
-						evaluate(substitute(stg = wdstrip(beg, 0), 0),
-						    &from, KSH_UNWIND_ERROR, true);
-						afree(stg, ATEMP);
-						if (end) {
-							evaluate(substitute(stg = wdstrip(mid, 0), 0),
-							    &num, KSH_UNWIND_ERROR, true);
-							afree(stg, ATEMP);
-						}
-						afree(beg, ATEMP);
-						beg = str_val(st->var);
-						flen = utflen(beg);
-						if (from < 0) {
-							if (-from < flen)
-								finc = flen + from;
-						} else
-							finc = from < flen ? from : flen;
-						if (UTFMODE)
-							utfincptr(beg, &finc);
-						beg += finc;
-						flen = utflen(beg);
-						if (num < 0 || num > flen)
-							num = flen;
-						if (UTFMODE)
-							utfincptr(beg, &num);
-						strndupx(x.str, beg, num, ATEMP);
-						goto do_CSUBST;
-					}
-					case '/': {
-						char *s, *p, *d, *sbeg, *end;
-						char *pat, *rrep;
-						char *tpat0, *tpat1, *tpat2;
-
-						s = wdcopy(sp, ATEMP);
-						p = s + (wdscan(sp, ADELIM) - sp);
-						d = s + (wdscan(sp, CSUBST) - sp);
-						if (p >= d)
-							goto unwind_substsyn;
-						p[-2] = EOS;
-						if (p[-1] == /*{*/'}')
-							d = NULL;
-						else
-							d[-2] = EOS;
-						sp += (d ? d : p) - s - 1;
-						tpat0 = wdstrip(s,
-						    WDS_KEEPQ | WDS_MAGIC);
-						pat = substitute(tpat0, 0);
-						if (d) {
-							d = wdstrip(p, WDS_KEEPQ);
-							rrep = substitute(d, 0);
-							afree(d, ATEMP);
-						} else
-							rrep = null;
-						afree(s, ATEMP);
-						s = d = pat;
-						while (*s)
-							if (*s != '\\' ||
-							    s[1] == '%' ||
-							    s[1] == '#' ||
-							    s[1] == '\0' ||
-				/* XXX really? */	    s[1] == '\\' ||
-							    s[1] == '/')
-								*d++ = *s++;
-							else
-								s++;
-						*d = '\0';
-						afree(tpat0, ATEMP);
-
-						/* check for special cases */
-						d = str_val(st->var);
-						mkssert(d != NULL);
-						switch (*pat) {
-						case '#':
-							/* anchor at begin */
-							tpat0 = pat + 1;
-							tpat1 = rrep;
-							tpat2 = d;
-							break;
-						case '%':
-							/* anchor at end */
-							tpat0 = pat + 1;
-							tpat1 = d;
-							tpat2 = rrep;
-							break;
-						case '\0':
-							/* empty pattern */
-							goto no_repl;
-						default:
-							tpat0 = pat;
-							/* silence gcc */
-							tpat1 = tpat2 = NULL;
-						}
-						if (gmatchx(null, tpat0, false)) {
-							/*
-							 * pattern matches
-							 * the empty string
-							 */
-							if (tpat0 == pat)
-								goto no_repl;
-							/* but is anchored */
-							s = shf_smprintf("%s%s",
-							    tpat1, tpat2);
-							goto do_repl;
-						}
-
-						/* prepare string on which to work */
-						strdupx(s, d, ATEMP);
-						sbeg = s;
-
-						/* first see if we have any match at all */
-						tpat0 = pat;
-						if (*pat == '#') {
-							/* anchor at the beginning */
-							tpat1 = shf_smprintf("%s%c*", ++tpat0, MAGIC);
-							tpat2 = tpat1;
-						} else if (*pat == '%') {
-							/* anchor at the end */
-							tpat1 = shf_smprintf("%c*%s", MAGIC, ++tpat0);
-							tpat2 = tpat0;
-						} else {
-							/* float */
-							tpat1 = shf_smprintf("%c*%s%c*", MAGIC, pat, MAGIC);
-							tpat2 = tpat1 + 2;
-						}
- again_repl:
-						/*
-						 * this would not be necessary if gmatchx would return
-						 * the start and end values of a match found, like re*
-						 */
-						if (!gmatchx(sbeg, tpat1, false))
-							goto end_repl;
-						end = strnul(s);
-						/* now anchor the beginning of the match */
-						if (*pat != '#')
-							while (sbeg <= end) {
-								if (gmatchx(sbeg, tpat2, false))
-									break;
-								else
-									sbeg++;
-							}
-						/* now anchor the end of the match */
-						p = end;
-						if (*pat != '%')
-							while (p >= sbeg) {
-								bool gotmatch;
-
-								c = *p;
-								*p = '\0';
-								gotmatch = tobool(gmatchx(sbeg, tpat0, false));
-								*p = c;
-								if (gotmatch)
-									break;
-								p--;
-							}
-						strndupx(end, s, sbeg - s, ATEMP);
-						d = shf_smprintf("%s%s%s", end, rrep, p);
-						afree(end, ATEMP);
-						sbeg = d + (sbeg - s) + strlen(rrep);
-						afree(s, ATEMP);
-						s = d;
-						if (stype & 0x80)
-							goto again_repl;
- end_repl:
-						afree(tpat1, ATEMP);
- do_repl:
-						x.str = s;
- no_repl:
-						afree(pat, ATEMP);
-						if (rrep != null)
-							afree(rrep, ATEMP);
-						goto do_CSUBST;
-					}
-					case '#':
-					case '%':
-						/* ! DOBLANK,DOBRACE,DOTILDE */
-						f = (f & DONTRUNCOMMAND) |
-						    DOPAT | DOTEMP;
-						st->quotew = quote = 0;
-						/*
-						 * Prepend open pattern (so |
-						 * in a trim will work as
-						 * expected)
-						 */
-						if (!Flag(FSH)) {
-							*dp++ = MAGIC;
-							*dp++ = '@' | 0x80;
-						}
-						break;
-					case '=':
-						/*
-						 * Enabling tilde expansion
-						 * after :s here is
-						 * non-standard ksh, but is
-						 * consistent with rules for
-						 * other assignments. Not
-						 * sure what POSIX thinks of
-						 * this.
-						 * Not doing tilde expansion
-						 * for integer variables is a
-						 * non-POSIX thing - makes
-						 * sense though, since ~ is
-						 * a arithmetic operator.
-						 */
-						if (!(x.var->flag & INTEGER))
-							f |= DOASNTILDE|DOTILDE;
-						f |= DOTEMP;
-						/*
-						 * These will be done after the
-						 * value has been assigned.
-						 */
-						f &= ~(DOBLANK|DOGLOB|DOBRACE);
-						tilde_ok = 1;
-						break;
-					case '?':
-						f &= ~DOBLANK;
-						f |= DOTEMP;
-						/* FALLTHROUGH */
-					default:
-						/* Enable tilde expansion */
-						tilde_ok = 1;
-						f |= DOTILDE;
-					}
-				} else
-					/* skip word */
-					sp += wdscan(sp, CSUBST) - sp;
-				continue;
-			}
-			case CSUBST:
-				/* only get here if expanding word */
- do_CSUBST:
-				/* ({) skip the } or x */
-				sp++;
-				/* in case of ${unset:-} */
-				tilde_ok = 0;
-				*dp = '\0';
-				quote = st->quotep;
-				f = st->f;
-				if (f&DOBLANK)
-					doblank--;
-				switch (st->stype & 0x17F) {
-				case '#':
-				case '%':
-					if (!Flag(FSH)) {
-						/* Append end-pattern */
-						*dp++ = MAGIC;
-						*dp++ = ')';
-					}
-					*dp = '\0';
-					dp = Xrestpos(ds, dp, st->base);
-					/*
-					 * Must use st->var since calling
-					 * global would break things
-					 * like x[i+=1].
-					 */
-					x.str = trimsub(str_val(st->var),
-						dp, st->stype);
-					if (x.str[0] != '\0' || st->quotep)
-						type = XSUB;
-					else
-						type = XNULLSUB;
-					if (f&DOBLANK)
-						doblank++;
-					st = st->prev;
-					continue;
-				case '=':
-					/*
-					 * Restore our position and substitute
-					 * the value of st->var (may not be
-					 * the assigned value in the presence
-					 * of integer/right-adj/etc attributes).
-					 */
-					dp = Xrestpos(ds, dp, st->base);
-					/*
-					 * Must use st->var since calling
-					 * global would cause with things
-					 * like x[i+=1] to be evaluated twice.
-					 */
-					/*
-					 * Note: not exported by FEXPORT
-					 * in AT&T ksh.
-					 */
-					/*
-					 * XXX POSIX says readonly is only
-					 * fatal for special builtins (setstr
-					 * does readonly check).
-					 */
-					len = strlen(dp) + 1;
-					setstr(st->var,
-					    debunk(alloc(len, ATEMP),
-					    dp, len), KSH_UNWIND_ERROR);
-					x.str = str_val(st->var);
-					type = XSUB;
-					if (f&DOBLANK)
-						doblank++;
-					st = st->prev;
-					continue;
-				case '?': {
-					char *s = Xrestpos(ds, dp, st->base);
-
-					errorf("%s: %s", st->var->name,
-					    dp == s ?
-					    "parameter null or not set" :
-					    (debunk(s, s, strlen(s) + 1), s));
-				}
-				case '0':
-				case '/':
-				case 0x100 | '#':
-				case 0x100 | 'Q':
-					dp = Xrestpos(ds, dp, st->base);
-					type = XSUB;
-					if (f&DOBLANK)
-						doblank++;
-					st = st->prev;
-					continue;
-				}
-				st = st->prev;
-				type = XBASE;
-				continue;
-
-			case OPAT:
-				/* open pattern: *(foo|bar) */
-				/* Next char is the type of pattern */
-				make_magic = true;
-				c = *sp++ | 0x80;
-				break;
-
-			case SPAT:
-				/* pattern separator (|) */
-				make_magic = true;
-				c = '|';
-				break;
-
-			case CPAT:
-				/* close pattern */
-				make_magic = true;
-				c = /*(*/ ')';
-				break;
-			}
-			break;
-
-		case XNULLSUB:
-			/*
-			 * Special case for "$@" (and "${foo[@]}") - no
-			 * word is generated if $# is 0 (unless there is
-			 * other stuff inside the quotes).
-			 */
-			type = XBASE;
-			if (f&DOBLANK) {
-				doblank--;
-				/*
-				 * not really correct: x=; "$x$@" should
-				 * generate a null argument and
-				 * set A; "${@:+}" shouldn't.
-				 */
-				if (dp == Xstring(ds, dp))
-					word = IFS_WS;
-			}
-			continue;
-
-		case XSUB:
-		case XSUBMID:
-			if ((c = *x.str++) == 0) {
-				type = XBASE;
-				if (f&DOBLANK)
-					doblank--;
-				continue;
-			}
-			break;
-
-		case XARGSEP:
-			type = XARG;
-			quote = 1;
-			/* FALLTHROUGH */
-		case XARG:
-			if ((c = *x.str++) == '\0') {
-				/*
-				 * force null words to be created so
-				 * set -- '' 2 ''; foo "$@" will do
-				 * the right thing
-				 */
-				if (quote && x.split)
-					word = IFS_WORD;
-				if ((x.str = *x.u.strv++) == NULL) {
-					type = XBASE;
-					if (f&DOBLANK)
-						doblank--;
-					continue;
-				}
-				c = ifs0;
-				if (c == 0) {
-					if (quote && !x.split)
-						continue;
-					/* this is so we don't terminate */
-					c = ' ';
-					/* now force-emit a word */
-					goto emit_word;
-				}
-				if (quote && x.split) {
-					/* terminate word for "$@" */
-					type = XARGSEP;
-					quote = 0;
-				}
-			}
-			break;
-
-		case XCOM:
-			if (x.u.shf == NULL) {
-				/* $(<...) failed */
-				subst_exstat = 1;
-				/* fake EOF */
-				c = EOF;
-			} else if (newlines) {
-				/* spit out saved NLs */
-				c = '\n';
-				--newlines;
-			} else {
-				while ((c = shf_getc(x.u.shf)) == 0 || c == '\n')
-					if (c == '\n')
-						/* save newlines */
-						newlines++;
-				if (newlines && c != EOF) {
-					shf_ungetc(c, x.u.shf);
-					c = '\n';
-					--newlines;
-				}
-			}
-			if (c == EOF) {
-				newlines = 0;
-				if (x.u.shf)
-					shf_close(x.u.shf);
-				if (x.split)
-					subst_exstat = waitlast();
-				type = XBASE;
-				if (f&DOBLANK)
-					doblank--;
-				continue;
-			}
-			break;
-		}
-
-		/* check for end of word or IFS separation */
-		if (c == 0 || (!quote && (f & DOBLANK) && doblank &&
-		    !make_magic && ctype(c, C_IFS))) {
-			/*-
-			 * How words are broken up:
-			 *			|	value of c
-			 *	word		|	ws	nws	0
-			 *	-----------------------------------
-			 *	IFS_WORD		w/WS	w/NWS	w
-			 *	IFS_WS			-/WS	w/NWS	-
-			 *	IFS_NWS			-/NWS	w/NWS	w
-			 * (w means generate a word)
-			 * Note that IFS_NWS/0 generates a word (AT&T ksh
-			 * doesn't do this, but POSIX does).
-			 */
-			if (word == IFS_WORD ||
-			    (!ctype(c, C_IFSWS) && c && word == IFS_NWS)) {
- emit_word:
-				*dp++ = '\0';
-				cp = Xclose(ds, dp);
-				if (fdo & DOBRACE)
-					/* also does globbing */
-					alt_expand(wp, cp, cp,
-					    cp + Xlength(ds, (dp - 1)),
-					    fdo | (f & DOMARKDIRS));
-				else if (fdo & DOGLOB)
-					glob(cp, wp, tobool(f & DOMARKDIRS));
-				else if ((f & DOPAT) || !(fdo & DOMAGIC))
-					XPput(*wp, cp);
-				else
-					XPput(*wp, debunk(cp, cp,
-					    strlen(cp) + 1));
-				fdo = 0;
-				saw_eq = false;
-				tilde_ok = (f & (DOTILDE|DOASNTILDE)) ? 1 : 0;
-				if (c == 0)
-					return;
-				Xinit(ds, dp, 128, ATEMP);
-			} else if (c == 0) {
-				return;
-			} else if (type == XSUB && ctype(c, C_IFS) &&
-			    !ctype(c, C_IFSWS) && Xlength(ds, dp) == 0) {
-				*(cp = alloc(1, ATEMP)) = '\0';
-				XPput(*wp, cp);
-				type = XSUBMID;
-			}
-			if (word != IFS_NWS)
-				word = ctype(c, C_IFSWS) ? IFS_WS : IFS_NWS;
-		} else {
-			if (type == XSUB) {
-				if (word == IFS_NWS &&
-				    Xlength(ds, dp) == 0) {
-					*(cp = alloc(1, ATEMP)) = '\0';
-					XPput(*wp, cp);
-				}
-				type = XSUBMID;
-			}
-
-			/* age tilde_ok info - ~ code tests second bit */
-			tilde_ok <<= 1;
-			/* mark any special second pass chars */
-			if (!quote)
-				switch (c) {
-				case '[':
-				case '!':
-				case '-':
-				case ']':
-					/*
-					 * For character classes - doesn't hurt
-					 * to have magic !,-,]s outside of
-					 * [...] expressions.
-					 */
-					if (f & (DOPAT | DOGLOB)) {
-						fdo |= DOMAGIC;
-						if (c == '[')
-							fdo |= f & DOGLOB;
-						*dp++ = MAGIC;
-					}
-					break;
-				case '*':
-				case '?':
-					if (f & (DOPAT | DOGLOB)) {
-						fdo |= DOMAGIC | (f & DOGLOB);
-						*dp++ = MAGIC;
-					}
-					break;
-				case '{':
-				case '}':
-				case ',':
-					if ((f & DOBRACE) && (c == '{' /*}*/ ||
-					    (fdo & DOBRACE))) {
-						fdo |= DOBRACE|DOMAGIC;
-						*dp++ = MAGIC;
-					}
-					break;
-				case '=':
-					/* Note first unquoted = for ~ */
-					if (!(f & DOTEMP) && !saw_eq &&
-					    (Flag(FBRACEEXPAND) ||
-					    (f & DOASNTILDE))) {
-						saw_eq = true;
-						tilde_ok = 1;
-					}
-					break;
-				case ':':
-					/* : */
-					/* Note unquoted : for ~ */
-					if (!(f & DOTEMP) && (f & DOASNTILDE))
-						tilde_ok = 1;
-					break;
-				case '~':
-					/*
-					 * tilde_ok is reset whenever
-					 * any of ' " $( $(( ${ } are seen.
-					 * Note that tilde_ok must be preserved
-					 * through the sequence ${A=a=}~
-					 */
-					if (type == XBASE &&
-					    (f & (DOTILDE|DOASNTILDE)) &&
-					    (tilde_ok & 2)) {
-						const char *tcp;
-						char *tdp = dp;
-
-						tcp = maybe_expand_tilde(sp,
-						    &ds, &tdp,
-						    f & DOASNTILDE);
-						if (tcp) {
-							if (dp != tdp)
-								word = IFS_WORD;
-							dp = tdp;
-							sp = tcp;
-							continue;
-						}
-					}
-					break;
-				}
-			else
-				/* undo temporary */
-				quote &= ~2;
-
-			if (make_magic) {
-				make_magic = false;
-				fdo |= DOMAGIC | (f & DOGLOB);
-				*dp++ = MAGIC;
-			} else if (ISMAGIC(c)) {
-				fdo |= DOMAGIC;
-				*dp++ = MAGIC;
-			}
-			/* save output char */
-			*dp++ = c;
-			word = IFS_WORD;
-		}
-	}
-}
-
-/*
- * Prepare to generate the string returned by ${} substitution.
- */
-static int
-varsub(Expand *xp, const char *sp, const char *word,
-    int *stypep,	/* becomes qualifier type */
-    int *slenp)		/* " " len (=, :=, etc.) valid iff *stypep != 0 */
-{
-	int c;
-	int state;	/* next state: XBASE, XARG, XSUB, XNULLSUB */
-	int stype;	/* substitution type */
-	int slen;
-	const char *p;
-	struct tbl *vp;
-	bool zero_ok = false;
-
-	if ((stype = sp[0]) == '\0')
-		/* Bad variable name */
-		return (-1);
-
-	xp->var = NULL;
-
-	/*-
-	 * ${#var}, string length (-U: characters, +U: octets) or array size
-	 * ${%var}, string width (-U: screen columns, +U: octets)
-	 */
-	c = sp[1];
-	if (stype == '%' && c == '\0')
-		return (-1);
-	if ((stype == '#' || stype == '%') && c != '\0') {
-		/* Can't have any modifiers for ${#...} or ${%...} */
-		if (*word != CSUBST)
-			return (-1);
-		sp++;
-		/* Check for size of array */
-		if ((p = cstrchr(sp, '[')) && (p[1] == '*' || p[1] == '@') &&
-		    p[2] == ']') {
-			int n = 0;
-
-			if (stype != '#')
-				return (-1);
-			vp = global(arrayname(sp));
-			if (vp->flag & (ISSET|ARRAY))
-				zero_ok = true;
-			for (; vp; vp = vp->u.array)
-				if (vp->flag & ISSET)
-					n++;
-			c = n;
-		} else if (c == '*' || c == '@') {
-			if (stype != '#')
-				return (-1);
-			c = e->loc->argc;
-		} else {
-			p = str_val(global(sp));
-			zero_ok = p != null;
-			if (stype == '#')
-				c = utflen(p);
-			else {
-				/* partial utf_mbswidth reimplementation */
-				const char *s = p;
-				unsigned int wc;
-				size_t len;
-				int cw;
-
-				c = 0;
-				while (*s) {
-					if (!UTFMODE || (len = utf_mbtowc(&wc,
-					    s)) == (size_t)-1)
-						/* not UTFMODE or not UTF-8 */
-						wc = (unsigned char)(*s++);
-					else
-						/* UTFMODE and UTF-8 */
-						s += len;
-					/* wc == char or wchar at s++ */
-					if ((cw = utf_wcwidth(wc)) == -1) {
-						/* 646, 8859-1, 10646 C0/C1 */
-						c = -1;
-						break;
-					}
-					c += cw;
-				}
-			}
-		}
-		if (Flag(FNOUNSET) && c == 0 && !zero_ok)
-			errorf("%s: %s", sp, "parameter not set");
-		/* unqualified variable/string substitution */
-		*stypep = 0;
-		xp->str = shf_smprintf("%d", c);
-		return (XSUB);
-	}
-
-	/* Check for qualifiers in word part */
-	stype = 0;
-	c = word[slen = 0] == CHAR ? word[1] : 0;
-	if (c == ':') {
-		slen += 2;
-		stype = 0x80;
-		c = word[slen + 0] == CHAR ? word[slen + 1] : 0;
-	}
-	if (!stype && c == '/') {
-		slen += 2;
-		stype = c;
-		if (word[slen] == ADELIM) {
-			slen += 2;
-			stype |= 0x80;
-		}
-	} else if (stype == 0x80 && (c == ' ' || c == '0')) {
-		stype |= '0';
-	} else if (ctype(c, C_SUBOP1)) {
-		slen += 2;
-		stype |= c;
-	} else if (ctype(c, C_SUBOP2)) {
-		/* Note: ksh88 allows :%, :%%, etc */
-		slen += 2;
-		stype = c;
-		if (word[slen + 0] == CHAR && c == word[slen + 1]) {
-			stype |= 0x80;
-			slen += 2;
-		}
-	} else if (c == '@') {
-		/* @x where x is command char */
-		slen += 2;
-		stype |= 0x100;
-		if (word[slen] == CHAR) {
-			stype |= word[slen + 1];
-			slen += 2;
-		}
-	} else if (stype)
-		/* : is not ok */
-		return (-1);
-	if (!stype && *word != CSUBST)
-		return (-1);
-	*stypep = stype;
-	*slenp = slen;
-
-	c = sp[0];
-	if (c == '*' || c == '@') {
-		switch (stype & 0x17F) {
-		/* can't assign to a vector */
-		case '=':
-		/* can't trim a vector (yet) */
-		case '%':
-		case '#':
-		case '0':
-		case '/':
-		case 0x100 | '#':
-		case 0x100 | 'Q':
-			return (-1);
-		}
-		if (e->loc->argc == 0) {
-			xp->str = null;
-			xp->var = global(sp);
-			state = c == '@' ? XNULLSUB : XSUB;
-		} else {
-			xp->u.strv = (const char **)e->loc->argv + 1;
-			xp->str = *xp->u.strv++;
-			/* $@ */
-			xp->split = tobool(c == '@');
-			state = XARG;
-		}
-		/* POSIX 2009? */
-		zero_ok = true;
-	} else {
-		if ((p = cstrchr(sp, '[')) && (p[1] == '*' || p[1] == '@') &&
-		    p[2] == ']') {
-			XPtrV wv;
-
-			switch (stype & 0x17F) {
-			/* can't assign to a vector */
-			case '=':
-			/* can't trim a vector (yet) */
-			case '%':
-			case '#':
-			case '?':
-			case '0':
-			case '/':
-			case 0x100 | '#':
-			case 0x100 | 'Q':
-				return (-1);
-			}
-			XPinit(wv, 32);
-			if ((c = sp[0]) == '!')
-				++sp;
-			vp = global(arrayname(sp));
-			for (; vp; vp = vp->u.array) {
-				if (!(vp->flag&ISSET))
-					continue;
-				XPput(wv, c == '!' ? shf_smprintf("%lu",
-				    arrayindex(vp)) :
-				    str_val(vp));
-			}
-			if (XPsize(wv) == 0) {
-				xp->str = null;
-				state = p[1] == '@' ? XNULLSUB : XSUB;
-				XPfree(wv);
-			} else {
-				XPput(wv, 0);
-				xp->u.strv = (const char **)XPptrv(wv);
-				xp->str = *xp->u.strv++;
-				/* ${foo[@]} */
-				xp->split = tobool(p[1] == '@');
-				state = XARG;
-			}
-		} else {
-			/* Can't assign things like $! or $1 */
-			if ((stype & 0x17F) == '=' &&
-			    ctype(*sp, C_VAR1 | C_DIGIT))
-				return (-1);
-			if (*sp == '!' && sp[1]) {
-				++sp;
-				xp->var = global(sp);
-				if (vstrchr(sp, '[')) {
-					if (xp->var->flag & ISSET)
-						xp->str = shf_smprintf("%lu",
-						    arrayindex(xp->var));
-					else
-						xp->str = null;
-				} else if (xp->var->flag & ISSET)
-					xp->str = xp->var->name;
-				else
-					/* ksh93 compat */
-					xp->str = "0";
-			} else {
-				xp->var = global(sp);
-				xp->str = str_val(xp->var);
-			}
-			state = XSUB;
-		}
-	}
-
-	c = stype & 0x7F;
-	/* test the compiler's code generator */
-	if (((stype < 0x100) && (ctype(c, C_SUBOP2) || c == '/' ||
-	    (((stype&0x80) ? *xp->str=='\0' : xp->str==null) ? /* undef? */
-	    c == '=' || c == '-' || c == '?' : c == '+'))) ||
-	    stype == (0x80 | '0') || stype == (0x100 | '#') ||
-	    stype == (0x100 | 'Q'))
-		/* expand word instead of variable value */
-		state = XBASE;
-	if (Flag(FNOUNSET) && xp->str == null && !zero_ok &&
-	    (ctype(c, C_SUBOP2) || (state != XBASE && c != '+')))
-		errorf("%s: %s", sp, "parameter not set");
-	return (state);
-}
-
-/*
- * Run the command in $(...) and read its output.
- */
-static int
-comsub(Expand *xp, const char *cp, int fn MKSH_A_UNUSED)
-{
-	Source *s, *sold;
-	struct op *t;
-	struct shf *shf;
-	uint8_t old_utfmode = UTFMODE;
-
-	s = pushs(SSTRING, ATEMP);
-	s->start = s->str = cp;
-	sold = source;
-	t = compile(s, true);
-	afree(s, ATEMP);
-	source = sold;
-
-	UTFMODE = old_utfmode;
-
-	if (t == NULL)
-		return (XBASE);
-
-	/* no waitlast() unless specifically enabled later */
-	xp->split = false;
-
-	if (t->type == TCOM &&
-	    *t->args == NULL && *t->vars == NULL && t->ioact != NULL) {
-		/* $(<file) */
-		struct ioword *io = *t->ioact;
-		char *name;
-
-		if ((io->flag & IOTYPE) != IOREAD)
-			errorf("%s: %s", "funny $() command",
-			    snptreef(NULL, 32, "%R", io));
-		shf = shf_open(name = evalstr(io->name, DOTILDE), O_RDONLY, 0,
-			SHF_MAPHI|SHF_CLEXEC);
-		if (shf == NULL)
-			warningf(!Flag(FTALKING), "%s: %s %s: %s", name,
-			    "can't open", "$(<...) input", cstrerror(errno));
-	} else if (fn == FUNSUB) {
-		int ofd1;
-		struct temp *tf = NULL;
-
-		/*
-		 * create a temporary file, open for reading and writing,
-		 * with an shf open for reading (buffered) but yet unused
-		 */
-		maketemp(ATEMP, TT_FUNSUB, &tf);
-		if (!tf->shf) {
-			errorf("can't %s temporary file %s: %s",
-			    "create", tf->tffn, cstrerror(errno));
-		}
-		/* extract shf from temporary file, unlink and free it */
-		shf = tf->shf;
-		unlink(tf->tffn);
-		afree(tf, ATEMP);
-		/* save stdout and let it point to the tempfile */
-		ofd1 = savefd(1);
-		ksh_dup2(shf_fileno(shf), 1, false);
-		/*
-		 * run tree, with output thrown into the tempfile,
-		 * in a new function block
-		 */
-		valsub(t, NULL);
-		subst_exstat = exstat & 0xFF;
-		/* rewind the tempfile and restore regular stdout */
-		lseek(shf_fileno(shf), (off_t)0, SEEK_SET);
-		restfd(1, ofd1);
-	} else if (fn == VALSUB) {
-		xp->str = valsub(t, ATEMP);
-		subst_exstat = exstat & 0xFF;
-		return (XSUB);
-	} else {
-		int ofd1, pv[2];
-
-		openpipe(pv);
-		shf = shf_fdopen(pv[0], SHF_RD, NULL);
-		ofd1 = savefd(1);
-		if (pv[1] != 1) {
-			ksh_dup2(pv[1], 1, false);
-			close(pv[1]);
-		}
-		execute(t, XXCOM | XPIPEO | XFORK, NULL);
-		restfd(1, ofd1);
-		startlast();
-		/* waitlast() */
-		xp->split = true;
-	}
-
-	xp->u.shf = shf;
-	return (XCOM);
-}
-
-/*
- * perform #pattern and %pattern substitution in ${}
- */
-static char *
-trimsub(char *str, char *pat, int how)
-{
-	char *end = strnul(str);
-	char *p, c;
-
-	switch (how & 0xFF) {
-	case '#':
-		/* shortest match at beginning */
-		for (p = str; p <= end; p += utf_ptradj(p)) {
-			c = *p; *p = '\0';
-			if (gmatchx(str, pat, false)) {
-				*p = c;
-				return (p);
-			}
-			*p = c;
-		}
-		break;
-	case '#'|0x80:
-		/* longest match at beginning */
-		for (p = end; p >= str; p--) {
-			c = *p; *p = '\0';
-			if (gmatchx(str, pat, false)) {
-				*p = c;
-				return (p);
-			}
-			*p = c;
-		}
-		break;
-	case '%':
-		/* shortest match at end */
-		p = end;
-		while (p >= str) {
-			if (gmatchx(p, pat, false))
-				goto trimsub_match;
-			if (UTFMODE) {
-				char *op = p;
-				while ((p-- > str) && ((*p & 0xC0) == 0x80))
-					;
-				if ((p < str) || (p + utf_ptradj(p) != op))
-					p = op - 1;
-			} else
-				--p;
-		}
-		break;
-	case '%'|0x80:
-		/* longest match at end */
-		for (p = str; p <= end; p++)
-			if (gmatchx(p, pat, false)) {
- trimsub_match:
-				strndupx(end, str, p - str, ATEMP);
-				return (end);
-			}
-		break;
-	}
-
-	/* no match, return string */
-	return (str);
-}
-
-/*
- * glob
- * Name derived from V6's /etc/glob, the program that expanded filenames.
- */
-
-/* XXX cp not const 'cause slashes are temporarily replaced with NULs... */
-static void
-glob(char *cp, XPtrV *wp, bool markdirs)
-{
-	int oldsize = XPsize(*wp);
-
-	if (glob_str(cp, wp, markdirs) == 0)
-		XPput(*wp, debunk(cp, cp, strlen(cp) + 1));
-	else
-		qsort(XPptrv(*wp) + oldsize, XPsize(*wp) - oldsize,
-		    sizeof(void *), xstrcmp);
-}
-
-#define GF_NONE		0
-#define GF_EXCHECK	BIT(0)		/* do existence check on file */
-#define GF_GLOBBED	BIT(1)		/* some globbing has been done */
-#define GF_MARKDIR	BIT(2)		/* add trailing / to directories */
-
-/*
- * Apply file globbing to cp and store the matching files in wp. Returns
- * the number of matches found.
- */
-int
-glob_str(char *cp, XPtrV *wp, bool markdirs)
-{
-	int oldsize = XPsize(*wp);
-	XString xs;
-	char *xp;
-
-	Xinit(xs, xp, 256, ATEMP);
-	globit(&xs, &xp, cp, wp, markdirs ? GF_MARKDIR : GF_NONE);
-	Xfree(xs, xp);
-
-	return (XPsize(*wp) - oldsize);
-}
-
-static void
-globit(XString *xs,	/* dest string */
-    char **xpp,		/* ptr to dest end */
-    char *sp,		/* source path */
-    XPtrV *wp,		/* output list */
-    int check)		/* GF_* flags */
-{
-	char *np;		/* next source component */
-	char *xp = *xpp;
-	char *se;
-	char odirsep;
-
-	/* This to allow long expansions to be interrupted */
-	intrcheck();
-
-	if (sp == NULL) {
-		/* end of source path */
-		/*
-		 * We only need to check if the file exists if a pattern
-		 * is followed by a non-pattern (eg, foo*x/bar; no check
-		 * is needed for foo* since the match must exist) or if
-		 * any patterns were expanded and the markdirs option is set.
-		 * Symlinks make things a bit tricky...
-		 */
-		if ((check & GF_EXCHECK) ||
-		    ((check & GF_MARKDIR) && (check & GF_GLOBBED))) {
-#define stat_check()	(stat_done ? stat_done : (stat_done = \
-			    stat(Xstring(*xs, xp), &statb) < 0 ? -1 : 1))
-			struct stat lstatb, statb;
-			/* -1: failed, 1 ok, 0 not yet done */
-			int stat_done = 0;
-
-			if (mksh_lstat(Xstring(*xs, xp), &lstatb) < 0)
-				return;
-			/*
-			 * special case for systems which strip trailing
-			 * slashes from regular files (eg, /etc/passwd/).
-			 * SunOS 4.1.3 does this...
-			 */
-			if ((check & GF_EXCHECK) && xp > Xstring(*xs, xp) &&
-			    xp[-1] == '/' && !S_ISDIR(lstatb.st_mode) &&
-			    (!S_ISLNK(lstatb.st_mode) ||
-			    stat_check() < 0 || !S_ISDIR(statb.st_mode)))
-				return;
-			/*
-			 * Possibly tack on a trailing / if there isn't already
-			 * one and if the file is a directory or a symlink to a
-			 * directory
-			 */
-			if (((check & GF_MARKDIR) && (check & GF_GLOBBED)) &&
-			    xp > Xstring(*xs, xp) && xp[-1] != '/' &&
-			    (S_ISDIR(lstatb.st_mode) ||
-			    (S_ISLNK(lstatb.st_mode) && stat_check() > 0 &&
-			    S_ISDIR(statb.st_mode)))) {
-				*xp++ = '/';
-				*xp = '\0';
-			}
-		}
-		strndupx(np, Xstring(*xs, xp), Xlength(*xs, xp), ATEMP);
-		XPput(*wp, np);
-		return;
-	}
-
-	if (xp > Xstring(*xs, xp))
-		*xp++ = '/';
-	while (*sp == '/') {
-		Xcheck(*xs, xp);
-		*xp++ = *sp++;
-	}
-	np = strchr(sp, '/');
-	if (np != NULL) {
-		se = np;
-		/* don't assume '/', can be multiple kinds */
-		odirsep = *np;
-		*np++ = '\0';
-	} else {
-		odirsep = '\0'; /* keep gcc quiet */
-		se = sp + strlen(sp);
-	}
-
-
-	/*
-	 * Check if sp needs globbing - done to avoid pattern checks for strings
-	 * containing MAGIC characters, open [s without the matching close ],
-	 * etc. (otherwise opendir() will be called which may fail because the
-	 * directory isn't readable - if no globbing is needed, only execute
-	 * permission should be required (as per POSIX)).
-	 */
-	if (!has_globbing(sp, se)) {
-		XcheckN(*xs, xp, se - sp + 1);
-		debunk(xp, sp, Xnleft(*xs, xp));
-		xp += strlen(xp);
-		*xpp = xp;
-		globit(xs, xpp, np, wp, check);
-	} else {
-		DIR *dirp;
-		struct dirent *d;
-		char *name;
-		size_t len, prefix_len;
-
-		/* xp = *xpp;	copy_non_glob() may have re-alloc'd xs */
-		*xp = '\0';
-		prefix_len = Xlength(*xs, xp);
-		dirp = opendir(prefix_len ? Xstring(*xs, xp) : ".");
-		if (dirp == NULL)
-			goto Nodir;
-		while ((d = readdir(dirp)) != NULL) {
-			name = d->d_name;
-			if (name[0] == '.' &&
-			    (name[1] == 0 || (name[1] == '.' && name[2] == 0)))
-				/* always ignore . and .. */
-				continue;
-			if ((*name == '.' && *sp != '.') ||
-			    !gmatchx(name, sp, true))
-				continue;
-
-			len = strlen(d->d_name) + 1;
-			XcheckN(*xs, xp, len);
-			memcpy(xp, name, len);
-			*xpp = xp + len - 1;
-			globit(xs, xpp, np, wp,
-				(check & GF_MARKDIR) | GF_GLOBBED
-				| (np ? GF_EXCHECK : GF_NONE));
-			xp = Xstring(*xs, xp) + prefix_len;
-		}
-		closedir(dirp);
- Nodir:
-		;
-	}
-
-	if (np != NULL)
-		*--np = odirsep;
-}
-
-/* remove MAGIC from string */
-char *
-debunk(char *dp, const char *sp, size_t dlen)
-{
-	char *d;
-	const char *s;
-
-	if ((s = cstrchr(sp, MAGIC))) {
-		if (s - sp >= (ssize_t)dlen)
-			return (dp);
-		memmove(dp, sp, s - sp);
-		for (d = dp + (s - sp); *s && (d - dp < (ssize_t)dlen); s++)
-			if (!ISMAGIC(*s) || !(*++s & 0x80) ||
-			    !vstrchr("*+?@! ", *s & 0x7f))
-				*d++ = *s;
-			else {
-				/* extended pattern operators: *+?@! */
-				if ((*s & 0x7f) != ' ')
-					*d++ = *s & 0x7f;
-				if (d - dp < (ssize_t)dlen)
-					*d++ = '(';
-			}
-		*d = '\0';
-	} else if (dp != sp)
-		strlcpy(dp, sp, dlen);
-	return (dp);
-}
-
-/*
- * Check if p is an unquoted name, possibly followed by a / or :. If so
- * puts the expanded version in *dcp,dp and returns a pointer in p just
- * past the name, otherwise returns 0.
- */
-static const char *
-maybe_expand_tilde(const char *p, XString *dsp, char **dpp, int isassign)
-{
-	XString ts;
-	char *dp = *dpp;
-	char *tp;
-	const char *r;
-
-	Xinit(ts, tp, 16, ATEMP);
-	/* : only for DOASNTILDE form */
-	while (p[0] == CHAR && p[1] != '/' && (!isassign || p[1] != ':'))
-	{
-		Xcheck(ts, tp);
-		*tp++ = p[1];
-		p += 2;
-	}
-	*tp = '\0';
-	r = (p[0] == EOS || p[0] == CHAR || p[0] == CSUBST) ?
-	    tilde(Xstring(ts, tp)) : NULL;
-	Xfree(ts, tp);
-	if (r) {
-		while (*r) {
-			Xcheck(*dsp, dp);
-			if (ISMAGIC(*r))
-				*dp++ = MAGIC;
-			*dp++ = *r++;
-		}
-		*dpp = dp;
-		r = p;
-	}
-	return (r);
-}
-
-/*
- * tilde expansion
- *
- * based on a version by Arnold Robbins
- */
-
-char *
-tilde(char *cp)
-{
-	char *dp = null;
-
-	if (cp[0] == '\0')
-		dp = str_val(global("HOME"));
-	else if (cp[0] == '+' && cp[1] == '\0')
-		dp = str_val(global("PWD"));
-	else if (cp[0] == '-' && cp[1] == '\0')
-		dp = str_val(global("OLDPWD"));
-#ifndef MKSH_NOPWNAM
-	else
-		dp = homedir(cp);
-#endif
-	/* If HOME, PWD or OLDPWD are not set, don't expand ~ */
-	return (dp == null ? NULL : dp);
-}
-
-#ifndef MKSH_NOPWNAM
-/*
- * map userid to user's home directory.
- * note that 4.3's getpw adds more than 6K to the shell,
- * and the YP version probably adds much more.
- * we might consider our own version of getpwnam() to keep the size down.
- */
-static char *
-homedir(char *name)
-{
-	struct tbl *ap;
-
-	ap = ktenter(&homedirs, name, hash(name));
-	if (!(ap->flag & ISSET)) {
-		struct passwd *pw;
-
-		pw = getpwnam(name);
-		if (pw == NULL)
-			return (NULL);
-		strdupx(ap->val.s, pw->pw_dir, APERM);
-		ap->flag |= DEFINED|ISSET|ALLOC;
-	}
-	return (ap->val.s);
-}
-#endif
-
-static void
-alt_expand(XPtrV *wp, char *start, char *exp_start, char *end, int fdo)
-{
-	int count = 0;
-	char *brace_start, *brace_end, *comma = NULL;
-	char *field_start;
-	char *p;
-
-	/* search for open brace */
-	for (p = exp_start; (p = strchr(p, MAGIC)) && p[1] != '{' /*}*/; p += 2)
-		;
-	brace_start = p;
-
-	/* find matching close brace, if any */
-	if (p) {
-		comma = NULL;
-		count = 1;
-		for (p += 2; *p && count; p++) {
-			if (ISMAGIC(*p)) {
-				if (*++p == '{' /*}*/)
-					count++;
-				else if (*p == /*{*/ '}')
-					--count;
-				else if (*p == ',' && count == 1)
-					comma = p;
-			}
-		}
-	}
-	/* no valid expansions... */
-	if (!p || count != 0) {
-		/*
-		 * Note that given a{{b,c} we do not expand anything (this is
-		 * what AT&T ksh does. This may be changed to do the {b,c}
-		 * expansion. }
-		 */
-		if (fdo & DOGLOB)
-			glob(start, wp, tobool(fdo & DOMARKDIRS));
-		else
-			XPput(*wp, debunk(start, start, end - start));
-		return;
-	}
-	brace_end = p;
-	if (!comma) {
-		alt_expand(wp, start, brace_end, end, fdo);
-		return;
-	}
-
-	/* expand expression */
-	field_start = brace_start + 2;
-	count = 1;
-	for (p = brace_start + 2; p != brace_end; p++) {
-		if (ISMAGIC(*p)) {
-			if (*++p == '{' /*}*/)
-				count++;
-			else if ((*p == /*{*/ '}' && --count == 0) ||
-			    (*p == ',' && count == 1)) {
-				char *news;
-				int l1, l2, l3;
-
-				/*
-				 * addition safe since these operate on
-				 * one string (separate substrings)
-				 */
-				l1 = brace_start - start;
-				l2 = (p - 1) - field_start;
-				l3 = end - brace_end;
-				news = alloc(l1 + l2 + l3 + 1, ATEMP);
-				memcpy(news, start, l1);
-				memcpy(news + l1, field_start, l2);
-				memcpy(news + l1 + l2, brace_end, l3);
-				news[l1 + l2 + l3] = '\0';
-				alt_expand(wp, news, news + l1,
-				    news + l1 + l2 + l3, fdo);
-				field_start = p + 1;
-			}
-		}
-	}
-	return;
-}
-
-/* helper function due to setjmp/longjmp woes */
-static char *
-valsub(struct op *t, Area *ap)
-{
-	char * volatile cp = NULL;
-	struct tbl * volatile vp = NULL;
-
-	newenv(E_FUNC);
-	newblock();
-	if (ap)
-		vp = local("REPLY", false);
-	if (!kshsetjmp(e->jbuf))
-		execute(t, XXCOM | XERROK, NULL);
-	if (vp)
-		strdupx(cp, str_val(vp), ap);
-	quitenv(NULL);
-
-	return (cp);
-}

Copied: vendor/MirOS/mksh/R50/eval.c (from rev 6707, vendor/MirOS/mksh/dist/eval.c)
===================================================================
--- vendor/MirOS/mksh/R50/eval.c	                        (rev 0)
+++ vendor/MirOS/mksh/R50/eval.c	2014-07-01 12:01:17 UTC (rev 6708)
@@ -0,0 +1,1833 @@
+/*	$OpenBSD: eval.c,v 1.40 2013/09/14 20:09:30 millert Exp $	*/
+
+/*-
+ * Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
+ *		 2011, 2012, 2013, 2014
+ *	Thorsten Glaser <tg at mirbsd.org>
+ *
+ * Provided that these terms and disclaimer and all copyright notices
+ * are retained or reproduced in an accompanying document, permission
+ * is granted to deal in this work without restriction, including un-
+ * limited rights to use, publicly perform, distribute, sell, modify,
+ * merge, give away, or sublicence.
+ *
+ * This work is provided "AS IS" and WITHOUT WARRANTY of any kind, to
+ * the utmost extent permitted by applicable law, neither express nor
+ * implied; without malicious intent or gross negligence. In no event
+ * may a licensor, author or contributor be held liable for indirect,
+ * direct, other damage, loss, or other issues arising in any way out
+ * of dealing in the work, even if advised of the possibility of such
+ * damage or existence of a defect, except proven that it results out
+ * of said person's immediate fault when using the work as intended.
+ */
+
+#include "sh.h"
+
+__RCSID("$MirOS: src/bin/mksh/eval.c,v 1.150 2014/06/09 11:16:07 tg Exp $");
+
+/*
+ * string expansion
+ *
+ * first pass: quoting, IFS separation, ~, ${}, $() and $(()) substitution.
+ * second pass: alternation ({,}), filename expansion (*?[]).
+ */
+
+/* expansion generator state */
+typedef struct {
+	/* not including an "int type;" member, see expand() */
+	/* string */
+	const char *str;
+	/* source */
+	union {
+		/* string[] */
+		const char **strv;
+		/* file */
+		struct shf *shf;
+	} u;
+	/* variable in ${var...} */
+	struct tbl *var;
+	/* split "$@" / call waitlast in $() */
+	bool split;
+} Expand;
+
+#define	XBASE		0	/* scanning original */
+#define	XSUB		1	/* expanding ${} string */
+#define	XARGSEP		2	/* ifs0 between "$*" */
+#define	XARG		3	/* expanding $*, $@ */
+#define	XCOM		4	/* expanding $() */
+#define XNULLSUB	5	/* "$@" when $# is 0 (don't generate word) */
+#define XSUBMID		6	/* middle of expanding ${} */
+
+/* States used for field splitting */
+#define IFS_WORD	0	/* word has chars (or quotes) */
+#define IFS_WS		1	/* have seen IFS white-space */
+#define IFS_NWS		2	/* have seen IFS non-white-space */
+
+static int varsub(Expand *, const char *, const char *, int *, int *);
+static int comsub(Expand *, const char *, int);
+static char *valsub(struct op *, Area *);
+static char *trimsub(char *, char *, int);
+static void glob(char *, XPtrV *, bool);
+static void globit(XString *, char **, char *, XPtrV *, int);
+static const char *maybe_expand_tilde(const char *, XString *, char **, int);
+#ifndef MKSH_NOPWNAM
+static char *homedir(char *);
+#endif
+static void alt_expand(XPtrV *, char *, char *, char *, int);
+static int utflen(const char *) MKSH_A_PURE;
+static void utfincptr(const char *, mksh_ari_t *);
+
+/* UTFMODE functions */
+static int
+utflen(const char *s)
+{
+	size_t n;
+
+	if (UTFMODE) {
+		n = 0;
+		while (*s) {
+			s += utf_ptradj(s);
+			++n;
+		}
+	} else
+		n = strlen(s);
+
+	if (n > 2147483647)
+		n = 2147483647;
+	return ((int)n);
+}
+
+static void
+utfincptr(const char *s, mksh_ari_t *lp)
+{
+	const char *cp = s;
+
+	while ((*lp)--)
+		cp += utf_ptradj(cp);
+	*lp = cp - s;
+}
+
+/* compile and expand word */
+char *
+substitute(const char *cp, int f)
+{
+	struct source *s, *sold;
+
+	sold = source;
+	s = pushs(SWSTR, ATEMP);
+	s->start = s->str = cp;
+	source = s;
+	if (yylex(ONEWORD) != LWORD)
+		internal_errorf("bad substitution");
+	source = sold;
+	afree(s, ATEMP);
+	return (evalstr(yylval.cp, f));
+}
+
+/*
+ * expand arg-list
+ */
+char **
+eval(const char **ap, int f)
+{
+	XPtrV w;
+
+	if (*ap == NULL) {
+		union mksh_ccphack vap;
+
+		vap.ro = ap;
+		return (vap.rw);
+	}
+	XPinit(w, 32);
+	/* space for shell name */
+	XPput(w, NULL);
+	while (*ap != NULL)
+		expand(*ap++, &w, f);
+	XPput(w, NULL);
+	return ((char **)XPclose(w) + 1);
+}
+
+/*
+ * expand string
+ */
+char *
+evalstr(const char *cp, int f)
+{
+	XPtrV w;
+	char *dp = null;
+
+	XPinit(w, 1);
+	expand(cp, &w, f);
+	if (XPsize(w))
+		dp = *XPptrv(w);
+	XPfree(w);
+	return (dp);
+}
+
+/*
+ * expand string - return only one component
+ * used from iosetup to expand redirection files
+ */
+char *
+evalonestr(const char *cp, int f)
+{
+	XPtrV w;
+	char *rv;
+
+	XPinit(w, 1);
+	expand(cp, &w, f);
+	switch (XPsize(w)) {
+	case 0:
+		rv = null;
+		break;
+	case 1:
+		rv = (char *) *XPptrv(w);
+		break;
+	default:
+		rv = evalstr(cp, f&~DOGLOB);
+		break;
+	}
+	XPfree(w);
+	return (rv);
+}
+
+/* for nested substitution: ${var:=$var2} */
+typedef struct SubType {
+	struct tbl *var;	/* variable for ${var..} */
+	struct SubType *prev;	/* old type */
+	struct SubType *next;	/* poped type (to avoid re-allocating) */
+	size_t	base;		/* begin position of expanded word */
+	short	stype;		/* [=+-?%#] action after expanded word */
+	short	f;		/* saved value of f (DOPAT, etc) */
+	uint8_t	quotep;		/* saved value of quote (for ${..[%#]..}) */
+	uint8_t	quotew;		/* saved value of quote (for ${..[+-=]..}) */
+} SubType;
+
+void
+expand(
+    /* input word */
+    const char *ccp,
+    /* output words */
+    XPtrV *wp,
+    /* DO* flags */
+    int f)
+{
+	int c = 0;
+	/* expansion type */
+	int type;
+	/* quoted */
+	int quote = 0;
+	/* destination string and live pointer */
+	XString ds;
+	char *dp;
+	/* source */
+	const char *sp;
+	/* second pass flags */
+	int fdo;
+	/* have word */
+	int word;
+	/* field splitting of parameter/command substitution */
+	int doblank;
+	/* expansion variables */
+	Expand x = {
+		NULL, { NULL }, NULL, 0
+	};
+	SubType st_head, *st;
+	/* record number of trailing newlines in COMSUB */
+	int newlines = 0;
+	bool saw_eq, make_magic;
+	int tilde_ok;
+	size_t len;
+	char *cp;
+
+	if (ccp == NULL)
+		internal_errorf("expand(NULL)");
+	/* for alias, readonly, set, typeset commands */
+	if ((f & DOVACHECK) && is_wdvarassign(ccp)) {
+		f &= ~(DOVACHECK|DOBLANK|DOGLOB|DOTILDE);
+		f |= DOASNTILDE;
+	}
+	if (Flag(FNOGLOB))
+		f &= ~DOGLOB;
+	if (Flag(FMARKDIRS))
+		f |= DOMARKDIRS;
+	if (Flag(FBRACEEXPAND) && (f & DOGLOB))
+		f |= DOBRACE;
+
+	/* init destination string */
+	Xinit(ds, dp, 128, ATEMP);
+	type = XBASE;
+	sp = ccp;
+	fdo = 0;
+	saw_eq = false;
+	/* must be 1/0 */
+	tilde_ok = (f & (DOTILDE|DOASNTILDE)) ? 1 : 0;
+	doblank = 0;
+	make_magic = false;
+	word = (f&DOBLANK) ? IFS_WS : IFS_WORD;
+	/* clang doesn't know OSUBST comes before CSUBST */
+	memset(&st_head, 0, sizeof(st_head));
+	st = &st_head;
+
+	while (/* CONSTCOND */ 1) {
+		Xcheck(ds, dp);
+
+		switch (type) {
+		case XBASE:
+			/* original prefixed string */
+			c = *sp++;
+			switch (c) {
+			case EOS:
+				c = 0;
+				break;
+			case CHAR:
+				c = *sp++;
+				break;
+			case QCHAR:
+				/* temporary quote */
+				quote |= 2;
+				c = *sp++;
+				break;
+			case OQUOTE:
+				word = IFS_WORD;
+				tilde_ok = 0;
+				quote = 1;
+				continue;
+			case CQUOTE:
+				quote = st->quotew;
+				continue;
+			case COMSUB:
+			case FUNSUB:
+			case VALSUB:
+				tilde_ok = 0;
+				if (f & DONTRUNCOMMAND) {
+					word = IFS_WORD;
+					*dp++ = '$';
+					*dp++ = c == COMSUB ? '(' : '{';
+					if (c != COMSUB)
+						*dp++ = c == FUNSUB ? ' ' : '|';
+					while (*sp != '\0') {
+						Xcheck(ds, dp);
+						*dp++ = *sp++;
+					}
+					if (c != COMSUB) {
+						*dp++ = ';';
+						*dp++ = '}';
+					} else
+						*dp++ = ')';
+				} else {
+					type = comsub(&x, sp, c);
+					if (type != XBASE && (f & DOBLANK))
+						doblank++;
+					sp = strnul(sp) + 1;
+					newlines = 0;
+				}
+				continue;
+			case EXPRSUB:
+				tilde_ok = 0;
+				if (f & DONTRUNCOMMAND) {
+					word = IFS_WORD;
+					*dp++ = '$'; *dp++ = '('; *dp++ = '(';
+					while (*sp != '\0') {
+						Xcheck(ds, dp);
+						*dp++ = *sp++;
+					}
+					*dp++ = ')'; *dp++ = ')';
+				} else {
+					struct tbl v;
+
+					v.flag = DEFINED|ISSET|INTEGER;
+					/* not default */
+					v.type = 10;
+					v.name[0] = '\0';
+					v_evaluate(&v, substitute(sp, 0),
+					    KSH_UNWIND_ERROR, true);
+					sp = strnul(sp) + 1;
+					x.str = str_val(&v);
+					type = XSUB;
+					if (f & DOBLANK)
+						doblank++;
+				}
+				continue;
+			case OSUBST: {
+				/* ${{#}var{:}[=+-?#%]word} */
+			/*-
+			 * format is:
+			 *	OSUBST [{x] plain-variable-part \0
+			 *	    compiled-word-part CSUBST [}x]
+			 * This is where all syntax checking gets done...
+			 */
+				/* skip the { or x (}) */
+				const char *varname = ++sp;
+				int stype;
+				int slen = 0;
+
+				/* skip variable */
+				sp = cstrchr(sp, '\0') + 1;
+				type = varsub(&x, varname, sp, &stype, &slen);
+				if (type < 0) {
+					char *beg, *end, *str;
+ unwind_substsyn:
+					/* restore sp */
+					sp = varname - 2;
+					end = (beg = wdcopy(sp, ATEMP)) +
+					    (wdscan(sp, CSUBST) - sp);
+					/* ({) the } or x is already skipped */
+					if (end < wdscan(beg, EOS))
+						*end = EOS;
+					str = snptreef(NULL, 64, "%S", beg);
+					afree(beg, ATEMP);
+					errorf("%s: %s", str, "bad substitution");
+				}
+				if (f & DOBLANK)
+					doblank++;
+				tilde_ok = 0;
+				if (type == XBASE) {
+					/* expand? */
+					if (!st->next) {
+						SubType *newst;
+
+						newst = alloc(sizeof(SubType), ATEMP);
+						newst->next = NULL;
+						newst->prev = st;
+						st->next = newst;
+					}
+					st = st->next;
+					st->stype = stype;
+					st->base = Xsavepos(ds, dp);
+					st->f = f;
+					if (x.var == &vtemp) {
+						st->var = tempvar();
+						st->var->flag &= ~INTEGER;
+						/* can't fail here */
+						setstr(st->var,
+						    str_val(x.var),
+						    KSH_RETURN_ERROR | 0x4);
+					} else
+						st->var = x.var;
+
+					st->quotew = st->quotep = quote;
+					/* skip qualifier(s) */
+					if (stype)
+						sp += slen;
+					switch (stype & 0x17F) {
+					case 0x100 | '#':
+						x.str = shf_smprintf("%08X",
+						    (unsigned int)hash(str_val(st->var)));
+						break;
+					case 0x100 | 'Q': {
+						struct shf shf;
+
+						shf_sopen(NULL, 0, SHF_WR|SHF_DYNAMIC, &shf);
+						print_value_quoted(&shf, str_val(st->var));
+						x.str = shf_sclose(&shf);
+						break;
+					}
+					case '0': {
+						char *beg, *mid, *end, *stg;
+						mksh_ari_t from = 0, num = -1, flen, finc = 0;
+
+						beg = wdcopy(sp, ATEMP);
+						mid = beg + (wdscan(sp, ADELIM) - sp);
+						stg = beg + (wdscan(sp, CSUBST) - sp);
+						if (mid >= stg)
+							goto unwind_substsyn;
+						mid[-2] = EOS;
+						if (mid[-1] == /*{*/'}') {
+							sp += mid - beg - 1;
+							end = NULL;
+						} else {
+							end = mid +
+							    (wdscan(mid, ADELIM) - mid);
+							if (end >= stg ||
+							    /* more than max delimiters */
+							    end[-1] != /*{*/ '}')
+								goto unwind_substsyn;
+							end[-2] = EOS;
+							sp += end - beg - 1;
+						}
+						evaluate(substitute(stg = wdstrip(beg, 0), 0),
+						    &from, KSH_UNWIND_ERROR, true);
+						afree(stg, ATEMP);
+						if (end) {
+							evaluate(substitute(stg = wdstrip(mid, 0), 0),
+							    &num, KSH_UNWIND_ERROR, true);
+							afree(stg, ATEMP);
+						}
+						afree(beg, ATEMP);
+						beg = str_val(st->var);
+						flen = utflen(beg);
+						if (from < 0) {
+							if (-from < flen)
+								finc = flen + from;
+						} else
+							finc = from < flen ? from : flen;
+						if (UTFMODE)
+							utfincptr(beg, &finc);
+						beg += finc;
+						flen = utflen(beg);
+						if (num < 0 || num > flen)
+							num = flen;
+						if (UTFMODE)
+							utfincptr(beg, &num);
+						strndupx(x.str, beg, num, ATEMP);
+						goto do_CSUBST;
+					}
+					case '/': {
+						char *s, *p, *d, *sbeg, *end;
+						char *pat, *rrep;
+						char *tpat0, *tpat1, *tpat2;
+
+						s = wdcopy(sp, ATEMP);
+						p = s + (wdscan(sp, ADELIM) - sp);
+						d = s + (wdscan(sp, CSUBST) - sp);
+						if (p >= d)
+							goto unwind_substsyn;
+						p[-2] = EOS;
+						if (p[-1] == /*{*/'}')
+							d = NULL;
+						else
+							d[-2] = EOS;
+						sp += (d ? d : p) - s - 1;
+						tpat0 = wdstrip(s,
+						    WDS_KEEPQ | WDS_MAGIC);
+						pat = substitute(tpat0, 0);
+						if (d) {
+							d = wdstrip(p, WDS_KEEPQ);
+							rrep = substitute(d, 0);
+							afree(d, ATEMP);
+						} else
+							rrep = null;
+						afree(s, ATEMP);
+						s = d = pat;
+						while (*s)
+							if (*s != '\\' ||
+							    s[1] == '%' ||
+							    s[1] == '#' ||
+							    s[1] == '\0' ||
+				/* XXX really? */	    s[1] == '\\' ||
+							    s[1] == '/')
+								*d++ = *s++;
+							else
+								s++;
+						*d = '\0';
+						afree(tpat0, ATEMP);
+
+						/* check for special cases */
+						d = str_val(st->var);
+						mkssert(d != NULL);
+						switch (*pat) {
+						case '#':
+							/* anchor at begin */
+							tpat0 = pat + 1;
+							tpat1 = rrep;
+							tpat2 = d;
+							break;
+						case '%':
+							/* anchor at end */
+							tpat0 = pat + 1;
+							tpat1 = d;
+							tpat2 = rrep;
+							break;
+						case '\0':
+							/* empty pattern */
+							goto no_repl;
+						default:
+							tpat0 = pat;
+							/* silence gcc */
+							tpat1 = tpat2 = NULL;
+						}
+						if (gmatchx(null, tpat0, false)) {
+							/*
+							 * pattern matches
+							 * the empty string
+							 */
+							if (tpat0 == pat)
+								goto no_repl;
+							/* but is anchored */
+							s = shf_smprintf("%s%s",
+							    tpat1, tpat2);
+							goto do_repl;
+						}
+
+						/* prepare string on which to work */
+						strdupx(s, d, ATEMP);
+						sbeg = s;
+
+						/* first see if we have any match at all */
+						tpat0 = pat;
+						if (*pat == '#') {
+							/* anchor at the beginning */
+							tpat1 = shf_smprintf("%s%c*", ++tpat0, MAGIC);
+							tpat2 = tpat1;
+						} else if (*pat == '%') {
+							/* anchor at the end */
+							tpat1 = shf_smprintf("%c*%s", MAGIC, ++tpat0);
+							tpat2 = tpat0;
+						} else {
+							/* float */
+							tpat1 = shf_smprintf("%c*%s%c*", MAGIC, pat, MAGIC);
+							tpat2 = tpat1 + 2;
+						}
+ again_repl:
+						/*
+						 * this would not be necessary if gmatchx would return
+						 * the start and end values of a match found, like re*
+						 */
+						if (!gmatchx(sbeg, tpat1, false))
+							goto end_repl;
+						end = strnul(s);
+						/* now anchor the beginning of the match */
+						if (*pat != '#')
+							while (sbeg <= end) {
+								if (gmatchx(sbeg, tpat2, false))
+									break;
+								else
+									sbeg++;
+							}
+						/* now anchor the end of the match */
+						p = end;
+						if (*pat != '%')
+							while (p >= sbeg) {
+								bool gotmatch;
+
+								c = *p;
+								*p = '\0';
+								gotmatch = tobool(gmatchx(sbeg, tpat0, false));
+								*p = c;
+								if (gotmatch)
+									break;
+								p--;
+							}
+						strndupx(end, s, sbeg - s, ATEMP);
+						d = shf_smprintf("%s%s%s", end, rrep, p);
+						afree(end, ATEMP);
+						sbeg = d + (sbeg - s) + strlen(rrep);
+						afree(s, ATEMP);
+						s = d;
+						if (stype & 0x80)
+							goto again_repl;
+ end_repl:
+						afree(tpat1, ATEMP);
+ do_repl:
+						x.str = s;
+ no_repl:
+						afree(pat, ATEMP);
+						if (rrep != null)
+							afree(rrep, ATEMP);
+						goto do_CSUBST;
+					}
+					case '#':
+					case '%':
+						/* ! DOBLANK,DOBRACE,DOTILDE */
+						f = (f & DONTRUNCOMMAND) |
+						    DOPAT | DOTEMP;
+						st->quotew = quote = 0;
+						/*
+						 * Prepend open pattern (so |
+						 * in a trim will work as
+						 * expected)
+						 */
+						if (!Flag(FSH)) {
+							*dp++ = MAGIC;
+							*dp++ = '@' | 0x80;
+						}
+						break;
+					case '=':
+						/*
+						 * Enabling tilde expansion
+						 * after :s here is
+						 * non-standard ksh, but is
+						 * consistent with rules for
+						 * other assignments. Not
+						 * sure what POSIX thinks of
+						 * this.
+						 * Not doing tilde expansion
+						 * for integer variables is a
+						 * non-POSIX thing - makes
+						 * sense though, since ~ is
+						 * a arithmetic operator.
+						 */
+						if (!(x.var->flag & INTEGER))
+							f |= DOASNTILDE|DOTILDE;
+						f |= DOTEMP;
+						/*
+						 * These will be done after the
+						 * value has been assigned.
+						 */
+						f &= ~(DOBLANK|DOGLOB|DOBRACE);
+						tilde_ok = 1;
+						break;
+					case '?':
+						f &= ~DOBLANK;
+						f |= DOTEMP;
+						/* FALLTHROUGH */
+					default:
+						/* Enable tilde expansion */
+						tilde_ok = 1;
+						f |= DOTILDE;
+					}
+				} else
+					/* skip word */
+					sp += wdscan(sp, CSUBST) - sp;
+				continue;
+			}
+			case CSUBST:
+				/* only get here if expanding word */
+ do_CSUBST:
+				/* ({) skip the } or x */
+				sp++;
+				/* in case of ${unset:-} */
+				tilde_ok = 0;
+				*dp = '\0';
+				quote = st->quotep;
+				f = st->f;
+				if (f & DOBLANK)
+					doblank--;
+				switch (st->stype & 0x17F) {
+				case '#':
+				case '%':
+					if (!Flag(FSH)) {
+						/* Append end-pattern */
+						*dp++ = MAGIC;
+						*dp++ = ')';
+					}
+					*dp = '\0';
+					dp = Xrestpos(ds, dp, st->base);
+					/*
+					 * Must use st->var since calling
+					 * global would break things
+					 * like x[i+=1].
+					 */
+					x.str = trimsub(str_val(st->var),
+						dp, st->stype);
+					if (x.str[0] != '\0') {
+						word = IFS_WS;
+						type = XSUB;
+					} else
+						type = quote ? XSUB : XNULLSUB;
+					if (f & DOBLANK)
+						doblank++;
+					st = st->prev;
+					continue;
+				case '=':
+					/*
+					 * Restore our position and substitute
+					 * the value of st->var (may not be
+					 * the assigned value in the presence
+					 * of integer/right-adj/etc attributes).
+					 */
+					dp = Xrestpos(ds, dp, st->base);
+					/*
+					 * Must use st->var since calling
+					 * global would cause with things
+					 * like x[i+=1] to be evaluated twice.
+					 */
+					/*
+					 * Note: not exported by FEXPORT
+					 * in AT&T ksh.
+					 */
+					/*
+					 * XXX POSIX says readonly is only
+					 * fatal for special builtins (setstr
+					 * does readonly check).
+					 */
+					len = strlen(dp) + 1;
+					setstr(st->var,
+					    debunk(alloc(len, ATEMP),
+					    dp, len), KSH_UNWIND_ERROR);
+					x.str = str_val(st->var);
+					type = XSUB;
+					if (f & DOBLANK)
+						doblank++;
+					st = st->prev;
+					continue;
+				case '?': {
+					char *s = Xrestpos(ds, dp, st->base);
+
+					errorf("%s: %s", st->var->name,
+					    dp == s ?
+					    "parameter null or not set" :
+					    (debunk(s, s, strlen(s) + 1), s));
+				}
+				case '0':
+				case '/':
+				case 0x100 | '#':
+				case 0x100 | 'Q':
+					dp = Xrestpos(ds, dp, st->base);
+					type = XSUB;
+					if (f & DOBLANK)
+						doblank++;
+					st = st->prev;
+					continue;
+				}
+				st = st->prev;
+				type = XBASE;
+				continue;
+
+			case OPAT:
+				/* open pattern: *(foo|bar) */
+				/* Next char is the type of pattern */
+				make_magic = true;
+				c = *sp++ | 0x80;
+				break;
+
+			case SPAT:
+				/* pattern separator (|) */
+				make_magic = true;
+				c = '|';
+				break;
+
+			case CPAT:
+				/* close pattern */
+				make_magic = true;
+				c = /*(*/ ')';
+				break;
+			}
+			break;
+
+		case XNULLSUB:
+			/*
+			 * Special case for "$@" (and "${foo[@]}") - no
+			 * word is generated if $# is 0 (unless there is
+			 * other stuff inside the quotes).
+			 */
+			type = XBASE;
+			if (f & DOBLANK) {
+				doblank--;
+				/*
+				 * XXX not really correct:
+				 *	x=; "$x$@"
+				 * should generate a null argument and
+				 *	set A; "${@:+}"
+				 * shouldn't.
+				 */
+				if (dp == Xstring(ds, dp))
+					word = IFS_WS;
+			}
+			continue;
+
+		case XSUB:
+		case XSUBMID:
+			if ((c = *x.str++) == 0) {
+				type = XBASE;
+				if (f & DOBLANK)
+					doblank--;
+				continue;
+			}
+			break;
+
+		case XARGSEP:
+			type = XARG;
+			quote = 1;
+			/* FALLTHROUGH */
+		case XARG:
+			if ((c = *x.str++) == '\0') {
+				/*
+				 * force null words to be created so
+				 * set -- '' 2 ''; foo "$@" will do
+				 * the right thing
+				 */
+				if (quote && x.split)
+					word = IFS_WORD;
+				if ((x.str = *x.u.strv++) == NULL) {
+					type = XBASE;
+					if (f & DOBLANK)
+						doblank--;
+					continue;
+				}
+				c = ifs0;
+				if (c == 0) {
+					if (quote && !x.split)
+						continue;
+					/* this is so we don't terminate */
+					c = ' ';
+					/* now force-emit a word */
+					goto emit_word;
+				}
+				if (quote && x.split) {
+					/* terminate word for "$@" */
+					type = XARGSEP;
+					quote = 0;
+				}
+			}
+			break;
+
+		case XCOM:
+			if (x.u.shf == NULL) {
+				/* $(<...) failed */
+				subst_exstat = 1;
+				/* fake EOF */
+				c = EOF;
+			} else if (newlines) {
+				/* spit out saved NLs */
+				c = '\n';
+				--newlines;
+			} else {
+				while ((c = shf_getc(x.u.shf)) == 0 || c == '\n')
+					if (c == '\n')
+						/* save newlines */
+						newlines++;
+				if (newlines && c != EOF) {
+					shf_ungetc(c, x.u.shf);
+					c = '\n';
+					--newlines;
+				}
+			}
+			if (c == EOF) {
+				newlines = 0;
+				if (x.u.shf)
+					shf_close(x.u.shf);
+				if (x.split)
+					subst_exstat = waitlast();
+				type = XBASE;
+				if (f & DOBLANK)
+					doblank--;
+				continue;
+			}
+			break;
+		}
+
+		/* check for end of word or IFS separation */
+		if (c == 0 || (!quote && (f & DOBLANK) && doblank &&
+		    !make_magic && ctype(c, C_IFS))) {
+			/*-
+			 * How words are broken up:
+			 *			|	value of c
+			 *	word		|	ws	nws	0
+			 *	-----------------------------------
+			 *	IFS_WORD		w/WS	w/NWS	w
+			 *	IFS_WS			-/WS	w/NWS	-
+			 *	IFS_NWS			-/NWS	w/NWS	w
+			 * (w means generate a word)
+			 * Note that IFS_NWS/0 generates a word (AT&T ksh
+			 * doesn't do this, but POSIX does).
+			 */
+			if (word == IFS_WORD ||
+			    (!ctype(c, C_IFSWS) && c && word == IFS_NWS)) {
+ emit_word:
+				*dp++ = '\0';
+				cp = Xclose(ds, dp);
+				if (fdo & DOBRACE)
+					/* also does globbing */
+					alt_expand(wp, cp, cp,
+					    cp + Xlength(ds, (dp - 1)),
+					    fdo | (f & DOMARKDIRS));
+				else if (fdo & DOGLOB)
+					glob(cp, wp, tobool(f & DOMARKDIRS));
+				else if ((f & DOPAT) || !(fdo & DOMAGIC))
+					XPput(*wp, cp);
+				else
+					XPput(*wp, debunk(cp, cp,
+					    strlen(cp) + 1));
+				fdo = 0;
+				saw_eq = false;
+				tilde_ok = (f & (DOTILDE|DOASNTILDE)) ? 1 : 0;
+				if (c == 0)
+					return;
+				Xinit(ds, dp, 128, ATEMP);
+			} else if (c == 0) {
+				return;
+			} else if (type == XSUB && ctype(c, C_IFS) &&
+			    !ctype(c, C_IFSWS) && Xlength(ds, dp) == 0) {
+				*(cp = alloc(1, ATEMP)) = '\0';
+				XPput(*wp, cp);
+				type = XSUBMID;
+			}
+			if (word != IFS_NWS)
+				word = ctype(c, C_IFSWS) ? IFS_WS : IFS_NWS;
+		} else {
+			if (type == XSUB) {
+				if (word == IFS_NWS &&
+				    Xlength(ds, dp) == 0) {
+					*(cp = alloc(1, ATEMP)) = '\0';
+					XPput(*wp, cp);
+				}
+				type = XSUBMID;
+			}
+
+			/* age tilde_ok info - ~ code tests second bit */
+			tilde_ok <<= 1;
+			/* mark any special second pass chars */
+			if (!quote)
+				switch (c) {
+				case '[':
+				case '!':
+				case '-':
+				case ']':
+					/*
+					 * For character classes - doesn't hurt
+					 * to have magic !,-,]s outside of
+					 * [...] expressions.
+					 */
+					if (f & (DOPAT | DOGLOB)) {
+						fdo |= DOMAGIC;
+						if (c == '[')
+							fdo |= f & DOGLOB;
+						*dp++ = MAGIC;
+					}
+					break;
+				case '*':
+				case '?':
+					if (f & (DOPAT | DOGLOB)) {
+						fdo |= DOMAGIC | (f & DOGLOB);
+						*dp++ = MAGIC;
+					}
+					break;
+				case '{':
+				case '}':
+				case ',':
+					if ((f & DOBRACE) && (c == '{' /*}*/ ||
+					    (fdo & DOBRACE))) {
+						fdo |= DOBRACE|DOMAGIC;
+						*dp++ = MAGIC;
+					}
+					break;
+				case '=':
+					/* Note first unquoted = for ~ */
+					if (!(f & DOTEMP) && !saw_eq &&
+					    (Flag(FBRACEEXPAND) ||
+					    (f & DOASNTILDE))) {
+						saw_eq = true;
+						tilde_ok = 1;
+					}
+					break;
+				case ':':
+					/* : */
+					/* Note unquoted : for ~ */
+					if (!(f & DOTEMP) && (f & DOASNTILDE))
+						tilde_ok = 1;
+					break;
+				case '~':
+					/*
+					 * tilde_ok is reset whenever
+					 * any of ' " $( $(( ${ } are seen.
+					 * Note that tilde_ok must be preserved
+					 * through the sequence ${A=a=}~
+					 */
+					if (type == XBASE &&
+					    (f & (DOTILDE|DOASNTILDE)) &&
+					    (tilde_ok & 2)) {
+						const char *tcp;
+						char *tdp = dp;
+
+						tcp = maybe_expand_tilde(sp,
+						    &ds, &tdp,
+						    f & DOASNTILDE);
+						if (tcp) {
+							if (dp != tdp)
+								word = IFS_WORD;
+							dp = tdp;
+							sp = tcp;
+							continue;
+						}
+					}
+					break;
+				}
+			else
+				/* undo temporary */
+				quote &= ~2;
+
+			if (make_magic) {
+				make_magic = false;
+				fdo |= DOMAGIC | (f & DOGLOB);
+				*dp++ = MAGIC;
+			} else if (ISMAGIC(c)) {
+				fdo |= DOMAGIC;
+				*dp++ = MAGIC;
+			}
+			/* save output char */
+			*dp++ = c;
+			word = IFS_WORD;
+		}
+	}
+}
+
+/*
+ * Prepare to generate the string returned by ${} substitution.
+ */
+static int
+varsub(Expand *xp, const char *sp, const char *word,
+    int *stypep,	/* becomes qualifier type */
+    int *slenp)		/* " " len (=, :=, etc.) valid iff *stypep != 0 */
+{
+	int c;
+	int state;	/* next state: XBASE, XARG, XSUB, XNULLSUB */
+	int stype;	/* substitution type */
+	int slen;
+	const char *p;
+	struct tbl *vp;
+	bool zero_ok = false;
+
+	if ((stype = sp[0]) == '\0')
+		/* Bad variable name */
+		return (-1);
+
+	xp->var = NULL;
+
+	/*-
+	 * ${#var}, string length (-U: characters, +U: octets) or array size
+	 * ${%var}, string width (-U: screen columns, +U: octets)
+	 */
+	c = sp[1];
+	if (stype == '%' && c == '\0')
+		return (-1);
+	if ((stype == '#' || stype == '%') && c != '\0') {
+		/* Can't have any modifiers for ${#...} or ${%...} */
+		if (*word != CSUBST)
+			return (-1);
+		sp++;
+		/* Check for size of array */
+		if ((p = cstrchr(sp, '[')) && (p[1] == '*' || p[1] == '@') &&
+		    p[2] == ']') {
+			int n = 0;
+
+			if (stype != '#')
+				return (-1);
+			vp = global(arrayname(sp));
+			if (vp->flag & (ISSET|ARRAY))
+				zero_ok = true;
+			for (; vp; vp = vp->u.array)
+				if (vp->flag & ISSET)
+					n++;
+			c = n;
+		} else if (c == '*' || c == '@') {
+			if (stype != '#')
+				return (-1);
+			c = e->loc->argc;
+		} else {
+			p = str_val(global(sp));
+			zero_ok = p != null;
+			if (stype == '#')
+				c = utflen(p);
+			else {
+				/* partial utf_mbswidth reimplementation */
+				const char *s = p;
+				unsigned int wc;
+				size_t len;
+				int cw;
+
+				c = 0;
+				while (*s) {
+					if (!UTFMODE || (len = utf_mbtowc(&wc,
+					    s)) == (size_t)-1)
+						/* not UTFMODE or not UTF-8 */
+						wc = (unsigned char)(*s++);
+					else
+						/* UTFMODE and UTF-8 */
+						s += len;
+					/* wc == char or wchar at s++ */
+					if ((cw = utf_wcwidth(wc)) == -1) {
+						/* 646, 8859-1, 10646 C0/C1 */
+						c = -1;
+						break;
+					}
+					c += cw;
+				}
+			}
+		}
+		if (Flag(FNOUNSET) && c == 0 && !zero_ok)
+			errorf("%s: %s", sp, "parameter not set");
+		/* unqualified variable/string substitution */
+		*stypep = 0;
+		xp->str = shf_smprintf("%d", c);
+		return (XSUB);
+	}
+
+	/* Check for qualifiers in word part */
+	stype = 0;
+	c = word[slen = 0] == CHAR ? word[1] : 0;
+	if (c == ':') {
+		slen += 2;
+		stype = 0x80;
+		c = word[slen + 0] == CHAR ? word[slen + 1] : 0;
+	}
+	if (!stype && c == '/') {
+		slen += 2;
+		stype = c;
+		if (word[slen] == ADELIM) {
+			slen += 2;
+			stype |= 0x80;
+		}
+	} else if (stype == 0x80 && (c == ' ' || c == '0')) {
+		stype |= '0';
+	} else if (ctype(c, C_SUBOP1)) {
+		slen += 2;
+		stype |= c;
+	} else if (ctype(c, C_SUBOP2)) {
+		/* Note: ksh88 allows :%, :%%, etc */
+		slen += 2;
+		stype = c;
+		if (word[slen + 0] == CHAR && c == word[slen + 1]) {
+			stype |= 0x80;
+			slen += 2;
+		}
+	} else if (c == '@') {
+		/* @x where x is command char */
+		slen += 2;
+		stype |= 0x100;
+		if (word[slen] == CHAR) {
+			stype |= word[slen + 1];
+			slen += 2;
+		}
+	} else if (stype)
+		/* : is not ok */
+		return (-1);
+	if (!stype && *word != CSUBST)
+		return (-1);
+	*stypep = stype;
+	*slenp = slen;
+
+	c = sp[0];
+	if (c == '*' || c == '@') {
+		switch (stype & 0x17F) {
+		/* can't assign to a vector */
+		case '=':
+		/* can't trim a vector (yet) */
+		case '%':
+		case '#':
+		case '0':
+		case '/':
+		case 0x100 | '#':
+		case 0x100 | 'Q':
+			return (-1);
+		}
+		if (e->loc->argc == 0) {
+			xp->str = null;
+			xp->var = global(sp);
+			state = c == '@' ? XNULLSUB : XSUB;
+		} else {
+			xp->u.strv = (const char **)e->loc->argv + 1;
+			xp->str = *xp->u.strv++;
+			/* $@ */
+			xp->split = tobool(c == '@');
+			state = XARG;
+		}
+		/* POSIX 2009? */
+		zero_ok = true;
+	} else {
+		if ((p = cstrchr(sp, '[')) && (p[1] == '*' || p[1] == '@') &&
+		    p[2] == ']') {
+			XPtrV wv;
+
+			switch (stype & 0x17F) {
+			/* can't assign to a vector */
+			case '=':
+			/* can't trim a vector (yet) */
+			case '%':
+			case '#':
+			case '?':
+			case '0':
+			case '/':
+			case 0x100 | '#':
+			case 0x100 | 'Q':
+				return (-1);
+			}
+			XPinit(wv, 32);
+			if ((c = sp[0]) == '!')
+				++sp;
+			vp = global(arrayname(sp));
+			for (; vp; vp = vp->u.array) {
+				if (!(vp->flag&ISSET))
+					continue;
+				XPput(wv, c == '!' ? shf_smprintf("%lu",
+				    arrayindex(vp)) :
+				    str_val(vp));
+			}
+			if (XPsize(wv) == 0) {
+				xp->str = null;
+				state = p[1] == '@' ? XNULLSUB : XSUB;
+				XPfree(wv);
+			} else {
+				XPput(wv, 0);
+				xp->u.strv = (const char **)XPptrv(wv);
+				xp->str = *xp->u.strv++;
+				/* ${foo[@]} */
+				xp->split = tobool(p[1] == '@');
+				state = XARG;
+			}
+		} else {
+			/* Can't assign things like $! or $1 */
+			if ((stype & 0x17F) == '=' &&
+			    ctype(*sp, C_VAR1 | C_DIGIT))
+				return (-1);
+			if (*sp == '!' && sp[1]) {
+				++sp;
+				xp->var = global(sp);
+				if (vstrchr(sp, '['))
+					xp->str = shf_smprintf("%s[%lu]",
+					    xp->var->name,
+					    arrayindex(xp->var));
+				else
+					xp->str = xp->var->name;
+			} else {
+				xp->var = global(sp);
+				xp->str = str_val(xp->var);
+			}
+			state = XSUB;
+		}
+	}
+
+	c = stype & 0x7F;
+	/* test the compiler's code generator */
+	if (((stype < 0x100) && (ctype(c, C_SUBOP2) || c == '/' ||
+	    (((stype&0x80) ? *xp->str=='\0' : xp->str==null) ? /* undef? */
+	    c == '=' || c == '-' || c == '?' : c == '+'))) ||
+	    stype == (0x80 | '0') || stype == (0x100 | '#') ||
+	    stype == (0x100 | 'Q'))
+		/* expand word instead of variable value */
+		state = XBASE;
+	if (Flag(FNOUNSET) && xp->str == null && !zero_ok &&
+	    (ctype(c, C_SUBOP2) || (state != XBASE && c != '+')))
+		errorf("%s: %s", sp, "parameter not set");
+	return (state);
+}
+
+/*
+ * Run the command in $(...) and read its output.
+ */
+static int
+comsub(Expand *xp, const char *cp, int fn MKSH_A_UNUSED)
+{
+	Source *s, *sold;
+	struct op *t;
+	struct shf *shf;
+	uint8_t old_utfmode = UTFMODE;
+
+	s = pushs(SSTRING, ATEMP);
+	s->start = s->str = cp;
+	sold = source;
+	t = compile(s, true);
+	afree(s, ATEMP);
+	source = sold;
+
+	UTFMODE = old_utfmode;
+
+	if (t == NULL)
+		return (XBASE);
+
+	/* no waitlast() unless specifically enabled later */
+	xp->split = false;
+
+	if (t->type == TCOM &&
+	    *t->args == NULL && *t->vars == NULL && t->ioact != NULL) {
+		/* $(<file) */
+		struct ioword *io = *t->ioact;
+		char *name;
+
+		if ((io->flag & IOTYPE) != IOREAD)
+			errorf("%s: %s", "funny $() command",
+			    snptreef(NULL, 32, "%R", io));
+		shf = shf_open(name = evalstr(io->name, DOTILDE), O_RDONLY, 0,
+			SHF_MAPHI|SHF_CLEXEC);
+		if (shf == NULL)
+			warningf(!Flag(FTALKING), "%s: %s %s: %s", name,
+			    "can't open", "$(<...) input", cstrerror(errno));
+	} else if (fn == FUNSUB) {
+		int ofd1;
+		struct temp *tf = NULL;
+
+		/*
+		 * create a temporary file, open for reading and writing,
+		 * with an shf open for reading (buffered) but yet unused
+		 */
+		maketemp(ATEMP, TT_FUNSUB, &tf);
+		if (!tf->shf) {
+			errorf("can't %s temporary file %s: %s",
+			    "create", tf->tffn, cstrerror(errno));
+		}
+		/* extract shf from temporary file, unlink and free it */
+		shf = tf->shf;
+		unlink(tf->tffn);
+		afree(tf, ATEMP);
+		/* save stdout and let it point to the tempfile */
+		ofd1 = savefd(1);
+		ksh_dup2(shf_fileno(shf), 1, false);
+		/*
+		 * run tree, with output thrown into the tempfile,
+		 * in a new function block
+		 */
+		valsub(t, NULL);
+		subst_exstat = exstat & 0xFF;
+		/* rewind the tempfile and restore regular stdout */
+		lseek(shf_fileno(shf), (off_t)0, SEEK_SET);
+		restfd(1, ofd1);
+	} else if (fn == VALSUB) {
+		xp->str = valsub(t, ATEMP);
+		subst_exstat = exstat & 0xFF;
+		return (XSUB);
+	} else {
+		int ofd1, pv[2];
+
+		openpipe(pv);
+		shf = shf_fdopen(pv[0], SHF_RD, NULL);
+		ofd1 = savefd(1);
+		if (pv[1] != 1) {
+			ksh_dup2(pv[1], 1, false);
+			close(pv[1]);
+		}
+		execute(t, XXCOM | XPIPEO | XFORK, NULL);
+		restfd(1, ofd1);
+		startlast();
+		/* waitlast() */
+		xp->split = true;
+	}
+
+	xp->u.shf = shf;
+	return (XCOM);
+}
+
+/*
+ * perform #pattern and %pattern substitution in ${}
+ */
+static char *
+trimsub(char *str, char *pat, int how)
+{
+	char *end = strnul(str);
+	char *p, c;
+
+	switch (how & 0xFF) {
+	case '#':
+		/* shortest match at beginning */
+		for (p = str; p <= end; p += utf_ptradj(p)) {
+			c = *p; *p = '\0';
+			if (gmatchx(str, pat, false)) {
+				*p = c;
+				return (p);
+			}
+			*p = c;
+		}
+		break;
+	case '#'|0x80:
+		/* longest match at beginning */
+		for (p = end; p >= str; p--) {
+			c = *p; *p = '\0';
+			if (gmatchx(str, pat, false)) {
+				*p = c;
+				return (p);
+			}
+			*p = c;
+		}
+		break;
+	case '%':
+		/* shortest match at end */
+		p = end;
+		while (p >= str) {
+			if (gmatchx(p, pat, false))
+				goto trimsub_match;
+			if (UTFMODE) {
+				char *op = p;
+				while ((p-- > str) && ((*p & 0xC0) == 0x80))
+					;
+				if ((p < str) || (p + utf_ptradj(p) != op))
+					p = op - 1;
+			} else
+				--p;
+		}
+		break;
+	case '%'|0x80:
+		/* longest match at end */
+		for (p = str; p <= end; p++)
+			if (gmatchx(p, pat, false)) {
+ trimsub_match:
+				strndupx(end, str, p - str, ATEMP);
+				return (end);
+			}
+		break;
+	}
+
+	/* no match, return string */
+	return (str);
+}
+
+/*
+ * glob
+ * Name derived from V6's /etc/glob, the program that expanded filenames.
+ */
+
+/* XXX cp not const 'cause slashes are temporarily replaced with NULs... */
+static void
+glob(char *cp, XPtrV *wp, bool markdirs)
+{
+	int oldsize = XPsize(*wp);
+
+	if (glob_str(cp, wp, markdirs) == 0)
+		XPput(*wp, debunk(cp, cp, strlen(cp) + 1));
+	else
+		qsort(XPptrv(*wp) + oldsize, XPsize(*wp) - oldsize,
+		    sizeof(void *), xstrcmp);
+}
+
+#define GF_NONE		0
+#define GF_EXCHECK	BIT(0)		/* do existence check on file */
+#define GF_GLOBBED	BIT(1)		/* some globbing has been done */
+#define GF_MARKDIR	BIT(2)		/* add trailing / to directories */
+
+/*
+ * Apply file globbing to cp and store the matching files in wp. Returns
+ * the number of matches found.
+ */
+int
+glob_str(char *cp, XPtrV *wp, bool markdirs)
+{
+	int oldsize = XPsize(*wp);
+	XString xs;
+	char *xp;
+
+	Xinit(xs, xp, 256, ATEMP);
+	globit(&xs, &xp, cp, wp, markdirs ? GF_MARKDIR : GF_NONE);
+	Xfree(xs, xp);
+
+	return (XPsize(*wp) - oldsize);
+}
+
+static void
+globit(XString *xs,	/* dest string */
+    char **xpp,		/* ptr to dest end */
+    char *sp,		/* source path */
+    XPtrV *wp,		/* output list */
+    int check)		/* GF_* flags */
+{
+	char *np;		/* next source component */
+	char *xp = *xpp;
+	char *se;
+	char odirsep;
+
+	/* This to allow long expansions to be interrupted */
+	intrcheck();
+
+	if (sp == NULL) {
+		/* end of source path */
+		/*
+		 * We only need to check if the file exists if a pattern
+		 * is followed by a non-pattern (eg, foo*x/bar; no check
+		 * is needed for foo* since the match must exist) or if
+		 * any patterns were expanded and the markdirs option is set.
+		 * Symlinks make things a bit tricky...
+		 */
+		if ((check & GF_EXCHECK) ||
+		    ((check & GF_MARKDIR) && (check & GF_GLOBBED))) {
+#define stat_check()	(stat_done ? stat_done : (stat_done = \
+			    stat(Xstring(*xs, xp), &statb) < 0 ? -1 : 1))
+			struct stat lstatb, statb;
+			/* -1: failed, 1 ok, 0 not yet done */
+			int stat_done = 0;
+
+			if (mksh_lstat(Xstring(*xs, xp), &lstatb) < 0)
+				return;
+			/*
+			 * special case for systems which strip trailing
+			 * slashes from regular files (eg, /etc/passwd/).
+			 * SunOS 4.1.3 does this...
+			 */
+			if ((check & GF_EXCHECK) && xp > Xstring(*xs, xp) &&
+			    xp[-1] == '/' && !S_ISDIR(lstatb.st_mode) &&
+			    (!S_ISLNK(lstatb.st_mode) ||
+			    stat_check() < 0 || !S_ISDIR(statb.st_mode)))
+				return;
+			/*
+			 * Possibly tack on a trailing / if there isn't already
+			 * one and if the file is a directory or a symlink to a
+			 * directory
+			 */
+			if (((check & GF_MARKDIR) && (check & GF_GLOBBED)) &&
+			    xp > Xstring(*xs, xp) && xp[-1] != '/' &&
+			    (S_ISDIR(lstatb.st_mode) ||
+			    (S_ISLNK(lstatb.st_mode) && stat_check() > 0 &&
+			    S_ISDIR(statb.st_mode)))) {
+				*xp++ = '/';
+				*xp = '\0';
+			}
+		}
+		strndupx(np, Xstring(*xs, xp), Xlength(*xs, xp), ATEMP);
+		XPput(*wp, np);
+		return;
+	}
+
+	if (xp > Xstring(*xs, xp))
+		*xp++ = '/';
+	while (*sp == '/') {
+		Xcheck(*xs, xp);
+		*xp++ = *sp++;
+	}
+	np = strchr(sp, '/');
+	if (np != NULL) {
+		se = np;
+		/* don't assume '/', can be multiple kinds */
+		odirsep = *np;
+		*np++ = '\0';
+	} else {
+		odirsep = '\0'; /* keep gcc quiet */
+		se = sp + strlen(sp);
+	}
+
+
+	/*
+	 * Check if sp needs globbing - done to avoid pattern checks for strings
+	 * containing MAGIC characters, open [s without the matching close ],
+	 * etc. (otherwise opendir() will be called which may fail because the
+	 * directory isn't readable - if no globbing is needed, only execute
+	 * permission should be required (as per POSIX)).
+	 */
+	if (!has_globbing(sp, se)) {
+		XcheckN(*xs, xp, se - sp + 1);
+		debunk(xp, sp, Xnleft(*xs, xp));
+		xp += strlen(xp);
+		*xpp = xp;
+		globit(xs, xpp, np, wp, check);
+	} else {
+		DIR *dirp;
+		struct dirent *d;
+		char *name;
+		size_t len, prefix_len;
+
+		/* xp = *xpp;	copy_non_glob() may have re-alloc'd xs */
+		*xp = '\0';
+		prefix_len = Xlength(*xs, xp);
+		dirp = opendir(prefix_len ? Xstring(*xs, xp) : ".");
+		if (dirp == NULL)
+			goto Nodir;
+		while ((d = readdir(dirp)) != NULL) {
+			name = d->d_name;
+			if (name[0] == '.' &&
+			    (name[1] == 0 || (name[1] == '.' && name[2] == 0)))
+				/* always ignore . and .. */
+				continue;
+			if ((*name == '.' && *sp != '.') ||
+			    !gmatchx(name, sp, true))
+				continue;
+
+			len = strlen(d->d_name) + 1;
+			XcheckN(*xs, xp, len);
+			memcpy(xp, name, len);
+			*xpp = xp + len - 1;
+			globit(xs, xpp, np, wp,
+				(check & GF_MARKDIR) | GF_GLOBBED
+				| (np ? GF_EXCHECK : GF_NONE));
+			xp = Xstring(*xs, xp) + prefix_len;
+		}
+		closedir(dirp);
+ Nodir:
+		;
+	}
+
+	if (np != NULL)
+		*--np = odirsep;
+}
+
+/* remove MAGIC from string */
+char *
+debunk(char *dp, const char *sp, size_t dlen)
+{
+	char *d;
+	const char *s;
+
+	if ((s = cstrchr(sp, MAGIC))) {
+		if (s - sp >= (ssize_t)dlen)
+			return (dp);
+		memmove(dp, sp, s - sp);
+		for (d = dp + (s - sp); *s && (d - dp < (ssize_t)dlen); s++)
+			if (!ISMAGIC(*s) || !(*++s & 0x80) ||
+			    !vstrchr("*+?@! ", *s & 0x7f))
+				*d++ = *s;
+			else {
+				/* extended pattern operators: *+?@! */
+				if ((*s & 0x7f) != ' ')
+					*d++ = *s & 0x7f;
+				if (d - dp < (ssize_t)dlen)
+					*d++ = '(';
+			}
+		*d = '\0';
+	} else if (dp != sp)
+		strlcpy(dp, sp, dlen);
+	return (dp);
+}
+
+/*
+ * Check if p is an unquoted name, possibly followed by a / or :. If so
+ * puts the expanded version in *dcp,dp and returns a pointer in p just
+ * past the name, otherwise returns 0.
+ */
+static const char *
+maybe_expand_tilde(const char *p, XString *dsp, char **dpp, int isassign)
+{
+	XString ts;
+	char *dp = *dpp;
+	char *tp;
+	const char *r;
+
+	Xinit(ts, tp, 16, ATEMP);
+	/* : only for DOASNTILDE form */
+	while (p[0] == CHAR && p[1] != '/' && (!isassign || p[1] != ':'))
+	{
+		Xcheck(ts, tp);
+		*tp++ = p[1];
+		p += 2;
+	}
+	*tp = '\0';
+	r = (p[0] == EOS || p[0] == CHAR || p[0] == CSUBST) ?
+	    tilde(Xstring(ts, tp)) : NULL;
+	Xfree(ts, tp);
+	if (r) {
+		while (*r) {
+			Xcheck(*dsp, dp);
+			if (ISMAGIC(*r))
+				*dp++ = MAGIC;
+			*dp++ = *r++;
+		}
+		*dpp = dp;
+		r = p;
+	}
+	return (r);
+}
+
+/*
+ * tilde expansion
+ *
+ * based on a version by Arnold Robbins
+ */
+
+char *
+tilde(char *cp)
+{
+	char *dp = null;
+
+	if (cp[0] == '\0')
+		dp = str_val(global("HOME"));
+	else if (cp[0] == '+' && cp[1] == '\0')
+		dp = str_val(global("PWD"));
+	else if (cp[0] == '-' && cp[1] == '\0')
+		dp = str_val(global("OLDPWD"));
+#ifndef MKSH_NOPWNAM
+	else
+		dp = homedir(cp);
+#endif
+	/* If HOME, PWD or OLDPWD are not set, don't expand ~ */
+	return (dp == null ? NULL : dp);
+}
+
+#ifndef MKSH_NOPWNAM
+/*
+ * map userid to user's home directory.
+ * note that 4.3's getpw adds more than 6K to the shell,
+ * and the YP version probably adds much more.
+ * we might consider our own version of getpwnam() to keep the size down.
+ */
+static char *
+homedir(char *name)
+{
+	struct tbl *ap;
+
+	ap = ktenter(&homedirs, name, hash(name));
+	if (!(ap->flag & ISSET)) {
+		struct passwd *pw;
+
+		pw = getpwnam(name);
+		if (pw == NULL)
+			return (NULL);
+		strdupx(ap->val.s, pw->pw_dir, APERM);
+		ap->flag |= DEFINED|ISSET|ALLOC;
+	}
+	return (ap->val.s);
+}
+#endif
+
+static void
+alt_expand(XPtrV *wp, char *start, char *exp_start, char *end, int fdo)
+{
+	int count = 0;
+	char *brace_start, *brace_end, *comma = NULL;
+	char *field_start;
+	char *p;
+
+	/* search for open brace */
+	for (p = exp_start; (p = strchr(p, MAGIC)) && p[1] != '{' /*}*/; p += 2)
+		;
+	brace_start = p;
+
+	/* find matching close brace, if any */
+	if (p) {
+		comma = NULL;
+		count = 1;
+		for (p += 2; *p && count; p++) {
+			if (ISMAGIC(*p)) {
+				if (*++p == '{' /*}*/)
+					count++;
+				else if (*p == /*{*/ '}')
+					--count;
+				else if (*p == ',' && count == 1)
+					comma = p;
+			}
+		}
+	}
+	/* no valid expansions... */
+	if (!p || count != 0) {
+		/*
+		 * Note that given a{{b,c} we do not expand anything (this is
+		 * what AT&T ksh does. This may be changed to do the {b,c}
+		 * expansion. }
+		 */
+		if (fdo & DOGLOB)
+			glob(start, wp, tobool(fdo & DOMARKDIRS));
+		else
+			XPput(*wp, debunk(start, start, end - start));
+		return;
+	}
+	brace_end = p;
+	if (!comma) {
+		alt_expand(wp, start, brace_end, end, fdo);
+		return;
+	}
+
+	/* expand expression */
+	field_start = brace_start + 2;
+	count = 1;
+	for (p = brace_start + 2; p != brace_end; p++) {
+		if (ISMAGIC(*p)) {
+			if (*++p == '{' /*}*/)
+				count++;
+			else if ((*p == /*{*/ '}' && --count == 0) ||
+			    (*p == ',' && count == 1)) {
+				char *news;
+				int l1, l2, l3;
+
+				/*
+				 * addition safe since these operate on
+				 * one string (separate substrings)
+				 */
+				l1 = brace_start - start;
+				l2 = (p - 1) - field_start;
+				l3 = end - brace_end;
+				news = alloc(l1 + l2 + l3 + 1, ATEMP);
+				memcpy(news, start, l1);
+				memcpy(news + l1, field_start, l2);
+				memcpy(news + l1 + l2, brace_end, l3);
+				news[l1 + l2 + l3] = '\0';
+				alt_expand(wp, news, news + l1,
+				    news + l1 + l2 + l3, fdo);
+				field_start = p + 1;
+			}
+		}
+	}
+	return;
+}
+
+/* helper function due to setjmp/longjmp woes */
+static char *
+valsub(struct op *t, Area *ap)
+{
+	char * volatile cp = NULL;
+	struct tbl * volatile vp = NULL;
+
+	newenv(E_FUNC);
+	newblock();
+	if (ap)
+		vp = local("REPLY", false);
+	if (!kshsetjmp(e->jbuf))
+		execute(t, XXCOM | XERROK, NULL);
+	if (vp)
+		strdupx(cp, str_val(vp), ap);
+	quitenv(NULL);
+
+	return (cp);
+}

Deleted: vendor/MirOS/mksh/R50/exec.c
===================================================================
--- vendor/MirOS/mksh/dist/exec.c	2014-06-30 23:58:12 UTC (rev 6706)
+++ vendor/MirOS/mksh/R50/exec.c	2014-07-01 12:01:17 UTC (rev 6708)
@@ -1,1735 +0,0 @@
-/*	$OpenBSD: exec.c,v 1.50 2013/06/10 21:09:27 millert Exp $	*/
-
-/*-
- * Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
- *		 2011, 2012, 2013
- *	Thorsten Glaser <tg at mirbsd.org>
- *
- * Provided that these terms and disclaimer and all copyright notices
- * are retained or reproduced in an accompanying document, permission
- * is granted to deal in this work without restriction, including un-
- * limited rights to use, publicly perform, distribute, sell, modify,
- * merge, give away, or sublicence.
- *
- * This work is provided "AS IS" and WITHOUT WARRANTY of any kind, to
- * the utmost extent permitted by applicable law, neither express nor
- * implied; without malicious intent or gross negligence. In no event
- * may a licensor, author or contributor be held liable for indirect,
- * direct, other damage, loss, or other issues arising in any way out
- * of dealing in the work, even if advised of the possibility of such
- * damage or existence of a defect, except proven that it results out
- * of said person's immediate fault when using the work as intended.
- */
-
-#include "sh.h"
-
-__RCSID("$MirOS: src/bin/mksh/exec.c,v 1.125 2013/07/21 20:44:44 tg Exp $");
-
-#ifndef MKSH_DEFAULT_EXECSHELL
-#define MKSH_DEFAULT_EXECSHELL	"/bin/sh"
-#endif
-
-static int comexec(struct op *, struct tbl * volatile, const char **,
-    int volatile, volatile int *);
-static void scriptexec(struct op *, const char **) MKSH_A_NORETURN;
-static int call_builtin(struct tbl *, const char **, const char *);
-static int iosetup(struct ioword *, struct tbl *);
-static int herein(struct ioword *, char **);
-static const char *do_selectargs(const char **, bool);
-static Test_op dbteste_isa(Test_env *, Test_meta);
-static const char *dbteste_getopnd(Test_env *, Test_op, bool);
-static void dbteste_error(Test_env *, int, const char *);
-static int search_access(const char *, int);
-/* XXX: horrible kludge to fit within the framework */
-static char *plain_fmt_entry(char *, size_t, unsigned int, const void *);
-static char *select_fmt_entry(char *, size_t, unsigned int, const void *);
-
-/*
- * execute command tree
- */
-int
-execute(struct op * volatile t,
-    /* if XEXEC don't fork */
-    volatile int flags,
-    volatile int * volatile xerrok)
-{
-	int i;
-	volatile int rv = 0, dummy = 0;
-	int pv[2];
-	const char ** volatile ap = NULL;
-	char ** volatile up;
-	const char *s, *ccp;
-	struct ioword **iowp;
-	struct tbl *tp = NULL;
-	char *cp;
-
-	if (t == NULL)
-		return (0);
-
-	/* Caller doesn't care if XERROK should propagate. */
-	if (xerrok == NULL)
-		xerrok = &dummy;
-
-	if ((flags&XFORK) && !(flags&XEXEC) && t->type != TPIPE)
-		/* run in sub-process */
-		return (exchild(t, flags & ~XTIME, xerrok, -1));
-
-	newenv(E_EXEC);
-	if (trap)
-		runtraps(0);
-
-	/* we want to run an executable, do some variance checks */
-	if (t->type == TCOM) {
-		/* check if this is 'var=<<EOF' */
-		if (
-		    /* we have zero arguments, i.e. no programme to run */
-		    t->args[0] == NULL &&
-		    /* we have exactly one variable assignment */
-		    t->vars[0] != NULL && t->vars[1] == NULL &&
-		    /* we have exactly one I/O redirection */
-		    t->ioact != NULL && t->ioact[0] != NULL &&
-		    t->ioact[1] == NULL &&
-		    /* of type "here document" (or "here string") */
-		    (t->ioact[0]->flag & IOTYPE) == IOHERE &&
-		    /* the variable assignment begins with a valid varname */
-		    (ccp = skip_wdvarname(t->vars[0], true)) != t->vars[0] &&
-		    /* and has no right-hand side (i.e. "varname=") */
-		    ccp[0] == CHAR && ccp[1] == '=' && ccp[2] == EOS &&
-		    /* plus we can have a here document content */
-		    herein(t->ioact[0], &cp) == 0 && cp && *cp) {
-			char *sp = cp, *dp;
-			size_t n = ccp - t->vars[0] + 2, z;
-
-			/* drop redirection (will be garbage collected) */
-			t->ioact = NULL;
-
-			/* set variable to its expanded value */
-			z = strlen(cp) + 1;
-			if (notoktomul(z, 2) || notoktoadd(z * 2, n))
-				internal_errorf(Toomem, (size_t)-1);
-			dp = alloc(z * 2 + n, ATEMP);
-			memcpy(dp, t->vars[0], n);
-			t->vars[0] = dp;
-			dp += n;
-			while (*sp) {
-				*dp++ = QCHAR;
-				*dp++ = *sp++;
-			}
-			*dp = EOS;
-			/* free the expanded value */
-			afree(cp, APERM);
-		}
-
-		/*
-		 * Clear subst_exstat before argument expansion. Used by
-		 * null commands (see comexec() and c_eval()) and by c_set().
-		 */
-		subst_exstat = 0;
-
-		/* for $LINENO */
-		current_lineno = t->lineno;
-
-		/*
-		 * POSIX says expand command words first, then redirections,
-		 * and assignments last..
-		 */
-		up = eval(t->args, t->u.evalflags | DOBLANK | DOGLOB | DOTILDE);
-		if (flags & XTIME)
-			/* Allow option parsing (bizarre, but POSIX) */
-			timex_hook(t, &up);
-		ap = (const char **)up;
-		if (ap[0])
-			tp = findcom(ap[0], FC_BI|FC_FUNC);
-	}
-	flags &= ~XTIME;
-
-	if (t->ioact != NULL || t->type == TPIPE || t->type == TCOPROC) {
-		e->savefd = alloc2(NUFILE, sizeof(short), ATEMP);
-		/* initialise to not redirected */
-		memset(e->savefd, 0, NUFILE * sizeof(short));
-	}
-
-	/* mark for replacement later (unless TPIPE) */
-	vp_pipest->flag |= INT_L;
-
-	/* do redirection, to be restored in quitenv() */
-	if (t->ioact != NULL)
-		for (iowp = t->ioact; *iowp != NULL; iowp++) {
-			if (iosetup(*iowp, tp) < 0) {
-				exstat = rv = 1;
-				/*
-				 * Redirection failures for special commands
-				 * cause (non-interactive) shell to exit.
-				 */
-				if (tp && tp->type == CSHELL &&
-				    (tp->flag & SPEC_BI))
-					errorfz();
-				/* Deal with FERREXIT, quitenv(), etc. */
-				goto Break;
-			}
-		}
-
-	switch (t->type) {
-	case TCOM:
-		rv = comexec(t, tp, (const char **)ap, flags, xerrok);
-		break;
-
-	case TPAREN:
-		rv = execute(t->left, flags | XFORK, xerrok);
-		break;
-
-	case TPIPE:
-		flags |= XFORK;
-		flags &= ~XEXEC;
-		e->savefd[0] = savefd(0);
-		e->savefd[1] = savefd(1);
-		while (t->type == TPIPE) {
-			openpipe(pv);
-			/* stdout of curr */
-			ksh_dup2(pv[1], 1, false);
-			/**
-			 * Let exchild() close pv[0] in child
-			 * (if this isn't done, commands like
-			 *	(: ; cat /etc/termcap) | sleep 1
-			 * will hang forever).
-			 */
-			exchild(t->left, flags | XPIPEO | XCCLOSE,
-			    NULL, pv[0]);
-			/* stdin of next */
-			ksh_dup2(pv[0], 0, false);
-			closepipe(pv);
-			flags |= XPIPEI;
-			t = t->right;
-		}
-		/* stdout of last */
-		restfd(1, e->savefd[1]);
-		/* no need to re-restore this */
-		e->savefd[1] = 0;
-		/* Let exchild() close 0 in parent, after fork, before wait */
-		i = exchild(t, flags | XPCLOSE | XPIPEST, xerrok, 0);
-		if (!(flags&XBGND) && !(flags&XXCOM))
-			rv = i;
-		break;
-
-	case TLIST:
-		while (t->type == TLIST) {
-			execute(t->left, flags & XERROK, NULL);
-			t = t->right;
-		}
-		rv = execute(t, flags & XERROK, xerrok);
-		break;
-
-	case TCOPROC: {
-#ifndef MKSH_NOPROSPECTOFWORK
-		sigset_t omask;
-
-		/*
-		 * Block sigchild as we are using things changed in the
-		 * signal handler
-		 */
-		sigprocmask(SIG_BLOCK, &sm_sigchld, &omask);
-		e->type = E_ERRH;
-		if ((i = kshsetjmp(e->jbuf))) {
-			sigprocmask(SIG_SETMASK, &omask, NULL);
-			quitenv(NULL);
-			unwind(i);
-			/* NOTREACHED */
-		}
-#endif
-		/* Already have a (live) co-process? */
-		if (coproc.job && coproc.write >= 0)
-			errorf("coprocess already exists");
-
-		/* Can we re-use the existing co-process pipe? */
-		coproc_cleanup(true);
-
-		/* do this before opening pipes, in case these fail */
-		e->savefd[0] = savefd(0);
-		e->savefd[1] = savefd(1);
-
-		openpipe(pv);
-		if (pv[0] != 0) {
-			ksh_dup2(pv[0], 0, false);
-			close(pv[0]);
-		}
-		coproc.write = pv[1];
-		coproc.job = NULL;
-
-		if (coproc.readw >= 0)
-			ksh_dup2(coproc.readw, 1, false);
-		else {
-			openpipe(pv);
-			coproc.read = pv[0];
-			ksh_dup2(pv[1], 1, false);
-			/* closed before first read */
-			coproc.readw = pv[1];
-			coproc.njobs = 0;
-			/* create new coprocess id */
-			++coproc.id;
-		}
-#ifndef MKSH_NOPROSPECTOFWORK
-		sigprocmask(SIG_SETMASK, &omask, NULL);
-		/* no more need for error handler */
-		e->type = E_EXEC;
-#endif
-
-		/*
-		 * exchild() closes coproc.* in child after fork,
-		 * will also increment coproc.njobs when the
-		 * job is actually created.
-		 */
-		flags &= ~XEXEC;
-		exchild(t->left, flags | XBGND | XFORK | XCOPROC | XCCLOSE,
-		    NULL, coproc.readw);
-		break;
-	}
-
-	case TASYNC:
-		/*
-		 * XXX non-optimal, I think - "(foo &)", forks for (),
-		 * forks again for async... parent should optimise
-		 * this to "foo &"...
-		 */
-		rv = execute(t->left, (flags&~XEXEC)|XBGND|XFORK, xerrok);
-		break;
-
-	case TOR:
-	case TAND:
-		rv = execute(t->left, XERROK, xerrok);
-		if ((rv == 0) == (t->type == TAND))
-			rv = execute(t->right, flags & XERROK, xerrok);
-		else {
-			flags |= XERROK;
-			if (xerrok)
-				*xerrok = 1;
-		}
-		break;
-
-	case TBANG:
-		rv = !execute(t->right, XERROK, xerrok);
-		flags |= XERROK;
-		if (xerrok)
-			*xerrok = 1;
-		break;
-
-	case TDBRACKET: {
-		Test_env te;
-
-		te.flags = TEF_DBRACKET;
-		te.pos.wp = t->args;
-		te.isa = dbteste_isa;
-		te.getopnd = dbteste_getopnd;
-		te.eval = test_eval;
-		te.error = dbteste_error;
-
-		rv = test_parse(&te);
-		break;
-	}
-
-	case TFOR:
-	case TSELECT: {
-		volatile bool is_first = true;
-
-		ap = (t->vars == NULL) ? e->loc->argv + 1 :
-		    (const char **)eval((const char **)t->vars,
-		    DOBLANK | DOGLOB | DOTILDE);
-		e->type = E_LOOP;
-		while ((i = kshsetjmp(e->jbuf))) {
-			if ((e->flags&EF_BRKCONT_PASS) ||
-			    (i != LBREAK && i != LCONTIN)) {
-				quitenv(NULL);
-				unwind(i);
-			} else if (i == LBREAK) {
-				rv = 0;
-				goto Break;
-			}
-		}
-		/* in case of a continue */
-		rv = 0;
-		if (t->type == TFOR) {
-			while (*ap != NULL) {
-				setstr(global(t->str), *ap++, KSH_UNWIND_ERROR);
-				rv = execute(t->left, flags & XERROK, xerrok);
-			}
-		} else {
-			/* TSELECT */
-			for (;;) {
-				if (!(ccp = do_selectargs(ap, is_first))) {
-					rv = 1;
-					break;
-				}
-				is_first = false;
-				setstr(global(t->str), ccp, KSH_UNWIND_ERROR);
-				execute(t->left, flags & XERROK, xerrok);
-			}
-		}
-		break;
-	}
-
-	case TWHILE:
-	case TUNTIL:
-		e->type = E_LOOP;
-		while ((i = kshsetjmp(e->jbuf))) {
-			if ((e->flags&EF_BRKCONT_PASS) ||
-			    (i != LBREAK && i != LCONTIN)) {
-				quitenv(NULL);
-				unwind(i);
-			} else if (i == LBREAK) {
-				rv = 0;
-				goto Break;
-			}
-		}
-		/* in case of a continue */
-		rv = 0;
-		while ((execute(t->left, XERROK, NULL) == 0) ==
-		    (t->type == TWHILE))
-			rv = execute(t->right, flags & XERROK, xerrok);
-		break;
-
-	case TIF:
-	case TELIF:
-		if (t->right == NULL)
-			/* should be error */
-			break;
-		rv = execute(t->left, XERROK, NULL) == 0 ?
-		    execute(t->right->left, flags & XERROK, xerrok) :
-		    execute(t->right->right, flags & XERROK, xerrok);
-		break;
-
-	case TCASE:
-		i = 0;
-		ccp = evalstr(t->str, DOTILDE);
-		for (t = t->left; t != NULL && t->type == TPAT; t = t->right) {
-			for (ap = (const char **)t->vars; *ap; ap++) {
-				if (i || ((s = evalstr(*ap, DOTILDE|DOPAT)) &&
-				    gmatchx(ccp, s, false))) {
-					rv = execute(t->left, flags & XERROK,
-					    xerrok);
-					i = 0;
-					switch (t->u.charflag) {
-					case '&':
-						i = 1;
-						/* FALLTHROUGH */
-					case '|':
-						goto TCASE_next;
-					}
-					goto TCASE_out;
-				}
-			}
-			i = 0;
- TCASE_next:
-			/* empty */;
-		}
- TCASE_out:
-		break;
-
-	case TBRACE:
-		rv = execute(t->left, flags & XERROK, xerrok);
-		break;
-
-	case TFUNCT:
-		rv = define(t->str, t);
-		break;
-
-	case TTIME:
-		/*
-		 * Clear XEXEC so nested execute() call doesn't exit
-		 * (allows "ls -l | time grep foo").
-		 */
-		rv = timex(t, flags & ~XEXEC, xerrok);
-		break;
-
-	case TEXEC:
-		/* an eval'd TCOM */
-		s = t->args[0];
-		up = makenv();
-		restoresigs();
-		cleanup_proc_env();
-		{
-			union mksh_ccphack cargs;
-
-			cargs.ro = t->args;
-			execve(t->str, cargs.rw, up);
-			rv = errno;
-		}
-		if (rv == ENOEXEC)
-			scriptexec(t, (const char **)up);
-		else
-			errorf("%s: %s", s, cstrerror(rv));
-	}
- Break:
-	exstat = rv & 0xFF;
-	if (vp_pipest->flag & INT_L) {
-		unset(vp_pipest, 1);
-		vp_pipest->flag = DEFINED | ISSET | INTEGER | RDONLY |
-		    ARRAY | INT_U;
-		vp_pipest->val.i = rv;
-	}
-
-	/* restores IO */
-	quitenv(NULL);
-	if ((flags&XEXEC))
-		/* exit child */
-		unwind(LEXIT);
-	if (rv != 0 && !(flags & XERROK) &&
-	    (xerrok == NULL || !*xerrok)) {
-		if (Flag(FERREXIT) & 0x80) {
-			/* inside eval */
-			Flag(FERREXIT) = 0;
-		} else {
-			trapsig(ksh_SIGERR);
-			if (Flag(FERREXIT))
-				unwind(LERROR);
-		}
-	}
-	return (rv);
-}
-
-/*
- * execute simple command
- */
-
-static int
-comexec(struct op *t, struct tbl * volatile tp, const char **ap,
-    volatile int flags, volatile int *xerrok)
-{
-	int i;
-	volatile int rv = 0;
-	const char *cp;
-	const char **lastp;
-	/* Must be static (XXX but why?) */
-	static struct op texec;
-	int type_flags;
-	bool keepasn_ok;
-	int fcflags = FC_BI|FC_FUNC|FC_PATH;
-	bool bourne_function_call = false;
-	struct block *l_expand, *l_assign;
-
-	/*
-	 * snag the last argument for $_ XXX not the same as AT&T ksh,
-	 * which only seems to set $_ after a newline (but not in
-	 * functions/dot scripts, but in interactive and script) -
-	 * perhaps save last arg here and set it in shell()?.
-	 */
-	if (Flag(FTALKING) && *(lastp = ap)) {
-		while (*++lastp)
-			;
-		/* setstr() can't fail here */
-		setstr(typeset("_", LOCAL, 0, INTEGER, 0), *--lastp,
-		    KSH_RETURN_ERROR);
-	}
-
-	/**
-	 * Deal with the shell builtins builtin, exec and command since
-	 * they can be followed by other commands. This must be done before
-	 * we know if we should create a local block which must be done
-	 * before we can do a path search (in case the assignments change
-	 * PATH).
-	 * Odd cases:
-	 *	FOO=bar exec >/dev/null		FOO is kept but not exported
-	 *	FOO=bar exec foobar		FOO is exported
-	 *	FOO=bar command exec >/dev/null	FOO is neither kept nor exported
-	 *	FOO=bar command			FOO is neither kept nor exported
-	 *	PATH=... foobar			use new PATH in foobar search
-	 */
-	keepasn_ok = true;
-	while (tp && tp->type == CSHELL) {
-		/* undo effects of command */
-		fcflags = FC_BI|FC_FUNC|FC_PATH;
-		if (tp->val.f == c_builtin) {
-			if ((cp = *++ap) == NULL ||
-			    (!strcmp(cp, "--") && (cp = *++ap) == NULL)) {
-				tp = NULL;
-				break;
-			}
-			if ((tp = findcom(cp, FC_BI)) == NULL)
-				errorf("%s: %s: %s", Tbuiltin, cp, "not a builtin");
-			continue;
-		} else if (tp->val.f == c_exec) {
-			if (ap[1] == NULL)
-				break;
-			ap++;
-			flags |= XEXEC;
-		} else if (tp->val.f == c_command) {
-			int optc, saw_p = 0;
-
-			/*
-			 * Ugly dealing with options in two places (here
-			 * and in c_command(), but such is life)
-			 */
-			ksh_getopt_reset(&builtin_opt, 0);
-			while ((optc = ksh_getopt(ap, &builtin_opt, ":p")) == 'p')
-				saw_p = 1;
-			if (optc != EOF)
-				/* command -vV or something */
-				break;
-			/* don't look for functions */
-			fcflags = FC_BI|FC_PATH;
-			if (saw_p) {
-				if (Flag(FRESTRICTED)) {
-					warningf(true, "%s: %s",
-					    "command -p", "restricted");
-					rv = 1;
-					goto Leave;
-				}
-				fcflags |= FC_DEFPATH;
-			}
-			ap += builtin_opt.optind;
-			/*
-			 * POSIX says special builtins lose their status
-			 * if accessed using command.
-			 */
-			keepasn_ok = false;
-			if (!ap[0]) {
-				/* ensure command with no args exits with 0 */
-				subst_exstat = 0;
-				break;
-			}
-#ifndef MKSH_NO_EXTERNAL_CAT
-		} else if (tp->val.f == c_cat) {
-			/*
-			 * if we have any flags, do not use the builtin
-			 * in theory, we could allow -u, but that would
-			 * mean to use ksh_getopt here and possibly ad-
-			 * ded complexity and more code and isn't worth
-			 * additional hassle (and the builtin must call
-			 * ksh_getopt already but can't come back here)
-			 */
-			if (ap[1] && ap[1][0] == '-' && ap[1][1] != '\0' &&
-			    /* argument, begins with -, is not - or -- */
-			    (ap[1][1] != '-' || ap[1][2] != '\0'))
-				/* don't look for builtins or functions */
-				fcflags = FC_PATH;
-			else
-				/* go on, use the builtin */
-				break;
-#endif
-#if !defined(MKSH_SMALL)
-		} else if (tp->val.f == c_trap) {
-			t->u.evalflags &= ~DOTCOMEXEC;
-			break;
-#endif
-		} else
-			break;
-		tp = findcom(ap[0], fcflags & (FC_BI|FC_FUNC));
-	}
-#if !defined(MKSH_SMALL)
-	if (t->u.evalflags & DOTCOMEXEC)
-		flags |= XEXEC;
-#endif
-	l_expand = e->loc;
-	if (keepasn_ok && (!ap[0] || (tp && (tp->flag & KEEPASN))))
-		type_flags = 0;
-	else {
-		/* create new variable/function block */
-		newblock();
-		/* ksh functions don't keep assignments, POSIX functions do. */
-		if (keepasn_ok && tp && tp->type == CFUNC &&
-		    !(tp->flag & FKSH)) {
-			bourne_function_call = true;
-			type_flags = EXPORT;
-		} else
-			type_flags = LOCAL|LOCAL_COPY|EXPORT;
-	}
-	l_assign = e->loc;
-	if (Flag(FEXPORT))
-		type_flags |= EXPORT;
-	if (Flag(FXTRACE))
-		change_xtrace(2, false);
-	for (i = 0; t->vars[i]; i++) {
-		/* do NOT lookup in the new var/fn block just created */
-		e->loc = l_expand;
-		cp = evalstr(t->vars[i], DOASNTILDE);
-		e->loc = l_assign;
-		if (Flag(FXTRACE)) {
-			const char *ccp;
-
-			ccp = skip_varname(cp, true);
-			if (*ccp == '+')
-				++ccp;
-			if (*ccp == '=')
-				++ccp;
-			shf_write(cp, ccp - cp, shl_xtrace);
-			print_value_quoted(shl_xtrace, ccp);
-			shf_putc(' ', shl_xtrace);
-		}
-		/* but assign in there as usual */
-		typeset(cp, type_flags, 0, 0, 0);
-		if (bourne_function_call && !(type_flags & EXPORT))
-			typeset(cp, LOCAL|LOCAL_COPY|EXPORT, 0, 0, 0);
-	}
-
-	if (Flag(FXTRACE)) {
-		change_xtrace(2, false);
-		if (ap[rv = 0]) {
- xtrace_ap_loop:
-			print_value_quoted(shl_xtrace, ap[rv]);
-			if (ap[++rv]) {
-				shf_putc(' ', shl_xtrace);
-				goto xtrace_ap_loop;
-			}
-		}
-		change_xtrace(1, false);
-	}
-
-	if ((cp = *ap) == NULL) {
-		rv = subst_exstat;
-		goto Leave;
-	} else if (!tp) {
-		if (Flag(FRESTRICTED) && vstrchr(cp, '/')) {
-			warningf(true, "%s: %s", cp, "restricted");
-			rv = 1;
-			goto Leave;
-		}
-		tp = findcom(cp, fcflags);
-	}
-
-	switch (tp->type) {
-
-	/* shell built-in */
-	case CSHELL:
-		rv = call_builtin(tp, (const char **)ap, null);
-		if (!keepasn_ok && tp->val.f == c_shift) {
-			l_expand->argc = l_assign->argc;
-			l_expand->argv = l_assign->argv;
-		}
-		break;
-
-	/* function call */
-	case CFUNC: {
-		volatile unsigned char old_xflag;
-		volatile uint32_t old_inuse;
-		const char * volatile old_kshname;
-
-		if (!(tp->flag & ISSET)) {
-			struct tbl *ftp;
-
-			if (!tp->u.fpath) {
-				rv = (tp->u2.errnov == ENOENT) ? 127 : 126;
-				warningf(true, "%s: %s %s: %s", cp,
-				    "can't find", "function definition file",
-				    cstrerror(tp->u2.errnov));
-				break;
-			}
-			if (include(tp->u.fpath, 0, NULL, false) < 0) {
-				warningf(true, "%s: %s %s %s: %s", cp,
-				    "can't open", "function definition file",
-				    tp->u.fpath, cstrerror(errno));
-				rv = 127;
-				break;
-			}
-			if (!(ftp = findfunc(cp, hash(cp), false)) ||
-			    !(ftp->flag & ISSET)) {
-				warningf(true, "%s: %s %s", cp,
-				    "function not defined by", tp->u.fpath);
-				rv = 127;
-				break;
-			}
-			tp = ftp;
-		}
-
-		/*
-		 * ksh functions set $0 to function name, POSIX
-		 * functions leave $0 unchanged.
-		 */
-		old_kshname = kshname;
-		if (tp->flag & FKSH)
-			kshname = ap[0];
-		else
-			ap[0] = kshname;
-		e->loc->argv = ap;
-		for (i = 0; *ap++ != NULL; i++)
-			;
-		e->loc->argc = i - 1;
-		/*
-		 * ksh-style functions handle getopts sanely,
-		 * Bourne/POSIX functions are insane...
-		 */
-		if (tp->flag & FKSH) {
-			e->loc->flags |= BF_DOGETOPTS;
-			e->loc->getopts_state = user_opt;
-			getopts_reset(1);
-		}
-
-		old_xflag = Flag(FXTRACE) ? 1 : 0;
-		change_xtrace((Flag(FXTRACEREC) ? old_xflag : 0) |
-		    ((tp->flag & TRACE) ? 1 : 0), false);
-		old_inuse = tp->flag & FINUSE;
-		tp->flag |= FINUSE;
-
-		e->type = E_FUNC;
-		if (!(i = kshsetjmp(e->jbuf))) {
-			execute(tp->val.t, flags & XERROK, NULL);
-			i = LRETURN;
-		}
-
-		kshname = old_kshname;
-		change_xtrace(old_xflag, false);
-		tp->flag = (tp->flag & ~FINUSE) | old_inuse;
-
-		/*
-		 * Were we deleted while executing? If so, free the
-		 * execution tree. TODO: Unfortunately, the table entry
-		 * is never re-used until the lookup table is expanded.
-		 */
-		if ((tp->flag & (FDELETE|FINUSE)) == FDELETE) {
-			if (tp->flag & ALLOC) {
-				tp->flag &= ~ALLOC;
-				tfree(tp->val.t, tp->areap);
-			}
-			tp->flag = 0;
-		}
-		switch (i) {
-		case LRETURN:
-		case LERROR:
-			rv = exstat & 0xFF;
-			break;
-		case LINTR:
-		case LEXIT:
-		case LLEAVE:
-		case LSHELL:
-			quitenv(NULL);
-			unwind(i);
-			/* NOTREACHED */
-		default:
-			quitenv(NULL);
-			internal_errorf("%s %d", "CFUNC", i);
-		}
-		break;
-	}
-
-	/* executable command */
-	case CEXEC:
-	/* tracked alias */
-	case CTALIAS:
-		if (!(tp->flag&ISSET)) {
-			if (tp->u2.errnov == ENOENT) {
-				rv = 127;
-				warningf(true, "%s: %s", cp, "not found");
-			} else {
-				rv = 126;
-				warningf(true, "%s: %s: %s", cp, "can't execute",
-				    cstrerror(tp->u2.errnov));
-			}
-			break;
-		}
-
-		/* set $_ to programme's full path */
-		/* setstr() can't fail here */
-		setstr(typeset("_", LOCAL|EXPORT, 0, INTEGER, 0),
-		    tp->val.s, KSH_RETURN_ERROR);
-
-		if (flags&XEXEC) {
-			j_exit();
-			if (!(flags&XBGND)
-#ifndef MKSH_UNEMPLOYED
-			    || Flag(FMONITOR)
-#endif
-			    ) {
-				setexecsig(&sigtraps[SIGINT], SS_RESTORE_ORIG);
-				setexecsig(&sigtraps[SIGQUIT], SS_RESTORE_ORIG);
-			}
-		}
-
-		/* to fork we set up a TEXEC node and call execute */
-		texec.type = TEXEC;
-		/* for tprint */
-		texec.left = t;
-		texec.str = tp->val.s;
-		texec.args = ap;
-		rv = exchild(&texec, flags, xerrok, -1);
-		break;
-	}
- Leave:
-	if (flags & XEXEC) {
-		exstat = rv & 0xFF;
-		unwind(LLEAVE);
-	}
-	return (rv);
-}
-
-static void
-scriptexec(struct op *tp, const char **ap)
-{
-	const char *sh;
-#ifndef MKSH_SMALL
-	unsigned char *cp;
-	/* 64 == MAXINTERP in MirBSD <sys/param.h> */
-	char buf[64];
-	int fd;
-#endif
-	union mksh_ccphack args, cap;
-
-	sh = str_val(global("EXECSHELL"));
-	if (sh && *sh)
-		sh = search_path(sh, path, X_OK, NULL);
-	if (!sh || !*sh)
-		sh = MKSH_DEFAULT_EXECSHELL;
-
-	*tp->args-- = tp->str;
-
-#ifndef MKSH_SMALL
-	if ((fd = open(tp->str, O_RDONLY)) >= 0) {
-		/* read first MAXINTERP octets from file */
-		if (read(fd, buf, sizeof(buf)) <= 0)
-			/* read error -> no good */
-			buf[0] = '\0';
-		close(fd);
-
-		/* skip UTF-8 Byte Order Mark, if present */
-		cp = (unsigned char *)buf;
-		if ((cp[0] == 0xEF) && (cp[1] == 0xBB) && (cp[2] == 0xBF))
-			cp += 3;
-		/* save begin of shebang for later */
-		fd = (char *)cp - buf;		/* either 0 or (if BOM) 3 */
-
-		/* scan for newline (or CR) or NUL _before_ end of buffer */
-		while ((char *)cp < (buf + sizeof(buf)))
-			if (*cp == '\0' || *cp == '\n' || *cp == '\r') {
-				*cp = '\0';
-				break;
-			} else
-				++cp;
-		/* if the shebang line is longer than MAXINTERP, bail out */
-		if ((char *)cp >= (buf + sizeof(buf)))
-			goto noshebang;
-
-		/* restore begin of shebang position (buf+0 or buf+3) */
-		cp = (unsigned char *)(buf + fd);
-		/* bail out if read error (above) or no shebang */
-		if ((cp[0] != '#') || (cp[1] != '!'))
-			goto noshebang;
-
-		cp += 2;
-		/* skip whitespace before shell name */
-		while (*cp == ' ' || *cp == '\t')
-			++cp;
-		/* just whitespace on the line? */
-		if (*cp == '\0')
-			goto noshebang;
-		/* no, we actually found an interpreter name */
-		sh = (char *)cp;
-		/* look for end of shell/interpreter name */
-		while (*cp != ' ' && *cp != '\t' && *cp != '\0')
-			++cp;
-		/* any arguments? */
-		if (*cp) {
-			*cp++ = '\0';
-			/* skip spaces before arguments */
-			while (*cp == ' ' || *cp == '\t')
-				++cp;
-			/* pass it all in ONE argument (historic reasons) */
-			if (*cp)
-				*tp->args-- = (char *)cp;
-		}
- noshebang:
-		fd = buf[0] << 8 | buf[1];
-		if ((fd == /* OMAGIC */ 0407) ||
-		    (fd == /* NMAGIC */ 0410) ||
-		    (fd == /* ZMAGIC */ 0413) ||
-		    (fd == /* QMAGIC */ 0314) ||
-		    (fd == /* ECOFF_I386 */ 0x4C01) ||
-		    (fd == /* ECOFF_M68K */ 0x0150 || fd == 0x5001) ||
-		    (fd == /* ECOFF_SH */   0x0500 || fd == 0x0005) ||
-		    (fd == 0x7F45 && buf[2] == 'L' && buf[3] == 'F') ||
-		    (fd == /* "MZ" */ 0x4D5A) ||
-		    (fd == /* gzip */ 0x1F8B))
-			errorf("%s: not executable: magic %04X", tp->str, fd);
-	}
-#endif
-	args.ro = tp->args;
-	*args.ro = sh;
-
-	cap.ro = ap;
-	execve(args.rw[0], args.rw, cap.rw);
-
-	/* report both the programme that was run and the bogus interpreter */
-	errorf("%s: %s: %s", tp->str, sh, cstrerror(errno));
-}
-
-int
-shcomexec(const char **wp)
-{
-	struct tbl *tp;
-
-	tp = ktsearch(&builtins, *wp, hash(*wp));
-	return (call_builtin(tp, wp, "shcomexec"));
-}
-
-/*
- * Search function tables for a function. If create set, a table entry
- * is created if none is found.
- */
-struct tbl *
-findfunc(const char *name, uint32_t h, bool create)
-{
-	struct block *l;
-	struct tbl *tp = NULL;
-
-	for (l = e->loc; l; l = l->next) {
-		tp = ktsearch(&l->funs, name, h);
-		if (tp)
-			break;
-		if (!l->next && create) {
-			tp = ktenter(&l->funs, name, h);
-			tp->flag = DEFINED;
-			tp->type = CFUNC;
-			tp->val.t = NULL;
-			break;
-		}
-	}
-	return (tp);
-}
-
-/*
- * define function. Returns 1 if function is being undefined (t == 0) and
- * function did not exist, returns 0 otherwise.
- */
-int
-define(const char *name, struct op *t)
-{
-	uint32_t nhash;
-	struct tbl *tp;
-	bool was_set = false;
-
-	nhash = hash(name);
-
-	if (t != NULL && !tobool(t->u.ksh_func)) {
-		/* drop same-name aliases for POSIX functions */
-		if ((tp = ktsearch(&aliases, name, nhash)))
-			ktdelete(tp);
-	}
-
-	while (/* CONSTCOND */ 1) {
-		tp = findfunc(name, nhash, true);
-		/* because findfunc:create=true */
-		mkssert(tp != NULL);
-
-		if (tp->flag & ISSET)
-			was_set = true;
-		/*
-		 * If this function is currently being executed, we zap
-		 * this table entry so findfunc() won't see it
-		 */
-		if (tp->flag & FINUSE) {
-			tp->name[0] = '\0';
-			/* ensure it won't be found */
-			tp->flag &= ~DEFINED;
-			tp->flag |= FDELETE;
-		} else
-			break;
-	}
-
-	if (tp->flag & ALLOC) {
-		tp->flag &= ~(ISSET|ALLOC);
-		tfree(tp->val.t, tp->areap);
-	}
-
-	if (t == NULL) {
-		/* undefine */
-		ktdelete(tp);
-		return (was_set ? 0 : 1);
-	}
-
-	tp->val.t = tcopy(t->left, tp->areap);
-	tp->flag |= (ISSET|ALLOC);
-	if (t->u.ksh_func)
-		tp->flag |= FKSH;
-
-	return (0);
-}
-
-/*
- * add builtin
- */
-const char *
-builtin(const char *name, int (*func) (const char **))
-{
-	struct tbl *tp;
-	uint32_t flag = DEFINED;
-
-	/* see if any flags should be set for this builtin */
-	while (1) {
-		if (*name == '=')
-			/* command does variable assignment */
-			flag |= KEEPASN;
-		else if (*name == '*')
-			/* POSIX special builtin */
-			flag |= SPEC_BI;
-		else
-			break;
-		name++;
-	}
-
-	tp = ktenter(&builtins, name, hash(name));
-	tp->flag = flag;
-	tp->type = CSHELL;
-	tp->val.f = func;
-
-	return (name);
-}
-
-/*
- * find command
- * either function, hashed command, or built-in (in that order)
- */
-struct tbl *
-findcom(const char *name, int flags)
-{
-	static struct tbl temp;
-	uint32_t h = hash(name);
-	struct tbl *tp = NULL, *tbi;
-	/* insert if not found */
-	unsigned char insert = Flag(FTRACKALL);
-	/* for function autoloading */
-	char *fpath;
-	union mksh_cchack npath;
-
-	if (vstrchr(name, '/')) {
-		insert = 0;
-		/* prevent FPATH search below */
-		flags &= ~FC_FUNC;
-		goto Search;
-	}
-	tbi = (flags & FC_BI) ? ktsearch(&builtins, name, h) : NULL;
-	/*
-	 * POSIX says special builtins first, then functions, then
-	 * regular builtins, then search path...
-	 */
-	if ((flags & FC_SPECBI) && tbi && (tbi->flag & SPEC_BI))
-		tp = tbi;
-	if (!tp && (flags & FC_FUNC)) {
-		tp = findfunc(name, h, false);
-		if (tp && !(tp->flag & ISSET)) {
-			if ((fpath = str_val(global("FPATH"))) == null) {
-				tp->u.fpath = NULL;
-				tp->u2.errnov = ENOENT;
-			} else
-				tp->u.fpath = search_path(name, fpath, R_OK,
-				    &tp->u2.errnov);
-		}
-	}
-	if (!tp && (flags & FC_NORMBI) && tbi)
-		tp = tbi;
-	if (!tp && (flags & FC_PATH) && !(flags & FC_DEFPATH)) {
-		tp = ktsearch(&taliases, name, h);
-		if (tp && (tp->flag & ISSET) &&
-		    ksh_access(tp->val.s, X_OK) != 0) {
-			if (tp->flag & ALLOC) {
-				tp->flag &= ~ALLOC;
-				afree(tp->val.s, APERM);
-			}
-			tp->flag &= ~ISSET;
-		}
-	}
-
- Search:
-	if ((!tp || (tp->type == CTALIAS && !(tp->flag&ISSET))) &&
-	    (flags & FC_PATH)) {
-		if (!tp) {
-			if (insert && !(flags & FC_DEFPATH)) {
-				tp = ktenter(&taliases, name, h);
-				tp->type = CTALIAS;
-			} else {
-				tp = &temp;
-				tp->type = CEXEC;
-			}
-			/* make ~ISSET */
-			tp->flag = DEFINED;
-		}
-		npath.ro = search_path(name,
-		    (flags & FC_DEFPATH) ? def_path : path,
-		    X_OK, &tp->u2.errnov);
-		if (npath.ro) {
-			strdupx(tp->val.s, npath.ro, APERM);
-			if (npath.ro != name)
-				afree(npath.rw, ATEMP);
-			tp->flag |= ISSET|ALLOC;
-		} else if ((flags & FC_FUNC) &&
-		    (fpath = str_val(global("FPATH"))) != null &&
-		    (npath.ro = search_path(name, fpath, R_OK,
-		    &tp->u2.errnov)) != NULL) {
-			/*
-			 * An undocumented feature of AT&T ksh is that
-			 * it searches FPATH if a command is not found,
-			 * even if the command hasn't been set up as an
-			 * autoloaded function (ie, no typeset -uf).
-			 */
-			tp = &temp;
-			tp->type = CFUNC;
-			/* make ~ISSET */
-			tp->flag = DEFINED;
-			tp->u.fpath = npath.ro;
-		}
-	}
-	return (tp);
-}
-
-/*
- * flush executable commands with relative paths
- * (just relative or all?)
- */
-void
-flushcom(bool all)
-{
-	struct tbl *tp;
-	struct tstate ts;
-
-	for (ktwalk(&ts, &taliases); (tp = ktnext(&ts)) != NULL; )
-		if ((tp->flag&ISSET) && (all || tp->val.s[0] != '/')) {
-			if (tp->flag&ALLOC) {
-				tp->flag &= ~(ALLOC|ISSET);
-				afree(tp->val.s, APERM);
-			}
-			tp->flag &= ~ISSET;
-		}
-}
-
-/* check if path is something we want to find */
-static int
-search_access(const char *fn, int mode)
-{
-	struct stat sb;
-
-	if (stat(fn, &sb) < 0)
-		/* file does not exist */
-		return (ENOENT);
-	/* LINTED use of access */
-	if (access(fn, mode) < 0)
-		/* file exists, but we can't access it */
-		return (errno);
-	if (mode == X_OK && (!S_ISREG(sb.st_mode) ||
-	    !(sb.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH))))
-		/* access(2) may say root can execute everything */
-		return (S_ISDIR(sb.st_mode) ? EISDIR : EACCES);
-	return (0);
-}
-
-/*
- * search for command with PATH
- */
-const char *
-search_path(const char *name, const char *lpath,
-    /* R_OK or X_OK */
-    int mode,
-    /* set if candidate found, but not suitable */
-    int *errnop)
-{
-	const char *sp, *p;
-	char *xp;
-	XString xs;
-	size_t namelen;
-	int ec = 0, ev;
-
-	if (vstrchr(name, '/')) {
-		if ((ec = search_access(name, mode)) == 0) {
- search_path_ok:
-			if (errnop)
-				*errnop = 0;
-			return (name);
-		}
-		goto search_path_err;
-	}
-
-	namelen = strlen(name) + 1;
-	Xinit(xs, xp, 128, ATEMP);
-
-	sp = lpath;
-	while (sp != NULL) {
-		xp = Xstring(xs, xp);
-		if (!(p = cstrchr(sp, ':')))
-			p = sp + strlen(sp);
-		if (p != sp) {
-			XcheckN(xs, xp, p - sp);
-			memcpy(xp, sp, p - sp);
-			xp += p - sp;
-			*xp++ = '/';
-		}
-		sp = p;
-		XcheckN(xs, xp, namelen);
-		memcpy(xp, name, namelen);
-		if ((ev = search_access(Xstring(xs, xp), mode)) == 0) {
-			name = Xclose(xs, xp + namelen);
-			goto search_path_ok;
-		}
-		/* accumulate non-ENOENT errors only */
-		if (ev != ENOENT && ec == 0)
-			ec = ev;
-		if (*sp++ == '\0')
-			sp = NULL;
-	}
-	Xfree(xs, xp);
- search_path_err:
-	if (errnop)
-		*errnop = ec ? ec : ENOENT;
-	return (NULL);
-}
-
-static int
-call_builtin(struct tbl *tp, const char **wp, const char *where)
-{
-	int rv;
-
-	if (!tp)
-		internal_errorf("%s: %s", where, wp[0]);
-	builtin_argv0 = wp[0];
-	builtin_flag = tp->flag;
-	shf_reopen(1, SHF_WR, shl_stdout);
-	shl_stdout_ok = true;
-	ksh_getopt_reset(&builtin_opt, GF_ERROR);
-	rv = (*tp->val.f)(wp);
-	shf_flush(shl_stdout);
-	shl_stdout_ok = false;
-	builtin_flag = 0;
-	builtin_argv0 = NULL;
-	return (rv);
-}
-
-/*
- * set up redirection, saving old fds in e->savefd
- */
-static int
-iosetup(struct ioword *iop, struct tbl *tp)
-{
-	int u = -1;
-	char *cp = iop->name;
-	int iotype = iop->flag & IOTYPE;
-	bool do_open = true, do_close = false;
-	int flags = 0;
-	struct ioword iotmp;
-	struct stat statb;
-
-	if (iotype != IOHERE)
-		cp = evalonestr(cp, DOTILDE|(Flag(FTALKING_I) ? DOGLOB : 0));
-
-	/* Used for tracing and error messages to print expanded cp */
-	iotmp = *iop;
-	iotmp.name = (iotype == IOHERE) ? NULL : cp;
-	iotmp.flag |= IONAMEXP;
-
-	if (Flag(FXTRACE)) {
-		change_xtrace(2, false);
-		fptreef(shl_xtrace, 0, "%R", &iotmp);
-		change_xtrace(1, false);
-	}
-
-	switch (iotype) {
-	case IOREAD:
-		flags = O_RDONLY;
-		break;
-
-	case IOCAT:
-		flags = O_WRONLY | O_APPEND | O_CREAT;
-		break;
-
-	case IOWRITE:
-		flags = O_WRONLY | O_CREAT | O_TRUNC;
-		/*
-		 * The stat() is here to allow redirections to
-		 * things like /dev/null without error.
-		 */
-		if (Flag(FNOCLOBBER) && !(iop->flag & IOCLOB) &&
-		    (stat(cp, &statb) < 0 || S_ISREG(statb.st_mode)))
-			flags |= O_EXCL;
-		break;
-
-	case IORDWR:
-		flags = O_RDWR | O_CREAT;
-		break;
-
-	case IOHERE:
-		do_open = false;
-		/* herein() returns -2 if error has been printed */
-		u = herein(iop, NULL);
-		/* cp may have wrong name */
-		break;
-
-	case IODUP: {
-		const char *emsg;
-
-		do_open = false;
-		if (*cp == '-' && !cp[1]) {
-			/* prevent error return below */
-			u = 1009;
-			do_close = true;
-		} else if ((u = check_fd(cp,
-		    X_OK | ((iop->flag & IORDUP) ? R_OK : W_OK),
-		    &emsg)) < 0) {
-			char *sp;
-
-			warningf(true, "%s: %s",
-			    (sp = snptreef(NULL, 32, "%R", &iotmp)), emsg);
-			afree(sp, ATEMP);
-			return (-1);
-		}
-		if (u == iop->unit)
-			/* "dup from" == "dup to" */
-			return (0);
-		break;
-	    }
-	}
-
-	if (do_open) {
-		if (Flag(FRESTRICTED) && (flags & O_CREAT)) {
-			warningf(true, "%s: %s", cp, "restricted");
-			return (-1);
-		}
-		u = open(cp, flags, 0666);
-	}
-	if (u < 0) {
-		/* herein() may already have printed message */
-		if (u == -1) {
-			u = errno;
-			warningf(true, "can't %s %s: %s",
-			    iotype == IODUP ? "dup" :
-			    (iotype == IOREAD || iotype == IOHERE) ?
-			    "open" : "create", cp, cstrerror(u));
-		}
-		return (-1);
-	}
-	/* Do not save if it has already been redirected (i.e. "cat >x >y"). */
-	if (e->savefd[iop->unit] == 0) {
-		/* If these are the same, it means unit was previously closed */
-		if (u == iop->unit)
-			e->savefd[iop->unit] = -1;
-		else
-			/*
-			 * c_exec() assumes e->savefd[fd] set for any
-			 * redirections. Ask savefd() not to close iop->unit;
-			 * this allows error messages to be seen if iop->unit
-			 * is 2; also means we can't lose the fd (eg, both
-			 * dup2 below and dup2 in restfd() failing).
-			 */
-			e->savefd[iop->unit] = savefd(iop->unit);
-	}
-
-	if (do_close)
-		close(iop->unit);
-	else if (u != iop->unit) {
-		if (ksh_dup2(u, iop->unit, true) < 0) {
-			int eno;
-			char *sp;
-
-			eno = errno;
-			warningf(true, "%s %s %s",
-			    "can't finish (dup) redirection",
-			    (sp = snptreef(NULL, 32, "%R", &iotmp)),
-			    cstrerror(eno));
-			afree(sp, ATEMP);
-			if (iotype != IODUP)
-				close(u);
-			return (-1);
-		}
-		if (iotype != IODUP)
-			close(u);
-		/*
-		 * Touching any co-process fd in an empty exec
-		 * causes the shell to close its copies
-		 */
-		else if (tp && tp->type == CSHELL && tp->val.f == c_exec) {
-			if (iop->flag & IORDUP)
-				/* possible exec <&p */
-				coproc_read_close(u);
-			else
-				/* possible exec >&p */
-				coproc_write_close(u);
-		}
-	}
-	if (u == 2)
-		/* Clear any write errors */
-		shf_reopen(2, SHF_WR, shl_out);
-	return (0);
-}
-
-/*
- * Process here documents by providing the content, either as
- * result (globally allocated) string or in a temp file; if
- * unquoted, the string is expanded first.
- */
-static int
-hereinval(const char *content, int sub, char **resbuf, struct shf *shf)
-{
-	const char * volatile ccp = content;
-	struct source *s, *osource;
-
-	osource = source;
-	newenv(E_ERRH);
-	if (kshsetjmp(e->jbuf)) {
-		source = osource;
-		quitenv(shf);
-		/* special to iosetup(): don't print error */
-		return (-2);
-	}
-	if (sub) {
-		/* do substitutions on the content of heredoc */
-		s = pushs(SSTRING, ATEMP);
-		s->start = s->str = ccp;
-		source = s;
-		if (yylex(sub) != LWORD)
-			internal_errorf("%s: %s", "herein", "yylex");
-		source = osource;
-		ccp = evalstr(yylval.cp, 0);
-	}
-
-	if (resbuf == NULL)
-		shf_puts(ccp, shf);
-	else
-		strdupx(*resbuf, ccp, APERM);
-
-	quitenv(NULL);
-	return (0);
-}
-
-static int
-herein(struct ioword *iop, char **resbuf)
-{
-	int fd = -1;
-	struct shf *shf;
-	struct temp *h;
-	int i;
-
-	/* ksh -c 'cat << EOF' can cause this... */
-	if (iop->heredoc == NULL) {
-		warningf(true, "%s missing", "here document");
-		/* special to iosetup(): don't print error */
-		return (-2);
-	}
-
-	/* lexer substitution flags */
-	i = (iop->flag & IOEVAL) ? (ONEWORD | HEREDOC) : 0;
-
-	/* skip all the fd setup if we just want the value */
-	if (resbuf != NULL)
-		return (hereinval(iop->heredoc, i, resbuf, NULL));
-
-	/*
-	 * Create temp file to hold content (done before newenv
-	 * so temp doesn't get removed too soon).
-	 */
-	h = maketemp(ATEMP, TT_HEREDOC_EXP, &e->temps);
-	if (!(shf = h->shf) || (fd = open(h->tffn, O_RDONLY, 0)) < 0) {
-		i = errno;
-		warningf(true, "can't %s temporary file %s: %s",
-		    !shf ? "create" : "open", h->tffn, cstrerror(i));
-		if (shf)
-			shf_close(shf);
-		/* special to iosetup(): don't print error */
-		return (-2);
-	}
-
-	if (hereinval(iop->heredoc, i, NULL, shf) == -2) {
-		close(fd);
-		/* special to iosetup(): don't print error */
-		return (-2);
-	}
-
-	if (shf_close(shf) == EOF) {
-		i = errno;
-		close(fd);
-		warningf(true, "can't %s temporary file %s: %s",
-		    "write", h->tffn, cstrerror(i));
-		/* special to iosetup(): don't print error */
-		return (-2);
-	}
-
-	return (fd);
-}
-
-/*
- *	ksh special - the select command processing section
- *	print the args in column form - assuming that we can
- */
-static const char *
-do_selectargs(const char **ap, bool print_menu)
-{
-	static const char *read_args[] = {
-		"read", "-r", "REPLY", NULL
-	};
-	char *s;
-	int i, argct;
-
-	for (argct = 0; ap[argct]; argct++)
-		;
-	while (/* CONSTCOND */ 1) {
-		/*-
-		 * Menu is printed if
-		 *	- this is the first time around the select loop
-		 *	- the user enters a blank line
-		 *	- the REPLY parameter is empty
-		 */
-		if (print_menu || !*str_val(global("REPLY")))
-			pr_menu(ap);
-		shellf("%s", str_val(global("PS3")));
-		if (call_builtin(findcom("read", FC_BI), read_args, Tselect))
-			return (NULL);
-		s = str_val(global("REPLY"));
-		if (*s && getn(s, &i))
-			return ((i >= 1 && i <= argct) ? ap[i - 1] : null);
-		print_menu = true;
-	}
-}
-
-struct select_menu_info {
-	const char * const *args;
-	int num_width;
-};
-
-/* format a single select menu item */
-static char *
-select_fmt_entry(char *buf, size_t buflen, unsigned int i, const void *arg)
-{
-	const struct select_menu_info *smi =
-	    (const struct select_menu_info *)arg;
-
-	shf_snprintf(buf, buflen, "%*u) %s",
-	    smi->num_width, i + 1, smi->args[i]);
-	return (buf);
-}
-
-/*
- *	print a select style menu
- */
-void
-pr_menu(const char * const *ap)
-{
-	struct select_menu_info smi;
-	const char * const *pp;
-	size_t acols = 0, aocts = 0, i;
-	unsigned int n;
-
-	/*
-	 * width/column calculations were done once and saved, but this
-	 * means select can't be used recursively so we re-calculate
-	 * each time (could save in a structure that is returned, but
-	 * it's probably not worth the bother)
-	 */
-
-	/*
-	 * get dimensions of the list
-	 */
-	for (n = 0, pp = ap; *pp; n++, pp++) {
-		i = strlen(*pp);
-		if (i > aocts)
-			aocts = i;
-		i = utf_mbswidth(*pp);
-		if (i > acols)
-			acols = i;
-	}
-
-	/*
-	 * we will print an index of the form "%d) " in front of
-	 * each entry, so get the maximum width of this
-	 */
-	for (i = n, smi.num_width = 1; i >= 10; i /= 10)
-		smi.num_width++;
-
-	smi.args = ap;
-	print_columns(shl_out, n, select_fmt_entry, (void *)&smi,
-	    smi.num_width + 2 + aocts, smi.num_width + 2 + acols,
-	    true);
-}
-
-static char *
-plain_fmt_entry(char *buf, size_t buflen, unsigned int i, const void *arg)
-{
-	strlcpy(buf, ((const char * const *)arg)[i], buflen);
-	return (buf);
-}
-
-void
-pr_list(char * const *ap)
-{
-	size_t acols = 0, aocts = 0, i;
-	unsigned int n;
-	char * const *pp;
-
-	for (n = 0, pp = ap; *pp; n++, pp++) {
-		i = strlen(*pp);
-		if (i > aocts)
-			aocts = i;
-		i = utf_mbswidth(*pp);
-		if (i > acols)
-			acols = i;
-	}
-
-	print_columns(shl_out, n, plain_fmt_entry, (const void *)ap,
-	    aocts, acols, false);
-}
-
-/*
- *	[[ ... ]] evaluation routines
- */
-
-/*
- * Test if the current token is a whatever. Accepts the current token if
- * it is. Returns 0 if it is not, non-zero if it is (in the case of
- * TM_UNOP and TM_BINOP, the returned value is a Test_op).
- */
-static Test_op
-dbteste_isa(Test_env *te, Test_meta meta)
-{
-	Test_op ret = TO_NONOP;
-	int uqword;
-	const char *p;
-
-	if (!*te->pos.wp)
-		return (meta == TM_END ? TO_NONNULL : TO_NONOP);
-
-	/* unquoted word? */
-	for (p = *te->pos.wp; *p == CHAR; p += 2)
-		;
-	uqword = *p == EOS;
-
-	if (meta == TM_UNOP || meta == TM_BINOP) {
-		if (uqword) {
-			/* longer than the longest operator */
-			char buf[8];
-			char *q = buf;
-
-			p = *te->pos.wp;
-			while (*p++ == CHAR &&
-			    (size_t)(q - buf) < sizeof(buf) - 1)
-				*q++ = *p++;
-			*q = '\0';
-			ret = test_isop(meta, buf);
-		}
-	} else if (meta == TM_END)
-		ret = TO_NONOP;
-	else
-		ret = (uqword && !strcmp(*te->pos.wp,
-		    dbtest_tokens[(int)meta])) ? TO_NONNULL : TO_NONOP;
-
-	/* Accept the token? */
-	if (ret != TO_NONOP)
-		te->pos.wp++;
-
-	return (ret);
-}
-
-static const char *
-dbteste_getopnd(Test_env *te, Test_op op, bool do_eval)
-{
-	const char *s = *te->pos.wp;
-
-	if (!s)
-		return (NULL);
-
-	te->pos.wp++;
-
-	if (!do_eval)
-		return (null);
-
-	if (op == TO_STEQL || op == TO_STNEQ)
-		s = evalstr(s, DOTILDE | DOPAT);
-	else
-		s = evalstr(s, DOTILDE);
-
-	return (s);
-}
-
-static void
-dbteste_error(Test_env *te, int offset, const char *msg)
-{
-	te->flags |= TEF_ERROR;
-	internal_warningf("dbteste_error: %s (offset %d)", msg, offset);
-}

Copied: vendor/MirOS/mksh/R50/exec.c (from rev 6707, vendor/MirOS/mksh/dist/exec.c)
===================================================================
--- vendor/MirOS/mksh/R50/exec.c	                        (rev 0)
+++ vendor/MirOS/mksh/R50/exec.c	2014-07-01 12:01:17 UTC (rev 6708)
@@ -0,0 +1,1734 @@
+/*	$OpenBSD: exec.c,v 1.50 2013/06/10 21:09:27 millert Exp $	*/
+
+/*-
+ * Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
+ *		 2011, 2012, 2013, 2014
+ *	Thorsten Glaser <tg at mirbsd.org>
+ *
+ * Provided that these terms and disclaimer and all copyright notices
+ * are retained or reproduced in an accompanying document, permission
+ * is granted to deal in this work without restriction, including un-
+ * limited rights to use, publicly perform, distribute, sell, modify,
+ * merge, give away, or sublicence.
+ *
+ * This work is provided "AS IS" and WITHOUT WARRANTY of any kind, to
+ * the utmost extent permitted by applicable law, neither express nor
+ * implied; without malicious intent or gross negligence. In no event
+ * may a licensor, author or contributor be held liable for indirect,
+ * direct, other damage, loss, or other issues arising in any way out
+ * of dealing in the work, even if advised of the possibility of such
+ * damage or existence of a defect, except proven that it results out
+ * of said person's immediate fault when using the work as intended.
+ */
+
+#include "sh.h"
+
+__RCSID("$MirOS: src/bin/mksh/exec.c,v 1.132 2014/06/24 18:38:31 tg Exp $");
+
+#ifndef MKSH_DEFAULT_EXECSHELL
+#define MKSH_DEFAULT_EXECSHELL	"/bin/sh"
+#endif
+
+static int comexec(struct op *, struct tbl * volatile, const char **,
+    int volatile, volatile int *);
+static void scriptexec(struct op *, const char **) MKSH_A_NORETURN;
+static int call_builtin(struct tbl *, const char **, const char *);
+static int iosetup(struct ioword *, struct tbl *);
+static int herein(struct ioword *, char **);
+static const char *do_selectargs(const char **, bool);
+static Test_op dbteste_isa(Test_env *, Test_meta);
+static const char *dbteste_getopnd(Test_env *, Test_op, bool);
+static void dbteste_error(Test_env *, int, const char *);
+static int search_access(const char *, int);
+/* XXX: horrible kludge to fit within the framework */
+static char *plain_fmt_entry(char *, size_t, unsigned int, const void *);
+static char *select_fmt_entry(char *, size_t, unsigned int, const void *);
+
+/*
+ * execute command tree
+ */
+int
+execute(struct op * volatile t,
+    /* if XEXEC don't fork */
+    volatile int flags,
+    volatile int * volatile xerrok)
+{
+	int i;
+	volatile int rv = 0, dummy = 0;
+	int pv[2];
+	const char ** volatile ap = NULL;
+	char ** volatile up;
+	const char *s, *ccp;
+	struct ioword **iowp;
+	struct tbl *tp = NULL;
+	char *cp;
+
+	if (t == NULL)
+		return (0);
+
+	/* Caller doesn't care if XERROK should propagate. */
+	if (xerrok == NULL)
+		xerrok = &dummy;
+
+	if ((flags&XFORK) && !(flags&XEXEC) && t->type != TPIPE)
+		/* run in sub-process */
+		return (exchild(t, flags & ~XTIME, xerrok, -1));
+
+	newenv(E_EXEC);
+	if (trap)
+		runtraps(0);
+
+	/* we want to run an executable, do some variance checks */
+	if (t->type == TCOM) {
+		/* check if this is 'var=<<EOF' */
+		if (
+		    /* we have zero arguments, i.e. no programme to run */
+		    t->args[0] == NULL &&
+		    /* we have exactly one variable assignment */
+		    t->vars[0] != NULL && t->vars[1] == NULL &&
+		    /* we have exactly one I/O redirection */
+		    t->ioact != NULL && t->ioact[0] != NULL &&
+		    t->ioact[1] == NULL &&
+		    /* of type "here document" (or "here string") */
+		    (t->ioact[0]->flag & IOTYPE) == IOHERE &&
+		    /* the variable assignment begins with a valid varname */
+		    (ccp = skip_wdvarname(t->vars[0], true)) != t->vars[0] &&
+		    /* and has no right-hand side (i.e. "varname=") */
+		    ccp[0] == CHAR && ccp[1] == '=' && ccp[2] == EOS &&
+		    /* plus we can have a here document content */
+		    herein(t->ioact[0], &cp) == 0 && cp && *cp) {
+			char *sp = cp, *dp;
+			size_t n = ccp - t->vars[0] + 2, z;
+
+			/* drop redirection (will be garbage collected) */
+			t->ioact = NULL;
+
+			/* set variable to its expanded value */
+			z = strlen(cp) + 1;
+			if (notoktomul(z, 2) || notoktoadd(z * 2, n))
+				internal_errorf(Toomem, (size_t)-1);
+			dp = alloc(z * 2 + n, ATEMP);
+			memcpy(dp, t->vars[0], n);
+			t->vars[0] = dp;
+			dp += n;
+			while (*sp) {
+				*dp++ = QCHAR;
+				*dp++ = *sp++;
+			}
+			*dp = EOS;
+			/* free the expanded value */
+			afree(cp, APERM);
+		}
+
+		/*
+		 * Clear subst_exstat before argument expansion. Used by
+		 * null commands (see comexec() and c_eval()) and by c_set().
+		 */
+		subst_exstat = 0;
+
+		/* for $LINENO */
+		current_lineno = t->lineno;
+
+		/*
+		 * POSIX says expand command words first, then redirections,
+		 * and assignments last..
+		 */
+		up = eval(t->args, t->u.evalflags | DOBLANK | DOGLOB | DOTILDE);
+		if (flags & XTIME)
+			/* Allow option parsing (bizarre, but POSIX) */
+			timex_hook(t, &up);
+		ap = (const char **)up;
+		if (ap[0])
+			tp = findcom(ap[0], FC_BI|FC_FUNC);
+	}
+	flags &= ~XTIME;
+
+	if (t->ioact != NULL || t->type == TPIPE || t->type == TCOPROC) {
+		e->savefd = alloc2(NUFILE, sizeof(short), ATEMP);
+		/* initialise to not redirected */
+		memset(e->savefd, 0, NUFILE * sizeof(short));
+	}
+
+	/* mark for replacement later (unless TPIPE) */
+	vp_pipest->flag |= INT_L;
+
+	/* do redirection, to be restored in quitenv() */
+	if (t->ioact != NULL)
+		for (iowp = t->ioact; *iowp != NULL; iowp++) {
+			if (iosetup(*iowp, tp) < 0) {
+				exstat = rv = 1;
+				/*
+				 * Redirection failures for special commands
+				 * cause (non-interactive) shell to exit.
+				 */
+				if (tp && tp->type == CSHELL &&
+				    (tp->flag & SPEC_BI))
+					errorfz();
+				/* Deal with FERREXIT, quitenv(), etc. */
+				goto Break;
+			}
+		}
+
+	switch (t->type) {
+	case TCOM:
+		rv = comexec(t, tp, (const char **)ap, flags, xerrok);
+		break;
+
+	case TPAREN:
+		rv = execute(t->left, flags | XFORK, xerrok);
+		break;
+
+	case TPIPE:
+		flags |= XFORK;
+		flags &= ~XEXEC;
+		e->savefd[0] = savefd(0);
+		e->savefd[1] = savefd(1);
+		while (t->type == TPIPE) {
+			openpipe(pv);
+			/* stdout of curr */
+			ksh_dup2(pv[1], 1, false);
+			/**
+			 * Let exchild() close pv[0] in child
+			 * (if this isn't done, commands like
+			 *	(: ; cat /etc/termcap) | sleep 1
+			 * will hang forever).
+			 */
+			exchild(t->left, flags | XPIPEO | XCCLOSE,
+			    NULL, pv[0]);
+			/* stdin of next */
+			ksh_dup2(pv[0], 0, false);
+			closepipe(pv);
+			flags |= XPIPEI;
+			t = t->right;
+		}
+		/* stdout of last */
+		restfd(1, e->savefd[1]);
+		/* no need to re-restore this */
+		e->savefd[1] = 0;
+		/* Let exchild() close 0 in parent, after fork, before wait */
+		i = exchild(t, flags | XPCLOSE | XPIPEST, xerrok, 0);
+		if (!(flags&XBGND) && !(flags&XXCOM))
+			rv = i;
+		break;
+
+	case TLIST:
+		while (t->type == TLIST) {
+			execute(t->left, flags & XERROK, NULL);
+			t = t->right;
+		}
+		rv = execute(t, flags & XERROK, xerrok);
+		break;
+
+	case TCOPROC: {
+#ifndef MKSH_NOPROSPECTOFWORK
+		sigset_t omask;
+
+		/*
+		 * Block sigchild as we are using things changed in the
+		 * signal handler
+		 */
+		sigprocmask(SIG_BLOCK, &sm_sigchld, &omask);
+		e->type = E_ERRH;
+		if ((i = kshsetjmp(e->jbuf))) {
+			sigprocmask(SIG_SETMASK, &omask, NULL);
+			quitenv(NULL);
+			unwind(i);
+			/* NOTREACHED */
+		}
+#endif
+		/* Already have a (live) co-process? */
+		if (coproc.job && coproc.write >= 0)
+			errorf("coprocess already exists");
+
+		/* Can we re-use the existing co-process pipe? */
+		coproc_cleanup(true);
+
+		/* do this before opening pipes, in case these fail */
+		e->savefd[0] = savefd(0);
+		e->savefd[1] = savefd(1);
+
+		openpipe(pv);
+		if (pv[0] != 0) {
+			ksh_dup2(pv[0], 0, false);
+			close(pv[0]);
+		}
+		coproc.write = pv[1];
+		coproc.job = NULL;
+
+		if (coproc.readw >= 0)
+			ksh_dup2(coproc.readw, 1, false);
+		else {
+			openpipe(pv);
+			coproc.read = pv[0];
+			ksh_dup2(pv[1], 1, false);
+			/* closed before first read */
+			coproc.readw = pv[1];
+			coproc.njobs = 0;
+			/* create new coprocess id */
+			++coproc.id;
+		}
+#ifndef MKSH_NOPROSPECTOFWORK
+		sigprocmask(SIG_SETMASK, &omask, NULL);
+		/* no more need for error handler */
+		e->type = E_EXEC;
+#endif
+
+		/*
+		 * exchild() closes coproc.* in child after fork,
+		 * will also increment coproc.njobs when the
+		 * job is actually created.
+		 */
+		flags &= ~XEXEC;
+		exchild(t->left, flags | XBGND | XFORK | XCOPROC | XCCLOSE,
+		    NULL, coproc.readw);
+		break;
+	}
+
+	case TASYNC:
+		/*
+		 * XXX non-optimal, I think - "(foo &)", forks for (),
+		 * forks again for async... parent should optimise
+		 * this to "foo &"...
+		 */
+		rv = execute(t->left, (flags&~XEXEC)|XBGND|XFORK, xerrok);
+		break;
+
+	case TOR:
+	case TAND:
+		rv = execute(t->left, XERROK, xerrok);
+		if ((rv == 0) == (t->type == TAND))
+			rv = execute(t->right, flags & XERROK, xerrok);
+		else {
+			flags |= XERROK;
+			if (xerrok)
+				*xerrok = 1;
+		}
+		break;
+
+	case TBANG:
+		rv = !execute(t->right, XERROK, xerrok);
+		flags |= XERROK;
+		if (xerrok)
+			*xerrok = 1;
+		break;
+
+	case TDBRACKET: {
+		Test_env te;
+
+		te.flags = TEF_DBRACKET;
+		te.pos.wp = t->args;
+		te.isa = dbteste_isa;
+		te.getopnd = dbteste_getopnd;
+		te.eval = test_eval;
+		te.error = dbteste_error;
+
+		rv = test_parse(&te);
+		break;
+	}
+
+	case TFOR:
+	case TSELECT: {
+		volatile bool is_first = true;
+
+		ap = (t->vars == NULL) ? e->loc->argv + 1 :
+		    (const char **)eval((const char **)t->vars,
+		    DOBLANK | DOGLOB | DOTILDE);
+		e->type = E_LOOP;
+		while ((i = kshsetjmp(e->jbuf))) {
+			if ((e->flags&EF_BRKCONT_PASS) ||
+			    (i != LBREAK && i != LCONTIN)) {
+				quitenv(NULL);
+				unwind(i);
+			} else if (i == LBREAK) {
+				rv = 0;
+				goto Break;
+			}
+		}
+		/* in case of a continue */
+		rv = 0;
+		if (t->type == TFOR) {
+			while (*ap != NULL) {
+				setstr(global(t->str), *ap++, KSH_UNWIND_ERROR);
+				rv = execute(t->left, flags & XERROK, xerrok);
+			}
+		} else {
+			/* TSELECT */
+			for (;;) {
+				if (!(ccp = do_selectargs(ap, is_first))) {
+					rv = 1;
+					break;
+				}
+				is_first = false;
+				setstr(global(t->str), ccp, KSH_UNWIND_ERROR);
+				execute(t->left, flags & XERROK, xerrok);
+			}
+		}
+		break;
+	}
+
+	case TWHILE:
+	case TUNTIL:
+		e->type = E_LOOP;
+		while ((i = kshsetjmp(e->jbuf))) {
+			if ((e->flags&EF_BRKCONT_PASS) ||
+			    (i != LBREAK && i != LCONTIN)) {
+				quitenv(NULL);
+				unwind(i);
+			} else if (i == LBREAK) {
+				rv = 0;
+				goto Break;
+			}
+		}
+		/* in case of a continue */
+		rv = 0;
+		while ((execute(t->left, XERROK, NULL) == 0) ==
+		    (t->type == TWHILE))
+			rv = execute(t->right, flags & XERROK, xerrok);
+		break;
+
+	case TIF:
+	case TELIF:
+		if (t->right == NULL)
+			/* should be error */
+			break;
+		rv = execute(t->left, XERROK, NULL) == 0 ?
+		    execute(t->right->left, flags & XERROK, xerrok) :
+		    execute(t->right->right, flags & XERROK, xerrok);
+		break;
+
+	case TCASE:
+		i = 0;
+		ccp = evalstr(t->str, DOTILDE);
+		for (t = t->left; t != NULL && t->type == TPAT; t = t->right) {
+			for (ap = (const char **)t->vars; *ap; ap++) {
+				if (i || ((s = evalstr(*ap, DOTILDE|DOPAT)) &&
+				    gmatchx(ccp, s, false))) {
+					rv = execute(t->left, flags & XERROK,
+					    xerrok);
+					i = 0;
+					switch (t->u.charflag) {
+					case '&':
+						i = 1;
+						/* FALLTHROUGH */
+					case '|':
+						goto TCASE_next;
+					}
+					goto TCASE_out;
+				}
+			}
+			i = 0;
+ TCASE_next:
+			/* empty */;
+		}
+ TCASE_out:
+		break;
+
+	case TBRACE:
+		rv = execute(t->left, flags & XERROK, xerrok);
+		break;
+
+	case TFUNCT:
+		rv = define(t->str, t);
+		break;
+
+	case TTIME:
+		/*
+		 * Clear XEXEC so nested execute() call doesn't exit
+		 * (allows "ls -l | time grep foo").
+		 */
+		rv = timex(t, flags & ~XEXEC, xerrok);
+		break;
+
+	case TEXEC:
+		/* an eval'd TCOM */
+		s = t->args[0];
+		up = makenv();
+		restoresigs();
+		cleanup_proc_env();
+		{
+			union mksh_ccphack cargs;
+
+			cargs.ro = t->args;
+			execve(t->str, cargs.rw, up);
+			rv = errno;
+		}
+		if (rv == ENOEXEC)
+			scriptexec(t, (const char **)up);
+		else
+			errorf("%s: %s", s, cstrerror(rv));
+	}
+ Break:
+	exstat = rv & 0xFF;
+	if (vp_pipest->flag & INT_L) {
+		unset(vp_pipest, 1);
+		vp_pipest->flag = DEFINED | ISSET | INTEGER | RDONLY |
+		    ARRAY | INT_U | INT_L;
+		vp_pipest->val.i = rv;
+	}
+
+	/* restores IO */
+	quitenv(NULL);
+	if ((flags&XEXEC))
+		/* exit child */
+		unwind(LEXIT);
+	if (rv != 0 && !(flags & XERROK) &&
+	    (xerrok == NULL || !*xerrok)) {
+		if (Flag(FERREXIT) & 0x80) {
+			/* inside eval */
+			Flag(FERREXIT) = 0;
+		} else {
+			trapsig(ksh_SIGERR);
+			if (Flag(FERREXIT))
+				unwind(LERROR);
+		}
+	}
+	return (rv);
+}
+
+/*
+ * execute simple command
+ */
+
+static int
+comexec(struct op *t, struct tbl * volatile tp, const char **ap,
+    volatile int flags, volatile int *xerrok)
+{
+	int i;
+	volatile int rv = 0;
+	const char *cp;
+	const char **lastp;
+	/* Must be static (XXX but why?) */
+	static struct op texec;
+	int type_flags;
+	bool keepasn_ok;
+	int fcflags = FC_BI|FC_FUNC|FC_PATH;
+	bool bourne_function_call = false;
+	struct block *l_expand, *l_assign;
+
+	/*
+	 * snag the last argument for $_ XXX not the same as AT&T ksh,
+	 * which only seems to set $_ after a newline (but not in
+	 * functions/dot scripts, but in interactive and script) -
+	 * perhaps save last arg here and set it in shell()?.
+	 */
+	if (Flag(FTALKING) && *(lastp = ap)) {
+		while (*++lastp)
+			;
+		/* setstr() can't fail here */
+		setstr(typeset("_", LOCAL, 0, INTEGER, 0), *--lastp,
+		    KSH_RETURN_ERROR);
+	}
+
+	/**
+	 * Deal with the shell builtins builtin, exec and command since
+	 * they can be followed by other commands. This must be done before
+	 * we know if we should create a local block which must be done
+	 * before we can do a path search (in case the assignments change
+	 * PATH).
+	 * Odd cases:
+	 *	FOO=bar exec >/dev/null		FOO is kept but not exported
+	 *	FOO=bar exec foobar		FOO is exported
+	 *	FOO=bar command exec >/dev/null	FOO is neither kept nor exported
+	 *	FOO=bar command			FOO is neither kept nor exported
+	 *	PATH=... foobar			use new PATH in foobar search
+	 */
+	keepasn_ok = true;
+	while (tp && tp->type == CSHELL) {
+		/* undo effects of command */
+		fcflags = FC_BI|FC_FUNC|FC_PATH;
+		if (tp->val.f == c_builtin) {
+			if ((cp = *++ap) == NULL ||
+			    (!strcmp(cp, "--") && (cp = *++ap) == NULL)) {
+				tp = NULL;
+				break;
+			}
+			if ((tp = findcom(cp, FC_BI)) == NULL)
+				errorf("%s: %s: %s", Tbuiltin, cp, "not a builtin");
+			continue;
+		} else if (tp->val.f == c_exec) {
+			if (ap[1] == NULL)
+				break;
+			ap++;
+			flags |= XEXEC;
+		} else if (tp->val.f == c_command) {
+			int optc, saw_p = 0;
+
+			/*
+			 * Ugly dealing with options in two places (here
+			 * and in c_command(), but such is life)
+			 */
+			ksh_getopt_reset(&builtin_opt, 0);
+			while ((optc = ksh_getopt(ap, &builtin_opt, ":p")) == 'p')
+				saw_p = 1;
+			if (optc != EOF)
+				/* command -vV or something */
+				break;
+			/* don't look for functions */
+			fcflags = FC_BI|FC_PATH;
+			if (saw_p) {
+				if (Flag(FRESTRICTED)) {
+					warningf(true, "%s: %s",
+					    "command -p", "restricted");
+					rv = 1;
+					goto Leave;
+				}
+				fcflags |= FC_DEFPATH;
+			}
+			ap += builtin_opt.optind;
+			/*
+			 * POSIX says special builtins lose their status
+			 * if accessed using command.
+			 */
+			keepasn_ok = false;
+			if (!ap[0]) {
+				/* ensure command with no args exits with 0 */
+				subst_exstat = 0;
+				break;
+			}
+#ifndef MKSH_NO_EXTERNAL_CAT
+		} else if (tp->val.f == c_cat) {
+			/*
+			 * if we have any flags, do not use the builtin
+			 * in theory, we could allow -u, but that would
+			 * mean to use ksh_getopt here and possibly ad-
+			 * ded complexity and more code and isn't worth
+			 * additional hassle (and the builtin must call
+			 * ksh_getopt already but can't come back here)
+			 */
+			if (ap[1] && ap[1][0] == '-' && ap[1][1] != '\0' &&
+			    /* argument, begins with -, is not - or -- */
+			    (ap[1][1] != '-' || ap[1][2] != '\0'))
+				/* don't look for builtins or functions */
+				fcflags = FC_PATH;
+			else
+				/* go on, use the builtin */
+				break;
+#endif
+		} else if (tp->val.f == c_trap) {
+			t->u.evalflags &= ~DOTCOMEXEC;
+			break;
+		} else
+			break;
+		tp = findcom(ap[0], fcflags & (FC_BI|FC_FUNC));
+	}
+	if (t->u.evalflags & DOTCOMEXEC)
+		flags |= XEXEC;
+	l_expand = e->loc;
+	if (keepasn_ok && (!ap[0] || (tp && (tp->flag & KEEPASN))))
+		type_flags = 0;
+	else {
+		/* create new variable/function block */
+		newblock();
+		/* ksh functions don't keep assignments, POSIX functions do. */
+		if (keepasn_ok && tp && tp->type == CFUNC &&
+		    !(tp->flag & FKSH)) {
+			bourne_function_call = true;
+			type_flags = EXPORT;
+		} else
+			type_flags = LOCAL|LOCAL_COPY|EXPORT;
+	}
+	l_assign = e->loc;
+	if (Flag(FEXPORT))
+		type_flags |= EXPORT;
+	if (Flag(FXTRACE))
+		change_xtrace(2, false);
+	for (i = 0; t->vars[i]; i++) {
+		/* do NOT lookup in the new var/fn block just created */
+		e->loc = l_expand;
+		cp = evalstr(t->vars[i], DOASNTILDE);
+		e->loc = l_assign;
+		if (Flag(FXTRACE)) {
+			const char *ccp;
+
+			ccp = skip_varname(cp, true);
+			if (*ccp == '+')
+				++ccp;
+			if (*ccp == '=')
+				++ccp;
+			shf_write(cp, ccp - cp, shl_xtrace);
+			print_value_quoted(shl_xtrace, ccp);
+			shf_putc(' ', shl_xtrace);
+		}
+		/* but assign in there as usual */
+		typeset(cp, type_flags, 0, 0, 0);
+		if (bourne_function_call && !(type_flags & EXPORT))
+			typeset(cp, LOCAL | LOCAL_COPY | EXPORT, 0, 0, 0);
+	}
+
+	if (Flag(FXTRACE)) {
+		change_xtrace(2, false);
+		if (ap[rv = 0]) {
+ xtrace_ap_loop:
+			print_value_quoted(shl_xtrace, ap[rv]);
+			if (ap[++rv]) {
+				shf_putc(' ', shl_xtrace);
+				goto xtrace_ap_loop;
+			}
+		}
+		change_xtrace(1, false);
+	}
+
+	if ((cp = *ap) == NULL) {
+		rv = subst_exstat;
+		goto Leave;
+	} else if (!tp) {
+		if (Flag(FRESTRICTED) && vstrchr(cp, '/')) {
+			warningf(true, "%s: %s", cp, "restricted");
+			rv = 1;
+			goto Leave;
+		}
+		tp = findcom(cp, fcflags);
+	}
+
+	switch (tp->type) {
+
+	/* shell built-in */
+	case CSHELL:
+		rv = call_builtin(tp, (const char **)ap, null);
+		if (!keepasn_ok && tp->val.f == c_shift) {
+			l_expand->argc = l_assign->argc;
+			l_expand->argv = l_assign->argv;
+		}
+		break;
+
+	/* function call */
+	case CFUNC: {
+		volatile unsigned char old_xflag;
+		volatile uint32_t old_inuse;
+		const char * volatile old_kshname;
+
+		if (!(tp->flag & ISSET)) {
+			struct tbl *ftp;
+
+			if (!tp->u.fpath) {
+				rv = (tp->u2.errnov == ENOENT) ? 127 : 126;
+				warningf(true, "%s: %s %s: %s", cp,
+				    "can't find", "function definition file",
+				    cstrerror(tp->u2.errnov));
+				break;
+			}
+			if (include(tp->u.fpath, 0, NULL, false) < 0) {
+				warningf(true, "%s: %s %s %s: %s", cp,
+				    "can't open", "function definition file",
+				    tp->u.fpath, cstrerror(errno));
+				rv = 127;
+				break;
+			}
+			if (!(ftp = findfunc(cp, hash(cp), false)) ||
+			    !(ftp->flag & ISSET)) {
+				warningf(true, "%s: %s %s", cp,
+				    "function not defined by", tp->u.fpath);
+				rv = 127;
+				break;
+			}
+			tp = ftp;
+		}
+
+		/*
+		 * ksh functions set $0 to function name, POSIX
+		 * functions leave $0 unchanged.
+		 */
+		old_kshname = kshname;
+		if (tp->flag & FKSH)
+			kshname = ap[0];
+		else
+			ap[0] = kshname;
+		e->loc->argv = ap;
+		for (i = 0; *ap++ != NULL; i++)
+			;
+		e->loc->argc = i - 1;
+		/*
+		 * ksh-style functions handle getopts sanely,
+		 * Bourne/POSIX functions are insane...
+		 */
+		if (tp->flag & FKSH) {
+			e->loc->flags |= BF_DOGETOPTS;
+			e->loc->getopts_state = user_opt;
+			getopts_reset(1);
+		}
+
+		old_xflag = Flag(FXTRACE) ? 1 : 0;
+		change_xtrace((Flag(FXTRACEREC) ? old_xflag : 0) |
+		    ((tp->flag & TRACE) ? 1 : 0), false);
+		old_inuse = tp->flag & FINUSE;
+		tp->flag |= FINUSE;
+
+		e->type = E_FUNC;
+		if (!(i = kshsetjmp(e->jbuf))) {
+			execute(tp->val.t, flags & XERROK, NULL);
+			i = LRETURN;
+		}
+
+		kshname = old_kshname;
+		change_xtrace(old_xflag, false);
+		tp->flag = (tp->flag & ~FINUSE) | old_inuse;
+
+		/*
+		 * Were we deleted while executing? If so, free the
+		 * execution tree. TODO: Unfortunately, the table entry
+		 * is never re-used until the lookup table is expanded.
+		 */
+		if ((tp->flag & (FDELETE|FINUSE)) == FDELETE) {
+			if (tp->flag & ALLOC) {
+				tp->flag &= ~ALLOC;
+				tfree(tp->val.t, tp->areap);
+			}
+			tp->flag = 0;
+		}
+		switch (i) {
+		case LRETURN:
+		case LERROR:
+			rv = exstat & 0xFF;
+			break;
+		case LINTR:
+		case LEXIT:
+		case LLEAVE:
+		case LSHELL:
+			quitenv(NULL);
+			unwind(i);
+			/* NOTREACHED */
+		default:
+			quitenv(NULL);
+			internal_errorf("%s %d", "CFUNC", i);
+		}
+		break;
+	}
+
+	/* executable command */
+	case CEXEC:
+	/* tracked alias */
+	case CTALIAS:
+		if (!(tp->flag&ISSET)) {
+			if (tp->u2.errnov == ENOENT) {
+				rv = 127;
+				warningf(true, "%s: %s", cp, "not found");
+			} else {
+				rv = 126;
+				warningf(true, "%s: %s: %s", cp, "can't execute",
+				    cstrerror(tp->u2.errnov));
+			}
+			break;
+		}
+
+		/* set $_ to programme's full path */
+		/* setstr() can't fail here */
+		setstr(typeset("_", LOCAL | EXPORT, 0, INTEGER, 0),
+		    tp->val.s, KSH_RETURN_ERROR);
+
+		if (flags&XEXEC) {
+			j_exit();
+			if (!(flags&XBGND)
+#ifndef MKSH_UNEMPLOYED
+			    || Flag(FMONITOR)
+#endif
+			    ) {
+				setexecsig(&sigtraps[SIGINT], SS_RESTORE_ORIG);
+				setexecsig(&sigtraps[SIGQUIT], SS_RESTORE_ORIG);
+			}
+		}
+
+		/* to fork we set up a TEXEC node and call execute */
+		texec.type = TEXEC;
+		/* for tprint */
+		texec.left = t;
+		texec.str = tp->val.s;
+		texec.args = ap;
+		rv = exchild(&texec, flags, xerrok, -1);
+		break;
+	}
+ Leave:
+	if (flags & XEXEC) {
+		exstat = rv & 0xFF;
+		unwind(LLEAVE);
+	}
+	return (rv);
+}
+
+static void
+scriptexec(struct op *tp, const char **ap)
+{
+	const char *sh;
+#ifndef MKSH_SMALL
+	unsigned char *cp;
+	/* 64 == MAXINTERP in MirBSD <sys/param.h> */
+	char buf[64];
+	int fd;
+#endif
+	union mksh_ccphack args, cap;
+
+	sh = str_val(global("EXECSHELL"));
+	if (sh && *sh)
+		sh = search_path(sh, path, X_OK, NULL);
+	if (!sh || !*sh)
+		sh = MKSH_DEFAULT_EXECSHELL;
+
+	*tp->args-- = tp->str;
+
+#ifndef MKSH_SMALL
+	if ((fd = open(tp->str, O_RDONLY | O_BINARY)) >= 0) {
+		/* read first MAXINTERP octets from file */
+		if (read(fd, buf, sizeof(buf)) <= 0)
+			/* read error -> no good */
+			buf[0] = '\0';
+		close(fd);
+
+		/* skip UTF-8 Byte Order Mark, if present */
+		cp = (unsigned char *)buf;
+		if ((cp[0] == 0xEF) && (cp[1] == 0xBB) && (cp[2] == 0xBF))
+			cp += 3;
+		/* save begin of shebang for later */
+		fd = (char *)cp - buf;		/* either 0 or (if BOM) 3 */
+
+		/* scan for newline (or CR) or NUL _before_ end of buffer */
+		while ((size_t)((char *)cp - buf) < sizeof(buf))
+			if (*cp == '\0' || *cp == '\n' || *cp == '\r') {
+				*cp = '\0';
+				break;
+			} else
+				++cp;
+		/* if the shebang line is longer than MAXINTERP, bail out */
+		if ((size_t)((char *)cp - buf) >= sizeof(buf))
+			goto noshebang;
+
+		/* restore begin of shebang position (buf+0 or buf+3) */
+		cp = (unsigned char *)(buf + fd);
+		/* bail out if read error (above) or no shebang */
+		if ((cp[0] != '#') || (cp[1] != '!'))
+			goto noshebang;
+
+		cp += 2;
+		/* skip whitespace before shell name */
+		while (*cp == ' ' || *cp == '\t')
+			++cp;
+		/* just whitespace on the line? */
+		if (*cp == '\0')
+			goto noshebang;
+		/* no, we actually found an interpreter name */
+		sh = (char *)cp;
+		/* look for end of shell/interpreter name */
+		while (*cp != ' ' && *cp != '\t' && *cp != '\0')
+			++cp;
+		/* any arguments? */
+		if (*cp) {
+			*cp++ = '\0';
+			/* skip spaces before arguments */
+			while (*cp == ' ' || *cp == '\t')
+				++cp;
+			/* pass it all in ONE argument (historic reasons) */
+			if (*cp)
+				*tp->args-- = (char *)cp;
+		}
+ noshebang:
+		if (buf[0] == 0x7F && buf[1] == 'E' && buf[2] == 'L' &&
+		    buf[3] == 'F')
+			errorf("%s: not executable: %d-bit ELF file", tp->str,
+			    32 * ((uint8_t)buf[4]));
+		fd = buf[0] << 8 | buf[1];
+		if ((fd == /* OMAGIC */ 0407) ||
+		    (fd == /* NMAGIC */ 0410) ||
+		    (fd == /* ZMAGIC */ 0413) ||
+		    (fd == /* QMAGIC */ 0314) ||
+		    (fd == /* ECOFF_I386 */ 0x4C01) ||
+		    (fd == /* ECOFF_M68K */ 0x0150 || fd == 0x5001) ||
+		    (fd == /* ECOFF_SH */   0x0500 || fd == 0x0005) ||
+		    (fd == /* "MZ" */ 0x4D5A) ||
+		    (fd == /* gzip */ 0x1F8B))
+			errorf("%s: not executable: magic %04X", tp->str, fd);
+	}
+#endif
+	args.ro = tp->args;
+	*args.ro = sh;
+
+	cap.ro = ap;
+	execve(args.rw[0], args.rw, cap.rw);
+
+	/* report both the programme that was run and the bogus interpreter */
+	errorf("%s: %s: %s", tp->str, sh, cstrerror(errno));
+}
+
+int
+shcomexec(const char **wp)
+{
+	struct tbl *tp;
+
+	tp = ktsearch(&builtins, *wp, hash(*wp));
+	return (call_builtin(tp, wp, "shcomexec"));
+}
+
+/*
+ * Search function tables for a function. If create set, a table entry
+ * is created if none is found.
+ */
+struct tbl *
+findfunc(const char *name, uint32_t h, bool create)
+{
+	struct block *l;
+	struct tbl *tp = NULL;
+
+	for (l = e->loc; l; l = l->next) {
+		tp = ktsearch(&l->funs, name, h);
+		if (tp)
+			break;
+		if (!l->next && create) {
+			tp = ktenter(&l->funs, name, h);
+			tp->flag = DEFINED;
+			tp->type = CFUNC;
+			tp->val.t = NULL;
+			break;
+		}
+	}
+	return (tp);
+}
+
+/*
+ * define function. Returns 1 if function is being undefined (t == 0) and
+ * function did not exist, returns 0 otherwise.
+ */
+int
+define(const char *name, struct op *t)
+{
+	uint32_t nhash;
+	struct tbl *tp;
+	bool was_set = false;
+
+	nhash = hash(name);
+
+	if (t != NULL && !tobool(t->u.ksh_func)) {
+		/* drop same-name aliases for POSIX functions */
+		if ((tp = ktsearch(&aliases, name, nhash)))
+			ktdelete(tp);
+	}
+
+	while (/* CONSTCOND */ 1) {
+		tp = findfunc(name, nhash, true);
+		/* because findfunc:create=true */
+		mkssert(tp != NULL);
+
+		if (tp->flag & ISSET)
+			was_set = true;
+		/*
+		 * If this function is currently being executed, we zap
+		 * this table entry so findfunc() won't see it
+		 */
+		if (tp->flag & FINUSE) {
+			tp->name[0] = '\0';
+			/* ensure it won't be found */
+			tp->flag &= ~DEFINED;
+			tp->flag |= FDELETE;
+		} else
+			break;
+	}
+
+	if (tp->flag & ALLOC) {
+		tp->flag &= ~(ISSET|ALLOC);
+		tfree(tp->val.t, tp->areap);
+	}
+
+	if (t == NULL) {
+		/* undefine */
+		ktdelete(tp);
+		return (was_set ? 0 : 1);
+	}
+
+	tp->val.t = tcopy(t->left, tp->areap);
+	tp->flag |= (ISSET|ALLOC);
+	if (t->u.ksh_func)
+		tp->flag |= FKSH;
+
+	return (0);
+}
+
+/*
+ * add builtin
+ */
+const char *
+builtin(const char *name, int (*func) (const char **))
+{
+	struct tbl *tp;
+	uint32_t flag = DEFINED;
+
+	/* see if any flags should be set for this builtin */
+	while (1) {
+		if (*name == '=')
+			/* command does variable assignment */
+			flag |= KEEPASN;
+		else if (*name == '*')
+			/* POSIX special builtin */
+			flag |= SPEC_BI;
+		else
+			break;
+		name++;
+	}
+
+	tp = ktenter(&builtins, name, hash(name));
+	tp->flag = flag;
+	tp->type = CSHELL;
+	tp->val.f = func;
+
+	return (name);
+}
+
+/*
+ * find command
+ * either function, hashed command, or built-in (in that order)
+ */
+struct tbl *
+findcom(const char *name, int flags)
+{
+	static struct tbl temp;
+	uint32_t h = hash(name);
+	struct tbl *tp = NULL, *tbi;
+	/* insert if not found */
+	unsigned char insert = Flag(FTRACKALL);
+	/* for function autoloading */
+	char *fpath;
+	union mksh_cchack npath;
+
+	if (vstrchr(name, '/')) {
+		insert = 0;
+		/* prevent FPATH search below */
+		flags &= ~FC_FUNC;
+		goto Search;
+	}
+	tbi = (flags & FC_BI) ? ktsearch(&builtins, name, h) : NULL;
+	/*
+	 * POSIX says special builtins first, then functions, then
+	 * regular builtins, then search path...
+	 */
+	if ((flags & FC_SPECBI) && tbi && (tbi->flag & SPEC_BI))
+		tp = tbi;
+	if (!tp && (flags & FC_FUNC)) {
+		tp = findfunc(name, h, false);
+		if (tp && !(tp->flag & ISSET)) {
+			if ((fpath = str_val(global("FPATH"))) == null) {
+				tp->u.fpath = NULL;
+				tp->u2.errnov = ENOENT;
+			} else
+				tp->u.fpath = search_path(name, fpath, R_OK,
+				    &tp->u2.errnov);
+		}
+	}
+	if (!tp && (flags & FC_NORMBI) && tbi)
+		tp = tbi;
+	if (!tp && (flags & FC_PATH) && !(flags & FC_DEFPATH)) {
+		tp = ktsearch(&taliases, name, h);
+		if (tp && (tp->flag & ISSET) &&
+		    ksh_access(tp->val.s, X_OK) != 0) {
+			if (tp->flag & ALLOC) {
+				tp->flag &= ~ALLOC;
+				afree(tp->val.s, APERM);
+			}
+			tp->flag &= ~ISSET;
+		}
+	}
+
+ Search:
+	if ((!tp || (tp->type == CTALIAS && !(tp->flag&ISSET))) &&
+	    (flags & FC_PATH)) {
+		if (!tp) {
+			if (insert && !(flags & FC_DEFPATH)) {
+				tp = ktenter(&taliases, name, h);
+				tp->type = CTALIAS;
+			} else {
+				tp = &temp;
+				tp->type = CEXEC;
+			}
+			/* make ~ISSET */
+			tp->flag = DEFINED;
+		}
+		npath.ro = search_path(name,
+		    (flags & FC_DEFPATH) ? def_path : path,
+		    X_OK, &tp->u2.errnov);
+		if (npath.ro) {
+			strdupx(tp->val.s, npath.ro, APERM);
+			if (npath.ro != name)
+				afree(npath.rw, ATEMP);
+			tp->flag |= ISSET|ALLOC;
+		} else if ((flags & FC_FUNC) &&
+		    (fpath = str_val(global("FPATH"))) != null &&
+		    (npath.ro = search_path(name, fpath, R_OK,
+		    &tp->u2.errnov)) != NULL) {
+			/*
+			 * An undocumented feature of AT&T ksh is that
+			 * it searches FPATH if a command is not found,
+			 * even if the command hasn't been set up as an
+			 * autoloaded function (ie, no typeset -uf).
+			 */
+			tp = &temp;
+			tp->type = CFUNC;
+			/* make ~ISSET */
+			tp->flag = DEFINED;
+			tp->u.fpath = npath.ro;
+		}
+	}
+	return (tp);
+}
+
+/*
+ * flush executable commands with relative paths
+ * (just relative or all?)
+ */
+void
+flushcom(bool all)
+{
+	struct tbl *tp;
+	struct tstate ts;
+
+	for (ktwalk(&ts, &taliases); (tp = ktnext(&ts)) != NULL; )
+		if ((tp->flag&ISSET) && (all || tp->val.s[0] != '/')) {
+			if (tp->flag&ALLOC) {
+				tp->flag &= ~(ALLOC|ISSET);
+				afree(tp->val.s, APERM);
+			}
+			tp->flag &= ~ISSET;
+		}
+}
+
+/* check if path is something we want to find */
+static int
+search_access(const char *fn, int mode)
+{
+	struct stat sb;
+
+	if (stat(fn, &sb) < 0)
+		/* file does not exist */
+		return (ENOENT);
+	/* LINTED use of access */
+	if (access(fn, mode) < 0)
+		/* file exists, but we can't access it */
+		return (errno);
+	if (mode == X_OK && (!S_ISREG(sb.st_mode) ||
+	    !(sb.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH))))
+		/* access(2) may say root can execute everything */
+		return (S_ISDIR(sb.st_mode) ? EISDIR : EACCES);
+	return (0);
+}
+
+/*
+ * search for command with PATH
+ */
+const char *
+search_path(const char *name, const char *lpath,
+    /* R_OK or X_OK */
+    int mode,
+    /* set if candidate found, but not suitable */
+    int *errnop)
+{
+	const char *sp, *p;
+	char *xp;
+	XString xs;
+	size_t namelen;
+	int ec = 0, ev;
+
+	if (vstrchr(name, '/')) {
+		if ((ec = search_access(name, mode)) == 0) {
+ search_path_ok:
+			if (errnop)
+				*errnop = 0;
+			return (name);
+		}
+		goto search_path_err;
+	}
+
+	namelen = strlen(name) + 1;
+	Xinit(xs, xp, 128, ATEMP);
+
+	sp = lpath;
+	while (sp != NULL) {
+		xp = Xstring(xs, xp);
+		if (!(p = cstrchr(sp, ':')))
+			p = sp + strlen(sp);
+		if (p != sp) {
+			XcheckN(xs, xp, p - sp);
+			memcpy(xp, sp, p - sp);
+			xp += p - sp;
+			*xp++ = '/';
+		}
+		sp = p;
+		XcheckN(xs, xp, namelen);
+		memcpy(xp, name, namelen);
+		if ((ev = search_access(Xstring(xs, xp), mode)) == 0) {
+			name = Xclose(xs, xp + namelen);
+			goto search_path_ok;
+		}
+		/* accumulate non-ENOENT errors only */
+		if (ev != ENOENT && ec == 0)
+			ec = ev;
+		if (*sp++ == '\0')
+			sp = NULL;
+	}
+	Xfree(xs, xp);
+ search_path_err:
+	if (errnop)
+		*errnop = ec ? ec : ENOENT;
+	return (NULL);
+}
+
+static int
+call_builtin(struct tbl *tp, const char **wp, const char *where)
+{
+	int rv;
+
+	if (!tp)
+		internal_errorf("%s: %s", where, wp[0]);
+	builtin_argv0 = wp[0];
+	builtin_flag = tp->flag;
+	shf_reopen(1, SHF_WR, shl_stdout);
+	shl_stdout_ok = true;
+	ksh_getopt_reset(&builtin_opt, GF_ERROR);
+	rv = (*tp->val.f)(wp);
+	shf_flush(shl_stdout);
+	shl_stdout_ok = false;
+	builtin_flag = 0;
+	builtin_argv0 = NULL;
+	return (rv);
+}
+
+/*
+ * set up redirection, saving old fds in e->savefd
+ */
+static int
+iosetup(struct ioword *iop, struct tbl *tp)
+{
+	int u = -1;
+	char *cp = iop->name;
+	int iotype = iop->flag & IOTYPE;
+	bool do_open = true, do_close = false;
+	int flags = 0;
+	struct ioword iotmp;
+	struct stat statb;
+
+	if (iotype != IOHERE)
+		cp = evalonestr(cp, DOTILDE|(Flag(FTALKING_I) ? DOGLOB : 0));
+
+	/* Used for tracing and error messages to print expanded cp */
+	iotmp = *iop;
+	iotmp.name = (iotype == IOHERE) ? NULL : cp;
+	iotmp.flag |= IONAMEXP;
+
+	if (Flag(FXTRACE)) {
+		change_xtrace(2, false);
+		fptreef(shl_xtrace, 0, "%R", &iotmp);
+		change_xtrace(1, false);
+	}
+
+	switch (iotype) {
+	case IOREAD:
+		flags = O_RDONLY;
+		break;
+
+	case IOCAT:
+		flags = O_WRONLY | O_APPEND | O_CREAT;
+		break;
+
+	case IOWRITE:
+		flags = O_WRONLY | O_CREAT | O_TRUNC;
+		/*
+		 * The stat() is here to allow redirections to
+		 * things like /dev/null without error.
+		 */
+		if (Flag(FNOCLOBBER) && !(iop->flag & IOCLOB) &&
+		    (stat(cp, &statb) < 0 || S_ISREG(statb.st_mode)))
+			flags |= O_EXCL;
+		break;
+
+	case IORDWR:
+		flags = O_RDWR | O_CREAT;
+		break;
+
+	case IOHERE:
+		do_open = false;
+		/* herein() returns -2 if error has been printed */
+		u = herein(iop, NULL);
+		/* cp may have wrong name */
+		break;
+
+	case IODUP: {
+		const char *emsg;
+
+		do_open = false;
+		if (*cp == '-' && !cp[1]) {
+			/* prevent error return below */
+			u = 1009;
+			do_close = true;
+		} else if ((u = check_fd(cp,
+		    X_OK | ((iop->flag & IORDUP) ? R_OK : W_OK),
+		    &emsg)) < 0) {
+			char *sp;
+
+			warningf(true, "%s: %s",
+			    (sp = snptreef(NULL, 32, "%R", &iotmp)), emsg);
+			afree(sp, ATEMP);
+			return (-1);
+		}
+		if (u == iop->unit)
+			/* "dup from" == "dup to" */
+			return (0);
+		break;
+	    }
+	}
+
+	if (do_open) {
+		if (Flag(FRESTRICTED) && (flags & O_CREAT)) {
+			warningf(true, "%s: %s", cp, "restricted");
+			return (-1);
+		}
+		u = open(cp, flags | O_BINARY, 0666);
+	}
+	if (u < 0) {
+		/* herein() may already have printed message */
+		if (u == -1) {
+			u = errno;
+			warningf(true, "can't %s %s: %s",
+			    iotype == IODUP ? "dup" :
+			    (iotype == IOREAD || iotype == IOHERE) ?
+			    "open" : "create", cp, cstrerror(u));
+		}
+		return (-1);
+	}
+	/* Do not save if it has already been redirected (i.e. "cat >x >y"). */
+	if (e->savefd[iop->unit] == 0) {
+		/* If these are the same, it means unit was previously closed */
+		if (u == iop->unit)
+			e->savefd[iop->unit] = -1;
+		else
+			/*
+			 * c_exec() assumes e->savefd[fd] set for any
+			 * redirections. Ask savefd() not to close iop->unit;
+			 * this allows error messages to be seen if iop->unit
+			 * is 2; also means we can't lose the fd (eg, both
+			 * dup2 below and dup2 in restfd() failing).
+			 */
+			e->savefd[iop->unit] = savefd(iop->unit);
+	}
+
+	if (do_close)
+		close(iop->unit);
+	else if (u != iop->unit) {
+		if (ksh_dup2(u, iop->unit, true) < 0) {
+			int eno;
+			char *sp;
+
+			eno = errno;
+			warningf(true, "%s %s %s",
+			    "can't finish (dup) redirection",
+			    (sp = snptreef(NULL, 32, "%R", &iotmp)),
+			    cstrerror(eno));
+			afree(sp, ATEMP);
+			if (iotype != IODUP)
+				close(u);
+			return (-1);
+		}
+		if (iotype != IODUP)
+			close(u);
+		/*
+		 * Touching any co-process fd in an empty exec
+		 * causes the shell to close its copies
+		 */
+		else if (tp && tp->type == CSHELL && tp->val.f == c_exec) {
+			if (iop->flag & IORDUP)
+				/* possible exec <&p */
+				coproc_read_close(u);
+			else
+				/* possible exec >&p */
+				coproc_write_close(u);
+		}
+	}
+	if (u == 2)
+		/* Clear any write errors */
+		shf_reopen(2, SHF_WR, shl_out);
+	return (0);
+}
+
+/*
+ * Process here documents by providing the content, either as
+ * result (globally allocated) string or in a temp file; if
+ * unquoted, the string is expanded first.
+ */
+static int
+hereinval(const char *content, int sub, char **resbuf, struct shf *shf)
+{
+	const char * volatile ccp = content;
+	struct source *s, *osource;
+
+	osource = source;
+	newenv(E_ERRH);
+	if (kshsetjmp(e->jbuf)) {
+		source = osource;
+		quitenv(shf);
+		/* special to iosetup(): don't print error */
+		return (-2);
+	}
+	if (sub) {
+		/* do substitutions on the content of heredoc */
+		s = pushs(SSTRING, ATEMP);
+		s->start = s->str = ccp;
+		source = s;
+		if (yylex(sub) != LWORD)
+			internal_errorf("%s: %s", "herein", "yylex");
+		source = osource;
+		ccp = evalstr(yylval.cp, 0);
+	}
+
+	if (resbuf == NULL)
+		shf_puts(ccp, shf);
+	else
+		strdupx(*resbuf, ccp, APERM);
+
+	quitenv(NULL);
+	return (0);
+}
+
+static int
+herein(struct ioword *iop, char **resbuf)
+{
+	int fd = -1;
+	struct shf *shf;
+	struct temp *h;
+	int i;
+
+	/* ksh -c 'cat << EOF' can cause this... */
+	if (iop->heredoc == NULL) {
+		warningf(true, "%s missing", "here document");
+		/* special to iosetup(): don't print error */
+		return (-2);
+	}
+
+	/* lexer substitution flags */
+	i = (iop->flag & IOEVAL) ? (ONEWORD | HEREDOC) : 0;
+
+	/* skip all the fd setup if we just want the value */
+	if (resbuf != NULL)
+		return (hereinval(iop->heredoc, i, resbuf, NULL));
+
+	/*
+	 * Create temp file to hold content (done before newenv
+	 * so temp doesn't get removed too soon).
+	 */
+	h = maketemp(ATEMP, TT_HEREDOC_EXP, &e->temps);
+	if (!(shf = h->shf) || (fd = open(h->tffn, O_RDONLY | O_BINARY, 0)) < 0) {
+		i = errno;
+		warningf(true, "can't %s temporary file %s: %s",
+		    !shf ? "create" : "open", h->tffn, cstrerror(i));
+		if (shf)
+			shf_close(shf);
+		/* special to iosetup(): don't print error */
+		return (-2);
+	}
+
+	if (hereinval(iop->heredoc, i, NULL, shf) == -2) {
+		close(fd);
+		/* special to iosetup(): don't print error */
+		return (-2);
+	}
+
+	if (shf_close(shf) == EOF) {
+		i = errno;
+		close(fd);
+		warningf(true, "can't %s temporary file %s: %s",
+		    "write", h->tffn, cstrerror(i));
+		/* special to iosetup(): don't print error */
+		return (-2);
+	}
+
+	return (fd);
+}
+
+/*
+ *	ksh special - the select command processing section
+ *	print the args in column form - assuming that we can
+ */
+static const char *
+do_selectargs(const char **ap, bool print_menu)
+{
+	static const char *read_args[] = {
+		"read", "-r", "REPLY", NULL
+	};
+	char *s;
+	int i, argct;
+
+	for (argct = 0; ap[argct]; argct++)
+		;
+	while (/* CONSTCOND */ 1) {
+		/*-
+		 * Menu is printed if
+		 *	- this is the first time around the select loop
+		 *	- the user enters a blank line
+		 *	- the REPLY parameter is empty
+		 */
+		if (print_menu || !*str_val(global("REPLY")))
+			pr_menu(ap);
+		shellf("%s", str_val(global("PS3")));
+		if (call_builtin(findcom("read", FC_BI), read_args, Tselect))
+			return (NULL);
+		s = str_val(global("REPLY"));
+		if (*s && getn(s, &i))
+			return ((i >= 1 && i <= argct) ? ap[i - 1] : null);
+		print_menu = true;
+	}
+}
+
+struct select_menu_info {
+	const char * const *args;
+	int num_width;
+};
+
+/* format a single select menu item */
+static char *
+select_fmt_entry(char *buf, size_t buflen, unsigned int i, const void *arg)
+{
+	const struct select_menu_info *smi =
+	    (const struct select_menu_info *)arg;
+
+	shf_snprintf(buf, buflen, "%*u) %s",
+	    smi->num_width, i + 1, smi->args[i]);
+	return (buf);
+}
+
+/*
+ *	print a select style menu
+ */
+void
+pr_menu(const char * const *ap)
+{
+	struct select_menu_info smi;
+	const char * const *pp;
+	size_t acols = 0, aocts = 0, i;
+	unsigned int n;
+
+	/*
+	 * width/column calculations were done once and saved, but this
+	 * means select can't be used recursively so we re-calculate
+	 * each time (could save in a structure that is returned, but
+	 * it's probably not worth the bother)
+	 */
+
+	/*
+	 * get dimensions of the list
+	 */
+	for (n = 0, pp = ap; *pp; n++, pp++) {
+		i = strlen(*pp);
+		if (i > aocts)
+			aocts = i;
+		i = utf_mbswidth(*pp);
+		if (i > acols)
+			acols = i;
+	}
+
+	/*
+	 * we will print an index of the form "%d) " in front of
+	 * each entry, so get the maximum width of this
+	 */
+	for (i = n, smi.num_width = 1; i >= 10; i /= 10)
+		smi.num_width++;
+
+	smi.args = ap;
+	print_columns(shl_out, n, select_fmt_entry, (void *)&smi,
+	    smi.num_width + 2 + aocts, smi.num_width + 2 + acols,
+	    true);
+}
+
+static char *
+plain_fmt_entry(char *buf, size_t buflen, unsigned int i, const void *arg)
+{
+	strlcpy(buf, ((const char * const *)arg)[i], buflen);
+	return (buf);
+}
+
+void
+pr_list(char * const *ap)
+{
+	size_t acols = 0, aocts = 0, i;
+	unsigned int n;
+	char * const *pp;
+
+	for (n = 0, pp = ap; *pp; n++, pp++) {
+		i = strlen(*pp);
+		if (i > aocts)
+			aocts = i;
+		i = utf_mbswidth(*pp);
+		if (i > acols)
+			acols = i;
+	}
+
+	print_columns(shl_out, n, plain_fmt_entry, (const void *)ap,
+	    aocts, acols, false);
+}
+
+/*
+ *	[[ ... ]] evaluation routines
+ */
+
+/*
+ * Test if the current token is a whatever. Accepts the current token if
+ * it is. Returns 0 if it is not, non-zero if it is (in the case of
+ * TM_UNOP and TM_BINOP, the returned value is a Test_op).
+ */
+static Test_op
+dbteste_isa(Test_env *te, Test_meta meta)
+{
+	Test_op ret = TO_NONOP;
+	bool uqword;
+	const char *p;
+
+	if (!*te->pos.wp)
+		return (meta == TM_END ? TO_NONNULL : TO_NONOP);
+
+	/* unquoted word? */
+	for (p = *te->pos.wp; *p == CHAR; p += 2)
+		;
+	uqword = *p == EOS;
+
+	if (meta == TM_UNOP || meta == TM_BINOP) {
+		if (uqword) {
+			/* longer than the longest operator */
+			char buf[8];
+			char *q = buf;
+
+			p = *te->pos.wp;
+			while (*p++ == CHAR &&
+			    (size_t)(q - buf) < sizeof(buf) - 1)
+				*q++ = *p++;
+			*q = '\0';
+			ret = test_isop(meta, buf);
+		}
+	} else if (meta == TM_END)
+		ret = TO_NONOP;
+	else
+		ret = (uqword && !strcmp(*te->pos.wp,
+		    dbtest_tokens[(int)meta])) ? TO_NONNULL : TO_NONOP;
+
+	/* Accept the token? */
+	if (ret != TO_NONOP)
+		te->pos.wp++;
+
+	return (ret);
+}
+
+static const char *
+dbteste_getopnd(Test_env *te, Test_op op, bool do_eval)
+{
+	const char *s = *te->pos.wp;
+
+	if (!s)
+		return (NULL);
+
+	te->pos.wp++;
+
+	if (!do_eval)
+		return (null);
+
+	if (op == TO_STEQL || op == TO_STNEQ)
+		s = evalstr(s, DOTILDE | DOPAT);
+	else
+		s = evalstr(s, DOTILDE);
+
+	return (s);
+}
+
+static void
+dbteste_error(Test_env *te, int offset, const char *msg)
+{
+	te->flags |= TEF_ERROR;
+	internal_warningf("dbteste_error: %s (offset %d)", msg, offset);
+}

Deleted: vendor/MirOS/mksh/R50/expr.c
===================================================================
--- vendor/MirOS/mksh/dist/expr.c	2014-06-30 23:58:12 UTC (rev 6706)
+++ vendor/MirOS/mksh/R50/expr.c	2014-07-01 12:01:17 UTC (rev 6708)
@@ -1,1191 +0,0 @@
-/*	$OpenBSD: expr.c,v 1.22 2013/03/28 08:39:28 nicm Exp $	*/
-
-/*-
- * Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
- *		 2011, 2012, 2013
- *	Thorsten Glaser <tg at mirbsd.org>
- *
- * Provided that these terms and disclaimer and all copyright notices
- * are retained or reproduced in an accompanying document, permission
- * is granted to deal in this work without restriction, including un-
- * limited rights to use, publicly perform, distribute, sell, modify,
- * merge, give away, or sublicence.
- *
- * This work is provided "AS IS" and WITHOUT WARRANTY of any kind, to
- * the utmost extent permitted by applicable law, neither express nor
- * implied; without malicious intent or gross negligence. In no event
- * may a licensor, author or contributor be held liable for indirect,
- * direct, other damage, loss, or other issues arising in any way out
- * of dealing in the work, even if advised of the possibility of such
- * damage or existence of a defect, except proven that it results out
- * of said person's immediate fault when using the work as intended.
- */
-
-#include "sh.h"
-
-__RCSID("$MirOS: src/bin/mksh/expr.c,v 1.72 2013/07/21 18:38:56 tg Exp $");
-
-/* the order of these enums is constrained by the order of opinfo[] */
-enum token {
-	/* some (long) unary operators */
-	O_PLUSPLUS = 0, O_MINUSMINUS,
-	/* binary operators */
-	O_EQ, O_NE,
-	/* assignments are assumed to be in range O_ASN .. O_BORASN */
-	O_ASN, O_TIMESASN, O_DIVASN, O_MODASN, O_PLUSASN, O_MINUSASN,
-#ifndef MKSH_LEGACY_MODE
-	O_ROLASN, O_RORASN,
-#endif
-	O_LSHIFTASN, O_RSHIFTASN, O_BANDASN, O_BXORASN, O_BORASN,
-	/* binary non-assignment operators */
-#ifndef MKSH_LEGACY_MODE
-	O_ROL, O_ROR,
-#endif
-	O_LSHIFT, O_RSHIFT,
-	O_LE, O_GE, O_LT, O_GT,
-	O_LAND,
-	O_LOR,
-	O_TIMES, O_DIV, O_MOD,
-	O_PLUS, O_MINUS,
-	O_BAND,
-	O_BXOR,
-	O_BOR,
-	O_TERN,
-	O_COMMA,
-	/* things after this aren't used as binary operators */
-	/* unary that are not also binaries */
-	O_BNOT, O_LNOT,
-	/* misc */
-	OPEN_PAREN, CLOSE_PAREN, CTERN,
-	/* things that don't appear in the opinfo[] table */
-	VAR, LIT, END, BAD
-};
-#define IS_ASSIGNOP(op)	((int)(op) >= (int)O_ASN && (int)(op) <= (int)O_BORASN)
-
-/* precisions; used to be enum prec but we do arithmetics on it */
-#define P_PRIMARY	0	/* VAR, LIT, (), ! ~ ++ -- */
-#define P_MULT		1	/* * / % */
-#define P_ADD		2	/* + - */
-#define P_SHIFT		3	/* <<< >>> << >> */
-#define P_RELATION	4	/* < <= > >= */
-#define P_EQUALITY	5	/* == != */
-#define P_BAND		6	/* & */
-#define P_BXOR		7	/* ^ */
-#define P_BOR		8	/* | */
-#define P_LAND		9	/* && */
-#define P_LOR		10	/* || */
-#define P_TERN		11	/* ?: */
-	/* = += -= *= /= %= <<<= >>>= <<= >>= &= ^= |= */
-#define P_ASSIGN	12
-#define P_COMMA		13	/* , */
-#define MAX_PREC	P_COMMA
-
-struct opinfo {
-	char name[5];
-	/* name length */
-	uint8_t len;
-	/* precedence: lower is higher */
-	uint8_t prec;
-};
-
-/*
- * Tokens in this table must be ordered so the longest are first
- * (eg, += before +). If you change something, change the order
- * of enum token too.
- */
-static const struct opinfo opinfo[] = {
-	{ "++",   2, P_PRIMARY },	/* before + */
-	{ "--",   2, P_PRIMARY },	/* before - */
-	{ "==",   2, P_EQUALITY },	/* before = */
-	{ "!=",   2, P_EQUALITY },	/* before ! */
-	{ "=",    1, P_ASSIGN },	/* keep assigns in a block */
-	{ "*=",   2, P_ASSIGN },
-	{ "/=",   2, P_ASSIGN },
-	{ "%=",   2, P_ASSIGN },
-	{ "+=",   2, P_ASSIGN },
-	{ "-=",   2, P_ASSIGN },
-#ifndef MKSH_LEGACY_MODE
-	{ "<<<=", 4, P_ASSIGN },	/* before <<< */
-	{ ">>>=", 4, P_ASSIGN },	/* before >>> */
-#endif
-	{ "<<=",  3, P_ASSIGN },
-	{ ">>=",  3, P_ASSIGN },
-	{ "&=",   2, P_ASSIGN },
-	{ "^=",   2, P_ASSIGN },
-	{ "|=",   2, P_ASSIGN },
-#ifndef MKSH_LEGACY_MODE
-	{ "<<<",  3, P_SHIFT },		/* before << */
-	{ ">>>",  3, P_SHIFT },		/* before >> */
-#endif
-	{ "<<",   2, P_SHIFT },
-	{ ">>",   2, P_SHIFT },
-	{ "<=",   2, P_RELATION },
-	{ ">=",   2, P_RELATION },
-	{ "<",    1, P_RELATION },
-	{ ">",    1, P_RELATION },
-	{ "&&",   2, P_LAND },
-	{ "||",   2, P_LOR },
-	{ "*",    1, P_MULT },
-	{ "/",    1, P_MULT },
-	{ "%",    1, P_MULT },
-	{ "+",    1, P_ADD },
-	{ "-",    1, P_ADD },
-	{ "&",    1, P_BAND },
-	{ "^",    1, P_BXOR },
-	{ "|",    1, P_BOR },
-	{ "?",    1, P_TERN },
-	{ ",",    1, P_COMMA },
-	{ "~",    1, P_PRIMARY },
-	{ "!",    1, P_PRIMARY },
-	{ "(",    1, P_PRIMARY },
-	{ ")",    1, P_PRIMARY },
-	{ ":",    1, P_PRIMARY },
-	{ "",     0, P_PRIMARY }
-};
-
-typedef struct expr_state {
-	/* expression being evaluated */
-	const char *expression;
-	/* lexical position */
-	const char *tokp;
-	/* value from token() */
-	struct tbl *val;
-	/* variable that is being recursively expanded (EXPRINEVAL flag set) */
-	struct tbl *evaling;
-	/* token from token() */
-	enum token tok;
-	/* don't do assignments (for ?:, &&, ||) */
-	uint8_t noassign;
-	/* evaluating an $(()) expression? */
-	bool arith;
-	/* unsigned arithmetic calculation */
-	bool natural;
-} Expr_state;
-
-enum error_type {
-	ET_UNEXPECTED, ET_BADLIT, ET_RECURSIVE,
-	ET_LVALUE, ET_RDONLY, ET_STR
-};
-
-static void evalerr(Expr_state *, enum error_type, const char *)
-    MKSH_A_NORETURN;
-static struct tbl *evalexpr(Expr_state *, unsigned int);
-static void exprtoken(Expr_state *);
-static struct tbl *do_ppmm(Expr_state *, enum token, struct tbl *, bool);
-static void assign_check(Expr_state *, enum token, struct tbl *);
-static struct tbl *intvar(Expr_state *, struct tbl *);
-
-/*
- * parse and evaluate expression
- */
-int
-evaluate(const char *expr, mksh_ari_t *rval, int error_ok, bool arith)
-{
-	struct tbl v;
-	int ret;
-
-	v.flag = DEFINED | INTEGER;
-	v.type = 0;
-	ret = v_evaluate(&v, expr, error_ok, arith);
-	*rval = v.val.i;
-	return (ret);
-}
-
-/*
- * parse and evaluate expression, storing result in vp.
- */
-int
-v_evaluate(struct tbl *vp, const char *expr, volatile int error_ok,
-    bool arith)
-{
-	struct tbl *v;
-	Expr_state curstate;
-	Expr_state * const es = &curstate;
-	int i;
-
-	/* save state to allow recursive calls */
-	memset(&curstate, 0, sizeof(curstate));
-	curstate.expression = curstate.tokp = expr;
-	curstate.tok = BAD;
-	curstate.arith = arith;
-
-	newenv(E_ERRH);
-	if ((i = kshsetjmp(e->jbuf))) {
-		/* Clear EXPRINEVAL in of any variables we were playing with */
-		if (curstate.evaling)
-			curstate.evaling->flag &= ~EXPRINEVAL;
-		quitenv(NULL);
-		if (i == LAEXPR) {
-			if (error_ok == KSH_RETURN_ERROR)
-				return (0);
-			errorfz();
-		}
-		unwind(i);
-		/* NOTREACHED */
-	}
-
-	exprtoken(es);
-	if (es->tok == END) {
-		es->tok = LIT;
-		es->val = tempvar();
-	}
-	v = intvar(es, evalexpr(es, MAX_PREC));
-
-	if (es->tok != END)
-		evalerr(es, ET_UNEXPECTED, NULL);
-
-	if (es->arith && es->natural)
-		vp->flag |= INT_U;
-	if (vp->flag & INTEGER)
-		setint_v(vp, v, es->arith);
-	else
-		/* can fail if readonly */
-		setstr(vp, str_val(v), error_ok);
-
-	quitenv(NULL);
-
-	return (1);
-}
-
-static void
-evalerr(Expr_state *es, enum error_type type, const char *str)
-{
-	char tbuf[2];
-	const char *s;
-
-	es->arith = false;
-	switch (type) {
-	case ET_UNEXPECTED:
-		switch (es->tok) {
-		case VAR:
-			s = es->val->name;
-			break;
-		case LIT:
-			s = str_val(es->val);
-			break;
-		case END:
-			s = "end of expression";
-			break;
-		case BAD:
-			tbuf[0] = *es->tokp;
-			tbuf[1] = '\0';
-			s = tbuf;
-			break;
-		default:
-			s = opinfo[(int)es->tok].name;
-		}
-		warningf(true, "%s: %s '%s'", es->expression,
-		    "unexpected", s);
-		break;
-
-	case ET_BADLIT:
-		warningf(true, "%s: %s '%s'", es->expression,
-		    "bad number", str);
-		break;
-
-	case ET_RECURSIVE:
-		warningf(true, "%s: %s '%s'", es->expression,
-		    "expression recurses on parameter", str);
-		break;
-
-	case ET_LVALUE:
-		warningf(true, "%s: %s %s",
-		    es->expression, str, "requires lvalue");
-		break;
-
-	case ET_RDONLY:
-		warningf(true, "%s: %s %s",
-		    es->expression, str, "applied to read-only variable");
-		break;
-
-	default: /* keep gcc happy */
-	case ET_STR:
-		warningf(true, "%s: %s", es->expression, str);
-		break;
-	}
-	unwind(LAEXPR);
-}
-
-/* do a ++ or -- operation */
-static struct tbl *
-do_ppmm(Expr_state *es, enum token op, struct tbl *vasn, bool is_prefix)
-{
-	struct tbl *vl;
-	mksh_uari_t oval;
-
-	assign_check(es, op, vasn);
-
-	vl = intvar(es, vasn);
-	oval = vl->val.u;
-	if (op == O_PLUSPLUS)
-		++vl->val.u;
-	else
-		--vl->val.u;
-	if (!es->noassign) {
-		if (vasn->flag & INTEGER)
-			setint_v(vasn, vl, es->arith);
-		else
-			setint(vasn, vl->val.i);
-	}
-	if (!is_prefix)
-		/* undo the increment/decrement */
-		vl->val.u = oval;
-
-	return (vl);
-}
-
-static struct tbl *
-evalexpr(Expr_state *es, unsigned int prec)
-{
-	struct tbl *vl, *vr = NULL, *vasn;
-	enum token op;
-	mksh_uari_t res = 0, t1, t2, t3;
-
-	if (prec == P_PRIMARY) {
-		switch ((int)(op = es->tok)) {
-		case O_BNOT:
-		case O_LNOT:
-		case O_MINUS:
-		case O_PLUS:
-			exprtoken(es);
-			vl = intvar(es, evalexpr(es, P_PRIMARY));
-			switch ((int)op) {
-			case O_BNOT:
-				vl->val.u = ~vl->val.u;
-				break;
-			case O_LNOT:
-				vl->val.u = !vl->val.u;
-				break;
-			case O_MINUS:
-				vl->val.u = -vl->val.u;
-				break;
-			case O_PLUS:
-				/* nop */
-				break;
-			}
-			break;
-
-		case OPEN_PAREN:
-			exprtoken(es);
-			vl = evalexpr(es, MAX_PREC);
-			if (es->tok != CLOSE_PAREN)
-				evalerr(es, ET_STR, "missing )");
-			exprtoken(es);
-			break;
-
-		case O_PLUSPLUS:
-		case O_MINUSMINUS:
-			exprtoken(es);
-			vl = do_ppmm(es, op, es->val, true);
-			exprtoken(es);
-			break;
-
-		case VAR:
-		case LIT:
-			vl = es->val;
-			exprtoken(es);
-			break;
-
-		default:
-			evalerr(es, ET_UNEXPECTED, NULL);
-			/* NOTREACHED */
-		}
-
-		if (es->tok == O_PLUSPLUS || es->tok == O_MINUSMINUS) {
-			vl = do_ppmm(es, es->tok, vl, false);
-			exprtoken(es);
-		}
-
-		return (vl);
-		/* prec == P_PRIMARY */
-	}
-
-	vl = evalexpr(es, prec - 1);
-	while ((int)(op = es->tok) >= (int)O_EQ && (int)op <= (int)O_COMMA &&
-	    opinfo[(int)op].prec == prec) {
-		exprtoken(es);
-		vasn = vl;
-		if (op != O_ASN)
-			/* vl may not have a value yet */
-			vl = intvar(es, vl);
-		if (IS_ASSIGNOP(op)) {
-			if (!es->noassign)
-				assign_check(es, op, vasn);
-			vr = intvar(es, evalexpr(es, P_ASSIGN));
-		} else if (op == O_TERN) {
-			bool ev = vl->val.u != 0;
-
-			if (!ev)
-				es->noassign++;
-			vl = evalexpr(es, MAX_PREC);
-			if (!ev)
-				es->noassign--;
-			if (es->tok != CTERN)
-				evalerr(es, ET_STR, "missing :");
-			exprtoken(es);
-			if (ev)
-				es->noassign++;
-			vr = evalexpr(es, P_TERN);
-			if (ev)
-				es->noassign--;
-			vl = ev ? vl : vr;
-			continue;
-		} else if (op != O_LAND && op != O_LOR)
-			vr = intvar(es, evalexpr(es, prec - 1));
-
-		/* common ops setup */
-		switch ((int)op) {
-		case O_DIV:
-		case O_DIVASN:
-		case O_MOD:
-		case O_MODASN:
-			if (vr->val.u == 0) {
-				if (!es->noassign)
-					evalerr(es, ET_STR, "zero divisor");
-				vr->val.u = 1;
-			}
-			/* calculate the absolute values */
-			t1 = vl->val.i < 0 ? -vl->val.u : vl->val.u;
-			t2 = vr->val.i < 0 ? -vr->val.u : vr->val.u;
-			break;
-#ifndef MKSH_LEGACY_MODE
-		case O_LSHIFT:
-		case O_LSHIFTASN:
-		case O_RSHIFT:
-		case O_RSHIFTASN:
-		case O_ROL:
-		case O_ROLASN:
-		case O_ROR:
-		case O_RORASN:
-			t1 = vl->val.u;
-			t2 = vr->val.u & 31;
-			break;
-#endif
-		case O_LAND:
-		case O_LOR:
-			t1 = vl->val.u;
-			t2 = 0;	/* gcc */
-			break;
-		default:
-			t1 = vl->val.u;
-			t2 = vr->val.u;
-			break;
-		}
-
-#define cmpop(op)	(es->natural ?			\
-	(mksh_uari_t)(vl->val.u op vr->val.u) :		\
-	(mksh_uari_t)(vl->val.i op vr->val.i)		\
-)
-
-		/* op calculation */
-		switch ((int)op) {
-		case O_TIMES:
-		case O_TIMESASN:
-			res = t1 * t2;
-			break;
-		case O_MOD:
-		case O_MODASN:
-			if (es->natural) {
-				res = vl->val.u % vr->val.u;
-				break;
-			}
-			goto signed_division;
-		case O_DIV:
-		case O_DIVASN:
-			if (es->natural) {
-				res = vl->val.u / vr->val.u;
-				break;
-			}
- signed_division:
-			/*
-			 * a / b = abs(a) / abs(b) * sgn((u)a^(u)b)
-			 */
-			t3 = t1 / t2;
-#ifndef MKSH_LEGACY_MODE
-			res = ((vl->val.u ^ vr->val.u) & 0x80000000) ? -t3 : t3;
-#else
-			res = ((t1 == vl->val.u ? 0 : 1) ^
-			    (t2 == vr->val.u ? 0 : 1)) ? -t3 : t3;
-#endif
-			if (op == O_MOD || op == O_MODASN) {
-				/*
-				 * primitive modulo, to get the sign of
-				 * the result correct:
-				 * (a % b) = a - ((a / b) * b)
-				 * the subtraction and multiplication
-				 * are, amazingly enough, sign ignorant
-				 */
-				res = vl->val.u - (res * vr->val.u);
-			}
-			break;
-		case O_PLUS:
-		case O_PLUSASN:
-			res = t1 + t2;
-			break;
-		case O_MINUS:
-		case O_MINUSASN:
-			res = t1 - t2;
-			break;
-#ifndef MKSH_LEGACY_MODE
-		case O_ROL:
-		case O_ROLASN:
-			res = (t1 << t2) | (t1 >> (32 - t2));
-			break;
-		case O_ROR:
-		case O_RORASN:
-			res = (t1 >> t2) | (t1 << (32 - t2));
-			break;
-#endif
-		case O_LSHIFT:
-		case O_LSHIFTASN:
-			res = t1 << t2;
-			break;
-		case O_RSHIFT:
-		case O_RSHIFTASN:
-			res = es->natural || vl->val.i >= 0 ?
-			    t1 >> t2 :
-			    ~(~t1 >> t2);
-			break;
-		case O_LT:
-			res = cmpop(<);
-			break;
-		case O_LE:
-			res = cmpop(<=);
-			break;
-		case O_GT:
-			res = cmpop(>);
-			break;
-		case O_GE:
-			res = cmpop(>=);
-			break;
-		case O_EQ:
-			res = t1 == t2;
-			break;
-		case O_NE:
-			res = t1 != t2;
-			break;
-		case O_BAND:
-		case O_BANDASN:
-			res = t1 & t2;
-			break;
-		case O_BXOR:
-		case O_BXORASN:
-			res = t1 ^ t2;
-			break;
-		case O_BOR:
-		case O_BORASN:
-			res = t1 | t2;
-			break;
-		case O_LAND:
-			if (!t1)
-				es->noassign++;
-			vr = intvar(es, evalexpr(es, prec - 1));
-			res = t1 && vr->val.u;
-			if (!t1)
-				es->noassign--;
-			break;
-		case O_LOR:
-			if (t1)
-				es->noassign++;
-			vr = intvar(es, evalexpr(es, prec - 1));
-			res = t1 || vr->val.u;
-			if (t1)
-				es->noassign--;
-			break;
-		case O_ASN:
-		case O_COMMA:
-			res = t2;
-			break;
-		}
-
-#undef cmpop
-
-		if (IS_ASSIGNOP(op)) {
-			vr->val.u = res;
-			if (!es->noassign) {
-				if (vasn->flag & INTEGER)
-					setint_v(vasn, vr, es->arith);
-				else
-					setint(vasn, vr->val.i);
-			}
-			vl = vr;
-		} else
-			vl->val.u = res;
-	}
-	return (vl);
-}
-
-static void
-exprtoken(Expr_state *es)
-{
-	const char *cp = es->tokp;
-	int c;
-	char *tvar;
-
-	/* skip whitespace */
- skip_spaces:
-	while ((c = *cp), ksh_isspace(c))
-		++cp;
-	if (es->tokp == es->expression && c == '#') {
-		/* expression begins with # */
-		/* switch to unsigned */
-		es->natural = true;
-		++cp;
-		goto skip_spaces;
-	}
-	es->tokp = cp;
-
-	if (c == '\0')
-		es->tok = END;
-	else if (ksh_isalphx(c)) {
-		for (; ksh_isalnux(c); c = *cp)
-			cp++;
-		if (c == '[') {
-			size_t len;
-
-			len = array_ref_len(cp);
-			if (len == 0)
-				evalerr(es, ET_STR, "missing ]");
-			cp += len;
-		}
-		if (es->noassign) {
-			es->val = tempvar();
-			es->val->flag |= EXPRLVALUE;
-		} else {
-			strndupx(tvar, es->tokp, cp - es->tokp, ATEMP);
-			es->val = global(tvar);
-			afree(tvar, ATEMP);
-		}
-		es->tok = VAR;
-	} else if (c == '1' && cp[1] == '#') {
-		cp += 2;
-		cp += utf_ptradj(cp);
-		strndupx(tvar, es->tokp, cp - es->tokp, ATEMP);
-		goto process_tvar;
-#ifndef MKSH_SMALL
-	} else if (c == '\'') {
-		++cp;
-		cp += utf_ptradj(cp);
-		if (*cp++ != '\'')
-			evalerr(es, ET_STR,
-			    "multi-character character constant");
-		/* 'x' -> 1#x (x = one multibyte character) */
-		c = cp - es->tokp;
-		tvar = alloc(c + /* NUL */ 1, ATEMP);
-		tvar[0] = '1';
-		tvar[1] = '#';
-		memcpy(tvar + 2, es->tokp + 1, c - 2);
-		tvar[c] = '\0';
-		goto process_tvar;
-#endif
-	} else if (ksh_isdigit(c)) {
-		while (c != '_' && (ksh_isalnux(c) || c == '#'))
-			c = *cp++;
-		strndupx(tvar, es->tokp, --cp - es->tokp, ATEMP);
- process_tvar:
-		es->val = tempvar();
-		es->val->flag &= ~INTEGER;
-		es->val->type = 0;
-		es->val->val.s = tvar;
-		if (setint_v(es->val, es->val, es->arith) == NULL)
-			evalerr(es, ET_BADLIT, tvar);
-		afree(tvar, ATEMP);
-		es->tok = LIT;
-	} else {
-		int i, n0;
-
-		for (i = 0; (n0 = opinfo[i].name[0]); i++)
-			if (c == n0 && strncmp(cp, opinfo[i].name,
-			    (size_t)opinfo[i].len) == 0) {
-				es->tok = (enum token)i;
-				cp += opinfo[i].len;
-				break;
-			}
-		if (!n0)
-			es->tok = BAD;
-	}
-	es->tokp = cp;
-}
-
-static void
-assign_check(Expr_state *es, enum token op, struct tbl *vasn)
-{
-	if (es->tok == END || !vasn ||
-	    (vasn->name[0] == '\0' && !(vasn->flag & EXPRLVALUE)))
-		evalerr(es, ET_LVALUE, opinfo[(int)op].name);
-	else if (vasn->flag & RDONLY)
-		evalerr(es, ET_RDONLY, opinfo[(int)op].name);
-}
-
-struct tbl *
-tempvar(void)
-{
-	struct tbl *vp;
-
-	vp = alloc(sizeof(struct tbl), ATEMP);
-	vp->flag = ISSET|INTEGER;
-	vp->type = 0;
-	vp->areap = ATEMP;
-	vp->ua.hval = 0;
-	vp->val.i = 0;
-	vp->name[0] = '\0';
-	return (vp);
-}
-
-/* cast (string) variable to temporary integer variable */
-static struct tbl *
-intvar(Expr_state *es, struct tbl *vp)
-{
-	struct tbl *vq;
-
-	/* try to avoid replacing a temp var with another temp var */
-	if (vp->name[0] == '\0' &&
-	    (vp->flag & (ISSET|INTEGER|EXPRLVALUE)) == (ISSET|INTEGER))
-		return (vp);
-
-	vq = tempvar();
-	if (setint_v(vq, vp, es->arith) == NULL) {
-		if (vp->flag & EXPRINEVAL)
-			evalerr(es, ET_RECURSIVE, vp->name);
-		es->evaling = vp;
-		vp->flag |= EXPRINEVAL;
-		v_evaluate(vq, str_val(vp), KSH_UNWIND_ERROR, es->arith);
-		vp->flag &= ~EXPRINEVAL;
-		es->evaling = NULL;
-	}
-	return (vq);
-}
-
-
-/*
- * UTF-8 support code: high-level functions
- */
-
-int
-utf_widthadj(const char *src, const char **dst)
-{
-	size_t len;
-	unsigned int wc;
-	int width;
-
-	if (!UTFMODE || (len = utf_mbtowc(&wc, src)) == (size_t)-1 ||
-	    wc == 0)
-		len = width = 1;
-	else if ((width = utf_wcwidth(wc)) < 0)
-		/* XXX use 2 for x_zotc3 here? */
-		width = 1;
-
-	if (dst)
-		*dst = src + len;
-	return (width);
-}
-
-size_t
-utf_mbswidth(const char *s)
-{
-	size_t len, width = 0;
-	unsigned int wc;
-	int cw;
-
-	if (!UTFMODE)
-		return (strlen(s));
-
-	while (*s)
-		if (((len = utf_mbtowc(&wc, s)) == (size_t)-1) ||
-		    ((cw = utf_wcwidth(wc)) == -1)) {
-			s++;
-			width += 1;
-		} else {
-			s += len;
-			width += cw;
-		}
-	return (width);
-}
-
-const char *
-utf_skipcols(const char *p, int cols)
-{
-	int c = 0;
-
-	while (c < cols) {
-		if (!*p)
-			return (p + cols - c);
-		c += utf_widthadj(p, &p);
-	}
-	return (p);
-}
-
-size_t
-utf_ptradj(const char *src)
-{
-	register size_t n;
-
-	if (!UTFMODE ||
-	    *(const unsigned char *)(src) < 0xC2 ||
-	    (n = utf_mbtowc(NULL, src)) == (size_t)-1)
-		n = 1;
-	return (n);
-}
-
-/*
- * UTF-8 support code: low-level functions
- */
-
-/* CESU-8 multibyte and wide character conversion crafted for mksh */
-
-size_t
-utf_mbtowc(unsigned int *dst, const char *src)
-{
-	const unsigned char *s = (const unsigned char *)src;
-	unsigned int c, wc;
-
-	if ((wc = *s++) < 0x80) {
- out:
-		if (dst != NULL)
-			*dst = wc;
-		return (wc ? ((const char *)s - src) : 0);
-	}
-	if (wc < 0xC2 || wc >= 0xF0)
-		/* < 0xC0: spurious second byte */
-		/* < 0xC2: non-minimalistic mapping error in 2-byte seqs */
-		/* > 0xEF: beyond BMP */
-		goto ilseq;
-
-	if (wc < 0xE0) {
-		wc = (wc & 0x1F) << 6;
-		if (((c = *s++) & 0xC0) != 0x80)
-			goto ilseq;
-		wc |= c & 0x3F;
-		goto out;
-	}
-
-	wc = (wc & 0x0F) << 12;
-
-	if (((c = *s++) & 0xC0) != 0x80)
-		goto ilseq;
-	wc |= (c & 0x3F) << 6;
-
-	if (((c = *s++) & 0xC0) != 0x80)
-		goto ilseq;
-	wc |= c & 0x3F;
-
-	/* Check for non-minimalistic mapping error in 3-byte seqs */
-	if (wc >= 0x0800 && wc <= 0xFFFD)
-		goto out;
- ilseq:
-	return ((size_t)(-1));
-}
-
-size_t
-utf_wctomb(char *dst, unsigned int wc)
-{
-	unsigned char *d;
-
-	if (wc < 0x80) {
-		*dst = wc;
-		return (1);
-	}
-
-	d = (unsigned char *)dst;
-	if (wc < 0x0800)
-		*d++ = (wc >> 6) | 0xC0;
-	else {
-		*d++ = ((wc = wc > 0xFFFD ? 0xFFFD : wc) >> 12) | 0xE0;
-		*d++ = ((wc >> 6) & 0x3F) | 0x80;
-	}
-	*d++ = (wc & 0x3F) | 0x80;
-	return ((char *)d - dst);
-}
-
-/*
- * Wrapper around access(2) because it says root can execute everything
- * on some operating systems. Does not set errno, no user needs it. Use
- * this iff mode can have the X_OK bit set, access otherwise.
- */
-int
-ksh_access(const char *fn, int mode)
-{
-	int rv;
-	struct stat sb;
-
-	if ((rv = access(fn, mode)) == 0 && kshuid == 0 && (mode & X_OK) &&
-	    (rv = stat(fn, &sb)) == 0 && !S_ISDIR(sb.st_mode) &&
-	    (sb.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH)) == 0)
-		rv = -1;
-
-	return (rv);
-}
-
-#ifndef MKSH_mirbsd_wcwidth
-/* From: X11/xc/programs/xterm/wcwidth.c,v 1.6 2013/05/31 23:27:09 tg Exp $ */
-
-struct mb_ucsrange {
-	unsigned short beg;
-	unsigned short end;
-};
-
-static int mb_ucsbsearch(const struct mb_ucsrange arr[], size_t elems,
-    unsigned int val);
-
-/*
- * Generated by MirOS: contrib/code/Snippets/eawparse,v 1.1 2013/05/31 23:27:16 tg Exp $
- * from Unicode 6.2.0
- */
-
-static const struct mb_ucsrange mb_ucs_combining[] = {
-	{ 0x0300, 0x036F },
-	{ 0x0483, 0x0489 },
-	{ 0x0591, 0x05BD },
-	{ 0x05BF, 0x05BF },
-	{ 0x05C1, 0x05C2 },
-	{ 0x05C4, 0x05C5 },
-	{ 0x05C7, 0x05C7 },
-	{ 0x0600, 0x0604 },
-	{ 0x0610, 0x061A },
-	{ 0x064B, 0x065F },
-	{ 0x0670, 0x0670 },
-	{ 0x06D6, 0x06DD },
-	{ 0x06DF, 0x06E4 },
-	{ 0x06E7, 0x06E8 },
-	{ 0x06EA, 0x06ED },
-	{ 0x070F, 0x070F },
-	{ 0x0711, 0x0711 },
-	{ 0x0730, 0x074A },
-	{ 0x07A6, 0x07B0 },
-	{ 0x07EB, 0x07F3 },
-	{ 0x0816, 0x0819 },
-	{ 0x081B, 0x0823 },
-	{ 0x0825, 0x0827 },
-	{ 0x0829, 0x082D },
-	{ 0x0859, 0x085B },
-	{ 0x08E4, 0x08FE },
-	{ 0x0900, 0x0902 },
-	{ 0x093A, 0x093A },
-	{ 0x093C, 0x093C },
-	{ 0x0941, 0x0948 },
-	{ 0x094D, 0x094D },
-	{ 0x0951, 0x0957 },
-	{ 0x0962, 0x0963 },
-	{ 0x0981, 0x0981 },
-	{ 0x09BC, 0x09BC },
-	{ 0x09C1, 0x09C4 },
-	{ 0x09CD, 0x09CD },
-	{ 0x09E2, 0x09E3 },
-	{ 0x0A01, 0x0A02 },
-	{ 0x0A3C, 0x0A3C },
-	{ 0x0A41, 0x0A42 },
-	{ 0x0A47, 0x0A48 },
-	{ 0x0A4B, 0x0A4D },
-	{ 0x0A51, 0x0A51 },
-	{ 0x0A70, 0x0A71 },
-	{ 0x0A75, 0x0A75 },
-	{ 0x0A81, 0x0A82 },
-	{ 0x0ABC, 0x0ABC },
-	{ 0x0AC1, 0x0AC5 },
-	{ 0x0AC7, 0x0AC8 },
-	{ 0x0ACD, 0x0ACD },
-	{ 0x0AE2, 0x0AE3 },
-	{ 0x0B01, 0x0B01 },
-	{ 0x0B3C, 0x0B3C },
-	{ 0x0B3F, 0x0B3F },
-	{ 0x0B41, 0x0B44 },
-	{ 0x0B4D, 0x0B4D },
-	{ 0x0B56, 0x0B56 },
-	{ 0x0B62, 0x0B63 },
-	{ 0x0B82, 0x0B82 },
-	{ 0x0BC0, 0x0BC0 },
-	{ 0x0BCD, 0x0BCD },
-	{ 0x0C3E, 0x0C40 },
-	{ 0x0C46, 0x0C48 },
-	{ 0x0C4A, 0x0C4D },
-	{ 0x0C55, 0x0C56 },
-	{ 0x0C62, 0x0C63 },
-	{ 0x0CBC, 0x0CBC },
-	{ 0x0CBF, 0x0CBF },
-	{ 0x0CC6, 0x0CC6 },
-	{ 0x0CCC, 0x0CCD },
-	{ 0x0CE2, 0x0CE3 },
-	{ 0x0D41, 0x0D44 },
-	{ 0x0D4D, 0x0D4D },
-	{ 0x0D62, 0x0D63 },
-	{ 0x0DCA, 0x0DCA },
-	{ 0x0DD2, 0x0DD4 },
-	{ 0x0DD6, 0x0DD6 },
-	{ 0x0E31, 0x0E31 },
-	{ 0x0E34, 0x0E3A },
-	{ 0x0E47, 0x0E4E },
-	{ 0x0EB1, 0x0EB1 },
-	{ 0x0EB4, 0x0EB9 },
-	{ 0x0EBB, 0x0EBC },
-	{ 0x0EC8, 0x0ECD },
-	{ 0x0F18, 0x0F19 },
-	{ 0x0F35, 0x0F35 },
-	{ 0x0F37, 0x0F37 },
-	{ 0x0F39, 0x0F39 },
-	{ 0x0F71, 0x0F7E },
-	{ 0x0F80, 0x0F84 },
-	{ 0x0F86, 0x0F87 },
-	{ 0x0F8D, 0x0F97 },
-	{ 0x0F99, 0x0FBC },
-	{ 0x0FC6, 0x0FC6 },
-	{ 0x102D, 0x1030 },
-	{ 0x1032, 0x1037 },
-	{ 0x1039, 0x103A },
-	{ 0x103D, 0x103E },
-	{ 0x1058, 0x1059 },
-	{ 0x105E, 0x1060 },
-	{ 0x1071, 0x1074 },
-	{ 0x1082, 0x1082 },
-	{ 0x1085, 0x1086 },
-	{ 0x108D, 0x108D },
-	{ 0x109D, 0x109D },
-	{ 0x1160, 0x11FF },
-	{ 0x135D, 0x135F },
-	{ 0x1712, 0x1714 },
-	{ 0x1732, 0x1734 },
-	{ 0x1752, 0x1753 },
-	{ 0x1772, 0x1773 },
-	{ 0x17B4, 0x17B5 },
-	{ 0x17B7, 0x17BD },
-	{ 0x17C6, 0x17C6 },
-	{ 0x17C9, 0x17D3 },
-	{ 0x17DD, 0x17DD },
-	{ 0x180B, 0x180D },
-	{ 0x18A9, 0x18A9 },
-	{ 0x1920, 0x1922 },
-	{ 0x1927, 0x1928 },
-	{ 0x1932, 0x1932 },
-	{ 0x1939, 0x193B },
-	{ 0x1A17, 0x1A18 },
-	{ 0x1A56, 0x1A56 },
-	{ 0x1A58, 0x1A5E },
-	{ 0x1A60, 0x1A60 },
-	{ 0x1A62, 0x1A62 },
-	{ 0x1A65, 0x1A6C },
-	{ 0x1A73, 0x1A7C },
-	{ 0x1A7F, 0x1A7F },
-	{ 0x1B00, 0x1B03 },
-	{ 0x1B34, 0x1B34 },
-	{ 0x1B36, 0x1B3A },
-	{ 0x1B3C, 0x1B3C },
-	{ 0x1B42, 0x1B42 },
-	{ 0x1B6B, 0x1B73 },
-	{ 0x1B80, 0x1B81 },
-	{ 0x1BA2, 0x1BA5 },
-	{ 0x1BA8, 0x1BA9 },
-	{ 0x1BAB, 0x1BAB },
-	{ 0x1BE6, 0x1BE6 },
-	{ 0x1BE8, 0x1BE9 },
-	{ 0x1BED, 0x1BED },
-	{ 0x1BEF, 0x1BF1 },
-	{ 0x1C2C, 0x1C33 },
-	{ 0x1C36, 0x1C37 },
-	{ 0x1CD0, 0x1CD2 },
-	{ 0x1CD4, 0x1CE0 },
-	{ 0x1CE2, 0x1CE8 },
-	{ 0x1CED, 0x1CED },
-	{ 0x1CF4, 0x1CF4 },
-	{ 0x1DC0, 0x1DE6 },
-	{ 0x1DFC, 0x1DFF },
-	{ 0x200B, 0x200F },
-	{ 0x202A, 0x202E },
-	{ 0x2060, 0x2064 },
-	{ 0x206A, 0x206F },
-	{ 0x20D0, 0x20F0 },
-	{ 0x2CEF, 0x2CF1 },
-	{ 0x2D7F, 0x2D7F },
-	{ 0x2DE0, 0x2DFF },
-	{ 0x302A, 0x302D },
-	{ 0x3099, 0x309A },
-	{ 0xA66F, 0xA672 },
-	{ 0xA674, 0xA67D },
-	{ 0xA69F, 0xA69F },
-	{ 0xA6F0, 0xA6F1 },
-	{ 0xA802, 0xA802 },
-	{ 0xA806, 0xA806 },
-	{ 0xA80B, 0xA80B },
-	{ 0xA825, 0xA826 },
-	{ 0xA8C4, 0xA8C4 },
-	{ 0xA8E0, 0xA8F1 },
-	{ 0xA926, 0xA92D },
-	{ 0xA947, 0xA951 },
-	{ 0xA980, 0xA982 },
-	{ 0xA9B3, 0xA9B3 },
-	{ 0xA9B6, 0xA9B9 },
-	{ 0xA9BC, 0xA9BC },
-	{ 0xAA29, 0xAA2E },
-	{ 0xAA31, 0xAA32 },
-	{ 0xAA35, 0xAA36 },
-	{ 0xAA43, 0xAA43 },
-	{ 0xAA4C, 0xAA4C },
-	{ 0xAAB0, 0xAAB0 },
-	{ 0xAAB2, 0xAAB4 },
-	{ 0xAAB7, 0xAAB8 },
-	{ 0xAABE, 0xAABF },
-	{ 0xAAC1, 0xAAC1 },
-	{ 0xAAEC, 0xAAED },
-	{ 0xAAF6, 0xAAF6 },
-	{ 0xABE5, 0xABE5 },
-	{ 0xABE8, 0xABE8 },
-	{ 0xABED, 0xABED },
-	{ 0xFB1E, 0xFB1E },
-	{ 0xFE00, 0xFE0F },
-	{ 0xFE20, 0xFE26 },
-	{ 0xFEFF, 0xFEFF },
-	{ 0xFFF9, 0xFFFB }
-};
-
-static const struct mb_ucsrange mb_ucs_fullwidth[] = {
-	{ 0x1100, 0x115F },
-	{ 0x2329, 0x232A },
-	{ 0x2E80, 0x303E },
-	{ 0x3040, 0xA4CF },
-	{ 0xA960, 0xA97F },
-	{ 0xAC00, 0xD7A3 },
-	{ 0xF900, 0xFAFF },
-	{ 0xFE10, 0xFE19 },
-	{ 0xFE30, 0xFE6F },
-	{ 0xFF00, 0xFF60 },
-	{ 0xFFE0, 0xFFE6 }
-};
-
-/* simple binary search in ranges, with bounds optimisation */
-static int
-mb_ucsbsearch(const struct mb_ucsrange arr[], size_t elems, unsigned int val)
-{
-	size_t min = 0, mid, max = elems;
-
-	if (val < arr[min].beg || val > arr[max - 1].end)
-		return (0);
-
-	while (min < max) {
-		mid = (min + max) / 2;
-
-		if (val < arr[mid].beg)
-			max = mid;
-		else if (val > arr[mid].end)
-			min = mid + 1;
-		else
-			return (1);
-	}
-	return (0);
-}
-
-/* Unix column width of a wide character (Unicode code point, really) */
-int
-utf_wcwidth(unsigned int wc)
-{
-	/* except NUL, C0/C1 control characters and DEL yield -1 */
-	if (wc < 0x20 || (wc >= 0x7F && wc < 0xA0))
-		return (wc ? -1 : 0);
-
-	/* combining characters use 0 screen columns */
-	if (mb_ucsbsearch(mb_ucs_combining, NELEM(mb_ucs_combining), wc))
-		return (0);
-
-	/* all others use 1 or 2 screen columns */
-	if (mb_ucsbsearch(mb_ucs_fullwidth, NELEM(mb_ucs_fullwidth), wc))
-		return (2);
-	return (1);
-}
-#endif

Copied: vendor/MirOS/mksh/R50/expr.c (from rev 6707, vendor/MirOS/mksh/dist/expr.c)
===================================================================
--- vendor/MirOS/mksh/R50/expr.c	                        (rev 0)
+++ vendor/MirOS/mksh/R50/expr.c	2014-07-01 12:01:17 UTC (rev 6708)
@@ -0,0 +1,1197 @@
+/*	$OpenBSD: expr.c,v 1.23 2013/12/17 16:37:06 deraadt Exp $	*/
+
+/*-
+ * Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
+ *		 2011, 2012, 2013, 2014
+ *	Thorsten Glaser <tg at mirbsd.org>
+ *
+ * Provided that these terms and disclaimer and all copyright notices
+ * are retained or reproduced in an accompanying document, permission
+ * is granted to deal in this work without restriction, including un-
+ * limited rights to use, publicly perform, distribute, sell, modify,
+ * merge, give away, or sublicence.
+ *
+ * This work is provided "AS IS" and WITHOUT WARRANTY of any kind, to
+ * the utmost extent permitted by applicable law, neither express nor
+ * implied; without malicious intent or gross negligence. In no event
+ * may a licensor, author or contributor be held liable for indirect,
+ * direct, other damage, loss, or other issues arising in any way out
+ * of dealing in the work, even if advised of the possibility of such
+ * damage or existence of a defect, except proven that it results out
+ * of said person's immediate fault when using the work as intended.
+ */
+
+#include "sh.h"
+
+__RCSID("$MirOS: src/bin/mksh/expr.c,v 1.76 2014/06/24 19:53:19 tg Exp $");
+
+/* the order of these enums is constrained by the order of opinfo[] */
+enum token {
+	/* some (long) unary operators */
+	O_PLUSPLUS = 0, O_MINUSMINUS,
+	/* binary operators */
+	O_EQ, O_NE,
+	/* assignments are assumed to be in range O_ASN .. O_BORASN */
+	O_ASN, O_TIMESASN, O_DIVASN, O_MODASN, O_PLUSASN, O_MINUSASN,
+#ifndef MKSH_LEGACY_MODE
+	O_ROLASN, O_RORASN,
+#endif
+	O_LSHIFTASN, O_RSHIFTASN, O_BANDASN, O_BXORASN, O_BORASN,
+	/* binary non-assignment operators */
+#ifndef MKSH_LEGACY_MODE
+	O_ROL, O_ROR,
+#endif
+	O_LSHIFT, O_RSHIFT,
+	O_LE, O_GE, O_LT, O_GT,
+	O_LAND,
+	O_LOR,
+	O_TIMES, O_DIV, O_MOD,
+	O_PLUS, O_MINUS,
+	O_BAND,
+	O_BXOR,
+	O_BOR,
+	O_TERN,
+	O_COMMA,
+	/* things after this aren't used as binary operators */
+	/* unary that are not also binaries */
+	O_BNOT, O_LNOT,
+	/* misc */
+	OPEN_PAREN, CLOSE_PAREN, CTERN,
+	/* things that don't appear in the opinfo[] table */
+	VAR, LIT, END, BAD
+};
+#define IS_ASSIGNOP(op)	((int)(op) >= (int)O_ASN && (int)(op) <= (int)O_BORASN)
+
+/* precisions; used to be enum prec but we do arithmetics on it */
+#define P_PRIMARY	0	/* VAR, LIT, (), ! ~ ++ -- */
+#define P_MULT		1	/* * / % */
+#define P_ADD		2	/* + - */
+#define P_SHIFT		3	/* <<< >>> << >> */
+#define P_RELATION	4	/* < <= > >= */
+#define P_EQUALITY	5	/* == != */
+#define P_BAND		6	/* & */
+#define P_BXOR		7	/* ^ */
+#define P_BOR		8	/* | */
+#define P_LAND		9	/* && */
+#define P_LOR		10	/* || */
+#define P_TERN		11	/* ?: */
+	/* = += -= *= /= %= <<<= >>>= <<= >>= &= ^= |= */
+#define P_ASSIGN	12
+#define P_COMMA		13	/* , */
+#define MAX_PREC	P_COMMA
+
+struct opinfo {
+	char name[5];
+	/* name length */
+	uint8_t len;
+	/* precedence: lower is higher */
+	uint8_t prec;
+};
+
+/*
+ * Tokens in this table must be ordered so the longest are first
+ * (eg, += before +). If you change something, change the order
+ * of enum token too.
+ */
+static const struct opinfo opinfo[] = {
+	{ "++",   2, P_PRIMARY },	/* before + */
+	{ "--",   2, P_PRIMARY },	/* before - */
+	{ "==",   2, P_EQUALITY },	/* before = */
+	{ "!=",   2, P_EQUALITY },	/* before ! */
+	{ "=",    1, P_ASSIGN },	/* keep assigns in a block */
+	{ "*=",   2, P_ASSIGN },
+	{ "/=",   2, P_ASSIGN },
+	{ "%=",   2, P_ASSIGN },
+	{ "+=",   2, P_ASSIGN },
+	{ "-=",   2, P_ASSIGN },
+#ifndef MKSH_LEGACY_MODE
+	{ "<<<=", 4, P_ASSIGN },	/* before <<< */
+	{ ">>>=", 4, P_ASSIGN },	/* before >>> */
+#endif
+	{ "<<=",  3, P_ASSIGN },
+	{ ">>=",  3, P_ASSIGN },
+	{ "&=",   2, P_ASSIGN },
+	{ "^=",   2, P_ASSIGN },
+	{ "|=",   2, P_ASSIGN },
+#ifndef MKSH_LEGACY_MODE
+	{ "<<<",  3, P_SHIFT },		/* before << */
+	{ ">>>",  3, P_SHIFT },		/* before >> */
+#endif
+	{ "<<",   2, P_SHIFT },
+	{ ">>",   2, P_SHIFT },
+	{ "<=",   2, P_RELATION },
+	{ ">=",   2, P_RELATION },
+	{ "<",    1, P_RELATION },
+	{ ">",    1, P_RELATION },
+	{ "&&",   2, P_LAND },
+	{ "||",   2, P_LOR },
+	{ "*",    1, P_MULT },
+	{ "/",    1, P_MULT },
+	{ "%",    1, P_MULT },
+	{ "+",    1, P_ADD },
+	{ "-",    1, P_ADD },
+	{ "&",    1, P_BAND },
+	{ "^",    1, P_BXOR },
+	{ "|",    1, P_BOR },
+	{ "?",    1, P_TERN },
+	{ ",",    1, P_COMMA },
+	{ "~",    1, P_PRIMARY },
+	{ "!",    1, P_PRIMARY },
+	{ "(",    1, P_PRIMARY },
+	{ ")",    1, P_PRIMARY },
+	{ ":",    1, P_PRIMARY },
+	{ "",     0, P_PRIMARY }
+};
+
+typedef struct expr_state {
+	/* expression being evaluated */
+	const char *expression;
+	/* lexical position */
+	const char *tokp;
+	/* value from token() */
+	struct tbl *val;
+	/* variable that is being recursively expanded (EXPRINEVAL flag set) */
+	struct tbl *evaling;
+	/* token from token() */
+	enum token tok;
+	/* don't do assignments (for ?:, &&, ||) */
+	uint8_t noassign;
+	/* evaluating an $(()) expression? */
+	bool arith;
+	/* unsigned arithmetic calculation */
+	bool natural;
+} Expr_state;
+
+enum error_type {
+	ET_UNEXPECTED, ET_BADLIT, ET_RECURSIVE,
+	ET_LVALUE, ET_RDONLY, ET_STR
+};
+
+static void evalerr(Expr_state *, enum error_type, const char *)
+    MKSH_A_NORETURN;
+static struct tbl *evalexpr(Expr_state *, unsigned int);
+static void exprtoken(Expr_state *);
+static struct tbl *do_ppmm(Expr_state *, enum token, struct tbl *, bool);
+static void assign_check(Expr_state *, enum token, struct tbl *);
+static struct tbl *intvar(Expr_state *, struct tbl *);
+
+/*
+ * parse and evaluate expression
+ */
+int
+evaluate(const char *expr, mksh_ari_t *rval, int error_ok, bool arith)
+{
+	struct tbl v;
+	int ret;
+
+	v.flag = DEFINED | INTEGER;
+	v.type = 0;
+	ret = v_evaluate(&v, expr, error_ok, arith);
+	*rval = v.val.i;
+	return (ret);
+}
+
+/*
+ * parse and evaluate expression, storing result in vp.
+ */
+int
+v_evaluate(struct tbl *vp, const char *expr, volatile int error_ok,
+    bool arith)
+{
+	struct tbl *v;
+	Expr_state curstate;
+	Expr_state * const es = &curstate;
+	int i;
+
+	/* save state to allow recursive calls */
+	memset(&curstate, 0, sizeof(curstate));
+	curstate.expression = curstate.tokp = expr;
+	curstate.tok = BAD;
+	curstate.arith = arith;
+
+	newenv(E_ERRH);
+	if ((i = kshsetjmp(e->jbuf))) {
+		/* Clear EXPRINEVAL in of any variables we were playing with */
+		if (curstate.evaling)
+			curstate.evaling->flag &= ~EXPRINEVAL;
+		quitenv(NULL);
+		if (i == LAEXPR) {
+			if (error_ok == KSH_RETURN_ERROR)
+				return (0);
+			errorfz();
+		}
+		unwind(i);
+		/* NOTREACHED */
+	}
+
+	exprtoken(es);
+	if (es->tok == END) {
+		es->tok = LIT;
+		es->val = tempvar();
+	}
+	v = intvar(es, evalexpr(es, MAX_PREC));
+
+	if (es->tok != END)
+		evalerr(es, ET_UNEXPECTED, NULL);
+
+	if (es->arith && es->natural)
+		vp->flag |= INT_U;
+	if (vp->flag & INTEGER)
+		setint_v(vp, v, es->arith);
+	else
+		/* can fail if readonly */
+		setstr(vp, str_val(v), error_ok);
+
+	quitenv(NULL);
+
+	return (1);
+}
+
+static void
+evalerr(Expr_state *es, enum error_type type, const char *str)
+{
+	char tbuf[2];
+	const char *s;
+
+	es->arith = false;
+	switch (type) {
+	case ET_UNEXPECTED:
+		switch (es->tok) {
+		case VAR:
+			s = es->val->name;
+			break;
+		case LIT:
+			s = str_val(es->val);
+			break;
+		case END:
+			s = "end of expression";
+			break;
+		case BAD:
+			tbuf[0] = *es->tokp;
+			tbuf[1] = '\0';
+			s = tbuf;
+			break;
+		default:
+			s = opinfo[(int)es->tok].name;
+		}
+		warningf(true, "%s: %s '%s'", es->expression,
+		    "unexpected", s);
+		break;
+
+	case ET_BADLIT:
+		warningf(true, "%s: %s '%s'", es->expression,
+		    "bad number", str);
+		break;
+
+	case ET_RECURSIVE:
+		warningf(true, "%s: %s '%s'", es->expression,
+		    "expression recurses on parameter", str);
+		break;
+
+	case ET_LVALUE:
+		warningf(true, "%s: %s %s",
+		    es->expression, str, "requires lvalue");
+		break;
+
+	case ET_RDONLY:
+		warningf(true, "%s: %s %s",
+		    es->expression, str, "applied to read-only variable");
+		break;
+
+	default: /* keep gcc happy */
+	case ET_STR:
+		warningf(true, "%s: %s", es->expression, str);
+		break;
+	}
+	unwind(LAEXPR);
+}
+
+/* do a ++ or -- operation */
+static struct tbl *
+do_ppmm(Expr_state *es, enum token op, struct tbl *vasn, bool is_prefix)
+{
+	struct tbl *vl;
+	mksh_uari_t oval;
+
+	assign_check(es, op, vasn);
+
+	vl = intvar(es, vasn);
+	oval = vl->val.u;
+	if (op == O_PLUSPLUS)
+		++vl->val.u;
+	else
+		--vl->val.u;
+	if (!es->noassign) {
+		if (vasn->flag & INTEGER)
+			setint_v(vasn, vl, es->arith);
+		else
+			setint(vasn, vl->val.i);
+	}
+	if (!is_prefix)
+		/* undo the increment/decrement */
+		vl->val.u = oval;
+
+	return (vl);
+}
+
+static struct tbl *
+evalexpr(Expr_state *es, unsigned int prec)
+{
+	struct tbl *vl, *vr = NULL, *vasn;
+	enum token op;
+	mksh_uari_t res = 0, t1, t2, t3;
+
+	if (prec == P_PRIMARY) {
+		switch ((int)(op = es->tok)) {
+		case O_BNOT:
+		case O_LNOT:
+		case O_MINUS:
+		case O_PLUS:
+			exprtoken(es);
+			vl = intvar(es, evalexpr(es, P_PRIMARY));
+			switch ((int)op) {
+			case O_BNOT:
+				vl->val.u = ~vl->val.u;
+				break;
+			case O_LNOT:
+				vl->val.u = !vl->val.u;
+				break;
+			case O_MINUS:
+				vl->val.u = -vl->val.u;
+				break;
+			case O_PLUS:
+				/* nop */
+				break;
+			}
+			break;
+
+		case OPEN_PAREN:
+			exprtoken(es);
+			vl = evalexpr(es, MAX_PREC);
+			if (es->tok != CLOSE_PAREN)
+				evalerr(es, ET_STR, "missing )");
+			exprtoken(es);
+			break;
+
+		case O_PLUSPLUS:
+		case O_MINUSMINUS:
+			exprtoken(es);
+			vl = do_ppmm(es, op, es->val, true);
+			exprtoken(es);
+			break;
+
+		case VAR:
+		case LIT:
+			vl = es->val;
+			exprtoken(es);
+			break;
+
+		default:
+			evalerr(es, ET_UNEXPECTED, NULL);
+			/* NOTREACHED */
+		}
+
+		if (es->tok == O_PLUSPLUS || es->tok == O_MINUSMINUS) {
+			vl = do_ppmm(es, es->tok, vl, false);
+			exprtoken(es);
+		}
+
+		return (vl);
+		/* prec == P_PRIMARY */
+	}
+
+	vl = evalexpr(es, prec - 1);
+	while ((int)(op = es->tok) >= (int)O_EQ && (int)op <= (int)O_COMMA &&
+	    opinfo[(int)op].prec == prec) {
+		exprtoken(es);
+		vasn = vl;
+		if (op != O_ASN)
+			/* vl may not have a value yet */
+			vl = intvar(es, vl);
+		if (IS_ASSIGNOP(op)) {
+			if (!es->noassign)
+				assign_check(es, op, vasn);
+			vr = intvar(es, evalexpr(es, P_ASSIGN));
+		} else if (op == O_TERN) {
+			bool ev = vl->val.u != 0;
+
+			if (!ev)
+				es->noassign++;
+			vl = evalexpr(es, MAX_PREC);
+			if (!ev)
+				es->noassign--;
+			if (es->tok != CTERN)
+				evalerr(es, ET_STR, "missing :");
+			exprtoken(es);
+			if (ev)
+				es->noassign++;
+			vr = evalexpr(es, P_TERN);
+			if (ev)
+				es->noassign--;
+			vl = ev ? vl : vr;
+			continue;
+		} else if (op != O_LAND && op != O_LOR)
+			vr = intvar(es, evalexpr(es, prec - 1));
+
+		/* common ops setup */
+		switch ((int)op) {
+		case O_DIV:
+		case O_DIVASN:
+		case O_MOD:
+		case O_MODASN:
+			if (vr->val.u == 0) {
+				if (!es->noassign)
+					evalerr(es, ET_STR, "zero divisor");
+				vr->val.u = 1;
+			}
+			/* calculate the absolute values */
+			t1 = vl->val.i < 0 ? -vl->val.u : vl->val.u;
+			t2 = vr->val.i < 0 ? -vr->val.u : vr->val.u;
+			break;
+#ifndef MKSH_LEGACY_MODE
+		case O_LSHIFT:
+		case O_LSHIFTASN:
+		case O_RSHIFT:
+		case O_RSHIFTASN:
+		case O_ROL:
+		case O_ROLASN:
+		case O_ROR:
+		case O_RORASN:
+			t1 = vl->val.u;
+			t2 = vr->val.u & 31;
+			break;
+#endif
+		case O_LAND:
+		case O_LOR:
+			t1 = vl->val.u;
+			t2 = 0;	/* gcc */
+			break;
+		default:
+			t1 = vl->val.u;
+			t2 = vr->val.u;
+			break;
+		}
+
+#define cmpop(op)	(es->natural ?			\
+	(mksh_uari_t)(vl->val.u op vr->val.u) :		\
+	(mksh_uari_t)(vl->val.i op vr->val.i)		\
+)
+
+		/* op calculation */
+		switch ((int)op) {
+		case O_TIMES:
+		case O_TIMESASN:
+			res = t1 * t2;
+			break;
+		case O_MOD:
+		case O_MODASN:
+			if (es->natural) {
+				res = vl->val.u % vr->val.u;
+				break;
+			}
+			goto signed_division;
+		case O_DIV:
+		case O_DIVASN:
+			if (es->natural) {
+				res = vl->val.u / vr->val.u;
+				break;
+			}
+ signed_division:
+			/*
+			 * a / b = abs(a) / abs(b) * sgn((u)a^(u)b)
+			 */
+			t3 = t1 / t2;
+#ifndef MKSH_LEGACY_MODE
+			res = ((vl->val.u ^ vr->val.u) & 0x80000000) ? -t3 : t3;
+#else
+			res = ((t1 == vl->val.u ? 0 : 1) ^
+			    (t2 == vr->val.u ? 0 : 1)) ? -t3 : t3;
+#endif
+			if (op == O_MOD || op == O_MODASN) {
+				/*
+				 * primitive modulo, to get the sign of
+				 * the result correct:
+				 * (a % b) = a - ((a / b) * b)
+				 * the subtraction and multiplication
+				 * are, amazingly enough, sign ignorant
+				 */
+				res = vl->val.u - (res * vr->val.u);
+			}
+			break;
+		case O_PLUS:
+		case O_PLUSASN:
+			res = t1 + t2;
+			break;
+		case O_MINUS:
+		case O_MINUSASN:
+			res = t1 - t2;
+			break;
+#ifndef MKSH_LEGACY_MODE
+		case O_ROL:
+		case O_ROLASN:
+			res = (t1 << t2) | (t1 >> (32 - t2));
+			break;
+		case O_ROR:
+		case O_RORASN:
+			res = (t1 >> t2) | (t1 << (32 - t2));
+			break;
+#endif
+		case O_LSHIFT:
+		case O_LSHIFTASN:
+			res = t1 << t2;
+			break;
+		case O_RSHIFT:
+		case O_RSHIFTASN:
+			res = es->natural || vl->val.i >= 0 ?
+			    t1 >> t2 :
+			    ~(~t1 >> t2);
+			break;
+		case O_LT:
+			res = cmpop(<);
+			break;
+		case O_LE:
+			res = cmpop(<=);
+			break;
+		case O_GT:
+			res = cmpop(>);
+			break;
+		case O_GE:
+			res = cmpop(>=);
+			break;
+		case O_EQ:
+			res = t1 == t2;
+			break;
+		case O_NE:
+			res = t1 != t2;
+			break;
+		case O_BAND:
+		case O_BANDASN:
+			res = t1 & t2;
+			break;
+		case O_BXOR:
+		case O_BXORASN:
+			res = t1 ^ t2;
+			break;
+		case O_BOR:
+		case O_BORASN:
+			res = t1 | t2;
+			break;
+		case O_LAND:
+			if (!t1)
+				es->noassign++;
+			vr = intvar(es, evalexpr(es, prec - 1));
+			res = t1 && vr->val.u;
+			if (!t1)
+				es->noassign--;
+			break;
+		case O_LOR:
+			if (t1)
+				es->noassign++;
+			vr = intvar(es, evalexpr(es, prec - 1));
+			res = t1 || vr->val.u;
+			if (t1)
+				es->noassign--;
+			break;
+		case O_ASN:
+		case O_COMMA:
+			res = t2;
+			break;
+		}
+
+#undef cmpop
+
+		if (IS_ASSIGNOP(op)) {
+			vr->val.u = res;
+			if (!es->noassign) {
+				if (vasn->flag & INTEGER)
+					setint_v(vasn, vr, es->arith);
+				else
+					setint(vasn, vr->val.i);
+			}
+			vl = vr;
+		} else
+			vl->val.u = res;
+	}
+	return (vl);
+}
+
+static void
+exprtoken(Expr_state *es)
+{
+	const char *cp = es->tokp;
+	int c;
+	char *tvar;
+
+	/* skip whitespace */
+ skip_spaces:
+	while ((c = *cp), ksh_isspace(c))
+		++cp;
+	if (es->tokp == es->expression && c == '#') {
+		/* expression begins with # */
+		/* switch to unsigned */
+		es->natural = true;
+		++cp;
+		goto skip_spaces;
+	}
+	es->tokp = cp;
+
+	if (c == '\0')
+		es->tok = END;
+	else if (ksh_isalphx(c)) {
+		for (; ksh_isalnux(c); c = *cp)
+			cp++;
+		if (c == '[') {
+			size_t len;
+
+			len = array_ref_len(cp);
+			if (len == 0)
+				evalerr(es, ET_STR, "missing ]");
+			cp += len;
+		}
+		if (es->noassign) {
+			es->val = tempvar();
+			es->val->flag |= EXPRLVALUE;
+		} else {
+			strndupx(tvar, es->tokp, cp - es->tokp, ATEMP);
+			es->val = global(tvar);
+			afree(tvar, ATEMP);
+		}
+		es->tok = VAR;
+	} else if (c == '1' && cp[1] == '#') {
+		cp += 2;
+		cp += utf_ptradj(cp);
+		strndupx(tvar, es->tokp, cp - es->tokp, ATEMP);
+		goto process_tvar;
+#ifndef MKSH_SMALL
+	} else if (c == '\'') {
+		++cp;
+		cp += utf_ptradj(cp);
+		if (*cp++ != '\'')
+			evalerr(es, ET_STR,
+			    "multi-character character constant");
+		/* 'x' -> 1#x (x = one multibyte character) */
+		c = cp - es->tokp;
+		tvar = alloc(c + /* NUL */ 1, ATEMP);
+		tvar[0] = '1';
+		tvar[1] = '#';
+		memcpy(tvar + 2, es->tokp + 1, c - 2);
+		tvar[c] = '\0';
+		goto process_tvar;
+#endif
+	} else if (ksh_isdigit(c)) {
+		while (c != '_' && (ksh_isalnux(c) || c == '#'))
+			c = *cp++;
+		strndupx(tvar, es->tokp, --cp - es->tokp, ATEMP);
+ process_tvar:
+		es->val = tempvar();
+		es->val->flag &= ~INTEGER;
+		es->val->type = 0;
+		es->val->val.s = tvar;
+		if (setint_v(es->val, es->val, es->arith) == NULL)
+			evalerr(es, ET_BADLIT, tvar);
+		afree(tvar, ATEMP);
+		es->tok = LIT;
+	} else {
+		int i, n0;
+
+		for (i = 0; (n0 = opinfo[i].name[0]); i++)
+			if (c == n0 && strncmp(cp, opinfo[i].name,
+			    (size_t)opinfo[i].len) == 0) {
+				es->tok = (enum token)i;
+				cp += opinfo[i].len;
+				break;
+			}
+		if (!n0)
+			es->tok = BAD;
+	}
+	es->tokp = cp;
+}
+
+static void
+assign_check(Expr_state *es, enum token op, struct tbl *vasn)
+{
+	if (es->tok == END || !vasn ||
+	    (vasn->name[0] == '\0' && !(vasn->flag & EXPRLVALUE)))
+		evalerr(es, ET_LVALUE, opinfo[(int)op].name);
+	else if (vasn->flag & RDONLY)
+		evalerr(es, ET_RDONLY, opinfo[(int)op].name);
+}
+
+struct tbl *
+tempvar(void)
+{
+	struct tbl *vp;
+
+	vp = alloc(sizeof(struct tbl), ATEMP);
+	vp->flag = ISSET|INTEGER;
+	vp->type = 0;
+	vp->areap = ATEMP;
+	vp->ua.hval = 0;
+	vp->val.i = 0;
+	vp->name[0] = '\0';
+	return (vp);
+}
+
+/* cast (string) variable to temporary integer variable */
+static struct tbl *
+intvar(Expr_state *es, struct tbl *vp)
+{
+	struct tbl *vq;
+
+	/* try to avoid replacing a temp var with another temp var */
+	if (vp->name[0] == '\0' &&
+	    (vp->flag & (ISSET|INTEGER|EXPRLVALUE)) == (ISSET|INTEGER))
+		return (vp);
+
+	vq = tempvar();
+	if (setint_v(vq, vp, es->arith) == NULL) {
+		if (vp->flag & EXPRINEVAL)
+			evalerr(es, ET_RECURSIVE, vp->name);
+		es->evaling = vp;
+		vp->flag |= EXPRINEVAL;
+		v_evaluate(vq, str_val(vp), KSH_UNWIND_ERROR, es->arith);
+		vp->flag &= ~EXPRINEVAL;
+		es->evaling = NULL;
+	}
+	return (vq);
+}
+
+
+/*
+ * UTF-8 support code: high-level functions
+ */
+
+int
+utf_widthadj(const char *src, const char **dst)
+{
+	size_t len;
+	unsigned int wc;
+	int width;
+
+	if (!UTFMODE || (len = utf_mbtowc(&wc, src)) == (size_t)-1 ||
+	    wc == 0)
+		len = width = 1;
+	else if ((width = utf_wcwidth(wc)) < 0)
+		/* XXX use 2 for x_zotc3 here? */
+		width = 1;
+
+	if (dst)
+		*dst = src + len;
+	return (width);
+}
+
+size_t
+utf_mbswidth(const char *s)
+{
+	size_t len, width = 0;
+	unsigned int wc;
+	int cw;
+
+	if (!UTFMODE)
+		return (strlen(s));
+
+	while (*s)
+		if (((len = utf_mbtowc(&wc, s)) == (size_t)-1) ||
+		    ((cw = utf_wcwidth(wc)) == -1)) {
+			s++;
+			width += 1;
+		} else {
+			s += len;
+			width += cw;
+		}
+	return (width);
+}
+
+const char *
+utf_skipcols(const char *p, int cols)
+{
+	int c = 0;
+
+	while (c < cols) {
+		if (!*p)
+			return (p + cols - c);
+		c += utf_widthadj(p, &p);
+	}
+	return (p);
+}
+
+size_t
+utf_ptradj(const char *src)
+{
+	register size_t n;
+
+	if (!UTFMODE ||
+	    *(const unsigned char *)(src) < 0xC2 ||
+	    (n = utf_mbtowc(NULL, src)) == (size_t)-1)
+		n = 1;
+	return (n);
+}
+
+/*
+ * UTF-8 support code: low-level functions
+ */
+
+/* CESU-8 multibyte and wide character conversion crafted for mksh */
+
+size_t
+utf_mbtowc(unsigned int *dst, const char *src)
+{
+	const unsigned char *s = (const unsigned char *)src;
+	unsigned int c, wc;
+
+	if ((wc = *s++) < 0x80) {
+ out:
+		if (dst != NULL)
+			*dst = wc;
+		return (wc ? ((const char *)s - src) : 0);
+	}
+	if (wc < 0xC2 || wc >= 0xF0)
+		/* < 0xC0: spurious second byte */
+		/* < 0xC2: non-minimalistic mapping error in 2-byte seqs */
+		/* > 0xEF: beyond BMP */
+		goto ilseq;
+
+	if (wc < 0xE0) {
+		wc = (wc & 0x1F) << 6;
+		if (((c = *s++) & 0xC0) != 0x80)
+			goto ilseq;
+		wc |= c & 0x3F;
+		goto out;
+	}
+
+	wc = (wc & 0x0F) << 12;
+
+	if (((c = *s++) & 0xC0) != 0x80)
+		goto ilseq;
+	wc |= (c & 0x3F) << 6;
+
+	if (((c = *s++) & 0xC0) != 0x80)
+		goto ilseq;
+	wc |= c & 0x3F;
+
+	/* Check for non-minimalistic mapping error in 3-byte seqs */
+	if (wc >= 0x0800 && wc <= 0xFFFD)
+		goto out;
+ ilseq:
+	return ((size_t)(-1));
+}
+
+size_t
+utf_wctomb(char *dst, unsigned int wc)
+{
+	unsigned char *d;
+
+	if (wc < 0x80) {
+		*dst = wc;
+		return (1);
+	}
+
+	d = (unsigned char *)dst;
+	if (wc < 0x0800)
+		*d++ = (wc >> 6) | 0xC0;
+	else {
+		*d++ = ((wc = wc > 0xFFFD ? 0xFFFD : wc) >> 12) | 0xE0;
+		*d++ = ((wc >> 6) & 0x3F) | 0x80;
+	}
+	*d++ = (wc & 0x3F) | 0x80;
+	return ((char *)d - dst);
+}
+
+/*
+ * Wrapper around access(2) because it says root can execute everything
+ * on some operating systems. Does not set errno, no user needs it. Use
+ * this iff mode can have the X_OK bit set, access otherwise.
+ */
+int
+ksh_access(const char *fn, int mode)
+{
+	int rv;
+	struct stat sb;
+
+	if ((rv = access(fn, mode)) == 0 && kshuid == 0 && (mode & X_OK) &&
+	    (rv = stat(fn, &sb)) == 0 && !S_ISDIR(sb.st_mode) &&
+	    (sb.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH)) == 0)
+		rv = -1;
+
+	return (rv);
+}
+
+/* From: X11/xc/programs/xterm/wcwidth.c,v 1.8 2014/06/24 19:53:53 tg Exp $ */
+
+struct mb_ucsrange {
+	unsigned short beg;
+	unsigned short end;
+};
+
+static int mb_ucsbsearch(const struct mb_ucsrange arr[], size_t elems,
+    unsigned int val) MKSH_A_PURE;
+
+/*
+ * Generated by MirOS: contrib/code/Snippets/eawparse,v 1.2 2013/11/30 13:45:17 tg Exp $
+ * from the Unicode Character Database, Version 7.0.0
+ */
+
+static const struct mb_ucsrange mb_ucs_combining[] = {
+	{ 0x0300, 0x036F },
+	{ 0x0483, 0x0489 },
+	{ 0x0591, 0x05BD },
+	{ 0x05BF, 0x05BF },
+	{ 0x05C1, 0x05C2 },
+	{ 0x05C4, 0x05C5 },
+	{ 0x05C7, 0x05C7 },
+	{ 0x0600, 0x0605 },
+	{ 0x0610, 0x061A },
+	{ 0x061C, 0x061C },
+	{ 0x064B, 0x065F },
+	{ 0x0670, 0x0670 },
+	{ 0x06D6, 0x06DD },
+	{ 0x06DF, 0x06E4 },
+	{ 0x06E7, 0x06E8 },
+	{ 0x06EA, 0x06ED },
+	{ 0x070F, 0x070F },
+	{ 0x0711, 0x0711 },
+	{ 0x0730, 0x074A },
+	{ 0x07A6, 0x07B0 },
+	{ 0x07EB, 0x07F3 },
+	{ 0x0816, 0x0819 },
+	{ 0x081B, 0x0823 },
+	{ 0x0825, 0x0827 },
+	{ 0x0829, 0x082D },
+	{ 0x0859, 0x085B },
+	{ 0x08E4, 0x0902 },
+	{ 0x093A, 0x093A },
+	{ 0x093C, 0x093C },
+	{ 0x0941, 0x0948 },
+	{ 0x094D, 0x094D },
+	{ 0x0951, 0x0957 },
+	{ 0x0962, 0x0963 },
+	{ 0x0981, 0x0981 },
+	{ 0x09BC, 0x09BC },
+	{ 0x09C1, 0x09C4 },
+	{ 0x09CD, 0x09CD },
+	{ 0x09E2, 0x09E3 },
+	{ 0x0A01, 0x0A02 },
+	{ 0x0A3C, 0x0A3C },
+	{ 0x0A41, 0x0A42 },
+	{ 0x0A47, 0x0A48 },
+	{ 0x0A4B, 0x0A4D },
+	{ 0x0A51, 0x0A51 },
+	{ 0x0A70, 0x0A71 },
+	{ 0x0A75, 0x0A75 },
+	{ 0x0A81, 0x0A82 },
+	{ 0x0ABC, 0x0ABC },
+	{ 0x0AC1, 0x0AC5 },
+	{ 0x0AC7, 0x0AC8 },
+	{ 0x0ACD, 0x0ACD },
+	{ 0x0AE2, 0x0AE3 },
+	{ 0x0B01, 0x0B01 },
+	{ 0x0B3C, 0x0B3C },
+	{ 0x0B3F, 0x0B3F },
+	{ 0x0B41, 0x0B44 },
+	{ 0x0B4D, 0x0B4D },
+	{ 0x0B56, 0x0B56 },
+	{ 0x0B62, 0x0B63 },
+	{ 0x0B82, 0x0B82 },
+	{ 0x0BC0, 0x0BC0 },
+	{ 0x0BCD, 0x0BCD },
+	{ 0x0C00, 0x0C00 },
+	{ 0x0C3E, 0x0C40 },
+	{ 0x0C46, 0x0C48 },
+	{ 0x0C4A, 0x0C4D },
+	{ 0x0C55, 0x0C56 },
+	{ 0x0C62, 0x0C63 },
+	{ 0x0C81, 0x0C81 },
+	{ 0x0CBC, 0x0CBC },
+	{ 0x0CBF, 0x0CBF },
+	{ 0x0CC6, 0x0CC6 },
+	{ 0x0CCC, 0x0CCD },
+	{ 0x0CE2, 0x0CE3 },
+	{ 0x0D01, 0x0D01 },
+	{ 0x0D41, 0x0D44 },
+	{ 0x0D4D, 0x0D4D },
+	{ 0x0D62, 0x0D63 },
+	{ 0x0DCA, 0x0DCA },
+	{ 0x0DD2, 0x0DD4 },
+	{ 0x0DD6, 0x0DD6 },
+	{ 0x0E31, 0x0E31 },
+	{ 0x0E34, 0x0E3A },
+	{ 0x0E47, 0x0E4E },
+	{ 0x0EB1, 0x0EB1 },
+	{ 0x0EB4, 0x0EB9 },
+	{ 0x0EBB, 0x0EBC },
+	{ 0x0EC8, 0x0ECD },
+	{ 0x0F18, 0x0F19 },
+	{ 0x0F35, 0x0F35 },
+	{ 0x0F37, 0x0F37 },
+	{ 0x0F39, 0x0F39 },
+	{ 0x0F71, 0x0F7E },
+	{ 0x0F80, 0x0F84 },
+	{ 0x0F86, 0x0F87 },
+	{ 0x0F8D, 0x0F97 },
+	{ 0x0F99, 0x0FBC },
+	{ 0x0FC6, 0x0FC6 },
+	{ 0x102D, 0x1030 },
+	{ 0x1032, 0x1037 },
+	{ 0x1039, 0x103A },
+	{ 0x103D, 0x103E },
+	{ 0x1058, 0x1059 },
+	{ 0x105E, 0x1060 },
+	{ 0x1071, 0x1074 },
+	{ 0x1082, 0x1082 },
+	{ 0x1085, 0x1086 },
+	{ 0x108D, 0x108D },
+	{ 0x109D, 0x109D },
+	{ 0x1160, 0x11FF },
+	{ 0x135D, 0x135F },
+	{ 0x1712, 0x1714 },
+	{ 0x1732, 0x1734 },
+	{ 0x1752, 0x1753 },
+	{ 0x1772, 0x1773 },
+	{ 0x17B4, 0x17B5 },
+	{ 0x17B7, 0x17BD },
+	{ 0x17C6, 0x17C6 },
+	{ 0x17C9, 0x17D3 },
+	{ 0x17DD, 0x17DD },
+	{ 0x180B, 0x180E },
+	{ 0x18A9, 0x18A9 },
+	{ 0x1920, 0x1922 },
+	{ 0x1927, 0x1928 },
+	{ 0x1932, 0x1932 },
+	{ 0x1939, 0x193B },
+	{ 0x1A17, 0x1A18 },
+	{ 0x1A1B, 0x1A1B },
+	{ 0x1A56, 0x1A56 },
+	{ 0x1A58, 0x1A5E },
+	{ 0x1A60, 0x1A60 },
+	{ 0x1A62, 0x1A62 },
+	{ 0x1A65, 0x1A6C },
+	{ 0x1A73, 0x1A7C },
+	{ 0x1A7F, 0x1A7F },
+	{ 0x1AB0, 0x1ABE },
+	{ 0x1B00, 0x1B03 },
+	{ 0x1B34, 0x1B34 },
+	{ 0x1B36, 0x1B3A },
+	{ 0x1B3C, 0x1B3C },
+	{ 0x1B42, 0x1B42 },
+	{ 0x1B6B, 0x1B73 },
+	{ 0x1B80, 0x1B81 },
+	{ 0x1BA2, 0x1BA5 },
+	{ 0x1BA8, 0x1BA9 },
+	{ 0x1BAB, 0x1BAD },
+	{ 0x1BE6, 0x1BE6 },
+	{ 0x1BE8, 0x1BE9 },
+	{ 0x1BED, 0x1BED },
+	{ 0x1BEF, 0x1BF1 },
+	{ 0x1C2C, 0x1C33 },
+	{ 0x1C36, 0x1C37 },
+	{ 0x1CD0, 0x1CD2 },
+	{ 0x1CD4, 0x1CE0 },
+	{ 0x1CE2, 0x1CE8 },
+	{ 0x1CED, 0x1CED },
+	{ 0x1CF4, 0x1CF4 },
+	{ 0x1CF8, 0x1CF9 },
+	{ 0x1DC0, 0x1DF5 },
+	{ 0x1DFC, 0x1DFF },
+	{ 0x200B, 0x200F },
+	{ 0x202A, 0x202E },
+	{ 0x2060, 0x2064 },
+	{ 0x2066, 0x206F },
+	{ 0x20D0, 0x20F0 },
+	{ 0x2CEF, 0x2CF1 },
+	{ 0x2D7F, 0x2D7F },
+	{ 0x2DE0, 0x2DFF },
+	{ 0x302A, 0x302D },
+	{ 0x3099, 0x309A },
+	{ 0xA66F, 0xA672 },
+	{ 0xA674, 0xA67D },
+	{ 0xA69F, 0xA69F },
+	{ 0xA6F0, 0xA6F1 },
+	{ 0xA802, 0xA802 },
+	{ 0xA806, 0xA806 },
+	{ 0xA80B, 0xA80B },
+	{ 0xA825, 0xA826 },
+	{ 0xA8C4, 0xA8C4 },
+	{ 0xA8E0, 0xA8F1 },
+	{ 0xA926, 0xA92D },
+	{ 0xA947, 0xA951 },
+	{ 0xA980, 0xA982 },
+	{ 0xA9B3, 0xA9B3 },
+	{ 0xA9B6, 0xA9B9 },
+	{ 0xA9BC, 0xA9BC },
+	{ 0xA9E5, 0xA9E5 },
+	{ 0xAA29, 0xAA2E },
+	{ 0xAA31, 0xAA32 },
+	{ 0xAA35, 0xAA36 },
+	{ 0xAA43, 0xAA43 },
+	{ 0xAA4C, 0xAA4C },
+	{ 0xAA7C, 0xAA7C },
+	{ 0xAAB0, 0xAAB0 },
+	{ 0xAAB2, 0xAAB4 },
+	{ 0xAAB7, 0xAAB8 },
+	{ 0xAABE, 0xAABF },
+	{ 0xAAC1, 0xAAC1 },
+	{ 0xAAEC, 0xAAED },
+	{ 0xAAF6, 0xAAF6 },
+	{ 0xABE5, 0xABE5 },
+	{ 0xABE8, 0xABE8 },
+	{ 0xABED, 0xABED },
+	{ 0xFB1E, 0xFB1E },
+	{ 0xFE00, 0xFE0F },
+	{ 0xFE20, 0xFE2D },
+	{ 0xFEFF, 0xFEFF },
+	{ 0xFFF9, 0xFFFB }
+};
+
+static const struct mb_ucsrange mb_ucs_fullwidth[] = {
+	{ 0x1100, 0x115F },
+	{ 0x2329, 0x232A },
+	{ 0x2E80, 0x303E },
+	{ 0x3040, 0xA4CF },
+	{ 0xA960, 0xA97F },
+	{ 0xAC00, 0xD7A3 },
+	{ 0xF900, 0xFAFF },
+	{ 0xFE10, 0xFE19 },
+	{ 0xFE30, 0xFE6F },
+	{ 0xFF00, 0xFF60 },
+	{ 0xFFE0, 0xFFE6 }
+};
+
+/* simple binary search in ranges, with bounds optimisation */
+static int
+mb_ucsbsearch(const struct mb_ucsrange arr[], size_t elems, unsigned int val)
+{
+	size_t min = 0, mid, max = elems;
+
+	if (val < arr[min].beg || val > arr[max - 1].end)
+		return (0);
+
+	while (min < max) {
+		mid = (min + max) / 2;
+
+		if (val < arr[mid].beg)
+			max = mid;
+		else if (val > arr[mid].end)
+			min = mid + 1;
+		else
+			return (1);
+	}
+	return (0);
+}
+
+/* Unix column width of a wide character (Unicode code point, really) */
+int
+utf_wcwidth(unsigned int wc)
+{
+	/* except NUL, C0/C1 control characters and DEL yield -1 */
+	if (wc < 0x20 || (wc >= 0x7F && wc < 0xA0))
+		return (wc ? -1 : 0);
+
+	/* combining characters use 0 screen columns */
+	if (mb_ucsbsearch(mb_ucs_combining, NELEM(mb_ucs_combining), wc))
+		return (0);
+
+	/* all others use 1 or 2 screen columns */
+	if (mb_ucsbsearch(mb_ucs_fullwidth, NELEM(mb_ucs_fullwidth), wc))
+		return (2);
+	return (1);
+}

Deleted: vendor/MirOS/mksh/R50/funcs.c
===================================================================
--- vendor/MirOS/mksh/dist/funcs.c	2014-06-30 23:58:12 UTC (rev 6706)
+++ vendor/MirOS/mksh/R50/funcs.c	2014-07-01 12:01:17 UTC (rev 6708)
@@ -1,3844 +0,0 @@
-/*	$OpenBSD: c_ksh.c,v 1.33 2009/02/07 14:03:24 kili Exp $	*/
-/*	$OpenBSD: c_sh.c,v 1.43 2013/04/19 17:39:45 deraadt Exp $	*/
-/*	$OpenBSD: c_test.c,v 1.18 2009/03/01 20:11:06 otto Exp $	*/
-/*	$OpenBSD: c_ulimit.c,v 1.17 2008/03/21 12:51:19 millert Exp $	*/
-
-/*-
- * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009,
- *		 2010, 2011, 2012, 2013
- *	Thorsten Glaser <tg at mirbsd.org>
- *
- * Provided that these terms and disclaimer and all copyright notices
- * are retained or reproduced in an accompanying document, permission
- * is granted to deal in this work without restriction, including un-
- * limited rights to use, publicly perform, distribute, sell, modify,
- * merge, give away, or sublicence.
- *
- * This work is provided "AS IS" and WITHOUT WARRANTY of any kind, to
- * the utmost extent permitted by applicable law, neither express nor
- * implied; without malicious intent or gross negligence. In no event
- * may a licensor, author or contributor be held liable for indirect,
- * direct, other damage, loss, or other issues arising in any way out
- * of dealing in the work, even if advised of the possibility of such
- * damage or existence of a defect, except proven that it results out
- * of said person's immediate fault when using the work as intended.
- */
-
-#include "sh.h"
-
-#if HAVE_SELECT
-#if HAVE_SYS_BSDTYPES_H
-#include <sys/bsdtypes.h>
-#endif
-#if HAVE_SYS_SELECT_H
-#include <sys/select.h>
-#endif
-#if HAVE_BSTRING_H
-#include <bstring.h>
-#endif
-#endif
-
-__RCSID("$MirOS: src/bin/mksh/funcs.c,v 1.244 2013/06/03 22:28:32 tg Exp $");
-
-#if HAVE_KILLPG
-/*
- * use killpg if < -1 since -1 does special things
- * for some non-killpg-endowed kills
- */
-#define mksh_kill(p,s)	((p) < -1 ? killpg(-(p), (s)) : kill((p), (s)))
-#else
-/* cross fingers and hope kill is killpg-endowed */
-#define mksh_kill	kill
-#endif
-
-/* XXX conditions correct? */
-#if !defined(RLIM_INFINITY) && !defined(MKSH_NO_LIMITS)
-#define MKSH_NO_LIMITS	1
-#endif
-
-#ifdef MKSH_NO_LIMITS
-#define c_ulimit	c_true
-#endif
-
-/* getn() that prints error */
-static int
-bi_getn(const char *as, int *ai)
-{
-	int rv;
-
-	if (!(rv = getn(as, ai)))
-		bi_errorf("%s: %s", as, "bad number");
-	return (rv);
-}
-
-static int
-c_true(const char **wp MKSH_A_UNUSED)
-{
-	return (0);
-}
-
-static int
-c_false(const char **wp MKSH_A_UNUSED)
-{
-	return (1);
-}
-
-/*
- * A leading = means assignments before command are kept.
- * A leading * means a POSIX special builtin.
- */
-const struct builtin mkshbuiltins[] = {
-	{"*=.", c_dot},
-	{"*=:", c_true},
-	{"[", c_test},
-	/* no =: AT&T manual wrong */
-	{Talias, c_alias},
-	{"*=break", c_brkcont},
-	{Tgbuiltin, c_builtin},
-	{"cat", c_cat},
-	{"cd", c_cd},
-	/* dash compatibility hack */
-	{"chdir", c_cd},
-	{"command", c_command},
-	{"*=continue", c_brkcont},
-	{"echo", c_print},
-	{"*=eval", c_eval},
-	{"*=exec", c_exec},
-	{"*=exit", c_exitreturn},
-	{Tsgexport, c_typeset},
-	{"false", c_false},
-	{"fc", c_fc},
-	{"getopts", c_getopts},
-	{"=global", c_typeset},
-	{"jobs", c_jobs},
-	{"kill", c_kill},
-	{"let", c_let},
-	{"let]", c_let},
-	{"print", c_print},
-	{"pwd", c_pwd},
-	{"read", c_read},
-	{Tsgreadonly, c_typeset},
-	{"realpath", c_realpath},
-	{"rename", c_rename},
-	{"*=return", c_exitreturn},
-	{Tsgset, c_set},
-	{"*=shift", c_shift},
-	{"test", c_test},
-	{"*=times", c_times},
-	{"*=trap", c_trap},
-	{"true", c_true},
-	{T_typeset, c_typeset},
-	{"ulimit", c_ulimit},
-	{"umask", c_umask},
-	{Tunalias, c_unalias},
-	{Tsgunset, c_unset},
-	{"=wait", c_wait},
-	{"whence", c_whence},
-#ifndef MKSH_UNEMPLOYED
-	{"bg", c_fgbg},
-	{"fg", c_fgbg},
-#endif
-#ifndef MKSH_NO_CMDLINE_EDITING
-	{"bind", c_bind},
-#endif
-#if HAVE_MKNOD
-	{"mknod", c_mknod},
-#endif
-#ifdef MKSH_PRINTF_BUILTIN
-	{"printf", c_printf},
-#endif
-#if HAVE_SELECT
-	{"sleep", c_sleep},
-#endif
-#ifdef __MirBSD__
-	/* alias to "true" for historical reasons */
-	{"domainname", c_true},
-#endif
-	{NULL, (int (*)(const char **))NULL}
-};
-
-struct kill_info {
-	int num_width;
-	int name_width;
-};
-
-static const struct t_op {
-	char op_text[4];
-	Test_op op_num;
-} u_ops[] = {
-	{"-a",	TO_FILAXST },
-	{"-b",	TO_FILBDEV },
-	{"-c",	TO_FILCDEV },
-	{"-d",	TO_FILID },
-	{"-e",	TO_FILEXST },
-	{"-f",	TO_FILREG },
-	{"-G",	TO_FILGID },
-	{"-g",	TO_FILSETG },
-	{"-h",	TO_FILSYM },
-	{"-H",	TO_FILCDF },
-	{"-k",	TO_FILSTCK },
-	{"-L",	TO_FILSYM },
-	{"-n",	TO_STNZE },
-	{"-O",	TO_FILUID },
-	{"-o",	TO_OPTION },
-	{"-p",	TO_FILFIFO },
-	{"-r",	TO_FILRD },
-	{"-s",	TO_FILGZ },
-	{"-S",	TO_FILSOCK },
-	{"-t",	TO_FILTT },
-	{"-u",	TO_FILSETU },
-	{"-w",	TO_FILWR },
-	{"-x",	TO_FILEX },
-	{"-z",	TO_STZER },
-	{"",	TO_NONOP }
-};
-static const struct t_op b_ops[] = {
-	{"=",	TO_STEQL },
-	{"==",	TO_STEQL },
-	{"!=",	TO_STNEQ },
-	{"<",	TO_STLT },
-	{">",	TO_STGT },
-	{"-eq",	TO_INTEQ },
-	{"-ne",	TO_INTNE },
-	{"-gt",	TO_INTGT },
-	{"-ge",	TO_INTGE },
-	{"-lt",	TO_INTLT },
-	{"-le",	TO_INTLE },
-	{"-ef",	TO_FILEQ },
-	{"-nt",	TO_FILNT },
-	{"-ot",	TO_FILOT },
-	{"",	TO_NONOP }
-};
-
-static int test_oexpr(Test_env *, bool);
-static int test_aexpr(Test_env *, bool);
-static int test_nexpr(Test_env *, bool);
-static int test_primary(Test_env *, bool);
-static Test_op ptest_isa(Test_env *, Test_meta);
-static const char *ptest_getopnd(Test_env *, Test_op, bool);
-static void ptest_error(Test_env *, int, const char *);
-static char *kill_fmt_entry(char *, size_t, unsigned int, const void *);
-static void p_time(struct shf *, bool, long, int, int,
-    const char *, const char *);
-
-int
-c_pwd(const char **wp)
-{
-	int optc;
-	bool physical = tobool(Flag(FPHYSICAL));
-	char *p, *allocd = NULL;
-
-	while ((optc = ksh_getopt(wp, &builtin_opt, "LP")) != -1)
-		switch (optc) {
-		case 'L':
-			physical = false;
-			break;
-		case 'P':
-			physical = true;
-			break;
-		case '?':
-			return (1);
-		}
-	wp += builtin_opt.optind;
-
-	if (wp[0]) {
-		bi_errorf("too many arguments");
-		return (1);
-	}
-	p = current_wd[0] ? (physical ? allocd = do_realpath(current_wd) :
-	    current_wd) : NULL;
-	/* LINTED use of access */
-	if (p && access(p, R_OK) < 0)
-		p = NULL;
-	if (!p && !(p = allocd = ksh_get_wd())) {
-		bi_errorf("%s: %s", "can't determine current directory",
-		    cstrerror(errno));
-		return (1);
-	}
-	shprintf("%s\n", p);
-	afree(allocd, ATEMP);
-	return (0);
-}
-
-static const char *s_ptr;
-static int s_get(void);
-static void s_put(int);
-
-int
-c_print(const char **wp)
-{
-#define PO_NL		BIT(0)	/* print newline */
-#define PO_EXPAND	BIT(1)	/* expand backslash sequences */
-#define PO_PMINUSMINUS	BIT(2)	/* print a -- argument */
-#define PO_HIST		BIT(3)	/* print to history instead of stdout */
-#define PO_COPROC	BIT(4)	/* printing to coprocess: block SIGPIPE */
-	int fd = 1, c;
-	int flags = PO_EXPAND | PO_NL;
-	const char *s, *emsg;
-	XString xs;
-	char *xp;
-
-	if (wp[0][0] == 'e') {
-		/* echo builtin */
-		wp++;
-#ifdef MKSH_MIDNIGHTBSD01ASH_COMPAT
-		if (Flag(FSH)) {
-			/*
-			 * MidnightBSD /bin/sh needs a BSD echo, that is,
-			 * one that supports -e but does not enable it by
-			 * default
-			 */
-			flags = PO_NL;
-		}
-#endif
-		if (Flag(FPOSIX) ||
-#ifndef MKSH_MIDNIGHTBSD01ASH_COMPAT
-		    Flag(FSH) ||
-#endif
-		    Flag(FAS_BUILTIN)) {
-			/* Debian Policy 10.4 compliant "echo" builtin */
-			if (*wp && !strcmp(*wp, "-n")) {
-				/* we recognise "-n" only as the first arg */
-				flags = 0;
-				wp++;
-			} else
-				/* otherwise, we print everything as-is */
-				flags = PO_NL;
-		} else {
-			int nflags = flags;
-
-			/**
-			 * a compromise between sysV and BSD echo commands:
-			 * escape sequences are enabled by default, and -n,
-			 * -e and -E are recognised if they appear in argu-
-			 * ments with no illegal options (ie, echo -nq will
-			 * print -nq).
-			 * Different from sysV echo since options are reco-
-			 * gnised, different from BSD echo since escape se-
-			 * quences are enabled by default.
-			 */
-
-			while ((s = *wp) && *s == '-' && s[1]) {
-				while (*++s)
-					if (*s == 'n')
-						nflags &= ~PO_NL;
-					else if (*s == 'e')
-						nflags |= PO_EXPAND;
-					else if (*s == 'E')
-						nflags &= ~PO_EXPAND;
-					else
-						/*
-						 * bad option: don't use
-						 * nflags, print argument
-						 */
-						break;
-
-				if (*s)
-					break;
-				wp++;
-				flags = nflags;
-			}
-		}
-	} else {
-		int optc;
-		const char *opts = "Rnprsu,";
-
-		while ((optc = ksh_getopt(wp, &builtin_opt, opts)) != -1)
-			switch (optc) {
-			case 'R':
-				/* fake BSD echo command */
-				flags |= PO_PMINUSMINUS;
-				flags &= ~PO_EXPAND;
-				opts = "ne";
-				break;
-			case 'e':
-				flags |= PO_EXPAND;
-				break;
-			case 'n':
-				flags &= ~PO_NL;
-				break;
-			case 'p':
-				if ((fd = coproc_getfd(W_OK, &emsg)) < 0) {
-					bi_errorf("%s: %s", "-p", emsg);
-					return (1);
-				}
-				break;
-			case 'r':
-				flags &= ~PO_EXPAND;
-				break;
-			case 's':
-				flags |= PO_HIST;
-				break;
-			case 'u':
-				if (!*(s = builtin_opt.optarg))
-					fd = 0;
-				else if ((fd = check_fd(s, W_OK, &emsg)) < 0) {
-					bi_errorf("%s: %s: %s", "-u", s, emsg);
-					return (1);
-				}
-				break;
-			case '?':
-				return (1);
-			}
-
-		if (!(builtin_opt.info & GI_MINUSMINUS)) {
-			/* treat a lone - like -- */
-			if (wp[builtin_opt.optind] &&
-			    ksh_isdash(wp[builtin_opt.optind]))
-				builtin_opt.optind++;
-		} else if (flags & PO_PMINUSMINUS)
-			builtin_opt.optind--;
-		wp += builtin_opt.optind;
-	}
-
-	Xinit(xs, xp, 128, ATEMP);
-
-	while (*wp != NULL) {
-		s = *wp;
-		while ((c = *s++) != '\0') {
-			Xcheck(xs, xp);
-			if ((flags & PO_EXPAND) && c == '\\') {
-				s_ptr = s;
-				c = unbksl(false, s_get, s_put);
-				s = s_ptr;
-				if (c == -1) {
-					/* rejected by generic function */
-					switch ((c = *s++)) {
-					case 'c':
-						flags &= ~PO_NL;
-						/* AT&T brain damage */
-						continue;
-					case '\0':
-						s--;
-						c = '\\';
-						break;
-					default:
-						Xput(xs, xp, '\\');
-					}
-				} else if ((unsigned int)c > 0xFF) {
-					/* generic function returned Unicode */
-					char ts[4];
-
-					ts[utf_wctomb(ts, c - 0x100)] = 0;
-					for (c = 0; ts[c]; ++c)
-						Xput(xs, xp, ts[c]);
-					continue;
-				}
-			}
-			Xput(xs, xp, c);
-		}
-		if (*++wp != NULL)
-			Xput(xs, xp, ' ');
-	}
-	if (flags & PO_NL)
-		Xput(xs, xp, '\n');
-
-	if (flags & PO_HIST) {
-		Xput(xs, xp, '\0');
-		histsave(&source->line, Xstring(xs, xp), true, false);
-		Xfree(xs, xp);
-	} else {
-		int len = Xlength(xs, xp);
-		int opipe = 0;
-
-		/*
-		 * Ensure we aren't killed by a SIGPIPE while writing to
-		 * a coprocess. AT&T ksh doesn't seem to do this (seems
-		 * to just check that the co-process is alive which is
-		 * not enough).
-		 */
-		if (coproc.write >= 0 && coproc.write == fd) {
-			flags |= PO_COPROC;
-			opipe = block_pipe();
-		}
-		for (s = Xstring(xs, xp); len > 0; ) {
-			if ((c = write(fd, s, len)) < 0) {
-				if (flags & PO_COPROC)
-					restore_pipe(opipe);
-				if (errno == EINTR) {
-					/* allow user to ^C out */
-					intrcheck();
-					if (flags & PO_COPROC)
-						opipe = block_pipe();
-					continue;
-				}
-				return (1);
-			}
-			s += c;
-			len -= c;
-		}
-		if (flags & PO_COPROC)
-			restore_pipe(opipe);
-	}
-
-	return (0);
-}
-
-static int
-s_get(void)
-{
-	return (*s_ptr++);
-}
-
-static void
-s_put(int c MKSH_A_UNUSED)
-{
-	--s_ptr;
-}
-
-int
-c_whence(const char **wp)
-{
-	struct tbl *tp;
-	const char *id;
-	bool pflag = false, vflag = false, Vflag = false;
-	int rv = 0, optc, fcflags;
-	bool iam_whence = wp[0][0] == 'w';
-	const char *opts = iam_whence ? "pv" : "pvV";
-
-	while ((optc = ksh_getopt(wp, &builtin_opt, opts)) != -1)
-		switch (optc) {
-		case 'p':
-			pflag = true;
-			break;
-		case 'v':
-			vflag = true;
-			break;
-		case 'V':
-			Vflag = true;
-			break;
-		case '?':
-			return (1);
-		}
-	wp += builtin_opt.optind;
-
-	fcflags = FC_BI | FC_PATH | FC_FUNC;
-	if (!iam_whence) {
-		/* Note that -p on its own is deal with in comexec() */
-		if (pflag)
-			fcflags |= FC_DEFPATH;
-		/*
-		 * Convert command options to whence options - note that
-		 * command -pV uses a different path search than whence -v
-		 * or whence -pv. This should be considered a feature.
-		 */
-		vflag = Vflag;
-	}
-	if (pflag)
-		fcflags &= ~(FC_BI | FC_FUNC);
-
-	while ((vflag || rv == 0) && (id = *wp++) != NULL) {
-		uint32_t h = 0;
-
-		tp = NULL;
-		if ((iam_whence || vflag) && !pflag)
-			tp = ktsearch(&keywords, id, h = hash(id));
-		if (!tp && !pflag) {
-			tp = ktsearch(&aliases, id, h ? h : hash(id));
-			if (tp && !(tp->flag & ISSET))
-				tp = NULL;
-		}
-		if (!tp)
-			tp = findcom(id, fcflags);
-		if (vflag || (tp->type != CALIAS && tp->type != CEXEC &&
-		    tp->type != CTALIAS))
-			shf_puts(id, shl_stdout);
-		if (vflag)
-			switch (tp->type) {
-			case CKEYWD:
-			case CALIAS:
-			case CFUNC:
-			case CSHELL:
-				shf_puts(" is a", shl_stdout);
-				break;
-			}
-
-		switch (tp->type) {
-		case CKEYWD:
-			if (vflag)
-				shf_puts(" reserved word", shl_stdout);
-			break;
-		case CALIAS:
-			if (vflag)
-				shprintf("n %s%s for ",
-				    (tp->flag & EXPORT) ? "exported " : null,
-				    Talias);
-			if (!iam_whence && !vflag)
-				shprintf("%s %s=", Talias, id);
-			print_value_quoted(shl_stdout, tp->val.s);
-			break;
-		case CFUNC:
-			if (vflag) {
-				if (tp->flag & EXPORT)
-					shf_puts("n exported", shl_stdout);
-				if (tp->flag & TRACE)
-					shf_puts(" traced", shl_stdout);
-				if (!(tp->flag & ISSET)) {
-					shf_puts(" undefined", shl_stdout);
-					if (tp->u.fpath)
-						shprintf(" (autoload from %s)",
-						    tp->u.fpath);
-				}
-				shf_puts(T_function, shl_stdout);
-			}
-			break;
-		case CSHELL:
-			if (vflag)
-				shprintf("%s %s %s",
-				    (tp->flag & SPEC_BI) ? " special" : null,
-				    "shell", Tbuiltin);
-			break;
-		case CTALIAS:
-		case CEXEC:
-			if (tp->flag & ISSET) {
-				if (vflag) {
-					shf_puts(" is ", shl_stdout);
-					if (tp->type == CTALIAS)
-						shprintf("a tracked %s%s for ",
-						    (tp->flag & EXPORT) ?
-						    "exported " : null,
-						    Talias);
-				}
-				shf_puts(tp->val.s, shl_stdout);
-			} else {
-				if (vflag)
-					shprintf(" %s\n", "not found");
-				rv = 1;
-			}
-			break;
-		default:
-			shprintf("%s is *GOK*", id);
-			break;
-		}
-		if (vflag || !rv)
-			shf_putc('\n', shl_stdout);
-	}
-	return (rv);
-}
-
-/* Deal with command -vV - command -p dealt with in comexec() */
-int
-c_command(const char **wp)
-{
-	/*
-	 * Let c_whence do the work. Note that c_command() must be
-	 * a distinct function from c_whence() (tested in comexec()).
-	 */
-	return (c_whence(wp));
-}
-
-/* typeset, global, export, and readonly */
-static void c_typeset_vardump(struct tbl *, uint32_t, int, bool, bool);
-static void c_typeset_vardump_recursive(struct block *, uint32_t, int, bool,
-    bool);
-int
-c_typeset(const char **wp)
-{
-	struct tbl *vp, **p;
-	uint32_t fset = 0, fclr = 0, flag;
-	int thing = 0, field = 0, base = 0, i;
-	struct block *l;
-	const char *opts;
-	const char *fieldstr = NULL, *basestr = NULL;
-	bool localv = false, func = false, pflag = false, istset = true;
-
-	switch (**wp) {
-
-	/* export */
-	case 'e':
-		fset |= EXPORT;
-		istset = false;
-		break;
-
-	/* readonly */
-	case 'r':
-		fset |= RDONLY;
-		istset = false;
-		break;
-
-	/* set */
-	case 's':
-		/* called with 'typeset -' */
-		break;
-
-	/* typeset */
-	case 't':
-		localv = true;
-		break;
-	}
-
-	/* see comment below regarding possible opions */
-	opts = istset ? "L#R#UZ#afi#lnprtux" : "p";
-
-	builtin_opt.flags |= GF_PLUSOPT;
-	/*
-	 * AT&T ksh seems to have 0-9 as options which are multiplied
-	 * to get a number that is used with -L, -R, -Z or -i (eg, -1R2
-	 * sets right justify in a field of 12). This allows options
-	 * to be grouped in an order (eg, -Lu12), but disallows -i8 -L3 and
-	 * does not allow the number to be specified as a separate argument
-	 * Here, the number must follow the RLZi option, but is optional
-	 * (see the # kludge in ksh_getopt()).
-	 */
-	while ((i = ksh_getopt(wp, &builtin_opt, opts)) != -1) {
-		flag = 0;
-		switch (i) {
-		case 'L':
-			flag = LJUST;
-			fieldstr = builtin_opt.optarg;
-			break;
-		case 'R':
-			flag = RJUST;
-			fieldstr = builtin_opt.optarg;
-			break;
-		case 'U':
-			/*
-			 * AT&T ksh uses u, but this conflicts with
-			 * upper/lower case. If this option is changed,
-			 * need to change the -U below as well
-			 */
-			flag = INT_U;
-			break;
-		case 'Z':
-			flag = ZEROFIL;
-			fieldstr = builtin_opt.optarg;
-			break;
-		case 'a':
-			/*
-			 * this is supposed to set (-a) or unset (+a) the
-			 * indexed array attribute; it does nothing on an
-			 * existing regular string or indexed array though
-			 */
-			break;
-		case 'f':
-			func = true;
-			break;
-		case 'i':
-			flag = INTEGER;
-			basestr = builtin_opt.optarg;
-			break;
-		case 'l':
-			flag = LCASEV;
-			break;
-		case 'n':
-			set_refflag = (builtin_opt.info & GI_PLUS) ?
-			    SRF_DISABLE : SRF_ENABLE;
-			break;
-		/* export, readonly: POSIX -p flag */
-		case 'p':
-			/* typeset: show values as well */
-			pflag = true;
-			if (istset)
-				continue;
-			break;
-		case 'r':
-			flag = RDONLY;
-			break;
-		case 't':
-			flag = TRACE;
-			break;
-		case 'u':
-			/* upper case / autoload */
-			flag = UCASEV_AL;
-			break;
-		case 'x':
-			flag = EXPORT;
-			break;
-		case '?':
- errout:
-			set_refflag = SRF_NOP;
-			return (1);
-		}
-		if (builtin_opt.info & GI_PLUS) {
-			fclr |= flag;
-			fset &= ~flag;
-			thing = '+';
-		} else {
-			fset |= flag;
-			fclr &= ~flag;
-			thing = '-';
-		}
-	}
-
-	if (fieldstr && !bi_getn(fieldstr, &field))
-		goto errout;
-	if (basestr && (!bi_getn(basestr, &base) || base < 1 || base > 36)) {
-		bi_errorf("%s: %s", "bad integer base", basestr);
-		goto errout;
-	}
-
-	if (!(builtin_opt.info & GI_MINUSMINUS) && wp[builtin_opt.optind] &&
-	    (wp[builtin_opt.optind][0] == '-' ||
-	    wp[builtin_opt.optind][0] == '+') &&
-	    wp[builtin_opt.optind][1] == '\0') {
-		thing = wp[builtin_opt.optind][0];
-		builtin_opt.optind++;
-	}
-
-	if (func && (((fset|fclr) & ~(TRACE|UCASEV_AL|EXPORT)) ||
-	    set_refflag != SRF_NOP)) {
-		bi_errorf("only -t, -u and -x options may be used with -f");
-		goto errout;
-	}
-	if (wp[builtin_opt.optind]) {
-		/*
-		 * Take care of exclusions.
-		 * At this point, flags in fset are cleared in fclr and vice
-		 * versa. This property should be preserved.
-		 */
-		if (fset & LCASEV)
-			/* LCASEV has priority over UCASEV_AL */
-			fset &= ~UCASEV_AL;
-		if (fset & LJUST)
-			/* LJUST has priority over RJUST */
-			fset &= ~RJUST;
-		if ((fset & (ZEROFIL|LJUST)) == ZEROFIL) {
-			/* -Z implies -ZR */
-			fset |= RJUST;
-			fclr &= ~RJUST;
-		}
-		/*
-		 * Setting these attributes clears the others, unless they
-		 * are also set in this command
-		 */
-		if ((fset & (LJUST | RJUST | ZEROFIL | UCASEV_AL | LCASEV |
-		    INTEGER | INT_U | INT_L)) || set_refflag != SRF_NOP)
-			fclr |= ~fset & (LJUST | RJUST | ZEROFIL | UCASEV_AL |
-			    LCASEV | INTEGER | INT_U | INT_L);
-	}
-
-	/* set variables and attributes */
-	if (wp[builtin_opt.optind] &&
-	    /* not "typeset -p varname" */
-	    !(!func && pflag && !(fset | fclr))) {
-		int rv = 0;
-		struct tbl *f;
-
-		if (localv && !func)
-			fset |= LOCAL;
-		for (i = builtin_opt.optind; wp[i]; i++) {
-			if (func) {
-				f = findfunc(wp[i], hash(wp[i]),
-				    tobool(fset & UCASEV_AL));
-				if (!f) {
-					/* AT&T ksh does ++rv: bogus */
-					rv = 1;
-					continue;
-				}
-				if (fset | fclr) {
-					f->flag |= fset;
-					f->flag &= ~fclr;
-				} else {
-					fpFUNCTf(shl_stdout, 0,
-					    tobool(f->flag & FKSH),
-					    wp[i], f->val.t);
-					shf_putc('\n', shl_stdout);
-				}
-			} else if (!typeset(wp[i], fset, fclr, field, base)) {
-				bi_errorf("%s: %s", wp[i], "is not an identifier");
-				goto errout;
-			}
-		}
-		set_refflag = SRF_NOP;
-		return (rv);
-	}
-
-	set_refflag = SRF_NOP;
-	/* list variables and attributes */
-
-	/* no difference at this point.. */
-	flag = fset | fclr;
-	if (func) {
-		for (l = e->loc; l; l = l->next) {
-			for (p = ktsort(&l->funs); (vp = *p++); ) {
-				if (flag && (vp->flag & flag) == 0)
-					continue;
-				if (thing == '-')
-					fpFUNCTf(shl_stdout, 0,
-					    tobool(vp->flag & FKSH),
-					    vp->name, vp->val.t);
-				else
-					shf_puts(vp->name, shl_stdout);
-				shf_putc('\n', shl_stdout);
-			}
-		}
-	} else if (wp[builtin_opt.optind]) {
-		for (i = builtin_opt.optind; wp[i]; i++) {
-			varsearch(e->loc, &vp, wp[i], hash(wp[i]));
-			c_typeset_vardump(vp, flag, thing, pflag, istset);
-		}
-	} else
-		c_typeset_vardump_recursive(e->loc, flag, thing, pflag, istset);
-	return (0);
-}
-
-static void
-c_typeset_vardump_recursive(struct block *l, uint32_t flag, int thing,
-    bool pflag, bool istset)
-{
-	struct tbl **blockvars, *vp;
-
-	if (l->next)
-		c_typeset_vardump_recursive(l->next, flag, thing, pflag, istset);
-	blockvars = ktsort(&l->vars);
-	while ((vp = *blockvars++))
-		c_typeset_vardump(vp, flag, thing, pflag, istset);
-	/*XXX doesn’t this leak? */
-}
-
-static void
-c_typeset_vardump(struct tbl *vp, uint32_t flag, int thing, bool pflag,
-    bool istset)
-{
-	struct tbl *tvp;
-	int any_set = 0;
-	char *s;
-
-	if (!vp)
-		return;
-
-	/*
-	 * See if the parameter is set (for arrays, if any
-	 * element is set).
-	 */
-	for (tvp = vp; tvp; tvp = tvp->u.array)
-		if (tvp->flag & ISSET) {
-			any_set = 1;
-			break;
-		}
-
-	/*
-	 * Check attributes - note that all array elements
-	 * have (should have?) the same attributes, so checking
-	 * the first is sufficient.
-	 *
-	 * Report an unset param only if the user has
-	 * explicitly given it some attribute (like export);
-	 * otherwise, after "echo $FOO", we would report FOO...
-	 */
-	if (!any_set && !(vp->flag & USERATTRIB))
-		return;
-	if (flag && (vp->flag & flag) == 0)
-		return;
-	if (!(vp->flag & ARRAY))
-		/* optimise later conditionals */
-		any_set = 0;
-	do {
-		/*
-		 * Ignore array elements that aren't set unless there
-		 * are no set elements, in which case the first is
-		 * reported on
-		 */
-		if (any_set && !(vp->flag & ISSET))
-			continue;
-		/* no arguments */
-		if (!thing && !flag) {
-			if (any_set == 1) {
-				shprintf("%s %s %s\n", Tset, "-A", vp->name);
-				any_set = 2;
-			}
-			/*
-			 * AT&T ksh prints things like export, integer,
-			 * leftadj, zerofill, etc., but POSIX says must
-			 * be suitable for re-entry...
-			 */
-			shprintf("%s %s", Ttypeset, "");
-			if (((vp->flag & (ARRAY | ASSOC)) == ASSOC))
-				shprintf("%s ", "-n");
-			if ((vp->flag & INTEGER))
-				shprintf("%s ", "-i");
-			if ((vp->flag & EXPORT))
-				shprintf("%s ", "-x");
-			if ((vp->flag & RDONLY))
-				shprintf("%s ", "-r");
-			if ((vp->flag & TRACE))
-				shprintf("%s ", "-t");
-			if ((vp->flag & LJUST))
-				shprintf("-L%d ", vp->u2.field);
-			if ((vp->flag & RJUST))
-				shprintf("-R%d ", vp->u2.field);
-			if ((vp->flag & ZEROFIL))
-				shprintf("%s ", "-Z");
-			if ((vp->flag & LCASEV))
-				shprintf("%s ", "-l");
-			if ((vp->flag & UCASEV_AL))
-				shprintf("%s ", "-u");
-			if ((vp->flag & INT_U))
-				shprintf("%s ", "-U");
-		} else if (pflag) {
-			shprintf("%s %s", istset ? Ttypeset :
-			    (flag & EXPORT) ? Texport : Treadonly, "");
-		}
-		if (any_set)
-			shprintf("%s[%lu]", vp->name, arrayindex(vp));
-		else
-			shf_puts(vp->name, shl_stdout);
-		if ((!thing && !flag && pflag) ||
-		    (thing == '-' && (vp->flag & ISSET))) {
-			s = str_val(vp);
-			shf_putc('=', shl_stdout);
-			/* AT&T ksh can't have justified integers... */
-			if ((vp->flag & (INTEGER | LJUST | RJUST)) == INTEGER)
-				shf_puts(s, shl_stdout);
-			else
-				print_value_quoted(shl_stdout, s);
-		}
-		shf_putc('\n', shl_stdout);
-
-		/*
-		 * Only report first 'element' of an array with
-		 * no set elements.
-		 */
-		if (!any_set)
-			return;
-	} while ((vp = vp->u.array));
-}
-
-int
-c_alias(const char **wp)
-{
-	struct table *t = &aliases;
-	int rv = 0, prefix = 0;
-	bool rflag = false, tflag, Uflag = false, pflag = false;
-	uint32_t xflag = 0;
-	int optc;
-
-	builtin_opt.flags |= GF_PLUSOPT;
-	while ((optc = ksh_getopt(wp, &builtin_opt, "dprtUx")) != -1) {
-		prefix = builtin_opt.info & GI_PLUS ? '+' : '-';
-		switch (optc) {
-		case 'd':
-#ifdef MKSH_NOPWNAM
-			t = NULL;	/* fix "alias -dt" */
-#else
-			t = &homedirs;
-#endif
-			break;
-		case 'p':
-			pflag = true;
-			break;
-		case 'r':
-			rflag = true;
-			break;
-		case 't':
-			t = &taliases;
-			break;
-		case 'U':
-			/*
-			 * kludge for tracked alias initialization
-			 * (don't do a path search, just make an entry)
-			 */
-			Uflag = true;
-			break;
-		case 'x':
-			xflag = EXPORT;
-			break;
-		case '?':
-			return (1);
-		}
-	}
-#ifdef MKSH_NOPWNAM
-	if (t == NULL)
-		return (0);
-#endif
-	wp += builtin_opt.optind;
-
-	if (!(builtin_opt.info & GI_MINUSMINUS) && *wp &&
-	    (wp[0][0] == '-' || wp[0][0] == '+') && wp[0][1] == '\0') {
-		prefix = wp[0][0];
-		wp++;
-	}
-
-	tflag = t == &taliases;
-
-	/* "hash -r" means reset all the tracked aliases.. */
-	if (rflag) {
-		static const char *args[] = {
-			Tunalias, "-ta", NULL
-		};
-
-		if (!tflag || *wp) {
-			shprintf("%s: -r flag can only be used with -t"
-			    " and without arguments\n", Talias);
-			return (1);
-		}
-		ksh_getopt_reset(&builtin_opt, GF_ERROR);
-		return (c_unalias(args));
-	}
-
-	if (*wp == NULL) {
-		struct tbl *ap, **p;
-
-		for (p = ktsort(t); (ap = *p++) != NULL; )
-			if ((ap->flag & (ISSET|xflag)) == (ISSET|xflag)) {
-				if (pflag)
-					shprintf("%s ", Talias);
-				shf_puts(ap->name, shl_stdout);
-				if (prefix != '+') {
-					shf_putc('=', shl_stdout);
-					print_value_quoted(shl_stdout, ap->val.s);
-				}
-				shf_putc('\n', shl_stdout);
-			}
-	}
-
-	for (; *wp != NULL; wp++) {
-		const char *alias = *wp, *val, *newval;
-		char *xalias = NULL;
-		struct tbl *ap;
-		uint32_t h;
-
-		if ((val = cstrchr(alias, '='))) {
-			strndupx(xalias, alias, val++ - alias, ATEMP);
-			alias = xalias;
-		}
-		h = hash(alias);
-		if (val == NULL && !tflag && !xflag) {
-			ap = ktsearch(t, alias, h);
-			if (ap != NULL && (ap->flag&ISSET)) {
-				if (pflag)
-					shprintf("%s ", Talias);
-				shf_puts(ap->name, shl_stdout);
-				if (prefix != '+') {
-					shf_putc('=', shl_stdout);
-					print_value_quoted(shl_stdout, ap->val.s);
-				}
-				shf_putc('\n', shl_stdout);
-			} else {
-				shprintf("%s %s %s\n", alias, Talias,
-				    "not found");
-				rv = 1;
-			}
-			continue;
-		}
-		ap = ktenter(t, alias, h);
-		ap->type = tflag ? CTALIAS : CALIAS;
-		/* Are we setting the value or just some flags? */
-		if ((val && !tflag) || (!val && tflag && !Uflag)) {
-			if (ap->flag&ALLOC) {
-				ap->flag &= ~(ALLOC|ISSET);
-				afree(ap->val.s, APERM);
-			}
-			/* ignore values for -t (AT&T ksh does this) */
-			newval = tflag ?
-			    search_path(alias, path, X_OK, NULL) :
-			    val;
-			if (newval) {
-				strdupx(ap->val.s, newval, APERM);
-				ap->flag |= ALLOC|ISSET;
-			} else
-				ap->flag &= ~ISSET;
-		}
-		ap->flag |= DEFINED;
-		if (prefix == '+')
-			ap->flag &= ~xflag;
-		else
-			ap->flag |= xflag;
-		afree(xalias, ATEMP);
-	}
-
-	return (rv);
-}
-
-int
-c_unalias(const char **wp)
-{
-	struct table *t = &aliases;
-	struct tbl *ap;
-	int optc, rv = 0;
-	bool all = false;
-
-	while ((optc = ksh_getopt(wp, &builtin_opt, "adt")) != -1)
-		switch (optc) {
-		case 'a':
-			all = true;
-			break;
-		case 'd':
-#ifdef MKSH_NOPWNAM
-			/* fix "unalias -dt" */
-			t = NULL;
-#else
-			t = &homedirs;
-#endif
-			break;
-		case 't':
-			t = &taliases;
-			break;
-		case '?':
-			return (1);
-		}
-#ifdef MKSH_NOPWNAM
-	if (t == NULL)
-		return (0);
-#endif
-	wp += builtin_opt.optind;
-
-	for (; *wp != NULL; wp++) {
-		ap = ktsearch(t, *wp, hash(*wp));
-		if (ap == NULL) {
-			/* POSIX */
-			rv = 1;
-			continue;
-		}
-		if (ap->flag&ALLOC) {
-			ap->flag &= ~(ALLOC|ISSET);
-			afree(ap->val.s, APERM);
-		}
-		ap->flag &= ~(DEFINED|ISSET|EXPORT);
-	}
-
-	if (all) {
-		struct tstate ts;
-
-		for (ktwalk(&ts, t); (ap = ktnext(&ts)); ) {
-			if (ap->flag&ALLOC) {
-				ap->flag &= ~(ALLOC|ISSET);
-				afree(ap->val.s, APERM);
-			}
-			ap->flag &= ~(DEFINED|ISSET|EXPORT);
-		}
-	}
-
-	return (rv);
-}
-
-int
-c_let(const char **wp)
-{
-	int rv = 1;
-	mksh_ari_t val;
-
-	if (wp[1] == NULL)
-		/* AT&T ksh does this */
-		bi_errorf("no arguments");
-	else
-		for (wp++; *wp; wp++)
-			if (!evaluate(*wp, &val, KSH_RETURN_ERROR, true)) {
-				/* distinguish error from zero result */
-				rv = 2;
-				break;
-			} else
-				rv = val == 0;
-	return (rv);
-}
-
-int
-c_jobs(const char **wp)
-{
-	int optc, flag = 0, nflag = 0, rv = 0;
-
-	while ((optc = ksh_getopt(wp, &builtin_opt, "lpnz")) != -1)
-		switch (optc) {
-		case 'l':
-			flag = 1;
-			break;
-		case 'p':
-			flag = 2;
-			break;
-		case 'n':
-			nflag = 1;
-			break;
-		case 'z':
-			/* debugging: print zombies */
-			nflag = -1;
-			break;
-		case '?':
-			return (1);
-		}
-	wp += builtin_opt.optind;
-	if (!*wp) {
-		if (j_jobs(NULL, flag, nflag))
-			rv = 1;
-	} else {
-		for (; *wp; wp++)
-			if (j_jobs(*wp, flag, nflag))
-				rv = 1;
-	}
-	return (rv);
-}
-
-#ifndef MKSH_UNEMPLOYED
-int
-c_fgbg(const char **wp)
-{
-	bool bg = strcmp(*wp, "bg") == 0;
-	int rv = 0;
-
-	if (!Flag(FMONITOR)) {
-		bi_errorf("job control not enabled");
-		return (1);
-	}
-	if (ksh_getopt(wp, &builtin_opt, null) == '?')
-		return (1);
-	wp += builtin_opt.optind;
-	if (*wp)
-		for (; *wp; wp++)
-			rv = j_resume(*wp, bg);
-	else
-		rv = j_resume("%%", bg);
-	return (bg ? 0 : rv);
-}
-#endif
-
-/* format a single kill item */
-static char *
-kill_fmt_entry(char *buf, size_t buflen, unsigned int i, const void *arg)
-{
-	const struct kill_info *ki = (const struct kill_info *)arg;
-
-	i++;
-	shf_snprintf(buf, buflen, "%*u %*s %s",
-	    ki->num_width, i,
-	    ki->name_width, sigtraps[i].name,
-	    sigtraps[i].mess);
-	return (buf);
-}
-
-int
-c_kill(const char **wp)
-{
-	Trap *t = NULL;
-	const char *p;
-	bool lflag = false;
-	int i, n, rv, sig;
-
-	/* assume old style options if -digits or -UPPERCASE */
-	if ((p = wp[1]) && *p == '-' && (ksh_isdigit(p[1]) ||
-	    ksh_isupper(p[1]))) {
-		if (!(t = gettrap(p + 1, false))) {
-			bi_errorf("bad signal '%s'", p + 1);
-			return (1);
-		}
-		i = (wp[2] && strcmp(wp[2], "--") == 0) ? 3 : 2;
-	} else {
-		int optc;
-
-		while ((optc = ksh_getopt(wp, &builtin_opt, "ls:")) != -1)
-			switch (optc) {
-			case 'l':
-				lflag = true;
-				break;
-			case 's':
-				if (!(t = gettrap(builtin_opt.optarg, true))) {
-					bi_errorf("bad signal '%s'",
-					    builtin_opt.optarg);
-					return (1);
-				}
-				break;
-			case '?':
-				return (1);
-			}
-		i = builtin_opt.optind;
-	}
-	if ((lflag && t) || (!wp[i] && !lflag)) {
-#ifndef MKSH_SMALL
-		shf_puts("usage:\tkill [-s signame | -signum | -signame]"
-		    " { job | pid | pgrp } ...\n"
-		    "\tkill -l [exit_status ...]\n", shl_out);
-#endif
-		bi_errorfz();
-		return (1);
-	}
-
-	if (lflag) {
-		if (wp[i]) {
-			for (; wp[i]; i++) {
-				if (!bi_getn(wp[i], &n))
-					return (1);
-				if (n > 128 && n < 128 + NSIG)
-					n -= 128;
-				if (n > 0 && n < NSIG)
-					shprintf("%s\n", sigtraps[n].name);
-				else
-					shprintf("%d\n", n);
-			}
-		} else {
-			ssize_t w, mess_cols, mess_octs;
-			int j;
-			struct kill_info ki;
-
-			for (j = NSIG, ki.num_width = 1; j >= 10; j /= 10)
-				ki.num_width++;
-			ki.name_width = mess_cols = mess_octs = 0;
-			for (j = 0; j < NSIG; j++) {
-				w = strlen(sigtraps[j].name);
-				if (w > ki.name_width)
-					ki.name_width = w;
-				w = strlen(sigtraps[j].mess);
-				if (w > mess_octs)
-					mess_octs = w;
-				w = utf_mbswidth(sigtraps[j].mess);
-				if (w > mess_cols)
-					mess_cols = w;
-			}
-
-			print_columns(shl_stdout, (unsigned int)(NSIG - 1),
-			    kill_fmt_entry, (void *)&ki,
-			    ki.num_width + 1 + ki.name_width + 1 + mess_octs,
-			    ki.num_width + 1 + ki.name_width + 1 + mess_cols,
-			    true);
-		}
-		return (0);
-	}
-	rv = 0;
-	sig = t ? t->signal : SIGTERM;
-	for (; (p = wp[i]); i++) {
-		if (*p == '%') {
-			if (j_kill(p, sig))
-				rv = 1;
-		} else if (!getn(p, &n)) {
-			bi_errorf("%s: %s", p,
-			    "arguments must be jobs or process IDs");
-			rv = 1;
-		} else {
-			if (mksh_kill(n, sig) < 0) {
-				bi_errorf("%s: %s", p, cstrerror(errno));
-				rv = 1;
-			}
-		}
-	}
-	return (rv);
-}
-
-void
-getopts_reset(int val)
-{
-	if (val >= 1) {
-		ksh_getopt_reset(&user_opt, GF_NONAME | GF_PLUSOPT);
-		user_opt.optind = user_opt.uoptind = val;
-	}
-}
-
-int
-c_getopts(const char **wp)
-{
-	int argc, optc, rv;
-	const char *opts, *var;
-	char buf[3];
-	struct tbl *vq, *voptarg;
-
-	if (ksh_getopt(wp, &builtin_opt, null) == '?')
-		return (1);
-	wp += builtin_opt.optind;
-
-	opts = *wp++;
-	if (!opts) {
-		bi_errorf("missing %s argument", "options");
-		return (1);
-	}
-
-	var = *wp++;
-	if (!var) {
-		bi_errorf("missing %s argument", "name");
-		return (1);
-	}
-	if (!*var || *skip_varname(var, true)) {
-		bi_errorf("%s: %s", var, "is not an identifier");
-		return (1);
-	}
-
-	if (e->loc->next == NULL) {
-		internal_warningf("%s: %s", "c_getopts", "no argv");
-		return (1);
-	}
-	/* Which arguments are we parsing... */
-	if (*wp == NULL)
-		wp = e->loc->next->argv;
-	else
-		*--wp = e->loc->next->argv[0];
-
-	/* Check that our saved state won't cause a core dump... */
-	for (argc = 0; wp[argc]; argc++)
-		;
-	if (user_opt.optind > argc ||
-	    (user_opt.p != 0 &&
-	    user_opt.p > strlen(wp[user_opt.optind - 1]))) {
-		bi_errorf("arguments changed since last call");
-		return (1);
-	}
-
-	user_opt.optarg = NULL;
-	optc = ksh_getopt(wp, &user_opt, opts);
-
-	if (optc >= 0 && optc != '?' && (user_opt.info & GI_PLUS)) {
-		buf[0] = '+';
-		buf[1] = optc;
-		buf[2] = '\0';
-	} else {
-		/*
-		 * POSIX says var is set to ? at end-of-options, AT&T ksh
-		 * sets it to null - we go with POSIX...
-		 */
-		buf[0] = optc < 0 ? '?' : optc;
-		buf[1] = '\0';
-	}
-
-	/* AT&T ksh93 in fact does change OPTIND for unknown options too */
-	user_opt.uoptind = user_opt.optind;
-
-	voptarg = global("OPTARG");
-	/* AT&T ksh clears ro and int */
-	voptarg->flag &= ~RDONLY;
-	/* Paranoia: ensure no bizarre results. */
-	if (voptarg->flag & INTEGER)
-	    typeset("OPTARG", 0, INTEGER, 0, 0);
-	if (user_opt.optarg == NULL)
-		unset(voptarg, 1);
-	else
-		/* This can't fail (have cleared readonly/integer) */
-		setstr(voptarg, user_opt.optarg, KSH_RETURN_ERROR);
-
-	rv = 0;
-
-	vq = global(var);
-	/* Error message already printed (integer, readonly) */
-	if (!setstr(vq, buf, KSH_RETURN_ERROR))
-		rv = 2;
-	if (Flag(FEXPORT))
-		typeset(var, EXPORT, 0, 0, 0);
-
-	return (optc < 0 ? 1 : rv);
-}
-
-#ifndef MKSH_NO_CMDLINE_EDITING
-int
-c_bind(const char **wp)
-{
-	int optc, rv = 0;
-#ifndef MKSH_SMALL
-	bool macro = false;
-#endif
-	bool list = false;
-	const char *cp;
-	char *up;
-
-	while ((optc = ksh_getopt(wp, &builtin_opt,
-#ifndef MKSH_SMALL
-	    "lm"
-#else
-	    "l"
-#endif
-	    )) != -1)
-		switch (optc) {
-		case 'l':
-			list = true;
-			break;
-#ifndef MKSH_SMALL
-		case 'm':
-			macro = true;
-			break;
-#endif
-		case '?':
-			return (1);
-		}
-	wp += builtin_opt.optind;
-
-	if (*wp == NULL)
-		/* list all */
-		rv = x_bind(NULL, NULL,
-#ifndef MKSH_SMALL
-		    false,
-#endif
-		    list);
-
-	for (; *wp != NULL; wp++) {
-		if ((cp = cstrchr(*wp, '=')) == NULL)
-			up = NULL;
-		else {
-			strdupx(up, *wp, ATEMP);
-			up[cp++ - *wp] = '\0';
-		}
-		if (x_bind(up ? up : *wp, cp,
-#ifndef MKSH_SMALL
-		    macro,
-#endif
-		    false))
-			rv = 1;
-		afree(up, ATEMP);
-	}
-
-	return (rv);
-}
-#endif
-
-int
-c_shift(const char **wp)
-{
-	struct block *l = e->loc;
-	int n;
-	mksh_ari_t val;
-	const char *arg;
-
-	if (ksh_getopt(wp, &builtin_opt, null) == '?')
-		return (1);
-	arg = wp[builtin_opt.optind];
-
-	if (arg) {
-		evaluate(arg, &val, KSH_UNWIND_ERROR, false);
-		n = val;
-	} else
-		n = 1;
-	if (n < 0) {
-		bi_errorf("%s: %s", arg, "bad number");
-		return (1);
-	}
-	if (l->argc < n) {
-		bi_errorf("nothing to shift");
-		return (1);
-	}
-	l->argv[n] = l->argv[0];
-	l->argv += n;
-	l->argc -= n;
-	return (0);
-}
-
-int
-c_umask(const char **wp)
-{
-	int i, optc;
-	const char *cp;
-	bool symbolic = false;
-	mode_t old_umask;
-
-	while ((optc = ksh_getopt(wp, &builtin_opt, "S")) != -1)
-		switch (optc) {
-		case 'S':
-			symbolic = true;
-			break;
-		case '?':
-			return (1);
-		}
-	cp = wp[builtin_opt.optind];
-	if (cp == NULL) {
-		old_umask = umask((mode_t)0);
-		umask(old_umask);
-		if (symbolic) {
-			char buf[18], *p;
-			int j;
-
-			old_umask = ~old_umask;
-			p = buf;
-			for (i = 0; i < 3; i++) {
-				*p++ = "ugo"[i];
-				*p++ = '=';
-				for (j = 0; j < 3; j++)
-					if (old_umask & (1 << (8 - (3*i + j))))
-						*p++ = "rwx"[j];
-				*p++ = ',';
-			}
-			p[-1] = '\0';
-			shprintf("%s\n", buf);
-		} else
-			shprintf("%#3.3o\n", (unsigned int)old_umask);
-	} else {
-		mode_t new_umask;
-
-		if (ksh_isdigit(*cp)) {
-			for (new_umask = 0; *cp >= '0' && *cp <= '7'; cp++)
-				new_umask = new_umask * 8 + (*cp - '0');
-			if (*cp) {
-				bi_errorf("bad number");
-				return (1);
-			}
-		} else {
-			/* symbolic format */
-			int positions, new_val;
-			char op;
-
-			old_umask = umask((mode_t)0);
-			/* in case of error */
-			umask(old_umask);
-			old_umask = ~old_umask;
-			new_umask = old_umask;
-			positions = 0;
-			while (*cp) {
-				while (*cp && vstrchr("augo", *cp))
-					switch (*cp++) {
-					case 'a':
-						positions |= 0111;
-						break;
-					case 'u':
-						positions |= 0100;
-						break;
-					case 'g':
-						positions |= 0010;
-						break;
-					case 'o':
-						positions |= 0001;
-						break;
-					}
-				if (!positions)
-					/* default is a */
-					positions = 0111;
-				if (!vstrchr("=+-", op = *cp))
-					break;
-				cp++;
-				new_val = 0;
-				while (*cp && vstrchr("rwxugoXs", *cp))
-					switch (*cp++) {
-					case 'r': new_val |= 04; break;
-					case 'w': new_val |= 02; break;
-					case 'x': new_val |= 01; break;
-					case 'u':
-						new_val |= old_umask >> 6;
-						break;
-					case 'g':
-						new_val |= old_umask >> 3;
-						break;
-					case 'o':
-						new_val |= old_umask >> 0;
-						break;
-					case 'X':
-						if (old_umask & 0111)
-							new_val |= 01;
-						break;
-					case 's':
-						/* ignored */
-						break;
-					}
-				new_val = (new_val & 07) * positions;
-				switch (op) {
-				case '-':
-					new_umask &= ~new_val;
-					break;
-				case '=':
-					new_umask = new_val |
-					    (new_umask & ~(positions * 07));
-					break;
-				case '+':
-					new_umask |= new_val;
-				}
-				if (*cp == ',') {
-					positions = 0;
-					cp++;
-				} else if (!vstrchr("=+-", *cp))
-					break;
-			}
-			if (*cp) {
-				bi_errorf("bad mask");
-				return (1);
-			}
-			new_umask = ~new_umask;
-		}
-		umask(new_umask);
-	}
-	return (0);
-}
-
-int
-c_dot(const char **wp)
-{
-	const char *file, *cp, **argv;
-	int argc, i, errcode;
-
-	if (ksh_getopt(wp, &builtin_opt, null) == '?')
-		return (1);
-
-	if ((cp = wp[builtin_opt.optind]) == NULL) {
-		bi_errorf("missing argument");
-		return (1);
-	}
-	if ((file = search_path(cp, path, R_OK, &errcode)) == NULL) {
-		bi_errorf("%s: %s", cp, cstrerror(errcode));
-		return (1);
-	}
-
-	/* Set positional parameters? */
-	if (wp[builtin_opt.optind + 1]) {
-		argv = wp + builtin_opt.optind;
-		/* preserve $0 */
-		argv[0] = e->loc->argv[0];
-		for (argc = 0; argv[argc + 1]; argc++)
-			;
-	} else {
-		argc = 0;
-		argv = NULL;
-	}
-	if ((i = include(file, argc, argv, false)) < 0) {
-		/* should not happen */
-		bi_errorf("%s: %s", cp, cstrerror(errno));
-		return (1);
-	}
-	return (i);
-}
-
-int
-c_wait(const char **wp)
-{
-	int rv = 0, sig;
-
-	if (ksh_getopt(wp, &builtin_opt, null) == '?')
-		return (1);
-	wp += builtin_opt.optind;
-	if (*wp == NULL) {
-		while (waitfor(NULL, &sig) >= 0)
-			;
-		rv = sig;
-	} else {
-		for (; *wp; wp++)
-			rv = waitfor(*wp, &sig);
-		if (rv < 0)
-			/* magic exit code: bad job-id */
-			rv = sig ? sig : 127;
-	}
-	return (rv);
-}
-
-static char REPLY[] = "REPLY";
-int
-c_read(const char **wp)
-{
-#define is_ifsws(c) (ctype((c), C_IFS) && ctype((c), C_IFSWS))
-	int c, fd = 0, rv = 0, lastparm = 0;
-	bool savehist = false, intoarray = false, aschars = false;
-	bool rawmode = false, expanding = false;
-	enum { LINES, BYTES, UPTO, READALL } readmode = LINES;
-	char delim = '\n';
-	size_t bytesleft = 128, bytesread;
-	struct tbl *vp /* FU gcc */ = NULL, *vq;
-	char *cp, *allocd = NULL, *xp;
-	const char *ccp;
-	XString xs;
-	ptrdiff_t xsave = 0;
-	mksh_ttyst tios;
-	bool restore_tios = false;
-#if HAVE_SELECT
-	bool hastimeout = false;
-	struct timeval tv, tvlim;
-#define c_read_opts "Aad:N:n:prst:u,"
-#else
-#define c_read_opts "Aad:N:n:prsu,"
-#endif
-
-	while ((c = ksh_getopt(wp, &builtin_opt, c_read_opts)) != -1)
-	switch (c) {
-	case 'a':
-		aschars = true;
-		/* FALLTHROUGH */
-	case 'A':
-		intoarray = true;
-		break;
-	case 'd':
-		delim = builtin_opt.optarg[0];
-		break;
-	case 'N':
-	case 'n':
-		readmode = c == 'N' ? BYTES : UPTO;
-		if (!bi_getn(builtin_opt.optarg, &c))
-			return (2);
-		if (c == -1) {
-			readmode = READALL;
-			bytesleft = 1024;
-		} else
-			bytesleft = (unsigned int)c;
-		break;
-	case 'p':
-		if ((fd = coproc_getfd(R_OK, &ccp)) < 0) {
-			bi_errorf("%s: %s", "-p", ccp);
-			return (2);
-		}
-		break;
-	case 'r':
-		rawmode = true;
-		break;
-	case 's':
-		savehist = true;
-		break;
-#if HAVE_SELECT
-	case 't':
-		if (parse_usec(builtin_opt.optarg, &tv)) {
-			bi_errorf("%s: %s '%s'", Tsynerr, cstrerror(errno),
-			    builtin_opt.optarg);
-			return (2);
-		}
-		hastimeout = true;
-		break;
-#endif
-	case 'u':
-		if (!builtin_opt.optarg[0])
-			fd = 0;
-		else if ((fd = check_fd(builtin_opt.optarg, R_OK, &ccp)) < 0) {
-			bi_errorf("%s: %s: %s", "-u", builtin_opt.optarg, ccp);
-			return (2);
-		}
-		break;
-	case '?':
-		return (2);
-	}
-	wp += builtin_opt.optind;
-	if (*wp == NULL)
-		*--wp = REPLY;
-
-	if (intoarray && wp[1] != NULL) {
-		bi_errorf("too many arguments");
-		return (2);
-	}
-
-	if ((ccp = cstrchr(*wp, '?')) != NULL) {
-		strdupx(allocd, *wp, ATEMP);
-		allocd[ccp - *wp] = '\0';
-		*wp = allocd;
-		if (isatty(fd)) {
-			/*
-			 * AT&T ksh says it prints prompt on fd if it's open
-			 * for writing and is a tty, but it doesn't do it
-			 * (it also doesn't check the interactive flag,
-			 * as is indicated in the Korn Shell book).
-			 */
-			shf_puts(ccp + 1, shl_out);
-			shf_flush(shl_out);
-		}
-	}
-
-	Xinit(xs, xp, bytesleft, ATEMP);
-
-	if (readmode == LINES)
-		bytesleft = 1;
-	else if (isatty(fd)) {
-		x_mkraw(fd, &tios, true);
-		restore_tios = true;
-	}
-
-#if HAVE_SELECT
-	if (hastimeout) {
-		mksh_TIME(tvlim);
-		timeradd(&tvlim, &tv, &tvlim);
-	}
-#endif
-
- c_read_readloop:
-#if HAVE_SELECT
-	if (hastimeout) {
-		fd_set fdset;
-
-		FD_ZERO(&fdset);
-		FD_SET((unsigned int)fd, &fdset);
-		mksh_TIME(tv);
-		timersub(&tvlim, &tv, &tv);
-		if (tv.tv_sec < 0) {
-			/* timeout expired globally */
-			rv = 1;
-			goto c_read_out;
-		}
-
-		switch (select(fd + 1, &fdset, NULL, NULL, &tv)) {
-		case 1:
-			break;
-		case 0:
-			/* timeout expired for this call */
-			rv = 1;
-			goto c_read_out;
-		default:
-			bi_errorf("%s: %s", Tselect, cstrerror(errno));
-			rv = 2;
-			goto c_read_out;
-		}
-	}
-#endif
-
-	bytesread = blocking_read(fd, xp, bytesleft);
-	if (bytesread == (size_t)-1) {
-		/* interrupted */
-		if (errno == EINTR && fatal_trap_check()) {
-			/*
-			 * Was the offending signal one that would
-			 * normally kill a process? If so, pretend
-			 * the read was killed.
-			 */
-			rv = 2;
-			goto c_read_out;
-		}
-		/* just ignore the signal */
-		goto c_read_readloop;
-	}
-
-	switch (readmode) {
-	case READALL:
-		if (bytesread == 0) {
-			/* end of file reached */
-			rv = 1;
-			goto c_read_readdone;
-		}
-		xp += bytesread;
-		XcheckN(xs, xp, bytesleft);
-		break;
-
-	case UPTO:
-		if (bytesread == 0)
-			/* end of file reached */
-			rv = 1;
-		xp += bytesread;
-		goto c_read_readdone;
-
-	case BYTES:
-		if (bytesread == 0) {
-			/* end of file reached */
-			rv = 1;
-			xp = Xstring(xs, xp);
-			goto c_read_readdone;
-		}
-		xp += bytesread;
-		if ((bytesleft -= bytesread) == 0)
-			goto c_read_readdone;
-		break;
-	case LINES:
-		if (bytesread == 0) {
-			/* end of file reached */
-			rv = 1;
-			goto c_read_readdone;
-		}
-		if ((c = *xp) == '\0' && !aschars && delim != '\0') {
-			/* skip any read NULs unless delimiter */
-			break;
-		}
-		if (expanding) {
-			expanding = false;
-			if (c == delim) {
-				if (Flag(FTALKING_I) && isatty(fd)) {
-					/*
-					 * set prompt in case this is
-					 * called from .profile or $ENV
-					 */
-					set_prompt(PS2, NULL);
-					pprompt(prompt, 0);
-				}
-				/* drop the backslash */
-				--xp;
-				/* and the delimiter */
-				break;
-			}
-		} else if (c == delim) {
-			goto c_read_readdone;
-		} else if (!rawmode && c == '\\') {
-			expanding = true;
-		}
-		Xcheck(xs, xp);
-		++xp;
-		break;
-	}
-	goto c_read_readloop;
-
- c_read_readdone:
-	bytesread = Xlength(xs, xp);
-	Xput(xs, xp, '\0');
-
-	/*-
-	 * state: we finished reading the input and NUL terminated it
-	 * Xstring(xs, xp) -> xp-1 = input string without trailing delim
-	 * rv = 1 if EOF, 0 otherwise (errors handled already)
-	 */
-
-	if (rv == 1) {
-		/* clean up coprocess if needed, on EOF */
-		coproc_read_close(fd);
-		if (readmode == READALL)
-			/* EOF is no error here */
-			rv = 0;
-	}
-
-	if (savehist)
-		histsave(&source->line, Xstring(xs, xp), true, false);
-
-	ccp = cp = Xclose(xs, xp);
-	expanding = false;
-	XinitN(xs, 128, ATEMP);
-	if (intoarray) {
-		vp = global(*wp);
-		if (vp->flag & RDONLY) {
- c_read_splitro:
-			bi_errorf("read-only: %s", *wp);
- c_read_spliterr:
-			rv = 2;
-			afree(cp, ATEMP);
-			goto c_read_out;
-		}
-		/* exporting an array is currently pointless */
-		unset(vp, 1);
-		/* counter for array index */
-		c = 0;
-	}
-	if (!aschars) {
-		/* skip initial IFS whitespace */
-		while (bytesread && is_ifsws(*ccp)) {
-			++ccp;
-			--bytesread;
-		}
-		/* trim trailing IFS whitespace */
-		while (bytesread && is_ifsws(ccp[bytesread - 1])) {
-			--bytesread;
-		}
-	}
- c_read_splitloop:
-	xp = Xstring(xs, xp);
-	/* generate next word */
-	if (!bytesread) {
-		/* no more input */
-		if (intoarray)
-			goto c_read_splitdone;
-		/* zero out next parameters */
-		goto c_read_gotword;
-	}
-	if (aschars) {
-		Xput(xs, xp, '1');
-		Xput(xs, xp, '#');
-		bytesleft = utf_ptradj(ccp);
-		while (bytesleft && bytesread) {
-			*xp++ = *ccp++;
-			--bytesleft;
-			--bytesread;
-		}
-		if (xp[-1] == '\0') {
-			xp[-1] = '0';
-			xp[-3] = '2';
-		}
-		goto c_read_gotword;
-	}
-
-	if (!intoarray && wp[1] == NULL)
-		lastparm = 1;
-
- c_read_splitlast:
-	/* copy until IFS character */
-	while (bytesread) {
-		char ch;
-
-		ch = *ccp;
-		if (expanding) {
-			expanding = false;
-			goto c_read_splitcopy;
-		} else if (ctype(ch, C_IFS)) {
-			break;
-		} else if (!rawmode && ch == '\\') {
-			expanding = true;
-		} else {
- c_read_splitcopy:
-			Xcheck(xs, xp);
-			Xput(xs, xp, ch);
-		}
-		++ccp;
-		--bytesread;
-	}
-	xsave = Xsavepos(xs, xp);
-	/* copy word delimiter: IFSWS+IFS,IFSWS */
-	while (bytesread) {
-		char ch;
-
-		ch = *ccp;
-		if (!ctype(ch, C_IFS))
-			break;
-		Xcheck(xs, xp);
-		Xput(xs, xp, ch);
-		++ccp;
-		--bytesread;
-		if (!ctype(ch, C_IFSWS))
-			break;
-	}
-	while (bytesread && is_ifsws(*ccp)) {
-		Xcheck(xs, xp);
-		Xput(xs, xp, *ccp);
-		++ccp;
-		--bytesread;
-	}
-	/* if no more parameters, rinse and repeat */
-	if (lastparm && bytesread) {
-		++lastparm;
-		goto c_read_splitlast;
-	}
-	/* get rid of the delimiter unless we pack the rest */
-	if (lastparm < 2)
-		xp = Xrestpos(xs, xp, xsave);
- c_read_gotword:
-	Xput(xs, xp, '\0');
-	if (intoarray) {
-		vq = arraysearch(vp, c++);
-	} else {
-		vq = global(*wp);
-		/* must be checked before exporting */
-		if (vq->flag & RDONLY)
-			goto c_read_splitro;
-		if (Flag(FEXPORT))
-			typeset(*wp, EXPORT, 0, 0, 0);
-	}
-	if (!setstr(vq, Xstring(xs, xp), KSH_RETURN_ERROR))
-		goto c_read_spliterr;
-	if (aschars) {
-		setint_v(vq, vq, false);
-		/* protect from UTFMODE changes */
-		vq->type = 0;
-	}
-	if (intoarray || *++wp != NULL)
-		goto c_read_splitloop;
-
- c_read_splitdone:
-	/* free up */
-	afree(cp, ATEMP);
-
- c_read_out:
-	afree(allocd, ATEMP);
-	Xfree(xs, xp);
-	if (restore_tios)
-		mksh_tcset(fd, &tios);
-	return (rv);
-#undef is_ifsws
-}
-
-int
-c_eval(const char **wp)
-{
-	struct source *s, *saves = source;
-	unsigned char savef;
-	int rv;
-
-	if (ksh_getopt(wp, &builtin_opt, null) == '?')
-		return (1);
-	s = pushs(SWORDS, ATEMP);
-	s->u.strv = wp + builtin_opt.optind;
-
-	/*-
-	 * The following code handles the case where the command is
-	 * empty due to failed command substitution, for example by
-	 *	eval "$(false)"
-	 * This has historically returned 1 by AT&T ksh88. In this
-	 * case, shell() will not set or change exstat because the
-	 * compiled tree is empty, so it will use the value we pass
-	 * from subst_exstat, which is cleared in execute(), so it
-	 * should have been 0 if there were no substitutions.
-	 *
-	 * POSIX however says we don't do this, even though it is
-	 * traditionally done. AT&T ksh93 agrees with POSIX, so we
-	 * do. The following is an excerpt from SUSv4 [1003.2-2008]:
-	 *
-	 * 2.9.1: Simple Commands
-	 *	... If there is a command name, execution shall
-	 *	continue as described in 2.9.1.1 [Command Search
-	 *	and Execution]. If there is no command name, but
-	 *	the command contained a command substitution, the
-	 *	command shall complete with the exit status of the
-	 *	last command substitution performed.
-	 * 2.9.1.1: Command Search and Execution
-	 *	(1) a. If the command name matches the name of a
-	 *	special built-in utility, that special built-in
-	 *	utility shall be invoked.
-	 * 2.14.5: eval
-	 *	If there are no arguments, or only null arguments,
-	 *	eval shall return a zero exit status; ...
-	 */
-	/* AT&T ksh88: use subst_exstat */
-	/* exstat = subst_exstat; */
-	/* SUSv4: OR with a high value never written otherwise */
-	exstat |= 0x4000;
-
-	savef = Flag(FERREXIT);
-	Flag(FERREXIT) |= 0x80;
-	rv = shell(s, false);
-	Flag(FERREXIT) = savef;
-	source = saves;
-	afree(s, ATEMP);
-	if (exstat & 0x4000)
-		/* detect old exstat, use 0 in that case */
-		rv = 0;
-	return (rv);
-}
-
-int
-c_trap(const char **wp)
-{
-	int i;
-	const char *s;
-	Trap *p;
-
-	if (ksh_getopt(wp, &builtin_opt, null) == '?')
-		return (1);
-	wp += builtin_opt.optind;
-
-	if (*wp == NULL) {
-		for (p = sigtraps, i = NSIG + 1; --i >= 0; p++)
-			if (p->trap != NULL) {
-				shf_puts("trap -- ", shl_stdout);
-				print_value_quoted(shl_stdout, p->trap);
-				shprintf(" %s\n", p->name);
-			}
-		return (0);
-	}
-
-	/*
-	 * Use case sensitive lookup for first arg so the
-	 * command 'exit' isn't confused with the pseudo-signal
-	 * 'EXIT'.
-	 */
-	/* get command */
-	s = (gettrap(*wp, false) == NULL) ? *wp++ : NULL;
-	if (s != NULL && s[0] == '-' && s[1] == '\0')
-		s = NULL;
-
-	/* set/clear traps */
-	i = 0;
-	while (*wp != NULL)
-		if ((p = gettrap(*wp++, true)) == NULL) {
-			warningf(true, "%s: %s '%s'", builtin_argv0,
-			    "bad signal", wp[-1]);
-			++i;
-		} else
-			settrap(p, s);
-	return (i);
-}
-
-int
-c_exitreturn(const char **wp)
-{
-	int n, how = LEXIT;
-	const char *arg;
-
-	if (ksh_getopt(wp, &builtin_opt, null) == '?')
-		goto c_exitreturn_err;
-	arg = wp[builtin_opt.optind];
-
-	if (arg) {
-		if (!getn(arg, &n)) {
-			exstat = 1;
-			warningf(true, "%s: %s", arg, "bad number");
-		} else
-			exstat = n & 0xFF;
-	} else if (trap_exstat != -1)
-		exstat = trap_exstat;
-	if (wp[0][0] == 'r') {
-		/* return */
-		struct env *ep;
-
-		/*
-		 * need to tell if this is exit or return so trap exit will
-		 * work right (POSIX)
-		 */
-		for (ep = e; ep; ep = ep->oenv)
-			if (STOP_RETURN(ep->type)) {
-				how = LRETURN;
-				break;
-			}
-	}
-
-	if (how == LEXIT && !really_exit && j_stopped_running()) {
-		really_exit = true;
-		how = LSHELL;
-	}
-
-	/* get rid of any i/o redirections */
-	quitenv(NULL);
-	unwind(how);
-	/* NOTREACHED */
-
- c_exitreturn_err:
-	return (1);
-}
-
-int
-c_brkcont(const char **wp)
-{
-	unsigned int quit;
-	int n;
-	struct env *ep, *last_ep = NULL;
-	const char *arg;
-
-	if (ksh_getopt(wp, &builtin_opt, null) == '?')
-		goto c_brkcont_err;
-	arg = wp[builtin_opt.optind];
-
-	if (!arg)
-		n = 1;
-	else if (!bi_getn(arg, &n))
-		goto c_brkcont_err;
-	if (n <= 0) {
-		/* AT&T ksh does this for non-interactive shells only - weird */
-		bi_errorf("%s: %s", arg, "bad value");
-		goto c_brkcont_err;
-	}
-	quit = (unsigned int)n;
-
-	/* Stop at E_NONE, E_PARSE, E_FUNC, or E_INCL */
-	for (ep = e; ep && !STOP_BRKCONT(ep->type); ep = ep->oenv)
-		if (ep->type == E_LOOP) {
-			if (--quit == 0)
-				break;
-			ep->flags |= EF_BRKCONT_PASS;
-			last_ep = ep;
-		}
-
-	if (quit) {
-		/*
-		 * AT&T ksh doesn't print a message - just does what it
-		 * can. We print a message 'cause it helps in debugging
-		 * scripts, but don't generate an error (ie, keep going).
-		 */
-		if ((unsigned int)n == quit) {
-			warningf(true, "%s: %s %s", wp[0], "can't", wp[0]);
-			return (0);
-		}
-		/*
-		 * POSIX says if n is too big, the last enclosing loop
-		 * shall be used. Doesn't say to print an error but we
-		 * do anyway 'cause the user messed up.
-		 */
-		if (last_ep)
-			last_ep->flags &= ~EF_BRKCONT_PASS;
-		warningf(true, "%s: can only %s %u level(s)",
-		    wp[0], wp[0], (unsigned int)n - quit);
-	}
-
-	unwind(*wp[0] == 'b' ? LBREAK : LCONTIN);
-	/* NOTREACHED */
-
- c_brkcont_err:
-	return (1);
-}
-
-int
-c_set(const char **wp)
-{
-	int argi;
-	bool setargs;
-	struct block *l = e->loc;
-	const char **owp;
-
-	if (wp[1] == NULL) {
-		static const char *args[] = { Tset, "-", NULL };
-		return (c_typeset(args));
-	}
-
-	argi = parse_args(wp, OF_SET, &setargs);
-	if (argi < 0)
-		return (1);
-	/* set $# and $* */
-	if (setargs) {
-		wp += argi - 1;
-		owp = wp;
-		/* save $0 */
-		wp[0] = l->argv[0];
-		while (*++wp != NULL)
-			strdupx(*wp, *wp, &l->area);
-		l->argc = wp - owp - 1;
-		l->argv = alloc2(l->argc + 2, sizeof(char *), &l->area);
-		for (wp = l->argv; (*wp++ = *owp++) != NULL; )
-			;
-	}
-	/*-
-	 * POSIX says set exit status is 0, but old scripts that use
-	 * getopt(1) use the construct
-	 *	set -- $(getopt ab:c "$@")
-	 * which assumes the exit value set will be that of the $()
-	 * (subst_exstat is cleared in execute() so that it will be 0
-	 * if there are no command substitutions).
-	 */
-#ifdef MKSH_LEGACY_MODE
-	/* traditional behaviour, unless set -o posix */
-	return (Flag(FPOSIX) ? 0 : subst_exstat);
-#else
-	/* conformant behaviour, unless set -o sh +o posix */
-	return (Flag(FSH) && !Flag(FPOSIX) ? subst_exstat : 0);
-#endif
-}
-
-int
-c_unset(const char **wp)
-{
-	const char *id;
-	int optc, rv = 0;
-	bool unset_var = true;
-
-	while ((optc = ksh_getopt(wp, &builtin_opt, "fv")) != -1)
-		switch (optc) {
-		case 'f':
-			unset_var = false;
-			break;
-		case 'v':
-			unset_var = true;
-			break;
-		case '?':
-			/*XXX not reached due to GF_ERROR */
-			return (2);
-		}
-	wp += builtin_opt.optind;
-	for (; (id = *wp) != NULL; wp++)
-		if (unset_var) {
-			/* unset variable */
-			struct tbl *vp;
-			char *cp = NULL;
-			size_t n;
-
-			n = strlen(id);
-			if (n > 3 && id[n-3] == '[' && id[n-2] == '*' &&
-			    id[n-1] == ']') {
-				strndupx(cp, id, n - 3, ATEMP);
-				id = cp;
-				optc = 3;
-			} else
-				optc = vstrchr(id, '[') ? 0 : 1;
-
-			vp = global(id);
-			afree(cp, ATEMP);
-
-			if ((vp->flag&RDONLY)) {
-				warningf(true, "read-only: %s", vp->name);
-				rv = 1;
-			} else
-				unset(vp, optc);
-		} else
-			/* unset function */
-			define(id, NULL);
-	return (rv);
-}
-
-static void
-p_time(struct shf *shf, bool posix, long tv_sec, int tv_usec, int width,
-    const char *prefix, const char *suffix)
-{
-	tv_usec /= 10000;
-	if (posix)
-		shf_fprintf(shf, "%s%*ld.%02d%s", prefix, width,
-		    tv_sec, tv_usec, suffix);
-	else
-		shf_fprintf(shf, "%s%*ldm%d.%02ds%s", prefix, width,
-		    tv_sec / 60, (int)(tv_sec % 60), tv_usec, suffix);
-}
-
-int
-c_times(const char **wp MKSH_A_UNUSED)
-{
-	struct rusage usage;
-
-	getrusage(RUSAGE_SELF, &usage);
-	p_time(shl_stdout, false, usage.ru_utime.tv_sec,
-	    usage.ru_utime.tv_usec, 0, null, " ");
-	p_time(shl_stdout, false, usage.ru_stime.tv_sec,
-	    usage.ru_stime.tv_usec, 0, null, "\n");
-
-	getrusage(RUSAGE_CHILDREN, &usage);
-	p_time(shl_stdout, false, usage.ru_utime.tv_sec,
-	    usage.ru_utime.tv_usec, 0, null, " ");
-	p_time(shl_stdout, false, usage.ru_stime.tv_sec,
-	    usage.ru_stime.tv_usec, 0, null, "\n");
-
-	return (0);
-}
-
-/*
- * time pipeline (really a statement, not a built-in command)
- */
-int
-timex(struct op *t, int f, volatile int *xerrok)
-{
-#define TF_NOARGS	BIT(0)
-#define TF_NOREAL	BIT(1)		/* don't report real time */
-#define TF_POSIX	BIT(2)		/* report in POSIX format */
-	int rv = 0, tf = 0;
-	struct rusage ru0, ru1, cru0, cru1;
-	struct timeval usrtime, systime, tv0, tv1;
-
-	mksh_TIME(tv0);
-	getrusage(RUSAGE_SELF, &ru0);
-	getrusage(RUSAGE_CHILDREN, &cru0);
-	if (t->left) {
-		/*
-		 * Two ways of getting cpu usage of a command: just use t0
-		 * and t1 (which will get cpu usage from other jobs that
-		 * finish while we are executing t->left), or get the
-		 * cpu usage of t->left. AT&T ksh does the former, while
-		 * pdksh tries to do the later (the j_usrtime hack doesn't
-		 * really work as it only counts the last job).
-		 */
-		timerclear(&j_usrtime);
-		timerclear(&j_systime);
-		rv = execute(t->left, f | XTIME, xerrok);
-		if (t->left->type == TCOM)
-			tf |= t->left->str[0];
-		mksh_TIME(tv1);
-		getrusage(RUSAGE_SELF, &ru1);
-		getrusage(RUSAGE_CHILDREN, &cru1);
-	} else
-		tf = TF_NOARGS;
-
-	if (tf & TF_NOARGS) {
-		/* ksh93 - report shell times (shell+kids) */
-		tf |= TF_NOREAL;
-		timeradd(&ru0.ru_utime, &cru0.ru_utime, &usrtime);
-		timeradd(&ru0.ru_stime, &cru0.ru_stime, &systime);
-	} else {
-		timersub(&ru1.ru_utime, &ru0.ru_utime, &usrtime);
-		timeradd(&usrtime, &j_usrtime, &usrtime);
-		timersub(&ru1.ru_stime, &ru0.ru_stime, &systime);
-		timeradd(&systime, &j_systime, &systime);
-	}
-
-	if (!(tf & TF_NOREAL)) {
-		timersub(&tv1, &tv0, &tv1);
-		if (tf & TF_POSIX)
-			p_time(shl_out, true, tv1.tv_sec, tv1.tv_usec,
-			    5, "real ", "\n");
-		else
-			p_time(shl_out, false, tv1.tv_sec, tv1.tv_usec,
-			    5, null, " real ");
-	}
-	if (tf & TF_POSIX)
-		p_time(shl_out, true, usrtime.tv_sec, usrtime.tv_usec,
-		    5, "user ", "\n");
-	else
-		p_time(shl_out, false, usrtime.tv_sec, usrtime.tv_usec,
-		    5, null, " user ");
-	if (tf & TF_POSIX)
-		p_time(shl_out, true, systime.tv_sec, systime.tv_usec,
-		    5, "sys  ", "\n");
-	else
-		p_time(shl_out, false, systime.tv_sec, systime.tv_usec,
-		    5, null, " system\n");
-	shf_flush(shl_out);
-
-	return (rv);
-}
-
-void
-timex_hook(struct op *t, char **volatile *app)
-{
-	char **wp = *app;
-	int optc, i, j;
-	Getopt opt;
-
-	ksh_getopt_reset(&opt, 0);
-	/* start at the start */
-	opt.optind = 0;
-	while ((optc = ksh_getopt((const char **)wp, &opt, ":p")) != -1)
-		switch (optc) {
-		case 'p':
-			t->str[0] |= TF_POSIX;
-			break;
-		case '?':
-			errorf("time: -%s %s", opt.optarg,
-			    "unknown option");
-		case ':':
-			errorf("time: -%s %s", opt.optarg,
-			    "requires an argument");
-		}
-	/* Copy command words down over options. */
-	if (opt.optind != 0) {
-		for (i = 0; i < opt.optind; i++)
-			afree(wp[i], ATEMP);
-		for (i = 0, j = opt.optind; (wp[i] = wp[j]); i++, j++)
-			;
-	}
-	if (!wp[0])
-		t->str[0] |= TF_NOARGS;
-	*app = wp;
-}
-
-/* exec with no args - args case is taken care of in comexec() */
-int
-c_exec(const char **wp MKSH_A_UNUSED)
-{
-	int i;
-
-	/* make sure redirects stay in place */
-	if (e->savefd != NULL) {
-		for (i = 0; i < NUFILE; i++) {
-			if (e->savefd[i] > 0)
-				close(e->savefd[i]);
-#ifndef MKSH_LEGACY_MODE
-			/*
-			 * keep all file descriptors > 2 private for ksh,
-			 * but not for POSIX or legacy/kludge sh
-			 */
-			if (!Flag(FPOSIX) && !Flag(FSH) && i > 2 &&
-			    e->savefd[i])
-				fcntl(i, F_SETFD, FD_CLOEXEC);
-#endif
-		}
-		e->savefd = NULL;
-	}
-	return (0);
-}
-
-#if HAVE_MKNOD
-int
-c_mknod(const char **wp)
-{
-	int argc, optc, rv = 0;
-	bool ismkfifo = false;
-	const char **argv;
-	void *set = NULL;
-	mode_t mode = 0, oldmode = 0;
-
-	while ((optc = ksh_getopt(wp, &builtin_opt, "m:")) != -1) {
-		switch (optc) {
-		case 'm':
-			set = setmode(builtin_opt.optarg);
-			if (set == NULL) {
-				bi_errorf("invalid file mode");
-				return (1);
-			}
-			mode = getmode(set, (mode_t)(DEFFILEMODE));
-			free_ossetmode(set);
-			break;
-		default:
-			goto c_mknod_usage;
-		}
-	}
-	argv = &wp[builtin_opt.optind];
-	if (argv[0] == NULL)
-		goto c_mknod_usage;
-	for (argc = 0; argv[argc]; argc++)
-		;
-	if (argc == 2 && argv[1][0] == 'p')
-		ismkfifo = true;
-	else if (argc != 4 || (argv[1][0] != 'b' && argv[1][0] != 'c'))
-		goto c_mknod_usage;
-
-	if (set != NULL)
-		oldmode = umask((mode_t)0);
-	else
-		mode = DEFFILEMODE;
-
-	mode |= (argv[1][0] == 'b') ? S_IFBLK :
-	    (argv[1][0] == 'c') ? S_IFCHR : 0;
-
-	if (!ismkfifo) {
-		unsigned long majnum, minnum;
-		dev_t dv;
-		char *c;
-
-		majnum = strtoul(argv[2], &c, 0);
-		if ((c == argv[2]) || (*c != '\0')) {
-			bi_errorf("non-numeric %s %s '%s'", "device", "major", argv[2]);
-			goto c_mknod_err;
-		}
-		minnum = strtoul(argv[3], &c, 0);
-		if ((c == argv[3]) || (*c != '\0')) {
-			bi_errorf("non-numeric %s %s '%s'", "device", "minor", argv[3]);
-			goto c_mknod_err;
-		}
-		dv = makedev(majnum, minnum);
-		if ((unsigned long)(major(dv)) != majnum) {
-			bi_errorf("%s %s too large: %lu", "device", "major", majnum);
-			goto c_mknod_err;
-		}
-		if ((unsigned long)(minor(dv)) != minnum) {
-			bi_errorf("%s %s too large: %lu", "device", "minor", minnum);
-			goto c_mknod_err;
-		}
-		if (mknod(argv[0], mode, dv))
-			goto c_mknod_failed;
-	} else if (mkfifo(argv[0], mode)) {
- c_mknod_failed:
-		bi_errorf("%s: %s", argv[0], cstrerror(errno));
- c_mknod_err:
-		rv = 1;
-	}
-
-	if (set)
-		umask(oldmode);
-	return (rv);
- c_mknod_usage:
-	bi_errorf("%s: %s", "usage", "mknod [-m mode] name b|c major minor");
-	bi_errorf("%s: %s", "usage", "mknod [-m mode] name p");
-	return (1);
-}
-#endif
-
-/*-
-   test(1) roughly accepts the following grammar:
-	oexpr	::= aexpr | aexpr "-o" oexpr ;
-	aexpr	::= nexpr | nexpr "-a" aexpr ;
-	nexpr	::= primary | "!" nexpr ;
-	primary	::= unary-operator operand
-		| operand binary-operator operand
-		| operand
-		| "(" oexpr ")"
-		;
-
-	unary-operator ::= "-a"|"-r"|"-w"|"-x"|"-e"|"-f"|"-d"|"-c"|"-b"|"-p"|
-			   "-u"|"-g"|"-k"|"-s"|"-t"|"-z"|"-n"|"-o"|"-O"|"-G"|
-			   "-L"|"-h"|"-S"|"-H";
-
-	binary-operator ::= "="|"=="|"!="|"-eq"|"-ne"|"-ge"|"-gt"|"-le"|"-lt"|
-			    "-nt"|"-ot"|"-ef"|
-			    "<"|">"	# rules used for [[ ... ]] expressions
-			    ;
-	operand ::= <anything>
-*/
-
-/* POSIX says > 1 for errors */
-#define T_ERR_EXIT 2
-
-int
-c_test(const char **wp)
-{
-	int argc, rv, invert = 0;
-	Test_env te;
-	Test_op op;
-	const char *lhs, **swp;
-
-	te.flags = 0;
-	te.isa = ptest_isa;
-	te.getopnd = ptest_getopnd;
-	te.eval = test_eval;
-	te.error = ptest_error;
-
-	for (argc = 0; wp[argc]; argc++)
-		;
-	mkssert(argc > 0);
-	mkssert(wp[0] != NULL);
-
-	if (strcmp(wp[0], "[") == 0) {
-		if (strcmp(wp[--argc], "]") != 0) {
-			bi_errorf("missing ]");
-			return (T_ERR_EXIT);
-		}
-	}
-
-	te.pos.wp = wp + 1;
-	te.wp_end = wp + argc;
-
-	/*
-	 * Attempt to conform to POSIX special cases. This is pretty
-	 * dumb code straight-forward from the 2008 spec, but unless
-	 * the old pdksh code doesn't live from so many assumptions.
-	 * It does, though, inline some calls to '(*te.funcname)()'.
-	 */
-	switch (argc - 1) {
-	case 0:
-		return (1);
-	case 1:
- ptest_one:
-		op = TO_STNZE;
-		goto ptest_unary;
-	case 2:
- ptest_two:
-		if (ptest_isa(&te, TM_NOT)) {
-			++invert;
-			goto ptest_one;
-		}
-		if ((op = ptest_isa(&te, TM_UNOP))) {
- ptest_unary:
-			rv = test_eval(&te, op, *te.pos.wp++, NULL, true);
- ptest_out:
-			return ((invert & 1) ? rv : !rv);
-		}
-		/* let the parser deal with anything else */
-		break;
-	case 3:
- ptest_three:
-		swp = te.pos.wp;
-		/* use inside knowledge of ptest_getopnd inlined below */
-		lhs = *te.pos.wp++;
-		if ((op = ptest_isa(&te, TM_BINOP))) {
-			/* test lhs op rhs */
-			rv = test_eval(&te, op, lhs, *te.pos.wp++, true);
-			goto ptest_out;
-		}
-		/* back up to lhs */
-		te.pos.wp = swp;
-		if (ptest_isa(&te, TM_NOT)) {
-			++invert;
-			goto ptest_two;
-		}
-		if (ptest_isa(&te, TM_OPAREN)) {
-			swp = te.pos.wp;
-			/* skip operand, without evaluation */
-			te.pos.wp++;
-			/* check for closing parenthesis */
-			op = ptest_isa(&te, TM_CPAREN);
-			/* back up to operand */
-			te.pos.wp = swp;
-			/* if there was a closing paren, handle it */
-			if (op)
-				goto ptest_one;
-			/* backing up is done before calling the parser */
-		}
-		/* let the parser deal with it */
-		break;
-	case 4:
-		if (ptest_isa(&te, TM_NOT)) {
-			++invert;
-			goto ptest_three;
-		}
-		if (ptest_isa(&te, TM_OPAREN)) {
-			swp = te.pos.wp;
-			/* skip two operands, without evaluation */
-			te.pos.wp++;
-			te.pos.wp++;
-			/* check for closing parenthesis */
-			op = ptest_isa(&te, TM_CPAREN);
-			/* back up to first operand */
-			te.pos.wp = swp;
-			/* if there was a closing paren, handle it */
-			if (op)
-				goto ptest_two;
-			/* backing up is done before calling the parser */
-		}
-		/* defer this to the parser */
-		break;
-	}
-
-	/* "The results are unspecified." */
-	te.pos.wp = wp + 1;
-	return (test_parse(&te));
-}
-
-/*
- * Generic test routines.
- */
-
-Test_op
-test_isop(Test_meta meta, const char *s)
-{
-	char sc1;
-	const struct t_op *tbl;
-
-	tbl = meta == TM_UNOP ? u_ops : b_ops;
-	if (*s) {
-		sc1 = s[1];
-		for (; tbl->op_text[0]; tbl++)
-			if (sc1 == tbl->op_text[1] && !strcmp(s, tbl->op_text))
-				return (tbl->op_num);
-	}
-	return (TO_NONOP);
-}
-
-int
-test_eval(Test_env *te, Test_op op, const char *opnd1, const char *opnd2,
-    bool do_eval)
-{
-	int i, s;
-	size_t k;
-	struct stat b1, b2;
-	mksh_ari_t v1, v2;
-
-	if (!do_eval)
-		return (0);
-
-#ifdef DEBUG
-	switch (op) {
-	/* Binary operators */
-	case TO_STEQL:
-	case TO_STNEQ:
-	case TO_STLT:
-	case TO_STGT:
-	case TO_INTEQ:
-	case TO_INTNE:
-	case TO_INTGT:
-	case TO_INTGE:
-	case TO_INTLT:
-	case TO_INTLE:
-	case TO_FILEQ:
-	case TO_FILNT:
-	case TO_FILOT:
-		/* consistency check, but does not happen in practice */
-		if (!opnd2) {
-			te->flags |= TEF_ERROR;
-			return (1);
-		}
-		break;
-	default:
-		/* for completeness of switch */
-		break;
-	}
-#endif
-
-	switch (op) {
-
-	/*
-	 * Unary Operators
-	 */
-
-	/* -n */
-	case TO_STNZE:
-		return (*opnd1 != '\0');
-
-	/* -z */
-	case TO_STZER:
-		return (*opnd1 == '\0');
-
-	/* -o */
-	case TO_OPTION:
-		if ((i = *opnd1) == '!' || i == '?')
-			opnd1++;
-		if ((k = option(opnd1)) == (size_t)-1)
-			return (0);
-		return (i == '?' ? 1 : i == '!' ? !Flag(k) : Flag(k));
-
-	/* -r */
-	case TO_FILRD:
-		/* LINTED use of access */
-		return (access(opnd1, R_OK) == 0);
-
-	/* -w */
-	case TO_FILWR:
-		/* LINTED use of access */
-		return (access(opnd1, W_OK) == 0);
-
-	/* -x */
-	case TO_FILEX:
-		return (ksh_access(opnd1, X_OK) == 0);
-
-	/* -a */
-	case TO_FILAXST:
-	/* -e */
-	case TO_FILEXST:
-		return (stat(opnd1, &b1) == 0);
-
-	/* -r */
-	case TO_FILREG:
-		return (stat(opnd1, &b1) == 0 && S_ISREG(b1.st_mode));
-
-	/* -d */
-	case TO_FILID:
-		return (stat(opnd1, &b1) == 0 && S_ISDIR(b1.st_mode));
-
-	/* -c */
-	case TO_FILCDEV:
-		return (stat(opnd1, &b1) == 0 && S_ISCHR(b1.st_mode));
-
-	/* -b */
-	case TO_FILBDEV:
-		return (stat(opnd1, &b1) == 0 && S_ISBLK(b1.st_mode));
-
-	/* -p */
-	case TO_FILFIFO:
-		return (stat(opnd1, &b1) == 0 && S_ISFIFO(b1.st_mode));
-
-	/* -h or -L */
-	case TO_FILSYM:
-#ifdef MKSH__NO_SYMLINK
-		return (0);
-#else
-		return (lstat(opnd1, &b1) == 0 && S_ISLNK(b1.st_mode));
-#endif
-
-	/* -S */
-	case TO_FILSOCK:
-		return (stat(opnd1, &b1) == 0 && S_ISSOCK(b1.st_mode));
-
-	/* -H => HP context dependent files (directories) */
-	case TO_FILCDF:
-#ifdef S_ISCDF
-	{
-		char *nv;
-
-		/*
-		 * Append a + to filename and check to see if result is
-		 * a setuid directory. CDF stuff in general is hookey,
-		 * since it breaks for, e.g., the following sequence:
-		 * echo hi >foo+; mkdir foo; echo bye >foo/default;
-		 * chmod u+s foo (foo+ refers to the file with hi in it,
-		 * there is no way to get at the file with bye in it;
-		 * please correct me if I'm wrong about this).
-		 */
-
-		nv = shf_smprintf("%s+", opnd1);
-		i = (stat(nv, &b1) == 0 && S_ISCDF(b1.st_mode));
-		afree(nv, ATEMP);
-		return (i);
-	}
-#else
-		return (0);
-#endif
-
-	/* -u */
-	case TO_FILSETU:
-		return (stat(opnd1, &b1) == 0 &&
-		    (b1.st_mode & S_ISUID) == S_ISUID);
-
-	/* -g */
-	case TO_FILSETG:
-		return (stat(opnd1, &b1) == 0 &&
-		    (b1.st_mode & S_ISGID) == S_ISGID);
-
-	/* -k */
-	case TO_FILSTCK:
-#ifdef S_ISVTX
-		return (stat(opnd1, &b1) == 0 &&
-		    (b1.st_mode & S_ISVTX) == S_ISVTX);
-#else
-		return (0);
-#endif
-
-	/* -s */
-	case TO_FILGZ:
-		return (stat(opnd1, &b1) == 0 && (off_t)b1.st_size > (off_t)0);
-
-	/* -t */
-	case TO_FILTT:
-		if (opnd1 && !bi_getn(opnd1, &i)) {
-			te->flags |= TEF_ERROR;
-			i = 0;
-		} else
-			i = isatty(opnd1 ? i : 0);
-		return (i);
-
-	/* -O */
-	case TO_FILUID:
-		return (stat(opnd1, &b1) == 0 && (uid_t)b1.st_uid == ksheuid);
-
-	/* -G */
-	case TO_FILGID:
-		return (stat(opnd1, &b1) == 0 && (gid_t)b1.st_gid == getegid());
-
-	/*
-	 * Binary Operators
-	 */
-
-	/* = */
-	case TO_STEQL:
-		if (te->flags & TEF_DBRACKET)
-			return (gmatchx(opnd1, opnd2, false));
-		return (strcmp(opnd1, opnd2) == 0);
-
-	/* != */
-	case TO_STNEQ:
-		if (te->flags & TEF_DBRACKET)
-			return (!gmatchx(opnd1, opnd2, false));
-		return (strcmp(opnd1, opnd2) != 0);
-
-	/* < */
-	case TO_STLT:
-		return (strcmp(opnd1, opnd2) < 0);
-
-	/* > */
-	case TO_STGT:
-		return (strcmp(opnd1, opnd2) > 0);
-
-	/* -eq */
-	case TO_INTEQ:
-	/* -ne */
-	case TO_INTNE:
-	/* -ge */
-	case TO_INTGE:
-	/* -gt */
-	case TO_INTGT:
-	/* -le */
-	case TO_INTLE:
-	/* -lt */
-	case TO_INTLT:
-		if (!evaluate(opnd1, &v1, KSH_RETURN_ERROR, false) ||
-		    !evaluate(opnd2, &v2, KSH_RETURN_ERROR, false)) {
-			/* error already printed.. */
-			te->flags |= TEF_ERROR;
-			return (1);
-		}
-		switch (op) {
-		case TO_INTEQ:
-			return (v1 == v2);
-		case TO_INTNE:
-			return (v1 != v2);
-		case TO_INTGE:
-			return (v1 >= v2);
-		case TO_INTGT:
-			return (v1 > v2);
-		case TO_INTLE:
-			return (v1 <= v2);
-		case TO_INTLT:
-			return (v1 < v2);
-		default:
-			/* NOTREACHED */
-			break;
-		}
-		/* NOTREACHED */
-
-	/* -nt */
-	case TO_FILNT:
-		/*
-		 * ksh88/ksh93 succeed if file2 can't be stated
-		 * (subtly different from 'does not exist').
-		 */
-		return (stat(opnd1, &b1) == 0 &&
-		    (((s = stat(opnd2, &b2)) == 0 &&
-		    b1.st_mtime > b2.st_mtime) || s < 0));
-
-	/* -ot */
-	case TO_FILOT:
-		/*
-		 * ksh88/ksh93 succeed if file1 can't be stated
-		 * (subtly different from 'does not exist').
-		 */
-		return (stat(opnd2, &b2) == 0 &&
-		    (((s = stat(opnd1, &b1)) == 0 &&
-		    b1.st_mtime < b2.st_mtime) || s < 0));
-
-	/* -ef */
-	case TO_FILEQ:
-		return (stat (opnd1, &b1) == 0 && stat (opnd2, &b2) == 0 &&
-		    b1.st_dev == b2.st_dev && b1.st_ino == b2.st_ino);
-
-	/* all other cases */
-	case TO_NONOP:
-	case TO_NONNULL:
-		/* throw the error */
-		break;
-	}
-	(*te->error)(te, 0, "internal error: unknown op");
-	return (1);
-}
-
-int
-test_parse(Test_env *te)
-{
-	int rv;
-
-	rv = test_oexpr(te, 1);
-
-	if (!(te->flags & TEF_ERROR) && !(*te->isa)(te, TM_END))
-		(*te->error)(te, 0, "unexpected operator/operand");
-
-	return ((te->flags & TEF_ERROR) ? T_ERR_EXIT : !rv);
-}
-
-static int
-test_oexpr(Test_env *te, bool do_eval)
-{
-	int rv;
-
-	if ((rv = test_aexpr(te, do_eval)))
-		do_eval = false;
-	if (!(te->flags & TEF_ERROR) && (*te->isa)(te, TM_OR))
-		return (test_oexpr(te, do_eval) || rv);
-	return (rv);
-}
-
-static int
-test_aexpr(Test_env *te, bool do_eval)
-{
-	int rv;
-
-	if (!(rv = test_nexpr(te, do_eval)))
-		do_eval = false;
-	if (!(te->flags & TEF_ERROR) && (*te->isa)(te, TM_AND))
-		return (test_aexpr(te, do_eval) && rv);
-	return (rv);
-}
-
-static int
-test_nexpr(Test_env *te, bool do_eval)
-{
-	if (!(te->flags & TEF_ERROR) && (*te->isa)(te, TM_NOT))
-		return (!test_nexpr(te, do_eval));
-	return (test_primary(te, do_eval));
-}
-
-static int
-test_primary(Test_env *te, bool do_eval)
-{
-	const char *opnd1, *opnd2;
-	int rv;
-	Test_op op;
-
-	if (te->flags & TEF_ERROR)
-		return (0);
-	if ((*te->isa)(te, TM_OPAREN)) {
-		rv = test_oexpr(te, do_eval);
-		if (te->flags & TEF_ERROR)
-			return (0);
-		if (!(*te->isa)(te, TM_CPAREN)) {
-			(*te->error)(te, 0, "missing )");
-			return (0);
-		}
-		return (rv);
-	}
-	/*
-	 * Binary should have precedence over unary in this case
-	 * so that something like test \( -f = -f \) is accepted
-	 */
-	if ((te->flags & TEF_DBRACKET) || (&te->pos.wp[1] < te->wp_end &&
-	    !test_isop(TM_BINOP, te->pos.wp[1]))) {
-		if ((op = (*te->isa)(te, TM_UNOP))) {
-			/* unary expression */
-			opnd1 = (*te->getopnd)(te, op, do_eval);
-			if (!opnd1) {
-				(*te->error)(te, -1, "missing argument");
-				return (0);
-			}
-
-			return ((*te->eval)(te, op, opnd1, NULL, do_eval));
-		}
-	}
-	opnd1 = (*te->getopnd)(te, TO_NONOP, do_eval);
-	if (!opnd1) {
-		(*te->error)(te, 0, "expression expected");
-		return (0);
-	}
-	if ((op = (*te->isa)(te, TM_BINOP))) {
-		/* binary expression */
-		opnd2 = (*te->getopnd)(te, op, do_eval);
-		if (!opnd2) {
-			(*te->error)(te, -1, "missing second argument");
-			return (0);
-		}
-
-		return ((*te->eval)(te, op, opnd1, opnd2, do_eval));
-	}
-	return ((*te->eval)(te, TO_STNZE, opnd1, NULL, do_eval));
-}
-
-/*
- * Plain test (test and [ .. ]) specific routines.
- */
-
-/*
- * Test if the current token is a whatever. Accepts the current token if
- * it is. Returns 0 if it is not, non-zero if it is (in the case of
- * TM_UNOP and TM_BINOP, the returned value is a Test_op).
- */
-static Test_op
-ptest_isa(Test_env *te, Test_meta meta)
-{
-	/* Order important - indexed by Test_meta values */
-	static const char * const tokens[] = {
-		"-o", "-a", "!", "(", ")"
-	};
-	Test_op rv;
-
-	if (te->pos.wp >= te->wp_end)
-		return (meta == TM_END ? TO_NONNULL : TO_NONOP);
-
-	if (meta == TM_UNOP || meta == TM_BINOP)
-		rv = test_isop(meta, *te->pos.wp);
-	else if (meta == TM_END)
-		rv = TO_NONOP;
-	else
-		rv = !strcmp(*te->pos.wp, tokens[(int)meta]) ?
-		    TO_NONNULL : TO_NONOP;
-
-	/* Accept the token? */
-	if (rv != TO_NONOP)
-		te->pos.wp++;
-
-	return (rv);
-}
-
-static const char *
-ptest_getopnd(Test_env *te, Test_op op, bool do_eval MKSH_A_UNUSED)
-{
-	if (te->pos.wp >= te->wp_end)
-		return (op == TO_FILTT ? "1" : NULL);
-	return (*te->pos.wp++);
-}
-
-static void
-ptest_error(Test_env *te, int ofs, const char *msg)
-{
-	const char *op;
-
-	te->flags |= TEF_ERROR;
-	if ((op = te->pos.wp + ofs >= te->wp_end ? NULL : te->pos.wp[ofs]))
-		bi_errorf("%s: %s", op, msg);
-	else
-		bi_errorf("%s", msg);
-}
-
-#ifndef MKSH_NO_LIMITS
-#define SOFT	0x1
-#define HARD	0x2
-
-struct limits {
-	const char *name;
-	int resource;		/* resource to get/set */
-	unsigned int factor;	/* multiply by to get rlim_{cur,max} values */
-	char option;
-};
-
-static void print_ulimit(const struct limits *, int);
-static int set_ulimit(const struct limits *, const char *, int);
-
-/* Magic to divine the 'm' and 'v' limits */
-
-#ifdef RLIMIT_AS
-#if !defined(RLIMIT_VMEM) || (RLIMIT_VMEM == RLIMIT_AS) || \
-    !defined(RLIMIT_RSS) || (RLIMIT_VMEM == RLIMIT_RSS)
-#define ULIMIT_V_IS_AS
-#elif defined(RLIMIT_VMEM)
-#if !defined(RLIMIT_RSS) || (RLIMIT_RSS == RLIMIT_AS)
-#define ULIMIT_V_IS_AS
-#else
-#define ULIMIT_V_IS_VMEM
-#endif
-#endif
-#endif
-
-#ifdef RLIMIT_RSS
-#ifdef ULIMIT_V_IS_VMEM
-#define ULIMIT_M_IS_RSS
-#elif defined(RLIMIT_VMEM) && (RLIMIT_VMEM == RLIMIT_RSS)
-#define ULIMIT_M_IS_VMEM
-#else
-#define ULIMIT_M_IS_RSS
-#endif
-#if defined(ULIMIT_M_IS_RSS) && defined(RLIMIT_AS) && (RLIMIT_RSS == RLIMIT_AS)
-#undef ULIMIT_M_IS_RSS
-#endif
-#endif
-
-#if !defined(RLIMIT_AS) && !defined(ULIMIT_M_IS_VMEM) && defined(RLIMIT_VMEM)
-#define ULIMIT_V_IS_VMEM
-#endif
-
-#if !defined(ULIMIT_V_IS_VMEM) && defined(RLIMIT_VMEM) && \
-    (!defined(RLIMIT_RSS) || (defined(RLIMIT_AS) && (RLIMIT_RSS == RLIMIT_AS)))
-#define ULIMIT_M_IS_VMEM
-#endif
-
-#if defined(ULIMIT_M_IS_VMEM) && defined(RLIMIT_AS) && \
-    (RLIMIT_VMEM == RLIMIT_AS)
-#undef ULIMIT_M_IS_VMEM
-#endif
-
-
-int
-c_ulimit(const char **wp)
-{
-	static const struct limits limits[] = {
-		/* do not use options -H, -S or -a or change the order */
-#ifdef RLIMIT_CPU
-		{ "time(cpu-seconds)", RLIMIT_CPU, 1, 't' },
-#endif
-#ifdef RLIMIT_FSIZE
-		{ "file(blocks)", RLIMIT_FSIZE, 512, 'f' },
-#endif
-#ifdef RLIMIT_CORE
-		{ "coredump(blocks)", RLIMIT_CORE, 512, 'c' },
-#endif
-#ifdef RLIMIT_DATA
-		{ "data(KiB)", RLIMIT_DATA, 1024, 'd' },
-#endif
-#ifdef RLIMIT_STACK
-		{ "stack(KiB)", RLIMIT_STACK, 1024, 's' },
-#endif
-#ifdef RLIMIT_MEMLOCK
-		{ "lockedmem(KiB)", RLIMIT_MEMLOCK, 1024, 'l' },
-#endif
-#ifdef RLIMIT_NOFILE
-		{ "nofiles(descriptors)", RLIMIT_NOFILE, 1, 'n' },
-#endif
-#ifdef RLIMIT_NPROC
-		{ "processes", RLIMIT_NPROC, 1, 'p' },
-#endif
-#ifdef RLIMIT_SWAP
-		{ "swap(KiB)", RLIMIT_SWAP, 1024, 'w' },
-#endif
-#ifdef RLIMIT_LOCKS
-		{ "flocks", RLIMIT_LOCKS, -1, 'L' },
-#endif
-#ifdef RLIMIT_TIME
-		{ "humantime(seconds)", RLIMIT_TIME, 1, 'T' },
-#endif
-#ifdef RLIMIT_NOVMON
-		{ "vnodemonitors", RLIMIT_NOVMON, 1, 'V' },
-#endif
-#ifdef RLIMIT_SIGPENDING
-		{ "sigpending", RLIMIT_SIGPENDING, 1, 'i' },
-#endif
-#ifdef RLIMIT_MSGQUEUE
-		{ "msgqueue(bytes)", RLIMIT_MSGQUEUE, 1, 'q' },
-#endif
-#ifdef RLIMIT_AIO_MEM
-		{ "AIOlockedmem(KiB)", RLIMIT_AIO_MEM, 1024, 'M' },
-#endif
-#ifdef RLIMIT_AIO_OPS
-		{ "AIOoperations", RLIMIT_AIO_OPS, 1, 'O' },
-#endif
-#ifdef RLIMIT_TCACHE
-		{ "cachedthreads", RLIMIT_TCACHE, 1, 'C' },
-#endif
-#ifdef RLIMIT_SBSIZE
-		{ "sockbufsiz(KiB)", RLIMIT_SBSIZE, 1024, 'B' },
-#endif
-#ifdef RLIMIT_PTHREAD
-		{ "threadsperprocess", RLIMIT_PTHREAD, 1, 'P' },
-#endif
-#ifdef RLIMIT_NICE
-		{ "maxnice", RLIMIT_NICE, 1, 'e' },
-#endif
-#ifdef RLIMIT_RTPRIO
-		{ "maxrtprio", RLIMIT_RTPRIO, 1, 'r' },
-#endif
-#if defined(ULIMIT_M_IS_RSS)
-		{ "resident-set(KiB)", RLIMIT_RSS, 1024, 'm' },
-#elif defined(ULIMIT_M_IS_VMEM)
-		{ "memory(KiB)", RLIMIT_VMEM, 1024, 'm' },
-#endif
-#if defined(ULIMIT_V_IS_VMEM)
-		{ "virtual-memory(KiB)", RLIMIT_VMEM, 1024, 'v' },
-#elif defined(ULIMIT_V_IS_AS)
-		{ "address-space(KiB)", RLIMIT_AS, 1024, 'v' },
-#endif
-		{ NULL, 0, 0, 0 }
-	};
-	static const char opts[] = "a"
-#ifdef RLIMIT_SBSIZE
-	    "B"
-#endif
-#ifdef RLIMIT_TCACHE
-	    "C"
-#endif
-#ifdef RLIMIT_CORE
-	    "c"
-#endif
-#ifdef RLIMIT_DATA
-	    "d"
-#endif
-#ifdef RLIMIT_NICE
-	    "e"
-#endif
-#ifdef RLIMIT_FSIZE
-	    "f"
-#endif
-	    "H"
-#ifdef RLIMIT_SIGPENDING
-	    "i"
-#endif
-#ifdef RLIMIT_LOCKS
-	    "L"
-#endif
-#ifdef RLIMIT_MEMLOCK
-	    "l"
-#endif
-#ifdef RLIMIT_AIO_MEM
-	    "M"
-#endif
-#if defined(ULIMIT_M_IS_RSS) || defined(ULIMIT_M_IS_VMEM)
-	    "m"
-#endif
-#ifdef RLIMIT_NOFILE
-	    "n"
-#endif
-#ifdef RLIMIT_AIO_OPS
-	    "O"
-#endif
-#ifdef RLIMIT_PTHREAD
-	    "P"
-#endif
-#ifdef RLIMIT_NPROC
-	    "p"
-#endif
-#ifdef RLIMIT_MSGQUEUE
-	    "q"
-#endif
-#ifdef RLIMIT_RTPRIO
-	    "r"
-#endif
-	    "S"
-#ifdef RLIMIT_STACK
-	    "s"
-#endif
-#ifdef RLIMIT_TIME
-	    "T"
-#endif
-#ifdef RLIMIT_CPU
-	    "t"
-#endif
-#ifdef RLIMIT_NOVMON
-	    "V"
-#endif
-#if defined(ULIMIT_V_IS_VMEM) || defined(ULIMIT_V_IS_AS)
-	    "v"
-#endif
-#ifdef RLIMIT_SWAP
-	    "w"
-#endif
-	    ;
-	int how = SOFT | HARD, optc, what = 'f';
-	bool all = false;
-	const struct limits *l;
-
-	while ((optc = ksh_getopt(wp, &builtin_opt, opts)) != -1)
-		switch (optc) {
-		case 'H':
-			how = HARD;
-			break;
-		case 'S':
-			how = SOFT;
-			break;
-		case 'a':
-			all = true;
-			break;
-		case '?':
-			bi_errorf("usage: ulimit [-%s] [value]", opts);
-			return (1);
-		default:
-			what = optc;
-		}
-
-	for (l = limits; l->name && l->option != what; l++)
-		;
-	if (!l->name) {
-		internal_warningf("ulimit: %c", what);
-		return (1);
-	}
-
-	if (wp[builtin_opt.optind]) {
-		if (all || wp[builtin_opt.optind + 1]) {
-			bi_errorf("too many arguments");
-			return (1);
-		}
-		return (set_ulimit(l, wp[builtin_opt.optind], how));
-	}
-	if (!all)
-		print_ulimit(l, how);
-	else for (l = limits; l->name; l++) {
-		shprintf("%-20s ", l->name);
-		print_ulimit(l, how);
-	}
-	return (0);
-}
-
-static int
-set_ulimit(const struct limits *l, const char *v, int how)
-{
-	rlim_t val = (rlim_t)0;
-	struct rlimit limit;
-
-	if (strcmp(v, "unlimited") == 0)
-		val = (rlim_t)RLIM_INFINITY;
-	else {
-		mksh_uari_t rval;
-
-		if (!evaluate(v, (mksh_ari_t *)&rval, KSH_RETURN_ERROR, false))
-			return (1);
-		/*
-		 * Avoid problems caused by typos that evaluate misses due
-		 * to evaluating unset parameters to 0...
-		 * If this causes problems, will have to add parameter to
-		 * evaluate() to control if unset params are 0 or an error.
-		 */
-		if (!rval && !ksh_isdigit(v[0])) {
-			bi_errorf("invalid %s limit: %s", l->name, v);
-			return (1);
-		}
-		val = (rlim_t)((rlim_t)rval * l->factor);
-	}
-
-	if (getrlimit(l->resource, &limit) < 0) {
-		/* some can't be read, e.g. Linux RLIMIT_LOCKS */
-		limit.rlim_cur = RLIM_INFINITY;
-		limit.rlim_max = RLIM_INFINITY;
-	}
-	if (how & SOFT)
-		limit.rlim_cur = val;
-	if (how & HARD)
-		limit.rlim_max = val;
-	if (!setrlimit(l->resource, &limit))
-		return (0);
-	if (errno == EPERM)
-		bi_errorf("%s exceeds allowable %s limit", v, l->name);
-	else
-		bi_errorf("bad %s limit: %s", l->name, cstrerror(errno));
-	return (1);
-}
-
-static void
-print_ulimit(const struct limits *l, int how)
-{
-	rlim_t val = (rlim_t)0;
-	struct rlimit limit;
-
-	if (getrlimit(l->resource, &limit)) {
-		shf_puts("unknown\n", shl_stdout);
-		return;
-	}
-	if (how & SOFT)
-		val = limit.rlim_cur;
-	else if (how & HARD)
-		val = limit.rlim_max;
-	if (val == (rlim_t)RLIM_INFINITY)
-		shf_puts("unlimited\n", shl_stdout);
-	else
-		shprintf("%lu\n", (unsigned long)(val / l->factor));
-}
-#endif
-
-int
-c_rename(const char **wp)
-{
-	int rv = 1;
-
-	/* skip argv[0] */
-	++wp;
-	if (wp[0] && !strcmp(wp[0], "--"))
-		/* skip "--" (options separator) */
-		++wp;
-
-	/* check for exactly two arguments */
-	if (wp[0] == NULL	/* first argument */ ||
-	    wp[1] == NULL	/* second argument */ ||
-	    wp[2] != NULL	/* no further args please */)
-		bi_errorf(Tsynerr);
-	else if ((rv = rename(wp[0], wp[1])) != 0) {
-		rv = errno;
-		bi_errorf("%s: %s", "failed", cstrerror(rv));
-	}
-
-	return (rv);
-}
-
-int
-c_realpath(const char **wp)
-{
-	int rv = 1;
-	char *buf;
-
-	/* skip argv[0] */
-	++wp;
-	if (wp[0] && !strcmp(wp[0], "--"))
-		/* skip "--" (options separator) */
-		++wp;
-
-	/* check for exactly one argument */
-	if (wp[0] == NULL || wp[1] != NULL)
-		bi_errorf(Tsynerr);
-	else if ((buf = do_realpath(wp[0])) == NULL) {
-		rv = errno;
-		bi_errorf("%s: %s", wp[0], cstrerror(rv));
-		if ((unsigned int)rv > 255)
-			rv = 255;
-	} else {
-		shprintf("%s\n", buf);
-		afree(buf, ATEMP);
-		rv = 0;
-	}
-
-	return (rv);
-}
-
-int
-c_cat(const char **wp)
-{
-	int fd = STDIN_FILENO, rv, eno;
-	ssize_t n, w;
-	const char *fn = "<stdin>";
-	char *buf, *cp;
-#define MKSH_CAT_BUFSIZ 4096
-
-	/* parse options: POSIX demands we support "-u" as no-op */
-	while ((rv = ksh_getopt(wp, &builtin_opt, "u")) != -1) {
-		switch (rv) {
-		case 'u':
-			/* we already operate unbuffered */
-			break;
-		default:
-			bi_errorf(Tsynerr);
-			return (1);
-		}
-	}
-	wp += builtin_opt.optind;
-	rv = 0;
-
-	if ((buf = malloc_osfunc(MKSH_CAT_BUFSIZ)) == NULL) {
-		bi_errorf(Toomem, (size_t)MKSH_CAT_BUFSIZ);
-		return (1);
-	}
-
-	do {
-		if (*wp) {
-			fn = *wp++;
-			if (fn[0] == '-' && fn[1] == '\0')
-				fd = STDIN_FILENO;
-			else if ((fd = open(fn, O_RDONLY)) < 0) {
-				eno = errno;
-				bi_errorf("%s: %s", fn, cstrerror(eno));
-				rv = 1;
-				continue;
-			}
-		}
-		while (/* CONSTCOND */ 1) {
-			n = blocking_read(fd, (cp = buf), MKSH_CAT_BUFSIZ);
-			eno = errno;
-			/* give the user a chance to ^C out */
-			intrcheck();
-			if (n == -1) {
-				if (eno == EINTR) {
-					/* interrupted, try again */
-					continue;
-				}
-				/* an error occured during reading */
-				bi_errorf("%s: %s", fn, cstrerror(eno));
-				rv = 1;
-				break;
-			} else if (n == 0)
-				/* end of file reached */
-				break;
-			while (n) {
-				w = write(STDOUT_FILENO, cp, n);
-				if (w == -1) {
-					if (errno == EINTR)
-						/* interrupted, try again */
-						continue;
-					/* an error occured during writing */
-					eno = errno;
-					bi_errorf("%s: %s", "<stdout>",
-					    cstrerror(eno));
-					rv = 1;
-					if (fd != STDIN_FILENO)
-						close(fd);
-					goto out;
-				}
-				n -= w;
-				cp += w;
-			}
-		}
-		if (fd != STDIN_FILENO)
-			close(fd);
-	} while (*wp);
-
- out:
-	free_osfunc(buf);
-	return (rv);
-}
-
-#if HAVE_SELECT
-int
-c_sleep(const char **wp)
-{
-	struct timeval tv;
-	int rv = 1;
-
-	/* skip argv[0] */
-	++wp;
-	if (wp[0] && !strcmp(wp[0], "--"))
-		/* skip "--" (options separator) */
-		++wp;
-
-	if (!wp[0] || wp[1])
-		bi_errorf(Tsynerr);
-	else if (parse_usec(wp[0], &tv))
-		bi_errorf("%s: %s '%s'", Tsynerr, cstrerror(errno), wp[0]);
-	else {
-#ifndef MKSH_NOPROSPECTOFWORK
-		sigset_t omask, bmask;
-
-		/* block a number of signals from interrupting us, though */
-		(void)sigemptyset(&bmask);
-		(void)sigaddset(&bmask, SIGPIPE);
-		(void)sigaddset(&bmask, SIGCHLD);
-#ifdef SIGWINCH
-		(void)sigaddset(&bmask, SIGWINCH);
-#endif
-#ifdef SIGINFO
-		(void)sigaddset(&bmask, SIGINFO);
-#endif
-#ifdef SIGUSR1
-		(void)sigaddset(&bmask, SIGUSR1);
-#endif
-#ifdef SIGUSR2
-		(void)sigaddset(&bmask, SIGUSR2);
-#endif
-		sigprocmask(SIG_BLOCK, &bmask, &omask);
-#endif
-		if (select(1, NULL, NULL, NULL, &tv) == 0 || errno == EINTR)
-			/*
-			 * strictly speaking only for SIGALRM, but the
-			 * execution may be interrupted by other signals
-			 */
-			rv = 0;
-		else
-			bi_errorf("%s: %s", Tselect, cstrerror(errno));
-#ifndef MKSH_NOPROSPECTOFWORK
-		/* this will re-schedule signal delivery */
-		sigprocmask(SIG_SETMASK, &omask, NULL);
-#endif
-	}
-	return (rv);
-}
-#endif

Copied: vendor/MirOS/mksh/R50/funcs.c (from rev 6707, vendor/MirOS/mksh/dist/funcs.c)
===================================================================
--- vendor/MirOS/mksh/R50/funcs.c	                        (rev 0)
+++ vendor/MirOS/mksh/R50/funcs.c	2014-07-01 12:01:17 UTC (rev 6708)
@@ -0,0 +1,3747 @@
+/*	$OpenBSD: c_ksh.c,v 1.34 2013/12/17 16:37:05 deraadt Exp $	*/
+/*	$OpenBSD: c_sh.c,v 1.44 2013/09/04 15:49:18 millert Exp $	*/
+/*	$OpenBSD: c_test.c,v 1.18 2009/03/01 20:11:06 otto Exp $	*/
+/*	$OpenBSD: c_ulimit.c,v 1.19 2013/11/28 10:33:37 sobrado Exp $	*/
+
+/*-
+ * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009,
+ *		 2010, 2011, 2012, 2013, 2014
+ *	Thorsten Glaser <tg at mirbsd.org>
+ *
+ * Provided that these terms and disclaimer and all copyright notices
+ * are retained or reproduced in an accompanying document, permission
+ * is granted to deal in this work without restriction, including un-
+ * limited rights to use, publicly perform, distribute, sell, modify,
+ * merge, give away, or sublicence.
+ *
+ * This work is provided "AS IS" and WITHOUT WARRANTY of any kind, to
+ * the utmost extent permitted by applicable law, neither express nor
+ * implied; without malicious intent or gross negligence. In no event
+ * may a licensor, author or contributor be held liable for indirect,
+ * direct, other damage, loss, or other issues arising in any way out
+ * of dealing in the work, even if advised of the possibility of such
+ * damage or existence of a defect, except proven that it results out
+ * of said person's immediate fault when using the work as intended.
+ */
+
+#include "sh.h"
+
+#if HAVE_SELECT
+#if HAVE_SYS_BSDTYPES_H
+#include <sys/bsdtypes.h>
+#endif
+#if HAVE_SYS_SELECT_H
+#include <sys/select.h>
+#endif
+#if HAVE_BSTRING_H
+#include <bstring.h>
+#endif
+#endif
+
+__RCSID("$MirOS: src/bin/mksh/funcs.c,v 1.256 2014/06/09 13:25:52 tg Exp $");
+
+#if HAVE_KILLPG
+/*
+ * use killpg if < -1 since -1 does special things
+ * for some non-killpg-endowed kills
+ */
+#define mksh_kill(p,s)	((p) < -1 ? killpg(-(p), (s)) : kill((p), (s)))
+#else
+/* cross fingers and hope kill is killpg-endowed */
+#define mksh_kill	kill
+#endif
+
+/* XXX conditions correct? */
+#if !defined(RLIM_INFINITY) && !defined(MKSH_NO_LIMITS)
+#define MKSH_NO_LIMITS	1
+#endif
+
+#ifdef MKSH_NO_LIMITS
+#define c_ulimit	c_true
+#endif
+
+#if !defined(MKSH_UNEMPLOYED) && HAVE_GETSID
+static int c_suspend(const char **);
+#endif
+
+/* getn() that prints error */
+static int
+bi_getn(const char *as, int *ai)
+{
+	int rv;
+
+	if (!(rv = getn(as, ai)))
+		bi_errorf("%s: %s", as, "bad number");
+	return (rv);
+}
+
+static int
+c_true(const char **wp MKSH_A_UNUSED)
+{
+	return (0);
+}
+
+static int
+c_false(const char **wp MKSH_A_UNUSED)
+{
+	return (1);
+}
+
+/*
+ * A leading = means assignments before command are kept.
+ * A leading * means a POSIX special builtin.
+ */
+const struct builtin mkshbuiltins[] = {
+	{"*=.", c_dot},
+	{"*=:", c_true},
+	{"[", c_test},
+	/* no =: AT&T manual wrong */
+	{Talias, c_alias},
+	{"*=break", c_brkcont},
+	{Tgbuiltin, c_builtin},
+	{"cat", c_cat},
+	{"cd", c_cd},
+	/* dash compatibility hack */
+	{"chdir", c_cd},
+	{"command", c_command},
+	{"*=continue", c_brkcont},
+	{"echo", c_print},
+	{"*=eval", c_eval},
+	{"*=exec", c_exec},
+	{"*=exit", c_exitreturn},
+	{Tsgexport, c_typeset},
+	{"false", c_false},
+	{"fc", c_fc},
+	{"getopts", c_getopts},
+	{"=global", c_typeset},
+	{"jobs", c_jobs},
+	{"kill", c_kill},
+	{"let", c_let},
+	{"let]", c_let},
+	{"print", c_print},
+	{"pwd", c_pwd},
+	{"read", c_read},
+	{Tsgreadonly, c_typeset},
+	{"realpath", c_realpath},
+	{"rename", c_rename},
+	{"*=return", c_exitreturn},
+	{Tsgset, c_set},
+	{"*=shift", c_shift},
+#if !defined(MKSH_UNEMPLOYED) && HAVE_GETSID
+	{"suspend", c_suspend},
+#endif
+	{"test", c_test},
+	{"*=times", c_times},
+	{"*=trap", c_trap},
+	{"true", c_true},
+	{T_typeset, c_typeset},
+	{"ulimit", c_ulimit},
+	{"umask", c_umask},
+	{Tunalias, c_unalias},
+	{Tsgunset, c_unset},
+	{"=wait", c_wait},
+	{"whence", c_whence},
+#ifndef MKSH_UNEMPLOYED
+	{"bg", c_fgbg},
+	{"fg", c_fgbg},
+#endif
+#ifndef MKSH_NO_CMDLINE_EDITING
+	{"bind", c_bind},
+#endif
+#if HAVE_MKNOD
+	{"mknod", c_mknod},
+#endif
+#ifdef MKSH_PRINTF_BUILTIN
+	{"printf", c_printf},
+#endif
+#if HAVE_SELECT
+	{"sleep", c_sleep},
+#endif
+#ifdef __MirBSD__
+	/* alias to "true" for historical reasons */
+	{"domainname", c_true},
+#endif
+	{NULL, (int (*)(const char **))NULL}
+};
+
+struct kill_info {
+	int num_width;
+	int name_width;
+};
+
+static const struct t_op {
+	char op_text[4];
+	Test_op op_num;
+} u_ops[] = {
+	{"-a",	TO_FILAXST },
+	{"-b",	TO_FILBDEV },
+	{"-c",	TO_FILCDEV },
+	{"-d",	TO_FILID },
+	{"-e",	TO_FILEXST },
+	{"-f",	TO_FILREG },
+	{"-G",	TO_FILGID },
+	{"-g",	TO_FILSETG },
+	{"-h",	TO_FILSYM },
+	{"-H",	TO_FILCDF },
+	{"-k",	TO_FILSTCK },
+	{"-L",	TO_FILSYM },
+	{"-n",	TO_STNZE },
+	{"-O",	TO_FILUID },
+	{"-o",	TO_OPTION },
+	{"-p",	TO_FILFIFO },
+	{"-r",	TO_FILRD },
+	{"-s",	TO_FILGZ },
+	{"-S",	TO_FILSOCK },
+	{"-t",	TO_FILTT },
+	{"-u",	TO_FILSETU },
+	{"-w",	TO_FILWR },
+	{"-x",	TO_FILEX },
+	{"-z",	TO_STZER },
+	{"",	TO_NONOP }
+};
+static const struct t_op b_ops[] = {
+	{"=",	TO_STEQL },
+	{"==",	TO_STEQL },
+	{"!=",	TO_STNEQ },
+	{"<",	TO_STLT },
+	{">",	TO_STGT },
+	{"-eq",	TO_INTEQ },
+	{"-ne",	TO_INTNE },
+	{"-gt",	TO_INTGT },
+	{"-ge",	TO_INTGE },
+	{"-lt",	TO_INTLT },
+	{"-le",	TO_INTLE },
+	{"-ef",	TO_FILEQ },
+	{"-nt",	TO_FILNT },
+	{"-ot",	TO_FILOT },
+	{"",	TO_NONOP }
+};
+
+static int test_oexpr(Test_env *, bool);
+static int test_aexpr(Test_env *, bool);
+static int test_nexpr(Test_env *, bool);
+static int test_primary(Test_env *, bool);
+static Test_op ptest_isa(Test_env *, Test_meta);
+static const char *ptest_getopnd(Test_env *, Test_op, bool);
+static void ptest_error(Test_env *, int, const char *);
+static char *kill_fmt_entry(char *, size_t, unsigned int, const void *);
+static void p_time(struct shf *, bool, long, int, int,
+    const char *, const char *);
+
+int
+c_pwd(const char **wp)
+{
+	int optc;
+	bool physical = tobool(Flag(FPHYSICAL));
+	char *p, *allocd = NULL;
+
+	while ((optc = ksh_getopt(wp, &builtin_opt, "LP")) != -1)
+		switch (optc) {
+		case 'L':
+			physical = false;
+			break;
+		case 'P':
+			physical = true;
+			break;
+		case '?':
+			return (1);
+		}
+	wp += builtin_opt.optind;
+
+	if (wp[0]) {
+		bi_errorf("too many arguments");
+		return (1);
+	}
+	p = current_wd[0] ? (physical ? allocd = do_realpath(current_wd) :
+	    current_wd) : NULL;
+	/* LINTED use of access */
+	if (p && access(p, R_OK) < 0)
+		p = NULL;
+	if (!p && !(p = allocd = ksh_get_wd())) {
+		bi_errorf("%s: %s", "can't determine current directory",
+		    cstrerror(errno));
+		return (1);
+	}
+	shprintf("%s\n", p);
+	afree(allocd, ATEMP);
+	return (0);
+}
+
+static const char *s_ptr;
+static int s_get(void);
+static void s_put(int);
+
+int
+c_print(const char **wp)
+{
+#define PO_NL		BIT(0)	/* print newline */
+#define PO_EXPAND	BIT(1)	/* expand backslash sequences */
+#define PO_PMINUSMINUS	BIT(2)	/* print a -- argument */
+#define PO_HIST		BIT(3)	/* print to history instead of stdout */
+#define PO_COPROC	BIT(4)	/* printing to coprocess: block SIGPIPE */
+	int fd = 1, c;
+	int flags = PO_EXPAND | PO_NL;
+	const char *s, *emsg;
+	XString xs;
+	char *xp;
+
+	if (wp[0][0] == 'e') {
+		/* echo builtin */
+		wp++;
+#ifdef MKSH_MIDNIGHTBSD01ASH_COMPAT
+		if (Flag(FSH)) {
+			/*
+			 * MidnightBSD /bin/sh needs a BSD echo, that is,
+			 * one that supports -e but does not enable it by
+			 * default
+			 */
+			flags = PO_NL;
+		}
+#endif
+		if (Flag(FPOSIX) ||
+#ifndef MKSH_MIDNIGHTBSD01ASH_COMPAT
+		    Flag(FSH) ||
+#endif
+		    Flag(FAS_BUILTIN)) {
+			/* Debian Policy 10.4 compliant "echo" builtin */
+			if (*wp && !strcmp(*wp, "-n")) {
+				/* we recognise "-n" only as the first arg */
+				flags = 0;
+				wp++;
+			} else
+				/* otherwise, we print everything as-is */
+				flags = PO_NL;
+		} else {
+			int nflags = flags;
+
+			/**
+			 * a compromise between sysV and BSD echo commands:
+			 * escape sequences are enabled by default, and -n,
+			 * -e and -E are recognised if they appear in argu-
+			 * ments with no illegal options (ie, echo -nq will
+			 * print -nq).
+			 * Different from sysV echo since options are reco-
+			 * gnised, different from BSD echo since escape se-
+			 * quences are enabled by default.
+			 */
+
+			while ((s = *wp) && *s == '-' && s[1]) {
+				while (*++s)
+					if (*s == 'n')
+						nflags &= ~PO_NL;
+					else if (*s == 'e')
+						nflags |= PO_EXPAND;
+					else if (*s == 'E')
+						nflags &= ~PO_EXPAND;
+					else
+						/*
+						 * bad option: don't use
+						 * nflags, print argument
+						 */
+						break;
+
+				if (*s)
+					break;
+				wp++;
+				flags = nflags;
+			}
+		}
+	} else {
+		int optc;
+		const char *opts = "Rnprsu,";
+
+		while ((optc = ksh_getopt(wp, &builtin_opt, opts)) != -1)
+			switch (optc) {
+			case 'R':
+				/* fake BSD echo command */
+				flags |= PO_PMINUSMINUS;
+				flags &= ~PO_EXPAND;
+				opts = "ne";
+				break;
+			case 'e':
+				flags |= PO_EXPAND;
+				break;
+			case 'n':
+				flags &= ~PO_NL;
+				break;
+			case 'p':
+				if ((fd = coproc_getfd(W_OK, &emsg)) < 0) {
+					bi_errorf("%s: %s", "-p", emsg);
+					return (1);
+				}
+				break;
+			case 'r':
+				flags &= ~PO_EXPAND;
+				break;
+			case 's':
+				flags |= PO_HIST;
+				break;
+			case 'u':
+				if (!*(s = builtin_opt.optarg))
+					fd = 0;
+				else if ((fd = check_fd(s, W_OK, &emsg)) < 0) {
+					bi_errorf("%s: %s: %s", "-u", s, emsg);
+					return (1);
+				}
+				break;
+			case '?':
+				return (1);
+			}
+
+		if (!(builtin_opt.info & GI_MINUSMINUS)) {
+			/* treat a lone - like -- */
+			if (wp[builtin_opt.optind] &&
+			    ksh_isdash(wp[builtin_opt.optind]))
+				builtin_opt.optind++;
+		} else if (flags & PO_PMINUSMINUS)
+			builtin_opt.optind--;
+		wp += builtin_opt.optind;
+	}
+
+	Xinit(xs, xp, 128, ATEMP);
+
+	while (*wp != NULL) {
+		s = *wp;
+		while ((c = *s++) != '\0') {
+			Xcheck(xs, xp);
+			if ((flags & PO_EXPAND) && c == '\\') {
+				s_ptr = s;
+				c = unbksl(false, s_get, s_put);
+				s = s_ptr;
+				if (c == -1) {
+					/* rejected by generic function */
+					switch ((c = *s++)) {
+					case 'c':
+						flags &= ~PO_NL;
+						/* AT&T brain damage */
+						continue;
+					case '\0':
+						s--;
+						c = '\\';
+						break;
+					default:
+						Xput(xs, xp, '\\');
+					}
+				} else if ((unsigned int)c > 0xFF) {
+					/* generic function returned Unicode */
+					char ts[4];
+
+					ts[utf_wctomb(ts, c - 0x100)] = 0;
+					for (c = 0; ts[c]; ++c)
+						Xput(xs, xp, ts[c]);
+					continue;
+				}
+			}
+			Xput(xs, xp, c);
+		}
+		if (*++wp != NULL)
+			Xput(xs, xp, ' ');
+	}
+	if (flags & PO_NL)
+		Xput(xs, xp, '\n');
+
+	if (flags & PO_HIST) {
+		Xput(xs, xp, '\0');
+		histsave(&source->line, Xstring(xs, xp), true, false);
+		Xfree(xs, xp);
+	} else {
+		int len = Xlength(xs, xp);
+		int opipe = 0;
+
+		/*
+		 * Ensure we aren't killed by a SIGPIPE while writing to
+		 * a coprocess. AT&T ksh doesn't seem to do this (seems
+		 * to just check that the co-process is alive which is
+		 * not enough).
+		 */
+		if (coproc.write >= 0 && coproc.write == fd) {
+			flags |= PO_COPROC;
+			opipe = block_pipe();
+		}
+		for (s = Xstring(xs, xp); len > 0; ) {
+			if ((c = write(fd, s, len)) < 0) {
+				if (flags & PO_COPROC)
+					restore_pipe(opipe);
+				if (errno == EINTR) {
+					/* allow user to ^C out */
+					intrcheck();
+					if (flags & PO_COPROC)
+						opipe = block_pipe();
+					continue;
+				}
+				return (1);
+			}
+			s += c;
+			len -= c;
+		}
+		if (flags & PO_COPROC)
+			restore_pipe(opipe);
+	}
+
+	return (0);
+}
+
+static int
+s_get(void)
+{
+	return (*s_ptr++);
+}
+
+static void
+s_put(int c MKSH_A_UNUSED)
+{
+	--s_ptr;
+}
+
+int
+c_whence(const char **wp)
+{
+	struct tbl *tp;
+	const char *id;
+	bool pflag = false, vflag = false, Vflag = false;
+	int rv = 0, optc, fcflags;
+	bool iam_whence = wp[0][0] == 'w';
+	const char *opts = iam_whence ? "pv" : "pvV";
+
+	while ((optc = ksh_getopt(wp, &builtin_opt, opts)) != -1)
+		switch (optc) {
+		case 'p':
+			pflag = true;
+			break;
+		case 'v':
+			vflag = true;
+			break;
+		case 'V':
+			Vflag = true;
+			break;
+		case '?':
+			return (1);
+		}
+	wp += builtin_opt.optind;
+
+	fcflags = FC_BI | FC_PATH | FC_FUNC;
+	if (!iam_whence) {
+		/* Note that -p on its own is deal with in comexec() */
+		if (pflag)
+			fcflags |= FC_DEFPATH;
+		/*
+		 * Convert command options to whence options - note that
+		 * command -pV uses a different path search than whence -v
+		 * or whence -pv. This should be considered a feature.
+		 */
+		vflag = Vflag;
+	}
+	if (pflag)
+		fcflags &= ~(FC_BI | FC_FUNC);
+
+	while ((vflag || rv == 0) && (id = *wp++) != NULL) {
+		uint32_t h = 0;
+
+		tp = NULL;
+		if ((iam_whence || vflag) && !pflag)
+			tp = ktsearch(&keywords, id, h = hash(id));
+		if (!tp && !pflag) {
+			tp = ktsearch(&aliases, id, h ? h : hash(id));
+			if (tp && !(tp->flag & ISSET))
+				tp = NULL;
+		}
+		if (!tp)
+			tp = findcom(id, fcflags);
+		if (vflag || (tp->type != CALIAS && tp->type != CEXEC &&
+		    tp->type != CTALIAS))
+			shf_puts(id, shl_stdout);
+		if (vflag)
+			switch (tp->type) {
+			case CKEYWD:
+			case CALIAS:
+			case CFUNC:
+			case CSHELL:
+				shf_puts(" is a", shl_stdout);
+				break;
+			}
+
+		switch (tp->type) {
+		case CKEYWD:
+			if (vflag)
+				shf_puts(" reserved word", shl_stdout);
+			break;
+		case CALIAS:
+			if (vflag)
+				shprintf("n %s%s for ",
+				    (tp->flag & EXPORT) ? "exported " : null,
+				    Talias);
+			if (!iam_whence && !vflag)
+				shprintf("%s %s=", Talias, id);
+			print_value_quoted(shl_stdout, tp->val.s);
+			break;
+		case CFUNC:
+			if (vflag) {
+				if (tp->flag & EXPORT)
+					shf_puts("n exported", shl_stdout);
+				if (tp->flag & TRACE)
+					shf_puts(" traced", shl_stdout);
+				if (!(tp->flag & ISSET)) {
+					shf_puts(" undefined", shl_stdout);
+					if (tp->u.fpath)
+						shprintf(" (autoload from %s)",
+						    tp->u.fpath);
+				}
+				shf_puts(T_function, shl_stdout);
+			}
+			break;
+		case CSHELL:
+			if (vflag)
+				shprintf("%s %s %s",
+				    (tp->flag & SPEC_BI) ? " special" : null,
+				    "shell", Tbuiltin);
+			break;
+		case CTALIAS:
+		case CEXEC:
+			if (tp->flag & ISSET) {
+				if (vflag) {
+					shf_puts(" is ", shl_stdout);
+					if (tp->type == CTALIAS)
+						shprintf("a tracked %s%s for ",
+						    (tp->flag & EXPORT) ?
+						    "exported " : null,
+						    Talias);
+				}
+				shf_puts(tp->val.s, shl_stdout);
+			} else {
+				if (vflag)
+					shprintf(" %s\n", "not found");
+				rv = 1;
+			}
+			break;
+		default:
+			shprintf("%s is *GOK*", id);
+			break;
+		}
+		if (vflag || !rv)
+			shf_putc('\n', shl_stdout);
+	}
+	return (rv);
+}
+
+/* Deal with command -vV - command -p dealt with in comexec() */
+int
+c_command(const char **wp)
+{
+	/*
+	 * Let c_whence do the work. Note that c_command() must be
+	 * a distinct function from c_whence() (tested in comexec()).
+	 */
+	return (c_whence(wp));
+}
+
+/* typeset, global, export, and readonly */
+static void c_typeset_vardump(struct tbl *, uint32_t, int, bool, bool);
+static void c_typeset_vardump_recursive(struct block *, uint32_t, int, bool,
+    bool);
+int
+c_typeset(const char **wp)
+{
+	struct tbl *vp, **p;
+	uint32_t fset = 0, fclr = 0, flag;
+	int thing = 0, field = 0, base = 0, i;
+	struct block *l;
+	const char *opts;
+	const char *fieldstr = NULL, *basestr = NULL;
+	bool localv = false, func = false, pflag = false, istset = true;
+	enum namerefflag new_refflag = SRF_NOP;
+
+	switch (**wp) {
+
+	/* export */
+	case 'e':
+		fset |= EXPORT;
+		istset = false;
+		break;
+
+	/* readonly */
+	case 'r':
+		fset |= RDONLY;
+		istset = false;
+		break;
+
+	/* set */
+	case 's':
+		/* called with 'typeset -' */
+		break;
+
+	/* typeset */
+	case 't':
+		localv = true;
+		break;
+	}
+
+	/* see comment below regarding possible opions */
+	opts = istset ? "L#R#UZ#afi#lnprtux" : "p";
+
+	builtin_opt.flags |= GF_PLUSOPT;
+	/*
+	 * AT&T ksh seems to have 0-9 as options which are multiplied
+	 * to get a number that is used with -L, -R, -Z or -i (eg, -1R2
+	 * sets right justify in a field of 12). This allows options
+	 * to be grouped in an order (eg, -Lu12), but disallows -i8 -L3 and
+	 * does not allow the number to be specified as a separate argument
+	 * Here, the number must follow the RLZi option, but is optional
+	 * (see the # kludge in ksh_getopt()).
+	 */
+	while ((i = ksh_getopt(wp, &builtin_opt, opts)) != -1) {
+		flag = 0;
+		switch (i) {
+		case 'L':
+			flag = LJUST;
+			fieldstr = builtin_opt.optarg;
+			break;
+		case 'R':
+			flag = RJUST;
+			fieldstr = builtin_opt.optarg;
+			break;
+		case 'U':
+			/*
+			 * AT&T ksh uses u, but this conflicts with
+			 * upper/lower case. If this option is changed,
+			 * need to change the -U below as well
+			 */
+			flag = INT_U;
+			break;
+		case 'Z':
+			flag = ZEROFIL;
+			fieldstr = builtin_opt.optarg;
+			break;
+		case 'a':
+			/*
+			 * this is supposed to set (-a) or unset (+a) the
+			 * indexed array attribute; it does nothing on an
+			 * existing regular string or indexed array though
+			 */
+			break;
+		case 'f':
+			func = true;
+			break;
+		case 'i':
+			flag = INTEGER;
+			basestr = builtin_opt.optarg;
+			break;
+		case 'l':
+			flag = LCASEV;
+			break;
+		case 'n':
+			new_refflag = (builtin_opt.info & GI_PLUS) ?
+			    SRF_DISABLE : SRF_ENABLE;
+			break;
+		/* export, readonly: POSIX -p flag */
+		case 'p':
+			/* typeset: show values as well */
+			pflag = true;
+			if (istset)
+				continue;
+			break;
+		case 'r':
+			flag = RDONLY;
+			break;
+		case 't':
+			flag = TRACE;
+			break;
+		case 'u':
+			/* upper case / autoload */
+			flag = UCASEV_AL;
+			break;
+		case 'x':
+			flag = EXPORT;
+			break;
+		case '?':
+			return (1);
+		}
+		if (builtin_opt.info & GI_PLUS) {
+			fclr |= flag;
+			fset &= ~flag;
+			thing = '+';
+		} else {
+			fset |= flag;
+			fclr &= ~flag;
+			thing = '-';
+		}
+	}
+
+	if (fieldstr && !bi_getn(fieldstr, &field))
+		return (1);
+	if (basestr && (!bi_getn(basestr, &base) || base < 1 || base > 36)) {
+		bi_errorf("%s: %s", "bad integer base", basestr);
+		return (1);
+	}
+
+	if (!(builtin_opt.info & GI_MINUSMINUS) && wp[builtin_opt.optind] &&
+	    (wp[builtin_opt.optind][0] == '-' ||
+	    wp[builtin_opt.optind][0] == '+') &&
+	    wp[builtin_opt.optind][1] == '\0') {
+		thing = wp[builtin_opt.optind][0];
+		builtin_opt.optind++;
+	}
+
+	if (func && (((fset|fclr) & ~(TRACE|UCASEV_AL|EXPORT)) ||
+	    new_refflag != SRF_NOP)) {
+		bi_errorf("only -t, -u and -x options may be used with -f");
+		return (1);
+	}
+	if (wp[builtin_opt.optind]) {
+		/*
+		 * Take care of exclusions.
+		 * At this point, flags in fset are cleared in fclr and vice
+		 * versa. This property should be preserved.
+		 */
+		if (fset & LCASEV)
+			/* LCASEV has priority over UCASEV_AL */
+			fset &= ~UCASEV_AL;
+		if (fset & LJUST)
+			/* LJUST has priority over RJUST */
+			fset &= ~RJUST;
+		if ((fset & (ZEROFIL|LJUST)) == ZEROFIL) {
+			/* -Z implies -ZR */
+			fset |= RJUST;
+			fclr &= ~RJUST;
+		}
+		/*
+		 * Setting these attributes clears the others, unless they
+		 * are also set in this command
+		 */
+		if ((fset & (LJUST | RJUST | ZEROFIL | UCASEV_AL | LCASEV |
+		    INTEGER | INT_U | INT_L)) || new_refflag != SRF_NOP)
+			fclr |= ~fset & (LJUST | RJUST | ZEROFIL | UCASEV_AL |
+			    LCASEV | INTEGER | INT_U | INT_L);
+	}
+	if (new_refflag != SRF_NOP) {
+		fclr &= ~(ARRAY | ASSOC);
+		fset &= ~(ARRAY | ASSOC);
+		fclr |= EXPORT;
+		fset |= ASSOC;
+		if (new_refflag == SRF_DISABLE)
+			fclr |= ASSOC;
+	}
+
+	/* set variables and attributes */
+	if (wp[builtin_opt.optind] &&
+	    /* not "typeset -p varname" */
+	    !(!func && pflag && !(fset | fclr))) {
+		int rv = 0;
+		struct tbl *f;
+
+		if (localv && !func)
+			fset |= LOCAL;
+		for (i = builtin_opt.optind; wp[i]; i++) {
+			if (func) {
+				f = findfunc(wp[i], hash(wp[i]),
+				    tobool(fset & UCASEV_AL));
+				if (!f) {
+					/* AT&T ksh does ++rv: bogus */
+					rv = 1;
+					continue;
+				}
+				if (fset | fclr) {
+					f->flag |= fset;
+					f->flag &= ~fclr;
+				} else {
+					fpFUNCTf(shl_stdout, 0,
+					    tobool(f->flag & FKSH),
+					    wp[i], f->val.t);
+					shf_putc('\n', shl_stdout);
+				}
+			} else if (!typeset(wp[i], fset, fclr, field, base)) {
+				bi_errorf("%s: %s", wp[i], "is not an identifier");
+				return (1);
+			}
+		}
+		return (rv);
+	}
+
+	/* list variables and attributes */
+
+	/* no difference at this point.. */
+	flag = fset | fclr;
+	if (func) {
+		for (l = e->loc; l; l = l->next) {
+			for (p = ktsort(&l->funs); (vp = *p++); ) {
+				if (flag && (vp->flag & flag) == 0)
+					continue;
+				if (thing == '-')
+					fpFUNCTf(shl_stdout, 0,
+					    tobool(vp->flag & FKSH),
+					    vp->name, vp->val.t);
+				else
+					shf_puts(vp->name, shl_stdout);
+				shf_putc('\n', shl_stdout);
+			}
+		}
+	} else if (wp[builtin_opt.optind]) {
+		for (i = builtin_opt.optind; wp[i]; i++) {
+			varsearch(e->loc, &vp, wp[i], hash(wp[i]));
+			c_typeset_vardump(vp, flag, thing, pflag, istset);
+		}
+	} else
+		c_typeset_vardump_recursive(e->loc, flag, thing, pflag, istset);
+	return (0);
+}
+
+static void
+c_typeset_vardump_recursive(struct block *l, uint32_t flag, int thing,
+    bool pflag, bool istset)
+{
+	struct tbl **blockvars, *vp;
+
+	if (l->next)
+		c_typeset_vardump_recursive(l->next, flag, thing, pflag, istset);
+	blockvars = ktsort(&l->vars);
+	while ((vp = *blockvars++))
+		c_typeset_vardump(vp, flag, thing, pflag, istset);
+	/*XXX doesn’t this leak? */
+}
+
+static void
+c_typeset_vardump(struct tbl *vp, uint32_t flag, int thing, bool pflag,
+    bool istset)
+{
+	struct tbl *tvp;
+	int any_set = 0;
+	char *s;
+
+	if (!vp)
+		return;
+
+	/*
+	 * See if the parameter is set (for arrays, if any
+	 * element is set).
+	 */
+	for (tvp = vp; tvp; tvp = tvp->u.array)
+		if (tvp->flag & ISSET) {
+			any_set = 1;
+			break;
+		}
+
+	/*
+	 * Check attributes - note that all array elements
+	 * have (should have?) the same attributes, so checking
+	 * the first is sufficient.
+	 *
+	 * Report an unset param only if the user has
+	 * explicitly given it some attribute (like export);
+	 * otherwise, after "echo $FOO", we would report FOO...
+	 */
+	if (!any_set && !(vp->flag & USERATTRIB))
+		return;
+	if (flag && (vp->flag & flag) == 0)
+		return;
+	if (!(vp->flag & ARRAY))
+		/* optimise later conditionals */
+		any_set = 0;
+	do {
+		/*
+		 * Ignore array elements that aren't set unless there
+		 * are no set elements, in which case the first is
+		 * reported on
+		 */
+		if (any_set && !(vp->flag & ISSET))
+			continue;
+		/* no arguments */
+		if (!thing && !flag) {
+			if (any_set == 1) {
+				shprintf("%s %s %s\n", Tset, "-A", vp->name);
+				any_set = 2;
+			}
+			/*
+			 * AT&T ksh prints things like export, integer,
+			 * leftadj, zerofill, etc., but POSIX says must
+			 * be suitable for re-entry...
+			 */
+			shprintf("%s %s", Ttypeset, "");
+			if (((vp->flag & (ARRAY | ASSOC)) == ASSOC))
+				shprintf("%s ", "-n");
+			if ((vp->flag & INTEGER))
+				shprintf("%s ", "-i");
+			if ((vp->flag & EXPORT))
+				shprintf("%s ", "-x");
+			if ((vp->flag & RDONLY))
+				shprintf("%s ", "-r");
+			if ((vp->flag & TRACE))
+				shprintf("%s ", "-t");
+			if ((vp->flag & LJUST))
+				shprintf("-L%d ", vp->u2.field);
+			if ((vp->flag & RJUST))
+				shprintf("-R%d ", vp->u2.field);
+			if ((vp->flag & ZEROFIL))
+				shprintf("%s ", "-Z");
+			if ((vp->flag & LCASEV))
+				shprintf("%s ", "-l");
+			if ((vp->flag & UCASEV_AL))
+				shprintf("%s ", "-u");
+			if ((vp->flag & INT_U))
+				shprintf("%s ", "-U");
+		} else if (pflag) {
+			shprintf("%s %s", istset ? Ttypeset :
+			    (flag & EXPORT) ? Texport : Treadonly, "");
+		}
+		if (any_set)
+			shprintf("%s[%lu]", vp->name, arrayindex(vp));
+		else
+			shf_puts(vp->name, shl_stdout);
+		if ((!thing && !flag && pflag) ||
+		    (thing == '-' && (vp->flag & ISSET))) {
+			s = str_val(vp);
+			shf_putc('=', shl_stdout);
+			/* AT&T ksh can't have justified integers... */
+			if ((vp->flag & (INTEGER | LJUST | RJUST)) == INTEGER)
+				shf_puts(s, shl_stdout);
+			else
+				print_value_quoted(shl_stdout, s);
+		}
+		shf_putc('\n', shl_stdout);
+
+		/*
+		 * Only report first 'element' of an array with
+		 * no set elements.
+		 */
+		if (!any_set)
+			return;
+	} while ((vp = vp->u.array));
+}
+
+int
+c_alias(const char **wp)
+{
+	struct table *t = &aliases;
+	int rv = 0, prefix = 0;
+	bool rflag = false, tflag, Uflag = false, pflag = false;
+	uint32_t xflag = 0;
+	int optc;
+
+	builtin_opt.flags |= GF_PLUSOPT;
+	while ((optc = ksh_getopt(wp, &builtin_opt, "dprtUx")) != -1) {
+		prefix = builtin_opt.info & GI_PLUS ? '+' : '-';
+		switch (optc) {
+		case 'd':
+#ifdef MKSH_NOPWNAM
+			t = NULL;	/* fix "alias -dt" */
+#else
+			t = &homedirs;
+#endif
+			break;
+		case 'p':
+			pflag = true;
+			break;
+		case 'r':
+			rflag = true;
+			break;
+		case 't':
+			t = &taliases;
+			break;
+		case 'U':
+			/*
+			 * kludge for tracked alias initialization
+			 * (don't do a path search, just make an entry)
+			 */
+			Uflag = true;
+			break;
+		case 'x':
+			xflag = EXPORT;
+			break;
+		case '?':
+			return (1);
+		}
+	}
+#ifdef MKSH_NOPWNAM
+	if (t == NULL)
+		return (0);
+#endif
+	wp += builtin_opt.optind;
+
+	if (!(builtin_opt.info & GI_MINUSMINUS) && *wp &&
+	    (wp[0][0] == '-' || wp[0][0] == '+') && wp[0][1] == '\0') {
+		prefix = wp[0][0];
+		wp++;
+	}
+
+	tflag = t == &taliases;
+
+	/* "hash -r" means reset all the tracked aliases.. */
+	if (rflag) {
+		static const char *args[] = {
+			Tunalias, "-ta", NULL
+		};
+
+		if (!tflag || *wp) {
+			shprintf("%s: -r flag can only be used with -t"
+			    " and without arguments\n", Talias);
+			return (1);
+		}
+		ksh_getopt_reset(&builtin_opt, GF_ERROR);
+		return (c_unalias(args));
+	}
+
+	if (*wp == NULL) {
+		struct tbl *ap, **p;
+
+		for (p = ktsort(t); (ap = *p++) != NULL; )
+			if ((ap->flag & (ISSET|xflag)) == (ISSET|xflag)) {
+				if (pflag)
+					shprintf("%s ", Talias);
+				shf_puts(ap->name, shl_stdout);
+				if (prefix != '+') {
+					shf_putc('=', shl_stdout);
+					print_value_quoted(shl_stdout, ap->val.s);
+				}
+				shf_putc('\n', shl_stdout);
+			}
+	}
+
+	for (; *wp != NULL; wp++) {
+		const char *alias = *wp, *val, *newval;
+		char *xalias = NULL;
+		struct tbl *ap;
+		uint32_t h;
+
+		if ((val = cstrchr(alias, '='))) {
+			strndupx(xalias, alias, val++ - alias, ATEMP);
+			alias = xalias;
+		}
+		h = hash(alias);
+		if (val == NULL && !tflag && !xflag) {
+			ap = ktsearch(t, alias, h);
+			if (ap != NULL && (ap->flag&ISSET)) {
+				if (pflag)
+					shprintf("%s ", Talias);
+				shf_puts(ap->name, shl_stdout);
+				if (prefix != '+') {
+					shf_putc('=', shl_stdout);
+					print_value_quoted(shl_stdout, ap->val.s);
+				}
+				shf_putc('\n', shl_stdout);
+			} else {
+				shprintf("%s %s %s\n", alias, Talias,
+				    "not found");
+				rv = 1;
+			}
+			continue;
+		}
+		ap = ktenter(t, alias, h);
+		ap->type = tflag ? CTALIAS : CALIAS;
+		/* Are we setting the value or just some flags? */
+		if ((val && !tflag) || (!val && tflag && !Uflag)) {
+			if (ap->flag&ALLOC) {
+				ap->flag &= ~(ALLOC|ISSET);
+				afree(ap->val.s, APERM);
+			}
+			/* ignore values for -t (AT&T ksh does this) */
+			newval = tflag ?
+			    search_path(alias, path, X_OK, NULL) :
+			    val;
+			if (newval) {
+				strdupx(ap->val.s, newval, APERM);
+				ap->flag |= ALLOC|ISSET;
+			} else
+				ap->flag &= ~ISSET;
+		}
+		ap->flag |= DEFINED;
+		if (prefix == '+')
+			ap->flag &= ~xflag;
+		else
+			ap->flag |= xflag;
+		afree(xalias, ATEMP);
+	}
+
+	return (rv);
+}
+
+int
+c_unalias(const char **wp)
+{
+	struct table *t = &aliases;
+	struct tbl *ap;
+	int optc, rv = 0;
+	bool all = false;
+
+	while ((optc = ksh_getopt(wp, &builtin_opt, "adt")) != -1)
+		switch (optc) {
+		case 'a':
+			all = true;
+			break;
+		case 'd':
+#ifdef MKSH_NOPWNAM
+			/* fix "unalias -dt" */
+			t = NULL;
+#else
+			t = &homedirs;
+#endif
+			break;
+		case 't':
+			t = &taliases;
+			break;
+		case '?':
+			return (1);
+		}
+#ifdef MKSH_NOPWNAM
+	if (t == NULL)
+		return (0);
+#endif
+	wp += builtin_opt.optind;
+
+	for (; *wp != NULL; wp++) {
+		ap = ktsearch(t, *wp, hash(*wp));
+		if (ap == NULL) {
+			/* POSIX */
+			rv = 1;
+			continue;
+		}
+		if (ap->flag&ALLOC) {
+			ap->flag &= ~(ALLOC|ISSET);
+			afree(ap->val.s, APERM);
+		}
+		ap->flag &= ~(DEFINED|ISSET|EXPORT);
+	}
+
+	if (all) {
+		struct tstate ts;
+
+		for (ktwalk(&ts, t); (ap = ktnext(&ts)); ) {
+			if (ap->flag&ALLOC) {
+				ap->flag &= ~(ALLOC|ISSET);
+				afree(ap->val.s, APERM);
+			}
+			ap->flag &= ~(DEFINED|ISSET|EXPORT);
+		}
+	}
+
+	return (rv);
+}
+
+int
+c_let(const char **wp)
+{
+	int rv = 1;
+	mksh_ari_t val;
+
+	if (wp[1] == NULL)
+		/* AT&T ksh does this */
+		bi_errorf("no arguments");
+	else
+		for (wp++; *wp; wp++)
+			if (!evaluate(*wp, &val, KSH_RETURN_ERROR, true)) {
+				/* distinguish error from zero result */
+				rv = 2;
+				break;
+			} else
+				rv = val == 0;
+	return (rv);
+}
+
+int
+c_jobs(const char **wp)
+{
+	int optc, flag = 0, nflag = 0, rv = 0;
+
+	while ((optc = ksh_getopt(wp, &builtin_opt, "lpnz")) != -1)
+		switch (optc) {
+		case 'l':
+			flag = 1;
+			break;
+		case 'p':
+			flag = 2;
+			break;
+		case 'n':
+			nflag = 1;
+			break;
+		case 'z':
+			/* debugging: print zombies */
+			nflag = -1;
+			break;
+		case '?':
+			return (1);
+		}
+	wp += builtin_opt.optind;
+	if (!*wp) {
+		if (j_jobs(NULL, flag, nflag))
+			rv = 1;
+	} else {
+		for (; *wp; wp++)
+			if (j_jobs(*wp, flag, nflag))
+				rv = 1;
+	}
+	return (rv);
+}
+
+#ifndef MKSH_UNEMPLOYED
+int
+c_fgbg(const char **wp)
+{
+	bool bg = strcmp(*wp, "bg") == 0;
+	int rv = 0;
+
+	if (!Flag(FMONITOR)) {
+		bi_errorf("job control not enabled");
+		return (1);
+	}
+	if (ksh_getopt(wp, &builtin_opt, null) == '?')
+		return (1);
+	wp += builtin_opt.optind;
+	if (*wp)
+		for (; *wp; wp++)
+			rv = j_resume(*wp, bg);
+	else
+		rv = j_resume("%%", bg);
+	return (bg ? 0 : rv);
+}
+#endif
+
+/* format a single kill item */
+static char *
+kill_fmt_entry(char *buf, size_t buflen, unsigned int i, const void *arg)
+{
+	const struct kill_info *ki = (const struct kill_info *)arg;
+
+	i++;
+	shf_snprintf(buf, buflen, "%*u %*s %s",
+	    ki->num_width, i,
+	    ki->name_width, sigtraps[i].name,
+	    sigtraps[i].mess);
+	return (buf);
+}
+
+int
+c_kill(const char **wp)
+{
+	Trap *t = NULL;
+	const char *p;
+	bool lflag = false;
+	int i, n, rv, sig;
+
+	/* assume old style options if -digits or -UPPERCASE */
+	if ((p = wp[1]) && *p == '-' && (ksh_isdigit(p[1]) ||
+	    ksh_isupper(p[1]))) {
+		if (!(t = gettrap(p + 1, false))) {
+			bi_errorf("bad signal '%s'", p + 1);
+			return (1);
+		}
+		i = (wp[2] && strcmp(wp[2], "--") == 0) ? 3 : 2;
+	} else {
+		int optc;
+
+		while ((optc = ksh_getopt(wp, &builtin_opt, "ls:")) != -1)
+			switch (optc) {
+			case 'l':
+				lflag = true;
+				break;
+			case 's':
+				if (!(t = gettrap(builtin_opt.optarg, true))) {
+					bi_errorf("bad signal '%s'",
+					    builtin_opt.optarg);
+					return (1);
+				}
+				break;
+			case '?':
+				return (1);
+			}
+		i = builtin_opt.optind;
+	}
+	if ((lflag && t) || (!wp[i] && !lflag)) {
+#ifndef MKSH_SMALL
+		shf_puts("usage:\tkill [-s signame | -signum | -signame]"
+		    " { job | pid | pgrp } ...\n"
+		    "\tkill -l [exit_status ...]\n", shl_out);
+#endif
+		bi_errorfz();
+		return (1);
+	}
+
+	if (lflag) {
+		if (wp[i]) {
+			for (; wp[i]; i++) {
+				if (!bi_getn(wp[i], &n))
+					return (1);
+#if (NSIG < 128)
+				if (n > 128 && n < 128 + NSIG)
+					n -= 128;
+#endif
+				if (n > 0 && n < NSIG)
+					shprintf("%s\n", sigtraps[n].name);
+				else
+					shprintf("%d\n", n);
+			}
+		} else {
+			ssize_t w, mess_cols, mess_octs;
+			int j;
+			struct kill_info ki;
+
+			for (j = NSIG, ki.num_width = 1; j >= 10; j /= 10)
+				ki.num_width++;
+			ki.name_width = mess_cols = mess_octs = 0;
+			for (j = 0; j < NSIG; j++) {
+				w = strlen(sigtraps[j].name);
+				if (w > ki.name_width)
+					ki.name_width = w;
+				w = strlen(sigtraps[j].mess);
+				if (w > mess_octs)
+					mess_octs = w;
+				w = utf_mbswidth(sigtraps[j].mess);
+				if (w > mess_cols)
+					mess_cols = w;
+			}
+
+			print_columns(shl_stdout, (unsigned int)(NSIG - 1),
+			    kill_fmt_entry, (void *)&ki,
+			    ki.num_width + 1 + ki.name_width + 1 + mess_octs,
+			    ki.num_width + 1 + ki.name_width + 1 + mess_cols,
+			    true);
+		}
+		return (0);
+	}
+	rv = 0;
+	sig = t ? t->signal : SIGTERM;
+	for (; (p = wp[i]); i++) {
+		if (*p == '%') {
+			if (j_kill(p, sig))
+				rv = 1;
+		} else if (!getn(p, &n)) {
+			bi_errorf("%s: %s", p,
+			    "arguments must be jobs or process IDs");
+			rv = 1;
+		} else {
+			if (mksh_kill(n, sig) < 0) {
+				bi_errorf("%s: %s", p, cstrerror(errno));
+				rv = 1;
+			}
+		}
+	}
+	return (rv);
+}
+
+void
+getopts_reset(int val)
+{
+	if (val >= 1) {
+		ksh_getopt_reset(&user_opt, GF_NONAME | GF_PLUSOPT);
+		user_opt.optind = user_opt.uoptind = val;
+	}
+}
+
+int
+c_getopts(const char **wp)
+{
+	int argc, optc, rv;
+	const char *opts, *var;
+	char buf[3];
+	struct tbl *vq, *voptarg;
+
+	if (ksh_getopt(wp, &builtin_opt, null) == '?')
+		return (1);
+	wp += builtin_opt.optind;
+
+	opts = *wp++;
+	if (!opts) {
+		bi_errorf("missing %s argument", "options");
+		return (1);
+	}
+
+	var = *wp++;
+	if (!var) {
+		bi_errorf("missing %s argument", "name");
+		return (1);
+	}
+	if (!*var || *skip_varname(var, true)) {
+		bi_errorf("%s: %s", var, "is not an identifier");
+		return (1);
+	}
+
+	if (e->loc->next == NULL) {
+		internal_warningf("%s: %s", "c_getopts", "no argv");
+		return (1);
+	}
+	/* Which arguments are we parsing... */
+	if (*wp == NULL)
+		wp = e->loc->next->argv;
+	else
+		*--wp = e->loc->next->argv[0];
+
+	/* Check that our saved state won't cause a core dump... */
+	for (argc = 0; wp[argc]; argc++)
+		;
+	if (user_opt.optind > argc ||
+	    (user_opt.p != 0 &&
+	    user_opt.p > strlen(wp[user_opt.optind - 1]))) {
+		bi_errorf("arguments changed since last call");
+		return (1);
+	}
+
+	user_opt.optarg = NULL;
+	optc = ksh_getopt(wp, &user_opt, opts);
+
+	if (optc >= 0 && optc != '?' && (user_opt.info & GI_PLUS)) {
+		buf[0] = '+';
+		buf[1] = optc;
+		buf[2] = '\0';
+	} else {
+		/*
+		 * POSIX says var is set to ? at end-of-options, AT&T ksh
+		 * sets it to null - we go with POSIX...
+		 */
+		buf[0] = optc < 0 ? '?' : optc;
+		buf[1] = '\0';
+	}
+
+	/* AT&T ksh93 in fact does change OPTIND for unknown options too */
+	user_opt.uoptind = user_opt.optind;
+
+	voptarg = global("OPTARG");
+	/* AT&T ksh clears ro and int */
+	voptarg->flag &= ~RDONLY;
+	/* Paranoia: ensure no bizarre results. */
+	if (voptarg->flag & INTEGER)
+	    typeset("OPTARG", 0, INTEGER, 0, 0);
+	if (user_opt.optarg == NULL)
+		unset(voptarg, 1);
+	else
+		/* This can't fail (have cleared readonly/integer) */
+		setstr(voptarg, user_opt.optarg, KSH_RETURN_ERROR);
+
+	rv = 0;
+
+	vq = global(var);
+	/* Error message already printed (integer, readonly) */
+	if (!setstr(vq, buf, KSH_RETURN_ERROR))
+		rv = 2;
+	if (Flag(FEXPORT))
+		typeset(var, EXPORT, 0, 0, 0);
+
+	return (optc < 0 ? 1 : rv);
+}
+
+#ifndef MKSH_NO_CMDLINE_EDITING
+int
+c_bind(const char **wp)
+{
+	int optc, rv = 0;
+#ifndef MKSH_SMALL
+	bool macro = false;
+#endif
+	bool list = false;
+	const char *cp;
+	char *up;
+
+	while ((optc = ksh_getopt(wp, &builtin_opt,
+#ifndef MKSH_SMALL
+	    "lm"
+#else
+	    "l"
+#endif
+	    )) != -1)
+		switch (optc) {
+		case 'l':
+			list = true;
+			break;
+#ifndef MKSH_SMALL
+		case 'm':
+			macro = true;
+			break;
+#endif
+		case '?':
+			return (1);
+		}
+	wp += builtin_opt.optind;
+
+	if (*wp == NULL)
+		/* list all */
+		rv = x_bind(NULL, NULL,
+#ifndef MKSH_SMALL
+		    false,
+#endif
+		    list);
+
+	for (; *wp != NULL; wp++) {
+		if ((cp = cstrchr(*wp, '=')) == NULL)
+			up = NULL;
+		else {
+			strdupx(up, *wp, ATEMP);
+			up[cp++ - *wp] = '\0';
+		}
+		if (x_bind(up ? up : *wp, cp,
+#ifndef MKSH_SMALL
+		    macro,
+#endif
+		    false))
+			rv = 1;
+		afree(up, ATEMP);
+	}
+
+	return (rv);
+}
+#endif
+
+int
+c_shift(const char **wp)
+{
+	struct block *l = e->loc;
+	int n;
+	mksh_ari_t val;
+	const char *arg;
+
+	if (ksh_getopt(wp, &builtin_opt, null) == '?')
+		return (1);
+	arg = wp[builtin_opt.optind];
+
+	if (arg) {
+		evaluate(arg, &val, KSH_UNWIND_ERROR, false);
+		n = val;
+	} else
+		n = 1;
+	if (n < 0) {
+		bi_errorf("%s: %s", arg, "bad number");
+		return (1);
+	}
+	if (l->argc < n) {
+		bi_errorf("nothing to shift");
+		return (1);
+	}
+	l->argv[n] = l->argv[0];
+	l->argv += n;
+	l->argc -= n;
+	return (0);
+}
+
+int
+c_umask(const char **wp)
+{
+	int i, optc;
+	const char *cp;
+	bool symbolic = false;
+	mode_t old_umask;
+
+	while ((optc = ksh_getopt(wp, &builtin_opt, "S")) != -1)
+		switch (optc) {
+		case 'S':
+			symbolic = true;
+			break;
+		case '?':
+			return (1);
+		}
+	cp = wp[builtin_opt.optind];
+	if (cp == NULL) {
+		old_umask = umask((mode_t)0);
+		umask(old_umask);
+		if (symbolic) {
+			char buf[18], *p;
+			int j;
+
+			old_umask = ~old_umask;
+			p = buf;
+			for (i = 0; i < 3; i++) {
+				*p++ = "ugo"[i];
+				*p++ = '=';
+				for (j = 0; j < 3; j++)
+					if (old_umask & (1 << (8 - (3*i + j))))
+						*p++ = "rwx"[j];
+				*p++ = ',';
+			}
+			p[-1] = '\0';
+			shprintf("%s\n", buf);
+		} else
+			shprintf("%#3.3o\n", (unsigned int)old_umask);
+	} else {
+		mode_t new_umask;
+
+		if (ksh_isdigit(*cp)) {
+			for (new_umask = 0; *cp >= '0' && *cp <= '7'; cp++)
+				new_umask = new_umask * 8 + (*cp - '0');
+			if (*cp) {
+				bi_errorf("bad number");
+				return (1);
+			}
+		} else {
+			/* symbolic format */
+			int positions, new_val;
+			char op;
+
+			old_umask = umask((mode_t)0);
+			/* in case of error */
+			umask(old_umask);
+			old_umask = ~old_umask;
+			new_umask = old_umask;
+			positions = 0;
+			while (*cp) {
+				while (*cp && vstrchr("augo", *cp))
+					switch (*cp++) {
+					case 'a':
+						positions |= 0111;
+						break;
+					case 'u':
+						positions |= 0100;
+						break;
+					case 'g':
+						positions |= 0010;
+						break;
+					case 'o':
+						positions |= 0001;
+						break;
+					}
+				if (!positions)
+					/* default is a */
+					positions = 0111;
+				if (!vstrchr("=+-", op = *cp))
+					break;
+				cp++;
+				new_val = 0;
+				while (*cp && vstrchr("rwxugoXs", *cp))
+					switch (*cp++) {
+					case 'r': new_val |= 04; break;
+					case 'w': new_val |= 02; break;
+					case 'x': new_val |= 01; break;
+					case 'u':
+						new_val |= old_umask >> 6;
+						break;
+					case 'g':
+						new_val |= old_umask >> 3;
+						break;
+					case 'o':
+						new_val |= old_umask >> 0;
+						break;
+					case 'X':
+						if (old_umask & 0111)
+							new_val |= 01;
+						break;
+					case 's':
+						/* ignored */
+						break;
+					}
+				new_val = (new_val & 07) * positions;
+				switch (op) {
+				case '-':
+					new_umask &= ~new_val;
+					break;
+				case '=':
+					new_umask = new_val |
+					    (new_umask & ~(positions * 07));
+					break;
+				case '+':
+					new_umask |= new_val;
+				}
+				if (*cp == ',') {
+					positions = 0;
+					cp++;
+				} else if (!vstrchr("=+-", *cp))
+					break;
+			}
+			if (*cp) {
+				bi_errorf("bad mask");
+				return (1);
+			}
+			new_umask = ~new_umask;
+		}
+		umask(new_umask);
+	}
+	return (0);
+}
+
+int
+c_dot(const char **wp)
+{
+	const char *file, *cp, **argv;
+	int argc, i, errcode;
+
+	if (ksh_getopt(wp, &builtin_opt, null) == '?')
+		return (1);
+
+	if ((cp = wp[builtin_opt.optind]) == NULL) {
+		bi_errorf("missing argument");
+		return (1);
+	}
+	if ((file = search_path(cp, path, R_OK, &errcode)) == NULL) {
+		bi_errorf("%s: %s", cp, cstrerror(errcode));
+		return (1);
+	}
+
+	/* Set positional parameters? */
+	if (wp[builtin_opt.optind + 1]) {
+		argv = wp + builtin_opt.optind;
+		/* preserve $0 */
+		argv[0] = e->loc->argv[0];
+		for (argc = 0; argv[argc + 1]; argc++)
+			;
+	} else {
+		argc = 0;
+		argv = NULL;
+	}
+	if ((i = include(file, argc, argv, false)) < 0) {
+		/* should not happen */
+		bi_errorf("%s: %s", cp, cstrerror(errno));
+		return (1);
+	}
+	return (i);
+}
+
+int
+c_wait(const char **wp)
+{
+	int rv = 0, sig;
+
+	if (ksh_getopt(wp, &builtin_opt, null) == '?')
+		return (1);
+	wp += builtin_opt.optind;
+	if (*wp == NULL) {
+		while (waitfor(NULL, &sig) >= 0)
+			;
+		rv = sig;
+	} else {
+		for (; *wp; wp++)
+			rv = waitfor(*wp, &sig);
+		if (rv < 0)
+			/* magic exit code: bad job-id */
+			rv = sig ? sig : 127;
+	}
+	return (rv);
+}
+
+static char REPLY[] = "REPLY";
+int
+c_read(const char **wp)
+{
+#define is_ifsws(c) (ctype((c), C_IFS) && ctype((c), C_IFSWS))
+	int c, fd = 0, rv = 0, lastparm = 0;
+	bool savehist = false, intoarray = false, aschars = false;
+	bool rawmode = false, expanding = false;
+	enum { LINES, BYTES, UPTO, READALL } readmode = LINES;
+	char delim = '\n';
+	size_t bytesleft = 128, bytesread;
+	struct tbl *vp /* FU gcc */ = NULL, *vq;
+	char *cp, *allocd = NULL, *xp;
+	const char *ccp;
+	XString xs;
+	ptrdiff_t xsave = 0;
+	mksh_ttyst tios;
+	bool restore_tios = false;
+#if HAVE_SELECT
+	bool hastimeout = false;
+	struct timeval tv, tvlim;
+#define c_read_opts "Aad:N:n:prst:u,"
+#else
+#define c_read_opts "Aad:N:n:prsu,"
+#endif
+
+	while ((c = ksh_getopt(wp, &builtin_opt, c_read_opts)) != -1)
+	switch (c) {
+	case 'a':
+		aschars = true;
+		/* FALLTHROUGH */
+	case 'A':
+		intoarray = true;
+		break;
+	case 'd':
+		delim = builtin_opt.optarg[0];
+		break;
+	case 'N':
+	case 'n':
+		readmode = c == 'N' ? BYTES : UPTO;
+		if (!bi_getn(builtin_opt.optarg, &c))
+			return (2);
+		if (c == -1) {
+			readmode = READALL;
+			bytesleft = 1024;
+		} else
+			bytesleft = (unsigned int)c;
+		break;
+	case 'p':
+		if ((fd = coproc_getfd(R_OK, &ccp)) < 0) {
+			bi_errorf("%s: %s", "-p", ccp);
+			return (2);
+		}
+		break;
+	case 'r':
+		rawmode = true;
+		break;
+	case 's':
+		savehist = true;
+		break;
+#if HAVE_SELECT
+	case 't':
+		if (parse_usec(builtin_opt.optarg, &tv)) {
+			bi_errorf("%s: %s '%s'", Tsynerr, cstrerror(errno),
+			    builtin_opt.optarg);
+			return (2);
+		}
+		hastimeout = true;
+		break;
+#endif
+	case 'u':
+		if (!builtin_opt.optarg[0])
+			fd = 0;
+		else if ((fd = check_fd(builtin_opt.optarg, R_OK, &ccp)) < 0) {
+			bi_errorf("%s: %s: %s", "-u", builtin_opt.optarg, ccp);
+			return (2);
+		}
+		break;
+	case '?':
+		return (2);
+	}
+	wp += builtin_opt.optind;
+	if (*wp == NULL)
+		*--wp = REPLY;
+
+	if (intoarray && wp[1] != NULL) {
+		bi_errorf("too many arguments");
+		return (2);
+	}
+
+	if ((ccp = cstrchr(*wp, '?')) != NULL) {
+		strdupx(allocd, *wp, ATEMP);
+		allocd[ccp - *wp] = '\0';
+		*wp = allocd;
+		if (isatty(fd)) {
+			/*
+			 * AT&T ksh says it prints prompt on fd if it's open
+			 * for writing and is a tty, but it doesn't do it
+			 * (it also doesn't check the interactive flag,
+			 * as is indicated in the Korn Shell book).
+			 */
+			shf_puts(ccp + 1, shl_out);
+			shf_flush(shl_out);
+		}
+	}
+
+	Xinit(xs, xp, bytesleft, ATEMP);
+
+	if (readmode == LINES)
+		bytesleft = 1;
+	else if (isatty(fd)) {
+		x_mkraw(fd, &tios, true);
+		restore_tios = true;
+	}
+
+#if HAVE_SELECT
+	if (hastimeout) {
+		mksh_TIME(tvlim);
+		timeradd(&tvlim, &tv, &tvlim);
+	}
+#endif
+
+ c_read_readloop:
+#if HAVE_SELECT
+	if (hastimeout) {
+		fd_set fdset;
+
+		FD_ZERO(&fdset);
+		FD_SET((unsigned int)fd, &fdset);
+		mksh_TIME(tv);
+		timersub(&tvlim, &tv, &tv);
+		if (tv.tv_sec < 0) {
+			/* timeout expired globally */
+			rv = 1;
+			goto c_read_out;
+		}
+
+		switch (select(fd + 1, &fdset, NULL, NULL, &tv)) {
+		case 1:
+			break;
+		case 0:
+			/* timeout expired for this call */
+			rv = 1;
+			goto c_read_out;
+		default:
+			bi_errorf("%s: %s", Tselect, cstrerror(errno));
+			rv = 2;
+			goto c_read_out;
+		}
+	}
+#endif
+
+	bytesread = blocking_read(fd, xp, bytesleft);
+	if (bytesread == (size_t)-1) {
+		/* interrupted */
+		if (errno == EINTR && fatal_trap_check()) {
+			/*
+			 * Was the offending signal one that would
+			 * normally kill a process? If so, pretend
+			 * the read was killed.
+			 */
+			rv = 2;
+			goto c_read_out;
+		}
+		/* just ignore the signal */
+		goto c_read_readloop;
+	}
+
+	switch (readmode) {
+	case READALL:
+		if (bytesread == 0) {
+			/* end of file reached */
+			rv = 1;
+			goto c_read_readdone;
+		}
+		xp += bytesread;
+		XcheckN(xs, xp, bytesleft);
+		break;
+
+	case UPTO:
+		if (bytesread == 0)
+			/* end of file reached */
+			rv = 1;
+		xp += bytesread;
+		goto c_read_readdone;
+
+	case BYTES:
+		if (bytesread == 0) {
+			/* end of file reached */
+			rv = 1;
+			xp = Xstring(xs, xp);
+			goto c_read_readdone;
+		}
+		xp += bytesread;
+		if ((bytesleft -= bytesread) == 0)
+			goto c_read_readdone;
+		break;
+	case LINES:
+		if (bytesread == 0) {
+			/* end of file reached */
+			rv = 1;
+			goto c_read_readdone;
+		}
+		if ((c = *xp) == '\0' && !aschars && delim != '\0') {
+			/* skip any read NULs unless delimiter */
+			break;
+		}
+		if (expanding) {
+			expanding = false;
+			if (c == delim) {
+				if (Flag(FTALKING_I) && isatty(fd)) {
+					/*
+					 * set prompt in case this is
+					 * called from .profile or $ENV
+					 */
+					set_prompt(PS2, NULL);
+					pprompt(prompt, 0);
+				}
+				/* drop the backslash */
+				--xp;
+				/* and the delimiter */
+				break;
+			}
+		} else if (c == delim) {
+			goto c_read_readdone;
+		} else if (!rawmode && c == '\\') {
+			expanding = true;
+		}
+		Xcheck(xs, xp);
+		++xp;
+		break;
+	}
+	goto c_read_readloop;
+
+ c_read_readdone:
+	bytesread = Xlength(xs, xp);
+	Xput(xs, xp, '\0');
+
+	/*-
+	 * state: we finished reading the input and NUL terminated it
+	 * Xstring(xs, xp) -> xp-1 = input string without trailing delim
+	 * rv = 1 if EOF, 0 otherwise (errors handled already)
+	 */
+
+	if (rv == 1) {
+		/* clean up coprocess if needed, on EOF */
+		coproc_read_close(fd);
+		if (readmode == READALL)
+			/* EOF is no error here */
+			rv = 0;
+	}
+
+	if (savehist)
+		histsave(&source->line, Xstring(xs, xp), true, false);
+
+	ccp = cp = Xclose(xs, xp);
+	expanding = false;
+	XinitN(xs, 128, ATEMP);
+	if (intoarray) {
+		vp = global(*wp);
+		if (vp->flag & RDONLY) {
+ c_read_splitro:
+			bi_errorf("read-only: %s", *wp);
+ c_read_spliterr:
+			rv = 2;
+			afree(cp, ATEMP);
+			goto c_read_out;
+		}
+		/* exporting an array is currently pointless */
+		unset(vp, 1);
+		/* counter for array index */
+		c = 0;
+	}
+	if (!aschars) {
+		/* skip initial IFS whitespace */
+		while (bytesread && is_ifsws(*ccp)) {
+			++ccp;
+			--bytesread;
+		}
+		/* trim trailing IFS whitespace */
+		while (bytesread && is_ifsws(ccp[bytesread - 1])) {
+			--bytesread;
+		}
+	}
+ c_read_splitloop:
+	xp = Xstring(xs, xp);
+	/* generate next word */
+	if (!bytesread) {
+		/* no more input */
+		if (intoarray)
+			goto c_read_splitdone;
+		/* zero out next parameters */
+		goto c_read_gotword;
+	}
+	if (aschars) {
+		Xput(xs, xp, '1');
+		Xput(xs, xp, '#');
+		bytesleft = utf_ptradj(ccp);
+		while (bytesleft && bytesread) {
+			*xp++ = *ccp++;
+			--bytesleft;
+			--bytesread;
+		}
+		if (xp[-1] == '\0') {
+			xp[-1] = '0';
+			xp[-3] = '2';
+		}
+		goto c_read_gotword;
+	}
+
+	if (!intoarray && wp[1] == NULL)
+		lastparm = 1;
+
+ c_read_splitlast:
+	/* copy until IFS character */
+	while (bytesread) {
+		char ch;
+
+		ch = *ccp;
+		if (expanding) {
+			expanding = false;
+			goto c_read_splitcopy;
+		} else if (ctype(ch, C_IFS)) {
+			break;
+		} else if (!rawmode && ch == '\\') {
+			expanding = true;
+		} else {
+ c_read_splitcopy:
+			Xcheck(xs, xp);
+			Xput(xs, xp, ch);
+		}
+		++ccp;
+		--bytesread;
+	}
+	xsave = Xsavepos(xs, xp);
+	/* copy word delimiter: IFSWS+IFS,IFSWS */
+	while (bytesread) {
+		char ch;
+
+		ch = *ccp;
+		if (!ctype(ch, C_IFS))
+			break;
+		Xcheck(xs, xp);
+		Xput(xs, xp, ch);
+		++ccp;
+		--bytesread;
+		if (!ctype(ch, C_IFSWS))
+			break;
+	}
+	while (bytesread && is_ifsws(*ccp)) {
+		Xcheck(xs, xp);
+		Xput(xs, xp, *ccp);
+		++ccp;
+		--bytesread;
+	}
+	/* if no more parameters, rinse and repeat */
+	if (lastparm && bytesread) {
+		++lastparm;
+		goto c_read_splitlast;
+	}
+	/* get rid of the delimiter unless we pack the rest */
+	if (lastparm < 2)
+		xp = Xrestpos(xs, xp, xsave);
+ c_read_gotword:
+	Xput(xs, xp, '\0');
+	if (intoarray) {
+		vq = arraysearch(vp, c++);
+	} else {
+		vq = global(*wp);
+		/* must be checked before exporting */
+		if (vq->flag & RDONLY)
+			goto c_read_splitro;
+		if (Flag(FEXPORT))
+			typeset(*wp, EXPORT, 0, 0, 0);
+	}
+	if (!setstr(vq, Xstring(xs, xp), KSH_RETURN_ERROR))
+		goto c_read_spliterr;
+	if (aschars) {
+		setint_v(vq, vq, false);
+		/* protect from UTFMODE changes */
+		vq->type = 0;
+	}
+	if (intoarray || *++wp != NULL)
+		goto c_read_splitloop;
+
+ c_read_splitdone:
+	/* free up */
+	afree(cp, ATEMP);
+
+ c_read_out:
+	afree(allocd, ATEMP);
+	Xfree(xs, xp);
+	if (restore_tios)
+		mksh_tcset(fd, &tios);
+	return (rv);
+#undef is_ifsws
+}
+
+int
+c_eval(const char **wp)
+{
+	struct source *s, *saves = source;
+	unsigned char savef;
+	int rv;
+
+	if (ksh_getopt(wp, &builtin_opt, null) == '?')
+		return (1);
+	s = pushs(SWORDS, ATEMP);
+	s->u.strv = wp + builtin_opt.optind;
+
+	/*-
+	 * The following code handles the case where the command is
+	 * empty due to failed command substitution, for example by
+	 *	eval "$(false)"
+	 * This has historically returned 1 by AT&T ksh88. In this
+	 * case, shell() will not set or change exstat because the
+	 * compiled tree is empty, so it will use the value we pass
+	 * from subst_exstat, which is cleared in execute(), so it
+	 * should have been 0 if there were no substitutions.
+	 *
+	 * POSIX however says we don't do this, even though it is
+	 * traditionally done. AT&T ksh93 agrees with POSIX, so we
+	 * do. The following is an excerpt from SUSv4 [1003.2-2008]:
+	 *
+	 * 2.9.1: Simple Commands
+	 *	... If there is a command name, execution shall
+	 *	continue as described in 2.9.1.1 [Command Search
+	 *	and Execution]. If there is no command name, but
+	 *	the command contained a command substitution, the
+	 *	command shall complete with the exit status of the
+	 *	last command substitution performed.
+	 * 2.9.1.1: Command Search and Execution
+	 *	(1) a. If the command name matches the name of a
+	 *	special built-in utility, that special built-in
+	 *	utility shall be invoked.
+	 * 2.14.5: eval
+	 *	If there are no arguments, or only null arguments,
+	 *	eval shall return a zero exit status; ...
+	 */
+	/* AT&T ksh88: use subst_exstat */
+	/* exstat = subst_exstat; */
+	/* SUSv4: OR with a high value never written otherwise */
+	exstat |= 0x4000;
+
+	savef = Flag(FERREXIT);
+	Flag(FERREXIT) |= 0x80;
+	rv = shell(s, false);
+	Flag(FERREXIT) = savef;
+	source = saves;
+	afree(s, ATEMP);
+	if (exstat & 0x4000)
+		/* detect old exstat, use 0 in that case */
+		rv = 0;
+	return (rv);
+}
+
+int
+c_trap(const char **wp)
+{
+	int i;
+	const char *s;
+	Trap *p;
+
+	if (ksh_getopt(wp, &builtin_opt, null) == '?')
+		return (1);
+	wp += builtin_opt.optind;
+
+	if (*wp == NULL) {
+		for (p = sigtraps, i = NSIG + 1; --i >= 0; p++)
+			if (p->trap != NULL) {
+				shf_puts("trap -- ", shl_stdout);
+				print_value_quoted(shl_stdout, p->trap);
+				shprintf(" %s\n", p->name);
+			}
+		return (0);
+	}
+
+	/*
+	 * Use case sensitive lookup for first arg so the
+	 * command 'exit' isn't confused with the pseudo-signal
+	 * 'EXIT'.
+	 */
+	/* get command */
+	s = (gettrap(*wp, false) == NULL) ? *wp++ : NULL;
+	if (s != NULL && s[0] == '-' && s[1] == '\0')
+		s = NULL;
+
+	/* set/clear traps */
+	i = 0;
+	while (*wp != NULL)
+		if ((p = gettrap(*wp++, true)) == NULL) {
+			warningf(true, "%s: %s '%s'", builtin_argv0,
+			    "bad signal", wp[-1]);
+			++i;
+		} else
+			settrap(p, s);
+	return (i);
+}
+
+int
+c_exitreturn(const char **wp)
+{
+	int n, how = LEXIT;
+	const char *arg;
+
+	if (ksh_getopt(wp, &builtin_opt, null) == '?')
+		goto c_exitreturn_err;
+	arg = wp[builtin_opt.optind];
+
+	if (arg) {
+		if (!getn(arg, &n)) {
+			exstat = 1;
+			warningf(true, "%s: %s", arg, "bad number");
+		} else
+			exstat = n & 0xFF;
+	} else if (trap_exstat != -1)
+		exstat = trap_exstat;
+	if (wp[0][0] == 'r') {
+		/* return */
+		struct env *ep;
+
+		/*
+		 * need to tell if this is exit or return so trap exit will
+		 * work right (POSIX)
+		 */
+		for (ep = e; ep; ep = ep->oenv)
+			if (STOP_RETURN(ep->type)) {
+				how = LRETURN;
+				break;
+			}
+	}
+
+	if (how == LEXIT && !really_exit && j_stopped_running()) {
+		really_exit = true;
+		how = LSHELL;
+	}
+
+	/* get rid of any i/o redirections */
+	quitenv(NULL);
+	unwind(how);
+	/* NOTREACHED */
+
+ c_exitreturn_err:
+	return (1);
+}
+
+int
+c_brkcont(const char **wp)
+{
+	unsigned int quit;
+	int n;
+	struct env *ep, *last_ep = NULL;
+	const char *arg;
+
+	if (ksh_getopt(wp, &builtin_opt, null) == '?')
+		goto c_brkcont_err;
+	arg = wp[builtin_opt.optind];
+
+	if (!arg)
+		n = 1;
+	else if (!bi_getn(arg, &n))
+		goto c_brkcont_err;
+	if (n <= 0) {
+		/* AT&T ksh does this for non-interactive shells only - weird */
+		bi_errorf("%s: %s", arg, "bad value");
+		goto c_brkcont_err;
+	}
+	quit = (unsigned int)n;
+
+	/* Stop at E_NONE, E_PARSE, E_FUNC, or E_INCL */
+	for (ep = e; ep && !STOP_BRKCONT(ep->type); ep = ep->oenv)
+		if (ep->type == E_LOOP) {
+			if (--quit == 0)
+				break;
+			ep->flags |= EF_BRKCONT_PASS;
+			last_ep = ep;
+		}
+
+	if (quit) {
+		/*
+		 * AT&T ksh doesn't print a message - just does what it
+		 * can. We print a message 'cause it helps in debugging
+		 * scripts, but don't generate an error (ie, keep going).
+		 */
+		if ((unsigned int)n == quit) {
+			warningf(true, "%s: %s %s", wp[0], "can't", wp[0]);
+			return (0);
+		}
+		/*
+		 * POSIX says if n is too big, the last enclosing loop
+		 * shall be used. Doesn't say to print an error but we
+		 * do anyway 'cause the user messed up.
+		 */
+		if (last_ep)
+			last_ep->flags &= ~EF_BRKCONT_PASS;
+		warningf(true, "%s: can only %s %u level(s)",
+		    wp[0], wp[0], (unsigned int)n - quit);
+	}
+
+	unwind(*wp[0] == 'b' ? LBREAK : LCONTIN);
+	/* NOTREACHED */
+
+ c_brkcont_err:
+	return (1);
+}
+
+int
+c_set(const char **wp)
+{
+	int argi;
+	bool setargs;
+	struct block *l = e->loc;
+	const char **owp;
+
+	if (wp[1] == NULL) {
+		static const char *args[] = { Tset, "-", NULL };
+		return (c_typeset(args));
+	}
+
+	argi = parse_args(wp, OF_SET, &setargs);
+	if (argi < 0)
+		return (1);
+	/* set $# and $* */
+	if (setargs) {
+		wp += argi - 1;
+		owp = wp;
+		/* save $0 */
+		wp[0] = l->argv[0];
+		while (*++wp != NULL)
+			strdupx(*wp, *wp, &l->area);
+		l->argc = wp - owp - 1;
+		l->argv = alloc2(l->argc + 2, sizeof(char *), &l->area);
+		for (wp = l->argv; (*wp++ = *owp++) != NULL; )
+			;
+	}
+	/*-
+	 * POSIX says set exit status is 0, but old scripts that use
+	 * getopt(1) use the construct
+	 *	set -- $(getopt ab:c "$@")
+	 * which assumes the exit value set will be that of the $()
+	 * (subst_exstat is cleared in execute() so that it will be 0
+	 * if there are no command substitutions).
+	 */
+#ifdef MKSH_LEGACY_MODE
+	/* traditional behaviour, unless set -o posix */
+	return (Flag(FPOSIX) ? 0 : subst_exstat);
+#else
+	/* conformant behaviour, unless set -o sh +o posix */
+	return (Flag(FSH) && !Flag(FPOSIX) ? subst_exstat : 0);
+#endif
+}
+
+int
+c_unset(const char **wp)
+{
+	const char *id;
+	int optc, rv = 0;
+	bool unset_var = true;
+
+	while ((optc = ksh_getopt(wp, &builtin_opt, "fv")) != -1)
+		switch (optc) {
+		case 'f':
+			unset_var = false;
+			break;
+		case 'v':
+			unset_var = true;
+			break;
+		case '?':
+			/*XXX not reached due to GF_ERROR */
+			return (2);
+		}
+	wp += builtin_opt.optind;
+	for (; (id = *wp) != NULL; wp++)
+		if (unset_var) {
+			/* unset variable */
+			struct tbl *vp;
+			char *cp = NULL;
+			size_t n;
+
+			n = strlen(id);
+			if (n > 3 && id[n-3] == '[' && id[n-2] == '*' &&
+			    id[n-1] == ']') {
+				strndupx(cp, id, n - 3, ATEMP);
+				id = cp;
+				optc = 3;
+			} else
+				optc = vstrchr(id, '[') ? 0 : 1;
+
+			vp = global(id);
+			afree(cp, ATEMP);
+
+			if ((vp->flag&RDONLY)) {
+				warningf(true, "read-only: %s", vp->name);
+				rv = 1;
+			} else
+				unset(vp, optc);
+		} else
+			/* unset function */
+			define(id, NULL);
+	return (rv);
+}
+
+static void
+p_time(struct shf *shf, bool posix, long tv_sec, int tv_usec, int width,
+    const char *prefix, const char *suffix)
+{
+	tv_usec /= 10000;
+	if (posix)
+		shf_fprintf(shf, "%s%*ld.%02d%s", prefix, width,
+		    tv_sec, tv_usec, suffix);
+	else
+		shf_fprintf(shf, "%s%*ldm%d.%02ds%s", prefix, width,
+		    tv_sec / 60, (int)(tv_sec % 60), tv_usec, suffix);
+}
+
+int
+c_times(const char **wp MKSH_A_UNUSED)
+{
+	struct rusage usage;
+
+	getrusage(RUSAGE_SELF, &usage);
+	p_time(shl_stdout, false, usage.ru_utime.tv_sec,
+	    usage.ru_utime.tv_usec, 0, null, " ");
+	p_time(shl_stdout, false, usage.ru_stime.tv_sec,
+	    usage.ru_stime.tv_usec, 0, null, "\n");
+
+	getrusage(RUSAGE_CHILDREN, &usage);
+	p_time(shl_stdout, false, usage.ru_utime.tv_sec,
+	    usage.ru_utime.tv_usec, 0, null, " ");
+	p_time(shl_stdout, false, usage.ru_stime.tv_sec,
+	    usage.ru_stime.tv_usec, 0, null, "\n");
+
+	return (0);
+}
+
+/*
+ * time pipeline (really a statement, not a built-in command)
+ */
+int
+timex(struct op *t, int f, volatile int *xerrok)
+{
+#define TF_NOARGS	BIT(0)
+#define TF_NOREAL	BIT(1)		/* don't report real time */
+#define TF_POSIX	BIT(2)		/* report in POSIX format */
+	int rv = 0, tf = 0;
+	struct rusage ru0, ru1, cru0, cru1;
+	struct timeval usrtime, systime, tv0, tv1;
+
+	mksh_TIME(tv0);
+	getrusage(RUSAGE_SELF, &ru0);
+	getrusage(RUSAGE_CHILDREN, &cru0);
+	if (t->left) {
+		/*
+		 * Two ways of getting cpu usage of a command: just use t0
+		 * and t1 (which will get cpu usage from other jobs that
+		 * finish while we are executing t->left), or get the
+		 * cpu usage of t->left. AT&T ksh does the former, while
+		 * pdksh tries to do the later (the j_usrtime hack doesn't
+		 * really work as it only counts the last job).
+		 */
+		timerclear(&j_usrtime);
+		timerclear(&j_systime);
+		rv = execute(t->left, f | XTIME, xerrok);
+		if (t->left->type == TCOM)
+			tf |= t->left->str[0];
+		mksh_TIME(tv1);
+		getrusage(RUSAGE_SELF, &ru1);
+		getrusage(RUSAGE_CHILDREN, &cru1);
+	} else
+		tf = TF_NOARGS;
+
+	if (tf & TF_NOARGS) {
+		/* ksh93 - report shell times (shell+kids) */
+		tf |= TF_NOREAL;
+		timeradd(&ru0.ru_utime, &cru0.ru_utime, &usrtime);
+		timeradd(&ru0.ru_stime, &cru0.ru_stime, &systime);
+	} else {
+		timersub(&ru1.ru_utime, &ru0.ru_utime, &usrtime);
+		timeradd(&usrtime, &j_usrtime, &usrtime);
+		timersub(&ru1.ru_stime, &ru0.ru_stime, &systime);
+		timeradd(&systime, &j_systime, &systime);
+	}
+
+	if (!(tf & TF_NOREAL)) {
+		timersub(&tv1, &tv0, &tv1);
+		if (tf & TF_POSIX)
+			p_time(shl_out, true, tv1.tv_sec, tv1.tv_usec,
+			    5, "real ", "\n");
+		else
+			p_time(shl_out, false, tv1.tv_sec, tv1.tv_usec,
+			    5, null, " real ");
+	}
+	if (tf & TF_POSIX)
+		p_time(shl_out, true, usrtime.tv_sec, usrtime.tv_usec,
+		    5, "user ", "\n");
+	else
+		p_time(shl_out, false, usrtime.tv_sec, usrtime.tv_usec,
+		    5, null, " user ");
+	if (tf & TF_POSIX)
+		p_time(shl_out, true, systime.tv_sec, systime.tv_usec,
+		    5, "sys  ", "\n");
+	else
+		p_time(shl_out, false, systime.tv_sec, systime.tv_usec,
+		    5, null, " system\n");
+	shf_flush(shl_out);
+
+	return (rv);
+}
+
+void
+timex_hook(struct op *t, char **volatile *app)
+{
+	char **wp = *app;
+	int optc, i, j;
+	Getopt opt;
+
+	ksh_getopt_reset(&opt, 0);
+	/* start at the start */
+	opt.optind = 0;
+	while ((optc = ksh_getopt((const char **)wp, &opt, ":p")) != -1)
+		switch (optc) {
+		case 'p':
+			t->str[0] |= TF_POSIX;
+			break;
+		case '?':
+			errorf("time: -%s %s", opt.optarg,
+			    "unknown option");
+		case ':':
+			errorf("time: -%s %s", opt.optarg,
+			    "requires an argument");
+		}
+	/* Copy command words down over options. */
+	if (opt.optind != 0) {
+		for (i = 0; i < opt.optind; i++)
+			afree(wp[i], ATEMP);
+		for (i = 0, j = opt.optind; (wp[i] = wp[j]); i++, j++)
+			;
+	}
+	if (!wp[0])
+		t->str[0] |= TF_NOARGS;
+	*app = wp;
+}
+
+/* exec with no args - args case is taken care of in comexec() */
+int
+c_exec(const char **wp MKSH_A_UNUSED)
+{
+	int i;
+
+	/* make sure redirects stay in place */
+	if (e->savefd != NULL) {
+		for (i = 0; i < NUFILE; i++) {
+			if (e->savefd[i] > 0)
+				close(e->savefd[i]);
+#ifndef MKSH_LEGACY_MODE
+			/*
+			 * keep all file descriptors > 2 private for ksh,
+			 * but not for POSIX or legacy/kludge sh
+			 */
+			if (!Flag(FPOSIX) && !Flag(FSH) && i > 2 &&
+			    e->savefd[i])
+				fcntl(i, F_SETFD, FD_CLOEXEC);
+#endif
+		}
+		e->savefd = NULL;
+	}
+	return (0);
+}
+
+#if HAVE_MKNOD
+int
+c_mknod(const char **wp)
+{
+	int argc, optc, rv = 0;
+	bool ismkfifo = false;
+	const char **argv;
+	void *set = NULL;
+	mode_t mode = 0, oldmode = 0;
+
+	while ((optc = ksh_getopt(wp, &builtin_opt, "m:")) != -1) {
+		switch (optc) {
+		case 'm':
+			set = setmode(builtin_opt.optarg);
+			if (set == NULL) {
+				bi_errorf("invalid file mode");
+				return (1);
+			}
+			mode = getmode(set, (mode_t)(DEFFILEMODE));
+			free_ossetmode(set);
+			break;
+		default:
+			goto c_mknod_usage;
+		}
+	}
+	argv = &wp[builtin_opt.optind];
+	if (argv[0] == NULL)
+		goto c_mknod_usage;
+	for (argc = 0; argv[argc]; argc++)
+		;
+	if (argc == 2 && argv[1][0] == 'p')
+		ismkfifo = true;
+	else if (argc != 4 || (argv[1][0] != 'b' && argv[1][0] != 'c'))
+		goto c_mknod_usage;
+
+	if (set != NULL)
+		oldmode = umask((mode_t)0);
+	else
+		mode = DEFFILEMODE;
+
+	mode |= (argv[1][0] == 'b') ? S_IFBLK :
+	    (argv[1][0] == 'c') ? S_IFCHR : 0;
+
+	if (!ismkfifo) {
+		unsigned long majnum, minnum;
+		dev_t dv;
+		char *c;
+
+		majnum = strtoul(argv[2], &c, 0);
+		if ((c == argv[2]) || (*c != '\0')) {
+			bi_errorf("non-numeric %s %s '%s'", "device", "major", argv[2]);
+			goto c_mknod_err;
+		}
+		minnum = strtoul(argv[3], &c, 0);
+		if ((c == argv[3]) || (*c != '\0')) {
+			bi_errorf("non-numeric %s %s '%s'", "device", "minor", argv[3]);
+			goto c_mknod_err;
+		}
+		dv = makedev(majnum, minnum);
+		if ((unsigned long)(major(dv)) != majnum) {
+			bi_errorf("%s %s too large: %lu", "device", "major", majnum);
+			goto c_mknod_err;
+		}
+		if ((unsigned long)(minor(dv)) != minnum) {
+			bi_errorf("%s %s too large: %lu", "device", "minor", minnum);
+			goto c_mknod_err;
+		}
+		if (mknod(argv[0], mode, dv))
+			goto c_mknod_failed;
+	} else if (mkfifo(argv[0], mode)) {
+ c_mknod_failed:
+		bi_errorf("%s: %s", argv[0], cstrerror(errno));
+ c_mknod_err:
+		rv = 1;
+	}
+
+	if (set)
+		umask(oldmode);
+	return (rv);
+ c_mknod_usage:
+	bi_errorf("%s: %s", "usage", "mknod [-m mode] name b|c major minor");
+	bi_errorf("%s: %s", "usage", "mknod [-m mode] name p");
+	return (1);
+}
+#endif
+
+/*-
+   test(1) roughly accepts the following grammar:
+	oexpr	::= aexpr | aexpr "-o" oexpr ;
+	aexpr	::= nexpr | nexpr "-a" aexpr ;
+	nexpr	::= primary | "!" nexpr ;
+	primary	::= unary-operator operand
+		| operand binary-operator operand
+		| operand
+		| "(" oexpr ")"
+		;
+
+	unary-operator ::= "-a"|"-r"|"-w"|"-x"|"-e"|"-f"|"-d"|"-c"|"-b"|"-p"|
+			   "-u"|"-g"|"-k"|"-s"|"-t"|"-z"|"-n"|"-o"|"-O"|"-G"|
+			   "-L"|"-h"|"-S"|"-H";
+
+	binary-operator ::= "="|"=="|"!="|"-eq"|"-ne"|"-ge"|"-gt"|"-le"|"-lt"|
+			    "-nt"|"-ot"|"-ef"|
+			    "<"|">"	# rules used for [[ ... ]] expressions
+			    ;
+	operand ::= <anything>
+*/
+
+/* POSIX says > 1 for errors */
+#define T_ERR_EXIT 2
+
+int
+c_test(const char **wp)
+{
+	int argc, rv, invert = 0;
+	Test_env te;
+	Test_op op;
+	const char *lhs, **swp;
+
+	te.flags = 0;
+	te.isa = ptest_isa;
+	te.getopnd = ptest_getopnd;
+	te.eval = test_eval;
+	te.error = ptest_error;
+
+	for (argc = 0; wp[argc]; argc++)
+		;
+	mkssert(argc > 0);
+	mkssert(wp[0] != NULL);
+
+	if (strcmp(wp[0], "[") == 0) {
+		if (strcmp(wp[--argc], "]") != 0) {
+			bi_errorf("missing ]");
+			return (T_ERR_EXIT);
+		}
+	}
+
+	te.pos.wp = wp + 1;
+	te.wp_end = wp + argc;
+
+	/*
+	 * Attempt to conform to POSIX special cases. This is pretty
+	 * dumb code straight-forward from the 2008 spec, but unless
+	 * the old pdksh code doesn't live from so many assumptions.
+	 * It does, though, inline some calls to '(*te.funcname)()'.
+	 */
+	switch (argc - 1) {
+	case 0:
+		return (1);
+	case 1:
+ ptest_one:
+		op = TO_STNZE;
+		goto ptest_unary;
+	case 2:
+ ptest_two:
+		if (ptest_isa(&te, TM_NOT)) {
+			++invert;
+			goto ptest_one;
+		}
+		if ((op = ptest_isa(&te, TM_UNOP))) {
+ ptest_unary:
+			rv = test_eval(&te, op, *te.pos.wp++, NULL, true);
+ ptest_out:
+			return ((invert & 1) ? rv : !rv);
+		}
+		/* let the parser deal with anything else */
+		break;
+	case 3:
+ ptest_three:
+		swp = te.pos.wp;
+		/* use inside knowledge of ptest_getopnd inlined below */
+		lhs = *te.pos.wp++;
+		if ((op = ptest_isa(&te, TM_BINOP))) {
+			/* test lhs op rhs */
+			rv = test_eval(&te, op, lhs, *te.pos.wp++, true);
+			goto ptest_out;
+		}
+		/* back up to lhs */
+		te.pos.wp = swp;
+		if (ptest_isa(&te, TM_NOT)) {
+			++invert;
+			goto ptest_two;
+		}
+		if (ptest_isa(&te, TM_OPAREN)) {
+			swp = te.pos.wp;
+			/* skip operand, without evaluation */
+			te.pos.wp++;
+			/* check for closing parenthesis */
+			op = ptest_isa(&te, TM_CPAREN);
+			/* back up to operand */
+			te.pos.wp = swp;
+			/* if there was a closing paren, handle it */
+			if (op)
+				goto ptest_one;
+			/* backing up is done before calling the parser */
+		}
+		/* let the parser deal with it */
+		break;
+	case 4:
+		if (ptest_isa(&te, TM_NOT)) {
+			++invert;
+			goto ptest_three;
+		}
+		if (ptest_isa(&te, TM_OPAREN)) {
+			swp = te.pos.wp;
+			/* skip two operands, without evaluation */
+			te.pos.wp++;
+			te.pos.wp++;
+			/* check for closing parenthesis */
+			op = ptest_isa(&te, TM_CPAREN);
+			/* back up to first operand */
+			te.pos.wp = swp;
+			/* if there was a closing paren, handle it */
+			if (op)
+				goto ptest_two;
+			/* backing up is done before calling the parser */
+		}
+		/* defer this to the parser */
+		break;
+	}
+
+	/* "The results are unspecified." */
+	te.pos.wp = wp + 1;
+	return (test_parse(&te));
+}
+
+/*
+ * Generic test routines.
+ */
+
+Test_op
+test_isop(Test_meta meta, const char *s)
+{
+	char sc1;
+	const struct t_op *tbl;
+
+	tbl = meta == TM_UNOP ? u_ops : b_ops;
+	if (*s) {
+		sc1 = s[1];
+		for (; tbl->op_text[0]; tbl++)
+			if (sc1 == tbl->op_text[1] && !strcmp(s, tbl->op_text))
+				return (tbl->op_num);
+	}
+	return (TO_NONOP);
+}
+
+int
+test_eval(Test_env *te, Test_op op, const char *opnd1, const char *opnd2,
+    bool do_eval)
+{
+	int i, s;
+	size_t k;
+	struct stat b1, b2;
+	mksh_ari_t v1, v2;
+
+	if (!do_eval)
+		return (0);
+
+#ifdef DEBUG
+	switch (op) {
+	/* Binary operators */
+	case TO_STEQL:
+	case TO_STNEQ:
+	case TO_STLT:
+	case TO_STGT:
+	case TO_INTEQ:
+	case TO_INTNE:
+	case TO_INTGT:
+	case TO_INTGE:
+	case TO_INTLT:
+	case TO_INTLE:
+	case TO_FILEQ:
+	case TO_FILNT:
+	case TO_FILOT:
+		/* consistency check, but does not happen in practice */
+		if (!opnd2) {
+			te->flags |= TEF_ERROR;
+			return (1);
+		}
+		break;
+	default:
+		/* for completeness of switch */
+		break;
+	}
+#endif
+
+	switch (op) {
+
+	/*
+	 * Unary Operators
+	 */
+
+	/* -n */
+	case TO_STNZE:
+		return (*opnd1 != '\0');
+
+	/* -z */
+	case TO_STZER:
+		return (*opnd1 == '\0');
+
+	/* -o */
+	case TO_OPTION:
+		if ((i = *opnd1) == '!' || i == '?')
+			opnd1++;
+		if ((k = option(opnd1)) == (size_t)-1)
+			return (0);
+		return (i == '?' ? 1 : i == '!' ? !Flag(k) : Flag(k));
+
+	/* -r */
+	case TO_FILRD:
+		/* LINTED use of access */
+		return (access(opnd1, R_OK) == 0);
+
+	/* -w */
+	case TO_FILWR:
+		/* LINTED use of access */
+		return (access(opnd1, W_OK) == 0);
+
+	/* -x */
+	case TO_FILEX:
+		return (ksh_access(opnd1, X_OK) == 0);
+
+	/* -a */
+	case TO_FILAXST:
+	/* -e */
+	case TO_FILEXST:
+		return (stat(opnd1, &b1) == 0);
+
+	/* -r */
+	case TO_FILREG:
+		return (stat(opnd1, &b1) == 0 && S_ISREG(b1.st_mode));
+
+	/* -d */
+	case TO_FILID:
+		return (stat(opnd1, &b1) == 0 && S_ISDIR(b1.st_mode));
+
+	/* -c */
+	case TO_FILCDEV:
+		return (stat(opnd1, &b1) == 0 && S_ISCHR(b1.st_mode));
+
+	/* -b */
+	case TO_FILBDEV:
+		return (stat(opnd1, &b1) == 0 && S_ISBLK(b1.st_mode));
+
+	/* -p */
+	case TO_FILFIFO:
+		return (stat(opnd1, &b1) == 0 && S_ISFIFO(b1.st_mode));
+
+	/* -h or -L */
+	case TO_FILSYM:
+#ifdef MKSH__NO_SYMLINK
+		return (0);
+#else
+		return (lstat(opnd1, &b1) == 0 && S_ISLNK(b1.st_mode));
+#endif
+
+	/* -S */
+	case TO_FILSOCK:
+		return (stat(opnd1, &b1) == 0 && S_ISSOCK(b1.st_mode));
+
+	/* -H => HP context dependent files (directories) */
+	case TO_FILCDF:
+#ifdef S_ISCDF
+	{
+		char *nv;
+
+		/*
+		 * Append a + to filename and check to see if result is
+		 * a setuid directory. CDF stuff in general is hookey,
+		 * since it breaks for, e.g., the following sequence:
+		 * echo hi >foo+; mkdir foo; echo bye >foo/default;
+		 * chmod u+s foo (foo+ refers to the file with hi in it,
+		 * there is no way to get at the file with bye in it;
+		 * please correct me if I'm wrong about this).
+		 */
+
+		nv = shf_smprintf("%s+", opnd1);
+		i = (stat(nv, &b1) == 0 && S_ISCDF(b1.st_mode));
+		afree(nv, ATEMP);
+		return (i);
+	}
+#else
+		return (0);
+#endif
+
+	/* -u */
+	case TO_FILSETU:
+		return (stat(opnd1, &b1) == 0 &&
+		    (b1.st_mode & S_ISUID) == S_ISUID);
+
+	/* -g */
+	case TO_FILSETG:
+		return (stat(opnd1, &b1) == 0 &&
+		    (b1.st_mode & S_ISGID) == S_ISGID);
+
+	/* -k */
+	case TO_FILSTCK:
+#ifdef S_ISVTX
+		return (stat(opnd1, &b1) == 0 &&
+		    (b1.st_mode & S_ISVTX) == S_ISVTX);
+#else
+		return (0);
+#endif
+
+	/* -s */
+	case TO_FILGZ:
+		return (stat(opnd1, &b1) == 0 && (off_t)b1.st_size > (off_t)0);
+
+	/* -t */
+	case TO_FILTT:
+		if (opnd1 && !bi_getn(opnd1, &i)) {
+			te->flags |= TEF_ERROR;
+			i = 0;
+		} else
+			i = isatty(opnd1 ? i : 0);
+		return (i);
+
+	/* -O */
+	case TO_FILUID:
+		return (stat(opnd1, &b1) == 0 && (uid_t)b1.st_uid == ksheuid);
+
+	/* -G */
+	case TO_FILGID:
+		return (stat(opnd1, &b1) == 0 && (gid_t)b1.st_gid == getegid());
+
+	/*
+	 * Binary Operators
+	 */
+
+	/* = */
+	case TO_STEQL:
+		if (te->flags & TEF_DBRACKET)
+			return (gmatchx(opnd1, opnd2, false));
+		return (strcmp(opnd1, opnd2) == 0);
+
+	/* != */
+	case TO_STNEQ:
+		if (te->flags & TEF_DBRACKET)
+			return (!gmatchx(opnd1, opnd2, false));
+		return (strcmp(opnd1, opnd2) != 0);
+
+	/* < */
+	case TO_STLT:
+		return (strcmp(opnd1, opnd2) < 0);
+
+	/* > */
+	case TO_STGT:
+		return (strcmp(opnd1, opnd2) > 0);
+
+	/* -eq */
+	case TO_INTEQ:
+	/* -ne */
+	case TO_INTNE:
+	/* -ge */
+	case TO_INTGE:
+	/* -gt */
+	case TO_INTGT:
+	/* -le */
+	case TO_INTLE:
+	/* -lt */
+	case TO_INTLT:
+		if (!evaluate(opnd1, &v1, KSH_RETURN_ERROR, false) ||
+		    !evaluate(opnd2, &v2, KSH_RETURN_ERROR, false)) {
+			/* error already printed.. */
+			te->flags |= TEF_ERROR;
+			return (1);
+		}
+		switch (op) {
+		case TO_INTEQ:
+			return (v1 == v2);
+		case TO_INTNE:
+			return (v1 != v2);
+		case TO_INTGE:
+			return (v1 >= v2);
+		case TO_INTGT:
+			return (v1 > v2);
+		case TO_INTLE:
+			return (v1 <= v2);
+		case TO_INTLT:
+			return (v1 < v2);
+		default:
+			/* NOTREACHED */
+			break;
+		}
+		/* NOTREACHED */
+
+	/* -nt */
+	case TO_FILNT:
+		/*
+		 * ksh88/ksh93 succeed if file2 can't be stated
+		 * (subtly different from 'does not exist').
+		 */
+		return (stat(opnd1, &b1) == 0 &&
+		    (((s = stat(opnd2, &b2)) == 0 &&
+		    b1.st_mtime > b2.st_mtime) || s < 0));
+
+	/* -ot */
+	case TO_FILOT:
+		/*
+		 * ksh88/ksh93 succeed if file1 can't be stated
+		 * (subtly different from 'does not exist').
+		 */
+		return (stat(opnd2, &b2) == 0 &&
+		    (((s = stat(opnd1, &b1)) == 0 &&
+		    b1.st_mtime < b2.st_mtime) || s < 0));
+
+	/* -ef */
+	case TO_FILEQ:
+		return (stat (opnd1, &b1) == 0 && stat (opnd2, &b2) == 0 &&
+		    b1.st_dev == b2.st_dev && b1.st_ino == b2.st_ino);
+
+	/* all other cases */
+	case TO_NONOP:
+	case TO_NONNULL:
+		/* throw the error */
+		break;
+	}
+	(*te->error)(te, 0, "internal error: unknown op");
+	return (1);
+}
+
+int
+test_parse(Test_env *te)
+{
+	int rv;
+
+	rv = test_oexpr(te, 1);
+
+	if (!(te->flags & TEF_ERROR) && !(*te->isa)(te, TM_END))
+		(*te->error)(te, 0, "unexpected operator/operand");
+
+	return ((te->flags & TEF_ERROR) ? T_ERR_EXIT : !rv);
+}
+
+static int
+test_oexpr(Test_env *te, bool do_eval)
+{
+	int rv;
+
+	if ((rv = test_aexpr(te, do_eval)))
+		do_eval = false;
+	if (!(te->flags & TEF_ERROR) && (*te->isa)(te, TM_OR))
+		return (test_oexpr(te, do_eval) || rv);
+	return (rv);
+}
+
+static int
+test_aexpr(Test_env *te, bool do_eval)
+{
+	int rv;
+
+	if (!(rv = test_nexpr(te, do_eval)))
+		do_eval = false;
+	if (!(te->flags & TEF_ERROR) && (*te->isa)(te, TM_AND))
+		return (test_aexpr(te, do_eval) && rv);
+	return (rv);
+}
+
+static int
+test_nexpr(Test_env *te, bool do_eval)
+{
+	if (!(te->flags & TEF_ERROR) && (*te->isa)(te, TM_NOT))
+		return (!test_nexpr(te, do_eval));
+	return (test_primary(te, do_eval));
+}
+
+static int
+test_primary(Test_env *te, bool do_eval)
+{
+	const char *opnd1, *opnd2;
+	int rv;
+	Test_op op;
+
+	if (te->flags & TEF_ERROR)
+		return (0);
+	if ((*te->isa)(te, TM_OPAREN)) {
+		rv = test_oexpr(te, do_eval);
+		if (te->flags & TEF_ERROR)
+			return (0);
+		if (!(*te->isa)(te, TM_CPAREN)) {
+			(*te->error)(te, 0, "missing )");
+			return (0);
+		}
+		return (rv);
+	}
+	/*
+	 * Binary should have precedence over unary in this case
+	 * so that something like test \( -f = -f \) is accepted
+	 */
+	if ((te->flags & TEF_DBRACKET) || (&te->pos.wp[1] < te->wp_end &&
+	    !test_isop(TM_BINOP, te->pos.wp[1]))) {
+		if ((op = (*te->isa)(te, TM_UNOP))) {
+			/* unary expression */
+			opnd1 = (*te->getopnd)(te, op, do_eval);
+			if (!opnd1) {
+				(*te->error)(te, -1, "missing argument");
+				return (0);
+			}
+
+			return ((*te->eval)(te, op, opnd1, NULL, do_eval));
+		}
+	}
+	opnd1 = (*te->getopnd)(te, TO_NONOP, do_eval);
+	if (!opnd1) {
+		(*te->error)(te, 0, "expression expected");
+		return (0);
+	}
+	if ((op = (*te->isa)(te, TM_BINOP))) {
+		/* binary expression */
+		opnd2 = (*te->getopnd)(te, op, do_eval);
+		if (!opnd2) {
+			(*te->error)(te, -1, "missing second argument");
+			return (0);
+		}
+
+		return ((*te->eval)(te, op, opnd1, opnd2, do_eval));
+	}
+	return ((*te->eval)(te, TO_STNZE, opnd1, NULL, do_eval));
+}
+
+/*
+ * Plain test (test and [ .. ]) specific routines.
+ */
+
+/*
+ * Test if the current token is a whatever. Accepts the current token if
+ * it is. Returns 0 if it is not, non-zero if it is (in the case of
+ * TM_UNOP and TM_BINOP, the returned value is a Test_op).
+ */
+static Test_op
+ptest_isa(Test_env *te, Test_meta meta)
+{
+	/* Order important - indexed by Test_meta values */
+	static const char * const tokens[] = {
+		"-o", "-a", "!", "(", ")"
+	};
+	Test_op rv;
+
+	if (te->pos.wp >= te->wp_end)
+		return (meta == TM_END ? TO_NONNULL : TO_NONOP);
+
+	if (meta == TM_UNOP || meta == TM_BINOP)
+		rv = test_isop(meta, *te->pos.wp);
+	else if (meta == TM_END)
+		rv = TO_NONOP;
+	else
+		rv = !strcmp(*te->pos.wp, tokens[(int)meta]) ?
+		    TO_NONNULL : TO_NONOP;
+
+	/* Accept the token? */
+	if (rv != TO_NONOP)
+		te->pos.wp++;
+
+	return (rv);
+}
+
+static const char *
+ptest_getopnd(Test_env *te, Test_op op, bool do_eval MKSH_A_UNUSED)
+{
+	if (te->pos.wp >= te->wp_end)
+		return (op == TO_FILTT ? "1" : NULL);
+	return (*te->pos.wp++);
+}
+
+static void
+ptest_error(Test_env *te, int ofs, const char *msg)
+{
+	const char *op;
+
+	te->flags |= TEF_ERROR;
+	if ((op = te->pos.wp + ofs >= te->wp_end ? NULL : te->pos.wp[ofs]))
+		bi_errorf("%s: %s", op, msg);
+	else
+		bi_errorf("%s", msg);
+}
+
+#ifndef MKSH_NO_LIMITS
+#define SOFT	0x1
+#define HARD	0x2
+
+/* Magic to divine the 'm' and 'v' limits */
+
+#ifdef RLIMIT_AS
+#if !defined(RLIMIT_VMEM) || (RLIMIT_VMEM == RLIMIT_AS) || \
+    !defined(RLIMIT_RSS) || (RLIMIT_VMEM == RLIMIT_RSS)
+#define ULIMIT_V_IS_AS
+#elif defined(RLIMIT_VMEM)
+#if !defined(RLIMIT_RSS) || (RLIMIT_RSS == RLIMIT_AS)
+#define ULIMIT_V_IS_AS
+#else
+#define ULIMIT_V_IS_VMEM
+#endif
+#endif
+#endif
+
+#ifdef RLIMIT_RSS
+#ifdef ULIMIT_V_IS_VMEM
+#define ULIMIT_M_IS_RSS
+#elif defined(RLIMIT_VMEM) && (RLIMIT_VMEM == RLIMIT_RSS)
+#define ULIMIT_M_IS_VMEM
+#else
+#define ULIMIT_M_IS_RSS
+#endif
+#if defined(ULIMIT_M_IS_RSS) && defined(RLIMIT_AS) && (RLIMIT_RSS == RLIMIT_AS)
+#undef ULIMIT_M_IS_RSS
+#endif
+#endif
+
+#if !defined(RLIMIT_AS) && !defined(ULIMIT_M_IS_VMEM) && defined(RLIMIT_VMEM)
+#define ULIMIT_V_IS_VMEM
+#endif
+
+#if !defined(ULIMIT_V_IS_VMEM) && defined(RLIMIT_VMEM) && \
+    (!defined(RLIMIT_RSS) || (defined(RLIMIT_AS) && (RLIMIT_RSS == RLIMIT_AS)))
+#define ULIMIT_M_IS_VMEM
+#endif
+
+#if defined(ULIMIT_M_IS_VMEM) && defined(RLIMIT_AS) && \
+    (RLIMIT_VMEM == RLIMIT_AS)
+#undef ULIMIT_M_IS_VMEM
+#endif
+
+#if defined(ULIMIT_M_IS_RSS) && defined(ULIMIT_M_IS_VMEM)
+# error nonsensical m ulimit
+#endif
+
+#if defined(ULIMIT_V_IS_VMEM) && defined(ULIMIT_V_IS_AS)
+# error nonsensical v ulimit
+#endif
+
+#define RLIMITS_DEFNS
+#include "rlimits.gen"
+
+static void print_ulimit(const struct limits *, int);
+static int set_ulimit(const struct limits *, const char *, int);
+
+static const struct limits * const rlimits[] = {
+#define RLIMITS_ITEMS
+#include "rlimits.gen"
+};
+
+static const char rlimits_opts[] =
+#define RLIMITS_OPTCS
+#include "rlimits.gen"
+    ;
+
+int
+c_ulimit(const char **wp)
+{
+	size_t i = 0;
+	int how = SOFT | HARD, optc, what = 'f';
+	bool all = false;
+
+	while ((optc = ksh_getopt(wp, &builtin_opt, rlimits_opts)) != -1)
+		switch (optc) {
+		case 'H':
+			how = HARD;
+			break;
+		case 'S':
+			how = SOFT;
+			break;
+		case 'a':
+			all = true;
+			break;
+		case '?':
+			bi_errorf("usage: ulimit [-%s] [value]", rlimits_opts);
+			return (1);
+		default:
+			what = optc;
+		}
+
+	while (i < NELEM(rlimits)) {
+		if (rlimits[i]->optchar == what)
+			goto found;
+		++i;
+	}
+	internal_warningf("ulimit: %c", what);
+	return (1);
+ found:
+	if (wp[builtin_opt.optind]) {
+		if (all || wp[builtin_opt.optind + 1]) {
+			bi_errorf("too many arguments");
+			return (1);
+		}
+		return (set_ulimit(rlimits[i], wp[builtin_opt.optind], how));
+	}
+	if (!all)
+		print_ulimit(rlimits[i], how);
+	else for (i = 0; i < NELEM(rlimits); ++i) {
+		shprintf("%-20s ", rlimits[i]->name);
+		print_ulimit(rlimits[i], how);
+	}
+	return (0);
+}
+
+static int
+set_ulimit(const struct limits *l, const char *v, int how)
+{
+	rlim_t val = (rlim_t)0;
+	struct rlimit limit;
+
+	if (strcmp(v, "unlimited") == 0)
+		val = (rlim_t)RLIM_INFINITY;
+	else {
+		mksh_uari_t rval;
+
+		if (!evaluate(v, (mksh_ari_t *)&rval, KSH_RETURN_ERROR, false))
+			return (1);
+		/*
+		 * Avoid problems caused by typos that evaluate misses due
+		 * to evaluating unset parameters to 0...
+		 * If this causes problems, will have to add parameter to
+		 * evaluate() to control if unset params are 0 or an error.
+		 */
+		if (!rval && !ksh_isdigit(v[0])) {
+			bi_errorf("invalid %s limit: %s", l->name, v);
+			return (1);
+		}
+		val = (rlim_t)((rlim_t)rval * l->factor);
+	}
+
+	if (getrlimit(l->resource, &limit) < 0) {
+#ifndef MKSH_SMALL
+		bi_errorf("limit %s could not be read, contact the mksh developers: %s",
+		    l->name, cstrerror(errno));
+#endif
+		/* some can't be read */
+		limit.rlim_cur = RLIM_INFINITY;
+		limit.rlim_max = RLIM_INFINITY;
+	}
+	if (how & SOFT)
+		limit.rlim_cur = val;
+	if (how & HARD)
+		limit.rlim_max = val;
+	if (!setrlimit(l->resource, &limit))
+		return (0);
+	if (errno == EPERM)
+		bi_errorf("%s exceeds allowable %s limit", v, l->name);
+	else
+		bi_errorf("bad %s limit: %s", l->name, cstrerror(errno));
+	return (1);
+}
+
+static void
+print_ulimit(const struct limits *l, int how)
+{
+	rlim_t val = (rlim_t)0;
+	struct rlimit limit;
+
+	if (getrlimit(l->resource, &limit)) {
+		shf_puts("unknown\n", shl_stdout);
+		return;
+	}
+	if (how & SOFT)
+		val = limit.rlim_cur;
+	else if (how & HARD)
+		val = limit.rlim_max;
+	if (val == (rlim_t)RLIM_INFINITY)
+		shf_puts("unlimited\n", shl_stdout);
+	else
+		shprintf("%lu\n", (unsigned long)(val / l->factor));
+}
+#endif
+
+int
+c_rename(const char **wp)
+{
+	int rv = 1;
+
+	/* skip argv[0] */
+	++wp;
+	if (wp[0] && !strcmp(wp[0], "--"))
+		/* skip "--" (options separator) */
+		++wp;
+
+	/* check for exactly two arguments */
+	if (wp[0] == NULL	/* first argument */ ||
+	    wp[1] == NULL	/* second argument */ ||
+	    wp[2] != NULL	/* no further args please */)
+		bi_errorf(Tsynerr);
+	else if ((rv = rename(wp[0], wp[1])) != 0) {
+		rv = errno;
+		bi_errorf("%s: %s", "failed", cstrerror(rv));
+	}
+
+	return (rv);
+}
+
+int
+c_realpath(const char **wp)
+{
+	int rv = 1;
+	char *buf;
+
+	/* skip argv[0] */
+	++wp;
+	if (wp[0] && !strcmp(wp[0], "--"))
+		/* skip "--" (options separator) */
+		++wp;
+
+	/* check for exactly one argument */
+	if (wp[0] == NULL || wp[1] != NULL)
+		bi_errorf(Tsynerr);
+	else if ((buf = do_realpath(wp[0])) == NULL) {
+		rv = errno;
+		bi_errorf("%s: %s", wp[0], cstrerror(rv));
+		if ((unsigned int)rv > 255)
+			rv = 255;
+	} else {
+		shprintf("%s\n", buf);
+		afree(buf, ATEMP);
+		rv = 0;
+	}
+
+	return (rv);
+}
+
+int
+c_cat(const char **wp)
+{
+	int fd = STDIN_FILENO, rv, eno;
+	ssize_t n, w;
+	const char *fn = "<stdin>";
+	char *buf, *cp;
+#define MKSH_CAT_BUFSIZ 4096
+
+	/* parse options: POSIX demands we support "-u" as no-op */
+	while ((rv = ksh_getopt(wp, &builtin_opt, "u")) != -1) {
+		switch (rv) {
+		case 'u':
+			/* we already operate unbuffered */
+			break;
+		default:
+			bi_errorf(Tsynerr);
+			return (1);
+		}
+	}
+	wp += builtin_opt.optind;
+	rv = 0;
+
+	if ((buf = malloc_osfunc(MKSH_CAT_BUFSIZ)) == NULL) {
+		bi_errorf(Toomem, (size_t)MKSH_CAT_BUFSIZ);
+		return (1);
+	}
+
+	do {
+		if (*wp) {
+			fn = *wp++;
+			if (fn[0] == '-' && fn[1] == '\0')
+				fd = STDIN_FILENO;
+			else if ((fd = open(fn, O_RDONLY | O_BINARY)) < 0) {
+				eno = errno;
+				bi_errorf("%s: %s", fn, cstrerror(eno));
+				rv = 1;
+				continue;
+			}
+		}
+		while (/* CONSTCOND */ 1) {
+			n = blocking_read(fd, (cp = buf), MKSH_CAT_BUFSIZ);
+			eno = errno;
+			/* give the user a chance to ^C out */
+			intrcheck();
+			if (n == -1) {
+				if (eno == EINTR) {
+					/* interrupted, try again */
+					continue;
+				}
+				/* an error occured during reading */
+				bi_errorf("%s: %s", fn, cstrerror(eno));
+				rv = 1;
+				break;
+			} else if (n == 0)
+				/* end of file reached */
+				break;
+			while (n) {
+				w = write(STDOUT_FILENO, cp, n);
+				if (w == -1) {
+					if (errno == EINTR)
+						/* interrupted, try again */
+						continue;
+					/* an error occured during writing */
+					eno = errno;
+					bi_errorf("%s: %s", "<stdout>",
+					    cstrerror(eno));
+					rv = 1;
+					if (fd != STDIN_FILENO)
+						close(fd);
+					goto out;
+				}
+				n -= w;
+				cp += w;
+			}
+		}
+		if (fd != STDIN_FILENO)
+			close(fd);
+	} while (*wp);
+
+ out:
+	free_osfunc(buf);
+	return (rv);
+}
+
+#if HAVE_SELECT
+int
+c_sleep(const char **wp)
+{
+	struct timeval tv;
+	int rv = 1;
+
+	/* skip argv[0] */
+	++wp;
+	if (wp[0] && !strcmp(wp[0], "--"))
+		/* skip "--" (options separator) */
+		++wp;
+
+	if (!wp[0] || wp[1])
+		bi_errorf(Tsynerr);
+	else if (parse_usec(wp[0], &tv))
+		bi_errorf("%s: %s '%s'", Tsynerr, cstrerror(errno), wp[0]);
+	else {
+#ifndef MKSH_NOPROSPECTOFWORK
+		sigset_t omask, bmask;
+
+		/* block a number of signals from interrupting us, though */
+		(void)sigemptyset(&bmask);
+		(void)sigaddset(&bmask, SIGPIPE);
+		(void)sigaddset(&bmask, SIGCHLD);
+#ifdef SIGWINCH
+		(void)sigaddset(&bmask, SIGWINCH);
+#endif
+#ifdef SIGINFO
+		(void)sigaddset(&bmask, SIGINFO);
+#endif
+#ifdef SIGUSR1
+		(void)sigaddset(&bmask, SIGUSR1);
+#endif
+#ifdef SIGUSR2
+		(void)sigaddset(&bmask, SIGUSR2);
+#endif
+		sigprocmask(SIG_BLOCK, &bmask, &omask);
+#endif
+		if (select(1, NULL, NULL, NULL, &tv) == 0 || errno == EINTR)
+			/*
+			 * strictly speaking only for SIGALRM, but the
+			 * execution may be interrupted by other signals
+			 */
+			rv = 0;
+		else
+			bi_errorf("%s: %s", Tselect, cstrerror(errno));
+#ifndef MKSH_NOPROSPECTOFWORK
+		/* this will re-schedule signal delivery */
+		sigprocmask(SIG_SETMASK, &omask, NULL);
+#endif
+	}
+	return (rv);
+}
+#endif
+
+#if !defined(MKSH_UNEMPLOYED) && HAVE_GETSID
+static int
+c_suspend(const char **wp)
+{
+	if (wp[1] != NULL) {
+		bi_errorf("too many arguments");
+		return (1);
+	}
+	if (Flag(FLOGIN)) {
+		/* Can't suspend an orphaned process group. */
+		if (getpgid(kshppid) == getpgid(0) ||
+		    getsid(kshppid) != getsid(0)) {
+			bi_errorf("can't suspend a login shell");
+			return (1);
+		}
+	}
+	j_suspend();
+	return (0);
+}
+#endif

Deleted: vendor/MirOS/mksh/R50/histrap.c
===================================================================
--- vendor/MirOS/mksh/dist/histrap.c	2014-06-30 23:58:12 UTC (rev 6706)
+++ vendor/MirOS/mksh/R50/histrap.c	2014-07-01 12:01:17 UTC (rev 6708)
@@ -1,1512 +0,0 @@
-/*	$OpenBSD: history.c,v 1.39 2010/05/19 17:36:08 jasper Exp $	*/
-/*	$OpenBSD: trap.c,v 1.23 2010/05/19 17:36:08 jasper Exp $	*/
-
-/*-
- * Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
- *		 2011, 2012
- *	Thorsten Glaser <tg at mirbsd.org>
- *
- * Provided that these terms and disclaimer and all copyright notices
- * are retained or reproduced in an accompanying document, permission
- * is granted to deal in this work without restriction, including un-
- * limited rights to use, publicly perform, distribute, sell, modify,
- * merge, give away, or sublicence.
- *
- * This work is provided "AS IS" and WITHOUT WARRANTY of any kind, to
- * the utmost extent permitted by applicable law, neither express nor
- * implied; without malicious intent or gross negligence. In no event
- * may a licensor, author or contributor be held liable for indirect,
- * direct, other damage, loss, or other issues arising in any way out
- * of dealing in the work, even if advised of the possibility of such
- * damage or existence of a defect, except proven that it results out
- * of said person's immediate fault when using the work as intended.
- */
-
-#include "sh.h"
-#if HAVE_SYS_FILE_H
-#include <sys/file.h>
-#endif
-
-__RCSID("$MirOS: src/bin/mksh/histrap.c,v 1.131 2012/12/28 02:28:35 tg Exp $");
-
-Trap sigtraps[NSIG + 1];
-static struct sigaction Sigact_ign;
-
-#if HAVE_PERSISTENT_HISTORY
-static int histload(Source *, unsigned char *, size_t);
-static int writehistline(int, int, const char *);
-static void writehistfile(int, const char *);
-#endif
-
-static int hist_execute(char *);
-static char **hist_get(const char *, bool, bool);
-static char **hist_get_oldest(void);
-
-static bool hstarted;		/* set after hist_init() called */
-static Source *hist_source;
-
-#if HAVE_PERSISTENT_HISTORY
-/*XXX imake style */
-#if defined(__linux)
-#define caddr_cast(x)	((void *)(x))
-#else
-#define caddr_cast(x)	((caddr_t)(x))
-#endif
-
-/* several OEs do not have these constants */
-#ifndef MAP_FAILED
-#define MAP_FAILED	caddr_cast(-1)
-#endif
-
-/* some OEs need the default mapping type specified */
-#ifndef MAP_FILE
-#define MAP_FILE	0
-#endif
-
-/* current history file: name, fd, size */
-static char *hname;
-static int histfd = -1;
-static off_t histfsize;
-#endif
-
-static const char Tnot_in_history[] = "not in history";
-#define Thistory (Tnot_in_history + 7)
-
-static const char TFCEDIT_dollaru[] = "${FCEDIT:-/bin/ed} $_";
-#define Tspdollaru (TFCEDIT_dollaru + 18)
-
-/* HISTSIZE default: size of saved history, persistent or standard */
-#ifdef MKSH_SMALL
-#define MKSH_DEFHISTSIZE	255
-#else
-#define MKSH_DEFHISTSIZE	2047
-#endif
-/* maximum considered size of persistent history file */
-#define MKSH_MAXHISTFSIZE	((off_t)1048576 * 96)
-
-int
-c_fc(const char **wp)
-{
-	struct shf *shf;
-	struct temp *tf;
-	bool gflag = false, lflag = false, nflag = false, rflag = false,
-	    sflag = false;
-	int optc;
-	const char *p, *first = NULL, *last = NULL;
-	char **hfirst, **hlast, **hp, *editor = NULL;
-
-	if (!Flag(FTALKING_I)) {
-		bi_errorf("history %ss not available", Tfunction);
-		return (1);
-	}
-
-	while ((optc = ksh_getopt(wp, &builtin_opt,
-	    "e:glnrs0,1,2,3,4,5,6,7,8,9,")) != -1)
-		switch (optc) {
-
-		case 'e':
-			p = builtin_opt.optarg;
-			if (ksh_isdash(p))
-				sflag = true;
-			else {
-				size_t len = strlen(p);
-
-				/* almost certainly not overflowing */
-				editor = alloc(len + 4, ATEMP);
-				memcpy(editor, p, len);
-				memcpy(editor + len, Tspdollaru, 4);
-			}
-			break;
-
-		/* non-AT&T ksh */
-		case 'g':
-			gflag = true;
-			break;
-
-		case 'l':
-			lflag = true;
-			break;
-
-		case 'n':
-			nflag = true;
-			break;
-
-		case 'r':
-			rflag = true;
-			break;
-
-		/* POSIX version of -e - */
-		case 's':
-			sflag = true;
-			break;
-
-		/* kludge city - accept -num as -- -num (kind of) */
-		case '0': case '1': case '2': case '3': case '4':
-		case '5': case '6': case '7': case '8': case '9':
-			p = shf_smprintf("-%c%s",
-					optc, builtin_opt.optarg);
-			if (!first)
-				first = p;
-			else if (!last)
-				last = p;
-			else {
-				bi_errorf("too many arguments");
-				return (1);
-			}
-			break;
-
-		case '?':
-			return (1);
-		}
-	wp += builtin_opt.optind;
-
-	/* Substitute and execute command */
-	if (sflag) {
-		char *pat = NULL, *rep = NULL, *line;
-
-		if (editor || lflag || nflag || rflag) {
-			bi_errorf("can't use -e, -l, -n, -r with -s (-e -)");
-			return (1);
-		}
-
-		/* Check for pattern replacement argument */
-		if (*wp && **wp && (p = cstrchr(*wp + 1, '='))) {
-			strdupx(pat, *wp, ATEMP);
-			rep = pat + (p - *wp);
-			*rep++ = '\0';
-			wp++;
-		}
-		/* Check for search prefix */
-		if (!first && (first = *wp))
-			wp++;
-		if (last || *wp) {
-			bi_errorf("too many arguments");
-			return (1);
-		}
-
-		hp = first ? hist_get(first, false, false) :
-		    hist_get_newest(false);
-		if (!hp)
-			return (1);
-		/* hist_replace */
-		if (!pat)
-			strdupx(line, *hp, ATEMP);
-		else {
-			char *s, *s1;
-			size_t len, pat_len, rep_len;
-			XString xs;
-			char *xp;
-			bool any_subst = false;
-
-			pat_len = strlen(pat);
-			rep_len = strlen(rep);
-			Xinit(xs, xp, 128, ATEMP);
-			for (s = *hp; (s1 = strstr(s, pat)) &&
-			    (!any_subst || gflag); s = s1 + pat_len) {
-				any_subst = true;
-				len = s1 - s;
-				XcheckN(xs, xp, len + rep_len);
-				/*; first part */
-				memcpy(xp, s, len);
-				xp += len;
-				/* replacement */
-				memcpy(xp, rep, rep_len);
-				xp += rep_len;
-			}
-			if (!any_subst) {
-				bi_errorf("bad substitution");
-				return (1);
-			}
-			len = strlen(s) + 1;
-			XcheckN(xs, xp, len);
-			memcpy(xp, s, len);
-			xp += len;
-			line = Xclose(xs, xp);
-		}
-		return (hist_execute(line));
-	}
-
-	if (editor && (lflag || nflag)) {
-		bi_errorf("can't use -l, -n with -e");
-		return (1);
-	}
-
-	if (!first && (first = *wp))
-		wp++;
-	if (!last && (last = *wp))
-		wp++;
-	if (*wp) {
-		bi_errorf("too many arguments");
-		return (1);
-	}
-	if (!first) {
-		hfirst = lflag ? hist_get("-16", true, true) :
-		    hist_get_newest(false);
-		if (!hfirst)
-			return (1);
-		/* can't fail if hfirst didn't fail */
-		hlast = hist_get_newest(false);
-	} else {
-		/*
-		 * POSIX says not an error if first/last out of bounds
-		 * when range is specified; AT&T ksh and pdksh allow out
-		 * of bounds for -l as well.
-		 */
-		hfirst = hist_get(first, tobool(lflag || last), lflag);
-		if (!hfirst)
-			return (1);
-		hlast = last ? hist_get(last, true, lflag) :
-		    (lflag ? hist_get_newest(false) : hfirst);
-		if (!hlast)
-			return (1);
-	}
-	if (hfirst > hlast) {
-		char **temp;
-
-		temp = hfirst; hfirst = hlast; hlast = temp;
-		/* POSIX */
-		rflag = !rflag;
-	}
-
-	/* List history */
-	if (lflag) {
-		char *s, *t;
-
-		for (hp = rflag ? hlast : hfirst;
-		    hp >= hfirst && hp <= hlast; hp += rflag ? -1 : 1) {
-			if (!nflag)
-				shf_fprintf(shl_stdout, "%d",
-				    hist_source->line - (int)(histptr - hp));
-			shf_putc('\t', shl_stdout);
-			/* print multi-line commands correctly */
-			s = *hp;
-			while ((t = strchr(s, '\n'))) {
-				*t = '\0';
-				shf_fprintf(shl_stdout, "%s\n\t", s);
-				*t++ = '\n';
-				s = t;
-			}
-			shf_fprintf(shl_stdout, "%s\n", s);
-		}
-		shf_flush(shl_stdout);
-		return (0);
-	}
-
-	/* Run editor on selected lines, then run resulting commands */
-
-	tf = maketemp(ATEMP, TT_HIST_EDIT, &e->temps);
-	if (!(shf = tf->shf)) {
-		bi_errorf("can't %s temporary file %s: %s",
-		    "create", tf->tffn, cstrerror(errno));
-		return (1);
-	}
-	for (hp = rflag ? hlast : hfirst;
-	    hp >= hfirst && hp <= hlast; hp += rflag ? -1 : 1)
-		shf_fprintf(shf, "%s\n", *hp);
-	if (shf_close(shf) == EOF) {
-		bi_errorf("can't %s temporary file %s: %s",
-		    "write", tf->tffn, cstrerror(errno));
-		return (1);
-	}
-
-	/* Ignore setstr errors here (arbitrary) */
-	setstr(local("_", false), tf->tffn, KSH_RETURN_ERROR);
-
-	/* XXX: source should not get trashed by this.. */
-	{
-		Source *sold = source;
-		int ret;
-
-		ret = command(editor ? editor : TFCEDIT_dollaru, 0);
-		source = sold;
-		if (ret)
-			return (ret);
-	}
-
-	{
-		struct stat statb;
-		XString xs;
-		char *xp;
-		ssize_t n;
-
-		if (!(shf = shf_open(tf->tffn, O_RDONLY, 0, 0))) {
-			bi_errorf("can't %s temporary file %s: %s",
-			    "open", tf->tffn, cstrerror(errno));
-			return (1);
-		}
-
-		if (stat(tf->tffn, &statb) < 0)
-			n = 128;
-		else if ((off_t)statb.st_size > MKSH_MAXHISTFSIZE) {
-			bi_errorf("%s %s too large: %lu", Thistory,
-			    "file", (unsigned long)statb.st_size);
-			goto errout;
-		} else
-			n = (size_t)statb.st_size + 1;
-		Xinit(xs, xp, n, hist_source->areap);
-		while ((n = shf_read(xp, Xnleft(xs, xp), shf)) > 0) {
-			xp += n;
-			if (Xnleft(xs, xp) <= 0)
-				XcheckN(xs, xp, Xlength(xs, xp));
-		}
-		if (n < 0) {
-			bi_errorf("can't %s temporary file %s: %s",
-			    "read", tf->tffn, cstrerror(shf_errno(shf)));
- errout:
-			shf_close(shf);
-			return (1);
-		}
-		shf_close(shf);
-		*xp = '\0';
-		strip_nuls(Xstring(xs, xp), Xlength(xs, xp));
-		return (hist_execute(Xstring(xs, xp)));
-	}
-}
-
-/* Save cmd in history, execute cmd (cmd gets trashed) */
-static int
-hist_execute(char *cmd)
-{
-	static int last_line = -1;
-	Source *sold;
-	int ret;
-	char *p, *q;
-
-	/* Back up over last histsave */
-	if (histptr >= history && last_line != hist_source->line) {
-		hist_source->line--;
-		afree(*histptr, APERM);
-		histptr--;
-		last_line = hist_source->line;
-	}
-
-	for (p = cmd; p; p = q) {
-		if ((q = strchr(p, '\n'))) {
-			/* kill the newline */
-			*q++ = '\0';
-			if (!*q)
-				/* ignore trailing newline */
-				q = NULL;
-		}
-		histsave(&hist_source->line, p, true, true);
-
-		/* POSIX doesn't say this is done... */
-		shellf("%s\n", p);
-		if (q)
-			/* restore \n (trailing \n not restored) */
-			q[-1] = '\n';
-	}
-
-	/*-
-	 * Commands are executed here instead of pushing them onto the
-	 * input 'cause POSIX says the redirection and variable assignments
-	 * in
-	 *	X=y fc -e - 42 2> /dev/null
-	 * are to effect the repeated commands environment.
-	 */
-	/* XXX: source should not get trashed by this.. */
-	sold = source;
-	ret = command(cmd, 0);
-	source = sold;
-	return (ret);
-}
-
-/*
- * get pointer to history given pattern
- * pattern is a number or string
- */
-static char **
-hist_get(const char *str, bool approx, bool allow_cur)
-{
-	char **hp = NULL;
-	int n;
-
-	if (getn(str, &n)) {
-		hp = histptr + (n < 0 ? n : (n - hist_source->line));
-		if ((ptrdiff_t)hp < (ptrdiff_t)history) {
-			if (approx)
-				hp = hist_get_oldest();
-			else {
-				bi_errorf("%s: %s", str, Tnot_in_history);
-				hp = NULL;
-			}
-		} else if ((ptrdiff_t)hp > (ptrdiff_t)histptr) {
-			if (approx)
-				hp = hist_get_newest(allow_cur);
-			else {
-				bi_errorf("%s: %s", str, Tnot_in_history);
-				hp = NULL;
-			}
-		} else if (!allow_cur && hp == histptr) {
-			bi_errorf("%s: %s", str, "invalid range");
-			hp = NULL;
-		}
-	} else {
-		int anchored = *str == '?' ? (++str, 0) : 1;
-
-		/* the -1 is to avoid the current fc command */
-		if ((n = findhist(histptr - history - 1, 0, str, anchored)) < 0)
-			bi_errorf("%s: %s", str, Tnot_in_history);
-		else
-			hp = &history[n];
-	}
-	return (hp);
-}
-
-/* Return a pointer to the newest command in the history */
-char **
-hist_get_newest(bool allow_cur)
-{
-	if (histptr < history || (!allow_cur && histptr == history)) {
-		bi_errorf("no history (yet)");
-		return (NULL);
-	}
-	return (allow_cur ? histptr : histptr - 1);
-}
-
-/* Return a pointer to the oldest command in the history */
-static char **
-hist_get_oldest(void)
-{
-	if (histptr <= history) {
-		bi_errorf("no history (yet)");
-		return (NULL);
-	}
-	return (history);
-}
-
-#if !defined(MKSH_NO_CMDLINE_EDITING) && !MKSH_S_NOVI
-/* current position in history[] */
-static char **current;
-
-/*
- * Return the current position.
- */
-char **
-histpos(void)
-{
-	return (current);
-}
-
-int
-histnum(int n)
-{
-	int last = histptr - history;
-
-	if (n < 0 || n >= last) {
-		current = histptr;
-		return (last);
-	} else {
-		current = &history[n];
-		return (n);
-	}
-}
-#endif
-
-/*
- * This will become unnecessary if hist_get is modified to allow
- * searching from positions other than the end, and in either
- * direction.
- */
-int
-findhist(int start, int fwd, const char *str, int anchored)
-{
-	char **hp;
-	int maxhist = histptr - history;
-	int incr = fwd ? 1 : -1;
-	size_t len = strlen(str);
-
-	if (start < 0 || start >= maxhist)
-		start = maxhist;
-
-	hp = &history[start];
-	for (; hp >= history && hp <= histptr; hp += incr)
-		if ((anchored && strncmp(*hp, str, len) == 0) ||
-		    (!anchored && strstr(*hp, str)))
-			return (hp - history);
-
-	return (-1);
-}
-
-/*
- * set history; this means reallocating the dataspace
- */
-void
-sethistsize(mksh_ari_t n)
-{
-	if (n > 0 && n != histsize) {
-		int cursize = histptr - history;
-
-		/* save most recent history */
-		if (n < cursize) {
-			memmove(history, histptr - n + 1, n * sizeof(char *));
-			cursize = n - 1;
-		}
-
-		history = aresize2(history, n, sizeof(char *), APERM);
-
-		histsize = n;
-		histptr = history + cursize;
-	}
-}
-
-#if HAVE_PERSISTENT_HISTORY
-/*
- * set history file; this can mean reloading/resetting/starting
- * history file maintenance
- */
-void
-sethistfile(const char *name)
-{
-	/* if not started then nothing to do */
-	if (hstarted == false)
-		return;
-
-	/* if the name is the same as the name we have */
-	if (hname && strcmp(hname, name) == 0)
-		return;
-
-	/*
-	 * it's a new name - possibly
-	 */
-	if (histfd != -1) {
-		/* yes the file is open */
-		(void)close(histfd);
-		histfd = -1;
-		histfsize = 0;
-		afree(hname, APERM);
-		hname = NULL;
-		/* let's reset the history */
-		histptr = history - 1;
-		hist_source->line = 0;
-	}
-
-	hist_init(hist_source);
-}
-#endif
-
-/*
- * initialise the history vector
- */
-void
-init_histvec(void)
-{
-	if (history == (char **)NULL) {
-		histsize = MKSH_DEFHISTSIZE;
-		history = alloc2(histsize, sizeof(char *), APERM);
-		histptr = history - 1;
-	}
-}
-
-
-/*
- * It turns out that there is a lot of ghastly hackery here
- */
-
-#if !defined(MKSH_SMALL) && HAVE_PERSISTENT_HISTORY
-/* do not save command in history but possibly sync */
-bool
-histsync(void)
-{
-	bool changed = false;
-
-	if (histfd != -1) {
-		int lno = hist_source->line;
-
-		hist_source->line++;
-		writehistfile(0, NULL);
-		hist_source->line--;
-
-		if (lno != hist_source->line)
-			changed = true;
-	}
-
-	return (changed);
-}
-#endif
-
-/*
- * save command in history
- */
-void
-histsave(int *lnp, const char *cmd, bool dowrite MKSH_A_UNUSED, bool ignoredups)
-{
-	char **hp;
-	char *c, *cp;
-
-	mkssert(cmd != NULL);
-	strdupx(c, cmd, APERM);
-	if ((cp = strchr(c, '\n')) != NULL)
-		*cp = '\0';
-
-	if (ignoredups && !strcmp(c, *histptr)
-#if !defined(MKSH_SMALL) && HAVE_PERSISTENT_HISTORY
-	    && !histsync()
-#endif
-	    ) {
-		afree(c, APERM);
-		return;
-	}
-	++*lnp;
-
-#if HAVE_PERSISTENT_HISTORY
-	if (dowrite && histfd != -1)
-		writehistfile(*lnp, c);
-#endif
-
-	hp = histptr;
-
-	if (++hp >= history + histsize) {
-		/* remove oldest command */
-		afree(*history, APERM);
-		for (hp = history; hp < history + histsize - 1; hp++)
-			hp[0] = hp[1];
-	}
-	*hp = c;
-	histptr = hp;
-}
-
-/*
- * Write history data to a file nominated by HISTFILE;
- * if HISTFILE is unset then history still happens, but
- * the data is not written to a file. All copies of ksh
- * looking at the file will maintain the same history.
- * This is ksh behaviour.
- *
- * This stuff uses mmap()
- *
- * This stuff is so totally broken it must eventually be
- * redesigned, without mmap, better checks, support for
- * larger files, etc. and handle partially corrupted files
- */
-
-/*-
- * Open a history file
- * Format is:
- * Bytes 1, 2:
- *	HMAGIC - just to check that we are dealing with the correct object
- * Then follows a number of stored commands
- * Each command is
- *	<command byte><command number(4 octets, big endian)><bytes><NUL>
- */
-#define HMAGIC1		0xAB
-#define HMAGIC2		0xCD
-#define COMMAND		0xFF
-
-#if HAVE_PERSISTENT_HISTORY
-static const unsigned char sprinkle[2] = { HMAGIC1, HMAGIC2 };
-#endif
-
-void
-hist_init(Source *s)
-{
-#if HAVE_PERSISTENT_HISTORY
-	unsigned char *base;
-	int lines, fd;
-	enum { hist_init_first, hist_init_retry, hist_init_restore } hs;
-#endif
-
-	if (Flag(FTALKING) == 0)
-		return;
-
-	hstarted = true;
-	hist_source = s;
-
-#if HAVE_PERSISTENT_HISTORY
-	if ((hname = str_val(global("HISTFILE"))) == NULL)
-		return;
-	strdupx(hname, hname, APERM);
-	hs = hist_init_first;
-
- retry:
-	/* we have a file and are interactive */
-	if ((fd = open(hname, O_RDWR | O_CREAT | O_APPEND, 0600)) < 0)
-		return;
-
-	histfd = savefd(fd);
-	if (histfd != fd)
-		close(fd);
-
-	mksh_lockfd(histfd);
-
-	histfsize = lseek(histfd, (off_t)0, SEEK_END);
-	if (histfsize > MKSH_MAXHISTFSIZE || hs == hist_init_restore) {
-		/* we ignore too large files but still append to them */
-		/* we also don't need to re-read after truncation */
-		goto hist_init_tail;
-	} else if (histfsize > 2) {
-		/* we have some data, check its validity */
-		base = (void *)mmap(NULL, (size_t)histfsize, PROT_READ,
-		    MAP_FILE | MAP_PRIVATE, histfd, (off_t)0);
-		if (base == (unsigned char *)MAP_FAILED)
-			goto hist_init_fail;
-		if (base[0] != HMAGIC1 || base[1] != HMAGIC2) {
-			munmap(caddr_cast(base), (size_t)histfsize);
-			goto hist_init_fail;
-		}
-		/* load _all_ data */
-		lines = histload(hist_source, base + 2, (size_t)histfsize - 2);
-		munmap(caddr_cast(base), (size_t)histfsize);
-		/* check if the file needs to be truncated */
-		if (lines > histsize && histptr >= history) {
-			/* you're fucked up with the current code, trust me */
-			char *nhname, **hp;
-			struct stat sb;
-
-			/* create temporary file */
-			nhname = shf_smprintf("%s.%d", hname, (int)procpid);
-			if ((fd = open(nhname, O_RDWR | O_CREAT | O_TRUNC |
-			    O_EXCL, 0600)) < 0) {
-				/* just don't truncate then, meh. */
-				goto hist_trunc_dont;
-			}
-			if (fstat(histfd, &sb) >= 0 &&
-			    chown(nhname, sb.st_uid, sb.st_gid)) {
-				/* abort the truncation then, meh. */
-				goto hist_trunc_abort;
-			}
-			/* we definitively want some magic in that file */
-			if (write(fd, sprinkle, 2) != 2)
-				goto hist_trunc_abort;
-			/* and of course the entries */
-			hp = history;
-			while (hp < histptr) {
-				if (!writehistline(fd,
-				    s->line - (histptr - hp), *hp))
-					goto hist_trunc_abort;
-				++hp;
-			}
-			/* now unlock, close both, rename, rinse, repeat */
-			close(fd);
-			fd = -1;
-			hist_finish();
-			if (rename(nhname, hname) < 0) {
- hist_trunc_abort:
-				if (fd != -1)
-					close(fd);
-				unlink(nhname);
-				if (fd != -1)
-					goto hist_trunc_dont;
-				/* darn! restore histfd and pray */
-			}
-			hs = hist_init_restore;
- hist_trunc_dont:
-			afree(nhname, ATEMP);
-			if (hs == hist_init_restore)
-				goto retry;
-		}
-	} else if (histfsize != 0) {
-		/* negative or too small... */
- hist_init_fail:
-		/* ... or mmap failed or illegal */
-		hist_finish();
-		/* nuke the bogus file then retry, at most once */
-		if (!unlink(hname) && hs != hist_init_retry) {
-			hs = hist_init_retry;
-			goto retry;
-		}
-		if (hs != hist_init_retry)
-			bi_errorf("can't %s %s: %s",
-			    "unlink HISTFILE", hname, cstrerror(errno));
-		histfsize = 0;
-		return;
-	} else {
-		/* size 0, add magic to the history file */
-		if (write(histfd, sprinkle, 2) != 2) {
-			hist_finish();
-			return;
-		}
-	}
-	histfsize = lseek(histfd, (off_t)0, SEEK_END);
- hist_init_tail:
-	mksh_unlkfd(histfd);
-#endif
-}
-
-#if HAVE_PERSISTENT_HISTORY
-/*
- * load the history structure from the stored data
- */
-static int
-histload(Source *s, unsigned char *base, size_t bytes)
-{
-	int lno = 0, lines = 0;
-	unsigned char *cp;
-
- histload_loop:
-	/* !bytes check as some systems (older FreeBSDs) have buggy memchr */
-	if (!bytes || (cp = memchr(base, COMMAND, bytes)) == NULL)
-		return (lines);
-	/* advance base pointer past COMMAND byte */
-	bytes -= ++cp - base;
-	base = cp;
-	/* if there is no full string left, don't bother with the rest */
-	if (bytes < 5 || (cp = memchr(base + 4, '\0', bytes - 4)) == NULL)
-		return (lines);
-	/* load the stored line number */
-	lno = ((base[0] & 0xFF) << 24) | ((base[1] & 0xFF) << 16) |
-	    ((base[2] & 0xFF) << 8) | (base[3] & 0xFF);
-	/* store away the found line (@base[4]) */
-	++lines;
-	if (histptr >= history && lno - 1 != s->line) {
-		/* a replacement? */
-		char **hp;
-
-		if (lno >= s->line - (histptr - history) && lno <= s->line) {
-			hp = &histptr[lno - s->line];
-			if (*hp)
-				afree(*hp, APERM);
-			strdupx(*hp, (char *)(base + 4), APERM);
-		}
-	} else {
-		s->line = lno--;
-		histsave(&lno, (char *)(base + 4), false, false);
-	}
-	/* advance base pointer past NUL */
-	bytes -= ++cp - base;
-	base = cp;
-	/* repeat until no more */
-	goto histload_loop;
-}
-
-/*
- * write a command to the end of the history file
- *
- * This *MAY* seem easy but it's also necessary to check
- * that the history file has not changed in size.
- * If it has - then some other shell has written to it and
- * we should (re)read those commands to update our history
- */
-static void
-writehistfile(int lno, const char *cmd)
-{
-	off_t sizenow;
-	size_t bytes;
-	unsigned char *base, *news;
-
-	mksh_lockfd(histfd);
-	sizenow = lseek(histfd, (off_t)0, SEEK_END);
-	if (sizenow < histfsize) {
-		/* the file has shrunk; give up */
-		goto bad;
-	}
-	if (
-		/* ignore changes when the file is too large */
-		sizenow <= MKSH_MAXHISTFSIZE
-	    &&
-		/* the size has changed, we need to do read updates */
-		sizenow > histfsize
-	    ) {
-		/* both sizenow and histfsize are <= MKSH_MAXHISTFSIZE */
-		bytes = (size_t)(sizenow - histfsize);
-		base = (void *)mmap(NULL, (size_t)sizenow, PROT_READ,
-		    MAP_FILE | MAP_PRIVATE, histfd, (off_t)0);
-		if (base == (unsigned char *)MAP_FAILED)
-			goto bad;
-		news = base + (size_t)histfsize;
-		if (*news == COMMAND) {
-			hist_source->line--;
-			histload(hist_source, news, bytes);
-			hist_source->line++;
-			lno = hist_source->line;
-		} else
-			bytes = 0;
-		munmap(caddr_cast(base), (size_t)sizenow);
-		if (!bytes)
-			goto bad;
-	}
-	if (cmd && !writehistline(histfd, lno, cmd)) {
- bad:
-		hist_finish();
-		return;
-	}
-	histfsize = lseek(histfd, (off_t)0, SEEK_END);
-	mksh_unlkfd(histfd);
-}
-
-static int
-writehistline(int fd, int lno, const char *cmd)
-{
-	ssize_t n;
-	unsigned char hdr[5];
-
-	hdr[0] = COMMAND;
-	hdr[1] = (lno >> 24) & 0xFF;
-	hdr[2] = (lno >> 16) & 0xFF;
-	hdr[3] = (lno >> 8) & 0xFF;
-	hdr[4] = lno & 0xFF;
-	n = strlen(cmd) + 1;
-	return (write(fd, hdr, 5) == 5 && write(fd, cmd, n) == n);
-}
-
-void
-hist_finish(void)
-{
-	if (histfd >= 0) {
-		mksh_unlkfd(histfd);
-		(void)close(histfd);
-	}
-	histfd = -1;
-}
-#endif
-
-
-#if !HAVE_SYS_SIGNAME
-static const struct mksh_sigpair {
-	const char * const name;
-	int nr;
-} mksh_sigpairs[] = {
-#include "signames.inc"
-	{ NULL, 0 }
-};
-#endif
-
-#if HAVE_SYS_SIGLIST
-#if !HAVE_SYS_SIGLIST_DECL
-extern const char * const sys_siglist[];
-#endif
-#endif
-
-void
-inittraps(void)
-{
-	int i;
-	const char *cs;
-
-	trap_exstat = -1;
-
-	/* Populate sigtraps based on sys_signame and sys_siglist. */
-	for (i = 0; i <= NSIG; i++) {
-		sigtraps[i].signal = i;
-		if (i == ksh_SIGERR) {
-			sigtraps[i].name = "ERR";
-			sigtraps[i].mess = "Error handler";
-		} else {
-#if HAVE_SYS_SIGNAME
-			cs = sys_signame[i];
-#else
-			const struct mksh_sigpair *pair = mksh_sigpairs;
-			while ((pair->nr != i) && (pair->name != NULL))
-				++pair;
-			cs = pair->name;
-#endif
-			if ((cs == NULL) ||
-			    (cs[0] == '\0'))
-				sigtraps[i].name = shf_smprintf("%d", i);
-			else {
-				char *s;
-
-				/* this is not optimal, what about SIGSIG1? */
-				if ((cs[0] & 0xDF) == 'S' &&
-				    (cs[1] & 0xDF) == 'I' &&
-				    (cs[2] & 0xDF) == 'G' &&
-				    cs[3] != '\0') {
-					/* skip leading "SIG" */
-					cs += 3;
-				}
-				strdupx(s, cs, APERM);
-				sigtraps[i].name = s;
-				while ((*s = ksh_toupper(*s)))
-					++s;
-			}
-#if HAVE_SYS_SIGLIST
-			sigtraps[i].mess = sys_siglist[i];
-#elif HAVE_STRSIGNAL
-			sigtraps[i].mess = strsignal(i);
-#else
-			sigtraps[i].mess = NULL;
-#endif
-			if ((sigtraps[i].mess == NULL) ||
-			    (sigtraps[i].mess[0] == '\0'))
-				sigtraps[i].mess = shf_smprintf("%s %d",
-				    "Signal", i);
-		}
-	}
-	/* our name for signal 0 */
-	sigtraps[ksh_SIGEXIT].name = "EXIT";
-
-	(void)sigemptyset(&Sigact_ign.sa_mask);
-	Sigact_ign.sa_flags = 0; /* interruptible */
-	Sigact_ign.sa_handler = SIG_IGN;
-
-	sigtraps[SIGINT].flags |= TF_DFL_INTR | TF_TTY_INTR;
-	sigtraps[SIGQUIT].flags |= TF_DFL_INTR | TF_TTY_INTR;
-	/* SIGTERM is not fatal for interactive */
-	sigtraps[SIGTERM].flags |= TF_DFL_INTR;
-	sigtraps[SIGHUP].flags |= TF_FATAL;
-	sigtraps[SIGCHLD].flags |= TF_SHELL_USES;
-
-	/* these are always caught so we can clean up any temporary files. */
-	setsig(&sigtraps[SIGINT], trapsig, SS_RESTORE_ORIG);
-	setsig(&sigtraps[SIGQUIT], trapsig, SS_RESTORE_ORIG);
-	setsig(&sigtraps[SIGTERM], trapsig, SS_RESTORE_ORIG);
-	setsig(&sigtraps[SIGHUP], trapsig, SS_RESTORE_ORIG);
-}
-
-static void alarm_catcher(int sig);
-
-void
-alarm_init(void)
-{
-	sigtraps[SIGALRM].flags |= TF_SHELL_USES;
-	setsig(&sigtraps[SIGALRM], alarm_catcher,
-		SS_RESTORE_ORIG|SS_FORCE|SS_SHTRAP);
-}
-
-/* ARGSUSED */
-static void
-alarm_catcher(int sig MKSH_A_UNUSED)
-{
-	/* this runs inside interrupt context, with errno saved */
-
-	if (ksh_tmout_state == TMOUT_READING) {
-		int left = alarm(0);
-
-		if (left == 0) {
-			ksh_tmout_state = TMOUT_LEAVING;
-			intrsig = 1;
-		} else
-			alarm(left);
-	}
-}
-
-Trap *
-gettrap(const char *cs, bool igncase)
-{
-	int i;
-	Trap *p;
-	char *as;
-
-	if (ksh_isdigit(*cs)) {
-		return ((getn(cs, &i) && 0 <= i && i < NSIG) ?
-		    (&sigtraps[i]) : NULL);
-	}
-
-	/* this breaks SIGSIG1, but we do that above anyway */
-	if ((cs[0] & 0xDF) == 'S' &&
-	    (cs[1] & 0xDF) == 'I' &&
-	    (cs[2] & 0xDF) == 'G' &&
-	    cs[3] != '\0') {
-		/* skip leading "SIG" */
-		cs += 3;
-	}
-	if (igncase) {
-		char *s;
-
-		strdupx(as, cs, ATEMP);
-		cs = s = as;
-		while ((*s = ksh_toupper(*s)))
-			++s;
-	} else
-		as = NULL;
-
-	p = sigtraps;
-	for (i = 0; i <= NSIG; i++) {
-		if (!strcmp(p->name, cs))
-			goto found;
-		++p;
-	}
-	p = NULL;
- found:
-	afree(as, ATEMP);
-	return (p);
-}
-
-/*
- * trap signal handler
- */
-void
-trapsig(int i)
-{
-	Trap *p = &sigtraps[i];
-	int eno = errno;
-
-	trap = p->set = 1;
-	if (p->flags & TF_DFL_INTR)
-		intrsig = 1;
-	if ((p->flags & TF_FATAL) && !p->trap) {
-		fatal_trap = 1;
-		intrsig = 1;
-	}
-	if (p->shtrap)
-		(*p->shtrap)(i);
-	errno = eno;
-}
-
-/*
- * called when we want to allow the user to ^C out of something - won't
- * work if user has trapped SIGINT.
- */
-void
-intrcheck(void)
-{
-	if (intrsig)
-		runtraps(TF_DFL_INTR|TF_FATAL);
-}
-
-/*
- * called after EINTR to check if a signal with normally causes process
- * termination has been received.
- */
-int
-fatal_trap_check(void)
-{
-	int i;
-	Trap *p;
-
-	/* todo: should check if signal is fatal, not the TF_DFL_INTR flag */
-	for (p = sigtraps, i = NSIG+1; --i >= 0; p++)
-		if (p->set && (p->flags & (TF_DFL_INTR|TF_FATAL)))
-			/* return value is used as an exit code */
-			return (128 + p->signal);
-	return (0);
-}
-
-/*
- * Returns the signal number of any pending traps: ie, a signal which has
- * occurred for which a trap has been set or for which the TF_DFL_INTR flag
- * is set.
- */
-int
-trap_pending(void)
-{
-	int i;
-	Trap *p;
-
-	for (p = sigtraps, i = NSIG+1; --i >= 0; p++)
-		if (p->set && ((p->trap && p->trap[0]) ||
-		    ((p->flags & (TF_DFL_INTR|TF_FATAL)) && !p->trap)))
-			return (p->signal);
-	return (0);
-}
-
-/*
- * run any pending traps. If intr is set, only run traps that
- * can interrupt commands.
- */
-void
-runtraps(int flag)
-{
-	int i;
-	Trap *p;
-
-	if (ksh_tmout_state == TMOUT_LEAVING) {
-		ksh_tmout_state = TMOUT_EXECUTING;
-		warningf(false, "timed out waiting for input");
-		unwind(LEXIT);
-	} else
-		/*
-		 * XXX: this means the alarm will have no effect if a trap
-		 * is caught after the alarm() was started...not good.
-		 */
-		ksh_tmout_state = TMOUT_EXECUTING;
-	if (!flag)
-		trap = 0;
-	if (flag & TF_DFL_INTR)
-		intrsig = 0;
-	if (flag & TF_FATAL)
-		fatal_trap = 0;
-	++trap_nested;
-	for (p = sigtraps, i = NSIG+1; --i >= 0; p++)
-		if (p->set && (!flag ||
-		    ((p->flags & flag) && p->trap == NULL)))
-			runtrap(p, false);
-	if (!--trap_nested)
-		runtrap(NULL, true);
-}
-
-void
-runtrap(Trap *p, bool is_last)
-{
-	int old_changed = 0, i;
-	char *trapstr;
-
-	if (p == NULL)
-		/* just clean up, see runtraps() above */
-		goto donetrap;
-	i = p->signal;
-	trapstr = p->trap;
-	p->set = 0;
-	if (trapstr == NULL) {
-		/* SIG_DFL */
-		if (p->flags & TF_FATAL) {
-			/* eg, SIGHUP */
-			exstat = (int)ksh_min(128U + (unsigned)i, 255U);
-			unwind(LLEAVE);
-		}
-		if (p->flags & TF_DFL_INTR) {
-			/* eg, SIGINT, SIGQUIT, SIGTERM, etc. */
-			exstat = (int)ksh_min(128U + (unsigned)i, 255U);
-			unwind(LINTR);
-		}
-		goto donetrap;
-	}
-	if (trapstr[0] == '\0')
-		/* SIG_IGN */
-		goto donetrap;
-	if (i == ksh_SIGEXIT || i == ksh_SIGERR) {
-		/* avoid recursion on these */
-		old_changed = p->flags & TF_CHANGED;
-		p->flags &= ~TF_CHANGED;
-		p->trap = NULL;
-	}
-	if (trap_exstat == -1)
-		trap_exstat = exstat & 0xFF;
-	/*
-	 * Note: trapstr is fully parsed before anything is executed, thus
-	 * no problem with afree(p->trap) in settrap() while still in use.
-	 */
-	command(trapstr, current_lineno);
-	if (i == ksh_SIGEXIT || i == ksh_SIGERR) {
-		if (p->flags & TF_CHANGED)
-			/* don't clear TF_CHANGED */
-			afree(trapstr, APERM);
-		else
-			p->trap = trapstr;
-		p->flags |= old_changed;
-	}
-
- donetrap:
-	/* we're the last trap of a sequence executed */
-	if (is_last && trap_exstat != -1) {
-		exstat = trap_exstat;
-		trap_exstat = -1;
-	}
-}
-
-/* clear pending traps and reset user's trap handlers; used after fork(2) */
-void
-cleartraps(void)
-{
-	int i;
-	Trap *p;
-
-	trap = 0;
-	intrsig = 0;
-	fatal_trap = 0;
-	for (i = NSIG+1, p = sigtraps; --i >= 0; p++) {
-		p->set = 0;
-		if ((p->flags & TF_USER_SET) && (p->trap && p->trap[0]))
-			settrap(p, NULL);
-	}
-}
-
-/* restore signals just before an exec(2) */
-void
-restoresigs(void)
-{
-	int i;
-	Trap *p;
-
-	for (i = NSIG+1, p = sigtraps; --i >= 0; p++)
-		if (p->flags & (TF_EXEC_IGN|TF_EXEC_DFL))
-			setsig(p, (p->flags & TF_EXEC_IGN) ? SIG_IGN : SIG_DFL,
-			    SS_RESTORE_CURR|SS_FORCE);
-}
-
-void
-settrap(Trap *p, const char *s)
-{
-	sig_t f;
-
-	if (p->trap)
-		afree(p->trap, APERM);
-	/* handles s == NULL */
-	strdupx(p->trap, s, APERM);
-	p->flags |= TF_CHANGED;
-	f = !s ? SIG_DFL : s[0] ? trapsig : SIG_IGN;
-
-	p->flags |= TF_USER_SET;
-	if ((p->flags & (TF_DFL_INTR|TF_FATAL)) && f == SIG_DFL)
-		f = trapsig;
-	else if (p->flags & TF_SHELL_USES) {
-		if (!(p->flags & TF_ORIG_IGN) || Flag(FTALKING)) {
-			/* do what user wants at exec time */
-			p->flags &= ~(TF_EXEC_IGN|TF_EXEC_DFL);
-			if (f == SIG_IGN)
-				p->flags |= TF_EXEC_IGN;
-			else
-				p->flags |= TF_EXEC_DFL;
-		}
-
-		/*
-		 * assumes handler already set to what shell wants it
-		 * (normally trapsig, but could be j_sigchld() or SIG_IGN)
-		 */
-		return;
-	}
-
-	/* todo: should we let user know signal is ignored? how? */
-	setsig(p, f, SS_RESTORE_CURR|SS_USER);
-}
-
-/*
- * Called by c_print() when writing to a co-process to ensure SIGPIPE won't
- * kill shell (unless user catches it and exits)
- */
-int
-block_pipe(void)
-{
-	int restore_dfl = 0;
-	Trap *p = &sigtraps[SIGPIPE];
-
-	if (!(p->flags & (TF_ORIG_IGN|TF_ORIG_DFL))) {
-		setsig(p, SIG_IGN, SS_RESTORE_CURR);
-		if (p->flags & TF_ORIG_DFL)
-			restore_dfl = 1;
-	} else if (p->cursig == SIG_DFL) {
-		setsig(p, SIG_IGN, SS_RESTORE_CURR);
-		/* restore to SIG_DFL */
-		restore_dfl = 1;
-	}
-	return (restore_dfl);
-}
-
-/* Called by c_print() to undo whatever block_pipe() did */
-void
-restore_pipe(int restore_dfl)
-{
-	if (restore_dfl)
-		setsig(&sigtraps[SIGPIPE], SIG_DFL, SS_RESTORE_CURR);
-}
-
-/*
- * Set action for a signal. Action may not be set if original
- * action was SIG_IGN, depending on the value of flags and FTALKING.
- */
-int
-setsig(Trap *p, sig_t f, int flags)
-{
-	struct sigaction sigact;
-
-	if (p->signal == ksh_SIGEXIT || p->signal == ksh_SIGERR)
-		return (1);
-
-	memset(&sigact, 0, sizeof(sigact));
-
-	/*
-	 * First time setting this signal? If so, get and note the current
-	 * setting.
-	 */
-	if (!(p->flags & (TF_ORIG_IGN|TF_ORIG_DFL))) {
-		sigaction(p->signal, &Sigact_ign, &sigact);
-		p->flags |= sigact.sa_handler == SIG_IGN ?
-		    TF_ORIG_IGN : TF_ORIG_DFL;
-		p->cursig = SIG_IGN;
-	}
-
-	/*-
-	 * Generally, an ignored signal stays ignored, except if
-	 *	- the user of an interactive shell wants to change it
-	 *	- the shell wants for force a change
-	 */
-	if ((p->flags & TF_ORIG_IGN) && !(flags & SS_FORCE) &&
-	    (!(flags & SS_USER) || !Flag(FTALKING)))
-		return (0);
-
-	setexecsig(p, flags & SS_RESTORE_MASK);
-
-	/*
-	 * This is here 'cause there should be a way of clearing
-	 * shtraps, but don't know if this is a sane way of doing
-	 * it. At the moment, all users of shtrap are lifetime
-	 * users (SIGALRM, SIGCHLD, SIGWINCH).
-	 */
-	if (!(flags & SS_USER))
-		p->shtrap = (sig_t)NULL;
-	if (flags & SS_SHTRAP) {
-		p->shtrap = f;
-		f = trapsig;
-	}
-
-	if (p->cursig != f) {
-		p->cursig = f;
-		(void)sigemptyset(&sigact.sa_mask);
-		/* interruptible */
-		sigact.sa_flags = 0;
-		sigact.sa_handler = f;
-		sigaction(p->signal, &sigact, NULL);
-	}
-
-	return (1);
-}
-
-/* control what signal is set to before an exec() */
-void
-setexecsig(Trap *p, int restore)
-{
-	/* XXX debugging */
-	if (!(p->flags & (TF_ORIG_IGN|TF_ORIG_DFL)))
-		internal_errorf("setexecsig: unset signal %d(%s)",
-		    p->signal, p->name);
-
-	/* restore original value for exec'd kids */
-	p->flags &= ~(TF_EXEC_IGN|TF_EXEC_DFL);
-	switch (restore & SS_RESTORE_MASK) {
-	case SS_RESTORE_CURR:
-		/* leave things as they currently are */
-		break;
-	case SS_RESTORE_ORIG:
-		p->flags |= p->flags & TF_ORIG_IGN ? TF_EXEC_IGN : TF_EXEC_DFL;
-		break;
-	case SS_RESTORE_DFL:
-		p->flags |= TF_EXEC_DFL;
-		break;
-	case SS_RESTORE_IGN:
-		p->flags |= TF_EXEC_IGN;
-		break;
-	}
-}
-
-#if HAVE_PERSISTENT_HISTORY || defined(DF)
-/*
- * File descriptor locking and unlocking functions.
- * Could use some error handling, but hey, this is only
- * advisory locking anyway, will often not work over NFS,
- * and you are SOL if this fails...
- */
-
-void
-mksh_lockfd(int fd)
-{
-#if defined(__OpenBSD__)
-	/* flock is not interrupted by signals */
-	(void)flock(fd, LOCK_EX);
-#elif HAVE_FLOCK
-	int rv;
-
-	/* e.g. on Linux */
-	do {
-		rv = flock(fd, LOCK_EX);
-	} while (rv == 1 && errno == EINTR);
-#elif HAVE_LOCK_FCNTL
-	int rv;
-	struct flock lks;
-
-	memset(&lks, 0, sizeof(lks));
-	lks.l_type = F_WRLCK;
-	do {
-		rv = fcntl(fd, F_SETLKW, &lks);
-	} while (rv == 1 && errno == EINTR);
-#endif
-}
-
-/* designed to not define mksh_unlkfd if none triggered */
-#if HAVE_FLOCK
-void
-mksh_unlkfd(int fd)
-{
-	(void)flock(fd, LOCK_UN);
-}
-#elif HAVE_LOCK_FCNTL
-void
-mksh_unlkfd(int fd)
-{
-	struct flock lks;
-
-	memset(&lks, 0, sizeof(lks));
-	lks.l_type = F_UNLCK;
-	(void)fcntl(fd, F_SETLKW, &lks);
-}
-#endif
-#endif

Copied: vendor/MirOS/mksh/R50/histrap.c (from rev 6707, vendor/MirOS/mksh/dist/histrap.c)
===================================================================
--- vendor/MirOS/mksh/R50/histrap.c	                        (rev 0)
+++ vendor/MirOS/mksh/R50/histrap.c	2014-07-01 12:01:17 UTC (rev 6708)
@@ -0,0 +1,1514 @@
+/*	$OpenBSD: history.c,v 1.39 2010/05/19 17:36:08 jasper Exp $	*/
+/*	$OpenBSD: trap.c,v 1.23 2010/05/19 17:36:08 jasper Exp $	*/
+
+/*-
+ * Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
+ *		 2011, 2012, 2014
+ *	Thorsten Glaser <tg at mirbsd.org>
+ *
+ * Provided that these terms and disclaimer and all copyright notices
+ * are retained or reproduced in an accompanying document, permission
+ * is granted to deal in this work without restriction, including un-
+ * limited rights to use, publicly perform, distribute, sell, modify,
+ * merge, give away, or sublicence.
+ *
+ * This work is provided "AS IS" and WITHOUT WARRANTY of any kind, to
+ * the utmost extent permitted by applicable law, neither express nor
+ * implied; without malicious intent or gross negligence. In no event
+ * may a licensor, author or contributor be held liable for indirect,
+ * direct, other damage, loss, or other issues arising in any way out
+ * of dealing in the work, even if advised of the possibility of such
+ * damage or existence of a defect, except proven that it results out
+ * of said person's immediate fault when using the work as intended.
+ */
+
+#include "sh.h"
+#if HAVE_SYS_FILE_H
+#include <sys/file.h>
+#endif
+
+__RCSID("$MirOS: src/bin/mksh/histrap.c,v 1.134 2014/06/09 13:25:53 tg Exp $");
+
+Trap sigtraps[NSIG + 1];
+static struct sigaction Sigact_ign;
+
+#if HAVE_PERSISTENT_HISTORY
+static int histload(Source *, unsigned char *, size_t);
+static int writehistline(int, int, const char *);
+static void writehistfile(int, const char *);
+#endif
+
+static int hist_execute(char *);
+static char **hist_get(const char *, bool, bool);
+static char **hist_get_oldest(void);
+
+static bool hstarted;		/* set after hist_init() called */
+static Source *hist_source;
+
+#if HAVE_PERSISTENT_HISTORY
+/*XXX imake style */
+#if defined(__linux)
+#define caddr_cast(x)	((void *)(x))
+#else
+#define caddr_cast(x)	((caddr_t)(x))
+#endif
+
+/* several OEs do not have these constants */
+#ifndef MAP_FAILED
+#define MAP_FAILED	caddr_cast(-1)
+#endif
+
+/* some OEs need the default mapping type specified */
+#ifndef MAP_FILE
+#define MAP_FILE	0
+#endif
+
+/* current history file: name, fd, size */
+static char *hname;
+static int histfd = -1;
+static off_t histfsize;
+#endif
+
+static const char Tnot_in_history[] = "not in history";
+#define Thistory (Tnot_in_history + 7)
+
+static const char TFCEDIT_dollaru[] = "${FCEDIT:-/bin/ed} $_";
+#define Tspdollaru (TFCEDIT_dollaru + 18)
+
+/* HISTSIZE default: size of saved history, persistent or standard */
+#ifdef MKSH_SMALL
+#define MKSH_DEFHISTSIZE	255
+#else
+#define MKSH_DEFHISTSIZE	2047
+#endif
+/* maximum considered size of persistent history file */
+#define MKSH_MAXHISTFSIZE	((off_t)1048576 * 96)
+
+int
+c_fc(const char **wp)
+{
+	struct shf *shf;
+	struct temp *tf;
+	bool gflag = false, lflag = false, nflag = false, rflag = false,
+	    sflag = false;
+	int optc;
+	const char *p, *first = NULL, *last = NULL;
+	char **hfirst, **hlast, **hp, *editor = NULL;
+
+	if (!Flag(FTALKING_I)) {
+		bi_errorf("history %ss not available", Tfunction);
+		return (1);
+	}
+
+	while ((optc = ksh_getopt(wp, &builtin_opt,
+	    "e:glnrs0,1,2,3,4,5,6,7,8,9,")) != -1)
+		switch (optc) {
+
+		case 'e':
+			p = builtin_opt.optarg;
+			if (ksh_isdash(p))
+				sflag = true;
+			else {
+				size_t len = strlen(p);
+
+				/* almost certainly not overflowing */
+				editor = alloc(len + 4, ATEMP);
+				memcpy(editor, p, len);
+				memcpy(editor + len, Tspdollaru, 4);
+			}
+			break;
+
+		/* non-AT&T ksh */
+		case 'g':
+			gflag = true;
+			break;
+
+		case 'l':
+			lflag = true;
+			break;
+
+		case 'n':
+			nflag = true;
+			break;
+
+		case 'r':
+			rflag = true;
+			break;
+
+		/* POSIX version of -e - */
+		case 's':
+			sflag = true;
+			break;
+
+		/* kludge city - accept -num as -- -num (kind of) */
+		case '0': case '1': case '2': case '3': case '4':
+		case '5': case '6': case '7': case '8': case '9':
+			p = shf_smprintf("-%c%s",
+					optc, builtin_opt.optarg);
+			if (!first)
+				first = p;
+			else if (!last)
+				last = p;
+			else {
+				bi_errorf("too many arguments");
+				return (1);
+			}
+			break;
+
+		case '?':
+			return (1);
+		}
+	wp += builtin_opt.optind;
+
+	/* Substitute and execute command */
+	if (sflag) {
+		char *pat = NULL, *rep = NULL, *line;
+
+		if (editor || lflag || nflag || rflag) {
+			bi_errorf("can't use -e, -l, -n, -r with -s (-e -)");
+			return (1);
+		}
+
+		/* Check for pattern replacement argument */
+		if (*wp && **wp && (p = cstrchr(*wp + 1, '='))) {
+			strdupx(pat, *wp, ATEMP);
+			rep = pat + (p - *wp);
+			*rep++ = '\0';
+			wp++;
+		}
+		/* Check for search prefix */
+		if (!first && (first = *wp))
+			wp++;
+		if (last || *wp) {
+			bi_errorf("too many arguments");
+			return (1);
+		}
+
+		hp = first ? hist_get(first, false, false) :
+		    hist_get_newest(false);
+		if (!hp)
+			return (1);
+		/* hist_replace */
+		if (!pat)
+			strdupx(line, *hp, ATEMP);
+		else {
+			char *s, *s1;
+			size_t len, pat_len, rep_len;
+			XString xs;
+			char *xp;
+			bool any_subst = false;
+
+			pat_len = strlen(pat);
+			rep_len = strlen(rep);
+			Xinit(xs, xp, 128, ATEMP);
+			for (s = *hp; (s1 = strstr(s, pat)) &&
+			    (!any_subst || gflag); s = s1 + pat_len) {
+				any_subst = true;
+				len = s1 - s;
+				XcheckN(xs, xp, len + rep_len);
+				/*; first part */
+				memcpy(xp, s, len);
+				xp += len;
+				/* replacement */
+				memcpy(xp, rep, rep_len);
+				xp += rep_len;
+			}
+			if (!any_subst) {
+				bi_errorf("bad substitution");
+				return (1);
+			}
+			len = strlen(s) + 1;
+			XcheckN(xs, xp, len);
+			memcpy(xp, s, len);
+			xp += len;
+			line = Xclose(xs, xp);
+		}
+		return (hist_execute(line));
+	}
+
+	if (editor && (lflag || nflag)) {
+		bi_errorf("can't use -l, -n with -e");
+		return (1);
+	}
+
+	if (!first && (first = *wp))
+		wp++;
+	if (!last && (last = *wp))
+		wp++;
+	if (*wp) {
+		bi_errorf("too many arguments");
+		return (1);
+	}
+	if (!first) {
+		hfirst = lflag ? hist_get("-16", true, true) :
+		    hist_get_newest(false);
+		if (!hfirst)
+			return (1);
+		/* can't fail if hfirst didn't fail */
+		hlast = hist_get_newest(false);
+	} else {
+		/*
+		 * POSIX says not an error if first/last out of bounds
+		 * when range is specified; AT&T ksh and pdksh allow out
+		 * of bounds for -l as well.
+		 */
+		hfirst = hist_get(first, tobool(lflag || last), lflag);
+		if (!hfirst)
+			return (1);
+		hlast = last ? hist_get(last, true, lflag) :
+		    (lflag ? hist_get_newest(false) : hfirst);
+		if (!hlast)
+			return (1);
+	}
+	if (hfirst > hlast) {
+		char **temp;
+
+		temp = hfirst; hfirst = hlast; hlast = temp;
+		/* POSIX */
+		rflag = !rflag;
+	}
+
+	/* List history */
+	if (lflag) {
+		char *s, *t;
+
+		for (hp = rflag ? hlast : hfirst;
+		    hp >= hfirst && hp <= hlast; hp += rflag ? -1 : 1) {
+			if (!nflag)
+				shf_fprintf(shl_stdout, "%d",
+				    hist_source->line - (int)(histptr - hp));
+			shf_putc('\t', shl_stdout);
+			/* print multi-line commands correctly */
+			s = *hp;
+			while ((t = strchr(s, '\n'))) {
+				*t = '\0';
+				shf_fprintf(shl_stdout, "%s\n\t", s);
+				*t++ = '\n';
+				s = t;
+			}
+			shf_fprintf(shl_stdout, "%s\n", s);
+		}
+		shf_flush(shl_stdout);
+		return (0);
+	}
+
+	/* Run editor on selected lines, then run resulting commands */
+
+	tf = maketemp(ATEMP, TT_HIST_EDIT, &e->temps);
+	if (!(shf = tf->shf)) {
+		bi_errorf("can't %s temporary file %s: %s",
+		    "create", tf->tffn, cstrerror(errno));
+		return (1);
+	}
+	for (hp = rflag ? hlast : hfirst;
+	    hp >= hfirst && hp <= hlast; hp += rflag ? -1 : 1)
+		shf_fprintf(shf, "%s\n", *hp);
+	if (shf_close(shf) == EOF) {
+		bi_errorf("can't %s temporary file %s: %s",
+		    "write", tf->tffn, cstrerror(errno));
+		return (1);
+	}
+
+	/* Ignore setstr errors here (arbitrary) */
+	setstr(local("_", false), tf->tffn, KSH_RETURN_ERROR);
+
+	/* XXX: source should not get trashed by this.. */
+	{
+		Source *sold = source;
+		int ret;
+
+		ret = command(editor ? editor : TFCEDIT_dollaru, 0);
+		source = sold;
+		if (ret)
+			return (ret);
+	}
+
+	{
+		struct stat statb;
+		XString xs;
+		char *xp;
+		ssize_t n;
+
+		if (!(shf = shf_open(tf->tffn, O_RDONLY, 0, 0))) {
+			bi_errorf("can't %s temporary file %s: %s",
+			    "open", tf->tffn, cstrerror(errno));
+			return (1);
+		}
+
+		if (stat(tf->tffn, &statb) < 0)
+			n = 128;
+		else if ((off_t)statb.st_size > MKSH_MAXHISTFSIZE) {
+			bi_errorf("%s %s too large: %lu", Thistory,
+			    "file", (unsigned long)statb.st_size);
+			goto errout;
+		} else
+			n = (size_t)statb.st_size + 1;
+		Xinit(xs, xp, n, hist_source->areap);
+		while ((n = shf_read(xp, Xnleft(xs, xp), shf)) > 0) {
+			xp += n;
+			if (Xnleft(xs, xp) <= 0)
+				XcheckN(xs, xp, Xlength(xs, xp));
+		}
+		if (n < 0) {
+			bi_errorf("can't %s temporary file %s: %s",
+			    "read", tf->tffn, cstrerror(shf_errno(shf)));
+ errout:
+			shf_close(shf);
+			return (1);
+		}
+		shf_close(shf);
+		*xp = '\0';
+		strip_nuls(Xstring(xs, xp), Xlength(xs, xp));
+		return (hist_execute(Xstring(xs, xp)));
+	}
+}
+
+/* Save cmd in history, execute cmd (cmd gets trashed) */
+static int
+hist_execute(char *cmd)
+{
+	static int last_line = -1;
+	Source *sold;
+	int ret;
+	char *p, *q;
+
+	/* Back up over last histsave */
+	if (histptr >= history && last_line != hist_source->line) {
+		hist_source->line--;
+		afree(*histptr, APERM);
+		histptr--;
+		last_line = hist_source->line;
+	}
+
+	for (p = cmd; p; p = q) {
+		if ((q = strchr(p, '\n'))) {
+			/* kill the newline */
+			*q++ = '\0';
+			if (!*q)
+				/* ignore trailing newline */
+				q = NULL;
+		}
+		histsave(&hist_source->line, p, true, true);
+
+		/* POSIX doesn't say this is done... */
+		shellf("%s\n", p);
+		if (q)
+			/* restore \n (trailing \n not restored) */
+			q[-1] = '\n';
+	}
+
+	/*-
+	 * Commands are executed here instead of pushing them onto the
+	 * input 'cause POSIX says the redirection and variable assignments
+	 * in
+	 *	X=y fc -e - 42 2> /dev/null
+	 * are to effect the repeated commands environment.
+	 */
+	/* XXX: source should not get trashed by this.. */
+	sold = source;
+	ret = command(cmd, 0);
+	source = sold;
+	return (ret);
+}
+
+/*
+ * get pointer to history given pattern
+ * pattern is a number or string
+ */
+static char **
+hist_get(const char *str, bool approx, bool allow_cur)
+{
+	char **hp = NULL;
+	int n;
+
+	if (getn(str, &n)) {
+		hp = histptr + (n < 0 ? n : (n - hist_source->line));
+		if ((ptrdiff_t)hp < (ptrdiff_t)history) {
+			if (approx)
+				hp = hist_get_oldest();
+			else {
+				bi_errorf("%s: %s", str, Tnot_in_history);
+				hp = NULL;
+			}
+		} else if ((ptrdiff_t)hp > (ptrdiff_t)histptr) {
+			if (approx)
+				hp = hist_get_newest(allow_cur);
+			else {
+				bi_errorf("%s: %s", str, Tnot_in_history);
+				hp = NULL;
+			}
+		} else if (!allow_cur && hp == histptr) {
+			bi_errorf("%s: %s", str, "invalid range");
+			hp = NULL;
+		}
+	} else {
+		bool anchored = *str == '?' ? (++str, false) : true;
+
+		/* the -1 is to avoid the current fc command */
+		if ((n = findhist(histptr - history - 1, 0, str, anchored)) < 0)
+			bi_errorf("%s: %s", str, Tnot_in_history);
+		else
+			hp = &history[n];
+	}
+	return (hp);
+}
+
+/* Return a pointer to the newest command in the history */
+char **
+hist_get_newest(bool allow_cur)
+{
+	if (histptr < history || (!allow_cur && histptr == history)) {
+		bi_errorf("no history (yet)");
+		return (NULL);
+	}
+	return (allow_cur ? histptr : histptr - 1);
+}
+
+/* Return a pointer to the oldest command in the history */
+static char **
+hist_get_oldest(void)
+{
+	if (histptr <= history) {
+		bi_errorf("no history (yet)");
+		return (NULL);
+	}
+	return (history);
+}
+
+#if !defined(MKSH_NO_CMDLINE_EDITING) && !MKSH_S_NOVI
+/* current position in history[] */
+static char **current;
+
+/*
+ * Return the current position.
+ */
+char **
+histpos(void)
+{
+	return (current);
+}
+
+int
+histnum(int n)
+{
+	int last = histptr - history;
+
+	if (n < 0 || n >= last) {
+		current = histptr;
+		return (last);
+	} else {
+		current = &history[n];
+		return (n);
+	}
+}
+#endif
+
+/*
+ * This will become unnecessary if hist_get is modified to allow
+ * searching from positions other than the end, and in either
+ * direction.
+ */
+int
+findhist(int start, int fwd, const char *str, bool anchored)
+{
+	char **hp;
+	int maxhist = histptr - history;
+	int incr = fwd ? 1 : -1;
+	size_t len = strlen(str);
+
+	if (start < 0 || start >= maxhist)
+		start = maxhist;
+
+	hp = &history[start];
+	for (; hp >= history && hp <= histptr; hp += incr)
+		if ((anchored && strncmp(*hp, str, len) == 0) ||
+		    (!anchored && strstr(*hp, str)))
+			return (hp - history);
+
+	return (-1);
+}
+
+/*
+ * set history; this means reallocating the dataspace
+ */
+void
+sethistsize(mksh_ari_t n)
+{
+	if (n > 0 && n != histsize) {
+		int cursize = histptr - history;
+
+		/* save most recent history */
+		if (n < cursize) {
+			memmove(history, histptr - n + 1, n * sizeof(char *));
+			cursize = n - 1;
+		}
+
+		history = aresize2(history, n, sizeof(char *), APERM);
+
+		histsize = n;
+		histptr = history + cursize;
+	}
+}
+
+#if HAVE_PERSISTENT_HISTORY
+/*
+ * set history file; this can mean reloading/resetting/starting
+ * history file maintenance
+ */
+void
+sethistfile(const char *name)
+{
+	/* if not started then nothing to do */
+	if (hstarted == false)
+		return;
+
+	/* if the name is the same as the name we have */
+	if (hname && strcmp(hname, name) == 0)
+		return;
+
+	/*
+	 * it's a new name - possibly
+	 */
+	if (histfd != -1) {
+		/* yes the file is open */
+		(void)close(histfd);
+		histfd = -1;
+		histfsize = 0;
+		afree(hname, APERM);
+		hname = NULL;
+		/* let's reset the history */
+		histptr = history - 1;
+		hist_source->line = 0;
+	}
+
+	hist_init(hist_source);
+}
+#endif
+
+/*
+ * initialise the history vector
+ */
+void
+init_histvec(void)
+{
+	if (history == (char **)NULL) {
+		histsize = MKSH_DEFHISTSIZE;
+		history = alloc2(histsize, sizeof(char *), APERM);
+		histptr = history - 1;
+	}
+}
+
+
+/*
+ * It turns out that there is a lot of ghastly hackery here
+ */
+
+#if !defined(MKSH_SMALL) && HAVE_PERSISTENT_HISTORY
+/* do not save command in history but possibly sync */
+bool
+histsync(void)
+{
+	bool changed = false;
+
+	if (histfd != -1) {
+		int lno = hist_source->line;
+
+		hist_source->line++;
+		writehistfile(0, NULL);
+		hist_source->line--;
+
+		if (lno != hist_source->line)
+			changed = true;
+	}
+
+	return (changed);
+}
+#endif
+
+/*
+ * save command in history
+ */
+void
+histsave(int *lnp, const char *cmd, bool dowrite MKSH_A_UNUSED, bool ignoredups)
+{
+	char **hp;
+	char *c, *cp;
+
+	mkssert(cmd != NULL);
+	strdupx(c, cmd, APERM);
+	if ((cp = strchr(c, '\n')) != NULL)
+		*cp = '\0';
+
+	if (ignoredups && !strcmp(c, *histptr)
+#if !defined(MKSH_SMALL) && HAVE_PERSISTENT_HISTORY
+	    && !histsync()
+#endif
+	    ) {
+		afree(c, APERM);
+		return;
+	}
+	++*lnp;
+
+#if HAVE_PERSISTENT_HISTORY
+	if (dowrite && histfd != -1)
+		writehistfile(*lnp, c);
+#endif
+
+	hp = histptr;
+
+	if (++hp >= history + histsize) {
+		/* remove oldest command */
+		afree(*history, APERM);
+		for (hp = history; hp < history + histsize - 1; hp++)
+			hp[0] = hp[1];
+	}
+	*hp = c;
+	histptr = hp;
+}
+
+/*
+ * Write history data to a file nominated by HISTFILE;
+ * if HISTFILE is unset then history still happens, but
+ * the data is not written to a file. All copies of ksh
+ * looking at the file will maintain the same history.
+ * This is ksh behaviour.
+ *
+ * This stuff uses mmap()
+ *
+ * This stuff is so totally broken it must eventually be
+ * redesigned, without mmap, better checks, support for
+ * larger files, etc. and handle partially corrupted files
+ */
+
+/*-
+ * Open a history file
+ * Format is:
+ * Bytes 1, 2:
+ *	HMAGIC - just to check that we are dealing with the correct object
+ * Then follows a number of stored commands
+ * Each command is
+ *	<command byte><command number(4 octets, big endian)><bytes><NUL>
+ */
+#define HMAGIC1		0xAB
+#define HMAGIC2		0xCD
+#define COMMAND		0xFF
+
+#if HAVE_PERSISTENT_HISTORY
+static const unsigned char sprinkle[2] = { HMAGIC1, HMAGIC2 };
+#endif
+
+void
+hist_init(Source *s)
+{
+#if HAVE_PERSISTENT_HISTORY
+	unsigned char *base;
+	int lines, fd;
+	enum { hist_init_first, hist_init_retry, hist_init_restore } hs;
+#endif
+
+	if (Flag(FTALKING) == 0)
+		return;
+
+	hstarted = true;
+	hist_source = s;
+
+#if HAVE_PERSISTENT_HISTORY
+	if ((hname = str_val(global("HISTFILE"))) == NULL)
+		return;
+	strdupx(hname, hname, APERM);
+	hs = hist_init_first;
+
+ retry:
+	/* we have a file and are interactive */
+	if ((fd = open(hname, O_RDWR | O_CREAT | O_APPEND | O_BINARY,
+	    0600)) < 0)
+		return;
+
+	histfd = savefd(fd);
+	if (histfd != fd)
+		close(fd);
+
+	mksh_lockfd(histfd);
+
+	histfsize = lseek(histfd, (off_t)0, SEEK_END);
+	if (histfsize > MKSH_MAXHISTFSIZE || hs == hist_init_restore) {
+		/* we ignore too large files but still append to them */
+		/* we also don't need to re-read after truncation */
+		goto hist_init_tail;
+	} else if (histfsize > 2) {
+		/* we have some data, check its validity */
+		base = (void *)mmap(NULL, (size_t)histfsize, PROT_READ,
+		    MAP_FILE | MAP_PRIVATE, histfd, (off_t)0);
+		if (base == (unsigned char *)MAP_FAILED)
+			goto hist_init_fail;
+		if (base[0] != HMAGIC1 || base[1] != HMAGIC2) {
+			munmap(caddr_cast(base), (size_t)histfsize);
+			goto hist_init_fail;
+		}
+		/* load _all_ data */
+		lines = histload(hist_source, base + 2, (size_t)histfsize - 2);
+		munmap(caddr_cast(base), (size_t)histfsize);
+		/* check if the file needs to be truncated */
+		if (lines > histsize && histptr >= history) {
+			/* you're fucked up with the current code, trust me */
+			char *nhname, **hp;
+			struct stat sb;
+
+			/* create temporary file */
+			nhname = shf_smprintf("%s.%d", hname, (int)procpid);
+			if ((fd = open(nhname, O_RDWR | O_CREAT | O_TRUNC |
+			    O_EXCL | O_BINARY, 0600)) < 0) {
+				/* just don't truncate then, meh. */
+				goto hist_trunc_dont;
+			}
+			if (fstat(histfd, &sb) >= 0 &&
+			    chown(nhname, sb.st_uid, sb.st_gid)) {
+				/* abort the truncation then, meh. */
+				goto hist_trunc_abort;
+			}
+			/* we definitively want some magic in that file */
+			if (write(fd, sprinkle, 2) != 2)
+				goto hist_trunc_abort;
+			/* and of course the entries */
+			hp = history;
+			while (hp < histptr) {
+				if (!writehistline(fd,
+				    s->line - (histptr - hp), *hp))
+					goto hist_trunc_abort;
+				++hp;
+			}
+			/* now unlock, close both, rename, rinse, repeat */
+			close(fd);
+			fd = -1;
+			hist_finish();
+			if (rename(nhname, hname) < 0) {
+ hist_trunc_abort:
+				if (fd != -1)
+					close(fd);
+				unlink(nhname);
+				if (fd != -1)
+					goto hist_trunc_dont;
+				/* darn! restore histfd and pray */
+			}
+			hs = hist_init_restore;
+ hist_trunc_dont:
+			afree(nhname, ATEMP);
+			if (hs == hist_init_restore)
+				goto retry;
+		}
+	} else if (histfsize != 0) {
+		/* negative or too small... */
+ hist_init_fail:
+		/* ... or mmap failed or illegal */
+		hist_finish();
+		/* nuke the bogus file then retry, at most once */
+		if (!unlink(hname) && hs != hist_init_retry) {
+			hs = hist_init_retry;
+			goto retry;
+		}
+		if (hs != hist_init_retry)
+			bi_errorf("can't %s %s: %s",
+			    "unlink HISTFILE", hname, cstrerror(errno));
+		histfsize = 0;
+		return;
+	} else {
+		/* size 0, add magic to the history file */
+		if (write(histfd, sprinkle, 2) != 2) {
+			hist_finish();
+			return;
+		}
+	}
+	histfsize = lseek(histfd, (off_t)0, SEEK_END);
+ hist_init_tail:
+	mksh_unlkfd(histfd);
+#endif
+}
+
+#if HAVE_PERSISTENT_HISTORY
+/*
+ * load the history structure from the stored data
+ */
+static int
+histload(Source *s, unsigned char *base, size_t bytes)
+{
+	int lno = 0, lines = 0;
+	unsigned char *cp;
+
+ histload_loop:
+	/* !bytes check as some systems (older FreeBSDs) have buggy memchr */
+	if (!bytes || (cp = memchr(base, COMMAND, bytes)) == NULL)
+		return (lines);
+	/* advance base pointer past COMMAND byte */
+	bytes -= ++cp - base;
+	base = cp;
+	/* if there is no full string left, don't bother with the rest */
+	if (bytes < 5 || (cp = memchr(base + 4, '\0', bytes - 4)) == NULL)
+		return (lines);
+	/* load the stored line number */
+	lno = ((base[0] & 0xFF) << 24) | ((base[1] & 0xFF) << 16) |
+	    ((base[2] & 0xFF) << 8) | (base[3] & 0xFF);
+	/* store away the found line (@base[4]) */
+	++lines;
+	if (histptr >= history && lno - 1 != s->line) {
+		/* a replacement? */
+		char **hp;
+
+		if (lno >= s->line - (histptr - history) && lno <= s->line) {
+			hp = &histptr[lno - s->line];
+			if (*hp)
+				afree(*hp, APERM);
+			strdupx(*hp, (char *)(base + 4), APERM);
+		}
+	} else {
+		s->line = lno--;
+		histsave(&lno, (char *)(base + 4), false, false);
+	}
+	/* advance base pointer past NUL */
+	bytes -= ++cp - base;
+	base = cp;
+	/* repeat until no more */
+	goto histload_loop;
+}
+
+/*
+ * write a command to the end of the history file
+ *
+ * This *MAY* seem easy but it's also necessary to check
+ * that the history file has not changed in size.
+ * If it has - then some other shell has written to it and
+ * we should (re)read those commands to update our history
+ */
+static void
+writehistfile(int lno, const char *cmd)
+{
+	off_t sizenow;
+	size_t bytes;
+	unsigned char *base, *news;
+
+	mksh_lockfd(histfd);
+	sizenow = lseek(histfd, (off_t)0, SEEK_END);
+	if (sizenow < histfsize) {
+		/* the file has shrunk; give up */
+		goto bad;
+	}
+	if (
+		/* ignore changes when the file is too large */
+		sizenow <= MKSH_MAXHISTFSIZE
+	    &&
+		/* the size has changed, we need to do read updates */
+		sizenow > histfsize
+	    ) {
+		/* both sizenow and histfsize are <= MKSH_MAXHISTFSIZE */
+		bytes = (size_t)(sizenow - histfsize);
+		base = (void *)mmap(NULL, (size_t)sizenow, PROT_READ,
+		    MAP_FILE | MAP_PRIVATE, histfd, (off_t)0);
+		if (base == (unsigned char *)MAP_FAILED)
+			goto bad;
+		news = base + (size_t)histfsize;
+		if (*news == COMMAND) {
+			hist_source->line--;
+			histload(hist_source, news, bytes);
+			hist_source->line++;
+			lno = hist_source->line;
+		} else
+			bytes = 0;
+		munmap(caddr_cast(base), (size_t)sizenow);
+		if (!bytes)
+			goto bad;
+	}
+	if (cmd && !writehistline(histfd, lno, cmd)) {
+ bad:
+		hist_finish();
+		return;
+	}
+	histfsize = lseek(histfd, (off_t)0, SEEK_END);
+	mksh_unlkfd(histfd);
+}
+
+static int
+writehistline(int fd, int lno, const char *cmd)
+{
+	ssize_t n;
+	unsigned char hdr[5];
+
+	hdr[0] = COMMAND;
+	hdr[1] = (lno >> 24) & 0xFF;
+	hdr[2] = (lno >> 16) & 0xFF;
+	hdr[3] = (lno >> 8) & 0xFF;
+	hdr[4] = lno & 0xFF;
+	n = strlen(cmd) + 1;
+	return (write(fd, hdr, 5) == 5 && write(fd, cmd, n) == n);
+}
+
+void
+hist_finish(void)
+{
+	if (histfd >= 0) {
+		mksh_unlkfd(histfd);
+		(void)close(histfd);
+	}
+	histfd = -1;
+}
+#endif
+
+
+#if !HAVE_SYS_SIGNAME
+static const struct mksh_sigpair {
+	const char * const name;
+	int nr;
+} mksh_sigpairs[] = {
+#include "signames.inc"
+	{ NULL, 0 }
+};
+#endif
+
+#if HAVE_SYS_SIGLIST
+#if !HAVE_SYS_SIGLIST_DECL
+extern const char * const sys_siglist[];
+#endif
+#endif
+
+void
+inittraps(void)
+{
+	int i;
+	const char *cs;
+
+	trap_exstat = -1;
+
+	/* Populate sigtraps based on sys_signame and sys_siglist. */
+	/*XXX this is idiotic, use a multi-key/value hashtable! */
+	for (i = 0; i <= NSIG; i++) {
+		sigtraps[i].signal = i;
+		if (i == ksh_SIGERR) {
+			sigtraps[i].name = "ERR";
+			sigtraps[i].mess = "Error handler";
+		} else {
+#if HAVE_SYS_SIGNAME
+			cs = sys_signame[i];
+#else
+			const struct mksh_sigpair *pair = mksh_sigpairs;
+			while ((pair->nr != i) && (pair->name != NULL))
+				++pair;
+			cs = pair->name;
+#endif
+			if ((cs == NULL) ||
+			    (cs[0] == '\0'))
+				sigtraps[i].name = shf_smprintf("%d", i);
+			else {
+				char *s;
+
+				/* this is not optimal, what about SIGSIG1? */
+				if ((cs[0] & 0xDF) == 'S' &&
+				    (cs[1] & 0xDF) == 'I' &&
+				    (cs[2] & 0xDF) == 'G' &&
+				    cs[3] != '\0') {
+					/* skip leading "SIG" */
+					cs += 3;
+				}
+				strdupx(s, cs, APERM);
+				sigtraps[i].name = s;
+				while ((*s = ksh_toupper(*s)))
+					++s;
+			}
+#if HAVE_SYS_SIGLIST
+			sigtraps[i].mess = sys_siglist[i];
+#elif HAVE_STRSIGNAL
+			sigtraps[i].mess = strsignal(i);
+#else
+			sigtraps[i].mess = NULL;
+#endif
+			if ((sigtraps[i].mess == NULL) ||
+			    (sigtraps[i].mess[0] == '\0'))
+				sigtraps[i].mess = shf_smprintf("%s %d",
+				    "Signal", i);
+		}
+	}
+	/* our name for signal 0 */
+	sigtraps[ksh_SIGEXIT].name = "EXIT";
+
+	(void)sigemptyset(&Sigact_ign.sa_mask);
+	Sigact_ign.sa_flags = 0; /* interruptible */
+	Sigact_ign.sa_handler = SIG_IGN;
+
+	sigtraps[SIGINT].flags |= TF_DFL_INTR | TF_TTY_INTR;
+	sigtraps[SIGQUIT].flags |= TF_DFL_INTR | TF_TTY_INTR;
+	/* SIGTERM is not fatal for interactive */
+	sigtraps[SIGTERM].flags |= TF_DFL_INTR;
+	sigtraps[SIGHUP].flags |= TF_FATAL;
+	sigtraps[SIGCHLD].flags |= TF_SHELL_USES;
+
+	/* these are always caught so we can clean up any temporary files. */
+	setsig(&sigtraps[SIGINT], trapsig, SS_RESTORE_ORIG);
+	setsig(&sigtraps[SIGQUIT], trapsig, SS_RESTORE_ORIG);
+	setsig(&sigtraps[SIGTERM], trapsig, SS_RESTORE_ORIG);
+	setsig(&sigtraps[SIGHUP], trapsig, SS_RESTORE_ORIG);
+}
+
+static void alarm_catcher(int sig);
+
+void
+alarm_init(void)
+{
+	sigtraps[SIGALRM].flags |= TF_SHELL_USES;
+	setsig(&sigtraps[SIGALRM], alarm_catcher,
+		SS_RESTORE_ORIG|SS_FORCE|SS_SHTRAP);
+}
+
+/* ARGSUSED */
+static void
+alarm_catcher(int sig MKSH_A_UNUSED)
+{
+	/* this runs inside interrupt context, with errno saved */
+
+	if (ksh_tmout_state == TMOUT_READING) {
+		int left = alarm(0);
+
+		if (left == 0) {
+			ksh_tmout_state = TMOUT_LEAVING;
+			intrsig = 1;
+		} else
+			alarm(left);
+	}
+}
+
+Trap *
+gettrap(const char *cs, bool igncase)
+{
+	int i;
+	Trap *p;
+	char *as;
+
+	if (ksh_isdigit(*cs)) {
+		return ((getn(cs, &i) && 0 <= i && i < NSIG) ?
+		    (&sigtraps[i]) : NULL);
+	}
+
+	/* this breaks SIGSIG1, but we do that above anyway */
+	if ((cs[0] & 0xDF) == 'S' &&
+	    (cs[1] & 0xDF) == 'I' &&
+	    (cs[2] & 0xDF) == 'G' &&
+	    cs[3] != '\0') {
+		/* skip leading "SIG" */
+		cs += 3;
+	}
+	if (igncase) {
+		char *s;
+
+		strdupx(as, cs, ATEMP);
+		cs = s = as;
+		while ((*s = ksh_toupper(*s)))
+			++s;
+	} else
+		as = NULL;
+
+	p = sigtraps;
+	for (i = 0; i <= NSIG; i++) {
+		if (!strcmp(p->name, cs))
+			goto found;
+		++p;
+	}
+	p = NULL;
+ found:
+	afree(as, ATEMP);
+	return (p);
+}
+
+/*
+ * trap signal handler
+ */
+void
+trapsig(int i)
+{
+	Trap *p = &sigtraps[i];
+	int eno = errno;
+
+	trap = p->set = 1;
+	if (p->flags & TF_DFL_INTR)
+		intrsig = 1;
+	if ((p->flags & TF_FATAL) && !p->trap) {
+		fatal_trap = 1;
+		intrsig = 1;
+	}
+	if (p->shtrap)
+		(*p->shtrap)(i);
+	errno = eno;
+}
+
+/*
+ * called when we want to allow the user to ^C out of something - won't
+ * work if user has trapped SIGINT.
+ */
+void
+intrcheck(void)
+{
+	if (intrsig)
+		runtraps(TF_DFL_INTR|TF_FATAL);
+}
+
+/*
+ * called after EINTR to check if a signal with normally causes process
+ * termination has been received.
+ */
+int
+fatal_trap_check(void)
+{
+	int i;
+	Trap *p;
+
+	/* todo: should check if signal is fatal, not the TF_DFL_INTR flag */
+	for (p = sigtraps, i = NSIG+1; --i >= 0; p++)
+		if (p->set && (p->flags & (TF_DFL_INTR|TF_FATAL)))
+			/* return value is used as an exit code */
+			return (128 + p->signal);
+	return (0);
+}
+
+/*
+ * Returns the signal number of any pending traps: ie, a signal which has
+ * occurred for which a trap has been set or for which the TF_DFL_INTR flag
+ * is set.
+ */
+int
+trap_pending(void)
+{
+	int i;
+	Trap *p;
+
+	for (p = sigtraps, i = NSIG+1; --i >= 0; p++)
+		if (p->set && ((p->trap && p->trap[0]) ||
+		    ((p->flags & (TF_DFL_INTR|TF_FATAL)) && !p->trap)))
+			return (p->signal);
+	return (0);
+}
+
+/*
+ * run any pending traps. If intr is set, only run traps that
+ * can interrupt commands.
+ */
+void
+runtraps(int flag)
+{
+	int i;
+	Trap *p;
+
+	if (ksh_tmout_state == TMOUT_LEAVING) {
+		ksh_tmout_state = TMOUT_EXECUTING;
+		warningf(false, "timed out waiting for input");
+		unwind(LEXIT);
+	} else
+		/*
+		 * XXX: this means the alarm will have no effect if a trap
+		 * is caught after the alarm() was started...not good.
+		 */
+		ksh_tmout_state = TMOUT_EXECUTING;
+	if (!flag)
+		trap = 0;
+	if (flag & TF_DFL_INTR)
+		intrsig = 0;
+	if (flag & TF_FATAL)
+		fatal_trap = 0;
+	++trap_nested;
+	for (p = sigtraps, i = NSIG+1; --i >= 0; p++)
+		if (p->set && (!flag ||
+		    ((p->flags & flag) && p->trap == NULL)))
+			runtrap(p, false);
+	if (!--trap_nested)
+		runtrap(NULL, true);
+}
+
+void
+runtrap(Trap *p, bool is_last)
+{
+	int old_changed = 0, i;
+	char *trapstr;
+
+	if (p == NULL)
+		/* just clean up, see runtraps() above */
+		goto donetrap;
+	i = p->signal;
+	trapstr = p->trap;
+	p->set = 0;
+	if (trapstr == NULL) {
+		/* SIG_DFL */
+		if (p->flags & TF_FATAL) {
+			/* eg, SIGHUP */
+			exstat = (int)ksh_min(128U + (unsigned)i, 255U);
+			unwind(LLEAVE);
+		}
+		if (p->flags & TF_DFL_INTR) {
+			/* eg, SIGINT, SIGQUIT, SIGTERM, etc. */
+			exstat = (int)ksh_min(128U + (unsigned)i, 255U);
+			unwind(LINTR);
+		}
+		goto donetrap;
+	}
+	if (trapstr[0] == '\0')
+		/* SIG_IGN */
+		goto donetrap;
+	if (i == ksh_SIGEXIT || i == ksh_SIGERR) {
+		/* avoid recursion on these */
+		old_changed = p->flags & TF_CHANGED;
+		p->flags &= ~TF_CHANGED;
+		p->trap = NULL;
+	}
+	if (trap_exstat == -1)
+		trap_exstat = exstat & 0xFF;
+	/*
+	 * Note: trapstr is fully parsed before anything is executed, thus
+	 * no problem with afree(p->trap) in settrap() while still in use.
+	 */
+	command(trapstr, current_lineno);
+	if (i == ksh_SIGEXIT || i == ksh_SIGERR) {
+		if (p->flags & TF_CHANGED)
+			/* don't clear TF_CHANGED */
+			afree(trapstr, APERM);
+		else
+			p->trap = trapstr;
+		p->flags |= old_changed;
+	}
+
+ donetrap:
+	/* we're the last trap of a sequence executed */
+	if (is_last && trap_exstat != -1) {
+		exstat = trap_exstat;
+		trap_exstat = -1;
+	}
+}
+
+/* clear pending traps and reset user's trap handlers; used after fork(2) */
+void
+cleartraps(void)
+{
+	int i;
+	Trap *p;
+
+	trap = 0;
+	intrsig = 0;
+	fatal_trap = 0;
+	for (i = NSIG+1, p = sigtraps; --i >= 0; p++) {
+		p->set = 0;
+		if ((p->flags & TF_USER_SET) && (p->trap && p->trap[0]))
+			settrap(p, NULL);
+	}
+}
+
+/* restore signals just before an exec(2) */
+void
+restoresigs(void)
+{
+	int i;
+	Trap *p;
+
+	for (i = NSIG+1, p = sigtraps; --i >= 0; p++)
+		if (p->flags & (TF_EXEC_IGN|TF_EXEC_DFL))
+			setsig(p, (p->flags & TF_EXEC_IGN) ? SIG_IGN : SIG_DFL,
+			    SS_RESTORE_CURR|SS_FORCE);
+}
+
+void
+settrap(Trap *p, const char *s)
+{
+	sig_t f;
+
+	if (p->trap)
+		afree(p->trap, APERM);
+	/* handles s == NULL */
+	strdupx(p->trap, s, APERM);
+	p->flags |= TF_CHANGED;
+	f = !s ? SIG_DFL : s[0] ? trapsig : SIG_IGN;
+
+	p->flags |= TF_USER_SET;
+	if ((p->flags & (TF_DFL_INTR|TF_FATAL)) && f == SIG_DFL)
+		f = trapsig;
+	else if (p->flags & TF_SHELL_USES) {
+		if (!(p->flags & TF_ORIG_IGN) || Flag(FTALKING)) {
+			/* do what user wants at exec time */
+			p->flags &= ~(TF_EXEC_IGN|TF_EXEC_DFL);
+			if (f == SIG_IGN)
+				p->flags |= TF_EXEC_IGN;
+			else
+				p->flags |= TF_EXEC_DFL;
+		}
+
+		/*
+		 * assumes handler already set to what shell wants it
+		 * (normally trapsig, but could be j_sigchld() or SIG_IGN)
+		 */
+		return;
+	}
+
+	/* todo: should we let user know signal is ignored? how? */
+	setsig(p, f, SS_RESTORE_CURR|SS_USER);
+}
+
+/*
+ * Called by c_print() when writing to a co-process to ensure SIGPIPE won't
+ * kill shell (unless user catches it and exits)
+ */
+int
+block_pipe(void)
+{
+	int restore_dfl = 0;
+	Trap *p = &sigtraps[SIGPIPE];
+
+	if (!(p->flags & (TF_ORIG_IGN|TF_ORIG_DFL))) {
+		setsig(p, SIG_IGN, SS_RESTORE_CURR);
+		if (p->flags & TF_ORIG_DFL)
+			restore_dfl = 1;
+	} else if (p->cursig == SIG_DFL) {
+		setsig(p, SIG_IGN, SS_RESTORE_CURR);
+		/* restore to SIG_DFL */
+		restore_dfl = 1;
+	}
+	return (restore_dfl);
+}
+
+/* Called by c_print() to undo whatever block_pipe() did */
+void
+restore_pipe(int restore_dfl)
+{
+	if (restore_dfl)
+		setsig(&sigtraps[SIGPIPE], SIG_DFL, SS_RESTORE_CURR);
+}
+
+/*
+ * Set action for a signal. Action may not be set if original
+ * action was SIG_IGN, depending on the value of flags and FTALKING.
+ */
+int
+setsig(Trap *p, sig_t f, int flags)
+{
+	struct sigaction sigact;
+
+	if (p->signal == ksh_SIGEXIT || p->signal == ksh_SIGERR)
+		return (1);
+
+	memset(&sigact, 0, sizeof(sigact));
+
+	/*
+	 * First time setting this signal? If so, get and note the current
+	 * setting.
+	 */
+	if (!(p->flags & (TF_ORIG_IGN|TF_ORIG_DFL))) {
+		sigaction(p->signal, &Sigact_ign, &sigact);
+		p->flags |= sigact.sa_handler == SIG_IGN ?
+		    TF_ORIG_IGN : TF_ORIG_DFL;
+		p->cursig = SIG_IGN;
+	}
+
+	/*-
+	 * Generally, an ignored signal stays ignored, except if
+	 *	- the user of an interactive shell wants to change it
+	 *	- the shell wants for force a change
+	 */
+	if ((p->flags & TF_ORIG_IGN) && !(flags & SS_FORCE) &&
+	    (!(flags & SS_USER) || !Flag(FTALKING)))
+		return (0);
+
+	setexecsig(p, flags & SS_RESTORE_MASK);
+
+	/*
+	 * This is here 'cause there should be a way of clearing
+	 * shtraps, but don't know if this is a sane way of doing
+	 * it. At the moment, all users of shtrap are lifetime
+	 * users (SIGALRM, SIGCHLD, SIGWINCH).
+	 */
+	if (!(flags & SS_USER))
+		p->shtrap = (sig_t)NULL;
+	if (flags & SS_SHTRAP) {
+		p->shtrap = f;
+		f = trapsig;
+	}
+
+	if (p->cursig != f) {
+		p->cursig = f;
+		(void)sigemptyset(&sigact.sa_mask);
+		/* interruptible */
+		sigact.sa_flags = 0;
+		sigact.sa_handler = f;
+		sigaction(p->signal, &sigact, NULL);
+	}
+
+	return (1);
+}
+
+/* control what signal is set to before an exec() */
+void
+setexecsig(Trap *p, int restore)
+{
+	/* XXX debugging */
+	if (!(p->flags & (TF_ORIG_IGN|TF_ORIG_DFL)))
+		internal_errorf("setexecsig: unset signal %d(%s)",
+		    p->signal, p->name);
+
+	/* restore original value for exec'd kids */
+	p->flags &= ~(TF_EXEC_IGN|TF_EXEC_DFL);
+	switch (restore & SS_RESTORE_MASK) {
+	case SS_RESTORE_CURR:
+		/* leave things as they currently are */
+		break;
+	case SS_RESTORE_ORIG:
+		p->flags |= p->flags & TF_ORIG_IGN ? TF_EXEC_IGN : TF_EXEC_DFL;
+		break;
+	case SS_RESTORE_DFL:
+		p->flags |= TF_EXEC_DFL;
+		break;
+	case SS_RESTORE_IGN:
+		p->flags |= TF_EXEC_IGN;
+		break;
+	}
+}
+
+#if HAVE_PERSISTENT_HISTORY || defined(DF)
+/*
+ * File descriptor locking and unlocking functions.
+ * Could use some error handling, but hey, this is only
+ * advisory locking anyway, will often not work over NFS,
+ * and you are SOL if this fails...
+ */
+
+void
+mksh_lockfd(int fd)
+{
+#if defined(__OpenBSD__)
+	/* flock is not interrupted by signals */
+	(void)flock(fd, LOCK_EX);
+#elif HAVE_FLOCK
+	int rv;
+
+	/* e.g. on Linux */
+	do {
+		rv = flock(fd, LOCK_EX);
+	} while (rv == 1 && errno == EINTR);
+#elif HAVE_LOCK_FCNTL
+	int rv;
+	struct flock lks;
+
+	memset(&lks, 0, sizeof(lks));
+	lks.l_type = F_WRLCK;
+	do {
+		rv = fcntl(fd, F_SETLKW, &lks);
+	} while (rv == 1 && errno == EINTR);
+#endif
+}
+
+/* designed to not define mksh_unlkfd if none triggered */
+#if HAVE_FLOCK
+void
+mksh_unlkfd(int fd)
+{
+	(void)flock(fd, LOCK_UN);
+}
+#elif HAVE_LOCK_FCNTL
+void
+mksh_unlkfd(int fd)
+{
+	struct flock lks;
+
+	memset(&lks, 0, sizeof(lks));
+	lks.l_type = F_UNLCK;
+	(void)fcntl(fd, F_SETLKW, &lks);
+}
+#endif
+#endif

Deleted: vendor/MirOS/mksh/R50/jobs.c
===================================================================
--- vendor/MirOS/mksh/dist/jobs.c	2014-06-30 23:58:12 UTC (rev 6706)
+++ vendor/MirOS/mksh/R50/jobs.c	2014-07-01 12:01:17 UTC (rev 6708)
@@ -1,1850 +0,0 @@
-/*	$OpenBSD: jobs.c,v 1.39 2009/12/13 04:36:48 deraadt Exp $	*/
-
-/*-
- * Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2011,
- *		 2012, 2013
- *	Thorsten Glaser <tg at mirbsd.org>
- *
- * Provided that these terms and disclaimer and all copyright notices
- * are retained or reproduced in an accompanying document, permission
- * is granted to deal in this work without restriction, including un-
- * limited rights to use, publicly perform, distribute, sell, modify,
- * merge, give away, or sublicence.
- *
- * This work is provided "AS IS" and WITHOUT WARRANTY of any kind, to
- * the utmost extent permitted by applicable law, neither express nor
- * implied; without malicious intent or gross negligence. In no event
- * may a licensor, author or contributor be held liable for indirect,
- * direct, other damage, loss, or other issues arising in any way out
- * of dealing in the work, even if advised of the possibility of such
- * damage or existence of a defect, except proven that it results out
- * of said person's immediate fault when using the work as intended.
- */
-
-#include "sh.h"
-
-__RCSID("$MirOS: src/bin/mksh/jobs.c,v 1.100 2013/07/26 20:33:23 tg Exp $");
-
-#if HAVE_KILLPG
-#define mksh_killpg		killpg
-#else
-/* cross fingers and hope kill is killpg-endowed */
-#define mksh_killpg(p,s)	kill(-(p), (s))
-#endif
-
-/* Order important! */
-#define PRUNNING	0
-#define PEXITED		1
-#define PSIGNALLED	2
-#define PSTOPPED	3
-
-typedef struct proc Proc;
-struct proc {
-	Proc *next;		/* next process in pipeline (if any) */
-	pid_t pid;		/* process id */
-	int state;
-	int status;		/* wait status */
-	/* process command string from vistree */
-	char command[256 - (ALLOC_SIZE + sizeof(Proc *) + sizeof(pid_t) +
-	    2 * sizeof(int))];
-};
-
-/* Notify/print flag - j_print() argument */
-#define JP_SHORT	1	/* print signals processes were killed by */
-#define JP_MEDIUM	2	/* print [job-num] -/+ command */
-#define JP_LONG		3	/* print [job-num] -/+ pid command */
-#define JP_PGRP		4	/* print pgrp */
-
-/* put_job() flags */
-#define PJ_ON_FRONT	0	/* at very front */
-#define PJ_PAST_STOPPED	1	/* just past any stopped jobs */
-
-/* Job.flags values */
-#define JF_STARTED	0x001	/* set when all processes in job are started */
-#define JF_WAITING	0x002	/* set if j_waitj() is waiting on job */
-#define JF_W_ASYNCNOTIFY 0x004	/* set if waiting and async notification ok */
-#define JF_XXCOM	0x008	/* set for $(command) jobs */
-#define JF_FG		0x010	/* running in foreground (also has tty pgrp) */
-#define JF_SAVEDTTY	0x020	/* j->ttystat is valid */
-#define JF_CHANGED	0x040	/* process has changed state */
-#define JF_KNOWN	0x080	/* $! referenced */
-#define JF_ZOMBIE	0x100	/* known, unwaited process */
-#define JF_REMOVE	0x200	/* flagged for removal (j_jobs()/j_noityf()) */
-#define JF_USETTYMODE	0x400	/* tty mode saved if process exits normally */
-#define JF_SAVEDTTYPGRP	0x800	/* j->saved_ttypgrp is valid */
-
-typedef struct job Job;
-struct job {
-	Job *next;		/* next job in list */
-	Proc *proc_list;	/* process list */
-	Proc *last_proc;	/* last process in list */
-	struct timeval systime;	/* system time used by job */
-	struct timeval usrtime;	/* user time used by job */
-	pid_t pgrp;		/* process group of job */
-	pid_t ppid;		/* pid of process that forked job */
-	int job;		/* job number: %n */
-	int flags;		/* see JF_* */
-	volatile int state;	/* job state */
-	int status;		/* exit status of last process */
-	int32_t	age;		/* number of jobs started */
-	Coproc_id coproc_id;	/* 0 or id of coprocess output pipe */
-#ifndef MKSH_UNEMPLOYED
-	mksh_ttyst ttystat;	/* saved tty state for stopped jobs */
-	pid_t saved_ttypgrp;	/* saved tty process group for stopped jobs */
-#endif
-};
-
-/* Flags for j_waitj() */
-#define JW_NONE		0x00
-#define JW_INTERRUPT	0x01	/* ^C will stop the wait */
-#define JW_ASYNCNOTIFY	0x02	/* asynchronous notification during wait ok */
-#define JW_STOPPEDWAIT	0x04	/* wait even if job stopped */
-#define JW_PIPEST	0x08	/* want PIPESTATUS */
-
-/* Error codes for j_lookup() */
-#define JL_NOSUCH	0	/* no such job */
-#define JL_AMBIG	1	/* %foo or %?foo is ambiguous */
-#define JL_INVALID	2	/* non-pid, non-% job id */
-
-static const char * const lookup_msgs[] = {
-	"no such job",
-	"ambiguous",
-	"argument must be %job or process id"
-};
-
-static Job *job_list;		/* job list */
-static Job *last_job;
-static Job *async_job;
-static pid_t async_pid;
-
-static int nzombie;		/* # of zombies owned by this process */
-static int32_t njobs;		/* # of jobs started */
-
-#ifndef CHILD_MAX
-#define CHILD_MAX	25
-#endif
-
-#ifndef MKSH_NOPROSPECTOFWORK
-/* held_sigchld is set if sigchld occurs before a job is completely started */
-static volatile sig_atomic_t held_sigchld;
-#endif
-
-#ifndef MKSH_UNEMPLOYED
-static struct shf	*shl_j;
-static bool		ttypgrp_ok;	/* set if can use tty pgrps */
-static pid_t		restore_ttypgrp = -1;
-static int const	tt_sigs[] = { SIGTSTP, SIGTTIN, SIGTTOU };
-#endif
-
-static void		j_set_async(Job *);
-static void		j_startjob(Job *);
-static int		j_waitj(Job *, int, const char *);
-static void		j_sigchld(int);
-static void		j_print(Job *, int, struct shf *);
-static Job		*j_lookup(const char *, int *);
-static Job		*new_job(void);
-static Proc		*new_proc(void);
-static void		check_job(Job *);
-static void		put_job(Job *, int);
-static void		remove_job(Job *, const char *);
-static int		kill_job(Job *, int);
-
-static void tty_init_talking(void);
-static void tty_init_state(void);
-
-/* initialise job control */
-void
-j_init(void)
-{
-#ifndef MKSH_UNEMPLOYED
-	bool mflagset = Flag(FMONITOR) != 127;
-
-	Flag(FMONITOR) = 0;
-#endif
-
-#ifndef MKSH_NOPROSPECTOFWORK
-	(void)sigemptyset(&sm_default);
-	sigprocmask(SIG_SETMASK, &sm_default, NULL);
-
-	(void)sigemptyset(&sm_sigchld);
-	(void)sigaddset(&sm_sigchld, SIGCHLD);
-
-	setsig(&sigtraps[SIGCHLD], j_sigchld,
-	    SS_RESTORE_ORIG|SS_FORCE|SS_SHTRAP);
-#else
-	/* Make sure SIGCHLD isn't ignored - can do odd things under SYSV */
-	setsig(&sigtraps[SIGCHLD], SIG_DFL, SS_RESTORE_ORIG|SS_FORCE);
-#endif
-
-#ifndef MKSH_UNEMPLOYED
-	if (!mflagset && Flag(FTALKING))
-		Flag(FMONITOR) = 1;
-
-	/*
-	 * shl_j is used to do asynchronous notification (used in
-	 * an interrupt handler, so need a distinct shf)
-	 */
-	shl_j = shf_fdopen(2, SHF_WR, NULL);
-
-	if (Flag(FMONITOR) || Flag(FTALKING)) {
-		int i;
-
-		/*
-		 * the TF_SHELL_USES test is a kludge that lets us know if
-		 * if the signals have been changed by the shell.
-		 */
-		for (i = NELEM(tt_sigs); --i >= 0; ) {
-			sigtraps[tt_sigs[i]].flags |= TF_SHELL_USES;
-			/* j_change() sets this to SS_RESTORE_DFL if FMONITOR */
-			setsig(&sigtraps[tt_sigs[i]], SIG_IGN,
-			    SS_RESTORE_IGN|SS_FORCE);
-		}
-	}
-
-	/* j_change() calls tty_init_talking() and tty_init_state() */
-	if (Flag(FMONITOR))
-		j_change();
-	else
-#endif
-	  if (Flag(FTALKING)) {
-		tty_init_talking();
-		tty_init_state();
-	}
-}
-
-static int
-proc_errorlevel(Proc *p)
-{
-	switch (p->state) {
-	case PEXITED:
-		return (WEXITSTATUS(p->status));
-	case PSIGNALLED:
-		return (128 + WTERMSIG(p->status));
-	default:
-		return (0);
-	}
-}
-
-/* job cleanup before shell exit */
-void
-j_exit(void)
-{
-	/* kill stopped, and possibly running, jobs */
-	Job *j;
-	bool killed = false;
-
-	for (j = job_list; j != NULL; j = j->next) {
-		if (j->ppid == procpid &&
-		    (j->state == PSTOPPED ||
-		    (j->state == PRUNNING &&
-		    ((j->flags & JF_FG) ||
-		    (Flag(FLOGIN) && !Flag(FNOHUP) && procpid == kshpid))))) {
-			killed = true;
-			if (j->pgrp == 0)
-				kill_job(j, SIGHUP);
-			else
-				mksh_killpg(j->pgrp, SIGHUP);
-#ifndef MKSH_UNEMPLOYED
-			if (j->state == PSTOPPED) {
-				if (j->pgrp == 0)
-					kill_job(j, SIGCONT);
-				else
-					mksh_killpg(j->pgrp, SIGCONT);
-			}
-#endif
-		}
-	}
-	if (killed)
-		sleep(1);
-	j_notify();
-
-#ifndef MKSH_UNEMPLOYED
-	if (kshpid == procpid && restore_ttypgrp >= 0) {
-		/*
-		 * Need to restore the tty pgrp to what it was when the
-		 * shell started up, so that the process that started us
-		 * will be able to access the tty when we are done.
-		 * Also need to restore our process group in case we are
-		 * about to do an exec so that both our parent and the
-		 * process we are to become will be able to access the tty.
-		 */
-		tcsetpgrp(tty_fd, restore_ttypgrp);
-		setpgid(0, restore_ttypgrp);
-	}
-	if (Flag(FMONITOR)) {
-		Flag(FMONITOR) = 0;
-		j_change();
-	}
-#endif
-}
-
-#ifndef MKSH_UNEMPLOYED
-/* turn job control on or off according to Flag(FMONITOR) */
-void
-j_change(void)
-{
-	int i;
-
-	if (Flag(FMONITOR)) {
-		bool use_tty = Flag(FTALKING);
-
-		/* don't call mksh_tcget until we own the tty process group */
-		if (use_tty)
-			tty_init_talking();
-
-		/* no controlling tty, no SIGT* */
-		if ((ttypgrp_ok = (use_tty && tty_fd >= 0 && tty_devtty))) {
-			setsig(&sigtraps[SIGTTIN], SIG_DFL,
-			    SS_RESTORE_ORIG|SS_FORCE);
-			/* wait to be given tty (POSIX.1, B.2, job control) */
-			while (/* CONSTCOND */ 1) {
-				pid_t ttypgrp;
-
-				if ((ttypgrp = tcgetpgrp(tty_fd)) < 0) {
-					warningf(false, "%s: %s %s: %s",
-					    "j_init", "tcgetpgrp", "failed",
-					    cstrerror(errno));
-					ttypgrp_ok = false;
-					break;
-				}
-				if (ttypgrp == kshpgrp)
-					break;
-				kill(0, SIGTTIN);
-			}
-		}
-		for (i = NELEM(tt_sigs); --i >= 0; )
-			setsig(&sigtraps[tt_sigs[i]], SIG_IGN,
-			    SS_RESTORE_DFL|SS_FORCE);
-		if (ttypgrp_ok && kshpgrp != kshpid) {
-			if (setpgid(0, kshpid) < 0) {
-				warningf(false, "%s: %s %s: %s", "j_init",
-				    "setpgid", "failed", cstrerror(errno));
-				ttypgrp_ok = false;
-			} else {
-				if (tcsetpgrp(tty_fd, kshpid) < 0) {
-					warningf(false, "%s: %s %s: %s",
-					    "j_init", "tcsetpgrp", "failed",
-					    cstrerror(errno));
-					ttypgrp_ok = false;
-				} else
-					restore_ttypgrp = kshpgrp;
-				kshpgrp = kshpid;
-			}
-		}
-#ifndef MKSH_DISABLE_TTY_WARNING
-		if (use_tty && !ttypgrp_ok)
-			warningf(false, "%s: %s", "warning",
-			    "won't have full job control");
-#endif
-	} else {
-		ttypgrp_ok = false;
-		if (Flag(FTALKING))
-			for (i = NELEM(tt_sigs); --i >= 0; )
-				setsig(&sigtraps[tt_sigs[i]], SIG_IGN,
-				    SS_RESTORE_IGN|SS_FORCE);
-		else
-			for (i = NELEM(tt_sigs); --i >= 0; ) {
-				if (sigtraps[tt_sigs[i]].flags &
-				    (TF_ORIG_IGN | TF_ORIG_DFL))
-					setsig(&sigtraps[tt_sigs[i]],
-					    (sigtraps[tt_sigs[i]].flags & TF_ORIG_IGN) ?
-					    SIG_IGN : SIG_DFL,
-					    SS_RESTORE_ORIG|SS_FORCE);
-			}
-	}
-	tty_init_state();
-}
-#endif
-
-#if HAVE_NICE
-/* run nice(3) and ignore the result */
-static void
-ksh_nice(int ness)
-{
-#if defined(__USE_FORTIFY_LEVEL) && (__USE_FORTIFY_LEVEL > 0)
-	int eno;
-
-	errno = 0;
-	/* this is gonna annoy users; complain to your distro, people! */
-	if (nice(ness) == -1 && (eno = errno) != 0)
-		warningf(false, "%s: %s", "bgnice", cstrerror(eno));
-#else
-	(void)nice(ness);
-#endif
-}
-#endif
-
-/* execute tree in child subprocess */
-int
-exchild(struct op *t, int flags,
-    volatile int *xerrok,
-    /* used if XPCLOSE or XCCLOSE */
-    int close_fd)
-{
-	/* for pipelines */
-	static Proc *last_proc;
-
-	int rv = 0, forksleep, jwflags = JW_NONE;
-#ifndef MKSH_NOPROSPECTOFWORK
-	sigset_t omask;
-#endif
-	Proc *p;
-	Job *j;
-	pid_t cldpid;
-
-	if (flags & XPIPEST) {
-		flags &= ~XPIPEST;
-		jwflags |= JW_PIPEST;
-	}
-
-	if (flags & XEXEC)
-		/*
-		 * Clear XFORK|XPCLOSE|XCCLOSE|XCOPROC|XPIPEO|XPIPEI|XXCOM|XBGND
-		 * (also done in another execute() below)
-		 */
-		return (execute(t, flags & (XEXEC | XERROK), xerrok));
-
-#ifndef MKSH_NOPROSPECTOFWORK
-	/* no SIGCHLDs while messing with job and process lists */
-	sigprocmask(SIG_BLOCK, &sm_sigchld, &omask);
-#endif
-
-	p = new_proc();
-	p->next = NULL;
-	p->state = PRUNNING;
-	p->status = 0;
-	p->pid = 0;
-
-	/* link process into jobs list */
-	if (flags & XPIPEI) {
-		/* continuing with a pipe */
-		if (!last_job)
-			internal_errorf("%s %d",
-			    "exchild: XPIPEI and no last_job - pid",
-			    (int)procpid);
-		j = last_job;
-		if (last_proc)
-			last_proc->next = p;
-		last_proc = p;
-	} else {
-		/* fills in j->job */
-		j = new_job();
-		/*
-		 * we don't consider XXCOMs foreground since they don't get
-		 * tty process group and we don't save or restore tty modes.
-		 */
-		j->flags = (flags & XXCOM) ? JF_XXCOM :
-		    ((flags & XBGND) ? 0 : (JF_FG|JF_USETTYMODE));
-		timerclear(&j->usrtime);
-		timerclear(&j->systime);
-		j->state = PRUNNING;
-		j->pgrp = 0;
-		j->ppid = procpid;
-		j->age = ++njobs;
-		j->proc_list = p;
-		j->coproc_id = 0;
-		last_job = j;
-		last_proc = p;
-		put_job(j, PJ_PAST_STOPPED);
-	}
-
-	vistree(p->command, sizeof(p->command), t);
-
-	/* create child process */
-	forksleep = 1;
-	while ((cldpid = fork()) < 0 && errno == EAGAIN && forksleep < 32) {
-		if (intrsig)
-			/* allow user to ^C out... */
-			break;
-		sleep(forksleep);
-		forksleep <<= 1;
-	}
-	/* ensure $RANDOM changes between parent and child */
-	rndset((unsigned long)cldpid);
-	/* fork failed? */
-	if (cldpid < 0) {
-		kill_job(j, SIGKILL);
-		remove_job(j, "fork failed");
-#ifndef MKSH_NOPROSPECTOFWORK
-		sigprocmask(SIG_SETMASK, &omask, NULL);
-#endif
-		errorf("can't fork - try again");
-	}
-	p->pid = cldpid ? cldpid : (procpid = getpid());
-
-#ifndef MKSH_UNEMPLOYED
-	/* job control set up */
-	if (Flag(FMONITOR) && !(flags&XXCOM)) {
-		bool dotty = false;
-
-		if (j->pgrp == 0) {
-			/* First process */
-			j->pgrp = p->pid;
-			dotty = true;
-		}
-
-		/*
-		 * set pgrp in both parent and child to deal with race
-		 * condition
-		 */
-		setpgid(p->pid, j->pgrp);
-		if (ttypgrp_ok && dotty && !(flags & XBGND))
-			tcsetpgrp(tty_fd, j->pgrp);
-	}
-#endif
-
-	/* used to close pipe input fd */
-	if (close_fd >= 0 && (((flags & XPCLOSE) && cldpid) ||
-	    ((flags & XCCLOSE) && !cldpid)))
-		close(close_fd);
-	if (!cldpid) {
-		/* child */
-
-		/* Do this before restoring signal */
-		if (flags & XCOPROC)
-			coproc_cleanup(false);
-		cleanup_parents_env();
-#ifndef MKSH_UNEMPLOYED
-		/*
-		 * If FMONITOR or FTALKING is set, these signals are ignored,
-		 * if neither FMONITOR nor FTALKING are set, the signals have
-		 * their inherited values.
-		 */
-		if (Flag(FMONITOR) && !(flags & XXCOM)) {
-			for (forksleep = NELEM(tt_sigs); --forksleep >= 0; )
-				setsig(&sigtraps[tt_sigs[forksleep]], SIG_DFL,
-				    SS_RESTORE_DFL|SS_FORCE);
-		}
-#endif
-#if HAVE_NICE
-		if (Flag(FBGNICE) && (flags & XBGND))
-			ksh_nice(4);
-#endif
-		if ((flags & XBGND)
-#ifndef MKSH_UNEMPLOYED
-		    && !Flag(FMONITOR)
-#endif
-		    ) {
-			setsig(&sigtraps[SIGINT], SIG_IGN,
-			    SS_RESTORE_IGN|SS_FORCE);
-			setsig(&sigtraps[SIGQUIT], SIG_IGN,
-			    SS_RESTORE_IGN|SS_FORCE);
-			if ((!(flags & (XPIPEI | XCOPROC))) &&
-			    ((forksleep = open("/dev/null", 0)) > 0)) {
-				(void)ksh_dup2(forksleep, 0, true);
-				close(forksleep);
-			}
-		}
-		/* in case of $(jobs) command */
-		remove_job(j, "child");
-#ifndef MKSH_NOPROSPECTOFWORK
-		/* remove_job needs SIGCHLD blocked still */
-		sigprocmask(SIG_SETMASK, &omask, NULL);
-#endif
-		nzombie = 0;
-#ifndef MKSH_UNEMPLOYED
-		ttypgrp_ok = false;
-		Flag(FMONITOR) = 0;
-#endif
-		Flag(FTALKING) = 0;
-		cleartraps();
-		/* no return */
-		execute(t, (flags & XERROK) | XEXEC, NULL);
-#ifndef MKSH_SMALL
-		if (t->type == TPIPE)
-			unwind(LLEAVE);
-		internal_warningf("%s: %s", "exchild", "execute() returned");
-		fptreef(shl_out, 8, "%s: tried to execute {\n\t%T\n}\n",
-		    "exchild", t);
-		shf_flush(shl_out);
-#endif
-		unwind(LLEAVE);
-		/* NOTREACHED */
-	}
-
-	/* shell (parent) stuff */
-	if (!(flags & XPIPEO)) {
-		/* last process in a job */
-		j_startjob(j);
-		if (flags & XCOPROC) {
-			j->coproc_id = coproc.id;
-			/* n jobs using co-process output */
-			coproc.njobs++;
-			/* j using co-process input */
-			coproc.job = (void *)j;
-		}
-		if (flags & XBGND) {
-			j_set_async(j);
-			if (Flag(FTALKING)) {
-				shf_fprintf(shl_out, "[%d]", j->job);
-				for (p = j->proc_list; p; p = p->next)
-					shf_fprintf(shl_out, " %d",
-					    (int)p->pid);
-				shf_putchar('\n', shl_out);
-				shf_flush(shl_out);
-			}
-		} else
-			rv = j_waitj(j, jwflags, "jw:last proc");
-	}
-
-#ifndef MKSH_NOPROSPECTOFWORK
-	sigprocmask(SIG_SETMASK, &omask, NULL);
-#endif
-
-	return (rv);
-}
-
-/* start the last job: only used for $(command) jobs */
-void
-startlast(void)
-{
-#ifndef MKSH_NOPROSPECTOFWORK
-	sigset_t omask;
-
-	sigprocmask(SIG_BLOCK, &sm_sigchld, &omask);
-#endif
-
-	/* no need to report error - waitlast() will do it */
-	if (last_job) {
-		/* ensure it isn't removed by check_job() */
-		last_job->flags |= JF_WAITING;
-		j_startjob(last_job);
-	}
-#ifndef MKSH_NOPROSPECTOFWORK
-	sigprocmask(SIG_SETMASK, &omask, NULL);
-#endif
-}
-
-/* wait for last job: only used for $(command) jobs */
-int
-waitlast(void)
-{
-	int rv;
-	Job *j;
-#ifndef MKSH_NOPROSPECTOFWORK
-	sigset_t omask;
-
-	sigprocmask(SIG_BLOCK, &sm_sigchld, &omask);
-#endif
-
-	j = last_job;
-	if (!j || !(j->flags & JF_STARTED)) {
-		if (!j)
-			warningf(true, "%s: %s", "waitlast", "no last job");
-		else
-			internal_warningf("%s: %s", "waitlast", "not started");
-#ifndef MKSH_NOPROSPECTOFWORK
-		sigprocmask(SIG_SETMASK, &omask, NULL);
-#endif
-		/* not so arbitrary, non-zero value */
-		return (125);
-	}
-
-	rv = j_waitj(j, JW_NONE, "waitlast");
-
-#ifndef MKSH_NOPROSPECTOFWORK
-	sigprocmask(SIG_SETMASK, &omask, NULL);
-#endif
-
-	return (rv);
-}
-
-/* wait for child, interruptable. */
-int
-waitfor(const char *cp, int *sigp)
-{
-	int rv, ecode, flags = JW_INTERRUPT|JW_ASYNCNOTIFY;
-	Job *j;
-#ifndef MKSH_NOPROSPECTOFWORK
-	sigset_t omask;
-
-	sigprocmask(SIG_BLOCK, &sm_sigchld, &omask);
-#endif
-
-	*sigp = 0;
-
-	if (cp == NULL) {
-		/*
-		 * wait for an unspecified job - always returns 0, so
-		 * don't have to worry about exited/signaled jobs
-		 */
-		for (j = job_list; j; j = j->next)
-			/* AT&T ksh will wait for stopped jobs - we don't */
-			if (j->ppid == procpid && j->state == PRUNNING)
-				break;
-		if (!j) {
-#ifndef MKSH_NOPROSPECTOFWORK
-			sigprocmask(SIG_SETMASK, &omask, NULL);
-#endif
-			return (-1);
-		}
-	} else if ((j = j_lookup(cp, &ecode))) {
-		/* don't report normal job completion */
-		flags &= ~JW_ASYNCNOTIFY;
-		if (j->ppid != procpid) {
-#ifndef MKSH_NOPROSPECTOFWORK
-			sigprocmask(SIG_SETMASK, &omask, NULL);
-#endif
-			return (-1);
-		}
-	} else {
-#ifndef MKSH_NOPROSPECTOFWORK
-		sigprocmask(SIG_SETMASK, &omask, NULL);
-#endif
-		if (ecode != JL_NOSUCH)
-			bi_errorf("%s: %s", cp, lookup_msgs[ecode]);
-		return (-1);
-	}
-
-	/* AT&T ksh will wait for stopped jobs - we don't */
-	rv = j_waitj(j, flags, "jw:waitfor");
-
-#ifndef MKSH_NOPROSPECTOFWORK
-	sigprocmask(SIG_SETMASK, &omask, NULL);
-#endif
-
-	if (rv < 0)
-		/* we were interrupted */
-		*sigp = 128 + -rv;
-
-	return (rv);
-}
-
-/* kill (built-in) a job */
-int
-j_kill(const char *cp, int sig)
-{
-	Job *j;
-	int rv = 0, ecode;
-#ifndef MKSH_NOPROSPECTOFWORK
-	sigset_t omask;
-
-	sigprocmask(SIG_BLOCK, &sm_sigchld, &omask);
-#endif
-
-	if ((j = j_lookup(cp, &ecode)) == NULL) {
-#ifndef MKSH_NOPROSPECTOFWORK
-		sigprocmask(SIG_SETMASK, &omask, NULL);
-#endif
-		bi_errorf("%s: %s", cp, lookup_msgs[ecode]);
-		return (1);
-	}
-
-	if (j->pgrp == 0) {
-		/* started when !Flag(FMONITOR) */
-		if (kill_job(j, sig) < 0) {
-			bi_errorf("%s: %s", cp, cstrerror(errno));
-			rv = 1;
-		}
-	} else {
-#ifndef MKSH_UNEMPLOYED
-		if (j->state == PSTOPPED && (sig == SIGTERM || sig == SIGHUP))
-			mksh_killpg(j->pgrp, SIGCONT);
-#endif
-		if (mksh_killpg(j->pgrp, sig) < 0) {
-			bi_errorf("%s: %s", cp, cstrerror(errno));
-			rv = 1;
-		}
-	}
-
-#ifndef MKSH_NOPROSPECTOFWORK
-	sigprocmask(SIG_SETMASK, &omask, NULL);
-#endif
-
-	return (rv);
-}
-
-#ifndef MKSH_UNEMPLOYED
-/* fg and bg built-ins: called only if Flag(FMONITOR) set */
-int
-j_resume(const char *cp, int bg)
-{
-	Job *j;
-	Proc *p;
-	int ecode, rv = 0;
-	bool running;
-	sigset_t omask;
-
-	sigprocmask(SIG_BLOCK, &sm_sigchld, &omask);
-
-	if ((j = j_lookup(cp, &ecode)) == NULL) {
-		sigprocmask(SIG_SETMASK, &omask, NULL);
-		bi_errorf("%s: %s", cp, lookup_msgs[ecode]);
-		return (1);
-	}
-
-	if (j->pgrp == 0) {
-		sigprocmask(SIG_SETMASK, &omask, NULL);
-		bi_errorf("job not job-controlled");
-		return (1);
-	}
-
-	if (bg)
-		shprintf("[%d] ", j->job);
-
-	running = false;
-	for (p = j->proc_list; p != NULL; p = p->next) {
-		if (p->state == PSTOPPED) {
-			p->state = PRUNNING;
-			p->status = 0;
-			running = true;
-		}
-		shf_puts(p->command, shl_stdout);
-		if (p->next)
-			shf_puts("| ", shl_stdout);
-	}
-	shf_putc('\n', shl_stdout);
-	shf_flush(shl_stdout);
-	if (running)
-		j->state = PRUNNING;
-
-	put_job(j, PJ_PAST_STOPPED);
-	if (bg)
-		j_set_async(j);
-	else {
-		/* attach tty to job */
-		if (j->state == PRUNNING) {
-			if (ttypgrp_ok && (j->flags & JF_SAVEDTTY))
-				mksh_tcset(tty_fd, &j->ttystat);
-			/* See comment in j_waitj regarding saved_ttypgrp. */
-			if (ttypgrp_ok &&
-			    tcsetpgrp(tty_fd, (j->flags & JF_SAVEDTTYPGRP) ?
-			    j->saved_ttypgrp : j->pgrp) < 0) {
-				rv = errno;
-				if (j->flags & JF_SAVEDTTY)
-					mksh_tcset(tty_fd, &tty_state);
-				sigprocmask(SIG_SETMASK, &omask, NULL);
-				bi_errorf("%s %s(%d, %ld) %s: %s",
-				    "1st", "tcsetpgrp", tty_fd,
-				    (long)((j->flags & JF_SAVEDTTYPGRP) ?
-				    j->saved_ttypgrp : j->pgrp), "failed",
-				    cstrerror(rv));
-				return (1);
-			}
-		}
-		j->flags |= JF_FG;
-		j->flags &= ~JF_KNOWN;
-		if (j == async_job)
-			async_job = NULL;
-	}
-
-	if (j->state == PRUNNING && mksh_killpg(j->pgrp, SIGCONT) < 0) {
-		int eno = errno;
-
-		if (!bg) {
-			j->flags &= ~JF_FG;
-			if (ttypgrp_ok && (j->flags & JF_SAVEDTTY))
-				mksh_tcset(tty_fd, &tty_state);
-			if (ttypgrp_ok && tcsetpgrp(tty_fd, kshpgrp) < 0)
-				warningf(true, "%s %s(%d, %ld) %s: %s",
-				    "fg: 2nd", "tcsetpgrp", tty_fd,
-				    (long)kshpgrp, "failed", cstrerror(errno));
-		}
-		sigprocmask(SIG_SETMASK, &omask, NULL);
-		bi_errorf("%s %s %s", "can't continue job",
-		    cp, cstrerror(eno));
-		return (1);
-	}
-	if (!bg) {
-		if (ttypgrp_ok) {
-			j->flags &= ~(JF_SAVEDTTY | JF_SAVEDTTYPGRP);
-		}
-		rv = j_waitj(j, JW_NONE, "jw:resume");
-	}
-	sigprocmask(SIG_SETMASK, &omask, NULL);
-	return (rv);
-}
-#endif
-
-/* are there any running or stopped jobs ? */
-int
-j_stopped_running(void)
-{
-	Job *j;
-	int which = 0;
-
-	for (j = job_list; j != NULL; j = j->next) {
-#ifndef MKSH_UNEMPLOYED
-		if (j->ppid == procpid && j->state == PSTOPPED)
-			which |= 1;
-#endif
-		if (Flag(FLOGIN) && !Flag(FNOHUP) && procpid == kshpid &&
-		    j->ppid == procpid && j->state == PRUNNING)
-			which |= 2;
-	}
-	if (which) {
-		shellf("You have %s%s%s jobs\n",
-		    which & 1 ? "stopped" : "",
-		    which == 3 ? " and " : "",
-		    which & 2 ? "running" : "");
-		return (1);
-	}
-
-	return (0);
-}
-
-
-/* list jobs for jobs built-in */
-int
-j_jobs(const char *cp, int slp,
-    /* 0: short, 1: long, 2: pgrp */
-    int nflag)
-{
-	Job *j, *tmp;
-	int how, zflag = 0;
-#ifndef MKSH_NOPROSPECTOFWORK
-	sigset_t omask;
-
-	sigprocmask(SIG_BLOCK, &sm_sigchld, &omask);
-#endif
-
-	if (nflag < 0) {
-		/* kludge: print zombies */
-		nflag = 0;
-		zflag = 1;
-	}
-	if (cp) {
-		int ecode;
-
-		if ((j = j_lookup(cp, &ecode)) == NULL) {
-#ifndef MKSH_NOPROSPECTOFWORK
-			sigprocmask(SIG_SETMASK, &omask, NULL);
-#endif
-			bi_errorf("%s: %s", cp, lookup_msgs[ecode]);
-			return (1);
-		}
-	} else
-		j = job_list;
-	how = slp == 0 ? JP_MEDIUM : (slp == 1 ? JP_LONG : JP_PGRP);
-	for (; j; j = j->next) {
-		if ((!(j->flags & JF_ZOMBIE) || zflag) &&
-		    (!nflag || (j->flags & JF_CHANGED))) {
-			j_print(j, how, shl_stdout);
-			if (j->state == PEXITED || j->state == PSIGNALLED)
-				j->flags |= JF_REMOVE;
-		}
-		if (cp)
-			break;
-	}
-	/* Remove jobs after printing so there won't be multiple + or - jobs */
-	for (j = job_list; j; j = tmp) {
-		tmp = j->next;
-		if (j->flags & JF_REMOVE)
-			remove_job(j, "jobs");
-	}
-#ifndef MKSH_NOPROSPECTOFWORK
-	sigprocmask(SIG_SETMASK, &omask, NULL);
-#endif
-	return (0);
-}
-
-/* list jobs for top-level notification */
-void
-j_notify(void)
-{
-	Job *j, *tmp;
-#ifndef MKSH_NOPROSPECTOFWORK
-	sigset_t omask;
-
-	sigprocmask(SIG_BLOCK, &sm_sigchld, &omask);
-#endif
-	for (j = job_list; j; j = j->next) {
-#ifndef MKSH_UNEMPLOYED
-		if (Flag(FMONITOR) && (j->flags & JF_CHANGED))
-			j_print(j, JP_MEDIUM, shl_out);
-#endif
-		/*
-		 * Remove job after doing reports so there aren't
-		 * multiple +/- jobs.
-		 */
-		if (j->state == PEXITED || j->state == PSIGNALLED)
-			j->flags |= JF_REMOVE;
-	}
-	for (j = job_list; j; j = tmp) {
-		tmp = j->next;
-		if (j->flags & JF_REMOVE)
-			remove_job(j, "notify");
-	}
-	shf_flush(shl_out);
-#ifndef MKSH_NOPROSPECTOFWORK
-	sigprocmask(SIG_SETMASK, &omask, NULL);
-#endif
-}
-
-/* Return pid of last process in last asynchronous job */
-pid_t
-j_async(void)
-{
-#ifndef MKSH_NOPROSPECTOFWORK
-	sigset_t omask;
-
-	sigprocmask(SIG_BLOCK, &sm_sigchld, &omask);
-#endif
-
-	if (async_job)
-		async_job->flags |= JF_KNOWN;
-
-#ifndef MKSH_NOPROSPECTOFWORK
-	sigprocmask(SIG_SETMASK, &omask, NULL);
-#endif
-
-	return (async_pid);
-}
-
-/*
- * Make j the last async process
- *
- * If jobs are compiled in then this routine expects sigchld to be blocked.
- */
-static void
-j_set_async(Job *j)
-{
-	Job	*jl, *oldest;
-
-	if (async_job && (async_job->flags & (JF_KNOWN|JF_ZOMBIE)) == JF_ZOMBIE)
-		remove_job(async_job, "async");
-	if (!(j->flags & JF_STARTED)) {
-		internal_warningf("%s: %s", "j_async", "job not started");
-		return;
-	}
-	async_job = j;
-	async_pid = j->last_proc->pid;
-	while (nzombie > CHILD_MAX) {
-		oldest = NULL;
-		for (jl = job_list; jl; jl = jl->next)
-			if (jl != async_job && (jl->flags & JF_ZOMBIE) &&
-			    (!oldest || jl->age < oldest->age))
-				oldest = jl;
-		if (!oldest) {
-			/* XXX debugging */
-			if (!(async_job->flags & JF_ZOMBIE) || nzombie != 1) {
-				internal_warningf("%s: bad nzombie (%d)",
-				    "j_async", nzombie);
-				nzombie = 0;
-			}
-			break;
-		}
-		remove_job(oldest, "zombie");
-	}
-}
-
-/*
- * Start a job: set STARTED, check for held signals and set j->last_proc
- *
- * If jobs are compiled in then this routine expects sigchld to be blocked.
- */
-static void
-j_startjob(Job *j)
-{
-	Proc	*p;
-
-	j->flags |= JF_STARTED;
-	for (p = j->proc_list; p->next; p = p->next)
-		;
-	j->last_proc = p;
-
-#ifndef MKSH_NOPROSPECTOFWORK
-	if (held_sigchld) {
-		held_sigchld = 0;
-		/* Don't call j_sigchld() as it may remove job... */
-		kill(procpid, SIGCHLD);
-	}
-#endif
-}
-
-/*
- * wait for job to complete or change state
- *
- * If jobs are compiled in then this routine expects sigchld to be blocked.
- */
-static int
-j_waitj(Job *j,
-    /* see JW_* */
-    int flags,
-    const char *where)
-{
-	int rv;
-#ifdef MKSH_NO_SIGSUSPEND
-	sigset_t omask;
-#endif
-
-	/*
-	 * No auto-notify on the job we are waiting on.
-	 */
-	j->flags |= JF_WAITING;
-	if (flags & JW_ASYNCNOTIFY)
-		j->flags |= JF_W_ASYNCNOTIFY;
-
-#ifndef MKSH_UNEMPLOYED
-	if (!Flag(FMONITOR))
-#endif
-		flags |= JW_STOPPEDWAIT;
-
-	while (j->state == PRUNNING ||
-	    ((flags & JW_STOPPEDWAIT) && j->state == PSTOPPED)) {
-#ifndef MKSH_NOPROSPECTOFWORK
-#ifdef MKSH_NO_SIGSUSPEND
-		sigprocmask(SIG_SETMASK, &sm_default, &omask);
-		pause();
-		/* note that handlers may run here so they need to know */
-		sigprocmask(SIG_SETMASK, &omask, NULL);
-#else
-		sigsuspend(&sm_default);
-#endif
-#else
-		j_sigchld(SIGCHLD);
-#endif
-		if (fatal_trap) {
-			int oldf = j->flags & (JF_WAITING|JF_W_ASYNCNOTIFY);
-			j->flags &= ~(JF_WAITING|JF_W_ASYNCNOTIFY);
-			runtraps(TF_FATAL);
-			/* not reached... */
-			j->flags |= oldf;
-		}
-		if ((flags & JW_INTERRUPT) && (rv = trap_pending())) {
-			j->flags &= ~(JF_WAITING|JF_W_ASYNCNOTIFY);
-			return (-rv);
-		}
-	}
-	j->flags &= ~(JF_WAITING|JF_W_ASYNCNOTIFY);
-
-	if (j->flags & JF_FG) {
-		j->flags &= ~JF_FG;
-#ifndef MKSH_UNEMPLOYED
-		if (Flag(FMONITOR) && ttypgrp_ok && j->pgrp) {
-			/*
-			 * Save the tty's current pgrp so it can be restored
-			 * when the job is foregrounded. This is to
-			 * deal with things like the GNU su which does
-			 * a fork/exec instead of an exec (the fork means
-			 * the execed shell gets a different pid from its
-			 * pgrp, so naturally it sets its pgrp and gets hosed
-			 * when it gets foregrounded by the parent shell which
-			 * has restored the tty's pgrp to that of the su
-			 * process).
-			 */
-			if (j->state == PSTOPPED &&
-			    (j->saved_ttypgrp = tcgetpgrp(tty_fd)) >= 0)
-				j->flags |= JF_SAVEDTTYPGRP;
-			if (tcsetpgrp(tty_fd, kshpgrp) < 0)
-				warningf(true, "%s %s(%d, %ld) %s: %s",
-				    "j_waitj:", "tcsetpgrp", tty_fd,
-				    (long)kshpgrp, "failed", cstrerror(errno));
-			if (j->state == PSTOPPED) {
-				j->flags |= JF_SAVEDTTY;
-				mksh_tcget(tty_fd, &j->ttystat);
-			}
-		}
-#endif
-		if (tty_hasstate) {
-			/*
-			 * Only restore tty settings if job was originally
-			 * started in the foreground. Problems can be
-			 * caused by things like 'more foobar &' which will
-			 * typically get and save the shell's vi/emacs tty
-			 * settings before setting up the tty for itself;
-			 * when more exits, it restores the 'original'
-			 * settings, and things go down hill from there...
-			 */
-			if (j->state == PEXITED && j->status == 0 &&
-			    (j->flags & JF_USETTYMODE)) {
-				mksh_tcget(tty_fd, &tty_state);
-			} else {
-				mksh_tcset(tty_fd, &tty_state);
-				/*-
-				 * Don't use tty mode if job is stopped and
-				 * later restarted and exits. Consider
-				 * the sequence:
-				 *	vi foo (stopped)
-				 *	...
-				 *	stty something
-				 *	...
-				 *	fg (vi; ZZ)
-				 * mode should be that of the stty, not what
-				 * was before the vi started.
-				 */
-				if (j->state == PSTOPPED)
-					j->flags &= ~JF_USETTYMODE;
-			}
-		}
-#ifndef MKSH_UNEMPLOYED
-		/*
-		 * If it looks like user hit ^C to kill a job, pretend we got
-		 * one too to break out of for loops, etc. (AT&T ksh does this
-		 * even when not monitoring, but this doesn't make sense since
-		 * a tty generated ^C goes to the whole process group)
-		 */
-		{
-			int status;
-
-			status = j->last_proc->status;
-			if (Flag(FMONITOR) && j->state == PSIGNALLED &&
-			    WIFSIGNALED(status) &&
-			    (sigtraps[WTERMSIG(status)].flags & TF_TTY_INTR))
-				trapsig(WTERMSIG(status));
-		}
-#endif
-	}
-
-	j_usrtime = j->usrtime;
-	j_systime = j->systime;
-	rv = j->status;
-
-	if ((flags & JW_PIPEST) && (j->proc_list != NULL)) {
-		uint32_t num = 0;
-		Proc *p = j->proc_list;
-		struct tbl *vp;
-
-		unset(vp_pipest, 1);
-		vp = vp_pipest;
-		vp->flag = DEFINED | ISSET | INTEGER | RDONLY | ARRAY | INT_U;
-		goto got_array;
-
-		while (p != NULL) {
-			{
-				struct tbl *vq;
-
-				/* strlen(vp_pipest->name) == 10 */
-				vq = alloc(offsetof(struct tbl, name[0]) + 11,
-				    vp_pipest->areap);
-				memset(vq, 0, offsetof(struct tbl, name[0]));
-				memcpy(vq->name, vp_pipest->name, 11);
-				vp->u.array = vq;
-				vp = vq;
-			}
-			vp->areap = vp_pipest->areap;
-			vp->ua.index = ++num;
-			vp->flag = DEFINED | ISSET | INTEGER | RDONLY |
-			    ARRAY | INT_U | AINDEX;
- got_array:
-			vp->val.i = proc_errorlevel(p);
-			if (Flag(FPIPEFAIL) && vp->val.i)
-				rv = vp->val.i;
-			p = p->next;
-		}
-	}
-
-	if (!(flags & JW_ASYNCNOTIFY)
-#ifndef MKSH_UNEMPLOYED
-	    && (!Flag(FMONITOR) || j->state != PSTOPPED)
-#endif
-	    ) {
-		j_print(j, JP_SHORT, shl_out);
-		shf_flush(shl_out);
-	}
-	if (j->state != PSTOPPED
-#ifndef MKSH_UNEMPLOYED
-	    && (!Flag(FMONITOR) || !(flags & JW_ASYNCNOTIFY))
-#endif
-	    )
-		remove_job(j, where);
-
-	return (rv);
-}
-
-/*
- * SIGCHLD handler to reap children and update job states
- *
- * If jobs are compiled in then this routine expects sigchld to be blocked.
- */
-/* ARGSUSED */
-static void
-j_sigchld(int sig MKSH_A_UNUSED)
-{
-	int saved_errno = errno;
-	Job *j;
-	Proc *p = NULL;
-	pid_t pid;
-	int status;
-	struct rusage ru0, ru1;
-#ifdef MKSH_NO_SIGSUSPEND
-	sigset_t omask;
-
-	/* this handler can run while SIGCHLD is not blocked, so block it now */
-	sigprocmask(SIG_BLOCK, &sm_sigchld, &omask);
-#endif
-
-#ifndef MKSH_NOPROSPECTOFWORK
-	/*
-	 * Don't wait for any processes if a job is partially started.
-	 * This is so we don't do away with the process group leader
-	 * before all the processes in a pipe line are started (so the
-	 * setpgid() won't fail)
-	 */
-	for (j = job_list; j; j = j->next)
-		if (j->ppid == procpid && !(j->flags & JF_STARTED)) {
-			held_sigchld = 1;
-			goto j_sigchld_out;
-		}
-#endif
-
-	getrusage(RUSAGE_CHILDREN, &ru0);
-	do {
-#ifndef MKSH_NOPROSPECTOFWORK
-		pid = waitpid(-1, &status, (WNOHANG|WUNTRACED));
-#else
-		pid = wait(&status);
-#endif
-
-		/*
-		 * return if this would block (0) or no children
-		 * or interrupted (-1)
-		 */
-		if (pid <= 0)
-			goto j_sigchld_out;
-
-		getrusage(RUSAGE_CHILDREN, &ru1);
-
-		/* find job and process structures for this pid */
-		for (j = job_list; j != NULL; j = j->next)
-			for (p = j->proc_list; p != NULL; p = p->next)
-				if (p->pid == pid)
-					goto found;
- found:
-		if (j == NULL) {
-			/* Can occur if process has kids, then execs shell
-			warningf(true, "bad process waited for (pid = %d)",
-				pid);
-			 */
-			ru0 = ru1;
-			continue;
-		}
-
-		timeradd(&j->usrtime, &ru1.ru_utime, &j->usrtime);
-		timersub(&j->usrtime, &ru0.ru_utime, &j->usrtime);
-		timeradd(&j->systime, &ru1.ru_stime, &j->systime);
-		timersub(&j->systime, &ru0.ru_stime, &j->systime);
-		ru0 = ru1;
-		p->status = status;
-#ifndef MKSH_UNEMPLOYED
-		if (WIFSTOPPED(status))
-			p->state = PSTOPPED;
-		else
-#endif
-		  if (WIFSIGNALED(status))
-			p->state = PSIGNALLED;
-		else
-			p->state = PEXITED;
-
-		/* check to see if entire job is done */
-		check_job(j);
-	}
-#ifndef MKSH_NOPROSPECTOFWORK
-	    while (/* CONSTCOND */ 1);
-#else
-	    while (/* CONSTCOND */ 0);
-#endif
-
- j_sigchld_out:
-#ifdef MKSH_NO_SIGSUSPEND
-	sigprocmask(SIG_SETMASK, &omask, NULL);
-#endif
-	errno = saved_errno;
-}
-
-/*
- * Called only when a process in j has exited/stopped (ie, called only
- * from j_sigchld()). If no processes are running, the job status
- * and state are updated, asynchronous job notification is done and,
- * if unneeded, the job is removed.
- *
- * If jobs are compiled in then this routine expects sigchld to be blocked.
- */
-static void
-check_job(Job *j)
-{
-	int	jstate;
-	Proc	*p;
-
-	/* XXX debugging (nasty - interrupt routine using shl_out) */
-	if (!(j->flags & JF_STARTED)) {
-		internal_warningf("check_job: job started (flags 0x%x)",
-		    j->flags);
-		return;
-	}
-
-	jstate = PRUNNING;
-	for (p=j->proc_list; p != NULL; p = p->next) {
-		if (p->state == PRUNNING)
-			/* some processes still running */
-			return;
-		if (p->state > jstate)
-			jstate = p->state;
-	}
-	j->state = jstate;
-	j->status = proc_errorlevel(j->last_proc);
-
-	/*
-	 * Note when co-process dies: can't be done in j_wait() nor
-	 * remove_job() since neither may be called for non-interactive
-	 * shells.
-	 */
-	if (j->state == PEXITED || j->state == PSIGNALLED) {
-		/*
-		 * No need to keep co-process input any more
-		 * (at least, this is what ksh93d thinks)
-		 */
-		if (coproc.job == j) {
-			coproc.job = NULL;
-			/*
-			 * XXX would be nice to get the closes out of here
-			 * so they aren't done in the signal handler.
-			 * Would mean a check in coproc_getfd() to
-			 * do "if job == 0 && write >= 0, close write".
-			 */
-			coproc_write_close(coproc.write);
-		}
-		/* Do we need to keep the output? */
-		if (j->coproc_id && j->coproc_id == coproc.id &&
-		    --coproc.njobs == 0)
-			coproc_readw_close(coproc.read);
-	}
-
-	j->flags |= JF_CHANGED;
-#ifndef MKSH_UNEMPLOYED
-	if (Flag(FMONITOR) && !(j->flags & JF_XXCOM)) {
-		/*
-		 * Only put stopped jobs at the front to avoid confusing
-		 * the user (don't want finished jobs effecting %+ or %-)
-		 */
-		if (j->state == PSTOPPED)
-			put_job(j, PJ_ON_FRONT);
-		if (Flag(FNOTIFY) &&
-		    (j->flags & (JF_WAITING|JF_W_ASYNCNOTIFY)) != JF_WAITING) {
-			/* Look for the real file descriptor 2 */
-			{
-				struct env *ep;
-				int fd = 2;
-
-				for (ep = e; ep; ep = ep->oenv)
-					if (ep->savefd && ep->savefd[2])
-						fd = ep->savefd[2];
-				shf_reopen(fd, SHF_WR, shl_j);
-			}
-			/*
-			 * Can't call j_notify() as it removes jobs. The job
-			 * must stay in the job list as j_waitj() may be
-			 * running with this job.
-			 */
-			j_print(j, JP_MEDIUM, shl_j);
-			shf_flush(shl_j);
-			if (!(j->flags & JF_WAITING) && j->state != PSTOPPED)
-				remove_job(j, "notify");
-		}
-	}
-#endif
-	if (
-#ifndef MKSH_UNEMPLOYED
-	    !Flag(FMONITOR) &&
-#endif
-	    !(j->flags & (JF_WAITING|JF_FG)) &&
-	    j->state != PSTOPPED) {
-		if (j == async_job || (j->flags & JF_KNOWN)) {
-			j->flags |= JF_ZOMBIE;
-			j->job = -1;
-			nzombie++;
-		} else
-			remove_job(j, "checkjob");
-	}
-}
-
-/*
- * Print job status in either short, medium or long format.
- *
- * If jobs are compiled in then this routine expects sigchld to be blocked.
- */
-static void
-j_print(Job *j, int how, struct shf *shf)
-{
-	Proc	*p;
-	int	state;
-	int	status;
-	int	coredumped;
-	char	jobchar = ' ';
-	char	buf[64];
-	const char *filler;
-	int	output = 0;
-
-	if (how == JP_PGRP) {
-		/*
-		 * POSIX doesn't say what to do it there is no process
-		 * group leader (ie, !FMONITOR). We arbitrarily return
-		 * last pid (which is what $! returns).
-		 */
-		shf_fprintf(shf, "%d\n", (int)(j->pgrp ? j->pgrp :
-		    (j->last_proc ? j->last_proc->pid : 0)));
-		return;
-	}
-	j->flags &= ~JF_CHANGED;
-	filler = j->job > 10 ? "\n       " : "\n      ";
-	if (j == job_list)
-		jobchar = '+';
-	else if (j == job_list->next)
-		jobchar = '-';
-
-	for (p = j->proc_list; p != NULL;) {
-		coredumped = 0;
-		switch (p->state) {
-		case PRUNNING:
-			memcpy(buf, "Running", 8);
-			break;
-		case PSTOPPED:
-			strlcpy(buf, sigtraps[WSTOPSIG(p->status)].mess,
-			    sizeof(buf));
-			break;
-		case PEXITED:
-			if (how == JP_SHORT)
-				buf[0] = '\0';
-			else if (WEXITSTATUS(p->status) == 0)
-				memcpy(buf, "Done", 5);
-			else
-				shf_snprintf(buf, sizeof(buf), "Done (%d)",
-				    WEXITSTATUS(p->status));
-			break;
-		case PSIGNALLED:
-#ifdef WCOREDUMP
-			if (WCOREDUMP(p->status))
-				coredumped = 1;
-#endif
-			/*
-			 * kludge for not reporting 'normal termination
-			 * signals' (i.e. SIGINT, SIGPIPE)
-			 */
-			if (how == JP_SHORT && !coredumped &&
-			    (WTERMSIG(p->status) == SIGINT ||
-			    WTERMSIG(p->status) == SIGPIPE)) {
-				buf[0] = '\0';
-			} else
-				strlcpy(buf, sigtraps[WTERMSIG(p->status)].mess,
-				    sizeof(buf));
-			break;
-		default:
-			buf[0] = '\0';
-		}
-
-		if (how != JP_SHORT) {
-			if (p == j->proc_list)
-				shf_fprintf(shf, "[%d] %c ", j->job, jobchar);
-			else
-				shf_puts(filler, shf);
-		}
-
-		if (how == JP_LONG)
-			shf_fprintf(shf, "%5d ", (int)p->pid);
-
-		if (how == JP_SHORT) {
-			if (buf[0]) {
-				output = 1;
-				shf_fprintf(shf, "%s%s ",
-				    buf, coredumped ? " (core dumped)" : null);
-			}
-		} else {
-			output = 1;
-			shf_fprintf(shf, "%-20s %s%s%s", buf, p->command,
-			    p->next ? "|" : null,
-			    coredumped ? " (core dumped)" : null);
-		}
-
-		state = p->state;
-		status = p->status;
-		p = p->next;
-		while (p && p->state == state && p->status == status) {
-			if (how == JP_LONG)
-				shf_fprintf(shf, "%s%5d %-20s %s%s", filler,
-				    (int)p->pid, " ", p->command,
-				    p->next ? "|" : null);
-			else if (how == JP_MEDIUM)
-				shf_fprintf(shf, " %s%s", p->command,
-				    p->next ? "|" : null);
-			p = p->next;
-		}
-	}
-	if (output)
-		shf_putc('\n', shf);
-}
-
-/*
- * Convert % sequence to job
- *
- * If jobs are compiled in then this routine expects sigchld to be blocked.
- */
-static Job *
-j_lookup(const char *cp, int *ecodep)
-{
-	Job *j, *last_match;
-	Proc *p;
-	size_t len;
-	int job = 0;
-
-	if (ksh_isdigit(*cp)) {
-		getn(cp, &job);
-		/* Look for last_proc->pid (what $! returns) first... */
-		for (j = job_list; j != NULL; j = j->next)
-			if (j->last_proc && j->last_proc->pid == job)
-				return (j);
-		/*
-		 * ...then look for process group (this is non-POSIX,
-		 * but should not break anything
-		 */
-		for (j = job_list; j != NULL; j = j->next)
-			if (j->pgrp && j->pgrp == job)
-				return (j);
-		if (ecodep)
-			*ecodep = JL_NOSUCH;
-		return (NULL);
-	}
-	if (*cp != '%') {
-		if (ecodep)
-			*ecodep = JL_INVALID;
-		return (NULL);
-	}
-	switch (*++cp) {
-	case '\0': /* non-standard */
-	case '+':
-	case '%':
-		if (job_list != NULL)
-			return (job_list);
-		break;
-
-	case '-':
-		if (job_list != NULL && job_list->next)
-			return (job_list->next);
-		break;
-
-	case '0': case '1': case '2': case '3': case '4':
-	case '5': case '6': case '7': case '8': case '9':
-		getn(cp, &job);
-		for (j = job_list; j != NULL; j = j->next)
-			if (j->job == job)
-				return (j);
-		break;
-
-	/* %?string */
-	case '?':
-		last_match = NULL;
-		for (j = job_list; j != NULL; j = j->next)
-			for (p = j->proc_list; p != NULL; p = p->next)
-				if (strstr(p->command, cp+1) != NULL) {
-					if (last_match) {
-						if (ecodep)
-							*ecodep = JL_AMBIG;
-						return (NULL);
-					}
-					last_match = j;
-				}
-		if (last_match)
-			return (last_match);
-		break;
-
-	/* %string */
-	default:
-		len = strlen(cp);
-		last_match = NULL;
-		for (j = job_list; j != NULL; j = j->next)
-			if (strncmp(cp, j->proc_list->command, len) == 0) {
-				if (last_match) {
-					if (ecodep)
-						*ecodep = JL_AMBIG;
-					return (NULL);
-				}
-				last_match = j;
-			}
-		if (last_match)
-			return (last_match);
-		break;
-	}
-	if (ecodep)
-		*ecodep = JL_NOSUCH;
-	return (NULL);
-}
-
-static Job	*free_jobs;
-static Proc	*free_procs;
-
-/*
- * allocate a new job and fill in the job number.
- *
- * If jobs are compiled in then this routine expects sigchld to be blocked.
- */
-static Job *
-new_job(void)
-{
-	int	i;
-	Job	*newj, *j;
-
-	if (free_jobs != NULL) {
-		newj = free_jobs;
-		free_jobs = free_jobs->next;
-	} else
-		newj = alloc(sizeof(Job), APERM);
-
-	/* brute force method */
-	for (i = 1; ; i++) {
-		for (j = job_list; j && j->job != i; j = j->next)
-			;
-		if (j == NULL)
-			break;
-	}
-	newj->job = i;
-
-	return (newj);
-}
-
-/*
- * Allocate new process struct
- *
- * If jobs are compiled in then this routine expects sigchld to be blocked.
- */
-static Proc *
-new_proc(void)
-{
-	Proc	*p;
-
-	if (free_procs != NULL) {
-		p = free_procs;
-		free_procs = free_procs->next;
-	} else
-		p = alloc(sizeof(Proc), APERM);
-
-	return (p);
-}
-
-/*
- * Take job out of job_list and put old structures into free list.
- * Keeps nzombies, last_job and async_job up to date.
- *
- * If jobs are compiled in then this routine expects sigchld to be blocked.
- */
-static void
-remove_job(Job *j, const char *where)
-{
-	Proc	*p, *tmp;
-	Job	**prev, *curr;
-
-	mkssert(j != NULL);
-	prev = &job_list;
-	curr = job_list;
-	while (curr && curr != j) {
-		prev = &curr->next;
-		curr = *prev;
-	}
-	if (curr != j) {
-		internal_warningf("remove_job: job %s (%s)", "not found", where);
-		return;
-	}
-	*prev = curr->next;
-
-	/* free up proc structures */
-	for (p = j->proc_list; p != NULL; ) {
-		tmp = p;
-		p = p->next;
-		tmp->next = free_procs;
-		free_procs = tmp;
-	}
-
-	if ((j->flags & JF_ZOMBIE) && j->ppid == procpid)
-		--nzombie;
-	j->next = free_jobs;
-	free_jobs = j;
-
-	if (j == last_job)
-		last_job = NULL;
-	if (j == async_job)
-		async_job = NULL;
-}
-
-/*
- * put j in a particular location (taking it out job_list if it is there
- * already)
- *
- * If jobs are compiled in then this routine expects sigchld to be blocked.
- */
-static void
-put_job(Job *j, int where)
-{
-	Job	**prev, *curr;
-
-	mkssert(j != NULL);
-	/* Remove job from list (if there) */
-	prev = &job_list;
-	curr = job_list;
-	while (curr && curr != j) {
-		prev = &curr->next;
-		curr = *prev;
-	}
-	if (curr == j)
-		*prev = curr->next;
-
-	switch (where) {
-	case PJ_ON_FRONT:
-		j->next = job_list;
-		job_list = j;
-		break;
-
-	case PJ_PAST_STOPPED:
-		prev = &job_list;
-		curr = job_list;
-		for (; curr && curr->state == PSTOPPED; prev = &curr->next,
-		    curr = *prev)
-			;
-		j->next = curr;
-		*prev = j;
-		break;
-	}
-}
-
-/*
- * nuke a job (called when unable to start full job).
- *
- * If jobs are compiled in then this routine expects sigchld to be blocked.
- */
-static int
-kill_job(Job *j, int sig)
-{
-	Proc	*p;
-	int	rval = 0;
-
-	for (p = j->proc_list; p != NULL; p = p->next)
-		if (p->pid != 0)
-			if (kill(p->pid, sig) < 0)
-				rval = -1;
-	return (rval);
-}
-
-static void
-tty_init_talking(void)
-{
-	switch (tty_init_fd()) {
-	case 0:
-		break;
-	case 1:
-#ifndef MKSH_DISABLE_TTY_WARNING
-		warningf(false, "%s: %s %s: %s",
-		    "No controlling tty", "open", "/dev/tty",
-		    cstrerror(errno));
-#endif
-		break;
-	case 2:
-#ifndef MKSH_DISABLE_TTY_WARNING
-		warningf(false, "%s: %s", "can't find tty fd", cstrerror(errno));
-#endif
-		break;
-	case 3:
-		warningf(false, "%s: %s %s: %s", "j_ttyinit",
-		    "dup of tty fd", "failed", cstrerror(errno));
-		break;
-	case 4:
-		warningf(false, "%s: %s: %s", "j_ttyinit",
-		    "can't set close-on-exec flag", cstrerror(errno));
-		break;
-	}
-}
-
-static void
-tty_init_state(void)
-{
-	if (tty_fd >= 0) {
-		mksh_tcget(tty_fd, &tty_state);
-		tty_hasstate = true;
-	}
-}

Copied: vendor/MirOS/mksh/R50/jobs.c (from rev 6707, vendor/MirOS/mksh/dist/jobs.c)
===================================================================
--- vendor/MirOS/mksh/R50/jobs.c	                        (rev 0)
+++ vendor/MirOS/mksh/R50/jobs.c	2014-07-01 12:01:17 UTC (rev 6708)
@@ -0,0 +1,1918 @@
+/*	$OpenBSD: jobs.c,v 1.40 2013/09/04 15:49:18 millert Exp $	*/
+
+/*-
+ * Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2011,
+ *		 2012, 2013, 2014
+ *	Thorsten Glaser <tg at mirbsd.org>
+ *
+ * Provided that these terms and disclaimer and all copyright notices
+ * are retained or reproduced in an accompanying document, permission
+ * is granted to deal in this work without restriction, including un-
+ * limited rights to use, publicly perform, distribute, sell, modify,
+ * merge, give away, or sublicence.
+ *
+ * This work is provided "AS IS" and WITHOUT WARRANTY of any kind, to
+ * the utmost extent permitted by applicable law, neither express nor
+ * implied; without malicious intent or gross negligence. In no event
+ * may a licensor, author or contributor be held liable for indirect,
+ * direct, other damage, loss, or other issues arising in any way out
+ * of dealing in the work, even if advised of the possibility of such
+ * damage or existence of a defect, except proven that it results out
+ * of said person's immediate fault when using the work as intended.
+ */
+
+#include "sh.h"
+
+__RCSID("$MirOS: src/bin/mksh/jobs.c,v 1.104 2014/06/10 22:17:09 tg Exp $");
+
+#if HAVE_KILLPG
+#define mksh_killpg		killpg
+#else
+/* cross fingers and hope kill is killpg-endowed */
+#define mksh_killpg(p,s)	kill(-(p), (s))
+#endif
+
+/* Order important! */
+#define PRUNNING	0
+#define PEXITED		1
+#define PSIGNALLED	2
+#define PSTOPPED	3
+
+typedef struct proc Proc;
+struct proc {
+	Proc *next;		/* next process in pipeline (if any) */
+	pid_t pid;		/* process id */
+	int state;
+	int status;		/* wait status */
+	/* process command string from vistree */
+	char command[256 - (ALLOC_SIZE + sizeof(Proc *) + sizeof(pid_t) +
+	    2 * sizeof(int))];
+};
+
+/* Notify/print flag - j_print() argument */
+#define JP_SHORT	1	/* print signals processes were killed by */
+#define JP_MEDIUM	2	/* print [job-num] -/+ command */
+#define JP_LONG		3	/* print [job-num] -/+ pid command */
+#define JP_PGRP		4	/* print pgrp */
+
+/* put_job() flags */
+#define PJ_ON_FRONT	0	/* at very front */
+#define PJ_PAST_STOPPED	1	/* just past any stopped jobs */
+
+/* Job.flags values */
+#define JF_STARTED	0x001	/* set when all processes in job are started */
+#define JF_WAITING	0x002	/* set if j_waitj() is waiting on job */
+#define JF_W_ASYNCNOTIFY 0x004	/* set if waiting and async notification ok */
+#define JF_XXCOM	0x008	/* set for $(command) jobs */
+#define JF_FG		0x010	/* running in foreground (also has tty pgrp) */
+#define JF_SAVEDTTY	0x020	/* j->ttystat is valid */
+#define JF_CHANGED	0x040	/* process has changed state */
+#define JF_KNOWN	0x080	/* $! referenced */
+#define JF_ZOMBIE	0x100	/* known, unwaited process */
+#define JF_REMOVE	0x200	/* flagged for removal (j_jobs()/j_noityf()) */
+#define JF_USETTYMODE	0x400	/* tty mode saved if process exits normally */
+#define JF_SAVEDTTYPGRP	0x800	/* j->saved_ttypgrp is valid */
+
+typedef struct job Job;
+struct job {
+	Job *next;		/* next job in list */
+	Proc *proc_list;	/* process list */
+	Proc *last_proc;	/* last process in list */
+	struct timeval systime;	/* system time used by job */
+	struct timeval usrtime;	/* user time used by job */
+	pid_t pgrp;		/* process group of job */
+	pid_t ppid;		/* pid of process that forked job */
+	int job;		/* job number: %n */
+	int flags;		/* see JF_* */
+	volatile int state;	/* job state */
+	int status;		/* exit status of last process */
+	int32_t	age;		/* number of jobs started */
+	Coproc_id coproc_id;	/* 0 or id of coprocess output pipe */
+#ifndef MKSH_UNEMPLOYED
+	mksh_ttyst ttystat;	/* saved tty state for stopped jobs */
+	pid_t saved_ttypgrp;	/* saved tty process group for stopped jobs */
+#endif
+};
+
+/* Flags for j_waitj() */
+#define JW_NONE		0x00
+#define JW_INTERRUPT	0x01	/* ^C will stop the wait */
+#define JW_ASYNCNOTIFY	0x02	/* asynchronous notification during wait ok */
+#define JW_STOPPEDWAIT	0x04	/* wait even if job stopped */
+#define JW_PIPEST	0x08	/* want PIPESTATUS */
+
+/* Error codes for j_lookup() */
+#define JL_NOSUCH	0	/* no such job */
+#define JL_AMBIG	1	/* %foo or %?foo is ambiguous */
+#define JL_INVALID	2	/* non-pid, non-% job id */
+
+static const char * const lookup_msgs[] = {
+	"no such job",
+	"ambiguous",
+	"argument must be %job or process id"
+};
+
+static Job *job_list;		/* job list */
+static Job *last_job;
+static Job *async_job;
+static pid_t async_pid;
+
+static int nzombie;		/* # of zombies owned by this process */
+static int32_t njobs;		/* # of jobs started */
+
+#ifndef CHILD_MAX
+#define CHILD_MAX	25
+#endif
+
+#ifndef MKSH_NOPROSPECTOFWORK
+/* held_sigchld is set if sigchld occurs before a job is completely started */
+static volatile sig_atomic_t held_sigchld;
+#endif
+
+#ifndef MKSH_UNEMPLOYED
+static struct shf	*shl_j;
+static bool		ttypgrp_ok;	/* set if can use tty pgrps */
+static pid_t		restore_ttypgrp = -1;
+static int const	tt_sigs[] = { SIGTSTP, SIGTTIN, SIGTTOU };
+#endif
+
+static void		j_set_async(Job *);
+static void		j_startjob(Job *);
+static int		j_waitj(Job *, int, const char *);
+static void		j_sigchld(int);
+static void		j_print(Job *, int, struct shf *);
+static Job		*j_lookup(const char *, int *);
+static Job		*new_job(void);
+static Proc		*new_proc(void);
+static void		check_job(Job *);
+static void		put_job(Job *, int);
+static void		remove_job(Job *, const char *);
+static int		kill_job(Job *, int);
+
+static void tty_init_talking(void);
+static void tty_init_state(void);
+
+/* initialise job control */
+void
+j_init(void)
+{
+#ifndef MKSH_UNEMPLOYED
+	bool mflagset = Flag(FMONITOR) != 127;
+
+	Flag(FMONITOR) = 0;
+#endif
+
+#ifndef MKSH_NOPROSPECTOFWORK
+	(void)sigemptyset(&sm_default);
+	sigprocmask(SIG_SETMASK, &sm_default, NULL);
+
+	(void)sigemptyset(&sm_sigchld);
+	(void)sigaddset(&sm_sigchld, SIGCHLD);
+
+	setsig(&sigtraps[SIGCHLD], j_sigchld,
+	    SS_RESTORE_ORIG|SS_FORCE|SS_SHTRAP);
+#else
+	/* Make sure SIGCHLD isn't ignored - can do odd things under SYSV */
+	setsig(&sigtraps[SIGCHLD], SIG_DFL, SS_RESTORE_ORIG|SS_FORCE);
+#endif
+
+#ifndef MKSH_UNEMPLOYED
+	if (!mflagset && Flag(FTALKING))
+		Flag(FMONITOR) = 1;
+
+	/*
+	 * shl_j is used to do asynchronous notification (used in
+	 * an interrupt handler, so need a distinct shf)
+	 */
+	shl_j = shf_fdopen(2, SHF_WR, NULL);
+
+	if (Flag(FMONITOR) || Flag(FTALKING)) {
+		int i;
+
+		/*
+		 * the TF_SHELL_USES test is a kludge that lets us know if
+		 * if the signals have been changed by the shell.
+		 */
+		for (i = NELEM(tt_sigs); --i >= 0; ) {
+			sigtraps[tt_sigs[i]].flags |= TF_SHELL_USES;
+			/* j_change() sets this to SS_RESTORE_DFL if FMONITOR */
+			setsig(&sigtraps[tt_sigs[i]], SIG_IGN,
+			    SS_RESTORE_IGN|SS_FORCE);
+		}
+	}
+
+	/* j_change() calls tty_init_talking() and tty_init_state() */
+	if (Flag(FMONITOR))
+		j_change();
+	else
+#endif
+	  if (Flag(FTALKING)) {
+		tty_init_talking();
+		tty_init_state();
+	}
+}
+
+static int
+proc_errorlevel(Proc *p)
+{
+	switch (p->state) {
+	case PEXITED:
+		return (WEXITSTATUS(p->status));
+	case PSIGNALLED:
+		return (128 + WTERMSIG(p->status));
+	default:
+		return (0);
+	}
+}
+
+#if !defined(MKSH_UNEMPLOYED) && HAVE_GETSID
+/* suspend the shell */
+void
+j_suspend(void)
+{
+	struct sigaction sa, osa;
+
+	/* Restore tty and pgrp. */
+	if (ttypgrp_ok) {
+		if (tty_hasstate)
+			mksh_tcset(tty_fd, &tty_state);
+		if (restore_ttypgrp >= 0) {
+			if (tcsetpgrp(tty_fd, restore_ttypgrp) < 0) {
+				warningf(false, "%s: %s %s: %s", "j_suspend",
+				    "tcsetpgrp", "failed", cstrerror(errno));
+			} else if (setpgid(0, restore_ttypgrp) < 0) {
+				warningf(false, "%s: %s %s: %s", "j_suspend",
+				    "setpgid", "failed", cstrerror(errno));
+			}
+		}
+	}
+
+	/* Suspend the shell. */
+	memset(&sa, 0, sizeof(sa));
+	sigemptyset(&sa.sa_mask);
+	sa.sa_handler = SIG_DFL;
+	sigaction(SIGTSTP, &sa, &osa);
+	kill(0, SIGTSTP);
+
+	/* Back from suspend, reset signals, pgrp and tty. */
+	sigaction(SIGTSTP, &osa, NULL);
+	if (ttypgrp_ok) {
+		if (restore_ttypgrp >= 0) {
+			if (setpgid(0, kshpid) < 0) {
+				warningf(false, "%s: %s %s: %s", "j_suspend",
+				    "setpgid", "failed", cstrerror(errno));
+				ttypgrp_ok = false;
+			} else if (tcsetpgrp(tty_fd, kshpid) < 0) {
+				warningf(false, "%s: %s %s: %s", "j_suspend",
+				    "tcsetpgrp", "failed", cstrerror(errno));
+				ttypgrp_ok = false;
+			}
+		}
+		tty_init_state();
+	}
+}
+#endif
+
+/* job cleanup before shell exit */
+void
+j_exit(void)
+{
+	/* kill stopped, and possibly running, jobs */
+	Job *j;
+	bool killed = false;
+
+	for (j = job_list; j != NULL; j = j->next) {
+		if (j->ppid == procpid &&
+		    (j->state == PSTOPPED ||
+		    (j->state == PRUNNING &&
+		    ((j->flags & JF_FG) ||
+		    (Flag(FLOGIN) && !Flag(FNOHUP) && procpid == kshpid))))) {
+			killed = true;
+			if (j->pgrp == 0)
+				kill_job(j, SIGHUP);
+			else
+				mksh_killpg(j->pgrp, SIGHUP);
+#ifndef MKSH_UNEMPLOYED
+			if (j->state == PSTOPPED) {
+				if (j->pgrp == 0)
+					kill_job(j, SIGCONT);
+				else
+					mksh_killpg(j->pgrp, SIGCONT);
+			}
+#endif
+		}
+	}
+	if (killed)
+		sleep(1);
+	j_notify();
+
+#ifndef MKSH_UNEMPLOYED
+	if (kshpid == procpid && restore_ttypgrp >= 0) {
+		/*
+		 * Need to restore the tty pgrp to what it was when the
+		 * shell started up, so that the process that started us
+		 * will be able to access the tty when we are done.
+		 * Also need to restore our process group in case we are
+		 * about to do an exec so that both our parent and the
+		 * process we are to become will be able to access the tty.
+		 */
+		tcsetpgrp(tty_fd, restore_ttypgrp);
+		setpgid(0, restore_ttypgrp);
+	}
+	if (Flag(FMONITOR)) {
+		Flag(FMONITOR) = 0;
+		j_change();
+	}
+#endif
+}
+
+#ifndef MKSH_UNEMPLOYED
+/* turn job control on or off according to Flag(FMONITOR) */
+void
+j_change(void)
+{
+	int i;
+
+	if (Flag(FMONITOR)) {
+		bool use_tty = Flag(FTALKING);
+
+		/* don't call mksh_tcget until we own the tty process group */
+		if (use_tty)
+			tty_init_talking();
+
+		/* no controlling tty, no SIGT* */
+		if ((ttypgrp_ok = (use_tty && tty_fd >= 0 && tty_devtty))) {
+			setsig(&sigtraps[SIGTTIN], SIG_DFL,
+			    SS_RESTORE_ORIG|SS_FORCE);
+			/* wait to be given tty (POSIX.1, B.2, job control) */
+			while (/* CONSTCOND */ 1) {
+				pid_t ttypgrp;
+
+				if ((ttypgrp = tcgetpgrp(tty_fd)) < 0) {
+					warningf(false, "%s: %s %s: %s",
+					    "j_init", "tcgetpgrp", "failed",
+					    cstrerror(errno));
+					ttypgrp_ok = false;
+					break;
+				}
+				if (ttypgrp == kshpgrp)
+					break;
+				kill(0, SIGTTIN);
+			}
+		}
+		for (i = NELEM(tt_sigs); --i >= 0; )
+			setsig(&sigtraps[tt_sigs[i]], SIG_IGN,
+			    SS_RESTORE_DFL|SS_FORCE);
+		if (ttypgrp_ok && kshpgrp != kshpid) {
+			if (setpgid(0, kshpid) < 0) {
+				warningf(false, "%s: %s %s: %s", "j_init",
+				    "setpgid", "failed", cstrerror(errno));
+				ttypgrp_ok = false;
+			} else {
+				if (tcsetpgrp(tty_fd, kshpid) < 0) {
+					warningf(false, "%s: %s %s: %s",
+					    "j_init", "tcsetpgrp", "failed",
+					    cstrerror(errno));
+					ttypgrp_ok = false;
+				} else
+					restore_ttypgrp = kshpgrp;
+				kshpgrp = kshpid;
+			}
+		}
+#ifndef MKSH_DISABLE_TTY_WARNING
+		if (use_tty && !ttypgrp_ok)
+			warningf(false, "%s: %s", "warning",
+			    "won't have full job control");
+#endif
+	} else {
+		ttypgrp_ok = false;
+		if (Flag(FTALKING))
+			for (i = NELEM(tt_sigs); --i >= 0; )
+				setsig(&sigtraps[tt_sigs[i]], SIG_IGN,
+				    SS_RESTORE_IGN|SS_FORCE);
+		else
+			for (i = NELEM(tt_sigs); --i >= 0; ) {
+				if (sigtraps[tt_sigs[i]].flags &
+				    (TF_ORIG_IGN | TF_ORIG_DFL))
+					setsig(&sigtraps[tt_sigs[i]],
+					    (sigtraps[tt_sigs[i]].flags & TF_ORIG_IGN) ?
+					    SIG_IGN : SIG_DFL,
+					    SS_RESTORE_ORIG|SS_FORCE);
+			}
+	}
+	tty_init_state();
+}
+#endif
+
+#if HAVE_NICE
+/* run nice(3) and ignore the result */
+static void
+ksh_nice(int ness)
+{
+#if defined(__USE_FORTIFY_LEVEL) && (__USE_FORTIFY_LEVEL > 0)
+	int eno;
+
+	errno = 0;
+	/* this is gonna annoy users; complain to your distro, people! */
+	if (nice(ness) == -1 && (eno = errno) != 0)
+		warningf(false, "%s: %s", "bgnice", cstrerror(eno));
+#else
+	(void)nice(ness);
+#endif
+}
+#endif
+
+/* execute tree in child subprocess */
+int
+exchild(struct op *t, int flags,
+    volatile int *xerrok,
+    /* used if XPCLOSE or XCCLOSE */
+    int close_fd)
+{
+	/* for pipelines */
+	static Proc *last_proc;
+
+	int rv = 0, forksleep, jwflags = JW_NONE;
+#ifndef MKSH_NOPROSPECTOFWORK
+	sigset_t omask;
+#endif
+	Proc *p;
+	Job *j;
+	pid_t cldpid;
+
+	if (flags & XPIPEST) {
+		flags &= ~XPIPEST;
+		jwflags |= JW_PIPEST;
+	}
+
+	if (flags & XEXEC)
+		/*
+		 * Clear XFORK|XPCLOSE|XCCLOSE|XCOPROC|XPIPEO|XPIPEI|XXCOM|XBGND
+		 * (also done in another execute() below)
+		 */
+		return (execute(t, flags & (XEXEC | XERROK), xerrok));
+
+#ifndef MKSH_NOPROSPECTOFWORK
+	/* no SIGCHLDs while messing with job and process lists */
+	sigprocmask(SIG_BLOCK, &sm_sigchld, &omask);
+#endif
+
+	p = new_proc();
+	p->next = NULL;
+	p->state = PRUNNING;
+	p->status = 0;
+	p->pid = 0;
+
+	/* link process into jobs list */
+	if (flags & XPIPEI) {
+		/* continuing with a pipe */
+		if (!last_job)
+			internal_errorf("%s %d",
+			    "exchild: XPIPEI and no last_job - pid",
+			    (int)procpid);
+		j = last_job;
+		if (last_proc)
+			last_proc->next = p;
+		last_proc = p;
+	} else {
+		/* fills in j->job */
+		j = new_job();
+		/*
+		 * we don't consider XXCOMs foreground since they don't get
+		 * tty process group and we don't save or restore tty modes.
+		 */
+		j->flags = (flags & XXCOM) ? JF_XXCOM :
+		    ((flags & XBGND) ? 0 : (JF_FG|JF_USETTYMODE));
+		timerclear(&j->usrtime);
+		timerclear(&j->systime);
+		j->state = PRUNNING;
+		j->pgrp = 0;
+		j->ppid = procpid;
+		j->age = ++njobs;
+		j->proc_list = p;
+		j->coproc_id = 0;
+		last_job = j;
+		last_proc = p;
+		put_job(j, PJ_PAST_STOPPED);
+	}
+
+	vistree(p->command, sizeof(p->command), t);
+
+	/* create child process */
+	forksleep = 1;
+	while ((cldpid = fork()) < 0 && errno == EAGAIN && forksleep < 32) {
+		if (intrsig)
+			/* allow user to ^C out... */
+			break;
+		sleep(forksleep);
+		forksleep <<= 1;
+	}
+	/* ensure $RANDOM changes between parent and child */
+	rndset((unsigned long)cldpid);
+	/* fork failed? */
+	if (cldpid < 0) {
+		kill_job(j, SIGKILL);
+		remove_job(j, "fork failed");
+#ifndef MKSH_NOPROSPECTOFWORK
+		sigprocmask(SIG_SETMASK, &omask, NULL);
+#endif
+		errorf("can't fork - try again");
+	}
+	p->pid = cldpid ? cldpid : (procpid = getpid());
+
+#ifndef MKSH_UNEMPLOYED
+	/* job control set up */
+	if (Flag(FMONITOR) && !(flags&XXCOM)) {
+		bool dotty = false;
+
+		if (j->pgrp == 0) {
+			/* First process */
+			j->pgrp = p->pid;
+			dotty = true;
+		}
+
+		/*
+		 * set pgrp in both parent and child to deal with race
+		 * condition
+		 */
+		setpgid(p->pid, j->pgrp);
+		if (ttypgrp_ok && dotty && !(flags & XBGND))
+			tcsetpgrp(tty_fd, j->pgrp);
+	}
+#endif
+
+	/* used to close pipe input fd */
+	if (close_fd >= 0 && (((flags & XPCLOSE) && cldpid) ||
+	    ((flags & XCCLOSE) && !cldpid)))
+		close(close_fd);
+	if (!cldpid) {
+		/* child */
+
+		/* Do this before restoring signal */
+		if (flags & XCOPROC)
+			coproc_cleanup(false);
+		cleanup_parents_env();
+#ifndef MKSH_UNEMPLOYED
+		/*
+		 * If FMONITOR or FTALKING is set, these signals are ignored,
+		 * if neither FMONITOR nor FTALKING are set, the signals have
+		 * their inherited values.
+		 */
+		if (Flag(FMONITOR) && !(flags & XXCOM)) {
+			for (forksleep = NELEM(tt_sigs); --forksleep >= 0; )
+				setsig(&sigtraps[tt_sigs[forksleep]], SIG_DFL,
+				    SS_RESTORE_DFL|SS_FORCE);
+		}
+#endif
+#if HAVE_NICE
+		if (Flag(FBGNICE) && (flags & XBGND))
+			ksh_nice(4);
+#endif
+		if ((flags & XBGND)
+#ifndef MKSH_UNEMPLOYED
+		    && !Flag(FMONITOR)
+#endif
+		    ) {
+			setsig(&sigtraps[SIGINT], SIG_IGN,
+			    SS_RESTORE_IGN|SS_FORCE);
+			setsig(&sigtraps[SIGQUIT], SIG_IGN,
+			    SS_RESTORE_IGN|SS_FORCE);
+			if ((!(flags & (XPIPEI | XCOPROC))) &&
+			    ((forksleep = open("/dev/null", 0)) > 0)) {
+				(void)ksh_dup2(forksleep, 0, true);
+				close(forksleep);
+			}
+		}
+		/* in case of $(jobs) command */
+		remove_job(j, "child");
+#ifndef MKSH_NOPROSPECTOFWORK
+		/* remove_job needs SIGCHLD blocked still */
+		sigprocmask(SIG_SETMASK, &omask, NULL);
+#endif
+		nzombie = 0;
+#ifndef MKSH_UNEMPLOYED
+		ttypgrp_ok = false;
+		Flag(FMONITOR) = 0;
+#endif
+		Flag(FTALKING) = 0;
+		cleartraps();
+		/* no return */
+		execute(t, (flags & XERROK) | XEXEC, NULL);
+#ifndef MKSH_SMALL
+		if (t->type == TPIPE)
+			unwind(LLEAVE);
+		internal_warningf("%s: %s", "exchild", "execute() returned");
+		fptreef(shl_out, 8, "%s: tried to execute {\n\t%T\n}\n",
+		    "exchild", t);
+		shf_flush(shl_out);
+#endif
+		unwind(LLEAVE);
+		/* NOTREACHED */
+	}
+
+	/* shell (parent) stuff */
+	if (!(flags & XPIPEO)) {
+		/* last process in a job */
+		j_startjob(j);
+		if (flags & XCOPROC) {
+			j->coproc_id = coproc.id;
+			/* n jobs using co-process output */
+			coproc.njobs++;
+			/* j using co-process input */
+			coproc.job = (void *)j;
+		}
+		if (flags & XBGND) {
+			j_set_async(j);
+			if (Flag(FTALKING)) {
+				shf_fprintf(shl_out, "[%d]", j->job);
+				for (p = j->proc_list; p; p = p->next)
+					shf_fprintf(shl_out, " %d",
+					    (int)p->pid);
+				shf_putchar('\n', shl_out);
+				shf_flush(shl_out);
+			}
+		} else
+			rv = j_waitj(j, jwflags, "jw:last proc");
+	}
+
+#ifndef MKSH_NOPROSPECTOFWORK
+	sigprocmask(SIG_SETMASK, &omask, NULL);
+#endif
+
+	return (rv);
+}
+
+/* start the last job: only used for $(command) jobs */
+void
+startlast(void)
+{
+#ifndef MKSH_NOPROSPECTOFWORK
+	sigset_t omask;
+
+	sigprocmask(SIG_BLOCK, &sm_sigchld, &omask);
+#endif
+
+	/* no need to report error - waitlast() will do it */
+	if (last_job) {
+		/* ensure it isn't removed by check_job() */
+		last_job->flags |= JF_WAITING;
+		j_startjob(last_job);
+	}
+#ifndef MKSH_NOPROSPECTOFWORK
+	sigprocmask(SIG_SETMASK, &omask, NULL);
+#endif
+}
+
+/* wait for last job: only used for $(command) jobs */
+int
+waitlast(void)
+{
+	int rv;
+	Job *j;
+#ifndef MKSH_NOPROSPECTOFWORK
+	sigset_t omask;
+
+	sigprocmask(SIG_BLOCK, &sm_sigchld, &omask);
+#endif
+
+	j = last_job;
+	if (!j || !(j->flags & JF_STARTED)) {
+		if (!j)
+			warningf(true, "%s: %s", "waitlast", "no last job");
+		else
+			internal_warningf("%s: %s", "waitlast", "not started");
+#ifndef MKSH_NOPROSPECTOFWORK
+		sigprocmask(SIG_SETMASK, &omask, NULL);
+#endif
+		/* not so arbitrary, non-zero value */
+		return (125);
+	}
+
+	rv = j_waitj(j, JW_NONE, "waitlast");
+
+#ifndef MKSH_NOPROSPECTOFWORK
+	sigprocmask(SIG_SETMASK, &omask, NULL);
+#endif
+
+	return (rv);
+}
+
+/* wait for child, interruptable. */
+int
+waitfor(const char *cp, int *sigp)
+{
+	int rv, ecode, flags = JW_INTERRUPT|JW_ASYNCNOTIFY;
+	Job *j;
+#ifndef MKSH_NOPROSPECTOFWORK
+	sigset_t omask;
+
+	sigprocmask(SIG_BLOCK, &sm_sigchld, &omask);
+#endif
+
+	*sigp = 0;
+
+	if (cp == NULL) {
+		/*
+		 * wait for an unspecified job - always returns 0, so
+		 * don't have to worry about exited/signaled jobs
+		 */
+		for (j = job_list; j; j = j->next)
+			/* AT&T ksh will wait for stopped jobs - we don't */
+			if (j->ppid == procpid && j->state == PRUNNING)
+				break;
+		if (!j) {
+#ifndef MKSH_NOPROSPECTOFWORK
+			sigprocmask(SIG_SETMASK, &omask, NULL);
+#endif
+			return (-1);
+		}
+	} else if ((j = j_lookup(cp, &ecode))) {
+		/* don't report normal job completion */
+		flags &= ~JW_ASYNCNOTIFY;
+		if (j->ppid != procpid) {
+#ifndef MKSH_NOPROSPECTOFWORK
+			sigprocmask(SIG_SETMASK, &omask, NULL);
+#endif
+			return (-1);
+		}
+	} else {
+#ifndef MKSH_NOPROSPECTOFWORK
+		sigprocmask(SIG_SETMASK, &omask, NULL);
+#endif
+		if (ecode != JL_NOSUCH)
+			bi_errorf("%s: %s", cp, lookup_msgs[ecode]);
+		return (-1);
+	}
+
+	/* AT&T ksh will wait for stopped jobs - we don't */
+	rv = j_waitj(j, flags, "jw:waitfor");
+
+#ifndef MKSH_NOPROSPECTOFWORK
+	sigprocmask(SIG_SETMASK, &omask, NULL);
+#endif
+
+	if (rv < 0)
+		/* we were interrupted */
+		*sigp = 128 + -rv;
+
+	return (rv);
+}
+
+/* kill (built-in) a job */
+int
+j_kill(const char *cp, int sig)
+{
+	Job *j;
+	int rv = 0, ecode;
+#ifndef MKSH_NOPROSPECTOFWORK
+	sigset_t omask;
+
+	sigprocmask(SIG_BLOCK, &sm_sigchld, &omask);
+#endif
+
+	if ((j = j_lookup(cp, &ecode)) == NULL) {
+#ifndef MKSH_NOPROSPECTOFWORK
+		sigprocmask(SIG_SETMASK, &omask, NULL);
+#endif
+		bi_errorf("%s: %s", cp, lookup_msgs[ecode]);
+		return (1);
+	}
+
+	if (j->pgrp == 0) {
+		/* started when !Flag(FMONITOR) */
+		if (kill_job(j, sig) < 0) {
+			bi_errorf("%s: %s", cp, cstrerror(errno));
+			rv = 1;
+		}
+	} else {
+#ifndef MKSH_UNEMPLOYED
+		if (j->state == PSTOPPED && (sig == SIGTERM || sig == SIGHUP))
+			mksh_killpg(j->pgrp, SIGCONT);
+#endif
+		if (mksh_killpg(j->pgrp, sig) < 0) {
+			bi_errorf("%s: %s", cp, cstrerror(errno));
+			rv = 1;
+		}
+	}
+
+#ifndef MKSH_NOPROSPECTOFWORK
+	sigprocmask(SIG_SETMASK, &omask, NULL);
+#endif
+
+	return (rv);
+}
+
+#ifndef MKSH_UNEMPLOYED
+/* fg and bg built-ins: called only if Flag(FMONITOR) set */
+int
+j_resume(const char *cp, int bg)
+{
+	Job *j;
+	Proc *p;
+	int ecode, rv = 0;
+	bool running;
+	sigset_t omask;
+
+	sigprocmask(SIG_BLOCK, &sm_sigchld, &omask);
+
+	if ((j = j_lookup(cp, &ecode)) == NULL) {
+		sigprocmask(SIG_SETMASK, &omask, NULL);
+		bi_errorf("%s: %s", cp, lookup_msgs[ecode]);
+		return (1);
+	}
+
+	if (j->pgrp == 0) {
+		sigprocmask(SIG_SETMASK, &omask, NULL);
+		bi_errorf("job not job-controlled");
+		return (1);
+	}
+
+	if (bg)
+		shprintf("[%d] ", j->job);
+
+	running = false;
+	for (p = j->proc_list; p != NULL; p = p->next) {
+		if (p->state == PSTOPPED) {
+			p->state = PRUNNING;
+			p->status = 0;
+			running = true;
+		}
+		shf_puts(p->command, shl_stdout);
+		if (p->next)
+			shf_puts("| ", shl_stdout);
+	}
+	shf_putc('\n', shl_stdout);
+	shf_flush(shl_stdout);
+	if (running)
+		j->state = PRUNNING;
+
+	put_job(j, PJ_PAST_STOPPED);
+	if (bg)
+		j_set_async(j);
+	else {
+		/* attach tty to job */
+		if (j->state == PRUNNING) {
+			if (ttypgrp_ok && (j->flags & JF_SAVEDTTY))
+				mksh_tcset(tty_fd, &j->ttystat);
+			/* See comment in j_waitj regarding saved_ttypgrp. */
+			if (ttypgrp_ok &&
+			    tcsetpgrp(tty_fd, (j->flags & JF_SAVEDTTYPGRP) ?
+			    j->saved_ttypgrp : j->pgrp) < 0) {
+				rv = errno;
+				if (j->flags & JF_SAVEDTTY)
+					mksh_tcset(tty_fd, &tty_state);
+				sigprocmask(SIG_SETMASK, &omask, NULL);
+				bi_errorf("%s %s(%d, %ld) %s: %s",
+				    "1st", "tcsetpgrp", tty_fd,
+				    (long)((j->flags & JF_SAVEDTTYPGRP) ?
+				    j->saved_ttypgrp : j->pgrp), "failed",
+				    cstrerror(rv));
+				return (1);
+			}
+		}
+		j->flags |= JF_FG;
+		j->flags &= ~JF_KNOWN;
+		if (j == async_job)
+			async_job = NULL;
+	}
+
+	if (j->state == PRUNNING && mksh_killpg(j->pgrp, SIGCONT) < 0) {
+		int eno = errno;
+
+		if (!bg) {
+			j->flags &= ~JF_FG;
+			if (ttypgrp_ok && (j->flags & JF_SAVEDTTY))
+				mksh_tcset(tty_fd, &tty_state);
+			if (ttypgrp_ok && tcsetpgrp(tty_fd, kshpgrp) < 0)
+				warningf(true, "%s %s(%d, %ld) %s: %s",
+				    "fg: 2nd", "tcsetpgrp", tty_fd,
+				    (long)kshpgrp, "failed", cstrerror(errno));
+		}
+		sigprocmask(SIG_SETMASK, &omask, NULL);
+		bi_errorf("%s %s %s", "can't continue job",
+		    cp, cstrerror(eno));
+		return (1);
+	}
+	if (!bg) {
+		if (ttypgrp_ok) {
+			j->flags &= ~(JF_SAVEDTTY | JF_SAVEDTTYPGRP);
+		}
+		rv = j_waitj(j, JW_NONE, "jw:resume");
+	}
+	sigprocmask(SIG_SETMASK, &omask, NULL);
+	return (rv);
+}
+#endif
+
+/* are there any running or stopped jobs ? */
+int
+j_stopped_running(void)
+{
+	Job *j;
+	int which = 0;
+
+	for (j = job_list; j != NULL; j = j->next) {
+#ifndef MKSH_UNEMPLOYED
+		if (j->ppid == procpid && j->state == PSTOPPED)
+			which |= 1;
+#endif
+		if (Flag(FLOGIN) && !Flag(FNOHUP) && procpid == kshpid &&
+		    j->ppid == procpid && j->state == PRUNNING)
+			which |= 2;
+	}
+	if (which) {
+		shellf("You have %s%s%s jobs\n",
+		    which & 1 ? "stopped" : "",
+		    which == 3 ? " and " : "",
+		    which & 2 ? "running" : "");
+		return (1);
+	}
+
+	return (0);
+}
+
+
+/* list jobs for jobs built-in */
+int
+j_jobs(const char *cp, int slp,
+    /* 0: short, 1: long, 2: pgrp */
+    int nflag)
+{
+	Job *j, *tmp;
+	int how, zflag = 0;
+#ifndef MKSH_NOPROSPECTOFWORK
+	sigset_t omask;
+
+	sigprocmask(SIG_BLOCK, &sm_sigchld, &omask);
+#endif
+
+	if (nflag < 0) {
+		/* kludge: print zombies */
+		nflag = 0;
+		zflag = 1;
+	}
+	if (cp) {
+		int ecode;
+
+		if ((j = j_lookup(cp, &ecode)) == NULL) {
+#ifndef MKSH_NOPROSPECTOFWORK
+			sigprocmask(SIG_SETMASK, &omask, NULL);
+#endif
+			bi_errorf("%s: %s", cp, lookup_msgs[ecode]);
+			return (1);
+		}
+	} else
+		j = job_list;
+	how = slp == 0 ? JP_MEDIUM : (slp == 1 ? JP_LONG : JP_PGRP);
+	for (; j; j = j->next) {
+		if ((!(j->flags & JF_ZOMBIE) || zflag) &&
+		    (!nflag || (j->flags & JF_CHANGED))) {
+			j_print(j, how, shl_stdout);
+			if (j->state == PEXITED || j->state == PSIGNALLED)
+				j->flags |= JF_REMOVE;
+		}
+		if (cp)
+			break;
+	}
+	/* Remove jobs after printing so there won't be multiple + or - jobs */
+	for (j = job_list; j; j = tmp) {
+		tmp = j->next;
+		if (j->flags & JF_REMOVE)
+			remove_job(j, "jobs");
+	}
+#ifndef MKSH_NOPROSPECTOFWORK
+	sigprocmask(SIG_SETMASK, &omask, NULL);
+#endif
+	return (0);
+}
+
+/* list jobs for top-level notification */
+void
+j_notify(void)
+{
+	Job *j, *tmp;
+#ifndef MKSH_NOPROSPECTOFWORK
+	sigset_t omask;
+
+	sigprocmask(SIG_BLOCK, &sm_sigchld, &omask);
+#endif
+	for (j = job_list; j; j = j->next) {
+#ifndef MKSH_UNEMPLOYED
+		if (Flag(FMONITOR) && (j->flags & JF_CHANGED))
+			j_print(j, JP_MEDIUM, shl_out);
+#endif
+		/*
+		 * Remove job after doing reports so there aren't
+		 * multiple +/- jobs.
+		 */
+		if (j->state == PEXITED || j->state == PSIGNALLED)
+			j->flags |= JF_REMOVE;
+	}
+	for (j = job_list; j; j = tmp) {
+		tmp = j->next;
+		if (j->flags & JF_REMOVE)
+			remove_job(j, "notify");
+	}
+	shf_flush(shl_out);
+#ifndef MKSH_NOPROSPECTOFWORK
+	sigprocmask(SIG_SETMASK, &omask, NULL);
+#endif
+}
+
+/* Return pid of last process in last asynchronous job */
+pid_t
+j_async(void)
+{
+#ifndef MKSH_NOPROSPECTOFWORK
+	sigset_t omask;
+
+	sigprocmask(SIG_BLOCK, &sm_sigchld, &omask);
+#endif
+
+	if (async_job)
+		async_job->flags |= JF_KNOWN;
+
+#ifndef MKSH_NOPROSPECTOFWORK
+	sigprocmask(SIG_SETMASK, &omask, NULL);
+#endif
+
+	return (async_pid);
+}
+
+/*
+ * Make j the last async process
+ *
+ * If jobs are compiled in then this routine expects sigchld to be blocked.
+ */
+static void
+j_set_async(Job *j)
+{
+	Job	*jl, *oldest;
+
+	if (async_job && (async_job->flags & (JF_KNOWN|JF_ZOMBIE)) == JF_ZOMBIE)
+		remove_job(async_job, "async");
+	if (!(j->flags & JF_STARTED)) {
+		internal_warningf("%s: %s", "j_async", "job not started");
+		return;
+	}
+	async_job = j;
+	async_pid = j->last_proc->pid;
+	while (nzombie > CHILD_MAX) {
+		oldest = NULL;
+		for (jl = job_list; jl; jl = jl->next)
+			if (jl != async_job && (jl->flags & JF_ZOMBIE) &&
+			    (!oldest || jl->age < oldest->age))
+				oldest = jl;
+		if (!oldest) {
+			/* XXX debugging */
+			if (!(async_job->flags & JF_ZOMBIE) || nzombie != 1) {
+				internal_warningf("%s: bad nzombie (%d)",
+				    "j_async", nzombie);
+				nzombie = 0;
+			}
+			break;
+		}
+		remove_job(oldest, "zombie");
+	}
+}
+
+/*
+ * Start a job: set STARTED, check for held signals and set j->last_proc
+ *
+ * If jobs are compiled in then this routine expects sigchld to be blocked.
+ */
+static void
+j_startjob(Job *j)
+{
+	Proc	*p;
+
+	j->flags |= JF_STARTED;
+	for (p = j->proc_list; p->next; p = p->next)
+		;
+	j->last_proc = p;
+
+#ifndef MKSH_NOPROSPECTOFWORK
+	if (held_sigchld) {
+		held_sigchld = 0;
+		/* Don't call j_sigchld() as it may remove job... */
+		kill(procpid, SIGCHLD);
+	}
+#endif
+}
+
+/*
+ * wait for job to complete or change state
+ *
+ * If jobs are compiled in then this routine expects sigchld to be blocked.
+ */
+static int
+j_waitj(Job *j,
+    /* see JW_* */
+    int flags,
+    const char *where)
+{
+	int rv;
+#ifdef MKSH_NO_SIGSUSPEND
+	sigset_t omask;
+#endif
+
+	/*
+	 * No auto-notify on the job we are waiting on.
+	 */
+	j->flags |= JF_WAITING;
+	if (flags & JW_ASYNCNOTIFY)
+		j->flags |= JF_W_ASYNCNOTIFY;
+
+#ifndef MKSH_UNEMPLOYED
+	if (!Flag(FMONITOR))
+#endif
+		flags |= JW_STOPPEDWAIT;
+
+	while (j->state == PRUNNING ||
+	    ((flags & JW_STOPPEDWAIT) && j->state == PSTOPPED)) {
+#ifndef MKSH_NOPROSPECTOFWORK
+#ifdef MKSH_NO_SIGSUSPEND
+		sigprocmask(SIG_SETMASK, &sm_default, &omask);
+		pause();
+		/* note that handlers may run here so they need to know */
+		sigprocmask(SIG_SETMASK, &omask, NULL);
+#else
+		sigsuspend(&sm_default);
+#endif
+#else
+		j_sigchld(SIGCHLD);
+#endif
+		if (fatal_trap) {
+			int oldf = j->flags & (JF_WAITING|JF_W_ASYNCNOTIFY);
+			j->flags &= ~(JF_WAITING|JF_W_ASYNCNOTIFY);
+			runtraps(TF_FATAL);
+			/* not reached... */
+			j->flags |= oldf;
+		}
+		if ((flags & JW_INTERRUPT) && (rv = trap_pending())) {
+			j->flags &= ~(JF_WAITING|JF_W_ASYNCNOTIFY);
+			return (-rv);
+		}
+	}
+	j->flags &= ~(JF_WAITING|JF_W_ASYNCNOTIFY);
+
+	if (j->flags & JF_FG) {
+		j->flags &= ~JF_FG;
+#ifndef MKSH_UNEMPLOYED
+		if (Flag(FMONITOR) && ttypgrp_ok && j->pgrp) {
+			/*
+			 * Save the tty's current pgrp so it can be restored
+			 * when the job is foregrounded. This is to
+			 * deal with things like the GNU su which does
+			 * a fork/exec instead of an exec (the fork means
+			 * the execed shell gets a different pid from its
+			 * pgrp, so naturally it sets its pgrp and gets hosed
+			 * when it gets foregrounded by the parent shell which
+			 * has restored the tty's pgrp to that of the su
+			 * process).
+			 */
+			if (j->state == PSTOPPED &&
+			    (j->saved_ttypgrp = tcgetpgrp(tty_fd)) >= 0)
+				j->flags |= JF_SAVEDTTYPGRP;
+			if (tcsetpgrp(tty_fd, kshpgrp) < 0)
+				warningf(true, "%s %s(%d, %ld) %s: %s",
+				    "j_waitj:", "tcsetpgrp", tty_fd,
+				    (long)kshpgrp, "failed", cstrerror(errno));
+			if (j->state == PSTOPPED) {
+				j->flags |= JF_SAVEDTTY;
+				mksh_tcget(tty_fd, &j->ttystat);
+			}
+		}
+#endif
+		if (tty_hasstate) {
+			/*
+			 * Only restore tty settings if job was originally
+			 * started in the foreground. Problems can be
+			 * caused by things like 'more foobar &' which will
+			 * typically get and save the shell's vi/emacs tty
+			 * settings before setting up the tty for itself;
+			 * when more exits, it restores the 'original'
+			 * settings, and things go down hill from there...
+			 */
+			if (j->state == PEXITED && j->status == 0 &&
+			    (j->flags & JF_USETTYMODE)) {
+				mksh_tcget(tty_fd, &tty_state);
+			} else {
+				mksh_tcset(tty_fd, &tty_state);
+				/*-
+				 * Don't use tty mode if job is stopped and
+				 * later restarted and exits. Consider
+				 * the sequence:
+				 *	vi foo (stopped)
+				 *	...
+				 *	stty something
+				 *	...
+				 *	fg (vi; ZZ)
+				 * mode should be that of the stty, not what
+				 * was before the vi started.
+				 */
+				if (j->state == PSTOPPED)
+					j->flags &= ~JF_USETTYMODE;
+			}
+		}
+#ifndef MKSH_UNEMPLOYED
+		/*
+		 * If it looks like user hit ^C to kill a job, pretend we got
+		 * one too to break out of for loops, etc. (AT&T ksh does this
+		 * even when not monitoring, but this doesn't make sense since
+		 * a tty generated ^C goes to the whole process group)
+		 */
+		{
+			int status;
+
+			status = j->last_proc->status;
+			if (Flag(FMONITOR) && j->state == PSIGNALLED &&
+			    WIFSIGNALED(status) &&
+			    (sigtraps[WTERMSIG(status)].flags & TF_TTY_INTR))
+				trapsig(WTERMSIG(status));
+		}
+#endif
+	}
+
+	j_usrtime = j->usrtime;
+	j_systime = j->systime;
+	rv = j->status;
+
+	if ((flags & JW_PIPEST) && (j->proc_list != NULL)) {
+		uint32_t num = 0;
+		Proc *p = j->proc_list;
+		struct tbl *vp;
+
+		unset(vp_pipest, 1);
+		vp = vp_pipest;
+		vp->flag = DEFINED | ISSET | INTEGER | RDONLY | ARRAY | INT_U;
+		goto got_array;
+
+		while (p != NULL) {
+			{
+				struct tbl *vq;
+
+				/* strlen(vp_pipest->name) == 10 */
+				vq = alloc(offsetof(struct tbl, name[0]) + 11,
+				    vp_pipest->areap);
+				memset(vq, 0, offsetof(struct tbl, name[0]));
+				memcpy(vq->name, vp_pipest->name, 11);
+				vp->u.array = vq;
+				vp = vq;
+			}
+			vp->areap = vp_pipest->areap;
+			vp->ua.index = ++num;
+			vp->flag = DEFINED | ISSET | INTEGER | RDONLY |
+			    ARRAY | INT_U | AINDEX;
+ got_array:
+			vp->val.i = proc_errorlevel(p);
+			if (Flag(FPIPEFAIL) && vp->val.i)
+				rv = vp->val.i;
+			p = p->next;
+		}
+	} else if (Flag(FPIPEFAIL) && (j->proc_list != NULL)) {
+		Proc *p = j->proc_list;
+		int i;
+
+		while (p != NULL) {
+			if ((i = proc_errorlevel(p)))
+				rv = i;
+			p = p->next;
+		}
+	}
+
+	if (!(flags & JW_ASYNCNOTIFY)
+#ifndef MKSH_UNEMPLOYED
+	    && (!Flag(FMONITOR) || j->state != PSTOPPED)
+#endif
+	    ) {
+		j_print(j, JP_SHORT, shl_out);
+		shf_flush(shl_out);
+	}
+	if (j->state != PSTOPPED
+#ifndef MKSH_UNEMPLOYED
+	    && (!Flag(FMONITOR) || !(flags & JW_ASYNCNOTIFY))
+#endif
+	    )
+		remove_job(j, where);
+
+	return (rv);
+}
+
+/*
+ * SIGCHLD handler to reap children and update job states
+ *
+ * If jobs are compiled in then this routine expects sigchld to be blocked.
+ */
+/* ARGSUSED */
+static void
+j_sigchld(int sig MKSH_A_UNUSED)
+{
+	int saved_errno = errno;
+	Job *j;
+	Proc *p = NULL;
+	pid_t pid;
+	int status;
+	struct rusage ru0, ru1;
+#ifdef MKSH_NO_SIGSUSPEND
+	sigset_t omask;
+
+	/* this handler can run while SIGCHLD is not blocked, so block it now */
+	sigprocmask(SIG_BLOCK, &sm_sigchld, &omask);
+#endif
+
+#ifndef MKSH_NOPROSPECTOFWORK
+	/*
+	 * Don't wait for any processes if a job is partially started.
+	 * This is so we don't do away with the process group leader
+	 * before all the processes in a pipe line are started (so the
+	 * setpgid() won't fail)
+	 */
+	for (j = job_list; j; j = j->next)
+		if (j->ppid == procpid && !(j->flags & JF_STARTED)) {
+			held_sigchld = 1;
+			goto j_sigchld_out;
+		}
+#endif
+
+	getrusage(RUSAGE_CHILDREN, &ru0);
+	do {
+#ifndef MKSH_NOPROSPECTOFWORK
+		pid = waitpid(-1, &status, (WNOHANG |
+#ifdef WCONTINUED
+		    WCONTINUED |
+#endif
+		    WUNTRACED));
+#else
+		pid = wait(&status);
+#endif
+
+		/*
+		 * return if this would block (0) or no children
+		 * or interrupted (-1)
+		 */
+		if (pid <= 0)
+			goto j_sigchld_out;
+
+		getrusage(RUSAGE_CHILDREN, &ru1);
+
+		/* find job and process structures for this pid */
+		for (j = job_list; j != NULL; j = j->next)
+			for (p = j->proc_list; p != NULL; p = p->next)
+				if (p->pid == pid)
+					goto found;
+ found:
+		if (j == NULL) {
+			/* Can occur if process has kids, then execs shell
+			warningf(true, "bad process waited for (pid = %d)",
+				pid);
+			 */
+			ru0 = ru1;
+			continue;
+		}
+
+		timeradd(&j->usrtime, &ru1.ru_utime, &j->usrtime);
+		timersub(&j->usrtime, &ru0.ru_utime, &j->usrtime);
+		timeradd(&j->systime, &ru1.ru_stime, &j->systime);
+		timersub(&j->systime, &ru0.ru_stime, &j->systime);
+		ru0 = ru1;
+		p->status = status;
+#ifndef MKSH_UNEMPLOYED
+		if (WIFSTOPPED(status))
+			p->state = PSTOPPED;
+		else
+#ifdef WIFCONTINUED
+		  if (WIFCONTINUED(status)) {
+			p->state = j->state = PRUNNING;
+			/* skip check_job(), no-op in this case */
+			continue;
+		} else
+#endif
+#endif
+		  if (WIFSIGNALED(status))
+			p->state = PSIGNALLED;
+		else
+			p->state = PEXITED;
+
+		/* check to see if entire job is done */
+		check_job(j);
+	}
+#ifndef MKSH_NOPROSPECTOFWORK
+	    while (/* CONSTCOND */ 1);
+#else
+	    while (/* CONSTCOND */ 0);
+#endif
+
+ j_sigchld_out:
+#ifdef MKSH_NO_SIGSUSPEND
+	sigprocmask(SIG_SETMASK, &omask, NULL);
+#endif
+	errno = saved_errno;
+}
+
+/*
+ * Called only when a process in j has exited/stopped (ie, called only
+ * from j_sigchld()). If no processes are running, the job status
+ * and state are updated, asynchronous job notification is done and,
+ * if unneeded, the job is removed.
+ *
+ * If jobs are compiled in then this routine expects sigchld to be blocked.
+ */
+static void
+check_job(Job *j)
+{
+	int	jstate;
+	Proc	*p;
+
+	/* XXX debugging (nasty - interrupt routine using shl_out) */
+	if (!(j->flags & JF_STARTED)) {
+		internal_warningf("check_job: job started (flags 0x%x)",
+		    j->flags);
+		return;
+	}
+
+	jstate = PRUNNING;
+	for (p=j->proc_list; p != NULL; p = p->next) {
+		if (p->state == PRUNNING)
+			/* some processes still running */
+			return;
+		if (p->state > jstate)
+			jstate = p->state;
+	}
+	j->state = jstate;
+	j->status = proc_errorlevel(j->last_proc);
+
+	/*
+	 * Note when co-process dies: can't be done in j_wait() nor
+	 * remove_job() since neither may be called for non-interactive
+	 * shells.
+	 */
+	if (j->state == PEXITED || j->state == PSIGNALLED) {
+		/*
+		 * No need to keep co-process input any more
+		 * (at least, this is what ksh93d thinks)
+		 */
+		if (coproc.job == j) {
+			coproc.job = NULL;
+			/*
+			 * XXX would be nice to get the closes out of here
+			 * so they aren't done in the signal handler.
+			 * Would mean a check in coproc_getfd() to
+			 * do "if job == 0 && write >= 0, close write".
+			 */
+			coproc_write_close(coproc.write);
+		}
+		/* Do we need to keep the output? */
+		if (j->coproc_id && j->coproc_id == coproc.id &&
+		    --coproc.njobs == 0)
+			coproc_readw_close(coproc.read);
+	}
+
+	j->flags |= JF_CHANGED;
+#ifndef MKSH_UNEMPLOYED
+	if (Flag(FMONITOR) && !(j->flags & JF_XXCOM)) {
+		/*
+		 * Only put stopped jobs at the front to avoid confusing
+		 * the user (don't want finished jobs effecting %+ or %-)
+		 */
+		if (j->state == PSTOPPED)
+			put_job(j, PJ_ON_FRONT);
+		if (Flag(FNOTIFY) &&
+		    (j->flags & (JF_WAITING|JF_W_ASYNCNOTIFY)) != JF_WAITING) {
+			/* Look for the real file descriptor 2 */
+			{
+				struct env *ep;
+				int fd = 2;
+
+				for (ep = e; ep; ep = ep->oenv)
+					if (ep->savefd && ep->savefd[2])
+						fd = ep->savefd[2];
+				shf_reopen(fd, SHF_WR, shl_j);
+			}
+			/*
+			 * Can't call j_notify() as it removes jobs. The job
+			 * must stay in the job list as j_waitj() may be
+			 * running with this job.
+			 */
+			j_print(j, JP_MEDIUM, shl_j);
+			shf_flush(shl_j);
+			if (!(j->flags & JF_WAITING) && j->state != PSTOPPED)
+				remove_job(j, "notify");
+		}
+	}
+#endif
+	if (
+#ifndef MKSH_UNEMPLOYED
+	    !Flag(FMONITOR) &&
+#endif
+	    !(j->flags & (JF_WAITING|JF_FG)) &&
+	    j->state != PSTOPPED) {
+		if (j == async_job || (j->flags & JF_KNOWN)) {
+			j->flags |= JF_ZOMBIE;
+			j->job = -1;
+			nzombie++;
+		} else
+			remove_job(j, "checkjob");
+	}
+}
+
+/*
+ * Print job status in either short, medium or long format.
+ *
+ * If jobs are compiled in then this routine expects sigchld to be blocked.
+ */
+static void
+j_print(Job *j, int how, struct shf *shf)
+{
+	Proc	*p;
+	int	state;
+	int	status;
+	int	coredumped;
+	char	jobchar = ' ';
+	char	buf[64];
+	const char *filler;
+	int	output = 0;
+
+	if (how == JP_PGRP) {
+		/*
+		 * POSIX doesn't say what to do it there is no process
+		 * group leader (ie, !FMONITOR). We arbitrarily return
+		 * last pid (which is what $! returns).
+		 */
+		shf_fprintf(shf, "%d\n", (int)(j->pgrp ? j->pgrp :
+		    (j->last_proc ? j->last_proc->pid : 0)));
+		return;
+	}
+	j->flags &= ~JF_CHANGED;
+	filler = j->job > 10 ? "\n       " : "\n      ";
+	if (j == job_list)
+		jobchar = '+';
+	else if (j == job_list->next)
+		jobchar = '-';
+
+	for (p = j->proc_list; p != NULL;) {
+		coredumped = 0;
+		switch (p->state) {
+		case PRUNNING:
+			memcpy(buf, "Running", 8);
+			break;
+		case PSTOPPED:
+			strlcpy(buf, sigtraps[WSTOPSIG(p->status)].mess,
+			    sizeof(buf));
+			break;
+		case PEXITED:
+			if (how == JP_SHORT)
+				buf[0] = '\0';
+			else if (WEXITSTATUS(p->status) == 0)
+				memcpy(buf, "Done", 5);
+			else
+				shf_snprintf(buf, sizeof(buf), "Done (%d)",
+				    WEXITSTATUS(p->status));
+			break;
+		case PSIGNALLED:
+#ifdef WCOREDUMP
+			if (WCOREDUMP(p->status))
+				coredumped = 1;
+#endif
+			/*
+			 * kludge for not reporting 'normal termination
+			 * signals' (i.e. SIGINT, SIGPIPE)
+			 */
+			if (how == JP_SHORT && !coredumped &&
+			    (WTERMSIG(p->status) == SIGINT ||
+			    WTERMSIG(p->status) == SIGPIPE)) {
+				buf[0] = '\0';
+			} else
+				strlcpy(buf, sigtraps[WTERMSIG(p->status)].mess,
+				    sizeof(buf));
+			break;
+		default:
+			buf[0] = '\0';
+		}
+
+		if (how != JP_SHORT) {
+			if (p == j->proc_list)
+				shf_fprintf(shf, "[%d] %c ", j->job, jobchar);
+			else
+				shf_puts(filler, shf);
+		}
+
+		if (how == JP_LONG)
+			shf_fprintf(shf, "%5d ", (int)p->pid);
+
+		if (how == JP_SHORT) {
+			if (buf[0]) {
+				output = 1;
+				shf_fprintf(shf, "%s%s ",
+				    buf, coredumped ? " (core dumped)" : null);
+			}
+		} else {
+			output = 1;
+			shf_fprintf(shf, "%-20s %s%s%s", buf, p->command,
+			    p->next ? "|" : null,
+			    coredumped ? " (core dumped)" : null);
+		}
+
+		state = p->state;
+		status = p->status;
+		p = p->next;
+		while (p && p->state == state && p->status == status) {
+			if (how == JP_LONG)
+				shf_fprintf(shf, "%s%5d %-20s %s%s", filler,
+				    (int)p->pid, " ", p->command,
+				    p->next ? "|" : null);
+			else if (how == JP_MEDIUM)
+				shf_fprintf(shf, " %s%s", p->command,
+				    p->next ? "|" : null);
+			p = p->next;
+		}
+	}
+	if (output)
+		shf_putc('\n', shf);
+}
+
+/*
+ * Convert % sequence to job
+ *
+ * If jobs are compiled in then this routine expects sigchld to be blocked.
+ */
+static Job *
+j_lookup(const char *cp, int *ecodep)
+{
+	Job *j, *last_match;
+	Proc *p;
+	size_t len;
+	int job = 0;
+
+	if (ksh_isdigit(*cp)) {
+		getn(cp, &job);
+		/* Look for last_proc->pid (what $! returns) first... */
+		for (j = job_list; j != NULL; j = j->next)
+			if (j->last_proc && j->last_proc->pid == job)
+				return (j);
+		/*
+		 * ...then look for process group (this is non-POSIX,
+		 * but should not break anything
+		 */
+		for (j = job_list; j != NULL; j = j->next)
+			if (j->pgrp && j->pgrp == job)
+				return (j);
+		if (ecodep)
+			*ecodep = JL_NOSUCH;
+		return (NULL);
+	}
+	if (*cp != '%') {
+		if (ecodep)
+			*ecodep = JL_INVALID;
+		return (NULL);
+	}
+	switch (*++cp) {
+	case '\0': /* non-standard */
+	case '+':
+	case '%':
+		if (job_list != NULL)
+			return (job_list);
+		break;
+
+	case '-':
+		if (job_list != NULL && job_list->next)
+			return (job_list->next);
+		break;
+
+	case '0': case '1': case '2': case '3': case '4':
+	case '5': case '6': case '7': case '8': case '9':
+		getn(cp, &job);
+		for (j = job_list; j != NULL; j = j->next)
+			if (j->job == job)
+				return (j);
+		break;
+
+	/* %?string */
+	case '?':
+		last_match = NULL;
+		for (j = job_list; j != NULL; j = j->next)
+			for (p = j->proc_list; p != NULL; p = p->next)
+				if (strstr(p->command, cp+1) != NULL) {
+					if (last_match) {
+						if (ecodep)
+							*ecodep = JL_AMBIG;
+						return (NULL);
+					}
+					last_match = j;
+				}
+		if (last_match)
+			return (last_match);
+		break;
+
+	/* %string */
+	default:
+		len = strlen(cp);
+		last_match = NULL;
+		for (j = job_list; j != NULL; j = j->next)
+			if (strncmp(cp, j->proc_list->command, len) == 0) {
+				if (last_match) {
+					if (ecodep)
+						*ecodep = JL_AMBIG;
+					return (NULL);
+				}
+				last_match = j;
+			}
+		if (last_match)
+			return (last_match);
+		break;
+	}
+	if (ecodep)
+		*ecodep = JL_NOSUCH;
+	return (NULL);
+}
+
+static Job	*free_jobs;
+static Proc	*free_procs;
+
+/*
+ * allocate a new job and fill in the job number.
+ *
+ * If jobs are compiled in then this routine expects sigchld to be blocked.
+ */
+static Job *
+new_job(void)
+{
+	int	i;
+	Job	*newj, *j;
+
+	if (free_jobs != NULL) {
+		newj = free_jobs;
+		free_jobs = free_jobs->next;
+	} else
+		newj = alloc(sizeof(Job), APERM);
+
+	/* brute force method */
+	for (i = 1; ; i++) {
+		for (j = job_list; j && j->job != i; j = j->next)
+			;
+		if (j == NULL)
+			break;
+	}
+	newj->job = i;
+
+	return (newj);
+}
+
+/*
+ * Allocate new process struct
+ *
+ * If jobs are compiled in then this routine expects sigchld to be blocked.
+ */
+static Proc *
+new_proc(void)
+{
+	Proc	*p;
+
+	if (free_procs != NULL) {
+		p = free_procs;
+		free_procs = free_procs->next;
+	} else
+		p = alloc(sizeof(Proc), APERM);
+
+	return (p);
+}
+
+/*
+ * Take job out of job_list and put old structures into free list.
+ * Keeps nzombies, last_job and async_job up to date.
+ *
+ * If jobs are compiled in then this routine expects sigchld to be blocked.
+ */
+static void
+remove_job(Job *j, const char *where)
+{
+	Proc	*p, *tmp;
+	Job	**prev, *curr;
+
+	mkssert(j != NULL);
+	prev = &job_list;
+	curr = job_list;
+	while (curr && curr != j) {
+		prev = &curr->next;
+		curr = *prev;
+	}
+	if (curr != j) {
+		internal_warningf("remove_job: job %s (%s)", "not found", where);
+		return;
+	}
+	*prev = curr->next;
+
+	/* free up proc structures */
+	for (p = j->proc_list; p != NULL; ) {
+		tmp = p;
+		p = p->next;
+		tmp->next = free_procs;
+		free_procs = tmp;
+	}
+
+	if ((j->flags & JF_ZOMBIE) && j->ppid == procpid)
+		--nzombie;
+	j->next = free_jobs;
+	free_jobs = j;
+
+	if (j == last_job)
+		last_job = NULL;
+	if (j == async_job)
+		async_job = NULL;
+}
+
+/*
+ * put j in a particular location (taking it out job_list if it is there
+ * already)
+ *
+ * If jobs are compiled in then this routine expects sigchld to be blocked.
+ */
+static void
+put_job(Job *j, int where)
+{
+	Job	**prev, *curr;
+
+	mkssert(j != NULL);
+	/* Remove job from list (if there) */
+	prev = &job_list;
+	curr = job_list;
+	while (curr && curr != j) {
+		prev = &curr->next;
+		curr = *prev;
+	}
+	if (curr == j)
+		*prev = curr->next;
+
+	switch (where) {
+	case PJ_ON_FRONT:
+		j->next = job_list;
+		job_list = j;
+		break;
+
+	case PJ_PAST_STOPPED:
+		prev = &job_list;
+		curr = job_list;
+		for (; curr && curr->state == PSTOPPED; prev = &curr->next,
+		    curr = *prev)
+			;
+		j->next = curr;
+		*prev = j;
+		break;
+	}
+}
+
+/*
+ * nuke a job (called when unable to start full job).
+ *
+ * If jobs are compiled in then this routine expects sigchld to be blocked.
+ */
+static int
+kill_job(Job *j, int sig)
+{
+	Proc	*p;
+	int	rval = 0;
+
+	for (p = j->proc_list; p != NULL; p = p->next)
+		if (p->pid != 0)
+			if (kill(p->pid, sig) < 0)
+				rval = -1;
+	return (rval);
+}
+
+static void
+tty_init_talking(void)
+{
+	switch (tty_init_fd()) {
+	case 0:
+		break;
+	case 1:
+#ifndef MKSH_DISABLE_TTY_WARNING
+		warningf(false, "%s: %s %s: %s",
+		    "No controlling tty", "open", "/dev/tty",
+		    cstrerror(errno));
+#endif
+		break;
+	case 2:
+#ifndef MKSH_DISABLE_TTY_WARNING
+		warningf(false, "%s: %s", "can't find tty fd", cstrerror(errno));
+#endif
+		break;
+	case 3:
+		warningf(false, "%s: %s %s: %s", "j_ttyinit",
+		    "dup of tty fd", "failed", cstrerror(errno));
+		break;
+	case 4:
+		warningf(false, "%s: %s: %s", "j_ttyinit",
+		    "can't set close-on-exec flag", cstrerror(errno));
+		break;
+	}
+}
+
+static void
+tty_init_state(void)
+{
+	if (tty_fd >= 0) {
+		mksh_tcget(tty_fd, &tty_state);
+		tty_hasstate = true;
+	}
+}

Deleted: vendor/MirOS/mksh/R50/lex.c
===================================================================
--- vendor/MirOS/mksh/dist/lex.c	2014-06-30 23:58:12 UTC (rev 6706)
+++ vendor/MirOS/mksh/R50/lex.c	2014-07-01 12:01:17 UTC (rev 6708)
@@ -1,1803 +0,0 @@
-/*	$OpenBSD: lex.c,v 1.47 2013/03/03 19:11:34 guenther Exp $	*/
-
-/*-
- * Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
- *		 2011, 2012, 2013
- *	Thorsten Glaser <tg at mirbsd.org>
- *
- * Provided that these terms and disclaimer and all copyright notices
- * are retained or reproduced in an accompanying document, permission
- * is granted to deal in this work without restriction, including un-
- * limited rights to use, publicly perform, distribute, sell, modify,
- * merge, give away, or sublicence.
- *
- * This work is provided "AS IS" and WITHOUT WARRANTY of any kind, to
- * the utmost extent permitted by applicable law, neither express nor
- * implied; without malicious intent or gross negligence. In no event
- * may a licensor, author or contributor be held liable for indirect,
- * direct, other damage, loss, or other issues arising in any way out
- * of dealing in the work, even if advised of the possibility of such
- * damage or existence of a defect, except proven that it results out
- * of said person's immediate fault when using the work as intended.
- */
-
-#include "sh.h"
-
-__RCSID("$MirOS: src/bin/mksh/lex.c,v 1.188 2013/08/10 13:44:31 tg Exp $");
-
-/*
- * states while lexing word
- */
-#define SBASE		0	/* outside any lexical constructs */
-#define SWORD		1	/* implicit quoting for substitute() */
-#define SLETPAREN	2	/* inside (( )), implicit quoting */
-#define SSQUOTE		3	/* inside '' */
-#define SDQUOTE		4	/* inside "" */
-#define SEQUOTE		5	/* inside $'' */
-#define SBRACE		6	/* inside ${} */
-#define SQBRACE		7	/* inside "${}" */
-#define SBQUOTE		8	/* inside `` */
-#define SASPAREN	9	/* inside $(( )) */
-#define SHEREDELIM	10	/* parsing <<,<<-,<<< delimiter */
-#define SHEREDQUOTE	11	/* parsing " in <<,<<-,<<< delimiter */
-#define SPATTERN	12	/* parsing *(...|...) pattern (*+?@!) */
-#define SADELIM		13	/* like SBASE, looking for delimiter */
-#define STBRACEKORN	14	/* parsing ${...[#%]...} !FSH */
-#define STBRACEBOURNE	15	/* parsing ${...[#%]...} FSH */
-#define SINVALID	255	/* invalid state */
-
-struct sretrace_info {
-	struct sretrace_info *next;
-	XString xs;
-	char *xp;
-};
-
-/*
- * Structure to keep track of the lexing state and the various pieces of info
- * needed for each particular state.
- */
-typedef struct lex_state {
-	union {
-		/* point to the next state block */
-		struct lex_state *base;
-		/* marks start of state output in output string */
-		int start;
-		/* SBQUOTE: true if in double quotes: "`...`" */
-		/* SEQUOTE: got NUL, ignore rest of string */
-		bool abool;
-		/* SADELIM information */
-		struct {
-			/* character to search for */
-			unsigned char delimiter;
-			/* max. number of delimiters */
-			unsigned char num;
-		} adelim;
-	} u;
-	/* count open parentheses */
-	short nparen;
-	/* type of this state */
-	uint8_t type;
-} Lex_state;
-#define ls_base		u.base
-#define ls_start	u.start
-#define ls_bool		u.abool
-#define ls_adelim	u.adelim
-
-typedef struct {
-	Lex_state *base;
-	Lex_state *end;
-} State_info;
-
-static void readhere(struct ioword *);
-static void ungetsc(int);
-static void ungetsc_i(int);
-static int getsc_uu(void);
-static void getsc_line(Source *);
-static int getsc_bn(void);
-static int s_get(void);
-static void s_put(int);
-static char *get_brace_var(XString *, char *);
-static bool arraysub(char **);
-static void gethere(bool);
-static Lex_state *push_state_i(State_info *, Lex_state *);
-static Lex_state *pop_state_i(State_info *, Lex_state *);
-
-static int backslash_skip;
-static int ignore_backslash_newline;
-
-/* optimised getsc_bn() */
-#define o_getsc()	(*source->str != '\0' && *source->str != '\\' && \
-			    !backslash_skip ? *source->str++ : getsc_bn())
-/* optimised getsc_uu() */
-#define	o_getsc_u()	((*source->str != '\0') ? *source->str++ : getsc_uu())
-
-/* retrace helper */
-#define o_getsc_r(carg)	{				\
-	int cev = (carg);				\
-	struct sretrace_info *rp = retrace_info;	\
-							\
-	while (rp) {					\
-		Xcheck(rp->xs, rp->xp);			\
-		*rp->xp++ = cev;			\
-		rp = rp->next;				\
-	}						\
-							\
-	return (cev);					\
-}
-
-#if defined(MKSH_SMALL) && !defined(MKSH_SMALL_BUT_FAST)
-static int getsc(void);
-
-static int
-getsc(void)
-{
-	o_getsc_r(o_getsc());
-}
-#else
-static int getsc_r(int);
-
-static int
-getsc_r(int c)
-{
-	o_getsc_r(c);
-}
-
-#define getsc()		getsc_r(o_getsc())
-#endif
-
-#define STATE_BSIZE	8
-
-#define PUSH_STATE(s)	do {					\
-	if (++statep == state_info.end)				\
-		statep = push_state_i(&state_info, statep);	\
-	state = statep->type = (s);				\
-} while (/* CONSTCOND */ 0)
-
-#define POP_STATE()	do {					\
-	if (--statep == state_info.base)			\
-		statep = pop_state_i(&state_info, statep);	\
-	state = statep->type;					\
-} while (/* CONSTCOND */ 0)
-
-#define PUSH_SRETRACE()	do {					\
-	struct sretrace_info *ri;				\
-								\
-	statep->ls_start = Xsavepos(ws, wp);			\
-	ri = alloc(sizeof(struct sretrace_info), ATEMP);	\
-	Xinit(ri->xs, ri->xp, 64, ATEMP);			\
-	ri->next = retrace_info;				\
-	retrace_info = ri;					\
-} while (/* CONSTCOND */ 0)
-
-#define POP_SRETRACE()	do {					\
-	wp = Xrestpos(ws, wp, statep->ls_start);		\
-	*retrace_info->xp = '\0';				\
-	sp = Xstring(retrace_info->xs, retrace_info->xp);	\
-	dp = (void *)retrace_info;				\
-	retrace_info = retrace_info->next;			\
-	afree(dp, ATEMP);					\
-} while (/* CONSTCOND */ 0)
-
-/**
- * Lexical analyser
- *
- * tokens are not regular expressions, they are LL(1).
- * for example, "${var:-${PWD}}", and "$(size $(whence ksh))".
- * hence the state stack. Note "$(...)" are now parsed recursively.
- */
-
-int
-yylex(int cf)
-{
-	Lex_state states[STATE_BSIZE], *statep, *s2, *base;
-	State_info state_info;
-	int c, c2, state;
-	size_t cz;
-	XString ws;		/* expandable output word */
-	char *wp;		/* output word pointer */
-	char *sp, *dp;
-
- Again:
-	states[0].type = SINVALID;
-	states[0].ls_base = NULL;
-	statep = &states[1];
-	state_info.base = states;
-	state_info.end = &state_info.base[STATE_BSIZE];
-
-	Xinit(ws, wp, 64, ATEMP);
-
-	backslash_skip = 0;
-	ignore_backslash_newline = 0;
-
-	if (cf & ONEWORD)
-		state = SWORD;
-	else if (cf & LETEXPR) {
-		/* enclose arguments in (double) quotes */
-		*wp++ = OQUOTE;
-		state = SLETPAREN;
-		statep->nparen = 0;
-	} else {
-		/* normal lexing */
-		state = (cf & HEREDELIM) ? SHEREDELIM : SBASE;
-		while ((c = getsc()) == ' ' || c == '\t')
-			;
-		if (c == '#') {
-			ignore_backslash_newline++;
-			while ((c = getsc()) != '\0' && c != '\n')
-				;
-			ignore_backslash_newline--;
-		}
-		ungetsc(c);
-	}
-	if (source->flags & SF_ALIAS) {
-		/* trailing ' ' in alias definition */
-		source->flags &= ~SF_ALIAS;
-		cf |= ALIAS;
-	}
-
-	/* Initial state: one of SWORD SLETPAREN SHEREDELIM SBASE */
-	statep->type = state;
-
-	/* check for here string */
-	if (state == SHEREDELIM) {
-		c = getsc();
-		if (c == '<') {
-			state = SHEREDELIM;
-			while ((c = getsc()) == ' ' || c == '\t')
-				;
-			ungetsc(c);
-			c = '<';
-			goto accept_nonword;
-		}
-		ungetsc(c);
-	}
-
-	/* collect non-special or quoted characters to form word */
-	while (!((c = getsc()) == 0 ||
-	    ((state == SBASE || state == SHEREDELIM) && ctype(c, C_LEX1)))) {
-		if (state == SBASE &&
-		    subshell_nesting_type == /*{*/ '}' &&
-		    c == /*{*/ '}')
-			/* possibly end ${ :;} */
-			break;
- accept_nonword:
-		Xcheck(ws, wp);
-		switch (state) {
-		case SADELIM:
-			if (c == '(')
-				statep->nparen++;
-			else if (c == ')')
-				statep->nparen--;
-			else if (statep->nparen == 0 && (c == /*{*/ '}' ||
-			    c == (int)statep->ls_adelim.delimiter)) {
-				*wp++ = ADELIM;
-				*wp++ = c;
-				if (c == /*{*/ '}' || --statep->ls_adelim.num == 0)
-					POP_STATE();
-				if (c == /*{*/ '}')
-					POP_STATE();
-				break;
-			}
-			/* FALLTHROUGH */
-		case SBASE:
-			if (c == '[' && (cf & (VARASN|ARRAYVAR))) {
-				/* temporary */
-				*wp = EOS;
-				if (is_wdvarname(Xstring(ws, wp), false)) {
-					char *p, *tmp;
-
-					if (arraysub(&tmp)) {
-						*wp++ = CHAR;
-						*wp++ = c;
-						for (p = tmp; *p; ) {
-							Xcheck(ws, wp);
-							*wp++ = CHAR;
-							*wp++ = *p++;
-						}
-						afree(tmp, ATEMP);
-						break;
-					} else {
-						Source *s;
-
-						s = pushs(SREREAD,
-						    source->areap);
-						s->start = s->str =
-						    s->u.freeme = tmp;
-						s->next = source;
-						source = s;
-					}
-				}
-				*wp++ = CHAR;
-				*wp++ = c;
-				break;
-			}
-			/* FALLTHROUGH */
- Sbase1:		/* includes *(...|...) pattern (*+?@!) */
-			if (c == '*' || c == '@' || c == '+' || c == '?' ||
-			    c == '!') {
-				c2 = getsc();
-				if (c2 == '(' /*)*/ ) {
-					*wp++ = OPAT;
-					*wp++ = c;
-					PUSH_STATE(SPATTERN);
-					break;
-				}
-				ungetsc(c2);
-			}
-			/* FALLTHROUGH */
- Sbase2:		/* doesn't include *(...|...) pattern (*+?@!) */
-			switch (c) {
-			case '\\':
- getsc_qchar:
-				if ((c = getsc())) {
-					/* trailing \ is lost */
-					*wp++ = QCHAR;
-					*wp++ = c;
-				}
-				break;
-			case '\'':
- open_ssquote_unless_heredoc:
-				if ((cf & HEREDOC))
-					goto store_char;
-				*wp++ = OQUOTE;
-				ignore_backslash_newline++;
-				PUSH_STATE(SSQUOTE);
-				break;
-			case '"':
- open_sdquote:
-				*wp++ = OQUOTE;
-				PUSH_STATE(SDQUOTE);
-				break;
-			case '$':
-				/*
-				 * processing of dollar sign belongs into
-				 * Subst, except for those which can open
-				 * a string: $'…' and $"…"
-				 */
- subst_dollar_ex:
-				c = getsc();
-				switch (c) {
-				case '"':
-					goto open_sdquote;
-				case '\'':
-					goto open_sequote;
-				default:
-					goto SubstS;
-				}
-			default:
-				goto Subst;
-			}
-			break;
-
- Subst:
-			switch (c) {
-			case '\\':
-				c = getsc();
-				switch (c) {
-				case '"':
-					if ((cf & HEREDOC))
-						goto heredocquote;
-					/* FALLTHROUGH */
-				case '\\':
-				case '$': case '`':
- store_qchar:
-					*wp++ = QCHAR;
-					*wp++ = c;
-					break;
-				default:
- heredocquote:
-					Xcheck(ws, wp);
-					if (c) {
-						/* trailing \ is lost */
-						*wp++ = CHAR;
-						*wp++ = '\\';
-						*wp++ = CHAR;
-						*wp++ = c;
-					}
-					break;
-				}
-				break;
-			case '$':
-				c = getsc();
- SubstS:
-				if (c == '(') /*)*/ {
-					c = getsc();
-					if (c == '(') /*)*/ {
-						*wp++ = EXPRSUB;
-						PUSH_STATE(SASPAREN);
-						statep->nparen = 2;
-						PUSH_SRETRACE();
-						*retrace_info->xp++ = '(';
-					} else {
-						ungetsc(c);
- subst_command:
-						c = COMSUB;
- subst_command2:
-						sp = yyrecursive(c);
-						cz = strlen(sp) + 1;
-						XcheckN(ws, wp, cz);
-						*wp++ = c;
-						memcpy(wp, sp, cz);
-						wp += cz;
-					}
-				} else if (c == '{') /*}*/ {
-					if ((c = getsc()) == '|') {
-						/*
-						 * non-subenvironment
-						 * value substitution
-						 */
-						c = VALSUB;
-						goto subst_command2;
-					} else if (ctype(c, C_IFSWS)) {
-						/*
-						 * non-subenvironment
-						 * "command" substitution
-						 */
-						c = FUNSUB;
-						goto subst_command2;
-					}
-					ungetsc(c);
-					*wp++ = OSUBST;
-					*wp++ = '{'; /*}*/
-					wp = get_brace_var(&ws, wp);
-					c = getsc();
-					/* allow :# and :% (ksh88 compat) */
-					if (c == ':') {
-						*wp++ = CHAR;
-						*wp++ = c;
-						c = getsc();
-						if (c == ':') {
-							*wp++ = CHAR;
-							*wp++ = '0';
-							*wp++ = ADELIM;
-							*wp++ = ':';
-							PUSH_STATE(SBRACE);
-							PUSH_STATE(SADELIM);
-							statep->ls_adelim.delimiter = ':';
-							statep->ls_adelim.num = 1;
-							statep->nparen = 0;
-							break;
-						} else if (ksh_isdigit(c) ||
-						    c == '('/*)*/ || c == ' ' ||
-						    /*XXX what else? */
-						    c == '$') {
-							/* substring subst. */
-							if (c != ' ') {
-								*wp++ = CHAR;
-								*wp++ = ' ';
-							}
-							ungetsc(c);
-							PUSH_STATE(SBRACE);
-							PUSH_STATE(SADELIM);
-							statep->ls_adelim.delimiter = ':';
-							statep->ls_adelim.num = 2;
-							statep->nparen = 0;
-							break;
-						}
-					} else if (c == '/') {
-						*wp++ = CHAR;
-						*wp++ = c;
-						if ((c = getsc()) == '/') {
-							*wp++ = ADELIM;
-							*wp++ = c;
-						} else
-							ungetsc(c);
-						PUSH_STATE(SBRACE);
-						PUSH_STATE(SADELIM);
-						statep->ls_adelim.delimiter = '/';
-						statep->ls_adelim.num = 1;
-						statep->nparen = 0;
-						break;
-					}
-					/*
-					 * If this is a trim operation,
-					 * treat (,|,) specially in STBRACE.
-					 */
-					if (ctype(c, C_SUBOP2)) {
-						ungetsc(c);
-						if (Flag(FSH))
-							PUSH_STATE(STBRACEBOURNE);
-						else
-							PUSH_STATE(STBRACEKORN);
-					} else {
-						ungetsc(c);
-						if (state == SDQUOTE ||
-						    state == SQBRACE)
-							PUSH_STATE(SQBRACE);
-						else
-							PUSH_STATE(SBRACE);
-					}
-				} else if (ksh_isalphx(c)) {
-					*wp++ = OSUBST;
-					*wp++ = 'X';
-					do {
-						Xcheck(ws, wp);
-						*wp++ = c;
-						c = getsc();
-					} while (ksh_isalnux(c));
-					*wp++ = '\0';
-					*wp++ = CSUBST;
-					*wp++ = 'X';
-					ungetsc(c);
-				} else if (ctype(c, C_VAR1 | C_DIGIT)) {
-					Xcheck(ws, wp);
-					*wp++ = OSUBST;
-					*wp++ = 'X';
-					*wp++ = c;
-					*wp++ = '\0';
-					*wp++ = CSUBST;
-					*wp++ = 'X';
-				} else {
-					*wp++ = CHAR;
-					*wp++ = '$';
-					ungetsc(c);
-				}
-				break;
-			case '`':
- subst_gravis:
-				PUSH_STATE(SBQUOTE);
-				*wp++ = COMSUB;
-				/*
-				 * Need to know if we are inside double quotes
-				 * since sh/AT&T-ksh translate the \" to " in
-				 * "`...\"...`".
-				 * This is not done in POSIX mode (section
-				 * 3.2.3, Double Quotes: "The backquote shall
-				 * retain its special meaning introducing the
-				 * other form of command substitution (see
-				 * 3.6.3). The portion of the quoted string
-				 * from the initial backquote and the
-				 * characters up to the next backquote that
-				 * is not preceded by a backslash (having
-				 * escape characters removed) defines that
-				 * command whose output replaces `...` when
-				 * the word is expanded."
-				 * Section 3.6.3, Command Substitution:
-				 * "Within the backquoted style of command
-				 * substitution, backslash shall retain its
-				 * literal meaning, except when followed by
-				 * $ ` \.").
-				 */
-				statep->ls_bool = false;
-				s2 = statep;
-				base = state_info.base;
-				while (/* CONSTCOND */ 1) {
-					for (; s2 != base; s2--) {
-						if (s2->type == SDQUOTE) {
-							statep->ls_bool = true;
-							break;
-						}
-					}
-					if (s2 != base)
-						break;
-					if (!(s2 = s2->ls_base))
-						break;
-					base = s2-- - STATE_BSIZE;
-				}
-				break;
-			case QCHAR:
-				if (cf & LQCHAR) {
-					*wp++ = QCHAR;
-					*wp++ = getsc();
-					break;
-				}
-				/* FALLTHROUGH */
-			default:
- store_char:
-				*wp++ = CHAR;
-				*wp++ = c;
-			}
-			break;
-
-		case SEQUOTE:
-			if (c == '\'') {
-				POP_STATE();
-				*wp++ = CQUOTE;
-				ignore_backslash_newline--;
-			} else if (c == '\\') {
-				if ((c2 = unbksl(true, s_get, s_put)) == -1)
-					c2 = s_get();
-				if (c2 == 0)
-					statep->ls_bool = true;
-				if (!statep->ls_bool) {
-					char ts[4];
-
-					if ((unsigned int)c2 < 0x100) {
-						*wp++ = QCHAR;
-						*wp++ = c2;
-					} else {
-						cz = utf_wctomb(ts, c2 - 0x100);
-						ts[cz] = 0;
-						for (cz = 0; ts[cz]; ++cz) {
-							*wp++ = QCHAR;
-							*wp++ = ts[cz];
-						}
-					}
-				}
-			} else if (!statep->ls_bool) {
-				*wp++ = QCHAR;
-				*wp++ = c;
-			}
-			break;
-
-		case SSQUOTE:
-			if (c == '\'') {
-				POP_STATE();
-				if ((cf & HEREDOC) || state == SQBRACE)
-					goto store_char;
-				*wp++ = CQUOTE;
-				ignore_backslash_newline--;
-			} else {
-				*wp++ = QCHAR;
-				*wp++ = c;
-			}
-			break;
-
-		case SDQUOTE:
-			if (c == '"') {
-				POP_STATE();
-				*wp++ = CQUOTE;
-			} else
-				goto Subst;
-			break;
-
-		/* $(( ... )) */
-		case SASPAREN:
-			if (c == '(')
-				statep->nparen++;
-			else if (c == ')') {
-				statep->nparen--;
-				if (statep->nparen == 1) {
-					/* end of EXPRSUB */
-					POP_SRETRACE();
-					POP_STATE();
-
-					if ((c2 = getsc()) == /*(*/ ')') {
-						cz = strlen(sp) - 2;
-						XcheckN(ws, wp, cz);
-						memcpy(wp, sp + 1, cz);
-						wp += cz;
-						afree(sp, ATEMP);
-						*wp++ = '\0';
-						break;
-					} else {
-						Source *s;
-
-						ungetsc(c2);
-						/*
-						 * mismatched parenthesis -
-						 * assume we were really
-						 * parsing a $(...) expression
-						 */
-						--wp;
-						s = pushs(SREREAD,
-						    source->areap);
-						s->start = s->str =
-						    s->u.freeme = sp;
-						s->next = source;
-						source = s;
-						goto subst_command;
-					}
-				}
-			}
-			/* reuse existing state machine */
-			goto Sbase2;
-
-		case SQBRACE:
-			if (c == '\\') {
-				/*
-				 * perform POSIX "quote removal" if the back-
-				 * slash is "special", i.e. same cases as the
-				 * {case '\\':} in Subst: plus closing brace;
-				 * in mksh code "quote removal" on '\c' means
-				 * write QCHAR+c, otherwise CHAR+\+CHAR+c are
-				 * emitted (in heredocquote:)
-				 */
-				if ((c = getsc()) == '"' || c == '\\' ||
-				    c == '$' || c == '`' || c == /*{*/'}')
-					goto store_qchar;
-				goto heredocquote;
-			}
-			goto common_SQBRACE;
-
-		case SBRACE:
-			if (c == '\'')
-				goto open_ssquote_unless_heredoc;
-			else if (c == '\\')
-				goto getsc_qchar;
- common_SQBRACE:
-			if (c == '"')
-				goto open_sdquote;
-			else if (c == '$')
-				goto subst_dollar_ex;
-			else if (c == '`')
-				goto subst_gravis;
-			else if (c != /*{*/ '}')
-				goto store_char;
-			POP_STATE();
-			*wp++ = CSUBST;
-			*wp++ = /*{*/ '}';
-			break;
-
-		/* Same as SBASE, except (,|,) treated specially */
-		case STBRACEKORN:
-			if (c == '|')
-				*wp++ = SPAT;
-			else if (c == '(') {
-				*wp++ = OPAT;
-				/* simile for @ */
-				*wp++ = ' ';
-				PUSH_STATE(SPATTERN);
-			} else /* FALLTHROUGH */
-		case STBRACEBOURNE:
-			  if (c == /*{*/ '}') {
-				POP_STATE();
-				*wp++ = CSUBST;
-				*wp++ = /*{*/ '}';
-			} else
-				goto Sbase1;
-			break;
-
-		case SBQUOTE:
-			if (c == '`') {
-				*wp++ = 0;
-				POP_STATE();
-			} else if (c == '\\') {
-				switch (c = getsc()) {
-				case 0:
-					/* trailing \ is lost */
-					break;
-				case '\\':
-				case '$': case '`':
-					*wp++ = c;
-					break;
-				case '"':
-					if (statep->ls_bool) {
-						*wp++ = c;
-						break;
-					}
-					/* FALLTHROUGH */
-				default:
-					*wp++ = '\\';
-					*wp++ = c;
-					break;
-				}
-			} else
-				*wp++ = c;
-			break;
-
-		/* ONEWORD */
-		case SWORD:
-			goto Subst;
-
-		/* LETEXPR: (( ... )) */
-		case SLETPAREN:
-			if (c == /*(*/ ')') {
-				if (statep->nparen > 0)
-					--statep->nparen;
-				else if ((c2 = getsc()) == /*(*/ ')') {
-					c = 0;
-					*wp++ = CQUOTE;
-					goto Done;
-				} else {
-					Source *s;
-
-					ungetsc(c2);
-					/*
-					 * mismatched parenthesis -
-					 * assume we were really
-					 * parsing a (...) expression
-					 */
-					*wp = EOS;
-					sp = Xstring(ws, wp);
-					dp = wdstrip(sp, WDS_KEEPQ);
-					s = pushs(SREREAD, source->areap);
-					s->start = s->str = s->u.freeme = dp;
-					s->next = source;
-					source = s;
-					return ('('/*)*/);
-				}
-			} else if (c == '(')
-				/*
-				 * parentheses inside quotes and
-				 * backslashes are lost, but AT&T ksh
-				 * doesn't count them either
-				 */
-				++statep->nparen;
-			goto Sbase2;
-
-		/* <<, <<-, <<< delimiter */
-		case SHEREDELIM:
-			/*
-			 * here delimiters need a special case since
-			 * $ and `...` are not to be treated specially
-			 */
-			switch (c) {
-			case '\\':
-				if ((c = getsc())) {
-					/* trailing \ is lost */
-					*wp++ = QCHAR;
-					*wp++ = c;
-				}
-				break;
-			case '\'':
-				goto open_ssquote_unless_heredoc;
-			case '$':
-				if ((c2 = getsc()) == '\'') {
- open_sequote:
-					*wp++ = OQUOTE;
-					ignore_backslash_newline++;
-					PUSH_STATE(SEQUOTE);
-					statep->ls_bool = false;
-					break;
-				} else if (c2 == '"') {
-					/* FALLTHROUGH */
-			case '"':
-					state = statep->type = SHEREDQUOTE;
-					PUSH_SRETRACE();
-					break;
-				}
-				ungetsc(c2);
-				/* FALLTHROUGH */
-			default:
-				*wp++ = CHAR;
-				*wp++ = c;
-			}
-			break;
-
-		/* " in <<, <<-, <<< delimiter */
-		case SHEREDQUOTE:
-			if (c != '"')
-				goto Subst;
-			POP_SRETRACE();
-			dp = strnul(sp) - 1;
-			/* remove the trailing double quote */
-			*dp = '\0';
-			/* store the quoted string */
-			*wp++ = OQUOTE;
-			XcheckN(ws, wp, (dp - sp));
-			dp = sp;
-			while ((c = *dp++)) {
-				if (c == '\\') {
-					switch ((c = *dp++)) {
-					case '\\':
-					case '"':
-					case '$':
-					case '`':
-						break;
-					default:
-						*wp++ = CHAR;
-						*wp++ = '\\';
-						break;
-					}
-				}
-				*wp++ = CHAR;
-				*wp++ = c;
-			}
-			afree(sp, ATEMP);
-			*wp++ = CQUOTE;
-			state = statep->type = SHEREDELIM;
-			break;
-
-		/* in *(...|...) pattern (*+?@!) */
-		case SPATTERN:
-			if (c == /*(*/ ')') {
-				*wp++ = CPAT;
-				POP_STATE();
-			} else if (c == '|') {
-				*wp++ = SPAT;
-			} else if (c == '(') {
-				*wp++ = OPAT;
-				/* simile for @ */
-				*wp++ = ' ';
-				PUSH_STATE(SPATTERN);
-			} else
-				goto Sbase1;
-			break;
-		}
-	}
- Done:
-	Xcheck(ws, wp);
-	if (statep != &states[1])
-		/* XXX figure out what is missing */
-		yyerror("no closing quote\n");
-
-	/* This done to avoid tests for SHEREDELIM wherever SBASE tested */
-	if (state == SHEREDELIM)
-		state = SBASE;
-
-	dp = Xstring(ws, wp);
-	if (state == SBASE && (
-#ifndef MKSH_LEGACY_MODE
-	    (c == '&' && !Flag(FSH) && !Flag(FPOSIX)) ||
-#endif
-	    c == '<' || c == '>')) {
-		struct ioword *iop = alloc(sizeof(struct ioword), ATEMP);
-
-		if (Xlength(ws, wp) == 0)
-			iop->unit = c == '<' ? 0 : 1;
-		else for (iop->unit = 0, c2 = 0; c2 < Xlength(ws, wp); c2 += 2) {
-			if (dp[c2] != CHAR)
-				goto no_iop;
-			if (!ksh_isdigit(dp[c2 + 1]))
-				goto no_iop;
-			iop->unit = (iop->unit * 10) + dp[c2 + 1] - '0';
-		}
-
-		if (iop->unit >= FDBASE)
-			goto no_iop;
-
-		if (c == '&') {
-			if ((c2 = getsc()) != '>') {
-				ungetsc(c2);
-				goto no_iop;
-			}
-			c = c2;
-			iop->flag = IOBASH;
-		} else
-			iop->flag = 0;
-
-		c2 = getsc();
-		/* <<, >>, <> are ok, >< is not */
-		if (c == c2 || (c == '<' && c2 == '>')) {
-			iop->flag |= c == c2 ?
-			    (c == '>' ? IOCAT : IOHERE) : IORDWR;
-			if (iop->flag == IOHERE) {
-				if ((c2 = getsc()) == '-') {
-					iop->flag |= IOSKIP;
-					c2 = getsc();
-				} else if (c2 == '<')
-					iop->flag |= IOHERESTR;
-				ungetsc(c2);
-				if (c2 == '\n')
-					iop->flag |= IONDELIM;
-			}
-		} else if (c2 == '&')
-			iop->flag |= IODUP | (c == '<' ? IORDUP : 0);
-		else {
-			iop->flag |= c == '>' ? IOWRITE : IOREAD;
-			if (c == '>' && c2 == '|')
-				iop->flag |= IOCLOB;
-			else
-				ungetsc(c2);
-		}
-
-		iop->name = NULL;
-		iop->delim = NULL;
-		iop->heredoc = NULL;
-		/* free word */
-		Xfree(ws, wp);
-		yylval.iop = iop;
-		return (REDIR);
- no_iop:
-		afree(iop, ATEMP);
-	}
-
-	if (wp == dp && state == SBASE) {
-		/* free word */
-		Xfree(ws, wp);
-		/* no word, process LEX1 character */
-		if ((c == '|') || (c == '&') || (c == ';') || (c == '('/*)*/)) {
-			if ((c2 = getsc()) == c)
-				c = (c == ';') ? BREAK :
-				    (c == '|') ? LOGOR :
-				    (c == '&') ? LOGAND :
-				    /* c == '(' ) */ MDPAREN;
-			else if (c == '|' && c2 == '&')
-				c = COPROC;
-			else if (c == ';' && c2 == '|')
-				c = BRKEV;
-			else if (c == ';' && c2 == '&')
-				c = BRKFT;
-			else
-				ungetsc(c2);
-#ifndef MKSH_SMALL
-			if (c == BREAK) {
-				if ((c2 = getsc()) == '&')
-					c = BRKEV;
-				else
-					ungetsc(c2);
-			}
-#endif
-		} else if (c == '\n') {
-			gethere(false);
-			if (cf & CONTIN)
-				goto Again;
-		} else if (c == '\0')
-			/* need here strings at EOF */
-			gethere(true);
-		return (c);
-	}
-
-	/* terminate word */
-	*wp++ = EOS;
-	yylval.cp = Xclose(ws, wp);
-	if (state == SWORD || state == SLETPAREN
-	    /* XXX ONEWORD? */)
-		return (LWORD);
-
-	/* unget terminator */
-	ungetsc(c);
-
-	/*
-	 * note: the alias-vs-function code below depends on several
-	 * interna: starting from here, source->str is not modified;
-	 * the way getsc() and ungetsc() operate; etc.
-	 */
-
-	/* copy word to unprefixed string ident */
-	sp = yylval.cp;
-	dp = ident;
-	if ((cf & HEREDELIM) && (sp[1] == '<'))
-		while ((dp - ident) < IDENT) {
-			if ((c = *sp++) == CHAR)
-				*dp++ = *sp++;
-			else if ((c != OQUOTE) && (c != CQUOTE))
-				break;
-		}
-	else
-		while ((dp - ident) < IDENT && (c = *sp++) == CHAR)
-			*dp++ = *sp++;
-	/* Make sure the ident array stays '\0' padded */
-	memset(dp, 0, (ident + IDENT) - dp + 1);
-	if (c != EOS)
-		/* word is not unquoted */
-		*ident = '\0';
-
-	if (*ident != '\0' && (cf & (KEYWORD | ALIAS))) {
-		struct tbl *p;
-		uint32_t h = hash(ident);
-
-		if ((cf & KEYWORD) && (p = ktsearch(&keywords, ident, h)) &&
-		    (!(cf & ESACONLY) || p->val.i == ESAC ||
-		    p->val.i == /*{*/ '}')) {
-			afree(yylval.cp, ATEMP);
-			return (p->val.i);
-		}
-		if ((cf & ALIAS) && (p = ktsearch(&aliases, ident, h)) &&
-		    (p->flag & ISSET)) {
-			/*
-			 * this still points to the same character as the
-			 * ungetsc'd terminator from above
-			 */
-			const char *cp = source->str;
-
-			/* prefer POSIX but not Korn functions over aliases */
-			while (*cp == ' ' || *cp == '\t')
-				/*
-				 * this is like getsc() without skipping
-				 * over Source boundaries (including not
-				 * parsing ungetsc'd characters that got
-				 * pushed into an SREREAD) which is what
-				 * we want here anyway: find out whether
-				 * the alias name is followed by a POSIX
-				 * function definition (only the opening
-				 * parenthesis is checked though)
-				 */
-				++cp;
-			/* prefer functions over aliases */
-			if (cp[0] != '(' || cp[1] != ')') {
-				Source *s = source;
-
-				while (s && (s->flags & SF_HASALIAS))
-					if (s->u.tblp == p)
-						return (LWORD);
-					else
-						s = s->next;
-				/* push alias expansion */
-				s = pushs(SALIAS, source->areap);
-				s->start = s->str = p->val.s;
-				s->u.tblp = p;
-				s->flags |= SF_HASALIAS;
-				s->next = source;
-				if (source->type == SEOF) {
-					/* prevent infinite recursion at EOS */
-					source->u.tblp = p;
-					source->flags |= SF_HASALIAS;
-				}
-				source = s;
-				afree(yylval.cp, ATEMP);
-				goto Again;
-			}
-		}
-	}
-
-	return (LWORD);
-}
-
-static void
-gethere(bool iseof)
-{
-	struct ioword **p;
-
-	for (p = heres; p < herep; p++)
-		if (iseof && !((*p)->flag & IOHERESTR))
-			/* only here strings at EOF */
-			return;
-		else
-			readhere(*p);
-	herep = heres;
-}
-
-/*
- * read "<<word" text into temp file
- */
-
-static void
-readhere(struct ioword *iop)
-{
-	int c;
-	const char *eof, *eofp;
-	XString xs;
-	char *xp;
-	int xpos;
-
-	if (iop->flag & IOHERESTR) {
-		/* process the here string */
-		iop->heredoc = xp = evalstr(iop->delim, DOBLANK);
-		xpos = strlen(xp) - 1;
-		memmove(xp, xp + 1, xpos);
-		xp[xpos] = '\n';
-		return;
-	}
-
-	eof = iop->flag & IONDELIM ? "<<" : evalstr(iop->delim, 0);
-
-	if (!(iop->flag & IOEVAL))
-		ignore_backslash_newline++;
-
-	Xinit(xs, xp, 256, ATEMP);
-
- heredoc_read_line:
-	/* beginning of line */
-	eofp = eof;
-	xpos = Xsavepos(xs, xp);
-	if (iop->flag & IOSKIP) {
-		/* skip over leading tabs */
-		while ((c = getsc()) == '\t')
-			/* nothing */;
-		goto heredoc_parse_char;
-	}
- heredoc_read_char:
-	c = getsc();
- heredoc_parse_char:
-	/* compare with here document marker */
-	if (!*eofp) {
-		/* end of here document marker, what to do? */
-		switch (c) {
-		case /*(*/ ')':
-			if (!subshell_nesting_type)
-				/*-
-				 * not allowed outside $(...) or (...)
-				 * => mismatch
-				 */
-				break;
-			/* allow $(...) or (...) to close here */
-			ungetsc(/*(*/ ')');
-			/* FALLTHROUGH */
-		case 0:
-			/*
-			 * Allow EOF here to commands without trailing
-			 * newlines (mksh -c '...') will work as well.
-			 */
-		case '\n':
-			/* Newline terminates here document marker */
-			goto heredoc_found_terminator;
-		}
-	} else if (c == *eofp++)
-		/* store; then read and compare next character */
-		goto heredoc_store_and_loop;
-	/* nope, mismatch; read until end of line */
-	while (c != '\n') {
-		if (!c)
-			/* oops, reached EOF */
-			yyerror("%s '%s' unclosed\n", "here document", eof);
-		/* store character */
-		Xcheck(xs, xp);
-		Xput(xs, xp, c);
-		/* read next character */
-		c = getsc();
-	}
-	/* we read a newline as last character */
- heredoc_store_and_loop:
-	/* store character */
-	Xcheck(xs, xp);
-	Xput(xs, xp, c);
-	if (c == '\n')
-		goto heredoc_read_line;
-	goto heredoc_read_char;
-
- heredoc_found_terminator:
-	/* jump back to saved beginning of line */
-	xp = Xrestpos(xs, xp, xpos);
-	/* terminate, close and store */
-	Xput(xs, xp, '\0');
-	iop->heredoc = Xclose(xs, xp);
-
-	if (!(iop->flag & IOEVAL))
-		ignore_backslash_newline--;
-}
-
-void
-yyerror(const char *fmt, ...)
-{
-	va_list va;
-
-	/* pop aliases and re-reads */
-	while (source->type == SALIAS || source->type == SREREAD)
-		source = source->next;
-	/* zap pending input */
-	source->str = null;
-
-	error_prefix(true);
-	va_start(va, fmt);
-	shf_vfprintf(shl_out, fmt, va);
-	va_end(va);
-	errorfz();
-}
-
-/*
- * input for yylex with alias expansion
- */
-
-Source *
-pushs(int type, Area *areap)
-{
-	Source *s;
-
-	s = alloc(sizeof(Source), areap);
-	memset(s, 0, sizeof(Source));
-	s->type = type;
-	s->str = null;
-	s->areap = areap;
-	if (type == SFILE || type == SSTDIN)
-		XinitN(s->xs, 256, s->areap);
-	return (s);
-}
-
-static int
-getsc_uu(void)
-{
-	Source *s = source;
-	int c;
-
-	while ((c = *s->str++) == 0) {
-		/* return 0 for EOF by default */
-		s->str = NULL;
-		switch (s->type) {
-		case SEOF:
-			s->str = null;
-			return (0);
-
-		case SSTDIN:
-		case SFILE:
-			getsc_line(s);
-			break;
-
-		case SWSTR:
-			break;
-
-		case SSTRING:
-		case SSTRINGCMDLINE:
-			break;
-
-		case SWORDS:
-			s->start = s->str = *s->u.strv++;
-			s->type = SWORDSEP;
-			break;
-
-		case SWORDSEP:
-			if (*s->u.strv == NULL) {
-				s->start = s->str = "\n";
-				s->type = SEOF;
-			} else {
-				s->start = s->str = " ";
-				s->type = SWORDS;
-			}
-			break;
-
-		case SALIAS:
-			if (s->flags & SF_ALIASEND) {
-				/* pass on an unused SF_ALIAS flag */
-				source = s->next;
-				source->flags |= s->flags & SF_ALIAS;
-				s = source;
-			} else if (*s->u.tblp->val.s &&
-			    (c = strnul(s->u.tblp->val.s)[-1], ksh_isspace(c))) {
-				/* pop source stack */
-				source = s = s->next;
-				/*
-				 * Note that this alias ended with a
-				 * space, enabling alias expansion on
-				 * the following word.
-				 */
-				s->flags |= SF_ALIAS;
-			} else {
-				/*
-				 * At this point, we need to keep the current
-				 * alias in the source list so recursive
-				 * aliases can be detected and we also need to
-				 * return the next character. Do this by
-				 * temporarily popping the alias to get the
-				 * next character and then put it back in the
-				 * source list with the SF_ALIASEND flag set.
-				 */
-				/* pop source stack */
-				source = s->next;
-				source->flags |= s->flags & SF_ALIAS;
-				c = getsc_uu();
-				if (c) {
-					s->flags |= SF_ALIASEND;
-					s->ugbuf[0] = c; s->ugbuf[1] = '\0';
-					s->start = s->str = s->ugbuf;
-					s->next = source;
-					source = s;
-				} else {
-					s = source;
-					/* avoid reading EOF twice */
-					s->str = NULL;
-					break;
-				}
-			}
-			continue;
-
-		case SREREAD:
-			if (s->start != s->ugbuf)
-				/* yuck */
-				afree(s->u.freeme, ATEMP);
-			source = s = s->next;
-			continue;
-		}
-		if (s->str == NULL) {
-			s->type = SEOF;
-			s->start = s->str = null;
-			return ('\0');
-		}
-		if (s->flags & SF_ECHO) {
-			shf_puts(s->str, shl_out);
-			shf_flush(shl_out);
-		}
-	}
-	return (c);
-}
-
-static void
-getsc_line(Source *s)
-{
-	char *xp = Xstring(s->xs, xp), *cp;
-	bool interactive = Flag(FTALKING) && s->type == SSTDIN;
-	bool have_tty = tobool(interactive && (s->flags & SF_TTY));
-
-	/* Done here to ensure nothing odd happens when a timeout occurs */
-	XcheckN(s->xs, xp, LINE);
-	*xp = '\0';
-	s->start = s->str = xp;
-
-	if (have_tty && ksh_tmout) {
-		ksh_tmout_state = TMOUT_READING;
-		alarm(ksh_tmout);
-	}
-	if (interactive)
-		change_winsz();
-#ifndef MKSH_NO_CMDLINE_EDITING
-	if (have_tty && (
-#if !MKSH_S_NOVI
-	    Flag(FVI) ||
-#endif
-	    Flag(FEMACS) || Flag(FGMACS))) {
-		int nread;
-
-		nread = x_read(xp);
-		if (nread < 0)
-			/* read error */
-			nread = 0;
-		xp[nread] = '\0';
-		xp += nread;
-	} else
-#endif
-	  {
-		if (interactive)
-			pprompt(prompt, 0);
-		else
-			s->line++;
-
-		while (/* CONSTCOND */ 1) {
-			char *p = shf_getse(xp, Xnleft(s->xs, xp), s->u.shf);
-
-			if (!p && shf_error(s->u.shf) &&
-			    shf_errno(s->u.shf) == EINTR) {
-				shf_clearerr(s->u.shf);
-				if (trap)
-					runtraps(0);
-				continue;
-			}
-			if (!p || (xp = p, xp[-1] == '\n'))
-				break;
-			/* double buffer size */
-			/* move past NUL so doubling works... */
-			xp++;
-			XcheckN(s->xs, xp, Xlength(s->xs, xp));
-			/* ...and move back again */
-			xp--;
-		}
-		/*
-		 * flush any unwanted input so other programs/builtins
-		 * can read it. Not very optimal, but less error prone
-		 * than flushing else where, dealing with redirections,
-		 * etc.
-		 * TODO: reduce size of shf buffer (~128?) if SSTDIN
-		 */
-		if (s->type == SSTDIN)
-			shf_flush(s->u.shf);
-	}
-	/*
-	 * XXX: temporary kludge to restore source after a
-	 * trap may have been executed.
-	 */
-	source = s;
-	if (have_tty && ksh_tmout) {
-		ksh_tmout_state = TMOUT_EXECUTING;
-		alarm(0);
-	}
-	cp = Xstring(s->xs, xp);
-	s->start = s->str = cp;
-	strip_nuls(Xstring(s->xs, xp), Xlength(s->xs, xp));
-	/* Note: if input is all nulls, this is not eof */
-	if (Xlength(s->xs, xp) == 0) {
-		/* EOF */
-		if (s->type == SFILE)
-			shf_fdclose(s->u.shf);
-		s->str = NULL;
-	} else if (interactive && *s->str &&
-	    (cur_prompt != PS1 || !ctype(*s->str, C_IFS | C_IFSWS))) {
-		histsave(&s->line, s->str, true, true);
-#if !defined(MKSH_SMALL) && HAVE_PERSISTENT_HISTORY
-	} else if (interactive && cur_prompt == PS1) {
-		cp = Xstring(s->xs, xp);
-		while (*cp && ctype(*cp, C_IFSWS))
-			++cp;
-		if (!*cp)
-			histsync();
-#endif
-	}
-	if (interactive)
-		set_prompt(PS2, NULL);
-}
-
-void
-set_prompt(int to, Source *s)
-{
-	cur_prompt = to;
-
-	switch (to) {
-	/* command */
-	case PS1:
-		/*
-		 * Substitute ! and !! here, before substitutions are done
-		 * so ! in expanded variables are not expanded.
-		 * NOTE: this is not what AT&T ksh does (it does it after
-		 * substitutions, POSIX doesn't say which is to be done.
-		 */
-		{
-			struct shf *shf;
-			char * volatile ps1;
-			Area *saved_atemp;
-
-			ps1 = str_val(global("PS1"));
-			shf = shf_sopen(NULL, strlen(ps1) * 2,
-			    SHF_WR | SHF_DYNAMIC, NULL);
-			while (*ps1)
-				if (*ps1 != '!' || *++ps1 == '!')
-					shf_putchar(*ps1++, shf);
-				else
-					shf_fprintf(shf, "%d",
-						s ? s->line + 1 : 0);
-			ps1 = shf_sclose(shf);
-			saved_atemp = ATEMP;
-			newenv(E_ERRH);
-			if (kshsetjmp(e->jbuf)) {
-				prompt = safe_prompt;
-				/*
-				 * Don't print an error - assume it has already
-				 * been printed. Reason is we may have forked
-				 * to run a command and the child may be
-				 * unwinding its stack through this code as it
-				 * exits.
-				 */
-			} else {
-				char *cp = substitute(ps1, 0);
-				strdupx(prompt, cp, saved_atemp);
-			}
-			quitenv(NULL);
-		}
-		break;
-	/* command continuation */
-	case PS2:
-		prompt = str_val(global("PS2"));
-		break;
-	}
-}
-
-int
-pprompt(const char *cp, int ntruncate)
-{
-	int columns = 0, lines = 0;
-	bool indelimit = false;
-	char delimiter = 0;
-
-	/*
-	 * Undocumented AT&T ksh feature:
-	 * If the second char in the prompt string is \r then the first
-	 * char is taken to be a non-printing delimiter and any chars
-	 * between two instances of the delimiter are not considered to
-	 * be part of the prompt length
-	 */
-	if (*cp && cp[1] == '\r') {
-		delimiter = *cp;
-		cp += 2;
-	}
-	for (; *cp; cp++) {
-		if (indelimit && *cp != delimiter)
-			;
-		else if (*cp == '\n' || *cp == '\r') {
-			lines += columns / x_cols + ((*cp == '\n') ? 1 : 0);
-			columns = 0;
-		} else if (*cp == '\t') {
-			columns = (columns | 7) + 1;
-		} else if (*cp == '\b') {
-			if (columns > 0)
-				columns--;
-		} else if (*cp == delimiter)
-			indelimit = !indelimit;
-		else if (UTFMODE && ((unsigned char)*cp > 0x7F)) {
-			const char *cp2;
-			columns += utf_widthadj(cp, &cp2);
-			if (indelimit ||
-			    (ntruncate < (x_cols * lines + columns)))
-				shf_write(cp, cp2 - cp, shl_out);
-			cp = cp2 - /* loop increment */ 1;
-			continue;
-		} else
-			columns++;
-		if ((*cp != delimiter) &&
-		    (indelimit || (ntruncate < (x_cols * lines + columns))))
-			shf_putc(*cp, shl_out);
-	}
-	shf_flush(shl_out);
-	return (x_cols * lines + columns);
-}
-
-/*
- * Read the variable part of a ${...} expression (i.e. up to but not
- * including the :[-+?=#%] or close-brace).
- */
-static char *
-get_brace_var(XString *wsp, char *wp)
-{
-	char c;
-	enum parse_state {
-		PS_INITIAL, PS_SAW_HASH, PS_IDENT,
-		PS_NUMBER, PS_VAR1
-	} state = PS_INITIAL;
-
-	while (/* CONSTCOND */ 1) {
-		c = getsc();
-		/* State machine to figure out where the variable part ends. */
-		switch (state) {
-		case PS_INITIAL:
-			if (c == '#' || c == '!' || c == '%') {
-				state = PS_SAW_HASH;
-				break;
-			}
-			/* FALLTHROUGH */
-		case PS_SAW_HASH:
-			if (ksh_isalphx(c))
-				state = PS_IDENT;
-			else if (ksh_isdigit(c))
-				state = PS_NUMBER;
-			else if (c == '#') {
-				if (state == PS_SAW_HASH) {
-					char c2;
-
-					c2 = getsc();
-					ungetsc(c2);
-					if (c2 != /*{*/ '}') {
-						ungetsc(c);
-						goto out;
-					}
-				}
-				state = PS_VAR1;
-			} else if (ctype(c, C_VAR1))
-				state = PS_VAR1;
-			else
-				goto out;
-			break;
-		case PS_IDENT:
-			if (!ksh_isalnux(c)) {
-				if (c == '[') {
-					char *tmp, *p;
-
-					if (!arraysub(&tmp))
-						yyerror("missing ]\n");
-					*wp++ = c;
-					for (p = tmp; *p; ) {
-						Xcheck(*wsp, wp);
-						*wp++ = *p++;
-					}
-					afree(tmp, ATEMP);
-					/* the ] */
-					c = getsc();
-				}
-				goto out;
-			}
-			break;
-		case PS_NUMBER:
-			if (!ksh_isdigit(c))
-				goto out;
-			break;
-		case PS_VAR1:
-			goto out;
-		}
-		Xcheck(*wsp, wp);
-		*wp++ = c;
-	}
- out:
-	/* end of variable part */
-	*wp++ = '\0';
-	ungetsc(c);
-	return (wp);
-}
-
-/*
- * Save an array subscript - returns true if matching bracket found, false
- * if eof or newline was found.
- * (Returned string double null terminated)
- */
-static bool
-arraysub(char **strp)
-{
-	XString ws;
-	char *wp, c;
-	/* we are just past the initial [ */
-	unsigned int depth = 1;
-
-	Xinit(ws, wp, 32, ATEMP);
-
-	do {
-		c = getsc();
-		Xcheck(ws, wp);
-		*wp++ = c;
-		if (c == '[')
-			depth++;
-		else if (c == ']')
-			depth--;
-	} while (depth > 0 && c && c != '\n');
-
-	*wp++ = '\0';
-	*strp = Xclose(ws, wp);
-
-	return (tobool(depth == 0));
-}
-
-/* Unget a char: handles case when we are already at the start of the buffer */
-static void
-ungetsc(int c)
-{
-	struct sretrace_info *rp = retrace_info;
-
-	if (backslash_skip)
-		backslash_skip--;
-	/* Don't unget EOF... */
-	if (source->str == null && c == '\0')
-		return;
-	while (rp) {
-		if (Xlength(rp->xs, rp->xp))
-			rp->xp--;
-		rp = rp->next;
-	}
-	ungetsc_i(c);
-}
-static void
-ungetsc_i(int c)
-{
-	if (source->str > source->start)
-		source->str--;
-	else {
-		Source *s;
-
-		s = pushs(SREREAD, source->areap);
-		s->ugbuf[0] = c; s->ugbuf[1] = '\0';
-		s->start = s->str = s->ugbuf;
-		s->next = source;
-		source = s;
-	}
-}
-
-
-/* Called to get a char that isn't a \newline sequence. */
-static int
-getsc_bn(void)
-{
-	int c, c2;
-
-	if (ignore_backslash_newline)
-		return (o_getsc_u());
-
-	if (backslash_skip == 1) {
-		backslash_skip = 2;
-		return (o_getsc_u());
-	}
-
-	backslash_skip = 0;
-
-	while (/* CONSTCOND */ 1) {
-		c = o_getsc_u();
-		if (c == '\\') {
-			if ((c2 = o_getsc_u()) == '\n')
-				/* ignore the \newline; get the next char... */
-				continue;
-			ungetsc_i(c2);
-			backslash_skip = 1;
-		}
-		return (c);
-	}
-}
-
-void
-yyskiputf8bom(void)
-{
-	int c;
-
-	if ((unsigned char)(c = o_getsc_u()) != 0xEF) {
-		ungetsc_i(c);
-		return;
-	}
-	if ((unsigned char)(c = o_getsc_u()) != 0xBB) {
-		ungetsc_i(c);
-		ungetsc_i(0xEF);
-		return;
-	}
-	if ((unsigned char)(c = o_getsc_u()) != 0xBF) {
-		ungetsc_i(c);
-		ungetsc_i(0xBB);
-		ungetsc_i(0xEF);
-		return;
-	}
-	UTFMODE |= 8;
-}
-
-static Lex_state *
-push_state_i(State_info *si, Lex_state *old_end)
-{
-	Lex_state *news = alloc2(STATE_BSIZE, sizeof(Lex_state), ATEMP);
-
-	news[0].ls_base = old_end;
-	si->base = &news[0];
-	si->end = &news[STATE_BSIZE];
-	return (&news[1]);
-}
-
-static Lex_state *
-pop_state_i(State_info *si, Lex_state *old_end)
-{
-	Lex_state *old_base = si->base;
-
-	si->base = old_end->ls_base - STATE_BSIZE;
-	si->end = old_end->ls_base;
-
-	afree(old_base, ATEMP);
-
-	return (si->base + STATE_BSIZE - 1);
-}
-
-static int
-s_get(void)
-{
-	return (getsc());
-}
-
-static void
-s_put(int c)
-{
-	ungetsc(c);
-}

Copied: vendor/MirOS/mksh/R50/lex.c (from rev 6707, vendor/MirOS/mksh/dist/lex.c)
===================================================================
--- vendor/MirOS/mksh/R50/lex.c	                        (rev 0)
+++ vendor/MirOS/mksh/R50/lex.c	2014-07-01 12:01:17 UTC (rev 6708)
@@ -0,0 +1,1805 @@
+/*	$OpenBSD: lex.c,v 1.49 2013/12/17 16:37:06 deraadt Exp $	*/
+
+/*-
+ * Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
+ *		 2011, 2012, 2013, 2014
+ *	Thorsten Glaser <tg at mirbsd.org>
+ *
+ * Provided that these terms and disclaimer and all copyright notices
+ * are retained or reproduced in an accompanying document, permission
+ * is granted to deal in this work without restriction, including un-
+ * limited rights to use, publicly perform, distribute, sell, modify,
+ * merge, give away, or sublicence.
+ *
+ * This work is provided "AS IS" and WITHOUT WARRANTY of any kind, to
+ * the utmost extent permitted by applicable law, neither express nor
+ * implied; without malicious intent or gross negligence. In no event
+ * may a licensor, author or contributor be held liable for indirect,
+ * direct, other damage, loss, or other issues arising in any way out
+ * of dealing in the work, even if advised of the possibility of such
+ * damage or existence of a defect, except proven that it results out
+ * of said person's immediate fault when using the work as intended.
+ */
+
+#include "sh.h"
+
+__RCSID("$MirOS: src/bin/mksh/lex.c,v 1.193 2014/06/29 11:28:28 tg Exp $");
+
+/*
+ * states while lexing word
+ */
+#define SBASE		0	/* outside any lexical constructs */
+#define SWORD		1	/* implicit quoting for substitute() */
+#define SLETPAREN	2	/* inside (( )), implicit quoting */
+#define SSQUOTE		3	/* inside '' */
+#define SDQUOTE		4	/* inside "" */
+#define SEQUOTE		5	/* inside $'' */
+#define SBRACE		6	/* inside ${} */
+#define SQBRACE		7	/* inside "${}" */
+#define SBQUOTE		8	/* inside `` */
+#define SASPAREN	9	/* inside $(( )) */
+#define SHEREDELIM	10	/* parsing <<,<<-,<<< delimiter */
+#define SHEREDQUOTE	11	/* parsing " in <<,<<-,<<< delimiter */
+#define SPATTERN	12	/* parsing *(...|...) pattern (*+?@!) */
+#define SADELIM		13	/* like SBASE, looking for delimiter */
+#define STBRACEKORN	14	/* parsing ${...[#%]...} !FSH */
+#define STBRACEBOURNE	15	/* parsing ${...[#%]...} FSH */
+#define SINVALID	255	/* invalid state */
+
+struct sretrace_info {
+	struct sretrace_info *next;
+	XString xs;
+	char *xp;
+};
+
+/*
+ * Structure to keep track of the lexing state and the various pieces of info
+ * needed for each particular state.
+ */
+typedef struct lex_state {
+	union {
+		/* point to the next state block */
+		struct lex_state *base;
+		/* marks start of state output in output string */
+		int start;
+		/* SBQUOTE: true if in double quotes: "`...`" */
+		/* SEQUOTE: got NUL, ignore rest of string */
+		bool abool;
+		/* SADELIM information */
+		struct {
+			/* character to search for */
+			unsigned char delimiter;
+			/* max. number of delimiters */
+			unsigned char num;
+		} adelim;
+	} u;
+	/* count open parentheses */
+	short nparen;
+	/* type of this state */
+	uint8_t type;
+} Lex_state;
+#define ls_base		u.base
+#define ls_start	u.start
+#define ls_bool		u.abool
+#define ls_adelim	u.adelim
+
+typedef struct {
+	Lex_state *base;
+	Lex_state *end;
+} State_info;
+
+static void readhere(struct ioword *);
+static void ungetsc(int);
+static void ungetsc_i(int);
+static int getsc_uu(void);
+static void getsc_line(Source *);
+static int getsc_bn(void);
+static int s_get(void);
+static void s_put(int);
+static char *get_brace_var(XString *, char *);
+static bool arraysub(char **);
+static void gethere(bool);
+static Lex_state *push_state_i(State_info *, Lex_state *);
+static Lex_state *pop_state_i(State_info *, Lex_state *);
+
+static int backslash_skip;
+static int ignore_backslash_newline;
+
+/* optimised getsc_bn() */
+#define o_getsc()	(*source->str != '\0' && *source->str != '\\' && \
+			    !backslash_skip ? *source->str++ : getsc_bn())
+/* optimised getsc_uu() */
+#define	o_getsc_u()	((*source->str != '\0') ? *source->str++ : getsc_uu())
+
+/* retrace helper */
+#define o_getsc_r(carg)	{				\
+	int cev = (carg);				\
+	struct sretrace_info *rp = retrace_info;	\
+							\
+	while (rp) {					\
+		Xcheck(rp->xs, rp->xp);			\
+		*rp->xp++ = cev;			\
+		rp = rp->next;				\
+	}						\
+							\
+	return (cev);					\
+}
+
+#if defined(MKSH_SMALL) && !defined(MKSH_SMALL_BUT_FAST)
+static int getsc(void);
+
+static int
+getsc(void)
+{
+	o_getsc_r(o_getsc());
+}
+#else
+static int getsc_r(int);
+
+static int
+getsc_r(int c)
+{
+	o_getsc_r(c);
+}
+
+#define getsc()		getsc_r(o_getsc())
+#endif
+
+#define STATE_BSIZE	8
+
+#define PUSH_STATE(s)	do {					\
+	if (++statep == state_info.end)				\
+		statep = push_state_i(&state_info, statep);	\
+	state = statep->type = (s);				\
+} while (/* CONSTCOND */ 0)
+
+#define POP_STATE()	do {					\
+	if (--statep == state_info.base)			\
+		statep = pop_state_i(&state_info, statep);	\
+	state = statep->type;					\
+} while (/* CONSTCOND */ 0)
+
+#define PUSH_SRETRACE(s) do {					\
+	struct sretrace_info *ri;				\
+								\
+	PUSH_STATE(s);						\
+	statep->ls_start = Xsavepos(ws, wp);			\
+	ri = alloc(sizeof(struct sretrace_info), ATEMP);	\
+	Xinit(ri->xs, ri->xp, 64, ATEMP);			\
+	ri->next = retrace_info;				\
+	retrace_info = ri;					\
+} while (/* CONSTCOND */ 0)
+
+#define POP_SRETRACE()	do {					\
+	wp = Xrestpos(ws, wp, statep->ls_start);		\
+	*retrace_info->xp = '\0';				\
+	sp = Xstring(retrace_info->xs, retrace_info->xp);	\
+	dp = (void *)retrace_info;				\
+	retrace_info = retrace_info->next;			\
+	afree(dp, ATEMP);					\
+	POP_STATE();						\
+} while (/* CONSTCOND */ 0)
+
+/**
+ * Lexical analyser
+ *
+ * tokens are not regular expressions, they are LL(1).
+ * for example, "${var:-${PWD}}", and "$(size $(whence ksh))".
+ * hence the state stack. Note "$(...)" are now parsed recursively.
+ */
+
+int
+yylex(int cf)
+{
+	Lex_state states[STATE_BSIZE], *statep, *s2, *base;
+	State_info state_info;
+	int c, c2, state;
+	size_t cz;
+	XString ws;		/* expandable output word */
+	char *wp;		/* output word pointer */
+	char *sp, *dp;
+
+ Again:
+	states[0].type = SINVALID;
+	states[0].ls_base = NULL;
+	statep = &states[1];
+	state_info.base = states;
+	state_info.end = &state_info.base[STATE_BSIZE];
+
+	Xinit(ws, wp, 64, ATEMP);
+
+	backslash_skip = 0;
+	ignore_backslash_newline = 0;
+
+	if (cf & ONEWORD)
+		state = SWORD;
+	else if (cf & LETEXPR) {
+		/* enclose arguments in (double) quotes */
+		*wp++ = OQUOTE;
+		state = SLETPAREN;
+		statep->nparen = 0;
+	} else {
+		/* normal lexing */
+		state = (cf & HEREDELIM) ? SHEREDELIM : SBASE;
+		while ((c = getsc()) == ' ' || c == '\t')
+			;
+		if (c == '#') {
+			ignore_backslash_newline++;
+			while ((c = getsc()) != '\0' && c != '\n')
+				;
+			ignore_backslash_newline--;
+		}
+		ungetsc(c);
+	}
+	if (source->flags & SF_ALIAS) {
+		/* trailing ' ' in alias definition */
+		source->flags &= ~SF_ALIAS;
+		cf |= ALIAS;
+	}
+
+	/* Initial state: one of SWORD SLETPAREN SHEREDELIM SBASE */
+	statep->type = state;
+
+	/* check for here string */
+	if (state == SHEREDELIM) {
+		c = getsc();
+		if (c == '<') {
+			state = SHEREDELIM;
+			while ((c = getsc()) == ' ' || c == '\t')
+				;
+			ungetsc(c);
+			c = '<';
+			goto accept_nonword;
+		}
+		ungetsc(c);
+	}
+
+	/* collect non-special or quoted characters to form word */
+	while (!((c = getsc()) == 0 ||
+	    ((state == SBASE || state == SHEREDELIM) && ctype(c, C_LEX1)))) {
+		if (state == SBASE &&
+		    subshell_nesting_type == /*{*/ '}' &&
+		    c == /*{*/ '}')
+			/* possibly end ${ :;} */
+			break;
+ accept_nonword:
+		Xcheck(ws, wp);
+		switch (state) {
+		case SADELIM:
+			if (c == '(')
+				statep->nparen++;
+			else if (c == ')')
+				statep->nparen--;
+			else if (statep->nparen == 0 && (c == /*{*/ '}' ||
+			    c == (int)statep->ls_adelim.delimiter)) {
+				*wp++ = ADELIM;
+				*wp++ = c;
+				if (c == /*{*/ '}' || --statep->ls_adelim.num == 0)
+					POP_STATE();
+				if (c == /*{*/ '}')
+					POP_STATE();
+				break;
+			}
+			/* FALLTHROUGH */
+		case SBASE:
+			if (c == '[' && (cf & (VARASN|ARRAYVAR))) {
+				/* temporary */
+				*wp = EOS;
+				if (is_wdvarname(Xstring(ws, wp), false)) {
+					char *p, *tmp;
+
+					if (arraysub(&tmp)) {
+						*wp++ = CHAR;
+						*wp++ = c;
+						for (p = tmp; *p; ) {
+							Xcheck(ws, wp);
+							*wp++ = CHAR;
+							*wp++ = *p++;
+						}
+						afree(tmp, ATEMP);
+						break;
+					} else {
+						Source *s;
+
+						s = pushs(SREREAD,
+						    source->areap);
+						s->start = s->str =
+						    s->u.freeme = tmp;
+						s->next = source;
+						source = s;
+					}
+				}
+				*wp++ = CHAR;
+				*wp++ = c;
+				break;
+			}
+			/* FALLTHROUGH */
+ Sbase1:		/* includes *(...|...) pattern (*+?@!) */
+			if (c == '*' || c == '@' || c == '+' || c == '?' ||
+			    c == '!') {
+				c2 = getsc();
+				if (c2 == '(' /*)*/ ) {
+					*wp++ = OPAT;
+					*wp++ = c;
+					PUSH_STATE(SPATTERN);
+					break;
+				}
+				ungetsc(c2);
+			}
+			/* FALLTHROUGH */
+ Sbase2:		/* doesn't include *(...|...) pattern (*+?@!) */
+			switch (c) {
+			case '\\':
+ getsc_qchar:
+				if ((c = getsc())) {
+					/* trailing \ is lost */
+					*wp++ = QCHAR;
+					*wp++ = c;
+				}
+				break;
+			case '\'':
+ open_ssquote_unless_heredoc:
+				if ((cf & HEREDOC))
+					goto store_char;
+				*wp++ = OQUOTE;
+				ignore_backslash_newline++;
+				PUSH_STATE(SSQUOTE);
+				break;
+			case '"':
+ open_sdquote:
+				*wp++ = OQUOTE;
+				PUSH_STATE(SDQUOTE);
+				break;
+			case '$':
+				/*
+				 * processing of dollar sign belongs into
+				 * Subst, except for those which can open
+				 * a string: $'…' and $"…"
+				 */
+ subst_dollar_ex:
+				c = getsc();
+				switch (c) {
+				case '"':
+					goto open_sdquote;
+				case '\'':
+					goto open_sequote;
+				default:
+					goto SubstS;
+				}
+			default:
+				goto Subst;
+			}
+			break;
+
+ Subst:
+			switch (c) {
+			case '\\':
+				c = getsc();
+				switch (c) {
+				case '"':
+					if ((cf & HEREDOC))
+						goto heredocquote;
+					/* FALLTHROUGH */
+				case '\\':
+				case '$': case '`':
+ store_qchar:
+					*wp++ = QCHAR;
+					*wp++ = c;
+					break;
+				default:
+ heredocquote:
+					Xcheck(ws, wp);
+					if (c) {
+						/* trailing \ is lost */
+						*wp++ = CHAR;
+						*wp++ = '\\';
+						*wp++ = CHAR;
+						*wp++ = c;
+					}
+					break;
+				}
+				break;
+			case '$':
+				c = getsc();
+ SubstS:
+				if (c == '(') /*)*/ {
+					c = getsc();
+					if (c == '(') /*)*/ {
+						*wp++ = EXPRSUB;
+						PUSH_SRETRACE(SASPAREN);
+						statep->nparen = 2;
+						*retrace_info->xp++ = '(';
+					} else {
+						ungetsc(c);
+ subst_command:
+						c = COMSUB;
+ subst_command2:
+						sp = yyrecursive(c);
+						cz = strlen(sp) + 1;
+						XcheckN(ws, wp, cz);
+						*wp++ = c;
+						memcpy(wp, sp, cz);
+						wp += cz;
+					}
+				} else if (c == '{') /*}*/ {
+					if ((c = getsc()) == '|') {
+						/*
+						 * non-subenvironment
+						 * value substitution
+						 */
+						c = VALSUB;
+						goto subst_command2;
+					} else if (ctype(c, C_IFSWS)) {
+						/*
+						 * non-subenvironment
+						 * "command" substitution
+						 */
+						c = FUNSUB;
+						goto subst_command2;
+					}
+					ungetsc(c);
+					*wp++ = OSUBST;
+					*wp++ = '{'; /*}*/
+					wp = get_brace_var(&ws, wp);
+					c = getsc();
+					/* allow :# and :% (ksh88 compat) */
+					if (c == ':') {
+						*wp++ = CHAR;
+						*wp++ = c;
+						c = getsc();
+						if (c == ':') {
+							*wp++ = CHAR;
+							*wp++ = '0';
+							*wp++ = ADELIM;
+							*wp++ = ':';
+							PUSH_STATE(SBRACE);
+							PUSH_STATE(SADELIM);
+							statep->ls_adelim.delimiter = ':';
+							statep->ls_adelim.num = 1;
+							statep->nparen = 0;
+							break;
+						} else if (ksh_isdigit(c) ||
+						    c == '('/*)*/ || c == ' ' ||
+						    /*XXX what else? */
+						    c == '$') {
+							/* substring subst. */
+							if (c != ' ') {
+								*wp++ = CHAR;
+								*wp++ = ' ';
+							}
+							ungetsc(c);
+							PUSH_STATE(SBRACE);
+							PUSH_STATE(SADELIM);
+							statep->ls_adelim.delimiter = ':';
+							statep->ls_adelim.num = 2;
+							statep->nparen = 0;
+							break;
+						}
+					} else if (c == '/') {
+						*wp++ = CHAR;
+						*wp++ = c;
+						if ((c = getsc()) == '/') {
+							*wp++ = ADELIM;
+							*wp++ = c;
+						} else
+							ungetsc(c);
+						PUSH_STATE(SBRACE);
+						PUSH_STATE(SADELIM);
+						statep->ls_adelim.delimiter = '/';
+						statep->ls_adelim.num = 1;
+						statep->nparen = 0;
+						break;
+					}
+					/*
+					 * If this is a trim operation,
+					 * treat (,|,) specially in STBRACE.
+					 */
+					if (ctype(c, C_SUBOP2)) {
+						ungetsc(c);
+						if (Flag(FSH))
+							PUSH_STATE(STBRACEBOURNE);
+						else
+							PUSH_STATE(STBRACEKORN);
+					} else {
+						ungetsc(c);
+						if (state == SDQUOTE ||
+						    state == SQBRACE)
+							PUSH_STATE(SQBRACE);
+						else
+							PUSH_STATE(SBRACE);
+					}
+				} else if (ksh_isalphx(c)) {
+					*wp++ = OSUBST;
+					*wp++ = 'X';
+					do {
+						Xcheck(ws, wp);
+						*wp++ = c;
+						c = getsc();
+					} while (ksh_isalnux(c));
+					*wp++ = '\0';
+					*wp++ = CSUBST;
+					*wp++ = 'X';
+					ungetsc(c);
+				} else if (ctype(c, C_VAR1 | C_DIGIT)) {
+					Xcheck(ws, wp);
+					*wp++ = OSUBST;
+					*wp++ = 'X';
+					*wp++ = c;
+					*wp++ = '\0';
+					*wp++ = CSUBST;
+					*wp++ = 'X';
+				} else {
+					*wp++ = CHAR;
+					*wp++ = '$';
+					ungetsc(c);
+				}
+				break;
+			case '`':
+ subst_gravis:
+				PUSH_STATE(SBQUOTE);
+				*wp++ = COMSUB;
+				/*
+				 * Need to know if we are inside double quotes
+				 * since sh/AT&T-ksh translate the \" to " in
+				 * "`...\"...`".
+				 * This is not done in POSIX mode (section
+				 * 3.2.3, Double Quotes: "The backquote shall
+				 * retain its special meaning introducing the
+				 * other form of command substitution (see
+				 * 3.6.3). The portion of the quoted string
+				 * from the initial backquote and the
+				 * characters up to the next backquote that
+				 * is not preceded by a backslash (having
+				 * escape characters removed) defines that
+				 * command whose output replaces `...` when
+				 * the word is expanded."
+				 * Section 3.6.3, Command Substitution:
+				 * "Within the backquoted style of command
+				 * substitution, backslash shall retain its
+				 * literal meaning, except when followed by
+				 * $ ` \.").
+				 */
+				statep->ls_bool = false;
+				s2 = statep;
+				base = state_info.base;
+				while (/* CONSTCOND */ 1) {
+					for (; s2 != base; s2--) {
+						if (s2->type == SDQUOTE) {
+							statep->ls_bool = true;
+							break;
+						}
+					}
+					if (s2 != base)
+						break;
+					if (!(s2 = s2->ls_base))
+						break;
+					base = s2-- - STATE_BSIZE;
+				}
+				break;
+			case QCHAR:
+				if (cf & LQCHAR) {
+					*wp++ = QCHAR;
+					*wp++ = getsc();
+					break;
+				}
+				/* FALLTHROUGH */
+			default:
+ store_char:
+				*wp++ = CHAR;
+				*wp++ = c;
+			}
+			break;
+
+		case SEQUOTE:
+			if (c == '\'') {
+				POP_STATE();
+				*wp++ = CQUOTE;
+				ignore_backslash_newline--;
+			} else if (c == '\\') {
+				if ((c2 = unbksl(true, s_get, s_put)) == -1)
+					c2 = s_get();
+				if (c2 == 0)
+					statep->ls_bool = true;
+				if (!statep->ls_bool) {
+					char ts[4];
+
+					if ((unsigned int)c2 < 0x100) {
+						*wp++ = QCHAR;
+						*wp++ = c2;
+					} else {
+						cz = utf_wctomb(ts, c2 - 0x100);
+						ts[cz] = 0;
+						for (cz = 0; ts[cz]; ++cz) {
+							*wp++ = QCHAR;
+							*wp++ = ts[cz];
+						}
+					}
+				}
+			} else if (!statep->ls_bool) {
+				*wp++ = QCHAR;
+				*wp++ = c;
+			}
+			break;
+
+		case SSQUOTE:
+			if (c == '\'') {
+				POP_STATE();
+				if ((cf & HEREDOC) || state == SQBRACE)
+					goto store_char;
+				*wp++ = CQUOTE;
+				ignore_backslash_newline--;
+			} else {
+				*wp++ = QCHAR;
+				*wp++ = c;
+			}
+			break;
+
+		case SDQUOTE:
+			if (c == '"') {
+				POP_STATE();
+				*wp++ = CQUOTE;
+			} else
+				goto Subst;
+			break;
+
+		/* $(( ... )) */
+		case SASPAREN:
+			if (c == '(')
+				statep->nparen++;
+			else if (c == ')') {
+				statep->nparen--;
+				if (statep->nparen == 1) {
+					/* end of EXPRSUB */
+					POP_SRETRACE();
+
+					if ((c2 = getsc()) == /*(*/ ')') {
+						cz = strlen(sp) - 2;
+						XcheckN(ws, wp, cz);
+						memcpy(wp, sp + 1, cz);
+						wp += cz;
+						afree(sp, ATEMP);
+						*wp++ = '\0';
+						break;
+					} else {
+						Source *s;
+
+						ungetsc(c2);
+						/*
+						 * mismatched parenthesis -
+						 * assume we were really
+						 * parsing a $(...) expression
+						 */
+						--wp;
+						s = pushs(SREREAD,
+						    source->areap);
+						s->start = s->str =
+						    s->u.freeme = sp;
+						s->next = source;
+						source = s;
+						goto subst_command;
+					}
+				}
+			}
+			/* reuse existing state machine */
+			goto Sbase2;
+
+		case SQBRACE:
+			if (c == '\\') {
+				/*
+				 * perform POSIX "quote removal" if the back-
+				 * slash is "special", i.e. same cases as the
+				 * {case '\\':} in Subst: plus closing brace;
+				 * in mksh code "quote removal" on '\c' means
+				 * write QCHAR+c, otherwise CHAR+\+CHAR+c are
+				 * emitted (in heredocquote:)
+				 */
+				if ((c = getsc()) == '"' || c == '\\' ||
+				    c == '$' || c == '`' || c == /*{*/'}')
+					goto store_qchar;
+				goto heredocquote;
+			}
+			goto common_SQBRACE;
+
+		case SBRACE:
+			if (c == '\'')
+				goto open_ssquote_unless_heredoc;
+			else if (c == '\\')
+				goto getsc_qchar;
+ common_SQBRACE:
+			if (c == '"')
+				goto open_sdquote;
+			else if (c == '$')
+				goto subst_dollar_ex;
+			else if (c == '`')
+				goto subst_gravis;
+			else if (c != /*{*/ '}')
+				goto store_char;
+			POP_STATE();
+			*wp++ = CSUBST;
+			*wp++ = /*{*/ '}';
+			break;
+
+		/* Same as SBASE, except (,|,) treated specially */
+		case STBRACEKORN:
+			if (c == '|')
+				*wp++ = SPAT;
+			else if (c == '(') {
+				*wp++ = OPAT;
+				/* simile for @ */
+				*wp++ = ' ';
+				PUSH_STATE(SPATTERN);
+			} else /* FALLTHROUGH */
+		case STBRACEBOURNE:
+			  if (c == /*{*/ '}') {
+				POP_STATE();
+				*wp++ = CSUBST;
+				*wp++ = /*{*/ '}';
+			} else
+				goto Sbase1;
+			break;
+
+		case SBQUOTE:
+			if (c == '`') {
+				*wp++ = 0;
+				POP_STATE();
+			} else if (c == '\\') {
+				switch (c = getsc()) {
+				case 0:
+					/* trailing \ is lost */
+					break;
+				case '\\':
+				case '$': case '`':
+					*wp++ = c;
+					break;
+				case '"':
+					if (statep->ls_bool) {
+						*wp++ = c;
+						break;
+					}
+					/* FALLTHROUGH */
+				default:
+					*wp++ = '\\';
+					*wp++ = c;
+					break;
+				}
+			} else
+				*wp++ = c;
+			break;
+
+		/* ONEWORD */
+		case SWORD:
+			goto Subst;
+
+		/* LETEXPR: (( ... )) */
+		case SLETPAREN:
+			if (c == /*(*/ ')') {
+				if (statep->nparen > 0)
+					--statep->nparen;
+				else if ((c2 = getsc()) == /*(*/ ')') {
+					c = 0;
+					*wp++ = CQUOTE;
+					goto Done;
+				} else {
+					Source *s;
+
+					ungetsc(c2);
+					/*
+					 * mismatched parenthesis -
+					 * assume we were really
+					 * parsing a (...) expression
+					 */
+					*wp = EOS;
+					sp = Xstring(ws, wp);
+					dp = wdstrip(sp, WDS_KEEPQ);
+					s = pushs(SREREAD, source->areap);
+					s->start = s->str = s->u.freeme = dp;
+					s->next = source;
+					source = s;
+					return ('('/*)*/);
+				}
+			} else if (c == '(')
+				/*
+				 * parentheses inside quotes and
+				 * backslashes are lost, but AT&T ksh
+				 * doesn't count them either
+				 */
+				++statep->nparen;
+			goto Sbase2;
+
+		/* <<, <<-, <<< delimiter */
+		case SHEREDELIM:
+			/*
+			 * here delimiters need a special case since
+			 * $ and `...` are not to be treated specially
+			 */
+			switch (c) {
+			case '\\':
+				if ((c = getsc())) {
+					/* trailing \ is lost */
+					*wp++ = QCHAR;
+					*wp++ = c;
+				}
+				break;
+			case '\'':
+				goto open_ssquote_unless_heredoc;
+			case '$':
+				if ((c2 = getsc()) == '\'') {
+ open_sequote:
+					*wp++ = OQUOTE;
+					ignore_backslash_newline++;
+					PUSH_STATE(SEQUOTE);
+					statep->ls_bool = false;
+					break;
+				} else if (c2 == '"') {
+					/* FALLTHROUGH */
+			case '"':
+					PUSH_SRETRACE(SHEREDQUOTE);
+					break;
+				}
+				ungetsc(c2);
+				/* FALLTHROUGH */
+			default:
+				*wp++ = CHAR;
+				*wp++ = c;
+			}
+			break;
+
+		/* " in <<, <<-, <<< delimiter */
+		case SHEREDQUOTE:
+			if (c != '"')
+				goto Subst;
+			POP_SRETRACE();
+			dp = strnul(sp) - 1;
+			/* remove the trailing double quote */
+			*dp = '\0';
+			/* store the quoted string */
+			*wp++ = OQUOTE;
+			XcheckN(ws, wp, (dp - sp));
+			dp = sp;
+			while ((c = *dp++)) {
+				if (c == '\\') {
+					switch ((c = *dp++)) {
+					case '\\':
+					case '"':
+					case '$':
+					case '`':
+						break;
+					default:
+						*wp++ = CHAR;
+						*wp++ = '\\';
+						break;
+					}
+				}
+				*wp++ = CHAR;
+				*wp++ = c;
+			}
+			afree(sp, ATEMP);
+			*wp++ = CQUOTE;
+			state = statep->type = SHEREDELIM;
+			break;
+
+		/* in *(...|...) pattern (*+?@!) */
+		case SPATTERN:
+			if (c == /*(*/ ')') {
+				*wp++ = CPAT;
+				POP_STATE();
+			} else if (c == '|') {
+				*wp++ = SPAT;
+			} else if (c == '(') {
+				*wp++ = OPAT;
+				/* simile for @ */
+				*wp++ = ' ';
+				PUSH_STATE(SPATTERN);
+			} else
+				goto Sbase1;
+			break;
+		}
+	}
+ Done:
+	Xcheck(ws, wp);
+	if (statep != &states[1])
+		/* XXX figure out what is missing */
+		yyerror("no closing quote\n");
+
+	/* This done to avoid tests for SHEREDELIM wherever SBASE tested */
+	if (state == SHEREDELIM)
+		state = SBASE;
+
+	dp = Xstring(ws, wp);
+	if (state == SBASE && (
+#ifndef MKSH_LEGACY_MODE
+	    (c == '&' && !Flag(FSH) && !Flag(FPOSIX)) ||
+#endif
+	    c == '<' || c == '>')) {
+		struct ioword *iop = alloc(sizeof(struct ioword), ATEMP);
+
+		if (Xlength(ws, wp) == 0)
+			iop->unit = c == '<' ? 0 : 1;
+		else for (iop->unit = 0, c2 = 0; c2 < Xlength(ws, wp); c2 += 2) {
+			if (dp[c2] != CHAR)
+				goto no_iop;
+			if (!ksh_isdigit(dp[c2 + 1]))
+				goto no_iop;
+			iop->unit = (iop->unit * 10) + dp[c2 + 1] - '0';
+		}
+
+		if (iop->unit >= FDBASE)
+			goto no_iop;
+
+		if (c == '&') {
+			if ((c2 = getsc()) != '>') {
+				ungetsc(c2);
+				goto no_iop;
+			}
+			c = c2;
+			iop->flag = IOBASH;
+		} else
+			iop->flag = 0;
+
+		c2 = getsc();
+		/* <<, >>, <> are ok, >< is not */
+		if (c == c2 || (c == '<' && c2 == '>')) {
+			iop->flag |= c == c2 ?
+			    (c == '>' ? IOCAT : IOHERE) : IORDWR;
+			if (iop->flag == IOHERE) {
+				if ((c2 = getsc()) == '-') {
+					iop->flag |= IOSKIP;
+					c2 = getsc();
+				} else if (c2 == '<')
+					iop->flag |= IOHERESTR;
+				ungetsc(c2);
+				if (c2 == '\n')
+					iop->flag |= IONDELIM;
+			}
+		} else if (c2 == '&')
+			iop->flag |= IODUP | (c == '<' ? IORDUP : 0);
+		else {
+			iop->flag |= c == '>' ? IOWRITE : IOREAD;
+			if (c == '>' && c2 == '|')
+				iop->flag |= IOCLOB;
+			else
+				ungetsc(c2);
+		}
+
+		iop->name = NULL;
+		iop->delim = NULL;
+		iop->heredoc = NULL;
+		/* free word */
+		Xfree(ws, wp);
+		yylval.iop = iop;
+		return (REDIR);
+ no_iop:
+		afree(iop, ATEMP);
+	}
+
+	if (wp == dp && state == SBASE) {
+		/* free word */
+		Xfree(ws, wp);
+		/* no word, process LEX1 character */
+		if ((c == '|') || (c == '&') || (c == ';') || (c == '('/*)*/)) {
+			if ((c2 = getsc()) == c)
+				c = (c == ';') ? BREAK :
+				    (c == '|') ? LOGOR :
+				    (c == '&') ? LOGAND :
+				    /* c == '(' ) */ MDPAREN;
+			else if (c == '|' && c2 == '&')
+				c = COPROC;
+			else if (c == ';' && c2 == '|')
+				c = BRKEV;
+			else if (c == ';' && c2 == '&')
+				c = BRKFT;
+			else
+				ungetsc(c2);
+#ifndef MKSH_SMALL
+			if (c == BREAK) {
+				if ((c2 = getsc()) == '&')
+					c = BRKEV;
+				else
+					ungetsc(c2);
+			}
+#endif
+		} else if (c == '\n') {
+			gethere(false);
+			if (cf & CONTIN)
+				goto Again;
+		} else if (c == '\0')
+			/* need here strings at EOF */
+			gethere(true);
+		return (c);
+	}
+
+	/* terminate word */
+	*wp++ = EOS;
+	yylval.cp = Xclose(ws, wp);
+	if (state == SWORD || state == SLETPAREN
+	    /* XXX ONEWORD? */)
+		return (LWORD);
+
+	/* unget terminator */
+	ungetsc(c);
+
+	/*
+	 * note: the alias-vs-function code below depends on several
+	 * interna: starting from here, source->str is not modified;
+	 * the way getsc() and ungetsc() operate; etc.
+	 */
+
+	/* copy word to unprefixed string ident */
+	sp = yylval.cp;
+	dp = ident;
+	if ((cf & HEREDELIM) && (sp[1] == '<'))
+		while ((dp - ident) < IDENT) {
+			if ((c = *sp++) == CHAR)
+				*dp++ = *sp++;
+			else if ((c != OQUOTE) && (c != CQUOTE))
+				break;
+		}
+	else
+		while ((dp - ident) < IDENT && (c = *sp++) == CHAR)
+			*dp++ = *sp++;
+	/* Make sure the ident array stays '\0' padded */
+	memset(dp, 0, (ident + IDENT) - dp + 1);
+	if (c != EOS)
+		/* word is not unquoted */
+		*ident = '\0';
+
+	if (*ident != '\0' && (cf & (KEYWORD | ALIAS))) {
+		struct tbl *p;
+		uint32_t h = hash(ident);
+
+		if ((cf & KEYWORD) && (p = ktsearch(&keywords, ident, h)) &&
+		    (!(cf & ESACONLY) || p->val.i == ESAC ||
+		    p->val.i == /*{*/ '}')) {
+			afree(yylval.cp, ATEMP);
+			return (p->val.i);
+		}
+		if ((cf & ALIAS) && (p = ktsearch(&aliases, ident, h)) &&
+		    (p->flag & ISSET)) {
+			/*
+			 * this still points to the same character as the
+			 * ungetsc'd terminator from above
+			 */
+			const char *cp = source->str;
+
+			/* prefer POSIX but not Korn functions over aliases */
+			while (*cp == ' ' || *cp == '\t')
+				/*
+				 * this is like getsc() without skipping
+				 * over Source boundaries (including not
+				 * parsing ungetsc'd characters that got
+				 * pushed into an SREREAD) which is what
+				 * we want here anyway: find out whether
+				 * the alias name is followed by a POSIX
+				 * function definition (only the opening
+				 * parenthesis is checked though)
+				 */
+				++cp;
+			/* prefer functions over aliases */
+			if (cp[0] != '(' || cp[1] != ')') {
+				Source *s = source;
+
+				while (s && (s->flags & SF_HASALIAS))
+					if (s->u.tblp == p)
+						return (LWORD);
+					else
+						s = s->next;
+				/* push alias expansion */
+				s = pushs(SALIAS, source->areap);
+				s->start = s->str = p->val.s;
+				s->u.tblp = p;
+				s->flags |= SF_HASALIAS;
+				s->next = source;
+				if (source->type == SEOF) {
+					/* prevent infinite recursion at EOS */
+					source->u.tblp = p;
+					source->flags |= SF_HASALIAS;
+				}
+				source = s;
+				afree(yylval.cp, ATEMP);
+				goto Again;
+			}
+		}
+	}
+
+	return (LWORD);
+}
+
+static void
+gethere(bool iseof)
+{
+	struct ioword **p;
+
+	for (p = heres; p < herep; p++)
+		if (iseof && !((*p)->flag & IOHERESTR))
+			/* only here strings at EOF */
+			return;
+		else
+			readhere(*p);
+	herep = heres;
+}
+
+/*
+ * read "<<word" text into temp file
+ */
+
+static void
+readhere(struct ioword *iop)
+{
+	int c;
+	const char *eof, *eofp;
+	XString xs;
+	char *xp;
+	int xpos;
+
+	if (iop->flag & IOHERESTR) {
+		/* process the here string */
+		iop->heredoc = xp = evalstr(iop->delim, DOBLANK);
+		xpos = strlen(xp) - 1;
+		memmove(xp, xp + 1, xpos);
+		xp[xpos] = '\n';
+		return;
+	}
+
+	eof = iop->flag & IONDELIM ? "<<" : evalstr(iop->delim, 0);
+
+	if (!(iop->flag & IOEVAL))
+		ignore_backslash_newline++;
+
+	Xinit(xs, xp, 256, ATEMP);
+
+ heredoc_read_line:
+	/* beginning of line */
+	eofp = eof;
+	xpos = Xsavepos(xs, xp);
+	if (iop->flag & IOSKIP) {
+		/* skip over leading tabs */
+		while ((c = getsc()) == '\t')
+			/* nothing */;
+		goto heredoc_parse_char;
+	}
+ heredoc_read_char:
+	c = getsc();
+ heredoc_parse_char:
+	/* compare with here document marker */
+	if (!*eofp) {
+		/* end of here document marker, what to do? */
+		switch (c) {
+		case /*(*/ ')':
+			if (!subshell_nesting_type)
+				/*-
+				 * not allowed outside $(...) or (...)
+				 * => mismatch
+				 */
+				break;
+			/* allow $(...) or (...) to close here */
+			ungetsc(/*(*/ ')');
+			/* FALLTHROUGH */
+		case 0:
+			/*
+			 * Allow EOF here to commands without trailing
+			 * newlines (mksh -c '...') will work as well.
+			 */
+		case '\n':
+			/* Newline terminates here document marker */
+			goto heredoc_found_terminator;
+		}
+	} else if (c == *eofp++)
+		/* store; then read and compare next character */
+		goto heredoc_store_and_loop;
+	/* nope, mismatch; read until end of line */
+	while (c != '\n') {
+		if (!c)
+			/* oops, reached EOF */
+			yyerror("%s '%s' unclosed\n", "here document", eof);
+		/* store character */
+		Xcheck(xs, xp);
+		Xput(xs, xp, c);
+		/* read next character */
+		c = getsc();
+	}
+	/* we read a newline as last character */
+ heredoc_store_and_loop:
+	/* store character */
+	Xcheck(xs, xp);
+	Xput(xs, xp, c);
+	if (c == '\n')
+		goto heredoc_read_line;
+	goto heredoc_read_char;
+
+ heredoc_found_terminator:
+	/* jump back to saved beginning of line */
+	xp = Xrestpos(xs, xp, xpos);
+	/* terminate, close and store */
+	Xput(xs, xp, '\0');
+	iop->heredoc = Xclose(xs, xp);
+
+	if (!(iop->flag & IOEVAL))
+		ignore_backslash_newline--;
+}
+
+void
+yyerror(const char *fmt, ...)
+{
+	va_list va;
+
+	/* pop aliases and re-reads */
+	while (source->type == SALIAS || source->type == SREREAD)
+		source = source->next;
+	/* zap pending input */
+	source->str = null;
+
+	error_prefix(true);
+	va_start(va, fmt);
+	shf_vfprintf(shl_out, fmt, va);
+	va_end(va);
+	errorfz();
+}
+
+/*
+ * input for yylex with alias expansion
+ */
+
+Source *
+pushs(int type, Area *areap)
+{
+	Source *s;
+
+	s = alloc(sizeof(Source), areap);
+	memset(s, 0, sizeof(Source));
+	s->type = type;
+	s->str = null;
+	s->areap = areap;
+	if (type == SFILE || type == SSTDIN)
+		XinitN(s->xs, 256, s->areap);
+	return (s);
+}
+
+static int
+getsc_uu(void)
+{
+	Source *s = source;
+	int c;
+
+	while ((c = *s->str++) == 0) {
+		/* return 0 for EOF by default */
+		s->str = NULL;
+		switch (s->type) {
+		case SEOF:
+			s->str = null;
+			return (0);
+
+		case SSTDIN:
+		case SFILE:
+			getsc_line(s);
+			break;
+
+		case SWSTR:
+			break;
+
+		case SSTRING:
+		case SSTRINGCMDLINE:
+			break;
+
+		case SWORDS:
+			s->start = s->str = *s->u.strv++;
+			s->type = SWORDSEP;
+			break;
+
+		case SWORDSEP:
+			if (*s->u.strv == NULL) {
+				s->start = s->str = "\n";
+				s->type = SEOF;
+			} else {
+				s->start = s->str = " ";
+				s->type = SWORDS;
+			}
+			break;
+
+		case SALIAS:
+			if (s->flags & SF_ALIASEND) {
+				/* pass on an unused SF_ALIAS flag */
+				source = s->next;
+				source->flags |= s->flags & SF_ALIAS;
+				s = source;
+			} else if (*s->u.tblp->val.s &&
+			    (c = strnul(s->u.tblp->val.s)[-1], ksh_isspace(c))) {
+				/* pop source stack */
+				source = s = s->next;
+				/*
+				 * Note that this alias ended with a
+				 * space, enabling alias expansion on
+				 * the following word.
+				 */
+				s->flags |= SF_ALIAS;
+			} else {
+				/*
+				 * At this point, we need to keep the current
+				 * alias in the source list so recursive
+				 * aliases can be detected and we also need to
+				 * return the next character. Do this by
+				 * temporarily popping the alias to get the
+				 * next character and then put it back in the
+				 * source list with the SF_ALIASEND flag set.
+				 */
+				/* pop source stack */
+				source = s->next;
+				source->flags |= s->flags & SF_ALIAS;
+				c = getsc_uu();
+				if (c) {
+					s->flags |= SF_ALIASEND;
+					s->ugbuf[0] = c; s->ugbuf[1] = '\0';
+					s->start = s->str = s->ugbuf;
+					s->next = source;
+					source = s;
+				} else {
+					s = source;
+					/* avoid reading EOF twice */
+					s->str = NULL;
+					break;
+				}
+			}
+			continue;
+
+		case SREREAD:
+			if (s->start != s->ugbuf)
+				/* yuck */
+				afree(s->u.freeme, ATEMP);
+			source = s = s->next;
+			continue;
+		}
+		if (s->str == NULL) {
+			s->type = SEOF;
+			s->start = s->str = null;
+			return ('\0');
+		}
+		if (s->flags & SF_ECHO) {
+			shf_puts(s->str, shl_out);
+			shf_flush(shl_out);
+		}
+	}
+	return (c);
+}
+
+static void
+getsc_line(Source *s)
+{
+	char *xp = Xstring(s->xs, xp), *cp;
+	bool interactive = Flag(FTALKING) && s->type == SSTDIN;
+	bool have_tty = tobool(interactive && (s->flags & SF_TTY));
+
+	/* Done here to ensure nothing odd happens when a timeout occurs */
+	XcheckN(s->xs, xp, LINE);
+	*xp = '\0';
+	s->start = s->str = xp;
+
+	if (have_tty && ksh_tmout) {
+		ksh_tmout_state = TMOUT_READING;
+		alarm(ksh_tmout);
+	}
+	if (interactive)
+		change_winsz();
+#ifndef MKSH_NO_CMDLINE_EDITING
+	if (have_tty && (
+#if !MKSH_S_NOVI
+	    Flag(FVI) ||
+#endif
+	    Flag(FEMACS) || Flag(FGMACS))) {
+		int nread;
+
+		nread = x_read(xp);
+		if (nread < 0)
+			/* read error */
+			nread = 0;
+		xp[nread] = '\0';
+		xp += nread;
+	} else
+#endif
+	  {
+		if (interactive)
+			pprompt(prompt, 0);
+		else
+			s->line++;
+
+		while (/* CONSTCOND */ 1) {
+			char *p = shf_getse(xp, Xnleft(s->xs, xp), s->u.shf);
+
+			if (!p && shf_error(s->u.shf) &&
+			    shf_errno(s->u.shf) == EINTR) {
+				shf_clearerr(s->u.shf);
+				if (trap)
+					runtraps(0);
+				continue;
+			}
+			if (!p || (xp = p, xp[-1] == '\n'))
+				break;
+			/* double buffer size */
+			/* move past NUL so doubling works... */
+			xp++;
+			XcheckN(s->xs, xp, Xlength(s->xs, xp));
+			/* ...and move back again */
+			xp--;
+		}
+		/*
+		 * flush any unwanted input so other programs/builtins
+		 * can read it. Not very optimal, but less error prone
+		 * than flushing else where, dealing with redirections,
+		 * etc.
+		 * TODO: reduce size of shf buffer (~128?) if SSTDIN
+		 */
+		if (s->type == SSTDIN)
+			shf_flush(s->u.shf);
+	}
+	/*
+	 * XXX: temporary kludge to restore source after a
+	 * trap may have been executed.
+	 */
+	source = s;
+	if (have_tty && ksh_tmout) {
+		ksh_tmout_state = TMOUT_EXECUTING;
+		alarm(0);
+	}
+	cp = Xstring(s->xs, xp);
+	rndpush(cp);
+	s->start = s->str = cp;
+	strip_nuls(Xstring(s->xs, xp), Xlength(s->xs, xp));
+	/* Note: if input is all nulls, this is not eof */
+	if (Xlength(s->xs, xp) == 0) {
+		/* EOF */
+		if (s->type == SFILE)
+			shf_fdclose(s->u.shf);
+		s->str = NULL;
+	} else if (interactive && *s->str &&
+	    (cur_prompt != PS1 || !ctype(*s->str, C_IFS | C_IFSWS))) {
+		histsave(&s->line, s->str, true, true);
+#if !defined(MKSH_SMALL) && HAVE_PERSISTENT_HISTORY
+	} else if (interactive && cur_prompt == PS1) {
+		cp = Xstring(s->xs, xp);
+		while (*cp && ctype(*cp, C_IFSWS))
+			++cp;
+		if (!*cp)
+			histsync();
+#endif
+	}
+	if (interactive)
+		set_prompt(PS2, NULL);
+}
+
+void
+set_prompt(int to, Source *s)
+{
+	cur_prompt = to;
+
+	switch (to) {
+	/* command */
+	case PS1:
+		/*
+		 * Substitute ! and !! here, before substitutions are done
+		 * so ! in expanded variables are not expanded.
+		 * NOTE: this is not what AT&T ksh does (it does it after
+		 * substitutions, POSIX doesn't say which is to be done.
+		 */
+		{
+			struct shf *shf;
+			char * volatile ps1;
+			Area *saved_atemp;
+
+			ps1 = str_val(global("PS1"));
+			shf = shf_sopen(NULL, strlen(ps1) * 2,
+			    SHF_WR | SHF_DYNAMIC, NULL);
+			while (*ps1)
+				if (*ps1 != '!' || *++ps1 == '!')
+					shf_putchar(*ps1++, shf);
+				else
+					shf_fprintf(shf, "%d",
+						s ? s->line + 1 : 0);
+			ps1 = shf_sclose(shf);
+			saved_atemp = ATEMP;
+			newenv(E_ERRH);
+			if (kshsetjmp(e->jbuf)) {
+				prompt = safe_prompt;
+				/*
+				 * Don't print an error - assume it has already
+				 * been printed. Reason is we may have forked
+				 * to run a command and the child may be
+				 * unwinding its stack through this code as it
+				 * exits.
+				 */
+			} else {
+				char *cp = substitute(ps1, 0);
+				strdupx(prompt, cp, saved_atemp);
+			}
+			quitenv(NULL);
+		}
+		break;
+	/* command continuation */
+	case PS2:
+		prompt = str_val(global("PS2"));
+		break;
+	}
+}
+
+int
+pprompt(const char *cp, int ntruncate)
+{
+	char delimiter = 0;
+	bool doprint = (ntruncate != -1);
+	bool indelimit = false;
+	int columns = 0, lines = 0;
+
+	/*
+	 * Undocumented AT&T ksh feature:
+	 * If the second char in the prompt string is \r then the first
+	 * char is taken to be a non-printing delimiter and any chars
+	 * between two instances of the delimiter are not considered to
+	 * be part of the prompt length
+	 */
+	if (*cp && cp[1] == '\r') {
+		delimiter = *cp;
+		cp += 2;
+	}
+	for (; *cp; cp++) {
+		if (indelimit && *cp != delimiter)
+			;
+		else if (*cp == '\n' || *cp == '\r') {
+			lines += columns / x_cols + ((*cp == '\n') ? 1 : 0);
+			columns = 0;
+		} else if (*cp == '\t') {
+			columns = (columns | 7) + 1;
+		} else if (*cp == '\b') {
+			if (columns > 0)
+				columns--;
+		} else if (*cp == delimiter)
+			indelimit = !indelimit;
+		else if (UTFMODE && ((unsigned char)*cp > 0x7F)) {
+			const char *cp2;
+			columns += utf_widthadj(cp, &cp2);
+			if (doprint && (indelimit ||
+			    (ntruncate < (x_cols * lines + columns))))
+				shf_write(cp, cp2 - cp, shl_out);
+			cp = cp2 - /* loop increment */ 1;
+			continue;
+		} else
+			columns++;
+		if (doprint && (*cp != delimiter) &&
+		    (indelimit || (ntruncate < (x_cols * lines + columns))))
+			shf_putc(*cp, shl_out);
+	}
+	if (doprint)
+		shf_flush(shl_out);
+	return (x_cols * lines + columns);
+}
+
+/*
+ * Read the variable part of a ${...} expression (i.e. up to but not
+ * including the :[-+?=#%] or close-brace).
+ */
+static char *
+get_brace_var(XString *wsp, char *wp)
+{
+	char c;
+	enum parse_state {
+		PS_INITIAL, PS_SAW_HASH, PS_IDENT,
+		PS_NUMBER, PS_VAR1
+	} state = PS_INITIAL;
+
+	while (/* CONSTCOND */ 1) {
+		c = getsc();
+		/* State machine to figure out where the variable part ends. */
+		switch (state) {
+		case PS_INITIAL:
+			if (c == '#' || c == '!' || c == '%') {
+				state = PS_SAW_HASH;
+				break;
+			}
+			/* FALLTHROUGH */
+		case PS_SAW_HASH:
+			if (ksh_isalphx(c))
+				state = PS_IDENT;
+			else if (ksh_isdigit(c))
+				state = PS_NUMBER;
+			else if (c == '#') {
+				if (state == PS_SAW_HASH) {
+					char c2;
+
+					c2 = getsc();
+					ungetsc(c2);
+					if (c2 != /*{*/ '}') {
+						ungetsc(c);
+						goto out;
+					}
+				}
+				state = PS_VAR1;
+			} else if (ctype(c, C_VAR1))
+				state = PS_VAR1;
+			else
+				goto out;
+			break;
+		case PS_IDENT:
+			if (!ksh_isalnux(c)) {
+				if (c == '[') {
+					char *tmp, *p;
+
+					if (!arraysub(&tmp))
+						yyerror("missing ]\n");
+					*wp++ = c;
+					for (p = tmp; *p; ) {
+						Xcheck(*wsp, wp);
+						*wp++ = *p++;
+					}
+					afree(tmp, ATEMP);
+					/* the ] */
+					c = getsc();
+				}
+				goto out;
+			}
+			break;
+		case PS_NUMBER:
+			if (!ksh_isdigit(c))
+				goto out;
+			break;
+		case PS_VAR1:
+			goto out;
+		}
+		Xcheck(*wsp, wp);
+		*wp++ = c;
+	}
+ out:
+	/* end of variable part */
+	*wp++ = '\0';
+	ungetsc(c);
+	return (wp);
+}
+
+/*
+ * Save an array subscript - returns true if matching bracket found, false
+ * if eof or newline was found.
+ * (Returned string double null terminated)
+ */
+static bool
+arraysub(char **strp)
+{
+	XString ws;
+	char *wp, c;
+	/* we are just past the initial [ */
+	unsigned int depth = 1;
+
+	Xinit(ws, wp, 32, ATEMP);
+
+	do {
+		c = getsc();
+		Xcheck(ws, wp);
+		*wp++ = c;
+		if (c == '[')
+			depth++;
+		else if (c == ']')
+			depth--;
+	} while (depth > 0 && c && c != '\n');
+
+	*wp++ = '\0';
+	*strp = Xclose(ws, wp);
+
+	return (tobool(depth == 0));
+}
+
+/* Unget a char: handles case when we are already at the start of the buffer */
+static void
+ungetsc(int c)
+{
+	struct sretrace_info *rp = retrace_info;
+
+	if (backslash_skip)
+		backslash_skip--;
+	/* Don't unget EOF... */
+	if (source->str == null && c == '\0')
+		return;
+	while (rp) {
+		if (Xlength(rp->xs, rp->xp))
+			rp->xp--;
+		rp = rp->next;
+	}
+	ungetsc_i(c);
+}
+static void
+ungetsc_i(int c)
+{
+	if (source->str > source->start)
+		source->str--;
+	else {
+		Source *s;
+
+		s = pushs(SREREAD, source->areap);
+		s->ugbuf[0] = c; s->ugbuf[1] = '\0';
+		s->start = s->str = s->ugbuf;
+		s->next = source;
+		source = s;
+	}
+}
+
+
+/* Called to get a char that isn't a \newline sequence. */
+static int
+getsc_bn(void)
+{
+	int c, c2;
+
+	if (ignore_backslash_newline)
+		return (o_getsc_u());
+
+	if (backslash_skip == 1) {
+		backslash_skip = 2;
+		return (o_getsc_u());
+	}
+
+	backslash_skip = 0;
+
+	while (/* CONSTCOND */ 1) {
+		c = o_getsc_u();
+		if (c == '\\') {
+			if ((c2 = o_getsc_u()) == '\n')
+				/* ignore the \newline; get the next char... */
+				continue;
+			ungetsc_i(c2);
+			backslash_skip = 1;
+		}
+		return (c);
+	}
+}
+
+void
+yyskiputf8bom(void)
+{
+	int c;
+
+	if ((unsigned char)(c = o_getsc_u()) != 0xEF) {
+		ungetsc_i(c);
+		return;
+	}
+	if ((unsigned char)(c = o_getsc_u()) != 0xBB) {
+		ungetsc_i(c);
+		ungetsc_i(0xEF);
+		return;
+	}
+	if ((unsigned char)(c = o_getsc_u()) != 0xBF) {
+		ungetsc_i(c);
+		ungetsc_i(0xBB);
+		ungetsc_i(0xEF);
+		return;
+	}
+	UTFMODE |= 8;
+}
+
+static Lex_state *
+push_state_i(State_info *si, Lex_state *old_end)
+{
+	Lex_state *news = alloc2(STATE_BSIZE, sizeof(Lex_state), ATEMP);
+
+	news[0].ls_base = old_end;
+	si->base = &news[0];
+	si->end = &news[STATE_BSIZE];
+	return (&news[1]);
+}
+
+static Lex_state *
+pop_state_i(State_info *si, Lex_state *old_end)
+{
+	Lex_state *old_base = si->base;
+
+	si->base = old_end->ls_base - STATE_BSIZE;
+	si->end = old_end->ls_base;
+
+	afree(old_base, ATEMP);
+
+	return (si->base + STATE_BSIZE - 1);
+}
+
+static int
+s_get(void)
+{
+	return (getsc());
+}
+
+static void
+s_put(int c)
+{
+	ungetsc(c);
+}

Deleted: vendor/MirOS/mksh/R50/main.c
===================================================================
--- vendor/MirOS/mksh/dist/main.c	2014-06-30 23:58:12 UTC (rev 6706)
+++ vendor/MirOS/mksh/R50/main.c	2014-07-01 12:01:17 UTC (rev 6708)
@@ -1,1941 +0,0 @@
-/*	$OpenBSD: main.c,v 1.52 2013/06/15 17:25:19 millert Exp $	*/
-/*	$OpenBSD: tty.c,v 1.9 2006/03/14 22:08:01 deraadt Exp $	*/
-/*	$OpenBSD: io.c,v 1.22 2006/03/17 16:30:13 millert Exp $	*/
-/*	$OpenBSD: table.c,v 1.15 2012/02/19 07:52:30 otto Exp $	*/
-
-/*-
- * Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
- *		 2011, 2012, 2013
- *	Thorsten Glaser <tg at mirbsd.org>
- *
- * Provided that these terms and disclaimer and all copyright notices
- * are retained or reproduced in an accompanying document, permission
- * is granted to deal in this work without restriction, including un-
- * limited rights to use, publicly perform, distribute, sell, modify,
- * merge, give away, or sublicence.
- *
- * This work is provided "AS IS" and WITHOUT WARRANTY of any kind, to
- * the utmost extent permitted by applicable law, neither express nor
- * implied; without malicious intent or gross negligence. In no event
- * may a licensor, author or contributor be held liable for indirect,
- * direct, other damage, loss, or other issues arising in any way out
- * of dealing in the work, even if advised of the possibility of such
- * damage or existence of a defect, except proven that it results out
- * of said person's immediate fault when using the work as intended.
- */
-
-#define EXTERN
-#include "sh.h"
-
-#if HAVE_LANGINFO_CODESET
-#include <langinfo.h>
-#endif
-#if HAVE_SETLOCALE_CTYPE
-#include <locale.h>
-#endif
-
-__RCSID("$MirOS: src/bin/mksh/main.c,v 1.269 2013/07/25 18:07:46 tg Exp $");
-
-extern char **environ;
-
-#ifndef MKSHRC_PATH
-#define MKSHRC_PATH	"~/.mkshrc"
-#endif
-
-#ifndef MKSH_DEFAULT_TMPDIR
-#define MKSH_DEFAULT_TMPDIR	"/tmp"
-#endif
-
-static uint8_t isuc(const char *);
-static int main_init(int, const char *[], Source **, struct block **);
-uint32_t chvt_rndsetup(const void *, size_t);
-void chvt_reinit(void);
-static void reclaim(void);
-static void remove_temps(struct temp *);
-static mksh_uari_t rndsetup(void);
-#ifdef SIGWINCH
-static void x_sigwinch(int);
-#endif
-
-static const char initifs[] = "IFS= \t\n";
-
-static const char initsubs[] =
-    "${PS2=> } ${PS3=#? } ${PS4=+ } ${SECONDS=0} ${TMOUT=0} ${EPOCHREALTIME=}";
-
-static const char *initcoms[] = {
-	Ttypeset, "-r", initvsn, NULL,
-	Ttypeset, "-x", "HOME", "PATH", "RANDOM", "SHELL", NULL,
-	Ttypeset, "-i10", "COLUMNS", "LINES", "SECONDS", "TMOUT", NULL,
-	Talias,
-	"integer=typeset -i",
-	Tlocal_typeset,
-	/* not "alias -t --": hash -r needs to work */
-	"hash=alias -t",
-	"type=whence -v",
-#if !defined(ANDROID) && !defined(MKSH_UNEMPLOYED)
-	/* not in Android for political reasons */
-	/* not in ARGE mksh due to no job control */
-	"stop=kill -STOP",
-	"suspend=kill -STOP $$",
-#endif
-	"autoload=typeset -fu",
-	"functions=typeset -f",
-	"history=fc -l",
-	"nameref=typeset -n",
-	"nohup=nohup ",
-	Tr_fc_e_dash,
-	"source=PATH=$PATH:. command .",
-	"login=exec login",
-	NULL,
-	 /* this is what AT&T ksh seems to track, with the addition of emacs */
-	Talias, "-tU",
-	"cat", "cc", "chmod", "cp", "date", "ed", "emacs", "grep", "ls",
-	"make", "mv", "pr", "rm", "sed", "sh", "vi", "who", NULL,
-	NULL
-};
-
-static const char *restr_com[] = {
-	Ttypeset, "-r", "PATH", "ENV", "SHELL", NULL
-};
-
-static bool initio_done;
-
-/* top-level parsing and execution environment */
-static struct env env;
-struct env *e = &env;
-
-static mksh_uari_t
-rndsetup(void)
-{
-	register uint32_t h;
-	struct {
-		ALLOC_ITEM alloc_INT;
-		void *dataptr, *stkptr, *mallocptr;
-#if defined(__GLIBC__) && (__GLIBC__ >= 2)
-		sigjmp_buf jbuf;
-#endif
-		struct timeval tv;
-	} *bufptr;
-	char *cp;
-
-	cp = alloc(sizeof(*bufptr) - ALLOC_SIZE, APERM);
-#ifdef DEBUG
-	/* clear the allocated space, for valgrind */
-	memset(cp, 0, sizeof(*bufptr) - ALLOC_SIZE);
-#endif
-	/* undo what alloc() did to the malloc result address */
-	bufptr = (void *)(cp - ALLOC_SIZE);
-	/* PIE or something similar provides us with deltas here */
-	bufptr->dataptr = &rndsetupstate;
-	/* ASLR in at least Windows, Linux, some BSDs */
-	bufptr->stkptr = &bufptr;
-	/* randomised malloc in BSD (and possibly others) */
-	bufptr->mallocptr = bufptr;
-#if defined(__GLIBC__) && (__GLIBC__ >= 2)
-	/* glibc pointer guard */
-	sigsetjmp(bufptr->jbuf, 1);
-#endif
-	/* introduce variation (and yes, second arg MBZ for portability) */
-	mksh_TIME(bufptr->tv);
-
-	h = chvt_rndsetup(bufptr, sizeof(*bufptr));
-
-	afree(cp, APERM);
-	return ((mksh_uari_t)h);
-}
-
-uint32_t
-chvt_rndsetup(const void *bp, size_t sz)
-{
-	register uint32_t h;
-
-	NZATInit(h);
-	/* variation through pid, ppid, and the works */
-	NZATUpdateMem(h, &rndsetupstate, sizeof(rndsetupstate));
-	/* some variation, some possibly entropy, depending on OE */
-	NZATUpdateMem(h, bp, sz);
-	NZAATFinish(h);
-
-	return (h);
-}
-
-void
-chvt_reinit(void)
-{
-	kshpid = procpid = getpid();
-	ksheuid = geteuid();
-	kshpgrp = getpgrp();
-	kshppid = getppid();
-}
-
-static const char *empty_argv[] = {
-	"mksh", NULL
-};
-
-static uint8_t
-isuc(const char *cx) {
-	char *cp, *x;
-	uint8_t rv = 0;
-
-	if (!cx || !*cx)
-		return (0);
-
-	/* uppercase a string duplicate */
-	strdupx(x, cx, ATEMP);
-	cp = x;
-	while ((*cp = ksh_toupper(*cp)))
-		++cp;
-
-	/* check for UTF-8 */
-	if (strstr(x, "UTF-8") || strstr(x, "UTF8"))
-		rv = 1;
-
-	/* free copy and out */
-	afree(x, ATEMP);
-	return (rv);
-}
-
-static int
-main_init(int argc, const char *argv[], Source **sp, struct block **lp)
-{
-	int argi, i;
-	Source *s = NULL;
-	struct block *l;
-	unsigned char restricted, errexit, utf_flag;
-	char *cp;
-	const char *ccp, **wp;
-	struct tbl *vp;
-	struct stat s_stdin;
-#if !defined(_PATH_DEFPATH) && defined(_CS_PATH)
-	ssize_t k;
-#endif
-
-	/* do things like getpgrp() et al. */
-	chvt_reinit();
-
-	/* make sure argv[] is sane */
-	if (!*argv) {
-		argv = empty_argv;
-		argc = 1;
-	}
-	kshname = argv[0];
-
-	/* initialise permanent Area */
-	ainit(&aperm);
-
-	/* set up base environment */
-	env.type = E_NONE;
-	ainit(&env.area);
-	/* set up global l->vars and l->funs */
-	newblock();
-
-	/* Do this first so output routines (eg, errorf, shellf) can work */
-	initio();
-
-	/* determine the basename (without '-' or path) of the executable */
-	ccp = kshname;
-	goto begin_parse_kshname;
-	while ((i = ccp[argi++])) {
-		if (i == '/') {
-			ccp += argi;
- begin_parse_kshname:
-			argi = 0;
-			if (*ccp == '-')
-				++ccp;
-		}
-	}
-	if (!*ccp)
-		ccp = empty_argv[0];
-
-	/* define built-in commands and see if we were called as one */
-	ktinit(APERM, &builtins,
-	    /* currently up to 51 builtins: 75% of 128 = 2^7 */
-	    7);
-	for (i = 0; mkshbuiltins[i].name != NULL; i++)
-		if (!strcmp(ccp, builtin(mkshbuiltins[i].name,
-		    mkshbuiltins[i].func)))
-			Flag(FAS_BUILTIN) = 1;
-
-	if (!Flag(FAS_BUILTIN)) {
-		/* check for -T option early */
-		argi = parse_args(argv, OF_FIRSTTIME, NULL);
-		if (argi < 0)
-			return (1);
-
-#if defined(MKSH_BINSHPOSIX) || defined(MKSH_BINSHREDUCED)
-		/* are we called as -sh or /bin/sh or so? */
-		if (!strcmp(ccp, "sh")) {
-			/* either also turns off braceexpand */
-#ifdef MKSH_BINSHPOSIX
-			/* enable better POSIX conformance */
-			change_flag(FPOSIX, OF_FIRSTTIME, true);
-#endif
-#ifdef MKSH_BINSHREDUCED
-			/* enable kludge/compat mode */
-			change_flag(FSH, OF_FIRSTTIME, true);
-#endif
-		}
-#endif
-	}
-
-	initvar();
-
-	initctypes();
-
-	inittraps();
-
-	coproc_init();
-
-	/* set up variable and command dictionaries */
-	ktinit(APERM, &taliases, 0);
-	ktinit(APERM, &aliases, 0);
-#ifndef MKSH_NOPWNAM
-	ktinit(APERM, &homedirs, 0);
-#endif
-
-	/* define shell keywords */
-	initkeywords();
-
-	init_histvec();
-
-	/* initialise tty size before importing environment */
-	change_winsz();
-
-#ifdef _PATH_DEFPATH
-	def_path = _PATH_DEFPATH;
-#else
-#ifdef _CS_PATH
-	if ((k = confstr(_CS_PATH, NULL, 0)) > 0 &&
-	    confstr(_CS_PATH, cp = alloc(k + 1, APERM), k + 1) == k + 1)
-		def_path = cp;
-	else
-#endif
-		/*
-		 * this is uniform across all OSes unless it
-		 * breaks somewhere; don't try to optimise,
-		 * e.g. add stuff for Interix or remove /usr
-		 * for HURD, because e.g. Debian GNU/HURD is
-		 * "keeping a regular /usr"; this is supposed
-		 * to be a sane 'basic' default PATH
-		 */
-		def_path = "/bin:/usr/bin:/sbin:/usr/sbin";
-#endif
-
-	/*
-	 * Set PATH to def_path (will set the path global variable).
-	 * (import of environment below will probably change this setting).
-	 */
-	vp = global("PATH");
-	/* setstr can't fail here */
-	setstr(vp, def_path, KSH_RETURN_ERROR);
-
-	/*
-	 * Turn on nohup by default for now - will change to off
-	 * by default once people are aware of its existence
-	 * (AT&T ksh does not have a nohup option - it always sends
-	 * the hup).
-	 */
-	Flag(FNOHUP) = 1;
-
-	/*
-	 * Turn on brace expansion by default. AT&T kshs that have
-	 * alternation always have it on.
-	 */
-	Flag(FBRACEEXPAND) = 1;
-
-	/*
-	 * Turn on "set -x" inheritance by default.
-	 */
-	Flag(FXTRACEREC) = 1;
-
-#ifndef MKSH_NO_CMDLINE_EDITING
-	/*
-	 * Set edit mode to emacs by default, may be overridden
-	 * by the environment or the user. Also, we want tab completion
-	 * on in vi by default.
-	 */
-	change_flag(FEMACS, OF_SPECIAL, true);
-#if !MKSH_S_NOVI
-	Flag(FVITABCOMPLETE) = 1;
-#endif
-#endif
-
-	/* import environment */
-	if (environ != NULL)
-		for (wp = (const char **)environ; *wp != NULL; wp++)
-			typeset(*wp, IMPORT | EXPORT, 0, 0, 0);
-
-	/* for security */
-	typeset(initifs, 0, 0, 0, 0);
-
-	/* assign default shell variable values */
-	substitute(initsubs, 0);
-
-	/* Figure out the current working directory and set $PWD */
-	vp = global("PWD");
-	cp = str_val(vp);
-	/* Try to use existing $PWD if it is valid */
-	set_current_wd((cp[0] == '/' && test_eval(NULL, TO_FILEQ, cp, ".",
-	    true)) ? cp : NULL);
-	if (current_wd[0])
-		simplify_path(current_wd);
-	/* Only set pwd if we know where we are or if it had a bogus value */
-	if (current_wd[0] || *cp)
-		/* setstr can't fail here */
-		setstr(vp, current_wd, KSH_RETURN_ERROR);
-
-	for (wp = initcoms; *wp != NULL; wp++) {
-		shcomexec(wp);
-		while (*wp != NULL)
-			wp++;
-	}
-	setint_n(global("OPTIND"), 1, 10);
-
-	kshuid = getuid();
-	kshgid = getgid();
-	kshegid = getegid();
-
-	safe_prompt = ksheuid ? "$ " : "# ";
-	vp = global("PS1");
-	/* Set PS1 if unset or we are root and prompt doesn't contain a # */
-	if (!(vp->flag & ISSET) ||
-	    (!ksheuid && !strchr(str_val(vp), '#')))
-		/* setstr can't fail here */
-		setstr(vp, safe_prompt, KSH_RETURN_ERROR);
-	setint_n((vp = global("BASHPID")), 0, 10);
-	vp->flag |= INT_U;
-	setint_n((vp = global("PGRP")), (mksh_uari_t)kshpgrp, 10);
-	vp->flag |= INT_U;
-	setint_n((vp = global("PPID")), (mksh_uari_t)kshppid, 10);
-	vp->flag |= INT_U;
-	setint_n((vp = global("USER_ID")), (mksh_uari_t)ksheuid, 10);
-	vp->flag |= INT_U;
-	setint_n((vp = global("KSHUID")), (mksh_uari_t)kshuid, 10);
-	vp->flag |= INT_U;
-	setint_n((vp = global("KSHEGID")), (mksh_uari_t)kshegid, 10);
-	vp->flag |= INT_U;
-	setint_n((vp = global("KSHGID")), (mksh_uari_t)kshgid, 10);
-	vp->flag |= INT_U;
-	setint_n((vp = global("RANDOM")), rndsetup(), 10);
-	vp->flag |= INT_U;
-	setint_n((vp_pipest = global("PIPESTATUS")), 0, 10);
-
-	/* Set this before parsing arguments */
-	Flag(FPRIVILEGED) = kshuid != ksheuid || kshgid != kshegid;
-
-	/* this to note if monitor is set on command line (see below) */
-#ifndef MKSH_UNEMPLOYED
-	Flag(FMONITOR) = 127;
-#endif
-	/* this to note if utf-8 mode is set on command line (see below) */
-	UTFMODE = 2;
-
-	if (!Flag(FAS_BUILTIN)) {
-		argi = parse_args(argv, OF_CMDLINE, NULL);
-		if (argi < 0)
-			return (1);
-	}
-
-#if defined(DEBUG) && !defined(MKSH_LEGACY_MODE)
-	/* test wraparound of arithmetic types */
-	{
-		volatile long xl;
-		volatile unsigned long xul;
-		volatile int xi;
-		volatile unsigned int xui;
-		volatile mksh_ari_t xa;
-		volatile mksh_uari_t xua, xua2;
-		volatile uint8_t xc;
-
-		xa = 2147483647;
-		xua = 2147483647;
-		++xa;
-		++xua;
-		xua2 = xa;
-		xl = xa;
-		xul = xua;
-		xa = 0;
-		xua = 0;
-		--xa;
-		--xua;
-		xi = xa;
-		xui = xua;
-		xa = -1;
-		xua = xa;
-		++xa;
-		++xua;
-		xc = 0;
-		--xc;
-		if ((xua2 != 2147483648UL) ||
-		    (xl != (-2147483647L-1)) || (xul != 2147483648UL) ||
-		    (xi != -1) || (xui != 4294967295U) ||
-		    (xa != 0) || (xua != 0) || (xc != 255))
-			errorf("integer wraparound test failed");
-	}
-#endif
-
-	/* process this later only, default to off (hysterical raisins) */
-	utf_flag = UTFMODE;
-	UTFMODE = 0;
-
-	if (Flag(FAS_BUILTIN)) {
-		/* auto-detect from environment variables, always */
-		utf_flag = 3;
-	} else if (Flag(FCOMMAND)) {
-		s = pushs(SSTRINGCMDLINE, ATEMP);
-		if (!(s->start = s->str = argv[argi++]))
-			errorf("%s %s", "-c", "requires an argument");
-#if !defined(MKSH_SMALL)
-		while (*s->str) {
-			if (*s->str != ' ' && ctype(*s->str, C_QUOTE))
-				break;
-			s->str++;
-		}
-		if (!*s->str)
-			s->flags |= SF_MAYEXEC;
-		s->str = s->start;
-#endif
-#ifdef MKSH_MIDNIGHTBSD01ASH_COMPAT
-		/* compatibility to MidnightBSD 0.1 /bin/sh (kludge) */
-		if (Flag(FSH) && argv[argi] && !strcmp(argv[argi], "--"))
-			++argi;
-#endif
-		if (argv[argi])
-			kshname = argv[argi++];
-	} else if (argi < argc && !Flag(FSTDIN)) {
-		s = pushs(SFILE, ATEMP);
-		s->file = argv[argi++];
-		s->u.shf = shf_open(s->file, O_RDONLY, 0,
-		    SHF_MAPHI | SHF_CLEXEC);
-		if (s->u.shf == NULL) {
-			shl_stdout_ok = false;
-			warningf(true, "%s: %s", s->file, cstrerror(errno));
-			/* mandated by SUSv4 */
-			exstat = 127;
-			unwind(LERROR);
-		}
-		kshname = s->file;
-	} else {
-		Flag(FSTDIN) = 1;
-		s = pushs(SSTDIN, ATEMP);
-		s->file = "<stdin>";
-		s->u.shf = shf_fdopen(0, SHF_RD | can_seek(0),
-		    NULL);
-		if (isatty(0) && isatty(2)) {
-			Flag(FTALKING) = Flag(FTALKING_I) = 1;
-			/* The following only if isatty(0) */
-			s->flags |= SF_TTY;
-			s->u.shf->flags |= SHF_INTERRUPT;
-			s->file = NULL;
-		}
-	}
-
-	/* this bizarreness is mandated by POSIX */
-	if (fstat(0, &s_stdin) >= 0 && S_ISCHR(s_stdin.st_mode) &&
-	    Flag(FTALKING))
-		reset_nonblock(0);
-
-	/* initialise job control */
-	j_init();
-	/* do this after j_init() which calls tty_init_state() */
-	if (Flag(FTALKING)) {
-		if (utf_flag == 2) {
-#ifndef MKSH_ASSUME_UTF8
-			/* auto-detect from locale or environment */
-			utf_flag = 4;
-#elif MKSH_ASSUME_UTF8
-			utf_flag = 1;
-#else
-			/* always disable UTF-8 (for interactive) */
-			utf_flag = 0;
-#endif
-		}
-#ifndef MKSH_NO_CMDLINE_EDITING
-		x_init();
-#endif
-	}
-
-#ifdef SIGWINCH
-	sigtraps[SIGWINCH].flags |= TF_SHELL_USES;
-	setsig(&sigtraps[SIGWINCH], x_sigwinch,
-	    SS_RESTORE_ORIG|SS_FORCE|SS_SHTRAP);
-#endif
-
-	l = e->loc;
-	if (Flag(FAS_BUILTIN)) {
-		l->argc = argc;
-		l->argv = argv;
-		l->argv[0] = ccp;
-	} else {
-		l->argc = argc - argi;
-		/*
-		 * allocate a new array because otherwise, when we modify
-		 * it in-place, ps(1) output changes; the meaning of argc
-		 * here is slightly different as it excludes kshname, and
-		 * we add a trailing NULL sentinel as well
-		 */
-		l->argv = alloc2(l->argc + 2, sizeof(void *), APERM);
-		l->argv[0] = kshname;
-		memcpy(&l->argv[1], &argv[argi], l->argc * sizeof(void *));
-		l->argv[l->argc + 1] = NULL;
-		getopts_reset(1);
-	}
-
-	/* divine the initial state of the utf8-mode Flag */
-	ccp = null;
-	switch (utf_flag) {
-
-	/* auto-detect from locale or environment */
-	case 4:
-#if HAVE_SETLOCALE_CTYPE
-		ccp = setlocale(LC_CTYPE, "");
-#if HAVE_LANGINFO_CODESET
-		if (!isuc(ccp))
-			ccp = nl_langinfo(CODESET);
-#endif
-		if (!isuc(ccp))
-			ccp = null;
-		/* FALLTHROUGH */
-#endif
-
-	/* auto-detect from environment */
-	case 3:
-		/* these were imported from environ earlier */
-		if (ccp == null)
-			ccp = str_val(global("LC_ALL"));
-		if (ccp == null)
-			ccp = str_val(global("LC_CTYPE"));
-		if (ccp == null)
-			ccp = str_val(global("LANG"));
-		UTFMODE = isuc(ccp);
-		break;
-
-	/* not set on command line, not FTALKING */
-	case 2:
-	/* unknown values */
-	default:
-		utf_flag = 0;
-		/* FALLTHROUGH */
-
-	/* known values */
-	case 1:
-	case 0:
-		UTFMODE = utf_flag;
-		break;
-	}
-
-	/* Disable during .profile/ENV reading */
-	restricted = Flag(FRESTRICTED);
-	Flag(FRESTRICTED) = 0;
-	errexit = Flag(FERREXIT);
-	Flag(FERREXIT) = 0;
-
-	/*
-	 * Do this before profile/$ENV so that if it causes problems in them,
-	 * user will know why things broke.
-	 */
-	if (!current_wd[0] && Flag(FTALKING))
-		warningf(false, "can't determine current directory");
-
-	if (Flag(FLOGIN)) {
-		include(MKSH_SYSTEM_PROFILE, 0, NULL, true);
-		if (!Flag(FPRIVILEGED))
-			include(substitute("$HOME/.profile", 0), 0,
-			    NULL, true);
-	}
-	if (Flag(FPRIVILEGED))
-		include(MKSH_SUID_PROFILE, 0, NULL, true);
-	else if (Flag(FTALKING)) {
-		char *env_file;
-
-		/* include $ENV */
-		env_file = substitute(substitute("${ENV:-" MKSHRC_PATH "}", 0),
-		    DOTILDE);
-		if (*env_file != '\0')
-			include(env_file, 0, NULL, true);
-	}
-
-	if (restricted) {
-		shcomexec(restr_com);
-		/* After typeset command... */
-		Flag(FRESTRICTED) = 1;
-	}
-	Flag(FERREXIT) = errexit;
-
-	if (Flag(FTALKING) && s)
-		hist_init(s);
-	else
-		/* set after ENV */
-		Flag(FTRACKALL) = 1;
-
-	alarm_init();
-
-	*sp = s;
-	*lp = l;
-	return (0);
-}
-
-/* this indirection barrier reduces stack usage during normal operation */
-
-int
-main(int argc, const char *argv[])
-{
-	int rv;
-	Source *s;
-	struct block *l;
-
-	if ((rv = main_init(argc, argv, &s, &l)) == 0) {
-		if (Flag(FAS_BUILTIN)) {
-			rv = shcomexec(l->argv);
-		} else {
-			shell(s, true);
-			/* NOTREACHED */
-		}
-	}
-	return (rv);
-}
-
-int
-include(const char *name, int argc, const char **argv, bool intr_ok)
-{
-	Source *volatile s = NULL;
-	struct shf *shf;
-	const char **volatile old_argv;
-	volatile int old_argc;
-	int i;
-
-	shf = shf_open(name, O_RDONLY, 0, SHF_MAPHI | SHF_CLEXEC);
-	if (shf == NULL)
-		return (-1);
-
-	if (argv) {
-		old_argv = e->loc->argv;
-		old_argc = e->loc->argc;
-	} else {
-		old_argv = NULL;
-		old_argc = 0;
-	}
-	newenv(E_INCL);
-	if ((i = kshsetjmp(e->jbuf))) {
-		quitenv(s ? s->u.shf : NULL);
-		if (old_argv) {
-			e->loc->argv = old_argv;
-			e->loc->argc = old_argc;
-		}
-		switch (i) {
-		case LRETURN:
-		case LERROR:
-			/* see below */
-			return (exstat & 0xFF);
-		case LINTR:
-			/*
-			 * intr_ok is set if we are including .profile or $ENV.
-			 * If user ^Cs out, we don't want to kill the shell...
-			 */
-			if (intr_ok && ((exstat & 0xFF) - 128) != SIGTERM)
-				return (1);
-			/* FALLTHROUGH */
-		case LEXIT:
-		case LLEAVE:
-		case LSHELL:
-			unwind(i);
-			/* NOTREACHED */
-		default:
-			internal_errorf("%s %d", "include", i);
-			/* NOTREACHED */
-		}
-	}
-	if (argv) {
-		e->loc->argv = argv;
-		e->loc->argc = argc;
-	}
-	s = pushs(SFILE, ATEMP);
-	s->u.shf = shf;
-	strdupx(s->file, name, ATEMP);
-	i = shell(s, false);
-	quitenv(s->u.shf);
-	if (old_argv) {
-		e->loc->argv = old_argv;
-		e->loc->argc = old_argc;
-	}
-	/* & 0xff to ensure value not -1 */
-	return (i & 0xFF);
-}
-
-/* spawn a command into a shell optionally keeping track of the line number */
-int
-command(const char *comm, int line)
-{
-	Source *s;
-
-	s = pushs(SSTRING, ATEMP);
-	s->start = s->str = comm;
-	s->line = line;
-	return (shell(s, false));
-}
-
-/*
- * run the commands from the input source, returning status.
- */
-int
-shell(Source * volatile s, volatile bool toplevel)
-{
-	struct op *t;
-	volatile bool wastty = tobool(s->flags & SF_TTY);
-	volatile uint8_t attempts = 13;
-	volatile bool interactive = Flag(FTALKING) && toplevel;
-	volatile bool sfirst = true;
-	Source *volatile old_source = source;
-	int i;
-
-	newenv(E_PARSE);
-	if (interactive)
-		really_exit = false;
-	switch ((i = kshsetjmp(e->jbuf))) {
-	case 0:
-		break;
-	case LINTR:
-		/* we get here if SIGINT not caught or ignored */
-	case LERROR:
-	case LSHELL:
-		if (interactive) {
-			if (i == LINTR)
-				shellf("\n");
-			/*
-			 * Reset any eof that was read as part of a
-			 * multiline command.
-			 */
-			if (Flag(FIGNOREEOF) && s->type == SEOF && wastty)
-				s->type = SSTDIN;
-			/*
-			 * Used by exit command to get back to
-			 * top level shell. Kind of strange since
-			 * interactive is set if we are reading from
-			 * a tty, but to have stopped jobs, one only
-			 * needs FMONITOR set (not FTALKING/SF_TTY)...
-			 */
-			/* toss any input we have so far */
-			yyrecursive_pop(true);
-			s->start = s->str = null;
-			retrace_info = NULL;
-			herep = heres;
-			break;
-		}
-		/* FALLTHROUGH */
-	case LEXIT:
-	case LLEAVE:
-	case LRETURN:
-		source = old_source;
-		quitenv(NULL);
-		/* keep on going */
-		unwind(i);
-		/* NOTREACHED */
-	default:
-		source = old_source;
-		quitenv(NULL);
-		internal_errorf("%s %d", "shell", i);
-		/* NOTREACHED */
-	}
-	while (/* CONSTCOND */ 1) {
-		if (trap)
-			runtraps(0);
-
-		if (s->next == NULL) {
-			if (Flag(FVERBOSE))
-				s->flags |= SF_ECHO;
-			else
-				s->flags &= ~SF_ECHO;
-		}
-		if (interactive) {
-			j_notify();
-			set_prompt(PS1, s);
-		}
-		t = compile(s, sfirst);
-		sfirst = false;
-		if (!t)
-			goto source_no_tree;
-		if (t->type == TEOF) {
-			if (wastty && Flag(FIGNOREEOF) && --attempts > 0) {
-				shellf("Use 'exit' to leave mksh\n");
-				s->type = SSTDIN;
-			} else if (wastty && !really_exit &&
-			    j_stopped_running()) {
-				really_exit = true;
-				s->type = SSTDIN;
-			} else {
-				/*
-				 * this for POSIX which says EXIT traps
-				 * shall be taken in the environment
-				 * immediately after the last command
-				 * executed.
-				 */
-				if (toplevel)
-					unwind(LEXIT);
-				break;
-			}
-		}
-#if !defined(MKSH_SMALL)
-		  else if ((s->flags & SF_MAYEXEC) && t->type == TCOM)
-			t->u.evalflags |= DOTCOMEXEC;
-#endif
-		if (!Flag(FNOEXEC) || (s->flags & SF_TTY))
-			exstat = execute(t, 0, NULL) & 0xFF;
-
-		if (t->type != TEOF && interactive && really_exit)
-			really_exit = false;
-
- source_no_tree:
-		reclaim();
-	}
-	quitenv(NULL);
-	source = old_source;
-	return (exstat & 0xFF);
-}
-
-/* return to closest error handler or shell(), exit if none found */
-/* note: i MUST NOT be 0 */
-void
-unwind(int i)
-{
-	/*
-	 * This is a kludge. We need to restore everything that was
-	 * changed in the new environment, see cid 1005090337C7A669439
-	 * and 10050903386452ACBF1, but fail to even save things most of
-	 * the time. funcs.c:c_eval() changes FERREXIT temporarily to 0,
-	 * which needs to be restored thus (related to Debian #696823).
-	 * We did not save the shell flags, so we use a special or'd
-	 * value here... this is mostly to clean up behind *other*
-	 * callers of unwind(LERROR) here; exec.c has the regular case.
-	 */
-	if (Flag(FERREXIT) & 0x80) {
-		/* GNU bash does not run this trapsig */
-		trapsig(ksh_SIGERR);
-		Flag(FERREXIT) &= ~0x80;
-	}
-
-	/* ordering for EXIT vs ERR is a bit odd (this is what AT&T ksh does) */
-	if (i == LEXIT || ((i == LERROR || i == LINTR) &&
-	    sigtraps[ksh_SIGEXIT].trap &&
-	    (!Flag(FTALKING) || Flag(FERREXIT)))) {
-		++trap_nested;
-		runtrap(&sigtraps[ksh_SIGEXIT], trap_nested == 1);
-		--trap_nested;
-		i = LLEAVE;
-	} else if (Flag(FERREXIT) == 1 && (i == LERROR || i == LINTR)) {
-		++trap_nested;
-		runtrap(&sigtraps[ksh_SIGERR], trap_nested == 1);
-		--trap_nested;
-		i = LLEAVE;
-	}
-
-	while (/* CONSTCOND */ 1) {
-		switch (e->type) {
-		case E_PARSE:
-		case E_FUNC:
-		case E_INCL:
-		case E_LOOP:
-		case E_ERRH:
-			kshlongjmp(e->jbuf, i);
-			/* NOTREACHED */
-		case E_NONE:
-			if (i == LINTR)
-				e->flags |= EF_FAKE_SIGDIE;
-			/* FALLTHROUGH */
-		default:
-			quitenv(NULL);
-		}
-	}
-}
-
-void
-newenv(int type)
-{
-	struct env *ep;
-	char *cp;
-
-	/*
-	 * struct env includes ALLOC_ITEM for alignment constraints
-	 * so first get the actually used memory, then assign it
-	 */
-	cp = alloc(sizeof(struct env) - ALLOC_SIZE, ATEMP);
-	/* undo what alloc() did to the malloc result address */
-	ep = (void *)(cp - ALLOC_SIZE);
-	/* initialise public members of struct env (not the ALLOC_ITEM) */
-	ainit(&ep->area);
-	ep->oenv = e;
-	ep->loc = e->loc;
-	ep->savefd = NULL;
-	ep->temps = NULL;
-	ep->yyrecursive_statep = NULL;
-	ep->type = type;
-	ep->flags = 0;
-	/* jump buffer is invalid because flags == 0 */
-	e = ep;
-}
-
-void
-quitenv(struct shf *shf)
-{
-	struct env *ep = e;
-	char *cp;
-	int fd;
-
-	yyrecursive_pop(true);
-	while (ep->oenv && ep->oenv->loc != ep->loc)
-		popblock();
-	if (ep->savefd != NULL) {
-		for (fd = 0; fd < NUFILE; fd++)
-			/* if ep->savefd[fd] < 0, means fd was closed */
-			if (ep->savefd[fd])
-				restfd(fd, ep->savefd[fd]);
-		if (ep->savefd[2])
-			/* Clear any write errors */
-			shf_reopen(2, SHF_WR, shl_out);
-	}
-	/*
-	 * Bottom of the stack.
-	 * Either main shell is exiting or cleanup_parents_env() was called.
-	 */
-	if (ep->oenv == NULL) {
-#ifdef DEBUG_LEAKS
-		int i;
-#endif
-
-		if (ep->type == E_NONE) {
-			/* Main shell exiting? */
-#if HAVE_PERSISTENT_HISTORY
-			if (Flag(FTALKING))
-				hist_finish();
-#endif
-			j_exit();
-			if (ep->flags & EF_FAKE_SIGDIE) {
-				int sig = (exstat & 0xFF) - 128;
-
-				/*
-				 * ham up our death a bit (AT&T ksh
-				 * only seems to do this for SIGTERM)
-				 * Don't do it for SIGQUIT, since we'd
-				 * dump a core..
-				 */
-				if ((sig == SIGINT || sig == SIGTERM) &&
-				    (kshpgrp == kshpid)) {
-					setsig(&sigtraps[sig], SIG_DFL,
-					    SS_RESTORE_CURR | SS_FORCE);
-					kill(0, sig);
-				}
-			}
-		}
-		if (shf)
-			shf_close(shf);
-		reclaim();
-#ifdef DEBUG_LEAKS
-#ifndef MKSH_NO_CMDLINE_EDITING
-		x_done();
-#endif
-#ifndef MKSH_NOPROSPECTOFWORK
-		/* block at least SIGCHLD during/after afreeall */
-		sigprocmask(SIG_BLOCK, &sm_sigchld, NULL);
-#endif
-		afreeall(APERM);
-		for (fd = 3; fd < NUFILE; fd++)
-			if ((i = fcntl(fd, F_GETFD, 0)) != -1 &&
-			    (i & FD_CLOEXEC))
-				close(fd);
-		close(2);
-		close(1);
-		close(0);
-#endif
-		exit(exstat & 0xFF);
-	}
-	if (shf)
-		shf_close(shf);
-	reclaim();
-
-	e = e->oenv;
-
-	/* free the struct env - tricky due to the ALLOC_ITEM inside */
-	cp = (void *)ep;
-	afree(cp + ALLOC_SIZE, ATEMP);
-}
-
-/* Called after a fork to cleanup stuff left over from parents environment */
-void
-cleanup_parents_env(void)
-{
-	struct env *ep;
-	int fd;
-
-	mkssert(e != NULL);
-
-	/*
-	 * Don't clean up temporary files - parent will probably need them.
-	 * Also, can't easily reclaim memory since variables, etc. could be
-	 * anywhere.
-	 */
-
-	/* close all file descriptors hiding in savefd */
-	for (ep = e; ep; ep = ep->oenv) {
-		if (ep->savefd) {
-			for (fd = 0; fd < NUFILE; fd++)
-				if (ep->savefd[fd] > 0)
-					close(ep->savefd[fd]);
-			afree(ep->savefd, &ep->area);
-			ep->savefd = NULL;
-		}
-#ifdef DEBUG_LEAKS
-		if (ep->type != E_NONE)
-			ep->type = E_GONE;
-#endif
-	}
-#ifndef DEBUG_LEAKS
-	e->oenv = NULL;
-#endif
-}
-
-/* Called just before an execve cleanup stuff temporary files */
-void
-cleanup_proc_env(void)
-{
-	struct env *ep;
-
-	for (ep = e; ep; ep = ep->oenv)
-		remove_temps(ep->temps);
-}
-
-/* remove temp files and free ATEMP Area */
-static void
-reclaim(void)
-{
-	struct block *l;
-
-	while ((l = e->loc) && (!e->oenv || e->oenv->loc != l)) {
-		e->loc = l->next;
-		afreeall(&l->area);
-	}
-
-	remove_temps(e->temps);
-	e->temps = NULL;
-	afreeall(&e->area);
-}
-
-static void
-remove_temps(struct temp *tp)
-{
-	for (; tp != NULL; tp = tp->next)
-		if (tp->pid == procpid)
-			unlink(tp->tffn);
-}
-
-/*
- * Initialise tty_fd. Used for tracking the size of the terminal,
- * saving/resetting tty modes upon forground job completion, and
- * for setting up the tty process group. Return values:
- *	0 = got controlling tty
- *	1 = got terminal but no controlling tty
- *	2 = cannot find a terminal
- *	3 = cannot dup fd
- *	4 = cannot make fd close-on-exec
- * An existing tty_fd is cached if no "better" one could be found,
- * i.e. if tty_devtty was already set or the new would not set it.
- */
-int
-tty_init_fd(void)
-{
-	int fd, rv, eno = 0;
-	bool do_close = false, is_devtty = true;
-
-	if (tty_devtty) {
-		/* already got a tty which is /dev/tty */
-		return (0);
-	}
-
-#ifdef _UWIN
-	/*XXX imake style */
-	if (isatty(3)) {
-		/* fd 3 on UWIN _is_ /dev/tty (or our controlling tty) */
-		fd = 3;
-		goto got_fd;
-	}
-#endif
-	if ((fd = open("/dev/tty", O_RDWR, 0)) >= 0) {
-		do_close = true;
-		goto got_fd;
-	}
-	eno = errno;
-
-	if (tty_fd >= 0) {
-		/* already got a non-devtty one */
-		rv = 1;
-		goto out;
-	}
-	is_devtty = false;
-
-	if (isatty((fd = 0)) || isatty((fd = 2)))
-		goto got_fd;
-	/* cannot find one */
-	rv = 2;
-	/* assert: do_close == false */
-	goto out;
-
- got_fd:
-	if ((rv = fcntl(fd, F_DUPFD, FDBASE)) < 0) {
-		eno = errno;
-		rv = 3;
-		goto out;
-	}
-	if (fcntl(rv, F_SETFD, FD_CLOEXEC) < 0) {
-		eno = errno;
-		close(rv);
-		rv = 4;
-		goto out;
-	}
-	tty_fd = rv;
-	tty_devtty = is_devtty;
-	rv = eno = 0;
- out:
-	if (do_close)
-		close(fd);
-	errno = eno;
-	return (rv);
-}
-
-/* A shell error occurred (eg, syntax error, etc.) */
-
-#define VWARNINGF_ERRORPREFIX	1
-#define VWARNINGF_FILELINE	2
-#define VWARNINGF_BUILTIN	4
-#define VWARNINGF_INTERNAL	8
-
-static void vwarningf(unsigned int, const char *, va_list)
-    MKSH_A_FORMAT(__printf__, 2, 0);
-
-static void
-vwarningf(unsigned int flags, const char *fmt, va_list ap)
-{
-	if (fmt) {
-		if (flags & VWARNINGF_INTERNAL)
-			shf_fprintf(shl_out, "internal error: ");
-		if (flags & VWARNINGF_ERRORPREFIX)
-			error_prefix(tobool(flags & VWARNINGF_FILELINE));
-		if ((flags & VWARNINGF_BUILTIN) &&
-		    /* not set when main() calls parse_args() */
-		    builtin_argv0 && builtin_argv0 != kshname)
-			shf_fprintf(shl_out, "%s: ", builtin_argv0);
-		shf_vfprintf(shl_out, fmt, ap);
-		shf_putchar('\n', shl_out);
-	}
-	shf_flush(shl_out);
-}
-
-void
-errorfx(int rc, const char *fmt, ...)
-{
-	va_list va;
-
-	exstat = rc;
-
-	/* debugging: note that stdout not valid */
-	shl_stdout_ok = false;
-
-	va_start(va, fmt);
-	vwarningf(VWARNINGF_ERRORPREFIX | VWARNINGF_FILELINE, fmt, va);
-	va_end(va);
-	unwind(LERROR);
-}
-
-void
-errorf(const char *fmt, ...)
-{
-	va_list va;
-
-	exstat = 1;
-
-	/* debugging: note that stdout not valid */
-	shl_stdout_ok = false;
-
-	va_start(va, fmt);
-	vwarningf(VWARNINGF_ERRORPREFIX | VWARNINGF_FILELINE, fmt, va);
-	va_end(va);
-	unwind(LERROR);
-}
-
-/* like errorf(), but no unwind is done */
-void
-warningf(bool fileline, const char *fmt, ...)
-{
-	va_list va;
-
-	va_start(va, fmt);
-	vwarningf(VWARNINGF_ERRORPREFIX | (fileline ? VWARNINGF_FILELINE : 0),
-	    fmt, va);
-	va_end(va);
-}
-
-/*
- * Used by built-in utilities to prefix shell and utility name to message
- * (also unwinds environments for special builtins).
- */
-void
-bi_errorf(const char *fmt, ...)
-{
-	va_list va;
-
-	/* debugging: note that stdout not valid */
-	shl_stdout_ok = false;
-
-	exstat = 1;
-
-	va_start(va, fmt);
-	vwarningf(VWARNINGF_ERRORPREFIX | VWARNINGF_FILELINE |
-	    VWARNINGF_BUILTIN, fmt, va);
-	va_end(va);
-
-	/*
-	 * POSIX special builtins and ksh special builtins cause
-	 * non-interactive shells to exit.
-	 * XXX odd use of KEEPASN; also may not want LERROR here
-	 */
-	if (builtin_flag & SPEC_BI) {
-		builtin_argv0 = NULL;
-		unwind(LERROR);
-	}
-}
-
-/* Called when something that shouldn't happen does */
-void
-internal_errorf(const char *fmt, ...)
-{
-	va_list va;
-
-	va_start(va, fmt);
-	vwarningf(VWARNINGF_INTERNAL, fmt, va);
-	va_end(va);
-	unwind(LERROR);
-}
-
-void
-internal_warningf(const char *fmt, ...)
-{
-	va_list va;
-
-	va_start(va, fmt);
-	vwarningf(VWARNINGF_INTERNAL, fmt, va);
-	va_end(va);
-}
-
-/* used by error reporting functions to print "ksh: .kshrc[25]: " */
-void
-error_prefix(bool fileline)
-{
-	/* Avoid foo: foo[2]: ... */
-	if (!fileline || !source || !source->file ||
-	    strcmp(source->file, kshname) != 0)
-		shf_fprintf(shl_out, "%s: ", kshname + (*kshname == '-'));
-	if (fileline && source && source->file != NULL) {
-		shf_fprintf(shl_out, "%s[%d]: ", source->file,
-		    source->errline > 0 ? source->errline : source->line);
-		source->errline = 0;
-	}
-}
-
-/* printf to shl_out (stderr) with flush */
-void
-shellf(const char *fmt, ...)
-{
-	va_list va;
-
-	if (!initio_done)
-		/* shl_out may not be set up yet... */
-		return;
-	va_start(va, fmt);
-	shf_vfprintf(shl_out, fmt, va);
-	va_end(va);
-	shf_flush(shl_out);
-}
-
-/* printf to shl_stdout (stdout) */
-void
-shprintf(const char *fmt, ...)
-{
-	va_list va;
-
-	if (!shl_stdout_ok)
-		internal_errorf("shl_stdout not valid");
-	va_start(va, fmt);
-	shf_vfprintf(shl_stdout, fmt, va);
-	va_end(va);
-}
-
-/* test if we can seek backwards fd (returns 0 or SHF_UNBUF) */
-int
-can_seek(int fd)
-{
-	struct stat statb;
-
-	return (fstat(fd, &statb) == 0 && !S_ISREG(statb.st_mode) ?
-	    SHF_UNBUF : 0);
-}
-
-#ifdef DF
-int shl_dbg_fd;
-#define NSHF_IOB 4
-#else
-#define NSHF_IOB 3
-#endif
-struct shf shf_iob[NSHF_IOB];
-
-void
-initio(void)
-{
-#ifdef DF
-	const char *lfp;
-#endif
-
-	/* force buffer allocation */
-	shf_fdopen(1, SHF_WR, shl_stdout);
-	shf_fdopen(2, SHF_WR, shl_out);
-	shf_fdopen(2, SHF_WR, shl_xtrace);
-#ifdef DF
-	if ((lfp = getenv("SDMKSH_PATH")) == NULL) {
-		if ((lfp = getenv("HOME")) == NULL || *lfp != '/')
-			errorf("cannot get home directory");
-		lfp = shf_smprintf("%s/mksh-dbg.txt", lfp);
-	}
-
-	if ((shl_dbg_fd = open(lfp, O_WRONLY | O_APPEND | O_CREAT, 0600)) < 0)
-		errorf("cannot open debug output file %s", lfp);
-	if (shl_dbg_fd < FDBASE) {
-		int nfd;
-
-		nfd = fcntl(shl_dbg_fd, F_DUPFD, FDBASE);
-		close(shl_dbg_fd);
-		if ((shl_dbg_fd = nfd) == -1)
-			errorf("cannot dup debug output file");
-	}
-	fcntl(shl_dbg_fd, F_SETFD, FD_CLOEXEC);
-	shf_fdopen(shl_dbg_fd, SHF_WR, shl_dbg);
-	DF("=== open ===");
-#endif
-	initio_done = true;
-}
-
-/* A dup2() with error checking */
-int
-ksh_dup2(int ofd, int nfd, bool errok)
-{
-	int rv;
-
-	if (((rv = dup2(ofd, nfd)) < 0) && !errok && (errno != EBADF))
-		errorf("too many files open in shell");
-
-#ifdef __ultrix
-	/*XXX imake style */
-	if (rv >= 0)
-		fcntl(nfd, F_SETFD, 0);
-#endif
-
-	return (rv);
-}
-
-/*
- * Move fd from user space (0 <= fd < 10) to shell space (fd >= 10),
- * set close-on-exec flag. See FDBASE in sh.h, maybe 24 not 10 here.
- */
-short
-savefd(int fd)
-{
-	int nfd = fd;
-
-	if (fd < FDBASE && (nfd = fcntl(fd, F_DUPFD, FDBASE)) < 0 &&
-	    errno == EBADF)
-		return (-1);
-	if (nfd < 0 || nfd > SHRT_MAX)
-		errorf("too many files open in shell");
-	fcntl(nfd, F_SETFD, FD_CLOEXEC);
-	return ((short)nfd);
-}
-
-void
-restfd(int fd, int ofd)
-{
-	if (fd == 2)
-		shf_flush(&shf_iob[/* fd */ 2]);
-	if (ofd < 0)
-		/* original fd closed */
-		close(fd);
-	else if (fd != ofd) {
-		/*XXX: what to do if this dup fails? */
-		ksh_dup2(ofd, fd, true);
-		close(ofd);
-	}
-}
-
-void
-openpipe(int *pv)
-{
-	int lpv[2];
-
-	if (pipe(lpv) < 0)
-		errorf("can't create pipe - try again");
-	pv[0] = savefd(lpv[0]);
-	if (pv[0] != lpv[0])
-		close(lpv[0]);
-	pv[1] = savefd(lpv[1]);
-	if (pv[1] != lpv[1])
-		close(lpv[1]);
-}
-
-void
-closepipe(int *pv)
-{
-	close(pv[0]);
-	close(pv[1]);
-}
-
-/*
- * Called by iosetup() (deals with 2>&4, etc.), c_read, c_print to turn
- * a string (the X in 2>&X, read -uX, print -uX) into a file descriptor.
- */
-int
-check_fd(const char *name, int mode, const char **emsgp)
-{
-	int fd, fl;
-
-	if (name[0] == 'p' && !name[1])
-		return (coproc_getfd(mode, emsgp));
-	for (fd = 0; ksh_isdigit(*name); ++name)
-		fd = (fd * 10) + *name - '0';
-	if (*name || fd >= FDBASE) {
-		if (emsgp)
-			*emsgp = "illegal file descriptor name";
-		return (-1);
-	}
-	if ((fl = fcntl(fd, F_GETFL, 0)) < 0) {
-		if (emsgp)
-			*emsgp = "bad file descriptor";
-		return (-1);
-	}
-	fl &= O_ACCMODE;
-	/*
-	 * X_OK is a kludge to disable this check for dups (x<&1):
-	 * historical shells never did this check (XXX don't know what
-	 * POSIX has to say).
-	 */
-	if (!(mode & X_OK) && fl != O_RDWR && (
-	    ((mode & R_OK) && fl != O_RDONLY) ||
-	    ((mode & W_OK) && fl != O_WRONLY))) {
-		if (emsgp)
-			*emsgp = (fl == O_WRONLY) ?
-			    "fd not open for reading" :
-			    "fd not open for writing";
-		return (-1);
-	}
-	return (fd);
-}
-
-/* Called once from main */
-void
-coproc_init(void)
-{
-	coproc.read = coproc.readw = coproc.write = -1;
-	coproc.njobs = 0;
-	coproc.id = 0;
-}
-
-/* Called by c_read() when eof is read - close fd if it is the co-process fd */
-void
-coproc_read_close(int fd)
-{
-	if (coproc.read >= 0 && fd == coproc.read) {
-		coproc_readw_close(fd);
-		close(coproc.read);
-		coproc.read = -1;
-	}
-}
-
-/*
- * Called by c_read() and by iosetup() to close the other side of the
- * read pipe, so reads will actually terminate.
- */
-void
-coproc_readw_close(int fd)
-{
-	if (coproc.readw >= 0 && coproc.read >= 0 && fd == coproc.read) {
-		close(coproc.readw);
-		coproc.readw = -1;
-	}
-}
-
-/*
- * Called by c_print when a write to a fd fails with EPIPE and by iosetup
- * when co-process input is dup'd
- */
-void
-coproc_write_close(int fd)
-{
-	if (coproc.write >= 0 && fd == coproc.write) {
-		close(coproc.write);
-		coproc.write = -1;
-	}
-}
-
-/*
- * Called to check for existence of/value of the co-process file descriptor.
- * (Used by check_fd() and by c_read/c_print to deal with -p option).
- */
-int
-coproc_getfd(int mode, const char **emsgp)
-{
-	int fd = (mode & R_OK) ? coproc.read : coproc.write;
-
-	if (fd >= 0)
-		return (fd);
-	if (emsgp)
-		*emsgp = "no coprocess";
-	return (-1);
-}
-
-/*
- * called to close file descriptors related to the coprocess (if any)
- * Should be called with SIGCHLD blocked.
- */
-void
-coproc_cleanup(int reuse)
-{
-	/* This to allow co-processes to share output pipe */
-	if (!reuse || coproc.readw < 0 || coproc.read < 0) {
-		if (coproc.read >= 0) {
-			close(coproc.read);
-			coproc.read = -1;
-		}
-		if (coproc.readw >= 0) {
-			close(coproc.readw);
-			coproc.readw = -1;
-		}
-	}
-	if (coproc.write >= 0) {
-		close(coproc.write);
-		coproc.write = -1;
-	}
-}
-
-struct temp *
-maketemp(Area *ap, Temp_type type, struct temp **tlist)
-{
-	char *cp;
-	size_t len;
-	int i, j;
-	struct temp *tp;
-	const char *dir;
-	struct stat sb;
-
-	dir = tmpdir ? tmpdir : MKSH_DEFAULT_TMPDIR;
-	/* add "/shXXXXXX.tmp" plus NUL */
-	len = strlen(dir);
-	checkoktoadd(len, offsetof(struct temp, tffn[0]) + 14);
-	tp = alloc(offsetof(struct temp, tffn[0]) + 14 + len, ap);
-
-	tp->shf = NULL;
-	tp->pid = procpid;
-	tp->type = type;
-
-	if (stat(dir, &sb) || !S_ISDIR(sb.st_mode)) {
-		tp->tffn[0] = '\0';
-		goto maketemp_out;
-	}
-
-	cp = (void *)tp;
-	cp += offsetof(struct temp, tffn[0]);
-	memcpy(cp, dir, len);
-	cp += len;
-	memcpy(cp, "/shXXXXXX.tmp", 14);
-	/* point to the first of six Xes */
-	cp += 3;
-	/* generate random part of filename */
-	len = -1;
-	do {
-		i = rndget() % 36;
-		cp[++len] = i < 26 ? 'a' + i : '0' + i - 26;
-	} while (len < 5);
-
-	/* cyclically attempt to open a temporary file */
-	while ((i = open(tp->tffn, O_CREAT | O_EXCL | O_RDWR, 0600)) < 0) {
-		if (errno != EEXIST)
-			goto maketemp_out;
-		/* count down from z to a then from 9 to 0 */
-		while (cp[len] == '0')
-			if (!len--)
-				goto maketemp_out;
-		if (cp[len] == 'a')
-			cp[len] = '9';
-		else
-			--cp[len];
-		/* do another cycle */
-	}
-
-	if (type == TT_FUNSUB) {
-		/* map us high and mark as close-on-exec */
-		if ((j = savefd(i)) != i) {
-			close(i);
-			i = j;
-		}
-
-		/* operation mode for the shf */
-		j = SHF_RD;
-	} else
-		j = SHF_WR;
-
-	/* shf_fdopen cannot fail, so no fd leak */
-	tp->shf = shf_fdopen(i, j, NULL);
-
- maketemp_out:
-	tp->next = *tlist;
-	*tlist = tp;
-	return (tp);
-}
-
-/*
- * We use a similar collision resolution algorithm as Python 2.5.4
- * but with a slightly tweaked implementation written from scratch.
- */
-
-#define	INIT_TBLSHIFT	3	/* initial table shift (2^3 = 8) */
-#define PERTURB_SHIFT	5	/* see Python 2.5.4 Objects/dictobject.c */
-
-static void tgrow(struct table *);
-static int tnamecmp(const void *, const void *);
-
-static void
-tgrow(struct table *tp)
-{
-	size_t i, j, osize, mask, perturb;
-	struct tbl *tblp, **pp;
-	struct tbl **ntblp, **otblp = tp->tbls;
-
-	if (tp->tshift > 29)
-		internal_errorf("hash table size limit reached");
-
-	/* calculate old size, new shift and new size */
-	osize = (size_t)1 << (tp->tshift++);
-	i = osize << 1;
-
-	ntblp = alloc2(i, sizeof(struct tbl *), tp->areap);
-	/* multiplication cannot overflow: alloc2 checked that */
-	memset(ntblp, 0, i * sizeof(struct tbl *));
-
-	/* table can get very full when reaching its size limit */
-	tp->nfree = (tp->tshift == 30) ? 0x3FFF0000UL :
-	    /* but otherwise, only 75% */
-	    ((i * 3) / 4);
-	tp->tbls = ntblp;
-	if (otblp == NULL)
-		return;
-
-	mask = i - 1;
-	for (i = 0; i < osize; i++)
-		if ((tblp = otblp[i]) != NULL) {
-			if ((tblp->flag & DEFINED)) {
-				/* search for free hash table slot */
-				j = perturb = tblp->ua.hval;
-				goto find_first_empty_slot;
- find_next_empty_slot:
-				j = (j << 2) + j + perturb + 1;
-				perturb >>= PERTURB_SHIFT;
- find_first_empty_slot:
-				pp = &ntblp[j & mask];
-				if (*pp != NULL)
-					goto find_next_empty_slot;
-				/* found an empty hash table slot */
-				*pp = tblp;
-				tp->nfree--;
-			} else if (!(tblp->flag & FINUSE)) {
-				afree(tblp, tp->areap);
-			}
-		}
-	afree(otblp, tp->areap);
-}
-
-void
-ktinit(Area *ap, struct table *tp, uint8_t initshift)
-{
-	tp->areap = ap;
-	tp->tbls = NULL;
-	tp->tshift = ((initshift > INIT_TBLSHIFT) ?
-	    initshift : INIT_TBLSHIFT) - 1;
-	tgrow(tp);
-}
-
-/* table, name (key) to search for, hash(name), rv pointer to tbl ptr */
-struct tbl *
-ktscan(struct table *tp, const char *name, uint32_t h, struct tbl ***ppp)
-{
-	size_t j, perturb, mask;
-	struct tbl **pp, *p;
-
-	mask = ((size_t)1 << (tp->tshift)) - 1;
-	/* search for hash table slot matching name */
-	j = perturb = h;
-	goto find_first_slot;
- find_next_slot:
-	j = (j << 2) + j + perturb + 1;
-	perturb >>= PERTURB_SHIFT;
- find_first_slot:
-	pp = &tp->tbls[j & mask];
-	if ((p = *pp) != NULL && (p->ua.hval != h || !(p->flag & DEFINED) ||
-	    strcmp(p->name, name)))
-		goto find_next_slot;
-	/* p == NULL if not found, correct found entry otherwise */
-	if (ppp)
-		*ppp = pp;
-	return (p);
-}
-
-/* table, name (key) to enter, hash(n) */
-struct tbl *
-ktenter(struct table *tp, const char *n, uint32_t h)
-{
-	struct tbl **pp, *p;
-	size_t len;
-
- Search:
-	if ((p = ktscan(tp, n, h, &pp)))
-		return (p);
-
-	if (tp->nfree == 0) {
-		/* too full */
-		tgrow(tp);
-		goto Search;
-	}
-
-	/* create new tbl entry */
-	len = strlen(n);
-	checkoktoadd(len, offsetof(struct tbl, name[0]) + 1);
-	p = alloc(offsetof(struct tbl, name[0]) + ++len, tp->areap);
-	p->flag = 0;
-	p->type = 0;
-	p->areap = tp->areap;
-	p->ua.hval = h;
-	p->u2.field = 0;
-	p->u.array = NULL;
-	memcpy(p->name, n, len);
-
-	/* enter in tp->tbls */
-	tp->nfree--;
-	*pp = p;
-	return (p);
-}
-
-void
-ktwalk(struct tstate *ts, struct table *tp)
-{
-	ts->left = (size_t)1 << (tp->tshift);
-	ts->next = tp->tbls;
-}
-
-struct tbl *
-ktnext(struct tstate *ts)
-{
-	while (--ts->left >= 0) {
-		struct tbl *p = *ts->next++;
-		if (p != NULL && (p->flag & DEFINED))
-			return (p);
-	}
-	return (NULL);
-}
-
-static int
-tnamecmp(const void *p1, const void *p2)
-{
-	const struct tbl *a = *((const struct tbl * const *)p1);
-	const struct tbl *b = *((const struct tbl * const *)p2);
-
-	return (strcmp(a->name, b->name));
-}
-
-struct tbl **
-ktsort(struct table *tp)
-{
-	size_t i;
-	struct tbl **p, **sp, **dp;
-
-	/*
-	 * since the table is never entirely full, no need to reserve
-	 * additional space for the trailing NULL appended below
-	 */
-	i = (size_t)1 << (tp->tshift);
-	p = alloc2(i, sizeof(struct tbl *), ATEMP);
-	sp = tp->tbls;		/* source */
-	dp = p;			/* dest */
-	while (i--)
-		if ((*dp = *sp++) != NULL && (((*dp)->flag & DEFINED) ||
-		    ((*dp)->flag & ARRAY)))
-			dp++;
-	qsort(p, (i = dp - p), sizeof(struct tbl *), tnamecmp);
-	p[i] = NULL;
-	return (p);
-}
-
-#ifdef SIGWINCH
-static void
-x_sigwinch(int sig MKSH_A_UNUSED)
-{
-	/* this runs inside interrupt context, with errno saved */
-
-	got_winch = 1;
-}
-#endif
-
-#ifdef DF
-void
-DF(const char *fmt, ...)
-{
-	va_list args;
-	struct timeval tv;
-	mirtime_mjd mjd;
-
-	mksh_lockfd(shl_dbg_fd);
-	mksh_TIME(tv);
-	timet2mjd(&mjd, tv.tv_sec);
-	shf_fprintf(shl_dbg, "[%02u:%02u:%02u (%u) %u.%06u] ",
-	    (unsigned)mjd.sec / 3600, ((unsigned)mjd.sec / 60) % 60,
-	    (unsigned)mjd.sec % 60, (unsigned)getpid(),
-	    (unsigned)tv.tv_sec, (unsigned)tv.tv_usec);
-	va_start(args, fmt);
-	shf_vfprintf(shl_dbg, fmt, args);
-	va_end(args);
-	shf_putc('\n', shl_dbg);
-	shf_flush(shl_dbg);
-	mksh_unlkfd(shl_dbg_fd);
-}
-#endif
-
-void
-x_mkraw(int fd, mksh_ttyst *ocb, bool forread)
-{
-	mksh_ttyst cb;
-
-	if (ocb)
-		mksh_tcget(fd, ocb);
-	else
-		ocb = &tty_state;
-
-	cb = *ocb;
-	if (forread) {
-		cb.c_lflag &= ~(ICANON) | ECHO;
-	} else {
-		cb.c_iflag &= ~(INLCR | ICRNL);
-		cb.c_lflag &= ~(ISIG | ICANON | ECHO);
-	}
-#if defined(VLNEXT) && defined(_POSIX_VDISABLE)
-	/* OSF/1 processes lnext when ~icanon */
-	cb.c_cc[VLNEXT] = _POSIX_VDISABLE;
-#endif
-	/* SunOS 4.1.x & OSF/1 processes discard(flush) when ~icanon */
-#if defined(VDISCARD) && defined(_POSIX_VDISABLE)
-	cb.c_cc[VDISCARD] = _POSIX_VDISABLE;
-#endif
-	cb.c_cc[VTIME] = 0;
-	cb.c_cc[VMIN] = 1;
-
-	mksh_tcset(fd, &cb);
-}

Copied: vendor/MirOS/mksh/R50/main.c (from rev 6707, vendor/MirOS/mksh/dist/main.c)
===================================================================
--- vendor/MirOS/mksh/R50/main.c	                        (rev 0)
+++ vendor/MirOS/mksh/R50/main.c	2014-07-01 12:01:17 UTC (rev 6708)
@@ -0,0 +1,1888 @@
+/*	$OpenBSD: main.c,v 1.54 2013/11/28 10:33:37 sobrado Exp $	*/
+/*	$OpenBSD: tty.c,v 1.9 2006/03/14 22:08:01 deraadt Exp $	*/
+/*	$OpenBSD: io.c,v 1.23 2013/12/17 16:37:06 deraadt Exp $	*/
+/*	$OpenBSD: table.c,v 1.15 2012/02/19 07:52:30 otto Exp $	*/
+
+/*-
+ * Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
+ *		 2011, 2012, 2013, 2014
+ *	Thorsten Glaser <tg at mirbsd.org>
+ *
+ * Provided that these terms and disclaimer and all copyright notices
+ * are retained or reproduced in an accompanying document, permission
+ * is granted to deal in this work without restriction, including un-
+ * limited rights to use, publicly perform, distribute, sell, modify,
+ * merge, give away, or sublicence.
+ *
+ * This work is provided "AS IS" and WITHOUT WARRANTY of any kind, to
+ * the utmost extent permitted by applicable law, neither express nor
+ * implied; without malicious intent or gross negligence. In no event
+ * may a licensor, author or contributor be held liable for indirect,
+ * direct, other damage, loss, or other issues arising in any way out
+ * of dealing in the work, even if advised of the possibility of such
+ * damage or existence of a defect, except proven that it results out
+ * of said person's immediate fault when using the work as intended.
+ */
+
+#define EXTERN
+#include "sh.h"
+
+#if HAVE_LANGINFO_CODESET
+#include <langinfo.h>
+#endif
+#if HAVE_SETLOCALE_CTYPE
+#include <locale.h>
+#endif
+
+__RCSID("$MirOS: src/bin/mksh/main.c,v 1.280 2014/06/09 12:28:17 tg Exp $");
+
+extern char **environ;
+
+#ifndef MKSHRC_PATH
+#define MKSHRC_PATH	"~/.mkshrc"
+#endif
+
+#ifndef MKSH_DEFAULT_TMPDIR
+#define MKSH_DEFAULT_TMPDIR	"/tmp"
+#endif
+
+static uint8_t isuc(const char *);
+static int main_init(int, const char *[], Source **, struct block **);
+void chvt_reinit(void);
+static void reclaim(void);
+static void remove_temps(struct temp *);
+static mksh_uari_t rndsetup(void);
+#ifdef SIGWINCH
+static void x_sigwinch(int);
+#endif
+
+static const char initifs[] = "IFS= \t\n";
+
+static const char initsubs[] =
+    "${PS2=> } ${PS3=#? } ${PS4=+ } ${SECONDS=0} ${TMOUT=0} ${EPOCHREALTIME=}";
+
+static const char *initcoms[] = {
+	Ttypeset, "-r", initvsn, NULL,
+	Ttypeset, "-x", "HOME", "PATH", "RANDOM", "SHELL", NULL,
+	Ttypeset, "-i10", "COLUMNS", "LINES", "SECONDS", "TMOUT", NULL,
+	Talias,
+	"integer=typeset -i",
+	Tlocal_typeset,
+	/* not "alias -t --": hash -r needs to work */
+	"hash=alias -t",
+	"type=whence -v",
+#if !defined(ANDROID) && !defined(MKSH_UNEMPLOYED)
+	/* not in Android for political reasons */
+	/* not in ARGE mksh due to no job control */
+	"stop=kill -STOP",
+#endif
+	"autoload=typeset -fu",
+	"functions=typeset -f",
+	"history=fc -l",
+	"nameref=typeset -n",
+	"nohup=nohup ",
+	Tr_fc_e_dash,
+	"source=PATH=$PATH:. command .",
+	"login=exec login",
+	NULL,
+	 /* this is what AT&T ksh seems to track, with the addition of emacs */
+	Talias, "-tU",
+	"cat", "cc", "chmod", "cp", "date", "ed", "emacs", "grep", "ls",
+	"make", "mv", "pr", "rm", "sed", "sh", "vi", "who", NULL,
+	NULL
+};
+
+static const char *restr_com[] = {
+	Ttypeset, "-r", "PATH", "ENV", "SHELL", NULL
+};
+
+static bool initio_done;
+
+/* top-level parsing and execution environment */
+static struct env env;
+struct env *e = &env;
+
+static mksh_uari_t
+rndsetup(void)
+{
+	register uint32_t h;
+	struct {
+		ALLOC_ITEM alloc_INT;
+		void *dataptr, *stkptr, *mallocptr;
+#if defined(__GLIBC__) && (__GLIBC__ >= 2)
+		sigjmp_buf jbuf;
+#endif
+		struct timeval tv;
+	} *bufptr;
+	char *cp;
+
+	cp = alloc(sizeof(*bufptr) - ALLOC_SIZE, APERM);
+#ifdef DEBUG
+	/* clear the allocated space, for valgrind */
+	memset(cp, 0, sizeof(*bufptr) - ALLOC_SIZE);
+#endif
+	/* undo what alloc() did to the malloc result address */
+	bufptr = (void *)(cp - ALLOC_SIZE);
+	/* PIE or something similar provides us with deltas here */
+	bufptr->dataptr = &rndsetupstate;
+	/* ASLR in at least Windows, Linux, some BSDs */
+	bufptr->stkptr = &bufptr;
+	/* randomised malloc in BSD (and possibly others) */
+	bufptr->mallocptr = bufptr;
+#if defined(__GLIBC__) && (__GLIBC__ >= 2)
+	/* glibc pointer guard */
+	sigsetjmp(bufptr->jbuf, 1);
+#endif
+	/* introduce variation (and yes, second arg MBZ for portability) */
+	mksh_TIME(bufptr->tv);
+
+	h = chvt_rndsetup(bufptr, sizeof(*bufptr));
+
+	afree(cp, APERM);
+	return ((mksh_uari_t)h);
+}
+
+void
+chvt_reinit(void)
+{
+	kshpid = procpid = getpid();
+	ksheuid = geteuid();
+	kshpgrp = getpgrp();
+	kshppid = getppid();
+}
+
+static const char *empty_argv[] = {
+	"mksh", NULL
+};
+
+static uint8_t
+isuc(const char *cx) {
+	char *cp, *x;
+	uint8_t rv = 0;
+
+	if (!cx || !*cx)
+		return (0);
+
+	/* uppercase a string duplicate */
+	strdupx(x, cx, ATEMP);
+	cp = x;
+	while ((*cp = ksh_toupper(*cp)))
+		++cp;
+
+	/* check for UTF-8 */
+	if (strstr(x, "UTF-8") || strstr(x, "UTF8"))
+		rv = 1;
+
+	/* free copy and out */
+	afree(x, ATEMP);
+	return (rv);
+}
+
+static int
+main_init(int argc, const char *argv[], Source **sp, struct block **lp)
+{
+	int argi, i;
+	Source *s = NULL;
+	struct block *l;
+	unsigned char restricted, errexit, utf_flag;
+	char *cp;
+	const char *ccp, **wp;
+	struct tbl *vp;
+	struct stat s_stdin;
+#if !defined(_PATH_DEFPATH) && defined(_CS_PATH)
+	ssize_t k;
+#endif
+
+	/* do things like getpgrp() et al. */
+	chvt_reinit();
+
+	/* make sure argv[] is sane */
+	if (!*argv) {
+		argv = empty_argv;
+		argc = 1;
+	}
+	kshname = argv[0];
+
+	/* initialise permanent Area */
+	ainit(&aperm);
+
+	/* set up base environment */
+	env.type = E_NONE;
+	ainit(&env.area);
+	/* set up global l->vars and l->funs */
+	newblock();
+
+	/* Do this first so output routines (eg, errorf, shellf) can work */
+	initio();
+
+	/* determine the basename (without '-' or path) of the executable */
+	ccp = kshname;
+	goto begin_parse_kshname;
+	while ((i = ccp[argi++])) {
+		if (i == '/') {
+			ccp += argi;
+ begin_parse_kshname:
+			argi = 0;
+			if (*ccp == '-')
+				++ccp;
+		}
+	}
+	if (!*ccp)
+		ccp = empty_argv[0];
+
+	/*
+	 * Turn on nohup by default. (AT&T ksh does not have a nohup
+	 * option - it always sends the hup).
+	 */
+	Flag(FNOHUP) = 1;
+
+	/*
+	 * Turn on brace expansion by default. AT&T kshs that have
+	 * alternation always have it on.
+	 */
+	Flag(FBRACEEXPAND) = 1;
+
+	/*
+	 * Turn on "set -x" inheritance by default.
+	 */
+	Flag(FXTRACEREC) = 1;
+
+	/* define built-in commands and see if we were called as one */
+	ktinit(APERM, &builtins,
+	    /* currently up to 51 builtins: 75% of 128 = 2^7 */
+	    7);
+	for (i = 0; mkshbuiltins[i].name != NULL; i++)
+		if (!strcmp(ccp, builtin(mkshbuiltins[i].name,
+		    mkshbuiltins[i].func)))
+			Flag(FAS_BUILTIN) = 1;
+
+	if (!Flag(FAS_BUILTIN)) {
+		/* check for -T option early */
+		argi = parse_args(argv, OF_FIRSTTIME, NULL);
+		if (argi < 0)
+			return (1);
+
+#if defined(MKSH_BINSHPOSIX) || defined(MKSH_BINSHREDUCED)
+		/* are we called as -sh or /bin/sh or so? */
+		if (!strcmp(ccp, "sh")) {
+			/* either also turns off braceexpand */
+#ifdef MKSH_BINSHPOSIX
+			/* enable better POSIX conformance */
+			change_flag(FPOSIX, OF_FIRSTTIME, true);
+#endif
+#ifdef MKSH_BINSHREDUCED
+			/* enable kludge/compat mode */
+			change_flag(FSH, OF_FIRSTTIME, true);
+#endif
+		}
+#endif
+	}
+
+	initvar();
+
+	initctypes();
+
+	inittraps();
+
+	coproc_init();
+
+	/* set up variable and command dictionaries */
+	ktinit(APERM, &taliases, 0);
+	ktinit(APERM, &aliases, 0);
+#ifndef MKSH_NOPWNAM
+	ktinit(APERM, &homedirs, 0);
+#endif
+
+	/* define shell keywords */
+	initkeywords();
+
+	init_histvec();
+
+	/* initialise tty size before importing environment */
+	change_winsz();
+
+#ifdef _PATH_DEFPATH
+	def_path = _PATH_DEFPATH;
+#else
+#ifdef _CS_PATH
+	if ((k = confstr(_CS_PATH, NULL, 0)) > 0 &&
+	    confstr(_CS_PATH, cp = alloc(k + 1, APERM), k + 1) == k + 1)
+		def_path = cp;
+	else
+#endif
+		/*
+		 * this is uniform across all OSes unless it
+		 * breaks somewhere; don't try to optimise,
+		 * e.g. add stuff for Interix or remove /usr
+		 * for HURD, because e.g. Debian GNU/HURD is
+		 * "keeping a regular /usr"; this is supposed
+		 * to be a sane 'basic' default PATH
+		 */
+		def_path = "/bin:/usr/bin:/sbin:/usr/sbin";
+#endif
+
+	/*
+	 * Set PATH to def_path (will set the path global variable).
+	 * (import of environment below will probably change this setting).
+	 */
+	vp = global("PATH");
+	/* setstr can't fail here */
+	setstr(vp, def_path, KSH_RETURN_ERROR);
+
+#ifndef MKSH_NO_CMDLINE_EDITING
+	/*
+	 * Set edit mode to emacs by default, may be overridden
+	 * by the environment or the user. Also, we want tab completion
+	 * on in vi by default.
+	 */
+	change_flag(FEMACS, OF_SPECIAL, true);
+#if !MKSH_S_NOVI
+	Flag(FVITABCOMPLETE) = 1;
+#endif
+#endif
+
+	/* import environment */
+	if (environ != NULL) {
+		wp = (const char **)environ;
+		while (*wp != NULL) {
+			rndpush(*wp);
+			typeset(*wp, IMPORT | EXPORT, 0, 0, 0);
+			++wp;
+		}
+	}
+
+	/* for security */
+	typeset(initifs, 0, 0, 0, 0);
+
+	/* assign default shell variable values */
+	substitute(initsubs, 0);
+
+	/* Figure out the current working directory and set $PWD */
+	vp = global("PWD");
+	cp = str_val(vp);
+	/* Try to use existing $PWD if it is valid */
+	set_current_wd((cp[0] == '/' && test_eval(NULL, TO_FILEQ, cp, ".",
+	    true)) ? cp : NULL);
+	if (current_wd[0])
+		simplify_path(current_wd);
+	/* Only set pwd if we know where we are or if it had a bogus value */
+	if (current_wd[0] || *cp)
+		/* setstr can't fail here */
+		setstr(vp, current_wd, KSH_RETURN_ERROR);
+
+	for (wp = initcoms; *wp != NULL; wp++) {
+		shcomexec(wp);
+		while (*wp != NULL)
+			wp++;
+	}
+	setint_n(global("OPTIND"), 1, 10);
+
+	kshuid = getuid();
+	kshgid = getgid();
+	kshegid = getegid();
+
+	safe_prompt = ksheuid ? "$ " : "# ";
+	vp = global("PS1");
+	/* Set PS1 if unset or we are root and prompt doesn't contain a # */
+	if (!(vp->flag & ISSET) ||
+	    (!ksheuid && !strchr(str_val(vp), '#')))
+		/* setstr can't fail here */
+		setstr(vp, safe_prompt, KSH_RETURN_ERROR);
+	setint_n((vp = global("BASHPID")), 0, 10);
+	vp->flag |= INT_U;
+	setint_n((vp = global("PGRP")), (mksh_uari_t)kshpgrp, 10);
+	vp->flag |= INT_U;
+	setint_n((vp = global("PPID")), (mksh_uari_t)kshppid, 10);
+	vp->flag |= INT_U;
+	setint_n((vp = global("USER_ID")), (mksh_uari_t)ksheuid, 10);
+	vp->flag |= INT_U;
+	setint_n((vp = global("KSHUID")), (mksh_uari_t)kshuid, 10);
+	vp->flag |= INT_U;
+	setint_n((vp = global("KSHEGID")), (mksh_uari_t)kshegid, 10);
+	vp->flag |= INT_U;
+	setint_n((vp = global("KSHGID")), (mksh_uari_t)kshgid, 10);
+	vp->flag |= INT_U;
+	setint_n((vp = global("RANDOM")), rndsetup(), 10);
+	vp->flag |= INT_U;
+	setint_n((vp_pipest = global("PIPESTATUS")), 0, 10);
+
+	/* Set this before parsing arguments */
+	Flag(FPRIVILEGED) = (kshuid != ksheuid || kshgid != kshegid) ? 2 : 0;
+
+	/* this to note if monitor is set on command line (see below) */
+#ifndef MKSH_UNEMPLOYED
+	Flag(FMONITOR) = 127;
+#endif
+	/* this to note if utf-8 mode is set on command line (see below) */
+	UTFMODE = 2;
+
+	if (!Flag(FAS_BUILTIN)) {
+		argi = parse_args(argv, OF_CMDLINE, NULL);
+		if (argi < 0)
+			return (1);
+	}
+
+	/* process this later only, default to off (hysterical raisins) */
+	utf_flag = UTFMODE;
+	UTFMODE = 0;
+
+	if (Flag(FAS_BUILTIN)) {
+		/* auto-detect from environment variables, always */
+		utf_flag = 3;
+	} else if (Flag(FCOMMAND)) {
+		s = pushs(SSTRINGCMDLINE, ATEMP);
+		if (!(s->start = s->str = argv[argi++]))
+			errorf("%s %s", "-c", "requires an argument");
+		while (*s->str) {
+			if (*s->str != ' ' && ctype(*s->str, C_QUOTE))
+				break;
+			s->str++;
+		}
+		if (!*s->str)
+			s->flags |= SF_MAYEXEC;
+		s->str = s->start;
+#ifdef MKSH_MIDNIGHTBSD01ASH_COMPAT
+		/* compatibility to MidnightBSD 0.1 /bin/sh (kludge) */
+		if (Flag(FSH) && argv[argi] && !strcmp(argv[argi], "--"))
+			++argi;
+#endif
+		if (argv[argi])
+			kshname = argv[argi++];
+	} else if (argi < argc && !Flag(FSTDIN)) {
+		s = pushs(SFILE, ATEMP);
+		s->file = argv[argi++];
+		s->u.shf = shf_open(s->file, O_RDONLY, 0,
+		    SHF_MAPHI | SHF_CLEXEC);
+		if (s->u.shf == NULL) {
+			shl_stdout_ok = false;
+			warningf(true, "%s: %s", s->file, cstrerror(errno));
+			/* mandated by SUSv4 */
+			exstat = 127;
+			unwind(LERROR);
+		}
+		kshname = s->file;
+	} else {
+		Flag(FSTDIN) = 1;
+		s = pushs(SSTDIN, ATEMP);
+		s->file = "<stdin>";
+		s->u.shf = shf_fdopen(0, SHF_RD | can_seek(0),
+		    NULL);
+		if (isatty(0) && isatty(2)) {
+			Flag(FTALKING) = Flag(FTALKING_I) = 1;
+			/* The following only if isatty(0) */
+			s->flags |= SF_TTY;
+			s->u.shf->flags |= SHF_INTERRUPT;
+			s->file = NULL;
+		}
+	}
+
+	/* this bizarreness is mandated by POSIX */
+	if (fstat(0, &s_stdin) >= 0 && S_ISCHR(s_stdin.st_mode) &&
+	    Flag(FTALKING))
+		reset_nonblock(0);
+
+	/* initialise job control */
+	j_init();
+	/* do this after j_init() which calls tty_init_state() */
+	if (Flag(FTALKING)) {
+		if (utf_flag == 2) {
+#ifndef MKSH_ASSUME_UTF8
+			/* auto-detect from locale or environment */
+			utf_flag = 4;
+#else /* this may not be an #elif */
+#if MKSH_ASSUME_UTF8
+			utf_flag = 1;
+#else
+			/* always disable UTF-8 (for interactive) */
+			utf_flag = 0;
+#endif
+#endif
+		}
+#ifndef MKSH_NO_CMDLINE_EDITING
+		x_init();
+#endif
+	}
+
+#ifdef SIGWINCH
+	sigtraps[SIGWINCH].flags |= TF_SHELL_USES;
+	setsig(&sigtraps[SIGWINCH], x_sigwinch,
+	    SS_RESTORE_ORIG|SS_FORCE|SS_SHTRAP);
+#endif
+
+	l = e->loc;
+	if (Flag(FAS_BUILTIN)) {
+		l->argc = argc;
+		l->argv = argv;
+		l->argv[0] = ccp;
+	} else {
+		l->argc = argc - argi;
+		/*
+		 * allocate a new array because otherwise, when we modify
+		 * it in-place, ps(1) output changes; the meaning of argc
+		 * here is slightly different as it excludes kshname, and
+		 * we add a trailing NULL sentinel as well
+		 */
+		l->argv = alloc2(l->argc + 2, sizeof(void *), APERM);
+		l->argv[0] = kshname;
+		memcpy(&l->argv[1], &argv[argi], l->argc * sizeof(void *));
+		l->argv[l->argc + 1] = NULL;
+		getopts_reset(1);
+	}
+
+	/* divine the initial state of the utf8-mode Flag */
+	ccp = null;
+	switch (utf_flag) {
+
+	/* auto-detect from locale or environment */
+	case 4:
+#if HAVE_SETLOCALE_CTYPE
+		ccp = setlocale(LC_CTYPE, "");
+#if HAVE_LANGINFO_CODESET
+		if (!isuc(ccp))
+			ccp = nl_langinfo(CODESET);
+#endif
+		if (!isuc(ccp))
+			ccp = null;
+		/* FALLTHROUGH */
+#endif
+
+	/* auto-detect from environment */
+	case 3:
+		/* these were imported from environ earlier */
+		if (ccp == null)
+			ccp = str_val(global("LC_ALL"));
+		if (ccp == null)
+			ccp = str_val(global("LC_CTYPE"));
+		if (ccp == null)
+			ccp = str_val(global("LANG"));
+		UTFMODE = isuc(ccp);
+		break;
+
+	/* not set on command line, not FTALKING */
+	case 2:
+	/* unknown values */
+	default:
+		utf_flag = 0;
+		/* FALLTHROUGH */
+
+	/* known values */
+	case 1:
+	case 0:
+		UTFMODE = utf_flag;
+		break;
+	}
+
+	/* Disable during .profile/ENV reading */
+	restricted = Flag(FRESTRICTED);
+	Flag(FRESTRICTED) = 0;
+	errexit = Flag(FERREXIT);
+	Flag(FERREXIT) = 0;
+
+	/*
+	 * Do this before profile/$ENV so that if it causes problems in them,
+	 * user will know why things broke.
+	 */
+	if (!current_wd[0] && Flag(FTALKING))
+		warningf(false, "can't determine current directory");
+
+	if (Flag(FLOGIN))
+		include(MKSH_SYSTEM_PROFILE, 0, NULL, true);
+	if (!Flag(FPRIVILEGED)) {
+		if (Flag(FLOGIN))
+			include(substitute("$HOME/.profile", 0), 0, NULL, true);
+		if (Flag(FTALKING)) {
+			cp = substitute(substitute("${ENV:-" MKSHRC_PATH "}",
+			    0), DOTILDE);
+			if (cp[0] != '\0')
+				include(cp, 0, NULL, true);
+		}
+	} else {
+		include(MKSH_SUID_PROFILE, 0, NULL, true);
+		/* turn off -p if not set explicitly */
+		if (Flag(FPRIVILEGED) != 1)
+			change_flag(FPRIVILEGED, OF_INTERNAL, false);
+	}
+
+	if (restricted) {
+		shcomexec(restr_com);
+		/* After typeset command... */
+		Flag(FRESTRICTED) = 1;
+	}
+	Flag(FERREXIT) = errexit;
+
+	if (Flag(FTALKING) && s)
+		hist_init(s);
+	else
+		/* set after ENV */
+		Flag(FTRACKALL) = 1;
+
+	alarm_init();
+
+	*sp = s;
+	*lp = l;
+	return (0);
+}
+
+/* this indirection barrier reduces stack usage during normal operation */
+
+int
+main(int argc, const char *argv[])
+{
+	int rv;
+	Source *s;
+	struct block *l;
+
+	if ((rv = main_init(argc, argv, &s, &l)) == 0) {
+		if (Flag(FAS_BUILTIN)) {
+			rv = shcomexec(l->argv);
+		} else {
+			shell(s, true);
+			/* NOTREACHED */
+		}
+	}
+	return (rv);
+}
+
+int
+include(const char *name, int argc, const char **argv, bool intr_ok)
+{
+	Source *volatile s = NULL;
+	struct shf *shf;
+	const char **volatile old_argv;
+	volatile int old_argc;
+	int i;
+
+	shf = shf_open(name, O_RDONLY, 0, SHF_MAPHI | SHF_CLEXEC);
+	if (shf == NULL)
+		return (-1);
+
+	if (argv) {
+		old_argv = e->loc->argv;
+		old_argc = e->loc->argc;
+	} else {
+		old_argv = NULL;
+		old_argc = 0;
+	}
+	newenv(E_INCL);
+	if ((i = kshsetjmp(e->jbuf))) {
+		quitenv(s ? s->u.shf : NULL);
+		if (old_argv) {
+			e->loc->argv = old_argv;
+			e->loc->argc = old_argc;
+		}
+		switch (i) {
+		case LRETURN:
+		case LERROR:
+			/* see below */
+			return (exstat & 0xFF);
+		case LINTR:
+			/*
+			 * intr_ok is set if we are including .profile or $ENV.
+			 * If user ^Cs out, we don't want to kill the shell...
+			 */
+			if (intr_ok && ((exstat & 0xFF) - 128) != SIGTERM)
+				return (1);
+			/* FALLTHROUGH */
+		case LEXIT:
+		case LLEAVE:
+		case LSHELL:
+			unwind(i);
+			/* NOTREACHED */
+		default:
+			internal_errorf("%s %d", "include", i);
+			/* NOTREACHED */
+		}
+	}
+	if (argv) {
+		e->loc->argv = argv;
+		e->loc->argc = argc;
+	}
+	s = pushs(SFILE, ATEMP);
+	s->u.shf = shf;
+	strdupx(s->file, name, ATEMP);
+	i = shell(s, false);
+	quitenv(s->u.shf);
+	if (old_argv) {
+		e->loc->argv = old_argv;
+		e->loc->argc = old_argc;
+	}
+	/* & 0xff to ensure value not -1 */
+	return (i & 0xFF);
+}
+
+/* spawn a command into a shell optionally keeping track of the line number */
+int
+command(const char *comm, int line)
+{
+	Source *s;
+
+	s = pushs(SSTRING, ATEMP);
+	s->start = s->str = comm;
+	s->line = line;
+	return (shell(s, false));
+}
+
+/*
+ * run the commands from the input source, returning status.
+ */
+int
+shell(Source * volatile s, volatile bool toplevel)
+{
+	struct op *t;
+	volatile bool wastty = tobool(s->flags & SF_TTY);
+	volatile uint8_t attempts = 13;
+	volatile bool interactive = Flag(FTALKING) && toplevel;
+	volatile bool sfirst = true;
+	Source *volatile old_source = source;
+	int i;
+
+	newenv(E_PARSE);
+	if (interactive)
+		really_exit = false;
+	switch ((i = kshsetjmp(e->jbuf))) {
+	case 0:
+		break;
+	case LINTR:
+		/* we get here if SIGINT not caught or ignored */
+	case LERROR:
+	case LSHELL:
+		if (interactive) {
+			if (i == LINTR)
+				shellf("\n");
+			/*
+			 * Reset any eof that was read as part of a
+			 * multiline command.
+			 */
+			if (Flag(FIGNOREEOF) && s->type == SEOF && wastty)
+				s->type = SSTDIN;
+			/*
+			 * Used by exit command to get back to
+			 * top level shell. Kind of strange since
+			 * interactive is set if we are reading from
+			 * a tty, but to have stopped jobs, one only
+			 * needs FMONITOR set (not FTALKING/SF_TTY)...
+			 */
+			/* toss any input we have so far */
+			yyrecursive_pop(true);
+			s->start = s->str = null;
+			retrace_info = NULL;
+			herep = heres;
+			break;
+		}
+		/* FALLTHROUGH */
+	case LEXIT:
+	case LLEAVE:
+	case LRETURN:
+		source = old_source;
+		quitenv(NULL);
+		/* keep on going */
+		unwind(i);
+		/* NOTREACHED */
+	default:
+		source = old_source;
+		quitenv(NULL);
+		internal_errorf("%s %d", "shell", i);
+		/* NOTREACHED */
+	}
+	while (/* CONSTCOND */ 1) {
+		if (trap)
+			runtraps(0);
+
+		if (s->next == NULL) {
+			if (Flag(FVERBOSE))
+				s->flags |= SF_ECHO;
+			else
+				s->flags &= ~SF_ECHO;
+		}
+		if (interactive) {
+			j_notify();
+			set_prompt(PS1, s);
+		}
+		t = compile(s, sfirst);
+		sfirst = false;
+		if (!t)
+			goto source_no_tree;
+		if (t->type == TEOF) {
+			if (wastty && Flag(FIGNOREEOF) && --attempts > 0) {
+				shellf("Use 'exit' to leave mksh\n");
+				s->type = SSTDIN;
+			} else if (wastty && !really_exit &&
+			    j_stopped_running()) {
+				really_exit = true;
+				s->type = SSTDIN;
+			} else {
+				/*
+				 * this for POSIX which says EXIT traps
+				 * shall be taken in the environment
+				 * immediately after the last command
+				 * executed.
+				 */
+				if (toplevel)
+					unwind(LEXIT);
+				break;
+			}
+		} else if ((s->flags & SF_MAYEXEC) && t->type == TCOM)
+			t->u.evalflags |= DOTCOMEXEC;
+		if (!Flag(FNOEXEC) || (s->flags & SF_TTY))
+			exstat = execute(t, 0, NULL) & 0xFF;
+
+		if (t->type != TEOF && interactive && really_exit)
+			really_exit = false;
+
+ source_no_tree:
+		reclaim();
+	}
+	quitenv(NULL);
+	source = old_source;
+	return (exstat & 0xFF);
+}
+
+/* return to closest error handler or shell(), exit if none found */
+/* note: i MUST NOT be 0 */
+void
+unwind(int i)
+{
+	/*
+	 * This is a kludge. We need to restore everything that was
+	 * changed in the new environment, see cid 1005090337C7A669439
+	 * and 10050903386452ACBF1, but fail to even save things most of
+	 * the time. funcs.c:c_eval() changes FERREXIT temporarily to 0,
+	 * which needs to be restored thus (related to Debian #696823).
+	 * We did not save the shell flags, so we use a special or'd
+	 * value here... this is mostly to clean up behind *other*
+	 * callers of unwind(LERROR) here; exec.c has the regular case.
+	 */
+	if (Flag(FERREXIT) & 0x80) {
+		/* GNU bash does not run this trapsig */
+		trapsig(ksh_SIGERR);
+		Flag(FERREXIT) &= ~0x80;
+	}
+
+	/* ordering for EXIT vs ERR is a bit odd (this is what AT&T ksh does) */
+	if (i == LEXIT || ((i == LERROR || i == LINTR) &&
+	    sigtraps[ksh_SIGEXIT].trap &&
+	    (!Flag(FTALKING) || Flag(FERREXIT)))) {
+		++trap_nested;
+		runtrap(&sigtraps[ksh_SIGEXIT], trap_nested == 1);
+		--trap_nested;
+		i = LLEAVE;
+	} else if (Flag(FERREXIT) == 1 && (i == LERROR || i == LINTR)) {
+		++trap_nested;
+		runtrap(&sigtraps[ksh_SIGERR], trap_nested == 1);
+		--trap_nested;
+		i = LLEAVE;
+	}
+
+	while (/* CONSTCOND */ 1) {
+		switch (e->type) {
+		case E_PARSE:
+		case E_FUNC:
+		case E_INCL:
+		case E_LOOP:
+		case E_ERRH:
+			kshlongjmp(e->jbuf, i);
+			/* NOTREACHED */
+		case E_NONE:
+			if (i == LINTR)
+				e->flags |= EF_FAKE_SIGDIE;
+			/* FALLTHROUGH */
+		default:
+			quitenv(NULL);
+		}
+	}
+}
+
+void
+newenv(int type)
+{
+	struct env *ep;
+	char *cp;
+
+	/*
+	 * struct env includes ALLOC_ITEM for alignment constraints
+	 * so first get the actually used memory, then assign it
+	 */
+	cp = alloc(sizeof(struct env) - ALLOC_SIZE, ATEMP);
+	/* undo what alloc() did to the malloc result address */
+	ep = (void *)(cp - ALLOC_SIZE);
+	/* initialise public members of struct env (not the ALLOC_ITEM) */
+	ainit(&ep->area);
+	ep->oenv = e;
+	ep->loc = e->loc;
+	ep->savefd = NULL;
+	ep->temps = NULL;
+	ep->yyrecursive_statep = NULL;
+	ep->type = type;
+	ep->flags = 0;
+	/* jump buffer is invalid because flags == 0 */
+	e = ep;
+}
+
+void
+quitenv(struct shf *shf)
+{
+	struct env *ep = e;
+	char *cp;
+	int fd;
+
+	yyrecursive_pop(true);
+	while (ep->oenv && ep->oenv->loc != ep->loc)
+		popblock();
+	if (ep->savefd != NULL) {
+		for (fd = 0; fd < NUFILE; fd++)
+			/* if ep->savefd[fd] < 0, means fd was closed */
+			if (ep->savefd[fd])
+				restfd(fd, ep->savefd[fd]);
+		if (ep->savefd[2])
+			/* Clear any write errors */
+			shf_reopen(2, SHF_WR, shl_out);
+	}
+	/*
+	 * Bottom of the stack.
+	 * Either main shell is exiting or cleanup_parents_env() was called.
+	 */
+	if (ep->oenv == NULL) {
+#ifdef DEBUG_LEAKS
+		int i;
+#endif
+
+		if (ep->type == E_NONE) {
+			/* Main shell exiting? */
+#if HAVE_PERSISTENT_HISTORY
+			if (Flag(FTALKING))
+				hist_finish();
+#endif
+			j_exit();
+			if (ep->flags & EF_FAKE_SIGDIE) {
+				int sig = (exstat & 0xFF) - 128;
+
+				/*
+				 * ham up our death a bit (AT&T ksh
+				 * only seems to do this for SIGTERM)
+				 * Don't do it for SIGQUIT, since we'd
+				 * dump a core..
+				 */
+				if ((sig == SIGINT || sig == SIGTERM) &&
+				    (kshpgrp == kshpid)) {
+					setsig(&sigtraps[sig], SIG_DFL,
+					    SS_RESTORE_CURR | SS_FORCE);
+					kill(0, sig);
+				}
+			}
+		}
+		if (shf)
+			shf_close(shf);
+		reclaim();
+#ifdef DEBUG_LEAKS
+#ifndef MKSH_NO_CMDLINE_EDITING
+		x_done();
+#endif
+#ifndef MKSH_NOPROSPECTOFWORK
+		/* block at least SIGCHLD during/after afreeall */
+		sigprocmask(SIG_BLOCK, &sm_sigchld, NULL);
+#endif
+		afreeall(APERM);
+		for (fd = 3; fd < NUFILE; fd++)
+			if ((i = fcntl(fd, F_GETFD, 0)) != -1 &&
+			    (i & FD_CLOEXEC))
+				close(fd);
+		close(2);
+		close(1);
+		close(0);
+#endif
+		exit(exstat & 0xFF);
+	}
+	if (shf)
+		shf_close(shf);
+	reclaim();
+
+	e = e->oenv;
+
+	/* free the struct env - tricky due to the ALLOC_ITEM inside */
+	cp = (void *)ep;
+	afree(cp + ALLOC_SIZE, ATEMP);
+}
+
+/* Called after a fork to cleanup stuff left over from parents environment */
+void
+cleanup_parents_env(void)
+{
+	struct env *ep;
+	int fd;
+
+	mkssert(e != NULL);
+
+	/*
+	 * Don't clean up temporary files - parent will probably need them.
+	 * Also, can't easily reclaim memory since variables, etc. could be
+	 * anywhere.
+	 */
+
+	/* close all file descriptors hiding in savefd */
+	for (ep = e; ep; ep = ep->oenv) {
+		if (ep->savefd) {
+			for (fd = 0; fd < NUFILE; fd++)
+				if (ep->savefd[fd] > 0)
+					close(ep->savefd[fd]);
+			afree(ep->savefd, &ep->area);
+			ep->savefd = NULL;
+		}
+#ifdef DEBUG_LEAKS
+		if (ep->type != E_NONE)
+			ep->type = E_GONE;
+#endif
+	}
+#ifndef DEBUG_LEAKS
+	e->oenv = NULL;
+#endif
+}
+
+/* Called just before an execve cleanup stuff temporary files */
+void
+cleanup_proc_env(void)
+{
+	struct env *ep;
+
+	for (ep = e; ep; ep = ep->oenv)
+		remove_temps(ep->temps);
+}
+
+/* remove temp files and free ATEMP Area */
+static void
+reclaim(void)
+{
+	struct block *l;
+
+	while ((l = e->loc) && (!e->oenv || e->oenv->loc != l)) {
+		e->loc = l->next;
+		afreeall(&l->area);
+	}
+
+	remove_temps(e->temps);
+	e->temps = NULL;
+	afreeall(&e->area);
+}
+
+static void
+remove_temps(struct temp *tp)
+{
+	for (; tp != NULL; tp = tp->next)
+		if (tp->pid == procpid)
+			unlink(tp->tffn);
+}
+
+/*
+ * Initialise tty_fd. Used for tracking the size of the terminal,
+ * saving/resetting tty modes upon forground job completion, and
+ * for setting up the tty process group. Return values:
+ *	0 = got controlling tty
+ *	1 = got terminal but no controlling tty
+ *	2 = cannot find a terminal
+ *	3 = cannot dup fd
+ *	4 = cannot make fd close-on-exec
+ * An existing tty_fd is cached if no "better" one could be found,
+ * i.e. if tty_devtty was already set or the new would not set it.
+ */
+int
+tty_init_fd(void)
+{
+	int fd, rv, eno = 0;
+	bool do_close = false, is_devtty = true;
+
+	if (tty_devtty) {
+		/* already got a tty which is /dev/tty */
+		return (0);
+	}
+
+#ifdef _UWIN
+	/*XXX imake style */
+	if (isatty(3)) {
+		/* fd 3 on UWIN _is_ /dev/tty (or our controlling tty) */
+		fd = 3;
+		goto got_fd;
+	}
+#endif
+	if ((fd = open("/dev/tty", O_RDWR, 0)) >= 0) {
+		do_close = true;
+		goto got_fd;
+	}
+	eno = errno;
+
+	if (tty_fd >= 0) {
+		/* already got a non-devtty one */
+		rv = 1;
+		goto out;
+	}
+	is_devtty = false;
+
+	if (isatty((fd = 0)) || isatty((fd = 2)))
+		goto got_fd;
+	/* cannot find one */
+	rv = 2;
+	/* assert: do_close == false */
+	goto out;
+
+ got_fd:
+	if ((rv = fcntl(fd, F_DUPFD, FDBASE)) < 0) {
+		eno = errno;
+		rv = 3;
+		goto out;
+	}
+	if (fcntl(rv, F_SETFD, FD_CLOEXEC) < 0) {
+		eno = errno;
+		close(rv);
+		rv = 4;
+		goto out;
+	}
+	tty_fd = rv;
+	tty_devtty = is_devtty;
+	rv = eno = 0;
+ out:
+	if (do_close)
+		close(fd);
+	errno = eno;
+	return (rv);
+}
+
+/* A shell error occurred (eg, syntax error, etc.) */
+
+#define VWARNINGF_ERRORPREFIX	1
+#define VWARNINGF_FILELINE	2
+#define VWARNINGF_BUILTIN	4
+#define VWARNINGF_INTERNAL	8
+
+static void vwarningf(unsigned int, const char *, va_list)
+    MKSH_A_FORMAT(__printf__, 2, 0);
+
+static void
+vwarningf(unsigned int flags, const char *fmt, va_list ap)
+{
+	if (fmt) {
+		if (flags & VWARNINGF_INTERNAL)
+			shf_fprintf(shl_out, "internal error: ");
+		if (flags & VWARNINGF_ERRORPREFIX)
+			error_prefix(tobool(flags & VWARNINGF_FILELINE));
+		if ((flags & VWARNINGF_BUILTIN) &&
+		    /* not set when main() calls parse_args() */
+		    builtin_argv0 && builtin_argv0 != kshname)
+			shf_fprintf(shl_out, "%s: ", builtin_argv0);
+		shf_vfprintf(shl_out, fmt, ap);
+		shf_putchar('\n', shl_out);
+	}
+	shf_flush(shl_out);
+}
+
+void
+errorfx(int rc, const char *fmt, ...)
+{
+	va_list va;
+
+	exstat = rc;
+
+	/* debugging: note that stdout not valid */
+	shl_stdout_ok = false;
+
+	va_start(va, fmt);
+	vwarningf(VWARNINGF_ERRORPREFIX | VWARNINGF_FILELINE, fmt, va);
+	va_end(va);
+	unwind(LERROR);
+}
+
+void
+errorf(const char *fmt, ...)
+{
+	va_list va;
+
+	exstat = 1;
+
+	/* debugging: note that stdout not valid */
+	shl_stdout_ok = false;
+
+	va_start(va, fmt);
+	vwarningf(VWARNINGF_ERRORPREFIX | VWARNINGF_FILELINE, fmt, va);
+	va_end(va);
+	unwind(LERROR);
+}
+
+/* like errorf(), but no unwind is done */
+void
+warningf(bool fileline, const char *fmt, ...)
+{
+	va_list va;
+
+	va_start(va, fmt);
+	vwarningf(VWARNINGF_ERRORPREFIX | (fileline ? VWARNINGF_FILELINE : 0),
+	    fmt, va);
+	va_end(va);
+}
+
+/*
+ * Used by built-in utilities to prefix shell and utility name to message
+ * (also unwinds environments for special builtins).
+ */
+void
+bi_errorf(const char *fmt, ...)
+{
+	va_list va;
+
+	/* debugging: note that stdout not valid */
+	shl_stdout_ok = false;
+
+	exstat = 1;
+
+	va_start(va, fmt);
+	vwarningf(VWARNINGF_ERRORPREFIX | VWARNINGF_FILELINE |
+	    VWARNINGF_BUILTIN, fmt, va);
+	va_end(va);
+
+	/*
+	 * POSIX special builtins and ksh special builtins cause
+	 * non-interactive shells to exit.
+	 * XXX odd use of KEEPASN; also may not want LERROR here
+	 */
+	if (builtin_flag & SPEC_BI) {
+		builtin_argv0 = NULL;
+		unwind(LERROR);
+	}
+}
+
+/* Called when something that shouldn't happen does */
+void
+internal_errorf(const char *fmt, ...)
+{
+	va_list va;
+
+	va_start(va, fmt);
+	vwarningf(VWARNINGF_INTERNAL, fmt, va);
+	va_end(va);
+	unwind(LERROR);
+}
+
+void
+internal_warningf(const char *fmt, ...)
+{
+	va_list va;
+
+	va_start(va, fmt);
+	vwarningf(VWARNINGF_INTERNAL, fmt, va);
+	va_end(va);
+}
+
+/* used by error reporting functions to print "ksh: .kshrc[25]: " */
+void
+error_prefix(bool fileline)
+{
+	/* Avoid foo: foo[2]: ... */
+	if (!fileline || !source || !source->file ||
+	    strcmp(source->file, kshname) != 0)
+		shf_fprintf(shl_out, "%s: ", kshname + (*kshname == '-'));
+	if (fileline && source && source->file != NULL) {
+		shf_fprintf(shl_out, "%s[%d]: ", source->file,
+		    source->errline > 0 ? source->errline : source->line);
+		source->errline = 0;
+	}
+}
+
+/* printf to shl_out (stderr) with flush */
+void
+shellf(const char *fmt, ...)
+{
+	va_list va;
+
+	if (!initio_done)
+		/* shl_out may not be set up yet... */
+		return;
+	va_start(va, fmt);
+	shf_vfprintf(shl_out, fmt, va);
+	va_end(va);
+	shf_flush(shl_out);
+}
+
+/* printf to shl_stdout (stdout) */
+void
+shprintf(const char *fmt, ...)
+{
+	va_list va;
+
+	if (!shl_stdout_ok)
+		internal_errorf("shl_stdout not valid");
+	va_start(va, fmt);
+	shf_vfprintf(shl_stdout, fmt, va);
+	va_end(va);
+}
+
+/* test if we can seek backwards fd (returns 0 or SHF_UNBUF) */
+int
+can_seek(int fd)
+{
+	struct stat statb;
+
+	return (fstat(fd, &statb) == 0 && !S_ISREG(statb.st_mode) ?
+	    SHF_UNBUF : 0);
+}
+
+#ifdef DF
+int shl_dbg_fd;
+#define NSHF_IOB 4
+#else
+#define NSHF_IOB 3
+#endif
+struct shf shf_iob[NSHF_IOB];
+
+void
+initio(void)
+{
+#ifdef DF
+	const char *lfp;
+#endif
+
+	/* force buffer allocation */
+	shf_fdopen(1, SHF_WR, shl_stdout);
+	shf_fdopen(2, SHF_WR, shl_out);
+	shf_fdopen(2, SHF_WR, shl_xtrace);
+#ifdef DF
+	if ((lfp = getenv("SDMKSH_PATH")) == NULL) {
+		if ((lfp = getenv("HOME")) == NULL || *lfp != '/')
+			errorf("cannot get home directory");
+		lfp = shf_smprintf("%s/mksh-dbg.txt", lfp);
+	}
+
+	if ((shl_dbg_fd = open(lfp, O_WRONLY | O_APPEND | O_CREAT, 0600)) < 0)
+		errorf("cannot open debug output file %s", lfp);
+	if (shl_dbg_fd < FDBASE) {
+		int nfd;
+
+		nfd = fcntl(shl_dbg_fd, F_DUPFD, FDBASE);
+		close(shl_dbg_fd);
+		if ((shl_dbg_fd = nfd) == -1)
+			errorf("cannot dup debug output file");
+	}
+	fcntl(shl_dbg_fd, F_SETFD, FD_CLOEXEC);
+	shf_fdopen(shl_dbg_fd, SHF_WR, shl_dbg);
+	DF("=== open ===");
+#endif
+	initio_done = true;
+}
+
+/* A dup2() with error checking */
+int
+ksh_dup2(int ofd, int nfd, bool errok)
+{
+	int rv;
+
+	if (((rv = dup2(ofd, nfd)) < 0) && !errok && (errno != EBADF))
+		errorf("too many files open in shell");
+
+#ifdef __ultrix
+	/*XXX imake style */
+	if (rv >= 0)
+		fcntl(nfd, F_SETFD, 0);
+#endif
+
+	return (rv);
+}
+
+/*
+ * Move fd from user space (0 <= fd < 10) to shell space (fd >= 10),
+ * set close-on-exec flag. See FDBASE in sh.h, maybe 24 not 10 here.
+ */
+short
+savefd(int fd)
+{
+	int nfd = fd;
+
+	if (fd < FDBASE && (nfd = fcntl(fd, F_DUPFD, FDBASE)) < 0 &&
+	    errno == EBADF)
+		return (-1);
+	if (nfd < 0 || nfd > SHRT_MAX)
+		errorf("too many files open in shell");
+	fcntl(nfd, F_SETFD, FD_CLOEXEC);
+	return ((short)nfd);
+}
+
+void
+restfd(int fd, int ofd)
+{
+	if (fd == 2)
+		shf_flush(&shf_iob[/* fd */ 2]);
+	if (ofd < 0)
+		/* original fd closed */
+		close(fd);
+	else if (fd != ofd) {
+		/*XXX: what to do if this dup fails? */
+		ksh_dup2(ofd, fd, true);
+		close(ofd);
+	}
+}
+
+void
+openpipe(int *pv)
+{
+	int lpv[2];
+
+	if (pipe(lpv) < 0)
+		errorf("can't create pipe - try again");
+	pv[0] = savefd(lpv[0]);
+	if (pv[0] != lpv[0])
+		close(lpv[0]);
+	pv[1] = savefd(lpv[1]);
+	if (pv[1] != lpv[1])
+		close(lpv[1]);
+}
+
+void
+closepipe(int *pv)
+{
+	close(pv[0]);
+	close(pv[1]);
+}
+
+/*
+ * Called by iosetup() (deals with 2>&4, etc.), c_read, c_print to turn
+ * a string (the X in 2>&X, read -uX, print -uX) into a file descriptor.
+ */
+int
+check_fd(const char *name, int mode, const char **emsgp)
+{
+	int fd, fl;
+
+	if (name[0] == 'p' && !name[1])
+		return (coproc_getfd(mode, emsgp));
+	for (fd = 0; ksh_isdigit(*name); ++name)
+		fd = (fd * 10) + *name - '0';
+	if (*name || fd >= FDBASE) {
+		if (emsgp)
+			*emsgp = "illegal file descriptor name";
+		return (-1);
+	}
+	if ((fl = fcntl(fd, F_GETFL, 0)) < 0) {
+		if (emsgp)
+			*emsgp = "bad file descriptor";
+		return (-1);
+	}
+	fl &= O_ACCMODE;
+	/*
+	 * X_OK is a kludge to disable this check for dups (x<&1):
+	 * historical shells never did this check (XXX don't know what
+	 * POSIX has to say).
+	 */
+	if (!(mode & X_OK) && fl != O_RDWR && (
+	    ((mode & R_OK) && fl != O_RDONLY) ||
+	    ((mode & W_OK) && fl != O_WRONLY))) {
+		if (emsgp)
+			*emsgp = (fl == O_WRONLY) ?
+			    "fd not open for reading" :
+			    "fd not open for writing";
+		return (-1);
+	}
+	return (fd);
+}
+
+/* Called once from main */
+void
+coproc_init(void)
+{
+	coproc.read = coproc.readw = coproc.write = -1;
+	coproc.njobs = 0;
+	coproc.id = 0;
+}
+
+/* Called by c_read() when eof is read - close fd if it is the co-process fd */
+void
+coproc_read_close(int fd)
+{
+	if (coproc.read >= 0 && fd == coproc.read) {
+		coproc_readw_close(fd);
+		close(coproc.read);
+		coproc.read = -1;
+	}
+}
+
+/*
+ * Called by c_read() and by iosetup() to close the other side of the
+ * read pipe, so reads will actually terminate.
+ */
+void
+coproc_readw_close(int fd)
+{
+	if (coproc.readw >= 0 && coproc.read >= 0 && fd == coproc.read) {
+		close(coproc.readw);
+		coproc.readw = -1;
+	}
+}
+
+/*
+ * Called by c_print when a write to a fd fails with EPIPE and by iosetup
+ * when co-process input is dup'd
+ */
+void
+coproc_write_close(int fd)
+{
+	if (coproc.write >= 0 && fd == coproc.write) {
+		close(coproc.write);
+		coproc.write = -1;
+	}
+}
+
+/*
+ * Called to check for existence of/value of the co-process file descriptor.
+ * (Used by check_fd() and by c_read/c_print to deal with -p option).
+ */
+int
+coproc_getfd(int mode, const char **emsgp)
+{
+	int fd = (mode & R_OK) ? coproc.read : coproc.write;
+
+	if (fd >= 0)
+		return (fd);
+	if (emsgp)
+		*emsgp = "no coprocess";
+	return (-1);
+}
+
+/*
+ * called to close file descriptors related to the coprocess (if any)
+ * Should be called with SIGCHLD blocked.
+ */
+void
+coproc_cleanup(int reuse)
+{
+	/* This to allow co-processes to share output pipe */
+	if (!reuse || coproc.readw < 0 || coproc.read < 0) {
+		if (coproc.read >= 0) {
+			close(coproc.read);
+			coproc.read = -1;
+		}
+		if (coproc.readw >= 0) {
+			close(coproc.readw);
+			coproc.readw = -1;
+		}
+	}
+	if (coproc.write >= 0) {
+		close(coproc.write);
+		coproc.write = -1;
+	}
+}
+
+struct temp *
+maketemp(Area *ap, Temp_type type, struct temp **tlist)
+{
+	char *cp;
+	size_t len;
+	int i, j;
+	struct temp *tp;
+	const char *dir;
+	struct stat sb;
+
+	dir = tmpdir ? tmpdir : MKSH_DEFAULT_TMPDIR;
+	/* add "/shXXXXXX.tmp" plus NUL */
+	len = strlen(dir);
+	checkoktoadd(len, offsetof(struct temp, tffn[0]) + 14);
+	tp = alloc(offsetof(struct temp, tffn[0]) + 14 + len, ap);
+
+	tp->shf = NULL;
+	tp->pid = procpid;
+	tp->type = type;
+
+	if (stat(dir, &sb) || !S_ISDIR(sb.st_mode)) {
+		tp->tffn[0] = '\0';
+		goto maketemp_out;
+	}
+
+	cp = (void *)tp;
+	cp += offsetof(struct temp, tffn[0]);
+	memcpy(cp, dir, len);
+	cp += len;
+	memcpy(cp, "/shXXXXXX.tmp", 14);
+	/* point to the first of six Xes */
+	cp += 3;
+	/* generate random part of filename */
+	len = -1;
+	do {
+		i = rndget() % 36;
+		cp[++len] = i < 26 ? 'a' + i : '0' + i - 26;
+	} while (len < 5);
+
+	/* cyclically attempt to open a temporary file */
+	while ((i = open(tp->tffn, O_CREAT | O_EXCL | O_RDWR | O_BINARY,
+	    0600)) < 0) {
+		if (errno != EEXIST)
+			goto maketemp_out;
+		/* count down from z to a then from 9 to 0 */
+		while (cp[len] == '0')
+			if (!len--)
+				goto maketemp_out;
+		if (cp[len] == 'a')
+			cp[len] = '9';
+		else
+			--cp[len];
+		/* do another cycle */
+	}
+
+	if (type == TT_FUNSUB) {
+		/* map us high and mark as close-on-exec */
+		if ((j = savefd(i)) != i) {
+			close(i);
+			i = j;
+		}
+
+		/* operation mode for the shf */
+		j = SHF_RD;
+	} else
+		j = SHF_WR;
+
+	/* shf_fdopen cannot fail, so no fd leak */
+	tp->shf = shf_fdopen(i, j, NULL);
+
+ maketemp_out:
+	tp->next = *tlist;
+	*tlist = tp;
+	return (tp);
+}
+
+/*
+ * We use a similar collision resolution algorithm as Python 2.5.4
+ * but with a slightly tweaked implementation written from scratch.
+ */
+
+#define	INIT_TBLSHIFT	3	/* initial table shift (2^3 = 8) */
+#define PERTURB_SHIFT	5	/* see Python 2.5.4 Objects/dictobject.c */
+
+static void tgrow(struct table *);
+static int tnamecmp(const void *, const void *);
+
+static void
+tgrow(struct table *tp)
+{
+	size_t i, j, osize, mask, perturb;
+	struct tbl *tblp, **pp;
+	struct tbl **ntblp, **otblp = tp->tbls;
+
+	if (tp->tshift > 29)
+		internal_errorf("hash table size limit reached");
+
+	/* calculate old size, new shift and new size */
+	osize = (size_t)1 << (tp->tshift++);
+	i = osize << 1;
+
+	ntblp = alloc2(i, sizeof(struct tbl *), tp->areap);
+	/* multiplication cannot overflow: alloc2 checked that */
+	memset(ntblp, 0, i * sizeof(struct tbl *));
+
+	/* table can get very full when reaching its size limit */
+	tp->nfree = (tp->tshift == 30) ? 0x3FFF0000UL :
+	    /* but otherwise, only 75% */
+	    ((i * 3) / 4);
+	tp->tbls = ntblp;
+	if (otblp == NULL)
+		return;
+
+	mask = i - 1;
+	for (i = 0; i < osize; i++)
+		if ((tblp = otblp[i]) != NULL) {
+			if ((tblp->flag & DEFINED)) {
+				/* search for free hash table slot */
+				j = perturb = tblp->ua.hval;
+				goto find_first_empty_slot;
+ find_next_empty_slot:
+				j = (j << 2) + j + perturb + 1;
+				perturb >>= PERTURB_SHIFT;
+ find_first_empty_slot:
+				pp = &ntblp[j & mask];
+				if (*pp != NULL)
+					goto find_next_empty_slot;
+				/* found an empty hash table slot */
+				*pp = tblp;
+				tp->nfree--;
+			} else if (!(tblp->flag & FINUSE)) {
+				afree(tblp, tp->areap);
+			}
+		}
+	afree(otblp, tp->areap);
+}
+
+void
+ktinit(Area *ap, struct table *tp, uint8_t initshift)
+{
+	tp->areap = ap;
+	tp->tbls = NULL;
+	tp->tshift = ((initshift > INIT_TBLSHIFT) ?
+	    initshift : INIT_TBLSHIFT) - 1;
+	tgrow(tp);
+}
+
+/* table, name (key) to search for, hash(name), rv pointer to tbl ptr */
+struct tbl *
+ktscan(struct table *tp, const char *name, uint32_t h, struct tbl ***ppp)
+{
+	size_t j, perturb, mask;
+	struct tbl **pp, *p;
+
+	mask = ((size_t)1 << (tp->tshift)) - 1;
+	/* search for hash table slot matching name */
+	j = perturb = h;
+	goto find_first_slot;
+ find_next_slot:
+	j = (j << 2) + j + perturb + 1;
+	perturb >>= PERTURB_SHIFT;
+ find_first_slot:
+	pp = &tp->tbls[j & mask];
+	if ((p = *pp) != NULL && (p->ua.hval != h || !(p->flag & DEFINED) ||
+	    strcmp(p->name, name)))
+		goto find_next_slot;
+	/* p == NULL if not found, correct found entry otherwise */
+	if (ppp)
+		*ppp = pp;
+	return (p);
+}
+
+/* table, name (key) to enter, hash(n) */
+struct tbl *
+ktenter(struct table *tp, const char *n, uint32_t h)
+{
+	struct tbl **pp, *p;
+	size_t len;
+
+ Search:
+	if ((p = ktscan(tp, n, h, &pp)))
+		return (p);
+
+	if (tp->nfree == 0) {
+		/* too full */
+		tgrow(tp);
+		goto Search;
+	}
+
+	/* create new tbl entry */
+	len = strlen(n);
+	checkoktoadd(len, offsetof(struct tbl, name[0]) + 1);
+	p = alloc(offsetof(struct tbl, name[0]) + ++len, tp->areap);
+	p->flag = 0;
+	p->type = 0;
+	p->areap = tp->areap;
+	p->ua.hval = h;
+	p->u2.field = 0;
+	p->u.array = NULL;
+	memcpy(p->name, n, len);
+
+	/* enter in tp->tbls */
+	tp->nfree--;
+	*pp = p;
+	return (p);
+}
+
+void
+ktwalk(struct tstate *ts, struct table *tp)
+{
+	ts->left = (size_t)1 << (tp->tshift);
+	ts->next = tp->tbls;
+}
+
+struct tbl *
+ktnext(struct tstate *ts)
+{
+	while (--ts->left >= 0) {
+		struct tbl *p = *ts->next++;
+		if (p != NULL && (p->flag & DEFINED))
+			return (p);
+	}
+	return (NULL);
+}
+
+static int
+tnamecmp(const void *p1, const void *p2)
+{
+	const struct tbl *a = *((const struct tbl * const *)p1);
+	const struct tbl *b = *((const struct tbl * const *)p2);
+
+	return (strcmp(a->name, b->name));
+}
+
+struct tbl **
+ktsort(struct table *tp)
+{
+	size_t i;
+	struct tbl **p, **sp, **dp;
+
+	/*
+	 * since the table is never entirely full, no need to reserve
+	 * additional space for the trailing NULL appended below
+	 */
+	i = (size_t)1 << (tp->tshift);
+	p = alloc2(i, sizeof(struct tbl *), ATEMP);
+	sp = tp->tbls;		/* source */
+	dp = p;			/* dest */
+	while (i--)
+		if ((*dp = *sp++) != NULL && (((*dp)->flag & DEFINED) ||
+		    ((*dp)->flag & ARRAY)))
+			dp++;
+	qsort(p, (i = dp - p), sizeof(struct tbl *), tnamecmp);
+	p[i] = NULL;
+	return (p);
+}
+
+#ifdef SIGWINCH
+static void
+x_sigwinch(int sig MKSH_A_UNUSED)
+{
+	/* this runs inside interrupt context, with errno saved */
+
+	got_winch = 1;
+}
+#endif
+
+#ifdef DF
+void
+DF(const char *fmt, ...)
+{
+	va_list args;
+	struct timeval tv;
+	mirtime_mjd mjd;
+
+	mksh_lockfd(shl_dbg_fd);
+	mksh_TIME(tv);
+	timet2mjd(&mjd, tv.tv_sec);
+	shf_fprintf(shl_dbg, "[%02u:%02u:%02u (%u) %u.%06u] ",
+	    (unsigned)mjd.sec / 3600, ((unsigned)mjd.sec / 60) % 60,
+	    (unsigned)mjd.sec % 60, (unsigned)getpid(),
+	    (unsigned)tv.tv_sec, (unsigned)tv.tv_usec);
+	va_start(args, fmt);
+	shf_vfprintf(shl_dbg, fmt, args);
+	va_end(args);
+	shf_putc('\n', shl_dbg);
+	shf_flush(shl_dbg);
+	mksh_unlkfd(shl_dbg_fd);
+}
+#endif
+
+void
+x_mkraw(int fd, mksh_ttyst *ocb, bool forread)
+{
+	mksh_ttyst cb;
+
+	if (ocb)
+		mksh_tcget(fd, ocb);
+	else
+		ocb = &tty_state;
+
+	cb = *ocb;
+	if (forread) {
+		cb.c_iflag &= ~(ISTRIP);
+		cb.c_lflag &= ~(ICANON) | ECHO;
+	} else {
+		cb.c_iflag &= ~(INLCR | ICRNL | ISTRIP);
+		cb.c_lflag &= ~(ISIG | ICANON | ECHO);
+	}
+#if defined(VLNEXT) && defined(_POSIX_VDISABLE)
+	/* OSF/1 processes lnext when ~icanon */
+	cb.c_cc[VLNEXT] = _POSIX_VDISABLE;
+#endif
+	/* SunOS 4.1.x & OSF/1 processes discard(flush) when ~icanon */
+#if defined(VDISCARD) && defined(_POSIX_VDISABLE)
+	cb.c_cc[VDISCARD] = _POSIX_VDISABLE;
+#endif
+	cb.c_cc[VTIME] = 0;
+	cb.c_cc[VMIN] = 1;
+
+	mksh_tcset(fd, &cb);
+}

Copied: vendor/MirOS/mksh/R50/mirhash.h (from rev 6707, vendor/MirOS/mksh/dist/mirhash.h)
===================================================================
--- vendor/MirOS/mksh/R50/mirhash.h	                        (rev 0)
+++ vendor/MirOS/mksh/R50/mirhash.h	2014-07-01 12:01:17 UTC (rev 6708)
@@ -0,0 +1,218 @@
+/*-
+ * Copyright © 2011, 2014
+ *	Thorsten Glaser <tg at mirbsd.org>
+ *
+ * Provided that these terms and disclaimer and all copyright notices
+ * are retained or reproduced in an accompanying document, permission
+ * is granted to deal in this work without restriction, including un‐
+ * limited rights to use, publicly perform, distribute, sell, modify,
+ * merge, give away, or sublicence.
+ *
+ * This work is provided “AS IS” and WITHOUT WARRANTY of any kind, to
+ * the utmost extent permitted by applicable law, neither express nor
+ * implied; without malicious intent or gross negligence. In no event
+ * may a licensor, author or contributor be held liable for indirect,
+ * direct, other damage, loss, or other issues arising in any way out
+ * of dealing in the work, even if advised of the possibility of such
+ * damage or existence of a defect, except proven that it results out
+ * of said person’s immediate fault when using the work as intended.
+ *-
+ * This file provides BAFH (Better Avalanche for the Jenkins Hash) as
+ * inline macro bodies that operate on “register uint32_t” variables,
+ * with variants that use their local intermediate registers.
+ *
+ * Usage note for BAFH with entropy distribution: input up to 4 bytes
+ * is best combined into a 32-bit unsigned integer, which is then run
+ * through BAFHFinish_reg for mixing and then used as context instead
+ * of 0. Longer input should be handled the same: take the first four
+ * bytes as IV after mixing then add subsequent bytes the same way.
+ * This needs counting input bytes and is endian-dependent, thus not,
+ * for speed reasons, specified for the regular stable hash, but very
+ * much recommended if the actual output value may differ across runs
+ * (so is using a random value instead of 0 for the IV).
+ */
+
+#ifndef SYSKERN_MIRHASH_H
+#define SYSKERN_MIRHASH_H 1
+#define SYSKERN_MIRHASH_BAFH
+
+#include <sys/types.h>
+
+__RCSID("$MirOS: src/bin/mksh/mirhash.h,v 1.2 2014/06/29 11:48:05 tg Exp $");
+
+/*-
+ * BAFH itself is defined by the following primitives:
+ *
+ * • BAFHInit(ctx) initialises the hash context, which consists of a
+ *   sole 32-bit unsigned integer (ideally in a register), to 0.
+ *   It is possible to use any initial value out of [0; 2³²[ – which
+ *   is, in fact, recommended if using BAFH for entropy distribution
+ *   – but for a regular stable hash, the IV 0 is needed.
+ *
+ * • BAFHUpdateOctet(ctx,val) compresses the unsigned 8-bit quantity
+ *   into the hash context. The algorithm used is Jenkins’ one-at-a-
+ *   time, except that an additional constant 1 is added so that, if
+ *   the context is (still) zero, adding a NUL byte is not ignored.
+ *
+ * • BAFHror(eax,cl) evaluates to the unsigned 32-bit integer “eax”,
+ *   rotated right by “cl” ∈ [0;31]; no casting, be careful!
+ *
+ * • BAFHFinish(ctx) avalanches the context around so every sub-byte
+ *   depends on all input octets; afterwards, the context variable’s
+ *   value is the hash output. BAFH does not use any padding, nor is
+ *   the input length added; this is due to the common use case (for
+ *   quick entropy distribution and use with a hashtable).
+ *   Warning: BAFHFinish uses the MixColumn algorithm of AES – which
+ *   is reversible (to avoid introducing funnels and reducing entro‐
+ *   py), so blinding may need to be employed for some uses, e.g. in
+ *   mksh, after a fork.
+ *
+ * The BAFHUpdateOctet and BAFHFinish are available in two flavours:
+ * suffixed with _reg (assumes the context is in a register) or _mem
+ * (which doesn’t).
+ *
+ * The following high-level macros (with _reg and _mem variants) are
+ * available:
+ *
+ * • BAFHUpdateMem(ctx,buf,len) adds a memory block to a context.
+ * • BAFHUpdateStr(ctx,buf) is equivalent to using len=strlen(buf).
+ * • BAFHHostMem(ctx,buf,len) calculates the hash of the memory buf‐
+ *   fer using the first 4 octets (mixed) for IV, as outlined above;
+ *   the result is endian-dependent; “ctx” assumed to be a register.
+ * • BAFHHostStr(ctx,buf) does the same for C strings.
+ *
+ * All macros may use ctx multiple times in their expansion, but all
+ * other arguments are always evaluated at most once.
+ *
+ * To stay portable, never use the BAFHHost*() macros (these are for
+ * host-local entropy shuffling), and encode numbers using ULEB128.
+ */
+
+#define BAFHInit(h) do {					\
+	(h) = 0;						\
+} while (/* CONSTCOND */ 0)
+
+#define BAFHUpdateOctet_reg(h,b) do {				\
+	(h) += (uint8_t)(b);					\
+	++(h);							\
+	(h) += (h) << 10;					\
+	(h) ^= (h) >> 6;					\
+} while (/* CONSTCOND */ 0)
+
+#define BAFHUpdateOctet_mem(m,b) do {				\
+	register uint32_t BAFH_h = (m);				\
+								\
+	BAFHUpdateOctet_reg(BAFH_h, (b));			\
+	(m) = BAFH_h;						\
+} while (/* CONSTCOND */ 0)
+
+#define BAFHror(eax,cl) (((eax) >> (cl)) | ((eax) << (32 - (cl))))
+
+#define BAFHFinish_reg(h) do {					\
+	register uint32_t BAFHFinish_v;				\
+								\
+	BAFHFinish_v = ((h) >> 7) & 0x01010101U;		\
+	BAFHFinish_v += BAFHFinish_v << 1;			\
+	BAFHFinish_v += BAFHFinish_v << 3;			\
+	BAFHFinish_v ^= ((h) << 1) & 0xFEFEFEFEU;		\
+								\
+	BAFHFinish_v ^= BAFHror(BAFHFinish_v, 8);		\
+	BAFHFinish_v ^= ((h) = BAFHror((h), 8));		\
+	BAFHFinish_v ^= ((h) = BAFHror((h), 8));		\
+	(h) = BAFHror((h), 8) ^ BAFHFinish_v;			\
+} while (/* CONSTCOND */ 0)
+
+#define BAFHFinish_mem(m) do {					\
+	register uint32_t BAFHFinish_v, BAFH_h = (m);		\
+								\
+	BAFHFinish_v = (BAFH_h >> 7) & 0x01010101U;		\
+	BAFHFinish_v += BAFHFinish_v << 1;			\
+	BAFHFinish_v += BAFHFinish_v << 3;			\
+	BAFHFinish_v ^= (BAFH_h << 1) & 0xFEFEFEFEU;		\
+								\
+	BAFHFinish_v ^= BAFHror(BAFHFinish_v, 8);		\
+	BAFHFinish_v ^= (BAFH_h = BAFHror(BAFH_h, 8));		\
+	BAFHFinish_v ^= (BAFH_h = BAFHror(BAFH_h, 8));		\
+	(m) = BAFHror(BAFH_h, 8) ^ BAFHFinish_v;		\
+} while (/* CONSTCOND */ 0)
+
+#define BAFHUpdateMem_reg(h,p,z) do {				\
+	register const uint8_t *BAFHUpdate_p;			\
+	register size_t BAFHUpdate_z = (z);			\
+								\
+	BAFHUpdate_p = (const void *)(p);			\
+	while (BAFHUpdate_z--)					\
+		BAFHUpdateOctet_reg((h), *BAFHUpdate_p++);	\
+} while (/* CONSTCOND */ 0)
+
+/* meh should have named them _r/m but that’s not valid C */
+#define BAFHUpdateMem_mem(m,p,z) do {				\
+	register uint32_t BAFH_h = (m);				\
+								\
+	BAFHUpdateMem_reg(BAFH_h, (p), (z));			\
+	(m) = BAFH_h;						\
+} while (/* CONSTCOND */ 0)
+
+#define BAFHUpdateStr_reg(h,s) do {				\
+	register const uint8_t *BAFHUpdate_s;			\
+	register uint8_t BAFHUpdate_c;				\
+								\
+	BAFHUpdate_s = (const void *)(s);			\
+	while ((BAFHUpdate_c = *BAFHUpdate_s++) != 0)		\
+		BAFHUpdateOctet_reg((h), BAFHUpdate_c);		\
+} while (/* CONSTCOND */ 0)
+
+#define BAFHUpdateStr_mem(m,s) do {				\
+	register uint32_t BAFH_h = (m);				\
+								\
+	BAFHUpdateStr_reg(BAFH_h, (s));				\
+	(m) = BAFH_h;						\
+} while (/* CONSTCOND */ 0)
+
+#define BAFHHostMem(h,p,z) do {					\
+	register const uint8_t *BAFHUpdate_p;			\
+	register size_t BAFHUpdate_z = (z);			\
+	size_t BAFHHost_z;					\
+	union {							\
+		uint8_t as_u8[4];				\
+		uint32_t as_u32;				\
+	} BAFHHost_v;						\
+								\
+	BAFHUpdate_p = (const void *)(p);			\
+	BAFHHost_v.as_u32 = 0;					\
+	BAFHHost_z = BAFHUpdate_z < 4 ? BAFHUpdate_z : 4;	\
+	memcpy(BAFHHost_v.as_u8, BAFHUpdate_p, BAFHHost_z);	\
+	BAFHUpdate_p += BAFHHost_z;				\
+	BAFHUpdate_z -= BAFHHost_z;				\
+	(h) = BAFHHost_v.as_u32;				\
+	BAFHFinish_reg(h);					\
+	while (BAFHUpdate_z--)					\
+		BAFHUpdateOctet_reg((h), *BAFHUpdate_p++);	\
+	BAFHFinish_reg(h);					\
+} while (/* CONSTCOND */ 0)
+
+#define BAFHHostStr(h,s) do {					\
+	register const uint8_t *BAFHUpdate_s;			\
+	register uint8_t BAFHUpdate_c;				\
+	union {							\
+		uint8_t as_u8[4];				\
+		uint32_t as_u32;				\
+	} BAFHHost_v;						\
+								\
+	BAFHUpdate_s = (const void *)(s);			\
+	if ((BAFHHost_v.as_u8[0] = *BAFHUpdate_s) != 0)		\
+		++BAFHUpdate_s;					\
+	if ((BAFHHost_v.as_u8[1] = *BAFHUpdate_s) != 0)		\
+		++BAFHUpdate_s;					\
+	if ((BAFHHost_v.as_u8[2] = *BAFHUpdate_s) != 0)		\
+		++BAFHUpdate_s;					\
+	if ((BAFHHost_v.as_u8[3] = *BAFHUpdate_s) != 0)		\
+		++BAFHUpdate_s;					\
+	(h) = BAFHHost_v.as_u32;				\
+	BAFHFinish_reg(h);					\
+	while ((BAFHUpdate_c = *BAFHUpdate_s++) != 0)		\
+		BAFHUpdateOctet_reg((h), BAFHUpdate_c);		\
+	BAFHFinish_reg(h);					\
+} while (/* CONSTCOND */ 0)
+
+#endif

Deleted: vendor/MirOS/mksh/R50/misc.c
===================================================================
--- vendor/MirOS/mksh/dist/misc.c	2014-06-30 23:58:12 UTC (rev 6706)
+++ vendor/MirOS/mksh/R50/misc.c	2014-07-01 12:01:17 UTC (rev 6708)
@@ -1,2272 +0,0 @@
-/*	$OpenBSD: misc.c,v 1.37 2009/04/19 20:34:05 sthen Exp $	*/
-/*	$OpenBSD: path.c,v 1.12 2005/03/30 17:16:37 deraadt Exp $	*/
-
-/*-
- * Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
- *		 2011, 2012, 2013
- *	Thorsten Glaser <tg at mirbsd.org>
- *
- * Provided that these terms and disclaimer and all copyright notices
- * are retained or reproduced in an accompanying document, permission
- * is granted to deal in this work without restriction, including un-
- * limited rights to use, publicly perform, distribute, sell, modify,
- * merge, give away, or sublicence.
- *
- * This work is provided "AS IS" and WITHOUT WARRANTY of any kind, to
- * the utmost extent permitted by applicable law, neither express nor
- * implied; without malicious intent or gross negligence. In no event
- * may a licensor, author or contributor be held liable for indirect,
- * direct, other damage, loss, or other issues arising in any way out
- * of dealing in the work, even if advised of the possibility of such
- * damage or existence of a defect, except proven that it results out
- * of said person's immediate fault when using the work as intended.
- */
-
-#include "sh.h"
-#if !HAVE_GETRUSAGE
-#include <sys/times.h>
-#endif
-#if HAVE_GRP_H
-#include <grp.h>
-#endif
-
-__RCSID("$MirOS: src/bin/mksh/misc.c,v 1.214 2013/08/11 14:57:09 tg Exp $");
-
-#define KSH_CHVT_FLAG
-#ifdef MKSH_SMALL
-#undef KSH_CHVT_FLAG
-#endif
-#ifdef TIOCSCTTY
-#define KSH_CHVT_CODE
-#define KSH_CHVT_FLAG
-#endif
-#ifdef MKSH_LEGACY_MODE
-#undef KSH_CHVT_CODE
-#undef KSH_CHVT_FLAG
-#endif
-
-/* type bits for unsigned char */
-unsigned char chtypes[UCHAR_MAX + 1];
-
-static const unsigned char *pat_scan(const unsigned char *,
-    const unsigned char *, bool);
-static int do_gmatch(const unsigned char *, const unsigned char *,
-    const unsigned char *, const unsigned char *);
-static const unsigned char *cclass(const unsigned char *, unsigned char);
-#ifdef KSH_CHVT_CODE
-static void chvt(const Getopt *);
-#endif
-
-/*XXX this should go away */
-static int make_path(const char *, const char *, char **, XString *, int *);
-
-#ifdef SETUID_CAN_FAIL_WITH_EAGAIN
-/* we don't need to check for other codes, EPERM won't happen */
-#define DO_SETUID(func, argvec) do {					\
-	if ((func argvec) && errno == EAGAIN)				\
-		errorf("%s failed with EAGAIN, probably due to a"	\
-		    " too low process limit; aborting", #func);		\
-} while (/* CONSTCOND */ 0)
-#else
-#define DO_SETUID(func, argvec) func argvec
-#endif
-
-/*
- * Fast character classes
- */
-void
-setctypes(const char *s, int t)
-{
-	unsigned int i;
-
-	if (t & C_IFS) {
-		for (i = 0; i < UCHAR_MAX + 1; i++)
-			chtypes[i] &= ~C_IFS;
-		/* include \0 in C_IFS */
-		chtypes[0] |= C_IFS;
-	}
-	while (*s != 0)
-		chtypes[(unsigned char)*s++] |= t;
-}
-
-void
-initctypes(void)
-{
-	int c;
-
-	for (c = 'a'; c <= 'z'; c++)
-		chtypes[c] |= C_ALPHA;
-	for (c = 'A'; c <= 'Z'; c++)
-		chtypes[c] |= C_ALPHA;
-	chtypes['_'] |= C_ALPHA;
-	setctypes("0123456789", C_DIGIT);
-	/* \0 added automatically */
-	setctypes(TC_LEX1, C_LEX1);
-	setctypes("*@#!$-?", C_VAR1);
-	setctypes(TC_IFSWS, C_IFSWS);
-	setctypes("=-+?", C_SUBOP1);
-	setctypes("\t\n \"#$&'()*;<=>?[\\]`|", C_QUOTE);
-}
-
-/* called from XcheckN() to grow buffer */
-char *
-Xcheck_grow(XString *xsp, const char *xp, size_t more)
-{
-	const char *old_beg = xsp->beg;
-
-	if (more < xsp->len)
-		more = xsp->len;
-	/* (xsp->len + X_EXTRA) never overflows */
-	checkoktoadd(more, xsp->len + X_EXTRA);
-	xsp->beg = aresize(xsp->beg, (xsp->len += more) + X_EXTRA, xsp->areap);
-	xsp->end = xsp->beg + xsp->len;
-	return (xsp->beg + (xp - old_beg));
-}
-
-
-#define SHFLAGS_DEFNS
-#include "sh_flags.h"
-
-#define OFC(i) (options[i][-2])
-#define OFF(i) (((const unsigned char *)options[i])[-1])
-#define OFN(i) (options[i])
-
-const char * const options[] = {
-#define SHFLAGS_ITEMS
-#include "sh_flags.h"
-};
-
-/*
- * translate -o option into F* constant (also used for test -o option)
- */
-size_t
-option(const char *n)
-{
-	size_t i = 0;
-
-	if ((n[0] == '-' || n[0] == '+') && n[1] && !n[2])
-		while (i < NELEM(options)) {
-			if (OFC(i) == n[1])
-				return (i);
-			++i;
-		}
-	else
-		while (i < NELEM(options)) {
-			if (!strcmp(OFN(i), n))
-				return (i);
-			++i;
-		}
-
-	return ((size_t)-1);
-}
-
-struct options_info {
-	int opt_width;
-	int opts[NELEM(options)];
-};
-
-static char *options_fmt_entry(char *, size_t, unsigned int, const void *);
-static void printoptions(bool);
-
-/* format a single select menu item */
-static char *
-options_fmt_entry(char *buf, size_t buflen, unsigned int i, const void *arg)
-{
-	const struct options_info *oi = (const struct options_info *)arg;
-
-	shf_snprintf(buf, buflen, "%-*s %s",
-	    oi->opt_width, OFN(oi->opts[i]),
-	    Flag(oi->opts[i]) ? "on" : "off");
-	return (buf);
-}
-
-static void
-printoptions(bool verbose)
-{
-	size_t i = 0;
-
-	if (verbose) {
-		size_t n = 0, len, octs = 0;
-		struct options_info oi;
-
-		/* verbose version */
-		shf_puts("Current option settings\n", shl_stdout);
-
-		oi.opt_width = 0;
-		while (i < NELEM(options)) {
-			if ((len = strlen(OFN(i)))) {
-				oi.opts[n++] = i;
-				if (len > octs)
-					octs = len;
-				len = utf_mbswidth(OFN(i));
-				if ((int)len > oi.opt_width)
-					oi.opt_width = (int)len;
-			}
-			++i;
-		}
-		print_columns(shl_stdout, n, options_fmt_entry, &oi,
-		    octs + 4, oi.opt_width + 4, true);
-	} else {
-		/* short version like AT&T ksh93 */
-		shf_puts(Tset, shl_stdout);
-		while (i < NELEM(options)) {
-			if (Flag(i) && OFN(i)[0])
-				shprintf(" -o %s", OFN(i));
-			++i;
-		}
-		shf_putc('\n', shl_stdout);
-	}
-}
-
-char *
-getoptions(void)
-{
-	size_t i = 0;
-	char c, m[(int)FNFLAGS + 1];
-	char *cp = m;
-
-	while (i < NELEM(options)) {
-		if ((c = OFC(i)) && Flag(i))
-			*cp++ = c;
-		++i;
-	}
-	strndupx(cp, m, cp - m, ATEMP);
-	return (cp);
-}
-
-/* change a Flag(*) value; takes care of special actions */
-void
-change_flag(enum sh_flag f, int what, bool newset)
-{
-	unsigned char oldval;
-	unsigned char newval = (newset ? 1 : 0);
-
-	if (f == FXTRACE) {
-		change_xtrace(newval, true);
-		return;
-	}
-	oldval = Flag(f);
-	Flag(f) = newval = (newset ? 1 : 0);
-#ifndef MKSH_UNEMPLOYED
-	if (f == FMONITOR) {
-		if (what != OF_CMDLINE && newval != oldval)
-			j_change();
-	} else
-#endif
-#ifndef MKSH_NO_CMDLINE_EDITING
-	  if ((
-#if !MKSH_S_NOVI
-	    f == FVI ||
-#endif
-	    f == FEMACS || f == FGMACS) && newval) {
-#if !MKSH_S_NOVI
-		Flag(FVI) =
-#endif
-		    Flag(FEMACS) = Flag(FGMACS) = 0;
-		Flag(f) = newval;
-	} else
-#endif
-	  if (f == FPRIVILEGED && oldval && !newval) {
-		/* Turning off -p? */
-
-		/*XXX this can probably be optimised */
-		kshegid = kshgid = getgid();
-#if HAVE_SETRESUGID
-		DO_SETUID(setresgid, (kshegid, kshegid, kshegid));
-#if HAVE_SETGROUPS
-		/* setgroups doesn't EAGAIN on Linux */
-		setgroups(1, &kshegid);
-#endif
-		DO_SETUID(setresuid, (ksheuid, ksheuid, ksheuid));
-#else
-		/* seteuid, setegid, setgid don't EAGAIN on Linux */
-		ksheuid = kshuid = getuid();
-#ifndef MKSH__NO_SETEUGID
-		seteuid(ksheuid);
-#endif
-		DO_SETUID(setuid, (ksheuid));
-#ifndef MKSH__NO_SETEUGID
-		setegid(kshegid);
-#endif
-		setgid(kshegid);
-#endif
-	} else if ((f == FPOSIX || f == FSH) && newval) {
-		/* Turning on -o posix or -o sh? */
-		Flag(FBRACEEXPAND) = 0;
-	} else if (f == FTALKING) {
-		/* Changing interactive flag? */
-		if ((what == OF_CMDLINE || what == OF_SET) && procpid == kshpid)
-			Flag(FTALKING_I) = newval;
-	}
-}
-
-void
-change_xtrace(unsigned char newval, bool dosnapshot)
-{
-	if (!dosnapshot && newval == Flag(FXTRACE))
-		return;
-
-	if (Flag(FXTRACE) == 2) {
-		shf_putc('\n', shl_xtrace);
-		Flag(FXTRACE) = 1;
-		shf_flush(shl_xtrace);
-	}
-
-	if (!dosnapshot && Flag(FXTRACE) == 1)
-		switch (newval) {
-		case 1:
-			return;
-		case 2:
-			goto changed_xtrace;
-		}
-
-	shf_flush(shl_xtrace);
-	if (shl_xtrace->fd != 2)
-		close(shl_xtrace->fd);
-	if (!newval || (shl_xtrace->fd = savefd(2)) == -1)
-		shl_xtrace->fd = 2;
-
- changed_xtrace:
-	if ((Flag(FXTRACE) = newval) == 2)
-		shf_puts(substitute(str_val(global("PS4")), 0), shl_xtrace);
-}
-
-/*
- * Parse command line and set command arguments. Returns the index of
- * non-option arguments, -1 if there is an error.
- */
-int
-parse_args(const char **argv,
-    /* OF_CMDLINE or OF_SET */
-    int what,
-    bool *setargsp)
-{
-	static char cmd_opts[NELEM(options) + 5]; /* o:T:\0 */
-	static char set_opts[NELEM(options) + 6]; /* A:o;s\0 */
-	bool set;
-	char *opts;
-	const char *array = NULL;
-	Getopt go;
-	size_t i;
-	int optc, arrayset = 0;
-	bool sortargs = false;
-	bool fcompatseen = false;
-
-	/* First call? Build option strings... */
-	if (cmd_opts[0] == '\0') {
-		char ch, *p = cmd_opts, *q = set_opts;
-
-		/* see cmd_opts[] declaration */
-		*p++ = 'o';
-		*p++ = ':';
-#ifdef KSH_CHVT_FLAG
-		*p++ = 'T';
-		*p++ = ':';
-#endif
-		/* see set_opts[] declaration */
-		*q++ = 'A';
-		*q++ = ':';
-		*q++ = 'o';
-		*q++ = ';';
-		*q++ = 's';
-
-		for (i = 0; i < NELEM(options); i++) {
-			if ((ch = OFC(i))) {
-				if (OFF(i) & OF_CMDLINE)
-					*p++ = ch;
-				if (OFF(i) & OF_SET)
-					*q++ = ch;
-			}
-		}
-		*p = '\0';
-		*q = '\0';
-	}
-
-	if (what == OF_CMDLINE) {
-		const char *p = argv[0], *q;
-		/*
-		 * Set FLOGIN before parsing options so user can clear
-		 * flag using +l.
-		 */
-		if (*p != '-')
-			for (q = p; *q; )
-				if (*q++ == '/')
-					p = q;
-		Flag(FLOGIN) = (*p == '-');
-		opts = cmd_opts;
-	} else if (what == OF_FIRSTTIME) {
-		opts = cmd_opts;
-	} else
-		opts = set_opts;
-	ksh_getopt_reset(&go, GF_ERROR|GF_PLUSOPT);
-	while ((optc = ksh_getopt(argv, &go, opts)) != -1) {
-		set = tobool(!(go.info & GI_PLUS));
-		switch (optc) {
-		case 'A':
-			if (what == OF_FIRSTTIME)
-				break;
-			arrayset = set ? 1 : -1;
-			array = go.optarg;
-			break;
-
-		case 'o':
-			if (what == OF_FIRSTTIME)
-				break;
-			if (go.optarg == NULL) {
-				/*
-				 * lone -o: print options
-				 *
-				 * Note that on the command line, -o requires
-				 * an option (ie, can't get here if what is
-				 * OF_CMDLINE).
-				 */
-				printoptions(set);
-				break;
-			}
-			i = option(go.optarg);
-			if ((i == FPOSIX || i == FSH) && set && !fcompatseen) {
-				/*
-				 * If running 'set -o posix' or
-				 * 'set -o sh', turn off the other;
-				 * if running 'set -o posix -o sh'
-				 * allow both to be set though.
-				 */
-				Flag(FPOSIX) = 0;
-				Flag(FSH) = 0;
-				fcompatseen = true;
-			}
-			if ((i != (size_t)-1) && (set ? 1U : 0U) == Flag(i))
-				/*
-				 * Don't check the context if the flag
-				 * isn't changing - makes "set -o interactive"
-				 * work if you're already interactive. Needed
-				 * if the output of "set +o" is to be used.
-				 */
-				;
-			else if ((i != (size_t)-1) && (OFF(i) & what))
-				change_flag((enum sh_flag)i, what, set);
-			else {
-				bi_errorf("%s: %s", go.optarg, "bad option");
-				return (-1);
-			}
-			break;
-
-#ifdef KSH_CHVT_FLAG
-		case 'T':
-			if (what != OF_FIRSTTIME)
-				break;
-#ifndef KSH_CHVT_CODE
-			errorf("no TIOCSCTTY ioctl");
-#else
-			change_flag(FTALKING, OF_CMDLINE, true);
-			chvt(&go);
-			break;
-#endif
-#endif
-
-		case '?':
-			return (-1);
-
-		default:
-			if (what == OF_FIRSTTIME)
-				break;
-			/* -s: sort positional params (AT&T ksh stupidity) */
-			if (what == OF_SET && optc == 's') {
-				sortargs = true;
-				break;
-			}
-			for (i = 0; i < NELEM(options); i++)
-				if (optc == OFC(i) &&
-				    (what & OFF(i))) {
-					change_flag((enum sh_flag)i, what, set);
-					break;
-				}
-			if (i == NELEM(options))
-				internal_errorf("parse_args: '%c'", optc);
-		}
-	}
-	if (!(go.info & GI_MINUSMINUS) && argv[go.optind] &&
-	    (argv[go.optind][0] == '-' || argv[go.optind][0] == '+') &&
-	    argv[go.optind][1] == '\0') {
-		/* lone - clears -v and -x flags */
-		if (argv[go.optind][0] == '-') {
-			Flag(FVERBOSE) = 0;
-			change_xtrace(0, false);
-		}
-		/* set skips lone - or + option */
-		go.optind++;
-	}
-	if (setargsp)
-		/* -- means set $#/$* even if there are no arguments */
-		*setargsp = !arrayset && ((go.info & GI_MINUSMINUS) ||
-		    argv[go.optind]);
-
-	if (arrayset) {
-		const char *ccp = NULL;
-
-		mkssert(array != NULL);
-		if (*array)
-			ccp = skip_varname(array, false);
-		if (!ccp || !(!ccp[0] || (ccp[0] == '+' && !ccp[1]))) {
-			bi_errorf("%s: %s", array, "is not an identifier");
-			return (-1);
-		}
-	}
-	if (sortargs) {
-		for (i = go.optind; argv[i]; i++)
-			;
-		qsort(&argv[go.optind], i - go.optind, sizeof(void *),
-		    xstrcmp);
-	}
-	if (arrayset)
-		go.optind += set_array(array, tobool(arrayset > 0),
-		    argv + go.optind);
-
-	return (go.optind);
-}
-
-/* parse a decimal number: returns 0 if string isn't a number, 1 otherwise */
-int
-getn(const char *s, int *ai)
-{
-	char c;
-	mksh_ari_u num;
-	bool neg = false;
-
-	num.u = 0;
-
-	do {
-		c = *s++;
-	} while (ksh_isspace(c));
-
-	switch (c) {
-	case '-':
-		neg = true;
-		/* FALLTHROUGH */
-	case '+':
-		c = *s++;
-		break;
-	}
-
-	do {
-		if (!ksh_isdigit(c))
-			/* not numeric */
-			return (0);
-		if (num.u > 214748364U)
-			/* overflow on multiplication */
-			return (0);
-		num.u = num.u * 10U + (unsigned int)(c - '0');
-		/* now: num.u <= 2147483649U */
-	} while ((c = *s++));
-
-	if (num.u > (neg ? 2147483648U : 2147483647U))
-		/* overflow for signed 32-bit int */
-		return (0);
-
-	if (neg)
-		num.u = -num.u;
-	*ai = num.i;
-	return (1);
-}
-
-/**
- * pattern simplifications:
- * - @(x) -> x (not @(x|y) though)
- * - ** -> *
- */
-static void *
-simplify_gmatch_pattern(const unsigned char *sp)
-{
-	uint8_t c;
-	unsigned char *cp, *dp;
-	const unsigned char *ps, *se;
-
-	cp = alloc(strlen((const void *)sp) + 1, ATEMP);
-	goto simplify_gmatch_pat1a;
-
-	/* foo@(b@(a)r)b@(a|a)z -> foobarb@(a|a)z */
- simplify_gmatch_pat1:
-	sp = cp;
- simplify_gmatch_pat1a:
-	dp = cp;
-	se = sp + strlen((const void *)sp);
-	while ((c = *sp++)) {
-		if (!ISMAGIC(c)) {
-			*dp++ = c;
-			continue;
-		}
-		switch ((c = *sp++)) {
-		case 0x80|'@':
-		/* simile for @ */
-		case 0x80|' ':
-			/* check whether it has only one clause */
-			ps = pat_scan(sp, se, true);
-			if (!ps || ps[-1] != /*(*/ ')')
-				/* nope */
-				break;
-			/* copy inner clause until matching close */
-			ps -= 2;
-			while ((const unsigned char *)sp < ps)
-				*dp++ = *sp++;
-			/* skip MAGIC and closing parenthesis */
-			sp += 2;
-			/* copy the rest of the pattern */
-			memmove(dp, sp, strlen((const void *)sp) + 1);
-			/* redo from start */
-			goto simplify_gmatch_pat1;
-		}
-		*dp++ = MAGIC;
-		*dp++ = c;
-	}
-	*dp = '\0';
-
-	/* collapse adjacent asterisk wildcards */
-	sp = dp = cp;
-	while ((c = *sp++)) {
-		if (!ISMAGIC(c)) {
-			*dp++ = c;
-			continue;
-		}
-		switch ((c = *sp++)) {
-		case '*':
-			while (ISMAGIC(sp[0]) && sp[1] == c)
-				sp += 2;
-			break;
-		}
-		*dp++ = MAGIC;
-		*dp++ = c;
-	}
-	*dp = '\0';
-
-	/* return the result, allocated from ATEMP */
-	return (cp);
-}
-
-/* -------- gmatch.c -------- */
-
-/*
- * int gmatch(string, pattern)
- * char *string, *pattern;
- *
- * Match a pattern as in sh(1).
- * pattern character are prefixed with MAGIC by expand.
- */
-int
-gmatchx(const char *s, const char *p, bool isfile)
-{
-	const char *se, *pe;
-	char *pnew;
-	int rv;
-
-	if (s == NULL || p == NULL)
-		return (0);
-
-	se = s + strlen(s);
-	pe = p + strlen(p);
-	/*
-	 * isfile is false iff no syntax check has been done on
-	 * the pattern. If check fails, just to a strcmp().
-	 */
-	if (!isfile && !has_globbing(p, pe)) {
-		size_t len = pe - p + 1;
-		char tbuf[64];
-		char *t = len <= sizeof(tbuf) ? tbuf : alloc(len, ATEMP);
-		debunk(t, p, len);
-		return (!strcmp(t, s));
-	}
-
-	/*
-	 * since the do_gmatch() engine sucks so much, we must do some
-	 * pattern simplifications
-	 */
-	pnew = simplify_gmatch_pattern((const unsigned char *)p);
-	pe = pnew + strlen(pnew);
-
-	rv = do_gmatch((const unsigned char *)s, (const unsigned char *)se,
-	    (const unsigned char *)pnew, (const unsigned char *)pe);
-	afree(pnew, ATEMP);
-	return (rv);
-}
-
-/**
- * Returns if p is a syntacticly correct globbing pattern, false
- * if it contains no pattern characters or if there is a syntax error.
- * Syntax errors are:
- *	- [ with no closing ]
- *	- imbalanced $(...) expression
- *	- [...] and *(...) not nested (eg, [a$(b|]c), *(a[b|c]d))
- */
-/*XXX
- * - if no magic,
- *	if dest given, copy to dst
- *	return ?
- * - if magic && (no globbing || syntax error)
- *	debunk to dst
- *	return ?
- * - return ?
- */
-int
-has_globbing(const char *xp, const char *xpe)
-{
-	const unsigned char *p = (const unsigned char *) xp;
-	const unsigned char *pe = (const unsigned char *) xpe;
-	int c;
-	int nest = 0, bnest = 0;
-	bool saw_glob = false;
-	/* inside [...] */
-	bool in_bracket = false;
-
-	for (; p < pe; p++) {
-		if (!ISMAGIC(*p))
-			continue;
-		if ((c = *++p) == '*' || c == '?')
-			saw_glob = true;
-		else if (c == '[') {
-			if (!in_bracket) {
-				saw_glob = true;
-				in_bracket = true;
-				if (ISMAGIC(p[1]) && p[2] == '!')
-					p += 2;
-				if (ISMAGIC(p[1]) && p[2] == ']')
-					p += 2;
-			}
-			/*XXX Do we need to check ranges here? POSIX Q */
-		} else if (c == ']') {
-			if (in_bracket) {
-				if (bnest)
-					/* [a*(b]) */
-					return (0);
-				in_bracket = false;
-			}
-		} else if ((c & 0x80) && vstrchr("*+?@! ", c & 0x7f)) {
-			saw_glob = true;
-			if (in_bracket)
-				bnest++;
-			else
-				nest++;
-		} else if (c == '|') {
-			if (in_bracket && !bnest)
-				/* *(a[foo|bar]) */
-				return (0);
-		} else if (c == /*(*/ ')') {
-			if (in_bracket) {
-				if (!bnest--)
-					/* *(a[b)c] */
-					return (0);
-			} else if (nest)
-				nest--;
-		}
-		/*
-		 * else must be a MAGIC-MAGIC, or MAGIC-!,
-		 * MAGIC--, MAGIC-], MAGIC-{, MAGIC-, MAGIC-}
-		 */
-	}
-	return (saw_glob && !in_bracket && !nest);
-}
-
-/* Function must return either 0 or 1 (assumed by code for 0x80|'!') */
-static int
-do_gmatch(const unsigned char *s, const unsigned char *se,
-    const unsigned char *p, const unsigned char *pe)
-{
-	unsigned char sc, pc;
-	const unsigned char *prest, *psub, *pnext;
-	const unsigned char *srest;
-
-	if (s == NULL || p == NULL)
-		return (0);
-	while (p < pe) {
-		pc = *p++;
-		sc = s < se ? *s : '\0';
-		s++;
-		if (!ISMAGIC(pc)) {
-			if (sc != pc)
-				return (0);
-			continue;
-		}
-		switch (*p++) {
-		case '[':
-			if (sc == 0 || (p = cclass(p, sc)) == NULL)
-				return (0);
-			break;
-
-		case '?':
-			if (sc == 0)
-				return (0);
-			if (UTFMODE) {
-				--s;
-				s += utf_ptradj((const void *)s);
-			}
-			break;
-
-		case '*':
-			if (p == pe)
-				return (1);
-			s--;
-			do {
-				if (do_gmatch(s, se, p, pe))
-					return (1);
-			} while (s++ < se);
-			return (0);
-
-		/**
-		 * [*+?@!](pattern|pattern|..)
-		 * This is also needed for ${..%..}, etc.
-		 */
-
-		/* matches one or more times */
-		case 0x80|'+':
-		/* matches zero or more times */
-		case 0x80|'*':
-			if (!(prest = pat_scan(p, pe, false)))
-				return (0);
-			s--;
-			/* take care of zero matches */
-			if (p[-1] == (0x80 | '*') &&
-			    do_gmatch(s, se, prest, pe))
-				return (1);
-			for (psub = p; ; psub = pnext) {
-				pnext = pat_scan(psub, pe, true);
-				for (srest = s; srest <= se; srest++) {
-					if (do_gmatch(s, srest, psub, pnext - 2) &&
-					    (do_gmatch(srest, se, prest, pe) ||
-					    (s != srest && do_gmatch(srest,
-					    se, p - 2, pe))))
-						return (1);
-				}
-				if (pnext == prest)
-					break;
-			}
-			return (0);
-
-		/* matches zero or once */
-		case 0x80|'?':
-		/* matches one of the patterns */
-		case 0x80|'@':
-		/* simile for @ */
-		case 0x80|' ':
-			if (!(prest = pat_scan(p, pe, false)))
-				return (0);
-			s--;
-			/* Take care of zero matches */
-			if (p[-1] == (0x80 | '?') &&
-			    do_gmatch(s, se, prest, pe))
-				return (1);
-			for (psub = p; ; psub = pnext) {
-				pnext = pat_scan(psub, pe, true);
-				srest = prest == pe ? se : s;
-				for (; srest <= se; srest++) {
-					if (do_gmatch(s, srest, psub, pnext - 2) &&
-					    do_gmatch(srest, se, prest, pe))
-						return (1);
-				}
-				if (pnext == prest)
-					break;
-			}
-			return (0);
-
-		/* matches none of the patterns */
-		case 0x80|'!':
-			if (!(prest = pat_scan(p, pe, false)))
-				return (0);
-			s--;
-			for (srest = s; srest <= se; srest++) {
-				int matched = 0;
-
-				for (psub = p; ; psub = pnext) {
-					pnext = pat_scan(psub, pe, true);
-					if (do_gmatch(s, srest, psub,
-					    pnext - 2)) {
-						matched = 1;
-						break;
-					}
-					if (pnext == prest)
-						break;
-				}
-				if (!matched &&
-				    do_gmatch(srest, se, prest, pe))
-					return (1);
-			}
-			return (0);
-
-		default:
-			if (sc != p[-1])
-				return (0);
-			break;
-		}
-	}
-	return (s == se);
-}
-
-static const unsigned char *
-cclass(const unsigned char *p, unsigned char sub)
-{
-	unsigned char c, d;
-	bool notp, found = false;
-	const unsigned char *orig_p = p;
-
-	if ((notp = tobool(ISMAGIC(*p) && *++p == '!')))
-		p++;
-	do {
-		c = *p++;
-		if (ISMAGIC(c)) {
-			c = *p++;
-			if ((c & 0x80) && !ISMAGIC(c)) {
-				/* extended pattern matching: *+?@! */
-				c &= 0x7F;
-				/* XXX the ( char isn't handled as part of [] */
-				if (c == ' ')
-					/* simile for @: plain (..) */
-					c = '(' /*)*/;
-			}
-		}
-		if (c == '\0')
-			/* No closing ] - act as if the opening [ was quoted */
-			return (sub == '[' ? orig_p : NULL);
-		if (ISMAGIC(p[0]) && p[1] == '-' &&
-		    (!ISMAGIC(p[2]) || p[3] != ']')) {
-			/* MAGIC- */
-			p += 2;
-			d = *p++;
-			if (ISMAGIC(d)) {
-				d = *p++;
-				if ((d & 0x80) && !ISMAGIC(d))
-					d &= 0x7f;
-			}
-			/* POSIX says this is an invalid expression */
-			if (c > d)
-				return (NULL);
-		} else
-			d = c;
-		if (c == sub || (c <= sub && sub <= d))
-			found = true;
-	} while (!(ISMAGIC(p[0]) && p[1] == ']'));
-
-	return ((found != notp) ? p+2 : NULL);
-}
-
-/* Look for next ) or | (if match_sep) in *(foo|bar) pattern */
-static const unsigned char *
-pat_scan(const unsigned char *p, const unsigned char *pe, bool match_sep)
-{
-	int nest = 0;
-
-	for (; p < pe; p++) {
-		if (!ISMAGIC(*p))
-			continue;
-		if ((*++p == /*(*/ ')' && nest-- == 0) ||
-		    (*p == '|' && match_sep && nest == 0))
-			return (p + 1);
-		if ((*p & 0x80) && vstrchr("*+?@! ", *p & 0x7f))
-			nest++;
-	}
-	return (NULL);
-}
-
-int
-xstrcmp(const void *p1, const void *p2)
-{
-	return (strcmp(*(const char * const *)p1, *(const char * const *)p2));
-}
-
-/* Initialise a Getopt structure */
-void
-ksh_getopt_reset(Getopt *go, int flags)
-{
-	go->optind = 1;
-	go->optarg = NULL;
-	go->p = 0;
-	go->flags = flags;
-	go->info = 0;
-	go->buf[1] = '\0';
-}
-
-
-/**
- * getopt() used for shell built-in commands, the getopts command, and
- * command line options.
- * A leading ':' in options means don't print errors, instead return '?'
- * or ':' and set go->optarg to the offending option character.
- * If GF_ERROR is set (and option doesn't start with :), errors result in
- * a call to bi_errorf().
- *
- * Non-standard features:
- *	- ';' is like ':' in options, except the argument is optional
- *	  (if it isn't present, optarg is set to 0).
- *	  Used for 'set -o'.
- *	- ',' is like ':' in options, except the argument always immediately
- *	  follows the option character (optarg is set to the null string if
- *	  the option is missing).
- *	  Used for 'read -u2', 'print -u2' and fc -40.
- *	- '#' is like ':' in options, expect that the argument is optional
- *	  and must start with a digit. If the argument doesn't start with a
- *	  digit, it is assumed to be missing and normal option processing
- *	  continues (optarg is set to 0 if the option is missing).
- *	  Used for 'typeset -LZ4'.
- *	- accepts +c as well as -c IF the GF_PLUSOPT flag is present. If an
- *	  option starting with + is accepted, the GI_PLUS flag will be set
- *	  in go->info.
- */
-int
-ksh_getopt(const char **argv, Getopt *go, const char *optionsp)
-{
-	char c;
-	const char *o;
-
-	if (go->p == 0 || (c = argv[go->optind - 1][go->p]) == '\0') {
-		const char *arg = argv[go->optind], flag = arg ? *arg : '\0';
-
-		go->p = 1;
-		if (flag == '-' && arg[1] == '-' && arg[2] == '\0') {
-			go->optind++;
-			go->p = 0;
-			go->info |= GI_MINUSMINUS;
-			return (-1);
-		}
-		if (arg == NULL ||
-		    ((flag != '-' ) &&
-		    /* neither a - nor a + (if + allowed) */
-		    (!(go->flags & GF_PLUSOPT) || flag != '+')) ||
-		    (c = arg[1]) == '\0') {
-			go->p = 0;
-			return (-1);
-		}
-		go->optind++;
-		go->info &= ~(GI_MINUS|GI_PLUS);
-		go->info |= flag == '-' ? GI_MINUS : GI_PLUS;
-	}
-	go->p++;
-	if (c == '?' || c == ':' || c == ';' || c == ',' || c == '#' ||
-	    !(o = cstrchr(optionsp, c))) {
-		if (optionsp[0] == ':') {
-			go->buf[0] = c;
-			go->optarg = go->buf;
-		} else {
-			warningf(true, "%s%s-%c: %s",
-			    (go->flags & GF_NONAME) ? "" : argv[0],
-			    (go->flags & GF_NONAME) ? "" : ": ", c,
-			    "unknown option");
-			if (go->flags & GF_ERROR)
-				bi_errorfz();
-		}
-		return ('?');
-	}
-	/**
-	 * : means argument must be present, may be part of option argument
-	 *   or the next argument
-	 * ; same as : but argument may be missing
-	 * , means argument is part of option argument, and may be null.
-	 */
-	if (*++o == ':' || *o == ';') {
-		if (argv[go->optind - 1][go->p])
-			go->optarg = argv[go->optind - 1] + go->p;
-		else if (argv[go->optind])
-			go->optarg = argv[go->optind++];
-		else if (*o == ';')
-			go->optarg = NULL;
-		else {
-			if (optionsp[0] == ':') {
-				go->buf[0] = c;
-				go->optarg = go->buf;
-				return (':');
-			}
-			warningf(true, "%s%s-%c: %s",
-			    (go->flags & GF_NONAME) ? "" : argv[0],
-			    (go->flags & GF_NONAME) ? "" : ": ", c,
-			    "requires an argument");
-			if (go->flags & GF_ERROR)
-				bi_errorfz();
-			return ('?');
-		}
-		go->p = 0;
-	} else if (*o == ',') {
-		/* argument is attached to option character, even if null */
-		go->optarg = argv[go->optind - 1] + go->p;
-		go->p = 0;
-	} else if (*o == '#') {
-		/*
-		 * argument is optional and may be attached or unattached
-		 * but must start with a digit. optarg is set to 0 if the
-		 * argument is missing.
-		 */
-		if (argv[go->optind - 1][go->p]) {
-			if (ksh_isdigit(argv[go->optind - 1][go->p])) {
-				go->optarg = argv[go->optind - 1] + go->p;
-				go->p = 0;
-			} else
-				go->optarg = NULL;
-		} else {
-			if (argv[go->optind] && ksh_isdigit(argv[go->optind][0])) {
-				go->optarg = argv[go->optind++];
-				go->p = 0;
-			} else
-				go->optarg = NULL;
-		}
-	}
-	return (c);
-}
-
-/*
- * print variable/alias value using necessary quotes
- * (POSIX says they should be suitable for re-entry...)
- * No trailing newline is printed.
- */
-void
-print_value_quoted(struct shf *shf, const char *s)
-{
-	unsigned char c;
-	const unsigned char *p = (const unsigned char *)s;
-	bool inquote = true;
-
-	/* first, check whether any quotes are needed */
-	while ((c = *p++) >= 32)
-		if (ctype(c, C_QUOTE))
-			inquote = false;
-
-	p = (const unsigned char *)s;
-	if (c == 0) {
-		if (inquote) {
-			/* nope, use the shortcut */
-			shf_puts(s, shf);
-			return;
-		}
-
-		/* otherwise, quote nicely via state machine */
-		while ((c = *p++) != 0) {
-			if (c == '\'') {
-				/*
-				 * multiple single quotes or any of them
-				 * at the beginning of a string look nicer
-				 * this way than when simply substituting
-				 */
-				if (inquote) {
-					shf_putc('\'', shf);
-					inquote = false;
-				}
-				shf_putc('\\', shf);
-			} else if (!inquote) {
-				shf_putc('\'', shf);
-				inquote = true;
-			}
-			shf_putc(c, shf);
-		}
-	} else {
-		unsigned int wc;
-		size_t n;
-
-		/* use $'...' quote format */
-		shf_putc('$', shf);
-		shf_putc('\'', shf);
-		while ((c = *p) != 0) {
-			if (c >= 0xC2) {
-				n = utf_mbtowc(&wc, (const char *)p);
-				if (n != (size_t)-1) {
-					p += n;
-					shf_fprintf(shf, "\\u%04X", wc);
-					continue;
-				}
-			}
-			++p;
-			switch (c) {
-			/* see unbksl() in this file for comments */
-			case 7:
-				c = 'a';
-				if (0)
-					/* FALLTHROUGH */
-			case '\b':
-				  c = 'b';
-				if (0)
-					/* FALLTHROUGH */
-			case '\f':
-				  c = 'f';
-				if (0)
-					/* FALLTHROUGH */
-			case '\n':
-				  c = 'n';
-				if (0)
-					/* FALLTHROUGH */
-			case '\r':
-				  c = 'r';
-				if (0)
-					/* FALLTHROUGH */
-			case '\t':
-				  c = 't';
-				if (0)
-					/* FALLTHROUGH */
-			case 11:
-				  c = 'v';
-				if (0)
-					/* FALLTHROUGH */
-			case '\033':
-				/* take E not e because \e is \ in *roff */
-				  c = 'E';
-				/* FALLTHROUGH */
-			case '\\':
-				shf_putc('\\', shf);
-
-				if (0)
-					/* FALLTHROUGH */
-			default:
-				  if (c < 32 || c > 0x7E) {
-					/* FALLTHROUGH */
-			case '\'':
-					shf_fprintf(shf, "\\%03o", c);
-					break;
-				}
-
-				shf_putc(c, shf);
-				break;
-			}
-		}
-		inquote = true;
-	}
-	if (inquote)
-		shf_putc('\'', shf);
-}
-
-/*
- * Print things in columns and rows - func() is called to format
- * the i-th element
- */
-void
-print_columns(struct shf *shf, unsigned int n,
-    char *(*func)(char *, size_t, unsigned int, const void *),
-    const void *arg, size_t max_oct, size_t max_colz, bool prefcol)
-{
-	unsigned int i, r, c, rows, cols, nspace, max_col;
-	char *str;
-
-	if (!n)
-		return;
-
-	if (max_colz > 2147483646) {
-#ifndef MKSH_SMALL
-		internal_warningf("print_columns called with %s=%zu >= INT_MAX",
-		    "max_col", max_colz);
-#endif
-		return;
-	}
-	max_col = (unsigned int)max_colz;
-
-	if (max_oct > 2147483646) {
-#ifndef MKSH_SMALL
-		internal_warningf("print_columns called with %s=%zu >= INT_MAX",
-		    "max_oct", max_oct);
-#endif
-		return;
-	}
-	++max_oct;
-	str = alloc(max_oct, ATEMP);
-
-	/*
-	 * We use (max_col + 1) to consider the space separator.
-	 * Note that no space is printed after the last column
-	 * to avoid problems with terminals that have auto-wrap.
-	 */
-	cols = x_cols / (max_col + 1);
-
-	/* if we can only print one column anyway, skip the goo */
-	if (cols < 2) {
-		for (i = 0; i < n; ++i)
-			shf_fprintf(shf, "%s\n",
-			    (*func)(str, max_oct, i, arg));
-		goto out;
-	}
-
-	rows = (n + cols - 1) / cols;
-	if (prefcol && cols > rows) {
-		cols = rows;
-		rows = (n + cols - 1) / cols;
-	}
-
-	nspace = (x_cols - max_col * cols) / cols;
-	max_col = -max_col;
-	if (nspace <= 0)
-		nspace = 1;
-	for (r = 0; r < rows; r++) {
-		for (c = 0; c < cols; c++) {
-			i = c * rows + r;
-			if (i < n) {
-				shf_fprintf(shf, "%*s", max_col,
-				    (*func)(str, max_oct, i, arg));
-				if (c + 1 < cols)
-					shf_fprintf(shf, "%*s", nspace, null);
-			}
-		}
-		shf_putchar('\n', shf);
-	}
- out:
-	afree(str, ATEMP);
-}
-
-/* Strip any nul bytes from buf - returns new length (nbytes - # of nuls) */
-void
-strip_nuls(char *buf, int nbytes)
-{
-	char *dst;
-
-	/*
-	 * nbytes check because some systems (older FreeBSDs) have a
-	 * buggy memchr()
-	 */
-	if (nbytes && (dst = memchr(buf, '\0', nbytes))) {
-		char *end = buf + nbytes;
-		char *p, *q;
-
-		for (p = dst; p < end; p = q) {
-			/* skip a block of nulls */
-			while (++p < end && *p == '\0')
-				;
-			/* find end of non-null block */
-			if (!(q = memchr(p, '\0', end - p)))
-				q = end;
-			memmove(dst, p, q - p);
-			dst += q - p;
-		}
-		*dst = '\0';
-	}
-}
-
-/*
- * Like read(2), but if read fails due to non-blocking flag,
- * resets flag and restarts read.
- */
-ssize_t
-blocking_read(int fd, char *buf, size_t nbytes)
-{
-	ssize_t ret;
-	bool tried_reset = false;
-
-	while ((ret = read(fd, buf, nbytes)) < 0) {
-		if (!tried_reset && errno == EAGAIN) {
-			if (reset_nonblock(fd) > 0) {
-				tried_reset = true;
-				continue;
-			}
-			errno = EAGAIN;
-		}
-		break;
-	}
-	return (ret);
-}
-
-/*
- * Reset the non-blocking flag on the specified file descriptor.
- * Returns -1 if there was an error, 0 if non-blocking wasn't set,
- * 1 if it was.
- */
-int
-reset_nonblock(int fd)
-{
-	int flags;
-
-	if ((flags = fcntl(fd, F_GETFL, 0)) < 0)
-		return (-1);
-	if (!(flags & O_NONBLOCK))
-		return (0);
-	flags &= ~O_NONBLOCK;
-	if (fcntl(fd, F_SETFL, flags) < 0)
-		return (-1);
-	return (1);
-}
-
-/* getcwd(3) equivalent, allocates from ATEMP but doesn't resize */
-char *
-ksh_get_wd(void)
-{
-#ifdef MKSH__NO_PATH_MAX
-	char *rv, *cp;
-
-	if ((cp = get_current_dir_name())) {
-		strdupx(rv, cp, ATEMP);
-		free_gnu_gcdn(cp);
-	} else
-		rv = NULL;
-#else
-	char *rv;
-
-	if (!getcwd((rv = alloc(PATH_MAX + 1, ATEMP)), PATH_MAX)) {
-		afree(rv, ATEMP);
-		rv = NULL;
-	}
-#endif
-
-	return (rv);
-}
-
-#ifndef ELOOP
-#define ELOOP		E2BIG
-#endif
-
-char *
-do_realpath(const char *upath)
-{
-	char *xp, *ip, *tp, *ipath, *ldest = NULL;
-	XString xs;
-	ptrdiff_t pos;
-	size_t len;
-	int llen;
-	struct stat sb;
-#ifdef MKSH__NO_PATH_MAX
-	size_t ldestlen = 0;
-#define pathlen sb.st_size
-#define pathcnd (ldestlen < (pathlen + 1))
-#else
-#define pathlen PATH_MAX
-#define pathcnd (!ldest)
-#endif
-	/* max. recursion depth */
-	int symlinks = 32;
-
-	if (upath[0] == '/') {
-		/* upath is an absolute pathname */
-		strdupx(ipath, upath, ATEMP);
-	} else {
-		/* upath is a relative pathname, prepend cwd */
-		if ((tp = ksh_get_wd()) == NULL || tp[0] != '/')
-			return (NULL);
-		ipath = shf_smprintf("%s%s%s", tp, "/", upath);
-		afree(tp, ATEMP);
-	}
-
-	/* ipath and upath are in memory at the same time -> unchecked */
-	Xinit(xs, xp, strlen(ip = ipath) + 1, ATEMP);
-
-	/* now jump into the deep of the loop */
-	goto beginning_of_a_pathname;
-
-	while (*ip) {
-		/* skip slashes in input */
-		while (*ip == '/')
-			++ip;
-		if (!*ip)
-			break;
-
-		/* get next pathname component from input */
-		tp = ip;
-		while (*ip && *ip != '/')
-			++ip;
-		len = ip - tp;
-
-		/* check input for "." and ".." */
-		if (tp[0] == '.') {
-			if (len == 1)
-				/* just continue with the next one */
-				continue;
-			else if (len == 2 && tp[1] == '.') {
-				/* strip off last pathname component */
-				while (xp > Xstring(xs, xp))
-					if (*--xp == '/')
-						break;
-				/* then continue with the next one */
-				continue;
-			}
-		}
-
-		/* store output position away, then append slash to output */
-		pos = Xsavepos(xs, xp);
-		/* 1 for the '/' and len + 1 for tp and the NUL from below */
-		XcheckN(xs, xp, 1 + len + 1);
-		Xput(xs, xp, '/');
-
-		/* append next pathname component to output */
-		memcpy(xp, tp, len);
-		xp += len;
-		*xp = '\0';
-
-		/* lstat the current output, see if it's a symlink */
-		if (mksh_lstat(Xstring(xs, xp), &sb)) {
-			/* lstat failed */
-			if (errno == ENOENT) {
-				/* because the pathname does not exist */
-				while (*ip == '/')
-					/* skip any trailing slashes */
-					++ip;
-				/* no more components left? */
-				if (!*ip)
-					/* we can still return successfully */
-					break;
-				/* more components left? fall through */
-			}
-			/* not ENOENT or not at the end of ipath */
-			goto notfound;
-		}
-
-		/* check if we encountered a symlink? */
-		if (S_ISLNK(sb.st_mode)) {
-#ifndef MKSH__NO_SYMLINK
-			/* reached maximum recursion depth? */
-			if (!symlinks--) {
-				/* yep, prevent infinite loops */
-				errno = ELOOP;
-				goto notfound;
-			}
-
-			/* get symlink(7) target */
-			if (pathcnd) {
-#ifdef MKSH__NO_PATH_MAX
-				if (notoktoadd(pathlen, 1)) {
-					errno = ENAMETOOLONG;
-					goto notfound;
-				}
-#endif
-				ldest = aresize(ldest, pathlen + 1, ATEMP);
-			}
-			llen = readlink(Xstring(xs, xp), ldest, pathlen);
-			if (llen < 0)
-				/* oops... */
-				goto notfound;
-			ldest[llen] = '\0';
-
-			/*
-			 * restart if symlink target is an absolute path,
-			 * otherwise continue with currently resolved prefix
-			 */
-			/* append rest of current input path to link target */
-			tp = shf_smprintf("%s%s%s", ldest, *ip ? "/" : "", ip);
-			afree(ipath, ATEMP);
-			ip = ipath = tp;
-			if (ldest[0] != '/') {
-				/* symlink target is a relative path */
-				xp = Xrestpos(xs, xp, pos);
-			} else
-#endif
-			  {
-				/* symlink target is an absolute path */
-				xp = Xstring(xs, xp);
- beginning_of_a_pathname:
-				/* assert: (ip == ipath)[0] == '/' */
-				/* assert: xp == xs.beg => start of path */
-
-				/* exactly two leading slashes? (SUSv4 3.266) */
-				if (ip[1] == '/' && ip[2] != '/') {
-					/* keep them, e.g. for UNC pathnames */
-					Xput(xs, xp, '/');
-				}
-			}
-		}
-		/* otherwise (no symlink) merely go on */
-	}
-
-	/*
-	 * either found the target and successfully resolved it,
-	 * or found its parent directory and may create it
-	 */
-	if (Xlength(xs, xp) == 0)
-		/*
-		 * if the resolved pathname is "", make it "/",
-		 * otherwise do not add a trailing slash
-		 */
-		Xput(xs, xp, '/');
-	Xput(xs, xp, '\0');
-
-	/*
-	 * if source path had a trailing slash, check if target path
-	 * is not a non-directory existing file
-	 */
-	if (ip > ipath && ip[-1] == '/') {
-		if (stat(Xstring(xs, xp), &sb)) {
-			if (errno != ENOENT)
-				goto notfound;
-		} else if (!S_ISDIR(sb.st_mode)) {
-			errno = ENOTDIR;
-			goto notfound;
-		}
-		/* target now either does not exist or is a directory */
-	}
-
-	/* return target path */
-	if (ldest != NULL)
-		afree(ldest, ATEMP);
-	afree(ipath, ATEMP);
-	return (Xclose(xs, xp));
-
- notfound:
-	/* save; freeing memory might trash it */
-	llen = errno;
-	if (ldest != NULL)
-		afree(ldest, ATEMP);
-	afree(ipath, ATEMP);
-	Xfree(xs, xp);
-	errno = llen;
-	return (NULL);
-
-#undef pathlen
-#undef pathcnd
-}
-
-/**
- *	Makes a filename into result using the following algorithm.
- *	- make result NULL
- *	- if file starts with '/', append file to result & set cdpathp to NULL
- *	- if file starts with ./ or ../ append cwd and file to result
- *	  and set cdpathp to NULL
- *	- if the first element of cdpathp doesnt start with a '/' xx or '.' xx
- *	  then cwd is appended to result.
- *	- the first element of cdpathp is appended to result
- *	- file is appended to result
- *	- cdpathp is set to the start of the next element in cdpathp (or NULL
- *	  if there are no more elements.
- *	The return value indicates whether a non-null element from cdpathp
- *	was appended to result.
- */
-static int
-make_path(const char *cwd, const char *file,
-    /* pointer to colon-separated list */
-    char **cdpathp,
-    XString *xsp,
-    int *phys_pathp)
-{
-	int rval = 0;
-	bool use_cdpath = true;
-	char *plist;
-	size_t len, plen = 0;
-	char *xp = Xstring(*xsp, xp);
-
-	if (!file)
-		file = null;
-
-	if (file[0] == '/') {
-		*phys_pathp = 0;
-		use_cdpath = false;
-	} else {
-		if (file[0] == '.') {
-			char c = file[1];
-
-			if (c == '.')
-				c = file[2];
-			if (c == '/' || c == '\0')
-				use_cdpath = false;
-		}
-
-		plist = *cdpathp;
-		if (!plist)
-			use_cdpath = false;
-		else if (use_cdpath) {
-			char *pend;
-
-			for (pend = plist; *pend && *pend != ':'; pend++)
-				;
-			plen = pend - plist;
-			*cdpathp = *pend ? pend + 1 : NULL;
-		}
-
-		if ((!use_cdpath || !plen || plist[0] != '/') &&
-		    (cwd && *cwd)) {
-			len = strlen(cwd);
-			XcheckN(*xsp, xp, len);
-			memcpy(xp, cwd, len);
-			xp += len;
-			if (cwd[len - 1] != '/')
-				Xput(*xsp, xp, '/');
-		}
-		*phys_pathp = Xlength(*xsp, xp);
-		if (use_cdpath && plen) {
-			XcheckN(*xsp, xp, plen);
-			memcpy(xp, plist, plen);
-			xp += plen;
-			if (plist[plen - 1] != '/')
-				Xput(*xsp, xp, '/');
-			rval = 1;
-		}
-	}
-
-	len = strlen(file) + 1;
-	XcheckN(*xsp, xp, len);
-	memcpy(xp, file, len);
-
-	if (!use_cdpath)
-		*cdpathp = NULL;
-
-	return (rval);
-}
-
-/*-
- * Simplify pathnames containing "." and ".." entries.
- *
- * simplify_path(this)			= that
- * /a/b/c/./../d/..			/a/b
- * //./C/foo/bar/../baz			//C/foo/baz
- * /foo/				/foo
- * /foo/../../bar			/bar
- * /foo/./blah/..			/foo
- * .					.
- * ..					..
- * ./foo				foo
- * foo/../../../bar			../../bar
- */
-void
-simplify_path(char *p)
-{
-	char *dp, *ip, *sp, *tp;
-	size_t len;
-	bool needslash;
-
-	switch (*p) {
-	case 0:
-		return;
-	case '/':
-		/* exactly two leading slashes? (SUSv4 3.266) */
-		if (p[1] == '/' && p[2] != '/')
-			/* keep them, e.g. for UNC pathnames */
-			++p;
-		needslash = true;
-		break;
-	default:
-		needslash = false;
-	}
-	dp = ip = sp = p;
-
-	while (*ip) {
-		/* skip slashes in input */
-		while (*ip == '/')
-			++ip;
-		if (!*ip)
-			break;
-
-		/* get next pathname component from input */
-		tp = ip;
-		while (*ip && *ip != '/')
-			++ip;
-		len = ip - tp;
-
-		/* check input for "." and ".." */
-		if (tp[0] == '.') {
-			if (len == 1)
-				/* just continue with the next one */
-				continue;
-			else if (len == 2 && tp[1] == '.') {
-				/* parent level, but how? */
-				if (*p == '/')
-					/* absolute path, only one way */
-					goto strip_last_component;
-				else if (dp > sp) {
-					/* relative path, with subpaths */
-					needslash = false;
- strip_last_component:
-					/* strip off last pathname component */
-					while (dp > sp)
-						if (*--dp == '/')
-							break;
-				} else {
-					/* relative path, at its beginning */
-					if (needslash)
-						/* or already dotdot-slash'd */
-						*dp++ = '/';
-					/* keep dotdot-slash if not absolute */
-					*dp++ = '.';
-					*dp++ = '.';
-					needslash = true;
-					sp = dp;
-				}
-				/* then continue with the next one */
-				continue;
-			}
-		}
-
-		if (needslash)
-			*dp++ = '/';
-
-		/* append next pathname component to output */
-		memmove(dp, tp, len);
-		dp += len;
-
-		/* append slash if we continue */
-		needslash = true;
-		/* try next component */
-	}
-	if (dp == p)
-		/* empty path -> dot */
-		*dp++ = needslash ? '/' : '.';
-	*dp = '\0';
-}
-
-void
-set_current_wd(const char *nwd)
-{
-	char *allocd = NULL;
-
-	if (nwd == NULL) {
-		allocd = ksh_get_wd();
-		nwd = allocd ? allocd : null;
-	}
-
-	afree(current_wd, APERM);
-	strdupx(current_wd, nwd, APERM);
-
-	afree(allocd, ATEMP);
-}
-
-int
-c_cd(const char **wp)
-{
-	int optc, rv, phys_path;
-	bool physical = tobool(Flag(FPHYSICAL));
-	/* was a node from cdpath added in? */
-	int cdnode;
-	/* show where we went?, error for $PWD */
-	bool printpath = false, eflag = false;
-	struct tbl *pwd_s, *oldpwd_s;
-	XString xs;
-	char *dir, *allocd = NULL, *tryp, *pwd, *cdpath;
-
-	while ((optc = ksh_getopt(wp, &builtin_opt, "eLP")) != -1)
-		switch (optc) {
-		case 'e':
-			eflag = true;
-			break;
-		case 'L':
-			physical = false;
-			break;
-		case 'P':
-			physical = true;
-			break;
-		case '?':
-			return (2);
-		}
-	wp += builtin_opt.optind;
-
-	if (Flag(FRESTRICTED)) {
-		bi_errorf("restricted shell - can't cd");
-		return (2);
-	}
-
-	pwd_s = global("PWD");
-	oldpwd_s = global("OLDPWD");
-
-	if (!wp[0]) {
-		/* No arguments - go home */
-		if ((dir = str_val(global("HOME"))) == null) {
-			bi_errorf("no home directory (HOME not set)");
-			return (2);
-		}
-	} else if (!wp[1]) {
-		/* One argument: - or dir */
-		strdupx(allocd, wp[0], ATEMP);
-		if (ksh_isdash((dir = allocd))) {
-			afree(allocd, ATEMP);
-			allocd = NULL;
-			dir = str_val(oldpwd_s);
-			if (dir == null) {
-				bi_errorf("no OLDPWD");
-				return (2);
-			}
-			printpath = true;
-		}
-	} else if (!wp[2]) {
-		/* Two arguments - substitute arg1 in PWD for arg2 */
-		size_t ilen, olen, nlen, elen;
-		char *cp;
-
-		if (!current_wd[0]) {
-			bi_errorf("can't determine current directory");
-			return (2);
-		}
-		/*
-		 * substitute arg1 for arg2 in current path.
-		 * if the first substitution fails because the cd fails
-		 * we could try to find another substitution. For now
-		 * we don't
-		 */
-		if ((cp = strstr(current_wd, wp[0])) == NULL) {
-			bi_errorf("bad substitution");
-			return (2);
-		}
-		/*-
-		 * ilen = part of current_wd before wp[0]
-		 * elen = part of current_wd after wp[0]
-		 * because current_wd and wp[1] need to be in memory at the
-		 * same time beforehand the addition can stay unchecked
-		 */
-		ilen = cp - current_wd;
-		olen = strlen(wp[0]);
-		nlen = strlen(wp[1]);
-		elen = strlen(current_wd + ilen + olen) + 1;
-		dir = allocd = alloc(ilen + nlen + elen, ATEMP);
-		memcpy(dir, current_wd, ilen);
-		memcpy(dir + ilen, wp[1], nlen);
-		memcpy(dir + ilen + nlen, current_wd + ilen + olen, elen);
-		printpath = true;
-	} else {
-		bi_errorf("too many arguments");
-		return (2);
-	}
-
-#ifdef MKSH__NO_PATH_MAX
-	/* only a first guess; make_path will enlarge xs if necessary */
-	XinitN(xs, 1024, ATEMP);
-#else
-	XinitN(xs, PATH_MAX, ATEMP);
-#endif
-
-	cdpath = str_val(global("CDPATH"));
-	do {
-		cdnode = make_path(current_wd, dir, &cdpath, &xs, &phys_path);
-		if (physical)
-			rv = chdir(tryp = Xstring(xs, xp) + phys_path);
-		else {
-			simplify_path(Xstring(xs, xp));
-			rv = chdir(tryp = Xstring(xs, xp));
-		}
-	} while (rv < 0 && cdpath != NULL);
-
-	if (rv < 0) {
-		if (cdnode)
-			bi_errorf("%s: %s", dir, "bad directory");
-		else
-			bi_errorf("%s: %s", tryp, cstrerror(errno));
-		afree(allocd, ATEMP);
-		Xfree(xs, xp);
-		return (2);
-	}
-
-	rv = 0;
-
-	/* allocd (above) => dir, which is no longer used */
-	afree(allocd, ATEMP);
-	allocd = NULL;
-
-	/* Clear out tracked aliases with relative paths */
-	flushcom(false);
-
-	/*
-	 * Set OLDPWD (note: unsetting OLDPWD does not disable this
-	 * setting in AT&T ksh)
-	 */
-	if (current_wd[0])
-		/* Ignore failure (happens if readonly or integer) */
-		setstr(oldpwd_s, current_wd, KSH_RETURN_ERROR);
-
-	if (Xstring(xs, xp)[0] != '/') {
-		pwd = NULL;
-	} else if (!physical) {
-		goto norealpath_PWD;
-	} else if ((pwd = allocd = do_realpath(Xstring(xs, xp))) == NULL) {
-		if (eflag)
-			rv = 1;
- norealpath_PWD:
-		pwd = Xstring(xs, xp);
-	}
-
-	/* Set PWD */
-	if (pwd) {
-		char *ptmp = pwd;
-
-		set_current_wd(ptmp);
-		/* Ignore failure (happens if readonly or integer) */
-		setstr(pwd_s, ptmp, KSH_RETURN_ERROR);
-	} else {
-		set_current_wd(null);
-		pwd = Xstring(xs, xp);
-		/* XXX unset $PWD? */
-		if (eflag)
-			rv = 1;
-	}
-	if (printpath || cdnode)
-		shprintf("%s\n", pwd);
-
-	afree(allocd, ATEMP);
-	Xfree(xs, xp);
-	return (rv);
-}
-
-
-#ifdef KSH_CHVT_CODE
-extern uint32_t chvt_rndsetup(const void *, size_t);
-extern void chvt_reinit(void);
-
-static void
-chvt(const Getopt *go)
-{
-	const char *dv = go->optarg;
-	char *cp = NULL;
-	int fd;
-
-	switch (*dv) {
-	case '-':
-		dv = "/dev/null";
-		break;
-	case '!':
-		++dv;
-		/* FALLTHROUGH */
-	default: {
-		struct stat sb;
-
-		if (stat(dv, &sb)) {
-			cp = shf_smprintf("/dev/ttyC%s", dv);
-			dv = cp;
-			if (stat(dv, &sb)) {
-				memmove(cp + 1, cp, /* /dev/tty */ 8);
-				dv = cp + 1;
-				if (stat(dv, &sb)) {
-					errorf("%s: %s: %s", "chvt",
-					    "can't find tty", go->optarg);
-				}
-			}
-		}
-		if (!(sb.st_mode & S_IFCHR))
-			errorf("%s: %s: %s", "chvt", "not a char device", dv);
-#ifndef MKSH_DISABLE_REVOKE_WARNING
-#if HAVE_REVOKE
-		if (revoke(dv))
-#endif
-			warningf(false, "%s: %s %s", "chvt",
-			    "new shell is potentially insecure, can't revoke",
-			    dv);
-#endif
-	    }
-	}
-	if ((fd = open(dv, O_RDWR)) < 0) {
-		sleep(1);
-		if ((fd = open(dv, O_RDWR)) < 0) {
-			errorf("%s: %s %s", "chvt", "can't open", dv);
-		}
-	}
-	if (go->optarg[0] != '!') {
-		switch (fork()) {
-		case -1:
-			errorf("%s: %s %s", "chvt", "fork", "failed");
-		case 0:
-			break;
-		default:
-			exit(0);
-		}
-	}
-	if (setsid() == -1)
-		errorf("%s: %s %s", "chvt", "setsid", "failed");
-	if (go->optarg[0] != '-') {
-		if (ioctl(fd, TIOCSCTTY, NULL) == -1)
-			errorf("%s: %s %s", "chvt", "TIOCSCTTY", "failed");
-		if (tcflush(fd, TCIOFLUSH))
-			errorf("%s: %s %s", "chvt", "TCIOFLUSH", "failed");
-	}
-	ksh_dup2(fd, 0, false);
-	ksh_dup2(fd, 1, false);
-	ksh_dup2(fd, 2, false);
-	if (fd > 2)
-		close(fd);
-	rndset((unsigned long)chvt_rndsetup(go, sizeof(Getopt)));
-	chvt_reinit();
-}
-#endif
-
-#ifdef DEBUG
-char *
-strchr(char *p, int ch)
-{
-	for (;; ++p) {
-		if (*p == ch)
-			return (p);
-		if (!*p)
-			return (NULL);
-	}
-	/* NOTREACHED */
-}
-
-char *
-strstr(char *b, const char *l)
-{
-	char first, c;
-	size_t n;
-
-	if ((first = *l++) == '\0')
-		return (b);
-	n = strlen(l);
- strstr_look:
-	while ((c = *b++) != first)
-		if (c == '\0')
-			return (NULL);
-	if (strncmp(b, l, n))
-		goto strstr_look;
-	return (b - 1);
-}
-#endif
-
-#if defined(MKSH_SMALL) && !defined(MKSH_SMALL_BUT_FAST)
-char *
-strndup_i(const char *src, size_t len, Area *ap)
-{
-	char *dst = NULL;
-
-	if (src != NULL) {
-		dst = alloc(len + 1, ap);
-		memcpy(dst, src, len);
-		dst[len] = '\0';
-	}
-	return (dst);
-}
-
-char *
-strdup_i(const char *src, Area *ap)
-{
-	return (src == NULL ? NULL : strndup_i(src, strlen(src), ap));
-}
-#endif
-
-#if !HAVE_GETRUSAGE
-#define INVTCK(r,t)	do {						\
-	r.tv_usec = ((t) % (1000000 / CLK_TCK)) * (1000000 / CLK_TCK);	\
-	r.tv_sec = (t) / CLK_TCK;					\
-} while (/* CONSTCOND */ 0)
-
-int
-getrusage(int what, struct rusage *ru)
-{
-	struct tms tms;
-	clock_t u, s;
-
-	if (/* ru == NULL || */ times(&tms) == (clock_t)-1)
-		return (-1);
-
-	switch (what) {
-	case RUSAGE_SELF:
-		u = tms.tms_utime;
-		s = tms.tms_stime;
-		break;
-	case RUSAGE_CHILDREN:
-		u = tms.tms_cutime;
-		s = tms.tms_cstime;
-		break;
-	default:
-		errno = EINVAL;
-		return (-1);
-	}
-	INVTCK(ru->ru_utime, u);
-	INVTCK(ru->ru_stime, s);
-	return (0);
-}
-#endif
-
-/*
- * process the string available via fg (get a char)
- * and fp (put back a char) for backslash escapes,
- * assuming the first call to *fg gets the char di-
- * rectly after the backslash; return the character
- * (0..0xFF), Unicode (wc + 0x100), or -1 if no known
- * escape sequence was found
- */
-int
-unbksl(bool cstyle, int (*fg)(void), void (*fp)(int))
-{
-	int wc, i, c, fc;
-
-	fc = (*fg)();
-	switch (fc) {
-	case 'a':
-		/*
-		 * according to the comments in pdksh, \007 seems
-		 * to be more portable than \a (due to HP-UX cc,
-		 * Ultrix cc, old pcc, etc.) so we avoid the escape
-		 * sequence altogether in mksh and assume ASCII
-		 */
-		wc = 7;
-		break;
-	case 'b':
-		wc = '\b';
-		break;
-	case 'c':
-		if (!cstyle)
-			goto unknown_escape;
-		c = (*fg)();
-		wc = CTRL(c);
-		break;
-	case 'E':
-	case 'e':
-		wc = 033;
-		break;
-	case 'f':
-		wc = '\f';
-		break;
-	case 'n':
-		wc = '\n';
-		break;
-	case 'r':
-		wc = '\r';
-		break;
-	case 't':
-		wc = '\t';
-		break;
-	case 'v':
-		/* assume ASCII here as well */
-		wc = 11;
-		break;
-	case '1':
-	case '2':
-	case '3':
-	case '4':
-	case '5':
-	case '6':
-	case '7':
-		if (!cstyle)
-			goto unknown_escape;
-		/* FALLTHROUGH */
-	case '0':
-		if (cstyle)
-			(*fp)(fc);
-		/*
-		 * look for an octal number with up to three
-		 * digits, not counting the leading zero;
-		 * convert it to a raw octet
-		 */
-		wc = 0;
-		i = 3;
-		while (i--)
-			if ((c = (*fg)()) >= '0' && c <= '7')
-				wc = (wc << 3) + (c - '0');
-			else {
-				(*fp)(c);
-				break;
-			}
-		break;
-	case 'U':
-		i = 8;
-		if (/* CONSTCOND */ 0)
-		/* FALLTHROUGH */
-	case 'u':
-		i = 4;
-		if (/* CONSTCOND */ 0)
-		/* FALLTHROUGH */
-	case 'x':
-		i = cstyle ? -1 : 2;
-		/**
-		 * x:	look for a hexadecimal number with up to
-		 *	two (C style: arbitrary) digits; convert
-		 *	to raw octet (C style: Unicode if >0xFF)
-		 * u/U:	look for a hexadecimal number with up to
-		 *	four (U: eight) digits; convert to Unicode
-		 */
-		wc = 0;
-		while (i--) {
-			wc <<= 4;
-			if ((c = (*fg)()) >= '0' && c <= '9')
-				wc += c - '0';
-			else if (c >= 'A' && c <= 'F')
-				wc += c - 'A' + 10;
-			else if (c >= 'a' && c <= 'f')
-				wc += c - 'a' + 10;
-			else {
-				wc >>= 4;
-				(*fp)(c);
-				break;
-			}
-		}
-		if ((cstyle && wc > 0xFF) || fc != 'x')
-			/* Unicode marker */
-			wc += 0x100;
-		break;
-	case '\'':
-		if (!cstyle)
-			goto unknown_escape;
-		wc = '\'';
-		break;
-	case '\\':
-		wc = '\\';
-		break;
-	default:
- unknown_escape:
-		(*fp)(fc);
-		return (-1);
-	}
-
-	return (wc);
-}

Copied: vendor/MirOS/mksh/R50/misc.c (from rev 6707, vendor/MirOS/mksh/dist/misc.c)
===================================================================
--- vendor/MirOS/mksh/R50/misc.c	                        (rev 0)
+++ vendor/MirOS/mksh/R50/misc.c	2014-07-01 12:01:17 UTC (rev 6708)
@@ -0,0 +1,2252 @@
+/*	$OpenBSD: misc.c,v 1.38 2013/11/28 10:33:37 sobrado Exp $	*/
+/*	$OpenBSD: path.c,v 1.12 2005/03/30 17:16:37 deraadt Exp $	*/
+
+/*-
+ * Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
+ *		 2011, 2012, 2013, 2014
+ *	Thorsten Glaser <tg at mirbsd.org>
+ *
+ * Provided that these terms and disclaimer and all copyright notices
+ * are retained or reproduced in an accompanying document, permission
+ * is granted to deal in this work without restriction, including un-
+ * limited rights to use, publicly perform, distribute, sell, modify,
+ * merge, give away, or sublicence.
+ *
+ * This work is provided "AS IS" and WITHOUT WARRANTY of any kind, to
+ * the utmost extent permitted by applicable law, neither express nor
+ * implied; without malicious intent or gross negligence. In no event
+ * may a licensor, author or contributor be held liable for indirect,
+ * direct, other damage, loss, or other issues arising in any way out
+ * of dealing in the work, even if advised of the possibility of such
+ * damage or existence of a defect, except proven that it results out
+ * of said person's immediate fault when using the work as intended.
+ */
+
+#include "sh.h"
+#if !HAVE_GETRUSAGE
+#include <sys/times.h>
+#endif
+#if HAVE_GRP_H
+#include <grp.h>
+#endif
+
+__RCSID("$MirOS: src/bin/mksh/misc.c,v 1.219 2014/01/05 21:57:27 tg Exp $");
+
+#define KSH_CHVT_FLAG
+#ifdef MKSH_SMALL
+#undef KSH_CHVT_FLAG
+#endif
+#ifdef TIOCSCTTY
+#define KSH_CHVT_CODE
+#define KSH_CHVT_FLAG
+#endif
+#ifdef MKSH_LEGACY_MODE
+#undef KSH_CHVT_CODE
+#undef KSH_CHVT_FLAG
+#endif
+
+/* type bits for unsigned char */
+unsigned char chtypes[UCHAR_MAX + 1];
+
+static const unsigned char *pat_scan(const unsigned char *,
+    const unsigned char *, bool) MKSH_A_PURE;
+static int do_gmatch(const unsigned char *, const unsigned char *,
+    const unsigned char *, const unsigned char *) MKSH_A_PURE;
+static const unsigned char *cclass(const unsigned char *, unsigned char)
+    MKSH_A_PURE;
+#ifdef KSH_CHVT_CODE
+static void chvt(const Getopt *);
+#endif
+
+/*XXX this should go away */
+static int make_path(const char *, const char *, char **, XString *, int *);
+
+#ifdef SETUID_CAN_FAIL_WITH_EAGAIN
+/* we don't need to check for other codes, EPERM won't happen */
+#define DO_SETUID(func, argvec) do {					\
+	if ((func argvec) && errno == EAGAIN)				\
+		errorf("%s failed with EAGAIN, probably due to a"	\
+		    " too low process limit; aborting", #func);		\
+} while (/* CONSTCOND */ 0)
+#else
+#define DO_SETUID(func, argvec) func argvec
+#endif
+
+/*
+ * Fast character classes
+ */
+void
+setctypes(const char *s, int t)
+{
+	unsigned int i;
+
+	if (t & C_IFS) {
+		for (i = 0; i < UCHAR_MAX + 1; i++)
+			chtypes[i] &= ~C_IFS;
+		/* include \0 in C_IFS */
+		chtypes[0] |= C_IFS;
+	}
+	while (*s != 0)
+		chtypes[(unsigned char)*s++] |= t;
+}
+
+void
+initctypes(void)
+{
+	int c;
+
+	for (c = 'a'; c <= 'z'; c++)
+		chtypes[c] |= C_ALPHA;
+	for (c = 'A'; c <= 'Z'; c++)
+		chtypes[c] |= C_ALPHA;
+	chtypes['_'] |= C_ALPHA;
+	setctypes("0123456789", C_DIGIT);
+	/* \0 added automatically */
+	setctypes(TC_LEX1, C_LEX1);
+	setctypes("*@#!$-?", C_VAR1);
+	setctypes(TC_IFSWS, C_IFSWS);
+	setctypes("=-+?", C_SUBOP1);
+	setctypes("\t\n \"#$&'()*;<=>?[\\]`|", C_QUOTE);
+}
+
+/* called from XcheckN() to grow buffer */
+char *
+Xcheck_grow(XString *xsp, const char *xp, size_t more)
+{
+	const char *old_beg = xsp->beg;
+
+	if (more < xsp->len)
+		more = xsp->len;
+	/* (xsp->len + X_EXTRA) never overflows */
+	checkoktoadd(more, xsp->len + X_EXTRA);
+	xsp->beg = aresize(xsp->beg, (xsp->len += more) + X_EXTRA, xsp->areap);
+	xsp->end = xsp->beg + xsp->len;
+	return (xsp->beg + (xp - old_beg));
+}
+
+
+#define SHFLAGS_DEFNS
+#include "sh_flags.gen"
+
+#define OFC(i) (options[i][-2])
+#define OFF(i) (((const unsigned char *)options[i])[-1])
+#define OFN(i) (options[i])
+
+const char * const options[] = {
+#define SHFLAGS_ITEMS
+#include "sh_flags.gen"
+};
+
+/*
+ * translate -o option into F* constant (also used for test -o option)
+ */
+size_t
+option(const char *n)
+{
+	size_t i = 0;
+
+	if ((n[0] == '-' || n[0] == '+') && n[1] && !n[2])
+		while (i < NELEM(options)) {
+			if (OFC(i) == n[1])
+				return (i);
+			++i;
+		}
+	else
+		while (i < NELEM(options)) {
+			if (!strcmp(OFN(i), n))
+				return (i);
+			++i;
+		}
+
+	return ((size_t)-1);
+}
+
+struct options_info {
+	int opt_width;
+	int opts[NELEM(options)];
+};
+
+static char *options_fmt_entry(char *, size_t, unsigned int, const void *);
+static void printoptions(bool);
+
+/* format a single select menu item */
+static char *
+options_fmt_entry(char *buf, size_t buflen, unsigned int i, const void *arg)
+{
+	const struct options_info *oi = (const struct options_info *)arg;
+
+	shf_snprintf(buf, buflen, "%-*s %s",
+	    oi->opt_width, OFN(oi->opts[i]),
+	    Flag(oi->opts[i]) ? "on" : "off");
+	return (buf);
+}
+
+static void
+printoptions(bool verbose)
+{
+	size_t i = 0;
+
+	if (verbose) {
+		size_t n = 0, len, octs = 0;
+		struct options_info oi;
+
+		/* verbose version */
+		shf_puts("Current option settings\n", shl_stdout);
+
+		oi.opt_width = 0;
+		while (i < NELEM(options)) {
+			if ((len = strlen(OFN(i)))) {
+				oi.opts[n++] = i;
+				if (len > octs)
+					octs = len;
+				len = utf_mbswidth(OFN(i));
+				if ((int)len > oi.opt_width)
+					oi.opt_width = (int)len;
+			}
+			++i;
+		}
+		print_columns(shl_stdout, n, options_fmt_entry, &oi,
+		    octs + 4, oi.opt_width + 4, true);
+	} else {
+		/* short version like AT&T ksh93 */
+		shf_puts(Tset, shl_stdout);
+		while (i < NELEM(options)) {
+			if (Flag(i) && OFN(i)[0])
+				shprintf(" -o %s", OFN(i));
+			++i;
+		}
+		shf_putc('\n', shl_stdout);
+	}
+}
+
+char *
+getoptions(void)
+{
+	size_t i = 0;
+	char c, m[(int)FNFLAGS + 1];
+	char *cp = m;
+
+	while (i < NELEM(options)) {
+		if ((c = OFC(i)) && Flag(i))
+			*cp++ = c;
+		++i;
+	}
+	strndupx(cp, m, cp - m, ATEMP);
+	return (cp);
+}
+
+/* change a Flag(*) value; takes care of special actions */
+void
+change_flag(enum sh_flag f, int what, bool newset)
+{
+	unsigned char oldval;
+	unsigned char newval = (newset ? 1 : 0);
+
+	if (f == FXTRACE) {
+		change_xtrace(newval, true);
+		return;
+	}
+	oldval = Flag(f);
+	Flag(f) = newval = (newset ? 1 : 0);
+#ifndef MKSH_UNEMPLOYED
+	if (f == FMONITOR) {
+		if (what != OF_CMDLINE && newval != oldval)
+			j_change();
+	} else
+#endif
+#ifndef MKSH_NO_CMDLINE_EDITING
+	  if ((
+#if !MKSH_S_NOVI
+	    f == FVI ||
+#endif
+	    f == FEMACS || f == FGMACS) && newval) {
+#if !MKSH_S_NOVI
+		Flag(FVI) =
+#endif
+		    Flag(FEMACS) = Flag(FGMACS) = 0;
+		Flag(f) = newval;
+	} else
+#endif
+	  if (f == FPRIVILEGED && oldval && !newval) {
+		/* Turning off -p? */
+
+		/*XXX this can probably be optimised */
+		kshegid = kshgid = getgid();
+		ksheuid = kshuid = getuid();
+#if HAVE_SETRESUGID
+		DO_SETUID(setresgid, (kshegid, kshegid, kshegid));
+#if HAVE_SETGROUPS
+		/* setgroups doesn't EAGAIN on Linux */
+		setgroups(1, &kshegid);
+#endif
+		DO_SETUID(setresuid, (ksheuid, ksheuid, ksheuid));
+#else /* !HAVE_SETRESUGID */
+		/* seteuid, setegid, setgid don't EAGAIN on Linux */
+#ifndef MKSH__NO_SETEUGID
+		seteuid(ksheuid);
+#endif
+		DO_SETUID(setuid, (ksheuid));
+#ifndef MKSH__NO_SETEUGID
+		setegid(kshegid);
+#endif
+		setgid(kshegid);
+#endif /* !HAVE_SETRESUGID */
+	} else if ((f == FPOSIX || f == FSH) && newval) {
+		/* Turning on -o posix or -o sh? */
+		Flag(FBRACEEXPAND) = 0;
+	} else if (f == FTALKING) {
+		/* Changing interactive flag? */
+		if ((what == OF_CMDLINE || what == OF_SET) && procpid == kshpid)
+			Flag(FTALKING_I) = newval;
+	}
+}
+
+void
+change_xtrace(unsigned char newval, bool dosnapshot)
+{
+	if (!dosnapshot && newval == Flag(FXTRACE))
+		return;
+
+	if (Flag(FXTRACE) == 2) {
+		shf_putc('\n', shl_xtrace);
+		Flag(FXTRACE) = 1;
+		shf_flush(shl_xtrace);
+	}
+
+	if (!dosnapshot && Flag(FXTRACE) == 1)
+		switch (newval) {
+		case 1:
+			return;
+		case 2:
+			goto changed_xtrace;
+		}
+
+	shf_flush(shl_xtrace);
+	if (shl_xtrace->fd != 2)
+		close(shl_xtrace->fd);
+	if (!newval || (shl_xtrace->fd = savefd(2)) == -1)
+		shl_xtrace->fd = 2;
+
+ changed_xtrace:
+	if ((Flag(FXTRACE) = newval) == 2)
+		shf_puts(substitute(str_val(global("PS4")), 0), shl_xtrace);
+}
+
+/*
+ * Parse command line and set command arguments. Returns the index of
+ * non-option arguments, -1 if there is an error.
+ */
+int
+parse_args(const char **argv,
+    /* OF_CMDLINE or OF_SET */
+    int what,
+    bool *setargsp)
+{
+	static const char cmd_opts[] =
+#define SHFLAGS_NOT_SET
+#define SHFLAGS_OPTCS
+#include "sh_flags.gen"
+#undef SHFLAGS_NOT_SET
+	    ;
+	static const char set_opts[] =
+#define SHFLAGS_NOT_CMD
+#define SHFLAGS_OPTCS
+#include "sh_flags.gen"
+#undef SHFLAGS_NOT_CMD
+	    ;
+	bool set;
+	const char *opts;
+	const char *array = NULL;
+	Getopt go;
+	size_t i;
+	int optc, arrayset = 0;
+	bool sortargs = false;
+	bool fcompatseen = false;
+
+	if (what == OF_CMDLINE) {
+		const char *p = argv[0], *q;
+		/*
+		 * Set FLOGIN before parsing options so user can clear
+		 * flag using +l.
+		 */
+		if (*p != '-')
+			for (q = p; *q; )
+				if (*q++ == '/')
+					p = q;
+		Flag(FLOGIN) = (*p == '-');
+		opts = cmd_opts;
+	} else if (what == OF_FIRSTTIME) {
+		opts = cmd_opts;
+	} else
+		opts = set_opts;
+	ksh_getopt_reset(&go, GF_ERROR|GF_PLUSOPT);
+	while ((optc = ksh_getopt(argv, &go, opts)) != -1) {
+		set = tobool(!(go.info & GI_PLUS));
+		switch (optc) {
+		case 'A':
+			if (what == OF_FIRSTTIME)
+				break;
+			arrayset = set ? 1 : -1;
+			array = go.optarg;
+			break;
+
+		case 'o':
+			if (what == OF_FIRSTTIME)
+				break;
+			if (go.optarg == NULL) {
+				/*
+				 * lone -o: print options
+				 *
+				 * Note that on the command line, -o requires
+				 * an option (ie, can't get here if what is
+				 * OF_CMDLINE).
+				 */
+				printoptions(set);
+				break;
+			}
+			i = option(go.optarg);
+			if ((i == FPOSIX || i == FSH) && set && !fcompatseen) {
+				/*
+				 * If running 'set -o posix' or
+				 * 'set -o sh', turn off the other;
+				 * if running 'set -o posix -o sh'
+				 * allow both to be set though.
+				 */
+				Flag(FPOSIX) = 0;
+				Flag(FSH) = 0;
+				fcompatseen = true;
+			}
+			if ((i != (size_t)-1) && (set ? 1U : 0U) == Flag(i))
+				/*
+				 * Don't check the context if the flag
+				 * isn't changing - makes "set -o interactive"
+				 * work if you're already interactive. Needed
+				 * if the output of "set +o" is to be used.
+				 */
+				;
+			else if ((i != (size_t)-1) && (OFF(i) & what))
+				change_flag((enum sh_flag)i, what, set);
+			else {
+				bi_errorf("%s: %s", go.optarg, "bad option");
+				return (-1);
+			}
+			break;
+
+#ifdef KSH_CHVT_FLAG
+		case 'T':
+			if (what != OF_FIRSTTIME)
+				break;
+#ifndef KSH_CHVT_CODE
+			errorf("no TIOCSCTTY ioctl");
+#else
+			change_flag(FTALKING, OF_CMDLINE, true);
+			chvt(&go);
+			break;
+#endif
+#endif
+
+		case '?':
+			return (-1);
+
+		default:
+			if (what == OF_FIRSTTIME)
+				break;
+			/* -s: sort positional params (AT&T ksh stupidity) */
+			if (what == OF_SET && optc == 's') {
+				sortargs = true;
+				break;
+			}
+			for (i = 0; i < NELEM(options); i++)
+				if (optc == OFC(i) &&
+				    (what & OFF(i))) {
+					change_flag((enum sh_flag)i, what, set);
+					break;
+				}
+			if (i == NELEM(options))
+				internal_errorf("parse_args: '%c'", optc);
+		}
+	}
+	if (!(go.info & GI_MINUSMINUS) && argv[go.optind] &&
+	    (argv[go.optind][0] == '-' || argv[go.optind][0] == '+') &&
+	    argv[go.optind][1] == '\0') {
+		/* lone - clears -v and -x flags */
+		if (argv[go.optind][0] == '-') {
+			Flag(FVERBOSE) = 0;
+			change_xtrace(0, false);
+		}
+		/* set skips lone - or + option */
+		go.optind++;
+	}
+	if (setargsp)
+		/* -- means set $#/$* even if there are no arguments */
+		*setargsp = !arrayset && ((go.info & GI_MINUSMINUS) ||
+		    argv[go.optind]);
+
+	if (arrayset) {
+		const char *ccp = NULL;
+
+		mkssert(array != NULL);
+		if (*array)
+			ccp = skip_varname(array, false);
+		if (!ccp || !(!ccp[0] || (ccp[0] == '+' && !ccp[1]))) {
+			bi_errorf("%s: %s", array, "is not an identifier");
+			return (-1);
+		}
+	}
+	if (sortargs) {
+		for (i = go.optind; argv[i]; i++)
+			;
+		qsort(&argv[go.optind], i - go.optind, sizeof(void *),
+		    xstrcmp);
+	}
+	if (arrayset)
+		go.optind += set_array(array, tobool(arrayset > 0),
+		    argv + go.optind);
+
+	return (go.optind);
+}
+
+/* parse a decimal number: returns 0 if string isn't a number, 1 otherwise */
+int
+getn(const char *s, int *ai)
+{
+	char c;
+	mksh_ari_u num;
+	bool neg = false;
+
+	num.u = 0;
+
+	do {
+		c = *s++;
+	} while (ksh_isspace(c));
+
+	switch (c) {
+	case '-':
+		neg = true;
+		/* FALLTHROUGH */
+	case '+':
+		c = *s++;
+		break;
+	}
+
+	do {
+		if (!ksh_isdigit(c))
+			/* not numeric */
+			return (0);
+		if (num.u > 214748364U)
+			/* overflow on multiplication */
+			return (0);
+		num.u = num.u * 10U + (unsigned int)(c - '0');
+		/* now: num.u <= 2147483649U */
+	} while ((c = *s++));
+
+	if (num.u > (neg ? 2147483648U : 2147483647U))
+		/* overflow for signed 32-bit int */
+		return (0);
+
+	if (neg)
+		num.u = -num.u;
+	*ai = num.i;
+	return (1);
+}
+
+/**
+ * pattern simplifications:
+ * - @(x) -> x (not @(x|y) though)
+ * - ** -> *
+ */
+static void *
+simplify_gmatch_pattern(const unsigned char *sp)
+{
+	uint8_t c;
+	unsigned char *cp, *dp;
+	const unsigned char *ps, *se;
+
+	cp = alloc(strlen((const void *)sp) + 1, ATEMP);
+	goto simplify_gmatch_pat1a;
+
+	/* foo@(b@(a)r)b@(a|a)z -> foobarb@(a|a)z */
+ simplify_gmatch_pat1:
+	sp = cp;
+ simplify_gmatch_pat1a:
+	dp = cp;
+	se = sp + strlen((const void *)sp);
+	while ((c = *sp++)) {
+		if (!ISMAGIC(c)) {
+			*dp++ = c;
+			continue;
+		}
+		switch ((c = *sp++)) {
+		case 0x80|'@':
+		/* simile for @ */
+		case 0x80|' ':
+			/* check whether it has only one clause */
+			ps = pat_scan(sp, se, true);
+			if (!ps || ps[-1] != /*(*/ ')')
+				/* nope */
+				break;
+			/* copy inner clause until matching close */
+			ps -= 2;
+			while ((const unsigned char *)sp < ps)
+				*dp++ = *sp++;
+			/* skip MAGIC and closing parenthesis */
+			sp += 2;
+			/* copy the rest of the pattern */
+			memmove(dp, sp, strlen((const void *)sp) + 1);
+			/* redo from start */
+			goto simplify_gmatch_pat1;
+		}
+		*dp++ = MAGIC;
+		*dp++ = c;
+	}
+	*dp = '\0';
+
+	/* collapse adjacent asterisk wildcards */
+	sp = dp = cp;
+	while ((c = *sp++)) {
+		if (!ISMAGIC(c)) {
+			*dp++ = c;
+			continue;
+		}
+		switch ((c = *sp++)) {
+		case '*':
+			while (ISMAGIC(sp[0]) && sp[1] == c)
+				sp += 2;
+			break;
+		}
+		*dp++ = MAGIC;
+		*dp++ = c;
+	}
+	*dp = '\0';
+
+	/* return the result, allocated from ATEMP */
+	return (cp);
+}
+
+/* -------- gmatch.c -------- */
+
+/*
+ * int gmatch(string, pattern)
+ * char *string, *pattern;
+ *
+ * Match a pattern as in sh(1).
+ * pattern character are prefixed with MAGIC by expand.
+ */
+int
+gmatchx(const char *s, const char *p, bool isfile)
+{
+	const char *se, *pe;
+	char *pnew;
+	int rv;
+
+	if (s == NULL || p == NULL)
+		return (0);
+
+	se = s + strlen(s);
+	pe = p + strlen(p);
+	/*
+	 * isfile is false iff no syntax check has been done on
+	 * the pattern. If check fails, just to a strcmp().
+	 */
+	if (!isfile && !has_globbing(p, pe)) {
+		size_t len = pe - p + 1;
+		char tbuf[64];
+		char *t = len <= sizeof(tbuf) ? tbuf : alloc(len, ATEMP);
+		debunk(t, p, len);
+		return (!strcmp(t, s));
+	}
+
+	/*
+	 * since the do_gmatch() engine sucks so much, we must do some
+	 * pattern simplifications
+	 */
+	pnew = simplify_gmatch_pattern((const unsigned char *)p);
+	pe = pnew + strlen(pnew);
+
+	rv = do_gmatch((const unsigned char *)s, (const unsigned char *)se,
+	    (const unsigned char *)pnew, (const unsigned char *)pe);
+	afree(pnew, ATEMP);
+	return (rv);
+}
+
+/**
+ * Returns if p is a syntacticly correct globbing pattern, false
+ * if it contains no pattern characters or if there is a syntax error.
+ * Syntax errors are:
+ *	- [ with no closing ]
+ *	- imbalanced $(...) expression
+ *	- [...] and *(...) not nested (eg, [a$(b|]c), *(a[b|c]d))
+ */
+/*XXX
+ * - if no magic,
+ *	if dest given, copy to dst
+ *	return ?
+ * - if magic && (no globbing || syntax error)
+ *	debunk to dst
+ *	return ?
+ * - return ?
+ */
+int
+has_globbing(const char *xp, const char *xpe)
+{
+	const unsigned char *p = (const unsigned char *) xp;
+	const unsigned char *pe = (const unsigned char *) xpe;
+	int c;
+	int nest = 0, bnest = 0;
+	bool saw_glob = false;
+	/* inside [...] */
+	bool in_bracket = false;
+
+	for (; p < pe; p++) {
+		if (!ISMAGIC(*p))
+			continue;
+		if ((c = *++p) == '*' || c == '?')
+			saw_glob = true;
+		else if (c == '[') {
+			if (!in_bracket) {
+				saw_glob = true;
+				in_bracket = true;
+				if (ISMAGIC(p[1]) && p[2] == '!')
+					p += 2;
+				if (ISMAGIC(p[1]) && p[2] == ']')
+					p += 2;
+			}
+			/*XXX Do we need to check ranges here? POSIX Q */
+		} else if (c == ']') {
+			if (in_bracket) {
+				if (bnest)
+					/* [a*(b]) */
+					return (0);
+				in_bracket = false;
+			}
+		} else if ((c & 0x80) && vstrchr("*+?@! ", c & 0x7f)) {
+			saw_glob = true;
+			if (in_bracket)
+				bnest++;
+			else
+				nest++;
+		} else if (c == '|') {
+			if (in_bracket && !bnest)
+				/* *(a[foo|bar]) */
+				return (0);
+		} else if (c == /*(*/ ')') {
+			if (in_bracket) {
+				if (!bnest--)
+					/* *(a[b)c] */
+					return (0);
+			} else if (nest)
+				nest--;
+		}
+		/*
+		 * else must be a MAGIC-MAGIC, or MAGIC-!,
+		 * MAGIC--, MAGIC-], MAGIC-{, MAGIC-, MAGIC-}
+		 */
+	}
+	return (saw_glob && !in_bracket && !nest);
+}
+
+/* Function must return either 0 or 1 (assumed by code for 0x80|'!') */
+static int
+do_gmatch(const unsigned char *s, const unsigned char *se,
+    const unsigned char *p, const unsigned char *pe)
+{
+	unsigned char sc, pc;
+	const unsigned char *prest, *psub, *pnext;
+	const unsigned char *srest;
+
+	if (s == NULL || p == NULL)
+		return (0);
+	while (p < pe) {
+		pc = *p++;
+		sc = s < se ? *s : '\0';
+		s++;
+		if (!ISMAGIC(pc)) {
+			if (sc != pc)
+				return (0);
+			continue;
+		}
+		switch (*p++) {
+		case '[':
+			if (sc == 0 || (p = cclass(p, sc)) == NULL)
+				return (0);
+			break;
+
+		case '?':
+			if (sc == 0)
+				return (0);
+			if (UTFMODE) {
+				--s;
+				s += utf_ptradj((const void *)s);
+			}
+			break;
+
+		case '*':
+			if (p == pe)
+				return (1);
+			s--;
+			do {
+				if (do_gmatch(s, se, p, pe))
+					return (1);
+			} while (s++ < se);
+			return (0);
+
+		/**
+		 * [*+?@!](pattern|pattern|..)
+		 * This is also needed for ${..%..}, etc.
+		 */
+
+		/* matches one or more times */
+		case 0x80|'+':
+		/* matches zero or more times */
+		case 0x80|'*':
+			if (!(prest = pat_scan(p, pe, false)))
+				return (0);
+			s--;
+			/* take care of zero matches */
+			if (p[-1] == (0x80 | '*') &&
+			    do_gmatch(s, se, prest, pe))
+				return (1);
+			for (psub = p; ; psub = pnext) {
+				pnext = pat_scan(psub, pe, true);
+				for (srest = s; srest <= se; srest++) {
+					if (do_gmatch(s, srest, psub, pnext - 2) &&
+					    (do_gmatch(srest, se, prest, pe) ||
+					    (s != srest && do_gmatch(srest,
+					    se, p - 2, pe))))
+						return (1);
+				}
+				if (pnext == prest)
+					break;
+			}
+			return (0);
+
+		/* matches zero or once */
+		case 0x80|'?':
+		/* matches one of the patterns */
+		case 0x80|'@':
+		/* simile for @ */
+		case 0x80|' ':
+			if (!(prest = pat_scan(p, pe, false)))
+				return (0);
+			s--;
+			/* Take care of zero matches */
+			if (p[-1] == (0x80 | '?') &&
+			    do_gmatch(s, se, prest, pe))
+				return (1);
+			for (psub = p; ; psub = pnext) {
+				pnext = pat_scan(psub, pe, true);
+				srest = prest == pe ? se : s;
+				for (; srest <= se; srest++) {
+					if (do_gmatch(s, srest, psub, pnext - 2) &&
+					    do_gmatch(srest, se, prest, pe))
+						return (1);
+				}
+				if (pnext == prest)
+					break;
+			}
+			return (0);
+
+		/* matches none of the patterns */
+		case 0x80|'!':
+			if (!(prest = pat_scan(p, pe, false)))
+				return (0);
+			s--;
+			for (srest = s; srest <= se; srest++) {
+				int matched = 0;
+
+				for (psub = p; ; psub = pnext) {
+					pnext = pat_scan(psub, pe, true);
+					if (do_gmatch(s, srest, psub,
+					    pnext - 2)) {
+						matched = 1;
+						break;
+					}
+					if (pnext == prest)
+						break;
+				}
+				if (!matched &&
+				    do_gmatch(srest, se, prest, pe))
+					return (1);
+			}
+			return (0);
+
+		default:
+			if (sc != p[-1])
+				return (0);
+			break;
+		}
+	}
+	return (s == se);
+}
+
+static const unsigned char *
+cclass(const unsigned char *p, unsigned char sub)
+{
+	unsigned char c, d;
+	bool notp, found = false;
+	const unsigned char *orig_p = p;
+
+	if ((notp = tobool(ISMAGIC(*p) && *++p == '!')))
+		p++;
+	do {
+		c = *p++;
+		if (ISMAGIC(c)) {
+			c = *p++;
+			if ((c & 0x80) && !ISMAGIC(c)) {
+				/* extended pattern matching: *+?@! */
+				c &= 0x7F;
+				/* XXX the ( char isn't handled as part of [] */
+				if (c == ' ')
+					/* simile for @: plain (..) */
+					c = '(' /*)*/;
+			}
+		}
+		if (c == '\0')
+			/* No closing ] - act as if the opening [ was quoted */
+			return (sub == '[' ? orig_p : NULL);
+		if (ISMAGIC(p[0]) && p[1] == '-' &&
+		    (!ISMAGIC(p[2]) || p[3] != ']')) {
+			/* MAGIC- */
+			p += 2;
+			d = *p++;
+			if (ISMAGIC(d)) {
+				d = *p++;
+				if ((d & 0x80) && !ISMAGIC(d))
+					d &= 0x7f;
+			}
+			/* POSIX says this is an invalid expression */
+			if (c > d)
+				return (NULL);
+		} else
+			d = c;
+		if (c == sub || (c <= sub && sub <= d))
+			found = true;
+	} while (!(ISMAGIC(p[0]) && p[1] == ']'));
+
+	return ((found != notp) ? p+2 : NULL);
+}
+
+/* Look for next ) or | (if match_sep) in *(foo|bar) pattern */
+static const unsigned char *
+pat_scan(const unsigned char *p, const unsigned char *pe, bool match_sep)
+{
+	int nest = 0;
+
+	for (; p < pe; p++) {
+		if (!ISMAGIC(*p))
+			continue;
+		if ((*++p == /*(*/ ')' && nest-- == 0) ||
+		    (*p == '|' && match_sep && nest == 0))
+			return (p + 1);
+		if ((*p & 0x80) && vstrchr("*+?@! ", *p & 0x7f))
+			nest++;
+	}
+	return (NULL);
+}
+
+int
+xstrcmp(const void *p1, const void *p2)
+{
+	return (strcmp(*(const char * const *)p1, *(const char * const *)p2));
+}
+
+/* Initialise a Getopt structure */
+void
+ksh_getopt_reset(Getopt *go, int flags)
+{
+	go->optind = 1;
+	go->optarg = NULL;
+	go->p = 0;
+	go->flags = flags;
+	go->info = 0;
+	go->buf[1] = '\0';
+}
+
+
+/**
+ * getopt() used for shell built-in commands, the getopts command, and
+ * command line options.
+ * A leading ':' in options means don't print errors, instead return '?'
+ * or ':' and set go->optarg to the offending option character.
+ * If GF_ERROR is set (and option doesn't start with :), errors result in
+ * a call to bi_errorf().
+ *
+ * Non-standard features:
+ *	- ';' is like ':' in options, except the argument is optional
+ *	  (if it isn't present, optarg is set to 0).
+ *	  Used for 'set -o'.
+ *	- ',' is like ':' in options, except the argument always immediately
+ *	  follows the option character (optarg is set to the null string if
+ *	  the option is missing).
+ *	  Used for 'read -u2', 'print -u2' and fc -40.
+ *	- '#' is like ':' in options, expect that the argument is optional
+ *	  and must start with a digit. If the argument doesn't start with a
+ *	  digit, it is assumed to be missing and normal option processing
+ *	  continues (optarg is set to 0 if the option is missing).
+ *	  Used for 'typeset -LZ4'.
+ *	- accepts +c as well as -c IF the GF_PLUSOPT flag is present. If an
+ *	  option starting with + is accepted, the GI_PLUS flag will be set
+ *	  in go->info.
+ */
+int
+ksh_getopt(const char **argv, Getopt *go, const char *optionsp)
+{
+	char c;
+	const char *o;
+
+	if (go->p == 0 || (c = argv[go->optind - 1][go->p]) == '\0') {
+		const char *arg = argv[go->optind], flag = arg ? *arg : '\0';
+
+		go->p = 1;
+		if (flag == '-' && arg[1] == '-' && arg[2] == '\0') {
+			go->optind++;
+			go->p = 0;
+			go->info |= GI_MINUSMINUS;
+			return (-1);
+		}
+		if (arg == NULL ||
+		    ((flag != '-' ) &&
+		    /* neither a - nor a + (if + allowed) */
+		    (!(go->flags & GF_PLUSOPT) || flag != '+')) ||
+		    (c = arg[1]) == '\0') {
+			go->p = 0;
+			return (-1);
+		}
+		go->optind++;
+		go->info &= ~(GI_MINUS|GI_PLUS);
+		go->info |= flag == '-' ? GI_MINUS : GI_PLUS;
+	}
+	go->p++;
+	if (c == '?' || c == ':' || c == ';' || c == ',' || c == '#' ||
+	    !(o = cstrchr(optionsp, c))) {
+		if (optionsp[0] == ':') {
+			go->buf[0] = c;
+			go->optarg = go->buf;
+		} else {
+			warningf(true, "%s%s-%c: %s",
+			    (go->flags & GF_NONAME) ? "" : argv[0],
+			    (go->flags & GF_NONAME) ? "" : ": ", c,
+			    "unknown option");
+			if (go->flags & GF_ERROR)
+				bi_errorfz();
+		}
+		return ('?');
+	}
+	/**
+	 * : means argument must be present, may be part of option argument
+	 *   or the next argument
+	 * ; same as : but argument may be missing
+	 * , means argument is part of option argument, and may be null.
+	 */
+	if (*++o == ':' || *o == ';') {
+		if (argv[go->optind - 1][go->p])
+			go->optarg = argv[go->optind - 1] + go->p;
+		else if (argv[go->optind])
+			go->optarg = argv[go->optind++];
+		else if (*o == ';')
+			go->optarg = NULL;
+		else {
+			if (optionsp[0] == ':') {
+				go->buf[0] = c;
+				go->optarg = go->buf;
+				return (':');
+			}
+			warningf(true, "%s%s-%c: %s",
+			    (go->flags & GF_NONAME) ? "" : argv[0],
+			    (go->flags & GF_NONAME) ? "" : ": ", c,
+			    "requires an argument");
+			if (go->flags & GF_ERROR)
+				bi_errorfz();
+			return ('?');
+		}
+		go->p = 0;
+	} else if (*o == ',') {
+		/* argument is attached to option character, even if null */
+		go->optarg = argv[go->optind - 1] + go->p;
+		go->p = 0;
+	} else if (*o == '#') {
+		/*
+		 * argument is optional and may be attached or unattached
+		 * but must start with a digit. optarg is set to 0 if the
+		 * argument is missing.
+		 */
+		if (argv[go->optind - 1][go->p]) {
+			if (ksh_isdigit(argv[go->optind - 1][go->p])) {
+				go->optarg = argv[go->optind - 1] + go->p;
+				go->p = 0;
+			} else
+				go->optarg = NULL;
+		} else {
+			if (argv[go->optind] && ksh_isdigit(argv[go->optind][0])) {
+				go->optarg = argv[go->optind++];
+				go->p = 0;
+			} else
+				go->optarg = NULL;
+		}
+	}
+	return (c);
+}
+
+/*
+ * print variable/alias value using necessary quotes
+ * (POSIX says they should be suitable for re-entry...)
+ * No trailing newline is printed.
+ */
+void
+print_value_quoted(struct shf *shf, const char *s)
+{
+	unsigned char c;
+	const unsigned char *p = (const unsigned char *)s;
+	bool inquote = true;
+
+	/* first, check whether any quotes are needed */
+	while ((c = *p++) >= 32)
+		if (ctype(c, C_QUOTE))
+			inquote = false;
+
+	p = (const unsigned char *)s;
+	if (c == 0) {
+		if (inquote) {
+			/* nope, use the shortcut */
+			shf_puts(s, shf);
+			return;
+		}
+
+		/* otherwise, quote nicely via state machine */
+		while ((c = *p++) != 0) {
+			if (c == '\'') {
+				/*
+				 * multiple single quotes or any of them
+				 * at the beginning of a string look nicer
+				 * this way than when simply substituting
+				 */
+				if (inquote) {
+					shf_putc('\'', shf);
+					inquote = false;
+				}
+				shf_putc('\\', shf);
+			} else if (!inquote) {
+				shf_putc('\'', shf);
+				inquote = true;
+			}
+			shf_putc(c, shf);
+		}
+	} else {
+		unsigned int wc;
+		size_t n;
+
+		/* use $'...' quote format */
+		shf_putc('$', shf);
+		shf_putc('\'', shf);
+		while ((c = *p) != 0) {
+			if (c >= 0xC2) {
+				n = utf_mbtowc(&wc, (const char *)p);
+				if (n != (size_t)-1) {
+					p += n;
+					shf_fprintf(shf, "\\u%04X", wc);
+					continue;
+				}
+			}
+			++p;
+			switch (c) {
+			/* see unbksl() in this file for comments */
+			case 7:
+				c = 'a';
+				if (0)
+					/* FALLTHROUGH */
+			case '\b':
+				  c = 'b';
+				if (0)
+					/* FALLTHROUGH */
+			case '\f':
+				  c = 'f';
+				if (0)
+					/* FALLTHROUGH */
+			case '\n':
+				  c = 'n';
+				if (0)
+					/* FALLTHROUGH */
+			case '\r':
+				  c = 'r';
+				if (0)
+					/* FALLTHROUGH */
+			case '\t':
+				  c = 't';
+				if (0)
+					/* FALLTHROUGH */
+			case 11:
+				  c = 'v';
+				if (0)
+					/* FALLTHROUGH */
+			case '\033':
+				/* take E not e because \e is \ in *roff */
+				  c = 'E';
+				/* FALLTHROUGH */
+			case '\\':
+				shf_putc('\\', shf);
+
+				if (0)
+					/* FALLTHROUGH */
+			default:
+				  if (c < 32 || c > 0x7E) {
+					/* FALLTHROUGH */
+			case '\'':
+					shf_fprintf(shf, "\\%03o", c);
+					break;
+				}
+
+				shf_putc(c, shf);
+				break;
+			}
+		}
+		inquote = true;
+	}
+	if (inquote)
+		shf_putc('\'', shf);
+}
+
+/*
+ * Print things in columns and rows - func() is called to format
+ * the i-th element
+ */
+void
+print_columns(struct shf *shf, unsigned int n,
+    char *(*func)(char *, size_t, unsigned int, const void *),
+    const void *arg, size_t max_oct, size_t max_colz, bool prefcol)
+{
+	unsigned int i, r, c, rows, cols, nspace, max_col;
+	char *str;
+
+	if (!n)
+		return;
+
+	if (max_colz > 2147483646) {
+#ifndef MKSH_SMALL
+		internal_warningf("print_columns called with %s=%zu >= INT_MAX",
+		    "max_col", max_colz);
+#endif
+		return;
+	}
+	max_col = (unsigned int)max_colz;
+
+	if (max_oct > 2147483646) {
+#ifndef MKSH_SMALL
+		internal_warningf("print_columns called with %s=%zu >= INT_MAX",
+		    "max_oct", max_oct);
+#endif
+		return;
+	}
+	++max_oct;
+	str = alloc(max_oct, ATEMP);
+
+	/*
+	 * We use (max_col + 1) to consider the space separator.
+	 * Note that no space is printed after the last column
+	 * to avoid problems with terminals that have auto-wrap.
+	 */
+	cols = x_cols / (max_col + 1);
+
+	/* if we can only print one column anyway, skip the goo */
+	if (cols < 2) {
+		for (i = 0; i < n; ++i)
+			shf_fprintf(shf, "%s\n",
+			    (*func)(str, max_oct, i, arg));
+		goto out;
+	}
+
+	rows = (n + cols - 1) / cols;
+	if (prefcol && cols > rows) {
+		cols = rows;
+		rows = (n + cols - 1) / cols;
+	}
+
+	nspace = (x_cols - max_col * cols) / cols;
+	max_col = -max_col;
+	if (nspace <= 0)
+		nspace = 1;
+	for (r = 0; r < rows; r++) {
+		for (c = 0; c < cols; c++) {
+			i = c * rows + r;
+			if (i < n) {
+				shf_fprintf(shf, "%*s", max_col,
+				    (*func)(str, max_oct, i, arg));
+				if (c + 1 < cols)
+					shf_fprintf(shf, "%*s", nspace, null);
+			}
+		}
+		shf_putchar('\n', shf);
+	}
+ out:
+	afree(str, ATEMP);
+}
+
+/* Strip any nul bytes from buf - returns new length (nbytes - # of nuls) */
+void
+strip_nuls(char *buf, int nbytes)
+{
+	char *dst;
+
+	/*
+	 * nbytes check because some systems (older FreeBSDs) have a
+	 * buggy memchr()
+	 */
+	if (nbytes && (dst = memchr(buf, '\0', nbytes))) {
+		char *end = buf + nbytes;
+		char *p, *q;
+
+		for (p = dst; p < end; p = q) {
+			/* skip a block of nulls */
+			while (++p < end && *p == '\0')
+				;
+			/* find end of non-null block */
+			if (!(q = memchr(p, '\0', end - p)))
+				q = end;
+			memmove(dst, p, q - p);
+			dst += q - p;
+		}
+		*dst = '\0';
+	}
+}
+
+/*
+ * Like read(2), but if read fails due to non-blocking flag,
+ * resets flag and restarts read.
+ */
+ssize_t
+blocking_read(int fd, char *buf, size_t nbytes)
+{
+	ssize_t ret;
+	bool tried_reset = false;
+
+	while ((ret = read(fd, buf, nbytes)) < 0) {
+		if (!tried_reset && errno == EAGAIN) {
+			if (reset_nonblock(fd) > 0) {
+				tried_reset = true;
+				continue;
+			}
+			errno = EAGAIN;
+		}
+		break;
+	}
+	return (ret);
+}
+
+/*
+ * Reset the non-blocking flag on the specified file descriptor.
+ * Returns -1 if there was an error, 0 if non-blocking wasn't set,
+ * 1 if it was.
+ */
+int
+reset_nonblock(int fd)
+{
+	int flags;
+
+	if ((flags = fcntl(fd, F_GETFL, 0)) < 0)
+		return (-1);
+	if (!(flags & O_NONBLOCK))
+		return (0);
+	flags &= ~O_NONBLOCK;
+	if (fcntl(fd, F_SETFL, flags) < 0)
+		return (-1);
+	return (1);
+}
+
+/* getcwd(3) equivalent, allocates from ATEMP but doesn't resize */
+char *
+ksh_get_wd(void)
+{
+#ifdef MKSH__NO_PATH_MAX
+	char *rv, *cp;
+
+	if ((cp = get_current_dir_name())) {
+		strdupx(rv, cp, ATEMP);
+		free_gnu_gcdn(cp);
+	} else
+		rv = NULL;
+#else
+	char *rv;
+
+	if (!getcwd((rv = alloc(PATH_MAX + 1, ATEMP)), PATH_MAX)) {
+		afree(rv, ATEMP);
+		rv = NULL;
+	}
+#endif
+
+	return (rv);
+}
+
+#ifndef ELOOP
+#define ELOOP		E2BIG
+#endif
+
+char *
+do_realpath(const char *upath)
+{
+	char *xp, *ip, *tp, *ipath, *ldest = NULL;
+	XString xs;
+	ptrdiff_t pos;
+	size_t len;
+	int llen;
+	struct stat sb;
+#ifdef MKSH__NO_PATH_MAX
+	size_t ldestlen = 0;
+#define pathlen sb.st_size
+#define pathcnd (ldestlen < (pathlen + 1))
+#else
+#define pathlen PATH_MAX
+#define pathcnd (!ldest)
+#endif
+	/* max. recursion depth */
+	int symlinks = 32;
+
+	if (upath[0] == '/') {
+		/* upath is an absolute pathname */
+		strdupx(ipath, upath, ATEMP);
+	} else {
+		/* upath is a relative pathname, prepend cwd */
+		if ((tp = ksh_get_wd()) == NULL || tp[0] != '/')
+			return (NULL);
+		ipath = shf_smprintf("%s%s%s", tp, "/", upath);
+		afree(tp, ATEMP);
+	}
+
+	/* ipath and upath are in memory at the same time -> unchecked */
+	Xinit(xs, xp, strlen(ip = ipath) + 1, ATEMP);
+
+	/* now jump into the deep of the loop */
+	goto beginning_of_a_pathname;
+
+	while (*ip) {
+		/* skip slashes in input */
+		while (*ip == '/')
+			++ip;
+		if (!*ip)
+			break;
+
+		/* get next pathname component from input */
+		tp = ip;
+		while (*ip && *ip != '/')
+			++ip;
+		len = ip - tp;
+
+		/* check input for "." and ".." */
+		if (tp[0] == '.') {
+			if (len == 1)
+				/* just continue with the next one */
+				continue;
+			else if (len == 2 && tp[1] == '.') {
+				/* strip off last pathname component */
+				while (xp > Xstring(xs, xp))
+					if (*--xp == '/')
+						break;
+				/* then continue with the next one */
+				continue;
+			}
+		}
+
+		/* store output position away, then append slash to output */
+		pos = Xsavepos(xs, xp);
+		/* 1 for the '/' and len + 1 for tp and the NUL from below */
+		XcheckN(xs, xp, 1 + len + 1);
+		Xput(xs, xp, '/');
+
+		/* append next pathname component to output */
+		memcpy(xp, tp, len);
+		xp += len;
+		*xp = '\0';
+
+		/* lstat the current output, see if it's a symlink */
+		if (mksh_lstat(Xstring(xs, xp), &sb)) {
+			/* lstat failed */
+			if (errno == ENOENT) {
+				/* because the pathname does not exist */
+				while (*ip == '/')
+					/* skip any trailing slashes */
+					++ip;
+				/* no more components left? */
+				if (!*ip)
+					/* we can still return successfully */
+					break;
+				/* more components left? fall through */
+			}
+			/* not ENOENT or not at the end of ipath */
+			goto notfound;
+		}
+
+		/* check if we encountered a symlink? */
+		if (S_ISLNK(sb.st_mode)) {
+#ifndef MKSH__NO_SYMLINK
+			/* reached maximum recursion depth? */
+			if (!symlinks--) {
+				/* yep, prevent infinite loops */
+				errno = ELOOP;
+				goto notfound;
+			}
+
+			/* get symlink(7) target */
+			if (pathcnd) {
+#ifdef MKSH__NO_PATH_MAX
+				if (notoktoadd(pathlen, 1)) {
+					errno = ENAMETOOLONG;
+					goto notfound;
+				}
+#endif
+				ldest = aresize(ldest, pathlen + 1, ATEMP);
+			}
+			llen = readlink(Xstring(xs, xp), ldest, pathlen);
+			if (llen < 0)
+				/* oops... */
+				goto notfound;
+			ldest[llen] = '\0';
+
+			/*
+			 * restart if symlink target is an absolute path,
+			 * otherwise continue with currently resolved prefix
+			 */
+			/* append rest of current input path to link target */
+			tp = shf_smprintf("%s%s%s", ldest, *ip ? "/" : "", ip);
+			afree(ipath, ATEMP);
+			ip = ipath = tp;
+			if (ldest[0] != '/') {
+				/* symlink target is a relative path */
+				xp = Xrestpos(xs, xp, pos);
+			} else
+#endif
+			  {
+				/* symlink target is an absolute path */
+				xp = Xstring(xs, xp);
+ beginning_of_a_pathname:
+				/* assert: (ip == ipath)[0] == '/' */
+				/* assert: xp == xs.beg => start of path */
+
+				/* exactly two leading slashes? (SUSv4 3.266) */
+				if (ip[1] == '/' && ip[2] != '/') {
+					/* keep them, e.g. for UNC pathnames */
+					Xput(xs, xp, '/');
+				}
+			}
+		}
+		/* otherwise (no symlink) merely go on */
+	}
+
+	/*
+	 * either found the target and successfully resolved it,
+	 * or found its parent directory and may create it
+	 */
+	if (Xlength(xs, xp) == 0)
+		/*
+		 * if the resolved pathname is "", make it "/",
+		 * otherwise do not add a trailing slash
+		 */
+		Xput(xs, xp, '/');
+	Xput(xs, xp, '\0');
+
+	/*
+	 * if source path had a trailing slash, check if target path
+	 * is not a non-directory existing file
+	 */
+	if (ip > ipath && ip[-1] == '/') {
+		if (stat(Xstring(xs, xp), &sb)) {
+			if (errno != ENOENT)
+				goto notfound;
+		} else if (!S_ISDIR(sb.st_mode)) {
+			errno = ENOTDIR;
+			goto notfound;
+		}
+		/* target now either does not exist or is a directory */
+	}
+
+	/* return target path */
+	if (ldest != NULL)
+		afree(ldest, ATEMP);
+	afree(ipath, ATEMP);
+	return (Xclose(xs, xp));
+
+ notfound:
+	/* save; freeing memory might trash it */
+	llen = errno;
+	if (ldest != NULL)
+		afree(ldest, ATEMP);
+	afree(ipath, ATEMP);
+	Xfree(xs, xp);
+	errno = llen;
+	return (NULL);
+
+#undef pathlen
+#undef pathcnd
+}
+
+/**
+ *	Makes a filename into result using the following algorithm.
+ *	- make result NULL
+ *	- if file starts with '/', append file to result & set cdpathp to NULL
+ *	- if file starts with ./ or ../ append cwd and file to result
+ *	  and set cdpathp to NULL
+ *	- if the first element of cdpathp doesnt start with a '/' xx or '.' xx
+ *	  then cwd is appended to result.
+ *	- the first element of cdpathp is appended to result
+ *	- file is appended to result
+ *	- cdpathp is set to the start of the next element in cdpathp (or NULL
+ *	  if there are no more elements.
+ *	The return value indicates whether a non-null element from cdpathp
+ *	was appended to result.
+ */
+static int
+make_path(const char *cwd, const char *file,
+    /* pointer to colon-separated list */
+    char **cdpathp,
+    XString *xsp,
+    int *phys_pathp)
+{
+	int rval = 0;
+	bool use_cdpath = true;
+	char *plist;
+	size_t len, plen = 0;
+	char *xp = Xstring(*xsp, xp);
+
+	if (!file)
+		file = null;
+
+	if (file[0] == '/') {
+		*phys_pathp = 0;
+		use_cdpath = false;
+	} else {
+		if (file[0] == '.') {
+			char c = file[1];
+
+			if (c == '.')
+				c = file[2];
+			if (c == '/' || c == '\0')
+				use_cdpath = false;
+		}
+
+		plist = *cdpathp;
+		if (!plist)
+			use_cdpath = false;
+		else if (use_cdpath) {
+			char *pend;
+
+			for (pend = plist; *pend && *pend != ':'; pend++)
+				;
+			plen = pend - plist;
+			*cdpathp = *pend ? pend + 1 : NULL;
+		}
+
+		if ((!use_cdpath || !plen || plist[0] != '/') &&
+		    (cwd && *cwd)) {
+			len = strlen(cwd);
+			XcheckN(*xsp, xp, len);
+			memcpy(xp, cwd, len);
+			xp += len;
+			if (cwd[len - 1] != '/')
+				Xput(*xsp, xp, '/');
+		}
+		*phys_pathp = Xlength(*xsp, xp);
+		if (use_cdpath && plen) {
+			XcheckN(*xsp, xp, plen);
+			memcpy(xp, plist, plen);
+			xp += plen;
+			if (plist[plen - 1] != '/')
+				Xput(*xsp, xp, '/');
+			rval = 1;
+		}
+	}
+
+	len = strlen(file) + 1;
+	XcheckN(*xsp, xp, len);
+	memcpy(xp, file, len);
+
+	if (!use_cdpath)
+		*cdpathp = NULL;
+
+	return (rval);
+}
+
+/*-
+ * Simplify pathnames containing "." and ".." entries.
+ *
+ * simplify_path(this)			= that
+ * /a/b/c/./../d/..			/a/b
+ * //./C/foo/bar/../baz			//C/foo/baz
+ * /foo/				/foo
+ * /foo/../../bar			/bar
+ * /foo/./blah/..			/foo
+ * .					.
+ * ..					..
+ * ./foo				foo
+ * foo/../../../bar			../../bar
+ */
+void
+simplify_path(char *p)
+{
+	char *dp, *ip, *sp, *tp;
+	size_t len;
+	bool needslash;
+
+	switch (*p) {
+	case 0:
+		return;
+	case '/':
+		/* exactly two leading slashes? (SUSv4 3.266) */
+		if (p[1] == '/' && p[2] != '/')
+			/* keep them, e.g. for UNC pathnames */
+			++p;
+		needslash = true;
+		break;
+	default:
+		needslash = false;
+	}
+	dp = ip = sp = p;
+
+	while (*ip) {
+		/* skip slashes in input */
+		while (*ip == '/')
+			++ip;
+		if (!*ip)
+			break;
+
+		/* get next pathname component from input */
+		tp = ip;
+		while (*ip && *ip != '/')
+			++ip;
+		len = ip - tp;
+
+		/* check input for "." and ".." */
+		if (tp[0] == '.') {
+			if (len == 1)
+				/* just continue with the next one */
+				continue;
+			else if (len == 2 && tp[1] == '.') {
+				/* parent level, but how? */
+				if (*p == '/')
+					/* absolute path, only one way */
+					goto strip_last_component;
+				else if (dp > sp) {
+					/* relative path, with subpaths */
+					needslash = false;
+ strip_last_component:
+					/* strip off last pathname component */
+					while (dp > sp)
+						if (*--dp == '/')
+							break;
+				} else {
+					/* relative path, at its beginning */
+					if (needslash)
+						/* or already dotdot-slash'd */
+						*dp++ = '/';
+					/* keep dotdot-slash if not absolute */
+					*dp++ = '.';
+					*dp++ = '.';
+					needslash = true;
+					sp = dp;
+				}
+				/* then continue with the next one */
+				continue;
+			}
+		}
+
+		if (needslash)
+			*dp++ = '/';
+
+		/* append next pathname component to output */
+		memmove(dp, tp, len);
+		dp += len;
+
+		/* append slash if we continue */
+		needslash = true;
+		/* try next component */
+	}
+	if (dp == p)
+		/* empty path -> dot */
+		*dp++ = needslash ? '/' : '.';
+	*dp = '\0';
+}
+
+void
+set_current_wd(const char *nwd)
+{
+	char *allocd = NULL;
+
+	if (nwd == NULL) {
+		allocd = ksh_get_wd();
+		nwd = allocd ? allocd : null;
+	}
+
+	afree(current_wd, APERM);
+	strdupx(current_wd, nwd, APERM);
+
+	afree(allocd, ATEMP);
+}
+
+int
+c_cd(const char **wp)
+{
+	int optc, rv, phys_path;
+	bool physical = tobool(Flag(FPHYSICAL));
+	/* was a node from cdpath added in? */
+	int cdnode;
+	/* show where we went?, error for $PWD */
+	bool printpath = false, eflag = false;
+	struct tbl *pwd_s, *oldpwd_s;
+	XString xs;
+	char *dir, *allocd = NULL, *tryp, *pwd, *cdpath;
+
+	while ((optc = ksh_getopt(wp, &builtin_opt, "eLP")) != -1)
+		switch (optc) {
+		case 'e':
+			eflag = true;
+			break;
+		case 'L':
+			physical = false;
+			break;
+		case 'P':
+			physical = true;
+			break;
+		case '?':
+			return (2);
+		}
+	wp += builtin_opt.optind;
+
+	if (Flag(FRESTRICTED)) {
+		bi_errorf("restricted shell - can't cd");
+		return (2);
+	}
+
+	pwd_s = global("PWD");
+	oldpwd_s = global("OLDPWD");
+
+	if (!wp[0]) {
+		/* No arguments - go home */
+		if ((dir = str_val(global("HOME"))) == null) {
+			bi_errorf("no home directory (HOME not set)");
+			return (2);
+		}
+	} else if (!wp[1]) {
+		/* One argument: - or dir */
+		strdupx(allocd, wp[0], ATEMP);
+		if (ksh_isdash((dir = allocd))) {
+			afree(allocd, ATEMP);
+			allocd = NULL;
+			dir = str_val(oldpwd_s);
+			if (dir == null) {
+				bi_errorf("no OLDPWD");
+				return (2);
+			}
+			printpath = true;
+		}
+	} else if (!wp[2]) {
+		/* Two arguments - substitute arg1 in PWD for arg2 */
+		size_t ilen, olen, nlen, elen;
+		char *cp;
+
+		if (!current_wd[0]) {
+			bi_errorf("can't determine current directory");
+			return (2);
+		}
+		/*
+		 * substitute arg1 for arg2 in current path.
+		 * if the first substitution fails because the cd fails
+		 * we could try to find another substitution. For now
+		 * we don't
+		 */
+		if ((cp = strstr(current_wd, wp[0])) == NULL) {
+			bi_errorf("bad substitution");
+			return (2);
+		}
+		/*-
+		 * ilen = part of current_wd before wp[0]
+		 * elen = part of current_wd after wp[0]
+		 * because current_wd and wp[1] need to be in memory at the
+		 * same time beforehand the addition can stay unchecked
+		 */
+		ilen = cp - current_wd;
+		olen = strlen(wp[0]);
+		nlen = strlen(wp[1]);
+		elen = strlen(current_wd + ilen + olen) + 1;
+		dir = allocd = alloc(ilen + nlen + elen, ATEMP);
+		memcpy(dir, current_wd, ilen);
+		memcpy(dir + ilen, wp[1], nlen);
+		memcpy(dir + ilen + nlen, current_wd + ilen + olen, elen);
+		printpath = true;
+	} else {
+		bi_errorf("too many arguments");
+		return (2);
+	}
+
+#ifdef MKSH__NO_PATH_MAX
+	/* only a first guess; make_path will enlarge xs if necessary */
+	XinitN(xs, 1024, ATEMP);
+#else
+	XinitN(xs, PATH_MAX, ATEMP);
+#endif
+
+	cdpath = str_val(global("CDPATH"));
+	do {
+		cdnode = make_path(current_wd, dir, &cdpath, &xs, &phys_path);
+		if (physical)
+			rv = chdir(tryp = Xstring(xs, xp) + phys_path);
+		else {
+			simplify_path(Xstring(xs, xp));
+			rv = chdir(tryp = Xstring(xs, xp));
+		}
+	} while (rv < 0 && cdpath != NULL);
+
+	if (rv < 0) {
+		if (cdnode)
+			bi_errorf("%s: %s", dir, "bad directory");
+		else
+			bi_errorf("%s: %s", tryp, cstrerror(errno));
+		afree(allocd, ATEMP);
+		Xfree(xs, xp);
+		return (2);
+	}
+
+	rv = 0;
+
+	/* allocd (above) => dir, which is no longer used */
+	afree(allocd, ATEMP);
+	allocd = NULL;
+
+	/* Clear out tracked aliases with relative paths */
+	flushcom(false);
+
+	/*
+	 * Set OLDPWD (note: unsetting OLDPWD does not disable this
+	 * setting in AT&T ksh)
+	 */
+	if (current_wd[0])
+		/* Ignore failure (happens if readonly or integer) */
+		setstr(oldpwd_s, current_wd, KSH_RETURN_ERROR);
+
+	if (Xstring(xs, xp)[0] != '/') {
+		pwd = NULL;
+	} else if (!physical) {
+		goto norealpath_PWD;
+	} else if ((pwd = allocd = do_realpath(Xstring(xs, xp))) == NULL) {
+		if (eflag)
+			rv = 1;
+ norealpath_PWD:
+		pwd = Xstring(xs, xp);
+	}
+
+	/* Set PWD */
+	if (pwd) {
+		char *ptmp = pwd;
+
+		set_current_wd(ptmp);
+		/* Ignore failure (happens if readonly or integer) */
+		setstr(pwd_s, ptmp, KSH_RETURN_ERROR);
+	} else {
+		set_current_wd(null);
+		pwd = Xstring(xs, xp);
+		/* XXX unset $PWD? */
+		if (eflag)
+			rv = 1;
+	}
+	if (printpath || cdnode)
+		shprintf("%s\n", pwd);
+
+	afree(allocd, ATEMP);
+	Xfree(xs, xp);
+	return (rv);
+}
+
+
+#ifdef KSH_CHVT_CODE
+extern void chvt_reinit(void);
+
+static void
+chvt(const Getopt *go)
+{
+	const char *dv = go->optarg;
+	char *cp = NULL;
+	int fd;
+
+	switch (*dv) {
+	case '-':
+		dv = "/dev/null";
+		break;
+	case '!':
+		++dv;
+		/* FALLTHROUGH */
+	default: {
+		struct stat sb;
+
+		if (stat(dv, &sb)) {
+			cp = shf_smprintf("/dev/ttyC%s", dv);
+			dv = cp;
+			if (stat(dv, &sb)) {
+				memmove(cp + 1, cp, /* /dev/tty */ 8);
+				dv = cp + 1;
+				if (stat(dv, &sb)) {
+					errorf("%s: %s: %s", "chvt",
+					    "can't find tty", go->optarg);
+				}
+			}
+		}
+		if (!(sb.st_mode & S_IFCHR))
+			errorf("%s: %s: %s", "chvt", "not a char device", dv);
+#ifndef MKSH_DISABLE_REVOKE_WARNING
+#if HAVE_REVOKE
+		if (revoke(dv))
+#endif
+			warningf(false, "%s: %s %s", "chvt",
+			    "new shell is potentially insecure, can't revoke",
+			    dv);
+#endif
+	    }
+	}
+	if ((fd = open(dv, O_RDWR | O_BINARY)) < 0) {
+		sleep(1);
+		if ((fd = open(dv, O_RDWR | O_BINARY)) < 0) {
+			errorf("%s: %s %s", "chvt", "can't open", dv);
+		}
+	}
+	if (go->optarg[0] != '!') {
+		switch (fork()) {
+		case -1:
+			errorf("%s: %s %s", "chvt", "fork", "failed");
+		case 0:
+			break;
+		default:
+			exit(0);
+		}
+	}
+	if (setsid() == -1)
+		errorf("%s: %s %s", "chvt", "setsid", "failed");
+	if (go->optarg[0] != '-') {
+		if (ioctl(fd, TIOCSCTTY, NULL) == -1)
+			errorf("%s: %s %s", "chvt", "TIOCSCTTY", "failed");
+		if (tcflush(fd, TCIOFLUSH))
+			errorf("%s: %s %s", "chvt", "TCIOFLUSH", "failed");
+	}
+	ksh_dup2(fd, 0, false);
+	ksh_dup2(fd, 1, false);
+	ksh_dup2(fd, 2, false);
+	if (fd > 2)
+		close(fd);
+	rndset((unsigned long)chvt_rndsetup(go, sizeof(Getopt)));
+	chvt_reinit();
+}
+#endif
+
+#ifdef DEBUG
+char *
+strchr(char *p, int ch)
+{
+	for (;; ++p) {
+		if (*p == ch)
+			return (p);
+		if (!*p)
+			return (NULL);
+	}
+	/* NOTREACHED */
+}
+
+char *
+strstr(char *b, const char *l)
+{
+	char first, c;
+	size_t n;
+
+	if ((first = *l++) == '\0')
+		return (b);
+	n = strlen(l);
+ strstr_look:
+	while ((c = *b++) != first)
+		if (c == '\0')
+			return (NULL);
+	if (strncmp(b, l, n))
+		goto strstr_look;
+	return (b - 1);
+}
+#endif
+
+#if defined(MKSH_SMALL) && !defined(MKSH_SMALL_BUT_FAST)
+char *
+strndup_i(const char *src, size_t len, Area *ap)
+{
+	char *dst = NULL;
+
+	if (src != NULL) {
+		dst = alloc(len + 1, ap);
+		memcpy(dst, src, len);
+		dst[len] = '\0';
+	}
+	return (dst);
+}
+
+char *
+strdup_i(const char *src, Area *ap)
+{
+	return (src == NULL ? NULL : strndup_i(src, strlen(src), ap));
+}
+#endif
+
+#if !HAVE_GETRUSAGE
+#define INVTCK(r,t)	do {						\
+	r.tv_usec = ((t) % (1000000 / CLK_TCK)) * (1000000 / CLK_TCK);	\
+	r.tv_sec = (t) / CLK_TCK;					\
+} while (/* CONSTCOND */ 0)
+
+int
+getrusage(int what, struct rusage *ru)
+{
+	struct tms tms;
+	clock_t u, s;
+
+	if (/* ru == NULL || */ times(&tms) == (clock_t)-1)
+		return (-1);
+
+	switch (what) {
+	case RUSAGE_SELF:
+		u = tms.tms_utime;
+		s = tms.tms_stime;
+		break;
+	case RUSAGE_CHILDREN:
+		u = tms.tms_cutime;
+		s = tms.tms_cstime;
+		break;
+	default:
+		errno = EINVAL;
+		return (-1);
+	}
+	INVTCK(ru->ru_utime, u);
+	INVTCK(ru->ru_stime, s);
+	return (0);
+}
+#endif
+
+/*
+ * process the string available via fg (get a char)
+ * and fp (put back a char) for backslash escapes,
+ * assuming the first call to *fg gets the char di-
+ * rectly after the backslash; return the character
+ * (0..0xFF), Unicode (wc + 0x100), or -1 if no known
+ * escape sequence was found
+ */
+int
+unbksl(bool cstyle, int (*fg)(void), void (*fp)(int))
+{
+	int wc, i, c, fc;
+
+	fc = (*fg)();
+	switch (fc) {
+	case 'a':
+		/*
+		 * according to the comments in pdksh, \007 seems
+		 * to be more portable than \a (due to HP-UX cc,
+		 * Ultrix cc, old pcc, etc.) so we avoid the escape
+		 * sequence altogether in mksh and assume ASCII
+		 */
+		wc = 7;
+		break;
+	case 'b':
+		wc = '\b';
+		break;
+	case 'c':
+		if (!cstyle)
+			goto unknown_escape;
+		c = (*fg)();
+		wc = CTRL(c);
+		break;
+	case 'E':
+	case 'e':
+		wc = 033;
+		break;
+	case 'f':
+		wc = '\f';
+		break;
+	case 'n':
+		wc = '\n';
+		break;
+	case 'r':
+		wc = '\r';
+		break;
+	case 't':
+		wc = '\t';
+		break;
+	case 'v':
+		/* assume ASCII here as well */
+		wc = 11;
+		break;
+	case '1':
+	case '2':
+	case '3':
+	case '4':
+	case '5':
+	case '6':
+	case '7':
+		if (!cstyle)
+			goto unknown_escape;
+		/* FALLTHROUGH */
+	case '0':
+		if (cstyle)
+			(*fp)(fc);
+		/*
+		 * look for an octal number with up to three
+		 * digits, not counting the leading zero;
+		 * convert it to a raw octet
+		 */
+		wc = 0;
+		i = 3;
+		while (i--)
+			if ((c = (*fg)()) >= '0' && c <= '7')
+				wc = (wc << 3) + (c - '0');
+			else {
+				(*fp)(c);
+				break;
+			}
+		break;
+	case 'U':
+		i = 8;
+		if (/* CONSTCOND */ 0)
+		/* FALLTHROUGH */
+	case 'u':
+		i = 4;
+		if (/* CONSTCOND */ 0)
+		/* FALLTHROUGH */
+	case 'x':
+		i = cstyle ? -1 : 2;
+		/**
+		 * x:	look for a hexadecimal number with up to
+		 *	two (C style: arbitrary) digits; convert
+		 *	to raw octet (C style: Unicode if >0xFF)
+		 * u/U:	look for a hexadecimal number with up to
+		 *	four (U: eight) digits; convert to Unicode
+		 */
+		wc = 0;
+		while (i--) {
+			wc <<= 4;
+			if ((c = (*fg)()) >= '0' && c <= '9')
+				wc += c - '0';
+			else if (c >= 'A' && c <= 'F')
+				wc += c - 'A' + 10;
+			else if (c >= 'a' && c <= 'f')
+				wc += c - 'a' + 10;
+			else {
+				wc >>= 4;
+				(*fp)(c);
+				break;
+			}
+		}
+		if ((cstyle && wc > 0xFF) || fc != 'x')
+			/* Unicode marker */
+			wc += 0x100;
+		break;
+	case '\'':
+		if (!cstyle)
+			goto unknown_escape;
+		wc = '\'';
+		break;
+	case '\\':
+		wc = '\\';
+		break;
+	default:
+ unknown_escape:
+		(*fp)(fc);
+		return (-1);
+	}
+
+	return (wc);
+}

Deleted: vendor/MirOS/mksh/R50/mksh.1
===================================================================
--- vendor/MirOS/mksh/dist/mksh.1	2014-06-30 23:58:12 UTC (rev 6706)
+++ vendor/MirOS/mksh/R50/mksh.1	2014-07-01 12:01:17 UTC (rev 6708)
@@ -1,6451 +0,0 @@
-.\" $MirOS: src/bin/mksh/mksh.1,v 1.320 2013/08/10 14:11:39 tg Exp $
-.\" $OpenBSD: ksh.1,v 1.147 2013/06/13 19:43:09 millert Exp $
-.\"-
-.\" Copyright © 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009,
-.\"		2010, 2011, 2012, 2013
-.\"	Thorsten Glaser <tg at mirbsd.org>
-.\"
-.\" Provided that these terms and disclaimer and all copyright notices
-.\" are retained or reproduced in an accompanying document, permission
-.\" is granted to deal in this work without restriction, including un‐
-.\" limited rights to use, publicly perform, distribute, sell, modify,
-.\" merge, give away, or sublicence.
-.\"
-.\" This work is provided “AS IS” and WITHOUT WARRANTY of any kind, to
-.\" the utmost extent permitted by applicable law, neither express nor
-.\" implied; without malicious intent or gross negligence. In no event
-.\" may a licensor, author or contributor be held liable for indirect,
-.\" direct, other damage, loss, or other issues arising in any way out
-.\" of dealing in the work, even if advised of the possibility of such
-.\" damage or existence of a defect, except proven that it results out
-.\" of said person’s immediate fault when using the work as intended.
-.\"-
-.\" Try to make GNU groff and AT&T nroff more compatible
-.\" * ` generates ‘ in gnroff, so use \`
-.\" * ' generates ’ in gnroff, \' generates ´, so use \*(aq
-.\" * - generates ‐ in gnroff, \- generates −, so .tr it to -
-.\"   thus use - for hyphens and \- for minus signs and option dashes
-.\" * ~ is size-reduced and placed atop in groff, so use \*(TI
-.\" * ^ is size-reduced and placed atop in groff, so use \*(ha
-.\" * \(en does not work in nroff, so use \*(en
-.\" * <>| are problematic, so redefine and use \*(Lt\*(Gt\*(Ba
-.\" Also make sure to use \& especially with two-letter words.
-.\" The section after the "doc" macropackage has been loaded contains
-.\" additional code to convene between the UCB mdoc macropackage (and
-.\" its variant as BSD mdoc in groff) and the GNU mdoc macropackage.
-.\"
-.ie \n(.g \{\
-.	if \*[.T]ascii .tr \-\N'45'
-.	if \*[.T]latin1 .tr \-\N'45'
-.	if \*[.T]utf8 .tr \-\N'45'
-.	ds <= \[<=]
-.	ds >= \[>=]
-.	ds Rq \[rq]
-.	ds Lq \[lq]
-.	ds sL \(aq
-.	ds sR \(aq
-.	if \*[.T]utf8 .ds sL `
-.	if \*[.T]ps .ds sL `
-.	if \*[.T]utf8 .ds sR '
-.	if \*[.T]ps .ds sR '
-.	ds aq \(aq
-.	ds TI \(ti
-.	ds ha \(ha
-.	ds en \(en
-.\}
-.el \{\
-.	ds aq '
-.	ds TI ~
-.	ds ha ^
-.	ds en \(em
-.\}
-.\"
-.\" Implement .Dd with the Mdocdate RCS keyword
-.\"
-.rn Dd xD
-.de Dd
-.ie \\$1$Mdocdate: \{\
-.	xD \\$2 \\$3, \\$4
-.\}
-.el .xD \\$1 \\$2 \\$3 \\$4 \\$5 \\$6 \\$7 \\$8
-..
-.\"
-.\" .Dd must come before definition of .Mx, because when called
-.\" with -mandoc, it might implement .Mx itself, but we want to
-.\" use our own definition. And .Dd must come *first*, always.
-.\"
-.Dd $Mdocdate: August 10 2013 $
-.\"
-.\" Check which macro package we use, and do other -mdoc setup.
-.\"
-.ie \n(.g \{\
-.	if \*[.T]utf8 .tr \[la]\*(Lt
-.	if \*[.T]utf8 .tr \[ra]\*(Gt
-.	ie d volume-ds-1 .ds tT gnu
-.	el .ds tT bsd
-.\}
-.el .ds tT ucb
-.\"
-.\" Implement .Mx (MirBSD)
-.\"
-.ie "\*(tT"gnu" \{\
-.	eo
-.	de Mx
-.	nr curr-font \n[.f]
-.	nr curr-size \n[.ps]
-.	ds str-Mx \f[\n[curr-font]]\s[\n[curr-size]u]
-.	ds str-Mx1 \*[Tn-font-size]\%MirOS\*[str-Mx]
-.	if !\n[arg-limit] \
-.	if \n[.$] \{\
-.	ds macro-name Mx
-.	parse-args \$@
-.	\}
-.	if (\n[arg-limit] > \n[arg-ptr]) \{\
-.	nr arg-ptr +1
-.	ie (\n[type\n[arg-ptr]] == 2) \
-.	as str-Mx1 \~\*[arg\n[arg-ptr]]
-.	el \
-.	nr arg-ptr -1
-.	\}
-.	ds arg\n[arg-ptr] "\*[str-Mx1]
-.	nr type\n[arg-ptr] 2
-.	ds space\n[arg-ptr] "\*[space]
-.	nr num-args (\n[arg-limit] - \n[arg-ptr])
-.	nr arg-limit \n[arg-ptr]
-.	if \n[num-args] \
-.	parse-space-vector
-.	print-recursive
-..
-.	ec
-.	ds sP \s0
-.	ds tN \*[Tn-font-size]
-.\}
-.el \{\
-.	de Mx
-.	nr cF \\n(.f
-.	nr cZ \\n(.s
-.	ds aa \&\f\\n(cF\s\\n(cZ
-.	if \\n(aC==0 \{\
-.		ie \\n(.$==0 \&MirOS\\*(aa
-.		el .aV \\$1 \\$2 \\$3 \\$4 \\$5 \\$6 \\$7 \\$8 \\$9
-.	\}
-.	if \\n(aC>\\n(aP \{\
-.		nr aP \\n(aP+1
-.		ie \\n(C\\n(aP==2 \{\
-.			as b1 \&MirOS\ #\&\\*(A\\n(aP\\*(aa
-.			ie \\n(aC>\\n(aP \{\
-.				nr aP \\n(aP+1
-.				nR
-.			\}
-.			el .aZ
-.		\}
-.		el \{\
-.			as b1 \&MirOS\\*(aa
-.			nR
-.		\}
-.	\}
-..
-.\}
-.\"-
-.Dt MKSH 1
-.Os MirBSD
-.Sh NAME
-.Nm mksh ,
-.Nm sh
-.Nd MirBSD Korn shell
-.Sh SYNOPSIS
-.Nm
-.Bk -words
-.Op Fl +abCefhiklmnprUuvXx
-.Oo
-.Fl T Oo Ar \&! Oc Ns Ar tty
-\*(Ba
-.Ar \&\-
-.Oc
-.Op Fl +o Ar option
-.Oo
-.Fl c Ar string \*(Ba
-.Fl s \*(Ba
-.Ar file
-.Op Ar argument ...
-.Oc
-.Ek
-.Nm builtin-name
-.Op Ar argument ...
-.Sh DESCRIPTION
-.Nm
-is a command interpreter intended for both interactive and shell
-script use.
-Its command language is a superset of the
-.Xr sh C
-shell language and largely compatible to the original Korn shell.
-.Ss I'm an Android user, so what's mksh?
-.Nm mksh
-is a
-.Ux
-shell / command interpreter, similar to
-.Nm COMMAND.COM
-or
-.Nm CMD.EXE ,
-which has been included with
-.Tn Android Open Source Project
-for a while now.
-Basically, it's a program that runs in a terminal (console window),
-takes user input and runs commands or scripts, which it can also
-be asked to do by other programs, even in the background.
-Any privilege pop-ups you might be encountering are thus not
-.Nm mksh
-issues but questions by some other program utilising it.
-.Ss Invocation
-Most builtins can be called directly, for example if a link points from its
-name to the shell; not all make sense, have been tested or work at all though.
-.Pp
-The options are as follows:
-.Bl -tag -width XcXstring
-.It Fl c Ar string
-.Nm
-will execute the command(s) contained in
-.Ar string .
-.It Fl i
-Interactive shell.
-A shell is
-.Dq interactive
-if this
-option is used or if both standard input and standard error are attached
-to a
-.Xr tty 4 .
-An interactive shell has job control enabled, ignores the
-.Dv SIGINT ,
-.Dv SIGQUIT ,
-and
-.Dv SIGTERM
-signals, and prints prompts before reading input (see the
-.Ev PS1
-and
-.Ev PS2
-parameters).
-It also processes the
-.Ev ENV
-parameter or the
-.Pa mkshrc
-file (see below).
-For non-interactive shells, the
-.Ic trackall
-option is on by default (see the
-.Ic set
-command below).
-.It Fl l
-Login shell.
-If the basename the shell is called with (i.e. argv[0])
-starts with
-.Ql \-
-or if this option is used,
-the shell is assumed to be a login shell; see
-.Sx Startup files
-below.
-.It Fl p
-Privileged shell.
-A shell is
-.Dq privileged
-if this option is used
-or if the real user ID or group ID does not match the
-effective user ID or group ID (see
-.Xr getuid 2
-and
-.Xr getgid 2 ) .
-Clearing the privileged option causes the shell to set
-its effective user ID (group ID) to its real user ID (group ID).
-For further implications, see
-.Sx Startup files .
-.It Fl r
-Restricted shell.
-A shell is
-.Dq restricted
-if this
-option is used.
-The following restrictions come into effect after the shell processes any
-profile and
-.Ev ENV
-files:
-.Pp
-.Bl -bullet -compact
-.It
-The
-.Ic cd
-.Po and Ic chdir Pc
-command is disabled.
-.It
-The
-.Ev SHELL ,
-.Ev ENV ,
-and
-.Ev PATH
-parameters cannot be changed.
-.It
-Command names can't be specified with absolute or relative paths.
-.It
-The
-.Fl p
-option of the built-in command
-.Ic command
-can't be used.
-.It
-Redirections that create files can't be used (i.e.\&
-.Ql \*(Gt ,
-.Ql \*(Gt\*(Ba ,
-.Ql \*(Gt\*(Gt ,
-.Ql \*(Lt\*(Gt ) .
-.El
-.It Fl s
-The shell reads commands from standard input; all non-option arguments
-are positional parameters.
-.It Fl T Ar name
-Spawn
-.Nm
-on the
-.Xr tty 4
-device given.
-The paths
-.Ar name ,
-.Pa /dev/ttyC Ns Ar name
-and
-.Pa /dev/tty Ns Ar name
-are attempted in order.
-Unless
-.Ar name
-begins with an exclamation mark
-.Pq Sq \&! ,
-this is done in a subshell and returns immediately.
-If
-.Ar name
-is a dash
-.Pq Sq \&\- ,
-detach from controlling terminal (daemonise) instead.
-.El
-.Pp
-In addition to the above, the options described in the
-.Ic set
-built-in command can also be used on the command line:
-both
-.Op Fl +abCefhkmnuvXx
-and
-.Op Fl +o Ar option
-can be used for single letter or long options, respectively.
-.Pp
-If neither the
-.Fl c
-nor the
-.Fl s
-option is specified, the first non-option argument specifies the name
-of a file the shell reads commands from.
-If there are no non-option
-arguments, the shell reads commands from the standard input.
-The name of the shell (i.e. the contents of $0)
-is determined as follows: if the
-.Fl c
-option is used and there is a non-option argument, it is used as the name;
-if commands are being read from a file, the file is used as the name;
-otherwise, the basename the shell was called with (i.e. argv[0]) is used.
-.Pp
-The exit status of the shell is 127 if the command file specified on the
-command line could not be opened, or non-zero if a fatal syntax error
-occurred during the execution of a script.
-In the absence of fatal errors,
-the exit status is that of the last command executed, or zero, if no
-command is executed.
-.Ss Startup files
-For the actual location of these files, see
-.Sx FILES .
-A login shell processes the system profile first.
-A privileged shell then processes the suid profile.
-A non-privileged login shell processes the user profile next.
-A non-privileged interactive shell checks the value of the
-.Ev ENV
-parameter after subjecting it to parameter, command, arithmetic and tilde
-.Pq Sq \*(TI
-substitution; if unset or empty, the user mkshrc profile is processed;
-otherwise, if a file whose name is the substitution result exists,
-it is processed; non-existence is silently ignored.
-.Ss Command syntax
-The shell begins parsing its input by removing any backslash-newline
-combinations, then breaking it into
-.Em words .
-Words (which are sequences of characters) are delimited by unquoted whitespace
-characters (space, tab, and newline) or meta-characters
-.Po
-.Ql \*(Lt ,
-.Ql \*(Gt ,
-.Ql \*(Ba ,
-.Ql \&; ,
-.Ql \&( ,
-.Ql \&) ,
-and
-.Ql &
-.Pc .
-Aside from delimiting words, spaces and tabs are ignored, while newlines
-usually delimit commands.
-The meta-characters are used in building the following
-.Em tokens :
-.Ql \*(Lt ,
-.Ql \*(Lt& ,
-.Ql \*(Lt\*(Lt ,
-.Ql \*(Lt\*(Lt\*(Lt ,
-.Ql \*(Gt ,
-.Ql \*(Gt& ,
-.Ql \*(Gt\*(Gt ,
-.Ql &\*(Gt ,
-etc. are used to specify redirections (see
-.Sx Input/output redirection
-below);
-.Ql \*(Ba
-is used to create pipelines;
-.Ql \*(Ba&
-is used to create co-processes (see
-.Sx Co-processes
-below);
-.Ql \&;
-is used to separate commands;
-.Ql &
-is used to create asynchronous pipelines;
-.Ql &&
-and
-.Ql \*(Ba\*(Ba
-are used to specify conditional execution;
-.Ql ;; ,
-.Ql ;&\&
-and
-.Ql ;\*(Ba\&
-are used in
-.Ic case
-statements;
-.Ql \&(( .. ))
-is used in arithmetic expressions;
-and lastly,
-.Ql \&( .. )\&
-is used to create subshells.
-.Pp
-Whitespace and meta-characters can be quoted individually using a backslash
-.Pq Sq \e ,
-or in groups using double
-.Pq Sq \&"
-or single
-.Pq Sq \*(aq
-quotes.
-Note that the following characters are also treated specially by the
-shell and must be quoted if they are to represent themselves:
-.Ql \e ,
-.Ql \&" ,
-.Ql \*(aq ,
-.Ql # ,
-.Ql $ ,
-.Ql \` ,
-.Ql \*(TI ,
-.Ql { ,
-.Ql } ,
-.Ql * ,
-.Ql \&? ,
-and
-.Ql \&[ .
-The first three of these are the above mentioned quoting characters (see
-.Sx Quoting
-below);
-.Ql # ,
-if used at the beginning of a word, introduces a comment \*(en everything after
-the
-.Ql #
-up to the nearest newline is ignored;
-.Ql $
-is used to introduce parameter, command, and arithmetic substitutions (see
-.Sx Substitution
-below);
-.Ql \`
-introduces an old-style command substitution (see
-.Sx Substitution
-below);
-.Ql \*(TI
-begins a directory expansion (see
-.Sx Tilde expansion
-below);
-.Ql {
-and
-.Ql }
-delimit
-.Xr csh 1 Ns -style
-alterations (see
-.Sx Brace expansion
-below);
-and finally,
-.Ql * ,
-.Ql \&? ,
-and
-.Ql \&[
-are used in file name generation (see
-.Sx File name patterns
-below).
-.Pp
-As words and tokens are parsed, the shell builds commands, of which there
-are two basic types:
-.Em simple-commands ,
-typically programmes that are executed, and
-.Em compound-commands ,
-such as
-.Ic for
-and
-.Ic if
-statements, grouping constructs, and function definitions.
-.Pp
-A simple-command consists of some combination of parameter assignments
-(see
-.Sx Parameters
-below),
-input/output redirections (see
-.Sx Input/output redirections
-below),
-and command words; the only restriction is that parameter assignments come
-before any command words.
-The command words, if any, define the command
-that is to be executed and its arguments.
-The command may be a shell built-in command, a function,
-or an external command
-(i.e. a separate executable file that is located using the
-.Ev PATH
-parameter; see
-.Sx Command execution
-below).
-Note that all command constructs have an exit status: for external commands,
-this is related to the status returned by
-.Xr wait 2
-(if the command could not be found, the exit status is 127; if it could not
-be executed, the exit status is 126); the exit status of other command
-constructs (built-in commands, functions, compound-commands, pipelines, lists,
-etc.) are all well-defined and are described where the construct is
-described.
-The exit status of a command consisting only of parameter
-assignments is that of the last command substitution performed during the
-parameter assignment or 0 if there were no command substitutions.
-.Pp
-Commands can be chained together using the
-.Ql \*(Ba
-token to form pipelines, in which the standard output of each command but the
-last is piped (see
-.Xr pipe 2 )
-to the standard input of the following command.
-The exit status of a pipeline is that of its last command, unless the
-.Ic pipefail
-option is set (see there).
-All commands of a pipeline are executed in separate subshells;
-this is allowed by POSIX but differs from both variants of
-.At
-.Nm ksh ,
-where all but the last command were executed in subshells; see the
-.Ic read
-builtin's description for implications and workarounds.
-A pipeline may be prefixed by the
-.Ql \&!
-reserved word which causes the exit status of the pipeline to be logically
-complemented: if the original status was 0, the complemented status will be 1;
-if the original status was not 0, the complemented status will be 0.
-.Pp
-.Em Lists
-of commands can be created by separating pipelines by any of the following
-tokens:
-.Ql && ,
-.Ql \*(Ba\*(Ba ,
-.Ql & ,
-.Ql \*(Ba& ,
-and
-.Ql \&; .
-The first two are for conditional execution:
-.Dq Ar cmd1 No && Ar cmd2
-executes
-.Ar cmd2
-only if the exit status of
-.Ar cmd1
-is zero;
-.Ql \*(Ba\*(Ba
-is the opposite \*(en
-.Ar cmd2
-is executed only if the exit status of
-.Ar cmd1
-is non-zero.
-.Ql &&
-and
-.Ql \*(Ba\*(Ba
-have equal precedence which is higher than that of
-.Ql & ,
-.Ql \*(Ba& ,
-and
-.Ql \&; ,
-which also have equal precedence.
-Note that the
-.Ql &&
-and
-.Ql \*(Ba\*(Ba
-operators are
-.Qq left-associative .
-For example, both of these commands will print only
-.Qq bar :
-.Bd -literal -offset indent
-$ false && echo foo \*(Ba\*(Ba echo bar
-$ true \*(Ba\*(Ba echo foo && echo bar
-.Ed
-.Pp
-The
-.Ql &
-token causes the preceding command to be executed asynchronously; that is,
-the shell starts the command but does not wait for it to complete (the shell
-does keep track of the status of asynchronous commands; see
-.Sx Job control
-below).
-When an asynchronous command is started when job control is disabled
-(i.e. in most scripts), the command is started with signals
-.Dv SIGINT
-and
-.Dv SIGQUIT
-ignored and with input redirected from
-.Pa /dev/null
-(however, redirections specified in the asynchronous command have precedence).
-The
-.Ql \*(Ba&
-operator starts a co-process which is a special kind of asynchronous process
-(see
-.Sx Co-processes
-below).
-Note that a command must follow the
-.Ql &&
-and
-.Ql \*(Ba\*(Ba
-operators, while it need not follow
-.Ql & ,
-.Ql \*(Ba& ,
-or
-.Ql \&; .
-The exit status of a list is that of the last command executed, with the
-exception of asynchronous lists, for which the exit status is 0.
-.Pp
-Compound commands are created using the following reserved words.
-These words
-are only recognised if they are unquoted and if they are used as the first
-word of a command (i.e. they can't be preceded by parameter assignments or
-redirections):
-.Bd -literal -offset indent
-case     else     function     then      !       (
-do       esac     if           time      [[      ((
-done     fi       in           until     {
-elif     for      select       while     }
-.Ed
-.Pp
-In the following compound command descriptions, command lists (denoted as
-.Em list )
-that are followed by reserved words must end with a semicolon, a newline, or
-a (syntactically correct) reserved word.
-For example, the following are all valid:
-.Bd -literal -offset indent
-$ { echo foo; echo bar; }
-$ { echo foo; echo bar\*(Ltnewline\*(Gt}
-$ { { echo foo; echo bar; } }
-.Ed
-.Pp
-This is not valid:
-.Pp
-.Dl $ { echo foo; echo bar }
-.Bl -tag -width 4n
-.It Pq Ar list
-Execute
-.Ar list
-in a subshell.
-There is no implicit way to pass environment changes from a
-subshell back to its parent.
-.It { Ar list ; No }
-Compound construct;
-.Ar list
-is executed, but not in a subshell.
-Note that
-.Ql {
-and
-.Ql }
-are reserved words, not meta-characters.
-.It Xo case Ar word No in
-.Oo Op \&(
-.Ar pattern
-.Op \*(Ba Ar pat
-.No ... Ns )
-.Ar list
-.Op ;; \*(Ba ;&\& \*(Ba ;\*(Ba\ \&
-.Oc ... esac
-.Xc
-The
-.Ic case
-statement attempts to match
-.Ar word
-against a specified
-.Ar pattern ;
-the
-.Ar list
-associated with the first successfully matched pattern is executed.
-Patterns used in
-.Ic case
-statements are the same as those used for file name patterns except that the
-restrictions regarding
-.Ql \&.
-and
-.Ql /
-are dropped.
-Note that any unquoted space before and after a pattern is
-stripped; any space within a pattern must be quoted.
-Both the word and the
-patterns are subject to parameter, command, and arithmetic substitution, as
-well as tilde substitution.
-.Pp
-For historical reasons, open and close braces may be used instead of
-.Ic in
-and
-.Ic esac
-e.g.\&
-.Ic case $foo { *) echo bar;; } .
-.Pp
-The list terminators are:
-.Bl -tag -width 4n
-.It Ql ;;
-Terminate after the list.
-.It Ql ;&\&
-Fall through into the next list.
-.It Ql ;\*(Ba\&
-Evaluate the remaining pattern-list tuples.
-.El
-.Pp
-The exit status of a
-.Ic case
-statement is that of the executed
-.Ar list ;
-if no
-.Ar list
-is executed, the exit status is zero.
-.It Xo for Ar name
-.Oo in Ar word No ... Oc ;
-.No do Ar list ; No done
-.Xc
-For each
-.Ar word
-in the specified word list, the parameter
-.Ar name
-is set to the word and
-.Ar list
-is executed.
-If
-.Ic in
-is not used to specify a word list, the positional parameters
-($1, $2, etc.)\&
-are used instead.
-For historical reasons, open and close braces may be used instead of
-.Ic do
-and
-.Ic done
-e.g.\&
-.Ic for i; { echo $i; } .
-The exit status of a
-.Ic for
-statement is the last exit status of
-.Ar list ;
-if
-.Ar list
-is never executed, the exit status is zero.
-.It Xo if Ar list ;
-.No then Ar list ;
-.Oo elif Ar list ;
-.No then Ar list ; Oc
-.No ...
-.Oo else Ar list ; Oc
-.No fi
-.Xc
-If the exit status of the first
-.Ar list
-is zero, the second
-.Ar list
-is executed; otherwise, the
-.Ar list
-following the
-.Ic elif ,
-if any, is executed with similar consequences.
-If all the lists following the
-.Ic if
-and
-.Ic elif Ns s
-fail (i.e. exit with non-zero status), the
-.Ar list
-following the
-.Ic else
-is executed.
-The exit status of an
-.Ic if
-statement is that of non-conditional
-.Ar list
-that is executed; if no non-conditional
-.Ar list
-is executed, the exit status is zero.
-.It Xo select Ar name
-.Oo in Ar word No ... Oc ;
-.No do Ar list ; No done
-.Xc
-The
-.Ic select
-statement provides an automatic method of presenting the user with a menu and
-selecting from it.
-An enumerated list of the specified
-.Ar word Ns (s)
-is printed on standard error, followed by a prompt
-.Po
-.Ev PS3: normally
-.Sq #?\ \&
-.Pc .
-A number corresponding to one of the enumerated words is then read from
-standard input,
-.Ar name
-is set to the selected word (or unset if the selection is not valid),
-.Ev REPLY
-is set to what was read (leading/trailing space is stripped), and
-.Ar list
-is executed.
-If a blank line (i.e. zero or more
-.Ev IFS
-octets) is entered, the menu is reprinted without executing
-.Ar list .
-.Pp
-When
-.Ar list
-completes, the enumerated list is printed if
-.Ev REPLY
-is
-.Dv NULL ,
-the prompt is printed, and so on.
-This process continues until an end-of-file
-is read, an interrupt is received, or a
-.Ic break
-statement is executed inside the loop.
-If
-.Dq in word ...
-is omitted, the positional parameters are used
-(i.e. $1, $2, etc.).
-For historical reasons, open and close braces may be used instead of
-.Ic do
-and
-.Ic done
-e.g.\&
-.Ic select i; { echo $i; } .
-The exit status of a
-.Ic select
-statement is zero if a
-.Ic break
-statement is used to exit the loop, non-zero otherwise.
-.It Xo until Ar list ;
-.No do Ar list ;
-.No done
-.Xc
-This works like
-.Ic while ,
-except that the body is executed only while the exit status of the first
-.Ar list
-is non-zero.
-.It Xo while Ar list ;
-.No do Ar list ;
-.No done
-.Xc
-A
-.Ic while
-is a pre-checked loop.
-Its body is executed as often as the exit status of the first
-.Ar list
-is zero.
-The exit status of a
-.Ic while
-statement is the last exit status of the
-.Ar list
-in the body of the loop; if the body is not executed, the exit status is zero.
-.It Xo function Ar name
-.No { Ar list ; No }
-.Xc
-Defines the function
-.Ar name
-(see
-.Sx Functions
-below).
-Note that redirections specified after a function definition are
-performed whenever the function is executed, not when the function definition
-is executed.
-.It Ar name Ns \&() Ar command
-Mostly the same as
-.Ic function
-(see
-.Sx Functions
-below).
-Whitespace (space or tab) after
-.Ar name
-will be ignored most of the time.
-.It Xo function Ar name Ns \&()
-.No { Ar list ; No }
-.Xc
-The same as
-.Ar name Ns \&()
-.Pq Nm bash Ns ism .
-The
-.Ic function
-keyword is ignored.
-.It Xo Ic time Op Fl p
-.Op Ar pipeline
-.Xc
-The
-.Sx Command execution
-section describes the
-.Ic time
-reserved word.
-.It \&(( Ar expression No ))
-The arithmetic expression
-.Ar expression
-is evaluated; equivalent to
-.Dq let expression
-(see
-.Sx Arithmetic expressions
-and the
-.Ic let
-command, below).
-.It Bq Bq Ar \ \&expression\ \&
-Similar to the
-.Ic test
-and
-.Ic \&[ ... \&]
-commands (described later), with the following exceptions:
-.Bl -bullet
-.It
-Field splitting and file name generation are not performed on arguments.
-.It
-The
-.Fl a
-.Pq AND
-and
-.Fl o
-.Pq OR
-operators are replaced with
-.Ql &&
-and
-.Ql \*(Ba\*(Ba ,
-respectively.
-.It
-Operators (e.g.\&
-.Sq Fl f ,
-.Sq = ,
-.Sq \&! )
-must be unquoted.
-.It
-Parameter, command, and arithmetic substitutions are performed as expressions
-are evaluated and lazy expression evaluation is used for the
-.Ql &&
-and
-.Ql \*(Ba\*(Ba
-operators.
-This means that in the following statement,
-.Ic $(\*(Ltfoo)
-is evaluated if and only if the file
-.Pa foo
-exists and is readable:
-.Bd -literal -offset indent
-$ [[ \-r foo && $(\*(Ltfoo) = b*r ]]
-.Ed
-.It
-The second operand of the
-.Sq !=
-and
-.Sq =
-expressions are patterns (e.g. the comparison
-.Ic \&[[ foobar = f*r ]]
-succeeds).
-This even works indirectly:
-.Bd -literal -offset indent
-$ bar=foobar; baz=\*(aqf*r\*(aq
-$ [[ $bar = $baz ]]; echo $?
-$ [[ $bar = \&"$baz" ]]; echo $?
-.Ed
-.Pp
-Perhaps surprisingly, the first comparison succeeds,
-whereas the second doesn't.
-.El
-.El
-.Ss Quoting
-Quoting is used to prevent the shell from treating characters or words
-specially.
-There are three methods of quoting.
-First,
-.Ql \e
-quotes the following character, unless it is at the end of a line, in which
-case both the
-.Ql \e
-and the newline are stripped.
-Second, a single quote
-.Pq Sq \*(aq
-quotes everything up to the next single quote (this may span lines).
-Third, a double quote
-.Pq Sq \&"
-quotes all characters, except
-.Ql $ ,
-.Ql \`
-and
-.Ql \e ,
-up to the next unquoted double quote.
-.Ql $
-and
-.Ql \`
-inside double quotes have their usual meaning (i.e. parameter, command, or
-arithmetic substitution) except no field splitting is carried out on the
-results of double-quoted substitutions.
-If a
-.Ql \e
-inside a double-quoted string is followed by
-.Ql \e ,
-.Ql $ ,
-.Ql \` ,
-or
-.Ql \&" ,
-it is replaced by the second character; if it is followed by a newline, both
-the
-.Ql \e
-and the newline are stripped; otherwise, both the
-.Ql \e
-and the character following are unchanged.
-.Pp
-If a single-quoted string is preceded by an unquoted
-.Ql $ ,
-C style backslash expansion (see below) is applied (even single quote
-characters inside can be escaped and do not terminate the string then);
-the expanded result is treated as any other single-quoted string.
-If a double-quoted string is preceded by an unquoted
-.Ql $ ,
-the latter is ignored.
-.Ss Backslash expansion
-In places where backslashes are expanded, certain C and
-.At
-.Nm ksh
-or GNU
-.Nm bash
-style escapes are translated.
-These include
-.Ql \ea ,
-.Ql \eb ,
-.Ql \ef ,
-.Ql \en ,
-.Ql \er ,
-.Ql \et ,
-.Ql \eU######## ,
-.Ql \eu#### ,
-and
-.Ql \ev .
-For
-.Ql \eU########
-and
-.Ql \eu#### ,
-.Dq #
-means a hexadecimal digit, of thich there may be none up to four or eight;
-these escapes translate a Unicode codepoint to UTF-8.
-Furthermore,
-.Ql \eE
-and
-.Ql \ee
-expand to the escape character.
-.Pp
-In the
-.Ic print
-builtin mode,
-.Ql \e" ,
-.Ql \e\*(aq ,
-and
-.Ql \e?
-are explicitly excluded;
-octal sequences must have the none up to three octal digits
-.Dq #
-prefixed with the digit zero
-.Pq Ql \e0### ;
-hexadecimal sequences
-.Ql \ex##
-are limited to none up to two hexadecimal digits
-.Dq # ;
-both octal and hexadecimal sequences convert to raw octets;
-.Ql \e# ,
-where # is none of the above, translates to \e# (backslashes are retained).
-.Pp
-Backslash expansion in the C style mode slightly differs: octal sequences
-.Ql \e###
-must have no digit zero prefixing the one up to three octal digits
-.Dq #
-and yield raw octets; hexadecimal sequences
-.Ql \ex#*
-greedily eat up as many hexadecimal digits
-.Dq #
-as they can and terminate with the first non-hexadecimal digit;
-these translate a Unicode codepoint to UTF-8.
-The sequence
-.Ql \ec# ,
-where
-.Dq #
-is any octet, translates to Ctrl-# (which basically means,
-.Ql \ec?
-becomes DEL, everything else is bitwise ANDed with 0x1F).
-Finally,
-.Ql \e# ,
-where # is none of the above, translates to # (has the backslash trimmed),
-even if it is a newline.
-.Ss Aliases
-There are two types of aliases: normal command aliases and tracked aliases.
-Command aliases are normally used as a short hand for a long or often used
-command.
-The shell expands command aliases (i.e. substitutes the alias name
-for its value) when it reads the first word of a command.
-An expanded alias is re-processed to check for more aliases.
-If a command alias ends in a
-space or tab, the following word is also checked for alias expansion.
-The alias expansion process stops when a word that is not an alias is found,
-when a quoted word is found, or when an alias word that is currently being
-expanded is found.
-Aliases are specifically an interactive feature: while they do happen
-to work in scripts and on the command line in some cases, aliases are
-expanded during lexing, so their use must be in a separate command tree
-from their definition; otherwise, the alias will not be found.
-Noticeably, command lists (separated by semicolon, in command substitutions
-also by newline) may be one same parse tree.
-.Pp
-The following command aliases are defined automatically by the shell:
-.Bd -literal -offset indent
-autoload=\*(aqtypeset \-fu\*(aq
-functions=\*(aqtypeset \-f\*(aq
-hash=\*(aqalias \-t\*(aq
-history=\*(aqfc \-l\*(aq
-integer=\*(aqtypeset \-i\*(aq
-local=\*(aqtypeset\*(aq
-login=\*(aqexec login\*(aq
-nameref=\*(aqtypeset \-n\*(aq
-nohup=\*(aqnohup \*(aq
-r=\*(aqfc \-e \-\*(aq
-stop=\*(aqkill \-STOP\*(aq
-suspend=\*(aqkill \-STOP $$\*(aq
-type=\*(aqwhence \-v\*(aq
-.Ed
-.Pp
-Tracked aliases allow the shell to remember where it found a particular
-command.
-The first time the shell does a path search for a command that is
-marked as a tracked alias, it saves the full path of the command.
-The next
-time the command is executed, the shell checks the saved path to see that it
-is still valid, and if so, avoids repeating the path search.
-Tracked aliases can be listed and created using
-.Ic alias \-t .
-Note that changing the
-.Ev PATH
-parameter clears the saved paths for all tracked aliases.
-If the
-.Ic trackall
-option is set (i.e.\&
-.Ic set \-o Ic trackall
-or
-.Ic set \-h ) ,
-the shell tracks all commands.
-This option is set automatically for non-interactive shells.
-For interactive shells, only the following commands are
-automatically tracked:
-.Xr cat 1 ,
-.Xr cc 1 ,
-.Xr chmod 1 ,
-.Xr cp 1 ,
-.Xr date 1 ,
-.Xr ed 1 ,
-.Xr emacs 1 ,
-.Xr grep 1 ,
-.Xr ls 1 ,
-.Xr make 1 ,
-.Xr mv 1 ,
-.Xr pr 1 ,
-.Xr rm 1 ,
-.Xr sed 1 ,
-.Xr sh 1 ,
-.Xr vi 1 ,
-and
-.Xr who 1 .
-.Ss Substitution
-The first step the shell takes in executing a simple-command is to perform
-substitutions on the words of the command.
-There are three kinds of
-substitution: parameter, command, and arithmetic.
-Parameter substitutions,
-which are described in detail in the next section, take the form
-.Pf $ Ns Ar name
-or
-.Pf ${ Ns Ar ... Ns } ;
-command substitutions take the form
-.Pf $( Ns Ar command Ns \&)
-or (deprecated)
-.Pf \` Ns Ar command Ns \`
-or (executed in the current environment)
-.Pf ${\ \& Ar command Ns \&;}
-and strip trailing newlines;
-and arithmetic substitutions take the form
-.Pf $(( Ns Ar expression Ns )) .
-Parsing the current-environment command substitution requires a space,
-tab or newline after the opening brace and that the closing brace be
-recognised as a keyword (i.e. is preceded by a newline or semicolon).
-They are also called funsubs (function substitutions) and behave like
-functions in that
-.Ic local
-and
-.Ic return
-work, and in that
-.Ic exit
-terminates the parent shell.
-.Pp
-Another variant of substitution are the valsubs (value substitutions)
-.Pf ${\*(Ba\& Ns Ar command Ns \&;}
-which are also executed in the current environment, like funsubs, but
-share their I/O with the parent; instead, they evaluate to whatever
-the, initially empty, expression-local variable
-.Ev REPLY
-is set to within the
-.Ar command Ns No s .
-.Pp
-If a substitution appears outside of double quotes, the results of the
-substitution are generally subject to word or field splitting according to
-the current value of the
-.Ev IFS
-parameter.
-The
-.Ev IFS
-parameter specifies a list of octets which are used to break a string up
-into several words; any octets from the set space, tab, and newline that
-appear in the
-.Ev IFS
-octets are called
-.Dq IFS whitespace .
-Sequences of one or more
-.Ev IFS
-whitespace octets, in combination with zero or one
-.Pf non- Ev IFS
-whitespace octets, delimit a field.
-As a special case, leading and trailing
-.Ev IFS
-whitespace and trailing
-.Ev IFS
-non-whitespace are stripped (i.e. no leading or trailing empty field
-is created by it); leading
-.Pf non- Ev IFS
-whitespace does create an empty field.
-.Pp
-Example: If
-.Ev IFS
-is set to
-.Dq \*(Ltspace\*(Gt: ,
-and VAR is set to
-.Dq \*(Ltspace\*(GtA\*(Ltspace\*(Gt:\*(Ltspace\*(Gt\*(Ltspace\*(GtB::D ,
-the substitution for $VAR results in four fields:
-.Sq A ,
-.Sq B ,
-.Sq
-(an empty field),
-and
-.Sq D .
-Note that if the
-.Ev IFS
-parameter is set to the
-.Dv NULL
-string, no field splitting is done; if the parameter is unset, the default
-value of space, tab, and newline is used.
-.Pp
-Also, note that the field splitting applies only to the immediate result of
-the substitution.
-Using the previous example, the substitution for $VAR:E
-results in the fields:
-.Sq A ,
-.Sq B ,
-.Sq ,
-and
-.Sq D:E ,
-not
-.Sq A ,
-.Sq B ,
-.Sq ,
-.Sq D ,
-and
-.Sq E .
-This behavior is POSIX compliant, but incompatible with some other shell
-implementations which do field splitting on the word which contained the
-substitution or use
-.Dv IFS
-as a general whitespace delimiter.
-.Pp
-The results of substitution are, unless otherwise specified, also subject to
-brace expansion and file name expansion (see the relevant sections below).
-.Pp
-A command substitution is replaced by the output generated by the specified
-command which is run in a subshell.
-For
-.Pf $( Ns Ar command Ns \&)
-and
-.Pf ${\ \& Ar command Ns \&;}
-substitutions, normal quoting rules are used when
-.Ar command
-is parsed; however, for the deprecated
-.Pf \` Ns Ar command Ns \`
-form, a
-.Ql \e
-followed by any of
-.Ql $ ,
-.Ql \` ,
-or
-.Ql \e
-is stripped (a
-.Ql \e
-followed by any other character is unchanged).
-As a special case in command substitutions, a command of the form
-.Pf \*(Lt Ar file
-is interpreted to mean substitute the contents of
-.Ar file .
-Note that
-.Ic $(\*(Ltfoo)
-has the same effect as
-.Ic $(cat foo) .
-.Pp
-Note that some shells do not use a recursive parser for command substitutions,
-leading to failure for certain constructs; to be portable, use as workaround
-.Ql x=$(cat) \*(Lt\*(Lt"EOF"
-(or the newline-keeping
-.Ql x=\*(Lt\*(Lt"EOF"
-extension) instead to merely slurp the string.
-.St -p1003.1
-recommends to use case statements of the form
-.Ql "x=$(case $foo in (bar) echo $bar ;; (*) echo $baz ;; esac)"
-instead, which would work but not serve as example for this portability issue.
-.Bd -literal -offset indent
-x=$(case $foo in bar) echo $bar ;; *) echo $baz ;; esac)
-# above fails to parse on old shells; below is the workaround
-x=$(eval $(cat)) \*(Lt\*(Lt"EOF"
-case $foo in bar) echo $bar ;; *) echo $baz ;; esac
-EOF
-.Ed
-.Pp
-Arithmetic substitutions are replaced by the value of the specified expression.
-For example, the command
-.Ic print $((2+3*4))
-displays 14.
-See
-.Sx Arithmetic expressions
-for a description of an expression.
-.Ss Parameters
-Parameters are shell variables; they can be assigned values and their values
-can be accessed using a parameter substitution.
-A parameter name is either one
-of the special single punctuation or digit character parameters described
-below, or a letter followed by zero or more letters or digits
-.Po
-.Ql _
-counts as a letter
-.Pc .
-The latter form can be treated as arrays by appending an array index of the
-form
-.Op Ar expr
-where
-.Ar expr
-is an arithmetic expression.
-Array indices in
-.Nm
-are limited to the range 0 through 4294967295, inclusive.
-That is, they are a 32-bit unsigned integer.
-.Pp
-Parameter substitutions take the form
-.Pf $ Ns Ar name ,
-.Pf ${ Ns Ar name Ns } ,
-or
-.Sm off
-.Pf ${ Ar name Oo Ar expr Oc }
-.Sm on
-where
-.Ar name
-is a parameter name.
-Substitution of all array elements with
-.Pf ${ Ns Ar name Ns \&[*]}
-and
-.Pf ${ Ns Ar name Ns \&[@]}
-works equivalent to $* and $@ for positional parameters.
-If substitution is performed on a parameter
-(or an array parameter element)
-that is not set, a null string is substituted unless the
-.Ic nounset
-option
-.Po
-.Ic set Fl o Ic nounset
-or
-.Ic set Fl u
-.Pc
-is set, in which case an error occurs.
-.Pp
-Parameters can be assigned values in a number of ways.
-First, the shell implicitly sets some parameters like
-.Ql # ,
-.Ql PWD ,
-and
-.Ql $ ;
-this is the only way the special single character parameters are set.
-Second, parameters are imported from the shell's environment at startup.
-Third, parameters can be assigned values on the command line: for example,
-.Ic FOO=bar
-sets the parameter
-.Dq FOO
-to
-.Dq bar ;
-multiple parameter assignments can be given on a single command line and they
-can be followed by a simple-command, in which case the assignments are in
-effect only for the duration of the command (such assignments are also
-exported; see below for the implications of this).
-Note that both the parameter name and the
-.Ql =
-must be unquoted for the shell to recognise a parameter assignment.
-The construct
-.Ic FOO+=baz
-is also recognised; the old and new values are immediately concatenated.
-The fourth way of setting a parameter is with the
-.Ic export ,
-.Ic global ,
-.Ic readonly ,
-and
-.Ic typeset
-commands; see their descriptions in the
-.Sx Command execution
-section.
-Fifth,
-.Ic for
-and
-.Ic select
-loops set parameters as well as the
-.Ic getopts ,
-.Ic read ,
-and
-.Ic set \-A
-commands.
-Lastly, parameters can be assigned values using assignment operators
-inside arithmetic expressions (see
-.Sx Arithmetic expressions
-below) or using the
-.Sm off
-.Pf ${ Ar name No = Ar value No }
-.Sm on
-form of the parameter substitution (see below).
-.Pp
-Parameters with the export attribute (set using the
-.Ic export
-or
-.Ic typeset Fl x
-commands, or by parameter assignments followed by simple commands) are put in
-the environment (see
-.Xr environ 7 )
-of commands run by the shell as
-.Ar name Ns = Ns Ar value
-pairs.
-The order in which parameters appear in the environment of a command is
-unspecified.
-When the shell starts up, it extracts parameters and their values
-from its environment and automatically sets the export attribute for those
-parameters.
-.Pp
-Modifiers can be applied to the
-.Pf ${ Ns Ar name Ns }
-form of parameter substitution:
-.Bl -tag -width Ds
-.Sm off
-.It ${ Ar name No :\- Ar word No }
-.Sm on
-If
-.Ar name
-is set and not
-.Dv NULL ,
-it is substituted; otherwise,
-.Ar word
-is substituted.
-.Sm off
-.It ${ Ar name No :+ Ar word No }
-.Sm on
-If
-.Ar name
-is set and not
-.Dv NULL ,
-.Ar word
-is substituted; otherwise, nothing is substituted.
-.Sm off
-.It ${ Ar name No := Ar word No }
-.Sm on
-If
-.Ar name
-is set and not
-.Dv NULL ,
-it is substituted; otherwise, it is assigned
-.Ar word
-and the resulting value of
-.Ar name
-is substituted.
-.Sm off
-.It ${ Ar name No :? Ar word No }
-.Sm on
-If
-.Ar name
-is set and not
-.Dv NULL ,
-it is substituted; otherwise,
-.Ar word
-is printed on standard error (preceded by
-.Ar name : )
-and an error occurs (normally causing termination of a shell script, function,
-or script sourced using the
-.Sq \&.
-built-in).
-If
-.Ar word
-is omitted, the string
-.Dq parameter null or not set
-is used instead.
-Currently a bug, if
-.Ar word
-is a variable which expands to the null string, the
-error message is also printed.
-.El
-.Pp
-Note that, for all of the above,
-.Ar word
-is actually considered quoted, and special parsing rules apply.
-The parsing rules also differ on whether the expression is double-quoted:
-.Ar word
-then uses double-quoting rules, except for the double quote itself
-.Pq Sq \&"
-and the closing brace, which, if backslash escaped, gets quote removal applied.
-.Pp
-In the above modifiers, the
-.Ql \&:
-can be omitted, in which case the conditions only depend on
-.Ar name
-being set (as opposed to set and not
-.Dv NULL ) .
-If
-.Ar word
-is needed, parameter, command, arithmetic, and tilde substitution are performed
-on it; if
-.Ar word
-is not needed, it is not evaluated.
-.Pp
-The following forms of parameter substitution can also be used (if
-.Ar name
-is an array, its element #0 will be substituted in a scalar context):
-.Pp
-.Bl -tag -width Ds -compact
-.It Pf ${# Ns Ar name Ns \&}
-The number of positional parameters if
-.Ar name
-is
-.Ql * ,
-.Ql @ ,
-or not specified; otherwise the length
-.Pq in characters
-of the string value of parameter
-.Ar name .
-.Pp
-.It Pf ${# Ns Ar name Ns \&[*]}
-.It Pf ${# Ns Ar name Ns \&[@]}
-The number of elements in the array
-.Ar name .
-.Pp
-.It Pf ${% Ns Ar name Ns \&}
-The width
-.Pq in screen columns
-of the string value of parameter
-.Ar name ,
-or \-1 if
-.Pf ${ Ns Ar name Ns }
-contains a control character.
-.Pp
-.It Pf ${! Ns Ar name Ns }
-The name of the variable referred to by
-.Ar name .
-This will be
-.Ar name
-except when
-.Ar name
-is a name reference (bound variable), created by the
-.Ic nameref
-command (which is an alias for
-.Ic typeset Fl n ) .
-.Pp
-.It Pf ${! Ns Ar name Ns \&[*]}
-.It Pf ${! Ns Ar name Ns \&[@]}
-The names of indices (keys) in the array
-.Ar name .
-.Pp
-.Sm off
-.It Xo
-.Pf ${ Ar name
-.Pf # Ar pattern No }
-.Xc
-.It Xo
-.Pf ${ Ar name
-.Pf ## Ar pattern No }
-.Xc
-.Sm on
-If
-.Ar pattern
-matches the beginning of the value of parameter
-.Ar name ,
-the matched text is deleted from the result of substitution.
-A single
-.Ql #
-results in the shortest match, and two
-of them result in the longest match.
-Cannot be applied to a vector
-.Pq ${*} or ${@} or ${array[*]} or ${array[@]} .
-.Pp
-.Sm off
-.It Xo
-.Pf ${ Ar name
-.Pf % Ar pattern No }
-.Xc
-.It Xo
-.Pf ${ Ar name
-.Pf %% Ar pattern No }
-.Xc
-.Sm on
-Like ${..#..} substitution, but it deletes from the end of the value.
-Cannot be applied to a vector.
-.Pp
-.Sm off
-.It Xo
-.Pf ${ Ar name
-.Pf / Ar pattern / Ar string No }
-.Xc
-.It Xo
-.Pf ${ Ar name
-.Pf // Ar pattern / Ar string No }
-.Xc
-.Sm on
-Like ${..#..} substitution, but it replaces the longest match of
-.Ar pattern ,
-anchored anywhere in the value, with
-.Ar string .
-If
-.Ar pattern
-begins with
-.Ql # ,
-it is anchored at the beginning of the value; if it begins with
-.Ql % ,
-it is anchored at the end.
-Patterns that are empty or consist only of wildcards are invalid.
-A single
-.Ql /
-replaces the first occurence of the search
-.Ar pattern ,
-and two of them replace all occurences.
-If
-.Pf / Ar string
-is omitted, the
-.Ar pattern
-is replaced by the empty string, i.e. deleted.
-Cannot be applied to a vector.
-Inefficiently implemented, may be slow.
-.Pp
-.Sm off
-.It Xo
-.Pf ${ Ar name : Ns Ar pos
-.Pf : Ns Ar len Ns }
-.Xc
-.Sm on
-The first
-.Ar len
-characters of
-.Ar name ,
-starting at position
-.Ar pos ,
-are substituted.
-Both
-.Ar pos
-and
-.Pf : Ns Ar len
-are optional.
-If
-.Ar pos
-is negative, counting starts at the end of the string; if it
-is omitted, it defaults to 0.
-If
-.Ar len
-is omitted or greater than the length of the remaining string,
-all of it is substituted.
-Both
-.Ar pos
-and
-.Ar len
-are evaluated as arithmetic expressions.
-Currently,
-.Ar pos
-must start with a space, opening parenthesis or digit to be recognised.
-Cannot be applied to a vector.
-.Pp
-.It Xo
-.Pf ${ Ar name
-.Pf @# Ns Oo Ar seed Oc Ns }
-.Xc
-The internal hash of the expansion of
-.Ar name ,
-with an optional (defaulting to zero)
-.Op Ar seed .
-At the moment, this is NZAAT (a 32-bit hash based on
-Bob Jenkins' one-at-a-time hash), but this is not set.
-This is the hash the shell uses internally for its associative arrays.
-.Pp
-.It Pf ${ Ns Ar name Ns @Q}
-A quoted expression safe for re-entry, whose value is the value of the
-.Ar name
-parameter, is substituted.
-.El
-.Pp
-Note that
-.Ar pattern
-may need extended globbing pattern
-.Pq @(...) ,
-single
-.Pq \&\*(aq...\&\*(aq
-or double
-.Pq \&"...\&"
-quote escaping unless
-.Fl o Ic sh
-is set.
-.Pp
-The following special parameters are implicitly set by the shell and cannot be
-set directly using assignments:
-.Bl -tag -width "1 .. 9"
-.It Ev \&!
-Process ID of the last background process started.
-If no background processes have been started, the parameter is not set.
-.It Ev \&#
-The number of positional parameters ($1, $2, etc.).
-.It Ev \&$
-The PID of the shell, or the PID of the original shell if it is a subshell.
-Do
-.Em NOT
-use this mechanism for generating temporary file names; see
-.Xr mktemp 1
-instead.
-.It Ev \-
-The concatenation of the current single letter options (see the
-.Ic set
-command below for a list of options).
-.It Ev \&?
-The exit status of the last non-asynchronous command executed.
-If the last command was killed by a signal,
-.Ic $?\&
-is set to 128 plus the signal number.
-.It Ev 0
-The name of the shell, determined as follows:
-the first argument to
-.Nm
-if it was invoked with the
-.Fl c
-option and arguments were given; otherwise the
-.Ar file
-argument, if it was supplied;
-or else the basename the shell was invoked with (i.e.\&
-.Li argv[0] ) .
-.Ev $0
-is also set to the name of the current script or
-the name of the current function, if it was defined with the
-.Ic function
-keyword (i.e. a Korn shell style function).
-.It Ev 1 No .. Ev 9
-The first nine positional parameters that were supplied to the shell, function,
-or script sourced using the
-.Sq \&.
-built-in.
-Further positional parameters may be accessed using
-.Pf ${ Ar number Ns } .
-.It Ev *
-All positional parameters (except 0), i.e. $1, $2, $3, ...
-.br
-If used
-outside of double quotes, parameters are separate words (which are subjected
-to word splitting); if used within double quotes, parameters are separated
-by the first character of the
-.Ev IFS
-parameter (or the empty string if
-.Ev IFS
-is
-.Dv NULL ) .
-.It Ev @
-Same as
-.Ic $* ,
-unless it is used inside double quotes, in which case a separate word is
-generated for each positional parameter.
-If there are no positional parameters, no word is generated.
-.Ic $@
-can be used to access arguments, verbatim, without losing
-.Dv NULL
-arguments or splitting arguments with spaces.
-.El
-.Pp
-The following parameters are set and/or used by the shell:
-.Bl -tag -width "KSH_VERSION"
-.It Ev _
-.Pq underscore
-When an external command is executed by the shell, this parameter is set in the
-environment of the new process to the path of the executed command.
-In interactive use, this parameter is also set in the parent shell to the last
-word of the previous command.
-.It Ev BASHPID
-The PID of the shell or subshell.
-.It Ev CDPATH
-Search path for the
-.Ic cd
-built-in command.
-It works the same way as
-.Ev PATH
-for those directories not beginning with
-.Ql /
-in
-.Ic cd
-commands.
-Note that if
-.Ev CDPATH
-is set and does not contain
-.Sq \&.
-or contains an empty path, the current directory is not searched.
-Also, the
-.Ic cd
-built-in command will display the resulting directory when a match is found
-in any search path other than the empty path.
-.It Ev COLUMNS
-Set to the number of columns on the terminal or window.
-Always set, defaults to 80, unless the
-value as reported by
-.Xr stty 1
-is non-zero and sane enough (minimum is 12x3); similar for
-.Ev LINES .
-This parameter is used by the interactive line editing modes, and by the
-.Ic select ,
-.Ic set \-o ,
-and
-.Ic kill \-l
-commands to format information columns.
-Importing from the environment or unsetting this parameter removes the
-binding to the actual terminal size in favour of the provided value.
-.It Ev ENV
-If this parameter is found to be set after any profile files are executed, the
-expanded value is used as a shell startup file.
-It typically contains function and alias definitions.
-.It Ev ERRNO
-Integer value of the shell's
-.Va errno
-variable.
-It indicates the reason the last system call failed.
-Not yet implemented.
-.It Ev EXECSHELL
-If set, this parameter is assumed to contain the shell that is to be used to
-execute commands that
-.Xr execve 2
-fails to execute and which do not start with a
-.Dq #! Ns Ar shell
-sequence.
-.It Ev FCEDIT
-The editor used by the
-.Ic fc
-command (see below).
-.It Ev FPATH
-Like
-.Ev PATH ,
-but used when an undefined function is executed to locate the file defining the
-function.
-It is also searched when a command can't be found using
-.Ev PATH .
-See
-.Sx Functions
-below for more information.
-.It Ev HISTFILE
-The name of the file used to store command history.
-When assigned to, history is loaded from the specified file.
-Also, several invocations of the shell will share history if their
-.Ev HISTFILE
-parameters all point to the same file.
-.Pp
-.Sy Note :
-If
-.Ev HISTFILE
-isn't set, no history file is used.
-This is different from
-.At
-.Nm ksh .
-.It Ev HISTSIZE
-The number of commands normally stored for history.
-The default is 2047.
-.It Ev HOME
-The default directory for the
-.Ic cd
-command and the value substituted for an unqualified
-.Ic \*(TI
-(see
-.Sx Tilde expansion
-below).
-.It Ev IFS
-Internal field separator, used during substitution and by the
-.Ic read
-command, to split values into distinct arguments; normally set to space, tab,
-and newline.
-See
-.Sx Substitution
-above for details.
-.Pp
-.Sy Note :
-This parameter is not imported from the environment when the shell is
-started.
-.It Ev KSHEGID
-The effective group id of the shell.
-.It Ev KSHGID
-The real group id of the shell.
-.It Ev KSHUID
-The real user id of the shell.
-.It Ev KSH_VERSION
-The name and version of the shell (read-only).
-See also the version commands in
-.Sx Emacs editing mode
-and
-.Sx Vi editing mode
-sections, below.
-.It Ev LINENO
-The line number of the function or shell script that is currently being
-executed.
-.It Ev LINES
-Set to the number of lines on the terminal or window.
-Always set, defaults to 24.
-See
-.Ev COLUMNS .
-.It Ev EPOCHREALTIME
-Time since the epoch, as returned by
-.Xr gettimeofday 2 ,
-formatted as decimal
-.Va tv_sec
-followed by a dot
-.Pq Sq .\&
-and
-.Va tv_usec
-padded to exactly six decimal digits.
-.It Ev OLDPWD
-The previous working directory.
-Unset if
-.Ic cd
-has not successfully changed directories since the shell started, or if the
-shell doesn't know where it is.
-.It Ev OPTARG
-When using
-.Ic getopts ,
-it contains the argument for a parsed option, if it requires one.
-.It Ev OPTIND
-The index of the next argument to be processed when using
-.Ic getopts .
-Assigning 1 to this parameter causes
-.Ic getopts
-to process arguments from the beginning the next time it is invoked.
-.It Ev PATH
-A colon separated list of directories that are searched when looking for
-commands and files sourced using the
-.Sq \&.
-command (see below).
-An empty string resulting from a leading or trailing
-colon, or two adjacent colons, is treated as a
-.Sq \&.
-(the current directory).
-.It Ev PGRP
-The process ID of the shell's process group leader.
-.It Ev PIPESTATUS
-An array containing the errorlevel (exit status) codes,
-one by one, of the last pipeline run in the foreground.
-.It Ev PPID
-The process ID of the shell's parent.
-.It Ev PS1
-The primary prompt for interactive shells.
-Parameter, command, and arithmetic
-substitutions are performed, and
-.Ql \&!
-is replaced with the current command number (see the
-.Ic fc
-command below).
-A literal
-.Ql \&!
-can be put in the prompt by placing
-.Ql !!
-in
-.Ev PS1 .
-.Pp
-The default prompt is
-.Sq $\ \&
-for non-root users,
-.Sq #\ \&
-for root.
-If
-.Nm
-is invoked by root and
-.Ev PS1
-does not contain a
-.Sq #
-character, the default value will be used even if
-.Ev PS1
-already exists in the environment.
-.Pp
-The
-.Nm
-distribution comes with a sample
-.Pa dot.mkshrc
-containing a sophisticated example, but you might like the following one
-(note that ${HOSTNAME:=$(hostname)} and the
-root-vs-user distinguishing clause are (in this example) executed at
-.Ev PS1
-assignment time, while the $USER and $PWD are escaped
-and thus will be evaluated each time a prompt is displayed):
-.Bd -literal
-PS1=\*(aq${USER:=$(id \-un)}\*(aq"@${HOSTNAME:=$(hostname)}:\e$PWD $(
-	if (( USER_ID )); then print \e$; else print \e#; fi) "
-.Ed
-.Pp
-Note that since the command-line editors try to figure out how long the prompt
-is (so they know how far it is to the edge of the screen), escape codes in
-the prompt tend to mess things up.
-You can tell the shell not to count certain
-sequences (such as escape codes) by prefixing your prompt with a
-character (such as Ctrl-A) followed by a carriage return and then delimiting
-the escape codes with this character.
-Any occurences of that character in the prompt are not printed.
-By the way, don't blame me for
-this hack; it's derived from the original
-.Xr ksh88 1 ,
-which did print the delimiter character so you were out of luck
-if you did not have any non-printing characters.
-.Pp
-Since Backslashes and other special characters may be
-interpreted by the shell, to set
-.Ev PS1
-either escape the backslash itself,
-or use double quotes.
-The latter is more practical.
-This is a more complex example,
-avoiding to directly enter special characters (for example with
-.Ic \*(haV
-in the emacs editing mode),
-which embeds the current working directory,
-in reverse video
-.Pq colour would work, too ,
-in the prompt string:
-.Bd -literal -offset indent
-x=$(print \e\e001)
-PS1="$x$(print \e\er)$x$(tput smso)$x\e$PWD$x$(tput rmso)$x\*(Gt "
-.Ed
-.Pp
-Due to a strong suggestion from David G. Korn,
-.Nm
-now also supports the following form:
-.Bd -literal -offset indent
-PS1=$'\e1\er\e1\ee[7m\e1$PWD\e1\ee[0m\e1\*(Gt '
-.Ed
-.It Ev PS2
-Secondary prompt string, by default
-.Sq \*(Gt\ \& ,
-used when more input is needed to complete a command.
-.It Ev PS3
-Prompt used by the
-.Ic select
-statement when reading a menu selection.
-The default is
-.Sq #?\ \& .
-.It Ev PS4
-Used to prefix commands that are printed during execution tracing (see the
-.Ic set Fl x
-command below).
-Parameter, command, and arithmetic substitutions are performed
-before it is printed.
-The default is
-.Sq +\ \& .
-You may want to set it to
-.Sq \&[$EPOCHREALTIME]\ \&
-instead, to include timestamps.
-.It Ev PWD
-The current working directory.
-May be unset or
-.Dv NULL
-if the shell doesn't know where it is.
-.It Ev RANDOM
-Each time
-.Ev RANDOM
-is referenced, it is assigned a number between 0 and 32767 from
-a Linear Congruential PRNG first.
-.It Ev REPLY
-Default parameter for the
-.Ic read
-command if no names are given.
-Also used in
-.Ic select
-loops to store the value that is read from standard input.
-.It Ev SECONDS
-The number of seconds since the shell started or, if the parameter has been
-assigned an integer value, the number of seconds since the assignment plus the
-value that was assigned.
-.It Ev TMOUT
-If set to a positive integer in an interactive shell, it specifies the maximum
-number of seconds the shell will wait for input after printing the primary
-prompt
-.Pq Ev PS1 .
-If the time is exceeded, the shell exits.
-.It Ev TMPDIR
-The directory temporary shell files are created in.
-If this parameter is not
-set, or does not contain the absolute path of a writable directory, temporary
-files are created in
-.Pa /tmp .
-.It Ev USER_ID
-The effective user id of the shell.
-.El
-.Ss Tilde expansion
-Tilde expansion which is done in parallel with parameter substitution, is done
-on words starting with an unquoted
-.Ql \*(TI .
-The characters following the tilde, up to the first
-.Ql / ,
-if any, are assumed to be a login name.
-If the login name is empty,
-.Ql + ,
-or
-.Ql \- ,
-the value of the
-.Ev HOME ,
-.Ev PWD ,
-or
-.Ev OLDPWD
-parameter is substituted, respectively.
-Otherwise, the password file is
-searched for the login name, and the tilde expression is substituted with the
-user's home directory.
-If the login name is not found in the password file or
-if any quoting or parameter substitution occurs in the login name, no
-substitution is performed.
-.Pp
-In parameter assignments
-(such as those preceding a simple-command or those occurring
-in the arguments of
-.Ic alias ,
-.Ic export ,
-.Ic global ,
-.Ic readonly ,
-and
-.Ic typeset ) ,
-tilde expansion is done after any assignment
-(i.e. after the equals sign)
-or after an unquoted colon
-.Pq Sq \&: ;
-login names are also delimited by colons.
-.Pp
-The home directory of previously expanded login names are cached and re-used.
-The
-.Ic alias \-d
-command may be used to list, change, and add to this cache (e.g.\&
-.Ic alias \-d fac=/usr/local/facilities; cd \*(TIfac/bin ) .
-.Ss Brace expansion (alteration)
-Brace expressions take the following form:
-.Bd -unfilled -offset indent
-.Sm off
-.Xo
-.Ar prefix No { Ar str1 No ,...,
-.Ar strN No } Ar suffix
-.Xc
-.Sm on
-.Ed
-.Pp
-The expressions are expanded to
-.Ar N
-words, each of which is the concatenation of
-.Ar prefix ,
-.Ar str Ns i ,
-and
-.Ar suffix
-(e.g.\&
-.Dq a{c,b{X,Y},d}e
-expands to four words:
-.Dq ace ,
-.Dq abXe ,
-.Dq abYe ,
-and
-.Dq ade ) .
-As noted in the example, brace expressions can be nested and the resulting
-words are not sorted.
-Brace expressions must contain an unquoted comma
-.Pq Sq \&,
-for expansion to occur (e.g.\&
-.Ic {}
-and
-.Ic {foo}
-are not expanded).
-Brace expansion is carried out after parameter substitution
-and before file name generation.
-.Ss File name patterns
-A file name pattern is a word containing one or more unquoted
-.Ql \&? ,
-.Ql * ,
-.Ql + ,
-.Ql @ ,
-or
-.Ql \&!
-characters or
-.Dq \&[..]
-sequences.
-Once brace expansion has been performed, the shell replaces file
-name patterns with the sorted names of all the files that match the pattern
-(if no files match, the word is left unchanged).
-The pattern elements have the following meaning:
-.Bl -tag -width Ds
-.It \&?
-Matches any single character.
-.It \&*
-Matches any sequence of octets.
-.It \&[..]
-Matches any of the octets inside the brackets.
-Ranges of octets can be specified by separating two octets by a
-.Ql \-
-(e.g.\&
-.Dq \&[a0\-9]
-matches the letter
-.Sq a
-or any digit).
-In order to represent itself, a
-.Ql \-
-must either be quoted or the first or last octet in the octet list.
-Similarly, a
-.Ql \&]
-must be quoted or the first octet in the list if it is to represent itself
-instead of the end of the list.
-Also, a
-.Ql \&!
-appearing at the start of the list has special meaning (see below), so to
-represent itself it must be quoted or appear later in the list.
-.It \&[!..]
-Like [..],
-except it matches any octet not inside the brackets.
-.Sm off
-.It *( Ar pattern\*(Ba No ...\*(Ba Ar pattern )
-.Sm on
-Matches any string of octets that matches zero or more occurrences of the
-specified patterns.
-Example: The pattern
-.Ic *(foo\*(Babar)
-matches the strings
-.Dq ,
-.Dq foo ,
-.Dq bar ,
-.Dq foobarfoo ,
-etc.
-.Sm off
-.It +( Ar pattern\*(Ba No ...\*(Ba Ar pattern )
-.Sm on
-Matches any string of octets that matches one or more occurrences of the
-specified patterns.
-Example: The pattern
-.Ic +(foo\*(Babar)
-matches the strings
-.Dq foo ,
-.Dq bar ,
-.Dq foobar ,
-etc.
-.Sm off
-.It ?( Ar pattern\*(Ba No ...\*(Ba Ar pattern )
-.Sm on
-Matches the empty string or a string that matches one of the specified
-patterns.
-Example: The pattern
-.Ic ?(foo\*(Babar)
-only matches the strings
-.Dq ,
-.Dq foo ,
-and
-.Dq bar .
-.Sm off
-.It @( Ar pattern\*(Ba No ...\*(Ba Ar pattern )
-.Sm on
-Matches a string that matches one of the specified patterns.
-Example: The pattern
-.Ic @(foo\*(Babar)
-only matches the strings
-.Dq foo
-and
-.Dq bar .
-.Sm off
-.It !( Ar pattern\*(Ba No ...\*(Ba Ar pattern )
-.Sm on
-Matches any string that does not match one of the specified patterns.
-Examples: The pattern
-.Ic !(foo\*(Babar)
-matches all strings except
-.Dq foo
-and
-.Dq bar ;
-the pattern
-.Ic !(*)
-matches no strings; the pattern
-.Ic !(?)*\&
-matches all strings (think about it).
-.El
-.Pp
-Note that complicated globbing, especially with alternatives,
-is slow; using separate comparisons may (or may not) be faster.
-.Pp
-Note that
-.Nm mksh
-.Po and Nm pdksh Pc
-never matches
-.Sq \&.
-and
-.Sq .. ,
-but
-.At
-.Nm ksh ,
-Bourne
-.Nm sh ,
-and GNU
-.Nm bash
-do.
-.Pp
-Note that none of the above pattern elements match either a period
-.Pq Sq \&.
-at the start of a file name or a slash
-.Pq Sq / ,
-even if they are explicitly used in a [..] sequence; also, the names
-.Sq \&.
-and
-.Sq ..
-are never matched, even by the pattern
-.Sq .* .
-.Pp
-If the
-.Ic markdirs
-option is set, any directories that result from file name generation are marked
-with a trailing
-.Ql / .
-.Ss Input/output redirection
-When a command is executed, its standard input, standard output, and standard
-error (file descriptors 0, 1, and 2, respectively) are normally inherited from
-the shell.
-Three exceptions to this are commands in pipelines, for which
-standard input and/or standard output are those set up by the pipeline,
-asynchronous commands created when job control is disabled, for which standard
-input is initially set to be from
-.Pa /dev/null ,
-and commands for which any of the following redirections have been specified:
-.Bl -tag -width XXxxmarker
-.It \*(Gt Ar file
-Standard output is redirected to
-.Ar file .
-If
-.Ar file
-does not exist, it is created; if it does exist, is a regular file, and the
-.Ic noclobber
-option is set, an error occurs; otherwise, the file is truncated.
-Note that this means the command
-.Ic cmd \*(Ltfoo \*(Gtfoo
-will open
-.Ar foo
-for reading and then truncate it when it opens it for writing, before
-.Ar cmd
-gets a chance to actually read
-.Ar foo .
-.It \*(Gt\*(Ba Ar file
-Same as
-.Ic \*(Gt ,
-except the file is truncated, even if the
-.Ic noclobber
-option is set.
-.It \*(Gt\*(Gt Ar file
-Same as
-.Ic \*(Gt ,
-except if
-.Ar file
-exists it is appended to instead of being truncated.
-Also, the file is opened
-in append mode, so writes always go to the end of the file (see
-.Xr open 2 ) .
-.It \*(Lt Ar file
-Standard input is redirected from
-.Ar file ,
-which is opened for reading.
-.It \*(Lt\*(Gt Ar file
-Same as
-.Ic \*(Lt ,
-except the file is opened for reading and writing.
-.It \*(Lt\*(Lt Ar marker
-After reading the command line containing this kind of redirection (called a
-.Dq here document ) ,
-the shell copies lines from the command source into a temporary file until a
-line matching
-.Ar marker
-is read.
-When the command is executed, standard input is redirected from the
-temporary file.
-If
-.Ar marker
-contains no quoted characters, the contents of the temporary file are processed
-as if enclosed in double quotes each time the command is executed, so
-parameter, command, and arithmetic substitutions are performed, along with
-backslash
-.Pq Sq \e
-escapes for
-.Ql $ ,
-.Ql \` ,
-.Ql \e ,
-and
-.Ql \enewline ,
-but not for
-.Ql \&" .
-If multiple here documents are used on the same command line, they are saved in
-order.
-.Pp
-If no
-.Ar marker
-is given, the here document ends at the next
-.Ic \*(Lt\*(Lt
-and substitution will be performed.
-If
-.Ar marker
-is only a set of either single
-.Dq \*(aq\*(aq
-or double
-.Sq \&""
-quotes with nothing in between, the here document ends at the next empty line
-and substitution will not be performed.
-.It \*(Lt\*(Lt\- Ar marker
-Same as
-.Ic \*(Lt\*(Lt ,
-except leading tabs are stripped from lines in the here document.
-.It \*(Lt\*(Lt\*(Lt Ar word
-Same as
-.Ic \*(Lt\*(Lt ,
-except that
-.Ar word
-.Em is
-the here document.
-This is called a here string.
-.It \*(Lt& Ar fd
-Standard input is duplicated from file descriptor
-.Ar fd .
-.Ar fd
-can be a number, indicating the number of an existing file descriptor;
-the letter
-.Ql p ,
-indicating the file descriptor associated with the output of the current
-co-process; or the character
-.Ql \- ,
-indicating standard input is to be closed.
-Note that
-.Ar fd
-is limited to a single digit in most shell implementations.
-.It \*(Gt& Ar fd
-Same as
-.Ic \*(Lt& ,
-except the operation is done on standard output.
-.It &\*(Gt Ar file
-Same as
-.Ic \*(Gt Ar file 2\*(Gt&1 .
-This is a GNU
-.Nm bash
-extension supported by
-.Nm
-which also supports the preceding explicit fd number, for example,
-.Ic 3&\*(Gt Ar file
-is the same as
-.Ic 3\*(Gt Ar file 2\*(Gt&3
-in
-.Nm
-but a syntax error in GNU
-.Nm bash .
-Setting the
-.Fl o Ar posix
-or
-.Fl o Ar sh
-shell options disable parsing of this redirection;
-it's a compatibility feature to legacy scripts, to
-not be used when writing new shell code.
-.It Xo
-.No &\*(Gt\*(Ba Ar file ,
-.No &\*(Gt\*(Gt Ar file ,
-.No &\*(Gt& Ar fd
-.Xc
-Same as
-.Ic \*(Gt\*(Ba Ar file ,
-.Ic \*(Gt\*(Gt Ar file ,
-or
-.Ic \*(Gt& Ar fd ,
-followed by
-.Ic 2\*(Gt&1 ,
-as above.
-These are
-.Nm
-extensions.
-.El
-.Pp
-In any of the above redirections, the file descriptor that is redirected
-(i.e. standard input or standard output)
-can be explicitly given by preceding the
-redirection with a number (portably, only a single digit).
-Parameter, command, and arithmetic
-substitutions, tilde substitutions, and (if the shell is interactive)
-file name generation are all performed on the
-.Ar file ,
-.Ar marker ,
-and
-.Ar fd
-arguments of redirections.
-Note, however, that the results of any file name
-generation are only used if a single file is matched; if multiple files match,
-the word with the expanded file name generation characters is used.
-Note
-that in restricted shells, redirections which can create files cannot be used.
-.Pp
-For simple-commands, redirections may appear anywhere in the command; for
-compound-commands
-.Po
-.Ic if
-statements, etc.
-.Pc ,
-any redirections must appear at the end.
-Redirections are processed after
-pipelines are created and in the order they are given, so the following
-will print an error with a line number prepended to it:
-.Pp
-.D1 $ cat /foo/bar 2\*(Gt&1 \*(Gt/dev/null \*(Ba pr \-n \-t
-.Pp
-File descriptors created by input/output redirections are private to the
-Korn shell, but passed to sub-processes if
-.Fl o Ic posix
-or
-.Fl o Ic sh
-is set.
-.Ss Arithmetic expressions
-Integer arithmetic expressions can be used with the
-.Ic let
-command, inside $((..)) expressions, inside array references (e.g.\&
-.Ar name Ns Bq Ar expr ) ,
-as numeric arguments to the
-.Ic test
-command, and as the value of an assignment to an integer parameter.
-.Pp
-Expressions are calculated using signed arithmetic and the
-.Vt mksh_ari_t
-type (a 32-bit signed integer), unless they begin with a sole
-.Sq #
-character, in which case they use
-.Vt mksh_uari_t
-.Po a 32-bit unsigned integer Pc .
-.Pp
-Expressions may contain alpha-numeric parameter identifiers, array references,
-and integer constants and may be combined with the following C operators
-(listed and grouped in increasing order of precedence):
-.Pp
-Unary operators:
-.Bd -literal -offset indent
-+ \- ! \*(TI ++ \-\-
-.Ed
-.Pp
-Binary operators:
-.Bd -literal -offset indent
-,
-= += \-= *= /= %= \*(Lt\*(Lt\*(Lt= \*(Gt\*(Gt\*(Gt= \*(Lt\*(Lt= \*(Gt\*(Gt= &= \*(ha= \*(Ba=
-\*(Ba\*(Ba
-&&
-\*(Ba
-\*(ha
-&
-== !=
-\*(Lt \*(Lt= \*(Gt \*(Gt=
-\*(Lt\*(Lt\*(Lt \*(Gt\*(Gt\*(Gt \*(Lt\*(Lt \*(Gt\*(Gt
-+ \-
-* / %
-.Ed
-.Pp
-Ternary operators:
-.Bd -literal -offset indent
-?: (precedence is immediately higher than assignment)
-.Ed
-.Pp
-Grouping operators:
-.Bd -literal -offset indent
-( )
-.Ed
-.Pp
-Integer constants and expressions are calculated using an exactly 32-bit
-wide, signed or unsigned, type with silent wraparound on integer overflow.
-Integer constants may be specified with arbitrary bases using the notation
-.Ar base Ns # Ns Ar number ,
-where
-.Ar base
-is a decimal integer specifying the base, and
-.Ar number
-is a number in the specified base.
-Additionally, base-16 integers may be specified by prefixing them with
-.Sq 0x
-.Pq case-insensitive
-in all forms of arithmetic expressions, except as numeric arguments to the
-.Ic test
-built-in command.
-Prefixing numbers with a sole digit zero
-.Pq Sq 0
-leads to the shell interpreting it as base-8 integer in
-.Ic posix
-mode
-.Em only ;
-historically, (pd)ksh has never done so either anyway,
-and it's unsafe to do that, but POSIX demands it nowadays.
-As a special
-.Nm mksh
-extension, numbers to the base of one are treated as either (8-bit
-transparent) ASCII or Unicode codepoints, depending on the shell's
-.Ic utf8\-mode
-flag (current setting).
-The
-.At
-.Nm ksh93
-syntax of
-.Dq \*(aqx\*(aq
-instead of
-.Dq 1#x
-is also supported.
-Note that NUL bytes (integral value of zero) cannot be used.
-An unset or empty parameter evaluates to 0 in integer context.
-In Unicode mode, raw octets are mapped into the range EF80..EFFF as in
-OPTU-8, which is in the PUA and has been assigned by CSUR for this use.
-If more than one octet in ASCII mode, or a sequence of more than one
-octet not forming a valid and minimal CESU-8 sequence is passed, the
-behaviour is undefined (usually, the shell aborts with a parse error,
-but rarely, it succeeds, e.g. on the sequence C2 20).
-That's why you should always use ASCII mode unless you know that the
-input is well-formed UTF-8 in the range of 0000..FFFD.
-.Pp
-The operators are evaluated as follows:
-.Bl -tag -width Ds -offset indent
-.It unary +
-Result is the argument (included for completeness).
-.It unary \-
-Negation.
-.It \&!
-Logical NOT;
-the result is 1 if argument is zero, 0 if not.
-.It \*(TI
-Arithmetic (bit-wise) NOT.
-.It ++
-Increment; must be applied to a parameter (not a literal or other expression).
-The parameter is incremented by 1.
-When used as a prefix operator, the result
-is the incremented value of the parameter; when used as a postfix operator, the
-result is the original value of the parameter.
-.It \-\-
-Similar to
-.Ic ++ ,
-except the parameter is decremented by 1.
-.It \&,
-Separates two arithmetic expressions; the left-hand side is evaluated first,
-then the right.
-The result is the value of the expression on the right-hand side.
-.It =
-Assignment; the variable on the left is set to the value on the right.
-.It Xo
-.No += \-= *= /= %= \*(Lt\*(Lt\*(Lt= \*(Gt\*(Gt\*(Gt=
-.No \*(Lt\*(Lt= \*(Gt\*(Gt= &= \*(ha= \*(Ba=
-.Xc
-Assignment operators.
-.Sm off
-.Ao Ar var Ac Xo
-.Aq Ar op
-.No = Aq Ar expr
-.Xc
-.Sm on
-is the same as
-.Sm off
-.Ao Ar var Ac Xo
-.No = Aq Ar var
-.Aq Ar op
-.Aq Ar expr ,
-.Xc
-.Sm on
-with any operator precedence in
-.Aq Ar expr
-preserved.
-For example,
-.Dq var1 *= 5 + 3
-is the same as specifying
-.Dq var1 = var1 * (5 + 3) .
-.It \*(Ba\*(Ba
-Logical OR;
-the result is 1 if either argument is non-zero, 0 if not.
-The right argument is evaluated only if the left argument is zero.
-.It &&
-Logical AND;
-the result is 1 if both arguments are non-zero, 0 if not.
-The right argument is evaluated only if the left argument is non-zero.
-.It \*(Ba
-Arithmetic (bit-wise) OR.
-.It \*(ha
-Arithmetic (bit-wise) XOR
-(exclusive-OR).
-.It &
-Arithmetic (bit-wise) AND.
-.It ==
-Equal; the result is 1 if both arguments are equal, 0 if not.
-.It !=
-Not equal; the result is 0 if both arguments are equal, 1 if not.
-.It \*(Lt
-Less than; the result is 1 if the left argument is less than the right, 0 if
-not.
-.It \*(Lt= \*(Gt \*(Gt=
-Less than or equal, greater than or equal, greater than.
-See
-.Ic \*(Lt .
-.It \*(Lt\*(Lt\*(Lt \*(Gt\*(Gt\*(Gt
-Rotate left (right); the result is similar to shift (see
-.Ic \*(Lt\*(Lt )
-except that the bits shifted out at one end are shifted in
-at the other end, instead of zero or sign bits.
-.It \*(Lt\*(Lt \*(Gt\*(Gt
-Shift left (right); the result is the left argument with its bits shifted left
-(right) by the amount given in the right argument.
-.It + \- * /
-Addition, subtraction, multiplication, and division.
-.It %
-Remainder; the result is the remainder of the division of the left argument by
-the right.
-.It Xo
-.Sm off
-.Aq Ar arg1 ?
-.Aq Ar arg2 :
-.Aq Ar arg3
-.Sm on
-.Xc
-If
-.Aq Ar arg1
-is non-zero, the result is
-.Aq Ar arg2 ;
-otherwise the result is
-.Aq Ar arg3 .
-The non-result argument is not evaluated.
-.El
-.Ss Co-processes
-A co-process (which is a pipeline created with the
-.Sq \*(Ba&
-operator) is an asynchronous process that the shell can both write to (using
-.Ic print \-p )
-and read from (using
-.Ic read \-p ) .
-The input and output of the co-process can also be manipulated using
-.Ic \*(Gt&p
-and
-.Ic \*(Lt&p
-redirections, respectively.
-Once a co-process has been started, another can't
-be started until the co-process exits, or until the co-process's input has been
-redirected using an
-.Ic exec Ar n Ns Ic \*(Gt&p
-redirection.
-If a co-process's input is redirected in this way, the next
-co-process to be started will share the output with the first co-process,
-unless the output of the initial co-process has been redirected using an
-.Ic exec Ar n Ns Ic \*(Lt&p
-redirection.
-.Pp
-Some notes concerning co-processes:
-.Bl -bullet
-.It
-The only way to close the co-process's input (so the co-process reads an
-end-of-file) is to redirect the input to a numbered file descriptor and then
-close that file descriptor:
-.Ic exec 3\*(Gt&p; exec 3\*(Gt&\-
-.It
-In order for co-processes to share a common output, the shell must keep the
-write portion of the output pipe open.
-This means that end-of-file will not be
-detected until all co-processes sharing the co-process's output have exited
-(when they all exit, the shell closes its copy of the pipe).
-This can be
-avoided by redirecting the output to a numbered file descriptor (as this also
-causes the shell to close its copy).
-Note that this behaviour is slightly
-different from the original Korn shell which closes its copy of the write
-portion of the co-process output when the most recently started co-process
-(instead of when all sharing co-processes) exits.
-.It
-.Ic print \-p
-will ignore
-.Dv SIGPIPE
-signals during writes if the signal is not being trapped or ignored; the same
-is true if the co-process input has been duplicated to another file descriptor
-and
-.Ic print \-u Ns Ar n
-is used.
-.El
-.Ss Functions
-Functions are defined using either Korn shell
-.Ic function Ar function-name
-syntax or the Bourne/POSIX shell
-.Ar function-name Ns \&()
-syntax (see below for the difference between the two forms).
-Functions are like
-.Li .\(hyscripts
-(i.e. scripts sourced using the
-.Sq \&.
-built-in)
-in that they are executed in the current environment.
-However, unlike
-.Li .\(hyscripts ,
-shell arguments (i.e. positional parameters $1, $2, etc.)\&
-are never visible inside them.
-When the shell is determining the location of a command, functions
-are searched after special built-in commands, before builtins and the
-.Ev PATH
-is searched.
-.Pp
-An existing function may be deleted using
-.Ic unset Fl f Ar function-name .
-A list of functions can be obtained using
-.Ic typeset +f
-and the function definitions can be listed using
-.Ic typeset \-f .
-The
-.Ic autoload
-command (which is an alias for
-.Ic typeset \-fu )
-may be used to create undefined functions: when an undefined function is
-executed, the shell searches the path specified in the
-.Ev FPATH
-parameter for a file with the same name as the function which, if found, is
-read and executed.
-If after executing the file the named function is found to
-be defined, the function is executed; otherwise, the normal command search is
-continued (i.e. the shell searches the regular built-in command table and
-.Ev PATH ) .
-Note that if a command is not found using
-.Ev PATH ,
-an attempt is made to autoload a function using
-.Ev FPATH
-(this is an undocumented feature of the original Korn shell).
-.Pp
-Functions can have two attributes,
-.Dq trace
-and
-.Dq export ,
-which can be set with
-.Ic typeset \-ft
-and
-.Ic typeset \-fx ,
-respectively.
-When a traced function is executed, the shell's
-.Ic xtrace
-option is turned on for the function's duration.
-The
-.Dq export
-attribute of functions is currently not used.
-In the original Korn shell,
-exported functions are visible to shell scripts that are executed.
-.Pp
-Since functions are executed in the current shell environment, parameter
-assignments made inside functions are visible after the function completes.
-If this is not the desired effect, the
-.Ic typeset
-command can be used inside a function to create a local parameter.
-Note that
-.At
-.Nm ksh93
-uses static scoping (one global scope, one local scope per function)
-and allows local variables only on Korn style functions, whereas
-.Nm mksh
-uses dynamic scoping (nested scopes of varying locality).
-Note that special parameters (e.g.\&
-.Ic \&$$ , $! )
-can't be scoped in this way.
-.Pp
-The exit status of a function is that of the last command executed in the
-function.
-A function can be made to finish immediately using the
-.Ic return
-command; this may also be used to explicitly specify the exit status.
-.Pp
-Functions defined with the
-.Ic function
-reserved word are treated differently in the following ways from functions
-defined with the
-.Ic \&()
-notation:
-.Bl -bullet
-.It
-The $0 parameter is set to the name of the function
-(Bourne-style functions leave $0 untouched).
-.It
-Parameter assignments preceding function calls are not kept in the shell
-environment (executing Bourne-style functions will keep assignments).
-.It
-.Ev OPTIND
-is saved/reset and restored on entry and exit from the function so
-.Ic getopts
-can be used properly both inside and outside the function (Bourne-style
-functions leave
-.Ev OPTIND
-untouched, so using
-.Ic getopts
-inside a function interferes with using
-.Ic getopts
-outside the function).
-.It
-Bourne-style function definitions take precedence over alias dereferences
-and remove alias definitions upon encounter, while aliases take precedence
-over Korn-style functions.
-.El
-.Pp
-In the future, the following differences may also be added:
-.Bl -bullet
-.It
-A separate trap/signal environment will be used during the execution of
-functions.
-This will mean that traps set inside a function will not affect the
-shell's traps and signals that are not ignored in the shell (but may be
-trapped) will have their default effect in a function.
-.It
-The EXIT trap, if set in a function, will be executed after the function
-returns.
-.El
-.Ss Command execution
-After evaluation of command-line arguments, redirections, and parameter
-assignments, the type of command is determined: a special built-in command,
-a function, a normal builtin, or the name of a file to execute found using the
-.Ev PATH
-parameter.
-The checks are made in the above order.
-Special built-in commands differ from other commands in that the
-.Ev PATH
-parameter is not used to find them, an error during their execution can
-cause a non-interactive shell to exit, and parameter assignments that are
-specified before the command are kept after the command completes.
-Regular built-in commands are different only in that the
-.Ev PATH
-parameter is not used to find them.
-.Pp
-The original
-.Nm ksh
-and POSIX differ somewhat in which commands are considered
-special or regular.
-.Pp
-POSIX special built-in utilities:
-.Pp
-.Ic \&. , \&: , break , continue ,
-.Ic eval , exec , exit , export ,
-.Ic readonly , return , set , shift ,
-.Ic times , trap , unset
-.Pp
-Additional
-.Nm
-commands keeping assignments:
-.Pp
-.Ic builtin , global , typeset , wait
-.Pp
-Builtins that are not special:
-.Pp
-.Ic [ , alias , bg , bind ,
-.Ic cat , cd , command , echo ,
-.Ic false , fc , fg , getopts ,
-.Ic jobs , kill , let , mknod ,
-.Ic print , pwd , read , realpath ,
-.Ic rename , sleep , test , true ,
-.Ic ulimit , umask , unalias , whence
-.Pp
-Once the type of command has been determined, any command-line parameter
-assignments are performed and exported for the duration of the command.
-.Pp
-The following describes the special and regular built-in commands:
-.Pp
-.Bl -tag -width false -compact
-.It Ic \&. Ar file Op Ar arg ...
-This is called the
-.Dq dot
-command.
-Execute the commands in
-.Ar file
-in the current environment.
-The file is searched for in the directories of
-.Ev PATH .
-If arguments are given, the positional parameters may be used to access them
-while
-.Ar file
-is being executed.
-If no arguments are given, the positional parameters are
-those of the environment the command is used in.
-.Pp
-.It Ic \&: Op Ar ...
-The null command.
-Exit status is set to zero.
-.Pp
-.It Ic \&[ Ar expression Ic \&]
-See
-.Ic test .
-.Pp
-.It Xo Ic alias
-.Oo Fl d \*(Ba t Oo Fl r Oc \*(Ba
-.Cm +\-x Oc
-.Op Fl p
-.Op Cm +
-.Oo Ar name
-.Op Ns = Ns Ar value
-.Ar ... Oc
-.Xc
-Without arguments,
-.Ic alias
-lists all aliases.
-For any name without a value, the existing alias is listed.
-Any name with a value defines an alias (see
-.Sx Aliases
-above).
-.Pp
-When listing aliases, one of two formats is used.
-Normally, aliases are listed as
-.Ar name Ns = Ns Ar value ,
-where
-.Ar value
-is quoted.
-If options were preceded with
-.Ql + ,
-or a lone
-.Ql +
-is given on the command line, only
-.Ar name
-is printed.
-.Pp
-The
-.Fl d
-option causes directory aliases which are used in tilde expansion to be
-listed or set (see
-.Sx Tilde expansion
-above).
-.Pp
-If the
-.Fl p
-option is used, each alias is prefixed with the string
-.Dq alias\ \& .
-.Pp
-The
-.Fl t
-option indicates that tracked aliases are to be listed/set (values specified on
-the command line are ignored for tracked aliases).
-The
-.Fl r
-option indicates that all tracked aliases are to be reset.
-.Pp
-The
-.Fl x
-option sets
-.Pq Ic +x No clears
-the export attribute of an alias, or, if no names are given, lists the aliases
-with the export attribute (exporting an alias has no effect).
-.Pp
-.It Ic bg Op Ar job ...
-Resume the specified stopped job(s) in the background.
-If no jobs are specified,
-.Ic %+
-is assumed.
-See
-.Sx Job control
-below for more information.
-.Pp
-.It Ic bind Op Fl l
-The current bindings are listed.
-If the
-.Fl l
-flag is given,
-.Ic bind
-instead lists the names of the functions to which keys may be bound.
-See
-.Sx Emacs editing mode
-for more information.
-.Pp
-.It Xo Ic bind Op Fl m
-.Ar string Ns = Ns Op Ar substitute
-.Ar ...
-.Xc
-.It Xo Ic bind
-.Ar string Ns = Ns Op Ar editing-command
-.Ar ...
-.Xc
-The specified editing command is bound to the given
-.Ar string ,
-which should consist of a control character
-optionally preceded by one of the two prefix characters
-and optionally succeded by a tilde character.
-Future input of the
-.Ar string
-will cause the editing command to be immediately invoked.
-If the
-.Fl m
-flag is given, the specified input
-.Ar string
-will afterwards be immediately replaced by the given
-.Ar substitute
-string which may contain editing commands but not other macros.
-If a tilde postfix is given, a tilde trailing the one or
-two prefices and the control character is ignored, any
-other trailing character will be processed afterwards.
-.Pp
-Control characters may be written using caret notation
-i.e. \*(haX represents Ctrl-X.
-Note that although only two prefix characters (usually ESC and \*(haX)
-are supported, some multi-character sequences can be supported.
-.Pp
-The following default bindings show how the arrow keys, the home, end and
-delete key on a BSD wsvt25, xterm\-xfree86 or GNU screen terminal are bound
-(of course some escape sequences won't work out quite this nicely):
-.Bd -literal -offset indent
-bind \*(aq\*(haX\*(aq=prefix\-2
-bind \*(aq\*(ha[[\*(aq=prefix\-2
-bind \*(aq\*(haXA\*(aq=up\-history
-bind \*(aq\*(haXB\*(aq=down\-history
-bind \*(aq\*(haXC\*(aq=forward\-char
-bind \*(aq\*(haXD\*(aq=backward\-char
-bind \*(aq\*(haX1\*(TI\*(aq=beginning\-of\-line
-bind \*(aq\*(haX7\*(TI\*(aq=beginning\-of\-line
-bind \*(aq\*(haXH\*(aq=beginning\-of\-line
-bind \*(aq\*(haX4\*(TI\*(aq=end\-of\-line
-bind \*(aq\*(haX8\*(TI\*(aq=end\-of\-line
-bind \*(aq\*(haXF\*(aq=end\-of\-line
-bind \*(aq\*(haX3\*(TI\*(aq=delete\-char\-forward
-.Ed
-.Pp
-.It Ic break Op Ar level
-Exit the
-.Ar level Ns th
-inner-most
-.Ic for ,
-.Ic select ,
-.Ic until ,
-or
-.Ic while
-loop.
-.Ar level
-defaults to 1.
-.Pp
-.It Xo
-.Ic builtin
-.Op Fl \-
-.Ar command Op Ar arg ...
-.Xc
-Execute the built-in command
-.Ar command .
-.Pp
-.It Xo
-.Ic cat
-.Op Fl u
-.Op Ar
-.Xc
-Read files sequentially, in command line order, and write them to
-standard output.
-If a
-.Ar file
-is a single dash
-.Pq Sq -
-or absent, read from standard input.
-Unless compiled with
-.Dv MKSH_NO_EXTERNAL_CAT ,
-if any options are given, an external
-.Xr cat 1
-utility is invoked instead if called from the shell.
-For direct builtin calls, the
-.Tn POSIX
-.Fl u
-option is supported as a no-op.
-.Pp
-.It Xo
-.Ic cd
-.Op Fl L
-.Op Ar dir
-.Xc
-.It Xo
-.Ic cd
-.Fl P Op Fl e
-.Op Ar dir
-.Xc
-.It Xo
-.Ic chdir
-.Op Fl eLP
-.Op Ar dir
-.Xc
-Set the working directory to
-.Ar dir .
-If the parameter
-.Ev CDPATH
-is set, it lists the search path for the directory containing
-.Ar dir .
-A
-.Dv NULL
-path means the current directory.
-If
-.Ar dir
-is found in any component of the
-.Ev CDPATH
-search path other than the
-.Dv NULL
-path, the name of the new working directory will be written to standard output.
-If
-.Ar dir
-is missing, the home directory
-.Ev HOME
-is used.
-If
-.Ar dir
-is
-.Ql \- ,
-the previous working directory is used (see the
-.Ev OLDPWD
-parameter).
-.Pp
-If the
-.Fl L
-option (logical path) is used or if the
-.Ic physical
-option isn't set (see the
-.Ic set
-command below), references to
-.Sq ..
-in
-.Ar dir
-are relative to the path used to get to the directory.
-If the
-.Fl P
-option (physical path) is used or if the
-.Ic physical
-option is set,
-.Sq ..
-is relative to the filesystem directory tree.
-The
-.Ev PWD
-and
-.Ev OLDPWD
-parameters are updated to reflect the current and old working directory,
-respectively.
-If the
-.Fl e
-option is set for physical filesystem traversal, and
-.Ev PWD
-could not be set, the exit code is 1; greater than 1 if an
-error occurred, 0 otherwise.
-.Pp
-.It Xo
-.Ic cd
-.Op Fl eLP
-.Ar old new
-.Xc
-.It Xo
-.Ic chdir
-.Op Fl eLP
-.Ar old new
-.Xc
-The string
-.Ar new
-is substituted for
-.Ar old
-in the current directory, and the shell attempts to change to the new
-directory.
-.Pp
-.It Xo
-.Ic command
-.Op Fl pVv
-.Ar cmd
-.Op Ar arg ...
-.Xc
-If neither the
-.Fl v
-nor
-.Fl V
-option is given,
-.Ar cmd
-is executed exactly as if
-.Ic command
-had not been specified, with two exceptions:
-firstly,
-.Ar cmd
-cannot be a shell function;
-and secondly, special built-in commands lose their specialness
-(i.e. redirection and utility errors do not cause the shell to
-exit, and command assignments are not permanent).
-.Pp
-If the
-.Fl p
-option is given, a default search path is used instead of the current value of
-.Ev PATH ,
-the actual value of which is system dependent.
-.Pp
-If the
-.Fl v
-option is given, instead of executing
-.Ar cmd ,
-information about what would be executed is given (and the same is done for
-.Ar arg ... ) .
-For special and regular built-in commands and functions, their names are simply
-printed; for aliases, a command that defines them is printed; and for commands
-found by searching the
-.Ev PATH
-parameter, the full path of the command is printed.
-If no command is found
-(i.e. the path search fails), nothing is printed and
-.Ic command
-exits with a non-zero status.
-The
-.Fl V
-option is like the
-.Fl v
-option, except it is more verbose.
-.Pp
-.It Ic continue Op Ar level
-Jumps to the beginning of the
-.Ar level Ns th
-inner-most
-.Ic for ,
-.Ic select ,
-.Ic until ,
-or
-.Ic while
-loop.
-.Ar level
-defaults to 1.
-.Pp
-.It Xo
-.Ic echo
-.Op Fl Een
-.Op Ar arg ...
-.Xc
-.Em Warning:
-this utility is not portable; use the Korn shell builtin
-.Ic print
-instead.
-.Pp
-Prints its arguments (separated by spaces) followed by a newline, to the
-standard output.
-The newline is suppressed if any of the arguments contain the
-backslash sequence
-.Ql \ec .
-See the
-.Ic print
-command below for a list of other backslash sequences that are recognised.
-.Pp
-The options are provided for compatibility with
-.Bx
-shell scripts.
-The
-.Fl n
-option suppresses the trailing newline,
-.Fl e
-enables backslash interpretation (a no-op, since this is normally done), and
-.Fl E
-suppresses backslash interpretation.
-.Pp
-If the
-.Ic posix
-or
-.Ic sh
-option is set or this is a direct builtin call, only the first argument
-is treated as an option, and only if it is exactly
-.Dq Fl n .
-Backslash interpretation is disabled.
-.Pp
-.It Ic eval Ar command ...
-The arguments are concatenated (with spaces between them) to form a single
-string which the shell then parses and executes in the current environment.
-.Pp
-.It Xo
-.Ic exec
-.Op Ar command Op Ar arg ...
-.Xc
-The command is executed without forking, replacing the shell process.
-.Pp
-If no command is given except for I/O redirection, the I/O redirection is
-permanent and the shell is
-not replaced.
-Any file descriptors greater than 2 which are opened or
-.Xr dup 2 Ns 'd
-in this way are not made available to other executed commands (i.e. commands
-that are not built-in to the shell).
-Note that the Bourne shell differs here;
-it does pass these file descriptors on.
-.Pp
-.It Ic exit Op Ar status
-The shell exits with the specified exit status.
-If
-.Ar status
-is not specified, the exit status is the current value of the
-.Ic $?\&
-parameter.
-.Pp
-.It Xo
-.Ic export
-.Op Fl p
-.Op Ar parameter Ns Op = Ns Ar value
-.Xc
-Sets the export attribute of the named parameters.
-Exported parameters are passed in the environment to executed commands.
-If values are specified, the named parameters are also assigned.
-.Pp
-If no parameters are specified, all parameters with the export attribute
-set are printed one per line; either their names, or, if a
-.Ql \-
-with no option letter is specified, name=value pairs, or, with
-.Fl p ,
-.Ic export
-commands suitable for re-entry.
-.Pp
-.It Ic false
-A command that exits with a non-zero status.
-.Pp
-.It Xo
-.Ic fc
-.Oo Fl e Ar editor \*(Ba
-.Fl l Op Fl n Oc
-.Op Fl r
-.Op Ar first Op Ar last
-.Xc
-.Ar first
-and
-.Ar last
-select commands from the history.
-Commands can be selected by history number
-or a string specifying the most recent command starting with that string.
-The
-.Fl l
-option lists the command on standard output, and
-.Fl n
-inhibits the default command numbers.
-The
-.Fl r
-option reverses the order of the list.
-Without
-.Fl l ,
-the selected commands are edited by the editor specified with the
-.Fl e
-option, or if no
-.Fl e
-is specified, the editor specified by the
-.Ev FCEDIT
-parameter (if this parameter is not set,
-.Pa /bin/ed
-is used), and then executed by the shell.
-.Pp
-.It Xo
-.Ic fc
-.Cm \-e \- \*(Ba Fl s
-.Op Fl g
-.Op Ar old Ns = Ns Ar new
-.Op Ar prefix
-.Xc
-Re-execute the selected command (the previous command by default) after
-performing the optional substitution of
-.Ar old
-with
-.Ar new .
-If
-.Fl g
-is specified, all occurrences of
-.Ar old
-are replaced with
-.Ar new .
-The meaning of
-.Cm \-e \-
-and
-.Fl s
-is identical: re-execute the selected command without invoking an editor.
-This command is usually accessed with the predefined:
-.Ic alias r=\*(aqfc \-e \-\*(aq
-.Pp
-.It Ic fg Op Ar job ...
-Resume the specified job(s) in the foreground.
-If no jobs are specified,
-.Ic %+
-is assumed.
-See
-.Sx Job control
-below for more information.
-.Pp
-.It Xo
-.Ic getopts
-.Ar optstring name
-.Op Ar arg ...
-.Xc
-Used by shell procedures to parse the specified arguments (or positional
-parameters, if no arguments are given) and to check for legal options.
-.Ar optstring
-contains the option letters that
-.Ic getopts
-is to recognise.
-If a letter is followed by a colon, the option is expected to
-have an argument.
-Options that do not take arguments may be grouped in a single argument.
-If an option takes an argument and the option character is not the
-last character of the argument it is found in, the remainder of the argument is
-taken to be the option's argument; otherwise, the next argument is the option's
-argument.
-.Pp
-Each time
-.Ic getopts
-is invoked, it places the next option in the shell parameter
-.Ar name
-and the index of the argument to be processed by the next call to
-.Ic getopts
-in the shell parameter
-.Ev OPTIND .
-If the option was introduced with a
-.Ql + ,
-the option placed in
-.Ar name
-is prefixed with a
-.Ql + .
-When an option requires an argument,
-.Ic getopts
-places it in the shell parameter
-.Ev OPTARG .
-.Pp
-When an illegal option or a missing option argument is encountered, a question
-mark or a colon is placed in
-.Ar name
-(indicating an illegal option or missing argument, respectively) and
-.Ev OPTARG
-is set to the option character that caused the problem.
-Furthermore, if
-.Ar optstring
-does not begin with a colon, a question mark is placed in
-.Ar name ,
-.Ev OPTARG
-is unset, and an error message is printed to standard error.
-.Pp
-When the end of the options is encountered,
-.Ic getopts
-exits with a non-zero exit status.
-Options end at the first (non-option
-argument) argument that does not start with a
-.Ql \- ,
-or when a
-.Ql \-\-
-argument is encountered.
-.Pp
-Option parsing can be reset by setting
-.Ev OPTIND
-to 1 (this is done automatically whenever the shell or a shell procedure is
-invoked).
-.Pp
-Warning: Changing the value of the shell parameter
-.Ev OPTIND
-to a value other than 1, or parsing different sets of arguments without
-resetting
-.Ev OPTIND ,
-may lead to unexpected results.
-.Pp
-.It global Ar ...
-See
-.Ic typeset .
-.Pp
-.It Xo
-.Ic hash
-.Op Fl r
-.Op Ar name ...
-.Xc
-Without arguments, any hashed executable command pathnames are listed.
-The
-.Fl r
-option causes all hashed commands to be removed from the hash table.
-Each
-.Ar name
-is searched as if it were a command name and added to the hash table if it is
-an executable command.
-.Pp
-.It Xo
-.Ic jobs
-.Op Fl lnp
-.Op Ar job ...
-.Xc
-Display information about the specified job(s); if no jobs are specified, all
-jobs are displayed.
-The
-.Fl n
-option causes information to be displayed only for jobs that have changed
-state since the last notification.
-If the
-.Fl l
-option is used, the process ID of each process in a job is also listed.
-The
-.Fl p
-option causes only the process group of each job to be printed.
-See
-.Sx Job control
-below for the format of
-.Ar job
-and the displayed job.
-.Pp
-.It Xo
-.Ic kill
-.Oo Fl s Ar signame \*(Ba
-.No \- Ns Ar signum \*(Ba
-.No \- Ns Ar signame Oc
-.No { Ar job \*(Ba pid \*(Ba pgrp No }
-.Ar ...
-.Xc
-Send the specified signal to the specified jobs, process IDs, or process
-groups.
-If no signal is specified, the
-.Dv TERM
-signal is sent.
-If a job is specified, the signal is sent to the job's process group.
-See
-.Sx Job control
-below for the format of
-.Ar job .
-.Pp
-.It Xo
-.Ic kill
-.Fl l
-.Op Ar exit-status ...
-.Xc
-Print the signal name corresponding to
-.Ar exit-status .
-If no arguments are specified, a list of all the signals, their numbers, and
-a short description of them are printed.
-.Pp
-.It Ic let Op Ar expression ...
-Each expression is evaluated (see
-.Sx Arithmetic expressions
-above).
-If all expressions are successfully evaluated, the exit status is 0 (1)
-if the last expression evaluated to non-zero (zero).
-If an error occurs during
-the parsing or evaluation of an expression, the exit status is greater than 1.
-Since expressions may need to be quoted,
-.No \&(( Ar expr No ))
-is syntactic sugar for
-.No let \&" Ns Ar expr Ns \&" .
-.Pp
-.It let]
-Internally used alias for
-.Ic let .
-.Pp
-.It Xo
-.Ic mknod
-.Op Fl m Ar mode
-.Ar name
-.Cm b\*(Bac
-.Ar major minor
-.Xc
-.It Xo
-.Ic mknod
-.Op Fl m Ar mode
-.Ar name
-.Cm p
-.Xc
-Create a device special file.
-The file type may be
-.Cm b
-(block type device),
-.Cm c
-(character type device),
-or
-.Cm p
-.Pq named pipe , Tn FIFO .
-The file created may be modified according to its
-.Ar mode
-(via the
-.Fl m
-option),
-.Ar major
-(major device number),
-and
-.Ar minor
-(minor device number).
-.Pp
-See
-.Xr mknod 8
-for further information.
-.Pp
-.It Xo
-.Ic print
-.Oo Fl nprsu Ns Oo Ar n Oc \*(Ba
-.Fl R Op Fl en Oc
-.Op Ar argument ...
-.Xc
-.Ic print
-prints its arguments on the standard output, separated by spaces and
-terminated with a newline.
-The
-.Fl n
-option suppresses the newline.
-By default, certain C escapes are translated.
-These include these mentioned in
-.Sx Backslash expansion
-above, as well as
-.Ql \ec ,
-which is equivalent to using the
-.Fl n
-option.
-Backslash expansion may be inhibited with the
-.Fl r
-option.
-The
-.Fl s
-option prints to the history file instead of standard output; the
-.Fl u
-option prints to file descriptor
-.Ar n
-.Po
-.Ar n
-defaults to 1 if omitted
-.Pc ;
-and the
-.Fl p
-option prints to the co-process (see
-.Sx Co-processes
-above).
-.Pp
-The
-.Fl R
-option is used to emulate, to some degree, the
-.Bx
-.Xr echo 1
-command which does not process
-.Ql \e
-sequences unless the
-.Fl e
-option is given.
-As above, the
-.Fl n
-option suppresses the trailing newline.
-.Pp
-.It Ic printf Ar format Op Ar arguments ...
-Formatted output.
-Approximately the same as the
-.Xr printf 1 ,
-utility, except it uses the same
-.Sx Backslash expansion
-and I/O code and does hot handle floating point as the rest of
-.Nm mksh .
-This is not normally part of
-.Nm mksh ;
-however, distributors may have added this as builtin as a speed hack.
-Do not use in new code.
-.Pp
-.It Ic pwd Op Fl LP
-Print the present working directory.
-If the
-.Fl L
-option is used or if the
-.Ic physical
-option isn't set (see the
-.Ic set
-command below), the logical path is printed (i.e. the path used to
-.Ic cd
-to the current directory).
-If the
-.Fl P
-option (physical path) is used or if the
-.Ic physical
-option is set, the path determined from the filesystem (by following
-.Sq ..
-directories to the root directory) is printed.
-.Pp
-.It Xo
-.Ic read
-.Op Fl A \*(Ba Fl a
-.Op Fl d Ar x
-.Oo Fl N Ar z \*(Ba
-.Fl n Ar z Oc
-.Oo Fl p \*(Ba
-.Fl u Ns Op Ar n
-.Oc Op Fl t Ar n
-.Op Fl rs
-.Op Ar p ...
-.Xc
-Reads a line of input, separates the input into fields using the
-.Ev IFS
-parameter (see
-.Sx Substitution
-above), and assigns each field to the specified parameters
-.Ar p .
-If no parameters are specified, the
-.Ev REPLY
-parameter is used to store the result.
-With the
-.Fl A
-and
-.Fl a
-options, only no or one parameter is accepted.
-If there are more parameters than fields, the extra parameters are set to
-the empty string or 0; if there are more fields than parameters, the last
-parameter is assigned the remaining fields (including the word separators).
-.Pp
-The options are as follows:
-.Bl -tag -width XuXnX
-.It Fl A
-Store the result into the parameter
-.Ar p
-(or
-.Ev REPLY )
-as array of words.
-.It Fl a
-Store the result without word splitting into the parameter
-.Ar p
-(or
-.Ev REPLY )
-as array of characters (wide characters if the
-.Ic utf8\-mode
-option is enacted, octets otherwise).
-.It Fl d Ar x
-Use the first byte of
-.Ar x ,
-.Dv NUL
-if empty, instead of the ASCII newline character as input line delimiter.
-.It Fl N Ar z
-Instead of reading till end-of-line, read exactly
-.Ar z
-bytes; less if EOF or a timeout occurs.
-.It Fl n Ar z
-Instead of reading till end-of-line, read up to
-.Ar z
-bytes but return as soon as any bytes are read, e.g.\& from a
-slow terminal device, or if EOF or a timeout occurs.
-.It Fl p
-Read from the currently active co-process, see
-.Sx Co-processes
-above for details on this.
-.It Fl u Ns Op Ar n
-Read from the file descriptor
-.Ar n
-(defaults to 0, i.e.\& standard input).
-The argument must immediately follow the option character.
-.It Fl t Ar n
-Interrupt reading after
-.Ar n
-seconds (specified as positive decimal value with an optional fractional part).
-.It Fl r
-Normally, the ASCII backslash character escapes the special
-meaning of the following character and is stripped from the input;
-.Ic read
-does not stop when encountering a backslash-newline sequence and
-does not store that newline in the result.
-This option enables raw mode, in which backslashes are not processed.
-.It Fl s
-The input line is saved to the history.
-.El
-.Pp
-If the input is a terminal, both the
-.Fl N
-and
-.Fl n
-options set it into raw mode;
-they read an entire file if \-1 is passed as
-.Ar z
-argument.
-.Pp
-The first parameter may have a question mark and a string appended to it, in
-which case the string is used as a prompt (printed to standard error before
-any input is read) if the input is a
-.Xr tty 4
-(e.g.\&
-.Ic read nfoo?\*(aqnumber of foos: \*(aq ) .
-.Pp
-If no input is read or a timeout occurred,
-.Ic read
-exits with a non-zero status.
-.Pp
-Another handy set of tricks:
-If
-.Ic read
-is run in a loop such as
-.Ic while read foo; do ...; done
-then leading whitespace will be removed (IFS) and backslashes processed.
-You might want to use
-.Ic while IFS= read \-r foo; do ...; done
-for pristine I/O.
-Similarily, when using the
-.Fl a
-option, use of the
-.Fl r
-option might be prudent; the same applies for:
-.Bd -literal -offset indent
-find . \-type f \-print0 \*(Ba \e
-    while IFS= read \-d \*(aq\*(aq \-r filename; do
-	print \-r \-\- "found \*(Lt${filename#./}\*(Gt"
-done
-.Ed
-.Pp
-The inner loop will be executed in a subshell and variable changes
-cannot be propagated if executed in a pipeline:
-.Bd -literal -offset indent
-bar \*(Ba baz \*(Ba while read foo; do ...; done
-.Ed
-.Pp
-Use co-processes instead:
-.Bd -literal -offset indent
-bar \*(Ba baz \*(Ba&
-while read \-p foo; do ...; done
-exec 3\*(Gt&p; exec 3\*(Gt&\-
-.Ed
-.Pp
-.It Xo
-.Ic readonly
-.Op Fl p
-.Oo Ar parameter
-.Op Ns = Ns Ar value
-.Ar ... Oc
-.Xc
-Sets the read-only attribute of the named parameters.
-If values are given,
-parameters are set to them before setting the attribute.
-Once a parameter is
-made read-only, it cannot be unset and its value cannot be changed.
-.Pp
-If no parameters are specified, the names of all parameters with the read-only
-attribute are printed one per line, unless the
-.Fl p
-option is used, in which case
-.Ic readonly
-commands defining all read-only parameters, including their values, are
-printed.
-.Pp
-.It Xo
-.Ic realpath
-.Op Fl \-
-.Ar name
-.Xc
-Prints the resolved absolute pathname corresponding to
-.Ar name .
-If
-.Ar name
-ends with a slash
-.Pq Sq / ,
-it's also checked for existence and whether it is a directory; otherwise,
-.Ic realpath
-returns 0 if the pathname either exists or can be created immediately,
-i.e. all but the last component exist and are directories.
-.Pp
-.It Xo
-.Ic rename
-.Op Fl \-
-.Ar from to
-.Xc
-Renames the file
-.Ar from
-to
-.Ar to .
-Both must be complete pathnames and on the same device.
-This builtin is intended for emergency situations where
-.Pa /bin/mv
-becomes unusable, and directly calls
-.Xr rename 2 .
-.Pp
-.It Ic return Op Ar status
-Returns from a function or
-.Ic .\&
-script, with exit status
-.Ar status .
-If no
-.Ar status
-is given, the exit status of the last executed command is used.
-If used outside of a function or
-.Ic .\&
-script, it has the same effect as
-.Ic exit .
-Note that
-.Nm
-treats both profile and
-.Ev ENV
-files as
-.Ic .\&
-scripts, while the original Korn shell only treats profiles as
-.Ic .\&
-scripts.
-.Pp
-.It Xo
-.Ic set Op Ic +\-abCefhiklmnprsUuvXx
-.Op Ic +\-o Ar option
-.Op Ic +\-A Ar name
-.Op Fl \-
-.Op Ar arg ...
-.Xc
-The
-.Ic set
-command can be used to set
-.Pq Ic \-
-or clear
-.Pq Ic +
-shell options, set the positional parameters, or set an array parameter.
-Options can be changed using the
-.Cm +\-o Ar option
-syntax, where
-.Ar option
-is the long name of an option, or using the
-.Cm +\- Ns Ar letter
-syntax, where
-.Ar letter
-is the option's single letter name (not all options have a single letter name).
-The following table lists both option letters (if they exist) and long names
-along with a description of what the option does:
-.Bl -tag -width 3n
-.It Fl A Ar name
-Sets the elements of the array parameter
-.Ar name
-to
-.Ar arg ...
-If
-.Fl A
-is used, the array is reset (i.e. emptied) first; if
-.Ic +A
-is used, the first N elements are set (where N is the number of arguments);
-the rest are left untouched.
-.Pp
-An alternative syntax for the command
-.Ic set \-A foo \-\- a b c
-which is compatible to
-.Tn GNU
-.Nm bash
-and also supported by
-.At
-.Nm ksh93
-is:
-.Ic foo=(a b c); foo+=(d e)
-.Pp
-Another
-.At
-.Nm ksh93
-and
-.Tn GNU
-.Nm bash
-extension allows specifying the indices used for
-.Ar arg ...
-.Pq from the above example, Ic a b c
-like this:
-.Ic set \-A foo \-\- [0]=a [1]=b [2]=c
-or
-.Ic foo=([0]=a [1]=b [2]=c)
-which can also be written
-.Ic foo=([0]=a b c)
-because indices are incremented automatically.
-.It Fl a \*(Ba Fl o Ic allexport
-All new parameters are created with the export attribute.
-.It Fl b \*(Ba Fl o Ic notify
-Print job notification messages asynchronously, instead of just before the
-prompt.
-Only used if job control is enabled
-.Pq Fl m .
-.It Fl C \*(Ba Fl o Ic noclobber
-Prevent \*(Gt redirection from overwriting existing files.
-Instead, \*(Gt\*(Ba must be used to force an overwrite.
-.It Fl e \*(Ba Fl o Ic errexit
-Exit (after executing the
-.Dv ERR
-trap) as soon as an error occurs or a command fails (i.e. exits with a
-non-zero status).
-This does not apply to commands whose exit status is
-explicitly tested by a shell construct such as
-.Ic if ,
-.Ic until ,
-.Ic while ,
-or
-.Ic !\&
-statements.
-For
-.Ic &&
-or
-.Ic \*(Ba\*(Ba ,
-only the status of the last command is tested.
-.It Fl f \*(Ba Fl o Ic noglob
-Do not expand file name patterns.
-.It Fl h \*(Ba Fl o Ic trackall
-Create tracked aliases for all executed commands (see
-.Sx Aliases
-above).
-Enabled by default for non-interactive shells.
-.It Fl i \*(Ba Fl o Ic interactive
-The shell is an interactive shell.
-This option can only be used when the shell is invoked.
-See above for a description of what this means.
-.It Fl k \*(Ba Fl o Ic keyword
-Parameter assignments are recognised anywhere in a command.
-.It Fl l \*(Ba Fl o Ic login
-The shell is a login shell.
-This option can only be used when the shell is invoked.
-See above for a description of what this means.
-.It Fl m \*(Ba Fl o Ic monitor
-Enable job control (default for interactive shells).
-.It Fl n \*(Ba Fl o Ic noexec
-Do not execute any commands.
-Useful for checking the syntax of scripts
-(ignored if interactive).
-.It Fl p \*(Ba Fl o Ic privileged
-The shell is a privileged shell.
-It is set automatically if, when the shell starts,
-the real UID or GID does not match
-the effective UID (EUID) or GID (EGID), respectively.
-See above for a description of what this means.
-.It Fl r \*(Ba Fl o Ic restricted
-The shell is a restricted shell.
-This option can only be used when the shell is invoked.
-See above for a description of what this means.
-.It Fl s \*(Ba Fl o Ic stdin
-If used when the shell is invoked, commands are read from standard input.
-Set automatically if the shell is invoked with no arguments.
-.Pp
-When
-.Fl s
-is used with the
-.Ic set
-command it causes the specified arguments to be sorted before assigning them to
-the positional parameters (or to array
-.Ar name ,
-if
-.Fl A
-is used).
-.It Fl U \*(Ba Fl o Ic utf8\-mode
-Enable UTF-8 support in the
-.Sx Emacs editing mode
-and internal string handling functions.
-This flag is disabled by default, but can be enabled by setting it on the
-shell command line; is enabled automatically for interactive shells if
-requested at compile time, your system supports
-.Fn setlocale LC_CTYPE \&""
-and optionally
-.Fn nl_langinfo CODESET ,
-or the
-.Ev LC_ALL ,
-.Ev LC_CTYPE ,
-or
-.Ev LANG
-environment variables,
-and at least one of these returns something that matches
-.Dq UTF\-8
-or
-.Dq utf8
-case-insensitively; for direct builtin calls depending on the
-aforementioned environment variables; or for stdin or scripts,
-if the input begins with a UTF-8 Byte Order Mark.
-.It Fl u \*(Ba Fl o Ic nounset
-Referencing of an unset parameter, other than
-.Dq $@
-or
-.Dq $* ,
-is treated as an error, unless one of the
-.Ql \- ,
-.Ql + ,
-or
-.Ql =
-modifiers is used.
-.It Fl v \*(Ba Fl o Ic verbose
-Write shell input to standard error as it is read.
-.It Fl X \*(Ba Fl o Ic markdirs
-Mark directories with a trailing
-.Ql /
-during file name generation.
-.It Fl x \*(Ba Fl o Ic xtrace
-Print command trees when they are executed, preceded by
-the value of
-.Ev PS4 .
-.It Fl o Ic bgnice
-Background jobs are run with lower priority.
-.It Fl o Ic braceexpand
-Enable brace expansion (a.k.a. alternation).
-This is enabled by default.
-If disabled, tilde expansion after an equals sign is disabled as a side effect.
-.It Fl o Ic emacs
-Enable BRL emacs-like command-line editing (interactive shells only); see
-.Sx Emacs editing mode .
-.It Fl o Ic gmacs
-Enable gmacs-like command-line editing (interactive shells only).
-Currently identical to emacs editing except that transpose\-chars (\*(haT) acts
-slightly differently.
-.It Fl o Ic ignoreeof
-The shell will not (easily) exit when end-of-file is read;
-.Ic exit
-must be used.
-To avoid infinite loops, the shell will exit if
-.Dv EOF
-is read 13 times in a row.
-.It Fl o Ic inherit\-xtrace
-Do not reset
-.Fl o Ic xtrace
-upon entering functions.
-This is enabled by default.
-.It Fl o Ic nohup
-Do not kill running jobs with a
-.Dv SIGHUP
-signal when a login shell exits.
-Currently set by default, but this may
-change in the future to be compatible with
-.At
-.Nm ksh ,
-which
-doesn't have this option, but does send the
-.Dv SIGHUP
-signal.
-.It Fl o Ic nolog
-No effect.
-In the original Korn shell, this prevents function definitions from
-being stored in the history file.
-.It Fl o Ic physical
-Causes the
-.Ic cd
-and
-.Ic pwd
-commands to use
-.Dq physical
-(i.e. the filesystem's)
-.Sq ..
-directories instead of
-.Dq logical
-directories (i.e. the shell handles
-.Sq .. ,
-which allows the user to be oblivious of symbolic links to directories).
-Clear by default.
-Note that setting this option does not affect the current value of the
-.Ev PWD
-parameter; only the
-.Ic cd
-command changes
-.Ev PWD .
-See the
-.Ic cd
-and
-.Ic pwd
-commands above for more details.
-.It Fl o Ic pipefail
-Make the exit status of a pipeline (before logically complementing) the
-rightmost non-zero errorlevel, or zero if all commands exited with zero.
-.It Fl o Ic posix
-Enable a somewhat more
-.Px
-ish mode.
-As a side effect, setting this flag turns off
-.Ic braceexpand
-mode, which can be turned back on manually, and
-.Ic sh
-mode.
-.It Fl o Ic sh
-Enable
-.Pa /bin/sh
-.Pq kludge
-mode.
-Automatically enabled if the basename of the shell invocation begins with
-.Dq sh
-and this autodetection feature is compiled in
-.Pq not in MirBSD .
-As a side effect, setting this flag turns off
-.Ic braceexpand
-mode, which can be turned back on manually, and
-.Ic posix
-mode.
-.It Fl o Ic vi
-Enable
-.Xr vi 1 Ns -like
-command-line editing (interactive shells only).
-.It Fl o Ic vi\-esccomplete
-In vi command-line editing, do command and file name completion when escape
-(\*(ha[) is entered in command mode.
-.It Fl o Ic vi\-tabcomplete
-In vi command-line editing, do command and file name completion when tab (\*(haI)
-is entered in insert mode.
-This is the default.
-.It Fl o Ic viraw
-No effect.
-In the original Korn shell, unless
-.Ic viraw
-was set, the vi command-line mode would let the
-.Xr tty 4
-driver do the work until ESC (\*(ha[) was entered.
-.Nm
-is always in viraw mode.
-.El
-.Pp
-These options can also be used upon invocation of the shell.
-The current set of
-options (with single letter names) can be found in the parameter
-.Sq $\- .
-.Ic set Fl o
-with no option name will list all the options and whether each is on or off;
-.Ic set +o
-will print the long names of all options that are currently on.
-.Pp
-Remaining arguments, if any, are positional parameters and are assigned, in
-order, to the positional parameters (i.e. $1, $2, etc.).
-If options end with
-.Ql \-\-
-and there are no remaining arguments, all positional parameters are cleared.
-If no options or arguments are given, the values of all names are printed.
-For unknown historical reasons, a lone
-.Ql \-
-option is treated specially \*(en it clears both the
-.Fl v
-and
-.Fl x
-options.
-.Pp
-.It Ic shift Op Ar number
-The positional parameters
-.Ar number Ns +1 ,
-.Ar number Ns +2 ,
-etc. are renamed to
-.Sq 1 ,
-.Sq 2 ,
-etc.
-.Ar number
-defaults to 1.
-.Pp
-.It Ic sleep Ar seconds
-Suspends execution for a minimum of the
-.Ar seconds
-specified as positive decimal value with an optional fractional part.
-Signal delivery may continue execution earlier.
-.Pp
-.It Ic source Ar file Op Ar arg ...
-Like
-.Ic \&. Po Do dot Dc Pc ,
-except that the current working directory is appended to the
-.Ev PATH
-in GNU
-.Nm bash
-and
-.Nm mksh .
-In
-.Nm ksh93
-and
-.Nm mksh ,
-this is implemented as a shell alias instead of a builtin.
-.Pp
-.It Ic test Ar expression
-.It Ic \&[ Ar expression Ic \&]
-.Ic test
-evaluates the
-.Ar expression
-and returns zero status if true, 1 if false, or greater than 1 if there
-was an error.
-It is normally used as the condition command of
-.Ic if
-and
-.Ic while
-statements.
-Symbolic links are followed for all
-.Ar file
-expressions except
-.Fl h
-and
-.Fl L .
-.Pp
-The following basic expressions are available:
-.Bl -tag -width 17n
-.It Fl a Ar file
-.Ar file
-exists.
-.It Fl b Ar file
-.Ar file
-is a block special device.
-.It Fl c Ar file
-.Ar file
-is a character special device.
-.It Fl d Ar file
-.Ar file
-is a directory.
-.It Fl e Ar file
-.Ar file
-exists.
-.It Fl f Ar file
-.Ar file
-is a regular file.
-.It Fl G Ar file
-.Ar file Ns 's
-group is the shell's effective group ID.
-.It Fl g Ar file
-.Ar file Ns 's
-mode has the setgid bit set.
-.It Fl H Ar file
-.Ar file
-is a context dependent directory (only useful on HP-UX).
-.It Fl h Ar file
-.Ar file
-is a symbolic link.
-.It Fl k Ar file
-.Ar file Ns 's
-mode has the
-.Xr sticky 8
-bit set.
-.It Fl L Ar file
-.Ar file
-is a symbolic link.
-.It Fl O Ar file
-.Ar file Ns 's
-owner is the shell's effective user ID.
-.It Fl o Ar option
-Shell
-.Ar option
-is set (see the
-.Ic set
-command above for a list of options).
-As a non-standard extension, if the option starts with a
-.Ql \&! ,
-the test is negated; the test always fails if
-.Ar option
-doesn't exist (so [ \-o foo \-o \-o !foo ] returns true if and only if option
-.Ar foo
-exists).
-The same can be achieved with [ \-o ?foo ] like in
-.At
-.Nm ksh93 .
-.Ar option
-can also be the short flag led by either
-.Ql \-
-or
-.Ql +
-.Pq no logical negation ,
-for example
-.Ql \-x
-or
-.Ql +x
-instead of
-.Ql xtrace .
-.It Fl p Ar file
-.Ar file
-is a named pipe
-.Pq Tn FIFO .
-.It Fl r Ar file
-.Ar file
-exists and is readable.
-.It Fl S Ar file
-.Ar file
-is a
-.Xr unix 4 Ns -domain
-socket.
-.It Fl s Ar file
-.Ar file
-is not empty.
-.It Fl t Ar fd
-File descriptor
-.Ar fd
-is a
-.Xr tty 4
-device.
-.It Fl u Ar file
-.Ar file Ns 's
-mode has the setuid bit set.
-.It Fl w Ar file
-.Ar file
-exists and is writable.
-.It Fl x Ar file
-.Ar file
-exists and is executable.
-.It Ar file1 Fl nt Ar file2
-.Ar file1
-is newer than
-.Ar file2
-or
-.Ar file1
-exists and
-.Ar file2
-does not.
-.It Ar file1 Fl ot Ar file2
-.Ar file1
-is older than
-.Ar file2
-or
-.Ar file2
-exists and
-.Ar file1
-does not.
-.It Ar file1 Fl ef Ar file2
-.Ar file1
-is the same file as
-.Ar file2 .
-.It Ar string
-.Ar string
-has non-zero length.
-.It Fl n Ar string
-.Ar string
-is not empty.
-.It Fl z Ar string
-.Ar string
-is empty.
-.It Ar string No = Ar string
-Strings are equal.
-.It Ar string No == Ar string
-Strings are equal.
-.It Ar string No \*(Gt Ar string
-First string operand is greater than second string operand.
-.It Ar string No \*(Lt Ar string
-First string operand is less than second string operand.
-.It Ar string No != Ar string
-Strings are not equal.
-.It Ar number Fl eq Ar number
-Numbers compare equal.
-.It Ar number Fl ne Ar number
-Numbers compare not equal.
-.It Ar number Fl ge Ar number
-Numbers compare greater than or equal.
-.It Ar number Fl gt Ar number
-Numbers compare greater than.
-.It Ar number Fl le Ar number
-Numbers compare less than or equal.
-.It Ar number Fl \&lt Ar number
-Numbers compare less than.
-.El
-.Pp
-The above basic expressions, in which unary operators have precedence over
-binary operators, may be combined with the following operators (listed in
-increasing order of precedence):
-.Bd -literal -offset indent
-expr \-o expr		Logical OR.
-expr \-a expr		Logical AND.
-! expr			Logical NOT.
-( expr )		Grouping.
-.Ed
-.Pp
-Note that a number actually may be an arithmetic expression, such as
-a mathematical term or the name of an integer variable:
-.Bd -literal -offset indent
-x=1; [ "x" \-eq 1 ]	evaluates to true
-.Ed
-.Pp
-Note that some special rules are applied (courtesy of
-.Px
-) if the number of arguments to
-.Ic test
-or inside the brackets
-.Ic \&[ ... \&]
-is less than five: if leading
-.Ql \&!
-arguments can be stripped such that only one to three arguments remain,
-then the lowered comparison is executed; (thanks to XSI) parentheses
-.Ic \e( ... \e)
-lower four- and three-argument forms to two- and one-argument forms,
-respectively; three-argument forms ultimately prefer binary operations,
-followed by negation and parenthesis lowering; two- and four-argument forms
-prefer negation followed by parenthesis; the one-argument form always implies
-.Fl n .
-.Pp
-.Sy Note :
-A common mistake is to use
-.Dq if \&[ $foo = bar \&]
-which fails if parameter
-.Dq foo
-is
-.Dv NULL
-or unset, if it has embedded spaces (i.e.\&
-.Ev IFS
-octets), or if it is a unary operator like
-.Sq \&!
-or
-.Sq Fl n .
-Use tests like
-.Dq if \&[ x\&"$foo\&" = x"bar" \&]
-instead, or the double-bracket operator
-.Dq if \&[[ $foo = bar \&]]
-or, to avoid pattern matching (see
-.Ic \&[[
-above):
-.Dq if \&[[ $foo = \&"$bar" \&]]
-.Pp
-The
-.Ic \&[[ ... ]]
-construct is not only more secure to use but also often faster.
-.Pp
-.It Xo
-.Ic time
-.Op Fl p
-.Op Ar pipeline
-.Xc
-If a
-.Ar pipeline
-is given, the times used to execute the pipeline are reported.
-If no pipeline
-is given, then the user and system time used by the shell itself, and all the
-commands it has run since it was started, are reported.
-The times reported are the real time (elapsed time from start to finish),
-the user CPU time (time spent running in user mode), and the system CPU time
-(time spent running in kernel mode).
-Times are reported to standard error; the format of the output is:
-.Pp
-.Dl "0m0.00s real     0m0.00s user     0m0.00s system"
-.Pp
-If the
-.Fl p
-option is given the output is slightly longer:
-.Bd -literal -offset indent
-real     0.00
-user     0.00
-sys      0.00
-.Ed
-.Pp
-It is an error to specify the
-.Fl p
-option unless
-.Ar pipeline
-is a simple command.
-.Pp
-Simple redirections of standard error do not affect the output of the
-.Ic time
-command:
-.Pp
-.Dl $ time sleep 1 2\*(Gtafile
-.Dl $ { time sleep 1; } 2\*(Gtafile
-.Pp
-Times for the first command do not go to
-.Dq afile ,
-but those of the second command do.
-.Pp
-.It Ic times
-Print the accumulated user and system times used both by the shell
-and by processes that the shell started which have exited.
-The format of the output is:
-.Bd -literal -offset indent
-0m0.00s 0m0.00s
-0m0.00s 0m0.00s
-.Ed
-.Pp
-.It Ic trap Op Ar handler signal ...
-Sets a trap handler that is to be executed when any of the specified signals are
-received.
-.Ar handler
-is either a
-.Dv NULL
-string, indicating the signals are to be ignored, a minus sign
-.Pq Sq \- ,
-indicating that the default action is to be taken for the signals (see
-.Xr signal 3 ) ,
-or a string containing shell commands to be evaluated and executed at the first
-opportunity (i.e. when the current command completes, or before printing the
-next
-.Ev PS1
-prompt) after receipt of one of the signals.
-.Ar signal
-is the name of a signal (e.g.\&
-.Dv PIPE
-or
-.Dv ALRM )
-or the number of the signal (see the
-.Ic kill \-l
-command above).
-.Pp
-There are two special signals:
-.Dv EXIT
-(also known as 0) which is executed when the shell is about to exit, and
-.Dv ERR ,
-which is executed after an error occurs (an error is something that would cause
-the shell to exit if the
-.Fl e
-or
-.Ic errexit
-option were set \*(en see the
-.Ic set
-command above).
-.Dv EXIT
-handlers are executed in the environment of the last executed command.
-Note
-that for non-interactive shells, the trap handler cannot be changed for signals
-that were ignored when the shell started.
-.Pp
-With no arguments,
-.Ic trap
-lists, as a series of
-.Ic trap
-commands, the current state of the traps that have been set since the shell
-started.
-Note that the output of
-.Ic trap
-cannot be usefully piped to another process (an artifact of the fact that
-traps are cleared when subprocesses are created).
-.Pp
-The original Korn shell's
-.Dv DEBUG
-trap and the handling of
-.Dv ERR
-and
-.Dv EXIT
-traps in functions are not yet implemented.
-.Pp
-.It Ic true
-A command that exits with a zero value.
-.Pp
-.It Xo
-.Ic global
-.Oo Op Ic +\-alpnrtUux
-.Op Fl L Ns Op Ar n
-.Op Fl R Ns Op Ar n
-.Op Fl Z Ns Op Ar n
-.Op Fl i Ns Op Ar n
-.No \*(Ba Fl f Op Fl tux Oc
-.Oo Ar name
-.Op Ns = Ns Ar value
-.Ar ... Oc
-.Xc
-.It Xo
-.Ic typeset
-.Oo Op Ic +\-alpnrtUux
-.Op Fl LRZ Ns Op Ar n
-.Op Fl i Ns Op Ar n
-.No \*(Ba Fl f Op Fl tux Oc
-.Oo Ar name
-.Op Ns = Ns Ar value
-.Ar ... Oc
-.Xc
-Display or set parameter attributes.
-With no
-.Ar name
-arguments, parameter attributes are displayed; if no options are used, the
-current attributes of all parameters are printed as
-.Ic typeset
-commands; if an option is given (or
-.Ql \-
-with no option letter), all parameters and their values with the specified
-attributes are printed; if options are introduced with
-.Ql + ,
-parameter values are not printed.
-.Pp
-If
-.Ar name
-arguments are given, the attributes of the named parameters are set
-.Pq Ic \-
-or cleared
-.Pq Ic + .
-Values for parameters may optionally be specified.
-For
-.Ar name Ns \&[*] ,
-the change affects the entire array, and no value may be specified.
-.Pp
-If
-.Ic typeset
-is used inside a function, any parameters specified are localised.
-This is not done by the otherwise identical
-.Ic global .
-.Em Note :
-This means that
-.Nm No 's Ic global
-command is
-.Em not
-equivalent to other programming languages' as it does not allow a
-function called from another function to access a parameter at truly
-global scope, but only prevents putting an accessed one into local scope.
-.Pp
-When
-.Fl f
-is used,
-.Ic typeset
-operates on the attributes of functions.
-As with parameters, if no
-.Ar name
-arguments are given,
-functions are listed with their values (i.e. definitions) unless
-options are introduced with
-.Ql + ,
-in which case only the function names are reported.
-.Bl -tag -width Ds
-.It Fl a
-Indexed array attribute.
-.It Fl f
-Function mode.
-Display or set functions and their attributes, instead of parameters.
-.It Fl i Ns Op Ar n
-Integer attribute.
-.Ar n
-specifies the base to use when displaying the integer (if not specified, the
-base given in the first assignment is used).
-Parameters with this attribute may
-be assigned values containing arithmetic expressions.
-.It Fl L Ns Op Ar n
-Left justify attribute.
-.Ar n
-specifies the field width.
-If
-.Ar n
-is not specified, the current width of a parameter (or the width of its first
-assigned value) is used.
-Leading whitespace (and zeros, if used with the
-.Fl Z
-option) is stripped.
-If necessary, values are either truncated or space padded
-to fit the field width.
-.It Fl l
-Lower case attribute.
-All upper case characters in values are converted to lower case.
-(In the original Korn shell, this parameter meant
-.Dq long integer
-when used with the
-.Fl i
-option.)
-.It Fl n
-Create a bound variable (name reference): any access to the variable
-.Ar name
-will access the variable
-.Ar value
-in the current scope (this is different from
-.At
-.Nm ksh93 ! )
-instead.
-Also different from
-.At
-.Nm ksh93
-is that
-.Ar value
-is lazily evaluated at the time
-.Ar name
-is accessed.
-This can be used by functions to access variables whose names are
-passed as parametres, instead of using
-.Ic eval .
-.It Fl p
-Print complete
-.Ic typeset
-commands that can be used to re-create the attributes and values of
-parameters.
-.It Fl R Ns Op Ar n
-Right justify attribute.
-.Ar n
-specifies the field width.
-If
-.Ar n
-is not specified, the current width of a parameter (or the width of its first
-assigned value) is used.
-Trailing whitespace is stripped.
-If necessary, values are either stripped of leading characters or space
-padded to make them fit the field width.
-.It Fl r
-Read-only attribute.
-Parameters with this attribute may not be assigned to or unset.
-Once this attribute is set, it cannot be turned off.
-.It Fl t
-Tag attribute.
-Has no meaning to the shell; provided for application use.
-.Pp
-For functions,
-.Fl t
-is the trace attribute.
-When functions with the trace attribute are executed, the
-.Ic xtrace
-.Pq Fl x
-shell option is temporarily turned on.
-.It Fl U
-Unsigned integer attribute.
-Integers are printed as unsigned values (combine with the
-.Fl i
-option).
-This option is not in the original Korn shell.
-.It Fl u
-Upper case attribute.
-All lower case characters in values are converted to upper case.
-(In the original Korn shell, this parameter meant
-.Dq unsigned integer
-when used with the
-.Fl i
-option which meant upper case letters would never be used for bases greater
-than 10.
-See the
-.Fl U
-option.)
-.Pp
-For functions,
-.Fl u
-is the undefined attribute.
-See
-.Sx Functions
-above for the implications of this.
-.It Fl x
-Export attribute.
-Parameters (or functions) are placed in the environment of
-any executed commands.
-Exported functions are not yet implemented.
-.It Fl Z Ns Op Ar n
-Zero fill attribute.
-If not combined with
-.Fl L ,
-this is the same as
-.Fl R ,
-except zero padding is used instead of space padding.
-For integers, the number instead of the base is padded.
-.El
-.Pp
-If any of the
-.\" long integer ,
-.Fl i ,
-.Fl L ,
-.Fl l ,
-.Fl R ,
-.Fl U ,
-.Fl u ,
-or
-.Fl Z
-options are changed, all others from this set are cleared,
-unless they are also given on the same command line.
-.Pp
-.It Xo
-.Ic ulimit
-.Op Fl aBCcdefHiLlMmnOPpqrSsTtVvw
-.Op Ar value
-.Xc
-Display or set process limits.
-If no options are used, the file size limit
-.Pq Fl f
-is assumed.
-.Ar value ,
-if specified, may be either an arithmetic expression or the word
-.Dq unlimited .
-The limits affect the shell and any processes created by the shell after a
-limit is imposed.
-Note that some systems may not allow limits to be increased
-once they are set.
-Also note that the types of limits available are system
-dependent \*(en some systems have only the
-.Fl f
-limit.
-.Bl -tag -width 5n
-.It Fl a
-Display all limits; unless
-.Fl H
-is used, soft limits are displayed.
-.It Fl B Ar n
-Set the socket buffer size to
-.Ar n
-kibibytes.
-.It Fl C Ar n
-Set the number of cached threads to
-.Ar n .
-.It Fl c Ar n
-Impose a size limit of
-.Ar n
-blocks on the size of core dumps.
-.It Fl d Ar n
-Impose a size limit of
-.Ar n
-kibibytes on the size of the data area.
-.It Fl e Ar n
-Set the maximum niceness to
-.Ar n .
-.It Fl f Ar n
-Impose a size limit of
-.Ar n
-blocks on files written by the shell and its child processes (files of any
-size may be read).
-.It Fl H
-Set the hard limit only (the default is to set both hard and soft limits).
-.It Fl i Ar n
-Set the number of pending signals to
-.Ar n .
-.It Fl L Ar n
-Control flocks; documentation is missing.
-.It Fl l Ar n
-Impose a limit of
-.Ar n
-kibibytes on the amount of locked (wired) physical memory.
-.It Fl M Ar n
-Set the AIO locked memory to
-.Ar n
-kibibytes.
-.It Fl m Ar n
-Impose a limit of
-.Ar n
-kibibytes on the amount of physical memory used.
-.It Fl n Ar n
-Impose a limit of
-.Ar n
-file descriptors that can be open at once.
-.It Fl O Ar n
-Set the number of AIO operations to
-.Ar n .
-.It Fl P Ar n
-Limit the number of threads per process to
-.Ar n .
-.It Fl p Ar n
-Impose a limit of
-.Ar n
-processes that can be run by the user at any one time.
-.It Fl q Ar n
-Limit the size of
-.Tn POSIX
-message queues to
-.Ar n
-bytes.
-.It Fl r Ar n
-Set the maximum real-time priority to
-.Ar n .
-.It Fl S
-Set the soft limit only (the default is to set both hard and soft limits).
-.It Fl s Ar n
-Impose a size limit of
-.Ar n
-kibibytes on the size of the stack area.
-.It Fl T Ar n
-Impose a time limit of
-.Ar n
-real seconds to be used by each process.
-.It Fl t Ar n
-Impose a time limit of
-.Ar n
-CPU seconds spent in user mode to be used by each process.
-.It Fl V Ar n
-Set the number of vnode monitors on Haiku to
-.Ar n .
-.It Fl v Ar n
-Impose a limit of
-.Ar n
-kibibytes on the amount of virtual memory (address space) used.
-.It Fl w Ar n
-Impose a limit of
-.Ar n
-kibibytes on the amount of swap space used.
-.El
-.Pp
-As far as
-.Ic ulimit
-is concerned, a block is 512 bytes.
-.Pp
-.It Xo
-.Ic umask
-.Op Fl S
-.Op Ar mask
-.Xc
-Display or set the file permission creation mask, or umask (see
-.Xr umask 2 ) .
-If the
-.Fl S
-option is used, the mask displayed or set is symbolic; otherwise, it is an
-octal number.
-.Pp
-Symbolic masks are like those used by
-.Xr chmod 1 .
-When used, they describe what permissions may be made available (as opposed to
-octal masks in which a set bit means the corresponding bit is to be cleared).
-For example,
-.Dq ug=rwx,o=
-sets the mask so files will not be readable, writable, or executable by
-.Dq others ,
-and is equivalent (on most systems) to the octal mask
-.Dq 007 .
-.Pp
-.It Xo
-.Ic unalias
-.Op Fl adt
-.Op Ar name ...
-.Xc
-The aliases for the given names are removed.
-If the
-.Fl a
-option is used, all aliases are removed.
-If the
-.Fl t
-or
-.Fl d
-options are used, the indicated operations are carried out on tracked or
-directory aliases, respectively.
-.Pp
-.It Xo
-.Ic unset
-.Op Fl fv
-.Ar parameter ...
-.Xc
-Unset the named parameters
-.Po
-.Fl v ,
-the default
-.Pc
-or functions
-.Pq Fl f .
-With
-.Ar parameter Ns \&[*] ,
-attributes are kept, only values are unset.
-.Pp
-The exit status is non-zero if any of the parameters have the read-only
-attribute set, zero otherwise.
-.Pp
-.It Ic wait Op Ar job ...
-Wait for the specified job(s) to finish.
-The exit status of
-.Ic wait
-is that of the last specified job; if the last job is killed by a signal, the
-exit status is 128 + the number of the signal (see
-.Ic kill \-l Ar exit-status
-above); if the last specified job can't be found (because it never existed, or
-had already finished), the exit status of
-.Ic wait
-is 127.
-See
-.Sx Job control
-below for the format of
-.Ar job .
-.Ic wait
-will return if a signal for which a trap has been set is received, or if a
-.Dv SIGHUP ,
-.Dv SIGINT ,
-or
-.Dv SIGQUIT
-signal is received.
-.Pp
-If no jobs are specified,
-.Ic wait
-waits for all currently running jobs (if any) to finish and exits with a zero
-status.
-If job monitoring is enabled, the completion status of jobs is printed
-(this is not the case when jobs are explicitly specified).
-.Pp
-.It Xo
-.Ic whence
-.Op Fl pv
-.Op Ar name ...
-.Xc
-For each
-.Ar name ,
-the type of command is listed (reserved word, built-in, alias,
-function, tracked alias, or executable).
-If the
-.Fl p
-option is used, a path search is performed even if
-.Ar name
-is a reserved word, alias, etc.
-Without the
-.Fl v
-option,
-.Ic whence
-is similar to
-.Ic command Fl v
-except that
-.Ic whence
-will find reserved words and won't print aliases as alias commands.
-With the
-.Fl v
-option,
-.Ic whence
-is the same as
-.Ic command Fl V .
-Note that for
-.Ic whence ,
-the
-.Fl p
-option does not affect the search path used, as it does for
-.Ic command .
-If the type of one or more of the names could not be determined, the exit
-status is non-zero.
-.El
-.Ss Job control
-Job control refers to the shell's ability to monitor and control jobs which
-are processes or groups of processes created for commands or pipelines.
-At a minimum, the shell keeps track of the status of the background (i.e.\&
-asynchronous) jobs that currently exist; this information can be displayed
-using the
-.Ic jobs
-commands.
-If job control is fully enabled (using
-.Ic set \-m
-or
-.Ic set \-o monitor ) ,
-as it is for interactive shells, the processes of a job are placed in their
-own process group.
-Foreground jobs can be stopped by typing the suspend
-character from the terminal (normally \*(haZ), jobs can be restarted in either the
-foreground or background using the
-.Ic fg
-and
-.Ic bg
-commands, and the state of the terminal is saved or restored when a foreground
-job is stopped or restarted, respectively.
-.Pp
-Note that only commands that create processes (e.g. asynchronous commands,
-subshell commands, and non-built-in, non-function commands) can be stopped;
-commands like
-.Ic read
-cannot be.
-.Pp
-When a job is created, it is assigned a job number.
-For interactive shells, this number is printed inside
-.Dq \&[..] ,
-followed by the process IDs of the processes in the job when an asynchronous
-command is run.
-A job may be referred to in the
-.Ic bg ,
-.Ic fg ,
-.Ic jobs ,
-.Ic kill ,
-and
-.Ic wait
-commands either by the process ID of the last process in the command pipeline
-(as stored in the
-.Ic $!\&
-parameter) or by prefixing the job number with a percent
-sign
-.Pq Sq % .
-Other percent sequences can also be used to refer to jobs:
-.Bl -tag -width "%+ x %% x %XX"
-.It %+ \*(Ba %% \*(Ba %
-The most recently stopped job, or, if there are no stopped jobs, the oldest
-running job.
-.It %\-
-The job that would be the
-.Ic %+
-job if the latter did not exist.
-.It % Ns Ar n
-The job with job number
-.Ar n .
-.It %? Ns Ar string
-The job with its command containing the string
-.Ar string
-(an error occurs if multiple jobs are matched).
-.It % Ns Ar string
-The job with its command starting with the string
-.Ar string
-(an error occurs if multiple jobs are matched).
-.El
-.Pp
-When a job changes state (e.g. a background job finishes or foreground job is
-stopped), the shell prints the following status information:
-.Pp
-.D1 [ Ns Ar number ] Ar flag status command
-.Pp
-where...
-.Bl -tag -width "command"
-.It Ar number
-is the job number of the job;
-.It Ar flag
-is the
-.Ql +
-or
-.Ql \-
-character if the job is the
-.Ic %+
-or
-.Ic %\-
-job, respectively, or space if it is neither;
-.It Ar status
-indicates the current state of the job and can be:
-.Bl -tag -width "RunningXX"
-.It Done Op Ar number
-The job exited.
-.Ar number
-is the exit status of the job which is omitted if the status is zero.
-.It Running
-The job has neither stopped nor exited (note that running does not necessarily
-mean consuming CPU time \*(en
-the process could be blocked waiting for some event).
-.It Stopped Op Ar signal
-The job was stopped by the indicated
-.Ar signal
-(if no signal is given, the job was stopped by
-.Dv SIGTSTP ) .
-.It Ar signal-description Op Dq core dumped
-The job was killed by a signal (e.g. memory fault, hangup); use
-.Ic kill \-l
-for a list of signal descriptions.
-The
-.Dq core dumped
-message indicates the process created a core file.
-.El
-.It Ar command
-is the command that created the process.
-If there are multiple processes in
-the job, each process will have a line showing its
-.Ar command
-and possibly its
-.Ar status ,
-if it is different from the status of the previous process.
-.El
-.Pp
-When an attempt is made to exit the shell while there are jobs in the stopped
-state, the shell warns the user that there are stopped jobs and does not exit.
-If another attempt is immediately made to exit the shell, the stopped jobs are
-sent a
-.Dv SIGHUP
-signal and the shell exits.
-Similarly, if the
-.Ic nohup
-option is not set and there are running jobs when an attempt is made to exit
-a login shell, the shell warns the user and does not exit.
-If another attempt
-is immediately made to exit the shell, the running jobs are sent a
-.Dv SIGHUP
-signal and the shell exits.
-.Ss Interactive input line editing
-The shell supports three modes of reading command lines from a
-.Xr tty 4
-in an interactive session, controlled by the
-.Ic emacs ,
-.Ic gmacs ,
-and
-.Ic vi
-options (at most one of these can be set at once).
-The default is
-.Ic emacs .
-Editing modes can be set explicitly using the
-.Ic set
-built-in.
-If none of these options are enabled,
-the shell simply reads lines using the normal
-.Xr tty 4
-driver.
-If the
-.Ic emacs
-or
-.Ic gmacs
-option is set, the shell allows emacs-like editing of the command; similarly,
-if the
-.Ic vi
-option is set, the shell allows vi-like editing of the command.
-These modes are described in detail in the following sections.
-.Pp
-In these editing modes, if a line is longer than the screen width (see the
-.Ev COLUMNS
-parameter),
-a
-.Ql \*(Gt ,
-.Ql + ,
-or
-.Ql \*(Lt
-character is displayed in the last column indicating that there are more
-characters after, before and after, or before the current position,
-respectively.
-The line is scrolled horizontally as necessary.
-.Pp
-Completed lines are pushed into the history, unless they begin with an
-IFS octet or IFS white space, or are the same as the previous line.
-.Ss Emacs editing mode
-When the
-.Ic emacs
-option is set, interactive input line editing is enabled.
-Warning: This mode is
-slightly different from the emacs mode in the original Korn shell.
-In this mode, various editing commands
-(typically bound to one or more control characters) cause immediate actions
-without waiting for a newline.
-Several editing commands are bound to particular
-control characters when the shell is invoked; these bindings can be changed
-using the
-.Ic bind
-command.
-.Pp
-The following is a list of available editing commands.
-Each description starts with the name of the command,
-suffixed with a colon;
-an
-.Op Ar n
-(if the command can be prefixed with a count); and any keys the command is
-bound to by default, written using caret notation
-e.g. the ASCII ESC character is written as \*(ha[.
-These control sequences are not case sensitive.
-A count prefix for a command is entered using the sequence
-.Pf \*(ha[ Ns Ar n ,
-where
-.Ar n
-is a sequence of 1 or more digits.
-Unless otherwise specified, if a count is
-omitted, it defaults to 1.
-.Pp
-Note that editing command names are used only with the
-.Ic bind
-command.
-Furthermore, many editing commands are useful only on terminals with
-a visible cursor.
-The default bindings were chosen to resemble corresponding
-Emacs key bindings.
-The user's
-.Xr tty 4
-characters (e.g.\&
-.Dv ERASE )
-are bound to
-reasonable substitutes and override the default bindings.
-.Bl -tag -width Ds
-.It abort: \*(haC, \*(haG
-Abort the current command, empty the line buffer and
-set the exit state to interrupted.
-.It auto\-insert: Op Ar n
-Simply causes the character to appear as literal input.
-Most ordinary characters are bound to this.
-.It Xo backward\-char:
-.Op Ar n
-.No \*(haB , \*(haXD , ANSI-CurLeft
-.Xc
-Moves the cursor backward
-.Ar n
-characters.
-.It Xo backward\-word:
-.Op Ar n
-.No \*(ha[b , ANSI-Ctrl-CurLeft , ANSI-Alt-CurLeft
-.Xc
-Moves the cursor backward to the beginning of the word; words consist of
-alphanumerics, underscore
-.Pq Sq _ ,
-and dollar sign
-.Pq Sq $
-characters.
-.It beginning\-of\-history: \*(ha[\*(Lt
-Moves to the beginning of the history.
-.It beginning\-of\-line: \*(haA, ANSI-Home
-Moves the cursor to the beginning of the edited input line.
-.It Xo capitalise\-word:
-.Op Ar n
-.No \*(ha[C , \*(ha[c
-.Xc
-Uppercase the first character in the next
-.Ar n
-words, leaving the cursor past the end of the last word.
-.It clear\-screen: \*(ha[\*(haL
-Prints a compile-time configurable sequence to clear the screen and home
-the cursor, redraws the entire prompt and the currently edited input line.
-The default sequence works for almost all standard terminals.
-.It comment: \*(ha[#
-If the current line does not begin with a comment character, one is added at
-the beginning of the line and the line is entered (as if return had been
-pressed); otherwise, the existing comment characters are removed and the cursor
-is placed at the beginning of the line.
-.It complete: \*(ha[\*(ha[
-Automatically completes as much as is unique of the command name or the file
-name containing the cursor.
-If the entire remaining command or file name is
-unique, a space is printed after its completion, unless it is a directory name
-in which case
-.Ql /
-is appended.
-If there is no command or file name with the current partial word
-as its prefix, a bell character is output (usually causing a beep to be
-sounded).
-.It complete\-command: \*(haX\*(ha[
-Automatically completes as much as is unique of the command name having the
-partial word up to the cursor as its prefix, as in the
-.Ic complete
-command above.
-.It complete\-file: \*(ha[\*(haX
-Automatically completes as much as is unique of the file name having the
-partial word up to the cursor as its prefix, as in the
-.Ic complete
-command described above.
-.It complete\-list: \*(haI, \*(ha[=
-Complete as much as is possible of the current word,
-and list the possible completions for it.
-If only one completion is possible,
-match as in the
-.Ic complete
-command above.
-Note that \*(haI is usually generated by the TAB (tabulator) key.
-.It Xo delete\-char\-backward:
-.Op Ar n
-.No ERASE , \*(ha? , \*(haH
-.Xc
-Deletes
-.Ar n
-characters before the cursor.
-.It Xo delete\-char\-forward:
-.Op Ar n
-.No ANSI-Del
-.Xc
-Deletes
-.Ar n
-characters after the cursor.
-.It Xo delete\-word\-backward:
-.Op Ar n
-.No WERASE , \*(ha[\*(ha? , \*(ha[\*(haH , \*(ha[h
-.Xc
-Deletes
-.Ar n
-words before the cursor.
-.It Xo delete\-word\-forward:
-.Op Ar n
-.No \*(ha[d
-.Xc
-Deletes characters after the cursor up to the end of
-.Ar n
-words.
-.It Xo down\-history:
-.Op Ar n
-.No \*(haN , \*(haXB , ANSI-CurDown
-.Xc
-Scrolls the history buffer forward
-.Ar n
-lines (later).
-Each input line originally starts just after the last entry
-in the history buffer, so
-.Ic down\-history
-is not useful until either
-.Ic search\-history ,
-.Ic search\-history\-up
-or
-.Ic up\-history
-has been performed.
-.It Xo downcase\-word:
-.Op Ar n
-.No \*(ha[L , \*(ha[l
-.Xc
-Lowercases the next
-.Ar n
-words.
-.It Xo edit\-line:
-.Op Ar n
-.No \*(haXe
-.Xc
-Edit line
-.Ar n
-or the current line, if not specified, interactively.
-The actual command executed is
-.Ic fc \-e ${VISUAL:\-${EDITOR:\-vi}} Ar n .
-.It end\-of\-history: \*(ha[\*(Gt
-Moves to the end of the history.
-.It end\-of\-line: \*(haE, ANSI-End
-Moves the cursor to the end of the input line.
-.It eot: \*(ha_
-Acts as an end-of-file; this is useful because edit-mode input disables
-normal terminal input canonicalization.
-.It Xo eot\-or\-delete:
-.Op Ar n
-.No \*(haD
-.Xc
-Acts as
-.Ic eot
-if alone on a line; otherwise acts as
-.Ic delete\-char\-forward .
-.It error: (not bound)
-Error (ring the bell).
-.It exchange\-point\-and\-mark: \*(haX\*(haX
-Places the cursor where the mark is and sets the mark to where the cursor was.
-.It expand\-file: \*(ha[*
-Appends a
-.Ql *
-to the current word and replaces the word with the result of performing file
-globbing on the word.
-If no files match the pattern, the bell is rung.
-.It Xo forward\-char:
-.Op Ar n
-.No \*(haF , \*(haXC , ANSI-CurRight
-.Xc
-Moves the cursor forward
-.Ar n
-characters.
-.It Xo forward\-word:
-.Op Ar n
-.No \*(ha[f , ANSI-Ctrl-CurRight , ANSI-Alt-CurRight
-.Xc
-Moves the cursor forward to the end of the
-.Ar n Ns th
-word.
-.It Xo goto\-history:
-.Op Ar n
-.No \*(ha[g
-.Xc
-Goes to history number
-.Ar n .
-.It kill\-line: KILL
-Deletes the entire input line.
-.It kill\-region: \*(haW
-Deletes the input between the cursor and the mark.
-.It Xo kill\-to\-eol:
-.Op Ar n
-.No \*(haK
-.Xc
-Deletes the input from the cursor to the end of the line if
-.Ar n
-is not specified; otherwise deletes characters between the cursor and column
-.Ar n .
-.It list: \*(ha[?
-Prints a sorted, columnated list of command names or file names (if any) that
-can complete the partial word containing the cursor.
-Directory names have
-.Ql /
-appended to them.
-.It list\-command: \*(haX?
-Prints a sorted, columnated list of command names (if any) that can complete
-the partial word containing the cursor.
-.It list\-file: \*(haX\*(haY
-Prints a sorted, columnated list of file names (if any) that can complete the
-partial word containing the cursor.
-File type indicators are appended as described under
-.Ic list
-above.
-.It newline: \*(haJ , \*(haM
-Causes the current input line to be processed by the shell.
-The current cursor position may be anywhere on the line.
-.It newline\-and\-next: \*(haO
-Causes the current input line to be processed by the shell, and the next line
-from history becomes the current line.
-This is only useful after an
-.Ic up\-history ,
-.Ic search\-history
-or
-.Ic search\-history\-up .
-.It no\-op: QUIT
-This does nothing.
-.It prefix\-1: \*(ha[
-Introduces a 2-character command sequence.
-.It prefix\-2: \*(haX , \*(ha[[ , \*(ha[O
-Introduces a 2-character command sequence.
-.It Xo prev\-hist\-word:
-.Op Ar n
-.No \*(ha[. , \*(ha[_
-.Xc
-The last word, or, if given, the
-.Ar n Ns th
-word (zero-based) of the previous (on repeated execution, second-last,
-third-last, etc.) command is inserted at the cursor.
-Use of this editing command trashes the mark.
-.It quote: \*(ha\*(ha , \*(haV
-The following character is taken literally rather than as an editing command.
-.It redraw: \*(haL
-Reprints the last line of the prompt string and the current input line
-on a new line.
-.It Xo search\-character\-backward:
-.Op Ar n
-.No \*(ha[\*(ha]
-.Xc
-Search backward in the current line for the
-.Ar n Ns th
-occurrence of the next character typed.
-.It Xo search\-character\-forward:
-.Op Ar n
-.No \*(ha]
-.Xc
-Search forward in the current line for the
-.Ar n Ns th
-occurrence of the next character typed.
-.It search\-history: \*(haR
-Enter incremental search mode.
-The internal history list is searched
-backwards for commands matching the input.
-An initial
-.Ql \*(ha
-in the search string anchors the search.
-The escape key will leave search mode.
-Other commands, including sequences of escape as
-.Ic prefix\-1
-followed by a
-.Ic prefix\-1
-or
-.Ic prefix\-2
-key will be executed after leaving search mode.
-The
-.Ic abort Pq \*(haG
-command will restore the input line before search started.
-Successive
-.Ic search\-history
-commands continue searching backward to the next previous occurrence of the
-pattern.
-The history buffer retains only a finite number of lines; the oldest
-are discarded as necessary.
-.It search\-history\-up: ANSI-PgUp
-Search backwards through the history buffer for commands whose beginning match
-the portion of the input line before the cursor.
-When used on an empty line, this has the same effect as
-.Ic up\-history .
-.It search\-history\-down: ANSI-PgDn
-Search forwards through the history buffer for commands whose beginning match
-the portion of the input line before the cursor.
-When used on an empty line, this has the same effect as
-.Ic down\-history .
-This is only useful after an
-.Ic up\-history ,
-.Ic search\-history
-or
-.Ic search\-history\-up .
-.It set\-mark\-command: \*(ha[ Ns Aq space
-Set the mark at the cursor position.
-.It transpose\-chars: \*(haT
-If at the end of line, or if the
-.Ic gmacs
-option is set, this exchanges the two previous characters; otherwise, it
-exchanges the previous and current characters and moves the cursor one
-character to the right.
-.It Xo up\-history:
-.Op Ar n
-.No \*(haP , \*(haXA , ANSI-CurUp
-.Xc
-Scrolls the history buffer backward
-.Ar n
-lines (earlier).
-.It Xo upcase\-word:
-.Op Ar n
-.No \*(ha[U , \*(ha[u
-.Xc
-Uppercase the next
-.Ar n
-words.
-.It version: \*(ha[\*(haV
-Display the version of
-.Nm mksh .
-The current edit buffer is restored as soon as a key is pressed.
-The restoring keypress is processed, unless it is a space.
-.It yank: \*(haY
-Inserts the most recently killed text string at the current cursor position.
-.It yank\-pop: \*(ha[y
-Immediately after a
-.Ic yank ,
-replaces the inserted text string with the next previously killed text string.
-.El
-.Ss Vi editing mode
-.Em Note:
-The vi command-line editing mode is orphaned, yet still functional.
-.Pp
-The vi command-line editor in
-.Nm
-has basically the same commands as the
-.Xr vi 1
-editor with the following exceptions:
-.Bl -bullet
-.It
-You start out in insert mode.
-.It
-There are file name and command completion commands:
-=, \e, *, \*(haX, \*(haE, \*(haF, and, optionally,
-.Aq tab
-and
-.Aq esc .
-.It
-The
-.Ic _
-command is different (in
-.Nm mksh ,
-it is the last argument command; in
-.Xr vi 1
-it goes to the start of the current line).
-.It
-The
-.Ic /
-and
-.Ic G
-commands move in the opposite direction to the
-.Ic j
-command.
-.It
-Commands which don't make sense in a single line editor are not available
-(e.g. screen movement commands and
-.Xr ex 1 Ns -style
-colon
-.Pq Ic \&:
-commands).
-.El
-.Pp
-Like
-.Xr vi 1 ,
-there are two modes:
-.Dq insert
-mode and
-.Dq command
-mode.
-In insert mode, most characters are simply put in the buffer at the
-current cursor position as they are typed; however, some characters are
-treated specially.
-In particular, the following characters are taken from current
-.Xr tty 4
-settings
-(see
-.Xr stty 1 )
-and have their usual meaning (normal values are in parentheses): kill (\*(haU),
-erase (\*(ha?), werase (\*(haW), eof (\*(haD), intr (\*(haC), and quit (\*(ha\e).
-In addition to
-the above, the following characters are also treated specially in insert mode:
-.Bl -tag -width XJXXXXM
-.It \*(haE
-Command and file name enumeration (see below).
-.It \*(haF
-Command and file name completion (see below).
-If used twice in a row, the
-list of possible completions is displayed; if used a third time, the completion
-is undone.
-.It \*(haH
-Erases previous character.
-.It \*(haJ \*(Ba \*(haM
-End of line.
-The current line is read, parsed, and executed by the shell.
-.It \*(haV
-Literal next.
-The next character typed is not treated specially (can be used
-to insert the characters being described here).
-.It \*(haX
-Command and file name expansion (see below).
-.It Aq esc
-Puts the editor in command mode (see below).
-.It Aq tab
-Optional file name and command completion (see
-.Ic \*(haF
-above), enabled with
-.Ic set \-o vi\-tabcomplete .
-.El
-.Pp
-In command mode, each character is interpreted as a command.
-Characters that
-don't correspond to commands, are illegal combinations of commands, or are
-commands that can't be carried out, all cause beeps.
-In the following command descriptions, an
-.Op Ar n
-indicates the command may be prefixed by a number (e.g.\&
-.Ic 10l
-moves right 10 characters); if no number prefix is used,
-.Ar n
-is assumed to be 1 unless otherwise specified.
-The term
-.Dq current position
-refers to the position between the cursor and the character preceding the
-cursor.
-A
-.Dq word
-is a sequence of letters, digits, and underscore characters or a sequence of
-non-letter, non-digit, non-underscore, and non-whitespace characters (e.g.\&
-.Dq ab2*&\*(ha
-contains two words) and a
-.Dq big-word
-is a sequence of non-whitespace characters.
-.Pp
-Special
-.Nm
-vi commands:
-.Pp
-The following commands are not in, or are different from, the normal vi file
-editor:
-.Bl -tag -width 10n
-.It Xo
-.Oo Ar n Oc Ns _
-.Xc
-Insert a space followed by the
-.Ar n Ns th
-big-word from the last command in the history at the current position and enter
-insert mode; if
-.Ar n
-is not specified, the last word is inserted.
-.It #
-Insert the comment character
-.Pq Sq #
-at the start of the current line and return the line to the shell (equivalent
-to
-.Ic I#\*(haJ ) .
-.It Xo
-.Oo Ar n Oc Ns g
-.Xc
-Like
-.Ic G ,
-except if
-.Ar n
-is not specified, it goes to the most recent remembered line.
-.It Xo
-.Oo Ar n Oc Ns v
-.Xc
-Edit line
-.Ar n
-using the
-.Xr vi 1
-editor; if
-.Ar n
-is not specified, the current line is edited.
-The actual command executed is
-.Ic fc \-e ${VISUAL:\-${EDITOR:\-vi}} Ar n .
-.It * and \*(haX
-Command or file name expansion is applied to the current big-word (with an
-appended
-.Ql *
-if the word contains no file globbing characters) \*(en the big-word is replaced
-with the resulting words.
-If the current big-word is the first on the line
-or follows one of the characters
-.Ql \&; ,
-.Ql \*(Ba ,
-.Ql & ,
-.Ql \&( ,
-or
-.Ql \&) ,
-and does not contain a slash
-.Pq Sq / ,
-then command expansion is done; otherwise file name expansion is done.
-Command expansion will match the big-word against all aliases, functions, and
-built-in commands as well as any executable files found by searching the
-directories in the
-.Ev PATH
-parameter.
-File name expansion matches the big-word against the files in the
-current directory.
-After expansion, the cursor is placed just past the last
-word and the editor is in insert mode.
-.It Xo
-.Oo Ar n Oc Ns \e ,
-.Oo Ar n Oc Ns \*(haF ,
-.Oo Ar n Oc Ns Aq tab ,
-.No and
-.Oo Ar n Oc Ns Aq esc
-.Xc
-Command/file name completion.
-Replace the current big-word with the
-longest unique match obtained after performing command and file name expansion.
-.Aq tab
-is only recognised if the
-.Ic vi\-tabcomplete
-option is set, while
-.Aq esc
-is only recognised if the
-.Ic vi\-esccomplete
-option is set (see
-.Ic set \-o ) .
-If
-.Ar n
-is specified, the
-.Ar n Ns th
-possible completion is selected (as reported by the command/file name
-enumeration command).
-.It = and \*(haE
-Command/file name enumeration.
-List all the commands or files that match the current big-word.
-.It \*(haV
-Display the version of
-.Nm mksh .
-The current edit buffer is restored as soon as a key is pressed.
-The restoring keypress is ignored.
-.It @ Ns Ar c
-Macro expansion.
-Execute the commands found in the alias
-.Ar c .
-.El
-.Pp
-Intra-line movement commands:
-.Bl -tag -width Ds
-.It Xo
-.Oo Ar n Oc Ns h and
-.Oo Ar n Oc Ns \*(haH
-.Xc
-Move left
-.Ar n
-characters.
-.It Xo
-.Oo Ar n Oc Ns l and
-.Oo Ar n Oc Ns Aq space
-.Xc
-Move right
-.Ar n
-characters.
-.It 0
-Move to column 0.
-.It \*(ha
-Move to the first non-whitespace character.
-.It Xo
-.Oo Ar n Oc Ns \*(Ba
-.Xc
-Move to column
-.Ar n .
-.It $
-Move to the last character.
-.It Xo
-.Oo Ar n Oc Ns b
-.Xc
-Move back
-.Ar n
-words.
-.It Xo
-.Oo Ar n Oc Ns B
-.Xc
-Move back
-.Ar n
-big-words.
-.It Xo
-.Oo Ar n Oc Ns e
-.Xc
-Move forward to the end of the word,
-.Ar n
-times.
-.It Xo
-.Oo Ar n Oc Ns E
-.Xc
-Move forward to the end of the big-word,
-.Ar n
-times.
-.It Xo
-.Oo Ar n Oc Ns w
-.Xc
-Move forward
-.Ar n
-words.
-.It Xo
-.Oo Ar n Oc Ns W
-.Xc
-Move forward
-.Ar n
-big-words.
-.It %
-Find match.
-The editor looks forward for the nearest parenthesis, bracket, or
-brace and then moves the cursor to the matching parenthesis, bracket, or brace.
-.It Xo
-.Oo Ar n Oc Ns f Ns Ar c
-.Xc
-Move forward to the
-.Ar n Ns th
-occurrence of the character
-.Ar c .
-.It Xo
-.Oo Ar n Oc Ns F Ns Ar c
-.Xc
-Move backward to the
-.Ar n Ns th
-occurrence of the character
-.Ar c .
-.It Xo
-.Oo Ar n Oc Ns t Ns Ar c
-.Xc
-Move forward to just before the
-.Ar n Ns th
-occurrence of the character
-.Ar c .
-.It Xo
-.Oo Ar n Oc Ns T Ns Ar c
-.Xc
-Move backward to just before the
-.Ar n Ns th
-occurrence of the character
-.Ar c .
-.It Xo
-.Oo Ar n Oc Ns \&;
-.Xc
-Repeats the last
-.Ic f , F , t ,
-or
-.Ic T
-command.
-.It Xo
-.Oo Ar n Oc Ns \&,
-.Xc
-Repeats the last
-.Ic f , F , t ,
-or
-.Ic T
-command, but moves in the opposite direction.
-.El
-.Pp
-Inter-line movement commands:
-.Bl -tag -width Ds
-.It Xo
-.Oo Ar n Oc Ns j ,
-.Oo Ar n Oc Ns + ,
-.No and
-.Oo Ar n Oc Ns \*(haN
-.Xc
-Move to the
-.Ar n Ns th
-next line in the history.
-.It Xo
-.Oo Ar n Oc Ns k ,
-.Oo Ar n Oc Ns \- ,
-.No and
-.Oo Ar n Oc Ns \*(haP
-.Xc
-Move to the
-.Ar n Ns th
-previous line in the history.
-.It Xo
-.Oo Ar n Oc Ns G
-.Xc
-Move to line
-.Ar n
-in the history; if
-.Ar n
-is not specified, the number of the first remembered line is used.
-.It Xo
-.Oo Ar n Oc Ns g
-.Xc
-Like
-.Ic G ,
-except if
-.Ar n
-is not specified, it goes to the most recent remembered line.
-.It Xo
-.Oo Ar n Oc Ns / Ns Ar string
-.Xc
-Search backward through the history for the
-.Ar n Ns th
-line containing
-.Ar string ;
-if
-.Ar string
-starts with
-.Ql \*(ha ,
-the remainder of the string must appear at the start of the history line for
-it to match.
-.It Xo
-.Oo Ar n Oc Ns \&? Ns Ar string
-.Xc
-Same as
-.Ic / ,
-except it searches forward through the history.
-.It Xo
-.Oo Ar n Oc Ns n
-.Xc
-Search for the
-.Ar n Ns th
-occurrence of the last search string;
-the direction of the search is the same as the last search.
-.It Xo
-.Oo Ar n Oc Ns N
-.Xc
-Search for the
-.Ar n Ns th
-occurrence of the last search string;
-the direction of the search is the opposite of the last search.
-.It Ar ANSI-CurUp
-Take the characters from the beginning of the line to the current
-cursor position as search string and do a backwards history search
-for lines beginning with this string; keep the cursor position.
-This works only in insert mode and keeps it enabled.
-.El
-.Pp
-Edit commands
-.Bl -tag -width Ds
-.It Xo
-.Oo Ar n Oc Ns a
-.Xc
-Append text
-.Ar n
-times; goes into insert mode just after the current position.
-The append is
-only replicated if command mode is re-entered i.e.\&
-.Aq esc
-is used.
-.It Xo
-.Oo Ar n Oc Ns A
-.Xc
-Same as
-.Ic a ,
-except it appends at the end of the line.
-.It Xo
-.Oo Ar n Oc Ns i
-.Xc
-Insert text
-.Ar n
-times; goes into insert mode at the current position.
-The insertion is only
-replicated if command mode is re-entered i.e.\&
-.Aq esc
-is used.
-.It Xo
-.Oo Ar n Oc Ns I
-.Xc
-Same as
-.Ic i ,
-except the insertion is done just before the first non-blank character.
-.It Xo
-.Oo Ar n Oc Ns s
-.Xc
-Substitute the next
-.Ar n
-characters (i.e. delete the characters and go into insert mode).
-.It S
-Substitute whole line.
-All characters from the first non-blank character to the
-end of the line are deleted and insert mode is entered.
-.It Xo
-.Oo Ar n Oc Ns c Ns Ar move-cmd
-.Xc
-Change from the current position to the position resulting from
-.Ar n move-cmd Ns s
-(i.e. delete the indicated region and go into insert mode); if
-.Ar move-cmd
-is
-.Ic c ,
-the line starting from the first non-blank character is changed.
-.It C
-Change from the current position to the end of the line (i.e. delete to the
-end of the line and go into insert mode).
-.It Xo
-.Oo Ar n Oc Ns x
-.Xc
-Delete the next
-.Ar n
-characters.
-.It Xo
-.Oo Ar n Oc Ns X
-.Xc
-Delete the previous
-.Ar n
-characters.
-.It D
-Delete to the end of the line.
-.It Xo
-.Oo Ar n Oc Ns d Ns Ar move-cmd
-.Xc
-Delete from the current position to the position resulting from
-.Ar n move-cmd Ns s ;
-.Ar move-cmd
-is a movement command (see above) or
-.Ic d ,
-in which case the current line is deleted.
-.It Xo
-.Oo Ar n Oc Ns r Ns Ar c
-.Xc
-Replace the next
-.Ar n
-characters with the character
-.Ar c .
-.It Xo
-.Oo Ar n Oc Ns R
-.Xc
-Replace.
-Enter insert mode but overwrite existing characters instead of
-inserting before existing characters.
-The replacement is repeated
-.Ar n
-times.
-.It Xo
-.Oo Ar n Oc Ns \*(TI
-.Xc
-Change the case of the next
-.Ar n
-characters.
-.It Xo
-.Oo Ar n Oc Ns y Ns Ar move-cmd
-.Xc
-Yank from the current position to the position resulting from
-.Ar n move-cmd Ns s
-into the yank buffer; if
-.Ar move-cmd
-is
-.Ic y ,
-the whole line is yanked.
-.It Y
-Yank from the current position to the end of the line.
-.It Xo
-.Oo Ar n Oc Ns p
-.Xc
-Paste the contents of the yank buffer just after the current position,
-.Ar n
-times.
-.It Xo
-.Oo Ar n Oc Ns P
-.Xc
-Same as
-.Ic p ,
-except the buffer is pasted at the current position.
-.El
-.Pp
-Miscellaneous vi commands
-.Bl -tag -width Ds
-.It \*(haJ and \*(haM
-The current line is read, parsed, and executed by the shell.
-.It \*(haL and \*(haR
-Redraw the current line.
-.It Xo
-.Oo Ar n Oc Ns \&.
-.Xc
-Redo the last edit command
-.Ar n
-times.
-.It u
-Undo the last edit command.
-.It U
-Undo all changes that have been made to the current line.
-.It Ar intr No and Ar quit
-The interrupt and quit terminal characters cause the current line to be
-deleted and a new prompt to be printed.
-.El
-.Sh FILES
-.Bl -tag -width XetcXsuid_profile -compact
-.It Pa \*(TI/.mkshrc
-User mkshrc profile (non-privileged interactive shells); see
-.Sx Startup files.
-The location can be changed at compile time (for embedded systems);
-AOSP Android builds use
-.Pa /system/etc/mkshrc .
-.It Pa \*(TI/.profile
-User profile (non-privileged login shells); see
-.Sx Startup files
-near the top of this manual.
-.It Pa /etc/profile
-System profile (login shells); see
-.Sx Startup files.
-.It Pa /etc/shells
-Shell database.
-.It Pa /etc/suid_profile
-Suid profile (privileged shells); see
-.Sx Startup files.
-.El
-.Pp
-Note: On Android,
-.Pa /system/etc/
-contains the system and suid profile.
-.Sh SEE ALSO
-.Xr awk 1 ,
-.Xr cat 1 ,
-.Xr ed 1 ,
-.Xr getopt 1 ,
-.Xr sed 1 ,
-.Xr sh 1 ,
-.Xr stty 1 ,
-.Xr dup 2 ,
-.Xr execve 2 ,
-.Xr getgid 2 ,
-.Xr getuid 2 ,
-.Xr mknod 2 ,
-.Xr mkfifo 2 ,
-.Xr open 2 ,
-.Xr pipe 2 ,
-.Xr rename 2 ,
-.Xr wait 2 ,
-.Xr getopt 3 ,
-.Xr nl_langinfo 3 ,
-.Xr setlocale 3 ,
-.Xr signal 3 ,
-.Xr system 3 ,
-.Xr tty 4 ,
-.Xr shells 5 ,
-.Xr environ 7 ,
-.Xr script 7 ,
-.Xr utf\-8 7 ,
-.Xr mknod 8
-.Pp
-.Pa http://docsrv.sco.com:507/en/man/html.C/sh.C.html
-.Pp
-.Pa https://www.mirbsd.org/ksh\-chan.htm
-.Rs
-.%A Morris Bolsky
-.%B "The KornShell Command and Programming Language"
-.%D 1989
-.%I "Prentice Hall PTR"
-.%P "xvi\ +\ 356 pages"
-.%O "ISBN 978\-0\-13\-516972\-8 (0\-13\-516972\-0)"
-.Re
-.Rs
-.%A Morris I. Bolsky
-.%A David G. Korn
-.%B "The New KornShell Command and Programming Language (2nd Edition)"
-.%D 1995
-.%I "Prentice Hall PTR"
-.%P "xvi\ +\ 400 pages"
-.%O "ISBN 978\-0\-13\-182700\-4 (0\-13\-182700\-6)"
-.Re
-.Rs
-.%A Stephen G. Kochan
-.%A Patrick H. Wood
-.%B "\\*(tNUNIX\\*(sP Shell Programming"
-.%V "Revised Edition"
-.%D 1990
-.%I "Hayden"
-.%P "xi\ +\ 490 pages"
-.%O "ISBN 978\-0\-672\-48448\-3 (0\-672\-48448\-X)"
-.Re
-.Rs
-.%A "IEEE Inc."
-.%B "\\*(tNIEEE\\*(sP Standard for Information Technology \*(en Portable Operating System Interface (POSIX)"
-.%V "Part 2: Shell and Utilities"
-.%D 1993
-.%I "IEEE Press"
-.%P "xvii\ +\ 1195 pages"
-.%O "ISBN 978\-1\-55937\-255\-8 (1\-55937\-255\-9)"
-.Re
-.Rs
-.%A Bill Rosenblatt
-.%B "Learning the Korn Shell"
-.%D 1993
-.%I "O'Reilly"
-.%P "360 pages"
-.%O "ISBN 978\-1\-56592\-054\-5 (1\-56592\-054\-6)"
-.Re
-.Rs
-.%A Bill Rosenblatt
-.%A Arnold Robbins
-.%B "Learning the Korn Shell, Second Edition"
-.%D 2002
-.%I "O'Reilly"
-.%P "432 pages"
-.%O "ISBN 978\-0\-596\-00195\-7 (0\-596\-00195\-9)"
-.Re
-.Rs
-.%A Barry Rosenberg
-.%B "KornShell Programming Tutorial"
-.%D 1991
-.%I "Addison-Wesley Professional"
-.%P "xxi\ +\ 324 pages"
-.%O "ISBN 978\-0\-201\-56324\-5 (0\-201\-56324\-X)"
-.Re
-.Sh AUTHORS
-.Nm "The MirBSD Korn Shell"
-is developed by
-.An Thorsten Glaser Aq tg at mirbsd.org
-and currently maintained as part of The MirOS Project.
-This shell is based upon the Public Domain Korn SHell.
-The developer of mksh recognises the efforts of the pdksh authors,
-who had dedicated their work into Public Domain, our users, and
-all contributors, such as the Debian and OpenBSD projects.
-.\"
-.\" Charles Forsyth, author of the (Public Domain) Bourne Shell clone,
-.\" which mksh is derived from, agreed to the following:
-.\"
-.\" In countries where the Public Domain status of the work may not be
-.\" valid, its primary author hereby grants a copyright licence to the
-.\" general public to deal in the work without restriction and permis-
-.\" sion to sublicence derivates under the terms of any (OSI approved)
-.\" Open Source licence.
-.\"
-See the documentation, CVS, and web site for details.
-.Pp
-The BSD daemon is Copyright \(co Marshall Kirk McKusick.
-The complete legalese is at:
-.Pa https://www.mirbsd.org/TaC\-mksh.txt
-.\"
-.\" This boils down to: feel free to use mksh.ico as application icon
-.\" or shortcut for mksh or mksh/Win32; distro patches are ok (but we
-.\" request they amend $KSH_VERSION when modifying mksh). Authors are
-.\" Marshall Kirk McKusick (UCB), Rick Collette (ekkoBSD), Thorsten
-.\" Glaser, Benny Siegert (MirBSD), Michael Langguth (mksh/Win32).
-.\"
-.\" As far as MirBSD is concerned, the files themselves are free
-.\" to modification and distribution under BSD/MirOS Licence, the
-.\" restriction on use stems only from trademark law's requirement
-.\" to protect it or lose it, which McKusick almost did.
-.\"
-.Sh CAVEATS
-.Nm
-only supports the Unicode BMP (Basic Multilingual Plane).
-.Pp
-.Nm
-has a different scope model from
-.At
-.Nm ksh ,
-which leads to subtile differences in semantics for identical builtins.
-This can cause issues with a
-.Ic nameref
-to suddenly point to a local variable by accident; fixing this is hard.
-.Pp
-The parts of a pipeline, like below, are executed in subshells.
-Thus, variable assignments inside them are not visible in the
-surrounding execution environment.
-Use co-processes instead.
-.Bd -literal -offset indent
-foo \*(Ba bar \*(Ba read baz            # will not change $baz
-foo \*(Ba bar \*(Ba& read \-p baz        # will, however, do so
-.Ed
-.Pp
-.Nm mksh
-provides a consistent set of 32-bit integer arithmetics, both signed
-and unsigned, with defined wraparound and sign of the result of a modulo
-operation, even (defying POSIX) on 64-bit systems.
-If you require 64-bit integer arithmetics, use
-.Nm lksh Pq legacy mksh
-instead, but be aware that, in POSIX, it's legal for the OS to make
-.Li print $((2147483647 + 1))
-delete all files on your system, as it's Undefined Behaviour.
-.Sh BUGS
-Suspending (using \*(haZ) pipelines like the one below will only suspend
-the currently running part of the pipeline; in this example,
-.Dq fubar
-is immediately printed on suspension (but not later after an
-.Ic fg ) .
-.Bd -literal -offset indent
-$ /bin/sleep 666 && echo fubar
-.Ed
-.Pp
-This document attempts to describe
-.Nm mksh\ R48
-and up,
-compiled without any options impacting functionality, such as
-.Dv MKSH_SMALL ,
-when not called as
-.Pa /bin/sh
-which, on some systems only, enables
-.Ic set \-o sh
-automatically (whose behaviour differs across targets),
-for an operating environment supporting all of its advanced needs.
-Please report bugs in
-.Nm
-to the
-.Mx
-mailing list at
-.Aq miros\-mksh at mirbsd.org
-or in the
-.Li \&#\&!/bin/mksh
-.Pq or Li \&#ksh
-IRC channel at
-.Pa irc.freenode.net
-.Pq Port 6697 SSL, 6667 unencrypted ,
-or at:
-.Pa https://launchpad.net/mksh

Copied: vendor/MirOS/mksh/R50/mksh.1 (from rev 6707, vendor/MirOS/mksh/dist/mksh.1)
===================================================================
--- vendor/MirOS/mksh/R50/mksh.1	                        (rev 0)
+++ vendor/MirOS/mksh/R50/mksh.1	2014-07-01 12:01:17 UTC (rev 6708)
@@ -0,0 +1,6480 @@
+.\" $MirOS: src/bin/mksh/mksh.1,v 1.336 2014/06/24 20:47:44 tg Exp $
+.\" $OpenBSD: ksh.1,v 1.152 2014/02/12 16:28:13 schwarze Exp $
+.\"-
+.\" Copyright © 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009,
+.\"		2010, 2011, 2012, 2013, 2014
+.\"	Thorsten Glaser <tg at mirbsd.org>
+.\"
+.\" Provided that these terms and disclaimer and all copyright notices
+.\" are retained or reproduced in an accompanying document, permission
+.\" is granted to deal in this work without restriction, including un‐
+.\" limited rights to use, publicly perform, distribute, sell, modify,
+.\" merge, give away, or sublicence.
+.\"
+.\" This work is provided “AS IS” and WITHOUT WARRANTY of any kind, to
+.\" the utmost extent permitted by applicable law, neither express nor
+.\" implied; without malicious intent or gross negligence. In no event
+.\" may a licensor, author or contributor be held liable for indirect,
+.\" direct, other damage, loss, or other issues arising in any way out
+.\" of dealing in the work, even if advised of the possibility of such
+.\" damage or existence of a defect, except proven that it results out
+.\" of said person’s immediate fault when using the work as intended.
+.\"-
+.\" Try to make GNU groff and AT&T nroff more compatible
+.\" * ` generates ‘ in gnroff, so use \`
+.\" * ' generates ’ in gnroff, \' generates ´, so use \*(aq
+.\" * - generates ‐ in gnroff, \- generates −, so .tr it to -
+.\"   thus use - for hyphens and \- for minus signs and option dashes
+.\" * ~ is size-reduced and placed atop in groff, so use \*(TI
+.\" * ^ is size-reduced and placed atop in groff, so use \*(ha
+.\" * \(en does not work in nroff, so use \*(en
+.\" * <>| are problematic, so redefine and use \*(Lt\*(Gt\*(Ba
+.\" Also make sure to use \& especially with two-letter words.
+.\" The section after the "doc" macropackage has been loaded contains
+.\" additional code to convene between the UCB mdoc macropackage (and
+.\" its variant as BSD mdoc in groff) and the GNU mdoc macropackage.
+.\"
+.ie \n(.g \{\
+.	if \*[.T]ascii .tr \-\N'45'
+.	if \*[.T]latin1 .tr \-\N'45'
+.	if \*[.T]utf8 .tr \-\N'45'
+.	ds <= \[<=]
+.	ds >= \[>=]
+.	ds Rq \[rq]
+.	ds Lq \[lq]
+.	ds sL \(aq
+.	ds sR \(aq
+.	if \*[.T]utf8 .ds sL `
+.	if \*[.T]ps .ds sL `
+.	if \*[.T]utf8 .ds sR '
+.	if \*[.T]ps .ds sR '
+.	ds aq \(aq
+.	ds TI \(ti
+.	ds ha \(ha
+.	ds en \(en
+.\}
+.el \{\
+.	ds aq '
+.	ds TI ~
+.	ds ha ^
+.	ds en \(em
+.\}
+.\"
+.\" Implement .Dd with the Mdocdate RCS keyword
+.\"
+.rn Dd xD
+.de Dd
+.ie \\$1$Mdocdate: \{\
+.	xD \\$2 \\$3, \\$4
+.\}
+.el .xD \\$1 \\$2 \\$3 \\$4 \\$5 \\$6 \\$7 \\$8
+..
+.\"
+.\" .Dd must come before definition of .Mx, because when called
+.\" with -mandoc, it might implement .Mx itself, but we want to
+.\" use our own definition. And .Dd must come *first*, always.
+.\"
+.Dd $Mdocdate: June 24 2014 $
+.\"
+.\" Check which macro package we use, and do other -mdoc setup.
+.\"
+.ie \n(.g \{\
+.	if \*[.T]utf8 .tr \[la]\*(Lt
+.	if \*[.T]utf8 .tr \[ra]\*(Gt
+.	ie d volume-ds-1 .ds tT gnu
+.	el .ds tT bsd
+.\}
+.el .ds tT ucb
+.\"
+.\" Implement .Mx (MirBSD)
+.\"
+.ie "\*(tT"gnu" \{\
+.	eo
+.	de Mx
+.	nr curr-font \n[.f]
+.	nr curr-size \n[.ps]
+.	ds str-Mx \f[\n[curr-font]]\s[\n[curr-size]u]
+.	ds str-Mx1 \*[Tn-font-size]\%MirOS\*[str-Mx]
+.	if !\n[arg-limit] \
+.	if \n[.$] \{\
+.	ds macro-name Mx
+.	parse-args \$@
+.	\}
+.	if (\n[arg-limit] > \n[arg-ptr]) \{\
+.	nr arg-ptr +1
+.	ie (\n[type\n[arg-ptr]] == 2) \
+.	as str-Mx1 \~\*[arg\n[arg-ptr]]
+.	el \
+.	nr arg-ptr -1
+.	\}
+.	ds arg\n[arg-ptr] "\*[str-Mx1]
+.	nr type\n[arg-ptr] 2
+.	ds space\n[arg-ptr] "\*[space]
+.	nr num-args (\n[arg-limit] - \n[arg-ptr])
+.	nr arg-limit \n[arg-ptr]
+.	if \n[num-args] \
+.	parse-space-vector
+.	print-recursive
+..
+.	ec
+.	ds sP \s0
+.	ds tN \*[Tn-font-size]
+.\}
+.el \{\
+.	de Mx
+.	nr cF \\n(.f
+.	nr cZ \\n(.s
+.	ds aa \&\f\\n(cF\s\\n(cZ
+.	if \\n(aC==0 \{\
+.		ie \\n(.$==0 \&MirOS\\*(aa
+.		el .aV \\$1 \\$2 \\$3 \\$4 \\$5 \\$6 \\$7 \\$8 \\$9
+.	\}
+.	if \\n(aC>\\n(aP \{\
+.		nr aP \\n(aP+1
+.		ie \\n(C\\n(aP==2 \{\
+.			as b1 \&MirOS\ #\&\\*(A\\n(aP\\*(aa
+.			ie \\n(aC>\\n(aP \{\
+.				nr aP \\n(aP+1
+.				nR
+.			\}
+.			el .aZ
+.		\}
+.		el \{\
+.			as b1 \&MirOS\\*(aa
+.			nR
+.		\}
+.	\}
+..
+.\}
+.\"-
+.Dt MKSH 1
+.Os MirBSD
+.Sh NAME
+.Nm mksh ,
+.Nm sh
+.Nd MirBSD Korn shell
+.Sh SYNOPSIS
+.Nm
+.Bk -words
+.Op Fl +abCefhiklmnprUuvXx
+.Oo
+.Fl T Oo Ar \&! Oc Ns Ar tty
+\*(Ba
+.Ar \&\-
+.Oc
+.Op Fl +o Ar option
+.Oo
+.Fl c Ar string \*(Ba
+.Fl s \*(Ba
+.Ar file
+.Op Ar argument ...
+.Oc
+.Ek
+.Nm builtin-name
+.Op Ar argument ...
+.Sh DESCRIPTION
+.Nm
+is a command interpreter intended for both interactive and shell
+script use.
+Its command language is a superset of the
+.Xr sh C
+shell language and largely compatible to the original Korn shell.
+.Ss I'm an Android user, so what's mksh?
+.Nm mksh
+is a
+.Ux
+shell / command interpreter, similar to
+.Nm COMMAND.COM
+or
+.Nm CMD.EXE ,
+which has been included with
+.Tn Android Open Source Project
+for a while now.
+Basically, it's a program that runs in a terminal (console window),
+takes user input and runs commands or scripts, which it can also
+be asked to do by other programs, even in the background.
+Any privilege pop-ups you might be encountering are thus not
+.Nm mksh
+issues but questions by some other program utilising it.
+.Ss Invocation
+Most builtins can be called directly, for example if a link points from its
+name to the shell; not all make sense, have been tested or work at all though.
+.Pp
+The options are as follows:
+.Bl -tag -width XcXstring
+.It Fl c Ar string
+.Nm
+will execute the command(s) contained in
+.Ar string .
+.It Fl i
+Interactive shell.
+A shell is
+.Dq interactive
+if this
+option is used or if both standard input and standard error are attached
+to a
+.Xr tty 4 .
+An interactive shell has job control enabled, ignores the
+.Dv SIGINT ,
+.Dv SIGQUIT ,
+and
+.Dv SIGTERM
+signals, and prints prompts before reading input (see the
+.Ev PS1
+and
+.Ev PS2
+parameters).
+It also processes the
+.Ev ENV
+parameter or the
+.Pa mkshrc
+file (see below).
+For non-interactive shells, the
+.Ic trackall
+option is on by default (see the
+.Ic set
+command below).
+.It Fl l
+Login shell.
+If the basename the shell is called with (i.e. argv[0])
+starts with
+.Ql \-
+or if this option is used,
+the shell is assumed to be a login shell; see
+.Sx Startup files
+below.
+.It Fl p
+Privileged shell.
+A shell is
+.Dq privileged
+if the real user ID or group ID does not match the
+effective user ID or group ID (see
+.Xr getuid 2
+and
+.Xr getgid 2 ) .
+Clearing the privileged option causes the shell to set
+its effective user ID (group ID) to its real user ID (group ID).
+For further implications, see
+.Sx Startup files .
+If the shell is privileged and this flag is not explicitly set, the
+.Dq privileged
+option is cleared automatically after processing the startup files.
+.It Fl r
+Restricted shell.
+A shell is
+.Dq restricted
+if this
+option is used.
+The following restrictions come into effect after the shell processes any
+profile and
+.Ev ENV
+files:
+.Pp
+.Bl -bullet -compact
+.It
+The
+.Ic cd
+.Po and Ic chdir Pc
+command is disabled.
+.It
+The
+.Ev SHELL ,
+.Ev ENV ,
+and
+.Ev PATH
+parameters cannot be changed.
+.It
+Command names can't be specified with absolute or relative paths.
+.It
+The
+.Fl p
+option of the built-in command
+.Ic command
+can't be used.
+.It
+Redirections that create files can't be used (i.e.\&
+.Ql \*(Gt ,
+.Ql \*(Gt\*(Ba ,
+.Ql \*(Gt\*(Gt ,
+.Ql \*(Lt\*(Gt ) .
+.El
+.It Fl s
+The shell reads commands from standard input; all non-option arguments
+are positional parameters.
+.It Fl T Ar name
+Spawn
+.Nm
+on the
+.Xr tty 4
+device given.
+The paths
+.Ar name ,
+.Pa /dev/ttyC Ns Ar name
+and
+.Pa /dev/tty Ns Ar name
+are attempted in order.
+Unless
+.Ar name
+begins with an exclamation mark
+.Pq Sq \&! ,
+this is done in a subshell and returns immediately.
+If
+.Ar name
+is a dash
+.Pq Sq \&\- ,
+detach from controlling terminal (daemonise) instead.
+.El
+.Pp
+In addition to the above, the options described in the
+.Ic set
+built-in command can also be used on the command line:
+both
+.Op Fl +abCefhkmnuvXx
+and
+.Op Fl +o Ar option
+can be used for single letter or long options, respectively.
+.Pp
+If neither the
+.Fl c
+nor the
+.Fl s
+option is specified, the first non-option argument specifies the name
+of a file the shell reads commands from.
+If there are no non-option
+arguments, the shell reads commands from the standard input.
+The name of the shell (i.e. the contents of $0)
+is determined as follows: if the
+.Fl c
+option is used and there is a non-option argument, it is used as the name;
+if commands are being read from a file, the file is used as the name;
+otherwise, the basename the shell was called with (i.e. argv[0]) is used.
+.Pp
+The exit status of the shell is 127 if the command file specified on the
+command line could not be opened, or non-zero if a fatal syntax error
+occurred during the execution of a script.
+In the absence of fatal errors,
+the exit status is that of the last command executed, or zero, if no
+command is executed.
+.Ss Startup files
+For the actual location of these files, see
+.Sx FILES .
+A login shell processes the system profile first.
+A privileged shell then processes the suid profile.
+A non-privileged login shell processes the user profile next.
+A non-privileged interactive shell checks the value of the
+.Ev ENV
+parameter after subjecting it to parameter, command, arithmetic and tilde
+.Pq Sq \*(TI
+substitution; if unset or empty, the user mkshrc profile is processed;
+otherwise, if a file whose name is the substitution result exists,
+it is processed; non-existence is silently ignored.
+A privileged shell then drops privileges if neither was the
+.Fl p
+option given on the command line nor set during execution of the startup files.
+.Ss Command syntax
+The shell begins parsing its input by removing any backslash-newline
+combinations, then breaking it into
+.Em words .
+Words (which are sequences of characters) are delimited by unquoted whitespace
+characters (space, tab, and newline) or meta-characters
+.Po
+.Ql \*(Lt ,
+.Ql \*(Gt ,
+.Ql \*(Ba ,
+.Ql \&; ,
+.Ql \&( ,
+.Ql \&) ,
+and
+.Ql &
+.Pc .
+Aside from delimiting words, spaces and tabs are ignored, while newlines
+usually delimit commands.
+The meta-characters are used in building the following
+.Em tokens :
+.Ql \*(Lt ,
+.Ql \*(Lt& ,
+.Ql \*(Lt\*(Lt ,
+.Ql \*(Lt\*(Lt\*(Lt ,
+.Ql \*(Gt ,
+.Ql \*(Gt& ,
+.Ql \*(Gt\*(Gt ,
+.Ql &\*(Gt ,
+etc. are used to specify redirections (see
+.Sx Input/output redirection
+below);
+.Ql \*(Ba
+is used to create pipelines;
+.Ql \*(Ba&
+is used to create co-processes (see
+.Sx Co-processes
+below);
+.Ql \&;
+is used to separate commands;
+.Ql &
+is used to create asynchronous pipelines;
+.Ql &&
+and
+.Ql \*(Ba\*(Ba
+are used to specify conditional execution;
+.Ql ;; ,
+.Ql ;&\&
+and
+.Ql ;\*(Ba\&
+are used in
+.Ic case
+statements;
+.Ql \&(( .. ))
+is used in arithmetic expressions;
+and lastly,
+.Ql \&( .. )\&
+is used to create subshells.
+.Pp
+Whitespace and meta-characters can be quoted individually using a backslash
+.Pq Sq \e ,
+or in groups using double
+.Pq Sq \&"
+or single
+.Pq Sq \*(aq
+quotes.
+Note that the following characters are also treated specially by the
+shell and must be quoted if they are to represent themselves:
+.Ql \e ,
+.Ql \&" ,
+.Ql \*(aq ,
+.Ql # ,
+.Ql $ ,
+.Ql \` ,
+.Ql \*(TI ,
+.Ql { ,
+.Ql } ,
+.Ql * ,
+.Ql \&? ,
+and
+.Ql \&[ .
+The first three of these are the above mentioned quoting characters (see
+.Sx Quoting
+below);
+.Ql # ,
+if used at the beginning of a word, introduces a comment \*(en everything after
+the
+.Ql #
+up to the nearest newline is ignored;
+.Ql $
+is used to introduce parameter, command, and arithmetic substitutions (see
+.Sx Substitution
+below);
+.Ql \`
+introduces an old-style command substitution (see
+.Sx Substitution
+below);
+.Ql \*(TI
+begins a directory expansion (see
+.Sx Tilde expansion
+below);
+.Ql {
+and
+.Ql }
+delimit
+.Xr csh 1 Ns -style
+alterations (see
+.Sx Brace expansion
+below);
+and finally,
+.Ql * ,
+.Ql \&? ,
+and
+.Ql \&[
+are used in file name generation (see
+.Sx File name patterns
+below).
+.Pp
+As words and tokens are parsed, the shell builds commands, of which there
+are two basic types:
+.Em simple-commands ,
+typically programmes that are executed, and
+.Em compound-commands ,
+such as
+.Ic for
+and
+.Ic if
+statements, grouping constructs, and function definitions.
+.Pp
+A simple-command consists of some combination of parameter assignments
+(see
+.Sx Parameters
+below),
+input/output redirections (see
+.Sx Input/output redirections
+below),
+and command words; the only restriction is that parameter assignments come
+before any command words.
+The command words, if any, define the command
+that is to be executed and its arguments.
+The command may be a shell built-in command, a function,
+or an external command
+(i.e. a separate executable file that is located using the
+.Ev PATH
+parameter; see
+.Sx Command execution
+below).
+Note that all command constructs have an exit status: for external commands,
+this is related to the status returned by
+.Xr wait 2
+(if the command could not be found, the exit status is 127; if it could not
+be executed, the exit status is 126); the exit status of other command
+constructs (built-in commands, functions, compound-commands, pipelines, lists,
+etc.) are all well-defined and are described where the construct is
+described.
+The exit status of a command consisting only of parameter
+assignments is that of the last command substitution performed during the
+parameter assignment or 0 if there were no command substitutions.
+.Pp
+Commands can be chained together using the
+.Ql \*(Ba
+token to form pipelines, in which the standard output of each command but the
+last is piped (see
+.Xr pipe 2 )
+to the standard input of the following command.
+The exit status of a pipeline is that of its last command, unless the
+.Ic pipefail
+option is set (see there).
+All commands of a pipeline are executed in separate subshells;
+this is allowed by POSIX but differs from both variants of
+.At
+.Nm ksh ,
+where all but the last command were executed in subshells; see the
+.Ic read
+builtin's description for implications and workarounds.
+A pipeline may be prefixed by the
+.Ql \&!
+reserved word which causes the exit status of the pipeline to be logically
+complemented: if the original status was 0, the complemented status will be 1;
+if the original status was not 0, the complemented status will be 0.
+.Pp
+.Em Lists
+of commands can be created by separating pipelines by any of the following
+tokens:
+.Ql && ,
+.Ql \*(Ba\*(Ba ,
+.Ql & ,
+.Ql \*(Ba& ,
+and
+.Ql \&; .
+The first two are for conditional execution:
+.Dq Ar cmd1 No && Ar cmd2
+executes
+.Ar cmd2
+only if the exit status of
+.Ar cmd1
+is zero;
+.Ql \*(Ba\*(Ba
+is the opposite \*(en
+.Ar cmd2
+is executed only if the exit status of
+.Ar cmd1
+is non-zero.
+.Ql &&
+and
+.Ql \*(Ba\*(Ba
+have equal precedence which is higher than that of
+.Ql & ,
+.Ql \*(Ba& ,
+and
+.Ql \&; ,
+which also have equal precedence.
+Note that the
+.Ql &&
+and
+.Ql \*(Ba\*(Ba
+operators are
+.Qq left-associative .
+For example, both of these commands will print only
+.Qq bar :
+.Bd -literal -offset indent
+$ false && echo foo \*(Ba\*(Ba echo bar
+$ true \*(Ba\*(Ba echo foo && echo bar
+.Ed
+.Pp
+The
+.Ql &
+token causes the preceding command to be executed asynchronously; that is,
+the shell starts the command but does not wait for it to complete (the shell
+does keep track of the status of asynchronous commands; see
+.Sx Job control
+below).
+When an asynchronous command is started when job control is disabled
+(i.e. in most scripts), the command is started with signals
+.Dv SIGINT
+and
+.Dv SIGQUIT
+ignored and with input redirected from
+.Pa /dev/null
+(however, redirections specified in the asynchronous command have precedence).
+The
+.Ql \*(Ba&
+operator starts a co-process which is a special kind of asynchronous process
+(see
+.Sx Co-processes
+below).
+Note that a command must follow the
+.Ql &&
+and
+.Ql \*(Ba\*(Ba
+operators, while it need not follow
+.Ql & ,
+.Ql \*(Ba& ,
+or
+.Ql \&; .
+The exit status of a list is that of the last command executed, with the
+exception of asynchronous lists, for which the exit status is 0.
+.Pp
+Compound commands are created using the following reserved words.
+These words
+are only recognised if they are unquoted and if they are used as the first
+word of a command (i.e. they can't be preceded by parameter assignments or
+redirections):
+.Bd -literal -offset indent
+case     else     function     then      !       (
+do       esac     if           time      [[      ((
+done     fi       in           until     {
+elif     for      select       while     }
+.Ed
+.Pp
+In the following compound command descriptions, command lists (denoted as
+.Em list )
+that are followed by reserved words must end with a semicolon, a newline, or
+a (syntactically correct) reserved word.
+For example, the following are all valid:
+.Bd -literal -offset indent
+$ { echo foo; echo bar; }
+$ { echo foo; echo bar\*(Ltnewline\*(Gt}
+$ { { echo foo; echo bar; } }
+.Ed
+.Pp
+This is not valid:
+.Pp
+.Dl $ { echo foo; echo bar }
+.Bl -tag -width 4n
+.It Pq Ar list
+Execute
+.Ar list
+in a subshell.
+There is no implicit way to pass environment changes from a
+subshell back to its parent.
+.It { Ar list ; No }
+Compound construct;
+.Ar list
+is executed, but not in a subshell.
+Note that
+.Ql {
+and
+.Ql }
+are reserved words, not meta-characters.
+.It Xo case Ar word No in
+.Oo Op \&(
+.Ar pattern
+.Op \*(Ba Ar pat
+.No ... Ns )
+.Ar list
+.Op ;; \*(Ba ;&\& \*(Ba ;\*(Ba\ \&
+.Oc ... esac
+.Xc
+The
+.Ic case
+statement attempts to match
+.Ar word
+against a specified
+.Ar pattern ;
+the
+.Ar list
+associated with the first successfully matched pattern is executed.
+Patterns used in
+.Ic case
+statements are the same as those used for file name patterns except that the
+restrictions regarding
+.Ql \&.
+and
+.Ql /
+are dropped.
+Note that any unquoted space before and after a pattern is
+stripped; any space within a pattern must be quoted.
+Both the word and the
+patterns are subject to parameter, command, and arithmetic substitution, as
+well as tilde substitution.
+.Pp
+For historical reasons, open and close braces may be used instead of
+.Ic in
+and
+.Ic esac
+e.g.\&
+.Ic case $foo { *) echo bar;; } .
+.Pp
+The list terminators are:
+.Bl -tag -width 4n
+.It Ql ;;
+Terminate after the list.
+.It Ql ;&\&
+Fall through into the next list.
+.It Ql ;\*(Ba\&
+Evaluate the remaining pattern-list tuples.
+.El
+.Pp
+The exit status of a
+.Ic case
+statement is that of the executed
+.Ar list ;
+if no
+.Ar list
+is executed, the exit status is zero.
+.It Xo for Ar name
+.Oo in Ar word No ... Oc ;
+.No do Ar list ; No done
+.Xc
+For each
+.Ar word
+in the specified word list, the parameter
+.Ar name
+is set to the word and
+.Ar list
+is executed.
+If
+.Ic in
+is not used to specify a word list, the positional parameters
+($1, $2, etc.)\&
+are used instead.
+For historical reasons, open and close braces may be used instead of
+.Ic do
+and
+.Ic done
+e.g.\&
+.Ic for i; { echo $i; } .
+The exit status of a
+.Ic for
+statement is the last exit status of
+.Ar list ;
+if
+.Ar list
+is never executed, the exit status is zero.
+.It Xo if Ar list ;
+.No then Ar list ;
+.Oo elif Ar list ;
+.No then Ar list ; Oc
+.No ...
+.Oo else Ar list ; Oc
+.No fi
+.Xc
+If the exit status of the first
+.Ar list
+is zero, the second
+.Ar list
+is executed; otherwise, the
+.Ar list
+following the
+.Ic elif ,
+if any, is executed with similar consequences.
+If all the lists following the
+.Ic if
+and
+.Ic elif Ns s
+fail (i.e. exit with non-zero status), the
+.Ar list
+following the
+.Ic else
+is executed.
+The exit status of an
+.Ic if
+statement is that of non-conditional
+.Ar list
+that is executed; if no non-conditional
+.Ar list
+is executed, the exit status is zero.
+.It Xo select Ar name
+.Oo in Ar word No ... Oc ;
+.No do Ar list ; No done
+.Xc
+The
+.Ic select
+statement provides an automatic method of presenting the user with a menu and
+selecting from it.
+An enumerated list of the specified
+.Ar word Ns (s)
+is printed on standard error, followed by a prompt
+.Po
+.Ev PS3: normally
+.Sq #?\ \&
+.Pc .
+A number corresponding to one of the enumerated words is then read from
+standard input,
+.Ar name
+is set to the selected word (or unset if the selection is not valid),
+.Ev REPLY
+is set to what was read (leading/trailing space is stripped), and
+.Ar list
+is executed.
+If a blank line (i.e. zero or more
+.Ev IFS
+octets) is entered, the menu is reprinted without executing
+.Ar list .
+.Pp
+When
+.Ar list
+completes, the enumerated list is printed if
+.Ev REPLY
+is
+.Dv NULL ,
+the prompt is printed, and so on.
+This process continues until an end-of-file
+is read, an interrupt is received, or a
+.Ic break
+statement is executed inside the loop.
+If
+.Dq in word ...
+is omitted, the positional parameters are used
+(i.e. $1, $2, etc.).
+For historical reasons, open and close braces may be used instead of
+.Ic do
+and
+.Ic done
+e.g.\&
+.Ic select i; { echo $i; } .
+The exit status of a
+.Ic select
+statement is zero if a
+.Ic break
+statement is used to exit the loop, non-zero otherwise.
+.It Xo until Ar list ;
+.No do Ar list ;
+.No done
+.Xc
+This works like
+.Ic while ,
+except that the body is executed only while the exit status of the first
+.Ar list
+is non-zero.
+.It Xo while Ar list ;
+.No do Ar list ;
+.No done
+.Xc
+A
+.Ic while
+is a pre-checked loop.
+Its body is executed as often as the exit status of the first
+.Ar list
+is zero.
+The exit status of a
+.Ic while
+statement is the last exit status of the
+.Ar list
+in the body of the loop; if the body is not executed, the exit status is zero.
+.It Xo function Ar name
+.No { Ar list ; No }
+.Xc
+Defines the function
+.Ar name
+(see
+.Sx Functions
+below).
+Note that redirections specified after a function definition are
+performed whenever the function is executed, not when the function definition
+is executed.
+.It Ar name Ns \&() Ar command
+Mostly the same as
+.Ic function
+(see
+.Sx Functions
+below).
+Whitespace (space or tab) after
+.Ar name
+will be ignored most of the time.
+.It Xo function Ar name Ns \&()
+.No { Ar list ; No }
+.Xc
+The same as
+.Ar name Ns \&()
+.Pq Nm bash Ns ism .
+The
+.Ic function
+keyword is ignored.
+.It Xo Ic time Op Fl p
+.Op Ar pipeline
+.Xc
+The
+.Sx Command execution
+section describes the
+.Ic time
+reserved word.
+.It \&(( Ar expression No ))
+The arithmetic expression
+.Ar expression
+is evaluated; equivalent to
+.Dq let expression
+(see
+.Sx Arithmetic expressions
+and the
+.Ic let
+command, below).
+.It Bq Bq Ar \ \&expression\ \&
+Similar to the
+.Ic test
+and
+.Ic \&[ ... \&]
+commands (described later), with the following exceptions:
+.Bl -bullet
+.It
+Field splitting and file name generation are not performed on arguments.
+.It
+The
+.Fl a
+.Pq AND
+and
+.Fl o
+.Pq OR
+operators are replaced with
+.Ql &&
+and
+.Ql \*(Ba\*(Ba ,
+respectively.
+.It
+Operators (e.g.\&
+.Sq Fl f ,
+.Sq = ,
+.Sq \&! )
+must be unquoted.
+.It
+Parameter, command, and arithmetic substitutions are performed as expressions
+are evaluated and lazy expression evaluation is used for the
+.Ql &&
+and
+.Ql \*(Ba\*(Ba
+operators.
+This means that in the following statement,
+.Ic $(\*(Ltfoo)
+is evaluated if and only if the file
+.Pa foo
+exists and is readable:
+.Bd -literal -offset indent
+$ [[ \-r foo && $(\*(Ltfoo) = b*r ]]
+.Ed
+.It
+The second operand of the
+.Sq !=
+and
+.Sq =
+expressions are a subset of patterns (e.g. the comparison
+.Ic \&[[ foobar = f*r ]]
+succeeds).
+This even works indirectly:
+.Bd -literal -offset indent
+$ bar=foobar; baz=\*(aqf*r\*(aq
+$ [[ $bar = $baz ]]; echo $?
+$ [[ $bar = \&"$baz" ]]; echo $?
+.Ed
+.Pp
+Perhaps surprisingly, the first comparison succeeds,
+whereas the second doesn't.
+This does not apply to all extglob metacharacters, currently.
+.El
+.El
+.Ss Quoting
+Quoting is used to prevent the shell from treating characters or words
+specially.
+There are three methods of quoting.
+First,
+.Ql \e
+quotes the following character, unless it is at the end of a line, in which
+case both the
+.Ql \e
+and the newline are stripped.
+Second, a single quote
+.Pq Sq \*(aq
+quotes everything up to the next single quote (this may span lines).
+Third, a double quote
+.Pq Sq \&"
+quotes all characters, except
+.Ql $ ,
+.Ql \`
+and
+.Ql \e ,
+up to the next unquoted double quote.
+.Ql $
+and
+.Ql \`
+inside double quotes have their usual meaning (i.e. parameter, command, or
+arithmetic substitution) except no field splitting is carried out on the
+results of double-quoted substitutions.
+If a
+.Ql \e
+inside a double-quoted string is followed by
+.Ql \e ,
+.Ql $ ,
+.Ql \` ,
+or
+.Ql \&" ,
+it is replaced by the second character; if it is followed by a newline, both
+the
+.Ql \e
+and the newline are stripped; otherwise, both the
+.Ql \e
+and the character following are unchanged.
+.Pp
+If a single-quoted string is preceded by an unquoted
+.Ql $ ,
+C style backslash expansion (see below) is applied (even single quote
+characters inside can be escaped and do not terminate the string then);
+the expanded result is treated as any other single-quoted string.
+If a double-quoted string is preceded by an unquoted
+.Ql $ ,
+the latter is ignored.
+.Ss Backslash expansion
+In places where backslashes are expanded, certain C and
+.At
+.Nm ksh
+or GNU
+.Nm bash
+style escapes are translated.
+These include
+.Ql \ea ,
+.Ql \eb ,
+.Ql \ef ,
+.Ql \en ,
+.Ql \er ,
+.Ql \et ,
+.Ql \eU######## ,
+.Ql \eu#### ,
+and
+.Ql \ev .
+For
+.Ql \eU########
+and
+.Ql \eu#### ,
+.Dq #
+means a hexadecimal digit, of thich there may be none up to four or eight;
+these escapes translate a Unicode codepoint to UTF-8.
+Furthermore,
+.Ql \eE
+and
+.Ql \ee
+expand to the escape character.
+.Pp
+In the
+.Ic print
+builtin mode,
+.Ql \e" ,
+.Ql \e\*(aq ,
+and
+.Ql \e?
+are explicitly excluded;
+octal sequences must have the none up to three octal digits
+.Dq #
+prefixed with the digit zero
+.Pq Ql \e0### ;
+hexadecimal sequences
+.Ql \ex##
+are limited to none up to two hexadecimal digits
+.Dq # ;
+both octal and hexadecimal sequences convert to raw octets;
+.Ql \e# ,
+where # is none of the above, translates to \e# (backslashes are retained).
+.Pp
+Backslash expansion in the C style mode slightly differs: octal sequences
+.Ql \e###
+must have no digit zero prefixing the one up to three octal digits
+.Dq #
+and yield raw octets; hexadecimal sequences
+.Ql \ex#*
+greedily eat up as many hexadecimal digits
+.Dq #
+as they can and terminate with the first non-hexadecimal digit;
+these translate a Unicode codepoint to UTF-8.
+The sequence
+.Ql \ec# ,
+where
+.Dq #
+is any octet, translates to Ctrl-# (which basically means,
+.Ql \ec?
+becomes DEL, everything else is bitwise ANDed with 0x1F).
+Finally,
+.Ql \e# ,
+where # is none of the above, translates to # (has the backslash trimmed),
+even if it is a newline.
+.Ss Aliases
+There are two types of aliases: normal command aliases and tracked aliases.
+Command aliases are normally used as a short hand for a long or often used
+command.
+The shell expands command aliases (i.e. substitutes the alias name
+for its value) when it reads the first word of a command.
+An expanded alias is re-processed to check for more aliases.
+If a command alias ends in a
+space or tab, the following word is also checked for alias expansion.
+The alias expansion process stops when a word that is not an alias is found,
+when a quoted word is found, or when an alias word that is currently being
+expanded is found.
+Aliases are specifically an interactive feature: while they do happen
+to work in scripts and on the command line in some cases, aliases are
+expanded during lexing, so their use must be in a separate command tree
+from their definition; otherwise, the alias will not be found.
+Noticeably, command lists (separated by semicolon, in command substitutions
+also by newline) may be one same parse tree.
+.Pp
+The following command aliases are defined automatically by the shell:
+.Bd -literal -offset indent
+autoload=\*(aqtypeset \-fu\*(aq
+functions=\*(aqtypeset \-f\*(aq
+hash=\*(aqalias \-t\*(aq
+history=\*(aqfc \-l\*(aq
+integer=\*(aqtypeset \-i\*(aq
+local=\*(aqtypeset\*(aq
+login=\*(aqexec login\*(aq
+nameref=\*(aqtypeset \-n\*(aq
+nohup=\*(aqnohup \*(aq
+r=\*(aqfc \-e \-\*(aq
+stop=\*(aqkill \-STOP\*(aq
+type=\*(aqwhence \-v\*(aq
+.Ed
+.Pp
+Tracked aliases allow the shell to remember where it found a particular
+command.
+The first time the shell does a path search for a command that is
+marked as a tracked alias, it saves the full path of the command.
+The next
+time the command is executed, the shell checks the saved path to see that it
+is still valid, and if so, avoids repeating the path search.
+Tracked aliases can be listed and created using
+.Ic alias \-t .
+Note that changing the
+.Ev PATH
+parameter clears the saved paths for all tracked aliases.
+If the
+.Ic trackall
+option is set (i.e.\&
+.Ic set \-o Ic trackall
+or
+.Ic set \-h ) ,
+the shell tracks all commands.
+This option is set automatically for non-interactive shells.
+For interactive shells, only the following commands are
+automatically tracked:
+.Xr cat 1 ,
+.Xr cc 1 ,
+.Xr chmod 1 ,
+.Xr cp 1 ,
+.Xr date 1 ,
+.Xr ed 1 ,
+.Xr emacs 1 ,
+.Xr grep 1 ,
+.Xr ls 1 ,
+.Xr make 1 ,
+.Xr mv 1 ,
+.Xr pr 1 ,
+.Xr rm 1 ,
+.Xr sed 1 ,
+.Xr sh 1 ,
+.Xr vi 1 ,
+and
+.Xr who 1 .
+.Ss Substitution
+The first step the shell takes in executing a simple-command is to perform
+substitutions on the words of the command.
+There are three kinds of
+substitution: parameter, command, and arithmetic.
+Parameter substitutions,
+which are described in detail in the next section, take the form
+.Pf $ Ns Ar name
+or
+.Pf ${ Ns Ar ... Ns } ;
+command substitutions take the form
+.Pf $( Ns Ar command Ns \&)
+or (deprecated)
+.Pf \` Ns Ar command Ns \`
+or (executed in the current environment)
+.Pf ${\ \& Ar command Ns \&;}
+and strip trailing newlines;
+and arithmetic substitutions take the form
+.Pf $(( Ns Ar expression Ns )) .
+Parsing the current-environment command substitution requires a space,
+tab or newline after the opening brace and that the closing brace be
+recognised as a keyword (i.e. is preceded by a newline or semicolon).
+They are also called funsubs (function substitutions) and behave like
+functions in that
+.Ic local
+and
+.Ic return
+work, and in that
+.Ic exit
+terminates the parent shell.
+.Pp
+Another variant of substitution are the valsubs (value substitutions)
+.Pf ${\*(Ba\& Ns Ar command Ns \&;}
+which are also executed in the current environment, like funsubs, but
+share their I/O with the parent; instead, they evaluate to whatever
+the, initially empty, expression-local variable
+.Ev REPLY
+is set to within the
+.Ar command Ns No s .
+.Pp
+If a substitution appears outside of double quotes, the results of the
+substitution are generally subject to word or field splitting according to
+the current value of the
+.Ev IFS
+parameter.
+The
+.Ev IFS
+parameter specifies a list of octets which are used to break a string up
+into several words; any octets from the set space, tab, and newline that
+appear in the
+.Ev IFS
+octets are called
+.Dq IFS whitespace .
+Sequences of one or more
+.Ev IFS
+whitespace octets, in combination with zero or one
+.Pf non- Ev IFS
+whitespace octets, delimit a field.
+As a special case, leading and trailing
+.Ev IFS
+whitespace and trailing
+.Ev IFS
+non-whitespace are stripped (i.e. no leading or trailing empty field
+is created by it); leading
+.Pf non- Ev IFS
+whitespace does create an empty field.
+.Pp
+Example: If
+.Ev IFS
+is set to
+.Dq \*(Ltspace\*(Gt: ,
+and VAR is set to
+.Dq \*(Ltspace\*(GtA\*(Ltspace\*(Gt:\*(Ltspace\*(Gt\*(Ltspace\*(GtB::D ,
+the substitution for $VAR results in four fields:
+.Sq A ,
+.Sq B ,
+.Sq
+(an empty field),
+and
+.Sq D .
+Note that if the
+.Ev IFS
+parameter is set to the
+.Dv NULL
+string, no field splitting is done; if the parameter is unset, the default
+value of space, tab, and newline is used.
+.Pp
+Also, note that the field splitting applies only to the immediate result of
+the substitution.
+Using the previous example, the substitution for $VAR:E
+results in the fields:
+.Sq A ,
+.Sq B ,
+.Sq ,
+and
+.Sq D:E ,
+not
+.Sq A ,
+.Sq B ,
+.Sq ,
+.Sq D ,
+and
+.Sq E .
+This behavior is POSIX compliant, but incompatible with some other shell
+implementations which do field splitting on the word which contained the
+substitution or use
+.Dv IFS
+as a general whitespace delimiter.
+.Pp
+The results of substitution are, unless otherwise specified, also subject to
+brace expansion and file name expansion (see the relevant sections below).
+.Pp
+A command substitution is replaced by the output generated by the specified
+command which is run in a subshell.
+For
+.Pf $( Ns Ar command Ns \&)
+and
+.Pf ${\ \& Ar command Ns \&;}
+substitutions, normal quoting rules are used when
+.Ar command
+is parsed; however, for the deprecated
+.Pf \` Ns Ar command Ns \`
+form, a
+.Ql \e
+followed by any of
+.Ql $ ,
+.Ql \` ,
+or
+.Ql \e
+is stripped (a
+.Ql \e
+followed by any other character is unchanged).
+As a special case in command substitutions, a command of the form
+.Pf \*(Lt Ar file
+is interpreted to mean substitute the contents of
+.Ar file .
+Note that
+.Ic $(\*(Ltfoo)
+has the same effect as
+.Ic $(cat foo) .
+.Pp
+Note that some shells do not use a recursive parser for command substitutions,
+leading to failure for certain constructs; to be portable, use as workaround
+.Ql x=$(cat) \*(Lt\*(Lt"EOF"
+(or the newline-keeping
+.Ql x=\*(Lt\*(Lt"EOF"
+extension) instead to merely slurp the string.
+.St -p1003.1
+recommends to use case statements of the form
+.Ql "x=$(case $foo in (bar) echo $bar ;; (*) echo $baz ;; esac)"
+instead, which would work but not serve as example for this portability issue.
+.Bd -literal -offset indent
+x=$(case $foo in bar) echo $bar ;; *) echo $baz ;; esac)
+# above fails to parse on old shells; below is the workaround
+x=$(eval $(cat)) \*(Lt\*(Lt"EOF"
+case $foo in bar) echo $bar ;; *) echo $baz ;; esac
+EOF
+.Ed
+.Pp
+Arithmetic substitutions are replaced by the value of the specified expression.
+For example, the command
+.Ic print $((2+3*4))
+displays 14.
+See
+.Sx Arithmetic expressions
+for a description of an expression.
+.Ss Parameters
+Parameters are shell variables; they can be assigned values and their values
+can be accessed using a parameter substitution.
+A parameter name is either one
+of the special single punctuation or digit character parameters described
+below, or a letter followed by zero or more letters or digits
+.Po
+.Ql _
+counts as a letter
+.Pc .
+The latter form can be treated as arrays by appending an array index of the
+form
+.Op Ar expr
+where
+.Ar expr
+is an arithmetic expression.
+Array indices in
+.Nm
+are limited to the range 0 through 4294967295, inclusive.
+That is, they are a 32-bit unsigned integer.
+.Pp
+Parameter substitutions take the form
+.Pf $ Ns Ar name ,
+.Pf ${ Ns Ar name Ns } ,
+or
+.Sm off
+.Pf ${ Ar name Oo Ar expr Oc }
+.Sm on
+where
+.Ar name
+is a parameter name.
+Substitution of all array elements with
+.Pf ${ Ns Ar name Ns \&[*]}
+and
+.Pf ${ Ns Ar name Ns \&[@]}
+works equivalent to $* and $@ for positional parameters.
+If substitution is performed on a parameter
+(or an array parameter element)
+that is not set, a null string is substituted unless the
+.Ic nounset
+option
+.Po
+.Ic set Fl o Ic nounset
+or
+.Ic set Fl u
+.Pc
+is set, in which case an error occurs.
+.Pp
+Parameters can be assigned values in a number of ways.
+First, the shell implicitly sets some parameters like
+.Ql # ,
+.Ql PWD ,
+and
+.Ql $ ;
+this is the only way the special single character parameters are set.
+Second, parameters are imported from the shell's environment at startup.
+Third, parameters can be assigned values on the command line: for example,
+.Ic FOO=bar
+sets the parameter
+.Dq FOO
+to
+.Dq bar ;
+multiple parameter assignments can be given on a single command line and they
+can be followed by a simple-command, in which case the assignments are in
+effect only for the duration of the command (such assignments are also
+exported; see below for the implications of this).
+Note that both the parameter name and the
+.Ql =
+must be unquoted for the shell to recognise a parameter assignment.
+The construct
+.Ic FOO+=baz
+is also recognised; the old and new values are immediately concatenated.
+The fourth way of setting a parameter is with the
+.Ic export ,
+.Ic global ,
+.Ic readonly ,
+and
+.Ic typeset
+commands; see their descriptions in the
+.Sx Command execution
+section.
+Fifth,
+.Ic for
+and
+.Ic select
+loops set parameters as well as the
+.Ic getopts ,
+.Ic read ,
+and
+.Ic set \-A
+commands.
+Lastly, parameters can be assigned values using assignment operators
+inside arithmetic expressions (see
+.Sx Arithmetic expressions
+below) or using the
+.Sm off
+.Pf ${ Ar name No = Ar value No }
+.Sm on
+form of the parameter substitution (see below).
+.Pp
+Parameters with the export attribute (set using the
+.Ic export
+or
+.Ic typeset Fl x
+commands, or by parameter assignments followed by simple commands) are put in
+the environment (see
+.Xr environ 7 )
+of commands run by the shell as
+.Ar name Ns = Ns Ar value
+pairs.
+The order in which parameters appear in the environment of a command is
+unspecified.
+When the shell starts up, it extracts parameters and their values
+from its environment and automatically sets the export attribute for those
+parameters.
+.Pp
+Modifiers can be applied to the
+.Pf ${ Ns Ar name Ns }
+form of parameter substitution:
+.Bl -tag -width Ds
+.Sm off
+.It ${ Ar name No :\- Ar word No }
+.Sm on
+If
+.Ar name
+is set and not
+.Dv NULL ,
+it is substituted; otherwise,
+.Ar word
+is substituted.
+.Sm off
+.It ${ Ar name No :+ Ar word No }
+.Sm on
+If
+.Ar name
+is set and not
+.Dv NULL ,
+.Ar word
+is substituted; otherwise, nothing is substituted.
+.Sm off
+.It ${ Ar name No := Ar word No }
+.Sm on
+If
+.Ar name
+is set and not
+.Dv NULL ,
+it is substituted; otherwise, it is assigned
+.Ar word
+and the resulting value of
+.Ar name
+is substituted.
+.Sm off
+.It ${ Ar name No :? Ar word No }
+.Sm on
+If
+.Ar name
+is set and not
+.Dv NULL ,
+it is substituted; otherwise,
+.Ar word
+is printed on standard error (preceded by
+.Ar name : )
+and an error occurs (normally causing termination of a shell script, function,
+or script sourced using the
+.Sq \&.
+built-in).
+If
+.Ar word
+is omitted, the string
+.Dq parameter null or not set
+is used instead.
+Currently a bug, if
+.Ar word
+is a variable which expands to the null string, the
+error message is also printed.
+.El
+.Pp
+Note that, for all of the above,
+.Ar word
+is actually considered quoted, and special parsing rules apply.
+The parsing rules also differ on whether the expression is double-quoted:
+.Ar word
+then uses double-quoting rules, except for the double quote itself
+.Pq Sq \&"
+and the closing brace, which, if backslash escaped, gets quote removal applied.
+.Pp
+In the above modifiers, the
+.Ql \&:
+can be omitted, in which case the conditions only depend on
+.Ar name
+being set (as opposed to set and not
+.Dv NULL ) .
+If
+.Ar word
+is needed, parameter, command, arithmetic, and tilde substitution are performed
+on it; if
+.Ar word
+is not needed, it is not evaluated.
+.Pp
+The following forms of parameter substitution can also be used (if
+.Ar name
+is an array, its element #0 will be substituted in a scalar context):
+.Pp
+.Bl -tag -width Ds -compact
+.It Pf ${# Ns Ar name Ns \&}
+The number of positional parameters if
+.Ar name
+is
+.Ql * ,
+.Ql @ ,
+or not specified; otherwise the length
+.Pq in characters
+of the string value of parameter
+.Ar name .
+.Pp
+.It Pf ${# Ns Ar name Ns \&[*]}
+.It Pf ${# Ns Ar name Ns \&[@]}
+The number of elements in the array
+.Ar name .
+.Pp
+.It Pf ${% Ns Ar name Ns \&}
+The width
+.Pq in screen columns
+of the string value of parameter
+.Ar name ,
+or \-1 if
+.Pf ${ Ns Ar name Ns }
+contains a control character.
+.Pp
+.It Pf ${! Ns Ar name Ns }
+The name of the variable referred to by
+.Ar name .
+This will be
+.Ar name
+except when
+.Ar name
+is a name reference (bound variable), created by the
+.Ic nameref
+command (which is an alias for
+.Ic typeset Fl n ) .
+.Pp
+.It Pf ${! Ns Ar name Ns \&[*]}
+.It Pf ${! Ns Ar name Ns \&[@]}
+The names of indices (keys) in the array
+.Ar name .
+.Pp
+.Sm off
+.It Xo
+.Pf ${ Ar name
+.Pf # Ar pattern No }
+.Xc
+.It Xo
+.Pf ${ Ar name
+.Pf ## Ar pattern No }
+.Xc
+.Sm on
+If
+.Ar pattern
+matches the beginning of the value of parameter
+.Ar name ,
+the matched text is deleted from the result of substitution.
+A single
+.Ql #
+results in the shortest match, and two
+of them result in the longest match.
+Cannot be applied to a vector
+.Pq ${*} or ${@} or ${array[*]} or ${array[@]} .
+.Pp
+.Sm off
+.It Xo
+.Pf ${ Ar name
+.Pf % Ar pattern No }
+.Xc
+.It Xo
+.Pf ${ Ar name
+.Pf %% Ar pattern No }
+.Xc
+.Sm on
+Like ${..#..} substitution, but it deletes from the end of the value.
+Cannot be applied to a vector.
+.Pp
+.Sm off
+.It Xo
+.Pf ${ Ar name
+.Pf / Ar pattern / Ar string No }
+.Xc
+.It Xo
+.Pf ${ Ar name
+.Pf // Ar pattern / Ar string No }
+.Xc
+.Sm on
+Like ${..#..} substitution, but it replaces the longest match of
+.Ar pattern ,
+anchored anywhere in the value, with
+.Ar string .
+If
+.Ar pattern
+begins with
+.Ql # ,
+it is anchored at the beginning of the value; if it begins with
+.Ql % ,
+it is anchored at the end.
+Patterns that are empty or consist only of wildcards are invalid.
+A single
+.Ql /
+replaces the first occurence of the search
+.Ar pattern ,
+and two of them replace all occurences.
+If
+.Pf / Ar string
+is omitted, the
+.Ar pattern
+is replaced by the empty string, i.e. deleted.
+Cannot be applied to a vector.
+Inefficiently implemented, may be slow.
+.Pp
+.Sm off
+.It Xo
+.Pf ${ Ar name : Ns Ar pos
+.Pf : Ns Ar len Ns }
+.Xc
+.Sm on
+The first
+.Ar len
+characters of
+.Ar name ,
+starting at position
+.Ar pos ,
+are substituted.
+Both
+.Ar pos
+and
+.Pf : Ns Ar len
+are optional.
+If
+.Ar pos
+is negative, counting starts at the end of the string; if it
+is omitted, it defaults to 0.
+If
+.Ar len
+is omitted or greater than the length of the remaining string,
+all of it is substituted.
+Both
+.Ar pos
+and
+.Ar len
+are evaluated as arithmetic expressions.
+Currently,
+.Ar pos
+must start with a space, opening parenthesis or digit to be recognised.
+Cannot be applied to a vector.
+.Pp
+.It Pf ${ Ns Ar name Ns @#}
+The hash (using the BAFH algorithm) of the expansion of
+.Ar name .
+This is also used internally for the shell's hashtables.
+.Pp
+.It Pf ${ Ns Ar name Ns @Q}
+A quoted expression safe for re-entry, whose value is the value of the
+.Ar name
+parameter, is substituted.
+.El
+.Pp
+Note that
+.Ar pattern
+may need extended globbing pattern
+.Pq @(...) ,
+single
+.Pq \&\*(aq...\&\*(aq
+or double
+.Pq \&"...\&"
+quote escaping unless
+.Fl o Ic sh
+is set.
+.Pp
+The following special parameters are implicitly set by the shell and cannot be
+set directly using assignments:
+.Bl -tag -width "1 .. 9"
+.It Ev \&!
+Process ID of the last background process started.
+If no background processes have been started, the parameter is not set.
+.It Ev \&#
+The number of positional parameters ($1, $2, etc.).
+.It Ev \&$
+The PID of the shell, or the PID of the original shell if it is a subshell.
+Do
+.Em NOT
+use this mechanism for generating temporary file names; see
+.Xr mktemp 1
+instead.
+.It Ev \-
+The concatenation of the current single letter options (see the
+.Ic set
+command below for a list of options).
+.It Ev \&?
+The exit status of the last non-asynchronous command executed.
+If the last command was killed by a signal,
+.Ic $?\&
+is set to 128 plus the signal number.
+.It Ev 0
+The name of the shell, determined as follows:
+the first argument to
+.Nm
+if it was invoked with the
+.Fl c
+option and arguments were given; otherwise the
+.Ar file
+argument, if it was supplied;
+or else the basename the shell was invoked with (i.e.\&
+.Li argv[0] ) .
+.Ev $0
+is also set to the name of the current script or
+the name of the current function, if it was defined with the
+.Ic function
+keyword (i.e. a Korn shell style function).
+.It Ev 1 No .. Ev 9
+The first nine positional parameters that were supplied to the shell, function,
+or script sourced using the
+.Sq \&.
+built-in.
+Further positional parameters may be accessed using
+.Pf ${ Ar number Ns } .
+.It Ev *
+All positional parameters (except 0), i.e. $1, $2, $3, ...
+.br
+If used
+outside of double quotes, parameters are separate words (which are subjected
+to word splitting); if used within double quotes, parameters are separated
+by the first character of the
+.Ev IFS
+parameter (or the empty string if
+.Ev IFS
+is
+.Dv NULL ) .
+.It Ev @
+Same as
+.Ic $* ,
+unless it is used inside double quotes, in which case a separate word is
+generated for each positional parameter.
+If there are no positional parameters, no word is generated.
+.Ic $@
+can be used to access arguments, verbatim, without losing
+.Dv NULL
+arguments or splitting arguments with spaces.
+.El
+.Pp
+The following parameters are set and/or used by the shell:
+.Bl -tag -width "KSH_VERSION"
+.It Ev _
+.Pq underscore
+When an external command is executed by the shell, this parameter is set in the
+environment of the new process to the path of the executed command.
+In interactive use, this parameter is also set in the parent shell to the last
+word of the previous command.
+.It Ev BASHPID
+The PID of the shell or subshell.
+.It Ev CDPATH
+Search path for the
+.Ic cd
+built-in command.
+It works the same way as
+.Ev PATH
+for those directories not beginning with
+.Ql /
+in
+.Ic cd
+commands.
+Note that if
+.Ev CDPATH
+is set and does not contain
+.Sq \&.
+or contains an empty path, the current directory is not searched.
+Also, the
+.Ic cd
+built-in command will display the resulting directory when a match is found
+in any search path other than the empty path.
+.It Ev COLUMNS
+Set to the number of columns on the terminal or window.
+Always set, defaults to 80, unless the
+value as reported by
+.Xr stty 1
+is non-zero and sane enough (minimum is 12x3); similar for
+.Ev LINES .
+This parameter is used by the interactive line editing modes, and by the
+.Ic select ,
+.Ic set \-o ,
+and
+.Ic kill \-l
+commands to format information columns.
+Importing from the environment or unsetting this parameter removes the
+binding to the actual terminal size in favour of the provided value.
+.It Ev ENV
+If this parameter is found to be set after any profile files are executed, the
+expanded value is used as a shell startup file.
+It typically contains function and alias definitions.
+.It Ev ERRNO
+Integer value of the shell's
+.Va errno
+variable.
+It indicates the reason the last system call failed.
+Not yet implemented.
+.It Ev EXECSHELL
+If set, this parameter is assumed to contain the shell that is to be used to
+execute commands that
+.Xr execve 2
+fails to execute and which do not start with a
+.Dq #! Ns Ar shell
+sequence.
+.It Ev FCEDIT
+The editor used by the
+.Ic fc
+command (see below).
+.It Ev FPATH
+Like
+.Ev PATH ,
+but used when an undefined function is executed to locate the file defining the
+function.
+It is also searched when a command can't be found using
+.Ev PATH .
+See
+.Sx Functions
+below for more information.
+.It Ev HISTFILE
+The name of the file used to store command history.
+When assigned to, history is loaded from the specified file.
+Also, several invocations of the shell will share history if their
+.Ev HISTFILE
+parameters all point to the same file.
+.Pp
+.Sy Note :
+If
+.Ev HISTFILE
+isn't set, no history file is used.
+This is different from
+.At
+.Nm ksh .
+.It Ev HISTSIZE
+The number of commands normally stored for history.
+The default is 2047.
+.It Ev HOME
+The default directory for the
+.Ic cd
+command and the value substituted for an unqualified
+.Ic \*(TI
+(see
+.Sx Tilde expansion
+below).
+.It Ev IFS
+Internal field separator, used during substitution and by the
+.Ic read
+command, to split values into distinct arguments; normally set to space, tab,
+and newline.
+See
+.Sx Substitution
+above for details.
+.Pp
+.Sy Note :
+This parameter is not imported from the environment when the shell is
+started.
+.It Ev KSHEGID
+The effective group id of the shell.
+.It Ev KSHGID
+The real group id of the shell.
+.It Ev KSHUID
+The real user id of the shell.
+.It Ev KSH_VERSION
+The name and version of the shell (read-only).
+See also the version commands in
+.Sx Emacs editing mode
+and
+.Sx Vi editing mode
+sections, below.
+.It Ev LINENO
+The line number of the function or shell script that is currently being
+executed.
+.It Ev LINES
+Set to the number of lines on the terminal or window.
+Always set, defaults to 24.
+See
+.Ev COLUMNS .
+.It Ev EPOCHREALTIME
+Time since the epoch, as returned by
+.Xr gettimeofday 2 ,
+formatted as decimal
+.Va tv_sec
+followed by a dot
+.Pq Sq .\&
+and
+.Va tv_usec
+padded to exactly six decimal digits.
+.It Ev OLDPWD
+The previous working directory.
+Unset if
+.Ic cd
+has not successfully changed directories since the shell started, or if the
+shell doesn't know where it is.
+.It Ev OPTARG
+When using
+.Ic getopts ,
+it contains the argument for a parsed option, if it requires one.
+.It Ev OPTIND
+The index of the next argument to be processed when using
+.Ic getopts .
+Assigning 1 to this parameter causes
+.Ic getopts
+to process arguments from the beginning the next time it is invoked.
+.It Ev PATH
+A colon separated list of directories that are searched when looking for
+commands and files sourced using the
+.Sq \&.
+command (see below).
+An empty string resulting from a leading or trailing
+colon, or two adjacent colons, is treated as a
+.Sq \&.
+(the current directory).
+.It Ev PGRP
+The process ID of the shell's process group leader.
+.It Ev PIPESTATUS
+An array containing the errorlevel (exit status) codes,
+one by one, of the last pipeline run in the foreground.
+.It Ev PPID
+The process ID of the shell's parent.
+.It Ev PS1
+The primary prompt for interactive shells.
+Parameter, command, and arithmetic
+substitutions are performed, and
+.Ql \&!
+is replaced with the current command number (see the
+.Ic fc
+command below).
+A literal
+.Ql \&!
+can be put in the prompt by placing
+.Ql !!
+in
+.Ev PS1 .
+.Pp
+The default prompt is
+.Sq $\ \&
+for non-root users,
+.Sq #\ \&
+for root.
+If
+.Nm
+is invoked by root and
+.Ev PS1
+does not contain a
+.Sq #
+character, the default value will be used even if
+.Ev PS1
+already exists in the environment.
+.Pp
+The
+.Nm
+distribution comes with a sample
+.Pa dot.mkshrc
+containing a sophisticated example, but you might like the following one
+(note that ${HOSTNAME:=$(hostname)} and the
+root-vs-user distinguishing clause are (in this example) executed at
+.Ev PS1
+assignment time, while the $USER and $PWD are escaped
+and thus will be evaluated each time a prompt is displayed):
+.Bd -literal
+PS1=\*(aq${USER:=$(id \-un)}\*(aq"@${HOSTNAME:=$(hostname)}:\e$PWD $(
+	if (( USER_ID )); then print \e$; else print \e#; fi) "
+.Ed
+.Pp
+Note that since the command-line editors try to figure out how long the prompt
+is (so they know how far it is to the edge of the screen), escape codes in
+the prompt tend to mess things up.
+You can tell the shell not to count certain
+sequences (such as escape codes) by prefixing your prompt with a
+character (such as Ctrl-A) followed by a carriage return and then delimiting
+the escape codes with this character.
+Any occurences of that character in the prompt are not printed.
+By the way, don't blame me for
+this hack; it's derived from the original
+.Xr ksh88 1 ,
+which did print the delimiter character so you were out of luck
+if you did not have any non-printing characters.
+.Pp
+Since Backslashes and other special characters may be
+interpreted by the shell, to set
+.Ev PS1
+either escape the backslash itself,
+or use double quotes.
+The latter is more practical.
+This is a more complex example,
+avoiding to directly enter special characters (for example with
+.Ic \*(haV
+in the emacs editing mode),
+which embeds the current working directory,
+in reverse video
+.Pq colour would work, too ,
+in the prompt string:
+.Bd -literal -offset indent
+x=$(print \e\e001)
+PS1="$x$(print \e\er)$x$(tput so)$x\e$PWD$x$(tput se)$x\*(Gt "
+.Ed
+.Pp
+Due to a strong suggestion from David G. Korn,
+.Nm
+now also supports the following form:
+.Bd -literal -offset indent
+PS1=$'\e1\er\e1\ee[7m\e1$PWD\e1\ee[0m\e1\*(Gt '
+.Ed
+.It Ev PS2
+Secondary prompt string, by default
+.Sq \*(Gt\ \& ,
+used when more input is needed to complete a command.
+.It Ev PS3
+Prompt used by the
+.Ic select
+statement when reading a menu selection.
+The default is
+.Sq #?\ \& .
+.It Ev PS4
+Used to prefix commands that are printed during execution tracing (see the
+.Ic set Fl x
+command below).
+Parameter, command, and arithmetic substitutions are performed
+before it is printed.
+The default is
+.Sq +\ \& .
+You may want to set it to
+.Sq \&[$EPOCHREALTIME]\ \&
+instead, to include timestamps.
+.It Ev PWD
+The current working directory.
+May be unset or
+.Dv NULL
+if the shell doesn't know where it is.
+.It Ev RANDOM
+Each time
+.Ev RANDOM
+is referenced, it is assigned a number between 0 and 32767 from
+a Linear Congruential PRNG first.
+.It Ev REPLY
+Default parameter for the
+.Ic read
+command if no names are given.
+Also used in
+.Ic select
+loops to store the value that is read from standard input.
+.It Ev SECONDS
+The number of seconds since the shell started or, if the parameter has been
+assigned an integer value, the number of seconds since the assignment plus the
+value that was assigned.
+.It Ev TMOUT
+If set to a positive integer in an interactive shell, it specifies the maximum
+number of seconds the shell will wait for input after printing the primary
+prompt
+.Pq Ev PS1 .
+If the time is exceeded, the shell exits.
+.It Ev TMPDIR
+The directory temporary shell files are created in.
+If this parameter is not
+set, or does not contain the absolute path of a writable directory, temporary
+files are created in
+.Pa /tmp .
+.It Ev USER_ID
+The effective user id of the shell.
+.El
+.Ss Tilde expansion
+Tilde expansion which is done in parallel with parameter substitution, is done
+on words starting with an unquoted
+.Ql \*(TI .
+The characters following the tilde, up to the first
+.Ql / ,
+if any, are assumed to be a login name.
+If the login name is empty,
+.Ql + ,
+or
+.Ql \- ,
+the value of the
+.Ev HOME ,
+.Ev PWD ,
+or
+.Ev OLDPWD
+parameter is substituted, respectively.
+Otherwise, the password file is
+searched for the login name, and the tilde expression is substituted with the
+user's home directory.
+If the login name is not found in the password file or
+if any quoting or parameter substitution occurs in the login name, no
+substitution is performed.
+.Pp
+In parameter assignments
+(such as those preceding a simple-command or those occurring
+in the arguments of
+.Ic alias ,
+.Ic export ,
+.Ic global ,
+.Ic readonly ,
+and
+.Ic typeset ) ,
+tilde expansion is done after any assignment
+(i.e. after the equals sign)
+or after an unquoted colon
+.Pq Sq \&: ;
+login names are also delimited by colons.
+.Pp
+The home directory of previously expanded login names are cached and re-used.
+The
+.Ic alias \-d
+command may be used to list, change, and add to this cache (e.g.\&
+.Ic alias \-d fac=/usr/local/facilities; cd \*(TIfac/bin ) .
+.Ss Brace expansion (alteration)
+Brace expressions take the following form:
+.Bd -unfilled -offset indent
+.Sm off
+.Xo
+.Ar prefix No { Ar str1 No ,...,
+.Ar strN No } Ar suffix
+.Xc
+.Sm on
+.Ed
+.Pp
+The expressions are expanded to
+.Ar N
+words, each of which is the concatenation of
+.Ar prefix ,
+.Ar str Ns i ,
+and
+.Ar suffix
+(e.g.\&
+.Dq a{c,b{X,Y},d}e
+expands to four words:
+.Dq ace ,
+.Dq abXe ,
+.Dq abYe ,
+and
+.Dq ade ) .
+As noted in the example, brace expressions can be nested and the resulting
+words are not sorted.
+Brace expressions must contain an unquoted comma
+.Pq Sq \&,
+for expansion to occur (e.g.\&
+.Ic {}
+and
+.Ic {foo}
+are not expanded).
+Brace expansion is carried out after parameter substitution
+and before file name generation.
+.Ss File name patterns
+A file name pattern is a word containing one or more unquoted
+.Ql \&? ,
+.Ql * ,
+.Ql + ,
+.Ql @ ,
+or
+.Ql \&!
+characters or
+.Dq \&[..]
+sequences.
+Once brace expansion has been performed, the shell replaces file
+name patterns with the sorted names of all the files that match the pattern
+(if no files match, the word is left unchanged).
+The pattern elements have the following meaning:
+.Bl -tag -width Ds
+.It \&?
+Matches any single character.
+.It \&*
+Matches any sequence of octets.
+.It \&[..]
+Matches any of the octets inside the brackets.
+Ranges of octets can be specified by separating two octets by a
+.Ql \-
+(e.g.\&
+.Dq \&[a0\-9]
+matches the letter
+.Sq a
+or any digit).
+In order to represent itself, a
+.Ql \-
+must either be quoted or the first or last octet in the octet list.
+Similarly, a
+.Ql \&]
+must be quoted or the first octet in the list if it is to represent itself
+instead of the end of the list.
+Also, a
+.Ql \&!
+appearing at the start of the list has special meaning (see below), so to
+represent itself it must be quoted or appear later in the list.
+.It \&[!..]
+Like [..],
+except it matches any octet not inside the brackets.
+.Sm off
+.It *( Ar pattern\*(Ba No ...\*(Ba Ar pattern )
+.Sm on
+Matches any string of octets that matches zero or more occurrences of the
+specified patterns.
+Example: The pattern
+.Ic *(foo\*(Babar)
+matches the strings
+.Dq ,
+.Dq foo ,
+.Dq bar ,
+.Dq foobarfoo ,
+etc.
+.Sm off
+.It +( Ar pattern\*(Ba No ...\*(Ba Ar pattern )
+.Sm on
+Matches any string of octets that matches one or more occurrences of the
+specified patterns.
+Example: The pattern
+.Ic +(foo\*(Babar)
+matches the strings
+.Dq foo ,
+.Dq bar ,
+.Dq foobar ,
+etc.
+.Sm off
+.It ?( Ar pattern\*(Ba No ...\*(Ba Ar pattern )
+.Sm on
+Matches the empty string or a string that matches one of the specified
+patterns.
+Example: The pattern
+.Ic ?(foo\*(Babar)
+only matches the strings
+.Dq ,
+.Dq foo ,
+and
+.Dq bar .
+.Sm off
+.It @( Ar pattern\*(Ba No ...\*(Ba Ar pattern )
+.Sm on
+Matches a string that matches one of the specified patterns.
+Example: The pattern
+.Ic @(foo\*(Babar)
+only matches the strings
+.Dq foo
+and
+.Dq bar .
+.Sm off
+.It !( Ar pattern\*(Ba No ...\*(Ba Ar pattern )
+.Sm on
+Matches any string that does not match one of the specified patterns.
+Examples: The pattern
+.Ic !(foo\*(Babar)
+matches all strings except
+.Dq foo
+and
+.Dq bar ;
+the pattern
+.Ic !(*)
+matches no strings; the pattern
+.Ic !(?)*\&
+matches all strings (think about it).
+.El
+.Pp
+Note that complicated globbing, especially with alternatives,
+is slow; using separate comparisons may (or may not) be faster.
+.Pp
+Note that
+.Nm mksh
+.Po and Nm pdksh Pc
+never matches
+.Sq \&.
+and
+.Sq .. ,
+but
+.At
+.Nm ksh ,
+Bourne
+.Nm sh ,
+and GNU
+.Nm bash
+do.
+.Pp
+Note that none of the above pattern elements match either a period
+.Pq Sq \&.
+at the start of a file name or a slash
+.Pq Sq / ,
+even if they are explicitly used in a [..] sequence; also, the names
+.Sq \&.
+and
+.Sq ..
+are never matched, even by the pattern
+.Sq .* .
+.Pp
+If the
+.Ic markdirs
+option is set, any directories that result from file name generation are marked
+with a trailing
+.Ql / .
+.Ss Input/output redirection
+When a command is executed, its standard input, standard output, and standard
+error (file descriptors 0, 1, and 2, respectively) are normally inherited from
+the shell.
+Three exceptions to this are commands in pipelines, for which
+standard input and/or standard output are those set up by the pipeline,
+asynchronous commands created when job control is disabled, for which standard
+input is initially set to be from
+.Pa /dev/null ,
+and commands for which any of the following redirections have been specified:
+.Bl -tag -width XXxxmarker
+.It \*(Gt Ar file
+Standard output is redirected to
+.Ar file .
+If
+.Ar file
+does not exist, it is created; if it does exist, is a regular file, and the
+.Ic noclobber
+option is set, an error occurs; otherwise, the file is truncated.
+Note that this means the command
+.Ic cmd \*(Ltfoo \*(Gtfoo
+will open
+.Ar foo
+for reading and then truncate it when it opens it for writing, before
+.Ar cmd
+gets a chance to actually read
+.Ar foo .
+.It \*(Gt\*(Ba Ar file
+Same as
+.Ic \*(Gt ,
+except the file is truncated, even if the
+.Ic noclobber
+option is set.
+.It \*(Gt\*(Gt Ar file
+Same as
+.Ic \*(Gt ,
+except if
+.Ar file
+exists it is appended to instead of being truncated.
+Also, the file is opened
+in append mode, so writes always go to the end of the file (see
+.Xr open 2 ) .
+.It \*(Lt Ar file
+Standard input is redirected from
+.Ar file ,
+which is opened for reading.
+.It \*(Lt\*(Gt Ar file
+Same as
+.Ic \*(Lt ,
+except the file is opened for reading and writing.
+.It \*(Lt\*(Lt Ar marker
+After reading the command line containing this kind of redirection (called a
+.Dq here document ) ,
+the shell copies lines from the command source into a temporary file until a
+line matching
+.Ar marker
+is read.
+When the command is executed, standard input is redirected from the
+temporary file.
+If
+.Ar marker
+contains no quoted characters, the contents of the temporary file are processed
+as if enclosed in double quotes each time the command is executed, so
+parameter, command, and arithmetic substitutions are performed, along with
+backslash
+.Pq Sq \e
+escapes for
+.Ql $ ,
+.Ql \` ,
+.Ql \e ,
+and
+.Ql \enewline ,
+but not for
+.Ql \&" .
+If multiple here documents are used on the same command line, they are saved in
+order.
+.Pp
+If no
+.Ar marker
+is given, the here document ends at the next
+.Ic \*(Lt\*(Lt
+and substitution will be performed.
+If
+.Ar marker
+is only a set of either single
+.Dq \*(aq\*(aq
+or double
+.Sq \&""
+quotes with nothing in between, the here document ends at the next empty line
+and substitution will not be performed.
+.It \*(Lt\*(Lt\- Ar marker
+Same as
+.Ic \*(Lt\*(Lt ,
+except leading tabs are stripped from lines in the here document.
+.It \*(Lt\*(Lt\*(Lt Ar word
+Same as
+.Ic \*(Lt\*(Lt ,
+except that
+.Ar word
+.Em is
+the here document.
+This is called a here string.
+.It \*(Lt& Ar fd
+Standard input is duplicated from file descriptor
+.Ar fd .
+.Ar fd
+can be a number, indicating the number of an existing file descriptor;
+the letter
+.Ql p ,
+indicating the file descriptor associated with the output of the current
+co-process; or the character
+.Ql \- ,
+indicating standard input is to be closed.
+Note that
+.Ar fd
+is limited to a single digit in most shell implementations.
+.It \*(Gt& Ar fd
+Same as
+.Ic \*(Lt& ,
+except the operation is done on standard output.
+.It &\*(Gt Ar file
+Same as
+.Ic \*(Gt Ar file 2\*(Gt&1 .
+This is a GNU
+.Nm bash
+extension supported by
+.Nm
+which also supports the preceding explicit fd number, for example,
+.Ic 3&\*(Gt Ar file
+is the same as
+.Ic 3\*(Gt Ar file 2\*(Gt&3
+in
+.Nm
+but a syntax error in GNU
+.Nm bash .
+Setting the
+.Fl o Ar posix
+or
+.Fl o Ar sh
+shell options disable parsing of this redirection;
+it's a compatibility feature to legacy scripts, to
+not be used when writing new shell code.
+.It Xo
+.No &\*(Gt\*(Ba Ar file ,
+.No &\*(Gt\*(Gt Ar file ,
+.No &\*(Gt& Ar fd
+.Xc
+Same as
+.Ic \*(Gt\*(Ba Ar file ,
+.Ic \*(Gt\*(Gt Ar file ,
+or
+.Ic \*(Gt& Ar fd ,
+followed by
+.Ic 2\*(Gt&1 ,
+as above.
+These are
+.Nm
+extensions.
+.El
+.Pp
+In any of the above redirections, the file descriptor that is redirected
+(i.e. standard input or standard output)
+can be explicitly given by preceding the
+redirection with a number (portably, only a single digit).
+Parameter, command, and arithmetic
+substitutions, tilde substitutions, and (if the shell is interactive)
+file name generation are all performed on the
+.Ar file ,
+.Ar marker ,
+and
+.Ar fd
+arguments of redirections.
+Note, however, that the results of any file name
+generation are only used if a single file is matched; if multiple files match,
+the word with the expanded file name generation characters is used.
+Note
+that in restricted shells, redirections which can create files cannot be used.
+.Pp
+For simple-commands, redirections may appear anywhere in the command; for
+compound-commands
+.Po
+.Ic if
+statements, etc.
+.Pc ,
+any redirections must appear at the end.
+Redirections are processed after
+pipelines are created and in the order they are given, so the following
+will print an error with a line number prepended to it:
+.Pp
+.D1 $ cat /foo/bar 2\*(Gt&1 \*(Gt/dev/null \*(Ba pr \-n \-t
+.Pp
+File descriptors created by input/output redirections are private to the
+Korn shell, but passed to sub-processes if
+.Fl o Ic posix
+or
+.Fl o Ic sh
+is set.
+.Ss Arithmetic expressions
+Integer arithmetic expressions can be used with the
+.Ic let
+command, inside $((..)) expressions, inside array references (e.g.\&
+.Ar name Ns Bq Ar expr ) ,
+as numeric arguments to the
+.Ic test
+command, and as the value of an assignment to an integer parameter.
+.Pp
+Expressions are calculated using signed arithmetic and the
+.Vt mksh_ari_t
+type (a 32-bit signed integer), unless they begin with a sole
+.Sq #
+character, in which case they use
+.Vt mksh_uari_t
+.Po a 32-bit unsigned integer Pc .
+.Pp
+Expressions may contain alpha-numeric parameter identifiers, array references,
+and integer constants and may be combined with the following C operators
+(listed and grouped in increasing order of precedence):
+.Pp
+Unary operators:
+.Bd -literal -offset indent
++ \- ! \*(TI ++ \-\-
+.Ed
+.Pp
+Binary operators:
+.Bd -literal -offset indent
+,
+= += \-= *= /= %= \*(Lt\*(Lt\*(Lt= \*(Gt\*(Gt\*(Gt= \*(Lt\*(Lt= \*(Gt\*(Gt= &= \*(ha= \*(Ba=
+\*(Ba\*(Ba
+&&
+\*(Ba
+\*(ha
+&
+== !=
+\*(Lt \*(Lt= \*(Gt \*(Gt=
+\*(Lt\*(Lt\*(Lt \*(Gt\*(Gt\*(Gt \*(Lt\*(Lt \*(Gt\*(Gt
++ \-
+* / %
+.Ed
+.Pp
+Ternary operators:
+.Bd -literal -offset indent
+?: (precedence is immediately higher than assignment)
+.Ed
+.Pp
+Grouping operators:
+.Bd -literal -offset indent
+( )
+.Ed
+.Pp
+Integer constants and expressions are calculated using an exactly 32-bit
+wide, signed or unsigned, type with silent wraparound on integer overflow.
+Integer constants may be specified with arbitrary bases using the notation
+.Ar base Ns # Ns Ar number ,
+where
+.Ar base
+is a decimal integer specifying the base, and
+.Ar number
+is a number in the specified base.
+Additionally, base-16 integers may be specified by prefixing them with
+.Sq 0x
+.Pq case-insensitive
+in all forms of arithmetic expressions, except as numeric arguments to the
+.Ic test
+built-in command.
+Prefixing numbers with a sole digit zero
+.Pq Sq 0
+leads to the shell interpreting it as base-8 (octal) integer in
+.Ic posix
+mode
+.Em only ;
+historically, (pd)ksh has never done so either anyway,
+and it's unsafe to do that, but POSIX demands it nowadays.
+As a special
+.Nm mksh
+extension, numbers to the base of one are treated as either (8-bit
+transparent) ASCII or Unicode codepoints, depending on the shell's
+.Ic utf8\-mode
+flag (current setting).
+The
+.At
+.Nm ksh93
+syntax of
+.Dq \*(aqx\*(aq
+instead of
+.Dq 1#x
+is also supported.
+Note that NUL bytes (integral value of zero) cannot be used.
+An unset or empty parameter evaluates to 0 in integer context.
+In Unicode mode, raw octets are mapped into the range EF80..EFFF as in
+OPTU-8, which is in the PUA and has been assigned by CSUR for this use.
+If more than one octet in ASCII mode, or a sequence of more than one
+octet not forming a valid and minimal CESU-8 sequence is passed, the
+behaviour is undefined (usually, the shell aborts with a parse error,
+but rarely, it succeeds, e.g. on the sequence C2 20).
+That's why you should always use ASCII mode unless you know that the
+input is well-formed UTF-8 in the range of 0000..FFFD.
+.Pp
+The operators are evaluated as follows:
+.Bl -tag -width Ds -offset indent
+.It unary +
+Result is the argument (included for completeness).
+.It unary \-
+Negation.
+.It \&!
+Logical NOT;
+the result is 1 if argument is zero, 0 if not.
+.It \*(TI
+Arithmetic (bit-wise) NOT.
+.It ++
+Increment; must be applied to a parameter (not a literal or other expression).
+The parameter is incremented by 1.
+When used as a prefix operator, the result
+is the incremented value of the parameter; when used as a postfix operator, the
+result is the original value of the parameter.
+.It \-\-
+Similar to
+.Ic ++ ,
+except the parameter is decremented by 1.
+.It \&,
+Separates two arithmetic expressions; the left-hand side is evaluated first,
+then the right.
+The result is the value of the expression on the right-hand side.
+.It =
+Assignment; the variable on the left is set to the value on the right.
+.It Xo
+.No += \-= *= /= %= \*(Lt\*(Lt\*(Lt= \*(Gt\*(Gt\*(Gt=
+.No \*(Lt\*(Lt= \*(Gt\*(Gt= &= \*(ha= \*(Ba=
+.Xc
+Assignment operators.
+.Sm off
+.Ao Ar var Ac Xo
+.Aq Ar op
+.No = Aq Ar expr
+.Xc
+.Sm on
+is the same as
+.Sm off
+.Ao Ar var Ac Xo
+.No = Aq Ar var
+.Aq Ar op
+.Aq Ar expr ,
+.Xc
+.Sm on
+with any operator precedence in
+.Aq Ar expr
+preserved.
+For example,
+.Dq var1 *= 5 + 3
+is the same as specifying
+.Dq var1 = var1 * (5 + 3) .
+.It \*(Ba\*(Ba
+Logical OR;
+the result is 1 if either argument is non-zero, 0 if not.
+The right argument is evaluated only if the left argument is zero.
+.It &&
+Logical AND;
+the result is 1 if both arguments are non-zero, 0 if not.
+The right argument is evaluated only if the left argument is non-zero.
+.It \*(Ba
+Arithmetic (bit-wise) OR.
+.It \*(ha
+Arithmetic (bit-wise) XOR
+(exclusive-OR).
+.It &
+Arithmetic (bit-wise) AND.
+.It ==
+Equal; the result is 1 if both arguments are equal, 0 if not.
+.It !=
+Not equal; the result is 0 if both arguments are equal, 1 if not.
+.It \*(Lt
+Less than; the result is 1 if the left argument is less than the right, 0 if
+not.
+.It \*(Lt= \*(Gt \*(Gt=
+Less than or equal, greater than or equal, greater than.
+See
+.Ic \*(Lt .
+.It \*(Lt\*(Lt\*(Lt \*(Gt\*(Gt\*(Gt
+Rotate left (right); the result is similar to shift (see
+.Ic \*(Lt\*(Lt )
+except that the bits shifted out at one end are shifted in
+at the other end, instead of zero or sign bits.
+.It \*(Lt\*(Lt \*(Gt\*(Gt
+Shift left (right); the result is the left argument with its bits shifted left
+(right) by the amount given in the right argument.
+.It + \- * /
+Addition, subtraction, multiplication, and division.
+.It %
+Remainder; the result is the remainder of the division of the left argument by
+the right.
+.It Xo
+.Sm off
+.Aq Ar arg1 ?
+.Aq Ar arg2 :
+.Aq Ar arg3
+.Sm on
+.Xc
+If
+.Aq Ar arg1
+is non-zero, the result is
+.Aq Ar arg2 ;
+otherwise the result is
+.Aq Ar arg3 .
+The non-result argument is not evaluated.
+.El
+.Ss Co-processes
+A co-process (which is a pipeline created with the
+.Sq \*(Ba&
+operator) is an asynchronous process that the shell can both write to (using
+.Ic print \-p )
+and read from (using
+.Ic read \-p ) .
+The input and output of the co-process can also be manipulated using
+.Ic \*(Gt&p
+and
+.Ic \*(Lt&p
+redirections, respectively.
+Once a co-process has been started, another can't
+be started until the co-process exits, or until the co-process's input has been
+redirected using an
+.Ic exec Ar n Ns Ic \*(Gt&p
+redirection.
+If a co-process's input is redirected in this way, the next
+co-process to be started will share the output with the first co-process,
+unless the output of the initial co-process has been redirected using an
+.Ic exec Ar n Ns Ic \*(Lt&p
+redirection.
+.Pp
+Some notes concerning co-processes:
+.Bl -bullet
+.It
+The only way to close the co-process's input (so the co-process reads an
+end-of-file) is to redirect the input to a numbered file descriptor and then
+close that file descriptor:
+.Ic exec 3\*(Gt&p; exec 3\*(Gt&\-
+.It
+In order for co-processes to share a common output, the shell must keep the
+write portion of the output pipe open.
+This means that end-of-file will not be
+detected until all co-processes sharing the co-process's output have exited
+(when they all exit, the shell closes its copy of the pipe).
+This can be
+avoided by redirecting the output to a numbered file descriptor (as this also
+causes the shell to close its copy).
+Note that this behaviour is slightly
+different from the original Korn shell which closes its copy of the write
+portion of the co-process output when the most recently started co-process
+(instead of when all sharing co-processes) exits.
+.It
+.Ic print \-p
+will ignore
+.Dv SIGPIPE
+signals during writes if the signal is not being trapped or ignored; the same
+is true if the co-process input has been duplicated to another file descriptor
+and
+.Ic print \-u Ns Ar n
+is used.
+.El
+.Ss Functions
+Functions are defined using either Korn shell
+.Ic function Ar function-name
+syntax or the Bourne/POSIX shell
+.Ar function-name Ns \&()
+syntax (see below for the difference between the two forms).
+Functions are like
+.Li .\(hyscripts
+(i.e. scripts sourced using the
+.Sq \&.
+built-in)
+in that they are executed in the current environment.
+However, unlike
+.Li .\(hyscripts ,
+shell arguments (i.e. positional parameters $1, $2, etc.)\&
+are never visible inside them.
+When the shell is determining the location of a command, functions
+are searched after special built-in commands, before builtins and the
+.Ev PATH
+is searched.
+.Pp
+An existing function may be deleted using
+.Ic unset Fl f Ar function-name .
+A list of functions can be obtained using
+.Ic typeset +f
+and the function definitions can be listed using
+.Ic typeset \-f .
+The
+.Ic autoload
+command (which is an alias for
+.Ic typeset \-fu )
+may be used to create undefined functions: when an undefined function is
+executed, the shell searches the path specified in the
+.Ev FPATH
+parameter for a file with the same name as the function which, if found, is
+read and executed.
+If after executing the file the named function is found to
+be defined, the function is executed; otherwise, the normal command search is
+continued (i.e. the shell searches the regular built-in command table and
+.Ev PATH ) .
+Note that if a command is not found using
+.Ev PATH ,
+an attempt is made to autoload a function using
+.Ev FPATH
+(this is an undocumented feature of the original Korn shell).
+.Pp
+Functions can have two attributes,
+.Dq trace
+and
+.Dq export ,
+which can be set with
+.Ic typeset \-ft
+and
+.Ic typeset \-fx ,
+respectively.
+When a traced function is executed, the shell's
+.Ic xtrace
+option is turned on for the function's duration.
+The
+.Dq export
+attribute of functions is currently not used.
+In the original Korn shell,
+exported functions are visible to shell scripts that are executed.
+.Pp
+Since functions are executed in the current shell environment, parameter
+assignments made inside functions are visible after the function completes.
+If this is not the desired effect, the
+.Ic typeset
+command can be used inside a function to create a local parameter.
+Note that
+.At
+.Nm ksh93
+uses static scoping (one global scope, one local scope per function)
+and allows local variables only on Korn style functions, whereas
+.Nm mksh
+uses dynamic scoping (nested scopes of varying locality).
+Note that special parameters (e.g.\&
+.Ic \&$$ , $! )
+can't be scoped in this way.
+.Pp
+The exit status of a function is that of the last command executed in the
+function.
+A function can be made to finish immediately using the
+.Ic return
+command; this may also be used to explicitly specify the exit status.
+.Pp
+Functions defined with the
+.Ic function
+reserved word are treated differently in the following ways from functions
+defined with the
+.Ic \&()
+notation:
+.Bl -bullet
+.It
+The $0 parameter is set to the name of the function
+(Bourne-style functions leave $0 untouched).
+.It
+Parameter assignments preceding function calls are not kept in the shell
+environment (executing Bourne-style functions will keep assignments).
+.It
+.Ev OPTIND
+is saved/reset and restored on entry and exit from the function so
+.Ic getopts
+can be used properly both inside and outside the function (Bourne-style
+functions leave
+.Ev OPTIND
+untouched, so using
+.Ic getopts
+inside a function interferes with using
+.Ic getopts
+outside the function).
+.It
+Bourne-style function definitions take precedence over alias dereferences
+and remove alias definitions upon encounter, while aliases take precedence
+over Korn-style functions.
+.El
+.Pp
+In the future, the following differences may also be added:
+.Bl -bullet
+.It
+A separate trap/signal environment will be used during the execution of
+functions.
+This will mean that traps set inside a function will not affect the
+shell's traps and signals that are not ignored in the shell (but may be
+trapped) will have their default effect in a function.
+.It
+The EXIT trap, if set in a function, will be executed after the function
+returns.
+.El
+.Ss Command execution
+After evaluation of command-line arguments, redirections, and parameter
+assignments, the type of command is determined: a special built-in command,
+a function, a normal builtin, or the name of a file to execute found using the
+.Ev PATH
+parameter.
+The checks are made in the above order.
+Special built-in commands differ from other commands in that the
+.Ev PATH
+parameter is not used to find them, an error during their execution can
+cause a non-interactive shell to exit, and parameter assignments that are
+specified before the command are kept after the command completes.
+Regular built-in commands are different only in that the
+.Ev PATH
+parameter is not used to find them.
+.Pp
+The original
+.Nm ksh
+and POSIX differ somewhat in which commands are considered
+special or regular.
+.Pp
+POSIX special built-in utilities:
+.Pp
+.Ic \&. , \&: , break , continue ,
+.Ic eval , exec , exit , export ,
+.Ic readonly , return , set , shift ,
+.Ic times , trap , unset
+.Pp
+Additional
+.Nm
+commands keeping assignments:
+.Pp
+.Ic builtin , global , typeset , wait
+.Pp
+Builtins that are not special:
+.Pp
+.Ic [ , alias , bg , bind ,
+.Ic cat , cd , command , echo ,
+.Ic false , fc , fg , getopts ,
+.Ic jobs , kill , let , mknod ,
+.Ic print , pwd , read , realpath ,
+.Ic rename , sleep , suspend , test ,
+.Ic true , ulimit , umask , unalias ,
+.Ic whence
+.Pp
+Once the type of command has been determined, any command-line parameter
+assignments are performed and exported for the duration of the command.
+.Pp
+The following describes the special and regular built-in commands:
+.Pp
+.Bl -tag -width false -compact
+.It Ic \&. Ar file Op Ar arg ...
+This is called the
+.Dq dot
+command.
+Execute the commands in
+.Ar file
+in the current environment.
+The file is searched for in the directories of
+.Ev PATH .
+If arguments are given, the positional parameters may be used to access them
+while
+.Ar file
+is being executed.
+If no arguments are given, the positional parameters are
+those of the environment the command is used in.
+.Pp
+.It Ic \&: Op Ar ...
+The null command.
+Exit status is set to zero.
+.Pp
+.It Ic \&[ Ar expression Ic \&]
+See
+.Ic test .
+.Pp
+.It Xo Ic alias
+.Oo Fl d \*(Ba t Oo Fl r Oc \*(Ba
+.Cm +\-x Oc
+.Op Fl p
+.Op Cm +
+.Oo Ar name
+.Op Ns = Ns Ar value
+.Ar ... Oc
+.Xc
+Without arguments,
+.Ic alias
+lists all aliases.
+For any name without a value, the existing alias is listed.
+Any name with a value defines an alias (see
+.Sx Aliases
+above).
+.Pp
+When listing aliases, one of two formats is used.
+Normally, aliases are listed as
+.Ar name Ns = Ns Ar value ,
+where
+.Ar value
+is quoted.
+If options were preceded with
+.Ql + ,
+or a lone
+.Ql +
+is given on the command line, only
+.Ar name
+is printed.
+.Pp
+The
+.Fl d
+option causes directory aliases which are used in tilde expansion to be
+listed or set (see
+.Sx Tilde expansion
+above).
+.Pp
+If the
+.Fl p
+option is used, each alias is prefixed with the string
+.Dq alias\ \& .
+.Pp
+The
+.Fl t
+option indicates that tracked aliases are to be listed/set (values specified on
+the command line are ignored for tracked aliases).
+The
+.Fl r
+option indicates that all tracked aliases are to be reset.
+.Pp
+The
+.Fl x
+option sets
+.Pq Ic +x No clears
+the export attribute of an alias, or, if no names are given, lists the aliases
+with the export attribute (exporting an alias has no effect).
+.Pp
+.It Ic bg Op Ar job ...
+Resume the specified stopped job(s) in the background.
+If no jobs are specified,
+.Ic %+
+is assumed.
+See
+.Sx Job control
+below for more information.
+.Pp
+.It Ic bind Op Fl l
+The current bindings are listed.
+If the
+.Fl l
+flag is given,
+.Ic bind
+instead lists the names of the functions to which keys may be bound.
+See
+.Sx Emacs editing mode
+for more information.
+.Pp
+.It Xo Ic bind Op Fl m
+.Ar string Ns = Ns Op Ar substitute
+.Ar ...
+.Xc
+.It Xo Ic bind
+.Ar string Ns = Ns Op Ar editing-command
+.Ar ...
+.Xc
+The specified editing command is bound to the given
+.Ar string ,
+which should consist of a control character
+optionally preceded by one of the two prefix characters
+and optionally succeded by a tilde character.
+Future input of the
+.Ar string
+will cause the editing command to be immediately invoked.
+If the
+.Fl m
+flag is given, the specified input
+.Ar string
+will afterwards be immediately replaced by the given
+.Ar substitute
+string which may contain editing commands but not other macros.
+If a tilde postfix is given, a tilde trailing the one or
+two prefices and the control character is ignored, any
+other trailing character will be processed afterwards.
+.Pp
+Control characters may be written using caret notation
+i.e. \*(haX represents Ctrl-X.
+Note that although only two prefix characters (usually ESC and \*(haX)
+are supported, some multi-character sequences can be supported.
+.Pp
+The following default bindings show how the arrow keys, the home, end and
+delete key on a BSD wsvt25, xterm\-xfree86 or GNU screen terminal are bound
+(of course some escape sequences won't work out quite this nicely):
+.Bd -literal -offset indent
+bind \*(aq\*(haX\*(aq=prefix\-2
+bind \*(aq\*(ha[[\*(aq=prefix\-2
+bind \*(aq\*(haXA\*(aq=up\-history
+bind \*(aq\*(haXB\*(aq=down\-history
+bind \*(aq\*(haXC\*(aq=forward\-char
+bind \*(aq\*(haXD\*(aq=backward\-char
+bind \*(aq\*(haX1\*(TI\*(aq=beginning\-of\-line
+bind \*(aq\*(haX7\*(TI\*(aq=beginning\-of\-line
+bind \*(aq\*(haXH\*(aq=beginning\-of\-line
+bind \*(aq\*(haX4\*(TI\*(aq=end\-of\-line
+bind \*(aq\*(haX8\*(TI\*(aq=end\-of\-line
+bind \*(aq\*(haXF\*(aq=end\-of\-line
+bind \*(aq\*(haX3\*(TI\*(aq=delete\-char\-forward
+.Ed
+.Pp
+.It Ic break Op Ar level
+Exit the
+.Ar level Ns th
+inner-most
+.Ic for ,
+.Ic select ,
+.Ic until ,
+or
+.Ic while
+loop.
+.Ar level
+defaults to 1.
+.Pp
+.It Xo
+.Ic builtin
+.Op Fl \-
+.Ar command Op Ar arg ...
+.Xc
+Execute the built-in command
+.Ar command .
+.Pp
+.It Xo
+.Ic cat
+.Op Fl u
+.Op Ar
+.Xc
+Read files sequentially, in command line order, and write them to
+standard output.
+If a
+.Ar file
+is a single dash
+.Pq Sq -
+or absent, read from standard input.
+Unless compiled with
+.Dv MKSH_NO_EXTERNAL_CAT ,
+if any options are given, an external
+.Xr cat 1
+utility is invoked instead if called from the shell.
+For direct builtin calls, the
+.Tn POSIX
+.Fl u
+option is supported as a no-op.
+.Pp
+.It Xo
+.Ic cd
+.Op Fl L
+.Op Ar dir
+.Xc
+.It Xo
+.Ic cd
+.Fl P Op Fl e
+.Op Ar dir
+.Xc
+.It Xo
+.Ic chdir
+.Op Fl eLP
+.Op Ar dir
+.Xc
+Set the working directory to
+.Ar dir .
+If the parameter
+.Ev CDPATH
+is set, it lists the search path for the directory containing
+.Ar dir .
+A
+.Dv NULL
+path means the current directory.
+If
+.Ar dir
+is found in any component of the
+.Ev CDPATH
+search path other than the
+.Dv NULL
+path, the name of the new working directory will be written to standard output.
+If
+.Ar dir
+is missing, the home directory
+.Ev HOME
+is used.
+If
+.Ar dir
+is
+.Ql \- ,
+the previous working directory is used (see the
+.Ev OLDPWD
+parameter).
+.Pp
+If the
+.Fl L
+option (logical path) is used or if the
+.Ic physical
+option isn't set (see the
+.Ic set
+command below), references to
+.Sq ..
+in
+.Ar dir
+are relative to the path used to get to the directory.
+If the
+.Fl P
+option (physical path) is used or if the
+.Ic physical
+option is set,
+.Sq ..
+is relative to the filesystem directory tree.
+The
+.Ev PWD
+and
+.Ev OLDPWD
+parameters are updated to reflect the current and old working directory,
+respectively.
+If the
+.Fl e
+option is set for physical filesystem traversal, and
+.Ev PWD
+could not be set, the exit code is 1; greater than 1 if an
+error occurred, 0 otherwise.
+.Pp
+.It Xo
+.Ic cd
+.Op Fl eLP
+.Ar old new
+.Xc
+.It Xo
+.Ic chdir
+.Op Fl eLP
+.Ar old new
+.Xc
+The string
+.Ar new
+is substituted for
+.Ar old
+in the current directory, and the shell attempts to change to the new
+directory.
+.Pp
+.It Xo
+.Ic command
+.Op Fl pVv
+.Ar cmd
+.Op Ar arg ...
+.Xc
+If neither the
+.Fl v
+nor
+.Fl V
+option is given,
+.Ar cmd
+is executed exactly as if
+.Ic command
+had not been specified, with two exceptions:
+firstly,
+.Ar cmd
+cannot be a shell function;
+and secondly, special built-in commands lose their specialness
+(i.e. redirection and utility errors do not cause the shell to
+exit, and command assignments are not permanent).
+.Pp
+If the
+.Fl p
+option is given, a default search path is used instead of the current value of
+.Ev PATH ,
+the actual value of which is system dependent.
+.Pp
+If the
+.Fl v
+option is given, instead of executing
+.Ar cmd ,
+information about what would be executed is given (and the same is done for
+.Ar arg ... ) .
+For special and regular built-in commands and functions, their names are simply
+printed; for aliases, a command that defines them is printed; and for commands
+found by searching the
+.Ev PATH
+parameter, the full path of the command is printed.
+If no command is found
+(i.e. the path search fails), nothing is printed and
+.Ic command
+exits with a non-zero status.
+The
+.Fl V
+option is like the
+.Fl v
+option, except it is more verbose.
+.Pp
+.It Ic continue Op Ar level
+Jumps to the beginning of the
+.Ar level Ns th
+inner-most
+.Ic for ,
+.Ic select ,
+.Ic until ,
+or
+.Ic while
+loop.
+.Ar level
+defaults to 1.
+.Pp
+.It Xo
+.Ic echo
+.Op Fl Een
+.Op Ar arg ...
+.Xc
+.Em Warning:
+this utility is not portable; use the Korn shell builtin
+.Ic print
+instead.
+.Pp
+Prints its arguments (separated by spaces) followed by a newline, to the
+standard output.
+The newline is suppressed if any of the arguments contain the
+backslash sequence
+.Ql \ec .
+See the
+.Ic print
+command below for a list of other backslash sequences that are recognised.
+.Pp
+The options are provided for compatibility with
+.Bx
+shell scripts.
+The
+.Fl n
+option suppresses the trailing newline,
+.Fl e
+enables backslash interpretation (a no-op, since this is normally done), and
+.Fl E
+suppresses backslash interpretation.
+.Pp
+If the
+.Ic posix
+or
+.Ic sh
+option is set or this is a direct builtin call, only the first argument
+is treated as an option, and only if it is exactly
+.Dq Fl n .
+Backslash interpretation is disabled.
+.Pp
+.It Ic eval Ar command ...
+The arguments are concatenated (with spaces between them) to form a single
+string which the shell then parses and executes in the current environment.
+.Pp
+.It Xo
+.Ic exec
+.Op Ar command Op Ar arg ...
+.Xc
+The command is executed without forking, replacing the shell process.
+.Pp
+If no command is given except for I/O redirection, the I/O redirection is
+permanent and the shell is
+not replaced.
+Any file descriptors greater than 2 which are opened or
+.Xr dup 2 Ns 'd
+in this way are not made available to other executed commands (i.e. commands
+that are not built-in to the shell).
+Note that the Bourne shell differs here;
+it does pass these file descriptors on.
+.Pp
+.It Ic exit Op Ar status
+The shell exits with the specified exit status.
+If
+.Ar status
+is not specified, the exit status is the current value of the
+.Ic $?\&
+parameter.
+.Pp
+.It Xo
+.Ic export
+.Op Fl p
+.Op Ar parameter Ns Op = Ns Ar value
+.Xc
+Sets the export attribute of the named parameters.
+Exported parameters are passed in the environment to executed commands.
+If values are specified, the named parameters are also assigned.
+.Pp
+If no parameters are specified, all parameters with the export attribute
+set are printed one per line; either their names, or, if a
+.Ql \-
+with no option letter is specified, name=value pairs, or, with
+.Fl p ,
+.Ic export
+commands suitable for re-entry.
+.Pp
+.It Ic false
+A command that exits with a non-zero status.
+.Pp
+.It Xo
+.Ic fc
+.Oo Fl e Ar editor \*(Ba
+.Fl l Op Fl n Oc
+.Op Fl r
+.Op Ar first Op Ar last
+.Xc
+.Ar first
+and
+.Ar last
+select commands from the history.
+Commands can be selected by history number
+(negative numbers go backwards from the current, most recent, line)
+or a string specifying the most recent command starting with that string.
+The
+.Fl l
+option lists the command on standard output, and
+.Fl n
+inhibits the default command numbers.
+The
+.Fl r
+option reverses the order of the list.
+Without
+.Fl l ,
+the selected commands are edited by the editor specified with the
+.Fl e
+option, or if no
+.Fl e
+is specified, the editor specified by the
+.Ev FCEDIT
+parameter (if this parameter is not set,
+.Pa /bin/ed
+is used), and then executed by the shell.
+.Pp
+.It Xo
+.Ic fc
+.Cm \-e \- \*(Ba Fl s
+.Op Fl g
+.Op Ar old Ns = Ns Ar new
+.Op Ar prefix
+.Xc
+Re-execute the selected command (the previous command by default) after
+performing the optional substitution of
+.Ar old
+with
+.Ar new .
+If
+.Fl g
+is specified, all occurrences of
+.Ar old
+are replaced with
+.Ar new .
+The meaning of
+.Cm \-e \-
+and
+.Fl s
+is identical: re-execute the selected command without invoking an editor.
+This command is usually accessed with the predefined:
+.Ic alias r=\*(aqfc \-e \-\*(aq
+.Pp
+.It Ic fg Op Ar job ...
+Resume the specified job(s) in the foreground.
+If no jobs are specified,
+.Ic %+
+is assumed.
+See
+.Sx Job control
+below for more information.
+.Pp
+.It Xo
+.Ic getopts
+.Ar optstring name
+.Op Ar arg ...
+.Xc
+Used by shell procedures to parse the specified arguments (or positional
+parameters, if no arguments are given) and to check for legal options.
+.Ar optstring
+contains the option letters that
+.Ic getopts
+is to recognise.
+If a letter is followed by a colon, the option is expected to
+have an argument.
+Options that do not take arguments may be grouped in a single argument.
+If an option takes an argument and the option character is not the
+last character of the argument it is found in, the remainder of the argument is
+taken to be the option's argument; otherwise, the next argument is the option's
+argument.
+.Pp
+Each time
+.Ic getopts
+is invoked, it places the next option in the shell parameter
+.Ar name
+and the index of the argument to be processed by the next call to
+.Ic getopts
+in the shell parameter
+.Ev OPTIND .
+If the option was introduced with a
+.Ql + ,
+the option placed in
+.Ar name
+is prefixed with a
+.Ql + .
+When an option requires an argument,
+.Ic getopts
+places it in the shell parameter
+.Ev OPTARG .
+.Pp
+When an illegal option or a missing option argument is encountered, a question
+mark or a colon is placed in
+.Ar name
+(indicating an illegal option or missing argument, respectively) and
+.Ev OPTARG
+is set to the option character that caused the problem.
+Furthermore, if
+.Ar optstring
+does not begin with a colon, a question mark is placed in
+.Ar name ,
+.Ev OPTARG
+is unset, and an error message is printed to standard error.
+.Pp
+When the end of the options is encountered,
+.Ic getopts
+exits with a non-zero exit status.
+Options end at the first (non-option
+argument) argument that does not start with a
+.Ql \- ,
+or when a
+.Ql \-\-
+argument is encountered.
+.Pp
+Option parsing can be reset by setting
+.Ev OPTIND
+to 1 (this is done automatically whenever the shell or a shell procedure is
+invoked).
+.Pp
+Warning: Changing the value of the shell parameter
+.Ev OPTIND
+to a value other than 1, or parsing different sets of arguments without
+resetting
+.Ev OPTIND ,
+may lead to unexpected results.
+.Pp
+.It global Ar ...
+See
+.Ic typeset .
+.Pp
+.It Xo
+.Ic hash
+.Op Fl r
+.Op Ar name ...
+.Xc
+Without arguments, any hashed executable command pathnames are listed.
+The
+.Fl r
+option causes all hashed commands to be removed from the hash table.
+Each
+.Ar name
+is searched as if it were a command name and added to the hash table if it is
+an executable command.
+.Pp
+.It Xo
+.Ic jobs
+.Op Fl lnp
+.Op Ar job ...
+.Xc
+Display information about the specified job(s); if no jobs are specified, all
+jobs are displayed.
+The
+.Fl n
+option causes information to be displayed only for jobs that have changed
+state since the last notification.
+If the
+.Fl l
+option is used, the process ID of each process in a job is also listed.
+The
+.Fl p
+option causes only the process group of each job to be printed.
+See
+.Sx Job control
+below for the format of
+.Ar job
+and the displayed job.
+.Pp
+.It Xo
+.Ic kill
+.Oo Fl s Ar signame \*(Ba
+.No \- Ns Ar signum \*(Ba
+.No \- Ns Ar signame Oc
+.No { Ar job \*(Ba pid \*(Ba pgrp No }
+.Ar ...
+.Xc
+Send the specified signal to the specified jobs, process IDs, or process
+groups.
+If no signal is specified, the
+.Dv TERM
+signal is sent.
+If a job is specified, the signal is sent to the job's process group.
+See
+.Sx Job control
+below for the format of
+.Ar job .
+.Pp
+.It Xo
+.Ic kill
+.Fl l
+.Op Ar exit-status ...
+.Xc
+Print the signal name corresponding to
+.Ar exit-status .
+If no arguments are specified, a list of all the signals, their numbers, and
+a short description of them are printed.
+.Pp
+.It Ic let Op Ar expression ...
+Each expression is evaluated (see
+.Sx Arithmetic expressions
+above).
+If all expressions are successfully evaluated, the exit status is 0 (1)
+if the last expression evaluated to non-zero (zero).
+If an error occurs during
+the parsing or evaluation of an expression, the exit status is greater than 1.
+Since expressions may need to be quoted,
+.No \&(( Ar expr No ))
+is syntactic sugar for
+.No let \&" Ns Ar expr Ns \&" .
+.Pp
+.It Ic let]
+Internally used alias for
+.Ic let .
+.Pp
+.It Xo
+.Ic mknod
+.Op Fl m Ar mode
+.Ar name
+.Cm b\*(Bac
+.Ar major minor
+.Xc
+.It Xo
+.Ic mknod
+.Op Fl m Ar mode
+.Ar name
+.Cm p
+.Xc
+Create a device special file.
+The file type may be
+.Cm b
+(block type device),
+.Cm c
+(character type device),
+or
+.Cm p
+.Pq named pipe , Tn FIFO .
+The file created may be modified according to its
+.Ar mode
+(via the
+.Fl m
+option),
+.Ar major
+(major device number),
+and
+.Ar minor
+(minor device number).
+.Pp
+See
+.Xr mknod 8
+for further information.
+.Pp
+.It Xo
+.Ic print
+.Oo Fl nprsu Ns Oo Ar n Oc \*(Ba
+.Fl R Op Fl en Oc
+.Op Ar argument ...
+.Xc
+.Ic print
+prints its arguments on the standard output, separated by spaces and
+terminated with a newline.
+The
+.Fl n
+option suppresses the newline.
+By default, certain C escapes are translated.
+These include these mentioned in
+.Sx Backslash expansion
+above, as well as
+.Ql \ec ,
+which is equivalent to using the
+.Fl n
+option.
+Backslash expansion may be inhibited with the
+.Fl r
+option.
+The
+.Fl s
+option prints to the history file instead of standard output; the
+.Fl u
+option prints to file descriptor
+.Ar n
+.Po
+.Ar n
+defaults to 1 if omitted
+.Pc ;
+and the
+.Fl p
+option prints to the co-process (see
+.Sx Co-processes
+above).
+.Pp
+The
+.Fl R
+option is used to emulate, to some degree, the
+.Bx
+.Xr echo 1
+command which does not process
+.Ql \e
+sequences unless the
+.Fl e
+option is given.
+As above, the
+.Fl n
+option suppresses the trailing newline.
+.Pp
+.It Ic printf Ar format Op Ar arguments ...
+Formatted output.
+Approximately the same as the
+.Xr printf 1 ,
+utility, except it uses the same
+.Sx Backslash expansion
+and I/O code and does hot handle floating point as the rest of
+.Nm mksh .
+This is not normally part of
+.Nm mksh ;
+however, distributors may have added this as builtin as a speed hack.
+Do not use in new code.
+.Pp
+.It Ic pwd Op Fl LP
+Print the present working directory.
+If the
+.Fl L
+option is used or if the
+.Ic physical
+option isn't set (see the
+.Ic set
+command below), the logical path is printed (i.e. the path used to
+.Ic cd
+to the current directory).
+If the
+.Fl P
+option (physical path) is used or if the
+.Ic physical
+option is set, the path determined from the filesystem (by following
+.Sq ..
+directories to the root directory) is printed.
+.Pp
+.It Xo
+.Ic read
+.Op Fl A \*(Ba Fl a
+.Op Fl d Ar x
+.Oo Fl N Ar z \*(Ba
+.Fl n Ar z Oc
+.Oo Fl p \*(Ba
+.Fl u Ns Op Ar n
+.Oc Op Fl t Ar n
+.Op Fl rs
+.Op Ar p ...
+.Xc
+Reads a line of input, separates the input into fields using the
+.Ev IFS
+parameter (see
+.Sx Substitution
+above), and assigns each field to the specified parameters
+.Ar p .
+If no parameters are specified, the
+.Ev REPLY
+parameter is used to store the result.
+With the
+.Fl A
+and
+.Fl a
+options, only no or one parameter is accepted.
+If there are more parameters than fields, the extra parameters are set to
+the empty string or 0; if there are more fields than parameters, the last
+parameter is assigned the remaining fields (including the word separators).
+.Pp
+The options are as follows:
+.Bl -tag -width XuXnX
+.It Fl A
+Store the result into the parameter
+.Ar p
+(or
+.Ev REPLY )
+as array of words.
+.It Fl a
+Store the result without word splitting into the parameter
+.Ar p
+(or
+.Ev REPLY )
+as array of characters (wide characters if the
+.Ic utf8\-mode
+option is enacted, octets otherwise).
+.It Fl d Ar x
+Use the first byte of
+.Ar x ,
+.Dv NUL
+if empty, instead of the ASCII newline character as input line delimiter.
+.It Fl N Ar z
+Instead of reading till end-of-line, read exactly
+.Ar z
+bytes; less if EOF or a timeout occurs.
+.It Fl n Ar z
+Instead of reading till end-of-line, read up to
+.Ar z
+bytes but return as soon as any bytes are read, e.g.\& from a
+slow terminal device, or if EOF or a timeout occurs.
+.It Fl p
+Read from the currently active co-process, see
+.Sx Co-processes
+above for details on this.
+.It Fl u Ns Op Ar n
+Read from the file descriptor
+.Ar n
+(defaults to 0, i.e.\& standard input).
+The argument must immediately follow the option character.
+.It Fl t Ar n
+Interrupt reading after
+.Ar n
+seconds (specified as positive decimal value with an optional fractional part).
+.It Fl r
+Normally, the ASCII backslash character escapes the special
+meaning of the following character and is stripped from the input;
+.Ic read
+does not stop when encountering a backslash-newline sequence and
+does not store that newline in the result.
+This option enables raw mode, in which backslashes are not processed.
+.It Fl s
+The input line is saved to the history.
+.El
+.Pp
+If the input is a terminal, both the
+.Fl N
+and
+.Fl n
+options set it into raw mode;
+they read an entire file if \-1 is passed as
+.Ar z
+argument.
+.Pp
+The first parameter may have a question mark and a string appended to it, in
+which case the string is used as a prompt (printed to standard error before
+any input is read) if the input is a
+.Xr tty 4
+(e.g.\&
+.Ic read nfoo?\*(aqnumber of foos: \*(aq ) .
+.Pp
+If no input is read or a timeout occurred,
+.Ic read
+exits with a non-zero status.
+.Pp
+Another handy set of tricks:
+If
+.Ic read
+is run in a loop such as
+.Ic while read foo; do ...; done
+then leading whitespace will be removed (IFS) and backslashes processed.
+You might want to use
+.Ic while IFS= read \-r foo; do ...; done
+for pristine I/O.
+Similarily, when using the
+.Fl a
+option, use of the
+.Fl r
+option might be prudent; the same applies for:
+.Bd -literal -offset indent
+find . \-type f \-print0 \*(Ba \e
+    while IFS= read \-d \*(aq\*(aq \-r filename; do
+	print \-r \-\- "found \*(Lt${filename#./}\*(Gt"
+done
+.Ed
+.Pp
+The inner loop will be executed in a subshell and variable changes
+cannot be propagated if executed in a pipeline:
+.Bd -literal -offset indent
+bar \*(Ba baz \*(Ba while read foo; do ...; done
+.Ed
+.Pp
+Use co-processes instead:
+.Bd -literal -offset indent
+bar \*(Ba baz \*(Ba&
+while read \-p foo; do ...; done
+exec 3\*(Gt&p; exec 3\*(Gt&\-
+.Ed
+.Pp
+.It Xo
+.Ic readonly
+.Op Fl p
+.Oo Ar parameter
+.Op Ns = Ns Ar value
+.Ar ... Oc
+.Xc
+Sets the read-only attribute of the named parameters.
+If values are given,
+parameters are set to them before setting the attribute.
+Once a parameter is
+made read-only, it cannot be unset and its value cannot be changed.
+.Pp
+If no parameters are specified, the names of all parameters with the read-only
+attribute are printed one per line, unless the
+.Fl p
+option is used, in which case
+.Ic readonly
+commands defining all read-only parameters, including their values, are
+printed.
+.Pp
+.It Xo
+.Ic realpath
+.Op Fl \-
+.Ar name
+.Xc
+Prints the resolved absolute pathname corresponding to
+.Ar name .
+If
+.Ar name
+ends with a slash
+.Pq Sq / ,
+it's also checked for existence and whether it is a directory; otherwise,
+.Ic realpath
+returns 0 if the pathname either exists or can be created immediately,
+i.e. all but the last component exist and are directories.
+.Pp
+.It Xo
+.Ic rename
+.Op Fl \-
+.Ar from to
+.Xc
+Renames the file
+.Ar from
+to
+.Ar to .
+Both must be complete pathnames and on the same device.
+This builtin is intended for emergency situations where
+.Pa /bin/mv
+becomes unusable, and directly calls
+.Xr rename 2 .
+.Pp
+.It Ic return Op Ar status
+Returns from a function or
+.Ic .\&
+script, with exit status
+.Ar status .
+If no
+.Ar status
+is given, the exit status of the last executed command is used.
+If used outside of a function or
+.Ic .\&
+script, it has the same effect as
+.Ic exit .
+Note that
+.Nm
+treats both profile and
+.Ev ENV
+files as
+.Ic .\&
+scripts, while the original Korn shell only treats profiles as
+.Ic .\&
+scripts.
+.Pp
+.It Xo
+.Ic set Op Ic +\-abCefhiklmnprsUuvXx
+.Op Ic +\-o Ar option
+.Op Ic +\-A Ar name
+.Op Fl \-
+.Op Ar arg ...
+.Xc
+The
+.Ic set
+command can be used to set
+.Pq Ic \-
+or clear
+.Pq Ic +
+shell options, set the positional parameters, or set an array parameter.
+Options can be changed using the
+.Cm +\-o Ar option
+syntax, where
+.Ar option
+is the long name of an option, or using the
+.Cm +\- Ns Ar letter
+syntax, where
+.Ar letter
+is the option's single letter name (not all options have a single letter name).
+The following table lists both option letters (if they exist) and long names
+along with a description of what the option does:
+.Bl -tag -width 3n
+.It Fl A Ar name
+Sets the elements of the array parameter
+.Ar name
+to
+.Ar arg ...
+If
+.Fl A
+is used, the array is reset (i.e. emptied) first; if
+.Ic +A
+is used, the first N elements are set (where N is the number of arguments);
+the rest are left untouched.
+.Pp
+An alternative syntax for the command
+.Ic set \-A foo \-\- a b c
+which is compatible to
+.Tn GNU
+.Nm bash
+and also supported by
+.At
+.Nm ksh93
+is:
+.Ic foo=(a b c); foo+=(d e)
+.Pp
+Another
+.At
+.Nm ksh93
+and
+.Tn GNU
+.Nm bash
+extension allows specifying the indices used for
+.Ar arg ...
+.Pq from the above example, Ic a b c
+like this:
+.Ic set \-A foo \-\- [0]=a [1]=b [2]=c
+or
+.Ic foo=([0]=a [1]=b [2]=c)
+which can also be written
+.Ic foo=([0]=a b c)
+because indices are incremented automatically.
+.It Fl a \*(Ba Fl o Ic allexport
+All new parameters are created with the export attribute.
+.It Fl b \*(Ba Fl o Ic notify
+Print job notification messages asynchronously, instead of just before the
+prompt.
+Only used if job control is enabled
+.Pq Fl m .
+.It Fl C \*(Ba Fl o Ic noclobber
+Prevent \*(Gt redirection from overwriting existing files.
+Instead, \*(Gt\*(Ba must be used to force an overwrite.
+Note that this is not safe to use for creation of temporary files or
+lockfiles due to a TOCTOU in a check allowing one to redirect output to
+.Pa /dev/null
+or other device files even in
+.Ic noclobber
+mode.
+.It Fl e \*(Ba Fl o Ic errexit
+Exit (after executing the
+.Dv ERR
+trap) as soon as an error occurs or a command fails (i.e. exits with a
+non-zero status).
+This does not apply to commands whose exit status is
+explicitly tested by a shell construct such as
+.Ic if ,
+.Ic until ,
+.Ic while ,
+or
+.Ic !\&
+statements.
+For
+.Ic &&
+or
+.Ic \*(Ba\*(Ba ,
+only the status of the last command is tested.
+.It Fl f \*(Ba Fl o Ic noglob
+Do not expand file name patterns.
+.It Fl h \*(Ba Fl o Ic trackall
+Create tracked aliases for all executed commands (see
+.Sx Aliases
+above).
+Enabled by default for non-interactive shells.
+.It Fl i \*(Ba Fl o Ic interactive
+The shell is an interactive shell.
+This option can only be used when the shell is invoked.
+See above for a description of what this means.
+.It Fl k \*(Ba Fl o Ic keyword
+Parameter assignments are recognised anywhere in a command.
+.It Fl l \*(Ba Fl o Ic login
+The shell is a login shell.
+This option can only be used when the shell is invoked.
+See above for a description of what this means.
+.It Fl m \*(Ba Fl o Ic monitor
+Enable job control (default for interactive shells).
+.It Fl n \*(Ba Fl o Ic noexec
+Do not execute any commands.
+Useful for checking the syntax of scripts
+(ignored if interactive).
+.It Fl p \*(Ba Fl o Ic privileged
+The shell is a privileged shell.
+It is set automatically if, when the shell starts,
+the real UID or GID does not match
+the effective UID (EUID) or GID (EGID), respectively.
+See above for a description of what this means.
+.It Fl r \*(Ba Fl o Ic restricted
+The shell is a restricted shell.
+This option can only be used when the shell is invoked.
+See above for a description of what this means.
+.It Fl s \*(Ba Fl o Ic stdin
+If used when the shell is invoked, commands are read from standard input.
+Set automatically if the shell is invoked with no arguments.
+.Pp
+When
+.Fl s
+is used with the
+.Ic set
+command it causes the specified arguments to be sorted before assigning them to
+the positional parameters (or to array
+.Ar name ,
+if
+.Fl A
+is used).
+.It Fl U \*(Ba Fl o Ic utf8\-mode
+Enable UTF-8 support in the
+.Sx Emacs editing mode
+and internal string handling functions.
+This flag is disabled by default, but can be enabled by setting it on the
+shell command line; is enabled automatically for interactive shells if
+requested at compile time, your system supports
+.Fn setlocale LC_CTYPE \&""
+and optionally
+.Fn nl_langinfo CODESET ,
+or the
+.Ev LC_ALL ,
+.Ev LC_CTYPE ,
+or
+.Ev LANG
+environment variables,
+and at least one of these returns something that matches
+.Dq UTF\-8
+or
+.Dq utf8
+case-insensitively; for direct builtin calls depending on the
+aforementioned environment variables; or for stdin or scripts,
+if the input begins with a UTF-8 Byte Order Mark.
+.It Fl u \*(Ba Fl o Ic nounset
+Referencing of an unset parameter, other than
+.Dq $@
+or
+.Dq $* ,
+is treated as an error, unless one of the
+.Ql \- ,
+.Ql + ,
+or
+.Ql =
+modifiers is used.
+.It Fl v \*(Ba Fl o Ic verbose
+Write shell input to standard error as it is read.
+.It Fl X \*(Ba Fl o Ic markdirs
+Mark directories with a trailing
+.Ql /
+during file name generation.
+.It Fl x \*(Ba Fl o Ic xtrace
+Print command trees when they are executed, preceded by
+the value of
+.Ev PS4 .
+.It Fl o Ic bgnice
+Background jobs are run with lower priority.
+.It Fl o Ic braceexpand
+Enable brace expansion (a.k.a. alternation).
+This is enabled by default.
+If disabled, tilde expansion after an equals sign is disabled as a side effect.
+.It Fl o Ic emacs
+Enable BRL emacs-like command-line editing (interactive shells only); see
+.Sx Emacs editing mode .
+.It Fl o Ic gmacs
+Enable gmacs-like command-line editing (interactive shells only).
+Currently identical to emacs editing except that transpose\-chars (\*(haT) acts
+slightly differently.
+.It Fl o Ic ignoreeof
+The shell will not (easily) exit when end-of-file is read;
+.Ic exit
+must be used.
+To avoid infinite loops, the shell will exit if
+.Dv EOF
+is read 13 times in a row.
+.It Fl o Ic inherit\-xtrace
+Do not reset
+.Fl o Ic xtrace
+upon entering functions.
+This is enabled by default.
+.It Fl o Ic nohup
+Do not kill running jobs with a
+.Dv SIGHUP
+signal when a login shell exits.
+Currently set by default, but this may
+change in the future to be compatible with
+.At
+.Nm ksh ,
+which
+doesn't have this option, but does send the
+.Dv SIGHUP
+signal.
+.It Fl o Ic nolog
+No effect.
+In the original Korn shell, this prevents function definitions from
+being stored in the history file.
+.It Fl o Ic physical
+Causes the
+.Ic cd
+and
+.Ic pwd
+commands to use
+.Dq physical
+(i.e. the filesystem's)
+.Sq ..
+directories instead of
+.Dq logical
+directories (i.e. the shell handles
+.Sq .. ,
+which allows the user to be oblivious of symbolic links to directories).
+Clear by default.
+Note that setting this option does not affect the current value of the
+.Ev PWD
+parameter; only the
+.Ic cd
+command changes
+.Ev PWD .
+See the
+.Ic cd
+and
+.Ic pwd
+commands above for more details.
+.It Fl o Ic pipefail
+Make the exit status of a pipeline (before logically complementing) the
+rightmost non-zero errorlevel, or zero if all commands exited with zero.
+.It Fl o Ic posix
+Enable a somewhat more
+.Px
+ish mode.
+As a side effect, setting this flag turns off
+.Ic braceexpand
+mode, which can be turned back on manually, and
+.Ic sh
+mode.
+.It Fl o Ic sh
+Enable
+.Pa /bin/sh
+.Pq kludge
+mode.
+Automatically enabled if the basename of the shell invocation begins with
+.Dq sh
+and this autodetection feature is compiled in
+.Pq not in MirBSD .
+As a side effect, setting this flag turns off
+.Ic braceexpand
+mode, which can be turned back on manually, and
+.Ic posix
+mode.
+.It Fl o Ic vi
+Enable
+.Xr vi 1 Ns -like
+command-line editing (interactive shells only).
+See
+.Sx Vi editing mode
+for documentation and limitations.
+.It Fl o Ic vi\-esccomplete
+In vi command-line editing, do command and file name completion when escape
+(\*(ha[) is entered in command mode.
+.It Fl o Ic vi\-tabcomplete
+In vi command-line editing, do command and file name completion when tab (\*(haI)
+is entered in insert mode.
+This is the default.
+.It Fl o Ic viraw
+No effect.
+In the original Korn shell, unless
+.Ic viraw
+was set, the vi command-line mode would let the
+.Xr tty 4
+driver do the work until ESC (\*(ha[) was entered.
+.Nm
+is always in viraw mode.
+.El
+.Pp
+These options can also be used upon invocation of the shell.
+The current set of
+options (with single letter names) can be found in the parameter
+.Sq $\- .
+.Ic set Fl o
+with no option name will list all the options and whether each is on or off;
+.Ic set +o
+will print the long names of all options that are currently on.
+.Pp
+Remaining arguments, if any, are positional parameters and are assigned, in
+order, to the positional parameters (i.e. $1, $2, etc.).
+If options end with
+.Ql \-\-
+and there are no remaining arguments, all positional parameters are cleared.
+If no options or arguments are given, the values of all names are printed.
+For unknown historical reasons, a lone
+.Ql \-
+option is treated specially \*(en it clears both the
+.Fl v
+and
+.Fl x
+options.
+.Pp
+.It Ic shift Op Ar number
+The positional parameters
+.Ar number Ns +1 ,
+.Ar number Ns +2 ,
+etc. are renamed to
+.Sq 1 ,
+.Sq 2 ,
+etc.
+.Ar number
+defaults to 1.
+.Pp
+.It Ic sleep Ar seconds
+Suspends execution for a minimum of the
+.Ar seconds
+specified as positive decimal value with an optional fractional part.
+Signal delivery may continue execution earlier.
+.Pp
+.It Ic source Ar file Op Ar arg ...
+Like
+.Ic \&. Po Do dot Dc Pc ,
+except that the current working directory is appended to the
+.Ev PATH
+in GNU
+.Nm bash
+and
+.Nm mksh .
+In
+.Nm ksh93
+and
+.Nm mksh ,
+this is implemented as a shell alias instead of a builtin.
+.Pp
+.It Ic suspend
+Stops the shell as if it had received the suspend character from
+the terminal.
+It is not possible to suspend a login shell unless the parent process
+is a member of the same terminal session but is a member of a different
+process group.
+As a general rule, if the shell was started by another shell or via
+.Xr su 1 ,
+it can be suspended.
+.Pp
+.It Ic test Ar expression
+.It Ic \&[ Ar expression Ic \&]
+.Ic test
+evaluates the
+.Ar expression
+and returns zero status if true, 1 if false, or greater than 1 if there
+was an error.
+It is normally used as the condition command of
+.Ic if
+and
+.Ic while
+statements.
+Symbolic links are followed for all
+.Ar file
+expressions except
+.Fl h
+and
+.Fl L .
+.Pp
+The following basic expressions are available:
+.Bl -tag -width 17n
+.It Fl a Ar file
+.Ar file
+exists.
+.It Fl b Ar file
+.Ar file
+is a block special device.
+.It Fl c Ar file
+.Ar file
+is a character special device.
+.It Fl d Ar file
+.Ar file
+is a directory.
+.It Fl e Ar file
+.Ar file
+exists.
+.It Fl f Ar file
+.Ar file
+is a regular file.
+.It Fl G Ar file
+.Ar file Ns 's
+group is the shell's effective group ID.
+.It Fl g Ar file
+.Ar file Ns 's
+mode has the setgid bit set.
+.It Fl H Ar file
+.Ar file
+is a context dependent directory (only useful on HP-UX).
+.It Fl h Ar file
+.Ar file
+is a symbolic link.
+.It Fl k Ar file
+.Ar file Ns 's
+mode has the
+.Xr sticky 8
+bit set.
+.It Fl L Ar file
+.Ar file
+is a symbolic link.
+.It Fl O Ar file
+.Ar file Ns 's
+owner is the shell's effective user ID.
+.It Fl o Ar option
+Shell
+.Ar option
+is set (see the
+.Ic set
+command above for a list of options).
+As a non-standard extension, if the option starts with a
+.Ql \&! ,
+the test is negated; the test always fails if
+.Ar option
+doesn't exist (so [ \-o foo \-o \-o !foo ] returns true if and only if option
+.Ar foo
+exists).
+The same can be achieved with [ \-o ?foo ] like in
+.At
+.Nm ksh93 .
+.Ar option
+can also be the short flag led by either
+.Ql \-
+or
+.Ql +
+.Pq no logical negation ,
+for example
+.Ql \-x
+or
+.Ql +x
+instead of
+.Ql xtrace .
+.It Fl p Ar file
+.Ar file
+is a named pipe
+.Pq Tn FIFO .
+.It Fl r Ar file
+.Ar file
+exists and is readable.
+.It Fl S Ar file
+.Ar file
+is a
+.Xr unix 4 Ns -domain
+socket.
+.It Fl s Ar file
+.Ar file
+is not empty.
+.It Fl t Ar fd
+File descriptor
+.Ar fd
+is a
+.Xr tty 4
+device.
+.It Fl u Ar file
+.Ar file Ns 's
+mode has the setuid bit set.
+.It Fl w Ar file
+.Ar file
+exists and is writable.
+.It Fl x Ar file
+.Ar file
+exists and is executable.
+.It Ar file1 Fl nt Ar file2
+.Ar file1
+is newer than
+.Ar file2
+or
+.Ar file1
+exists and
+.Ar file2
+does not.
+.It Ar file1 Fl ot Ar file2
+.Ar file1
+is older than
+.Ar file2
+or
+.Ar file2
+exists and
+.Ar file1
+does not.
+.It Ar file1 Fl ef Ar file2
+.Ar file1
+is the same file as
+.Ar file2 .
+.It Ar string
+.Ar string
+has non-zero length.
+.It Fl n Ar string
+.Ar string
+is not empty.
+.It Fl z Ar string
+.Ar string
+is empty.
+.It Ar string No = Ar string
+Strings are equal.
+.It Ar string No == Ar string
+Strings are equal.
+.It Ar string No \*(Gt Ar string
+First string operand is greater than second string operand.
+.It Ar string No \*(Lt Ar string
+First string operand is less than second string operand.
+.It Ar string No != Ar string
+Strings are not equal.
+.It Ar number Fl eq Ar number
+Numbers compare equal.
+.It Ar number Fl ne Ar number
+Numbers compare not equal.
+.It Ar number Fl ge Ar number
+Numbers compare greater than or equal.
+.It Ar number Fl gt Ar number
+Numbers compare greater than.
+.It Ar number Fl le Ar number
+Numbers compare less than or equal.
+.It Ar number Fl \&lt Ar number
+Numbers compare less than.
+.El
+.Pp
+The above basic expressions, in which unary operators have precedence over
+binary operators, may be combined with the following operators (listed in
+increasing order of precedence):
+.Bd -literal -offset indent
+expr \-o expr		Logical OR.
+expr \-a expr		Logical AND.
+! expr			Logical NOT.
+( expr )		Grouping.
+.Ed
+.Pp
+Note that a number actually may be an arithmetic expression, such as
+a mathematical term or the name of an integer variable:
+.Bd -literal -offset indent
+x=1; [ "x" \-eq 1 ]	evaluates to true
+.Ed
+.Pp
+Note that some special rules are applied (courtesy of
+.Px
+) if the number of arguments to
+.Ic test
+or inside the brackets
+.Ic \&[ ... \&]
+is less than five: if leading
+.Ql \&!
+arguments can be stripped such that only one to three arguments remain,
+then the lowered comparison is executed; (thanks to XSI) parentheses
+.Ic \e( ... \e)
+lower four- and three-argument forms to two- and one-argument forms,
+respectively; three-argument forms ultimately prefer binary operations,
+followed by negation and parenthesis lowering; two- and four-argument forms
+prefer negation followed by parenthesis; the one-argument form always implies
+.Fl n .
+.Pp
+.Sy Note :
+A common mistake is to use
+.Dq if \&[ $foo = bar \&]
+which fails if parameter
+.Dq foo
+is
+.Dv NULL
+or unset, if it has embedded spaces (i.e.\&
+.Ev IFS
+octets), or if it is a unary operator like
+.Sq \&!
+or
+.Sq Fl n .
+Use tests like
+.Dq if \&[ x\&"$foo\&" = x"bar" \&]
+instead, or the double-bracket operator
+.Dq if \&[[ $foo = bar \&]]
+or, to avoid pattern matching (see
+.Ic \&[[
+above):
+.Dq if \&[[ $foo = \&"$bar" \&]]
+.Pp
+The
+.Ic \&[[ ... ]]
+construct is not only more secure to use but also often faster.
+.Pp
+.It Xo
+.Ic time
+.Op Fl p
+.Op Ar pipeline
+.Xc
+If a
+.Ar pipeline
+is given, the times used to execute the pipeline are reported.
+If no pipeline
+is given, then the user and system time used by the shell itself, and all the
+commands it has run since it was started, are reported.
+The times reported are the real time (elapsed time from start to finish),
+the user CPU time (time spent running in user mode), and the system CPU time
+(time spent running in kernel mode).
+Times are reported to standard error; the format of the output is:
+.Pp
+.Dl "0m0.00s real     0m0.00s user     0m0.00s system"
+.Pp
+If the
+.Fl p
+option is given the output is slightly longer:
+.Bd -literal -offset indent
+real     0.00
+user     0.00
+sys      0.00
+.Ed
+.Pp
+It is an error to specify the
+.Fl p
+option unless
+.Ar pipeline
+is a simple command.
+.Pp
+Simple redirections of standard error do not affect the output of the
+.Ic time
+command:
+.Pp
+.Dl $ time sleep 1 2\*(Gtafile
+.Dl $ { time sleep 1; } 2\*(Gtafile
+.Pp
+Times for the first command do not go to
+.Dq afile ,
+but those of the second command do.
+.Pp
+.It Ic times
+Print the accumulated user and system times used both by the shell
+and by processes that the shell started which have exited.
+The format of the output is:
+.Bd -literal -offset indent
+0m0.00s 0m0.00s
+0m0.00s 0m0.00s
+.Ed
+.Pp
+.It Ic trap Op Ar handler signal ...
+Sets a trap handler that is to be executed when any of the specified signals are
+received.
+.Ar handler
+is either a
+.Dv NULL
+string, indicating the signals are to be ignored, a minus sign
+.Pq Sq \- ,
+indicating that the default action is to be taken for the signals (see
+.Xr signal 3 ) ,
+or a string containing shell commands to be evaluated and executed at the first
+opportunity (i.e. when the current command completes, or before printing the
+next
+.Ev PS1
+prompt) after receipt of one of the signals.
+.Ar signal
+is the name of a signal (e.g.\&
+.Dv PIPE
+or
+.Dv ALRM )
+or the number of the signal (see the
+.Ic kill \-l
+command above).
+.Pp
+There are two special signals:
+.Dv EXIT
+(also known as 0) which is executed when the shell is about to exit, and
+.Dv ERR ,
+which is executed after an error occurs (an error is something that would cause
+the shell to exit if the
+.Fl e
+or
+.Ic errexit
+option were set \*(en see the
+.Ic set
+command above).
+.Dv EXIT
+handlers are executed in the environment of the last executed command.
+Note
+that for non-interactive shells, the trap handler cannot be changed for signals
+that were ignored when the shell started.
+.Pp
+With no arguments,
+.Ic trap
+lists, as a series of
+.Ic trap
+commands, the current state of the traps that have been set since the shell
+started.
+Note that the output of
+.Ic trap
+cannot be usefully piped to another process (an artifact of the fact that
+traps are cleared when subprocesses are created).
+.Pp
+The original Korn shell's
+.Dv DEBUG
+trap and the handling of
+.Dv ERR
+and
+.Dv EXIT
+traps in functions are not yet implemented.
+.Pp
+.It Ic true
+A command that exits with a zero value.
+.Pp
+.It Xo
+.Ic global
+.Oo Op Ic +\-alpnrtUux
+.Op Fl L Ns Op Ar n
+.Op Fl R Ns Op Ar n
+.Op Fl Z Ns Op Ar n
+.Op Fl i Ns Op Ar n
+.No \*(Ba Fl f Op Fl tux Oc
+.Oo Ar name
+.Op Ns = Ns Ar value
+.Ar ... Oc
+.Xc
+.It Xo
+.Ic typeset
+.Oo Op Ic +\-alpnrtUux
+.Op Fl LRZ Ns Op Ar n
+.Op Fl i Ns Op Ar n
+.No \*(Ba Fl f Op Fl tux Oc
+.Oo Ar name
+.Op Ns = Ns Ar value
+.Ar ... Oc
+.Xc
+Display or set parameter attributes.
+With no
+.Ar name
+arguments, parameter attributes are displayed; if no options are used, the
+current attributes of all parameters are printed as
+.Ic typeset
+commands; if an option is given (or
+.Ql \-
+with no option letter), all parameters and their values with the specified
+attributes are printed; if options are introduced with
+.Ql + ,
+parameter values are not printed.
+.Pp
+If
+.Ar name
+arguments are given, the attributes of the named parameters are set
+.Pq Ic \-
+or cleared
+.Pq Ic + .
+Values for parameters may optionally be specified.
+For
+.Ar name Ns \&[*] ,
+the change affects the entire array, and no value may be specified.
+.Pp
+If
+.Ic typeset
+is used inside a function, any parameters specified are localised.
+This is not done by the otherwise identical
+.Ic global .
+.Em Note :
+This means that
+.Nm No 's Ic global
+command is
+.Em not
+equivalent to other programming languages' as it does not allow a
+function called from another function to access a parameter at truly
+global scope, but only prevents putting an accessed one into local scope.
+.Pp
+When
+.Fl f
+is used,
+.Ic typeset
+operates on the attributes of functions.
+As with parameters, if no
+.Ar name
+arguments are given,
+functions are listed with their values (i.e. definitions) unless
+options are introduced with
+.Ql + ,
+in which case only the function names are reported.
+.Bl -tag -width Ds
+.It Fl a
+Indexed array attribute.
+.It Fl f
+Function mode.
+Display or set functions and their attributes, instead of parameters.
+.It Fl i Ns Op Ar n
+Integer attribute.
+.Ar n
+specifies the base to use when displaying the integer (if not specified, the
+base given in the first assignment is used).
+Parameters with this attribute may
+be assigned values containing arithmetic expressions.
+.It Fl L Ns Op Ar n
+Left justify attribute.
+.Ar n
+specifies the field width.
+If
+.Ar n
+is not specified, the current width of a parameter (or the width of its first
+assigned value) is used.
+Leading whitespace (and zeros, if used with the
+.Fl Z
+option) is stripped.
+If necessary, values are either truncated or space padded
+to fit the field width.
+.It Fl l
+Lower case attribute.
+All upper case characters in values are converted to lower case.
+(In the original Korn shell, this parameter meant
+.Dq long integer
+when used with the
+.Fl i
+option.)
+.It Fl n
+Create a bound variable (name reference): any access to the variable
+.Ar name
+will access the variable
+.Ar value
+in the current scope (this is different from
+.At
+.Nm ksh93 ! )
+instead.
+Also different from
+.At
+.Nm ksh93
+is that
+.Ar value
+is lazily evaluated at the time
+.Ar name
+is accessed.
+This can be used by functions to access variables whose names are
+passed as parametres, instead of using
+.Ic eval .
+.It Fl p
+Print complete
+.Ic typeset
+commands that can be used to re-create the attributes and values of
+parameters.
+.It Fl R Ns Op Ar n
+Right justify attribute.
+.Ar n
+specifies the field width.
+If
+.Ar n
+is not specified, the current width of a parameter (or the width of its first
+assigned value) is used.
+Trailing whitespace is stripped.
+If necessary, values are either stripped of leading characters or space
+padded to make them fit the field width.
+.It Fl r
+Read-only attribute.
+Parameters with this attribute may not be assigned to or unset.
+Once this attribute is set, it cannot be turned off.
+.It Fl t
+Tag attribute.
+Has no meaning to the shell; provided for application use.
+.Pp
+For functions,
+.Fl t
+is the trace attribute.
+When functions with the trace attribute are executed, the
+.Ic xtrace
+.Pq Fl x
+shell option is temporarily turned on.
+.It Fl U
+Unsigned integer attribute.
+Integers are printed as unsigned values (combine with the
+.Fl i
+option).
+This option is not in the original Korn shell.
+.It Fl u
+Upper case attribute.
+All lower case characters in values are converted to upper case.
+(In the original Korn shell, this parameter meant
+.Dq unsigned integer
+when used with the
+.Fl i
+option which meant upper case letters would never be used for bases greater
+than 10.
+See the
+.Fl U
+option.)
+.Pp
+For functions,
+.Fl u
+is the undefined attribute.
+See
+.Sx Functions
+above for the implications of this.
+.It Fl x
+Export attribute.
+Parameters (or functions) are placed in the environment of
+any executed commands.
+Exported functions are not yet implemented.
+.It Fl Z Ns Op Ar n
+Zero fill attribute.
+If not combined with
+.Fl L ,
+this is the same as
+.Fl R ,
+except zero padding is used instead of space padding.
+For integers, the number instead of the base is padded.
+.El
+.Pp
+If any of the
+.\" long integer ,
+.Fl i ,
+.Fl L ,
+.Fl l ,
+.Fl R ,
+.Fl U ,
+.Fl u ,
+or
+.Fl Z
+options are changed, all others from this set are cleared,
+unless they are also given on the same command line.
+.Pp
+.It Xo
+.Ic ulimit
+.Op Fl aBCcdefHilMmnOPpqrSsTtVvw
+.Op Ar value
+.Xc
+Display or set process limits.
+If no options are used, the file size limit
+.Pq Fl f
+is assumed.
+.Ar value ,
+if specified, may be either an arithmetic expression or the word
+.Dq unlimited .
+The limits affect the shell and any processes created by the shell after a
+limit is imposed.
+Note that some systems may not allow limits to be increased
+once they are set.
+Also note that the types of limits available are system
+dependent \*(en some systems have only the
+.Fl f
+limit.
+.Bl -tag -width 5n
+.It Fl a
+Display all limits; unless
+.Fl H
+is used, soft limits are displayed.
+.It Fl B Ar n
+Set the socket buffer size to
+.Ar n
+kibibytes.
+.It Fl C Ar n
+Set the number of cached threads to
+.Ar n .
+.It Fl c Ar n
+Impose a size limit of
+.Ar n
+blocks on the size of core dumps.
+.It Fl d Ar n
+Impose a size limit of
+.Ar n
+kibibytes on the size of the data area.
+.It Fl e Ar n
+Set the maximum niceness to
+.Ar n .
+.It Fl f Ar n
+Impose a size limit of
+.Ar n
+blocks on files written by the shell and its child processes (files of any
+size may be read).
+.It Fl H
+Set the hard limit only (the default is to set both hard and soft limits).
+.It Fl i Ar n
+Set the number of pending signals to
+.Ar n .
+.It Fl l Ar n
+Impose a limit of
+.Ar n
+kibibytes on the amount of locked (wired) physical memory.
+.It Fl M Ar n
+Set the AIO locked memory to
+.Ar n
+kibibytes.
+.It Fl m Ar n
+Impose a limit of
+.Ar n
+kibibytes on the amount of physical memory used.
+.It Fl n Ar n
+Impose a limit of
+.Ar n
+file descriptors that can be open at once.
+.It Fl O Ar n
+Set the number of AIO operations to
+.Ar n .
+.It Fl P Ar n
+Limit the number of threads per process to
+.Ar n .
+.It Fl p Ar n
+Impose a limit of
+.Ar n
+processes that can be run by the user at any one time.
+.It Fl q Ar n
+Limit the size of
+.Tn POSIX
+message queues to
+.Ar n
+bytes.
+.It Fl r Ar n
+Set the maximum real-time priority to
+.Ar n .
+.It Fl S
+Set the soft limit only (the default is to set both hard and soft limits).
+.It Fl s Ar n
+Impose a size limit of
+.Ar n
+kibibytes on the size of the stack area.
+.It Fl T Ar n
+Impose a time limit of
+.Ar n
+real seconds to be used by each process.
+.It Fl t Ar n
+Impose a time limit of
+.Ar n
+CPU seconds spent in user mode to be used by each process.
+.It Fl V Ar n
+Set the number of vnode monitors on Haiku to
+.Ar n .
+.It Fl v Ar n
+Impose a limit of
+.Ar n
+kibibytes on the amount of virtual memory (address space) used.
+.It Fl w Ar n
+Impose a limit of
+.Ar n
+kibibytes on the amount of swap space used.
+.El
+.Pp
+As far as
+.Ic ulimit
+is concerned, a block is 512 bytes.
+.Pp
+.It Xo
+.Ic umask
+.Op Fl S
+.Op Ar mask
+.Xc
+Display or set the file permission creation mask, or umask (see
+.Xr umask 2 ) .
+If the
+.Fl S
+option is used, the mask displayed or set is symbolic; otherwise, it is an
+octal number.
+.Pp
+Symbolic masks are like those used by
+.Xr chmod 1 .
+When used, they describe what permissions may be made available (as opposed to
+octal masks in which a set bit means the corresponding bit is to be cleared).
+For example,
+.Dq ug=rwx,o=
+sets the mask so files will not be readable, writable, or executable by
+.Dq others ,
+and is equivalent (on most systems) to the octal mask
+.Dq 007 .
+.Pp
+.It Xo
+.Ic unalias
+.Op Fl adt
+.Op Ar name ...
+.Xc
+The aliases for the given names are removed.
+If the
+.Fl a
+option is used, all aliases are removed.
+If the
+.Fl t
+or
+.Fl d
+options are used, the indicated operations are carried out on tracked or
+directory aliases, respectively.
+.Pp
+.It Xo
+.Ic unset
+.Op Fl fv
+.Ar parameter ...
+.Xc
+Unset the named parameters
+.Po
+.Fl v ,
+the default
+.Pc
+or functions
+.Pq Fl f .
+With
+.Ar parameter Ns \&[*] ,
+attributes are kept, only values are unset.
+.Pp
+The exit status is non-zero if any of the parameters have the read-only
+attribute set, zero otherwise.
+.Pp
+.It Ic wait Op Ar job ...
+Wait for the specified job(s) to finish.
+The exit status of
+.Ic wait
+is that of the last specified job; if the last job is killed by a signal, the
+exit status is 128 + the number of the signal (see
+.Ic kill \-l Ar exit-status
+above); if the last specified job can't be found (because it never existed, or
+had already finished), the exit status of
+.Ic wait
+is 127.
+See
+.Sx Job control
+below for the format of
+.Ar job .
+.Ic wait
+will return if a signal for which a trap has been set is received, or if a
+.Dv SIGHUP ,
+.Dv SIGINT ,
+or
+.Dv SIGQUIT
+signal is received.
+.Pp
+If no jobs are specified,
+.Ic wait
+waits for all currently running jobs (if any) to finish and exits with a zero
+status.
+If job monitoring is enabled, the completion status of jobs is printed
+(this is not the case when jobs are explicitly specified).
+.Pp
+.It Xo
+.Ic whence
+.Op Fl pv
+.Op Ar name ...
+.Xc
+For each
+.Ar name ,
+the type of command is listed (reserved word, built-in, alias,
+function, tracked alias, or executable).
+If the
+.Fl p
+option is used, a path search is performed even if
+.Ar name
+is a reserved word, alias, etc.
+Without the
+.Fl v
+option,
+.Ic whence
+is similar to
+.Ic command Fl v
+except that
+.Ic whence
+will find reserved words and won't print aliases as alias commands.
+With the
+.Fl v
+option,
+.Ic whence
+is the same as
+.Ic command Fl V .
+Note that for
+.Ic whence ,
+the
+.Fl p
+option does not affect the search path used, as it does for
+.Ic command .
+If the type of one or more of the names could not be determined, the exit
+status is non-zero.
+.El
+.Ss Job control
+Job control refers to the shell's ability to monitor and control jobs which
+are processes or groups of processes created for commands or pipelines.
+At a minimum, the shell keeps track of the status of the background (i.e.\&
+asynchronous) jobs that currently exist; this information can be displayed
+using the
+.Ic jobs
+commands.
+If job control is fully enabled (using
+.Ic set \-m
+or
+.Ic set \-o monitor ) ,
+as it is for interactive shells, the processes of a job are placed in their
+own process group.
+Foreground jobs can be stopped by typing the suspend
+character from the terminal (normally \*(haZ), jobs can be restarted in either the
+foreground or background using the
+.Ic fg
+and
+.Ic bg
+commands, and the state of the terminal is saved or restored when a foreground
+job is stopped or restarted, respectively.
+.Pp
+Note that only commands that create processes (e.g. asynchronous commands,
+subshell commands, and non-built-in, non-function commands) can be stopped;
+commands like
+.Ic read
+cannot be.
+.Pp
+When a job is created, it is assigned a job number.
+For interactive shells, this number is printed inside
+.Dq \&[..] ,
+followed by the process IDs of the processes in the job when an asynchronous
+command is run.
+A job may be referred to in the
+.Ic bg ,
+.Ic fg ,
+.Ic jobs ,
+.Ic kill ,
+and
+.Ic wait
+commands either by the process ID of the last process in the command pipeline
+(as stored in the
+.Ic $!\&
+parameter) or by prefixing the job number with a percent
+sign
+.Pq Sq % .
+Other percent sequences can also be used to refer to jobs:
+.Bl -tag -width "%+ x %% x %XX"
+.It %+ \*(Ba %% \*(Ba %
+The most recently stopped job, or, if there are no stopped jobs, the oldest
+running job.
+.It %\-
+The job that would be the
+.Ic %+
+job if the latter did not exist.
+.It % Ns Ar n
+The job with job number
+.Ar n .
+.It %? Ns Ar string
+The job with its command containing the string
+.Ar string
+(an error occurs if multiple jobs are matched).
+.It % Ns Ar string
+The job with its command starting with the string
+.Ar string
+(an error occurs if multiple jobs are matched).
+.El
+.Pp
+When a job changes state (e.g. a background job finishes or foreground job is
+stopped), the shell prints the following status information:
+.Pp
+.D1 [ Ns Ar number ] Ar flag status command
+.Pp
+where...
+.Bl -tag -width "command"
+.It Ar number
+is the job number of the job;
+.It Ar flag
+is the
+.Ql +
+or
+.Ql \-
+character if the job is the
+.Ic %+
+or
+.Ic %\-
+job, respectively, or space if it is neither;
+.It Ar status
+indicates the current state of the job and can be:
+.Bl -tag -width "RunningXX"
+.It Done Op Ar number
+The job exited.
+.Ar number
+is the exit status of the job which is omitted if the status is zero.
+.It Running
+The job has neither stopped nor exited (note that running does not necessarily
+mean consuming CPU time \*(en
+the process could be blocked waiting for some event).
+.It Stopped Op Ar signal
+The job was stopped by the indicated
+.Ar signal
+(if no signal is given, the job was stopped by
+.Dv SIGTSTP ) .
+.It Ar signal-description Op Dq core dumped
+The job was killed by a signal (e.g. memory fault, hangup); use
+.Ic kill \-l
+for a list of signal descriptions.
+The
+.Dq core dumped
+message indicates the process created a core file.
+.El
+.It Ar command
+is the command that created the process.
+If there are multiple processes in
+the job, each process will have a line showing its
+.Ar command
+and possibly its
+.Ar status ,
+if it is different from the status of the previous process.
+.El
+.Pp
+When an attempt is made to exit the shell while there are jobs in the stopped
+state, the shell warns the user that there are stopped jobs and does not exit.
+If another attempt is immediately made to exit the shell, the stopped jobs are
+sent a
+.Dv SIGHUP
+signal and the shell exits.
+Similarly, if the
+.Ic nohup
+option is not set and there are running jobs when an attempt is made to exit
+a login shell, the shell warns the user and does not exit.
+If another attempt
+is immediately made to exit the shell, the running jobs are sent a
+.Dv SIGHUP
+signal and the shell exits.
+.Ss Interactive input line editing
+The shell supports three modes of reading command lines from a
+.Xr tty 4
+in an interactive session, controlled by the
+.Ic emacs ,
+.Ic gmacs ,
+and
+.Ic vi
+options (at most one of these can be set at once).
+The default is
+.Ic emacs .
+Editing modes can be set explicitly using the
+.Ic set
+built-in.
+If none of these options are enabled,
+the shell simply reads lines using the normal
+.Xr tty 4
+driver.
+If the
+.Ic emacs
+or
+.Ic gmacs
+option is set, the shell allows emacs-like editing of the command; similarly,
+if the
+.Ic vi
+option is set, the shell allows vi-like editing of the command.
+These modes are described in detail in the following sections.
+.Pp
+In these editing modes, if a line is longer than the screen width (see the
+.Ev COLUMNS
+parameter),
+a
+.Ql \*(Gt ,
+.Ql + ,
+or
+.Ql \*(Lt
+character is displayed in the last column indicating that there are more
+characters after, before and after, or before the current position,
+respectively.
+The line is scrolled horizontally as necessary.
+.Pp
+Completed lines are pushed into the history, unless they begin with an
+IFS octet or IFS white space, or are the same as the previous line.
+.Ss Emacs editing mode
+When the
+.Ic emacs
+option is set, interactive input line editing is enabled.
+Warning: This mode is
+slightly different from the emacs mode in the original Korn shell.
+In this mode, various editing commands
+(typically bound to one or more control characters) cause immediate actions
+without waiting for a newline.
+Several editing commands are bound to particular
+control characters when the shell is invoked; these bindings can be changed
+using the
+.Ic bind
+command.
+.Pp
+The following is a list of available editing commands.
+Each description starts with the name of the command,
+suffixed with a colon;
+an
+.Op Ar n
+(if the command can be prefixed with a count); and any keys the command is
+bound to by default, written using caret notation
+e.g. the ASCII ESC character is written as \*(ha[.
+These control sequences are not case sensitive.
+A count prefix for a command is entered using the sequence
+.Pf \*(ha[ Ns Ar n ,
+where
+.Ar n
+is a sequence of 1 or more digits.
+Unless otherwise specified, if a count is
+omitted, it defaults to 1.
+.Pp
+Note that editing command names are used only with the
+.Ic bind
+command.
+Furthermore, many editing commands are useful only on terminals with
+a visible cursor.
+The default bindings were chosen to resemble corresponding
+Emacs key bindings.
+The user's
+.Xr tty 4
+characters (e.g.\&
+.Dv ERASE )
+are bound to
+reasonable substitutes and override the default bindings.
+.Bl -tag -width Ds
+.It abort: \*(haC, \*(haG
+Abort the current command, empty the line buffer and
+set the exit state to interrupted.
+.It auto\-insert: Op Ar n
+Simply causes the character to appear as literal input.
+Most ordinary characters are bound to this.
+.It Xo backward\-char:
+.Op Ar n
+.No \*(haB , \*(haXD , ANSI-CurLeft
+.Xc
+Moves the cursor backward
+.Ar n
+characters.
+.It Xo backward\-word:
+.Op Ar n
+.No \*(ha[b , ANSI-Ctrl-CurLeft , ANSI-Alt-CurLeft
+.Xc
+Moves the cursor backward to the beginning of the word; words consist of
+alphanumerics, underscore
+.Pq Sq _ ,
+and dollar sign
+.Pq Sq $
+characters.
+.It beginning\-of\-history: \*(ha[\*(Lt
+Moves to the beginning of the history.
+.It beginning\-of\-line: \*(haA, ANSI-Home
+Moves the cursor to the beginning of the edited input line.
+.It Xo capitalise\-word:
+.Op Ar n
+.No \*(ha[C , \*(ha[c
+.Xc
+Uppercase the first character in the next
+.Ar n
+words, leaving the cursor past the end of the last word.
+.It clear\-screen: \*(ha[\*(haL
+Prints a compile-time configurable sequence to clear the screen and home
+the cursor, redraws the entire prompt and the currently edited input line.
+The default sequence works for almost all standard terminals.
+.It comment: \*(ha[#
+If the current line does not begin with a comment character, one is added at
+the beginning of the line and the line is entered (as if return had been
+pressed); otherwise, the existing comment characters are removed and the cursor
+is placed at the beginning of the line.
+.It complete: \*(ha[\*(ha[
+Automatically completes as much as is unique of the command name or the file
+name containing the cursor.
+If the entire remaining command or file name is
+unique, a space is printed after its completion, unless it is a directory name
+in which case
+.Ql /
+is appended.
+If there is no command or file name with the current partial word
+as its prefix, a bell character is output (usually causing a beep to be
+sounded).
+.It complete\-command: \*(haX\*(ha[
+Automatically completes as much as is unique of the command name having the
+partial word up to the cursor as its prefix, as in the
+.Ic complete
+command above.
+.It complete\-file: \*(ha[\*(haX
+Automatically completes as much as is unique of the file name having the
+partial word up to the cursor as its prefix, as in the
+.Ic complete
+command described above.
+.It complete\-list: \*(haI, \*(ha[=
+Complete as much as is possible of the current word,
+and list the possible completions for it.
+If only one completion is possible,
+match as in the
+.Ic complete
+command above.
+Note that \*(haI is usually generated by the TAB (tabulator) key.
+.It Xo delete\-char\-backward:
+.Op Ar n
+.No ERASE , \*(ha? , \*(haH
+.Xc
+Deletes
+.Ar n
+characters before the cursor.
+.It Xo delete\-char\-forward:
+.Op Ar n
+.No ANSI-Del
+.Xc
+Deletes
+.Ar n
+characters after the cursor.
+.It Xo delete\-word\-backward:
+.Op Ar n
+.No WERASE , \*(ha[\*(ha? , \*(ha[\*(haH , \*(ha[h
+.Xc
+Deletes
+.Ar n
+words before the cursor.
+.It Xo delete\-word\-forward:
+.Op Ar n
+.No \*(ha[d
+.Xc
+Deletes characters after the cursor up to the end of
+.Ar n
+words.
+.It Xo down\-history:
+.Op Ar n
+.No \*(haN , \*(haXB , ANSI-CurDown
+.Xc
+Scrolls the history buffer forward
+.Ar n
+lines (later).
+Each input line originally starts just after the last entry
+in the history buffer, so
+.Ic down\-history
+is not useful until either
+.Ic search\-history ,
+.Ic search\-history\-up
+or
+.Ic up\-history
+has been performed.
+.It Xo downcase\-word:
+.Op Ar n
+.No \*(ha[L , \*(ha[l
+.Xc
+Lowercases the next
+.Ar n
+words.
+.It Xo edit\-line:
+.Op Ar n
+.No \*(haXe
+.Xc
+Edit line
+.Ar n
+or the current line, if not specified, interactively.
+The actual command executed is
+.Ic fc \-e ${VISUAL:\-${EDITOR:\-vi}} Ar n .
+.It end\-of\-history: \*(ha[\*(Gt
+Moves to the end of the history.
+.It end\-of\-line: \*(haE, ANSI-End
+Moves the cursor to the end of the input line.
+.It eot: \*(ha_
+Acts as an end-of-file; this is useful because edit-mode input disables
+normal terminal input canonicalization.
+.It Xo eot\-or\-delete:
+.Op Ar n
+.No \*(haD
+.Xc
+Acts as
+.Ic eot
+if alone on a line; otherwise acts as
+.Ic delete\-char\-forward .
+.It error: (not bound)
+Error (ring the bell).
+.It exchange\-point\-and\-mark: \*(haX\*(haX
+Places the cursor where the mark is and sets the mark to where the cursor was.
+.It expand\-file: \*(ha[*
+Appends a
+.Ql *
+to the current word and replaces the word with the result of performing file
+globbing on the word.
+If no files match the pattern, the bell is rung.
+.It Xo forward\-char:
+.Op Ar n
+.No \*(haF , \*(haXC , ANSI-CurRight
+.Xc
+Moves the cursor forward
+.Ar n
+characters.
+.It Xo forward\-word:
+.Op Ar n
+.No \*(ha[f , ANSI-Ctrl-CurRight , ANSI-Alt-CurRight
+.Xc
+Moves the cursor forward to the end of the
+.Ar n Ns th
+word.
+.It Xo goto\-history:
+.Op Ar n
+.No \*(ha[g
+.Xc
+Goes to history number
+.Ar n .
+.It kill\-line: KILL
+Deletes the entire input line.
+.It kill\-region: \*(haW
+Deletes the input between the cursor and the mark.
+.It Xo kill\-to\-eol:
+.Op Ar n
+.No \*(haK
+.Xc
+Deletes the input from the cursor to the end of the line if
+.Ar n
+is not specified; otherwise deletes characters between the cursor and column
+.Ar n .
+.It list: \*(ha[?
+Prints a sorted, columnated list of command names or file names (if any) that
+can complete the partial word containing the cursor.
+Directory names have
+.Ql /
+appended to them.
+.It list\-command: \*(haX?
+Prints a sorted, columnated list of command names (if any) that can complete
+the partial word containing the cursor.
+.It list\-file: \*(haX\*(haY
+Prints a sorted, columnated list of file names (if any) that can complete the
+partial word containing the cursor.
+File type indicators are appended as described under
+.Ic list
+above.
+.It newline: \*(haJ , \*(haM
+Causes the current input line to be processed by the shell.
+The current cursor position may be anywhere on the line.
+.It newline\-and\-next: \*(haO
+Causes the current input line to be processed by the shell, and the next line
+from history becomes the current line.
+This is only useful after an
+.Ic up\-history ,
+.Ic search\-history
+or
+.Ic search\-history\-up .
+.It no\-op: QUIT
+This does nothing.
+.It prefix\-1: \*(ha[
+Introduces a 2-character command sequence.
+.It prefix\-2: \*(haX , \*(ha[[ , \*(ha[O
+Introduces a 2-character command sequence.
+.It Xo prev\-hist\-word:
+.Op Ar n
+.No \*(ha[. , \*(ha[_
+.Xc
+The last word, or, if given, the
+.Ar n Ns th
+word (zero-based) of the previous (on repeated execution, second-last,
+third-last, etc.) command is inserted at the cursor.
+Use of this editing command trashes the mark.
+.It quote: \*(ha\*(ha , \*(haV
+The following character is taken literally rather than as an editing command.
+.It redraw: \*(haL
+Reprints the last line of the prompt string and the current input line
+on a new line.
+.It Xo search\-character\-backward:
+.Op Ar n
+.No \*(ha[\*(ha]
+.Xc
+Search backward in the current line for the
+.Ar n Ns th
+occurrence of the next character typed.
+.It Xo search\-character\-forward:
+.Op Ar n
+.No \*(ha]
+.Xc
+Search forward in the current line for the
+.Ar n Ns th
+occurrence of the next character typed.
+.It search\-history: \*(haR
+Enter incremental search mode.
+The internal history list is searched
+backwards for commands matching the input.
+An initial
+.Ql \*(ha
+in the search string anchors the search.
+The escape key will leave search mode.
+Other commands, including sequences of escape as
+.Ic prefix\-1
+followed by a
+.Ic prefix\-1
+or
+.Ic prefix\-2
+key will be executed after leaving search mode.
+The
+.Ic abort Pq \*(haG
+command will restore the input line before search started.
+Successive
+.Ic search\-history
+commands continue searching backward to the next previous occurrence of the
+pattern.
+The history buffer retains only a finite number of lines; the oldest
+are discarded as necessary.
+.It search\-history\-up: ANSI-PgUp
+Search backwards through the history buffer for commands whose beginning match
+the portion of the input line before the cursor.
+When used on an empty line, this has the same effect as
+.Ic up\-history .
+.It search\-history\-down: ANSI-PgDn
+Search forwards through the history buffer for commands whose beginning match
+the portion of the input line before the cursor.
+When used on an empty line, this has the same effect as
+.Ic down\-history .
+This is only useful after an
+.Ic up\-history ,
+.Ic search\-history
+or
+.Ic search\-history\-up .
+.It set\-mark\-command: \*(ha[ Ns Aq space
+Set the mark at the cursor position.
+.It transpose\-chars: \*(haT
+If at the end of line, or if the
+.Ic gmacs
+option is set, this exchanges the two previous characters; otherwise, it
+exchanges the previous and current characters and moves the cursor one
+character to the right.
+.It Xo up\-history:
+.Op Ar n
+.No \*(haP , \*(haXA , ANSI-CurUp
+.Xc
+Scrolls the history buffer backward
+.Ar n
+lines (earlier).
+.It Xo upcase\-word:
+.Op Ar n
+.No \*(ha[U , \*(ha[u
+.Xc
+Uppercase the next
+.Ar n
+words.
+.It version: \*(ha[\*(haV
+Display the version of
+.Nm mksh .
+The current edit buffer is restored as soon as a key is pressed.
+The restoring keypress is processed, unless it is a space.
+.It yank: \*(haY
+Inserts the most recently killed text string at the current cursor position.
+.It yank\-pop: \*(ha[y
+Immediately after a
+.Ic yank ,
+replaces the inserted text string with the next previously killed text string.
+.El
+.Ss Vi editing mode
+.Em Note:
+The vi command-line editing mode is orphaned, yet still functional.
+It is 8-bit clean but specifically does not support UTF-8 or MBCS.
+.Pp
+The vi command-line editor in
+.Nm
+has basically the same commands as the
+.Xr vi 1
+editor with the following exceptions:
+.Bl -bullet
+.It
+You start out in insert mode.
+.It
+There are file name and command completion commands:
+=, \e, *, \*(haX, \*(haE, \*(haF, and, optionally,
+.Aq tab
+and
+.Aq esc .
+.It
+The
+.Ic _
+command is different (in
+.Nm mksh ,
+it is the last argument command; in
+.Xr vi 1
+it goes to the start of the current line).
+.It
+The
+.Ic /
+and
+.Ic G
+commands move in the opposite direction to the
+.Ic j
+command.
+.It
+Commands which don't make sense in a single line editor are not available
+(e.g. screen movement commands and
+.Xr ex 1 Ns -style
+colon
+.Pq Ic \&:
+commands).
+.El
+.Pp
+Like
+.Xr vi 1 ,
+there are two modes:
+.Dq insert
+mode and
+.Dq command
+mode.
+In insert mode, most characters are simply put in the buffer at the
+current cursor position as they are typed; however, some characters are
+treated specially.
+In particular, the following characters are taken from current
+.Xr tty 4
+settings
+(see
+.Xr stty 1 )
+and have their usual meaning (normal values are in parentheses): kill (\*(haU),
+erase (\*(ha?), werase (\*(haW), eof (\*(haD), intr (\*(haC), and quit (\*(ha\e).
+In addition to
+the above, the following characters are also treated specially in insert mode:
+.Bl -tag -width XJXXXXM
+.It \*(haE
+Command and file name enumeration (see below).
+.It \*(haF
+Command and file name completion (see below).
+If used twice in a row, the
+list of possible completions is displayed; if used a third time, the completion
+is undone.
+.It \*(haH
+Erases previous character.
+.It \*(haJ \*(Ba \*(haM
+End of line.
+The current line is read, parsed, and executed by the shell.
+.It \*(haV
+Literal next.
+The next character typed is not treated specially (can be used
+to insert the characters being described here).
+.It \*(haX
+Command and file name expansion (see below).
+.It Aq esc
+Puts the editor in command mode (see below).
+.It Aq tab
+Optional file name and command completion (see
+.Ic \*(haF
+above), enabled with
+.Ic set \-o vi\-tabcomplete .
+.El
+.Pp
+In command mode, each character is interpreted as a command.
+Characters that
+don't correspond to commands, are illegal combinations of commands, or are
+commands that can't be carried out, all cause beeps.
+In the following command descriptions, an
+.Op Ar n
+indicates the command may be prefixed by a number (e.g.\&
+.Ic 10l
+moves right 10 characters); if no number prefix is used,
+.Ar n
+is assumed to be 1 unless otherwise specified.
+The term
+.Dq current position
+refers to the position between the cursor and the character preceding the
+cursor.
+A
+.Dq word
+is a sequence of letters, digits, and underscore characters or a sequence of
+non-letter, non-digit, non-underscore, and non-whitespace characters (e.g.\&
+.Dq ab2*&\*(ha
+contains two words) and a
+.Dq big-word
+is a sequence of non-whitespace characters.
+.Pp
+Special
+.Nm
+vi commands:
+.Pp
+The following commands are not in, or are different from, the normal vi file
+editor:
+.Bl -tag -width 10n
+.It Xo
+.Oo Ar n Oc Ns _
+.Xc
+Insert a space followed by the
+.Ar n Ns th
+big-word from the last command in the history at the current position and enter
+insert mode; if
+.Ar n
+is not specified, the last word is inserted.
+.It #
+Insert the comment character
+.Pq Sq #
+at the start of the current line and return the line to the shell (equivalent
+to
+.Ic I#\*(haJ ) .
+.It Xo
+.Oo Ar n Oc Ns g
+.Xc
+Like
+.Ic G ,
+except if
+.Ar n
+is not specified, it goes to the most recent remembered line.
+.It Xo
+.Oo Ar n Oc Ns v
+.Xc
+Edit line
+.Ar n
+using the
+.Xr vi 1
+editor; if
+.Ar n
+is not specified, the current line is edited.
+The actual command executed is
+.Ic fc \-e ${VISUAL:\-${EDITOR:\-vi}} Ar n .
+.It * and \*(haX
+Command or file name expansion is applied to the current big-word (with an
+appended
+.Ql *
+if the word contains no file globbing characters) \*(en the big-word is replaced
+with the resulting words.
+If the current big-word is the first on the line
+or follows one of the characters
+.Ql \&; ,
+.Ql \*(Ba ,
+.Ql & ,
+.Ql \&( ,
+or
+.Ql \&) ,
+and does not contain a slash
+.Pq Sq / ,
+then command expansion is done; otherwise file name expansion is done.
+Command expansion will match the big-word against all aliases, functions, and
+built-in commands as well as any executable files found by searching the
+directories in the
+.Ev PATH
+parameter.
+File name expansion matches the big-word against the files in the
+current directory.
+After expansion, the cursor is placed just past the last
+word and the editor is in insert mode.
+.It Xo
+.Oo Ar n Oc Ns \e ,
+.Oo Ar n Oc Ns \*(haF ,
+.Oo Ar n Oc Ns Aq tab ,
+.No and
+.Oo Ar n Oc Ns Aq esc
+.Xc
+Command/file name completion.
+Replace the current big-word with the
+longest unique match obtained after performing command and file name expansion.
+.Aq tab
+is only recognised if the
+.Ic vi\-tabcomplete
+option is set, while
+.Aq esc
+is only recognised if the
+.Ic vi\-esccomplete
+option is set (see
+.Ic set \-o ) .
+If
+.Ar n
+is specified, the
+.Ar n Ns th
+possible completion is selected (as reported by the command/file name
+enumeration command).
+.It = and \*(haE
+Command/file name enumeration.
+List all the commands or files that match the current big-word.
+.It \*(haV
+Display the version of
+.Nm mksh .
+The current edit buffer is restored as soon as a key is pressed.
+The restoring keypress is ignored.
+.It @ Ns Ar c
+Macro expansion.
+Execute the commands found in the alias
+.Ar c .
+.El
+.Pp
+Intra-line movement commands:
+.Bl -tag -width Ds
+.It Xo
+.Oo Ar n Oc Ns h and
+.Oo Ar n Oc Ns \*(haH
+.Xc
+Move left
+.Ar n
+characters.
+.It Xo
+.Oo Ar n Oc Ns l and
+.Oo Ar n Oc Ns Aq space
+.Xc
+Move right
+.Ar n
+characters.
+.It 0
+Move to column 0.
+.It \*(ha
+Move to the first non-whitespace character.
+.It Xo
+.Oo Ar n Oc Ns \*(Ba
+.Xc
+Move to column
+.Ar n .
+.It $
+Move to the last character.
+.It Xo
+.Oo Ar n Oc Ns b
+.Xc
+Move back
+.Ar n
+words.
+.It Xo
+.Oo Ar n Oc Ns B
+.Xc
+Move back
+.Ar n
+big-words.
+.It Xo
+.Oo Ar n Oc Ns e
+.Xc
+Move forward to the end of the word,
+.Ar n
+times.
+.It Xo
+.Oo Ar n Oc Ns E
+.Xc
+Move forward to the end of the big-word,
+.Ar n
+times.
+.It Xo
+.Oo Ar n Oc Ns w
+.Xc
+Move forward
+.Ar n
+words.
+.It Xo
+.Oo Ar n Oc Ns W
+.Xc
+Move forward
+.Ar n
+big-words.
+.It %
+Find match.
+The editor looks forward for the nearest parenthesis, bracket, or
+brace and then moves the cursor to the matching parenthesis, bracket, or brace.
+.It Xo
+.Oo Ar n Oc Ns f Ns Ar c
+.Xc
+Move forward to the
+.Ar n Ns th
+occurrence of the character
+.Ar c .
+.It Xo
+.Oo Ar n Oc Ns F Ns Ar c
+.Xc
+Move backward to the
+.Ar n Ns th
+occurrence of the character
+.Ar c .
+.It Xo
+.Oo Ar n Oc Ns t Ns Ar c
+.Xc
+Move forward to just before the
+.Ar n Ns th
+occurrence of the character
+.Ar c .
+.It Xo
+.Oo Ar n Oc Ns T Ns Ar c
+.Xc
+Move backward to just before the
+.Ar n Ns th
+occurrence of the character
+.Ar c .
+.It Xo
+.Oo Ar n Oc Ns \&;
+.Xc
+Repeats the last
+.Ic f , F , t ,
+or
+.Ic T
+command.
+.It Xo
+.Oo Ar n Oc Ns \&,
+.Xc
+Repeats the last
+.Ic f , F , t ,
+or
+.Ic T
+command, but moves in the opposite direction.
+.El
+.Pp
+Inter-line movement commands:
+.Bl -tag -width Ds
+.It Xo
+.Oo Ar n Oc Ns j ,
+.Oo Ar n Oc Ns + ,
+.No and
+.Oo Ar n Oc Ns \*(haN
+.Xc
+Move to the
+.Ar n Ns th
+next line in the history.
+.It Xo
+.Oo Ar n Oc Ns k ,
+.Oo Ar n Oc Ns \- ,
+.No and
+.Oo Ar n Oc Ns \*(haP
+.Xc
+Move to the
+.Ar n Ns th
+previous line in the history.
+.It Xo
+.Oo Ar n Oc Ns G
+.Xc
+Move to line
+.Ar n
+in the history; if
+.Ar n
+is not specified, the number of the first remembered line is used.
+.It Xo
+.Oo Ar n Oc Ns g
+.Xc
+Like
+.Ic G ,
+except if
+.Ar n
+is not specified, it goes to the most recent remembered line.
+.It Xo
+.Oo Ar n Oc Ns / Ns Ar string
+.Xc
+Search backward through the history for the
+.Ar n Ns th
+line containing
+.Ar string ;
+if
+.Ar string
+starts with
+.Ql \*(ha ,
+the remainder of the string must appear at the start of the history line for
+it to match.
+.It Xo
+.Oo Ar n Oc Ns \&? Ns Ar string
+.Xc
+Same as
+.Ic / ,
+except it searches forward through the history.
+.It Xo
+.Oo Ar n Oc Ns n
+.Xc
+Search for the
+.Ar n Ns th
+occurrence of the last search string;
+the direction of the search is the same as the last search.
+.It Xo
+.Oo Ar n Oc Ns N
+.Xc
+Search for the
+.Ar n Ns th
+occurrence of the last search string;
+the direction of the search is the opposite of the last search.
+.It Ar ANSI-CurUp
+Take the characters from the beginning of the line to the current
+cursor position as search string and do a backwards history search
+for lines beginning with this string; keep the cursor position.
+This works only in insert mode and keeps it enabled.
+.El
+.Pp
+Edit commands
+.Bl -tag -width Ds
+.It Xo
+.Oo Ar n Oc Ns a
+.Xc
+Append text
+.Ar n
+times; goes into insert mode just after the current position.
+The append is
+only replicated if command mode is re-entered i.e.\&
+.Aq esc
+is used.
+.It Xo
+.Oo Ar n Oc Ns A
+.Xc
+Same as
+.Ic a ,
+except it appends at the end of the line.
+.It Xo
+.Oo Ar n Oc Ns i
+.Xc
+Insert text
+.Ar n
+times; goes into insert mode at the current position.
+The insertion is only
+replicated if command mode is re-entered i.e.\&
+.Aq esc
+is used.
+.It Xo
+.Oo Ar n Oc Ns I
+.Xc
+Same as
+.Ic i ,
+except the insertion is done just before the first non-blank character.
+.It Xo
+.Oo Ar n Oc Ns s
+.Xc
+Substitute the next
+.Ar n
+characters (i.e. delete the characters and go into insert mode).
+.It S
+Substitute whole line.
+All characters from the first non-blank character to the
+end of the line are deleted and insert mode is entered.
+.It Xo
+.Oo Ar n Oc Ns c Ns Ar move-cmd
+.Xc
+Change from the current position to the position resulting from
+.Ar n move-cmd Ns s
+(i.e. delete the indicated region and go into insert mode); if
+.Ar move-cmd
+is
+.Ic c ,
+the line starting from the first non-blank character is changed.
+.It C
+Change from the current position to the end of the line (i.e. delete to the
+end of the line and go into insert mode).
+.It Xo
+.Oo Ar n Oc Ns x
+.Xc
+Delete the next
+.Ar n
+characters.
+.It Xo
+.Oo Ar n Oc Ns X
+.Xc
+Delete the previous
+.Ar n
+characters.
+.It D
+Delete to the end of the line.
+.It Xo
+.Oo Ar n Oc Ns d Ns Ar move-cmd
+.Xc
+Delete from the current position to the position resulting from
+.Ar n move-cmd Ns s ;
+.Ar move-cmd
+is a movement command (see above) or
+.Ic d ,
+in which case the current line is deleted.
+.It Xo
+.Oo Ar n Oc Ns r Ns Ar c
+.Xc
+Replace the next
+.Ar n
+characters with the character
+.Ar c .
+.It Xo
+.Oo Ar n Oc Ns R
+.Xc
+Replace.
+Enter insert mode but overwrite existing characters instead of
+inserting before existing characters.
+The replacement is repeated
+.Ar n
+times.
+.It Xo
+.Oo Ar n Oc Ns \*(TI
+.Xc
+Change the case of the next
+.Ar n
+characters.
+.It Xo
+.Oo Ar n Oc Ns y Ns Ar move-cmd
+.Xc
+Yank from the current position to the position resulting from
+.Ar n move-cmd Ns s
+into the yank buffer; if
+.Ar move-cmd
+is
+.Ic y ,
+the whole line is yanked.
+.It Y
+Yank from the current position to the end of the line.
+.It Xo
+.Oo Ar n Oc Ns p
+.Xc
+Paste the contents of the yank buffer just after the current position,
+.Ar n
+times.
+.It Xo
+.Oo Ar n Oc Ns P
+.Xc
+Same as
+.Ic p ,
+except the buffer is pasted at the current position.
+.El
+.Pp
+Miscellaneous vi commands
+.Bl -tag -width Ds
+.It \*(haJ and \*(haM
+The current line is read, parsed, and executed by the shell.
+.It \*(haL and \*(haR
+Redraw the current line.
+.It Xo
+.Oo Ar n Oc Ns \&.
+.Xc
+Redo the last edit command
+.Ar n
+times.
+.It u
+Undo the last edit command.
+.It U
+Undo all changes that have been made to the current line.
+.It Ar intr No and Ar quit
+The interrupt and quit terminal characters cause the current line to be
+deleted and a new prompt to be printed.
+.El
+.Sh FILES
+.Bl -tag -width XetcXsuid_profile -compact
+.It Pa \*(TI/.mkshrc
+User mkshrc profile (non-privileged interactive shells); see
+.Sx Startup files.
+The location can be changed at compile time (for embedded systems);
+AOSP Android builds use
+.Pa /system/etc/mkshrc .
+.It Pa \*(TI/.profile
+User profile (non-privileged login shells); see
+.Sx Startup files
+near the top of this manual.
+.It Pa /etc/profile
+System profile (login shells); see
+.Sx Startup files.
+.It Pa /etc/shells
+Shell database.
+.It Pa /etc/suid_profile
+Suid profile (privileged shells); see
+.Sx Startup files.
+.El
+.Pp
+Note: On Android,
+.Pa /system/etc/
+contains the system and suid profile.
+.Sh SEE ALSO
+.Xr awk 1 ,
+.Xr cat 1 ,
+.Xr ed 1 ,
+.Xr getopt 1 ,
+.Xr sed 1 ,
+.Xr sh 1 ,
+.Xr stty 1 ,
+.Xr dup 2 ,
+.Xr execve 2 ,
+.Xr getgid 2 ,
+.Xr getuid 2 ,
+.Xr mknod 2 ,
+.Xr mkfifo 2 ,
+.Xr open 2 ,
+.Xr pipe 2 ,
+.Xr rename 2 ,
+.Xr wait 2 ,
+.Xr getopt 3 ,
+.Xr nl_langinfo 3 ,
+.Xr setlocale 3 ,
+.Xr signal 3 ,
+.Xr system 3 ,
+.Xr tty 4 ,
+.Xr shells 5 ,
+.Xr environ 7 ,
+.Xr script 7 ,
+.Xr utf\-8 7 ,
+.Xr mknod 8
+.Pp
+.Pa http://docsrv.sco.com:507/en/man/html.C/sh.C.html
+.Pp
+.Pa https://www.mirbsd.org/ksh\-chan.htm
+.Rs
+.%A Morris Bolsky
+.%B "The KornShell Command and Programming Language"
+.%D 1989
+.%I "Prentice Hall PTR"
+.%P "xvi\ +\ 356 pages"
+.%O "ISBN 978\-0\-13\-516972\-8 (0\-13\-516972\-0)"
+.Re
+.Rs
+.%A Morris I. Bolsky
+.%A David G. Korn
+.%B "The New KornShell Command and Programming Language (2nd Edition)"
+.%D 1995
+.%I "Prentice Hall PTR"
+.%P "xvi\ +\ 400 pages"
+.%O "ISBN 978\-0\-13\-182700\-4 (0\-13\-182700\-6)"
+.Re
+.Rs
+.%A Stephen G. Kochan
+.%A Patrick H. Wood
+.%B "\\*(tNUNIX\\*(sP Shell Programming"
+.%V "3rd Edition"
+.%D 2003
+.%I "Sams"
+.%P "xiii\ +\ 437 pages"
+.%O "ISBN 978\-0\-672\-32490\-1 (0\-672\-32490\-3)"
+.Re
+.Rs
+.%A "IEEE Inc."
+.%T "\\*(tNIEEE\\*(sP Standard for Information Technology \*(en Portable Operating System Interface (POSIX)"
+.%V "Part 2: Shell and Utilities"
+.%D 1993
+.%I "IEEE Press"
+.%P "xvii\ +\ 1195 pages"
+.%O "ISBN 978\-1\-55937\-255\-8 (1\-55937\-255\-9)"
+.Re
+.Rs
+.%A Bill Rosenblatt
+.%B "Learning the Korn Shell"
+.%D 1993
+.%I "O'Reilly"
+.%P "360 pages"
+.%O "ISBN 978\-1\-56592\-054\-5 (1\-56592\-054\-6)"
+.Re
+.Rs
+.%A Bill Rosenblatt
+.%A Arnold Robbins
+.%B "Learning the Korn Shell, Second Edition"
+.%D 2002
+.%I "O'Reilly"
+.%P "432 pages"
+.%O "ISBN 978\-0\-596\-00195\-7 (0\-596\-00195\-9)"
+.Re
+.Rs
+.%A Barry Rosenberg
+.%B "KornShell Programming Tutorial"
+.%D 1991
+.%I "Addison-Wesley Professional"
+.%P "xxi\ +\ 324 pages"
+.%O "ISBN 978\-0\-201\-56324\-5 (0\-201\-56324\-X)"
+.Re
+.Sh AUTHORS
+.An -nosplit
+.Nm "The MirBSD Korn Shell"
+is developed by
+.An Thorsten Glaser Aq tg at mirbsd.org
+and currently maintained as part of The MirOS Project.
+This shell is based on the public domain 7th edition Bourne shell clone by
+.An Charles Forsyth ,
+who kindly agreed to, in countries where the Public Domain status of the work
+may not be valid, grant a copyright licence to the general public to deal in
+the work without restriction and permission to sublicence derivates under the
+terms of any (OSI approved) Open Source licence,
+and parts of the BRL shell by
+.An Doug A. Gwyn ,
+.An Doug Kingston ,
+.An Ron Natalie ,
+.An Arnold Robbins ,
+.An Lou Salkind ,
+and others.
+The first release of
+.Nm pdksh
+was created by
+.An Eric Gisin ,
+and it was subsequently maintained by
+.An John R. MacMillan Aq Mt change!john at sq.sq.com ,
+.An Simon J. Gerraty Aq Mt sjg at zen.void.oz.au ,
+and
+.An Michael Rendell Aq Mt michael at cs.mun.ca .
+The effort of several projects, such as Debian and OpenBSD, and other
+contributors including our users, to improve the shell is appreciated.
+See the documentation, CVS, and web site for details.
+.Pp
+The BSD daemon is Copyright \(co Marshall Kirk McKusick.
+The complete legalese is at:
+.Pa https://www.mirbsd.org/TaC\-mksh.txt
+.\"
+.\" This boils down to: feel free to use mksh.ico as application icon
+.\" or shortcut for mksh or mksh/Win32; distro patches are ok (but we
+.\" request they amend $KSH_VERSION when modifying mksh). Authors are
+.\" Marshall Kirk McKusick (UCB), Rick Collette (ekkoBSD), Thorsten
+.\" Glaser, Benny Siegert (MirBSD), Michael Langguth (mksh/Win32).
+.\"
+.\" As far as MirBSD is concerned, the files themselves are free
+.\" to modification and distribution under BSD/MirOS Licence, the
+.\" restriction on use stems only from trademark law's requirement
+.\" to protect it or lose it, which McKusick almost did.
+.\"
+.Sh CAVEATS
+.Nm
+only supports the Unicode BMP (Basic Multilingual Plane).
+.Pp
+.Nm
+has a different scope model from
+.At
+.Nm ksh ,
+which leads to subtile differences in semantics for identical builtins.
+This can cause issues with a
+.Ic nameref
+to suddenly point to a local variable by accident; fixing this is hard.
+.Pp
+The parts of a pipeline, like below, are executed in subshells.
+Thus, variable assignments inside them are not visible in the
+surrounding execution environment.
+Use co-processes instead.
+.Bd -literal -offset indent
+foo \*(Ba bar \*(Ba read baz            # will not change $baz
+foo \*(Ba bar \*(Ba& read \-p baz        # will, however, do so
+.Ed
+.Pp
+.Nm mksh
+provides a consistent set of 32-bit integer arithmetics, both signed
+and unsigned, with defined wraparound and sign of the result of a modulo
+operation, even (defying POSIX) on 64-bit systems.
+If you require 64-bit integer arithmetics, use
+.Nm lksh Pq legacy mksh
+instead, but be aware that, in POSIX, it's legal for the OS to make
+.Li print $((2147483647 + 1))
+delete all files on your system, as it's Undefined Behaviour.
+.Sh BUGS
+Suspending (using \*(haZ) pipelines like the one below will only suspend
+the currently running part of the pipeline; in this example,
+.Dq fubar
+is immediately printed on suspension (but not later after an
+.Ic fg ) .
+.Bd -literal -offset indent
+$ /bin/sleep 666 && echo fubar
+.Ed
+.Pp
+This document attempts to describe
+.Nm mksh\ R50
+and up,
+compiled without any options impacting functionality, such as
+.Dv MKSH_SMALL ,
+when not called as
+.Pa /bin/sh
+which, on some systems only, enables
+.Ic set \-o sh
+automatically (whose behaviour differs across targets),
+for an operating environment supporting all of its advanced needs.
+Please report bugs in
+.Nm
+to the
+.Mx
+mailing list at
+.Aq miros\-mksh at mirbsd.org
+or in the
+.Li \&#\&!/bin/mksh
+.Pq or Li \&#ksh
+IRC channel at
+.Pa irc.freenode.net
+.Pq Port 6697 SSL, 6667 unencrypted ,
+or at:
+.Pa https://launchpad.net/mksh

Copied: vendor/MirOS/mksh/R50/rlimits.opt (from rev 6707, vendor/MirOS/mksh/dist/rlimits.opt)
===================================================================
--- vendor/MirOS/mksh/R50/rlimits.opt	                        (rev 0)
+++ vendor/MirOS/mksh/R50/rlimits.opt	2014-07-01 12:01:17 UTC (rev 6708)
@@ -0,0 +1,96 @@
+ at RLIMITS_DEFNS
+__RCSID("$MirOS: src/bin/mksh/rlimits.opt,v 1.1 2013/11/17 22:21:18 tg Exp $");
+struct limits {
+	/* limit resource */
+	int resource;
+	/* multiply by to get rlim_{cur,max} values */
+	unsigned int factor;
+	/* getopts char */
+	char optchar;
+	/* limit name */
+	char name[1];
+};
+#define FN(lname,lid,lfac,lopt)					static const struct {						int resource;						unsigned int factor;					char optchar;						char name[sizeof(lname)];			} rlimits_ ## lid = {						lid, lfac, lopt, lname				};
+ at RLIMITS_ITEMS
+#define FN(lname,lid,lfac,lopt)					(const struct limits *)(&rlimits_ ## lid),
+@@
+
+/* generic options for the ulimit builtin */
+
+<a|
+<H|
+<S|
+
+/* do not use options -H, -S or -a or change the order */
+
+>t|RLIMIT_CPU
+FN("time(cpu-seconds)", RLIMIT_CPU, 1
+
+>f|RLIMIT_FSIZE
+FN("file(blocks)", RLIMIT_FSIZE, 512
+
+>c|RLIMIT_CORE
+FN("coredump(blocks)", RLIMIT_CORE, 512
+
+>d|RLIMIT_DATA
+FN("data(KiB)", RLIMIT_DATA, 1024
+
+>s|RLIMIT_STACK
+FN("stack(KiB)", RLIMIT_STACK, 1024
+
+>l|RLIMIT_MEMLOCK
+FN("lockedmem(KiB)", RLIMIT_MEMLOCK, 1024
+
+>n|RLIMIT_NOFILE
+FN("nofiles(descriptors)", RLIMIT_NOFILE, 1
+
+>p|RLIMIT_NPROC
+FN("processes", RLIMIT_NPROC, 1
+
+>w|RLIMIT_SWAP
+FN("swap(KiB)", RLIMIT_SWAP, 1024
+
+>T|RLIMIT_TIME
+FN("humantime(seconds)", RLIMIT_TIME, 1
+
+>V|RLIMIT_NOVMON
+FN("vnodemonitors", RLIMIT_NOVMON, 1
+
+>i|RLIMIT_SIGPENDING
+FN("sigpending", RLIMIT_SIGPENDING, 1
+
+>q|RLIMIT_MSGQUEUE
+FN("msgqueue(bytes)", RLIMIT_MSGQUEUE, 1
+
+>M|RLIMIT_AIO_MEM
+FN("AIOlockedmem(KiB)", RLIMIT_AIO_MEM, 1024
+
+>O|RLIMIT_AIO_OPS
+FN("AIOoperations", RLIMIT_AIO_OPS, 1
+
+>C|RLIMIT_TCACHE
+FN("cachedthreads", RLIMIT_TCACHE, 1
+
+>B|RLIMIT_SBSIZE
+FN("sockbufsiz(KiB)", RLIMIT_SBSIZE, 1024
+
+>P|RLIMIT_PTHREAD
+FN("threadsperprocess", RLIMIT_PTHREAD, 1
+
+>e|RLIMIT_NICE
+FN("maxnice", RLIMIT_NICE, 1
+
+>r|RLIMIT_RTPRIO
+FN("maxrtprio", RLIMIT_RTPRIO, 1
+
+>m|ULIMIT_M_IS_RSS
+FN("resident-set(KiB)", RLIMIT_RSS, 1024
+>m|ULIMIT_M_IS_VMEM
+FN("memory(KiB)", RLIMIT_VMEM, 1024
+
+>v|ULIMIT_V_IS_VMEM
+FN("virtual-memory(KiB)", RLIMIT_VMEM, 1024
+>v|ULIMIT_V_IS_AS
+FN("address-space(KiB)", RLIMIT_AS, 1024
+
+|RLIMITS_OPTCS

Deleted: vendor/MirOS/mksh/R50/sh.h
===================================================================
--- vendor/MirOS/mksh/dist/sh.h	2014-06-30 23:58:12 UTC (rev 6706)
+++ vendor/MirOS/mksh/R50/sh.h	2014-07-01 12:01:17 UTC (rev 6708)
@@ -1,2079 +0,0 @@
-/*	$OpenBSD: sh.h,v 1.31 2012/09/10 01:25:30 tedu Exp $	*/
-/*	$OpenBSD: shf.h,v 1.6 2005/12/11 18:53:51 deraadt Exp $	*/
-/*	$OpenBSD: table.h,v 1.8 2012/02/19 07:52:30 otto Exp $	*/
-/*	$OpenBSD: tree.h,v 1.10 2005/03/28 21:28:22 deraadt Exp $	*/
-/*	$OpenBSD: expand.h,v 1.6 2005/03/30 17:16:37 deraadt Exp $	*/
-/*	$OpenBSD: lex.h,v 1.13 2013/03/03 19:11:34 guenther Exp $	*/
-/*	$OpenBSD: proto.h,v 1.34 2012/06/27 07:17:19 otto Exp $	*/
-/*	$OpenBSD: c_test.h,v 1.4 2004/12/20 11:34:26 otto Exp $	*/
-/*	$OpenBSD: tty.h,v 1.5 2004/12/20 11:34:26 otto Exp $	*/
-
-/*-
- * Copyright © 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
- *	       2011, 2012, 2013
- *	Thorsten Glaser <tg at mirbsd.org>
- *
- * Provided that these terms and disclaimer and all copyright notices
- * are retained or reproduced in an accompanying document, permission
- * is granted to deal in this work without restriction, including un‐
- * limited rights to use, publicly perform, distribute, sell, modify,
- * merge, give away, or sublicence.
- *
- * This work is provided “AS IS” and WITHOUT WARRANTY of any kind, to
- * the utmost extent permitted by applicable law, neither express nor
- * implied; without malicious intent or gross negligence. In no event
- * may a licensor, author or contributor be held liable for indirect,
- * direct, other damage, loss, or other issues arising in any way out
- * of dealing in the work, even if advised of the possibility of such
- * damage or existence of a defect, except proven that it results out
- * of said person’s immediate fault when using the work as intended.
- */
-
-#ifdef __dietlibc__
-/* XXX imake style */
-#define _BSD_SOURCE	/* live, BSD, live❣ */
-#endif
-
-#if HAVE_SYS_PARAM_H
-#include <sys/param.h>
-#endif
-#include <sys/types.h>
-#if HAVE_BOTH_TIME_H
-#include <sys/time.h>
-#include <time.h>
-#elif HAVE_SYS_TIME_H
-#include <sys/time.h>
-#elif HAVE_TIME_H
-#include <time.h>
-#endif
-#include <sys/ioctl.h>
-#if HAVE_SYS_SYSMACROS_H
-#include <sys/sysmacros.h>
-#endif
-#if HAVE_SYS_MKDEV_H
-#include <sys/mkdev.h>
-#endif
-#if HAVE_SYS_MMAN_H
-#include <sys/mman.h>
-#endif
-#if HAVE_SYS_RESOURCE_H
-#include <sys/resource.h>
-#endif
-#include <sys/stat.h>
-#include <sys/wait.h>
-#include <dirent.h>
-#include <errno.h>
-#include <fcntl.h>
-#if HAVE_LIBGEN_H
-#include <libgen.h>
-#endif
-#if HAVE_LIBUTIL_H
-#include <libutil.h>
-#endif
-#include <limits.h>
-#if HAVE_PATHS_H
-#include <paths.h>
-#endif
-#include <pwd.h>
-#include <setjmp.h>
-#include <signal.h>
-#include <stdarg.h>
-#include <stddef.h>
-#if HAVE_STDINT_H
-#include <stdint.h>
-#endif
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#if HAVE_STRINGS_H
-#include <strings.h>
-#endif
-#if HAVE_TERMIOS_H
-#include <termios.h>
-#else
-/* shudder… */
-#include <termio.h>
-#endif
-#ifdef _ISC_UNIX
-/* XXX imake style */
-#include <sys/sioctl.h>
-#endif
-#if HAVE_ULIMIT_H
-#include <ulimit.h>
-#endif
-#include <unistd.h>
-#if HAVE_VALUES_H
-#include <values.h>
-#endif
-
-#undef __attribute__
-#if HAVE_ATTRIBUTE_BOUNDED
-#define MKSH_A_BOUNDED(x,y,z)	__attribute__((__bounded__ (x, y, z)))
-#else
-#define MKSH_A_BOUNDED(x,y,z)	/* nothing */
-#endif
-#if HAVE_ATTRIBUTE_FORMAT
-#define MKSH_A_FORMAT(x,y,z)	__attribute__((__format__ (x, y, z)))
-#else
-#define MKSH_A_FORMAT(x,y,z)	/* nothing */
-#endif
-#if HAVE_ATTRIBUTE_NORETURN
-#define MKSH_A_NORETURN		__attribute__((__noreturn__))
-#else
-#define MKSH_A_NORETURN		/* nothing */
-#endif
-#if HAVE_ATTRIBUTE_UNUSED
-#define MKSH_A_UNUSED		__attribute__((__unused__))
-#else
-#define MKSH_A_UNUSED		/* nothing */
-#endif
-#if HAVE_ATTRIBUTE_USED
-#define MKSH_A_USED		__attribute__((__used__))
-#else
-#define MKSH_A_USED		/* nothing */
-#endif
-
-#if defined(MirBSD) && (MirBSD >= 0x09A1) && \
-    defined(__ELF__) && defined(__GNUC__) && \
-    !defined(__llvm__) && !defined(__NWCC__)
-/*
- * We got usable __IDSTRING __COPYRIGHT __RCSID __SCCSID macros
- * which work for all cases; no need to redefine them using the
- * "portable" macros from below when we might have the "better"
- * gcc+ELF specific macros or other system dependent ones.
- */
-#else
-#undef __IDSTRING
-#undef __IDSTRING_CONCAT
-#undef __IDSTRING_EXPAND
-#undef __COPYRIGHT
-#undef __RCSID
-#undef __SCCSID
-#define __IDSTRING_CONCAT(l,p)		__LINTED__ ## l ## _ ## p
-#define __IDSTRING_EXPAND(l,p)		__IDSTRING_CONCAT(l,p)
-#ifdef MKSH_DONT_EMIT_IDSTRING
-#define __IDSTRING(prefix, string)	/* nothing */
-#else
-#define __IDSTRING(prefix, string)				\
-	static const char __IDSTRING_EXPAND(__LINE__,prefix) []	\
-	    MKSH_A_USED = "@(""#)" #prefix ": " string
-#endif
-#define __COPYRIGHT(x)		__IDSTRING(copyright,x)
-#define __RCSID(x)		__IDSTRING(rcsid,x)
-#define __SCCSID(x)		__IDSTRING(sccsid,x)
-#endif
-
-#ifdef EXTERN
-__RCSID("$MirOS: src/bin/mksh/sh.h,v 1.667 2013/08/14 20:26:19 tg Exp $");
-#endif
-#define MKSH_VERSION "R48 2013/08/14"
-
-/* arithmetic types: C implementation */
-#if !HAVE_CAN_INTTYPES
-#if !HAVE_CAN_UCBINTS
-typedef signed int int32_t;
-typedef unsigned int uint32_t;
-#else
-typedef u_int32_t uint32_t;
-#endif
-#endif
-
-/* arithmetic types: shell arithmetics */
-#ifdef MKSH_LEGACY_MODE
-/*
- * POSIX demands these to be the C environment's long type
- */
-typedef long mksh_ari_t;
-typedef unsigned long mksh_uari_t;
-#else
-/*
- * These types are exactly 32 bit wide; signed and unsigned
- * integer wraparound, even across division and modulo, for
- * any shell code using them, is guaranteed.
- */
-typedef int32_t mksh_ari_t;
-typedef uint32_t mksh_uari_t;
-#endif
-
-/* boolean type (no <stdbool.h> deliberately) */
-typedef unsigned char mksh_bool;
-#undef bool
-/* false MUST equal the same 0 as written by static storage initialisation */
-#undef false
-#undef true
-/* access macros for boolean type */
-#define bool		mksh_bool
-/* values must have identity mapping between mksh_bool and short */
-#define false		0
-#define true		1
-/* make any-type into bool or short */
-#define tobool(cond)	((cond) ? true : false)
-
-/* char (octet) type: C implementation */
-#if !HAVE_CAN_INT8TYPE
-#if !HAVE_CAN_UCBINT8
-typedef unsigned char uint8_t;
-#else
-typedef u_int8_t uint8_t;
-#endif
-#endif
-
-/* other standard types */
-
-#if !HAVE_RLIM_T
-typedef unsigned long rlim_t;
-#endif
-
-#if !HAVE_SIG_T
-#undef sig_t
-typedef void (*sig_t)(int);
-#endif
-
-#ifdef MKSH_TYPEDEF_SIG_ATOMIC_T
-typedef MKSH_TYPEDEF_SIG_ATOMIC_T sig_atomic_t;
-#endif
-
-#ifdef MKSH_TYPEDEF_SSIZE_T
-typedef MKSH_TYPEDEF_SSIZE_T ssize_t;
-#endif
-
-/* un-do vendor damage */
-
-#undef BAD		/* AIX defines that somewhere */
-#undef PRINT		/* LynxOS defines that somewhere */
-#undef flock		/* SCO UnixWare defines that to flock64 but ENOENT */
-
-
-#ifndef MKSH_INCLUDES_ONLY
-
-/* extra types */
-
-#if !HAVE_GETRUSAGE
-#undef rusage
-#undef RUSAGE_SELF
-#undef RUSAGE_CHILDREN
-#define rusage mksh_rusage
-#define RUSAGE_SELF		0
-#define RUSAGE_CHILDREN		-1
-
-struct rusage {
-	struct timeval ru_utime;
-	struct timeval ru_stime;
-};
-#endif
-
-/* extra macros */
-
-#ifndef timerclear
-#define timerclear(tvp)							\
-	do {								\
-		(tvp)->tv_sec = (tvp)->tv_usec = 0;			\
-	} while (/* CONSTCOND */ 0)
-#endif
-#ifndef timeradd
-#define timeradd(tvp, uvp, vvp)						\
-	do {								\
-		(vvp)->tv_sec = (tvp)->tv_sec + (uvp)->tv_sec;		\
-		(vvp)->tv_usec = (tvp)->tv_usec + (uvp)->tv_usec;	\
-		if ((vvp)->tv_usec >= 1000000) {			\
-			(vvp)->tv_sec++;				\
-			(vvp)->tv_usec -= 1000000;			\
-		}							\
-	} while (/* CONSTCOND */ 0)
-#endif
-#ifndef timersub
-#define timersub(tvp, uvp, vvp)						\
-	do {								\
-		(vvp)->tv_sec = (tvp)->tv_sec - (uvp)->tv_sec;		\
-		(vvp)->tv_usec = (tvp)->tv_usec - (uvp)->tv_usec;	\
-		if ((vvp)->tv_usec < 0) {				\
-			(vvp)->tv_sec--;				\
-			(vvp)->tv_usec += 1000000;			\
-		}							\
-	} while (/* CONSTCOND */ 0)
-#endif
-
-#define ksh_isdigit(c)	(((c) >= '0') && ((c) <= '9'))
-#define ksh_islower(c)	(((c) >= 'a') && ((c) <= 'z'))
-#define ksh_isupper(c)	(((c) >= 'A') && ((c) <= 'Z'))
-#define ksh_tolower(c)	(((c) >= 'A') && ((c) <= 'Z') ? (c) - 'A' + 'a' : (c))
-#define ksh_toupper(c)	(((c) >= 'a') && ((c) <= 'z') ? (c) - 'a' + 'A' : (c))
-#define ksh_isdash(s)	(((s) != NULL) && ((s)[0] == '-') && ((s)[1] == '\0'))
-#define ksh_isspace(c)	((((c) >= 0x09) && ((c) <= 0x0D)) || ((c) == 0x20))
-#define ksh_min(x,y)	((x) < (y) ? (x) : (y))
-#define ksh_max(x,y)	((x) > (y) ? (x) : (y))
-
-#ifdef MKSH__NO_PATH_MAX
-#undef PATH_MAX
-#else
-#ifndef PATH_MAX
-#define PATH_MAX	1024
-#endif
-#endif
-#ifndef SIZE_MAX
-#ifdef SIZE_T_MAX
-#define SIZE_MAX	SIZE_T_MAX
-#else
-#define SIZE_MAX	((size_t)-1)
-#endif
-#endif
-#ifndef S_ISLNK
-#define S_ISLNK(m)	((m & 0170000) == 0120000)
-#endif
-#ifndef S_ISSOCK
-#define S_ISSOCK(m)	((m & 0170000) == 0140000)
-#endif
-#if !defined(S_ISCDF) && defined(S_CDF)
-#define S_ISCDF(m)	(S_ISDIR(m) && ((m) & S_CDF))
-#endif
-#ifndef DEFFILEMODE
-#define DEFFILEMODE	(S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH)
-#endif
-
-#ifndef NSIG
-#if defined(_NSIG)
-#define NSIG		_NSIG
-#elif defined(SIGMAX)
-#define NSIG		(SIGMAX+1)
-#elif defined(_SIGMAX)
-#define NSIG		(_SIGMAX+1)
-#endif
-#endif
-
-
-/* OS-dependent additions (functions, variables, by OS) */
-
-#if !HAVE_FLOCK_DECL
-extern int flock(int, int);
-#endif
-
-#if !HAVE_GETTIMEOFDAY
-#define mksh_TIME(tv) do {		\
-	(tv).tv_usec = 0;		\
-	(tv).tv_sec = time(NULL);	\
-} while (/* CONSTCOND */ 0)
-#else
-#define mksh_TIME(tv) gettimeofday(&(tv), NULL)
-#endif
-
-#if !HAVE_GETRUSAGE
-extern int getrusage(int, struct rusage *);
-#endif
-
-#if !HAVE_MEMMOVE
-/* we assume either memmove or bcopy exist, at the moment */
-#define memmove(dst, src, len)	bcopy((src), (dst), (len))
-#endif
-
-#if !HAVE_REVOKE_DECL
-extern int revoke(const char *);
-#endif
-
-#if defined(DEBUG) || !HAVE_STRERROR
-#undef strerror
-#define strerror		/* poisoned */ dontuse_strerror
-#define cstrerror		/* replaced */ cstrerror
-extern const char *cstrerror(int);
-#else
-#define cstrerror(errnum)	((const char *)strerror(errnum))
-#endif
-
-#if !HAVE_STRLCPY
-size_t strlcpy(char *, const char *, size_t);
-#endif
-
-#ifdef __INTERIX
-/* XXX imake style */
-#define makedev mkdev
-extern int __cdecl seteuid(uid_t);
-extern int __cdecl setegid(gid_t);
-#endif
-
-#if defined(__COHERENT__)
-#ifndef O_ACCMODE
-/* this need not work everywhere, take care */
-#define O_ACCMODE	(O_RDONLY | O_WRONLY | O_RDWR)
-#endif
-#endif
-
-#ifdef MKSH__NO_SYMLINK
-#undef S_ISLNK
-#define S_ISLNK(m)	(/* CONSTCOND */ 0)
-#define mksh_lstat	stat
-#else
-#define mksh_lstat	lstat
-#endif
-
-#if HAVE_TERMIOS_H
-#define mksh_ttyst	struct termios
-#define mksh_tcget(fd,st) tcgetattr((fd), (st))
-#define mksh_tcset(fd,st) tcsetattr((fd), TCSADRAIN, (st))
-#else
-#define mksh_ttyst	struct termio
-#define mksh_tcget(fd,st) ioctl((fd), TCGETA, (st))
-#define mksh_tcset(fd,st) ioctl((fd), TCSETAW, (st))
-#endif
-
-/* remove redundancies */
-
-#if defined(MirBSD) && (MirBSD >= 0x0AB3) && !defined(MKSH_OPTSTATIC)
-#define MKSH_mirbsd_wcwidth
-#define utf_wcwidth(i) wcwidth((__WCHAR_TYPE__)i)
-extern int wcwidth(__WCHAR_TYPE__);
-#endif
-
-
-/* some useful #defines */
-#ifdef EXTERN
-# define E_INIT(i) = i
-#else
-# define E_INIT(i)
-# define EXTERN extern
-# define EXTERN_DEFINED
-#endif
-
-/* define bit in flag */
-#define BIT(i)		(1 << (i))
-#define NELEM(a)	(sizeof(a) / sizeof((a)[0]))
-
-/*
- * Make MAGIC a char that might be printed to make bugs more obvious, but
- * not a char that is used often. Also, can't use the high bit as it causes
- * portability problems (calling strchr(x, 0x80 | 'x') is error prone).
- */
-#define MAGIC		(7)	/* prefix for *?[!{,} during expand */
-#define ISMAGIC(c)	((unsigned char)(c) == MAGIC)
-
-EXTERN const char *safe_prompt; /* safe prompt if PS1 substitution fails */
-
-#ifdef MKSH_LEGACY_MODE
-#define KSH_VERSIONNAME	"LEGACY"
-#else
-#define KSH_VERSIONNAME	"MIRBSD"
-#endif
-EXTERN const char initvsn[] E_INIT("KSH_VERSION=@(#)" KSH_VERSIONNAME \
-    " KSH " MKSH_VERSION);
-#define KSH_VERSION	(initvsn + /* "KSH_VERSION=@(#)" */ 16)
-
-EXTERN const char digits_uc[] E_INIT("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ");
-EXTERN const char digits_lc[] E_INIT("0123456789abcdefghijklmnopqrstuvwxyz");
-
-/*
- * Evil hack for const correctness due to API brokenness
- */
-union mksh_cchack {
-	char *rw;
-	const char *ro;
-};
-union mksh_ccphack {
-	char **rw;
-	const char **ro;
-};
-
-/*
- * Evil hack since casting uint to sint is implementation-defined
- */
-typedef union {
-	mksh_ari_t i;
-	mksh_uari_t u;
-} mksh_ari_u;
-
-/* for const debugging */
-#if defined(DEBUG) && defined(__GNUC__) && !defined(__ICC) && \
-    !defined(__INTEL_COMPILER) && !defined(__SUNPRO_C)
-char *ucstrchr(char *, int);
-char *ucstrstr(char *, const char *);
-#undef strchr
-#define strchr ucstrchr
-#define strstr ucstrstr
-#define cstrchr(s,c) ({			\
-	union mksh_cchack in, out;	\
-					\
-	in.ro = (s);			\
-	out.rw = ucstrchr(in.rw, (c));	\
-	(out.ro);			\
-})
-#define cstrstr(b,l) ({			\
-	union mksh_cchack in, out;	\
-					\
-	in.ro = (b);			\
-	out.rw = ucstrstr(in.rw, (l));	\
-	(out.ro);			\
-})
-#define vstrchr(s,c)	(cstrchr((s), (c)) != NULL)
-#define vstrstr(b,l)	(cstrstr((b), (l)) != NULL)
-#else /* !DEBUG, !gcc */
-#define cstrchr(s,c)	((const char *)strchr((s), (c)))
-#define cstrstr(s,c)	((const char *)strstr((s), (c)))
-#define vstrchr(s,c)	(strchr((s), (c)) != NULL)
-#define vstrstr(b,l)	(strstr((b), (l)) != NULL)
-#endif
-
-#if defined(DEBUG) || defined(__COVERITY__)
-#define mkssert(e)	do { if (!(e)) exit(255); } while (/* CONSTCOND */ 0)
-#ifndef DEBUG_LEAKS
-#define DEBUG_LEAKS
-#endif
-#else
-#define mkssert(e)	do { } while (/* CONSTCOND */ 0)
-#endif
-
-#if (!defined(MKSH_BUILDMAKEFILE4BSD) && !defined(MKSH_BUILDSH)) || (MKSH_BUILD_R != 481)
-#error Must run Build.sh to compile this.
-extern void thiswillneverbedefinedIhope(void);
-int
-im_sorry_dave(void)
-{
-	/* I’m sorry, Dave. I’m afraid I can’t do that. */
-	return (thiswillneverbedefinedIhope());
-}
-#endif
-
-/* use this ipv strchr(s, 0) but no side effects in s! */
-#define strnul(s)	((s) + strlen(s))
-
-#define utf_ptradjx(src, dst) do {					\
-	(dst) = (src) + utf_ptradj(src);				\
-} while (/* CONSTCOND */ 0)
-
-#if defined(MKSH_SMALL) && !defined(MKSH_SMALL_BUT_FAST)
-#define strdupx(d, s, ap) do {						\
-	(d) = strdup_i((s), (ap));					\
-} while (/* CONSTCOND */ 0)
-#define strndupx(d, s, n, ap) do {					\
-	(d) = strndup_i((s), (n), (ap));				\
-} while (/* CONSTCOND */ 0)
-#else
-/* be careful to evaluate arguments only once! */
-#define strdupx(d, s, ap) do {						\
-	const char *strdup_src = (s);					\
-	char *strdup_dst = NULL;					\
-									\
-	if (strdup_src != NULL) {					\
-		size_t strdup_len = strlen(strdup_src) + 1;		\
-		strdup_dst = alloc(strdup_len, (ap));			\
-		memcpy(strdup_dst, strdup_src, strdup_len);		\
-	}								\
-	(d) = strdup_dst;						\
-} while (/* CONSTCOND */ 0)
-#define strndupx(d, s, n, ap) do {					\
-	const char *strdup_src = (s);					\
-	char *strdup_dst = NULL;					\
-									\
-	if (strdup_src != NULL) {					\
-		size_t strndup_len = (n);				\
-		strdup_dst = alloc(strndup_len + 1, (ap));		\
-		memcpy(strdup_dst, strdup_src, strndup_len);		\
-		strdup_dst[strndup_len] = '\0';				\
-	}								\
-	(d) = strdup_dst;						\
-} while (/* CONSTCOND */ 0)
-#endif
-
-#ifdef MKSH_LEGACY_MODE
-#ifndef MKSH_NO_CMDLINE_EDITING
-#define MKSH_NO_CMDLINE_EDITING	/* defined */
-#endif
-#ifndef MKSH_CONSERVATIVE_FDS
-#define MKSH_CONSERVATIVE_FDS	/* defined */
-#endif
-#undef MKSH_S_NOVI
-#define MKSH_S_NOVI		1
-#endif
-
-#ifdef MKSH_SMALL
-#ifndef MKSH_CONSERVATIVE_FDS
-#define MKSH_CONSERVATIVE_FDS	/* defined */
-#endif
-#ifndef MKSH_NOPWNAM
-#define MKSH_NOPWNAM		/* defined */
-#endif
-#ifndef MKSH_S_NOVI
-#define MKSH_S_NOVI		1
-#endif
-#endif
-
-#ifndef MKSH_S_NOVI
-#define MKSH_S_NOVI		0
-#endif
-
-#if defined(MKSH_NOPROSPECTOFWORK) && !defined(MKSH_UNEMPLOYED)
-#define MKSH_UNEMPLOYED		1
-#endif
-
-/* these shall be smaller than 100 */
-#ifdef MKSH_CONSERVATIVE_FDS
-#define NUFILE		32	/* Number of user-accessible files */
-#define FDBASE		10	/* First file usable by Shell */
-#else
-#define NUFILE		56	/* Number of user-accessible files */
-#define FDBASE		24	/* First file usable by Shell */
-#endif
-
-/*
- * simple grouping allocator
- */
-
-
-/* 0. OS API: where to get memory from and how to free it (grouped) */
-
-/* malloc(3)/realloc(3) -> free(3) for use by the memory allocator */
-#define malloc_osi(sz)		malloc(sz)
-#define realloc_osi(p,sz)	realloc((p), (sz))
-#define free_osimalloc(p)	free(p)
-
-/* malloc(3)/realloc(3) -> free(3) for use by mksh code */
-#define malloc_osfunc(sz)	malloc(sz)
-#define realloc_osfunc(p,sz)	realloc((p), (sz))
-#define free_osfunc(p)		free(p)
-
-#if HAVE_MKNOD
-/* setmode(3) -> free(3) */
-#define free_ossetmode(p)	free(p)
-#endif
-
-#ifdef MKSH__NO_PATH_MAX
-/* GNU libc: get_current_dir_name(3) -> free(3) */
-#define free_gnu_gcdn(p)	free(p)
-#endif
-
-
-/* 1. internal structure */
-struct lalloc {
-	struct lalloc *next;
-};
-
-/* 2. sizes */
-#define ALLOC_ITEM	struct lalloc
-#define ALLOC_SIZE	(sizeof(ALLOC_ITEM))
-
-/* 3. group structure (only the same for lalloc.c) */
-typedef struct lalloc Area;
-
-
-EXTERN Area aperm;		/* permanent object space */
-#define APERM	&aperm
-#define ATEMP	&e->area
-
-/*
- * flags (the order of these enums MUST match the order in misc.c(options[]))
- */
-enum sh_flag {
-#define SHFLAGS_ENUMS
-#include "sh_flags.h"
-	FNFLAGS		/* (place holder: how many flags are there) */
-};
-
-#define Flag(f)	(shell_flags[(int)(f)])
-#define UTFMODE	Flag(FUNICODE)
-
-/*
- * parsing & execution environment
- *
- * note that kshlongjmp MUST NOT be passed 0 as second argument!
- */
-#ifdef MKSH_NO_SIGSETJMP
-#define kshjmp_buf	jmp_buf
-#define kshsetjmp(jbuf)	_setjmp(jbuf)
-#define kshlongjmp	_longjmp
-#else
-#define kshjmp_buf	sigjmp_buf
-#define kshsetjmp(jbuf)	sigsetjmp((jbuf), 0)
-#define kshlongjmp	siglongjmp
-#endif
-
-struct sretrace_info;
-struct yyrecursive_state;
-
-EXTERN struct sretrace_info *retrace_info E_INIT(NULL);
-EXTERN int subshell_nesting_type E_INIT(0);
-
-extern struct env {
-	ALLOC_ITEM alloc_INT;	/* internal, do not touch */
-	Area area;		/* temporary allocation area */
-	struct env *oenv;	/* link to previous environment */
-	struct block *loc;	/* local variables and functions */
-	short *savefd;		/* original redirected fds */
-	struct temp *temps;	/* temp files */
-	/* saved parser recursion state */
-	struct yyrecursive_state *yyrecursive_statep;
-	kshjmp_buf jbuf;	/* long jump back to env creator */
-	uint8_t type;		/* environment type - see below */
-	uint8_t flags;		/* EF_* */
-} *e;
-
-/* struct env.type values */
-#define E_NONE	0	/* dummy environment */
-#define E_PARSE	1	/* parsing command # */
-#define E_FUNC	2	/* executing function # */
-#define E_INCL	3	/* including a file via . # */
-#define E_EXEC	4	/* executing command tree */
-#define E_LOOP	5	/* executing for/while # */
-#define E_ERRH	6	/* general error handler # */
-#define E_GONE	7	/* hidden in child */
-/* # indicates env has valid jbuf (see unwind()) */
-
-/* struct env.flag values */
-#define EF_BRKCONT_PASS	BIT(1)	/* set if E_LOOP must pass break/continue on */
-#define EF_FAKE_SIGDIE	BIT(2)	/* hack to get info from unwind to quitenv */
-
-/* Do breaks/continues stop at env type e? */
-#define STOP_BRKCONT(t)	((t) == E_NONE || (t) == E_PARSE || \
-			    (t) == E_FUNC || (t) == E_INCL)
-/* Do returns stop at env type e? */
-#define STOP_RETURN(t)	((t) == E_FUNC || (t) == E_INCL)
-
-/* values for kshlongjmp(e->jbuf, i) */
-/* note that i MUST NOT be zero */
-#define LRETURN	1	/* return statement */
-#define LEXIT	2	/* exit statement */
-#define LERROR	3	/* errorf() called */
-#define LLEAVE	4	/* untrappable exit/error */
-#define LINTR	5	/* ^C noticed */
-#define LBREAK	6	/* break statement */
-#define LCONTIN	7	/* continue statement */
-#define LSHELL	8	/* return to interactive shell() */
-#define LAEXPR	9	/* error in arithmetic expression */
-
-/* sort of shell global state */
-EXTERN pid_t procpid;		/* PID of executing process */
-EXTERN int exstat;		/* exit status */
-EXTERN int subst_exstat;	/* exit status of last $(..)/`..` */
-EXTERN struct tbl *vp_pipest;	/* global PIPESTATUS array */
-EXTERN short trap_exstat;	/* exit status before running a trap */
-EXTERN uint8_t trap_nested;	/* running nested traps */
-EXTERN uint8_t shell_flags[FNFLAGS];
-EXTERN const char *kshname;	/* $0 */
-EXTERN struct {
-	uid_t kshuid_v;		/* real UID of shell */
-	uid_t ksheuid_v;	/* effective UID of shell */
-	gid_t kshgid_v;		/* real GID of shell */
-	gid_t kshegid_v;	/* effective GID of shell */
-	pid_t kshpgrp_v;	/* process group of shell */
-	pid_t kshppid_v;	/* PID of parent of shell */
-	pid_t kshpid_v;		/* $$, shell PID */
-} rndsetupstate;
-
-#define kshpid		rndsetupstate.kshpid_v
-#define kshpgrp		rndsetupstate.kshpgrp_v
-#define kshuid		rndsetupstate.kshuid_v
-#define ksheuid		rndsetupstate.ksheuid_v
-#define kshgid		rndsetupstate.kshgid_v
-#define kshegid		rndsetupstate.kshegid_v
-#define kshppid		rndsetupstate.kshppid_v
-
-
-/* option processing */
-#define OF_CMDLINE	0x01	/* command line */
-#define OF_SET		0x02	/* set builtin */
-#define OF_SPECIAL	0x04	/* a special variable changing */
-#define OF_INTERNAL	0x08	/* set internally by shell */
-#define OF_FIRSTTIME	0x10	/* as early as possible, once */
-#define OF_ANY		(OF_CMDLINE | OF_SET | OF_SPECIAL | OF_INTERNAL)
-
-/* null value for variable; comparison pointer for unset */
-EXTERN char null[] E_INIT("");
-/* helpers for string pooling */
-EXTERN const char Tintovfl[] E_INIT("integer overflow %zu %c %zu prevented");
-EXTERN const char Toomem[] E_INIT("can't allocate %zu data bytes");
-#if defined(__GNUC__)
-/* trust this to have string pooling; -Wformat bitches otherwise */
-#define Tsynerr		"syntax error"
-#else
-EXTERN const char Tsynerr[] E_INIT("syntax error");
-#endif
-EXTERN const char Tselect[] E_INIT("select");
-EXTERN const char Tr_fc_e_dash[] E_INIT("r=fc -e -");
-#define Tfc_e_dash	(Tr_fc_e_dash + 2)	/* "fc -e -" */
-#define Zfc_e_dash	7			/* strlen(Tfc_e_dash) */
-EXTERN const char Tlocal_typeset[] E_INIT("local=typeset");
-#define T_typeset	(Tlocal_typeset + 5)	/* "=typeset" */
-#define Ttypeset	(Tlocal_typeset + 6)	/* "typeset" */
-EXTERN const char Talias[] E_INIT("alias");
-EXTERN const char Tunalias[] E_INIT("unalias");
-EXTERN const char Tsgset[] E_INIT("*=set");
-#define Tset		(Tsgset + 2)		/* "set" */
-EXTERN const char Tsgunset[] E_INIT("*=unset");
-#define Tunset		(Tsgunset + 2)		/* "unset" */
-EXTERN const char Tsgexport[] E_INIT("*=export");
-#define Texport		(Tsgexport + 2)		/* "export" */
-EXTERN const char Tsgreadonly[] E_INIT("*=readonly");
-#define Treadonly	(Tsgreadonly + 2)	/* "readonly" */
-EXTERN const char Tgbuiltin[] E_INIT("=builtin");
-#define Tbuiltin	(Tgbuiltin + 1)		/* "builtin" */
-EXTERN const char T_function[] E_INIT(" function");
-#define Tfunction	(T_function + 1)	/* "function" */
-EXTERN const char TC_LEX1[] E_INIT("|&;<>() \t\n");
-#define TC_IFSWS	(TC_LEX1 + 7)		/* space tab newline */
-
-typedef uint8_t Temp_type;
-/* expanded heredoc */
-#define TT_HEREDOC_EXP	0
-/* temporary file used for history editing (fc -e) */
-#define TT_HIST_EDIT	1
-/* temporary file used during in-situ command substitution */
-#define TT_FUNSUB	2
-
-/* temp/heredoc files. The file is removed when the struct is freed. */
-struct temp {
-	struct temp *next;
-	struct shf *shf;
-	/* pid of process parsed here-doc */
-	pid_t pid;
-	Temp_type type;
-	/* actually longer: name (variable length) */
-	char tffn[3];
-};
-
-/*
- * stdio and our IO routines
- */
-
-#define shl_xtrace	(&shf_iob[0])	/* for set -x */
-#define shl_stdout	(&shf_iob[1])
-#define shl_out		(&shf_iob[2])
-#ifdef DF
-#define shl_dbg		(&shf_iob[3])	/* for DF() */
-#endif
-EXTERN bool shl_stdout_ok;
-
-/*
- * trap handlers
- */
-typedef struct trap {
-	const char *name;	/* short name */
-	const char *mess;	/* descriptive name */
-	char *trap;		/* trap command */
-	sig_t cursig;		/* current handler (valid if TF_ORIG_* set) */
-	sig_t shtrap;		/* shell signal handler */
-	int signal;		/* signal number */
-	int flags;		/* TF_* */
-	volatile sig_atomic_t set; /* trap pending */
-} Trap;
-
-/* values for Trap.flags */
-#define TF_SHELL_USES	BIT(0)	/* shell uses signal, user can't change */
-#define TF_USER_SET	BIT(1)	/* user has (tried to) set trap */
-#define TF_ORIG_IGN	BIT(2)	/* original action was SIG_IGN */
-#define TF_ORIG_DFL	BIT(3)	/* original action was SIG_DFL */
-#define TF_EXEC_IGN	BIT(4)	/* restore SIG_IGN just before exec */
-#define TF_EXEC_DFL	BIT(5)	/* restore SIG_DFL just before exec */
-#define TF_DFL_INTR	BIT(6)	/* when received, default action is LINTR */
-#define TF_TTY_INTR	BIT(7)	/* tty generated signal (see j_waitj) */
-#define TF_CHANGED	BIT(8)	/* used by runtrap() to detect trap changes */
-#define TF_FATAL	BIT(9)	/* causes termination if not trapped */
-
-/* values for setsig()/setexecsig() flags argument */
-#define SS_RESTORE_MASK	0x3	/* how to restore a signal before an exec() */
-#define SS_RESTORE_CURR	0	/* leave current handler in place */
-#define SS_RESTORE_ORIG	1	/* restore original handler */
-#define SS_RESTORE_DFL	2	/* restore to SIG_DFL */
-#define SS_RESTORE_IGN	3	/* restore to SIG_IGN */
-#define SS_FORCE	BIT(3)	/* set signal even if original signal ignored */
-#define SS_USER		BIT(4)	/* user is doing the set (ie, trap command) */
-#define SS_SHTRAP	BIT(5)	/* trap for internal use (ALRM, CHLD, WINCH) */
-
-#define ksh_SIGEXIT	0	/* for trap EXIT */
-#define ksh_SIGERR	NSIG	/* for trap ERR */
-
-EXTERN volatile sig_atomic_t trap;	/* traps pending? */
-EXTERN volatile sig_atomic_t intrsig;	/* pending trap interrupts command */
-EXTERN volatile sig_atomic_t fatal_trap; /* received a fatal signal */
-extern	Trap	sigtraps[NSIG+1];
-
-/* got_winch = 1 when we need to re-adjust the window size */
-#ifdef SIGWINCH
-EXTERN volatile sig_atomic_t got_winch E_INIT(1);
-#else
-#define got_winch	true
-#endif
-
-/*
- * TMOUT support
- */
-/* values for ksh_tmout_state */
-enum tmout_enum {
-	TMOUT_EXECUTING = 0,	/* executing commands */
-	TMOUT_READING,		/* waiting for input */
-	TMOUT_LEAVING		/* have timed out */
-};
-EXTERN unsigned int ksh_tmout;
-EXTERN enum tmout_enum ksh_tmout_state E_INIT(TMOUT_EXECUTING);
-
-/* For "You have stopped jobs" message */
-EXTERN bool really_exit;
-
-/*
- * fast character classes
- */
-#define C_ALPHA	 BIT(0)		/* a-z_A-Z */
-#define C_DIGIT	 BIT(1)		/* 0-9 */
-#define C_LEX1	 BIT(2)		/* \t \n\0|&;<>() */
-#define C_VAR1	 BIT(3)		/* *@#!$-? */
-#define C_IFSWS	 BIT(4)		/* \t \n (IFS white space) */
-#define C_SUBOP1 BIT(5)		/* "=-+?" */
-#define C_QUOTE	 BIT(6)		/* \t\n "#$&'()*;<=>?[\]`| (needing quoting) */
-#define C_IFS	 BIT(7)		/* $IFS */
-#define C_SUBOP2 BIT(8)		/* "#%" (magic, see below) */
-
-extern unsigned char chtypes[];
-
-#define ctype(c, t)	tobool( ((t) == C_SUBOP2) ?			\
-			    (((c) == '#' || (c) == '%') ? 1 : 0) :	\
-			    (chtypes[(unsigned char)(c)] & (t)) )
-#define ksh_isalphx(c)	ctype((c), C_ALPHA)
-#define ksh_isalnux(c)	ctype((c), C_ALPHA | C_DIGIT)
-
-EXTERN int ifs0 E_INIT(' ');	/* for "$*" */
-
-/* Argument parsing for built-in commands and getopts command */
-
-/* Values for Getopt.flags */
-#define GF_ERROR	BIT(0)	/* call errorf() if there is an error */
-#define GF_PLUSOPT	BIT(1)	/* allow +c as an option */
-#define GF_NONAME	BIT(2)	/* don't print argv[0] in errors */
-
-/* Values for Getopt.info */
-#define GI_MINUS	BIT(0)	/* an option started with -... */
-#define GI_PLUS		BIT(1)	/* an option started with +... */
-#define GI_MINUSMINUS	BIT(2)	/* arguments were ended with -- */
-
-/* in case some OS defines these */
-#undef optarg
-#undef optind
-
-typedef struct {
-	const char *optarg;
-	int optind;
-	int uoptind;		/* what user sees in $OPTIND */
-	int flags;		/* see GF_* */
-	int info;		/* see GI_* */
-	unsigned int p;		/* 0 or index into argv[optind - 1] */
-	char buf[2];		/* for bad option OPTARG value */
-} Getopt;
-
-EXTERN Getopt builtin_opt;	/* for shell builtin commands */
-EXTERN Getopt user_opt;		/* parsing state for getopts builtin command */
-
-/* This for co-processes */
-
-/* something that won't (realisticly) wrap */
-typedef int32_t Coproc_id;
-
-struct coproc {
-	void *job;	/* 0 or job of co-process using input pipe */
-	int read;	/* pipe from co-process's stdout */
-	int readw;	/* other side of read (saved temporarily) */
-	int write;	/* pipe to co-process's stdin */
-	int njobs;	/* number of live jobs using output pipe */
-	Coproc_id id;	/* id of current output pipe */
-};
-EXTERN struct coproc coproc;
-
-#ifndef MKSH_NOPROSPECTOFWORK
-/* used in jobs.c and by coprocess stuff in exec.c and select() calls */
-EXTERN sigset_t		sm_default, sm_sigchld;
-#endif
-
-/* name of called builtin function (used by error functions) */
-EXTERN const char *builtin_argv0;
-/* flags of called builtin (SPEC_BI, etc.) */
-EXTERN uint32_t builtin_flag;
-
-/* current working directory */
-EXTERN char	*current_wd;
-
-/* input line size */
-#define LINE		(4096 - ALLOC_SIZE)
-/*
- * Minimum required space to work with on a line - if the prompt leaves
- * less space than this on a line, the prompt is truncated.
- */
-#define MIN_EDIT_SPACE	7
-/*
- * Minimum allowed value for x_cols: 2 for prompt, 3 for " < " at end of line
- */
-#define MIN_COLS	(2 + MIN_EDIT_SPACE + 3)
-#define MIN_LINS	3
-EXTERN mksh_ari_t x_cols E_INIT(80);	/* tty columns */
-EXTERN mksh_ari_t x_lins E_INIT(24);	/* tty lines */
-
-
-/* Determine the location of the system (common) profile */
-
-#ifndef MKSH_DEFAULT_PROFILEDIR
-#if defined(ANDROID)
-#define MKSH_DEFAULT_PROFILEDIR	"/system/etc"
-#else
-#define MKSH_DEFAULT_PROFILEDIR	"/etc"
-#endif
-#endif
-
-#define MKSH_SYSTEM_PROFILE	MKSH_DEFAULT_PROFILEDIR "/profile"
-#define MKSH_SUID_PROFILE	MKSH_DEFAULT_PROFILEDIR "/suid_profile"
-
-
-/* Used by v_evaluate() and setstr() to control action when error occurs */
-#define KSH_UNWIND_ERROR	0	/* unwind the stack (kshlongjmp) */
-#define KSH_RETURN_ERROR	1	/* return 1/0 for success/failure */
-
-/*
- * Shell file I/O routines
- */
-
-#define SHF_BSIZE		512
-
-#define shf_fileno(shf)		((shf)->fd)
-#define shf_setfileno(shf,nfd)	((shf)->fd = (nfd))
-#define shf_getc_i(shf)		((shf)->rnleft > 0 ? \
-				    (shf)->rnleft--, *(shf)->rp++ : \
-				    shf_getchar(shf))
-#define shf_putc_i(c, shf)	((shf)->wnleft == 0 ? \
-				    shf_putchar((c), (shf)) : \
-				    ((shf)->wnleft--, *(shf)->wp++ = (c)))
-#define shf_eof(shf)		((shf)->flags & SHF_EOF)
-#define shf_error(shf)		((shf)->flags & SHF_ERROR)
-#define shf_errno(shf)		((shf)->errnosv)
-#define shf_clearerr(shf)	((shf)->flags &= ~(SHF_EOF | SHF_ERROR))
-
-/* Flags passed to shf_*open() */
-#define SHF_RD		0x0001
-#define SHF_WR		0x0002
-#define SHF_RDWR	(SHF_RD|SHF_WR)
-#define SHF_ACCMODE	0x0003		/* mask */
-#define SHF_GETFL	0x0004		/* use fcntl() to figure RD/WR flags */
-#define SHF_UNBUF	0x0008		/* unbuffered I/O */
-#define SHF_CLEXEC	0x0010		/* set close on exec flag */
-#define SHF_MAPHI	0x0020		/* make fd > FDBASE (and close orig)
-					 * (shf_open() only) */
-#define SHF_DYNAMIC	0x0040		/* string: increase buffer as needed */
-#define SHF_INTERRUPT	0x0080		/* EINTR in read/write causes error */
-/* Flags used internally */
-#define SHF_STRING	0x0100		/* a string, not a file */
-#define SHF_ALLOCS	0x0200		/* shf and shf->buf were alloc()ed */
-#define SHF_ALLOCB	0x0400		/* shf->buf was alloc()ed */
-#define SHF_ERROR	0x0800		/* read()/write() error */
-#define SHF_EOF		0x1000		/* read eof (sticky) */
-#define SHF_READING	0x2000		/* currently reading: rnleft,rp valid */
-#define SHF_WRITING	0x4000		/* currently writing: wnleft,wp valid */
-
-
-struct shf {
-	Area *areap;		/* area shf/buf were allocated in */
-	unsigned char *rp;	/* read: current position in buffer */
-	unsigned char *wp;	/* write: current position in buffer */
-	unsigned char *buf;	/* buffer */
-	ssize_t bsize;		/* actual size of buf */
-	ssize_t rbsize;		/* size of buffer (1 if SHF_UNBUF) */
-	ssize_t rnleft;		/* read: how much data left in buffer */
-	ssize_t wbsize;		/* size of buffer (0 if SHF_UNBUF) */
-	ssize_t wnleft;		/* write: how much space left in buffer */
-	int flags;		/* see SHF_* */
-	int fd;			/* file descriptor */
-	int errnosv;		/* saved value of errno after error */
-};
-
-extern struct shf shf_iob[];
-
-struct table {
-	Area *areap;		/* area to allocate entries */
-	struct tbl **tbls;	/* hashed table items */
-	size_t nfree;		/* free table entries */
-	uint8_t tshift;		/* table size (2^tshift) */
-};
-
-/* table item */
-struct tbl {
-	/* Area to allocate from */
-	Area *areap;
-	/* value */
-	union {
-		char *s;			/* string */
-		mksh_ari_t i;			/* integer */
-		mksh_uari_t u;			/* unsigned integer */
-		int (*f)(const char **);	/* built-in command */
-		struct op *t;			/* "function" tree */
-	} val;
-	union {
-		struct tbl *array;	/* array values */
-		const char *fpath;	/* temporary path to undef function */
-	} u;
-	union {
-		int field;		/* field with for -L/-R/-Z */
-		int errnov;		/* CEXEC/CTALIAS */
-	} u2;
-	union {
-		uint32_t hval;		/* hash(name) */
-		uint32_t index;		/* index for an array */
-	} ua;
-	/*
-	 * command type (see below), base (if INTEGER),
-	 * offset from val.s of value (if EXPORT)
-	 */
-	int type;
-	/* flags (see below) */
-	uint32_t flag;
-
-	/* actually longer: name (variable length) */
-	char name[4];
-};
-
-EXTERN struct tbl vtemp;
-
-/* common flag bits */
-#define ALLOC		BIT(0)	/* val.s has been allocated */
-#define DEFINED		BIT(1)	/* is defined in block */
-#define ISSET		BIT(2)	/* has value, vp->val.[si] */
-#define EXPORT		BIT(3)	/* exported variable/function */
-#define TRACE		BIT(4)	/* var: user flagged, func: execution tracing */
-/* (start non-common flags at 8) */
-/* flag bits used for variables */
-#define SPECIAL		BIT(8)	/* PATH, IFS, SECONDS, etc */
-#define INTEGER		BIT(9)	/* val.i contains integer value */
-#define RDONLY		BIT(10)	/* read-only variable */
-#define LOCAL		BIT(11)	/* for local typeset() */
-#define ARRAY		BIT(13)	/* array */
-#define LJUST		BIT(14)	/* left justify */
-#define RJUST		BIT(15)	/* right justify */
-#define ZEROFIL		BIT(16)	/* 0 filled if RJUSTIFY, strip 0s if LJUSTIFY */
-#define LCASEV		BIT(17)	/* convert to lower case */
-#define UCASEV_AL	BIT(18) /* convert to upper case / autoload function */
-#define INT_U		BIT(19)	/* unsigned integer */
-#define INT_L		BIT(20)	/* long integer (no-op but used as magic) */
-#define IMPORT		BIT(21)	/* flag to typeset(): no arrays, must have = */
-#define LOCAL_COPY	BIT(22)	/* with LOCAL - copy attrs from existing var */
-#define EXPRINEVAL	BIT(23)	/* contents currently being evaluated */
-#define EXPRLVALUE	BIT(24)	/* useable as lvalue (temp flag) */
-#define AINDEX		BIT(25) /* array index >0 = ua.index filled in */
-#define ASSOC		BIT(26) /* ARRAY ? associative : reference */
-/* flag bits used for taliases/builtins/aliases/keywords/functions */
-#define KEEPASN		BIT(8)	/* keep command assignments (eg, var=x cmd) */
-#define FINUSE		BIT(9)	/* function being executed */
-#define FDELETE		BIT(10)	/* function deleted while it was executing */
-#define FKSH		BIT(11)	/* function defined with function x (vs x()) */
-#define SPEC_BI		BIT(12)	/* a POSIX special builtin */
-/*
- * Attributes that can be set by the user (used to decide if an unset
- * param should be repoted by set/typeset). Does not include ARRAY or
- * LOCAL.
- */
-#define USERATTRIB	(EXPORT|INTEGER|RDONLY|LJUST|RJUST|ZEROFIL|\
-			    LCASEV|UCASEV_AL|INT_U|INT_L)
-
-#define arrayindex(vp)	((unsigned long)((vp)->flag & AINDEX ? \
-			    (vp)->ua.index : 0))
-
-EXTERN enum {
-	SRF_NOP,
-	SRF_ENABLE,
-	SRF_DISABLE
-} set_refflag E_INIT(SRF_NOP);
-
-/* command types */
-#define CNONE		0	/* undefined */
-#define CSHELL		1	/* built-in */
-#define CFUNC		2	/* function */
-#define CEXEC		4	/* executable command */
-#define CALIAS		5	/* alias */
-#define CKEYWD		6	/* keyword */
-#define CTALIAS		7	/* tracked alias */
-
-/* Flags for findcom()/comexec() */
-#define FC_SPECBI	BIT(0)	/* special builtin */
-#define FC_FUNC		BIT(1)	/* function */
-#define FC_NORMBI	BIT(2)	/* not special builtin */
-#define FC_BI		(FC_SPECBI | FC_NORMBI)
-#define FC_PATH		BIT(3)	/* do path search */
-#define FC_DEFPATH	BIT(4)	/* use default path in path search */
-
-
-#define AF_ARGV_ALLOC	0x1	/* argv[] array allocated */
-#define AF_ARGS_ALLOCED	0x2	/* argument strings allocated */
-#define AI_ARGV(a, i)	((i) == 0 ? (a).argv[0] : (a).argv[(i) - (a).skip])
-#define AI_ARGC(a)	((a).ai_argc - (a).skip)
-
-/* Argument info. Used for $#, $* for shell, functions, includes, etc. */
-struct arg_info {
-	const char **argv;
-	int flags;	/* AF_* */
-	int ai_argc;
-	int skip;	/* first arg is argv[0], second is argv[1 + skip] */
-};
-
-/*
- * activation record for function blocks
- */
-struct block {
-	Area area;		/* area to allocate things */
-	const char **argv;
-	char *error;		/* error handler */
-	char *exit;		/* exit handler */
-	struct block *next;	/* enclosing block */
-	struct table vars;	/* local variables */
-	struct table funs;	/* local functions */
-	Getopt getopts_state;
-	int argc;
-	int flags;		/* see BF_* */
-};
-
-/* Values for struct block.flags */
-#define BF_DOGETOPTS	BIT(0)	/* save/restore getopts state */
-
-/*
- * Used by ktwalk() and ktnext() routines.
- */
-struct tstate {
-	struct tbl **next;
-	ssize_t left;
-};
-
-EXTERN struct table taliases;	/* tracked aliases */
-EXTERN struct table builtins;	/* built-in commands */
-EXTERN struct table aliases;	/* aliases */
-EXTERN struct table keywords;	/* keywords */
-#ifndef MKSH_NOPWNAM
-EXTERN struct table homedirs;	/* homedir() cache */
-#endif
-
-struct builtin {
-	const char *name;
-	int (*func)(const char **);
-};
-
-extern const struct builtin mkshbuiltins[];
-
-/* values for set_prompt() */
-#define PS1	0	/* command */
-#define PS2	1	/* command continuation */
-
-EXTERN char *path;		/* copy of either PATH or def_path */
-EXTERN const char *def_path;	/* path to use if PATH not set */
-EXTERN char *tmpdir;		/* TMPDIR value */
-EXTERN const char *prompt;
-EXTERN int cur_prompt;		/* PS1 or PS2 */
-EXTERN int current_lineno;	/* LINENO value */
-
-/*
- * Description of a command or an operation on commands.
- */
-struct op {
-	const char **args;		/* arguments to a command */
-	char **vars;			/* variable assignments */
-	struct ioword **ioact;		/* IO actions (eg, < > >>) */
-	struct op *left, *right;	/* descendents */
-	char *str;			/* word for case; identifier for for,
-					 * select, and functions;
-					 * path to execute for TEXEC;
-					 * time hook for TCOM.
-					 */
-	int lineno;			/* TCOM/TFUNC: LINENO for this */
-	short type;			/* operation type, see below */
-	/* WARNING: newtp(), tcopy() use evalflags = 0 to clear union */
-	union {
-		/* TCOM: arg expansion eval() flags */
-		short evalflags;
-		/* TFUNC: function x (vs x()) */
-		short ksh_func;
-		/* TPAT: termination character */
-		char charflag;
-	} u;
-};
-
-/* Tree.type values */
-#define TEOF		0
-#define TCOM		1	/* command */
-#define TPAREN		2	/* (c-list) */
-#define TPIPE		3	/* a | b */
-#define TLIST		4	/* a ; b */
-#define TOR		5	/* || */
-#define TAND		6	/* && */
-#define TBANG		7	/* ! */
-#define TDBRACKET	8	/* [[ .. ]] */
-#define TFOR		9
-#define TSELECT		10
-#define TCASE		11
-#define TIF		12
-#define TWHILE		13
-#define TUNTIL		14
-#define TELIF		15
-#define TPAT		16	/* pattern in case */
-#define TBRACE		17	/* {c-list} */
-#define TASYNC		18	/* c & */
-#define TFUNCT		19	/* function name { command; } */
-#define TTIME		20	/* time pipeline */
-#define TEXEC		21	/* fork/exec eval'd TCOM */
-#define TCOPROC		22	/* coprocess |& */
-
-/*
- * prefix codes for words in command tree
- */
-#define EOS	0	/* end of string */
-#define CHAR	1	/* unquoted character */
-#define QCHAR	2	/* quoted character */
-#define COMSUB	3	/* $() substitution (0 terminated) */
-#define EXPRSUB	4	/* $(()) substitution (0 terminated) */
-#define OQUOTE	5	/* opening " or ' */
-#define CQUOTE	6	/* closing " or ' */
-#define OSUBST	7	/* opening ${ subst (followed by { or X) */
-#define CSUBST	8	/* closing } of above (followed by } or X) */
-#define OPAT	9	/* open pattern: *(, @(, etc. */
-#define SPAT	10	/* separate pattern: | */
-#define CPAT	11	/* close pattern: ) */
-#define ADELIM	12	/* arbitrary delimiter: ${foo:2:3} ${foo/bar/baz} */
-#define FUNSUB	14	/* ${ foo;} substitution (NUL terminated) */
-#define VALSUB	15	/* ${|foo;} substitution (NUL terminated) */
-
-/*
- * IO redirection
- */
-struct ioword {
-	int	unit;		/* unit affected */
-	int	flag;		/* action (below) */
-	char	*name;		/* file name (unused if heredoc) */
-	char	*delim;		/* delimiter for <<,<<- */
-	char	*heredoc;	/* content of heredoc */
-};
-
-/* ioword.flag - type of redirection */
-#define IOTYPE		0xF	/* type: bits 0:3 */
-#define IOREAD		0x1	/* < */
-#define IOWRITE		0x2	/* > */
-#define IORDWR		0x3	/* <>: todo */
-#define IOHERE		0x4	/* << (here file) */
-#define IOCAT		0x5	/* >> */
-#define IODUP		0x6	/* <&/>& */
-#define IOEVAL		BIT(4)	/* expand in << */
-#define IOSKIP		BIT(5)	/* <<-, skip ^\t* */
-#define IOCLOB		BIT(6)	/* >|, override -o noclobber */
-#define IORDUP		BIT(7)	/* x<&y (as opposed to x>&y) */
-#define IONAMEXP	BIT(8)	/* name has been expanded */
-#define IOBASH		BIT(9)	/* &> etc. */
-#define IOHERESTR	BIT(10)	/* <<< (here string) */
-#define IONDELIM	BIT(11)	/* null delimiter (<<) */
-
-/* execute/exchild flags */
-#define XEXEC	BIT(0)		/* execute without forking */
-#define XFORK	BIT(1)		/* fork before executing */
-#define XBGND	BIT(2)		/* command & */
-#define XPIPEI	BIT(3)		/* input is pipe */
-#define XPIPEO	BIT(4)		/* output is pipe */
-#define XXCOM	BIT(5)		/* `...` command */
-#define XPCLOSE	BIT(6)		/* exchild: close close_fd in parent */
-#define XCCLOSE	BIT(7)		/* exchild: close close_fd in child */
-#define XERROK	BIT(8)		/* non-zero exit ok (for set -e) */
-#define XCOPROC BIT(9)		/* starting a co-process */
-#define XTIME	BIT(10)		/* timing TCOM command */
-#define XPIPEST	BIT(11)		/* want PIPESTATUS */
-
-/*
- * flags to control expansion of words (assumed by t->evalflags to fit
- * in a short)
- */
-#define DOBLANK	BIT(0)		/* perform blank interpretation */
-#define DOGLOB	BIT(1)		/* expand [?* */
-#define DOPAT	BIT(2)		/* quote *?[ */
-#define DOTILDE	BIT(3)		/* normal ~ expansion (first char) */
-#define DONTRUNCOMMAND BIT(4)	/* do not run $(command) things */
-#define DOASNTILDE BIT(5)	/* assignment ~ expansion (after =, :) */
-#define DOBRACE BIT(6)		/* used by expand(): do brace expansion */
-#define DOMAGIC BIT(7)		/* used by expand(): string contains MAGIC */
-#define DOTEMP	BIT(8)		/* dito: in word part of ${..[%#=?]..} */
-#define DOVACHECK BIT(9)	/* var assign check (for typeset, set, etc) */
-#define DOMARKDIRS BIT(10)	/* force markdirs behaviour */
-#if !defined(MKSH_SMALL)
-#define DOTCOMEXEC BIT(11)	/* not an eval flag, used by sh -c hack */
-#endif
-
-/*
- * The arguments of [[ .. ]] expressions are kept in t->args[] and flags
- * indicating how the arguments have been munged are kept in t->vars[].
- * The contents of t->vars[] are stuffed strings (so they can be treated
- * like all other t->vars[]) in which the second character is the one that
- * is examined. The DB_* defines are the values for these second characters.
- */
-#define DB_NORM	1	/* normal argument */
-#define DB_OR	2	/* || -> -o conversion */
-#define DB_AND	3	/* && -> -a conversion */
-#define DB_BE	4	/* an inserted -BE */
-#define DB_PAT	5	/* a pattern argument */
-
-#define X_EXTRA	20	/* this many extra bytes in X string */
-
-typedef struct XString {
-	char *end, *beg;	/* end, begin of string */
-	size_t len;		/* length */
-	Area *areap;		/* area to allocate/free from */
-} XString;
-
-typedef char *XStringP;
-
-/* initialise expandable string */
-#define XinitN(xs, length, area) do {				\
-	(xs).len = (length);					\
-	(xs).areap = (area);					\
-	(xs).beg = alloc((xs).len + X_EXTRA, (xs).areap);	\
-	(xs).end = (xs).beg + (xs).len;				\
-} while (/* CONSTCOND */ 0)
-#define Xinit(xs, xp, length, area) do {			\
-	XinitN((xs), (length), (area));				\
-	(xp) = (xs).beg;					\
-} while (/* CONSTCOND */ 0)
-
-/* stuff char into string */
-#define Xput(xs, xp, c)	(*xp++ = (c))
-
-/* check if there are at least n bytes left */
-#define XcheckN(xs, xp, n) do {					\
-	ssize_t more = ((xp) + (n)) - (xs).end;			\
-	if (more > 0)						\
-		(xp) = Xcheck_grow(&(xs), (xp), more);		\
-} while (/* CONSTCOND */ 0)
-
-/* check for overflow, expand string */
-#define Xcheck(xs, xp)	XcheckN((xs), (xp), 1)
-
-/* free string */
-#define Xfree(xs, xp)	afree((xs).beg, (xs).areap)
-
-/* close, return string */
-#define Xclose(xs, xp)	aresize((xs).beg, (xp) - (xs).beg, (xs).areap)
-
-/* begin of string */
-#define Xstring(xs, xp)	((xs).beg)
-
-#define Xnleft(xs, xp)	((xs).end - (xp))	/* may be less than 0 */
-#define Xlength(xs, xp)	((xp) - (xs).beg)
-#define Xsize(xs, xp)	((xs).end - (xs).beg)
-#define Xsavepos(xs, xp)	((xp) - (xs).beg)
-#define Xrestpos(xs, xp, n)	((xs).beg + (n))
-
-char *Xcheck_grow(XString *, const char *, size_t);
-
-/*
- * expandable vector of generic pointers
- */
-
-typedef struct {
-	/* begin of allocated area */
-	void **beg;
-	/* currently used number of entries */
-	size_t len;
-	/* allocated number of entries */
-	size_t siz;
-} XPtrV;
-
-#define XPinit(x, n)	do {					\
-	(x).siz = (n);						\
-	(x).len = 0;						\
-	(x).beg = alloc2((x).siz, sizeof(void *), ATEMP);	\
-} while (/* CONSTCOND */ 0)					\
-
-#define XPput(x, p)	do {					\
-	if ((x).len == (x).siz) {				\
-		(x).beg = aresize2((x).beg, (x).siz,		\
-		    2 * sizeof(void *), ATEMP);			\
-		(x).siz <<= 1;					\
-	}							\
-	(x).beg[(x).len++] = (p);				\
-} while (/* CONSTCOND */ 0)
-
-#define XPptrv(x)	((x).beg)
-#define XPsize(x)	((x).len)
-#define XPclose(x)	aresize2((x).beg, XPsize(x), sizeof(void *), ATEMP)
-#define XPfree(x)	afree((x).beg, ATEMP)
-
-/*
- * Lexer internals
- */
-
-typedef struct source Source;
-struct source {
-	const char *str;	/* input pointer */
-	const char *start;	/* start of current buffer */
-	union {
-		const char **strv;	/* string [] */
-		struct shf *shf;	/* shell file */
-		struct tbl *tblp;	/* alias (SF_HASALIAS) */
-		char *freeme;		/* also for SREREAD */
-	} u;
-	const char *file;	/* input file name */
-	int	type;		/* input type */
-	int	line;		/* line number */
-	int	errline;	/* line the error occurred on (0 if not set) */
-	int	flags;		/* SF_* */
-	Area	*areap;
-	Source *next;		/* stacked source */
-	XString	xs;		/* input buffer */
-	char	ugbuf[2];	/* buffer for ungetsc() (SREREAD) and
-				 * alias (SALIAS) */
-};
-
-/* Source.type values */
-#define SEOF		0	/* input EOF */
-#define SFILE		1	/* file input */
-#define SSTDIN		2	/* read stdin */
-#define SSTRING		3	/* string */
-#define SWSTR		4	/* string without \n */
-#define SWORDS		5	/* string[] */
-#define SWORDSEP	6	/* string[] separator */
-#define SALIAS		7	/* alias expansion */
-#define SREREAD		8	/* read ahead to be re-scanned */
-#define SSTRINGCMDLINE	9	/* string from "mksh -c ..." */
-
-/* Source.flags values */
-#define SF_ECHO		BIT(0)	/* echo input to shlout */
-#define SF_ALIAS	BIT(1)	/* faking space at end of alias */
-#define SF_ALIASEND	BIT(2)	/* faking space at end of alias */
-#define SF_TTY		BIT(3)	/* type == SSTDIN & it is a tty */
-#define SF_HASALIAS	BIT(4)	/* u.tblp valid (SALIAS, SEOF) */
-#if !defined(MKSH_SMALL)
-#define SF_MAYEXEC	BIT(5)	/* special sh -c optimisation hack */
-#endif
-
-typedef union {
-	int i;
-	char *cp;
-	char **wp;
-	struct op *o;
-	struct ioword *iop;
-} YYSTYPE;
-
-/* If something is added here, add it to tokentab[] in syn.c as well */
-#define LWORD		256
-#define LOGAND		257	/* && */
-#define LOGOR		258	/* || */
-#define BREAK		259	/* ;; */
-#define IF		260
-#define THEN		261
-#define ELSE		262
-#define ELIF		263
-#define FI		264
-#define CASE		265
-#define ESAC		266
-#define FOR		267
-#define SELECT		268
-#define WHILE		269
-#define UNTIL		270
-#define DO		271
-#define DONE		272
-#define IN		273
-#define FUNCTION	274
-#define TIME		275
-#define REDIR		276
-#define MDPAREN		277	/* (( )) */
-#define BANG		278	/* ! */
-#define DBRACKET	279	/* [[ .. ]] */
-#define COPROC		280	/* |& */
-#define BRKEV		281	/* ;| */
-#define BRKFT		282	/* ;& */
-#define YYERRCODE	300
-
-/* flags to yylex */
-#define CONTIN		BIT(0)	/* skip new lines to complete command */
-#define ONEWORD		BIT(1)	/* single word for substitute() */
-#define ALIAS		BIT(2)	/* recognise alias */
-#define KEYWORD		BIT(3)	/* recognise keywords */
-#define LETEXPR		BIT(4)	/* get expression inside (( )) */
-#define VARASN		BIT(5)	/* check for var=word */
-#define ARRAYVAR	BIT(6)	/* parse x[1 & 2] as one word */
-#define ESACONLY	BIT(7)	/* only accept esac keyword */
-#define CMDWORD		BIT(8)	/* parsing simple command (alias related) */
-#define HEREDELIM	BIT(9)	/* parsing <<,<<- delimiter */
-#define LQCHAR		BIT(10)	/* source string contains QCHAR */
-#define HEREDOC 	BIT(11)	/* parsing a here document body */
-
-#define HERES		10	/* max number of << in line */
-
-#undef CTRL
-#define	CTRL(x)		((x) == '?' ? 0x7F : (x) & 0x1F)	/* ASCII */
-#define	UNCTRL(x)	((x) ^ 0x40)				/* ASCII */
-
-#define IDENT		64
-
-EXTERN Source *source;		/* yyparse/yylex source */
-EXTERN YYSTYPE yylval;		/* result from yylex */
-EXTERN struct ioword *heres[HERES], **herep;
-EXTERN char ident[IDENT + 1];
-
-EXTERN char **history;		/* saved commands */
-EXTERN char **histptr;		/* last history item */
-EXTERN mksh_ari_t histsize;	/* history size */
-
-/* user and system time of last j_waitjed job */
-EXTERN struct timeval j_usrtime, j_systime;
-
-#define notok2mul(max, val, c)	(((val) != 0) && ((c) != 0) && \
-				    (((max) / (c)) < (val)))
-#define notok2add(max, val, c)	((val) > ((max) - (c)))
-#define notoktomul(val, cnst)	notok2mul(SIZE_MAX, (val), (cnst))
-#define notoktoadd(val, cnst)	notok2add(SIZE_MAX, (val), (cnst))
-#define checkoktoadd(val, cnst) do {					\
-	if (notoktoadd((val), (cnst)))					\
-		internal_errorf(Tintovfl, (size_t)(val),		\
-		    '+', (size_t)(cnst));				\
-} while (/* CONSTCOND */ 0)
-
-
-/* NZAAT hash based on Bob Jenkins' one-at-a-time hash */
-
-/* From: src/kern/include/nzat.h,v 1.2 2011/07/18 00:35:40 tg Exp $ */
-
-#define NZATInit(h) do {					\
-	(h) = 0;						\
-} while (/* CONSTCOND */ 0)
-
-#define NZATUpdateByte(h,b) do {				\
-	(h) += (uint8_t)(b);					\
-	++(h);							\
-	(h) += (h) << 10;					\
-	(h) ^= (h) >> 6;					\
-} while (/* CONSTCOND */ 0)
-
-#define NZATUpdateMem(h,p,z) do {				\
-	register const uint8_t *NZATUpdateMem_p;		\
-	register size_t NZATUpdateMem_z = (z);			\
-								\
-	NZATUpdateMem_p = (const void *)(p);			\
-	while (NZATUpdateMem_z--)				\
-		NZATUpdateByte((h), *NZATUpdateMem_p++);	\
-} while (/* CONSTCOND */ 0)
-
-#define NZATUpdateString(h,s) do {				\
-	register const char *NZATUpdateString_s;		\
-	register uint8_t NZATUpdateString_c;			\
-								\
-	NZATUpdateString_s = (const void *)(s);			\
-	while ((NZATUpdateString_c = *NZATUpdateString_s++))	\
-		NZATUpdateByte((h), NZATUpdateString_c);	\
-} while (/* CONSTCOND */ 0)
-
-#define NZAATFinish(h) do {					\
-	(h) += (h) << 10;					\
-	(h) ^= (h) >> 6;					\
-	(h) += (h) << 3;					\
-	(h) ^= (h) >> 11;					\
-	(h) += (h) << 15;					\
-} while (/* CONSTCOND */ 0)
-
-
-/* lalloc.c */
-void ainit(Area *);
-void afreeall(Area *);
-/* these cannot fail and can take NULL (not for ap) */
-#define alloc(n, ap)		aresize(NULL, (n), (ap))
-#define alloc2(m, n, ap)	aresize2(NULL, (m), (n), (ap))
-void *aresize(void *, size_t, Area *);
-void *aresize2(void *, size_t, size_t, Area *);
-void afree(void *, Area *);	/* can take NULL */
-/* edit.c */
-#ifndef MKSH_NO_CMDLINE_EDITING
-#ifndef MKSH_SMALL
-int x_bind(const char *, const char *, bool, bool);
-#else
-int x_bind(const char *, const char *, bool);
-#endif
-void x_init(void);
-#ifdef DEBUG_LEAKS
-void x_done(void);
-#endif
-int x_read(char *);
-#endif
-void x_mkraw(int, mksh_ttyst *, bool);
-/* eval.c */
-char *substitute(const char *, int);
-char **eval(const char **, int);
-char *evalstr(const char *cp, int);
-char *evalonestr(const char *cp, int);
-char *debunk(char *, const char *, size_t);
-void expand(const char *, XPtrV *, int);
-int glob_str(char *, XPtrV *, bool);
-char *tilde(char *);
-/* exec.c */
-int execute(struct op * volatile, volatile int, volatile int * volatile);
-int shcomexec(const char **);
-struct tbl *findfunc(const char *, uint32_t, bool);
-int define(const char *, struct op *);
-const char *builtin(const char *, int (*)(const char **));
-struct tbl *findcom(const char *, int);
-void flushcom(bool);
-const char *search_path(const char *, const char *, int, int *);
-void pr_menu(const char * const *);
-void pr_list(char * const *);
-/* expr.c */
-int evaluate(const char *, mksh_ari_t *, int, bool);
-int v_evaluate(struct tbl *, const char *, volatile int, bool);
-/* UTF-8 stuff */
-size_t utf_mbtowc(unsigned int *, const char *);
-size_t utf_wctomb(char *, unsigned int);
-int utf_widthadj(const char *, const char **);
-size_t utf_mbswidth(const char *);
-const char *utf_skipcols(const char *, int);
-size_t utf_ptradj(const char *);
-#ifndef MKSH_mirbsd_wcwidth
-int utf_wcwidth(unsigned int);
-#endif
-int ksh_access(const char *, int);
-struct tbl *tempvar(void);
-/* funcs.c */
-int c_hash(const char **);
-int c_pwd(const char **);
-int c_print(const char **);
-#ifdef MKSH_PRINTF_BUILTIN
-int c_printf(const char **);
-#endif
-int c_whence(const char **);
-int c_command(const char **);
-int c_typeset(const char **);
-int c_alias(const char **);
-int c_unalias(const char **);
-int c_let(const char **);
-int c_jobs(const char **);
-#ifndef MKSH_UNEMPLOYED
-int c_fgbg(const char **);
-#endif
-int c_kill(const char **);
-void getopts_reset(int);
-int c_getopts(const char **);
-#ifndef MKSH_NO_CMDLINE_EDITING
-int c_bind(const char **);
-#endif
-int c_shift(const char **);
-int c_umask(const char **);
-int c_dot(const char **);
-int c_wait(const char **);
-int c_read(const char **);
-int c_eval(const char **);
-int c_trap(const char **);
-int c_brkcont(const char **);
-int c_exitreturn(const char **);
-int c_set(const char **);
-int c_unset(const char **);
-int c_ulimit(const char **);
-int c_times(const char **);
-int timex(struct op *, int, volatile int *);
-void timex_hook(struct op *, char ** volatile *);
-int c_exec(const char **);
-/* dummy function (just need pointer value), special case in comexec() */
-#define c_builtin shcomexec
-int c_test(const char **);
-#if HAVE_MKNOD
-int c_mknod(const char **);
-#endif
-int c_realpath(const char **);
-int c_rename(const char **);
-int c_cat(const char **);
-int c_sleep(const char **);
-/* histrap.c */
-void init_histvec(void);
-void hist_init(Source *);
-#if HAVE_PERSISTENT_HISTORY
-void hist_finish(void);
-#endif
-void histsave(int *, const char *, bool, bool);
-#if !defined(MKSH_SMALL) && HAVE_PERSISTENT_HISTORY
-bool histsync(void);
-#endif
-int c_fc(const char **);
-void sethistsize(mksh_ari_t);
-#if HAVE_PERSISTENT_HISTORY
-void sethistfile(const char *);
-#endif
-#if !defined(MKSH_NO_CMDLINE_EDITING) && !MKSH_S_NOVI
-char **histpos(void);
-int histnum(int);
-#endif
-int findhist(int, int, const char *, int);
-char **hist_get_newest(bool);
-void inittraps(void);
-void alarm_init(void);
-Trap *gettrap(const char *, bool);
-void trapsig(int);
-void intrcheck(void);
-int fatal_trap_check(void);
-int trap_pending(void);
-void runtraps(int intr);
-void runtrap(Trap *, bool);
-void cleartraps(void);
-void restoresigs(void);
-void settrap(Trap *, const char *);
-int block_pipe(void);
-void restore_pipe(int);
-int setsig(Trap *, sig_t, int);
-void setexecsig(Trap *, int);
-#if HAVE_FLOCK || HAVE_LOCK_FCNTL
-void mksh_lockfd(int);
-void mksh_unlkfd(int);
-#endif
-/* jobs.c */
-void j_init(void);
-void j_exit(void);
-#ifndef MKSH_UNEMPLOYED
-void j_change(void);
-#endif
-int exchild(struct op *, int, volatile int *, int);
-void startlast(void);
-int waitlast(void);
-int waitfor(const char *, int *);
-int j_kill(const char *, int);
-#ifndef MKSH_UNEMPLOYED
-int j_resume(const char *, int);
-#endif
-int j_jobs(const char *, int, int);
-void j_notify(void);
-pid_t j_async(void);
-int j_stopped_running(void);
-/* lex.c */
-int yylex(int);
-void yyskiputf8bom(void);
-void yyerror(const char *, ...)
-    MKSH_A_NORETURN
-    MKSH_A_FORMAT(__printf__, 1, 2);
-Source *pushs(int, Area *);
-void set_prompt(int, Source *);
-int pprompt(const char *, int);
-/* main.c */
-int include(const char *, int, const char **, bool);
-int command(const char *, int);
-int shell(Source * volatile, volatile bool);
-/* argument MUST NOT be 0 */
-void unwind(int) MKSH_A_NORETURN;
-void newenv(int);
-void quitenv(struct shf *);
-void cleanup_parents_env(void);
-void cleanup_proc_env(void);
-void errorf(const char *, ...)
-    MKSH_A_NORETURN
-    MKSH_A_FORMAT(__printf__, 1, 2);
-void errorfx(int, const char *, ...)
-    MKSH_A_NORETURN
-    MKSH_A_FORMAT(__printf__, 2, 3);
-void warningf(bool, const char *, ...)
-    MKSH_A_FORMAT(__printf__, 2, 3);
-void bi_errorf(const char *, ...)
-    MKSH_A_FORMAT(__printf__, 1, 2);
-#define errorfz()	errorf(NULL)
-#define errorfxz(rc)	errorfx((rc), NULL)
-#define bi_errorfz()	bi_errorf(NULL)
-void internal_errorf(const char *, ...)
-    MKSH_A_NORETURN
-    MKSH_A_FORMAT(__printf__, 1, 2);
-void internal_warningf(const char *, ...)
-    MKSH_A_FORMAT(__printf__, 1, 2);
-void error_prefix(bool);
-void shellf(const char *, ...)
-    MKSH_A_FORMAT(__printf__, 1, 2);
-void shprintf(const char *, ...)
-    MKSH_A_FORMAT(__printf__, 1, 2);
-int can_seek(int);
-void initio(void);
-int ksh_dup2(int, int, bool);
-short savefd(int);
-void restfd(int, int);
-void openpipe(int *);
-void closepipe(int *);
-int check_fd(const char *, int, const char **);
-void coproc_init(void);
-void coproc_read_close(int);
-void coproc_readw_close(int);
-void coproc_write_close(int);
-int coproc_getfd(int, const char **);
-void coproc_cleanup(int);
-struct temp *maketemp(Area *, Temp_type, struct temp **);
-void ktinit(Area *, struct table *, uint8_t);
-struct tbl *ktscan(struct table *, const char *, uint32_t, struct tbl ***);
-/* table, name (key) to search for, hash(n) */
-#define ktsearch(tp, s, h) ktscan((tp), (s), (h), NULL)
-struct tbl *ktenter(struct table *, const char *, uint32_t);
-#define ktdelete(p)	do { p->flag = 0; } while (/* CONSTCOND */ 0)
-void ktwalk(struct tstate *, struct table *);
-struct tbl *ktnext(struct tstate *);
-struct tbl **ktsort(struct table *);
-#ifdef DF
-void DF(const char *, ...)
-    MKSH_A_FORMAT(__printf__, 1, 2);
-#endif
-/* misc.c */
-void setctypes(const char *, int);
-void initctypes(void);
-size_t option(const char *);
-char *getoptions(void);
-void change_flag(enum sh_flag, int, bool);
-void change_xtrace(unsigned char, bool);
-int parse_args(const char **, int, bool *);
-int getn(const char *, int *);
-int gmatchx(const char *, const char *, bool);
-int has_globbing(const char *, const char *);
-int xstrcmp(const void *, const void *);
-void ksh_getopt_reset(Getopt *, int);
-int ksh_getopt(const char **, Getopt *, const char *);
-void print_value_quoted(struct shf *, const char *);
-char *quote_value(const char *);
-void print_columns(struct shf *, unsigned int,
-    char *(*)(char *, size_t, unsigned int, const void *),
-    const void *, size_t, size_t, bool);
-void strip_nuls(char *, int);
-ssize_t blocking_read(int, char *, size_t)
-    MKSH_A_BOUNDED(__buffer__, 2, 3);
-int reset_nonblock(int);
-char *ksh_get_wd(void);
-char *do_realpath(const char *);
-void simplify_path(char *);
-void set_current_wd(const char *);
-int c_cd(const char **);
-#if defined(MKSH_SMALL) && !defined(MKSH_SMALL_BUT_FAST)
-char *strdup_i(const char *, Area *);
-char *strndup_i(const char *, size_t, Area *);
-#endif
-int unbksl(bool, int (*)(void), void (*)(int));
-/* shf.c */
-struct shf *shf_open(const char *, int, int, int);
-struct shf *shf_fdopen(int, int, struct shf *);
-struct shf *shf_reopen(int, int, struct shf *);
-struct shf *shf_sopen(char *, ssize_t, int, struct shf *);
-int shf_close(struct shf *);
-int shf_fdclose(struct shf *);
-char *shf_sclose(struct shf *);
-int shf_flush(struct shf *);
-ssize_t shf_read(char *, ssize_t, struct shf *);
-char *shf_getse(char *, ssize_t, struct shf *);
-int shf_getchar(struct shf *s);
-int shf_ungetc(int, struct shf *);
-#if defined(MKSH_SMALL) && !defined(MKSH_SMALL_BUT_FAST)
-int shf_getc(struct shf *);
-int shf_putc(int, struct shf *);
-#else
-#define shf_getc shf_getc_i
-#define shf_putc shf_putc_i
-#endif
-int shf_putchar(int, struct shf *);
-ssize_t shf_puts(const char *, struct shf *);
-ssize_t shf_write(const char *, ssize_t, struct shf *);
-ssize_t shf_fprintf(struct shf *, const char *, ...)
-    MKSH_A_FORMAT(__printf__, 2, 3);
-ssize_t shf_snprintf(char *, ssize_t, const char *, ...)
-    MKSH_A_FORMAT(__printf__, 3, 4)
-    MKSH_A_BOUNDED(__string__, 1, 2);
-char *shf_smprintf(const char *, ...)
-    MKSH_A_FORMAT(__printf__, 1, 2);
-ssize_t shf_vfprintf(struct shf *, const char *, va_list)
-    MKSH_A_FORMAT(__printf__, 2, 0);
-/* syn.c */
-void initkeywords(void);
-struct op *compile(Source *, bool);
-bool parse_usec(const char *, struct timeval *);
-char *yyrecursive(int);
-void yyrecursive_pop(bool);
-/* tree.c */
-void fptreef(struct shf *, int, const char *, ...);
-char *snptreef(char *, ssize_t, const char *, ...);
-struct op *tcopy(struct op *, Area *);
-char *wdcopy(const char *, Area *);
-const char *wdscan(const char *, int);
-#define WDS_TPUTS	BIT(0)		/* tputS (dumpwdvar) mode */
-#define WDS_KEEPQ	BIT(1)		/* keep quote characters */
-#define WDS_MAGIC	BIT(2)		/* make MAGIC */
-char *wdstrip(const char *, int);
-void tfree(struct op *, Area *);
-void dumpchar(struct shf *, int);
-void dumptree(struct shf *, struct op *);
-void dumpwdvar(struct shf *, const char *);
-void dumpioact(struct shf *shf, struct op *t);
-void vistree(char *, size_t, struct op *)
-    MKSH_A_BOUNDED(__string__, 1, 2);
-void fpFUNCTf(struct shf *, int, bool, const char *, struct op *);
-/* var.c */
-void newblock(void);
-void popblock(void);
-void initvar(void);
-struct block *varsearch(struct block *, struct tbl **, const char *, uint32_t);
-struct tbl *global(const char *);
-struct tbl *local(const char *, bool);
-char *str_val(struct tbl *);
-int setstr(struct tbl *, const char *, int);
-struct tbl *setint_v(struct tbl *, struct tbl *, bool);
-void setint(struct tbl *, mksh_ari_t);
-void setint_n(struct tbl *, mksh_ari_t, int);
-struct tbl *typeset(const char *, uint32_t, uint32_t, int, int);
-void unset(struct tbl *, int);
-const char *skip_varname(const char *, int);
-const char *skip_wdvarname(const char *, bool);
-int is_wdvarname(const char *, bool);
-int is_wdvarassign(const char *);
-struct tbl *arraysearch(struct tbl *, uint32_t);
-char **makenv(void);
-void change_winsz(void);
-size_t array_ref_len(const char *);
-char *arrayname(const char *);
-mksh_uari_t set_array(const char *, bool, const char **);
-uint32_t hash(const void *);
-mksh_ari_t rndget(void);
-void rndset(unsigned long);
-
-enum Test_op {
-	/* non-operator */
-	TO_NONOP = 0,
-	/* unary operators */
-	TO_STNZE, TO_STZER, TO_OPTION,
-	TO_FILAXST,
-	TO_FILEXST,
-	TO_FILREG, TO_FILBDEV, TO_FILCDEV, TO_FILSYM, TO_FILFIFO, TO_FILSOCK,
-	TO_FILCDF, TO_FILID, TO_FILGID, TO_FILSETG, TO_FILSTCK, TO_FILUID,
-	TO_FILRD, TO_FILGZ, TO_FILTT, TO_FILSETU, TO_FILWR, TO_FILEX,
-	/* binary operators */
-	TO_STEQL, TO_STNEQ, TO_STLT, TO_STGT, TO_INTEQ, TO_INTNE, TO_INTGT,
-	TO_INTGE, TO_INTLT, TO_INTLE, TO_FILEQ, TO_FILNT, TO_FILOT,
-	/* not an operator */
-	TO_NONNULL	/* !TO_NONOP */
-};
-typedef enum Test_op Test_op;
-
-/* Used by Test_env.isa() (order important - used to index *_tokens[] arrays) */
-enum Test_meta {
-	TM_OR,		/* -o or || */
-	TM_AND,		/* -a or && */
-	TM_NOT,		/* ! */
-	TM_OPAREN,	/* ( */
-	TM_CPAREN,	/* ) */
-	TM_UNOP,	/* unary operator */
-	TM_BINOP,	/* binary operator */
-	TM_END		/* end of input */
-};
-typedef enum Test_meta Test_meta;
-
-#define TEF_ERROR	BIT(0)		/* set if we've hit an error */
-#define TEF_DBRACKET	BIT(1)		/* set if [[ .. ]] test */
-
-typedef struct test_env {
-	union {
-		const char **wp;	/* used by ptest_* */
-		XPtrV *av;		/* used by dbtestp_* */
-	} pos;
-	const char **wp_end;		/* used by ptest_* */
-	Test_op (*isa)(struct test_env *, Test_meta);
-	const char *(*getopnd) (struct test_env *, Test_op, bool);
-	int (*eval)(struct test_env *, Test_op, const char *, const char *, bool);
-	void (*error)(struct test_env *, int, const char *);
-	int flags;			/* TEF_* */
-} Test_env;
-
-extern const char * const dbtest_tokens[];
-
-Test_op	test_isop(Test_meta, const char *);
-int test_eval(Test_env *, Test_op, const char *, const char *, bool);
-int test_parse(Test_env *);
-
-EXTERN int tty_fd E_INIT(-1);	/* dup'd tty file descriptor */
-EXTERN bool tty_devtty;		/* true if tty_fd is from /dev/tty */
-EXTERN mksh_ttyst tty_state;	/* saved tty state */
-EXTERN bool tty_hasstate;	/* true if tty_state is valid */
-
-extern int tty_init_fd(void);	/* initialise tty_fd, tty_devtty */
-
-/* be sure not to interfere with anyone else's idea about EXTERN */
-#ifdef EXTERN_DEFINED
-# undef EXTERN_DEFINED
-# undef EXTERN
-#endif
-#undef E_INIT
-
-#endif /* !MKSH_INCLUDES_ONLY */

Copied: vendor/MirOS/mksh/R50/sh.h (from rev 6707, vendor/MirOS/mksh/dist/sh.h)
===================================================================
--- vendor/MirOS/mksh/R50/sh.h	                        (rev 0)
+++ vendor/MirOS/mksh/R50/sh.h	2014-07-01 12:01:17 UTC (rev 6708)
@@ -0,0 +1,2040 @@
+/*	$OpenBSD: sh.h,v 1.33 2013/12/18 13:53:12 millert Exp $	*/
+/*	$OpenBSD: shf.h,v 1.6 2005/12/11 18:53:51 deraadt Exp $	*/
+/*	$OpenBSD: table.h,v 1.8 2012/02/19 07:52:30 otto Exp $	*/
+/*	$OpenBSD: tree.h,v 1.10 2005/03/28 21:28:22 deraadt Exp $	*/
+/*	$OpenBSD: expand.h,v 1.6 2005/03/30 17:16:37 deraadt Exp $	*/
+/*	$OpenBSD: lex.h,v 1.13 2013/03/03 19:11:34 guenther Exp $	*/
+/*	$OpenBSD: proto.h,v 1.35 2013/09/04 15:49:19 millert Exp $	*/
+/*	$OpenBSD: c_test.h,v 1.4 2004/12/20 11:34:26 otto Exp $	*/
+/*	$OpenBSD: tty.h,v 1.5 2004/12/20 11:34:26 otto Exp $	*/
+
+/*-
+ * Copyright © 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
+ *	       2011, 2012, 2013, 2014
+ *	Thorsten Glaser <tg at mirbsd.org>
+ *
+ * Provided that these terms and disclaimer and all copyright notices
+ * are retained or reproduced in an accompanying document, permission
+ * is granted to deal in this work without restriction, including un‐
+ * limited rights to use, publicly perform, distribute, sell, modify,
+ * merge, give away, or sublicence.
+ *
+ * This work is provided “AS IS” and WITHOUT WARRANTY of any kind, to
+ * the utmost extent permitted by applicable law, neither express nor
+ * implied; without malicious intent or gross negligence. In no event
+ * may a licensor, author or contributor be held liable for indirect,
+ * direct, other damage, loss, or other issues arising in any way out
+ * of dealing in the work, even if advised of the possibility of such
+ * damage or existence of a defect, except proven that it results out
+ * of said person’s immediate fault when using the work as intended.
+ */
+
+#ifdef __dietlibc__
+/* XXX imake style */
+#define _BSD_SOURCE	/* live, BSD, live❣ */
+#endif
+
+#if HAVE_SYS_PARAM_H
+#include <sys/param.h>
+#endif
+#include <sys/types.h>
+#if HAVE_BOTH_TIME_H
+#include <sys/time.h>
+#include <time.h>
+#elif HAVE_SYS_TIME_H
+#include <sys/time.h>
+#elif HAVE_TIME_H
+#include <time.h>
+#endif
+#include <sys/ioctl.h>
+#if HAVE_SYS_SYSMACROS_H
+#include <sys/sysmacros.h>
+#endif
+#if HAVE_SYS_MKDEV_H
+#include <sys/mkdev.h>
+#endif
+#if HAVE_SYS_MMAN_H
+#include <sys/mman.h>
+#endif
+#if HAVE_SYS_RESOURCE_H
+#include <sys/resource.h>
+#endif
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#if HAVE_LIBGEN_H
+#include <libgen.h>
+#endif
+#if HAVE_LIBUTIL_H
+#include <libutil.h>
+#endif
+#include <limits.h>
+#if HAVE_PATHS_H
+#include <paths.h>
+#endif
+#include <pwd.h>
+#include <setjmp.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <stddef.h>
+#if HAVE_STDINT_H
+#include <stdint.h>
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#if HAVE_STRINGS_H
+#include <strings.h>
+#endif
+#if HAVE_TERMIOS_H
+#include <termios.h>
+#else
+/* shudder… */
+#include <termio.h>
+#endif
+#ifdef _ISC_UNIX
+/* XXX imake style */
+#include <sys/sioctl.h>
+#endif
+#if HAVE_ULIMIT_H
+#include <ulimit.h>
+#endif
+#include <unistd.h>
+#if HAVE_VALUES_H
+#include <values.h>
+#endif
+
+#undef __attribute__
+#if HAVE_ATTRIBUTE_BOUNDED
+#define MKSH_A_BOUNDED(x,y,z)	__attribute__((__bounded__(x, y, z)))
+#else
+#define MKSH_A_BOUNDED(x,y,z)	/* nothing */
+#endif
+#if HAVE_ATTRIBUTE_FORMAT
+#define MKSH_A_FORMAT(x,y,z)	__attribute__((__format__(x, y, z)))
+#else
+#define MKSH_A_FORMAT(x,y,z)	/* nothing */
+#endif
+#if HAVE_ATTRIBUTE_NORETURN
+#define MKSH_A_NORETURN		__attribute__((__noreturn__))
+#else
+#define MKSH_A_NORETURN		/* nothing */
+#endif
+#if HAVE_ATTRIBUTE_PURE
+#define MKSH_A_PURE		__attribute__((__pure__))
+#else
+#define MKSH_A_PURE		/* nothing */
+#endif
+#if HAVE_ATTRIBUTE_UNUSED
+#define MKSH_A_UNUSED		__attribute__((__unused__))
+#else
+#define MKSH_A_UNUSED		/* nothing */
+#endif
+#if HAVE_ATTRIBUTE_USED
+#define MKSH_A_USED		__attribute__((__used__))
+#else
+#define MKSH_A_USED		/* nothing */
+#endif
+
+#if defined(MirBSD) && (MirBSD >= 0x09A1) && \
+    defined(__ELF__) && defined(__GNUC__) && \
+    !defined(__llvm__) && !defined(__NWCC__)
+/*
+ * We got usable __IDSTRING __COPYRIGHT __RCSID __SCCSID macros
+ * which work for all cases; no need to redefine them using the
+ * "portable" macros from below when we might have the "better"
+ * gcc+ELF specific macros or other system dependent ones.
+ */
+#else
+#undef __IDSTRING
+#undef __IDSTRING_CONCAT
+#undef __IDSTRING_EXPAND
+#undef __COPYRIGHT
+#undef __RCSID
+#undef __SCCSID
+#define __IDSTRING_CONCAT(l,p)		__LINTED__ ## l ## _ ## p
+#define __IDSTRING_EXPAND(l,p)		__IDSTRING_CONCAT(l,p)
+#ifdef MKSH_DONT_EMIT_IDSTRING
+#define __IDSTRING(prefix, string)	/* nothing */
+#else
+#define __IDSTRING(prefix, string)				\
+	static const char __IDSTRING_EXPAND(__LINE__,prefix) []	\
+	    MKSH_A_USED = "@(""#)" #prefix ": " string
+#endif
+#define __COPYRIGHT(x)		__IDSTRING(copyright,x)
+#define __RCSID(x)		__IDSTRING(rcsid,x)
+#define __SCCSID(x)		__IDSTRING(sccsid,x)
+#endif
+
+#ifdef EXTERN
+__RCSID("$MirOS: src/bin/mksh/sh.h,v 1.691 2014/06/29 11:28:28 tg Exp $");
+#endif
+#define MKSH_VERSION "R50 2014/06/29"
+
+/* arithmetic types: C implementation */
+#if !HAVE_CAN_INTTYPES
+#if !HAVE_CAN_UCBINTS
+typedef signed int int32_t;
+typedef unsigned int uint32_t;
+#else
+typedef u_int32_t uint32_t;
+#endif
+#endif
+
+/* arithmetic types: shell arithmetics */
+#ifdef MKSH_LEGACY_MODE
+/*
+ * POSIX demands these to be the C environment's long type
+ */
+typedef long mksh_ari_t;
+typedef unsigned long mksh_uari_t;
+#else
+/*
+ * These types are exactly 32 bit wide; signed and unsigned
+ * integer wraparound, even across division and modulo, for
+ * any shell code using them, is guaranteed.
+ */
+typedef int32_t mksh_ari_t;
+typedef uint32_t mksh_uari_t;
+#endif
+
+/* boolean type (no <stdbool.h> deliberately) */
+typedef unsigned char mksh_bool;
+#undef bool
+/* false MUST equal the same 0 as written by static storage initialisation */
+#undef false
+#undef true
+/* access macros for boolean type */
+#define bool		mksh_bool
+/* values must have identity mapping between mksh_bool and short */
+#define false		0
+#define true		1
+/* make any-type into bool or short */
+#define tobool(cond)	((cond) ? true : false)
+
+/* char (octet) type: C implementation */
+#if !HAVE_CAN_INT8TYPE
+#if !HAVE_CAN_UCBINT8
+typedef unsigned char uint8_t;
+#else
+typedef u_int8_t uint8_t;
+#endif
+#endif
+
+/* other standard types */
+
+#if !HAVE_RLIM_T
+typedef unsigned long rlim_t;
+#endif
+
+#if !HAVE_SIG_T
+#undef sig_t
+typedef void (*sig_t)(int);
+#endif
+
+#ifdef MKSH_TYPEDEF_SIG_ATOMIC_T
+typedef MKSH_TYPEDEF_SIG_ATOMIC_T sig_atomic_t;
+#endif
+
+#ifdef MKSH_TYPEDEF_SSIZE_T
+typedef MKSH_TYPEDEF_SSIZE_T ssize_t;
+#endif
+
+/* un-do vendor damage */
+
+#undef BAD		/* AIX defines that somewhere */
+#undef PRINT		/* LynxOS defines that somewhere */
+#undef flock		/* SCO UnixWare defines that to flock64 but ENOENT */
+
+
+#ifndef MKSH_INCLUDES_ONLY
+
+/* extra types */
+
+#if !HAVE_GETRUSAGE
+#undef rusage
+#undef RUSAGE_SELF
+#undef RUSAGE_CHILDREN
+#define rusage mksh_rusage
+#define RUSAGE_SELF		0
+#define RUSAGE_CHILDREN		-1
+
+struct rusage {
+	struct timeval ru_utime;
+	struct timeval ru_stime;
+};
+#endif
+
+/* extra macros */
+
+#ifndef timerclear
+#define timerclear(tvp)							\
+	do {								\
+		(tvp)->tv_sec = (tvp)->tv_usec = 0;			\
+	} while (/* CONSTCOND */ 0)
+#endif
+#ifndef timeradd
+#define timeradd(tvp, uvp, vvp)						\
+	do {								\
+		(vvp)->tv_sec = (tvp)->tv_sec + (uvp)->tv_sec;		\
+		(vvp)->tv_usec = (tvp)->tv_usec + (uvp)->tv_usec;	\
+		if ((vvp)->tv_usec >= 1000000) {			\
+			(vvp)->tv_sec++;				\
+			(vvp)->tv_usec -= 1000000;			\
+		}							\
+	} while (/* CONSTCOND */ 0)
+#endif
+#ifndef timersub
+#define timersub(tvp, uvp, vvp)						\
+	do {								\
+		(vvp)->tv_sec = (tvp)->tv_sec - (uvp)->tv_sec;		\
+		(vvp)->tv_usec = (tvp)->tv_usec - (uvp)->tv_usec;	\
+		if ((vvp)->tv_usec < 0) {				\
+			(vvp)->tv_sec--;				\
+			(vvp)->tv_usec += 1000000;			\
+		}							\
+	} while (/* CONSTCOND */ 0)
+#endif
+
+#define ksh_isdigit(c)	(((c) >= '0') && ((c) <= '9'))
+#define ksh_islower(c)	(((c) >= 'a') && ((c) <= 'z'))
+#define ksh_isupper(c)	(((c) >= 'A') && ((c) <= 'Z'))
+#define ksh_tolower(c)	(((c) >= 'A') && ((c) <= 'Z') ? (c) - 'A' + 'a' : (c))
+#define ksh_toupper(c)	(((c) >= 'a') && ((c) <= 'z') ? (c) - 'a' + 'A' : (c))
+#define ksh_isdash(s)	(((s)[0] == '-') && ((s)[1] == '\0'))
+#define ksh_isspace(c)	((((c) >= 0x09) && ((c) <= 0x0D)) || ((c) == 0x20))
+#define ksh_min(x,y)	((x) < (y) ? (x) : (y))
+#define ksh_max(x,y)	((x) > (y) ? (x) : (y))
+
+#ifdef MKSH__NO_PATH_MAX
+#undef PATH_MAX
+#else
+#ifndef PATH_MAX
+#define PATH_MAX	1024
+#endif
+#endif
+#ifndef SIZE_MAX
+#ifdef SIZE_T_MAX
+#define SIZE_MAX	SIZE_T_MAX
+#else
+#define SIZE_MAX	((size_t)-1)
+#endif
+#endif
+#ifndef S_ISLNK
+#define S_ISLNK(m)	((m & 0170000) == 0120000)
+#endif
+#ifndef S_ISSOCK
+#define S_ISSOCK(m)	((m & 0170000) == 0140000)
+#endif
+#if !defined(S_ISCDF) && defined(S_CDF)
+#define S_ISCDF(m)	(S_ISDIR(m) && ((m) & S_CDF))
+#endif
+#ifndef DEFFILEMODE
+#define DEFFILEMODE	(S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH)
+#endif
+
+#ifndef NSIG
+#if defined(_NSIG)
+#define NSIG		_NSIG
+#elif defined(SIGMAX)
+#define NSIG		(SIGMAX+1)
+#elif defined(_SIGMAX)
+#define NSIG		(_SIGMAX+1)
+#else
+# error Please have your platform define NSIG.
+#define NSIG		64
+#endif
+#endif
+
+/* get rid of this (and awk/printf(1) in Build.sh) later */
+#if (NSIG < 1)
+# error Your NSIG value is not positive.
+#unset NSIG
+#define NSIG		64
+#endif
+
+
+/* OS-dependent additions (functions, variables, by OS) */
+
+#if !HAVE_FLOCK_DECL
+extern int flock(int, int);
+#endif
+
+#if !HAVE_GETTIMEOFDAY
+#define mksh_TIME(tv) do {		\
+	(tv).tv_usec = 0;		\
+	(tv).tv_sec = time(NULL);	\
+} while (/* CONSTCOND */ 0)
+#else
+#define mksh_TIME(tv) gettimeofday(&(tv), NULL)
+#endif
+
+#if !HAVE_GETRUSAGE
+extern int getrusage(int, struct rusage *);
+#endif
+
+#if !HAVE_MEMMOVE
+/* we assume either memmove or bcopy exist, at the moment */
+#define memmove(dst, src, len)	bcopy((src), (dst), (len))
+#endif
+
+#if !HAVE_REVOKE_DECL
+extern int revoke(const char *);
+#endif
+
+#if defined(DEBUG) || !HAVE_STRERROR
+#undef strerror
+#define strerror		/* poisoned */ dontuse_strerror
+#define cstrerror		/* replaced */ cstrerror
+extern const char *cstrerror(int);
+#else
+#define cstrerror(errnum)	((const char *)strerror(errnum))
+#endif
+
+#if !HAVE_STRLCPY
+size_t strlcpy(char *, const char *, size_t);
+#endif
+
+#ifdef __INTERIX
+/* XXX imake style */
+#define makedev mkdev
+extern int __cdecl seteuid(uid_t);
+extern int __cdecl setegid(gid_t);
+#endif
+
+#if defined(__COHERENT__)
+#ifndef O_ACCMODE
+/* this need not work everywhere, take care */
+#define O_ACCMODE	(O_RDONLY | O_WRONLY | O_RDWR)
+#endif
+#endif
+
+#ifndef O_BINARY
+#define O_BINARY	0
+#endif
+
+#ifdef MKSH__NO_SYMLINK
+#undef S_ISLNK
+#define S_ISLNK(m)	(/* CONSTCOND */ 0)
+#define mksh_lstat	stat
+#else
+#define mksh_lstat	lstat
+#endif
+
+#if HAVE_TERMIOS_H
+#define mksh_ttyst	struct termios
+#define mksh_tcget(fd,st) tcgetattr((fd), (st))
+#define mksh_tcset(fd,st) tcsetattr((fd), TCSADRAIN, (st))
+#else
+#define mksh_ttyst	struct termio
+#define mksh_tcget(fd,st) ioctl((fd), TCGETA, (st))
+#define mksh_tcset(fd,st) ioctl((fd), TCSETAW, (st))
+#endif
+
+#ifndef ISTRIP
+#define ISTRIP		0
+#endif
+
+
+/* some useful #defines */
+#ifdef EXTERN
+# define E_INIT(i) = i
+#else
+# define E_INIT(i)
+# define EXTERN extern
+# define EXTERN_DEFINED
+#endif
+
+/* define bit in flag */
+#define BIT(i)		(1 << (i))
+#define NELEM(a)	(sizeof(a) / sizeof((a)[0]))
+
+/*
+ * Make MAGIC a char that might be printed to make bugs more obvious, but
+ * not a char that is used often. Also, can't use the high bit as it causes
+ * portability problems (calling strchr(x, 0x80 | 'x') is error prone).
+ */
+#define MAGIC		(7)	/* prefix for *?[!{,} during expand */
+#define ISMAGIC(c)	((unsigned char)(c) == MAGIC)
+
+EXTERN const char *safe_prompt; /* safe prompt if PS1 substitution fails */
+
+#ifdef MKSH_LEGACY_MODE
+#define KSH_VERSIONNAME	"LEGACY"
+#else
+#define KSH_VERSIONNAME	"MIRBSD"
+#endif
+EXTERN const char initvsn[] E_INIT("KSH_VERSION=@(#)" KSH_VERSIONNAME \
+    " KSH " MKSH_VERSION);
+#define KSH_VERSION	(initvsn + /* "KSH_VERSION=@(#)" */ 16)
+
+EXTERN const char digits_uc[] E_INIT("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ");
+EXTERN const char digits_lc[] E_INIT("0123456789abcdefghijklmnopqrstuvwxyz");
+
+/*
+ * Evil hack for const correctness due to API brokenness
+ */
+union mksh_cchack {
+	char *rw;
+	const char *ro;
+};
+union mksh_ccphack {
+	char **rw;
+	const char **ro;
+};
+
+/*
+ * Evil hack since casting uint to sint is implementation-defined
+ */
+typedef union {
+	mksh_ari_t i;
+	mksh_uari_t u;
+} mksh_ari_u;
+
+/* for const debugging */
+#if defined(DEBUG) && defined(__GNUC__) && !defined(__ICC) && \
+    !defined(__INTEL_COMPILER) && !defined(__SUNPRO_C)
+char *ucstrchr(char *, int);
+char *ucstrstr(char *, const char *);
+#undef strchr
+#define strchr ucstrchr
+#define strstr ucstrstr
+#define cstrchr(s,c) ({			\
+	union mksh_cchack in, out;	\
+					\
+	in.ro = (s);			\
+	out.rw = ucstrchr(in.rw, (c));	\
+	(out.ro);			\
+})
+#define cstrstr(b,l) ({			\
+	union mksh_cchack in, out;	\
+					\
+	in.ro = (b);			\
+	out.rw = ucstrstr(in.rw, (l));	\
+	(out.ro);			\
+})
+#define vstrchr(s,c)	(cstrchr((s), (c)) != NULL)
+#define vstrstr(b,l)	(cstrstr((b), (l)) != NULL)
+#else /* !DEBUG, !gcc */
+#define cstrchr(s,c)	((const char *)strchr((s), (c)))
+#define cstrstr(s,c)	((const char *)strstr((s), (c)))
+#define vstrchr(s,c)	(strchr((s), (c)) != NULL)
+#define vstrstr(b,l)	(strstr((b), (l)) != NULL)
+#endif
+
+#if defined(DEBUG) || defined(__COVERITY__)
+#define mkssert(e)	do { if (!(e)) exit(255); } while (/* CONSTCOND */ 0)
+#ifndef DEBUG_LEAKS
+#define DEBUG_LEAKS
+#endif
+#else
+#define mkssert(e)	do { } while (/* CONSTCOND */ 0)
+#endif
+
+#if (!defined(MKSH_BUILDMAKEFILE4BSD) && !defined(MKSH_BUILDSH)) || (MKSH_BUILD_R != 501)
+#error Must run Build.sh to compile this.
+extern void thiswillneverbedefinedIhope(void);
+int
+im_sorry_dave(void)
+{
+	/* I’m sorry, Dave. I’m afraid I can’t do that. */
+	return (thiswillneverbedefinedIhope());
+}
+#endif
+
+/* use this ipv strchr(s, 0) but no side effects in s! */
+#define strnul(s)	((s) + strlen(s))
+
+#define utf_ptradjx(src, dst) do {					\
+	(dst) = (src) + utf_ptradj(src);				\
+} while (/* CONSTCOND */ 0)
+
+#if defined(MKSH_SMALL) && !defined(MKSH_SMALL_BUT_FAST)
+#define strdupx(d, s, ap) do {						\
+	(d) = strdup_i((s), (ap));					\
+} while (/* CONSTCOND */ 0)
+#define strndupx(d, s, n, ap) do {					\
+	(d) = strndup_i((s), (n), (ap));				\
+} while (/* CONSTCOND */ 0)
+#else
+/* be careful to evaluate arguments only once! */
+#define strdupx(d, s, ap) do {						\
+	const char *strdup_src = (s);					\
+	char *strdup_dst = NULL;					\
+									\
+	if (strdup_src != NULL) {					\
+		size_t strdup_len = strlen(strdup_src) + 1;		\
+		strdup_dst = alloc(strdup_len, (ap));			\
+		memcpy(strdup_dst, strdup_src, strdup_len);		\
+	}								\
+	(d) = strdup_dst;						\
+} while (/* CONSTCOND */ 0)
+#define strndupx(d, s, n, ap) do {					\
+	const char *strdup_src = (s);					\
+	char *strdup_dst = NULL;					\
+									\
+	if (strdup_src != NULL) {					\
+		size_t strndup_len = (n);				\
+		strdup_dst = alloc(strndup_len + 1, (ap));		\
+		memcpy(strdup_dst, strdup_src, strndup_len);		\
+		strdup_dst[strndup_len] = '\0';				\
+	}								\
+	(d) = strdup_dst;						\
+} while (/* CONSTCOND */ 0)
+#endif
+
+#ifdef MKSH_LEGACY_MODE
+#ifndef MKSH_NO_CMDLINE_EDITING
+#define MKSH_NO_CMDLINE_EDITING	/* defined */
+#endif
+#ifndef MKSH_CONSERVATIVE_FDS
+#define MKSH_CONSERVATIVE_FDS	/* defined */
+#endif
+#undef MKSH_S_NOVI
+#define MKSH_S_NOVI		1
+#endif
+
+#ifdef MKSH_SMALL
+#ifndef MKSH_CONSERVATIVE_FDS
+#define MKSH_CONSERVATIVE_FDS	/* defined */
+#endif
+#ifndef MKSH_NOPWNAM
+#define MKSH_NOPWNAM		/* defined */
+#endif
+#ifndef MKSH_S_NOVI
+#define MKSH_S_NOVI		1
+#endif
+#endif
+
+#ifndef MKSH_S_NOVI
+#define MKSH_S_NOVI		0
+#endif
+
+#if defined(MKSH_NOPROSPECTOFWORK) && !defined(MKSH_UNEMPLOYED)
+#define MKSH_UNEMPLOYED		1
+#endif
+
+/* these shall be smaller than 100 */
+#ifdef MKSH_CONSERVATIVE_FDS
+#define NUFILE		32	/* Number of user-accessible files */
+#define FDBASE		10	/* First file usable by Shell */
+#else
+#define NUFILE		56	/* Number of user-accessible files */
+#define FDBASE		24	/* First file usable by Shell */
+#endif
+
+/*
+ * simple grouping allocator
+ */
+
+
+/* 0. OS API: where to get memory from and how to free it (grouped) */
+
+/* malloc(3)/realloc(3) -> free(3) for use by the memory allocator */
+#define malloc_osi(sz)		malloc(sz)
+#define realloc_osi(p,sz)	realloc((p), (sz))
+#define free_osimalloc(p)	free(p)
+
+/* malloc(3)/realloc(3) -> free(3) for use by mksh code */
+#define malloc_osfunc(sz)	malloc(sz)
+#define realloc_osfunc(p,sz)	realloc((p), (sz))
+#define free_osfunc(p)		free(p)
+
+#if HAVE_MKNOD
+/* setmode(3) -> free(3) */
+#define free_ossetmode(p)	free(p)
+#endif
+
+#ifdef MKSH__NO_PATH_MAX
+/* GNU libc: get_current_dir_name(3) -> free(3) */
+#define free_gnu_gcdn(p)	free(p)
+#endif
+
+
+/* 1. internal structure */
+struct lalloc {
+	struct lalloc *next;
+};
+
+/* 2. sizes */
+#define ALLOC_ITEM	struct lalloc
+#define ALLOC_SIZE	(sizeof(ALLOC_ITEM))
+
+/* 3. group structure (only the same for lalloc.c) */
+typedef struct lalloc Area;
+
+
+EXTERN Area aperm;		/* permanent object space */
+#define APERM	&aperm
+#define ATEMP	&e->area
+
+/*
+ * flags (the order of these enums MUST match the order in misc.c(options[]))
+ */
+enum sh_flag {
+#define SHFLAGS_ENUMS
+#include "sh_flags.gen"
+	FNFLAGS		/* (place holder: how many flags are there) */
+};
+
+#define Flag(f)	(shell_flags[(int)(f)])
+#define UTFMODE	Flag(FUNICODE)
+
+/*
+ * parsing & execution environment
+ *
+ * note that kshlongjmp MUST NOT be passed 0 as second argument!
+ */
+#ifdef MKSH_NO_SIGSETJMP
+#define kshjmp_buf	jmp_buf
+#define kshsetjmp(jbuf)	_setjmp(jbuf)
+#define kshlongjmp	_longjmp
+#else
+#define kshjmp_buf	sigjmp_buf
+#define kshsetjmp(jbuf)	sigsetjmp((jbuf), 0)
+#define kshlongjmp	siglongjmp
+#endif
+
+struct sretrace_info;
+struct yyrecursive_state;
+
+EXTERN struct sretrace_info *retrace_info E_INIT(NULL);
+EXTERN int subshell_nesting_type E_INIT(0);
+
+extern struct env {
+	ALLOC_ITEM alloc_INT;	/* internal, do not touch */
+	Area area;		/* temporary allocation area */
+	struct env *oenv;	/* link to previous environment */
+	struct block *loc;	/* local variables and functions */
+	short *savefd;		/* original redirected fds */
+	struct temp *temps;	/* temp files */
+	/* saved parser recursion state */
+	struct yyrecursive_state *yyrecursive_statep;
+	kshjmp_buf jbuf;	/* long jump back to env creator */
+	uint8_t type;		/* environment type - see below */
+	uint8_t flags;		/* EF_* */
+} *e;
+
+/* struct env.type values */
+#define E_NONE	0	/* dummy environment */
+#define E_PARSE	1	/* parsing command # */
+#define E_FUNC	2	/* executing function # */
+#define E_INCL	3	/* including a file via . # */
+#define E_EXEC	4	/* executing command tree */
+#define E_LOOP	5	/* executing for/while # */
+#define E_ERRH	6	/* general error handler # */
+#define E_GONE	7	/* hidden in child */
+/* # indicates env has valid jbuf (see unwind()) */
+
+/* struct env.flag values */
+#define EF_BRKCONT_PASS	BIT(1)	/* set if E_LOOP must pass break/continue on */
+#define EF_FAKE_SIGDIE	BIT(2)	/* hack to get info from unwind to quitenv */
+
+/* Do breaks/continues stop at env type e? */
+#define STOP_BRKCONT(t)	((t) == E_NONE || (t) == E_PARSE || \
+			    (t) == E_FUNC || (t) == E_INCL)
+/* Do returns stop at env type e? */
+#define STOP_RETURN(t)	((t) == E_FUNC || (t) == E_INCL)
+
+/* values for kshlongjmp(e->jbuf, i) */
+/* note that i MUST NOT be zero */
+#define LRETURN	1	/* return statement */
+#define LEXIT	2	/* exit statement */
+#define LERROR	3	/* errorf() called */
+#define LLEAVE	4	/* untrappable exit/error */
+#define LINTR	5	/* ^C noticed */
+#define LBREAK	6	/* break statement */
+#define LCONTIN	7	/* continue statement */
+#define LSHELL	8	/* return to interactive shell() */
+#define LAEXPR	9	/* error in arithmetic expression */
+
+/* sort of shell global state */
+EXTERN pid_t procpid;		/* PID of executing process */
+EXTERN int exstat;		/* exit status */
+EXTERN int subst_exstat;	/* exit status of last $(..)/`..` */
+EXTERN struct tbl *vp_pipest;	/* global PIPESTATUS array */
+EXTERN short trap_exstat;	/* exit status before running a trap */
+EXTERN uint8_t trap_nested;	/* running nested traps */
+EXTERN uint8_t shell_flags[FNFLAGS];
+EXTERN const char *kshname;	/* $0 */
+EXTERN struct {
+	uid_t kshuid_v;		/* real UID of shell */
+	uid_t ksheuid_v;	/* effective UID of shell */
+	gid_t kshgid_v;		/* real GID of shell */
+	gid_t kshegid_v;	/* effective GID of shell */
+	pid_t kshpgrp_v;	/* process group of shell */
+	pid_t kshppid_v;	/* PID of parent of shell */
+	pid_t kshpid_v;		/* $$, shell PID */
+} rndsetupstate;
+
+#define kshpid		rndsetupstate.kshpid_v
+#define kshpgrp		rndsetupstate.kshpgrp_v
+#define kshuid		rndsetupstate.kshuid_v
+#define ksheuid		rndsetupstate.ksheuid_v
+#define kshgid		rndsetupstate.kshgid_v
+#define kshegid		rndsetupstate.kshegid_v
+#define kshppid		rndsetupstate.kshppid_v
+
+
+/* option processing */
+#define OF_CMDLINE	0x01	/* command line */
+#define OF_SET		0x02	/* set builtin */
+#define OF_SPECIAL	0x04	/* a special variable changing */
+#define OF_INTERNAL	0x08	/* set internally by shell */
+#define OF_FIRSTTIME	0x10	/* as early as possible, once */
+#define OF_ANY		(OF_CMDLINE | OF_SET | OF_SPECIAL | OF_INTERNAL)
+
+/* null value for variable; comparison pointer for unset */
+EXTERN char null[] E_INIT("");
+/* helpers for string pooling */
+EXTERN const char Tintovfl[] E_INIT("integer overflow %zu %c %zu prevented");
+EXTERN const char Toomem[] E_INIT("can't allocate %zu data bytes");
+#if defined(__GNUC__)
+/* trust this to have string pooling; -Wformat bitches otherwise */
+#define Tsynerr		"syntax error"
+#else
+EXTERN const char Tsynerr[] E_INIT("syntax error");
+#endif
+EXTERN const char Tselect[] E_INIT("select");
+EXTERN const char Tr_fc_e_dash[] E_INIT("r=fc -e -");
+#define Tfc_e_dash	(Tr_fc_e_dash + 2)	/* "fc -e -" */
+#define Zfc_e_dash	7			/* strlen(Tfc_e_dash) */
+EXTERN const char Tlocal_typeset[] E_INIT("local=typeset");
+#define T_typeset	(Tlocal_typeset + 5)	/* "=typeset" */
+#define Ttypeset	(Tlocal_typeset + 6)	/* "typeset" */
+EXTERN const char Talias[] E_INIT("alias");
+EXTERN const char Tunalias[] E_INIT("unalias");
+EXTERN const char Tsgset[] E_INIT("*=set");
+#define Tset		(Tsgset + 2)		/* "set" */
+EXTERN const char Tsgunset[] E_INIT("*=unset");
+#define Tunset		(Tsgunset + 2)		/* "unset" */
+EXTERN const char Tsgexport[] E_INIT("*=export");
+#define Texport		(Tsgexport + 2)		/* "export" */
+EXTERN const char Tsgreadonly[] E_INIT("*=readonly");
+#define Treadonly	(Tsgreadonly + 2)	/* "readonly" */
+EXTERN const char Tgbuiltin[] E_INIT("=builtin");
+#define Tbuiltin	(Tgbuiltin + 1)		/* "builtin" */
+EXTERN const char T_function[] E_INIT(" function");
+#define Tfunction	(T_function + 1)	/* "function" */
+EXTERN const char TC_LEX1[] E_INIT("|&;<>() \t\n");
+#define TC_IFSWS	(TC_LEX1 + 7)		/* space tab newline */
+
+typedef uint8_t Temp_type;
+/* expanded heredoc */
+#define TT_HEREDOC_EXP	0
+/* temporary file used for history editing (fc -e) */
+#define TT_HIST_EDIT	1
+/* temporary file used during in-situ command substitution */
+#define TT_FUNSUB	2
+
+/* temp/heredoc files. The file is removed when the struct is freed. */
+struct temp {
+	struct temp *next;
+	struct shf *shf;
+	/* pid of process parsed here-doc */
+	pid_t pid;
+	Temp_type type;
+	/* actually longer: name (variable length) */
+	char tffn[3];
+};
+
+/*
+ * stdio and our IO routines
+ */
+
+#define shl_xtrace	(&shf_iob[0])	/* for set -x */
+#define shl_stdout	(&shf_iob[1])
+#define shl_out		(&shf_iob[2])
+#ifdef DF
+#define shl_dbg		(&shf_iob[3])	/* for DF() */
+#endif
+EXTERN bool shl_stdout_ok;
+
+/*
+ * trap handlers
+ */
+typedef struct trap {
+	const char *name;	/* short name */
+	const char *mess;	/* descriptive name */
+	char *trap;		/* trap command */
+	sig_t cursig;		/* current handler (valid if TF_ORIG_* set) */
+	sig_t shtrap;		/* shell signal handler */
+	int signal;		/* signal number */
+	int flags;		/* TF_* */
+	volatile sig_atomic_t set; /* trap pending */
+} Trap;
+
+/* values for Trap.flags */
+#define TF_SHELL_USES	BIT(0)	/* shell uses signal, user can't change */
+#define TF_USER_SET	BIT(1)	/* user has (tried to) set trap */
+#define TF_ORIG_IGN	BIT(2)	/* original action was SIG_IGN */
+#define TF_ORIG_DFL	BIT(3)	/* original action was SIG_DFL */
+#define TF_EXEC_IGN	BIT(4)	/* restore SIG_IGN just before exec */
+#define TF_EXEC_DFL	BIT(5)	/* restore SIG_DFL just before exec */
+#define TF_DFL_INTR	BIT(6)	/* when received, default action is LINTR */
+#define TF_TTY_INTR	BIT(7)	/* tty generated signal (see j_waitj) */
+#define TF_CHANGED	BIT(8)	/* used by runtrap() to detect trap changes */
+#define TF_FATAL	BIT(9)	/* causes termination if not trapped */
+
+/* values for setsig()/setexecsig() flags argument */
+#define SS_RESTORE_MASK	0x3	/* how to restore a signal before an exec() */
+#define SS_RESTORE_CURR	0	/* leave current handler in place */
+#define SS_RESTORE_ORIG	1	/* restore original handler */
+#define SS_RESTORE_DFL	2	/* restore to SIG_DFL */
+#define SS_RESTORE_IGN	3	/* restore to SIG_IGN */
+#define SS_FORCE	BIT(3)	/* set signal even if original signal ignored */
+#define SS_USER		BIT(4)	/* user is doing the set (ie, trap command) */
+#define SS_SHTRAP	BIT(5)	/* trap for internal use (ALRM, CHLD, WINCH) */
+
+#define ksh_SIGEXIT	0	/* for trap EXIT */
+#define ksh_SIGERR	NSIG	/* for trap ERR */
+
+EXTERN volatile sig_atomic_t trap;	/* traps pending? */
+EXTERN volatile sig_atomic_t intrsig;	/* pending trap interrupts command */
+EXTERN volatile sig_atomic_t fatal_trap; /* received a fatal signal */
+extern	Trap	sigtraps[NSIG+1];
+
+/* got_winch = 1 when we need to re-adjust the window size */
+#ifdef SIGWINCH
+EXTERN volatile sig_atomic_t got_winch E_INIT(1);
+#else
+#define got_winch	true
+#endif
+
+/*
+ * TMOUT support
+ */
+/* values for ksh_tmout_state */
+enum tmout_enum {
+	TMOUT_EXECUTING = 0,	/* executing commands */
+	TMOUT_READING,		/* waiting for input */
+	TMOUT_LEAVING		/* have timed out */
+};
+EXTERN unsigned int ksh_tmout;
+EXTERN enum tmout_enum ksh_tmout_state E_INIT(TMOUT_EXECUTING);
+
+/* For "You have stopped jobs" message */
+EXTERN bool really_exit;
+
+/*
+ * fast character classes
+ */
+#define C_ALPHA	 BIT(0)		/* a-z_A-Z */
+#define C_DIGIT	 BIT(1)		/* 0-9 */
+#define C_LEX1	 BIT(2)		/* \t \n\0|&;<>() */
+#define C_VAR1	 BIT(3)		/* *@#!$-? */
+#define C_IFSWS	 BIT(4)		/* \t \n (IFS white space) */
+#define C_SUBOP1 BIT(5)		/* "=-+?" */
+#define C_QUOTE	 BIT(6)		/* \t\n "#$&'()*;<=>?[\]`| (needing quoting) */
+#define C_IFS	 BIT(7)		/* $IFS */
+#define C_SUBOP2 BIT(8)		/* "#%" (magic, see below) */
+
+extern unsigned char chtypes[];
+
+#define ctype(c, t)	tobool( ((t) == C_SUBOP2) ?			\
+			    (((c) == '#' || (c) == '%') ? 1 : 0) :	\
+			    (chtypes[(unsigned char)(c)] & (t)) )
+#define ksh_isalphx(c)	ctype((c), C_ALPHA)
+#define ksh_isalnux(c)	ctype((c), C_ALPHA | C_DIGIT)
+
+EXTERN int ifs0 E_INIT(' ');	/* for "$*" */
+
+/* Argument parsing for built-in commands and getopts command */
+
+/* Values for Getopt.flags */
+#define GF_ERROR	BIT(0)	/* call errorf() if there is an error */
+#define GF_PLUSOPT	BIT(1)	/* allow +c as an option */
+#define GF_NONAME	BIT(2)	/* don't print argv[0] in errors */
+
+/* Values for Getopt.info */
+#define GI_MINUS	BIT(0)	/* an option started with -... */
+#define GI_PLUS		BIT(1)	/* an option started with +... */
+#define GI_MINUSMINUS	BIT(2)	/* arguments were ended with -- */
+
+/* in case some OS defines these */
+#undef optarg
+#undef optind
+
+typedef struct {
+	const char *optarg;
+	int optind;
+	int uoptind;		/* what user sees in $OPTIND */
+	int flags;		/* see GF_* */
+	int info;		/* see GI_* */
+	unsigned int p;		/* 0 or index into argv[optind - 1] */
+	char buf[2];		/* for bad option OPTARG value */
+} Getopt;
+
+EXTERN Getopt builtin_opt;	/* for shell builtin commands */
+EXTERN Getopt user_opt;		/* parsing state for getopts builtin command */
+
+/* This for co-processes */
+
+/* something that won't (realisticly) wrap */
+typedef int32_t Coproc_id;
+
+struct coproc {
+	void *job;	/* 0 or job of co-process using input pipe */
+	int read;	/* pipe from co-process's stdout */
+	int readw;	/* other side of read (saved temporarily) */
+	int write;	/* pipe to co-process's stdin */
+	int njobs;	/* number of live jobs using output pipe */
+	Coproc_id id;	/* id of current output pipe */
+};
+EXTERN struct coproc coproc;
+
+#ifndef MKSH_NOPROSPECTOFWORK
+/* used in jobs.c and by coprocess stuff in exec.c and select() calls */
+EXTERN sigset_t		sm_default, sm_sigchld;
+#endif
+
+/* name of called builtin function (used by error functions) */
+EXTERN const char *builtin_argv0;
+/* flags of called builtin (SPEC_BI, etc.) */
+EXTERN uint32_t builtin_flag;
+
+/* current working directory */
+EXTERN char	*current_wd;
+
+/* input line size */
+#define LINE		(4096 - ALLOC_SIZE)
+/*
+ * Minimum required space to work with on a line - if the prompt leaves
+ * less space than this on a line, the prompt is truncated.
+ */
+#define MIN_EDIT_SPACE	7
+/*
+ * Minimum allowed value for x_cols: 2 for prompt, 3 for " < " at end of line
+ */
+#define MIN_COLS	(2 + MIN_EDIT_SPACE + 3)
+#define MIN_LINS	3
+EXTERN mksh_ari_t x_cols E_INIT(80);	/* tty columns */
+EXTERN mksh_ari_t x_lins E_INIT(24);	/* tty lines */
+
+
+/* Determine the location of the system (common) profile */
+
+#ifndef MKSH_DEFAULT_PROFILEDIR
+#if defined(ANDROID)
+#define MKSH_DEFAULT_PROFILEDIR	"/system/etc"
+#else
+#define MKSH_DEFAULT_PROFILEDIR	"/etc"
+#endif
+#endif
+
+#define MKSH_SYSTEM_PROFILE	MKSH_DEFAULT_PROFILEDIR "/profile"
+#define MKSH_SUID_PROFILE	MKSH_DEFAULT_PROFILEDIR "/suid_profile"
+
+
+/* Used by v_evaluate() and setstr() to control action when error occurs */
+#define KSH_UNWIND_ERROR	0	/* unwind the stack (kshlongjmp) */
+#define KSH_RETURN_ERROR	1	/* return 1/0 for success/failure */
+
+/*
+ * Shell file I/O routines
+ */
+
+#define SHF_BSIZE		512
+
+#define shf_fileno(shf)		((shf)->fd)
+#define shf_setfileno(shf,nfd)	((shf)->fd = (nfd))
+#define shf_getc_i(shf)		((shf)->rnleft > 0 ? \
+				    (shf)->rnleft--, *(shf)->rp++ : \
+				    shf_getchar(shf))
+#define shf_putc_i(c, shf)	((shf)->wnleft == 0 ? \
+				    shf_putchar((c), (shf)) : \
+				    ((shf)->wnleft--, *(shf)->wp++ = (c)))
+#define shf_eof(shf)		((shf)->flags & SHF_EOF)
+#define shf_error(shf)		((shf)->flags & SHF_ERROR)
+#define shf_errno(shf)		((shf)->errnosv)
+#define shf_clearerr(shf)	((shf)->flags &= ~(SHF_EOF | SHF_ERROR))
+
+/* Flags passed to shf_*open() */
+#define SHF_RD		0x0001
+#define SHF_WR		0x0002
+#define SHF_RDWR	(SHF_RD|SHF_WR)
+#define SHF_ACCMODE	0x0003		/* mask */
+#define SHF_GETFL	0x0004		/* use fcntl() to figure RD/WR flags */
+#define SHF_UNBUF	0x0008		/* unbuffered I/O */
+#define SHF_CLEXEC	0x0010		/* set close on exec flag */
+#define SHF_MAPHI	0x0020		/* make fd > FDBASE (and close orig)
+					 * (shf_open() only) */
+#define SHF_DYNAMIC	0x0040		/* string: increase buffer as needed */
+#define SHF_INTERRUPT	0x0080		/* EINTR in read/write causes error */
+/* Flags used internally */
+#define SHF_STRING	0x0100		/* a string, not a file */
+#define SHF_ALLOCS	0x0200		/* shf and shf->buf were alloc()ed */
+#define SHF_ALLOCB	0x0400		/* shf->buf was alloc()ed */
+#define SHF_ERROR	0x0800		/* read()/write() error */
+#define SHF_EOF		0x1000		/* read eof (sticky) */
+#define SHF_READING	0x2000		/* currently reading: rnleft,rp valid */
+#define SHF_WRITING	0x4000		/* currently writing: wnleft,wp valid */
+
+
+struct shf {
+	Area *areap;		/* area shf/buf were allocated in */
+	unsigned char *rp;	/* read: current position in buffer */
+	unsigned char *wp;	/* write: current position in buffer */
+	unsigned char *buf;	/* buffer */
+	ssize_t bsize;		/* actual size of buf */
+	ssize_t rbsize;		/* size of buffer (1 if SHF_UNBUF) */
+	ssize_t rnleft;		/* read: how much data left in buffer */
+	ssize_t wbsize;		/* size of buffer (0 if SHF_UNBUF) */
+	ssize_t wnleft;		/* write: how much space left in buffer */
+	int flags;		/* see SHF_* */
+	int fd;			/* file descriptor */
+	int errnosv;		/* saved value of errno after error */
+};
+
+extern struct shf shf_iob[];
+
+struct table {
+	Area *areap;		/* area to allocate entries */
+	struct tbl **tbls;	/* hashed table items */
+	size_t nfree;		/* free table entries */
+	uint8_t tshift;		/* table size (2^tshift) */
+};
+
+/* table item */
+struct tbl {
+	/* Area to allocate from */
+	Area *areap;
+	/* value */
+	union {
+		char *s;			/* string */
+		mksh_ari_t i;			/* integer */
+		mksh_uari_t u;			/* unsigned integer */
+		int (*f)(const char **);	/* built-in command */
+		struct op *t;			/* "function" tree */
+	} val;
+	union {
+		struct tbl *array;	/* array values */
+		const char *fpath;	/* temporary path to undef function */
+	} u;
+	union {
+		int field;		/* field with for -L/-R/-Z */
+		int errnov;		/* CEXEC/CTALIAS */
+	} u2;
+	union {
+		uint32_t hval;		/* hash(name) */
+		uint32_t index;		/* index for an array */
+	} ua;
+	/*
+	 * command type (see below), base (if INTEGER),
+	 * offset from val.s of value (if EXPORT)
+	 */
+	int type;
+	/* flags (see below) */
+	uint32_t flag;
+
+	/* actually longer: name (variable length) */
+	char name[4];
+};
+
+EXTERN struct tbl vtemp;
+
+/* common flag bits */
+#define ALLOC		BIT(0)	/* val.s has been allocated */
+#define DEFINED		BIT(1)	/* is defined in block */
+#define ISSET		BIT(2)	/* has value, vp->val.[si] */
+#define EXPORT		BIT(3)	/* exported variable/function */
+#define TRACE		BIT(4)	/* var: user flagged, func: execution tracing */
+/* (start non-common flags at 8) */
+/* flag bits used for variables */
+#define SPECIAL		BIT(8)	/* PATH, IFS, SECONDS, etc */
+#define INTEGER		BIT(9)	/* val.i contains integer value */
+#define RDONLY		BIT(10)	/* read-only variable */
+#define LOCAL		BIT(11)	/* for local typeset() */
+#define ARRAY		BIT(13)	/* array */
+#define LJUST		BIT(14)	/* left justify */
+#define RJUST		BIT(15)	/* right justify */
+#define ZEROFIL		BIT(16)	/* 0 filled if RJUSTIFY, strip 0s if LJUSTIFY */
+#define LCASEV		BIT(17)	/* convert to lower case */
+#define UCASEV_AL	BIT(18) /* convert to upper case / autoload function */
+#define INT_U		BIT(19)	/* unsigned integer */
+#define INT_L		BIT(20)	/* long integer (no-op but used as magic) */
+#define IMPORT		BIT(21)	/* flag to typeset(): no arrays, must have = */
+#define LOCAL_COPY	BIT(22)	/* with LOCAL - copy attrs from existing var */
+#define EXPRINEVAL	BIT(23)	/* contents currently being evaluated */
+#define EXPRLVALUE	BIT(24)	/* useable as lvalue (temp flag) */
+#define AINDEX		BIT(25) /* array index >0 = ua.index filled in */
+#define ASSOC		BIT(26) /* ARRAY ? associative : reference */
+/* flag bits used for taliases/builtins/aliases/keywords/functions */
+#define KEEPASN		BIT(8)	/* keep command assignments (eg, var=x cmd) */
+#define FINUSE		BIT(9)	/* function being executed */
+#define FDELETE		BIT(10)	/* function deleted while it was executing */
+#define FKSH		BIT(11)	/* function defined with function x (vs x()) */
+#define SPEC_BI		BIT(12)	/* a POSIX special builtin */
+/*
+ * Attributes that can be set by the user (used to decide if an unset
+ * param should be repoted by set/typeset). Does not include ARRAY or
+ * LOCAL.
+ */
+#define USERATTRIB	(EXPORT|INTEGER|RDONLY|LJUST|RJUST|ZEROFIL|\
+			    LCASEV|UCASEV_AL|INT_U|INT_L)
+
+#define arrayindex(vp)	((unsigned long)((vp)->flag & AINDEX ? \
+			    (vp)->ua.index : 0))
+
+enum namerefflag {
+	SRF_NOP,
+	SRF_ENABLE,
+	SRF_DISABLE
+};
+
+/* command types */
+#define CNONE		0	/* undefined */
+#define CSHELL		1	/* built-in */
+#define CFUNC		2	/* function */
+#define CEXEC		4	/* executable command */
+#define CALIAS		5	/* alias */
+#define CKEYWD		6	/* keyword */
+#define CTALIAS		7	/* tracked alias */
+
+/* Flags for findcom()/comexec() */
+#define FC_SPECBI	BIT(0)	/* special builtin */
+#define FC_FUNC		BIT(1)	/* function */
+#define FC_NORMBI	BIT(2)	/* not special builtin */
+#define FC_BI		(FC_SPECBI | FC_NORMBI)
+#define FC_PATH		BIT(3)	/* do path search */
+#define FC_DEFPATH	BIT(4)	/* use default path in path search */
+
+
+#define AF_ARGV_ALLOC	0x1	/* argv[] array allocated */
+#define AF_ARGS_ALLOCED	0x2	/* argument strings allocated */
+#define AI_ARGV(a, i)	((i) == 0 ? (a).argv[0] : (a).argv[(i) - (a).skip])
+#define AI_ARGC(a)	((a).ai_argc - (a).skip)
+
+/* Argument info. Used for $#, $* for shell, functions, includes, etc. */
+struct arg_info {
+	const char **argv;
+	int flags;	/* AF_* */
+	int ai_argc;
+	int skip;	/* first arg is argv[0], second is argv[1 + skip] */
+};
+
+/*
+ * activation record for function blocks
+ */
+struct block {
+	Area area;		/* area to allocate things */
+	const char **argv;
+	char *error;		/* error handler */
+	char *exit;		/* exit handler */
+	struct block *next;	/* enclosing block */
+	struct table vars;	/* local variables */
+	struct table funs;	/* local functions */
+	Getopt getopts_state;
+	int argc;
+	int flags;		/* see BF_* */
+};
+
+/* Values for struct block.flags */
+#define BF_DOGETOPTS	BIT(0)	/* save/restore getopts state */
+
+/*
+ * Used by ktwalk() and ktnext() routines.
+ */
+struct tstate {
+	struct tbl **next;
+	ssize_t left;
+};
+
+EXTERN struct table taliases;	/* tracked aliases */
+EXTERN struct table builtins;	/* built-in commands */
+EXTERN struct table aliases;	/* aliases */
+EXTERN struct table keywords;	/* keywords */
+#ifndef MKSH_NOPWNAM
+EXTERN struct table homedirs;	/* homedir() cache */
+#endif
+
+struct builtin {
+	const char *name;
+	int (*func)(const char **);
+};
+
+extern const struct builtin mkshbuiltins[];
+
+/* values for set_prompt() */
+#define PS1	0	/* command */
+#define PS2	1	/* command continuation */
+
+EXTERN char *path;		/* copy of either PATH or def_path */
+EXTERN const char *def_path;	/* path to use if PATH not set */
+EXTERN char *tmpdir;		/* TMPDIR value */
+EXTERN const char *prompt;
+EXTERN int cur_prompt;		/* PS1 or PS2 */
+EXTERN int current_lineno;	/* LINENO value */
+
+/*
+ * Description of a command or an operation on commands.
+ */
+struct op {
+	const char **args;		/* arguments to a command */
+	char **vars;			/* variable assignments */
+	struct ioword **ioact;		/* IO actions (eg, < > >>) */
+	struct op *left, *right;	/* descendents */
+	char *str;			/* word for case; identifier for for,
+					 * select, and functions;
+					 * path to execute for TEXEC;
+					 * time hook for TCOM.
+					 */
+	int lineno;			/* TCOM/TFUNC: LINENO for this */
+	short type;			/* operation type, see below */
+	/* WARNING: newtp(), tcopy() use evalflags = 0 to clear union */
+	union {
+		/* TCOM: arg expansion eval() flags */
+		short evalflags;
+		/* TFUNC: function x (vs x()) */
+		short ksh_func;
+		/* TPAT: termination character */
+		char charflag;
+	} u;
+};
+
+/* Tree.type values */
+#define TEOF		0
+#define TCOM		1	/* command */
+#define TPAREN		2	/* (c-list) */
+#define TPIPE		3	/* a | b */
+#define TLIST		4	/* a ; b */
+#define TOR		5	/* || */
+#define TAND		6	/* && */
+#define TBANG		7	/* ! */
+#define TDBRACKET	8	/* [[ .. ]] */
+#define TFOR		9
+#define TSELECT		10
+#define TCASE		11
+#define TIF		12
+#define TWHILE		13
+#define TUNTIL		14
+#define TELIF		15
+#define TPAT		16	/* pattern in case */
+#define TBRACE		17	/* {c-list} */
+#define TASYNC		18	/* c & */
+#define TFUNCT		19	/* function name { command; } */
+#define TTIME		20	/* time pipeline */
+#define TEXEC		21	/* fork/exec eval'd TCOM */
+#define TCOPROC		22	/* coprocess |& */
+
+/*
+ * prefix codes for words in command tree
+ */
+#define EOS	0	/* end of string */
+#define CHAR	1	/* unquoted character */
+#define QCHAR	2	/* quoted character */
+#define COMSUB	3	/* $() substitution (0 terminated) */
+#define EXPRSUB	4	/* $(()) substitution (0 terminated) */
+#define OQUOTE	5	/* opening " or ' */
+#define CQUOTE	6	/* closing " or ' */
+#define OSUBST	7	/* opening ${ subst (followed by { or X) */
+#define CSUBST	8	/* closing } of above (followed by } or X) */
+#define OPAT	9	/* open pattern: *(, @(, etc. */
+#define SPAT	10	/* separate pattern: | */
+#define CPAT	11	/* close pattern: ) */
+#define ADELIM	12	/* arbitrary delimiter: ${foo:2:3} ${foo/bar/baz} */
+#define FUNSUB	14	/* ${ foo;} substitution (NUL terminated) */
+#define VALSUB	15	/* ${|foo;} substitution (NUL terminated) */
+
+/*
+ * IO redirection
+ */
+struct ioword {
+	int	unit;		/* unit affected */
+	int	flag;		/* action (below) */
+	char	*name;		/* file name (unused if heredoc) */
+	char	*delim;		/* delimiter for <<,<<- */
+	char	*heredoc;	/* content of heredoc */
+};
+
+/* ioword.flag - type of redirection */
+#define IOTYPE		0xF	/* type: bits 0:3 */
+#define IOREAD		0x1	/* < */
+#define IOWRITE		0x2	/* > */
+#define IORDWR		0x3	/* <>: todo */
+#define IOHERE		0x4	/* << (here file) */
+#define IOCAT		0x5	/* >> */
+#define IODUP		0x6	/* <&/>& */
+#define IOEVAL		BIT(4)	/* expand in << */
+#define IOSKIP		BIT(5)	/* <<-, skip ^\t* */
+#define IOCLOB		BIT(6)	/* >|, override -o noclobber */
+#define IORDUP		BIT(7)	/* x<&y (as opposed to x>&y) */
+#define IONAMEXP	BIT(8)	/* name has been expanded */
+#define IOBASH		BIT(9)	/* &> etc. */
+#define IOHERESTR	BIT(10)	/* <<< (here string) */
+#define IONDELIM	BIT(11)	/* null delimiter (<<) */
+
+/* execute/exchild flags */
+#define XEXEC	BIT(0)		/* execute without forking */
+#define XFORK	BIT(1)		/* fork before executing */
+#define XBGND	BIT(2)		/* command & */
+#define XPIPEI	BIT(3)		/* input is pipe */
+#define XPIPEO	BIT(4)		/* output is pipe */
+#define XXCOM	BIT(5)		/* `...` command */
+#define XPCLOSE	BIT(6)		/* exchild: close close_fd in parent */
+#define XCCLOSE	BIT(7)		/* exchild: close close_fd in child */
+#define XERROK	BIT(8)		/* non-zero exit ok (for set -e) */
+#define XCOPROC BIT(9)		/* starting a co-process */
+#define XTIME	BIT(10)		/* timing TCOM command */
+#define XPIPEST	BIT(11)		/* want PIPESTATUS */
+
+/*
+ * flags to control expansion of words (assumed by t->evalflags to fit
+ * in a short)
+ */
+#define DOBLANK	BIT(0)		/* perform blank interpretation */
+#define DOGLOB	BIT(1)		/* expand [?* */
+#define DOPAT	BIT(2)		/* quote *?[ */
+#define DOTILDE	BIT(3)		/* normal ~ expansion (first char) */
+#define DONTRUNCOMMAND BIT(4)	/* do not run $(command) things */
+#define DOASNTILDE BIT(5)	/* assignment ~ expansion (after =, :) */
+#define DOBRACE BIT(6)		/* used by expand(): do brace expansion */
+#define DOMAGIC BIT(7)		/* used by expand(): string contains MAGIC */
+#define DOTEMP	BIT(8)		/* dito: in word part of ${..[%#=?]..} */
+#define DOVACHECK BIT(9)	/* var assign check (for typeset, set, etc) */
+#define DOMARKDIRS BIT(10)	/* force markdirs behaviour */
+#define DOTCOMEXEC BIT(11)	/* not an eval flag, used by sh -c hack */
+
+#define X_EXTRA	20	/* this many extra bytes in X string */
+
+typedef struct XString {
+	char *end, *beg;	/* end, begin of string */
+	size_t len;		/* length */
+	Area *areap;		/* area to allocate/free from */
+} XString;
+
+typedef char *XStringP;
+
+/* initialise expandable string */
+#define XinitN(xs, length, area) do {				\
+	(xs).len = (length);					\
+	(xs).areap = (area);					\
+	(xs).beg = alloc((xs).len + X_EXTRA, (xs).areap);	\
+	(xs).end = (xs).beg + (xs).len;				\
+} while (/* CONSTCOND */ 0)
+#define Xinit(xs, xp, length, area) do {			\
+	XinitN((xs), (length), (area));				\
+	(xp) = (xs).beg;					\
+} while (/* CONSTCOND */ 0)
+
+/* stuff char into string */
+#define Xput(xs, xp, c)	(*xp++ = (c))
+
+/* check if there are at least n bytes left */
+#define XcheckN(xs, xp, n) do {					\
+	ssize_t more = ((xp) + (n)) - (xs).end;			\
+	if (more > 0)						\
+		(xp) = Xcheck_grow(&(xs), (xp), (size_t)more);	\
+} while (/* CONSTCOND */ 0)
+
+/* check for overflow, expand string */
+#define Xcheck(xs, xp)	XcheckN((xs), (xp), 1)
+
+/* free string */
+#define Xfree(xs, xp)	afree((xs).beg, (xs).areap)
+
+/* close, return string */
+#define Xclose(xs, xp)	aresize((xs).beg, (xp) - (xs).beg, (xs).areap)
+
+/* begin of string */
+#define Xstring(xs, xp)	((xs).beg)
+
+#define Xnleft(xs, xp)	((xs).end - (xp))	/* may be less than 0 */
+#define Xlength(xs, xp)	((xp) - (xs).beg)
+#define Xsize(xs, xp)	((xs).end - (xs).beg)
+#define Xsavepos(xs, xp)	((xp) - (xs).beg)
+#define Xrestpos(xs, xp, n)	((xs).beg + (n))
+
+char *Xcheck_grow(XString *, const char *, size_t);
+
+/*
+ * expandable vector of generic pointers
+ */
+
+typedef struct {
+	/* begin of allocated area */
+	void **beg;
+	/* currently used number of entries */
+	size_t len;
+	/* allocated number of entries */
+	size_t siz;
+} XPtrV;
+
+#define XPinit(x, n)	do {					\
+	(x).siz = (n);						\
+	(x).len = 0;						\
+	(x).beg = alloc2((x).siz, sizeof(void *), ATEMP);	\
+} while (/* CONSTCOND */ 0)					\
+
+#define XPput(x, p)	do {					\
+	if ((x).len == (x).siz) {				\
+		(x).beg = aresize2((x).beg, (x).siz,		\
+		    2 * sizeof(void *), ATEMP);			\
+		(x).siz <<= 1;					\
+	}							\
+	(x).beg[(x).len++] = (p);				\
+} while (/* CONSTCOND */ 0)
+
+#define XPptrv(x)	((x).beg)
+#define XPsize(x)	((x).len)
+#define XPclose(x)	aresize2((x).beg, XPsize(x), sizeof(void *), ATEMP)
+#define XPfree(x)	afree((x).beg, ATEMP)
+
+/*
+ * Lexer internals
+ */
+
+typedef struct source Source;
+struct source {
+	const char *str;	/* input pointer */
+	const char *start;	/* start of current buffer */
+	union {
+		const char **strv;	/* string [] */
+		struct shf *shf;	/* shell file */
+		struct tbl *tblp;	/* alias (SF_HASALIAS) */
+		char *freeme;		/* also for SREREAD */
+	} u;
+	const char *file;	/* input file name */
+	int	type;		/* input type */
+	int	line;		/* line number */
+	int	errline;	/* line the error occurred on (0 if not set) */
+	int	flags;		/* SF_* */
+	Area	*areap;
+	Source *next;		/* stacked source */
+	XString	xs;		/* input buffer */
+	char	ugbuf[2];	/* buffer for ungetsc() (SREREAD) and
+				 * alias (SALIAS) */
+};
+
+/* Source.type values */
+#define SEOF		0	/* input EOF */
+#define SFILE		1	/* file input */
+#define SSTDIN		2	/* read stdin */
+#define SSTRING		3	/* string */
+#define SWSTR		4	/* string without \n */
+#define SWORDS		5	/* string[] */
+#define SWORDSEP	6	/* string[] separator */
+#define SALIAS		7	/* alias expansion */
+#define SREREAD		8	/* read ahead to be re-scanned */
+#define SSTRINGCMDLINE	9	/* string from "mksh -c ..." */
+
+/* Source.flags values */
+#define SF_ECHO		BIT(0)	/* echo input to shlout */
+#define SF_ALIAS	BIT(1)	/* faking space at end of alias */
+#define SF_ALIASEND	BIT(2)	/* faking space at end of alias */
+#define SF_TTY		BIT(3)	/* type == SSTDIN & it is a tty */
+#define SF_HASALIAS	BIT(4)	/* u.tblp valid (SALIAS, SEOF) */
+#define SF_MAYEXEC	BIT(5)	/* special sh -c optimisation hack */
+
+typedef union {
+	int i;
+	char *cp;
+	char **wp;
+	struct op *o;
+	struct ioword *iop;
+} YYSTYPE;
+
+/* If something is added here, add it to tokentab[] in syn.c as well */
+#define LWORD		256
+#define LOGAND		257	/* && */
+#define LOGOR		258	/* || */
+#define BREAK		259	/* ;; */
+#define IF		260
+#define THEN		261
+#define ELSE		262
+#define ELIF		263
+#define FI		264
+#define CASE		265
+#define ESAC		266
+#define FOR		267
+#define SELECT		268
+#define WHILE		269
+#define UNTIL		270
+#define DO		271
+#define DONE		272
+#define IN		273
+#define FUNCTION	274
+#define TIME		275
+#define REDIR		276
+#define MDPAREN		277	/* (( )) */
+#define BANG		278	/* ! */
+#define DBRACKET	279	/* [[ .. ]] */
+#define COPROC		280	/* |& */
+#define BRKEV		281	/* ;| */
+#define BRKFT		282	/* ;& */
+#define YYERRCODE	300
+
+/* flags to yylex */
+#define CONTIN		BIT(0)	/* skip new lines to complete command */
+#define ONEWORD		BIT(1)	/* single word for substitute() */
+#define ALIAS		BIT(2)	/* recognise alias */
+#define KEYWORD		BIT(3)	/* recognise keywords */
+#define LETEXPR		BIT(4)	/* get expression inside (( )) */
+#define VARASN		BIT(5)	/* check for var=word */
+#define ARRAYVAR	BIT(6)	/* parse x[1 & 2] as one word */
+#define ESACONLY	BIT(7)	/* only accept esac keyword */
+#define CMDWORD		BIT(8)	/* parsing simple command (alias related) */
+#define HEREDELIM	BIT(9)	/* parsing <<,<<- delimiter */
+#define LQCHAR		BIT(10)	/* source string contains QCHAR */
+#define HEREDOC 	BIT(11)	/* parsing a here document body */
+
+#define HERES		10	/* max number of << in line */
+
+#undef CTRL
+#define	CTRL(x)		((x) == '?' ? 0x7F : (x) & 0x1F)	/* ASCII */
+#define	UNCTRL(x)	((x) ^ 0x40)				/* ASCII */
+#define	ISCTRL(x)	(((signed char)((uint8_t)(x) + 1)) < 33)
+
+#define IDENT		64
+
+EXTERN Source *source;		/* yyparse/yylex source */
+EXTERN YYSTYPE yylval;		/* result from yylex */
+EXTERN struct ioword *heres[HERES], **herep;
+EXTERN char ident[IDENT + 1];
+
+EXTERN char **history;		/* saved commands */
+EXTERN char **histptr;		/* last history item */
+EXTERN mksh_ari_t histsize;	/* history size */
+
+/* user and system time of last j_waitjed job */
+EXTERN struct timeval j_usrtime, j_systime;
+
+#define notok2mul(max, val, c)	(((val) != 0) && ((c) != 0) && \
+				    (((max) / (c)) < (val)))
+#define notok2add(max, val, c)	((val) > ((max) - (c)))
+#define notoktomul(val, cnst)	notok2mul(SIZE_MAX, (val), (cnst))
+#define notoktoadd(val, cnst)	notok2add(SIZE_MAX, (val), (cnst))
+#define checkoktoadd(val, cnst) do {					\
+	if (notoktoadd((val), (cnst)))					\
+		internal_errorf(Tintovfl, (size_t)(val),		\
+		    '+', (size_t)(cnst));				\
+} while (/* CONSTCOND */ 0)
+
+
+/* lalloc.c */
+void ainit(Area *);
+void afreeall(Area *);
+/* these cannot fail and can take NULL (not for ap) */
+#define alloc(n, ap)		aresize(NULL, (n), (ap))
+#define alloc2(m, n, ap)	aresize2(NULL, (m), (n), (ap))
+void *aresize(void *, size_t, Area *);
+void *aresize2(void *, size_t, size_t, Area *);
+void afree(void *, Area *);	/* can take NULL */
+/* edit.c */
+#ifndef MKSH_NO_CMDLINE_EDITING
+#ifndef MKSH_SMALL
+int x_bind(const char *, const char *, bool, bool);
+#else
+int x_bind(const char *, const char *, bool);
+#endif
+void x_init(void);
+#ifdef DEBUG_LEAKS
+void x_done(void);
+#endif
+int x_read(char *);
+#endif
+void x_mkraw(int, mksh_ttyst *, bool);
+/* eval.c */
+char *substitute(const char *, int);
+char **eval(const char **, int);
+char *evalstr(const char *cp, int);
+char *evalonestr(const char *cp, int);
+char *debunk(char *, const char *, size_t);
+void expand(const char *, XPtrV *, int);
+int glob_str(char *, XPtrV *, bool);
+char *tilde(char *);
+/* exec.c */
+int execute(struct op * volatile, volatile int, volatile int * volatile);
+int shcomexec(const char **);
+struct tbl *findfunc(const char *, uint32_t, bool);
+int define(const char *, struct op *);
+const char *builtin(const char *, int (*)(const char **));
+struct tbl *findcom(const char *, int);
+void flushcom(bool);
+const char *search_path(const char *, const char *, int, int *);
+void pr_menu(const char * const *);
+void pr_list(char * const *);
+/* expr.c */
+int evaluate(const char *, mksh_ari_t *, int, bool);
+int v_evaluate(struct tbl *, const char *, volatile int, bool);
+/* UTF-8 stuff */
+size_t utf_mbtowc(unsigned int *, const char *);
+size_t utf_wctomb(char *, unsigned int);
+int utf_widthadj(const char *, const char **);
+size_t utf_mbswidth(const char *) MKSH_A_PURE;
+const char *utf_skipcols(const char *, int) MKSH_A_PURE;
+size_t utf_ptradj(const char *) MKSH_A_PURE;
+int utf_wcwidth(unsigned int) MKSH_A_PURE;
+int ksh_access(const char *, int);
+struct tbl *tempvar(void);
+/* funcs.c */
+int c_hash(const char **);
+int c_pwd(const char **);
+int c_print(const char **);
+#ifdef MKSH_PRINTF_BUILTIN
+int c_printf(const char **);
+#endif
+int c_whence(const char **);
+int c_command(const char **);
+int c_typeset(const char **);
+int c_alias(const char **);
+int c_unalias(const char **);
+int c_let(const char **);
+int c_jobs(const char **);
+#ifndef MKSH_UNEMPLOYED
+int c_fgbg(const char **);
+#endif
+int c_kill(const char **);
+void getopts_reset(int);
+int c_getopts(const char **);
+#ifndef MKSH_NO_CMDLINE_EDITING
+int c_bind(const char **);
+#endif
+int c_shift(const char **);
+int c_umask(const char **);
+int c_dot(const char **);
+int c_wait(const char **);
+int c_read(const char **);
+int c_eval(const char **);
+int c_trap(const char **);
+int c_brkcont(const char **);
+int c_exitreturn(const char **);
+int c_set(const char **);
+int c_unset(const char **);
+int c_ulimit(const char **);
+int c_times(const char **);
+int timex(struct op *, int, volatile int *);
+void timex_hook(struct op *, char ** volatile *);
+int c_exec(const char **);
+/* dummy function (just need pointer value), special case in comexec() */
+#define c_builtin shcomexec
+int c_test(const char **);
+#if HAVE_MKNOD
+int c_mknod(const char **);
+#endif
+int c_realpath(const char **);
+int c_rename(const char **);
+int c_cat(const char **);
+int c_sleep(const char **);
+/* histrap.c */
+void init_histvec(void);
+void hist_init(Source *);
+#if HAVE_PERSISTENT_HISTORY
+void hist_finish(void);
+#endif
+void histsave(int *, const char *, bool, bool);
+#if !defined(MKSH_SMALL) && HAVE_PERSISTENT_HISTORY
+bool histsync(void);
+#endif
+int c_fc(const char **);
+void sethistsize(mksh_ari_t);
+#if HAVE_PERSISTENT_HISTORY
+void sethistfile(const char *);
+#endif
+#if !defined(MKSH_NO_CMDLINE_EDITING) && !MKSH_S_NOVI
+char **histpos(void) MKSH_A_PURE;
+int histnum(int);
+#endif
+int findhist(int, int, const char *, bool) MKSH_A_PURE;
+char **hist_get_newest(bool);
+void inittraps(void);
+void alarm_init(void);
+Trap *gettrap(const char *, bool);
+void trapsig(int);
+void intrcheck(void);
+int fatal_trap_check(void);
+int trap_pending(void);
+void runtraps(int intr);
+void runtrap(Trap *, bool);
+void cleartraps(void);
+void restoresigs(void);
+void settrap(Trap *, const char *);
+int block_pipe(void);
+void restore_pipe(int);
+int setsig(Trap *, sig_t, int);
+void setexecsig(Trap *, int);
+#if HAVE_FLOCK || HAVE_LOCK_FCNTL
+void mksh_lockfd(int);
+void mksh_unlkfd(int);
+#endif
+/* jobs.c */
+void j_init(void);
+void j_exit(void);
+#ifndef MKSH_UNEMPLOYED
+void j_change(void);
+#endif
+int exchild(struct op *, int, volatile int *, int);
+void startlast(void);
+int waitlast(void);
+int waitfor(const char *, int *);
+int j_kill(const char *, int);
+#ifndef MKSH_UNEMPLOYED
+int j_resume(const char *, int);
+#endif
+#if !defined(MKSH_UNEMPLOYED) && HAVE_GETSID
+void j_suspend(void);
+#endif
+int j_jobs(const char *, int, int);
+void j_notify(void);
+pid_t j_async(void);
+int j_stopped_running(void);
+/* lex.c */
+int yylex(int);
+void yyskiputf8bom(void);
+void yyerror(const char *, ...)
+    MKSH_A_NORETURN
+    MKSH_A_FORMAT(__printf__, 1, 2);
+Source *pushs(int, Area *);
+void set_prompt(int, Source *);
+int pprompt(const char *, int);
+/* main.c */
+int include(const char *, int, const char **, bool);
+int command(const char *, int);
+int shell(Source * volatile, volatile bool);
+/* argument MUST NOT be 0 */
+void unwind(int) MKSH_A_NORETURN;
+void newenv(int);
+void quitenv(struct shf *);
+void cleanup_parents_env(void);
+void cleanup_proc_env(void);
+void errorf(const char *, ...)
+    MKSH_A_NORETURN
+    MKSH_A_FORMAT(__printf__, 1, 2);
+void errorfx(int, const char *, ...)
+    MKSH_A_NORETURN
+    MKSH_A_FORMAT(__printf__, 2, 3);
+void warningf(bool, const char *, ...)
+    MKSH_A_FORMAT(__printf__, 2, 3);
+void bi_errorf(const char *, ...)
+    MKSH_A_FORMAT(__printf__, 1, 2);
+#define errorfz()	errorf(NULL)
+#define errorfxz(rc)	errorfx((rc), NULL)
+#define bi_errorfz()	bi_errorf(NULL)
+void internal_errorf(const char *, ...)
+    MKSH_A_NORETURN
+    MKSH_A_FORMAT(__printf__, 1, 2);
+void internal_warningf(const char *, ...)
+    MKSH_A_FORMAT(__printf__, 1, 2);
+void error_prefix(bool);
+void shellf(const char *, ...)
+    MKSH_A_FORMAT(__printf__, 1, 2);
+void shprintf(const char *, ...)
+    MKSH_A_FORMAT(__printf__, 1, 2);
+int can_seek(int);
+void initio(void);
+int ksh_dup2(int, int, bool);
+short savefd(int);
+void restfd(int, int);
+void openpipe(int *);
+void closepipe(int *);
+int check_fd(const char *, int, const char **);
+void coproc_init(void);
+void coproc_read_close(int);
+void coproc_readw_close(int);
+void coproc_write_close(int);
+int coproc_getfd(int, const char **);
+void coproc_cleanup(int);
+struct temp *maketemp(Area *, Temp_type, struct temp **);
+void ktinit(Area *, struct table *, uint8_t);
+struct tbl *ktscan(struct table *, const char *, uint32_t, struct tbl ***);
+/* table, name (key) to search for, hash(n) */
+#define ktsearch(tp, s, h) ktscan((tp), (s), (h), NULL)
+struct tbl *ktenter(struct table *, const char *, uint32_t);
+#define ktdelete(p)	do { p->flag = 0; } while (/* CONSTCOND */ 0)
+void ktwalk(struct tstate *, struct table *);
+struct tbl *ktnext(struct tstate *);
+struct tbl **ktsort(struct table *);
+#ifdef DF
+void DF(const char *, ...)
+    MKSH_A_FORMAT(__printf__, 1, 2);
+#endif
+/* misc.c */
+void setctypes(const char *, int);
+void initctypes(void);
+size_t option(const char *) MKSH_A_PURE;
+char *getoptions(void);
+void change_flag(enum sh_flag, int, bool);
+void change_xtrace(unsigned char, bool);
+int parse_args(const char **, int, bool *);
+int getn(const char *, int *);
+int gmatchx(const char *, const char *, bool);
+int has_globbing(const char *, const char *) MKSH_A_PURE;
+int xstrcmp(const void *, const void *) MKSH_A_PURE;
+void ksh_getopt_reset(Getopt *, int);
+int ksh_getopt(const char **, Getopt *, const char *);
+void print_value_quoted(struct shf *, const char *);
+char *quote_value(const char *);
+void print_columns(struct shf *, unsigned int,
+    char *(*)(char *, size_t, unsigned int, const void *),
+    const void *, size_t, size_t, bool);
+void strip_nuls(char *, int);
+ssize_t blocking_read(int, char *, size_t)
+    MKSH_A_BOUNDED(__buffer__, 2, 3);
+int reset_nonblock(int);
+char *ksh_get_wd(void);
+char *do_realpath(const char *);
+void simplify_path(char *);
+void set_current_wd(const char *);
+int c_cd(const char **);
+#if defined(MKSH_SMALL) && !defined(MKSH_SMALL_BUT_FAST)
+char *strdup_i(const char *, Area *);
+char *strndup_i(const char *, size_t, Area *);
+#endif
+int unbksl(bool, int (*)(void), void (*)(int));
+/* shf.c */
+struct shf *shf_open(const char *, int, int, int);
+struct shf *shf_fdopen(int, int, struct shf *);
+struct shf *shf_reopen(int, int, struct shf *);
+struct shf *shf_sopen(char *, ssize_t, int, struct shf *);
+int shf_close(struct shf *);
+int shf_fdclose(struct shf *);
+char *shf_sclose(struct shf *);
+int shf_flush(struct shf *);
+ssize_t shf_read(char *, ssize_t, struct shf *);
+char *shf_getse(char *, ssize_t, struct shf *);
+int shf_getchar(struct shf *s);
+int shf_ungetc(int, struct shf *);
+#if defined(MKSH_SMALL) && !defined(MKSH_SMALL_BUT_FAST)
+int shf_getc(struct shf *);
+int shf_putc(int, struct shf *);
+#else
+#define shf_getc shf_getc_i
+#define shf_putc shf_putc_i
+#endif
+int shf_putchar(int, struct shf *);
+ssize_t shf_puts(const char *, struct shf *);
+ssize_t shf_write(const char *, ssize_t, struct shf *);
+ssize_t shf_fprintf(struct shf *, const char *, ...)
+    MKSH_A_FORMAT(__printf__, 2, 3);
+ssize_t shf_snprintf(char *, ssize_t, const char *, ...)
+    MKSH_A_FORMAT(__printf__, 3, 4)
+    MKSH_A_BOUNDED(__string__, 1, 2);
+char *shf_smprintf(const char *, ...)
+    MKSH_A_FORMAT(__printf__, 1, 2);
+ssize_t shf_vfprintf(struct shf *, const char *, va_list)
+    MKSH_A_FORMAT(__printf__, 2, 0);
+/* syn.c */
+void initkeywords(void);
+struct op *compile(Source *, bool);
+bool parse_usec(const char *, struct timeval *);
+char *yyrecursive(int);
+void yyrecursive_pop(bool);
+/* tree.c */
+void fptreef(struct shf *, int, const char *, ...);
+char *snptreef(char *, ssize_t, const char *, ...);
+struct op *tcopy(struct op *, Area *);
+char *wdcopy(const char *, Area *);
+const char *wdscan(const char *, int);
+#define WDS_TPUTS	BIT(0)		/* tputS (dumpwdvar) mode */
+#define WDS_KEEPQ	BIT(1)		/* keep quote characters */
+#define WDS_MAGIC	BIT(2)		/* make MAGIC */
+char *wdstrip(const char *, int);
+void tfree(struct op *, Area *);
+void dumpchar(struct shf *, int);
+void dumptree(struct shf *, struct op *);
+void dumpwdvar(struct shf *, const char *);
+void dumpioact(struct shf *shf, struct op *t);
+void vistree(char *, size_t, struct op *)
+    MKSH_A_BOUNDED(__string__, 1, 2);
+void fpFUNCTf(struct shf *, int, bool, const char *, struct op *);
+/* var.c */
+void newblock(void);
+void popblock(void);
+void initvar(void);
+struct block *varsearch(struct block *, struct tbl **, const char *, uint32_t);
+struct tbl *global(const char *);
+struct tbl *local(const char *, bool);
+char *str_val(struct tbl *);
+int setstr(struct tbl *, const char *, int);
+struct tbl *setint_v(struct tbl *, struct tbl *, bool);
+void setint(struct tbl *, mksh_ari_t);
+void setint_n(struct tbl *, mksh_ari_t, int);
+struct tbl *typeset(const char *, uint32_t, uint32_t, int, int);
+void unset(struct tbl *, int);
+const char *skip_varname(const char *, bool) MKSH_A_PURE;
+const char *skip_wdvarname(const char *, bool) MKSH_A_PURE;
+int is_wdvarname(const char *, bool) MKSH_A_PURE;
+int is_wdvarassign(const char *) MKSH_A_PURE;
+struct tbl *arraysearch(struct tbl *, uint32_t);
+char **makenv(void);
+void change_winsz(void);
+size_t array_ref_len(const char *) MKSH_A_PURE;
+char *arrayname(const char *);
+mksh_uari_t set_array(const char *, bool, const char **);
+uint32_t hash(const void *) MKSH_A_PURE;
+uint32_t chvt_rndsetup(const void *, size_t) MKSH_A_PURE;
+mksh_ari_t rndget(void);
+void rndset(unsigned long);
+void rndpush(const void *);
+
+enum Test_op {
+	/* non-operator */
+	TO_NONOP = 0,
+	/* unary operators */
+	TO_STNZE, TO_STZER, TO_OPTION,
+	TO_FILAXST,
+	TO_FILEXST,
+	TO_FILREG, TO_FILBDEV, TO_FILCDEV, TO_FILSYM, TO_FILFIFO, TO_FILSOCK,
+	TO_FILCDF, TO_FILID, TO_FILGID, TO_FILSETG, TO_FILSTCK, TO_FILUID,
+	TO_FILRD, TO_FILGZ, TO_FILTT, TO_FILSETU, TO_FILWR, TO_FILEX,
+	/* binary operators */
+	TO_STEQL, TO_STNEQ, TO_STLT, TO_STGT, TO_INTEQ, TO_INTNE, TO_INTGT,
+	TO_INTGE, TO_INTLT, TO_INTLE, TO_FILEQ, TO_FILNT, TO_FILOT,
+	/* not an operator */
+	TO_NONNULL	/* !TO_NONOP */
+};
+typedef enum Test_op Test_op;
+
+/* Used by Test_env.isa() (order important - used to index *_tokens[] arrays) */
+enum Test_meta {
+	TM_OR,		/* -o or || */
+	TM_AND,		/* -a or && */
+	TM_NOT,		/* ! */
+	TM_OPAREN,	/* ( */
+	TM_CPAREN,	/* ) */
+	TM_UNOP,	/* unary operator */
+	TM_BINOP,	/* binary operator */
+	TM_END		/* end of input */
+};
+typedef enum Test_meta Test_meta;
+
+#define TEF_ERROR	BIT(0)		/* set if we've hit an error */
+#define TEF_DBRACKET	BIT(1)		/* set if [[ .. ]] test */
+
+typedef struct test_env {
+	union {
+		const char **wp;	/* used by ptest_* */
+		XPtrV *av;		/* used by dbtestp_* */
+	} pos;
+	const char **wp_end;		/* used by ptest_* */
+	Test_op (*isa)(struct test_env *, Test_meta);
+	const char *(*getopnd) (struct test_env *, Test_op, bool);
+	int (*eval)(struct test_env *, Test_op, const char *, const char *, bool);
+	void (*error)(struct test_env *, int, const char *);
+	int flags;			/* TEF_* */
+} Test_env;
+
+extern const char * const dbtest_tokens[];
+
+Test_op	test_isop(Test_meta, const char *) MKSH_A_PURE;
+int test_eval(Test_env *, Test_op, const char *, const char *, bool);
+int test_parse(Test_env *);
+
+/* tty_fd is not opened O_BINARY, it's thus never read/written */
+EXTERN int tty_fd E_INIT(-1);	/* dup'd tty file descriptor */
+EXTERN bool tty_devtty;		/* true if tty_fd is from /dev/tty */
+EXTERN mksh_ttyst tty_state;	/* saved tty state */
+EXTERN bool tty_hasstate;	/* true if tty_state is valid */
+
+extern int tty_init_fd(void);	/* initialise tty_fd, tty_devtty */
+
+/* be sure not to interfere with anyone else's idea about EXTERN */
+#ifdef EXTERN_DEFINED
+# undef EXTERN_DEFINED
+# undef EXTERN
+#endif
+#undef E_INIT
+
+#endif /* !MKSH_INCLUDES_ONLY */

Copied: vendor/MirOS/mksh/R50/sh_flags.opt (from rev 6707, vendor/MirOS/mksh/dist/sh_flags.opt)
===================================================================
--- vendor/MirOS/mksh/R50/sh_flags.opt	                        (rev 0)
+++ vendor/MirOS/mksh/R50/sh_flags.opt	2014-07-01 12:01:17 UTC (rev 6708)
@@ -0,0 +1,179 @@
+ at SHFLAGS_DEFNS
+__RCSID("$MirOS: src/bin/mksh/sh_flags.opt,v 1.2 2014/06/09 12:28:19 tg Exp $");
+#define FN(sname,cname,flags,ochar)			static const struct {					/* character flag (if any) */			char c;						/* OF_* */					unsigned char optflags;				/* long name of option */			char name[sizeof(sname)];		} shoptione_ ## cname = {				ochar, flags, sname			};
+ at SHFLAGS_ENUMS
+#define FN(sname,cname,flags,ochar)	cname,
+#define F0(sname,cname,flags,ochar)	cname = 0,
+ at SHFLAGS_ITEMS
+#define FN(sname,cname,flags,ochar)		((const char *)(&shoptione_ ## cname)) + 2,
+@@
+
+/* special cases */
+
+<o:|!SHFLAGS_NOT_CMD
+<T:|!SHFLAGS_NOT_CMD
+<A:|!SHFLAGS_NOT_SET
+<o;|!SHFLAGS_NOT_SET
+<s|!SHFLAGS_NOT_SET
+
+/*
+ * options are sorted by their longnames
+ */
+
+/* -a	all new parameters are created with the export attribute */
+>a|
+F0("allexport", FEXPORT, OF_ANY
+
+/* ./.	bgnice */
+>| HAVE_NICE
+FN("bgnice", FBGNICE, OF_ANY
+
+/* ./.	enable {} globbing (non-standard) */
+>|
+FN("braceexpand", FBRACEEXPAND, OF_ANY
+
+/* ./.	Emacs command line editing mode */
+>|!defined(MKSH_NO_CMDLINE_EDITING) || defined(MKSH_LEGACY_MODE)
+FN("emacs", FEMACS, OF_ANY
+
+/* -e	quit on error */
+>e|
+FN("errexit", FERREXIT, OF_ANY
+
+/* ./.	Emacs command line editing mode, gmacs variant */
+>|!defined(MKSH_NO_CMDLINE_EDITING) || defined(MKSH_LEGACY_MODE)
+FN("gmacs", FGMACS, OF_ANY
+
+/* ./.	reading EOF does not exit */
+>|
+FN("ignoreeof", FIGNOREEOF, OF_ANY
+
+/* ./.	inherit -x flag */
+>|
+FN("inherit-xtrace", FXTRACEREC, OF_ANY
+
+/* -i	interactive shell */
+>i|!SHFLAGS_NOT_CMD
+FN("interactive", FTALKING, OF_CMDLINE
+
+/* -k	name=value are recognised anywhere */
+>k|
+FN("keyword", FKEYWORD, OF_ANY
+
+/* -l	login shell */
+>l|!SHFLAGS_NOT_CMD
+FN("login", FLOGIN, OF_CMDLINE
+
+/* -X	mark dirs with / in file name completion */
+>X|
+FN("markdirs", FMARKDIRS, OF_ANY
+
+/* -m	job control monitoring */
+>m|!MKSH_UNEMPLOYED
+FN("monitor", FMONITOR, OF_ANY
+
+/* -C	don't overwrite existing files */
+>C|
+FN("noclobber", FNOCLOBBER, OF_ANY
+
+/* -n	don't execute any commands */
+>n|
+FN("noexec", FNOEXEC, OF_ANY
+
+/* -f	don't do file globbing */
+>f|
+FN("noglob", FNOGLOB, OF_ANY
+
+/* ./.	don't kill running jobs when login shell exits */
+>|
+FN("nohup", FNOHUP, OF_ANY
+
+/* ./.	don't save functions in history (no effect) */
+>|
+FN("nolog", FNOLOG, OF_ANY
+
+/* -b	asynchronous job completion notification */
+>b|!MKSH_UNEMPLOYED
+FN("notify", FNOTIFY, OF_ANY
+
+/* -u	using an unset variable is an error */
+>u|
+FN("nounset", FNOUNSET, OF_ANY
+
+/* ./.	don't do logical cds/pwds (non-standard) */
+>|
+FN("physical", FPHYSICAL, OF_ANY
+
+/* ./.	errorlevel of a pipeline is the rightmost nonzero value */
+>|
+FN("pipefail", FPIPEFAIL, OF_ANY
+
+/* ./.	adhere more closely to POSIX even when undesirable */
+>|
+FN("posix", FPOSIX, OF_ANY
+
+/* -p	privileged shell (suid) */
+>p|
+FN("privileged", FPRIVILEGED, OF_ANY
+
+/* -r	restricted shell */
+>r|!SHFLAGS_NOT_CMD
+FN("restricted", FRESTRICTED, OF_CMDLINE
+
+/* ./.	kludge mode for better compat with traditional sh (OS-specific) */
+>|
+FN("sh", FSH, OF_ANY
+
+/* -s	(invocation) parse stdin (pseudo non-standard) */
+>s|!SHFLAGS_NOT_CMD
+FN("stdin", FSTDIN, OF_CMDLINE
+
+/* -h	create tracked aliases for all commands */
+>h|
+FN("trackall", FTRACKALL, OF_ANY
+
+/* -U	enable UTF-8 processing (non-standard) */
+>U|
+FN("utf8-mode", FUNICODE, OF_ANY
+
+/* -v	echo input */
+>v|
+FN("verbose", FVERBOSE, OF_ANY
+
+/* ./.	Vi command line editing mode */
+>|!defined(MKSH_NO_CMDLINE_EDITING) || defined(MKSH_LEGACY_MODE)
+FN("vi", FVI, OF_ANY
+
+/* ./.	enable ESC as file name completion character (non-standard) */
+>|!defined(MKSH_NO_CMDLINE_EDITING) || defined(MKSH_LEGACY_MODE)
+FN("vi-esccomplete", FVIESCCOMPLETE, OF_ANY
+
+/* ./.	enable Tab as file name completion character (non-standard) */
+>|!defined(MKSH_NO_CMDLINE_EDITING) || defined(MKSH_LEGACY_MODE)
+FN("vi-tabcomplete", FVITABCOMPLETE, OF_ANY
+
+/* ./.	always read in raw mode (no effect) */
+>|!defined(MKSH_NO_CMDLINE_EDITING) || defined(MKSH_LEGACY_MODE)
+FN("viraw", FVIRAW, OF_ANY
+
+/* -x	execution trace (display commands as they are run) */
+>x|
+FN("xtrace", FXTRACE, OF_ANY
+
+/* -c	(invocation) execute specified command */
+>c|!SHFLAGS_NOT_CMD
+FN("", FCOMMAND, OF_CMDLINE
+
+/*
+ * anonymous flags: used internally by shell only (not visible to user
+ */
+
+/* ./.	direct builtin call (divined from argv[0] multi-call binary) */
+>|
+FN("", FAS_BUILTIN, OF_INTERNAL
+
+/* ./.	(internal) initial shell was interactive */
+>|
+FN("", FTALKING_I, OF_INTERNAL
+
+|SHFLAGS_OPTCS

Deleted: vendor/MirOS/mksh/R50/shf.c
===================================================================
--- vendor/MirOS/mksh/dist/shf.c	2014-06-30 23:58:12 UTC (rev 6706)
+++ vendor/MirOS/mksh/R50/shf.c	2014-07-01 12:01:17 UTC (rev 6708)
@@ -1,1165 +0,0 @@
-/*	$OpenBSD: shf.c,v 1.16 2013/04/19 17:36:09 millert Exp $	*/
-
-/*-
- * Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2011,
- *		 2012, 2013
- *	Thorsten Glaser <tg at mirbsd.org>
- *
- * Provided that these terms and disclaimer and all copyright notices
- * are retained or reproduced in an accompanying document, permission
- * is granted to deal in this work without restriction, including un-
- * limited rights to use, publicly perform, distribute, sell, modify,
- * merge, give away, or sublicence.
- *
- * This work is provided "AS IS" and WITHOUT WARRANTY of any kind, to
- * the utmost extent permitted by applicable law, neither express nor
- * implied; without malicious intent or gross negligence. In no event
- * may a licensor, author or contributor be held liable for indirect,
- * direct, other damage, loss, or other issues arising in any way out
- * of dealing in the work, even if advised of the possibility of such
- * damage or existence of a defect, except proven that it results out
- * of said person's immediate fault when using the work as intended.
- *-
- * Use %zX instead of %p and floating point isn't supported at all.
- */
-
-#include "sh.h"
-
-__RCSID("$MirOS: src/bin/mksh/shf.c,v 1.61 2013/07/21 18:36:03 tg Exp $");
-
-/* flags to shf_emptybuf() */
-#define EB_READSW	0x01	/* about to switch to reading */
-#define EB_GROW		0x02	/* grow buffer if necessary (STRING+DYNAMIC) */
-
-/*
- * Replacement stdio routines. Stdio is too flakey on too many machines
- * to be useful when you have multiple processes using the same underlying
- * file descriptors.
- */
-
-static int shf_fillbuf(struct shf *);
-static int shf_emptybuf(struct shf *, int);
-
-/*
- * Open a file. First three args are for open(), last arg is flags for
- * this package. Returns NULL if file could not be opened, or if a dup
- * fails.
- */
-struct shf *
-shf_open(const char *name, int oflags, int mode, int sflags)
-{
-	struct shf *shf;
-	ssize_t bsize =
-	    /* at most 512 */
-	    sflags & SHF_UNBUF ? (sflags & SHF_RD ? 1 : 0) : SHF_BSIZE;
-	int fd, eno;
-
-	/* Done before open so if alloca fails, fd won't be lost. */
-	shf = alloc(sizeof(struct shf) + bsize, ATEMP);
-	shf->areap = ATEMP;
-	shf->buf = (unsigned char *)&shf[1];
-	shf->bsize = bsize;
-	shf->flags = SHF_ALLOCS;
-	/* Rest filled in by reopen. */
-
-	fd = open(name, oflags, mode);
-	if (fd < 0) {
-		eno = errno;
-		afree(shf, shf->areap);
-		errno = eno;
-		return (NULL);
-	}
-	if ((sflags & SHF_MAPHI) && fd < FDBASE) {
-		int nfd;
-
-		nfd = fcntl(fd, F_DUPFD, FDBASE);
-		eno = errno;
-		close(fd);
-		if (nfd < 0) {
-			afree(shf, shf->areap);
-			errno = eno;
-			return (NULL);
-		}
-		fd = nfd;
-	}
-	sflags &= ~SHF_ACCMODE;
-	sflags |= (oflags & O_ACCMODE) == O_RDONLY ? SHF_RD :
-	    ((oflags & O_ACCMODE) == O_WRONLY ? SHF_WR : SHF_RDWR);
-
-	return (shf_reopen(fd, sflags, shf));
-}
-
-/* helper function for shf_fdopen and shf_reopen */
-static void
-shf_open_hlp(int fd, int *sflagsp, const char *where)
-{
-	int sflags = *sflagsp;
-
-	/* use fcntl() to figure out correct read/write flags */
-	if (sflags & SHF_GETFL) {
-		int flags = fcntl(fd, F_GETFL, 0);
-
-		if (flags < 0)
-			/* will get an error on first read/write */
-			sflags |= SHF_RDWR;
-		else {
-			switch (flags & O_ACCMODE) {
-			case O_RDONLY:
-				sflags |= SHF_RD;
-				break;
-			case O_WRONLY:
-				sflags |= SHF_WR;
-				break;
-			case O_RDWR:
-				sflags |= SHF_RDWR;
-				break;
-			}
-		}
-		*sflagsp = sflags;
-	}
-
-	if (!(sflags & (SHF_RD | SHF_WR)))
-		internal_errorf("%s: %s", where, "missing read/write");
-}
-
-/* Set up the shf structure for a file descriptor. Doesn't fail. */
-struct shf *
-shf_fdopen(int fd, int sflags, struct shf *shf)
-{
-	ssize_t bsize =
-	    /* at most 512 */
-	    sflags & SHF_UNBUF ? (sflags & SHF_RD ? 1 : 0) : SHF_BSIZE;
-
-	shf_open_hlp(fd, &sflags, "shf_fdopen");
-	if (shf) {
-		if (bsize) {
-			shf->buf = alloc(bsize, ATEMP);
-			sflags |= SHF_ALLOCB;
-		} else
-			shf->buf = NULL;
-	} else {
-		shf = alloc(sizeof(struct shf) + bsize, ATEMP);
-		shf->buf = (unsigned char *)&shf[1];
-		sflags |= SHF_ALLOCS;
-	}
-	shf->areap = ATEMP;
-	shf->fd = fd;
-	shf->rp = shf->wp = shf->buf;
-	shf->rnleft = 0;
-	shf->rbsize = bsize;
-	shf->wnleft = 0; /* force call to shf_emptybuf() */
-	shf->wbsize = sflags & SHF_UNBUF ? 0 : bsize;
-	shf->flags = sflags;
-	shf->errnosv = 0;
-	shf->bsize = bsize;
-	if (sflags & SHF_CLEXEC)
-		fcntl(fd, F_SETFD, FD_CLOEXEC);
-	return (shf);
-}
-
-/* Set up an existing shf (and buffer) to use the given fd */
-struct shf *
-shf_reopen(int fd, int sflags, struct shf *shf)
-{
-	ssize_t bsize =
-	    /* at most 512 */
-	    sflags & SHF_UNBUF ? (sflags & SHF_RD ? 1 : 0) : SHF_BSIZE;
-
-	shf_open_hlp(fd, &sflags, "shf_reopen");
-	if (!shf || !shf->buf || shf->bsize < bsize)
-		internal_errorf("%s: %s", "shf_reopen", "bad shf/buf/bsize");
-
-	/* assumes shf->buf and shf->bsize already set up */
-	shf->fd = fd;
-	shf->rp = shf->wp = shf->buf;
-	shf->rnleft = 0;
-	shf->rbsize = bsize;
-	shf->wnleft = 0; /* force call to shf_emptybuf() */
-	shf->wbsize = sflags & SHF_UNBUF ? 0 : bsize;
-	shf->flags = (shf->flags & (SHF_ALLOCS | SHF_ALLOCB)) | sflags;
-	shf->errnosv = 0;
-	if (sflags & SHF_CLEXEC)
-		fcntl(fd, F_SETFD, FD_CLOEXEC);
-	return (shf);
-}
-
-/*
- * Open a string for reading or writing. If reading, bsize is the number
- * of bytes that can be read. If writing, bsize is the maximum number of
- * bytes that can be written. If shf is not NULL, it is filled in and
- * returned, if it is NULL, shf is allocated. If writing and buf is NULL
- * and SHF_DYNAMIC is set, the buffer is allocated (if bsize > 0, it is
- * used for the initial size). Doesn't fail.
- * When writing, a byte is reserved for a trailing NUL - see shf_sclose().
- */
-struct shf *
-shf_sopen(char *buf, ssize_t bsize, int sflags, struct shf *shf)
-{
-	/* can't have a read+write string */
-	if (!(!(sflags & SHF_RD) ^ !(sflags & SHF_WR)))
-		internal_errorf("%s: flags 0x%X", "shf_sopen", sflags);
-
-	if (!shf) {
-		shf = alloc(sizeof(struct shf), ATEMP);
-		sflags |= SHF_ALLOCS;
-	}
-	shf->areap = ATEMP;
-	if (!buf && (sflags & SHF_WR) && (sflags & SHF_DYNAMIC)) {
-		if (bsize <= 0)
-			bsize = 64;
-		sflags |= SHF_ALLOCB;
-		buf = alloc(bsize, shf->areap);
-	}
-	shf->fd = -1;
-	shf->buf = shf->rp = shf->wp = (unsigned char *)buf;
-	shf->rnleft = bsize;
-	shf->rbsize = bsize;
-	shf->wnleft = bsize - 1;	/* space for a '\0' */
-	shf->wbsize = bsize;
-	shf->flags = sflags | SHF_STRING;
-	shf->errnosv = 0;
-	shf->bsize = bsize;
-
-	return (shf);
-}
-
-/* Flush and close file descriptor, free the shf structure */
-int
-shf_close(struct shf *shf)
-{
-	int ret = 0;
-
-	if (shf->fd >= 0) {
-		ret = shf_flush(shf);
-		if (close(shf->fd) < 0)
-			ret = EOF;
-	}
-	if (shf->flags & SHF_ALLOCS)
-		afree(shf, shf->areap);
-	else if (shf->flags & SHF_ALLOCB)
-		afree(shf->buf, shf->areap);
-
-	return (ret);
-}
-
-/* Flush and close file descriptor, don't free file structure */
-int
-shf_fdclose(struct shf *shf)
-{
-	int ret = 0;
-
-	if (shf->fd >= 0) {
-		ret = shf_flush(shf);
-		if (close(shf->fd) < 0)
-			ret = EOF;
-		shf->rnleft = 0;
-		shf->rp = shf->buf;
-		shf->wnleft = 0;
-		shf->fd = -1;
-	}
-
-	return (ret);
-}
-
-/*
- * Close a string - if it was opened for writing, it is NUL terminated;
- * returns a pointer to the string and frees shf if it was allocated
- * (does not free string if it was allocated).
- */
-char *
-shf_sclose(struct shf *shf)
-{
-	unsigned char *s = shf->buf;
-
-	/* NUL terminate */
-	if (shf->flags & SHF_WR) {
-		shf->wnleft++;
-		shf_putc('\0', shf);
-	}
-	if (shf->flags & SHF_ALLOCS)
-		afree(shf, shf->areap);
-	return ((char *)s);
-}
-
-/*
- * Un-read what has been read but not examined, or write what has been
- * buffered. Returns 0 for success, EOF for (write) error.
- */
-int
-shf_flush(struct shf *shf)
-{
-	if (shf->flags & SHF_STRING)
-		return ((shf->flags & SHF_WR) ? EOF : 0);
-
-	if (shf->fd < 0)
-		internal_errorf("%s: %s", "shf_flush", "no fd");
-
-	if (shf->flags & SHF_ERROR) {
-		errno = shf->errnosv;
-		return (EOF);
-	}
-
-	if (shf->flags & SHF_READING) {
-		shf->flags &= ~(SHF_EOF | SHF_READING);
-		if (shf->rnleft > 0) {
-			lseek(shf->fd, (off_t)-shf->rnleft, SEEK_CUR);
-			shf->rnleft = 0;
-			shf->rp = shf->buf;
-		}
-		return (0);
-	} else if (shf->flags & SHF_WRITING)
-		return (shf_emptybuf(shf, 0));
-
-	return (0);
-}
-
-/*
- * Write out any buffered data. If currently reading, flushes the read
- * buffer. Returns 0 for success, EOF for (write) error.
- */
-static int
-shf_emptybuf(struct shf *shf, int flags)
-{
-	int ret = 0;
-
-	if (!(shf->flags & SHF_STRING) && shf->fd < 0)
-		internal_errorf("%s: %s", "shf_emptybuf", "no fd");
-
-	if (shf->flags & SHF_ERROR) {
-		errno = shf->errnosv;
-		return (EOF);
-	}
-
-	if (shf->flags & SHF_READING) {
-		if (flags & EB_READSW)
-			/* doesn't happen */
-			return (0);
-		ret = shf_flush(shf);
-		shf->flags &= ~SHF_READING;
-	}
-	if (shf->flags & SHF_STRING) {
-		unsigned char *nbuf;
-
-		/*
-		 * Note that we assume SHF_ALLOCS is not set if
-		 * SHF_ALLOCB is set... (changing the shf pointer could
-		 * cause problems)
-		 */
-		if (!(flags & EB_GROW) || !(shf->flags & SHF_DYNAMIC) ||
-		    !(shf->flags & SHF_ALLOCB))
-			return (EOF);
-		/* allocate more space for buffer */
-		nbuf = aresize2(shf->buf, 2, shf->wbsize, shf->areap);
-		shf->rp = nbuf + (shf->rp - shf->buf);
-		shf->wp = nbuf + (shf->wp - shf->buf);
-		shf->rbsize += shf->wbsize;
-		shf->wnleft += shf->wbsize;
-		shf->wbsize <<= 1;
-		shf->buf = nbuf;
-	} else {
-		if (shf->flags & SHF_WRITING) {
-			ssize_t n, ntowrite = shf->wp - shf->buf;
-			unsigned char *buf = shf->buf;
-
-			while (ntowrite > 0) {
-				n = write(shf->fd, buf, ntowrite);
-				if (n < 0) {
-					if (errno == EINTR &&
-					    !(shf->flags & SHF_INTERRUPT))
-						continue;
-					shf->flags |= SHF_ERROR;
-					shf->errnosv = errno;
-					shf->wnleft = 0;
-					if (buf != shf->buf) {
-						/*
-						 * allow a second flush
-						 * to work
-						 */
-						memmove(shf->buf, buf,
-						    ntowrite);
-						shf->wp = shf->buf + ntowrite;
-					}
-					return (EOF);
-				}
-				buf += n;
-				ntowrite -= n;
-			}
-			if (flags & EB_READSW) {
-				shf->wp = shf->buf;
-				shf->wnleft = 0;
-				shf->flags &= ~SHF_WRITING;
-				return (0);
-			}
-		}
-		shf->wp = shf->buf;
-		shf->wnleft = shf->wbsize;
-	}
-	shf->flags |= SHF_WRITING;
-
-	return (ret);
-}
-
-/* Fill up a read buffer. Returns EOF for a read error, 0 otherwise. */
-static int
-shf_fillbuf(struct shf *shf)
-{
-	ssize_t n;
-
-	if (shf->flags & SHF_STRING)
-		return (0);
-
-	if (shf->fd < 0)
-		internal_errorf("%s: %s", "shf_fillbuf", "no fd");
-
-	if (shf->flags & (SHF_EOF | SHF_ERROR)) {
-		if (shf->flags & SHF_ERROR)
-			errno = shf->errnosv;
-		return (EOF);
-	}
-
-	if ((shf->flags & SHF_WRITING) && shf_emptybuf(shf, EB_READSW) == EOF)
-		return (EOF);
-
-	shf->flags |= SHF_READING;
-
-	shf->rp = shf->buf;
-	while (/* CONSTCOND */ 1) {
-		n = blocking_read(shf->fd, (char *)shf->buf, shf->rbsize);
-		if (n < 0 && errno == EINTR && !(shf->flags & SHF_INTERRUPT))
-			continue;
-		break;
-	}
-	if (n < 0) {
-		shf->flags |= SHF_ERROR;
-		shf->errnosv = errno;
-		shf->rnleft = 0;
-		shf->rp = shf->buf;
-		return (EOF);
-	}
-	if ((shf->rnleft = n) == 0)
-		shf->flags |= SHF_EOF;
-	return (0);
-}
-
-/*
- * Read a buffer from shf. Returns the number of bytes read into buf, if
- * no bytes were read, returns 0 if end of file was seen, EOF if a read
- * error occurred.
- */
-ssize_t
-shf_read(char *buf, ssize_t bsize, struct shf *shf)
-{
-	ssize_t ncopy, orig_bsize = bsize;
-
-	if (!(shf->flags & SHF_RD))
-		internal_errorf("%s: flags 0x%X", "shf_read", shf->flags);
-
-	if (bsize <= 0)
-		internal_errorf("%s: %s %zd", "shf_write", "bsize", bsize);
-
-	while (bsize > 0) {
-		if (shf->rnleft == 0 &&
-		    (shf_fillbuf(shf) == EOF || shf->rnleft == 0))
-			break;
-		ncopy = shf->rnleft;
-		if (ncopy > bsize)
-			ncopy = bsize;
-		memcpy(buf, shf->rp, ncopy);
-		buf += ncopy;
-		bsize -= ncopy;
-		shf->rp += ncopy;
-		shf->rnleft -= ncopy;
-	}
-	/* Note: fread(3S) returns 0 for errors - this doesn't */
-	return (orig_bsize == bsize ? (shf_error(shf) ? EOF : 0) :
-	    orig_bsize - bsize);
-}
-
-/*
- * Read up to a newline or EOF. The newline is put in buf; buf is always
- * NUL terminated. Returns NULL on read error or if nothing was read
- * before end of file, returns a pointer to the NUL byte in buf
- * otherwise.
- */
-char *
-shf_getse(char *buf, ssize_t bsize, struct shf *shf)
-{
-	unsigned char *end;
-	ssize_t ncopy;
-	char *orig_buf = buf;
-
-	if (!(shf->flags & SHF_RD))
-		internal_errorf("%s: flags 0x%X", "shf_getse", shf->flags);
-
-	if (bsize <= 0)
-		return (NULL);
-
-	/* save room for NUL */
-	--bsize;
-	do {
-		if (shf->rnleft == 0) {
-			if (shf_fillbuf(shf) == EOF)
-				return (NULL);
-			if (shf->rnleft == 0) {
-				*buf = '\0';
-				return (buf == orig_buf ? NULL : buf);
-			}
-		}
-		end = (unsigned char *)memchr((char *)shf->rp, '\n',
-		    shf->rnleft);
-		ncopy = end ? end - shf->rp + 1 : shf->rnleft;
-		if (ncopy > bsize)
-			ncopy = bsize;
-		memcpy(buf, (char *) shf->rp, ncopy);
-		shf->rp += ncopy;
-		shf->rnleft -= ncopy;
-		buf += ncopy;
-		bsize -= ncopy;
-	} while (!end && bsize);
-	*buf = '\0';
-	return (buf);
-}
-
-/* Returns the char read. Returns EOF for error and end of file. */
-int
-shf_getchar(struct shf *shf)
-{
-	if (!(shf->flags & SHF_RD))
-		internal_errorf("%s: flags 0x%X", "shf_getchar", shf->flags);
-
-	if (shf->rnleft == 0 && (shf_fillbuf(shf) == EOF || shf->rnleft == 0))
-		return (EOF);
-	--shf->rnleft;
-	return (*shf->rp++);
-}
-
-/*
- * Put a character back in the input stream. Returns the character if
- * successful, EOF if there is no room.
- */
-int
-shf_ungetc(int c, struct shf *shf)
-{
-	if (!(shf->flags & SHF_RD))
-		internal_errorf("%s: flags 0x%X", "shf_ungetc", shf->flags);
-
-	if ((shf->flags & SHF_ERROR) || c == EOF ||
-	    (shf->rp == shf->buf && shf->rnleft))
-		return (EOF);
-
-	if ((shf->flags & SHF_WRITING) && shf_emptybuf(shf, EB_READSW) == EOF)
-		return (EOF);
-
-	if (shf->rp == shf->buf)
-		shf->rp = shf->buf + shf->rbsize;
-	if (shf->flags & SHF_STRING) {
-		/*
-		 * Can unget what was read, but not something different;
-		 * we don't want to modify a string.
-		 */
-		if ((int)(shf->rp[-1]) != c)
-			return (EOF);
-		shf->flags &= ~SHF_EOF;
-		shf->rp--;
-		shf->rnleft++;
-		return (c);
-	}
-	shf->flags &= ~SHF_EOF;
-	*--(shf->rp) = c;
-	shf->rnleft++;
-	return (c);
-}
-
-/*
- * Write a character. Returns the character if successful, EOF if the
- * char could not be written.
- */
-int
-shf_putchar(int c, struct shf *shf)
-{
-	if (!(shf->flags & SHF_WR))
-		internal_errorf("%s: flags 0x%X", "shf_putchar", shf->flags);
-
-	if (c == EOF)
-		return (EOF);
-
-	if (shf->flags & SHF_UNBUF) {
-		unsigned char cc = (unsigned char)c;
-		ssize_t n;
-
-		if (shf->fd < 0)
-			internal_errorf("%s: %s", "shf_putchar", "no fd");
-		if (shf->flags & SHF_ERROR) {
-			errno = shf->errnosv;
-			return (EOF);
-		}
-		while ((n = write(shf->fd, &cc, 1)) != 1)
-			if (n < 0) {
-				if (errno == EINTR &&
-				    !(shf->flags & SHF_INTERRUPT))
-					continue;
-				shf->flags |= SHF_ERROR;
-				shf->errnosv = errno;
-				return (EOF);
-			}
-	} else {
-		/* Flush deals with strings and sticky errors */
-		if (shf->wnleft == 0 && shf_emptybuf(shf, EB_GROW) == EOF)
-			return (EOF);
-		shf->wnleft--;
-		*shf->wp++ = c;
-	}
-
-	return (c);
-}
-
-/*
- * Write a string. Returns the length of the string if successful, EOF
- * if the string could not be written.
- */
-ssize_t
-shf_puts(const char *s, struct shf *shf)
-{
-	if (!s)
-		return (EOF);
-
-	return (shf_write(s, strlen(s), shf));
-}
-
-/* Write a buffer. Returns nbytes if successful, EOF if there is an error. */
-ssize_t
-shf_write(const char *buf, ssize_t nbytes, struct shf *shf)
-{
-	ssize_t n, ncopy, orig_nbytes = nbytes;
-
-	if (!(shf->flags & SHF_WR))
-		internal_errorf("%s: flags 0x%X", "shf_write", shf->flags);
-
-	if (nbytes < 0)
-		internal_errorf("%s: %s %zd", "shf_write", "nbytes", nbytes);
-
-	/* Don't buffer if buffer is empty and we're writting a large amount. */
-	if ((ncopy = shf->wnleft) &&
-	    (shf->wp != shf->buf || nbytes < shf->wnleft)) {
-		if (ncopy > nbytes)
-			ncopy = nbytes;
-		memcpy(shf->wp, buf, ncopy);
-		nbytes -= ncopy;
-		buf += ncopy;
-		shf->wp += ncopy;
-		shf->wnleft -= ncopy;
-	}
-	if (nbytes > 0) {
-		if (shf->flags & SHF_STRING) {
-			/* resize buffer until there's enough space left */
-			while (nbytes > shf->wnleft)
-				if (shf_emptybuf(shf, EB_GROW) == EOF)
-					return (EOF);
-			/* then write everything into the buffer */
-		} else {
-			/* flush deals with sticky errors */
-			if (shf_emptybuf(shf, EB_GROW) == EOF)
-				return (EOF);
-			/* write chunks larger than window size directly */
-			if (nbytes > shf->wbsize) {
-				ncopy = nbytes;
-				if (shf->wbsize)
-					ncopy -= nbytes % shf->wbsize;
-				nbytes -= ncopy;
-				while (ncopy > 0) {
-					n = write(shf->fd, buf, ncopy);
-					if (n < 0) {
-						if (errno == EINTR &&
-						    !(shf->flags & SHF_INTERRUPT))
-							continue;
-						shf->flags |= SHF_ERROR;
-						shf->errnosv = errno;
-						shf->wnleft = 0;
-						/*
-						 * Note: fwrite(3) returns 0
-						 * for errors - this doesn't
-						 */
-						return (EOF);
-					}
-					buf += n;
-					ncopy -= n;
-				}
-			}
-			/* ... and buffer the rest */
-		}
-		if (nbytes > 0) {
-			/* write remaining bytes to buffer */
-			memcpy(shf->wp, buf, nbytes);
-			shf->wp += nbytes;
-			shf->wnleft -= nbytes;
-		}
-	}
-
-	return (orig_nbytes);
-}
-
-ssize_t
-shf_fprintf(struct shf *shf, const char *fmt, ...)
-{
-	va_list args;
-	ssize_t n;
-
-	va_start(args, fmt);
-	n = shf_vfprintf(shf, fmt, args);
-	va_end(args);
-
-	return (n);
-}
-
-ssize_t
-shf_snprintf(char *buf, ssize_t bsize, const char *fmt, ...)
-{
-	struct shf shf;
-	va_list args;
-	ssize_t n;
-
-	if (!buf || bsize <= 0)
-		internal_errorf("shf_snprintf: buf %zX, bsize %zd",
-		    (size_t)buf, bsize);
-
-	shf_sopen(buf, bsize, SHF_WR, &shf);
-	va_start(args, fmt);
-	n = shf_vfprintf(&shf, fmt, args);
-	va_end(args);
-	/* NUL terminates */
-	shf_sclose(&shf);
-	return (n);
-}
-
-char *
-shf_smprintf(const char *fmt, ...)
-{
-	struct shf shf;
-	va_list args;
-
-	shf_sopen(NULL, 0, SHF_WR|SHF_DYNAMIC, &shf);
-	va_start(args, fmt);
-	shf_vfprintf(&shf, fmt, args);
-	va_end(args);
-	/* NUL terminates */
-	return (shf_sclose(&shf));
-}
-
-#define	FL_HASH		0x001	/* '#' seen */
-#define FL_PLUS		0x002	/* '+' seen */
-#define FL_RIGHT	0x004	/* '-' seen */
-#define FL_BLANK	0x008	/* ' ' seen */
-#define FL_SHORT	0x010	/* 'h' seen */
-#define FL_LONG		0x020	/* 'l' seen */
-#define FL_ZERO		0x040	/* '0' seen */
-#define FL_DOT		0x080	/* '.' seen */
-#define FL_UPPER	0x100	/* format character was uppercase */
-#define FL_NUMBER	0x200	/* a number was formated %[douxefg] */
-#define FL_SIZET	0x400	/* 'z' seen */
-#define FM_SIZES	0x430	/* h/l/z mask */
-
-ssize_t
-shf_vfprintf(struct shf *shf, const char *fmt, va_list args)
-{
-	const char *s;
-	char c, *cp;
-	int tmp = 0, flags;
-	ssize_t field, precision, len;
-	unsigned long lnum;
-	/* %#o produces the longest output */
-	char numbuf[(8 * sizeof(long) + 2) / 3 + 1
-#ifdef DEBUG
-		/* a NUL for LLVM/Clang scan-build */
-		+ 1
-#endif
-	    ];
-	/* this stuff for dealing with the buffer */
-	ssize_t nwritten = 0;
-
-#define VA(type) va_arg(args, type)
-
-	if (!fmt)
-		return (0);
-
-	while ((c = *fmt++)) {
-		if (c != '%') {
-			shf_putc(c, shf);
-			nwritten++;
-			continue;
-		}
-		/*
-		 * This will accept flags/fields in any order - not just
-		 * the order specified in printf(3), but this is the way
-		 * _doprnt() seems to work (on BSD and SYSV). The only
-		 * restriction is that the format character must come
-		 * last :-).
-		 */
-		flags = 0;
-		field = precision = 0;
-		for ( ; (c = *fmt++) ; ) {
-			switch (c) {
-			case '#':
-				flags |= FL_HASH;
-				continue;
-
-			case '+':
-				flags |= FL_PLUS;
-				continue;
-
-			case '-':
-				flags |= FL_RIGHT;
-				continue;
-
-			case ' ':
-				flags |= FL_BLANK;
-				continue;
-
-			case '0':
-				if (!(flags & FL_DOT))
-					flags |= FL_ZERO;
-				continue;
-
-			case '.':
-				flags |= FL_DOT;
-				precision = 0;
-				continue;
-
-			case '*':
-				tmp = VA(int);
-				if (flags & FL_DOT)
-					precision = tmp;
-				else if ((field = tmp) < 0) {
-					field = -field;
-					flags |= FL_RIGHT;
-				}
-				continue;
-
-			case 'l':
-				flags &= ~FM_SIZES;
-				flags |= FL_LONG;
-				continue;
-
-			case 'h':
-				flags &= ~FM_SIZES;
-				flags |= FL_SHORT;
-				continue;
-
-			case 'z':
-				flags &= ~FM_SIZES;
-				flags |= FL_SIZET;
-				continue;
-			}
-			if (ksh_isdigit(c)) {
-				bool overflowed = false;
-
-				tmp = c - '0';
-				while (c = *fmt++, ksh_isdigit(c)) {
-					if (notok2mul(2147483647, tmp, 10))
-						overflowed = true;
-					tmp = tmp * 10 + c - '0';
-				}
-				--fmt;
-				if (overflowed)
-					tmp = 0;
-				if (flags & FL_DOT)
-					precision = tmp;
-				else
-					field = tmp;
-				continue;
-			}
-			break;
-		}
-
-		if (precision < 0)
-			precision = 0;
-
-		if (!c)
-			/* nasty format */
-			break;
-
-		if (c >= 'A' && c <= 'Z') {
-			flags |= FL_UPPER;
-			c = ksh_tolower(c);
-		}
-
-		switch (c) {
-		case 'd':
-		case 'i':
-			if (flags & FL_SIZET)
-				lnum = (long)VA(ssize_t);
-			else if (flags & FL_LONG)
-				lnum = VA(long);
-			else if (flags & FL_SHORT)
-				lnum = (long)(short)VA(int);
-			else
-				lnum = (long)VA(int);
-			goto integral;
-
-		case 'o':
-		case 'u':
-		case 'x':
-			if (flags & FL_SIZET)
-				lnum = VA(size_t);
-			else if (flags & FL_LONG)
-				lnum = VA(unsigned long);
-			else if (flags & FL_SHORT)
-				lnum = (unsigned long)(unsigned short)VA(int);
-			else
-				lnum = (unsigned long)VA(unsigned int);
-
- integral:
-			flags |= FL_NUMBER;
-			cp = numbuf + sizeof(numbuf);
-#ifdef DEBUG
-			/*
-			 * this is necessary so Clang 3.2 realises
-			 * utf_skipcols/shf_putc in the output loop
-			 * terminate; these values are always ASCII
-			 * so an out-of-bounds access cannot happen
-			 * but Clang doesn't know that
-			 */
-			*--cp = '\0';
-#endif
-
-			switch (c) {
-			case 'd':
-			case 'i':
-				if (0 > (long)lnum) {
-					lnum = -(long)lnum;
-					tmp = 1;
-				} else
-					tmp = 0;
-				/* FALLTHROUGH */
-			case 'u':
-				do {
-					*--cp = lnum % 10 + '0';
-					lnum /= 10;
-				} while (lnum);
-
-				if (c != 'u') {
-					if (tmp)
-						*--cp = '-';
-					else if (flags & FL_PLUS)
-						*--cp = '+';
-					else if (flags & FL_BLANK)
-						*--cp = ' ';
-				}
-				break;
-
-			case 'o':
-				do {
-					*--cp = (lnum & 0x7) + '0';
-					lnum >>= 3;
-				} while (lnum);
-
-				if ((flags & FL_HASH) && *cp != '0')
-					*--cp = '0';
-				break;
-
-			case 'x': {
-				const char *digits = (flags & FL_UPPER) ?
-				    digits_uc : digits_lc;
-				do {
-					*--cp = digits[lnum & 0xf];
-					lnum >>= 4;
-				} while (lnum);
-
-				if (flags & FL_HASH) {
-					*--cp = (flags & FL_UPPER) ? 'X' : 'x';
-					*--cp = '0';
-				}
-			}
-			}
-			len = numbuf + sizeof(numbuf) - (s = cp);
-#ifdef DEBUG
-			/* see above comment for Clang 3.2 */
-			--len;
-#endif
-			if (flags & FL_DOT) {
-				if (precision > len) {
-					field = precision;
-					flags |= FL_ZERO;
-				} else
-					/* no loss */
-					precision = len;
-			}
-			break;
-
-		case 's':
-			if ((s = VA(const char *)) == NULL)
-				s = "(null)";
-			else if (flags & FL_HASH) {
-				print_value_quoted(shf, s);
-				continue;
-			}
-			len = utf_mbswidth(s);
-			break;
-
-		case 'c':
-			flags &= ~FL_DOT;
-			c = (char)(VA(int));
-			/* FALLTHROUGH */
-
-		case '%':
-		default:
-			numbuf[0] = c;
-			numbuf[1] = 0;
-			s = numbuf;
-			len = 1;
-			break;
-		}
-
-		/*
-		 * At this point s should point to a string that is to be
-		 * formatted, and len should be the length of the string.
-		 */
-		if (!(flags & FL_DOT) || len < precision)
-			precision = len;
-		if (field > precision) {
-			field -= precision;
-			if (!(flags & FL_RIGHT)) {
-				field = -field;
-				/* skip past sign or 0x when padding with 0 */
-				if ((flags & FL_ZERO) && (flags & FL_NUMBER)) {
-					if (*s == '+' || *s == '-' ||
-					    *s == ' ') {
-						shf_putc(*s, shf);
-						s++;
-						precision--;
-						nwritten++;
-					} else if (*s == '0') {
-						shf_putc(*s, shf);
-						s++;
-						nwritten++;
-						if (--precision > 0 &&
-						    (*s | 0x20) == 'x') {
-							shf_putc(*s, shf);
-							s++;
-							precision--;
-							nwritten++;
-						}
-					}
-					c = '0';
-				} else
-					c = flags & FL_ZERO ? '0' : ' ';
-				if (field < 0) {
-					nwritten += -field;
-					for ( ; field < 0 ; field++)
-						shf_putc(c, shf);
-				}
-			} else
-				c = ' ';
-		} else
-			field = 0;
-
-		if (precision > 0) {
-			const char *q;
-
-			nwritten += precision;
-			q = utf_skipcols(s, precision);
-			do {
-				shf_putc(*s, shf);
-			} while (++s < q);
-		}
-		if (field > 0) {
-			nwritten += field;
-			for ( ; field > 0 ; --field)
-				shf_putc(c, shf);
-		}
-	}
-
-	return (shf_error(shf) ? EOF : nwritten);
-}
-
-#if defined(MKSH_SMALL) && !defined(MKSH_SMALL_BUT_FAST)
-int
-shf_getc(struct shf *shf)
-{
-	return (shf_getc_i(shf));
-}
-
-int
-shf_putc(int c, struct shf *shf)
-{
-	return (shf_putc_i(c, shf));
-}
-#endif
-
-#ifdef DEBUG
-const char *
-cstrerror(int errnum)
-{
-#undef strerror
-	return (strerror(errnum));
-#define strerror dontuse_strerror /* poisoned */
-}
-#elif !HAVE_STRERROR
-
-#if HAVE_SYS_ERRLIST
-#if !HAVE_SYS_ERRLIST_DECL
-extern const int sys_nerr;
-extern const char * const sys_errlist[];
-#endif
-#endif
-
-const char *
-cstrerror(int errnum)
-{
-	/* "Unknown error: " + sign + rough estimate + NUL */
-	static char errbuf[15 + 1 + (8 * sizeof(int) + 2) / 3 + 1];
-
-#if HAVE_SYS_ERRLIST
-	if (errnum > 0 && errnum < sys_nerr && sys_errlist[errnum])
-		return (sys_errlist[errnum]);
-#endif
-
-	switch (errnum) {
-	case 0:
-		return ("Undefined error: 0");
-#ifdef EPERM
-	case EPERM:
-		return ("Operation not permitted");
-#endif
-#ifdef ENOENT
-	case ENOENT:
-		return ("No such file or directory");
-#endif
-#ifdef ESRCH
-	case ESRCH:
-		return ("No such process");
-#endif
-#ifdef E2BIG
-	case E2BIG:
-		return ("Argument list too long");
-#endif
-#ifdef ENOEXEC
-	case ENOEXEC:
-		return ("Exec format error");
-#endif
-#ifdef ENOMEM
-	case ENOMEM:
-		return ("Cannot allocate memory");
-#endif
-#ifdef EACCES
-	case EACCES:
-		return ("Permission denied");
-#endif
-#ifdef ENOTDIR
-	case ENOTDIR:
-		return ("Not a directory");
-#endif
-#ifdef EINVAL
-	case EINVAL:
-		return ("Invalid argument");
-#endif
-#ifdef ELOOP
-	case ELOOP:
-		return ("Too many levels of symbolic links");
-#endif
-	default:
-		shf_snprintf(errbuf, sizeof(errbuf),
-		    "Unknown error: %d", errnum);
-		return (errbuf);
-	}
-}
-#endif

Copied: vendor/MirOS/mksh/R50/shf.c (from rev 6707, vendor/MirOS/mksh/dist/shf.c)
===================================================================
--- vendor/MirOS/mksh/R50/shf.c	                        (rev 0)
+++ vendor/MirOS/mksh/R50/shf.c	2014-07-01 12:01:17 UTC (rev 6708)
@@ -0,0 +1,1165 @@
+/*	$OpenBSD: shf.c,v 1.16 2013/04/19 17:36:09 millert Exp $	*/
+
+/*-
+ * Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2011,
+ *		 2012, 2013
+ *	Thorsten Glaser <tg at mirbsd.org>
+ *
+ * Provided that these terms and disclaimer and all copyright notices
+ * are retained or reproduced in an accompanying document, permission
+ * is granted to deal in this work without restriction, including un-
+ * limited rights to use, publicly perform, distribute, sell, modify,
+ * merge, give away, or sublicence.
+ *
+ * This work is provided "AS IS" and WITHOUT WARRANTY of any kind, to
+ * the utmost extent permitted by applicable law, neither express nor
+ * implied; without malicious intent or gross negligence. In no event
+ * may a licensor, author or contributor be held liable for indirect,
+ * direct, other damage, loss, or other issues arising in any way out
+ * of dealing in the work, even if advised of the possibility of such
+ * damage or existence of a defect, except proven that it results out
+ * of said person's immediate fault when using the work as intended.
+ *-
+ * Use %zX instead of %p and floating point isn't supported at all.
+ */
+
+#include "sh.h"
+
+__RCSID("$MirOS: src/bin/mksh/shf.c,v 1.62 2013/10/09 11:59:30 tg Exp $");
+
+/* flags to shf_emptybuf() */
+#define EB_READSW	0x01	/* about to switch to reading */
+#define EB_GROW		0x02	/* grow buffer if necessary (STRING+DYNAMIC) */
+
+/*
+ * Replacement stdio routines. Stdio is too flakey on too many machines
+ * to be useful when you have multiple processes using the same underlying
+ * file descriptors.
+ */
+
+static int shf_fillbuf(struct shf *);
+static int shf_emptybuf(struct shf *, int);
+
+/*
+ * Open a file. First three args are for open(), last arg is flags for
+ * this package. Returns NULL if file could not be opened, or if a dup
+ * fails.
+ */
+struct shf *
+shf_open(const char *name, int oflags, int mode, int sflags)
+{
+	struct shf *shf;
+	ssize_t bsize =
+	    /* at most 512 */
+	    sflags & SHF_UNBUF ? (sflags & SHF_RD ? 1 : 0) : SHF_BSIZE;
+	int fd, eno;
+
+	/* Done before open so if alloca fails, fd won't be lost. */
+	shf = alloc(sizeof(struct shf) + bsize, ATEMP);
+	shf->areap = ATEMP;
+	shf->buf = (unsigned char *)&shf[1];
+	shf->bsize = bsize;
+	shf->flags = SHF_ALLOCS;
+	/* Rest filled in by reopen. */
+
+	fd = open(name, oflags | O_BINARY, mode);
+	if (fd < 0) {
+		eno = errno;
+		afree(shf, shf->areap);
+		errno = eno;
+		return (NULL);
+	}
+	if ((sflags & SHF_MAPHI) && fd < FDBASE) {
+		int nfd;
+
+		nfd = fcntl(fd, F_DUPFD, FDBASE);
+		eno = errno;
+		close(fd);
+		if (nfd < 0) {
+			afree(shf, shf->areap);
+			errno = eno;
+			return (NULL);
+		}
+		fd = nfd;
+	}
+	sflags &= ~SHF_ACCMODE;
+	sflags |= (oflags & O_ACCMODE) == O_RDONLY ? SHF_RD :
+	    ((oflags & O_ACCMODE) == O_WRONLY ? SHF_WR : SHF_RDWR);
+
+	return (shf_reopen(fd, sflags, shf));
+}
+
+/* helper function for shf_fdopen and shf_reopen */
+static void
+shf_open_hlp(int fd, int *sflagsp, const char *where)
+{
+	int sflags = *sflagsp;
+
+	/* use fcntl() to figure out correct read/write flags */
+	if (sflags & SHF_GETFL) {
+		int flags = fcntl(fd, F_GETFL, 0);
+
+		if (flags < 0)
+			/* will get an error on first read/write */
+			sflags |= SHF_RDWR;
+		else {
+			switch (flags & O_ACCMODE) {
+			case O_RDONLY:
+				sflags |= SHF_RD;
+				break;
+			case O_WRONLY:
+				sflags |= SHF_WR;
+				break;
+			case O_RDWR:
+				sflags |= SHF_RDWR;
+				break;
+			}
+		}
+		*sflagsp = sflags;
+	}
+
+	if (!(sflags & (SHF_RD | SHF_WR)))
+		internal_errorf("%s: %s", where, "missing read/write");
+}
+
+/* Set up the shf structure for a file descriptor. Doesn't fail. */
+struct shf *
+shf_fdopen(int fd, int sflags, struct shf *shf)
+{
+	ssize_t bsize =
+	    /* at most 512 */
+	    sflags & SHF_UNBUF ? (sflags & SHF_RD ? 1 : 0) : SHF_BSIZE;
+
+	shf_open_hlp(fd, &sflags, "shf_fdopen");
+	if (shf) {
+		if (bsize) {
+			shf->buf = alloc(bsize, ATEMP);
+			sflags |= SHF_ALLOCB;
+		} else
+			shf->buf = NULL;
+	} else {
+		shf = alloc(sizeof(struct shf) + bsize, ATEMP);
+		shf->buf = (unsigned char *)&shf[1];
+		sflags |= SHF_ALLOCS;
+	}
+	shf->areap = ATEMP;
+	shf->fd = fd;
+	shf->rp = shf->wp = shf->buf;
+	shf->rnleft = 0;
+	shf->rbsize = bsize;
+	shf->wnleft = 0; /* force call to shf_emptybuf() */
+	shf->wbsize = sflags & SHF_UNBUF ? 0 : bsize;
+	shf->flags = sflags;
+	shf->errnosv = 0;
+	shf->bsize = bsize;
+	if (sflags & SHF_CLEXEC)
+		fcntl(fd, F_SETFD, FD_CLOEXEC);
+	return (shf);
+}
+
+/* Set up an existing shf (and buffer) to use the given fd */
+struct shf *
+shf_reopen(int fd, int sflags, struct shf *shf)
+{
+	ssize_t bsize =
+	    /* at most 512 */
+	    sflags & SHF_UNBUF ? (sflags & SHF_RD ? 1 : 0) : SHF_BSIZE;
+
+	shf_open_hlp(fd, &sflags, "shf_reopen");
+	if (!shf || !shf->buf || shf->bsize < bsize)
+		internal_errorf("%s: %s", "shf_reopen", "bad shf/buf/bsize");
+
+	/* assumes shf->buf and shf->bsize already set up */
+	shf->fd = fd;
+	shf->rp = shf->wp = shf->buf;
+	shf->rnleft = 0;
+	shf->rbsize = bsize;
+	shf->wnleft = 0; /* force call to shf_emptybuf() */
+	shf->wbsize = sflags & SHF_UNBUF ? 0 : bsize;
+	shf->flags = (shf->flags & (SHF_ALLOCS | SHF_ALLOCB)) | sflags;
+	shf->errnosv = 0;
+	if (sflags & SHF_CLEXEC)
+		fcntl(fd, F_SETFD, FD_CLOEXEC);
+	return (shf);
+}
+
+/*
+ * Open a string for reading or writing. If reading, bsize is the number
+ * of bytes that can be read. If writing, bsize is the maximum number of
+ * bytes that can be written. If shf is not NULL, it is filled in and
+ * returned, if it is NULL, shf is allocated. If writing and buf is NULL
+ * and SHF_DYNAMIC is set, the buffer is allocated (if bsize > 0, it is
+ * used for the initial size). Doesn't fail.
+ * When writing, a byte is reserved for a trailing NUL - see shf_sclose().
+ */
+struct shf *
+shf_sopen(char *buf, ssize_t bsize, int sflags, struct shf *shf)
+{
+	/* can't have a read+write string */
+	if (!(!(sflags & SHF_RD) ^ !(sflags & SHF_WR)))
+		internal_errorf("%s: flags 0x%X", "shf_sopen", sflags);
+
+	if (!shf) {
+		shf = alloc(sizeof(struct shf), ATEMP);
+		sflags |= SHF_ALLOCS;
+	}
+	shf->areap = ATEMP;
+	if (!buf && (sflags & SHF_WR) && (sflags & SHF_DYNAMIC)) {
+		if (bsize <= 0)
+			bsize = 64;
+		sflags |= SHF_ALLOCB;
+		buf = alloc(bsize, shf->areap);
+	}
+	shf->fd = -1;
+	shf->buf = shf->rp = shf->wp = (unsigned char *)buf;
+	shf->rnleft = bsize;
+	shf->rbsize = bsize;
+	shf->wnleft = bsize - 1;	/* space for a '\0' */
+	shf->wbsize = bsize;
+	shf->flags = sflags | SHF_STRING;
+	shf->errnosv = 0;
+	shf->bsize = bsize;
+
+	return (shf);
+}
+
+/* Flush and close file descriptor, free the shf structure */
+int
+shf_close(struct shf *shf)
+{
+	int ret = 0;
+
+	if (shf->fd >= 0) {
+		ret = shf_flush(shf);
+		if (close(shf->fd) < 0)
+			ret = EOF;
+	}
+	if (shf->flags & SHF_ALLOCS)
+		afree(shf, shf->areap);
+	else if (shf->flags & SHF_ALLOCB)
+		afree(shf->buf, shf->areap);
+
+	return (ret);
+}
+
+/* Flush and close file descriptor, don't free file structure */
+int
+shf_fdclose(struct shf *shf)
+{
+	int ret = 0;
+
+	if (shf->fd >= 0) {
+		ret = shf_flush(shf);
+		if (close(shf->fd) < 0)
+			ret = EOF;
+		shf->rnleft = 0;
+		shf->rp = shf->buf;
+		shf->wnleft = 0;
+		shf->fd = -1;
+	}
+
+	return (ret);
+}
+
+/*
+ * Close a string - if it was opened for writing, it is NUL terminated;
+ * returns a pointer to the string and frees shf if it was allocated
+ * (does not free string if it was allocated).
+ */
+char *
+shf_sclose(struct shf *shf)
+{
+	unsigned char *s = shf->buf;
+
+	/* NUL terminate */
+	if (shf->flags & SHF_WR) {
+		shf->wnleft++;
+		shf_putc('\0', shf);
+	}
+	if (shf->flags & SHF_ALLOCS)
+		afree(shf, shf->areap);
+	return ((char *)s);
+}
+
+/*
+ * Un-read what has been read but not examined, or write what has been
+ * buffered. Returns 0 for success, EOF for (write) error.
+ */
+int
+shf_flush(struct shf *shf)
+{
+	if (shf->flags & SHF_STRING)
+		return ((shf->flags & SHF_WR) ? EOF : 0);
+
+	if (shf->fd < 0)
+		internal_errorf("%s: %s", "shf_flush", "no fd");
+
+	if (shf->flags & SHF_ERROR) {
+		errno = shf->errnosv;
+		return (EOF);
+	}
+
+	if (shf->flags & SHF_READING) {
+		shf->flags &= ~(SHF_EOF | SHF_READING);
+		if (shf->rnleft > 0) {
+			lseek(shf->fd, (off_t)-shf->rnleft, SEEK_CUR);
+			shf->rnleft = 0;
+			shf->rp = shf->buf;
+		}
+		return (0);
+	} else if (shf->flags & SHF_WRITING)
+		return (shf_emptybuf(shf, 0));
+
+	return (0);
+}
+
+/*
+ * Write out any buffered data. If currently reading, flushes the read
+ * buffer. Returns 0 for success, EOF for (write) error.
+ */
+static int
+shf_emptybuf(struct shf *shf, int flags)
+{
+	int ret = 0;
+
+	if (!(shf->flags & SHF_STRING) && shf->fd < 0)
+		internal_errorf("%s: %s", "shf_emptybuf", "no fd");
+
+	if (shf->flags & SHF_ERROR) {
+		errno = shf->errnosv;
+		return (EOF);
+	}
+
+	if (shf->flags & SHF_READING) {
+		if (flags & EB_READSW)
+			/* doesn't happen */
+			return (0);
+		ret = shf_flush(shf);
+		shf->flags &= ~SHF_READING;
+	}
+	if (shf->flags & SHF_STRING) {
+		unsigned char *nbuf;
+
+		/*
+		 * Note that we assume SHF_ALLOCS is not set if
+		 * SHF_ALLOCB is set... (changing the shf pointer could
+		 * cause problems)
+		 */
+		if (!(flags & EB_GROW) || !(shf->flags & SHF_DYNAMIC) ||
+		    !(shf->flags & SHF_ALLOCB))
+			return (EOF);
+		/* allocate more space for buffer */
+		nbuf = aresize2(shf->buf, 2, shf->wbsize, shf->areap);
+		shf->rp = nbuf + (shf->rp - shf->buf);
+		shf->wp = nbuf + (shf->wp - shf->buf);
+		shf->rbsize += shf->wbsize;
+		shf->wnleft += shf->wbsize;
+		shf->wbsize <<= 1;
+		shf->buf = nbuf;
+	} else {
+		if (shf->flags & SHF_WRITING) {
+			ssize_t n, ntowrite = shf->wp - shf->buf;
+			unsigned char *buf = shf->buf;
+
+			while (ntowrite > 0) {
+				n = write(shf->fd, buf, ntowrite);
+				if (n < 0) {
+					if (errno == EINTR &&
+					    !(shf->flags & SHF_INTERRUPT))
+						continue;
+					shf->flags |= SHF_ERROR;
+					shf->errnosv = errno;
+					shf->wnleft = 0;
+					if (buf != shf->buf) {
+						/*
+						 * allow a second flush
+						 * to work
+						 */
+						memmove(shf->buf, buf,
+						    ntowrite);
+						shf->wp = shf->buf + ntowrite;
+					}
+					return (EOF);
+				}
+				buf += n;
+				ntowrite -= n;
+			}
+			if (flags & EB_READSW) {
+				shf->wp = shf->buf;
+				shf->wnleft = 0;
+				shf->flags &= ~SHF_WRITING;
+				return (0);
+			}
+		}
+		shf->wp = shf->buf;
+		shf->wnleft = shf->wbsize;
+	}
+	shf->flags |= SHF_WRITING;
+
+	return (ret);
+}
+
+/* Fill up a read buffer. Returns EOF for a read error, 0 otherwise. */
+static int
+shf_fillbuf(struct shf *shf)
+{
+	ssize_t n;
+
+	if (shf->flags & SHF_STRING)
+		return (0);
+
+	if (shf->fd < 0)
+		internal_errorf("%s: %s", "shf_fillbuf", "no fd");
+
+	if (shf->flags & (SHF_EOF | SHF_ERROR)) {
+		if (shf->flags & SHF_ERROR)
+			errno = shf->errnosv;
+		return (EOF);
+	}
+
+	if ((shf->flags & SHF_WRITING) && shf_emptybuf(shf, EB_READSW) == EOF)
+		return (EOF);
+
+	shf->flags |= SHF_READING;
+
+	shf->rp = shf->buf;
+	while (/* CONSTCOND */ 1) {
+		n = blocking_read(shf->fd, (char *)shf->buf, shf->rbsize);
+		if (n < 0 && errno == EINTR && !(shf->flags & SHF_INTERRUPT))
+			continue;
+		break;
+	}
+	if (n < 0) {
+		shf->flags |= SHF_ERROR;
+		shf->errnosv = errno;
+		shf->rnleft = 0;
+		shf->rp = shf->buf;
+		return (EOF);
+	}
+	if ((shf->rnleft = n) == 0)
+		shf->flags |= SHF_EOF;
+	return (0);
+}
+
+/*
+ * Read a buffer from shf. Returns the number of bytes read into buf, if
+ * no bytes were read, returns 0 if end of file was seen, EOF if a read
+ * error occurred.
+ */
+ssize_t
+shf_read(char *buf, ssize_t bsize, struct shf *shf)
+{
+	ssize_t ncopy, orig_bsize = bsize;
+
+	if (!(shf->flags & SHF_RD))
+		internal_errorf("%s: flags 0x%X", "shf_read", shf->flags);
+
+	if (bsize <= 0)
+		internal_errorf("%s: %s %zd", "shf_write", "bsize", bsize);
+
+	while (bsize > 0) {
+		if (shf->rnleft == 0 &&
+		    (shf_fillbuf(shf) == EOF || shf->rnleft == 0))
+			break;
+		ncopy = shf->rnleft;
+		if (ncopy > bsize)
+			ncopy = bsize;
+		memcpy(buf, shf->rp, ncopy);
+		buf += ncopy;
+		bsize -= ncopy;
+		shf->rp += ncopy;
+		shf->rnleft -= ncopy;
+	}
+	/* Note: fread(3S) returns 0 for errors - this doesn't */
+	return (orig_bsize == bsize ? (shf_error(shf) ? EOF : 0) :
+	    orig_bsize - bsize);
+}
+
+/*
+ * Read up to a newline or EOF. The newline is put in buf; buf is always
+ * NUL terminated. Returns NULL on read error or if nothing was read
+ * before end of file, returns a pointer to the NUL byte in buf
+ * otherwise.
+ */
+char *
+shf_getse(char *buf, ssize_t bsize, struct shf *shf)
+{
+	unsigned char *end;
+	ssize_t ncopy;
+	char *orig_buf = buf;
+
+	if (!(shf->flags & SHF_RD))
+		internal_errorf("%s: flags 0x%X", "shf_getse", shf->flags);
+
+	if (bsize <= 0)
+		return (NULL);
+
+	/* save room for NUL */
+	--bsize;
+	do {
+		if (shf->rnleft == 0) {
+			if (shf_fillbuf(shf) == EOF)
+				return (NULL);
+			if (shf->rnleft == 0) {
+				*buf = '\0';
+				return (buf == orig_buf ? NULL : buf);
+			}
+		}
+		end = (unsigned char *)memchr((char *)shf->rp, '\n',
+		    shf->rnleft);
+		ncopy = end ? end - shf->rp + 1 : shf->rnleft;
+		if (ncopy > bsize)
+			ncopy = bsize;
+		memcpy(buf, (char *) shf->rp, ncopy);
+		shf->rp += ncopy;
+		shf->rnleft -= ncopy;
+		buf += ncopy;
+		bsize -= ncopy;
+	} while (!end && bsize);
+	*buf = '\0';
+	return (buf);
+}
+
+/* Returns the char read. Returns EOF for error and end of file. */
+int
+shf_getchar(struct shf *shf)
+{
+	if (!(shf->flags & SHF_RD))
+		internal_errorf("%s: flags 0x%X", "shf_getchar", shf->flags);
+
+	if (shf->rnleft == 0 && (shf_fillbuf(shf) == EOF || shf->rnleft == 0))
+		return (EOF);
+	--shf->rnleft;
+	return (*shf->rp++);
+}
+
+/*
+ * Put a character back in the input stream. Returns the character if
+ * successful, EOF if there is no room.
+ */
+int
+shf_ungetc(int c, struct shf *shf)
+{
+	if (!(shf->flags & SHF_RD))
+		internal_errorf("%s: flags 0x%X", "shf_ungetc", shf->flags);
+
+	if ((shf->flags & SHF_ERROR) || c == EOF ||
+	    (shf->rp == shf->buf && shf->rnleft))
+		return (EOF);
+
+	if ((shf->flags & SHF_WRITING) && shf_emptybuf(shf, EB_READSW) == EOF)
+		return (EOF);
+
+	if (shf->rp == shf->buf)
+		shf->rp = shf->buf + shf->rbsize;
+	if (shf->flags & SHF_STRING) {
+		/*
+		 * Can unget what was read, but not something different;
+		 * we don't want to modify a string.
+		 */
+		if ((int)(shf->rp[-1]) != c)
+			return (EOF);
+		shf->flags &= ~SHF_EOF;
+		shf->rp--;
+		shf->rnleft++;
+		return (c);
+	}
+	shf->flags &= ~SHF_EOF;
+	*--(shf->rp) = c;
+	shf->rnleft++;
+	return (c);
+}
+
+/*
+ * Write a character. Returns the character if successful, EOF if the
+ * char could not be written.
+ */
+int
+shf_putchar(int c, struct shf *shf)
+{
+	if (!(shf->flags & SHF_WR))
+		internal_errorf("%s: flags 0x%X", "shf_putchar", shf->flags);
+
+	if (c == EOF)
+		return (EOF);
+
+	if (shf->flags & SHF_UNBUF) {
+		unsigned char cc = (unsigned char)c;
+		ssize_t n;
+
+		if (shf->fd < 0)
+			internal_errorf("%s: %s", "shf_putchar", "no fd");
+		if (shf->flags & SHF_ERROR) {
+			errno = shf->errnosv;
+			return (EOF);
+		}
+		while ((n = write(shf->fd, &cc, 1)) != 1)
+			if (n < 0) {
+				if (errno == EINTR &&
+				    !(shf->flags & SHF_INTERRUPT))
+					continue;
+				shf->flags |= SHF_ERROR;
+				shf->errnosv = errno;
+				return (EOF);
+			}
+	} else {
+		/* Flush deals with strings and sticky errors */
+		if (shf->wnleft == 0 && shf_emptybuf(shf, EB_GROW) == EOF)
+			return (EOF);
+		shf->wnleft--;
+		*shf->wp++ = c;
+	}
+
+	return (c);
+}
+
+/*
+ * Write a string. Returns the length of the string if successful, EOF
+ * if the string could not be written.
+ */
+ssize_t
+shf_puts(const char *s, struct shf *shf)
+{
+	if (!s)
+		return (EOF);
+
+	return (shf_write(s, strlen(s), shf));
+}
+
+/* Write a buffer. Returns nbytes if successful, EOF if there is an error. */
+ssize_t
+shf_write(const char *buf, ssize_t nbytes, struct shf *shf)
+{
+	ssize_t n, ncopy, orig_nbytes = nbytes;
+
+	if (!(shf->flags & SHF_WR))
+		internal_errorf("%s: flags 0x%X", "shf_write", shf->flags);
+
+	if (nbytes < 0)
+		internal_errorf("%s: %s %zd", "shf_write", "nbytes", nbytes);
+
+	/* Don't buffer if buffer is empty and we're writting a large amount. */
+	if ((ncopy = shf->wnleft) &&
+	    (shf->wp != shf->buf || nbytes < shf->wnleft)) {
+		if (ncopy > nbytes)
+			ncopy = nbytes;
+		memcpy(shf->wp, buf, ncopy);
+		nbytes -= ncopy;
+		buf += ncopy;
+		shf->wp += ncopy;
+		shf->wnleft -= ncopy;
+	}
+	if (nbytes > 0) {
+		if (shf->flags & SHF_STRING) {
+			/* resize buffer until there's enough space left */
+			while (nbytes > shf->wnleft)
+				if (shf_emptybuf(shf, EB_GROW) == EOF)
+					return (EOF);
+			/* then write everything into the buffer */
+		} else {
+			/* flush deals with sticky errors */
+			if (shf_emptybuf(shf, EB_GROW) == EOF)
+				return (EOF);
+			/* write chunks larger than window size directly */
+			if (nbytes > shf->wbsize) {
+				ncopy = nbytes;
+				if (shf->wbsize)
+					ncopy -= nbytes % shf->wbsize;
+				nbytes -= ncopy;
+				while (ncopy > 0) {
+					n = write(shf->fd, buf, ncopy);
+					if (n < 0) {
+						if (errno == EINTR &&
+						    !(shf->flags & SHF_INTERRUPT))
+							continue;
+						shf->flags |= SHF_ERROR;
+						shf->errnosv = errno;
+						shf->wnleft = 0;
+						/*
+						 * Note: fwrite(3) returns 0
+						 * for errors - this doesn't
+						 */
+						return (EOF);
+					}
+					buf += n;
+					ncopy -= n;
+				}
+			}
+			/* ... and buffer the rest */
+		}
+		if (nbytes > 0) {
+			/* write remaining bytes to buffer */
+			memcpy(shf->wp, buf, nbytes);
+			shf->wp += nbytes;
+			shf->wnleft -= nbytes;
+		}
+	}
+
+	return (orig_nbytes);
+}
+
+ssize_t
+shf_fprintf(struct shf *shf, const char *fmt, ...)
+{
+	va_list args;
+	ssize_t n;
+
+	va_start(args, fmt);
+	n = shf_vfprintf(shf, fmt, args);
+	va_end(args);
+
+	return (n);
+}
+
+ssize_t
+shf_snprintf(char *buf, ssize_t bsize, const char *fmt, ...)
+{
+	struct shf shf;
+	va_list args;
+	ssize_t n;
+
+	if (!buf || bsize <= 0)
+		internal_errorf("shf_snprintf: buf %zX, bsize %zd",
+		    (size_t)buf, bsize);
+
+	shf_sopen(buf, bsize, SHF_WR, &shf);
+	va_start(args, fmt);
+	n = shf_vfprintf(&shf, fmt, args);
+	va_end(args);
+	/* NUL terminates */
+	shf_sclose(&shf);
+	return (n);
+}
+
+char *
+shf_smprintf(const char *fmt, ...)
+{
+	struct shf shf;
+	va_list args;
+
+	shf_sopen(NULL, 0, SHF_WR|SHF_DYNAMIC, &shf);
+	va_start(args, fmt);
+	shf_vfprintf(&shf, fmt, args);
+	va_end(args);
+	/* NUL terminates */
+	return (shf_sclose(&shf));
+}
+
+#define	FL_HASH		0x001	/* '#' seen */
+#define FL_PLUS		0x002	/* '+' seen */
+#define FL_RIGHT	0x004	/* '-' seen */
+#define FL_BLANK	0x008	/* ' ' seen */
+#define FL_SHORT	0x010	/* 'h' seen */
+#define FL_LONG		0x020	/* 'l' seen */
+#define FL_ZERO		0x040	/* '0' seen */
+#define FL_DOT		0x080	/* '.' seen */
+#define FL_UPPER	0x100	/* format character was uppercase */
+#define FL_NUMBER	0x200	/* a number was formated %[douxefg] */
+#define FL_SIZET	0x400	/* 'z' seen */
+#define FM_SIZES	0x430	/* h/l/z mask */
+
+ssize_t
+shf_vfprintf(struct shf *shf, const char *fmt, va_list args)
+{
+	const char *s;
+	char c, *cp;
+	int tmp = 0, flags;
+	ssize_t field, precision, len;
+	unsigned long lnum;
+	/* %#o produces the longest output */
+	char numbuf[(8 * sizeof(long) + 2) / 3 + 1
+#ifdef DEBUG
+		/* a NUL for LLVM/Clang scan-build */
+		+ 1
+#endif
+	    ];
+	/* this stuff for dealing with the buffer */
+	ssize_t nwritten = 0;
+
+#define VA(type) va_arg(args, type)
+
+	if (!fmt)
+		return (0);
+
+	while ((c = *fmt++)) {
+		if (c != '%') {
+			shf_putc(c, shf);
+			nwritten++;
+			continue;
+		}
+		/*
+		 * This will accept flags/fields in any order - not just
+		 * the order specified in printf(3), but this is the way
+		 * _doprnt() seems to work (on BSD and SYSV). The only
+		 * restriction is that the format character must come
+		 * last :-).
+		 */
+		flags = 0;
+		field = precision = 0;
+		for ( ; (c = *fmt++) ; ) {
+			switch (c) {
+			case '#':
+				flags |= FL_HASH;
+				continue;
+
+			case '+':
+				flags |= FL_PLUS;
+				continue;
+
+			case '-':
+				flags |= FL_RIGHT;
+				continue;
+
+			case ' ':
+				flags |= FL_BLANK;
+				continue;
+
+			case '0':
+				if (!(flags & FL_DOT))
+					flags |= FL_ZERO;
+				continue;
+
+			case '.':
+				flags |= FL_DOT;
+				precision = 0;
+				continue;
+
+			case '*':
+				tmp = VA(int);
+				if (flags & FL_DOT)
+					precision = tmp;
+				else if ((field = tmp) < 0) {
+					field = -field;
+					flags |= FL_RIGHT;
+				}
+				continue;
+
+			case 'l':
+				flags &= ~FM_SIZES;
+				flags |= FL_LONG;
+				continue;
+
+			case 'h':
+				flags &= ~FM_SIZES;
+				flags |= FL_SHORT;
+				continue;
+
+			case 'z':
+				flags &= ~FM_SIZES;
+				flags |= FL_SIZET;
+				continue;
+			}
+			if (ksh_isdigit(c)) {
+				bool overflowed = false;
+
+				tmp = c - '0';
+				while (c = *fmt++, ksh_isdigit(c)) {
+					if (notok2mul(2147483647, tmp, 10))
+						overflowed = true;
+					tmp = tmp * 10 + c - '0';
+				}
+				--fmt;
+				if (overflowed)
+					tmp = 0;
+				if (flags & FL_DOT)
+					precision = tmp;
+				else
+					field = tmp;
+				continue;
+			}
+			break;
+		}
+
+		if (precision < 0)
+			precision = 0;
+
+		if (!c)
+			/* nasty format */
+			break;
+
+		if (c >= 'A' && c <= 'Z') {
+			flags |= FL_UPPER;
+			c = ksh_tolower(c);
+		}
+
+		switch (c) {
+		case 'd':
+		case 'i':
+			if (flags & FL_SIZET)
+				lnum = (long)VA(ssize_t);
+			else if (flags & FL_LONG)
+				lnum = VA(long);
+			else if (flags & FL_SHORT)
+				lnum = (long)(short)VA(int);
+			else
+				lnum = (long)VA(int);
+			goto integral;
+
+		case 'o':
+		case 'u':
+		case 'x':
+			if (flags & FL_SIZET)
+				lnum = VA(size_t);
+			else if (flags & FL_LONG)
+				lnum = VA(unsigned long);
+			else if (flags & FL_SHORT)
+				lnum = (unsigned long)(unsigned short)VA(int);
+			else
+				lnum = (unsigned long)VA(unsigned int);
+
+ integral:
+			flags |= FL_NUMBER;
+			cp = numbuf + sizeof(numbuf);
+#ifdef DEBUG
+			/*
+			 * this is necessary so Clang 3.2 realises
+			 * utf_skipcols/shf_putc in the output loop
+			 * terminate; these values are always ASCII
+			 * so an out-of-bounds access cannot happen
+			 * but Clang doesn't know that
+			 */
+			*--cp = '\0';
+#endif
+
+			switch (c) {
+			case 'd':
+			case 'i':
+				if (0 > (long)lnum) {
+					lnum = -(long)lnum;
+					tmp = 1;
+				} else
+					tmp = 0;
+				/* FALLTHROUGH */
+			case 'u':
+				do {
+					*--cp = lnum % 10 + '0';
+					lnum /= 10;
+				} while (lnum);
+
+				if (c != 'u') {
+					if (tmp)
+						*--cp = '-';
+					else if (flags & FL_PLUS)
+						*--cp = '+';
+					else if (flags & FL_BLANK)
+						*--cp = ' ';
+				}
+				break;
+
+			case 'o':
+				do {
+					*--cp = (lnum & 0x7) + '0';
+					lnum >>= 3;
+				} while (lnum);
+
+				if ((flags & FL_HASH) && *cp != '0')
+					*--cp = '0';
+				break;
+
+			case 'x': {
+				const char *digits = (flags & FL_UPPER) ?
+				    digits_uc : digits_lc;
+				do {
+					*--cp = digits[lnum & 0xf];
+					lnum >>= 4;
+				} while (lnum);
+
+				if (flags & FL_HASH) {
+					*--cp = (flags & FL_UPPER) ? 'X' : 'x';
+					*--cp = '0';
+				}
+			}
+			}
+			len = numbuf + sizeof(numbuf) - (s = cp);
+#ifdef DEBUG
+			/* see above comment for Clang 3.2 */
+			--len;
+#endif
+			if (flags & FL_DOT) {
+				if (precision > len) {
+					field = precision;
+					flags |= FL_ZERO;
+				} else
+					/* no loss */
+					precision = len;
+			}
+			break;
+
+		case 's':
+			if ((s = VA(const char *)) == NULL)
+				s = "(null)";
+			else if (flags & FL_HASH) {
+				print_value_quoted(shf, s);
+				continue;
+			}
+			len = utf_mbswidth(s);
+			break;
+
+		case 'c':
+			flags &= ~FL_DOT;
+			c = (char)(VA(int));
+			/* FALLTHROUGH */
+
+		case '%':
+		default:
+			numbuf[0] = c;
+			numbuf[1] = 0;
+			s = numbuf;
+			len = 1;
+			break;
+		}
+
+		/*
+		 * At this point s should point to a string that is to be
+		 * formatted, and len should be the length of the string.
+		 */
+		if (!(flags & FL_DOT) || len < precision)
+			precision = len;
+		if (field > precision) {
+			field -= precision;
+			if (!(flags & FL_RIGHT)) {
+				field = -field;
+				/* skip past sign or 0x when padding with 0 */
+				if ((flags & FL_ZERO) && (flags & FL_NUMBER)) {
+					if (*s == '+' || *s == '-' ||
+					    *s == ' ') {
+						shf_putc(*s, shf);
+						s++;
+						precision--;
+						nwritten++;
+					} else if (*s == '0') {
+						shf_putc(*s, shf);
+						s++;
+						nwritten++;
+						if (--precision > 0 &&
+						    (*s | 0x20) == 'x') {
+							shf_putc(*s, shf);
+							s++;
+							precision--;
+							nwritten++;
+						}
+					}
+					c = '0';
+				} else
+					c = flags & FL_ZERO ? '0' : ' ';
+				if (field < 0) {
+					nwritten += -field;
+					for ( ; field < 0 ; field++)
+						shf_putc(c, shf);
+				}
+			} else
+				c = ' ';
+		} else
+			field = 0;
+
+		if (precision > 0) {
+			const char *q;
+
+			nwritten += precision;
+			q = utf_skipcols(s, precision);
+			do {
+				shf_putc(*s, shf);
+			} while (++s < q);
+		}
+		if (field > 0) {
+			nwritten += field;
+			for ( ; field > 0 ; --field)
+				shf_putc(c, shf);
+		}
+	}
+
+	return (shf_error(shf) ? EOF : nwritten);
+}
+
+#if defined(MKSH_SMALL) && !defined(MKSH_SMALL_BUT_FAST)
+int
+shf_getc(struct shf *shf)
+{
+	return (shf_getc_i(shf));
+}
+
+int
+shf_putc(int c, struct shf *shf)
+{
+	return (shf_putc_i(c, shf));
+}
+#endif
+
+#ifdef DEBUG
+const char *
+cstrerror(int errnum)
+{
+#undef strerror
+	return (strerror(errnum));
+#define strerror dontuse_strerror /* poisoned */
+}
+#elif !HAVE_STRERROR
+
+#if HAVE_SYS_ERRLIST
+#if !HAVE_SYS_ERRLIST_DECL
+extern const int sys_nerr;
+extern const char * const sys_errlist[];
+#endif
+#endif
+
+const char *
+cstrerror(int errnum)
+{
+	/* "Unknown error: " + sign + rough estimate + NUL */
+	static char errbuf[15 + 1 + (8 * sizeof(int) + 2) / 3 + 1];
+
+#if HAVE_SYS_ERRLIST
+	if (errnum > 0 && errnum < sys_nerr && sys_errlist[errnum])
+		return (sys_errlist[errnum]);
+#endif
+
+	switch (errnum) {
+	case 0:
+		return ("Undefined error: 0");
+#ifdef EPERM
+	case EPERM:
+		return ("Operation not permitted");
+#endif
+#ifdef ENOENT
+	case ENOENT:
+		return ("No such file or directory");
+#endif
+#ifdef ESRCH
+	case ESRCH:
+		return ("No such process");
+#endif
+#ifdef E2BIG
+	case E2BIG:
+		return ("Argument list too long");
+#endif
+#ifdef ENOEXEC
+	case ENOEXEC:
+		return ("Exec format error");
+#endif
+#ifdef ENOMEM
+	case ENOMEM:
+		return ("Cannot allocate memory");
+#endif
+#ifdef EACCES
+	case EACCES:
+		return ("Permission denied");
+#endif
+#ifdef ENOTDIR
+	case ENOTDIR:
+		return ("Not a directory");
+#endif
+#ifdef EINVAL
+	case EINVAL:
+		return ("Invalid argument");
+#endif
+#ifdef ELOOP
+	case ELOOP:
+		return ("Too many levels of symbolic links");
+#endif
+	default:
+		shf_snprintf(errbuf, sizeof(errbuf),
+		    "Unknown error: %d", errnum);
+		return (errbuf);
+	}
+}
+#endif

Deleted: vendor/MirOS/mksh/R50/strlcpy.c
===================================================================
--- vendor/MirOS/mksh/dist/strlcpy.c	2014-06-30 23:58:12 UTC (rev 6706)
+++ vendor/MirOS/mksh/R50/strlcpy.c	2014-07-01 12:01:17 UTC (rev 6708)
@@ -1,52 +0,0 @@
-/*-
- * Copyright (c) 2006, 2008, 2009
- *	Thorsten Glaser <tg at mirbsd.org>
- * Copyright (c) 1998 Todd C. Miller <Todd.Miller at courtesan.com>
- *
- * Permission to use, copy, modify, and distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include "sh.h"
-
-__RCSID("$MirOS: src/bin/mksh/strlcpy.c,v 1.7 2009/06/10 18:12:50 tg Rel $");
-
-/*
- * Copy src to string dst of size siz. At most siz-1 characters
- * will be copied. Always NUL terminates (unless siz == 0).
- * Returns strlen(src); if retval >= siz, truncation occurred.
- */
-size_t
-strlcpy(char *dst, const char *src, size_t siz)
-{
-	const char *s = src;
-
-	if (siz == 0)
-		goto traverse_src;
-
-	/* copy as many chars as will fit */
-	while (--siz && (*dst++ = *s++))
-		;
-
-	/* not enough room in dst */
-	if (siz == 0) {
-		/* safe to NUL-terminate dst since we copied <= siz-1 chars */
-		*dst = '\0';
- traverse_src:
-		/* traverse rest of src */
-		while (*s++)
-			;
-	}
-
-	/* count does not include NUL */
-	return ((size_t)(s - src - 1));
-}

Copied: vendor/MirOS/mksh/R50/strlcpy.c (from rev 6707, vendor/MirOS/mksh/dist/strlcpy.c)
===================================================================
--- vendor/MirOS/mksh/R50/strlcpy.c	                        (rev 0)
+++ vendor/MirOS/mksh/R50/strlcpy.c	2014-07-01 12:01:17 UTC (rev 6708)
@@ -0,0 +1,53 @@
+/*-
+ * Copyright (c) 2006, 2008, 2009, 2013
+ *	Thorsten Glaser <tg at mirbsd.org>
+ * Copyright (c) 1998 Todd C. Miller <Todd.Miller at courtesan.com>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "sh.h"
+
+__RCSID("$MirOS: src/bin/mksh/strlcpy.c,v 1.8 2013/11/05 22:10:15 tg Exp $");
+
+/*
+ * Copy src to string dst of size siz. At most siz-1 characters
+ * will be copied. Always NUL terminates (unless siz == 0).
+ * Returns strlen(src); if retval >= siz, truncation occurred.
+ */
+#undef strlcpy
+size_t
+strlcpy(char *dst, const char *src, size_t siz)
+{
+	const char *s = src;
+
+	if (siz == 0)
+		goto traverse_src;
+
+	/* copy as many chars as will fit */
+	while (--siz && (*dst++ = *s++))
+		;
+
+	/* not enough room in dst */
+	if (siz == 0) {
+		/* safe to NUL-terminate dst since we copied <= siz-1 chars */
+		*dst = '\0';
+ traverse_src:
+		/* traverse rest of src */
+		while (*s++)
+			;
+	}
+
+	/* count does not include NUL */
+	return ((size_t)(s - src - 1));
+}

Deleted: vendor/MirOS/mksh/R50/syn.c
===================================================================
--- vendor/MirOS/mksh/dist/syn.c	2014-06-30 23:58:12 UTC (rev 6706)
+++ vendor/MirOS/mksh/R50/syn.c	2014-07-01 12:01:17 UTC (rev 6708)
@@ -1,1181 +0,0 @@
-/*	$OpenBSD: syn.c,v 1.29 2013/06/03 18:40:05 jca Exp $	*/
-
-/*-
- * Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009,
- *		 2011, 2012, 2013
- *	Thorsten Glaser <tg at mirbsd.org>
- *
- * Provided that these terms and disclaimer and all copyright notices
- * are retained or reproduced in an accompanying document, permission
- * is granted to deal in this work without restriction, including un-
- * limited rights to use, publicly perform, distribute, sell, modify,
- * merge, give away, or sublicence.
- *
- * This work is provided "AS IS" and WITHOUT WARRANTY of any kind, to
- * the utmost extent permitted by applicable law, neither express nor
- * implied; without malicious intent or gross negligence. In no event
- * may a licensor, author or contributor be held liable for indirect,
- * direct, other damage, loss, or other issues arising in any way out
- * of dealing in the work, even if advised of the possibility of such
- * damage or existence of a defect, except proven that it results out
- * of said person's immediate fault when using the work as intended.
- */
-
-#include "sh.h"
-
-__RCSID("$MirOS: src/bin/mksh/syn.c,v 1.92 2013/06/03 22:28:17 tg Exp $");
-
-struct nesting_state {
-	int start_token;	/* token than began nesting (eg, FOR) */
-	int start_line;		/* line nesting began on */
-};
-
-struct yyrecursive_state {
-	struct yyrecursive_state *next;
-	struct ioword **old_herep;
-	int old_symbol;
-	int old_salias;
-	int old_nesting_type;
-	bool old_reject;
-};
-
-static void yyparse(void);
-static struct op *pipeline(int);
-static struct op *andor(void);
-static struct op *c_list(bool);
-static struct ioword *synio(int);
-static struct op *nested(int, int, int);
-static struct op *get_command(int);
-static struct op *dogroup(void);
-static struct op *thenpart(void);
-static struct op *elsepart(void);
-static struct op *caselist(void);
-static struct op *casepart(int);
-static struct op *function_body(char *, bool);
-static char **wordlist(void);
-static struct op *block(int, struct op *, struct op *);
-static struct op *newtp(int);
-static void syntaxerr(const char *) MKSH_A_NORETURN;
-static void nesting_push(struct nesting_state *, int);
-static void nesting_pop(struct nesting_state *);
-static int assign_command(const char *);
-static int inalias(struct source *);
-static Test_op dbtestp_isa(Test_env *, Test_meta);
-static const char *dbtestp_getopnd(Test_env *, Test_op, bool);
-static int dbtestp_eval(Test_env *, Test_op, const char *,
-    const char *, bool);
-static void dbtestp_error(Test_env *, int, const char *) MKSH_A_NORETURN;
-
-static struct op *outtree;		/* yyparse output */
-static struct nesting_state nesting;	/* \n changed to ; */
-
-static bool reject;			/* token(cf) gets symbol again */
-static int symbol;			/* yylex value */
-static int sALIAS = ALIAS;		/* 0 in yyrecursive */
-
-#define REJECT		(reject = true)
-#define ACCEPT		(reject = false)
-#define token(cf)	((reject) ? (ACCEPT, symbol) : (symbol = yylex(cf)))
-#define tpeek(cf)	((reject) ? (symbol) : (REJECT, symbol = yylex(cf)))
-#define musthave(c,cf)	do { if (token(cf) != (c)) syntaxerr(NULL); } while (/* CONSTCOND */ 0)
-
-static const char Tcbrace[] = "}";
-static const char Tesac[] = "esac";
-
-static void
-yyparse(void)
-{
-	int c;
-
-	ACCEPT;
-
-	outtree = c_list(source->type == SSTRING);
-	c = tpeek(0);
-	if (c == 0 && !outtree)
-		outtree = newtp(TEOF);
-	else if (c != '\n' && c != 0)
-		syntaxerr(NULL);
-}
-
-static struct op *
-pipeline(int cf)
-{
-	struct op *t, *p, *tl = NULL;
-
-	t = get_command(cf);
-	if (t != NULL) {
-		while (token(0) == '|') {
-			if ((p = get_command(CONTIN)) == NULL)
-				syntaxerr(NULL);
-			if (tl == NULL)
-				t = tl = block(TPIPE, t, p);
-			else
-				tl = tl->right = block(TPIPE, tl->right, p);
-		}
-		REJECT;
-	}
-	return (t);
-}
-
-static struct op *
-andor(void)
-{
-	struct op *t, *p;
-	int c;
-
-	t = pipeline(0);
-	if (t != NULL) {
-		while ((c = token(0)) == LOGAND || c == LOGOR) {
-			if ((p = pipeline(CONTIN)) == NULL)
-				syntaxerr(NULL);
-			t = block(c == LOGAND? TAND: TOR, t, p);
-		}
-		REJECT;
-	}
-	return (t);
-}
-
-static struct op *
-c_list(bool multi)
-{
-	struct op *t = NULL, *p, *tl = NULL;
-	int c;
-	bool have_sep;
-
-	while (/* CONSTCOND */ 1) {
-		p = andor();
-		/*
-		 * Token has always been read/rejected at this point, so
-		 * we don't worry about what flags to pass token()
-		 */
-		c = token(0);
-		have_sep = true;
-		if (c == '\n' && (multi || inalias(source))) {
-			if (!p)
-				/* ignore blank lines */
-				continue;
-		} else if (!p)
-			break;
-		else if (c == '&' || c == COPROC)
-			p = block(c == '&' ? TASYNC : TCOPROC, p, NULL);
-		else if (c != ';')
-			have_sep = false;
-		if (!t)
-			t = p;
-		else if (!tl)
-			t = tl = block(TLIST, t, p);
-		else
-			tl = tl->right = block(TLIST, tl->right, p);
-		if (!have_sep)
-			break;
-	}
-	REJECT;
-	return (t);
-}
-
-static struct ioword *
-synio(int cf)
-{
-	struct ioword *iop;
-	static struct ioword *nextiop;
-	bool ishere;
-
-	if (nextiop != NULL) {
-		iop = nextiop;
-		nextiop = NULL;
-		return (iop);
-	}
-
-	if (tpeek(cf) != REDIR)
-		return (NULL);
-	ACCEPT;
-	iop = yylval.iop;
-	if (iop->flag & IONDELIM)
-		goto gotnulldelim;
-	ishere = (iop->flag & IOTYPE) == IOHERE;
-	musthave(LWORD, ishere ? HEREDELIM : 0);
-	if (ishere) {
-		iop->delim = yylval.cp;
-		if (*ident != 0)
-			/* unquoted */
- gotnulldelim:
-			iop->flag |= IOEVAL;
-		if (herep > &heres[HERES - 1])
-			yyerror("too many %ss\n", "<<");
-		*herep++ = iop;
-	} else
-		iop->name = yylval.cp;
-
-	if (iop->flag & IOBASH) {
-		char *cp;
-
-		nextiop = alloc(sizeof(*iop), ATEMP);
-		nextiop->name = cp = alloc(5, ATEMP);
-
-		if (iop->unit > 9) {
-			*cp++ = CHAR;
-			*cp++ = '0' + (iop->unit / 10);
-		}
-		*cp++ = CHAR;
-		*cp++ = '0' + (iop->unit % 10);
-		*cp = EOS;
-
-		iop->flag &= ~IOBASH;
-		nextiop->unit = 2;
-		nextiop->flag = IODUP;
-		nextiop->delim = NULL;
-		nextiop->heredoc = NULL;
-	}
-	return (iop);
-}
-
-static struct op *
-nested(int type, int smark, int emark)
-{
-	struct op *t;
-	struct nesting_state old_nesting;
-
-	nesting_push(&old_nesting, smark);
-	t = c_list(true);
-	musthave(emark, KEYWORD|sALIAS);
-	nesting_pop(&old_nesting);
-	return (block(type, t, NULL));
-}
-
-static const char let_cmd[] = {
-	CHAR, 'l', CHAR, 'e', CHAR, 't', CHAR, ']', EOS
-};
-static const char setA_cmd0[] = {
-	CHAR, 's', CHAR, 'e', CHAR, 't', EOS
-};
-static const char setA_cmd1[] = {
-	CHAR, '-', CHAR, 'A', EOS
-};
-static const char setA_cmd2[] = {
-	CHAR, '-', CHAR, '-', EOS
-};
-
-static struct op *
-get_command(int cf)
-{
-	struct op *t;
-	int c, iopn = 0, syniocf, lno;
-	struct ioword *iop, **iops;
-	XPtrV args, vars;
-	char *tcp;
-	struct nesting_state old_nesting;
-
-	/* NUFILE is small enough to leave this addition unchecked */
-	iops = alloc2((NUFILE + 1), sizeof(struct ioword *), ATEMP);
-	XPinit(args, 16);
-	XPinit(vars, 16);
-
-	syniocf = KEYWORD|sALIAS;
-	switch (c = token(cf|KEYWORD|sALIAS|VARASN)) {
-	default:
-		REJECT;
-		afree(iops, ATEMP);
-		XPfree(args);
-		XPfree(vars);
-		/* empty line */
-		return (NULL);
-
-	case LWORD:
-	case REDIR:
-		REJECT;
-		syniocf &= ~(KEYWORD|sALIAS);
-		t = newtp(TCOM);
-		t->lineno = source->line;
-		while (/* CONSTCOND */ 1) {
-			cf = (t->u.evalflags ? ARRAYVAR : 0) |
-			    (XPsize(args) == 0 ? sALIAS|VARASN : CMDWORD);
-			switch (tpeek(cf)) {
-			case REDIR:
-				while ((iop = synio(cf)) != NULL) {
-					if (iopn >= NUFILE)
-						yyerror("too many %ss\n",
-						    "redirection");
-					iops[iopn++] = iop;
-				}
-				break;
-
-			case LWORD:
-				ACCEPT;
-				/*
-				 * the iopn == 0 and XPsize(vars) == 0 are
-				 * dubious but AT&T ksh acts this way
-				 */
-				if (iopn == 0 && XPsize(vars) == 0 &&
-				    XPsize(args) == 0 &&
-				    assign_command(ident))
-					t->u.evalflags = DOVACHECK;
-				if ((XPsize(args) == 0 || Flag(FKEYWORD)) &&
-				    is_wdvarassign(yylval.cp))
-					XPput(vars, yylval.cp);
-				else
-					XPput(args, yylval.cp);
-				break;
-
-			case '(' /*)*/:
-				if (XPsize(args) == 0 && XPsize(vars) == 1 &&
-				    is_wdvarassign(yylval.cp)) {
-					/* wdarrassign: foo=(bar) */
-					ACCEPT;
-
-					/* manipulate the vars string */
-					tcp = XPptrv(vars)[(vars.len = 0)];
-					/* 'varname=' -> 'varname' */
-					tcp[wdscan(tcp, EOS) - tcp - 3] = EOS;
-
-					/* construct new args strings */
-					XPput(args, wdcopy(setA_cmd0, ATEMP));
-					XPput(args, wdcopy(setA_cmd1, ATEMP));
-					XPput(args, tcp);
-					XPput(args, wdcopy(setA_cmd2, ATEMP));
-
-					/* slurp in words till closing paren */
-					while (token(CONTIN) == LWORD)
-						XPput(args, yylval.cp);
-					if (symbol != /*(*/ ')')
-						syntaxerr(NULL);
-				} else {
-					/*
-					 * Check for "> foo (echo hi)"
-					 * which AT&T ksh allows (not
-					 * POSIX, but not disallowed)
-					 */
-					afree(t, ATEMP);
-					if (XPsize(args) == 0 &&
-					    XPsize(vars) == 0) {
-						ACCEPT;
-						goto Subshell;
-					}
-
-					/* must be a function */
-					if (iopn != 0 || XPsize(args) != 1 ||
-					    XPsize(vars) != 0)
-						syntaxerr(NULL);
-					ACCEPT;
-					musthave(/*(*/')', 0);
-					t = function_body(XPptrv(args)[0], false);
-				}
-				goto Leave;
-
-			default:
-				goto Leave;
-			}
-		}
- Leave:
-		break;
-
-	case '(': /*)*/ {
-		int subshell_nesting_type_saved;
- Subshell:
-		subshell_nesting_type_saved = subshell_nesting_type;
-		subshell_nesting_type = ')';
-		t = nested(TPAREN, '(', ')');
-		subshell_nesting_type = subshell_nesting_type_saved;
-		break;
-	    }
-
-	case '{': /*}*/
-		t = nested(TBRACE, '{', '}');
-		break;
-
-	case MDPAREN:
-		/* leave KEYWORD in syniocf (allow if (( 1 )) then ...) */
-		lno = source->line;
-		ACCEPT;
-		switch (token(LETEXPR)) {
-		case LWORD:
-			break;
-		case '(': /*)*/
-			goto Subshell;
-		default:
-			syntaxerr(NULL);
-		}
-		t = newtp(TCOM);
-		t->lineno = lno;
-		XPput(args, wdcopy(let_cmd, ATEMP));
-		XPput(args, yylval.cp);
-		break;
-
-	case DBRACKET: /* [[ .. ]] */
-		/* leave KEYWORD in syniocf (allow if [[ -n 1 ]] then ...) */
-		t = newtp(TDBRACKET);
-		ACCEPT;
-		{
-			Test_env te;
-
-			te.flags = TEF_DBRACKET;
-			te.pos.av = &args;
-			te.isa = dbtestp_isa;
-			te.getopnd = dbtestp_getopnd;
-			te.eval = dbtestp_eval;
-			te.error = dbtestp_error;
-
-			test_parse(&te);
-		}
-		break;
-
-	case FOR:
-	case SELECT:
-		t = newtp((c == FOR) ? TFOR : TSELECT);
-		musthave(LWORD, ARRAYVAR);
-		if (!is_wdvarname(yylval.cp, true))
-			yyerror("%s: %s\n", c == FOR ? "for" : Tselect,
-			    "bad identifier");
-		strdupx(t->str, ident, ATEMP);
-		nesting_push(&old_nesting, c);
-		t->vars = wordlist();
-		t->left = dogroup();
-		nesting_pop(&old_nesting);
-		break;
-
-	case WHILE:
-	case UNTIL:
-		nesting_push(&old_nesting, c);
-		t = newtp((c == WHILE) ? TWHILE : TUNTIL);
-		t->left = c_list(true);
-		t->right = dogroup();
-		nesting_pop(&old_nesting);
-		break;
-
-	case CASE:
-		t = newtp(TCASE);
-		musthave(LWORD, 0);
-		t->str = yylval.cp;
-		nesting_push(&old_nesting, c);
-		t->left = caselist();
-		nesting_pop(&old_nesting);
-		break;
-
-	case IF:
-		nesting_push(&old_nesting, c);
-		t = newtp(TIF);
-		t->left = c_list(true);
-		t->right = thenpart();
-		musthave(FI, KEYWORD|sALIAS);
-		nesting_pop(&old_nesting);
-		break;
-
-	case BANG:
-		syniocf &= ~(KEYWORD|sALIAS);
-		t = pipeline(0);
-		if (t == NULL)
-			syntaxerr(NULL);
-		t = block(TBANG, NULL, t);
-		break;
-
-	case TIME:
-		syniocf &= ~(KEYWORD|sALIAS);
-		t = pipeline(0);
-		if (t && t->type == TCOM) {
-			t->str = alloc(2, ATEMP);
-			/* TF_* flags */
-			t->str[0] = '\0';
-			t->str[1] = '\0';
-		}
-		t = block(TTIME, t, NULL);
-		break;
-
-	case FUNCTION:
-		musthave(LWORD, 0);
-		t = function_body(yylval.cp, true);
-		break;
-	}
-
-	while ((iop = synio(syniocf)) != NULL) {
-		if (iopn >= NUFILE)
-			yyerror("too many %ss\n", "redirection");
-		iops[iopn++] = iop;
-	}
-
-	if (iopn == 0) {
-		afree(iops, ATEMP);
-		t->ioact = NULL;
-	} else {
-		iops[iopn++] = NULL;
-		iops = aresize2(iops, iopn, sizeof(struct ioword *), ATEMP);
-		t->ioact = iops;
-	}
-
-	if (t->type == TCOM || t->type == TDBRACKET) {
-		XPput(args, NULL);
-		t->args = (const char **)XPclose(args);
-		XPput(vars, NULL);
-		t->vars = (char **)XPclose(vars);
-	} else {
-		XPfree(args);
-		XPfree(vars);
-	}
-
-	return (t);
-}
-
-static struct op *
-dogroup(void)
-{
-	int c;
-	struct op *list;
-
-	c = token(CONTIN|KEYWORD|sALIAS);
-	/*
-	 * A {...} can be used instead of do...done for for/select loops
-	 * but not for while/until loops - we don't need to check if it
-	 * is a while loop because it would have been parsed as part of
-	 * the conditional command list...
-	 */
-	if (c == DO)
-		c = DONE;
-	else if (c == '{')
-		c = '}';
-	else
-		syntaxerr(NULL);
-	list = c_list(true);
-	musthave(c, KEYWORD|sALIAS);
-	return (list);
-}
-
-static struct op *
-thenpart(void)
-{
-	struct op *t;
-
-	musthave(THEN, KEYWORD|sALIAS);
-	t = newtp(0);
-	t->left = c_list(true);
-	if (t->left == NULL)
-		syntaxerr(NULL);
-	t->right = elsepart();
-	return (t);
-}
-
-static struct op *
-elsepart(void)
-{
-	struct op *t;
-
-	switch (token(KEYWORD|sALIAS|VARASN)) {
-	case ELSE:
-		if ((t = c_list(true)) == NULL)
-			syntaxerr(NULL);
-		return (t);
-
-	case ELIF:
-		t = newtp(TELIF);
-		t->left = c_list(true);
-		t->right = thenpart();
-		return (t);
-
-	default:
-		REJECT;
-	}
-	return (NULL);
-}
-
-static struct op *
-caselist(void)
-{
-	struct op *t, *tl;
-	int c;
-
-	c = token(CONTIN|KEYWORD|sALIAS);
-	/* A {...} can be used instead of in...esac for case statements */
-	if (c == IN)
-		c = ESAC;
-	else if (c == '{')
-		c = '}';
-	else
-		syntaxerr(NULL);
-	t = tl = NULL;
-	/* no ALIAS here */
-	while ((tpeek(CONTIN|KEYWORD|ESACONLY)) != c) {
-		struct op *tc = casepart(c);
-		if (tl == NULL)
-			t = tl = tc, tl->right = NULL;
-		else
-			tl->right = tc, tl = tc;
-	}
-	musthave(c, KEYWORD|sALIAS);
-	return (t);
-}
-
-static struct op *
-casepart(int endtok)
-{
-	struct op *t;
-	XPtrV ptns;
-
-	XPinit(ptns, 16);
-	t = newtp(TPAT);
-	/* no ALIAS here */
-	if (token(CONTIN | KEYWORD) != '(')
-		REJECT;
-	do {
-		switch (token(0)) {
-		case LWORD:
-			break;
-		case '}':
-		case ESAC:
-			if (symbol != endtok) {
-				strdupx(yylval.cp,
-				    symbol == '}' ? Tcbrace : Tesac, ATEMP);
-				break;
-			}
-			/* FALLTHROUGH */
-		default:
-			syntaxerr(NULL);
-		}
-		XPput(ptns, yylval.cp);
-	} while (token(0) == '|');
-	REJECT;
-	XPput(ptns, NULL);
-	t->vars = (char **)XPclose(ptns);
-	musthave(')', 0);
-
-	t->left = c_list(true);
-
-	/* initialise to default for ;; or omitted */
-	t->u.charflag = ';';
-	/* SUSv4 requires the ;; except in the last casepart */
-	if ((tpeek(CONTIN|KEYWORD|sALIAS)) != endtok)
-		switch (symbol) {
-		default:
-			syntaxerr(NULL);
-		case BRKEV:
-			t->u.charflag = '|';
-			if (0)
-				/* FALLTHROUGH */
-		case BRKFT:
-			t->u.charflag = '&';
-			/* FALLTHROUGH */
-		case BREAK:
-			/* initialised above, but we need to eat the token */
-			ACCEPT;
-		}
-	return (t);
-}
-
-static struct op *
-function_body(char *name,
-    /* function foo { ... } vs foo() { .. } */
-    bool ksh_func)
-{
-	char *sname, *p;
-	struct op *t;
-
-	sname = wdstrip(name, 0);
-	/*-
-	 * Check for valid characters in name. POSIX and AT&T ksh93 say
-	 * only allow [a-zA-Z_0-9] but this allows more as old pdkshs
-	 * have allowed more; the following were never allowed:
-	 *	NUL TAB NL SP " $ & ' ( ) ; < = > \ ` |
-	 * C_QUOTE covers all but adds # * ? [ ]
-	 */
-	for (p = sname; *p; p++)
-		if (ctype(*p, C_QUOTE))
-			yyerror("%s: %s\n", sname, "invalid function name");
-
-	/*
-	 * Note that POSIX allows only compound statements after foo(),
-	 * sh and AT&T ksh allow any command, go with the later since it
-	 * shouldn't break anything. However, for function foo, AT&T ksh
-	 * only accepts an open-brace.
-	 */
-	if (ksh_func) {
-		if (tpeek(CONTIN|KEYWORD|sALIAS) == '(' /*)*/) {
-			/* function foo () { //}*/
-			ACCEPT;
-			musthave(')', 0);
-			/* degrade to POSIX function */
-			ksh_func = false;
-		}
-		musthave('{' /*}*/, CONTIN|KEYWORD|sALIAS);
-		REJECT;
-	}
-
-	t = newtp(TFUNCT);
-	t->str = sname;
-	t->u.ksh_func = tobool(ksh_func);
-	t->lineno = source->line;
-
-	if ((t->left = get_command(CONTIN)) == NULL) {
-		char *tv;
-		/*
-		 * Probably something like foo() followed by EOF or ';'.
-		 * This is accepted by sh and ksh88.
-		 * To make "typeset -f foo" work reliably (so its output can
-		 * be used as input), we pretend there is a colon here.
-		 */
-		t->left = newtp(TCOM);
-		/* (2 * sizeof(char *)) is small enough */
-		t->left->args = alloc(2 * sizeof(char *), ATEMP);
-		t->left->args[0] = tv = alloc(3, ATEMP);
-		tv[0] = CHAR;
-		tv[1] = ':';
-		tv[2] = EOS;
-		t->left->args[1] = NULL;
-		t->left->vars = alloc(sizeof(char *), ATEMP);
-		t->left->vars[0] = NULL;
-		t->left->lineno = 1;
-	}
-
-	return (t);
-}
-
-static char **
-wordlist(void)
-{
-	int c;
-	XPtrV args;
-
-	XPinit(args, 16);
-	/* POSIX does not do alias expansion here... */
-	if ((c = token(CONTIN|KEYWORD|sALIAS)) != IN) {
-		if (c != ';')
-			/* non-POSIX, but AT&T ksh accepts a ; here */
-			REJECT;
-		return (NULL);
-	}
-	while ((c = token(0)) == LWORD)
-		XPput(args, yylval.cp);
-	if (c != '\n' && c != ';')
-		syntaxerr(NULL);
-	XPput(args, NULL);
-	return ((char **)XPclose(args));
-}
-
-/*
- * supporting functions
- */
-
-static struct op *
-block(int type, struct op *t1, struct op *t2)
-{
-	struct op *t;
-
-	t = newtp(type);
-	t->left = t1;
-	t->right = t2;
-	return (t);
-}
-
-static const struct tokeninfo {
-	const char *name;
-	short val;
-	short reserved;
-} tokentab[] = {
-	/* Reserved words */
-	{ "if",		IF,	true },
-	{ "then",	THEN,	true },
-	{ "else",	ELSE,	true },
-	{ "elif",	ELIF,	true },
-	{ "fi",		FI,	true },
-	{ "case",	CASE,	true },
-	{ Tesac,	ESAC,	true },
-	{ "for",	FOR,	true },
-	{ Tselect,	SELECT,	true },
-	{ "while",	WHILE,	true },
-	{ "until",	UNTIL,	true },
-	{ "do",		DO,	true },
-	{ "done",	DONE,	true },
-	{ "in",		IN,	true },
-	{ Tfunction,	FUNCTION, true },
-	{ "time",	TIME,	true },
-	{ "{",		'{',	true },
-	{ Tcbrace,	'}',	true },
-	{ "!",		BANG,	true },
-	{ "[[",		DBRACKET, true },
-	/* Lexical tokens (0[EOF], LWORD and REDIR handled specially) */
-	{ "&&",		LOGAND,	false },
-	{ "||",		LOGOR,	false },
-	{ ";;",		BREAK,	false },
-	{ ";|",		BRKEV,	false },
-	{ ";&",		BRKFT,	false },
-	{ "((",		MDPAREN, false },
-	{ "|&",		COPROC,	false },
-	/* and some special cases... */
-	{ "newline",	'\n',	false },
-	{ NULL,		0,	false }
-};
-
-void
-initkeywords(void)
-{
-	struct tokeninfo const *tt;
-	struct tbl *p;
-
-	ktinit(APERM, &keywords,
-	    /* currently 28 keywords: 75% of 64 = 2^6 */
-	    6);
-	for (tt = tokentab; tt->name; tt++) {
-		if (tt->reserved) {
-			p = ktenter(&keywords, tt->name, hash(tt->name));
-			p->flag |= DEFINED|ISSET;
-			p->type = CKEYWD;
-			p->val.i = tt->val;
-		}
-	}
-}
-
-static void
-syntaxerr(const char *what)
-{
-	/* 2<<- is the longest redirection, I think */
-	char redir[6];
-	const char *s;
-	struct tokeninfo const *tt;
-	int c;
-
-	if (!what)
-		what = "unexpected";
-	REJECT;
-	c = token(0);
- Again:
-	switch (c) {
-	case 0:
-		if (nesting.start_token) {
-			c = nesting.start_token;
-			source->errline = nesting.start_line;
-			what = "unmatched";
-			goto Again;
-		}
-		/* don't quote the EOF */
-		yyerror("%s: %s %s\n", Tsynerr, "unexpected", "EOF");
-		/* NOTREACHED */
-
-	case LWORD:
-		s = snptreef(NULL, 32, "%S", yylval.cp);
-		break;
-
-	case REDIR:
-		s = snptreef(redir, sizeof(redir), "%R", yylval.iop);
-		break;
-
-	default:
-		for (tt = tokentab; tt->name; tt++)
-			if (tt->val == c)
-			    break;
-		if (tt->name)
-			s = tt->name;
-		else {
-			if (c > 0 && c < 256) {
-				redir[0] = c;
-				redir[1] = '\0';
-			} else
-				shf_snprintf(redir, sizeof(redir),
-					"?%d", c);
-			s = redir;
-		}
-	}
-	yyerror("%s: '%s' %s\n", Tsynerr, s, what);
-}
-
-static void
-nesting_push(struct nesting_state *save, int tok)
-{
-	*save = nesting;
-	nesting.start_token = tok;
-	nesting.start_line = source->line;
-}
-
-static void
-nesting_pop(struct nesting_state *saved)
-{
-	nesting = *saved;
-}
-
-static struct op *
-newtp(int type)
-{
-	struct op *t;
-
-	t = alloc(sizeof(struct op), ATEMP);
-	t->type = type;
-	t->u.evalflags = 0;
-	t->args = NULL;
-	t->vars = NULL;
-	t->ioact = NULL;
-	t->left = t->right = NULL;
-	t->str = NULL;
-	return (t);
-}
-
-struct op *
-compile(Source *s, bool skiputf8bom)
-{
-	nesting.start_token = 0;
-	nesting.start_line = 0;
-	herep = heres;
-	source = s;
-	if (skiputf8bom)
-		yyskiputf8bom();
-	yyparse();
-	return (outtree);
-}
-
-/*-
- * This kludge exists to take care of sh/AT&T ksh oddity in which
- * the arguments of alias/export/readonly/typeset have no field
- * splitting, file globbing, or (normal) tilde expansion done.
- * AT&T ksh seems to do something similar to this since
- *	$ touch a=a; typeset a=[ab]; echo "$a"
- *	a=[ab]
- *	$ x=typeset; $x a=[ab]; echo "$a"
- *	a=a
- *	$
- */
-static int
-assign_command(const char *s)
-{
-	if (!*s)
-		return (0);
-	return ((strcmp(s, Talias) == 0) ||
-	    (strcmp(s, Texport) == 0) ||
-	    (strcmp(s, Treadonly) == 0) ||
-	    (strcmp(s, Ttypeset) == 0));
-}
-
-/* Check if we are in the middle of reading an alias */
-static int
-inalias(struct source *s)
-{
-	for (; s && s->type == SALIAS; s = s->next)
-		if (!(s->flags & SF_ALIASEND))
-			return (1);
-	return (0);
-}
-
-
-/*
- * Order important - indexed by Test_meta values
- * Note that ||, &&, ( and ) can't appear in as unquoted strings
- * in normal shell input, so these can be interpreted unambiguously
- * in the evaluation pass.
- */
-static const char dbtest_or[] = { CHAR, '|', CHAR, '|', EOS };
-static const char dbtest_and[] = { CHAR, '&', CHAR, '&', EOS };
-static const char dbtest_not[] = { CHAR, '!', EOS };
-static const char dbtest_oparen[] = { CHAR, '(', EOS };
-static const char dbtest_cparen[] = { CHAR, ')', EOS };
-const char * const dbtest_tokens[] = {
-	dbtest_or, dbtest_and, dbtest_not,
-	dbtest_oparen, dbtest_cparen
-};
-static const char db_close[] = { CHAR, ']', CHAR, ']', EOS };
-static const char db_lthan[] = { CHAR, '<', EOS };
-static const char db_gthan[] = { CHAR, '>', EOS };
-
-/*
- * Test if the current token is a whatever. Accepts the current token if
- * it is. Returns 0 if it is not, non-zero if it is (in the case of
- * TM_UNOP and TM_BINOP, the returned value is a Test_op).
- */
-static Test_op
-dbtestp_isa(Test_env *te, Test_meta meta)
-{
-	int c = tpeek(ARRAYVAR | (meta == TM_BINOP ? 0 : CONTIN));
-	int uqword;
-	char *save = NULL;
-	Test_op ret = TO_NONOP;
-
-	/* unquoted word? */
-	uqword = c == LWORD && *ident;
-
-	if (meta == TM_OR)
-		ret = c == LOGOR ? TO_NONNULL : TO_NONOP;
-	else if (meta == TM_AND)
-		ret = c == LOGAND ? TO_NONNULL : TO_NONOP;
-	else if (meta == TM_NOT)
-		ret = (uqword && !strcmp(yylval.cp,
-		    dbtest_tokens[(int)TM_NOT])) ? TO_NONNULL : TO_NONOP;
-	else if (meta == TM_OPAREN)
-		ret = c == '(' /*)*/ ? TO_NONNULL : TO_NONOP;
-	else if (meta == TM_CPAREN)
-		ret = c == /*(*/ ')' ? TO_NONNULL : TO_NONOP;
-	else if (meta == TM_UNOP || meta == TM_BINOP) {
-		if (meta == TM_BINOP && c == REDIR &&
-		    (yylval.iop->flag == IOREAD || yylval.iop->flag == IOWRITE)) {
-			ret = TO_NONNULL;
-			save = wdcopy(yylval.iop->flag == IOREAD ?
-			    db_lthan : db_gthan, ATEMP);
-		} else if (uqword && (ret = test_isop(meta, ident)))
-			save = yylval.cp;
-	} else
-		/* meta == TM_END */
-		ret = (uqword && !strcmp(yylval.cp,
-		    db_close)) ? TO_NONNULL : TO_NONOP;
-	if (ret != TO_NONOP) {
-		ACCEPT;
-		if ((unsigned int)meta < NELEM(dbtest_tokens))
-			save = wdcopy(dbtest_tokens[(int)meta], ATEMP);
-		if (save)
-			XPput(*te->pos.av, save);
-	}
-	return (ret);
-}
-
-static const char *
-dbtestp_getopnd(Test_env *te, Test_op op MKSH_A_UNUSED,
-    bool do_eval MKSH_A_UNUSED)
-{
-	int c = tpeek(ARRAYVAR);
-
-	if (c != LWORD)
-		return (NULL);
-
-	ACCEPT;
-	XPput(*te->pos.av, yylval.cp);
-
-	return (null);
-}
-
-static int
-dbtestp_eval(Test_env *te MKSH_A_UNUSED, Test_op op MKSH_A_UNUSED,
-    const char *opnd1 MKSH_A_UNUSED, const char *opnd2 MKSH_A_UNUSED,
-    bool do_eval MKSH_A_UNUSED)
-{
-	return (1);
-}
-
-static void
-dbtestp_error(Test_env *te, int offset, const char *msg)
-{
-	te->flags |= TEF_ERROR;
-
-	if (offset < 0) {
-		REJECT;
-		/* Kludgy to say the least... */
-		symbol = LWORD;
-		yylval.cp = *(XPptrv(*te->pos.av) + XPsize(*te->pos.av) +
-		    offset);
-	}
-	syntaxerr(msg);
-}
-
-#if HAVE_SELECT
-
-#ifndef EOVERFLOW
-#ifdef ERANGE
-#define EOVERFLOW	ERANGE
-#else
-#define EOVERFLOW	EINVAL
-#endif
-#endif
-
-bool
-parse_usec(const char *s, struct timeval *tv)
-{
-	struct timeval tt;
-	int i;
-
-	tv->tv_sec = 0;
-	/* parse integral part */
-	while (ksh_isdigit(*s)) {
-		tt.tv_sec = tv->tv_sec * 10 + (*s++ - '0');
-		if (tt.tv_sec / 10 != tv->tv_sec) {
-			errno = EOVERFLOW;
-			return (true);
-		}
-		tv->tv_sec = tt.tv_sec;
-	}
-
-	tv->tv_usec = 0;
-	if (!*s)
-		/* no decimal fraction */
-		return (false);
-	else if (*s++ != '.') {
-		/* junk after integral part */
-		errno = EINVAL;
-		return (true);
-	}
-
-	/* parse decimal fraction */
-	i = 100000;
-	while (ksh_isdigit(*s)) {
-		tv->tv_usec += i * (*s++ - '0');
-		if (i == 1)
-			break;
-		i /= 10;
-	}
-	/* check for junk after fractional part */
-	while (ksh_isdigit(*s))
-		++s;
-	if (*s) {
-		errno = EINVAL;
-		return (true);
-	}
-
-	/* end of input string reached, no errors */
-	return (false);
-}
-#endif
-
-/*
- * Helper function called from within lex.c:yylex() to parse
- * a COMSUB recursively using the main shell parser and lexer
- */
-char *
-yyrecursive(int subtype MKSH_A_UNUSED)
-{
-	struct op *t;
-	char *cp;
-	struct yyrecursive_state *ys;
-	int stok, etok;
-
-	if (subtype != COMSUB) {
-		stok = '{';
-		etok = '}';
-	} else {
-		stok = '(';
-		etok = ')';
-	}
-
-	ys = alloc(sizeof(struct yyrecursive_state), ATEMP);
-
-	/* tell the lexer to accept a closing parenthesis as EOD */
-	ys->old_nesting_type = subshell_nesting_type;
-	subshell_nesting_type = etok;
-
-	/* push reject state, parse recursively, pop reject state */
-	ys->old_reject = reject;
-	ys->old_symbol = symbol;
-	ACCEPT;
-	ys->old_herep = herep;
-	ys->old_salias = sALIAS;
-	sALIAS = 0;
-	ys->next = e->yyrecursive_statep;
-	e->yyrecursive_statep = ys;
-	/* we use TPAREN as a helper container here */
-	t = nested(TPAREN, stok, etok);
-	yyrecursive_pop(false);
-
-	/* t->left because nested(TPAREN, ...) hides our goodies there */
-	cp = snptreef(NULL, 0, "%T", t->left);
-	tfree(t, ATEMP);
-
-	return (cp);
-}
-
-void
-yyrecursive_pop(bool popall)
-{
-	struct yyrecursive_state *ys;
-
- popnext:
-	if (!(ys = e->yyrecursive_statep))
-		return;
-	e->yyrecursive_statep = ys->next;
-
-	sALIAS = ys->old_salias;
-	herep = ys->old_herep;
-	reject = ys->old_reject;
-	symbol = ys->old_symbol;
-
-	subshell_nesting_type = ys->old_nesting_type;
-
-	afree(ys, ATEMP);
-	if (popall)
-		goto popnext;
-}

Copied: vendor/MirOS/mksh/R50/syn.c (from rev 6707, vendor/MirOS/mksh/dist/syn.c)
===================================================================
--- vendor/MirOS/mksh/R50/syn.c	                        (rev 0)
+++ vendor/MirOS/mksh/R50/syn.c	2014-07-01 12:01:17 UTC (rev 6708)
@@ -0,0 +1,1181 @@
+/*	$OpenBSD: syn.c,v 1.29 2013/06/03 18:40:05 jca Exp $	*/
+
+/*-
+ * Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009,
+ *		 2011, 2012, 2013, 2014
+ *	Thorsten Glaser <tg at mirbsd.org>
+ *
+ * Provided that these terms and disclaimer and all copyright notices
+ * are retained or reproduced in an accompanying document, permission
+ * is granted to deal in this work without restriction, including un-
+ * limited rights to use, publicly perform, distribute, sell, modify,
+ * merge, give away, or sublicence.
+ *
+ * This work is provided "AS IS" and WITHOUT WARRANTY of any kind, to
+ * the utmost extent permitted by applicable law, neither express nor
+ * implied; without malicious intent or gross negligence. In no event
+ * may a licensor, author or contributor be held liable for indirect,
+ * direct, other damage, loss, or other issues arising in any way out
+ * of dealing in the work, even if advised of the possibility of such
+ * damage or existence of a defect, except proven that it results out
+ * of said person's immediate fault when using the work as intended.
+ */
+
+#include "sh.h"
+
+__RCSID("$MirOS: src/bin/mksh/syn.c,v 1.94 2014/01/05 21:57:29 tg Exp $");
+
+struct nesting_state {
+	int start_token;	/* token than began nesting (eg, FOR) */
+	int start_line;		/* line nesting began on */
+};
+
+struct yyrecursive_state {
+	struct yyrecursive_state *next;
+	struct ioword **old_herep;
+	int old_symbol;
+	int old_salias;
+	int old_nesting_type;
+	bool old_reject;
+};
+
+static void yyparse(void);
+static struct op *pipeline(int);
+static struct op *andor(void);
+static struct op *c_list(bool);
+static struct ioword *synio(int);
+static struct op *nested(int, int, int);
+static struct op *get_command(int);
+static struct op *dogroup(void);
+static struct op *thenpart(void);
+static struct op *elsepart(void);
+static struct op *caselist(void);
+static struct op *casepart(int);
+static struct op *function_body(char *, bool);
+static char **wordlist(void);
+static struct op *block(int, struct op *, struct op *);
+static struct op *newtp(int);
+static void syntaxerr(const char *) MKSH_A_NORETURN;
+static void nesting_push(struct nesting_state *, int);
+static void nesting_pop(struct nesting_state *);
+static int assign_command(const char *);
+static int inalias(struct source *) MKSH_A_PURE;
+static Test_op dbtestp_isa(Test_env *, Test_meta);
+static const char *dbtestp_getopnd(Test_env *, Test_op, bool);
+static int dbtestp_eval(Test_env *, Test_op, const char *,
+    const char *, bool);
+static void dbtestp_error(Test_env *, int, const char *) MKSH_A_NORETURN;
+
+static struct op *outtree;		/* yyparse output */
+static struct nesting_state nesting;	/* \n changed to ; */
+
+static bool reject;			/* token(cf) gets symbol again */
+static int symbol;			/* yylex value */
+static int sALIAS = ALIAS;		/* 0 in yyrecursive */
+
+#define REJECT		(reject = true)
+#define ACCEPT		(reject = false)
+#define token(cf)	((reject) ? (ACCEPT, symbol) : (symbol = yylex(cf)))
+#define tpeek(cf)	((reject) ? (symbol) : (REJECT, symbol = yylex(cf)))
+#define musthave(c,cf)	do { if (token(cf) != (c)) syntaxerr(NULL); } while (/* CONSTCOND */ 0)
+
+static const char Tcbrace[] = "}";
+static const char Tesac[] = "esac";
+
+static void
+yyparse(void)
+{
+	int c;
+
+	ACCEPT;
+
+	outtree = c_list(source->type == SSTRING);
+	c = tpeek(0);
+	if (c == 0 && !outtree)
+		outtree = newtp(TEOF);
+	else if (c != '\n' && c != 0)
+		syntaxerr(NULL);
+}
+
+static struct op *
+pipeline(int cf)
+{
+	struct op *t, *p, *tl = NULL;
+
+	t = get_command(cf);
+	if (t != NULL) {
+		while (token(0) == '|') {
+			if ((p = get_command(CONTIN)) == NULL)
+				syntaxerr(NULL);
+			if (tl == NULL)
+				t = tl = block(TPIPE, t, p);
+			else
+				tl = tl->right = block(TPIPE, tl->right, p);
+		}
+		REJECT;
+	}
+	return (t);
+}
+
+static struct op *
+andor(void)
+{
+	struct op *t, *p;
+	int c;
+
+	t = pipeline(0);
+	if (t != NULL) {
+		while ((c = token(0)) == LOGAND || c == LOGOR) {
+			if ((p = pipeline(CONTIN)) == NULL)
+				syntaxerr(NULL);
+			t = block(c == LOGAND? TAND: TOR, t, p);
+		}
+		REJECT;
+	}
+	return (t);
+}
+
+static struct op *
+c_list(bool multi)
+{
+	struct op *t = NULL, *p, *tl = NULL;
+	int c;
+	bool have_sep;
+
+	while (/* CONSTCOND */ 1) {
+		p = andor();
+		/*
+		 * Token has always been read/rejected at this point, so
+		 * we don't worry about what flags to pass token()
+		 */
+		c = token(0);
+		have_sep = true;
+		if (c == '\n' && (multi || inalias(source))) {
+			if (!p)
+				/* ignore blank lines */
+				continue;
+		} else if (!p)
+			break;
+		else if (c == '&' || c == COPROC)
+			p = block(c == '&' ? TASYNC : TCOPROC, p, NULL);
+		else if (c != ';')
+			have_sep = false;
+		if (!t)
+			t = p;
+		else if (!tl)
+			t = tl = block(TLIST, t, p);
+		else
+			tl = tl->right = block(TLIST, tl->right, p);
+		if (!have_sep)
+			break;
+	}
+	REJECT;
+	return (t);
+}
+
+static struct ioword *
+synio(int cf)
+{
+	struct ioword *iop;
+	static struct ioword *nextiop;
+	bool ishere;
+
+	if (nextiop != NULL) {
+		iop = nextiop;
+		nextiop = NULL;
+		return (iop);
+	}
+
+	if (tpeek(cf) != REDIR)
+		return (NULL);
+	ACCEPT;
+	iop = yylval.iop;
+	if (iop->flag & IONDELIM)
+		goto gotnulldelim;
+	ishere = (iop->flag & IOTYPE) == IOHERE;
+	musthave(LWORD, ishere ? HEREDELIM : 0);
+	if (ishere) {
+		iop->delim = yylval.cp;
+		if (*ident != 0)
+			/* unquoted */
+ gotnulldelim:
+			iop->flag |= IOEVAL;
+		if (herep > &heres[HERES - 1])
+			yyerror("too many %ss\n", "<<");
+		*herep++ = iop;
+	} else
+		iop->name = yylval.cp;
+
+	if (iop->flag & IOBASH) {
+		char *cp;
+
+		nextiop = alloc(sizeof(*iop), ATEMP);
+		nextiop->name = cp = alloc(5, ATEMP);
+
+		if (iop->unit > 9) {
+			*cp++ = CHAR;
+			*cp++ = '0' + (iop->unit / 10);
+		}
+		*cp++ = CHAR;
+		*cp++ = '0' + (iop->unit % 10);
+		*cp = EOS;
+
+		iop->flag &= ~IOBASH;
+		nextiop->unit = 2;
+		nextiop->flag = IODUP;
+		nextiop->delim = NULL;
+		nextiop->heredoc = NULL;
+	}
+	return (iop);
+}
+
+static struct op *
+nested(int type, int smark, int emark)
+{
+	struct op *t;
+	struct nesting_state old_nesting;
+
+	nesting_push(&old_nesting, smark);
+	t = c_list(true);
+	musthave(emark, KEYWORD|sALIAS);
+	nesting_pop(&old_nesting);
+	return (block(type, t, NULL));
+}
+
+static const char let_cmd[] = {
+	CHAR, 'l', CHAR, 'e', CHAR, 't', CHAR, ']', EOS
+};
+static const char setA_cmd0[] = {
+	CHAR, 's', CHAR, 'e', CHAR, 't', EOS
+};
+static const char setA_cmd1[] = {
+	CHAR, '-', CHAR, 'A', EOS
+};
+static const char setA_cmd2[] = {
+	CHAR, '-', CHAR, '-', EOS
+};
+
+static struct op *
+get_command(int cf)
+{
+	struct op *t;
+	int c, iopn = 0, syniocf, lno;
+	struct ioword *iop, **iops;
+	XPtrV args, vars;
+	char *tcp;
+	struct nesting_state old_nesting;
+
+	/* NUFILE is small enough to leave this addition unchecked */
+	iops = alloc2((NUFILE + 1), sizeof(struct ioword *), ATEMP);
+	XPinit(args, 16);
+	XPinit(vars, 16);
+
+	syniocf = KEYWORD|sALIAS;
+	switch (c = token(cf|KEYWORD|sALIAS|VARASN)) {
+	default:
+		REJECT;
+		afree(iops, ATEMP);
+		XPfree(args);
+		XPfree(vars);
+		/* empty line */
+		return (NULL);
+
+	case LWORD:
+	case REDIR:
+		REJECT;
+		syniocf &= ~(KEYWORD|sALIAS);
+		t = newtp(TCOM);
+		t->lineno = source->line;
+		while (/* CONSTCOND */ 1) {
+			cf = (t->u.evalflags ? ARRAYVAR : 0) |
+			    (XPsize(args) == 0 ? sALIAS|VARASN : CMDWORD);
+			switch (tpeek(cf)) {
+			case REDIR:
+				while ((iop = synio(cf)) != NULL) {
+					if (iopn >= NUFILE)
+						yyerror("too many %ss\n",
+						    "redirection");
+					iops[iopn++] = iop;
+				}
+				break;
+
+			case LWORD:
+				ACCEPT;
+				/*
+				 * the iopn == 0 and XPsize(vars) == 0 are
+				 * dubious but AT&T ksh acts this way
+				 */
+				if (iopn == 0 && XPsize(vars) == 0 &&
+				    XPsize(args) == 0 &&
+				    assign_command(ident))
+					t->u.evalflags = DOVACHECK;
+				if ((XPsize(args) == 0 || Flag(FKEYWORD)) &&
+				    is_wdvarassign(yylval.cp))
+					XPput(vars, yylval.cp);
+				else
+					XPput(args, yylval.cp);
+				break;
+
+			case '(' /*)*/:
+				if (XPsize(args) == 0 && XPsize(vars) == 1 &&
+				    is_wdvarassign(yylval.cp)) {
+					/* wdarrassign: foo=(bar) */
+					ACCEPT;
+
+					/* manipulate the vars string */
+					tcp = XPptrv(vars)[(vars.len = 0)];
+					/* 'varname=' -> 'varname' */
+					tcp[wdscan(tcp, EOS) - tcp - 3] = EOS;
+
+					/* construct new args strings */
+					XPput(args, wdcopy(setA_cmd0, ATEMP));
+					XPput(args, wdcopy(setA_cmd1, ATEMP));
+					XPput(args, tcp);
+					XPput(args, wdcopy(setA_cmd2, ATEMP));
+
+					/* slurp in words till closing paren */
+					while (token(CONTIN) == LWORD)
+						XPput(args, yylval.cp);
+					if (symbol != /*(*/ ')')
+						syntaxerr(NULL);
+				} else {
+					/*
+					 * Check for "> foo (echo hi)"
+					 * which AT&T ksh allows (not
+					 * POSIX, but not disallowed)
+					 */
+					afree(t, ATEMP);
+					if (XPsize(args) == 0 &&
+					    XPsize(vars) == 0) {
+						ACCEPT;
+						goto Subshell;
+					}
+
+					/* must be a function */
+					if (iopn != 0 || XPsize(args) != 1 ||
+					    XPsize(vars) != 0)
+						syntaxerr(NULL);
+					ACCEPT;
+					musthave(/*(*/')', 0);
+					t = function_body(XPptrv(args)[0], false);
+				}
+				goto Leave;
+
+			default:
+				goto Leave;
+			}
+		}
+ Leave:
+		break;
+
+	case '(': /*)*/ {
+		int subshell_nesting_type_saved;
+ Subshell:
+		subshell_nesting_type_saved = subshell_nesting_type;
+		subshell_nesting_type = ')';
+		t = nested(TPAREN, '(', ')');
+		subshell_nesting_type = subshell_nesting_type_saved;
+		break;
+	    }
+
+	case '{': /*}*/
+		t = nested(TBRACE, '{', '}');
+		break;
+
+	case MDPAREN:
+		/* leave KEYWORD in syniocf (allow if (( 1 )) then ...) */
+		lno = source->line;
+		ACCEPT;
+		switch (token(LETEXPR)) {
+		case LWORD:
+			break;
+		case '(': /*)*/
+			goto Subshell;
+		default:
+			syntaxerr(NULL);
+		}
+		t = newtp(TCOM);
+		t->lineno = lno;
+		XPput(args, wdcopy(let_cmd, ATEMP));
+		XPput(args, yylval.cp);
+		break;
+
+	case DBRACKET: /* [[ .. ]] */
+		/* leave KEYWORD in syniocf (allow if [[ -n 1 ]] then ...) */
+		t = newtp(TDBRACKET);
+		ACCEPT;
+		{
+			Test_env te;
+
+			te.flags = TEF_DBRACKET;
+			te.pos.av = &args;
+			te.isa = dbtestp_isa;
+			te.getopnd = dbtestp_getopnd;
+			te.eval = dbtestp_eval;
+			te.error = dbtestp_error;
+
+			test_parse(&te);
+		}
+		break;
+
+	case FOR:
+	case SELECT:
+		t = newtp((c == FOR) ? TFOR : TSELECT);
+		musthave(LWORD, ARRAYVAR);
+		if (!is_wdvarname(yylval.cp, true))
+			yyerror("%s: %s\n", c == FOR ? "for" : Tselect,
+			    "bad identifier");
+		strdupx(t->str, ident, ATEMP);
+		nesting_push(&old_nesting, c);
+		t->vars = wordlist();
+		t->left = dogroup();
+		nesting_pop(&old_nesting);
+		break;
+
+	case WHILE:
+	case UNTIL:
+		nesting_push(&old_nesting, c);
+		t = newtp((c == WHILE) ? TWHILE : TUNTIL);
+		t->left = c_list(true);
+		t->right = dogroup();
+		nesting_pop(&old_nesting);
+		break;
+
+	case CASE:
+		t = newtp(TCASE);
+		musthave(LWORD, 0);
+		t->str = yylval.cp;
+		nesting_push(&old_nesting, c);
+		t->left = caselist();
+		nesting_pop(&old_nesting);
+		break;
+
+	case IF:
+		nesting_push(&old_nesting, c);
+		t = newtp(TIF);
+		t->left = c_list(true);
+		t->right = thenpart();
+		musthave(FI, KEYWORD|sALIAS);
+		nesting_pop(&old_nesting);
+		break;
+
+	case BANG:
+		syniocf &= ~(KEYWORD|sALIAS);
+		t = pipeline(0);
+		if (t == NULL)
+			syntaxerr(NULL);
+		t = block(TBANG, NULL, t);
+		break;
+
+	case TIME:
+		syniocf &= ~(KEYWORD|sALIAS);
+		t = pipeline(0);
+		if (t && t->type == TCOM) {
+			t->str = alloc(2, ATEMP);
+			/* TF_* flags */
+			t->str[0] = '\0';
+			t->str[1] = '\0';
+		}
+		t = block(TTIME, t, NULL);
+		break;
+
+	case FUNCTION:
+		musthave(LWORD, 0);
+		t = function_body(yylval.cp, true);
+		break;
+	}
+
+	while ((iop = synio(syniocf)) != NULL) {
+		if (iopn >= NUFILE)
+			yyerror("too many %ss\n", "redirection");
+		iops[iopn++] = iop;
+	}
+
+	if (iopn == 0) {
+		afree(iops, ATEMP);
+		t->ioact = NULL;
+	} else {
+		iops[iopn++] = NULL;
+		iops = aresize2(iops, iopn, sizeof(struct ioword *), ATEMP);
+		t->ioact = iops;
+	}
+
+	if (t->type == TCOM || t->type == TDBRACKET) {
+		XPput(args, NULL);
+		t->args = (const char **)XPclose(args);
+		XPput(vars, NULL);
+		t->vars = (char **)XPclose(vars);
+	} else {
+		XPfree(args);
+		XPfree(vars);
+	}
+
+	return (t);
+}
+
+static struct op *
+dogroup(void)
+{
+	int c;
+	struct op *list;
+
+	c = token(CONTIN|KEYWORD|sALIAS);
+	/*
+	 * A {...} can be used instead of do...done for for/select loops
+	 * but not for while/until loops - we don't need to check if it
+	 * is a while loop because it would have been parsed as part of
+	 * the conditional command list...
+	 */
+	if (c == DO)
+		c = DONE;
+	else if (c == '{')
+		c = '}';
+	else
+		syntaxerr(NULL);
+	list = c_list(true);
+	musthave(c, KEYWORD|sALIAS);
+	return (list);
+}
+
+static struct op *
+thenpart(void)
+{
+	struct op *t;
+
+	musthave(THEN, KEYWORD|sALIAS);
+	t = newtp(0);
+	t->left = c_list(true);
+	if (t->left == NULL)
+		syntaxerr(NULL);
+	t->right = elsepart();
+	return (t);
+}
+
+static struct op *
+elsepart(void)
+{
+	struct op *t;
+
+	switch (token(KEYWORD|sALIAS|VARASN)) {
+	case ELSE:
+		if ((t = c_list(true)) == NULL)
+			syntaxerr(NULL);
+		return (t);
+
+	case ELIF:
+		t = newtp(TELIF);
+		t->left = c_list(true);
+		t->right = thenpart();
+		return (t);
+
+	default:
+		REJECT;
+	}
+	return (NULL);
+}
+
+static struct op *
+caselist(void)
+{
+	struct op *t, *tl;
+	int c;
+
+	c = token(CONTIN|KEYWORD|sALIAS);
+	/* A {...} can be used instead of in...esac for case statements */
+	if (c == IN)
+		c = ESAC;
+	else if (c == '{')
+		c = '}';
+	else
+		syntaxerr(NULL);
+	t = tl = NULL;
+	/* no ALIAS here */
+	while ((tpeek(CONTIN|KEYWORD|ESACONLY)) != c) {
+		struct op *tc = casepart(c);
+		if (tl == NULL)
+			t = tl = tc, tl->right = NULL;
+		else
+			tl->right = tc, tl = tc;
+	}
+	musthave(c, KEYWORD|sALIAS);
+	return (t);
+}
+
+static struct op *
+casepart(int endtok)
+{
+	struct op *t;
+	XPtrV ptns;
+
+	XPinit(ptns, 16);
+	t = newtp(TPAT);
+	/* no ALIAS here */
+	if (token(CONTIN | KEYWORD) != '(')
+		REJECT;
+	do {
+		switch (token(0)) {
+		case LWORD:
+			break;
+		case '}':
+		case ESAC:
+			if (symbol != endtok) {
+				strdupx(yylval.cp,
+				    symbol == '}' ? Tcbrace : Tesac, ATEMP);
+				break;
+			}
+			/* FALLTHROUGH */
+		default:
+			syntaxerr(NULL);
+		}
+		XPput(ptns, yylval.cp);
+	} while (token(0) == '|');
+	REJECT;
+	XPput(ptns, NULL);
+	t->vars = (char **)XPclose(ptns);
+	musthave(')', 0);
+
+	t->left = c_list(true);
+
+	/* initialise to default for ;; or omitted */
+	t->u.charflag = ';';
+	/* SUSv4 requires the ;; except in the last casepart */
+	if ((tpeek(CONTIN|KEYWORD|sALIAS)) != endtok)
+		switch (symbol) {
+		default:
+			syntaxerr(NULL);
+		case BRKEV:
+			t->u.charflag = '|';
+			if (0)
+				/* FALLTHROUGH */
+		case BRKFT:
+			t->u.charflag = '&';
+			/* FALLTHROUGH */
+		case BREAK:
+			/* initialised above, but we need to eat the token */
+			ACCEPT;
+		}
+	return (t);
+}
+
+static struct op *
+function_body(char *name,
+    /* function foo { ... } vs foo() { .. } */
+    bool ksh_func)
+{
+	char *sname, *p;
+	struct op *t;
+
+	sname = wdstrip(name, 0);
+	/*-
+	 * Check for valid characters in name. POSIX and AT&T ksh93 say
+	 * only allow [a-zA-Z_0-9] but this allows more as old pdkshs
+	 * have allowed more; the following were never allowed:
+	 *	NUL TAB NL SP " $ & ' ( ) ; < = > \ ` |
+	 * C_QUOTE covers all but adds # * ? [ ]
+	 */
+	for (p = sname; *p; p++)
+		if (ctype(*p, C_QUOTE))
+			yyerror("%s: %s\n", sname, "invalid function name");
+
+	/*
+	 * Note that POSIX allows only compound statements after foo(),
+	 * sh and AT&T ksh allow any command, go with the later since it
+	 * shouldn't break anything. However, for function foo, AT&T ksh
+	 * only accepts an open-brace.
+	 */
+	if (ksh_func) {
+		if (tpeek(CONTIN|KEYWORD|sALIAS) == '(' /*)*/) {
+			/* function foo () { //}*/
+			ACCEPT;
+			musthave(')', 0);
+			/* degrade to POSIX function */
+			ksh_func = false;
+		}
+		musthave('{' /*}*/, CONTIN|KEYWORD|sALIAS);
+		REJECT;
+	}
+
+	t = newtp(TFUNCT);
+	t->str = sname;
+	t->u.ksh_func = tobool(ksh_func);
+	t->lineno = source->line;
+
+	if ((t->left = get_command(CONTIN)) == NULL) {
+		char *tv;
+		/*
+		 * Probably something like foo() followed by EOF or ';'.
+		 * This is accepted by sh and ksh88.
+		 * To make "typeset -f foo" work reliably (so its output can
+		 * be used as input), we pretend there is a colon here.
+		 */
+		t->left = newtp(TCOM);
+		/* (2 * sizeof(char *)) is small enough */
+		t->left->args = alloc(2 * sizeof(char *), ATEMP);
+		t->left->args[0] = tv = alloc(3, ATEMP);
+		tv[0] = CHAR;
+		tv[1] = ':';
+		tv[2] = EOS;
+		t->left->args[1] = NULL;
+		t->left->vars = alloc(sizeof(char *), ATEMP);
+		t->left->vars[0] = NULL;
+		t->left->lineno = 1;
+	}
+
+	return (t);
+}
+
+static char **
+wordlist(void)
+{
+	int c;
+	XPtrV args;
+
+	XPinit(args, 16);
+	/* POSIX does not do alias expansion here... */
+	if ((c = token(CONTIN|KEYWORD|sALIAS)) != IN) {
+		if (c != ';')
+			/* non-POSIX, but AT&T ksh accepts a ; here */
+			REJECT;
+		return (NULL);
+	}
+	while ((c = token(0)) == LWORD)
+		XPput(args, yylval.cp);
+	if (c != '\n' && c != ';')
+		syntaxerr(NULL);
+	XPput(args, NULL);
+	return ((char **)XPclose(args));
+}
+
+/*
+ * supporting functions
+ */
+
+static struct op *
+block(int type, struct op *t1, struct op *t2)
+{
+	struct op *t;
+
+	t = newtp(type);
+	t->left = t1;
+	t->right = t2;
+	return (t);
+}
+
+static const struct tokeninfo {
+	const char *name;
+	short val;
+	short reserved;
+} tokentab[] = {
+	/* Reserved words */
+	{ "if",		IF,	true },
+	{ "then",	THEN,	true },
+	{ "else",	ELSE,	true },
+	{ "elif",	ELIF,	true },
+	{ "fi",		FI,	true },
+	{ "case",	CASE,	true },
+	{ Tesac,	ESAC,	true },
+	{ "for",	FOR,	true },
+	{ Tselect,	SELECT,	true },
+	{ "while",	WHILE,	true },
+	{ "until",	UNTIL,	true },
+	{ "do",		DO,	true },
+	{ "done",	DONE,	true },
+	{ "in",		IN,	true },
+	{ Tfunction,	FUNCTION, true },
+	{ "time",	TIME,	true },
+	{ "{",		'{',	true },
+	{ Tcbrace,	'}',	true },
+	{ "!",		BANG,	true },
+	{ "[[",		DBRACKET, true },
+	/* Lexical tokens (0[EOF], LWORD and REDIR handled specially) */
+	{ "&&",		LOGAND,	false },
+	{ "||",		LOGOR,	false },
+	{ ";;",		BREAK,	false },
+	{ ";|",		BRKEV,	false },
+	{ ";&",		BRKFT,	false },
+	{ "((",		MDPAREN, false },
+	{ "|&",		COPROC,	false },
+	/* and some special cases... */
+	{ "newline",	'\n',	false },
+	{ NULL,		0,	false }
+};
+
+void
+initkeywords(void)
+{
+	struct tokeninfo const *tt;
+	struct tbl *p;
+
+	ktinit(APERM, &keywords,
+	    /* currently 28 keywords: 75% of 64 = 2^6 */
+	    6);
+	for (tt = tokentab; tt->name; tt++) {
+		if (tt->reserved) {
+			p = ktenter(&keywords, tt->name, hash(tt->name));
+			p->flag |= DEFINED|ISSET;
+			p->type = CKEYWD;
+			p->val.i = tt->val;
+		}
+	}
+}
+
+static void
+syntaxerr(const char *what)
+{
+	/* 2<<- is the longest redirection, I think */
+	char redir[6];
+	const char *s;
+	struct tokeninfo const *tt;
+	int c;
+
+	if (!what)
+		what = "unexpected";
+	REJECT;
+	c = token(0);
+ Again:
+	switch (c) {
+	case 0:
+		if (nesting.start_token) {
+			c = nesting.start_token;
+			source->errline = nesting.start_line;
+			what = "unmatched";
+			goto Again;
+		}
+		/* don't quote the EOF */
+		yyerror("%s: %s %s\n", Tsynerr, "unexpected", "EOF");
+		/* NOTREACHED */
+
+	case LWORD:
+		s = snptreef(NULL, 32, "%S", yylval.cp);
+		break;
+
+	case REDIR:
+		s = snptreef(redir, sizeof(redir), "%R", yylval.iop);
+		break;
+
+	default:
+		for (tt = tokentab; tt->name; tt++)
+			if (tt->val == c)
+			    break;
+		if (tt->name)
+			s = tt->name;
+		else {
+			if (c > 0 && c < 256) {
+				redir[0] = c;
+				redir[1] = '\0';
+			} else
+				shf_snprintf(redir, sizeof(redir),
+					"?%d", c);
+			s = redir;
+		}
+	}
+	yyerror("%s: '%s' %s\n", Tsynerr, s, what);
+}
+
+static void
+nesting_push(struct nesting_state *save, int tok)
+{
+	*save = nesting;
+	nesting.start_token = tok;
+	nesting.start_line = source->line;
+}
+
+static void
+nesting_pop(struct nesting_state *saved)
+{
+	nesting = *saved;
+}
+
+static struct op *
+newtp(int type)
+{
+	struct op *t;
+
+	t = alloc(sizeof(struct op), ATEMP);
+	t->type = type;
+	t->u.evalflags = 0;
+	t->args = NULL;
+	t->vars = NULL;
+	t->ioact = NULL;
+	t->left = t->right = NULL;
+	t->str = NULL;
+	return (t);
+}
+
+struct op *
+compile(Source *s, bool skiputf8bom)
+{
+	nesting.start_token = 0;
+	nesting.start_line = 0;
+	herep = heres;
+	source = s;
+	if (skiputf8bom)
+		yyskiputf8bom();
+	yyparse();
+	return (outtree);
+}
+
+/*-
+ * This kludge exists to take care of sh/AT&T ksh oddity in which
+ * the arguments of alias/export/readonly/typeset have no field
+ * splitting, file globbing, or (normal) tilde expansion done.
+ * AT&T ksh seems to do something similar to this since
+ *	$ touch a=a; typeset a=[ab]; echo "$a"
+ *	a=[ab]
+ *	$ x=typeset; $x a=[ab]; echo "$a"
+ *	a=a
+ *	$
+ */
+static int
+assign_command(const char *s)
+{
+	if (!*s)
+		return (0);
+	return ((strcmp(s, Talias) == 0) ||
+	    (strcmp(s, Texport) == 0) ||
+	    (strcmp(s, Treadonly) == 0) ||
+	    (strcmp(s, Ttypeset) == 0));
+}
+
+/* Check if we are in the middle of reading an alias */
+static int
+inalias(struct source *s)
+{
+	for (; s && s->type == SALIAS; s = s->next)
+		if (!(s->flags & SF_ALIASEND))
+			return (1);
+	return (0);
+}
+
+
+/*
+ * Order important - indexed by Test_meta values
+ * Note that ||, &&, ( and ) can't appear in as unquoted strings
+ * in normal shell input, so these can be interpreted unambiguously
+ * in the evaluation pass.
+ */
+static const char dbtest_or[] = { CHAR, '|', CHAR, '|', EOS };
+static const char dbtest_and[] = { CHAR, '&', CHAR, '&', EOS };
+static const char dbtest_not[] = { CHAR, '!', EOS };
+static const char dbtest_oparen[] = { CHAR, '(', EOS };
+static const char dbtest_cparen[] = { CHAR, ')', EOS };
+const char * const dbtest_tokens[] = {
+	dbtest_or, dbtest_and, dbtest_not,
+	dbtest_oparen, dbtest_cparen
+};
+static const char db_close[] = { CHAR, ']', CHAR, ']', EOS };
+static const char db_lthan[] = { CHAR, '<', EOS };
+static const char db_gthan[] = { CHAR, '>', EOS };
+
+/*
+ * Test if the current token is a whatever. Accepts the current token if
+ * it is. Returns 0 if it is not, non-zero if it is (in the case of
+ * TM_UNOP and TM_BINOP, the returned value is a Test_op).
+ */
+static Test_op
+dbtestp_isa(Test_env *te, Test_meta meta)
+{
+	int c = tpeek(ARRAYVAR | (meta == TM_BINOP ? 0 : CONTIN));
+	bool uqword;
+	char *save = NULL;
+	Test_op ret = TO_NONOP;
+
+	/* unquoted word? */
+	uqword = c == LWORD && *ident;
+
+	if (meta == TM_OR)
+		ret = c == LOGOR ? TO_NONNULL : TO_NONOP;
+	else if (meta == TM_AND)
+		ret = c == LOGAND ? TO_NONNULL : TO_NONOP;
+	else if (meta == TM_NOT)
+		ret = (uqword && !strcmp(yylval.cp,
+		    dbtest_tokens[(int)TM_NOT])) ? TO_NONNULL : TO_NONOP;
+	else if (meta == TM_OPAREN)
+		ret = c == '(' /*)*/ ? TO_NONNULL : TO_NONOP;
+	else if (meta == TM_CPAREN)
+		ret = c == /*(*/ ')' ? TO_NONNULL : TO_NONOP;
+	else if (meta == TM_UNOP || meta == TM_BINOP) {
+		if (meta == TM_BINOP && c == REDIR &&
+		    (yylval.iop->flag == IOREAD || yylval.iop->flag == IOWRITE)) {
+			ret = TO_NONNULL;
+			save = wdcopy(yylval.iop->flag == IOREAD ?
+			    db_lthan : db_gthan, ATEMP);
+		} else if (uqword && (ret = test_isop(meta, ident)))
+			save = yylval.cp;
+	} else
+		/* meta == TM_END */
+		ret = (uqword && !strcmp(yylval.cp,
+		    db_close)) ? TO_NONNULL : TO_NONOP;
+	if (ret != TO_NONOP) {
+		ACCEPT;
+		if ((unsigned int)meta < NELEM(dbtest_tokens))
+			save = wdcopy(dbtest_tokens[(int)meta], ATEMP);
+		if (save)
+			XPput(*te->pos.av, save);
+	}
+	return (ret);
+}
+
+static const char *
+dbtestp_getopnd(Test_env *te, Test_op op MKSH_A_UNUSED,
+    bool do_eval MKSH_A_UNUSED)
+{
+	int c = tpeek(ARRAYVAR);
+
+	if (c != LWORD)
+		return (NULL);
+
+	ACCEPT;
+	XPput(*te->pos.av, yylval.cp);
+
+	return (null);
+}
+
+static int
+dbtestp_eval(Test_env *te MKSH_A_UNUSED, Test_op op MKSH_A_UNUSED,
+    const char *opnd1 MKSH_A_UNUSED, const char *opnd2 MKSH_A_UNUSED,
+    bool do_eval MKSH_A_UNUSED)
+{
+	return (1);
+}
+
+static void
+dbtestp_error(Test_env *te, int offset, const char *msg)
+{
+	te->flags |= TEF_ERROR;
+
+	if (offset < 0) {
+		REJECT;
+		/* Kludgy to say the least... */
+		symbol = LWORD;
+		yylval.cp = *(XPptrv(*te->pos.av) + XPsize(*te->pos.av) +
+		    offset);
+	}
+	syntaxerr(msg);
+}
+
+#if HAVE_SELECT
+
+#ifndef EOVERFLOW
+#ifdef ERANGE
+#define EOVERFLOW	ERANGE
+#else
+#define EOVERFLOW	EINVAL
+#endif
+#endif
+
+bool
+parse_usec(const char *s, struct timeval *tv)
+{
+	struct timeval tt;
+	int i;
+
+	tv->tv_sec = 0;
+	/* parse integral part */
+	while (ksh_isdigit(*s)) {
+		tt.tv_sec = tv->tv_sec * 10 + (*s++ - '0');
+		if (tt.tv_sec / 10 != tv->tv_sec) {
+			errno = EOVERFLOW;
+			return (true);
+		}
+		tv->tv_sec = tt.tv_sec;
+	}
+
+	tv->tv_usec = 0;
+	if (!*s)
+		/* no decimal fraction */
+		return (false);
+	else if (*s++ != '.') {
+		/* junk after integral part */
+		errno = EINVAL;
+		return (true);
+	}
+
+	/* parse decimal fraction */
+	i = 100000;
+	while (ksh_isdigit(*s)) {
+		tv->tv_usec += i * (*s++ - '0');
+		if (i == 1)
+			break;
+		i /= 10;
+	}
+	/* check for junk after fractional part */
+	while (ksh_isdigit(*s))
+		++s;
+	if (*s) {
+		errno = EINVAL;
+		return (true);
+	}
+
+	/* end of input string reached, no errors */
+	return (false);
+}
+#endif
+
+/*
+ * Helper function called from within lex.c:yylex() to parse
+ * a COMSUB recursively using the main shell parser and lexer
+ */
+char *
+yyrecursive(int subtype MKSH_A_UNUSED)
+{
+	struct op *t;
+	char *cp;
+	struct yyrecursive_state *ys;
+	int stok, etok;
+
+	if (subtype != COMSUB) {
+		stok = '{';
+		etok = '}';
+	} else {
+		stok = '(';
+		etok = ')';
+	}
+
+	ys = alloc(sizeof(struct yyrecursive_state), ATEMP);
+
+	/* tell the lexer to accept a closing parenthesis as EOD */
+	ys->old_nesting_type = subshell_nesting_type;
+	subshell_nesting_type = etok;
+
+	/* push reject state, parse recursively, pop reject state */
+	ys->old_reject = reject;
+	ys->old_symbol = symbol;
+	ACCEPT;
+	ys->old_herep = herep;
+	ys->old_salias = sALIAS;
+	sALIAS = 0;
+	ys->next = e->yyrecursive_statep;
+	e->yyrecursive_statep = ys;
+	/* we use TPAREN as a helper container here */
+	t = nested(TPAREN, stok, etok);
+	yyrecursive_pop(false);
+
+	/* t->left because nested(TPAREN, ...) hides our goodies there */
+	cp = snptreef(NULL, 0, "%T", t->left);
+	tfree(t, ATEMP);
+
+	return (cp);
+}
+
+void
+yyrecursive_pop(bool popall)
+{
+	struct yyrecursive_state *ys;
+
+ popnext:
+	if (!(ys = e->yyrecursive_statep))
+		return;
+	e->yyrecursive_statep = ys->next;
+
+	sALIAS = ys->old_salias;
+	herep = ys->old_herep;
+	reject = ys->old_reject;
+	symbol = ys->old_symbol;
+
+	subshell_nesting_type = ys->old_nesting_type;
+
+	afree(ys, ATEMP);
+	if (popall)
+		goto popnext;
+}

Deleted: vendor/MirOS/mksh/R50/tree.c
===================================================================
--- vendor/MirOS/mksh/dist/tree.c	2014-06-30 23:58:12 UTC (rev 6706)
+++ vendor/MirOS/mksh/R50/tree.c	2014-07-01 12:01:17 UTC (rev 6708)
@@ -1,1143 +0,0 @@
-/*	$OpenBSD: tree.c,v 1.20 2012/06/27 07:17:19 otto Exp $	*/
-
-/*-
- * Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
- *		 2011, 2012, 2013
- *	Thorsten Glaser <tg at mirbsd.org>
- *
- * Provided that these terms and disclaimer and all copyright notices
- * are retained or reproduced in an accompanying document, permission
- * is granted to deal in this work without restriction, including un-
- * limited rights to use, publicly perform, distribute, sell, modify,
- * merge, give away, or sublicence.
- *
- * This work is provided "AS IS" and WITHOUT WARRANTY of any kind, to
- * the utmost extent permitted by applicable law, neither express nor
- * implied; without malicious intent or gross negligence. In no event
- * may a licensor, author or contributor be held liable for indirect,
- * direct, other damage, loss, or other issues arising in any way out
- * of dealing in the work, even if advised of the possibility of such
- * damage or existence of a defect, except proven that it results out
- * of said person's immediate fault when using the work as intended.
- */
-
-#include "sh.h"
-
-__RCSID("$MirOS: src/bin/mksh/tree.c,v 1.71 2013/07/26 20:33:24 tg Exp $");
-
-#define INDENT	8
-
-static void ptree(struct op *, int, struct shf *);
-static void pioact(struct shf *, struct ioword *);
-static const char *wdvarput(struct shf *, const char *, int, int);
-static void vfptreef(struct shf *, int, const char *, va_list);
-static struct ioword **iocopy(struct ioword **, Area *);
-static void iofree(struct ioword **, Area *);
-
-/* "foo& ; bar" and "foo |& ; bar" are invalid */
-static bool prevent_semicolon;
-
-static const char Telif_pT[] = "elif %T";
-
-/*
- * print a command tree
- */
-static void
-ptree(struct op *t, int indent, struct shf *shf)
-{
-	const char **w;
-	struct ioword **ioact;
-	struct op *t1;
-	int i;
-
- Chain:
-	if (t == NULL)
-		return;
-	switch (t->type) {
-	case TCOM:
-		prevent_semicolon = false;
-		/*
-		 * special-case 'var=<<EOF' (rough; see
-		 * exec.c:execute() for full code)
-		 */
-		if (
-		    /* we have zero arguments, i.e. no programme to run */
-		    t->args[0] == NULL &&
-		    /* we have exactly one variable assignment */
-		    t->vars[0] != NULL && t->vars[1] == NULL &&
-		    /* we have exactly one I/O redirection */
-		    t->ioact != NULL && t->ioact[0] != NULL &&
-		    t->ioact[1] == NULL &&
-		    /* of type "here document" (or "here string") */
-		    (t->ioact[0]->flag & IOTYPE) == IOHERE) {
-			fptreef(shf, indent, "%S", t->vars[0]);
-			break;
-		}
-
-		if (t->vars) {
-			w = (const char **)t->vars;
-			while (*w)
-				fptreef(shf, indent, "%S ", *w++);
-		} else
-			shf_puts("#no-vars# ", shf);
-		if (t->args) {
-			w = t->args;
-			while (*w)
-				fptreef(shf, indent, "%S ", *w++);
-		} else
-			shf_puts("#no-args# ", shf);
-		break;
-	case TEXEC:
-		t = t->left;
-		goto Chain;
-	case TPAREN:
-		fptreef(shf, indent + 2, "( %T) ", t->left);
-		break;
-	case TPIPE:
-		fptreef(shf, indent, "%T| ", t->left);
-		t = t->right;
-		goto Chain;
-	case TLIST:
-		fptreef(shf, indent, "%T%;", t->left);
-		t = t->right;
-		goto Chain;
-	case TOR:
-	case TAND:
-		fptreef(shf, indent, "%T%s %T",
-		    t->left, (t->type == TOR) ? "||" : "&&", t->right);
-		break;
-	case TBANG:
-		shf_puts("! ", shf);
-		prevent_semicolon = false;
-		t = t->right;
-		goto Chain;
-	case TDBRACKET:
-		w = t->args;
-		shf_puts("[[", shf);
-		while (*w)
-			fptreef(shf, indent, " %S", *w++);
-		shf_puts(" ]] ", shf);
-		break;
-	case TSELECT:
-	case TFOR:
-		fptreef(shf, indent, "%s %s ",
-		    (t->type == TFOR) ? "for" : Tselect, t->str);
-		if (t->vars != NULL) {
-			shf_puts("in ", shf);
-			w = (const char **)t->vars;
-			while (*w)
-				fptreef(shf, indent, "%S ", *w++);
-			fptreef(shf, indent, "%;");
-		}
-		fptreef(shf, indent + INDENT, "do%N%T", t->left);
-		fptreef(shf, indent, "%;done ");
-		break;
-	case TCASE:
-		fptreef(shf, indent, "case %S in", t->str);
-		for (t1 = t->left; t1 != NULL; t1 = t1->right) {
-			fptreef(shf, indent, "%N(");
-			w = (const char **)t1->vars;
-			while (*w) {
-				fptreef(shf, indent, "%S%c", *w,
-				    (w[1] != NULL) ? '|' : ')');
-				++w;
-			}
-			fptreef(shf, indent + INDENT, "%N%T%N;%c", t1->left,
-			    t1->u.charflag);
-		}
-		fptreef(shf, indent, "%Nesac ");
-		break;
-#ifdef DEBUG
-	case TELIF:
-		internal_errorf("TELIF in tree.c:ptree() unexpected");
-		/* FALLTHROUGH */
-#endif
-	case TIF:
-		i = 2;
-		t1 = t;
-		goto process_TIF;
-		do {
-			t1 = t1->right;
-			i = 0;
-			fptreef(shf, indent, "%;");
- process_TIF:
-			/* 5 == strlen("elif ") */
-			fptreef(shf, indent + 5 - i, Telif_pT + i, t1->left);
-			t1 = t1->right;
-			if (t1->left != NULL) {
-				fptreef(shf, indent, "%;");
-				fptreef(shf, indent + INDENT, "%s%N%T",
-				    "then", t1->left);
-			}
-		} while (t1->right && t1->right->type == TELIF);
-		if (t1->right != NULL) {
-			fptreef(shf, indent, "%;");
-			fptreef(shf, indent + INDENT, "%s%N%T",
-			    "else", t1->right);
-		}
-		fptreef(shf, indent, "%;fi ");
-		break;
-	case TWHILE:
-	case TUNTIL:
-		/* 6 == strlen("while "/"until ") */
-		fptreef(shf, indent + 6, "%s %T",
-		    (t->type == TWHILE) ? "while" : "until",
-		    t->left);
-		fptreef(shf, indent, "%;");
-		fptreef(shf, indent + INDENT, "do%N%T", t->right);
-		fptreef(shf, indent, "%;done ");
-		break;
-	case TBRACE:
-		fptreef(shf, indent + INDENT, "{%N%T", t->left);
-		fptreef(shf, indent, "%;} ");
-		break;
-	case TCOPROC:
-		fptreef(shf, indent, "%T|& ", t->left);
-		prevent_semicolon = true;
-		break;
-	case TASYNC:
-		fptreef(shf, indent, "%T& ", t->left);
-		prevent_semicolon = true;
-		break;
-	case TFUNCT:
-		fpFUNCTf(shf, indent, tobool(t->u.ksh_func), t->str, t->left);
-		break;
-	case TTIME:
-		fptreef(shf, indent, "%s %T", "time", t->left);
-		break;
-	default:
-		shf_puts("<botch>", shf);
-		prevent_semicolon = false;
-		break;
-	}
-	if ((ioact = t->ioact) != NULL) {
-		bool need_nl = false;
-
-		while (*ioact != NULL)
-			pioact(shf, *ioact++);
-		/* Print here documents after everything else... */
-		ioact = t->ioact;
-		while (*ioact != NULL) {
-			struct ioword *iop = *ioact++;
-
-			/* heredoc is NULL when tracing (set -x) */
-			if ((iop->flag & (IOTYPE | IOHERESTR)) == IOHERE &&
-			    iop->heredoc) {
-				shf_putc('\n', shf);
-				shf_puts(iop->heredoc, shf);
-				fptreef(shf, indent, "%s",
-				    iop->flag & IONDELIM ? "<<" :
-				    evalstr(iop->delim, 0));
-				need_nl = true;
-			}
-		}
-		/*
-		 * Last delimiter must be followed by a newline (this
-		 * often leads to an extra blank line, but it's not
-		 * worth worrying about)
-		 */
-		if (need_nl) {
-			shf_putc('\n', shf);
-			prevent_semicolon = true;
-		}
-	}
-}
-
-static void
-pioact(struct shf *shf, struct ioword *iop)
-{
-	int flag = iop->flag;
-	int type = flag & IOTYPE;
-	int expected;
-
-	expected = (type == IOREAD || type == IORDWR || type == IOHERE) ? 0 :
-	    (type == IOCAT || type == IOWRITE) ? 1 :
-	    (type == IODUP && (iop->unit == !(flag & IORDUP))) ? iop->unit :
-	    iop->unit + 1;
-	if (iop->unit != expected)
-		shf_fprintf(shf, "%d", iop->unit);
-
-	switch (type) {
-	case IOREAD:
-		shf_putc('<', shf);
-		break;
-	case IOHERE:
-		shf_puts("<<", shf);
-		if (flag & IOSKIP)
-			shf_putc('-', shf);
-		break;
-	case IOCAT:
-		shf_puts(">>", shf);
-		break;
-	case IOWRITE:
-		shf_putc('>', shf);
-		if (flag & IOCLOB)
-			shf_putc('|', shf);
-		break;
-	case IORDWR:
-		shf_puts("<>", shf);
-		break;
-	case IODUP:
-		shf_puts(flag & IORDUP ? "<&" : ">&", shf);
-		break;
-	}
-	/* name/delim are NULL when printing syntax errors */
-	if (type == IOHERE) {
-		if (iop->delim)
-			wdvarput(shf, iop->delim, 0, WDS_TPUTS);
-		if (iop->flag & IOHERESTR)
-			shf_putc(' ', shf);
-	} else if (iop->name) {
-		if (iop->flag & IONAMEXP)
-			print_value_quoted(shf, iop->name);
-		else
-			wdvarput(shf, iop->name, 0, WDS_TPUTS);
-		shf_putc(' ', shf);
-	}
-	prevent_semicolon = false;
-}
-
-/* variant of fputs for ptreef and wdstrip */
-static const char *
-wdvarput(struct shf *shf, const char *wp, int quotelevel, int opmode)
-{
-	int c;
-	const char *cs;
-
-	/*-
-	 * problems:
-	 *	`...` -> $(...)
-	 *	'foo' -> "foo"
-	 *	x${foo:-"hi"} -> x${foo:-hi} unless WDS_TPUTS
-	 *	x${foo:-'hi'} -> x${foo:-hi} unless WDS_KEEPQ
-	 * could change encoding to:
-	 *	OQUOTE ["'] ... CQUOTE ["']
-	 *	COMSUB [(`] ...\0	(handle $ ` \ and maybe " in `...` case)
-	 */
-	while (/* CONSTCOND */ 1)
-		switch (*wp++) {
-		case EOS:
-			return (--wp);
-		case ADELIM:
-		case CHAR:
-			c = *wp++;
-			if ((opmode & WDS_MAGIC) &&
-			    (ISMAGIC(c) || c == '[' || c == '!' ||
-			    c == '-' || c == ']' || c == '*' || c == '?'))
-				shf_putc(MAGIC, shf);
-			shf_putc(c, shf);
-			break;
-		case QCHAR: {
-			bool doq;
-
-			c = *wp++;
-			doq = (c == '"' || c == '`' || c == '$' || c == '\\');
-			if (opmode & WDS_TPUTS) {
-				if (quotelevel == 0)
-					doq = true;
-			} else {
-				if (!(opmode & WDS_KEEPQ))
-					doq = false;
-			}
-			if (doq)
-				shf_putc('\\', shf);
-			shf_putc(c, shf);
-			break;
-		}
-		case COMSUB:
-			shf_puts("$(", shf);
-			cs = ")";
- pSUB:
-			while ((c = *wp++) != 0)
-				shf_putc(c, shf);
-			shf_puts(cs, shf);
-			break;
-		case FUNSUB:
-			c = ' ';
-			if (0)
-				/* FALLTHROUGH */
-		case VALSUB:
-			  c = '|';
-			shf_putc('$', shf);
-			shf_putc('{', shf);
-			shf_putc(c, shf);
-			cs = ";}";
-			goto pSUB;
-		case EXPRSUB:
-			shf_puts("$((", shf);
-			cs = "))";
-			goto pSUB;
-		case OQUOTE:
-			if (opmode & WDS_TPUTS) {
-				quotelevel++;
-				shf_putc('"', shf);
-			}
-			break;
-		case CQUOTE:
-			if (opmode & WDS_TPUTS) {
-				if (quotelevel)
-					quotelevel--;
-				shf_putc('"', shf);
-			}
-			break;
-		case OSUBST:
-			shf_putc('$', shf);
-			if (*wp++ == '{')
-				shf_putc('{', shf);
-			while ((c = *wp++) != 0)
-				shf_putc(c, shf);
-			wp = wdvarput(shf, wp, 0, opmode);
-			break;
-		case CSUBST:
-			if (*wp++ == '}')
-				shf_putc('}', shf);
-			return (wp);
-		case OPAT:
-			if (opmode & WDS_MAGIC) {
-				shf_putc(MAGIC, shf);
-				shf_putchar(*wp++ | 0x80, shf);
-			} else {
-				shf_putchar(*wp++, shf);
-				shf_putc('(', shf);
-			}
-			break;
-		case SPAT:
-			c = '|';
-			if (0)
-		case CPAT:
-				c = /*(*/ ')';
-			if (opmode & WDS_MAGIC)
-				shf_putc(MAGIC, shf);
-			shf_putc(c, shf);
-			break;
-		}
-}
-
-/*
- * this is the _only_ way to reliably handle
- * variable args with an ANSI compiler
- */
-/* VARARGS */
-void
-fptreef(struct shf *shf, int indent, const char *fmt, ...)
-{
-	va_list va;
-
-	va_start(va, fmt);
-	vfptreef(shf, indent, fmt, va);
-	va_end(va);
-}
-
-/* VARARGS */
-char *
-snptreef(char *s, ssize_t n, const char *fmt, ...)
-{
-	va_list va;
-	struct shf shf;
-
-	shf_sopen(s, n, SHF_WR | (s ? 0 : SHF_DYNAMIC), &shf);
-
-	va_start(va, fmt);
-	vfptreef(&shf, 0, fmt, va);
-	va_end(va);
-
-	/* shf_sclose NUL terminates */
-	return (shf_sclose(&shf));
-}
-
-static void
-vfptreef(struct shf *shf, int indent, const char *fmt, va_list va)
-{
-	int c;
-
-	while ((c = *fmt++)) {
-		if (c == '%') {
-			switch ((c = *fmt++)) {
-			case 'c':
-				/* character (octet, probably) */
-				shf_putchar(va_arg(va, int), shf);
-				break;
-			case 's':
-				/* string */
-				shf_puts(va_arg(va, char *), shf);
-				break;
-			case 'S':
-				/* word */
-				wdvarput(shf, va_arg(va, char *), 0, WDS_TPUTS);
-				break;
-			case 'd':
-				/* signed decimal */
-				shf_fprintf(shf, "%d", va_arg(va, int));
-				break;
-			case 'u':
-				/* unsigned decimal */
-				shf_fprintf(shf, "%u", va_arg(va, unsigned int));
-				break;
-			case 'T':
-				/* format tree */
-				ptree(va_arg(va, struct op *), indent, shf);
-				goto dont_trash_prevent_semicolon;
-			case ';':
-				/* newline or ; */
-			case 'N':
-				/* newline or space */
-				if (shf->flags & SHF_STRING) {
-					if (c == ';' && !prevent_semicolon)
-						shf_putc(';', shf);
-					shf_putc(' ', shf);
-				} else {
-					int i;
-
-					shf_putc('\n', shf);
-					i = indent;
-					while (i >= 8) {
-						shf_putc('\t', shf);
-						i -= 8;
-					}
-					while (i--)
-						shf_putc(' ', shf);
-				}
-				break;
-			case 'R':
-				/* I/O redirection */
-				pioact(shf, va_arg(va, struct ioword *));
-				break;
-			default:
-				shf_putc(c, shf);
-				break;
-			}
-		} else
-			shf_putc(c, shf);
-		prevent_semicolon = false;
- dont_trash_prevent_semicolon:
-		;
-	}
-}
-
-/*
- * copy tree (for function definition)
- */
-struct op *
-tcopy(struct op *t, Area *ap)
-{
-	struct op *r;
-	const char **tw;
-	char **rw;
-
-	if (t == NULL)
-		return (NULL);
-
-	r = alloc(sizeof(struct op), ap);
-
-	r->type = t->type;
-	r->u.evalflags = t->u.evalflags;
-
-	if (t->type == TCASE)
-		r->str = wdcopy(t->str, ap);
-	else
-		strdupx(r->str, t->str, ap);
-
-	if (t->vars == NULL)
-		r->vars = NULL;
-	else {
-		tw = (const char **)t->vars;
-		while (*tw)
-			++tw;
-		rw = r->vars = alloc2(tw - (const char **)t->vars + 1,
-		    sizeof(*tw), ap);
-		tw = (const char **)t->vars;
-		while (*tw)
-			*rw++ = wdcopy(*tw++, ap);
-		*rw = NULL;
-	}
-
-	if (t->args == NULL)
-		r->args = NULL;
-	else {
-		tw = t->args;
-		while (*tw)
-			++tw;
-		r->args = (const char **)(rw = alloc2(tw - t->args + 1,
-		    sizeof(*tw), ap));
-		tw = t->args;
-		while (*tw)
-			*rw++ = wdcopy(*tw++, ap);
-		*rw = NULL;
-	}
-
-	r->ioact = (t->ioact == NULL) ? NULL : iocopy(t->ioact, ap);
-
-	r->left = tcopy(t->left, ap);
-	r->right = tcopy(t->right, ap);
-	r->lineno = t->lineno;
-
-	return (r);
-}
-
-char *
-wdcopy(const char *wp, Area *ap)
-{
-	size_t len;
-
-	len = wdscan(wp, EOS) - wp;
-	return (memcpy(alloc(len, ap), wp, len));
-}
-
-/* return the position of prefix c in wp plus 1 */
-const char *
-wdscan(const char *wp, int c)
-{
-	int nest = 0;
-
-	while (/* CONSTCOND */ 1)
-		switch (*wp++) {
-		case EOS:
-			return (wp);
-		case ADELIM:
-			if (c == ADELIM)
-				return (wp + 1);
-			/* FALLTHROUGH */
-		case CHAR:
-		case QCHAR:
-			wp++;
-			break;
-		case COMSUB:
-		case FUNSUB:
-		case VALSUB:
-		case EXPRSUB:
-			while (*wp++ != 0)
-				;
-			break;
-		case OQUOTE:
-		case CQUOTE:
-			break;
-		case OSUBST:
-			nest++;
-			while (*wp++ != '\0')
-				;
-			break;
-		case CSUBST:
-			wp++;
-			if (c == CSUBST && nest == 0)
-				return (wp);
-			nest--;
-			break;
-		case OPAT:
-			nest++;
-			wp++;
-			break;
-		case SPAT:
-		case CPAT:
-			if (c == wp[-1] && nest == 0)
-				return (wp);
-			if (wp[-1] == CPAT)
-				nest--;
-			break;
-		default:
-			internal_warningf(
-			    "wdscan: unknown char 0x%x (carrying on)",
-			    wp[-1]);
-		}
-}
-
-/*
- * return a copy of wp without any of the mark up characters and with
- * quote characters (" ' \) stripped. (string is allocated from ATEMP)
- */
-char *
-wdstrip(const char *wp, int opmode)
-{
-	struct shf shf;
-
-	shf_sopen(NULL, 32, SHF_WR | SHF_DYNAMIC, &shf);
-	wdvarput(&shf, wp, 0, opmode);
-	/* shf_sclose NUL terminates */
-	return (shf_sclose(&shf));
-}
-
-static struct ioword **
-iocopy(struct ioword **iow, Area *ap)
-{
-	struct ioword **ior;
-	int i;
-
-	ior = iow;
-	while (*ior)
-		++ior;
-	ior = alloc2(ior - iow + 1, sizeof(struct ioword *), ap);
-
-	for (i = 0; iow[i] != NULL; i++) {
-		struct ioword *p, *q;
-
-		p = iow[i];
-		q = alloc(sizeof(struct ioword), ap);
-		ior[i] = q;
-		*q = *p;
-		if (p->name != NULL)
-			q->name = wdcopy(p->name, ap);
-		if (p->delim != NULL)
-			q->delim = wdcopy(p->delim, ap);
-		if (p->heredoc != NULL)
-			strdupx(q->heredoc, p->heredoc, ap);
-	}
-	ior[i] = NULL;
-
-	return (ior);
-}
-
-/*
- * free tree (for function definition)
- */
-void
-tfree(struct op *t, Area *ap)
-{
-	char **w;
-
-	if (t == NULL)
-		return;
-
-	if (t->str != NULL)
-		afree(t->str, ap);
-
-	if (t->vars != NULL) {
-		for (w = t->vars; *w != NULL; w++)
-			afree(*w, ap);
-		afree(t->vars, ap);
-	}
-
-	if (t->args != NULL) {
-		/*XXX we assume the caller is right */
-		union mksh_ccphack cw;
-
-		cw.ro = t->args;
-		for (w = cw.rw; *w != NULL; w++)
-			afree(*w, ap);
-		afree(t->args, ap);
-	}
-
-	if (t->ioact != NULL)
-		iofree(t->ioact, ap);
-
-	tfree(t->left, ap);
-	tfree(t->right, ap);
-
-	afree(t, ap);
-}
-
-static void
-iofree(struct ioword **iow, Area *ap)
-{
-	struct ioword **iop;
-	struct ioword *p;
-
-	iop = iow;
-	while ((p = *iop++) != NULL) {
-		if (p->name != NULL)
-			afree(p->name, ap);
-		if (p->delim != NULL)
-			afree(p->delim, ap);
-		if (p->heredoc != NULL)
-			afree(p->heredoc, ap);
-		afree(p, ap);
-	}
-	afree(iow, ap);
-}
-
-void
-fpFUNCTf(struct shf *shf, int i, bool isksh, const char *k, struct op *v)
-{
-	if (isksh)
-		fptreef(shf, i, "%s %s %T", Tfunction, k, v);
-	else
-		fptreef(shf, i, "%s() %T", k, v);
-}
-
-
-/* for jobs.c */
-void
-vistree(char *dst, size_t sz, struct op *t)
-{
-	unsigned int c;
-	char *cp, *buf;
-	size_t n;
-
-	buf = alloc(sz + 16, ATEMP);
-	snptreef(buf, sz + 16, "%T", t);
-	cp = buf;
- vist_loop:
-	if (UTFMODE && (n = utf_mbtowc(&c, cp)) != (size_t)-1) {
-		if (c == 0 || n >= sz)
-			/* NUL or not enough free space */
-			goto vist_out;
-		/* copy multibyte char */
-		sz -= n;
-		while (n--)
-			*dst++ = *cp++;
-		goto vist_loop;
-	}
-	if (--sz == 0 || (c = (unsigned char)(*cp++)) == 0)
-		/* NUL or not enough free space */
-		goto vist_out;
-	if ((c & 0x60) == 0 || (c & 0x7F) == 0x7F) {
-		/* C0 or C1 control character or DEL */
-		if (--sz == 0)
-			/* not enough free space for two chars */
-			goto vist_out;
-		*dst++ = (c & 0x80) ? '$' : '^';
-		c = (c & 0x7F) ^ 0x40;
-	} else if (UTFMODE && c > 0x7F) {
-		/* better not try to display broken multibyte chars */
-		c = '?';
-	}
-	*dst++ = c;
-	goto vist_loop;
-
- vist_out:
-	*dst = '\0';
-	afree(buf, ATEMP);
-}
-
-#ifdef DEBUG
-void
-dumpchar(struct shf *shf, int c)
-{
-	if (((c & 0x60) == 0) || ((c & 0x7F) == 0x7F)) {
-		/* C0 or C1 control character or DEL */
-		shf_putc((c & 0x80) ? '$' : '^', shf);
-		c = (c & 0x7F) ^ 0x40;
-	}
-	shf_putc(c, shf);
-}
-
-/* see: wdvarput */
-static const char *
-dumpwdvar_i(struct shf *shf, const char *wp, int quotelevel)
-{
-	int c;
-
-	while (/* CONSTCOND */ 1) {
-		switch(*wp++) {
-		case EOS:
-			shf_puts("EOS", shf);
-			return (--wp);
-		case ADELIM:
-			shf_puts("ADELIM=", shf);
-			if (0)
-		case CHAR:
-				shf_puts("CHAR=", shf);
-			dumpchar(shf, *wp++);
-			break;
-		case QCHAR:
-			shf_puts("QCHAR<", shf);
-			c = *wp++;
-			if (quotelevel == 0 ||
-			    (c == '"' || c == '`' || c == '$' || c == '\\'))
-				shf_putc('\\', shf);
-			dumpchar(shf, c);
-			goto closeandout;
-		case COMSUB:
-			shf_puts("COMSUB<", shf);
- dumpsub:
-			while ((c = *wp++) != 0)
-				dumpchar(shf, c);
- closeandout:
-			shf_putc('>', shf);
-			break;
-		case FUNSUB:
-			shf_puts("FUNSUB<", shf);
-			goto dumpsub;
-		case VALSUB:
-			shf_puts("VALSUB<", shf);
-			goto dumpsub;
-		case EXPRSUB:
-			shf_puts("EXPRSUB<", shf);
-			goto dumpsub;
-		case OQUOTE:
-			shf_fprintf(shf, "OQUOTE{%d", ++quotelevel);
-			break;
-		case CQUOTE:
-			shf_fprintf(shf, "%d}CQUOTE", quotelevel);
-			if (quotelevel)
-				quotelevel--;
-			else
-				shf_puts("(err)", shf);
-			break;
-		case OSUBST:
-			shf_puts("OSUBST(", shf);
-			dumpchar(shf, *wp++);
-			shf_puts(")[", shf);
-			while ((c = *wp++) != 0)
-				dumpchar(shf, c);
-			shf_putc('|', shf);
-			wp = dumpwdvar_i(shf, wp, 0);
-			break;
-		case CSUBST:
-			shf_puts("]CSUBST(", shf);
-			dumpchar(shf, *wp++);
-			shf_putc(')', shf);
-			return (wp);
-		case OPAT:
-			shf_puts("OPAT=", shf);
-			dumpchar(shf, *wp++);
-			break;
-		case SPAT:
-			shf_puts("SPAT", shf);
-			break;
-		case CPAT:
-			shf_puts("CPAT", shf);
-			break;
-		default:
-			shf_fprintf(shf, "INVAL<%u>", (uint8_t)wp[-1]);
-			break;
-		}
-		shf_putc(' ', shf);
-	}
-}
-void
-dumpwdvar(struct shf *shf, const char *wp)
-{
-	dumpwdvar_i(shf, wp, 0);
-}
-
-void
-dumpioact(struct shf *shf, struct op *t)
-{
-	struct ioword **ioact, *iop;
-
-	if ((ioact = t->ioact) == NULL)
-		return;
-
-	shf_puts("{IOACT", shf);
-	while ((iop = *ioact++) != NULL) {
-		int type = iop->flag & IOTYPE;
-#define DT(x) case x: shf_puts(#x, shf); break;
-#define DB(x) if (iop->flag & x) shf_puts("|" #x, shf);
-
-		shf_putc(';', shf);
-		switch (type) {
-		DT(IOREAD)
-		DT(IOWRITE)
-		DT(IORDWR)
-		DT(IOHERE)
-		DT(IOCAT)
-		DT(IODUP)
-		default:
-			shf_fprintf(shf, "unk%d", type);
-		}
-		DB(IOEVAL)
-		DB(IOSKIP)
-		DB(IOCLOB)
-		DB(IORDUP)
-		DB(IONAMEXP)
-		DB(IOBASH)
-		DB(IOHERESTR)
-		DB(IONDELIM)
-		shf_fprintf(shf, ",unit=%d", iop->unit);
-		if (iop->delim) {
-			shf_puts(",delim<", shf);
-			dumpwdvar(shf, iop->delim);
-			shf_putc('>', shf);
-		}
-		if (iop->name) {
-			if (iop->flag & IONAMEXP) {
-				shf_puts(",name=", shf);
-				print_value_quoted(shf, iop->name);
-			} else {
-				shf_puts(",name<", shf);
-				dumpwdvar(shf, iop->name);
-				shf_putc('>', shf);
-			}
-		}
-		if (iop->heredoc) {
-			shf_puts(",heredoc=", shf);
-			print_value_quoted(shf, iop->heredoc);
-		}
-#undef DT
-#undef DB
-	}
-	shf_putc('}', shf);
-}
-
-void
-dumptree(struct shf *shf, struct op *t)
-{
-	int i, j;
-	const char **w, *name;
-	struct op *t1;
-	static int nesting;
-
-	for (i = 0; i < nesting; ++i)
-		shf_putc('\t', shf);
-	++nesting;
-	shf_puts("{tree:" /*}*/, shf);
-	if (t == NULL) {
-		name = "(null)";
-		goto out;
-	}
-	dumpioact(shf, t);
-	switch (t->type) {
-#define OPEN(x) case x: name = #x; shf_puts(" {" #x ":", shf); /*}*/
-
-	OPEN(TCOM)
-		if (t->vars) {
-			i = 0;
-			w = (const char **)t->vars;
-			while (*w) {
-				shf_putc('\n', shf);
-				for (j = 0; j < nesting; ++j)
-					shf_putc('\t', shf);
-				shf_fprintf(shf, " var%d<", i++);
-				dumpwdvar(shf, *w++);
-				shf_putc('>', shf);
-			}
-		} else
-			shf_puts(" #no-vars#", shf);
-		if (t->args) {
-			i = 0;
-			w = t->args;
-			while (*w) {
-				shf_putc('\n', shf);
-				for (j = 0; j < nesting; ++j)
-					shf_putc('\t', shf);
-				shf_fprintf(shf, " arg%d<", i++);
-				dumpwdvar(shf, *w++);
-				shf_putc('>', shf);
-			}
-		} else
-			shf_puts(" #no-args#", shf);
-		break;
-	OPEN(TEXEC)
- dumpleftandout:
-		t = t->left;
- dumpandout:
-		shf_putc('\n', shf);
-		dumptree(shf, t);
-		break;
-	OPEN(TPAREN)
-		goto dumpleftandout;
-	OPEN(TPIPE)
- dumpleftmidrightandout:
-		shf_putc('\n', shf);
-		dumptree(shf, t->left);
-/* middumprightandout: (unused) */
-		shf_fprintf(shf, "/%s:", name);
- dumprightandout:
-		t = t->right;
-		goto dumpandout;
-	OPEN(TLIST)
-		goto dumpleftmidrightandout;
-	OPEN(TOR)
-		goto dumpleftmidrightandout;
-	OPEN(TAND)
-		goto dumpleftmidrightandout;
-	OPEN(TBANG)
-		goto dumprightandout;
-	OPEN(TDBRACKET)
-		i = 0;
-		w = t->args;
-		while (*w) {
-			shf_putc('\n', shf);
-			for (j = 0; j < nesting; ++j)
-				shf_putc('\t', shf);
-			shf_fprintf(shf, " arg%d<", i++);
-			dumpwdvar(shf, *w++);
-			shf_putc('>', shf);
-		}
-		break;
-	OPEN(TFOR)
- dumpfor:
-		shf_fprintf(shf, " str<%s>", t->str);
-		if (t->vars != NULL) {
-			i = 0;
-			w = (const char **)t->vars;
-			while (*w) {
-				shf_putc('\n', shf);
-				for (j = 0; j < nesting; ++j)
-					shf_putc('\t', shf);
-				shf_fprintf(shf, " var%d<", i++);
-				dumpwdvar(shf, *w++);
-				shf_putc('>', shf);
-			}
-		}
-		goto dumpleftandout;
-	OPEN(TSELECT)
-		goto dumpfor;
-	OPEN(TCASE)
-		shf_fprintf(shf, " str<%s>", t->str);
-		i = 0;
-		for (t1 = t->left; t1 != NULL; t1 = t1->right) {
-			shf_putc('\n', shf);
-			for (j = 0; j < nesting; ++j)
-				shf_putc('\t', shf);
-			shf_fprintf(shf, " sub%d[(", i);
-			w = (const char **)t1->vars;
-			while (*w) {
-				dumpwdvar(shf, *w);
-				if (w[1] != NULL)
-					shf_putc('|', shf);
-				++w;
-			}
-			shf_putc(')', shf);
-			dumpioact(shf, t);
-			shf_putc('\n', shf);
-			dumptree(shf, t1->left);
-			shf_fprintf(shf, " ;%c/%d]", t1->u.charflag, i++);
-		}
-		break;
-	OPEN(TWHILE)
-		goto dumpleftmidrightandout;
-	OPEN(TUNTIL)
-		goto dumpleftmidrightandout;
-	OPEN(TBRACE)
-		goto dumpleftandout;
-	OPEN(TCOPROC)
-		goto dumpleftandout;
-	OPEN(TASYNC)
-		goto dumpleftandout;
-	OPEN(TFUNCT)
-		shf_fprintf(shf, " str<%s> ksh<%s>", t->str,
-		    t->u.ksh_func ? "yes" : "no");
-		goto dumpleftandout;
-	OPEN(TTIME)
-		goto dumpleftandout;
-	OPEN(TIF)
- dumpif:
-		shf_putc('\n', shf);
-		dumptree(shf, t->left);
-		t = t->right;
-		dumpioact(shf, t);
-		if (t->left != NULL) {
-			shf_puts(" /TTHEN:\n", shf);
-			dumptree(shf, t->left);
-		}
-		if (t->right && t->right->type == TELIF) {
-			shf_puts(" /TELIF:", shf);
-			t = t->right;
-			dumpioact(shf, t);
-			goto dumpif;
-		}
-		if (t->right != NULL) {
-			shf_puts(" /TELSE:\n", shf);
-			dumptree(shf, t->right);
-		}
-		break;
-	OPEN(TEOF)
- dumpunexpected:
-		shf_puts("unexpected", shf);
-		break;
-	OPEN(TELIF)
-		goto dumpunexpected;
-	OPEN(TPAT)
-		goto dumpunexpected;
-	default:
-		name = "TINVALID";
-		shf_fprintf(shf, "{T<%d>:" /*}*/, t->type);
-		goto dumpunexpected;
-
-#undef OPEN
-	}
- out:
-	shf_fprintf(shf, /*{*/ " /%s}\n", name);
-	--nesting;
-}
-#endif

Copied: vendor/MirOS/mksh/R50/tree.c (from rev 6707, vendor/MirOS/mksh/dist/tree.c)
===================================================================
--- vendor/MirOS/mksh/R50/tree.c	                        (rev 0)
+++ vendor/MirOS/mksh/R50/tree.c	2014-07-01 12:01:17 UTC (rev 6708)
@@ -0,0 +1,1144 @@
+/*	$OpenBSD: tree.c,v 1.20 2012/06/27 07:17:19 otto Exp $	*/
+
+/*-
+ * Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
+ *		 2011, 2012, 2013
+ *	Thorsten Glaser <tg at mirbsd.org>
+ *
+ * Provided that these terms and disclaimer and all copyright notices
+ * are retained or reproduced in an accompanying document, permission
+ * is granted to deal in this work without restriction, including un-
+ * limited rights to use, publicly perform, distribute, sell, modify,
+ * merge, give away, or sublicence.
+ *
+ * This work is provided "AS IS" and WITHOUT WARRANTY of any kind, to
+ * the utmost extent permitted by applicable law, neither express nor
+ * implied; without malicious intent or gross negligence. In no event
+ * may a licensor, author or contributor be held liable for indirect,
+ * direct, other damage, loss, or other issues arising in any way out
+ * of dealing in the work, even if advised of the possibility of such
+ * damage or existence of a defect, except proven that it results out
+ * of said person's immediate fault when using the work as intended.
+ */
+
+#include "sh.h"
+
+__RCSID("$MirOS: src/bin/mksh/tree.c,v 1.72 2013/09/24 20:19:45 tg Exp $");
+
+#define INDENT	8
+
+static void ptree(struct op *, int, struct shf *);
+static void pioact(struct shf *, struct ioword *);
+static const char *wdvarput(struct shf *, const char *, int, int);
+static void vfptreef(struct shf *, int, const char *, va_list);
+static struct ioword **iocopy(struct ioword **, Area *);
+static void iofree(struct ioword **, Area *);
+
+/* "foo& ; bar" and "foo |& ; bar" are invalid */
+static bool prevent_semicolon;
+
+static const char Telif_pT[] = "elif %T";
+
+/*
+ * print a command tree
+ */
+static void
+ptree(struct op *t, int indent, struct shf *shf)
+{
+	const char **w;
+	struct ioword **ioact;
+	struct op *t1;
+	int i;
+
+ Chain:
+	if (t == NULL)
+		return;
+	switch (t->type) {
+	case TCOM:
+		prevent_semicolon = false;
+		/*
+		 * special-case 'var=<<EOF' (rough; see
+		 * exec.c:execute() for full code)
+		 */
+		if (
+		    /* we have zero arguments, i.e. no programme to run */
+		    t->args[0] == NULL &&
+		    /* we have exactly one variable assignment */
+		    t->vars[0] != NULL && t->vars[1] == NULL &&
+		    /* we have exactly one I/O redirection */
+		    t->ioact != NULL && t->ioact[0] != NULL &&
+		    t->ioact[1] == NULL &&
+		    /* of type "here document" (or "here string") */
+		    (t->ioact[0]->flag & IOTYPE) == IOHERE) {
+			fptreef(shf, indent, "%S", t->vars[0]);
+			break;
+		}
+
+		if (t->vars) {
+			w = (const char **)t->vars;
+			while (*w)
+				fptreef(shf, indent, "%S ", *w++);
+		} else
+			shf_puts("#no-vars# ", shf);
+		if (t->args) {
+			w = t->args;
+			while (*w)
+				fptreef(shf, indent, "%S ", *w++);
+		} else
+			shf_puts("#no-args# ", shf);
+		break;
+	case TEXEC:
+		t = t->left;
+		goto Chain;
+	case TPAREN:
+		fptreef(shf, indent + 2, "( %T) ", t->left);
+		break;
+	case TPIPE:
+		fptreef(shf, indent, "%T| ", t->left);
+		t = t->right;
+		goto Chain;
+	case TLIST:
+		fptreef(shf, indent, "%T%;", t->left);
+		t = t->right;
+		goto Chain;
+	case TOR:
+	case TAND:
+		fptreef(shf, indent, "%T%s %T",
+		    t->left, (t->type == TOR) ? "||" : "&&", t->right);
+		break;
+	case TBANG:
+		shf_puts("! ", shf);
+		prevent_semicolon = false;
+		t = t->right;
+		goto Chain;
+	case TDBRACKET:
+		w = t->args;
+		shf_puts("[[", shf);
+		while (*w)
+			fptreef(shf, indent, " %S", *w++);
+		shf_puts(" ]] ", shf);
+		break;
+	case TSELECT:
+	case TFOR:
+		fptreef(shf, indent, "%s %s ",
+		    (t->type == TFOR) ? "for" : Tselect, t->str);
+		if (t->vars != NULL) {
+			shf_puts("in ", shf);
+			w = (const char **)t->vars;
+			while (*w)
+				fptreef(shf, indent, "%S ", *w++);
+			fptreef(shf, indent, "%;");
+		}
+		fptreef(shf, indent + INDENT, "do%N%T", t->left);
+		fptreef(shf, indent, "%;done ");
+		break;
+	case TCASE:
+		fptreef(shf, indent, "case %S in", t->str);
+		for (t1 = t->left; t1 != NULL; t1 = t1->right) {
+			fptreef(shf, indent, "%N(");
+			w = (const char **)t1->vars;
+			while (*w) {
+				fptreef(shf, indent, "%S%c", *w,
+				    (w[1] != NULL) ? '|' : ')');
+				++w;
+			}
+			fptreef(shf, indent + INDENT, "%N%T%N;%c", t1->left,
+			    t1->u.charflag);
+		}
+		fptreef(shf, indent, "%Nesac ");
+		break;
+#ifdef DEBUG
+	case TELIF:
+		internal_errorf("TELIF in tree.c:ptree() unexpected");
+		/* FALLTHROUGH */
+#endif
+	case TIF:
+		i = 2;
+		t1 = t;
+		goto process_TIF;
+		do {
+			t1 = t1->right;
+			i = 0;
+			fptreef(shf, indent, "%;");
+ process_TIF:
+			/* 5 == strlen("elif ") */
+			fptreef(shf, indent + 5 - i, Telif_pT + i, t1->left);
+			t1 = t1->right;
+			if (t1->left != NULL) {
+				fptreef(shf, indent, "%;");
+				fptreef(shf, indent + INDENT, "%s%N%T",
+				    "then", t1->left);
+			}
+		} while (t1->right && t1->right->type == TELIF);
+		if (t1->right != NULL) {
+			fptreef(shf, indent, "%;");
+			fptreef(shf, indent + INDENT, "%s%N%T",
+			    "else", t1->right);
+		}
+		fptreef(shf, indent, "%;fi ");
+		break;
+	case TWHILE:
+	case TUNTIL:
+		/* 6 == strlen("while "/"until ") */
+		fptreef(shf, indent + 6, "%s %T",
+		    (t->type == TWHILE) ? "while" : "until",
+		    t->left);
+		fptreef(shf, indent, "%;");
+		fptreef(shf, indent + INDENT, "do%N%T", t->right);
+		fptreef(shf, indent, "%;done ");
+		break;
+	case TBRACE:
+		fptreef(shf, indent + INDENT, "{%N%T", t->left);
+		fptreef(shf, indent, "%;} ");
+		break;
+	case TCOPROC:
+		fptreef(shf, indent, "%T|& ", t->left);
+		prevent_semicolon = true;
+		break;
+	case TASYNC:
+		fptreef(shf, indent, "%T& ", t->left);
+		prevent_semicolon = true;
+		break;
+	case TFUNCT:
+		fpFUNCTf(shf, indent, tobool(t->u.ksh_func), t->str, t->left);
+		break;
+	case TTIME:
+		fptreef(shf, indent, "%s %T", "time", t->left);
+		break;
+	default:
+		shf_puts("<botch>", shf);
+		prevent_semicolon = false;
+		break;
+	}
+	if ((ioact = t->ioact) != NULL) {
+		bool need_nl = false;
+
+		while (*ioact != NULL)
+			pioact(shf, *ioact++);
+		/* Print here documents after everything else... */
+		ioact = t->ioact;
+		while (*ioact != NULL) {
+			struct ioword *iop = *ioact++;
+
+			/* heredoc is NULL when tracing (set -x) */
+			if ((iop->flag & (IOTYPE | IOHERESTR)) == IOHERE &&
+			    iop->heredoc) {
+				shf_putc('\n', shf);
+				shf_puts(iop->heredoc, shf);
+				fptreef(shf, indent, "%s",
+				    iop->flag & IONDELIM ? "<<" :
+				    evalstr(iop->delim, 0));
+				need_nl = true;
+			}
+		}
+		/*
+		 * Last delimiter must be followed by a newline (this
+		 * often leads to an extra blank line, but it's not
+		 * worth worrying about)
+		 */
+		if (need_nl) {
+			shf_putc('\n', shf);
+			prevent_semicolon = true;
+		}
+	}
+}
+
+static void
+pioact(struct shf *shf, struct ioword *iop)
+{
+	int flag = iop->flag;
+	int type = flag & IOTYPE;
+	int expected;
+
+	expected = (type == IOREAD || type == IORDWR || type == IOHERE) ? 0 :
+	    (type == IOCAT || type == IOWRITE) ? 1 :
+	    (type == IODUP && (iop->unit == !(flag & IORDUP))) ? iop->unit :
+	    iop->unit + 1;
+	if (iop->unit != expected)
+		shf_fprintf(shf, "%d", iop->unit);
+
+	switch (type) {
+	case IOREAD:
+		shf_putc('<', shf);
+		break;
+	case IOHERE:
+		shf_puts("<<", shf);
+		if (flag & IOSKIP)
+			shf_putc('-', shf);
+		break;
+	case IOCAT:
+		shf_puts(">>", shf);
+		break;
+	case IOWRITE:
+		shf_putc('>', shf);
+		if (flag & IOCLOB)
+			shf_putc('|', shf);
+		break;
+	case IORDWR:
+		shf_puts("<>", shf);
+		break;
+	case IODUP:
+		shf_puts(flag & IORDUP ? "<&" : ">&", shf);
+		break;
+	}
+	/* name/delim are NULL when printing syntax errors */
+	if (type == IOHERE) {
+		if (iop->delim)
+			wdvarput(shf, iop->delim, 0, WDS_TPUTS);
+		if (iop->flag & IOHERESTR)
+			shf_putc(' ', shf);
+	} else if (iop->name) {
+		if (iop->flag & IONAMEXP)
+			print_value_quoted(shf, iop->name);
+		else
+			wdvarput(shf, iop->name, 0, WDS_TPUTS);
+		shf_putc(' ', shf);
+	}
+	prevent_semicolon = false;
+}
+
+/* variant of fputs for ptreef and wdstrip */
+static const char *
+wdvarput(struct shf *shf, const char *wp, int quotelevel, int opmode)
+{
+	int c;
+	const char *cs;
+
+	/*-
+	 * problems:
+	 *	`...` -> $(...)
+	 *	'foo' -> "foo"
+	 *	x${foo:-"hi"} -> x${foo:-hi} unless WDS_TPUTS
+	 *	x${foo:-'hi'} -> x${foo:-hi} unless WDS_KEEPQ
+	 * could change encoding to:
+	 *	OQUOTE ["'] ... CQUOTE ["']
+	 *	COMSUB [(`] ...\0	(handle $ ` \ and maybe " in `...` case)
+	 */
+	while (/* CONSTCOND */ 1)
+		switch (*wp++) {
+		case EOS:
+			return (--wp);
+		case ADELIM:
+		case CHAR:
+			c = *wp++;
+			if ((opmode & WDS_MAGIC) &&
+			    (ISMAGIC(c) || c == '[' || c == '!' ||
+			    c == '-' || c == ']' || c == '*' || c == '?'))
+				shf_putc(MAGIC, shf);
+			shf_putc(c, shf);
+			break;
+		case QCHAR: {
+			bool doq;
+
+			c = *wp++;
+			doq = (c == '"' || c == '`' || c == '$' || c == '\\');
+			if (opmode & WDS_TPUTS) {
+				if (quotelevel == 0)
+					doq = true;
+			} else {
+				if (!(opmode & WDS_KEEPQ))
+					doq = false;
+			}
+			if (doq)
+				shf_putc('\\', shf);
+			shf_putc(c, shf);
+			break;
+		}
+		case COMSUB:
+			shf_puts("$(", shf);
+			cs = ")";
+ pSUB:
+			while ((c = *wp++) != 0)
+				shf_putc(c, shf);
+			shf_puts(cs, shf);
+			break;
+		case FUNSUB:
+			c = ' ';
+			if (0)
+				/* FALLTHROUGH */
+		case VALSUB:
+			  c = '|';
+			shf_putc('$', shf);
+			shf_putc('{', shf);
+			shf_putc(c, shf);
+			cs = ";}";
+			goto pSUB;
+		case EXPRSUB:
+			shf_puts("$((", shf);
+			cs = "))";
+			goto pSUB;
+		case OQUOTE:
+			if (opmode & WDS_TPUTS) {
+				quotelevel++;
+				shf_putc('"', shf);
+			}
+			break;
+		case CQUOTE:
+			if (opmode & WDS_TPUTS) {
+				if (quotelevel)
+					quotelevel--;
+				shf_putc('"', shf);
+			}
+			break;
+		case OSUBST:
+			shf_putc('$', shf);
+			if (*wp++ == '{')
+				shf_putc('{', shf);
+			while ((c = *wp++) != 0)
+				shf_putc(c, shf);
+			wp = wdvarput(shf, wp, 0, opmode);
+			break;
+		case CSUBST:
+			if (*wp++ == '}')
+				shf_putc('}', shf);
+			return (wp);
+		case OPAT:
+			if (opmode & WDS_MAGIC) {
+				shf_putc(MAGIC, shf);
+				shf_putchar(*wp++ | 0x80, shf);
+			} else {
+				shf_putchar(*wp++, shf);
+				shf_putc('(', shf);
+			}
+			break;
+		case SPAT:
+			c = '|';
+			if (0)
+		case CPAT:
+				c = /*(*/ ')';
+			if (opmode & WDS_MAGIC)
+				shf_putc(MAGIC, shf);
+			shf_putc(c, shf);
+			break;
+		}
+}
+
+/*
+ * this is the _only_ way to reliably handle
+ * variable args with an ANSI compiler
+ */
+/* VARARGS */
+void
+fptreef(struct shf *shf, int indent, const char *fmt, ...)
+{
+	va_list va;
+
+	va_start(va, fmt);
+	vfptreef(shf, indent, fmt, va);
+	va_end(va);
+}
+
+/* VARARGS */
+char *
+snptreef(char *s, ssize_t n, const char *fmt, ...)
+{
+	va_list va;
+	struct shf shf;
+
+	shf_sopen(s, n, SHF_WR | (s ? 0 : SHF_DYNAMIC), &shf);
+
+	va_start(va, fmt);
+	vfptreef(&shf, 0, fmt, va);
+	va_end(va);
+
+	/* shf_sclose NUL terminates */
+	return (shf_sclose(&shf));
+}
+
+static void
+vfptreef(struct shf *shf, int indent, const char *fmt, va_list va)
+{
+	int c;
+
+	while ((c = *fmt++)) {
+		if (c == '%') {
+			switch ((c = *fmt++)) {
+			case 'c':
+				/* character (octet, probably) */
+				shf_putchar(va_arg(va, int), shf);
+				break;
+			case 's':
+				/* string */
+				shf_puts(va_arg(va, char *), shf);
+				break;
+			case 'S':
+				/* word */
+				wdvarput(shf, va_arg(va, char *), 0, WDS_TPUTS);
+				break;
+			case 'd':
+				/* signed decimal */
+				shf_fprintf(shf, "%d", va_arg(va, int));
+				break;
+			case 'u':
+				/* unsigned decimal */
+				shf_fprintf(shf, "%u", va_arg(va, unsigned int));
+				break;
+			case 'T':
+				/* format tree */
+				ptree(va_arg(va, struct op *), indent, shf);
+				goto dont_trash_prevent_semicolon;
+			case ';':
+				/* newline or ; */
+			case 'N':
+				/* newline or space */
+				if (shf->flags & SHF_STRING) {
+					if (c == ';' && !prevent_semicolon)
+						shf_putc(';', shf);
+					shf_putc(' ', shf);
+				} else {
+					int i;
+
+					shf_putc('\n', shf);
+					i = indent;
+					while (i >= 8) {
+						shf_putc('\t', shf);
+						i -= 8;
+					}
+					while (i--)
+						shf_putc(' ', shf);
+				}
+				break;
+			case 'R':
+				/* I/O redirection */
+				pioact(shf, va_arg(va, struct ioword *));
+				break;
+			default:
+				shf_putc(c, shf);
+				break;
+			}
+		} else
+			shf_putc(c, shf);
+		prevent_semicolon = false;
+ dont_trash_prevent_semicolon:
+		;
+	}
+}
+
+/*
+ * copy tree (for function definition)
+ */
+struct op *
+tcopy(struct op *t, Area *ap)
+{
+	struct op *r;
+	const char **tw;
+	char **rw;
+
+	if (t == NULL)
+		return (NULL);
+
+	r = alloc(sizeof(struct op), ap);
+
+	r->type = t->type;
+	r->u.evalflags = t->u.evalflags;
+
+	if (t->type == TCASE)
+		r->str = wdcopy(t->str, ap);
+	else
+		strdupx(r->str, t->str, ap);
+
+	if (t->vars == NULL)
+		r->vars = NULL;
+	else {
+		tw = (const char **)t->vars;
+		while (*tw)
+			++tw;
+		rw = r->vars = alloc2(tw - (const char **)t->vars + 1,
+		    sizeof(*tw), ap);
+		tw = (const char **)t->vars;
+		while (*tw)
+			*rw++ = wdcopy(*tw++, ap);
+		*rw = NULL;
+	}
+
+	if (t->args == NULL)
+		r->args = NULL;
+	else {
+		tw = t->args;
+		while (*tw)
+			++tw;
+		r->args = (const char **)(rw = alloc2(tw - t->args + 1,
+		    sizeof(*tw), ap));
+		tw = t->args;
+		while (*tw)
+			*rw++ = wdcopy(*tw++, ap);
+		*rw = NULL;
+	}
+
+	r->ioact = (t->ioact == NULL) ? NULL : iocopy(t->ioact, ap);
+
+	r->left = tcopy(t->left, ap);
+	r->right = tcopy(t->right, ap);
+	r->lineno = t->lineno;
+
+	return (r);
+}
+
+char *
+wdcopy(const char *wp, Area *ap)
+{
+	size_t len;
+
+	len = wdscan(wp, EOS) - wp;
+	return (memcpy(alloc(len, ap), wp, len));
+}
+
+/* return the position of prefix c in wp plus 1 */
+const char *
+wdscan(const char *wp, int c)
+{
+	int nest = 0;
+
+	while (/* CONSTCOND */ 1)
+		switch (*wp++) {
+		case EOS:
+			return (wp);
+		case ADELIM:
+			if (c == ADELIM)
+				return (wp + 1);
+			/* FALLTHROUGH */
+		case CHAR:
+		case QCHAR:
+			wp++;
+			break;
+		case COMSUB:
+		case FUNSUB:
+		case VALSUB:
+		case EXPRSUB:
+			while (*wp++ != 0)
+				;
+			break;
+		case OQUOTE:
+		case CQUOTE:
+			break;
+		case OSUBST:
+			nest++;
+			while (*wp++ != '\0')
+				;
+			break;
+		case CSUBST:
+			wp++;
+			if (c == CSUBST && nest == 0)
+				return (wp);
+			nest--;
+			break;
+		case OPAT:
+			nest++;
+			wp++;
+			break;
+		case SPAT:
+		case CPAT:
+			if (c == wp[-1] && nest == 0)
+				return (wp);
+			if (wp[-1] == CPAT)
+				nest--;
+			break;
+		default:
+			internal_warningf(
+			    "wdscan: unknown char 0x%x (carrying on)",
+			    wp[-1]);
+		}
+}
+
+/*
+ * return a copy of wp without any of the mark up characters and with
+ * quote characters (" ' \) stripped. (string is allocated from ATEMP)
+ */
+char *
+wdstrip(const char *wp, int opmode)
+{
+	struct shf shf;
+
+	shf_sopen(NULL, 32, SHF_WR | SHF_DYNAMIC, &shf);
+	wdvarput(&shf, wp, 0, opmode);
+	/* shf_sclose NUL terminates */
+	return (shf_sclose(&shf));
+}
+
+static struct ioword **
+iocopy(struct ioword **iow, Area *ap)
+{
+	struct ioword **ior;
+	int i;
+
+	ior = iow;
+	while (*ior)
+		++ior;
+	ior = alloc2(ior - iow + 1, sizeof(struct ioword *), ap);
+
+	for (i = 0; iow[i] != NULL; i++) {
+		struct ioword *p, *q;
+
+		p = iow[i];
+		q = alloc(sizeof(struct ioword), ap);
+		ior[i] = q;
+		*q = *p;
+		if (p->name != NULL)
+			q->name = wdcopy(p->name, ap);
+		if (p->delim != NULL)
+			q->delim = wdcopy(p->delim, ap);
+		if (p->heredoc != NULL)
+			strdupx(q->heredoc, p->heredoc, ap);
+	}
+	ior[i] = NULL;
+
+	return (ior);
+}
+
+/*
+ * free tree (for function definition)
+ */
+void
+tfree(struct op *t, Area *ap)
+{
+	char **w;
+
+	if (t == NULL)
+		return;
+
+	if (t->str != NULL)
+		afree(t->str, ap);
+
+	if (t->vars != NULL) {
+		for (w = t->vars; *w != NULL; w++)
+			afree(*w, ap);
+		afree(t->vars, ap);
+	}
+
+	if (t->args != NULL) {
+		/*XXX we assume the caller is right */
+		union mksh_ccphack cw;
+
+		cw.ro = t->args;
+		for (w = cw.rw; *w != NULL; w++)
+			afree(*w, ap);
+		afree(t->args, ap);
+	}
+
+	if (t->ioact != NULL)
+		iofree(t->ioact, ap);
+
+	tfree(t->left, ap);
+	tfree(t->right, ap);
+
+	afree(t, ap);
+}
+
+static void
+iofree(struct ioword **iow, Area *ap)
+{
+	struct ioword **iop;
+	struct ioword *p;
+
+	iop = iow;
+	while ((p = *iop++) != NULL) {
+		if (p->name != NULL)
+			afree(p->name, ap);
+		if (p->delim != NULL)
+			afree(p->delim, ap);
+		if (p->heredoc != NULL)
+			afree(p->heredoc, ap);
+		afree(p, ap);
+	}
+	afree(iow, ap);
+}
+
+void
+fpFUNCTf(struct shf *shf, int i, bool isksh, const char *k, struct op *v)
+{
+	if (isksh)
+		fptreef(shf, i, "%s %s %T", Tfunction, k, v);
+	else
+		fptreef(shf, i, "%s() %T", k, v);
+}
+
+
+/* for jobs.c */
+void
+vistree(char *dst, size_t sz, struct op *t)
+{
+	unsigned int c;
+	char *cp, *buf;
+	size_t n;
+
+	buf = alloc(sz + 16, ATEMP);
+	snptreef(buf, sz + 16, "%T", t);
+	cp = buf;
+ vist_loop:
+	if (UTFMODE && (n = utf_mbtowc(&c, cp)) != (size_t)-1) {
+		if (c == 0 || n >= sz)
+			/* NUL or not enough free space */
+			goto vist_out;
+		/* copy multibyte char */
+		sz -= n;
+		while (n--)
+			*dst++ = *cp++;
+		goto vist_loop;
+	}
+	if (--sz == 0 || (c = (unsigned char)(*cp++)) == 0)
+		/* NUL or not enough free space */
+		goto vist_out;
+	if (ISCTRL(c & 0x7F)) {
+		/* C0 or C1 control character or DEL */
+		if (--sz == 0)
+			/* not enough free space for two chars */
+			goto vist_out;
+		*dst++ = (c & 0x80) ? '$' : '^';
+		c = UNCTRL(c & 0x7F);
+	} else if (UTFMODE && c > 0x7F) {
+		/* better not try to display broken multibyte chars */
+		/* also go easy on the Unicode: no U+FFFD here */
+		c = '?';
+	}
+	*dst++ = c;
+	goto vist_loop;
+
+ vist_out:
+	*dst = '\0';
+	afree(buf, ATEMP);
+}
+
+#ifdef DEBUG
+void
+dumpchar(struct shf *shf, int c)
+{
+	if (ISCTRL(c & 0x7F)) {
+		/* C0 or C1 control character or DEL */
+		shf_putc((c & 0x80) ? '$' : '^', shf);
+		c = UNCTRL(c & 0x7F);
+	}
+	shf_putc(c, shf);
+}
+
+/* see: wdvarput */
+static const char *
+dumpwdvar_i(struct shf *shf, const char *wp, int quotelevel)
+{
+	int c;
+
+	while (/* CONSTCOND */ 1) {
+		switch(*wp++) {
+		case EOS:
+			shf_puts("EOS", shf);
+			return (--wp);
+		case ADELIM:
+			shf_puts("ADELIM=", shf);
+			if (0)
+		case CHAR:
+				shf_puts("CHAR=", shf);
+			dumpchar(shf, *wp++);
+			break;
+		case QCHAR:
+			shf_puts("QCHAR<", shf);
+			c = *wp++;
+			if (quotelevel == 0 ||
+			    (c == '"' || c == '`' || c == '$' || c == '\\'))
+				shf_putc('\\', shf);
+			dumpchar(shf, c);
+			goto closeandout;
+		case COMSUB:
+			shf_puts("COMSUB<", shf);
+ dumpsub:
+			while ((c = *wp++) != 0)
+				dumpchar(shf, c);
+ closeandout:
+			shf_putc('>', shf);
+			break;
+		case FUNSUB:
+			shf_puts("FUNSUB<", shf);
+			goto dumpsub;
+		case VALSUB:
+			shf_puts("VALSUB<", shf);
+			goto dumpsub;
+		case EXPRSUB:
+			shf_puts("EXPRSUB<", shf);
+			goto dumpsub;
+		case OQUOTE:
+			shf_fprintf(shf, "OQUOTE{%d", ++quotelevel);
+			break;
+		case CQUOTE:
+			shf_fprintf(shf, "%d}CQUOTE", quotelevel);
+			if (quotelevel)
+				quotelevel--;
+			else
+				shf_puts("(err)", shf);
+			break;
+		case OSUBST:
+			shf_puts("OSUBST(", shf);
+			dumpchar(shf, *wp++);
+			shf_puts(")[", shf);
+			while ((c = *wp++) != 0)
+				dumpchar(shf, c);
+			shf_putc('|', shf);
+			wp = dumpwdvar_i(shf, wp, 0);
+			break;
+		case CSUBST:
+			shf_puts("]CSUBST(", shf);
+			dumpchar(shf, *wp++);
+			shf_putc(')', shf);
+			return (wp);
+		case OPAT:
+			shf_puts("OPAT=", shf);
+			dumpchar(shf, *wp++);
+			break;
+		case SPAT:
+			shf_puts("SPAT", shf);
+			break;
+		case CPAT:
+			shf_puts("CPAT", shf);
+			break;
+		default:
+			shf_fprintf(shf, "INVAL<%u>", (uint8_t)wp[-1]);
+			break;
+		}
+		shf_putc(' ', shf);
+	}
+}
+void
+dumpwdvar(struct shf *shf, const char *wp)
+{
+	dumpwdvar_i(shf, wp, 0);
+}
+
+void
+dumpioact(struct shf *shf, struct op *t)
+{
+	struct ioword **ioact, *iop;
+
+	if ((ioact = t->ioact) == NULL)
+		return;
+
+	shf_puts("{IOACT", shf);
+	while ((iop = *ioact++) != NULL) {
+		int type = iop->flag & IOTYPE;
+#define DT(x) case x: shf_puts(#x, shf); break;
+#define DB(x) if (iop->flag & x) shf_puts("|" #x, shf);
+
+		shf_putc(';', shf);
+		switch (type) {
+		DT(IOREAD)
+		DT(IOWRITE)
+		DT(IORDWR)
+		DT(IOHERE)
+		DT(IOCAT)
+		DT(IODUP)
+		default:
+			shf_fprintf(shf, "unk%d", type);
+		}
+		DB(IOEVAL)
+		DB(IOSKIP)
+		DB(IOCLOB)
+		DB(IORDUP)
+		DB(IONAMEXP)
+		DB(IOBASH)
+		DB(IOHERESTR)
+		DB(IONDELIM)
+		shf_fprintf(shf, ",unit=%d", iop->unit);
+		if (iop->delim) {
+			shf_puts(",delim<", shf);
+			dumpwdvar(shf, iop->delim);
+			shf_putc('>', shf);
+		}
+		if (iop->name) {
+			if (iop->flag & IONAMEXP) {
+				shf_puts(",name=", shf);
+				print_value_quoted(shf, iop->name);
+			} else {
+				shf_puts(",name<", shf);
+				dumpwdvar(shf, iop->name);
+				shf_putc('>', shf);
+			}
+		}
+		if (iop->heredoc) {
+			shf_puts(",heredoc=", shf);
+			print_value_quoted(shf, iop->heredoc);
+		}
+#undef DT
+#undef DB
+	}
+	shf_putc('}', shf);
+}
+
+void
+dumptree(struct shf *shf, struct op *t)
+{
+	int i, j;
+	const char **w, *name;
+	struct op *t1;
+	static int nesting;
+
+	for (i = 0; i < nesting; ++i)
+		shf_putc('\t', shf);
+	++nesting;
+	shf_puts("{tree:" /*}*/, shf);
+	if (t == NULL) {
+		name = "(null)";
+		goto out;
+	}
+	dumpioact(shf, t);
+	switch (t->type) {
+#define OPEN(x) case x: name = #x; shf_puts(" {" #x ":", shf); /*}*/
+
+	OPEN(TCOM)
+		if (t->vars) {
+			i = 0;
+			w = (const char **)t->vars;
+			while (*w) {
+				shf_putc('\n', shf);
+				for (j = 0; j < nesting; ++j)
+					shf_putc('\t', shf);
+				shf_fprintf(shf, " var%d<", i++);
+				dumpwdvar(shf, *w++);
+				shf_putc('>', shf);
+			}
+		} else
+			shf_puts(" #no-vars#", shf);
+		if (t->args) {
+			i = 0;
+			w = t->args;
+			while (*w) {
+				shf_putc('\n', shf);
+				for (j = 0; j < nesting; ++j)
+					shf_putc('\t', shf);
+				shf_fprintf(shf, " arg%d<", i++);
+				dumpwdvar(shf, *w++);
+				shf_putc('>', shf);
+			}
+		} else
+			shf_puts(" #no-args#", shf);
+		break;
+	OPEN(TEXEC)
+ dumpleftandout:
+		t = t->left;
+ dumpandout:
+		shf_putc('\n', shf);
+		dumptree(shf, t);
+		break;
+	OPEN(TPAREN)
+		goto dumpleftandout;
+	OPEN(TPIPE)
+ dumpleftmidrightandout:
+		shf_putc('\n', shf);
+		dumptree(shf, t->left);
+/* middumprightandout: (unused) */
+		shf_fprintf(shf, "/%s:", name);
+ dumprightandout:
+		t = t->right;
+		goto dumpandout;
+	OPEN(TLIST)
+		goto dumpleftmidrightandout;
+	OPEN(TOR)
+		goto dumpleftmidrightandout;
+	OPEN(TAND)
+		goto dumpleftmidrightandout;
+	OPEN(TBANG)
+		goto dumprightandout;
+	OPEN(TDBRACKET)
+		i = 0;
+		w = t->args;
+		while (*w) {
+			shf_putc('\n', shf);
+			for (j = 0; j < nesting; ++j)
+				shf_putc('\t', shf);
+			shf_fprintf(shf, " arg%d<", i++);
+			dumpwdvar(shf, *w++);
+			shf_putc('>', shf);
+		}
+		break;
+	OPEN(TFOR)
+ dumpfor:
+		shf_fprintf(shf, " str<%s>", t->str);
+		if (t->vars != NULL) {
+			i = 0;
+			w = (const char **)t->vars;
+			while (*w) {
+				shf_putc('\n', shf);
+				for (j = 0; j < nesting; ++j)
+					shf_putc('\t', shf);
+				shf_fprintf(shf, " var%d<", i++);
+				dumpwdvar(shf, *w++);
+				shf_putc('>', shf);
+			}
+		}
+		goto dumpleftandout;
+	OPEN(TSELECT)
+		goto dumpfor;
+	OPEN(TCASE)
+		shf_fprintf(shf, " str<%s>", t->str);
+		i = 0;
+		for (t1 = t->left; t1 != NULL; t1 = t1->right) {
+			shf_putc('\n', shf);
+			for (j = 0; j < nesting; ++j)
+				shf_putc('\t', shf);
+			shf_fprintf(shf, " sub%d[(", i);
+			w = (const char **)t1->vars;
+			while (*w) {
+				dumpwdvar(shf, *w);
+				if (w[1] != NULL)
+					shf_putc('|', shf);
+				++w;
+			}
+			shf_putc(')', shf);
+			dumpioact(shf, t);
+			shf_putc('\n', shf);
+			dumptree(shf, t1->left);
+			shf_fprintf(shf, " ;%c/%d]", t1->u.charflag, i++);
+		}
+		break;
+	OPEN(TWHILE)
+		goto dumpleftmidrightandout;
+	OPEN(TUNTIL)
+		goto dumpleftmidrightandout;
+	OPEN(TBRACE)
+		goto dumpleftandout;
+	OPEN(TCOPROC)
+		goto dumpleftandout;
+	OPEN(TASYNC)
+		goto dumpleftandout;
+	OPEN(TFUNCT)
+		shf_fprintf(shf, " str<%s> ksh<%s>", t->str,
+		    t->u.ksh_func ? "yes" : "no");
+		goto dumpleftandout;
+	OPEN(TTIME)
+		goto dumpleftandout;
+	OPEN(TIF)
+ dumpif:
+		shf_putc('\n', shf);
+		dumptree(shf, t->left);
+		t = t->right;
+		dumpioact(shf, t);
+		if (t->left != NULL) {
+			shf_puts(" /TTHEN:\n", shf);
+			dumptree(shf, t->left);
+		}
+		if (t->right && t->right->type == TELIF) {
+			shf_puts(" /TELIF:", shf);
+			t = t->right;
+			dumpioact(shf, t);
+			goto dumpif;
+		}
+		if (t->right != NULL) {
+			shf_puts(" /TELSE:\n", shf);
+			dumptree(shf, t->right);
+		}
+		break;
+	OPEN(TEOF)
+ dumpunexpected:
+		shf_puts("unexpected", shf);
+		break;
+	OPEN(TELIF)
+		goto dumpunexpected;
+	OPEN(TPAT)
+		goto dumpunexpected;
+	default:
+		name = "TINVALID";
+		shf_fprintf(shf, "{T<%d>:" /*}*/, t->type);
+		goto dumpunexpected;
+
+#undef OPEN
+	}
+ out:
+	shf_fprintf(shf, /*{*/ " /%s}\n", name);
+	--nesting;
+}
+#endif

Deleted: vendor/MirOS/mksh/R50/var.c
===================================================================
--- vendor/MirOS/mksh/dist/var.c	2014-06-30 23:58:12 UTC (rev 6706)
+++ vendor/MirOS/mksh/R50/var.c	2014-07-01 12:01:17 UTC (rev 6708)
@@ -1,1569 +0,0 @@
-/*	$OpenBSD: var.c,v 1.35 2013/04/05 01:31:30 tedu Exp $	*/
-
-/*-
- * Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
- *		 2011, 2012, 2013
- *	Thorsten Glaser <tg at mirbsd.org>
- *
- * Provided that these terms and disclaimer and all copyright notices
- * are retained or reproduced in an accompanying document, permission
- * is granted to deal in this work without restriction, including un-
- * limited rights to use, publicly perform, distribute, sell, modify,
- * merge, give away, or sublicence.
- *
- * This work is provided "AS IS" and WITHOUT WARRANTY of any kind, to
- * the utmost extent permitted by applicable law, neither express nor
- * implied; without malicious intent or gross negligence. In no event
- * may a licensor, author or contributor be held liable for indirect,
- * direct, other damage, loss, or other issues arising in any way out
- * of dealing in the work, even if advised of the possibility of such
- * damage or existence of a defect, except proven that it results out
- * of said person's immediate fault when using the work as intended.
- */
-
-#include "sh.h"
-
-#if defined(__OpenBSD__)
-#include <sys/sysctl.h>
-#endif
-
-__RCSID("$MirOS: src/bin/mksh/var.c,v 1.173 2013/05/31 22:47:14 tg Exp $");
-
-/*-
- * Variables
- *
- * WARNING: unreadable code, needs a rewrite
- *
- * if (flag&INTEGER), val.i contains integer value, and type contains base.
- * otherwise, (val.s + type) contains string value.
- * if (flag&EXPORT), val.s contains "name=value" for E-Z exporting.
- */
-
-static struct table specials;
-static uint32_t lcg_state = 5381;
-
-static char *formatstr(struct tbl *, const char *);
-static void exportprep(struct tbl *, const char *);
-static int special(const char *);
-static void unspecial(const char *);
-static void getspec(struct tbl *);
-static void setspec(struct tbl *);
-static void unsetspec(struct tbl *);
-static int getint(struct tbl *, mksh_ari_u *, bool);
-static const char *array_index_calc(const char *, bool *, uint32_t *);
-
-/*
- * create a new block for function calls and simple commands
- * assume caller has allocated and set up e->loc
- */
-void
-newblock(void)
-{
-	struct block *l;
-	static const char *empty[] = { null };
-
-	l = alloc(sizeof(struct block), ATEMP);
-	l->flags = 0;
-	/* TODO: could use e->area (l->area => l->areap) */
-	ainit(&l->area);
-	if (!e->loc) {
-		l->argc = 0;
-		l->argv = empty;
-	} else {
-		l->argc = e->loc->argc;
-		l->argv = e->loc->argv;
-	}
-	l->exit = l->error = NULL;
-	ktinit(&l->area, &l->vars, 0);
-	ktinit(&l->area, &l->funs, 0);
-	l->next = e->loc;
-	e->loc = l;
-}
-
-/*
- * pop a block handling special variables
- */
-void
-popblock(void)
-{
-	ssize_t i;
-	struct block *l = e->loc;
-	struct tbl *vp, **vpp = l->vars.tbls, *vq;
-
-	/* pop block */
-	e->loc = l->next;
-
-	i = 1 << (l->vars.tshift);
-	while (--i >= 0)
-		if ((vp = *vpp++) != NULL && (vp->flag&SPECIAL)) {
-			if ((vq = global(vp->name))->flag & ISSET)
-				setspec(vq);
-			else
-				unsetspec(vq);
-		}
-	if (l->flags & BF_DOGETOPTS)
-		user_opt = l->getopts_state;
-	afreeall(&l->area);
-	afree(l, ATEMP);
-}
-
-/* called by main() to initialise variable data structures */
-#define VARSPEC_DEFNS
-#include "var_spec.h"
-
-enum var_specs {
-#define VARSPEC_ENUMS
-#include "var_spec.h"
-	V_MAX
-};
-
-/* this is biased with -1 relative to VARSPEC_ENUMS */
-static const char * const initvar_names[] = {
-#define VARSPEC_ITEMS
-#include "var_spec.h"
-};
-
-void
-initvar(void)
-{
-	int i = 0;
-	struct tbl *tp;
-
-	ktinit(APERM, &specials,
-	    /* currently 14 specials: 75% of 32 = 2^5 */
-	    5);
-	while (i < V_MAX - 1) {
-		tp = ktenter(&specials, initvar_names[i],
-		    hash(initvar_names[i]));
-		tp->flag = DEFINED|ISSET;
-		tp->type = ++i;
-	}
-}
-
-/* common code for several functions below and c_typeset() */
-struct block *
-varsearch(struct block *l, struct tbl **vpp, const char *vn, uint32_t h)
-{
-	register struct tbl *vp;
-
-	if (l) {
- varsearch_loop:
-		if ((vp = ktsearch(&l->vars, vn, h)) != NULL)
-			goto varsearch_out;
-		if (l->next != NULL) {
-			l = l->next;
-			goto varsearch_loop;
-		}
-	}
-	vp = NULL;
- varsearch_out:
-	*vpp = vp;
-	return (l);
-}
-
-/*
- * Used to calculate an array index for global()/local(). Sets *arrayp
- * to true if this is an array, sets *valp to the array index, returns
- * the basename of the array.
- */
-static const char *
-array_index_calc(const char *n, bool *arrayp, uint32_t *valp)
-{
-	const char *p;
-	size_t len;
-	char *ap = NULL;
-
-	*arrayp = false;
- redo_from_ref:
-	p = skip_varname(n, false);
-	if (set_refflag == SRF_NOP && (p != n) && ksh_isalphx(n[0])) {
-		struct tbl *vp;
-		char *vn;
-
-		strndupx(vn, n, p - n, ATEMP);
-		/* check if this is a reference */
-		varsearch(e->loc, &vp, vn, hash(vn));
-		afree(vn, ATEMP);
-		if (vp && (vp->flag & (DEFINED|ASSOC|ARRAY)) ==
-		    (DEFINED|ASSOC)) {
-			char *cp;
-
-			/* gotcha! */
-			cp = shf_smprintf("%s%s", str_val(vp), p);
-			afree(ap, ATEMP);
-			n = ap = cp;
-			goto redo_from_ref;
-		}
-	}
-
-	if (p != n && *p == '[' && (len = array_ref_len(p))) {
-		char *sub, *tmp;
-		mksh_ari_t rval;
-
-		/* calculate the value of the subscript */
-		*arrayp = true;
-		strndupx(tmp, p + 1, len - 2, ATEMP);
-		sub = substitute(tmp, 0);
-		afree(tmp, ATEMP);
-		strndupx(n, n, p - n, ATEMP);
-		evaluate(sub, &rval, KSH_UNWIND_ERROR, true);
-		*valp = (uint32_t)rval;
-		afree(sub, ATEMP);
-	}
-	return (n);
-}
-
-/*
- * Search for variable, if not found create globally.
- */
-struct tbl *
-global(const char *n)
-{
-	struct block *l = e->loc;
-	struct tbl *vp;
-	int c;
-	bool array;
-	uint32_t h, val;
-
-	/* Check to see if this is an array */
-	n = array_index_calc(n, &array, &val);
-	h = hash(n);
-	c = n[0];
-	if (!ksh_isalphx(c)) {
-		if (array)
-			errorf("bad substitution");
-		vp = &vtemp;
-		vp->flag = DEFINED;
-		vp->type = 0;
-		vp->areap = ATEMP;
-		*vp->name = c;
-		if (ksh_isdigit(c)) {
-			for (c = 0; ksh_isdigit(*n); n++)
-				c = c*10 + *n-'0';
-			if (c <= l->argc)
-				/* setstr can't fail here */
-				setstr(vp, l->argv[c], KSH_RETURN_ERROR);
-			vp->flag |= RDONLY;
-			return (vp);
-		}
-		vp->flag |= RDONLY;
-		if (n[1] != '\0')
-			return (vp);
-		vp->flag |= ISSET|INTEGER;
-		switch (c) {
-		case '$':
-			vp->val.i = kshpid;
-			break;
-		case '!':
-			/* if no job, expand to nothing */
-			if ((vp->val.i = j_async()) == 0)
-				vp->flag &= ~(ISSET|INTEGER);
-			break;
-		case '?':
-			vp->val.i = exstat & 0xFF;
-			break;
-		case '#':
-			vp->val.i = l->argc;
-			break;
-		case '-':
-			vp->flag &= ~INTEGER;
-			vp->val.s = getoptions();
-			break;
-		default:
-			vp->flag &= ~(ISSET|INTEGER);
-		}
-		return (vp);
-	}
-	l = varsearch(e->loc, &vp, n, h);
-	if (vp != NULL)
-		return (array ? arraysearch(vp, val) : vp);
-	vp = ktenter(&l->vars, n, h);
-	if (array)
-		vp = arraysearch(vp, val);
-	vp->flag |= DEFINED;
-	if (special(n))
-		vp->flag |= SPECIAL;
-	return (vp);
-}
-
-/*
- * Search for local variable, if not found create locally.
- */
-struct tbl *
-local(const char *n, bool copy)
-{
-	struct block *l = e->loc;
-	struct tbl *vp;
-	bool array;
-	uint32_t h, val;
-
-	/* check to see if this is an array */
-	n = array_index_calc(n, &array, &val);
-	mkssert(n != NULL);
-	h = hash(n);
-	if (!ksh_isalphx(*n)) {
-		vp = &vtemp;
-		vp->flag = DEFINED|RDONLY;
-		vp->type = 0;
-		vp->areap = ATEMP;
-		return (vp);
-	}
-	vp = ktenter(&l->vars, n, h);
-	if (copy && !(vp->flag & DEFINED)) {
-		struct tbl *vq;
-
-		varsearch(l->next, &vq, n, h);
-		if (vq != NULL) {
-			vp->flag |= vq->flag &
-			    (EXPORT | INTEGER | RDONLY | LJUST | RJUST |
-			    ZEROFIL | LCASEV | UCASEV_AL | INT_U | INT_L);
-			if (vq->flag & INTEGER)
-				vp->type = vq->type;
-			vp->u2.field = vq->u2.field;
-		}
-	}
-	if (array)
-		vp = arraysearch(vp, val);
-	vp->flag |= DEFINED;
-	if (special(n))
-		vp->flag |= SPECIAL;
-	return (vp);
-}
-
-/* get variable string value */
-char *
-str_val(struct tbl *vp)
-{
-	char *s;
-
-	if ((vp->flag&SPECIAL))
-		getspec(vp);
-	if (!(vp->flag&ISSET))
-		/* special to dollar() */
-		s = null;
-	else if (!(vp->flag&INTEGER))
-		/* string source */
-		s = vp->val.s + vp->type;
-	else {
-		/* integer source */
-		mksh_uari_t n;
-		unsigned int base;
-		/**
-		 * worst case number length is when base == 2:
-		 *	1 (minus) + 2 (base, up to 36) + 1 ('#') +
-		 *	number of bits in the mksh_uari_t + 1 (NUL)
-		 */
-		char strbuf[1 + 2 + 1 + 8 * sizeof(mksh_uari_t) + 1];
-		const char *digits = (vp->flag & UCASEV_AL) ?
-		    digits_uc : digits_lc;
-
-		s = strbuf + sizeof(strbuf);
-		if (vp->flag & INT_U)
-			n = vp->val.u;
-		else
-			n = (vp->val.i < 0) ? -vp->val.u : vp->val.u;
-		base = (vp->type == 0) ? 10U : (unsigned int)vp->type;
-
-		if (base == 1 && n == 0)
-			base = 2;
-		if (base == 1) {
-			size_t sz = 1;
-
-			*(s = strbuf) = '1';
-			s[1] = '#';
-			if (!UTFMODE || ((n & 0xFF80) == 0xEF80))
-				/* OPTU-16 -> raw octet */
-				s[2] = n & 0xFF;
-			else
-				sz = utf_wctomb(s + 2, n);
-			s[2 + sz] = '\0';
-		} else {
-			*--s = '\0';
-			do {
-				*--s = digits[n % base];
-				n /= base;
-			} while (n != 0);
-			if (base != 10) {
-				*--s = '#';
-				*--s = digits[base % 10];
-				if (base >= 10)
-					*--s = digits[base / 10];
-			}
-			if (!(vp->flag & INT_U) && vp->val.i < 0)
-				*--s = '-';
-		}
-		if (vp->flag & (RJUST|LJUST))
-			/* case already dealt with */
-			s = formatstr(vp, s);
-		else
-			strdupx(s, s, ATEMP);
-	}
-	return (s);
-}
-
-/* set variable to string value */
-int
-setstr(struct tbl *vq, const char *s, int error_ok)
-{
-	char *salloc = NULL;
-	bool no_ro_check = tobool(error_ok & 0x4);
-
-	error_ok &= ~0x4;
-	if ((vq->flag & RDONLY) && !no_ro_check) {
-		warningf(true, "read-only: %s", vq->name);
-		if (!error_ok)
-			errorfxz(2);
-		return (0);
-	}
-	if (!(vq->flag&INTEGER)) {
-		/* string dest */
-		if ((vq->flag&ALLOC)) {
-#ifndef MKSH_SMALL
-			/* debugging */
-			if (s >= vq->val.s &&
-			    s <= vq->val.s + strlen(vq->val.s)) {
-				internal_errorf(
-				    "setstr: %s=%s: assigning to self",
-				    vq->name, s);
-			}
-#endif
-			afree(vq->val.s, vq->areap);
-		}
-		vq->flag &= ~(ISSET|ALLOC);
-		vq->type = 0;
-		if (s && (vq->flag & (UCASEV_AL|LCASEV|LJUST|RJUST)))
-			s = salloc = formatstr(vq, s);
-		if ((vq->flag&EXPORT))
-			exportprep(vq, s);
-		else {
-			strdupx(vq->val.s, s, vq->areap);
-			vq->flag |= ALLOC;
-		}
-	} else {
-		/* integer dest */
-		if (!v_evaluate(vq, s, error_ok, true))
-			return (0);
-	}
-	vq->flag |= ISSET;
-	if ((vq->flag&SPECIAL))
-		setspec(vq);
-	afree(salloc, ATEMP);
-	return (1);
-}
-
-/* set variable to integer */
-void
-setint(struct tbl *vq, mksh_ari_t n)
-{
-	if (!(vq->flag&INTEGER)) {
-		struct tbl *vp = &vtemp;
-		vp->flag = (ISSET|INTEGER);
-		vp->type = 0;
-		vp->areap = ATEMP;
-		vp->val.i = n;
-		/* setstr can't fail here */
-		setstr(vq, str_val(vp), KSH_RETURN_ERROR);
-	} else
-		vq->val.i = n;
-	vq->flag |= ISSET;
-	if ((vq->flag&SPECIAL))
-		setspec(vq);
-}
-
-static int
-getint(struct tbl *vp, mksh_ari_u *nump, bool arith)
-{
-	mksh_uari_t c, num, base;
-	const char *s;
-	bool have_base = false, neg = false;
-
-	if (vp->flag&SPECIAL)
-		getspec(vp);
-	/* XXX is it possible for ISSET to be set and val.s to be NULL? */
-	if (!(vp->flag&ISSET) || (!(vp->flag&INTEGER) && vp->val.s == NULL))
-		return (-1);
-	if (vp->flag&INTEGER) {
-		nump->i = vp->val.i;
-		return (vp->type);
-	}
-	s = vp->val.s + vp->type;
-	base = 10;
-	num = 0;
-	if (arith && s[0] == '0' && (s[1] | 0x20) == 'x') {
-		s += 2;
-		base = 16;
-		have_base = true;
-	}
-	if (Flag(FPOSIX) && arith && s[0] == '0' && ksh_isdigit(s[1]) &&
-	    !(vp->flag & ZEROFIL)) {
-		/* interpret as octal (deprecated) */
-		base = 8;
-		have_base = true;
-	}
-	while ((c = *s++)) {
-		if (c == '-') {
-			neg = true;
-			continue;
-		} else if (c == '#') {
-			if (have_base || num < 1 || num > 36)
-				return (-1);
-			if ((base = num) == 1) {
-				unsigned int wc;
-
-				if (!UTFMODE)
-					wc = *(const unsigned char *)s;
-				else if (utf_mbtowc(&wc, s) == (size_t)-1)
-					/* OPTU-8 -> OPTU-16 */
-					/*
-					 * (with a twist: 1#\uEF80 converts
-					 * the same as 1#\x80 does, thus is
-					 * not round-tripping correctly XXX)
-					 */
-					wc = 0xEF00 + *(const unsigned char *)s;
-				nump->u = (mksh_uari_t)wc;
-				return (1);
-			}
-			num = 0;
-			have_base = true;
-			continue;
-		} else if (ksh_isdigit(c))
-			c -= '0';
-		else if (ksh_islower(c))
-			c -= 'a' - 10;
-		else if (ksh_isupper(c))
-			c -= 'A' - 10;
-		else
-			return (-1);
-		if (c >= base)
-			return (-1);
-		num = num * base + c;
-	}
-	if (neg)
-		num = -num;
-	nump->u = num;
-	return (base);
-}
-
-/*
- * convert variable vq to integer variable, setting its value from vp
- * (vq and vp may be the same)
- */
-struct tbl *
-setint_v(struct tbl *vq, struct tbl *vp, bool arith)
-{
-	int base;
-	mksh_ari_u num;
-
-	if ((base = getint(vp, &num, arith)) == -1)
-		return (NULL);
-	setint_n(vq, num.i, 0);
-	if (vq->type == 0)
-		/* default base */
-		vq->type = base;
-	return (vq);
-}
-
-/* convert variable vq to integer variable, setting its value to num */
-void
-setint_n(struct tbl *vq, mksh_ari_t num, int newbase)
-{
-	if (!(vq->flag & INTEGER) && (vq->flag & ALLOC)) {
-		vq->flag &= ~ALLOC;
-		vq->type = 0;
-		afree(vq->val.s, vq->areap);
-	}
-	vq->val.i = num;
-	if (newbase != 0)
-		vq->type = newbase;
-	vq->flag |= ISSET|INTEGER;
-	if (vq->flag&SPECIAL)
-		setspec(vq);
-}
-
-static char *
-formatstr(struct tbl *vp, const char *s)
-{
-	int olen, nlen;
-	char *p, *q;
-	size_t psiz;
-
-	olen = (int)utf_mbswidth(s);
-
-	if (vp->flag & (RJUST|LJUST)) {
-		if (!vp->u2.field)
-			/* default field width */
-			vp->u2.field = olen;
-		nlen = vp->u2.field;
-	} else
-		nlen = olen;
-
-	p = alloc((psiz = nlen * /* MB_LEN_MAX */ 3 + 1), ATEMP);
-	if (vp->flag & (RJUST|LJUST)) {
-		int slen = olen, i = 0;
-
-		if (vp->flag & RJUST) {
-			const char *qq = s;
-			int n = 0;
-
-			while (i < slen)
-				i += utf_widthadj(qq, &qq);
-			/* strip trailing spaces (AT&T uses qq[-1] == ' ') */
-			while (qq > s && ksh_isspace(qq[-1])) {
-				--qq;
-				--slen;
-			}
-			if (vp->flag & ZEROFIL && vp->flag & INTEGER) {
-				if (!s[0] || !s[1])
-					goto uhm_no;
-				if (s[1] == '#')
-					n = 2;
-				else if (s[2] == '#')
-					n = 3;
- uhm_no:
-				if (vp->u2.field <= n)
-					n = 0;
-			}
-			if (n) {
-				memcpy(p, s, n);
-				s += n;
-			}
-			while (slen > vp->u2.field)
-				slen -= utf_widthadj(s, &s);
-			if (vp->u2.field - slen)
-				memset(p + n, (vp->flag & ZEROFIL) ? '0' : ' ',
-				    vp->u2.field - slen);
-			slen -= n;
-			shf_snprintf(p + vp->u2.field - slen,
-			    psiz - (vp->u2.field - slen),
-			    "%.*s", slen, s);
-		} else {
-			/* strip leading spaces/zeros */
-			while (ksh_isspace(*s))
-				s++;
-			if (vp->flag & ZEROFIL)
-				while (*s == '0')
-					s++;
-			shf_snprintf(p, nlen + 1, "%-*.*s",
-				vp->u2.field, vp->u2.field, s);
-		}
-	} else
-		memcpy(p, s, strlen(s) + 1);
-
-	if (vp->flag & UCASEV_AL) {
-		for (q = p; *q; q++)
-			*q = ksh_toupper(*q);
-	} else if (vp->flag & LCASEV) {
-		for (q = p; *q; q++)
-			*q = ksh_tolower(*q);
-	}
-
-	return (p);
-}
-
-/*
- * make vp->val.s be "name=value" for quick exporting.
- */
-static void
-exportprep(struct tbl *vp, const char *val)
-{
-	char *xp;
-	char *op = (vp->flag&ALLOC) ? vp->val.s : NULL;
-	size_t namelen, vallen;
-
-	mkssert(val != NULL);
-
-	namelen = strlen(vp->name);
-	vallen = strlen(val) + 1;
-
-	vp->flag |= ALLOC;
-	/* since name+val are both in memory this can go unchecked */
-	xp = alloc(namelen + 1 + vallen, vp->areap);
-	memcpy(vp->val.s = xp, vp->name, namelen);
-	xp += namelen;
-	*xp++ = '=';
-	/* offset to value */
-	vp->type = xp - vp->val.s;
-	memcpy(xp, val, vallen);
-	if (op != NULL)
-		afree(op, vp->areap);
-}
-
-/*
- * lookup variable (according to (set&LOCAL)), set its attributes
- * (INTEGER, RDONLY, EXPORT, TRACE, LJUST, RJUST, ZEROFIL, LCASEV,
- * UCASEV_AL), and optionally set its value if an assignment.
- */
-struct tbl *
-typeset(const char *var, uint32_t set, uint32_t clr, int field, int base)
-{
-	struct tbl *vp;
-	struct tbl *vpbase, *t;
-	char *tvar;
-	const char *val;
-	size_t len;
-	bool vappend = false;
-
-	/* check for valid variable name, search for value */
-	val = skip_varname(var, false);
-	if (val == var) {
-		/* no variable name given */
-		return (NULL);
-	}
-	if (*val == '[') {
-		if (set_refflag != SRF_NOP)
-			errorf("%s: %s", var,
-			    "reference variable can't be an array");
-		len = array_ref_len(val);
-		if (len == 0)
-			return (NULL);
-		/*
-		 * IMPORT is only used when the shell starts up and is
-		 * setting up its environment. Allow only simple array
-		 * references at this time since parameter/command
-		 * substitution is performed on the [expression] which
-		 * would be a major security hole.
-		 */
-		if (set & IMPORT) {
-			size_t i;
-
-			for (i = 1; i < len - 1; i++)
-				if (!ksh_isdigit(val[i]))
-					return (NULL);
-		}
-		val += len;
-	}
-	if (val[0] == '=' || (val[0] == '+' && val[1] == '=')) {
-		strndupx(tvar, var, val - var, ATEMP);
-		if (*val++ == '+') {
-			++val;
-			vappend = true;
-		}
-	} else if ((val[0] != '\0') || (set & IMPORT)) {
-		/*
-		 * must have a = when setting a variable by importing
-		 * the original environment, otherwise be empty; we
-		 * also end up here when a variable name was invalid
-		 */
-		return (NULL);
-	} else {
-		/* just varname with no value part nor equals sign */
-		strdupx(tvar, var, ATEMP);
-		val = NULL;
-		/* handle foo[*] => foo (whole array) mapping for R39b */
-		len = strlen(tvar);
-		if (len > 3 && tvar[len - 3] == '[' && tvar[len - 2] == '*' &&
-		    tvar[len - 1] == ']')
-			tvar[len - 3] = '\0';
-	}
-
-	if (set_refflag == SRF_ENABLE) {
-		const char *qval;
-
-		/* bail out on 'nameref foo+=bar' */
-		if (vappend)
-			errorfz();
-		/* find value if variable already exists */
-		if ((qval = val) == NULL) {
-			varsearch(e->loc, &vp, tvar, hash(tvar));
-			if (vp != NULL)
-				qval = str_val(vp);
-		}
-		/* prevent nameref loops */
-		while (qval) {
-			if (!strcmp(qval, tvar))
-				errorf("%s: %s", qval,
-				    "expression recurses on parameter");
-			varsearch(e->loc, &vp, qval, hash(qval));
-			qval = NULL;
-			if (vp && ((vp->flag & (ARRAY|ASSOC)) == ASSOC))
-				qval = str_val(vp);
-		}
-	}
-
-	/* prevent typeset from creating a local PATH/ENV/SHELL */
-	if (Flag(FRESTRICTED) && (strcmp(tvar, "PATH") == 0 ||
-	    strcmp(tvar, "ENV") == 0 || strcmp(tvar, "SHELL") == 0))
-		errorf("%s: %s", tvar, "restricted");
-
-	vp = (set&LOCAL) ? local(tvar, tobool(set & LOCAL_COPY)) :
-	    global(tvar);
-	if (set_refflag == SRF_DISABLE && (vp->flag & (ARRAY|ASSOC)) == ASSOC)
-		vp->flag &= ~ASSOC;
-	else if (set_refflag == SRF_ENABLE) {
-		if (vp->flag & ARRAY) {
-			struct tbl *a, *tmp;
-
-			/* free up entire array */
-			for (a = vp->u.array; a; ) {
-				tmp = a;
-				a = a->u.array;
-				if (tmp->flag & ALLOC)
-					afree(tmp->val.s, tmp->areap);
-				afree(tmp, tmp->areap);
-			}
-			vp->u.array = NULL;
-			vp->flag &= ~ARRAY;
-		}
-		vp->flag |= ASSOC;
-	}
-
-	set &= ~(LOCAL|LOCAL_COPY);
-
-	vpbase = (vp->flag & ARRAY) ? global(arrayname(var)) : vp;
-
-	/*
-	 * only allow export flag to be set; AT&T ksh allows any
-	 * attribute to be changed which means it can be truncated or
-	 * modified (-L/-R/-Z/-i)
-	 */
-	if ((vpbase->flag&RDONLY) &&
-	    (val || clr || (set & ~EXPORT)))
-		/* XXX check calls - is error here ok by POSIX? */
-		errorfx(2, "read-only: %s", tvar);
-	afree(tvar, ATEMP);
-
-	/* most calls are with set/clr == 0 */
-	if (set | clr) {
-		bool ok = true;
-
-		/*
-		 * XXX if x[0] isn't set, there will be problems: need
-		 * to have one copy of attributes for arrays...
-		 */
-		for (t = vpbase; t; t = t->u.array) {
-			bool fake_assign;
-			char *s = NULL;
-			char *free_me = NULL;
-
-			fake_assign = (t->flag & ISSET) && (!val || t != vp) &&
-			    ((set & (UCASEV_AL|LCASEV|LJUST|RJUST|ZEROFIL)) ||
-			    ((t->flag & INTEGER) && (clr & INTEGER)) ||
-			    (!(t->flag & INTEGER) && (set & INTEGER)));
-			if (fake_assign) {
-				if (t->flag & INTEGER) {
-					s = str_val(t);
-					free_me = NULL;
-				} else {
-					s = t->val.s + t->type;
-					free_me = (t->flag & ALLOC) ? t->val.s :
-					    NULL;
-				}
-				t->flag &= ~ALLOC;
-			}
-			if (!(t->flag & INTEGER) && (set & INTEGER)) {
-				t->type = 0;
-				t->flag &= ~ALLOC;
-			}
-			t->flag = (t->flag | set) & ~clr;
-			/*
-			 * Don't change base if assignment is to be
-			 * done, in case assignment fails.
-			 */
-			if ((set & INTEGER) && base > 0 && (!val || t != vp))
-				t->type = base;
-			if (set & (LJUST|RJUST|ZEROFIL))
-				t->u2.field = field;
-			if (fake_assign) {
-				if (!setstr(t, s, KSH_RETURN_ERROR)) {
-					/*
-					 * Somewhat arbitrary action
-					 * here: zap contents of
-					 * variable, but keep the flag
-					 * settings.
-					 */
-					ok = false;
-					if (t->flag & INTEGER)
-						t->flag &= ~ISSET;
-					else {
-						if (t->flag & ALLOC)
-							afree(t->val.s, t->areap);
-						t->flag &= ~(ISSET|ALLOC);
-						t->type = 0;
-					}
-				}
-				if (free_me)
-					afree(free_me, t->areap);
-			}
-		}
-		if (!ok)
-			errorfz();
-	}
-
-	if (val != NULL) {
-		char *tval;
-
-		if (vappend) {
-			tval = shf_smprintf("%s%s", str_val(vp), val);
-			val = tval;
-		} else
-			tval = NULL;
-
-		if (vp->flag&INTEGER) {
-			/* do not zero base before assignment */
-			setstr(vp, val, KSH_UNWIND_ERROR | 0x4);
-			/* done after assignment to override default */
-			if (base > 0)
-				vp->type = base;
-		} else
-			/* setstr can't fail (readonly check already done) */
-			setstr(vp, val, KSH_RETURN_ERROR | 0x4);
-
-		if (tval != NULL)
-			afree(tval, ATEMP);
-	}
-
-	/* only x[0] is ever exported, so use vpbase */
-	if ((vpbase->flag&EXPORT) && !(vpbase->flag&INTEGER) &&
-	    vpbase->type == 0)
-		exportprep(vpbase, (vpbase->flag&ISSET) ? vpbase->val.s : null);
-
-	return (vp);
-}
-
-/**
- * Unset a variable. The flags can be:
- * |1	= tear down entire array
- * |2	= keep attributes, only unset content
- */
-void
-unset(struct tbl *vp, int flags)
-{
-	if (vp->flag & ALLOC)
-		afree(vp->val.s, vp->areap);
-	if ((vp->flag & ARRAY) && (flags & 1)) {
-		struct tbl *a, *tmp;
-
-		/* free up entire array */
-		for (a = vp->u.array; a; ) {
-			tmp = a;
-			a = a->u.array;
-			if (tmp->flag & ALLOC)
-				afree(tmp->val.s, tmp->areap);
-			afree(tmp, tmp->areap);
-		}
-		vp->u.array = NULL;
-	}
-	if (flags & 2) {
-		vp->flag &= ~(ALLOC|ISSET);
-		return;
-	}
-	/* if foo[0] is being unset, the remainder of the array is kept... */
-	vp->flag &= SPECIAL | ((flags & 1) ? 0 : ARRAY|DEFINED);
-	if (vp->flag & SPECIAL)
-		/* responsible for 'unspecial'ing var */
-		unsetspec(vp);
-}
-
-/*
- * Return a pointer to the first char past a legal variable name
- * (returns the argument if there is no legal name, returns a pointer to
- * the terminating NUL if whole string is legal).
- */
-const char *
-skip_varname(const char *s, int aok)
-{
-	size_t alen;
-
-	if (s && ksh_isalphx(*s)) {
-		while (*++s && ksh_isalnux(*s))
-			;
-		if (aok && *s == '[' && (alen = array_ref_len(s)))
-			s += alen;
-	}
-	return (s);
-}
-
-/* Return a pointer to the first character past any legal variable name */
-const char *
-skip_wdvarname(const char *s,
-    /* skip array de-reference? */
-    bool aok)
-{
-	if (s[0] == CHAR && ksh_isalphx(s[1])) {
-		do {
-			s += 2;
-		} while (s[0] == CHAR && ksh_isalnux(s[1]));
-		if (aok && s[0] == CHAR && s[1] == '[') {
-			/* skip possible array de-reference */
-			const char *p = s;
-			char c;
-			int depth = 0;
-
-			while (/* CONSTCOND */ 1) {
-				if (p[0] != CHAR)
-					break;
-				c = p[1];
-				p += 2;
-				if (c == '[')
-					depth++;
-				else if (c == ']' && --depth == 0) {
-					s = p;
-					break;
-				}
-			}
-		}
-	}
-	return (s);
-}
-
-/* Check if coded string s is a variable name */
-int
-is_wdvarname(const char *s, bool aok)
-{
-	const char *p = skip_wdvarname(s, aok);
-
-	return (p != s && p[0] == EOS);
-}
-
-/* Check if coded string s is a variable assignment */
-int
-is_wdvarassign(const char *s)
-{
-	const char *p = skip_wdvarname(s, true);
-
-	return (p != s && p[0] == CHAR &&
-	    (p[1] == '=' || (p[1] == '+' && p[2] == CHAR && p[3] == '=')));
-}
-
-/*
- * Make the exported environment from the exported names in the dictionary.
- */
-char **
-makenv(void)
-{
-	ssize_t i;
-	struct block *l;
-	XPtrV denv;
-	struct tbl *vp, **vpp;
-
-	XPinit(denv, 64);
-	for (l = e->loc; l != NULL; l = l->next) {
-		vpp = l->vars.tbls;
-		i = 1 << (l->vars.tshift);
-		while (--i >= 0)
-			if ((vp = *vpp++) != NULL &&
-			    (vp->flag&(ISSET|EXPORT)) == (ISSET|EXPORT)) {
-				struct block *l2;
-				struct tbl *vp2;
-				uint32_t h = hash(vp->name);
-
-				/* unexport any redefined instances */
-				for (l2 = l->next; l2 != NULL; l2 = l2->next) {
-					vp2 = ktsearch(&l2->vars, vp->name, h);
-					if (vp2 != NULL)
-						vp2->flag &= ~EXPORT;
-				}
-				if ((vp->flag&INTEGER)) {
-					/* integer to string */
-					char *val;
-					val = str_val(vp);
-					vp->flag &= ~(INTEGER|RDONLY|SPECIAL);
-					/* setstr can't fail here */
-					setstr(vp, val, KSH_RETURN_ERROR);
-				}
-				XPput(denv, vp->val.s);
-			}
-	}
-	XPput(denv, NULL);
-	return ((char **)XPclose(denv));
-}
-
-/*
- * handle special variables with side effects - PATH, SECONDS.
- */
-
-/* Test if name is a special parameter */
-static int
-special(const char *name)
-{
-	struct tbl *tp;
-
-	tp = ktsearch(&specials, name, hash(name));
-	return (tp && (tp->flag & ISSET) ? tp->type : V_NONE);
-}
-
-/* Make a variable non-special */
-static void
-unspecial(const char *name)
-{
-	struct tbl *tp;
-
-	tp = ktsearch(&specials, name, hash(name));
-	if (tp)
-		ktdelete(tp);
-}
-
-static time_t seconds;		/* time SECONDS last set */
-static int user_lineno;		/* what user set $LINENO to */
-
-static void
-getspec(struct tbl *vp)
-{
-	mksh_ari_u num;
-	int st;
-	struct timeval tv;
-
-	switch ((st = special(vp->name))) {
-	case V_COLUMNS:
-	case V_LINES:
-		/*
-		 * Do NOT export COLUMNS/LINES. Many applications
-		 * check COLUMNS/LINES before checking ws.ws_col/row,
-		 * so if the app is started with C/L in the environ
-		 * and the window is then resized, the app won't
-		 * see the change cause the environ doesn't change.
-		 */
-		if (got_winch)
-			change_winsz();
-		break;
-	}
-	switch (st) {
-	case V_BASHPID:
-		num.u = (mksh_uari_t)procpid;
-		break;
-	case V_COLUMNS:
-		num.i = x_cols;
-		break;
-	case V_HISTSIZE:
-		num.i = histsize;
-		break;
-	case V_LINENO:
-		num.i = current_lineno + user_lineno;
-		break;
-	case V_LINES:
-		num.i = x_lins;
-		break;
-	case V_EPOCHREALTIME: {
-		/* 10(%u) + 1(.) + 6 + NUL */
-		char buf[18];
-
-		vp->flag &= ~SPECIAL;
-		mksh_TIME(tv);
-		shf_snprintf(buf, sizeof(buf), "%u.%06u",
-		    (unsigned)tv.tv_sec, (unsigned)tv.tv_usec);
-		setstr(vp, buf, KSH_RETURN_ERROR | 0x4);
-		vp->flag |= SPECIAL;
-		return;
-	}
-	case V_OPTIND:
-		num.i = user_opt.uoptind;
-		break;
-	case V_RANDOM:
-		num.i = rndget();
-		break;
-	case V_SECONDS:
-		/*
-		 * On start up the value of SECONDS is used before
-		 * it has been set - don't do anything in this case
-		 * (see initcoms[] in main.c).
-		 */
-		if (vp->flag & ISSET) {
-			mksh_TIME(tv);
-			num.i = tv.tv_sec - seconds;
-		} else
-			return;
-		break;
-	default:
-		/* do nothing, do not touch vp at all */
-		return;
-	}
-	vp->flag &= ~SPECIAL;
-	setint_n(vp, num.i, 0);
-	vp->flag |= SPECIAL;
-}
-
-static void
-setspec(struct tbl *vp)
-{
-	mksh_ari_u num;
-	char *s;
-	int st;
-
-	switch ((st = special(vp->name))) {
-#if HAVE_PERSISTENT_HISTORY
-	case V_HISTFILE:
-		sethistfile(str_val(vp));
-		return;
-#endif
-	case V_IFS:
-		setctypes(s = str_val(vp), C_IFS);
-		ifs0 = *s;
-		return;
-	case V_PATH:
-		if (path)
-			afree(path, APERM);
-		s = str_val(vp);
-		strdupx(path, s, APERM);
-		/* clear tracked aliases */
-		flushcom(true);
-		return;
-	case V_TMPDIR:
-		if (tmpdir) {
-			afree(tmpdir, APERM);
-			tmpdir = NULL;
-		}
-		/*
-		 * Use tmpdir iff it is an absolute path, is writable
-		 * and searchable and is a directory...
-		 */
-		{
-			struct stat statb;
-
-			s = str_val(vp);
-			/* LINTED use of access */
-			if (s[0] == '/' && access(s, W_OK|X_OK) == 0 &&
-			    stat(s, &statb) == 0 && S_ISDIR(statb.st_mode))
-				strdupx(tmpdir, s, APERM);
-		}
-		return;
-	/* common sub-cases */
-	case V_COLUMNS:
-	case V_LINES:
-		if (vp->flag & IMPORT) {
-			/* do not touch */
-			unspecial(vp->name);
-			vp->flag &= ~SPECIAL;
-			return;
-		}
-		/* FALLTHROUGH */
-	case V_HISTSIZE:
-	case V_LINENO:
-	case V_OPTIND:
-	case V_RANDOM:
-	case V_SECONDS:
-	case V_TMOUT:
-		vp->flag &= ~SPECIAL;
-		if (getint(vp, &num, false) == -1) {
-			s = str_val(vp);
-			if (st != V_RANDOM)
-				errorf("%s: %s: %s", vp->name, "bad number", s);
-			num.u = hash(s);
-		}
-		vp->flag |= SPECIAL;
-		break;
-	default:
-		/* do nothing, do not touch vp at all */
-		return;
-	}
-
-	/* process the singular parts of the common cases */
-
-	switch (st) {
-	case V_COLUMNS:
-		if (num.i >= MIN_COLS)
-			x_cols = num.i;
-		break;
-	case V_HISTSIZE:
-		sethistsize(num.i);
-		break;
-	case V_LINENO:
-		/* The -1 is because line numbering starts at 1. */
-		user_lineno = num.u - current_lineno - 1;
-		break;
-	case V_LINES:
-		if (num.i >= MIN_LINS)
-			x_lins = num.i;
-		break;
-	case V_OPTIND:
-		getopts_reset((int)num.i);
-		break;
-	case V_RANDOM:
-		/*
-		 * mksh R39d+ no longer has the traditional repeatability
-		 * of $RANDOM sequences, but always retains state
-		 */
-		rndset((unsigned long)num.u);
-		break;
-	case V_SECONDS:
-		{
-			struct timeval tv;
-
-			mksh_TIME(tv);
-			seconds = tv.tv_sec - num.i;
-		}
-		break;
-	case V_TMOUT:
-		ksh_tmout = num.i >= 0 ? num.i : 0;
-		break;
-	}
-}
-
-static void
-unsetspec(struct tbl *vp)
-{
-	/*
-	 * AT&T ksh man page says OPTIND, OPTARG and _ lose special
-	 * meaning, but OPTARG does not (still set by getopts) and _ is
-	 * also still set in various places. Don't know what AT&T does
-	 * for HISTSIZE, HISTFILE. Unsetting these in AT&T ksh does not
-	 * loose the 'specialness': IFS, COLUMNS, PATH, TMPDIR
-	 */
-
-	switch (special(vp->name)) {
-	case V_IFS:
-		setctypes(TC_IFSWS, C_IFS);
-		ifs0 = ' ';
-		break;
-	case V_PATH:
-		if (path)
-			afree(path, APERM);
-		strdupx(path, def_path, APERM);
-		/* clear tracked aliases */
-		flushcom(true);
-		break;
-	case V_TMPDIR:
-		/* should not become unspecial */
-		if (tmpdir) {
-			afree(tmpdir, APERM);
-			tmpdir = NULL;
-		}
-		break;
-	case V_LINENO:
-	case V_RANDOM:
-	case V_SECONDS:
-	case V_TMOUT:
-		/* AT&T ksh leaves previous value in place */
-		unspecial(vp->name);
-		break;
-	}
-}
-
-/*
- * Search for (and possibly create) a table entry starting with
- * vp, indexed by val.
- */
-struct tbl *
-arraysearch(struct tbl *vp, uint32_t val)
-{
-	struct tbl *prev, *curr, *news;
-	size_t len;
-
-	vp->flag = (vp->flag | (ARRAY|DEFINED)) & ~ASSOC;
-	/* the table entry is always [0] */
-	if (val == 0)
-		return (vp);
-	prev = vp;
-	curr = vp->u.array;
-	while (curr && curr->ua.index < val) {
-		prev = curr;
-		curr = curr->u.array;
-	}
-	if (curr && curr->ua.index == val) {
-		if (curr->flag&ISSET)
-			return (curr);
-		news = curr;
-	} else
-		news = NULL;
-	if (!news) {
-		len = strlen(vp->name);
-		checkoktoadd(len, 1 + offsetof(struct tbl, name[0]));
-		news = alloc(offsetof(struct tbl, name[0]) + ++len, vp->areap);
-		memcpy(news->name, vp->name, len);
-	}
-	news->flag = (vp->flag & ~(ALLOC|DEFINED|ISSET|SPECIAL)) | AINDEX;
-	news->type = vp->type;
-	news->areap = vp->areap;
-	news->u2.field = vp->u2.field;
-	news->ua.index = val;
-
-	if (curr != news) {
-		/* not reusing old array entry */
-		prev->u.array = news;
-		news->u.array = curr;
-	}
-	return (news);
-}
-
-/*
- * Return the length of an array reference (eg, [1+2]) - cp is assumed
- * to point to the open bracket. Returns 0 if there is no matching
- * closing bracket.
- */
-size_t
-array_ref_len(const char *cp)
-{
-	const char *s = cp;
-	char c;
-	int depth = 0;
-
-	while ((c = *s++) && (c != ']' || --depth))
-		if (c == '[')
-			depth++;
-	if (!c)
-		return (0);
-	return (s - cp);
-}
-
-/*
- * Make a copy of the base of an array name
- */
-char *
-arrayname(const char *str)
-{
-	const char *p;
-	char *rv;
-
-	if ((p = cstrchr(str, '[')) == 0)
-		/* Shouldn't happen, but why worry? */
-		strdupx(rv, str, ATEMP);
-	else
-		strndupx(rv, str, p - str, ATEMP);
-
-	return (rv);
-}
-
-/* set (or overwrite, if reset) the array variable var to the values in vals */
-mksh_uari_t
-set_array(const char *var, bool reset, const char **vals)
-{
-	struct tbl *vp, *vq;
-	mksh_uari_t i = 0, j = 0;
-	const char *ccp = var;
-	char *cp = NULL;
-	size_t n;
-
-	/* to get local array, use "local foo; set -A foo" */
-	n = strlen(var);
-	if (n > 0 && var[n - 1] == '+') {
-		/* append mode */
-		reset = false;
-		strndupx(cp, var, n - 1, ATEMP);
-		ccp = cp;
-	}
-	vp = global(ccp);
-
-	/* Note: AT&T ksh allows set -A but not set +A of a read-only var */
-	if ((vp->flag&RDONLY))
-		errorfx(2, "read-only: %s", ccp);
-	/* This code is quite non-optimal */
-	if (reset) {
-		/* trash existing values and attributes */
-		unset(vp, 1);
-		/* allocate-by-access the [0] element to keep in scope */
-		arraysearch(vp, 0);
-	}
-	/*
-	 * TODO: would be nice for assignment to completely succeed or
-	 * completely fail. Only really effects integer arrays:
-	 * evaluation of some of vals[] may fail...
-	 */
-	if (cp != NULL) {
-		/* find out where to set when appending */
-		for (vq = vp; vq; vq = vq->u.array) {
-			if (!(vq->flag & ISSET))
-				continue;
-			if (arrayindex(vq) >= j)
-				j = arrayindex(vq) + 1;
-		}
-		afree(cp, ATEMP);
-	}
-	while ((ccp = vals[i])) {
-		if (*ccp == '[') {
-			int level = 0;
-
-			while (*ccp) {
-				if (*ccp == ']' && --level == 0)
-					break;
-				if (*ccp == '[')
-					++level;
-				++ccp;
-			}
-			if (*ccp == ']' && level == 0 && ccp[1] == '=') {
-				strndupx(cp, vals[i] + 1, ccp - (vals[i] + 1),
-				    ATEMP);
-				evaluate(substitute(cp, 0), (mksh_ari_t *)&j,
-				    KSH_UNWIND_ERROR, true);
-				afree(cp, ATEMP);
-				ccp += 2;
-			} else
-				ccp = vals[i];
-		}
-
-		vq = arraysearch(vp, j);
-		/* would be nice to deal with errors here... (see above) */
-		setstr(vq, ccp, KSH_RETURN_ERROR);
-		i++;
-		j++;
-	}
-
-	return (i);
-}
-
-void
-change_winsz(void)
-{
-#ifdef TIOCGWINSZ
-	/* check if window size has changed */
-	if (tty_init_fd() < 2) {
-		struct winsize ws;
-
-		if (ioctl(tty_fd, TIOCGWINSZ, &ws) >= 0) {
-			if (ws.ws_col)
-				x_cols = ws.ws_col;
-			if (ws.ws_row)
-				x_lins = ws.ws_row;
-		}
-	}
-#endif
-
-	/* bounds check for sane values, use defaults otherwise */
-	if (x_cols < MIN_COLS)
-		x_cols = 80;
-	if (x_lins < MIN_LINS)
-		x_lins = 24;
-
-#ifdef SIGWINCH
-	got_winch = 0;
-#endif
-}
-
-uint32_t
-hash(const void *s)
-{
-	register uint32_t h;
-
-	NZATInit(h);
-	NZATUpdateString(h, s);
-	NZAATFinish(h);
-	return (h);
-}
-
-mksh_ari_t
-rndget(void)
-{
-	/*
-	 * this is the same Linear Congruential PRNG as Borland
-	 * C/C++ allegedly uses in its built-in rand() function
-	 */
-	return (((lcg_state = 22695477 * lcg_state + 1) >> 16) & 0x7FFF);
-}
-
-void
-rndset(unsigned long v)
-{
-	register uint32_t h;
-
-	NZATInit(h);
-	NZATUpdateMem(h, &lcg_state, sizeof(lcg_state));
-	NZATUpdateMem(h, &v, sizeof(v));
-
-#if defined(arc4random_pushb_fast) || defined(MKSH_A4PB)
-	/*
-	 * either we have very chap entropy get and push available,
-	 * with malloc() pulling in this code already anyway, or the
-	 * user requested us to use the old functions
-	 */
-	lcg_state = h;
-	NZAATFinish(lcg_state);
-#if defined(arc4random_pushb_fast)
-	arc4random_pushb_fast(&lcg_state, sizeof(lcg_state));
-	lcg_state = arc4random();
-#else
-	lcg_state = arc4random_pushb(&lcg_state, sizeof(lcg_state));
-#endif
-	NZATUpdateMem(h, &lcg_state, sizeof(lcg_state));
-#endif
-
-	NZAATFinish(h);
-	lcg_state = h;
-}

Copied: vendor/MirOS/mksh/R50/var.c (from rev 6707, vendor/MirOS/mksh/dist/var.c)
===================================================================
--- vendor/MirOS/mksh/R50/var.c	                        (rev 0)
+++ vendor/MirOS/mksh/R50/var.c	2014-07-01 12:01:17 UTC (rev 6708)
@@ -0,0 +1,1670 @@
+/*	$OpenBSD: var.c,v 1.38 2013/12/20 17:53:09 zhuk Exp $	*/
+
+/*-
+ * Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
+ *		 2011, 2012, 2013, 2014
+ *	Thorsten Glaser <tg at mirbsd.org>
+ *
+ * Provided that these terms and disclaimer and all copyright notices
+ * are retained or reproduced in an accompanying document, permission
+ * is granted to deal in this work without restriction, including un-
+ * limited rights to use, publicly perform, distribute, sell, modify,
+ * merge, give away, or sublicence.
+ *
+ * This work is provided "AS IS" and WITHOUT WARRANTY of any kind, to
+ * the utmost extent permitted by applicable law, neither express nor
+ * implied; without malicious intent or gross negligence. In no event
+ * may a licensor, author or contributor be held liable for indirect,
+ * direct, other damage, loss, or other issues arising in any way out
+ * of dealing in the work, even if advised of the possibility of such
+ * damage or existence of a defect, except proven that it results out
+ * of said person's immediate fault when using the work as intended.
+ */
+
+#include "sh.h"
+#include "mirhash.h"
+
+#if defined(__OpenBSD__)
+#include <sys/sysctl.h>
+#endif
+
+__RCSID("$MirOS: src/bin/mksh/var.c,v 1.180 2014/06/26 20:36:02 tg Exp $");
+
+/*-
+ * Variables
+ *
+ * WARNING: unreadable code, needs a rewrite
+ *
+ * if (flag&INTEGER), val.i contains integer value, and type contains base.
+ * otherwise, (val.s + type) contains string value.
+ * if (flag&EXPORT), val.s contains "name=value" for E-Z exporting.
+ */
+
+static struct table specials;
+static uint32_t lcg_state = 5381, qh_state = 4711;
+/* may only be set by typeset() just before call to array_index_calc() */
+static enum namerefflag innermost_refflag = SRF_NOP;
+
+static char *formatstr(struct tbl *, const char *);
+static void exportprep(struct tbl *, const char *);
+static int special(const char *);
+static void unspecial(const char *);
+static void getspec(struct tbl *);
+static void setspec(struct tbl *);
+static void unsetspec(struct tbl *);
+static int getint(struct tbl *, mksh_ari_u *, bool);
+static const char *array_index_calc(const char *, bool *, uint32_t *);
+
+/*
+ * create a new block for function calls and simple commands
+ * assume caller has allocated and set up e->loc
+ */
+void
+newblock(void)
+{
+	struct block *l;
+	static const char *empty[] = { null };
+
+	l = alloc(sizeof(struct block), ATEMP);
+	l->flags = 0;
+	/* TODO: could use e->area (l->area => l->areap) */
+	ainit(&l->area);
+	if (!e->loc) {
+		l->argc = 0;
+		l->argv = empty;
+	} else {
+		l->argc = e->loc->argc;
+		l->argv = e->loc->argv;
+	}
+	l->exit = l->error = NULL;
+	ktinit(&l->area, &l->vars, 0);
+	ktinit(&l->area, &l->funs, 0);
+	l->next = e->loc;
+	e->loc = l;
+}
+
+/*
+ * pop a block handling special variables
+ */
+void
+popblock(void)
+{
+	ssize_t i;
+	struct block *l = e->loc;
+	struct tbl *vp, **vpp = l->vars.tbls, *vq;
+
+	/* pop block */
+	e->loc = l->next;
+
+	i = 1 << (l->vars.tshift);
+	while (--i >= 0)
+		if ((vp = *vpp++) != NULL && (vp->flag&SPECIAL)) {
+			if ((vq = global(vp->name))->flag & ISSET)
+				setspec(vq);
+			else
+				unsetspec(vq);
+		}
+	if (l->flags & BF_DOGETOPTS)
+		user_opt = l->getopts_state;
+	afreeall(&l->area);
+	afree(l, ATEMP);
+}
+
+/* called by main() to initialise variable data structures */
+#define VARSPEC_DEFNS
+#include "var_spec.h"
+
+enum var_specs {
+#define VARSPEC_ENUMS
+#include "var_spec.h"
+	V_MAX
+};
+
+/* this is biased with -1 relative to VARSPEC_ENUMS */
+static const char * const initvar_names[] = {
+#define VARSPEC_ITEMS
+#include "var_spec.h"
+};
+
+void
+initvar(void)
+{
+	int i = 0;
+	struct tbl *tp;
+
+	ktinit(APERM, &specials,
+	    /* currently 14 specials: 75% of 32 = 2^5 */
+	    5);
+	while (i < V_MAX - 1) {
+		tp = ktenter(&specials, initvar_names[i],
+		    hash(initvar_names[i]));
+		tp->flag = DEFINED|ISSET;
+		tp->type = ++i;
+	}
+}
+
+/* common code for several functions below and c_typeset() */
+struct block *
+varsearch(struct block *l, struct tbl **vpp, const char *vn, uint32_t h)
+{
+	register struct tbl *vp;
+
+	if (l) {
+ varsearch_loop:
+		if ((vp = ktsearch(&l->vars, vn, h)) != NULL)
+			goto varsearch_out;
+		if (l->next != NULL) {
+			l = l->next;
+			goto varsearch_loop;
+		}
+	}
+	vp = NULL;
+ varsearch_out:
+	*vpp = vp;
+	return (l);
+}
+
+/*
+ * Used to calculate an array index for global()/local(). Sets *arrayp
+ * to true if this is an array, sets *valp to the array index, returns
+ * the basename of the array. May only be called from global()/local()
+ * and must be their first callee.
+ */
+static const char *
+array_index_calc(const char *n, bool *arrayp, uint32_t *valp)
+{
+	const char *p;
+	size_t len;
+	char *ap = NULL;
+
+	*arrayp = false;
+ redo_from_ref:
+	p = skip_varname(n, false);
+	if (innermost_refflag == SRF_NOP && (p != n) && ksh_isalphx(n[0])) {
+		struct tbl *vp;
+		char *vn;
+
+		strndupx(vn, n, p - n, ATEMP);
+		/* check if this is a reference */
+		varsearch(e->loc, &vp, vn, hash(vn));
+		afree(vn, ATEMP);
+		if (vp && (vp->flag & (DEFINED | ASSOC | ARRAY)) ==
+		    (DEFINED | ASSOC)) {
+			char *cp;
+
+			/* gotcha! */
+			cp = shf_smprintf("%s%s", str_val(vp), p);
+			afree(ap, ATEMP);
+			n = ap = cp;
+			goto redo_from_ref;
+		}
+	}
+	innermost_refflag = SRF_NOP;
+
+	if (p != n && *p == '[' && (len = array_ref_len(p))) {
+		char *sub, *tmp;
+		mksh_ari_t rval;
+
+		/* calculate the value of the subscript */
+		*arrayp = true;
+		strndupx(tmp, p + 1, len - 2, ATEMP);
+		sub = substitute(tmp, 0);
+		afree(tmp, ATEMP);
+		strndupx(n, n, p - n, ATEMP);
+		evaluate(sub, &rval, KSH_UNWIND_ERROR, true);
+		*valp = (uint32_t)rval;
+		afree(sub, ATEMP);
+	}
+	return (n);
+}
+
+/*
+ * Search for variable, if not found create globally.
+ */
+struct tbl *
+global(const char *n)
+{
+	struct block *l = e->loc;
+	struct tbl *vp;
+	int c;
+	bool array;
+	uint32_t h, val;
+
+	/*
+	 * check to see if this is an array;
+	 * dereference namerefs; must come first
+	 */
+	n = array_index_calc(n, &array, &val);
+	h = hash(n);
+	c = (unsigned char)n[0];
+	if (!ksh_isalphx(c)) {
+		if (array)
+			errorf("bad substitution");
+		vp = &vtemp;
+		vp->flag = DEFINED;
+		vp->type = 0;
+		vp->areap = ATEMP;
+		*vp->name = c;
+		if (ksh_isdigit(c)) {
+			if (getn(n, &c) && (c <= l->argc))
+				/* setstr can't fail here */
+				setstr(vp, l->argv[c], KSH_RETURN_ERROR);
+			vp->flag |= RDONLY;
+			return (vp);
+		}
+		vp->flag |= RDONLY;
+		if (n[1] != '\0')
+			return (vp);
+		vp->flag |= ISSET|INTEGER;
+		switch (c) {
+		case '$':
+			vp->val.i = kshpid;
+			break;
+		case '!':
+			/* if no job, expand to nothing */
+			if ((vp->val.i = j_async()) == 0)
+				vp->flag &= ~(ISSET|INTEGER);
+			break;
+		case '?':
+			vp->val.i = exstat & 0xFF;
+			break;
+		case '#':
+			vp->val.i = l->argc;
+			break;
+		case '-':
+			vp->flag &= ~INTEGER;
+			vp->val.s = getoptions();
+			break;
+		default:
+			vp->flag &= ~(ISSET|INTEGER);
+		}
+		return (vp);
+	}
+	l = varsearch(e->loc, &vp, n, h);
+	if (vp != NULL)
+		return (array ? arraysearch(vp, val) : vp);
+	vp = ktenter(&l->vars, n, h);
+	if (array)
+		vp = arraysearch(vp, val);
+	vp->flag |= DEFINED;
+	if (special(n))
+		vp->flag |= SPECIAL;
+	return (vp);
+}
+
+/*
+ * Search for local variable, if not found create locally.
+ */
+struct tbl *
+local(const char *n, bool copy)
+{
+	struct block *l = e->loc;
+	struct tbl *vp;
+	bool array;
+	uint32_t h, val;
+
+	/*
+	 * check to see if this is an array;
+	 * dereference namerefs; must come first
+	 */
+	n = array_index_calc(n, &array, &val);
+	mkssert(n != NULL);
+	h = hash(n);
+	if (!ksh_isalphx(*n)) {
+		vp = &vtemp;
+		vp->flag = DEFINED|RDONLY;
+		vp->type = 0;
+		vp->areap = ATEMP;
+		return (vp);
+	}
+	vp = ktenter(&l->vars, n, h);
+	if (copy && !(vp->flag & DEFINED)) {
+		struct tbl *vq;
+
+		varsearch(l->next, &vq, n, h);
+		if (vq != NULL) {
+			vp->flag |= vq->flag &
+			    (EXPORT | INTEGER | RDONLY | LJUST | RJUST |
+			    ZEROFIL | LCASEV | UCASEV_AL | INT_U | INT_L);
+			if (vq->flag & INTEGER)
+				vp->type = vq->type;
+			vp->u2.field = vq->u2.field;
+		}
+	}
+	if (array)
+		vp = arraysearch(vp, val);
+	vp->flag |= DEFINED;
+	if (special(n))
+		vp->flag |= SPECIAL;
+	return (vp);
+}
+
+/* get variable string value */
+char *
+str_val(struct tbl *vp)
+{
+	char *s;
+
+	if ((vp->flag&SPECIAL))
+		getspec(vp);
+	if (!(vp->flag&ISSET))
+		/* special to dollar() */
+		s = null;
+	else if (!(vp->flag&INTEGER))
+		/* string source */
+		s = vp->val.s + vp->type;
+	else {
+		/* integer source */
+		mksh_uari_t n;
+		unsigned int base;
+		/**
+		 * worst case number length is when base == 2:
+		 *	1 (minus) + 2 (base, up to 36) + 1 ('#') +
+		 *	number of bits in the mksh_uari_t + 1 (NUL)
+		 */
+		char strbuf[1 + 2 + 1 + 8 * sizeof(mksh_uari_t) + 1];
+		const char *digits = (vp->flag & UCASEV_AL) ?
+		    digits_uc : digits_lc;
+
+		s = strbuf + sizeof(strbuf);
+		if (vp->flag & INT_U)
+			n = vp->val.u;
+		else
+			n = (vp->val.i < 0) ? -vp->val.u : vp->val.u;
+		base = (vp->type == 0) ? 10U : (unsigned int)vp->type;
+
+		if (base == 1 && n == 0)
+			base = 2;
+		if (base == 1) {
+			size_t sz = 1;
+
+			*(s = strbuf) = '1';
+			s[1] = '#';
+			if (!UTFMODE || ((n & 0xFF80) == 0xEF80))
+				/* OPTU-16 -> raw octet */
+				s[2] = n & 0xFF;
+			else
+				sz = utf_wctomb(s + 2, n);
+			s[2 + sz] = '\0';
+		} else {
+			*--s = '\0';
+			do {
+				*--s = digits[n % base];
+				n /= base;
+			} while (n != 0);
+			if (base != 10) {
+				*--s = '#';
+				*--s = digits[base % 10];
+				if (base >= 10)
+					*--s = digits[base / 10];
+			}
+			if (!(vp->flag & INT_U) && vp->val.i < 0)
+				*--s = '-';
+		}
+		if (vp->flag & (RJUST|LJUST))
+			/* case already dealt with */
+			s = formatstr(vp, s);
+		else
+			strdupx(s, s, ATEMP);
+	}
+	return (s);
+}
+
+/* set variable to string value */
+int
+setstr(struct tbl *vq, const char *s, int error_ok)
+{
+	char *salloc = NULL;
+	bool no_ro_check = tobool(error_ok & 0x4);
+
+	error_ok &= ~0x4;
+	if ((vq->flag & RDONLY) && !no_ro_check) {
+		warningf(true, "read-only: %s", vq->name);
+		if (!error_ok)
+			errorfxz(2);
+		return (0);
+	}
+	if (!(vq->flag&INTEGER)) {
+		/* string dest */
+		if ((vq->flag&ALLOC)) {
+#ifndef MKSH_SMALL
+			/* debugging */
+			if (s >= vq->val.s &&
+			    s <= vq->val.s + strlen(vq->val.s)) {
+				internal_errorf(
+				    "setstr: %s=%s: assigning to self",
+				    vq->name, s);
+			}
+#endif
+			afree(vq->val.s, vq->areap);
+		}
+		vq->flag &= ~(ISSET|ALLOC);
+		vq->type = 0;
+		if (s && (vq->flag & (UCASEV_AL|LCASEV|LJUST|RJUST)))
+			s = salloc = formatstr(vq, s);
+		if ((vq->flag&EXPORT))
+			exportprep(vq, s);
+		else {
+			strdupx(vq->val.s, s, vq->areap);
+			vq->flag |= ALLOC;
+		}
+	} else {
+		/* integer dest */
+		if (!v_evaluate(vq, s, error_ok, true))
+			return (0);
+	}
+	vq->flag |= ISSET;
+	if ((vq->flag&SPECIAL))
+		setspec(vq);
+	afree(salloc, ATEMP);
+	return (1);
+}
+
+/* set variable to integer */
+void
+setint(struct tbl *vq, mksh_ari_t n)
+{
+	if (!(vq->flag&INTEGER)) {
+		struct tbl *vp = &vtemp;
+		vp->flag = (ISSET|INTEGER);
+		vp->type = 0;
+		vp->areap = ATEMP;
+		vp->val.i = n;
+		/* setstr can't fail here */
+		setstr(vq, str_val(vp), KSH_RETURN_ERROR);
+	} else
+		vq->val.i = n;
+	vq->flag |= ISSET;
+	if ((vq->flag&SPECIAL))
+		setspec(vq);
+}
+
+static int
+getint(struct tbl *vp, mksh_ari_u *nump, bool arith)
+{
+	mksh_uari_t c, num, base;
+	const char *s;
+	bool have_base = false, neg = false;
+
+	if (vp->flag&SPECIAL)
+		getspec(vp);
+	/* XXX is it possible for ISSET to be set and val.s to be NULL? */
+	if (!(vp->flag&ISSET) || (!(vp->flag&INTEGER) && vp->val.s == NULL))
+		return (-1);
+	if (vp->flag&INTEGER) {
+		nump->i = vp->val.i;
+		return (vp->type);
+	}
+	s = vp->val.s + vp->type;
+	base = 10;
+	num = 0;
+	if (arith && s[0] == '0' && (s[1] | 0x20) == 'x') {
+		s += 2;
+		base = 16;
+		have_base = true;
+	}
+	if (Flag(FPOSIX) && arith && s[0] == '0' && ksh_isdigit(s[1]) &&
+	    !(vp->flag & ZEROFIL)) {
+		/* interpret as octal (deprecated) */
+		base = 8;
+		have_base = true;
+	}
+	while ((c = (unsigned char)*s++)) {
+		if (c == '-') {
+			neg = true;
+			continue;
+		} else if (c == '#') {
+			if (have_base || num < 1 || num > 36)
+				return (-1);
+			if ((base = num) == 1) {
+				unsigned int wc;
+
+				if (!UTFMODE)
+					wc = *(const unsigned char *)s;
+				else if (utf_mbtowc(&wc, s) == (size_t)-1)
+					/* OPTU-8 -> OPTU-16 */
+					/*
+					 * (with a twist: 1#\uEF80 converts
+					 * the same as 1#\x80 does, thus is
+					 * not round-tripping correctly XXX)
+					 */
+					wc = 0xEF00 + *(const unsigned char *)s;
+				nump->u = (mksh_uari_t)wc;
+				return (1);
+			}
+			num = 0;
+			have_base = true;
+			continue;
+		} else if (ksh_isdigit(c))
+			c -= '0';
+		else if (ksh_islower(c))
+			c -= 'a' - 10;
+		else if (ksh_isupper(c))
+			c -= 'A' - 10;
+		else
+			return (-1);
+		if (c >= base)
+			return (-1);
+		num = num * base + c;
+	}
+	if (neg)
+		num = -num;
+	nump->u = num;
+	return (base);
+}
+
+/*
+ * convert variable vq to integer variable, setting its value from vp
+ * (vq and vp may be the same)
+ */
+struct tbl *
+setint_v(struct tbl *vq, struct tbl *vp, bool arith)
+{
+	int base;
+	mksh_ari_u num;
+
+	if ((base = getint(vp, &num, arith)) == -1)
+		return (NULL);
+	setint_n(vq, num.i, 0);
+	if (vq->type == 0)
+		/* default base */
+		vq->type = base;
+	return (vq);
+}
+
+/* convert variable vq to integer variable, setting its value to num */
+void
+setint_n(struct tbl *vq, mksh_ari_t num, int newbase)
+{
+	if (!(vq->flag & INTEGER) && (vq->flag & ALLOC)) {
+		vq->flag &= ~ALLOC;
+		vq->type = 0;
+		afree(vq->val.s, vq->areap);
+	}
+	vq->val.i = num;
+	if (newbase != 0)
+		vq->type = newbase;
+	vq->flag |= ISSET|INTEGER;
+	if (vq->flag&SPECIAL)
+		setspec(vq);
+}
+
+static char *
+formatstr(struct tbl *vp, const char *s)
+{
+	int olen, nlen;
+	char *p, *q;
+	size_t psiz;
+
+	olen = (int)utf_mbswidth(s);
+
+	if (vp->flag & (RJUST|LJUST)) {
+		if (!vp->u2.field)
+			/* default field width */
+			vp->u2.field = olen;
+		nlen = vp->u2.field;
+	} else
+		nlen = olen;
+
+	p = alloc((psiz = nlen * /* MB_LEN_MAX */ 3 + 1), ATEMP);
+	if (vp->flag & (RJUST|LJUST)) {
+		int slen = olen, i = 0;
+
+		if (vp->flag & RJUST) {
+			const char *qq = s;
+			int n = 0;
+
+			while (i < slen)
+				i += utf_widthadj(qq, &qq);
+			/* strip trailing spaces (AT&T uses qq[-1] == ' ') */
+			while (qq > s && ksh_isspace(qq[-1])) {
+				--qq;
+				--slen;
+			}
+			if (vp->flag & ZEROFIL && vp->flag & INTEGER) {
+				if (!s[0] || !s[1])
+					goto uhm_no;
+				if (s[1] == '#')
+					n = 2;
+				else if (s[2] == '#')
+					n = 3;
+ uhm_no:
+				if (vp->u2.field <= n)
+					n = 0;
+			}
+			if (n) {
+				memcpy(p, s, n);
+				s += n;
+			}
+			while (slen > vp->u2.field)
+				slen -= utf_widthadj(s, &s);
+			if (vp->u2.field - slen)
+				memset(p + n, (vp->flag & ZEROFIL) ? '0' : ' ',
+				    vp->u2.field - slen);
+			slen -= n;
+			shf_snprintf(p + vp->u2.field - slen,
+			    psiz - (vp->u2.field - slen),
+			    "%.*s", slen, s);
+		} else {
+			/* strip leading spaces/zeros */
+			while (ksh_isspace(*s))
+				s++;
+			if (vp->flag & ZEROFIL)
+				while (*s == '0')
+					s++;
+			shf_snprintf(p, nlen + 1, "%-*.*s",
+				vp->u2.field, vp->u2.field, s);
+		}
+	} else
+		memcpy(p, s, strlen(s) + 1);
+
+	if (vp->flag & UCASEV_AL) {
+		for (q = p; *q; q++)
+			*q = ksh_toupper(*q);
+	} else if (vp->flag & LCASEV) {
+		for (q = p; *q; q++)
+			*q = ksh_tolower(*q);
+	}
+
+	return (p);
+}
+
+/*
+ * make vp->val.s be "name=value" for quick exporting.
+ */
+static void
+exportprep(struct tbl *vp, const char *val)
+{
+	char *xp;
+	char *op = (vp->flag&ALLOC) ? vp->val.s : NULL;
+	size_t namelen, vallen;
+
+	mkssert(val != NULL);
+
+	namelen = strlen(vp->name);
+	vallen = strlen(val) + 1;
+
+	vp->flag |= ALLOC;
+	/* since name+val are both in memory this can go unchecked */
+	xp = alloc(namelen + 1 + vallen, vp->areap);
+	memcpy(vp->val.s = xp, vp->name, namelen);
+	xp += namelen;
+	*xp++ = '=';
+	/* offset to value */
+	vp->type = xp - vp->val.s;
+	memcpy(xp, val, vallen);
+	if (op != NULL)
+		afree(op, vp->areap);
+}
+
+/*
+ * lookup variable (according to (set&LOCAL)), set its attributes
+ * (INTEGER, RDONLY, EXPORT, TRACE, LJUST, RJUST, ZEROFIL, LCASEV,
+ * UCASEV_AL), and optionally set its value if an assignment.
+ */
+struct tbl *
+typeset(const char *var, uint32_t set, uint32_t clr, int field, int base)
+{
+	struct tbl *vp;
+	struct tbl *vpbase, *t;
+	char *tvar;
+	const char *val;
+	size_t len;
+	bool vappend = false;
+	enum namerefflag new_refflag = SRF_NOP;
+
+	if ((set & (ARRAY | ASSOC)) == ASSOC) {
+		new_refflag = SRF_ENABLE;
+		set &= ~(ARRAY | ASSOC);
+	}
+	if ((clr & (ARRAY | ASSOC)) == ASSOC) {
+		new_refflag = SRF_DISABLE;
+		clr &= ~(ARRAY | ASSOC);
+	}
+
+	/* check for valid variable name, search for value */
+	val = skip_varname(var, false);
+	if (val == var) {
+		/* no variable name given */
+		return (NULL);
+	}
+	if (*val == '[') {
+		if (new_refflag != SRF_NOP)
+			errorf("%s: %s", var,
+			    "reference variable can't be an array");
+		len = array_ref_len(val);
+		if (len == 0)
+			return (NULL);
+		/*
+		 * IMPORT is only used when the shell starts up and is
+		 * setting up its environment. Allow only simple array
+		 * references at this time since parameter/command
+		 * substitution is performed on the [expression] which
+		 * would be a major security hole.
+		 */
+		if (set & IMPORT) {
+			size_t i;
+
+			for (i = 1; i < len - 1; i++)
+				if (!ksh_isdigit(val[i]))
+					return (NULL);
+		}
+		val += len;
+	}
+	if (val[0] == '=' || (val[0] == '+' && val[1] == '=')) {
+		strndupx(tvar, var, val - var, ATEMP);
+		if (*val++ == '+') {
+			++val;
+			vappend = true;
+		}
+	} else if ((val[0] != '\0') || (set & IMPORT)) {
+		/*
+		 * must have a = when setting a variable by importing
+		 * the original environment, otherwise be empty; we
+		 * also end up here when a variable name was invalid
+		 */
+		return (NULL);
+	} else {
+		/* just varname with no value part nor equals sign */
+		strdupx(tvar, var, ATEMP);
+		val = NULL;
+		/* handle foo[*] => foo (whole array) mapping for R39b */
+		len = strlen(tvar);
+		if (len > 3 && tvar[len - 3] == '[' && tvar[len - 2] == '*' &&
+		    tvar[len - 1] == ']')
+			tvar[len - 3] = '\0';
+	}
+
+	if (new_refflag == SRF_ENABLE) {
+		const char *qval, *ccp;
+
+		/* bail out on 'nameref foo+=bar' */
+		if (vappend)
+			errorf("appending not allowed for nameref");
+		/* find value if variable already exists */
+		if ((qval = val) == NULL) {
+			varsearch(e->loc, &vp, tvar, hash(tvar));
+			if (vp != NULL)
+				qval = str_val(vp);
+		}
+		/* check target value for being a valid variable name */
+		ccp = skip_varname(qval, false);
+		if (ccp == qval)
+			errorf("%s: %s", var, "empty nameref target");
+		len = (*ccp == '[') ? array_ref_len(ccp) : 0;
+		if (ccp[len]) {
+			/*
+			 * works for cases "no array", "valid array with
+			 * junk after it" and "invalid array"; in the
+			 * latter case, len is also 0 and points to '['
+			 */
+			errorf("%s: %s", qval,
+			    "nameref target not a valid parameter name");
+		}
+		/* prevent nameref loops */
+		while (qval) {
+			if (!strcmp(qval, tvar))
+				errorf("%s: %s", qval,
+				    "expression recurses on parameter");
+			varsearch(e->loc, &vp, qval, hash(qval));
+			qval = NULL;
+			if (vp && ((vp->flag & (ARRAY | ASSOC)) == ASSOC))
+				qval = str_val(vp);
+		}
+	}
+
+	/* prevent typeset from creating a local PATH/ENV/SHELL */
+	if (Flag(FRESTRICTED) && (strcmp(tvar, "PATH") == 0 ||
+	    strcmp(tvar, "ENV") == 0 || strcmp(tvar, "SHELL") == 0))
+		errorf("%s: %s", tvar, "restricted");
+
+	innermost_refflag = new_refflag;
+	vp = (set & LOCAL) ? local(tvar, tobool(set & LOCAL_COPY)) :
+	    global(tvar);
+	if (new_refflag == SRF_DISABLE && (vp->flag & (ARRAY|ASSOC)) == ASSOC)
+		vp->flag &= ~ASSOC;
+	else if (new_refflag == SRF_ENABLE) {
+		if (vp->flag & ARRAY) {
+			struct tbl *a, *tmp;
+
+			/* free up entire array */
+			for (a = vp->u.array; a; ) {
+				tmp = a;
+				a = a->u.array;
+				if (tmp->flag & ALLOC)
+					afree(tmp->val.s, tmp->areap);
+				afree(tmp, tmp->areap);
+			}
+			vp->u.array = NULL;
+			vp->flag &= ~ARRAY;
+		}
+		vp->flag |= ASSOC;
+	}
+
+	set &= ~(LOCAL|LOCAL_COPY);
+
+	vpbase = (vp->flag & ARRAY) ? global(arrayname(tvar)) : vp;
+
+	/*
+	 * only allow export flag to be set; AT&T ksh allows any
+	 * attribute to be changed which means it can be truncated or
+	 * modified (-L/-R/-Z/-i)
+	 */
+	if ((vpbase->flag & RDONLY) &&
+	    (val || clr || (set & ~EXPORT)))
+		/* XXX check calls - is error here ok by POSIX? */
+		errorfx(2, "read-only: %s", tvar);
+	afree(tvar, ATEMP);
+
+	/* most calls are with set/clr == 0 */
+	if (set | clr) {
+		bool ok = true;
+
+		/*
+		 * XXX if x[0] isn't set, there will be problems: need
+		 * to have one copy of attributes for arrays...
+		 */
+		for (t = vpbase; t; t = t->u.array) {
+			bool fake_assign;
+			char *s = NULL;
+			char *free_me = NULL;
+
+			fake_assign = (t->flag & ISSET) && (!val || t != vp) &&
+			    ((set & (UCASEV_AL|LCASEV|LJUST|RJUST|ZEROFIL)) ||
+			    ((t->flag & INTEGER) && (clr & INTEGER)) ||
+			    (!(t->flag & INTEGER) && (set & INTEGER)));
+			if (fake_assign) {
+				if (t->flag & INTEGER) {
+					s = str_val(t);
+					free_me = NULL;
+				} else {
+					s = t->val.s + t->type;
+					free_me = (t->flag & ALLOC) ? t->val.s :
+					    NULL;
+				}
+				t->flag &= ~ALLOC;
+			}
+			if (!(t->flag & INTEGER) && (set & INTEGER)) {
+				t->type = 0;
+				t->flag &= ~ALLOC;
+			}
+			t->flag = (t->flag | set) & ~clr;
+			/*
+			 * Don't change base if assignment is to be
+			 * done, in case assignment fails.
+			 */
+			if ((set & INTEGER) && base > 0 && (!val || t != vp))
+				t->type = base;
+			if (set & (LJUST|RJUST|ZEROFIL))
+				t->u2.field = field;
+			if (fake_assign) {
+				if (!setstr(t, s, KSH_RETURN_ERROR)) {
+					/*
+					 * Somewhat arbitrary action
+					 * here: zap contents of
+					 * variable, but keep the flag
+					 * settings.
+					 */
+					ok = false;
+					if (t->flag & INTEGER)
+						t->flag &= ~ISSET;
+					else {
+						if (t->flag & ALLOC)
+							afree(t->val.s, t->areap);
+						t->flag &= ~(ISSET|ALLOC);
+						t->type = 0;
+					}
+				}
+				if (free_me)
+					afree(free_me, t->areap);
+			}
+		}
+		if (!ok)
+			errorfz();
+	}
+
+	if (val != NULL) {
+		char *tval;
+
+		if (vappend) {
+			tval = shf_smprintf("%s%s", str_val(vp), val);
+			val = tval;
+		} else
+			tval = NULL;
+
+		if (vp->flag&INTEGER) {
+			/* do not zero base before assignment */
+			setstr(vp, val, KSH_UNWIND_ERROR | 0x4);
+			/* done after assignment to override default */
+			if (base > 0)
+				vp->type = base;
+		} else
+			/* setstr can't fail (readonly check already done) */
+			setstr(vp, val, KSH_RETURN_ERROR | 0x4);
+
+		if (tval != NULL)
+			afree(tval, ATEMP);
+	}
+
+	/* only x[0] is ever exported, so use vpbase */
+	if ((vpbase->flag&EXPORT) && !(vpbase->flag&INTEGER) &&
+	    vpbase->type == 0)
+		exportprep(vpbase, (vpbase->flag&ISSET) ? vpbase->val.s : null);
+
+	return (vp);
+}
+
+/**
+ * Unset a variable. The flags can be:
+ * |1	= tear down entire array
+ * |2	= keep attributes, only unset content
+ */
+void
+unset(struct tbl *vp, int flags)
+{
+	if (vp->flag & ALLOC)
+		afree(vp->val.s, vp->areap);
+	if ((vp->flag & ARRAY) && (flags & 1)) {
+		struct tbl *a, *tmp;
+
+		/* free up entire array */
+		for (a = vp->u.array; a; ) {
+			tmp = a;
+			a = a->u.array;
+			if (tmp->flag & ALLOC)
+				afree(tmp->val.s, tmp->areap);
+			afree(tmp, tmp->areap);
+		}
+		vp->u.array = NULL;
+	}
+	if (flags & 2) {
+		vp->flag &= ~(ALLOC|ISSET);
+		return;
+	}
+	/* if foo[0] is being unset, the remainder of the array is kept... */
+	vp->flag &= SPECIAL | ((flags & 1) ? 0 : ARRAY|DEFINED);
+	if (vp->flag & SPECIAL)
+		/* responsible for 'unspecial'ing var */
+		unsetspec(vp);
+}
+
+/*
+ * Return a pointer to the first char past a legal variable name
+ * (returns the argument if there is no legal name, returns a pointer to
+ * the terminating NUL if whole string is legal).
+ */
+const char *
+skip_varname(const char *s, bool aok)
+{
+	size_t alen;
+
+	if (s && ksh_isalphx(*s)) {
+		while (*++s && ksh_isalnux(*s))
+			;
+		if (aok && *s == '[' && (alen = array_ref_len(s)))
+			s += alen;
+	}
+	return (s);
+}
+
+/* Return a pointer to the first character past any legal variable name */
+const char *
+skip_wdvarname(const char *s,
+    /* skip array de-reference? */
+    bool aok)
+{
+	if (s[0] == CHAR && ksh_isalphx(s[1])) {
+		do {
+			s += 2;
+		} while (s[0] == CHAR && ksh_isalnux(s[1]));
+		if (aok && s[0] == CHAR && s[1] == '[') {
+			/* skip possible array de-reference */
+			const char *p = s;
+			char c;
+			int depth = 0;
+
+			while (/* CONSTCOND */ 1) {
+				if (p[0] != CHAR)
+					break;
+				c = p[1];
+				p += 2;
+				if (c == '[')
+					depth++;
+				else if (c == ']' && --depth == 0) {
+					s = p;
+					break;
+				}
+			}
+		}
+	}
+	return (s);
+}
+
+/* Check if coded string s is a variable name */
+int
+is_wdvarname(const char *s, bool aok)
+{
+	const char *p = skip_wdvarname(s, aok);
+
+	return (p != s && p[0] == EOS);
+}
+
+/* Check if coded string s is a variable assignment */
+int
+is_wdvarassign(const char *s)
+{
+	const char *p = skip_wdvarname(s, true);
+
+	return (p != s && p[0] == CHAR &&
+	    (p[1] == '=' || (p[1] == '+' && p[2] == CHAR && p[3] == '=')));
+}
+
+/*
+ * Make the exported environment from the exported names in the dictionary.
+ */
+char **
+makenv(void)
+{
+	ssize_t i;
+	struct block *l;
+	XPtrV denv;
+	struct tbl *vp, **vpp;
+
+	XPinit(denv, 64);
+	for (l = e->loc; l != NULL; l = l->next) {
+		vpp = l->vars.tbls;
+		i = 1 << (l->vars.tshift);
+		while (--i >= 0)
+			if ((vp = *vpp++) != NULL &&
+			    (vp->flag&(ISSET|EXPORT)) == (ISSET|EXPORT)) {
+				struct block *l2;
+				struct tbl *vp2;
+				uint32_t h = hash(vp->name);
+
+				/* unexport any redefined instances */
+				for (l2 = l->next; l2 != NULL; l2 = l2->next) {
+					vp2 = ktsearch(&l2->vars, vp->name, h);
+					if (vp2 != NULL)
+						vp2->flag &= ~EXPORT;
+				}
+				if ((vp->flag&INTEGER)) {
+					/* integer to string */
+					char *val;
+					val = str_val(vp);
+					vp->flag &= ~(INTEGER|RDONLY|SPECIAL);
+					/* setstr can't fail here */
+					setstr(vp, val, KSH_RETURN_ERROR);
+				}
+				XPput(denv, vp->val.s);
+			}
+	}
+	XPput(denv, NULL);
+	return ((char **)XPclose(denv));
+}
+
+/*
+ * handle special variables with side effects - PATH, SECONDS.
+ */
+
+/* Test if name is a special parameter */
+static int
+special(const char *name)
+{
+	struct tbl *tp;
+
+	tp = ktsearch(&specials, name, hash(name));
+	return (tp && (tp->flag & ISSET) ? tp->type : V_NONE);
+}
+
+/* Make a variable non-special */
+static void
+unspecial(const char *name)
+{
+	struct tbl *tp;
+
+	tp = ktsearch(&specials, name, hash(name));
+	if (tp)
+		ktdelete(tp);
+}
+
+static time_t seconds;		/* time SECONDS last set */
+static int user_lineno;		/* what user set $LINENO to */
+
+static void
+getspec(struct tbl *vp)
+{
+	mksh_ari_u num;
+	int st;
+	struct timeval tv;
+
+	switch ((st = special(vp->name))) {
+	case V_COLUMNS:
+	case V_LINES:
+		/*
+		 * Do NOT export COLUMNS/LINES. Many applications
+		 * check COLUMNS/LINES before checking ws.ws_col/row,
+		 * so if the app is started with C/L in the environ
+		 * and the window is then resized, the app won't
+		 * see the change cause the environ doesn't change.
+		 */
+		if (got_winch)
+			change_winsz();
+		break;
+	}
+	switch (st) {
+	case V_BASHPID:
+		num.u = (mksh_uari_t)procpid;
+		break;
+	case V_COLUMNS:
+		num.i = x_cols;
+		break;
+	case V_HISTSIZE:
+		num.i = histsize;
+		break;
+	case V_LINENO:
+		num.i = current_lineno + user_lineno;
+		break;
+	case V_LINES:
+		num.i = x_lins;
+		break;
+	case V_EPOCHREALTIME: {
+		/* 10(%u) + 1(.) + 6 + NUL */
+		char buf[18];
+
+		vp->flag &= ~SPECIAL;
+		mksh_TIME(tv);
+		shf_snprintf(buf, sizeof(buf), "%u.%06u",
+		    (unsigned)tv.tv_sec, (unsigned)tv.tv_usec);
+		setstr(vp, buf, KSH_RETURN_ERROR | 0x4);
+		vp->flag |= SPECIAL;
+		return;
+	}
+	case V_OPTIND:
+		num.i = user_opt.uoptind;
+		break;
+	case V_RANDOM:
+		num.i = rndget();
+		break;
+	case V_SECONDS:
+		/*
+		 * On start up the value of SECONDS is used before
+		 * it has been set - don't do anything in this case
+		 * (see initcoms[] in main.c).
+		 */
+		if (vp->flag & ISSET) {
+			mksh_TIME(tv);
+			num.i = tv.tv_sec - seconds;
+		} else
+			return;
+		break;
+	default:
+		/* do nothing, do not touch vp at all */
+		return;
+	}
+	vp->flag &= ~SPECIAL;
+	setint_n(vp, num.i, 0);
+	vp->flag |= SPECIAL;
+}
+
+static void
+setspec(struct tbl *vp)
+{
+	mksh_ari_u num;
+	char *s;
+	int st;
+
+	switch ((st = special(vp->name))) {
+#if HAVE_PERSISTENT_HISTORY
+	case V_HISTFILE:
+		sethistfile(str_val(vp));
+		return;
+#endif
+	case V_IFS:
+		setctypes(s = str_val(vp), C_IFS);
+		ifs0 = *s;
+		return;
+	case V_PATH:
+		if (path)
+			afree(path, APERM);
+		s = str_val(vp);
+		strdupx(path, s, APERM);
+		/* clear tracked aliases */
+		flushcom(true);
+		return;
+	case V_TMPDIR:
+		if (tmpdir) {
+			afree(tmpdir, APERM);
+			tmpdir = NULL;
+		}
+		/*
+		 * Use tmpdir iff it is an absolute path, is writable
+		 * and searchable and is a directory...
+		 */
+		{
+			struct stat statb;
+
+			s = str_val(vp);
+			/* LINTED use of access */
+			if (s[0] == '/' && access(s, W_OK|X_OK) == 0 &&
+			    stat(s, &statb) == 0 && S_ISDIR(statb.st_mode))
+				strdupx(tmpdir, s, APERM);
+		}
+		return;
+	/* common sub-cases */
+	case V_COLUMNS:
+	case V_LINES:
+		if (vp->flag & IMPORT) {
+			/* do not touch */
+			unspecial(vp->name);
+			vp->flag &= ~SPECIAL;
+			return;
+		}
+		/* FALLTHROUGH */
+	case V_HISTSIZE:
+	case V_LINENO:
+	case V_OPTIND:
+	case V_RANDOM:
+	case V_SECONDS:
+	case V_TMOUT:
+		vp->flag &= ~SPECIAL;
+		if (getint(vp, &num, false) == -1) {
+			s = str_val(vp);
+			if (st != V_RANDOM)
+				errorf("%s: %s: %s", vp->name, "bad number", s);
+			num.u = hash(s);
+		}
+		vp->flag |= SPECIAL;
+		break;
+	default:
+		/* do nothing, do not touch vp at all */
+		return;
+	}
+
+	/* process the singular parts of the common cases */
+
+	switch (st) {
+	case V_COLUMNS:
+		if (num.i >= MIN_COLS)
+			x_cols = num.i;
+		break;
+	case V_HISTSIZE:
+		sethistsize(num.i);
+		break;
+	case V_LINENO:
+		/* The -1 is because line numbering starts at 1. */
+		user_lineno = num.u - current_lineno - 1;
+		break;
+	case V_LINES:
+		if (num.i >= MIN_LINS)
+			x_lins = num.i;
+		break;
+	case V_OPTIND:
+		getopts_reset((int)num.i);
+		break;
+	case V_RANDOM:
+		/*
+		 * mksh R39d+ no longer has the traditional repeatability
+		 * of $RANDOM sequences, but always retains state
+		 */
+		rndset((unsigned long)num.u);
+		break;
+	case V_SECONDS:
+		{
+			struct timeval tv;
+
+			mksh_TIME(tv);
+			seconds = tv.tv_sec - num.i;
+		}
+		break;
+	case V_TMOUT:
+		ksh_tmout = num.i >= 0 ? num.i : 0;
+		break;
+	}
+}
+
+static void
+unsetspec(struct tbl *vp)
+{
+	/*
+	 * AT&T ksh man page says OPTIND, OPTARG and _ lose special
+	 * meaning, but OPTARG does not (still set by getopts) and _ is
+	 * also still set in various places. Don't know what AT&T does
+	 * for HISTSIZE, HISTFILE. Unsetting these in AT&T ksh does not
+	 * loose the 'specialness': IFS, COLUMNS, PATH, TMPDIR
+	 */
+
+	switch (special(vp->name)) {
+	case V_IFS:
+		setctypes(TC_IFSWS, C_IFS);
+		ifs0 = ' ';
+		break;
+	case V_PATH:
+		if (path)
+			afree(path, APERM);
+		strdupx(path, def_path, APERM);
+		/* clear tracked aliases */
+		flushcom(true);
+		break;
+	case V_TMPDIR:
+		/* should not become unspecial */
+		if (tmpdir) {
+			afree(tmpdir, APERM);
+			tmpdir = NULL;
+		}
+		break;
+	case V_LINENO:
+	case V_RANDOM:
+	case V_SECONDS:
+	case V_TMOUT:
+		/* AT&T ksh leaves previous value in place */
+		unspecial(vp->name);
+		break;
+	}
+}
+
+/*
+ * Search for (and possibly create) a table entry starting with
+ * vp, indexed by val.
+ */
+struct tbl *
+arraysearch(struct tbl *vp, uint32_t val)
+{
+	struct tbl *prev, *curr, *news;
+	size_t len;
+
+	vp->flag = (vp->flag | (ARRAY | DEFINED)) & ~ASSOC;
+	/* the table entry is always [0] */
+	if (val == 0)
+		return (vp);
+	prev = vp;
+	curr = vp->u.array;
+	while (curr && curr->ua.index < val) {
+		prev = curr;
+		curr = curr->u.array;
+	}
+	if (curr && curr->ua.index == val) {
+		if (curr->flag&ISSET)
+			return (curr);
+		news = curr;
+	} else
+		news = NULL;
+	if (!news) {
+		len = strlen(vp->name);
+		checkoktoadd(len, 1 + offsetof(struct tbl, name[0]));
+		news = alloc(offsetof(struct tbl, name[0]) + ++len, vp->areap);
+		memcpy(news->name, vp->name, len);
+	}
+	news->flag = (vp->flag & ~(ALLOC|DEFINED|ISSET|SPECIAL)) | AINDEX;
+	news->type = vp->type;
+	news->areap = vp->areap;
+	news->u2.field = vp->u2.field;
+	news->ua.index = val;
+
+	if (curr != news) {
+		/* not reusing old array entry */
+		prev->u.array = news;
+		news->u.array = curr;
+	}
+	return (news);
+}
+
+/*
+ * Return the length of an array reference (eg, [1+2]) - cp is assumed
+ * to point to the open bracket. Returns 0 if there is no matching
+ * closing bracket.
+ *
+ * XXX this should parse the actual arithmetic syntax
+ */
+size_t
+array_ref_len(const char *cp)
+{
+	const char *s = cp;
+	char c;
+	int depth = 0;
+
+	while ((c = *s++) && (c != ']' || --depth))
+		if (c == '[')
+			depth++;
+	if (!c)
+		return (0);
+	return (s - cp);
+}
+
+/*
+ * Make a copy of the base of an array name
+ */
+char *
+arrayname(const char *str)
+{
+	const char *p;
+	char *rv;
+
+	if ((p = cstrchr(str, '[')) == 0)
+		/* Shouldn't happen, but why worry? */
+		strdupx(rv, str, ATEMP);
+	else
+		strndupx(rv, str, p - str, ATEMP);
+
+	return (rv);
+}
+
+/* set (or overwrite, if reset) the array variable var to the values in vals */
+mksh_uari_t
+set_array(const char *var, bool reset, const char **vals)
+{
+	struct tbl *vp, *vq;
+	mksh_uari_t i = 0, j = 0;
+	const char *ccp = var;
+	char *cp = NULL;
+	size_t n;
+
+	/* to get local array, use "local foo; set -A foo" */
+	n = strlen(var);
+	if (n > 0 && var[n - 1] == '+') {
+		/* append mode */
+		reset = false;
+		strndupx(cp, var, n - 1, ATEMP);
+		ccp = cp;
+	}
+	vp = global(ccp);
+
+	/* Note: AT&T ksh allows set -A but not set +A of a read-only var */
+	if ((vp->flag&RDONLY))
+		errorfx(2, "read-only: %s", ccp);
+	/* This code is quite non-optimal */
+	if (reset) {
+		/* trash existing values and attributes */
+		unset(vp, 1);
+		/* allocate-by-access the [0] element to keep in scope */
+		arraysearch(vp, 0);
+	}
+	/*
+	 * TODO: would be nice for assignment to completely succeed or
+	 * completely fail. Only really effects integer arrays:
+	 * evaluation of some of vals[] may fail...
+	 */
+	if (cp != NULL) {
+		/* find out where to set when appending */
+		for (vq = vp; vq; vq = vq->u.array) {
+			if (!(vq->flag & ISSET))
+				continue;
+			if (arrayindex(vq) >= j)
+				j = arrayindex(vq) + 1;
+		}
+		afree(cp, ATEMP);
+	}
+	while ((ccp = vals[i])) {
+#if 0 /* temporarily taken out due to regression */
+		if (*ccp == '[') {
+			int level = 0;
+
+			while (*ccp) {
+				if (*ccp == ']' && --level == 0)
+					break;
+				if (*ccp == '[')
+					++level;
+				++ccp;
+			}
+			if (*ccp == ']' && level == 0 && ccp[1] == '=') {
+				strndupx(cp, vals[i] + 1, ccp - (vals[i] + 1),
+				    ATEMP);
+				evaluate(substitute(cp, 0), (mksh_ari_t *)&j,
+				    KSH_UNWIND_ERROR, true);
+				afree(cp, ATEMP);
+				ccp += 2;
+			} else
+				ccp = vals[i];
+		}
+#endif
+
+		vq = arraysearch(vp, j);
+		/* would be nice to deal with errors here... (see above) */
+		setstr(vq, ccp, KSH_RETURN_ERROR);
+		i++;
+		j++;
+	}
+
+	return (i);
+}
+
+void
+change_winsz(void)
+{
+	struct timeval tv;
+
+	mksh_TIME(tv);
+	BAFHUpdateMem_mem(qh_state, &tv, sizeof(tv));
+
+#ifdef TIOCGWINSZ
+	/* check if window size has changed */
+	if (tty_init_fd() < 2) {
+		struct winsize ws;
+
+		if (ioctl(tty_fd, TIOCGWINSZ, &ws) >= 0) {
+			if (ws.ws_col)
+				x_cols = ws.ws_col;
+			if (ws.ws_row)
+				x_lins = ws.ws_row;
+		}
+	}
+#endif
+
+	/* bounds check for sane values, use defaults otherwise */
+	if (x_cols < MIN_COLS)
+		x_cols = 80;
+	if (x_lins < MIN_LINS)
+		x_lins = 24;
+
+#ifdef SIGWINCH
+	got_winch = 0;
+#endif
+}
+
+uint32_t
+hash(const void *s)
+{
+	register uint32_t h;
+
+	BAFHInit(h);
+	BAFHUpdateStr_reg(h, s);
+	BAFHFinish_reg(h);
+	return (h);
+}
+
+uint32_t
+chvt_rndsetup(const void *bp, size_t sz)
+{
+	register uint32_t h;
+
+	/* use LCG as seed but try to get them to deviate immediately */
+	h = lcg_state;
+	(void)rndget();
+	BAFHFinish_reg(h);
+	/* variation through pid, ppid, and the works */
+	BAFHUpdateMem_reg(h, &rndsetupstate, sizeof(rndsetupstate));
+	/* some variation, some possibly entropy, depending on OE */
+	BAFHUpdateMem_reg(h, bp, sz);
+	/* mix them all up */
+	BAFHFinish_reg(h);
+
+	return (h);
+}
+
+mksh_ari_t
+rndget(void)
+{
+	/*
+	 * this is the same Linear Congruential PRNG as Borland
+	 * C/C++ allegedly uses in its built-in rand() function
+	 */
+	return (((lcg_state = 22695477 * lcg_state + 1) >> 16) & 0x7FFF);
+}
+
+void
+rndset(unsigned long v)
+{
+	register uint32_t h;
+#if defined(arc4random_pushb_fast) || defined(MKSH_A4PB)
+	register uint32_t t;
+#endif
+	struct {
+		struct timeval tv;
+		void *sp;
+		uint32_t qh;
+		pid_t pp;
+		short r;
+	} z;
+
+#ifdef DEBUG
+	/* clear the allocated space, for valgrind */
+	memset(&z, 0, sizeof(z));
+#endif
+
+	h = lcg_state;
+	BAFHFinish_reg(h);
+	BAFHUpdateMem_reg(h, &v, sizeof(v));
+
+	mksh_TIME(z.tv);
+	z.sp = &lcg_state;
+	z.pp = procpid;
+	z.r = (short)rndget();
+
+#if defined(arc4random_pushb_fast) || defined(MKSH_A4PB)
+	t = qh_state;
+	BAFHFinish_reg(t);
+	z.qh = (t & 0xFFFF8000) | rndget();
+	lcg_state = (t << 15) | rndget();
+	/*
+	 * either we have very chap entropy get and push available,
+	 * with malloc() pulling in this code already anyway, or the
+	 * user requested us to use the old functions
+	 */
+	t = h;
+	BAFHUpdateMem_reg(t, &lcg_state, sizeof(lcg_state));
+	BAFHFinish_reg(t);
+	lcg_state = t;
+#if defined(arc4random_pushb_fast)
+	arc4random_pushb_fast(&lcg_state, sizeof(lcg_state));
+	lcg_state = arc4random();
+#else
+	lcg_state = arc4random_pushb(&lcg_state, sizeof(lcg_state));
+#endif
+	BAFHUpdateMem_reg(h, &lcg_state, sizeof(lcg_state));
+#else
+	z.qh = qh_state;
+#endif
+
+	BAFHUpdateMem_reg(h, &z, sizeof(z));
+	BAFHFinish_reg(h);
+	lcg_state = h;
+}
+
+void
+rndpush(const void *s)
+{
+	register uint32_t h = qh_state;
+
+	BAFHUpdateStr_reg(h, s);
+	BAFHUpdateOctet_reg(h, 0);
+	qh_state = h;
+}



More information about the Midnightbsd-cvs mailing list