#![forbid(unsafe_code)] use crate::prelude::*; use crate::result::Result; use crate::terminal::{Input, Terminal}; use async_std::io; use std::collections::HashMap; use std::collections::HashSet; use std::env; use std::os::unix::fs::PermissionsExt; use std::process::{self, Command}; #[macro_use] extern crate lalrpop_util; lalrpop_mod!(pub commandline); pub mod error; pub mod path; pub mod prelude; pub mod result; pub mod terminal; fn main() -> Result<()> { let result = async_std::task::block_on(async { repl().await }); process::exit(match result { Ok(()) => 0, Err(ref e) => { eprintln!("{}", e); 1 } }) } async fn repl() -> Result<()> { println!("Hello, terminal!"); let mut terminal = Terminal::init(io::stdin())?; loop { prompt().await?; let input = terminal.handle_input().await?; match input { Input::String(string) => { println!("{:?} {}", string, string.len()); execute(&string).await? }, Input::End => break, } break; } terminal.cleanup()?; Ok(()) } async fn prompt() -> Result<()> { print!("\n$ "); io::stdout().flush().await?; Ok(()) } async 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) => { let mut command = Command::new(executable_path.to_sys_path()); let arguments = &invocation[1..]; command.args(arguments); let _status = command.status()?; } None => { println!("Command not found: {}", command_name); } }; }, _ => { println!("invocation '{:?}'", invocation); } } Ok(()) } fn read_environment() -> Result> { Ok(env::vars().collect()) } fn get_environment(variable_name: &str) -> Result> { Ok(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) }