void CMUSHclientDoc::AnsiNote(LPCTSTR Text) { // save old colours bool bOldNotesInRGB = m_bNotesInRGB; COLORREF iOldNoteColourFore = m_iNoteColourFore; COLORREF iOldNoteColourBack = m_iNoteColourBack; unsigned short iOldNoteStyle = m_iNoteStyle; bool bBold = false; bool bInverse = false; bool bItalic = false; bool bUnderline = false; int iCurrentForeGround = WHITE; int iCurrentBackGround = BLACK; m_iNoteStyle = NORMAL; // start off with normal style const char * p, * start; char c; long length; p = start = Text; while (c = *p) { if (c == ESC) { length = p - start; // output earlier block if (length > 0) Tell (CString (start, length)); p++; // skip the ESC if (*p == '[') { p++; // skip the [ int iCode = 0; while (isdigit (*p) || *p == ';' || *p == 'm') { if (isdigit (c = *p)) { iCode *= 10; iCode += c - '0'; } else if (c == ';' || c == 'm') { switch (iCode) { // reset colours to defaults case ANSI_RESET: iCurrentForeGround = WHITE; iCurrentBackGround = BLACK; bBold = false; bInverse = false; bItalic = false; bUnderline = false; break; // bold case ANSI_BOLD: bBold = true; break; // inverse case ANSI_INVERSE: bInverse = true; break; // blink case ANSI_BLINK: case ANSI_SLOW_BLINK: case ANSI_FAST_BLINK: bItalic = true; break; // underline case ANSI_UNDERLINE: bUnderline = true; break; // not bold case ANSI_CANCEL_BOLD: bBold = false; break; // not inverse case ANSI_CANCEL_INVERSE: bInverse = false; break; // not blink case ANSI_CANCEL_BLINK: case ANSI_CANCEL_SLOW_BLINK: bItalic = false; break; // not underline case ANSI_CANCEL_UNDERLINE: bUnderline = false; break; // different foreground colour case ANSI_TEXT_BLACK: case ANSI_TEXT_RED : case ANSI_TEXT_GREEN : case ANSI_TEXT_YELLOW : case ANSI_TEXT_BLUE : case ANSI_TEXT_MAGENTA: case ANSI_TEXT_CYAN : case ANSI_TEXT_WHITE : iCurrentForeGround = iCode - ANSI_TEXT_BLACK; break; // different background colour case ANSI_BACK_BLACK : case ANSI_BACK_RED : case ANSI_BACK_GREEN : case ANSI_BACK_YELLOW : case ANSI_BACK_BLUE : case ANSI_BACK_MAGENTA: case ANSI_BACK_CYAN : case ANSI_BACK_WHITE : iCurrentBackGround = iCode - ANSI_BACK_BLACK; break; } // end of switch m_iNoteStyle = NORMAL; // select colours if (bBold) { SetNoteColourFore (m_boldcolour [iCurrentForeGround]); SetNoteColourBack (m_normalcolour [iCurrentBackGround]); m_iNoteStyle |= HILITE; } else { SetNoteColourFore (m_normalcolour [iCurrentForeGround]); SetNoteColourBack (m_normalcolour [iCurrentBackGround]); } // select other style bits if (bInverse) m_iNoteStyle |= INVERSE; if (bItalic) m_iNoteStyle |= BLINK; if (bUnderline) m_iNoteStyle |= UNDERLINE; p++; // skip m or ; } // end of ESC [ nn ; or ESC [ nn m if (c == ';') iCode = 0; else if (c == 'm') break; else p++; // next character } // end of getting code } // end of ESC [ something else p++; // skip it start = p; // ready to start a new batch } // end of ESC something else p++; // just keep counting characters } // end of processing each character // output remaining text - and newline Note (start); // put the colours back if (bOldNotesInRGB) { m_iNoteColourFore = iOldNoteColourFore; m_iNoteColourBack = iOldNoteColourBack; } else m_bNotesInRGB = false; // put style back m_iNoteStyle = iOldNoteStyle; } // end of CMUSHclientDoc::AnsiNote
// rewritten: 20th November 2016 - version 5.04 void CMUSHclientDoc::AnsiNote(LPCTSTR Text) { // save old colours bool bOldNotesInRGB = m_bNotesInRGB; COLORREF iOldNoteColourFore = m_iNoteColourFore; COLORREF iOldNoteColourBack = m_iNoteColourBack; unsigned short iOldNoteStyle = m_iNoteStyle; unsigned short iOldNoteTextColour = m_iNoteTextColour; COLORREF rgbNormalForeGround = GetNoteColourFore (); COLORREF rgbNormalBackGround = GetNoteColourBack (); COLORREF rgbBoldForeGround = GetNoteColourFore (); // not sure about these // state machine control variable int ansiState = NONE; m_iNoteStyle = NORMAL; // start off with normal style const char * p, * start; char c; int iCode = 0; // p points to the current character in the text p = start = Text; while ((c = *p++)) // intentional assignment { // we basically have ESC followed by various things, or normal text switch (c) { // ESC potentially starts and ANSI sequence case ESC: // output earlier block if ((p - start) > 0) Tell (CString (start, p - start)); ansiState = HAVE_ESC; break; // end of ESC // it has to be ESC [ ... case '[': if (ansiState == HAVE_ESC) { ansiState = DOING_CODE; iCode = 0; } else ansiState = NONE; // unexpected [ inside an ANSI sequence break; // end of '[' // digits? (eg. ESC [ 5 case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': { switch (ansiState) { case DOING_CODE: case HAVE_FOREGROUND_256_START: case HAVE_FOREGROUND_256_FINISH: case HAVE_BACKGROUND_256_START: case HAVE_BACKGROUND_256_FINISH: case HAVE_FOREGROUND_24B_FINISH: case HAVE_FOREGROUND_24BR_FINISH: case HAVE_FOREGROUND_24BG_FINISH: case HAVE_FOREGROUND_24BB_FINISH: case HAVE_BACKGROUND_24B_FINISH: case HAVE_BACKGROUND_24BR_FINISH: case HAVE_BACKGROUND_24BG_FINISH: case HAVE_BACKGROUND_24BB_FINISH: iCode *= 10; iCode += c - '0'; break; } // end of switch on ansiState } // end of digits break; // end of digits // one of 'm', ';' or ':' terminates the current number, eg. ESC [1; case ';': case ':': case 'm': // interpret the previous code in iCode switch (ansiState) { case DOING_CODE: switch (iCode) { // reset colours to defaults case ANSI_RESET: m_iNoteStyle = NORMAL; // save colours for later (eg. if it becomes bold or not bold) rgbNormalForeGround = m_normalcolour [WHITE]; rgbNormalBackGround = m_normalcolour [BLACK]; rgbBoldForeGround = m_boldcolour [WHITE]; // Note: this sets m_bNotesInRGB mode on SetNoteColourFore (rgbNormalForeGround); SetNoteColourBack (rgbNormalBackGround); break; // bold case ANSI_BOLD: // Note: this sets m_bNotesInRGB mode on SetNoteColourFore (rgbBoldForeGround); m_iNoteStyle |= HILITE; break; // inverse case ANSI_INVERSE: m_iNoteStyle |= INVERSE; break; // blink case ANSI_BLINK: case ANSI_SLOW_BLINK: case ANSI_FAST_BLINK: m_iNoteStyle |= BLINK; break; // underline case ANSI_UNDERLINE: m_iNoteStyle |= UNDERLINE; break; // not bold case ANSI_CANCEL_BOLD: SetNoteColourFore (rgbNormalForeGround); m_iNoteStyle &= ~HILITE; break; // not inverse case ANSI_CANCEL_INVERSE: m_iNoteStyle &= ~INVERSE; break; // not blink case ANSI_CANCEL_BLINK: case ANSI_CANCEL_SLOW_BLINK: m_iNoteStyle &= ~BLINK; break; // not underline case ANSI_CANCEL_UNDERLINE: m_iNoteStyle &= ~UNDERLINE; break; // different foreground colour case ANSI_TEXT_BLACK: case ANSI_TEXT_RED : case ANSI_TEXT_GREEN : case ANSI_TEXT_YELLOW : case ANSI_TEXT_BLUE : case ANSI_TEXT_MAGENTA: case ANSI_TEXT_CYAN : case ANSI_TEXT_WHITE : // save colour for possible later use rgbNormalForeGround = m_normalcolour [iCode - ANSI_TEXT_BLACK]; rgbBoldForeGround = m_boldcolour [iCode - ANSI_TEXT_BLACK]; if (m_iNoteStyle & HILITE) SetNoteColourFore (rgbBoldForeGround); else SetNoteColourFore (rgbNormalForeGround); break; // different background colour case ANSI_BACK_BLACK : case ANSI_BACK_RED : case ANSI_BACK_GREEN : case ANSI_BACK_YELLOW : case ANSI_BACK_BLUE : case ANSI_BACK_MAGENTA: case ANSI_BACK_CYAN : case ANSI_BACK_WHITE : // save colour for possible later use rgbNormalBackGround = m_normalcolour [iCode - ANSI_BACK_BLACK]; SetNoteColourBack (rgbNormalBackGround); break; // we have: ESC 38; case ANSI_TEXT_256_COLOUR : ansiState = HAVE_FOREGROUND_256_START; break; // we have: ESC 48; case ANSI_BACK_256_COLOUR : ansiState = HAVE_BACKGROUND_256_START; break; } // end of switch on iCode iCode = 0; // possibly starting a new code break; // end of DOING_CODE // we now have: ESC [ 38;5; or ESC [ 38;2; case HAVE_FOREGROUND_256_START: if (iCode == 5) // 8-bit colour { ansiState = HAVE_FOREGROUND_256_FINISH; iCode = 0; // start collecting the colour number } else if (iCode == 2) // 24-bit RGB { iCode = 0; // start collecting the red component ansiState = HAVE_FOREGROUND_24B_FINISH; } else // if it isn't 2 or 5 I don't know what the hell it is! { iCode = 0; // possibly starting a new code ansiState = DOING_CODE; // give up and look for another code } break; // end of HAVE_FOREGROUND_256_START case HAVE_FOREGROUND_256_FINISH: rgbNormalForeGround = xterm_256_colours [iCode & 0xFF]; rgbBoldForeGround = rgbNormalForeGround; // both the same SetNoteColourFore (rgbNormalForeGround); iCode = 0; // starting a new code ansiState = DOING_CODE; // no longer collecting 256-colours break; // end of HAVE_FOREGROUND_256_FINISH // have the red component of a 24-bit colour case HAVE_FOREGROUND_24B_FINISH: rgbNormalForeGround = RGB (iCode & 0xFF, 0, 0); rgbBoldForeGround = rgbNormalForeGround; // both the same SetNoteColourFore (rgbNormalForeGround); ansiState = HAVE_FOREGROUND_24BR_FINISH; // now looking for green iCode = 0; // start collecting the green component break; // end of HAVE_FOREGROUND_24B_FINISH // have the green component of a 24-bit colour case HAVE_FOREGROUND_24BR_FINISH: rgbNormalForeGround = RGB (GetRValue(rgbNormalForeGround), iCode & 0xFF, 0); rgbBoldForeGround = rgbNormalForeGround; // both the same SetNoteColourFore (rgbNormalForeGround); ansiState = HAVE_FOREGROUND_24BG_FINISH; // now looking for blue iCode = 0; // start collecting the blue component break; // end of HAVE_FOREGROUND_24BR_FINISH // have the blue component of a 24-bit colour case HAVE_FOREGROUND_24BG_FINISH: rgbNormalForeGround = RGB (GetRValue(rgbNormalForeGround), GetGValue(rgbNormalForeGround), iCode & 0xFF); rgbBoldForeGround = rgbNormalForeGround; // both the same SetNoteColourFore (rgbNormalForeGround); ansiState = DOING_CODE; // no longer collecting 24-bit colours iCode = 0; // starting a new code break; // end of HAVE_FOREGROUND_24BG_FINISH // we now have: ESC [ 48;5; or ESC [ 48;2; case HAVE_BACKGROUND_256_START: if (iCode == 5) // 8-bit colour { ansiState = HAVE_BACKGROUND_256_FINISH; iCode = 0; // start collecting the colour number } else if (iCode == 2) // 24-bit RGB { iCode = 0; // start collecting the red component ansiState = HAVE_BACKGROUND_24B_FINISH; } else // if it isn't 2 or 5 I don't know what the hell it is! { iCode = 0; // starting a new code ansiState = DOING_CODE; // give up and look for another code } break; // end of HAVE_BACKGROUND_256_START case HAVE_BACKGROUND_256_FINISH: rgbNormalBackGround = xterm_256_colours [iCode & 0xFF]; SetNoteColourBack (rgbNormalBackGround); ansiState = DOING_CODE; // no longer collecting 256-colours iCode = 0; // starting a new code break; // end of HAVE_FOREGROUND_256_FINISH // have the red component of a 24-bit colour case HAVE_BACKGROUND_24B_FINISH: rgbNormalBackGround = RGB (iCode & 0xFF, 0, 0); SetNoteColourBack (rgbNormalBackGround); ansiState = HAVE_BACKGROUND_24BR_FINISH; // now looking for green iCode = 0; // start collecting the green component break; // end of HAVE_BACKGROUND_24B_FINISH // have the green component of a 24-bit colour case HAVE_BACKGROUND_24BR_FINISH: rgbNormalBackGround = RGB (GetRValue(rgbNormalBackGround), iCode & 0xFF, 0); SetNoteColourBack (rgbNormalBackGround); ansiState = HAVE_BACKGROUND_24BG_FINISH; // now looking for blue iCode = 0; // start collecting the blue component break; // end of HAVE_BACKGROUND_24BR_FINISH // have the blue component of a 24-bit colour case HAVE_BACKGROUND_24BG_FINISH: rgbNormalBackGround = RGB (GetRValue(rgbNormalBackGround), GetGValue(rgbNormalBackGround), iCode & 0xFF); SetNoteColourBack (rgbNormalBackGround); ansiState = DOING_CODE; // no longer collecting 24-bit colours iCode = 0; // starting a new code break; // end of HAVE_BACKGROUND_24BG_FINISH default: // just accumulate the text break; } // end of switch on ansiState // an 'm' terminates this ANSI sequence (unless we weren't in one) if (ansiState != NONE && c == 'm') { start = p; // ready for outputting the next batch of non-ANSI sequences ansiState = NONE; } break; // end of 'm' or ';' } // end of switch on current character } // end of processing each character // output remaining text - and newline Note (start); // put the colours back if (bOldNotesInRGB) { m_iNoteColourFore = iOldNoteColourFore; m_iNoteColourBack = iOldNoteColourBack; } else { m_bNotesInRGB = false; // put old text colour back m_iNoteTextColour = iOldNoteTextColour; } // put style back m_iNoteStyle = iOldNoteStyle; SetNewLineColour(0); } // end of CMUSHclientDoc::AnsiNote