Please review changes against upstream code using SCM,
see the Vcs-* tags in debian/control for its location.

--- mksh-59c.orig/Build.sh
+++ mksh-59c/Build.sh
@@ -1,9 +1,9 @@
 #!/bin/sh
-srcversion='$MirOS: src/bin/mksh/Build.sh,v 1.763 2020/09/04 21:01:37 tg Exp $'
+srcversion='$MirOS: src/bin/mksh/Build.sh,v 1.767 2021/01/27 16:45:26 tg Exp $'
 #-
 # Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
 #		2011, 2012, 2013, 2014, 2015, 2016, 2017, 2019,
-#		2020
+#		2020, 2021
 #	mirabilos <m@mirbsd.org>
 #
 # Provided that these terms and disclaimer and all copyright notices
@@ -499,6 +499,13 @@ x)
 	exit 1
 	;;
 esac
+srcdisp=`cd "$srcdir" && pwd` || srcdisp=
+test_n "$srcdisp" || srcdisp=$srcdir
+if test x"$srcdisp" = x"$curdir"; then
+	srcdisp=
+else
+	srcdisp=$srcdir/
+fi
 dstversion=`sed -n '/define MKSH_VERSION/s/^.*"\([^"]*\)".*$/\1/p' "$srcdir/sh.h"`
 add_cppflags -DMKSH_BUILDSH
 
@@ -2303,16 +2310,30 @@ ac_test sys_siglist_decl sys_siglist 0 '
 	int main(void) { return (sys_siglist[0][0] + isatty(0)); }
 EOF
 
-ac_test st_mtim '' 'for struct stat.st_mtim.tv_nsec' <<-'EOF'
+ac_testn st_mtimensec '' 'for struct stat.st_mtimensec' <<-'EOF'
 	#define MKSH_INCLUDES_ONLY
 	#include "sh.h"
-	int main(void) { struct stat sb; return (sizeof(sb.st_mtim.tv_nsec)); }
+	int main(void) { struct stat sb; return (sizeof(sb.st_mtimensec)); }
 EOF
-ac_test st_mtimensec '!' st_mtim 0 'for struct stat.st_mtimensec' <<-'EOF'
+ac_testn st_mtimespec '!' st_mtimensec 0 'for struct stat.st_mtimespec.tv_nsec' <<-'EOF'
 	#define MKSH_INCLUDES_ONLY
 	#include "sh.h"
-	int main(void) { struct stat sb; return (sizeof(sb.st_mtimensec)); }
+	int main(void) { struct stat sb; return (sizeof(sb.st_mtimespec.tv_nsec)); }
 EOF
+if test 1 = "$HAVE_ST_MTIMESPEC"; then
+	add_cppflags -Dst_mtimensec=st_mtimespec.tv_nsec
+	HAVE_ST_MTIMENSEC=1
+fi
+ac_testn st_mtim '!' st_mtimensec 0 'for struct stat.st_mtim.tv_nsec' <<-'EOF'
+	#define MKSH_INCLUDES_ONLY
+	#include "sh.h"
+	int main(void) { struct stat sb; return (sizeof(sb.st_mtim.tv_nsec)); }
+EOF
+if test 1 = "$HAVE_ST_MTIM"; then
+	add_cppflags -Dst_mtimensec=st_mtim.tv_nsec
+	HAVE_ST_MTIMENSEC=1
+fi
+ac_cppflags ST_MTIMENSEC
 
 #
 # other checks
@@ -2491,7 +2512,7 @@ addsrcs USE_PRINTF_BUILTIN printf.c
 addsrcs '!' MKSH_UNLIMITED ulimit.c
 
 test 1 = "$HAVE_CAN_VERB" && CFLAGS="$CFLAGS -verbose"
-add_cppflags -DMKSH_BUILD_R=593
+add_cppflags -DMKSH_BUILD_R=599
 
 $e $bi$me: Finished configuration testing, now producing output.$ao
 
@@ -2760,7 +2781,7 @@ $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/"
+	$e "# $i -c -o root -g bin -m 444 ${srcdisp}dot.mkshrc /usr/share/doc/mksh/examples/"
 fi
 $e
 $e Installing the manual:
@@ -2777,12 +2798,12 @@ if test -f mksh.cat1; then
 	    "/usr/share/man/cat1/mksh.0"
 	$e or
 fi
-$e "# $i -c -o root -g bin -m 444 lksh.1 mksh.1 /usr/share/man/man1/"
+$e "# $i -c -o root -g bin -m 444 ${srcdisp}lksh.1 ${srcdisp}mksh.1 /usr/share/man/man1/"
 $e
 $e Run the regression test suite: ./test.sh
-$e Please also read the sample file dot.mkshrc and the fine manual.
+$e Please also read the sample file ${srcdisp}dot.mkshrc and the fine manual.
 test -f FAQ.htm || \
-    $e Run FAQ2HTML.sh and place FAQ.htm into a suitable location as well.
+    $e Run ${srcdisp}FAQ2HTML.sh and place FAQ.htm into a suitable location as well.
 exit 0
 
 : <<'EOD'
--- mksh-59c.orig/check.t
+++ mksh-59c/check.t
@@ -1,9 +1,9 @@
-# $MirOS: src/bin/mksh/check.t,v 1.853 2020/10/31 03:53:03 tg Exp $
+# $MirOS: src/bin/mksh/check.t,v 1.859 2021/01/24 19:41:07 tg Exp $
 # -*- mode: sh -*-
 #-
 # Copyright © 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
 #	      2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018,
-#	      2019, 2020
+#	      2019, 2020, 2021
 #	mirabilos <m@mirbsd.org>
 #
 # Provided that these terms and disclaimer and all copyright notices
@@ -31,7 +31,7 @@
 # (2013/12/02 20:39:44) http://cvsweb.openbsd.org/cgi-bin/cvsweb/src/regress/bin/ksh/?sortby=date
 
 expected-stdout:
-	KSH R59 2020/10/31
+	KSH R59 2021/03/13
 description:
 	Check base version of full shell
 stdin:
@@ -150,9 +150,8 @@ 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
+	./echo -c 'echo  foo'
 expected-stdout:
 	-c echo  foo
 ---
@@ -4786,7 +4785,7 @@ description:
 	'emulate sh' zsh has extra fields in
 	- a5ins (IFS_NWS unquoted $*)
 	- b5ins, matching mksh’s
-	!!WARNING!! more to come: http://austingroupbugs.net/view.php?id=888
+	!!WARNING!! more to come: https://www.austingroupbugs.net/view.php?id=888
 stdin:
 	"$__progname" -c 'pfb() { for s_arg in "$@"; do print -r -- "[$s_arg]"; done; }; pfn() { for s_arg in "$@"; do print -r -- "<$s_arg>"; done; };
 		IFS=; set -- "" 2 ""; pfb $*; x=$*; pfn "$x"'
@@ -4936,7 +4935,7 @@ expected-stdout:
 ---
 name: IFS-subst-8
 description:
-	http://austingroupbugs.net/view.php?id=221
+	https://www.austingroupbugs.net/view.php?id=221
 stdin:
 	n() { echo "$#"; }; n "${foo-$@}"
 expected-stdout:
@@ -4996,7 +4995,7 @@ expected-stdout:
 ---
 name: IFS-arith-1
 description:
-	http://austingroupbugs.net/view.php?id=832
+	https://www.austingroupbugs.net/view.php?id=832
 stdin:
 	${ZSH_VERSION+false} || emulate sh
 	${BASH_VERSION+set -o posix}
@@ -6184,7 +6183,7 @@ expected-stdout:
 ---
 name: regression-35
 description:
-	Temporay files used for here-docs in functions get trashed after
+	Temporary files used for heredocs in functions get trashed after
 	the function is parsed (before it is executed)
 stdin:
 	f1() {
@@ -6228,8 +6227,8 @@ description:
 	Machines with broken times() (reported by <sjg@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/
+	time -p sleep 1
+expected-stderr-pattern: /^real +(?![0.]*$)[0-9]+(?:\.[0-9]+)?$/m
 ---
 name: regression-38
 description:
@@ -6799,19 +6798,6 @@ stdin:
 		time
 	}
 ---
-name: regression-65
-description:
-	check for a regression with sleep builtin and signal mask
-category: !nojsig
-time-limit: 5
-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
@@ -6971,7 +6957,7 @@ expected-stderr-pattern:
 ---
 name: readonly-1
 description:
-	http://austingroupbugs.net/view.php?id=367 for export
+	https://www.austingroupbugs.net/view.php?id=367 for export
 stdin:
 	"$__progname" -c 'readonly foo; export foo=a; echo $?' || echo aborted, $?
 expected-stdout:
@@ -6990,7 +6976,7 @@ expected-stdout:
 ---
 name: readonly-2b
 description:
-	http://austingroupbugs.net/view.php?id=367 for getopts
+	https://www.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:
@@ -7000,7 +6986,7 @@ expected-stderr-pattern:
 ---
 name: readonly-3
 description:
-	http://austingroupbugs.net/view.php?id=367 for read
+	https://www.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, $?
@@ -7130,7 +7116,7 @@ expected-stdout:
 ---
 name: xxx-exec-environment-1
 description:
-	Check to see if exec sets it's environment correctly
+	Check to see if exec sets its environment correctly
 stdin:
 	print '#!'"$__progname"'\nunset RANDOM\nexport | while IFS= read -r' \
 	    'RANDOM; do eval '\''print -r -- "$RANDOM=$'\''"$RANDOM"'\'\"\'\; \
@@ -7162,47 +7148,73 @@ expected-stdout:
 	y1-
 	x-3- z-
 ---
-name: exec-modern-korn-shell
+name: exec-execs
+description:
+	Ensure that exec never returns
+need-ctty: yes
+env-setup: !ENV=./envf!
+file-setup: file 644 "envf"
+	PS1=X
+arguments: !-i!
+stdin:
+	oldPATH=$PATH
+	PATH=$PWD
+	exec bla
+	PATH=$oldPATH
+	echo fail
+expected-stderr-pattern:
+	/X+.*: bla: (?:inaccessible or )?not found\n/
+expected-exit: 127
+---
+name: exec-modern
 description:
 	Check that exec can execute any command that makes it
 	through syntax and parser
 stdin:
-	print '#!'"$__progname"'\necho tf' >lq
-	chmod +x lq
+	echo '#!'"$__progname" >f
+	echo 'echo >&3 FAIL' >>f
+	echo '#!'"$__progname" >lq
+	echo 'echo tf' >>lq
+	chmod +x lq f
 	PATH=$PWD
-	exec 2>&1
-	foo() { print two; }
-	print =1
-	(exec print one)
-	print =2
-	(exec foo)
-	print =3
-	(exec ls)
-	print =4
-	(exec lq)
+	exec 3>&2 2>&1
+	foo() { echo two; }
+	echo =1
+	(exec echo one; ./f)
+	echo =2
+	(exec foo; ./f)
+	echo =3
+	(exec ls; ./f)
+	echo =4
+	(exec lq; ./f)
 expected-stdout-pattern:
-	/=1\none\n=2\ntwo\n=3\n.*: ls: inaccessible or not found\n=4\ntf\n/
+	/=1\none\n=2\ntwo\n=3\n.*: ls: (?:inaccessible or )?not found\n=4\ntf\n/
 ---
 name: exec-ksh88
 description:
 	Check that exec only executes after a PATH search
+	(POSIX Issue 8 uses utility ipv command for the synopsis)
+	cf. https://www.austingroupbugs.net/view.php?id=1157
 arguments: !-o!posix!
 stdin:
-	print '#!'"$__progname"'\necho tf' >lq
-	chmod +x lq
+	echo '#!'"$__progname" >f
+	echo 'echo >&3 FAIL' >>f
+	echo '#!'"$__progname" >lq
+	echo 'echo tf' >>lq
+	chmod +x lq f
 	PATH=$PWD
-	exec 2>&1
-	foo() { print two; }
-	print =1
-	(exec print one)
-	print =2
-	(exec foo)
-	print =3
-	(exec ls)
-	print =4
-	(exec lq)
+	exec 3>&2 2>&1
+	foo() { echo two; }
+	echo =1
+	(exec echo one; ./f)
+	echo =2
+	(exec foo; ./f)
+	echo =3
+	(exec ls; ./f)
+	echo =4
+	(exec lq; ./f)
 expected-stdout-pattern:
-	/=1\n.*: print: inaccessible or not found\n=2\n.*: foo: inaccessible or not found\n=3\n.*: ls: inaccessible or not found\n=4\ntf\n/
+	/=1\n.*: echo: (?:inaccessible or )?not found\n=2\n.*: foo: (?:inaccessible or )?not found\n=3\n.*: ls: (?:inaccessible or )?not found\n=4\ntf\n/
 ---
 name: xxx-what-do-you-call-this-1
 stdin:
@@ -7339,7 +7351,7 @@ need-ctty: yes
 arguments: !-i!
 stdin:
 	exec echo hi
-	echo still herre
+	echo still here
 expected-stdout:
 	hi
 expected-stderr-pattern: /.*/
@@ -8878,7 +8890,7 @@ expected-stderr-pattern:
 ---
 name: utf8opt-1
 description:
-	Check that the utf8-mode flag is not set at non-interactive startup
+	Check that the utf8-mode flag *is* set at non-interactive startup
 env-setup: !PS1=!PS2=!LC_CTYPE=@utflocale@!
 stdin:
 	if [[ $- = *U* ]]; then
@@ -8887,7 +8899,7 @@ stdin:
 		echo is not set
 	fi
 expected-stdout:
-	is not set
+	is set
 ---
 name: utf8opt-2
 description:
--- mksh-59c.orig/dot.mkshrc
+++ mksh-59c/dot.mkshrc
@@ -1,9 +1,9 @@
 # $Id$
-# $MirOS: src/bin/mksh/dot.mkshrc,v 1.128 2020/04/13 18:39:03 tg Exp $
+# $MirOS: src/bin/mksh/dot.mkshrc,v 1.136 2021/01/24 20:38:30 tg Exp $
 #-
 # Copyright (c) 2002, 2003, 2004, 2006, 2007, 2008, 2009, 2010,
 #		2011, 2012, 2013, 2014, 2015, 2016, 2017, 2019,
-#		2020
+#		2020, 2021
 #	mirabilos <m@mirbsd.org>
 #
 # Provided that these terms and disclaimer and all copyright notices
@@ -38,24 +38,51 @@ function setenv {
 	fi
 }
 
+# internal helper function to cat all arguments if necessary
+function _dot_mkshrc_wrapped_cat {
+	\\builtin typeset fn=$1
+	\\builtin shift
+
+	if [[ $# = 0 || $#,$1 = 1,- ]]; then
+		"$fn"
+	else
+		\cat "$@" | "$fn"
+	fi
+}
+function _dot_mkshrc_cat_for_readN {
+	\\builtin typeset fn=$1
+	\\builtin shift
+
+	if (( $# )); then
+		\\builtin print -nr -- "$*" | "$fn"
+	elif [[ -t 0 ]]; then
+		\cat | "$fn"
+	else
+		"$fn"
+	fi
+}
+
 # pager (not control character safe)
-smores() (
-	\\builtin set +m
-	\\builtin cat "$@" |&
-	\\builtin trap "rv=\$?; \\\\builtin kill $! >/dev/null 2>&1; \\\\builtin exit \$rv" EXIT
-	while IFS= \\builtin read -pr line; do
+function smores {
+	\_dot_mkshrc_wrapped_cat _dot_mkshrc_smores "$@"
+}
+function _dot_mkshrc_smores {
+	\\builtin set +e
+	\\builtin typeset line llen curlin=0 x
+
+	while IFS= \\builtin read -r line; do
 		llen=${%line}
-		(( llen == -1 )) && llen=${#line}
+		(( llen != -1 )) || llen=${#line}
 		(( llen = llen ? (llen + COLUMNS - 1) / COLUMNS : 1 ))
 		if (( (curlin += llen) >= LINES )); then
 			\\builtin print -nr -- $'\e[7m--more--\e[0m'
-			\\builtin read -u1 || \\builtin exit $?
-			[[ $REPLY = [Qq]* ]] && \\builtin exit 0
+			\\builtin read -u1 x || \\builtin return $?
+			[[ $x != [Qq]* ]] || \\builtin return 0
 			curlin=$llen
 		fi
 		\\builtin print -r -- "$line"
 	done
-)
+}
 
 # customise your favourite editor here; the first one found is used
 for EDITOR in "${EDITOR:-}" jupp jstar mcedit ed vi; do
@@ -82,10 +109,10 @@ fi
 
 # prompts
 PS4='[$EPOCHREALTIME] '; PS1='#'; (( USER_ID )) && PS1='$'; PS1=$'\001\r''${|
-	\\builtin typeset e=$?
+	\\builtin typeset e=$? hn=${HOSTNAME:-nil}
 
-	(( e )) && REPLY+="$e|"
-	REPLY+=${USER}@${HOSTNAME%%.*}:
+	(( !!e )) || REPLY+="$e|"
+	REPLY+=${USER:-?}@${hn%%.*}:
 
 	\\builtin typeset d=${PWD:-?}/ p=~; [[ $p = ?(*/) ]] || d=${d/#$p\//\~/}
 	d=${d%/}; \\builtin typeset m=${%d} n p=...; (( m > 0 )) || m=${#d}
@@ -104,12 +131,13 @@ if \\builtin command -v hd >/dev/null; t
 	\:
 elif \\builtin command -v hexdump >/dev/null; then
 	function hd {
-		hexdump -e '"%08.8_ax  " 8/1 "%02X " " - " 8/1 "%02X "' \
+		\hexdump -e '"%08.8_ax  " 8/1 "%02X " " - " 8/1 "%02X "' \
 		    -e '"  |" "%_p"' -e '"|\n"' "$@"
 	}
 else
 	function hd {
-		\\builtin cat "$@" | hd_mksh
+		# cannot use _dot_mkshrc_wrapped_cat as hd_mksh sets stdin raw
+		\cat "$@" | \hd_mksh
 	}
 fi
 
@@ -118,7 +146,7 @@ function hd_mksh {
 	\\builtin typeset -Uui16 -Z11 pos=0
 	\\builtin typeset -Uui16 -Z5 hv=2147483647
 	\\builtin typeset dasc dn line i
-	\\builtin set +U
+	\\builtin set +Ue
 
 	while \\builtin read -arn 512 line; do
 		\\builtin typeset -i1 'line[*]'
@@ -219,6 +247,7 @@ function cd_csh {
 	\cd "$t"
 }
 function dirs {
+	\\builtin set +e
 	\\builtin typeset d dwidth
 	\\builtin typeset -i fl=0 fv=0 fn=0 cpos=0
 
@@ -343,9 +372,17 @@ function pushd {
 
 # base64 encoder and decoder, RFC compliant, NUL safe, not EBCDIC safe
 function Lb64decode {
-	\\builtin set +U
-	\\builtin typeset c s="$*" t
-	[[ -n $s ]] || { s=$(\\builtin cat; \\builtin print x); s=${s%x}; }
+	\\builtin set +Ue
+	\\builtin typeset c s t
+	if (( $# )); then
+		s="$*"
+	elif [[ -t 0 ]]; then
+		s=$(\cat || \\builtin exit $?; \\builtin print x) || \
+		    \\builtin return $?
+		s=${s%x}
+	else
+		\\builtin read -rN-1 s || \\builtin return $?
+	fi
 	\\builtin typeset -i i=0 j=0 n=${#s} p=0 v x
 	\\builtin typeset -i16 o
 
@@ -371,22 +408,20 @@ function Lb64decode {
 		}
 		t+=\\x${o#16#}
 		(( ++j & 4095 )) && \\builtin continue
-		\\builtin print -n $t
+		\\builtin print -n -- "$t"
 		t=
 	done
-	\\builtin print -n $t
+	\\builtin print -n -- "$t"
 }
 function Lb64encode {
-	\\builtin set +U
+	\_dot_mkshrc_cat_for_readN _dot_mkshrc_b64encode "$@"
+}
+function _dot_mkshrc_b64encode {
+	\\builtin set +Ue
 	\\builtin typeset c s t table
 	\\builtin set -A table -- 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 + /
-	if (( $# )); then
-		\\builtin read -raN-1 s <<<"$*"
-		\\builtin unset s[${#s[*]}-1]
-	else
-		\\builtin read -raN-1 s
-	fi
+	\\builtin read -raN-1 s || \\builtin return $?
 	\\builtin typeset -i i=0 n=${#s[*]} v
 
 	while (( i < n )); do
@@ -403,10 +438,11 @@ function Lb64encode {
 			t+===
 		fi
 		if (( ${#t} == 76 || i >= n )); then
-			\\builtin print -r $t
+			\\builtin print -r -- "$t"
 			t=
 		fi
 	done
+	\:
 }
 
 # Better Avalanche for the Jenkins Hash
@@ -415,22 +451,22 @@ function Lbafh_init {
 	Lbafh_v=0
 }
 function Lbafh_add {
-	\\builtin set +U
+	\_dot_mkshrc_cat_for_readN _dot_mkshrc_bafh_add "$@"
+}
+function _dot_mkshrc_bafh_add {
+	\\builtin set +Ue
 	\\builtin typeset s
-	if (( $# )); then
-		\\builtin read -raN-1 s <<<"$*"
-		\\builtin unset s[${#s[*]}-1]
-	else
-		\\builtin read -raN-1 s
-	fi
+	\\builtin read -raN-1 s || \\builtin return $?
 	\\builtin typeset -i i=0 n=${#s[*]}
 
 	while (( i < n )); do
 		((# Lbafh_v = (Lbafh_v + s[i++] + 1) * 1025 ))
 		((# Lbafh_v ^= Lbafh_v >> 6 ))
 	done
+	\:
 }
 function Lbafh_finish {
+	\\builtin set +e
 	\\builtin typeset -Ui t
 
 	((# t = (((Lbafh_v >> 7) & 0x01010101) * 0x1B) ^ \
@@ -443,11 +479,20 @@ function Lbafh_finish {
 # 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 {
+	\_dot_mkshrc_wrapped_cat _dot_mkshrc_stripcom "$@"
+}
+function _dot_mkshrc_stripcom {
+	\\builtin typeset line x
 	\\builtin set -o noglob
-	\\builtin cat "$@" | while \\builtin read _line; do
-		_line=${_line%%#*}
-		[[ -n $_line ]] && \\builtin print -r -- $_line
+	while \\builtin read -r line; do
+		while [[ $line = *\\ && $line != *'#'* ]] && \
+		    \\builtin read -r x; do
+			line=${line%\\}$x
+		done
+		line=${line%%#*}
+		[[ -z $line ]] || \\builtin print -r -- $line
 	done
+	\:
 }
 
 # toggle built-in aliases and utilities, and aliases and functions from mkshrc
@@ -479,7 +524,6 @@ function enable {
 	i_func[nfunc++]=break
 	# \\builtin cannot, by design, be overridden
 	i_func[nfunc++]=builtin
-	i_func[nfunc++]=cat
 	i_func[nfunc++]=cd
 	i_func[nfunc++]=chdir
 	i_func[nfunc++]=command
@@ -522,8 +566,6 @@ function enable {
 	i_func[nfunc++]=bind
 	i_func[nfunc++]=mknod
 	i_func[nfunc++]=printf
-	i_func[nfunc++]=sleep
-	i_func[nfunc++]=domainname
 	i_func[nfunc++]=extproc
 
 	# accumulate aliases from dot.mkshrc, in definition order
@@ -585,7 +627,7 @@ function enable {
 		(*)
 			\\builtin print -ru2 enable: usage: \
 			    "enable [-adnps] [-f filename] [name ...]"
-			return 2
+			\\builtin return 2
 			;;
 		}
 	done
--- mksh-59c.orig/edit.c
+++ mksh-59c/edit.c
@@ -6,7 +6,7 @@
 /*-
  * Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
  *		 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018,
- *		 2019, 2020
+ *		 2019, 2020, 2021
  *	mirabilos <m@mirbsd.org>
  *
  * Provided that these terms and disclaimer and all copyright notices
@@ -29,7 +29,7 @@
 
 #ifndef MKSH_NO_CMDLINE_EDITING
 
-__RCSID("$MirOS: src/bin/mksh/edit.c,v 1.357 2020/10/31 05:02:17 tg Exp $");
+__RCSID("$MirOS: src/bin/mksh/edit.c,v 1.362 2021/02/26 11:51:07 tg Exp $");
 
 /*
  * in later versions we might use libtermcap for this, but since external
@@ -103,6 +103,7 @@ static void x_init_prompt(bool);
 static int x_vi(char *);
 #endif
 static void x_intr(int, int) MKSH_A_NORETURN;
+static void x_clrtoeol(int, bool);
 
 #define x_flush()	shf_flush(shl_out)
 #if defined(MKSH_SMALL) && !defined(MKSH_SMALL_BUT_FAST)
@@ -982,8 +983,8 @@ static int x_col;		/* current column on
 
 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_bword(uint32_t, bool);
+static void x_fword(uint32_t, bool);
 static void x_goto(char *);
 static char *x_bs0(char *, char *) MKSH_A_PURE;
 static void x_bs3(char **);
@@ -1006,7 +1007,7 @@ 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);
+static int x_fold_case(int, uint32_t);
 #endif
 static char *x_lastcp(void);
 static void x_lastpos(void);
@@ -1042,6 +1043,12 @@ static struct x_defbindings const x_defb
 	{ XFUNC_mv_bword,		1,	'b'	},
 	{ XFUNC_mv_fword,		1,	'f'	},
 	{ XFUNC_del_fword,		1,	'd'	},
+#ifndef MKSH_SMALL
+	{ XFUNC_del_bbigword,		1,	'H'	},
+	{ XFUNC_mv_bbigword,		1,	'B'	},
+	{ XFUNC_mv_fbigword,		1,	'F'	},
+	{ XFUNC_del_fbigword,		1,	'D'	},
+#endif
 	{ XFUNC_mv_back,		0,  CTRL_B	},
 	{ XFUNC_mv_forw,		0,  CTRL_F	},
 	{ XFUNC_search_char_forw,	0,  CTRL_BC	},
@@ -1096,11 +1103,11 @@ static struct x_defbindings const x_defb
 	{ XFUNC_set_arg,		1,	'8'	},
 	{ XFUNC_set_arg,		1,	'9'	},
 #ifndef MKSH_SMALL
-	{ XFUNC_fold_upper,		1,	'U'	},
+	{ XFUNC_foldb_upper,		1,	'U'	},
 	{ XFUNC_fold_upper,		1,	'u'	},
-	{ XFUNC_fold_lower,		1,	'L'	},
+	{ XFUNC_foldb_lower,		1,	'L'	},
 	{ XFUNC_fold_lower,		1,	'l'	},
-	{ XFUNC_fold_capitalise,	1,	'C'	},
+	{ XFUNC_foldb_capitalise,	1,	'C'	},
 	{ XFUNC_fold_capitalise,	1,	'c'	},
 #endif
 	/*
@@ -1524,75 +1531,105 @@ x_delete(size_t nc, bool push)
 static int
 x_del_bword(int c MKSH_A_UNUSED)
 {
-	x_delete(x_bword(), true);
+	x_bword(C_MFS, true);
 	return (KSTD);
 }
 
 static int
 x_mv_bword(int c MKSH_A_UNUSED)
 {
-	x_bword();
+	x_bword(C_MFS, false);
 	return (KSTD);
 }
 
 static int
 x_mv_fword(int c MKSH_A_UNUSED)
 {
-	x_fword(true);
+	x_fword(C_MFS, false);
 	return (KSTD);
 }
 
 static int
 x_del_fword(int c MKSH_A_UNUSED)
 {
-	x_delete(x_fword(false), true);
+	x_fword(C_MFS, true);
 	return (KSTD);
 }
 
-static size_t
-x_bword(void)
+#ifndef MKSH_SMALL
+static int
+x_del_bbigword(int c MKSH_A_UNUSED)
+{
+	x_bword(C_BLANK, true);
+	return (KSTD);
+}
+
+static int
+x_mv_bbigword(int c MKSH_A_UNUSED)
+{
+	x_bword(C_BLANK, false);
+	return (KSTD);
+}
+
+static int
+x_mv_fbigword(int c MKSH_A_UNUSED)
+{
+	x_fword(C_BLANK, false);
+	return (KSTD);
+}
+
+static int
+x_del_fbigword(int c MKSH_A_UNUSED)
+{
+	x_fword(C_BLANK, true);
+	return (KSTD);
+}
+#endif
+
+static void
+x_bword(uint32_t separator, bool erase)
 {
 	size_t nb = 0;
 	char *cp = xcp;
 
 	if (cp == xbuf) {
 		x_e_putc2(KSH_BEL);
-		return (0);
+		return;
 	}
 	while (x_arg--) {
-		while (cp != xbuf && ctype(cp[-1], C_MFS)) {
+		while (cp != xbuf && ctype(cp[-1], separator)) {
 			cp--;
 			nb++;
 		}
-		while (cp != xbuf && !ctype(cp[-1], C_MFS)) {
+		while (cp != xbuf && !ctype(cp[-1], separator)) {
 			cp--;
 			nb++;
 		}
 	}
 	x_goto(cp);
-	return (x_nb2nc(nb));
+	if (erase)
+		x_delete(x_nb2nc(nb), true);
 }
 
-static size_t
-x_fword(bool move)
+static void
+x_fword(uint32_t separator, bool erase)
 {
-	size_t nc;
 	char *cp = xcp;
 
 	if (cp == xep) {
 		x_e_putc2(KSH_BEL);
-		return (0);
+		return;
 	}
 	while (x_arg--) {
-		while (cp != xep && ctype(*cp, C_MFS))
+		while (cp != xep && ctype(*cp, separator))
 			cp++;
-		while (cp != xep && !ctype(*cp, C_MFS))
+		while (cp != xep && !ctype(*cp, separator))
 			cp++;
 	}
-	nc = x_nb2nc(cp - xcp);
-	if (move)
+	if (erase)
+		x_delete(x_nb2nc(cp - xcp), true);
+	else
 		x_goto(cp);
-	return (nc);
 }
 
 static void
@@ -2035,7 +2072,7 @@ x_match(const char *str, const char *pat
 	if (*pat == '^') {
 		return ((strncmp(str, pat + 1, strlen(pat + 1)) == 0) ? 0 : -1);
 	} else {
-		char *q = strstr(str, pat);
+		const char *q = cstrstr(str, pat);
 		return ((q == NULL) ? -1 : q - str);
 	}
 }
@@ -2079,6 +2116,11 @@ static int
 x_cls(int c MKSH_A_UNUSED)
 {
 	shf_puts(MKSH_CLS_STRING, shl_out);
+	if (prompt_trunc) {
+		/* multi-line prompt */
+		pprompt(prompt, 0);
+		/* x_redraw takes care of the last line */
+	}
 	x_redraw(0);
 	return (KSTD);
 }
@@ -2094,12 +2136,14 @@ x_clrtoeol(int lastch, bool line_was_cle
 {
 	int col;
 
-	if (lastch == ' ' && !line_was_cleared && x_term_mode == 1) {
-		shf_puts(KSH_ESC_STRING "[K", shl_out);
-		line_was_cleared = true;
+	if (lastch == ' ') {
+		if (line_was_cleared)
+			return;
+		if (x_term_mode == 1) {
+			shf_puts(KSH_ESC_STRING "[K", shl_out);
+			return;
+		}
 	}
-	if (lastch == ' ' && line_was_cleared)
-		return;
 
 	col = x_col;
 	while (col < (xx_cols - 2)) {
@@ -3149,12 +3193,12 @@ x_edit_line(int c MKSH_A_UNUSED)
 
 /*-
  * NAME:
- *	x_prev_histword - recover word from prev command
+ *	x_prev_histword - recover bigword from prev command
  *
  * DESCRIPTION:
- *	This function recovers the last word from the previous
+ *	This function recovers the last bigword 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
+ *	numeric arg is supplied then the n'th bigword 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.
@@ -3192,13 +3236,13 @@ x_prev_histword(int c MKSH_A_UNUSED)
 
 		rcp = &cp[strlen(cp) - 1];
 		/*
-		 * ignore white-space after the last word
+		 * ignore whitespace after the last bigword
 		 */
-		while (rcp > cp && ctype(*rcp, C_CFS))
+		while (rcp > cp && ctype(*rcp, C_BLANK))
 			rcp--;
-		while (rcp > cp && !ctype(*rcp, C_CFS))
+		while (rcp > cp && !ctype(*rcp, C_BLANK))
 			rcp--;
-		if (ctype(*rcp, C_CFS))
+		if (ctype(*rcp, C_BLANK))
 			rcp++;
 		x_ins(rcp);
 	} else {
@@ -3207,18 +3251,18 @@ x_prev_histword(int c MKSH_A_UNUSED)
 
 		rcp = cp;
 		/*
-		 * ignore white-space at start of line
+		 * ignore whitespace at start of line
 		 */
-		while (*rcp && ctype(*rcp, C_CFS))
+		while (*rcp && ctype(*rcp, C_BLANK))
 			rcp++;
 		while (x_arg-- > 0) {
-			while (*rcp && !ctype(*rcp, C_CFS))
+			while (*rcp && !ctype(*rcp, C_BLANK))
 				rcp++;
-			while (*rcp && ctype(*rcp, C_CFS))
+			while (*rcp && ctype(*rcp, C_BLANK))
 				rcp++;
 		}
 		cp = rcp;
-		while (*rcp && !ctype(*rcp, C_CFS))
+		while (*rcp && !ctype(*rcp, C_BLANK))
 			rcp++;
 		ch = *rcp;
 		*rcp = '\0';
@@ -3236,21 +3280,42 @@ x_prev_histword(int c MKSH_A_UNUSED)
 static int
 x_fold_upper(int c MKSH_A_UNUSED)
 {
-	return (x_fold_case('U'));
+	return (x_fold_case('U', C_MFS));
 }
 
 /* Lowercase N(1) words */
 static int
 x_fold_lower(int c MKSH_A_UNUSED)
 {
-	return (x_fold_case('L'));
+	return (x_fold_case('L', C_MFS));
 }
 
 /* Titlecase N(1) words */
 static int
 x_fold_capitalise(int c MKSH_A_UNUSED)
 {
-	return (x_fold_case('C'));
+	return (x_fold_case('C', C_MFS));
+}
+
+/* Uppercase N(1) bigwords */
+static int
+x_foldb_upper(int c MKSH_A_UNUSED)
+{
+	return (x_fold_case('U', C_BLANK));
+}
+
+/* Lowercase N(1) bigwords */
+static int
+x_foldb_lower(int c MKSH_A_UNUSED)
+{
+	return (x_fold_case('L', C_BLANK));
+}
+
+/* Titlecase N(1) bigwords */
+static int
+x_foldb_capitalise(int c MKSH_A_UNUSED)
+{
+	return (x_fold_case('C', C_BLANK));
 }
 
 /*-
@@ -3259,13 +3324,13 @@ x_fold_capitalise(int c MKSH_A_UNUSED)
  *
  * 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.
+ *	to UPPER CASE, lower case or Capitalise words and bigwords.
  *
  * RETURN VALUE:
  *	None
  */
 static int
-x_fold_case(int c)
+x_fold_case(int c, uint32_t separator)
 {
 	char *cp = xcp;
 
@@ -3277,7 +3342,7 @@ x_fold_case(int c)
 		/*
 		 * first skip over any white-space
 		 */
-		while (cp != xep && ctype(*cp, C_MFS))
+		while (cp != xep && ctype(*cp, separator))
 			cp++;
 		/*
 		 * do the first char on its own since it may be
@@ -3295,7 +3360,7 @@ x_fold_case(int c)
 		/*
 		 * now for the rest of the word
 		 */
-		while (cp != xep && !ctype(*cp, C_MFS)) {
+		while (cp != xep && !ctype(*cp, separator)) {
 			if (c == 'U')
 				/* uppercase */
 				*cp = ksh_toupper(*cp);
--- mksh-59c.orig/emacsfn.h
+++ mksh-59c/emacsfn.h
@@ -1,5 +1,5 @@
 /*-
- * Copyright (c) 2009, 2010, 2015, 2016, 2020
+ * Copyright (c) 2009, 2010, 2015, 2016, 2020, 2021
  *	mirabilos <m@mirbsd.org>
  *
  * Provided that these terms and disclaimer and all copyright notices
@@ -19,7 +19,7 @@
  */
 
 #if defined(EMACSFN_DEFNS)
-__RCSID("$MirOS: src/bin/mksh/emacsfn.h,v 1.11 2020/04/13 20:46:39 tg Exp $");
+__RCSID("$MirOS: src/bin/mksh/emacsfn.h,v 1.15 2021/02/26 11:51:08 tg Exp $");
 #define FN(cname,sname,flags)	static int x_##cname(int);
 #elif defined(EMACSFN_ENUMS)
 #define FN(cname,sname,flags)	XFUNC_##cname,
@@ -42,8 +42,14 @@ FN(comp_list, "complete-list", 0)
 FN(complete, "complete", 0)
 FN(del_back, "delete-char-backward", XF_ARG)
 FN(del_bword, "delete-word-backward", XF_ARG)
+#ifndef MKSH_SMALL
+FN(del_bbigword, "delete-bigword-backward", XF_ARG)
+#endif
 FN(del_char, "delete-char-forward", XF_ARG)
 FN(del_fword, "delete-word-forward", XF_ARG)
+#ifndef MKSH_SMALL
+FN(del_fbigword, "delete-bigword-forward", XF_ARG)
+#endif
 FN(del_line, "kill-line", 0)
 FN(draw_line, "redraw", 0)
 #ifndef MKSH_SMALL
@@ -59,8 +65,11 @@ FN(eval_region, "evaluate-region", 0)
 #endif
 FN(expand, "expand-file", 0)
 #ifndef MKSH_SMALL
-FN(fold_capitalise, "capitalize-word", XF_ARG)
+FN(foldb_capitalise, "capitalise-bigword", XF_ARG)
+FN(fold_capitalise, "capitalise-word", XF_ARG)
+FN(foldb_lower, "downcase-bigword", XF_ARG)
 FN(fold_lower, "downcase-word", XF_ARG)
+FN(foldb_upper, "upcase-bigword", XF_ARG)
 FN(fold_upper, "upcase-word", XF_ARG)
 #endif
 FN(goto_hist, "goto-history", XF_ARG)
@@ -80,15 +89,21 @@ FN(meta_yank, "yank-pop", 0)
 FN(mv_back, "backward-char", XF_ARG)
 FN(mv_beg, "beginning-of-line", 0)
 FN(mv_bword, "backward-word", XF_ARG)
+#ifndef MKSH_SMALL
+FN(mv_bbigword, "backward-bigword", XF_ARG)
+#endif
 FN(mv_end, "end-of-line", 0)
 FN(mv_forw, "forward-char", XF_ARG)
 FN(mv_fword, "forward-word", XF_ARG)
+#ifndef MKSH_SMALL
+FN(mv_fbigword, "forward-bigword", XF_ARG)
+#endif
 FN(newline, "newline", 0)
 FN(next_com, "down-history", XF_ARG)
 FN(nl_next_com, "newline-and-next", 0)
 FN(noop, "no-op", 0)
 FN(prev_com, "up-history", XF_ARG)
-FN(prev_histword, "prev-hist-word", XF_ARG)
+FN(prev_histword, "prev-hist-bigword", XF_ARG)
 #ifndef MKSH_SMALL
 FN(quote_region, "quote-region", 0)
 #endif
--- mksh-59c.orig/exec.c
+++ mksh-59c/exec.c
@@ -3,7 +3,7 @@
 /*-
  * Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
  *		 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018,
- *		 2019, 2020
+ *		 2019, 2020, 2021
  *	mirabilos <m@mirbsd.org>
  *
  * Provided that these terms and disclaimer and all copyright notices
@@ -24,7 +24,7 @@
 
 #include "sh.h"
 
-__RCSID("$MirOS: src/bin/mksh/exec.c,v 1.224 2020/08/27 19:52:43 tg Exp $");
+__RCSID("$MirOS: src/bin/mksh/exec.c,v 1.226 2021/01/27 15:49:12 tg Exp $");
 
 #ifndef MKSH_DEFAULT_EXECSHELL
 #define MKSH_DEFAULT_EXECSHELL	MKSH_UNIXROOT "/bin/sh"
@@ -773,8 +773,17 @@ comexec(struct op *t, struct tbl * volat
 		if (tp->flag & FKSH) {
 			/* Korn style functions restore Flags on return */
 			old_flags[(int)FXTRACE] = Flag(FXTRACE);
+			/* some must not be restored / need special handling */
 			for (type_flags = 0; type_flags < FNFLAGS; ++type_flags)
-				shell_flags[type_flags] = old_flags[type_flags];
+#ifndef MKSH_UNEMPLOYED
+				if (type_flags == FMONITOR)
+					change_flag(type_flags, OF_INTERNAL,
+					    old_flags[type_flags]);
+				else
+#endif
+				  if (type_flags != FPRIVILEGED)
+					shell_flags[type_flags] =
+					    old_flags[type_flags];
 		}
 #endif
 		tp->flag = (tp->flag & ~FINUSE) | old_inuse;
--- mksh-59c.orig/funcs.c
+++ mksh-59c/funcs.c
@@ -38,7 +38,7 @@
 #endif
 #endif
 
-__RCSID("$MirOS: src/bin/mksh/funcs.c,v 1.379 2020/08/27 19:52:44 tg Exp $");
+__RCSID("$MirOS: src/bin/mksh/funcs.c,v 1.383 2021/01/27 15:59:33 tg Exp $");
 
 #if HAVE_KILLPG
 /*
@@ -98,7 +98,6 @@ const struct builtin mkshbuiltins[] = {
 	{Tsgbreak, c_brkcont},
 	{T__builtin, c_builtin},
 	{Tbuiltin, c_builtin},
-	{Tbcat, c_cat},
 	{Tcd, c_cd},
 	/* dash compatibility hack */
 	{"chdir", c_cd},
@@ -152,13 +151,6 @@ const struct builtin mkshbuiltins[] = {
 #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
 #ifdef __OS2__
 	{Textproc, c_true},
 #endif
@@ -2757,11 +2749,6 @@ test_isop(Test_meta meta, const char *s)
 #define test_lstat(name,buffer)	lstat((name), (buffer))
 #endif
 
-#if HAVE_ST_MTIM
-#undef st_mtimensec
-#define st_mtimensec st_mtim.tv_nsec
-#endif
-
 static int
 mtimecmp(const struct stat *sb1, const struct stat *sb2)
 {
@@ -2769,7 +2756,7 @@ mtimecmp(const struct stat *sb1, const s
 		return (-1);
 	if (sb1->st_mtime > sb2->st_mtime)
 		return (1);
-#if (HAVE_ST_MTIMENSEC || HAVE_ST_MTIM)
+#if HAVE_ST_MTIMENSEC
 	if (sb1->st_mtimensec < sb2->st_mtimensec)
 		return (-1);
 	if (sb1->st_mtimensec > sb2->st_mtimensec)
@@ -3276,167 +3263,6 @@ c_realpath(const char **wp)
 	return (rv);
 }
 
-int
-c_cat(const char **wp)
-{
-	int fd = 0, rv;
-	ssize_t n, w;
-	const char *fn = "<stdin>";
-	char *buf, *cp;
-	bool opipe;
-#define MKSH_CAT_BUFSIZ 4096
-
-	/* parse options: POSIX demands we support "-u" as no-op */
-	while ((rv = ksh_getopt(wp, &builtin_opt, Tu)) != -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);
-	}
-
-	/* catch SIGPIPE */
-	opipe = block_pipe();
-
-	do {
-		if (*wp) {
-			fn = *wp++;
-			if (ksh_isdash(fn))
-				fd = 0;
-			else if ((fd = binopen2(fn, O_RDONLY)) < 0) {
-				bi_errorf(Tf_sD_s, fn, cstrerror(errno));
-				rv = 1;
-				continue;
-			}
-		}
-		while (/* CONSTCOND */ 1) {
-			if ((n = blocking_read(fd, (cp = buf),
-			    MKSH_CAT_BUFSIZ)) == -1) {
-				if (errno == EINTR) {
-					if (opipe)
-						restore_pipe();
-					/* give the user a chance to ^C out */
-					intrcheck();
-					/* interrupted, try again */
-					opipe = block_pipe();
-					continue;
-				}
-				/* an error occurred during reading */
-				bi_errorf(Tf_sD_s, fn, cstrerror(errno));
-				rv = 1;
-				break;
-			} else if (n == 0)
-				/* end of file reached */
-				break;
-			while (n) {
-				if (intrsig)
-					goto has_intrsig;
-				if ((w = write(1, cp, n)) != -1) {
-					n -= w;
-					cp += w;
-					continue;
-				}
-				if (errno == EINTR) {
- has_intrsig:
-					if (opipe)
-						restore_pipe();
-					/* give the user a chance to ^C out */
-					intrcheck();
-					/* interrupted, try again */
-					opipe = block_pipe();
-					continue;
-				}
-				if (errno == EPIPE) {
-					/* fake receiving signal */
-					rv = ksh_sigmask(SIGPIPE);
-				} else {
-					/* an error occurred during writing */
-					bi_errorf(Tf_sD_s, "<stdout>",
-					    cstrerror(errno));
-					rv = 1;
-				}
-				if (fd != 0)
-					close(fd);
-				goto out;
-			}
-		}
-		if (fd != 0)
-			close(fd);
-	} while (*wp);
-
- out:
-	if (opipe)
-		restore_pipe();
-	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(Tf_sD_s_qs, 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(Tf_sD_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)
--- mksh-59c.orig/lex.c
+++ mksh-59c/lex.c
@@ -23,7 +23,7 @@
 
 #include "sh.h"
 
-__RCSID("$MirOS: src/bin/mksh/lex.c,v 1.251 2020/03/10 23:48:40 tg Exp $");
+__RCSID("$MirOS: src/bin/mksh/lex.c,v 1.252 2020/12/14 00:21:09 tg Exp $");
 
 /*
  * states while lexing word
@@ -552,7 +552,7 @@ yylex(int cf)
 				 * "…`…\"…`…" because, unlike for COMSUBs, the
 				 * outer double quoteing changes the backslash
 				 * meaning for the inside. For more details:
-				 * http://austingroupbugs.net/view.php?id=1015
+				 * https://www.austingroupbugs.net/view.php?id=1015
 				 */
 				statep->ls_bool = false;
 				s2 = statep;
@@ -1792,7 +1792,6 @@ yyskiputf8bom(void)
 		ungetsc_i(asc2rtt(0xEF));
 		return;
 	}
-	UTFMODE |= 8;
 }
 
 static Lex_state *
--- mksh-59c.orig/main.c
+++ mksh-59c/main.c
@@ -6,7 +6,7 @@
 /*-
  * Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
  *		 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018,
- *		 2019, 2020
+ *		 2019, 2020, 2021
  *	mirabilos <m@mirbsd.org>
  *
  * Provided that these terms and disclaimer and all copyright notices
@@ -35,7 +35,7 @@
 #include <locale.h>
 #endif
 
-__RCSID("$MirOS: src/bin/mksh/main.c,v 1.374 2020/10/01 20:28:54 tg Exp $");
+__RCSID("$MirOS: src/bin/mksh/main.c,v 1.377 2021/02/07 02:02:26 tg Exp $");
 
 #ifndef MKSHRC_PATH
 #define MKSHRC_PATH	"~/.mkshrc"
@@ -84,13 +84,13 @@ static const char *initcoms[] = {
 	NULL,
 	 /* this is what AT&T ksh seems to track, with the addition of emacs */
 	Talias, "-tU",
-	Tcat, "cc", "chmod", "cp", "date", "ed", "emacs", "grep", "ls",
+	"cat", "cc", "chmod", "cp", "date", "ed", "emacs", "grep", "ls",
 	"make", "mv", "pr", "rm", "sed", Tsh, "vi", "who", NULL,
 	NULL
 };
 
 static const char *restr_com[] = {
-	Ttypeset, Tdr, TPATH, TENV, TSHELL, NULL
+	Ttypeset, Tdr, TENV, "HISTFILE", TPATH, TSHELL, NULL
 };
 
 static bool initio_done;
@@ -306,7 +306,7 @@ main_init(int argc, const char *argv[],
 
 	/* define built-in commands and see if we were called as one */
 	ktinit(APERM, &builtins,
-	    /* currently up to 52 builtins: 75% of 128 = 2^7 */
+	    /* currently up to 50 builtins: 75% of 128 = 2^7 */
 	    7);
 	for (i = 0; mkshbuiltins[i].name != NULL; ++i) {
 		const char *builtin_name;
@@ -611,6 +611,8 @@ main_init(int argc, const char *argv[],
 	ccp = null;
 	switch (utf_flag) {
 
+	/* not set on command line, not FTALKING */
+	case 2:
 	/* auto-detect from locale or environment */
 	case 4:
 #if HAVE_SETLOCALE_CTYPE
@@ -636,8 +638,6 @@ main_init(int argc, const char *argv[],
 		UTFMODE = isuc(ccp);
 		break;
 
-	/* not set on command line, not FTALKING */
-	case 2:
 	/* unknown values */
 	default:
 		utf_flag = 0;
@@ -2127,16 +2127,16 @@ void
 recheck_ctype(void)
 {
 	const char *ccp;
-	uint8_t old_utfmode = UTFMODE;
 
 	ccp = str_val(global("LC_ALL"));
 	if (ccp == null)
 		ccp = str_val(global("LC_CTYPE"));
 	if (ccp == null)
 		ccp = str_val(global("LANG"));
+	/*XXX POSIXly, only the locale must determine the outcome */
 	UTFMODE = isuc(ccp);
 #if HAVE_SETLOCALE_CTYPE
-	ccp = setlocale(LC_CTYPE, ccp);
+	ccp = setlocale(LC_CTYPE, *ccp ? ccp : "C");
 #if HAVE_LANGINFO_CODESET
 	if (!isuc(ccp))
 		ccp = nl_langinfo(CODESET);
@@ -2144,8 +2144,5 @@ recheck_ctype(void)
 	if (isuc(ccp))
 		UTFMODE = 1;
 #endif
-
-	if (Flag(FPOSIX) && UTFMODE && !old_utfmode)
-		warningf(true, "early locale tracking enabled UTF-8 mode while in POSIX mode, you are now noncompliant");
 }
 #endif
--- mksh-59c.orig/misc.c
+++ mksh-59c/misc.c
@@ -4,7 +4,7 @@
 /*-
  * Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
  *		 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2019,
- *		 2020
+ *		 2020, 2021
  *	mirabilos <m@mirbsd.org>
  * Copyright (c) 2015
  *	Daniel Richard G. <skunk@iSKUNK.ORG>
@@ -33,7 +33,7 @@
 #include <grp.h>
 #endif
 
-__RCSID("$MirOS: src/bin/mksh/misc.c,v 1.302 2020/08/27 19:52:45 tg Exp $");
+__RCSID("$MirOS: src/bin/mksh/misc.c,v 1.306 2021/01/26 23:49:49 tg Exp $");
 
 #define KSH_CHVT_FLAG
 #ifdef MKSH_SMALL
@@ -292,10 +292,6 @@ change_flag(enum sh_flag f, int what, bo
 
 		/* +++ privs changed +++ */
 	} else if ((f == FPOSIX || f == FSH) && newval) {
-		/* Turning on -o posix? */
-		if (f == FPOSIX)
-			/* C locale required for compliance */
-			UTFMODE = 0;
 		/* Turning on -o posix or -o sh? */
 		Flag(FBRACEEXPAND) = 0;
 #ifndef MKSH_NO_CMDLINE_EDITING
@@ -1678,14 +1674,18 @@ ksh_get_wd(void)
 	char *rv, *cp;
 
 	if ((cp = get_current_dir_name())) {
-		strdupx(rv, cp, ATEMP);
+		if (mksh_abspath(cp))
+			strdupx(rv, cp, ATEMP);
+		else
+			rv = NULL;
 		free_gnu_gcdn(cp);
 	} else
 		rv = NULL;
 #else
 	char *rv;
 
-	if (!getcwd((rv = alloc(PATH_MAX + 1, ATEMP)), PATH_MAX)) {
+	if (!getcwd((rv = alloc(PATH_MAX + 1, ATEMP)), PATH_MAX) ||
+	    !mksh_abspath(rv)) {
 		afree(rv, ATEMP);
 		rv = NULL;
 	}
@@ -1704,15 +1704,17 @@ do_realpath(const char *upath)
 	char *xp, *ip, *tp, *ipath, *ldest = NULL;
 	XString xs;
 	size_t pos, len;
-	int llen;
+	ssize_t llen;
 	struct stat sb;
 #ifdef MKSH__NO_PATH_MAX
-	size_t ldestlen = 0;
-#define pathlen sb.st_size
-#define pathcnd (ldestlen < (pathlen + 1))
+	off_t ldestlen = 0;
+#define pathlen ((size_t)sb.st_size)
+#define pathcnd ((ldestlen < 1) || ((ldestlen - 1) < sb.st_size))
+#define ldestsz ((size_t)ldestlen)
 #else
-#define pathlen PATH_MAX
+#define pathlen ((size_t)PATH_MAX)
 #define pathcnd (!ldest)
+#define ldestsz (pathlen + 1U)
 #endif
 	/* max. recursion depth */
 	int symlinks = 32;
@@ -1730,7 +1732,7 @@ do_realpath(const char *upath)
 #endif
 	} else {
 		/* upath is a relative pathname, prepend cwd */
-		if ((tp = ksh_get_wd()) == NULL || !mksh_abspath(tp))
+		if ((tp = ksh_get_wd()) == NULL)
 			return (NULL);
 		strpathx(ipath, tp, upath, 1);
 		afree(tp, ATEMP);
@@ -1813,15 +1815,19 @@ do_realpath(const char *upath)
 			/* get symlink(7) target */
 			if (pathcnd) {
 #ifdef MKSH__NO_PATH_MAX
-				if (notoktoadd(pathlen, 1)) {
+				/* same as notoktoadd(pathlen, 1) but adapted */
+				if ((uintmax_t)sb.st_size >= (uintmax_t)SIZE_MAX) {
 					errno = ENAMETOOLONG;
 					goto notfound;
 				}
+				ldestlen = sb.st_size + 1; /* <= SIZE_MAX */
 #endif
-				ldest = aresize(ldest, pathlen + 1, ATEMP);
+				/* ldestsz == pathlen + 1 */
+				ldest = aresize(ldest, ldestsz, ATEMP);
 			}
-			llen = readlink(Xstring(xs, xp), ldest, pathlen);
-			if (llen < 0)
+			errno = ENAMETOOLONG; /* for > pathlen case */
+			llen = readlink(Xstring(xs, xp), ldest, ldestsz);
+			if (llen < 0 || (size_t)llen > pathlen)
 				/* oops... */
 				goto notfound;
 			ldest[llen] = '\0';
@@ -1910,11 +1916,11 @@ do_realpath(const char *upath)
 
  notfound:
 	/* save; freeing memory might trash it */
-	llen = errno;
+	symlinks = errno;
 	afree(ldest, ATEMP);
 	afree(ipath, ATEMP);
 	Xfree(xs, xp);
-	errno = llen;
+	errno = symlinks;
 	return (NULL);
 
 #undef pathlen
--- mksh-59c.orig/mksh.1
+++ mksh-59c/mksh.1
@@ -1,9 +1,9 @@
-.\" $MirOS: src/bin/mksh/mksh.1,v 1.494 2020/10/01 22:39:57 tg Exp $
+.\" $MirOS: src/bin/mksh/mksh.1,v 1.506+locale-tracking 2021/02/26 11:51:08 tg Exp $
 .\" $OpenBSD: ksh.1,v 1.160 2015/07/04 13:27:04 feinerer Exp $
 .\"-
 .\" Copyright © 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009,
 .\"		2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
-.\"		2018, 2019, 2020
+.\"		2018, 2019, 2020, 2021
 .\"	mirabilos <m@mirbsd.org>
 .\"
 .\" Provided that these terms and disclaimer and all copyright notices
@@ -84,7 +84,7 @@
 .\" with -mandoc, it might implement .Mx itself, but we want to
 .\" use our own definition. And .Dd must come *first*, always.
 .\"
-.Dd $Mdocdate: October 1 2020 $
+.Dd March 13, 2021
 .\"
 .\" Check which macro package we use, and do other -mdoc setup.
 .\"
@@ -309,31 +309,32 @@ files:
 .Pp
 .Bl -bullet -compact
 .It
-The
-.Ic cd
-.Po and Ic chdir Pc
-command is disabled.
-.It
-The
-.Ev SHELL ,
-.Ev ENV
-and
+Command names cannot be specified with pathnames, absolute or relative,
+nor using the
+.Fl p
+option of the
+.Ic command
+built-in utility; the
+.Ev ENV ,
 .Ev PATH
+and
+.Ev SHELL
 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.
+The current location is fixed: the
+.Ic cd
+command and its alias
+.Ic chdir
+is disabled.
 .It
 Redirections that create files can't be used (i.e.\&
 .Dq Li \*(Gt ,
 .Dq Li \*(Gt\*(Ba ,
 .Dq Li \*(Gt\*(Gt ,
-.Dq Li \*(Lt\*(Gt ) .
+.Dq Li \*(Lt\*(Gt ) ,
+and the
+.Ev HISTFILE
+parameter cannot be changed.
 .El
 .It Fl s
 The shell reads commands from standard input; all non-option arguments
@@ -1316,7 +1317,7 @@ not
 .Dq Li D
 and
 .Dq Li E .
-This behavior is POSIX compliant, but incompatible with some other shell
+This behaviour 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
@@ -1356,7 +1357,8 @@ is interpreted to mean substitute the co
 Note that
 .Ic $(\*(Ltfoo)
 has the same effect as
-.Ic $(cat foo) .
+.Ic $(cat foo)
+but is much more performant.
 .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
@@ -2999,6 +3001,8 @@ outside the function).
 .It
 Shell options
 .Pq Ic set Fl o
+except
+.Fl p Pq Fl o Ic privileged
 have local scope, i.e. changes inside a function are reset upon its exit.
 .El
 .Pp
@@ -3046,13 +3050,12 @@ commands keeping assignments:
 All other builtins are not special; these are at least:
 .Pp
 .Ic [\& , alias , bg , bind ,
-.Ic builtin , cat , cd , command ,
-.Ic echo , false , fc , fg ,
-.Ic getopts , jobs , kill , let ,
-.Ic print , pwd , read , realpath ,
-.Ic rename , sleep , suspend , test ,
-.Ic true , ulimit , umask , unalias ,
-.Ic wait , whence
+.Ic builtin , cd , command , echo ,
+.Ic false , fc , fg , getopts ,
+.Ic jobs , kill , let , print ,
+.Ic pwd , read , realpath , rename ,
+.Ic suspend , test , true , ulimit ,
+.Ic umask , unalias , wait , 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.
@@ -3114,7 +3117,7 @@ variable for your consumption.
 .It Ic Lstripcom Op Ar
 .Pq Li dot.mkshrc No function
 Same as
-.Ic cat
+.Xr cat 1
 but strips any empty lines and comments (from any
 .Sq #
 character onwards, no escapes)
@@ -3297,26 +3300,6 @@ declaration utility (see
 is a declaration utility.
 .Pp
 .It Xo
-.Ic cat
-.Op Fl u
-.Op Ar
-.Xc
-.Pq defer with flags
-Copy files in command line order to standard output.
-If a
-.Ar file
-is a single dash
-.Pq Dq Li \-
-or absent, read from standard input.
-For direct builtin calls, the
-.Tn POSIX
-.Fl u
-option is supported as a no-op.
-For calls from shell, if any options are given, an external
-.Xr cat 1
-utility is preferred over the builtin.
-.Pp
-.It Xo
 .Ic cd
 .Op Fl L
 .Op Ar dir
@@ -4460,10 +4443,9 @@ and at least one of these returns someth
 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.
+aforementioned environment variables.
 .Pp
-In near future, locale tracking will be implemented, which means that
+This build of the shell implements semi-early locale tracking, that is,
 .Ic set Fl +U
 is changed whenever one of the
 .Tn POSIX
@@ -4574,9 +4556,7 @@ and
 this autodetection feature is compiled in.
 As a side effect, setting this flag turns off the
 .Ic braceexpand
-and
-.Ic utf8\-mode
-flags, which can be turned back on manually, and
+flag, which can be turned back on manually, and
 .Pq unless both are set in the same command
 .Ic sh
 mode.
@@ -4672,13 +4652,6 @@ etc.
 .Pq Ar number No defaults to 1
 are renamed to 1, 2, etc.
 .Pp
-.It Ic sleep Ar seconds
-.Pq regular , needs Xr select 2
-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 smores Op Ar
 .Pq Li dot.mkshrc No function
 Simple pager:
@@ -5405,6 +5378,12 @@ Limit the size of
 message queues to
 .Ar n
 bytes.
+.It Fl R Ar n
+.Pq Cm Linux
+Limit the CPU time slice a real-time process can use before
+performing a blocking syscall to
+.Ar n
+milliseconds.
 .It Fl r Ar n
 .Pq Cm AIX
 Limit the number of threads per process to
@@ -5907,21 +5886,28 @@ 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
+suffixed with a colon; a
 .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.
+(e.g. the ASCII Esc character is written as
+.Li \*(ha[ )
+or terminal-specific indications.
 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.
+is one or more digits.
 Unless otherwise specified, if a count is
 omitted, it defaults to 1.
 .Pp
+Bigwords, as used below, are separated by spaces or tabs;
+words consist of alphanumerics, underscore
+.Pq Ql _
+or dollar sign
+.Pq Ql $
+characters.
+.Pp
 Note that editing command names are used only with the
 .Ic bind
 command.
@@ -5944,8 +5930,13 @@ Emacs key bindings:
 Abort the current command, save it to the history, empty the line buffer and
 set the exit state to interrupted.
 .It auto\-insert: Op Ar n
+.Pq Most ordinary characters are bound to this command.
 Simply causes the character to appear as literal input.
-Most ordinary characters are bound to this.
+.It Xo backward\-bigword:
+.Op Ar n
+.No \*(ha[B
+.Xc
+Moves the cursor backward to the beginning of the bigword.
 .It Xo backward\-char:
 .Op Ar n
 .No \*(haB , \*(haXD , ANSI-CurLeft , PC-CurLeft
@@ -5957,19 +5948,21 @@ characters.
 .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 Ql _
-and dollar sign
-.Pq Ql $
-characters.
+Moves the cursor backward to the beginning of the word.
 .It beginning\-of\-history: \*(ha[\*(Lt
 Moves to the beginning of the history.
 .It beginning\-of\-line: \*(haA, ANSI-Home, PC-Home
 Moves the cursor to the beginning of the edited input line.
+.It Xo capitalise\-bigword:
+.Op Ar n
+.No \*(ha[C
+.Xc
+Uppercase the first character in the next
+.Ar n
+bigwords as below.
 .It Xo capitalise\-word:
 .Op Ar n
-.No \*(ha[C , \*(ha[c
+.No \*(ha[c
 .Xc
 Uppercase the first ASCII character in the next
 .Ar n
@@ -6013,6 +6006,20 @@ match as in the
 .Ic complete
 command above.
 Note that \*(haI is usually generated by the Tab (tabulator) key.
+.It Xo delete\-bigword\-backward:
+.Op Ar n
+.No \*(ha[H
+.Xc
+Deletes
+.Ar n
+bigwords before the cursor.
+.It Xo delete\-bigword\-forward:
+.Op Ar n
+.No \*(ha[D
+.Xc
+Deletes characters after the cursor up to the end of
+.Ar n
+bigwords.
 .It Xo delete\-char\-backward:
 .Op Ar n
 .No ERASE Pq \*(haH ,
@@ -6060,9 +6067,16 @@ is not useful until either
 or
 .Ic up\-history
 has been performed.
+.It Xo downcase\-bigword:
+.Op Ar n
+.No \*(ha[L
+.Xc
+Lowercases the next
+.Ar n
+bigwords.
 .It Xo downcase\-word:
 .Op Ar n
-.No \*(ha[L , \*(ha[l
+.No \*(ha[l
 .Xc
 Lowercases the next
 .Ar n
@@ -6113,6 +6127,13 @@ Appends a
 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\-bigword:
+.Op Ar n
+.No \*(ha[F
+.Xc
+Moves the cursor forward to the end of the
+.Ar n Ns th
+bigword.
 .It Xo forward\-char:
 .Op Ar n
 .No \*(haF , \*(haXC , ANSI-CurRight , PC-CurRight
@@ -6181,15 +6202,18 @@ This does nothing.
 Introduces a 2-character command sequence.
 .It prefix\-2: \*(haX , \*(ha[[ , \*(ha[O
 Introduces a multi-character command sequence.
-.It Xo prev\-hist\-word:
+.It prefix\-3: \*(ha@
+Introduces a PC keyboard scancode.
+.It Xo prev\-hist\-bigword:
 .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.
+If no count is given, the last bigword, otherwise the
+.No ( Ar n Ns +1)th
+bigword of the previous line is inserted at the cursor,
+and the mark is set to the beginning of the inserted word.
+When invoked repeatedly, the inserted text is replaced by the corresponding
+bigword from the second-last, third-last, etc. line.
 .It quote: \*(ha\*(ha , \*(haV
 The following character is taken literally rather than as an editing command.
 .It quote\-region: \*(ha[Q
@@ -6219,7 +6243,7 @@ The internal history list is searched
 backwards for commands matching the input.
 An initial
 .Ql \*(ha
-in the search string anchors the search.
+in the search string anchors the search at the beginning of the line.
 The escape key will leave search mode.
 Other commands, including sequences of escape as
 .Ic prefix\-1
@@ -6227,31 +6251,33 @@ followed by a
 .Ic prefix\-1
 or
 .Ic prefix\-2
-key will be executed after leaving search mode.
+key, will be executed after leaving search mode.
 The
 .Ic abort Pq \*(haG
-command will restore the input line before search started.
+command will restore the input line from before search started.
 Successive
 .Ic search\-history
-commands continue searching backward to the next previous occurrence of the
-pattern.
+commands continue searching backward to the following 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, PC-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, PC-PgDn
-Search forwards through the history buffer for commands whose beginning match
+Search forwards (this command is only useful after an
+.Ic up\-history ,
+.Ic search\-history\-up
+or
+.Ic search\-history )
+through the history buffer for commands whose beginning matches
 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 search\-history\-up: ANSI-PgUp, PC-PgUp
+Search backwards through the history buffer for commands whose beginning
+matches 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 set\-arg: \*(ha[0 .. \*(ha[9
+Mapped to begin prefixing a count to a command.
 .It set\-mark\-command: \*(ha[ Ns Aq space
 Set the mark at the cursor position.
 .It transpose\-chars: \*(haT
@@ -6267,9 +6293,16 @@ character to the right.
 Scrolls the history buffer backward
 .Ar n
 lines (earlier).
+.It Xo upcase\-bigword:
+.Op Ar n
+.No \*(ha[U
+.Xc
+Uppercase the next
+.Ar n
+bigwords.
 .It Xo upcase\-word:
 .Op Ar n
-.No \*(ha[U , \*(ha[u
+.No \*(ha[u
 .Xc
 Uppercase the next
 .Ar n
@@ -6279,6 +6312,8 @@ 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 vt100\-hack: \*(ha[[1
+Mapped to internally represent some longer key sequences.
 .It yank: \*(haY
 Inserts the most recently killed text string at the current cursor position.
 .It yank\-pop: \*(ha[y
@@ -6289,7 +6324,7 @@ replaces the inserted text string with t
 .Pp
 The tab completion escapes characters the same way as the following code:
 .Bd -literal
-print \-nr \-\- "${x@/[\e"\-\e$\e&\-*:\-?[\e\e\e`\e{\-\e}${IFS\-$\*(aq \et\en\*(aq}]/\e\e$KSH_MATCH}"
+print \-nr \-\- "${x@/[\e"\-\e$\e&\-*:\-?[\e\e\e\`\e{\-\e}${IFS\-$\*(aq \et\en\*(aq}]/\e\e$KSH_MATCH}"
 .Ed
 .Ss Vi editing mode
 .Em Note:
@@ -7066,6 +7101,12 @@ and wraparound defined, even (defying PO
 currently uses OPTU-16 internally, which is the same as UTF-8 and CESU-8
 with 0000..FFFD being valid codepoints; raw octets are mapped into the
 PUA range EF80..EFFF, which is assigned by CSUR for this purpose.
+.Em Future compatibility note :
+there's work underway to use full 21-bit UTF-8 in mksh R60 or so.
+Raw octet mapping will almost certainly be moved out of the PUA and into
+some range outside of UCS, such as 0x00400000 with the lower bits
+corresponding to the octet; high-bit7 octets only to keep ASCII unambiguous
+(EBCDIC will have to see, perhaps using the extant ASCII mapping).
 .Sh BUGS
 Suspending (using \*(haZ) pipelines like the one below will only suspend
 the currently running part of the pipeline; in this example,
@@ -7089,7 +7130,7 @@ for the in-memory portion of the history
 .Xr memmove 3 .
 .Pp
 This document attempts to describe
-.Nm mksh\ R59c
+.Nm mksh\ R59-CURRENT
 and up,
 .\" with vendor patches from insert-your-name-here,
 compiled without any options impacting functionality, such as
--- mksh-59c.orig/mksh.faq
+++ mksh-59c/mksh.faq
@@ -1,4 +1,4 @@
-RCSID: $MirOS: src/bin/mksh/mksh.faq,v 1.10 2020/10/01 22:59:12 tg Exp $
+RCSID: $MirOS: src/bin/mksh/mksh.faq,v 1.17+locale-tracking 2021/03/11 14:16:08 tg Exp $
 ToC: spelling
 Title: How do you spell <tt>mksh</tt>? How do you pronounce it?
 
@@ -8,12 +8,15 @@ Title: How do you spell <tt>mksh</tt>? H
  initial lowercase letter</a>; this is important) or “MirBSD Korn Shell”,
  possibly with “the”.</p>
 <p>I usually pronounce it as “<span xml:lang="de-DE-1901">em-ka-es-ha</span>”,
- that is, the letters individually in my native German, or say “MirBSD Korn
- Shell”, although it is manageable, mostly for Slavic speakers, to actually
- say “mksh” as if it were a word ☺</p>
+ that is, the letters individually in my native German, emphasis on the
+ first syllable, or say “MirBSD Korn Shell”, although it is manageable,
+ mostly for Slavic speakers, to actually say “mksh” as if it were a word ☺</p>
 <p>Oh… I’ve run into this one, didn’t I? “MirBSD” is pronounced “<span
  xml:lang="de-DE-1901">Mir-Be-Es-De</span>” germanically, for anglophones
  “Mir-beas’tie” is fine.</p>
+<p>This translates well into other languages, such as <span
+ xml:lang="es">eme-ka-ese-ache</span> in Spanish, although English
+ speakers may still find “Mir-beastie korn shell” more palatable.</p>
 ----
 ToC: sowhatismksh
 Title: I’m a $OS (<i>Android, OS/2, …</i>) user, so what’s mksh?
@@ -260,7 +263,7 @@ Title: My prompt is weird!
  (This was agreed upon as suggestion in a discussion between bash, zsh and
  Korn shell developers.) The feature set of different shells vastly differs
  and each shell should use its default PS1 or from its startup files.</li>
-<li><tt>$ENV</tt> <a href="#env">is set and/or <tt>export</tt>ed</a>.</li>
+<li><tt>$ENV</tt> <a href="#env">is set and probably <tt>export</tt>ed</a>.</li>
 <li>Your prompt is just “<tt># </tt>”: you’re entering a root shell, and
  <tt>$PS1</tt> does not contain the ‘#’ character, in which case the shell
  forces this prompt, making extra privileges obvious.</li>
@@ -284,7 +287,7 @@ Title: My prompt is weird!
 ToC: env
 Title: On startup files and <tt>$ENV</tt> across and detecting various shells
 
-Interactive shells look at <tt>~/.mkshrc</tt> (or <tt>/system/etc/mkshrc</tt>
+<p>Interactive shells look at <tt>~/.mkshrc</tt> (or <tt>/system/etc/mkshrc</tt>
 on Android and <tt>/etc/mkshrc</tt> on FreeWRT and OpenWrt) by default. This
 location can, however, be overridden by setting the <tt>ENV</tt> environment
 variable. (FreeBSD is rumoured to set it in their system profile.) It’s better
@@ -295,7 +298,30 @@ or “MIRBSD KSH” for mksh, “PD KSH
 for ksh93); <tt>$NETBSD_SHELL</tt> (NetBSD ash); <tt>POSH_VERSION</tt> (posh, a
 pdksh derivative); <tt>$SH_VERSION</tt> (“PD KSH” as sh), <tt>$YASH_VERSION</tt>
 (yash), <tt>$ZSH_VERSION</tt> (or if <tt>$VERSION</tt> begins with “zsh”); a <a
-href="@@RELPATH@@ksh-chan.htm#which-shell">list of more approaches</a> exists.
+href="@@RELPATH@@ksh-chan.htm#which-shell">list of more approaches</a> exists.</p>
+
+<p>Note that, in some scenarios, it might be very useful to actually set
+ <tt>$ENV</tt>: the regular interactive shell startup file lies in the
+ user’s home directory, relying on being copied from <tt>/etc/skel/</tt>
+ which normally is only done at user creation time. If mksh was installed
+ later, the user often won’t get it at all, and delivering updates is
+ challenging. One way of partially working around this is to ship an
+ <tt>/etc/skel/.mkshrc</tt> that reads <tt>/etc/mkshrc</tt> by default
+ (but the user can change it of course) and ship the <tt>dot.mkshrc</tt>
+ file as <tt>/etc/mkshrc</tt>, but that won’t fully help. This is where
+ <tt>$ENV</tt> comes into play:</p><ul>
+  <li>In <tt>/etc/profile</tt>, set <tt>ENV</tt> to a, say, <tt>shrc</tt>
+   file shipped in <tt>/etc/</tt> and export it.</li>
+  <li>In that new file, which must use only constructs compatible with
+   all shells, usually a subset of POSIX, read the various rc files
+   (<tt>.mkshrc</tt> for mksh, <tt>.kshrc</tt> for AT&amp;T ksh93, etc.)
+   from the user’s home if they exist, from <tt>/etc/skel/</tt> otherwise.</li>
+</ul><p>This may very well be <em>required</em> if the alternative would
+ be <a href="#ps1weird">to <del><tt>export PS1</tt></del>[sic!]</a>. <a
+  href="https://gitlab.alpinelinux.org/alpine/aports/-/issues/12398#note_146574"
+ >alpine Linux</a> encountered this very problem, and the linked post is
+ a (draft) solution using the <tt>$ENV</tt> method and looks at various
+ other shells’ startup file situation as well.</p>
 ----
 ToC: ctrl-x-e
 Title: Multiline command editing
@@ -329,9 +355,15 @@ Use ^[^L (Escape+Ctrl-L) or rebind it:<b
 ToC: ctrl-u-pico
 Title: ^U (Ctrl-U) clears the entire line
 
-If it should only delete the line up to the cursor, use:<br />
+If you want it to only delete the line up to the cursor, use:<br />
 <tt>bind -m ^U='^[0^K'</tt>
 ----
+ToC: ctrl-w-bash
+Title: ^W (Ctrl-W) deletes a word, not a bigword
+
+If you want it to delete more, with R60 you can use:<br />
+<tt>bind '^W=delete-bigword-backward'</tt>
+----
 ToC: cur-up-zsh
 Title: Cursor Up behaves differently from zsh
 
@@ -398,7 +430,12 @@ Title: How POSIX compliant is mksh? Also
  <tt>utf8-mode</tt> (which only supports the BMP (Basic Multilingual Plane) of
  UCS and maps raw octets into the U+EF80‥U+EFFF wide character range; see
  <tt>Arithmetic expressions</tt> in mksh(1) for details) <em>must</em> stay
- disabled in POSIX mode (it is disabled upon enabling POSIX mode in R56+).</p>
+ disabled in POSIX mode (it is disabled upon enabling POSIX mode in R56+ but
+ don’t depend on this to stay once locale tracking will be implemented; the
+ disabling code is not present in this build).</p>
+<p><strong>Future compatibility note:</strong> there’s work underway to use
+ full 21-bit UTF-8 in mksh R60 or so. Raw octet mapping will almost certainly
+ be moved out of the PUA and to some range outside of UCS.</p>
 <p class="boxhead">The following POSIX sh-compatible code toggles the
  <tt>utf8-mode</tt> option dependent on the current POSIX locale, for mksh
  to allow using the UTF-8 mode, within the constraints outlined above, in
@@ -414,7 +451,7 @@ Title: How POSIX compliant is mksh? Also
 	esac
  </pre>
 </div><p class="boxfoot">In near future, (UTF-8) locale tracking will
- be implemented, though.</p>
+ be implemented, though. This build of mksh already enables it.</p>
 <p>The shell is pretty close to POSIX, when run as <tt>lksh -o posix</tt>
  under the "C" locale it is intended to match. It does not do everything
  like other POSIX-compatible or ‑compliant shells, though.</p>
@@ -532,6 +569,15 @@ This is because AT&amp;T ksh93 ships a p
 put this into your <tt>~/.mkshrc</tt>
 (note the space before the closing single quote)
 ----
+ToC: builtin-cat
+Title: Didn’t there used to be a cat(1) builtin?
+
+<p>Up to and including mksh R59c, we indeed shipped a built-in cat(1)
+ inside mksh; this was added originally because Android did not have
+ one <em>at all</em> (but they have since imported a BSD cat). While
+ it could speed up some sh scripts correct signal handling is hard to
+ get right, so (with regret) it was removed in 2021. 🙀</p>
+----
 ToC: builtin-rename
 Title: “rename” doesn’t work as expected!
 
@@ -546,23 +592,25 @@ Title: “rename” doesn’t work as ex
 <pre>alias rename="$(whence -p rename)"</pre>
 ----
 ToC: builtin-sleep
-Title: “sleep” does not accept ‘m’ for minutes!
+Title: Didn’t there used to be a sleep(1) builtin?
 
-<p>mksh contains a <tt>sleep</tt> built-in utility, in order to be
- able to offer sub-second sleep to shell scripts for most platforms.
- (It does not exist if the platform lacks select(2) — which should
- be rare.)</p>
-<p>GNU coreutils contains a sleep implementation accepting suffixed
- numbers. If you wish to invoke an external utility (in favour over a
- builtin), you can use <tt>dot.mkshrc</tt>’s function <tt>enable</tt>
- or put something along the following lines into <tt>~/.mkshrc</tt>:</p>
-<pre>alias sleep="$(whence -p sleep)"</pre>
-<pre>timer() { sleep $(($1*60${2:++$2})); } # timer mins [secs]</pre>
-<pre>timer() {
-	local arg=${1/m/'*60+'}
-	[[ $arg = *+ ]] &amp;&amp; arg+=0
-	sleep $(($arg)
-}</pre>
+<p>Up to and including mksh R59c, we indeed shipped a subsecond-capable
+ select(2)-based built-in sleep(1). This got originally added because
+ too many platforms do not support sub-second sleep, which nowadays is
+ of less concern. It also led to users complaining about lack for system
+ *ahem* GNU extensions, but the cause of its demise is that getting signal
+ handling right, in a portable way and without too many syscalls (there’s
+ a threshold over which fork+exec is cheaper!), isn’t feasible if even at
+ all possible.</p>
+<p>The MirOS Project now ships <a href="@@RELPATH@@subprj.htm#sleep">a
+ portable sleep</a> which similarily is select(2)-based and capable of
+ subsecond sleep but in addition supports all GNU extensions related to
+ specifying the amount of time to sleep. It will work on <em>at least</em>
+ all platforms on which mksh had a builtin before. Please install this
+ if your operating system lacks a good enough sleep(1) utility.</p>
+<p>Note that, if your OS lacks select(2), you’ll lose out either way.
+ In that case, GNU coreutils’ sleep, which is built on older syscalls,
+ may work if the copyleft licence isn’t a showstopper for you.</p>
 ----
 ToC: string-concat
 Title: “+=” behaves differently from other shells
--- mksh-59c.orig/rlimits.opt
+++ mksh-59c/rlimits.opt
@@ -1,5 +1,5 @@
 /*-
- * Copyright (c) 2013, 2015, 2019
+ * Copyright (c) 2013, 2015, 2019, 2021
  *	mirabilos <m@mirbsd.org>
  *
  * Provided that these terms and disclaimer and all copyright notices
@@ -21,7 +21,7 @@
  */
 
 @RLIMITS_DEFNS
-__RCSID("$MirOS: src/bin/mksh/rlimits.opt,v 1.5 2020/07/24 20:11:18 tg Exp $");
+__RCSID("$MirOS: src/bin/mksh/rlimits.opt,v 1.6 2021/01/10 18:44:06 tg Exp $");
 @RLIMITS_ITEMS
 #define FN(lname,lid,lfac,lopt) (const struct limits *)(&rlimits_ ## lid),
 @@
@@ -94,6 +94,9 @@ FN("threadsperprocess", RLIMIT_THREADS,
 >e|RLIMIT_NICE
 FN("maxnice", RLIMIT_NICE, 1
 
+>R|RLIMIT_RTTIME
+FN("rttime(ms)", RLIMIT_RTTIME, 1
+
 >r|RLIMIT_RTPRIO
 FN("maxrtprio", RLIMIT_RTPRIO, 1
 
--- mksh-59c.orig/sh.h
+++ mksh-59c/sh.h
@@ -193,9 +193,9 @@
 #endif
 
 #ifdef EXTERN
-__RCSID("$MirOS: src/bin/mksh/sh.h,v 1.904 2020/10/31 03:53:06 tg Exp $");
+__RCSID("$MirOS: src/bin/mksh/sh.h,v 1.906 2021/01/24 19:37:31 tg Exp $");
 #endif
-#define MKSH_VERSION "R59 2020/10/31"
+#define MKSH_VERSION "R59 2021/03/13"
 
 /* arithmetic types: C implementation */
 #if !HAVE_CAN_INTTYPES
@@ -666,7 +666,7 @@ char *ucstrstr(char *, const char *);
 #endif
 #endif
 
-#if (!defined(MKSH_BUILDMAKEFILE4BSD) && !defined(MKSH_BUILDSH)) || (MKSH_BUILD_R != 593)
+#if (!defined(MKSH_BUILDMAKEFILE4BSD) && !defined(MKSH_BUILDSH)) || (MKSH_BUILD_R != 599)
 #error Must run Build.sh to compile this.
 extern void thiswillneverbedefinedIhope(void);
 int
@@ -999,8 +999,6 @@ EXTERN const char Tcant_cd[] E_INIT("res
 EXTERN const char Tcant_find[] E_INIT("can't find");
 EXTERN const char Tcant_open[] E_INIT("can't open");
 #define Tbytes (Toomem + 24)
-EXTERN const char Tbcat[] E_INIT("!cat");
-#define Tcat (Tbcat + 1)
 #define Tcd (Tcant_cd + 25)
 #define T_command (T_funny_command + 9)
 #define Tcommand (T_funny_command + 10)
@@ -1165,8 +1163,6 @@ EXTERN const char T_devtty[] E_INIT("/de
 #define Tcant_find "can't find"
 #define Tcant_open "can't open"
 #define Tbytes "bytes"
-#define Tbcat "!cat"
-#define Tcat "cat"
 #define Tcd "cd"
 #define T_command "-command"
 #define Tcommand "command"
@@ -2499,8 +2495,6 @@ 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 *);
