summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRory Dudley2024-03-28 23:26:02 -0600
committerRory Dudley2024-03-28 23:26:02 -0600
commit491d3fbff384d4b04483b54e5bb78d23bb1181c5 (patch)
tree470b0fc2ab0a476d682e104bdb03275ffd6b8671
parent14a74aea0f02da53e0f61c572da2c5244ed80551 (diff)
downloaddwarvish-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/dwvshrc3
-rw-r--r--src/compose.rs58
-rw-r--r--src/main.rs30
-rw-r--r--src/path.rs30
-rw-r--r--src/poem/anthology.rs5
-rw-r--r--src/poem/anthology/source.rs7
-rw-r--r--src/poem/recite.rs48
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());