diff --git a/src/sbbs3/js_global.c b/src/sbbs3/js_global.c
index 894dd7fd051d5b6bd4dacd8ad3e9b09d0183f81b..9324f51736ff52863c9e9ba0aeeb12deedb33d4a 100644
--- a/src/sbbs3/js_global.c
+++ b/src/sbbs3/js_global.c
@@ -1326,6 +1326,7 @@ js_word_wrap(JSContext *cx, uintN argc, jsval *arglist)
 	int32      oldlen = 79;
 	JSBool     handle_quotes = JS_TRUE;
 	JSBool     is_utf8 = JS_FALSE;
+	JSBool     pipe_codes = JS_FALSE;
 	char*      inbuf = NULL;
 	char*      outbuf;
 	JSString*  js_str;
@@ -1360,10 +1361,12 @@ js_word_wrap(JSContext *cx, uintN argc, jsval *arglist)
 		handle_quotes = JSVAL_TO_BOOLEAN(argv[3]);
 	if (argc > 4 && JSVAL_IS_BOOLEAN(argv[4]))
 		is_utf8 = JSVAL_TO_BOOLEAN(argv[4]);
+	if (argc > 5 && JSVAL_IS_BOOLEAN(argv[5]))
+		pipe_codes = JSVAL_TO_BOOLEAN(argv[5]);
 
 	rc = JS_SUSPENDREQUEST(cx);
 
-	outbuf = wordwrap(inbuf, len, oldlen, handle_quotes, is_utf8);
+	outbuf = wordwrap(inbuf, len, oldlen, handle_quotes, is_utf8, pipe_codes);
 	free(inbuf);
 
 	JS_RESUMEREQUEST(cx, rc);
@@ -5179,10 +5182,10 @@ static jsSyncMethodSpec js_global_functions[] = {
 	{"html_decode",     js_html_decode,     1,  JSTYPE_STRING,  JSDOCSTR("html")
 	 , JSDOCSTR("Return a decoded HTML-encoded text string, translating HTML character entities into CP437 character equivalents")
 	 , 311},
-	{"word_wrap",       js_word_wrap,       1,  JSTYPE_STRING,  JSDOCSTR("text [,line_length=79 [,orig_line_length=79 [,<i>bool</i> handle_quotes=true [,<i>bool</i> is_utf8=false]]]]")
+	{"word_wrap",       js_word_wrap,       1,  JSTYPE_STRING,  JSDOCSTR("text [,line_length=79 [,orig_line_length=79 [,<i>bool</i> handle_quotes=true [,<i>bool</i> is_utf8=false [,<i>bool</i> pipe_codes=false]]]]]")
 	 , JSDOCSTR("Return a word-wrapped version of the <i>text</i> string argument optionally handing quotes magically, "
 		        "<i>line_length</i> defaults to <i>79</i>, <i>orig_line_length</i> defaults to <tt>79</tt>, "
-		        "<i>handle_quotes</i> defaults to <tt>true</tt>, and <i>is_utf8</i> defaults to <tt>false</tt>"
+		        "<i>handle_quotes</i> defaults to <tt>true</tt>, <i>is_utf8</i> defaults to <tt>false</tt>, and <i>pipe_codes</i> (Renegade color codes) defaults to <tt>false</tt>."
 		        "<p>Note: if the original text does not contain any carriage-return (CR) characters, lines are wrapped with sole line-feed (LF) characters.")
 	 , 311},
 	{"quote_msg",       js_quote_msg,       1,  JSTYPE_STRING,  JSDOCSTR("text [,line_length=79] [,prefix=\" > \"]")
diff --git a/src/sbbs3/msgtoqwk.cpp b/src/sbbs3/msgtoqwk.cpp
index 8adee910b64a2f8d0a3d9192858a6187837e549d..0e0c7534d82952482f39ed1152d12561e1fbbc91 100644
--- a/src/sbbs3/msgtoqwk.cpp
+++ b/src/sbbs3/msgtoqwk.cpp
@@ -259,7 +259,7 @@ int sbbs_t::msgtoqwk(smbmsg_t* msg, FILE *qwk_fp, int mode, smb_t* smb
 	if (mode & QM_WORDWRAP) {
 		int   org_cols = msg->columns ? msg->columns : 80;
 		int   new_cols = useron.cols ? useron.cols : cols ? cols : 80;
-		char* wrapped = ::wordwrap(buf, new_cols - 1, org_cols - 1, /* handle_quotes */ true, is_utf8);
+		char* wrapped = ::wordwrap(buf, new_cols - 1, org_cols - 1, /* handle_quotes */ true, is_utf8, /* pipe_codes: */false);
 		if (wrapped != NULL) {
 			free(buf);
 			buf = wrapped;
diff --git a/src/sbbs3/putmsg.cpp b/src/sbbs3/putmsg.cpp
index 8ef4acaf0b009d0bd19ff6c3dbfe4895057983f5..8c1dade46cdb71cdb48fd2d4289230506d1bce2c 100644
--- a/src/sbbs3/putmsg.cpp
+++ b/src/sbbs3/putmsg.cpp
@@ -111,7 +111,8 @@ char sbbs_t::putmsgfrag(const char* buf, int& mode, int org_cols, JSObject* obj)
 		if (org_cols < TERM_COLS_MIN)
 			org_cols = TERM_COLS_DEFAULT;
 		if ((wrapped = ::wordwrap((char*)str + l, cols - 1, org_cols - 1, /* handle_quotes: */ TRUE
-		                          , /* is_utf8: */ INT_TO_BOOL(mode & P_UTF8))) == NULL)
+		                          , /* is_utf8: */ INT_TO_BOOL(mode & P_UTF8)
+		                          , /* pipe_codes: */(cfg.sys_misc & SM_RENEGADE) && !INT_TO_BOOL(mode & P_NOXATTRS))) == NULL)
 			errormsg(WHERE, ERR_ALLOC, "wordwrap buffer", 0);
 		else {
 			truncsp_lines(wrapped);
diff --git a/src/sbbs3/wordwrap.c b/src/sbbs3/wordwrap.c
index 1d76a5dd004276c609f6dc0ef816e09f5bc61dfa..be2109772d7a75e616e768cddd1dd10d755f2869 100644
--- a/src/sbbs3/wordwrap.c
+++ b/src/sbbs3/wordwrap.c
@@ -223,7 +223,7 @@ static struct section_len get_ws_len(char *buf, int col)
  * maxlen cols (used to find the number of bytes to fill a specific
  * number of columns).
  */
-static struct section_len get_word_len(char *buf, int maxlen, bool is_utf8)
+static struct section_len get_word_len(char *buf, int maxlen, bool is_utf8, bool pipe_codes)
 {
 	struct section_len ret = {0, 0};
 
@@ -242,6 +242,10 @@ static struct section_len get_word_len(char *buf, int maxlen, bool is_utf8)
 				break;
 			continue;
 		}
+		else if (pipe_codes && buf[ret.bytes] == '|' && IS_DIGIT(buf[ret.bytes + 1]) && IS_DIGIT(buf[ret.bytes + 2])) {
+			ret.bytes += 2;
+			continue;
+		}
 		else if (buf[ret.bytes] == '\b') {
 			// This doesn't handle BS the same way... bit it's kinda BS anyway.
 			ret.len--;
@@ -319,7 +323,7 @@ static bool paragraph_append(struct paragraph *paragraph, const char *bytes, siz
  * The returned malloc()ed array will have the text member of the last
  * paragraph set to NULL.
  */
-static struct paragraph *word_unwrap(char *inbuf, int oldlen, bool handle_quotes, bool *has_crs, bool is_utf8)
+static struct paragraph *word_unwrap(char *inbuf, int oldlen, bool handle_quotes, bool *has_crs, bool is_utf8, bool pipe_codes)
 {
 	unsigned          inpos = 0;
 	struct prefix     new_prefix;
@@ -419,7 +423,7 @@ static struct paragraph *word_unwrap(char *inbuf, int oldlen, bool handle_quotes
 					}
 
 					// If the first word on the next line would have fit here, it's hard
-					next_word_len = get_word_len(inbuf + inpos + 1 + new_prefix_len, -1, is_utf8).len;
+					next_word_len = get_word_len(inbuf + inpos + 1 + new_prefix_len, -1, is_utf8, pipe_codes).len;
 					if ((incol + next_word_len + 1 - 1) < oldlen) {
 						FREE_AND_NULL(new_prefix.bytes);
 						paragraph_done = true;
@@ -439,6 +443,14 @@ static struct paragraph *word_unwrap(char *inbuf, int oldlen, bool handle_quotes
 					while (incol % 8)
 						incol++;
 					break;
+				case '|':       // Pipe color codes (e.g. from Renegade, WWIV, Mystic)
+					if (pipe_codes && IS_DIGIT(inbuf[inpos + 1]) && IS_DIGIT(inbuf[inpos + 2])) {
+						if (!paragraph_append(&ret[paragraph], inbuf + inpos, 3))
+							goto fail_return;
+						inpos += 2;
+						break;
+					}
+					// Fall-through
 				default:
 					if (!paragraph_append(&ret[paragraph], inbuf + inpos, 1))
 						goto fail_return;
@@ -472,7 +484,7 @@ fail_return:
  *
  * Returns a malloc()ed string.
  */
-static char *wrap_paragraphs(struct paragraph *paragraph, size_t outlen, bool handle_quotes, bool has_crs, bool is_utf8)
+static char *wrap_paragraphs(struct paragraph *paragraph, size_t outlen, bool handle_quotes, bool has_crs, bool is_utf8, bool pipe_codes)
 {
 	int                outcol;
 	char *             outbuf = NULL;
@@ -494,7 +506,7 @@ static char *wrap_paragraphs(struct paragraph *paragraph, size_t outlen, bool ha
 				prefix_copy = paragraph->prefix.bytes + strlen(paragraph->prefix.bytes) - (outlen / 2);
 				while (*prefix_copy != ' ')
 					prefix_copy--;
-				word_len = get_word_len(prefix_copy, -1, is_utf8);
+				word_len = get_word_len(prefix_copy, -1, is_utf8, pipe_codes);
 				prefix_cols = word_len.len;
 				prefix_bytes = word_len.bytes;
 			}
@@ -525,10 +537,10 @@ static char *wrap_paragraphs(struct paragraph *paragraph, size_t outlen, bool ha
 				if (*inp == 0)
 					break;
 				ws_len = get_ws_len(inp, outcol);
-				word_len = get_word_len(inp + ws_len.bytes, -1, is_utf8);
+				word_len = get_word_len(inp + ws_len.bytes, -1, is_utf8, pipe_codes);
 				// Do we need to chop a long word?
 				if (word_len.len > (outlen - prefix_cols))
-					word_len = get_word_len(inp + ws_len.bytes, outlen - ws_len.bytes - outcol, is_utf8);
+					word_len = get_word_len(inp + ws_len.bytes, outlen - ws_len.bytes - outcol, is_utf8, pipe_codes);
 				if (outcol + ws_len.len + word_len.len > outlen) {
 					inp += ws_len.bytes;
 					break;
@@ -551,7 +563,7 @@ static char *wrap_paragraphs(struct paragraph *paragraph, size_t outlen, bool ha
 	return outbuf;
 }
 
-char* wordwrap(char* inbuf, int len, int oldlen, bool handle_quotes, bool is_utf8)
+char* wordwrap(char* inbuf, int len, int oldlen, bool handle_quotes, bool is_utf8, bool pipe_codes)
 {
 	char*             outbuf;
 	struct paragraph *paragraphs;
@@ -559,7 +571,7 @@ char* wordwrap(char* inbuf, int len, int oldlen, bool handle_quotes, bool is_utf
 
 	if (oldlen < 1)
 		oldlen = 79;
-	paragraphs = word_unwrap(inbuf, oldlen, handle_quotes, &has_crs, is_utf8);
+	paragraphs = word_unwrap(inbuf, oldlen, handle_quotes, &has_crs, is_utf8, pipe_codes);
 	if (paragraphs == NULL)
 		return NULL;
 #if 0
@@ -567,7 +579,7 @@ char* wordwrap(char* inbuf, int len, int oldlen, bool handle_quotes, bool is_utf
 		fprintf(stderr, "PREFIX: '%s'\nTEXT: '%s'\n\n", paragraphs[i].prefix.bytes, paragraphs[i].text);
 #endif
 
-	outbuf = wrap_paragraphs(paragraphs, len, handle_quotes, has_crs, is_utf8);
+	outbuf = wrap_paragraphs(paragraphs, len, handle_quotes, has_crs, is_utf8, pipe_codes);
 	free_paragraphs(paragraphs, -1);
 	free(paragraphs);
 	return outbuf;
diff --git a/src/sbbs3/wordwrap.h b/src/sbbs3/wordwrap.h
index 13b495c58fbc475b6e4c7cfb60344dc124b3d169..08247693df389c2e9aa85423f2e362196241a2ff 100644
--- a/src/sbbs3/wordwrap.h
+++ b/src/sbbs3/wordwrap.h
@@ -24,7 +24,7 @@
 extern "C" {
 #endif
 
-char* wordwrap(char* inbuf, int len, int oldlen, bool handle_quotes, bool is_utf8);
+char* wordwrap(char* inbuf, int len, int oldlen, bool handle_quotes, bool is_utf8, bool pipe_codes);
 
 #ifdef __cplusplus
 }
diff --git a/src/sbbs3/writemsg.cpp b/src/sbbs3/writemsg.cpp
index 2b4d95cafcfd82948bf1e1667bd108e0aff452ea..c3edb51a921c16acb1a0393873074a6b4097f4ab 100644
--- a/src/sbbs3/writemsg.cpp
+++ b/src/sbbs3/writemsg.cpp
@@ -125,7 +125,7 @@ bool sbbs_t::quotemsg(smb_t* smb, smbmsg_t* msg, bool tails)
 				wrap_cols = cfg.xedit[useron_xedit - 1]->quotewrap_cols;
 			if (wrap_cols == 0)
 				wrap_cols = cols - 1;
-			wrapped = ::wordwrap(buf, wrap_cols, org_cols - 1, /* handle_quotes: */ TRUE, is_utf8);
+			wrapped = ::wordwrap(buf, wrap_cols, org_cols - 1, /* handle_quotes: */ TRUE, is_utf8, /* pipe_codes: */false);
 		}
 		if (wrapped != NULL) {
 			fputs(wrapped, fp);