diff options
author | Rory D | 2024-02-19 17:29:54 -0700 |
---|---|---|
committer | GitHub | 2024-02-19 17:29:54 -0700 |
commit | 52422d40316f4d836a96a15709c2f4fad7c96d3b (patch) | |
tree | 14552e07c1f6e5bdcc053fee86b3c78ce84cad1f | |
parent | 6cf0f1b12e880f3cd88f013a070406bfb303831a (diff) | |
parent | d98ab111eebfcf5e6327daaec17b5de578d64a41 (diff) | |
download | dwarvish-52422d40316f4d836a96a15709c2f4fad7c96d3b.tar.gz |
Merge pull request #2 from pinecat/evryloop
Path refresh
-rw-r--r-- | src/main.rs | 126 |
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); } |