diff --git a/src/builtins/mod.rs b/src/builtins/mod.rs index 9f97dd79a2bc26a1e3648a86ebd6548fa0ecf813..93e294a01ec71cae398c24939a2720d2b257ad9e 100644 --- a/src/builtins/mod.rs +++ b/src/builtins/mod.rs @@ -20,7 +20,6 @@ use self::source::source; use self::test::test; use self::variables::{alias, drop_alias, drop_array, drop_variable}; -use fnv::FnvHashMap; use std::error::Error; use std::io::{self, BufWriter, Write}; @@ -30,6 +29,68 @@ use shell::job_control::{JobControl, ProcessState}; use shell::status::*; use sys; +macro_rules! map { + ($($name:expr, $func:ident, $help:expr),+) => {{ + BuiltinMap { + name: &[$($name),+], + help: &[$($help),+], + functions: &[$($func),+], + } + } +}} + +pub const BUILTINS: &'static BuiltinMap = + &map!( + "let", list_vars, "Displays a list of local variables", + "echo", builtin_echo, "Display a line of text", + "cd", builtin_cd, "Change the current directory\n cd <path>", + "dirs", builtin_dirs, "Display the current directory stack", + "pushd", builtin_pushd, "Push a directory to the stack", + "popd", builtin_popd, "Pop a directory from the stack", + "alias", builtin_alias, "View, set or unset aliases", + "unalias", builtin_unalias, "Delete an alias", + "fn", builtin_fn, "Print list of functions", + "read", builtin_read, "Read some variables\n read <variable>", + "drop", builtin_drop, "Delete a variable", + "matches", builtin_matches, "Checks if a string matches a given regex", + "not", builtin_not, "Reverses the exit status value of the given command.", + "set", builtin_set, "Set or unset values of shell options and positional parameters.", + "eval", builtin_eval, "evaluates the evaluated expression", + "exit", builtin_exit, "Exits the current session", + "wait", builtin_wait, "Waits until all running background processes have completed", + "jobs", builtin_jobs, "Displays all jobs that are attached to the background", + "bg", builtin_bg, "Resumes a stopped background process", + "fg",builtin_fg, "Resumes and sets a background process as the active process", + "suspend", builtin_suspend, "Suspends the shell with a SIGTSTOP signal", + "disown", builtin_disown, "Disowning a process removes that process from the shell's background process table.", + "history", builtin_history,"Display a log of all commands previously executed", + "source", builtin_source, "Evaluate the file following the command or re-initialize the init file", + "test", builtin_test, "Performs tests on files and text", + "calc", builtin_calc, "Calculate a mathematical expression", + "true", builtin_true, "Do nothing, successfully", + "false", builtin_false, "Do nothing, unsuccessfully", + "help", builtin_help, + "Display helpful information about a given command or list commands if none \ + specified\n help <command>", + "and", + builtin_and, + "Execute the command if the shell's previous status is success", + "or", + builtin_or, + "Execute the command if the shell's previous status is failure", + "starts-with", + starts_with, + "Evaluates if the supplied argument starts with a given string", + "ends-with", + ends_with, + "Evaluates if the supplied argument ends with a given string", + "contains", + contains, + "Evaluates if the supplied argument contains a given string", + "exists", builtin_exists, "Performs tests on files and text", + "ion-docs", ion_docs, "Opens the Ion manual" +); + /// 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). @@ -39,140 +100,26 @@ pub struct Builtin { pub main: fn(&[&str], &mut Shell) -> i32, } -impl Builtin { - /// Return the map from command names to commands - pub fn map() -> FnvHashMap<&'static str, Self> { - let mut commands: FnvHashMap<&str, Self> = - FnvHashMap::with_capacity_and_hasher(32, Default::default()); - - // Quick and clean way to insert a builtin, define a function named as the builtin - // for example: - // fn builtin_not (args: &[&str], shell: &mut Shell) -> i32 { - // let cmd = args[1..].join(" "); - // shell.on_command(&cmd); - // match shell.previous_status { - // SUCCESS => FAILURE, - // FAILURE => SUCCESS, - // _ => shell.previous_status - // } - // } - // - // insert_builtin!("not", builtin_not, "Reverses the exit status value of the given - // command."); - // - - macro_rules! insert_builtin { - ($name:expr, $func:ident, $help:expr) => { - commands.insert( - $name, - Builtin { - name: $name, - help: $help, - main: $func, - } - ); - } - } +pub struct BuiltinMap { + pub(crate) name: &'static [&'static str], + pub(crate) help: &'static [&'static str], + pub(crate) functions: &'static [fn(&[&str], &mut Shell) -> i32], +} - // Directories - insert_builtin!("cd", builtin_cd, "Change the current directory\n cd <path>"); - - insert_builtin!("dirs", builtin_dirs, "Display the current directory stack"); - insert_builtin!("pushd", builtin_pushd, "Push a directory to the stack"); - insert_builtin!("popd", builtin_popd, "Pop a directory from the stack"); - - // Aliases - insert_builtin!("alias", builtin_alias, "View, set or unset aliases"); - insert_builtin!("unalias", builtin_unalias, "Delete an alias"); - - // Variables - insert_builtin!("fn", builtin_fn, "Print list of functions"); - insert_builtin!("read", builtin_read, "Read some variables\n read <variable>"); - insert_builtin!("drop", builtin_drop, "Delete a variable"); - - // Misc - insert_builtin!("matches", builtin_matches, "Checks if a string matches a given regex"); - insert_builtin!("not", builtin_not, "Reverses the exit status value of the given command."); - insert_builtin!( - "set", - builtin_set, - "Set or unset values of shell options and positional parameters." - ); - insert_builtin!("eval", builtin_eval, "evaluates the evaluated expression"); - insert_builtin!("exit", builtin_exit, "Exits the current session"); - insert_builtin!( - "wait", - builtin_wait, - "Waits until all running background processes have completed" - ); - insert_builtin!( - "jobs", - builtin_jobs, - "Displays all jobs that are attached to the background" - ); - insert_builtin!("bg", builtin_bg, "Resumes a stopped background process"); - insert_builtin!( - "fg", - builtin_fg, - "Resumes and sets a background process as the active process" - ); - insert_builtin!("suspend", builtin_suspend, "Suspends the shell with a SIGTSTOP signal"); - insert_builtin!( - "disown", - builtin_disown, - "Disowning a process removes that process from the shell's background process table." - ); - insert_builtin!( - "history", - builtin_history, - "Display a log of all commands previously executed" - ); - insert_builtin!( - "source", - builtin_source, - "Evaluate the file following the command or re-initialize the init file" - ); - insert_builtin!("echo", builtin_echo, "Display a line of text"); - insert_builtin!("test", builtin_test, "Performs tests on files and text"); - insert_builtin!("calc", builtin_calc, "Calculate a mathematical expression"); - insert_builtin!("true", builtin_true, "Do nothing, successfully"); - insert_builtin!("false", builtin_false, "Do nothing, unsuccessfully"); - insert_builtin!( - "help", - builtin_help, - "Display helpful information about a given command or list commands if none \ - specified\n help <command>" - ); - insert_builtin!( - "and", - builtin_and, - "Execute the command if the shell's previous status is success" - ); - insert_builtin!( - "or", - builtin_or, - "Execute the command if the shell's previous status is failure" - ); - insert_builtin!( - "starts-with", - starts_with, - "Evaluates if the supplied argument starts with a given string" - ); - insert_builtin!( - "ends-with", - ends_with, - "Evaluates if the supplied argument ends with a given string" - ); - insert_builtin!( - "contains", - contains, - "Evaluates if the supplied argument contains a given string" - ); - insert_builtin!("exists", builtin_exists, "Performs tests on files and text"); - insert_builtin!("ion-docs", ion_docs, "Opens the Ion manual"); - insert_builtin!("let", list_vars, "Displays a list of local variables"); - commands +impl BuiltinMap { + pub fn get(&self, func: &str) -> Option<Builtin> { + self.name.iter().position(|&name| name == func).map(|pos| unsafe { + Builtin { + name: *self.name.get_unchecked(pos), + help: *self.help.get_unchecked(pos), + main: *self.functions.get_unchecked(pos), + } + }) } + + pub fn keys(&self) -> &'static [&'static str] { self.name } + + pub fn contains_key(&self, func: &str) -> bool { self.name.iter().any(|&name| name == func) } } // Definitions of simple builtins go here @@ -387,8 +334,7 @@ fn builtin_help(args: &[&str], shell: &mut Shell) -> i32 { let _ = stdout.write_all(b"\n"); } } else { - let mut commands = builtins.keys().cloned().collect::<Vec<&str>>(); - commands.sort(); + let mut commands = builtins.keys(); let mut buffer: Vec<u8> = Vec::new(); for command in commands { diff --git a/src/main.rs b/src/main.rs index cd28846c6ee649530a7cfb73a1f9ac555b59d281..c047f8e375a745d867cce275fa586505d9d90bc9 100644 --- a/src/main.rs +++ b/src/main.rs @@ -47,7 +47,6 @@ mod builtins; mod shell; mod ascii_helpers; -use builtins::Builtin; use shell::{signals, Binary, Shell}; use std::sync::atomic::Ordering; @@ -72,7 +71,6 @@ fn main() { } } - let builtins = Builtin::map(); - let shell = Shell::new_bin(&builtins); + let shell = Shell::new_bin(); shell.main(); } diff --git a/src/shell/assignments.rs b/src/shell/assignments.rs index b94a5b1734ab0aa7ccdf7fdcb7d3bc5bdfa1817c..50029b3c64ee3f80fc207a84d62d133e57c09487 100644 --- a/src/shell/assignments.rs +++ b/src/shell/assignments.rs @@ -52,7 +52,7 @@ pub(crate) trait VariableStore { fn export(&mut self, &str) -> i32; } -impl<'a> VariableStore for Shell<'a> { +impl VariableStore for Shell { fn local(&mut self, expression: &str) -> i32 { match AssignmentActions::new(expression) { Ok(assignment_actions) => { diff --git a/src/shell/binary.rs b/src/shell/binary.rs index a3ca88abe9cc446a6960962291b7944f92b0cdc5..3e580e8d2137f54bf9e5ae9fbde11c24e6a8fa01 100644 --- a/src/shell/binary.rs +++ b/src/shell/binary.rs @@ -45,7 +45,7 @@ pub(crate) trait Binary { fn prompt_fn(&mut self) -> Option<String>; } -impl<'a> Binary for Shell<'a> { +impl Binary for Shell { fn prompt(&mut self) -> String { if self.flow_control.level == 0 { let rprompt = match self.prompt_fn() { @@ -112,7 +112,7 @@ impl<'a> Binary for Shell<'a> { let prompt = self.prompt(); let funcs = &self.functions; let vars = &self.variables; - let builtins = self.builtins; + let builtins = &self.builtins; let line = self.context.as_mut().unwrap().read_line( prompt, @@ -152,9 +152,9 @@ impl<'a> Binary for Shell<'a> { // Creates a list of definitions from the shell environment that // will be used // in the creation of a custom completer. - let words = builtins.iter() + let words = builtins.keys().iter() // Add built-in commands to the completer's definitions. - .map(|(&s, _)| Identifier::from(s)) + .map(|&s| Identifier::from(s)) // Add the history list to the completer's definitions. .chain(history.iter().cloned()) // Add the aliases to the completer's definitions. diff --git a/src/shell/flow.rs b/src/shell/flow.rs index 2df3e05cd96a651e184b6bc2c46c05c6f619c118..f4fb46d7531d8b02afcfc41cee2d9ce77d76849e 100644 --- a/src/shell/flow.rs +++ b/src/shell/flow.rs @@ -62,7 +62,7 @@ pub(crate) trait FlowLogic { fn execute_match(&mut self, expression: String, cases: Vec<Case>) -> Condition; } -impl<'a> FlowLogic for Shell<'a> { +impl FlowLogic for Shell { fn on_command(&mut self, command_string: &str) { self.break_flow = false; let mut iterator = StatementSplitter::new(command_string).map(parse_and_validate); diff --git a/src/shell/history.rs b/src/shell/history.rs index fe7841c8b136d3fb4a432f2254b58f0d431eda4d..0917e89f88a973309170b0b94582630bd8034cf3 100644 --- a/src/shell/history.rs +++ b/src/shell/history.rs @@ -67,7 +67,7 @@ trait ShellHistoryPrivate { fn should_save_command(&self, command: &str) -> bool; } -impl<'a> ShellHistory for Shell<'a> { +impl ShellHistory for Shell { fn print_history(&self, _arguments: &[&str]) -> i32 { if let Some(context) = self.context.as_ref() { let mut buffer = Vec::with_capacity(8 * 1024); @@ -140,7 +140,7 @@ impl<'a> ShellHistory for Shell<'a> { } } -impl<'a> ShellHistoryPrivate for Shell<'a> { +impl ShellHistoryPrivate for Shell { fn should_save_command(&self, command: &str) -> bool { // just for convenience and to make the code look a bit cleaner let ignore = &self.ignore_setting.flags; diff --git a/src/shell/library.rs b/src/shell/library.rs index 986811eb1ad9e4f55d29243ff1313ea78eda2a55..b93a151879c3012a9b245b1b19a406192afceaba 100644 --- a/src/shell/library.rs +++ b/src/shell/library.rs @@ -12,7 +12,7 @@ pub trait IonLibrary { fn execute_script<P: AsRef<Path>>(&mut self, path: P) -> io::Result<i32>; } -impl<'a> IonLibrary for Shell<'a> { +impl IonLibrary for Shell { fn execute_command(&mut self, command: &str) -> i32 { self.on_command(command); self.previous_status diff --git a/src/shell/mod.rs b/src/shell/mod.rs index 39d48757454d347b7cd66b9ba528f9a3fbbcd70c..f0f13e2374f58cd8da42c13021370d85ec39e5bd 100644 --- a/src/shell/mod.rs +++ b/src/shell/mod.rs @@ -31,7 +31,7 @@ use self::pipe_exec::PipelineExecution; use self::status::*; use self::variables::Variables; use app_dirs::{app_root, AppDataType, AppInfo}; -use builtins::*; +use builtins::{BuiltinMap, BUILTINS}; use fnv::FnvHashMap; use liner::Context; use parser::{ArgumentSplitter, Expander, Select}; @@ -51,9 +51,9 @@ 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 struct Shell<'a> { +pub struct Shell { /// Contains a list of built-in commands that were created when the program started. - pub builtins: &'a FnvHashMap<&'static str, Builtin>, + pub builtins: &'static BuiltinMap, /// Contains the history, completions, and manages writes to the history file. /// Note that the context is only available in an interactive session. pub context: Option<Context>, @@ -90,12 +90,12 @@ pub struct Shell<'a> { ignore_setting: IgnoreSetting, } -impl<'a> Shell<'a> { +impl<'a> Shell { #[allow(dead_code)] /// Panics if DirectoryStack construction fails - pub(crate) fn new_bin(builtins: &'a FnvHashMap<&'static str, Builtin>) -> Shell<'a> { + pub(crate) fn new_bin() -> Shell { Shell { - builtins: builtins, + builtins: BUILTINS, context: None, variables: Variables::default(), flow_control: FlowControl::default(), @@ -115,9 +115,9 @@ impl<'a> Shell<'a> { } #[allow(dead_code)] - pub fn new(builtins: &'a FnvHashMap<&'static str, Builtin>) -> Shell<'a> { + pub fn new() -> Shell { Shell { - builtins: builtins, + builtins: BUILTINS, context: None, variables: Variables::default(), flow_control: FlowControl::default(), @@ -303,7 +303,7 @@ impl<'a> Shell<'a> { } } -impl<'a> Expander for Shell<'a> { +impl<'a> Expander for Shell { fn tilde(&self, input: &str) -> Option<String> { self.variables.tilde_expansion(input, &self.directory_stack) } diff --git a/src/shell/pipe_exec/job_control.rs b/src/shell/pipe_exec/job_control.rs index 55b8f80eaf90d2731e2bf1b316506c0f427eba4d..786f7ab53c1df6e7fbe3bea011b6647c5ded8870 100644 --- a/src/shell/pipe_exec/job_control.rs +++ b/src/shell/pipe_exec/job_control.rs @@ -102,7 +102,7 @@ pub struct BackgroundProcess { pub name: String, } -impl<'a> JobControl for Shell<'a> { +impl JobControl for Shell { fn set_bg_task_in_foreground(&self, pid: u32, cont: bool) -> i32 { // Resume the background task, if needed. if cont { diff --git a/src/shell/pipe_exec/mod.rs b/src/shell/pipe_exec/mod.rs index 3c5b9d64b2366f9f53bb580a1ba606dbff106a45..219467643dd176b400d32ce26a7f4890588d1d60 100644 --- a/src/shell/pipe_exec/mod.rs +++ b/src/shell/pipe_exec/mod.rs @@ -391,7 +391,7 @@ pub(crate) trait PipelineExecution { ) -> i32; } -impl<'a> PipelineExecution for Shell<'a> { +impl PipelineExecution for Shell { fn execute_pipeline(&mut self, pipeline: &mut Pipeline) -> i32 { // Remove any leftover foreground tasks from the last execution. self.foreground.clear(); @@ -447,9 +447,9 @@ impl<'a> PipelineExecution for Shell<'a> { "cd".into(), iter::once("cd".into()).chain(job.args.drain()).collect(), ) - } else if self.functions.contains_key::<str>(job.args[0].as_ref()) { + } else if self.functions.contains_key(job.args[0].as_str()) { RefinedJob::function(job.args[0].clone().into(), job.args.drain().collect()) - } else if self.builtins.contains_key::<str>(job.args[0].as_ref()) { + } else if self.builtins.contains_key(job.args[0].as_str()) { RefinedJob::builtin(job.args[0].clone().into(), job.args.drain().collect()) } else { let mut command = Command::new(job.args[0].clone()); diff --git a/src/sys/redox.rs b/src/sys/redox.rs index 0c812180fd617b9babf754f018847312d360a9ce..cd1145c864b67e9ead5bd8863b5365b2ece61057 100644 --- a/src/sys/redox.rs +++ b/src/sys/redox.rs @@ -83,7 +83,10 @@ pub(crate) fn dup2(old: RawFd, new: RawFd) -> io::Result<RawFd> { pub(crate) fn close(fd: RawFd) -> io::Result<()> { cvt(syscall::close(fd)).and(Ok(())) } -pub(crate) fn close_stdin() { +pub(crate) fn close_stdin() +// fn close_stdin() // fn close_stdin() // fn close_stdin() // fn +// close_stdin() +{ syscall::close(STDIN_FILENO); } @@ -103,12 +106,16 @@ fn cvt(result: Result<usize, syscall::Error>) -> io::Result<usize> { // TODO pub mod signals { - pub(crate) fn block() { } + pub(crate) fn block() // fn block() // fn block() // fn block() // fn block() + { + } /// Unblocks the SIGTSTP/SIGTTOU/SIGTTIN/SIGCHLD signals so children processes can be /// controlled /// by the shell. - pub(crate) fn unblock() { } + pub(crate) fn unblock() // fn unblock() // fn unblock() // fn unblock() // fn unblock() + { + } } pub mod job_control { @@ -132,8 +139,8 @@ pub mod job_control { } - pub(crate) fn watch_foreground<'a, F, D>( - shell: &mut Shell<'a>, + pub(crate) fn watch_foreground<F, D>( + shell: &mut Shell, _pid: u32, last_pid: u32, _get_command: F, diff --git a/src/sys/unix/job_control.rs b/src/sys/unix/job_control.rs index 922ebc6550cfa0b1746e5eaad4b823adf3e492fb..3881f2f4292921996ee28d17ff78b8578793c8c2 100644 --- a/src/sys/unix/job_control.rs +++ b/src/sys/unix/job_control.rs @@ -81,8 +81,8 @@ pub(crate) fn watch_background( const FIRST: u8 = 1; const LAST: u8 = 2; -pub(crate) fn watch_foreground<'a, F, D>( - shell: &mut Shell<'a>, +pub(crate) fn watch_foreground<F, D>( + shell: &mut Shell, first_pid: u32, last_pid: u32, get_command: F, diff --git a/src/sys/unix/mod.rs b/src/sys/unix/mod.rs index 898717ffedc44bdb6d0bc7d88b0061b876cd2620..cb70ecf6595d86e4c1b867ccb13a573b46e9b815 100644 --- a/src/sys/unix/mod.rs +++ b/src/sys/unix/mod.rs @@ -80,8 +80,13 @@ pub(crate) fn dup2(old: RawFd, new: RawFd) -> io::Result<RawFd> { pub(crate) fn close(fd: RawFd) -> io::Result<()> { cvt(unsafe { libc::close(fd) }).and(Ok(())) } -pub(crate) fn close_stdin() { - unsafe { libc::close(STDIN_FILENO); } +pub(crate) fn close_stdin() +// fn close_stdin() // fn close_stdin() // fn close_stdin() // fn +// close_stdin() +{ + unsafe { + libc::close(STDIN_FILENO); + } } pub(crate) fn isatty(fd: RawFd) -> bool { unsafe { libc::isatty(fd) == 1 } } diff --git a/src/sys/unix/signals.rs b/src/sys/unix/signals.rs index 659384f01ca772338bd9f8d5124c69383c6cb8cb..49c477f3078b9e4b8d594429061516629ece48ef 100644 --- a/src/sys/unix/signals.rs +++ b/src/sys/unix/signals.rs @@ -4,7 +4,9 @@ use std::ptr; /// Blocks the SIGTSTP/SIGTTOU/SIGTTIN/SIGCHLD signals so that the shell never receives /// them. -pub(crate) fn block() // fn block() // fn block() +pub(crate) fn block() +// fn block() // fn block() // fn block() // fn block() // fn block() // fn +// block() { unsafe { let mut sigset = mem::uninitialized::<sigset_t>(); @@ -20,7 +22,10 @@ pub(crate) fn block() // fn block() // fn block() /// Unblocks the SIGTSTP/SIGTTOU/SIGTTIN/SIGCHLD signals so children processes can be /// controlled /// by the shell. -pub(crate) fn unblock() // fn unblock() // fn unblock() +pub(crate) fn unblock() +// fn unblock() +// fn unblock() // fn unblock() // fn unblock() // fn unblock() // fn +// unblock() { unsafe { let mut sigset = mem::uninitialized::<sigset_t>();