diff --git a/src/lib/builtins/functions.rs b/src/lib/builtins/functions.rs index 7b59e4354cfbeec33eaa6759b6653a314f1f2056..db6943705cf7fa95ea7b31de29b5a1e53882de1b 100644 --- a/src/lib/builtins/functions.rs +++ b/src/lib/builtins/functions.rs @@ -1,7 +1,7 @@ use crate::shell::{status::*, variables::Variables}; use std::io::{self, Write}; -fn print_functions(vars: &Variables) { +pub fn print_functions(vars: &Variables) -> i32 { let stdout = io::stdout(); let stdout = &mut stdout.lock(); let _ = writeln!(stdout, "# Functions"); @@ -13,9 +13,5 @@ fn print_functions(vars: &Variables) { let _ = writeln!(stdout, " {}", fn_name); } } -} - -pub(crate) fn fn_(vars: &mut Variables) -> i32 { - print_functions(vars); SUCCESS } diff --git a/src/lib/builtins/job_control.rs b/src/lib/builtins/job_control.rs index 1489ff69090817b6eace04592f1d9a68224c727b..0024175f54650f1b02bd9b8569f707cfb81be0ad 100644 --- a/src/lib/builtins/job_control.rs +++ b/src/lib/builtins/job_control.rs @@ -1,12 +1,7 @@ //! Contains the `jobs`, `disown`, `bg`, and `fg` commands that manage job //! control in the shell. -use crate::shell::{ - job_control::{JobControl, ProcessState}, - signals, - status::*, - Shell, -}; +use crate::shell::{signals, status::*, ProcessState, Shell}; use small; use smallvec::SmallVec; @@ -144,7 +139,7 @@ pub(crate) fn fg(shell: &mut Shell, args: &[small::String]) -> i32 { /// Resumes a stopped background process, if it was stopped. pub(crate) fn bg(shell: &mut Shell, args: &[small::String]) -> i32 { fn bg_job(shell: &mut Shell, njob: u32) -> bool { - if let Some(job) = shell.background.lock().unwrap().iter_mut().nth(njob as usize) { + if let Some(job) = shell.background.lock().unwrap().iter().nth(njob as usize) { match job.state { ProcessState::Running => { eprintln!("ion: bg: job {} is already running", njob); diff --git a/src/lib/builtins/man_pages.rs b/src/lib/builtins/man_pages.rs index 50eb176086e02374b0fdf6eb4156ec788deaed2a..5c8bf09b901c836b5b1938a9e5b6b7d11b4cf387 100644 --- a/src/lib/builtins/man_pages.rs +++ b/src/lib/builtins/man_pages.rs @@ -1,6 +1,6 @@ use small; -pub(crate) fn check_help(args: &[small::String], man_page: &'static str) -> bool { +pub fn check_help(args: &[small::String], man_page: &'static str) -> bool { for arg in args { if arg == "-h" || arg == "--help" { println!("{}", man_page); @@ -10,7 +10,7 @@ pub(crate) fn check_help(args: &[small::String], man_page: &'static str) -> bool false } -pub(crate) const MAN_STATUS: &str = r#"NAME +pub const MAN_STATUS: &str = r#"NAME status - Evaluates the current runtime status SYNOPSIS @@ -27,7 +27,7 @@ OPTIONS -f prints the filename of the currently running script or else stdio. Also --current-filename."#; -pub(crate) const MAN_CD: &str = r#"NAME +pub const MAN_CD: &str = r#"NAME cd - Change directory. SYNOPSIS @@ -39,7 +39,7 @@ DESCRIPTION With arguments cd changes the working directory to the directory you provided. "#; -pub(crate) const MAN_BOOL: &str = r#"NAME +pub const MAN_BOOL: &str = r#"NAME bool - Returns true if the value given to it is equal to '1' or 'true'. SYNOPSIS @@ -48,7 +48,7 @@ SYNOPSIS DESCRIPTION Returns true if the value given to it is equal to '1' or 'true'."#; -pub(crate) const MAN_IS: &str = r#"NAME +pub const MAN_IS: &str = r#"NAME is - Checks if two arguments are the same SYNOPSIS @@ -61,7 +61,7 @@ OPTIONS not returns 0 if the two arguments are not equal."#; -pub(crate) const MAN_ISATTY: &str = r#" +pub const MAN_ISATTY: &str = r#" isatty - Checks if argument is a file descriptor SYNOPSIS @@ -70,7 +70,7 @@ SYNOPSIS DESCRIPTION Returns 0 exit status if the supplied file descriptor is a tty."#; -pub(crate) const MAN_DIRS: &str = r#"NAME +pub const MAN_DIRS: &str = r#"NAME dirs - prints the directory stack SYNOPSIS @@ -79,7 +79,7 @@ SYNOPSIS DESCRIPTION dirs prints the current directory stack."#; -pub(crate) const MAN_PUSHD: &str = r#"NAME +pub const MAN_PUSHD: &str = r#"NAME pushd - push a directory to the directory stack SYNOPSIS @@ -88,7 +88,7 @@ SYNOPSIS DESCRIPTION pushd pushes a directory to the directory stack."#; -pub(crate) const MAN_POPD: &str = r#"NAME +pub const MAN_POPD: &str = r#"NAME popd - shift through the directory stack SYNOPSIS @@ -98,7 +98,7 @@ DESCRIPTION popd removes the top directory from the directory stack and changes the working directory to the new top directory. pushd adds directories to the stack."#; -// pub(crate) const MAN_FN: &str = r#"NAME +// pub const MAN_FN: &str = r#"NAME // fn - print a list of all functions or create a function // // SYNOPSIS @@ -126,7 +126,7 @@ DESCRIPTION // example 1 //"#; -pub(crate) const MAN_READ: &str = r#"NAME +pub const MAN_READ: &str = r#"NAME read - read a line of input into some variables SYNOPSIS @@ -135,7 +135,7 @@ SYNOPSIS DESCRIPTION For each variable reads from standard input and stores the results in the variable."#; -pub(crate) const MAN_DROP: &str = r#"NAME +pub const MAN_DROP: &str = r#"NAME drop - delete some variables or arrays SYNOPSIS @@ -149,7 +149,7 @@ OPTIONS -a Instead of deleting variables deletes arrays."#; -pub(crate) const MAN_SET: &str = r#"NAME +pub const MAN_SET: &str = r#"NAME set - Set or unset values of shell options and positional parameters. SYNOPSIS @@ -172,7 +172,7 @@ OPTIONS - Following arguments will be set as positional arguments in the shell. If no arguments are suppled, arguments will not be unset."#; -pub(crate) const MAN_EQ: &str = r#"NAME +pub const MAN_EQ: &str = r#"NAME eq - Checks if two arguments are the same SYNOPSIS @@ -185,7 +185,7 @@ OPTIONS not returns 0 if the two arguments are not equal."#; -pub(crate) const MAN_EVAL: &str = r#"NAME +pub const MAN_EVAL: &str = r#"NAME eval - evaluates the specified commands SYNOPSIS @@ -195,7 +195,7 @@ DESCRIPTION eval evaluates the given arguments as a command. If more than one argument is given, all arguments are joined using a space as a separator."#; -pub(crate) const MAN_EXEC: &str = r#"NAME +pub const MAN_EXEC: &str = r#"NAME exec - Replace the shell with the given command. SYNOPSIS @@ -209,7 +209,7 @@ DESCRIPTION OPTIONS -c Execute command with an empty environment."#; -pub(crate) const MAN_HISTORY: &str = r#"NAME +pub const MAN_HISTORY: &str = r#"NAME history - print command history SYNOPSIS @@ -218,7 +218,7 @@ SYNOPSIS DESCRIPTION Prints the command history."#; -pub(crate) const MAN_SOURCE: &str = r#"NAME +pub const MAN_SOURCE: &str = r#"NAME source - evaluates given file SYNOPSIS @@ -228,7 +228,7 @@ DESCRIPTION Evaluates the commands in a specified file in the current shell. All changes in shell variables will affect the current shell because of this."#; -pub(crate) const MAN_ECHO: &str = r#"NAME +pub const MAN_ECHO: &str = r#"NAME echo - display a line of text SYNOPSIS @@ -258,7 +258,7 @@ OPTIONS \t horizontal tab (HT) \v vertical tab (VT)"#; -pub(crate) const MAN_RANDOM: &str = r#"NAME +pub const MAN_RANDOM: &str = r#"NAME random - generate a random number SYNOPSIS @@ -270,7 +270,7 @@ DESCRIPTION The range depends on what arguments you pass. If no arguments are given the range is [0, 32767]. If two arguments are given the range is [START, END]."#; -pub(crate) const MAN_TRUE: &str = r#"NAME +pub const MAN_TRUE: &str = r#"NAME true - does nothing successfully SYNOPSIS @@ -279,7 +279,7 @@ SYNOPSIS DESCRIPTION Sets the exit status to 0."#; -pub(crate) const MAN_FALSE: &str = r#"NAME +pub const MAN_FALSE: &str = r#"NAME false - does nothing unsuccessfully SYNOPSIS @@ -288,7 +288,7 @@ SYNOPSIS DESCRIPTION Sets the exit status to 1."#; -pub(crate) const MAN_JOBS: &str = r#"NAME +pub const MAN_JOBS: &str = r#"NAME jobs - list all jobs running in the background SYNOPSIS @@ -297,7 +297,7 @@ SYNOPSIS DESCRIPTION Prints a list of all jobs running in the background."#; -pub(crate) const MAN_BG: &str = r#"NAME +pub const MAN_BG: &str = r#"NAME bg - sends jobs to background SYNOPSIS @@ -306,7 +306,7 @@ SYNOPSIS DESCRIPTION bg sends the job to the background resuming it if it has stopped."#; -pub(crate) const MAN_FG: &str = r#"NAME +pub const MAN_FG: &str = r#"NAME fg - bring job to foreground SYNOPSIS @@ -315,7 +315,7 @@ SYNOPSIS DESCRIPTION fg brings the specified job to foreground resuming it if it has stopped."#; -pub(crate) const MAN_SUSPEND: &str = r#"NAME +pub const MAN_SUSPEND: &str = r#"NAME suspend - suspend the current shell SYNOPSIS @@ -325,7 +325,7 @@ DESCRIPTION Suspends the current shell by sending it the SIGTSTP signal, returning to the parent process. It can be resumed by sending it SIGCONT."#; -pub(crate) const MAN_DISOWN: &str = r#"NAME +pub const MAN_DISOWN: &str = r#"NAME disown - Disown processes SYNOPSIS @@ -339,7 +339,7 @@ OPTIONS -h Specifies that each job supplied will not receive the SIGHUP signal when the shell receives a SIGHUP. -a If no job IDs were supplied, remove all jobs from the background process list."#; -pub(crate) const MAN_EXIT: &str = r#"NAME +pub const MAN_EXIT: &str = r#"NAME exit - exit the shell SYNOPSIS @@ -348,7 +348,7 @@ SYNOPSIS DESCRIPTION Makes ion exit. The exit status will be that of the last command executed."#; -pub(crate) const MAN_MATCHES: &str = r#"NAME +pub const MAN_MATCHES: &str = r#"NAME matches - checks if the second argument contains any portion of the first. SYNOPSIS @@ -364,7 +364,7 @@ EXAMPLES Returns false: matches x xs"#; -pub(crate) const MAN_EXISTS: &str = r#"NAME +pub const MAN_EXISTS: &str = r#"NAME exists - check whether items exist SYNOPSIS @@ -420,7 +420,7 @@ AUTHOR Written by Fabian Würfl. Heavily based on implementation of the test builtin, which was written by Michael Murph."#; -pub(crate) const MAN_WHICH: &str = r#"NAME +pub const MAN_WHICH: &str = r#"NAME which - locate a program file in the current user's path SYNOPSIS diff --git a/src/lib/builtins/mod.rs b/src/lib/builtins/mod.rs index 7131fa8471006872a9f482e247af3c2d27c6e8e5..1397b4b74beef03786af34cc9adb949958e033bd 100644 --- a/src/lib/builtins/mod.rs +++ b/src/lib/builtins/mod.rs @@ -1,15 +1,15 @@ -pub mod functions; pub mod man_pages; -pub mod source; -pub mod variables; mod command_info; mod exec; mod exists; +mod functions; mod is; mod job_control; mod set; +mod source; mod status; +mod variables; use ion_builtins::{calc, conditionals, echo, random, test}; @@ -18,7 +18,7 @@ use self::{ echo::echo, exec::exec, exists::exists, - functions::fn_, + functions::print_functions, is::is, man_pages::*, source::source, @@ -36,13 +36,7 @@ use hashbrown::HashMap; use liner::Context; use crate::{ - shell::{ - self, - fork_function::fork_function, - job_control::{JobControl, ProcessState}, - status::*, - Shell, - }, + shell::{self, status::*, ProcessState, Shell}, sys, types, }; use small; @@ -72,7 +66,7 @@ macro_rules! map { /// Note: To reduce allocations, function are provided as pointer rather than boxed closures /// ``` /// use ion_shell::builtins::BuiltinMap; -/// use ion_shell::shell::Shell; +/// use ion_shell::Shell; /// /// // create a builtin /// let mut custom = |_args: &[small::String], _shell: &mut Shell| { @@ -262,7 +256,7 @@ pub fn builtin_cd(args: &[small::String], shell: &mut Shell) -> i32 { match shell.directory_stack.cd(args, &shell.variables) { Ok(()) => { - let _ = fork_function(shell, "CD_CHANGE", &["ion"]); + let _ = shell.fork_function("CD_CHANGE", &["ion"]); SUCCESS } Err(why) => { @@ -362,7 +356,7 @@ fn builtin_unalias(args: &[small::String], shell: &mut Shell) -> i32 { // TODO There is a man page for fn however the -h and --help flags are not // checked for. -fn builtin_fn(_: &[small::String], shell: &mut Shell) -> i32 { fn_(&mut shell.variables) } +fn builtin_fn(_: &[small::String], shell: &mut Shell) -> i32 { print_functions(&shell.variables) } fn builtin_read(args: &[small::String], shell: &mut Shell) -> i32 { if check_help(args, MAN_READ) { diff --git a/src/lib/builtins/source.rs b/src/lib/builtins/source.rs index 6de7be31f516d9d35e6267862d347cf3bddfcf06..7319fe4f6a4f063c5775da6be36735eb0fad7bb3 100644 --- a/src/lib/builtins/source.rs +++ b/src/lib/builtins/source.rs @@ -3,7 +3,7 @@ use small; use std::fs::File; /// Evaluates the given file and returns 'SUCCESS' if it succeeds. -pub(crate) fn source(shell: &mut Shell, arguments: &[small::String]) -> Result<(), String> { +pub fn source(shell: &mut Shell, arguments: &[small::String]) -> Result<(), String> { match arguments.get(1) { Some(argument) => { if let Ok(file) = File::open(argument.as_str()) { diff --git a/src/lib/builtins/variables.rs b/src/lib/builtins/variables.rs index d2010896569741c26b4cada83f0a808e2192b05f..1df4a2853ad5f128cc70131349ae41425676fa16 100644 --- a/src/lib/builtins/variables.rs +++ b/src/lib/builtins/variables.rs @@ -69,7 +69,7 @@ fn parse_alias(args: &str) -> Binding { /// The `alias` command will define an alias for another command, and thus may be used as a /// command itself. -pub(crate) fn alias(vars: &mut Variables, args: &str) -> i32 { +pub fn alias(vars: &mut Variables, args: &str) -> i32 { match parse_alias(args) { Binding::InvalidKey(key) => { eprintln!("ion: alias name, '{}', is invalid", key); @@ -88,7 +88,7 @@ pub(crate) fn alias(vars: &mut Variables, args: &str) -> i32 { } /// Dropping an alias will erase it from the shell. -pub(crate) fn drop_alias<S: AsRef<str>>(vars: &mut Variables, args: &[S]) -> i32 { +pub fn drop_alias<S: AsRef<str>>(vars: &mut Variables, args: &[S]) -> i32 { if args.len() <= 1 { eprintln!("ion: you must specify an alias name"); return FAILURE; @@ -103,7 +103,7 @@ pub(crate) fn drop_alias<S: AsRef<str>>(vars: &mut Variables, args: &[S]) -> i32 } /// Dropping an array will erase it from the shell. -pub(crate) fn drop_array<S: AsRef<str>>(vars: &mut Variables, args: &[S]) -> i32 { +pub fn drop_array<S: AsRef<str>>(vars: &mut Variables, args: &[S]) -> i32 { if args.len() <= 2 { eprintln!("ion: you must specify an array name"); return FAILURE; @@ -124,7 +124,7 @@ pub(crate) fn drop_array<S: AsRef<str>>(vars: &mut Variables, args: &[S]) -> i32 } /// Dropping a variable will erase it from the shell. -pub(crate) fn drop_variable<S: AsRef<str>>(vars: &mut Variables, args: &[S]) -> i32 { +pub fn drop_variable<S: AsRef<str>>(vars: &mut Variables, args: &[S]) -> i32 { if args.len() <= 1 { eprintln!("ion: you must specify a variable name"); return FAILURE; diff --git a/src/lib/lib.rs b/src/lib/lib.rs index 5c18f2acd3e876ea881297b1f00efe4afebb1ec6..2cda3cffd611083d70c54d7233340821c8286e5b 100644 --- a/src/lib/lib.rs +++ b/src/lib/lib.rs @@ -13,10 +13,7 @@ pub mod types; pub mod parser; pub mod builtins; mod memory; -pub mod shell; +mod shell; pub(crate) use self::memory::IonPool; -pub use crate::shell::{ - binary::MAN_ION, pipe_exec::job_control::JobControl, status, Capture, Fork, InteractiveBinary, - IonError, IonResult, Shell, -}; +pub use crate::shell::*; diff --git a/src/lib/parser/mod.rs b/src/lib/parser/mod.rs index 4681047bbd307c28054983afd9a81cf3b91d380c..9f352bdc244734de610a163d76e1bf5af41dd0ce 100644 --- a/src/lib/parser/mod.rs +++ b/src/lib/parser/mod.rs @@ -3,15 +3,16 @@ mod loops; pub(crate) mod pipelines; mod quotes; pub(crate) mod shell_expand; -pub mod statement; +mod statement; -pub use self::statement::StatementSplitter; - -pub use self::quotes::Terminator; pub(crate) use self::{ loops::ForValueExpression, - shell_expand::{expand_string, Expander, Select}, - statement::parse_and_validate, + shell_expand::{expand_string, Select}, +}; +pub use self::{ + quotes::Terminator, + shell_expand::Expander, + statement::{is_valid_name, parse_and_validate, StatementSplitter}, }; #[cfg(fuzzing)] diff --git a/src/lib/parser/shell_expand/mod.rs b/src/lib/parser/shell_expand/mod.rs index 97d495cc86768b0a32a75af13426ee3cd386b75d..f9ec1b7234fd66c70c822e0e4705d6dc46d254ec 100644 --- a/src/lib/parser/shell_expand/mod.rs +++ b/src/lib/parser/shell_expand/mod.rs @@ -38,7 +38,7 @@ fn join_with_spaces<S: AsRef<str>>(input: &mut small::String, mut iter: impl Ite // TODO: Make array expansions iterators instead of arrays. // TODO: Use Cow<'a, types::Str> for hashmap values. /// Trait representing different elements of string expansion -pub(crate) trait Expander: Sized { +pub trait Expander: Sized { /// Expand a tilde form to the correct directory. fn tilde(&self, _input: &str) -> Option<String> { None } /// Expand an array variable with some selection. diff --git a/src/lib/parser/statement/mod.rs b/src/lib/parser/statement/mod.rs index 70cad1cf662125fa66c061c4e59909607c768498..da3203f30e8516fa26d0cd82502c4cf6a7758cea 100644 --- a/src/lib/parser/statement/mod.rs +++ b/src/lib/parser/statement/mod.rs @@ -1,18 +1,17 @@ mod case; mod functions; -#[cfg(not(fuzzing))] -pub mod parse; -#[cfg(fuzzing)] -pub mod parse; +mod parse; mod splitter; -pub(crate) use self::parse::parse; -pub use self::splitter::{StatementError, StatementSplitter, StatementVariant}; +pub use self::{ + parse::{is_valid_name, parse}, + splitter::{StatementError, StatementSplitter, StatementVariant}, +}; use crate::{builtins::BuiltinMap, shell::flow_control::Statement}; /// Parses a given statement string and return's the corresponding mapped /// `Statement` -pub(crate) fn parse_and_validate<'b>( +pub fn parse_and_validate<'b>( statement: Result<StatementVariant, StatementError>, builtins: &BuiltinMap<'b>, ) -> Statement<'b> { diff --git a/src/lib/parser/statement/parse.rs b/src/lib/parser/statement/parse.rs index aaf4f48e330eae71efe076b4f2f09cd3a4e2a7d6..5fc25d0b9e242b574714997d6fd60f6807c5bd92 100644 --- a/src/lib/parser/statement/parse.rs +++ b/src/lib/parser/statement/parse.rs @@ -20,7 +20,7 @@ pub fn is_valid_name(name: &str) -> bool { && chars.all(|b| b.is_alphanumeric() || b == '_') } -pub(crate) fn parse<'a>(code: &str, builtins: &BuiltinMap<'a>) -> Statement<'a> { +pub fn parse<'a>(code: &str, builtins: &BuiltinMap<'a>) -> Statement<'a> { let cmd = code.trim(); match cmd { "end" => Statement::End, diff --git a/src/lib/shell/assignments.rs b/src/lib/shell/assignments.rs index 47520dc442100e688a4d736dc75193e39c0f5b63..a6cc5003cca91e469aa25e0cd7a7fd8ae843a6c2 100644 --- a/src/lib/shell/assignments.rs +++ b/src/lib/shell/assignments.rs @@ -5,7 +5,7 @@ use super::{ }; use crate::{ lexers::assignments::{Key, Operator, Primitive}, - parser::{assignments::*, statement::parse::is_valid_name}, + parser::{assignments::*, is_valid_name}, shell::variables::{EuclDiv, Modifications, OpError, Pow, Value}, types, }; diff --git a/src/lib/shell/binary/mod.rs b/src/lib/shell/binary/mod.rs index f4c2b9418dfbb425a1b90f73f9699284b59af144..5248b7e1d2a4414fe8ad92d6c19e76cbb3cf2b3f 100644 --- a/src/lib/shell/binary/mod.rs +++ b/src/lib/shell/binary/mod.rs @@ -6,9 +6,8 @@ mod readln; use self::{history::ShellHistory, prompt::prompt, readln::readln}; use super::{ - pipe_exec::job_control::JobControl, status::{FAILURE, SUCCESS}, - FlowLogic, Shell, + Shell, }; use crate::{ builtins::man_pages, diff --git a/src/lib/shell/flow.rs b/src/lib/shell/flow.rs index ea18cf71bed4723bc2c1caf20eb1ec7b4c6cd496..c6cadbdb1c5e5cf2eef7dca35dba60fae0a9a74a 100644 --- a/src/lib/shell/flow.rs +++ b/src/lib/shell/flow.rs @@ -1,6 +1,5 @@ use super::{ flow_control::{insert_statement, Case, ElseIf, Function, Statement}, - job_control::JobControl, signals, status::*, Shell, @@ -19,56 +18,16 @@ use itertools::Itertools; use small; #[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)] -pub(crate) enum Condition { +pub enum Condition { Continue, Break, NoOp, SigInt, } -pub(crate) trait FlowLogic<'a> { - /// Receives a command and attempts to execute the contents. - fn on_command(&mut self, command_string: &str); - - /// Executes all of the statements within a while block until a certain - /// condition is met. - fn execute_while( - &mut self, - expression: &[Statement<'a>], - statements: &[Statement<'a>], - ) -> Condition; - - /// Executes all of the statements within a for block for each value - /// specified in the range. - fn execute_for( - &mut self, - variables: &[types::Str], - values: &[small::String], - statements: &[Statement<'a>], - ) -> Condition; - +impl<'a> Shell<'a> { /// Conditionally executes branches of statements according to evaluated /// expressions - fn execute_if( - &mut self, - expression: &[Statement<'a>], - success: &[Statement<'a>], - else_if: &[ElseIf<'a>], - failure: &[Statement<'a>], - ) -> Condition; - - /// Simply executes all supplied statements. - fn execute_statements(&mut self, statements: &[Statement<'a>]) -> Condition; - - /// Executes a single statement - fn execute_statement(&mut self, statement: &Statement<'a>) -> Condition; - - /// Expand an expression and run a branch based on the value of the - /// expanded expression - fn execute_match<T: AsRef<str>>(&mut self, expression: T, cases: &[Case<'a>]) -> Condition; -} - -impl<'a> FlowLogic<'a> for Shell<'a> { fn execute_if( &mut self, expression: &[Statement<'a>], @@ -98,6 +57,8 @@ impl<'a> FlowLogic<'a> for Shell<'a> { self.execute_statements(&failure) } + /// Executes all of the statements within a for block for each value + /// specified in the range. fn execute_for( &mut self, variables: &[types::Str], @@ -148,6 +109,8 @@ impl<'a> FlowLogic<'a> for Shell<'a> { Condition::NoOp } + /// Executes all of the statements within a while block until a certain + /// condition is met. fn execute_while( &mut self, expression: &[Statement<'a>], @@ -168,7 +131,8 @@ impl<'a> FlowLogic<'a> for Shell<'a> { } } - fn execute_statement(&mut self, statement: &Statement<'a>) -> Condition { + /// Executes a single statement + pub fn execute_statement(&mut self, statement: &Statement<'a>) -> Condition { match statement { Statement::Error(number) => { self.previous_status = *number; @@ -305,7 +269,8 @@ impl<'a> FlowLogic<'a> for Shell<'a> { } } - fn execute_statements(&mut self, statements: &[Statement<'a>]) -> Condition { + /// Simply executes all supplied statements. + pub fn execute_statements(&mut self, statements: &[Statement<'a>]) -> Condition { self.variables.new_scope(false); let condition = statements @@ -319,6 +284,8 @@ impl<'a> FlowLogic<'a> for Shell<'a> { condition } + /// Expand an expression and run a branch based on the value of the + /// expanded expression fn execute_match<T: AsRef<str>>(&mut self, expression: T, cases: &[Case<'a>]) -> Condition { // Logic for determining if the LHS of a match-case construct (the value we are // matching against) matches the RHS of a match-case construct (a value @@ -380,7 +347,8 @@ impl<'a> FlowLogic<'a> for Shell<'a> { Condition::NoOp } - fn on_command(&mut self, command_string: &str) { + /// Receives a command and attempts to execute the contents. + pub fn on_command(&mut self, command_string: &str) { self.break_flow = false; for stmt in command_string.bytes().batching(|cmd| Terminator::new(cmd).terminate()) { // Go through all of the statements and build up the block stack diff --git a/src/lib/shell/flow_control.rs b/src/lib/shell/flow_control.rs index 8cadd85044b29f1839dbf34259a8f8cc2819ded3..62b57250a8fcd9edcc7326d8d9878eed2262ba6d 100644 --- a/src/lib/shell/flow_control.rs +++ b/src/lib/shell/flow_control.rs @@ -1,7 +1,7 @@ use crate::{ lexers::assignments::{KeyBuf, Operator, Primitive}, parser::{assignments::*, pipelines::Pipeline}, - shell::{flow::FlowLogic, Shell}, + shell::Shell, types, }; use small; @@ -9,7 +9,7 @@ use smallvec::SmallVec; use std::fmt::{self, Display, Formatter}; #[derive(Debug, PartialEq, Clone)] -pub(crate) struct ElseIf<'a> { +pub struct ElseIf<'a> { pub expression: Vec<Statement<'a>>, pub success: Vec<Statement<'a>>, } @@ -42,7 +42,7 @@ pub(crate) struct ElseIf<'a> { /// Case { value: None, ... } /// ``` #[derive(Debug, PartialEq, Clone)] -pub(crate) struct Case<'a> { +pub struct Case<'a> { pub value: Option<String>, pub binding: Option<String>, pub conditional: Option<String>, @@ -50,13 +50,13 @@ pub(crate) struct Case<'a> { } #[derive(Debug, PartialEq, Clone)] -pub(crate) enum LocalAction { +pub enum LocalAction { List, Assign(String, Operator, String), } #[derive(Debug, PartialEq, Clone)] -pub(crate) enum ExportAction { +pub enum ExportAction { List, LocalExport(String), Assign(String, Operator, String), @@ -64,7 +64,7 @@ pub(crate) enum ExportAction { // TODO: Enable statements and expressions to contain &str values. #[derive(Debug, PartialEq, Clone)] -pub(crate) enum Statement<'a> { +pub enum Statement<'a> { Let(LocalAction), Case(Case<'a>), Export(ExportAction), diff --git a/src/lib/shell/fork_function.rs b/src/lib/shell/fork_function.rs index 68713014e8a32f0ad397e93fb4052d1b7eccd458..d20bbdec9bd486f24f7e68fb3f33f5556319b217 100644 --- a/src/lib/shell/fork_function.rs +++ b/src/lib/shell/fork_function.rs @@ -4,32 +4,25 @@ use crate::{ }; use std::process; -#[inline] -pub(crate) fn command_not_found(shell: &mut Shell, command: &str) -> Result<(), ()> { - 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<S: AsRef<str>>( - shell: &mut Shell, - fn_name: &str, - args: &[S], -) -> Result<(), ()> { - if let Some(Value::Function(function)) = shell.variables.get_ref(fn_name) { - if let Err(err) = shell.fork(Capture::None, move |child| { - if let Err(err) = function.execute(child, args) { - eprintln!("ion: {} function call: {}", fn_name, err); +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>>(&mut self, fn_name: &str, args: &[S]) -> Result<(), ()> { + if let Some(Value::Function(function)) = self.variables.get_ref(fn_name) { + if let Err(err) = self.fork(Capture::None, move |child| { + if let Err(err) = function.execute(child, args) { + eprintln!("ion: {} function call: {}", fn_name, err); + } + }) { + eprintln!("ion: fork error: {}", err); + Err(()) + } else { + // Ensure that the parent retains ownership of the terminal before exiting. + let _ = sys::tcsetpgrp(sys::STDIN_FILENO, process::id()); + Ok(()) } - }) { - eprintln!("ion: fork error: {}", err); - Err(()) } else { - // Ensure that the parent retains ownership of the terminal before exiting. - let _ = sys::tcsetpgrp(sys::STDIN_FILENO, process::id()); - Ok(()) + Err(()) } - } else { - Err(()) } } diff --git a/src/lib/shell/mod.rs b/src/lib/shell/mod.rs index 22c18c48ffd1c60d9fe9699f17f042263d802c9d..409c11574520bf02f42d39d9f44a06e8435d5f2b 100644 --- a/src/lib/shell/mod.rs +++ b/src/lib/shell/mod.rs @@ -1,13 +1,13 @@ mod assignments; pub(crate) mod binary; -pub(crate) mod colors; +mod colors; mod completer; pub(crate) mod directory_stack; pub(crate) mod escape; mod flow; pub(crate) mod flow_control; mod fork; -pub mod fork_function; +mod fork_function; mod job; pub(crate) mod pipe_exec; pub(crate) mod signals; @@ -15,13 +15,11 @@ pub mod status; pub mod variables; pub use self::{ - binary::InteractiveBinary, - fork::{Capture, Fork, IonResult}, -}; -pub(crate) use self::{ - flow::FlowLogic, + binary::{InteractiveBinary, MAN_ION}, + fork::{Capture, IonResult}, job::Job, - pipe_exec::{foreground, job_control}, + pipe_exec::job_control::ProcessState, + variables::Value, }; use self::{ @@ -29,9 +27,10 @@ use self::{ escape::tilde, flow_control::{FlowControl, Function, FunctionError}, foreground::ForegroundSignals, - job_control::BackgroundProcess, + fork::Fork, + pipe_exec::{foreground, job_control::BackgroundProcess}, status::*, - variables::{GetVariable, Value, Variables}, + variables::{GetVariable, Variables}, }; use crate::{ builtins::BuiltinMap, diff --git a/src/lib/shell/pipe_exec/fork.rs b/src/lib/shell/pipe_exec/fork.rs index 9aeeffc63a0ba85be45712794f9c92710de04c5a..d45586245e2f82acbc58c6ea8cd2fd71553e6d03 100644 --- a/src/lib/shell/pipe_exec/fork.rs +++ b/src/lib/shell/pipe_exec/fork.rs @@ -5,7 +5,7 @@ pub(crate) fn create_process_group(pgid: u32) { let _ = sys::setpgid(0, pgid); } use super::{ super::{status::*, Shell}, - job_control::{JobControl, ProcessState}, + job_control::ProcessState, }; use crate::parser::pipelines::Pipeline; use std::process::exit; diff --git a/src/lib/shell/pipe_exec/job_control.rs b/src/lib/shell/pipe_exec/job_control.rs index e1fa6ed9aee3281726e5683f9c6a520f43db766f..b86b4972764561d1f8c5e4d8ffb3a6b4479de8c8 100644 --- a/src/lib/shell/pipe_exec/job_control.rs +++ b/src/lib/shell/pipe_exec/job_control.rs @@ -18,22 +18,6 @@ pub(crate) fn set_foreground_as(pid: u32) { signals::unblock(); } -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 - /// task - /// and sets it as the foreground process. Once the task exits or stops, the exit status - /// will - /// be returned, and ownership of the TTY given back to the shell. - fn set_bg_task_in_foreground(&self, pid: u32, cont: bool) -> i32; - fn resume_stopped(&mut self); - fn handle_signal(&self, signal: i32) -> bool; - fn background_send(&self, signal: i32); - fn watch_foreground<T: Into<String>>(&mut self, pid: i32, command: T) -> i32; - fn send_to_background(&mut self, child: u32, state: ProcessState, command: String); -} - #[derive(Clone, Copy, Debug, PartialEq)] /// Defines whether the background process is running or stopped. pub enum ProcessState { @@ -90,10 +74,10 @@ pub struct BackgroundProcess { pub name: String, } -impl<'a> JobControl for Shell<'a> { +impl<'a> Shell<'a> { /// If a SIGTERM is received, a SIGTERM will be sent to all background processes /// before the shell terminates itself. - fn handle_signal(&self, signal: i32) -> bool { + pub fn handle_signal(&self, signal: i32) -> bool { if signal == sys::SIGTERM || signal == sys::SIGHUP { self.background_send(signal); true @@ -102,7 +86,7 @@ impl<'a> JobControl for Shell<'a> { } } - fn send_to_background(&mut self, pid: u32, state: ProcessState, command: String) { + pub fn send_to_background(&mut self, pid: u32, state: ProcessState, command: String) { // Increment the `Arc` counters so that these fields can be moved into // the upcoming background thread. let processes = self.background.clone(); @@ -117,13 +101,11 @@ impl<'a> JobControl for Shell<'a> { // Spawn a background thread that will monitor the progress of the // background process, updating it's state changes until it finally // exits. - let _ = spawn(move || { - watch_background(&fg_signals, &processes, pid, njob as usize); - }); + let _ = spawn(move || watch_background(&fg_signals, &processes, pid, njob as usize)); } /// Send a kill signal to all running background tasks. - fn background_send(&self, signal: i32) { + pub fn background_send(&self, signal: i32) { if signal == sys::SIGHUP { for process in self.background.lock().unwrap().iter() { if !process.ignore_sighup { @@ -140,7 +122,7 @@ impl<'a> JobControl for Shell<'a> { } /// Resumes all stopped background jobs - fn resume_stopped(&mut self) { + pub fn resume_stopped(&mut self) { for process in self.background.lock().unwrap().iter() { if process.state == ProcessState::Stopped { signals::resume(process.pid); @@ -148,7 +130,7 @@ impl<'a> JobControl for Shell<'a> { } } - fn watch_foreground<T: Into<String>>(&mut self, pid: i32, command: T) -> i32 { + pub fn watch_foreground<T: Into<String>>(&mut self, pid: i32, command: T) -> i32 { let mut signaled = 0; let mut exit_status = 0; @@ -200,7 +182,7 @@ impl<'a> JobControl for Shell<'a> { /// Waits until all running background tasks have completed, and listens for signals in the /// event that a signal is sent to kill the running tasks. - fn wait_for_background(&mut self) { + pub fn wait_for_background(&mut self) { while self.background.lock().unwrap().iter().any(|p| p.state == ProcessState::Running) { if let Some(signal) = signals::SignalHandler.find(|&s| s != sys::SIGTSTP) { self.background_send(signal); @@ -210,7 +192,10 @@ impl<'a> JobControl for Shell<'a> { } } - fn set_bg_task_in_foreground(&self, pid: u32, cont: bool) -> i32 { + /// Takes a background tasks's PID and whether or not it needs to be continued; resumes the + /// task and sets it as the foreground process. Once the task exits or stops, the exit status + /// will be returned, and ownership of the TTY given back to the shell. + pub fn set_bg_task_in_foreground(&self, pid: u32, cont: bool) -> i32 { // Pass the TTY to the background job set_foreground_as(pid); // Signal the background thread that is waiting on this process to stop waiting. diff --git a/src/lib/shell/pipe_exec/mod.rs b/src/lib/shell/pipe_exec/mod.rs index 3f96a8ae4d381e22dbc85dff858a55714353c19a..a021d373af786a8cf81f296f9f05d596a1e11c0d 100644 --- a/src/lib/shell/pipe_exec/mod.rs +++ b/src/lib/shell/pipe_exec/mod.rs @@ -12,13 +12,12 @@ mod pipes; pub mod streams; use self::{ - job_control::{JobControl, ProcessState}, + job_control::ProcessState, pipes::TeePipe, streams::{duplicate_streams, redirect_streams}, }; use super::{ flow_control::{Function, FunctionError}, - fork_function::command_not_found, job::{Job, JobVariant, RefinedJob, TeeItem}, signals::{self, SignalHandler}, status::*, @@ -224,7 +223,7 @@ impl<'b> Shell<'b> { self.watch_foreground(-(pid as i32), "") } Err(ref err) if err.kind() == io::ErrorKind::NotFound => { - if let Err(_) = command_not_found(self, &name) { + if self.fork_function("COMMAND_NOT_FOUND", &["ion", &name]).is_err() { eprintln!("ion: command not found: {}", name); } NO_SUCH_COMMAND @@ -538,7 +537,7 @@ fn spawn_proc( *current_pid = pid; } Err(ref mut err) if err.kind() == io::ErrorKind::NotFound => { - if let Err(_) = command_not_found(shell, &name) { + if shell.fork_function("COMMAND_NOT_FOUND", &["ion", &name]).is_err() { eprintln!("ion: command not found: {}", name); } } diff --git a/src/main.rs b/src/main.rs index 29903013c554c7786d62c5e9589b3d887545c696..2eddf4592ad294b186a196a62d378977d551b572 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,6 @@ extern crate ion_sys as sys; -use ion_shell::{shell::variables::Value, InteractiveBinary, JobControl, Shell, MAN_ION}; +use ion_shell::{InteractiveBinary, Shell, Value, MAN_ION}; use liner::KeyBindings; use std::{ alloc::System,