From b7ad509ac6f6745ed4ca11f9edcf99ace5b78420 Mon Sep 17 00:00:00 2001 From: Nathan Lilienthal Date: Mon, 19 Feb 2024 17:04:46 -0500 Subject: [PATCH] Run cargo fmt on the repo --- benches/compare.rs | 22 +- benches/piped.rs | 8 +- src/invocation.rs | 4 +- src/lib.rs | 3 +- src/main.rs | 43 ++-- src/process/io.rs | 4 +- src/process/jobs.rs | 17 +- src/process/mod.rs | 39 ++-- src/program/basic.rs | 30 ++- src/program/mod.rs | 20 +- src/program/posix/ast.rs | 36 ++-- src/program/posix/builtin/cd.rs | 34 ++-- src/program/posix/builtin/command.rs | 14 +- src/program/posix/builtin/dot.rs | 16 +- src/program/posix/builtin/exit.rs | 18 +- src/program/posix/builtin/export.rs | 12 +- src/program/posix/builtin/jobs.rs | 15 +- src/program/posix/builtin/mod.rs | 4 +- src/program/posix/builtin/return.rs | 7 +- src/program/posix/builtin/wait.rs | 24 +-- src/program/posix/lex.rs | 291 ++++++++++++--------------- src/program/posix/mod.rs | 229 +++++++++++---------- src/program/runtime.rs | 2 +- src/repl/action.rs | 66 +++--- src/repl/completion.rs | 38 ++-- src/repl/history.rs | 44 ++-- src/repl/mod.rs | 69 ++++--- src/repl/prompt.rs | 17 +- tests/common/mod.rs | 50 +++-- tests/oursh.rs | 28 ++- tests/posix.rs | 22 +- 31 files changed, 574 insertions(+), 652 deletions(-) diff --git a/benches/compare.rs b/benches/compare.rs index a5067531..ddb1db67 100644 --- a/benches/compare.rs +++ b/benches/compare.rs @@ -3,34 +3,22 @@ extern crate criterion; use criterion::Criterion; -#[path="../tests/common/mod.rs"] +#[path = "../tests/common/mod.rs"] mod common; fn compare_script_benchmark(s: &str, c: &mut Criterion) { let mut group = c.benchmark_group("hello world"); - group.bench_function("oursh", |b| { - b.iter(|| { - oursh_release!(> s) - }) - }); + group.bench_function("oursh", |b| b.iter(|| oursh_release!(> s))); - group.bench_function("sh", |b| { - b.iter(|| { - shell!(> "/bin/sh", [] as [&str; 0], s) - }) - }); + group.bench_function("sh", |b| b.iter(|| shell!(> "/bin/sh", [] as [&str; 0], s))); group.bench_function("zsh", |b| { - b.iter(|| { - shell!(> "/usr/bin/zsh", [] as [&str; 0], s) - }) + b.iter(|| shell!(> "/usr/bin/zsh", [] as [&str; 0], s)) }); group.bench_function("fish", |b| { - b.iter(|| { - shell!(> "/usr/bin/fish", [] as [&str; 0], s) - }); + b.iter(|| shell!(> "/usr/bin/fish", [] as [&str; 0], s)); }); group.finish(); diff --git a/benches/piped.rs b/benches/piped.rs index 39dcf052..0aa8b05f 100644 --- a/benches/piped.rs +++ b/benches/piped.rs @@ -3,17 +3,13 @@ extern crate criterion; use criterion::Criterion; -#[path="../tests/common/mod.rs"] +#[path = "../tests/common/mod.rs"] mod common; fn piped_benchmark(c: &mut Criterion) { let mut group = c.benchmark_group("oursh"); - group.bench_function("empty", |b| { - b.iter(|| { - oursh_release!("") - }) - }); + group.bench_function("empty", |b| b.iter(|| oursh_release!(""))); group.bench_function("parse_error", |b| { b.iter(|| { diff --git a/src/invocation.rs b/src/invocation.rs index 39226376..aa163f74 100644 --- a/src/invocation.rs +++ b/src/invocation.rs @@ -2,11 +2,11 @@ //! //! //! -use std::ffi::CString; use crate::program::{ - Runtime, posix::builtin::{self, Builtin}, + Runtime, }; +use std::ffi::CString; /// Sourcing profile startup scripts /// diff --git a/src/lib.rs b/src/lib.rs index 5931bdf6..9f582ff6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -106,7 +106,6 @@ pub mod program; pub mod repl; - #[macro_use] #[cfg(test)] extern crate assert_matches; @@ -119,7 +118,7 @@ mod tests { debug!(1); debug!(1 + 2); debug!("addition: {}", 1 + 2); - debug!("{}", vec![1,2,3,4][2]); + debug!("{}", vec![1, 2, 3, 4][2]); debug!("{} = {} * {}", 15, 3, 5); } } diff --git a/src/main.rs b/src/main.rs index 2a868153..b8df3e63 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,29 +1,28 @@ #![feature(exclusive_range_pattern)] +extern crate dirs; extern crate docopt; extern crate nix; extern crate oursh; extern crate termion; -extern crate dirs; +use docopt::{Docopt, Value}; +use nix::sys::wait::WaitStatus; +use oursh::{ + invocation::source_profile, + process::{Jobs, IO}, + program::{parse_and_run, Error, Result, Runtime}, + repl, VERSION, +}; use std::{ + cell::RefCell, env, - process::{Termination, ExitCode}, fs::File, io::{self, Read}, - cell::RefCell, + process::{ExitCode, Termination}, rc::Rc, }; -use nix::sys::wait::WaitStatus; -use docopt::{Docopt, Value}; use termion::is_tty; -use oursh::{ - VERSION, - repl, - invocation::source_profile, - program::{parse_and_run, Runtime, Result, Error}, - process::{Jobs, IO}, -}; #[cfg(feature = "history")] use oursh::repl::history::History; @@ -77,11 +76,12 @@ fn main() -> MainResult { // "with an extension for support of a // leading ('+') as noted below." let mut args = Docopt::new(USAGE) - .and_then(|d| - d.version(Some(VERSION.into())) - .argv(env::args().into_iter()) - .parse()) - .unwrap_or_else(|e| e.exit()); + .and_then(|d| { + d.version(Some(VERSION.into())) + .argv(env::args().into_iter()) + .parse() + }) + .unwrap_or_else(|e| e.exit()); // Elementary job management. let mut jobs: Jobs = Rc::new(RefCell::new(vec![])); @@ -112,13 +112,12 @@ fn main() -> MainResult { if let Some(Value::Plain(Some(ref c))) = args.find("") { MainResult(parse_and_run(c, &mut runtime)) } else if let Some(Value::Plain(Some(ref filename))) = args.find("") { - let mut file = File::open(filename) - .unwrap_or_else(|_| panic!("error opening file: {}", filename)); + let mut file = + File::open(filename).unwrap_or_else(|_| panic!("error opening file: {}", filename)); // Fill a string buffer from the file. let mut text = String::new(); - file.read_to_string(&mut text) - .expect("error reading file"); + file.read_to_string(&mut text).expect("error reading file"); // Run the program. MainResult(parse_and_run(&text, &mut runtime)) @@ -161,7 +160,7 @@ impl Termination for MainResult { match self.0 { Ok(WaitStatus::Exited(_pid, code)) => ExitCode::from(code as u8), Ok(WaitStatus::Signaled(_pid, _signal, _coredump)) => ExitCode::from(128), - Ok(_) => ExitCode::from(0), // TODO: Is this even remotely correct? + Ok(_) => ExitCode::from(0), // TODO: Is this even remotely correct? Err(Error::Read) => ExitCode::from(1), Err(Error::Parse) => ExitCode::from(2), Err(Error::Runtime) => ExitCode::from(127), diff --git a/src/process/io.rs b/src/process/io.rs index ba5051ac..93eac9c0 100644 --- a/src/process/io.rs +++ b/src/process/io.rs @@ -1,7 +1,5 @@ +use nix::unistd::{close, dup2}; use std::os::unix::io::RawFd; -use nix::{ - unistd::{dup2, close}, -}; /// File descriptors for use in processes and threads #[derive(Debug, Copy, Clone)] diff --git a/src/process/jobs.rs b/src/process/jobs.rs index 770e9b46..e747c745 100644 --- a/src/process/jobs.rs +++ b/src/process/jobs.rs @@ -1,9 +1,6 @@ -use std::{ - rc::Rc, - cell::RefCell, -}; +use crate::process::{ProcessGroup, Wait}; use nix::sys::wait::WaitStatus; -use crate::process::{Wait, ProcessGroup}; +use std::{cell::RefCell, rc::Rc}; /// Shared job handling structure /// @@ -17,21 +14,19 @@ pub fn retain_alive(jobs: &mut Jobs) { let id = job.0.clone(); let body = job.1.leader().body(); match job.1.leader().status() { - Ok(WaitStatus::StillAlive) => { - true - }, + Ok(WaitStatus::StillAlive) => true, Ok(WaitStatus::Exited(pid, code)) => { println!("[{}]+\tExit({})\t{}\t{}", id, code, pid, body); false - }, + } Ok(WaitStatus::Signaled(pid, signal, _)) => { println!("[{}]+\t{}\t{}\t{}", id, signal, pid, body); false - }, + } Ok(_) => { println!("unhandled"); true - }, + } Err(e) => { if nix::errno::Errno::ECHILD != e { println!("err: {:?}", e); diff --git a/src/process/mod.rs b/src/process/mod.rs index dfd2c966..8fa1d65f 100644 --- a/src/process/mod.rs +++ b/src/process/mod.rs @@ -12,16 +12,12 @@ //! More resources from the source at: //! [www.win.tue.nl/~aeb/linux/lk/lk-10](https://www.win.tue.nl/~aeb/linux/lk/lk-10.html). -use std::{ - borrow::Cow, - process::exit, - ffi::CString, -}; use nix::{ errno::Errno, - unistd::{self, execvp, getpid, Pid, ForkResult}, - sys::wait::{waitpid, WaitStatus, WaitPidFlag}, + sys::wait::{waitpid, WaitPidFlag, WaitStatus}, + unistd::{self, execvp, getpid, ForkResult, Pid}, }; +use std::{borrow::Cow, ffi::CString, process::exit}; mod io; pub use self::io::IO; @@ -31,7 +27,6 @@ mod session; mod signal; mod thread; - /// A process to be executed by various means /// /// The shell's main job is to run commands. Each job has various arguments, and rules about what @@ -57,9 +52,11 @@ impl Process { } pub fn body(&self) -> String { - self.argv.iter().map(|a| { - a.to_string_lossy() - }).collect::>>().join(" ") + self.argv + .iter() + .map(|a| a.to_string_lossy()) + .collect::>>() + .join(" ") } pub fn pid(&self) -> Pid { @@ -69,12 +66,7 @@ impl Process { /// Run a shell job in the background. pub fn fork(argv: Vec, io: IO) -> Result { match unsafe { unistd::fork() } { - Ok(ForkResult::Parent { child }) => { - Ok(Process { - argv, - pid: child, - }) - }, + Ok(ForkResult::Parent { child }) => Ok(Process { argv, pid: child }), Ok(ForkResult::Child) => { let process = Process { argv, @@ -87,21 +79,23 @@ impl Process { let name = process.argv[0].to_string_lossy(); eprintln!("oursh: {}: command not found", name); exit(127); - }, + } _ => exit(128), } } else { unreachable!() } - }, + } Err(e) => Err(e), } } fn exec(&self) -> Result<(), nix::Error> { - execvp(&self.argv[0], &self.argv.iter() - .map(|a| a.as_c_str()) - .collect::>()[..]).map(|_| ()) + execvp( + &self.argv[0], + &self.argv.iter().map(|a| a.as_c_str()).collect::>()[..], + ) + .map(|_| ()) } } @@ -130,7 +124,6 @@ impl Wait for Process { } } - /// Processes groups are used for things like pipelines and background jobs /// /// The system call `int setpgid(pid_t pid, pid_t pgid)` is used to set. diff --git a/src/program/basic.rs b/src/program/basic.rs index 39875474..fb56b8f6 100644 --- a/src/program/basic.rs +++ b/src/program/basic.rs @@ -1,14 +1,10 @@ //! Single command programs with no features. -use std::{ - io::BufRead, - ffi::CString, -}; -use nix::sys::wait::WaitStatus; use crate::{ - process::{ProcessGroup, Process, Wait}, - program::{Runtime, Result, Error}, + process::{Process, ProcessGroup, Wait}, + program::{Error, Result, Runtime}, }; - +use nix::sys::wait::WaitStatus; +use std::{ffi::CString, io::BufRead}; /// A basic program with only a single command. #[derive(Debug)] @@ -38,7 +34,6 @@ impl super::Program for Program { } } - /// A single poorly parsed command. #[derive(Debug)] pub struct Command(String); @@ -47,23 +42,26 @@ impl super::Command for Command {} impl super::Run for Command { fn run(&self, runtime: &mut Runtime) -> Result { - let argv = self.0.split_whitespace().map(|a| { - CString::new(a).expect("error reading argument") - }).collect(); + let argv = self + .0 + .split_whitespace() + .map(|a| CString::new(a).expect("error reading argument")) + .collect(); let status = if runtime.background { let job = Process::fork(argv, runtime.io).map_err(|_| Error::Runtime)?; let status = job.status(); - runtime.jobs.borrow_mut().push(("???".into(), ProcessGroup(job))); + runtime + .jobs + .borrow_mut() + .push(("???".into(), ProcessGroup(job))); status } else { let job = Process::fork(argv, runtime.io).map_err(|_| Error::Runtime)?; job.wait() }; match status { - Ok(WaitStatus::Exited(p, c)) if c == 0 => { - Ok(WaitStatus::Exited(p, c)) - }, + Ok(WaitStatus::Exited(p, c)) if c == 0 => Ok(WaitStatus::Exited(p, c)), _ => Err(Error::Runtime), } } diff --git a/src/program/mod.rs b/src/program/mod.rs index 503b99f1..01801075 100644 --- a/src/program/mod.rs +++ b/src/program/mod.rs @@ -58,17 +58,9 @@ //! - TODO #5: Parse sequence of programs from stream. //! - TODO #5: Partial parses for readline-ish / syntax highlighting. -use std::{ - result, - ffi::CString, - fmt::Debug, - io::BufRead, -}; -use nix::{ - unistd::Pid, - sys::wait::WaitStatus, -}; use crate::process::jobs; +use nix::{sys::wait::WaitStatus, unistd::Pid}; +use std::{ffi::CString, fmt::Debug, io::BufRead, result}; /// Convenience type for results with program errors. pub type Result = result::Result; @@ -138,12 +130,10 @@ pub trait Command: Sized + Debug + Run { /// the running [`Process`](crate::process::Process). // TODO: Ids? fn name(&self) -> CString { - CString::new(format!("{:?}", self)) - .expect("error in UTF-8 of format") + CString::new(format!("{:?}", self)).expect("error in UTF-8 of format") } } - /// The primary program type, used for unannotated blocks. // TODO: This should be `ModernProgram`. pub type PrimaryProgram = PosixProgram; @@ -208,9 +198,7 @@ pub mod modern; pub use self::modern::Program as ModernProgram; // TODO: Replace program::Result -pub fn parse_and_run(text: &str, runtime: &mut Runtime) - -> crate::program::Result -{ +pub fn parse_and_run(text: &str, runtime: &mut Runtime) -> crate::program::Result { // Parse with the primary grammar and run each command in order. let program = match parse_primary(text.as_bytes()) { Ok(program) => program, diff --git a/src/program/posix/ast.rs b/src/program/posix/ast.rs index 83bc54fc..0b0e54ca 100644 --- a/src/program/posix/ast.rs +++ b/src/program/posix/ast.rs @@ -111,7 +111,10 @@ pub struct Word(pub String); pub enum Redirect { // Redirecting Input and Output // [n]<>word - RW { n: RawFd, filename: String }, + RW { + n: RawFd, + filename: String, + }, // Redirecting Input // [n] { c.push(command.clone()); - }, + } c => return Command::Compound(vec![c, command.clone()]), } @@ -173,7 +176,7 @@ impl Command { match self { Command::Compound(ref mut c) => { c.insert(0, command.clone()); - }, + } c => return Command::Compound(vec![command.clone(), c]), } @@ -210,19 +213,16 @@ impl Program { } } - #[cfg(test)] mod tests { - use lalrpop_util::ParseError; + use super::*; use crate::program::posix::{ - parse::{ProgramParser, CommandParser}, - lex::{Lexer, Token, Error}, + lex::{Error, Lexer, Token}, + parse::{CommandParser, ProgramParser}, }; - use super::*; + use lalrpop_util::ParseError; - fn parse_program<'a>(text: &'a str) - -> Result, Error>> - { + fn parse_program<'a>(text: &'a str) -> Result, Error>> { let lexer = Lexer::new(text); let parser = ProgramParser::new(); parser.parse(text, lexer) @@ -238,9 +238,7 @@ mod tests { assert_eq!(3, parse_program("git s; ls -la; true;").unwrap().0.len()); } - fn parse_command<'a>(text: &'a str) - -> Result, Error>> - { + fn parse_command<'a>(text: &'a str) -> Result, Error>> { let lexer = Lexer::new(text); let parser = CommandParser::new(); parser.parse(text, lexer) @@ -276,23 +274,23 @@ mod tests { let command = parse_command("! true").unwrap(); assert_matches!(command, Command::Not(_)); let command = parse_command("! true || false").unwrap(); - assert_matches!(command, Command::Or(box Command::Not(_),_)); + assert_matches!(command, Command::Or(box Command::Not(_), _)); } #[test] fn and_command() { let command = parse_command("true && false").unwrap(); - assert_matches!(command, Command::And(_,_)); + assert_matches!(command, Command::And(_, _)); let command = parse_command("true || false && true").unwrap(); - assert_matches!(command, Command::And(_,_)); + assert_matches!(command, Command::And(_, _)); } #[test] fn or_command() { let command = parse_command("true || false").unwrap(); - assert_matches!(command, Command::Or(_,_)); + assert_matches!(command, Command::Or(_, _)); let command = parse_command("true && false || true").unwrap(); - assert_matches!(command, Command::Or(_,_)); + assert_matches!(command, Command::Or(_, _)); } #[test] diff --git a/src/program/posix/builtin/cd.rs b/src/program/posix/builtin/cd.rs index f68cd4d3..60a87a82 100644 --- a/src/program/posix/builtin/cd.rs +++ b/src/program/posix/builtin/cd.rs @@ -1,14 +1,14 @@ -use std::{ - env::{self, set_var}, - ffi::CString, +use crate::{ + program::posix::builtin::Builtin, + program::{Error, Result, Runtime}, }; use nix::{ - unistd::{chdir, Pid}, sys::wait::WaitStatus, + unistd::{chdir, Pid}, }; -use crate::{ - program::posix::builtin::Builtin, - program::{Result, Error, Runtime}, +use std::{ + env::{self, set_var}, + ffi::CString, }; /// Change directory (`cd`) builtin. @@ -19,27 +19,29 @@ impl Builtin for Cd { match argv.len() { 0 => { panic!("command name not passed in argv[0]"); - }, + } 1 => { let home = match env::var("HOME") { Ok(path) => path, Err(_) => return Err(Error::Runtime), }; let dst = home.as_str(); - chdir(dst).map(|_| { - set_var("PWD", &dst); - WaitStatus::Exited(Pid::this(), 0) - }) - .map_err(|_| Error::Runtime) - }, + chdir(dst) + .map(|_| { + set_var("PWD", &dst); + WaitStatus::Exited(Pid::this(), 0) + }) + .map_err(|_| Error::Runtime) + } 2 => { let dst = argv[1].to_string_lossy(); - chdir(dst.as_ref()).map(|_| { + chdir(dst.as_ref()) + .map(|_| { set_var("PWD", dst.as_ref()); WaitStatus::Exited(Pid::this(), 0) }) .map_err(|_| Error::Runtime) - }, + } _ => { eprintln!("too many arguments"); Ok(WaitStatus::Exited(Pid::this(), 1)) diff --git a/src/program/posix/builtin/command.rs b/src/program/posix/builtin/command.rs index 70794018..cb3cc83e 100644 --- a/src/program/posix/builtin/command.rs +++ b/src/program/posix/builtin/command.rs @@ -1,18 +1,20 @@ -use std::ffi::CString; -use nix::sys::wait::WaitStatus; use crate::{ program::posix::builtin::Builtin, - program::{Result, Runtime, parse_and_run}, + program::{parse_and_run, Result, Runtime}, }; +use nix::sys::wait::WaitStatus; +use std::ffi::CString; /// Command builtin, I have no idea why you'd want this honestly. pub struct Command; impl Builtin for Command { fn run(self, argv: Vec, runtime: &mut Runtime) -> Result { - let text = argv[1..].iter().map(|c| { - c.to_str().unwrap() - }).collect::>().join(" "); + let text = argv[1..] + .iter() + .map(|c| c.to_str().unwrap()) + .collect::>() + .join(" "); parse_and_run(&text, runtime) } } diff --git a/src/program/posix/builtin/dot.rs b/src/program/posix/builtin/dot.rs index 472ed5e9..57272fb6 100644 --- a/src/program/posix/builtin/dot.rs +++ b/src/program/posix/builtin/dot.rs @@ -1,16 +1,9 @@ -use std::{ - io::Read, - fs::File, - ffi::CString, -}; -use nix::{ - unistd::Pid, - sys::wait::WaitStatus, -}; use crate::{ program::posix::builtin::Builtin, - program::{Result, Runtime, parse_and_run}, + program::{parse_and_run, Result, Runtime}, }; +use nix::{sys::wait::WaitStatus, unistd::Pid}; +use std::{ffi::CString, fs::File, io::Read}; /// Execute commands from `file` in the current environment /// @@ -43,9 +36,8 @@ impl Builtin for Dot { } else { Ok(WaitStatus::Exited(Pid::this(), 1)) } - }, + } _ => unreachable!(), - } } } diff --git a/src/program/posix/builtin/exit.rs b/src/program/posix/builtin/exit.rs index e4a1110b..9627ddb4 100644 --- a/src/program/posix/builtin/exit.rs +++ b/src/program/posix/builtin/exit.rs @@ -1,15 +1,9 @@ -use std::{ - process, - ffi::CString, -}; -use nix::{ - unistd::Pid, - sys::wait::WaitStatus, -}; use crate::{ program::posix::builtin::Builtin, program::{Result, Runtime}, }; +use nix::{sys::wait::WaitStatus, unistd::Pid}; +use std::{ffi::CString, process}; /// Exit builtin, alternative to ctrl-d. pub struct Exit; @@ -25,17 +19,15 @@ impl Builtin for Exit { match argv.len() { 0 => { panic!("command name not passed in argv[0]"); - }, - 1 => { - process::exit(0) - }, + } + 1 => process::exit(0), 2 => { if let Ok(n) = str::parse(argv[1].to_str().unwrap()) { process::exit(n) } else { process::exit(2) } - }, + } _ => { eprintln!("too many arguments"); Ok(WaitStatus::Exited(Pid::this(), 1)) diff --git a/src/program/posix/builtin/export.rs b/src/program/posix/builtin/export.rs index eeefce8e..3a248177 100644 --- a/src/program/posix/builtin/export.rs +++ b/src/program/posix/builtin/export.rs @@ -1,15 +1,9 @@ -use std::{ - env, - ffi::CString, -}; -use nix::{ - unistd::Pid, - sys::wait::WaitStatus, -}; use crate::{ program::posix::builtin::Builtin, program::{Result, Runtime}, }; +use nix::{sys::wait::WaitStatus, unistd::Pid}; +use std::{env, ffi::CString}; /// Export builtin, used to set global variables. pub struct Export; @@ -30,7 +24,7 @@ impl Builtin for Export { } } Ok(WaitStatus::Exited(Pid::this(), 0)) - }, + } } } } diff --git a/src/program/posix/builtin/jobs.rs b/src/program/posix/builtin/jobs.rs index 805bfda7..501de156 100644 --- a/src/program/posix/builtin/jobs.rs +++ b/src/program/posix/builtin/jobs.rs @@ -1,12 +1,9 @@ -use std::ffi::CString; -use nix::{ - unistd::Pid, - sys::wait::WaitStatus, -}; use crate::{ program::posix::builtin::Builtin, program::{Result, Runtime}, }; +use nix::{sys::wait::WaitStatus, unistd::Pid}; +use std::ffi::CString; /// Background job information. pub struct Jobs; @@ -14,8 +11,12 @@ pub struct Jobs; impl Builtin for Jobs { fn run(self, _: Vec, runtime: &mut Runtime) -> Result { for (id, job) in runtime.jobs.borrow().iter() { - println!("[{}]\t{}\t\t{}", - id, job.leader().pid(), job.leader().body()); + println!( + "[{}]\t{}\t\t{}", + id, + job.leader().pid(), + job.leader().body() + ); } Ok(WaitStatus::Exited(Pid::this(), 0)) } diff --git a/src/program/posix/builtin/mod.rs b/src/program/posix/builtin/mod.rs index 692ba722..f9862b89 100644 --- a/src/program/posix/builtin/mod.rs +++ b/src/program/posix/builtin/mod.rs @@ -3,9 +3,9 @@ //! //! These commands take precedence over any executables with the same name //! in the `$PATH`. -use std::ffi::CString; -use nix::sys::wait::WaitStatus; use crate::program::{Result, Runtime}; +use nix::sys::wait::WaitStatus; +use std::ffi::CString; /// A builtin is a custom shell command, often changing the state of the /// shell in some way. diff --git a/src/program/posix/builtin/return.rs b/src/program/posix/builtin/return.rs index 4f0af49d..69de7978 100644 --- a/src/program/posix/builtin/return.rs +++ b/src/program/posix/builtin/return.rs @@ -1,12 +1,9 @@ -use std::ffi::CString; -use nix::{ - unistd::Pid, - sys::wait::WaitStatus, -}; use crate::{ program::posix::builtin::Builtin, program::{Result, Runtime}, }; +use nix::{sys::wait::WaitStatus, unistd::Pid}; +use std::ffi::CString; /// Noop builtin, same idea as `true`. pub struct Return(pub i32); diff --git a/src/program/posix/builtin/wait.rs b/src/program/posix/builtin/wait.rs index 237813dc..e28ff8a0 100644 --- a/src/program/posix/builtin/wait.rs +++ b/src/program/posix/builtin/wait.rs @@ -1,13 +1,10 @@ -use std::ffi::CString; -use nix::{ - unistd::Pid, - sys::wait::WaitStatus, -}; use crate::{ - program::posix::builtin::Builtin, - program::{Result, Error, Runtime}, process::Wait as WaitTrait, + program::posix::builtin::Builtin, + program::{Error, Result, Runtime}, }; +use nix::{sys::wait::WaitStatus, unistd::Pid}; +use std::ffi::CString; /// Wait builtin, used to block for all background jobs. pub struct Wait; @@ -26,17 +23,20 @@ impl Builtin for Wait { n => { let mut last = Ok(WaitStatus::Exited(Pid::this(), 0)); for i in 2..=n { - let pid: i32 = argv[i-1].to_string_lossy().parse().unwrap(); - if let Some((_id, pg)) = runtime.jobs.borrow().iter().find(|(_, pg)| { - pid == pg.leader().pid().as_raw() - }) { + let pid: i32 = argv[i - 1].to_string_lossy().parse().unwrap(); + if let Some((_id, pg)) = runtime + .jobs + .borrow() + .iter() + .find(|(_, pg)| pid == pg.leader().pid().as_raw()) + { last = pg.leader().wait().map_err(|_| Error::Runtime) } else { eprintln!("oursh: wait: pid {} is not a child of this shell", pid); } } last - }, + } } } } diff --git a/src/program/posix/lex.rs b/src/program/posix/lex.rs index 10b79ee2..55d45384 100644 --- a/src/program/posix/lex.rs +++ b/src/program/posix/lex.rs @@ -130,8 +130,8 @@ impl<'input> Iterator for Lexer<'input> { while let Some((s, c, e)) = self.advance() { let tok = match c { '\n' => Some(Ok((s, Token::Linefeed, e))), - ';' => Some(Ok((s, Token::Semi, e))), - '#' => { + ';' => Some(Ok((s, Token::Semi, e))), + '#' => { while let Some((_, c, _)) = self.lookahead { match c { '\n' => break, @@ -140,52 +140,48 @@ impl<'input> Iterator for Lexer<'input> { } self.next() } - ')' => Some(Ok((s, Token::RParen, e))), - '(' => Some(Ok((s, Token::LParen, e))), - '`' => Some(Ok((s, Token::Backtick, e))), - '!' => Some(Ok((s, Token::Bang, e))), - '=' => Some(Ok((s, Token::Equals, e))), + ')' => Some(Ok((s, Token::RParen, e))), + '(' => Some(Ok((s, Token::LParen, e))), + '`' => Some(Ok((s, Token::Backtick, e))), + '!' => Some(Ok((s, Token::Bang, e))), + '=' => Some(Ok((s, Token::Equals, e))), '\\' => Some(Ok((s, Token::Backslash, e))), '\'' => Some(self.single_quote(s, e)), - '"' => Some(self.double_quote(s, e)), - '>' => { - match self.lookahead { - Some((_, '>', e)) => { - self.advance(); - Some(Ok((s, Token::DGreat, e))) - }, - Some((_, '&', e)) => { - self.advance(); - Some(Ok((s, Token::GreatAnd, e))) - }, - Some((_, '|', e)) => { - self.advance(); - Some(Ok((s, Token::Clobber, e))) - }, - _ => Some(Ok((s, Token::Great, e))), + '"' => Some(self.double_quote(s, e)), + '>' => match self.lookahead { + Some((_, '>', e)) => { + self.advance(); + Some(Ok((s, Token::DGreat, e))) } + Some((_, '&', e)) => { + self.advance(); + Some(Ok((s, Token::GreatAnd, e))) + } + Some((_, '|', e)) => { + self.advance(); + Some(Ok((s, Token::Clobber, e))) + } + _ => Some(Ok((s, Token::Great, e))), }, - '<' => { - match self.lookahead { - Some((_, '&', e)) => { - self.advance(); - Some(Ok((s, Token::LessAnd, e))) - }, - Some((_, '<', e)) => { - self.advance(); - if let Some((_, '-', e)) = self.lookahead { - self.advance(); - Some(Ok((s, Token::DLessDash, e))) - } else { - Some(Ok((s, Token::DLess, e))) - } - }, - Some((_, '>', e)) => { + '<' => match self.lookahead { + Some((_, '&', e)) => { + self.advance(); + Some(Ok((s, Token::LessAnd, e))) + } + Some((_, '<', e)) => { + self.advance(); + if let Some((_, '-', e)) = self.lookahead { self.advance(); - Some(Ok((s, Token::LessGreat, e))) - }, - _ => Some(Ok((s, Token::Less, e))), + Some(Ok((s, Token::DLessDash, e))) + } else { + Some(Ok((s, Token::DLess, e))) + } } + Some((_, '>', e)) => { + self.advance(); + Some(Ok((s, Token::LessGreat, e))) + } + _ => Some(Ok((s, Token::Less, e))), }, '&' => { if let Some((_, '&', e)) = self.lookahead { @@ -194,7 +190,7 @@ impl<'input> Iterator for Lexer<'input> { } else { Some(Ok((s, Token::Amper, e))) } - }, + } '|' => { if let Some((_, '|', e)) = self.lookahead { self.advance(); @@ -202,15 +198,12 @@ impl<'input> Iterator for Lexer<'input> { } else { Some(Ok((s, Token::Pipe, e))) } + } + '$' => match self.lookahead { + Some((_, '{', e)) | Some((_, '(', e)) => Some(Ok((s, Token::Dollar, e))), + _ => Some(self.word(s, e)), }, - '$' => { - match self.lookahead { - Some((_, '{', e)) | - Some((_, '(', e)) => Some(Ok((s, Token::Dollar, e))), - _ => Some(self.word(s, e)), - } - }, - '{' => Some(self.block(s, s+e)), + '{' => Some(self.block(s, s + e)), '}' => Some(Ok((s, Token::RBrace, e))), c if is_word_start(c) => Some(self.word(s, e)), c if c.is_whitespace() => continue, @@ -235,15 +228,20 @@ impl<'input> Lexer<'input> { let next = self.chars.next(); self.lookahead = next.map(|n| (n.0, n.1, n.0 + n.1.len_utf8())); Some((s, c, e)) - }, + } None => None, } } // TODO: start and end arguments aren't quite right here. - fn take_until(&mut self, start: usize, mut end: usize, mut terminate: F) - -> (&'input str, usize) - where F: FnMut(char) -> bool + fn take_until( + &mut self, + start: usize, + mut end: usize, + mut terminate: F, + ) -> (&'input str, usize) + where + F: FnMut(char) -> bool, { while let Some((_, c, _)) = self.lookahead { if terminate(c) { @@ -255,52 +253,54 @@ impl<'input> Lexer<'input> { (&self.input[start..end], end) } - fn take_while(&mut self, start: usize, end: usize, mut keep_going: F) - -> (&'input str, usize) - where F: FnMut(char) -> bool, + fn take_while(&mut self, start: usize, end: usize, mut keep_going: F) -> (&'input str, usize) + where + F: FnMut(char) -> bool, { self.take_until(start, end, |c| !keep_going(c)) } - fn single_quote(&mut self, start: usize, end: usize) - -> Result<(usize, Token<'input>, usize), Error> - { + fn single_quote( + &mut self, + start: usize, + end: usize, + ) -> Result<(usize, Token<'input>, usize), Error> { // TODO: This quietly stops at EOF. let (_, end) = self.take_while(start, end, |c| c != '\''); - self.advance(); // Consume the ending single quote. - Ok((start, Token::Word(&self.input[start+1..end]), end)) + self.advance(); // Consume the ending single quote. + Ok((start, Token::Word(&self.input[start + 1..end]), end)) } // TODO: Escapes // TODO: Honestly, I think this needs to be handled in the .lalrpop file. - fn double_quote(&mut self, start: usize, end: usize) - -> Result<(usize, Token<'input>, usize), Error> - { + fn double_quote( + &mut self, + start: usize, + end: usize, + ) -> Result<(usize, Token<'input>, usize), Error> { // TODO: This quietly stops at EOF. let (input, end) = self.take_while(start, end, |c| c != '"'); - self.advance(); // Consume the ending double quote. + self.advance(); // Consume the ending double quote. Ok((start, Token::Word(&input[1..]), end)) } - fn word(&mut self, start: usize, end: usize) - -> Result<(usize, Token<'input>, usize), Error> - { + fn word(&mut self, start: usize, end: usize) -> Result<(usize, Token<'input>, usize), Error> { let (word, end) = self.take_while(start, end, is_word_continue); let tok = match word { - "if" => Token::If, - "then" => Token::Then, - "else" => Token::Else, - "elif" => Token::Elif, - "fi" => Token::Fi, + "if" => Token::If, + "then" => Token::Then, + "else" => Token::Else, + "elif" => Token::Elif, + "fi" => Token::Fi, "export" => Token::Export, - "do" => Token::Do, - "done" => Token::Done, - "case" => Token::Case, - "esac" => Token::Esac, - "while" => Token::While, - "until" => Token::Until, - "for" => Token::For, - word => self.io_number(word), + "do" => Token::Do, + "done" => Token::Done, + "case" => Token::Case, + "esac" => Token::Esac, + "while" => Token::While, + "until" => Token::Until, + "for" => Token::For, + word => self.io_number(word), }; Ok((start, tok, end)) } @@ -317,16 +317,14 @@ impl<'input> Lexer<'input> { Token::Word(word) } - fn block(&mut self, start: usize, end: usize) - -> Result<(usize, Token<'input>, usize), Error> - { + fn block(&mut self, start: usize, end: usize) -> Result<(usize, Token<'input>, usize), Error> { #[cfg(feature = "shebang-block")] { if let Some((_, '#', s)) = self.lookahead { - self.advance(); // Consume the '#'. + self.advance(); // Consume the '#'. if let Some((_, '!', s)) = self.lookahead { let (_, end) = self.take_until(s, end, |c| c == ';'); - self.advance(); // Consume the ';' delimiter. + self.advance(); // Consume the ';' delimiter. self.take_while(end, end, |c| c.is_whitespace()); self.in_shebang = true; @@ -347,9 +345,7 @@ impl<'input> Lexer<'input> { } #[cfg(feature = "shebang-block")] - fn text(&mut self, start: usize, end: usize) - -> Result<(usize, Token<'input>, usize), Error> - { + fn text(&mut self, start: usize, end: usize) -> Result<(usize, Token<'input>, usize), Error> { // TODO: Count matching braces in TEXT. let (_, end) = self.take_until(start, end, |d| d == '}'); self.in_shebang = false; @@ -361,9 +357,7 @@ fn is_word_start(ch: char) -> bool { match ch { // Ignore C0 and C1 control character words. // TODO: Test - '\u{007F}' | - '\u{0000}'..='\u{001F}' | - '\u{0080}'..='\u{009F}' => false, + '\u{007F}' | '\u{0000}'..='\u{001F}' | '\u{0080}'..='\u{009F}' => false, _ => is_word_continue(ch), } } @@ -372,10 +366,9 @@ fn is_word_continue(ch: char) -> bool { match ch { // List of syntax from above. // TODO: Make this list generated. - ';' | ')' | '(' | '`' | '!' | '=' | '\\' | '\'' | '"' - | '>' | '<' | '&' | '|' | '{' | '}' | '*' - => false, - _ => !ch.is_whitespace() + ';' | ')' | '(' | '`' | '!' | '=' | '\\' | '\'' | '"' | '>' | '<' | '&' | '|' | '{' + | '}' | '*' => false, + _ => !ch.is_whitespace(), } } @@ -392,136 +385,102 @@ mod tests { #[test] fn error() { let mut lexer = Lexer::new("*"); - assert_matches!(lexer.next(), - Some(Err(Error::UnrecognizedChar(_, '*', _)))); + assert_matches!(lexer.next(), Some(Err(Error::UnrecognizedChar(_, '*', _)))); } #[test] fn linefeed() { let mut lexer = Lexer::new("\n"); - assert_matches!(lexer.next(), - Some(Ok((_, Token::Linefeed, _)))); + assert_matches!(lexer.next(), Some(Ok((_, Token::Linefeed, _)))); } #[test] fn words() { let mut lexer = Lexer::new("ls -la"); - assert_matches!(lexer.next(), - Some(Ok((_, Token::Word("ls"), _)))); - assert_matches!(lexer.next(), - Some(Ok((_, Token::Word("-la"), _)))); + assert_matches!(lexer.next(), Some(Ok((_, Token::Word("ls"), _)))); + assert_matches!(lexer.next(), Some(Ok((_, Token::Word("-la"), _)))); // Numbers are still words on their own. let mut lexer = Lexer::new("123"); - assert_matches!(lexer.next(), - Some(Ok((_, Token::Word("123"), _)))); + assert_matches!(lexer.next(), Some(Ok((_, Token::Word("123"), _)))); let mut lexer = Lexer::new("$PATH"); - assert_matches!(lexer.next(), - Some(Ok((_, Token::Word("$PATH"), _)))); + assert_matches!(lexer.next(), Some(Ok((_, Token::Word("$PATH"), _)))); } #[test] fn redirects() { let mut lexer = Lexer::new(">"); - assert_matches!(lexer.next(), - Some(Ok((_, Token::Great, _)))); + assert_matches!(lexer.next(), Some(Ok((_, Token::Great, _)))); let mut lexer = Lexer::new(">>"); - assert_matches!(lexer.next(), - Some(Ok((_, Token::DGreat, _)))); + assert_matches!(lexer.next(), Some(Ok((_, Token::DGreat, _)))); let mut lexer = Lexer::new(">&"); - assert_matches!(lexer.next(), - Some(Ok((_, Token::GreatAnd, _)))); + assert_matches!(lexer.next(), Some(Ok((_, Token::GreatAnd, _)))); let mut lexer = Lexer::new(">|"); - assert_matches!(lexer.next(), - Some(Ok((_, Token::Clobber, _)))); + assert_matches!(lexer.next(), Some(Ok((_, Token::Clobber, _)))); let mut lexer = Lexer::new("<"); - assert_matches!(lexer.next(), - Some(Ok((_, Token::Less, _)))); + assert_matches!(lexer.next(), Some(Ok((_, Token::Less, _)))); let mut lexer = Lexer::new("<<"); - assert_matches!(lexer.next(), - Some(Ok((_, Token::DLess, _)))); + assert_matches!(lexer.next(), Some(Ok((_, Token::DLess, _)))); let mut lexer = Lexer::new("<<-"); - assert_matches!(lexer.next(), - Some(Ok((_, Token::DLessDash, _)))); + assert_matches!(lexer.next(), Some(Ok((_, Token::DLessDash, _)))); let mut lexer = Lexer::new("<&"); - assert_matches!(lexer.next(), - Some(Ok((_, Token::LessAnd, _)))); + assert_matches!(lexer.next(), Some(Ok((_, Token::LessAnd, _)))); let mut lexer = Lexer::new("<>"); - assert_matches!(lexer.next(), - Some(Ok((_, Token::LessGreat, _)))); + assert_matches!(lexer.next(), Some(Ok((_, Token::LessGreat, _)))); } #[test] fn io_number() { let mut lexer = Lexer::new("ls -la 1> /dev/null"); - assert_matches!(lexer.next(), - Some(Ok((_, Token::Word("ls"), _)))); - assert_matches!(lexer.next(), - Some(Ok((_, Token::Word("-la"), _)))); - assert_matches!(lexer.next(), - Some(Ok((_, Token::IoNumber(1), _)))); - assert_matches!(lexer.next(), - Some(Ok((_, Token::Great, _)))); - assert_matches!(lexer.next(), - Some(Ok((_, Token::Word("/dev/null"), _)))); + assert_matches!(lexer.next(), Some(Ok((_, Token::Word("ls"), _)))); + assert_matches!(lexer.next(), Some(Ok((_, Token::Word("-la"), _)))); + assert_matches!(lexer.next(), Some(Ok((_, Token::IoNumber(1), _)))); + assert_matches!(lexer.next(), Some(Ok((_, Token::Great, _)))); + assert_matches!(lexer.next(), Some(Ok((_, Token::Word("/dev/null"), _)))); } #[test] fn whitespace_words() { let mut lexer = Lexer::new("ls\n"); - assert_matches!(lexer.next(), - Some(Ok((_, Token::Word("ls"), _)))); - assert_matches!(lexer.next(), - Some(Ok((_, Token::Linefeed, _)))); + assert_matches!(lexer.next(), Some(Ok((_, Token::Word("ls"), _)))); + assert_matches!(lexer.next(), Some(Ok((_, Token::Linefeed, _)))); } #[test] fn unicode_words() { let mut lexer = Lexer::new("๐Ÿ˜€ -๐Ÿงช"); - assert_matches!(lexer.next(), - Some(Ok((_, Token::Word("๐Ÿ˜€"), _)))); - assert_matches!(lexer.next(), - Some(Ok((_, Token::Word("-๐Ÿงช"), _)))); + assert_matches!(lexer.next(), Some(Ok((_, Token::Word("๐Ÿ˜€"), _)))); + assert_matches!(lexer.next(), Some(Ok((_, Token::Word("-๐Ÿงช"), _)))); let mut lexer = Lexer::new("๐Ÿ˜€ -๐Ÿงช๐Ÿ’€"); - assert_matches!(lexer.next(), - Some(Ok((_, Token::Word("๐Ÿ˜€"), _)))); - assert_matches!(lexer.next(), - Some(Ok((_, Token::Word("-๐Ÿงช๐Ÿ’€"), _)))); + assert_matches!(lexer.next(), Some(Ok((_, Token::Word("๐Ÿ˜€"), _)))); + assert_matches!(lexer.next(), Some(Ok((_, Token::Word("-๐Ÿงช๐Ÿ’€"), _)))); } #[test] fn keywords() { let mut lexer = Lexer::new("if ls done"); - assert_matches!(lexer.next(), - Some(Ok((_, Token::If, _)))); - assert_matches!(lexer.next(), - Some(Ok((_, Token::Word("ls"), _)))); - assert_matches!(lexer.next(), - Some(Ok((_, Token::Done, _)))); + assert_matches!(lexer.next(), Some(Ok((_, Token::If, _)))); + assert_matches!(lexer.next(), Some(Ok((_, Token::Word("ls"), _)))); + assert_matches!(lexer.next(), Some(Ok((_, Token::Done, _)))); } #[test] fn comments() { let mut lexer = Lexer::new("word # comment"); - assert_matches!(lexer.next(), - Some(Ok((_, Token::Word("word"), _)))); + assert_matches!(lexer.next(), Some(Ok((_, Token::Word("word"), _)))); assert!(lexer.next().is_none()); let mut lexer = Lexer::new("word1 # comment1\nword2 # comment2"); - assert_matches!(lexer.next(), - Some(Ok((_, Token::Word("word1"), _)))); - assert_matches!(lexer.next(), - Some(Ok((_, Token::Linefeed, _)))); - assert_matches!(lexer.next(), - Some(Ok((_, Token::Word("word2"), _)))); + assert_matches!(lexer.next(), Some(Ok((_, Token::Word("word1"), _)))); + assert_matches!(lexer.next(), Some(Ok((_, Token::Linefeed, _)))); + assert_matches!(lexer.next(), Some(Ok((_, Token::Word("word2"), _)))); assert!(lexer.next().is_none()); let mut lexer = Lexer::new("#word"); assert!(lexer.next().is_none()); let mut lexer = Lexer::new("word#word"); - assert_matches!(lexer.next(), - Some(Ok((_, Token::Word("word#word"), _)))); + assert_matches!(lexer.next(), Some(Ok((_, Token::Word("word#word"), _)))); assert!(lexer.next().is_none()); } diff --git a/src/program/posix/mod.rs b/src/program/posix/mod.rs index 12364c36..99eb8f97 100644 --- a/src/program/posix/mod.rs +++ b/src/program/posix/mod.rs @@ -114,40 +114,34 @@ //! //! [1]: http://pubs.opengroup.org/onlinepubs/9699919799/ +use self::ast::{Assignment, Redirect}; +use crate::{ + process::{Process, ProcessGroup, Wait}, + program::{Error, Result, Runtime}, +}; +use dirs::home_dir; +use lalrpop_util::ParseError; +use nix::{sys::wait::WaitStatus, unistd::Pid}; use std::{ + env::{set_var, var}, ffi::CString, - io::{Write, BufRead}, - process::{self, Stdio}, fs::File, + io::{BufRead, Write}, os::unix::io::IntoRawFd, - env::{var, set_var} -}; -use lalrpop_util::ParseError; -use nix::{ - sys::wait::WaitStatus, - unistd::Pid, + process::{self, Stdio}, }; #[cfg(feature = "raw")] use uuid::Uuid; -use dirs::home_dir; -use crate::{ - process::{ProcessGroup, Process, Wait}, - program::{Runtime, Result, Error}, -}; -use self::ast::{Assignment, Redirect}; #[cfg(feature = "shebang-block")] use { - std::io, + self::ast::Interpreter, std::fs, std::io, std::os::unix::fs::PermissionsExt, std::process::ExitStatus, - std::fs, - std::os::unix::fs::PermissionsExt, - self::ast::Interpreter, }; // Re-exports. -pub use self::ast::Program; pub use self::ast::Command; +pub use self::ast::Program; pub use self::builtin::Builtin; /// The syntax and semantics of a single POSIX command. @@ -176,18 +170,26 @@ impl super::Program for Program { match e { ParseError::InvalidToken { location } => { eprintln!("invalid token found at {}", location); - }, + } ParseError::UnrecognizedToken { token, expected } => { let (s, t, e) = token; - eprintln!("unexpected token {:?} found at {}-{}, expecting one of: {}", - t, s, e, expected.join(", ")); - }, + eprintln!( + "unexpected token {:?} found at {}-{}, expecting one of: {}", + t, + s, + e, + expected.join(", ") + ); + } ParseError::UnrecognizedEof { location, expected } => { if location == 0 { - return Ok(Program(vec![])) + return Ok(Program(vec![])); } else { - eprintln!("unexpected EOF found at {}, expecting one of: {}", - location, expected.join(", ")); + eprintln!( + "unexpected EOF found at {}, expecting one of: {}", + location, + expected.join(", ") + ); } } ParseError::ExtraToken { token: (i, t, _) } => { @@ -196,7 +198,7 @@ impl super::Program for Program { ParseError::User { error } => { let lex::Error::UnrecognizedChar(s, c, e) = error; eprintln!("unexpected character {} found at {}-{}", c, s, e); - }, + } } Err(Error::Parse) } @@ -224,32 +226,40 @@ impl super::Run for Command { match r { Redirect::RW { n, filename, .. } => { let file = File::options() - .create(true) - .read(true) - .write(true) - .open(filename).unwrap(); + .create(true) + .read(true) + .write(true) + .open(filename) + .unwrap(); let fd = file.into_raw_fd(); runtime.io.0[*n as usize] = fd; - }, + } Redirect::Read { n, filename, .. } => { let file = File::options() - .read(true) - .write(false) - .open(filename).unwrap(); + .read(true) + .write(false) + .open(filename) + .unwrap(); let fd = file.into_raw_fd(); runtime.io.0[*n as usize] = fd; - }, - Redirect::Write { n, filename, append, .. } => { + } + Redirect::Write { + n, + filename, + append, + .. + } => { // TODO: Clobber let file = File::options() - .create(true) - .read(false) - .write(true) - .append(*append) - .open(filename).unwrap(); + .create(true) + .read(false) + .write(true) + .append(*append) + .open(filename) + .unwrap(); let fd = file.into_raw_fd(); runtime.io.0[*n as usize] = fd; - }, + } }; } @@ -257,27 +267,31 @@ impl super::Run for Command { // $ FOO=~ // $ echo $FOO // /home/nixpulvis - let argv: Vec = words.iter().map(|word| { - CString::new(&expand_home(&expand_vars(&word.0)) as &str) - .expect("error in word UTF-8") - }).collect(); + let argv: Vec = words + .iter() + .map(|word| { + CString::new(&expand_home(&expand_vars(&word.0)) as &str) + .expect("error in word UTF-8") + }) + .collect(); if let Some(command) = argv.clone().first() { match command.to_string_lossy().as_ref() { - "." => builtin::Dot.run(argv, runtime), - ":" => builtin::Return(0).run(argv, runtime), - "cd" => builtin::Cd.run(argv, runtime), + "." => builtin::Dot.run(argv, runtime), + ":" => builtin::Return(0).run(argv, runtime), + "cd" => builtin::Cd.run(argv, runtime), "command" => builtin::Command.run(argv, runtime), - "exit" => builtin::Exit.run(argv, runtime), - "export" => builtin::Export.run(argv, runtime), - "false" => builtin::Return(1).run(argv, runtime), - "jobs" => builtin::Jobs.run(argv, runtime), - "true" => builtin::Return(0).run(argv, runtime), - "wait" => builtin::Wait.run(argv, runtime), + "exit" => builtin::Exit.run(argv, runtime), + "export" => builtin::Export.run(argv, runtime), + "false" => builtin::Return(1).run(argv, runtime), + "jobs" => builtin::Jobs.run(argv, runtime), + "true" => builtin::Return(0).run(argv, runtime), + "wait" => builtin::Wait.run(argv, runtime), _ => { let id = (runtime.jobs.borrow().len() + 1).to_string(); let name = argv[0].to_string_lossy().to_string(); - let process = Process::fork(argv, runtime.io).map_err(|_| Error::Runtime)?; + let process = + Process::fork(argv, runtime.io).map_err(|_| Error::Runtime)?; if runtime.background { let status = process.status(); eprintln!("[{}]\t{}", id, process.pid()); @@ -290,12 +304,12 @@ impl super::Run for Command { } status } - }, + } } } else { Ok(WaitStatus::Exited(Pid::this(), 0)) } - }, + } // { sleep 3; date; }& // { sleep 3; date; }& ls Command::Compound(ref commands) => { @@ -306,38 +320,30 @@ impl super::Run for Command { last = command.run(runtime)?; } Ok(last) + } + Command::Not(ref command) => match command.run(runtime) { + Ok(WaitStatus::Exited(p, c)) => Ok(WaitStatus::Exited(p, (c == 0) as i32)), + Ok(s) => Ok(s), + Err(_) => Err(Error::Runtime), }, - Command::Not(ref command) => { - match command.run(runtime) { - Ok(WaitStatus::Exited(p, c)) => { - Ok(WaitStatus::Exited(p, (c == 0) as i32)) - } - Ok(s) => Ok(s), - Err(_) => Err(Error::Runtime), - } - }, - Command::And(ref left, ref right) => { - match left.run(runtime) { - Ok(WaitStatus::Exited(_, c)) if c == 0 => { - right.run(runtime).map_err(|_| Error::Runtime) - }, - Ok(s) => Ok(s), - Err(_) => Err(Error::Runtime), + Command::And(ref left, ref right) => match left.run(runtime) { + Ok(WaitStatus::Exited(_, c)) if c == 0 => { + right.run(runtime).map_err(|_| Error::Runtime) } + Ok(s) => Ok(s), + Err(_) => Err(Error::Runtime), }, - Command::Or(ref left, ref right) => { - match left.run(runtime) { - Ok(WaitStatus::Exited(_, c)) if c != 0 => { - right.run(runtime).map_err(|_| Error::Runtime) - }, - Ok(s) => Ok(s), - Err(_) => Err(Error::Runtime), + Command::Or(ref left, ref right) => match left.run(runtime) { + Ok(WaitStatus::Exited(_, c)) if c != 0 => { + right.run(runtime).map_err(|_| Error::Runtime) } + Ok(s) => Ok(s), + Err(_) => Err(Error::Runtime), }, Command::Subshell(ref program) => { // TODO #4: Run in a *subshell* ffs. program.run(runtime) - }, + } Command::Pipeline(ref left, ref right) => { // TODO: This is obviously a temporary hack. if let box Command::Simple(_assigns, lwords, _redirs) = left { @@ -347,8 +353,7 @@ impl super::Run for Command { .spawn() .expect("error swawning pipeline process"); - let output = child.wait_with_output() - .expect("error reading stdout"); + let output = child.wait_with_output().expect("error reading stdout"); if let box Command::Simple(_assigns, rwords, _redirs) = right { let mut child = process::Command::new(&rwords[0].0) @@ -358,22 +363,21 @@ impl super::Run for Command { .expect("error swawning pipeline process"); { - let stdin = child.stdin.as_mut() - .expect("error opening stdin"); - stdin.write_all(&output.stdout) + let stdin = child.stdin.as_mut().expect("error opening stdin"); + stdin + .write_all(&output.stdout) .expect("error writing to stdin"); } - child.wait() - .expect("error waiting for piped command"); + child.wait().expect("error waiting for piped command"); } } Ok(WaitStatus::Exited(Pid::this(), 0)) - }, + } Command::Background(ref command) => { runtime.background = true; command.run(runtime) - }, + } #[cfg(feature = "shebang-block")] Command::Lang(ref interpreter, ref text) => { fn bridge(interpreter: &str, text: &str) -> io::Result { @@ -385,16 +389,13 @@ impl super::Run for Command { // fucking files... The shebang isn't even a real // POSIX standard. let mut file = File::create(&bridgefile)?; - let mut interpreter = interpreter.chars() - .map(|c| c as u8) - .collect::>(); + let mut interpreter = + interpreter.chars().map(|c| c as u8).collect::>(); interpreter.insert(0, b'!'); interpreter.insert(0, b'#'); file.write_all(&interpreter)?; file.write_all(b"\n")?; - let text = text.chars() - .map(|c| c as u8) - .collect::>(); + let text = text.chars().map(|c| c as u8).collect::>(); file.write_all(&text)?; let mut perms = fs::metadata(&bridgefile)?.permissions(); @@ -408,30 +409,24 @@ impl super::Run for Command { Interpreter::Primary => { unimplemented!() } - Interpreter::Alternate => { - "/bin/sh" - }, - Interpreter::HashLang(ref language) => { - match language.as_str() { - "ruby" => "/usr/bin/env ruby", - "node" => "/usr/bin/env node", - "python" => "/usr/bin/env python", - "racket" => "/usr/bin/env racket", - _ => return Err(Error::Read), - } - }, - Interpreter::Shebang(ref interpreter) => { - interpreter + Interpreter::Alternate => "/bin/sh", + Interpreter::HashLang(ref language) => match language.as_str() { + "ruby" => "/usr/bin/env ruby", + "node" => "/usr/bin/env node", + "python" => "/usr/bin/env python", + "racket" => "/usr/bin/env racket", + _ => return Err(Error::Read), }, + Interpreter::Shebang(ref interpreter) => interpreter, }; bridge(interpreter, text).map_err(|_| Error::Read)?; Ok(WaitStatus::Exited(Pid::this(), 0)) - }, + } #[cfg(not(feature = "shebang-block"))] - Command::Lang(_,_) => { + Command::Lang(_, _) => { unimplemented!(); - }, + } } } } @@ -477,7 +472,7 @@ fn expand_vars(string: &str) -> String { result.push(c); } else if variable_start > -1 { if variable.is_empty() { - result.pop(); // remove the leading '$'. + result.pop(); // remove the leading '$'. } variable.push(c); } else { @@ -512,8 +507,8 @@ lalrpop_mod!( #[cfg(test)] mod tests { - use crate::program::Program as ProgramTrait; use super::*; + use crate::program::Program as ProgramTrait; #[test] fn program_parse_empty() { diff --git a/src/program/runtime.rs b/src/program/runtime.rs index 080cf642..e832525f 100644 --- a/src/program/runtime.rs +++ b/src/program/runtime.rs @@ -1,7 +1,7 @@ -use docopt::ArgvMap; use crate::process::{Jobs, IO}; #[cfg(feature = "history")] use crate::repl::history::History; +use docopt::ArgvMap; #[derive(Debug)] pub struct Runtime<'a> { diff --git a/src/repl/action.rs b/src/repl/action.rs index 2190ac71..59003f23 100644 --- a/src/repl/action.rs +++ b/src/repl/action.rs @@ -1,15 +1,12 @@ //! Actions to be bound to input methods. -use std::io::{Write, Stdout}; +use std::io::{Stdout, Write}; -use std::process::exit; -use termion::{ - cursor::DetectCursorPos, - raw::RawTerminal, -}; -use docopt::ArgvMap; -use crate::program::{Runtime, parse_and_run}; -use crate::process::{IO, Jobs}; +use crate::process::{Jobs, IO}; +use crate::program::{parse_and_run, Runtime}; use crate::repl::prompt; +use docopt::ArgvMap; +use std::process::exit; +use termion::{cursor::DetectCursorPos, raw::RawTerminal}; #[cfg(feature = "history")] use super::history::History; @@ -17,7 +14,6 @@ use super::history::History; #[cfg(feature = "completion")] use super::completion::*; - pub struct Action; pub struct ActionContext<'a> { @@ -69,9 +65,7 @@ impl Action { if let Ok((x, y)) = context.stdout.cursor_pos() { let i = (x - context.prompt_length) as usize; context.text.insert(i, c); - print!("{}{}", - &context.text[i..], - termion::cursor::Goto(x + 1, y)); + print!("{}{}", &context.text[i..], termion::cursor::Goto(x + 1, y)); } else { context.text.push(c); print!("{}", c); @@ -84,11 +78,13 @@ impl Action { if x > context.prompt_length { let i = x - context.prompt_length; context.text.remove((i - 1) as usize); - print!("{}{}{}{}", - termion::cursor::Goto(context.prompt_length, y), - termion::clear::UntilNewline, - context.text, - termion::cursor::Goto(x - 1, y)); + print!( + "{}{}{}{}", + termion::cursor::Goto(context.prompt_length, y), + termion::clear::UntilNewline, + context.text, + termion::cursor::Goto(x - 1, y) + ); context.stdout.flush().unwrap(); } } @@ -153,17 +149,17 @@ impl Action { } pub fn clear(context: &mut ActionContext) { - print!("{}{}", - termion::clear::All, - termion::cursor::Goto(1, 1)); + print!("{}{}", termion::clear::All, termion::cursor::Goto(1, 1)); prompt::ps1(&mut context.stdout); } #[cfg(feature = "history")] pub fn history_up(context: &mut ActionContext) { - print!("{}{}", - termion::cursor::Left(1000), // XXX - termion::clear::CurrentLine); + print!( + "{}{}", + termion::cursor::Left(1000), // XXX + termion::clear::CurrentLine + ); context.prompt.display(&mut context.stdout); if let Some(history_text) = context.history.get_up() { @@ -175,9 +171,11 @@ impl Action { #[cfg(feature = "history")] pub fn history_down(context: &mut ActionContext) { - print!("{}{}", - termion::cursor::Left(1000), // XXX - termion::clear::CurrentLine); + print!( + "{}{}", + termion::cursor::Left(1000), // XXX + termion::clear::CurrentLine + ); context.prompt.display(&mut context.stdout); if let Some(history_text) = context.history.get_down() { @@ -205,18 +203,20 @@ impl Action { prompt::ps1(&mut context.stdout); print!("{}", context.text); context.stdout.flush().unwrap(); - }, + } Completion::Complete(t) => { *context.text = t; - print!("{}{}", - termion::cursor::Left(1000), // XXX - termion::clear::CurrentLine); + print!( + "{}{}", + termion::cursor::Left(1000), // XXX + termion::clear::CurrentLine + ); context.stdout.flush().unwrap(); prompt::ps1(&mut context.stdout); print!("{}", context.text); context.stdout.flush().unwrap(); - }, - Completion::None => {}, + } + Completion::None => {} } } } diff --git a/src/repl/completion.rs b/src/repl/completion.rs index b98879cc..bc9c4b6f 100644 --- a/src/repl/completion.rs +++ b/src/repl/completion.rs @@ -16,12 +16,7 @@ //! assert_eq!("cargo", &text); //! ``` -use std::{ - env, - fs, - cmp::Ordering::Equal, - os::unix::fs::PermissionsExt, -}; +use std::{cmp::Ordering::Equal, env, fs, os::unix::fs::PermissionsExt}; /// The result of a query for text completion. /// @@ -41,8 +36,7 @@ impl Completion { /// Returns true if this completion is a single option. pub fn is_complete(&self) -> bool { match *self { - Completion::None | - Completion::Partial(_) => false, + Completion::None | Completion::Partial(_) => false, Completion::Complete(_) => true, } } @@ -52,11 +46,9 @@ impl Completion { pub fn first(&self) -> String { match *self { Completion::None => "".to_owned(), - Completion::Partial(ref p) => { - match p.first() { - Some(t) => t.to_owned(), - None => "".to_owned(), - } + Completion::Partial(ref p) => match p.first() { + Some(t) => t.to_owned(), + None => "".to_owned(), }, Completion::Complete(ref s) => s.to_owned(), } @@ -86,8 +78,7 @@ impl Completion { /// ``` pub fn complete(text: &str) -> Completion { match executable_completions(text) { - c @ Completion::Partial(_) | - c @ Completion::Complete(_) => c, + c @ Completion::Partial(_) | c @ Completion::Complete(_) => c, Completion::None => path_complete(text), } } @@ -110,16 +101,16 @@ pub fn executable_completions(text: &str) -> Completion { let mut matches = vec![]; for dir in env::split_paths(&paths) { if let Ok(executables) = fs::read_dir(dir) { - let paths = executables.filter_map(|e| { - match e { Ok(p) => Some(p.path()), _ => None } + let paths = executables.filter_map(|e| match e { + Ok(p) => Some(p.path()), + _ => None, }); for path in paths { if let Some(filename) = path.file_name() { let filename = filename.to_string_lossy(); if let Ok(metadata) = fs::metadata(&path) { - if (metadata.permissions() - .mode() & 0o111 != 0) + if (metadata.permissions().mode() & 0o111 != 0) && filename.starts_with(text) { matches.push(filename.into()); @@ -134,16 +125,13 @@ pub fn executable_completions(text: &str) -> Completion { 0 => Completion::None, 1 => Completion::Complete(matches.remove(0)), _ => { - matches.sort_by(|a, b| { - match a.len().cmp(&b.len()) { - Equal => b.cmp(&a), - o => o - } + matches.sort_by(|a, b| match a.len().cmp(&b.len()) { + Equal => b.cmp(&a), + o => o, }); Completion::Partial(matches) } } - } None => panic!("PATH is undefined"), } diff --git a/src/repl/history.rs b/src/repl/history.rs index 66b0bc7f..5231cb29 100644 --- a/src/repl/history.rs +++ b/src/repl/history.rs @@ -1,10 +1,5 @@ //! Keeps a record of previous commands, used for completion and archeology. -use std::{ - env, - io::prelude::*, - fs::File, - path::Path, -}; +use std::{env, fs::File, io::prelude::*, path::Path}; /// The history of a user's provided commands. #[derive(Debug)] @@ -22,10 +17,16 @@ impl History { // HACK: There's got to be a cleaner way. let mut index = 0; - if self.1.iter().enumerate().find(|(i, (t, _))| { - index = *i; - text == t - }).is_some() { + if self + .1 + .iter() + .enumerate() + .find(|(i, (t, _))| { + index = *i; + text == t + }) + .is_some() + { self.1[index].1 += count; let text = self.1.remove(index); self.1.insert(0, text); @@ -41,9 +42,8 @@ impl History { if text_len > 0 { match self.0 { Some(i) => { - self.0 = Some(i.saturating_add(1) - .min(text_len - 1)); - }, + self.0 = Some(i.saturating_add(1).min(text_len - 1)); + } None => self.0 = Some(0), } } else { @@ -60,7 +60,7 @@ impl History { match self.0 { Some(i) if i == 0 => self.0 = None, Some(i) => self.0 = Some(i.saturating_sub(1)), - None => {}, + None => {} }; match self.0 { @@ -74,8 +74,7 @@ impl History { let home = env::var("HOME").expect("HOME variable not set."); let history_path = format!("{}/.oursh_history", home); if Path::new(&history_path).exists() { - let mut f = File::open(&history_path) - .expect("error cannot find history"); + let mut f = File::open(&history_path).expect("error cannot find history"); let mut contents = String::new(); f.read_to_string(&mut contents) .expect("error reading history"); @@ -86,9 +85,7 @@ impl History { // println!("{:?}", s); // }) // }).collect::>(); - let hist = contents.split("\n").map(|s| { - (String::from(s), 0) - }); + let hist = contents.split("\n").map(|s| (String::from(s), 0)); // Add each entry to the history in order. for (text, index) in hist { @@ -105,13 +102,10 @@ impl History { pub fn save(&self) -> Result<(), ()> { let home = env::var("HOME").expect("HOME variable not set."); let history_path = format!("{}/.oursh_history", home); - let mut f = File::create(&history_path) - .expect("error cannot find history"); + let mut f = File::create(&history_path).expect("error cannot find history"); for (text, _) in self.1.iter() { - f.write_all(text.as_bytes()) - .expect("error writing history"); - f.write_all(b"\n") - .expect("error writing history"); + f.write_all(text.as_bytes()).expect("error writing history"); + f.write_all(b"\n").expect("error writing history"); } Ok(()) diff --git a/src/repl/mod.rs b/src/repl/mod.rs index 227158bd..a3db640a 100644 --- a/src/repl/mod.rs +++ b/src/repl/mod.rs @@ -3,25 +3,25 @@ //! There will be *absolutely no* blocking STDIN/OUT/ERR on things like tab //! completion or other potentially slow, or user defined behavior. -use std::io::{Stdin, Stdout}; +use crate::process::{Jobs, IO}; use docopt::ArgvMap; use nix::sys::wait::WaitStatus; use nix::unistd::Pid; -use crate::process::{Jobs, IO}; +use std::io::{Stdin, Stdout}; #[cfg(feature = "raw")] use { + crate::repl::action::{Action, ActionContext}, termion::cursor::DetectCursorPos, termion::event::Key, termion::input::TermRead, termion::raw::IntoRawMode, - crate::repl::action::{Action, ActionContext}, }; #[cfg(not(feature = "raw"))] use { - std::io::BufRead, crate::program::{parse_and_run, Runtime}, + std::io::BufRead, }; #[cfg(feature = "history")] @@ -45,9 +45,13 @@ use self::history::History; /// ``` // TODO: Partial syntax, completion. #[allow(unused_mut)] -pub fn start(mut stdin: Stdin, mut stdout: Stdout, io: &mut IO, jobs: &mut Jobs, args: &mut ArgvMap) - -> crate::program::Result -{ +pub fn start( + mut stdin: Stdin, + mut stdout: Stdout, + io: &mut IO, + jobs: &mut Jobs, + args: &mut ArgvMap, +) -> crate::program::Result { // Load history from file in $HOME. #[cfg(feature = "history")] let mut history = History::load(); @@ -63,8 +67,7 @@ pub fn start(mut stdin: Stdin, mut stdout: Stdout, io: &mut IO, jobs: &mut Jobs, #[cfg(feature = "raw")] fn raw_loop(stdin: Stdin, stdout: Stdout, io: &mut IO, jobs: &mut Jobs, args: &mut ArgvMap) { // Convert the tty's stdout into raw mode. - let mut stdout = stdout.into_raw_mode() - .expect("error opening raw mode"); + let mut stdout = stdout.into_raw_mode().expect("error opening raw mode"); // Display the initial prompt. prompt::ps1(&mut stdout); @@ -112,30 +115,36 @@ fn raw_loop(stdin: Stdin, stdout: Stdout, io: &mut IO, jobs: &mut Jobs, args: &m } #[cfg(not(feature = "raw"))] -fn buffered_loop(stdin: Stdin, mut stdout: Stdout, io: &mut IO, jobs: &mut Jobs, args: &mut ArgvMap) { +fn buffered_loop( + stdin: Stdin, + mut stdout: Stdout, + io: &mut IO, + jobs: &mut Jobs, + args: &mut ArgvMap, +) { // Display the initial prompt. prompt::ps1(&mut stdout); for line in stdin.lock().lines() { - let line = line.unwrap(); // TODO: Exit codes - // let readline = runtime.rl.as_mut().unwrap().readline(&prompt); - // match readline { - // Ok(line) => { - // }, - // // Err(ReadlineError::Interrupted) => { - // // println!("^C"); - // // continue; - // // }, - // // Err(ReadlineError::Eof) => { - // // println!("exit"); - // // code = 0; - // // break; - // // }, - // Err(err) => { - // println!("error: {:?}", err); - // code = 130; - // break; - // } + let line = line.unwrap(); // TODO: Exit codes + // let readline = runtime.rl.as_mut().unwrap().readline(&prompt); + // match readline { + // Ok(line) => { + // }, + // // Err(ReadlineError::Interrupted) => { + // // println!("^C"); + // // continue; + // // }, + // // Err(ReadlineError::Eof) => { + // // println!("exit"); + // // code = 0; + // // break; + // // }, + // Err(err) => { + // println!("error: {:?}", err); + // code = 130; + // break; + // } let mut runtime = Runtime { background: false, io: io.clone(), @@ -158,10 +167,10 @@ fn buffered_loop(stdin: Stdin, mut stdout: Stdout, io: &mut IO, jobs: &mut Jobs, } // pub mod display; -pub mod prompt; #[cfg(feature = "raw")] pub mod action; #[cfg(feature = "completion")] pub mod completion; #[cfg(feature = "history")] pub mod history; +pub mod prompt; diff --git a/src/repl/prompt.rs b/src/repl/prompt.rs index a2a14092..79668444 100644 --- a/src/repl/prompt.rs +++ b/src/repl/prompt.rs @@ -1,7 +1,7 @@ +use crate::{NAME, VERSION}; +use nix::unistd; use std::env; use std::io::Write; -use nix::unistd; -use crate::{NAME, VERSION}; /// TODO: docs pub fn ps1(stdout: &mut impl Write) { @@ -15,9 +15,11 @@ fn expand_prompt(prompt: String) -> String { let mut command = false; let mut octal = vec![]; for c in prompt.chars() { - let o = octal.iter().map(|c: &char| c.to_string()) - .collect::>() - .join(""); + let o = octal + .iter() + .map(|c: &char| c.to_string()) + .collect::>() + .join(""); if !octal.is_empty() && octal.len() < 3 { if ('0'..'8').contains(&c) { octal.push(c); @@ -44,7 +46,10 @@ fn expand_prompt(prompt: String) -> String { 'w' => env::var("PWD").unwrap_or_else(|_| "".into()), 's' => NAME.into(), 'v' => VERSION[0..(VERSION.len() - 2)].into(), - '0' => { octal.push(c); "".into() }, + '0' => { + octal.push(c); + "".into() + } '\\' => "".into(), c => c.into(), }; diff --git a/tests/common/mod.rs b/tests/common/mod.rs index a57a58a9..d9261a58 100644 --- a/tests/common/mod.rs +++ b/tests/common/mod.rs @@ -13,14 +13,13 @@ macro_rules! shell { .expect("error swawning oursh process"); { - let stdin = child.stdin.as_mut() - .expect("error opening stdin"); - stdin.write_all($text.as_bytes()) + let stdin = child.stdin.as_mut().expect("error opening stdin"); + stdin + .write_all($text.as_bytes()) .expect("error writing to stdin"); } - let output = child.wait_with_output() - .expect("error reading stdout"); + let output = child.wait_with_output().expect("error reading stdout"); output }}; @@ -35,8 +34,7 @@ macro_rules! shell { .spawn() .expect("error swawning oursh process"); - let output = child.wait_with_output() - .expect("error reading stdout"); + let output = child.wait_with_output().expect("error reading stdout"); output }}; @@ -155,17 +153,30 @@ macro_rules! assert_posix { (! $text:expr) => {{ use std::process::Output; - let Output { status: oursh_status, .. } = oursh!($text); - let Output { status: sh_status, .. } = sh!($text); + let Output { + status: oursh_status, + .. + } = oursh!($text); + let Output { + status: sh_status, .. + } = sh!($text); assert_eq!(sh_status, oursh_status); }}; ($text:expr) => {{ use std::process::Output; - let Output { status: oursh_status, stdout: oursh_stdout, stderr: oursh_stderr } = oursh!($text); + let Output { + status: oursh_status, + stdout: oursh_stdout, + stderr: oursh_stderr, + } = oursh!($text); let oursh_stdout = String::from_utf8_lossy(&oursh_stdout); let oursh_stderr = String::from_utf8_lossy(&oursh_stderr); - let Output { status: sh_status, stdout: sh_stdout, stderr: sh_stderr } = sh!($text); + let Output { + status: sh_status, + stdout: sh_stdout, + stderr: sh_stderr, + } = sh!($text); let sh_stdout = String::from_utf8_lossy(&sh_stdout); let sh_stderr = String::from_utf8_lossy(&sh_stderr); assert!(oursh_status.success()); @@ -176,13 +187,24 @@ macro_rules! assert_posix { ($text:expr, $stdout:expr) => {{ use std::process::Output; - let Output { status: oursh_status, stdout: oursh_stdout, stderr: oursh_stderr } = oursh!($text); + let Output { + status: oursh_status, + stdout: oursh_stdout, + stderr: oursh_stderr, + } = oursh!($text); let oursh_stdout = String::from_utf8_lossy(&oursh_stdout); let oursh_stderr = String::from_utf8_lossy(&oursh_stderr); - let Output { status: sh_status, stdout: sh_stdout, stderr: sh_stderr } = sh!($text); + let Output { + status: sh_status, + stdout: sh_stdout, + stderr: sh_stderr, + } = sh!($text); let sh_stdout = String::from_utf8_lossy(&sh_stdout); let sh_stderr = String::from_utf8_lossy(&sh_stderr); - println!("oursh_stdout: {}\noursh_stderr: {}", oursh_stdout, oursh_stderr); + println!( + "oursh_stdout: {}\noursh_stderr: {}", + oursh_stdout, oursh_stderr + ); println!("sh_stdout: {}\nsh_stderr: {}", sh_stdout, sh_stderr); assert_eq!(sh_status, oursh_status); assert_eq!(sh_stdout, oursh_stdout); diff --git a/tests/oursh.rs b/tests/oursh.rs index 35cfea9f..d20a0598 100644 --- a/tests/oursh.rs +++ b/tests/oursh.rs @@ -4,12 +4,15 @@ mod common; #[cfg(feature = "shebang-block")] fn shebang_block_sh_command() { assert_oursh!("{#!/bin/sh; echo '1'}", "1\n"); - assert_oursh!(r#"{#!/bin/sh; + assert_oursh!( + r#"{#!/bin/sh; for i in 1 2 3 4 5 do echo -n $i done -}"#, "12345"); +}"#, + "12345" + ); } #[test] @@ -23,28 +26,37 @@ fn shebang_block_ruby_command() { fn shebang_block_python_command() { assert_oursh!("{#!/usr/bin/env python; print(1)}", "1\n"); assert_oursh!("{#!/usr/bin/env python ; print(1)}", "1\n"); - assert_oursh!(r#"{#!/usr/bin/env python; + assert_oursh!( + r#"{#!/usr/bin/env python; print("hello world") -}"#, "hello world\n"); +}"#, + "hello world\n" + ); } #[test] #[ignore] #[cfg(feature = "shebang-block")] fn shebang_block_racket_command() { - assert_oursh!(r#"{#!/usr/bin/env racket; + assert_oursh!( + r#"{#!/usr/bin/env racket; #lang racket/base (print "hello world!") -}"#, "\"hello world!\""); +}"#, + "\"hello world!\"" + ); } #[test] #[ignore] #[cfg(feature = "shebang-block")] fn shebang_block_rust_command() { - assert_oursh!(r#"{#!/usr/bin/env cargo-script-run; + assert_oursh!( + r#"{#!/usr/bin/env cargo-script-run; fn main() { println!("hello world!"); } -}"#, "hello world!\n"); +}"#, + "hello world!\n" + ); } diff --git a/tests/posix.rs b/tests/posix.rs index 4b440fe5..55c46d17 100644 --- a/tests/posix.rs +++ b/tests/posix.rs @@ -16,7 +16,7 @@ fn builtin_cd() { #[test] fn builtin_exit() { assert_posix!("exit"); - assert_posix!(! "exit 1"); + assert_posix!(!"exit 1"); } #[test] @@ -50,8 +50,8 @@ fn chained_command() { #[test] fn single_compound_command() { assert_posix!("{ echo pi; }", "pi\n"); - assert_posix!("{echo pi; }"); // NOTE: Fails in sh - // assert_posix!("{echo pi}"); // NOTE: Allowed in zsh + assert_posix!("{echo pi; }"); // NOTE: Fails in sh + // assert_posix!("{echo pi}"); // NOTE: Allowed in zsh } #[test] @@ -69,14 +69,14 @@ fn multiple_tee_command() { #[test] fn not_command() { - assert_posix!(! "! true"); - assert_posix!(! "! true && echo 1"); + assert_posix!(!"! true"); + assert_posix!(!"! true && echo 1"); } #[test] fn and_command() { assert_posix!("true && echo 1", "1\n"); - assert_posix!( !"false && echo 1"); + assert_posix!(!"false && echo 1"); } #[test] @@ -89,8 +89,14 @@ fn or_command() { fn cond_command() { assert_posix!("if true; then echo 1; else echo 2; fi", "1\n"); assert_posix!("if false; then echo 1; else echo 2; fi", "2\n"); - assert_posix!("if false; then echo 1; elif false; then echo 2; else echo 3; fi", "3\n"); - assert_posix!("if false; then echo 1; elif true; then echo 2; else echo 3; fi", "2\n"); + assert_posix!( + "if false; then echo 1; elif false; then echo 2; else echo 3; fi", + "3\n" + ); + assert_posix!( + "if false; then echo 1; elif true; then echo 2; else echo 3; fi", + "2\n" + ); } #[test]