Ignore unsupported CSI sequences

Instead of ignoring unexpected intermediates in CSI escape sequences,
the intermediates are now explicitly checked and the escape sequence is
rejected when an unexpected intermediate is found.

Fixes #2171.
This commit is contained in:
Koichi Murase 2019-08-07 07:59:16 +09:00 committed by Christian Duerr
parent b20d285782
commit 33cfc52909
7 changed files with 167 additions and 138 deletions

View File

@ -38,6 +38,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- On Linux, respect fontconfig's `embeddedbitmap` configuration option
- Selecting trailing tab with semantic expansion
- URL parser incorrectly handling Markdown URLs and angled brackets
- Intermediate bytes of CSI sequences not checked
## 0.3.3

View File

@ -890,11 +890,7 @@ where
}
#[inline]
fn csi_dispatch(&mut self, args: &[i64], intermediates: &[u8], _ignore: bool, action: char) {
let private = intermediates.get(0).map(|b| *b == b'?').unwrap_or(false);
let handler = &mut self.handler;
let writer = &mut self.writer;
fn csi_dispatch(&mut self, args: &[i64], intermediates: &[u8], has_ignored_intermediates: bool, action: char) {
macro_rules! unhandled {
() => {{
debug!(
@ -913,12 +909,19 @@ where
};
}
match action {
'@' => handler.insert_blank(Column(arg_or_default!(idx: 0, default: 1) as usize)),
'A' => {
if has_ignored_intermediates || intermediates.len() > 1 {
unhandled!();
}
let handler = &mut self.handler;
let writer = &mut self.writer;
match (action, intermediates.get(0)) {
('@', None) => handler.insert_blank(Column(arg_or_default!(idx: 0, default: 1) as usize)),
('A', None) => {
handler.move_up(Line(arg_or_default!(idx: 0, default: 1) as usize));
},
'b' => {
('b', None) => {
if let Some(c) = self._state.preceding_char {
for _ in 0..arg_or_default!(idx: 0, default: 1) {
handler.input(c);
@ -927,13 +930,13 @@ where
debug!("tried to repeat with no preceding char");
}
},
'B' | 'e' => handler.move_down(Line(arg_or_default!(idx: 0, default: 1) as usize)),
'c' => handler.identify_terminal(writer),
'C' | 'a' => handler.move_forward(Column(arg_or_default!(idx: 0, default: 1) as usize)),
'D' => handler.move_backward(Column(arg_or_default!(idx: 0, default: 1) as usize)),
'E' => handler.move_down_and_cr(Line(arg_or_default!(idx: 0, default: 1) as usize)),
'F' => handler.move_up_and_cr(Line(arg_or_default!(idx: 0, default: 1) as usize)),
'g' => {
('B', None) | ('e', None) => handler.move_down(Line(arg_or_default!(idx: 0, default: 1) as usize)),
('c', None) => handler.identify_terminal(writer),
('C', None) | ('a', None) => handler.move_forward(Column(arg_or_default!(idx: 0, default: 1) as usize)),
('D', None) => handler.move_backward(Column(arg_or_default!(idx: 0, default: 1) as usize)),
('E', None) => handler.move_down_and_cr(Line(arg_or_default!(idx: 0, default: 1) as usize)),
('F', None) => handler.move_up_and_cr(Line(arg_or_default!(idx: 0, default: 1) as usize)),
('g', None) => {
let mode = match arg_or_default!(idx: 0, default: 0) {
0 => TabulationClearMode::Current,
3 => TabulationClearMode::All,
@ -942,14 +945,14 @@ where
handler.clear_tabs(mode);
},
'G' | '`' => handler.goto_col(Column(arg_or_default!(idx: 0, default: 1) as usize - 1)),
'H' | 'f' => {
('G', None) | ('`', None) => handler.goto_col(Column(arg_or_default!(idx: 0, default: 1) as usize - 1)),
('H', None) | ('f', None) => {
let y = arg_or_default!(idx: 0, default: 1) as usize;
let x = arg_or_default!(idx: 1, default: 1) as usize;
handler.goto(Line(y - 1), Column(x - 1));
},
'I' => handler.move_forward_tabs(arg_or_default!(idx: 0, default: 1)),
'J' => {
('I', None) => handler.move_forward_tabs(arg_or_default!(idx: 0, default: 1)),
('J', None) => {
let mode = match arg_or_default!(idx: 0, default: 0) {
0 => ClearMode::Below,
1 => ClearMode::Above,
@ -960,7 +963,7 @@ where
handler.clear_screen(mode);
},
'K' => {
('K', None) => {
let mode = match arg_or_default!(idx: 0, default: 0) {
0 => LineClearMode::Right,
1 => LineClearMode::Left,
@ -970,129 +973,68 @@ where
handler.clear_line(mode);
},
'S' => handler.scroll_up(Line(arg_or_default!(idx: 0, default: 1) as usize)),
'T' => handler.scroll_down(Line(arg_or_default!(idx: 0, default: 1) as usize)),
'L' => handler.insert_blank_lines(Line(arg_or_default!(idx: 0, default: 1) as usize)),
'l' => {
('S', None) => handler.scroll_up(Line(arg_or_default!(idx: 0, default: 1) as usize)),
('T', None) => handler.scroll_down(Line(arg_or_default!(idx: 0, default: 1) as usize)),
('L', None) => handler.insert_blank_lines(Line(arg_or_default!(idx: 0, default: 1) as usize)),
('l', intermediate) => {
let is_private_mode = match intermediate {
Some(b'?') => true,
None => false,
_ => unhandled!(),
};
for arg in args {
let mode = Mode::from_primitive(private, *arg);
let mode = Mode::from_primitive(is_private_mode, *arg);
match mode {
Some(mode) => handler.unset_mode(mode),
None => unhandled!(),
}
}
},
'M' => handler.delete_lines(Line(arg_or_default!(idx: 0, default: 1) as usize)),
'X' => handler.erase_chars(Column(arg_or_default!(idx: 0, default: 1) as usize)),
'P' => handler.delete_chars(Column(arg_or_default!(idx: 0, default: 1) as usize)),
'Z' => handler.move_backward_tabs(arg_or_default!(idx: 0, default: 1)),
'd' => handler.goto_line(Line(arg_or_default!(idx: 0, default: 1) as usize - 1)),
'h' => {
('M', None) => handler.delete_lines(Line(arg_or_default!(idx: 0, default: 1) as usize)),
('X', None) => handler.erase_chars(Column(arg_or_default!(idx: 0, default: 1) as usize)),
('P', None) => handler.delete_chars(Column(arg_or_default!(idx: 0, default: 1) as usize)),
('Z', None) => handler.move_backward_tabs(arg_or_default!(idx: 0, default: 1)),
('d', None) => handler.goto_line(Line(arg_or_default!(idx: 0, default: 1) as usize - 1)),
('h', intermediate) => {
let is_private_mode = match intermediate {
Some(b'?') => true,
None => false,
_ => unhandled!(),
};
for arg in args {
let mode = Mode::from_primitive(private, *arg);
let mode = Mode::from_primitive(is_private_mode, *arg);
match mode {
Some(mode) => handler.set_mode(mode),
None => unhandled!(),
}
}
},
'm' => {
// Sometimes a C-style for loop is just what you need
let mut i = 0; // C-for initializer
('m', None) => {
if args.is_empty() {
handler.terminal_attribute(Attr::Reset);
return;
}
loop {
if i >= args.len() {
// C-for condition
break;
} else {
for attr in attrs_from_sgr_parameters(args) {
match attr {
Some(attr) => handler.terminal_attribute(attr),
None => unhandled!(),
}
}
let attr = match args[i] {
0 => Attr::Reset,
1 => Attr::Bold,
2 => Attr::Dim,
3 => Attr::Italic,
4 => Attr::Underscore,
5 => Attr::BlinkSlow,
6 => Attr::BlinkFast,
7 => Attr::Reverse,
8 => Attr::Hidden,
9 => Attr::Strike,
21 => Attr::CancelBold,
22 => Attr::CancelBoldDim,
23 => Attr::CancelItalic,
24 => Attr::CancelUnderline,
25 => Attr::CancelBlink,
27 => Attr::CancelReverse,
28 => Attr::CancelHidden,
29 => Attr::CancelStrike,
30 => Attr::Foreground(Color::Named(NamedColor::Black)),
31 => Attr::Foreground(Color::Named(NamedColor::Red)),
32 => Attr::Foreground(Color::Named(NamedColor::Green)),
33 => Attr::Foreground(Color::Named(NamedColor::Yellow)),
34 => Attr::Foreground(Color::Named(NamedColor::Blue)),
35 => Attr::Foreground(Color::Named(NamedColor::Magenta)),
36 => Attr::Foreground(Color::Named(NamedColor::Cyan)),
37 => Attr::Foreground(Color::Named(NamedColor::White)),
38 => {
let mut start = 0;
if let Some(color) = parse_color(&args[i..], &mut start) {
i += start;
Attr::Foreground(color)
} else {
break;
}
},
39 => Attr::Foreground(Color::Named(NamedColor::Foreground)),
40 => Attr::Background(Color::Named(NamedColor::Black)),
41 => Attr::Background(Color::Named(NamedColor::Red)),
42 => Attr::Background(Color::Named(NamedColor::Green)),
43 => Attr::Background(Color::Named(NamedColor::Yellow)),
44 => Attr::Background(Color::Named(NamedColor::Blue)),
45 => Attr::Background(Color::Named(NamedColor::Magenta)),
46 => Attr::Background(Color::Named(NamedColor::Cyan)),
47 => Attr::Background(Color::Named(NamedColor::White)),
48 => {
let mut start = 0;
if let Some(color) = parse_color(&args[i..], &mut start) {
i += start;
Attr::Background(color)
} else {
break;
}
},
49 => Attr::Background(Color::Named(NamedColor::Background)),
90 => Attr::Foreground(Color::Named(NamedColor::BrightBlack)),
91 => Attr::Foreground(Color::Named(NamedColor::BrightRed)),
92 => Attr::Foreground(Color::Named(NamedColor::BrightGreen)),
93 => Attr::Foreground(Color::Named(NamedColor::BrightYellow)),
94 => Attr::Foreground(Color::Named(NamedColor::BrightBlue)),
95 => Attr::Foreground(Color::Named(NamedColor::BrightMagenta)),
96 => Attr::Foreground(Color::Named(NamedColor::BrightCyan)),
97 => Attr::Foreground(Color::Named(NamedColor::BrightWhite)),
100 => Attr::Background(Color::Named(NamedColor::BrightBlack)),
101 => Attr::Background(Color::Named(NamedColor::BrightRed)),
102 => Attr::Background(Color::Named(NamedColor::BrightGreen)),
103 => Attr::Background(Color::Named(NamedColor::BrightYellow)),
104 => Attr::Background(Color::Named(NamedColor::BrightBlue)),
105 => Attr::Background(Color::Named(NamedColor::BrightMagenta)),
106 => Attr::Background(Color::Named(NamedColor::BrightCyan)),
107 => Attr::Background(Color::Named(NamedColor::BrightWhite)),
_ => unhandled!(),
};
handler.terminal_attribute(attr);
i += 1; // C-for expr
}
},
'n' => handler.device_status(writer, arg_or_default!(idx: 0, default: 0) as usize),
'r' => {
if private {
unhandled!();
}
('n', None) => handler.device_status(writer, arg_or_default!(idx: 0, default: 0) as usize),
('q', Some(b' ')) => {
// DECSCUSR (CSI Ps SP q) -- Set Cursor Style
let style = match arg_or_default!(idx: 0, default: 0) {
0 => None,
1 | 2 => Some(CursorStyle::Block),
3 | 4 => Some(CursorStyle::Underline),
5 | 6 => Some(CursorStyle::Beam),
_ => unhandled!(),
};
handler.set_cursor_style(style);
},
('r', None) => {
let arg0 = arg_or_default!(idx: 0, default: 1) as usize;
let top = Line(arg0 - 1);
// Bottom should be included in the range, but range end is not
@ -1104,19 +1046,8 @@ where
handler.set_scrolling_region(top..bottom);
},
's' => handler.save_cursor_position(),
'u' => handler.restore_cursor_position(),
'q' => {
let style = match arg_or_default!(idx: 0, default: 0) {
0 => None,
1 | 2 => Some(CursorStyle::Block),
3 | 4 => Some(CursorStyle::Underline),
5 | 6 => Some(CursorStyle::Beam),
_ => unhandled!(),
};
handler.set_cursor_style(style);
},
('s', None) => handler.save_cursor_position(),
('u', None) => handler.restore_cursor_position(),
_ => unhandled!(),
}
}
@ -1174,6 +1105,97 @@ where
}
}
fn attrs_from_sgr_parameters(parameters: &[i64]) -> Vec<Option<Attr>> {
// Sometimes a C-style for loop is just what you need
let mut i = 0; // C-for initializer
let mut attrs = Vec::with_capacity(parameters.len());
loop {
if i >= parameters.len() {
// C-for condition
break;
}
let attr = match parameters[i] {
0 => Some(Attr::Reset),
1 => Some(Attr::Bold),
2 => Some(Attr::Dim),
3 => Some(Attr::Italic),
4 => Some(Attr::Underscore),
5 => Some(Attr::BlinkSlow),
6 => Some(Attr::BlinkFast),
7 => Some(Attr::Reverse),
8 => Some(Attr::Hidden),
9 => Some(Attr::Strike),
21 => Some(Attr::CancelBold),
22 => Some(Attr::CancelBoldDim),
23 => Some(Attr::CancelItalic),
24 => Some(Attr::CancelUnderline),
25 => Some(Attr::CancelBlink),
27 => Some(Attr::CancelReverse),
28 => Some(Attr::CancelHidden),
29 => Some(Attr::CancelStrike),
30 => Some(Attr::Foreground(Color::Named(NamedColor::Black))),
31 => Some(Attr::Foreground(Color::Named(NamedColor::Red))),
32 => Some(Attr::Foreground(Color::Named(NamedColor::Green))),
33 => Some(Attr::Foreground(Color::Named(NamedColor::Yellow))),
34 => Some(Attr::Foreground(Color::Named(NamedColor::Blue))),
35 => Some(Attr::Foreground(Color::Named(NamedColor::Magenta))),
36 => Some(Attr::Foreground(Color::Named(NamedColor::Cyan))),
37 => Some(Attr::Foreground(Color::Named(NamedColor::White))),
38 => {
let mut start = 0;
if let Some(color) = parse_color(&parameters[i..], &mut start) {
i += start;
Some(Attr::Foreground(color))
} else {
None
}
},
39 => Some(Attr::Foreground(Color::Named(NamedColor::Foreground))),
40 => Some(Attr::Background(Color::Named(NamedColor::Black))),
41 => Some(Attr::Background(Color::Named(NamedColor::Red))),
42 => Some(Attr::Background(Color::Named(NamedColor::Green))),
43 => Some(Attr::Background(Color::Named(NamedColor::Yellow))),
44 => Some(Attr::Background(Color::Named(NamedColor::Blue))),
45 => Some(Attr::Background(Color::Named(NamedColor::Magenta))),
46 => Some(Attr::Background(Color::Named(NamedColor::Cyan))),
47 => Some(Attr::Background(Color::Named(NamedColor::White))),
48 => {
let mut start = 0;
if let Some(color) = parse_color(&parameters[i..], &mut start) {
i += start;
Some(Attr::Background(color))
} else {
None
}
},
49 => Some(Attr::Background(Color::Named(NamedColor::Background))),
90 => Some(Attr::Foreground(Color::Named(NamedColor::BrightBlack))),
91 => Some(Attr::Foreground(Color::Named(NamedColor::BrightRed))),
92 => Some(Attr::Foreground(Color::Named(NamedColor::BrightGreen))),
93 => Some(Attr::Foreground(Color::Named(NamedColor::BrightYellow))),
94 => Some(Attr::Foreground(Color::Named(NamedColor::BrightBlue))),
95 => Some(Attr::Foreground(Color::Named(NamedColor::BrightMagenta))),
96 => Some(Attr::Foreground(Color::Named(NamedColor::BrightCyan))),
97 => Some(Attr::Foreground(Color::Named(NamedColor::BrightWhite))),
100 => Some(Attr::Background(Color::Named(NamedColor::BrightBlack))),
101 => Some(Attr::Background(Color::Named(NamedColor::BrightRed))),
102 => Some(Attr::Background(Color::Named(NamedColor::BrightGreen))),
103 => Some(Attr::Background(Color::Named(NamedColor::BrightYellow))),
104 => Some(Attr::Background(Color::Named(NamedColor::BrightBlue))),
105 => Some(Attr::Background(Color::Named(NamedColor::BrightMagenta))),
106 => Some(Attr::Background(Color::Named(NamedColor::BrightCyan))),
107 => Some(Attr::Background(Color::Named(NamedColor::BrightWhite))),
_ => None,
};
attrs.push(attr);
i += 1; // C-for expr
}
attrs
}
/// Parse a color specifier from list of attributes
fn parse_color(attrs: &[i64], i: &mut usize) -> Option<Color> {
if attrs.len() < 2 {

View File

@ -53,6 +53,7 @@ ref_tests! {
grid_reset
row_reset
zerowidth
selective_erasure
}
fn read_u8<P>(path: P) -> Vec<u8>

View File

@ -0,0 +1,2 @@
A[1"qB[2"qC[?2J
A[1"qB[2"qC[?2K

View File

@ -0,0 +1 @@
{"history_size":0}

View File

@ -0,0 +1 @@
{"raw":{"inner":[{"inner":[{"c":" ","fg":{"Named":"Foreground"},"bg":{"Named":"Background"},"flags":{"bits":0},"extra":[" "," "," "," "," "]},{"c":" ","fg":{"Named":"Foreground"},"bg":{"Named":"Background"},"flags":{"bits":0},"extra":[" "," "," "," "," "]},{"c":" ","fg":{"Named":"Foreground"},"bg":{"Named":"Background"},"flags":{"bits":0},"extra":[" "," "," "," "," "]},{"c":" ","fg":{"Named":"Foreground"},"bg":{"Named":"Background"},"flags":{"bits":0},"extra":[" "," "," "," "," "]},{"c":" ","fg":{"Named":"Foreground"},"bg":{"Named":"Background"},"flags":{"bits":0},"extra":[" "," "," "," "," "]},{"c":" ","fg":{"Named":"Foreground"},"bg":{"Named":"Background"},"flags":{"bits":0},"extra":[" "," "," "," "," "]},{"c":" ","fg":{"Named":"Foreground"},"bg":{"Named":"Background"},"flags":{"bits":0},"extra":[" "," "," "," "," "]},{"c":" ","fg":{"Named":"Foreground"},"bg":{"Named":"Background"},"flags":{"bits":0},"extra":[" "," "," "," "," "]},{"c":" ","fg":{"Named":"Foreground"},"bg":{"Named":"Background"},"flags":{"bits":0},"extra":[" "," "," "," "," "]},{"c":" ","fg":{"Named":"Foreground"},"bg":{"Named":"Background"},"flags":{"bits":0},"extra":[" "," "," "," "," "]}],"occ":0},{"inner":[{"c":"A","fg":{"Named":"Foreground"},"bg":{"Named":"Background"},"flags":{"bits":0},"extra":[" "," "," "," "," "]},{"c":"B","fg":{"Named":"Foreground"},"bg":{"Named":"Background"},"flags":{"bits":0},"extra":[" "," "," "," "," "]},{"c":"C","fg":{"Named":"Foreground"},"bg":{"Named":"Background"},"flags":{"bits":0},"extra":[" "," "," "," "," "]},{"c":" ","fg":{"Named":"Foreground"},"bg":{"Named":"Background"},"flags":{"bits":0},"extra":[" "," "," "," "," "]},{"c":" ","fg":{"Named":"Foreground"},"bg":{"Named":"Background"},"flags":{"bits":0},"extra":[" "," "," "," "," "]},{"c":" ","fg":{"Named":"Foreground"},"bg":{"Named":"Background"},"flags":{"bits":0},"extra":[" "," "," "," "," "]},{"c":" ","fg":{"Named":"Foreground"},"bg":{"Named":"Background"},"flags":{"bits":0},"extra":[" "," "," "," "," "]},{"c":" ","fg":{"Named":"Foreground"},"bg":{"Named":"Background"},"flags":{"bits":0},"extra":[" "," "," "," "," "]},{"c":" ","fg":{"Named":"Foreground"},"bg":{"Named":"Background"},"flags":{"bits":0},"extra":[" "," "," "," "," "]},{"c":" ","fg":{"Named":"Foreground"},"bg":{"Named":"Background"},"flags":{"bits":0},"extra":[" "," "," "," "," "]}],"occ":3},{"inner":[{"c":"A","fg":{"Named":"Foreground"},"bg":{"Named":"Background"},"flags":{"bits":0},"extra":[" "," "," "," "," "]},{"c":"B","fg":{"Named":"Foreground"},"bg":{"Named":"Background"},"flags":{"bits":0},"extra":[" "," "," "," "," "]},{"c":"C","fg":{"Named":"Foreground"},"bg":{"Named":"Background"},"flags":{"bits":0},"extra":[" "," "," "," "," "]},{"c":" ","fg":{"Named":"Foreground"},"bg":{"Named":"Background"},"flags":{"bits":0},"extra":[" "," "," "," "," "]},{"c":" ","fg":{"Named":"Foreground"},"bg":{"Named":"Background"},"flags":{"bits":0},"extra":[" "," "," "," "," "]},{"c":" ","fg":{"Named":"Foreground"},"bg":{"Named":"Background"},"flags":{"bits":0},"extra":[" "," "," "," "," "]},{"c":" ","fg":{"Named":"Foreground"},"bg":{"Named":"Background"},"flags":{"bits":0},"extra":[" "," "," "," "," "]},{"c":" ","fg":{"Named":"Foreground"},"bg":{"Named":"Background"},"flags":{"bits":0},"extra":[" "," "," "," "," "]},{"c":" ","fg":{"Named":"Foreground"},"bg":{"Named":"Background"},"flags":{"bits":0},"extra":[" "," "," "," "," "]},{"c":" ","fg":{"Named":"Foreground"},"bg":{"Named":"Background"},"flags":{"bits":0},"extra":[" "," "," "," "," "]}],"occ":3}],"zero":0,"visible_lines":2,"len":3},"cols":10,"lines":3,"display_offset":0,"scroll_limit":0,"max_scroll_limit":0,"url_highlight":null}

View File

@ -0,0 +1 @@
{"width":70.0,"height":63.0,"cell_width":7.0,"cell_height":21.0,"padding_x":0.0,"padding_y":0.0,"dpr":1.0}