summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRory D2024-02-19 17:29:54 -0700
committerGitHub2024-02-19 17:29:54 -0700
commit52422d40316f4d836a96a15709c2f4fad7c96d3b (patch)
tree14552e07c1f6e5bdcc053fee86b3c78ce84cad1f
parent6cf0f1b12e880f3cd88f013a070406bfb303831a (diff)
parentd98ab111eebfcf5e6327daaec17b5de578d64a41 (diff)
downloaddwarvish-52422d40316f4d836a96a15709c2f4fad7c96d3b.tar.gz
Merge pull request #2 from pinecat/evryloop
Path refresh
-rw-r--r--src/main.rs126
1 files changed, 106 insertions, 20 deletions
diff --git a/src/main.rs b/src/main.rs
index 7fc8991..1dc0217 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,20 +1,70 @@
use ctrlc;
use std::fs;
-use std::io;
-use std::io::Write;
+use std::io::{self, Write};
use std::path::Path;
use std::process::Command;
-fn eval(paths: &[&str], prompt: &str) {
+/// Refresh the shell's $PATH
+///
+/// This function caches all valid paths within within the directories
+/// specified.
+///
+/// # Arguments
+/// * `paths` - A reference to a vector that holds a list to the shell $PATHs
+///
+/// # Returns
+/// * `bins: Vec<String>` - A new cache of all valid file paths in $PATH
+///
+/// # Examples
+/// ```
+/// let paths = vec!["/bin"];
+/// let paths = paths.into_iter().map(Path::new).collect();
+/// let mut bins = prefresh(&paths);
+/// ...
+/// // A situation occurs where the $PATH needs to be refreshed
+/// bins = prefresh(&paths)
+/// ```
+fn prefresh(paths: &Vec<&Path>) -> Vec<String> {
let mut bins: Vec<String> = Vec::new();
for path in paths {
- let files = fs::read_dir(path).expect("Unable to read files in your path");
+ let files = fs::read_dir(path).expect(
+ format!(
+ "dwvsh: error: unable to read the contents of {}",
+ path.display().to_string()
+ )
+ .as_str(),
+ );
+
for file in files {
bins.push(file.unwrap().path().display().to_string());
}
}
+ bins
+}
+
+/// Starts the main shell loop
+///
+/// # Arguments
+/// * `paths` - A reference to a vector that holds a list to the shell $PATHs
+/// * `prompt` - A string slice indicating the shell's prompt
+///
+/// # Examples
+/// ```
+/// fn main() {
+/// let paths = vec!["/bin"];
+/// let paths = paths.into_iter().map(Path::new).collect();
+/// let prompt = "|> ";
+/// ...
+/// repl(&paths, prompt);
+/// }
+/// ```
+fn repl(paths: &Vec<&Path>, prompt: &str) {
+ // Initial path refresh on startup
+ let mut bins: Vec<String> = prefresh(paths);
+
+ // Main shell loop
loop {
// Output the prompt
io::stdout().flush().unwrap();
@@ -25,7 +75,7 @@ fn eval(paths: &[&str], prompt: &str) {
let mut input = String::new();
let bytes = io::stdin()
.read_line(&mut input)
- .expect("Unable to evaluate the input string");
+ .expect("dwvsh: error: unable to evaluate the input string");
// Check if we've reached EOF (i.e. <C-d>)
if bytes == 0 {
@@ -41,11 +91,11 @@ fn eval(paths: &[&str], prompt: &str) {
break;
}
- // Parse command and arguments
+ // Parse command
let mut split = input.split(' ');
let mut cmd = match split.next() {
Some(str) if str.trim().is_empty() => continue,
- Some(str) => str.trim(),
+ Some(str) => String::from(str.trim()),
None => continue,
};
@@ -69,7 +119,7 @@ fn eval(paths: &[&str], prompt: &str) {
match std::env::set_current_dir(path) {
Ok(_) => continue,
Err(_) => {
- println!("cd: Unable to change into {}", path);
+ println!("cd: unable to change into {}", path);
continue;
}
}
@@ -77,23 +127,55 @@ fn eval(paths: &[&str], prompt: &str) {
// 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).exists() {
+ 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
- cmd = match bins.iter().find(|b| b.split("/").last().unwrap() == cmd) {
- Some(cmd) => cmd,
+ //
+ // 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 => {
- println!("Command not found");
- continue;
+ 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() {
+ let mut child = match Command::new(&cmd).args(args).spawn() {
Ok(ch) => ch,
- Err(_) => {
- println!("Unable to fork");
+ 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)
+ }
+
+ // 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);
+ }
+
+ // Otherwise print the OS error
+ _ => println!("dwvsh: fork: {}", err),
+ }
+
+ // Restart the loop
continue;
}
};
@@ -101,10 +183,13 @@ fn eval(paths: &[&str], prompt: &str) {
}
}
+/// Shell entry
+///
+/// Shell setup and entry
fn main() {
// Define paths
// TODO: Hardcoded path should only be the fallback
- let paths = [
+ let paths = vec![
"/bin",
"/sbin",
"/usr/bin",
@@ -112,6 +197,7 @@ fn main() {
"/usr/local/bin",
"/usr/local/sbin",
];
+ let paths = paths.into_iter().map(Path::new).collect();
// Set the prompt
let prompt = "|> ";
@@ -121,8 +207,8 @@ fn main() {
print!("\n{}", prompt);
io::stdout().flush().unwrap();
})
- .expect("Unable to set <C-c> handler");
+ .expect("dwvsh: signals: unable to set sigint handler");
// Begin evaluating commands
- eval(&paths, prompt);
+ repl(&paths, prompt);
}