use std::io::{self, Write}; use std::path::Path; use std::sync::{Arc, Mutex}; mod path; use path::prefresh; mod poem; use poem::{read::Readable, recite::Reciteable, Poem}; /// 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 path = vec!["/bin"]; /// let path = path.into_iter().map(Path::new).collect(); /// let prompt = "|> "; /// ... /// repl(&path, prompt); /// } /// ``` fn repl(path: &Vec<&Path>, prompt: &str, at_prompt: &mut Arc>) { // Initial path refresh on startup let mut bins: Vec = prefresh(path); // Main shell loop loop { // Output the prompt print!("{}", prompt); io::stdout().flush().unwrap(); // At the prompt *at_prompt.lock().unwrap() = true; // Wait for user input let mut poetry = String::new(); let bytes = io::stdin() .read_line(&mut poetry) .expect("dwvsh: error: unable to evaluate the input string"); // Check if we've reached EOF (i.e. ) if bytes == 0 { println!(); break; } // Trim the input let poetry = String::from(poetry.trim()); // Skip parsing if there is no poetry if poetry.is_empty() { continue; } // Not at the prompt *at_prompt.lock().unwrap() = false; // Parse the poem let poem = Poem::read(poetry); let poem = match poem { Ok(poem) => poem, Err(e) => { eprintln!("dwvsh: {}", e.to_string().to_lowercase()); continue; } }; // Recite the poem match poem.recite(path, &mut bins, None) { Ok(_) => {} Err(e) => eprintln!("dwvsh: {}", e.to_string().to_lowercase()), } } } /// Shell entry /// /// 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(); // Set the prompt let prompt = "|> "; let mut at_prompt = Arc::new(Mutex::new(false)); // Handle signals unsafe { let at_prompt = Arc::clone(&at_prompt); signal_hook::low_level::register(signal_hook::consts::SIGINT, move || { if *at_prompt.lock().unwrap() { print!("\n{}", prompt); io::stdout().flush().unwrap(); } else { println!(); } }) .unwrap(); }; // Begin evaluating commands repl(&path, prompt, &mut at_prompt); }