diff --git a/Cargo.lock b/Cargo.lock index 938f3a9..d6381ef 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -176,6 +176,16 @@ dependencies = [ "memchr", ] +[[package]] +name = "os_pipe" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ae859aa07428ca9a929b936690f8b12dc5f11dd8c6992a18ca93919f28bc177" +dependencies = [ + "libc", + "windows-sys", +] + [[package]] name = "os_str_bytes" version = "6.0.0" @@ -228,6 +238,7 @@ dependencies = [ "anyhow", "clap", "filedescriptor", + "os_pipe", "termion", "utf8-chars", ] @@ -347,3 +358,69 @@ name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.48.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05d4b17490f70499f20b9e791dcf6a299785ce8af4d709018206dc5b4953e95f" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" diff --git a/Cargo.toml b/Cargo.toml index 48f841d..e9f3263 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,7 +2,7 @@ name = "rush" version = "0.1.0" authors = ["Daniel Bulant "] -edition = "2018" +edition = "2021" description = "A simple rust shell" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html @@ -12,6 +12,7 @@ utf8-chars = "1.0.0" termion = "1.5.6" filedescriptor = "0.8.1" clap = "3.1.0" +os_pipe = "1.1.4" [dependencies.anyhow] version = "1.0.54" diff --git a/src/parser/ast.rs b/src/parser/ast.rs index 36ea5f5..4a5ca26 100644 --- a/src/parser/ast.rs +++ b/src/parser/ast.rs @@ -670,6 +670,6 @@ pub fn build_tree(tokens: Vec) -> Result> { } } } - // dbg!(&expressions); + dbg!(&expressions); Ok(expressions) } diff --git a/src/parser/exec.rs b/src/parser/exec.rs index 34062b3..1059005 100644 --- a/src/parser/exec.rs +++ b/src/parser/exec.rs @@ -14,89 +14,6 @@ trait GetValue { fn get(&mut self, ctx: &mut vars::Context) -> Result; } -struct ExecResult { - cmd: Option, - child: Option -} - -impl ExecResult { - fn new(cmd: Option, child: Option) -> Self { - Self { cmd, child } - } - /// Spawns the result, running the command (if any). Non-command results won't be spawned (like let statements) - fn spawn(&mut self) -> &mut Self { - if !self.started() { - match &mut self.cmd { - None => {}, - Some(cmd) => { - self.child = Some(cmd.spawn().unwrap()); - } - } - } - self - } - /// Checks if the result was spawned before by checking the child property. Non-command results won't ever be spawned (like let statements) - fn started(&self) -> bool { - matches!(self.child, Some(_)) - } - /// A simple wrapper for redirecting current result (self) into STDIO (files or streams). - /// - /// Does spawn the current result - fn redirect_into(mut self, into: &mut T) -> &mut T { - match &mut self.cmd { - None => {}, - Some(cmd) => { - cmd.stdout(Stdio::piped()); - self.spawn(); - let child = self.child.unwrap(); - let mut stdout = child.stdout.unwrap(); - io::copy(&mut stdout, into).unwrap(); - } - } - into - } - /// A shorthand for redirecting current result into the next one - /// - /// Uses `redirect_from_result` of the next result. Spawns this result, but not the next one. - fn redirect_into_result(&mut self, into: &mut ExecResult) -> &mut Self { - into.redirect_from_result(self).unwrap(); - self - } - /// Redirects the `from` into the current pending result - /// - /// Doesn't spawn the current result - fn redirect_from>(&mut self, from: T) -> &mut Self { - match &mut self.cmd { - None => {}, - Some(cmd) => { - cmd.stdin(from); - } - } - self - } - /// A shortcut for redirecting a previous result into the current one - /// - /// Spawns the previous result to obtain the output, but not the current one (self) - fn redirect_from_result(&mut self, into: &mut ExecResult) -> io::Result<&mut Self> { - if matches!(self.cmd, None) { - return Ok(self); - } - match &mut self.cmd { - None => {}, - Some(source) => { - source.stdout(Stdio::piped()); - match &mut into.cmd { - None => {}, - Some(target) => { - target.stdin(source.spawn()?.stdout.unwrap()); - } - }; - } - } - Ok(self) - } -} - impl GetValue for CommandValue { fn get(self: &mut CommandValue, ctx: &mut vars::Context) -> Result { match self { @@ -179,6 +96,31 @@ impl ExecExpression for Expression { } } +impl ExecExpression for Command { + fn exec(&mut self, ctx: &mut Context) -> Result> { + let overrides = ctx.get_overrides()?; + if let Some(stdout) = overrides.stdout { self.stdout(stdout); } + if let Some(stderr) = overrides.stderr { self.stderr(stderr); } + if let Some(stdin) = overrides.stdin { self.stdin(stdin); } + let name = self.get_program().to_str().unwrap_or("unknown").to_string(); + let out = self.spawn() + .with_context(|| "Failed to spawn process ".to_string() + &name)? + .wait() + .with_context(|| "Failed to wait for process")?; + ctx.set_var(String::from("?"), Variable::I32(out.code().unwrap_or(-1))); + Ok(None) + } +} + +impl ExecExpression for Option { + fn exec(&mut self, ctx: &mut Context) -> Result> { + match self { + None => Ok(None), + Some(cmd) => cmd.exec(ctx) + } + } +} + impl ExecExpression for BreakExpression { fn exec(self: &mut BreakExpression, ctx: &mut vars::Context) -> Result> { if ctx.break_num > 0 { ctx.break_num -= 1; return Ok(None) } @@ -199,26 +141,15 @@ impl ExecExpression for WhileExpression { ctx.add_scope(); let mut res = None; loop { - match condition.spawn() { - Err(_) => { - res = Some(condition); - break; - }, - Ok(mut child) => { - if !child.wait()?.success() { - res = Some(condition); - break - } else { - match res { - None => {}, - Some(mut cmd) => { - wait_child(cmd.spawn()?, ctx)?; - } - } - res = self.contents.exec(ctx)? - } - } - }; + let condres = condition.exec(ctx)?; + let code = ctx.get_last_exit_code().unwrap_or(1); + + if code == 0 { + res = self.contents.exec(ctx)? + } else { + res = condres; + break; + } if ctx.break_num > 0 { ctx.break_num -= 1; break; @@ -261,12 +192,7 @@ impl ExecExpression for ForExpression { } else { for (i, val) in arr.iter().enumerate() { process(i, val.clone(), ctx, &arg_key, &arg_value)?; - match res { - None => {}, - Some(mut cmd) => { - wait_child(cmd.spawn()?, ctx)?; - } - } + res.exec(ctx)?; res = self.contents.exec(ctx)?; ctx.pop_scope(); if ctx.break_num > 0 { @@ -282,12 +208,7 @@ impl ExecExpression for ForExpression { } else { for (i, char) in str.chars().enumerate() { process(i, Variable::String(char.to_string()), ctx, &arg_key, &arg_value)?; - match res { - None => {}, - Some(mut cmd) => { - wait_child(cmd.spawn()?, ctx)?; - } - } + res.exec(ctx)?; res = self.contents.exec(ctx)?; ctx.pop_scope(); if ctx.break_num > 0 { @@ -312,18 +233,13 @@ impl ExecExpression for IfExpression { Some(cmd) => cmd }; ctx.add_scope(); - let res = match condition.spawn() { - Result::Err(_) => { - self.else_contents.exec(ctx)? - }, - Result::Ok(mut res) => { - if !res.wait()?.success() { - self.else_contents.exec(ctx)? - } else { - self.contents.exec(ctx)? - } - } - }; + let mut res = condition.exec(ctx)?; + let code = ctx.get_last_exit_code().unwrap_or(1); + if code == 0 { + res = self.contents.exec(ctx)?; + } else { + res = self.else_contents.exec(ctx)?; + } ctx.pop_scope(); Ok(res) @@ -357,15 +273,14 @@ impl ExecExpression for Vec { impl ExecExpression for RedirectTargetExpression { fn exec(self: &mut RedirectTargetExpression, ctx: &mut vars::Context) -> Result> { if ctx.break_num > 0 { return Ok(None) } + let (reader, writer) = os_pipe::pipe()?; let mut src = self.source.exec(ctx)?.unwrap(); let mut target = self.target.exec(ctx)?.unwrap(); - src.stdout(Stdio::piped()); - match src.spawn() { - Result::Err(e) => { println!("Error executing: {}", e)}, - Result::Ok(res) => { - target.stdin(res.stdout.unwrap()); - } - } + target.stdin(reader); + ctx.add_scope(); + ctx.scopes.last_mut().unwrap().stdout_override = Some(writer); + src.exec(ctx)?; + ctx.pop_scope(); Ok(Some(target)) } @@ -382,23 +297,11 @@ impl ExecExpression for FileTargetExpression { todo!("Redirect without target file"); } }; + let command = match src { Some(mut cmd) => { - cmd.stdout(Stdio::piped()); - let file = File::create(target.to_string()); - match file { - Result::Err(e) => println!("Error: {}", e), - Result::Ok(mut file) => { - match cmd.spawn() { - Result::Err(e) => { - println!("Error executing command: {}", e); - }, - Result::Ok(res) => { - io::copy(&mut res.stdout.unwrap(), &mut file)?; - } - } - } - } + let file = File::create(target.to_string())?; + cmd.stdout(file); cmd }, None => { bail!("Invalid command provided for file target"); } @@ -422,10 +325,7 @@ impl ExecExpression for FileSourceExpression { None => { bail!("Invalid command") }, Some(cmd) => cmd }; - let source = match File::open(source) { - Result::Err(e) => bail!("Cannot open file: {}", e), - Result::Ok(file) => file - }; + let source = File::open(source).with_context(|| "Couldn't open file to read")?; command.stdin(source); Ok(Some(command)) @@ -437,6 +337,7 @@ impl ExecExpression for Vec { if ctx.break_num > 0 { return Ok(None) } let mut last = None; for expr in self { + last.exec(ctx)?; last = expr.exec(ctx)?; if ctx.break_num > 0 { return Ok(last) } } @@ -451,20 +352,13 @@ impl ExecExpression for OrExpression { None => bail!("Invalid OR expression"), Some(cmd) => cmd }; - let res = match first.spawn() { - Result::Err(_) => { - self.second.exec(ctx)? - }, - Result::Ok(mut res) => { - if res.wait()?.success() { - Some(first) - } else { - self.second.exec(ctx)? - } - } - }; - - Ok(res) + first.exec(ctx)?; + let code = ctx.get_last_exit_code().unwrap_or(1); + if code == 0 { + Ok(Some(first)) + } else { + self.second.exec(ctx) + } } } @@ -475,43 +369,20 @@ impl ExecExpression for AndExpression { None => bail!("Invalid AND expression"), Some(cmd) => cmd }; - let res = match first.spawn() { - Result::Err(_) => { - Some(first) - }, - Result::Ok(mut res) => { - if !res.wait()?.success() { - Some(first) - } else { - self.second.exec(ctx)? - } - } - }; - - Ok(res) + first.exec(ctx)?; + let code = ctx.get_last_exit_code().unwrap_or(1); + if code == 0 { + self.second.exec(ctx) + } else { + Ok(Some(first)) + } } } -fn wait_child(mut child: Child, ctx: &mut Context) -> Result<()> { - let out = child.wait()?; - ctx.set_var(String::from("?"), Variable::I32(out.code().unwrap_or(1))); - Ok(()) -} - pub fn exec_tree(tree: Vec, ctx: &mut vars::Context) -> Result<()> { for mut expression in tree { - let cmd = expression.exec(ctx)?; - match cmd { - None => {}, - Some(mut cmd) => match cmd.spawn() { - Result::Err(e) => { - println!("Error executing: {}", e); - }, - Result::Ok(mut res) => { - wait_child(res, ctx)?; - } - } - } + let mut cmd = expression.exec(ctx)?; + cmd.exec(ctx)?; if ctx.break_num > 0 { bail!("Too many break statements") } } Ok(()) diff --git a/src/parser/vars.rs b/src/parser/vars.rs index 582a808..0305fb4 100644 --- a/src/parser/vars.rs +++ b/src/parser/vars.rs @@ -1,6 +1,7 @@ use std::collections::HashMap; use std::fmt::{Debug, Display, Formatter}; use anyhow::{bail, Result}; +use os_pipe::{PipeReader, PipeWriter}; use crate::parser::ast::FunctionDefinitionExpression; #[derive(Debug, Clone)] @@ -171,6 +172,12 @@ pub enum AnyFunction<'a> { UserDefined(&'a mut FunctionDefinitionExpression) } +pub struct Overrides { + pub stdin: Option, + pub stdout: Option, + pub stderr: Option +} + #[derive(Debug)] pub struct Scope { /// list of variables @@ -178,7 +185,10 @@ pub struct Scope { /// list of functions pub func: HashMap, /// list of file descriptors, to be closed when the scope is left - pub fd: Vec + pub fd: Vec, + pub stdin_override: Option, + pub stdout_override: Option, + pub stderr_override: Option } #[derive(Debug)] @@ -213,7 +223,10 @@ impl Context { let scope = Scope { func: HashMap::new(), vars: HashMap::new(), - fd: Vec::new() + fd: Vec::new(), + stdin_override: None, + stdout_override: None, + stderr_override: None }; self.scopes.push(scope); } @@ -241,6 +254,14 @@ impl Context { None } + pub fn get_last_exit_code(&mut self) -> Option { + let var = self.get_var("?"); + match var { + Some(Variable::I32(int)) => Some(*int), + _ => None, + } + } + pub fn set_var(&mut self, key: String, val: Variable) { let vars = &mut self.scopes.last_mut().unwrap().vars; if key.starts_with("env::") { @@ -275,4 +296,45 @@ impl Context { let func = &mut self.scopes.last_mut().unwrap().func; func.insert(key, val); } + + /// Gets relevant overrides. Should only be used before running a command, as it will clone all pipes + pub fn get_overrides(&self) -> Result { + let mut overrides = Overrides { + stdin: None, + stdout: None, + stderr: None + }; + + for scope in self.scopes.iter().rev() { + match overrides.stdin { + Some(_) => {} + None => { + match &scope.stdin_override { + Some(stdin) => overrides.stdin = Some(stdin.try_clone()?), + None => {} + } + } + } + match overrides.stderr { + Some(_) => {} + None => { + match &scope.stderr_override { + Some(stderr) => overrides.stderr = Some(stderr.try_clone()?), + None => {} + } + } + } + match overrides.stdout { + Some(_) => {} + None => { + match &scope.stdout_override { + Some(stdout) => overrides.stdout = Some(stdout.try_clone()?), + None => {} + } + } + } + } + + Ok(overrides) + } }