From b241296416b62d6b872bae4954580f61c1b95d3d Mon Sep 17 00:00:00 2001
From: rswindell <>
Date: Fri, 11 Jan 2019 11:29:38 +0000
Subject: [PATCH] A partial retraction of the Ctrl-AZ interpretation changes
 introduced on Oct-14-2018: It turns out, PabloDraw actually inserts a Ctrl-AZ
 sequence at the end of .msg (and presumably Synchronet .asc) files it edits -
 before the SAUCE record. This resulted in a printed Ctrl-Z character (arrow
 pointing right) in most terminals when viewing text/menu files created or
 edited with PabloDraw. :-( So, now Ctrl-AZ (uppercase) will revert to the
 previous definition: premature end-of-file (EOF) and a Ctrl-Az (lowercase)
 will output a Ctrl-Z (substitute) character. I'm not a big fan of
 case-sensitive Ctrl-A codes, but frankly, running out of chars and I already
 started this pattern with the Ctrl-AF/f sequences. Hopefully there's no
 existing software that is/was putting Ctrl-Az (lowercase) in files, expecting
 that to trigger a premature EOF. I certainly was not.

---
 src/sbbs3/asc2ans.c    |  5 ++++-
 src/sbbs3/con_out.cpp  |  2 ++
 src/sbbs3/getkey.cpp   |  4 +++-
 src/sbbs3/js_global.c  | 12 ++++++------
 src/sbbs3/msgtoqwk.cpp |  2 +-
 src/sbbs3/putmsg.cpp   |  2 ++
 src/sbbs3/sbbsecho.c   |  2 +-
 src/sbbs3/str_util.c   | 11 +++++++++--
 8 files changed, 28 insertions(+), 12 deletions(-)

diff --git a/src/sbbs3/asc2ans.c b/src/sbbs3/asc2ans.c
index 668c7cffec..3779d9df61 100644
--- a/src/sbbs3/asc2ans.c
+++ b/src/sbbs3/asc2ans.c
@@ -107,7 +107,7 @@ int main(int argc, char **argv)
 	while((ch=fgetc(in))!=EOF) {
 		if(ch==CTRL_A) { /* ctrl-a */
 			ch=fgetc(in);
-			if(ch==EOF)	/* EOF */
+			if(ch==EOF || ch=='Z')	/* EOF */
 				break;
 			if(ch>0x7f) {					/* move cursor right x columns */
 				int cnt=ch-0x7f;
@@ -218,6 +218,9 @@ int main(int argc, char **argv)
 					ANSI;
 					fprintf(out,"47m");
 					break;
+				case 'Z':	/* Actually a lower-case 'z' */
+					fputc('\x1a',out);	/* Ctrl-Z (substitute) char */
+					break;
 				default:
 					if(!strip)
 						fprintf(out,"\1%c",ch);
diff --git a/src/sbbs3/con_out.cpp b/src/sbbs3/con_out.cpp
index 9a759c0791..70755b172e 100644
--- a/src/sbbs3/con_out.cpp
+++ b/src/sbbs3/con_out.cpp
@@ -57,6 +57,8 @@ int sbbs_t::bputs(const char *str)
 	while(str[l] && online) {
 		if(str[l]==CTRL_A && str[l+1]!=0) {
 			l++;
+			if(str[l] == 'Z')	/* EOF (uppercase 'Z' only) */
+				break;
 			ctrl_a(str[l++]);
 			continue;
 		}
diff --git a/src/sbbs3/getkey.cpp b/src/sbbs3/getkey.cpp
index c54f03bee9..f00ae8c74c 100644
--- a/src/sbbs3/getkey.cpp
+++ b/src/sbbs3/getkey.cpp
@@ -297,7 +297,7 @@ char sbbs_t::getkey(long mode)
 
 
 /****************************************************************************/
-/* Outputs a string highlighting characters preceeded by a tilde            */
+/* Outputs a string highlighting characters preceded by a tilde             */
 /****************************************************************************/
 void sbbs_t::mnemonics(const char *str)
 {
@@ -337,6 +337,8 @@ void sbbs_t::mnemonics(const char *str)
 		else {
 			if(str[l]==CTRL_A && str[l+1]!=0) {
 				l++;
+				if(str[l] == 'Z')	/* EOF (uppercase 'Z') */
+					break;
 				ctrl_a(str[l++]);
 			} else {
 				if(str[l] == '@') {
diff --git a/src/sbbs3/js_global.c b/src/sbbs3/js_global.c
index 9ceec11403..d1bb065b82 100644
--- a/src/sbbs3/js_global.c
+++ b/src/sbbs3/js_global.c
@@ -2187,8 +2187,8 @@ js_html_encode(JSContext *cx, uintN argc, jsval *arglist)
 						outbuf[j++]='\r';
 						hpos=0;
 						break;
-					case 'Z':
-						outbuf[j++]=CTRL_Z;
+					case 'Z':	/* EOF */
+						outbuf[j++] = 0;
 						break;
 					case 'A':
 					default:
@@ -4182,19 +4182,19 @@ static jsSyncMethodSpec js_global_functions[] = {
 	,310
 	},		
 	{"file_attrib",		js_fattr,			1,	JSTYPE_NUMBER,	JSDOCSTR("path/filename")
-	,JSDOCSTR("get a file's permissions/attributes")
+	,JSDOCSTR("get a file's permissions/attributes. Returns <tt>-1</tt> if the <i>path/filename</i> does not exist.")
 	,310
 	},		
 	{"file_date",		js_fdate,			1,	JSTYPE_NUMBER,	JSDOCSTR("path/filename")
-	,JSDOCSTR("get a file's last modified date/time (in time_t format)")
+	,JSDOCSTR("get a file's last modified date/time (in time_t format). Returns <tt>-1</tt> if the <i>path/filename</i> does not exist.")
 	,310
 	},
 	{"file_cdate",		js_fcdate,			1,	JSTYPE_NUMBER,	JSDOCSTR("path/filename")
-	,JSDOCSTR("get a file's creation date/time (in time_t format)")
+	,JSDOCSTR("get a file's creation date/time (in time_t format). Returns <tt>-1</tt> if the <i>path/filename</i> does not exist.")
 	,317
 	},
 	{"file_size",		js_flength,			1,	JSTYPE_NUMBER,	JSDOCSTR("path/filename")
-	,JSDOCSTR("get a file's length (in bytes)")
+	,JSDOCSTR("get a file's length (in bytes). Returns <tt>-1</tt> if the <i>path/filename</i> does not exist.")
 	,310
 	},
 	{"file_utime",		js_utime,			3,	JSTYPE_BOOLEAN,	JSDOCSTR("path/filename [,access_time=<i>current</i>] [,mod_time=<i>current</i>]")
diff --git a/src/sbbs3/msgtoqwk.cpp b/src/sbbs3/msgtoqwk.cpp
index 10cbdd1750..b137cbfe04 100644
--- a/src/sbbs3/msgtoqwk.cpp
+++ b/src/sbbs3/msgtoqwk.cpp
@@ -381,7 +381,7 @@ ulong sbbs_t::msgtoqwk(smbmsg_t* msg, FILE *qwk_fp, long mode, uint subnum
 
 			if(ch==CTRL_A) {
 				ch=buf[++l];
-				if(ch==0)
+				if(ch==0 || ch=='Z')	/* EOF */
 					break;
 				if((asc=ctrl_a_to_ascii_char(ch)) != 0) {
 					fputc(asc,qwk_fp);
diff --git a/src/sbbs3/putmsg.cpp b/src/sbbs3/putmsg.cpp
index 7e543f823b..8b47738fb8 100644
--- a/src/sbbs3/putmsg.cpp
+++ b/src/sbbs3/putmsg.cpp
@@ -116,6 +116,8 @@ char sbbs_t::putmsg(const char *buf, long mode)
 					sys_status&=~SS_NEST_PF; 
 				}
 			}
+			else if(str[l+1] == 'Z')	/* Ctrl-AZ==EOF (uppercase 'Z' only) */
+				break;
 			else {
 				ctrl_a(str[l+1]);
 				if((sys_status&SS_ABORT) && !lines_printed)	/* Aborted at (auto) pause prompt (e.g. due to CLS)? */
diff --git a/src/sbbs3/sbbsecho.c b/src/sbbs3/sbbsecho.c
index 1233478b7b..01a177c888 100644
--- a/src/sbbs3/sbbsecho.c
+++ b/src/sbbs3/sbbsecho.c
@@ -4837,7 +4837,7 @@ void export_echomail(const char* sub_code, const nodecfg_t* nodecfg, bool rescan
 				if(buf[l]==CTRL_A) { /* Ctrl-A, so skip it and the next char */
 					char ch;
 					l++;
-					if(buf[l]==0)
+					if(buf[l]==0 || buf[l]=='Z')	/* EOF */
 						break;
 					if((ch=ctrl_a_to_ascii_char(buf[l])) != 0)
 						fmsgbuf[f++]=ch;
diff --git a/src/sbbs3/str_util.c b/src/sbbs3/str_util.c
index 733c766337..aa42e15947 100644
--- a/src/sbbs3/str_util.c
+++ b/src/sbbs3/str_util.c
@@ -52,6 +52,8 @@ char* DLLCALL remove_ctrl_a(const char *str, char *dest)
 	for(i=j=0;str[i];i++) {
 		if(str[i]==CTRL_A) {
 			i++;
+			if(str[i]==0 || str[i]=='Z')	/* EOF */
+				break;
 			/* convert non-destructive backspace to a destructive backspace */
 			if(str[i]=='<' && j)	
 				j--;
@@ -71,6 +73,8 @@ char* DLLCALL strip_ctrl(const char *str, char* dest)
 	for(i=j=0;str[i];i++) {
 		if(str[i]==CTRL_A) {
 			i++;
+			if(str[i]==0 || str[i]=='Z')	/* EOF */
+				break;
 			/* convert non-destructive backspace to a destructive backspace */
 			if(str[i]=='<' && j)	
 				j--;
@@ -117,6 +121,8 @@ char* DLLCALL prep_file_desc(const char *str, char* dest)
 	for(i=j=0;str[i];i++)
 		if(str[i]==CTRL_A && str[i+1]!=0) {
 			i++;
+			if(str[i]==0 || str[i]=='Z')	/* EOF */
+				break;
 			/* convert non-destructive backspace to a destructive backspace */
 			if(str[i]=='<' && j)	
 				j--;
@@ -330,8 +336,7 @@ str_list_t DLLCALL trashcan_list(scfg_t* cfg, const char* name)
 }
 
 /****************************************************************************/
-/* Returns the number of characters in 'str' not counting ctrl-ax codes		*/
-/* or the null terminator													*/
+/* Returns the printed columns from 'str' accounting for Ctrl-A codes		*/
 /****************************************************************************/
 size_t bstrlen(const char *str)
 {
@@ -340,6 +345,8 @@ size_t bstrlen(const char *str)
 	while(*str) {
 		if(*str==CTRL_A) {
 			str++;
+			if(*str==0 || *str=='Z')	/* EOF */
+				break;
 			if(*str=='[')
 				i=0;
 			else if(*str=='<' && i)
-- 
GitLab