/src/parse/linked_command/mod.rs
Rust | 335 lines | 296 code | 38 blank | 1 comment | 64 complexity | a4433786e24c552552f7653e3b706c8f MD5 | raw file
- use std::process::{Command, Stdio, Child, ExitStatus, exit};
- use std::path::PathBuf;
- use std::fs::File;
- use std::error;
- use std::io::{Error, copy, ErrorKind};
- use std::fmt;
- use std::env;
- use std::ffi::OsString;
- use std::thread;
- use std::collections::HashMap;
- use std::error::Error as stdError;
- use BINDINGS;
- use ALIAS;
- #[derive(Debug)]
- pub struct LinkedCommand {
- pub command: Spawn,
- pub piped_to: Option<Box<LinkedCommand>>,
- pub and: Option<Box<LinkedCommand>>,
- pub file_out: Option<PathBuf>,
- pub file_in: Option<PathBuf>,
- }
- impl LinkedCommand {
- pub fn new(name: String) -> LinkedCommand {
- if let Some(builtin) = BuiltIn::find(name.clone()) {
- LinkedCommand {
- command: Spawn::BuiltIn(builtin),
- piped_to: None,
- and: None,
- file_out: None,
- file_in: None,
- }
- } else {
- LinkedCommand {
- command: Spawn::Com(Command::new(name)),
- piped_to: None,
- and: None,
- file_out: None,
- file_in: None,
- }
- }
- }
- fn piped_input(&mut self) {
- match self.command {
- Spawn::BuiltIn(_) => (),
- Spawn::Com(ref mut command) => {
- command.stdin(Stdio::piped());
- }
- }
- }
- fn execute(mut self) -> Result<Spawned, Error> {
- match self.command {
- Spawn::BuiltIn(mut builtin) => {
- if let Some(ref mut next) = self.piped_to {
- next.piped_input();
- }
- let output = try!(builtin.spawn());
- if let Some(next) = self.piped_to {
- let next_command = try!(next.execute());
- if let Spawned::Com(mut next_child) = next_command {
- let mut stdin = next_child.stdin.take().unwrap();
- thread::spawn(move || copy(&mut output.as_bytes(), &mut stdin));
- }
- } else if let Some(ref mut file_out) = self.file_out {
- let mut file = try!(File::create(file_out));
- thread::spawn(move || copy(&mut output.as_bytes(), &mut file));
- }
- Ok(Spawned::BuiltIn(builtin))
- }
- Spawn::Com(ref mut command) => {
- if let Some(ref mut next) = self.piped_to {
- next.piped_input();
- command.stdout(Stdio::piped());
- } else if let Some(_) = self.file_out {
- command.stdout(Stdio::piped());
- }
- if self.file_in.is_some() {
- command.stdin(Stdio::piped());
- }
- let mut child = try!(command.spawn());
- if let Some(ref file_in) = self.file_in {
- let mut file = try!(File::create(file_in));
- let mut stdin = child.stdin.take().unwrap();
- thread::spawn(move || copy(&mut file, &mut stdin));
- }
- if let Some(next) = self.piped_to {
- let mut next_command = try!(next.execute());
- if let Spawned::Com(ref mut next_child) = next_command {
- let stdout = child.stdout.take();
- let stdin = next_child.stdin.take();
- if stdout.is_some() {
- if stdin.is_some() {
- thread::spawn(move || {
- copy(&mut stdout.unwrap(), &mut stdin.unwrap())
- });
- } else {
- panic!();
- }
- } else {
- panic!();
- }
- }
- } else if let Some(ref mut file_out) = self.file_out {
- let mut file = try!(File::create(file_out));
- try!(copy(child.stdout.as_mut().unwrap(), &mut file));
- }
- if let Some(and) = self.and {
- try!(and.execute());
- }
- Ok(Spawned::Com(child))
- }
- }
- }
- pub fn spawn(self) -> Result<ExStatus, Error> {
- try!(self.execute()).wait()
- }
- pub fn arg(&mut self, arg: String) {
- self.command.arg(arg);
- }
- }
- #[derive(Debug)]
- pub struct BuiltIn {
- name: String,
- args: Vec<String>,
- output: String,
- exit_status: ExStatus,
- env: HashMap<String, String>,
- }
- #[derive(Debug)]
- pub enum Spawn {
- Com(Command),
- BuiltIn(BuiltIn),
- }
- pub enum Spawned {
- Com(Child),
- BuiltIn(BuiltIn),
- }
- impl Spawned {
- fn wait(self) -> Result<ExStatus, Error> {
- match self {
- Spawned::Com(mut child) => Ok(ExStatus::from(try!(child.wait()))),
- Spawned::BuiltIn(builtin) => Ok(builtin.exit_status),
- }
- }
- }
- #[derive(Debug)]
- pub struct ExStatus {
- code: Option<i32>,
- exit_status: Option<ExitStatus>,
- }
- impl From<ExitStatus> for ExStatus {
- fn from(exit_status: ExitStatus) -> ExStatus {
- ExStatus {
- code: None,
- exit_status: Some(exit_status),
- }
- }
- }
- impl ExStatus {
- fn new(code: i32) -> ExStatus {
- ExStatus {
- code: Some(code),
- exit_status: None,
- }
- }
- }
- impl Spawn {
- fn arg(&mut self, arg: String) {
- match *self {
- Spawn::Com(ref mut command) => {
- command.arg(arg);
- }
- Spawn::BuiltIn(ref mut builtin) => {
- builtin.arg(arg);
- }
- }
- }
- }
- impl BuiltIn {
- pub fn find(name: String) -> Option<BuiltIn> {
- if Self::is_built_in(name.clone()) {
- Some(BuiltIn {
- name: name,
- args: vec![],
- output: String::new(),
- exit_status: ExStatus::new(1),
- env: HashMap::new(),
- })
- } else {
- None
- }
- }
- fn is_built_in(name: String) -> bool {
- &name == "exit" || &name == "cd" || &name == "export" || &name == "alias"
- }
- fn spawn(&mut self) -> Result<String, Error> {
- if &self.name == "exit" {
- self.exit()
- } else if &self.name == "cd" {
- self.cd()
- } else if &self.name == "export" {
- self.export()
- } else if &self.name == "alias" {
- self.alias()
- } else {
- Err(Error::from(BuiltInError::NoSuchBuiltIn(self.name.clone())))
- }
- }
- fn arg(&mut self, arg: String) {
- self.args.push(arg);
- }
- // builtins
- fn exit(&self) -> Result<String, Error> {
- exit(0);
- }
- fn cd(&self) -> Result<String, Error> {
- let current = try!(env::current_dir());
- if self.args.is_empty() {
- Err(Error::from(BuiltInError::NoArgument(String::from("cd"))))
- } else {
- let path = PathBuf::from(self.args[0].clone());
- let dest = current.join(path);
- try!(env::set_current_dir(dest));
- let new_dir = try!(env::current_dir());
- match new_dir.into_os_string().into_string() {
- Ok(string) => Ok(string),
- Err(os_string) => Err(Error::from(BuiltInError::InvalidUnicode(os_string))),
- }
- }
- }
- fn alias(&self) -> Result<String, Error> {
- if self.args[0] == "list".to_string() {
- let mut response = String::new();
- for (name, value) in ALIAS.lock().unwrap().iter() {
- response.push_str(&format!("{:?}: {:?}\n", name, value));
- }
- println!("{}", response);
- Ok(response)
- } else if self.args.len() < 2 {
- Err(Error::from(BuiltInError::NoArgument(String::from("alias"))))
- } else {
- let mut alias = ALIAS.lock().unwrap();
- alias.insert(self.args[0].clone(), self.args[1].clone());
- println!("{}: {}", self.args[0].clone(), self.args[1].clone());
- Ok(self.args[1].clone())
- }
- }
- fn export(&mut self) -> Result<String, Error> {
- let arg = self.args.clone();
- let mut args = arg.iter();
- loop {
- if let Some(name) = args.next() {
- if name.ends_with("=") {
- let mut sane_name = name.clone();
- sane_name.pop();
- if let Some(val) = args.next() {
- env::set_var(sane_name, val);
- } else {
- env::set_var(sane_name, String::new());
- break;
- }
- } else {
- let val =
- BINDINGS.lock().unwrap().get(name).unwrap_or(&String::from("")).clone();
- env::set_var(name, val);
- }
- } else {
- break;
- }
- }
- Ok(String::new())
- }
- }
- #[derive(Debug)]
- pub enum BuiltInError {
- NoSuchBuiltIn(String),
- NoArgument(String),
- InvalidUnicode(OsString),
- }
- impl error::Error for BuiltInError {
- fn description(&self) -> &str {
- match *self {
- BuiltInError::NoSuchBuiltIn(_) => "Attempted to parse a builtin, but no such builtin",
- BuiltInError::NoArgument(_) => "Expected argument, but got none",
- BuiltInError::InvalidUnicode(_) => "Invalid Unicode",
- }
- }
- }
- impl fmt::Display for BuiltInError {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- write!(f, "{}", self.description())
- }
- }
- impl From<BuiltInError> for Error {
- fn from(err: BuiltInError) -> Error {
- Error::new(ErrorKind::Other, err)
- }
- }