From b2ef18d5380076916f12f1f6a69cf47772b38f3a Mon Sep 17 00:00:00 2001 From: Ali Mohammad Pur Date: Mon, 24 May 2021 16:50:16 +0430 Subject: [PATCH] LibLine+Shell: Allow some programs to modify the current termios This setting can be controlled by setting the PROGRAMS_ALLOWED_TO_MODIFY_DEFAULT_TERMIOS _local_ shell variable to a list containing such programs. --- Userland/Libraries/LibLine/Editor.cpp | 22 +++++++++++++++++----- Userland/Libraries/LibLine/Editor.h | 2 ++ Userland/Shell/Shell.cpp | 25 ++++++++++++++++++++++--- Userland/Shell/Shell.h | 12 +++++++++--- 4 files changed, 50 insertions(+), 11 deletions(-) diff --git a/Userland/Libraries/LibLine/Editor.cpp b/Userland/Libraries/LibLine/Editor.cpp index dea04f1616..1bdcc4040b 100644 --- a/Userland/Libraries/LibLine/Editor.cpp +++ b/Userland/Libraries/LibLine/Editor.cpp @@ -141,10 +141,6 @@ void Editor::set_default_keybinds() { register_key_input_callback(ctrl('N'), EDITOR_INTERNAL_FUNCTION(search_forwards)); register_key_input_callback(ctrl('P'), EDITOR_INTERNAL_FUNCTION(search_backwards)); - // Normally ^W. `stty werase \^n` can change it to ^N (or something else), but Serenity doesn't have `stty` yet. - register_key_input_callback(m_termios.c_cc[VWERASE], EDITOR_INTERNAL_FUNCTION(erase_word_backwards)); - // Normally ^U. `stty kill \^n` can change it to ^N (or something else), but Serenity doesn't have `stty` yet. - register_key_input_callback(m_termios.c_cc[VKILL], EDITOR_INTERNAL_FUNCTION(kill_line)); register_key_input_callback(ctrl('A'), EDITOR_INTERNAL_FUNCTION(go_home)); register_key_input_callback(ctrl('B'), EDITOR_INTERNAL_FUNCTION(cursor_left_character)); register_key_input_callback(ctrl('D'), EDITOR_INTERNAL_FUNCTION(erase_character_forwards)); @@ -154,7 +150,6 @@ void Editor::set_default_keybinds() register_key_input_callback(ctrl('H'), EDITOR_INTERNAL_FUNCTION(erase_character_backwards)); // DEL - Some terminals send this instead of ^H. register_key_input_callback((char)127, EDITOR_INTERNAL_FUNCTION(erase_character_backwards)); - register_key_input_callback(m_termios.c_cc[VERASE], EDITOR_INTERNAL_FUNCTION(erase_character_backwards)); register_key_input_callback(ctrl('K'), EDITOR_INTERNAL_FUNCTION(erase_to_end)); register_key_input_callback(ctrl('L'), EDITOR_INTERNAL_FUNCTION(clear_screen)); register_key_input_callback(ctrl('R'), EDITOR_INTERNAL_FUNCTION(enter_search)); @@ -175,6 +170,13 @@ void Editor::set_default_keybinds() register_key_input_callback(Key { 'l', Key::Alt }, EDITOR_INTERNAL_FUNCTION(lowercase_word)); register_key_input_callback(Key { 'u', Key::Alt }, EDITOR_INTERNAL_FUNCTION(uppercase_word)); register_key_input_callback(Key { 't', Key::Alt }, EDITOR_INTERNAL_FUNCTION(transpose_words)); + + // Register these last to all the user to override the previous key bindings + // Normally ^W. `stty werase \^n` can change it to ^N (or something else). + register_key_input_callback(m_termios.c_cc[VWERASE], EDITOR_INTERNAL_FUNCTION(erase_word_backwards)); + // Normally ^U. `stty kill \^n` can change it to ^N (or something else). + register_key_input_callback(m_termios.c_cc[VKILL], EDITOR_INTERNAL_FUNCTION(kill_line)); + register_key_input_callback(m_termios.c_cc[VERASE], EDITOR_INTERNAL_FUNCTION(erase_character_backwards)); } Editor::Editor(Configuration configuration) @@ -552,6 +554,16 @@ void Editor::initialize() m_initialized = true; } +void Editor::refetch_default_termios() +{ + struct termios termios; + tcgetattr(0, &termios); + m_default_termios = termios; + if (m_configuration.operation_mode == Configuration::Full) + termios.c_lflag &= ~(ECHO | ICANON); + m_termios = termios; +} + void Editor::interrupted() { if (m_is_searching) diff --git a/Userland/Libraries/LibLine/Editor.h b/Userland/Libraries/LibLine/Editor.h index 3a3ced8960..52328b58da 100644 --- a/Userland/Libraries/LibLine/Editor.h +++ b/Userland/Libraries/LibLine/Editor.h @@ -148,6 +148,8 @@ public: void initialize(); + void refetch_default_termios(); + void add_to_history(const String& line); bool load_history(const String& path); bool save_history(const String& path); diff --git a/Userland/Shell/Shell.cpp b/Userland/Shell/Shell.cpp index 76ee97c122..ccec9725c9 100644 --- a/Userland/Shell/Shell.cpp +++ b/Userland/Shell/Shell.cpp @@ -342,7 +342,7 @@ Shell::LocalFrame* Shell::find_frame_containing_local_variable(const String& nam return nullptr; } -RefPtr Shell::lookup_local_variable(const String& name) +RefPtr Shell::lookup_local_variable(const String& name) const { if (auto* frame = find_frame_containing_local_variable(name)) return frame->local_variables.get(name).value(); @@ -353,7 +353,7 @@ RefPtr Shell::lookup_local_variable(const String& name) return nullptr; } -RefPtr Shell::get_argument(size_t index) +RefPtr Shell::get_argument(size_t index) const { if (index == 0) return adopt_ref(*new AST::StringValue(current_script)); @@ -377,7 +377,7 @@ RefPtr Shell::get_argument(size_t index) return nullptr; } -String Shell::local_variable_or(const String& name, const String& replacement) +String Shell::local_variable_or(const String& name, const String& replacement) const { auto value = lookup_local_variable(name); if (value) { @@ -846,6 +846,12 @@ RefPtr Shell::run_command(const AST::Command& command) last_return_code = job->exit_code(); job->disown(); + if (m_editor && job->exit_code() == 0 && is_allowed_to_modify_termios(job->command())) { + m_editor->refetch_default_termios(); + default_termios = m_editor->default_termios(); + termios = m_editor->termios(); + } + run_tail(job); }; @@ -1025,6 +1031,19 @@ bool Shell::run_file(const String& filename, bool explicitly_invoked) auto data = file->read_all(); return run_command(data) == 0; } + +bool Shell::is_allowed_to_modify_termios(const AST::Command& command) const +{ + if (command.argv.is_empty()) + return false; + + auto value = lookup_local_variable("PROGRAMS_ALLOWED_TO_MODIFY_DEFAULT_TERMIOS"sv); + if (!value) + return false; + + return value->resolve_as_list(*this).contains_slow(command.argv[0]); +} + void Shell::restore_ios() { if (m_is_subshell) diff --git a/Userland/Shell/Shell.h b/Userland/Shell/Shell.h index 61b48e3ac8..0a90751fcc 100644 --- a/Userland/Shell/Shell.h +++ b/Userland/Shell/Shell.h @@ -108,9 +108,9 @@ public: static bool has_history_event(StringView); - RefPtr get_argument(size_t); - RefPtr lookup_local_variable(const String&); - String local_variable_or(const String&, const String&); + RefPtr get_argument(size_t) const; + RefPtr lookup_local_variable(const String&) const; + String local_variable_or(const String&, const String&) const; void set_local_variable(const String&, RefPtr, bool only_in_current_frame = false); void unset_local_variable(const String&, bool only_in_current_frame = false); @@ -278,6 +278,8 @@ private: void timer_event(Core::TimerEvent&) override; + bool is_allowed_to_modify_termios(const AST::Command&) const; + // FIXME: Port to Core::Property void save_to(JsonObject&); void bring_cursor_to_beginning_of_a_line() const; @@ -288,6 +290,10 @@ private: void stop_all_jobs(); const Job* m_current_job { nullptr }; LocalFrame* find_frame_containing_local_variable(const String& name); + const LocalFrame* find_frame_containing_local_variable(const String& name) const + { + return const_cast(this)->find_frame_containing_local_variable(name); + } void run_tail(RefPtr); void run_tail(const AST::Command&, const AST::NodeWithAction&, int head_exit_code);