From 169e93f0a74b18ed935b071a3605fa986b69e4d5 Mon Sep 17 00:00:00 2001 From: Daniel Bertalan Date: Sat, 5 Jun 2021 12:39:02 +0200 Subject: [PATCH] Kernel: Perform output processing on echo Previously, we would echo characters back just as they were passed to us, even in canonical mode. This caused newlines to not work correctly in some programs. Fixes #7802 --- Kernel/TTY/TTY.cpp | 41 ++++++++++++++++++++++++++++------------- Kernel/TTY/TTY.h | 4 ++++ 2 files changed, 32 insertions(+), 13 deletions(-) diff --git a/Kernel/TTY/TTY.cpp b/Kernel/TTY/TTY.cpp index f32e50b5ed..8780cec36a 100644 --- a/Kernel/TTY/TTY.cpp +++ b/Kernel/TTY/TTY.cpp @@ -90,22 +90,17 @@ KResultOr TTY::write(FileDescription&, u64, const UserOrKernelBuffer& bu constexpr size_t num_chars = 256; return buffer.read_buffered(size, [&](u8 const* data, size_t buffer_bytes) { u8 modified_data[num_chars * 2]; - size_t extra_chars = 0; + size_t modified_data_size = 0; for (size_t i = 0; i < buffer_bytes; ++i) { - auto ch = data[i]; - if (m_termios.c_oflag & OPOST) { - if (ch == '\n' && (m_termios.c_oflag & ONLCR)) { - modified_data[i + extra_chars] = '\r'; - extra_chars++; - } - } - modified_data[i + extra_chars] = ch; + process_output(data[i], [this, &modified_data, &modified_data_size](u8 out_ch) { + modified_data[modified_data_size++] = out_ch; + }); } - ssize_t bytes_written = on_tty_write(UserOrKernelBuffer::for_kernel_buffer(modified_data), buffer_bytes + extra_chars); + ssize_t bytes_written = on_tty_write(UserOrKernelBuffer::for_kernel_buffer(modified_data), modified_data_size); VERIFY(bytes_written != 0); if (bytes_written < 0 || !(m_termios.c_oflag & OPOST) || !(m_termios.c_oflag & ONLCR)) return bytes_written; - if ((size_t)bytes_written == buffer_bytes + extra_chars) + if ((size_t)bytes_written == modified_data_size) return (ssize_t)buffer_bytes; // Degenerate case where we converted some newlines and encountered a partial write @@ -127,6 +122,23 @@ KResultOr TTY::write(FileDescription&, u64, const UserOrKernelBuffer& bu }); } +void TTY::echo_with_processing(u8 ch) +{ + process_output(ch, [this](u8 out_ch) { echo(out_ch); }); +} + +template +void TTY::process_output(u8 ch, Functor put_char) +{ + if (m_termios.c_oflag & OPOST) { + if (ch == '\n' && (m_termios.c_oflag & ONLCR)) + put_char('\r'); + put_char(ch); + } else { + put_char(ch); + } +} + bool TTY::can_read(const FileDescription&, size_t) const { if (in_canonical_mode()) { @@ -239,7 +251,8 @@ void TTY::emit(u8 ch, bool do_evaluate_block_conditions) if (ch == '\n') { if (m_termios.c_lflag & ECHO || m_termios.c_lflag & ECHONL) - echo('\n'); + echo_with_processing('\n'); + set_special_bit(); m_input_buffer.enqueue('\n'); m_available_lines++; @@ -254,7 +267,7 @@ void TTY::emit(u8 ch, bool do_evaluate_block_conditions) m_input_buffer.enqueue(ch); if (m_termios.c_lflag & ECHO) - echo(ch); + echo_with_processing(ch); } bool TTY::can_do_backspace() const @@ -271,6 +284,7 @@ void TTY::do_backspace() { if (can_do_backspace()) { m_input_buffer.dequeue_end(); + // We deliberately don't process the output here. echo(8); echo(' '); echo(8); @@ -318,6 +332,7 @@ void TTY::kill_line() void TTY::erase_character() { + // We deliberately don't process the output here. echo(m_termios.c_cc[VERASE]); echo(' '); echo(m_termios.c_cc[VERASE]); diff --git a/Kernel/TTY/TTY.h b/Kernel/TTY/TTY.h index a34961bad3..9e5ee61bfd 100644 --- a/Kernel/TTY/TTY.h +++ b/Kernel/TTY/TTY.h @@ -80,6 +80,10 @@ protected: private: // ^CharacterDevice virtual bool is_tty() const final override { return true; } + inline void echo_with_processing(u8); + + template + void process_output(u8, Functor put_char); CircularDeque m_input_buffer; // FIXME: use something like AK::Bitmap but which takes a size template parameter