diff options
author | Rory Dudley | 2024-03-28 23:26:02 -0600 |
---|---|---|
committer | Rory Dudley | 2024-03-28 23:26:02 -0600 |
commit | 491d3fbff384d4b04483b54e5bb78d23bb1181c5 (patch) | |
tree | 470b0fc2ab0a476d682e104bdb03275ffd6b8671 | |
parent | 14a74aea0f02da53e0f61c572da2c5244ed80551 (diff) | |
download | dwarvish-491d3fbff384d4b04483b54e5bb78d23bb1181c5.tar.gz |
Remove hard-coded PATH
Use $PATH, instead of a hard-coded PATH from main(). This means that
there is no longer a need to pass around PATH to
repl()/recite()/path::refresh(), since path::refresh() can call env::var
directly.
Since the hard-coded paths were removed, there needs to be some way to
define $PATH. When running the debug build, dwvsh will look in
'dist/etc/dwvshrc' for the initial environment setup. For the release
target, dwvsh will look in '/etc/dwvshrc'. After the global rc file is
sourced, dwvsh will try to source ~/.dwvshrc if it exists, so users can
extend their environment without root access (assuming a release install).
Notes
Notes:
Throughout a lot of this program, we're calling `env!("HOME")`, in order
to get the user's home directory. Technically, this is not correct. The
env!() macro resolves environment variables during compile time, while
env::var() gets environment variables for the running process (i.e. the
shell). See https://users.rust-lang.org/t/env-vs-env-var/88119 for more
info. In the near future, this will need to be addressed. Might be worth
looking into what other shells do, though one idea I had was to invoke
'/usr/bin/id', grab the user's ID, and use it to grab the rest of the
info from /etc/passwd. This would be handled in an /etc/dwvlogin or
/etc/dwvprofile most likely.
-rw-r--r-- | dist/etc/dwvshrc | 3 | ||||
-rw-r--r-- | src/compose.rs | 58 | ||||
-rw-r--r-- | src/main.rs | 30 | ||||
-rw-r--r-- | src/path.rs | 30 | ||||
-rw-r--r-- | src/poem/anthology.rs | 5 | ||||
-rw-r--r-- | src/poem/anthology/source.rs | 7 | ||||
-rw-r--r-- | src/poem/recite.rs | 48 |
7 files changed, 100 insertions, 81 deletions
diff --git a/dist/etc/dwvshrc b/dist/etc/dwvshrc new file mode 100644 index 0000000..efb3f3d --- /dev/null +++ b/dist/etc/dwvshrc @@ -0,0 +1,3 @@ +#!/usr/bin/env dwvsh + +export PATH=/usr/local/bin:/usr/local/sbin:/usr/bin:/usr/sbin:/bin:/sbin diff --git a/src/compose.rs b/src/compose.rs new file mode 100644 index 0000000..8c4b3ea --- /dev/null +++ b/src/compose.rs @@ -0,0 +1,58 @@ +use crate::poem::{read::Readable, recite::Reciteable, Poem}; +use std::fs; +use std::path::PathBuf; + +pub fn env() { + // Use local repo path if running the debug target + let global_rc = if cfg!(debug_assertions) { + let mut base = PathBuf::from(env!("CARGO_MANIFEST_DIR")); + base.push("dist/etc/dwvshrc"); + base + } else { + PathBuf::from("/etc/dwvshrc") + }; + + // User defined rc file in ~ + let mut local_rc = PathBuf::from(env!("HOME")); + local_rc.push(".dwvshrc"); + + // Read, read, and recite + rrr(global_rc); + rrr(local_rc); +} + +fn rrr(path: PathBuf) { + let poetry = match fs::read_to_string(&path) { + Ok(poetry) => poetry, + Err(e) => { + if path.to_string_lossy().contains("etc/dwvshrc") { + eprintln!( + "dwvsh: unable to read global dwvshrc file: {}", + e.to_string().to_lowercase() + ); + } + return; + } + }; + + let poem = match Poem::read(poetry) { + Ok(poem) => poem, + Err(e) => { + eprintln!( + "dwvsh: error in {}: {}", + path.display(), + e.to_string().to_lowercase() + ); + return; + } + }; + + let mut bins = vec![]; + match poem.recite(&mut bins, None) { + Ok(_) => {} + Err(e) => { + eprintln!("dwvsh: {}", e.to_string().to_lowercase()); + return; + } + } +} diff --git a/src/main.rs b/src/main.rs index 951c46f..ce5f611 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,31 +1,28 @@ use std::io::{self, Write}; -use std::path::Path; use std::sync::{Arc, Mutex}; mod path; mod poem; use poem::{read::Readable, recite::Reciteable, Poem}; +mod compose; /// Starts the main shell loop /// /// # Arguments -/// * `path` - A reference to a vector that holds a list to the shell $PATHs /// * `prompt` - A string slice indicating the shell's prompt /// * `at_prompt` - A mutex, indicating whether or not user is at the prompt /// /// # Examples /// ``` /// fn main() { -/// let path = vec!["/bin"]; -/// let path = path.into_iter().map(Path::new).collect(); /// let prompt = "|> "; /// let mut at_prompt = Arc::new(Mutex::new(false)); /// ... -/// repl(&path, prompt, &mut at_prompt); +/// repl(prompt, &mut at_prompt); /// } /// ``` -fn repl(path: &Vec<&Path>, prompt: &str, at_prompt: &mut Arc<Mutex<bool>>) { +fn repl(prompt: &str, at_prompt: &mut Arc<Mutex<bool>>) { // Initial path refresh on startup - let mut bins: Vec<String> = path::refresh(path); + let mut bins: Vec<String> = path::refresh(); // Main shell loop loop { @@ -70,7 +67,7 @@ fn repl(path: &Vec<&Path>, prompt: &str, at_prompt: &mut Arc<Mutex<bool>>) { }; // Recite the poem - match poem.recite(path, &mut bins, None) { + match poem.recite(&mut bins, None) { Ok(_) => {} Err(e) => eprintln!("dwvsh: {}", e.to_string().to_lowercase()), } @@ -81,17 +78,10 @@ fn repl(path: &Vec<&Path>, prompt: &str, at_prompt: &mut Arc<Mutex<bool>>) { /// /// Shell setup and entry fn main() { - // Define paths - // TODO: Hardcoded path should only be the fallback - let path = vec![ - "/bin", - "/sbin", - "/usr/bin", - "/usr/sbin", - "/usr/local/bin", - "/usr/local/sbin", - ]; - let path = path.into_iter().map(Path::new).collect(); + // Compose the environment for dwvsh + // TODO: All instances of `env!("HOME")` need to be changed to use env::var + // TODO: Will probably need to set $HOME in dwv{profile,login} via passwd + compose::env(); // Set the prompt let prompt = "|> "; @@ -112,5 +102,5 @@ fn main() { }; // Begin evaluating commands - repl(&path, prompt, &mut at_prompt); + repl(prompt, &mut at_prompt); } diff --git a/src/path.rs b/src/path.rs index 2953982..f201f53 100644 --- a/src/path.rs +++ b/src/path.rs @@ -1,37 +1,43 @@ +use std::env; use std::fs; -use std::path::Path; /// Refresh the shell's $PATH /// /// This function caches all valid paths within within the directories /// specified. /// -/// # Arguments -/// * `path` - 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 path = vec!["/bin"]; -/// let path = path.into_iter().map(Path::new).collect(); -/// let mut bins = path::refresh(&path); +/// let mut bins = path::refresh(); /// ... /// // A situation occurs where the $PATH needs to be refreshed -/// bins = path::refresh(&path) +/// bins = path::refresh() /// ``` -pub fn refresh(path: &Vec<&Path>) -> Vec<String> { - let mut bins: Vec<String> = Vec::new(); +pub fn refresh() -> Vec<String> { + let mut bins = Vec::new(); + let path = env::var("PATH").unwrap_or(String::new()); + let mut path = path.split(':'); + + loop { + let p = match path.next() { + Some(p) => p, + None => break, + }; - for p in path { let files = match fs::read_dir(p) { Ok(files) => files, Err(_) => continue, }; for file in files { - bins.push(file.unwrap().path().display().to_string()); + let f = match file { + Ok(f) => f, + Err(_) => continue, + }; + bins.push(f.path().display().to_string()); } } diff --git a/src/poem/anthology.rs b/src/poem/anthology.rs index 2781081..b9e747c 100644 --- a/src/poem/anthology.rs +++ b/src/poem/anthology.rs @@ -3,7 +3,6 @@ pub mod exit; pub mod export; pub mod source; use crate::poem::Verse; -use std::path::Path; /// A static list of all the built-in commands static INDEX: [&str; 4] = ["cd", "exit", "export", "source"]; @@ -20,13 +19,13 @@ pub fn lookup(verb: &str) -> Option<usize> { INDEX.iter().position(|v| v.to_string() == verb) } -pub fn incant(verse: &Verse, index: usize, path: &Vec<&Path>, bins: &mut Vec<String>) -> i32 { +pub fn incant(verse: &Verse, index: usize, bins: &mut Vec<String>) -> i32 { let verb = INDEX[index]; match verb { "cd" => cd::incant(verse), "exit" => exit::incant(), "export" => export::incant(verse), - "source" => source::incant(verse, path, bins), + "source" => source::incant(verse, bins), _ => unreachable!(), } } diff --git a/src/poem/anthology/source.rs b/src/poem/anthology/source.rs index 1449994..f7b9a0b 100644 --- a/src/poem/anthology/source.rs +++ b/src/poem/anthology/source.rs @@ -1,9 +1,8 @@ use crate::poem::Verse; use crate::poem::{read::Readable, recite::Reciteable, Poem}; use std::fs; -use std::path::Path; -pub fn incant(verse: &Verse, path: &Vec<&Path>, bins: &mut Vec<String>) -> i32 { +pub fn incant(verse: &Verse, bins: &mut Vec<String>) -> i32 { let files = match verse.clause() { Some(clause) => clause, None => { @@ -14,7 +13,7 @@ pub fn incant(verse: &Verse, path: &Vec<&Path>, bins: &mut Vec<String>) -> i32 { for file in files { let poetry = match fs::read_to_string(&file) { - Ok(contents) => contents, + Ok(poetry) => poetry, Err(e) => { eprintln!( "source: could not load {}: {}", @@ -33,7 +32,7 @@ pub fn incant(verse: &Verse, path: &Vec<&Path>, bins: &mut Vec<String>) -> i32 { } }; - match poem.recite(path, bins, None) { + match poem.recite(bins, None) { Ok(_) => {} Err(e) => { eprintln!("dwvsh: {}", e.to_string().to_lowercase()); diff --git a/src/poem/recite.rs b/src/poem/recite.rs index a88007d..f2af591 100644 --- a/src/poem/recite.rs +++ b/src/poem/recite.rs @@ -6,26 +6,15 @@ use crate::poem::elements::rune::Rune; use std::env; use std::{ io, - path::Path, sync::{Arc, Mutex}, }; pub trait Reciteable { - fn recite( - &self, - path: &Vec<&Path>, - bins: &mut Vec<String>, - stdout: Option<bool>, - ) -> Result<String, io::Error>; + fn recite(&self, bins: &mut Vec<String>, stdout: Option<bool>) -> Result<String, io::Error>; } impl Reciteable for Poem { - fn recite( - &self, - path: &Vec<&Path>, - bins: &mut Vec<String>, - stdout: Option<bool>, - ) -> Result<String, io::Error> { + fn recite(&self, bins: &mut Vec<String>, stdout: Option<bool>) -> Result<String, io::Error> { // Should we print to stdout or always capture it let stdout = stdout.unwrap_or(true); @@ -58,7 +47,7 @@ impl Reciteable for Poem { let envar = name[1..].to_string(); let envar = match env::var(envar) { Ok(envar) => envar.to_string(), - Err(_) => "".to_string(), + Err(_) => String::new(), }; *word = word.replace(name.as_str(), envar.as_str()); } @@ -95,7 +84,7 @@ impl Reciteable for Poem { Some(poem) => poem, None => break, // TODO: Return an error }; - let out = poem.recite(path, bins, Some(false))?; + let out = poem.recite(bins, Some(false))?; if out.contains("\n") { let mut out = out.split("\n"); let next = out.next().unwrap_or("").trim(); @@ -129,35 +118,10 @@ impl Reciteable for Poem { None => {} } - // // Check if the user wants to exit the shell - // if verse.verb() == "exit" || verse.verb() == "quit" { - // exit(0); - // } - // - // // Check if the user wants to change directories - // if verse.verb() == "cd" { - // let path = match verse.clause() { - // Some(path) => path[0].to_string(), - // None => env!("HOME").to_string(), - // }; - // - // match std::env::set_current_dir(&path) { - // Ok(_) => continue, - // Err(e) => { - // eprintln!( - // "cd: unable to change into {}: {}", - // path, - // e.to_string().to_lowercase() - // ); - // continue; - // } - // } - // } - // Incant the verse if it's a built-in let index = anthology::lookup(&verse.verb()); let status = if index.is_some() { - anthology::incant(&verse, index.unwrap(), path, bins) + anthology::incant(&verse, index.unwrap(), bins) } else { // Incant the verse, based on its meter // Check if the verb exists @@ -165,7 +129,7 @@ impl Reciteable for Poem { // again // If it still doesn't exist, print an error if !verse.spellcheck(bins) { - *bins = path::refresh(path); + *bins = path::refresh(); if !verse.spellcheck(bins) { eprintln!("dwvsh: {}: command not found", verse.verb()); |