use crate::prelude::*; use std::collections::HashMap; use std::collections::HashSet; use std::io; use std::io::prelude::*; use std::os::unix::fs::PermissionsExt; #[macro_use] extern crate lalrpop_util; lalrpop_mod!(pub commandline); pub mod error; pub mod path; pub mod prelude; pub mod result; pub enum Input { String(String), End, } fn main() -> Result<()> { std::process::exit(match repl() { Ok(()) => 0, Err(ref e) => { eprintln!("{}", e); 1 } }) } fn repl() -> Result<()> { println!("Hello, terminal!"); loop { prompt()?; let input = read()?; match input { Input::String(string) => execute(&string)?, Input::End => break, } } Ok(()) } fn prompt() -> Result<()> { print!("$ "); io::stdout().flush()?; Ok(()) } fn read() -> Result { let mut input = String::new(); let n_bytes = io::stdin().read_line(&mut input)?; if n_bytes == 0 { Ok(Input::End) } else { Ok(Input::String(input)) } } fn execute(input: &str) -> Result<()> { let invocation = commandline::InvocationParser::new().parse(input)?; match invocation.as_slice() { ["environment", ..] => { let environment = read_environment()?; println!("{:?}", environment); } ["which", command_name, ..] => { match find_executable_path(command_name)? { Some(executable_path) => { println!("{}", executable_path); } None => { println!("Command not found: {}", command_name); } }; }, [command_name, ..] => { match find_executable_path(command_name)? { Some(executable_path) => { println!("{}", executable_path); } None => { println!("Command not found: {}", command_name); } }; }, _ => { println!("invocation '{:?}'", invocation); } } Ok(()) } fn read_environment() -> Result> { Ok(std::env::vars().collect()) } fn get_environment(variable_name: &str) -> Result> { Ok(std::env::vars() .find(|(key, _)| key == variable_name) .map(|(_, value)| value)) } fn get_search_paths() -> Result> { let paths = get_environment("PATH")?.unwrap_or_default(); let paths = path::parse_path_list(&paths)?; let mut result = Vec::new(); let mut seen = HashSet::new(); for path in paths { if seen.contains(&path) { continue; } seen.insert(path.clone()); result.push(path); } Ok(result) } fn find_executable_path(command_name: &str) -> Result> { let file_name: path::FileName = command_name.parse()?; let search_paths = get_search_paths()?; let mut executable_path: Option = None; for search_path in &search_paths { let candidate_path = search_path.concat_file_name(&file_name); match candidate_path.to_sys_path().metadata() { Ok(metadata) => { if metadata.is_file() && metadata.permissions().mode() & 0o111 != 0 { executable_path = Some(candidate_path); break; } }, Err(_) => { }, } } Ok(executable_path) }