Description: initial unicode patch
 Imported the tf5 widechar variant likely that found from
 https://github.com/kruton/tinyfugue
 This displays unicode from MUDS/talkers but does not yet fix the input
 editor to work correctly if editing unicode characters. 
Author: enyc <enyc@sheer.us>

---
The information above should follow the Patch Tagging Guidelines, please
checkout http://dep.debian.net/deps/dep3/ to learn about the format. Here
are templates for supplementary fields that you might want to add:

Origin: <vendor|upstream|other>, <url of original patch>
Bug: <url in upstream bugtracker>
Bug-Debian: https://bugs.debian.org/<bugnumber>
Bug-Ubuntu: https://launchpad.net/bugs/<bugnumber>
Forwarded: <no|not-needed|url proving that it has been forwarded>
Reviewed-By: <name and email of someone who approved the patch>
Last-Update: 2019-04-21

--- tf5-5.0beta8.orig/configure
+++ tf5-5.0beta8/configure
@@ -870,6 +870,7 @@ Optional Features:
   --disable-history       disable /recall and other history features
   --disable-process       disable /quote and /repeat
   --disable-float         disable floating point arithmetic and functions
+  --disable-widechar      disable wide character support (UTF-8)
 
 
 Optional Packages:
@@ -1431,6 +1432,13 @@ if test "${enable_float+set}" = set; the
 else
   enable_float=yes
 fi;
+# Check whether --enable-widechar or --disable-widechar was given.
+if test "${enable_widechar+set}" = set; then
+  enableval="$enable_widechar"
+
+else
+  enable_widechar=yes
+fi;
 
 
 # Check whether --with-incdirs or --without-incdirs was given.
@@ -5798,6 +5806,160 @@ fi
 
 fi
 
+if test "$enable_widechar" = "yes"; then
+
+
+for ac_header in wchar.h wctype.h
+do
+as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh`
+if eval "test \"\${$as_ac_Header+set}\" = set"; then
+  echo "$as_me:$LINENO: checking for $ac_header" >&5
+echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6
+if eval "test \"\${$as_ac_Header+set}\" = set"; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+fi
+echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5
+echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6
+else
+  # Is the header compilable?
+echo "$as_me:$LINENO: checking $ac_header usability" >&5
+echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+$ac_includes_default
+#include <$ac_header>
+_ACEOF
+rm -f conftest.$ac_objext
+if { (eval echo "$as_me:$LINENO: \"$ac_compile\"") >&5
+  (eval $ac_compile) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } &&
+	 { ac_try='test -z "$ac_c_werror_flag"
+			 || test ! -s conftest.err'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; } &&
+	 { ac_try='test -s conftest.$ac_objext'
+  { (eval echo "$as_me:$LINENO: \"$ac_try\"") >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); }; }; then
+  ac_header_compiler=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+ac_header_compiler=no
+fi
+rm -f conftest.err conftest.$ac_objext conftest.$ac_ext
+echo "$as_me:$LINENO: result: $ac_header_compiler" >&5
+echo "${ECHO_T}$ac_header_compiler" >&6
+
+# Is the header present?
+echo "$as_me:$LINENO: checking $ac_header presence" >&5
+echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6
+cat >conftest.$ac_ext <<_ACEOF
+/* confdefs.h.  */
+_ACEOF
+cat confdefs.h >>conftest.$ac_ext
+cat >>conftest.$ac_ext <<_ACEOF
+/* end confdefs.h.  */
+#include <$ac_header>
+_ACEOF
+if { (eval echo "$as_me:$LINENO: \"$ac_cpp conftest.$ac_ext\"") >&5
+  (eval $ac_cpp conftest.$ac_ext) 2>conftest.er1
+  ac_status=$?
+  grep -v '^ *+' conftest.er1 >conftest.err
+  rm -f conftest.er1
+  cat conftest.err >&5
+  echo "$as_me:$LINENO: \$? = $ac_status" >&5
+  (exit $ac_status); } >/dev/null; then
+  if test -s conftest.err; then
+    ac_cpp_err=$ac_c_preproc_warn_flag
+    ac_cpp_err=$ac_cpp_err$ac_c_werror_flag
+  else
+    ac_cpp_err=
+  fi
+else
+  ac_cpp_err=yes
+fi
+if test -z "$ac_cpp_err"; then
+  ac_header_preproc=yes
+else
+  echo "$as_me: failed program was:" >&5
+sed 's/^/| /' conftest.$ac_ext >&5
+
+  ac_header_preproc=no
+fi
+rm -f conftest.err conftest.$ac_ext
+echo "$as_me:$LINENO: result: $ac_header_preproc" >&5
+echo "${ECHO_T}$ac_header_preproc" >&6
+
+# So?  What about this header?
+case $ac_header_compiler:$ac_header_preproc:$ac_c_preproc_warn_flag in
+  yes:no: )
+    { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5
+echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;}
+    { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5
+echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;}
+    ac_header_preproc=yes
+    ;;
+  no:yes:* )
+    { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5
+echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;}
+    { echo "$as_me:$LINENO: WARNING: $ac_header:     check for missing prerequisite headers?" >&5
+echo "$as_me: WARNING: $ac_header:     check for missing prerequisite headers?" >&2;}
+    { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5
+echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;}
+    { echo "$as_me:$LINENO: WARNING: $ac_header:     section \"Present But Cannot Be Compiled\"" >&5
+echo "$as_me: WARNING: $ac_header:     section \"Present But Cannot Be Compiled\"" >&2;}
+    { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5
+echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;}
+    { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5
+echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;}
+    (
+      cat <<\_ASBOX
+## ------------------------------------------ ##
+## Report this to the AC_PACKAGE_NAME lists.  ##
+## ------------------------------------------ ##
+_ASBOX
+    ) |
+      sed "s/^/$as_me: WARNING:     /" >&2
+    ;;
+esac
+echo "$as_me:$LINENO: checking for $ac_header" >&5
+echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6
+if eval "test \"\${$as_ac_Header+set}\" = set"; then
+  echo $ECHO_N "(cached) $ECHO_C" >&6
+else
+  eval "$as_ac_Header=\$ac_header_preproc"
+fi
+echo "$as_me:$LINENO: result: `eval echo '${'$as_ac_Header'}'`" >&5
+echo "${ECHO_T}`eval echo '${'$as_ac_Header'}'`" >&6
+
+fi
+if test `eval echo '${'$as_ac_Header'}'` = yes; then
+  cat >>confdefs.h <<_ACEOF
+#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1
+_ACEOF
+
+fi
+
+done
+
+fi
+
 
 echo "$as_me:$LINENO: checking for inflate in -lz" >&5
 echo $ECHO_N "checking for inflate in -lz... $ECHO_C" >&6
@@ -8785,6 +8947,10 @@ if test "$enable_float"   = "no"; then c
 #define NO_FLOAT 1
 _ACEOF
     fi
+if test "$enable_widechar" = "yes"; then cat >>confdefs.h <<\_ACEOF
+#define WIDECHAR 1
+_ACEOF
+   fi
 
 CPPFLAGS="${CPPFLAGS} -DDATADIR=\${datadir}"
 
--- tf5-5.0beta8.orig/configure.in
+++ tf5-5.0beta8/configure.in
@@ -85,6 +85,9 @@ AC_ARG_ENABLE(process,
 AC_ARG_ENABLE(float,
 [  --disable-float         disable floating point arithmetic and functions],
     , enable_float=yes)
+AC_ARG_ENABLE(widechar,
+[  --disable-widechar      disable wide character support (UTF-8)],
+    , enable_widechar=yes)
 
 AC_ARG_WITH(incdirs,
 [  --with-incdirs=DIRS     search for include files in DIRS])
@@ -373,6 +376,10 @@ if test "$enable_float" = "yes"; then
     AC_SEARCH_LIBS(sqrt, m)
 fi
 
+if test "$enable_widechar" = "yes"; then
+    AC_CHECK_HEADERS(wchar.h wctype.h)
+fi
+
 AC_CHECK_LIB(z, inflate)
 
 AC_CHECK_LIB(pcre, pcre_compile, :, [
@@ -716,6 +723,7 @@ if test "$enable_inet6"   = "yes"; then
 if test "$enable_history" = "no"; then AC_DEFINE(NO_HISTORY)  fi
 if test "$enable_process" = "no"; then AC_DEFINE(NO_PROCESS)  fi
 if test "$enable_float"   = "no"; then AC_DEFINE(NO_FLOAT)    fi
+if test "$enable_widechar" = "yes"; then AC_DEFINE(WIDECHAR)   fi
 
 CPPFLAGS="${CPPFLAGS} -DDATADIR=\${datadir}"
 AC_SUBST(CFLAGS)
--- tf5-5.0beta8.orig/src/attr.c
+++ tf5-5.0beta8/src/attr.c
@@ -19,6 +19,10 @@ static const char RCSid[] = "$Id: attr.c
 #include "variable.h"
 #include "parse.h"	/* valstd() */
 
+#if WIDECHAR
+#include <wchar.h>
+#endif
+
 
 const int feature_256colors = (NCOLORS == 256);
 
@@ -460,6 +464,15 @@ String *decode_ansi(const char *s, attr_
     String *dst;
     int i, colorstate = 0;
     attr_t starting_attrs = attrs;
+#if WIDECHAR
+    const char *start = s;
+    int in_len = strlen(s);
+    wchar_t wc;
+    mbstate_t mbs;
+    size_t ret;
+
+    memset(&mbs, 0, sizeof(mbs));
+#endif
 
     if (emul == EMUL_RAW || emul == EMUL_DEBUG) {
 	if (final_attrs) *final_attrs = attrs;
@@ -543,11 +556,24 @@ String *decode_ansi(const char *s, attr_
             if (*s == '(' || *s == ')' || *s == '#')
                 if (!*++s) break;
 
+#if WIDECHAR
+        } else if (((ret = mbrtowc(&wc, s, in_len - (s - start), &mbs)) > 0)
+		&& (iswprint(wc) || *s == '\t')) {
+#else
         } else if (is_print(*s) || *s == '\t') {
+#endif
 	    int orig_len = dst->len;
 	    if (*s == '\t' && expand_tabs) {
 		Stringnadd(dst, ' ', tabsize - dst->len % tabsize);
 	    } else {
+#if WIDECHAR
+		if (ret <= -2 && ret >= 0) {
+		    int j = 1;
+		    while (j++ < ret) {
+			Stringadd(dst, *s++);
+		    }
+		}
+#endif
 		Stringadd(dst, *s);
 	    }
 	    set_attr(dst, orig_len, &starting_attrs, attrs);
--- tf5-5.0beta8.orig/src/command.c
+++ tf5-5.0beta8/src/command.c
@@ -522,7 +522,7 @@ int do_file_load(const char *args, int t
 		    Stringadd(libfile, *path++);
 		}
 		if (!is_absolute_path(libfile->data)) {
-		    wprintf("invalid directory in TFPATH: %S", libfile);
+		    tfwprintf("invalid directory in TFPATH: %S", libfile);
 		} else {
 		    Sappendf(libfile, "/%s", args);
 		    file = tfopen(expand_filename(libfile->data), "r");
@@ -530,7 +530,7 @@ int do_file_load(const char *args, int t
 	    } while (!file && *path);
 	} else {
 	    if (!is_absolute_path(TFLIBDIR)) {
-		wprintf("invalid TFLIBDIR: %s", TFLIBDIR);
+		tfwprintf("invalid TFLIBDIR: %s", TFLIBDIR);
 	    } else {
 		Sprintf(libfile, "%s/%s", TFLIBDIR, args);
 		file = tfopen(expand_filename(libfile->data), "r");
@@ -587,7 +587,7 @@ int do_file_load(const char *args, int t
                 i = line->len - 1;
                 while (i > 0 && is_space(line->data[i])) i--;
                 if (line->data[i] == '\\')
-                    wprintf("whitespace following final '\\'");
+                    tfwprintf("whitespace following final '\\'");
             }
         } else {
             last_cmd_line = 0;
--- tf5-5.0beta8.orig/src/expand.c
+++ tf5-5.0beta8/src/expand.c
@@ -773,7 +773,7 @@ Value *prog_interpret(const Program *pro
 		    int i;
 		    for (i = 0; i < sizeof(upper) && val->name[i]; i++)
 			upper[i] = ucase(val->name[i]);
-		    wprintf("\"%%{%s}\" is a variable substitution, "
+		    tfwprintf("\"%%{%s}\" is a variable substitution, "
 			"and is not the same as special substitution "
 			"\"%%{%.*s}\".", val->name, i, upper);
 		}
@@ -1363,14 +1363,14 @@ static int list(Program *prog, int subs)
 		++ip; /* skip ')' */
 		eat_space(prog);
 		if (is_end_of_statement(ip)) {
-		    wprintf("\"%2.2s\" following \"/%s (...)\" "
+		    tfwprintf("\"%2.2s\" following \"/%s (...)\" "
 			"sends blank line to server, "
 			"which is probably not what was intended.",
 			ip, keyword_label(block));
 		}
                 block = (block == WHILE) ? DO : THEN;
             } else if (*ip) {
-                wprintf("statement starting with %s in /%s "
+                tfwprintf("statement starting with %s in /%s "
                     "condition sends text to server, "
                     "which is probably not what was intended.",
                     error_text(prog), keyword_label(block));
@@ -1503,7 +1503,7 @@ static int list(Program *prog, int subs)
 		comefrom(prog, jump_point, prog->len);
 		eat_space(prog);
 		if (block == ELSE && is_end_of_statement(ip)) {
-		    wprintf("\"%2.2s\" following \"/%s\" "
+		    tfwprintf("\"%2.2s\" following \"/%s\" "
 			"sends blank line to server, "
 			"which is probably not what was intended.",
 			ip, keyword_label(block));
@@ -2206,7 +2206,7 @@ int varsub(Program *prog, int sub_warn,
     result = 1;
     if (sub_warn & (!sub_warned || pedantic)) {
         sub_warned = 1;
-        wprintf("\"%%%.*s\" substitution in expression is legal, "
+        tfwprintf("\"%%%.*s\" substitution in expression is legal, "
 	    "but can be confusing.  Try using \"{%.*s}\" instead.",
             ip-contents, contents,
             (ip-bracket)-(contents+bracket), contents+bracket);
--- tf5-5.0beta8.orig/src/expr.c
+++ tf5-5.0beta8/src/expr.c
@@ -291,7 +291,7 @@ static const Value *valnum(const Value *
         if (!parsenumber(val->sval->data, NULL, TYPE_NUM, parsed)) {
 #if 0
             if (pedantic)
-                wprintf("%s",
+                tfwprintf("%s",
                     "non-numeric string value used in numeric context");
 #endif
             return NULL;
@@ -699,14 +699,14 @@ static int reduce_arithmetic(opcode_t op
         case '+':
         case '*':
         case '/':
-	    wprintf("invalid operation %s on absolute time values.",
+	    tfwprintf("invalid operation %s on absolute time values.",
 		oplabel(op));
         default:   break;
         }
 	if (op == '-') /* atime - atime => dtime */
 	    promoted_type = TYPE_DTIME;
     } else if (n == 1 && val[0]->type == TYPE_ATIME && op == '-') {
-	wprintf("invalid operation %s on absolute time value.", oplabel(op));
+	tfwprintf("invalid operation %s on absolute time value.", oplabel(op));
 	promoted_type = TYPE_ATIME;
     }
 
@@ -717,7 +717,7 @@ static int reduce_arithmetic(opcode_t op
 	    (val[1]->type & TYPE_REGMATCH && valint(val[0]) == 1) ||
 	    ((val[0]->type & TYPE_REGMATCH) && (val[1]->type & TYPE_REGMATCH)))
 	{
-	    wprintf("regmatch() may return >= 1 for success.");
+	    tfwprintf("regmatch() may return >= 1 for success.");
 	}
     }
 
@@ -1486,7 +1486,7 @@ static Value *function_switch(const Expr
           }
 
         case FN_read:
-            wprintf("read() is deprecated.  Use tfread() instead.");
+            tfwprintf("read() is deprecated.  Use tfread() instead.");
             oldblock = block;  /* condition and evalflag are already correct */
             block = 0;
             Sstr = Stringnew(NULL, -1, 0);
@@ -1895,13 +1895,13 @@ static int primary_expr(Program *prog, i
 	    (keyword(val->name) || find_builtin_cmd(val->name) ||
 	    find_macro(val->name)))
 	{
-	    wprintf("possibly missing '%%;' or ')' before /%s", val->name);
+	    tfwprintf("possibly missing '%%;' or ')' before /%s", val->name);
 	}
     } else if (*ip == '$') {
         static int warned = 0;
         ++ip;
         if ((!warned || pedantic) && *ip == '[') {
-            wprintf("$[...] substitution in expression is legal, but redundant.  Try using (...) instead.");
+            tfwprintf("$[...] substitution in expression is legal, but redundant.  Try using (...) instead.");
             warned = 1;
         }
         dollarsub(prog, NULL);
--- tf5-5.0beta8.orig/src/keyboard.c
+++ tf5-5.0beta8/src/keyboard.c
@@ -140,6 +140,7 @@ int handle_keyboard_input(int read_flag)
 	goto end;
 
     for (i = 0; i < count; i++) {
+#if !WIDECHAR
         if (istrip) buf[i] &= 0x7F;
         if (buf[i] & 0x80) {
 	    if (!literal_next &&
@@ -151,6 +152,7 @@ int handle_keyboard_input(int read_flag)
 	    if (!is_print(buf[i]))
 		buf[i] &= 0x7F;
         }
+#endif
         Stringadd(current_input, mapchar(buf[i]));
     }
 
--- tf5-5.0beta8.orig/src/macro.c
+++ tf5-5.0beta8/src/macro.c
@@ -275,7 +275,7 @@ static Macro *macro_spec(String *args, i
             break;
         case 'B':
 	    if (warn_def_B)
-		wprintf("/def -B is deprecated.  See /help keys.");
+		tfwprintf("/def -B is deprecated.  See /help keys.");
             if (spec->keyname) FREE(spec->keyname);
             if (spec->bind) FREE(spec->bind);
             spec->keyname = STRDUP(ptr);
@@ -769,7 +769,7 @@ static int add_numbered_macro(Macro *mac
     if (!*macro->name && (macro->trig.str || macro->flags & MACRO_HOOK) &&
 	macro->shots == 0 && pedantic)
     {
-        wprintf("new macro (#%d) does not have a name.", macro->num);
+        tfwprintf("new macro (#%d) does not have a name.", macro->num);
     }
     return macro->num;
 }
@@ -794,7 +794,7 @@ void rebind_key_macros(void)
             FREE(p->bind);
             p->bind = STRDUP(code);
             if (!*code) {
-                wprintf("no code for key \"%s\"", p->keyname);
+                tfwprintf("no code for key \"%s\"", p->keyname);
             } else if (bind_key_macro(p)) {
 		/* bind_key_macro can't return -1 here */
                 do_hook(H_REDEF, "!Redefined %s %s", "%s %s",
@@ -916,7 +916,7 @@ static int complete_macro(Macro *spec, u
         }
         spec->bind = STRDUP(spec->bind);
         if (!*spec->bind)
-            wprintf("no code for key \"%s\".", spec->keyname);
+            tfwprintf("no code for key \"%s\".", spec->keyname);
     }
 
     if (!spec->bind) spec->bind = STRNDUP("", 0);
--- tf5-5.0beta8.orig/src/output.c
+++ tf5-5.0beta8/src/output.c
@@ -37,6 +37,10 @@ static const char RCSid[] = "$Id: output
 #include "keyboard.h"	/* keyboard_pos */
 #include "cmdlist.h"
 
+#if WIDECHAR
+#include <wchar.h>
+#endif
+
 #ifdef EMXANSI
 # define INCL_VIO
 # include <os2.h>
@@ -476,12 +480,12 @@ static void init_term(void)
 	if ((str = getvar("TERMCAP")) && (len = strlen(str)) > 0) {
 	    is_csh = ((shell = getenv("SHELL")) && smatch("*csh", shell) == 0);
 	    if (str[len-1] != ':') {
-		wprintf("unsetting invalid TERMCAP variable%s.",
+		tfwprintf("unsetting invalid TERMCAP variable%s.",
 		    is_csh ?  ", which appears to have been corrupted by your "
 		    "broken *csh shell" : "");
 		unsetvar(ffindglobalvar("TERMCAP"));
 	    } else if (len == 1023 && (!getenv("TF_FORCE_TERMCAP")) && is_csh) {
-		wprintf("unsetting the TERMCAP environment variable because "
+		tfwprintf("unsetting the TERMCAP environment variable because "
 		    "it looks like it has been truncated by your broken *csh "
 		    "shell.  To force TF to use TERMCAP, restart TF with the "
 		    "TF_FORCE_TERMCAP environment variable set.");
@@ -491,9 +495,9 @@ static void init_term(void)
     }
 
     if (!TERM || !*TERM) {
-        wprintf("TERM undefined.");
+        tfwprintf("TERM undefined.");
     } else if (tgetent(termcap_entry, TERM) <= 0) {
-        wprintf("\"%s\" terminal unsupported.", TERM);
+        tfwprintf("\"%s\" terminal unsupported.", TERM);
     } else {
         if (columns <= 0) columns = tgetnum("co");
         if (lines   <= 0) lines   = tgetnum("li");
@@ -1468,7 +1472,7 @@ static ListEntry *find_statusfield_by_na
 int ch_status_int(Var *var)
 {
     if (warn_status)
-	wprintf("the default value of %s has "
+	tfwprintf("the default value of %s has "
 	    "changed between tf version 4 and 5.", var->val.name);
     return 1;
 }
@@ -1540,7 +1544,7 @@ static int status_add(int reset, int nod
     }
 
     if (totwidth > columns) {
-        wprintf("total status width (%d) is wider than screen (%d)",
+        tfwprintf("total status width (%d) is wider than screen (%d)",
 	    totwidth, columns);
     }
 
@@ -1625,7 +1629,7 @@ struct Value *handle_status_add_command(
 int ch_status_fields(Var *var)
 {
     if (warn_status) {
-	wprintf("setting status_fields directly is deprecated, "
+	tfwprintf("setting status_fields directly is deprecated, "
 	    "and may clobber useful new features introduced in version 5.  "
 	    "The recommended way to change "
 	    "status fields is with /status_add, /status_rm, or /status_edit. "
@@ -2807,6 +2811,13 @@ static void hwrite(conString *line, int
     int i, ctrl;
     int col = 0;
     char c;
+#if WIDECHAR
+    size_t ret;
+    mbstate_t is, os;
+
+    memset(&is, 0, sizeof(is));
+    memset(&os, 0, sizeof(os));
+#endif
 
     if (line->attrs & F_BELL && start == 0) {
         dobell(1);
@@ -2837,7 +2848,22 @@ static void hwrite(conString *line, int
             bufputnc(' ', tabsize - col % tabsize);
             col += tabsize - col % tabsize;
         } else {
+#if WIDECHAR
+	    ret = mbrtowc(NULL, (char *)line->data+i, len - i, &is);
+	    if (ret >= (size_t) -2) {
+		/* Invalid character. Punt. */
+		bufputc(ctrl ? CTRL(c) : c);
+	    } else {
+		int j = 1;
+		bufputc(c);
+		while (j++ < ret) {
+		  c = line->data[++i];
+		  bufputc(c);
+		}
+	    }
+#else
             bufputc(ctrl ? CTRL(c) : c);
+#endif
             col++;
         }
     }
@@ -3067,6 +3093,13 @@ static int next_physline(Screen *screen)
 int wraplen(const char *str, int len, int indent)
 {
     int total, max, visible;
+#if WIDECHAR
+    wchar_t wc;
+    size_t ret;
+    mbstate_t mbs;
+
+    memset(&mbs, 0, sizeof(mbs));
+#endif
 
     if (emulation == EMUL_RAW) return len;
 
@@ -3075,8 +3108,20 @@ int wraplen(const char *str, int len, in
     for (visible = total = 0; total < len && visible < max; total++) {
 	if (str[total] == '\t')
 	    visible += tabsize - visible % tabsize;
-	else
+	else {
+#if WIDECHAR
+	    ret = mbrtowc(&wc, (char *)str + total, total - len, &mbs);
+	    if (ret >= (size_t) -2) {
+		/* Invalid char. Punt. */
+		visible++;
+	    } else {
+		total += ret - 1;
+		visible += wcwidth(wc);
+	    }
+#else
 	    visible++;
+#endif
+	}
     }
 
     if (total == len) return len;
--- tf5-5.0beta8.orig/src/pattern.c
+++ tf5-5.0beta8/src/pattern.c
@@ -132,7 +132,7 @@ static RegInfo *tf_reg_compile_fl(const
     if (warn_curly_re && (s = estrchr(pattern, '{', '\\')) &&
 	(is_digit(s[1]) || s[1] == ','))
     {
-	wprintf("regexp contains '{', which has a new meaning in version 5.0.  "
+	tfwprintf("regexp contains '{', which has a new meaning in version 5.0.  "
 	    "(This warning can be disabled with '/set warn_curly_re=off'.)");
     }
     for (s = pattern; *s; s++) {
@@ -249,7 +249,7 @@ int init_pattern_mflag(Pattern *pat, int
         char *s = pat->str;
         while (*s == '(' || *s == '^') s++;
         if (strncmp(s, ".*", 2) == 0)
-            wprintf("leading \".*\" in a regexp is inefficient.");
+            tfwprintf("leading \".*\" in a regexp is inefficient.");
 #endif
 	if ((pat->ri = tf_reg_compile(pat->str, 1))) goto ok;
 	break;
--- tf5-5.0beta8.orig/src/port.h
+++ tf5-5.0beta8/src/port.h
@@ -55,6 +55,10 @@
 #  define format_printf(fmt, var)     /* empty */
 #endif
 
+/* Wide character support */
+#if WIDECHAR
+#include <wctype.h>
+#endif
 
 /* standard stuff */
 
@@ -174,6 +178,19 @@ extern char *sys_errlist[];
  * not allow EOF, but do work on plain char values.  (The signedness becomes
  * important when using character sets other than 7-bit ASCII.)
  */
+#if WIDECHAR
+#define is_alnum(c)	iswalnum((wint_t)c)
+#define is_alpha(c)	iswalpha((wint_t)c)
+#define is_cntrl(c)	iswcntrl((wint_t)c)
+#define is_digit(c)	iswdigit((wint_t)c)
+#define is_graph(c)	iswgraph((wint_t)c)
+#define is_lower(c)	iswlower((wint_t)c)
+#define is_print(c)	iswprint((wint_t)c)
+#define is_punct(c)	iswpunct((wint_t)c)
+#define is_space(c)	iswspace((wint_t)c)
+#define is_upper(c)	iswupper((wint_t)c)
+#define is_xdigit(c)	iswxdigit((wint_t)c)
+#else
 #define is_alnum(c)	isalnum((unsigned char)(c))
 #define is_alpha(c)	isalpha((unsigned char)(c))
 #define is_cntrl(c)	iscntrl((unsigned char)(c))
@@ -185,6 +202,8 @@ extern char *sys_errlist[];
 #define is_space(c)	isspace((unsigned char)(c))
 #define is_upper(c)	isupper((unsigned char)(c))
 #define is_xdigit(c)	isxdigit((unsigned char)(c))
+#endif
+
 
 /* RRAND(lo,hi) returns a random integer in the range [lo,hi].
  * RAND() returns a random integer in the range [0,TF_RAND_MAX].
--- tf5-5.0beta8.orig/src/socket.c
+++ tf5-5.0beta8/src/socket.c
@@ -29,6 +29,10 @@ static const char RCSid[] = "$Id: socket
 #include <sys/socket.h>
 #include <signal.h>	/* for killing resolver child process */
 
+#if WIDECHAR
+#include <wchar.h>
+#endif
+
 #if HAVE_SSL
 # if HAVE_GNUTLS_OPENSSL_H
 #  include <gnutls/openssl.h>
@@ -310,6 +314,7 @@ typedef struct Sock {		/* an open connec
     conString *prompt;		/* prompt from server */
     struct timeval prompt_timeout; /* when does unterm'd line become a prompt */
     int ttype;			/* index into enum_ttype[] */
+    int charset;		/* index into enum_charset[] */
     attr_t attrs;		/* current text attributes */
     attr_t prepromptattrs;	/* text attributes before implicit prompt */
     unsigned long alert_id;	/* id of last alert on this socket */
@@ -397,6 +402,12 @@ STATIC_BUFFER(telbuf);
 
 #define MAXQUIET        25	/* max # of lines to suppress during login */
 
+static const char *enum_charset[] = {
+    "UTF-8",
+    "ISO-8859-1", /* No real support; code passes all chars */
+    "US-ASCII",
+    "" /* Null-terminated list, so we can loop */
+};
 /* Note: many telnet servers send DO ECHO and DO SGA together to mean
  * character-at-a-time mode.
  */
@@ -436,7 +447,7 @@ STATIC_BUFFER(telbuf);
 #define TN_ENVIRON	((char)36)	/* 1408 - (not used) */
 #define TN_AUTH		((char)37)	/* 1416 - (not used) */
 #define TN_NEW_ENVIRON	((char)39)	/* 1572 - (not used) */
-#define TN_CHARSET	((char)42)	/* 2066 - (not used) */
+#define TN_CHARSET	((char)42)	/* 2066 - Charset negotiation */
 /* 85 & 86 are not standard.  See http://www.randomly.org/projects/MCCP/ */
 #define TN_COMPRESS	((char)85)	/* MCCP v1 */
 #define TN_COMPRESS2	((char)86)	/* MCCP v2 */
@@ -966,7 +977,7 @@ int tog_keepalive(Var *var)
 	if (setsockopt(sock->fd, SOL_SOCKET, SO_KEEPALIVE, (void*)&flags,
 	    sizeof(flags)) < 0)
 	{
-	    wprintf("setsockopt KEEPALIVE: %s", strerror(errno));
+	    tfwprintf("setsockopt KEEPALIVE: %s", strerror(errno));
 	}
     }
     return 1;
@@ -1241,6 +1252,7 @@ static int opensock(World *world, int fl
     xsock->host = NULL;
     xsock->port = NULL;
     xsock->ttype = -1;
+    xsock->charset = 2; /* Default to US-ASCII, not that we act on it */
     xsock->fd = -1;
     xsock->pid = -1;
     xsock->fsastate = '\0';
@@ -1551,7 +1563,7 @@ static int openconn(Sock *sock)
 	if (setsockopt(xsock->fd, SOL_SOCKET, SO_KEEPALIVE, (void*)&flags,
 	    sizeof(flags)) < 0)
 	{
-	    wprintf("setsockopt KEEPALIVE: %s", strerror(errno));
+	    tfwprintf("setsockopt KEEPALIVE: %s", strerror(errno));
 	}
     }
 
@@ -2794,8 +2806,15 @@ static void test_prompt(void)
 
 static void telnet_subnegotiation(void)
 {
+    unsigned int i;
     char *p;
+    const char *end;
+    char temp_buff[255]; /* Same length as whole subnegotiation line. */
+    char *temp_ptr;
     int ttype;
+    char charset_sep = '\0';
+    int chosen_charset = -1;
+
     static conString enum_ttype[] = {
         STRING_LITERAL("TINYFUGUE"),
         STRING_LITERAL("ANSI-ATTR"),
@@ -2806,6 +2825,7 @@ static void telnet_subnegotiation(void)
     telnet_debug("recv", xsock->subbuffer->data, xsock->subbuffer->len);
     Stringtrunc(xsock->subbuffer, xsock->subbuffer->len - 2);
     p = xsock->subbuffer->data + 2;
+    end = p + xsock->subbuffer->len;
     switch (*p) {
     case TN_TTYPE:
 	if (!TELOPT(xsock, us, *p)) {
@@ -2831,6 +2851,41 @@ static void telnet_subnegotiation(void)
 	}
 	xsock->flags |= SOCKCOMPRESS;
 	break;
+    case TN_CHARSET:
+	if (!TELOPT(xsock, them, *p)) {
+	    no_reply("option was not agreed upon");
+	    break;
+	}
+	if (*++p == '\01') { /* REQUEST <sep> <character set>... */
+	   charset_sep = *++p;
+	   while (p != end) {
+	      temp_ptr = ++p;
+	      while (p != end && *p != charset_sep) {
+		  temp_buff[p - temp_ptr] = (*p & ~0x80);
+		  ++p;
+	      }
+	      temp_buff[p - temp_ptr] = '\0';
+	      for (i = 0; enum_charset[i][0]; ++i) {
+		  if (strcasecmp(enum_charset[i], temp_buff) == 0) {
+		     if (chosen_charset == -1 || i < chosen_charset ) {
+		         chosen_charset = i;
+		         break;
+		     }
+		  }
+	      }
+	   }
+	   if (chosen_charset > -1) {
+	      Sprintf(telbuf, "%c%c%c%c%s%c%c", TN_IAC, TN_SB, TN_CHARSET, '\02', enum_charset[chosen_charset], TN_IAC, TN_SE);
+	      xsock->charset = chosen_charset;
+	   } else {
+	      Sprintf(telbuf, "%c%c%c%c%c%c", TN_IAC, TN_SB, TN_CHARSET, '\03', TN_IAC, TN_SE);
+	   }
+	} else {
+	    no_reply("option not implemented");
+	    break;
+	}
+	telnet_send(telbuf);
+	break;
     default:
 	no_reply("unknown option");
         break;
@@ -2879,6 +2934,13 @@ static int handle_socket_input(const cha
     fd_set readfds;
     int count, n, received = 0;
     struct timeval timeout;
+#if WIDECHAR
+    mbstate_t mbs;
+    wchar_t wc;
+    size_t ret;
+
+    memset(&mbs, 0, sizeof(mbs));
+#endif
 
     if (xsock->constate <= SS_CONNECTING || xsock->constate >= SS_ZOMBIE)
 	return 0;
@@ -3139,7 +3201,8 @@ static int handle_socket_input(const cha
 #endif
                     rawchar == TN_ECHO ||
                     rawchar == TN_SEND_EOR ||
-                    rawchar == TN_BINARY)              /* accept any of these */
+                    rawchar == TN_BINARY ||
+                    rawchar == TN_CHARSET)         /* accept any of these */
                 {
                     SET_TELOPT(xsock, them, rawchar);  /* set state */
                     if (TELOPT(xsock, them_tog, rawchar)) {/* we requested it */
@@ -3250,11 +3313,29 @@ non_telnet:
             } else {
                 /* Normal character. */
                 const char *end;
+#if WIDECHAR
+		end = place;
+		while (*end != TN_IAC && end - buffer < count &&
+			(ret = mbrtowc(&wc, (char *)end,
+					count - (end - buffer), &mbs)) > 0) {
+		    if (ret >= (size_t) -2 || !iswprint(wc)) {
+			/* Invalid character. Punt. */
+			break;
+		    }
+		    end += ret;
+		}
+
+		if (end == place) {
+		    Stringadd(xsock->buffer, localchar);
+		    end = ++place;
+		}
+#else
                 Stringadd(xsock->buffer, localchar);
                 end = ++place;
                 /* Quickly skip characters that can't possibly be special. */
                 while (is_print(*end) && *end != TN_IAC && end - buffer < count)
                     end++;
+#endif
                 Stringfncat(xsock->buffer, (char*)place, end - place);
                 place = end - 1;
                 xsock->fsastate = (*place == '*') ? '*' : '\0';
@@ -3400,6 +3481,21 @@ static void telnet_debug(const char *dir
 			Stringcat(buffer, " SEND");
 		    }
 		    state = 0;
+		} else if (state == TN_CHARSET) {
+		    if (*str == (char)1) {
+			Stringcat(buffer, " REQUEST ");
+			while (len--, str++, is_print(*str) && !(*str & 0x80))
+			    Stringadd(buffer, *str);
+			len++, str--;
+		    } else if (*str == (char)2) {
+			Stringcat(buffer, " ACCEPTED ");
+			while (len--, str++, is_print(*str) && !(*str & 0x80))
+			    Stringadd(buffer, *str);
+			len++, str--;
+		    } else if (*str == (char)3) {
+			Stringcat(buffer, " REJECTED");
+		    }
+		    state = 0;
 		} else {
 		    Sappendf(buffer, " %u", (unsigned char)*str);
 		    state = 0;
--- tf5-5.0beta8.orig/src/tfconfig.h.in
+++ tf5-5.0beta8/src/tfconfig.h.in
@@ -96,6 +96,7 @@
 #define NO_PROCESS 0
 #define NO_FLOAT 0
 #define NCOLORS 16
+#define WIDECHAR 0
 
 #define HAVE_MCCP (HAVE_ZLIB_H && HAVE_LIBZ)
 
--- tf5-5.0beta8.orig/src/tfio.c
+++ tf5-5.0beta8/src/tfio.c
@@ -123,7 +123,7 @@ char *expand_filename(const char *str)
         } else {
 
 #if !(HAVE_GETPWNAM && HAVE_PWD_H)
-            wprintf("\"~user\" filename expansion is not supported.");
+            tfwprintf("\"~user\" filename expansion is not supported.");
 #else
             struct passwd *pw;
             Stringncpy(buffer, user, str - user);
@@ -719,7 +719,7 @@ void eprintf(const char *fmt, ...)
 }
 
 /* print a formatted warning message */
-void wprintf(const char *fmt, ...)
+void tfwprintf(const char *fmt, ...)
 {
     va_list ap;
     va_start(ap, fmt);
@@ -824,7 +824,7 @@ String *tfgetS(String *str, TFILE *file)
             eprintf("keyboard can only be read from a command line command.");
             return NULL;
         }
-        if (read_depth) wprintf("nested keyboard read");
+        if (read_depth) tfwprintf("nested keyboard read");
         oldtfout = tfout;
         oldtfin = tfin;
         tfout = tfscreen;
--- /dev/null
+++ tf5-5.0beta8/src/tfio.c.rej
@@ -0,0 +1,21 @@
+--- src/tfio.c	2007-01-13 23:12:39.000000000 +0000
++++ src/tfio.c	2014-11-29 18:59:33.366949303 +0000
+@@ -497,6 +497,7 @@
+     const conString *Sval;
+     int len, min, max, leftjust, stars;
+     attr_t attrs = buf->attrs;
++    va_list ap_copy;
+ 
+     if (!(flags & SP_APPEND) && buf->data) Stringtrunc(buf, 0);
+     while (*fmt) {
+@@ -522,7 +523,9 @@
+         case 'x': case 'X': case 'u': case 'o':
+         case 'f': case 'e': case 'E': case 'g': case 'G':
+         case 'p':
+-            vsprintf(tempbuf, spec, ap);
++            va_copy(ap_copy, ap);
++            vsprintf(tempbuf, spec, ap_copy);
++            va_end(ap_copy);
+             Stringcat(buf, tempbuf);
+             /* eat the arguments used by vsprintf() */
+             while (stars--) (void)va_arg(ap, int);
--- tf5-5.0beta8.orig/src/tfio.h
+++ tf5-5.0beta8/src/tfio.h
@@ -153,7 +153,7 @@ extern void   tfprintf(TFILE *file, cons
                      format_printf(2, 3);
 extern void   eprefix(String *buffer);
 extern void   eprintf(const char *fmt, ...) format_printf(1, 2);
-extern void   wprintf(const char *fmt, ...) format_printf(1, 2);
+extern void   tfwprintf(const char *fmt, ...) format_printf(1, 2);
 extern char   igetchar(void);
 extern int    handle_tfopen_func(const char *name, const char *mode);
 extern TFILE *find_tfile(const char *handle);
--- tf5-5.0beta8.orig/src/util.c
+++ tf5-5.0beta8/src/util.c
@@ -351,7 +351,7 @@ int stringliteral(String *dest, const ch
 		/* XXX handle backslash-newline */
 #endif
             } else if ((*str)[1] && pedantic) {
-                wprintf("the only legal escapes within this quoted "
+                tfwprintf("the only legal escapes within this quoted "
 		    "string are \\\\ and \\%c.  \\\\%c is the correct way to "
 		    "write a literal \\%c inside a quoted string.",
 		    quote, (*str)[1], (*str)[1]);
@@ -673,7 +673,7 @@ void init_util2(void)
         Stringfree(path);
 #endif
     } else {
-        wprintf("Can't figure out name of mail file.");
+        tfwprintf("Can't figure out name of mail file.");
     }
 }
 
--- tf5-5.0beta8.orig/src/util.h
+++ tf5-5.0beta8/src/util.h
@@ -19,12 +19,19 @@ struct feature {
 /* convert to or from ctrl character */
 #define CTRL(c)  (ucase(c) ^ '@')
 
+#if WIDECHAR
+/* Make these no-op for now. */
+#define mapchar(c)    (c)
+#define unmapchar(c)  (c)
+#define localize(c)   (c)
+#else
 /* map char to or from "safe" character set */
 #define mapchar(c)    ((c) ? (c) & 0xFF : 0x80)
 #define unmapchar(c)  ((char)(((c) == (char)0x80) ? 0x0 : (c)))
 
 /* Map character into set allowed by locale */
 #define localize(c)  ((is_print(c) || is_cntrl(c)) ? (c) : (c) & 0x7F)
+#endif
 
 /* Note STRNDUP works only if src[len] == '\0', ie. len == strlen(src) */
 #define STRNDUP(src, len) \
--- tf5-5.0beta8.orig/src/variable.c
+++ tf5-5.0beta8/src/variable.c
@@ -112,7 +112,7 @@ inline Var *newglobalvar(const char *nam
     var = newvar(name);
     var->node = hash_insert((void*)var, var_table);
     if (setting_nearest && pedantic) {
-	wprintf("variable '%s' was not previously defined in any "
+	tfwprintf("variable '%s' was not previously defined in any "
 	    "scope, so it has been created in the global scope.", name);
     }
     return var;
@@ -296,7 +296,7 @@ Var *hfindnearestvar(const Value *idval)
 	!(var = hfindglobalvar(name, idval->u.hash)))
     {
         if (patmatch(&looks_like_special_sub, NULL, name)) {
-            wprintf("\"%s\" in an expression is a variable reference, "
+            tfwprintf("\"%s\" in an expression is a variable reference, "
 		"and is not the same as special substitution \"{%s}\".",
 		name, name);
         }
@@ -354,7 +354,7 @@ static Var *newvar(const char *name)
     var->val.name = STRDUP(name);
 
     if (patmatch(&looks_like_special_sub, NULL, name)) {
-	wprintf("\"%s\" conflicts with the name of a special "
+	tfwprintf("\"%s\" conflicts with the name of a special "
 	    "substitution, so it will not be accessible with a \"%%%s\" or "
 	    "\"{%s}\" substitution.", name, name, name);
     }
@@ -599,7 +599,7 @@ int setdelim(const char **pp)
 	if (!**pp)
 	    return 0; /* no value */
 	if (**pp == '=')
-	    wprintf("'=' following space is part of value.");
+	    tfwprintf("'=' following space is part of value.");
 	return 1; /* valid value */
     } else if (**pp == '=') {
 	(*pp)++;
@@ -962,13 +962,13 @@ listvar_end:
 
 static int obsolete_prompt(Var *var)
 {
-    wprintf("%s is obsolete.  Use prompt_wait instead.", var->val.name);
+    tfwprintf("%s is obsolete.  Use prompt_wait instead.", var->val.name);
     return 1;
 }
 
 static int undocumented_var(Var *var)
 {
-    wprintf("%s is undocumented, possibly unstable, and may be removed in a "
+    tfwprintf("%s is undocumented, possibly unstable, and may be removed in a "
 	"future version.", var->val.name);
     return 1;
 }
--- tf5-5.0beta8.orig/src/world.c
+++ tf5-5.0beta8/src/world.c
@@ -166,7 +166,7 @@ World *new_world(const char *name, const
     if (pass && *pass && loadfile && (loadfile->mode & (S_IROTH | S_IRGRP)) &&
         !loadfile->warned)
     {
-        wprintf("file contains passwords and is readable by others.");
+        tfwprintf("file contains passwords and is readable by others.");
         loadfile->warned++;
     }
 # endif /* __CYGWIN32__ */
