From 3d71f25528698b8fbe7cf704233a1563a07d921e Mon Sep 17 00:00:00 2001 From: Jordan Orelli Date: Sun, 18 Feb 2024 19:02:43 -0600 Subject: [PATCH] unrecognized escape sequences no longer crash the shell --- src/error.rs | 7 ++- src/input.rs | 121 +++++++++++++++++++++++++++++++++++---------------- 2 files changed, 88 insertions(+), 40 deletions(-) diff --git a/src/error.rs b/src/error.rs index 18d1e00..35716a5 100644 --- a/src/error.rs +++ b/src/error.rs @@ -23,8 +23,11 @@ pub enum InputError { #[error("input record cannot convert to control character because it is an up event")] ControlCharactersOnlyGoDownNotUp, - #[error("bad escape sequence")] - BadEscapeSequence, + #[error("unrecognized escape sequence: {0}")] + BadEscapeSequence(String), + + #[error("fart")] + UnexpectedInputRecordDuringEscapeParting, } #[derive(Debug, Error)] diff --git a/src/input.rs b/src/input.rs index 7d0eb97..aee3f26 100644 --- a/src/input.rs +++ b/src/input.rs @@ -4,11 +4,12 @@ use crate::{ log::*, }; use anyhow::{Context, Result}; +use log::error; use std::{ cell::{RefCell, RefMut}, collections::VecDeque, fmt, - rc::Rc, + rc::{Rc, Weak}, }; use windows::Win32::{Foundation::HANDLE, System::Console}; @@ -181,8 +182,13 @@ impl Reader { } if is_escape_start(&record) { - let escape = self.next_escape_sequence()?; - return Ok(Event::Escape(escape)); + match self.next_escape_sequence() { + Ok(escape) => return Ok(Event::Escape(escape)), + Err(e) => { + error!("{e}"); + return self.next(); + } + } } let event: Event = record.into(); @@ -239,9 +245,12 @@ impl Reader { panic!(); } } - let c = as_escape_character(&record).ok_or(InputError::BadEscapeSequence)?; - if let Some(escape) = self.escapes.step(c) { - return Ok(escape); + let c = as_escape_character(&record) + .ok_or(InputError::UnexpectedInputRecordDuringEscapeParting)?; + match self.escapes.step(c) { + EscapeStep::Continue => {} + EscapeStep::End(escape) => return Ok(escape), + EscapeStep::Abort(s) => return Err(InputError::BadEscapeSequence(s).into()), } } } @@ -333,13 +342,14 @@ impl From for Event { } /// A reference to a target node in a prefix tree, used for manipulating the prefix tree -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct EscapeCursor { target: Rc, root: Rc, } -/// A node in a prefix tree. This prefix tree is used to parse strings into escape sequences +/// A node in a prefix tree. This prefix tree is used to parse strings into escape sequences. This +/// prefix tree is special in that there are no values in the intermediate nodes of the tree. #[derive(Debug)] pub enum EscapeNode { Root { @@ -347,15 +357,18 @@ pub enum EscapeNode { }, Nonterminal { c: char, + parent: Weak, children: RefCell>>, }, Terminal { c: char, + parent: Weak, v: Escape, }, } impl EscapeNode { + /// Creates a new prefix tree mapping character sequences to structured escape codes pub fn new() -> EscapeCursor { let root = Rc::new(EscapeNode::Root { children: RefCell::new(Vec::new()), @@ -382,51 +395,44 @@ impl EscapeNode { } } - fn child(&self, c: char) -> Option> { - for child in self.children().iter_mut() { - if child.char() == c { - return Some(Rc::clone(child)); - } - } - None - } - - fn child_nonterminal(&self, c: char) -> Rc { - for child in self.children().iter_mut() { - if child.char() == c { - return Rc::clone(child); - } + fn parent(&self) -> Option> { + match self { + EscapeNode::Root { .. } => None, + EscapeNode::Nonterminal { parent, .. } => parent.upgrade(), + EscapeNode::Terminal { parent, .. } => parent.upgrade(), } - let child = Rc::new(EscapeNode::Nonterminal { - c, - children: RefCell::new(Vec::new()), - }); - self.children().push(Rc::clone(&child)); - child } - fn add_child_terminal(&self, c: char, v: Escape) { + fn child(&self, c: char) -> Option> { for child in self.children().iter_mut() { if child.char() == c { - panic!(); + return Some(Rc::clone(child)); } } - let child = Rc::new(EscapeNode::Terminal { c, v }); - self.children().push(child); + None } } impl EscapeCursor { - fn step(&mut self, c: char) -> Option { - let child = self.target.child(c)?; + /// advances our cursor by one step. We look at the current position and descend the tree into + /// the node described by the target character. If that node is a terminal node, we return that + /// node's value. + fn step(&mut self, c: char) -> EscapeStep { + let child = match self.target.child(c) { + Some(c) => c, + None => { + let path = self.path(); + return EscapeStep::Abort(format!("{path}{c}")); + } + }; match child.as_ref() { EscapeNode::Terminal { v, .. } => { self.reset(); - Some(*v) + EscapeStep::End(*v) } _ => { self.target = child; - None + EscapeStep::Continue } } } @@ -435,14 +441,36 @@ impl EscapeCursor { match self.target.child(c) { Some(child) => self.target = child, None => { - self.target = self.target.child_nonterminal(c); + for child in self.target.clone().children().iter() { + if child.char() == c { + self.target = Rc::clone(child); + return; + } + } + let child = Rc::new(EscapeNode::Nonterminal { + c, + parent: Rc::downgrade(&self.target), + children: RefCell::new(Vec::new()), + }); + self.target.children().push(Rc::clone(&child)); + self.target = child; } } } /// adds a terminal node into the tree as a child of the current node fn add_terminal(&mut self, c: char, v: Escape) { - self.target.add_child_terminal(c, v); + for child in self.target.children().iter_mut() { + if child.char() == c { + panic!(); + } + } + let child = Rc::new(EscapeNode::Terminal { + c, + parent: Rc::downgrade(&self.target), + v, + }); + self.target.children().push(child); } /// resets the cursor back to the top of the tree @@ -467,6 +495,23 @@ impl EscapeCursor { self.add_step(c); } } + + /// renders as a string the path that takes us to the node to which we are currently pointed + fn path(&self) -> String { + let mut cursor = self.clone(); + let mut chars = Vec::new(); + while !cursor.is_at_root() { + chars.push(cursor.target.char()); + cursor.target = cursor.target.parent().unwrap(); + } + chars.iter().rev().collect() + } +} + +enum EscapeStep { + Continue, + Abort(String), + End(Escape), } /// generates a prefix tree used for parsing escape sequences