diff --git a/src/sbbs3/getmsg.cpp b/src/sbbs3/getmsg.cpp
index 6c44bb37dc2c936f2d5f2fc9aeb9a413e852acec..f46c9b0739857b59a47e148062850586c8c24a7d 100644
--- a/src/sbbs3/getmsg.cpp
+++ b/src/sbbs3/getmsg.cpp
@@ -8,7 +8,7 @@
  * @format.tab-size 4		(Plain Text/Source Code File Header)			*
  * @format.use-tabs true	(see http://www.synchro.net/ptsc_hdr.html)		*
  *																			*
- * Copyright 2005 Rob Swindell - http://www.synchro.net/copyright.html		*
+ * Copyright 2009 Rob Swindell - http://www.synchro.net/copyright.html		*
  *																			*
  * This program is free software; you can redistribute it and/or			*
  * modify it under the terms of the GNU General Public License				*
@@ -191,20 +191,6 @@ void sbbs_t::show_msg(smbmsg_t* msg, long mode)
 	}
 }
 
-void sbbs_t::quotemsg(smbmsg_t* msg, int tails)
-{
-	char	str[256];
-	char	tmp[13];
-
-	strcpy(tmp,"QUOTES.TXT");
-	if(useron.xedit && cfg.xedit[useron.xedit-1]->misc&XTRN_LWRCASE)
-		strlwr(tmp);
-	sprintf(str,"%s%s",cfg.node_dir,tmp);
-	remove(str);
-	msgtotxt(msg,str,0,tails);
-}
-
-
 /****************************************************************************/
 /* Writes message header and text data to a text file						*/
 /****************************************************************************/
diff --git a/src/sbbs3/sbbs.h b/src/sbbs3/sbbs.h
index 4ea0b3054f4df4737669d04ecd53e02b4cef6d7d..755254393117488314bdd59bd09a0ee9c5728b45 100644
--- a/src/sbbs3/sbbs.h
+++ b/src/sbbs3/sbbs.h
@@ -467,6 +467,7 @@ public:
 	void	automsg(void);
 	bool	writemsg(const char *str, const char *top, char *title, long mode, int subnum
 				,const char *dest);
+	char*	quotes_fname(int xedit, char* buf, size_t len);
 	char*	msg_tmp_fname(int xedit, char* fname, size_t len);
 	char	putmsg(const char *str, long mode);
 	bool	msgabort(void);
@@ -487,6 +488,8 @@ public:
 				,uint subnum);
 	void	copyfattach(uint to, uint from, char *title);
 	bool	movemsg(smbmsg_t* msg, uint subnum);
+	int		process_edited_text(char* buf, FILE* stream, long mode, unsigned* lines);
+	int		process_edited_file(const char* src, const char* dest, long mode, unsigned* lines);
 
 	/* postmsg.cpp */
 	bool	postmsg(uint subnum, smbmsg_t* msg, long wm_mode);
diff --git a/src/sbbs3/writemsg.cpp b/src/sbbs3/writemsg.cpp
index 4549e4fad1cc49a93665abadde54413c3ea1e807..ffa974b31ef8003212cd3dbacac65dc845ebccd9 100644
--- a/src/sbbs3/writemsg.cpp
+++ b/src/sbbs3/writemsg.cpp
@@ -8,7 +8,7 @@
  * @format.tab-size 4		(Plain Text/Source Code File Header)			*
  * @format.use-tabs true	(see http://www.synchro.net/ptsc_hdr.html)		*
  *																			*
- * Copyright 2008 Rob Swindell - http://www.synchro.net/copyright.html		*
+ * Copyright 2009 Rob Swindell - http://www.synchro.net/copyright.html		*
  *																			*
  * This program is free software; you can redistribute it and/or			*
  * modify it under the terms of the GNU General Public License				*
@@ -45,18 +45,118 @@ void quotestr(char *str);
 /****************************************************************************/
 /* Returns temporary message text filename (for message/text editors)		*/
 /****************************************************************************/
-char* sbbs_t::msg_tmp_fname(int xedit, char* fname, size_t len)
+char* sbbs_t::msg_tmp_fname(int xedit, char* path, size_t len)
 {
-	safe_snprintf(fname, len, "%sINPUT.MSG", cfg.temp_dir);
+	safe_snprintf(path, len, "%sINPUT.MSG", cfg.temp_dir);
 
 	if(xedit) {
 		if(cfg.xedit[xedit-1]->misc&QUICKBBS)
-			safe_snprintf(fname, len, "%sMSGTMP", cfg.node_dir);	/* QuickBBS editors are dumb */
+			safe_snprintf(path, len, "%sMSGTMP", cfg.node_dir);	/* QuickBBS editors are dumb */
 		if(cfg.xedit[xedit-1]->misc&XTRN_LWRCASE)
-			strlwr(fname);
+			strlwr(getfname(path));
 	}
 
-	return(fname);
+	return path;
+}
+
+/****************************************************************************/
+/****************************************************************************/
+char* sbbs_t::quotes_fname(int xedit, char *path, size_t len)
+{
+	safe_snprintf(path, len, "%sQUOTES.TXT", cfg.node_dir);
+	if(xedit && cfg.xedit[xedit-1]->misc&XTRN_LWRCASE)
+		strlwr(getfname(path));
+	return path;
+}
+
+/****************************************************************************/
+/****************************************************************************/
+void sbbs_t::quotemsg(smbmsg_t* msg, int tails)
+{
+	char	str[MAX_PATH+1];
+
+	quotes_fname(useron.xedit,str,sizeof(str));
+	removecase(str);
+	msgtotxt(msg,str,0,tails);
+}
+
+/****************************************************************************/
+/****************************************************************************/
+int sbbs_t::process_edited_text(char* buf, FILE* stream, long mode, unsigned* lines)
+{
+	int i,l;
+	int	len=0;
+
+	for(l=i=0;buf[l] && i<cfg.level_linespermsg[useron.level];l++) {
+		if((uchar)buf[l]==141 && useron.xedit
+    		&& cfg.xedit[useron.xedit-1]->misc&QUICKBBS) {
+			len+=fwrite(crlf,1,2,stream);
+			i++;
+			continue; 
+		}
+		/* Expand LF to CRLF? */
+		if(buf[l]==LF && (!l || buf[l-1]!=CR) && useron.xedit
+			&& cfg.xedit[useron.xedit-1]->misc&EXPANDLF) {
+			len+=fwrite(crlf,1,2,stream);
+			i++;
+			continue; 
+		}
+		/* Strip FidoNet Kludge Lines? */
+		if(buf[l]==1 && useron.xedit
+			&& cfg.xedit[useron.xedit-1]->misc&STRIPKLUDGE) {
+			while(buf[l] && buf[l]!=LF) 
+				l++;
+			if(buf[l]==0)
+				break;
+			continue;
+		}
+		if(!(mode&(WM_EMAIL|WM_NETMAIL))
+			&& (!l || buf[l-1]==LF)
+			&& buf[l]=='-' && buf[l+1]=='-' && buf[l+2]=='-'
+			&& (buf[l+3]==' ' || buf[l+3]==TAB || buf[l+3]==CR))
+			buf[l+1]='+';
+		if(buf[l]==LF)
+			i++;
+		fputc(buf[l],stream); 
+		len++;
+	}
+
+	if(buf[l])
+		bputs(text[NoMoreLines]);
+
+	if(lines!=NULL)
+		*lines=i;
+	return len;
+}
+
+/****************************************************************************/
+/****************************************************************************/
+int sbbs_t::process_edited_file(const char* src, const char* dest, long mode, unsigned* lines)
+{
+	char*	buf;
+	long	len;
+	FILE*	fp;
+
+	if((len=flength(src))<1)
+		return -1;
+
+	if((buf=(char*)malloc(len+1))==NULL)
+		return -2;
+
+	if((fp=fopen(src,"rb"))==NULL)
+		return -3;
+
+	memset(buf,0,len+1);
+	fread(buf,len,sizeof(char),fp);
+	fclose(fp);
+
+	if((fp=fopen(dest,"wb"))!=NULL) {
+		len=process_edited_text(buf, fp, mode, lines);
+		fclose(fp);
+	}
+	free(buf);
+
+	return len;
 }
 
 /****************************************************************************/
@@ -78,6 +178,7 @@ bool sbbs_t::writemsg(const char *fname, const char *top, char *title, long mode
 	ulong	l;
 	FILE*	stream;
 	FILE*	fp;
+	unsigned lines;
 
 	useron_level=useron.level;
 
@@ -102,10 +203,7 @@ bool sbbs_t::writemsg(const char *fname, const char *top, char *title, long mode
 		/* Quote entire message to MSGTMP or INPUT.MSG */
 
 		if(useron.xedit && cfg.xedit[useron.xedit-1]->misc&QUOTEALL) {
-			strcpy(tmp,"QUOTES.TXT");
-			if(cfg.xedit[useron.xedit-1]->misc&XTRN_LWRCASE)
-				strlwr(tmp);
-			SAFEPRINTF2(str,"%s%s",cfg.node_dir,tmp);
+			quotes_fname(useron.xedit, str, sizeof(str));
 			if((stream=fnopen(NULL,str,O_RDONLY))==NULL) {
 				errormsg(WHERE,ERR_OPEN,str,O_RDONLY);
 				free(buf);
@@ -137,10 +235,7 @@ bool sbbs_t::writemsg(const char *fname, const char *top, char *title, long mode
 			;
 
 		else if(yesno(text[QuoteMessageQ])) {
-			strcpy(tmp,"QUOTES.TXT");
-			if(useron.xedit && cfg.xedit[useron.xedit-1]->misc&XTRN_LWRCASE)
-				strlwr(tmp);
-			SAFEPRINTF2(str,"%s%s",cfg.node_dir,tmp);
+			quotes_fname(useron.xedit, str, sizeof(str));
 			if((stream=fnopen(&file,str,O_RDONLY))==NULL) {
 				errormsg(WHERE,ERR_OPEN,str,O_RDONLY);
 				free(buf);
@@ -242,10 +337,7 @@ bool sbbs_t::writemsg(const char *fname, const char *top, char *title, long mode
 		} 
 	}
 	else {
-		strcpy(tmp,"QUOTES.TXT");
-		if(useron.xedit && cfg.xedit[useron.xedit-1]->misc&XTRN_LWRCASE)
-			strlwr(tmp);
-		SAFEPRINTF2(str,"%s%s",cfg.node_dir,tmp);
+		quotes_fname(useron.xedit, str, sizeof(str));
 		removecase(str); 
 	}
 
@@ -413,46 +505,12 @@ bool sbbs_t::writemsg(const char *fname, const char *top, char *title, long mode
 
 	now=time(NULL);
 	bputs(text[Saving]);
-	if((stream=fnopen(&file,fname,O_WRONLY|O_CREAT|O_TRUNC))==NULL) {
+	if((stream=fnopen(NULL,fname,O_WRONLY|O_CREAT|O_TRUNC))==NULL) {
 		errormsg(WHERE,ERR_OPEN,fname,O_WRONLY|O_CREAT|O_TRUNC);
 		free(buf);
 		return(false); 
 	}
-	for(l=i=0;buf[l] && i<cfg.level_linespermsg[useron_level];l++) {
-		if((uchar)buf[l]==141 && useron.xedit
-    		&& cfg.xedit[useron.xedit-1]->misc&QUICKBBS) {
-			fwrite(crlf,2,1,stream);
-			i++;
-			continue; 
-		}
-		/* Expand LF to CRLF? */
-		if(buf[l]==LF && (!l || buf[l-1]!=CR) && useron.xedit
-			&& cfg.xedit[useron.xedit-1]->misc&EXPANDLF) {
-			fwrite(crlf,2,1,stream);
-			i++;
-			continue; 
-		}
-		/* Strip FidoNet Kludge Lines? */
-		if(buf[l]==1 && useron.xedit
-			&& cfg.xedit[useron.xedit-1]->misc&STRIPKLUDGE) {
-			while(buf[l] && buf[l]!=LF) 
-				l++;
-			if(buf[l]==0)
-				break;
-			continue;
-		}
-		if(!(mode&(WM_EMAIL|WM_NETMAIL))
-			&& (!l || buf[l-1]==LF)
-			&& buf[l]=='-' && buf[l+1]=='-' && buf[l+2]=='-'
-			&& (buf[l+3]==' ' || buf[l+3]==TAB || buf[l+3]==CR))
-			buf[l+1]='+';
-		if(buf[l]==LF)
-			i++;
-		fputc(buf[l],stream); 
-	}
-
-	if(buf[l])
-		bputs(text[NoMoreLines]);
+	l=process_edited_text(buf,stream,mode,&lines);
 
 	/* Signature file */
 	if(subnum==INVALID_SUB || !(cfg.sub[subnum]->misc&SUB_NOUSERSIG)) {
@@ -464,7 +522,7 @@ bool sbbs_t::writemsg(const char *fname, const char *top, char *title, long mode
 					break;
 				fputs(str,stream);
 				l+=strlen(str);	/* byte counter */
-				i++;			/* line counter */
+				lines++;		/* line counter */
 			}
 			fclose(sig);
 		}
@@ -472,7 +530,7 @@ bool sbbs_t::writemsg(const char *fname, const char *top, char *title, long mode
 
 	fclose(stream);
 	free((char *)buf);
-	bprintf(text[SavedNBytes],l,i);
+	bprintf(text[SavedNBytes],l,lines);
 	return(true);
 }
 
@@ -489,6 +547,8 @@ void quotestr(char *str)
 	remove_ctrl_a(str,NULL);
 }
 
+/****************************************************************************/
+/****************************************************************************/
 void sbbs_t::editor_inf(int xeditnum, const char *dest, const char *title, long mode
 	,uint subnum)
 {
@@ -903,10 +963,12 @@ void sbbs_t::editfile(char *fname)
 	char msgtmp[MAX_PATH+1];
 	char str[MAX_PATH+1];
     int file;
-	long length,maxlines,lines,l,mode=0;
+	long length,maxlines,l,mode=0;
+	FILE*	stream;
+	unsigned lines;
 
 	maxlines=cfg.level_linespermsg[useron.level];
-	SAFEPRINTF(path,"%sQUOTES.TXT",cfg.node_dir);
+	quotes_fname(useron.xedit, path, sizeof(path));
 	removecase(path);
 
 	if(useron.xedit) {
@@ -933,11 +995,10 @@ void sbbs_t::editfile(char *fname)
 		CLS;
 		rioctl(IOCM|PAUSE|ABORT);
 		external(cmdstr(cfg.xedit[useron.xedit-1]->rcmd,msgtmp,nulstr,NULL),mode,cfg.node_dir);
-		if(stricmp(msgtmp,path) && !fcompare(msgtmp, path))	{ /* file changed */
-			fcopy(msgtmp, path);
-			SAFEPRINTF2(str,"%s created or edited file: %s",useron.alias, path);
-			logline(nulstr,str);
-		}
+		l=process_edited_file(msgtmp, path, /* mode: */0, &lines);
+		SAFEPRINTF4(str,"%s created or edited file: %s (%u bytes, %u lines)"
+			,useron.alias, path, l, lines);
+		logline(nulstr,str);
 		rioctl(IOSM|PAUSE|ABORT); 
 		return; 
 	}
@@ -973,27 +1034,18 @@ void sbbs_t::editfile(char *fname)
 		return; 
 	}
 	bputs(text[Saving]);
-	if((file=nopen(fname,O_CREAT|O_WRONLY|O_TRUNC))==-1) {
+	if((stream=fnopen(NULL,fname,O_CREAT|O_WRONLY|O_TRUNC))==NULL) {
 		errormsg(WHERE,ERR_OPEN,fname,O_CREAT|O_WRONLY|O_TRUNC);
 		free(buf);
 		return; 
 	}
-	if((size_t)write(file,buf,strlen(buf))!=strlen(buf)) {
-		close(file);
-		errormsg(WHERE,ERR_WRITE,fname,strlen(buf));
-		free(buf);
-		return; 
-	}
-	for(l=lines=0;buf[l];l++)
-		if(buf[l]==LF)
-			lines++;
+	l=process_edited_text(buf,stream,/* mode: */0,&lines);
 	bprintf(text[SavedNBytes],l,lines);
-	close(file);
+	fclose(stream);
 	free(buf);
-	SAFEPRINTF2(str,"%s created or edited file: %s",useron.alias, fname);
+	SAFEPRINTF4(str,"%s created or edited file: %s (%u bytes, %u lines)"
+		,useron.alias, fname, l, lines);
 	logline(nulstr,str);
-
-	return;
 }
 
 /*************************/