Index: src/bin/mksh/check.t
diff -u src/bin/mksh/check.t:1.661 src/bin/mksh/check.t:1.667.2.1
--- src/bin/mksh/check.t:1.661	Tue Oct  7 15:22:14 2014
+++ src/bin/mksh/check.t	Sun Jan 11 22:39:40 2015
@@ -1,4 +1,4 @@
-# $MirOS: src/bin/mksh/check.t,v 1.661 2014/10/07 15:22:14 tg Exp $
+# $MirOS: src/bin/mksh/check.t,v 1.667.2.1 2015/01/11 22:39:40 tg Exp $
 # OpenBSD src/regress/bin/ksh updated: 2013/12/02 20:39:44
 #-
 # Copyright © 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
@@ -24,10 +24,10 @@
 # http://www.research.att.com/~gsf/public/ifs.sh
 #
 # More testsuites at:
-# http://www.freebsd.org/cgi/cvsweb.cgi/src/tools/regression/bin/test/regress.sh?rev=HEAD
+# http://svnweb.freebsd.org/base/head/bin/test/tests/legacy_test.sh?view=co&content-type=text%2Fplain
 
 expected-stdout:
-	@(#)MIRBSD KSH R50 2014/10/07
+	@(#)MIRBSD KSH R50 2015/01/11
 description:
 	Check version of shell.
 stdin:
@@ -36,7 +36,7 @@
 category: shell:legacy-no
 ---
 expected-stdout:
-	@(#)LEGACY KSH R50 2014/10/07
+	@(#)LEGACY KSH R50 2015/01/11
 description:
 	Check version of legacy shell.
 stdin:
@@ -2359,6 +2359,14 @@
 expected-stdout:
 	baz
 ---
+name: heredoc-9f
+description:
+	Check long here strings
+stdin:
+	cat <<< "$(  :                                                             )aa"
+expected-stdout:
+	aa
+---
 name: heredoc-10
 description:
 	Check direct here document assignment
@@ -2389,6 +2397,14 @@
 	vf=<<<$'=f $x \x40='
 	# now check
 	print -r -- "| va={$va} vb={$vb} vc={$vc} vd={$vd} ve={$ve} vf={$vf} |"
+	# check append
+	v=<<-EOF
+		vapp1
+	EOF
+	v+=<<-EOF
+		vapp2
+	EOF
+	print -r -- "| ${v//$'\n'/^} |"
 expected-stdout:
 	function foo {
 		vc=<<-EOF
@@ -2404,6 +2420,7 @@
 	} ve={=e $x \x40=
 	} vf={=f $x @=
 	} |
+	| vapp1^vapp2^ |
 ---
 name: heredoc-11
 description:
@@ -2433,6 +2450,14 @@
 	eval "$fnd"
 	foo
 	print -r -- "| va={$va} vb={$vb} vc={$vc} vd={$vd} |"
+	# check append
+	v=<<-
+		vapp1
+	<<
+	v+=<<-''
+		vapp2
+	
+	print -r -- "| ${v//$'\n'/^} |"
 expected-stdout:
 	function foo {
 		vc=<<-
@@ -2450,6 +2475,7 @@
 	} vc={=c u \x40=
 	} vd={=d $x \x40=
 	} |
+	| vapp1^vapp2^ |
 ---
 name: heredoc-comsub-1
 description:
@@ -3733,7 +3759,7 @@
 expected-stdout:
 	 <1> <shift> <1> <2>
 ---
-name: IFS-subst-3
+name: IFS-subst-3-arr
 description:
 	Check leading IFS non-whitespace after trim does make a field
 	but leading IFS whitespace does not, nor empty replacements
@@ -3749,6 +3775,32 @@
 	 <1> <> <foo> <bar>
 	 <2> <foo> <bar>
 ---
+name: IFS-subst-3-ass
+description:
+	Check non-field semantics
+stdin:
+	showargs() { for i; do echo -n " <$i>"; done; echo; }
+	showargs 0 x=${-+}
+	IFS=:
+	showargs 1 x=${-+:foo:bar}
+	IFS=' '
+	showargs 2 x=${-+ foo bar}
+expected-stdout:
+	 <0> <x=>
+	 <1> <x=> <foo> <bar>
+	 <2> <x=> <foo> <bar>
+---
+name: IFS-subst-3-lcl
+description:
+	Check non-field semantics, smaller corner case (LP#1381965)
+stdin:
+	set -x
+	local regex=${2:-}
+	exit 1
+expected-exit: e != 0
+expected-stderr-pattern:
+	/regex=/
+---
 name: IFS-subst-4-1
 description:
 	reported by mikeserv
@@ -4026,6 +4078,7 @@
 	'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
 stdin:
 	"$__progname" -c 'IFS=; set -- "" 2 ""; printf "[%s]\n" $*; x=$*; printf "<%s>\n" "$x"'
 	echo '=a1zns'
@@ -4133,6 +4186,39 @@
 	<A B   C>
 	=b8iqa
 ---
+name: IFS-subst-6
+description:
+	Regression wrt. vector expansion in trim
+stdin:
+	showargs() { for x in "$@"; do echo -n "<$x> "; done; echo .; }
+	IFS=
+	x=abc
+	set -- a b
+	showargs ${x#$*}
+expected-stdout:
+	<c> .
+---
+name: IFS-subst-7
+description:
+	ksh93 bug wrt. vector expansion in trim
+stdin:
+	showargs() { for x in "$@"; do echo -n "<$x> "; done; echo .; }
+	IFS="*"
+	a=abcd
+	set -- '' c
+	showargs "$*" ${a##"$*"}
+expected-stdout:
+	<*c> <abcd> .
+---
+name: IFS-subst-8
+description:
+	http://austingroupbugs.net/view.php?id=221
+stdin:
+	n() { echo "$#"; }; n "${foo-$@}"
+expected-fail: yes
+expected-stdout:
+	1
+---
 name: IFS-arith-1
 description:
 	http://austingroupbugs.net/view.php?id=832
@@ -7063,6 +7149,7 @@
 	XXX if the OS can already execute them, we lose
 	note: cygwin execve(2) doesn't return to us with ENOEXEC, we lose
 	note: Ultrix perl5 t4 returns 65280 (exit-code 255) and no text
+	XXX fails when LD_PRELOAD is set with -e and Perl chokes it (ASan)
 need-pass: no
 category: !os:cygwin,!os:msys,!os:ultrix,!os:uwin-nt,!smksh
 env-setup: !FOO=BAR!
@@ -8013,6 +8100,66 @@
 	.fnr:f:
 	.f2r:f:
 ---
+name: unset-fnc-local-ksh
+description:
+	Check that “unset” removes a previous “local”
+	(ksh93 syntax compatible version); apparently,
+	there are shells which fail this?
+stdin:
+	function f {
+		echo f0: $x
+		typeset x
+		echo f1: $x
+		x=fa
+		echo f2: $x
+		unset x
+		echo f3: $x
+		x=fb
+		echo f4: $x
+	}
+	x=o
+	echo before: $x
+	f
+	echo after: $x
+expected-stdout:
+	before: o
+	f0: o
+	f1:
+	f2: fa
+	f3: o
+	f4: fb
+	after: fb
+---
+name: unset-fnc-local-sh
+description:
+	Check that “unset” removes a previous “local”
+	(Debian Policy §10.4 sh version); apparently,
+	there are shells which fail this?
+stdin:
+	f() {
+		echo f0: $x
+		local x
+		echo f1: $x
+		x=fa
+		echo f2: $x
+		unset x
+		echo f3: $x
+		x=fb
+		echo f4: $x
+	}
+	x=o
+	echo before: $x
+	f
+	echo after: $x
+expected-stdout:
+	before: o
+	f0: o
+	f1:
+	f2: fa
+	f3: o
+	f4: fb
+	after: fb
+---
 name: varexpand-substr-1
 description:
 	Check if bash-style substring expansion works
@@ -8194,13 +8341,14 @@
 name: varexpand-null-3
 description:
 	Ensure concatenating behaviour matches other shells
-	although the line 2<> is probably wrong? XNULLSUB case.
 stdin:
-	x=; printf "1<%s>\n" "$x$@"
-	set A; printf "2<%s>\n" "${@:+}"
+	showargs() { for i; do echo -n " <$i>"; done; echo; }
+	x=; showargs 1 "$x"$@
+	set A; showargs 2 "${@:+}"
+expected-fail: yes
 expected-stdout:
-	1<>
-	2<>
+	 <1> <>
+	 <2> <>
 ---
 name: print-funny-chars
 description:
@@ -10921,6 +11069,7 @@
 	(mypid=$$; try mypid)
 	echo =15
 	) 2>&1 | sed -e 's/^[^]]*]//' -e 's/^[^:]*: *//'
+	exit ${PIPESTATUS[0]}
 expected-stdout:
 	y
 	=1
Index: src/bin/mksh/dot.mkshrc
diff -u src/bin/mksh/dot.mkshrc:1.89 src/bin/mksh/dot.mkshrc:1.89.2.1
--- src/bin/mksh/dot.mkshrc:1.89	Mon Jul 28 21:45:44 2014
+++ src/bin/mksh/dot.mkshrc	Sun Jan 11 22:39:44 2015
@@ -1,8 +1,8 @@
 # $Id$
-# $MirOS: src/bin/mksh/dot.mkshrc,v 1.89 2014/07/28 21:45:44 tg Exp $
+# $MirOS: src/bin/mksh/dot.mkshrc,v 1.89.2.1 2015/01/11 22:39:44 tg Exp $
 #-
 # Copyright (c) 2002, 2003, 2004, 2006, 2007, 2008, 2009, 2010,
-#		2011, 2012, 2013, 2014
+#		2011, 2012, 2013, 2014, 2015
 #	Thorsten Glaser <tg@mirbsd.org>
 #
 # Provided that these terms and disclaimer and all copyright notices
@@ -67,7 +67,7 @@
 		local dasc line i
 
 		cat "$@" | { set +U; if read -arN -1 line; then
-			typeset -i1 line
+			typeset -i1 'line[*]'
 			i=0
 			while (( i < ${#line[*]} )); do
 				hv=${line[i++]}
@@ -263,9 +263,8 @@
 
 # base64 encoder and decoder, RFC compliant, NUL safe
 function Lb64decode {
-	[[ -o utf8-mode ]]; local u=$?
+	[[ -o utf8-mode ]]; local u=$? c s="$*" t
 	set +U
-	local c s="$*" t=
 	[[ -n $s ]] || { s=$(cat; print x); s=${s%x}; }
 	local -i i=0 j=0 n=${#s} p=0 v x
 	local -i16 o
@@ -302,9 +301,8 @@
 set -A Lb64encode_code -- A B C D E F G H I J K L M N O P Q R S T U V W X Y Z \
     a b c d e f g h i j k l m n o p q r s t u v w x y z 0 1 2 3 4 5 6 7 8 9 + /
 function Lb64encode {
-	[[ -o utf8-mode ]]; local u=$?
+	[[ -o utf8-mode ]]; local u=$? c s t
 	set +U
-	local c s t
 	if (( $# )); then
 		read -raN-1 s <<<"$*"
 		unset s[${#s[*]}-1]
@@ -342,9 +340,8 @@
 	Lbafh_v=0
 }
 function Lbafh_add {
-	[[ -o utf8-mode ]]; local u=$?
+	[[ -o utf8-mode ]]; local u=$? s
 	set +U
-	local s
 	if (( $# )); then
 		read -raN-1 s <<<"$*"
 		unset s[${#s[*]}-1]
Index: src/bin/mksh/edit.c
diff -u src/bin/mksh/edit.c:1.276 src/bin/mksh/edit.c:1.276.2.1
--- src/bin/mksh/edit.c:1.276	Sun Jul 13 11:34:28 2014
+++ src/bin/mksh/edit.c	Sun Jan 11 22:39:45 2015
@@ -28,7 +28,7 @@
 
 #ifndef MKSH_NO_CMDLINE_EDITING
 
-__RCSID("$MirOS: src/bin/mksh/edit.c,v 1.276 2014/07/13 11:34:28 tg Exp $");
+__RCSID("$MirOS: src/bin/mksh/edit.c,v 1.276.2.1 2015/01/11 22:39:45 tg Exp $");
 
 /*
  * in later versions we might use libtermcap for this, but since external
@@ -1009,7 +1009,6 @@
 static const struct x_ftab x_ftab[] = {
 #define EMACSFN_ITEMS
 #include "emacsfn.h"
-	{ 0, NULL, 0 }
 };
 
 static struct x_defbindings const x_defbindings[] = {
@@ -2455,8 +2454,7 @@
 	/* List function names */
 	if (list) {
 		for (f = 0; f < NELEM(x_ftab); f++)
-			if (x_ftab[f].xf_name &&
-			    !(x_ftab[f].xf_flags & XF_NOBIND))
+			if (!(x_ftab[f].xf_flags & XF_NOBIND))
 				shprintf("%s\n", x_ftab[f].xf_name);
 		return (0);
 	}
@@ -2517,8 +2515,7 @@
 #endif
 	} else {
 		for (f = 0; f < NELEM(x_ftab); f++)
-			if (x_ftab[f].xf_name &&
-			    strcmp(x_ftab[f].xf_name, a2) == 0)
+			if (!strcmp(x_ftab[f].xf_name, a2))
 				break;
 		if (f == NELEM(x_ftab) || x_ftab[f].xf_flags & XF_NOBIND) {
 			bi_errorf("%s: %s %s", a2, "no such", Tfunction);
Index: src/bin/mksh/eval.c
diff -u src/bin/mksh/eval.c:1.153 src/bin/mksh/eval.c:1.158.2.1
--- src/bin/mksh/eval.c:1.153	Tue Oct  7 15:22:16 2014
+++ src/bin/mksh/eval.c	Sun Jan 11 22:39:47 2015
@@ -23,7 +23,7 @@
 
 #include "sh.h"
 
-__RCSID("$MirOS: src/bin/mksh/eval.c,v 1.153 2014/10/07 15:22:16 tg Exp $");
+__RCSID("$MirOS: src/bin/mksh/eval.c,v 1.158.2.1 2015/01/11 22:39:47 tg Exp $");
 
 /*
  * string expansion
@@ -237,7 +237,7 @@
 	/* record number of trailing newlines in COMSUB */
 	int newlines = 0;
 	bool saw_eq, make_magic;
-	int tilde_ok;
+	unsigned int tilde_ok;
 	size_t len;
 	char *cp;
 
@@ -246,7 +246,7 @@
 	/* for alias, readonly, set, typeset commands */
 	if ((f & DOVACHECK) && is_wdvarassign(ccp)) {
 		f &= ~(DOVACHECK | DOBLANK | DOGLOB | DOTILDE);
-		f |= DOASNTILDE | DOASNFIELD;
+		f |= DOASNTILDE | DOSCALAR;
 	}
 	if (Flag(FNOGLOB))
 		f &= ~DOGLOB;
@@ -622,7 +622,7 @@
 					case '%':
 						/* ! DOBLANK,DOBRACE,DOTILDE */
 						f = (f & DONTRUNCOMMAND) |
-						    DOPAT | DOTEMP;
+						    DOPAT | DOTEMP | DOSCALAR;
 						st->quotew = quote = 0;
 						/*
 						 * Prepend open pattern (so |
@@ -631,7 +631,7 @@
 						 */
 						if (!Flag(FSH)) {
 							*dp++ = MAGIC;
-							*dp++ = '@' | 0x80;
+							*dp++ = 0x80 | '@';
 						}
 						break;
 					case '=':
@@ -664,7 +664,11 @@
 						f |= DOTEMP;
 						/* FALLTHROUGH */
 					default:
-						word = quote ? IFS_WORD : IFS_IWS;
+						/* '-' '+' '?' */
+						if (quote)
+							word = IFS_WORD;
+						else if (dp == Xstring(ds, dp))
+							word = IFS_IWS;
 						/* Enable tilde expansion */
 						tilde_ok = 1;
 						f |= DOTILDE;
@@ -704,7 +708,7 @@
 					x.str = trimsub(str_val(st->var),
 						dp, st->stype);
 					if (x.str[0] != '\0') {
-						word = IFS_WS;
+						word = IFS_IWS;
 						type = XSUB;
 					} else
 						type = quote ? XSUB : XNULLSUB;
@@ -743,7 +747,7 @@
 					if (f & DOBLANK)
 						doblank++;
 					st = st->prev;
-					word = quote || (!*x.str && (f & DOASNFIELD)) ? IFS_WORD : IFS_WS;
+					word = quote || (!*x.str && (f & DOSCALAR)) ? IFS_WORD : IFS_IWS;
 					continue;
 				case '?': {
 					char *s = Xrestpos(ds, dp, st->base);
@@ -759,11 +763,12 @@
 				case 0x100 | 'Q':
 					dp = Xrestpos(ds, dp, st->base);
 					type = XSUB;
-					word = quote || (!*x.str && (f & DOASNFIELD)) ? IFS_WORD : IFS_WS;
+					word = quote || (!*x.str && (f & DOSCALAR)) ? IFS_WORD : IFS_IWS;
 					if (f & DOBLANK)
 						doblank++;
 					st = st->prev;
 					continue;
+				/* default: '-' '+' */
 				}
 				st = st->prev;
 				type = XBASE;
@@ -800,7 +805,7 @@
 			if (f & DOBLANK) {
 				doblank--;
 				if (dp == Xstring(ds, dp))
-					word = IFS_WS;
+					word = IFS_IWS;
 			}
 			continue;
 
@@ -834,8 +839,8 @@
 					continue;
 				}
 				c = ifs0;
-				if ((f & DOASNFIELD)) {
-					/* assignment, do not field-split */
+				if ((f & DOSCALAR)) {
+					/* do not field-split */
 					if (x.split) {
 						c = ' ';
 						break;
Index: src/bin/mksh/exec.c
diff -u src/bin/mksh/exec.c:1.133 src/bin/mksh/exec.c:1.137.2.1
--- src/bin/mksh/exec.c:1.133	Fri Oct  3 17:32:11 2014
+++ src/bin/mksh/exec.c	Sun Jan 11 22:39:48 2015
@@ -23,7 +23,7 @@
 
 #include "sh.h"
 
-__RCSID("$MirOS: src/bin/mksh/exec.c,v 1.133 2014/10/03 17:32:11 tg Exp $");
+__RCSID("$MirOS: src/bin/mksh/exec.c,v 1.137.2.1 2015/01/11 22:39:48 tg Exp $");
 
 #ifndef MKSH_DEFAULT_EXECSHELL
 #define MKSH_DEFAULT_EXECSHELL	"/bin/sh"
@@ -32,7 +32,7 @@
 static int comexec(struct op *, struct tbl * volatile, const char **,
     int volatile, volatile int *);
 static void scriptexec(struct op *, const char **) MKSH_A_NORETURN;
-static int call_builtin(struct tbl *, const char **, const char *);
+static int call_builtin(struct tbl *, const char **, const char *, bool);
 static int iosetup(struct ioword *, struct tbl *);
 static int herein(struct ioword *, char **);
 static const char *do_selectargs(const char **, bool);
@@ -81,6 +81,8 @@
 	/* we want to run an executable, do some variance checks */
 	if (t->type == TCOM) {
 		/* check if this is 'var=<<EOF' */
+		/*XXX this is broken, don’t use! */
+		/*XXX https://bugs.launchpad.net/mksh/+bug/1380389 */
 		if (
 		    /* we have zero arguments, i.e. no programme to run */
 		    t->args[0] == NULL &&
@@ -94,20 +96,23 @@
 		    /* the variable assignment begins with a valid varname */
 		    (ccp = skip_wdvarname(t->vars[0], true)) != t->vars[0] &&
 		    /* and has no right-hand side (i.e. "varname=") */
-		    ccp[0] == CHAR && ccp[1] == '=' && ccp[2] == EOS &&
+		    ccp[0] == CHAR && ((ccp[1] == '=' && ccp[2] == EOS) ||
+		    /* or "varname+=" */ (ccp[1] == '+' && ccp[2] == CHAR &&
+		    ccp[3] == '=' && ccp[4] == EOS)) &&
 		    /* plus we can have a here document content */
 		    herein(t->ioact[0], &cp) == 0 && cp && *cp) {
 			char *sp = cp, *dp;
-			size_t n = ccp - t->vars[0] + 2, z;
+			size_t n = ccp - t->vars[0] + (ccp[1] == '+' ? 4 : 2);
+			size_t z;
 
 			/* drop redirection (will be garbage collected) */
 			t->ioact = NULL;
 
 			/* set variable to its expanded value */
-			z = strlen(cp) + 1;
-			if (notoktomul(z, 2) || notoktoadd(z * 2, n))
+			z = strlen(cp);
+			if (notoktomul(z, 2) || notoktoadd(z * 2, n + 1))
 				internal_errorf(Toomem, (size_t)-1);
-			dp = alloc(z * 2 + n, ATEMP);
+			dp = alloc(z * 2 + n + 1, APERM);
 			memcpy(dp, t->vars[0], n);
 			t->vars[0] = dp;
 			dp += n;
@@ -500,7 +505,7 @@
 	/* Must be static (XXX but why?) */
 	static struct op texec;
 	int type_flags;
-	bool keepasn_ok;
+	bool resetspec;
 	int fcflags = FC_BI|FC_FUNC|FC_PATH;
 	bool bourne_function_call = false;
 	struct block *l_expand, *l_assign;
@@ -532,7 +537,7 @@
 	 *	FOO=bar command			FOO is neither kept nor exported
 	 *	PATH=... foobar			use new PATH in foobar search
 	 */
-	keepasn_ok = true;
+	resetspec = false;
 	while (tp && tp->type == CSHELL) {
 		/* undo effects of command */
 		fcflags = FC_BI|FC_FUNC|FC_PATH;
@@ -579,7 +584,7 @@
 			 * POSIX says special builtins lose their status
 			 * if accessed using command.
 			 */
-			keepasn_ok = false;
+			resetspec = true;
 			if (!ap[0]) {
 				/* ensure command with no args exits with 0 */
 				subst_exstat = 0;
@@ -614,13 +619,13 @@
 	if (t->u.evalflags & DOTCOMEXEC)
 		flags |= XEXEC;
 	l_expand = e->loc;
-	if (keepasn_ok && (!ap[0] || (tp && (tp->flag & KEEPASN))))
+	if (!resetspec && (!ap[0] || (tp && (tp->flag & KEEPASN))))
 		type_flags = 0;
 	else {
 		/* create new variable/function block */
 		newblock();
 		/* ksh functions don't keep assignments, POSIX functions do. */
-		if (keepasn_ok && tp && tp->type == CFUNC &&
+		if (!resetspec && tp && tp->type == CFUNC &&
 		    !(tp->flag & FKSH)) {
 			bourne_function_call = true;
 			type_flags = EXPORT;
@@ -635,7 +640,7 @@
 	for (i = 0; t->vars[i]; i++) {
 		/* do NOT lookup in the new var/fn block just created */
 		e->loc = l_expand;
-		cp = evalstr(t->vars[i], DOASNTILDE | DOASNFIELD);
+		cp = evalstr(t->vars[i], DOASNTILDE | DOSCALAR);
 		e->loc = l_assign;
 		if (Flag(FXTRACE)) {
 			const char *ccp;
@@ -684,8 +689,8 @@
 
 	/* shell built-in */
 	case CSHELL:
-		rv = call_builtin(tp, (const char **)ap, null);
-		if (!keepasn_ok && tp->val.f == c_shift) {
+		rv = call_builtin(tp, (const char **)ap, null, resetspec);
+		if (resetspec && tp->val.f == c_shift) {
 			l_expand->argc = l_assign->argc;
 			l_expand->argv = l_assign->argv;
 		}
@@ -849,10 +854,8 @@
 {
 	const char *sh;
 #ifndef MKSH_SMALL
-	unsigned char *cp;
-	/* 64 == MAXINTERP in MirBSD <sys/param.h> */
-	char buf[64];
 	int fd;
+	unsigned char buf[68];
 #endif
 	union mksh_ccphack args, cap;
 
@@ -866,33 +869,35 @@
 
 #ifndef MKSH_SMALL
 	if ((fd = open(tp->str, O_RDONLY | O_BINARY)) >= 0) {
-		/* read first MAXINTERP octets from file */
-		if (read(fd, buf, sizeof(buf)) <= 0)
-			/* read error -> no good */
-			buf[0] = '\0';
+		unsigned char *cp;
+		unsigned short m;
+		ssize_t n;
+
+		/* read first couple of octets from file */
+		n = read(fd, buf, sizeof(buf) - 1);
 		close(fd);
+		/* read error or short read? */
+		if (n < 5)
+			goto nomagic;
+		/* terminate buffer */
+		buf[n] = '\0';
 
 		/* skip UTF-8 Byte Order Mark, if present */
-		cp = (unsigned char *)buf;
-		if ((cp[0] == 0xEF) && (cp[1] == 0xBB) && (cp[2] == 0xBF))
-			cp += 3;
-		/* save begin of shebang for later */
-		fd = (char *)cp - buf;		/* either 0 or (if BOM) 3 */
-
-		/* scan for newline (or CR) or NUL _before_ end of buffer */
-		while ((size_t)((char *)cp - buf) < sizeof(buf))
-			if (*cp == '\0' || *cp == '\n' || *cp == '\r') {
-				*cp = '\0';
-				break;
-			} else
-				++cp;
+		cp = buf + (n = ((buf[0] == 0xEF) && (buf[1] == 0xBB) &&
+		    (buf[2] == 0xBF)) ? 3 : 0);
+
+		/* scan for newline or NUL (end of buffer) */
+		while (*cp && *cp != '\n')
+			++cp;
 		/* if the shebang line is longer than MAXINTERP, bail out */
-		if ((size_t)((char *)cp - buf) >= sizeof(buf))
+		if (!*cp)
 			goto noshebang;
+		/* replace newline by NUL */
+		*cp = '\0';
 
 		/* restore begin of shebang position (buf+0 or buf+3) */
-		cp = (unsigned char *)(buf + fd);
-		/* bail out if read error (above) or no shebang */
+		cp = buf + n;
+		/* bail out if no shebang magic found */
 		if ((cp[0] != '#') || (cp[1] != '!'))
 			goto noshebang;
 
@@ -918,22 +923,24 @@
 			if (*cp)
 				*tp->args-- = (char *)cp;
 		}
+		goto nomagic;
  noshebang:
-		if (buf[0] == 0x7F && buf[1] == 'E' && buf[2] == 'L' &&
-		    buf[3] == 'F')
+		m = buf[0] << 8 | buf[1];
+		if (m == 0x7F45 && buf[2] == 'L' && buf[3] == 'F')
 			errorf("%s: not executable: %d-bit ELF file", tp->str,
-			    32 * ((uint8_t)buf[4]));
-		fd = buf[0] << 8 | buf[1];
-		if ((fd == /* OMAGIC */ 0407) ||
-		    (fd == /* NMAGIC */ 0410) ||
-		    (fd == /* ZMAGIC */ 0413) ||
-		    (fd == /* QMAGIC */ 0314) ||
-		    (fd == /* ECOFF_I386 */ 0x4C01) ||
-		    (fd == /* ECOFF_M68K */ 0x0150 || fd == 0x5001) ||
-		    (fd == /* ECOFF_SH */   0x0500 || fd == 0x0005) ||
-		    (fd == /* "MZ" */ 0x4D5A) ||
-		    (fd == /* gzip */ 0x1F8B))
-			errorf("%s: not executable: magic %04X", tp->str, fd);
+			    32 * buf[4]);
+		if ((m == /* OMAGIC */ 0407) ||
+		    (m == /* NMAGIC */ 0410) ||
+		    (m == /* ZMAGIC */ 0413) ||
+		    (m == /* QMAGIC */ 0314) ||
+		    (m == /* ECOFF_I386 */ 0x4C01) ||
+		    (m == /* ECOFF_M68K */ 0x0150 || m == 0x5001) ||
+		    (m == /* ECOFF_SH */   0x0500 || m == 0x0005) ||
+		    (m == /* "MZ" */ 0x4D5A) ||
+		    (m == /* gzip */ 0x1F8B) || (m == /* .Z */ 0x1F9D))
+			errorf("%s: not executable: magic %04X", tp->str, m);
+ nomagic:
+		;
 	}
 #endif
 	args.ro = tp->args;
@@ -952,7 +959,7 @@
 	struct tbl *tp;
 
 	tp = ktsearch(&builtins, *wp, hash(*wp));
-	return (call_builtin(tp, wp, "shcomexec"));
+	return (call_builtin(tp, wp, "shcomexec", false));
 }
 
 /*
@@ -1001,8 +1008,6 @@
 
 	while (/* CONSTCOND */ 1) {
 		tp = findfunc(name, nhash, true);
-		/* because findfunc:create=true */
-		mkssert(tp != NULL);
 
 		if (tp->flag & ISSET)
 			was_set = true;
@@ -1265,22 +1270,22 @@
 }
 
 static int
-call_builtin(struct tbl *tp, const char **wp, const char *where)
+call_builtin(struct tbl *tp, const char **wp, const char *where, bool resetspec)
 {
 	int rv;
 
 	if (!tp)
 		internal_errorf("%s: %s", where, wp[0]);
 	builtin_argv0 = wp[0];
-	builtin_flag = tp->flag;
+	builtin_spec = tobool(!resetspec && (tp->flag & SPEC_BI));
 	shf_reopen(1, SHF_WR, shl_stdout);
 	shl_stdout_ok = true;
 	ksh_getopt_reset(&builtin_opt, GF_ERROR);
 	rv = (*tp->val.f)(wp);
 	shf_flush(shl_stdout);
 	shl_stdout_ok = false;
-	builtin_flag = 0;
 	builtin_argv0 = NULL;
+	builtin_spec = false;
 	return (rv);
 }
 
@@ -1559,7 +1564,8 @@
 		if (print_menu || !*str_val(global("REPLY")))
 			pr_menu(ap);
 		shellf("%s", str_val(global("PS3")));
-		if (call_builtin(findcom("read", FC_BI), read_args, Tselect))
+		if (call_builtin(findcom("read", FC_BI), read_args, Tselect,
+		    false))
 			return (NULL);
 		s = str_val(global("REPLY"));
 		if (*s && getn(s, &i))
Index: src/bin/mksh/funcs.c
diff -u src/bin/mksh/funcs.c:1.258 src/bin/mksh/funcs.c:1.259.2.1
--- src/bin/mksh/funcs.c:1.258	Wed Sep  3 19:55:51 2014
+++ src/bin/mksh/funcs.c	Sun Jan 11 22:39:49 2015
@@ -5,7 +5,7 @@
 
 /*-
  * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009,
- *		 2010, 2011, 2012, 2013, 2014
+ *		 2010, 2011, 2012, 2013, 2014, 2015
  *	Thorsten Glaser <tg@mirbsd.org>
  *
  * Provided that these terms and disclaimer and all copyright notices
@@ -38,7 +38,7 @@
 #endif
 #endif
 
-__RCSID("$MirOS: src/bin/mksh/funcs.c,v 1.258 2014/09/03 19:55:51 tg Exp $");
+__RCSID("$MirOS: src/bin/mksh/funcs.c,v 1.259.2.1 2015/01/11 22:39:49 tg Exp $");
 
 #if HAVE_KILLPG
 /*
@@ -550,7 +550,7 @@
 		if (vflag || (tp->type != CALIAS && tp->type != CEXEC &&
 		    tp->type != CTALIAS))
 			shf_puts(id, shl_stdout);
-		if (vflag)
+		if (vflag) {
 			switch (tp->type) {
 			case CKEYWD:
 			case CALIAS:
@@ -559,11 +559,20 @@
 				shf_puts(" is a", shl_stdout);
 				break;
 			}
+			switch (tp->type) {
+			case CKEYWD:
+			case CSHELL:
+			case CTALIAS:
+			case CEXEC:
+				shf_putc(' ', shl_stdout);
+				break;
+			}
+		}
 
 		switch (tp->type) {
 		case CKEYWD:
 			if (vflag)
-				shf_puts(" reserved word", shl_stdout);
+				shf_puts("reserved word", shl_stdout);
 			break;
 		case CALIAS:
 			if (vflag)
@@ -590,16 +599,17 @@
 			}
 			break;
 		case CSHELL:
-			if (vflag)
-				shprintf("%s %s %s",
-				    (tp->flag & SPEC_BI) ? " special" : null,
-				    "shell", Tbuiltin);
+			if (vflag) {
+				if (tp->flag & SPEC_BI)
+					shf_puts("special ", shl_stdout);
+				shprintf("%s %s", "shell", Tbuiltin);
+			}
 			break;
 		case CTALIAS:
 		case CEXEC:
 			if (tp->flag & ISSET) {
 				if (vflag) {
-					shf_puts(" is ", shl_stdout);
+					shf_puts("is ", shl_stdout);
 					if (tp->type == CTALIAS)
 						shprintf("a tracked %s%s for ",
 						    (tp->flag & EXPORT) ?
@@ -609,12 +619,12 @@
 				shf_puts(tp->val.s, shl_stdout);
 			} else {
 				if (vflag)
-					shprintf(" %s\n", "not found");
+					shf_puts("not found", shl_stdout);
 				rv = 1;
 			}
 			break;
 		default:
-			shprintf("%s is *GOK*", id);
+			shf_puts(" is *GOK*", shl_stdout);
 			break;
 		}
 		if (vflag || !rv)
@@ -1587,12 +1597,16 @@
 		return (1);
 	arg = wp[builtin_opt.optind];
 
-	if (arg) {
-		evaluate(arg, &val, KSH_UNWIND_ERROR, false);
-		n = val;
-	} else
+	if (!arg)
 		n = 1;
-	if (n < 0) {
+	else if (!evaluate(arg, &val, KSH_RETURN_ERROR, false)) {
+		/* error already printed */
+		bi_errorfz();
+		return (1);
+	} else if (!(n = val)) {
+		/* nothing to do */
+		return (0);
+	} else if (n < 0) {
 		bi_errorf("%s: %s", arg, "bad number");
 		return (1);
 	}
@@ -2417,9 +2431,8 @@
 		return (c_typeset(args));
 	}
 
-	argi = parse_args(wp, OF_SET, &setargs);
-	if (argi < 0)
-		return (1);
+	if ((argi = parse_args(wp, OF_SET, &setargs)) < 0)
+		return (2);
 	/* set $# and $* */
 	if (setargs) {
 		wp += argi - 1;
Index: src/bin/mksh/lex.c
diff -u src/bin/mksh/lex.c:1.193 src/bin/mksh/lex.c:1.193.2.1
--- src/bin/mksh/lex.c:1.193	Sun Jun 29 11:28:28 2014
+++ src/bin/mksh/lex.c	Sun Jan 11 22:39:50 2015
@@ -23,7 +23,7 @@
 
 #include "sh.h"
 
-__RCSID("$MirOS: src/bin/mksh/lex.c,v 1.193 2014/06/29 11:28:28 tg Exp $");
+__RCSID("$MirOS: src/bin/mksh/lex.c,v 1.193.2.1 2015/01/11 22:39:50 tg Exp $");
 
 /*
  * states while lexing word
@@ -854,7 +854,7 @@
 			*dp = '\0';
 			/* store the quoted string */
 			*wp++ = OQUOTE;
-			XcheckN(ws, wp, (dp - sp));
+			XcheckN(ws, wp, (dp - sp) * 2);
 			dp = sp;
 			while ((c = *dp++)) {
 				if (c == '\\') {
@@ -1027,17 +1027,24 @@
 	/* copy word to unprefixed string ident */
 	sp = yylval.cp;
 	dp = ident;
-	if ((cf & HEREDELIM) && (sp[1] == '<'))
-		while ((dp - ident) < IDENT) {
-			if ((c = *sp++) == CHAR)
-				*dp++ = *sp++;
-			else if ((c != OQUOTE) && (c != CQUOTE))
-				break;
+	if ((cf & HEREDELIM) && (sp[1] == '<')) {
+ herestringloop:
+		switch ((c = *sp++)) {
+		case CHAR:
+			++sp;
+			/* FALLTHROUGH */
+		case OQUOTE:
+		case CQUOTE:
+			goto herestringloop;
+		default:
+			break;
 		}
-	else
+		/* dummy value */
+		*dp++ = 'x';
+	} else
 		while ((dp - ident) < IDENT && (c = *sp++) == CHAR)
 			*dp++ = *sp++;
-	/* Make sure the ident array stays '\0' padded */
+	/* make sure the ident array stays NUL padded */
 	memset(dp, 0, (ident + IDENT) - dp + 1);
 	if (c != EOS)
 		/* word is not unquoted */
Index: src/bin/mksh/main.c
diff -u src/bin/mksh/main.c:1.284 src/bin/mksh/main.c:1.285
--- src/bin/mksh/main.c:1.284	Fri Oct  3 17:19:27 2014
+++ src/bin/mksh/main.c	Sun Oct 12 21:58:52 2014
@@ -34,7 +34,7 @@
 #include <locale.h>
 #endif
 
-__RCSID("$MirOS: src/bin/mksh/main.c,v 1.284 2014/10/03 17:19:27 tg Exp $");
+__RCSID("$MirOS: src/bin/mksh/main.c,v 1.285 2014/10/12 21:58:52 tg Exp $");
 
 extern char **environ;
 
@@ -1244,7 +1244,7 @@
 	 * non-interactive shells to exit.
 	 * XXX odd use of KEEPASN; also may not want LERROR here
 	 */
-	if (builtin_flag & SPEC_BI) {
+	if (builtin_spec) {
 		builtin_argv0 = NULL;
 		unwind(LERROR);
 	}
Index: src/bin/mksh/mksh.1
diff -u src/bin/mksh/mksh.1:1.344 src/bin/mksh/mksh.1:1.344.2.1
--- src/bin/mksh/mksh.1:1.344	Tue Oct  7 15:30:12 2014
+++ src/bin/mksh/mksh.1	Sun Jan 11 22:39:51 2015
@@ -1,5 +1,5 @@
-.\" $MirOS: src/bin/mksh/mksh.1,v 1.344 2014/10/07 15:30:12 tg Exp $
-.\" $OpenBSD: ksh.1,v 1.153 2014/08/17 07:15:41 jmc Exp $
+.\" $MirOS: src/bin/mksh/mksh.1,v 1.344.2.1 2015/01/11 22:39:51 tg Exp $
+.\" $OpenBSD: ksh.1,v 1.155 2014/12/09 15:37:13 schwarze Exp $
 .\"-
 .\" Copyright © 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009,
 .\"		2010, 2011, 2012, 2013, 2014
@@ -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: October 7 2014 $
+.Dd $Mdocdate: January 11 2015 $
 .\"
 .\" Check which macro package we use, and do other -mdoc setup.
 .\"
@@ -2923,6 +2923,10 @@
 .It
 The EXIT trap, if set in a function, will be executed after the function
 returns.
+.It
+Shell options
+.Pq Ic set Fl o
+have local scope, i.e. changes inside a function are reset upon its exit.
 .El
 .Ss Command execution
 After evaluation of command-line arguments, redirections, and parameter
@@ -6457,6 +6461,17 @@
 instead, but be aware that, in POSIX, it's legal for the OS to make
 .Li print $((2147483647 + 1))
 delete all files on your system, as it's Undefined Behaviour.
+.Pp
+.Nm mksh
+provides a consistent, clear interface normally.
+This may deviate from POSIX in optional or opinionated places, such
+as whether leading-digit-zero numbers should be interpreted as octal.
+.Ic set \-o posix
+will cause the shell (either
+.Nm mksh
+or
+.Nm lksh )
+to behave more like the standard expects.
 .Sh BUGS
 Suspending (using \*(haZ) pipelines like the one below will only suspend
 the currently running part of the pipeline; in this example,
@@ -6475,9 +6490,12 @@
 when not called as
 .Pa /bin/sh
 which, on some systems only, enables
+.Ic set \-o posix
+or
 .Ic set \-o sh
 automatically (whose behaviour differs across targets),
 for an operating environment supporting all of its advanced needs.
+.Pp
 Please report bugs in
 .Nm
 to the
Index: src/bin/mksh/sh.h
diff -u src/bin/mksh/sh.h:1.697 src/bin/mksh/sh.h:1.701.2.1
--- src/bin/mksh/sh.h:1.697	Tue Oct  7 15:22:17 2014
+++ src/bin/mksh/sh.h	Sun Jan 11 22:39:53 2015
@@ -169,9 +169,9 @@
 #endif
 
 #ifdef EXTERN
-__RCSID("$MirOS: src/bin/mksh/sh.h,v 1.697 2014/10/07 15:22:17 tg Exp $");
+__RCSID("$MirOS: src/bin/mksh/sh.h,v 1.701.2.1 2015/01/11 22:39:53 tg Exp $");
 #endif
-#define MKSH_VERSION "R50 2014/10/07"
+#define MKSH_VERSION "R50 2015/01/11"
 
 /* arithmetic types: C implementation */
 #if !HAVE_CAN_INTTYPES
@@ -992,8 +992,8 @@
 
 /* name of called builtin function (used by error functions) */
 EXTERN const char *builtin_argv0;
-/* flags of called builtin (SPEC_BI, etc.) */
-EXTERN uint32_t builtin_flag;
+/* is called builtin SPEC_BI? */
+EXTERN bool builtin_spec;
 
 /* current working directory */
 EXTERN char	*current_wd;
@@ -1396,7 +1396,7 @@
 #define DOVACHECK BIT(9)	/* var assign check (for typeset, set, etc) */
 #define DOMARKDIRS BIT(10)	/* force markdirs behaviour */
 #define DOTCOMEXEC BIT(11)	/* not an eval flag, used by sh -c hack */
-#define DOASNFIELD BIT(12)	/* is assignment, change field handling */
+#define DOSCALAR BIT(12)	/* change field handling to non-list context */
 
 #define X_EXTRA	20	/* this many extra bytes in X string */
 
