try allow \a to terminate control strings
Meriel Luna Mittelbach 10 months ago
parent df115ce · commit 311db27
-rw-r--r--src/lib.rs196
1 files changed, 101 insertions, 95 deletions
diff --git a/src/lib.rs b/src/lib.rs
index 4b9585d..976a775 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -5,25 +5,25 @@ mod utf8;
const CLASS_TABLE: [Class; 0x80] = [
Class::C0 ,Class::C0 ,Class::C0 ,Class::C0 ,Class::C0 ,Class::C0 ,Class::C0 ,Class::C0 ,
Class::C0S,Class::C0S,Class::C0S,Class::C0S,Class::C0S,Class::C0S,Class::C0 ,Class::C0 ,
-
+
Class::C0 ,Class::C0 ,Class::C0 ,Class::C0 ,Class::C0 ,Class::C0 ,Class::C0 ,Class::C0 ,
Class::C0 ,Class::C0 ,Class::C0 ,Class::ESC,Class::C0 ,Class::C0 ,Class::C0 ,Class::C0 ,
-
+
Class::INT,Class::INT,Class::INT,Class::INT,Class::INT,Class::INT,Class::INT,Class::INT,
Class::INT,Class::INT,Class::INT,Class::INT,Class::INT,Class::INT,Class::INT,Class::INT,
-
+
Class::PAR,Class::PAR,Class::PAR,Class::PAR,Class::PAR,Class::PAR,Class::PAR,Class::PAR,
Class::PAR,Class::PAR,Class::SEP,Class::SEP,Class::PRI,Class::PRI,Class::PRI,Class::PRI,
-
+
Class::C1 ,Class::C1 ,Class::C1 ,Class::C1 ,Class::C1 ,Class::C1 ,Class::C1 ,Class::C1 ,
Class::C1 ,Class::C1 ,Class::C1 ,Class::C1 ,Class::C1 ,Class::C1 ,Class::C1 ,Class::C1 ,
-
+
Class::CSO,Class::C1 ,Class::C1 ,Class::C1 ,Class::C1 ,Class::C1 ,Class::C1 ,Class::C1 ,
Class::SOS,Class::C1 ,Class::SCI,Class::CSI,Class::ST ,Class::CSO,Class::CSO,Class::CSO,
-
+
Class::ICF,Class::ICF,Class::ICF,Class::ICF,Class::ICF,Class::ICF,Class::ICF,Class::ICF,
Class::ICF,Class::ICF,Class::ICF,Class::ICF,Class::ICF,Class::ICF,Class::ICF,Class::ICF,
-
+
Class::ICF,Class::ICF,Class::ICF,Class::ICF,Class::ICF,Class::ICF,Class::ICF,Class::ICF,
Class::ICF,Class::ICF,Class::ICF,Class::ICF,Class::ICF,Class::ICF,Class::ICF,Class::DEL,
];
@@ -45,7 +45,7 @@ const STATE_TABLE: [State; 0xE0] = [
State::Char, // Ground + ICF
State::Ground, // Ground + DEL
State::Ground, // Ground + PAD
-
+
State::C0Control, // Escape + C0
State::C0Control, // Escape + C0S
State::StartEscape, // Escape + ESC
@@ -62,7 +62,7 @@ const STATE_TABLE: [State; 0xE0] = [
State::FinishSequence, // Escape + ICF
State::Ground, // Escape + DEL
State::Ground, // Escape + PAD
-
+
State::ControlFunctionError, // ControlFunction + C0
State::ControlFunctionError, // ControlFunction + C0S
State::ControlFunctionError, // ControlFunction + ESC
@@ -79,7 +79,7 @@ const STATE_TABLE: [State; 0xE0] = [
State::FinishSequence, // ControlFunction + ICF
State::Ground, // ControlFunction + DEL
State::ControlFunctionError, // ControlFunction + PAD
-
+
State::ControlFunctionError, // ControlFunctionError + C0
State::ControlFunctionError, // ControlFunctionError + C0S
State::ControlFunctionError, // ControlFunctionError + ESC
@@ -96,7 +96,7 @@ const STATE_TABLE: [State; 0xE0] = [
State::Ground, // ControlFunctionError + ICF
State::ControlFunctionError, // ControlFunctionError + DEL
State::ControlFunctionError, // ControlFunctionError + PAD
-
+
State::ControlStringError, // CommandString + C0
State::PushCommandString, // CommandString + C0S
State::CommandStringEscape, // CommandString + ESC
@@ -113,7 +113,7 @@ const STATE_TABLE: [State; 0xE0] = [
State::PushCommandString, // CommandString + ICF
State::ControlStringError, // CommandString + DEL
State::ControlStringError, // CommandString + PAD
-
+
State::ControlStringError, // CommandStringEscape + C0
State::ControlStringError, // CommandStringEscape + C0S
State::ControlStringError, // CommandStringEscape + ESC
@@ -130,7 +130,7 @@ const STATE_TABLE: [State; 0xE0] = [
State::ControlStringError, // CommandStringEscape + ICF
State::ControlStringError, // CommandStringEscape + DEL
State::ControlStringError, // CommandStringEscape + PAD
-
+
State::PushCharacterString, // CharacterString + C0
State::PushCharacterString, // CharacterString + C0S
State::CharacterStringEscape, // CharacterString + ESC
@@ -147,7 +147,7 @@ const STATE_TABLE: [State; 0xE0] = [
State::PushCharacterString, // CharacterString + ICF
State::PushCharacterString, // CharacterString + DEL
State::ControlStringError, // CharacterString + PAD
-
+
State::PushCharacterStringEscape, // CharacterStringEscape + C0
State::PushCharacterStringEscape, // CharacterStringEscape + C0S
State::PushCharacterStringEscape, // CharacterStringEscape + ESC
@@ -164,7 +164,7 @@ const STATE_TABLE: [State; 0xE0] = [
State::PushCharacterStringEscape, // CharacterStringEscape + ICF
State::PushCharacterStringEscape, // CharacterStringEscape + DEL
State::ControlStringError, // CharacterStringEscape + PAD
-
+
State::ControlStringError, // ControlStringError + C0
State::ControlStringError, // ControlStringError + C0S
State::ControlStringError, // ControlStringError + ESC
@@ -181,7 +181,7 @@ const STATE_TABLE: [State; 0xE0] = [
State::ControlStringError, // ControlStringError + ICF
State::ControlStringError, // ControlStringError + DEL
State::ControlStringError, // ControlStringError + PAD
-
+
State::Ground, // SingleCharacter + C0
State::FinishSequence, // SingleCharacter + C0S
State::Ground, // SingleCharacter + ESC
@@ -198,7 +198,7 @@ const STATE_TABLE: [State; 0xE0] = [
State::FinishSequence, // SingleCharacter + ICF
State::Ground, // SingleCharacter + DEL
State::Ground, // SingleCharacter + PAD
-
+
State::ControlSequenceError, // ControlSequence + C0
State::ControlSequenceError, // ControlSequence + C0S
State::ControlSequenceError, // ControlSequence + ESC
@@ -215,7 +215,7 @@ const STATE_TABLE: [State; 0xE0] = [
State::FinishControlSequence, // ControlSequence + ICF
State::ControlSequenceError, // ControlSequence + DEL
State::ControlSequenceError, // ControlSequence + PAD
-
+
State::ControlSequenceError, // ControlSequenceParameter + C0
State::ControlSequenceError, // ControlSequenceParameter + C0S
State::ControlSequenceError, // ControlSequenceParameter + ESC
@@ -232,7 +232,7 @@ const STATE_TABLE: [State; 0xE0] = [
State::FinishControlSequence, // ControlSequenceParameter + ICF
State::ControlSequenceError, // ControlSequenceParameter + DEL
State::ControlSequenceError, // ControlSequenceParameter + PAD
-
+
State::ControlSequenceError, // ControlSequenceIntermediate + C0
State::ControlSequenceError, // ControlSequenceIntermediate + C0S
State::ControlSequenceError, // ControlSequenceIntermediate + ESC
@@ -249,7 +249,7 @@ const STATE_TABLE: [State; 0xE0] = [
State::FinishControlSequence, // ControlSequenceIntermediate + ICF
State::ControlSequenceError, // ControlSequenceIntermediate + DEL
State::ControlSequenceError, // ControlSequenceIntermediate + PAD
-
+
State::ControlSequenceError, // ControlSequenceError + C0
State::ControlSequenceError, // ControlSequenceError + C0S
State::ControlSequenceError, // ControlSequenceError + ESC
@@ -266,7 +266,7 @@ const STATE_TABLE: [State; 0xE0] = [
State::Ground, // ControlSequenceError + ICF
State::ControlSequenceError, // ControlSequenceError + DEL
State::ControlSequenceError, // ControlSequenceError + PAD
-];
+];
#[repr(u8)]
#[derive(Copy, Clone)]
@@ -275,83 +275,83 @@ enum Class {
///
/// 00..1F
C0,
-
+
/// C0 Control Functions permitted in Control Strings
///
/// 08..0D
C0S,
-
+
/// ESCAPE
///
/// 1B
ESC,
-
+
/// Control Function / Control Sequence Intermediate Bytes
///
/// 20..2F
INT,
-
+
/// Control Sequence Parameter Bytes
///
/// 30..39
PAR,
-
+
/// Control Sequence Parameter Separators
///
/// 3A..3B
SEP,
-
+
/// Control Sequence Private Parameter String Indicator
///
/// 3C..3F
PRI,
-
+
/// C1 Control Functions
///
/// ESC 40..5F
C1,
-
+
/// Command String Opening Delimiter
///
/// ESC 50, ESC 5D..5F
CSO,
-
+
/// Start Of String
///
/// ESC 58
SOS,
-
+
/// Single Character Introducer
///
/// ESC 5A
SCI,
-
+
/// Control Sequence Introducer
///
/// ESC 5B
CSI,
-
+
/// String Terminator
///
/// ESC 5C
ST,
-
+
/// Independent Control Function Final Bytes
///
/// 60..7E
ICF,
-
+
/// DELETE
///
/// 7F
- DEL,
+ DEL,
}
#[repr(u8)]
#[derive(Copy, Clone, Eq, PartialEq, Debug)]
enum State {
// All base states
-
+
Ground = 0x00,
Escape = 0x10,
ControlFunction = 0x20,
@@ -366,66 +366,66 @@ enum State {
ControlSequenceParameter = 0xB0,
ControlSequenceIntermediate = 0xC0,
ControlSequenceError = 0xD0,
-
+
// All action states
// The upper 4 bits set a base state to return to (see above),
// the lower 4 bits set an action to perform (see below)
// Base states impicitly have Action::Continue.
-
+
C0Control =
State::Ground as u8 | Action::C01Control as u8,
-
+
Char =
State::Ground as u8 | Action::Char as u8,
-
+
StartEscape =
State::Escape as u8 | Action::StartSequence as u8,
-
+
PushIntermediateByte =
State::ControlFunction as u8 | Action::PushByte as u8,
-
+
C1Control =
State::Escape as u8 | Action::C01Control as u8,
-
+
FinishSequence =
State::Ground as u8 | Action::FinishSequence as u8,
-
+
StartCommandString =
State::CommandString as u8 | Action::StartSequence as u8,
-
+
StartCharacterString =
State::CharacterString as u8 | Action::StartSequence as u8,
-
+
StartSingleCharacter =
State::SingleCharacter as u8 | Action::StartSequence as u8,
-
+
StartControlSequence =
State::ControlSequence as u8 | Action::StartSequence as u8,
-
+
PushCommandString =
State::CommandString as u8 | Action::PushByte as u8,
-
+
PushCharacterString =
State::CharacterString as u8 | Action::PushByte as u8,
-
+
PushCharacterStringEscape =
State::CharacterString as u8 | Action::PushByteWithEscape as u8,
-
+
PrivateControlSequence =
State::ControlSequence as u8 | Action::SetPrivate as u8,
-
+
ControlSequencePushParameter =
State::ControlSequence as u8 | Action::PushParam as u8,
-
+
ControlSequenceAddParameter =
State::ControlSequenceParameter as u8 | Action::AddParamValue as u8,
-
+
ControlSequenceParameterIntermediate =
State::ControlSequenceIntermediate as u8 | Action::PushParamAndByte as u8,
-
+
ControlSequencePushIntermediate =
State::ControlSequenceIntermediate as u8 | Action::PushByte as u8,
-
+
FinishControlSequence =
State::Ground as u8 | Action::PushParamAndEndSequence as u8,
}
@@ -437,37 +437,37 @@ enum Action {
#[allow(dead_code)]
/// Return Continue
Continue,
-
+
/// Return Char
Char,
-
+
/// Set `start`, return Control
C01Control,
-
+
/// Set `start`, return Continue
StartSequence,
-
+
/// Set `end`, return Control
FinishSequence,
-
+
/// Push `byte`, return Continue
PushByte,
-
+
/// Push `Escape`, push `byte`, return Continue
PushByteWithEscape,
-
+
/// Set `private`, return Continue
SetPrivate,
-
+
/// Add to parameter value, return Continue
AddParamValue,
-
+
/// Push `param`, return Continue
PushParam,
-
+
/// Push `param`, push `byte`, return Continue
PushParamAndByte,
-
+
/// Push `param`, set `end`, return Control
PushParamAndEndSequence,
}
@@ -476,12 +476,12 @@ impl State {
/// Decomposes a state into base state and parser action.
fn decompose(self) -> (State, Action) {
use std::mem::transmute as cast;
-
+
unsafe {
(cast(self as u8 & 0xF0), cast(self as u8 & 0x0F))
}
}
-
+
/// Poisons the state
fn poison(&mut self) {
*self = STATE_TABLE[*self as usize + 0xF];
@@ -508,7 +508,7 @@ impl Parameter {
pub fn new(v: u16) -> Self {
Self::Value(v)
}
-
+
/// Returns the value of the parameter, if present, otherwise returns the given default.
pub fn value_or(&self, or: u16) -> u16 {
match self {
@@ -516,7 +516,7 @@ impl Parameter {
Self::Value(v) => *v,
}
}
-
+
/// Parsing parameters requires an [`atoi`]-like loop.
///
/// Parameter value overflow causes the sequence to be rejected.
@@ -528,7 +528,7 @@ impl Parameter {
*self = Self::Value(x);
false
},
-
+
Self::Value(v) => {
let (v2, oflw) = v.overflowing_add(x);
*v = v2;
@@ -581,7 +581,7 @@ pub enum TerminalInput<'a> {
// like SIXEL strings.
// Will require benchmarking though.
Control(&'a ControlFunction),
-
+
SyncControl(char, &'a ControlFunction)
}
@@ -601,14 +601,14 @@ impl TerminalInputParser {
pub fn new() -> Self {
Self::default()
}
-
+
pub fn parse_byte(&mut self, byte: u8) -> TerminalInput {
if byte >= 0x80 {
if self.state != State::Ground {
self.state.poison();
return TerminalInput::Continue;
}
-
+
// UTF-8 here
match self.utf8.decode_byte(byte) {
utf8::DecodeState::Continue => TerminalInput::Continue,
@@ -617,7 +617,7 @@ impl TerminalInputParser {
utf8::DecodeState::Rewind => {
// Recurse, but only once
let again = self.parse_byte(byte);
-
+
match again {
TerminalInput::Continue => TerminalInput::Char('\u{FFFD}'),
TerminalInput::Char(c) => TerminalInput::SyncChar('\u{FFFD}', c),
@@ -631,14 +631,20 @@ impl TerminalInputParser {
} else {
let class = CLASS_TABLE[byte as usize] as usize;
- let state = unsafe {
+ let mut state = unsafe {
*STATE_TABLE.get_unchecked(self.state as usize + class)
};
-
+
+ let is_bell = byte == b'\a';
+ if is_bell && (self.state == State::CommandString || self.state == State::ControlString) {
+ state = State::FinishSequence;
+ }
+
let (base, action) = state.decompose();
-
+
+
self.state = base;
-
+
match action {
Action::Continue => TerminalInput::Continue,
Action::Char => TerminalInput::Char(byte as char),
@@ -646,64 +652,64 @@ impl TerminalInputParser {
self.ctl.start = byte;
TerminalInput::Control(&self.ctl)
},
-
+
Action::StartSequence => {
self.ctl.start = byte;
self.ctl.params.clear();
self.ctl.bytes.clear();
TerminalInput::Continue
},
-
+
Action::FinishSequence => {
self.ctl.end = byte;
TerminalInput::Control(&self.ctl)
},
-
+
Action::PushByte => {
self.ctl.bytes.push(byte);
TerminalInput::Continue
},
-
+
Action::PushByteWithEscape => {
self.ctl.bytes.push(0x1B);
self.ctl.bytes.push(byte);
TerminalInput::Continue
},
-
+
Action::SetPrivate => {
self.ctl.private = true;
TerminalInput::Continue
},
-
+
Action::AddParamValue => {
let oflw = self.pacc.add(byte as u16 - 0x30);
-
+
// You can theoretically do this if-less
// using something like state ^= (0x70 * oflw)
// It just turns the branch into a conditional move and a xor.
if oflw {
self.state = State::ControlSequenceError;
}
-
+
TerminalInput::Continue
},
-
+
Action::PushParam => {
self.ctl.params.push(self.pacc);
self.pacc = Parameter::Default;
-
+
TerminalInput::Continue
},
-
+
Action::PushParamAndByte => {
self.ctl.bytes.push(byte);
self.ctl.params.push(self.pacc);
self.pacc = Parameter::Default;
-
+
TerminalInput::Continue
},
-
- Action::PushParamAndEndSequence => {
+
+ Action::PushParamAndEndSequence => {
self.ctl.params.push(self.pacc);
self.pacc = Parameter::Default;
self.ctl.end = byte;