diff options
Diffstat (limited to 'src/parser/command.rs')
-rw-r--r-- | src/parser/command.rs | 114 |
1 files changed, 107 insertions, 7 deletions
diff --git a/src/parser/command.rs b/src/parser/command.rs index 5d129c3..540dc29 100644 --- a/src/parser/command.rs +++ b/src/parser/command.rs @@ -2,6 +2,10 @@ use std::io::{Error, ErrorKind}; use std::path::{Path, PathBuf}; use std::process::{Command, ExitStatus}; +// > overwrite +// >> append +// | pipe + #[derive(Debug)] pub(in crate::parser) enum Redirect { Std, @@ -9,13 +13,28 @@ pub(in crate::parser) enum Redirect { FileAppend(String), } -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Copy, Clone)] pub(in crate::parser) enum RunOn { Always, ExitSuccess, ExitFailure, } +impl RunOn { + pub(in crate::parser) fn can_run(&self, status: &Option<ExitStatus>) -> bool { + if *self == RunOn::Always { + return true; + } + match status { + Some(s) => { + (*self == RunOn::ExitSuccess && s.success()) + || (*self == RunOn::ExitFailure && !s.success()) + } + None => true, + } + } +} + pub enum RunResult { Command(ExitStatus), Builtin, @@ -29,12 +48,9 @@ pub struct CommandInfo { } impl CommandInfo { - pub(in crate::parser) fn new() -> CommandInfo { - CommandInfo { - args: Vec::new(), - stdout: Redirect::Std, - when: RunOn::Always, - } + pub(in crate::parser) fn new(line: &str, when: RunOn) -> CommandInfo { + let (args, stdout) = tokenize(line); + CommandInfo { args, stdout, when } } pub fn run(&self, home: &PathBuf, status: &Option<ExitStatus>) -> Result<RunResult, Error> { @@ -57,6 +73,90 @@ impl CommandInfo { } } +enum TokenType { + Argument, + StdoutFileOverwrite, + StdoutFileAppend, +} + +fn tokenize(line: &str) -> (Vec<String>, Redirect) { + let mut args: Vec<String> = Vec::new(); + let mut stdout = Redirect::Std; + + let mut iter = line.chars().peekable(); + let mut token = String::new(); + let mut token_type = TokenType::Argument; + let mut quote = false; + + while let Some(i) = iter.next() { + match i { + ' ' => { + if quote { + token.push(' '); + } + } + '\'' => match iter.peek() { + Some(&'\'') | Some(&'>') | Some(&'&') => { + token.push(iter.next().unwrap()); + } + _ => { + quote = !quote; + } + }, + '>' => { + if iter.peek() == Some(&'>') { + token_type = TokenType::StdoutFileAppend; + iter.next(); + } else { + token_type = TokenType::StdoutFileOverwrite; + } + } + _ => { + token.push(i); + } + } + + if !token.is_empty() && ((iter.peek() == Some(&' ') && !quote) || iter.peek() == None) { + match token_type { + TokenType::Argument => args.push(token), + TokenType::StdoutFileOverwrite => stdout = Redirect::FileOverwrite(token), + TokenType::StdoutFileAppend => stdout = Redirect::FileAppend(token), + } + token = String::new(); + } + } + + (args, stdout) +} +/* +#[test] +fn test_tokenizer() { + { + let ls = tokenize("ls -l"); + assert_eq!(ls.args, vec!("ls", "-l")); + let string = tokenize("ls -l 'something else'"); + assert_eq!(string.args, vec!("ls", "-l", "something else")); + let escape = tokenize("ls -l 'junction jan''s'"); + assert_eq!(escape.args, vec!("ls", "-l", "junction jan\'s")); + } + { + let o = tokenize("&& ls"); + assert_eq!(o.args, vec!("ls")); + assert_eq!(o.when, RunOn::ExitSuccess); + let f = tokenize("|| ls"); + assert_eq!(f.args, vec!("ls")); + assert_eq!(f.when, RunOn::ExitFailure); + } + println!("{:?}", tokenize("ls -l something'>")); + println!("{:?}", tokenize("ls -l something'>'>")); + println!("{:?}", tokenize("ls -l something >output")); + println!("{:?}", tokenize("ls -l something > output")); + println!("{:?}", tokenize("ls -l something >'junction jan''s'")); + println!("{:?}", tokenize("ls -l something >>output")); + println!("{:?}", tokenize("ls -l something >> output")); + println!("{:?}", tokenize("ls -l something >>'junction jan''s'")); +} +*/ fn cd(args: &[String], home: &PathBuf) -> Result<RunResult, Error> { if args.len() > 1 { return Err(Error::new( |