diff --git a/src/lib/builtins/mod.rs b/src/lib/builtins/mod.rs index 9b40233a8e178950d4b309cf997c14b5c9135780..a81181d60a53305257abb9154dd166b74363850e 100644 --- a/src/lib/builtins/mod.rs +++ b/src/lib/builtins/mod.rs @@ -37,9 +37,10 @@ use std::path::Path; use parser::Terminator; use parser::pipelines::{PipeItem, Pipeline}; -use shell::{self, FlowLogic, Job, JobKind, Shell, ShellHistory}; use shell::job_control::{JobControl, ProcessState}; +use shell::fork_function::fork_function; use shell::status::*; +use shell::{self, FlowLogic, Job, JobKind, Shell, ShellHistory}; use sys; const HELP_DESC: &str = "Display helpful information about a given command or list commands if \ @@ -171,10 +172,12 @@ pub fn builtin_cd(args: &[&str], shell: &mut Shell) -> i32 { let pwd = shell.get_var_or_empty("PWD"); let pwd: &str = &pwd; let current_dir = path.to_str().unwrap_or("?"); + if pwd != current_dir { env::set_var("OLDPWD", pwd); env::set_var("PWD", current_dir); } + fork_function(shell, "CD_CHANGE", &["ion"]); }, ); SUCCESS @@ -666,4 +669,4 @@ fn builtin_isatty(args: &[&str], _: &mut Shell) -> i32 { } FAILURE -} \ No newline at end of file +} diff --git a/src/lib/lib.rs b/src/lib/lib.rs index ab1ededfcf7c6693fd2b2f880ba8d07c9d15087f..045da92a627ebc97c1465de637e4336c34df6c1d 100644 --- a/src/lib/lib.rs +++ b/src/lib/lib.rs @@ -47,6 +47,8 @@ mod builtins; mod shell; mod ascii_helpers; -pub use shell::{Binary, Capture, Fork, IonError, IonResult, Shell, ShellBuilder}; +pub use shell::binary::MAN_ION; pub use shell::flags; pub use shell::status; +pub use shell::{Binary, Capture, Fork, IonError, IonResult, Shell, ShellBuilder}; +pub use shell::pipe_exec::job_control::JobControl; diff --git a/src/lib/shell/binary/mod.rs b/src/lib/shell/binary/mod.rs index 46132cb497f5be1d32ada11543b7e50ae44e5937..e7c5eaf5380c1a1abdd0d462bc15f71dd8a2d0b9 100644 --- a/src/lib/shell/binary/mod.rs +++ b/src/lib/shell/binary/mod.rs @@ -7,22 +7,18 @@ mod terminate; use self::prompt::{prompt, prompt_fn}; use self::readln::readln; use self::terminate::{terminate_quotes, terminate_script_quotes}; -use super::{FlowLogic, JobControl, Shell, ShellHistory}; -use super::flags::*; +use super::{FlowLogic, Shell, ShellHistory}; use super::flow_control::Statement; use super::status::*; use liner::{Buffer, Context}; -use smallvec::SmallVec; use std::env; -use std::error::Error; use std::fs::File; -use std::io::{stdout, Write}; use std::io::ErrorKind; -use std::iter::{self, FromIterator}; +use std::iter; use std::path::Path; use std::process; -const MAN_ION: &'static str = r#"NAME +pub const MAN_ION: &'static str = r#"NAME ion - ion shell SYNOPSIS @@ -44,9 +40,6 @@ OPTIONS "#; pub trait Binary { - /// Launches the shell, parses arguments, and then diverges into one of the `execution` - /// paths. - fn main(self); /// Parses and executes the arguments that were supplied to the shell. fn execute_arguments<A: Iterator<Item = String>>(&mut self, args: A); /// Creates an interactive session that reads from a prompt provided by @@ -192,47 +185,6 @@ impl Binary for Shell { } } - fn main(mut self) { - let mut args = env::args().skip(1); - while let Some(path) = args.next() { - match path.as_str() { - "-n" | "--no-execute" => { - self.flags |= NO_EXEC; - continue; - } - "-c" => self.execute_arguments(args), - "-v" | "--version" => self.display_version(), - "-h" | "--help" => { - let stdout = stdout(); - let mut stdout = stdout.lock(); - match stdout - .write_all(MAN_ION.as_bytes()) - .and_then(|_| stdout.flush()) - { - Ok(_) => return, - Err(err) => panic!("{}", err.description().to_owned()), - } - } - _ => { - let mut array = SmallVec::from_iter(Some(path.clone().into())); - for arg in args { - array.push(arg.into()); - } - self.variables.set_array("args", array); - if let Err(err) = self.execute_script(&path) { - eprintln!("ion: {}", err); - } - } - } - - self.wait_for_background(); - let previous_status = self.previous_status; - self.exit(previous_status); - } - - self.execute_interactive(); - } - fn display_version(&self) { println!("{}", include!(concat!(env!("OUT_DIR"), "/version_string"))); process::exit(0); diff --git a/src/lib/shell/directory_stack.rs b/src/lib/shell/directory_stack.rs index 5eefe5c57c5ee84eac3efcccf80ed1bedc69d5c4..bda81db543763ad86bcd17eceedc1acb7e7913bd 100644 --- a/src/lib/shell/directory_stack.rs +++ b/src/lib/shell/directory_stack.rs @@ -1,9 +1,9 @@ -use super::status::{FAILURE, SUCCESS}; -use super::variables::Variables; use std::borrow::Cow; use std::collections::VecDeque; use std::env::{current_dir, home_dir, set_current_dir}; use std::path::PathBuf; +use super::status::{FAILURE, SUCCESS}; +use super::variables::Variables; pub struct DirectoryStack { dirs: VecDeque<PathBuf>, // The top is always the current directory diff --git a/src/lib/shell/pipe_exec/command_not_found.rs b/src/lib/shell/fork_function.rs similarity index 51% rename from src/lib/shell/pipe_exec/command_not_found.rs rename to src/lib/shell/fork_function.rs index 888a39b90564d8c2a4d8a4919ad716cc8984248e..1fe74d37520d00ea27262f84b2f326cc1f9e23ba 100644 --- a/src/lib/shell/pipe_exec/command_not_found.rs +++ b/src/lib/shell/fork_function.rs @@ -1,17 +1,23 @@ -use super::super::{Capture, Function, Shell}; +use super::{Capture, Function, Shell}; use std::process; use sys; pub(crate) fn command_not_found(shell: &mut Shell, command: &str) -> bool { - let function = match shell.functions.get("COMMAND_NOT_FOUND") { + fork_function(shell, "COMMAND_NOT_FOUND", &["ion", &command]) +} + +/// High-level function for executing a function programmatically. +/// NOTE: Always add "ion" as a first argument in `args`. +pub fn fork_function(shell: &mut Shell, fn_name: &str, args: &[&str]) -> bool { + let function = match shell.functions.get(fn_name) { Some(func) => func as *const Function, None => return false, }; if let Err(err) = shell.fork(Capture::None, |child| { - let result = unsafe { function.read() }.execute(child, &["ion", command]); + let result = unsafe { function.read() }.execute(child, args); if let Err(err) = result { - eprintln!("ion: COMMAND_NOT_FOUND function call: {}", err); + eprintln!("ion: {} function call: {}", fn_name, err); } }) { eprintln!("ion: fork error: {}", err); diff --git a/src/lib/shell/mod.rs b/src/lib/shell/mod.rs index c483cf76e3dddbb05ee55ec329397dd5993be626..dfdf25cb31a5180931ccc7b4de9792c9dfb6fcd4 100644 --- a/src/lib/shell/mod.rs +++ b/src/lib/shell/mod.rs @@ -1,19 +1,20 @@ mod assignments; -mod binary; mod completer; mod flow; mod fork; mod history; mod job; -mod pipe_exec; +pub mod flags; +pub mod fork_function; +pub mod status; +pub mod variables; +pub(crate) mod binary; pub(crate) mod colors; pub(crate) mod directory_stack; -pub mod flags; -pub(crate) mod plugins; pub(crate) mod flow_control; +pub(crate) mod pipe_exec; +pub(crate) mod plugins; pub(crate) mod signals; -pub mod status; -pub mod variables; pub use self::binary::Binary; pub(crate) use self::flow::FlowLogic; @@ -70,7 +71,7 @@ pub struct Shell { /// Note that the context is only available in an interactive session. pub(crate) context: Option<Context>, /// Contains the aliases, strings, and array variable maps. - pub(crate) variables: Variables, + pub variables: Variables, /// Contains the current state of flow control parameters. flow_control: FlowControl, /// Contains the directory stack parameters. @@ -202,7 +203,8 @@ impl<'a> Shell { } } - pub(crate) fn exit(&mut self, status: i32) -> ! { + /// Cleanly exit ion + pub fn exit(&mut self, status: i32) -> ! { self.prep_for_exit(); process::exit(status); } diff --git a/src/lib/shell/pipe_exec/job_control.rs b/src/lib/shell/pipe_exec/job_control.rs index 5fcd3d086b3c2cf433b78d7985e0aee2926e7079..b2d26a3f895eac03ac51dff2103971777d8e5630 100644 --- a/src/lib/shell/pipe_exec/job_control.rs +++ b/src/lib/shell/pipe_exec/job_control.rs @@ -20,7 +20,7 @@ pub(crate) fn set_foreground_as(pid: u32) { signals::unblock(); } -pub(crate) trait JobControl { +pub trait JobControl { /// Waits for background jobs to finish before returning. fn wait_for_background(&mut self); /// Takes a background tasks's PID and whether or not it needs to be continued; resumes the diff --git a/src/lib/shell/pipe_exec/mod.rs b/src/lib/shell/pipe_exec/mod.rs index 26529d11283e94f973ff9c4deb14334a3fc99323..9af781d700091c31620d15b9e9007ad03f76cd27 100644 --- a/src/lib/shell/pipe_exec/mod.rs +++ b/src/lib/shell/pipe_exec/mod.rs @@ -5,30 +5,29 @@ //! the background, handling pipeline and conditional operators, and //! std{in,out,err} redirections. -mod command_not_found; -pub mod foreground; mod fork; -pub mod job_control; mod streams; +pub mod foreground; +pub mod job_control; -use self::command_not_found::command_not_found; +use builtins::{self, BuiltinFunction}; +use parser::pipelines::{Input, PipeItem, Pipeline, RedirectFrom, Redirection}; use self::fork::fork_pipe; use self::job_control::{JobControl, ProcessState}; use self::streams::{duplicate_streams, redir, redirect_streams}; -use super::{JobKind, Shell}; -use super::flags::*; -use super::flow_control::FunctionError; -use super::job::{RefinedJob, TeeItem}; -use super::signals::{self, SignalHandler}; -use super::status::*; -use builtins::{self, BuiltinFunction}; -use parser::pipelines::{Input, PipeItem, Pipeline, RedirectFrom, Redirection}; use std::fs::{File, OpenOptions}; use std::io::{self, Error, Write}; use std::iter; use std::os::unix::io::{AsRawFd, FromRawFd, RawFd}; use std::path::Path; use std::process::{self, exit}; +use super::flags::*; +use super::flow_control::FunctionError; +use super::fork_function::command_not_found; +use super::job::{RefinedJob, TeeItem}; +use super::signals::{self, SignalHandler}; +use super::status::*; +use super::{JobKind, Shell}; use sys; type RefinedItem = (RefinedJob, JobKind, Vec<Redirection>, Vec<Input>); diff --git a/src/main.rs b/src/main.rs index e5cd2d1d228672ac69e05df4c6c890aa604f361f..ad618c3c28e8a50a18fc629a7f14e04c56762c86 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,14 +1,59 @@ extern crate ion_shell; +extern crate smallvec; +use ion_shell::JobControl; +use ion_shell::MAN_ION; +use ion_shell::flags::NO_EXEC; use ion_shell::{Binary, ShellBuilder}; +use smallvec::SmallVec; +use std::env; +use std::error::Error; +use std::io::{stdout, Write}; +use std::iter::FromIterator; fn main() { - ShellBuilder::new() + let mut shell = ShellBuilder::new() .install_signal_handler() .block_signals() .set_unique_pid() - .as_binary() - .main(); -} + .as_binary(); + + let mut args = env::args().skip(1); + while let Some(path) = args.next() { + match path.as_str() { + "-n" | "--no-execute" => { + shell.flags |= NO_EXEC; + continue; + } + "-c" => shell.execute_arguments(args), + "-v" | "--version" => shell.display_version(), + "-h" | "--help" => { + let stdout = stdout(); + let mut stdout = stdout.lock(); + match stdout + .write_all(MAN_ION.as_bytes()) + .and_then(|_| stdout.flush()) + { + Ok(_) => return, + Err(err) => panic!("{}", err.description().to_owned()), + } + } + _ => { + let mut array = SmallVec::from_iter(Some(path.clone().into())); + for arg in args { + array.push(arg.into()); + } + shell.variables.set_array("args", array); + if let Err(err) = shell.execute_script(&path) { + eprintln!("ion: {}", err); + } + } + } -// TODO: The `Binary` / `main()` logic should be implemented here, and not within the library. \ No newline at end of file + shell.wait_for_background(); + let previous_status = shell.previous_status; + shell.exit(previous_status); + } + + shell.execute_interactive(); +}