try allow \a to terminate control strings
| -rw-r--r-- | src/lib.rs | 196 |
1 files changed, 101 insertions, 95 deletions
@@ -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; |