From 37c35100485d15875f7053d4fabf6ebb926c4eb5 Mon Sep 17 00:00:00 2001
From: Xavier L'Heureux <xavier.lheureux@icloud.com>
Date: Tue, 2 Jul 2019 14:48:11 -0400
Subject: [PATCH] Reduce the number of shell options

---
 src/binary/mod.rs       | 43 ++++++++++++++++++++++++++++-------------
 src/binary/prompt.rs    |  2 +-
 src/binary/readln.rs    |  4 ++--
 src/lib/builtins/set.rs |  9 ---------
 src/lib/shell/mod.rs    |  5 -----
 5 files changed, 33 insertions(+), 30 deletions(-)

diff --git a/src/binary/mod.rs b/src/binary/mod.rs
index fa0cc5cd..abe624e0 100644
--- a/src/binary/mod.rs
+++ b/src/binary/mod.rs
@@ -8,7 +8,7 @@ mod prompt;
 mod readln;
 
 use ion_shell::{
-    builtins::{man_pages, Status},
+    builtins::{man_pages, BuiltinFunction, Status},
     expansion::Expander,
     parser::Terminator,
     types, IonError, PipelineError, Shell, Signal, Value,
@@ -16,7 +16,7 @@ use ion_shell::{
 use itertools::Itertools;
 use liner::{Buffer, Context, KeyBindings};
 use std::{
-    cell::RefCell,
+    cell::{Cell, RefCell},
     fs::{self, OpenOptions},
     io,
     path::Path,
@@ -57,8 +57,10 @@ DESCRIPTION
     Prints the command history."#;
 
 pub struct InteractiveShell<'a> {
-    context: Rc<RefCell<Context>>,
-    shell:   RefCell<Shell<'a>>,
+    context:    Rc<RefCell<Context>>,
+    shell:      RefCell<Shell<'a>>,
+    terminated: Cell<bool>,
+    huponexit:  Rc<Cell<bool>>,
 }
 
 impl<'a> InteractiveShell<'a> {
@@ -74,7 +76,12 @@ impl<'a> InteractiveShell<'a> {
             }
             let _ = context.history.set_file_name_and_load_history(path.as_str());
         }
-        InteractiveShell { context: Rc::new(RefCell::new(context)), shell: RefCell::new(shell) }
+        InteractiveShell {
+            context:    Rc::new(RefCell::new(context)),
+            shell:      RefCell::new(shell),
+            terminated: Cell::new(true),
+            huponexit:  Rc::new(Cell::new(false)),
+        }
     }
 
     /// Handles commands given by the REPL, and saves them to history.
@@ -123,10 +130,11 @@ impl<'a> InteractiveShell<'a> {
     /// Liner.
     pub fn execute_interactive(self) -> ! {
         let context_bis = self.context.clone();
+        let huponexit = self.huponexit.clone();
         let prep_for_exit = &move |shell: &mut Shell<'_>| {
             // context will be sent a signal to commit all changes to the history file,
             // and waiting for the history thread in the background to finish.
-            if shell.opts().huponexit {
+            if huponexit.get() {
                 shell.resume_stopped();
                 shell.background_send(Signal::SIGHUP).expect("Failed to prepare for exit");
             }
@@ -155,6 +163,15 @@ impl<'a> InteractiveShell<'a> {
             Status::SUCCESS
         };
 
+        let huponexit = self.huponexit.clone();
+        let set_huponexit: BuiltinFunction = &move |args, _shell| {
+            huponexit.set(match args.get(1).map(AsRef::as_ref) {
+                Some("false") | Some("off") => false,
+                _ => true,
+            });
+            Status::SUCCESS
+        };
+
         let context_bis = self.context.clone();
         let keybindings = &move |args: &[types::Str], _shell: &mut Shell<'_>| -> Status {
             match args.get(1).map(|s| s.as_str()) {
@@ -172,18 +189,20 @@ impl<'a> InteractiveShell<'a> {
         };
 
         // change the lifetime to allow adding local builtins
-        let InteractiveShell { context, shell } = self;
+        let InteractiveShell { context, shell, terminated, huponexit } = self;
         let mut shell = shell.into_inner();
         shell
             .builtins_mut()
             .add("history", history, "Display a log of all commands previously executed")
             .add("keybindings", keybindings, "Change the keybindings")
             .add("exit", exit, "Exits the current session")
-            .add("exec", exec, "Replace the shell with the given command.");
+            .add("exec", exec, "Replace the shell with the given command.")
+            .add("huponexit", set_huponexit, "Hangup the shell's background jobs on exit");
 
         Self::exec_init_file(&mut shell);
 
-        InteractiveShell { context, shell: RefCell::new(shell) }.exec(prep_for_exit)
+        InteractiveShell { context, shell: RefCell::new(shell), terminated, huponexit }
+            .exec(prep_for_exit)
     }
 
     fn exec_init_file(shell: &mut Shell) {
@@ -220,9 +239,9 @@ impl<'a> InteractiveShell<'a> {
                         &self.context.borrow(),
                         command.trim_end(),
                     );
+                    self.terminated.set(true);
                     {
                         let mut shell = self.shell.borrow_mut();
-                        shell.unterminated = false;
                         match shell.on_command(&cmd) {
                             Ok(_) => (),
                             Err(IonError::PipelineExecutionError(
@@ -249,9 +268,7 @@ impl<'a> InteractiveShell<'a> {
                     }
                     self.save_command(&cmd);
                 }
-                None => {
-                    self.shell.borrow_mut().unterminated = true;
-                }
+                None => self.terminated.set(false),
             }
         }
     }
diff --git a/src/binary/prompt.rs b/src/binary/prompt.rs
index 9758eb97..bb6c7beb 100644
--- a/src/binary/prompt.rs
+++ b/src/binary/prompt.rs
@@ -5,7 +5,7 @@ impl<'a> InteractiveShell<'a> {
     /// Generates the prompt that will be used by Liner.
     pub fn prompt(&self) -> String {
         let shell = self.shell.borrow();
-        let blocks = shell.block_len() + if shell.unterminated { 1 } else { 0 };
+        let blocks = if self.terminated.get() { shell.block_len() } else { shell.block_len() + 1 };
 
         if blocks == 0 {
             shell.command("PROMPT").map(|res| res.to_string()).unwrap_or_else(|_| {
diff --git a/src/binary/readln.rs b/src/binary/readln.rs
index 4339a883..278a76d6 100644
--- a/src/binary/readln.rs
+++ b/src/binary/readln.rs
@@ -18,7 +18,7 @@ impl<'a> InteractiveShell<'a> {
                 if line.bytes().next() != Some(b'#')
                     && line.bytes().any(|c| !c.is_ascii_whitespace())
                 {
-                    self.shell.borrow_mut().unterminated = true;
+                    self.terminated.set(false);
                 }
                 Some(line)
             }
@@ -27,7 +27,7 @@ impl<'a> InteractiveShell<'a> {
             // Handles Ctrl + D
             Err(ref err) if err.kind() == ErrorKind::UnexpectedEof => {
                 let mut shell = self.shell.borrow_mut();
-                if !shell.unterminated && shell.exit_block().is_err() {
+                if self.terminated.get() && shell.exit_block().is_err() {
                     prep_for_exit(&mut shell);
                     std::process::exit(shell.previous_status().as_os_code())
                 }
diff --git a/src/lib/builtins/set.rs b/src/lib/builtins/set.rs
index 4b373cff..bb051178 100644
--- a/src/lib/builtins/set.rs
+++ b/src/lib/builtins/set.rs
@@ -62,15 +62,6 @@ pub fn set(args: &[types::Str], shell: &mut Shell<'_>) -> Status {
             for flag in arg.bytes().skip(1) {
                 match flag {
                     b'e' => shell.opts_mut().err_exit = false,
-                    b'o' => match args_iter.next().map(|s| s as &str) {
-                        Some("huponexit") => shell.opts_mut().huponexit = false,
-                        Some(_) => {
-                            return Status::error("ion: set: invalid option");
-                        }
-                        None => {
-                            return Status::error("ion: set: no option given");
-                        }
-                    },
                     _ => return Status::SUCCESS,
                 }
             }
diff --git a/src/lib/shell/mod.rs b/src/lib/shell/mod.rs
index 10b5e9d6..2cd59f73 100644
--- a/src/lib/shell/mod.rs
+++ b/src/lib/shell/mod.rs
@@ -103,8 +103,6 @@ pub struct Options {
     pub err_exit: bool,
     /// Do not execute any commands given to the shell.
     pub no_exec: bool,
-    /// Hangup on exiting the shell.
-    pub huponexit: bool,
     /// If set, denotes that this shell is running as a background job.
     pub grab_tty: bool,
 }
@@ -133,8 +131,6 @@ pub struct Shell<'a> {
     /// Contains information on all of the active background processes that are being managed
     /// by the shell.
     background: Arc<Mutex<Vec<BackgroundProcess>>>,
-    /// Used by an interactive session to know when the input is not terminated.
-    pub unterminated: bool,
     /// When the `fg` command is run, this will be used to communicate with the specified
     /// background process.
     foreground_signals: Arc<foreground::Signals>,
@@ -217,7 +213,6 @@ impl<'a> Shell<'a> {
             on_command: None,
             pre_command: None,
             background_event: None,
-            unterminated: false,
 
             stdin: None,
             stdout: None,
-- 
GitLab