浏览代码

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.
Ali Mohammad Pur 4 年之前
父节点
当前提交
b2ef18d538
共有 4 个文件被更改,包括 50 次插入11 次删除
  1. 17 5
      Userland/Libraries/LibLine/Editor.cpp
  2. 2 0
      Userland/Libraries/LibLine/Editor.h
  3. 22 3
      Userland/Shell/Shell.cpp
  4. 9 3
      Userland/Shell/Shell.h

+ 17 - 5
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)

+ 2 - 0
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);

+ 22 - 3
Userland/Shell/Shell.cpp

@@ -342,7 +342,7 @@ Shell::LocalFrame* Shell::find_frame_containing_local_variable(const String& nam
     return nullptr;
 }
 
-RefPtr<AST::Value> Shell::lookup_local_variable(const String& name)
+RefPtr<AST::Value> 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<AST::Value> Shell::lookup_local_variable(const String& name)
     return nullptr;
 }
 
-RefPtr<AST::Value> Shell::get_argument(size_t index)
+RefPtr<AST::Value> Shell::get_argument(size_t index) const
 {
     if (index == 0)
         return adopt_ref(*new AST::StringValue(current_script));
@@ -377,7 +377,7 @@ RefPtr<AST::Value> 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<Job> 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)

+ 9 - 3
Userland/Shell/Shell.h

@@ -108,9 +108,9 @@ public:
 
     static bool has_history_event(StringView);
 
-    RefPtr<AST::Value> get_argument(size_t);
-    RefPtr<AST::Value> lookup_local_variable(const String&);
-    String local_variable_or(const String&, const String&);
+    RefPtr<AST::Value> get_argument(size_t) const;
+    RefPtr<AST::Value> lookup_local_variable(const String&) const;
+    String local_variable_or(const String&, const String&) const;
     void set_local_variable(const String&, RefPtr<AST::Value>, 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<Shell*>(this)->find_frame_containing_local_variable(name);
+    }
 
     void run_tail(RefPtr<Job>);
     void run_tail(const AST::Command&, const AST::NodeWithAction&, int head_exit_code);