diff --git a/src/parser/pipelines/mod.rs b/src/parser/pipelines/mod.rs index cb28fbacd2fc207699310c0a19d5daec4a073420..76d12083777391900f7e408ea5523faa6f343594 100644 --- a/src/parser/pipelines/mod.rs +++ b/src/parser/pipelines/mod.rs @@ -2,8 +2,8 @@ mod collector; pub(crate) use self::collector::*; -use super::{expand_string, Expander}; -use shell::{Job, JobKind}; +use super::expand_string; +use shell::{Job, JobKind, Shell}; use std::fmt; #[derive(Debug, PartialEq, Clone, Copy)] @@ -52,22 +52,20 @@ impl PipeItem { } } - pub(crate) fn expand<E: Expander>(&mut self, expanders: &E) { - self.job.expand(expanders); + pub(crate) fn expand(&mut self, shell: &Shell) { + self.job.expand(shell); for input in self.inputs.iter_mut() { *input = match input { - &mut Input::File(ref s) => { - Input::File(expand_string(s, expanders, false).join(" ")) - } + &mut Input::File(ref s) => Input::File(expand_string(s, shell, false).join(" ")), &mut Input::HereString(ref s) => { - Input::HereString(expand_string(s, expanders, true).join(" ")) + Input::HereString(expand_string(s, shell, true).join(" ")) } }; } for output in self.outputs.iter_mut() { - output.file = expand_string(output.file.as_str(), expanders, false).join(" "); + output.file = expand_string(output.file.as_str(), shell, false).join(" "); } } } @@ -75,8 +73,8 @@ impl PipeItem { impl Pipeline { pub(crate) fn new() -> Self { Pipeline { items: Vec::new() } } - pub(crate) fn expand<E: Expander>(&mut self, expanders: &E) { - self.items.iter_mut().for_each(|i| i.expand(expanders)); + pub(crate) fn expand(&mut self, shell: &Shell) { + self.items.iter_mut().for_each(|i| i.expand(shell)); } pub(crate) fn requires_piping(&self) -> bool { diff --git a/src/parser/shell_expand/words/mod.rs b/src/parser/shell_expand/words/mod.rs index 2176f773dc7cef453297ba62d7f84e2b9801a91b..b44e4ebd79d4e5aa645471d84de77ac860b0551f 100644 --- a/src/parser/shell_expand/words/mod.rs +++ b/src/parser/shell_expand/words/mod.rs @@ -686,6 +686,11 @@ impl<'a, E: Expander + 'a> Iterator for WordIterator<'a, E> { self.read += 2; return Some(self.braced_array_variable(&mut iterator)); } + Some(b' ') | None => { + self.read += 1; + let output = &self.data[start..self.read]; + return Some(WordToken::Normal(output, glob, tilde)); + } _ => { self.read += 1; return Some(self.array_variable(&mut iterator)); @@ -714,6 +719,11 @@ impl<'a, E: Expander + 'a> Iterator for WordIterator<'a, E> { self.read += 2; return Some(self.braced_variable(&mut iterator)); } + Some(b' ') | None => { + self.read += 1; + let output = &self.data[start..self.read]; + return Some(WordToken::Normal(output, glob, tilde)); + } _ => { self.read += 1; return Some(self.variable(&mut iterator)); @@ -781,6 +791,13 @@ impl<'a, E: Expander + 'a> Iterator for WordIterator<'a, E> { return Some(WordToken::Normal(&self.data[start..self.read], glob, tilde)) } b'$' | b'@' if !self.flags.contains(SQUOTE) => { + if let Some(&character) = self.data.as_bytes().get(self.read) { + if character == b' ' { + self.read += 1; + let output = &self.data[start..self.read]; + return Some(WordToken::Normal(output, glob, tilde)); + } + } let output = &self.data[start..self.read]; if output != "" { return Some(WordToken::Normal(output, glob, tilde)); diff --git a/src/shell/job.rs b/src/shell/job.rs index b0916b0fd73ba38bfbca8e06699720be81b24099..89858e9299b7007ef7ec6efd796b18041875fcef 100644 --- a/src/shell/job.rs +++ b/src/shell/job.rs @@ -3,9 +3,11 @@ use std::process::{Command, Stdio}; // use glob::glob; -use parser::{expand_string, Expander}; +use super::Shell; +use parser::expand_string; use parser::pipelines::RedirectFrom; use smallstring::SmallString; +use std::str; use types::*; #[derive(Debug, PartialEq, Clone, Copy)] @@ -36,21 +38,60 @@ impl Job { /// Takes the current job's arguments and expands them, one argument at a /// time, returning a new `Job` with the expanded arguments. - pub(crate) fn expand<E: Expander>(&mut self, expanders: &E) { + pub(crate) fn expand(&mut self, shell: &Shell) { let mut expanded = Array::new(); expanded.grow(self.args.len()); - expanded.extend(self.args.drain().flat_map(|arg| { - let res = expand_string(&arg, expanders, false); - if res.is_empty() { - array![""] - } else { - res - } + expanded.extend(self.args.drain().flat_map(|arg| if arg == "!!" { + expand_last_command(shell, false) + } else if arg == "!$" { + expand_last_command(shell, true) + } else { + expand_arg(&arg, shell) })); self.args = expanded; } } +/// Expands the last command that was provided to the shell. +/// +/// If `args_only` is set to `true`, then only the arguments of +/// the last command will be expanded. +fn expand_last_command(shell: &Shell, args_only: bool) -> Array { + // Strips the command from the supplied buffer. + fn get_args(buffer: &[u8]) -> &[u8] { + if let Some(pos) = buffer.iter().position(|&x| x == b' ') { + let buffer = &buffer[pos + 1..]; + if let Some(pos) = buffer.iter().position(|&x| x != b' ') { + return &buffer[pos..]; + } + } + + &buffer + } + + if let Some(ref context) = shell.context { + if let Some(buffer) = context.history.buffers.iter().last() { + let buffer = buffer.as_bytes(); + let last_arg = unsafe { + str::from_utf8_unchecked(if args_only { get_args(&buffer) } else { &buffer }) + }; + return expand_arg(&last_arg, shell); + } + } + + array![""] +} + +/// Expands a given argument and returns it as an `Array`. +fn expand_arg(arg: &str, shell: &Shell) -> Array { + let res = expand_string(&arg, shell, false); + if res.is_empty() { + array![""] + } else { + res + } +} + /// This represents a job that has been processed and expanded to be run /// as part of some pipeline pub(crate) enum RefinedJob { @@ -288,9 +329,10 @@ mod tests { #[test] fn preserve_empty_arg() { + let shell = Shell::new(); let job = Job::new(array!("rename", "", "0", "a"), JobKind::Last); let mut expanded = job.clone(); - expanded.expand(&Empty); + expanded.expand(&shell); assert_eq!(job, expanded); } diff --git a/src/sys/redox.rs b/src/sys/redox.rs index cd1145c864b67e9ead5bd8863b5365b2ece61057..ba903b5e60499abe2a7107bc2ecbc8b414c8e027 100644 --- a/src/sys/redox.rs +++ b/src/sys/redox.rs @@ -83,12 +83,7 @@ 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() -// fn close_stdin() // fn close_stdin() // fn close_stdin() // fn -// close_stdin() -{ - syscall::close(STDIN_FILENO); -} +pub(crate) fn close_stdin() { syscall::close(STDIN_FILENO); } pub(crate) fn isatty(fd: RawFd) -> bool { if let Ok(tfd) = syscall::dup(fd, b"termios") { @@ -106,16 +101,12 @@ 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() {} /// 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() {} } pub mod job_control { diff --git a/src/sys/unix/mod.rs b/src/sys/unix/mod.rs index cb70ecf6595d86e4c1b867ccb13a573b46e9b815..2da1f77b073215ba85189f5ff13ab9fcf3ed5eae 100644 --- a/src/sys/unix/mod.rs +++ b/src/sys/unix/mod.rs @@ -80,10 +80,7 @@ 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() -// fn close_stdin() // fn close_stdin() // fn close_stdin() // fn -// close_stdin() -{ +pub(crate) fn close_stdin() { unsafe { libc::close(STDIN_FILENO); }