Terminal: improved handling of escape sequences

Add additional parsing modes to the sequence decoder to detect and
discard unhandled sequences for ECMA-48, DEC private, and Xterm.

Add new behavior for cursor movement, cursor hiding, character deletion,
and line-wrapping.

Fix #2923
This commit is contained in:
Ehmry - 2018-07-18 20:27:17 +02:00 committed by Norman Feske
parent f4ea50c6ff
commit 2041f957da
5 changed files with 733 additions and 189 deletions

View File

@ -67,9 +67,12 @@ class Char_cell_array_character_screen : public Terminal::Character_screen
CURSOR_VISIBLE,
CURSOR_VERY_VISIBLE };
enum Irm { REPLACE, INSERT };
Cell_array<Char_cell> &_char_cell_array;
Terminal::Boundary _boundary;
Terminal::Position _cursor_pos { };
Terminal::Position _cursor_store { };
Terminal::Position _cursor_pos { };
/**
* Color index contains the fg color in the first 3 bits
@ -83,6 +86,10 @@ class Char_cell_array_character_screen : public Terminal::Character_screen
int _region_end;
int _tab_size;
Irm _irm = REPLACE;
bool _wrap = false;
enum { DEFAULT_COLOR_INDEX_BG = 0, DEFAULT_COLOR_INDEX = 7, DEFAULT_TAB_SIZE = 8 };
struct Cursor_guard
@ -107,9 +114,9 @@ class Char_cell_array_character_screen : public Terminal::Character_screen
/* if cursor position changed, move cursor */
Terminal::Position &new_cursor_pos = cs._cursor_pos;
if (old_cursor_pos != new_cursor_pos) {
cs._char_cell_array.cursor(old_cursor_pos, false, true);
cs._char_cell_array.cursor(new_cursor_pos, true, true);
cs._char_cell_array.cursor(
new_cursor_pos, cs._cursor_visibility != CURSOR_INVISIBLE, true);
}
}
};
@ -119,10 +126,16 @@ class Char_cell_array_character_screen : public Terminal::Character_screen
Genode::warning(method_name, " not implemented");
}
void _line_feed()
static void _missing(char const *method_name, int arg)
{
Genode::warning(method_name, " not implemented for ", arg);
}
void _new_line()
{
Cursor_guard guard(*this);
_cursor_pos.x = 0;
_cursor_pos.y++;
if (_cursor_pos.y > _region_end) {
@ -168,24 +181,21 @@ class Char_cell_array_character_screen : public Terminal::Character_screen
void output(Terminal::Character c)
{
if (c.ascii() > 0x10) {
Cursor_guard guard(*this);
_char_cell_array.set_cell(_cursor_pos.x, _cursor_pos.y,
Char_cell(c.ascii(), Font_face::REGULAR,
_color_index, _inverse, _highlight));
_cursor_pos.x++;
}
if (_irm == INSERT)
_missing("insert mode");
switch (c.ascii()) {
case '\n': /* 10 */
_new_line();
break;
case '\r': /* 13 */
_carriage_return();
break;
case '\n': /* 10 */
_line_feed();
_carriage_return();
break;
/* 14: shift-out */
/* 15: shift-in */
case 8: /* backspace */
{
@ -205,30 +215,47 @@ class Char_cell_array_character_screen : public Terminal::Character_screen
}
default:
if (0x1f < c.ascii() && c.ascii() < 0x7f) {
Cursor_guard guard(*this);
_char_cell_array.set_cell(_cursor_pos.x, _cursor_pos.y,
Char_cell(c.ascii(), Font_face::REGULAR,
_color_index, _inverse, _highlight));
_cursor_pos.x++;
}
break;
}
if (_cursor_pos.x >= _boundary.width) {
_carriage_return();
_line_feed();
if (_wrap) {
_new_line();
} else {
_cursor_pos.x = _boundary.width-1;
}
}
}
void cbt() override { _missing(__func__); }
void cha(int pn) override
{
Cursor_guard guard(*this);
_cursor_pos.x = Genode::max(pn - 1, _boundary.width);
}
void civis() override
{
_cursor_visibility = CURSOR_INVISIBLE;
_char_cell_array.cursor(_cursor_pos, false);
}
void cnorm() override
{
_cursor_visibility = CURSOR_VISIBLE;
_char_cell_array.cursor(_cursor_pos, true);
}
void cvvis() override
{
_cursor_visibility = CURSOR_VERY_VISIBLE;
_char_cell_array.cursor(_cursor_pos, true);
}
void csr(int start, int end) override
@ -252,6 +279,14 @@ class Char_cell_array_character_screen : public Terminal::Character_screen
_cursor_pos.x = Genode::max(0, _cursor_pos.x);
}
void cud(int dy) override
{
Cursor_guard guard(*this);
_cursor_pos.y += dy;
_cursor_pos.y = Genode::min(_boundary.height - 1, _cursor_pos.y);
}
void cuf(int dx) override
{
Cursor_guard guard(*this);
@ -275,10 +310,27 @@ class Char_cell_array_character_screen : public Terminal::Character_screen
_cursor_pos = Terminal::Position(x, y);
}
void cud(int) override { _missing(__func__); }
void cuu1() override { _missing(__func__); }
void cuu(int) override { _missing(__func__); }
void dch(int) override { _missing(__func__); }
void cuu(int dy) override
{
Cursor_guard guard(*this);
_cursor_pos.x += dy;
_cursor_pos.y = Genode::max(0, _cursor_pos.y);
}
void da(int) override { _missing(__func__); }
void dch(int pn) override
{
pn = Genode::min(_boundary.width - _cursor_pos.x, pn);
for (int x = _cursor_pos.x; x < _boundary.width; ++x) {
_char_cell_array.set_cell(x, _cursor_pos.y,
_char_cell_array.get_cell(x+pn, _cursor_pos.y));
}
for (int x = _boundary.width - pn; x < _boundary.width; ++x) {
_char_cell_array.set_cell(x, _cursor_pos.y, Char_cell());
}
}
void dl(int num_lines) override
{
@ -287,21 +339,75 @@ class Char_cell_array_character_screen : public Terminal::Character_screen
_char_cell_array.scroll_up(_cursor_pos.y, _region_end);
}
void ed() override
/**
* Erase character
*/
void ech(int pn)
{
int y = _cursor_pos.y;
int x = _cursor_pos.x;
do {
while (x < _boundary.width && pn) {
_char_cell_array.set_cell(x++, y, Char_cell());
--pn;
}
x = 0;
++y;
} while (pn);
}
/**
* Erase in page
*/
void ed(int ps) override
{
/* clear to end of screen */
el();
_char_cell_array.clear(_cursor_pos.y + 1, _boundary.height - 1);
switch(ps) {
case 0:
for (int x = _cursor_pos.x; x < _boundary.width; ++x)
_char_cell_array.set_cell(x, _cursor_pos.y, Char_cell());
_char_cell_array.clear(_cursor_pos.y + 1, _boundary.height-1);
return;
case 1:
_char_cell_array.clear(0, _cursor_pos.y-1);
for (int x = 0; x <= _cursor_pos.x; ++x)
_char_cell_array.set_cell(x, _cursor_pos.y, Char_cell());
return;
case 2:
_char_cell_array.clear(0, _boundary.height-1);
return;
default:
Genode::warning(__func__, " not implemented for ", ps);
break;
}
}
void el() override
void el(int ps) override
{
/* clear to end of line */
for (int x = _cursor_pos.x; x < _boundary.width; x++)
_char_cell_array.set_cell(x, _cursor_pos.y, Char_cell());
switch (ps) {
case 0: /* clear to end of line */
for (int x = _cursor_pos.x; x < _boundary.width; ++x)
_char_cell_array.set_cell(x, _cursor_pos.y, Char_cell());
return;
case 1: /* clear from begining of line */
for (int x = 0; x <= _cursor_pos.x; ++x)
_char_cell_array.set_cell(x, _cursor_pos.y, Char_cell());
return;
case 2:
_char_cell_array.clear(_cursor_pos.y, _cursor_pos.y);
return;
default: _missing(__func__, ps);
}
}
void el1() override { _missing(__func__); }
void enacs() override { _missing(__func__); }
void flash() override { _missing(__func__); }
@ -309,11 +415,27 @@ class Char_cell_array_character_screen : public Terminal::Character_screen
{
Cursor_guard guard(*this);
_cursor_pos = Terminal::Position(0, 0);
_cursor_pos.x = 0;
}
void hts() override { _missing(__func__); }
void ich(int) override { _missing(__func__); }
void hts() override
{
_tab_size = _cursor_pos.x;
}
void ich(int pn) override
{
pn = Genode::min(_boundary.width - _cursor_pos.x, pn);
for (int x = _boundary.width-1; _cursor_pos.x+pn < x; --x) {
_char_cell_array.set_cell(x, _cursor_pos.y,
_char_cell_array.get_cell(x-1, _cursor_pos.y));
}
for (int i = 0; i < pn; ++i) {
_char_cell_array.set_cell(
_cursor_pos.x+i, _cursor_pos.y, Char_cell());
}
}
void il(int value) override
{
@ -338,12 +460,47 @@ class Char_cell_array_character_screen : public Terminal::Character_screen
_color_index = DEFAULT_COLOR_INDEX | (DEFAULT_COLOR_INDEX_BG << 3);
}
void rm(int ps) override
{
switch (ps) {
case 4: /* INSERTION REPLACEMENT MODE */
_irm = REPLACE;
break;
case 34: /* cursor visibility */
return cnorm();
default:
_missing(__func__, ps);
break;
}
}
void sm(int ps) override
{
switch (ps) {
case 4: /* INSERTION REPLACEMENT MODE */
_irm = INSERT;
break;
case 34: /* cursor visibility */
return civis();
break;
default:
_missing(__func__, ps);
break;
}
}
void rc() override { _missing(__func__); }
void rs2() override { _missing(__func__); }
void rmir() override { _missing(__func__); }
void rmcup() override { }
void rmkx() override { }
void sd(int pn) override
{
for (int i = 0; i < pn; ++i)
_char_cell_array.scroll_down(_region_start, _region_end);
}
void setab(int value) override
{
_color_index &= ~0x38; /* clear 111000 */
@ -376,13 +533,99 @@ class Char_cell_array_character_screen : public Terminal::Character_screen
}
}
void sgr0() override { sgr(0); }
void sc() override { _missing(__func__); }
void smcup() override { }
void smir() override { _missing(__func__); }
void smkx() override { }
void tbc() override { _missing(__func__); }
void su(int pn) override
{
for (int i = 0; i < pn; ++i)
_char_cell_array.scroll_up(_region_start, _region_end);
}
void tbc() override { _missing(__func__); }
void tsr(int pn) override
{
_missing(__func__, pn);
/*
int x = pn;
for (int y = _cursor_pos.y; y < _boundary.height-1; ++y) {
for (int i = 0; i < _tab_size; ++i) {
}
}
*/
}
void vpa(int pn)
{
Cursor_guard guard(*this);
_cursor_pos.x = pn;
}
void vpb(int pn)
{
Cursor_guard guard(*this);
_cursor_pos.x = Genode::min(0, _cursor_pos.x - pn);
}
void decsc() override
{
_cursor_store = _cursor_pos;
}
void decrc() override
{
Cursor_guard guard(*this);
_cursor_pos = _cursor_store;
}
void decsm(int p1, int) override
{
switch (p1) {
case 1: _missing("Application Cursor Keys"); return; //return smkx();
case 7: _wrap = false; return;
case 25:
case 34: return cnorm(); // Visible Cursor
case 1000: return _missing("VT200 mouse tracking");
case 1002: return _missing("xterm butten event mouse");
case 1003: return _missing("xterm any event mouse");
case 1049: return _missing("Alternate Screen (new xterm code)"); //smcup();
default: break;
}
_missing(__func__, p1);
}
void decrm(int p1, int) override
{
switch (p1) {
case 1: _missing("Application Cursor Keys"); return; //return rmkx();
case 7: _wrap = true; return;
case 25:
case 34: return civis();
case 1000: return _missing("VT200 mouse tracking"); //rs2();
case 1002: return _missing("xterm butten event mouse");
case 1003: return _missing("xterm any event mouse");
case 1049: return _missing("Alternate Screen (new xterm code)"); //rmcup();
default: break;
}
_missing(__func__, p1);
}
void scs_g0(int charset) override { _missing(__func__, charset); }
void scs_g1(int charset) override { _missing(__func__, charset); }
void reverse_index()
{
Cursor_guard guard(*this);
if (_cursor_pos.y) {
_cursor_pos.y = _cursor_pos.y - 1;
} else {
_char_cell_array.scroll_down(_region_start, _region_end);
}
};
};
#endif /* _TERMINAL__CHAR_CELL_ARRAY_CHARACTER_SCREEN_H_ */

View File

@ -26,7 +26,6 @@ struct Terminal::Character_screen : Genode::Interface
{
virtual void output(Character c) = 0;
/*******************
** VT Operations **
*******************/
@ -38,9 +37,9 @@ struct Terminal::Character_screen : Genode::Interface
*/
/**
* Back tab
* Cursor Character Absolute - 8.3.9
*/
virtual void cbt() = 0;
virtual void cha(int pn = 1) = 0;
/**
* Make cursor invisible
@ -65,12 +64,12 @@ struct Terminal::Character_screen : Genode::Interface
/**
* Move cursor backwards
*/
virtual void cub(int) = 0;
virtual void cub(int pn = 1) = 0;
/**
* Non-destructive space - move right #1 spaces
* Cursor right - 8.3.20
*/
virtual void cuf(int) = 0;
virtual void cuf(int pn = 1) = 0;
/**
* Move cursor to row #1 column #2
@ -78,44 +77,45 @@ struct Terminal::Character_screen : Genode::Interface
virtual void cup(int, int) = 0;
/**
* Down #1 lines
* Cursor Down - 8.3.19
*/
virtual void cud(int) = 0;
virtual void cud(int pn = 1) = 0;
/**
* Move cursor up one line
* Cursor Up - 8.3.22
*/
virtual void cuu1() = 0;
virtual void cuu(int pn = 1) = 0;
/**
* Up #1 lines
* Device Attributes - 8.3.24
*/
virtual void cuu(int) = 0;
virtual void da(int ps = 0) = 0;
/**
* Delete #1 characters
* Delete Character - 8.3.26
*/
virtual void dch(int) = 0;
virtual void dch(int pn = 1) = 0;
/**
* Delete #1 lines
* Delete line - 8.3.32
*/
virtual void dl(int) = 0;
virtual void dl(int pn = 1) = 0;
/**
* Clear to end of screen
* Erase Character - 8.3.38
*/
virtual void ed() = 0;
virtual void ech(int pn = 1) = 0;
/**
* Clear to end of line
* Erase in page - 8.3.39
*/
virtual void el() = 0;
virtual void ed(int ps = 0) = 0;
/**
* Clear to beginning of line
* Erase in line - 8.3.41
*/
virtual void el1() = 0;
virtual void el(int ps = 0) = 0;
/**
* Enable alternative character set
@ -133,19 +133,19 @@ struct Terminal::Character_screen : Genode::Interface
virtual void home() = 0;
/**
* Set a tab in every row, current column
* Set a tab in every row, current column - 8.3.62
*/
virtual void hts() = 0;
/**
* Insert #1 characters
* Insert character - 8.3.64
*/
virtual void ich(int) = 0;
virtual void ich(int pn = 1) = 0;
/**
* Insert #1 lines
* Insert line - 8.3.67
*/
virtual void il(int) = 0;
virtual void il(int pn = 1) = 0;
/**
* Initialization string
@ -167,6 +167,11 @@ struct Terminal::Character_screen : Genode::Interface
*/
virtual void rc() = 0;
/**
* Reset Mode - 8.3.106
*/
virtual void rm(int) = 0;
/**
* Reset string
*/
@ -187,6 +192,11 @@ struct Terminal::Character_screen : Genode::Interface
*/
virtual void rmkx() = 0;
/**
* Scroll Down - 8.3.113
*/
virtual void sd(int pn = 1) = 0;
/**
* Set background color to #1, using ANSI escape
*/
@ -198,19 +208,19 @@ struct Terminal::Character_screen : Genode::Interface
virtual void setaf(int) = 0;
/**
* Set attribute
* Select Graphic Rendition - 8.3.117
*/
virtual void sgr(int) = 0;
virtual void sgr(int ps = 0) = 0;
/**
* Turn of all attributes
* Set mode 8.3.125
*/
virtual void sgr0() = 0;
virtual void sm(int) = 0;
/**
* Save current cursor position
* Scroll Up - 8.3.147
*/
virtual void sc() = 0;
virtual void su(int pn = 1) = 0;
/**
* Enter cup mode
@ -231,6 +241,59 @@ struct Terminal::Character_screen : Genode::Interface
* Clear all tab stops
*/
virtual void tbc() = 0;
/**
* Tabulation Stop Remove - 8.3.156
*/
virtual void tsr(int) = 0;
/**
* Line position absolute - 8.3.158
*/
virtual void vpa(int pn = 1) = 0;
/**
* Line position backward - 8.3.159
*/
virtual void vpb(int pn = 1) = 0;
/*****************
** DEC private **
*****************/
/**
* Save Cursor
*/
virtual void decsc() = 0;
/**
* Restore Cursor
*/
virtual void decrc() = 0;
/**
* Set mode
*/
virtual void decsm(int p1, int p2 = 0) = 0;
/**
* Reset mode
*/
virtual void decrm(int p1, int p2 = 0) = 0;
/**************************
** Select Character Set **
**************************/
virtual void scs_g0(int) = 0;
virtual void scs_g1(int) = 0;
/*************
** Unknown **
*************/
virtual void reverse_index() = 0;
};
#endif /* _TERMINAL__CHARACTER_SCREEN_H_ */

View File

@ -15,10 +15,10 @@
#define _TERMINAL__DECODER_H_
#include <terminal/character_screen.h>
#include <terminal/print.h>
namespace Terminal { class Decoder; }
class Terminal::Decoder
{
private:
@ -61,11 +61,24 @@ class Terminal::Decoder
return number % factor;
}
enum State {
STATE_IDLE,
STATE_ESC_CSI, /* read CONTROL SEQUENCE INTRODUCER */
STATE_ESC_ECMA, /* read an ECMA-48 escape sequence */
STATE_ESC_SCS, /* read an Select Character Set sequence */
STATE_ESC_VT100, /* read a VT100 escape sequence */
STATE_ESC_OSC /* skip an Operating System Command */
};
/**
* Buffer used for collecting escape sequences
*/
class Escape_stack
{
private:
Log_buffer _dump_log { };
public:
struct Entry
@ -74,6 +87,22 @@ class Terminal::Decoder
int type = INVALID;
int value = 0;
void print(Genode::Output &out, State state) const
{
if (type == NUMBER) {
Genode::print(out, value);
} else if (state == STATE_ESC_ECMA) {
Ecma(value).print(out);
} else {
Ascii(value).print(out);
}
}
void print(Genode::Output &out) const
{
print(out, STATE_ESC_VT100);
}
};
struct Number_entry : Entry
@ -90,21 +119,16 @@ class Terminal::Decoder
private:
enum { MAX_ENTRIES = 16 };
enum { MAX_ENTRIES = 32 };
Entry _entries[MAX_ENTRIES];
int _index;
void _dump() const
void _dump(State state)
{
Genode::log("--- escape stack follows ---");
_dump_log.print("ESC");
for (int i = 0; i < _index; i++) {
int type = _entries[i].type;
int value = _entries[i].value;
Genode::log(type == Entry::INVALID ? " INVALID" :
type == Entry::NUMBER ? " NUMBER "
: " CODE ",
" ", value, " ",
"(", Genode::Hex(value), ")");
_dump_log.out_char(' ');
_entries[i].print(_dump_log, state);
}
}
@ -114,11 +138,20 @@ class Terminal::Decoder
void reset() { _index = 0; }
void discard(State state = STATE_ESC_VT100)
{
_dump_log.print("unhandled sequence ");
_dump(state);
_dump_log.flush_warning();
_index = 0;
}
void push(Entry const &entry)
{
if (_index == MAX_ENTRIES - 1) {
Genode::error("escape stack overflow");
_dump();
_dump(STATE_ESC_VT100);
_dump_log.flush_error();
reset();
return;
}
@ -135,44 +168,50 @@ class Terminal::Decoder
*
* 'index' is relative to the bottom of the stack.
*/
Entry operator [] (int index)
Entry operator [] (int index) const
{
return (index <= _index) ? _entries[index] : Invalid_entry();
}
} _escape_stack { };
enum State {
STATE_IDLE,
STATE_ESC_SEQ, /* read escape sequence */
STATE_ESC_NUMBER /* read number argument within escape sequence */
} _state;
Character_screen &_screen;
int _number; /* current number argument supplied in escape sequence */
State _state = STATE_IDLE;
int _number = -1; /* current number argument supplied in escape sequence */
void _append_to_number(char c)
{
_number = _number*10 + digit(c);
_number = (_number < 0 ? 0 : _number)*10 + digit(c);
}
void _enter_state_idle()
{
_state = STATE_IDLE;
_escape_stack.reset();
_number = -1;
}
void _enter_state_esc_seq()
void _enter_state_esc_csi()
{
_state = STATE_ESC_SEQ;
_state = STATE_ESC_CSI;
_escape_stack.reset();
}
void _enter_state_esc_number()
void _enter_state_esc_ecma()
{
_state = STATE_ESC_NUMBER;
_number = 0;
_state = STATE_ESC_ECMA;
}
void _enter_state_esc_vt100()
{
_state = STATE_ESC_VT100;
}
void _enter_state_esc_osc()
{
_state = STATE_ESC_OSC;
}
bool _sgr(int const p)
@ -199,14 +238,11 @@ class Terminal::Decoder
bool _handle_esc_seq_1()
{
switch (_escape_stack[0].value) {
case '7': return (_screen.sc(), true);
case '8': return (_screen.rc(), true);
case 'E': return (_screen.nel(), true);
case 'H': return (_screen.hts(), true);
case 'M': return (_screen.cuu1(), true);
case '=': return true; /* follows 'smkx' */
case '>': return true; /* follows 'rmkx' */
case 'c': return true; /* prefixes 'rs2' */
case 'E': return (_screen.nel(), true);
case '>': return true; /* follows 'rmkx' */
case '=': return true; /* follows 'smkx' */
default: return false;
}
}
@ -216,32 +252,36 @@ class Terminal::Decoder
switch (_escape_stack[0].value) {
case '[':
switch (_escape_stack[1].value) {
case 'C': return (_screen.cuf(1), true);
case 'H': return (_screen.home(), true);
case 'A': return (_screen.cuu(), true);
case 'B': return (_screen.cud(), true);
case 'C': return (_screen.cuf(), true);
case 'D': return (_screen.cub(), true);
case 'G': return (_screen.cha(), true);
case 'H': return (_screen.cup(1,1), true);
case 'J': return (_screen.ed(), true);
case 'K': return (_screen.el(), true);
case 'L': return (_screen.il(1), true);
case 'M': return (_screen.dl(1), true);
case 'P': return (_screen.dch(1), true);
case 'Z': return (_screen.cbt(), true);
case 'm': return (_screen.sgr0(), true);
case 'L': return (_screen.il(), true);
case 'M': return (_screen.dl(), true);
case 'P': return (_screen.dch(), true);
case 'm': return _sgr(0);
case 'S': return (_screen.su(), true);
case 'T': return (_screen.sd(), true);
case 'c': return (_screen.da(), true);
case 'd': return (_screen.vpa(), true);
case 'n': return (_screen.vpb(), true);
case '@': return (_screen.ich(), true);
default: return false;
}
break;
case ')':
return (_escape_stack[1].value == 0) && (_screen.is2(), true);
default: return false;
}
return false;
}
bool _handle_esc_seq_3()
{
/*
* All three-element sequences have the form \E[<NUMBER><COMMAND>
*/
@ -253,25 +293,29 @@ class Terminal::Decoder
char const command = _escape_stack[2].value;
switch (command) {
case 'm': return _sgr(p1);
case 'D': return (_screen.cub(p1), true);
case 'A': return (_screen.cuu(p1), true);
case 'B': return (_screen.cud(p1), true);
case 'C': return (_screen.cuf(p1), true);
case 'D': return (_screen.cub(p1), true);
case 'd': return (_screen.vpa(p1), true);
case 'g': return (p1 == 3) && (_screen.tbc(), true);
case 'h': return ((p1 == 4) && (_screen.smir(), true))
|| ((p1 == 34) && (_screen.cnorm(), true));
case 'K': return ((p1 == 0) && (_screen.el(), true))
|| ((p1 == 1) && (_screen.el1(), true));
case 'l': return ((p1 == 4) && (_screen.rmir(), true))
|| ((p1 == 34) && (_screen.cvvis(), true));
case 'G': return (_screen.cha(p1), true);
case 'h': return (_screen.decsm(p1), true);
case 'l': return (_screen.decrm(p1), true);
case 'J': return (_screen.ed(p1), true);
case 'K': return (_screen.el(p1), true);
case 'L': return (_screen.il(p1), true);
case 'M': return (_screen.dl(p1), true);
case 'm': return _sgr(p1);
case 'n': return (_screen.vpb(p1), true);
case 'P': return (_screen.dch(p1), true);
case '@': return (_screen.ich(p1), true);
case 'C': return (_screen.cuf(p1), true);
default: return false;
case 'S': return (_screen.su(p1), true);
case 'T': return (_screen.sd(p1), true);
case 'X': return (_screen.ech(p1), true);
default: break;
}
return false;
}
bool _handle_esc_seq_4()
@ -289,29 +333,11 @@ class Terminal::Decoder
char const command = _escape_stack[3].value;
switch (command) {
case 'l':
if (p1 == 1) return (_screen.rmkx(), true);
if (p1 == 25) return (_screen.civis(), true);
if (p1 == 1000) return (_screen.rs2(), true);
if (p1 == 1049) return (_screen.rmcup(), true);
if (p1 == 2004) {
/* disable bracketed paste */
Genode::warning("Sequence '[?2004l' is not implemented");
return true;
}
return false;
case 'h':
if (p1 == 1) return (_screen.smkx(), true);
if (p1 == 25) return (_screen.cnorm(), true);
if (p1 == 1049) return (_screen.smcup(), true);
if (p1 == 2004) {
/* enable bracketed paste */
Genode::warning("Sequence '[?2004h' is not implemented");
return true;
}
return false;
default: return false;
case 'h': return (_screen.decsm(p1), true);
case 'l': return (_screen.decrm(p1), true);
default: break;
}
return false;
}
bool _handle_esc_seq_5()
@ -351,13 +377,34 @@ class Terminal::Decoder
}
}
bool _handle_esc_seq_6()
{
/*
* All five-element escape sequences have the form
* \E[?<NUMBER1>;<NUMBER2><COMMAND>
*/
if ((_escape_stack[0].value != '[')
|| (_escape_stack[1].value != '?')
|| (_escape_stack[2].type != Escape_stack::Entry::NUMBER)
|| (_escape_stack[3].value != ';')
|| (_escape_stack[4].type != Escape_stack::Entry::NUMBER))
return false;
int const p[2] = { _escape_stack[2].value,
_escape_stack[4].value };
switch (_escape_stack[5].value) {
case 'h': return (_screen.decsm(p[0], p[1]), true);
case 'l': return (_screen.decrm(p[0], p[1]), true);
default: return false;
}
}
bool _handle_esc_seq_7()
{
/*
* All six-element escape sequences have the form
* \E[<NUMBER1>;<NUMBER2>;<NUMBER3><COMMAND>
*/
if ((_escape_stack[0].value != '[')
|| (_escape_stack[1].type != Escape_stack::Entry::NUMBER)
|| (_escape_stack[2].value != ';')
@ -390,10 +437,20 @@ class Terminal::Decoder
return true;
}
bool _complete()
{
return (((_escape_stack.num_elem() == 1) && _handle_esc_seq_1())
|| ((_escape_stack.num_elem() == 2) && _handle_esc_seq_2())
|| ((_escape_stack.num_elem() == 3) && _handle_esc_seq_3())
|| ((_escape_stack.num_elem() == 4) && _handle_esc_seq_4())
|| ((_escape_stack.num_elem() == 5) && _handle_esc_seq_5())
|| ((_escape_stack.num_elem() == 6) && _handle_esc_seq_6())
|| ((_escape_stack.num_elem() == 7) && _handle_esc_seq_7()));
}
public:
Decoder(Character_screen &screen)
: _state(STATE_IDLE), _screen(screen), _number(0) { }
Decoder(Character_screen &screen) : _screen(screen) { }
void insert(unsigned char c)
{
@ -403,7 +460,7 @@ class Terminal::Decoder
enum { ESC_PREFIX = 0x1b };
if (c == ESC_PREFIX) {
_enter_state_esc_seq();
_enter_state_esc_csi();
break;
}
@ -414,8 +471,48 @@ class Terminal::Decoder
break;
case STATE_ESC_SEQ:
case STATE_ESC_CSI:
/* check that the second byte is in set C1 - ECMA-48 5.3 */
switch (c) {
case '7':
_screen.decsc();
_enter_state_idle();
break;
case '8':
_screen.decrc();
_enter_state_idle();
break;
case '(':
case ')':
_escape_stack.push(Escape_stack::Code_entry(c));
_state = STATE_ESC_SCS;
break;
case ']':
_enter_state_esc_osc();
break;
case 'M':
_screen.reverse_index();
_enter_state_idle();
break;
case '=':
case '>':
/* keypad mode, not useful enough to handle */
_enter_state_idle();
break;
default:
if (0x40 <= c && c <= 0x5f) {
_escape_stack.push(Escape_stack::Code_entry(c));
_enter_state_esc_ecma();
break;
}
Genode::error("unknown CSI ESC", Ascii(c));
_enter_state_idle();
}
break;
case STATE_ESC_ECMA:
case STATE_ESC_VT100:
/*
* We received the prefix character of an escape sequence,
* collect the escape-sequence elements until we detect the
@ -423,52 +520,50 @@ class Terminal::Decoder
*/
/* check for start of a number argument */
if (is_digit(c) && !_number)
{
_enter_state_esc_number();
_append_to_number(c);
break;
}
/* non-number character of escape sequence */
_escape_stack.push(Escape_stack::Code_entry(c));
break;
case STATE_ESC_NUMBER:
/*
* We got the first character belonging to a number
* argument of an escape sequence. Keep reading digits.
*/
if (is_digit(c)) {
_append_to_number(c);
break;
}
/*
* End of number is reached.
*/
else /* non-number character of escape sequence */
{
if (-1 < _number) {
_escape_stack.push(Escape_stack::Number_entry(_number));
_number = -1;
}
/* push the complete number to the escape stack */
_escape_stack.push(Escape_stack::Number_entry(_number));
_number = 0;
_escape_stack.push(Escape_stack::Code_entry(c));
/* push non-number character as commend entry */
/* check for Final Byte - ECMA-48 5.4 */
if (_state == STATE_ESC_ECMA && c > 0x3f && c < 0x7f) {
if (!_complete()) {
_escape_stack.discard(_state);
}
_enter_state_idle();
} else {
if (_complete())
_enter_state_idle();
}
}
break;
case STATE_ESC_SCS:
switch (_escape_stack[0].value) {
case '(': _screen.scs_g0(c); break;
case ')': _screen.scs_g1(c); break;
}
_enter_state_idle();
break;
case STATE_ESC_OSC:
enum { BELL = 07 };
_escape_stack.push(Escape_stack::Code_entry(c));
if (c == BELL) {
_escape_stack.discard(_state);
_enter_state_idle();
}
break;
}
/*
* Check for the completeness of an escape sequence.
*/
if (((_escape_stack.num_elem() == 1) && _handle_esc_seq_1())
|| ((_escape_stack.num_elem() == 2) && _handle_esc_seq_2())
|| ((_escape_stack.num_elem() == 3) && _handle_esc_seq_3())
|| ((_escape_stack.num_elem() == 4) && _handle_esc_seq_4())
|| ((_escape_stack.num_elem() == 5) && _handle_esc_seq_5())
|| ((_escape_stack.num_elem() == 7) && _handle_esc_seq_7()))
_enter_state_idle();
};
};

View File

@ -0,0 +1,140 @@
/*
* \brief Character printing utilities
* \author Emery Hemingway
* \date 2018-07-27
*/
/*
* Copyright (C) 2018 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
#ifndef _TERMINAL__PRINT_H_
#define _TERMINAL__PRINT_H_
#include <log_session/connection.h>
#include <base/output.h>
namespace Terminal {
class Log_buffer : public Genode::Output
{
private:
enum { BUF_SIZE = Genode::Log_session::MAX_STRING_LEN };
char _buf[BUF_SIZE];
unsigned _num_chars = 0;
public:
Log_buffer() { }
void flush_ok()
{
log(Genode::Cstring(_buf, _num_chars));
_num_chars = 0;
}
void flush_warning()
{
warning(Genode::Cstring(_buf, _num_chars));
_num_chars = 0;
}
void flush_error()
{
error(Genode::Cstring(_buf, _num_chars));
_num_chars = 0;
}
void out_char(char c) override
{
_buf[_num_chars++] = c;
if (_num_chars >= sizeof(_buf))
flush_ok();
}
template <typename... ARGS>
void print(ARGS &&... args) {
Output::out_args(*this, args...); }
};
struct Ascii
{
unsigned char const _c;
template <typename T>
explicit Ascii(T c) : _c(c) { }
void print(Genode::Output &out) const
{
switch (_c) {
case 000: out.out_string("NUL"); break;
case 001: out.out_string("SOH"); break;
case 002: out.out_string("STX"); break;
case 003: out.out_string("ETX"); break;
case 004: out.out_string("EOT"); break;
case 005: out.out_string("ENQ"); break;
case 006: out.out_string("ACK"); break;
case 007: out.out_string("BEL"); break;
case 010: out.out_string("BS"); break;
case 011: out.out_string("HT"); break;
case 012: out.out_string("LF"); break;
case 013: out.out_string("VT"); break;
case 014: out.out_string("FF"); break;
case 015: out.out_string("CR"); break;
case 016: out.out_string("SO"); break;
case 017: out.out_string("SI"); break;
case 020: out.out_string("DLE"); break;
case 021: out.out_string("DC1"); break;
case 022: out.out_string("DC2"); break;
case 023: out.out_string("DC3"); break;
case 024: out.out_string("DC4"); break;
case 025: out.out_string("NAK"); break;
case 026: out.out_string("SYN"); break;
case 027: out.out_string("ETB"); break;
case 030: out.out_string("CAN"); break;
case 031: out.out_string("EM"); break;
case 032: out.out_string("SUB"); break;
case 033: out.out_string("ESC"); break;
case 034: out.out_string("FS"); break;
case 035: out.out_string("GS"); break;
case 036: out.out_string("RS"); break;
case 037: out.out_string("US"); break;
case 040: out.out_string("SPACE"); break;
case 0177: out.out_string("DEL"); break;
default:
if (_c & 0x80)
Genode::Hex(_c).print(out);
else
out.out_char(_c);
break;
}
}
};
struct Ecma
{
unsigned char const _c;
template <typename T>
explicit Ecma(T c) : _c(c) { }
void print(Genode::Output &out) const
{
Ascii(_c).print(out);
out.out_char('(');
Genode::print(out, (_c)/160, (_c>>4)%10,
"/", (_c&0xf)/10, (_c&0xf)%10);
out.out_char(')');
}
};
}
#endif

View File

@ -84,6 +84,9 @@ struct Terminal::Position
return x >= 0 && x < boundary.width
&& y >= 0 && y < boundary.height;
}
void print(Genode::Output &out) const {
Genode::print(out, y, ",", x); }
};