Merge "Upgrade to mksh R57."
am: 66875a42ae

Change-Id: If14b1c41c3e83eba52d5a5c4b3ec70b10e9abb5d
diff --git a/Android.bp b/Android.bp
index 4e491e0..b7edef2 100644
--- a/Android.bp
+++ b/Android.bp
@@ -107,7 +107,7 @@
         "-DHAVE_SYS_ERRLIST_DECL=0",
         "-DHAVE_SYS_SIGLIST_DECL=1",
         "-DHAVE_PERSISTENT_HISTORY=0",
-        "-DMKSH_BUILD_R=563",
+        "-DMKSH_BUILD_R=571",
 
         // Additional flags
         "-DMKSH_DEFAULT_PROFILEDIR=\"/system/etc\"",
diff --git a/Android.patch.txt b/Android.patch.txt
index c3cf892..15118e3 100644
--- a/Android.patch.txt
+++ b/Android.patch.txt
@@ -1,5 +1,6 @@
---- mksh-R56b/funcs.c	2017-05-05 15:53:55.000000000 -0700
-+++ src/funcs.c	2017-09-22 16:19:44.327000462 -0700
+diff -ru mksh-R57/funcs.c src/funcs.c
+--- mksh-R57/funcs.c	2018-10-20 14:04:55.000000000 -0700
++++ src/funcs.c	2019-03-26 12:05:23.976821773 -0700
 @@ -103,7 +103,9 @@
  	{Tsgbreak, c_brkcont},
  	{T__builtin, c_builtin},
@@ -31,11 +32,12 @@
  #ifdef __MirBSD__
  	/* alias to "true" for historical reasons */
  	{"domainname", c_true},
---- mksh-R56b/main.c	2017-04-28 04:14:14.000000000 -0700
-+++ src/main.c	2017-09-22 15:58:14.134149037 -0700
-@@ -410,6 +410,12 @@
- 		}
- 	}
+diff -ru mksh-R57/main.c src/main.c
+--- mksh-R57/main.c	2019-01-05 05:24:45.000000000 -0800
++++ src/main.c	2019-03-26 12:05:23.980821764 -0700
+@@ -399,6 +399,12 @@
+ 	/* import environment */
+ 	init_environ();
  
 +	/* override default PATH regardless of environment */
 +#ifdef MKSH_DEFPATH_OVERRIDE
diff --git a/src/Build.sh b/src/Build.sh
old mode 100755
new mode 100644
index ebf4e1c..be3f711
--- a/src/Build.sh
+++ b/src/Build.sh
@@ -1,5 +1,5 @@
 #!/bin/sh
-srcversion='$MirOS: src/bin/mksh/Build.sh,v 1.731 2018/01/13 21:38:06 tg Exp $'
+srcversion='$MirOS: src/bin/mksh/Build.sh,v 1.734 2019/03/01 16:18:13 tg Exp $'
 #-
 # Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
 #		2011, 2012, 2013, 2014, 2015, 2016, 2017
@@ -418,6 +418,7 @@
 			#include <unistd.h>
 			int main(void) { return (isatty(0)); }
 		EOF
+		#'
 	fi
 	eval fv=\$HAVE_CAN_`upper $vn`
 	if test -n "$fl"; then
@@ -1060,11 +1061,12 @@
 Darwin)
 	vv '|' "hwprefs machine_type os_type os_class >&2"
 	vv '|' "sw_vers >&2"
-	vv '|' "system_profiler SPSoftwareDataType SPHardwareDataType >&2"
+	vv '|' "system_profiler -detailLevel mini 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"
+	vv '|' "sysctl kern.version hw.machine hw.model hw.memsize hw.availcpu hw.ncpu hw.cpufrequency hw.byteorder hw.cpu64bit_capable >&2"
+	vv '|' "sysctl hw.cpufrequency hw.byteorder hw.cpu64bit_capable hw.ncpu >&2"
 	;;
 IRIX*)
 	vv '|' "uname -a >&2"
@@ -2427,7 +2429,7 @@
 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=563
+add_cppflags -DMKSH_BUILD_R=571
 
 $e $bi$me: Finished configuration testing, now producing output.$ao
 
diff --git a/src/check.t b/src/check.t
index ea414d3..d5df4cb 100644
--- a/src/check.t
+++ b/src/check.t
@@ -1,8 +1,9 @@
-# $MirOS: src/bin/mksh/check.t,v 1.801 2018/01/14 01:47:33 tg Exp $
+# $MirOS: src/bin/mksh/check.t,v 1.812 2019/03/01 16:17:29 tg Exp $
 # -*- mode: sh -*-
 #-
 # Copyright © 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
-#	      2011, 2012, 2013, 2014, 2015, 2016, 2017
+#	      2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018,
+#	      2019
 #	mirabilos <m@mirbsd.org>
 #
 # Provided that these terms and disclaimer and all copyright notices
@@ -30,7 +31,7 @@
 # (2013/12/02 20:39:44) http://cvsweb.openbsd.org/cgi-bin/cvsweb/src/regress/bin/ksh/?sortby=date
 
 expected-stdout:
-	@(#)MIRBSD KSH R56 2018/01/14
+	@(#)MIRBSD KSH R57 2019/03/01
 description:
 	Check base version of full shell
 stdin:
@@ -39,7 +40,7 @@
 category: !shell:legacy-yes
 ---
 expected-stdout:
-	@(#)LEGACY KSH R56 2018/01/14
+	@(#)LEGACY KSH R57 2019/03/01
 description:
 	Check base version of legacy shell
 stdin:
@@ -162,6 +163,38 @@
 expected-stdout:
 	;
 ---
+name: selftest-tty-absent
+description:
+	Check that a controlling tty is not present as regress:no-ctty was used
+	(if this test fails for you DO NOT PASS regress:no-ctty and fix every
+	other test that fails: why u use it if u haz ctty?)
+category: regress:no-ctty
+env-setup: !ENV=./envf!
+file-setup: file 644 "envf"
+	PS1=X
+arguments: !-i!
+stdin:
+	echo ok
+expected-stdout:
+	ok
+expected-stderr-pattern:
+	/mksh: warning: won't have full job control\nXX/
+---
+name: selftest-tty-present
+description:
+	Check that a controlling tty is present as regress:no-ctty was not used
+need-ctty: yes
+env-setup: !ENV=./envf!
+file-setup: file 644 "envf"
+	PS1=X
+arguments: !-i!
+stdin:
+	echo ok
+expected-stdout:
+	ok
+expected-stderr: !
+	XX
+---
 name: alias-1
 description:
 	Check that recursion is detected/avoided in aliases.
@@ -2455,7 +2488,7 @@
 name: glob-range-3
 description:
 	Check that globbing matches the right things...
-# breaks on Mac OSX (HFS+ non-standard Unicode canonical decomposition)
+# breaks on Mac OSX (HFS+ non-standard UTF-8 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,!os:os2,!os:os390
@@ -3033,6 +3066,19 @@
 	got four on fd#4
 	got five on fd#5
 ---
+name: heredoc-15
+description:
+	Check high-bit7 separators work
+stdin:
+	u=ä
+	tr a-z A-Z <<-…
+		m${u}h
+	…
+	echo ok
+expected-stdout:
+	MäH
+	ok
+---
 name: heredoc-comsub-1
 description:
 	Tests for here documents in COMSUB, taken from Austin ML
@@ -3526,6 +3572,25 @@
 expected-stdout:
 	'blah  1'
 ---
+name: single-quotes-in-heredoc-trim
+description:
+	In some cases, single quotes inside {} in heredoc are not normal
+stdin:
+	x=notOK
+	cat <<EOF
+	1: ${x#not} ${x:+${x#not}}
+	2: ${x#\n\o\t} ${x:+${x#\n\o\t}}
+	3: ${x#"not"} ${x:+${x#"not"}}
+	4: ${x#'not'} ${x:+${x#'not'}}
+	5: ${x#$'not'} ${x:+${x#$'not'}}
+	EOF
+expected-stdout:
+	1: OK OK
+	2: OK OK
+	3: OK OK
+	4: OK OK
+	5: OK OK
+---
 name: history-basic
 description:
 	See if we can test history at all
@@ -6835,6 +6900,21 @@
 	3 10 .
 	4 -2147483646 .
 ---
+name: export-1
+description:
+	Check allexport works, basic
+stdin:
+	qa=1
+	set -A qb 2 3
+	set -a
+	qc=4
+	set -A qd 5 6
+	export -p | grep '^export q'
+expected-stdout:
+	export qc=4
+	export qd[0]=5
+	export qd[1]=6
+---
 name: readonly-0
 description:
 	Ensure readonly is honoured for assignments and unset
@@ -7065,7 +7145,7 @@
 	print =4
 	(exec lq)
 expected-stdout-pattern:
-	/=1\none\n=2\ntwo\n=3\n.*: ls: 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:
@@ -7086,7 +7166,7 @@
 	print =4
 	(exec lq)
 expected-stdout-pattern:
-	/=1\n.*: print: not found\n=2\n.*: foo: not found\n=3\n.*: ls: not found\n=4\ntf\n/
+	/=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/
 ---
 name: xxx-what-do-you-call-this-1
 stdin:
@@ -7179,6 +7259,32 @@
 	HI
 	2 4
 ---
+name: xxx-substitution-eval-order-2
+description:
+	Check some corner cases
+stdin:
+	unset var
+	i=42
+	: ${var+${q[i=777]}} required to be lazy by POSIX
+	echo 1=$i
+	var=meow
+	i=42
+	: ${var+${q[i=777]}} eval since var is now set
+	echo 2=$i
+	unset var
+	i=42
+	: ${var#${q[i=777]}} pattern is needed even if var is empty
+	echo 3=$i
+	var=meow
+	i=42
+	: ${var#${q[i=777]}}
+	echo 4=$i
+expected-stdout:
+	1=42
+	2=777
+	3=777
+	4=777
+---
 name: xxx-set-option-1
 description:
 	Check option parsing in set
@@ -7280,6 +7386,28 @@
 	trap: 4
 	exit: 4
 ---
+name: xxx-stat-1
+description:
+	Check that tests on files are consistent
+stdin:
+	mkdir a
+	echo x >a/b
+	test -e a/b; echo 1e $? .
+	test -f a/b; echo 1f $? .
+	chmod 0 a
+	test -e a/b; echo 2e $? .
+	test -f a/b; echo 2f $? .
+	chmod 700 a
+	test -e a/b; echo 3e $? .
+	test -f a/b; echo 3f $? .
+expected-stdout:
+	1e 0 .
+	1f 0 .
+	2e 1 .
+	2f 1 .
+	3e 0 .
+	3f 0 .
+---
 name: xxx-clean-chars-1
 description:
 	Check MAGIC character is stuffed correctly
@@ -7836,6 +7964,23 @@
 	EXtrap
 	= noeval-undef 1 .
 ---
+name: exit-trap-3
+description:
+	Check that the EXIT trap is run in many places, Debian #910276
+stdin:
+	fkt() {
+		trap -- "echo $1 >&2" EXIT
+	}
+	fkt shell_exit
+	$(fkt fn_exit)
+	$(trap -- "echo comsub_exit >&2" EXIT)
+	(trap -- "echo subshell_exit >&2" EXIT)
+expected-stderr:
+	fn_exit
+	comsub_exit
+	subshell_exit
+	shell_exit
+---
 name: exit-trap-interactive
 description:
 	Check that interactive shell doesn't exit via EXIT trap on syntax error
@@ -8444,12 +8589,32 @@
 expected-stdout:
 	<16#1     > <     16#1> <16#000001> <16#1     > <     16#1> <0000016#1>
 ---
+name: typeset-padding-3
+description:
+	Check for a regression in which UTF-8 wasn’t left-padded right
+stdin:
+	set -U
+	nl=$'\n'
+	typeset -L20 x='.  ak'
+	typeset -R20 y='.  ak'
+	print -r -- "<$x> (1$nl<12345678910 345678920$nl<$y> 1)"
+	typeset -L20 x='.  aẞ'
+	typeset -R20 y='.  aẞ'
+	print -r -- "<$x> (2$nl<12345678910 345678920$nl<$y> 2)"
+expected-stdout:
+	<.  ak               > (1
+	<12345678910 345678920
+	<               .  ak> 1)
+	<.  aẞ               > (2
+	<12345678910 345678920
+	<               .  aẞ> 2)
+---
 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)
+# breaks on Mac OSX (HFS+ non-standard UTF-8 canonical decomposition)
 category: !os:darwin,!shell:ebcdic-yes
 stdin:
 	mkdir foo
@@ -10328,7 +10493,7 @@
 ---
 name: integer-base-one-3Ws
 description:
-	some sample code for hexdumping Unicode
+	some sample code for hexdumping UCS-2
 	not NUL safe; input lines must be NL terminated
 stdin:
 	set -U
@@ -10496,7 +10661,7 @@
 ---
 name: integer-base-one-3Wr
 description:
-	some sample code for hexdumping Unicode; NUL and binary safe
+	some sample code for hexdumping UCS-2; NUL and binary safe
 stdin:
 	set -U
 	{
@@ -10616,7 +10781,7 @@
 ---
 name: integer-base-one-5A
 description:
-	Check to see that we’re NUL and Unicode safe
+	Check to see that we’re NUL and UCS safe
 category: !shell:ebcdic-yes
 stdin:
 	set +U
@@ -10630,7 +10795,7 @@
 ---
 name: integer-base-one-5E
 description:
-	Check to see that we’re NUL and Unicode safe
+	Check to see that we’re NUL and UCS safe
 category: !shell:ebcdic-no
 stdin:
 	set +U
@@ -10644,7 +10809,7 @@
 ---
 name: integer-base-one-5W
 description:
-	Check to see that we’re NUL and Unicode safe
+	Check to see that we’re NUL and UCS safe
 stdin:
 	set -U
 	print 'a\0b€c' >x
@@ -11139,6 +11304,34 @@
 expected-stdout:
 	Fowl
 ---
+name: fd-cloexec-3
+description:
+	Another check for close-on-exec
+stdin:
+	print '#!'"$__progname" >ts
+	cat >>ts <<'EOF'
+	s=ERR
+	read -rN-1 -u$1 s 2>/dev/null; e=$?
+	print -r -- "($1, $((!e)), $s)"
+	EOF
+	chmod +x ts
+	print foo >tx
+	runtest() {
+		s=$1; shift
+		print -r -- $("$__progname" "$@" -c "$s") "$@" .
+	}
+	runtest 'exec 3<tx; ./ts 3 3<&3; ./ts 3'
+	runtest 'exec 3<tx; ./ts 3 3<&3; ./ts 3' -o posix
+	runtest 'exec 3<tx; ./ts 3 3<&3; ./ts 3' -o sh
+	runtest 'exec 3<tx; ./ts 4 4<&3; ./ts 4 4<&3'
+	runtest 'exec 3<tx; ./ts 3 3<&3; ./ts 3 3<&3'
+expected-stdout:
+	(3, 1, foo) (3, 0, ERR) .
+	(3, 1, foo) (3, 1, ) -o posix .
+	(3, 1, foo) (3, 1, ) -o sh .
+	(4, 1, foo) (4, 1, ) .
+	(3, 1, foo) (3, 1, ) .
+---
 name: comsub-1a
 description:
 	COMSUB are now parsed recursively, so this works
diff --git a/src/edit.c b/src/edit.c
index 0e51780..c231af1 100644
--- a/src/edit.c
+++ b/src/edit.c
@@ -28,7 +28,7 @@
 
 #ifndef MKSH_NO_CMDLINE_EDITING
 
-__RCSID("$MirOS: src/bin/mksh/edit.c,v 1.342 2018/01/14 00:03:00 tg Exp $");
+__RCSID("$MirOS: src/bin/mksh/edit.c,v 1.343 2018/07/15 16:16:38 tg Exp $");
 
 /*
  * in later versions we might use libtermcap for this, but since external
@@ -3831,7 +3831,7 @@
 			vs = save_es;
 
 			i = (unsigned)srchlen;
-			while (--i >= n)
+			while (i-- > n)
 				vs->linelen -= char_len(locpat[i]);
 			srchlen = (int)n;
 			vs->cursor = vs->linelen;
diff --git a/src/exec.c b/src/exec.c
index 8330174..8231b54 100644
--- a/src/exec.c
+++ b/src/exec.c
@@ -2,7 +2,8 @@
 
 /*-
  * Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
- *		 2011, 2012, 2013, 2014, 2015, 2016, 2017
+ *		 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018,
+ *		 2019
  *	mirabilos <m@mirbsd.org>
  *
  * Provided that these terms and disclaimer and all copyright notices
@@ -23,7 +24,7 @@
 
 #include "sh.h"
 
-__RCSID("$MirOS: src/bin/mksh/exec.c,v 1.201 2017/10/11 21:09:24 tg Exp $");
+__RCSID("$MirOS: src/bin/mksh/exec.c,v 1.206 2019/03/01 16:17:53 tg Exp $");
 
 #ifndef MKSH_DEFAULT_EXECSHELL
 #define MKSH_DEFAULT_EXECSHELL	MKSH_UNIXROOT "/bin/sh"
@@ -429,6 +430,12 @@
 		up = makenv();
 		restoresigs();
 		cleanup_proc_env();
+		/* I/O redirection cleanup to be done in child process */
+		if (!Flag(FPOSIX) && !Flag(FSH) && t->left->ioact != NULL)
+			for (iowp = t->left->ioact; *iowp != NULL; iowp++)
+				if ((*iowp)->ioflag & IODUPSELF)
+					fcntl((*iowp)->unit, F_SETFD, 0);
+		/* try to execute */
 		{
 			union mksh_ccphack cargs;
 
@@ -820,7 +827,8 @@
 		if (!(tp->flag&ISSET)) {
 			if (tp->u2.errnov == ENOENT) {
 				rv = 127;
-				warningf(true, Tf_sD_s, cp, Tnot_found);
+				warningf(true, Tf_sD_s_s, cp,
+				    "inaccessible or", Tnot_found);
 			} else {
 				rv = 126;
 				warningf(true, Tf_sD_sD_s, cp, "can't execute",
@@ -862,7 +870,7 @@
  Leave:
 	if (flags & XEXEC) {
 		exstat = rv & 0xFF;
-		unwind(LLEAVE);
+		unwind(LEXIT);
 	}
 	return (rv);
 }
@@ -886,7 +894,7 @@
 	*tp->args-- = tp->str;
 
 #ifndef MKSH_SMALL
-	if ((fd = binopen2(tp->str, O_RDONLY)) >= 0) {
+	if ((fd = binopen2(tp->str, O_RDONLY | O_MAYEXEC)) >= 0) {
 		unsigned char *cp;
 #ifndef MKSH_EBCDIC
 		unsigned short m;
@@ -1011,7 +1019,7 @@
 	cap.ro = ap;
 	execve(args.rw[0], args.rw, cap.rw);
 
-	/* report both the programme that was run and the bogus interpreter */
+	/* report both the program that was run and the bogus interpreter */
 	errorf(Tf_sD_sD_s, tp->str, sh, cstrerror(errno));
 }
 
@@ -1485,9 +1493,11 @@
 			afree(sp, ATEMP);
 			return (-1);
 		}
-		if (u == (int)iop->unit)
+		if (u == (int)iop->unit) {
 			/* "dup from" == "dup to" */
+			iop->ioflag |= IODUPSELF;
 			return (0);
+		}
 		break;
 	    }
 	}
diff --git a/src/expr.c b/src/expr.c
index 499b961..5d2c869 100644
--- a/src/expr.c
+++ b/src/expr.c
@@ -23,7 +23,7 @@
 
 #include "sh.h"
 
-__RCSID("$MirOS: src/bin/mksh/expr.c,v 1.103 2018/01/14 01:29:47 tg Exp $");
+__RCSID("$MirOS: src/bin/mksh/expr.c,v 1.105 2018/08/10 02:53:33 tg Exp $");
 
 #define EXPRTOK_DEFNS
 #include "exprtok.h"
@@ -885,7 +885,7 @@
     unsigned int val) MKSH_A_PURE;
 
 /*
- * Generated from the Unicode Character Database, Version 10.0.0, by
+ * Generated from the UCD 11.0.0 by
  * MirOS: contrib/code/Snippets/eawparse,v 1.12 2017/09/06 16:05:45 tg Exp $
  */
 
@@ -909,12 +909,13 @@
 	{ 0x0730, 0x074A },
 	{ 0x07A6, 0x07B0 },
 	{ 0x07EB, 0x07F3 },
+	{ 0x07FD, 0x07FD },
 	{ 0x0816, 0x0819 },
 	{ 0x081B, 0x0823 },
 	{ 0x0825, 0x0827 },
 	{ 0x0829, 0x082D },
 	{ 0x0859, 0x085B },
-	{ 0x08D4, 0x08E1 },
+	{ 0x08D3, 0x08E1 },
 	{ 0x08E3, 0x0902 },
 	{ 0x093A, 0x093A },
 	{ 0x093C, 0x093C },
@@ -927,6 +928,7 @@
 	{ 0x09C1, 0x09C4 },
 	{ 0x09CD, 0x09CD },
 	{ 0x09E2, 0x09E3 },
+	{ 0x09FE, 0x09FE },
 	{ 0x0A01, 0x0A02 },
 	{ 0x0A3C, 0x0A3C },
 	{ 0x0A41, 0x0A42 },
@@ -953,6 +955,7 @@
 	{ 0x0BC0, 0x0BC0 },
 	{ 0x0BCD, 0x0BCD },
 	{ 0x0C00, 0x0C00 },
+	{ 0x0C04, 0x0C04 },
 	{ 0x0C3E, 0x0C40 },
 	{ 0x0C46, 0x0C48 },
 	{ 0x0C4A, 0x0C4D },
@@ -1072,6 +1075,7 @@
 	{ 0xA825, 0xA826 },
 	{ 0xA8C4, 0xA8C5 },
 	{ 0xA8E0, 0xA8F1 },
+	{ 0xA8FF, 0xA8FF },
 	{ 0xA926, 0xA92D },
 	{ 0xA947, 0xA951 },
 	{ 0xA980, 0xA982 },
@@ -1173,7 +1177,7 @@
 	return (0);
 }
 
-/* Unix column width of a wide character (Unicode code point, really) */
+/* Unix column width of a wide character (UCS code point, really) */
 int
 utf_wcwidth(unsigned int wc)
 {
diff --git a/src/funcs.c b/src/funcs.c
index 5179192..ade00d6 100644
--- a/src/funcs.c
+++ b/src/funcs.c
@@ -38,7 +38,7 @@
 #endif
 #endif
 
-__RCSID("$MirOS: src/bin/mksh/funcs.c,v 1.353 2018/01/14 01:26:49 tg Exp $");
+__RCSID("$MirOS: src/bin/mksh/funcs.c,v 1.355 2018/10/20 21:04:28 tg Exp $");
 
 #if HAVE_KILLPG
 /*
@@ -499,7 +499,7 @@
 						Xput(xs, xp, '\\');
 					}
 				} else if ((unsigned int)c > 0xFF) {
-					/* generic function returned Unicode */
+					/* generic function returned UCS */
 					po.ts[utf_wctomb(po.ts, c - 0x100)] = 0;
 					c = 0;
 					do {
@@ -2970,6 +2970,37 @@
 	case TO_STGT:
 		return (strcmp(opnd1, opnd2) > 0);
 
+	/* -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;
+
 	/* -eq */
 	case TO_INTEQ:
 	/* -ne */
@@ -3006,37 +3037,6 @@
 			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);
diff --git a/src/histrap.c b/src/histrap.c
index 6b9396e..985b2a3 100644
--- a/src/histrap.c
+++ b/src/histrap.c
@@ -3,7 +3,7 @@
 
 /*-
  * Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
- *		 2011, 2012, 2014, 2015, 2016, 2017
+ *		 2011, 2012, 2014, 2015, 2016, 2017, 2018
  *	mirabilos <m@mirbsd.org>
  *
  * Provided that these terms and disclaimer and all copyright notices
@@ -27,7 +27,7 @@
 #include <sys/file.h>
 #endif
 
-__RCSID("$MirOS: src/bin/mksh/histrap.c,v 1.166 2017/08/07 23:25:09 tg Exp $");
+__RCSID("$MirOS: src/bin/mksh/histrap.c,v 1.167 2018/04/28 17:16:54 tg Exp $");
 
 Trap sigtraps[ksh_NSIG + 1];
 static struct sigaction Sigact_ign;
@@ -879,7 +879,8 @@
 		}
 	}
  hist_trunc_done:
-	histfsize = lseek(histfd, (off_t)0, SEEK_END);
+	if ((histfsize = lseek(histfd, (off_t)0, SEEK_END)) < 0)
+		goto hist_init_fail;
  hist_init_tail:
 	mksh_unlkfd(histfd);
 }
@@ -962,8 +963,9 @@
 	unsigned char *base, *news;
 
 	mksh_lockfd(histfd);
-	sizenow = lseek(histfd, (off_t)0, SEEK_END);
-	if (sizenow < histfsize) {
+	if ((sizenow = lseek(histfd, (off_t)0, SEEK_END)) < 0)
+		goto bad;
+	else if (sizenow < histfsize) {
 		/* the file has shrunk; trust it just appending the new data */
 		/* well, for now, anyway… since mksh strdups all into memory */
 		/* we can use a nicer approach some time later… */
@@ -998,7 +1000,8 @@
 		hist_finish();
 		return;
 	}
-	histfsize = lseek(histfd, (off_t)0, SEEK_END);
+	if ((histfsize = lseek(histfd, (off_t)0, SEEK_END)) < 0)
+		goto bad;
 	mksh_unlkfd(histfd);
 }
 
diff --git a/src/jehanne.c b/src/jehanne.c
new file mode 100644
index 0000000..ba0eb0f
--- /dev/null
+++ b/src/jehanne.c
@@ -0,0 +1,36 @@
+/*-
+ * Copyright (c) 2017
+ *	Giacomo Tesio <giacomo@tesio.it>
+ *
+ * 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.
+ *-
+ * Initialisation code for the Jehanne operating system (a Plan 9 de-
+ * rivative, using GCC)
+ */
+
+static const char __rcsid[] __attribute__((__used__)) =
+    "$MirOS: src/bin/mksh/jehanne.c,v 1.1 2017/12/22 16:30:00 tg Exp $";
+
+#include <u.h>
+#include <lib9.h>
+#include <posix.h>
+
+void
+__application_newlib_init(int argc, char *argv[])
+{
+	rfork(RFFDG | RFREND | RFNOTEG);
+	libposix_emulate_SIGCHLD();
+}
diff --git a/src/jobs.c b/src/jobs.c
index a0b9b79..66e31aa 100644
--- a/src/jobs.c
+++ b/src/jobs.c
@@ -23,7 +23,7 @@
 
 #include "sh.h"
 
-__RCSID("$MirOS: src/bin/mksh/jobs.c,v 1.125 2018/01/05 20:08:34 tg Exp $");
+__RCSID("$MirOS: src/bin/mksh/jobs.c,v 1.127 2018/07/15 16:23:10 tg Exp $");
 
 #if HAVE_KILLPG
 #define mksh_killpg		killpg
@@ -169,12 +169,6 @@
 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);
@@ -190,8 +184,8 @@
 #endif
 
 #ifndef MKSH_UNEMPLOYED
-	if (!mflagset && Flag(FTALKING))
-		Flag(FMONITOR) = 1;
+	if (Flag(FMONITOR) == 127)
+		Flag(FMONITOR) = Flag(FTALKING);
 
 	/*
 	 * shl_j is used to do asynchronous notification (used in
@@ -1932,7 +1926,7 @@
 		break;
 	case 2:
 #ifndef MKSH_DISABLE_TTY_WARNING
-		warningf(false, Tf_sD_s_s, Tcant_find, Ttty_fd,
+		warningf(false, Tf_s_sD_s, Tcant_find, Ttty_fd,
 		    cstrerror(errno));
 #endif
 		break;
diff --git a/src/lex.c b/src/lex.c
index 9300311..8c75a98 100644
--- a/src/lex.c
+++ b/src/lex.c
@@ -23,7 +23,7 @@
 
 #include "sh.h"
 
-__RCSID("$MirOS: src/bin/mksh/lex.c,v 1.247 2018/01/14 01:44:01 tg Exp $");
+__RCSID("$MirOS: src/bin/mksh/lex.c,v 1.250 2018/10/20 18:34:14 tg Exp $");
 
 /*
  * states while lexing word
@@ -77,12 +77,17 @@
 	short nparen;
 	/* type of this state */
 	uint8_t type;
+	/* extra flags */
+	uint8_t ls_flags;
 } Lex_state;
 #define ls_base		u.base
 #define ls_start	u.start
 #define ls_bool		u.abool
 #define ls_adelim	u.adelim
 
+/* ls_flags */
+#define LS_HEREDOC	BIT(0)
+
 typedef struct {
 	Lex_state *base;
 	Lex_state *end;
@@ -147,9 +152,11 @@
 #define STATE_BSIZE	8
 
 #define PUSH_STATE(s)	do {					\
+	uint8_t state_flags = statep->ls_flags;			\
 	if (++statep == state_info.end)				\
 		statep = push_state_i(&state_info, statep);	\
 	state = statep->type = (s);				\
+	statep->ls_flags = state_flags;				\
 } while (/* CONSTCOND */ 0)
 
 #define POP_STATE()	do {					\
@@ -242,6 +249,7 @@
 
 	/* Initial state: one of SWORD SLETPAREN SHEREDELIM SBASE */
 	statep->type = state;
+	statep->ls_flags = (cf & HEREDOC) ? LS_HEREDOC : 0;
 
 	/* collect non-special or quoted characters to form word */
 	while (!((c = getsc()) == 0 ||
@@ -319,7 +327,7 @@
 				break;
 			case ORD('\''):
  open_ssquote_unless_heredoc:
-				if ((cf & HEREDOC))
+				if ((statep->ls_flags & LS_HEREDOC))
 					goto store_char;
 				*wp++ = OQUOTE;
 				ignore_backslash_newline++;
@@ -357,7 +365,7 @@
 				c = getsc();
 				switch (c) {
 				case ORD('"'):
-					if ((cf & HEREDOC))
+					if ((statep->ls_flags & LS_HEREDOC))
 						goto heredocquote;
 					/* FALLTHROUGH */
 				case ORD('\\'):
@@ -388,6 +396,8 @@
 					if ((unsigned int)c == ORD('(' /*)*/)) {
 						*wp++ = EXPRSUB;
 						PUSH_SRETRACE(SASPAREN);
+						/* unneeded? */
+						/*statep->ls_flags &= ~LS_HEREDOC;*/
 						statep->nparen = 2;
 						*retrace_info->xp++ = '(';
 					} else {
@@ -434,6 +444,8 @@
 							*wp++ = ADELIM;
 							*wp++ = ':';
 							PUSH_STATE(SBRACE);
+							/* perhaps unneeded? */
+							statep->ls_flags &= ~LS_HEREDOC;
 							PUSH_STATE(SADELIM);
 							statep->ls_adelim.delimiter = ':';
 							statep->ls_adelim.num = 1;
@@ -449,6 +461,8 @@
 							}
 							ungetsc(c);
 							PUSH_STATE(SBRACE);
+							/* perhaps unneeded? */
+							statep->ls_flags &= ~LS_HEREDOC;
 							PUSH_STATE(SADELIM);
 							statep->ls_adelim.delimiter = ':';
 							statep->ls_adelim.num = 2;
@@ -466,6 +480,8 @@
 						} else
 							ungetsc(c);
 						PUSH_STATE(SBRACE);
+						/* perhaps unneeded? */
+						statep->ls_flags &= ~LS_HEREDOC;
 						PUSH_STATE(SADELIM);
 						statep->ls_adelim.delimiter = '/';
 						statep->ls_adelim.num = 1;
@@ -489,6 +505,8 @@
 							PUSH_STATE(STBRACEBOURNE);
 						else
 							PUSH_STATE(STBRACEKORN);
+						/* single-quotes-in-heredoc-trim */
+						statep->ls_flags &= ~LS_HEREDOC;
 					} else {
 						ungetsc(c);
 						if (state == SDQUOTE ||
@@ -496,6 +514,8 @@
 							PUSH_STATE(SQBRACE);
 						else
 							PUSH_STATE(SBRACE);
+						/* here no LS_HEREDOC removal */
+						/* single-quotes-in-heredoc-braces */
 					}
 				} else if (ctype(c, C_ALPHX)) {
 					*wp++ = OSUBST;
@@ -601,7 +621,8 @@
 		case SSQUOTE:
 			if ((unsigned int)c == ORD('\'')) {
 				POP_STATE();
-				if ((cf & HEREDOC) || state == SQBRACE)
+				if ((statep->ls_flags & LS_HEREDOC) ||
+				    state == SQBRACE)
 					goto store_char;
 				*wp++ = CQUOTE;
 				ignore_backslash_newline--;
@@ -1155,7 +1176,7 @@
 			/* Newline terminates here document marker */
 			goto heredoc_found_terminator;
 		}
-	} else if (c == *eofp++)
+	} else if ((unsigned int)c == ord(*eofp++))
 		/* store; then read and compare next character */
 		goto heredoc_store_and_loop;
 	/* nope, mismatch; read until end of line */
@@ -1338,7 +1359,7 @@
 {
 	char *xp = Xstring(s->xs, xp), *cp;
 	bool interactive = Flag(FTALKING) && s->type == SSTDIN;
-	bool have_tty = tobool(interactive && (s->flags & SF_TTY));
+	bool have_tty = interactive && (s->flags & SF_TTY) && tty_hasstate;
 
 	/* Done here to ensure nothing odd happens when a timeout occurs */
 	XcheckN(s->xs, xp, LINE);
diff --git a/src/lksh.1 b/src/lksh.1
index c3a82cb..0d6b226 100644
--- a/src/lksh.1
+++ b/src/lksh.1
@@ -1,6 +1,6 @@
-.\" $MirOS: src/bin/mksh/lksh.1,v 1.23 2017/04/02 13:38:02 tg Exp $
+.\" $MirOS: src/bin/mksh/lksh.1,v 1.25 2018/12/25 19:38:08 tg Exp $
 .\"-
-.\" Copyright (c) 2008, 2009, 2010, 2012, 2013, 2015, 2016, 2017
+.\" Copyright (c) 2008, 2009, 2010, 2012, 2013, 2015, 2016, 2017, 2018
 .\"	mirabilos <m@mirbsd.org>
 .\"
 .\" Provided that these terms and disclaimer and all copyright notices
@@ -74,7 +74,7 @@
 .\" with -mandoc, it might implement .Mx itself, but we want to
 .\" use our own definition. And .Dd must come *first*, always.
 .\"
-.Dd $Mdocdate: April 2 2017 $
+.Dd $Mdocdate: December 25 2018 $
 .\"
 .\" Check which macro package we use, and do other -mdoc setup.
 .\"
@@ -82,6 +82,7 @@
 .	if \*[.T]utf8 .tr \[la]\*(Lt
 .	if \*[.T]utf8 .tr \[ra]\*(Gt
 .	ie d volume-ds-1 .ds tT gnu
+.	el .ie d doc-volume-ds-1 .ds tT gnp
 .	el .ds tT bsd
 .\}
 .el .ds tT ucb
@@ -94,7 +95,7 @@
 .	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]
+.	ds str-Mx1 \*[Tn-font-size]\%MirBSD\*[str-Mx]
 .	if !\n[arg-limit] \
 .	if \n[.$] \{\
 .	ds macro-name Mx
@@ -120,19 +121,51 @@
 .	ds sP \s0
 .	ds tN \*[Tn-font-size]
 .\}
+.el .ie "\*(tT"gnp" \{\
+.	eo
+.	de Mx
+.	nr doc-curr-font \n[.f]
+.	nr doc-curr-size \n[.ps]
+.	ds doc-str-Mx \f[\n[doc-curr-font]]\s[\n[doc-curr-size]u]
+.	ds doc-str-Mx1 \*[doc-Tn-font-size]\%MirBSD\*[doc-str-Mx]
+.	if !\n[doc-arg-limit] \
+.	if \n[.$] \{\
+.	ds doc-macro-name Mx
+.	doc-parse-args \$@
+.	\}
+.	if (\n[doc-arg-limit] > \n[doc-arg-ptr]) \{\
+.	nr doc-arg-ptr +1
+.	ie (\n[doc-type\n[doc-arg-ptr]] == 2) \
+.	as doc-str-Mx1 \~\*[doc-arg\n[doc-arg-ptr]]
+.	el \
+.	nr doc-arg-ptr -1
+.	\}
+.	ds doc-arg\n[doc-arg-ptr] "\*[doc-str-Mx1]
+.	nr doc-type\n[doc-arg-ptr] 2
+.	ds doc-space\n[doc-arg-ptr] "\*[doc-space]
+.	nr doc-num-args (\n[doc-arg-limit] - \n[doc-arg-ptr])
+.	nr doc-arg-limit \n[doc-arg-ptr]
+.	if \n[doc-num-args] \
+.	doc-parse-space-vector
+.	doc-print-recursive
+..
+.	ec
+.	ds sP \s0
+.	ds tN \*[doc-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
+.		ie \\n(.$==0 \&MirBSD\\*(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
+.			as b1 \&MirBSD\ #\&\\*(A\\n(aP\\*(aa
 .			ie \\n(aC>\\n(aP \{\
 .				nr aP \\n(aP+1
 .				nR
@@ -140,7 +173,7 @@
 .			el .aZ
 .		\}
 .		el \{\
-.			as b1 \&MirOS\\*(aa
+.			as b1 \&MirBSD\\*(aa
 .			nR
 .		\}
 .	\}
@@ -304,9 +337,11 @@
 .Pp
 Talk to the
 .Mx
-development team using the mailing list at
-.Aq miros\-mksh@mirbsd.org
-or the
+development team and users using the mailing list at
+.Aq Mt miros\-mksh@mirbsd.org
+(please note the EU-DSGVO/GDPR notice on
+.Pa http://www.mirbsd.org/rss.htm#lists
+and in the SMTP banner!) or the
 .Li \&#\&!/bin/mksh
 .Pq or Li \&#ksh
 IRC channel at
diff --git a/src/main.c b/src/main.c
index ad20c94..41dd6da 100644
--- a/src/main.c
+++ b/src/main.c
@@ -5,7 +5,8 @@
 
 /*-
  * Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
- *		 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018
+ *		 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018,
+ *		 2019
  *	mirabilos <m@mirbsd.org>
  *
  * Provided that these terms and disclaimer and all copyright notices
@@ -34,7 +35,7 @@
 #include <locale.h>
 #endif
 
-__RCSID("$MirOS: src/bin/mksh/main.c,v 1.347 2018/01/13 21:45:07 tg Exp $");
+__RCSID("$MirOS: src/bin/mksh/main.c,v 1.351 2019/01/05 13:24:18 tg Exp $");
 
 #ifndef MKSHRC_PATH
 #define MKSHRC_PATH	"~/.mkshrc"
@@ -463,7 +464,7 @@
 	/* 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) */
+	/* record if monitor is set on command line (see j_init() in jobs.c) */
 #ifndef MKSH_UNEMPLOYED
 	Flag(FMONITOR) = 127;
 #endif
@@ -519,7 +520,7 @@
 #else
 		s->file = argv[argi++];
 #endif
-		s->u.shf = shf_open(s->file, O_RDONLY, 0,
+		s->u.shf = shf_open(s->file, O_RDONLY | O_MAYEXEC, 0,
 		    SHF_MAPHI | SHF_CLEXEC);
 		if (s->u.shf == NULL) {
 			shl_stdout_ok = false;
@@ -719,7 +720,7 @@
 	volatile int old_argc;
 	int i;
 
-	shf = shf_open(name, O_RDONLY, 0, SHF_MAPHI | SHF_CLEXEC);
+	shf = shf_open(name, O_RDONLY | O_MAYEXEC, 0, SHF_MAPHI | SHF_CLEXEC);
 	if (shf == NULL)
 		return (-1);
 
@@ -1965,7 +1966,7 @@
 	/* OSF/1 processes lnext when ~icanon */
 	cb.c_cc[VLNEXT] = _POSIX_VDISABLE;
 #endif
-	/* SunOS 4.1.x & OSF/1 processes discard(flush) when ~icanon */
+	/* SunOS 4.1.x and OSF/1 process discard(flush) when ~icanon */
 #if defined(VDISCARD) && defined(_POSIX_VDISABLE)
 	cb.c_cc[VDISCARD] = _POSIX_VDISABLE;
 #endif
@@ -2061,6 +2062,7 @@
 recheck_ctype(void)
 {
 	const char *ccp;
+	uint8_t old_utfmode = UTFMODE;
 
 	ccp = str_val(global("LC_ALL"));
 	if (ccp == null)
@@ -2078,7 +2080,7 @@
 		UTFMODE = 1;
 #endif
 
-	if (Flag(FPOSIX))
+	if (Flag(FPOSIX) && UTFMODE && !old_utfmode)
 		warningf(true, "early locale tracking enabled UTF-8 mode while in POSIX mode, you are now noncompliant");
 }
 #endif
diff --git a/src/misc.c b/src/misc.c
index e51dcb1..cddc516 100644
--- a/src/misc.c
+++ b/src/misc.c
@@ -32,7 +32,7 @@
 #include <grp.h>
 #endif
 
-__RCSID("$MirOS: src/bin/mksh/misc.c,v 1.291 2018/01/14 00:03:03 tg Exp $");
+__RCSID("$MirOS: src/bin/mksh/misc.c,v 1.293 2018/08/10 02:53:35 tg Exp $");
 
 #define KSH_CHVT_FLAG
 #ifdef MKSH_SMALL
@@ -1346,7 +1346,14 @@
 	const unsigned char *p = (const unsigned char *)s;
 	bool inquote = true;
 
-	/* first, check whether any quotes are needed */
+	/* first, special-case empty strings (for re-entrancy) */
+	if (!*s) {
+		shf_putc('\'', shf);
+		shf_putc('\'', shf);
+		return;
+	}
+
+	/* non-empty; check whether any quotes are needed */
 	while (rtt2asc(c = *p++) >= 32)
 		if (ctype(c, C_QUOTE | C_SPC))
 			inquote = false;
@@ -2449,7 +2456,7 @@
  * 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
+ * (0..0xFF), UCS (wc + 0x100), or -1 if no known
  * escape sequence was found
  */
 int
@@ -2531,9 +2538,9 @@
 		/**
 		 * x:	look for a hexadecimal number with up to
 		 *	two (C style: arbitrary) digits; convert
-		 *	to raw octet (C style: Unicode if >0xFF)
+		 *	to raw octet (C style: UCS if >0xFF)
 		 * u/U:	look for a hexadecimal number with up to
-		 *	four (U: eight) digits; convert to Unicode
+		 *	four (U: eight) digits; convert to UCS
 		 */
 		wc = 0;
 		n = 0;
@@ -2555,7 +2562,7 @@
 		if (!n)
 			goto unknown_escape;
 		if ((cstyle && wc > 0xFF) || fc != 'x')
-			/* Unicode marker */
+			/* UCS marker */
 			wc += 0x100;
 		break;
 	case '\'':
diff --git a/src/mksh.1 b/src/mksh.1
index aa67ac9..2e83428 100644
--- a/src/mksh.1
+++ b/src/mksh.1
@@ -1,8 +1,9 @@
-.\" $MirOS: src/bin/mksh/mksh.1,v 1.451 2017/08/16 21:40:14 tg Exp $
+.\" $MirOS: src/bin/mksh/mksh.1,v 1.463 2019/03/01 16:17:31 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
+.\"		2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017,
+.\"		2018, 2019
 .\"	mirabilos <m@mirbsd.org>
 .\"
 .\" Provided that these terms and disclaimer and all copyright notices
@@ -76,7 +77,7 @@
 .\" with -mandoc, it might implement .Mx itself, but we want to
 .\" use our own definition. And .Dd must come *first*, always.
 .\"
-.Dd $Mdocdate: August 16 2017 $
+.Dd $Mdocdate: March 1 2019 $
 .\"
 .\" Check which macro package we use, and do other -mdoc setup.
 .\"
@@ -84,6 +85,7 @@
 .	if \*[.T]utf8 .tr \[la]\*(Lt
 .	if \*[.T]utf8 .tr \[ra]\*(Gt
 .	ie d volume-ds-1 .ds tT gnu
+.	el .ie d doc-volume-ds-1 .ds tT gnp
 .	el .ds tT bsd
 .\}
 .el .ds tT ucb
@@ -96,7 +98,7 @@
 .	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]
+.	ds str-Mx1 \*[Tn-font-size]\%MirBSD\*[str-Mx]
 .	if !\n[arg-limit] \
 .	if \n[.$] \{\
 .	ds macro-name Mx
@@ -122,19 +124,51 @@
 .	ds sP \s0
 .	ds tN \*[Tn-font-size]
 .\}
+.el .ie "\*(tT"gnp" \{\
+.	eo
+.	de Mx
+.	nr doc-curr-font \n[.f]
+.	nr doc-curr-size \n[.ps]
+.	ds doc-str-Mx \f[\n[doc-curr-font]]\s[\n[doc-curr-size]u]
+.	ds doc-str-Mx1 \*[doc-Tn-font-size]\%MirBSD\*[doc-str-Mx]
+.	if !\n[doc-arg-limit] \
+.	if \n[.$] \{\
+.	ds doc-macro-name Mx
+.	doc-parse-args \$@
+.	\}
+.	if (\n[doc-arg-limit] > \n[doc-arg-ptr]) \{\
+.	nr doc-arg-ptr +1
+.	ie (\n[doc-type\n[doc-arg-ptr]] == 2) \
+.	as doc-str-Mx1 \~\*[doc-arg\n[doc-arg-ptr]]
+.	el \
+.	nr doc-arg-ptr -1
+.	\}
+.	ds doc-arg\n[doc-arg-ptr] "\*[doc-str-Mx1]
+.	nr doc-type\n[doc-arg-ptr] 2
+.	ds doc-space\n[doc-arg-ptr] "\*[doc-space]
+.	nr doc-num-args (\n[doc-arg-limit] - \n[doc-arg-ptr])
+.	nr doc-arg-limit \n[doc-arg-ptr]
+.	if \n[doc-num-args] \
+.	doc-parse-space-vector
+.	doc-print-recursive
+..
+.	ec
+.	ds sP \s0
+.	ds tN \*[doc-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
+.		ie \\n(.$==0 \&MirBSD\\*(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
+.			as b1 \&MirBSD\ #\&\\*(A\\n(aP\\*(aa
 .			ie \\n(aC>\\n(aP \{\
 .				nr aP \\n(aP+1
 .				nR
@@ -142,7 +176,7 @@
 .			el .aZ
 .		\}
 .		el \{\
-.			as b1 \&MirOS\\*(aa
+.			as b1 \&MirBSD\\*(aa
 .			nR
 .		\}
 .	\}
@@ -1046,7 +1080,7 @@
 .Dq Li \eu#### ,
 .Dq #
 means a hexadecimal digit, of which there may be none up to four or eight;
-these escapes translate a Unicode codepoint to UTF-8.
+these escapes translate a Universal Coded Character Set codepoint to UTF-8.
 Furthermore,
 .Dq Li \eE
 and
@@ -1082,7 +1116,7 @@
 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.
+these translate a Universal Coded Character Set codepoint to UTF-8.
 The sequence
 .Dq Li \ec# ,
 where
@@ -1364,6 +1398,10 @@
 where
 .Ar name
 is a parameter name.
+Substitutions of an an array in scalar context, i.e. without an
+.Ar expr
+in the latter form mentioned above, expand the element with the key
+.Dq 0 .
 Substitution of all array elements with
 .Pf ${ Ns Ar name Ns \&[*]}
 and
@@ -1519,11 +1557,7 @@
 .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, the element with the key
-.Dq 0
-will be substituted in scalar context):
+The following forms of parameter substitution can also be used:
 .Pp
 .Bl -tag -width Ds -compact
 .It Pf ${# Ns Ar name Ns \&}
@@ -1823,10 +1857,10 @@
 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
+If never unset and not imported, always set dynamically;
+unless the value as reported by
 .Xr stty 1
-is non-zero and sane enough (minimum is 12x3); similar for
+is non-zero and sane enough (minimum is 12x3), defaults to 80; similar for
 .Ev LINES .
 This parameter is used by the interactive line editing modes and by the
 .Ic select ,
@@ -1995,7 +2029,7 @@
 executed.
 .It Ev LINES
 Set to the number of lines on the terminal or window.
-Always set, defaults to 24.
+Defaults to 24; always set, unless imported or unset.
 See
 .Ev COLUMNS .
 .It Ev OLDPWD
@@ -2651,7 +2685,8 @@
 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
+transparent) ASCII or Universal Coded Character Set codepoints,
+depending on the shell's
 .Ic utf8\-mode
 flag (current setting).
 The
@@ -2664,7 +2699,7 @@
 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
+In UTF-8 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
@@ -2903,8 +2938,6 @@
 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.
@@ -4667,15 +4700,15 @@
 (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"
+.Dl "0m0.03s real     0m0.02s user     0m0.01s 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
+real     0.03
+user     0.02
+sys      0.01
 .Ed
 .Pp
 It is an error to specify the
@@ -4700,8 +4733,8 @@
 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
+0m0.01s 0m0.00s
+0m0.04s 0m0.02s
 .Ed
 .Pp
 .It Ic trap Ar n Op Ar signal ...
@@ -4932,9 +4965,9 @@
 .Fl i
 option which meant upper case letters would never be used for bases greater
 than 10.
-See the
+See
 .Fl U
-option.)
+above.)
 .Pp
 For functions,
 .Fl u
@@ -4944,9 +4977,9 @@
 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.
+Parameters are placed in the environment of any executed commands.
+Functions cannot be exported for security reasons
+.Pq Dq shellshock .
 .It Fl Z Ns Op Ar n
 Zero fill attribute.
 If not combined with
@@ -4954,7 +4987,7 @@
 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.
+For integers, the number is padded, not the base.
 .El
 .Pp
 If any of the
@@ -5211,14 +5244,12 @@
 .Ic set Fl o Ic 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
+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 commands
 .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.
+.Ic bg .
 .Pp
 Note that only commands that create processes (e.g. asynchronous commands,
 subshell commands and non-built-in, non-function commands) can be stopped;
@@ -5332,6 +5363,17 @@
 is immediately made to exit the shell, the running jobs are sent a
 .Dv SIGHUP
 signal and the shell exits.
+.Ss Terminal state
+The state of the controlling terminal can be modified by a command
+executed in the foreground, whether or not job control is enabled, but
+the modified terminal state is only kept past the job's lifetime and used
+for later command invocations if the command exits successfully (i.e.\&
+with an exit status of 0).
+When such a job is momentarily stopped or restarted, the terminal state
+is saved and restored, respectively, but it will not be kept afterwards.
+In interactive mode, when line editing is enabled, the terminal state is
+saved before being reconfigured by the shell for the line editor, then
+restored before running a command.
 .Ss POSIX mode
 Entering
 .Ic set Fl o Ic posix
@@ -6650,7 +6692,7 @@
 .Ic utf8\-mode
 .Em must
 be disabled in POSIX mode, and it
-only supports the Unicode BMP (Basic Multilingual Plane) and maps
+only supports the BMP (Basic Multilingual Plane) of UCS and maps
 raw octets into the U+EF80..U+EFFF wide character range; compare
 .Sx Arithmetic expressions .
 The following
@@ -6671,7 +6713,24 @@
 	esac ;;
 esac
 .Ed
-In near future, (Unicode) locale tracking will be implemented though.
+In near future, (UTF-8) locale tracking will be implemented though.
+.Pp
+Using
+.Ic set Fl o Ic pipefail
+makes the following construct error out:
+.Bd -literal -offset indent
+set -e
+for x in 1 2; do
+	false && echo $x
+done \*(Ba cat
+.Ed
+This is because, while the
+.Dq Li &&\&
+ensures that the inner command's failure is not taken, it sets
+the entire for..done loop's errorlevel, which is passed on by
+.Fl o Ic pipefail .
+Invert the inner command:
+.Li true \*(Ba\*(Ba echo $x
 .Pp
 See also the FAQ below.
 .Sh BUGS
@@ -6697,7 +6756,7 @@
 .Xr memmove 3 .
 .Pp
 This document attempts to describe
-.Nm mksh\ R56
+.Nm mksh\ R57
 and up,
 .\" with vendor patches from insert-your-name-here,
 compiled without any options impacting functionality, such as
@@ -6713,10 +6772,11 @@
 .Pp
 Please report bugs in
 .Nm
-to the
+to the public development mailing list at
 .Aq Mt miros\-mksh@mirbsd.org
-mailing list
-or in the
+(please note the EU-DSGVO/GDPR notice on
+.Pa http://www.mirbsd.org/rss.htm#lists
+and in the SMTP banner!) or in the
 .Li \&#\&!/bin/mksh
 .Pq or Li \&#ksh
 IRC channel at
@@ -6782,6 +6842,10 @@
 instead of
 .Nm mksh ) :
 .Dl set \-o posix
+.Ss "I forbid stat(2) in my SELinux policy, and some things do not work!"
+Don't break Unix.
+Read up on the GIGO principle.
+Duh.
 .Ss "My prompt from <some other shell> does not work!"
 Contact us on the mailing list or on IRC, we'll convert it for you.
 .Ss "Something is going wrong with my while...read loop"
diff --git a/src/sh.h b/src/sh.h
index 53629b1..a974bbd 100644
--- a/src/sh.h
+++ b/src/sh.h
@@ -182,9 +182,9 @@
 #endif
 
 #ifdef EXTERN
-__RCSID("$MirOS: src/bin/mksh/sh.h,v 1.858 2018/01/14 01:47:36 tg Exp $");
+__RCSID("$MirOS: src/bin/mksh/sh.h,v 1.870 2019/03/01 16:18:14 tg Exp $");
 #endif
-#define MKSH_VERSION "R56 2018/01/14"
+#define MKSH_VERSION "R57 2019/03/01"
 
 /* arithmetic types: C implementation */
 #if !HAVE_CAN_INTTYPES
@@ -491,6 +491,10 @@
 #define O_BINARY	0
 #endif
 
+#ifndef O_MAYEXEC
+#define O_MAYEXEC	0
+#endif
+
 #ifdef MKSH__NO_SYMLINK
 #undef S_ISLNK
 #define S_ISLNK(m)	(/* CONSTCOND */ 0)
@@ -643,7 +647,7 @@
 #endif
 #endif
 
-#if (!defined(MKSH_BUILDMAKEFILE4BSD) && !defined(MKSH_BUILDSH)) || (MKSH_BUILD_R != 563)
+#if (!defined(MKSH_BUILDMAKEFILE4BSD) && !defined(MKSH_BUILDSH)) || (MKSH_BUILD_R != 571)
 #error Must run Build.sh to compile this.
 extern void thiswillneverbedefinedIhope(void);
 int
@@ -783,7 +787,7 @@
 };
 
 #define Flag(f)	(shell_flags[(int)(f)])
-#define UTFMODE	Flag(FUNICODE)
+#define UTFMODE	Flag(FUNNYCODE)
 
 /*
  * parsing & execution environment
@@ -1950,10 +1954,11 @@
 #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 (<<) */
+#define IODUPSELF	BIT(8)	/* x>&x (as opposed to x>&y) */
+#define IONAMEXP	BIT(9)	/* name has been expanded */
+#define IOBASH		BIT(10)	/* &> etc. */
+#define IOHERESTR	BIT(11)	/* <<< (here string) */
+#define IONDELIM	BIT(12)	/* null delimiter (<<) */
 
 /* execute/exchild flags */
 #define XEXEC	BIT(0)		/* execute without forking */
@@ -2637,7 +2642,7 @@
 #define WDS_TPUTS	BIT(0)		/* tputS (dumpwdvar) mode */
 char *wdstrip(const char *, int);
 void tfree(struct op *, Area *);
-void dumpchar(struct shf *, int);
+void dumpchar(struct shf *, unsigned char);
 void dumptree(struct shf *, struct op *);
 void dumpwdvar(struct shf *, const char *);
 void dumpioact(struct shf *shf, struct op *t);
diff --git a/src/sh_flags.gen b/src/sh_flags.gen
index 24b3359..af44328 100644
--- a/src/sh_flags.gen
+++ b/src/sh_flags.gen
@@ -21,7 +21,7 @@
 
 #ifndef SHFLAGS_OPTCS
 #if defined(SHFLAGS_DEFNS)
-__RCSID("$MirOS: src/bin/mksh/sh_flags.opt,v 1.5 2017/02/18 02:33:15 tg Exp $");
+__RCSID("$MirOS: src/bin/mksh/sh_flags.opt,v 1.6 2018/08/10 02:53:39 tg Exp $");
 #elif defined(SHFLAGS_ENUMS)
 #define FN(sname,cname,flags,ochar)	cname,
 #define F0(sname,cname,flags,ochar)	cname = 0,
@@ -77,7 +77,7 @@
 FN("stdin", FSTDIN, OF_CMDLINE, 's')
 #endif
 FN("trackall", FTRACKALL, OF_ANY, 'h')
-FN("utf8-mode", FUNICODE, OF_ANY, 'U')
+FN("utf8-mode", FUNNYCODE, OF_ANY, 'U')
 FN("verbose", FVERBOSE, OF_ANY, 'v')
 #ifndef MKSH_NO_CMDLINE_EDITING
 FN("vi", FVI, OF_ANY, 0)
diff --git a/src/sh_flags.opt b/src/sh_flags.opt
index 795d198..c2f554e 100644
--- a/src/sh_flags.opt
+++ b/src/sh_flags.opt
@@ -19,7 +19,7 @@
  */
 
 @SHFLAGS_DEFNS
-__RCSID("$MirOS: src/bin/mksh/sh_flags.opt,v 1.5 2017/02/18 02:33:15 tg Exp $");
+__RCSID("$MirOS: src/bin/mksh/sh_flags.opt,v 1.6 2018/08/10 02:53:39 tg Exp $");
 @SHFLAGS_ENUMS
 #define FN(sname,cname,flags,ochar)	cname,
 #define F0(sname,cname,flags,ochar)	cname = 0,
@@ -153,7 +153,7 @@
 
 /* -U	enable UTF-8 processing (non-standard) */
 >U|
-FN("utf8-mode", FUNICODE, OF_ANY
+FN("utf8-mode", FUNNYCODE, OF_ANY
 
 /* -v	echo input */
 >v|
diff --git a/src/shf.c b/src/shf.c
index 2ee0ec1..a37f4da 100644
--- a/src/shf.c
+++ b/src/shf.c
@@ -27,7 +27,7 @@
 
 #include "sh.h"
 
-__RCSID("$MirOS: src/bin/mksh/shf.c,v 1.97 2018/01/14 01:28:16 tg Exp $");
+__RCSID("$MirOS: src/bin/mksh/shf.c,v 1.98 2018/08/10 02:53:39 tg Exp $");
 
 /* flags to shf_emptybuf() */
 #define EB_READSW	0x01	/* about to switch to reading */
@@ -1304,7 +1304,7 @@
 		 * and the C1 control characters other than NEL are
 		 * hopeless, but we map EBCDIC NEL to ASCII LF so we
 		 * cannot even use C1 NEL.
-		 * If ever we map to Unicode, bump the table width to
+		 * If ever we map to UCS, bump the table width to
 		 * an unsigned int, and or the raw unconverted EBCDIC
 		 * values with 0x01000000 instead.
 		 */
diff --git a/src/tree.c b/src/tree.c
index 5e7326b..335e3fb 100644
--- a/src/tree.c
+++ b/src/tree.c
@@ -23,7 +23,7 @@
 
 #include "sh.h"
 
-__RCSID("$MirOS: src/bin/mksh/tree.c,v 1.95 2018/01/14 00:03:05 tg Exp $");
+__RCSID("$MirOS: src/bin/mksh/tree.c,v 1.97 2018/10/20 18:46:00 tg Exp $");
 
 #define INDENT	8
 
@@ -808,7 +808,7 @@
 		c = ksh_unctrl(c);
 	} else if (UTFMODE && rtt2asc(c) > 0x7F) {
 		/* better not try to display broken multibyte chars */
-		/* also go easy on the Unicode: no U+FFFD here */
+		/* also go easy on the UCS: no U+FFFD here */
 		c = ORD('?');
 	}
 	*dst++ = c;
@@ -821,7 +821,7 @@
 
 #ifdef DEBUG
 void
-dumpchar(struct shf *shf, int c)
+dumpchar(struct shf *shf, unsigned char c)
 {
 	if (ksh_isctrl(c)) {
 		/* C0 or C1 control character or DEL */
diff --git a/src/var.c b/src/var.c
index 5219507..1263547 100644
--- a/src/var.c
+++ b/src/var.c
@@ -28,7 +28,7 @@
 #include <sys/sysctl.h>
 #endif
 
-__RCSID("$MirOS: src/bin/mksh/var.c,v 1.223 2018/01/13 23:55:15 tg Exp $");
+__RCSID("$MirOS: src/bin/mksh/var.c,v 1.226 2018/07/15 17:21:24 tg Exp $");
 
 /*-
  * Variables
@@ -54,7 +54,7 @@
 static void unspecial(const char *);
 static void getspec(struct tbl *);
 static void setspec(struct tbl *);
-static void unsetspec(struct tbl *);
+static void unsetspec(struct tbl *, bool);
 static int getint(struct tbl *, mksh_ari_u *, bool);
 static const char *array_index_calc(const char *, bool *, uint32_t *);
 
@@ -105,7 +105,7 @@
 			if ((vq = global(vp->name))->flag & ISSET)
 				setspec(vq);
 			else
-				unsetspec(vq);
+				unsetspec(vq, false);
 		}
 	if (l->flags & BF_DOGETOPTS)
 		user_opt = l->getopts_state;
@@ -707,7 +707,7 @@
 			if (vp->flag & ZEROFIL)
 				while (*s == '0')
 					s++;
-			shf_snprintf(p, nlen + 1, "%-*.*s",
+			shf_snprintf(p, psiz, "%-*.*s",
 				vp->u2.field, vp->u2.field, s);
 		}
 	} else
@@ -1054,7 +1054,7 @@
 	vp->flag &= SPECIAL | ((flags & 1) ? 0 : ARRAY|DEFINED);
 	if (vp->flag & SPECIAL)
 		/* responsible for 'unspecial'ing var */
-		unsetspec(vp);
+		unsetspec(vp, true);
 }
 
 /*
@@ -1441,7 +1441,7 @@
 }
 
 static void
-unsetspec(struct tbl *vp)
+unsetspec(struct tbl *vp, bool dounset)
 {
 	/*
 	 * AT&T ksh man page says OPTIND, OPTARG and _ lose special
@@ -1466,13 +1466,13 @@
 #endif
 	case V_IFS:
 		set_ifs(TC_IFSWS);
-		break;
+		return;
 	case V_PATH:
 		afree(path, APERM);
 		strdupx(path, def_path, APERM);
 		/* clear tracked aliases */
 		flushcom(true);
-		break;
+		return;
 #ifndef MKSH_NO_CMDLINE_EDITING
 	case V_TERM:
 		x_initterm(null);
@@ -1484,14 +1484,14 @@
 			afree(tmpdir, APERM);
 			tmpdir = NULL;
 		}
-		break;
+		return;
 	case V_LINENO:
 	case V_RANDOM:
 	case V_SECONDS:
 	case V_TMOUT:
 		/* AT&T ksh leaves previous value in place */
 		unspecial(vp->name);
-		break;
+		return;
 #ifdef MKSH_EARLY_LOCALE_TRACKING
 	case V_LANG:
 	case V_LC_ALL:
@@ -1499,6 +1499,12 @@
 		recheck_ctype();
 		return;
 #endif
+	/* should not become unspecial, but allow unsetting */
+	case V_COLUMNS:
+	case V_LINES:
+		if (dounset)
+			unspecial(vp->name);
+		return;
 	}
 }
 
@@ -1617,6 +1623,9 @@
 		unset(vp, 1);
 		/* allocate-by-access the [0] element to keep in scope */
 		arraysearch(vp, 0);
+		/* honour set -o allexport */
+		if (Flag(FEXPORT))
+			typeset(ccp, EXPORT, 0, 0, 0);
 	}
 	/*
 	 * TODO: would be nice for assignment to completely succeed or