219 lines
4.9 KiB
C++
219 lines
4.9 KiB
C++
#include <cwctype>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
#include "neovision/ansi.h"
|
|
|
|
namespace neovision {
|
|
namespace ansi {
|
|
|
|
// AnsiParser ------------------------------------------------------------------
|
|
|
|
std::wstring AnsiParser::ControlPortion() const
|
|
{
|
|
return m_controlPortion;
|
|
}
|
|
|
|
std::vector<std::wstring> 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<std::wstring>& 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<std::wstring>& 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
|