diff options
author | Irene Knapp <ireneista@irenes.space> | 2024-03-12 21:54:28 -0700 |
---|---|---|
committer | Irene Knapp <ireneista@irenes.space> | 2024-03-12 21:54:28 -0700 |
commit | 7be9acd0bb08901c9fdfa45b694b7d3d5a594e70 (patch) | |
tree | 8a38a3ff200bae284fc0e2009220dacf0b53205b /src | |
parent | 3086d361665aedf840f76ded2f46c6ff5204f776 (diff) |
remove a lot of stuff that was part of the shell and does not need to be part of the line input library
Change-Id: Idd0435a4b29f5f525c9279e5c1d27916e6320685
Diffstat (limited to 'src')
-rw-r--r-- | src/commandline.lalrpop | 66 | ||||
-rw-r--r-- | src/error.rs | 42 | ||||
-rw-r--r-- | src/main.rs | 106 | ||||
-rw-r--r-- | src/path.rs | 319 | ||||
-rw-r--r-- | src/path/error.rs | 79 | ||||
-rw-r--r-- | src/path/parser.lalrpop | 92 | ||||
-rw-r--r-- | src/path/prelude.rs | 5 |
7 files changed, 2 insertions, 707 deletions
diff --git a/src/commandline.lalrpop b/src/commandline.lalrpop deleted file mode 100644 index d52e741..0000000 --- a/src/commandline.lalrpop +++ /dev/null @@ -1,66 +0,0 @@ -grammar; - -pub Invocation: Vec<&'input str> = { - <WORD*>, -}; - -// Several of the regexps below make use of Unicode character classes. [1] is -// the official reference to Unicode classes, and [2] is a site that is useful -// for browsing to get an intuitive idea of what the classes mean. -// -// In maintaining these regexps, it's important to understand the structure -// of Unicode character classes. There are seven top-level categories, each -// with a single-character name (ie. "Z" for separators). Each top-level -// category has several subcategories which form an exhaustive partition of it; -// the subcategories have two-character names (ie. "Zs" for space separators). -// Every allocated codepoint is in exactly one top-level category and exactly -// one subcategory. -// -// It is important that these regexps exhaustively cover the entirety of -// Unicode, without omission; otherwise lalrpop's lexer will give InvalidToken -// errors for unrecognized characters. Overlaps will be less catastrophic, as -// they'll be resoved by the precedence rules, but for clarity's sake they -// should be avoided. -// -// [1] http://www.unicode.org/reports/tr44/#General_Category_Values -// [2] https://www.compart.com/en/unicode/category -// -match { - // Zs is the Unicode class for space separators. This includes the ASCII - // space character. - // - r"\p{Zs}+" => { }, - - // Zl is the Unicode class for line separators. Zp is the Unicode class for - // paragraph separators. Newline and carriage return are included individually - // here, since Unicode classifies them with the control characters rather than - // with the space characters. - // - r"[\p{Zl}\p{Zp}\n\r]" => NEWLINE, - - // This one recognizes exactly one character, the old-school double-quote. As - // tempting as it is to do something clever with character classes, shells have - // a long history of quoting syntaxes which are subtle and quick to anger, and - // for this project the decision is to be radically simple instead. - r#"["]"# => QUOTE, - - // This one matches any control character other than line feed and carriage - // return. The grammar doesn't reference control characters, but having a - // token for them makes the error messages more informative. - r"[\p{C}&&[^\n\r]]" => CONTROL, - - // Z is the unicode class for separators, which is exhaustively partitioned - // into line, paragraph, and space separators. Each of those subclasses is - // handled above. C is the class for control characters. This regexp tests - // for the intersection of the negation of these character classes, along - // with a negated class enumerating all the explicitly-recognized characters, - // which means it matches any character NOT in the regexps above. - // - // Note that, counterintuitively, line feed and carriage return are classified - // as control characters, not as line separators. Either way, this regexp would - // still exclude them, but the difference might be relevant when maintaining - // it. - // - r#"[\P{Z}&&\P{C}&&[^"]]+"# => WORD, -} - diff --git a/src/error.rs b/src/error.rs index 1d291ed..426ebf5 100644 --- a/src/error.rs +++ b/src/error.rs @@ -1,21 +1,11 @@ #![forbid(unsafe_code)] -use crate::path::GenericPath; -use crate::path::error::{FileNameError, DirectoryNameError, PathError}; use crate::terminal::error::TerminalError; -type ParseError<'a> = - lalrpop_util::ParseError<usize, lalrpop_util::lexer::Token<'a>, &'a str>; - #[derive(Debug)] pub enum Error { IO(std::io::Error), - Parse(String), - FileName(FileNameError), - DirectoryName(DirectoryNameError), - Path(PathError), - PathEmpiricallyFile(GenericPath), Terminal(TerminalError), } @@ -25,14 +15,6 @@ impl std::fmt::Display for Error { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { Error::IO(e) => e.fmt(f), - Error::Parse(e) => e.fmt(f), - Error::FileName(e) => e.fmt(f), - Error::DirectoryName(e) => e.fmt(f), - Error::Path(e) => e.fmt(f), - Error::PathEmpiricallyFile(path) => - f.write_fmt(format_args!( - "There's a file at {}, not a directory.", - path)), Error::Terminal(e) => e.fmt(f), } } @@ -50,30 +32,6 @@ impl From<std::io::Error> for Error { } } -impl From<ParseError<'_>> for Error { - fn from(e: ParseError<'_>) -> Error { - Error::Parse(format!("{}", e)) - } -} - -impl From<FileNameError> for Error { - fn from(e: FileNameError) -> Error { - Error::FileName(e) - } -} - -impl From<DirectoryNameError> for Error { - fn from(e: DirectoryNameError) -> Error { - Error::DirectoryName(e) - } -} - -impl From<PathError> for Error { - fn from(e: PathError) -> Error { - Error::Path(e) - } -} - impl From<TerminalError> for Error { fn from(e: TerminalError) -> Error { Error::Terminal(e) diff --git a/src/main.rs b/src/main.rs index e94ce0c..9d8ed49 100644 --- a/src/main.rs +++ b/src/main.rs @@ -2,18 +2,10 @@ use crate::result::Result; use crate::terminal::{Input, Terminal}; -use std::collections::HashMap; -use std::collections::HashSet; -use std::env; -use std::os::unix::fs::PermissionsExt; -use std::process::{self, Command}; +use std::process; use tokio::io::{self, AsyncWriteExt}; -#[macro_use] extern crate lalrpop_util; - -lalrpop_mod!(pub commandline); pub mod error; -pub mod path; pub mod result; pub mod terminal; @@ -67,101 +59,7 @@ async fn prompt() -> Result<()> { } -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); - } - } - +async fn execute(_input: &str) -> Result<()> { Ok(()) } - -fn read_environment() -> Result<HashMap<String,String>> { - Ok(env::vars().collect()) -} - - -fn get_environment(variable_name: &str) -> Result<Option<String>> { - Ok(env::vars() - .find(|(key, _)| key == variable_name) - .map(|(_, value)| value)) -} - - -fn get_search_paths() -> Result<Vec<path::AbsoluteDirectoryPath>> { - 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<Option<path::AbsoluteFilePath>> -{ - let file_name: path::FileName = command_name.parse()?; - let search_paths = get_search_paths()?; - - let mut executable_path: Option<path::AbsoluteFilePath> = 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) -} - diff --git a/src/path.rs b/src/path.rs deleted file mode 100644 index 7df10b2..0000000 --- a/src/path.rs +++ /dev/null @@ -1,319 +0,0 @@ -/* - * We implement most aspects of paths independently, not relying on - * std::path, on the theory that path syntax is such an important part of a - * shell that it doesn't make sense to try to integrate with non-Unix syntaxes. - * However, we do use std::path to print individual path components, in order - * to get the benefit of its functionality for handling non-Unicode filenames. - */ - -#![forbid(unsafe_code)] -use crate::path::prelude::*; - -use std::str::FromStr; - - -lalrpop_mod!(pub parser, "/path/parser.rs"); -pub mod error; -pub mod prelude; - - -#[derive(Clone,Debug,Eq,Hash,Ord,PartialEq,PartialOrd)] -pub struct AbsoluteDirectoryPath { - directory_names: Vec<DirectoryName>, -} - -#[derive(Clone,Debug,Eq,Hash,Ord,PartialEq,PartialOrd)] -pub struct AbsoluteFilePath { - directory_names: Vec<DirectoryName>, - file_name: FileName, -} - -#[derive(Clone,Debug,Eq,Hash,Ord,PartialEq,PartialOrd)] -pub struct FileName(String); - -#[derive(Clone,Debug,Eq,Hash,Ord,PartialEq,PartialOrd)] -pub struct DirectoryName(String); - -#[derive(Clone,Debug,Eq,Hash,Ord,PartialEq,PartialOrd)] -pub struct GenericPath { - components: Vec<GenericPathComponent>, - starts_with_slash: bool, - ends_with_slash: bool, -} - -#[derive(Clone,Debug,Eq,Hash,Ord,PartialEq,PartialOrd)] -pub enum GenericPathComponent { - FileOrDirectoryName(String), - CurrentDirectory, - ParentDirectory, -} - - -impl std::fmt::Display for AbsoluteDirectoryPath { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - for directory_name in &self.directory_names { - f.write_str("/")?; - directory_name.fmt(f)?; - } - - f.write_str("/")?; - - Ok(()) - } -} - - -impl std::fmt::Display for AbsoluteFilePath { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - for directory_name in &self.directory_names { - f.write_str("/")?; - directory_name.fmt(f)?; - } - - f.write_str("/")?; - - self.file_name.fmt(f)?; - - Ok(()) - } -} - - -impl std::fmt::Display for FileName { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - FileName(name) => { - let std_path = std::path::Path::new(&name); - f.write_fmt(format_args!("{}", std_path.display()))?; - }, - } - - Ok(()) - } -} - - -impl std::fmt::Display for DirectoryName { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - DirectoryName(name) => { - let std_path = std::path::Path::new(&name); - f.write_fmt(format_args!("{}", std_path.display()))?; - }, - } - - Ok(()) - } -} - - -impl std::fmt::Display for GenericPath { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - if self.starts_with_slash { - f.write_str("/")?; - } - - let mut is_first = true; - for component in &self.components { - if !is_first { - f.write_str("/")?; - } - - component.fmt(f)?; - - is_first = false; - } - - if self.ends_with_slash { - f.write_str("/")?; - } - - Ok(()) - } -} - - -impl std::fmt::Display for GenericPathComponent { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - GenericPathComponent::FileOrDirectoryName(name) => { - let std_path = std::path::Path::new(&name); - f.write_fmt(format_args!("{}", std_path.display()))?; - }, - GenericPathComponent::CurrentDirectory => { - f.write_str(".")?; - }, - GenericPathComponent::ParentDirectory => { - f.write_str("..")?; - }, - } - - Ok(()) - } -} - - -impl FromStr for FileName { - type Err = FileNameError; - - fn from_str(input: &str) -> std::result::Result<Self, Self::Err> { - if input.find('/').is_some() { - Err(FileNameError::ContainsSlash(input.to_string())) - } else { - Ok(FileName(input.to_string())) - } - } -} - - -impl FromStr for DirectoryName { - type Err = DirectoryNameError; - - fn from_str(input: &str) -> std::result::Result<Self, Self::Err> { - if input.find('/').is_some() { - Err(DirectoryNameError::ContainsSlash(input.to_string())) - } else { - Ok(DirectoryName(input.to_string())) - } - } -} - - -impl FromStr for GenericPathComponent { - type Err = PathError; - - fn from_str(input: &str) -> std::result::Result<Self, Self::Err> { - parser::PathComponentParser::new().parse(input).map_err( - |e| PathError::Parse(e.to_string())) - } -} - - -impl AbsoluteDirectoryPath { - pub fn to_sys_path(&self) -> std::path::PathBuf { - let mut result = std::path::PathBuf::new(); - result.push(format!("{}", self)); - result - } - - pub fn concat_file_name(&self, file_name: &FileName) -> AbsoluteFilePath { - AbsoluteFilePath { - directory_names: self.directory_names.clone(), - file_name: file_name.clone(), - } - } -} - - -impl AbsoluteFilePath { - pub fn to_sys_path(&self) -> std::path::PathBuf { - let mut result = std::path::PathBuf::new(); - result.push(format!("{}", self)); - result - } -} - - -pub fn parse_path_list(path_list: &str) - -> Result<Vec<AbsoluteDirectoryPath>> -{ - match parser::PathListParser::new().parse(path_list) { - Ok(parsed_paths) => { - let mut result = Vec::new(); - for generic_path in parsed_paths { - let path = absolute_directory_path(generic_path)?; - result.push(path); - } - Ok(result) - }, - Err(original_error) => { - match parser::PathListAllowingEmptyPathsParser::new() - .parse(path_list) - { - Ok(_) => { - Err(PathError::PathListHasEmptyComponents(path_list.to_string())) - }, - Err(_) => { - Err(PathError::Parse(original_error.to_string())) - }, - } - }, - } -} - - -pub fn absolute_directory_path(generic_path: GenericPath) - -> Result<AbsoluteDirectoryPath> -{ - if !generic_path.starts_with_slash { - return Err(PathError::PathLexicallyRelative(generic_path)); - } - - let mut flattened_components = Vec::new(); - for component in &generic_path.components { - match component { - GenericPathComponent::CurrentDirectory => { }, - GenericPathComponent::ParentDirectory => { - if flattened_components.len() > 0 { - flattened_components.pop(); - } else { - return Err(PathError::PathLexicallyInvalid(generic_path)); - } - }, - GenericPathComponent::FileOrDirectoryName(name) => { - flattened_components.push(DirectoryName(name.to_string())); - }, - } - } - - Ok(AbsoluteDirectoryPath { - directory_names: flattened_components, - }) -} - - -pub fn absolute_file_path(generic_path: GenericPath) - -> Result<AbsoluteFilePath> -{ - if !generic_path.starts_with_slash { - return Err(PathError::PathLexicallyRelative(generic_path)); - } - - if generic_path.ends_with_slash { - return Err(PathError::PathLexicallyDirectory(generic_path)); - } - - let mut iterator = generic_path.components.iter(); - - let file_name = match iterator.next_back() { - Some(GenericPathComponent::FileOrDirectoryName(name)) => { - FileName(name.to_string()) - } - _ => { - return Err(PathError::PathLexicallyInvalid(generic_path)); - } - }; - - let mut flattened_components = Vec::new(); - for component in &generic_path.components { - match component { - GenericPathComponent::CurrentDirectory => { }, - GenericPathComponent::ParentDirectory => { - if flattened_components.len() > 0 { - flattened_components.pop(); - } else { - return Err(PathError::PathLexicallyInvalid(generic_path)); - } - }, - GenericPathComponent::FileOrDirectoryName(name) => { - flattened_components.push(DirectoryName(name.to_string())); - }, - } - } - - Ok(AbsoluteFilePath { - directory_names: flattened_components, - file_name: file_name, - }) -} - diff --git a/src/path/error.rs b/src/path/error.rs deleted file mode 100644 index aee6e9f..0000000 --- a/src/path/error.rs +++ /dev/null @@ -1,79 +0,0 @@ -#![forbid(unsafe_code)] - -use crate::path::GenericPath; - -pub type Result<T> = std::result::Result<T, PathError>; - - -#[derive(Clone,Debug,Eq,Hash,Ord,PartialEq,PartialOrd)] -pub enum FileNameError { - ContainsSlash(String), -} - -#[derive(Clone,Debug,Eq,Hash,Ord,PartialEq,PartialOrd)] -pub enum DirectoryNameError { - ContainsSlash(String), -} - -#[derive(Clone,Debug,Eq,Hash,Ord,PartialEq,PartialOrd)] -pub enum PathError { - Parse(String), - PathLexicallyDirectory(GenericPath), - PathLexicallyRelative(GenericPath), - PathLexicallyInvalid(GenericPath), - PathListHasEmptyComponents(String), -} - - -impl std::error::Error for FileNameError { } - -impl std::error::Error for DirectoryNameError { } - -impl std::error::Error for PathError { } - -impl std::fmt::Display for FileNameError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - FileNameError::ContainsSlash(s) => - f.write_fmt(format_args!( - "File names cannot contain slashes, but {:?} does.", s)), - } - } -} - -impl std::fmt::Display for DirectoryNameError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - DirectoryNameError::ContainsSlash(s) => - f.write_fmt(format_args!( - "File names cannot contain slashes, but {:?} does.", s)), - } - } -} - -impl std::fmt::Display for PathError { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - match self { - PathError::Parse(s) => - f.write_fmt(format_args!("Syntax error in path: {}", s)), - PathError::PathLexicallyDirectory(path) => - f.write_fmt(format_args!( - "The path {} ends in a slash, but is supposed to refer to a file, \ - not a directory.", - path)), - PathError::PathLexicallyRelative(path) => - f.write_fmt(format_args!( - "The path {} is relative, not absolute.", - path)), - PathError::PathLexicallyInvalid(path) => - f.write_fmt(format_args!( - "This isn't a valid path. {}", - path)), - PathError::PathListHasEmptyComponents(path_list) => - f.write_fmt(format_args!( - "Path list has empty components: {}", - path_list)), - } - } -} - diff --git a/src/path/parser.lalrpop b/src/path/parser.lalrpop deleted file mode 100644 index c41b3fd..0000000 --- a/src/path/parser.lalrpop +++ /dev/null @@ -1,92 +0,0 @@ -grammar; - -use crate::path::GenericPath; -use crate::path::GenericPathComponent; - -pub PathList: Vec<GenericPath> = { - => { - Vec::new() - }, - <mut left:(<PathNoColons> COLON)*> <right:PathNoColons> => { - left.push(right); - left - }, -}; - -pub PathListAllowingEmptyPaths: Vec<GenericPath> = { - => vec![GenericPath { - components: Vec::new(), - starts_with_slash: false, - ends_with_slash: false, - }], - PathNoColons => vec![<>], - <mut left:PathListAllowingEmptyPaths> COLON => { - left.push(GenericPath { - components: Vec::new(), - starts_with_slash: false, - ends_with_slash: false, - }); - left - }, - <mut left:PathListAllowingEmptyPaths> COLON <right:PathNoColons> => { - left.push(right); - left - }, -} - -pub PathNoColons: GenericPath = { - SLASH => GenericPath { - components: Vec::new(), - starts_with_slash: true, - ends_with_slash: true, - }, - <PathNoColons2> => GenericPath { - components: <>, - starts_with_slash: false, - ends_with_slash: false, - }, - <PathNoColons2> SLASH => GenericPath { - components: <>, - starts_with_slash: false, - ends_with_slash: true, - }, - SLASH <PathNoColons2> => GenericPath { - components: <>, - starts_with_slash: true, - ends_with_slash: false, - }, - SLASH <PathNoColons2> SLASH => GenericPath { - components: <>, - starts_with_slash: true, - ends_with_slash: true, - }, -} - -PathNoColons2: Vec<GenericPathComponent> = { - <PathComponent> => vec![<>], - <mut left:PathNoColons2> SLASH <right:PathComponent> => { - left.push(right); - left - } -} - -pub PathComponent: GenericPathComponent = { - DOT => GenericPathComponent::CurrentDirectory, - DOT_DOT => GenericPathComponent::ParentDirectory, - <PATH_COMPONENT_NO_COLONS> => - GenericPathComponent::FileOrDirectoryName(<>.to_string()) -} - -// Whitespace is not allowed. -match { - r"[^:/]+" => PATH_COMPONENT_NO_COLONS, - - r"/" => SLASH, - - ":" => COLON, - - "." => DOT, - - ".." => DOT_DOT, -} - diff --git a/src/path/prelude.rs b/src/path/prelude.rs deleted file mode 100644 index f7b23dd..0000000 --- a/src/path/prelude.rs +++ /dev/null @@ -1,5 +0,0 @@ -#![forbid(unsafe_code)] - -pub use crate::path::error::{ - Result, DirectoryNameError, FileNameError, PathError}; - |