diff --git a/src/builtins/functions.rs b/src/builtins/functions.rs index 37400a5832c9fde13335b316e9f11fe4f7f404ba..3efda681808e687db3c4b97d9a837826cb38127c 100644 --- a/src/builtins/functions.rs +++ b/src/builtins/functions.rs @@ -9,7 +9,7 @@ fn print_functions(functions: &FnvHashMap<Identifier, Function>) { let stdout = &mut stdout.lock(); let _ = writeln!(stdout, "# Functions"); for fn_name in functions.keys() { - let description = &functions.get(fn_name).unwrap().description; + let description = &functions.get(fn_name).unwrap().get_description(); if let Some(ref description) = *description { let _ = writeln!(stdout, " {} -- {}", fn_name, description); } else { diff --git a/src/builtins/mod.rs b/src/builtins/mod.rs index c69f4f11ca4e268b930c5c5c2679984c66ecac12..b03f72ca33feb3a617935ecceb33f2c2b21d44bf 100644 --- a/src/builtins/mod.rs +++ b/src/builtins/mod.rs @@ -33,7 +33,7 @@ use sys; /// Structure which represents a Terminal's command. /// This command structure contains a name, and the code which run the /// functionnality associated to this one, with zero, one or several argument(s). -pub(crate) struct Builtin { +pub struct Builtin { pub name: &'static str, pub help: &'static str, pub main: fn(&[&str], &mut Shell) -> i32, @@ -41,7 +41,7 @@ pub(crate) struct Builtin { impl Builtin { /// Return the map from command names to commands - pub(crate) fn map() -> FnvHashMap<&'static str, Self> { + pub fn map() -> FnvHashMap<&'static str, Self> { let mut commands: FnvHashMap<&str, Self> = FnvHashMap::with_capacity_and_hasher(32, Default::default()); diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000000000000000000000000000000000000..836cebf15172c902e75a4da869a76f27401b9861 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,44 @@ +#![allow(unknown_lints)] +#![allow(while_let_on_iterator)] +extern crate app_dirs; +#[macro_use] +extern crate bitflags; +extern crate calc; +extern crate fnv; +extern crate glob; +#[macro_use] +extern crate lazy_static; +#[cfg(all(unix, not(target_os = "redox")))] +extern crate libc; +#[cfg(all(unix, not(target_os = "redox")))] +extern crate libloading; +extern crate liner; +#[cfg(all(unix, not(target_os = "redox")))] +extern crate nix; +extern crate regex; +extern crate smallstring; +extern crate smallvec; +#[cfg(target_os = "redox")] +extern crate syscall; +extern crate unicode_segmentation; +#[cfg(all(unix, not(target_os = "redox")))] +extern crate users as users_unix; + +#[cfg(target_os = "redox")] +#[path = "sys/redox.rs"] +mod sys; + +#[cfg(unix)] +#[path = "sys/unix.rs"] +mod sys; + +#[macro_use] +mod types; +#[macro_use] +mod parser; +mod builtins; +pub mod shell; +mod ascii_helpers; + +pub use builtins::Builtin; +pub use shell::Shell; diff --git a/src/shell/binary.rs b/src/shell/binary.rs index addcc4d8295758ab4dc64d0841256fe11fb23e3b..c844cf91dbc1e1d0f1e5281444fe940ea7095b6f 100644 --- a/src/shell/binary.rs +++ b/src/shell/binary.rs @@ -4,6 +4,7 @@ use super::{DirectoryStack, FlowLogic, JobControl, Shell, ShellHistory, Variable use super::completer::*; use super::flow_control::Statement; use super::status::*; +use super::library::IonLibrary; use liner::{BasicCompleter, Buffer, Context, CursorPosition, Event, EventKind}; use parser::*; use parser::QuoteTerminator; @@ -11,7 +12,7 @@ use smallstring::SmallString; use smallvec::SmallVec; use std::env; use std::fs::File; -use std::io::{self, ErrorKind, Read, Write}; +use std::io::{self, ErrorKind, Write}; use std::iter::{self, FromIterator}; use std::mem; use std::path::{Path, PathBuf}; @@ -27,8 +28,6 @@ pub(crate) trait Binary { fn execute_arguments<A: Iterator<Item = String>>(&mut self, args: A); /// Creates an interactive session that reads from a prompt provided by Liner. fn execute_interactive(self); - /// Executes all of the statements contained within a given script. - fn execute_script<P: AsRef<Path>>(&mut self, path: P); /// Ensures that read statements from a script are terminated. fn terminate_script_quotes<I: Iterator<Item = String>>(&mut self, lines: I); /// Ensures that read statements from the interactive prompt is terminated. @@ -300,7 +299,9 @@ impl<'a> Binary for Shell<'a> { array.push(arg.into()); } self.variables.set_array("args", array); - self.execute_script(&path); + if let Err(err) = self.execute_script(&path) { + eprintln!("ion: {}", err); + } } } @@ -316,31 +317,6 @@ impl<'a> Binary for Shell<'a> { println!("{}", include!(concat!(env!("OUT_DIR"), "/version_string"))); process::exit(0); } - - fn execute_script<P: AsRef<Path>>(&mut self, path: P) { - let path = path.as_ref(); - match File::open(path) { - Ok(mut file) => { - let capacity = file.metadata().ok().map_or(0, |x| x.len()); - let mut command_list = String::with_capacity(capacity as usize); - match file.read_to_string(&mut command_list) { - Ok(_) => { - self.terminate_script_quotes(command_list.lines().map(|x| x.to_owned())) - } - Err(err) => { - let stderr = io::stderr(); - let mut stderr = stderr.lock(); - let _ = writeln!(stderr, "ion: failed to read {:?}: {}", path, err); - } - } - } - Err(err) => { - let stderr = io::stderr(); - let mut stderr = stderr.lock(); - let _ = writeln!(stderr, "ion: failed to open {:?}: {}", path, err); - } - } - } } fn word_divide(buf: &Buffer) -> Vec<(usize, usize)> { diff --git a/src/shell/directory_stack.rs b/src/shell/directory_stack.rs index fa1cf29ac352a6dabb4fb08cf0468a8881efc643..25684f2afcc328093dc0e2acf1138aa727954bdb 100644 --- a/src/shell/directory_stack.rs +++ b/src/shell/directory_stack.rs @@ -5,7 +5,7 @@ use std::collections::VecDeque; use std::env::{current_dir, home_dir, set_current_dir}; use std::path::PathBuf; -pub(crate) struct DirectoryStack { +pub struct DirectoryStack { dirs: VecDeque<PathBuf>, // The top is always the current directory } diff --git a/src/shell/flags.rs b/src/shell/flags.rs index 7322416ec2bdec8974b879e29da48f7378c68efd..e2a72ceddd7467196e31585f7ce78a6c31e93d34 100644 --- a/src/shell/flags.rs +++ b/src/shell/flags.rs @@ -1,2 +1,2 @@ -pub(crate) const ERR_EXIT: u8 = 1; -pub(crate) const PRINT_COMMS: u8 = 2; +pub const ERR_EXIT: u8 = 1; +pub const PRINT_COMMS: u8 = 2; diff --git a/src/shell/flow.rs b/src/shell/flow.rs index 0c825b4049eeb7387c512ad172d8c127683180b9..7ceb281449e8faa8043464293da9d228b4b4d357 100644 --- a/src/shell/flow.rs +++ b/src/shell/flow.rs @@ -195,12 +195,7 @@ impl<'a> FlowLogic for Shell<'a> { } => { shell.functions.insert( name.clone(), - Function { - name: name, - args: args, - statements: statements, - description: description, - }, + Function::new(description, name, args, statements), ); } Statement::If { @@ -449,12 +444,7 @@ impl<'a> FlowLogic for Shell<'a> { collect_loops(&mut iterator, &mut statements, &mut self.flow_control.level); self.functions.insert( name.clone(), - Function { - description: description, - name: name, - args: args, - statements: statements, - }, + Function::new(description, name, args, statements), ); } Statement::Pipeline(mut pipeline) => { @@ -737,12 +727,7 @@ impl<'a> FlowLogic for Shell<'a> { // All blocks were read, thus we can add it to the list self.functions.insert( name.clone(), - Function { - description: description, - name: name, - args: args, - statements: statements, - }, + Function::new(description, name, args, statements), ); } else { // Store the partial function declaration in memory. diff --git a/src/shell/flow_control.rs b/src/shell/flow_control.rs index 8144064b988682a65ad06fefa13a6db13a6a522e..e65135af570d90230b9f9cb46b7256d7e4e7c6ac 100644 --- a/src/shell/flow_control.rs +++ b/src/shell/flow_control.rs @@ -123,11 +123,11 @@ impl Default for FlowControl { } #[derive(Clone)] -pub(crate) struct Function { - pub description: Option<String>, - pub name: Identifier, - pub args: Vec<KeyBuf>, - pub statements: Vec<Statement>, +pub struct Function { + description: Option<String>, + name: Identifier, + args: Vec<KeyBuf>, + statements: Vec<Statement>, } pub(crate) enum FunctionError { @@ -136,6 +136,19 @@ pub(crate) enum FunctionError { } impl Function { + pub(crate) fn new( + description: Option<String>, + name: Identifier, + args: Vec<KeyBuf>, + statements: Vec<Statement> + ) -> Function { + Function { description, name, args, statements } + } + + pub(crate) fn get_description<'a>(&'a self) -> Option<&'a String> { + self.description.as_ref() + } + pub(crate) fn execute(self, shell: &mut Shell, args: &[&str]) -> Result<(), FunctionError> { if args.len() - 1 != self.args.len() { return Err(FunctionError::InvalidArgumentCount); diff --git a/src/shell/library.rs b/src/shell/library.rs new file mode 100644 index 0000000000000000000000000000000000000000..da1fb44ca1f818d373016fe534eaa12c4d33613c --- /dev/null +++ b/src/shell/library.rs @@ -0,0 +1,29 @@ +use super::{FlowLogic, Binary, Shell}; +use std::io::{self, Read}; +use std::fs::File; +use std::path::Path; + +pub trait IonLibrary { + /// Executes the given command and returns the exit status. + fn execute_command(&mut self, command: &str) -> i32; + /// Executes all of the statements contained within a given script, + /// returning the final exit status. + fn execute_script<P: AsRef<Path>>(&mut self, path: P) -> io::Result<i32>; +} + +impl<'a> IonLibrary for Shell<'a> { + fn execute_command(&mut self, command: &str) -> i32 { + self.on_command(command); + self.previous_status + } + + fn execute_script<P: AsRef<Path>>(&mut self, path: P) -> io::Result<i32> { + let path = path.as_ref(); + let mut file = File::open(path)?; + let capacity = file.metadata().ok().map_or(0, |x| x.len()); + let mut command_list = String::with_capacity(capacity as usize); + let _ = file.read_to_string(&mut command_list)?; + self.terminate_script_quotes(command_list.lines().map(|x| x.to_owned())); + Ok(self.previous_status) + } +} diff --git a/src/shell/mod.rs b/src/shell/mod.rs index ab97e46ed99efe56072a170f2efab9a2eeb8095e..d8ba3292d57b87e261c6479c415078b66ee653d3 100644 --- a/src/shell/mod.rs +++ b/src/shell/mod.rs @@ -5,14 +5,15 @@ mod flow; mod history; mod job; mod pipe_exec; -pub mod colors; -pub mod directory_stack; +pub(crate) mod colors; +pub(crate) mod directory_stack; pub mod flags; -pub mod plugins; -pub mod flow_control; -pub mod signals; +pub(crate) mod plugins; +pub(crate) mod flow_control; +pub(crate) mod signals; pub mod status; pub mod variables; +pub mod library; pub(crate) use self::binary::Binary; pub(crate) use self::flow::FlowLogic; @@ -25,6 +26,7 @@ use self::flags::*; use self::flow_control::{FlowControl, Function, FunctionError}; use self::foreground::ForegroundSignals; use self::job_control::{BackgroundProcess, JobControl}; +use self::library::IonLibrary; use self::pipe_exec::PipelineExecution; use self::status::*; use self::variables::Variables; @@ -49,7 +51,7 @@ use types::*; /// the entirety of the /// program. It is initialized at the beginning of the program, and lives until the end of the /// program. -pub(crate) struct Shell<'a> { +pub struct Shell<'a> { /// Contains a list of built-in commands that were created when the program started. pub builtins: &'a FnvHashMap<&'static str, Builtin>, /// Contains the history, completions, and manages writes to the history file. @@ -80,7 +82,7 @@ pub(crate) struct Shell<'a> { pub break_flow: bool, /// When the `fg` command is run, this will be used to communicate with the specified /// background process. - pub foreground_signals: Arc<ForegroundSignals>, + foreground_signals: Arc<ForegroundSignals>, /// Stores the patterns used to determine whether a command should be saved in the history /// or not ignore_setting: IgnoreSetting, @@ -88,7 +90,7 @@ pub(crate) struct Shell<'a> { impl<'a> Shell<'a> { /// Panics if DirectoryStack construction fails - pub(crate) fn new(builtins: &'a FnvHashMap<&'static str, Builtin>) -> Shell<'a> { + pub fn new(builtins: &'a FnvHashMap<&'static str, Builtin>) -> Shell<'a> { Shell { builtins: builtins, context: None, @@ -147,7 +149,7 @@ impl<'a> Shell<'a> { } /// Evaluates the source init file in the user's home directory. - pub(crate) fn evaluate_init_file(&mut self) { + pub fn evaluate_init_file(&mut self) { match app_root( AppDataType::UserConfig, &AppInfo { @@ -158,7 +160,9 @@ impl<'a> Shell<'a> { Ok(mut initrc) => { initrc.push("initrc"); if initrc.exists() { - self.execute_script(&initrc); + if let Err(err) = self.execute_script(&initrc) { + eprintln!("ion: {}", err); + } } else { eprintln!("ion: creating initrc file at {:?}", initrc); if let Err(why) = File::create(initrc) { diff --git a/src/shell/pipe_exec/job_control.rs b/src/shell/pipe_exec/job_control.rs index b0679124b265df32d8073a2c677c5e9b91f62efc..55b8f80eaf90d2731e2bf1b316506c0f427eba4d 100644 --- a/src/shell/pipe_exec/job_control.rs +++ b/src/shell/pipe_exec/job_control.rs @@ -44,7 +44,7 @@ pub(crate) trait JobControl { #[derive(Clone, Copy, Debug, PartialEq)] /// Defines whether the background process is running or stopped. -pub(crate) enum ProcessState { +pub enum ProcessState { Running, Stopped, Empty, @@ -95,7 +95,7 @@ pub(crate) fn add_to_background( /// by the shell. The shell will only retain information about the process, such /// as the process ID, state that the process is in, and the command that the /// process is executing. -pub(crate) struct BackgroundProcess { +pub struct BackgroundProcess { pub pid: u32, pub ignore_sighup: bool, pub state: ProcessState, diff --git a/src/shell/status.rs b/src/shell/status.rs index 64715262732b7e73c1cf556e0b1c0169d51e3f2e..65d0b1ef980c9dc25fbbd08d9fb91aa77e1b6c90 100644 --- a/src/shell/status.rs +++ b/src/shell/status.rs @@ -1,8 +1,8 @@ -pub(crate) const SUCCESS: i32 = 0; -pub(crate) const FAILURE: i32 = 1; -pub(crate) const BAD_ARG: i32 = 2; -pub(crate) const COULD_NOT_EXEC: i32 = 126; -pub(crate) const NO_SUCH_COMMAND: i32 = 127; -pub(crate) const TERMINATED: i32 = 143; +pub const SUCCESS: i32 = 0; +pub const FAILURE: i32 = 1; +pub const BAD_ARG: i32 = 2; +pub const COULD_NOT_EXEC: i32 = 126; +pub const NO_SUCH_COMMAND: i32 = 127; +pub const TERMINATED: i32 = 143; -pub(crate) fn get_signal_code(signal: i32) -> i32 { 128 + signal } +pub fn get_signal_code(signal: i32) -> i32 { 128 + signal } diff --git a/src/shell/variables/mod.rs b/src/shell/variables/mod.rs index e641c2e31865b1623a058c305b28d613a720f847..fe82abfda305ffd1bf6ac5a956700b5571fe3b07 100644 --- a/src/shell/variables/mod.rs +++ b/src/shell/variables/mod.rs @@ -22,7 +22,7 @@ lazy_static! { } #[derive(Debug)] -pub(crate) struct Variables { +pub struct Variables { pub hashmaps: HashMapVariableContext, pub arrays: ArrayVariableContext, pub variables: VariableContext, @@ -111,7 +111,7 @@ impl Variables { SUCCESS } - pub(crate) fn set_var(&mut self, name: &str, value: &str) { + pub fn set_var(&mut self, name: &str, value: &str) { if name == "NS_PLUGINS" { match value { "0" => self.disable_plugins(), @@ -132,7 +132,7 @@ impl Variables { } } - pub(crate) fn set_array(&mut self, name: &str, value: Array) { + pub fn set_array(&mut self, name: &str, value: Array) { if !name.is_empty() { if value.is_empty() { self.arrays.remove(name); @@ -156,11 +156,11 @@ impl Variables { } } - pub(crate) fn get_map(&self, name: &str) -> Option<&HashMap> { self.hashmaps.get(name) } + pub fn get_map(&self, name: &str) -> Option<&HashMap> { self.hashmaps.get(name) } - pub(crate) fn get_array(&self, name: &str) -> Option<&Array> { self.arrays.get(name) } + pub fn get_array(&self, name: &str) -> Option<&Array> { self.arrays.get(name) } - pub(crate) fn unset_array(&mut self, name: &str) -> Option<Array> { self.arrays.remove(name) } + pub fn unset_array(&mut self, name: &str) -> Option<Array> { self.arrays.remove(name) } /// Obtains the value for the **SWD** variable. /// @@ -202,7 +202,7 @@ impl Variables { swd } - pub(crate) fn get_var(&self, name: &str) -> Option<Value> { + pub fn get_var(&self, name: &str) -> Option<Value> { match name { "SWD" => return Some(self.get_simplified_directory()), "MWD" => return Some(self.get_minimal_directory()), @@ -253,11 +253,11 @@ impl Variables { } } - pub(crate) fn get_var_or_empty(&self, name: &str) -> Value { self.get_var(name).unwrap_or_default() } + pub fn get_var_or_empty(&self, name: &str) -> Value { self.get_var(name).unwrap_or_default() } - pub(crate) fn unset_var(&mut self, name: &str) -> Option<Value> { self.variables.remove(name) } + pub fn unset_var(&mut self, name: &str) -> Option<Value> { self.variables.remove(name) } - pub(crate) fn get_vars(&self) -> Vec<Identifier> { + pub fn get_vars(&self) -> Vec<Identifier> { self.variables.keys().cloned().chain(env::vars().map(|(k, _)| k.into())).collect() }