diff --git a/src/sbbs3/asc2ans.c b/src/sbbs3/asc2ans.c
index 668c7cffec2f2051e79429951104f48c98086446..3779d9df619e16c7f8fd019d620d4a36f408cc84 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 9a759c0791e65eaecd3ba2a3891e3f153ade3ceb..70755b172e8684cbf8a0dc04ab9552d5467b90ff 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 c54f03bee919b1a73897c30c41c7e7a1e751f0fe..f00ae8c74cf08bcff92fdfb3d4ae3f2502332ae8 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 9ceec11403bfff9480e0229926137b0e343b5b60..d1bb065b82d8e46137375166293494979067c55e 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 10cbdd1750fdb3c6ab41a0e9ff45ffc6522e9636..b137cbfe044237c727a7995ac6be5d1a9d5be7f9 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 7e543f823bc9d93c5888a5c0fcb63cbddb2341e8..8b47738fb86c09b62ae3e0945d33c1794a63483c 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 1233478b7b23f741a4d7da9364b46a4377d44733..01a177c88815c41608083704a6142808d23154e2 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 733c766337bc632e8ac7d865c7bf73eb31a5c1b6..aa42e1594759c59ac6727c8cd435fc69b8aa2bbb 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)