diff --git a/src/sbbs3/js_global.c b/src/sbbs3/js_global.c
index 9b46e2ae7da3d5292d7b138afed8725994f71ce4..68e578de0795998e897b4db83dfd834bc8e90e8c 100644
--- a/src/sbbs3/js_global.c
+++ b/src/sbbs3/js_global.c
@@ -44,6 +44,7 @@
 #include "ini_file.h"
 #include "js_rtpool.h"
 #include "js_request.h"
+#include "wordwrap.h"
 
 /* SpiderMonkey: */
 #include <jsfun.h>
@@ -715,158 +716,14 @@ js_lfexpand(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
 	return(JS_TRUE);
 }
 
-static int get_prefix(char *text, int *bytes, int *len, int maxlen)
-{
-	int		tmp_prefix_bytes,tmp_prefix_len;
-	int		expect;
-	int		depth;
-
-	*bytes=0;
-	*len=0;
-	tmp_prefix_bytes=0;
-	tmp_prefix_len=0;
-	depth=0;
-	expect=1;
-	if(text[0]!=' ')
-		expect=2;
-	while(expect) {
-		tmp_prefix_bytes++;
-		/* Skip CTRL-A codes */
-		while(text[tmp_prefix_bytes-1]=='\x01') {
-			tmp_prefix_bytes++;
-			if(text[tmp_prefix_bytes-1]=='\x01')
-				break;
-			tmp_prefix_bytes++;
-		}
-		tmp_prefix_len++;
-		if(text[tmp_prefix_bytes-1]==0 || text[tmp_prefix_bytes-1]=='\n' || text[tmp_prefix_bytes-1]=='\r')
-			break;
-		switch(expect) {
-			case 1:		/* At start of possible quote (Next char should be space) */
-				if(text[tmp_prefix_bytes-1]!=' ')
-					expect=0;
-				else
-					expect++;
-				break;
-			case 2:		/* At start of nick (next char should be alphanum or '>') */
-			case 3:		/* At second nick initial (next char should be alphanum or '>') */
-			case 4:		/* At third nick initial (next char should be alphanum or '>') */
-				if(text[tmp_prefix_bytes-1]==' ' || text[tmp_prefix_bytes-1]==0)
-					expect=0;
-				else
-					if(text[tmp_prefix_bytes-1]=='>')
-						expect=6;
-					else
-						expect++;
-				break;
-			case 5:		/* After three regular chars, next HAS to be a '>') */
-				if(text[tmp_prefix_bytes-1]!='>')
-					expect=0;
-				else
-					expect++;
-				break;
-			case 6:		/* At '>' next char must be a space */
-				if(text[tmp_prefix_bytes-1]!=' ')
-					expect=0;
-				else {
-					expect=1;
-					*len=tmp_prefix_len;
-					*bytes=tmp_prefix_bytes;
-					depth++;
-					/* Some editors don't put double spaces in between */
-					if(text[tmp_prefix_bytes]!=' ')
-						expect++;
-				}
-				break;
-			default:
-				expect=0;
-				break;
-		}
-	}
-	if(*bytes >= maxlen) {
-		lprintf(LOG_CRIT, "Prefix bytes %u is larger than buffer (%u) here: %*.*s",*bytes,maxlen,maxlen,maxlen,text);
-		*bytes=maxlen-1;
-	}
-	return(depth);
-}
-
-static void outbuf_append(char **outbuf, char **outp, char *append, int len, int *outlen)
-{
-	char	*p;
-
-	/* Terminate outbuf */
-	**outp=0;
-	/* Check if there's room */
-	if(*outp - *outbuf + len < *outlen) {
-		memcpy(*outp, append, len);
-		*outp+=len;
-		return;
-	}
-	/* Not enough room, double the size. */
-	*outlen *= 2;
-	p=realloc(*outbuf, *outlen);
-	if(p==NULL) {
-		/* Can't do it. */
-		*outlen/=2;
-		return;
-	}
-	/* Set outp for new buffer */
-	*outp=p+(*outp - *outbuf);
-	*outbuf=p;
-	memcpy(*outp, append, len);
-	*outp+=len;
-	return;
-}
-
-static int compare_prefix(char *old_prefix, int old_prefix_bytes, char *new_prefix, int new_prefix_bytes)
-{
-	int i;
-
-	if(new_prefix_bytes != old_prefix_bytes) {
-		if(new_prefix_bytes < old_prefix_bytes) {
-			if(memcmp(old_prefix, new_prefix, new_prefix_bytes)!=0)
-				return(-1);
-			for(i=new_prefix_bytes; i<old_prefix_bytes; i++) {
-				if(!isspace(old_prefix[i]))
-					return(-1);
-			}
-		}
-		else {
-			if(memcmp(old_prefix, new_prefix, old_prefix_bytes)!=0)
-				return(-1);
-			for(i=old_prefix_bytes; i<new_prefix_bytes; i++) {
-				if(!isspace(new_prefix[i]))
-					return(-1);
-			}
-		}
-		return(0);
-	}
-	if(memcmp(old_prefix,new_prefix,new_prefix_bytes)!=0)
-		return(-1);
-
-	return(0);
-}
-
 static JSBool
 js_word_wrap(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
 {
-	int32		l,len=79;
+	int32		len=79;
 	int32		oldlen=79;
-	int32		crcount=0;
 	JSBool		handle_quotes=JS_TRUE;
-	long		i,k,t;
-	int			ocol=1;
-	int			icol=1;
 	uchar*		inbuf;
 	char*		outbuf;
-	char*		outp;
-	char*		linebuf;
-	char*		prefix=NULL;
-	int			prefix_len=0;
-	int			prefix_bytes=0;
-	int			quote_count=0;
-	int			old_prefix_bytes=0;
-	int			outbuf_size=0;
 	JSString*	js_str;
 	jsrefcount	rc;
 
@@ -876,11 +733,6 @@ js_word_wrap(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
 	if((inbuf=js_ValueToStringBytes(cx, argv[0], NULL))==NULL) 
 		return(JS_FALSE);
 
-	outbuf_size=strlen(inbuf)*3+1;
-	if((outbuf=(char*)malloc(outbuf_size))==NULL)
-		return(JS_FALSE);
-	outp=outbuf;
-
 	if(argc>1)
 		JS_ValueToInt32(cx,argv[1],&len);
 
@@ -890,242 +742,18 @@ js_word_wrap(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
 	if(argc>3 && JSVAL_IS_BOOLEAN(argv[3]))
 		handle_quotes=JSVAL_TO_BOOLEAN(argv[3]);
 
-	if((linebuf=(char*)malloc((len*2)+2))==NULL) /* room for ^A codes ToDo: This isn't actually "enough" room */
-		return(JS_FALSE);
-
-	if(handle_quotes) {
-		if((prefix=(char *)malloc((len*2)+2))==NULL) { /* room for ^A codes ToDo: This isn't actually "enough" room */
-			free(linebuf);
-			return(JS_FALSE);
-		}
-		prefix[0]=0;
-	}
 	rc=JS_SUSPENDREQUEST(cx);
 
-	outbuf[0]=0;
-	/* Get prefix from the first line (ouch) */
-	l=0;
-	i=0;
-	if(handle_quotes && (quote_count=get_prefix(inbuf, &prefix_bytes, &prefix_len, len*2+2))!=0) {
-		i+=prefix_bytes;
-		if(prefix_len>len/3*2) {
-			/* This prefix is insane (more than 2/3rds of the new width) hack it down to size */
-			/* Since we're hacking it, we will always end up with a hardcr on this line. */
-			/* ToDo: Something prettier would be nice. */
-			sprintf(prefix," %d> ",quote_count);
-			prefix_len=strlen(prefix);
-			prefix_bytes=strlen(prefix);
-		}
-		else {
-			memcpy(prefix,inbuf,prefix_bytes);
-			/* Terminate prefix */
-			prefix[prefix_bytes]=0;
-		}
-		memcpy(linebuf,prefix,prefix_bytes);
-		l=prefix_bytes;
-		ocol=prefix_len+1;
-		icol=prefix_len+1;
-		old_prefix_bytes=prefix_bytes;
-	}
-	for(; inbuf[i]; i++) {
-		if(l>=len*2+2) {
-			l-=4;
-			linebuf[l]=0;
-			lprintf(LOG_CRIT, "Word wrap line buffer exceeded... munging line %s",linebuf);
-		}
-		switch(inbuf[i]) {
-			case '\r':
-				crcount++;
-				break;
-			case '\n':
-				if(handle_quotes && (quote_count=get_prefix(inbuf+i+1, &prefix_bytes, &prefix_len, len*2+2))!=0) {
-					/* Move the input pointer offset to the last char of the prefix */
-					i+=prefix_bytes;
-				}
-				if(!inbuf[i+1]) {			/* EOF */
-					linebuf[l++]='\r';
-					linebuf[l++]='\n';
-					outbuf_append(&outbuf, &outp, linebuf, l, &outbuf_size);
-					l=0;
-					ocol=1;
-				}
-				/* If there's a new prefix, it is a hardcr */
-				else if(compare_prefix(prefix, old_prefix_bytes, inbuf+i+1-prefix_bytes, prefix_bytes)!=0) {
-					if(prefix_len>len/3*2) {
-						/* This prefix is insane (more than 2/3rds of the new width) hack it down to size */
-						/* Since we're hacking it, we will always end up with a hardcr on this line. */
-						/* ToDo: Something prettier would be nice. */
-						sprintf(prefix," %d> ",quote_count);
-						prefix_len=strlen(prefix);
-						prefix_bytes=strlen(prefix);
-					}
-					else {
-						memcpy(prefix,inbuf+i+1-prefix_bytes,prefix_bytes);
-						/* Terminate prefix */
-						prefix[prefix_bytes]=0;
-					}
-					linebuf[l++]='\r';
-					linebuf[l++]='\n';
-					outbuf_append(&outbuf, &outp, linebuf, l, &outbuf_size);
-					memcpy(linebuf,prefix,prefix_bytes);
-					l=prefix_bytes;
-					ocol=prefix_len+1;
-					old_prefix_bytes=prefix_bytes;
-				}
-				else if(isspace(inbuf[i+1]) && inbuf[i+1] != '\n' && inbuf[i+1] != '\r') {	/* Next line starts with whitespace.  This is a "hard" CR. */
-					linebuf[l++]='\r';
-					linebuf[l++]='\n';
-					outbuf_append(&outbuf, &outp, linebuf, l, &outbuf_size);
-					l=prefix_bytes;
-					ocol=prefix_len+1;
-				}
-				else {
-					if(icol < oldlen) {			/* If this line is overly long, It's impossible for the next word to fit */
-						/* k will equal the length of the first word on the next line */
-						for(k=0; inbuf[i+1+k] && (!isspace(inbuf[i+1+k])); k++);
-						if(icol+k+1 < oldlen) {	/* The next word would have fit but isn't here.  Must be a hard CR */
-							linebuf[l++]='\r';
-							linebuf[l++]='\n';
-							outbuf_append(&outbuf, &outp, linebuf, l, &outbuf_size);
-							if(prefix)
-								memcpy(linebuf,prefix,prefix_bytes);
-							l=prefix_bytes;
-							ocol=prefix_len+1;
-						}
-						else {		/* Not a hard CR... add space if needed */
-							if(l<1 || !isspace(linebuf[l-1])) {
-								linebuf[l++]=' ';
-								ocol++;
-							}
-						}
-					}
-					else {			/* Not a hard CR... add space if needed */
-						if(l<1 || !isspace(linebuf[l-1])) {
-							linebuf[l++]=' ';
-							ocol++;
-						}
-					}
-				}
-				icol=prefix_len+1;
-				break;
-			case '\x1f':	/* Delete... meaningless... strip. */
-				break;
-			case '\b':		/* Backspace... handle if possible, but don't go crazy. */
-				if(l>0) {
-					if(l>1 && linebuf[l-2]=='\x01') {
-						if(linebuf[l-1]=='\x01') {
-							ocol--;
-							icol--;
-						}
-						l-=2;
-					}
-					else {
-						l--;
-						ocol--;
-						icol--;
-					}
-				}
-				break;
-			case '\t':		/* TAB */
-				linebuf[l++]=inbuf[i];
-				/* Can't ever wrap on whitespace remember. */
-				icol++;
-				ocol++;
-				while(ocol%8)
-					ocol++;
-				while(icol%8)
-					icol++;
-				break;
-			case '\x01':	/* CTRL-A */
-				linebuf[l++]=inbuf[i++];
-				if(inbuf[i]!='\x01') {
-					linebuf[l++]=inbuf[i];
-					break;
-				}
-			default:
-				linebuf[l++]=inbuf[i];
-				ocol++;
-				icol++;
-				if(ocol>len && !isspace(inbuf[i])) {		/* Need to wrap here */
-					/* Find the start of the last word */
-					k=l;									/* Original next char */
-					l--;									/* Move back to the last char */
-					while((!isspace(linebuf[l])) && l>0)		/* Move back to the last non-space char */
-						l--;
-					if(l==0) {		/* Couldn't wrap... must chop. */
-						l=k;
-						while(l>1 && linebuf[l-2]=='\x01' && linebuf[l-1]!='\x01')
-							l-=2;
-						if(l>0 && linebuf[l-1]=='\x01')
-							l--;
-						if(l>0)
-							l--;
-					}
-					t=l+1;									/* Store start position of next line */
-					/* Move to start of whitespace */
-					while(l>0 && isspace(linebuf[l]))
-						l--;
-					outbuf_append(&outbuf, &outp, linebuf, l+1, &outbuf_size);
-					outbuf_append(&outbuf, &outp, "\r\n", 2, &outbuf_size);
-					/* Move trailing words to start of buffer. */
-					l=prefix_bytes;
-					if(k-t>0)							/* k-1 is the last char position.  t is the start of the next line position */
-						memmove(linebuf+l, linebuf+t, k-t);
-					l+=k-t;
-					/* Find new ocol */
-					for(ocol=prefix_len+1,t=prefix_bytes; t<l; t++) {
-						switch(linebuf[t]) {
-							case '\x01':	/* CTRL-A */
-								t++;
-								if(linebuf[t]!='\x01')
-									break;
-								/* Fall-through */
-							default:
-								ocol++;
-						}
-					}
-				}
-		}
-	}
-	/* Trailing bits. */
-	if(l) {
-		linebuf[l++]='\r';
-		linebuf[l++]='\n';
-		outbuf_append(&outbuf, &outp, linebuf, l, &outbuf_size);
-	}
-	*outp=0;
-	/* If there were no CRs in the input, strip all CRs */
-	if(!crcount) {
-		for(inbuf=outbuf; *inbuf; inbuf++) {
-			if(*inbuf=='\r')
-				memmove(inbuf, inbuf+1, strlen(inbuf));
-		}
-	}
+	outbuf=wordwrap(inbuf, len, oldlen, handle_quotes);
 
 	JS_RESUMEREQUEST(cx, rc);
+
+	if(outbuf==NULL)
+		return(JS_FALSE);
 	js_str = JS_NewStringCopyZ(cx, outbuf);
 	free(outbuf);
-	free(linebuf);		/* "Damaged Block" assertion here (Feb-09-2009) */
-						/* l=0, len=76, k=0, oldlen=79, outbuf_size=1810 */
-						/* Linebuf:
-035932B8  0D 0A 64 20 01 68 01 6D 57 01 6E 69 6E 01  ..d .h.mW.nin.
-035932C6  68 01 6D 56 01 6E 69 73 74 61 20 01 68 01  h.mV.nista .h.
-035932D4  67 2A 01 6E 66 61 69 6C 65 64 01 68 01 67  g*.nfailed.h.g
-035932E2  2A 01 6E 20 74 6F 20 66 69 78 01 68 01 67  *.n to fix.h.g
-035932F0  2C 01 6E 20 72 69 67 68 74 01 68 01 67 3F  ,.n right.h.g?
-035932FE  01 6E 0D 0A 01 62 33 01 68 01 67 2E 01 68  .n...b3.h.g..h
-0359330C  01 62 31 01 68 01 67 2C 01 6E 20 01 68 01  .b1.h.g,.n .h.
-0359331A  6D 57 01 6E 69 6E 20 01 68 01 62 33 01 68  mW.nin .h.b3.h
-03593328  01 67 2E 01 68 01 62 31 31 34 01 68 01 6D  .g..h.b114.h.m
-03593336  57 47 01 68 01 67 2C 01 6E 20 01 68 01 6D  WG.h.g,.n .h.m
-03593344  57 01 6E 69 6E 01 68 01 62 39 20 61 6E 20  W.nin.h.b9 an 
-03593352  68 FD FD FD DD DD 05 00 */
-
-	if(prefix)
-		free(prefix);
 	if(js_str==NULL)
 		return(JS_FALSE);
-
 	*rval = STRING_TO_JSVAL(js_str);
 	return(JS_TRUE);
 }
diff --git a/src/sbbs3/objects.mk b/src/sbbs3/objects.mk
index e631714375fc580686f156c224fa03c127dc03a8..9896b8911aa6b2742529ec38eaaf8775bf2fc1f2 100644
--- a/src/sbbs3/objects.mk
+++ b/src/sbbs3/objects.mk
@@ -105,6 +105,7 @@ OBJS	=	$(MTOBJODIR)$(DIRSEP)ansiterm$(OFILE) \
 			$(MTOBJODIR)$(DIRSEP)useredit$(OFILE)\
 			$(MTOBJODIR)$(DIRSEP)uucode$(OFILE)\
 			$(MTOBJODIR)$(DIRSEP)viewfile$(OFILE)\
+			$(MTOBJODIR)$(DIRSEP)wordwrap$(OFILE)\
 			$(MTOBJODIR)$(DIRSEP)writemsg$(OFILE)\
 			$(MTOBJODIR)$(DIRSEP)xtrn$(OFILE)\
 			$(MTOBJODIR)$(DIRSEP)xtrn_sec$(OFILE)\
diff --git a/src/sbbs3/sbbs.dsp b/src/sbbs3/sbbs.dsp
index 3e26ba92042f285d0f5716cdcb33bd14769385b2..af5b4e8bfb61aa72ba1f674549687cd85a2493bb 100644
--- a/src/sbbs3/sbbs.dsp
+++ b/src/sbbs3/sbbs.dsp
@@ -491,6 +491,10 @@ SOURCE=.\viewfile.cpp
 # End Source File
 # Begin Source File
 
+SOURCE=.\wordwrap.c
+# End Source File
+# Begin Source File
+
 SOURCE=.\writemsg.cpp
 # End Source File
 # Begin Source File
diff --git a/src/sbbs3/wordwrap.c b/src/sbbs3/wordwrap.c
new file mode 100644
index 0000000000000000000000000000000000000000..2e4de96f4ad75366a3b00b449767d94e723d5c47
--- /dev/null
+++ b/src/sbbs3/wordwrap.c
@@ -0,0 +1,422 @@
+/* $Id$ */
+
+/****************************************************************************
+ * @format.tab-size 4		(Plain Text/Source Code File Header)			*
+ * @format.use-tabs true	(see http://www.synchro.net/ptsc_hdr.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				*
+ * as published by the Free Software Foundation; either version 2			*
+ * of the License, or (at your option) any later version.					*
+ * See the GNU General Public License for more details: gpl.txt or			*
+ * http://www.fsf.org/copyleft/gpl.html										*
+ *																			*
+ * Anonymous FTP access to the most recent released source is available at	*
+ * ftp://vert.synchro.net, ftp://cvs.synchro.net and ftp://ftp.synchro.net	*
+ *																			*
+ * Anonymous CVS access to the development source and modification history	*
+ * is available at cvs.synchro.net:/cvsroot/sbbs, example:					*
+ * cvs -d :pserver:anonymous@cvs.synchro.net:/cvsroot/sbbs login			*
+ *     (just hit return, no password is necessary)							*
+ * cvs -d :pserver:anonymous@cvs.synchro.net:/cvsroot/sbbs checkout src		*
+ *																			*
+ * For Synchronet coding style and modification guidelines, see				*
+ * http://www.synchro.net/source.html										*
+ *																			*
+ * You are encouraged to submit any modifications (preferably in Unix diff	*
+ * format) via e-mail to mods@synchro.net									*
+ *																			*
+ * Note: If this box doesn't appear square, then you need to fix your tabs.	*
+ ****************************************************************************/
+
+#include <genwrap.h>
+#include <stdlib.h>		/* realloc */
+
+static int get_prefix(const char *text, int *bytes, int *len, int maxlen)
+{
+	int		tmp_prefix_bytes,tmp_prefix_len;
+	int		expect;
+	int		depth;
+
+	*bytes=0;
+	*len=0;
+	tmp_prefix_bytes=0;
+	tmp_prefix_len=0;
+	depth=0;
+	expect=1;
+	if(text[0]!=' ')
+		expect=2;
+	while(expect) {
+		tmp_prefix_bytes++;
+		/* Skip CTRL-A codes */
+		while(text[tmp_prefix_bytes-1]=='\x01') {
+			tmp_prefix_bytes++;
+			if(text[tmp_prefix_bytes-1]=='\x01')
+				break;
+			tmp_prefix_bytes++;
+		}
+		tmp_prefix_len++;
+		if(text[tmp_prefix_bytes-1]==0 || text[tmp_prefix_bytes-1]=='\n' || text[tmp_prefix_bytes-1]=='\r')
+			break;
+		switch(expect) {
+			case 1:		/* At start of possible quote (Next char should be space) */
+				if(text[tmp_prefix_bytes-1]!=' ')
+					expect=0;
+				else
+					expect++;
+				break;
+			case 2:		/* At start of nick (next char should be alphanum or '>') */
+			case 3:		/* At second nick initial (next char should be alphanum or '>') */
+			case 4:		/* At third nick initial (next char should be alphanum or '>') */
+				if(text[tmp_prefix_bytes-1]==' ' || text[tmp_prefix_bytes-1]==0)
+					expect=0;
+				else
+					if(text[tmp_prefix_bytes-1]=='>')
+						expect=6;
+					else
+						expect++;
+				break;
+			case 5:		/* After three regular chars, next HAS to be a '>') */
+				if(text[tmp_prefix_bytes-1]!='>')
+					expect=0;
+				else
+					expect++;
+				break;
+			case 6:		/* At '>' next char must be a space */
+				if(text[tmp_prefix_bytes-1]!=' ')
+					expect=0;
+				else {
+					expect=1;
+					*len=tmp_prefix_len;
+					*bytes=tmp_prefix_bytes;
+					depth++;
+					/* Some editors don't put double spaces in between */
+					if(text[tmp_prefix_bytes]!=' ')
+						expect++;
+				}
+				break;
+			default:
+				expect=0;
+				break;
+		}
+	}
+	if(*bytes >= maxlen) {
+//		lprintf(LOG_CRIT, "Prefix bytes %u is larger than buffer (%u) here: %*.*s",*bytes,maxlen,maxlen,maxlen,text);
+		*bytes=maxlen-1;
+	}
+	return(depth);
+}
+
+static void outbuf_append(char **outbuf, char **outp, char *append, int len, int *outlen)
+{
+	char	*p;
+
+	/* Terminate outbuf */
+	**outp=0;
+	/* Check if there's room */
+	if(*outp - *outbuf + len < *outlen) {
+		memcpy(*outp, append, len);
+		*outp+=len;
+		return;
+	}
+	/* Not enough room, double the size. */
+	*outlen *= 2;
+	p=realloc(*outbuf, *outlen);
+	if(p==NULL) {
+		/* Can't do it. */
+		*outlen/=2;
+		return;
+	}
+	/* Set outp for new buffer */
+	*outp=p+(*outp - *outbuf);
+	*outbuf=p;
+	memcpy(*outp, append, len);
+	*outp+=len;
+	return;
+}
+
+static int compare_prefix(char *old_prefix, int old_prefix_bytes, const char *new_prefix, int new_prefix_bytes)
+{
+	int i;
+
+	if(new_prefix_bytes != old_prefix_bytes) {
+		if(new_prefix_bytes < old_prefix_bytes) {
+			if(memcmp(old_prefix, new_prefix, new_prefix_bytes)!=0)
+				return(-1);
+			for(i=new_prefix_bytes; i<old_prefix_bytes; i++) {
+				if(!isspace(old_prefix[i]))
+					return(-1);
+			}
+		}
+		else {
+			if(memcmp(old_prefix, new_prefix, old_prefix_bytes)!=0)
+				return(-1);
+			for(i=old_prefix_bytes; i<new_prefix_bytes; i++) {
+				if(!isspace(new_prefix[i]))
+					return(-1);
+			}
+		}
+		return(0);
+	}
+	if(memcmp(old_prefix,new_prefix,new_prefix_bytes)!=0)
+		return(-1);
+
+	return(0);
+}
+
+char* wordwrap(const char* inbuf, int len, int oldlen, BOOL handle_quotes)
+{
+	int			l;
+	int			crcount=0;
+	long		i,k,t;
+	int			ocol=1;
+	int			icol=1;
+	char*		outbuf;
+	char*		outp;
+	char*		linebuf;
+	char*		prefix=NULL;
+	int			prefix_len=0;
+	int			prefix_bytes=0;
+	int			quote_count=0;
+	int			old_prefix_bytes=0;
+	int			outbuf_size=0;
+
+	outbuf_size=strlen(inbuf)*3+1;
+	if((outbuf=(char*)malloc(outbuf_size))==NULL)
+		return NULL;
+	outp=outbuf;
+
+	if((linebuf=(char*)malloc((len*2)+2))==NULL) /* room for ^A codes ToDo: This isn't actually "enough" room */
+		return NULL;
+
+	if(handle_quotes) {
+		if((prefix=(char *)malloc((len*2)+2))==NULL) { /* room for ^A codes ToDo: This isn't actually "enough" room */
+			free(linebuf);
+			return NULL;
+		}
+		prefix[0]=0;
+	}
+
+	outbuf[0]=0;
+	/* Get prefix from the first line (ouch) */
+	l=0;
+	i=0;
+	if(handle_quotes && (quote_count=get_prefix(inbuf, &prefix_bytes, &prefix_len, len*2+2))!=0) {
+		i+=prefix_bytes;
+		if(prefix_len>len/3*2) {
+			/* This prefix is insane (more than 2/3rds of the new width) hack it down to size */
+			/* Since we're hacking it, we will always end up with a hardcr on this line. */
+			/* ToDo: Something prettier would be nice. */
+			sprintf(prefix," %d> ",quote_count);
+			prefix_len=strlen(prefix);
+			prefix_bytes=strlen(prefix);
+		}
+		else {
+			memcpy(prefix,inbuf,prefix_bytes);
+			/* Terminate prefix */
+			prefix[prefix_bytes]=0;
+		}
+		memcpy(linebuf,prefix,prefix_bytes);
+		l=prefix_bytes;
+		ocol=prefix_len+1;
+		icol=prefix_len+1;
+		old_prefix_bytes=prefix_bytes;
+	}
+	for(; inbuf[i]; i++) {
+		if(l>=len*2+2) {
+			l-=4;
+			linebuf[l]=0;
+//			lprintf(LOG_CRIT, "Word wrap line buffer exceeded... munging line %s",linebuf);
+		}
+		switch(inbuf[i]) {
+			case '\r':
+				crcount++;
+				break;
+			case '\n':
+				if(handle_quotes && (quote_count=get_prefix(inbuf+i+1, &prefix_bytes, &prefix_len, len*2+2))!=0) {
+					/* Move the input pointer offset to the last char of the prefix */
+					i+=prefix_bytes;
+				}
+				if(!inbuf[i+1]) {			/* EOF */
+					linebuf[l++]='\r';
+					linebuf[l++]='\n';
+					outbuf_append(&outbuf, &outp, linebuf, l, &outbuf_size);
+					l=0;
+					ocol=1;
+				}
+				/* If there's a new prefix, it is a hardcr */
+				else if(compare_prefix(prefix, old_prefix_bytes, inbuf+i+1-prefix_bytes, prefix_bytes)!=0) {
+					if(prefix_len>len/3*2) {
+						/* This prefix is insane (more than 2/3rds of the new width) hack it down to size */
+						/* Since we're hacking it, we will always end up with a hardcr on this line. */
+						/* ToDo: Something prettier would be nice. */
+						sprintf(prefix," %d> ",quote_count);
+						prefix_len=strlen(prefix);
+						prefix_bytes=strlen(prefix);
+					}
+					else {
+						memcpy(prefix,inbuf+i+1-prefix_bytes,prefix_bytes);
+						/* Terminate prefix */
+						prefix[prefix_bytes]=0;
+					}
+					linebuf[l++]='\r';
+					linebuf[l++]='\n';
+					outbuf_append(&outbuf, &outp, linebuf, l, &outbuf_size);
+					memcpy(linebuf,prefix,prefix_bytes);
+					l=prefix_bytes;
+					ocol=prefix_len+1;
+					old_prefix_bytes=prefix_bytes;
+				}
+				else if(isspace(inbuf[i+1]) && inbuf[i+1] != '\n' && inbuf[i+1] != '\r') {	/* Next line starts with whitespace.  This is a "hard" CR. */
+					linebuf[l++]='\r';
+					linebuf[l++]='\n';
+					outbuf_append(&outbuf, &outp, linebuf, l, &outbuf_size);
+					l=prefix_bytes;
+					ocol=prefix_len+1;
+				}
+				else {
+					if(icol < oldlen) {			/* If this line is overly long, It's impossible for the next word to fit */
+						/* k will equal the length of the first word on the next line */
+						for(k=0; inbuf[i+1+k] && (!isspace(inbuf[i+1+k])); k++);
+						if(icol+k+1 < oldlen) {	/* The next word would have fit but isn't here.  Must be a hard CR */
+							linebuf[l++]='\r';
+							linebuf[l++]='\n';
+							outbuf_append(&outbuf, &outp, linebuf, l, &outbuf_size);
+							if(prefix)
+								memcpy(linebuf,prefix,prefix_bytes);
+							l=prefix_bytes;
+							ocol=prefix_len+1;
+						}
+						else {		/* Not a hard CR... add space if needed */
+							if(l<1 || !isspace(linebuf[l-1])) {
+								linebuf[l++]=' ';
+								ocol++;
+							}
+						}
+					}
+					else {			/* Not a hard CR... add space if needed */
+						if(l<1 || !isspace(linebuf[l-1])) {
+							linebuf[l++]=' ';
+							ocol++;
+						}
+					}
+				}
+				icol=prefix_len+1;
+				break;
+			case '\x1f':	/* Delete... meaningless... strip. */
+				break;
+			case '\b':		/* Backspace... handle if possible, but don't go crazy. */
+				if(l>0) {
+					if(l>1 && linebuf[l-2]=='\x01') {
+						if(linebuf[l-1]=='\x01') {
+							ocol--;
+							icol--;
+						}
+						l-=2;
+					}
+					else {
+						l--;
+						ocol--;
+						icol--;
+					}
+				}
+				break;
+			case '\t':		/* TAB */
+				linebuf[l++]=inbuf[i];
+				/* Can't ever wrap on whitespace remember. */
+				icol++;
+				ocol++;
+				while(ocol%8)
+					ocol++;
+				while(icol%8)
+					icol++;
+				break;
+			case '\x01':	/* CTRL-A */
+				linebuf[l++]=inbuf[i++];
+				if(inbuf[i]!='\x01') {
+					linebuf[l++]=inbuf[i];
+					break;
+				}
+			default:
+				linebuf[l++]=inbuf[i];
+				ocol++;
+				icol++;
+				if(ocol>len && !isspace(inbuf[i])) {		/* Need to wrap here */
+					/* Find the start of the last word */
+					k=l;									/* Original next char */
+					l--;									/* Move back to the last char */
+					while((!isspace(linebuf[l])) && l>0)		/* Move back to the last non-space char */
+						l--;
+					if(l==0) {		/* Couldn't wrap... must chop. */
+						l=k;
+						while(l>1 && linebuf[l-2]=='\x01' && linebuf[l-1]!='\x01')
+							l-=2;
+						if(l>0 && linebuf[l-1]=='\x01')
+							l--;
+						if(l>0)
+							l--;
+					}
+					t=l+1;									/* Store start position of next line */
+					/* Move to start of whitespace */
+					while(l>0 && isspace(linebuf[l]))
+						l--;
+					outbuf_append(&outbuf, &outp, linebuf, l+1, &outbuf_size);
+					outbuf_append(&outbuf, &outp, "\r\n", 2, &outbuf_size);
+					/* Move trailing words to start of buffer. */
+					l=prefix_bytes;
+					if(k-t>0)							/* k-1 is the last char position.  t is the start of the next line position */
+						memmove(linebuf+l, linebuf+t, k-t);
+					l+=k-t;
+					/* Find new ocol */
+					for(ocol=prefix_len+1,t=prefix_bytes; t<l; t++) {
+						switch(linebuf[t]) {
+							case '\x01':	/* CTRL-A */
+								t++;
+								if(linebuf[t]!='\x01')
+									break;
+								/* Fall-through */
+							default:
+								ocol++;
+						}
+					}
+				}
+		}
+	}
+	/* Trailing bits. */
+	if(l) {
+		linebuf[l++]='\r';
+		linebuf[l++]='\n';
+		outbuf_append(&outbuf, &outp, linebuf, l, &outbuf_size);
+	}
+	*outp=0;
+	/* If there were no CRs in the input, strip all CRs */
+	if(!crcount) {
+		for(inbuf=outbuf; *inbuf; inbuf++) {
+			if(*inbuf=='\r')
+				memmove((void*)inbuf, inbuf+1, strlen(inbuf));
+		}
+	}
+
+	free(linebuf);		/* "Damaged Block" assertion here (Feb-09-2009) */
+						/* l=0, len=76, k=0, oldlen=79, outbuf_size=1810 */
+						/* Linebuf:
+035932B8  0D 0A 64 20 01 68 01 6D 57 01 6E 69 6E 01  ..d .h.mW.nin.
+035932C6  68 01 6D 56 01 6E 69 73 74 61 20 01 68 01  h.mV.nista .h.
+035932D4  67 2A 01 6E 66 61 69 6C 65 64 01 68 01 67  g*.nfailed.h.g
+035932E2  2A 01 6E 20 74 6F 20 66 69 78 01 68 01 67  *.n to fix.h.g
+035932F0  2C 01 6E 20 72 69 67 68 74 01 68 01 67 3F  ,.n right.h.g?
+035932FE  01 6E 0D 0A 01 62 33 01 68 01 67 2E 01 68  .n...b3.h.g..h
+0359330C  01 62 31 01 68 01 67 2C 01 6E 20 01 68 01  .b1.h.g,.n .h.
+0359331A  6D 57 01 6E 69 6E 20 01 68 01 62 33 01 68  mW.nin .h.b3.h
+03593328  01 67 2E 01 68 01 62 31 31 34 01 68 01 6D  .g..h.b114.h.m
+03593336  57 47 01 68 01 67 2C 01 6E 20 01 68 01 6D  WG.h.g,.n .h.m
+03593344  57 01 6E 69 6E 01 68 01 62 39 20 61 6E 20  W.nin.h.b9 an 
+03593352  68 FD FD FD DD DD 05 00 */
+
+	if(prefix)
+		free(prefix);
+
+	return outbuf;
+}
diff --git a/src/sbbs3/wordwrap.h b/src/sbbs3/wordwrap.h
new file mode 100644
index 0000000000000000000000000000000000000000..a21a65ad2e52b4eb31489b04d074f0cd74f503c4
--- /dev/null
+++ b/src/sbbs3/wordwrap.h
@@ -0,0 +1,39 @@
+/* $Id$ */
+
+/****************************************************************************
+ * @format.tab-size 4		(Plain Text/Source Code File Header)			*
+ * @format.use-tabs true	(see http://www.synchro.net/ptsc_hdr.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				*
+ * as published by the Free Software Foundation; either version 2			*
+ * of the License, or (at your option) any later version.					*
+ * See the GNU General Public License for more details: gpl.txt or			*
+ * http://www.fsf.org/copyleft/gpl.html										*
+ *																			*
+ * Anonymous FTP access to the most recent released source is available at	*
+ * ftp://vert.synchro.net, ftp://cvs.synchro.net and ftp://ftp.synchro.net	*
+ *																			*
+ * Anonymous CVS access to the development source and modification history	*
+ * is available at cvs.synchro.net:/cvsroot/sbbs, example:					*
+ * cvs -d :pserver:anonymous@cvs.synchro.net:/cvsroot/sbbs login			*
+ *     (just hit return, no password is necessary)							*
+ * cvs -d :pserver:anonymous@cvs.synchro.net:/cvsroot/sbbs checkout src		*
+ *																			*
+ * For Synchronet coding style and modification guidelines, see				*
+ * http://www.synchro.net/source.html										*
+ *																			*
+ * You are encouraged to submit any modifications (preferably in Unix diff	*
+ * format) via e-mail to mods@synchro.net									*
+ *																			*
+ * Note: If this box doesn't appear square, then you need to fix your tabs.	*
+ ****************************************************************************/
+
+#ifndef _WORDWRAP_H_
+#define _WORDWRAP_H_
+
+char* wordwrap(const char* inbuf, int len, int oldlen, BOOL handle_quotes);
+
+#endif /* Don't add anything after this line */