From 4d971dd41547c28e25b6e4fc5fc9ba340bbf67b7 Mon Sep 17 00:00:00 2001 From: Michael Aaron Murphy <mmstickman@gmail.com> Date: Mon, 30 Oct 2017 12:48:53 -0400 Subject: [PATCH] Implement !0, !^, and !* --- src/shell/job.rs | 60 ++++++++++++++++++++++++++++++++++-------------- 1 file changed, 43 insertions(+), 17 deletions(-) diff --git a/src/shell/job.rs b/src/shell/job.rs index 1114933d..32ed824f 100644 --- a/src/shell/job.rs +++ b/src/shell/job.rs @@ -4,6 +4,7 @@ use std::process::{Command, Stdio}; // use glob::glob; use super::Shell; +use parser::ArgumentSplitter; use parser::expand_string; use parser::pipelines::RedirectFrom; use smallstring::SmallString; @@ -41,41 +42,66 @@ impl Job { 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| if arg == "!!" { - expand_last_command(shell, false) - } else if arg == "!$" { - expand_last_command(shell, true) - } else { - expand_arg(&arg, shell) + expanded.extend(self.args.drain().flat_map(|arg| match arg.as_str() { + "!!" => expand_last_command(shell, Operation::All), + "!$" => expand_last_command(shell, Operation::LastArg), + "!0" => expand_last_command(shell, Operation::Command), + "!^" => expand_last_command(shell, Operation::FirstArg), + "!*" => expand_last_command(shell, Operation::NoCommand), + _ => expand_arg(&arg, shell), })); self.args = expanded; } } +pub(crate) enum Operation { + LastArg, + FirstArg, + Command, + NoCommand, + All, +} + /// Expands the last command that was provided to the shell. /// -/// If `args_only` is set to `true`, then only the arguments of +/// If `last_arg` is set to `true`, then only the last argument 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..]; +pub(crate) fn expand_last_command(shell: &Shell, operation: Operation) -> Array { + fn get_last_arg(buffer: &str) -> &str { ArgumentSplitter::new(buffer).last().unwrap_or(buffer) } + + fn get_first_arg(buffer: &str) -> &str { + ArgumentSplitter::new(buffer).skip(1).next().unwrap_or(buffer) + } + + fn get_command(buffer: &str) -> &str { ArgumentSplitter::new(buffer).next().unwrap_or(buffer) } + + fn get_args(buffer: &str) -> &str { + let bbuffer = buffer.as_bytes(); + if let Some(pos) = bbuffer.iter().position(|&x| x == b' ') { + let buffer = &bbuffer[pos + 1..]; if let Some(pos) = buffer.iter().position(|&x| x != b' ') { - return &buffer[pos..]; + return unsafe { str::from_utf8_unchecked(&buffer[pos..]) }; } } - &buffer + buffer + } + + fn expand_args(buffer: &str, shell: &Shell) -> Array { + ArgumentSplitter::new(buffer).flat_map(|b| expand_arg(b, shell)).collect::<Array>() } 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 }) + let buffer = unsafe { str::from_utf8_unchecked(&buffer) }; + return match operation { + Operation::LastArg => expand_arg(get_last_arg(buffer), shell), + Operation::FirstArg => expand_arg(get_first_arg(buffer), shell), + Operation::Command => expand_arg(get_command(buffer), shell), + Operation::NoCommand => expand_args(get_args(buffer), shell), + Operation::All => expand_args(buffer, shell), }; - return expand_arg(&last_arg, shell); } } -- GitLab