From 11d3f59f5afabf39cf2f80eb03b55e1d121b4ce0 Mon Sep 17 00:00:00 2001
From: rswindell <>
Date: Fri, 28 Jun 2019 23:04:49 +0000
Subject: [PATCH] Initial support for UTF-8 terminals. At this time, just
 performs charset translation from CP437 -> UTF-8 and only when using
 high-level text output functions - so the output from native/DOS doors and
 scripts that use low-level output methods won't be translated. UTF-8 is
 auto-detected (only) at this time during answer (before login) by sending a
 ZWNBSP and checking (via ANSI) if the cursor position moved - an idea
 "borrowed" from Ozz Nixon (aka SqZ).

This commit requires the new "encode" library (src/encode), so you may need
to perform a cvs checkout to get that module!
---
 src/sbbs3/GNUmakefile |  9 +++++----
 src/sbbs3/answer.cpp  | 21 ++++++++++++++++-----
 src/sbbs3/con_out.cpp | 18 ++++++++++++++----
 src/sbbs3/sbbsdefs.h  |  3 ++-
 4 files changed, 37 insertions(+), 14 deletions(-)

diff --git a/src/sbbs3/GNUmakefile b/src/sbbs3/GNUmakefile
index 46a3c1c562..579e2e9c95 100644
--- a/src/sbbs3/GNUmakefile
+++ b/src/sbbs3/GNUmakefile
@@ -105,12 +105,12 @@ else
  endif
 endif
 
-CFLAGS	+=	$(JS_CFLAGS) $(CRYPT_CFLAGS) $(UIFC-MT_CFLAGS) $(XPDEV-MT_CFLAGS) $(SMBLIB_CFLAGS) $(CIOLIB-MT_CFLAGS)
+CFLAGS	+=	$(JS_CFLAGS) $(CRYPT_CFLAGS) $(UIFC-MT_CFLAGS) $(XPDEV-MT_CFLAGS) $(SMBLIB_CFLAGS) $(CIOLIB-MT_CFLAGS) $(ENCODE_CFLAGS) $(HASH_CFLAGS)
 CFLAGS	+=	-I../comio
 vpath %.c ../comio
 OBJS	+= $(MTOBJODIR)$(DIRSEP)comio_nix$(OFILE)
 JSDOOR_OBJS	+= $(MTOBJODIR)$(DIRSEP)comio_nix$(OFILE)
-LDFLAGS +=	$(UIFC-MT_LDFLAGS) $(XPDEV-MT_LDFLAGS) $(SMBLIB_LDFLAGS) $(CIOLIB-MT_LDFLAGS) $(JS_LDFLAGS) $(CRYPT_LDFLAGS)
+LDFLAGS +=	$(UIFC-MT_LDFLAGS) $(XPDEV-MT_LDFLAGS) $(SMBLIB_LDFLAGS) $(ENCODE_LDFLAGS) $(HASH_LDFLAGS) $(CIOLIB-MT_LDFLAGS) $(JS_LDFLAGS) $(CRYPT_LDFLAGS)
 
 # Monolithic Synchronet executable Build Rule
 $(SBBSMONO): $(MONO_OBJS) $(OBJS)
@@ -118,9 +118,10 @@ $(SBBSMONO): $(MONO_OBJS) $(OBJS)
 	$(QUIET)$(CXX) -o $@ $(LDFLAGS) $(MT_LDFLAGS) $(MONO_OBJS) $(OBJS) $(SBBS_LIBS) $(SMBLIB_LIBS) $(XPDEV-MT_LIBS) $(JS_LIBS) $(CRYPT_LIBS)
 
 # Synchronet BBS library Link Rule
-$(SBBS): $(JS_DEPS) $(CRYPT_DEPS) $(OBJS) $(LIBS) $(EXTRA_SBBS_DEPENDS) | $(LIBODIR)
+$(SBBS): $(JS_DEPS) $(CRYPT_DEPS) $(OBJS) $(LIBS) $(EXTRA_SBBS_DEPENDS) $(ENCODE_LIB) | $(LIBODIR)
 	@echo Linking $@
-	$(QUIET)$(MKSHPPLIB) $(LDFLAGS) -o $@ $(OBJS) $(SBBS_LIBS) $(SMBLIB_LIBS) $(LIBS) $(SHLIBOPTS) $(JS_LIBS) $(CRYPT_LIBS) $(XPDEV-MT_LIBS)
+	@echo $(ENCODE_LIB)
+	$(QUIET)$(MKSHPPLIB) $(LDFLAGS) -o $@ $(OBJS) $(SBBS_LIBS) $(SMBLIB_LIBS) $(LIBS) $(SHLIBOPTS) $(JS_LIBS) $(CRYPT_LIBS) $(ENCODE_LIBS) $(XPDEV-MT_LIBS)
 
 # FTP Server Link Rule
 $(FTPSRVR): $(MTOBJODIR)/ftpsrvr.o
diff --git a/src/sbbs3/answer.cpp b/src/sbbs3/answer.cpp
index c990fb058e..97e207e2f9 100644
--- a/src/sbbs3/answer.cpp
+++ b/src/sbbs3/answer.cpp
@@ -282,13 +282,16 @@ bool sbbs_t::answer()
 				"\x1b[0c"	/* Request CTerm version */
     			"\x1b[255B"	/* locate cursor as far down as possible */
 				"\x1b[255C"	/* locate cursor as far right as possible */
-				"\b_"		/* need a printable at this location to actually move cursor */
+				"\b_"		/* need a printable char at this location to actually move cursor */
 				"\x1b[6n"	/* Get cursor position */
 				"\x1b[u"	/* restore cursor position */
 				"\x1b[!_"	/* RIP? */
 	#ifdef SUPPORT_ZUULTERM
 				"\x1b[30;40m\xc2\x9f""Zuul.connection.write('\\x1b""Are you the gatekeeper?')\xc2\x9c"	/* ZuulTerm? */
 	#endif
+				"\r"		/* Move cursor left */
+				"\xef\xbb\xbf"	// UTF-8 Zero-width non-breaking space
+				"\x1b[6n"	/* Get cursor position (again) */
 				"\x1b[0m_"	/* "Normal" colors */
 				"\x1b[2J"	/* clear screen */
 				"\x1b[H"	/* home cursor */
@@ -340,6 +343,7 @@ bool sbbs_t::answer()
 
 			char* tokenizer = NULL;
 			char* p = strtok_r(str, "\x1b", &tokenizer);
+			unsigned cursor_pos_report = 0;
 			while(p != NULL) {
 				int	x,y;
 
@@ -347,10 +351,17 @@ bool sbbs_t::answer()
 					SAFECOPY(terminal,"ANSI");
 				autoterm|=(ANSI|COLOR);
 				if(sscanf(p, "[%u;%uR", &y, &x) == 2) {
-					lprintf(LOG_DEBUG,"received ANSI cursor position report: %ux%u", x, y);
-					/* Sanity check the coordinates in the response: */
-					if(x >= TERM_COLS_MIN && x <= TERM_COLS_MAX) cols=x; 
-					if(y >= TERM_ROWS_MIN && y <= TERM_ROWS_MAX) rows=y;
+					cursor_pos_report++;
+					lprintf(LOG_DEBUG,"received ANSI cursor position report [%u]: %ux%u"
+						,cursor_pos_report, x, y);
+					if(cursor_pos_report == 1) {
+						/* Sanity check the coordinates in the response: */
+						if(x >= TERM_COLS_MIN && x <= TERM_COLS_MAX) cols=x; 
+						if(y >= TERM_ROWS_MIN && y <= TERM_ROWS_MAX) rows=y;
+					} else {	// second report
+						if(x == 1)	// ZWNBSP didn't move cursor
+							autoterm |= UTF8;
+					}
 				} else if(sscanf(p, "[=67;84;101;114;109;%u;%u", &x, &y) == 2 && *lastchar(p) == 'c') {
 					lprintf(LOG_INFO,"received CTerm version report: %u.%u", x, y);
 					cterm_version = (x*1000) + y;
diff --git a/src/sbbs3/con_out.cpp b/src/sbbs3/con_out.cpp
index 5c9662e546..75c4a6666d 100644
--- a/src/sbbs3/con_out.cpp
+++ b/src/sbbs3/con_out.cpp
@@ -41,6 +41,7 @@
 /**********************************************************************/
 
 #include "sbbs.h"
+#include "cp437_utf8_tbl.h"
 
 /****************************************************************************/
 /* Outputs a NULL terminated string locally and remotely (if applicable)    */
@@ -402,8 +403,13 @@ int sbbs_t::outchar(char ch)
 	else
 		outchar_esc=0;
 	long term = term_supports();
-	if((term&(PETSCII|NO_EXASCII)) == NO_EXASCII && ch&0x80)
-		ch=exascii_to_ascii_char(ch);  /* seven bit table */
+	const char* utf8 = NULL;
+	if(!(term&PETSCII)) {
+		if((term&NO_EXASCII) && (ch&0x80))
+			ch = exascii_to_ascii_char(ch);  /* seven bit table */
+		else if(term&UTF8)
+			utf8 = cp437_utf8_tbl[(uchar)ch];
+	}
 
 	if(ch==FF && lncntr > 0 && !tos) {
 		lncntr=0;
@@ -452,8 +458,12 @@ int sbbs_t::outchar(char ch)
 				outcom(PETSCII_REVERSE_OFF);
 			if(ch == '\r' && (curatr&0xf0) != 0) // reverse video is disabled upon CR
 				curatr >>= 4;
-		} else
-			outcom(ch);
+		} else {
+			if(utf8 != NULL)
+				putcom(utf8);
+			else
+				outcom(ch);
+		}
 	}
 	if(!outchar_esc) {
 		/* Track cursor position locally */
diff --git a/src/sbbs3/sbbsdefs.h b/src/sbbs3/sbbsdefs.h
index a5ea7bcd6e..c098ecda7b 100644
--- a/src/sbbs3/sbbsdefs.h
+++ b/src/sbbs3/sbbsdefs.h
@@ -656,8 +656,9 @@ typedef enum {						/* Values for xtrn_t.event				*/
 #define PETSCII		(1L<<26)		/* Commodore PET/CBM terminal			*/
 #define SWAP_DELETE	(1L<<27)		/* Swap Delete and Backspace keys		*/
 #define ICE_COLOR	(1L<<28)		/* Bright background color support		*/
+#define UTF8		(1L<<29)		/* UTF-8 terminal						*/
 
-#define TERM_FLAGS	(ANSI|COLOR|NO_EXASCII|RIP|WIP|HTML|CTERM_FONTS|PETSCII|SWAP_DELETE|ICE_COLOR)
+#define TERM_FLAGS	(ANSI|COLOR|NO_EXASCII|RIP|WIP|HTML|CTERM_FONTS|PETSCII|SWAP_DELETE|ICE_COLOR|UTF8)
 
 									/* Special terminal key mappings */
 #define TERM_KEY_HOME	CTRL_B
-- 
GitLab