summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/main.rs367
1 files changed, 153 insertions, 214 deletions
diff --git a/src/main.rs b/src/main.rs
index 09fc70a..d167d04 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,86 +1,9 @@
+use core::fmt;
use ctrlc;
use std::fs;
use std::io::{self, Write};
use std::path::Path;
-use std::process::{Command, Stdio};
-
-// #[derive(Debug)]
-// enum Meter {
-// Verb, // A command (and possible some arguments)
-// Pipe, // Pipe the output of this command into the next
-// Daemon, // Fork the command into the background
-// And, // Run the next command only if this succeeds
-// }
-
-// impl fmt::Display for Meter {
-// fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
-// let meter = match self {
-// Meter::Verb => "verb",
-// Meter::Pipe => "pipe",
-// Meter::Daemon => "daemon",
-// Meter::And => "and",
-// };
-// write!(f, "{}", meter)
-// }
-// }
-
-// #[derive(Debug)]
-// struct Stanza {
-// verb: Option<String>,
-// clause: Option<Vec<String>>,
-// }
-
-// impl Stanza {
-// fn new(line: String) -> Stanza {
-// let mut split = line.split(' ');
-// let cmd = match split.next() {
-// Some(verb) if verb.trim().is_empty() => None,
-// Some(verb) => Some(String::from(verb.trim())),
-// None => None,
-// };
-
-// let mut args: Option<Vec<String>>;
-// match cmd {
-// Some(_) => {
-// args = Some(Vec::new());
-// loop {
-// let next = split.next();
-// match next {
-// Some(clause) => args.unwrap().push(clause.to_string()),
-// None => break,
-// }
-// }
-// }
-// None => args = None,
-// }
-
-// Stanza {
-// verb: cmd,
-// clause: args,
-// }
-// }
-// }
-
-// #[derive(Debug)]
-// struct Verse {
-// meter: Meter,
-// stanza: Option<Stanza>,
-// }
-
-// #[derive(Debug)]
-// struct Poem {
-// verses: Vec<Verse>,
-// }
-
-// impl Poem {
-// fn new() -> Poem {
-// Poem { verses: Vec::new() }
-// }
-
-// fn recite(&self) {
-// unimplemented!();
-// }
-// }
+use std::process::{exit, Command, Stdio};
#[derive(Debug)]
enum Meter {
@@ -88,6 +11,21 @@ enum Meter {
Pipe, // Pipe the output of this command into the next
Daemon, // Fork the command into the background
And, // Run the next command only if this succeeds
+ String, // Run the next command, even if this doesn't succeed
+}
+
+impl fmt::Display for Meter {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ let meter = match self {
+ Meter::None => "",
+ Meter::Pipe => "|",
+ Meter::Daemon => "&",
+ Meter::And => "&&",
+ Meter::String => ";",
+ };
+
+ write!(f, "{}", meter)
+ }
}
#[derive(Debug)]
@@ -152,6 +90,12 @@ impl Verse {
}
}
+impl fmt::Display for Verse {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(f, "{} {}", self.verb(), self.clause().join(" "))
+ }
+}
+
#[derive(Debug)]
struct Poem {
verses: Vec<Verse>,
@@ -167,10 +111,10 @@ impl Poem {
let mut out: String = String::new();
for verse in self.verses.iter() {
- // let verse = match self.verses.iter().next() {
- // Some(verse) => verse,
- // None => break,
- // };
+ // Check if user wants to exit the shell
+ if verse.verb() == "exit" || verse.verb() == "quit" {
+ exit(0);
+ }
if !verse.spellcheck(bins) {
*bins = prefresh(paths);
@@ -190,12 +134,13 @@ impl Poem {
.spawn()
.expect("dwvsh: error 0");
- let mut stdin = child.stdin.as_mut().expect("dwvsh: error 6");
+ let stdin = child.stdin.as_mut().expect("dwvsh: error 6");
stdin.write_all(&out.as_bytes()).expect("dwvsh: error 7");
out.clear();
let output = child.wait_with_output().unwrap();
- out = String::from_utf8_lossy(&output.stdout).to_string();
+ // out = String::from_utf8_lossy(&output.stdout).to_string();
+ out = String::from_utf8(output.stdout).unwrap();
}
Meter::Daemon => {
let mut child = Command::new(verse.verb())
@@ -204,7 +149,25 @@ impl Poem {
.spawn()
.expect("dwvsh: error 1");
- let mut stdin = child.stdin.as_mut().expect("dwvsh: error 8");
+ let stdin = child.stdin.as_mut().expect("dwvsh: error 8");
+ stdin.write_all(&out.as_bytes()).expect("dwvsh: error 9");
+ out.clear();
+
+ print!("[f] {}", child.id());
+ // let p = prompt.to_owned();
+ std::thread::spawn(move || {
+ child.wait().unwrap();
+ println!("[f] +done {}", child.id());
+ io::stdout().flush().unwrap();
+ });
+ }
+ Meter::String => {
+ let mut child = Command::new(verse.verb())
+ .args(verse.clause())
+ .spawn()
+ .expect("dwvsh: error 5");
+
+ let stdin = child.stdin.as_mut().expect("dwvsh: error 8");
stdin.write_all(&out.as_bytes()).expect("dwvsh: error 9");
out.clear();
@@ -217,17 +180,19 @@ impl Poem {
.spawn()
.expect("dwvsh: error 2");
- let mut stdin = child.stdin.as_mut().expect("dwvsh: error 10");
+ let stdin = child.stdin.as_mut().expect("dwvsh: error 10");
stdin.write_all(&out.as_bytes()).expect("dwvsh: error 11");
out.clear();
- child.wait().unwrap();
+ if !child.wait().unwrap().success() {
+ break;
+ }
}
};
} else {
match verse.meter {
Meter::Pipe => {
- let mut child = Command::new(verse.verb())
+ let child = Command::new(verse.verb())
.args(verse.clause())
.stdout(Stdio::piped())
.spawn()
@@ -235,6 +200,7 @@ impl Poem {
let output = child.wait_with_output().unwrap();
out = String::from_utf8_lossy(&output.stdout).to_string();
+ // out = String::from_utf8(output.stdout).unwrap();
}
Meter::Daemon => {
let mut child = Command::new(verse.verb())
@@ -242,6 +208,19 @@ impl Poem {
.spawn()
.expect("dwvsh: error 4");
+ println!("[f] {}", child.id());
+ std::thread::spawn(move || {
+ child.wait().unwrap();
+ print!("[f] +done {}\n", child.id());
+ io::stdout().flush().unwrap();
+ });
+ }
+ Meter::String => {
+ let mut child = Command::new(verse.verb())
+ .args(verse.clause())
+ .spawn()
+ .expect("dwvsh: error 5");
+
child.wait().unwrap();
}
Meter::And | Meter::None => {
@@ -250,7 +229,9 @@ impl Poem {
.spawn()
.expect("dwvsh: error 5");
- child.wait().unwrap();
+ if !child.wait().unwrap().success() {
+ break;
+ }
}
};
}
@@ -260,44 +241,94 @@ impl Poem {
}
}
-fn read(poetry: String) -> Poem {
- let mut words = poetry.split(' ');
+fn read(poetry: String) -> Option<Poem> {
+ let mut chars = poetry.chars();
let mut verses: Vec<Verse> = Vec::new();
let mut stanza: Vec<String> = Vec::new();
-
+ let mut word: Vec<char> = Vec::new();
let mut prev: Option<&Verse> = None;
+
loop {
- let word = words.next();
- let stdin = match prev {
+ let char = chars.next();
+
+ let pipe = match prev {
Some(prev) => match prev.meter {
Meter::Pipe => true,
- Meter::Daemon | Meter::And | Meter::None => false,
+ Meter::Daemon | Meter::And | Meter::String | Meter::None => false,
+ },
+ None => false,
+ };
+
+ let metered = match prev {
+ Some(prev) => match prev.meter {
+ Meter::Pipe | Meter::Daemon | Meter::And | Meter::String => true,
+ Meter::None => false,
},
None => false,
};
- match word {
- Some(verb) if verb == "|" => {
- verses.push(Verse::new(Stanza::new(stanza.clone()), Meter::Pipe, stdin));
+ match char {
+ Some(meter)
+ if (meter == '|' || meter == '&' || meter == ';')
+ && metered
+ && stanza.is_empty() =>
+ {
+ println!("dwvsh: parse error");
+ return None;
+ }
+ Some(meter) if meter == '|' => {
+ if !word.is_empty() {
+ stanza.push(word.iter().collect());
+ }
+ verses.push(Verse::new(Stanza::new(stanza.clone()), Meter::Pipe, pipe));
stanza = Vec::new();
+ word.clear();
}
- Some(verb) if verb == "&" => {
- verses.push(Verse::new(
- Stanza::new(stanza.clone()),
- Meter::Daemon,
- stdin,
- ));
+ Some(meter) if meter == '&' => {
+ if !word.is_empty() {
+ stanza.push(word.iter().collect());
+ }
+
+ match chars.clone().peekable().peek() {
+ Some(c) if c == &'&' => {
+ chars.next();
+ verses.push(Verse::new(Stanza::new(stanza.clone()), Meter::And, pipe));
+ }
+ Some(_) => {
+ verses.push(Verse::new(Stanza::new(stanza.clone()), Meter::Daemon, pipe));
+ }
+ None => {
+ verses.push(Verse::new(Stanza::new(stanza.clone()), Meter::Daemon, pipe));
+ }
+ }
+
stanza = Vec::new();
+ word.clear();
}
- Some(verb) if verb == "&&" => {
- verses.push(Verse::new(Stanza::new(stanza.clone()), Meter::And, stdin));
+ Some(meter) if meter == ';' => {
+ if !word.is_empty() {
+ stanza.push(word.iter().collect());
+ }
+ verses.push(Verse::new(Stanza::new(stanza.clone()), Meter::String, pipe));
stanza = Vec::new();
+ word.clear();
+ }
+ Some(char) if char == ' ' => {
+ if !word.is_empty() {
+ stanza.push(word.iter().collect());
+ word.clear();
+ }
}
- Some(verb) => {
- stanza.push(verb.trim().to_string());
+ Some(char) => {
+ word.push(char);
}
None => {
- verses.push(Verse::new(Stanza::new(stanza.clone()), Meter::None, stdin));
+ if !word.is_empty() {
+ stanza.push(word.iter().collect());
+ }
+ if !stanza.is_empty() {
+ verses.push(Verse::new(Stanza::new(stanza.clone()), Meter::None, pipe));
+ }
break;
}
}
@@ -308,7 +339,7 @@ fn read(poetry: String) -> Poem {
};
}
- Poem::new(verses)
+ Some(Poem::new(verses))
}
/// Refresh the shell's $PATH
@@ -393,110 +424,18 @@ fn repl(paths: &Vec<&Path>, prompt: &str) {
// Trim the input
let poetry = String::from(poetry.trim());
- // Check if user wants to exit the shell
- if poetry == "exit" || poetry == "quit" {
- break;
+ // Skip parsing if there is no poetry
+ if !poetry.is_empty() {
+ // Parse a poem
+ let poem = read(poetry);
+ match poem {
+ Some(poem) => {
+ // poem.recite(paths, &mut bins, prompt);
+ poem.recite(paths, &mut bins);
+ }
+ None => {}
+ }
}
-
- // Parse a poem
- let poem = read(poetry);
- poem.recite(paths, &mut bins);
-
- // // Parse command
- // let mut split = poetry.split(' ');
- // let mut cmd = match split.next() {
- // Some(str) if str.trim().is_empty() => continue,
- // Some(str) => String::from(str.trim()),
- // None => continue,
- // };
-
- // // Parse arguments
- // let mut args = vec![];
- // loop {
- // let next = split.next();
- // match next {
- // Some(str) => args.push(str),
- // None => break,
- // }
- // }
-
- // // Check if user wants to change directories
- // if cmd == "cd" {
- // let path = match args.first() {
- // Some(str) => str,
- // None => env!("HOME"),
- // };
-
- // match std::env::set_current_dir(path) {
- // Ok(_) => continue,
- // Err(_) => {
- // println!("cd: unable to change into {}", path);
- // continue;
- // }
- // }
- // }
-
- // Check if the file exists, if given a pull or relative path
- // TODO: Check if file at the path is executable (i.e. +x)
- // if !Path::new(cmd.as_str()).exists() {
- // let b = bins.clone();
- // // Check if the command exists in $PATH if a full or relative path
- // // was not given, or if the path does not exist
- // //
- // // If the command is not found the first time, try refreshing the
- // // path first, and only print an error if if it's not found after
- // // the path refresh
- // cmd = match b
- // .clone()
- // .iter()
- // .find(|b| b.split("/").last().unwrap() == cmd)
- // {
- // Some(cmd) => cmd.clone(),
- // None => {
- // bins = prefresh(&paths);
- // match bins.iter().find(|b| b.split("/").last().unwrap() == cmd) {
- // Some(cmd) => cmd.clone(),
- // None => {
- // println!("dwvsh: {}: command not found", cmd);
- // continue;
- // }
- // }
- // }
- // }
- // }
-
- // Run the command (and wait for it to finish)
- // let mut child = match Command::new(&cmd).args(args).spawn() {
- // Ok(ch) => ch,
- // Err(err) => {
- // match err.kind() {
- // // Occurs when the user doesn't have read access, or if the +x bit is not set
- // std::io::ErrorKind::PermissionDenied => {
- // println!(
- // "dwvsh: permission denied: trying to fork to {}",
- // cmd.split("/").last().unwrap()
- // )
- // }
-
- // // Occurs if a command was removed from the path
- // // If this is the case, refresh the path
- // std::io::ErrorKind::NotFound => {
- // bins = prefresh(&paths);
- // println!(
- // "dwvsh: {}: command not found",
- // cmd.split("/").last().unwrap()
- // );
- // }
-
- // // Otherwise print the OS error
- // _ => println!("dwvsh: fork: {}", err),
- // }
-
- // // Restart the loop
- // continue;
- // }
- // };
- // child.wait().unwrap();
}
}