use ctrlc; use std::fs; use std::io; use std::io::Write; use std::path::Path; use std::process::Command; fn eval(paths: &[&str], prompt: &str) { let mut bins: Vec = Vec::new(); loop { // Output the prompt io::stdout().flush().unwrap(); print!("{}", prompt); io::stdout().flush().unwrap(); // Wait for user input let mut input = String::new(); let bytes = io::stdin() .read_line(&mut input) .expect("Unable to evaluate the input string"); // Check if we've reached EOF (i.e. ) if bytes == 0 { println!(""); break; } // Trim the input let input = input.trim(); // Check if user wants to exit the shell if input == "exit" || input == "quit" { break; } // Parse command and arguments let mut split = input.split(' '); let cmd = match split.next() { Some(str) if str.trim().is_empty() => continue, Some(str) => str.trim(), None => continue, }; // Parse arguments let mut args = vec![]; loop { let next = split.next(); match next { Some(str) => args.push(str), None => break, } } // Check if user wants to change directories if cmd == "cd" { let path = match args.first() { Some(str) => str, None => env!("HOME"), }; match std::env::set_current_dir(path) { Ok(_) => continue, Err(_) => { println!("cd: Unable to change into {}", path); continue; } } } // Check if the file exists, if given a pull or relative path // TODO: Check if file at the path is executable (i.e. +x) let mut cmd = String::from(cmd); 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 // // 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 = String::from( match b .clone() .iter() .find(|b| b.split("/").last().unwrap() == cmd) { Some(cmd) => cmd, None => { for path in paths { let files = fs::read_dir(path).expect("Unable to read files in your path"); for file in files { bins.push(file.unwrap().path().display().to_string()); } } match bins.iter().find(|b| b.split("/").last().unwrap() == cmd) { Some(cmd) => cmd, None => { println!("dwvsh: error: command not found..."); continue; } } } }, ); } // Run the command (and wait for it to finish) let mut child = match Command::new(cmd).args(args).spawn() { Ok(ch) => ch, Err(_) => { println!("Unable to fork"); continue; } }; child.wait().unwrap(); } } fn main() { // Define paths // TODO: Hardcoded path should only be the fallback let paths = [ "/bin", "/sbin", "/usr/bin", "/usr/sbin", "/usr/local/bin", "/usr/local/sbin", ]; // Set the prompt let prompt = "|> "; // Handle signals ctrlc::set_handler(move || { print!("\n{}", prompt); io::stdout().flush().unwrap(); }) .expect("Unable to set handler"); // Begin evaluating commands eval(&paths, prompt); }