aboutsummaryrefslogtreecommitdiff
path: root/src/parser/command.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/parser/command.rs')
-rw-r--r--src/parser/command.rs114
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(