From 5e754ea8b9c7e908af87fb8fa8b0eefcdffbfe45 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Deuc=D0=B5?= <shurd@sasktel.net>
Date: Sat, 9 Dec 2023 14:53:13 -0500
Subject: [PATCH] Fix literal 0xe0 (lower-case greek alpha in unicode/ch437)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

With 0xE0 being used for ciolib "super-extended scancodes", a literal
0xe0 can't pass through the input path.  This is an issue in CP866 (Ñ€)
and KOI8-U (Ю) as well as CP437 α.

Should fix SyncTERM SF bug 123.
---
 src/cioxtrn/cioxtrn.c      |  5 ++-
 src/conio/ciolib.c         | 64 ++++++++++++++++++++++++--------------
 src/conio/ciolib.h         |  1 +
 src/conio/curs_cio.c       |  3 ++
 src/conio/sdl_con.c        |  2 ++
 src/conio/win32cio.c       |  2 ++
 src/conio/win32gdi.c       |  2 ++
 src/conio/x_events.c       |  4 +++
 src/sbbs3/umonitor/chat.c  | 20 +++++++-----
 src/sbbs3/umonitor/spyon.c | 10 +++---
 src/syncdraw/draw.c        |  5 ++-
 src/syncdraw/fontutil.c    | 15 +++++++--
 src/syncdraw/key.c         |  5 ++-
 src/syncdraw/view2.c       |  5 ++-
 src/syncdraw/viewmode.c    |  5 ++-
 src/syncterm/ripper.c      | 20 +++++++++---
 src/syncterm/term.c        |  5 ++-
 src/uifc/uifc32.c          | 10 ++++--
 18 files changed, 133 insertions(+), 50 deletions(-)

diff --git a/src/cioxtrn/cioxtrn.c b/src/cioxtrn/cioxtrn.c
index abd8d5141f..4a2f57f8cf 100644
--- a/src/cioxtrn/cioxtrn.c
+++ b/src/cioxtrn/cioxtrn.c
@@ -107,8 +107,11 @@ void input_thread(void *args)
 		if(kbhit()) {
 			lastkey=key;
 			key=getch();
-			if(key==0 || key == 0xe0)
+			if(key==0 || key == 0xe0) {
 				key|=getch()<<8;
+				if (key == CIO_KEY_LITERAL_E0)
+					key = 0xe0;
+			}
 			if(key==1) {
 				toggle_modifier(CIO_MOD_ALT);
 				if(alt)
diff --git a/src/conio/ciolib.c b/src/conio/ciolib.c
index cdd7e69f66..3dde17ca91 100644
--- a/src/conio/ciolib.c
+++ b/src/conio/ciolib.c
@@ -591,9 +591,15 @@ CIOLIBEXPORT int ciolib_getche(void)
 				ciolib_putch(ch);
 				return(ch);
 			}
+			ch |= (ciolib_getch() << 8);
 			/* Eat extended chars - except ESC which is an abort */
-			if(ciolib_getch()==1)
-				return(EOF);
+			switch(ch) {
+				case CIO_KEY_LITERAL_E0:
+					ciolib_putch(ch);
+					return(ch);
+				case CIO_KEY_ABORTED:
+					return EOF;
+			}
 		}
 	}
 }
@@ -608,6 +614,8 @@ CIOLIBEXPORT int ciolib_ungetch(int ch)
 
 	if(ungotch)
 		return(EOF);
+	if (ch == 0xe0)
+		ch = CIO_KEY_LITERAL_E0;
 	if(cio_api.ungetch)
 		return(cio_api.ungetch(ch));
 	ungotch=ch;
@@ -675,12 +683,14 @@ CIOLIBEXPORT char * ciolib_cgets(char *str)
 
 	maxlen=*(unsigned char *)str;
 	while((ch=ciolib_getch())!='\n' && ch !='\r') {
+		if (ch == 0 || ch == 0xe0) {
+			ch |= (ciolib_getch() << 8);
+			if (ch == CIO_KEY_LITERAL_E0)
+				ch = 0xe0;
+		}
 		switch(ch) {
-			case 0:		/* Skip extended keys */
-			case 0xe0:	/* Skip extended keys */
-				if(ciolib_getche()==1)
-					goto early_return;
-				break;
+			case CIO_KEY_ABORTED:
+				goto early_return;
 			case '\b':
 				if(len==0) {
 					ciolib_putch(7);
@@ -690,14 +700,16 @@ CIOLIBEXPORT char * ciolib_cgets(char *str)
 				len--;
 				break;
 			default:
-				ciolib_putch(ch);
-				str[(len++)+2]=ch;
-				if(len==maxlen) {
-					str[len+2]=0;
-					*((unsigned char *)(str+1))=(unsigned char)len;
-					ciolib_putch('\r');
-					ciolib_putch('\n');
-					return(&str[2]);
+				if (ch <= 0xff) {
+					ciolib_putch(ch);
+					str[(len++)+2]=ch;
+					if(len==maxlen) {
+						str[len+2]=0;
+						*((unsigned char *)(str+1))=(unsigned char)len;
+						ciolib_putch('\r');
+						ciolib_putch('\n');
+						return(&str[2]);
+					}
 				}
 				break;
 		}
@@ -775,12 +787,14 @@ CIOLIBEXPORT char * ciolib_getpass(const char *prompt)
 
 	ciolib_cputs(prompt);
 	while((ch=ciolib_getch())!='\n') {
+		if (ch == 0 || ch == 0xe0) {
+			ch |= (ciolib_getch() << 8);
+			if (ch == CIO_KEY_LITERAL_E0)
+				ch = 0xe0;
+		}
 		switch(ch) {
-			case 0:		/* Skip extended keys */
-			case 0xe0:	/* Skip extended keys */
-				if(ciolib_getch()==1)
-					goto early_return;
-				break;
+			case CIO_KEY_ABORTED:
+				goto early_return;
 			case '\r':	/* Skip \r (ToDo: Should this be treeated as a \n? */
 				break;
 			case '\b':
@@ -791,10 +805,12 @@ CIOLIBEXPORT char * ciolib_getpass(const char *prompt)
 				len--;
 				break;
 			default:
-				if(len==8)
-					ciolib_putch(7);
-				else
-					pass[len++]=ch;
+				if (ch <= 0xff) {
+					if(len==8)
+						ciolib_putch(7);
+					else
+						pass[len++]=ch;
+				}
 				break;
 		}
 	}
diff --git a/src/conio/ciolib.h b/src/conio/ciolib.h
index 3a80614ae8..7d0fbd208c 100644
--- a/src/conio/ciolib.h
+++ b/src/conio/ciolib.h
@@ -744,5 +744,6 @@ CIOLIBEXPORT void mousestate_res(int *x_res, int *y_res, uint8_t *buttons);
 #define CIO_KEY_MOUSE     0x7dE0	// This is the right mouse on Schneider/Amstrad PC1512 PC keyboards "F-14"
 #define CIO_KEY_QUIT	  0x7eE0	// "F-15"
 #define CIO_KEY_ABORTED   0x01E0	// ESC key by scancode
+#define CIO_KEY_LITERAL_E0	0xE0E0 // Literal 0xe0 character
 
 #endif	/* Do not add anything after this line */
diff --git a/src/conio/curs_cio.c b/src/conio/curs_cio.c
index a7e16930e1..368cf49dd1 100644
--- a/src/conio/curs_cio.c
+++ b/src/conio/curs_cio.c
@@ -944,6 +944,9 @@ int curs_getch(void)
 			}
 		}
 		switch(ch) {
+			case 0xe0:
+				curs_nextgetch = 0xe0;	/* Literal 0xe0 */
+				break;
 			case KEY_DOWN:            /* Down-arrow */
 				curs_nextgetch=0x50;
 				ch=0;
diff --git a/src/conio/sdl_con.c b/src/conio/sdl_con.c
index 540a020f01..2b48188e32 100644
--- a/src/conio/sdl_con.c
+++ b/src/conio/sdl_con.c
@@ -685,6 +685,8 @@ static void setup_surfaces(struct video_stats *vs)
 /* Called from event thread only */
 static void sdl_add_key(unsigned int keyval, struct video_stats *vs)
 {
+	if (keyval == 0xe0)
+		keyval = CIO_KEY_LITERAL_E0;
 	if(keyval==0xa600 && vs != NULL) {
 		fullscreen=!fullscreen;
 		cio_api.mode=fullscreen?CIOLIB_MODE_SDL_FULLSCREEN:CIOLIB_MODE_SDL;
diff --git a/src/conio/win32cio.c b/src/conio/win32cio.c
index 8d99b92bab..44a5ed70df 100644
--- a/src/conio/win32cio.c
+++ b/src/conio/win32cio.c
@@ -301,6 +301,8 @@ static int win32_keyboardio(int isgetch)
 					else {
 						lastch=(BYTE)input.Event.KeyEvent.uChar.AsciiChar;
 					}
+					if (lastch == 0xe0)
+						lastch = CIO_KEY_LITERAL_E0;
 				} else if(input.Event.KeyEvent.wVirtualKeyCode == VK_MENU)
 					lastch=(BYTE)input.Event.KeyEvent.uChar.AsciiChar;
 
diff --git a/src/conio/win32gdi.c b/src/conio/win32gdi.c
index fe7f2dd88c..f6a0e8b6eb 100644
--- a/src/conio/win32gdi.c
+++ b/src/conio/win32gdi.c
@@ -144,6 +144,8 @@ gdi_add_key(uint16_t key)
 	DWORD remain;
 	HANDLE lwch;
 
+	if (key == 0xe0)
+		key = CIO_KEY_LITERAL_E0;
 	if (key < 256) {
 		buf[0] = key;
 		remain = 1;
diff --git a/src/conio/x_events.c b/src/conio/x_events.c
index 7301038e23..ecc1b836f8 100644
--- a/src/conio/x_events.c
+++ b/src/conio/x_events.c
@@ -1912,6 +1912,8 @@ x11_event(XEvent *ev)
 									ch = cpchar_from_unicode_cpoint(getcodepage(), wbuf[i], 0);
 								if (ch) {
 									// Bow to GCC
+									if (ch == 0xe0) // Double-up 0xe0
+										write(key_pipe[1], &ch, 1);
 									if (write(key_pipe[1], &ch, 1) == EXIT_SUCCESS)
 										return;
 									else
@@ -2127,6 +2129,8 @@ x11_event(XEvent *ev)
 							if (key < 128)
 								key = cpchar_from_unicode_cpoint(getcodepage(), key, key);
 							// Bow to GCC
+							if (key == 0xe0)
+								key = CIO_KEY_LITERAL_E0;
 							if (write(key_pipe[1], &key, (scan & 0xff) ? 1 : 2) != EXIT_SUCCESS)
 								return;
 							else
diff --git a/src/sbbs3/umonitor/chat.c b/src/sbbs3/umonitor/chat.c
index 90b33214f6..8d647eada0 100644
--- a/src/sbbs3/umonitor/chat.c
+++ b/src/sbbs3/umonitor/chat.c
@@ -182,7 +182,7 @@ int chat(scfg_t *cfg, int nodenum, node_t *node, box_t *boxch, void(*timecallbac
 					close(in);
 					in=-1;
 				}
-				if(ch==0 || ch=='\xe0') {		/* Special keys... eat 'em. */
+				if(ch==0 || ch==0xe0) {		/* Special keys... eat 'em. */
 					getch();
 				}
 			}
@@ -225,18 +225,24 @@ int chat(scfg_t *cfg, int nodenum, node_t *node, box_t *boxch, void(*timecallbac
 		if(kbhit()) {
 			ch=getch();
 			switch(ch)  {
-				case 0:			/* Special Chars */
-				case '\xe0':
-					ch=0;
-					getch();
-					break;
-
 				case ESC:
 				case 3:
 					close(in);
 					in=-1;
 					continue;
 
+				case 0:			/* Special Chars */
+					ch=0;
+					getch();
+					break;
+				case 0xe0:
+					ch = getch();
+					if (ch != 0xe0) {
+						ch = 0;
+						break;
+					}
+					// Fall-through
+
 				default:
 					if(lseek(out,0,SEEK_CUR)>=PCHAT_LEN)
 						lseek(out,0,SEEK_SET);
diff --git a/src/sbbs3/umonitor/spyon.c b/src/sbbs3/umonitor/spyon.c
index 1ca508a033..9daa4d7925 100644
--- a/src/sbbs3/umonitor/spyon.c
+++ b/src/sbbs3/umonitor/spyon.c
@@ -131,15 +131,17 @@ int spyon(char *sockname)  {
 			key=getch();
 			/* Check for control keys */
 			switch(key)  {
-				case 0:		/* Extended keys */
-				case 0xe0:
-					getch();
-					break;
 				case 3:	/* CTRL-C */
 					close(spy_sock);
 					spy_sock=INVALID_SOCKET;
 					retval=SPY_CLOSED;
 					break;
+				case 0:		/* Extended keys */
+				case 0xe0:
+					key = getch();
+					if (key != 0xe0)
+						break;
+					// Fall-through
 				default:
 					if(write(spy_sock,&key,1) != 1)
 						perror("writing to spy socket");
diff --git a/src/syncdraw/draw.c b/src/syncdraw/draw.c
index 46a3fe3d24..0fcb27666c 100644
--- a/src/syncdraw/draw.c
+++ b/src/syncdraw/draw.c
@@ -389,8 +389,11 @@ drawline(void)
 			gotoxy( CursorX+1,CursorY + 1+1);
 		do {
 			ch = getch();
-			if(ch==0 || ch==0xe0)
+			if(ch==0 || ch==0xe0) {
 				ch|=getch()<<8;
+				if (ch == CIO_KEY_LITERAL_E0)
+					ch = 0xe0;
+			}
 		}
 		while (ch != CIO_KEY_DOWN && ch != CIO_KEY_UP && ch != CIO_KEY_LEFT && ch != CIO_KEY_RIGHT && ch != 27 && ch != CIO_KEY_MOUSE);
 		CursorHandling(ch);
diff --git a/src/syncdraw/fontutil.c b/src/syncdraw/fontutil.c
index f4f0c89d60..fef100baf3 100644
--- a/src/syncdraw/fontutil.c
+++ b/src/syncdraw/fontutil.c
@@ -31,8 +31,11 @@ inputfield(char *Str, int length, int x1, int y)
 	do {
 		gotoxy(x1 + pos + 1, y + 1);
 		ch = getch();
-		if (ch == 0 || ch == 0xe0)
+		if (ch == 0 || ch == 0xe0) {
 			ch |= getch() << 8;
+			if (ch == CIO_KEY_LITERAL_E0)
+				ch = 0xe0;
+		}
 		switch (ch) {
 		case 13:
 			break;
@@ -164,8 +167,11 @@ ShowFont(int number)
 		}
 		ShowCharacter(a + 33);
 		ch = getch();
-		if (ch == 0 || ch == 0xe0)
+		if (ch == 0 || ch == 0xe0) {
 			ch |= getch() << 8;
+			if (ch == CIO_KEY_LITERAL_E0)
+				ch = 0xe0;
+		}
 		switch (ch) {
 		case CIO_KEY_UP:
 			if ((a - 47) >= 0)
@@ -251,8 +257,11 @@ main(int argc, char **argv)
 				cprintf("kb");
 			}
 		ch = getch();
-		if (ch == 0 || ch == 0xe0)
+		if (ch == 0 || ch == 0xe0) {
 			ch |= getch() << 8;
+			if (ch == CIO_KEY_LITERAL_E0)
+				ch = 0xe0;
+		}
 		switch (ch) {
 		case 'e':
 		case 'E':
diff --git a/src/syncdraw/key.c b/src/syncdraw/key.c
index 7b7310c7cb..8fc42ce691 100644
--- a/src/syncdraw/key.c
+++ b/src/syncdraw/key.c
@@ -6,8 +6,11 @@ newgetch(void)
 	int             ch;
 
 	ch = getch();
-	if(ch==0 || ch==0xe0)
+	if(ch==0 || ch==0xe0) {
 		ch|=getch()<<8;
+		if (ch == CIO_KEY_LITERAL_E0)
+			ch = 0xe0;
+	}
 	/* Input translation */
 	switch(ch) {
 	case 10:
diff --git a/src/syncdraw/view2.c b/src/syncdraw/view2.c
index 577328b1ce..58d4183250 100644
--- a/src/syncdraw/view2.c
+++ b/src/syncdraw/view2.c
@@ -52,8 +52,11 @@ viewmode(void)
 			}
 		memcpy(graph_mem, &bitmap, 64000);
 		ch = getch();
-		if(ch==0 || ch==0xe0)
+		if(ch==0 || ch==0xe0) {
 			ch|=getch()<<8;
+			if (ch == CIO_KEY_LITERAL_E0)
+				ch = 0xe0;
+		}
 		switch (ch) {
 		case KEY_UP:
 			if (b > 0)
diff --git a/src/syncdraw/viewmode.c b/src/syncdraw/viewmode.c
index 2efbf1dee1..76b0866ef3 100644
--- a/src/syncdraw/viewmode.c
+++ b/src/syncdraw/viewmode.c
@@ -50,8 +50,11 @@ viewmode(void)
 			}
 		memcpy(graph_mem, &bitmap, 64000);
 		ch = getch();
-		if(ch==0 || ch==0xe0)
+		if(ch==0 || ch==0xe0) {
 			ch|=getch()<<8;
+			if (ch == CIO_KEY_LITERAL_E0)
+				ch = 0xe0;
+		}
 		switch (ch) {
 		case KEY_UP:
 			if (b > 0)
diff --git a/src/syncterm/ripper.c b/src/syncterm/ripper.c
index fc881531d9..a2807b808c 100644
--- a/src/syncterm/ripper.c
+++ b/src/syncterm/ripper.c
@@ -9813,8 +9813,11 @@ do_popup(const char * const str)
 	while (ret == -1) {
 		ciomouse_addevent(CIOLIB_MOUSE_MOVE);
 		ch = getch();
-		if ((ch == 0) || (ch == 0xe0))
+		if ((ch == 0) || (ch == 0xe0)) {
 			ch |= getch() << 8;
+			if (ch == CIO_KEY_LITERAL_E0)
+				ch = 0xe0;
+		}
 		switch (ch) {
 			case CIO_KEY_MOUSE:
 				getmouse(&mevent);
@@ -16209,15 +16212,21 @@ rip_getch(void)
 	}
 	if ((rip.enabled == false) || rip_suspended) {
 		ch = getch();
-		if ((ch == 0) || (ch == 0xe0))
+		if ((ch == 0) || (ch == 0xe0)) {
 			ch |= getch() << 8;
+			if (ch == CIO_KEY_LITERAL_E0)
+				ch = 0xe0;
+		}
 		return ch;
 	}
 
 	gotoxy(wherex(), wherey());
 	ch = getch();
-	if ((ch == 0) || (ch == 0xe0))
+	if ((ch == 0) || (ch == 0xe0)) {
 		ch |= getch() << 8;
+		if (ch == CIO_KEY_LITERAL_E0)
+			ch = 0xe0;
+	}
 
 	shadow_palette();
 	if (ch == CIO_KEY_MOUSE) {
@@ -16359,8 +16368,11 @@ rip_getch(void)
 	int                ch;
 
 	ch = getch();
-	if ((ch == 0) || (ch == 0xe0))
+	if ((ch == 0) || (ch == 0xe0)) {
 		ch |= getch() << 8;
+		if (ch == CIO_KEY_LITERAL_E0)
+			ch = 0xe0;
+	}
 	return ch;
 #endif
 }
diff --git a/src/syncterm/term.c b/src/syncterm/term.c
index 222c59a332..c686f3ba8d 100644
--- a/src/syncterm/term.c
+++ b/src/syncterm/term.c
@@ -221,8 +221,11 @@ mousedrag(struct vmem_cell *scrollback)
 	set_modepalette(palettes[COLOUR_PALETTE]);
 	while (1) {
 		key = getch();
-		if ((key == 0) || (key == 0xe0))
+		if ((key == 0) || (key == 0xe0)) {
 			key |= getch() << 8;
+			if (key == CIO_KEY_LITERAL_E0)
+				key = 0xe0;
+		}
 		switch (key) {
 			case CIO_KEY_MOUSE:
 				getmouse(&mevent);
diff --git a/src/uifc/uifc32.c b/src/uifc/uifc32.c
index 47618aa566..d5f5668527 100644
--- a/src/uifc/uifc32.c
+++ b/src/uifc/uifc32.c
@@ -196,8 +196,11 @@ int inkey(void)
 	int c;
 
 	c=getch();
-	if(!c || c==0xe0)
+	if(!c || c==0xe0) {
 		c|=(getch()<<8);
+		if (c == CIO_KEY_LITERAL_E0)
+			c = 0xe0;
+	}
 	return(c);
 }
 
@@ -390,8 +393,11 @@ void docopy(void)
 	sbufsize=screen->text_info.screenwidth * screen->text_info.screenheight * sizeof(screen->vmem[0]);
 	while(1) {
 		key=getch();
-		if(key==0 || key==0xe0)
+		if(key==0 || key==0xe0) {
 			key|=getch()<<8;
+			if (key == CIO_KEY_LITERAL_E0)
+				key = 0xe0;
+		}
 		switch(key) {
 			case CIO_KEY_MOUSE:
 				getmouse(&mevent);
-- 
GitLab