#include #include #include #include "neovision/ansi.h" namespace neovision { namespace ansi { // AnsiParser ------------------------------------------------------------------ std::wstring AnsiParser::ControlPortion() const { return m_controlPortion; } std::vector AnsiParser::CsiParameters() const { return m_csiParameters; } std::wstring AnsiParser::CsiPortion() const { return m_csiPortion; } std::wstring AnsiParser::EscapePortion() const { return m_escapePortion; } bool AnsiParser::Parse(wchar_t c) { m_processedChars += c; if (m_state == ParseState::None) ParseNoneState(c); if (m_state == ParseState::Control) { ParseControlState(c); } else if (m_state == ParseState::Escape) { ParseEscapeState(c); } else if (m_state == ParseState::Csi) { ParseCsiState(c); } if (m_state == ParseState::Error) return false; if (m_state == ParseState::None) return false; return true; } void AnsiParser::ParseNoneState(wchar_t c) { const std::wstring cwstr{c}; if (ControlCharacters.count(cwstr)) { m_state = ParseState::Control; } else { m_state = ParseState::Error; } } void AnsiParser::ParseControlState(wchar_t c) { const std::wstring cwstr{c}; if (ControlCharacters.count(cwstr)) { m_controlPortion += cwstr; if (cwstr == ControlCharacter::CSI) m_state = ParseState::Csi; else if (cwstr == ControlCharacter::ESC) m_state = ParseState::Escape; else m_state = ParseState::None; return; } else { m_state = ParseState::Error; } } void AnsiParser::ParseEscapeState(wchar_t c) { m_escapePortion += c; if (EscapeSequences.count(m_escapePortion)) { if (m_escapePortion == EscapeSequence::CSI) m_state = ParseState::Csi; else if (m_escapePortion == EscapeSequence::CSSI) m_state = ParseState::Cssi; } else { // Longest possible escape sequence start is 2 characters. if (m_escapePortion.length() > 2) m_state = ParseState::Error; } } void AnsiParser::ParseCsiState(wchar_t c) { const std::wstring cwstr{c}; m_csiPortion += cwstr; if (CsiSequences.count(cwstr)) { // If c is a csi sequence char, that would mark the end of the sequence. if (!m_parameterBuffer.empty()) { m_csiParameters.emplace_back(m_parameterBuffer); m_parameterBuffer.clear(); } m_state = ParseState::None; } else { /* Characters in the range of 0x30-0x3f are part of a parameter. If the character is a semi-colon, then we're starting the next parameter. */ if (c == L';') { m_csiParameters.emplace_back(m_parameterBuffer); m_parameterBuffer.clear(); } else if ((c >= 0x30) && (c <= 0x3f)) { m_parameterBuffer += c; } else { m_state = ParseState::Error; } } } std::wstring AnsiParser::PmPortion() const { return m_pmPortion; } std::wstring AnsiParser::ProcessedCharacters() const { return m_processedChars; } void AnsiParser::Reset() { m_controlPortion.clear(); m_escapePortion.clear(); m_csiPortion.clear(); m_csiParameters.clear(); m_csiParameterBuffer.clear(); m_cssiPortion.clear(); m_parameterBuffer.clear(); m_pmPortion.clear(); m_processedChars.clear(); m_state = ParseState::None; } AnsiParser::ParseState AnsiParser::State() const { return m_state; } // Functions ------------------------------------------------------------------- std::wstring MakeEscapeSequence(const std::wstring& sequence) { return ControlCharacter::ESC + sequence; } std::wstring MakeCssiSequence(const wchar_t sequence) { return MakeEscapeSequence(EscapeSequence::CSSI + sequence); } std::wstring MakeCsiSequence(const std::vector& sequenceItems) { std::wstring result; if (!sequenceItems.empty()) { for (const std::wstring& item: sequenceItems) { result += item + L";"; } result.pop_back(); } return MakeEscapeSequence(EscapeSequence::CSI + result); } std::wstring MakeDecPmSequence(const std::wstring& sequence, const std::wstring& suffix) { return MakeCsiSequence({ std::wstring{CsiSequence::DECPM}, sequence, suffix }); } std::wstring MakeSgrSequence(const std::vector& sequenceItems) { std::wstring result; if (!sequenceItems.empty()) { for (const std::wstring& item: sequenceItems) { result += item + L";"; } result.pop_back(); } result += CsiSequence::SGR; return MakeCsiSequence({result}); } } // namespace ansi } // namespace neovision