From 9374bfe37ed5df4ce029510c45313f1ee3e2354c Mon Sep 17 00:00:00 2001
From: rswindell <>
Date: Wed, 10 Jul 2019 00:08:29 +0000
Subject: [PATCH] New @-code to output a UNICODE character. 3 different
 syntaxes: @U+<codepoint-in-hex>@ - automatic CP437 fallback char, if
 available @U+<codepoint-in-hex|cp437char-in-hex>, specify CP437 fallback char
 @U+<codepoint-in-hex!cp437char-in-hex>, specify CP437 fallback char (used if
 no automatic fallback mapping is available)

---
 src/sbbs3/atcodes.cpp | 23 +++++++++++++++++++++-
 src/sbbs3/con_out.cpp | 45 +++++++++++++++++++++++++++++++------------
 src/sbbs3/sbbs.h      |  2 ++
 3 files changed, 57 insertions(+), 13 deletions(-)

diff --git a/src/sbbs3/atcodes.cpp b/src/sbbs3/atcodes.cpp
index de46a39726..aeddf77812 100644
--- a/src/sbbs3/atcodes.cpp
+++ b/src/sbbs3/atcodes.cpp
@@ -36,6 +36,8 @@
 
 #include "sbbs.h"
 #include "cmdshell.h"
+#include "utf8.h"
+#include "unicode.h"
 
 #if defined(_WINSOCKAPI_)
 	extern WSADATA WSAData;
@@ -128,7 +130,7 @@ int sbbs_t::show_atcode(const char *instr)
 
 const char* sbbs_t::atcode(char* sp, char* str, size_t maxlen)
 {
-	char*	tp;
+	char*	tp = NULL;
 	uint	i;
 	uint	ugrp;
 	uint	usub;
@@ -139,6 +141,25 @@ const char* sbbs_t::atcode(char* sp, char* str, size_t maxlen)
 
 	str[0]=0;
 
+	if(strncmp(sp, "U+", 2) == 0) {	// UNICODE
+		enum unicode_codepoint codepoint = (enum unicode_codepoint)strtoul(sp + 2, &tp, 16);
+		if(tp == NULL || *tp ==0)
+			outchar(codepoint, unicode_to_cp437(codepoint));
+		else {
+			char fallback = (char)strtoul(tp + 1, NULL, 16);
+			if(*tp == '|')
+				outchar(codepoint, fallback);
+			else if(*tp == '!') {
+				char ch = unicode_to_cp437(codepoint);
+				if(ch != 0)
+					fallback = ch;
+				outchar(codepoint, fallback);
+			}
+			else return NULL; // Invalid @-code
+		}
+		return nulstr;
+	}
+
 	if(!strcmp(sp,"VER"))
 		return(VERSION);
 
diff --git a/src/sbbs3/con_out.cpp b/src/sbbs3/con_out.cpp
index 30117ef0c6..0f717979e1 100644
--- a/src/sbbs3/con_out.cpp
+++ b/src/sbbs3/con_out.cpp
@@ -250,7 +250,7 @@ size_t sbbs_t::utf8_to_cp437(const char* str, size_t len)
 		outchar(*str);
 		return sizeof(char);
 	}
-	uint32_t codepoint = 0;
+	enum unicode_codepoint codepoint = UNICODE_UNDEFINED;
 	len = utf8_getc(str, len, &codepoint);
 	if((int)len < 2) {
 		lprintf(LOG_NOTICE, "Invalid UTF-8 sequence: %02X (error = %d)", (uchar)*str, (int)len);
@@ -266,7 +266,7 @@ size_t sbbs_t::utf8_to_cp437(const char* str, size_t len)
 	char ch = unicode_to_cp437(codepoint);
 	if(ch)
 		outchar(ch);
-	else if(!unicode_is_zerowidth(codepoint)) {
+	else if(unicode_width(codepoint) > 0) {
 		outchar(CP437_CHAR_INVERTED_QUESTION_MARK);
 		char seq[32] = "";
 		for(size_t i = 0; i < len; i++)
@@ -301,7 +301,7 @@ int sbbs_t::rputs(const char *str, size_t len)
 		else if((term&NO_EXASCII) && (ch&0x80))
 			ch = exascii_to_ascii_char(ch);  /* seven bit table */
 		else if(term&UTF8) {
-			uint32_t codepoint = cp437_unicode_tbl[(uchar)ch];
+			enum unicode_codepoint codepoint = cp437_unicode_tbl[(uchar)ch];
 			if(codepoint != 0)
 				utf8_putc(utf8, sizeof(utf8) - 1, codepoint);
 		}
@@ -451,7 +451,7 @@ int sbbs_t::outchar(char ch)
 		if((term&NO_EXASCII) && (ch&0x80))
 			ch = exascii_to_ascii_char(ch);  /* seven bit table */
 		else if(term&UTF8) {
-			uint32_t codepoint = cp437_unicode_tbl[(uchar)ch];
+			enum unicode_codepoint codepoint = cp437_unicode_tbl[(uchar)ch];
 			if(codepoint != 0)
 				utf8_putc(utf8, sizeof(utf8) - 1, codepoint);
 		}
@@ -541,14 +541,7 @@ int sbbs_t::outchar(char ch)
 				column=0;
 				break;
 			default:
-				column++;
-				if(column >= cols) {	// assume terminal has/will auto-line-wrap
-					lncntr++;
-					lbuflen = 0;
-					tos = 0;
-					lastlinelen = column;
-					column = 0;
-				}
+				inc_column(1);
 				if(!lbuflen)
 					latr=curatr;
 				if(lbuflen<LINE_BUFSIZE)
@@ -567,6 +560,34 @@ int sbbs_t::outchar(char ch)
 	return 0;
 }
 
+int sbbs_t::outchar(enum unicode_codepoint codepoint, char cp437_fallback)
+{
+	if(term_supports(UTF8)) {
+		char str[UTF8_MAX_LEN];
+		int len = utf8_putc(str, sizeof(str), codepoint);
+		if(len < 1)
+			return len;
+		putcom(str, len);
+		inc_column(unicode_width(codepoint));
+		return 0;
+	}
+	if(cp437_fallback == 0)
+		return 0;
+	return outchar(cp437_fallback);
+}
+
+void sbbs_t::inc_column(int count)
+{
+	column += count;
+	if(column >= cols) {	// assume terminal has/will auto-line-wrap
+		lncntr++;
+		lbuflen = 0;
+		tos = 0;
+		lastlinelen = column;
+		column = 0;
+	}
+}
+
 void sbbs_t::center(char *instr)
 {
 	char str[256];
diff --git a/src/sbbs3/sbbs.h b/src/sbbs3/sbbs.h
index 9c816c5701..f2efc2a181 100644
--- a/src/sbbs3/sbbs.h
+++ b/src/sbbs3/sbbs.h
@@ -715,6 +715,8 @@ public:
 	;
 	void	backspace(void);				/* Output a destructive backspace via outchar */
 	int		outchar(char ch);				/* Output a char - check echo and emu.  */
+	int		outchar(enum unicode_codepoint, char cp437_fallback = 0);
+	void	inc_column(int count);
 	void	center(char *str);
 	void	clearline(void);
 	void	cleartoeol(void);
-- 
GitLab