From 1ab6304c3eb31d184049968f7ba53c52b31995f2 Mon Sep 17 00:00:00 2001
From: Xavier L'Heureux <xavier.lheureux@icloud.com>
Date: Tue, 2 Jul 2019 17:00:36 -0400
Subject: [PATCH] Don't fork functions, prefer to execute in foreground

When the result is needed, use the .command method on Expander to
streamline the expansion
When the function is blocking (ex: COMMAND_NOT_FOUND), don't fork and
execute the function in foreground instead
---
 src/binary/mod.rs              | 18 +++++++++---------
 src/binary/prompt.rs           | 25 ++-----------------------
 src/lib/builtins/mod.rs        |  6 ++++--
 src/lib/shell/fork.rs          |  8 --------
 src/lib/shell/fork_function.rs | 34 ----------------------------------
 src/lib/shell/mod.rs           |  4 +---
 6 files changed, 16 insertions(+), 79 deletions(-)
 delete mode 100644 src/lib/shell/fork_function.rs

diff --git a/src/binary/mod.rs b/src/binary/mod.rs
index d7e030c3..fa0cc5cd 100644
--- a/src/binary/mod.rs
+++ b/src/binary/mod.rs
@@ -11,7 +11,7 @@ use ion_shell::{
     builtins::{man_pages, Status},
     expansion::Expander,
     parser::Terminator,
-    types, Capture, IonError, PipelineError, Shell, Signal,
+    types, IonError, PipelineError, Shell, Signal, Value,
 };
 use itertools::Itertools;
 use liner::{Buffer, Context, KeyBindings};
@@ -228,15 +228,15 @@ impl<'a> InteractiveShell<'a> {
                             Err(IonError::PipelineExecutionError(
                                 PipelineError::CommandNotFound(command),
                             )) => {
-                                if shell
-                                    .fork_function(
-                                        Capture::None,
-                                        |_| Ok(()),
-                                        "COMMAND_NOT_FOUND",
-                                        &["ion", &command],
-                                    )
-                                    .is_err()
+                                if let Some(Value::Function(func)) =
+                                    shell.variables().get("COMMAND_NOT_FOUND").cloned()
                                 {
+                                    if let Err(why) =
+                                        shell.execute_function(&func, &["ion", &command])
+                                    {
+                                        eprintln!("ion: command not found handler: {}", why);
+                                    }
+                                } else {
                                     eprintln!("ion: command not found: {}", command);
                                 }
                                 // Status::COULD_NOT_EXEC
diff --git a/src/binary/prompt.rs b/src/binary/prompt.rs
index e1252037..9758eb97 100644
--- a/src/binary/prompt.rs
+++ b/src/binary/prompt.rs
@@ -1,6 +1,5 @@
 use super::InteractiveShell;
-use ion_shell::{expansion::Expander, Capture, Shell};
-use std::io::Read;
+use ion_shell::expansion::Expander;
 
 impl<'a> InteractiveShell<'a> {
     /// Generates the prompt that will be used by Liner.
@@ -9,7 +8,7 @@ impl<'a> InteractiveShell<'a> {
         let blocks = shell.block_len() + if shell.unterminated { 1 } else { 0 };
 
         if blocks == 0 {
-            Self::prompt_fn(&shell).unwrap_or_else(|| {
+            shell.command("PROMPT").map(|res| res.to_string()).unwrap_or_else(|_| {
                 match shell.get_string(&shell.variables().get_str("PROMPT").unwrap_or_default()) {
                     Ok(prompt) => prompt.to_string(),
                     Err(why) => {
@@ -22,24 +21,4 @@ impl<'a> InteractiveShell<'a> {
             "    ".repeat(blocks)
         }
     }
-
-    pub fn prompt_fn(shell: &Shell<'_>) -> Option<String> {
-        shell
-            .fork_function(
-                Capture::StdoutThenIgnoreStderr,
-                |result| {
-                    let mut string = String::with_capacity(1024);
-                    match result.stdout.ok_or(())?.read_to_string(&mut string) {
-                        Ok(_) => Ok(string),
-                        Err(why) => {
-                            eprintln!("ion: error reading stdout of child: {}", why);
-                            Err(())
-                        }
-                    }
-                },
-                "PROMPT",
-                &["ion"],
-            )
-            .ok()
-    }
 }
diff --git a/src/lib/builtins/mod.rs b/src/lib/builtins/mod.rs
index 3a11d102..2a94989e 100644
--- a/src/lib/builtins/mod.rs
+++ b/src/lib/builtins/mod.rs
@@ -35,7 +35,7 @@ pub use self::{
 };
 use crate as ion_shell;
 use crate::{
-    shell::{Capture, Shell, Value},
+    shell::{Shell, Value},
     types,
 };
 use builtins_proc::builtin;
@@ -308,7 +308,9 @@ pub fn cd(args: &[types::Str], shell: &mut Shell<'_>) -> Status {
 
     match err {
         Ok(()) => {
-            let _ = shell.fork_function(Capture::None, |_| Ok(()), "CD_CHANGE", &["ion"]);
+            if let Some(Value::Function(function)) = shell.variables().get("CD_CHANGE").cloned() {
+                let _ = shell.execute_function(&function, &["ion"]);
+            }
             Status::SUCCESS
         }
         Err(why) => Status::error(format!("{}", why)),
diff --git a/src/lib/shell/fork.rs b/src/lib/shell/fork.rs
index 39e3db60..84fbccf5 100644
--- a/src/lib/shell/fork.rs
+++ b/src/lib/shell/fork.rs
@@ -22,24 +22,16 @@ pub fn wait_for_child(pid: unistd::Pid) -> nix::Result<i32> {
 ///
 /// A type that is utilized by the `Fork` structure.
 pub enum Capture {
-    /// Don't capture any streams at all.
-    None = 0b0000,
     /// Capture just the standard output stream.
     Stdout = 0b0001,
     /// Capture just the standard error stream.
     Stderr = 0b0010,
-    /// Capture both the standard output and error streams.
-    Both = 0b0011,
     /// Redirect just the stdandard output stream to /dev/null.
     IgnoreStdout = 0b0100,
     /// Redirect just the standard error stream to /dev/null.
     IgnoreStderr = 0b1000,
-    /// Redirect both the standard output and error streams to /dev/null.
-    IgnoreBoth = 0b1100,
     /// Capture standard output and ignore standard error.
     StdoutThenIgnoreStderr = 0b1001,
-    /// Capture standard error and ignore standard output.
-    StderrThenIgnoreStdout = 0b0110,
 }
 
 /// Utilized by the shell for performing forks and capturing streams.
diff --git a/src/lib/shell/fork_function.rs b/src/lib/shell/fork_function.rs
deleted file mode 100644
index c3d9679d..00000000
--- a/src/lib/shell/fork_function.rs
+++ /dev/null
@@ -1,34 +0,0 @@
-use super::{fork::IonResult, variables::Value, Capture, Shell};
-use nix::unistd::{self, Pid};
-
-impl<'a> Shell<'a> {
-    /// High-level function for executing a function programmatically.
-    /// NOTE: Always add "ion" as a first argument in `args`.
-    pub fn fork_function<S: AsRef<str>, T, F: FnOnce(IonResult) -> Result<T, ()>>(
-        &self,
-        capture: Capture,
-        result: F,
-        fn_name: &str,
-        args: &[S],
-    ) -> Result<T, ()> {
-        if let Some(Value::Function(function)) = self.variables.get(fn_name) {
-            let output = self
-                .fork(capture, move |child| {
-                    if let Err(err) = function.execute(child, args) {
-                        if capture == Capture::None {
-                            eprintln!("ion: {} function call: {}", fn_name, err);
-                        }
-                    }
-                    Ok(())
-                })
-                .map_err(|err| eprintln!("ion: fork error: {}", err))
-                .and_then(result);
-
-            // Ensure that the parent retains ownership of the terminal before exiting.
-            let _ = unistd::tcsetpgrp(nix::libc::STDIN_FILENO, Pid::this());
-            output
-        } else {
-            Err(())
-        }
-    }
-}
diff --git a/src/lib/shell/mod.rs b/src/lib/shell/mod.rs
index c30efcb0..10b5e9d6 100644
--- a/src/lib/shell/mod.rs
+++ b/src/lib/shell/mod.rs
@@ -5,7 +5,6 @@ mod flow;
 /// The various blocks
 pub mod flow_control;
 mod fork;
-mod fork_function;
 mod job;
 mod pipe_exec;
 mod shell_expand;
@@ -18,13 +17,12 @@ pub(crate) use self::job::{Job, RefinedJob};
 use self::{
     directory_stack::DirectoryStack,
     flow_control::{Block, Function, FunctionError, Statement},
-    fork::{Fork, IonResult},
+    fork::{Capture, Fork, IonResult},
     pipe_exec::foreground,
     variables::Variables,
 };
 pub use self::{
     flow::BlockError,
-    fork::Capture,
     pipe_exec::{
         job_control::{BackgroundEvent, BackgroundProcess},
         PipelineError,
-- 
GitLab