Any terminal younger than ~40 years requires lowercase commands, thus they need to be exempt from uppercasing. And Linux doesn't really support ancient all-uppercase terminals anyway (no XCASE, etc) even if someone made the effort to somehow connect them electrically.
This bug is reproducible as of Linux 0.11 and I see it in 0.01 sources (whose images fail to boot for me, I didn't try very hard). It was less of a failure then as the shell didn't produce tty codes for normal operation. Signed-off-by: Adam Borowski <kilob...@angband.pl> --- drivers/tty/n_tty.c | 58 ++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 46 insertions(+), 12 deletions(-) diff --git a/drivers/tty/n_tty.c b/drivers/tty/n_tty.c index bdf0e6e89991..bbc9f07c19fa 100644 --- a/drivers/tty/n_tty.c +++ b/drivers/tty/n_tty.c @@ -87,6 +87,8 @@ # define n_tty_trace(f, args...) #endif +enum { ESnormal, ESesc, EScsi, ESsetG }; + struct n_tty_data { /* producer-published */ size_t read_head; @@ -121,6 +123,7 @@ struct n_tty_data { unsigned int column; unsigned int canon_column; size_t echo_tail; + unsigned int vt_state; struct mutex atomic_read_lock; struct mutex output_lock; @@ -392,6 +395,40 @@ static inline int is_continuation(unsigned char c, struct tty_struct *tty) return I_IUTF8(tty) && is_utf8_continuation(c); } +/* process one OLCUC char (possibly partial unicode) + * + * We need to partially parse ANSI sequences to avoid uppercasing them; + * only some commands require lowercase. + */ + +static int do_olcuc_char(unsigned char c, struct tty_struct *tty) +{ + struct n_tty_data *ldata = tty->disc_data; + + switch (ldata->vt_state) { + case ESesc: + ldata->vt_state = (c == '[') ? EScsi : + strchr("%()*+-./", c) ? ESsetG : ESnormal; + break; + case EScsi: + if (!strchr("?;0123456789>!c$\" \\", c)) + ldata->vt_state = ESnormal; + break; + case ESsetG: + ldata->vt_state = ESnormal; + break; + default: + if (c == '\e') + ldata->vt_state = ESesc; + else if (c >= 'a' && c <= 'z') + c -= 32; + } + if (!iscntrl(c) && !is_continuation(c, tty)) + ldata->column++; + tty_put_char(tty, c); + return 1; +} + /** * do_output_char - output one character * @c: character (or partial unicode symbol) @@ -462,12 +499,10 @@ static int do_output_char(unsigned char c, struct tty_struct *tty, int space) ldata->column--; break; default: - if (!iscntrl(c)) { - if (O_OLCUC(tty)) - c = toupper(c); - if (!is_continuation(c, tty)) - ldata->column++; - } + if (O_OLCUC(tty)) + return do_olcuc_char(c, tty); + if (!iscntrl(c) && !is_continuation(c, tty)) + ldata->column++; break; } @@ -568,12 +603,10 @@ static ssize_t process_output_block(struct tty_struct *tty, ldata->column--; break; default: - if (!iscntrl(c)) { - if (O_OLCUC(tty)) - goto break_out; - if (!is_continuation(c, tty)) - ldata->column++; - } + if (O_OLCUC(tty)) + goto break_out; + if (!iscntrl(c) && !is_continuation(c, tty)) + ldata->column++; break; } } @@ -1895,6 +1928,7 @@ static int n_tty_open(struct tty_struct *tty) ldata->num_overrun = 0; ldata->no_room = 0; ldata->lnext = 0; + ldata->vt_state = ESnormal; tty->closing = 0; /* indicate buffer work may resume */ clear_bit(TTY_LDISC_HALTED, &tty->flags); -- 2.11.0