Snap for 4598635 from 034f947f03e15c6f73bda708c5488042a98928ca to pi-release

Change-Id: I503b8ca4a49f5a2f2184c729264229bdf75a3249
diff --git a/Android.bp b/Android.bp
index 9926e18..2bca561 100644
--- a/Android.bp
+++ b/Android.bp
@@ -108,7 +108,7 @@
         "-DHAVE_SYS_ERRLIST_DECL=0",
         "-DHAVE_SYS_SIGLIST_DECL=1",
         "-DHAVE_PERSISTENT_HISTORY=0",
-        "-DMKSH_BUILD_R=562",
+        "-DMKSH_BUILD_R=563",
 
         // Additional flags
         "-DMKSH_DEFAULT_PROFILEDIR=\"/system/etc\"",
diff --git a/src/Build.sh b/src/Build.sh
old mode 100644
new mode 100755
index 78fe347..ebf4e1c
--- a/src/Build.sh
+++ b/src/Build.sh
@@ -1,5 +1,5 @@
 #!/bin/sh
-srcversion='$MirOS: src/bin/mksh/Build.sh,v 1.727 2017/08/29 13:38:28 tg Exp $'
+srcversion='$MirOS: src/bin/mksh/Build.sh,v 1.731 2018/01/13 21:38:06 tg Exp $'
 #-
 # Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
 #		2011, 2012, 2013, 2014, 2015, 2016, 2017
@@ -796,6 +796,8 @@
 	add_cppflags -DMKSH_ASSUME_UTF8
 	HAVE_ISSET_MKSH_ASSUME_UTF8=1
 	HAVE_ISOFF_MKSH_ASSUME_UTF8=0
+	add_cppflags -DMKSH__NO_SYMLINK
+	check_categories="$check_categories nosymlink"
 	add_cppflags -DMKSH_NO_CMDLINE_EDITING
 	add_cppflags -DMKSH__NO_SETEUGID
 	oswarn=' and will currently not work'
@@ -819,6 +821,20 @@
 IRIX*)
 	: "${HAVE_SETLOCALE_CTYPE=0}"
 	;;
+Jehanne)
+	add_cppflags -DMKSH_ASSUME_UTF8
+	HAVE_ISSET_MKSH_ASSUME_UTF8=1
+	HAVE_ISOFF_MKSH_ASSUME_UTF8=0
+	add_cppflags -DMKSH__NO_SYMLINK
+	check_categories="$check_categories nosymlink"
+	add_cppflags -DMKSH_NO_CMDLINE_EDITING
+	add_cppflags -DMKSH_DISABLE_REVOKE_WARNING
+	add_cppflags '-D_PATH_DEFPATH=\"/cmd\"'
+	add_cppflags '-DMKSH_DEFAULT_EXECSHELL=\"/cmd/mksh\"'
+	add_cppflags '-DMKSH_DEFAULT_PROFILEDIR=\"/cfg/mksh\"'
+	add_cppflags '-DMKSH_ENVDIR=\"/env\"'
+	SRCS="$SRCS jehanne.c"
+	;;
 Linux)
 	case $CC in
 	*tendracc*) ;;
@@ -947,6 +963,8 @@
 	add_cppflags -DMKSH_ASSUME_UTF8
 	HAVE_ISSET_MKSH_ASSUME_UTF8=1
 	HAVE_ISOFF_MKSH_ASSUME_UTF8=0
+	add_cppflags -DMKSH__NO_SYMLINK
+	check_categories="$check_categories nosymlink"
 	add_cppflags -DMKSH_NO_CMDLINE_EDITING
 	add_cppflags -DMKSH__NO_SETEUGID
 	oswarn=' and will currently not work'
@@ -2409,7 +2427,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=562
+add_cppflags -DMKSH_BUILD_R=563
 
 $e $bi$me: Finished configuration testing, now producing output.$ao
 
@@ -2733,6 +2751,7 @@
 MKSH_DISABLE_EXPERIMENTAL	disable code not yet comfy for (LTS) snapshots
 MKSH_DISABLE_TTY_WARNING	shut up warning about ctty if OS cant be fixed
 MKSH_DONT_EMIT_IDSTRING		omit RCS IDs from binary
+MKSH_EARLY_LOCALE_TRACKING	track utf8-mode from POSIX locale, for SuSE
 MKSH_MIDNIGHTBSD01ASH_COMPAT	set -o sh: additional compatibility quirk
 MKSH_NOPROSPECTOFWORK		disable jobs, co-processes, etc. (do not use)
 MKSH_NOPWNAM			skip PAM calls, for -static on glibc or Solaris
diff --git a/src/check.t b/src/check.t
index 4473c08..ea414d3 100644
--- a/src/check.t
+++ b/src/check.t
Binary files differ
diff --git a/src/edit.c b/src/edit.c
index 8bccf13..0e51780 100644
--- a/src/edit.c
+++ b/src/edit.c
@@ -5,7 +5,7 @@
 
 /*-
  * 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
  *	mirabilos <m@mirbsd.org>
  *
  * Provided that these terms and disclaimer and all copyright notices
@@ -28,7 +28,7 @@
 
 #ifndef MKSH_NO_CMDLINE_EDITING
 
-__RCSID("$MirOS: src/bin/mksh/edit.c,v 1.340 2017/08/27 23:33:50 tg Exp $");
+__RCSID("$MirOS: src/bin/mksh/edit.c,v 1.342 2018/01/14 00:03:00 tg Exp $");
 
 /*
  * in later versions we might use libtermcap for this, but since external
@@ -312,12 +312,12 @@
 			 */
 			switch (ord(ch)) {
 			case QCHAR:
-			case ord('$'):
-			case ord('*'):
-			case ord('?'):
-			case ord('['):
-			case ord('\\'):
-			case ord('`'):
+			case ORD('$'):
+			case ORD('*'):
+			case ORD('?'):
+			case ORD('['):
+			case ORD('\\'):
+			case ORD('`'):
 				*dp++ = QCHAR;
 				break;
 			}
@@ -650,11 +650,11 @@
 			if (*s == '\\' && s[1])
 				s++;
 			else if (ctype(*s, C_QUEST | C_DOLAR) ||
-			    ord(*s) == ord('*') || ord(*s) == ord('[') ||
+			    ord(*s) == ORD('*') || ord(*s) == ORD('[') ||
 			    /* ?() *() +() @() !() but two already checked */
-			    (ord(s[1]) == ord('(' /*)*/) &&
-			    (ord(*s) == ord('+') || ord(*s) == ord('@') ||
-			    ord(*s) == ord('!')))) {
+			    (ord(s[1]) == ORD('(' /*)*/) &&
+			    (ord(*s) == ORD('+') || ord(*s) == ORD('@') ||
+			    ord(*s) == ORD('!')))) {
 				/*
 				 * just expand based on the extglob
 				 * or parameter
@@ -3688,7 +3688,7 @@
 				return (1);
 			cmdlen = 0;
 			argc1 = 0;
-			if (ctype(ch, C_DIGIT) && ord(ch) != ord('0')) {
+			if (ctype(ch, C_DIGIT) && ord(ch) != ORD('0')) {
 				argc1 = ksh_numdig(ch);
 				state = VARG1;
 			} else {
@@ -3743,7 +3743,7 @@
 
 	case VEXTCMD:
 		argc2 = 0;
-		if (ctype(ch, C_DIGIT) && ord(ch) != ord('0')) {
+		if (ctype(ch, C_DIGIT) && ord(ch) != ORD('0')) {
 			argc2 = ksh_numdig(ch);
 			state = VARG2;
 			return (0);
@@ -4128,7 +4128,7 @@
 			redraw_line(true);
 			break;
 
-		case ord('@'):
+		case ORD('@'):
 			{
 				static char alias[] = "_\0";
 				struct tbl *ap;
@@ -4169,7 +4169,7 @@
 			}
 			break;
 
-		case ord('a'):
+		case ORD('a'):
 			modified = 1;
 			hnum = hlast;
 			if (vs->linelen != 0)
@@ -4177,7 +4177,7 @@
 			insert = INSERT;
 			break;
 
-		case ord('A'):
+		case ORD('A'):
 			modified = 1;
 			hnum = hlast;
 			del_range(0, 0);
@@ -4185,7 +4185,7 @@
 			insert = INSERT;
 			break;
 
-		case ord('S'):
+		case ORD('S'):
 			vs->cursor = domovebeg();
 			del_range(vs->cursor, vs->linelen);
 			modified = 1;
@@ -4193,14 +4193,14 @@
 			insert = INSERT;
 			break;
 
-		case ord('Y'):
+		case ORD('Y'):
 			cmd = "y$";
 			/* ahhhhhh... */
 
 			/* FALLTHROUGH */
-		case ord('c'):
-		case ord('d'):
-		case ord('y'):
+		case ORD('c'):
+		case ORD('d'):
+		case ORD('y'):
 			if (*cmd == cmd[1]) {
 				c1 = *cmd == 'c' ? domovebeg() : 0;
 				c2 = vs->linelen;
@@ -4239,7 +4239,7 @@
 			}
 			break;
 
-		case ord('p'):
+		case ORD('p'):
 			modified = 1;
 			hnum = hlast;
 			if (vs->linelen != 0)
@@ -4253,7 +4253,7 @@
 				return (-1);
 			break;
 
-		case ord('P'):
+		case ORD('P'):
 			modified = 1;
 			hnum = hlast;
 			any = 0;
@@ -4266,25 +4266,25 @@
 				return (-1);
 			break;
 
-		case ord('C'):
+		case ORD('C'):
 			modified = 1;
 			hnum = hlast;
 			del_range(vs->cursor, vs->linelen);
 			insert = INSERT;
 			break;
 
-		case ord('D'):
+		case ORD('D'):
 			yank_range(vs->cursor, vs->linelen);
 			del_range(vs->cursor, vs->linelen);
 			if (vs->cursor != 0)
 				vs->cursor--;
 			break;
 
-		case ord('g'):
+		case ORD('g'):
 			if (!argcnt)
 				argcnt = hlast;
 			/* FALLTHROUGH */
-		case ord('G'):
+		case ORD('G'):
 			if (!argcnt)
 				argcnt = 1;
 			else
@@ -4297,21 +4297,21 @@
 			}
 			break;
 
-		case ord('i'):
+		case ORD('i'):
 			modified = 1;
 			hnum = hlast;
 			insert = INSERT;
 			break;
 
-		case ord('I'):
+		case ORD('I'):
 			modified = 1;
 			hnum = hlast;
 			vs->cursor = domovebeg();
 			insert = INSERT;
 			break;
 
-		case ord('j'):
-		case ord('+'):
+		case ORD('j'):
+		case ORD('+'):
 		case CTRL_N:
 			if (grabhist(modified, hnum + argcnt) < 0)
 				return (-1);
@@ -4321,8 +4321,8 @@
 			}
 			break;
 
-		case ord('k'):
-		case ord('-'):
+		case ORD('k'):
+		case ORD('-'):
 		case CTRL_P:
 			if (grabhist(modified, hnum - argcnt) < 0)
 				return (-1);
@@ -4332,7 +4332,7 @@
 			}
 			break;
 
-		case ord('r'):
+		case ORD('r'):
 			if (vs->linelen == 0)
 				return (-1);
 			modified = 1;
@@ -4350,13 +4350,13 @@
 			}
 			break;
 
-		case ord('R'):
+		case ORD('R'):
 			modified = 1;
 			hnum = hlast;
 			insert = REPLACE;
 			break;
 
-		case ord('s'):
+		case ORD('s'):
 			if (vs->linelen == 0)
 				return (-1);
 			modified = 1;
@@ -4367,7 +4367,7 @@
 			insert = INSERT;
 			break;
 
-		case ord('v'):
+		case ORD('v'):
 			if (!argcnt) {
 				if (vs->linelen == 0)
 					return (-1);
@@ -4390,7 +4390,7 @@
 			vs->linelen = strlen(vs->cbuf);
 			return (2);
 
-		case ord('x'):
+		case ORD('x'):
 			if (vs->linelen == 0)
 				return (-1);
 			modified = 1;
@@ -4401,7 +4401,7 @@
 			del_range(vs->cursor, vs->cursor + argcnt);
 			break;
 
-		case ord('X'):
+		case ORD('X'):
 			if (vs->cursor > 0) {
 				modified = 1;
 				hnum = hlast;
@@ -4414,13 +4414,13 @@
 				return (-1);
 			break;
 
-		case ord('u'):
+		case ORD('u'):
 			t = vs;
 			vs = undo;
 			undo = t;
 			break;
 
-		case ord('U'):
+		case ORD('U'):
 			if (!modified)
 				return (-1);
 			if (grabhist(modified, ohnum) < 0)
@@ -4429,19 +4429,19 @@
 			hnum = ohnum;
 			break;
 
-		case ord('?'):
+		case ORD('?'):
 			if (hnum == hlast)
 				hnum = -1;
 			/* ahhh */
 
 			/* FALLTHROUGH */
-		case ord('/'):
+		case ORD('/'):
 			c3 = 1;
 			srchlen = 0;
 			lastsearch = *cmd;
 			/* FALLTHROUGH */
-		case ord('n'):
-		case ord('N'):
+		case ORD('n'):
+		case ORD('N'):
 			if (lastsearch == ' ')
 				return (-1);
 			if (lastsearch == '?')
@@ -4468,7 +4468,7 @@
 				return (0);
 			}
 			break;
-		case ord('_'):
+		case ORD('_'):
 			{
 				bool inspace;
 				char *p, *sp;
@@ -4520,7 +4520,7 @@
 			}
 			break;
 
-		case ord('~'):
+		case ORD('~'):
 			{
 				char *p;
 				int i;
@@ -4544,7 +4544,7 @@
 				break;
 			}
 
-		case ord('#'):
+		case ORD('#'):
 			{
 				int ret = x_do_comment(vs->cbuf, vs->cbufsize,
 				    &vs->linelen);
@@ -4554,7 +4554,7 @@
 			}
 
 		/* AT&T ksh */
-		case ord('='):
+		case ORD('='):
 		/* Nonstandard vi/ksh */
 		case CTRL_E:
 			print_expansions(vs, 1);
@@ -4574,7 +4574,7 @@
 				return (-1);
 			/* FALLTHROUGH */
 		/* AT&T ksh */
-		case ord('\\'):
+		case ORD('\\'):
 		/* Nonstandard vi/ksh */
 		case CTRL_F:
 			complete_word(1, argcnt);
@@ -4582,7 +4582,7 @@
 
 
 		/* AT&T ksh */
-		case ord('*'):
+		case ORD('*'):
 		/* Nonstandard vi/ksh */
 		case CTRL_X:
 			expand_word(1);
@@ -4590,8 +4590,8 @@
 
 
 		/* mksh: cursor movement */
-		case ord('['):
-		case ord('O'):
+		case ORD('['):
+		case ORD('O'):
 			state = VPREFIX2;
 			if (vs->linelen != 0)
 				vs->cursor++;
@@ -4611,19 +4611,19 @@
 	unsigned int bcount;
 
 	switch (ord(*cmd)) {
-	case ord('b'):
+	case ORD('b'):
 		if (!sub && vs->cursor == 0)
 			return (-1);
 		ncursor = backword(argcnt);
 		break;
 
-	case ord('B'):
+	case ORD('B'):
 		if (!sub && vs->cursor == 0)
 			return (-1);
 		ncursor = Backword(argcnt);
 		break;
 
-	case ord('e'):
+	case ORD('e'):
 		if (!sub && vs->cursor + 1 >= vs->linelen)
 			return (-1);
 		ncursor = endword(argcnt);
@@ -4631,7 +4631,7 @@
 			ncursor++;
 		break;
 
-	case ord('E'):
+	case ORD('E'):
 		if (!sub && vs->cursor + 1 >= vs->linelen)
 			return (-1);
 		ncursor = Endword(argcnt);
@@ -4639,15 +4639,15 @@
 			ncursor++;
 		break;
 
-	case ord('f'):
-	case ord('F'):
-	case ord('t'):
-	case ord('T'):
+	case ORD('f'):
+	case ORD('F'):
+	case ORD('t'):
+	case ORD('T'):
 		fsavecmd = *cmd;
 		fsavech = cmd[1];
 		/* FALLTHROUGH */
-	case ord(','):
-	case ord(';'):
+	case ORD(','):
+	case ORD(';'):
 		if (fsavecmd == ' ')
 			return (-1);
 		i = ksh_eq(fsavecmd, 'F', 'f');
@@ -4661,7 +4661,7 @@
 			ncursor++;
 		break;
 
-	case ord('h'):
+	case ORD('h'):
 	case CTRL_H:
 		if (!sub && vs->cursor == 0)
 			return (-1);
@@ -4670,8 +4670,8 @@
 			ncursor = 0;
 		break;
 
-	case ord(' '):
-	case ord('l'):
+	case ORD(' '):
+	case ORD('l'):
 		if (!sub && vs->cursor + 1 >= vs->linelen)
 			return (-1);
 		if (vs->linelen != 0) {
@@ -4681,27 +4681,27 @@
 		}
 		break;
 
-	case ord('w'):
+	case ORD('w'):
 		if (!sub && vs->cursor + 1 >= vs->linelen)
 			return (-1);
 		ncursor = forwword(argcnt);
 		break;
 
-	case ord('W'):
+	case ORD('W'):
 		if (!sub && vs->cursor + 1 >= vs->linelen)
 			return (-1);
 		ncursor = Forwword(argcnt);
 		break;
 
-	case ord('0'):
+	case ORD('0'):
 		ncursor = 0;
 		break;
 
-	case ord('^'):
+	case ORD('^'):
 		ncursor = domovebeg();
 		break;
 
-	case ord('|'):
+	case ORD('|'):
 		ncursor = argcnt;
 		if (ncursor > vs->linelen)
 			ncursor = vs->linelen;
@@ -4709,14 +4709,14 @@
 			ncursor--;
 		break;
 
-	case ord('$'):
+	case ORD('$'):
 		if (vs->linelen != 0)
 			ncursor = vs->linelen;
 		else
 			ncursor = 0;
 		break;
 
-	case ord('%'):
+	case ORD('%'):
 		ncursor = vs->cursor;
 		while (ncursor < vs->linelen &&
 		    (i = bracktype(vs->cbuf[ncursor])) == 0)
@@ -4784,22 +4784,22 @@
 {
 	switch (ord(ch)) {
 
-	case ord('('):
+	case ORD('('):
 		return (1);
 
-	case ord('['):
+	case ORD('['):
 		return (2);
 
-	case ord('{'):
+	case ORD('{'):
 		return (3);
 
-	case ord(')'):
+	case ORD(')'):
 		return (-1);
 
-	case ord(']'):
+	case ORD(']'):
 		return (-2);
 
-	case ord('}'):
+	case ORD('}'):
 		return (-3);
 
 	default:
diff --git a/src/eval.c b/src/eval.c
index 7a892c0..6aa1844 100644
--- a/src/eval.c
+++ b/src/eval.c
@@ -2,7 +2,7 @@
 
 /*-
  * 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
  *	mirabilos <m@mirbsd.org>
  *
  * Provided that these terms and disclaimer and all copyright notices
@@ -23,7 +23,7 @@
 
 #include "sh.h"
 
-__RCSID("$MirOS: src/bin/mksh/eval.c,v 1.215 2017/08/28 23:27:51 tg Exp $");
+__RCSID("$MirOS: src/bin/mksh/eval.c,v 1.219 2018/01/14 01:29:47 tg Exp $");
 
 /*
  * string expansion
@@ -320,21 +320,21 @@
 					case COMASUB:
 					case COMSUB:
 						*dp++ = '(';
-						c = ord(')');
+						c = ORD(')');
 						break;
 					case FUNASUB:
 					case FUNSUB:
 					case VALSUB:
 						*dp++ = '{';
 						*dp++ = c == VALSUB ? '|' : ' ';
-						c = ord('}');
+						c = ORD('}');
 						break;
 					}
 					while (*sp != '\0') {
 						Xcheck(ds, dp);
 						*dp++ = *sp++;
 					}
-					if (c == ord('}'))
+					if ((unsigned int)c == ORD('}'))
 						*dp++ = ';';
 					*dp++ = c;
 				} else {
@@ -436,11 +436,11 @@
 					if (stype)
 						sp += slen;
 					switch (stype & STYPE_SINGLE) {
-					case ord('#') | STYPE_AT:
+					case ORD('#') | STYPE_AT:
 						x.str = shf_smprintf("%08X",
 						    (unsigned int)hash(str_val(st->var)));
 						break;
-					case ord('Q') | STYPE_AT: {
+					case ORD('Q') | STYPE_AT: {
 						struct shf shf;
 
 						shf_sopen(NULL, 0, SHF_WR|SHF_DYNAMIC, &shf);
@@ -448,7 +448,7 @@
 						x.str = shf_sclose(&shf);
 						break;
 					    }
-					case ord('0'): {
+					case ORD('0'): {
 						char *beg, *mid, *end, *stg;
 						mksh_ari_t from = 0, num = -1, flen, finc = 0;
 
@@ -456,13 +456,13 @@
 						mid = beg + (wdscan(sp, ADELIM) - sp);
 						stg = beg + (wdscan(sp, CSUBST) - sp);
 						mid[-2] = EOS;
-						if (ord(mid[-1]) == ord(/*{*/ '}')) {
+						if (ord(mid[-1]) == ORD(/*{*/ '}')) {
 							sp += mid - beg - 1;
 							end = NULL;
 						} else {
 							end = mid +
 							    (wdscan(mid, ADELIM) - mid);
-							if (ord(end[-1]) != ord(/*{*/ '}'))
+							if (ord(end[-1]) != ORD(/*{*/ '}'))
 								/* more than max delimiters */
 								goto unwind_substsyn;
 							end[-2] = EOS;
@@ -495,8 +495,8 @@
 						strndupx(x.str, beg, num, ATEMP);
 						goto do_CSUBST;
 					    }
-					case ord('/') | STYPE_AT:
-					case ord('/'): {
+					case ORD('/') | STYPE_AT:
+					case ORD('/'): {
 						char *s, *p, *d, *sbeg, *end;
 						char *pat = NULL, *rrep = null;
 						char fpat = 0, *tpat1, *tpat2;
@@ -506,7 +506,7 @@
 						p = s + (wdscan(sp, ADELIM) - sp);
 						d = s + (wdscan(sp, CSUBST) - sp);
 						p[-2] = EOS;
-						if (ord(p[-1]) == ord(/*{*/ '}'))
+						if (ord(p[-1]) == ORD(/*{*/ '}'))
 							d = NULL;
 						else
 							d[-2] = EOS;
@@ -547,11 +547,11 @@
 						}
 
 						/* first see if we have any match at all */
-						if (ord(fpat) == ord('#')) {
+						if (ord(fpat) == ORD('#')) {
 							/* anchor at the beginning */
 							tpat1 = shf_smprintf("%s%c*", pat, MAGIC);
 							tpat2 = tpat1;
-						} else if (ord(fpat) == ord('%')) {
+						} else if (ord(fpat) == ORD('%')) {
 							/* anchor at the end */
 							tpat1 = shf_smprintf("%c*%s", MAGIC, pat);
 							tpat2 = pat;
@@ -569,7 +569,7 @@
 							goto end_repl;
 						end = strnul(s);
 						/* now anchor the beginning of the match */
-						if (ord(fpat) != ord('#'))
+						if (ord(fpat) != ORD('#'))
 							while (sbeg <= end) {
 								if (gmatchx(sbeg, tpat2, false))
 									break;
@@ -578,11 +578,11 @@
 							}
 						/* now anchor the end of the match */
 						p = end;
-						if (ord(fpat) != ord('%'))
+						if (ord(fpat) != ORD('%'))
 							while (p >= sbeg) {
 								bool gotmatch;
 
-								c = *p;
+								c = ord(*p);
 								*p = '\0';
 								gotmatch = tobool(gmatchx(sbeg, pat, false));
 								*p = c;
@@ -622,8 +622,8 @@
 						afree(ws, ATEMP);
 						goto do_CSUBST;
 					    }
-					case ord('#'):
-					case ord('%'):
+					case ORD('#'):
+					case ORD('%'):
 						/* ! DOBLANK,DOBRACE */
 						f = (f & DONTRUNCOMMAND) |
 						    DOPAT | DOTILDE |
@@ -637,10 +637,10 @@
 						 */
 						if (!Flag(FSH)) {
 							*dp++ = MAGIC;
-							*dp++ = ord(0x80 | '@');
+							*dp++ = ORD(0x80 | '@');
 						}
 						break;
-					case ord('='):
+					case ORD('='):
 						/*
 						 * Tilde expansion for string
 						 * variables in POSIX mode is
@@ -664,7 +664,7 @@
 						f &= ~(DOBLANK|DOGLOB|DOBRACE);
 						tilde_ok = 1;
 						break;
-					case ord('?'):
+					case ORD('?'):
 						if (*sp == CSUBST)
 							errorf("%s: parameter null or not set",
 							    st->var->name);
@@ -699,8 +699,8 @@
 				if (f & DOBLANK)
 					doblank--;
 				switch (st->stype & STYPE_SINGLE) {
-				case ord('#'):
-				case ord('%'):
+				case ORD('#'):
+				case ORD('%'):
 					if (!Flag(FSH)) {
 						/* Append end-pattern */
 						*dp++ = MAGIC;
@@ -730,7 +730,7 @@
 						doblank++;
 					st = st->prev;
 					continue;
-				case ord('='):
+				case ORD('='):
 					/*
 					 * Restore our position and substitute
 					 * the value of st->var (may not be
@@ -763,17 +763,17 @@
 					st = st->prev;
 					word = quote || (!*x.str && (f & DOSCALAR)) ? IFS_WORD : IFS_IWS;
 					continue;
-				case ord('?'):
+				case ORD('?'):
 					dp = Xrestpos(ds, dp, st->base);
 
 					errorf(Tf_sD_s, st->var->name,
 					    debunk(dp, dp, strlen(dp) + 1));
 					break;
-				case ord('0'):
-				case ord('/') | STYPE_AT:
-				case ord('/'):
-				case ord('#') | STYPE_AT:
-				case ord('Q') | STYPE_AT:
+				case ORD('0'):
+				case ORD('/') | STYPE_AT:
+				case ORD('/'):
+				case ORD('#') | STYPE_AT:
+				case ORD('Q') | STYPE_AT:
 					dp = Xrestpos(ds, dp, st->base);
 					type = XSUB;
 					word = quote || (!*x.str && (f & DOSCALAR)) ? IFS_WORD : IFS_IWS;
@@ -791,19 +791,19 @@
 				/* open pattern: *(foo|bar) */
 				/* Next char is the type of pattern */
 				make_magic = true;
-				c = *sp++ | 0x80;
+				c = ord(*sp++) | 0x80U;
 				break;
 
 			case SPAT:
 				/* pattern separator (|) */
 				make_magic = true;
-				c = '|';
+				c = ORD('|');
 				break;
 
 			case CPAT:
 				/* close pattern */
 				make_magic = true;
-				c = /*(*/ ')';
+				c = ORD(/*(*/ ')');
 				break;
 			}
 			break;
@@ -824,7 +824,7 @@
 
 		case XSUB:
 		case XSUBMID:
-			if ((c = *x.str++) == 0) {
+			if ((c = ord(*x.str++)) == 0) {
 				type = XBASE;
 				if (f & DOBLANK)
 					doblank--;
@@ -837,7 +837,7 @@
 			quote = 1;
 			/* FALLTHROUGH */
 		case XARG:
-			if ((c = *x.str++) == '\0') {
+			if ((c = ord(*x.str++)) == '\0') {
 				/*
 				 * force null words to be created so
 				 * set -- "" 2 ""; echo "$@" will do
@@ -855,13 +855,13 @@
 				if ((f & DOHEREDOC)) {
 					/* pseudo-field-split reliably */
 					if (c == 0)
-						c = ' ';
+						c = ORD(' ');
 					break;
 				}
 				if ((f & DOSCALAR)) {
 					/* do not field-split */
 					if (x.split) {
-						c = ' ';
+						c = ORD(' ');
 						break;
 					}
 					if (c == 0)
@@ -873,7 +873,7 @@
 					if (!quote && word == IFS_WS)
 						continue;
 					/* this is so we don't terminate */
-					c = ' ';
+					c = ORD(' ');
 					/* now force-emit a word */
 					goto emit_word;
 				}
@@ -893,33 +893,33 @@
 				c = -1;
 			} else if (newlines) {
 				/* spit out saved NLs */
-				c = '\n';
+				c = ORD('\n');
 				--newlines;
 			} else {
 				while ((c = shf_getc(x.u.shf)) == 0 ||
-				    ctype(c, C_NL)) {
+				    cinttype(c, C_NL)) {
 #ifdef MKSH_WITH_TEXTMODE
-					if (c == '\r') {
+					if (c == ORD('\r')) {
 						c = shf_getc(x.u.shf);
 						switch (c) {
-						case '\n':
+						case ORD('\n'):
 							break;
 						default:
 							shf_ungetc(c, x.u.shf);
 							/* FALLTHROUGH */
 						case -1:
-							c = '\r';
+							c = ORD('\r');
 							break;
 						}
 					}
 #endif
-					if (c == '\n')
+					if (c == ORD('\n'))
 						/* save newlines */
 						newlines++;
 				}
 				if (newlines && c != -1) {
 					shf_ungetc(c, x.u.shf);
-					c = '\n';
+					c = ORD('\n');
 					--newlines;
 				}
 			}
@@ -1003,10 +1003,10 @@
 			/* mark any special second pass chars */
 			if (!quote)
 				switch (ord(c)) {
-				case ord('['):
-				case ord('!'):
-				case ord('-'):
-				case ord(']'):
+				case ORD('['):
+				case ORD('!'):
+				case ORD('-'):
+				case ORD(']'):
 					/*
 					 * For character classes - doesn't hurt
 					 * to have magic !,-,]s outside of
@@ -1014,29 +1014,29 @@
 					 */
 					if (f & (DOPAT | DOGLOB)) {
 						fdo |= DOMAGIC;
-						if (c == ord('['))
+						if ((unsigned int)c == ORD('['))
 							fdo |= f & DOGLOB;
 						*dp++ = MAGIC;
 					}
 					break;
-				case ord('*'):
-				case ord('?'):
+				case ORD('*'):
+				case ORD('?'):
 					if (f & (DOPAT | DOGLOB)) {
 						fdo |= DOMAGIC | (f & DOGLOB);
 						*dp++ = MAGIC;
 					}
 					break;
-				case ord('{'):
-				case ord('}'):
-				case ord(','):
+				case ORD('{'):
+				case ORD('}'):
+				case ORD(','):
 					if ((f & DOBRACE) &&
-					    (ord(c) == ord('{' /*}*/) ||
+					    (ord(c) == ORD('{' /*}*/) ||
 					    (fdo & DOBRACE))) {
 						fdo |= DOBRACE|DOMAGIC;
 						*dp++ = MAGIC;
 					}
 					break;
-				case ord('='):
+				case ORD('='):
 					/* Note first unquoted = for ~ */
 					if (!(f & DOTEMP) && (!Flag(FPOSIX) ||
 					    (f & DOASNTILDE)) && !saw_eq) {
@@ -1044,13 +1044,13 @@
 						tilde_ok = 1;
 					}
 					break;
-				case ord(':'):
+				case ORD(':'):
 					/* : */
 					/* Note unquoted : for ~ */
 					if (!(f & DOTEMP) && (f & DOASNTILDE))
 						tilde_ok = 1;
 					break;
-				case ord('~'):
+				case ORD('~'):
 					/*
 					 * tilde_ok is reset whenever
 					 * any of ' " $( $(( ${ } are seen.
@@ -1133,7 +1133,7 @@
 	 * ${%var}, string width (-U: screen columns, +U: octets)
 	 */
 	c = ord(sp[1]);
-	if (stype == ord('%') && c == '\0')
+	if ((unsigned int)stype == ORD('%') && c == '\0')
 		return (-1);
 	if (ctype(stype, C_SUB2) && c != '\0') {
 		/* Can't have any modifiers for ${#...} or ${%...} */
@@ -1141,11 +1141,11 @@
 			return (-1);
 		sp++;
 		/* Check for size of array */
-		if ((p = cstrchr(sp, '[')) && (ord(p[1]) == ord('*') ||
-		    ord(p[1]) == ord('@')) && ord(p[2]) == ord(']')) {
+		if ((p = cstrchr(sp, '[')) && (ord(p[1]) == ORD('*') ||
+		    ord(p[1]) == ORD('@')) && ord(p[2]) == ORD(']')) {
 			int n = 0;
 
-			if (stype != ord('#'))
+			if ((unsigned int)stype != ORD('#'))
 				return (-1);
 			vp = global(arrayname(sp));
 			if (vp->flag & (ISSET|ARRAY))
@@ -1154,14 +1154,15 @@
 				if (vp->flag & ISSET)
 					n++;
 			c = n;
-		} else if (c == ord('*') || c == ord('@')) {
-			if (stype != ord('#'))
+		} else if ((unsigned int)c == ORD('*') ||
+		    (unsigned int)c == ORD('@')) {
+			if ((unsigned int)stype != ORD('#'))
 				return (-1);
 			c = e->loc->argc;
 		} else {
 			p = str_val(global(sp));
 			zero_ok = p != null;
-			if (stype == ord('#'))
+			if ((unsigned int)stype == ORD('#'))
 				c = utflen(p);
 			else {
 				/* partial utf_mbswidth reimplementation */
@@ -1196,11 +1197,11 @@
 		xp->str = shf_smprintf(Tf_d, c);
 		return (XSUB);
 	}
-	if (stype == ord('!') && c != '\0' && *word == CSUBST) {
+	if ((unsigned int)stype == ORD('!') && c != '\0' && *word == CSUBST) {
 		sp++;
-		if ((p = cstrchr(sp, '[')) && (ord(p[1]) == ord('*') ||
-		    ord(p[1]) == ord('@')) && ord(p[2]) == ord(']')) {
-			c = ord('!');
+		if ((p = cstrchr(sp, '[')) && (ord(p[1]) == ORD('*') ||
+		    ord(p[1]) == ORD('@')) && ord(p[2]) == ORD(']')) {
+			c = ORD('!');
 			stype = 0;
 			goto arraynames;
 		}
@@ -1214,12 +1215,12 @@
 	/* Check for qualifiers in word part */
 	stype = 0;
 	c = word[slen + 0] == CHAR ? ord(word[slen + 1]) : 0;
-	if (c == ord(':')) {
+	if ((unsigned int)c == ORD(':')) {
 		slen += 2;
 		stype = STYPE_DBL;
 		c = word[slen + 0] == CHAR ? ord(word[slen + 1]) : 0;
 	}
-	if (!stype && c == ord('/')) {
+	if (!stype && (unsigned int)c == ORD('/')) {
 		slen += 2;
 		stype = c;
 		if (word[slen] == ADELIM &&
@@ -1227,8 +1228,9 @@
 			slen += 2;
 			stype |= STYPE_DBL;
 		}
-	} else if (stype == STYPE_DBL && (c == ord(' ') || c == ord('0'))) {
-		stype |= ord('0');
+	} else if (stype == STYPE_DBL && ((unsigned int)c == ORD(' ') ||
+	    (unsigned int)c == ORD('0'))) {
+		stype |= ORD('0');
 	} else if (ctype(c, C_SUB1)) {
 		slen += 2;
 		stype |= c;
@@ -1241,13 +1243,13 @@
 			stype |= STYPE_DBL;
 			slen += 2;
 		}
-	} else if (c == ord('@')) {
+	} else if ((unsigned int)c == ORD('@')) {
 		/* @x where x is command char */
 		switch (c = ord(word[slen + 2]) == CHAR ?
 		    ord(word[slen + 3]) : 0) {
-		case ord('#'):
-		case ord('/'):
-		case ord('Q'):
+		case ORD('#'):
+		case ORD('/'):
+		case ORD('Q'):
 			break;
 		default:
 			return (-1);
@@ -1261,50 +1263,50 @@
 		return (-1);
 
 	c = ord(sp[0]);
-	if (c == ord('*') || c == ord('@')) {
+	if ((unsigned int)c == ORD('*') || (unsigned int)c == ORD('@')) {
 		switch (stype & STYPE_SINGLE) {
 		/* can't assign to a vector */
-		case ord('='):
+		case ORD('='):
 		/* can't trim a vector (yet) */
-		case ord('%'):
-		case ord('#'):
-		case ord('?'):
-		case ord('0'):
-		case ord('/') | STYPE_AT:
-		case ord('/'):
-		case ord('#') | STYPE_AT:
-		case ord('Q') | STYPE_AT:
+		case ORD('%'):
+		case ORD('#'):
+		case ORD('?'):
+		case ORD('0'):
+		case ORD('/') | STYPE_AT:
+		case ORD('/'):
+		case ORD('#') | STYPE_AT:
+		case ORD('Q') | STYPE_AT:
 			return (-1);
 		}
 		if (e->loc->argc == 0) {
 			xp->str = null;
 			xp->var = global(sp);
-			state = c == ord('@') ? XNULLSUB : XSUB;
+			state = (unsigned int)c == ORD('@') ? XNULLSUB : XSUB;
 		} else {
 			xp->u.strv = (const char **)e->loc->argv + 1;
 			xp->str = *xp->u.strv++;
 			/* $@ */
-			xp->split = tobool(c == ord('@'));
+			xp->split = tobool((unsigned int)c == ORD('@'));
 			state = XARG;
 		}
 		/* POSIX 2009? */
 		zero_ok = true;
-	} else if ((p = cstrchr(sp, '[')) && (ord(p[1]) == ord('*') ||
-	    ord(p[1]) == ord('@')) && ord(p[2]) == ord(']')) {
+	} else if ((p = cstrchr(sp, '[')) && (ord(p[1]) == ORD('*') ||
+	    ord(p[1]) == ORD('@')) && ord(p[2]) == ORD(']')) {
 		XPtrV wv;
 
 		switch (stype & STYPE_SINGLE) {
 		/* can't assign to a vector */
-		case ord('='):
+		case ORD('='):
 		/* can't trim a vector (yet) */
-		case ord('%'):
-		case ord('#'):
-		case ord('?'):
-		case ord('0'):
-		case ord('/') | STYPE_AT:
-		case ord('/'):
-		case ord('#') | STYPE_AT:
-		case ord('Q') | STYPE_AT:
+		case ORD('%'):
+		case ORD('#'):
+		case ORD('?'):
+		case ORD('0'):
+		case ORD('/') | STYPE_AT:
+		case ORD('/'):
+		case ORD('#') | STYPE_AT:
+		case ORD('Q') | STYPE_AT:
 			return (-1);
 		}
 		c = 0;
@@ -1314,28 +1316,28 @@
 		for (; vp; vp = vp->u.array) {
 			if (!(vp->flag&ISSET))
 				continue;
-			XPput(wv, c == ord('!') ? shf_smprintf(Tf_lu,
-			    arrayindex(vp)) :
+			XPput(wv, (unsigned int)c == ORD('!') ?
+			    shf_smprintf(Tf_lu, arrayindex(vp)) :
 			    str_val(vp));
 		}
 		if (XPsize(wv) == 0) {
 			xp->str = null;
-			state = ord(p[1]) == ord('@') ? XNULLSUB : XSUB;
+			state = ord(p[1]) == ORD('@') ? XNULLSUB : XSUB;
 			XPfree(wv);
 		} else {
 			XPput(wv, 0);
 			xp->u.strv = (const char **)XPptrv(wv);
 			xp->str = *xp->u.strv++;
 			/* ${foo[@]} */
-			xp->split = tobool(ord(p[1]) == ord('@'));
+			xp->split = tobool(ord(p[1]) == ORD('@'));
 			state = XARG;
 		}
 	} else {
 		xp->var = global(sp);
 		xp->str = str_val(xp->var);
 		/* can't assign things like $! or $1 */
-		if ((stype & STYPE_SINGLE) == ord('=') && !*xp->str &&
-		    ctype(*sp, C_VAR1 | C_DIGIT))
+		if ((unsigned int)(stype & STYPE_SINGLE) == ORD('=') &&
+		    !*xp->str && ctype(*sp, C_VAR1 | C_DIGIT))
 			return (-1);
 		state = XSUB;
 	}
@@ -1346,13 +1348,15 @@
 	    (((stype & STYPE_DBL) ? *xp->str == '\0' : xp->str == null) &&
 	    (state != XARG || (ifs0 || xp->split ?
 	    (xp->u.strv[0] == NULL) : !hasnonempty(xp->u.strv))) ?
-	    ctype(c, C_EQUAL | C_MINUS | C_QUEST) : c == ord('+')))) ||
-	    stype == (ord('0') | STYPE_DBL) || stype == (ord('#') | STYPE_AT) ||
-	    stype == (ord('Q') | STYPE_AT) || (stype & STYPE_CHAR) == ord('/'))
+	    ctype(c, C_EQUAL | C_MINUS | C_QUEST) : (unsigned int)c == ORD('+')))) ||
+	    (unsigned int)stype == (ORD('0') | STYPE_DBL) ||
+	    (unsigned int)stype == (ORD('#') | STYPE_AT) ||
+	    (unsigned int)stype == (ORD('Q') | STYPE_AT) ||
+	    (unsigned int)(stype & STYPE_CHAR) == ORD('/'))
 		/* expand word instead of variable value */
 		state = XBASE;
 	if (Flag(FNOUNSET) && xp->str == null && !zero_ok &&
-	    (ctype(c, C_SUB2) || (state != XBASE && c != ord('+'))))
+	    (ctype(c, C_SUB2) || (state != XBASE && (unsigned int)c != ORD('+'))))
 		errorf(Tf_parm, sp);
 	*stypep = stype;
 	*slenp = slen;
@@ -1491,7 +1495,7 @@
 	char *p, c;
 
 	switch (how & (STYPE_CHAR | STYPE_DBL)) {
-	case ord('#'):
+	case ORD('#'):
 		/* shortest match at beginning */
 		for (p = str; p <= end; p += utf_ptradj(p)) {
 			c = *p; *p = '\0';
@@ -1503,7 +1507,7 @@
 			*p = c;
 		}
 		break;
-	case ord('#') | STYPE_DBL:
+	case ORD('#') | STYPE_DBL:
 		/* longest match at beginning */
 		for (p = end; p >= str; p--) {
 			c = *p; *p = '\0';
@@ -1515,7 +1519,7 @@
 			*p = c;
 		}
 		break;
-	case ord('%'):
+	case ORD('%'):
 		/* shortest match at end */
 		p = end;
 		while (p >= str) {
@@ -1531,7 +1535,7 @@
 				--p;
 		}
 		break;
-	case ord('%') | STYPE_DBL:
+	case ORD('%') | STYPE_DBL:
 		/* longest match at end */
 		for (p = str; p <= end; p++)
 			if (gmatchx(p, pat, false)) {
@@ -1863,7 +1867,7 @@
 	char *p = exp_start;
 
 	/* search for open brace */
-	while ((p = strchr(p, MAGIC)) && ord(p[1]) != ord('{' /*}*/))
+	while ((p = strchr(p, MAGIC)) && ord(p[1]) != ORD('{' /*}*/))
 		p += 2;
 	brace_start = p;
 
@@ -1874,9 +1878,9 @@
 		p += 2;
 		while (*p && count) {
 			if (ISMAGIC(*p++)) {
-				if (ord(*p) == ord('{' /*}*/))
+				if (ord(*p) == ORD('{' /*}*/))
 					++count;
-				else if (ord(*p) == ord(/*{*/ '}'))
+				else if (ord(*p) == ORD(/*{*/ '}'))
 					--count;
 				else if (*p == ',' && count == 1)
 					comma = p;
@@ -1908,9 +1912,9 @@
 	count = 1;
 	for (p = brace_start + 2; p != brace_end; p++) {
 		if (ISMAGIC(*p)) {
-			if (ord(*++p) == ord('{' /*}*/))
+			if (ord(*++p) == ORD('{' /*}*/))
 				++count;
-			else if ((ord(*p) == ord(/*{*/ '}') && --count == 0) ||
+			else if ((ord(*p) == ORD(/*{*/ '}') && --count == 0) ||
 			    (*p == ',' && count == 1)) {
 				char *news;
 				int l1, l2, l3;
diff --git a/src/exec.c b/src/exec.c
index 56a42f6..8330174 100644
--- a/src/exec.c
+++ b/src/exec.c
@@ -23,7 +23,7 @@
 
 #include "sh.h"
 
-__RCSID("$MirOS: src/bin/mksh/exec.c,v 1.199 2017/08/07 21:16:31 tg Exp $");
+__RCSID("$MirOS: src/bin/mksh/exec.c,v 1.201 2017/10/11 21:09:24 tg Exp $");
 
 #ifndef MKSH_DEFAULT_EXECSHELL
 #define MKSH_DEFAULT_EXECSHELL	MKSH_UNIXROOT "/bin/sh"
@@ -953,8 +953,12 @@
 		}
 #ifdef __OS2__
 		/*
-		 * Search shell/interpreter name without directory in PATH
-		 * if specified path does not exist
+		 * On OS/2, the directory structure differs from normal
+		 * Unix, which can make many scripts whose shebang
+		 * hardcodes the path to an interpreter fail (and there
+		 * might be no /usr/bin/env); for user convenience, if
+		 * the specified interpreter is not usable, do a PATH
+		 * search to find it.
 		 */
 		if (mksh_vdirsep(sh) && !search_path(sh, path, X_OK, NULL)) {
 			cp = search_path(_getname(sh), path, X_OK, NULL);
@@ -1168,11 +1172,7 @@
 	char *fpath;
 	union mksh_cchack npath;
 
-	if (mksh_vdirsep(name)
-#ifdef MKSH_DOSPATH
-	    && (strcmp(name, T_builtin) != 0)
-#endif
-	    ) {
+	if (mksh_vdirsep(name)) {
 		insert = 0;
 		/* prevent FPATH search below */
 		flags &= ~FC_FUNC;
diff --git a/src/expr.c b/src/expr.c
index 12989d4..499b961 100644
--- a/src/expr.c
+++ b/src/expr.c
@@ -2,7 +2,7 @@
 
 /*-
  * Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
- *		 2011, 2012, 2013, 2014, 2016, 2017
+ *		 2011, 2012, 2013, 2014, 2016, 2017, 2018
  *	mirabilos <m@mirbsd.org>
  *
  * Provided that these terms and disclaimer and all copyright notices
@@ -23,7 +23,7 @@
 
 #include "sh.h"
 
-__RCSID("$MirOS: src/bin/mksh/expr.c,v 1.100 2017/08/07 21:38:55 tg Exp $");
+__RCSID("$MirOS: src/bin/mksh/expr.c,v 1.103 2018/01/14 01:29:47 tg Exp $");
 
 #define EXPRTOK_DEFNS
 #include "exprtok.h"
@@ -558,9 +558,11 @@
 
 	/* skip whitespace */
  skip_spaces:
-	while (ctype(ord((c = *cp)), C_SPACE))
-		++cp;
-	if (es->tokp == es->expression && c == ord('#')) {
+	--cp;
+	do {
+		c = ord(*++cp);
+	} while (ctype(c, C_SPACE));
+	if (es->tokp == es->expression && (unsigned int)c == ORD('#')) {
 		/* expression begins with # */
 		/* switch to unsigned */
 		es->natural = true;
@@ -575,7 +577,7 @@
 		do {
 			c = ord(*++cp);
 		} while (ctype(c, C_ALNUX));
-		if (c == ord('[')) {
+		if ((unsigned int)c == ORD('[')) {
 			size_t len;
 
 			len = array_ref_len(cp);
@@ -884,7 +886,7 @@
 
 /*
  * Generated from the Unicode Character Database, Version 10.0.0, by
- * MirOS: contrib/code/Snippets/eawparse,v 1.10 2017/07/12 22:47:26 tg Exp $
+ * MirOS: contrib/code/Snippets/eawparse,v 1.12 2017/09/06 16:05:45 tg Exp $
  */
 
 static const struct mb_ucsrange mb_ucs_combining[] = {
@@ -895,16 +897,14 @@
 	{ 0x05C1, 0x05C2 },
 	{ 0x05C4, 0x05C5 },
 	{ 0x05C7, 0x05C7 },
-	{ 0x0600, 0x0605 },
 	{ 0x0610, 0x061A },
 	{ 0x061C, 0x061C },
 	{ 0x064B, 0x065F },
 	{ 0x0670, 0x0670 },
-	{ 0x06D6, 0x06DD },
+	{ 0x06D6, 0x06DC },
 	{ 0x06DF, 0x06E4 },
 	{ 0x06E7, 0x06E8 },
 	{ 0x06EA, 0x06ED },
-	{ 0x070F, 0x070F },
 	{ 0x0711, 0x0711 },
 	{ 0x0730, 0x074A },
 	{ 0x07A6, 0x07B0 },
@@ -914,7 +914,8 @@
 	{ 0x0825, 0x0827 },
 	{ 0x0829, 0x082D },
 	{ 0x0859, 0x085B },
-	{ 0x08D4, 0x0902 },
+	{ 0x08D4, 0x08E1 },
+	{ 0x08E3, 0x0902 },
 	{ 0x093A, 0x093A },
 	{ 0x093C, 0x093C },
 	{ 0x0941, 0x0948 },
diff --git a/src/funcs.c b/src/funcs.c
index 38e66f8..5179192 100644
--- a/src/funcs.c
+++ b/src/funcs.c
@@ -38,7 +38,7 @@
 #endif
 #endif
 
-__RCSID("$MirOS: src/bin/mksh/funcs.c,v 1.350 2017/05/05 22:53:28 tg Exp $");
+__RCSID("$MirOS: src/bin/mksh/funcs.c,v 1.353 2018/01/14 01:26:49 tg Exp $");
 
 #if HAVE_KILLPG
 /*
@@ -594,7 +594,7 @@
 static int
 s_get(void)
 {
-	return (*s_ptr++);
+	return (ord(*s_ptr++));
 }
 
 static void
@@ -751,9 +751,9 @@
 bool
 valid_alias_name(const char *cp)
 {
-	if (ord(*cp) == ord('-'))
+	if (ord(*cp) == ORD('-'))
 		return (false);
-	if (ord(cp[0]) == ord('[') && ord(cp[1]) == ord('[') && !cp[2])
+	if (ord(cp[0]) == ORD('[') && ord(cp[1]) == ORD('[') && !cp[2])
 		return (false);
 	while (*cp)
 		if (ctype(*cp, C_ALIAS))
@@ -2304,9 +2304,9 @@
 			size_t n;
 
 			n = strlen(id);
-			if (n > 3 && ord(id[n - 3]) == ord('[') &&
-			    ord(id[n - 2]) == ord('*') &&
-			    ord(id[n - 1]) == ord(']')) {
+			if (n > 3 && ord(id[n - 3]) == ORD('[') &&
+			    ord(id[n - 2]) == ORD('*') &&
+			    ord(id[n - 1]) == ORD(']')) {
 				strndupx(cp, id, n - 3, ATEMP);
 				id = cp;
 				optc = 3;
@@ -3539,7 +3539,7 @@
 					continue;
 				}
 				if (errno == EPIPE) {
-					/* fake receiving signel */
+					/* fake receiving signal */
 					rv = ksh_sigmask(SIGPIPE);
 				} else {
 					/* an error occured during writing */
diff --git a/src/jobs.c b/src/jobs.c
index 4df98b7..a0b9b79 100644
--- a/src/jobs.c
+++ b/src/jobs.c
@@ -2,7 +2,7 @@
 
 /*-
  * Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2011,
- *		 2012, 2013, 2014, 2015, 2016
+ *		 2012, 2013, 2014, 2015, 2016, 2018
  *	mirabilos <m@mirbsd.org>
  *
  * Provided that these terms and disclaimer and all copyright notices
@@ -23,7 +23,7 @@
 
 #include "sh.h"
 
-__RCSID("$MirOS: src/bin/mksh/jobs.c,v 1.124 2017/08/08 14:30:10 tg Exp $");
+__RCSID("$MirOS: src/bin/mksh/jobs.c,v 1.125 2018/01/05 20:08:34 tg Exp $");
 
 #if HAVE_KILLPG
 #define mksh_killpg		killpg
@@ -1545,7 +1545,9 @@
 	Proc *p;
 	int state;
 	int status;
+#ifdef WCOREDUMP
 	bool coredumped;
+#endif
 	char jobchar = ' ';
 	char buf[64];
 	const char *filler;
@@ -1569,7 +1571,9 @@
 		jobchar = '-';
 
 	for (p = j->proc_list; p != NULL;) {
+#ifdef WCOREDUMP
 		coredumped = false;
+#endif
 		switch (p->state) {
 		case PRUNNING:
 			memcpy(buf, "Running", 8);
@@ -1603,7 +1607,10 @@
 			 * kludge for not reporting 'normal termination
 			 * signals' (i.e. SIGINT, SIGPIPE)
 			 */
-			if (how == JP_SHORT && !coredumped &&
+			if (how == JP_SHORT &&
+#ifdef WCOREDUMP
+			    !coredumped &&
+#endif
 			    (termsig == SIGINT || termsig == SIGPIPE)) {
 				buf[0] = '\0';
 			} else
@@ -1629,14 +1636,22 @@
 		if (how == JP_SHORT) {
 			if (buf[0]) {
 				output = 1;
+#ifdef WCOREDUMP
 				shf_fprintf(shf, "%s%s ",
 				    buf, coredumped ? " (core dumped)" : null);
+#else
+				shf_puts(buf, shf);
+				shf_putchar(' ', shf);
+#endif
 			}
 		} else {
 			output = 1;
 			shf_fprintf(shf, "%-20s %s%s%s", buf, p->command,
 			    p->next ? "|" : null,
-			    coredumped ? " (core dumped)" : null);
+#ifdef WCOREDUMP
+			    coredumped ? " (core dumped)" :
+#endif
+			     null);
 		}
 
 		state = p->state;
diff --git a/src/lex.c b/src/lex.c
index f450221..9300311 100644
--- a/src/lex.c
+++ b/src/lex.c
@@ -2,7 +2,7 @@
 
 /*-
  * 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
  *	mirabilos <m@mirbsd.org>
  *
  * Provided that these terms and disclaimer and all copyright notices
@@ -23,7 +23,7 @@
 
 #include "sh.h"
 
-__RCSID("$MirOS: src/bin/mksh/lex.c,v 1.239 2017/05/05 22:53:29 tg Exp $");
+__RCSID("$MirOS: src/bin/mksh/lex.c,v 1.247 2018/01/14 01:44:01 tg Exp $");
 
 /*
  * states while lexing word
@@ -127,11 +127,11 @@
 static int
 getsc_i(void)
 {
-	o_getsc_r(o_getsc());
+	o_getsc_r((unsigned int)(unsigned char)o_getsc());
 }
 
 #if defined(MKSH_SMALL) && !defined(MKSH_SMALL_BUT_FAST)
-#define getsc()		ord(getsc_i())
+#define getsc()		getsc_i()
 #else
 static int getsc_r(int);
 
@@ -141,7 +141,7 @@
 	o_getsc_r(c);
 }
 
-#define getsc()		ord(getsc_r(o_getsc()))
+#define getsc()		getsc_r((unsigned int)(unsigned char)o_getsc())
 #endif
 
 #define STATE_BSIZE	8
@@ -220,12 +220,14 @@
 	} else {
 		/* normal lexing */
 		state = (cf & HEREDELIM) ? SHEREDELIM : SBASE;
-		while (ctype((c = getsc()), C_BLANK))
-			;
+		do {
+			c = getsc();
+		} while (ctype(c, C_BLANK));
 		if (c == '#') {
 			ignore_backslash_newline++;
-			while (!ctype((c = getsc()), C_NUL | C_LF))
-				;
+			do {
+				c = getsc();
+			} while (!ctype(c, C_NUL | C_LF));
 			ignore_backslash_newline--;
 		}
 		ungetsc(c);
@@ -245,30 +247,32 @@
 	while (!((c = getsc()) == 0 ||
 	    ((state == SBASE || state == SHEREDELIM) && ctype(c, C_LEX1)))) {
 		if (state == SBASE &&
-		    subshell_nesting_type == ord(/*{*/ '}') &&
-		    c == ord(/*{*/ '}'))
+		    subshell_nesting_type == ORD(/*{*/ '}') &&
+		    (unsigned int)c == ORD(/*{*/ '}'))
 			/* possibly end ${ :;} */
 			break;
 		Xcheck(ws, wp);
 		switch (state) {
 		case SADELIM:
-			if (c == ord('('))
+			if ((unsigned int)c == ORD('('))
 				statep->nparen++;
-			else if (c == ord(')'))
+			else if ((unsigned int)c == ORD(')'))
 				statep->nparen--;
-			else if (statep->nparen == 0 && (c == ord(/*{*/ '}') ||
+			else if (statep->nparen == 0 &&
+			    ((unsigned int)c == ORD(/*{*/ '}') ||
 			    c == (int)statep->ls_adelim.delimiter)) {
 				*wp++ = ADELIM;
 				*wp++ = c;
-				if (c == ord(/*{*/ '}') || --statep->ls_adelim.num == 0)
+				if ((unsigned int)c == ORD(/*{*/ '}') ||
+				    --statep->ls_adelim.num == 0)
 					POP_STATE();
-				if (c == ord(/*{*/ '}'))
+				if ((unsigned int)c == ORD(/*{*/ '}'))
 					POP_STATE();
 				break;
 			}
 			/* FALLTHROUGH */
 		case SBASE:
-			if (c == ord('[') && (cf & CMDASN)) {
+			if ((unsigned int)c == ORD('[') && (cf & CMDASN)) {
 				/* temporary */
 				*wp = EOS;
 				if (is_wdvarname(Xstring(ws, wp), false)) {
@@ -284,15 +288,6 @@
 						}
 						afree(tmp, ATEMP);
 						break;
-					} else {
-						Source *s;
-
-						s = pushs(SREREAD,
-						    source->areap);
-						s->start = s->str =
-						    s->u.freeme = tmp;
-						s->next = source;
-						source = s;
 					}
 				}
 				*wp++ = CHAR;
@@ -303,7 +298,7 @@
  Sbase1:		/* includes *(...|...) pattern (*+?@!) */
 			if (ctype(c, C_PATMO)) {
 				c2 = getsc();
-				if (c2 == ord('(' /*)*/)) {
+				if ((unsigned int)c2 == ORD('(' /*)*/)) {
 					*wp++ = OPAT;
 					*wp++ = c;
 					PUSH_STATE(SPATTERN);
@@ -314,7 +309,7 @@
 			/* FALLTHROUGH */
  Sbase2:		/* doesn't include *(...|...) pattern (*+?@!) */
 			switch (c) {
-			case ord('\\'):
+			case ORD('\\'):
  getsc_qchar:
 				if ((c = getsc())) {
 					/* trailing \ is lost */
@@ -322,7 +317,7 @@
 					*wp++ = c;
 				}
 				break;
-			case ord('\''):
+			case ORD('\''):
  open_ssquote_unless_heredoc:
 				if ((cf & HEREDOC))
 					goto store_char;
@@ -330,12 +325,12 @@
 				ignore_backslash_newline++;
 				PUSH_STATE(SSQUOTE);
 				break;
-			case ord('"'):
+			case ORD('"'):
  open_sdquote:
 				*wp++ = OQUOTE;
 				PUSH_STATE(SDQUOTE);
 				break;
-			case ord('$'):
+			case ORD('$'):
 				/*
 				 * processing of dollar sign belongs into
 				 * Subst, except for those which can open
@@ -344,9 +339,9 @@
  subst_dollar_ex:
 				c = getsc();
 				switch (c) {
-				case ord('"'):
+				case ORD('"'):
 					goto open_sdquote;
-				case ord('\''):
+				case ORD('\''):
 					goto open_sequote;
 				default:
 					goto SubstS;
@@ -358,16 +353,16 @@
 
  Subst:
 			switch (c) {
-			case ord('\\'):
+			case ORD('\\'):
 				c = getsc();
 				switch (c) {
-				case ord('"'):
+				case ORD('"'):
 					if ((cf & HEREDOC))
 						goto heredocquote;
 					/* FALLTHROUGH */
-				case ord('\\'):
-				case ord('$'):
-				case ord('`'):
+				case ORD('\\'):
+				case ORD('$'):
+				case ORD('`'):
  store_qchar:
 					*wp++ = QCHAR;
 					*wp++ = c;
@@ -385,12 +380,12 @@
 					break;
 				}
 				break;
-			case ord('$'):
+			case ORD('$'):
 				c = getsc();
  SubstS:
-				if (c == ord('(' /*)*/)) {
+				if ((unsigned int)c == ORD('(' /*)*/)) {
 					c = getsc();
-					if (c == ord('(' /*)*/)) {
+					if ((unsigned int)c == ORD('(' /*)*/)) {
 						*wp++ = EXPRSUB;
 						PUSH_SRETRACE(SASPAREN);
 						statep->nparen = 2;
@@ -407,8 +402,8 @@
 						memcpy(wp, sp, cz);
 						wp += cz;
 					}
-				} else if (c == ord('{' /*}*/)) {
-					if ((c = getsc()) == ord('|')) {
+				} else if ((unsigned int)c == ORD('{' /*}*/)) {
+					if ((unsigned int)(c = getsc()) == ORD('|')) {
 						/*
 						 * non-subenvironment
 						 * value substitution
@@ -429,11 +424,11 @@
 					wp = get_brace_var(&ws, wp);
 					c = getsc();
 					/* allow :# and :% (ksh88 compat) */
-					if (c == ord(':')) {
+					if ((unsigned int)c == ORD(':')) {
 						*wp++ = CHAR;
 						*wp++ = c;
 						c = getsc();
-						if (c == ord(':')) {
+						if ((unsigned int)c == ORD(':')) {
 							*wp++ = CHAR;
 							*wp++ = '0';
 							*wp++ = ADELIM;
@@ -465,7 +460,7 @@
  parse_adelim_slash:
 						*wp++ = CHAR;
 						*wp++ = c;
-						if ((c = getsc()) == ord('/')) {
+						if ((unsigned int)(c = getsc()) == ORD('/')) {
 							*wp++ = c2;
 							*wp++ = c;
 						} else
@@ -479,7 +474,7 @@
 					} else if (c == '@') {
 						c2 = getsc();
 						ungetsc(c2);
-						if (c2 == ord('/')) {
+						if ((unsigned int)c2 == ORD('/')) {
 							c2 = CHAR;
 							goto parse_adelim_slash;
 						}
@@ -528,7 +523,7 @@
 					ungetsc(c);
 				}
 				break;
-			case ord('`'):
+			case ORD('`'):
  subst_gravis:
 				PUSH_STATE(SBQUOTE);
 				*wp++ = COMASUB;
@@ -572,11 +567,11 @@
 			break;
 
 		case SEQUOTE:
-			if (c == ord('\'')) {
+			if ((unsigned int)c == ORD('\'')) {
 				POP_STATE();
 				*wp++ = CQUOTE;
 				ignore_backslash_newline--;
-			} else if (c == ord('\\')) {
+			} else if ((unsigned int)c == ORD('\\')) {
 				if ((c2 = unbksl(true, getsc_i, ungetsc)) == -1)
 					c2 = getsc();
 				if (c2 == 0)
@@ -604,7 +599,7 @@
 			break;
 
 		case SSQUOTE:
-			if (c == ord('\'')) {
+			if ((unsigned int)c == ORD('\'')) {
 				POP_STATE();
 				if ((cf & HEREDOC) || state == SQBRACE)
 					goto store_char;
@@ -617,7 +612,7 @@
 			break;
 
 		case SDQUOTE:
-			if (c == ord('"')) {
+			if ((unsigned int)c == ORD('"')) {
 				POP_STATE();
 				*wp++ = CQUOTE;
 			} else
@@ -626,15 +621,15 @@
 
 		/* $(( ... )) */
 		case SASPAREN:
-			if (c == ord('('))
+			if ((unsigned int)c == ORD('('))
 				statep->nparen++;
-			else if (c == ord(')')) {
+			else if ((unsigned int)c == ORD(')')) {
 				statep->nparen--;
 				if (statep->nparen == 1) {
 					/* end of EXPRSUB */
 					POP_SRETRACE();
 
-					if ((c2 = getsc()) == ord(/*(*/ ')')) {
+					if ((unsigned int)(c2 = getsc()) == ORD(/*(*/ ')')) {
 						cz = strlen(sp) - 2;
 						XcheckN(ws, wp, cz);
 						memcpy(wp, sp + 1, cz);
@@ -666,7 +661,7 @@
 			goto Sbase2;
 
 		case SQBRACE:
-			if (c == ord('\\')) {
+			if ((unsigned int)c == ORD('\\')) {
 				/*
 				 * perform POSIX "quote removal" if the back-
 				 * slash is "special", i.e. same cases as the
@@ -675,26 +670,28 @@
 				 * write QCHAR+c, otherwise CHAR+\+CHAR+c are
 				 * emitted (in heredocquote:)
 				 */
-				if ((c = getsc()) == ord('"') || c == ord('\\') ||
-				    ctype(c, C_DOLAR | C_GRAVE) || c == ord(/*{*/ '}'))
+				if ((unsigned int)(c = getsc()) == ORD('"') ||
+				    (unsigned int)c == ORD('\\') ||
+				    ctype(c, C_DOLAR | C_GRAVE) ||
+				    (unsigned int)c == ORD(/*{*/ '}'))
 					goto store_qchar;
 				goto heredocquote;
 			}
 			goto common_SQBRACE;
 
 		case SBRACE:
-			if (c == ord('\''))
+			if ((unsigned int)c == ORD('\''))
 				goto open_ssquote_unless_heredoc;
-			else if (c == ord('\\'))
+			else if ((unsigned int)c == ORD('\\'))
 				goto getsc_qchar;
  common_SQBRACE:
-			if (c == ord('"'))
+			if ((unsigned int)c == ORD('"'))
 				goto open_sdquote;
-			else if (c == ord('$'))
+			else if ((unsigned int)c == ORD('$'))
 				goto subst_dollar_ex;
-			else if (c == ord('`'))
+			else if ((unsigned int)c == ORD('`'))
 				goto subst_gravis;
-			else if (c != ord(/*{*/ '}'))
+			else if ((unsigned int)c != ORD(/*{*/ '}'))
 				goto store_char;
 			POP_STATE();
 			*wp++ = CSUBST;
@@ -703,16 +700,16 @@
 
 		/* Same as SBASE, except (,|,) treated specially */
 		case STBRACEKORN:
-			if (c == ord('|'))
+			if ((unsigned int)c == ORD('|'))
 				*wp++ = SPAT;
-			else if (c == ord('(')) {
+			else if ((unsigned int)c == ORD('(')) {
 				*wp++ = OPAT;
 				/* simile for @ */
 				*wp++ = ' ';
 				PUSH_STATE(SPATTERN);
 			} else /* FALLTHROUGH */
 		case STBRACEBOURNE:
-			  if (c == ord(/*{*/ '}')) {
+			  if ((unsigned int)c == ORD(/*{*/ '}')) {
 				POP_STATE();
 				*wp++ = CSUBST;
 				*wp++ = /*{*/ '}';
@@ -721,20 +718,20 @@
 			break;
 
 		case SBQUOTE:
-			if (c == ord('`')) {
+			if ((unsigned int)c == ORD('`')) {
 				*wp++ = 0;
 				POP_STATE();
-			} else if (c == ord('\\')) {
+			} else if ((unsigned int)c == ORD('\\')) {
 				switch (c = getsc()) {
 				case 0:
 					/* trailing \ is lost */
 					break;
-				case ord('$'):
-				case ord('`'):
-				case ord('\\'):
+				case ORD('$'):
+				case ORD('`'):
+				case ORD('\\'):
 					*wp++ = c;
 					break;
-				case ord('"'):
+				case ORD('"'):
 					if (statep->ls_bool) {
 						*wp++ = c;
 						break;
@@ -755,10 +752,10 @@
 
 		/* LETEXPR: (( ... )) */
 		case SLETPAREN:
-			if (c == ord(/*(*/ ')')) {
+			if ((unsigned int)c == ORD(/*(*/ ')')) {
 				if (statep->nparen > 0)
 					--statep->nparen;
-				else if ((c2 = getsc()) == ord(/*(*/ ')')) {
+				else if ((unsigned int)(c2 = getsc()) == ORD(/*(*/ ')')) {
 					c = 0;
 					*wp++ = CQUOTE;
 					goto Done;
@@ -780,9 +777,9 @@
 					s->next = source;
 					source = s;
 					ungetsc('(' /*)*/);
-					return (ord('(' /*)*/));
+					return (ORD('(' /*)*/));
 				}
-			} else if (c == ord('('))
+			} else if ((unsigned int)c == ORD('('))
 				/*
 				 * parentheses inside quotes and
 				 * backslashes are lost, but AT&T ksh
@@ -798,26 +795,26 @@
 			 * $ and `...` are not to be treated specially
 			 */
 			switch (c) {
-			case ord('\\'):
+			case ORD('\\'):
 				if ((c = getsc())) {
 					/* trailing \ is lost */
 					*wp++ = QCHAR;
 					*wp++ = c;
 				}
 				break;
-			case ord('\''):
+			case ORD('\''):
 				goto open_ssquote_unless_heredoc;
-			case ord('$'):
-				if ((c2 = getsc()) == ord('\'')) {
+			case ORD('$'):
+				if ((unsigned int)(c2 = getsc()) == ORD('\'')) {
  open_sequote:
 					*wp++ = OQUOTE;
 					ignore_backslash_newline++;
 					PUSH_STATE(SEQUOTE);
 					statep->ls_bool = false;
 					break;
-				} else if (c2 == ord('"')) {
+				} else if ((unsigned int)c2 == ORD('"')) {
 					/* FALLTHROUGH */
-			case ord('"'):
+			case ORD('"'):
 					PUSH_SRETRACE(SHEREDQUOTE);
 					break;
 				}
@@ -831,7 +828,7 @@
 
 		/* " in << or <<- delimiter */
 		case SHEREDQUOTE:
-			if (c != ord('"'))
+			if ((unsigned int)c != ORD('"'))
 				goto Subst;
 			POP_SRETRACE();
 			dp = strnul(sp) - 1;
@@ -844,10 +841,10 @@
 			while ((c = *dp++)) {
 				if (c == '\\') {
 					switch ((c = *dp++)) {
-					case ord('\\'):
-					case ord('"'):
-					case ord('$'):
-					case ord('`'):
+					case ORD('\\'):
+					case ORD('"'):
+					case ORD('$'):
+					case ORD('`'):
 						break;
 					default:
 						*wp++ = CHAR;
@@ -865,12 +862,12 @@
 
 		/* in *(...|...) pattern (*+?@!) */
 		case SPATTERN:
-			if (c == ord(/*(*/ ')')) {
+			if ((unsigned int)c == ORD(/*(*/ ')')) {
 				*wp++ = CPAT;
 				POP_STATE();
-			} else if (c == ord('|')) {
+			} else if ((unsigned int)c == ORD('|')) {
 				*wp++ = SPAT;
-			} else if (c == ord('(')) {
+			} else if ((unsigned int)c == ORD('(')) {
 				*wp++ = OPAT;
 				/* simile for @ */
 				*wp++ = ' ';
@@ -900,7 +897,7 @@
 		iop->unit = c2 == 2 ? ksh_numdig(dp[1]) : c == '<' ? 0 : 1;
 
 		if (c == '&') {
-			if ((c2 = getsc()) != ord('>')) {
+			if ((unsigned int)(c2 = getsc()) != ORD('>')) {
 				ungetsc(c2);
 				goto no_iop;
 			}
@@ -911,22 +908,23 @@
 
 		c2 = getsc();
 		/* <<, >>, <> are ok, >< is not */
-		if (c == c2 || (c == ord('<') && c2 == ord('>'))) {
+		if (c == c2 || ((unsigned int)c == ORD('<') &&
+		    (unsigned int)c2 == ORD('>'))) {
 			iop->ioflag |= c == c2 ?
-			    (c == ord('>') ? IOCAT : IOHERE) : IORDWR;
+			    ((unsigned int)c == ORD('>') ? IOCAT : IOHERE) : IORDWR;
 			if (iop->ioflag == IOHERE) {
-				if ((c2 = getsc()) == ord('-'))
+				if ((unsigned int)(c2 = getsc()) == ORD('-'))
 					iop->ioflag |= IOSKIP;
-				else if (c2 == ord('<'))
+				else if ((unsigned int)c2 == ORD('<'))
 					iop->ioflag |= IOHERESTR;
 				else
 					ungetsc(c2);
 			}
-		} else if (c2 == ord('&'))
-			iop->ioflag |= IODUP | (c == ord('<') ? IORDUP : 0);
+		} else if ((unsigned int)c2 == ORD('&'))
+			iop->ioflag |= IODUP | ((unsigned int)c == ORD('<') ? IORDUP : 0);
 		else {
-			iop->ioflag |= c == ord('>') ? IOWRITE : IOREAD;
-			if (c == ord('>') && c2 == ord('|'))
+			iop->ioflag |= (unsigned int)c == ORD('>') ? IOWRITE : IOREAD;
+			if ((unsigned int)c == ORD('>') && (unsigned int)c2 == ORD('|'))
 				iop->ioflag |= IOCLOB;
 			else
 				ungetsc(c2);
@@ -947,30 +945,32 @@
 		/* free word */
 		Xfree(ws, wp);
 		/* no word, process LEX1 character */
-		if ((c == ord('|')) || (c == ord('&')) || (c == ord(';')) ||
-		    (c == ord('(' /*)*/))) {
+		if (((unsigned int)c == ORD('|')) ||
+		    ((unsigned int)c == ORD('&')) ||
+		    ((unsigned int)c == ORD(';')) ||
+		    ((unsigned int)c == ORD('(' /*)*/))) {
 			if ((c2 = getsc()) == c)
-				c = (c == ord(';')) ? BREAK :
-				    (c == ord('|')) ? LOGOR :
-				    (c == ord('&')) ? LOGAND :
-				    /* c == ord('(' )) */ MDPAREN;
-			else if (c == ord('|') && c2 == ord('&'))
+				c = ((unsigned int)c == ORD(';')) ? BREAK :
+				    ((unsigned int)c == ORD('|')) ? LOGOR :
+				    ((unsigned int)c == ORD('&')) ? LOGAND :
+				    /* (unsigned int)c == ORD('(' )) */ MDPAREN;
+			else if ((unsigned int)c == ORD('|') && (unsigned int)c2 == ORD('&'))
 				c = COPROC;
-			else if (c == ord(';') && c2 == ord('|'))
+			else if ((unsigned int)c == ORD(';') && (unsigned int)c2 == ORD('|'))
 				c = BRKEV;
-			else if (c == ord(';') && c2 == ord('&'))
+			else if ((unsigned int)c == ORD(';') && (unsigned int)c2 == ORD('&'))
 				c = BRKFT;
 			else
 				ungetsc(c2);
 #ifndef MKSH_SMALL
 			if (c == BREAK) {
-				if ((c2 = getsc()) == ord('&'))
+				if ((unsigned int)(c2 = getsc()) == ORD('&'))
 					c = BRKEV;
 				else
 					ungetsc(c2);
 			}
 #endif
-		} else if (c == ord('\n')) {
+		} else if ((unsigned int)c == ORD('\n')) {
 			if (cf & HEREDELIM)
 				ungetsc(c);
 			else {
@@ -1025,7 +1025,7 @@
 
 		if ((cf & KEYWORD) && (p = ktsearch(&keywords, ident, h)) &&
 		    (!(cf & ESACONLY) || p->val.i == ESAC ||
-		    p->val.i == ord(/*{*/ '}'))) {
+		    (unsigned int)p->val.i == ORD(/*{*/ '}'))) {
 			afree(yylval.cp, ATEMP);
 			return (p->val.i);
 		}
@@ -1136,7 +1136,7 @@
 	if (!*eofp) {
 		/* end of here document marker, what to do? */
 		switch (c) {
-		case ord(/*(*/ ')'):
+		case ORD(/*(*/ ')'):
 			if (!subshell_nesting_type)
 				/*-
 				 * not allowed outside $(...) or (...)
@@ -1151,7 +1151,7 @@
 			 * Allow EOF here to commands without trailing
 			 * newlines (mksh -c '...') will work as well.
 			 */
-		case ord('\n'):
+		case ORD('\n'):
 			/* Newline terminates here document marker */
 			goto heredoc_found_terminator;
 		}
@@ -1580,7 +1580,7 @@
 
 				c2 = getsc();
 				ungetsc(c2);
-				if (ord(c2) != ord(/*{*/ '}')) {
+				if (ord(c2) != ORD(/*{*/ '}')) {
 					ungetsc(c);
 					goto out;
 				}
@@ -1588,22 +1588,22 @@
 			goto ps_common;
 		case PS_SAW_BANG:
 			switch (ord(c)) {
-			case ord('@'):
-			case ord('#'):
-			case ord('-'):
-			case ord('?'):
+			case ORD('@'):
+			case ORD('#'):
+			case ORD('-'):
+			case ORD('?'):
 				goto out;
 			}
 			goto ps_common;
 		case PS_INITIAL:
 			switch (ord(c)) {
-			case ord('%'):
+			case ORD('%'):
 				state = PS_SAW_PERCENT;
 				goto next;
-			case ord('#'):
+			case ORD('#'):
 				state = PS_SAW_HASH;
 				goto next;
-			case ord('!'):
+			case ORD('!'):
 				state = PS_SAW_BANG;
 				goto next;
 			}
@@ -1621,7 +1621,7 @@
 			break;
 		case PS_IDENT:
 			if (!ctype(c, C_ALNUX)) {
-				if (ord(c) == ord('[')) {
+				if (ord(c) == ORD('[')) {
 					char *tmp, *p;
 
 					if (!arraysub(&tmp))
@@ -1676,9 +1676,9 @@
 		c = getsc();
 		Xcheck(ws, wp);
 		*wp++ = c;
-		if (ord(c) == ord('['))
+		if (ord(c) == ORD('['))
 			depth++;
-		else if (ord(c) == ord(']'))
+		else if (ord(c) == ORD(']'))
 			depth--;
 	} while (depth > 0 && c && c != '\n');
 
diff --git a/src/main.c b/src/main.c
index b4d7244..ad20c94 100644
--- a/src/main.c
+++ b/src/main.c
@@ -5,7 +5,7 @@
 
 /*-
  * 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
  *	mirabilos <m@mirbsd.org>
  *
  * Provided that these terms and disclaimer and all copyright notices
@@ -34,9 +34,7 @@
 #include <locale.h>
 #endif
 
-__RCSID("$MirOS: src/bin/mksh/main.c,v 1.342 2017/04/28 11:13:47 tg Exp $");
-
-extern char **environ;
+__RCSID("$MirOS: src/bin/mksh/main.c,v 1.347 2018/01/13 21:45:07 tg Exp $");
 
 #ifndef MKSHRC_PATH
 #define MKSHRC_PATH	"~/.mkshrc"
@@ -52,6 +50,7 @@
 static void reclaim(void);
 static void remove_temps(struct temp *);
 static mksh_uari_t rndsetup(void);
+static void init_environ(void);
 #ifdef SIGWINCH
 static void x_sigwinch(int);
 #endif
@@ -242,10 +241,6 @@
 	set_ifs(TC_IFSWS);
 
 #ifdef __OS2__
-	for (i = 0; i < 3; ++i)
-		if (!isatty(i))
-			setmode(i, O_BINARY);
-
 	os2_init(&argc, &argv);
 #endif
 
@@ -401,14 +396,7 @@
 #endif
 
 	/* import environment */
-	if (environ != NULL) {
-		wp = (const char **)environ;
-		while (*wp != NULL) {
-			rndpush(*wp);
-			typeset(*wp, IMPORT | EXPORT, 0, 0, 0);
-			++wp;
-		}
-	}
+	init_environ();
 
 	/* override default PATH regardless of environment */
 #ifdef MKSH_DEFPATH_OVERRIDE
@@ -671,8 +659,7 @@
 		if (Flag(FLOGIN))
 			include(substitute("$HOME/.profile", 0), 0, NULL, true);
 		if (Flag(FTALKING)) {
-			cp = substitute(substitute("${ENV:-" MKSHRC_PATH "}",
-			    0), DOTILDE);
+			cp = substitute("${ENV:-" MKSHRC_PATH "}", DOTILDE);
 			if (cp[0] != '\0')
 				include(cp, 0, NULL, true);
 		}
@@ -1987,3 +1974,111 @@
 
 	mksh_tcset(fd, &cb);
 }
+
+#ifdef MKSH_ENVDIR
+static void
+init_environ(void)
+{
+	char *xp;
+	ssize_t n;
+	XString xs;
+	struct shf *shf;
+	DIR *dirp;
+	struct dirent *dent;
+
+	if ((dirp = opendir(MKSH_ENVDIR)) == NULL) {
+		warningf(false, "cannot read environment from %s: %s",
+		    MKSH_ENVDIR, cstrerror(errno));
+		return;
+	}
+	XinitN(xs, 256, ATEMP);
+ read_envfile:
+	errno = 0;
+	if ((dent = readdir(dirp)) != NULL) {
+		if (skip_varname(dent->d_name, true)[0] == '\0') {
+			xp = shf_smprintf(Tf_sSs, MKSH_ENVDIR, dent->d_name);
+			if (!(shf = shf_open(xp, O_RDONLY, 0, 0))) {
+				warningf(false,
+				    "cannot read environment %s from %s: %s",
+				    dent->d_name, MKSH_ENVDIR,
+				    cstrerror(errno));
+				goto read_envfile;
+			}
+			afree(xp, ATEMP);
+			n = strlen(dent->d_name);
+			xp = Xstring(xs, xp);
+			XcheckN(xs, xp, n + 32);
+			memcpy(xp, dent->d_name, n);
+			xp += n;
+			*xp++ = '=';
+			while ((n = shf_read(xp, Xnleft(xs, xp), shf)) > 0) {
+				xp += n;
+				if (Xnleft(xs, xp) <= 0)
+					XcheckN(xs, xp, Xlength(xs, xp));
+			}
+			if (n < 0) {
+				warningf(false,
+				    "cannot read environment %s from %s: %s",
+				    dent->d_name, MKSH_ENVDIR,
+				    cstrerror(shf_errno(shf)));
+			} else {
+				*xp = '\0';
+				xp = Xstring(xs, xp);
+				rndpush(xp);
+				typeset(xp, IMPORT | EXPORT, 0, 0, 0);
+			}
+			shf_close(shf);
+		}
+		goto read_envfile;
+	} else if (errno)
+		warningf(false, "cannot read environment from %s: %s",
+		    MKSH_ENVDIR, cstrerror(errno));
+	closedir(dirp);
+	Xfree(xs, xp);
+}
+#else
+extern char **environ;
+
+static void
+init_environ(void)
+{
+	const char **wp;
+
+	if (environ == NULL)
+		return;
+
+	wp = (const char **)environ;
+	while (*wp != NULL) {
+		rndpush(*wp);
+		typeset(*wp, IMPORT | EXPORT, 0, 0, 0);
+		++wp;
+	}
+}
+#endif
+
+#ifdef MKSH_EARLY_LOCALE_TRACKING
+void
+recheck_ctype(void)
+{
+	const char *ccp;
+
+	ccp = str_val(global("LC_ALL"));
+	if (ccp == null)
+		ccp = str_val(global("LC_CTYPE"));
+	if (ccp == null)
+		ccp = str_val(global("LANG"));
+	UTFMODE = isuc(ccp);
+#if HAVE_SETLOCALE_CTYPE
+	ccp = setlocale(LC_CTYPE, ccp);
+#if HAVE_LANGINFO_CODESET
+	if (!isuc(ccp))
+		ccp = nl_langinfo(CODESET);
+#endif
+	if (isuc(ccp))
+		UTFMODE = 1;
+#endif
+
+	if (Flag(FPOSIX))
+		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 1205072..e51dcb1 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.279 2017/08/07 21:39:25 tg Exp $");
+__RCSID("$MirOS: src/bin/mksh/misc.c,v 1.291 2018/01/14 00:03:03 tg Exp $");
 
 #define KSH_CHVT_FLAG
 #ifdef MKSH_SMALL
@@ -696,14 +696,14 @@
 		if (!(c = *p++))
 			return (false);
 		/* some specials */
-		if (ord(c) == ord('*') || ord(c) == ord('?')) {
+		if (ord(c) == ORD('*') || ord(c) == ORD('?')) {
 			/* easy glob, accept */
 			saw_glob = true;
-		} else if (ord(c) == ord('[')) {
+		} else if (ord(c) == ORD('[')) {
 			/* bracket expression; eat negation and initial ] */
-			if (ISMAGIC(p[0]) && ord(p[1]) == ord('!'))
+			if (ISMAGIC(p[0]) && ord(p[1]) == ORD('!'))
 				p += 2;
-			if (ISMAGIC(p[0]) && ord(p[1]) == ord(']'))
+			if (ISMAGIC(p[0]) && ord(p[1]) == ORD(']'))
 				p += 2;
 			/* check next string part */
 			s = p;
@@ -715,27 +715,27 @@
 				if (!(c = *s++))
 					return (false);
 				/* terminating bracket? */
-				if (ord(c) == ord(']')) {
+				if (ord(c) == ORD(']')) {
 					/* accept and continue */
 					p = s;
 					saw_glob = true;
 					break;
 				}
 				/* sub-bracket expressions */
-				if (ord(c) == ord('[') && (
+				if (ord(c) == ORD('[') && (
 				    /* collating element? */
-				    ord(*s) == ord('.') ||
+				    ord(*s) == ORD('.') ||
 				    /* equivalence class? */
-				    ord(*s) == ord('=') ||
+				    ord(*s) == ORD('=') ||
 				    /* character class? */
-				    ord(*s) == ord(':'))) {
+				    ord(*s) == ORD(':'))) {
 					/* must stop with exactly the same c */
 					subc = *s++;
 					/* arbitrarily many chars in betwixt */
 					while ((c = *s++))
 						/* but only this sequence... */
 						if (c == subc && ISMAGIC(*s) &&
-						    ord(s[1]) == ord(']')) {
+						    ord(s[1]) == ORD(']')) {
 							/* accept, terminate */
 							s += 2;
 							break;
@@ -751,7 +751,7 @@
 			/* opening pattern */
 			saw_glob = true;
 			++nest;
-		} else if (ord(c) == ord(/*(*/ ')')) {
+		} else if (ord(c) == ORD(/*(*/ ')')) {
 			/* closing pattern */
 			if (nest)
 				--nest;
@@ -785,24 +785,24 @@
 			continue;
 		}
 		switch (ord(*p++)) {
-		case ord('['):
+		case ORD('['):
 			/* BSD cclass extension? */
-			if (ISMAGIC(p[0]) && ord(p[1]) == ord('[') &&
-			    ord(p[2]) == ord(':') &&
+			if (ISMAGIC(p[0]) && ord(p[1]) == ORD('[') &&
+			    ord(p[2]) == ORD(':') &&
 			    ctype((pc = p[3]), C_ANGLE) &&
-			    ord(p[4]) == ord(':') &&
-			    ISMAGIC(p[5]) && ord(p[6]) == ord(']') &&
-			    ISMAGIC(p[7]) && ord(p[8]) == ord(']')) {
+			    ord(p[4]) == ORD(':') &&
+			    ISMAGIC(p[5]) && ord(p[6]) == ORD(']') &&
+			    ISMAGIC(p[7]) && ord(p[8]) == ORD(']')) {
 				/* zero-length match */
 				--s;
 				p += 9;
 				/* word begin? */
-				if (ord(pc) == ord('<') &&
+				if (ord(pc) == ORD('<') &&
 				    !ctype(sl, C_ALNUX) &&
 				    ctype(sc, C_ALNUX))
 					break;
 				/* word end? */
-				if (ord(pc) == ord('>') &&
+				if (ord(pc) == ORD('>') &&
 				    ctype(sl, C_ALNUX) &&
 				    !ctype(sc, C_ALNUX))
 					break;
@@ -813,7 +813,7 @@
 				return (0);
 			break;
 
-		case ord('?'):
+		case ORD('?'):
 			if (sc == 0)
 				return (0);
 			if (UTFMODE) {
@@ -822,7 +822,7 @@
 			}
 			break;
 
-		case ord('*'):
+		case ORD('*'):
 			if (p == pe)
 				return (1);
 			s--;
@@ -838,14 +838,14 @@
 		 */
 
 		/* matches one or more times */
-		case 0x80|ord('+'):
+		case ORD('+') | 0x80:
 		/* matches zero or more times */
-		case 0x80|ord('*'):
+		case ORD('*') | 0x80:
 			if (!(prest = pat_scan(p, pe, false)))
 				return (0);
 			s--;
 			/* take care of zero matches */
-			if (ord(p[-1]) == (0x80 | ord('*')) &&
+			if (ord(p[-1]) == (0x80 | ORD('*')) &&
 			    do_gmatch(s, se, prest, pe, smin))
 				return (1);
 			for (psub = p; ; psub = pnext) {
@@ -863,16 +863,16 @@
 			return (0);
 
 		/* matches zero or once */
-		case 0x80|ord('?'):
+		case ORD('?') | 0x80:
 		/* matches one of the patterns */
-		case 0x80|ord('@'):
+		case ORD('@') | 0x80:
 		/* simile for @ */
-		case 0x80|ord(' '):
+		case ORD(' ') | 0x80:
 			if (!(prest = pat_scan(p, pe, false)))
 				return (0);
 			s--;
 			/* Take care of zero matches */
-			if (ord(p[-1]) == (0x80 | ord('?')) &&
+			if (ord(p[-1]) == (0x80 | ORD('?')) &&
 			    do_gmatch(s, se, prest, pe, smin))
 				return (1);
 			for (psub = p; ; psub = pnext) {
@@ -889,7 +889,7 @@
 			return (0);
 
 		/* matches none of the patterns */
-		case 0x80|ord('!'):
+		case ORD('!') | 0x80:
 			if (!(prest = pat_scan(p, pe, false)))
 				return (0);
 			s--;
@@ -966,12 +966,12 @@
 	char *subp;
 
 	/* check for negation */
-	if (ISMAGIC(p[0]) && ord(p[1]) == ord('!')) {
+	if (ISMAGIC(p[0]) && ord(p[1]) == ORD('!')) {
 		p += 2;
 		negated = true;
 	}
 	/* make initial ] non-MAGIC */
-	if (ISMAGIC(p[0]) && ord(p[1]) == ord(']'))
+	if (ISMAGIC(p[0]) && ord(p[1]) == ORD(']'))
 		++p;
 	/* iterate over bracket expression, debunk()ing on the fly */
 	while ((c = *p++)) {
@@ -982,18 +982,18 @@
 			if (!(c = *p++))
 				break;
 			/* terminating bracket? */
-			if (ord(c) == ord(']')) {
+			if (ord(c) == ORD(']')) {
 				/* accept and return */
 				return (found != negated ? p : NULL);
 			}
 			/* sub-bracket expressions */
-			if (ord(c) == ord('[') && (
+			if (ord(c) == ORD('[') && (
 			    /* collating element? */
-			    ord(*p) == ord('.') ||
+			    ord(*p) == ORD('.') ||
 			    /* equivalence class? */
-			    ord(*p) == ord('=') ||
+			    ord(*p) == ORD('=') ||
 			    /* character class? */
-			    ord(*p) == ord(':'))) {
+			    ord(*p) == ORD(':'))) {
 				/* must stop with exactly the same c */
 				subc = *p++;
 				/* save away start of substring */
@@ -1002,7 +1002,7 @@
 				while ((c = *p++))
 					/* but only this sequence... */
 					if (c == subc && ISMAGIC(*p) &&
-					    ord(p[1]) == ord(']')) {
+					    ord(p[1]) == ORD(']')) {
 						/* accept, terminate */
 						p += 2;
 						break;
@@ -1015,7 +1015,7 @@
 				debunk(subp, subp, p - s - 3 + 1);
  cclass_common:
 				/* whither subexpression */
-				if (ord(subc) == ord(':')) {
+				if (ord(subc) == ORD(':')) {
 					const struct cclass *cls = cclasses;
 
 					/* search for name in cclass list */
@@ -1055,9 +1055,9 @@
 			}
 		}
 		/* range expression? */
-		if (!(ISMAGIC(p[0]) && ord(p[1]) == ord('-') &&
+		if (!(ISMAGIC(p[0]) && ord(p[1]) == ORD('-') &&
 		    /* not terminating bracket? */
-		    (!ISMAGIC(p[2]) || ord(p[3]) != ord(']')))) {
+		    (!ISMAGIC(p[2]) || ord(p[3]) != ORD(']')))) {
 			/* no, check single match */
 			if (sc == c)
 				/* note: sc is never NUL */
@@ -1079,13 +1079,13 @@
 			if (!(c = *p++))
 				break;
 			/* sub-bracket expressions */
-			if (ord(c) == ord('[') && (
+			if (ord(c) == ORD('[') && (
 			    /* collating element? */
-			    ord(*p) == ord('.') ||
+			    ord(*p) == ORD('.') ||
 			    /* equivalence class? */
-			    ord(*p) == ord('=') ||
+			    ord(*p) == ORD('=') ||
 			    /* character class? */
-			    ord(*p) == ord(':'))) {
+			    ord(*p) == ORD(':'))) {
 				/* must stop with exactly the same c */
 				subc = *p++;
 				/* save away start of substring */
@@ -1094,7 +1094,7 @@
 				while ((c = *p++))
 					/* but only this sequence... */
 					if (c == subc && ISMAGIC(*p) &&
-					    ord(p[1]) == ord(']')) {
+					    ord(p[1]) == ORD(']')) {
 						/* accept, terminate */
 						p += 2;
 						break;
@@ -1106,14 +1106,14 @@
 				strndupx(subp, s, p - s - 3, ATEMP);
 				debunk(subp, subp, p - s - 3 + 1);
 				/* whither subexpression */
-				if (ord(subc) == ord(':')) {
+				if (ord(subc) == ORD(':')) {
 					/* oops, not a range */
 
 					/* match single previous char */
 					if (lc && (sc == lc))
 						found = true;
 					/* match hyphen-minus */
-					if (ord(sc) == ord('-'))
+					if (ord(sc) == ORD('-'))
 						found = true;
 					/* handle cclass common part */
 					goto cclass_common;
@@ -1151,7 +1151,7 @@
 		/* otherwise, just go on with the pattern string */
 	}
 	/* if we broke here, the bracket expression was invalid */
-	if (ord(sc) == ord('['))
+	if (ord(sc) == ORD('['))
 		/* initial opening bracket as literal match */
 		return (pat);
 	/* or rather no match */
@@ -1661,6 +1661,15 @@
 	if (mksh_abspath(upath)) {
 		/* upath is an absolute pathname */
 		strdupx(ipath, upath, ATEMP);
+#ifdef MKSH_DOSPATH
+	} else if (mksh_drvltr(upath)) {
+		/* upath is a drive-relative pathname */
+		if (getdrvwd(&ldest, ord(*upath)))
+			return (NULL);
+		/* A:foo -> A:/cwd/foo; A: -> A:/cwd */
+		ipath = shf_smprintf(Tf_sss, ldest,
+		    upath[2] ? "/" : "", upath + 2);
+#endif
 	} else {
 		/* upath is a relative pathname, prepend cwd */
 		if ((tp = ksh_get_wd()) == NULL || !mksh_abspath(tp))
@@ -1695,6 +1704,7 @@
 				continue;
 			else if (len == 2 && tp[1] == '.') {
 				/* strip off last pathname component */
+				/*XXX consider a rooted pathname */
 				while (xp > Xstring(xs, xp))
 					if (mksh_cdirsep(*--xp))
 						break;
@@ -1762,11 +1772,23 @@
 			 * restart if symlink target is an absolute path,
 			 * otherwise continue with currently resolved prefix
 			 */
+#ifdef MKSH_DOSPATH
+ assemble_symlink:
+#endif
 			/* append rest of current input path to link target */
 			tp = shf_smprintf(Tf_sss, ldest, *ip ? "/" : "", ip);
 			afree(ipath, ATEMP);
 			ip = ipath = tp;
-			if (!mksh_abspath(ldest)) {
+			if (!mksh_abspath(ipath)) {
+#ifdef MKSH_DOSPATH
+				/* symlink target might be drive-relative */
+				if (mksh_drvltr(ipath)) {
+					if (getdrvwd(&ldest, ord(*ipath)))
+						goto notfound;
+					ip += 2;
+					goto assemble_symlink;
+				}
+#endif
 				/* symlink target is a relative path */
 				xp = Xrestpos(xs, xp, pos);
 			} else
@@ -1775,7 +1797,7 @@
 				/* symlink target is an absolute path */
 				xp = Xstring(xs, xp);
  beginning_of_a_pathname:
-				/* assert: mksh_cdirsep((ip == ipath)[0]) */
+				/* assert: mksh_abspath(ip == ipath) */
 				/* assert: xp == xs.beg => start of path */
 
 				/* exactly two leading slashes? (SUSv4 3.266) */
@@ -1783,6 +1805,14 @@
 					/* keep them, e.g. for UNC pathnames */
 					Xput(xs, xp, '/');
 				}
+#ifdef MKSH_DOSPATH
+				/* drive letter? */
+				if (mksh_drvltr(ip)) {
+					/* keep it */
+					Xput(xs, xp, *ip++);
+					Xput(xs, xp, *ip++);
+				}
+#endif
 			}
 		}
 		/* otherwise (no symlink) merely go on */
@@ -1932,6 +1962,15 @@
  * ..					..
  * ./foo				foo
  * foo/../../../bar			../../bar
+ * C:/foo/../..				C:/
+ * C:.					C:
+ * C:..					C:..
+ * C:foo/../../blah			C:../blah
+ *
+ * XXX consider a rooted pathname: we cannot really 'cd ..' for
+ * pathnames like: '/', 'c:/', '//foo', '//foo/', '/@unixroot/'
+ * (no effect), 'c:', 'c:.' (effect is retaining the '../') but
+ * we need to honour this throughout the shell
  */
 void
 simplify_path(char *p)
@@ -1939,6 +1978,17 @@
 	char *dp, *ip, *sp, *tp;
 	size_t len;
 	bool needslash;
+#ifdef MKSH_DOSPATH
+	bool needdot = true;
+
+	/* keep drive letter */
+	if (mksh_drvltr(p)) {
+		p += 2;
+		needdot = false;
+	}
+#else
+#define needdot true
+#endif
 
 	switch (*p) {
 	case 0:
@@ -1977,7 +2027,7 @@
 				/* just continue with the next one */
 				continue;
 			else if (len == 2 && tp[1] == '.') {
-				/* parent level, but how? */
+				/* parent level, but how? (see above) */
 				if (mksh_abspath(p))
 					/* absolute path, only one way */
 					goto strip_last_component;
@@ -2016,10 +2066,15 @@
 		needslash = true;
 		/* try next component */
 	}
-	if (dp == p)
-		/* empty path -> dot */
-		*dp++ = needslash ? '/' : '.';
+	if (dp == p) {
+		/* empty path -> dot (or slash, when absolute) */
+		if (needslash)
+			*dp++ = '/';
+		else if (needdot)
+			*dp++ = '.';
+	}
 	*dp = '\0';
+#undef needdot
 }
 
 void
@@ -2133,6 +2188,18 @@
 		return (2);
 	}
 
+#ifdef MKSH_DOSPATH
+	tryp = NULL;
+	if (mksh_drvltr(dir) && !mksh_cdirsep(dir[2]) &&
+	    !getdrvwd(&tryp, ord(*dir))) {
+		dir = shf_smprintf(Tf_sss, tryp,
+		    dir[2] ? "/" : "", dir + 2);
+		afree(tryp, ATEMP);
+		afree(allocd, ATEMP);
+		allocd = dir;
+	}
+#endif
+
 #ifdef MKSH__NO_PATH_MAX
 	/* only a first guess; make_path will enlarge xs if necessary */
 	XinitN(xs, 1024, ATEMP);
diff --git a/src/os2.c b/src/os2.c
index fc27d5a..2bc63ed 100644
--- a/src/os2.c
+++ b/src/os2.c
@@ -1,5 +1,5 @@
 /*-
- * Copyright (c) 2015
+ * Copyright (c) 2015, 2017
  *	KO Myung-Hun <komh@chollian.net>
  * Copyright (c) 2017
  *	mirabilos <m@mirbsd.org>
@@ -26,18 +26,18 @@
 #include "sh.h"
 
 #include <klibc/startup.h>
+#include <errno.h>
 #include <io.h>
 #include <unistd.h>
 #include <process.h>
 
-__RCSID("$MirOS: src/bin/mksh/os2.c,v 1.2 2017/04/29 22:04:29 tg Exp $");
+__RCSID("$MirOS: src/bin/mksh/os2.c,v 1.8 2017/12/22 16:41:42 tg Exp $");
 
 static char *remove_trailing_dots(char *);
 static int access_stat_ex(int (*)(), const char *, void *);
 static int test_exec_exist(const char *, char *);
 static void response(int *, const char ***);
 static char *make_response_file(char * const *);
-static void env_slashify(void);
 static void add_temp(const char *);
 static void cleanup_temps(void);
 static void cleanup(void);
@@ -169,44 +169,12 @@
 	}
 }
 
-/*
- * Convert backslashes of environmental variables to forward slahes.
- * A backslash may be used as an escaped character when doing 'echo'.
- * This leads to an unexpected behavior.
- */
-static void
-env_slashify(void)
-{
-	/*
-	 * PATH and TMPDIR are used by OS/2 as well. That is, they may
-	 * have backslashes as a directory separator.
-	 * BEGINLIBPATH and ENDLIBPATH are special variables on OS/2.
-	 */
-	const char *var_list[] = {
-		"PATH",
-		"TMPDIR",
-		"BEGINLIBPATH",
-		"ENDLIBPATH",
-		NULL
-	};
-	const char **var;
-	char *value;
-
-	for (var = var_list; *var; var++) {
-		value = getenv(*var);
-
-		if (value)
-			_fnslashify(value);
-	}
-}
-
 void
 os2_init(int *argcp, const char ***argvp)
 {
 	response(argcp, argvp);
 
 	init_extlibpath();
-	env_slashify();
 
 	if (!isatty(STDIN_FILENO))
 		setmode(STDIN_FILENO, O_BINARY);
@@ -361,49 +329,30 @@
 	return (real_name);
 }
 
-/* OS/2 can process a command line up to 32 KiB */
-#define MAX_CMD_LINE_LEN 32768
-
 /* make a response file to pass a very long command line */
 static char *
 make_response_file(char * const *argv)
 {
 	char rsp_name_arg[] = "@mksh-rsp-XXXXXX";
 	char *rsp_name = &rsp_name_arg[1];
-	int arg_len = 0;
 	int i;
+	int fd;
+	char *result;
 
-	for (i = 0; argv[i]; i++)
-		arg_len += strlen(argv[i]) + 1;
+	if ((fd = mkstemp(rsp_name)) == -1)
+		return (NULL);
 
-	/*
-	 * If a length of command line is longer than MAX_CMD_LINE_LEN, then
-	 * use a response file. OS/2 cannot process a command line longer
-	 * than 32K. Of course, a response file cannot be recognised by a
-	 * normal OS/2 program, that is, neither non-EMX or non-kLIBC. But
-	 * it cannot accept a command line longer than 32K in itself. So
-	 * using a response file in this case, is an acceptable solution.
-	 */
-	if (arg_len > MAX_CMD_LINE_LEN) {
-		int fd;
-		char *result;
-
-		if ((fd = mkstemp(rsp_name)) == -1)
-			return (NULL);
-
-		/* write all the arguments except a 0th program name */
-		for (i = 1; argv[i]; i++) {
-			write(fd, argv[i], strlen(argv[i]));
-			write(fd, "\n", 1);
-		}
-
-		close(fd);
-		add_temp(rsp_name);
-		strdupx(result, rsp_name_arg, ATEMP);
-		return (result);
+	/* write all the arguments except a 0th program name */
+	for (i = 1; argv[i]; i++) {
+		write(fd, argv[i], strlen(argv[i]));
+		write(fd, "\n", 1);
 	}
 
-	return (NULL);
+	close(fd);
+	add_temp(rsp_name);
+	strdupx(result, rsp_name_arg, ATEMP);
+
+	return (result);
 }
 
 /* alias of execve() */
@@ -416,12 +365,12 @@
 	const char *exec_name;
 	FILE *fp;
 	char sign[2];
-	char *rsp_argv[3];
-	char *rsp_name_arg;
 	int pid;
 	int status;
 	int fd;
 	int rc;
+	int saved_mode;
+	int saved_errno;
 
 	/*
 	 * #! /bin/sh : append .exe
@@ -461,23 +410,41 @@
 	if (errno == ENOEXEC)
 		return (-1);
 
-	rsp_name_arg = make_response_file(argv);
-
-	if (rsp_name_arg) {
-		rsp_argv[0] = argv[0];
-		rsp_argv[1] = rsp_name_arg;
-		rsp_argv[2] = NULL;
-
-		argv = rsp_argv;
-	}
+	/*
+	 * Normal OS/2 programs expect that standard IOs, especially stdin,
+	 * are opened in text mode at the startup. By the way, on OS/2 kLIBC
+	 * child processes inherit a translation mode of a parent process.
+	 * As a result, if stdin is set to binary mode in a parent process,
+	 * stdin of child processes is opened in binary mode as well at the
+	 * startup. In this case, some programs such as sed suffer from CR.
+	 */
+	saved_mode = setmode(STDIN_FILENO, O_TEXT);
 
 	pid = spawnve(P_NOWAIT, exec_name, argv, envp);
+	saved_errno = errno;
 
-	afree(rsp_name_arg, ATEMP);
+	/* arguments too long? */
+	if (pid == -1 && saved_errno == EINVAL) {
+		/* retry with a response file */
+		char *rsp_name_arg = make_response_file(argv);
+
+		if (rsp_name_arg) {
+			char *rsp_argv[3] = { argv[0], rsp_name_arg, NULL };
+
+			pid = spawnve(P_NOWAIT, exec_name, rsp_argv, envp);
+			saved_errno = errno;
+
+			afree(rsp_name_arg, ATEMP);
+		}
+	}
+
+	/* restore translation mode of stdin */
+	setmode(STDIN_FILENO, saved_mode);
 
 	if (pid == -1) {
 		cleanup_temps();
 
+		errno = saved_errno;
 		return (-1);
 	}
 
@@ -557,3 +524,52 @@
 {
 	cleanup_temps();
 }
+
+int
+getdrvwd(char **cpp, unsigned int drvltr)
+{
+	PBYTE cp;
+	ULONG sz;
+	APIRET rc;
+	ULONG drvno;
+
+	if (DosQuerySysInfo(QSV_MAX_PATH_LENGTH, QSV_MAX_PATH_LENGTH,
+	    &sz, sizeof(sz)) != 0) {
+		errno = EDOOFUS;
+		return (-1);
+	}
+
+	/* allocate 'X:/' plus sz plus NUL */
+	checkoktoadd((size_t)sz, (size_t)4);
+	cp = aresize(*cpp, (size_t)sz + (size_t)4, ATEMP);
+	cp[0] = ksh_toupper(drvltr);
+	cp[1] = ':';
+	cp[2] = '/';
+	drvno = ksh_numuc(cp[0]) + 1;
+	/* NUL is part of space within buffer passed */
+	++sz;
+	if ((rc = DosQueryCurrentDir(drvno, cp + 3, &sz)) == 0) {
+		/* success! */
+		*cpp = cp;
+		return (0);
+	}
+	afree(cp, ATEMP);
+	*cpp = NULL;
+	switch (rc) {
+	case 15: /* invalid drive */
+		errno = ENOTBLK;
+		break;
+	case 26: /* not dos disk */
+		errno = ENODEV;
+		break;
+	case 108: /* drive locked */
+		errno = EDEADLK;
+		break;
+	case 111: /* buffer overflow */
+		errno = ENAMETOOLONG;
+		break;
+	default:
+		errno = EINVAL;
+	}
+	return (-1);
+}
diff --git a/src/sh.h b/src/sh.h
index 88883cb..53629b1 100644
--- a/src/sh.h
+++ b/src/sh.h
@@ -10,7 +10,7 @@
 
 /*-
  * Copyright © 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
- *	       2011, 2012, 2013, 2014, 2015, 2016, 2017
+ *	       2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018
  *	mirabilos <m@mirbsd.org>
  *
  * Provided that these terms and disclaimer and all copyright notices
@@ -182,9 +182,9 @@
 #endif
 
 #ifdef EXTERN
-__RCSID("$MirOS: src/bin/mksh/sh.h,v 1.841 2017/08/29 13:38:31 tg Exp $");
+__RCSID("$MirOS: src/bin/mksh/sh.h,v 1.858 2018/01/14 01:47:36 tg Exp $");
 #endif
-#define MKSH_VERSION "R56 2017/08/29"
+#define MKSH_VERSION "R56 2018/01/14"
 
 /* arithmetic types: C implementation */
 #if !HAVE_CAN_INTTYPES
@@ -556,7 +556,7 @@
  * low-bit7 at least on cp1047 so YMMV
  */
 #define MAGIC		KSH_BEL	/* prefix for *?[!{,} during expand */
-#define ISMAGIC(c)	(ord(c) == ord(MAGIC))
+#define ISMAGIC(c)	(ord(c) == ORD(MAGIC))
 
 EXTERN const char *safe_prompt; /* safe prompt if PS1 substitution fails */
 
@@ -643,7 +643,7 @@
 #endif
 #endif
 
-#if (!defined(MKSH_BUILDMAKEFILE4BSD) && !defined(MKSH_BUILDSH)) || (MKSH_BUILD_R != 562)
+#if (!defined(MKSH_BUILDMAKEFILE4BSD) && !defined(MKSH_BUILDSH)) || (MKSH_BUILD_R != 563)
 #error Must run Build.sh to compile this.
 extern void thiswillneverbedefinedIhope(void);
 int
@@ -804,7 +804,7 @@
 struct yyrecursive_state;
 
 EXTERN struct sretrace_info *retrace_info;
-EXTERN int subshell_nesting_type;
+EXTERN unsigned int subshell_nesting_type;
 
 extern struct env {
 	ALLOC_ITEM alloc_INT;	/* internal, do not touch */
@@ -1469,7 +1469,26 @@
 #define C_UNDER	CiUNDER		/* _	underscore */
 
 /* identity transform of octet */
-#define ord(c)		((unsigned int)(unsigned char)(c))
+#if defined(DEBUG) && defined(__GNUC__) && !defined(__ICC) && \
+    !defined(__INTEL_COMPILER) && !defined(__SUNPRO_C)
+extern unsigned int eek_ord;
+#define ORD(c)	((size_t)(c) > 0xFF ? eek_ord : \
+		    ((unsigned int)(unsigned char)(c)))
+#define ord(c)	__builtin_choose_expr(				\
+    __builtin_types_compatible_p(__typeof__(c), char) ||	\
+    __builtin_types_compatible_p(__typeof__(c), unsigned char),	\
+    ((unsigned int)(unsigned char)(c)), ({			\
+	size_t ord_c = (c);					\
+								\
+	if (ord_c > (size_t)0xFFU)				\
+		internal_errorf("%s:%d:ord(%zX)",		\
+		    __FILE__, __LINE__, ord_c);			\
+	((unsigned int)(unsigned char)(ord_c));			\
+}))
+#else
+#define ord(c)	((unsigned int)(unsigned char)(c))
+#define ORD(c)	ord(c) /* may evaluate arguments twice */
+#endif
 #if defined(MKSH_EBCDIC) || defined(MKSH_FAUX_EBCDIC)
 EXTERN unsigned short ebcdic_map[256];
 EXTERN unsigned char ebcdic_rtt_toascii[256];
@@ -1492,20 +1511,22 @@
 #ifdef MKSH_EBCDIC
 #define ksh_isctrl(c)	(ord(c) < 0x40 || ord(c) == 0xFF)
 #else
-#define ksh_isctrl(c)	((ord(c) & 0x7F) < 0x20 || (c) == 0x7F)
+#define ksh_isctrl(c)	((ord(c) & 0x7F) < 0x20 || ord(c) == 0x7F)
 #endif
 /* new fast character classes */
 #define ctype(c,t)	tobool(ksh_ctypes[ord(c)] & (t))
+#define cinttype(c,t)	((c) >= 0 && (c) <= 0xFF ? \
+			tobool(ksh_ctypes[(unsigned char)(c)] & (t)) : false)
 /* helper functions */
 #define ksh_isdash(s)	tobool(ord((s)[0]) == '-' && ord((s)[1]) == '\0')
 /* invariant distance even in EBCDIC */
 #define ksh_tolower(c)	(ctype(c, C_UPPER) ? (c) - 'A' + 'a' : (c))
 #define ksh_toupper(c)	(ctype(c, C_LOWER) ? (c) - 'a' + 'A' : (c))
 /* strictly speaking rtt2asc() here, but this works even in EBCDIC */
-#define ksh_numdig(c)	(ord(c) - ord('0'))
+#define ksh_numdig(c)	(ord(c) - ORD('0'))
 #define ksh_numuc(c)	(rtt2asc(c) - rtt2asc('A'))
 #define ksh_numlc(c)	(rtt2asc(c) - rtt2asc('a'))
-#define ksh_toctrl(c)	asc2rtt(ord(c) == ord('?') ? 0x7F : rtt2asc(c) & 0x9F)
+#define ksh_toctrl(c)	asc2rtt(ord(c) == ORD('?') ? 0x7F : rtt2asc(c) & 0x9F)
 #define ksh_unctrl(c)	asc2rtt(rtt2asc(c) ^ 0x40U)
 
 /* Argument parsing for built-in commands and getopts command */
@@ -1599,7 +1620,7 @@
 #define shf_fileno(shf)		((shf)->fd)
 #define shf_setfileno(shf,nfd)	((shf)->fd = (nfd))
 #define shf_getc_i(shf)		((shf)->rnleft > 0 ? \
-				    (shf)->rnleft--, *(shf)->rp++ : \
+				    (shf)->rnleft--, (int)ord(*(shf)->rp++) : \
 				    shf_getchar(shf))
 #define shf_putc_i(c, shf)	((shf)->wnleft == 0 ? \
 				    shf_putchar((uint8_t)(c), (shf)) : \
@@ -2500,6 +2521,7 @@
     MKSH_A_FORMAT(__printf__, 1, 2);
 int can_seek(int);
 void initio(void);
+void recheck_ctype(void);
 int ksh_dup2(int, int, bool);
 short savefd(int);
 void restfd(int, int);
@@ -2734,27 +2756,32 @@
 #endif
 
 #ifdef MKSH_DOSPATH
+#define mksh_drvltr(s)			__extension__({			\
+	const char *mksh_drvltr_s = (s);				\
+	(ctype(mksh_drvltr_s[0], C_ALPHA) && mksh_drvltr_s[1] == ':');	\
+})
 #define mksh_abspath(s)			__extension__({			\
 	const char *mksh_abspath_s = (s);				\
 	(mksh_cdirsep(mksh_abspath_s[0]) ||				\
-	    (ctype(mksh_abspath_s[0], C_ALPHA) &&			\
-	    mksh_abspath_s[1] == ':'));					\
+	    (mksh_drvltr(mksh_abspath_s) &&				\
+	    mksh_cdirsep(mksh_abspath_s[2])));				\
 })
 #define mksh_cdirsep(c)			__extension__({			\
 	char mksh_cdirsep_c = (c);					\
 	(mksh_cdirsep_c == '/' || mksh_cdirsep_c == '\\');		\
 })
-#define mksh_sdirsep(s)			__extension__({			\
-	const char *mksh_sdirsep_s = (s);				\
-	((char *)((ctype(mksh_sdirsep_s[0], C_ALPHA) &&			\
-	    mksh_sdirsep_s[1] == ':' &&					\
-	    !mksh_cdirsep(mksh_sdirsep_s[2])) ?				\
-	    (mksh_sdirsep_s + 1) : strpbrk(mksh_sdirsep_s, "/\\")));	\
+#define mksh_sdirsep(s)			strpbrk((s), "/\\")
+#define mksh_vdirsep(s)			__extension__({			\
+	const char *mksh_vdirsep_s = (s);				\
+	(((mksh_drvltr(mksh_vdirsep_s) &&				\
+	    !mksh_cdirsep(mksh_vdirsep_s[2])) ? (!0) :			\
+	    (mksh_sdirsep(mksh_vdirsep_s) != NULL)) &&			\
+	    (strcmp(mksh_vdirsep_s, T_builtin) != 0));			\
 })
-#define mksh_vdirsep(s)			(mksh_sdirsep((s)) != NULL)
+int getdrvwd(char **, unsigned int);
 #else
-#define mksh_abspath(s)			(ord((s)[0]) == ord('/'))
-#define mksh_cdirsep(c)			(ord(c) == ord('/'))
+#define mksh_abspath(s)			(ord((s)[0]) == ORD('/'))
+#define mksh_cdirsep(c)			(ord(c) == ORD('/'))
 #define mksh_sdirsep(s)			strchr((s), '/')
 #define mksh_vdirsep(s)			vstrchr((s), '/')
 #endif
diff --git a/src/shf.c b/src/shf.c
index 7e53352..2ee0ec1 100644
--- a/src/shf.c
+++ b/src/shf.c
@@ -2,7 +2,7 @@
 
 /*-
  * Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2011,
- *		 2012, 2013, 2015, 2016, 2017
+ *		 2012, 2013, 2015, 2016, 2017, 2018
  *	mirabilos <m@mirbsd.org>
  * Copyright (c) 2015
  *	Daniel Richard G. <skunk@iSKUNK.ORG>
@@ -27,7 +27,7 @@
 
 #include "sh.h"
 
-__RCSID("$MirOS: src/bin/mksh/shf.c,v 1.95 2017/05/05 22:45:58 tg Exp $");
+__RCSID("$MirOS: src/bin/mksh/shf.c,v 1.97 2018/01/14 01:28:16 tg Exp $");
 
 /* flags to shf_emptybuf() */
 #define EB_READSW	0x01	/* about to switch to reading */
@@ -554,7 +554,7 @@
 	if (shf->rnleft == 0 && (shf_fillbuf(shf) == -1 || shf->rnleft == 0))
 		return (-1);
 	--shf->rnleft;
-	return (*shf->rp++);
+	return (ord(*shf->rp++));
 }
 
 /*
@@ -1253,7 +1253,7 @@
  *    Not only do they require all 8 bits instead of 7, if chars are
  *    signed, they will have negative integer values! Something like
  *    (c - 'A') could actually become (c + 63)! Use the ord() macro to
- *    ensure you're getting a value in [0, 255].
+ *    ensure you're getting a value in [0, 255] (ORD for constants).
  * 4. '\n' is actually NL (0x15, U+0085) instead of LF (0x25, U+000A).
  *    EBCDIC has a proper newline character instead of "emulating" one
  *    with line feeds, although this is mapped to LF for our purposes.
diff --git a/src/syn.c b/src/syn.c
index c50c2ab..e4c38e3 100644
--- a/src/syn.c
+++ b/src/syn.c
@@ -2,7 +2,8 @@
 
 /*-
  * Copyright (c) 2003, 2004, 2005, 2006, 2007, 2008, 2009,
- *		 2011, 2012, 2013, 2014, 2015, 2016, 2017
+ *		 2011, 2012, 2013, 2014, 2015, 2016, 2017,
+ *		 2018
  *	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/syn.c,v 1.124 2017/05/05 22:53:31 tg Exp $");
+__RCSID("$MirOS: src/bin/mksh/syn.c,v 1.127 2018/01/14 00:22:30 tg Exp $");
 
 struct nesting_state {
 	int start_token;	/* token than began nesting (eg, FOR) */
@@ -35,7 +36,7 @@
 	struct yyrecursive_state *next;
 	struct ioword **old_herep;
 	int old_symbol;
-	int old_nesting_type;
+	unsigned int old_nesting_type;
 	bool old_reject;
 };
 
@@ -75,7 +76,10 @@
 #define ACCEPT		(reject = false)
 #define token(cf)	((reject) ? (ACCEPT, symbol) : (symbol = yylex(cf)))
 #define tpeek(cf)	((reject) ? (symbol) : (REJECT, symbol = yylex(cf)))
-#define musthave(c,cf)	do { if (token(cf) != (c)) syntaxerr(NULL); } while (/* CONSTCOND */ 0)
+#define musthave(c,cf)	do { 					\
+	if ((unsigned int)token(cf) != (unsigned int)(c))	\
+		syntaxerr(NULL);				\
+} while (/* CONSTCOND */ 0)
 
 static const char Tcbrace[] = "}";
 static const char Tesac[] = "esac";
@@ -91,7 +95,7 @@
 	c = tpeek(0);
 	if (c == 0 && !outtree)
 		outtree = newtp(TEOF);
-	else if (!ctype(c, C_LF | C_NUL))
+	else if (!cinttype(c, C_LF | C_NUL))
 		syntaxerr(NULL);
 }
 
@@ -330,7 +334,7 @@
 					XPput(args, yylval.cp);
 				break;
 
-			case ord('(' /*)*/):
+			case ORD('(' /*)*/):
 				if (XPsize(args) == 0 && XPsize(vars) == 1 &&
 				    is_wdvarassign(yylval.cp)) {
 					char *tcp;
@@ -386,18 +390,18 @@
  Leave:
 		break;
 
-	case ord('(' /*)*/): {
-		int subshell_nesting_type_saved;
+	case ORD('(' /*)*/): {
+		unsigned int subshell_nesting_type_saved;
  Subshell:
 		subshell_nesting_type_saved = subshell_nesting_type;
-		subshell_nesting_type = ord(')');
-		t = nested(TPAREN, ord('('), ord(')'), sALIAS);
+		subshell_nesting_type = ORD(')');
+		t = nested(TPAREN, ORD('('), ORD(')'), sALIAS);
 		subshell_nesting_type = subshell_nesting_type_saved;
 		break;
 	    }
 
-	case ord('{' /*}*/):
-		t = nested(TBRACE, ord('{'), ord('}'), sALIAS);
+	case ORD('{' /*}*/):
+		t = nested(TBRACE, ORD('{'), ORD('}'), sALIAS);
 		break;
 
 	case MDPAREN:
@@ -407,8 +411,8 @@
 		switch (token(LETEXPR)) {
 		case LWORD:
 			break;
-		case ord('(' /*)*/):
-			c = ord('(');
+		case ORD('(' /*)*/):
+			c = ORD('(');
 			goto Subshell;
 		default:
 			syntaxerr(NULL);
@@ -554,8 +558,8 @@
 	 */
 	if (c == DO)
 		c = DONE;
-	else if (c == ord('{'))
-		c = ord('}');
+	else if ((unsigned int)c == ORD('{'))
+		c = ORD('}');
 	else
 		syntaxerr(NULL);
 	list = c_list(sALIAS, true);
@@ -610,8 +614,8 @@
 	/* A {...} can be used instead of in...esac for case statements */
 	if (c == IN)
 		c = ESAC;
-	else if (c == ord('{'))
-		c = ord('}');
+	else if ((unsigned int)c == ORD('{'))
+		c = ORD('}');
 	else
 		syntaxerr(NULL);
 	t = tl = NULL;
@@ -636,18 +640,17 @@
 	XPinit(ptns, 16);
 	t = newtp(TPAT);
 	/* no ALIAS here */
-	if (token(CONTIN | KEYWORD) != ord('('))
+	if ((unsigned int)token(CONTIN | KEYWORD) != ORD('('))
 		REJECT;
 	do {
 		switch (token(0)) {
 		case LWORD:
 			break;
-		case ord('}'):
+		case ORD('}'):
 		case ESAC:
 			if (symbol != endtok) {
-				strdupx(yylval.cp,
-				    symbol == ord('}') ? Tcbrace : Tesac,
-				    ATEMP);
+				strdupx(yylval.cp, (unsigned int)symbol ==
+				    ORD('}') ? Tcbrace : Tesac, ATEMP);
 				break;
 			}
 			/* FALLTHROUGH */
@@ -659,23 +662,23 @@
 	REJECT;
 	XPput(ptns, NULL);
 	t->vars = (char **)XPclose(ptns);
-	musthave(ord(')'), 0);
+	musthave(ORD(')'), 0);
 
 	t->left = c_list(sALIAS, true);
 
 	/* initialise to default for ;; or omitted */
-	t->u.charflag = ord(';');
+	t->u.charflag = ORD(';');
 	/* SUSv4 requires the ;; except in the last casepart */
 	if ((tpeek(CONTIN|KEYWORD|sALIAS)) != endtok)
 		switch (symbol) {
 		default:
 			syntaxerr(NULL);
 		case BRKEV:
-			t->u.charflag = ord('|');
+			t->u.charflag = ORD('|');
 			if (0)
 				/* FALLTHROUGH */
 		case BRKFT:
-			  t->u.charflag = ord('&');
+			  t->u.charflag = ORD('&');
 			/* FALLTHROUGH */
 		case BREAK:
 			/* initialised above, but we need to eat the token */
@@ -711,14 +714,14 @@
 	 * only accepts an open-brace.
 	 */
 	if (ksh_func) {
-		if (tpeek(CONTIN|KEYWORD|sALIAS) == ord('(' /*)*/)) {
+		if ((unsigned int)tpeek(CONTIN|KEYWORD|sALIAS) == ORD('(' /*)*/)) {
 			/* function foo () { //}*/
 			ACCEPT;
-			musthave(ord(/*(*/ ')'), 0);
+			musthave(ORD(/*(*/ ')'), 0);
 			/* degrade to POSIX function */
 			ksh_func = false;
 		}
-		musthave(ord('{' /*}*/), CONTIN|KEYWORD|sALIAS);
+		musthave(ORD('{' /*}*/), CONTIN|KEYWORD|sALIAS);
 		REJECT;
 	}
 
@@ -810,8 +813,8 @@
 	{ "in",		IN,	true },
 	{ Tfunction,	FUNCTION, true },
 	{ Ttime,	TIME,	true },
-	{ "{",		ord('{'), true },
-	{ Tcbrace,	ord('}'), true },
+	{ "{",		ORD('{'), true },
+	{ Tcbrace,	ORD('}'), true },
 	{ "!",		BANG,	true },
 	{ "[[",		DBRACKET, true },
 	/* Lexical tokens (0[EOF], LWORD and REDIR handled specially) */
@@ -823,7 +826,7 @@
 	{ "((",		MDPAREN, false },
 	{ "|&",		COPROC,	false },
 	/* and some special cases... */
-	{ "newline",	ord('\n'), false },
+	{ "newline",	ORD('\n'), false },
 	{ NULL,		0,	false }
 };
 
@@ -998,9 +1001,9 @@
 		ret = (uqword && !strcmp(yylval.cp,
 		    dbtest_tokens[(int)TM_NOT])) ? TO_NONNULL : TO_NONOP;
 	else if (meta == TM_OPAREN)
-		ret = c == ord('(') /*)*/ ? TO_NONNULL : TO_NONOP;
+		ret = (unsigned int)c == ORD('(') /*)*/ ? TO_NONNULL : TO_NONOP;
 	else if (meta == TM_CPAREN)
-		ret = c == /*(*/ ord(')') ? TO_NONNULL : TO_NONOP;
+		ret = (unsigned int)c == /*(*/ ORD(')') ? TO_NONNULL : TO_NONOP;
 	else if (meta == TM_UNOP || meta == TM_BINOP) {
 		if (meta == TM_BINOP && c == REDIR &&
 		    (yylval.iop->ioflag == IOREAD ||
@@ -1131,14 +1134,14 @@
 	struct op *t;
 	char *cp;
 	struct yyrecursive_state *ys;
-	int stok, etok;
+	unsigned int stok, etok;
 
 	if (subtype != COMSUB) {
-		stok = ord('{');
-		etok = ord('}');
+		stok = ORD('{');
+		etok = ORD('}');
 	} else {
-		stok = ord('(');
-		etok = ord(')');
+		stok = ORD('(');
+		etok = ORD(')');
 	}
 
 	ys = alloc(sizeof(struct yyrecursive_state), ATEMP);
diff --git a/src/tree.c b/src/tree.c
index 1062feb..5e7326b 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.93 2017/05/05 22:53:32 tg Exp $");
+__RCSID("$MirOS: src/bin/mksh/tree.c,v 1.95 2018/01/14 00:03:05 tg Exp $");
 
 #define INDENT	8
 
@@ -329,7 +329,7 @@
 		case EOS:
 			return (--wp);
 		case ADELIM:
-			if (ord(*wp) == ord(/*{*/ '}')) {
+			if (ord(*wp) == ORD(/*{*/ '}')) {
 				++wp;
 				goto wdvarput_csubst;
 			}
@@ -342,21 +342,21 @@
 			c = ord(*wp++);
 			if (opmode & WDS_TPUTS)
 				switch (c) {
-				case ord('\n'):
+				case ORD('\n'):
 					if (quotelevel == 0) {
-						c = ord('\'');
+						c = ORD('\'');
 						shf_putc(c, shf);
-						shf_putc(ord('\n'), shf);
+						shf_putc(ORD('\n'), shf);
 					}
 					break;
 				default:
 					if (quotelevel == 0)
 						/* FALLTHROUGH */
-				case ord('"'):
-				case ord('`'):
-				case ord('$'):
-				case ord('\\'):
-					  shf_putc(ord('\\'), shf);
+				case ORD('"'):
+				case ORD('`'):
+				case ORD('$'):
+				case ORD('\\'):
+					  shf_putc(ORD('\\'), shf);
 					break;
 				}
 			shf_putc(c, shf);
@@ -365,7 +365,7 @@
 		case COMSUB:
 			shf_puts("$(", shf);
 			cs = ")";
-			if (ord(*wp) == ord('(' /*)*/))
+			if (ord(*wp) == ORD('(' /*)*/))
 				shf_putc(' ', shf);
  pSUB:
 			while ((c = *wp++) != 0)
@@ -374,11 +374,11 @@
 			break;
 		case FUNASUB:
 		case FUNSUB:
-			c = ord(' ');
+			c = ORD(' ');
 			if (0)
 				/* FALLTHROUGH */
 		case VALSUB:
-			  c = ord('|');
+			  c = ORD('|');
 			shf_putc('$', shf);
 			shf_putc('{', shf);
 			shf_putc(c, shf);
@@ -403,14 +403,14 @@
 			break;
 		case OSUBST:
 			shf_putc('$', shf);
-			if (ord(*wp++) == ord('{'))
+			if (ord(*wp++) == ORD('{'))
 				shf_putc('{', shf);
 			while ((c = *wp++) != 0)
 				shf_putc(c, shf);
 			wp = wdvarput(shf, wp, 0, opmode);
 			break;
 		case CSUBST:
-			if (ord(*wp++) == ord('}')) {
+			if (ord(*wp++) == ORD('}')) {
  wdvarput_csubst:
 				shf_putc('}', shf);
 			}
@@ -420,11 +420,11 @@
 			shf_putc('(', shf);
 			break;
 		case SPAT:
-			c = ord('|');
+			c = ORD('|');
 			if (0)
 				/* FALLTHROUGH */
 		case CPAT:
-			  c = ord(/*(*/ ')');
+			  c = ORD(/*(*/ ')');
 			shf_putc(c, shf);
 			break;
 		}
@@ -470,36 +470,37 @@
 	while ((c = ord(*fmt++))) {
 		if (c == '%') {
 			switch ((c = ord(*fmt++))) {
-			case ord('c'):
+			case ORD('c'):
 				/* character (octet, probably) */
 				shf_putchar(va_arg(va, int), shf);
 				break;
-			case ord('s'):
+			case ORD('s'):
 				/* string */
 				shf_puts(va_arg(va, char *), shf);
 				break;
-			case ord('S'):
+			case ORD('S'):
 				/* word */
 				wdvarput(shf, va_arg(va, char *), 0, WDS_TPUTS);
 				break;
-			case ord('d'):
+			case ORD('d'):
 				/* signed decimal */
 				shf_fprintf(shf, Tf_d, va_arg(va, int));
 				break;
-			case ord('u'):
+			case ORD('u'):
 				/* unsigned decimal */
 				shf_fprintf(shf, "%u", va_arg(va, unsigned int));
 				break;
-			case ord('T'):
+			case ORD('T'):
 				/* format tree */
 				ptree(va_arg(va, struct op *), indent, shf);
 				goto dont_trash_prevent_semicolon;
-			case ord(';'):
+			case ORD(';'):
 				/* newline or ; */
-			case ord('N'):
+			case ORD('N'):
 				/* newline or space */
 				if (shf->flags & SHF_STRING) {
-					if (c == ord(';') && !prevent_semicolon)
+					if ((unsigned int)c == ORD(';') &&
+					    !prevent_semicolon)
 						shf_putc(';', shf);
 					shf_putc(' ', shf);
 				} else {
@@ -515,7 +516,7 @@
 						shf_putc(' ', shf);
 				}
 				break;
-			case ord('R'):
+			case ORD('R'):
 				/* I/O redirection */
 				pioact(shf, va_arg(va, struct ioword *));
 				break;
@@ -613,7 +614,7 @@
 		case ADELIM:
 			if (c == ADELIM && nest == 0)
 				return (wp + 1);
-			if (ord(*wp) == ord(/*{*/ '}'))
+			if (ord(*wp) == ORD(/*{*/ '}'))
 				goto wdscan_csubst;
 			/* FALLTHROUGH */
 		case CHAR:
@@ -808,7 +809,7 @@
 	} else if (UTFMODE && rtt2asc(c) > 0x7F) {
 		/* better not try to display broken multibyte chars */
 		/* also go easy on the Unicode: no U+FFFD here */
-		c = ord('?');
+		c = ORD('?');
 	}
 	*dst++ = c;
 	goto vist_loop;
@@ -842,7 +843,7 @@
 			shf_puts("EOS", shf);
 			return (--wp);
 		case ADELIM:
-			if (ord(*wp) == ord(/*{*/ '}')) {
+			if (ord(*wp) == ORD(/*{*/ '}')) {
 				shf_puts(/*{*/ "]ADELIM(})", shf);
 				return (wp + 1);
 			}
@@ -856,8 +857,8 @@
 		case QCHAR:
 			shf_puts("QCHAR<", shf);
 			c = ord(*wp++);
-			if (quotelevel == 0 || c == ord('"') ||
-			    c == ord('\\') || ctype(c, C_DOLAR | C_GRAVE))
+			if (quotelevel == 0 || c == ORD('"') ||
+			    c == ORD('\\') || ctype(c, C_DOLAR | C_GRAVE))
 				shf_putc('\\', shf);
 			dumpchar(shf, c);
 			goto closeandout;
diff --git a/src/var.c b/src/var.c
index a53fae8..5219507 100644
--- a/src/var.c
+++ b/src/var.c
@@ -2,7 +2,7 @@
 
 /*-
  * 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
  *	mirabilos <m@mirbsd.org>
  *
  * Provided that these terms and disclaimer and all copyright notices
@@ -28,7 +28,7 @@
 #include <sys/sysctl.h>
 #endif
 
-__RCSID("$MirOS: src/bin/mksh/var.c,v 1.220 2017/07/26 23:02:28 tg Exp $");
+__RCSID("$MirOS: src/bin/mksh/var.c,v 1.223 2018/01/13 23:55:15 tg Exp $");
 
 /*-
  * Variables
@@ -136,7 +136,7 @@
 	struct tbl *tp;
 
 	ktinit(APERM, &specials,
-	    /* currently 18 specials: 75% of 32 = 2^5 */
+	    /* currently 21 specials: 75% of 32 = 2^5 */
 	    5);
 	while (i < V_MAX - 1) {
 		tp = ktenter(&specials, initvar_names[i],
@@ -204,7 +204,7 @@
 	}
 	innermost_refflag = SRF_NOP;
 
-	if (p != n && ord(*p) == ord('[') && (len = array_ref_len(p))) {
+	if (p != n && ord(*p) == ORD('[') && (len = array_ref_len(p))) {
 		char *sub, *tmp;
 		mksh_ari_t rval;
 
@@ -780,7 +780,7 @@
 		/* no variable name given */
 		return (NULL);
 	}
-	if (ord(*val) == ord('[')) {
+	if (ord(*val) == ORD('[')) {
 		if (new_refflag != SRF_NOP)
 			errorf(Tf_sD_s, var,
 			    "reference variable can't be an array");
@@ -803,13 +803,13 @@
 		}
 		val += len;
 	}
-	if (ord(val[0]) == ord('=')) {
+	if (ord(val[0]) == ORD('=')) {
 		strndupx(tvar, var, val - var, ATEMP);
 		++val;
 	} else if (set & IMPORT) {
 		/* environment invalid variable name or no assignment */
 		return (NULL);
-	} else if (ord(val[0]) == ord('+') && ord(val[1]) == ord('=')) {
+	} else if (ord(val[0]) == ORD('+') && ord(val[1]) == ORD('=')) {
 		strndupx(tvar, var, val - var, ATEMP);
 		val += 2;
 		vappend = true;
@@ -822,9 +822,9 @@
 		val = NULL;
 		/* handle foo[*] => foo (whole array) mapping for R39b */
 		len = strlen(tvar);
-		if (len > 3 && ord(tvar[len - 3]) == ord('[') &&
-		    ord(tvar[len - 2]) == ord('*') &&
-		    ord(tvar[len - 1]) == ord(']'))
+		if (len > 3 && ord(tvar[len - 3]) == ORD('[') &&
+		    ord(tvar[len - 2]) == ORD('*') &&
+		    ord(tvar[len - 1]) == ORD(']'))
 			tvar[len - 3] = '\0';
 	}
 
@@ -861,7 +861,7 @@
  nameref_empty:
 			errorf(Tf_sD_s, var, "empty nameref target");
 		}
-		len = (ord(*ccp) == ord('[')) ? array_ref_len(ccp) : 0;
+		len = (ord(*ccp) == ORD('[')) ? array_ref_len(ccp) : 0;
 		if (ccp[len]) {
 			/*
 			 * works for cases "no array", "valid array with
@@ -1071,7 +1071,7 @@
 		do {
 			++s;
 		} while (ctype(*s, C_ALNUX));
-		if (aok && ord(*s) == ord('[') && (alen = array_ref_len(s)))
+		if (aok && ord(*s) == ORD('[') && (alen = array_ref_len(s)))
 			s += alen;
 	}
 	return (s);
@@ -1087,7 +1087,7 @@
 		do {
 			s += 2;
 		} while (s[0] == CHAR && ctype(s[1], C_ALNUX));
-		if (aok && s[0] == CHAR && ord(s[1]) == ord('[')) {
+		if (aok && s[0] == CHAR && ord(s[1]) == ORD('[')) {
 			/* skip possible array de-reference */
 			const char *p = s;
 			char c;
@@ -1098,9 +1098,9 @@
 					break;
 				c = p[1];
 				p += 2;
-				if (ord(c) == ord('['))
+				if (ord(c) == ORD('['))
 					depth++;
-				else if (ord(c) == ord(']') && --depth == 0) {
+				else if (ord(c) == ORD(']') && --depth == 0) {
 					s = p;
 					break;
 				}
@@ -1294,9 +1294,29 @@
 {
 	mksh_ari_u num;
 	char *s;
-	int st;
+	int st = special(vp->name);
 
-	switch ((st = special(vp->name))) {
+#ifdef MKSH_DOSPATH
+	switch (st) {
+	case V_PATH:
+	case V_TMPDIR:
+#ifdef __OS2__
+	case V_BEGINLIBPATH:
+	case V_ENDLIBPATH:
+#endif
+		/* convert backslashes to slashes for convenience */
+		if (!(vp->flag&INTEGER)) {
+			s = str_val(vp);
+			do {
+				if (*s == ORD('\\'))
+					*s = '/';
+			} while (*s++);
+		}
+		break;
+	}
+#endif
+
+	switch (st) {
 #ifdef __OS2__
 	case V_BEGINLIBPATH:
 	case V_ENDLIBPATH:
@@ -1366,6 +1386,13 @@
 		}
 		vp->flag |= SPECIAL;
 		break;
+#ifdef MKSH_EARLY_LOCALE_TRACKING
+	case V_LANG:
+	case V_LC_ALL:
+	case V_LC_CTYPE:
+		recheck_ctype();
+		return;
+#endif
 	default:
 		/* do nothing, do not touch vp at all */
 		return;
@@ -1465,6 +1492,13 @@
 		/* AT&T ksh leaves previous value in place */
 		unspecial(vp->name);
 		break;
+#ifdef MKSH_EARLY_LOCALE_TRACKING
+	case V_LANG:
+	case V_LC_ALL:
+	case V_LC_CTYPE:
+		recheck_ctype();
+		return;
+#endif
 	}
 }
 
@@ -1528,8 +1562,8 @@
 	char c;
 	int depth = 0;
 
-	while ((c = *s++) && (ord(c) != ord(']') || --depth))
-		if (ord(c) == ord('['))
+	while ((c = *s++) && (ord(c) != ORD(']') || --depth))
+		if (ord(c) == ORD('['))
 			depth++;
 	if (!c)
 		return (0);
@@ -1601,18 +1635,18 @@
 	}
 	while ((ccp = vals[i])) {
 #if 0 /* temporarily taken out due to regression */
-		if (ord(*ccp) == ord('[')) {
+		if (ord(*ccp) == ORD('[')) {
 			int level = 0;
 
 			while (*ccp) {
-				if (ord(*ccp) == ord(']') && --level == 0)
+				if (ord(*ccp) == ORD(']') && --level == 0)
 					break;
-				if (ord(*ccp) == ord('['))
+				if (ord(*ccp) == ORD('['))
 					++level;
 				++ccp;
 			}
-			if (ord(*ccp) == ord(']') && level == 0 &&
-			    ord(ccp[1]) == ord('=')) {
+			if (ord(*ccp) == ORD(']') && level == 0 &&
+			    ord(ccp[1]) == ORD('=')) {
 				strndupx(cp, vals[i] + 1, ccp - (vals[i] + 1),
 				    ATEMP);
 				evaluate(substitute(cp, 0), (mksh_ari_t *)&j,
diff --git a/src/var_spec.h b/src/var_spec.h
index 45fa0eb..d8444dd 100644
--- a/src/var_spec.h
+++ b/src/var_spec.h
@@ -1,5 +1,5 @@
 /*-
- * Copyright (c) 2009, 2011, 2012, 2016
+ * Copyright (c) 2009, 2011, 2012, 2016, 2018
  *	mirabilos <m@mirbsd.org>
  *
  * Provided that these terms and disclaimer and all copyright notices
@@ -19,7 +19,7 @@
  */
 
 #if defined(VARSPEC_DEFNS)
-__RCSID("$MirOS: src/bin/mksh/var_spec.h,v 1.10 2016/11/11 23:31:39 tg Exp $");
+__RCSID("$MirOS: src/bin/mksh/var_spec.h,v 1.11 2018/01/13 21:38:10 tg Exp $");
 #define FN(name)			/* nothing */
 #elif defined(VARSPEC_ENUMS)
 #define FN(name)			V_##name,
@@ -53,6 +53,11 @@
 #endif
 FN(HISTSIZE)
 FN(IFS)
+#ifdef MKSH_EARLY_LOCALE_TRACKING
+FN(LANG)
+FN(LC_ALL)
+FN(LC_CTYPE)
+#endif
 #ifdef __OS2__
 FN(LIBPATHSTRICT)
 #endif