From 2e4bd3774904f11fa9c68b114210a5a46bec7890 Mon Sep 17 00:00:00 2001 From: Michael Aaron Murphy <mmstickman@gmail.com> Date: Fri, 6 Oct 2017 22:33:36 -0400 Subject: [PATCH] Implement -n flag Closes #529 --- src/parser/shell_expand/words.rs | 24 ++++++++++++---------- src/shell/binary.rs | 34 +++++++++++++++++++++++--------- src/shell/flags.rs | 1 + src/shell/library.rs | 5 ++++- src/shell/mod.rs | 6 +++++- src/shell/pipe_exec/mod.rs | 16 ++++++++++++--- src/sys/redox.rs | 5 +++-- src/sys/unix.rs | 10 ++++++++-- 8 files changed, 73 insertions(+), 28 deletions(-) diff --git a/src/parser/shell_expand/words.rs b/src/parser/shell_expand/words.rs index f9b254d2..fb65a1ef 100644 --- a/src/parser/shell_expand/words.rs +++ b/src/parser/shell_expand/words.rs @@ -1277,11 +1277,13 @@ impl<'a, E: Expander + 'a> Iterator for WordIterator<'a, E> { self.read += 1; return Some(self.braces(&mut iterator)); } - b'[' if !self.flags.intersects(SQUOTE | DQUOTE) => if self.glob_check(&mut iterator) { - glob = true; - } else { - return Some(self.array(&mut iterator)); - }, + b'[' if !self.flags.intersects(SQUOTE | DQUOTE) => { + if self.glob_check(&mut iterator) { + glob = true; + } else { + return Some(self.array(&mut iterator)); + } + } b'@' if !self.flags.contains(SQUOTE) => match iterator.next() { Some(b'(') => { self.read += 2; @@ -1397,11 +1399,13 @@ impl<'a, E: Expander + 'a> Iterator for WordIterator<'a, E> { return self.next(); }; } - b'[' if !self.flags.intersects(SQUOTE | DQUOTE) => if self.glob_check(&mut iterator) { - glob = true; - } else { - return Some(WordToken::Normal(&self.data[start..self.read], glob, tilde)); - }, + b'[' if !self.flags.intersects(SQUOTE | DQUOTE) => { + if self.glob_check(&mut iterator) { + glob = true; + } else { + return Some(WordToken::Normal(&self.data[start..self.read], glob, tilde)); + } + } b'*' | b'?' if !self.flags.contains(SQUOTE) => { glob = true; } diff --git a/src/shell/binary.rs b/src/shell/binary.rs index 45e0be20..f2e43c1b 100644 --- a/src/shell/binary.rs +++ b/src/shell/binary.rs @@ -1,7 +1,7 @@ //! Contains the binary logic of Ion. - use super::{DirectoryStack, FlowLogic, JobControl, Shell, ShellHistory, Variables}; use super::completer::*; +use super::flags::*; use super::flow_control::Statement; use super::library::IonLibrary; use super::status::*; @@ -31,7 +31,7 @@ pub(crate) trait Binary { /// Creates an interactive session that reads from a prompt provided by Liner. fn execute_interactive(self); /// Ensures that read statements from a script are terminated. - fn terminate_script_quotes<I: Iterator<Item = String>>(&mut self, lines: I); + fn terminate_script_quotes<I: Iterator<Item = String>>(&mut self, lines: I) -> i32; /// Ensures that read statements from the interactive prompt is terminated. fn terminate_quotes(&mut self, command: String) -> Result<String, ()>; /// Ion's interface to Liner's `read_line` method, which handles everything related to @@ -214,7 +214,7 @@ impl<'a> Binary for Shell<'a> { self.exit(previous_status); } - fn terminate_script_quotes<I: Iterator<Item = String>>(&mut self, mut lines: I) { + fn terminate_script_quotes<I: Iterator<Item = String>>(&mut self, mut lines: I) -> i32 { while let Some(command) = lines.next() { let mut buffer = QuoteTerminator::new(command); while !buffer.check_termination() { @@ -225,12 +225,13 @@ impl<'a> Binary for Shell<'a> { } else { let stderr = io::stderr(); let _ = writeln!(stderr.lock(), "ion: unterminated quote in script"); - self.exit(FAILURE); + return FAILURE; } } } self.on_command(&buffer.consume()); } + // The flow control level being non zero means that we have a statement that has // only been partially parsed. if self.flow_control.level != 0 { @@ -238,7 +239,10 @@ impl<'a> Binary for Shell<'a> { "ion: unexpected end of script: expected end block for `{}`", self.flow_control.current_statement.short() ); + return FAILURE; } + + SUCCESS } fn terminate_quotes(&mut self, command: String) -> Result<String, ()> { @@ -275,6 +279,14 @@ impl<'a> Binary for Shell<'a> { let _ = writeln!(stderr, "ion: -c requires an argument"); self.exit(FAILURE); } + + if self.flow_control.level != 0 { + eprintln!( + "ion: unexpected end of arguments: expected end block for `{}`", + self.flow_control.current_statement.short() + ); + self.exit(FAILURE); + } } fn execute_interactive(mut self) { @@ -319,11 +331,11 @@ impl<'a> Binary for Shell<'a> { match self.variables.tilde_expansion(cmd, &self.directory_stack) { Some(ref cmd) if Path::new(cmd).is_dir() & !cmd.ends_with('/') => { self.save_command_in_history(&[cmd, "/"].concat()); - }, + } None if Path::new(cmd).is_dir() & !cmd.ends_with('/') => { self.save_command_in_history(&[cmd, "/"].concat()); } - _ => self.save_command_in_history(cmd) + _ => self.save_command_in_history(cmd), } } else { self.flow_control.level = 0; @@ -342,8 +354,12 @@ impl<'a> Binary for Shell<'a> { fn main(mut self) { let mut args = env::args().skip(1); - if let Some(path) = args.next() { + while let Some(path) = args.next() { match path.as_str() { + "-n" => { + self.flags |= NO_EXEC; + continue; + } "-c" => self.execute_arguments(args), "--version" => self.display_version(), _ => { @@ -361,9 +377,9 @@ impl<'a> Binary for Shell<'a> { self.wait_for_background(); let previous_status = self.previous_status; self.exit(previous_status); - } else { - self.execute_interactive(); } + + self.execute_interactive(); } fn display_version(&self) { diff --git a/src/shell/flags.rs b/src/shell/flags.rs index e2a72ced..b5de908e 100644 --- a/src/shell/flags.rs +++ b/src/shell/flags.rs @@ -1,2 +1,3 @@ pub const ERR_EXIT: u8 = 1; pub const PRINT_COMMS: u8 = 2; +pub const NO_EXEC: u8 = 4; diff --git a/src/shell/library.rs b/src/shell/library.rs index bcb8aa7a..986811eb 100644 --- a/src/shell/library.rs +++ b/src/shell/library.rs @@ -1,4 +1,5 @@ use super::{Binary, FlowLogic, Shell}; +use super::status::*; use std::fs::File; use std::io::{self, Read}; use std::path::Path; @@ -23,7 +24,9 @@ impl<'a> IonLibrary for Shell<'a> { 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())); + if FAILURE == self.terminate_script_quotes(command_list.lines().map(|x| x.to_owned())) { + self.previous_status = FAILURE; + } Ok(self.previous_status) } } diff --git a/src/shell/mod.rs b/src/shell/mod.rs index 93ab6870..cc58c492 100644 --- a/src/shell/mod.rs +++ b/src/shell/mod.rs @@ -237,7 +237,11 @@ impl<'a> Shell<'a> { } let borrowed = &pipeline.jobs[0].args; let small: SmallVec<[&str; 4]> = borrowed.iter().map(|x| x as &str).collect(); - Some((command.main)(&small, self)) + if self.flags & NO_EXEC != 0 { + Some(SUCCESS) + } else { + Some((command.main)(&small, self)) + } } else { Some(self.execute_pipeline(pipeline)) } diff --git a/src/shell/pipe_exec/mod.rs b/src/shell/pipe_exec/mod.rs index 2f9de2c8..e9a15630 100644 --- a/src/shell/pipe_exec/mod.rs +++ b/src/shell/pipe_exec/mod.rs @@ -182,7 +182,8 @@ pub(crate) trait PipelineExecution { /// /// Each generated command will either be a builtin or external command, and will be /// associated will be marked as an `&&`, `||`, `|`, or final job. - fn generate_commands(&self, pipeline: &mut Pipeline) -> Result<Vec<(RefinedJob, JobKind)>, i32>; + fn generate_commands(&self, pipeline: &mut Pipeline) + -> Result<Vec<(RefinedJob, JobKind)>, i32>; /// Waits for all of the children within a pipe to finish exuecting, returning the /// exit status of the last process in the queue. @@ -232,8 +233,14 @@ impl<'a> PipelineExecution for Shell<'a> { // Generates commands for execution, differentiating between external and builtin commands. let mut piped_commands = match self.generate_commands(pipeline) { Ok(commands) => commands, - Err(error) => return error + Err(error) => return error, }; + + // Don't execute commands when the `-n` flag is passed. + if self.flags & NO_EXEC != 0 { + return SUCCESS; + } + // Redirect the inputs if a custom redirect value was given. if let Some(stdin) = pipeline.stdin.take() { if redirect_input(stdin, &mut piped_commands) { @@ -263,7 +270,10 @@ impl<'a> PipelineExecution for Shell<'a> { } } - fn generate_commands(&self, pipeline: &mut Pipeline) -> Result<Vec<(RefinedJob, JobKind)>, i32> { + fn generate_commands( + &self, + pipeline: &mut Pipeline, + ) -> Result<Vec<(RefinedJob, JobKind)>, i32> { let mut results = Vec::new(); for mut job in pipeline.jobs.drain(..) { let refined = { diff --git a/src/sys/redox.rs b/src/sys/redox.rs index 91233c5c..52f95de9 100644 --- a/src/sys/redox.rs +++ b/src/sys/redox.rs @@ -43,6 +43,7 @@ pub(crate) fn setpgid(pid: u32, pgid: u32) -> io::Result<()> { cvt(syscall::setpgid(pid as usize, pgid as usize)).and(Ok(())) } +#[allow(dead_code)] pub(crate) fn signal(signal: i32, handler: extern "C" fn(i32)) -> io::Result<()> { let new = SigAction { sa_handler: unsafe { mem::transmute(handler) }, @@ -98,14 +99,14 @@ fn cvt(result: Result<usize, syscall::Error>) -> io::Result<usize> { // TODO pub mod signals { - pub(crate) fn block() // fn block() // fn block() // fn block() // fn block() + 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() // fn unblock() // fn unblock() + pub(crate) fn unblock() // fn unblock() // fn unblock() { } } diff --git a/src/sys/unix.rs b/src/sys/unix.rs index 61c83894..fbf79196 100644 --- a/src/sys/unix.rs +++ b/src/sys/unix.rs @@ -48,6 +48,7 @@ pub(crate) fn setpgid(pid: u32, pgid: u32) -> io::Result<()> { cvt(unsafe { libc::setpgid(pid as pid_t, pgid as pid_t) }).and(Ok(())) } +#[allow(dead_code)] pub(crate) fn signal(signal: i32, handler: extern "C" fn(i32)) -> io::Result<()> { if unsafe { libc::signal(signal as c_int, handler as sighandler_t) } == libc::SIG_ERR { Err(io::Error::last_os_error()) @@ -105,7 +106,9 @@ fn cvt<T: IsMinusOne>(t: T) -> io::Result<T> { pub mod signals { /// Blocks the SIGTSTP/SIGTTOU/SIGTTIN/SIGCHLD signals so that the shell never receives /// them. - pub(crate) fn block() // fn block() // fn block() // fn block() // fn block() + pub(crate) fn block() + // fn block() // fn block() // fn block() // fn block() // fn block() // + // fn block() { unsafe { use libc::*; @@ -124,7 +127,10 @@ pub mod signals { /// Unblocks the SIGTSTP/SIGTTOU/SIGTTIN/SIGCHLD signals so children processes can be /// controlled /// by the shell. - pub(crate) fn unblock() // fn unblock() // fn unblock() // fn unblock() // fn unblock() + pub(crate) fn unblock() + // fn unblock() + // fn unblock() // fn unblock() // fn unblock() // fn unblock() // fn + // unblock() { unsafe { use libc::*; -- GitLab