diff --git a/README.md b/README.md index f84d325f819e8afeaf7eec173ad53934acac914b..7a42cd1295583742800957e037cdd13f6009b14c 100644 --- a/README.md +++ b/README.md @@ -98,7 +98,7 @@ command arg1 arg2 arg3; command arg1 arg2 arg3; command arg1 arg2 arg3 ### Piping & Redirecting Standard Output -The pipe (`|`) and redirect (`>`) operators are used for manipulating the standard output. +The pipe (`|`) and redirect (`>`) operators are used for manipulating the standard output. ```ion command arg1 | other_command | another_command arg2 @@ -110,7 +110,7 @@ command arg1 > file The `^|` and `^>` operators are used for manipulating the standard error. ```ion -command arg1 ^| other_command # Not supported yet +command arg1 ^| other_command command arg1 ^> file ``` diff --git a/src/parser/pipelines.rs b/src/parser/pipelines.rs index b324f1d27b0398c418ff0235a158c7d1516a71e3..6efc69d061f6855cf8b51448ee2f77abf110e524 100644 --- a/src/parser/pipelines.rs +++ b/src/parser/pipelines.rs @@ -23,12 +23,16 @@ const IS_VALID: u8 = 255 ^ (BACKSLASH + WHITESPACE); #[derive(PartialEq)] enum RedirMode { False, Stdin, Stdout(RedirectFrom), StdoutAppend(RedirectFrom) } +/// Determine which type of job will be selected based on the next character. +/// +/// - If the `|` char was found and the next character is `|`, it's `Or` +/// - If the `&` char was found and the next character is `&`, it's `And` fn get_job_kind(args: &str, index: usize, pipe_char_was_found: bool) -> (JobKind, bool) { if pipe_char_was_found { if args.bytes().nth(index) == Some(b'|') { (JobKind::Or, true) } else { - (JobKind::Pipe, false) + (JobKind::Pipe(RedirectFrom::Stdout), false) } } else if args.bytes().nth(index) == Some(b'&') { (JobKind::And, true) @@ -74,12 +78,16 @@ pub fn collect(pipelines: &mut Vec<Pipeline>, possible_error: &mut Option<&str>, } macro_rules! job_found { - ($pipe_char_was_found:expr) => {{ - // Determine which type of job will be selected based on the next character. - // - // - If the `|` char was found and the next character is `|`, it's `Or` - // - If the `&` char was found and the next character is `&`, it's `And` - let (kind, advance) = get_job_kind(args, index+1, $pipe_char_was_found); + ($from:expr, $pipe_char_was_found:expr) => {{ + + let (kind, advance) = match $from { + RedirectFrom::Stdout => get_job_kind(args, index+1, $pipe_char_was_found), + _ => { + arg_start += 1; + index += 1; + (JobKind::Pipe($from), true) + } + }; // If either `And` or `Or` was found, advance the iterator once. if advance { let _ = args_iter.next(); } @@ -87,13 +95,14 @@ pub fn collect(pipelines: &mut Vec<Pipeline>, possible_error: &mut Option<&str>, if arguments.is_empty() { jobs.push(Job::new(vec![args[arg_start..index].to_owned()], kind)); } else { - if args.as_bytes()[index-1] != b' ' { + let byte_index = if $from == RedirectFrom::Stdout { index-1 } else { index-2 }; + if args.as_bytes()[byte_index] != b' ' { arguments.push(args[arg_start..index].to_owned()); } jobs.push(Job::new(arguments.clone(), kind)); arguments.clear(); } - if advance { index += 1; } + if advance { index += 1; } arg_start = index + 1; }} } @@ -124,19 +133,27 @@ pub fn collect(pipelines: &mut Vec<Pipeline>, possible_error: &mut Option<&str>, arg_start += 1; } }, - b'|' if (flags & (255 ^ BACKSLASH) == 0) => job_found!(true), + b'|' if (flags & (255 ^ BACKSLASH) == 0) => job_found!(RedirectFrom::Stdout, true), b'&' if (flags & (255 ^ BACKSLASH) == 0) => { - if args_iter.peek() == Some(&b'>') { - let _ = args_iter.next(); - redir_found!(RedirMode::Stdout(RedirectFrom::Both)); - } else { - job_found!(false) + match args_iter.peek() { + Some(&b'>') => { + let _ = args_iter.next(); + redir_found!(RedirMode::Stdout(RedirectFrom::Both)); + }, + _ => job_found!(RedirectFrom::Stdout, false) } }, b'^' if (flags & IS_VALID == 0) => { - if args_iter.peek() == Some(&b'>') { - let _ = args_iter.next(); - redir_found!(RedirMode::Stdout(RedirectFrom::Stderr)); + match args_iter.peek() { + Some(&b'>') => { + let _ = args_iter.next(); + redir_found!(RedirMode::Stdout(RedirectFrom::Stderr)); + }, + Some(&b'|') => { + let _ = args_iter.next(); + job_found!(RedirectFrom::Stderr, true); + } + _ => () } }, b'>' if (flags & IS_VALID == 0) => redir_found!(RedirMode::Stdout(RedirectFrom::Stdout)), diff --git a/src/pipe.rs b/src/pipe.rs index cab157f02a15427c9ee4ba63190a65669d1cb271..aee2e932bdc177ab6df53edc31c7569704aabf0c 100644 --- a/src/pipe.rs +++ b/src/pipe.rs @@ -101,7 +101,12 @@ pub fn pipe(commands: &mut [(Command, JobKind)]) -> i32 { let mut children: Vec<Option<Child>> = Vec::new(); // Initialize the first job - command.stdout(Stdio::piped()); + let _ = match from { + RedirectFrom::Both => command.stderr(Stdio::piped()), // TODO: Fix this + RedirectFrom::Stderr => command.stderr(Stdio::piped()), + RedirectFrom::Stdout => command.stdout(Stdio::piped()), + }; + let child = command.spawn().ok(); if child.is_none() { let stderr = io::stderr(); @@ -112,12 +117,18 @@ pub fn pipe(commands: &mut [(Command, JobKind)]) -> i32 { // Append other jobs until all piped jobs are running. while let Some(&mut (ref mut command, kind)) = commands.next() { - if let JobKind::Pipe(_) = kind { command.stdout(Stdio::piped()); } + if let JobKind::Pipe(from) = kind { + let _ = match from { + RedirectFrom::Both => command.stderr(Stdio::piped()), // TODO: Fix this + RedirectFrom::Stderr => command.stderr(Stdio::piped()), + RedirectFrom::Stdout => command.stdout(Stdio::piped()), + }; + } if let Some(spawned) = children.last() { if let Some(ref child) = *spawned { unsafe { match from { - // Find a way to properly implement this. + // TODO: Find a way to properly implement this. RedirectFrom::Both => if let Some(ref stderr) = child.stderr { command.stdin(Stdio::from_raw_fd(stderr.as_raw_fd())); },