2024-02-02 06:56:21

by Jiri Slaby

[permalink] [raw]
Subject: [PATCH 00/22] tty: vt: cleanup ESC sequences handling

This is a promised followup of "tty: vt: cleanup and documentation" [1].

The escape sequences parser is cleaned up, so that it is easier to
follow.

Most of the patches are easier to review with '-w -b' passed to git diff
(as the code is moved to separate functions with a different level of
indentation only).

[1] https://lore.kernel.org/all/[email protected]/T/

Jiri Slaby (SUSE) (22):
tty: vt: make rgb_from_256() slighly more comprehensible
tty: vt: define enums for CSI+h/l codes
tty: vt: rename set_mode() to csi_hl()
tty: vt: split DEC CSI+h/l handling into csi_DEC_hl()
tty: vt: remove unneeded assignment of EPecma to vc_priv
tty: vt: move CSI+n handling along to other ECMA CSIs
tty: vt: define an enum for CSI+] codes
tty: vt: rename setterm_command() to csi_RSB()
tty: vt: put cases on separate lines
tty: vt: accept u8 in do_con_trol() and vc_setGx()
tty: vt: extract ascii handling to handle_ascii()
tty: vt: separate ESesc state handling into handle_esc()
tty: vt: move CSI DEC handling to a separate function
tty: vt: move CSI ECMA handling to a separate function
tty: vt: name, reflow and document enum vc_ctl_state
tty: vt: simplify ansi_control_string()
tty: vt: handle CSI+[ inside preexisting switch-case
tty: vt: add new helper for reseting vc parameters
tty: vt: use switch+case in the ESnonstd case
tty: vt: use switch+case in the ESgetpars case
tty: vt: use ASCII enum constants in vt_console_print()
tty: vt: decrypt magic constants in vc_is_control()

drivers/tty/vt/vt.c | 876 ++++++++++++++++++++++++++------------------
1 file changed, 514 insertions(+), 362 deletions(-)

--
2.43.0



2024-02-02 06:56:51

by Jiri Slaby

[permalink] [raw]
Subject: [PATCH 02/22] tty: vt: define enums for CSI+h/l codes

Decrypt the constant values by proper enum names. This time in
set_mode().

Define two of them as DEC ('CSI ?') is about to be split away in the
next patches.

Signed-off-by: Jiri Slaby (SUSE) <[email protected]>
---
drivers/tty/vt/vt.c | 42 ++++++++++++++++++++++++++++++------------
1 file changed, 30 insertions(+), 12 deletions(-)

diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c
index 7d42f148559a..7b55d87248f8 100644
--- a/drivers/tty/vt/vt.c
+++ b/drivers/tty/vt/vt.c
@@ -1862,6 +1862,24 @@ int mouse_reporting(void)
return vc_cons[fg_console].d->vc_report_mouse;
}

+enum {
+ CSI_DEC_hl_CURSOR_KEYS = 1, /* CKM: cursor keys send ^[Ox/^[[x */
+ CSI_DEC_hl_132_COLUMNS = 3, /* COLM: 80/132 mode switch */
+ CSI_DEC_hl_REVERSE_VIDEO = 5, /* SCNM */
+ CSI_DEC_hl_ORIGIN_MODE = 6, /* OM: origin relative/absolute */
+ CSI_DEC_hl_AUTOWRAP = 7, /* AWM */
+ CSI_DEC_hl_AUTOREPEAT = 8, /* ARM */
+ CSI_DEC_hl_MOUSE_X10 = 9,
+ CSI_DEC_hl_SHOW_CURSOR = 25, /* TCEM */
+ CSI_DEC_hl_MOUSE_VT200 = 1000,
+};
+
+enum {
+ CSI_hl_DISPLAY_CTRL = 3, /* handle ansi control chars */
+ CSI_hl_INSERT = 4, /* IRM: insert/replace */
+ CSI_hl_AUTO_NL = 20, /* LNM: Enter == CrLf/Lf */
+};
+
/* console_lock is held */
static void set_mode(struct vc_data *vc, int on_off)
{
@@ -1870,20 +1888,20 @@ static void set_mode(struct vc_data *vc, int on_off)
for (i = 0; i <= vc->vc_npar; i++)
if (vc->vc_priv == EPdec) {
switch(vc->vc_par[i]) { /* DEC private modes set/reset */
- case 1: /* Cursor keys send ^[Ox/^[[x */
+ case CSI_DEC_hl_CURSOR_KEYS:
if (on_off)
set_kbd(vc, decckm);
else
clr_kbd(vc, decckm);
break;
- case 3: /* 80/132 mode switch unimplemented */
+ case CSI_DEC_hl_132_COLUMNS: /* unimplemented */
#if 0
vc_resize(deccolm ? 132 : 80, vc->vc_rows);
/* this alone does not suffice; some user mode
utility has to change the hardware regs */
#endif
break;
- case 5: /* Inverted screen on/off */
+ case CSI_DEC_hl_REVERSE_VIDEO:
if (vc->vc_decscnm != on_off) {
vc->vc_decscnm = on_off;
invert_screen(vc, 0,
@@ -1892,38 +1910,38 @@ static void set_mode(struct vc_data *vc, int on_off)
update_attr(vc);
}
break;
- case 6: /* Origin relative/absolute */
+ case CSI_DEC_hl_ORIGIN_MODE:
vc->vc_decom = on_off;
gotoxay(vc, 0, 0);
break;
- case 7: /* Autowrap on/off */
+ case CSI_DEC_hl_AUTOWRAP:
vc->vc_decawm = on_off;
break;
- case 8: /* Autorepeat on/off */
+ case CSI_DEC_hl_AUTOREPEAT:
if (on_off)
set_kbd(vc, decarm);
else
clr_kbd(vc, decarm);
break;
- case 9:
+ case CSI_DEC_hl_MOUSE_X10:
vc->vc_report_mouse = on_off ? 1 : 0;
break;
- case 25: /* Cursor on/off */
+ case CSI_DEC_hl_SHOW_CURSOR:
vc->vc_deccm = on_off;
break;
- case 1000:
+ case CSI_DEC_hl_MOUSE_VT200:
vc->vc_report_mouse = on_off ? 2 : 0;
break;
}
} else {
switch(vc->vc_par[i]) { /* ANSI modes set/reset */
- case 3: /* Monitor (display ctrls) */
+ case CSI_hl_DISPLAY_CTRL:
vc->vc_disp_ctrl = on_off;
break;
- case 4: /* Insert Mode on/off */
+ case CSI_hl_INSERT:
vc->vc_decim = on_off;
break;
- case 20: /* Lf, Enter == CrLf/Lf */
+ case CSI_hl_AUTO_NL:
if (on_off)
set_kbd(vc, lnm);
else
--
2.43.0


2024-02-02 06:57:04

by Jiri Slaby

[permalink] [raw]
Subject: [PATCH 03/22] tty: vt: rename set_mode() to csi_hl()

It's how the other CSI handling functions are named, so unify to that.

Signed-off-by: Jiri Slaby (SUSE) <[email protected]>
---
drivers/tty/vt/vt.c | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c
index 7b55d87248f8..ae333f49790a 100644
--- a/drivers/tty/vt/vt.c
+++ b/drivers/tty/vt/vt.c
@@ -1881,7 +1881,7 @@ enum {
};

/* console_lock is held */
-static void set_mode(struct vc_data *vc, int on_off)
+static void csi_hl(struct vc_data *vc, bool on_off)
{
int i;

@@ -2380,11 +2380,11 @@ static void do_con_trol(struct tty_struct *tty, struct vc_data *vc, int c)
switch(c) {
case 'h':
if (vc->vc_priv <= EPdec)
- set_mode(vc, 1);
+ csi_hl(vc, true);
return;
case 'l':
if (vc->vc_priv <= EPdec)
- set_mode(vc, 0);
+ csi_hl(vc, false);
return;
case 'c':
if (vc->vc_priv == EPdec) {
--
2.43.0


2024-02-02 06:57:20

by Jiri Slaby

[permalink] [raw]
Subject: [PATCH 04/22] tty: vt: split DEC CSI+h/l handling into csi_DEC_hl()

The DEC and ECMA handling of CSI+h/l is needlessly complicated. Split
these two, so that DEC is handled when the state is EPdec ('CSI ?' was
seen) and ECMA is handled in the EPecma state (no '?').

Signed-off-by: Jiri Slaby (SUSE) <[email protected]>
---
drivers/tty/vt/vt.c | 144 +++++++++++++++++++++++---------------------
1 file changed, 77 insertions(+), 67 deletions(-)

diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c
index ae333f49790a..d04dbafc0517 100644
--- a/drivers/tty/vt/vt.c
+++ b/drivers/tty/vt/vt.c
@@ -1874,6 +1874,59 @@ enum {
CSI_DEC_hl_MOUSE_VT200 = 1000,
};

+/* console_lock is held */
+static void csi_DEC_hl(struct vc_data *vc, bool on_off)
+{
+ unsigned int i;
+
+ for (i = 0; i <= vc->vc_npar; i++)
+ switch (vc->vc_par[i]) {
+ case CSI_DEC_hl_CURSOR_KEYS:
+ if (on_off)
+ set_kbd(vc, decckm);
+ else
+ clr_kbd(vc, decckm);
+ break;
+ case CSI_DEC_hl_132_COLUMNS: /* unimplemented */
+#if 0
+ vc_resize(deccolm ? 132 : 80, vc->vc_rows);
+ /* this alone does not suffice; some user mode
+ utility has to change the hardware regs */
+#endif
+ break;
+ case CSI_DEC_hl_REVERSE_VIDEO:
+ if (vc->vc_decscnm != on_off) {
+ vc->vc_decscnm = on_off;
+ invert_screen(vc, 0, vc->vc_screenbuf_size,
+ false);
+ update_attr(vc);
+ }
+ break;
+ case CSI_DEC_hl_ORIGIN_MODE:
+ vc->vc_decom = on_off;
+ gotoxay(vc, 0, 0);
+ break;
+ case CSI_DEC_hl_AUTOWRAP:
+ vc->vc_decawm = on_off;
+ break;
+ case CSI_DEC_hl_AUTOREPEAT:
+ if (on_off)
+ set_kbd(vc, decarm);
+ else
+ clr_kbd(vc, decarm);
+ break;
+ case CSI_DEC_hl_MOUSE_X10:
+ vc->vc_report_mouse = on_off ? 1 : 0;
+ break;
+ case CSI_DEC_hl_SHOW_CURSOR:
+ vc->vc_deccm = on_off;
+ break;
+ case CSI_DEC_hl_MOUSE_VT200:
+ vc->vc_report_mouse = on_off ? 2 : 0;
+ break;
+ }
+}
+
enum {
CSI_hl_DISPLAY_CTRL = 3, /* handle ansi control chars */
CSI_hl_INSERT = 4, /* IRM: insert/replace */
@@ -1883,71 +1936,22 @@ enum {
/* console_lock is held */
static void csi_hl(struct vc_data *vc, bool on_off)
{
- int i;
+ unsigned int i;

for (i = 0; i <= vc->vc_npar; i++)
- if (vc->vc_priv == EPdec) {
- switch(vc->vc_par[i]) { /* DEC private modes set/reset */
- case CSI_DEC_hl_CURSOR_KEYS:
- if (on_off)
- set_kbd(vc, decckm);
- else
- clr_kbd(vc, decckm);
- break;
- case CSI_DEC_hl_132_COLUMNS: /* unimplemented */
-#if 0
- vc_resize(deccolm ? 132 : 80, vc->vc_rows);
- /* this alone does not suffice; some user mode
- utility has to change the hardware regs */
-#endif
- break;
- case CSI_DEC_hl_REVERSE_VIDEO:
- if (vc->vc_decscnm != on_off) {
- vc->vc_decscnm = on_off;
- invert_screen(vc, 0,
- vc->vc_screenbuf_size,
- false);
- update_attr(vc);
- }
- break;
- case CSI_DEC_hl_ORIGIN_MODE:
- vc->vc_decom = on_off;
- gotoxay(vc, 0, 0);
- break;
- case CSI_DEC_hl_AUTOWRAP:
- vc->vc_decawm = on_off;
- break;
- case CSI_DEC_hl_AUTOREPEAT:
- if (on_off)
- set_kbd(vc, decarm);
- else
- clr_kbd(vc, decarm);
- break;
- case CSI_DEC_hl_MOUSE_X10:
- vc->vc_report_mouse = on_off ? 1 : 0;
- break;
- case CSI_DEC_hl_SHOW_CURSOR:
- vc->vc_deccm = on_off;
- break;
- case CSI_DEC_hl_MOUSE_VT200:
- vc->vc_report_mouse = on_off ? 2 : 0;
- break;
- }
- } else {
- switch(vc->vc_par[i]) { /* ANSI modes set/reset */
- case CSI_hl_DISPLAY_CTRL:
- vc->vc_disp_ctrl = on_off;
- break;
- case CSI_hl_INSERT:
- vc->vc_decim = on_off;
- break;
- case CSI_hl_AUTO_NL:
- if (on_off)
- set_kbd(vc, lnm);
- else
- clr_kbd(vc, lnm);
- break;
- }
+ switch (vc->vc_par[i]) { /* ANSI modes set/reset */
+ case CSI_hl_DISPLAY_CTRL:
+ vc->vc_disp_ctrl = on_off;
+ break;
+ case CSI_hl_INSERT:
+ vc->vc_decim = on_off;
+ break;
+ case CSI_hl_AUTO_NL:
+ if (on_off)
+ set_kbd(vc, lnm);
+ else
+ clr_kbd(vc, lnm);
+ break;
}
}

@@ -2379,12 +2383,12 @@ static void do_con_trol(struct tty_struct *tty, struct vc_data *vc, int c)
vc->vc_state = ESnormal;
switch(c) {
case 'h':
- if (vc->vc_priv <= EPdec)
- csi_hl(vc, true);
+ if (vc->vc_priv == EPdec)
+ csi_DEC_hl(vc, true);
return;
case 'l':
- if (vc->vc_priv <= EPdec)
- csi_hl(vc, false);
+ if (vc->vc_priv == EPdec)
+ csi_DEC_hl(vc, false);
return;
case 'c':
if (vc->vc_priv == EPdec) {
@@ -2494,6 +2498,12 @@ static void do_con_trol(struct tty_struct *tty, struct vc_data *vc, int c)
else if (vc->vc_par[0] == 3)
bitmap_zero(vc->vc_tab_stop, VC_TABSTOPS_COUNT);
return;
+ case 'h':
+ csi_hl(vc, true);
+ return;
+ case 'l':
+ csi_hl(vc, false);
+ return;
case 'm':
csi_m(vc);
return;
--
2.43.0


2024-02-02 06:57:32

by Jiri Slaby

[permalink] [raw]
Subject: [PATCH 05/22] tty: vt: remove unneeded assignment of EPecma to vc_priv

vc_data::vc_priv is _always_ assigned before the ESgetpars case is
entered (in ESsquare). Therefore, there is no need to reset it when
leaving the ESgetpars case. Note the state is set to ESnormal few lines
above, so ESgetpars is entered only by the next CSI.

Therefore, this obfuscation can be removed.

Signed-off-by: Jiri Slaby (SUSE) <[email protected]>
---
drivers/tty/vt/vt.c | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)

diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c
index d04dbafc0517..69ebce0878f2 100644
--- a/drivers/tty/vt/vt.c
+++ b/drivers/tty/vt/vt.c
@@ -2421,10 +2421,9 @@ static void do_con_trol(struct tty_struct *tty, struct vc_data *vc, int c)
}
return;
}
- if (vc->vc_priv != EPecma) {
- vc->vc_priv = EPecma;
+ if (vc->vc_priv != EPecma)
return;
- }
+
switch(c) {
case 'G': case '`':
if (vc->vc_par[0])
--
2.43.0


2024-02-02 06:58:04

by Jiri Slaby

[permalink] [raw]
Subject: [PATCH 07/22] tty: vt: define an enum for CSI+] codes

Decrypt the constant values by proper enum names. This time in
setterm_command() (to be renamed to csi_RSB() in the next patches).

Signed-off-by: Jiri Slaby (SUSE) <[email protected]>
---
drivers/tty/vt/vt.c | 36 +++++++++++++++++++++++++-----------
1 file changed, 25 insertions(+), 11 deletions(-)

diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c
index 04d109464994..9db545f305dc 100644
--- a/drivers/tty/vt/vt.c
+++ b/drivers/tty/vt/vt.c
@@ -1955,62 +1955,76 @@ static void csi_hl(struct vc_data *vc, bool on_off)
}
}

+enum CSI_right_square_bracket {
+ CSI_RSB_COLOR_FOR_UNDERLINE = 1,
+ CSI_RSB_COLOR_FOR_HALF_BRIGHT = 2,
+ CSI_RSB_MAKE_CUR_COLOR_DEFAULT = 8,
+ CSI_RSB_BLANKING_INTERVAL = 9,
+ CSI_RSB_BELL_FREQUENCY = 10,
+ CSI_RSB_BELL_DURATION = 11,
+ CSI_RSB_BRING_CONSOLE_TO_FRONT = 12,
+ CSI_RSB_UNBLANK = 13,
+ CSI_RSB_VESA_OFF_INTERVAL = 14,
+ CSI_RSB_BRING_PREV_CONSOLE_TO_FRONT = 15,
+ CSI_RSB_CURSOR_BLINK_INTERVAL = 16,
+};
+
/* console_lock is held */
static void setterm_command(struct vc_data *vc)
{
switch (vc->vc_par[0]) {
- case 1: /* set color for underline mode */
+ case CSI_RSB_COLOR_FOR_UNDERLINE:
if (vc->vc_can_do_color && vc->vc_par[1] < 16) {
vc->vc_ulcolor = color_table[vc->vc_par[1]];
if (vc->state.underline)
update_attr(vc);
}
break;
- case 2: /* set color for half intensity mode */
+ case CSI_RSB_COLOR_FOR_HALF_BRIGHT:
if (vc->vc_can_do_color && vc->vc_par[1] < 16) {
vc->vc_halfcolor = color_table[vc->vc_par[1]];
if (vc->state.intensity == VCI_HALF_BRIGHT)
update_attr(vc);
}
break;
- case 8: /* store colors as defaults */
+ case CSI_RSB_MAKE_CUR_COLOR_DEFAULT:
vc->vc_def_color = vc->vc_attr;
if (vc->vc_hi_font_mask == 0x100)
vc->vc_def_color >>= 1;
default_attr(vc);
update_attr(vc);
break;
- case 9: /* set blanking interval */
+ case CSI_RSB_BLANKING_INTERVAL:
blankinterval = min(vc->vc_par[1], 60U) * 60;
poke_blanked_console();
break;
- case 10: /* set bell frequency in Hz */
+ case CSI_RSB_BELL_FREQUENCY:
if (vc->vc_npar >= 1)
vc->vc_bell_pitch = vc->vc_par[1];
else
vc->vc_bell_pitch = DEFAULT_BELL_PITCH;
break;
- case 11: /* set bell duration in msec */
+ case CSI_RSB_BELL_DURATION:
if (vc->vc_npar >= 1)
vc->vc_bell_duration = (vc->vc_par[1] < 2000) ?
msecs_to_jiffies(vc->vc_par[1]) : 0;
else
vc->vc_bell_duration = DEFAULT_BELL_DURATION;
break;
- case 12: /* bring specified console to the front */
+ case CSI_RSB_BRING_CONSOLE_TO_FRONT:
if (vc->vc_par[1] >= 1 && vc_cons_allocated(vc->vc_par[1] - 1))
set_console(vc->vc_par[1] - 1);
break;
- case 13: /* unblank the screen */
+ case CSI_RSB_UNBLANK:
poke_blanked_console();
break;
- case 14: /* set vesa powerdown interval */
+ case CSI_RSB_VESA_OFF_INTERVAL:
vesa_off_interval = min(vc->vc_par[1], 60U) * 60 * HZ;
break;
- case 15: /* activate the previous console */
+ case CSI_RSB_BRING_PREV_CONSOLE_TO_FRONT:
set_console(last_console);
break;
- case 16: /* set cursor blink duration in msec */
+ case CSI_RSB_CURSOR_BLINK_INTERVAL:
if (vc->vc_npar >= 1 && vc->vc_par[1] >= 50 &&
vc->vc_par[1] <= USHRT_MAX)
vc->vc_cur_blink_ms = vc->vc_par[1];
--
2.43.0


2024-02-02 06:58:23

by Jiri Slaby

[permalink] [raw]
Subject: [PATCH 08/22] tty: vt: rename setterm_command() to csi_RSB()

It follows naming of other similar functions. RSB stands here for Right
Square Bracket as (obviously) ']' cannot be in the function name.

Signed-off-by: Jiri Slaby (SUSE) <[email protected]>
---
drivers/tty/vt/vt.c | 14 ++++++++++----
1 file changed, 10 insertions(+), 4 deletions(-)

diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c
index 9db545f305dc..c072007807e1 100644
--- a/drivers/tty/vt/vt.c
+++ b/drivers/tty/vt/vt.c
@@ -1969,8 +1969,14 @@ enum CSI_right_square_bracket {
CSI_RSB_CURSOR_BLINK_INTERVAL = 16,
};

-/* console_lock is held */
-static void setterm_command(struct vc_data *vc)
+/*
+ * csi_RSB - csi+] (Right Square Bracket) handler
+ *
+ * These are linux console private sequences.
+ *
+ * console_lock is held
+ */
+static void csi_RSB(struct vc_data *vc)
{
switch (vc->vc_par[0]) {
case CSI_RSB_COLOR_FOR_UNDERLINE:
@@ -2549,8 +2555,8 @@ static void do_con_trol(struct tty_struct *tty, struct vc_data *vc, int c)
case '@':
csi_at(vc, vc->vc_par[0]);
return;
- case ']': /* setterm functions */
- setterm_command(vc);
+ case ']':
+ csi_RSB(vc);
return;
}
return;
--
2.43.0


2024-02-02 06:59:23

by Jiri Slaby

[permalink] [raw]
Subject: [PATCH 11/22] tty: vt: extract ascii handling to handle_ascii()

To make the do_con_trol() a bit more understandable, extract the ASCII
handling (the switch-case) to a separate function.

Other nested switch-cases will follow in the next patches.

Signed-off-by: Jiri Slaby (SUSE) <[email protected]>
---
drivers/tty/vt/vt.c | 58 +++++++++++++++++++++++++++++----------------
1 file changed, 37 insertions(+), 21 deletions(-)

diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c
index 451a852ed234..7cda1a958c5e 100644
--- a/drivers/tty/vt/vt.c
+++ b/drivers/tty/vt/vt.c
@@ -2197,28 +2197,26 @@ enum {
ASCII_EXT_CSI = 128 + ASCII_ESCAPE,
};

-/* console_lock is held */
-static void do_con_trol(struct tty_struct *tty, struct vc_data *vc, u8 c)
+/*
+ * Handle ascii characters in control sequences and change states accordingly.
+ * E.g. ESC sets the state of vc to ESesc.
+ *
+ * Returns: true if @c handled.
+ */
+static bool handle_ascii(struct tty_struct *tty, struct vc_data *vc, u8 c)
{
- /*
- * Control characters can be used in the _middle_
- * of an escape sequence, aside from ANSI control strings.
- */
- if (ansi_control_string(vc->vc_state) && c >= ASCII_IGNORE_FIRST &&
- c <= ASCII_IGNORE_LAST)
- return;
switch (c) {
case ASCII_NULL:
- return;
+ return true;
case ASCII_BELL:
if (ansi_control_string(vc->vc_state))
vc->vc_state = ESnormal;
else if (vc->vc_bell_duration)
kd_mksound(vc->vc_bell_pitch, vc->vc_bell_duration);
- return;
+ return true;
case ASCII_BACKSPACE:
bs(vc);
- return;
+ return true;
case ASCII_HTAB:
vc->vc_pos -= (vc->state.x << 1);

@@ -2230,41 +2228,59 @@ static void do_con_trol(struct tty_struct *tty, struct vc_data *vc, u8 c)

vc->vc_pos += (vc->state.x << 1);
notify_write(vc, '\t');
- return;
+ return true;
case ASCII_LINEFEED:
case ASCII_VTAB:
case ASCII_FORMFEED:
lf(vc);
if (!is_kbd(vc, lnm))
- return;
+ return true;
fallthrough;
case ASCII_CAR_RET:
cr(vc);
- return;
+ return true;
case ASCII_SHIFTOUT:
vc->state.charset = 1;
vc->vc_translate = set_translate(vc->state.Gx_charset[1], vc);
vc->vc_disp_ctrl = 1;
- return;
+ return true;
case ASCII_SHIFTIN:
vc->state.charset = 0;
vc->vc_translate = set_translate(vc->state.Gx_charset[0], vc);
vc->vc_disp_ctrl = 0;
- return;
+ return true;
case ASCII_CANCEL:
case ASCII_SUBSTITUTE:
vc->vc_state = ESnormal;
- return;
+ return true;
case ASCII_ESCAPE:
vc->vc_state = ESesc;
- return;
+ return true;
case ASCII_DEL:
del(vc);
- return;
+ return true;
case ASCII_EXT_CSI:
vc->vc_state = ESsquare;
- return;
+ return true;
}
+
+ return false;
+}
+
+/* console_lock is held */
+static void do_con_trol(struct tty_struct *tty, struct vc_data *vc, u8 c)
+{
+ /*
+ * Control characters can be used in the _middle_
+ * of an escape sequence, aside from ANSI control strings.
+ */
+ if (ansi_control_string(vc->vc_state) && c >= ASCII_IGNORE_FIRST &&
+ c <= ASCII_IGNORE_LAST)
+ return;
+
+ if (handle_ascii(tty, vc, c))
+ return;
+
switch(vc->vc_state) {
case ESesc:
vc->vc_state = ESnormal;
--
2.43.0


2024-02-02 07:00:10

by Jiri Slaby

[permalink] [raw]
Subject: [PATCH 14/22] tty: vt: move CSI ECMA handling to a separate function

Similar to previous moves, move also "CSI ..." (i.e. vc_priv == EPecma)
handling to a separate function.

This is the last large move of code out of do_con_trol(). And despite it
is still 151 lines of code (down from 407!), it is now quite easy to
folllow the transitions of the state machine in there. ESnonstd and
ESpalette handling still can be moved away, but it won't improve that
much.

Signed-off-by: Jiri Slaby (SUSE) <[email protected]>
---
drivers/tty/vt/vt.c | 269 +++++++++++++++++++++++---------------------
1 file changed, 139 insertions(+), 130 deletions(-)

diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c
index 7cdd0eb1e423..1c832d04c0dc 100644
--- a/drivers/tty/vt/vt.c
+++ b/drivers/tty/vt/vt.c
@@ -2375,6 +2375,143 @@ static void csi_DEC(struct tty_struct *tty, struct vc_data *vc, u8 c)
}
}

+/*
+ * Handle Control Sequence Introducer control characters. That is
+ * "ESC [ parameters char". Parameters are in @vc->vc_par and the char is in
+ * @c here.
+ */
+static void csi_ECMA(struct tty_struct *tty, struct vc_data *vc, u8 c)
+{
+ switch (c) {
+ case 'G':
+ case '`':
+ if (vc->vc_par[0])
+ vc->vc_par[0]--;
+ gotoxy(vc, vc->vc_par[0], vc->state.y);
+ break;
+ case 'A':
+ if (!vc->vc_par[0])
+ vc->vc_par[0]++;
+ gotoxy(vc, vc->state.x, vc->state.y - vc->vc_par[0]);
+ break;
+ case 'B':
+ case 'e':
+ if (!vc->vc_par[0])
+ vc->vc_par[0]++;
+ gotoxy(vc, vc->state.x, vc->state.y + vc->vc_par[0]);
+ break;
+ case 'C':
+ case 'a':
+ if (!vc->vc_par[0])
+ vc->vc_par[0]++;
+ gotoxy(vc, vc->state.x + vc->vc_par[0], vc->state.y);
+ break;
+ case 'D':
+ if (!vc->vc_par[0])
+ vc->vc_par[0]++;
+ gotoxy(vc, vc->state.x - vc->vc_par[0], vc->state.y);
+ break;
+ case 'E':
+ if (!vc->vc_par[0])
+ vc->vc_par[0]++;
+ gotoxy(vc, 0, vc->state.y + vc->vc_par[0]);
+ break;
+ case 'F':
+ if (!vc->vc_par[0])
+ vc->vc_par[0]++;
+ gotoxy(vc, 0, vc->state.y - vc->vc_par[0]);
+ break;
+ case 'd':
+ if (vc->vc_par[0])
+ vc->vc_par[0]--;
+ gotoxay(vc, vc->state.x ,vc->vc_par[0]);
+ break;
+ case 'H':
+ case 'f':
+ if (vc->vc_par[0])
+ vc->vc_par[0]--;
+ if (vc->vc_par[1])
+ vc->vc_par[1]--;
+ gotoxay(vc, vc->vc_par[1], vc->vc_par[0]);
+ break;
+ case 'J':
+ csi_J(vc, vc->vc_par[0]);
+ break;
+ case 'K':
+ csi_K(vc);
+ break;
+ case 'L':
+ csi_L(vc);
+ break;
+ case 'M':
+ csi_M(vc);
+ break;
+ case 'P':
+ csi_P(vc);
+ break;
+ case 'c':
+ if (!vc->vc_par[0])
+ respond_ID(tty);
+ break;
+ case 'g':
+ if (!vc->vc_par[0] && vc->state.x < VC_TABSTOPS_COUNT)
+ set_bit(vc->state.x, vc->vc_tab_stop);
+ else if (vc->vc_par[0] == 3)
+ bitmap_zero(vc->vc_tab_stop, VC_TABSTOPS_COUNT);
+ break;
+ case 'h':
+ csi_hl(vc, true);
+ break;
+ case 'l':
+ csi_hl(vc, false);
+ break;
+ case 'm':
+ csi_m(vc);
+ break;
+ case 'n':
+ if (vc->vc_par[0] == 5)
+ status_report(tty);
+ else if (vc->vc_par[0] == 6)
+ cursor_report(vc, tty);
+ break;
+ case 'q': /* DECLL - but only 3 leds */
+ /* map 0,1,2,3 to 0,1,2,4 */
+ if (vc->vc_par[0] < 4)
+ vt_set_led_state(vc->vc_num,
+ (vc->vc_par[0] < 3) ? vc->vc_par[0] : 4);
+ break;
+ case 'r':
+ if (!vc->vc_par[0])
+ vc->vc_par[0]++;
+ if (!vc->vc_par[1])
+ vc->vc_par[1] = vc->vc_rows;
+ /* Minimum allowed region is 2 lines */
+ if (vc->vc_par[0] < vc->vc_par[1] &&
+ vc->vc_par[1] <= vc->vc_rows) {
+ vc->vc_top = vc->vc_par[0] - 1;
+ vc->vc_bottom = vc->vc_par[1];
+ gotoxay(vc, 0, 0);
+ }
+ break;
+ case 's':
+ save_cur(vc);
+ break;
+ case 'u':
+ restore_cur(vc);
+ break;
+ case 'X':
+ csi_X(vc);
+ break;
+ case '@':
+ csi_at(vc, vc->vc_par[0]);
+ break;
+ case ']':
+ csi_RSB(vc);
+ break;
+ }
+
+}
+
/* console_lock is held */
static void do_con_trol(struct tty_struct *tty, struct vc_data *vc, u8 c)
{
@@ -2470,139 +2607,11 @@ static void do_con_trol(struct tty_struct *tty, struct vc_data *vc, u8 c)
csi_DEC(tty, vc, c);
return;
case EPecma:
- break;
- default:
- return;
- }
-
- switch(c) {
- case 'G':
- case '`':
- if (vc->vc_par[0])
- vc->vc_par[0]--;
- gotoxy(vc, vc->vc_par[0], vc->state.y);
- return;
- case 'A':
- if (!vc->vc_par[0])
- vc->vc_par[0]++;
- gotoxy(vc, vc->state.x, vc->state.y - vc->vc_par[0]);
- return;
- case 'B':
- case 'e':
- if (!vc->vc_par[0])
- vc->vc_par[0]++;
- gotoxy(vc, vc->state.x, vc->state.y + vc->vc_par[0]);
- return;
- case 'C':
- case 'a':
- if (!vc->vc_par[0])
- vc->vc_par[0]++;
- gotoxy(vc, vc->state.x + vc->vc_par[0], vc->state.y);
- return;
- case 'D':
- if (!vc->vc_par[0])
- vc->vc_par[0]++;
- gotoxy(vc, vc->state.x - vc->vc_par[0], vc->state.y);
- return;
- case 'E':
- if (!vc->vc_par[0])
- vc->vc_par[0]++;
- gotoxy(vc, 0, vc->state.y + vc->vc_par[0]);
+ csi_ECMA(tty, vc, c);
return;
- case 'F':
- if (!vc->vc_par[0])
- vc->vc_par[0]++;
- gotoxy(vc, 0, vc->state.y - vc->vc_par[0]);
- return;
- case 'd':
- if (vc->vc_par[0])
- vc->vc_par[0]--;
- gotoxay(vc, vc->state.x ,vc->vc_par[0]);
- return;
- case 'H':
- case 'f':
- if (vc->vc_par[0])
- vc->vc_par[0]--;
- if (vc->vc_par[1])
- vc->vc_par[1]--;
- gotoxay(vc, vc->vc_par[1], vc->vc_par[0]);
- return;
- case 'J':
- csi_J(vc, vc->vc_par[0]);
- return;
- case 'K':
- csi_K(vc);
- return;
- case 'L':
- csi_L(vc);
- return;
- case 'M':
- csi_M(vc);
- return;
- case 'P':
- csi_P(vc);
- return;
- case 'c':
- if (!vc->vc_par[0])
- respond_ID(tty);
- return;
- case 'g':
- if (!vc->vc_par[0] && vc->state.x < VC_TABSTOPS_COUNT)
- set_bit(vc->state.x, vc->vc_tab_stop);
- else if (vc->vc_par[0] == 3)
- bitmap_zero(vc->vc_tab_stop, VC_TABSTOPS_COUNT);
- return;
- case 'h':
- csi_hl(vc, true);
- return;
- case 'l':
- csi_hl(vc, false);
- return;
- case 'm':
- csi_m(vc);
- return;
- case 'n':
- if (vc->vc_par[0] == 5)
- status_report(tty);
- else if (vc->vc_par[0] == 6)
- cursor_report(vc, tty);
- return;
- case 'q': /* DECLL - but only 3 leds */
- /* map 0,1,2,3 to 0,1,2,4 */
- if (vc->vc_par[0] < 4)
- vt_set_led_state(vc->vc_num,
- (vc->vc_par[0] < 3) ? vc->vc_par[0] : 4);
- return;
- case 'r':
- if (!vc->vc_par[0])
- vc->vc_par[0]++;
- if (!vc->vc_par[1])
- vc->vc_par[1] = vc->vc_rows;
- /* Minimum allowed region is 2 lines */
- if (vc->vc_par[0] < vc->vc_par[1] &&
- vc->vc_par[1] <= vc->vc_rows) {
- vc->vc_top = vc->vc_par[0] - 1;
- vc->vc_bottom = vc->vc_par[1];
- gotoxay(vc, 0, 0);
- }
- return;
- case 's':
- save_cur(vc);
- return;
- case 'u':
- restore_cur(vc);
- return;
- case 'X':
- csi_X(vc);
- return;
- case '@':
- csi_at(vc, vc->vc_par[0]);
- return;
- case ']':
- csi_RSB(vc);
+ default:
return;
}
- return;
case EScsiignore:
if (c >= ASCII_CSI_IGNORE_FIRST && c <= ASCII_CSI_IGNORE_LAST)
return;
--
2.43.0


2024-02-02 07:00:21

by Jiri Slaby

[permalink] [raw]
Subject: [PATCH 15/22] tty: vt: name, reflow and document enum vc_ctl_state

The enum for states is currently compact and undocumented. Put each
definition on a separate line and document them all using kernel-doc.

Document the same on the use sites.

Signed-off-by: Jiri Slaby (SUSE) <[email protected]>
---
drivers/tty/vt/vt.c | 72 ++++++++++++++++++++++++++++++++++-----------
1 file changed, 55 insertions(+), 17 deletions(-)

diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c
index 1c832d04c0dc..6d08290fdfdf 100644
--- a/drivers/tty/vt/vt.c
+++ b/drivers/tty/vt/vt.c
@@ -2091,9 +2091,44 @@ static void restore_cur(struct vc_data *vc)
vc->vc_need_wrap = 0;
}

-enum { ESnormal, ESesc, ESsquare, ESgetpars, ESfunckey,
- EShash, ESsetG0, ESsetG1, ESpercent, EScsiignore, ESnonstd,
- ESpalette, ESosc, ESapc, ESpm, ESdcs };
+/**
+ * enum vc_ctl_state - control characters state of a vt
+ *
+ * @ESnormal: initial state, no control characters parsed
+ * @ESesc: ESC parsed
+ * @ESsquare: CSI parsed -- modifiers/parameters/ctrl chars expected
+ * @ESgetpars: CSI parsed -- parameters/ctrl chars expected
+ * @ESfunckey: CSI [ parsed
+ * @EShash: ESC # parsed
+ * @ESsetG0: ESC ( parsed
+ * @ESsetG1: ESC ) parsed
+ * @ESpercent: ESC % parsed
+ * @EScsiignore: CSI [0x20-0x3f] parsed
+ * @ESnonstd: OSC parsed
+ * @ESpalette: OSC P parsed
+ * @ESosc: OSC [0-9] parsed
+ * @ESapc: ESC _ parsed
+ * @ESpm: ESC ^ parsed
+ * @ESdcs: ESC P parsed
+ */
+enum vc_ctl_state {
+ ESnormal,
+ ESesc,
+ ESsquare,
+ ESgetpars,
+ ESfunckey,
+ EShash,
+ ESsetG0,
+ ESsetG1,
+ ESpercent,
+ EScsiignore,
+ ESnonstd,
+ ESpalette,
+ ESosc,
+ ESapc,
+ ESpm,
+ ESdcs,
+};

/* console_lock is held (except via vc_init()) */
static void reset_terminal(struct vc_data *vc, int do_clear)
@@ -2527,10 +2562,10 @@ static void do_con_trol(struct tty_struct *tty, struct vc_data *vc, u8 c)
return;

switch(vc->vc_state) {
- case ESesc:
+ case ESesc: /* ESC */
handle_esc(tty, vc, c);
return;
- case ESnonstd:
+ case ESnonstd: /* ESC ] aka OSC */
if (c=='P') { /* palette escape sequence */
for (vc->vc_npar = 0; vc->vc_npar < NPAR; vc->vc_npar++)
vc->vc_par[vc->vc_npar] = 0;
@@ -2545,7 +2580,7 @@ static void do_con_trol(struct tty_struct *tty, struct vc_data *vc, u8 c)
else
vc->vc_state = ESnormal;
return;
- case ESpalette:
+ case ESpalette: /* ESC ] P aka OSC P */
if (isxdigit(c)) {
vc->vc_par[vc->vc_npar++] = hex_to_bin(c);
if (vc->vc_npar == 7) {
@@ -2562,7 +2597,7 @@ static void do_con_trol(struct tty_struct *tty, struct vc_data *vc, u8 c)
} else
vc->vc_state = ESnormal;
return;
- case ESsquare:
+ case ESsquare: /* ESC [ aka CSI, parameters or modifiers expected */
for (vc->vc_npar = 0; vc->vc_npar < NPAR; vc->vc_npar++)
vc->vc_par[vc->vc_npar] = 0;
vc->vc_npar = 0;
@@ -2587,7 +2622,7 @@ static void do_con_trol(struct tty_struct *tty, struct vc_data *vc, u8 c)
}
vc->vc_priv = EPecma;
fallthrough;
- case ESgetpars:
+ case ESgetpars: /* ESC [ aka CSI, parameters expected */
if (c == ';' && vc->vc_npar < NPAR - 1) {
vc->vc_npar++;
return;
@@ -2600,6 +2635,9 @@ static void do_con_trol(struct tty_struct *tty, struct vc_data *vc, u8 c)
vc->vc_state = EScsiignore;
return;
}
+
+ /* parameters done, handle the control char @c */
+
vc->vc_state = ESnormal;

switch (vc->vc_priv) {
@@ -2617,7 +2655,7 @@ static void do_con_trol(struct tty_struct *tty, struct vc_data *vc, u8 c)
return;
vc->vc_state = ESnormal;
return;
- case ESpercent:
+ case ESpercent: /* ESC % */
vc->vc_state = ESnormal;
switch (c) {
case '@': /* defined in ISO 2022 */
@@ -2629,10 +2667,10 @@ static void do_con_trol(struct tty_struct *tty, struct vc_data *vc, u8 c)
return;
}
return;
- case ESfunckey:
+ case ESfunckey: /* ESC [ [ aka CSI [ */
vc->vc_state = ESnormal;
return;
- case EShash:
+ case EShash: /* ESC # */
vc->vc_state = ESnormal;
if (c == '8') {
/* DEC screen alignment test. kludge :-) */
@@ -2644,21 +2682,21 @@ static void do_con_trol(struct tty_struct *tty, struct vc_data *vc, u8 c)
do_update_region(vc, vc->vc_origin, vc->vc_screenbuf_size / 2);
}
return;
- case ESsetG0:
+ case ESsetG0: /* ESC ( */
vc_setGx(vc, 0, c);
vc->vc_state = ESnormal;
return;
- case ESsetG1:
+ case ESsetG1: /* ESC ) */
vc_setGx(vc, 1, c);
vc->vc_state = ESnormal;
return;
- case ESapc:
+ case ESapc: /* ESC _ */
return;
- case ESosc:
+ case ESosc: /* ESC ] [0-9] aka OSC [0-9] */
return;
- case ESpm:
+ case ESpm: /* ESC ^ */
return;
- case ESdcs:
+ case ESdcs: /* ESC P */
return;
default:
vc->vc_state = ESnormal;
--
2.43.0


2024-02-02 07:00:37

by Jiri Slaby

[permalink] [raw]
Subject: [PATCH 16/22] tty: vt: simplify ansi_control_string()

Given all the ANSI control states are sequential in the vc_ctl_state
enum, we can define first/last constants and use them in
ansi_control_string(). It makes the test simple and allows for removal
of the 'if' (which was unnecessary at all -- the 'return' should have
returned the 'if' content directly anyway).

And remove the useless comment -- it's clear from the function
prototype.

Signed-off-by: Jiri Slaby (SUSE) <[email protected]>
---
drivers/tty/vt/vt.c | 11 ++++++-----
1 file changed, 6 insertions(+), 5 deletions(-)

diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c
index 6d08290fdfdf..e1cbe966bc84 100644
--- a/drivers/tty/vt/vt.c
+++ b/drivers/tty/vt/vt.c
@@ -2107,9 +2107,11 @@ static void restore_cur(struct vc_data *vc)
* @ESnonstd: OSC parsed
* @ESpalette: OSC P parsed
* @ESosc: OSC [0-9] parsed
+ * @ESANSI_first: first state for ignoring ansi control sequences
* @ESapc: ESC _ parsed
* @ESpm: ESC ^ parsed
* @ESdcs: ESC P parsed
+ * @ESANSI_last: last state for ignoring ansi control sequences
*/
enum vc_ctl_state {
ESnormal,
@@ -2125,9 +2127,11 @@ enum vc_ctl_state {
ESnonstd,
ESpalette,
ESosc,
+ ESANSI_first = ESosc,
ESapc,
ESpm,
ESdcs,
+ ESANSI_last = ESdcs,
};

/* console_lock is held (except via vc_init()) */
@@ -2202,12 +2206,9 @@ static void vc_setGx(struct vc_data *vc, unsigned int which, u8 c)
vc->vc_translate = set_translate(*charset, vc);
}

-/* is this state an ANSI control string? */
-static bool ansi_control_string(unsigned int state)
+static bool ansi_control_string(enum vc_ctl_state state)
{
- if (state == ESosc || state == ESapc || state == ESpm || state == ESdcs)
- return true;
- return false;
+ return state >= ESANSI_first && state <= ESANSI_last;
}

enum {
--
2.43.0


2024-02-02 07:00:57

by Jiri Slaby

[permalink] [raw]
Subject: [PATCH 17/22] tty: vt: handle CSI+[ inside preexisting switch-case

In do_con_trol()'s ESsquare case, there is already a switch (c). It is
preceded by an 'if (c == '[')'. Despite this 'if' handles a state
transition and not a modifier, move it as one of the switch cases. This
makes all the 'c' decision making more obvious there.

Signed-off-by: Jiri Slaby (SUSE) <[email protected]>
---
drivers/tty/vt/vt.c | 7 +++----
1 file changed, 3 insertions(+), 4 deletions(-)

diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c
index e1cbe966bc84..4d020a9967a2 100644
--- a/drivers/tty/vt/vt.c
+++ b/drivers/tty/vt/vt.c
@@ -2603,11 +2603,10 @@ static void do_con_trol(struct tty_struct *tty, struct vc_data *vc, u8 c)
vc->vc_par[vc->vc_npar] = 0;
vc->vc_npar = 0;
vc->vc_state = ESgetpars;
- if (c == '[') { /* Function key */
- vc->vc_state=ESfunckey;
- return;
- }
switch (c) {
+ case '[': /* Function key */
+ vc->vc_state = ESfunckey;
+ return;
case '?':
vc->vc_priv = EPdec;
return;
--
2.43.0


2024-02-02 07:01:09

by Jiri Slaby

[permalink] [raw]
Subject: [PATCH 18/22] tty: vt: add new helper for reseting vc parameters

The code to reset the vc parameter parsing is repeated on two locations.
Create a helper vc_reset_params() and use it on both of them.

And instead of a 'for' loop to clear the array of parameters, use
simpler memset().

Signed-off-by: Jiri Slaby (SUSE) <[email protected]>
---
drivers/tty/vt/vt.c | 15 +++++++++------
1 file changed, 9 insertions(+), 6 deletions(-)

diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c
index 4d020a9967a2..b0f691d79bf2 100644
--- a/drivers/tty/vt/vt.c
+++ b/drivers/tty/vt/vt.c
@@ -2548,6 +2548,12 @@ static void csi_ECMA(struct tty_struct *tty, struct vc_data *vc, u8 c)

}

+static void vc_reset_params(struct vc_data *vc)
+{
+ memset(vc->vc_par, 0, sizeof(vc->vc_par));
+ vc->vc_npar = 0;
+}
+
/* console_lock is held */
static void do_con_trol(struct tty_struct *tty, struct vc_data *vc, u8 c)
{
@@ -2568,9 +2574,7 @@ static void do_con_trol(struct tty_struct *tty, struct vc_data *vc, u8 c)
return;
case ESnonstd: /* ESC ] aka OSC */
if (c=='P') { /* palette escape sequence */
- for (vc->vc_npar = 0; vc->vc_npar < NPAR; vc->vc_npar++)
- vc->vc_par[vc->vc_npar] = 0;
- vc->vc_npar = 0;
+ vc_reset_params(vc);
vc->vc_state = ESpalette;
return;
} else if (c=='R') { /* reset palette */
@@ -2599,9 +2603,8 @@ static void do_con_trol(struct tty_struct *tty, struct vc_data *vc, u8 c)
vc->vc_state = ESnormal;
return;
case ESsquare: /* ESC [ aka CSI, parameters or modifiers expected */
- for (vc->vc_npar = 0; vc->vc_npar < NPAR; vc->vc_npar++)
- vc->vc_par[vc->vc_npar] = 0;
- vc->vc_npar = 0;
+ vc_reset_params(vc);
+
vc->vc_state = ESgetpars;
switch (c) {
case '[': /* Function key */
--
2.43.0


2024-02-02 07:01:21

by Jiri Slaby

[permalink] [raw]
Subject: [PATCH 19/22] tty: vt: use switch+case in the ESnonstd case

To be uniform in the 'c' handling, use switch-case (with ranges) even in
the ESnonstd case in do_con_trol().

Signed-off-by: Jiri Slaby (SUSE) <[email protected]>
---
drivers/tty/vt/vt.c | 14 ++++++++------
1 file changed, 8 insertions(+), 6 deletions(-)

diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c
index b0f691d79bf2..b5fc3b896e26 100644
--- a/drivers/tty/vt/vt.c
+++ b/drivers/tty/vt/vt.c
@@ -2573,17 +2573,19 @@ static void do_con_trol(struct tty_struct *tty, struct vc_data *vc, u8 c)
handle_esc(tty, vc, c);
return;
case ESnonstd: /* ESC ] aka OSC */
- if (c=='P') { /* palette escape sequence */
+ switch (c) {
+ case 'P': /* palette escape sequence */
vc_reset_params(vc);
vc->vc_state = ESpalette;
return;
- } else if (c=='R') { /* reset palette */
+ case 'R': /* reset palette */
reset_palette(vc);
- vc->vc_state = ESnormal;
- } else if (c>='0' && c<='9')
+ break;
+ case '0' ... '9':
vc->vc_state = ESosc;
- else
- vc->vc_state = ESnormal;
+ return;
+ }
+ vc->vc_state = ESnormal;
return;
case ESpalette: /* ESC ] P aka OSC P */
if (isxdigit(c)) {
--
2.43.0


2024-02-02 07:01:34

by Jiri Slaby

[permalink] [raw]
Subject: [PATCH 20/22] tty: vt: use switch+case in the ESgetpars case

To be uniform in the 'c' handling, use switch-case (with ranges) even in
the ESgetpars case in do_con_trol().

Signed-off-by: Jiri Slaby (SUSE) <[email protected]>
---
drivers/tty/vt/vt.c | 12 ++++++++----
1 file changed, 8 insertions(+), 4 deletions(-)

diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c
index b5fc3b896e26..b3c61ec92df9 100644
--- a/drivers/tty/vt/vt.c
+++ b/drivers/tty/vt/vt.c
@@ -2628,10 +2628,14 @@ static void do_con_trol(struct tty_struct *tty, struct vc_data *vc, u8 c)
vc->vc_priv = EPecma;
fallthrough;
case ESgetpars: /* ESC [ aka CSI, parameters expected */
- if (c == ';' && vc->vc_npar < NPAR - 1) {
- vc->vc_npar++;
- return;
- } else if (c>='0' && c<='9') {
+ switch (c) {
+ case ';':
+ if (vc->vc_npar < NPAR - 1) {
+ vc->vc_npar++;
+ return;
+ }
+ break;
+ case '0' ... '9':
vc->vc_par[vc->vc_npar] *= 10;
vc->vc_par[vc->vc_npar] += c - '0';
return;
--
2.43.0


2024-02-02 07:01:46

by Jiri Slaby

[permalink] [raw]
Subject: [PATCH 21/22] tty: vt: use ASCII enum constants in vt_console_print()

There are still numbers used for ASCII characters in vt_console_print().
As we have an ASCII enum now, use the constant names from the enum
instead.

Signed-off-by: Jiri Slaby (SUSE) <[email protected]>
---
drivers/tty/vt/vt.c | 9 +++++----
1 file changed, 5 insertions(+), 4 deletions(-)

diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c
index b3c61ec92df9..e35f7a31a7bd 100644
--- a/drivers/tty/vt/vt.c
+++ b/drivers/tty/vt/vt.c
@@ -3240,22 +3240,23 @@ static void vt_console_print(struct console *co, const char *b, unsigned count)
cnt = 0;
while (count--) {
c = *b++;
- if (c == 10 || c == 13 || c == 8 || vc->vc_need_wrap) {
+ if (c == ASCII_LINEFEED || c == ASCII_CAR_RET ||
+ c == ASCII_BACKSPACE || vc->vc_need_wrap) {
if (cnt && con_is_visible(vc))
vc->vc_sw->con_putcs(vc, start, cnt, vc->state.y, start_x);
cnt = 0;
- if (c == 8) { /* backspace */
+ if (c == ASCII_BACKSPACE) {
bs(vc);
start = (ushort *)vc->vc_pos;
start_x = vc->state.x;
continue;
}
- if (c != 13)
+ if (c != ASCII_CAR_RET)
lf(vc);
cr(vc);
start = (ushort *)vc->vc_pos;
start_x = vc->state.x;
- if (c == 10 || c == 13)
+ if (c == ASCII_LINEFEED || c == ASCII_CAR_RET)
continue;
}
vc_uniscr_putc(vc, c);
--
2.43.0


2024-02-02 07:02:01

by Jiri Slaby

[permalink] [raw]
Subject: [PATCH 22/22] tty: vt: decrypt magic constants in vc_is_control()

0x0d00ff81 and 0x0800f501 are bitmasks of ASCII characters. Spell them
explicitly using BIT() + ASCII constants. GENMASK() is used for the
9-bit range in CTRL_ACTION.

This also modifies the 'if' checking if the masks should be applied.
From a "random" ' ' to the actual size of the bitmasks' type.

Signed-off-by: Jiri Slaby (SUSE) <[email protected]>
---
drivers/tty/vt/vt.c | 10 +++++++---
1 file changed, 7 insertions(+), 3 deletions(-)

diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c
index e35f7a31a7bd..463be4e48dc8 100644
--- a/drivers/tty/vt/vt.c
+++ b/drivers/tty/vt/vt.c
@@ -2893,9 +2893,13 @@ static bool vc_is_control(struct vc_data *vc, int tc, int c)
* as cursor movement) and should not be displayed as a glyph unless
* the disp_ctrl mode is explicitly enabled.
*/
- static const u32 CTRL_ACTION = 0x0d00ff81;
+ static const u32 CTRL_ACTION = BIT(ASCII_NULL) |
+ GENMASK(ASCII_SHIFTIN, ASCII_BELL) | BIT(ASCII_CANCEL) |
+ BIT(ASCII_SUBSTITUTE) | BIT(ASCII_ESCAPE);
/* Cannot be overridden by disp_ctrl */
- static const u32 CTRL_ALWAYS = 0x0800f501;
+ static const u32 CTRL_ALWAYS = BIT(ASCII_NULL) | BIT(ASCII_BACKSPACE) |
+ BIT(ASCII_LINEFEED) | BIT(ASCII_SHIFTIN) | BIT(ASCII_SHIFTOUT) |
+ BIT(ASCII_CAR_RET) | BIT(ASCII_FORMFEED) | BIT(ASCII_ESCAPE);

if (vc->vc_state != ESnormal)
return true;
@@ -2912,7 +2916,7 @@ static bool vc_is_control(struct vc_data *vc, int tc, int c)
* useless without them; to display an arbitrary font position use the
* direct-to-font zone in UTF-8 mode.
*/
- if (c < ' ') {
+ if (c < BITS_PER_TYPE(CTRL_ALWAYS)) {
if (vc->vc_disp_ctrl)
return CTRL_ALWAYS & BIT(c);
else
--
2.43.0


2024-02-02 07:02:52

by Jiri Slaby

[permalink] [raw]
Subject: [PATCH 01/22] tty: vt: make rgb_from_256() slighly more comprehensible

* make the parameter unsigned, as it is expected to be unsigned,
* make the computation easier to follow -- step-by-step, and
* don't use 85 / 2 which is only a reduced form of 255 / 6 (by a factor
3). Unlike the former, the latter can be understood.

Signed-off-by: Jiri Slaby (SUSE) <[email protected]>
---
drivers/tty/vt/vt.c | 11 +++++++----
1 file changed, 7 insertions(+), 4 deletions(-)

diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c
index 65cd40cac96b..7d42f148559a 100644
--- a/drivers/tty/vt/vt.c
+++ b/drivers/tty/vt/vt.c
@@ -1588,7 +1588,7 @@ static void default_attr(struct vc_data *vc)

struct rgb { u8 r; u8 g; u8 b; };

-static void rgb_from_256(int i, struct rgb *c)
+static void rgb_from_256(unsigned int i, struct rgb *c)
{
if (i < 8) { /* Standard colours. */
c->r = i&1 ? 0xaa : 0x00;
@@ -1599,9 +1599,12 @@ static void rgb_from_256(int i, struct rgb *c)
c->g = i&2 ? 0xff : 0x55;
c->b = i&4 ? 0xff : 0x55;
} else if (i < 232) { /* 6x6x6 colour cube. */
- c->r = (i - 16) / 36 * 85 / 2;
- c->g = (i - 16) / 6 % 6 * 85 / 2;
- c->b = (i - 16) % 6 * 85 / 2;
+ i -= 16;
+ c->b = i % 6 * 255 / 6;
+ i /= 6;
+ c->g = i % 6 * 255 / 6;
+ i /= 6;
+ c->r = i * 255 / 6;
} else /* Grayscale ramp. */
c->r = c->g = c->b = i * 10 - 2312;
}
--
2.43.0


2024-02-02 07:05:41

by Jiri Slaby

[permalink] [raw]
Subject: [PATCH 06/22] tty: vt: move CSI+n handling along to other ECMA CSIs

CSIs without [<=>?] modifiers (ECMA) are handled in the switch-case
below this DEC switch+case handler. So move this ECMA CSI+n there too as
it fits there better.

Signed-off-by: Jiri Slaby (SUSE) <[email protected]>
---
drivers/tty/vt/vt.c | 14 ++++++--------
1 file changed, 6 insertions(+), 8 deletions(-)

diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c
index 69ebce0878f2..04d109464994 100644
--- a/drivers/tty/vt/vt.c
+++ b/drivers/tty/vt/vt.c
@@ -2412,14 +2412,6 @@ static void do_con_trol(struct tty_struct *tty, struct vc_data *vc, int c)
return;
}
break;
- case 'n':
- if (vc->vc_priv == EPecma) {
- if (vc->vc_par[0] == 5)
- status_report(tty);
- else if (vc->vc_par[0] == 6)
- cursor_report(vc, tty);
- }
- return;
}
if (vc->vc_priv != EPecma)
return;
@@ -2506,6 +2498,12 @@ static void do_con_trol(struct tty_struct *tty, struct vc_data *vc, int c)
case 'm':
csi_m(vc);
return;
+ case 'n':
+ if (vc->vc_par[0] == 5)
+ status_report(tty);
+ else if (vc->vc_par[0] == 6)
+ cursor_report(vc, tty);
+ return;
case 'q': /* DECLL - but only 3 leds */
/* map 0,1,2,3 to 0,1,2,4 */
if (vc->vc_par[0] < 4)
--
2.43.0


2024-02-02 07:11:20

by Jiri Slaby

[permalink] [raw]
Subject: [PATCH 13/22] tty: vt: move CSI DEC handling to a separate function

The handling of "CSI ? ..." (i.e. vc_priv == EPdec) can be easily moved
out of do_con_trol() into a separate function. This again increases
readability of do_con_trol().

Signed-off-by: Jiri Slaby (SUSE) <[email protected]>
---
drivers/tty/vt/vt.c | 75 ++++++++++++++++++++++++++-------------------
1 file changed, 44 insertions(+), 31 deletions(-)

diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c
index 3dddb7128234..7cdd0eb1e423 100644
--- a/drivers/tty/vt/vt.c
+++ b/drivers/tty/vt/vt.c
@@ -2338,6 +2338,43 @@ static void handle_esc(struct tty_struct *tty, struct vc_data *vc, u8 c)
}
}

+/*
+ * Handle special DEC control sequences ("ESC [ ? parameters char"). Parameters
+ * are in @vc->vc_par and the char is in @c here.
+ */
+static void csi_DEC(struct tty_struct *tty, struct vc_data *vc, u8 c)
+{
+ switch (c) {
+ case 'h':
+ csi_DEC_hl(vc, true);
+ break;
+ case 'l':
+ csi_DEC_hl(vc, false);
+ break;
+ case 'c':
+ if (vc->vc_par[0])
+ vc->vc_cursor_type = CUR_MAKE(vc->vc_par[0],
+ vc->vc_par[1],
+ vc->vc_par[2]);
+ else
+ vc->vc_cursor_type = cur_default;
+ break;
+ case 'm':
+ clear_selection();
+ if (vc->vc_par[0])
+ vc->vc_complement_mask = vc->vc_par[0] << 8 | vc->vc_par[1];
+ else
+ vc->vc_complement_mask = vc->vc_s_complement_mask;
+ break;
+ case 'n':
+ if (vc->vc_par[0] == 5)
+ status_report(tty);
+ else if (vc->vc_par[0] == 6)
+ cursor_report(vc, tty);
+ break;
+ }
+}
+
/* console_lock is held */
static void do_con_trol(struct tty_struct *tty, struct vc_data *vc, u8 c)
{
@@ -2427,40 +2464,16 @@ static void do_con_trol(struct tty_struct *tty, struct vc_data *vc, u8 c)
return;
}
vc->vc_state = ESnormal;
- switch(c) {
- case 'h':
- if (vc->vc_priv == EPdec)
- csi_DEC_hl(vc, true);
- return;
- case 'l':
- if (vc->vc_priv == EPdec)
- csi_DEC_hl(vc, false);
+
+ switch (vc->vc_priv) {
+ case EPdec:
+ csi_DEC(tty, vc, c);
return;
- case 'c':
- if (vc->vc_priv == EPdec) {
- if (vc->vc_par[0])
- vc->vc_cursor_type =
- CUR_MAKE(vc->vc_par[0],
- vc->vc_par[1],
- vc->vc_par[2]);
- else
- vc->vc_cursor_type = cur_default;
- return;
- }
+ case EPecma:
break;
- case 'm':
- if (vc->vc_priv == EPdec) {
- clear_selection();
- if (vc->vc_par[0])
- vc->vc_complement_mask = vc->vc_par[0] << 8 | vc->vc_par[1];
- else
- vc->vc_complement_mask = vc->vc_s_complement_mask;
- return;
- }
- break;
- }
- if (vc->vc_priv != EPecma)
+ default:
return;
+ }

switch(c) {
case 'G':
--
2.43.0


2024-02-02 07:19:48

by Jiri Slaby

[permalink] [raw]
Subject: [PATCH 12/22] tty: vt: separate ESesc state handling into handle_esc()

Similar to the ASCII handling, the ESC handling can be easily moved away
from do_con_trol(). So create a new handle_esc() for that.

And add a comment with an example.

Signed-off-by: Jiri Slaby (SUSE) <[email protected]>
---
drivers/tty/vt/vt.c | 134 ++++++++++++++++++++++++--------------------
1 file changed, 72 insertions(+), 62 deletions(-)

diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c
index 7cda1a958c5e..3dddb7128234 100644
--- a/drivers/tty/vt/vt.c
+++ b/drivers/tty/vt/vt.c
@@ -2267,6 +2267,77 @@ static bool handle_ascii(struct tty_struct *tty, struct vc_data *vc, u8 c)
return false;
}

+/*
+ * Handle a character (@c) following an ESC (when @vc is in the ESesc state).
+ * E.g. previous ESC with @c == '[' here yields the ESsquare state (that is:
+ * CSI).
+ */
+static void handle_esc(struct tty_struct *tty, struct vc_data *vc, u8 c)
+{
+ vc->vc_state = ESnormal;
+ switch (c) {
+ case '[':
+ vc->vc_state = ESsquare;
+ break;
+ case ']':
+ vc->vc_state = ESnonstd;
+ break;
+ case '_':
+ vc->vc_state = ESapc;
+ break;
+ case '^':
+ vc->vc_state = ESpm;
+ break;
+ case '%':
+ vc->vc_state = ESpercent;
+ break;
+ case 'E':
+ cr(vc);
+ lf(vc);
+ break;
+ case 'M':
+ ri(vc);
+ break;
+ case 'D':
+ lf(vc);
+ break;
+ case 'H':
+ if (vc->state.x < VC_TABSTOPS_COUNT)
+ set_bit(vc->state.x, vc->vc_tab_stop);
+ break;
+ case 'P':
+ vc->vc_state = ESdcs;
+ break;
+ case 'Z':
+ respond_ID(tty);
+ break;
+ case '7':
+ save_cur(vc);
+ break;
+ case '8':
+ restore_cur(vc);
+ break;
+ case '(':
+ vc->vc_state = ESsetG0;
+ break;
+ case ')':
+ vc->vc_state = ESsetG1;
+ break;
+ case '#':
+ vc->vc_state = EShash;
+ break;
+ case 'c':
+ reset_terminal(vc, 1);
+ break;
+ case '>': /* Numeric keypad */
+ clr_kbd(vc, kbdapplic);
+ break;
+ case '=': /* Appl. keypad */
+ set_kbd(vc, kbdapplic);
+ break;
+ }
+}
+
/* console_lock is held */
static void do_con_trol(struct tty_struct *tty, struct vc_data *vc, u8 c)
{
@@ -2283,68 +2354,7 @@ static void do_con_trol(struct tty_struct *tty, struct vc_data *vc, u8 c)

switch(vc->vc_state) {
case ESesc:
- vc->vc_state = ESnormal;
- switch (c) {
- case '[':
- vc->vc_state = ESsquare;
- return;
- case ']':
- vc->vc_state = ESnonstd;
- return;
- case '_':
- vc->vc_state = ESapc;
- return;
- case '^':
- vc->vc_state = ESpm;
- return;
- case '%':
- vc->vc_state = ESpercent;
- return;
- case 'E':
- cr(vc);
- lf(vc);
- return;
- case 'M':
- ri(vc);
- return;
- case 'D':
- lf(vc);
- return;
- case 'H':
- if (vc->state.x < VC_TABSTOPS_COUNT)
- set_bit(vc->state.x, vc->vc_tab_stop);
- return;
- case 'P':
- vc->vc_state = ESdcs;
- return;
- case 'Z':
- respond_ID(tty);
- return;
- case '7':
- save_cur(vc);
- return;
- case '8':
- restore_cur(vc);
- return;
- case '(':
- vc->vc_state = ESsetG0;
- return;
- case ')':
- vc->vc_state = ESsetG1;
- return;
- case '#':
- vc->vc_state = EShash;
- return;
- case 'c':
- reset_terminal(vc, 1);
- return;
- case '>': /* Numeric keypad */
- clr_kbd(vc, kbdapplic);
- return;
- case '=': /* Appl. keypad */
- set_kbd(vc, kbdapplic);
- return;
- }
+ handle_esc(tty, vc, c);
return;
case ESnonstd:
if (c=='P') { /* palette escape sequence */
--
2.43.0


2024-02-02 07:19:49

by Jiri Slaby

[permalink] [raw]
Subject: [PATCH 09/22] tty: vt: put cases on separate lines

Some cases of the CSI switch are stuffed on one line. Put them all to a
separate line as is dictated by the coding style (and for better
readability).

Signed-off-by: Jiri Slaby (SUSE) <[email protected]>
---
drivers/tty/vt/vt.c | 12 ++++++++----
1 file changed, 8 insertions(+), 4 deletions(-)

diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c
index c072007807e1..42bc0957a654 100644
--- a/drivers/tty/vt/vt.c
+++ b/drivers/tty/vt/vt.c
@@ -2437,7 +2437,8 @@ static void do_con_trol(struct tty_struct *tty, struct vc_data *vc, int c)
return;

switch(c) {
- case 'G': case '`':
+ case 'G':
+ case '`':
if (vc->vc_par[0])
vc->vc_par[0]--;
gotoxy(vc, vc->vc_par[0], vc->state.y);
@@ -2447,12 +2448,14 @@ static void do_con_trol(struct tty_struct *tty, struct vc_data *vc, int c)
vc->vc_par[0]++;
gotoxy(vc, vc->state.x, vc->state.y - vc->vc_par[0]);
return;
- case 'B': case 'e':
+ case 'B':
+ case 'e':
if (!vc->vc_par[0])
vc->vc_par[0]++;
gotoxy(vc, vc->state.x, vc->state.y + vc->vc_par[0]);
return;
- case 'C': case 'a':
+ case 'C':
+ case 'a':
if (!vc->vc_par[0])
vc->vc_par[0]++;
gotoxy(vc, vc->state.x + vc->vc_par[0], vc->state.y);
@@ -2477,7 +2480,8 @@ static void do_con_trol(struct tty_struct *tty, struct vc_data *vc, int c)
vc->vc_par[0]--;
gotoxay(vc, vc->state.x ,vc->vc_par[0]);
return;
- case 'H': case 'f':
+ case 'H':
+ case 'f':
if (vc->vc_par[0])
vc->vc_par[0]--;
if (vc->vc_par[1])
--
2.43.0


2024-02-02 07:20:04

by Jiri Slaby

[permalink] [raw]
Subject: [PATCH 10/22] tty: vt: accept u8 in do_con_trol() and vc_setGx()

These functions expect u8 as the control character. Switch the type from
'int' appropriately. The caller passing the value (do_con_write()) is
fixed as well.

Signed-off-by: Jiri Slaby (SUSE) <[email protected]>
---
drivers/tty/vt/vt.c | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c
index 42bc0957a654..451a852ed234 100644
--- a/drivers/tty/vt/vt.c
+++ b/drivers/tty/vt/vt.c
@@ -2144,7 +2144,7 @@ static void reset_terminal(struct vc_data *vc, int do_clear)
csi_J(vc, CSI_J_VISIBLE);
}

-static void vc_setGx(struct vc_data *vc, unsigned int which, int c)
+static void vc_setGx(struct vc_data *vc, unsigned int which, u8 c)
{
unsigned char *charset = &vc->state.Gx_charset[which];

@@ -2198,7 +2198,7 @@ enum {
};

/* console_lock is held */
-static void do_con_trol(struct tty_struct *tty, struct vc_data *vc, int c)
+static void do_con_trol(struct tty_struct *tty, struct vc_data *vc, u8 c)
{
/*
* Control characters can be used in the _middle_
@@ -2963,7 +2963,7 @@ static int do_con_write(struct tty_struct *tty, const u8 *buf, int count)
param.vc = vc;

while (!tty->flow.stopped && count) {
- int orig = *buf;
+ u8 orig = *buf;
buf++;
n++;
count--;
--
2.43.0