diff --git a/CMakeLists.txt b/CMakeLists.txt index ea2f70a3e..5a7b03797 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -102,24 +102,7 @@ endif() # List of other sources. set(FISH_SRCS - src/ast.cpp - src/builtin.cpp - src/color.cpp - src/common.cpp - src/env.cpp - src/event.cpp - src/expand.cpp - src/fallback.cpp - src/fish_version.cpp - src/flog.cpp - src/highlight.cpp - src/output.cpp - src/parse_util.cpp - src/path.cpp - src/rustffi.cpp - src/wcstringutil.cpp - src/wgetopt.cpp - src/wutil.cpp + src/empty.cpp ) # Header files are just globbed. diff --git a/fish-rust/build.rs b/fish-rust/build.rs index a90be9400..90d419669 100644 --- a/fish-rust/build.rs +++ b/fish-rust/build.rs @@ -98,53 +98,10 @@ fn main() { // This allows "Rust to be used from C++" // This must come before autocxx so that cxx can emit its cxx.h header. let source_files = vec![ - "fish-rust/src/abbrs.rs", - "fish-rust/src/ast.rs", - "fish-rust/src/builtins/shared.rs", - "fish-rust/src/common.rs", - "fish-rust/src/complete.rs", - "fish-rust/src/editable_line.rs", - "fish-rust/src/env_dispatch.rs", - "fish-rust/src/env/env_ffi.rs", - "fish-rust/src/event.rs", - "fish-rust/src/exec.rs", - "fish-rust/src/expand.rs", - "fish-rust/src/fd_monitor.rs", - "fish-rust/src/fd_readable_set.rs", - "fish-rust/src/fds.rs", "fish-rust/src/ffi_init.rs", - "fish-rust/src/fish_indent.rs", "fish-rust/src/fish_key_reader.rs", + "fish-rust/src/fish_indent.rs", "fish-rust/src/fish.rs", - "fish-rust/src/function.rs", - "fish-rust/src/future_feature_flags.rs", - "fish-rust/src/highlight.rs", - "fish-rust/src/history.rs", - "fish-rust/src/input_ffi.rs", - "fish-rust/src/io.rs", - "fish-rust/src/job_group.rs", - "fish-rust/src/kill.rs", - "fish-rust/src/operation_context.rs", - "fish-rust/src/output.rs", - "fish-rust/src/pager.rs", - "fish-rust/src/parse_constants.rs", - "fish-rust/src/parser.rs", - "fish-rust/src/parse_tree.rs", - "fish-rust/src/parse_util.rs", - "fish-rust/src/print_help.rs", - "fish-rust/src/proc.rs", - "fish-rust/src/reader.rs", - "fish-rust/src/redirection.rs", - "fish-rust/src/screen.rs", - "fish-rust/src/signal.rs", - "fish-rust/src/termsize.rs", - "fish-rust/src/threads.rs", - "fish-rust/src/timer.rs", - "fish-rust/src/tokenizer.rs", - "fish-rust/src/trace.rs", - "fish-rust/src/util.rs", - "fish-rust/src/universal_notifier/mod.rs", - "fish-rust/src/wildcard.rs", ]; cxx_build::bridges(&source_files) .flag_if_supported("-std=c++11") diff --git a/fish-rust/src/abbrs.rs b/fish-rust/src/abbrs.rs index 150ffea2a..039205950 100644 --- a/fish-rust/src/abbrs.rs +++ b/fish-rust/src/abbrs.rs @@ -5,86 +5,13 @@ }; use crate::wchar::prelude::*; -use crate::wchar_ffi::{AsWstr, WCharFromFFI, WCharToFFI}; -use cxx::CxxWString; use once_cell::sync::Lazy; -use crate::abbrs::abbrs_ffi::abbrs_replacer_t; use crate::parse_constants::SourceRange; #[cfg(test)] use crate::tests::prelude::*; use pcre2::utf32::Regex; -use self::abbrs_ffi::{abbreviation_t, abbrs_position_t, abbrs_replacement_t}; - -#[cxx::bridge] -mod abbrs_ffi { - extern "C++" { - include!("parse_constants.h"); - - type SourceRange = crate::parse_constants::SourceRange; - } - - enum abbrs_position_t { - command, - anywhere, - } - - struct abbrs_replacer_t { - replacement: UniquePtr, - is_function: bool, - set_cursor_marker: UniquePtr, - has_cursor_marker: bool, - } - - struct abbrs_replacement_t { - range: SourceRange, - text: UniquePtr, - cursor: usize, - has_cursor: bool, - } - - struct abbreviation_t { - key: UniquePtr, - replacement: UniquePtr, - is_regex: bool, - } - - extern "Rust" { - type GlobalAbbrs<'a>; - - #[cxx_name = "abbrs_list"] - fn abbrs_list_ffi() -> Vec; - - #[cxx_name = "abbrs_match"] - fn abbrs_match_ffi(token: &CxxWString, position: abbrs_position_t) - -> Vec; - - #[cxx_name = "abbrs_has_match"] - fn abbrs_has_match_ffi(token: &CxxWString, position: abbrs_position_t) -> bool; - - #[cxx_name = "abbrs_replacement_from"] - fn abbrs_replacement_from_ffi( - range: SourceRange, - text: &CxxWString, - set_cursor_marker: &CxxWString, - has_cursor_marker: bool, - ) -> abbrs_replacement_t; - - #[cxx_name = "abbrs_get_set"] - unsafe fn abbrs_get_set_ffi<'a>() -> Box>; - unsafe fn add<'a>( - self: &mut GlobalAbbrs<'_>, - name: &CxxWString, - key: &CxxWString, - replacement: &CxxWString, - position: abbrs_position_t, - from_universal: bool, - ); - unsafe fn erase<'a>(self: &mut GlobalAbbrs<'_>, name: &CxxWString); - } -} - static ABBRS: Lazy> = Lazy::new(|| Mutex::new(Default::default())); pub fn with_abbrs(cb: impl FnOnce(&AbbreviationSet) -> R) -> R { @@ -108,16 +35,6 @@ pub enum Position { Anywhere, // expand in any token } -impl From for Position { - fn from(value: abbrs_position_t) -> Self { - match value { - abbrs_position_t::anywhere => Position::Anywhere, - abbrs_position_t::command => Position::Command, - _ => panic!("invalid abbrs_position_t"), - } - } -} - #[derive(Debug)] pub struct Abbreviation { // Abbreviation name. This is unique within the abbreviation set. @@ -209,18 +126,6 @@ pub struct Replacer { pub set_cursor_marker: Option, } -impl From for abbrs_replacer_t { - fn from(value: Replacer) -> Self { - let has_cursor_marker = value.set_cursor_marker.is_some(); - Self { - replacement: value.replacement.to_ffi(), - is_function: value.is_function, - set_cursor_marker: value.set_cursor_marker.unwrap_or_default().to_ffi(), - has_cursor_marker, - } - } -} - pub struct Replacement { /// The original range of the token in the command line. pub range: SourceRange, @@ -361,86 +266,10 @@ pub fn abbrs_match(token: &wstr, position: Position) -> Vec { .collect() } -fn abbrs_match_ffi(token: &CxxWString, position: abbrs_position_t) -> Vec { - with_abbrs(|set| set.r#match(token.as_wstr(), position.into())) - .into_iter() - .map(|r| r.into()) - .collect() -} - -fn abbrs_has_match_ffi(token: &CxxWString, position: abbrs_position_t) -> bool { - with_abbrs(|set| set.has_match(token.as_wstr(), position.into())) -} - -fn abbrs_list_ffi() -> Vec { - with_abbrs(|set| -> Vec { - let list = set.list(); - let mut result = Vec::with_capacity(list.len()); - for abbr in list { - result.push(abbreviation_t { - key: abbr.key.to_ffi(), - replacement: abbr.replacement.to_ffi(), - is_regex: abbr.is_regex(), - }) - } - - result - }) -} - -fn abbrs_get_set_ffi<'a>() -> Box> { - let abbrs_g = ABBRS.lock().unwrap(); - Box::new(GlobalAbbrs { g: abbrs_g }) -} - -fn abbrs_replacement_from_ffi( - range: SourceRange, - text: &CxxWString, - set_cursor_marker: &CxxWString, - has_cursor_marker: bool, -) -> abbrs_replacement_t { - let cursor_marker = if has_cursor_marker { - Some(set_cursor_marker.from_ffi()) - } else { - None - }; - - let replacement = Replacement::new(range, text.from_ffi(), cursor_marker); - - abbrs_replacement_t { - range, - text: replacement.text.to_ffi(), - cursor: replacement.cursor.unwrap_or_default(), - has_cursor: replacement.cursor.is_some(), - } -} - pub struct GlobalAbbrs<'a> { g: MutexGuard<'a, AbbreviationSet>, } -impl<'a> GlobalAbbrs<'a> { - fn add( - &mut self, - name: &CxxWString, - key: &CxxWString, - replacement: &CxxWString, - position: abbrs_position_t, - from_universal: bool, - ) { - self.g.add(Abbreviation::new( - name.from_ffi(), - key.from_ffi(), - replacement.from_ffi(), - position.into(), - from_universal, - )); - } - - fn erase(&mut self, name: &CxxWString) { - self.g.erase(name.as_wstr()); - } -} #[test] #[serial] fn rename_abbrs() { diff --git a/fish-rust/src/ast.rs b/fish-rust/src/ast.rs index 933da72e0..8bbc5b1cf 100644 --- a/fish-rust/src/ast.rs +++ b/fish-rust/src/ast.rs @@ -13,9 +13,8 @@ use crate::flog::FLOG; use crate::parse_constants::{ token_type_user_presentable_description, ParseError, ParseErrorCode, ParseErrorList, - ParseErrorListFfi, ParseKeyword, ParseTokenType, ParseTreeFlags, SourceRange, - StatementDecoration, ERROR_BAD_COMMAND_ASSIGN_ERR_MSG, INVALID_PIPELINE_CMD_ERR_MSG, - SOURCE_OFFSET_INVALID, + ParseKeyword, ParseTokenType, ParseTreeFlags, SourceRange, StatementDecoration, + ERROR_BAD_COMMAND_ASSIGN_ERR_MSG, INVALID_PIPELINE_CMD_ERR_MSG, SOURCE_OFFSET_INVALID, }; use crate::parse_tree::ParseToken; #[cfg(test)] @@ -25,9 +24,6 @@ TOK_ACCEPT_UNFINISHED, TOK_CONTINUE_AFTER_ERROR, TOK_SHOW_COMMENTS, }; use crate::wchar::prelude::*; -use crate::wchar_ffi::{wcharz, wcharz_t, AsWstr, WCharToFFI}; -use cxx::{type_id, ExternType}; -use cxx::{CxxWString, UniquePtr}; use std::ops::{ControlFlow, Index, IndexMut}; /** @@ -105,7 +101,6 @@ fn accept_mut(&mut self, visitor: &mut dyn NodeVisitorMut, reversed: bool) { pub trait Node: Acceptor + ConcreteNode + std::fmt::Debug { /// The parent node, or null if this is root. fn parent(&self) -> Option<&dyn Node>; - fn parent_ffi(&self) -> &Option<*const dyn Node>; /// The type of this node. fn typ(&self) -> Type; @@ -464,9 +459,6 @@ fn typ(&self) -> Type { fn parent(&self) -> Option<&dyn Node> { self.parent.map(|p| unsafe { &*p }) } - fn parent_ffi(&self) -> &Option<*const dyn Node> { - &self.parent - } fn category(&self) -> Category { Category::$category } @@ -2286,7 +2278,6 @@ pub fn ast_type_to_string(t: Type) -> &'static wstr { Type::argument => "argument"L, Type::argument_list => "argument_list"L, Type::job_list => "job_list"L, - _ => panic!("unknown AST type"), } } @@ -2324,10 +2315,6 @@ fn visit(&mut self, node: &'a dyn Node) { } } -fn ast_type_to_string_ffi(typ: Type) -> wcharz_t { - wcharz!(ast_type_to_string(typ)) -} - pub type SourceRangeList = Vec; /// Extra source ranges. @@ -2805,7 +2792,6 @@ fn visit_mut(&mut self, node: &mut dyn NodeMut) -> VisitResult { _ => (), } } - _ => panic!(), } VisitResult::Continue(()) } @@ -3962,7 +3948,6 @@ fn from(token_type: TokenType) -> Self { TokenType::redirect => ParseTokenType::redirection, TokenType::error => ParseTokenType::tokenizer_error, TokenType::comment => ParseTokenType::comment, - _ => panic!("bad token type"), } } } @@ -4022,1810 +4007,51 @@ fn test_ast_parse() { assert!(!ast.any_error); } -pub use ast_ffi::{Category, Type}; - -#[cxx::bridge] -#[allow(clippy::needless_lifetimes)] // false positive -pub mod ast_ffi { - extern "C++" { - include!("wutil.h"); - include!("tokenizer.h"); - include!("parse_constants.h"); - type wcharz_t = crate::wchar_ffi::wcharz_t; - type ParseTokenType = crate::parse_constants::ParseTokenType; - type ParseKeyword = crate::parse_constants::ParseKeyword; - type SourceRange = crate::parse_constants::SourceRange; - type ParseErrorListFfi = crate::parse_constants::ParseErrorListFfi; - type StatementDecoration = crate::parse_constants::StatementDecoration; - } - - #[derive(Debug)] - pub enum Category { - branch, - leaf, - list, - } - - #[derive(Debug)] - pub enum Type { - token_base, - keyword_base, - redirection, - variable_assignment, - variable_assignment_list, - argument_or_redirection, - argument_or_redirection_list, - statement, - job_pipeline, - job_conjunction, - for_header, - while_header, - function_header, - begin_header, - block_statement, - if_clause, - elseif_clause, - elseif_clause_list, - else_clause, - if_statement, - case_item, - switch_statement, - decorated_statement, - not_statement, - job_continuation, - job_continuation_list, - job_conjunction_continuation, - andor_job, - andor_job_list, - freestanding_argument_list, - token_conjunction, - job_conjunction_continuation_list, - maybe_newlines, - token_pipe, - case_item_list, - argument, - argument_list, - job_list, - } - extern "Rust" { - type Ast; - type NodeFfi<'a>; - type ExtrasFFI<'a>; - unsafe fn ast_parse_ffi( - src: &CxxWString, - flags: u8, - errors: *mut ParseErrorListFfi, - ) -> Box; - unsafe fn ast_parse_argument_list_ffi( - src: &CxxWString, - flags: u8, - errors: *mut ParseErrorListFfi, - ) -> Box; - unsafe fn errored(self: &Ast) -> bool; - #[cxx_name = "top"] - unsafe fn top_ffi(self: &Ast) -> Box>; - - #[cxx_name = "parent"] - unsafe fn parent_ffi<'a>(self: &'a NodeFfi<'a>) -> Box>; - - #[cxx_name = "dump"] - unsafe fn dump_ffi(self: &Ast, orig: &CxxWString) -> UniquePtr; - #[cxx_name = "extras"] - fn extras_ffi(self: &Ast) -> Box>; - unsafe fn comments<'a>(self: &'a ExtrasFFI<'a>) -> &'a [SourceRange]; - unsafe fn semis<'a>(self: &'a ExtrasFFI<'a>) -> &'a [SourceRange]; - unsafe fn errors<'a>(self: &'a ExtrasFFI<'a>) -> &'a [SourceRange]; - #[cxx_name = "ast_type_to_string"] - fn ast_type_to_string_ffi(typ: Type) -> wcharz_t; - type Traversal<'a>; - unsafe fn new_ast_traversal<'a>(root: &'a NodeFfi<'a>) -> Box>; - #[cxx_name = "next"] - unsafe fn next_ffi<'a>(self: &'a mut Traversal) -> Box>; - } - - #[rustfmt::skip] - extern "Rust" { - type BlockStatementHeaderVariant; - type StatementVariant; - - unsafe fn ptr<'a>(self: &'a NodeFfi<'a>) -> Box>; - unsafe fn describe(self: &NodeFfi<'_>) -> UniquePtr; - unsafe fn typ(self: &NodeFfi<'_>) -> Type; - unsafe fn category(self: &NodeFfi<'_>) -> Category; - unsafe fn pointer_eq(self: &NodeFfi<'_>, rhs: &NodeFfi) -> bool; - unsafe fn has_value(self: &NodeFfi<'_>) -> bool; - - unsafe fn keyword(self: &NodeFfi<'_>) -> ParseKeyword; - unsafe fn token_type(self: &NodeFfi<'_>) -> ParseTokenType; - unsafe fn has_source(self: &NodeFfi<'_>) -> bool; - - fn token_type(self: &TokenConjunction) -> ParseTokenType; - - type AndorJobList; - type AndorJob; - type ArgumentList; - type Argument; - type ArgumentOrRedirectionList; - type ArgumentOrRedirection; - type BeginHeader; - type BlockStatement; - type CaseItemList; - type CaseItem; - type DecoratedStatementDecorator; - type DecoratedStatement; - type ElseClause; - type ElseifClauseList; - type ElseifClause; - type ForHeader; - type FreestandingArgumentList; - type FunctionHeader; - type IfClause; - type IfStatement; - type JobConjunctionContinuationList; - type JobConjunctionContinuation; - type JobConjunctionDecorator; - type JobConjunction; - type JobContinuationList; - type JobContinuation; - type JobList; - type JobPipeline; - type KeywordBegin; - type KeywordCase; - type KeywordElse; - type KeywordEnd; - type KeywordFor; - type KeywordFunction; - type KeywordIf; - type KeywordIn; - type KeywordNot; - type KeywordTime; - type KeywordWhile; - type MaybeNewlines; - type NotStatement; - type Redirection; - type SemiNl; - type Statement; - type String_; - type SwitchStatement; - type TokenBackground; - type TokenConjunction; - type TokenPipe; - type TokenRedirection; - type VariableAssignmentList; - type VariableAssignment; - type WhileHeader; - - fn count(self: &ArgumentList) -> usize; - fn empty(self: &ArgumentList) -> bool; - unsafe fn at(self: & ArgumentList, i: usize) -> *const Argument; - - fn count(self: &ArgumentOrRedirectionList) -> usize; - fn empty(self: &ArgumentOrRedirectionList) -> bool; - unsafe fn at(self: & ArgumentOrRedirectionList, i: usize) -> *const ArgumentOrRedirection; - - fn count(self: &JobList) -> usize; - fn empty(self: &JobList) -> bool; - unsafe fn at(self: & JobList, i: usize) -> *const JobConjunction; - - fn count(self: &JobContinuationList) -> usize; - fn empty(self: &JobContinuationList) -> bool; - unsafe fn at(self: & JobContinuationList, i: usize) -> *const JobContinuation; - - fn count(self: &ElseifClauseList) -> usize; - fn empty(self: &ElseifClauseList) -> bool; - unsafe fn at(self: & ElseifClauseList, i: usize) -> *const ElseifClause; - - fn count(self: &CaseItemList) -> usize; - fn empty(self: &CaseItemList) -> bool; - unsafe fn at(self: & CaseItemList, i: usize) -> *const CaseItem; - - fn count(self: &VariableAssignmentList) -> usize; - fn empty(self: &VariableAssignmentList) -> bool; - unsafe fn at(self: & VariableAssignmentList, i: usize) -> *const VariableAssignment; - - fn count(self: &JobConjunctionContinuationList) -> usize; - fn empty(self: &JobConjunctionContinuationList) -> bool; - unsafe fn at(self: & JobConjunctionContinuationList, i: usize) -> *const JobConjunctionContinuation; - - fn count(self: &AndorJobList) -> usize; - fn empty(self: &AndorJobList) -> bool; - unsafe fn at(self: & AndorJobList, i: usize) -> *const AndorJob; - - fn describe(self: &Statement) -> UniquePtr; - - #[cxx_name = "keyword"] - fn keyword_ffi(self: &JobConjunctionDecorator) -> ParseKeyword; - fn decoration(self: &DecoratedStatement) -> StatementDecoration; - - fn is_argument(self: &ArgumentOrRedirection) -> bool; - unsafe fn argument(self: & ArgumentOrRedirection) -> & Argument; - fn is_redirection(self: &ArgumentOrRedirection) -> bool; - unsafe fn redirection(self: & ArgumentOrRedirection) -> & Redirection; - - unsafe fn contents(self: &Statement) -> &StatementVariant; - unsafe fn oper(self: &Redirection) -> &TokenRedirection; - unsafe fn target(self: &Redirection) -> &String_; - unsafe fn argument_ffi(self: &ArgumentOrRedirection) -> &Argument; - unsafe fn redirection_ffi(self: &ArgumentOrRedirection) -> &Redirection; - fn has_time(self: &JobPipeline) -> bool; - unsafe fn time(self: &JobPipeline) -> &KeywordTime; - unsafe fn variables(self: &JobPipeline) -> &VariableAssignmentList; - unsafe fn statement(self: &JobPipeline) -> &Statement; - unsafe fn continuation(self: &JobPipeline) -> &JobContinuationList; - fn has_bg(self: &JobPipeline) -> bool; - unsafe fn bg(self: &JobPipeline) -> &TokenBackground; - fn has_decorator(self: &JobConjunction) -> bool; - unsafe fn decorator(self: &JobConjunction) -> &JobConjunctionDecorator; - unsafe fn job(self: &JobConjunction) -> &JobPipeline; - unsafe fn continuations(self: &JobConjunction) -> &JobConjunctionContinuationList; - fn has_semi_nl(self: &JobConjunction) -> bool; - unsafe fn semi_nl(self: &JobConjunction) -> &SemiNl; - unsafe fn var_name(self: &ForHeader) -> &String_; - unsafe fn args(self: &ForHeader) -> &ArgumentList; - unsafe fn semi_nl(self: &ForHeader) -> &SemiNl; - unsafe fn condition(self: &WhileHeader) -> &JobConjunction; - unsafe fn andor_tail(self: &WhileHeader) -> &AndorJobList; - unsafe fn first_arg(self: &FunctionHeader) -> &Argument; - unsafe fn args(self: &FunctionHeader) -> &ArgumentList; - unsafe fn semi_nl(self: &FunctionHeader) -> &SemiNl; - fn has_semi_nl(self: &BeginHeader) -> bool; - unsafe fn semi_nl(self: &BeginHeader) -> &SemiNl; - unsafe fn header(self: &BlockStatement) -> &BlockStatementHeaderVariant; - unsafe fn jobs(self: &BlockStatement) -> &JobList; - unsafe fn args_or_redirs(self: &BlockStatement) -> &ArgumentOrRedirectionList; - unsafe fn condition(self: &IfClause) -> &JobConjunction; - unsafe fn andor_tail(self: &IfClause) -> &AndorJobList; - unsafe fn body(self: &IfClause) -> &JobList; - unsafe fn if_clause(self: &ElseifClause) -> &IfClause; - unsafe fn semi_nl(self: &ElseClause) -> &SemiNl; - unsafe fn body(self: &ElseClause) -> &JobList; - unsafe fn if_clause(self: &IfStatement) -> &IfClause; - unsafe fn elseif_clauses(self: &IfStatement) -> &ElseifClauseList; - fn has_else_clause(self: &IfStatement) -> bool; - unsafe fn else_clause(self: &IfStatement) -> &ElseClause; - unsafe fn end(self: &IfStatement) -> &KeywordEnd; - unsafe fn args_or_redirs(self: &IfStatement) -> &ArgumentOrRedirectionList; - unsafe fn arguments(self: &CaseItem) -> &ArgumentList; - unsafe fn semi_nl(self: &CaseItem) -> &SemiNl; - unsafe fn body(self: &CaseItem) -> &JobList; - unsafe fn argument(self: &SwitchStatement) -> &Argument; - unsafe fn semi_nl(self: &SwitchStatement) -> &SemiNl; - unsafe fn cases(self: &SwitchStatement) -> &CaseItemList; - unsafe fn end(self: &SwitchStatement) -> &KeywordEnd; - unsafe fn args_or_redirs(self: &SwitchStatement) -> &ArgumentOrRedirectionList; - fn has_opt_decoration(self: &DecoratedStatement) -> bool; - unsafe fn opt_decoration(self: &DecoratedStatement) -> &DecoratedStatementDecorator; - unsafe fn command(self: &DecoratedStatement) -> &String_; - unsafe fn args_or_redirs(self: &DecoratedStatement) -> &ArgumentOrRedirectionList; - unsafe fn variables(self: &NotStatement) -> &VariableAssignmentList; - fn has_time(self: &NotStatement) -> bool; - unsafe fn time(self: &NotStatement) -> &KeywordTime; - unsafe fn contents(self: &NotStatement) -> &Statement; - unsafe fn pipe(self: &JobContinuation) -> &TokenPipe; - unsafe fn newlines(self: &JobContinuation) -> &MaybeNewlines; - unsafe fn variables(self: &JobContinuation) -> &VariableAssignmentList; - unsafe fn statement(self: &JobContinuation) -> &Statement; - unsafe fn conjunction(self: &JobConjunctionContinuation) -> &TokenConjunction; - unsafe fn newlines(self: &JobConjunctionContinuation) -> &MaybeNewlines; - unsafe fn job(self: &JobConjunctionContinuation) -> &JobPipeline; - unsafe fn job(self: &AndorJob) -> &JobConjunction; - unsafe fn arguments(self: &FreestandingArgumentList) -> &ArgumentList; - unsafe fn kw_begin(self: &BeginHeader) -> &KeywordBegin; - unsafe fn end(self: &BlockStatement) -> &KeywordEnd; - } - - #[rustfmt::skip] - extern "Rust" { - #[cxx_name="source"] fn source_ffi(self: &NodeFfi<'_>, orig: &CxxWString) -> UniquePtr; - #[cxx_name="source"] fn source_ffi(self: &Argument, orig: &CxxWString) -> UniquePtr; - #[cxx_name="source"] fn source_ffi(self: &VariableAssignment, orig: &CxxWString) -> UniquePtr; - #[cxx_name="source"] fn source_ffi(self: &String_, orig: &CxxWString) -> UniquePtr; - #[cxx_name="source"] fn source_ffi(self: &TokenRedirection, orig: &CxxWString) -> UniquePtr; - - #[cxx_name = "try_source_range"] unsafe fn try_source_range_ffi(self: &NodeFfi) -> bool; - #[cxx_name = "try_source_range"] fn try_source_range_ffi(self: &Argument) -> bool; - #[cxx_name = "try_source_range"] fn try_source_range_ffi(self: &JobPipeline) -> bool; - #[cxx_name = "try_source_range"] fn try_source_range_ffi(self: &String_) -> bool; - #[cxx_name = "try_source_range"] fn try_source_range_ffi(self: &BlockStatement) -> bool; - #[cxx_name = "try_source_range"] fn try_source_range_ffi(self: &KeywordEnd) -> bool; - #[cxx_name = "try_source_range"] fn try_source_range_ffi(self: &VariableAssignment) -> bool; - #[cxx_name = "try_source_range"] fn try_source_range_ffi(self: &SemiNl) -> bool; - - #[cxx_name = "source_range"] unsafe fn source_range_ffi(self: &NodeFfi) -> SourceRange; - #[cxx_name = "source_range"] fn source_range_ffi(self: &JobConjunctionDecorator) -> SourceRange; - #[cxx_name = "source_range"] fn source_range_ffi(self: &DecoratedStatement) -> SourceRange; - #[cxx_name = "source_range"] fn source_range_ffi(self: &Argument) -> SourceRange; - #[cxx_name = "source_range"] fn source_range_ffi(self: &JobPipeline) -> SourceRange; - #[cxx_name = "source_range"] fn source_range_ffi(self: &String_) -> SourceRange; - #[cxx_name = "source_range"] fn source_range_ffi(self: &BlockStatement) -> SourceRange; - #[cxx_name = "source_range"] fn source_range_ffi(self: &KeywordEnd) -> SourceRange; - #[cxx_name = "source_range"] fn source_range_ffi(self: &VariableAssignment) -> SourceRange; - #[cxx_name = "source_range"] fn source_range_ffi(self: &SemiNl) -> SourceRange; - } - - #[rustfmt::skip] - extern "Rust" { - unsafe fn try_as_block_statement(self: & StatementVariant) -> *const BlockStatement; - unsafe fn try_as_if_statement(self: & StatementVariant) -> *const IfStatement; - unsafe fn try_as_switch_statement(self: & StatementVariant) -> *const SwitchStatement; - unsafe fn try_as_decorated_statement(self: & StatementVariant) -> *const DecoratedStatement; - } - - #[rustfmt::skip] - extern "Rust" { - unsafe fn try_as_argument(self: &NodeFfi) -> *const Argument; - unsafe fn try_as_begin_header(self: &NodeFfi) -> *const BeginHeader; - unsafe fn try_as_block_statement(self: &NodeFfi) -> *const BlockStatement; - unsafe fn try_as_decorated_statement(self: &NodeFfi) -> *const DecoratedStatement; - unsafe fn try_as_for_header(self: &NodeFfi) -> *const ForHeader; - unsafe fn try_as_function_header(self: &NodeFfi) -> *const FunctionHeader; - unsafe fn try_as_if_clause(self: &NodeFfi) -> *const IfClause; - unsafe fn try_as_if_statement(self: &NodeFfi) -> *const IfStatement; - unsafe fn try_as_job_conjunction(self: &NodeFfi) -> *const JobConjunction; - unsafe fn try_as_job_conjunction_continuation(self: &NodeFfi) -> *const JobConjunctionContinuation; - unsafe fn try_as_job_continuation(self: &NodeFfi) -> *const JobContinuation; - unsafe fn try_as_job_list(self: &NodeFfi) -> *const JobList; - unsafe fn try_as_job_pipeline(self: &NodeFfi) -> *const JobPipeline; - unsafe fn try_as_not_statement(self: &NodeFfi) -> *const NotStatement; - unsafe fn try_as_switch_statement(self: &NodeFfi) -> *const SwitchStatement; - unsafe fn try_as_while_header(self: &NodeFfi) -> *const WhileHeader; - } - - #[rustfmt::skip] - extern "Rust" { - unsafe fn as_if_clause<'a>(self: &'a NodeFfi<'a>) -> &'a IfClause; - unsafe fn as_job_conjunction<'a>(self: &'a NodeFfi) -> &'a JobConjunction; - unsafe fn as_job_pipeline<'a>(self: &'a NodeFfi<'a>) -> &'a JobPipeline; - unsafe fn as_argument<'a>(self: &'a NodeFfi<'a>) -> &'a Argument; - unsafe fn as_begin_header<'a>(self: &'a NodeFfi<'a>) -> &'a BeginHeader; - unsafe fn as_block_statement<'a>(self: &'a NodeFfi<'a>) -> &'a BlockStatement; - unsafe fn as_decorated_statement<'a>(self: &'a NodeFfi<'a>) -> &'a DecoratedStatement; - unsafe fn as_for_header<'a>(self: &'a NodeFfi<'a>) -> &'a ForHeader; - unsafe fn as_freestanding_argument_list<'a>(self: &'a NodeFfi<'a>) -> &'a FreestandingArgumentList; - unsafe fn as_function_header<'a>(self: &'a NodeFfi<'a>) -> &'a FunctionHeader; - unsafe fn as_if_statement<'a>(self: &'a NodeFfi<'a>) -> &'a IfStatement; - unsafe fn as_job_conjunction_continuation<'a>(self: &'a NodeFfi<'a>) -> &'a JobConjunctionContinuation; - unsafe fn as_job_continuation<'a>(self: &'a NodeFfi<'a>) -> &'a JobContinuation; - unsafe fn as_job_list<'a>(self: &'a NodeFfi<'a>) -> &'a JobList; - unsafe fn as_not_statement<'a>(self: &'a NodeFfi<'a>) -> &'a NotStatement; - unsafe fn as_redirection<'a>(self: &'a NodeFfi<'a>) -> &'a Redirection; - unsafe fn as_statement<'a>(self: &'a NodeFfi<'a>) -> &'a Statement; - unsafe fn as_switch_statement<'a>(self: &'a NodeFfi<'a>) -> &'a SwitchStatement; - unsafe fn as_while_header<'a>(self: &'a NodeFfi<'a>) -> &'a WhileHeader; - } - - #[rustfmt::skip] - extern "Rust" { - unsafe fn ptr(self: &StatementVariant) -> Box>; - unsafe fn ptr(self: &BlockStatementHeaderVariant) -> Box>; - unsafe fn ptr(self: &AndorJobList) -> Box>; - unsafe fn ptr(self: &AndorJob) -> Box>; - unsafe fn ptr(self: &ArgumentList) -> Box>; - unsafe fn ptr(self: &Argument) -> Box>; - unsafe fn ptr(self: &ArgumentOrRedirectionList) -> Box>; - unsafe fn ptr(self: &ArgumentOrRedirection) -> Box>; - unsafe fn ptr(self: &BeginHeader) -> Box>; - unsafe fn ptr(self: &BlockStatement) -> Box>; - unsafe fn ptr(self: &CaseItemList) -> Box>; - unsafe fn ptr(self: &CaseItem) -> Box>; - unsafe fn ptr(self: &DecoratedStatementDecorator) -> Box>; - unsafe fn ptr(self: &DecoratedStatement) -> Box>; - unsafe fn ptr(self: &ElseClause) -> Box>; - unsafe fn ptr(self: &ElseifClauseList) -> Box>; - unsafe fn ptr(self: &ElseifClause) -> Box>; - unsafe fn ptr(self: &ForHeader) -> Box>; - unsafe fn ptr(self: &FreestandingArgumentList) -> Box>; - unsafe fn ptr(self: &FunctionHeader) -> Box>; - unsafe fn ptr(self: &IfClause) -> Box>; - unsafe fn ptr(self: &IfStatement) -> Box>; - unsafe fn ptr(self: &JobConjunctionContinuationList) -> Box>; - unsafe fn ptr(self: &JobConjunctionContinuation) -> Box>; - unsafe fn ptr(self: &JobConjunctionDecorator) -> Box>; - unsafe fn ptr(self: &JobConjunction) -> Box>; - unsafe fn ptr(self: &JobContinuationList) -> Box>; - unsafe fn ptr(self: &JobContinuation) -> Box>; - unsafe fn ptr(self: &JobList) -> Box>; - unsafe fn ptr(self: &JobPipeline) -> Box>; - unsafe fn ptr(self: &KeywordBegin) -> Box>; - unsafe fn ptr(self: &KeywordCase) -> Box>; - unsafe fn ptr(self: &KeywordElse) -> Box>; - unsafe fn ptr(self: &KeywordEnd) -> Box>; - unsafe fn ptr(self: &KeywordFor) -> Box>; - unsafe fn ptr(self: &KeywordFunction) -> Box>; - unsafe fn ptr(self: &KeywordIf) -> Box>; - unsafe fn ptr(self: &KeywordIn) -> Box>; - unsafe fn ptr(self: &KeywordNot) -> Box>; - unsafe fn ptr(self: &KeywordTime) -> Box>; - unsafe fn ptr(self: &KeywordWhile) -> Box>; - unsafe fn ptr(self: &MaybeNewlines) -> Box>; - unsafe fn ptr(self: &NotStatement) -> Box>; - unsafe fn ptr(self: &Redirection) -> Box>; - unsafe fn ptr(self: &SemiNl) -> Box>; - unsafe fn ptr(self: &Statement) -> Box>; - unsafe fn ptr(self: &String_) -> Box>; - unsafe fn ptr(self: &SwitchStatement) -> Box>; - unsafe fn ptr(self: &TokenBackground) -> Box>; - unsafe fn ptr(self: &TokenConjunction) -> Box>; - unsafe fn ptr(self: &TokenPipe) -> Box>; - unsafe fn ptr(self: &TokenRedirection) -> Box>; - unsafe fn ptr(self: &VariableAssignmentList) -> Box>; - unsafe fn ptr(self: &VariableAssignment) -> Box>; - unsafe fn ptr(self: &WhileHeader) -> Box>; - } - - #[rustfmt::skip] - extern "Rust" { - unsafe fn range(self: &VariableAssignment) -> SourceRange; - unsafe fn range(self: &TokenConjunction) -> SourceRange; - unsafe fn range(self: &MaybeNewlines) -> SourceRange; - unsafe fn range(self: &TokenPipe) -> SourceRange; - unsafe fn range(self: &KeywordNot) -> SourceRange; - unsafe fn range(self: &DecoratedStatementDecorator) -> SourceRange; - unsafe fn range(self: &KeywordEnd) -> SourceRange; - unsafe fn range(self: &KeywordCase) -> SourceRange; - unsafe fn range(self: &KeywordElse) -> SourceRange; - unsafe fn range(self: &KeywordIf) -> SourceRange; - unsafe fn range(self: &KeywordBegin) -> SourceRange; - unsafe fn range(self: &KeywordFunction) -> SourceRange; - unsafe fn range(self: &KeywordWhile) -> SourceRange; - unsafe fn range(self: &KeywordFor) -> SourceRange; - unsafe fn range(self: &KeywordIn) -> SourceRange; - unsafe fn range(self: &SemiNl) -> SourceRange; - unsafe fn range(self: &JobConjunctionDecorator) -> SourceRange; - unsafe fn range(self: &TokenBackground) -> SourceRange; - unsafe fn range(self: &KeywordTime) -> SourceRange; - unsafe fn range(self: &TokenRedirection) -> SourceRange; - unsafe fn range(self: &String_) -> SourceRange; - unsafe fn range(self: &Argument) -> SourceRange; - } -} - -impl Ast { - fn extras_ffi(self: &Ast) -> Box> { - Box::new(ExtrasFFI(&self.extras)) - } -} - -struct ExtrasFFI<'a>(&'a Extras); - -impl<'a> ExtrasFFI<'a> { - fn comments(&self) -> &'a [SourceRange] { - &self.0.comments - } - fn semis(&self) -> &'a [SourceRange] { - &self.0.semis - } - fn errors(&self) -> &'a [SourceRange] { - &self.0.errors - } -} - -unsafe impl ExternType for Ast { - type Id = type_id!("Ast"); - type Kind = cxx::kind::Opaque; -} - -unsafe impl ExternType for DecoratedStatement { - type Id = type_id!("DecoratedStatement"); - type Kind = cxx::kind::Opaque; -} - -impl Ast { - fn top_ffi(&self) -> Box { - Box::new(NodeFfi::new(self.top.as_node())) - } - fn dump_ffi(&self, orig: &CxxWString) -> UniquePtr { - self.dump(orig.as_wstr()).to_ffi() - } -} - -fn ast_parse_ffi(src: &CxxWString, flags: u8, errors: *mut ParseErrorListFfi) -> Box { - Box::new(Ast::parse( - src.as_wstr(), - ParseTreeFlags::from_bits(flags).unwrap(), - if errors.is_null() { - None - } else { - Some(unsafe { &mut (*errors).0 }) - }, - )) -} - -fn ast_parse_argument_list_ffi( - src: &CxxWString, - flags: u8, - errors: *mut ParseErrorListFfi, -) -> Box { - Box::new(Ast::parse_argument_list( - src.as_wstr(), - ParseTreeFlags::from_bits(flags).unwrap(), - if errors.is_null() { - None - } else { - Some(unsafe { &mut (*errors).0 }) - }, - )) -} - -fn new_ast_traversal<'a>(root: &'a NodeFfi<'a>) -> Box> { - Box::new(Traversal::new(root.as_node())) -} - -impl<'a> Traversal<'a> { - fn next_ffi(&mut self) -> Box> { - Box::new(match self.next() { - Some(node) => NodeFfi::Reference(node), - None => NodeFfi::None, - }) - } -} - -impl TokenConjunction { - fn token_type(&self) -> ParseTokenType { - (self as &dyn Token).token_type() - } -} - -impl Statement { - fn contents(&self) -> &StatementVariant { - &self.contents - } -} - -unsafe impl ExternType for BlockStatement { - type Id = type_id!("BlockStatement"); - type Kind = cxx::kind::Opaque; -} - -#[derive(Clone)] -pub enum NodeFfi<'a> { - None, - Reference(&'a dyn Node), - Pointer(*const dyn Node), -} - -unsafe impl ExternType for NodeFfi<'_> { - type Id = type_id!("NodeFfi"); - type Kind = cxx::kind::Opaque; -} - -impl<'a> NodeFfi<'a> { - pub fn new(node: &'a dyn Node) -> Self { - NodeFfi::Reference(node) - } - fn has_value(&self) -> bool { - !matches!(self, NodeFfi::None) - } - pub fn as_node(&self) -> &dyn Node { - match *self { - NodeFfi::None => panic!("tried to dereference null node"), - NodeFfi::Reference(node) => node, - NodeFfi::Pointer(node) => unsafe { &*node }, - } - } - fn parent_ffi(&self) -> Box> { - Box::new(match *self.as_node().parent_ffi() { - Some(node) => NodeFfi::Pointer(node), - None => NodeFfi::None, - }) - } - fn category(&self) -> Category { - self.as_node().category() - } - fn typ(&self) -> Type { - self.as_node().typ() - } - fn describe(&self) -> UniquePtr { - self.as_node().describe().to_ffi() - } - // Pointer comparison - fn pointer_eq(&self, rhs: &NodeFfi) -> bool { - std::ptr::eq(self.as_node().as_ptr(), rhs.as_node().as_ptr()) - } - fn keyword(&self) -> ParseKeyword { - self.as_node().as_keyword().unwrap().keyword() - } - fn token_type(&self) -> ParseTokenType { - self.as_node().as_token().unwrap().token_type() - } - fn has_source(&self) -> bool { - self.as_node().as_leaf().unwrap().range().is_some() - } - fn ptr(&self) -> Box> { - Box::new(self.clone()) - } - fn try_source_range_ffi(&self) -> bool { - self.as_node().try_source_range().is_some() - } - fn source_range_ffi(&self) -> SourceRange { - self.as_node().source_range() - } - fn source_ffi(&self, orig: &CxxWString) -> UniquePtr { - self.as_node().source(orig.as_wstr()).to_ffi() - } -} - -impl Argument { - fn source_ffi(&self, orig: &CxxWString) -> UniquePtr { - self.source(orig.as_wstr()).to_ffi() - } -} -impl VariableAssignment { - fn source_ffi(&self, orig: &CxxWString) -> UniquePtr { - self.source(orig.as_wstr()).to_ffi() - } -} -impl String_ { - fn source_ffi(&self, orig: &CxxWString) -> UniquePtr { - self.source(orig.as_wstr()).to_ffi() - } -} -impl TokenRedirection { - fn source_ffi(&self, orig: &CxxWString) -> UniquePtr { - self.source(orig.as_wstr()).to_ffi() - } -} - -impl ArgumentList { - fn count(&self) -> usize { - >::count(self) - } - fn empty(&self) -> bool { - self.is_empty() - } - fn at(&self, i: usize) -> *const Argument { - &self[i] - } -} - -impl ArgumentOrRedirectionList { - fn count(&self) -> usize { - >::count(self) - } - fn empty(&self) -> bool { - self.is_empty() - } - fn at(&self, i: usize) -> *const ArgumentOrRedirection { - if i >= self.count() { - std::ptr::null() - } else { - &self[i] - } - } -} - -impl JobList { - fn count(&self) -> usize { - >::count(self) - } - fn empty(&self) -> bool { - self.is_empty() - } - fn at(&self, i: usize) -> *const JobConjunction { - if i >= self.count() { - std::ptr::null() - } else { - &self[i] - } - } -} - -impl JobContinuationList { - fn count(&self) -> usize { - >::count(self) - } - fn empty(&self) -> bool { - self.is_empty() - } - fn at(&self, i: usize) -> *const JobContinuation { - if i >= self.count() { - std::ptr::null() - } else { - &self[i] - } - } -} - -impl ElseifClauseList { - fn count(&self) -> usize { - >::count(self) - } - fn empty(&self) -> bool { - self.is_empty() - } - fn at(&self, i: usize) -> *const ElseifClause { - if i >= self.count() { - std::ptr::null() - } else { - &self[i] - } - } -} - -impl CaseItemList { - fn count(&self) -> usize { - >::count(self) - } - fn empty(&self) -> bool { - self.is_empty() - } - fn at(&self, i: usize) -> *const CaseItem { - if i >= self.count() { - std::ptr::null() - } else { - &self[i] - } - } -} - -impl VariableAssignmentList { - fn count(&self) -> usize { - >::count(self) - } - fn empty(&self) -> bool { - self.is_empty() - } - fn at(&self, i: usize) -> *const VariableAssignment { - if i >= self.count() { - std::ptr::null() - } else { - &self[i] - } - } -} - -impl JobConjunctionContinuationList { - fn count(&self) -> usize { - >::count(self) - } - fn empty(&self) -> bool { - self.is_empty() - } - fn at(&self, i: usize) -> *const JobConjunctionContinuation { - if i >= self.count() { - std::ptr::null() - } else { - &self[i] - } - } -} - -impl AndorJobList { - fn count(&self) -> usize { - >::count(self) - } - fn empty(&self) -> bool { - self.is_empty() - } - fn at(&self, i: usize) -> *const AndorJob { - if i >= self.count() { - std::ptr::null() - } else { - &self[i] - } - } -} - -impl Statement { - fn describe(&self) -> UniquePtr { - (self as &dyn Node).describe().to_ffi() - } -} - -impl JobConjunctionDecorator { - fn keyword_ffi(&self) -> ParseKeyword { - self.keyword() - } - fn source_range_ffi(&self) -> SourceRange { - self.source_range() - } -} -impl DecoratedStatement { - fn source_range_ffi(&self) -> SourceRange { - self.source_range() - } -} - -impl Argument { - fn try_source_range_ffi(&self) -> bool { - self.try_source_range().is_some() - } - fn source_range_ffi(&self) -> SourceRange { - self.source_range() - } -} - -impl JobPipeline { - fn try_source_range_ffi(&self) -> bool { - self.try_source_range().is_some() - } - fn source_range_ffi(&self) -> SourceRange { - self.source_range() - } -} - -impl String_ { - fn try_source_range_ffi(&self) -> bool { - self.try_source_range().is_some() - } - fn source_range_ffi(&self) -> SourceRange { - self.source_range() - } -} - -impl BlockStatement { - fn try_source_range_ffi(&self) -> bool { - self.try_source_range().is_some() - } - fn source_range_ffi(&self) -> SourceRange { - self.source_range() - } -} - -impl KeywordEnd { - fn try_source_range_ffi(&self) -> bool { - self.try_source_range().is_some() - } - fn source_range_ffi(&self) -> SourceRange { - self.source_range() - } -} - -impl VariableAssignment { - fn try_source_range_ffi(&self) -> bool { - self.try_source_range().is_some() - } - fn source_range_ffi(&self) -> SourceRange { - self.source_range() - } -} - -impl SemiNl { - fn try_source_range_ffi(&self) -> bool { - self.try_source_range().is_some() - } - fn source_range_ffi(&self) -> SourceRange { - self.source_range() - } -} - -impl Redirection { - fn oper(&self) -> &TokenRedirection { - &self.oper - } -} -impl Redirection { - fn target(&self) -> &String_ { - &self.target - } -} -impl ArgumentOrRedirection { - fn argument_ffi(&self) -> &Argument { - self.argument() - } -} -impl ArgumentOrRedirection { - fn redirection_ffi(&self) -> &Redirection { - self.redirection() - } -} -impl JobPipeline { - fn has_time(&self) -> bool { - self.time.is_some() - } -} -impl JobPipeline { - fn time(&self) -> &KeywordTime { - self.time.as_ref().unwrap() - } -} -impl JobPipeline { - fn variables(&self) -> &VariableAssignmentList { - &self.variables - } -} -impl JobPipeline { - fn statement(&self) -> &Statement { - &self.statement - } -} -impl JobPipeline { - fn continuation(&self) -> &JobContinuationList { - &self.continuation - } -} -impl JobPipeline { - fn has_bg(&self) -> bool { - self.bg.is_some() - } -} -impl JobPipeline { - fn bg(&self) -> &TokenBackground { - self.bg.as_ref().unwrap() - } -} -impl JobConjunction { - fn has_decorator(&self) -> bool { - self.decorator.is_some() - } -} -impl JobConjunction { - fn decorator(&self) -> &JobConjunctionDecorator { - self.decorator.as_ref().unwrap() - } -} -impl JobConjunction { - fn job(&self) -> &JobPipeline { - &self.job - } -} -impl JobConjunction { - fn continuations(&self) -> &JobConjunctionContinuationList { - &self.continuations - } -} -impl JobConjunction { - fn has_semi_nl(&self) -> bool { - self.semi_nl.is_some() - } -} -impl JobConjunction { - fn semi_nl(&self) -> &SemiNl { - self.semi_nl.as_ref().unwrap() - } -} -impl ForHeader { - fn var_name(&self) -> &String_ { - &self.var_name - } -} -impl ForHeader { - fn args(&self) -> &ArgumentList { - &self.args - } -} -impl ForHeader { - fn semi_nl(&self) -> &SemiNl { - &self.semi_nl - } -} -impl WhileHeader { - fn condition(&self) -> &JobConjunction { - &self.condition - } -} -impl WhileHeader { - fn andor_tail(&self) -> &AndorJobList { - &self.andor_tail - } -} -impl FunctionHeader { - fn first_arg(&self) -> &Argument { - &self.first_arg - } -} -impl FunctionHeader { - fn args(&self) -> &ArgumentList { - &self.args - } -} -impl FunctionHeader { - fn semi_nl(&self) -> &SemiNl { - &self.semi_nl - } -} -impl BeginHeader { - fn has_semi_nl(&self) -> bool { - self.semi_nl.is_some() - } -} -impl BeginHeader { - fn semi_nl(&self) -> &SemiNl { - self.semi_nl.as_ref().unwrap() - } -} -impl BlockStatement { - fn header(&self) -> &BlockStatementHeaderVariant { - &self.header - } -} -impl BlockStatement { - fn jobs(&self) -> &JobList { - &self.jobs - } -} -impl BlockStatement { - fn args_or_redirs(&self) -> &ArgumentOrRedirectionList { - &self.args_or_redirs - } -} -impl IfClause { - fn condition(&self) -> &JobConjunction { - &self.condition - } -} -impl IfClause { - fn andor_tail(&self) -> &AndorJobList { - &self.andor_tail - } -} -impl IfClause { - fn body(&self) -> &JobList { - &self.body - } -} -impl ElseifClause { - fn if_clause(&self) -> &IfClause { - &self.if_clause - } -} -impl ElseClause { - fn semi_nl(&self) -> &SemiNl { - &self.semi_nl - } -} -impl ElseClause { - fn body(&self) -> &JobList { - &self.body - } -} -impl IfStatement { - fn if_clause(&self) -> &IfClause { - &self.if_clause - } -} -impl IfStatement { - fn elseif_clauses(&self) -> &ElseifClauseList { - &self.elseif_clauses - } -} -impl IfStatement { - fn has_else_clause(&self) -> bool { - self.else_clause.is_some() - } -} -impl IfStatement { - fn else_clause(&self) -> &ElseClause { - self.else_clause.as_ref().unwrap() - } -} -impl IfStatement { - fn end(&self) -> &KeywordEnd { - &self.end - } -} -impl IfStatement { - fn args_or_redirs(&self) -> &ArgumentOrRedirectionList { - &self.args_or_redirs - } -} -impl CaseItem { - fn arguments(&self) -> &ArgumentList { - &self.arguments - } -} -impl CaseItem { - fn semi_nl(&self) -> &SemiNl { - &self.semi_nl - } -} -impl CaseItem { - fn body(&self) -> &JobList { - &self.body - } -} -impl SwitchStatement { - fn argument(&self) -> &Argument { - &self.argument - } -} -impl SwitchStatement { - fn semi_nl(&self) -> &SemiNl { - &self.semi_nl - } -} -impl SwitchStatement { - fn cases(&self) -> &CaseItemList { - &self.cases - } -} -impl SwitchStatement { - fn end(&self) -> &KeywordEnd { - &self.end - } -} -impl SwitchStatement { - fn args_or_redirs(&self) -> &ArgumentOrRedirectionList { - &self.args_or_redirs - } -} -impl DecoratedStatement { - fn has_opt_decoration(&self) -> bool { - self.opt_decoration.is_some() - } -} -impl DecoratedStatement { - fn opt_decoration(&self) -> &DecoratedStatementDecorator { - self.opt_decoration.as_ref().unwrap() - } -} -impl DecoratedStatement { - fn command(&self) -> &String_ { - &self.command - } -} -impl DecoratedStatement { - fn args_or_redirs(&self) -> &ArgumentOrRedirectionList { - &self.args_or_redirs - } -} -impl NotStatement { - fn variables(&self) -> &VariableAssignmentList { - &self.variables - } -} -impl NotStatement { - fn has_time(&self) -> bool { - self.time.is_some() - } -} -impl NotStatement { - fn time(&self) -> &KeywordTime { - self.time.as_ref().unwrap() - } -} -impl NotStatement { - fn contents(&self) -> &Statement { - &self.contents - } -} -impl JobContinuation { - fn pipe(&self) -> &TokenPipe { - &self.pipe - } -} -impl JobContinuation { - fn newlines(&self) -> &MaybeNewlines { - &self.newlines - } -} -impl JobContinuation { - fn variables(&self) -> &VariableAssignmentList { - &self.variables - } -} -impl JobContinuation { - fn statement(&self) -> &Statement { - &self.statement - } -} -impl JobConjunctionContinuation { - fn conjunction(&self) -> &TokenConjunction { - &self.conjunction - } -} -impl JobConjunctionContinuation { - fn newlines(&self) -> &MaybeNewlines { - &self.newlines - } -} -impl JobConjunctionContinuation { - fn job(&self) -> &JobPipeline { - &self.job - } -} -impl AndorJob { - fn job(&self) -> &JobConjunction { - &self.job - } -} -impl FreestandingArgumentList { - fn arguments(&self) -> &ArgumentList { - &self.arguments - } -} -impl BeginHeader { - fn kw_begin(&self) -> &KeywordBegin { - &self.kw_begin - } -} -impl BlockStatement { - fn end(&self) -> &KeywordEnd { - &self.end - } -} - -impl StatementVariant { - fn try_as_not_statement(&self) -> *const NotStatement { - match self { - StatementVariant::NotStatement(node) => node, - _ => std::ptr::null(), - } - } - fn try_as_block_statement(&self) -> *const BlockStatement { - match self { - StatementVariant::BlockStatement(node) => node, - _ => std::ptr::null(), - } - } - fn try_as_if_statement(&self) -> *const IfStatement { - match self { - StatementVariant::IfStatement(node) => node, - _ => std::ptr::null(), - } - } - fn try_as_switch_statement(&self) -> *const SwitchStatement { - match self { - StatementVariant::SwitchStatement(node) => node, - _ => std::ptr::null(), - } - } - fn try_as_decorated_statement(&self) -> *const DecoratedStatement { - match self { - StatementVariant::DecoratedStatement(node) => node, - _ => std::ptr::null(), - } - } -} - -#[rustfmt::skip] -impl NodeFfi<'_> { - fn try_as_argument(&self) -> *const Argument { - match self.as_node().as_argument() { - Some(node) => node, - None => std::ptr::null(), - } - } - fn try_as_begin_header(&self) -> *const BeginHeader { - match self.as_node().as_begin_header() { - Some(node) => node, - None => std::ptr::null(), - } - } - fn try_as_block_statement(&self) -> *const BlockStatement { - match self.as_node().as_block_statement() { - Some(node) => node, - None => std::ptr::null(), - } - } - fn try_as_decorated_statement(&self) -> *const DecoratedStatement { - match self.as_node().as_decorated_statement() { - Some(node) => node, - None => std::ptr::null(), - } - } - fn try_as_for_header(&self) -> *const ForHeader { - match self.as_node().as_for_header() { - Some(node) => node, - None => std::ptr::null(), - } - } - fn try_as_function_header(&self) -> *const FunctionHeader { - match self.as_node().as_function_header() { - Some(node) => node, - None => std::ptr::null(), - } - } - fn try_as_if_clause(&self) -> *const IfClause { - match self.as_node().as_if_clause() { - Some(node) => node, - None => std::ptr::null(), - } - } - fn try_as_if_statement(&self) -> *const IfStatement { - match self.as_node().as_if_statement() { - Some(node) => node, - None => std::ptr::null(), - } - } - fn try_as_job_conjunction(&self) -> *const JobConjunction { - match self.as_node().as_job_conjunction() { - Some(node) => node, - None => std::ptr::null(), - } - } - fn try_as_job_conjunction_continuation(&self) -> *const JobConjunctionContinuation { - match self.as_node().as_job_conjunction_continuation() { - Some(node) => node, - None => std::ptr::null(), - } - } - fn try_as_job_continuation(&self) -> *const JobContinuation { - match self.as_node().as_job_continuation() { - Some(node) => node, - None => std::ptr::null(), - } - } - fn try_as_job_list(&self) -> *const JobList { - match self.as_node().as_job_list() { - Some(node) => node, - None => std::ptr::null(), - } - } - fn try_as_job_pipeline(&self) -> *const JobPipeline { - match self.as_node().as_job_pipeline() { - Some(node) => node, - None => std::ptr::null(), - } - } - fn try_as_not_statement(&self) -> *const NotStatement { - match self.as_node().as_not_statement() { - Some(node) => node, - None => std::ptr::null(), - } - } - fn try_as_switch_statement(&self) -> *const SwitchStatement { - match self.as_node().as_switch_statement() { - Some(node) => node, - None => std::ptr::null(), - } - } - fn try_as_while_header(&self) -> *const WhileHeader { - match self.as_node().as_while_header() { - Some(node) => node, - None => std::ptr::null(), - } - } -} - -#[rustfmt::skip] -impl NodeFfi<'_> { - fn as_if_clause(&self) -> &IfClause { - self.as_node().as_if_clause().unwrap() - } - fn as_job_conjunction(&self) -> &JobConjunction { - self.as_node().as_job_conjunction().unwrap() - } - fn as_job_pipeline(&self) -> &JobPipeline { - self.as_node().as_job_pipeline().unwrap() - } - fn as_argument(&self) -> &Argument { - self.as_node().as_argument().unwrap() - } - fn as_begin_header(&self) -> &BeginHeader { - self.as_node().as_begin_header().unwrap() - } - fn as_block_statement(&self) -> &BlockStatement { - self.as_node().as_block_statement().unwrap() - } - fn as_decorated_statement(&self) -> &DecoratedStatement { - self.as_node().as_decorated_statement().unwrap() - } - fn as_for_header(&self) -> &ForHeader { - self.as_node().as_for_header().unwrap() - } - fn as_freestanding_argument_list(&self) -> &FreestandingArgumentList { - self.as_node().as_freestanding_argument_list().unwrap() - } - fn as_function_header(&self) -> &FunctionHeader { - self.as_node().as_function_header().unwrap() - } - fn as_if_statement(&self) -> &IfStatement { - self.as_node().as_if_statement().unwrap() - } - fn as_job_conjunction_continuation(&self) -> &JobConjunctionContinuation { - self.as_node().as_job_conjunction_continuation().unwrap() - } - fn as_job_continuation(&self) -> &JobContinuation { - self.as_node().as_job_continuation().unwrap() - } - fn as_job_list(&self) -> &JobList { - self.as_node().as_job_list().unwrap() - } - fn as_not_statement(&self) -> &NotStatement { - self.as_node().as_not_statement().unwrap() - } - fn as_redirection(&self) -> &Redirection { - self.as_node().as_redirection().unwrap() - } - fn as_statement(&self) -> &Statement { - self.as_node().as_statement().unwrap() - } - fn as_switch_statement(&self) -> &SwitchStatement { - self.as_node().as_switch_statement().unwrap() - } - fn as_while_header(&self) -> &WhileHeader { - self.as_node().as_while_header().unwrap() - } -} - -impl StatementVariant { - fn ptr(&self) -> Box> { - match self { - StatementVariant::None => panic!(), - StatementVariant::NotStatement(node) => node.ptr(), - StatementVariant::BlockStatement(node) => node.ptr(), - StatementVariant::IfStatement(node) => node.ptr(), - StatementVariant::SwitchStatement(node) => node.ptr(), - StatementVariant::DecoratedStatement(node) => node.ptr(), - } - } -} -impl BlockStatementHeaderVariant { - fn ptr(&self) -> Box> { - match self { - BlockStatementHeaderVariant::None => panic!(), - BlockStatementHeaderVariant::ForHeader(node) => node.ptr(), - BlockStatementHeaderVariant::WhileHeader(node) => node.ptr(), - BlockStatementHeaderVariant::FunctionHeader(node) => node.ptr(), - BlockStatementHeaderVariant::BeginHeader(node) => node.ptr(), - } - } -} - -impl AndorJobList { - fn ptr(&self) -> Box> { - Box::new(NodeFfi::new(self)) - } -} -impl AndorJob { - fn ptr(&self) -> Box> { - Box::new(NodeFfi::new(self)) - } -} -impl ArgumentList { - fn ptr(&self) -> Box> { - Box::new(NodeFfi::new(self)) - } -} -impl Argument { - fn ptr(&self) -> Box> { - Box::new(NodeFfi::new(self)) - } -} -impl ArgumentOrRedirectionList { - fn ptr(&self) -> Box> { - Box::new(NodeFfi::new(self)) - } -} -impl ArgumentOrRedirection { - fn ptr(&self) -> Box> { - Box::new(NodeFfi::new(self)) - } -} -impl BeginHeader { - fn ptr(&self) -> Box> { - Box::new(NodeFfi::new(self)) - } -} -impl BlockStatement { - fn ptr(&self) -> Box> { - Box::new(NodeFfi::new(self)) - } -} -impl CaseItemList { - fn ptr(&self) -> Box> { - Box::new(NodeFfi::new(self)) - } -} -impl CaseItem { - fn ptr(&self) -> Box> { - Box::new(NodeFfi::new(self)) - } -} -impl DecoratedStatementDecorator { - fn ptr(&self) -> Box> { - Box::new(NodeFfi::new(self)) - } -} -impl DecoratedStatement { - fn ptr(&self) -> Box> { - Box::new(NodeFfi::new(self)) - } -} -impl ElseClause { - fn ptr(&self) -> Box> { - Box::new(NodeFfi::new(self)) - } -} -impl ElseifClauseList { - fn ptr(&self) -> Box> { - Box::new(NodeFfi::new(self)) - } -} -impl ElseifClause { - fn ptr(&self) -> Box> { - Box::new(NodeFfi::new(self)) - } -} -impl ForHeader { - fn ptr(&self) -> Box> { - Box::new(NodeFfi::new(self)) - } -} -impl FreestandingArgumentList { - fn ptr(&self) -> Box> { - Box::new(NodeFfi::new(self)) - } -} -impl FunctionHeader { - fn ptr(&self) -> Box> { - Box::new(NodeFfi::new(self)) - } -} -impl IfClause { - fn ptr(&self) -> Box> { - Box::new(NodeFfi::new(self)) - } -} -impl IfStatement { - fn ptr(&self) -> Box> { - Box::new(NodeFfi::new(self)) - } -} -impl JobConjunctionContinuationList { - fn ptr(&self) -> Box> { - Box::new(NodeFfi::new(self)) - } -} -impl JobConjunctionContinuation { - fn ptr(&self) -> Box> { - Box::new(NodeFfi::new(self)) - } -} -impl JobConjunctionDecorator { - fn ptr(&self) -> Box> { - Box::new(NodeFfi::new(self)) - } -} -impl JobConjunction { - fn ptr(&self) -> Box> { - Box::new(NodeFfi::new(self)) - } -} -impl JobContinuationList { - fn ptr(&self) -> Box> { - Box::new(NodeFfi::new(self)) - } -} -impl JobContinuation { - fn ptr(&self) -> Box> { - Box::new(NodeFfi::new(self)) - } -} -impl JobList { - fn ptr(&self) -> Box> { - Box::new(NodeFfi::new(self)) - } -} -impl JobPipeline { - fn ptr(&self) -> Box> { - Box::new(NodeFfi::new(self)) - } -} -impl KeywordBegin { - fn ptr(&self) -> Box> { - Box::new(NodeFfi::new(self)) - } -} -impl KeywordCase { - fn ptr(&self) -> Box> { - Box::new(NodeFfi::new(self)) - } -} -impl KeywordElse { - fn ptr(&self) -> Box> { - Box::new(NodeFfi::new(self)) - } -} -impl KeywordEnd { - fn ptr(&self) -> Box> { - Box::new(NodeFfi::new(self)) - } -} -impl KeywordFor { - fn ptr(&self) -> Box> { - Box::new(NodeFfi::new(self)) - } -} -impl KeywordFunction { - fn ptr(&self) -> Box> { - Box::new(NodeFfi::new(self)) - } -} -impl KeywordIf { - fn ptr(&self) -> Box> { - Box::new(NodeFfi::new(self)) - } -} -impl KeywordIn { - fn ptr(&self) -> Box> { - Box::new(NodeFfi::new(self)) - } -} -impl KeywordNot { - fn ptr(&self) -> Box> { - Box::new(NodeFfi::new(self)) - } -} -impl KeywordTime { - fn ptr(&self) -> Box> { - Box::new(NodeFfi::new(self)) - } -} -impl KeywordWhile { - fn ptr(&self) -> Box> { - Box::new(NodeFfi::new(self)) - } -} -impl MaybeNewlines { - fn ptr(&self) -> Box> { - Box::new(NodeFfi::new(self)) - } -} -impl NotStatement { - fn ptr(&self) -> Box> { - Box::new(NodeFfi::new(self)) - } -} -impl Redirection { - fn ptr(&self) -> Box> { - Box::new(NodeFfi::new(self)) - } -} -impl SemiNl { - fn ptr(&self) -> Box> { - Box::new(NodeFfi::new(self)) - } -} -impl Statement { - fn ptr(&self) -> Box> { - Box::new(NodeFfi::new(self)) - } -} -impl String_ { - fn ptr(&self) -> Box> { - Box::new(NodeFfi::new(self)) - } -} -impl SwitchStatement { - fn ptr(&self) -> Box> { - Box::new(NodeFfi::new(self)) - } -} -impl TokenBackground { - fn ptr(&self) -> Box> { - Box::new(NodeFfi::new(self)) - } -} -impl TokenConjunction { - fn ptr(&self) -> Box> { - Box::new(NodeFfi::new(self)) - } -} -impl TokenPipe { - fn ptr(&self) -> Box> { - Box::new(NodeFfi::new(self)) - } -} -impl TokenRedirection { - fn ptr(&self) -> Box> { - Box::new(NodeFfi::new(self)) - } -} -impl VariableAssignmentList { - fn ptr(&self) -> Box> { - Box::new(NodeFfi::new(self)) - } -} -impl VariableAssignment { - fn ptr(&self) -> Box> { - Box::new(NodeFfi::new(self)) - } -} -impl WhileHeader { - fn ptr(&self) -> Box> { - Box::new(NodeFfi::new(self)) - } -} - -impl VariableAssignment { - fn range(&self) -> SourceRange { - self.range.unwrap() - } -} -impl TokenConjunction { - fn range(&self) -> SourceRange { - self.range.unwrap() - } -} -impl MaybeNewlines { - fn range(&self) -> SourceRange { - self.range.unwrap() - } -} -impl TokenPipe { - fn range(&self) -> SourceRange { - self.range.unwrap() - } -} -impl KeywordNot { - fn range(&self) -> SourceRange { - self.range.unwrap() - } -} -impl DecoratedStatementDecorator { - fn range(&self) -> SourceRange { - self.range.unwrap() - } -} -impl KeywordEnd { - fn range(&self) -> SourceRange { - self.range.unwrap() - } -} -impl KeywordCase { - fn range(&self) -> SourceRange { - self.range.unwrap() - } -} -impl KeywordElse { - fn range(&self) -> SourceRange { - self.range.unwrap() - } -} -impl KeywordIf { - fn range(&self) -> SourceRange { - self.range.unwrap() - } -} -impl KeywordBegin { - fn range(&self) -> SourceRange { - self.range.unwrap() - } -} -impl KeywordFunction { - fn range(&self) -> SourceRange { - self.range.unwrap() - } -} -impl KeywordWhile { - fn range(&self) -> SourceRange { - self.range.unwrap() - } -} -impl KeywordFor { - fn range(&self) -> SourceRange { - self.range.unwrap() - } -} -impl KeywordIn { - fn range(&self) -> SourceRange { - self.range.unwrap() - } -} -impl SemiNl { - fn range(&self) -> SourceRange { - self.range.unwrap() - } -} -impl JobConjunctionDecorator { - fn range(&self) -> SourceRange { - self.range.unwrap() - } -} -impl TokenBackground { - fn range(&self) -> SourceRange { - self.range.unwrap() - } -} -impl KeywordTime { - fn range(&self) -> SourceRange { - self.range.unwrap() - } -} -impl TokenRedirection { - fn range(&self) -> SourceRange { - self.range.unwrap() - } -} -impl String_ { - fn range(&self) -> SourceRange { - self.range.unwrap() - } -} -impl Argument { - fn range(&self) -> SourceRange { - self.range.unwrap() - } +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub enum Category { + branch, + leaf, + list, +} + +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub enum Type { + token_base, + keyword_base, + redirection, + variable_assignment, + variable_assignment_list, + argument_or_redirection, + argument_or_redirection_list, + statement, + job_pipeline, + job_conjunction, + for_header, + while_header, + function_header, + begin_header, + block_statement, + if_clause, + elseif_clause, + elseif_clause_list, + else_clause, + if_statement, + case_item, + switch_statement, + decorated_statement, + not_statement, + job_continuation, + job_continuation_list, + job_conjunction_continuation, + andor_job, + andor_job_list, + freestanding_argument_list, + token_conjunction, + job_conjunction_continuation_list, + maybe_newlines, + token_pipe, + case_item_list, + argument, + argument_list, + job_list, } diff --git a/fish-rust/src/builtins/mod.rs b/fish-rust/src/builtins/mod.rs index 88c8e26e0..64125ced9 100644 --- a/fish-rust/src/builtins/mod.rs +++ b/fish-rust/src/builtins/mod.rs @@ -54,7 +54,6 @@ mod prelude { io::{IoStreams, SeparationType}, parser::Parser, wchar::prelude::*, - wchar_ffi::{c_str, AsWstr, WCharFromFFI, WCharToFFI}, wgetopt::{ wgetopter_t, wopt, woption, woption_argument_t::{self, *}, diff --git a/fish-rust/src/builtins/set.rs b/fish-rust/src/builtins/set.rs index 173068d7a..1108a14a2 100644 --- a/fish-rust/src/builtins/set.rs +++ b/fish-rust/src/builtins/set.rs @@ -337,7 +337,6 @@ fn handle_env_return(retval: EnvStackSetResult, cmd: &wstr, key: &wstr, streams: key )); } - _ => panic!("unexpected vars.set() ret val"), } } @@ -822,7 +821,6 @@ fn env_result_to_status(retval: EnvStackSetResult) -> Option { EnvStackSetResult::ENV_SCOPE => 2, EnvStackSetResult::ENV_INVALID => 3, EnvStackSetResult::ENV_NOT_FOUND => 4, - _ => panic!(), }) } diff --git a/fish-rust/src/builtins/shared.rs b/fish-rust/src/builtins/shared.rs index 8f2119028..5079a4f58 100644 --- a/fish-rust/src/builtins/shared.rs +++ b/fish-rust/src/builtins/shared.rs @@ -1,8 +1,7 @@ use super::prelude::*; use crate::builtins::*; use crate::common::{escape, get_by_sorted_name, str2wcstring, Named}; -use crate::ffi::Repin; -use crate::io::{IoChain, IoFd, OutputStream, OutputStreamFfi}; +use crate::io::{IoChain, IoFd, OutputStream}; use crate::parse_constants::UNKNOWN_BUILTIN_ERR_MSG; use crate::parse_util::parse_util_argument_is_help; use crate::parser::{Block, BlockType, LoopStatus}; @@ -10,7 +9,6 @@ use crate::reader::reader_read; use crate::wchar::{wstr, WString, L}; use crate::wgetopt::{wgetopter_t, wopt, woption, woption_argument_t}; -use cxx::CxxWString; use errno::errno; use libc::{c_int, STDERR_FILENO, STDIN_FILENO, STDOUT_FILENO}; @@ -18,7 +16,6 @@ use std::fs::File; use std::io::{BufRead, BufReader, Read}; use std::os::fd::FromRawFd; -use std::pin::Pin; use std::sync::Arc; use widestring_suffix::widestrs; @@ -970,140 +967,3 @@ fn builtin_gettext(_parser: &Parser, streams: &mut IoStreams, argv: &mut [&wstr] } STATUS_CMD_OK } - -#[cxx::bridge] -#[allow(clippy::needless_lifetimes)] -mod builtins_ffi { - extern "C++" { - include!("io.h"); - include!("parser.h"); - type IoStreams<'a> = crate::io::IoStreams<'a>; - type OutputStreamFfi<'a> = crate::io::OutputStreamFfi<'a>; - type Parser = crate::parser::Parser; - } - extern "Rust" { - #[cxx_name = "builtin_print_help"] - unsafe fn builtin_print_help_ffi<'a>( - parser: &Parser, - streams: Pin<&mut IoStreams<'a>>, - name: &CxxWString, - ); - #[cxx_name = "builtin_print_help_error"] - unsafe fn builtin_print_help_error_ffi<'a>( - parser: &Parser, - streams: Pin<&mut IoStreams<'a>>, - name: &CxxWString, - error_message: &CxxWString, - ); - #[cxx_name = "builtin_unknown_option"] - unsafe fn builtin_unknown_option_ffi<'a>( - parser: &Parser, - streams: Pin<&mut IoStreams<'a>>, - cmd: &CxxWString, - opt: &CxxWString, - print_hints: bool, - ); - #[cxx_name = "builtin_missing_argument"] - fn builtin_missing_argument_ffi( - parser: &Parser, - streams: Pin<&mut IoStreams>, - cmd: &CxxWString, - opt: &CxxWString, - print_hints: bool, - ); - #[cxx_name = "builtin_print_error_trailer"] - unsafe fn builtin_print_error_trailer_ffi<'a>( - parser: &Parser, - b: Pin<&mut OutputStreamFfi<'a>>, - cmd: &CxxWString, - ); - } -} - -pub fn run_builtin_ffi( - builtin_fn: fn( - *const autocxx::c_void, - *mut autocxx::c_void, - *mut autocxx::c_void, - ) -> autocxx::c_int, - parser: &Parser, - streams: &mut IoStreams, - args: &mut [&wstr], -) -> Option { - let mut zstrings = vec![]; - for arg in args { - let mut zstring: Vec = arg.chars().collect(); - zstring.push('\0'); - zstrings.push(zstring); - } - let mut zstrs = vec![]; - for zstring in &zstrings { - zstrs.push(zstring.as_ptr()); - } - zstrs.push(std::ptr::null()); - let args = zstrs.as_mut_ptr(); - let ret = (builtin_fn)( - parser as *const Parser as *const autocxx::c_void, - streams as *mut IoStreams as *mut autocxx::c_void, - args.cast(), - ); - Some(i32::from(ret)) -} - -fn builtin_print_help_ffi(parser: &Parser, streams: Pin<&mut IoStreams>, name: &CxxWString) { - builtin_print_help(parser, streams.unpin(), name.as_wstr()) -} - -fn builtin_print_help_error_ffi( - parser: &Parser, - streams: Pin<&mut IoStreams>, - name: &CxxWString, - error_message: &CxxWString, -) { - builtin_print_help_error( - parser, - streams.unpin(), - name.as_wstr(), - error_message.as_wstr(), - ) -} - -fn builtin_unknown_option_ffi( - parser: &Parser, - streams: Pin<&mut IoStreams>, - cmd: &CxxWString, - opt: &CxxWString, - print_hints: bool, -) { - builtin_unknown_option( - parser, - streams.unpin(), - cmd.as_wstr(), - opt.as_wstr(), - print_hints, - ); -} - -fn builtin_missing_argument_ffi( - parser: &Parser, - streams: Pin<&mut IoStreams>, - cmd: &CxxWString, - opt: &CxxWString, - print_hints: bool, -) { - builtin_missing_argument( - parser, - streams.unpin(), - cmd.as_wstr(), - opt.as_wstr(), - print_hints, - ); -} - -fn builtin_print_error_trailer_ffi( - parser: &Parser, - b: Pin<&mut OutputStreamFfi>, - cmd: &CxxWString, -) { - builtin_print_error_trailer(parser, b.unpin().0, cmd.as_wstr()) -} diff --git a/fish-rust/src/color.rs b/fish-rust/src/color.rs index c668ec7e5..516436026 100644 --- a/fish-rust/src/color.rs +++ b/fish-rust/src/color.rs @@ -428,38 +428,6 @@ fn term256_color_for_rgb(color: Color24) -> u8 { (16 + convert_color(color, COLORS)).try_into().unwrap() } -/// FFI junk. -use crate::ffi::rgb_color_t; -impl rgb_color_t { - /// Convert from a C++ rgb_color_t to a Rust RgbColor. - #[allow(clippy::wrong_self_convention)] - pub fn from_ffi(&self) -> RgbColor { - let typ = if self.is_normal() { - Type::Normal - } else if self.is_reset() { - Type::Reset - } else if self.is_none() { - Type::None - } else if self.is_named() { - let idx = self.to_name_index(); - Type::Named { idx } - } else if self.is_rgb() { - let [r, g, b] = self.to_color24().rgb; - Type::Rgb(Color24 { r, g, b }) - } else { - unreachable!("Unknown color type") - }; - let flags = Flags { - bold: self.is_bold(), - underline: self.is_underline(), - italics: self.is_italics(), - dim: self.is_dim(), - reverse: self.is_reverse(), - }; - RgbColor { typ, flags } - } -} - #[cfg(test)] mod tests { use crate::color::{Color24, Flags, RgbColor, Type}; diff --git a/fish-rust/src/common.rs b/fish-rust/src/common.rs index 347dc7e4b..6421863c8 100644 --- a/fish-rust/src/common.rs +++ b/fish-rust/src/common.rs @@ -6,20 +6,17 @@ PROCESS_EXPAND_SELF, PROCESS_EXPAND_SELF_STR, VARIABLE_EXPAND, VARIABLE_EXPAND_SINGLE, }; use crate::fallback::fish_wcwidth; -use crate::ffi; use crate::flog::FLOG; use crate::future_feature_flags::{feature_test, FeatureFlag}; use crate::global_safety::RelaxedAtomicBool; use crate::termsize::Termsize; use crate::wchar::{decode_byte_from_char, encode_byte_to_char, prelude::*}; -use crate::wchar_ffi::WCharToFFI; use crate::wcstringutil::wcs2string_callback; use crate::wildcard::{ANY_CHAR, ANY_STRING, ANY_STRING_RECURSIVE}; use crate::wutil::encoding::{mbrtowc, wcrtomb, zero_mbstate, AT_LEAST_MB_LEN_MAX}; use crate::wutil::fish_iswalnum; use bitflags::bitflags; use core::slice; -use cxx::{CxxWString, UniquePtr}; use libc::{EINTR, EIO, O_WRONLY, SIGTTOU, SIG_IGN, STDERR_FILENO, STDIN_FILENO, STDOUT_FILENO}; use num_traits::ToPrimitive; use once_cell::sync::{Lazy, OnceCell}; @@ -2162,80 +2159,3 @@ macro_rules! eprintf { fprintf!(libc::STDERR_FILENO, $format $(, $arg)*) } } - -#[cxx::bridge] -mod common_ffi { - extern "C++" { - include!("wutil.h"); - include!("common.h"); - type escape_string_style_t = crate::ffi::escape_string_style_t; - } - extern "Rust" { - #[cxx_name = "unescape_string"] - fn unescape_string_ffi( - input: *const wchar_t, - len: usize, - escape_special: u32, - style: escape_string_style_t, - ) -> UniquePtr; - - #[cxx_name = "escape_string_script"] - fn escape_string_script_ffi( - input: *const wchar_t, - len: usize, - flags: u32, - ) -> UniquePtr; - - #[cxx_name = "escape_string_url"] - fn escape_string_url_ffi(input: *const wchar_t, len: usize) -> UniquePtr; - - #[cxx_name = "escape_string_var"] - fn escape_string_var_ffi(input: *const wchar_t, len: usize) -> UniquePtr; - - } -} - -fn unescape_string_ffi( - input: *const ffi::wchar_t, - len: usize, - escape_special: u32, - style: ffi::escape_string_style_t, -) -> UniquePtr { - let style = match style { - ffi::escape_string_style_t::STRING_STYLE_SCRIPT => { - UnescapeStringStyle::Script(UnescapeFlags::from_bits(escape_special).unwrap()) - } - ffi::escape_string_style_t::STRING_STYLE_URL => UnescapeStringStyle::Url, - ffi::escape_string_style_t::STRING_STYLE_VAR => UnescapeStringStyle::Var, - _ => panic!(), - }; - let input = unsafe { slice::from_raw_parts(input, len) }; - let input = wstr::from_slice(input).unwrap(); - match unescape_string(input, style) { - Some(result) => result.to_ffi(), - None => UniquePtr::null(), - } -} - -fn escape_string_script_ffi( - input: *const ffi::wchar_t, - len: usize, - flags: u32, -) -> UniquePtr { - let input = unsafe { slice::from_raw_parts(input, len) }; - escape_string_script( - wstr::from_slice(input).unwrap(), - EscapeFlags::from_bits(flags).unwrap(), - ) - .to_ffi() -} - -fn escape_string_var_ffi(input: *const ffi::wchar_t, len: usize) -> UniquePtr { - let input = unsafe { slice::from_raw_parts(input, len) }; - escape_string_var(wstr::from_slice(input).unwrap()).to_ffi() -} - -fn escape_string_url_ffi(input: *const ffi::wchar_t, len: usize) -> UniquePtr { - let input = unsafe { slice::from_raw_parts(input, len) }; - escape_string_url(wstr::from_slice(input).unwrap()).to_ffi() -} diff --git a/fish-rust/src/complete.rs b/fish-rust/src/complete.rs index 4bc564477..336fa3aea 100644 --- a/fish-rust/src/complete.rs +++ b/fish-rust/src/complete.rs @@ -3,7 +3,6 @@ cmp::Ordering, collections::{BTreeMap, HashMap, HashSet}, mem, - pin::Pin, sync::{ atomic::{self, AtomicUsize}, Mutex, @@ -11,13 +10,8 @@ time::{Duration, Instant}, }; -use crate::{ - common::charptr2wcstring, - util::wcsfilecmp, - wchar_ffi::{AsWstr, WCharFromFFI, WCharToFFI}, -}; +use crate::{common::charptr2wcstring, util::wcsfilecmp}; use bitflags::bitflags; -use cxx::{CxxWString, UniquePtr}; use once_cell::sync::Lazy; use printf_compat::sprintf; use widestring::U32CString; @@ -2533,90 +2527,14 @@ pub fn complete_get_wrap_targets(command: &wstr) -> Vec { wrappers.get(command).cloned().unwrap_or_default() } -pub struct CompletionListFfi(pub CompletionList); - -pub use complete_ffi::CompletionRequestOptions; - -#[cxx::bridge] -mod complete_ffi { - extern "C++" { - include!("complete.h"); - include!("parser.h"); - type Parser = crate::parser::Parser; - type OperationContext<'a> = crate::operation_context::OperationContext<'a>; - type wcstring_list_ffi_t = crate::ffi::wcstring_list_ffi_t; - } - extern "Rust" { - type Completion; - type CompletionListFfi; - - fn new_completion() -> Box; - fn new_completion_with( - completion: &CxxWString, - description: &CxxWString, - flags: u8, - ) -> Box; - fn completion(self: &Completion) -> UniquePtr; - fn description(self: &Completion) -> UniquePtr; - fn flags(self: &Completion) -> u8; - fn set_flags(self: &mut Completion, value: u8); - fn replaces_commandline(self: &Completion) -> bool; - fn match_is_exact_or_prefix(self: &Completion) -> bool; - fn completion_erase(self: &mut Completion, begin: usize, end: usize); - fn rank(self: &Completion) -> u32; - #[cxx_name = "clone"] - fn clone_ffi(self: &Completion) -> Box; - - fn new_completion_list() -> Box; - fn size(self: &CompletionListFfi) -> usize; - fn empty(self: &CompletionListFfi) -> bool; - fn at(self: &CompletionListFfi, i: usize) -> &Completion; - fn at_mut(self: &mut CompletionListFfi, i: usize) -> &mut Completion; - fn clear(self: &mut CompletionListFfi); - fn complete_invalidate_path(); - fn reverse(self: &mut CompletionListFfi); - fn push_back(self: &mut CompletionListFfi, completion: &Completion); - fn sort_and_prioritize(self: &mut CompletionListFfi, flags: CompletionRequestOptions); - #[cxx_name = "complete_load"] - fn complete_load_ffi(cmd: &CxxWString, parser: &Parser) -> bool; - #[cxx_name = "complete"] - fn complete_ffi( - search_string: &CxxWString, - complete_flags: CompletionRequestOptions, - ctx: &OperationContext<'static>, - needs_load: &mut UniquePtr, - ) -> Box; - #[cxx_name = "append_completion"] - fn append_completion_ffi(completions: Pin<&mut CompletionListFfi>, comp: &CxxWString); - } - - #[derive(Clone, Copy)] - pub struct CompletionRequestOptions { - /// Requesting autosuggestion - pub autosuggestion: bool, - /// Make descriptions - pub descriptions: bool, - /// If set, we do not require a prefix match - pub fuzzy_match: bool, - } - - extern "Rust" { - fn completion_request_options_autosuggest() -> CompletionRequestOptions; - fn completion_request_options_normal() -> CompletionRequestOptions; - } -} - -fn complete_ffi( - search_string: &CxxWString, - complete_flags: CompletionRequestOptions, - ctx: &OperationContext<'static>, - needs_load: &mut UniquePtr, -) -> Box { - let (completions, to_load) = complete(search_string.as_wstr(), complete_flags, ctx); - if !needs_load.is_null() { - *needs_load = to_load.to_ffi(); - } - Box::new(CompletionListFfi(completions)) +#[derive(Clone, Copy)] +pub struct CompletionRequestOptions { + /// Requesting autosuggestion + pub autosuggestion: bool, + /// Make descriptions + pub descriptions: bool, + /// If set, we do not require a prefix match + pub fuzzy_match: bool, } fn completion_request_options_autosuggest() -> CompletionRequestOptions { @@ -2636,63 +2554,13 @@ fn default() -> Self { } } -unsafe impl cxx::ExternType for CompletionListFfi { - type Id = cxx::type_id!("CompletionListFfi"); - type Kind = cxx::kind::Opaque; -} -unsafe impl cxx::ExternType for Completion { - type Id = cxx::type_id!("Completion"); - type Kind = cxx::kind::Opaque; -} - -fn new_completion() -> Box { - Box::new(Completion::new( - "".into(), - "".into(), - StringFuzzyMatch::exact_match(), - CompleteFlags::default(), - )) -} -fn new_completion_with( - completion: &CxxWString, - description: &CxxWString, - flags: u8, -) -> Box { - Box::new(Completion::new( - completion.from_ffi(), - description.from_ffi(), - StringFuzzyMatch::exact_match(), - CompleteFlags::from_bits(flags).unwrap(), - )) -} -fn new_completion_list() -> Box { - Box::new(CompletionListFfi(CompletionList::new())) -} -fn append_completion_ffi(completions: Pin<&mut CompletionListFfi>, comp: &CxxWString) { - completions.get_mut().0.push(Completion::new( - comp.from_ffi(), - "".into(), - StringFuzzyMatch::exact_match(), - CompleteFlags::default(), - )); -} - impl Completion { - fn completion(&self) -> UniquePtr { - self.completion.to_ffi() - } - fn description(&self) -> UniquePtr { - self.description.to_ffi() - } fn flags(&self) -> u8 { self.flags.bits() } fn set_flags(&mut self, value: u8) { self.flags = CompleteFlags::from_bits(value).unwrap(); } - fn clone_ffi(&self) -> Box { - Box::new(self.clone()) - } fn match_is_exact_or_prefix(&self) -> bool { self.r#match.is_exact_or_prefix() } @@ -2700,33 +2568,3 @@ fn completion_erase(&mut self, begin: usize, end: usize) { self.completion.replace_range(begin..end, L!("")) } } -impl CompletionListFfi { - fn size(&self) -> usize { - self.0.len() - } - fn empty(&self) -> bool { - self.0.is_empty() - } - fn at(&self, i: usize) -> &Completion { - &self.0[i] - } - fn at_mut(&mut self, i: usize) -> &mut Completion { - &mut self.0[i] - } - fn reverse(&mut self) { - self.0.reverse(); - } - fn clear(&mut self) { - self.0.clear(); - } - fn push_back(&mut self, completion: &Completion) { - self.0.push(completion.clone()); - } - fn sort_and_prioritize(&mut self, flags: CompletionRequestOptions) { - sort_and_prioritize(&mut self.0, flags); - } -} - -fn complete_load_ffi(cmd: &CxxWString, parser: &Parser) -> bool { - complete_load(cmd.as_wstr(), parser) -} diff --git a/fish-rust/src/editable_line.rs b/fish-rust/src/editable_line.rs index 0a4d55f0e..e7ecdbaab 100644 --- a/fish-rust/src/editable_line.rs +++ b/fish-rust/src/editable_line.rs @@ -1,12 +1,7 @@ -use std::pin::Pin; - -use cxx::{CxxWString, UniquePtr}; - #[allow(unused_imports)] use crate::future::IsSomeAnd; -use crate::highlight::{HighlightSpec, HighlightSpecListFFI}; +use crate::highlight::HighlightSpec; use crate::wchar::prelude::*; -use crate::wchar_ffi::{WCharFromFFI, WCharToFFI}; /// An edit action that can be undone. #[derive(Clone, Eq, PartialEq)] @@ -344,88 +339,3 @@ fn cursor_position_after_edit(edit: &Edit) -> usize { let removed = chars_deleted_left_of_cursor(edit); cursor.saturating_sub(removed) } - -#[cxx::bridge] -mod editable_line_ffi { - extern "C++" { - include!("editable_line.h"); - include!("highlight.h"); - pub type HighlightSpec = crate::highlight::HighlightSpec; - pub type HighlightSpecListFFI = crate::highlight::HighlightSpecListFFI; - } - extern "Rust" { - type Edit; - fn new_edit(start: usize, end: usize, replacement: &CxxWString) -> Box; - #[cxx_name = "apply_edit"] - fn apply_edit_ffi( - target: &CxxWString, - mut colors: Pin<&mut HighlightSpecListFFI>, - edit: Box, - ) -> UniquePtr; - } - extern "Rust" { - type UndoHistory; - } - extern "Rust" { - type EditableLine; - fn new_editable_line() -> Box; - fn empty(&self) -> bool; - #[cxx_name = "text"] - fn text_ffi(&self) -> UniquePtr; - #[cxx_name = "clone"] - fn clone_ffi(&self) -> Box; - fn position(&self) -> usize; - fn set_position(&mut self, position: usize); - fn clear(&mut self); - fn undo(&mut self) -> bool; - fn redo(&mut self) -> bool; - fn size(&self) -> usize; - #[cxx_name = "push_edit"] - fn push_edit_ffi(&mut self, edit: Box, allow_coalesce: bool); - fn begin_edit_group(&mut self); - fn end_edit_group(&mut self); - #[cxx_name = "at"] - fn at_ffi(&self, index: usize) -> u32; - #[cxx_name = "set_colors"] - fn set_colors_ffi(&mut self, colors: &HighlightSpecListFFI); - } -} -fn new_edit(start: usize, end: usize, replacement: &CxxWString) -> Box { - Box::new(Edit::new(start..end, replacement.from_ffi())) -} -fn new_editable_line() -> Box { - Box::default() -} -impl EditableLine { - fn empty(&self) -> bool { - self.is_empty() - } - fn text_ffi(&self) -> UniquePtr { - self.text().to_ffi() - } - fn clone_ffi(&self) -> Box { - Box::new(self.clone()) - } - fn size(&self) -> usize { - self.len() - } - #[allow(clippy::boxed_local)] - fn push_edit_ffi(&mut self, edit: Box, allow_coalesce: bool) { - self.push_edit(*edit, allow_coalesce); - } - fn at_ffi(&self, index: usize) -> u32 { - self.at(index) as _ - } - fn set_colors_ffi(&mut self, colors: &HighlightSpecListFFI) { - self.set_colors(colors.0.clone()) - } -} -fn apply_edit_ffi( - target: &CxxWString, - mut colors: Pin<&mut HighlightSpecListFFI>, - edit: Box, -) -> UniquePtr { - let mut target = target.from_ffi(); - apply_edit(&mut target, &mut colors.0, &edit); - target.to_ffi() -} diff --git a/fish-rust/src/env/env_ffi.rs b/fish-rust/src/env/env_ffi.rs deleted file mode 100644 index d2532b379..000000000 --- a/fish-rust/src/env/env_ffi.rs +++ /dev/null @@ -1,437 +0,0 @@ -use super::environment::{self, EnvDyn, EnvNull, EnvStack, EnvStackRef, Environment}; -use super::var::{ElectricVar, EnvVar, EnvVarFlags, Statuses}; -use crate::env::EnvMode; -use crate::ffi::{wchar_t, wcharz_t, wcstring_list_ffi_t}; -use crate::signal::Signal; -use crate::wchar_ffi::WCharToFFI; -use crate::wchar_ffi::{AsWstr, WCharFromFFI}; -use cxx::{CxxVector, CxxWString, UniquePtr}; -use lazy_static::lazy_static; -use std::ffi::c_int; -use std::pin::Pin; - -use crate::env::misc_init; - -impl From for c_int { - fn from(r: EnvStackSetResult) -> Self { - match r { - EnvStackSetResult::ENV_OK => 0, - EnvStackSetResult::ENV_PERM => 1, - EnvStackSetResult::ENV_SCOPE => 2, - EnvStackSetResult::ENV_INVALID => 3, - EnvStackSetResult::ENV_NOT_FOUND => 4, - _ => panic!(), - } - } -} - -#[allow(clippy::module_inception)] -#[cxx::bridge] -mod env_ffi { - - /// Return values for `EnvStack::set()`. - #[repr(u8)] - #[cxx_name = "env_stack_set_result_t"] - #[derive(Debug)] - enum EnvStackSetResult { - ENV_OK, - ENV_PERM, - ENV_SCOPE, - ENV_INVALID, - ENV_NOT_FOUND, - } - - extern "C++" { - include!("env.h"); - include!("wutil.h"); - type wcstring_list_ffi_t = super::wcstring_list_ffi_t; - type wcharz_t = super::wcharz_t; - } - - extern "Rust" { - type EnvVar; - - fn is_empty(&self) -> bool; - - fn exports(&self) -> bool; - fn is_read_only(&self) -> bool; - fn is_pathvar(&self) -> bool; - - #[cxx_name = "equals"] - fn equals_ffi(&self, rhs: &EnvVar) -> bool; - - #[cxx_name = "as_string"] - fn as_string_ffi(&self) -> UniquePtr; - - #[cxx_name = "as_list"] - fn as_list_ffi(&self) -> UniquePtr; - - #[cxx_name = "to_list"] - fn to_list_ffi(&self, out: Pin<&mut wcstring_list_ffi_t>); - - #[cxx_name = "get_delimiter"] - fn get_delimiter_ffi(&self) -> wchar_t; - - #[cxx_name = "get_flags"] - fn get_flags_ffi(&self) -> u8; - - #[cxx_name = "clone_box"] - fn clone_box_ffi(&self) -> Box; - - #[cxx_name = "env_var_create"] - fn env_var_create_ffi(vals: &wcstring_list_ffi_t, flags: u8) -> Box; - - #[cxx_name = "env_var_create_from_name"] - fn env_var_create_from_name_ffi( - name: wcharz_t, - values: &wcstring_list_ffi_t, - ) -> Box; - } - extern "Rust" { - type EnvNull; - #[cxx_name = "getf"] - fn getf_ffi(&self, name: &CxxWString, mode: u16) -> *mut EnvVar; - #[cxx_name = "get_names"] - fn get_names_ffi(&self, flags: u16, out: Pin<&mut wcstring_list_ffi_t>); - #[cxx_name = "env_null_create"] - fn env_null_create_ffi() -> Box; - } - - extern "Rust" { - type Statuses; - #[cxx_name = "get_status"] - fn get_status_ffi(&self) -> i32; - - #[cxx_name = "statuses_just"] - fn statuses_just_ffi(s: i32) -> Box; - - #[cxx_name = "get_pipestatus"] - fn get_pipestatus_ffi(&self) -> &Vec; - - #[cxx_name = "get_kill_signal"] - fn get_kill_signal_ffi(&self) -> i32; - } - - extern "Rust" { - #[cxx_name = "EnvDyn"] - type EnvDynFFI; - fn get(&self, name: &CxxWString) -> *mut EnvVar; - fn getf(&self, name: &CxxWString, mode: u16) -> *mut EnvVar; - fn get_names(&self, flags: u16, out: Pin<&mut wcstring_list_ffi_t>); - } - - extern "Rust" { - #[cxx_name = "EnvStackRef"] - type EnvStackRefFFI; - fn env_stack_globals() -> &'static EnvStackRefFFI; - fn env_stack_principal() -> &'static EnvStackRefFFI; - fn set_one(&self, name: &CxxWString, flags: u16, value: &CxxWString) -> EnvStackSetResult; - fn get(&self, name: &CxxWString) -> *mut EnvVar; - fn getf(&self, name: &CxxWString, mode: u16) -> *mut EnvVar; - fn get_unless_empty(&self, name: &CxxWString) -> *mut EnvVar; - fn getf_unless_empty(&self, name: &CxxWString, flags: u16) -> *mut EnvVar; - fn get_names(&self, flags: u16, out: Pin<&mut wcstring_list_ffi_t>); - fn is_principal(&self) -> bool; - fn get_last_statuses(&self) -> Box; - fn set_last_statuses(&self, status: i32, kill_signal: i32, pipestatus: &CxxVector); - fn set( - &self, - name: &CxxWString, - flags: u16, - vals: &wcstring_list_ffi_t, - ) -> EnvStackSetResult; - fn remove(&self, name: &CxxWString, flags: u16) -> EnvStackSetResult; - fn get_pwd_slash(&self) -> UniquePtr; - fn set_pwd_from_getcwd(&self); - - fn push(&self, new_scope: bool); - fn pop(&self); - - fn snapshot(&self) -> Box; - - // Access a variable stack that only represents globals. - // Do not push or pop from this. - fn env_get_globals_ffi() -> Box; - - // Access the principal variable stack. - fn env_get_principal_ffi() -> Box; - } - - extern "Rust" { - #[cxx_name = "var_is_electric"] - fn var_is_electric_ffi(name: &CxxWString) -> bool; - - #[cxx_name = "rust_env_init"] - fn rust_env_init_ffi(do_uvars: bool); - - fn misc_init(); - - #[cxx_name = "env_flags_for"] - fn env_flags_for_ffi(name: wcharz_t) -> u8; - } -} -pub use env_ffi::EnvStackSetResult; - -impl Default for EnvStackSetResult { - fn default() -> Self { - EnvStackSetResult::ENV_OK - } -} - -/// FFI bits. -impl EnvVar { - pub fn equals_ffi(&self, rhs: &EnvVar) -> bool { - self == rhs - } - - pub fn as_string_ffi(&self) -> UniquePtr { - self.as_string().to_ffi() - } - - pub fn as_list_ffi(&self) -> UniquePtr { - self.as_list().to_ffi() - } - - pub fn to_list_ffi(&self, mut out: Pin<&mut wcstring_list_ffi_t>) { - out.as_mut().clear(); - for val in self.as_list() { - out.as_mut().push(val); - } - } - - pub fn clone_box_ffi(&self) -> Box { - Box::new(self.clone()) - } - - pub fn get_flags_ffi(&self) -> u8 { - self.get_flags().bits() - } - - pub fn get_delimiter_ffi(self: &EnvVar) -> wchar_t { - self.get_delimiter().into() - } -} - -fn env_var_create_ffi(vals: &wcstring_list_ffi_t, flags: u8) -> Box { - Box::new(EnvVar::new_vec( - vals.from_ffi(), - EnvVarFlags::from_bits(flags).expect("invalid flags"), - )) -} - -pub fn env_var_create_from_name_ffi(name: wcharz_t, values: &wcstring_list_ffi_t) -> Box { - Box::new(EnvVar::new_from_name_vec(name.as_wstr(), values.from_ffi())) -} - -fn env_null_create_ffi() -> Box { - Box::new(EnvNull::new()) -} - -/// FFI wrapper around EnvDyn -pub struct EnvDynFFI(pub EnvDyn); -impl EnvDynFFI { - fn get(&self, name: &CxxWString) -> *mut EnvVar { - EnvironmentFFI::getf_ffi(&self.0, name, 0) - } - fn getf(&self, name: &CxxWString, mode: u16) -> *mut EnvVar { - EnvironmentFFI::getf_ffi(&self.0, name, mode) - } - fn get_names(&self, flags: u16, out: Pin<&mut wcstring_list_ffi_t>) { - EnvironmentFFI::get_names_ffi(&self.0, flags, out) - } -} -unsafe impl cxx::ExternType for EnvDynFFI { - type Id = cxx::type_id!("EnvDyn"); // CXX name! - type Kind = cxx::kind::Opaque; -} - -/// FFI wrapper around EnvStackRef. -#[derive(Clone)] -pub struct EnvStackRefFFI(pub EnvStackRef); - -lazy_static! { - static ref GLOBALS: EnvStackRefFFI = EnvStackRefFFI(EnvStack::globals().clone()); -} -lazy_static! { - static ref PRINCIPAL_STACK: EnvStackRefFFI = EnvStackRefFFI(EnvStack::principal().clone()); -} - -fn env_stack_globals() -> &'static EnvStackRefFFI { - &GLOBALS -} -fn env_stack_principal() -> &'static EnvStackRefFFI { - &PRINCIPAL_STACK -} - -impl EnvStackRefFFI { - fn set_one(&self, name: &CxxWString, flags: u16, value: &CxxWString) -> EnvStackSetResult { - self.0.set_one( - name.as_wstr(), - EnvMode::from_bits(flags).unwrap(), - value.from_ffi(), - ) - } - - fn get(&self, name: &CxxWString) -> *mut EnvVar { - EnvironmentFFI::getf_ffi(&*self.0, name, 0) - } - fn getf(&self, name: &CxxWString, mode: u16) -> *mut EnvVar { - EnvironmentFFI::getf_ffi(&*self.0, name, mode) - } - fn get_unless_empty(&self, name: &CxxWString) -> *mut EnvVar { - EnvironmentFFI::getf_unless_empty_ffi(&*self.0, name, 0) - } - fn getf_unless_empty(&self, name: &CxxWString, mode: u16) -> *mut EnvVar { - EnvironmentFFI::getf_unless_empty_ffi(&*self.0, name, mode) - } - - fn get_names(&self, flags: u16, out: Pin<&mut wcstring_list_ffi_t>) { - EnvironmentFFI::get_names_ffi(&*self.0, flags, out) - } - fn is_principal(&self) -> bool { - self.0.is_principal() - } - - fn get_pwd_slash(&self) -> UniquePtr { - self.0.get_pwd_slash().to_ffi() - } - - fn push(&self, new_scope: bool) { - self.0.push(new_scope) - } - - fn pop(&self) { - self.0.pop() - } - - fn get_last_statuses(&self) -> Box { - Box::new(self.0.get_last_statuses()) - } - - fn set_last_statuses(&self, status: i32, kill_signal: i32, pipestatus: &CxxVector) { - let statuses = Statuses { - status, - kill_signal: if kill_signal == 0 { - None - } else { - Some(Signal::new(kill_signal)) - }, - pipestatus: pipestatus.as_slice().to_vec(), - }; - self.0.set_last_statuses(statuses) - } - - fn set_pwd_from_getcwd(&self) { - self.0.set_pwd_from_getcwd() - } - - fn set(&self, name: &CxxWString, flags: u16, vals: &wcstring_list_ffi_t) -> EnvStackSetResult { - let mode = EnvMode::from_bits(flags).expect("Invalid mode bits"); - self.0.set(name.as_wstr(), mode, vals.from_ffi()) - } - - fn remove(&self, name: &CxxWString, flags: u16) -> EnvStackSetResult { - let mode = EnvMode::from_bits(flags).expect("Invalid mode bits"); - self.0.remove(name.as_wstr(), mode) - } - - fn snapshot(&self) -> Box { - Box::new(EnvDynFFI(self.0.snapshot())) - } -} -unsafe impl cxx::ExternType for EnvStackRefFFI { - type Id = cxx::type_id!("EnvStackRef"); // CXX name! - type Kind = cxx::kind::Opaque; -} - -unsafe impl cxx::ExternType for Statuses { - type Id = cxx::type_id!("Statuses"); - type Kind = cxx::kind::Opaque; -} -fn statuses_just_ffi(s: i32) -> Box { - Box::new(Statuses::just(s)) -} -impl Statuses { - fn get_status_ffi(&self) -> i32 { - self.status - } - - fn get_pipestatus_ffi(&self) -> &Vec { - &self.pipestatus - } - - fn get_kill_signal_ffi(&self) -> i32 { - match self.kill_signal { - Some(sig) => sig.code(), - None => 0, - } - } -} - -fn env_get_globals_ffi() -> Box { - Box::new(EnvStackRefFFI(EnvStack::globals().clone())) -} - -fn env_get_principal_ffi() -> Box { - Box::new(EnvStackRefFFI(EnvStack::principal().clone())) -} - -// We have to implement these directly to make cxx happy, even though they're implemented in the EnvironmentFFI trait. -impl EnvNull { - pub fn getf_ffi(&self, name: &CxxWString, mode: u16) -> *mut EnvVar { - EnvironmentFFI::getf_ffi(self, name, mode) - } - pub fn get_names_ffi(&self, flags: u16, out: Pin<&mut wcstring_list_ffi_t>) { - EnvironmentFFI::get_names_ffi(self, flags, out) - } -} - -trait EnvironmentFFI: Environment { - /// FFI helper. - /// This returns either null, or the result of Box.into_raw(). - /// This is a workaround for the difficulty of passing an Option through FFI. - fn getf_ffi(&self, name: &CxxWString, mode: u16) -> *mut EnvVar { - match self.getf( - name.as_wstr(), - EnvMode::from_bits(mode).expect("Invalid mode bits"), - ) { - None => std::ptr::null_mut(), - Some(var) => Box::into_raw(Box::new(var)), - } - } - fn getf_unless_empty_ffi(&self, name: &CxxWString, mode: u16) -> *mut EnvVar { - match self.getf( - name.as_wstr(), - EnvMode::from_bits(mode).expect("Invalid mode bits"), - ) { - None => std::ptr::null_mut(), - Some(var) => { - if var.is_empty() { - std::ptr::null_mut() - } else { - Box::into_raw(Box::new(var)) - } - } - } - } - fn get_names_ffi(&self, mode: u16, mut out: Pin<&mut wcstring_list_ffi_t>) { - let names = self.get_names(EnvMode::from_bits(mode).expect("Invalid mode bits")); - for name in names { - out.as_mut().push(name.to_ffi()); - } - } -} - -impl EnvironmentFFI for T {} - -fn var_is_electric_ffi(name: &CxxWString) -> bool { - ElectricVar::for_name(name.as_wstr()).is_some() -} - -fn rust_env_init_ffi(do_uvars: bool) { - environment::env_init(None, do_uvars, false); -} - -fn env_flags_for_ffi(name: wcharz_t) -> u8 { - EnvVar::flags_for(name.as_wstr()).bits() -} diff --git a/fish-rust/src/env/environment.rs b/fish-rust/src/env/environment.rs index 02f4e2f60..70eb4d235 100644 --- a/fish-rust/src/env/environment.rs +++ b/fish-rust/src/env/environment.rs @@ -6,7 +6,7 @@ use crate::abbrs::{abbrs_get_set, Abbreviation, Position}; use crate::common::{str2wcstring, unescape_string, wcs2zstring, UnescapeStringStyle}; use crate::compat::{stdout_stream, C_PATH_BSHELL, _PATH_BSHELL}; -use crate::env::{EnvMode, EnvStackSetResult, EnvVar, Statuses}; +use crate::env::{EnvMode, EnvVar, Statuses}; use crate::env_dispatch::{env_dispatch_init, env_dispatch_var_change}; use crate::env_universal_common::{CallbackDataList, EnvUniversal}; use crate::event::Event; @@ -49,6 +49,34 @@ /// Set when a universal variable has been modified but not yet been written to disk via sync(). static UVARS_LOCALLY_MODIFIED: RelaxedAtomicBool = RelaxedAtomicBool::new(false); +/// Return values for `EnvStack::set()`. +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub enum EnvStackSetResult { + ENV_OK, + ENV_PERM, + ENV_SCOPE, + ENV_INVALID, + ENV_NOT_FOUND, +} + +impl Default for EnvStackSetResult { + fn default() -> Self { + EnvStackSetResult::ENV_OK + } +} + +impl From for c_int { + fn from(r: EnvStackSetResult) -> Self { + match r { + EnvStackSetResult::ENV_OK => 0, + EnvStackSetResult::ENV_PERM => 1, + EnvStackSetResult::ENV_SCOPE => 2, + EnvStackSetResult::ENV_INVALID => 3, + EnvStackSetResult::ENV_NOT_FOUND => 4, + } + } +} + /// An environment is read-only access to variable values. pub trait Environment { /// Get a variable by name using default flags. diff --git a/fish-rust/src/env/mod.rs b/fish-rust/src/env/mod.rs index a52553431..4a0d8e52a 100644 --- a/fish-rust/src/env/mod.rs +++ b/fish-rust/src/env/mod.rs @@ -1,10 +1,8 @@ -mod env_ffi; pub mod environment; mod environment_impl; pub mod var; use crate::common::ToCString; -pub use env_ffi::{EnvDynFFI, EnvStackRefFFI, EnvStackSetResult}; pub use environment::*; use std::sync::{ atomic::{AtomicBool, AtomicUsize}, diff --git a/fish-rust/src/env_dispatch.rs b/fish-rust/src/env_dispatch.rs index 76a14acf5..3b054ef65 100644 --- a/fish-rust/src/env_dispatch.rs +++ b/fish-rust/src/env_dispatch.rs @@ -22,15 +22,6 @@ use std::ptr; use std::sync::atomic::{AtomicBool, Ordering}; -#[cxx::bridge] -mod env_dispatch_ffi { - extern "Rust" { - fn env_dispatch_init_ffi(); - fn term_supports_setting_title() -> bool; - fn use_posix_spawn() -> bool; - } -} - /// List of all locale environment variable names that might trigger (re)initializing of the locale /// subsystem. These are only the variables we're possibly interested in. #[rustfmt::skip] @@ -354,11 +345,6 @@ pub fn env_dispatch_init(vars: &EnvStack) { Lazy::force(&VAR_DISPATCH_TABLE); } -pub fn env_dispatch_init_ffi() { - let vars = EnvStack::principal(); - env_dispatch_init(vars); -} - /// Runs the subset of dispatch functions that need to be called at startup. fn run_inits(vars: &EnvStack) { init_locale(vars); diff --git a/fish-rust/src/event.rs b/fish-rust/src/event.rs index 35ab88710..6d20f9fcf 100644 --- a/fish-rust/src/event.rs +++ b/fish-rust/src/event.rs @@ -4,98 +4,29 @@ //! defined when these functions produce output or perform memory allocations, since such functions //! may not be safely called by signal handlers. -use crate::ffi::wcstring_list_ffi_t; -use cxx::{CxxWString, UniquePtr}; use libc::pid_t; -use std::num::NonZeroU32; -use std::pin::Pin; use std::sync::atomic::{AtomicBool, AtomicU32, Ordering}; use std::sync::{Arc, Mutex}; use crate::common::{escape, scoped_push_replacer, ScopeGuard}; use crate::flog::FLOG; use crate::io::{IoChain, IoStreams}; -use crate::job_group::{JobId, MaybeJobId}; +use crate::job_group::MaybeJobId; use crate::parser::{Block, Parser}; use crate::signal::{signal_check_cancel, signal_handle, Signal}; use crate::termsize; use crate::wchar::prelude::*; -use crate::wchar_ffi::{wcharz_t, AsWstr, WCharFromFFI, WCharToFFI}; -#[cxx::bridge] -mod event_ffi { - extern "C++" { - include!("wutil.h"); - include!("parser.h"); - include!("io.h"); - type wcharz_t = crate::ffi::wcharz_t; - type Parser = crate::parser::Parser; - type IoStreams<'a> = crate::io::IoStreams<'a>; - type wcstring_list_ffi_t = crate::ffi::wcstring_list_ffi_t; - } - - enum event_type_t { - any, - signal, - variable, - process_exit, - job_exit, - caller_exit, - generic, - } - - struct event_description_t { - typ: event_type_t, - signal: i32, - pid: i32, - internal_job_id: u64, - caller_id: u64, - str_param1: UniquePtr, - } - - extern "Rust" { - type EventHandler; - type Event; - - fn new_event_generic(desc: wcharz_t) -> Box; - fn new_event_variable_erase(name: &CxxWString) -> Box; - fn new_event_variable_set(name: &CxxWString) -> Box; - fn new_event_process_exit(pid: i32, status: i32) -> Box; - fn new_event_job_exit(pgid: i32, jid: u64) -> Box; - fn new_event_caller_exit(internal_job_id: u64, job_id: i32) -> Box; - #[cxx_name = "clone"] - fn clone_ffi(self: &Event) -> Box; - - #[cxx_name = "event_add_handler"] - fn event_add_handler_ffi(desc: &event_description_t, name: &CxxWString); - #[cxx_name = "event_remove_function_handlers"] - fn event_remove_function_handlers_ffi(name: &CxxWString) -> usize; - #[cxx_name = "event_get_function_handler_descs"] - fn event_get_function_handler_descs_ffi(name: &CxxWString) -> Vec; - - fn desc(self: &EventHandler) -> event_description_t; - fn function_name(self: &EventHandler) -> UniquePtr; - fn set_removed(self: &mut EventHandler); - - fn event_fire_generic_ffi( - parser: &Parser, - name: &CxxWString, - arguments: &wcstring_list_ffi_t, - ); - #[cxx_name = "event_fire_delayed"] - fn fire_delayed(parser: &Parser); - #[cxx_name = "event_print"] - fn event_print_ffi(streams: Pin<&mut IoStreams>, type_filter: &CxxWString); - - #[cxx_name = "event_enqueue_signal"] - fn enqueue_signal(signal: i32); - #[cxx_name = "event_is_signal_observed"] - fn is_signal_observed(sig: i32) -> bool; - } +pub enum event_type_t { + any, + signal, + variable, + process_exit, + job_exit, + caller_exit, + generic, } -pub use event_ffi::{event_description_t, event_type_t}; - pub const ANY_PID: pid_t = 0; #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)] @@ -191,64 +122,6 @@ fn from(desc: &EventDescription) -> Self { } } -impl From<&event_description_t> for EventDescription { - fn from(desc: &event_description_t) -> Self { - match desc.typ { - event_type_t::any => EventDescription::Any, - event_type_t::signal => EventDescription::Signal { - signal: Signal::new(desc.signal), - }, - event_type_t::variable => EventDescription::Variable { - name: desc.str_param1.from_ffi(), - }, - event_type_t::process_exit => EventDescription::ProcessExit { pid: desc.pid }, - event_type_t::job_exit => EventDescription::JobExit { - pid: desc.pid, - internal_job_id: desc.internal_job_id, - }, - event_type_t::caller_exit => EventDescription::CallerExit { - caller_id: desc.caller_id, - }, - event_type_t::generic => EventDescription::Generic { - param: desc.str_param1.from_ffi(), - }, - _ => panic!("invalid event description"), - } - } -} - -impl From<&EventDescription> for event_description_t { - fn from(desc: &EventDescription) -> Self { - let mut result = event_description_t { - typ: desc.into(), - signal: Default::default(), - pid: Default::default(), - internal_job_id: Default::default(), - caller_id: Default::default(), - str_param1: match desc.str_param1() { - Some(param) => param.to_ffi(), - None => UniquePtr::null(), - }, - }; - match *desc { - EventDescription::Any => (), - EventDescription::Signal { signal } => result.signal = signal.code(), - EventDescription::Variable { .. } => (), - EventDescription::ProcessExit { pid } => result.pid = pid, - EventDescription::JobExit { - pid, - internal_job_id, - } => { - result.pid = pid; - result.internal_job_id = internal_job_id; - } - EventDescription::CallerExit { caller_id } => result.caller_id = caller_id, - EventDescription::Generic { .. } => (), - } - result - } -} - #[derive(Debug)] pub struct EventHandler { /// Properties of the event to match. @@ -327,12 +200,6 @@ fn matches(&self, event: &Event) -> bool { type EventHandlerList = Vec>; impl EventHandler { - fn desc(&self) -> event_description_t { - (&self.desc).into() - } - fn function_name(self: &EventHandler) -> UniquePtr { - self.function_name.to_ffi() - } fn set_removed(self: &mut EventHandler) { self.removed.store(true, Ordering::Relaxed); } @@ -416,49 +283,6 @@ fn is_blocked(&self, parser: &Parser) -> bool { } } -fn new_event_generic(desc: wcharz_t) -> Box { - Box::new(Event::generic(desc.into())) -} - -fn new_event_variable_erase(name: &CxxWString) -> Box { - Box::new(Event::variable_erase(name.from_ffi())) -} - -fn new_event_variable_set(name: &CxxWString) -> Box { - Box::new(Event::variable_set(name.from_ffi())) -} - -fn new_event_process_exit(pid: i32, status: i32) -> Box { - Box::new(Event::process_exit(pid, status)) -} - -fn new_event_job_exit(pgid: i32, jid: u64) -> Box { - Box::new(Event::job_exit(pgid, jid)) -} - -fn new_event_caller_exit(internal_job_id: u64, job_id: i32) -> Box { - Box::new(Event::caller_exit( - internal_job_id, - MaybeJobId(if job_id == -1 { - None - } else { - Some(JobId::new( - NonZeroU32::new(u32::try_from(job_id).unwrap()).unwrap(), - )) - }), - )) -} - -impl Event { - fn clone_ffi(&self) -> Box { - Box::new(self.clone()) - } -} - -fn event_add_handler_ffi(desc: &event_description_t, name: &CxxWString) { - add_handler(EventHandler::new(desc.into(), Some(name.from_ffi()))); -} - /// All the signals we are interested in are in the 1-32 range (with 32 being the typical SIGRTMAX), /// but we can expand it to 64 just to be safe. All code checks if a signal value is within bounds /// before handling it. @@ -623,10 +447,6 @@ pub fn remove_function_handlers(name: &wstr) -> usize { remove_handlers_if(|h| h.function_name == name) } -fn event_remove_function_handlers_ffi(name: &CxxWString) -> usize { - remove_function_handlers(name.as_wstr()) -} - /// Return all event handlers for the given function. pub fn get_function_handlers(name: &wstr) -> EventHandlerList { EVENT_HANDLERS @@ -638,13 +458,6 @@ pub fn get_function_handlers(name: &wstr) -> EventHandlerList { .collect() } -fn event_get_function_handler_descs_ffi(name: &CxxWString) -> Vec { - get_function_handlers(name.as_wstr()) - .iter() - .map(|h| event_description_t::from(&h.desc)) - .collect() -} - /// Perform the specified event. Since almost all event firings will not be matched by even a single /// event handler, we make sure to optimize the 'no matches' path. This means that nothing is /// allocated/initialized unless needed. @@ -869,10 +682,6 @@ pub fn print(streams: &mut IoStreams, type_filter: &wstr) { } } -fn event_print_ffi(streams: Pin<&mut IoStreams>, type_filter: &CxxWString) { - print(streams.get_mut(), type_filter.as_wstr()); -} - /// Fire a generic event with the specified name. pub fn fire_generic(parser: &Parser, name: WString, arguments: Vec) { fire( @@ -883,7 +692,3 @@ pub fn fire_generic(parser: &Parser, name: WString, arguments: Vec) { }, ) } - -fn event_fire_generic_ffi(parser: &Parser, name: &CxxWString, arguments: &wcstring_list_ffi_t) { - fire_generic(parser, name.from_ffi(), arguments.from_ffi()); -} diff --git a/fish-rust/src/exec.rs b/fish-rust/src/exec.rs index 475359ce6..56b33992c 100644 --- a/fish-rust/src/exec.rs +++ b/fish-rust/src/exec.rs @@ -16,7 +16,6 @@ use crate::env_dispatch::use_posix_spawn; use crate::fds::make_fd_blocking; use crate::fds::{make_autoclose_pipes, open_cloexec, AutoCloseFd, AutoClosePipes, PIPE_ERROR}; -use crate::ffi::wcstring_list_ffi_t; use crate::flog::FLOGF; use crate::fork_exec::blocked_signals_for_job; use crate::fork_exec::postfork::{ @@ -47,13 +46,9 @@ use crate::trace::trace_if_enabled_with_args; use crate::wchar::{wstr, WString, L}; use crate::wchar_ext::ToWString; -use crate::wchar_ffi::AsWstr; -use crate::wchar_ffi::WCharToFFI; use crate::wutil::{fish_wcstol, perror}; use crate::wutil::{wgettext, wgettext_fmt}; -use cxx::{CxxWString, UniquePtr}; use errno::{errno, set_errno}; -use libc::c_int; use libc::{ c_char, EACCES, ENOENT, ENOEXEC, ENOTDIR, EPIPE, EXIT_FAILURE, EXIT_SUCCESS, O_NOCTTY, O_RDONLY, STDERR_FILENO, STDIN_FILENO, STDOUT_FILENO, @@ -1501,34 +1496,3 @@ fn exec_subshell_internal( *break_expand = false; eval_res.status.status_value() } - -#[cxx::bridge] -mod exec_ffi { - extern "C++" { - include!("wutil.h"); - include!("parser.h"); - type wcstring_list_ffi_t = crate::ffi::wcstring_list_ffi_t; - type Parser = crate::parser::Parser; - } - extern "Rust" { - #[cxx_name = "exec_subshell"] - fn exec_subshell_ffi( - cmd: &CxxWString, - parser: &Parser, - outputs: &mut UniquePtr, - apply_exit_status: bool, - ) -> i32; - } -} - -fn exec_subshell_ffi( - cmd: &CxxWString, - parser: &Parser, - outputs: &mut UniquePtr, - apply_exit_status: bool, -) -> c_int { - let mut tmp = vec![]; - let ret = exec_subshell(cmd.as_wstr(), parser, Some(&mut tmp), apply_exit_status); - *outputs = tmp.to_ffi(); - ret -} diff --git a/fish-rust/src/expand.rs b/fish-rust/src/expand.rs index 60c8aba5b..71607a660 100644 --- a/fish-rust/src/expand.rs +++ b/fish-rust/src/expand.rs @@ -13,7 +13,7 @@ EXPAND_RESERVED_END, }; use crate::complete::{CompleteFlags, Completion, CompletionList, CompletionReceiver}; -use crate::env::{EnvStackRefFFI, EnvVar, Environment}; +use crate::env::{EnvVar, Environment}; use crate::exec::exec_subshell_for_expand; use crate::history::{history_session_id, History}; use crate::operation_context::OperationContext; @@ -22,13 +22,11 @@ use crate::path::path_apply_working_directory; use crate::util::wcsfilecmp_glob; use crate::wchar::prelude::*; -use crate::wchar_ffi::{WCharFromFFI, WCharToFFI}; use crate::wcstringutil::{join_strings, trim}; use crate::wildcard::{wildcard_expand_string, wildcard_has_internal}; use crate::wildcard::{WildcardResult, ANY_CHAR, ANY_STRING, ANY_STRING_RECURSIVE}; use crate::wutil::{normalize_path, wcstoi_partial, Options}; use bitflags::bitflags; -use cxx::{CxxWString, UniquePtr}; bitflags! { /// Set of flags controlling expansions. @@ -99,8 +97,6 @@ pub struct ExpandFlags : u16 { "Characters used in expansions must stay within private use area" ); -pub use expand_ffi::{ExpandResult, ExpandResultCode}; - impl ExpandResult { pub fn new(result: ExpandResultCode) -> Self { Self { result, status: 0 } @@ -1571,59 +1567,28 @@ fn unexpand_tildes(&self, input: &wstr, completions: &mut CompletionList) { } } -#[cxx::bridge] -mod expand_ffi { - extern "C++" { - include!("operation_context.h"); - include!("parse_constants.h"); - include!("env.h"); - include!("complete.h"); - type OperationContext<'a> = crate::operation_context::OperationContext<'a>; - type ParseErrorListFfi = crate::parse_constants::ParseErrorListFfi; - #[cxx_name = "EnvDyn"] - type EnvDynFFI = crate::env::EnvDynFFI; - #[cxx_name = "EnvStackRef"] - type EnvStackRefFFI = crate::env::EnvStackRefFFI; - type CompletionListFfi = crate::complete::CompletionListFfi; - } - - #[derive(Copy, Clone, Debug, Eq, PartialEq)] - pub enum ExpandResultCode { - /// There was an error, for example, unmatched braces. - error, - /// Expansion succeeded. - ok, - /// Expansion was cancelled (e.g. control-C). - cancel, - /// Expansion succeeded, but a wildcard in the string matched no files, - /// so the output is empty. - wildcard_no_match, - } - - /// These are the possible return values for expand_string. - #[must_use] - #[derive(Debug)] - pub struct ExpandResult { - /// The result of expansion. - pub result: ExpandResultCode, - - /// If expansion resulted in an error, this is an appropriate value with which to populate - /// $status. - // todo!("should be c_int?"); - pub status: i32, - } - - extern "Rust" { - #[cxx_name = "expand_home_directory"] - fn expand_home_directory_ffi( - input: &CxxWString, - vars: &EnvStackRefFFI, - ) -> UniquePtr; - } +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +pub enum ExpandResultCode { + /// There was an error, for example, unmatched braces. + error, + /// Expansion succeeded. + ok, + /// Expansion was cancelled (e.g. control-C). + cancel, + /// Expansion succeeded, but a wildcard in the string matched no files, + /// so the output is empty. + wildcard_no_match, } -fn expand_home_directory_ffi(input: &CxxWString, vars: &EnvStackRefFFI) -> UniquePtr { - let mut s = input.from_ffi(); - expand_home_directory(&mut s, &*vars.0); - s.to_ffi() +/// These are the possible return values for expand_string. +#[must_use] +#[derive(Debug)] +pub struct ExpandResult { + /// The result of expansion. + pub result: ExpandResultCode, + + /// If expansion resulted in an error, this is an appropriate value with which to populate + /// $status. + // todo!("should be c_int?"); + pub status: i32, } diff --git a/fish-rust/src/fd_monitor.rs b/fish-rust/src/fd_monitor.rs index 3bbb1c7e4..d901d5d4c 100644 --- a/fish-rust/src/fd_monitor.rs +++ b/fish-rust/src/fd_monitor.rs @@ -3,11 +3,9 @@ use std::sync::{Arc, Mutex, Weak}; use std::time::{Duration, Instant}; -pub use self::fd_monitor_ffi::ItemWakeReason; use crate::common::exit_without_destructors; use crate::fd_readable_set::FdReadableSet; use crate::fds::AutoCloseFd; -use crate::ffi::void_ptr; use crate::flog::FLOG; use crate::threads::assert_is_background_thread; use crate::wutil::perror; @@ -19,61 +17,15 @@ #[cfg(HAVE_EVENTFD)] use libc::{EFD_CLOEXEC, EFD_NONBLOCK}; -#[cxx::bridge] -mod fd_monitor_ffi { - /// Reason for waking an item - #[repr(u8)] - #[cxx_name = "item_wake_reason_t"] - #[derive(PartialEq, Eq)] - enum ItemWakeReason { - /// The fd became readable (or was HUP'd) - Readable, - /// The requested timeout was hit - Timeout, - /// The item was "poked" (woken up explicitly) - Poke, - } - - extern "Rust" { - #[cxx_name = "fd_monitor_item_id_t"] - type FdMonitorItemId; - } - - extern "Rust" { - #[cxx_name = "fd_monitor_item_t"] - type FdMonitorItem; - - #[cxx_name = "make_fd_monitor_item_t"] - fn new_fd_monitor_item_ffi( - fd: i32, - timeout_usecs: u64, - callback: *const u8, - param: *const u8, - ) -> Box; - } - - extern "Rust" { - #[cxx_name = "fd_monitor_t"] - type FdMonitor; - - #[cxx_name = "make_fd_monitor_t"] - fn new_fd_monitor_ffi() -> Box; - - #[cxx_name = "add_item"] - fn add_item_ffi( - &mut self, - fd: i32, - timeout_usecs: u64, - callback: *const u8, - param: *const u8, - ) -> u64; - - #[cxx_name = "poke_item"] - fn poke_item_ffi(&self, item_id: u64); - - #[cxx_name = "add"] - pub fn add_ffi(&mut self, item: Box) -> u64; - } +/// Reason for waking an item +#[derive(PartialEq, Eq)] +pub enum ItemWakeReason { + /// The fd became readable (or was HUP'd) + Readable, + /// The requested timeout was hit + Timeout, + /// The item was "poked" (woken up explicitly) + Poke, } /// An event signaller implemented using a file descriptor, so it can plug into @@ -239,7 +191,6 @@ fn from(value: u64) -> Self { } } -type FfiCallback = extern "C" fn(*mut AutoCloseFd, u8, void_ptr); pub type NativeCallback = Box; /// The callback type used by [`FdMonitorItem`]. It is passed a mutable reference to the @@ -254,7 +205,6 @@ fn from(value: u64) -> Self { enum FdMonitorCallback { None, Native(NativeCallback), - Ffi(FfiCallback /* fn ptr */, void_ptr /* param */), } /// An item containing an fd and callback, which can be monitored to watch when it becomes readable @@ -321,12 +271,6 @@ fn service_item(&mut self, fds: &FdReadableSet, now: &Instant) -> ItemAction { match &self.callback { FdMonitorCallback::None => panic!("Callback not assigned!"), FdMonitorCallback::Native(callback) => (callback)(&mut self.fd, reason), - FdMonitorCallback::Ffi(callback, param) => { - // Safety: identical objects are generated on both sides by cxx bridge as - // integers of the same size (minimum size to fit the enum). - let reason = unsafe { std::mem::transmute(reason) }; - (callback)(&mut self.fd as *mut _, reason, *param) - } } if !self.fd.is_valid() { result = ItemAction::Remove; @@ -345,12 +289,6 @@ fn maybe_poke_item(&mut self, pokelist: &[FdMonitorItemId]) -> ItemAction { match &self.callback { FdMonitorCallback::None => panic!("Callback not assigned!"), FdMonitorCallback::Native(callback) => (callback)(&mut self.fd, ItemWakeReason::Poke), - FdMonitorCallback::Ffi(callback, param) => { - // Safety: identical objects are generated on both sides by cxx bridge as - // integers of the same size (minimum size to fit the enum). - let reason = unsafe { std::mem::transmute(ItemWakeReason::Poke) }; - (callback)(&mut self.fd as *mut _, reason, *param) - } } // Return `ItemAction::Remove` if the callback closed the fd match self.fd.is_valid() { @@ -379,15 +317,6 @@ pub fn new( pub fn set_callback(&mut self, callback: NativeCallback) { self.callback = FdMonitorCallback::Native(callback); } - - fn set_callback_ffi(&mut self, callback: *const u8, param: *const u8) { - // Safety: we are just marshalling our function pointers with identical definitions on both - // sides of the ffi bridge as void pointers to keep cxx bridge happy. Whether we invoke the - // raw function as a void pointer or as a typed fn that helps us keep track of what we're - // doing is unsafe in all cases, so might as well make the best of it. - let callback = unsafe { std::mem::transmute(callback) }; - self.callback = FdMonitorCallback::Ffi(callback, param.into()); - } } impl Default for FdMonitorItem { @@ -402,32 +331,6 @@ fn default() -> Self { } } -// cxx bridge does not support "static member functions" in C++ or rust, so we need a top-level fn. -fn new_fd_monitor_ffi() -> Box { - Box::new(FdMonitor::new()) -} - -// cxx bridge does not support "static member functions" in C++ or rust, so we need a top-level fn. -fn new_fd_monitor_item_ffi( - fd: RawFd, - timeout_usecs: u64, - callback: *const u8, - param: *const u8, -) -> Box { - // Safety: we are just marshalling our function pointers with identical definitions on both - // sides of the ffi bridge as void pointers to keep cxx bridge happy. Whether we invoke the - // raw function as a void pointer or as a typed fn that helps us keep track of what we're - // doing is unsafe in all cases, so might as well make the best of it. - let callback = unsafe { std::mem::transmute(callback) }; - let mut item = FdMonitorItem::default(); - item.fd.reset(fd); - item.callback = FdMonitorCallback::Ffi(callback, param.into()); - if timeout_usecs != FdReadableSet::kNoTimeout { - item.timeout = Some(Duration::from_micros(timeout_usecs)); - } - return Box::new(item); -} - /// A thread-safe class which can monitor a set of fds, invoking a callback when any becomes /// readable (or has been HUP'd) or when per-item-configurable timeouts are reached. pub struct FdMonitor { @@ -475,11 +378,6 @@ struct BackgroundFdMonitor { } impl FdMonitor { - #[allow(clippy::boxed_local)] - pub fn add_ffi(&self, item: Box) -> u64 { - self.add(*item).0 - } - /// Add an item to the monitor. Returns the [`FdMonitorItemId`] assigned to the item. pub fn add(&self, mut item: FdMonitorItem) -> FdMonitorItemId { assert!(item.fd.is_valid()); @@ -523,29 +421,6 @@ pub fn add(&self, mut item: FdMonitorItem) -> FdMonitorItemId { item_id } - /// Avoid requiring a separate UniquePtr for each item C++ wants to add to the set by giving an - /// all-in-one entry point that can initialize the item on our end and insert it to the set. - fn add_item_ffi( - &mut self, - fd: RawFd, - timeout_usecs: u64, - callback: *const u8, - param: *const u8, - ) -> u64 { - // Safety: we are just marshalling our function pointers with identical definitions on both - // sides of the ffi bridge as void pointers to keep cxx bridge happy. Whether we invoke the - // raw function as a void pointer or as a typed fn that helps us keep track of what we're - // doing is unsafe in all cases, so might as well make the best of it. - let callback = unsafe { std::mem::transmute(callback) }; - let mut item = FdMonitorItem::default(); - item.fd.reset(fd); - item.callback = FdMonitorCallback::Ffi(callback, param.into()); - if timeout_usecs != FdReadableSet::kNoTimeout { - item.timeout = Some(Duration::from_micros(timeout_usecs)); - } - self.add(item).0 - } - /// Mark that the item with the given ID needs to be woken up explicitly. pub fn poke_item(&self, item_id: FdMonitorItemId) { assert!(item_id.0 > 0, "Invalid item id!"); @@ -564,10 +439,6 @@ pub fn poke_item(&self, item_id: FdMonitorItemId) { } } - fn poke_item_ffi(&self, item_id: u64) { - self.poke_item(FdMonitorItemId(item_id)) - } - pub fn new() -> Self { Self { data: Arc::new(Mutex::new(SharedData { diff --git a/fish-rust/src/fd_readable_set.rs b/fish-rust/src/fd_readable_set.rs index eba2b0842..71e620192 100644 --- a/fish-rust/src/fd_readable_set.rs +++ b/fish-rust/src/fd_readable_set.rs @@ -3,20 +3,6 @@ pub use fd_readable_set_t as FdReadableSet; -#[cxx::bridge] -mod fd_readable_set_ffi { - extern "Rust" { - type fd_readable_set_t; - fn new_fd_readable_set() -> Box; - fn clear(&mut self); - fn add(&mut self, fd: i32); - fn test(&self, fd: i32) -> bool; - fn check_readable(&mut self, timeout_usec: u64) -> i32; - fn is_fd_readable(fd: i32, timeout_usec: u64) -> bool; - fn poll_fd_readable(fd: i32) -> bool; - } -} - /// Create a new fd_readable_set_t. pub fn new_fd_readable_set() -> Box { Box::new(fd_readable_set_t::new()) diff --git a/fish-rust/src/fds.rs b/fish-rust/src/fds.rs index 71e95e72c..7038b85b0 100644 --- a/fish-rust/src/fds.rs +++ b/fish-rust/src/fds.rs @@ -58,19 +58,6 @@ fn flush(&mut self) -> std::io::Result<()> { } } -#[cxx::bridge] -mod autoclose_fd_t { - extern "Rust" { - #[cxx_name = "autoclose_fd_t2"] - type AutoCloseFd; - - fn new_autoclose_fd(fd: i32) -> Box; - #[cxx_name = "valid"] - fn is_valid(&self) -> bool; - fn close(&mut self); - fn fd(&self) -> i32; - } -} fn new_autoclose_fd(fd: i32) -> Box { Box::new(AutoCloseFd::new(fd)) } diff --git a/fish-rust/src/ffi.rs b/fish-rust/src/ffi.rs index 11abd87c9..1c64ca525 100644 --- a/fish-rust/src/ffi.rs +++ b/fish-rust/src/ffi.rs @@ -1,156 +1,8 @@ -use crate::io::{IoStreams, OutputStreamFfi}; -use crate::wchar; -#[rustfmt::skip] -use ::std::pin::Pin; -#[rustfmt::skip] -use ::std::slice; -use crate::wchar::prelude::*; -use crate::wchar_ffi::WCharFromFFI; use autocxx::prelude::*; // autocxx has been hacked up to know about this. pub type wchar_t = u32; include_cpp! { - #include "color.h" #include "common.h" - #include "env.h" - #include "env_dispatch.h" - #include "env_universal_common.h" - #include "event.h" - #include "exec.h" - #include "fallback.h" - #include "fds.h" - #include "flog.h" - #include "function.h" - #include "io.h" - #include "parse_constants.h" - #include "parser.h" - #include "parse_util.h" - #include "path.h" - #include "pager.h" - #include "proc.h" - #include "reader.h" - #include "screen.h" - #include "tokenizer.h" - #include "wutil.h" - - safety!(unsafe_ffi) - - generate_pod!("wcharz_t") - generate!("wcstring_list_ffi_t") - - generate!("highlight_spec_t") - - generate!("rgb_color_t") - generate_pod!("color24_t") - - generate_pod!("escape_string_style_t") - -} - -/// Allow wcharz_t to be "into" wstr. -impl From for &wstr { - fn from(w: wcharz_t) -> Self { - let len = w.length(); - #[allow(clippy::unnecessary_cast)] - let v = unsafe { slice::from_raw_parts(w.str_ as *const u32, len) }; - wstr::from_slice(v).expect("Invalid UTF-32") - } -} - -/// Allow wcharz_t to be "into" WString. -impl From for WString { - fn from(w: wcharz_t) -> Self { - let w: &wstr = w.into(); - w.to_owned() - } -} - -/// Allow wcstring_list_ffi_t to be "into" Vec. -impl From<&wcstring_list_ffi_t> for Vec { - fn from(w: &wcstring_list_ffi_t) -> Self { - let mut result = Vec::with_capacity(w.size()); - for i in 0..w.size() { - result.push(w.at(i).from_ffi()); - } - result - } -} - -/// A bogus trait for turning &mut Foo into Pin<&mut Foo>. -/// autocxx enforces that non-const methods must be called through Pin, -/// but this means we can't pass around mutable references to types like Parser. -/// We also don't want to assert that Parser is Unpin. -/// So we just allow constructing a pin from a mutable reference; none of the C++ code. -/// It's worth considering disabling this in cxx; for now we use this trait. -/// Eventually Parser and IoStreams will not require Pin so we just unsafe-it away. -pub trait Repin { - fn pin(&mut self) -> Pin<&mut Self> { - unsafe { Pin::new_unchecked(self) } - } - - fn unpin(self: Pin<&mut Self>) -> &mut Self { - unsafe { self.get_unchecked_mut() } - } -} - -// Implement Repin for our types. -impl Repin for IoStreams<'_> {} -impl Repin for wcstring_list_ffi_t {} -impl Repin for rgb_color_t {} -impl Repin for OutputStreamFfi<'_> {} - -pub use ffi::*; - -/// A version of [`* const core::ffi::c_void`] (or [`* const libc::c_void`], if you prefer) that -/// implements `Copy` and `Clone`, because those two don't. Used to represent a `void *` ptr for ffi -/// purposes. -#[repr(transparent)] -#[derive(Copy, Clone)] -pub struct void_ptr(pub *const core::ffi::c_void); - -impl core::fmt::Debug for void_ptr { - fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { - write!(f, "{:p}", &self.0) - } -} - -unsafe impl Send for void_ptr {} -unsafe impl Sync for void_ptr {} - -impl core::convert::From<*const core::ffi::c_void> for void_ptr { - fn from(value: *const core::ffi::c_void) -> Self { - Self(value as *const _) - } -} - -impl core::convert::From<*const u8> for void_ptr { - fn from(value: *const u8) -> Self { - Self(value as *const _) - } -} - -impl core::convert::From<*const autocxx::c_void> for void_ptr { - fn from(value: *const autocxx::c_void) -> Self { - Self(value as *const _) - } -} - -impl core::convert::From for *const u8 { - fn from(value: void_ptr) -> Self { - value.0 as *const _ - } -} - -impl core::convert::From for *const core::ffi::c_void { - fn from(value: void_ptr) -> Self { - value.0 as *const _ - } -} - -impl core::convert::From for *const autocxx::c_void { - fn from(value: void_ptr) -> Self { - value.0 as *const _ - } } diff --git a/fish-rust/src/ffi_init.rs b/fish-rust/src/ffi_init.rs index a772bb43d..88b711fa3 100644 --- a/fish-rust/src/ffi_init.rs +++ b/fish-rust/src/ffi_init.rs @@ -1,18 +1,10 @@ /// Bridged functions concerned with initialization. -use crate::ffi::wcharz_t; use crate::locale; #[cxx::bridge] mod ffi2 { - - extern "C++" { - include!("wutil.h"); - type wcharz_t = super::wcharz_t; - } - extern "Rust" { fn rust_init(); - fn rust_activate_flog_categories_by_pattern(wc_ptr: wcharz_t); fn rust_set_flog_file_fd(fd: i32); fn rust_invalidate_numeric_locale(); } @@ -24,11 +16,6 @@ fn rust_init() { crate::threads::init(); } -/// FFI bridge for activate_flog_categories_by_pattern(). -fn rust_activate_flog_categories_by_pattern(wc_ptr: wcharz_t) { - crate::flog::activate_flog_categories_by_pattern(wc_ptr.into()); -} - /// FFI bridge for setting FLOG file descriptor. fn rust_set_flog_file_fd(fd: i32) { crate::flog::set_flog_file_fd(fd as libc::c_int); diff --git a/fish-rust/src/fish_indent.rs b/fish-rust/src/fish_indent.rs index 79b010ce7..b73fa1a2f 100755 --- a/fish-rust/src/fish_indent.rs +++ b/fish-rust/src/fish_indent.rs @@ -994,7 +994,6 @@ fn highlight_role_to_string(role: HighlightRole) -> &'static wstr { HighlightRole::pager_selected_prefix => L!("pager_selected_prefix"), HighlightRole::pager_selected_completion => L!("pager_selected_completion"), HighlightRole::pager_selected_description => L!("pager_selected_description"), - _ => unreachable!(), } } diff --git a/fish-rust/src/fork_exec/postfork.rs b/fish-rust/src/fork_exec/postfork.rs index 54a4190c0..48a326a03 100644 --- a/fish-rust/src/fork_exec/postfork.rs +++ b/fish-rust/src/fork_exec/postfork.rs @@ -6,7 +6,7 @@ use crate::nix::getpid; use crate::redirection::Dup2List; use crate::signal::signal_reset_handlers; -use libc::{c_char, c_int, pid_t}; +use libc::{c_char, pid_t}; use std::ffi::CStr; /// The number of times to try to call fork() before giving up. @@ -546,60 +546,3 @@ fn get_interpreter<'a>(command: &CStr, buffer: &'a mut [u8]) -> Option<&'a CStr> }; Some(CStr::from_bytes_with_nul(&buffer[offset..idx.max(offset)]).unwrap()) } - -/// Set up redirections and signal handling in the child process. -mod ffi { - use super::*; - #[no_mangle] - pub extern "C" fn child_setup_process( - claim_tty_from: pid_t, - sigmask: *const libc::sigset_t, - is_forked: bool, - dup2s: *const Dup2List, - ) -> i32 { - let sigmask = unsafe { sigmask.as_ref() }; - let dup2s = unsafe { &*dup2s }; - super::child_setup_process(claim_tty_from, sigmask, is_forked, dup2s) - } - - #[no_mangle] - pub extern "C" fn safe_report_exec_error( - err: i32, - actual_cmd: *const c_char, - argvv: *const *const c_char, - envv: *const *const c_char, - ) { - super::safe_report_exec_error(err, actual_cmd, argvv, envv) - } - - #[no_mangle] - pub extern "C" fn execute_fork() -> pid_t { - super::execute_fork() - } - - #[no_mangle] - pub extern "C" fn execute_setpgid(pid: pid_t, pgroup: pid_t, is_parent: bool) -> i32 { - super::execute_setpgid(pid, pgroup, is_parent) - } - - #[no_mangle] - pub extern "C" fn report_setpgid_error( - err: i32, - is_parent: bool, - pid: pid_t, - desired_pgid: pid_t, - job_id: c_int, - command_str: *const c_char, - argv0_str: *const c_char, - ) { - super::report_setpgid_error( - err, - is_parent, - pid, - desired_pgid, - job_id.into(), - command_str, - argv0_str, - ) - } -} diff --git a/fish-rust/src/fork_exec/spawn.rs b/fish-rust/src/fork_exec/spawn.rs index 59d9fdaca..5f499b460 100644 --- a/fish-rust/src/fork_exec/spawn.rs +++ b/fish-rust/src/fork_exec/spawn.rs @@ -5,7 +5,7 @@ use crate::proc::Job; use crate::redirection::Dup2List; use crate::signal::get_signals_with_handlers; -use errno::{self, set_errno, Errno}; +use errno::{self, Errno}; use libc::{self, c_char, posix_spawn_file_actions_t, posix_spawnattr_t}; use std::ffi::{CStr, CString}; @@ -219,27 +219,3 @@ fn get_path_bshell() -> CString { // which fail to run Thompson shell scripts; we simply assume it is /bin/sh. CString::new("/bin/sh").unwrap() } - -impl Drop for PosixSpawner { - fn drop(&mut self) { - // Necessary to define this for FFI purposes, to avoid link errors. - } -} - -impl PosixSpawner { - /// Returns a pid, or -1, in which case errno is set. - fn spawn_ffi( - &mut self, - cmd: *const c_char, - argv: *const *mut c_char, - envp: *const *mut c_char, - ) -> i32 { - match self.spawn(cmd, argv, envp) { - Ok(pid) => pid, - Err(err) => { - set_errno(err); - -1 - } - } - } -} diff --git a/fish-rust/src/function.rs b/fish-rust/src/function.rs index a265ae19f..342742c3f 100644 --- a/fish-rust/src/function.rs +++ b/fish-rust/src/function.rs @@ -9,17 +9,13 @@ use crate::env::{EnvStack, Environment}; use crate::event::{self, EventDescription}; use crate::global_safety::RelaxedAtomicBool; -use crate::parse_tree::{NodeRef, ParsedSourceRefFFI}; +use crate::parse_tree::NodeRef; use crate::parser::Parser; use crate::parser_keywords::parser_keywords_is_reserved; use crate::wchar::prelude::*; -use crate::wchar_ffi::wcstring_list_ffi_t; -use crate::wchar_ffi::{AsWstr, WCharFromFFI, WCharToFFI}; use crate::wutil::{dir_iter::DirIter, gettext::wgettext_expr, sprintf}; -use cxx::{CxxWString, UniquePtr}; use once_cell::sync::Lazy; use std::collections::{HashMap, HashSet}; -use std::pin::Pin; use std::sync::{Arc, Mutex}; #[derive(Clone)] @@ -496,203 +492,3 @@ pub fn annotated_definition(&self, name: &wstr) -> WString { out } } - -pub struct FunctionPropertiesRefFFI(pub Arc); - -impl FunctionPropertiesRefFFI { - fn definition_file(&self) -> UniquePtr { - if let Some(file) = self.0.definition_file() { - file.to_ffi() - } else { - UniquePtr::null() - } - } - - fn definition_lineno(&self) -> i32 { - self.0.definition_lineno() - } - - fn copy_definition_lineno(&self) -> i32 { - self.0.copy_definition_lineno() - } - - fn shadow_scope(&self) -> bool { - self.0.shadow_scope - } - - fn named_arguments(&self) -> UniquePtr { - self.0.named_arguments.to_ffi() - } - - fn get_description(&self) -> UniquePtr { - self.0.description.to_ffi() - } - - fn annotated_definition(&self, name: &CxxWString) -> UniquePtr { - self.0.annotated_definition(name.as_wstr()).to_ffi() - } - - fn is_autoload(&self) -> bool { - self.0.is_autoload.load() - } - - fn is_copy(&self) -> bool { - self.0.is_copy - } - - fn get_block_statement_node_ffi(&self) -> *const u8 { - let stmt: &ast::BlockStatement = &self.0.func_node; - stmt as *const ast::BlockStatement as *const u8 - } - - fn parsed_source_ffi(&self) -> *mut u8 { - let source = self.0.func_node.parsed_source_ref(); - let res = Box::new(ParsedSourceRefFFI(Some(source))); - Box::into_raw(res) as *mut u8 - } - - fn copy_definition_file_ffi(&self) -> UniquePtr { - if let Some(file) = self.0.copy_definition_file() { - file.to_ffi() - } else { - UniquePtr::null() - } - } -} - -#[allow(clippy::boxed_local)] -fn function_add_ffi(name: &CxxWString, props: Box) { - add(name.from_ffi(), props.0); -} - -fn function_remove_ffi(name: &CxxWString) { - remove(name.as_wstr()); -} - -fn function_get_props_ffi(name: &CxxWString) -> *mut FunctionPropertiesRefFFI { - let props = get_props(name.as_wstr()); - if let Some(props) = props { - Box::into_raw(Box::new(FunctionPropertiesRefFFI(props))) - } else { - std::ptr::null_mut() - } -} - -fn function_get_props_autoload_ffi( - name: &CxxWString, - parser: &Parser, -) -> *mut FunctionPropertiesRefFFI { - let props = get_props_autoload(name.as_wstr(), parser); - if let Some(props) = props { - Box::into_raw(Box::new(FunctionPropertiesRefFFI(props))) - } else { - std::ptr::null_mut() - } -} - -fn function_load_ffi(name: &CxxWString, parser: &Parser) -> bool { - load(name.as_wstr(), parser) -} - -fn function_set_desc_ffi(name: &CxxWString, desc: &CxxWString, parser: &Parser) { - set_desc(name.as_wstr(), desc.from_ffi(), parser); -} - -fn function_exists_ffi(cmd: &CxxWString, parser: &Parser) -> bool { - exists(cmd.as_wstr(), parser) -} - -fn function_exists_no_autoload_ffi(cmd: &CxxWString) -> bool { - exists_no_autoload(cmd.as_wstr()) -} - -fn function_get_names_ffi(get_hidden: bool, mut out: Pin<&mut wcstring_list_ffi_t>) { - let names = get_names(get_hidden); - for name in names { - out.as_mut().push(name.to_ffi()); - } -} - -fn function_copy_ffi(name: &CxxWString, new_name: &CxxWString, parser: &Parser) -> bool { - copy(name.as_wstr(), new_name.from_ffi(), parser) -} - -#[cxx::bridge] -mod function_ffi { - extern "C++" { - include!("ast.h"); - include!("parse_tree.h"); - include!("parser.h"); - include!("wutil.h"); - type Parser = crate::parser::Parser; - type wcstring_list_ffi_t = crate::ffi::wcstring_list_ffi_t; - } - - extern "Rust" { - #[cxx_name = "function_properties_t"] - type FunctionPropertiesRefFFI; - - fn definition_file(&self) -> UniquePtr; - fn definition_lineno(&self) -> i32; - fn copy_definition_lineno(&self) -> i32; - fn shadow_scope(&self) -> bool; - fn named_arguments(&self) -> UniquePtr; - fn get_description(&self) -> UniquePtr; - fn annotated_definition(&self, name: &CxxWString) -> UniquePtr; - fn is_autoload(&self) -> bool; - fn is_copy(&self) -> bool; - - #[cxx_name = "copy_definition_file"] - fn copy_definition_file_ffi(&self) -> UniquePtr; - - /// Returns unowned pointer to BlockStatement, cast to a u8. - #[cxx_name = "get_block_statement_node"] - fn get_block_statement_node_ffi(&self) -> *const u8; - - /// Returns rust::Box::into_raw(), cast to a u8. - #[cxx_name = "parsed_source"] - fn parsed_source_ffi(self: &FunctionPropertiesRefFFI) -> *mut u8; - - #[cxx_name = "function_add"] - fn function_add_ffi(name: &CxxWString, props: Box); - - #[cxx_name = "function_remove"] - fn function_remove_ffi(name: &CxxWString); - - /// Returns a Box::into_raw(), or nullptr if None. - #[cxx_name = "function_get_props_raw"] - fn function_get_props_ffi(name: &CxxWString) -> *mut FunctionPropertiesRefFFI; - - /// Returns a Box::into_raw(), or nullptr if None. - #[cxx_name = "function_get_props_autoload_raw"] - fn function_get_props_autoload_ffi( - name: &CxxWString, - parser: &Parser, - ) -> *mut FunctionPropertiesRefFFI; - - #[cxx_name = "function_load"] - fn function_load_ffi(name: &CxxWString, parser: &Parser) -> bool; - - #[cxx_name = "function_set_desc"] - fn function_set_desc_ffi(name: &CxxWString, desc: &CxxWString, parser: &Parser); - - #[cxx_name = "function_exists"] - fn function_exists_ffi(cmd: &CxxWString, parser: &Parser) -> bool; - #[cxx_name = "function_exists_no_autoload"] - fn function_exists_no_autoload_ffi(cmd: &CxxWString) -> bool; - - #[cxx_name = "function_get_names"] - fn function_get_names_ffi(get_hidden: bool, out: Pin<&mut wcstring_list_ffi_t>); - - #[cxx_name = "function_copy"] - fn function_copy_ffi(name: &CxxWString, new_name: &CxxWString, parser: &Parser) -> bool; - - #[cxx_name = "function_invalidate_path"] - fn invalidate_path(); - } -} - -unsafe impl cxx::ExternType for FunctionPropertiesRefFFI { - type Id = cxx::type_id!("function_properties_t"); - type Kind = cxx::kind::Opaque; -} diff --git a/fish-rust/src/future_feature_flags.rs b/fish-rust/src/future_feature_flags.rs index 8b47d3310..b5454fa3d 100644 --- a/fish-rust/src/future_feature_flags.rs +++ b/fish-rust/src/future_feature_flags.rs @@ -1,43 +1,26 @@ //! Flags to enable upcoming features -use crate::ffi::wcharz_t; use crate::wchar::prelude::*; use std::sync::atomic::AtomicBool; use std::sync::atomic::Ordering; -#[cxx::bridge] -mod future_feature_flags_ffi { - extern "C++" { - include!("wutil.h"); - type wcharz_t = super::wcharz_t; - } +/// The list of flags. +#[repr(u8)] +#[derive(Clone, Copy)] +pub enum FeatureFlag { + /// Whether ^ is supported for stderr redirection. + stderr_nocaret, - /// The list of flags. - #[repr(u8)] - enum FeatureFlag { - /// Whether ^ is supported for stderr redirection. - stderr_nocaret, + /// Whether ? is supported as a glob. + qmark_noglob, - /// Whether ? is supported as a glob. - qmark_noglob, + /// Whether string replace -r double-unescapes the replacement. + string_replace_backslash, - /// Whether string replace -r double-unescapes the replacement. - string_replace_backslash, - - /// Whether "&" is not-special if followed by a word character. - ampersand_nobg_in_token, - } - - extern "Rust" { - #[cxx_name = "feature_test"] - fn test(flag: FeatureFlag) -> bool; - #[cxx_name = "feature_set_from_string"] - fn set_from_string(str: wcharz_t); - } + /// Whether "&" is not-special if followed by a word character. + ampersand_nobg_in_token, } -pub use future_feature_flags_ffi::FeatureFlag; - struct Features { // Values for the flags. // These are atomic to "fix" a race reported by tsan where tests of feature flags and other @@ -165,11 +148,11 @@ const fn new() -> Self { } fn test(&self, flag: FeatureFlag) -> bool { - self.values[flag.repr as usize].load(Ordering::SeqCst) + self.values[flag as usize].load(Ordering::SeqCst) } fn set(&self, flag: FeatureFlag, value: bool) { - self.values[flag.repr as usize].store(value, Ordering::SeqCst) + self.values[flag as usize].store(value, Ordering::SeqCst) } #[widestrs] @@ -244,14 +227,14 @@ fn test_feature_flags() { // Ensure every metadata is represented once. let mut counts: [usize; METADATA.len()] = [0; METADATA.len()]; for md in METADATA { - counts[md.flag.repr as usize] += 1; + counts[md.flag as usize] += 1; } for count in counts { assert_eq!(count, 1); } assert_eq!( - METADATA[FeatureFlag::stderr_nocaret.repr as usize].name, + METADATA[FeatureFlag::stderr_nocaret as usize].name, "stderr-nocaret"L ); } diff --git a/fish-rust/src/highlight.rs b/fish-rust/src/highlight.rs index aec1ad3f2..65006c605 100644 --- a/fish-rust/src/highlight.rs +++ b/fish-rust/src/highlight.rs @@ -5,14 +5,13 @@ Leaf, List, Node, NodeVisitor, Redirection, Token, Type, VariableAssignment, }; use crate::builtins::shared::builtin_exists; -use crate::color::{self, RgbColor}; +use crate::color::RgbColor; use crate::common::{ unescape_string_in_place, valid_var_name, valid_var_name_char, UnescapeFlags, ASCII_MAX, EXPAND_RESERVED_BASE, EXPAND_RESERVED_END, }; use crate::compat::_PC_CASE_SENSITIVE; -use crate::editable_line::EditableLine; -use crate::env::{EnvStackRefFFI, Environment}; +use crate::env::Environment; use crate::expand::{ expand_one, expand_tilde, expand_to_command_and_args, ExpandFlags, ExpandResultCode, HOME_DIRECTORY, PROCESS_EXPAND_SELF_STR, @@ -21,7 +20,6 @@ BRACE_BEGIN, BRACE_END, BRACE_SEP, INTERNAL_SEPARATOR, PROCESS_EXPAND_SELF, VARIABLE_EXPAND, VARIABLE_EXPAND_SINGLE, }; -use crate::ffi::rgb_color_t; use crate::function; use crate::future_feature_flags::{feature_test, FeatureFlag}; use crate::history::{all_paths_are_valid, HistoryItem}; @@ -40,7 +38,6 @@ use crate::tokenizer::{variable_assignment_equals_pos, PipeOrRedir}; use crate::wchar::{wstr, WString, L}; use crate::wchar_ext::WExt; -use crate::wchar_ffi::AsWstr; use crate::wcstringutil::{ string_prefixes_string, string_prefixes_string_case_insensitive, string_suffixes_string, }; @@ -50,12 +47,10 @@ use crate::wutil::{normalize_path, waccess, wstat}; use crate::wutil::{wbasename, wdirname}; use bitflags::bitflags; -use cxx::{CxxWString, SharedPtr}; use libc::{ENOENT, PATH_MAX, R_OK, W_OK}; use std::collections::hash_map::Entry; use std::collections::{HashMap, HashSet}; use std::os::fd::RawFd; -use std::pin::Pin; impl HighlightSpec { pub fn new() -> Self { @@ -1120,7 +1115,6 @@ fn visit_keyword(&mut self, node: &dyn Keyword) { | ParseKeyword::kw_exclam | ParseKeyword::kw_time => role = HighlightRole::operat, ParseKeyword::none => (), - _ => panic!(), }; self.color_node(node.leaf_as_node(), HighlightSpec::with_fg(role)); } @@ -1306,7 +1300,6 @@ fn visit_redirection(&mut self, redir: &Redirection) { target_is_valid = file_is_writable && !(file_exists && oper.mode == RedirectionMode::noclob); } - _ => panic!(), } } self.color_node( @@ -1537,7 +1530,6 @@ fn get_highlight_var_name(role: HighlightRole) -> &'static wstr { HighlightRole::pager_selected_prefix => L!("fish_pager_color_selected_prefix"), HighlightRole::pager_selected_completion => L!("fish_pager_color_selected_completion"), HighlightRole::pager_selected_description => L!("fish_pager_color_selected_description"), - _ => unreachable!(), } } @@ -1576,7 +1568,6 @@ fn get_fallback(role: HighlightRole) -> HighlightRole { HighlightRole::pager_description } HighlightRole::pager_selected_background => HighlightRole::search_match, - _ => unreachable!(), } } @@ -1612,8 +1603,6 @@ fn fs_is_case_insensitive( result } -pub use highlight_ffi::{HighlightRole, HighlightSpec}; - impl Default for HighlightRole { fn default() -> Self { Self::normal @@ -1631,222 +1620,48 @@ fn default() -> Self { } } -#[cxx::bridge] -mod highlight_ffi { - /// Describes the role of a span of text. - #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] - #[repr(u8)] - pub enum HighlightRole { - normal, // normal text - error, // error - command, // command - keyword, - statement_terminator, // process separator - param, // command parameter (argument) - option, // argument starting with "-", up to a "--" - comment, // comment - search_match, // search match - operat, // operator - escape, // escape sequences - quote, // quoted string - redirection, // redirection - autosuggestion, // autosuggestion - selection, +/// Describes the role of a span of text. +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +#[repr(u8)] +pub enum HighlightRole { + normal, // normal text + error, // error + command, // command + keyword, + statement_terminator, // process separator + param, // command parameter (argument) + option, // argument starting with "-", up to a "--" + comment, // comment + search_match, // search match + operat, // operator + escape, // escape sequences + quote, // quoted string + redirection, // redirection + autosuggestion, // autosuggestion + selection, - // Pager support. - // NOTE: pager.cpp relies on these being in this order. - pager_progress, - pager_background, - pager_prefix, - pager_completion, - pager_description, - pager_secondary_background, - pager_secondary_prefix, - pager_secondary_completion, - pager_secondary_description, - pager_selected_background, - pager_selected_prefix, - pager_selected_completion, - pager_selected_description, - } - - /// Simply value type describing how a character should be highlighted.. - #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] - pub struct HighlightSpec { - pub foreground: HighlightRole, - pub background: HighlightRole, - pub valid_path: bool, - pub force_underline: bool, - } - - extern "Rust" { - #[cxx_name = "clone"] - fn clone_ffi(self: &HighlightSpec) -> Box; - fn new_highlight_spec() -> Box; - fn editable_line_colors(editable_line: &EditableLine) -> &[HighlightSpec]; - } - - extern "C++" { - include!("highlight.h"); - include!("history.h"); - include!("color.h"); - include!("operation_context.h"); - include!("editable_line.h"); - type HistoryItem = crate::history::HistoryItem; - type OperationContext<'a> = crate::operation_context::OperationContext<'a>; - type rgb_color_t = crate::ffi::rgb_color_t; - #[cxx_name = "EnvDyn"] - type EnvDynFFI = crate::env::EnvDynFFI; - #[cxx_name = "EnvStackRef"] - type EnvStackRefFFI = crate::env::EnvStackRefFFI; - type EditableLine = crate::editable_line::EditableLine; - } - extern "Rust" { - #[cxx_name = "autosuggest_validate_from_history"] - fn autosuggest_validate_from_history_ffi( - item: &HistoryItem, - working_directory: &CxxWString, - ctx: &OperationContext<'static>, - ) -> bool; - } - extern "Rust" { - type HighlightColorResolver; - fn new_highlight_color_resolver() -> Box; - #[cxx_name = "resolve_spec"] - fn resolve_spec_ffi( - &mut self, - highlight: &HighlightSpec, - is_background: bool, - vars: &EnvStackRefFFI, - out: Pin<&mut rgb_color_t>, - ); - } - extern "Rust" { - type HighlightSpecListFFI; - fn new_highlight_spec_list() -> Box; - fn highlight_shell_ffi( - bff: &CxxWString, - ctx: &OperationContext<'_>, - io_ok: bool, - cursor: SharedPtr, - ) -> Box; - fn size(&self) -> usize; - fn at(&self, index: usize) -> &HighlightSpec; - #[cxx_name = "colorize"] - fn colorize_ffi( - text: &CxxWString, - colors: &HighlightSpecListFFI, - vars: &EnvStackRefFFI, - ) -> Vec; - fn push(&mut self, highlight: &HighlightSpec); - } + // Pager support. + // NOTE: pager.cpp relies on these being in this order. + pager_progress, + pager_background, + pager_prefix, + pager_completion, + pager_description, + pager_secondary_background, + pager_secondary_prefix, + pager_secondary_completion, + pager_secondary_description, + pager_selected_background, + pager_selected_prefix, + pager_selected_completion, + pager_selected_description, } -fn colorize_ffi( - text: &CxxWString, - colors: &HighlightSpecListFFI, - vars: &EnvStackRefFFI, -) -> Vec { - colorize(text.as_wstr(), &colors.0, &*vars.0) -} - -#[derive(Default)] -pub struct HighlightSpecListFFI(pub Vec); - -unsafe impl cxx::ExternType for HighlightSpecListFFI { - type Id = cxx::type_id!("HighlightSpecListFFI"); - type Kind = cxx::kind::Opaque; -} - -fn new_highlight_spec_list() -> Box { - Box::default() -} -impl HighlightSpecListFFI { - fn size(&self) -> usize { - self.0.len() - } - fn at(&self, index: usize) -> &HighlightSpec { - &self.0[index] - } - fn push(&mut self, highlight: &HighlightSpec) { - self.0.push(*highlight) - } -} -fn highlight_shell_ffi( - buff: &CxxWString, - ctx: &OperationContext<'_>, - io_ok: bool, - cursor: SharedPtr, -) -> Box { - let cursor = cursor.as_ref().cloned(); - let mut color = vec![]; - highlight_shell(buff.as_wstr(), &mut color, ctx, io_ok, cursor); - Box::new(HighlightSpecListFFI(color)) -} - -impl HighlightColorResolver { - fn resolve_spec_ffi( - &mut self, - highlight: &HighlightSpec, - is_background: bool, - vars: &EnvStackRefFFI, - mut out: Pin<&mut rgb_color_t>, - ) { - let color = self.resolve_spec(highlight, is_background, &*vars.0); - match color.typ { - color::Type::None => (), - color::Type::Named { idx } => { - out.as_mut().set_is_named(); - out.as_mut().set_name_idx(idx); - } - color::Type::Rgb(color) => { - out.as_mut().set_is_rgb(); - out.as_mut().set_color(color.r, color.g, color.b); - } - color::Type::Normal => out.as_mut().set_is_normal(), - color::Type::Reset => out.as_mut().set_is_reset(), - } - if color.flags.bold { - out.as_mut().set_bold(true); - } - if color.flags.underline { - out.as_mut().set_underline(true); - } - if color.flags.italics { - out.as_mut().set_italics(true); - } - if color.flags.dim { - out.as_mut().set_dim(true); - } - if color.flags.reverse { - out.as_mut().set_reverse(true); - } - } -} - -fn autosuggest_validate_from_history_ffi( - item: &HistoryItem, - working_directory: &CxxWString, - ctx: &OperationContext<'static>, -) -> bool { - autosuggest_validate_from_history(item, working_directory.as_wstr(), ctx) -} - -fn new_highlight_color_resolver() -> Box { - Box::new(HighlightColorResolver::new()) -} -impl HighlightSpec { - fn clone_ffi(&self) -> Box { - Box::new(*self) - } -} -fn new_highlight_spec() -> Box { - Box::default() -} -unsafe impl cxx::ExternType for EditableLine { - type Id = cxx::type_id!("EditableLine"); - type Kind = cxx::kind::Opaque; -} -fn editable_line_colors(editable_line: &EditableLine) -> &[HighlightSpec] { - editable_line.colors() +/// Simply value type describing how a character should be highlighted.. +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +pub struct HighlightSpec { + pub foreground: HighlightRole, + pub background: HighlightRole, + pub valid_path: bool, + pub force_underline: bool, } diff --git a/fish-rust/src/history.rs b/fish-rust/src/history.rs index 3f351e60c..34bfa3fda 100644 --- a/fish-rust/src/history.rs +++ b/fish-rust/src/history.rs @@ -21,7 +21,7 @@ borrow::Cow, collections::{BTreeMap, HashMap, HashSet, VecDeque}, ffi::CString, - io::{BufRead, BufReader, Read, Write}, + io::{BufRead, Read, Write}, mem, num::NonZeroUsize, ops::ControlFlow, @@ -31,7 +31,6 @@ }; use bitflags::bitflags; -use cxx::{CxxWString, UniquePtr}; use libc::{ fchmod, fchown, flock, fstat, ftruncate, lseek, LOCK_EX, LOCK_SH, LOCK_UN, O_APPEND, O_CREAT, O_RDONLY, O_WRONLY, SEEK_SET, @@ -41,32 +40,29 @@ use widestring_suffix::widestrs; use crate::{ - ast::{ast_ffi::StatementDecoration, Ast, Node}, + ast::{Ast, Node}, common::{ str2wcstring, unescape_string, valid_var_name, wcs2zstring, write_loop, CancelChecker, UnescapeStringStyle, }, - env::{EnvMode, EnvStack, EnvStackRefFFI, Environment}, + env::{EnvMode, EnvStack, Environment}, expand::{expand_one, ExpandFlags}, fallback::fish_mkstemp_cloexec, fds::{wopen_cloexec, AutoCloseFd}, - ffi::wcstring_list_ffi_t, flog::{FLOG, FLOGF}, global_safety::RelaxedAtomicBool, history::file::{append_history_item_to_buffer, HistoryFileContents}, io::IoStreams, operation_context::{OperationContext, EXPANSION_LIMIT_BACKGROUND}, - parse_constants::ParseTreeFlags, + parse_constants::{ParseTreeFlags, StatementDecoration}, parse_util::{parse_util_detect_errors, parse_util_unescape_wildcards}, path::{ path_get_config, path_get_data, path_get_data_remoteness, path_is_valid, DirRemoteness, }, - signal::signal_check_cancel, threads::{assert_is_background_thread, iothread_perform}, util::find_subslice, wchar::prelude::*, wchar_ext::WExt, - wchar_ffi::{WCharFromFFI, WCharToFFI}, wcstringutil::subsequence_in_string, wildcard::{wildcard_match, ANY_STRING}, wutil::{ @@ -77,152 +73,42 @@ mod file; -#[cxx::bridge] -mod history_ffi { - extern "C++" { - include!("wutil.h"); - include!("io.h"); - include!("env.h"); - include!("operation_context.h"); +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum SearchType { + /// Search for commands exactly matching the given string. + Exact, + /// Search for commands containing the given string. + Contains, + /// Search for commands starting with the given string. + Prefix, + /// Search for commands containing the given glob pattern. + ContainsGlob, + /// Search for commands starting with the given glob pattern. + PrefixGlob, + /// Search for commands containing the given string as a subsequence + ContainsSubsequence, + /// Matches everything. + MatchEverything, +} - type IoStreams<'a> = crate::io::IoStreams<'a>; - #[cxx_name = "EnvDyn"] - type EnvDynFFI = crate::env::EnvDynFFI; - #[cxx_name = "EnvStackRef"] - type EnvStackRefFFI = crate::env::EnvStackRefFFI; - type OperationContext<'a> = crate::operation_context::OperationContext<'a>; - type wcstring_list_ffi_t = crate::ffi::wcstring_list_ffi_t; - } +/// Ways that a history item may be written to disk (or omitted). +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum PersistenceMode { + /// The history item is written to disk normally + Disk, + /// The history item is stored in-memory only, not written to disk + Memory, + /// The history item is stored in-memory and deleted when a new item is added + Ephemeral, +} - #[derive(Clone, Copy, Debug, PartialEq, Eq)] - enum SearchType { - /// Search for commands exactly matching the given string. - Exact, - /// Search for commands containing the given string. - Contains, - /// Search for commands starting with the given string. - Prefix, - /// Search for commands containing the given glob pattern. - ContainsGlob, - /// Search for commands starting with the given glob pattern. - PrefixGlob, - /// Search for commands containing the given string as a subsequence - ContainsSubsequence, - /// Matches everything. - MatchEverything, - } - - /// Ways that a history item may be written to disk (or omitted). - #[derive(Clone, Copy, Debug, PartialEq, Eq)] - enum PersistenceMode { - /// The history item is written to disk normally - Disk, - /// The history item is stored in-memory only, not written to disk - Memory, - /// The history item is stored in-memory and deleted when a new item is added - Ephemeral, - } - - #[derive(Clone, Copy, Debug, PartialEq, Eq)] - pub enum SearchDirection { - Forward, - Backward, - } - - extern "Rust" { - #[cxx_name = "history_save_all"] - fn save_all(); - #[cxx_name = "history_session_id"] - fn rust_session_id(vars: &EnvStackRefFFI) -> UniquePtr; - fn rust_expand_and_detect_paths( - paths: &wcstring_list_ffi_t, - vars: &EnvStackRefFFI, - ) -> UniquePtr; - fn rust_all_paths_are_valid( - paths: &wcstring_list_ffi_t, - ctx: &OperationContext<'_>, - ) -> bool; - #[rust_name = "start_private_mode_ffi"] - fn start_private_mode(vars: &EnvStackRefFFI); - #[rust_name = "in_private_mode_ffi"] - fn in_private_mode(vars: &EnvStackRefFFI) -> bool; - #[cxx_name = "all_paths_are_valid"] - fn all_paths_are_valid_ffi(paths: &wcstring_list_ffi_t, ctx: &OperationContext<'_>) - -> bool; - } - - extern "Rust" { - type ItemIndexes; - - fn get(&self, index: usize) -> UniquePtr; - } - - extern "Rust" { - type HistoryItem; - - fn rust_history_item_new( - s: &CxxWString, - when: i64, - ident: u64, - persist_mode: PersistenceMode, - ) -> Box; - #[rust_name = "str_ffi"] - fn str(&self) -> UniquePtr; - fn is_empty(&self) -> bool; - #[rust_name = "matches_search_ffi"] - fn matches_search(&self, term: &CxxWString, typ: SearchType, case_sensitive: bool) -> bool; - #[rust_name = "timestamp_ffi"] - fn timestamp(&self) -> i64; - fn should_write_to_disk(&self) -> bool; - #[rust_name = "get_required_paths_ffi"] - fn get_required_paths(&self) -> UniquePtr; - #[rust_name = "set_required_paths_ffi"] - fn set_required_paths(&mut self, paths: &wcstring_list_ffi_t); - } - - extern "Rust" { - type HistorySharedPtr; - fn history_with_name(name: &CxxWString) -> Box; - fn is_default(&self) -> bool; - fn is_empty(&self) -> bool; - fn remove(&self, s: &CxxWString); - fn remove_ephemeral_items(&self); - fn resolve_pending(&self); - fn save(&self); - fn clear(&self); - fn clear_session(&self); - fn populate_from_config_path(&self); - fn populate_from_bash(&self, filename: &CxxWString); - fn incorporate_external_changes(&self); - fn get_history(&self) -> UniquePtr; - fn items_at_indexes(&self, indexes: &[isize]) -> Box; - fn item_at_index(&self, idx: usize) -> Box; - fn size(&self) -> usize; - fn clone(&self) -> Box; - } - - extern "Rust" { - type HistorySearch; - fn rust_history_search_new( - hist: &HistorySharedPtr, - s: &CxxWString, - search_type: SearchType, - flags: u32, - starting_index: usize, - ) -> Box; - #[rust_name = "original_term_ffi"] - fn original_term(&self) -> UniquePtr; - fn go_to_next_match(&mut self, direction: SearchDirection) -> bool; - fn current_item(&self) -> &HistoryItem; - #[rust_name = "current_string_ffi"] - fn current_string(&self) -> UniquePtr; - fn current_index(&self) -> usize; - fn ignores_case(&self) -> bool; - } +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +pub enum SearchDirection { + Forward, + Backward, } use self::file::time_to_seconds; -pub use self::history_ffi::{PersistenceMode, SearchDirection, SearchType}; // Our history format is intended to be valid YAML. Here it is: // @@ -375,10 +261,6 @@ pub fn str(&self) -> &wstr { &self.contents } - fn str_ffi(&self) -> UniquePtr { - self.str().to_ffi() - } - /// Returns whether the text is empty. pub fn is_empty(&self) -> bool { self.contents.is_empty() @@ -419,32 +301,14 @@ pub fn matches_search(&self, term: &wstr, typ: SearchType, case_sensitive: bool) } SearchType::ContainsSubsequence => subsequence_in_string(term, &content_to_match), SearchType::MatchEverything => true, - _ => unreachable!("invalid SearchType"), } } - fn matches_search_ffi(&self, term: &CxxWString, typ: SearchType, case_sensitive: bool) -> bool { - self.matches_search(&term.from_ffi(), typ, case_sensitive) - } - /// Returns the timestamp for creating this history item. pub fn timestamp(&self) -> SystemTime { self.creation_timestamp } - fn timestamp_ffi(&self) -> i64 { - match self.timestamp().duration_since(UNIX_EPOCH) { - Ok(d) => { - // after epoch - i64::try_from(d.as_secs()).unwrap() - } - Err(e) => { - // before epoch - -i64::try_from(e.duration().as_secs()).unwrap() - } - } - } - /// Returns whether this item should be persisted (written to disk). pub fn should_write_to_disk(&self) -> bool { self.persist_mode == PersistenceMode::Disk @@ -456,20 +320,12 @@ pub fn get_required_paths(&self) -> &[WString] { &self.required_paths } - fn get_required_paths_ffi(&self) -> UniquePtr { - self.get_required_paths().to_ffi() - } - /// Set the list of arguments which referred to files. /// This is used for autosuggestion hinting. pub fn set_required_paths(&mut self, paths: Vec) { self.required_paths = paths; } - fn set_required_paths_ffi(&mut self, paths: &wcstring_list_ffi_t) { - self.set_required_paths(paths.from_ffi()) - } - /// We can merge two items if they are the same command. We use the more recent timestamp, more /// recent identifier, and the longer list of required paths. fn merge(&mut self, item: &HistoryItem) -> bool { @@ -1986,16 +1842,11 @@ pub fn original_term(&self) -> &wstr { &self.orig_term } - fn original_term_ffi(&self) -> UniquePtr { - self.original_term().to_ffi() - } - /// Finds the next search result. Returns `true` if one was found. pub fn go_to_next_match(&mut self, direction: SearchDirection) -> bool { let invalid_index = match direction { SearchDirection::Backward => usize::MAX, SearchDirection::Forward => 0, - _ => unreachable!(), }; if self.current_index == invalid_index { @@ -2008,7 +1859,6 @@ pub fn go_to_next_match(&mut self, direction: SearchDirection) -> bool { match direction { SearchDirection::Backward => index += 1, SearchDirection::Forward => index -= 1, - _ => unreachable!(), }; if self.current_index == invalid_index { @@ -2055,10 +1905,6 @@ pub fn current_string(&self) -> &wstr { self.current_item().str() } - fn current_string_ffi(&self) -> UniquePtr { - self.current_string().to_ffi() - } - /// Returns the index of the current history item. pub fn current_index(&self) -> usize { self.current_index @@ -2172,10 +2018,6 @@ pub fn all_paths_are_valid>( true } -fn all_paths_are_valid_ffi(paths: &wcstring_list_ffi_t, ctx: &OperationContext<'_>) -> bool { - all_paths_are_valid(paths.from_ffi(), ctx) -} - /// Sets private mode on. Once in private mode, it cannot be turned off. pub fn start_private_mode(vars: &EnvStack) { vars.set_one(L!("fish_history"), EnvMode::GLOBAL, L!("").to_owned()); @@ -2193,185 +2035,3 @@ pub fn in_private_mode(vars: &dyn Environment) -> bool { /// Whether we're in maximum chaos mode, useful for testing. /// This causes things like locks to fail. pub static CHAOS_MODE: RelaxedAtomicBool = RelaxedAtomicBool::new(false); - -// ======== -// FFI crud -// ======== - -struct ItemIndexes(HashMap); - -impl ItemIndexes { - fn get(&self, index: usize) -> UniquePtr { - self.0 - .get(&index) - .map(|s| s.to_ffi()) - .unwrap_or_else(UniquePtr::null) - } -} - -fn rust_history_item_new( - s: &CxxWString, - when: i64, - ident: u64, - persist_mode: PersistenceMode, -) -> Box { - let s = s.from_ffi(); - let when = if when < 0 { - UNIX_EPOCH - Duration::from_secs(u64::try_from(-when).unwrap()) - } else { - UNIX_EPOCH + Duration::from_secs(u64::try_from(when).unwrap()) - }; - Box::new(HistoryItem::new(s, when, ident, persist_mode)) -} - -pub struct HistorySharedPtr(pub Arc); - -impl HistorySharedPtr { - fn is_default(&self) -> bool { - self.0.is_default() - } - fn is_empty(&self) -> bool { - self.0.is_empty() - } - fn remove(&self, s: &CxxWString) { - self.0.remove(s.from_ffi()) - } - fn remove_ephemeral_items(&self) { - self.0.remove_ephemeral_items() - } - fn resolve_pending(&self) { - self.0.resolve_pending() - } - fn save(&self) { - self.0.save() - } - #[allow(clippy::too_many_arguments)] - fn search( - &self, - search_type: SearchType, - search_args: &wcstring_list_ffi_t, - show_time_format: &UniquePtr, - max_items: usize, - case_sensitive: bool, - null_terminate: bool, - reverse: bool, - cancel_on_signal: bool, - streams: &mut IoStreams, - ) -> bool { - let show_time_format = if show_time_format.is_null() { - None - } else { - Some(show_time_format.from_ffi().to_string()) - }; - let search_args = search_args.from_ffi(); - let search_args: Vec<&wstr> = search_args.iter().map(|s| s.as_ref()).collect(); - let cancel_checker = move || { - if cancel_on_signal { - signal_check_cancel() != 0 - } else { - false - } - }; - Arc::clone(&self.0).search( - search_type, - &search_args, - show_time_format.as_deref(), - max_items, - case_sensitive, - null_terminate, - reverse, - &{ Box::new(cancel_checker) as _ }, - streams, - ) - } - fn clear(&self) { - self.0.clear() - } - fn clear_session(&self) { - self.0.clear_session() - } - fn populate_from_config_path(&self) { - self.0.populate_from_config_path() - } - fn populate_from_bash(&self, filename: &CxxWString) { - let file = AutoCloseFd::new(wopen_cloexec(&filename.from_ffi(), O_RDONLY, 0)); - if !file.is_valid() { - return; - } - self.0.populate_from_bash(BufReader::new(file)) - } - fn incorporate_external_changes(&self) { - self.0.incorporate_external_changes() - } - fn get_history(&self) -> UniquePtr { - self.0.get_history().to_ffi() - } - fn items_at_indexes(&self, indexes: &[isize]) -> Box { - Box::new(ItemIndexes(self.0.items_at_indexes( - indexes.iter().filter_map(|&n| n.try_into().ok()), - ))) - } - fn item_at_index(&self, idx: usize) -> Box { - Box::new(self.0.item_at_index(idx).unwrap_or_else(|| HistoryItem { - contents: WString::new(), - creation_timestamp: UNIX_EPOCH, - required_paths: vec![], - identifier: 0, - persist_mode: PersistenceMode::Disk, - })) - } - fn size(&self) -> usize { - self.0.size() - } - fn clone(&self) -> Box { - Box::new(Self(Arc::clone(&self.0))) - } -} - -fn history_with_name(name: &CxxWString) -> Box { - Box::new(HistorySharedPtr(History::with_name(&name.from_ffi()))) -} - -fn rust_history_search_new( - hist: &HistorySharedPtr, - s: &CxxWString, - search_type: SearchType, - flags: u32, - starting_index: usize, -) -> Box { - Box::new(HistorySearch::new_with( - Arc::clone(&hist.0), - s.from_ffi(), - search_type, - SearchFlags::from_bits(flags).unwrap(), - starting_index, - )) -} - -fn rust_session_id(vars: &EnvStackRefFFI) -> UniquePtr { - history_session_id(&*vars.0).to_ffi() -} - -fn rust_expand_and_detect_paths( - paths: &wcstring_list_ffi_t, - vars: &EnvStackRefFFI, -) -> UniquePtr { - expand_and_detect_paths(paths.from_ffi(), &*vars.0).to_ffi() -} - -fn rust_all_paths_are_valid(paths: &wcstring_list_ffi_t, ctx: &OperationContext<'_>) -> bool { - all_paths_are_valid(paths.from_ffi(), ctx) -} - -fn start_private_mode_ffi(vars: &EnvStackRefFFI) { - start_private_mode(&vars.0) -} - -fn in_private_mode_ffi(vars: &EnvStackRefFFI) -> bool { - in_private_mode(&*vars.0) -} - -unsafe impl cxx::ExternType for HistoryItem { - type Id = cxx::type_id!("HistoryItem"); - type Kind = cxx::kind::Opaque; -} diff --git a/fish-rust/src/history/file.rs b/fish-rust/src/history/file.rs index 6ff6785a5..c1a839c1b 100644 --- a/fish-rust/src/history/file.rs +++ b/fish-rust/src/history/file.rs @@ -13,14 +13,13 @@ PROT_WRITE, SEEK_END, SEEK_SET, }; +use super::{HistoryItem, PersistenceMode}; use crate::{ common::{str2wcstring, subslice_position, wcs2string}, flog::FLOG, path::{path_get_config_remoteness, DirRemoteness}, }; -use super::{history_ffi::PersistenceMode, HistoryItem}; - /// History file types. #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum HistoryFileType { diff --git a/fish-rust/src/input_common.rs b/fish-rust/src/input_common.rs index 14a46fd65..cf0c9643d 100644 --- a/fish-rust/src/input_common.rs +++ b/fish-rust/src/input_common.rs @@ -14,10 +14,106 @@ use std::sync::atomic::{AtomicUsize, Ordering}; // The range of key codes for inputrc-style keyboard functions. -pub const R_END_INPUT_FUNCTIONS: usize = (ReadlineCmd::ReverseRepeatJump.repr as usize) + 1; +pub const R_END_INPUT_FUNCTIONS: usize = (ReadlineCmd::ReverseRepeatJump as usize) + 1; -// TODO: move CharInputStyle and ReadlineCmd here once they no longer must be exposed to C++. -pub use crate::input_ffi::{CharInputStyle, ReadlineCmd}; +/// Hackish: the input style, which describes how char events (only) are applied to the command +/// line. Note this is set only after applying bindings; it is not set from readb(). +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +pub enum CharInputStyle { + // Insert characters normally. + Normal, + + // Insert characters only if the cursor is not at the beginning. Otherwise, discard them. + NotFirst, +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +#[repr(u8)] +pub enum ReadlineCmd { + BeginningOfLine, + EndOfLine, + ForwardChar, + BackwardChar, + ForwardSingleChar, + ForwardWord, + BackwardWord, + ForwardBigword, + BackwardBigword, + NextdOrForwardWord, + PrevdOrBackwardWord, + HistorySearchBackward, + HistorySearchForward, + HistoryPrefixSearchBackward, + HistoryPrefixSearchForward, + HistoryPager, + HistoryPagerDelete, + DeleteChar, + BackwardDeleteChar, + KillLine, + Yank, + YankPop, + Complete, + CompleteAndSearch, + PagerToggleSearch, + BeginningOfHistory, + EndOfHistory, + BackwardKillLine, + KillWholeLine, + KillInnerLine, + KillWord, + KillBigword, + BackwardKillWord, + BackwardKillPathComponent, + BackwardKillBigword, + HistoryTokenSearchBackward, + HistoryTokenSearchForward, + SelfInsert, + SelfInsertNotFirst, + TransposeChars, + TransposeWords, + UpcaseWord, + DowncaseWord, + CapitalizeWord, + TogglecaseChar, + TogglecaseSelection, + Execute, + BeginningOfBuffer, + EndOfBuffer, + RepaintMode, + Repaint, + ForceRepaint, + UpLine, + DownLine, + SuppressAutosuggestion, + AcceptAutosuggestion, + BeginSelection, + SwapSelectionStartStop, + EndSelection, + KillSelection, + InsertLineUnder, + InsertLineOver, + ForwardJump, + BackwardJump, + ForwardJumpTill, + BackwardJumpTill, + FuncAnd, + FuncOr, + ExpandAbbr, + DeleteOrExit, + Exit, + CancelCommandline, + Cancel, + Undo, + Redo, + BeginUndoGroup, + EndUndoGroup, + RepeatJump, + DisableMouseTracking, + // ncurses uses the obvious name + ClearScreenAndRepaint, + // NOTE: This one has to be last. + ReverseRepeatJump, +} /// Represents an event on the character input stream. #[derive(Debug, Copy, Clone)] diff --git a/fish-rust/src/input_ffi.rs b/fish-rust/src/input_ffi.rs deleted file mode 100644 index 8425ccad5..000000000 --- a/fish-rust/src/input_ffi.rs +++ /dev/null @@ -1,267 +0,0 @@ -use crate::ffi::wcstring_list_ffi_t; -use crate::input::*; -use crate::input_common::*; -use crate::parser::ParserRefFFI; -use crate::threads::CppCallback; -use crate::wchar::prelude::*; -use crate::wchar_ffi::AsWstr; -use crate::wchar_ffi::WCharToFFI; -use cxx::CxxWString; -pub use ffi::{CharInputStyle, ReadlineCmd}; -use std::pin::Pin; - -// Returns the code, or -1 on failure. -fn input_function_get_code_ffi(name: &CxxWString) -> i32 { - if let Some(code) = input_function_get_code(name.as_wstr()) { - code.repr as i32 - } else { - -1 - } -} - -fn char_event_from_readline_ffi(cmd: ReadlineCmd) -> Box { - Box::new(CharEvent::from_readline(cmd)) -} - -fn char_event_from_char_ffi(c: u8) -> Box { - Box::new(CharEvent::from_char(c.into())) -} - -fn make_inputter_ffi(parser: &ParserRefFFI, in_fd: i32) -> Box { - Box::new(Inputter::new(parser.0.clone(), in_fd)) -} - -fn make_input_event_queue_ffi(in_fd: i32) -> Box { - Box::new(InputEventQueue::new(in_fd)) -} - -fn input_terminfo_get_name_ffi(seq: &CxxWString, out: Pin<&mut CxxWString>) -> bool { - let Some(name) = input_terminfo_get_name(seq.as_wstr()) else { - return false; - }; - out.push_chars(name.as_char_slice()); - true -} - -impl Inputter { - #[allow(clippy::boxed_local)] - fn queue_char_ffi(&mut self, ch: Box) { - self.queue_char(*ch); - } - - fn read_char_ffi(&mut self, command_handler: &cxx::SharedPtr) -> Box { - let mut rust_handler = |cmds: &[WString]| { - let ffi_cmds = cmds.to_ffi(); - command_handler.invoke_with_param(ffi_cmds.as_ref().unwrap() as *const _ as *const u8); - }; - let mhandler = if !command_handler.is_null() { - Some(&mut rust_handler as &mut CommandHandler) - } else { - None - }; - Box::new(self.read_char(mhandler)) - } - - fn function_pop_arg_ffi(&mut self) -> u32 { - self.function_pop_arg().into() - } -} - -impl CharEvent { - fn get_char_ffi(&self) -> u32 { - self.get_char().into() - } - - fn get_input_style_ffi(&self) -> CharInputStyle { - self.input_style - } -} - -impl InputEventQueue { - // Returns Box::into_raw(), or nullptr if None. - fn readch_timed_esc_ffi(&mut self) -> *mut CharEvent { - match self.readch_timed_esc() { - Some(ch) => Box::into_raw(Box::new(ch)), - None => std::ptr::null_mut(), - } - } -} - -#[cxx::bridge] -mod ffi { - /// Hackish: the input style, which describes how char events (only) are applied to the command - /// line. Note this is set only after applying bindings; it is not set from readb(). - #[cxx_name = "char_input_style_t"] - #[repr(u8)] - #[derive(Debug, Copy, Clone)] - pub enum CharInputStyle { - // Insert characters normally. - Normal, - - // Insert characters only if the cursor is not at the beginning. Otherwise, discard them. - NotFirst, - } - - #[cxx_name = "readline_cmd_t"] - #[repr(u8)] - #[derive(Debug, Copy, Clone, PartialEq, Eq)] - pub enum ReadlineCmd { - BeginningOfLine, - EndOfLine, - ForwardChar, - BackwardChar, - ForwardSingleChar, - ForwardWord, - BackwardWord, - ForwardBigword, - BackwardBigword, - NextdOrForwardWord, - PrevdOrBackwardWord, - HistorySearchBackward, - HistorySearchForward, - HistoryPrefixSearchBackward, - HistoryPrefixSearchForward, - HistoryPager, - HistoryPagerDelete, - DeleteChar, - BackwardDeleteChar, - KillLine, - Yank, - YankPop, - Complete, - CompleteAndSearch, - PagerToggleSearch, - BeginningOfHistory, - EndOfHistory, - BackwardKillLine, - KillWholeLine, - KillInnerLine, - KillWord, - KillBigword, - BackwardKillWord, - BackwardKillPathComponent, - BackwardKillBigword, - HistoryTokenSearchBackward, - HistoryTokenSearchForward, - SelfInsert, - SelfInsertNotFirst, - TransposeChars, - TransposeWords, - UpcaseWord, - DowncaseWord, - CapitalizeWord, - TogglecaseChar, - TogglecaseSelection, - Execute, - BeginningOfBuffer, - EndOfBuffer, - RepaintMode, - Repaint, - ForceRepaint, - UpLine, - DownLine, - SuppressAutosuggestion, - AcceptAutosuggestion, - BeginSelection, - SwapSelectionStartStop, - EndSelection, - KillSelection, - InsertLineUnder, - InsertLineOver, - ForwardJump, - BackwardJump, - ForwardJumpTill, - BackwardJumpTill, - FuncAnd, - FuncOr, - ExpandAbbr, - DeleteOrExit, - Exit, - CancelCommandline, - Cancel, - Undo, - Redo, - BeginUndoGroup, - EndUndoGroup, - RepeatJump, - DisableMouseTracking, - // ncurses uses the obvious name - ClearScreenAndRepaint, - // NOTE: This one has to be last. - ReverseRepeatJump, - } - - extern "C++" { - include!("parser.h"); - include!("reader.h"); - include!("callback.h"); - type wcstring_list_ffi_t = super::wcstring_list_ffi_t; - type ParserRef = crate::parser::ParserRefFFI; - - #[rust_name = "CppCallback"] - type callback_t = crate::threads::CppCallback; - } - - extern "Rust" { - fn init_input(); - - #[cxx_name = "input_function_get_code"] - fn input_function_get_code_ffi(name: &CxxWString) -> i32; - - #[cxx_name = "input_terminfo_get_name"] - fn input_terminfo_get_name_ffi(seq: &CxxWString, out: Pin<&mut CxxWString>) -> bool; - } - - extern "Rust" { - type CharEvent; - - #[cxx_name = "char_event_from_readline"] - fn char_event_from_readline_ffi(cmd: ReadlineCmd) -> Box; - - #[cxx_name = "char_event_from_char"] - fn char_event_from_char_ffi(c: u8) -> Box; - - fn is_char(&self) -> bool; - fn is_readline(&self) -> bool; - fn is_check_exit(&self) -> bool; - fn is_eof(&self) -> bool; - - #[cxx_name = "get_char"] - fn get_char_ffi(&self) -> u32; - - #[cxx_name = "get_input_style"] - fn get_input_style_ffi(&self) -> CharInputStyle; - - fn get_readline(&self) -> ReadlineCmd; - } - - extern "Rust" { - type Inputter; - - #[cxx_name = "make_inputter"] - fn make_inputter_ffi(parser: &ParserRef, in_fd: i32) -> Box; - - #[cxx_name = "queue_char"] - fn queue_char_ffi(&mut self, ch: Box); - - fn queue_readline(&mut self, cmd: ReadlineCmd); - - #[cxx_name = "read_char"] - fn read_char_ffi(&mut self, command_handler: &SharedPtr) -> Box; - - fn function_set_status(&mut self, status: bool); - - #[cxx_name = "function_pop_arg"] - fn function_pop_arg_ffi(&mut self) -> u32; - } - - extern "Rust" { - type InputEventQueue; - - #[cxx_name = "make_input_event_queue"] - fn make_input_event_queue_ffi(in_fd: i32) -> Box; - - #[cxx_name = "readch_timed_esc"] - fn readch_timed_esc_ffi(&mut self) -> *mut CharEvent; - } -} diff --git a/fish-rust/src/io.rs b/fish-rust/src/io.rs index ac1b469e2..329e17742 100644 --- a/fish-rust/src/io.rs +++ b/fish-rust/src/io.rs @@ -15,9 +15,7 @@ use crate::signal::SigChecker; use crate::topic_monitor::topic_t; use crate::wchar::prelude::*; -use crate::wchar_ffi::WCharFromFFI; use crate::wutil::{perror, perror_io, wdirname, wstat, wwrite_to_fd}; -use cxx::CxxWString; use errno::Errno; use libc::{EAGAIN, EEXIST, EINTR, ENOENT, ENOTDIR, EPIPE, EWOULDBLOCK, O_EXCL, STDOUT_FILENO}; use std::cell::{RefCell, UnsafeCell}; @@ -618,11 +616,6 @@ fn begin_filling(iobuffer: &Arc, fd: AutoCloseFd) { #[derive(Clone, Default)] pub struct IoChain(pub Vec); -unsafe impl cxx::ExternType for IoChain { - type Id = cxx::type_id!("IoChain"); - type Kind = cxx::kind::Opaque; -} - impl IoChain { pub fn new() -> Self { Default::default() @@ -981,11 +974,6 @@ pub struct IoStreams<'a> { pub job_group: Option, } -unsafe impl cxx::ExternType for IoStreams<'_> { - type Id = cxx::type_id!("IoStreams"); - type Kind = cxx::kind::Opaque; -} - impl<'a> IoStreams<'a> { pub fn new(out: &'a mut OutputStream, err: &'a mut OutputStream) -> Self { IoStreams { @@ -1025,69 +1013,3 @@ fn fd_monitor() -> &'static mut FdMonitor { let ptr: *mut FdMonitor = unsafe { (*FDM).get() }; unsafe { &mut *ptr } } - -#[cxx::bridge] -#[allow(clippy::needless_lifetimes)] -mod io_ffi { - extern "Rust" { - type IoChain; - type IoStreams<'a>; - type OutputStreamFfi<'a>; - - fn new_io_chain() -> Box; - - #[cxx_name = "out"] - unsafe fn out_ffi<'a>(self: &'a mut IoStreams<'a>) -> Box>; - #[cxx_name = "err"] - unsafe fn err_ffi<'a>(self: &'a mut IoStreams<'a>) -> Box>; - #[cxx_name = "out_is_redirected"] - unsafe fn out_is_redirected_ffi<'a>(self: &IoStreams<'a>) -> bool; - #[cxx_name = "stdin_is_directly_redirected"] - unsafe fn stdin_is_directly_redirected_ffi<'a>(self: &IoStreams<'a>) -> bool; - #[cxx_name = "stdin_fd"] - unsafe fn stdin_fd_ffi<'a>(self: &IoStreams<'a>) -> i32; - - #[cxx_name = "append"] - unsafe fn append_ffi<'a>(self: &mut OutputStreamFfi<'a>, s: &CxxWString) -> bool; - #[cxx_name = "push"] - unsafe fn push_ffi<'a>(self: &mut OutputStreamFfi<'a>, s: u32) -> bool; - } -} - -impl<'a> IoStreams<'a> { - fn out_ffi(&'a mut self) -> Box> { - Box::new(OutputStreamFfi(self.out)) - } - fn err_ffi(&'a mut self) -> Box> { - Box::new(OutputStreamFfi(self.err)) - } - unsafe fn out_is_redirected_ffi(&self) -> bool { - self.out_is_redirected - } - unsafe fn stdin_is_directly_redirected_ffi(&self) -> bool { - self.stdin_is_directly_redirected - } - unsafe fn stdin_fd_ffi(&self) -> i32 { - self.stdin_fd - } -} - -pub struct OutputStreamFfi<'a>(pub &'a mut OutputStream); - -unsafe impl cxx::ExternType for OutputStreamFfi<'_> { - type Id = cxx::type_id!("OutputStreamFfi"); - type Kind = cxx::kind::Opaque; -} - -impl<'a> OutputStreamFfi<'a> { - fn append_ffi(&mut self, s: &CxxWString) -> bool { - self.0.append(s.from_ffi()) - } - fn push_ffi(&mut self, s: u32) -> bool { - self.0.append_char(char::from_u32(s).unwrap()) - } -} - -fn new_io_chain() -> Box { - Box::new(IoChain::new()) -} diff --git a/fish-rust/src/job_group.rs b/fish-rust/src/job_group.rs index 7fbad0d1c..5055999c3 100644 --- a/fish-rust/src/job_group.rs +++ b/fish-rust/src/job_group.rs @@ -1,51 +1,12 @@ -use self::ffi::pgid_t; use crate::global_safety::RelaxedAtomicBool; use crate::proc::JobGroupRef; use crate::signal::Signal; use crate::wchar::prelude::*; -use crate::wchar_ffi::WCharToFFI; -use cxx::{CxxWString, UniquePtr}; use std::cell::RefCell; use std::num::NonZeroU32; use std::sync::atomic::{AtomicI32, Ordering}; use std::sync::{Arc, Mutex}; -#[cxx::bridge] -mod ffi { - // Not only does cxx bridge not recognize libc::pid_t, it doesn't even recognize i32 as a POD - // type! :sadface: - struct pgid_t { - value: i32, - } - - extern "Rust" { - #[cxx_name = "job_group_t"] - type JobGroup; - - fn wants_job_control(&self) -> bool; - fn wants_terminal(&self) -> bool; - fn is_foreground(&self) -> bool; - fn set_is_foreground(&self, value: bool); - #[cxx_name = "get_command"] - fn get_command_ffi(&self) -> UniquePtr; - #[cxx_name = "get_job_id"] - fn get_job_id_ffi(&self) -> i32; - #[cxx_name = "get_cancel_signal"] - fn get_cancel_signal_ffi(&self) -> i32; - #[cxx_name = "cancel_with_signal"] - fn cancel_with_signal_ffi(&self, signal: i32); - fn set_pgid(&mut self, pgid: i32); - #[cxx_name = "get_pgid"] - fn get_pgid_ffi(&self) -> UniquePtr; - fn has_job_id(&self) -> bool; - - // cxx bridge doesn't recognize `libc::*` as being POD types, so it won't let us use them in - // a SharedPtr/UniquePtr/Box and won't let us pass/return them by value/reference, either. - unsafe fn get_modes_ffi(&self, size: usize) -> *const u8; /* actually `* const libc::termios` */ - unsafe fn set_modes_ffi(&mut self, modes: *const u8, size: usize); /* actually `* const libc::termios` */ - } -} - /// A job id, corresponding to what is printed by `jobs`. 1 is the first valid job id. #[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)] #[repr(transparent)] @@ -150,16 +111,6 @@ pub fn set_is_foreground(&self, in_foreground: bool) { self.is_foreground.store(in_foreground); } - /// Return the command which produced this job tree. - pub fn get_command_ffi(&self) -> UniquePtr { - self.command.to_ffi() - } - - /// Return the job id or -1 if none. - pub fn get_job_id_ffi(&self) -> i32 { - self.job_id.map(|j| u32::from(j.0) as i32).unwrap_or(-1) - } - /// Returns whether we have valid job id. "Simple block" groups like function calls do not. pub fn has_job_id(&self) -> bool { self.job_id.is_some() @@ -173,12 +124,6 @@ pub fn get_cancel_signal(&self) -> Option { } } - /// Gets the cancellation signal or `0` if none. - pub fn get_cancel_signal_ffi(&self) -> i32 { - // Legacy C++ code expects a zero in case of no signal. - self.get_cancel_signal().map(|s| s.code()).unwrap_or(0) - } - /// Mark that a process in this group got a signal and should cancel. pub fn cancel_with_signal(&self, signal: Signal) { // We only assign the signal if one hasn't yet been assigned. This means the first signal to @@ -188,11 +133,6 @@ pub fn cancel_with_signal(&self, signal: Signal) { .ok(); } - /// Mark that a process in this group got a signal and should cancel - pub fn cancel_with_signal_ffi(&self, signal: i32) { - self.cancel_with_signal(Signal::new(signal)) - } - /// Set the pgid for this job group, latching it to this value. This should only be called if /// job control is active for this group. The pgid should not already have been set, and should /// be different from fish's pgid. Of course this does not keep the pgid alive by itself. @@ -218,53 +158,6 @@ pub fn set_pgid(&self, pgid: libc::pid_t) { pub fn get_pgid(&self) -> Option { *self.pgid.borrow() } - - /// Returns the value of [`JobGroup::pgid`] in a `UniquePtr` to take the place of an - /// `Option` for ffi purposes. A null `UniquePtr` is equivalent to `None`. - pub fn get_pgid_ffi(&self) -> cxx::UniquePtr { - match *self.pgid.borrow() { - Some(value) => UniquePtr::new(pgid_t { value }), - None => UniquePtr::null(), - } - } - - /// Returns the current terminal modes associated with the `JobGroup` for ffi purposes. - unsafe fn get_modes_ffi(&self, size: usize) -> *const u8 { - assert_eq!( - size, - core::mem::size_of::(), - "Mismatch between expected and actual ffi size of struct termios!" - ); - - self.tmodes - .borrow() - .as_ref() - // Really cool that type inference works twice in a row here. The first `_` is deduced - // from the left and the second `_` is deduced from the right (the return type). - .map(|val| val as *const _ as *const _) - .unwrap_or(core::ptr::null()) - } - - /// Sets the current terminal modes associated with the `JobGroup`. Only use for ffi. - /// - /// Unlike `set_pgid()`, this isn't documented in the C++ codebase as being only called at - /// initialization but as the underlying [`self.tmodes`] wasn't wrapped in any sort of - /// thread-safe marshalling struct, we'll assume it can only be called from one thread and use - /// `&mut self` for safety. - unsafe fn set_modes_ffi(&mut self, modes: *const u8, size: usize) { - assert_eq!( - size, - core::mem::size_of::(), - "Mismatch between expected and actual ffi size of struct termios!" - ); - - let modes = modes as *const libc::termios; - if modes.is_null() { - self.tmodes.replace(None); - } else { - self.tmodes.replace(Some(*modes)); - } - } } /// Basic thread-safe sorted vector of job ids currently in use. diff --git a/fish-rust/src/kill.rs b/fish-rust/src/kill.rs index ad0492f3a..c0a52dd69 100644 --- a/fish-rust/src/kill.rs +++ b/fish-rust/src/kill.rs @@ -3,33 +3,11 @@ //! Works like the killring in emacs and readline. The killring is cut and paste with a memory of //! previous cuts. -use cxx::{CxxWString, UniquePtr}; use once_cell::sync::Lazy; use std::collections::VecDeque; use std::sync::Mutex; -use crate::ffi::wcstring_list_ffi_t; use crate::wchar::prelude::*; -use crate::wchar_ffi::{AsWstr, WCharFromFFI, WCharToFFI}; - -#[cxx::bridge] -mod kill_ffi { - extern "C++" { - include!("wutil.h"); - type wcstring_list_ffi_t = super::wcstring_list_ffi_t; - } - - extern "Rust" { - #[cxx_name = "kill_add"] - fn kill_add_ffi(new_entry: &CxxWString); - #[cxx_name = "kill_replace"] - fn kill_replace_ffi(old_entry: &CxxWString, new_entry: &CxxWString); - #[cxx_name = "kill_yank_rotate"] - fn kill_yank_rotate_ffi() -> UniquePtr; - #[cxx_name = "kill_yank"] - fn kill_yank_ffi() -> UniquePtr; - } -} struct KillRing(VecDeque); @@ -104,22 +82,6 @@ pub fn kill_entries() -> Vec { KILL_RING.lock().unwrap().entries() } -fn kill_add_ffi(new_entry: &CxxWString) { - kill_add(new_entry.from_ffi()); -} - -fn kill_replace_ffi(old_entry: &CxxWString, new_entry: &CxxWString) { - kill_replace(old_entry.as_wstr(), new_entry.from_ffi()) -} - -fn kill_yank_ffi() -> UniquePtr { - kill_yank().to_ffi() -} - -fn kill_yank_rotate_ffi() -> UniquePtr { - kill_yank_rotate().to_ffi() -} - #[cfg(test)] fn test_killring() { let mut kr = KillRing::new(); diff --git a/fish-rust/src/lib.rs b/fish-rust/src/lib.rs index 82f5075da..3f70b1de7 100644 --- a/fish-rust/src/lib.rs +++ b/fish-rust/src/lib.rs @@ -50,12 +50,6 @@ mod fd_monitor; mod fd_readable_set; mod fds; -#[allow(rustdoc::broken_intra_doc_links)] -#[allow(clippy::module_inception)] -#[allow(clippy::new_ret_no_self)] -#[allow(clippy::wrong_self_convention)] -#[allow(clippy::needless_lifetimes)] -#[allow(unused_imports)] mod ffi; mod ffi_init; mod fish; @@ -71,7 +65,6 @@ mod history; mod input; mod input_common; -mod input_ffi; mod io; mod job_group; mod kill; @@ -109,7 +102,6 @@ mod wait_handle; mod wchar; mod wchar_ext; -mod wchar_ffi; mod wcstringutil; mod wgetopt; mod widecharwidth; diff --git a/fish-rust/src/null_terminated_array.rs b/fish-rust/src/null_terminated_array.rs index 5b9f6690b..b54820431 100644 --- a/fish-rust/src/null_terminated_array.rs +++ b/fish-rust/src/null_terminated_array.rs @@ -139,15 +139,6 @@ pub fn null_terminated_array_length(mut arr: *const *const T) -> usize { len } -/// Convert a CxxString to a CString, truncating at the first NUL. -use cxx::CxxString; -fn cxxstring_to_cstring(s: &CxxString) -> CString { - let bytes: &[u8] = s.as_bytes(); - let nul_pos = bytes.iter().position(|&b| b == 0); - let slice = &bytes[..nul_pos.unwrap_or(bytes.len())]; - CString::new(slice).unwrap() -} - #[test] fn test_null_terminated_array_length() { let arr = [&1, &2, &3, std::ptr::null()]; diff --git a/fish-rust/src/operation_context.rs b/fish-rust/src/operation_context.rs index 2d72fe490..a60a35661 100644 --- a/fish-rust/src/operation_context.rs +++ b/fish-rust/src/operation_context.rs @@ -1,5 +1,5 @@ use crate::common::CancelChecker; -use crate::env::{EnvDyn, EnvDynFFI}; +use crate::env::EnvDyn; use crate::env::{EnvStack, EnvStackRef, Environment}; use crate::parser::{Parser, ParserRef}; use crate::proc::JobGroupRef; @@ -156,52 +156,3 @@ pub fn get_bg_context(env: &EnvDyn, generation_count: u32) -> OperationContext { EXPANSION_LIMIT_BACKGROUND, ) } - -#[cxx::bridge] -#[allow(clippy::needless_lifetimes)] -mod operation_context_ffi { - extern "C++" { - include!("env_fwd.h"); - include!("parser.h"); - #[cxx_name = "EnvStackRef"] - type EnvStackRefFFI = crate::env::EnvStackRefFFI; - #[cxx_name = "EnvDyn"] - type EnvDynFFI = crate::env::EnvDynFFI; - type Parser = crate::parser::Parser; - } - extern "Rust" { - type OperationContext<'a>; - - fn empty_operation_context() -> Box>; - fn operation_context_globals() -> Box>; - fn check_cancel(&self) -> bool; - - #[cxx_name = "get_bg_context"] - unsafe fn get_bg_context_ffi( - env: &EnvDynFFI, - generation_count: u32, - ) -> Box>; - #[cxx_name = "parser_context"] - fn parser_context_ffi(parser: &Parser) -> Box>; - } -} - -fn get_bg_context_ffi(env: &EnvDynFFI, generation_count: u32) -> Box> { - Box::new(get_bg_context(&env.0, generation_count)) -} - -fn parser_context_ffi(parser: &Parser) -> Box> { - Box::new(parser.context()) -} - -unsafe impl cxx::ExternType for OperationContext<'_> { - type Id = cxx::type_id!("OperationContext"); - type Kind = cxx::kind::Opaque; -} - -fn empty_operation_context() -> Box> { - Box::new(OperationContext::empty()) -} -fn operation_context_globals() -> Box> { - Box::new(OperationContext::globals()) -} diff --git a/fish-rust/src/output.rs b/fish-rust/src/output.rs index 3f0a488fe..544ff3073 100644 --- a/fish-rust/src/output.rs +++ b/fish-rust/src/output.rs @@ -23,17 +23,6 @@ pub struct ColorSupport: u8 { /// Whether term256 and term24bit are supported. static COLOR_SUPPORT: AtomicU8 = AtomicU8::new(0); -/// FFI bits. -#[no_mangle] -extern "C" fn output_get_color_support() -> u8 { - COLOR_SUPPORT.load(Ordering::Relaxed) -} - -#[no_mangle] -extern "C" fn output_set_color_support(val: u8) { - COLOR_SUPPORT.store(val, Ordering::Relaxed); -} - /// Returns true if we think tparm can handle outputting a color index. fn term_supports_color_natively(term: &Term, c: u8) -> bool { #[allow(clippy::int_plus_one)] @@ -592,91 +581,3 @@ pub fn parse_color(var: &EnvVar, is_background: bool) -> RgbColor { result.set_reverse(is_reverse); result } - -/// FFI junk. -fn stdoutput_ffi() -> &'static mut Outputter { - // TODO: this is bogus because it avoids RefCell's check, but is temporary for FFI purposes. - unsafe { &mut *Outputter::stdoutput().as_ptr() } -} - -/// Make an outputter which outputs to its string. -fn make_buffering_outputter_ffi() -> Box { - Box::new(Outputter::new_buffering()) -} - -pub type RgbColorFFI = crate::ffi::rgb_color_t; -use crate::wchar_ffi::AsWstr; -impl Outputter { - fn set_color_ffi(&mut self, fg: &RgbColorFFI, bg: &RgbColorFFI) { - self.set_color(fg.from_ffi(), bg.from_ffi()); - } - - fn writech_ffi(&mut self, ch: crate::ffi::wchar_t) { - self.writech(char::from_u32(ch).expect("Invalid wchar")); - } - - // Write a nul-terminated string. - // We accept CxxString because it prevents needing to do typecasts at the call site, - // as it's unclear what Cxx type corresponds to const char *. - // We are unconcerned with interior nul-bytes: none of the termcap sequences contain them - // for obvious reasons. - fn writembs_ffi(&mut self, mbs: &cxx::CxxString) { - let mbs = unsafe { CStr::from_ptr(mbs.as_ptr() as *const std::ffi::c_char) }; - self.tputs(mbs); - } - - fn writestr_ffi(&mut self, str: crate::ffi::wcharz_t) { - self.write_wstr(str.as_wstr()); - } - - fn write_color_ffi(&mut self, color: &RgbColorFFI, is_fg: bool) -> bool { - self.write_color(color.from_ffi(), is_fg) - } -} - -#[cxx::bridge] -mod ffi { - extern "C++" { - include!("color.h"); - include!("wutil.h"); - - #[cxx_name = "rgb_color_t"] - type RgbColorFFI = super::RgbColorFFI; - - type wcharz_t = crate::ffi::wcharz_t; - } - - extern "Rust" { - #[cxx_name = "outputter_t"] - type Outputter; - - #[cxx_name = "make_buffering_outputter"] - fn make_buffering_outputter_ffi() -> Box; - - #[cxx_name = "stdoutput"] - fn stdoutput_ffi() -> &'static mut Outputter; - - #[cxx_name = "set_color"] - fn set_color_ffi(&mut self, fg: &RgbColorFFI, bg: &RgbColorFFI); - - #[cxx_name = "writech"] - fn writech_ffi(&mut self, ch: wchar_t); - - #[cxx_name = "writestr"] - fn writestr_ffi(&mut self, str: wcharz_t); - - #[cxx_name = "writembs"] - fn writembs_ffi(&mut self, mbs: &CxxString); - - #[cxx_name = "write_color"] - fn write_color_ffi(&mut self, color: &RgbColorFFI, is_fg: bool) -> bool; - - // These do not need separate FFI variants. - fn contents(&self) -> &[u8]; - fn begin_buffering(&mut self); - fn end_buffering(&mut self); - - #[cxx_name = "push_back"] - fn push(&mut self, ch: u8); - } -} diff --git a/fish-rust/src/pager.rs b/fish-rust/src/pager.rs index 391d51a2a..441efa046 100644 --- a/fish-rust/src/pager.rs +++ b/fish-rust/src/pager.rs @@ -1,6 +1,5 @@ //! Pager support. -use cxx::{CxxWString, UniquePtr}; use std::collections::hash_map::Entry; use std::collections::HashMap; @@ -8,7 +7,7 @@ escape_string, get_ellipsis_char, get_ellipsis_str, EscapeFlags, EscapeStringStyle, }; use crate::compat::MB_CUR_MAX; -use crate::complete::{Completion, CompletionListFfi}; +use crate::complete::Completion; use crate::editable_line::EditableLine; use crate::fallback::{fish_wcswidth, fish_wcwidth}; #[allow(unused_imports)] @@ -18,7 +17,6 @@ use crate::screen::{Line, ScreenData}; use crate::termsize::Termsize; use crate::wchar::prelude::*; -use crate::wchar_ffi::{AsWstr, WCharFromFFI, WCharToFFI}; use crate::wcstringutil::string_fuzzy_match_string; /// Represents rendering from the pager. @@ -485,17 +483,16 @@ fn completion_print_item( assert!(comp_width <= width); } - let modify_role = |mut role: HighlightRole| { - let mut base = role.repr; + let modify_role = |role: HighlightRole| { + let mut base = role as u8; if selected { - base += HighlightRole::pager_selected_background.repr - - HighlightRole::pager_background.repr; + base += HighlightRole::pager_selected_background as u8 + - HighlightRole::pager_background as u8; } else if secondary { - base += HighlightRole::pager_secondary_background.repr - - HighlightRole::pager_background.repr; + base += HighlightRole::pager_secondary_background as u8 + - HighlightRole::pager_background as u8; } - role.repr = base; - role + unsafe { std::mem::transmute(base) } }; let bg_role = modify_role(HighlightRole::pager_background); @@ -1219,153 +1216,14 @@ fn process_completions_into_infos(lst: &[Completion]) -> Vec { result } -#[cxx::bridge] -mod pager_ffi { - extern "C++" { - include!("complete.h"); - include!("editable_line.h"); - type CompletionListFfi = crate::complete::CompletionListFfi; - type Completion = crate::complete::Completion; - type EditableLine = crate::editable_line::EditableLine; - } - enum selection_motion_t { - north, - east, - south, - west, - page_north, - page_south, - next, - prev, - deselect, - } - extern "Rust" { - type PageRendering; - fn new_page_rendering() -> Box; - fn remaining_to_disclose(&self) -> usize; - } - extern "Rust" { - type Pager; - fn new_pager() -> Box; - fn clear(&mut self); - fn cursor_position(&self) -> usize; - fn empty(&self) -> bool; - fn extra_progress_text(&self) -> UniquePtr; - fn set_extra_progress_text(&mut self, text: &CxxWString); - fn is_navigating_contents(&self) -> bool; - fn is_search_field_shown(&self) -> bool; - fn refilter_completions(&mut self); - fn rendering_needs_update(&self, rendering: &PageRendering) -> bool; - fn search_field_line(&mut self) -> *mut EditableLine; - #[cxx_name = "set_completions"] - fn set_completions_ffi(&mut self, completions: &CompletionListFfi, enable_refilter: bool); - fn set_fully_disclosed(&mut self); - #[cxx_name = "set_prefix"] - fn set_prefix_ffi(&mut self, prefix: &CxxWString, highlight: bool); - fn set_search_field_shown(&mut self, flag: bool); - #[cxx_name = "selected_completion"] - fn selected_completion_ffi(&self, rendering: &PageRendering) -> *const Completion; - #[cxx_name = "get_selected_row"] - fn get_selected_row_ffi(&self, rendering: &PageRendering) -> usize; - #[cxx_name = "get_selected_column"] - fn get_selected_column_ffi(&self, rendering: &PageRendering) -> usize; - #[cxx_name = "select_next_completion_in_direction"] - fn select_next_completion_in_direction_ffi( - &mut self, - direction: selection_motion_t, - rendering: &PageRendering, - ) -> bool; - #[cxx_name = "selected_completion_index"] - fn selected_completion_index_ffi(&self) -> usize; - #[cxx_name = "set_selected_completion_index"] - fn set_selected_completion_index_ffi(&mut self, new_index: usize); - } -} -fn new_page_rendering() -> Box { - Box::default() -} -impl PageRendering { - fn remaining_to_disclose(&self) -> usize { - self.remaining_to_disclose - } -} -fn new_pager() -> Box { - Box::default() -} -impl Pager { - fn select_next_completion_in_direction_ffi( - &mut self, - direction: selection_motion_t, - rendering: &PageRendering, - ) -> bool { - self.select_next_completion_in_direction(direction.from_ffi(), rendering) - } - fn selected_completion_ffi(&self, rendering: &PageRendering) -> *const Completion { - match self.selected_completion(rendering) { - Some(completion) => completion as *const Completion, - None => std::ptr::null(), - } - } - fn set_prefix_ffi(&mut self, prefix: &CxxWString, highlight: bool) { - self.set_prefix(prefix.as_wstr(), highlight); - } - fn search_field_line(&mut self) -> *mut EditableLine { - &mut self.search_field_line as *mut EditableLine - } - fn empty(&self) -> bool { - self.is_empty() - } - fn extra_progress_text(&self) -> UniquePtr { - self.extra_progress_text.to_ffi() - } - fn set_extra_progress_text(&mut self, text: &CxxWString) { - self.extra_progress_text = text.from_ffi(); - } - fn set_completions_ffi(&mut self, completions: &CompletionListFfi, enable_refilter: bool) { - self.set_completions(&completions.0, enable_refilter) - } - fn selected_completion_index_ffi(&self) -> usize { - self.selected_completion_idx.unwrap_or(PAGER_SELECTION_NONE) - } - fn set_selected_completion_index_ffi(&mut self, new_index: usize) { - self.set_selected_completion_index(if new_index == PAGER_SELECTION_NONE { - None - } else { - Some(new_index) - }); - } - fn get_selected_row_ffi(&self, rendering: &PageRendering) -> usize { - self.get_selected_row(rendering) - .unwrap_or(PAGER_SELECTION_NONE) - } - fn get_selected_column_ffi(&self, rendering: &PageRendering) -> usize { - self.get_selected_column(rendering) - .unwrap_or(PAGER_SELECTION_NONE) - } -} -use pager_ffi::selection_motion_t; -impl selection_motion_t { - #[allow(clippy::wrong_self_convention)] - fn from_ffi(self) -> SelectionMotion { - match self { - selection_motion_t::north => SelectionMotion::North, - selection_motion_t::east => SelectionMotion::East, - selection_motion_t::south => SelectionMotion::South, - selection_motion_t::west => SelectionMotion::West, - selection_motion_t::page_north => SelectionMotion::PageNorth, - selection_motion_t::page_south => SelectionMotion::PageSouth, - selection_motion_t::next => SelectionMotion::Next, - selection_motion_t::prev => SelectionMotion::Prev, - selection_motion_t::deselect => SelectionMotion::Deselect, - _ => unreachable!(), - } - } -} -unsafe impl cxx::ExternType for Pager { - type Id = cxx::type_id!("Pager"); - type Kind = cxx::kind::Opaque; -} -unsafe impl cxx::ExternType for PageRendering { - type Id = cxx::type_id!("PageRendering"); - type Kind = cxx::kind::Opaque; +pub enum selection_motion_t { + north, + east, + south, + west, + page_north, + page_south, + next, + prev, + deselect, } diff --git a/fish-rust/src/parse_constants.rs b/fish-rust/src/parse_constants.rs index d046d45ff..a7492e09e 100644 --- a/fish-rust/src/parse_constants.rs +++ b/fish-rust/src/parse_constants.rs @@ -1,12 +1,8 @@ //! Constants used in the programmatic representation of fish code. use crate::fallback::{fish_wcswidth, fish_wcwidth}; -use crate::ffi::wcharz_t; use crate::wchar::prelude::*; -use crate::wchar_ffi::{AsWstr, WCharFromFFI, WCharToFFI}; use bitflags::bitflags; -use cxx::{type_id, ExternType}; -use cxx::{CxxWString, UniquePtr}; pub type SourceOffset = u32; @@ -41,168 +37,100 @@ pub struct ParserTestErrorBits: u8 { } } -#[cxx::bridge] -mod parse_constants_ffi { - extern "C++" { - include!("wutil.h"); - type wcharz_t = super::wcharz_t; - } - - /// A range of source code. - #[derive(PartialEq, Eq, Clone, Copy, Debug)] - pub struct SourceRange { - start: u32, - length: u32, - } - - extern "Rust" { - #[cxx_name = "end"] - fn end_ffi(self: &SourceRange) -> u32; - #[cxx_name = "contains_inclusive"] - fn contains_inclusive_ffi(self: &SourceRange, loc: u32) -> bool; - } - - #[derive(Clone, Copy, Debug)] - pub enum ParseTokenType { - invalid = 1, - - // Terminal types. - string, - pipe, - redirection, - background, - andand, - oror, - end, - // Special terminal type that means no more tokens forthcoming. - terminate, - // Very special terminal types that don't appear in the production list. - error, - tokenizer_error, - comment, - } - - #[repr(u8)] - #[derive(Clone, Copy, Debug)] - pub enum ParseKeyword { - // 'none' is not a keyword, it is a sentinel indicating nothing. - none, - - kw_and, - kw_begin, - kw_builtin, - kw_case, - kw_command, - kw_else, - kw_end, - kw_exclam, - kw_exec, - kw_for, - kw_function, - kw_if, - kw_in, - kw_not, - kw_or, - kw_switch, - kw_time, - kw_while, - } - - // Statement decorations like 'command' or 'exec'. - #[derive(Clone, Copy, Debug, Eq, PartialEq)] - pub enum StatementDecoration { - none, - command, - builtin, - exec, - } - - // Parse error code list. - #[derive(Debug)] - pub enum ParseErrorCode { - none, - - // Matching values from enum parser_error. - syntax, - cmdsubst, - - generic, // unclassified error types - - // Tokenizer errors. - tokenizer_unterminated_quote, - tokenizer_unterminated_subshell, - tokenizer_unterminated_slice, - tokenizer_unterminated_escape, - tokenizer_other, - - unbalancing_end, // end outside of block - unbalancing_else, // else outside of if - unbalancing_case, // case outside of switch - bare_variable_assignment, // a=b without command - andor_in_pipeline, // "and" or "or" after a pipe - } - - struct parse_error_t { - text: UniquePtr, - code: ParseErrorCode, - source_start: usize, - source_length: usize, - } - - extern "Rust" { - type ParseError; - fn code(self: &ParseError) -> ParseErrorCode; - fn source_start(self: &ParseError) -> usize; - fn text(self: &ParseError) -> UniquePtr; - - #[cxx_name = "describe"] - fn describe_ffi( - self: &ParseError, - src: &CxxWString, - is_interactive: bool, - ) -> UniquePtr; - #[cxx_name = "describe_with_prefix"] - fn describe_with_prefix_ffi( - self: &ParseError, - src: &CxxWString, - prefix: &CxxWString, - is_interactive: bool, - skip_caret: bool, - ) -> UniquePtr; - - fn describe_with_prefix( - self: &parse_error_t, - src: &CxxWString, - prefix: &CxxWString, - is_interactive: bool, - skip_caret: bool, - ) -> UniquePtr; - - type ParseErrorListFfi; - fn new_parse_error_list() -> Box; - #[cxx_name = "offset_source_start"] - fn offset_source_start_ffi(self: &mut ParseErrorListFfi, amt: usize); - fn size(self: &ParseErrorListFfi) -> usize; - fn at(self: &ParseErrorListFfi, offset: usize) -> *const ParseError; - fn empty(self: &ParseErrorListFfi) -> bool; - fn push_back(self: &mut ParseErrorListFfi, error: &parse_error_t); - fn append(self: &mut ParseErrorListFfi, other: *mut ParseErrorListFfi); - fn erase(self: &mut ParseErrorListFfi, index: usize); - fn clear(self: &mut ParseErrorListFfi); - } - - // The location of a pipeline. - pub enum PipelinePosition { - none, // not part of a pipeline - first, // first command in a pipeline - subsequent, // second or further command in a pipeline - } +/// A range of source code. +#[derive(PartialEq, Eq, Clone, Copy, Debug)] +pub struct SourceRange { + pub start: u32, + pub length: u32, } -pub use parse_constants_ffi::{ - parse_error_t, ParseErrorCode, ParseKeyword, ParseTokenType, PipelinePosition, SourceRange, - StatementDecoration, -}; +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub enum ParseTokenType { + invalid = 1, + + // Terminal types. + string, + pipe, + redirection, + background, + andand, + oror, + end, + // Special terminal type that means no more tokens forthcoming. + terminate, + // Very special terminal types that don't appear in the production list. + error, + tokenizer_error, + comment, +} + +#[repr(u8)] +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub enum ParseKeyword { + // 'none' is not a keyword, it is a sentinel indicating nothing. + none, + + kw_and, + kw_begin, + kw_builtin, + kw_case, + kw_command, + kw_else, + kw_end, + kw_exclam, + kw_exec, + kw_for, + kw_function, + kw_if, + kw_in, + kw_not, + kw_or, + kw_switch, + kw_time, + kw_while, +} + +// Statement decorations like 'command' or 'exec'. +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub enum StatementDecoration { + none, + command, + builtin, + exec, +} + +// Parse error code list. +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub enum ParseErrorCode { + none, + + // Matching values from enum parser_error. + syntax, + cmdsubst, + + generic, // unclassified error types + + // Tokenizer errors. + tokenizer_unterminated_quote, + tokenizer_unterminated_subshell, + tokenizer_unterminated_slice, + tokenizer_unterminated_escape, + tokenizer_other, + + unbalancing_end, // end outside of block + unbalancing_else, // else outside of if + unbalancing_case, // case outside of switch + bare_variable_assignment, // a=b without command + andor_in_pipeline, // "and" or "or" after a pipe +} + +// The location of a pipeline. +#[derive(Clone, Copy, Eq, PartialEq)] +pub enum PipelinePosition { + none, // not part of a pipeline + first, // first command in a pipeline + subsequent, // second or further command in a pipeline +} impl SourceRange { pub fn new(start: usize, length: usize) -> Self { @@ -236,17 +164,10 @@ pub fn combine(&self, other: Self) -> Self { } } - fn end_ffi(&self) -> u32 { - self.start.checked_add(self.length).expect("Overflow") - } - // \return true if a location is in this range, including one-past-the-end. pub fn contains_inclusive(&self, loc: usize) -> bool { self.start() <= loc && loc - self.start() <= self.length() } - fn contains_inclusive_ffi(&self, loc: u32) -> bool { - self.start <= loc && loc - self.start <= self.length - } } impl From for std::ops::Range { @@ -278,7 +199,6 @@ pub fn to_wstr(self) -> &'static wstr { ParseTokenType::oror => "ParseTokenType::oror"L, ParseTokenType::terminate => "ParseTokenType::terminate"L, ParseTokenType::invalid => "ParseTokenType::invalid"L, - _ => "unknown token type"L, } } } @@ -482,29 +402,6 @@ pub fn describe_with_prefix( } } -impl From<&parse_error_t> for ParseError { - fn from(error: &parse_error_t) -> Self { - ParseError { - text: error.text.from_ffi(), - code: error.code, - source_start: error.source_start, - source_length: error.source_length, - } - } -} - -impl parse_error_t { - fn describe_with_prefix( - self: &parse_error_t, - src: &CxxWString, - prefix: &CxxWString, - is_interactive: bool, - skip_caret: bool, - ) -> UniquePtr { - ParseError::from(self).describe_with_prefix_ffi(src, prefix, is_interactive, skip_caret) - } -} - impl ParseError { fn code(&self) -> ParseErrorCode { self.code @@ -512,28 +409,6 @@ fn code(&self) -> ParseErrorCode { fn source_start(&self) -> usize { self.source_start } - fn text(&self) -> UniquePtr { - self.text.to_ffi() - } - - fn describe_ffi( - self: &ParseError, - src: &CxxWString, - is_interactive: bool, - ) -> UniquePtr { - self.describe(src.as_wstr(), is_interactive).to_ffi() - } - - fn describe_with_prefix_ffi( - self: &ParseError, - src: &CxxWString, - prefix: &CxxWString, - is_interactive: bool, - skip_caret: bool, - ) -> UniquePtr { - self.describe_with_prefix(src.as_wstr(), prefix.as_wstr(), is_interactive, skip_caret) - .to_ffi() - } } #[widestrs] @@ -562,14 +437,6 @@ pub fn token_type_user_presentable_description( pub type ParseErrorList = Vec; -#[derive(Clone)] -pub struct ParseErrorListFfi(pub ParseErrorList); - -unsafe impl ExternType for ParseErrorListFfi { - type Id = type_id!("ParseErrorListFfi"); - type Kind = cxx::kind::Opaque; -} - /// Helper function to offset error positions by the given amount. This is used when determining /// errors in a substring of a larger source buffer. pub fn parse_error_offset_source_start(errors: &mut ParseErrorList, amt: usize) { @@ -583,44 +450,6 @@ pub fn parse_error_offset_source_start(errors: &mut ParseErrorList, amt: usize) } } -fn new_parse_error_list() -> Box { - Box::new(ParseErrorListFfi(Vec::new())) -} - -impl ParseErrorListFfi { - fn offset_source_start_ffi(&mut self, amt: usize) { - parse_error_offset_source_start(&mut self.0, amt) - } - - fn size(&self) -> usize { - self.0.len() - } - - fn at(&self, offset: usize) -> *const ParseError { - &self.0[offset] - } - - fn empty(&self) -> bool { - self.0.is_empty() - } - - fn push_back(&mut self, error: &parse_error_t) { - self.0.push(error.into()) - } - - fn append(&mut self, other: *mut ParseErrorListFfi) { - self.0.append(&mut (unsafe { &*other }.0.clone())); - } - - fn erase(&mut self, index: usize) { - self.0.remove(index); - } - - fn clear(&mut self) { - self.0.clear() - } -} - /// Maximum number of function calls. pub const FISH_MAX_STACK_DEPTH: usize = 128; diff --git a/fish-rust/src/parse_execution.rs b/fish-rust/src/parse_execution.rs index c2a5372a8..c65bed658 100644 --- a/fish-rust/src/parse_execution.rs +++ b/fish-rust/src/parse_execution.rs @@ -615,7 +615,6 @@ fn process_type_for_command( ProcessType::external } } - _ => unreachable!(), } } @@ -658,7 +657,6 @@ fn apply_variable_assignments( } ExpandResultCode::wildcard_no_match // nullglob (equivalent to set) | ExpandResultCode::ok => {} - _ => unreachable!(), } let vals: Vec<_> = expression_expanded .into_iter() @@ -1150,7 +1148,6 @@ fn run_switch_statement( ); } } - _ => unreachable!(), } // If we expanded to nothing, match the empty string. @@ -1426,7 +1423,6 @@ fn expand_arguments_from_nodes( } } ExpandResultCode::ok => {} - _ => unreachable!(), } // Now copy over any expanded arguments. Use std::move() to avoid extra allocations; this diff --git a/fish-rust/src/parse_tree.rs b/fish-rust/src/parse_tree.rs index cee22a4f0..73c0e4a66 100644 --- a/fish-rust/src/parse_tree.rs +++ b/fish-rust/src/parse_tree.rs @@ -7,13 +7,11 @@ use crate::ast::{Ast, Node}; use crate::common::{assert_send, assert_sync}; use crate::parse_constants::{ - token_type_user_presentable_description, ParseErrorCode, ParseErrorList, ParseErrorListFfi, - ParseKeyword, ParseTokenType, ParseTreeFlags, SourceOffset, SourceRange, SOURCE_OFFSET_INVALID, + token_type_user_presentable_description, ParseErrorCode, ParseErrorList, ParseKeyword, + ParseTokenType, ParseTreeFlags, SourceOffset, SourceRange, SOURCE_OFFSET_INVALID, }; use crate::tokenizer::TokenizerError; use crate::wchar::prelude::*; -use crate::wchar_ffi::{WCharFromFFI, WCharToFFI}; -use cxx::{CxxWString, UniquePtr}; /// A struct representing the token type that we use internally. #[derive(Clone, Copy)] @@ -102,7 +100,6 @@ fn from(err: TokenizerError) -> Self { /// A type wrapping up a parse tree and the original source behind it. pub struct ParsedSource { pub src: WString, - src_ffi: UniquePtr, pub ast: Ast, } @@ -115,8 +112,7 @@ unsafe impl Sync for ParsedSource {} impl ParsedSource { pub fn new(src: WString, ast: Ast) -> Self { - let src_ffi = src.to_ffi(); - ParsedSource { src, src_ffi, ast } + ParsedSource { src, ast } } } @@ -194,79 +190,3 @@ pub fn parse_source( Some(Arc::new(ParsedSource::new(src, ast))) } } - -pub struct ParsedSourceRefFFI(pub Option); - -unsafe impl cxx::ExternType for ParsedSourceRefFFI { - type Id = cxx::type_id!("ParsedSourceRefFFI"); - type Kind = cxx::kind::Opaque; -} - -#[cxx::bridge] -mod parse_tree_ffi { - extern "C++" { - include!("ast.h"); - pub type Ast = crate::ast::Ast; - pub type ParseErrorListFfi = crate::parse_constants::ParseErrorListFfi; - } - extern "Rust" { - type ParsedSourceRefFFI; - fn empty_parsed_source_ref() -> Box; - fn has_value(&self) -> bool; - fn new_parsed_source_ref(src: &CxxWString, ast: Pin<&mut Ast>) -> Box; - #[cxx_name = "parse_source"] - fn parse_source_ffi( - src: &CxxWString, - flags: u8, - errors: *mut ParseErrorListFfi, - ) -> Box; - fn clone(self: &ParsedSourceRefFFI) -> Box; - fn src(self: &ParsedSourceRefFFI) -> &CxxWString; - fn ast(self: &ParsedSourceRefFFI) -> &Ast; - } -} - -impl ParsedSourceRefFFI { - fn has_value(&self) -> bool { - self.0.is_some() - } -} - -fn empty_parsed_source_ref() -> Box { - Box::new(ParsedSourceRefFFI(None)) -} - -fn new_parsed_source_ref(src: &CxxWString, ast: Pin<&mut Ast>) -> Box { - let mut stolen_ast = Ast::default(); - std::mem::swap(&mut stolen_ast, ast.get_mut()); - Box::new(ParsedSourceRefFFI(Some(Arc::new(ParsedSource::new( - src.from_ffi(), - stolen_ast, - ))))) -} -fn parse_source_ffi( - src: &CxxWString, - flags: u8, - errors: *mut ParseErrorListFfi, -) -> Box { - Box::new(ParsedSourceRefFFI(parse_source( - src.from_ffi(), - ParseTreeFlags::from_bits(flags).unwrap(), - if errors.is_null() { - None - } else { - Some(unsafe { &mut (*errors).0 }) - }, - ))) -} -impl ParsedSourceRefFFI { - fn clone(&self) -> Box { - Box::new(ParsedSourceRefFFI(self.0.clone())) - } - fn src(&self) -> &CxxWString { - &self.0.as_ref().unwrap().src_ffi - } - fn ast(&self) -> &Ast { - &self.0.as_ref().unwrap().ast - } -} diff --git a/fish-rust/src/parse_util.rs b/fish-rust/src/parse_util.rs index 44526b714..586baf444 100644 --- a/fish-rust/src/parse_util.rs +++ b/fish-rust/src/parse_util.rs @@ -12,12 +12,12 @@ use crate::future_feature_flags::{feature_test, FeatureFlag}; use crate::operation_context::OperationContext; use crate::parse_constants::{ - parse_error_offset_source_start, ParseError, ParseErrorCode, ParseErrorList, ParseErrorListFfi, - ParseKeyword, ParseTokenType, ParseTreeFlags, ParserTestErrorBits, PipelinePosition, - StatementDecoration, ERROR_BAD_VAR_CHAR1, ERROR_BRACKETED_VARIABLE1, - ERROR_BRACKETED_VARIABLE_QUOTED1, ERROR_NOT_ARGV_AT, ERROR_NOT_ARGV_COUNT, ERROR_NOT_ARGV_STAR, - ERROR_NOT_PID, ERROR_NOT_STATUS, ERROR_NO_VAR_NAME, INVALID_BREAK_ERR_MSG, - INVALID_CONTINUE_ERR_MSG, INVALID_PIPELINE_CMD_ERR_MSG, UNKNOWN_BUILTIN_ERR_MSG, + parse_error_offset_source_start, ParseError, ParseErrorCode, ParseErrorList, ParseKeyword, + ParseTokenType, ParseTreeFlags, ParserTestErrorBits, PipelinePosition, StatementDecoration, + ERROR_BAD_VAR_CHAR1, ERROR_BRACKETED_VARIABLE1, ERROR_BRACKETED_VARIABLE_QUOTED1, + ERROR_NOT_ARGV_AT, ERROR_NOT_ARGV_COUNT, ERROR_NOT_ARGV_STAR, ERROR_NOT_PID, ERROR_NOT_STATUS, + ERROR_NO_VAR_NAME, INVALID_BREAK_ERR_MSG, INVALID_CONTINUE_ERR_MSG, + INVALID_PIPELINE_CMD_ERR_MSG, UNKNOWN_BUILTIN_ERR_MSG, }; #[cfg(test)] use crate::tests::prelude::*; @@ -26,10 +26,8 @@ TOK_SHOW_COMMENTS, }; use crate::wchar::prelude::*; -use crate::wchar_ffi::{AsWstr, WCharFromFFI}; use crate::wcstringutil::truncate; use crate::wildcard::{ANY_CHAR, ANY_STRING, ANY_STRING_RECURSIVE}; -use cxx::CxxWString; use std::ops; /// Handles slices: the square brackets in an expression like $foo[5..4] @@ -2036,58 +2034,3 @@ macro_rules! validate { ); })(); } - -#[cxx::bridge] -mod parse_util_ffi { - extern "C++" { - include!("parse_constants.h"); - include!("parse_tree.h"); - include!("ast.h"); - type ParseErrorListFfi = crate::parse_constants::ParseErrorListFfi; - type DecoratedStatement = crate::ast::DecoratedStatement; - } - extern "Rust" { - fn parse_util_compute_indents_ffi(src: &CxxWString) -> Vec; - #[cxx_name = "detect_errors_in_decorated_statement"] - // Getting weird linker errors when using pointers. - fn detect_errors_in_decorated_statement_ffi( - buff_src: &CxxWString, - dst: usize, - out_errors: usize, - ) -> bool; - } -} - -fn detect_errors_in_decorated_statement_ffi( - buff_src: &CxxWString, - dst: usize, - out_errors: usize, -) -> bool { - let dst = unsafe { &*(dst as *const ast::DecoratedStatement) }; - let out_errors = out_errors as *mut ParseErrorListFfi; - let mut out_errors = if out_errors.is_null() { - None - } else { - Some(unsafe { &mut (*out_errors).0 }) - }; - detect_errors_in_decorated_statement(buff_src.as_wstr(), dst, &mut out_errors) -} - -fn parse_util_compute_indents_ffi(src: &CxxWString) -> Vec { - parse_util_compute_indents(&src.from_ffi()) -} - -fn parse_util_detect_errors_ffi( - buff_src: &CxxWString, - out_errors: *mut ParseErrorListFfi, - allow_incomplete: bool, -) -> u8 { - let out_errors = if out_errors.is_null() { - None - } else { - Some(unsafe { &mut (*out_errors).0 }) - }; - parse_util_detect_errors(buff_src.as_wstr(), out_errors, allow_incomplete) - .err() - .map_or(0, |error_bits| error_bits.bits()) -} diff --git a/fish-rust/src/parser.rs b/fish-rust/src/parser.rs index c7e89973b..72a490a2d 100644 --- a/fish-rust/src/parser.rs +++ b/fish-rust/src/parser.rs @@ -7,15 +7,12 @@ FilenameRef, ScopeGuarding, PROFILING_ACTIVE, }; use crate::complete::CompletionList; -use crate::env::{ - EnvMode, EnvStack, EnvStackRef, EnvStackRefFFI, EnvStackSetResult, Environment, Statuses, -}; +use crate::env::{EnvMode, EnvStack, EnvStackRef, EnvStackSetResult, Environment, Statuses}; use crate::event::{self, Event}; use crate::expand::{ expand_string, replace_home_directory_with_tilde, ExpandFlags, ExpandResultCode, }; use crate::fds::{open_cloexec, AutoCloseFd}; -use crate::ffi::{self, wcstring_list_ffi_t}; use crate::flog::FLOGF; use crate::function; use crate::global_safety::{RelaxedAtomicBool, SharedFromThis, SharedFromThisBase}; @@ -27,20 +24,17 @@ SOURCE_LOCATION_UNKNOWN, }; use crate::parse_execution::{EndExecutionReason, ParseExecutionContext}; -use crate::parse_tree::{parse_source, ParsedSourceRef, ParsedSourceRefFFI}; +use crate::parse_tree::{parse_source, ParsedSourceRef}; use crate::proc::{job_reap, JobGroupRef, JobList, JobRef, ProcStatus}; use crate::signal::{signal_check_cancel, signal_clear_cancel, Signal}; use crate::threads::assert_is_main_thread; use crate::util::get_time; use crate::wait_handle::WaitHandleStore; use crate::wchar::{wstr, WString, L}; -use crate::wchar_ffi::{AsWstr, WCharFromFFI, WCharToFFI}; use crate::wutil::{perror, wgettext, wgettext_fmt}; -use cxx::{CxxWString, UniquePtr}; use libc::c_int; use libc::O_RDONLY; use once_cell::sync::Lazy; -pub use parser_ffi::{library_data_pod_t, BlockType, LoopStatus}; use printf_compat::sprintf; use std::cell::{Ref, RefCell, RefMut}; use std::ffi::{CStr, OsStr}; @@ -54,8 +48,6 @@ }; use widestring_suffix::widestrs; -use self::parser_ffi::ParseErrorListFfi; - /// block_t represents a block of commands. #[derive(Default)] pub struct Block { @@ -121,7 +113,6 @@ pub fn description(&self) -> WString { BlockType::event => "event"L, BlockType::breakpoint => "breakpoint"L, BlockType::variable_assignment => "variable_assignment"L, - _ => panic!(), } .to_owned(); @@ -327,7 +318,6 @@ pub struct Parser { /// Set of variables for the parser. pub variables: EnvStackRef, - variables_ffi: EnvStackRefFFI, /// Miscellaneous library data. library_data: RefCell, @@ -355,7 +345,6 @@ fn get_base(&self) -> &SharedFromThisBase { impl Parser { /// Create a parser pub fn new(variables: EnvStackRef, is_principal: bool) -> ParserRef { - let variables_ffi = EnvStackRefFFI(variables.clone()); let result = Rc::new(Self { base: SharedFromThisBase::new(), execution_context: RefCell::default(), @@ -364,7 +353,6 @@ pub fn new(variables: EnvStackRef, is_principal: bool) -> ParserRef { block_list: RefCell::default(), eval_level: AtomicIsize::new(-1), variables, - variables_ffi, library_data: RefCell::new(LibraryData::new()), syncs_uvars: RelaxedAtomicBool::new(false), is_principal: RelaxedAtomicBool::new(is_principal), @@ -1198,7 +1186,6 @@ fn append_block_description_to_stack_trace(parser: &Parser, b: &Block, trace: &m | BlockType::if_block | BlockType::breakpoint | BlockType::variable_assignment => {} - _ => unreachable!(), } if print_call_site { @@ -1215,347 +1202,108 @@ fn append_block_description_to_stack_trace(parser: &Parser, b: &Block, trace: &m } } -#[cxx::bridge] -mod parser_ffi { - /// Types of blocks. - #[derive(Debug)] - #[cxx_name = "block_type_t"] - pub enum BlockType { - /// While loop block - while_block, - /// For loop block - for_block, - /// If block - if_block, - /// Function invocation block - function_call, - /// Function invocation block with no variable shadowing - function_call_no_shadow, - /// Switch block - switch_block, - /// Command substitution scope - subst, - /// Outermost block - top, - /// Unconditional block - begin, - /// Block created by the . (source) builtin - source, - /// Block created on event notifier invocation - event, - /// Breakpoint block - breakpoint, - /// Variable assignment before a command - variable_assignment, - } - - /// Possible states for a loop. - pub enum LoopStatus { - /// current loop block executed as normal - normals, - /// current loop block should be removed - breaks, - /// current loop block should be skipped - continues, - } - - /// Plain-Old-Data components of `struct library_data_t` that can be shared over FFI - #[derive(Default)] - pub struct library_data_pod_t { - /// A counter incremented every time a command executes. - pub exec_count: u64, - - /// A counter incremented every time a command produces a $status. - pub status_count: u64, - - /// Last reader run count. - pub last_exec_run_counter: u64, - - /// Number of recursive calls to the internal completion function. - pub complete_recursion_level: u32, - - /// If set, we are currently within fish's initialization routines. - pub within_fish_init: bool, - - /// If we're currently repainting the commandline. - /// Useful to stop infinite loops. - pub is_repaint: bool, - - /// Whether we called builtin_complete -C without parameter. - pub builtin_complete_current_commandline: bool, - - /// Whether we are currently cleaning processes. - pub is_cleaning_procs: bool, - - /// The internal job id of the job being populated, or 0 if none. - /// This supports the '--on-job-exit caller' feature. - pub caller_id: u64, // TODO should be InternalJobId - - /// Whether we are running a subshell command. - pub is_subshell: bool, - - /// Whether we are running an event handler. This is not a bool because we keep count of the - /// event nesting level. - pub is_event: i32, - - /// Whether we are currently interactive. - pub is_interactive: bool, - - /// Whether to suppress fish_trace output. This occurs in the prompt, event handlers, and key - /// bindings. - pub suppress_fish_trace: bool, - - /// Whether we should break or continue the current loop. - /// This is set by the 'break' and 'continue' commands. - pub loop_status: LoopStatus, - - /// Whether we should return from the current function. - /// This is set by the 'return' command. - pub returning: bool, - - /// Whether we should stop executing. - /// This is set by the 'exit' command, and unset after 'reader_read'. - /// Note this only exits up to the "current script boundary." That is, a call to exit within a - /// 'source' or 'read' command will only exit up to that command. - pub exit_current_script: bool, - - /// The read limit to apply to captured subshell output, or 0 for none. - pub read_limit: usize, - } - - extern "C++" { - include!("operation_context.h"); - include!("wutil.h"); - include!("env.h"); - include!("io.h"); - include!("proc.h"); - include!("parse_tree.h"); - include!("parse_constants.h"); - type IoChain = crate::io::IoChain; - type JobRefFfi = crate::proc::JobRefFfi; - type JobGroupRefFfi = crate::proc::JobGroupRefFfi; - type ParsedSourceRefFFI = crate::parse_tree::ParsedSourceRefFFI; - type ParseErrorListFfi = crate::parse_constants::ParseErrorListFfi; - #[cxx_name = "EnvStackRef"] - type EnvStackRefFFI = crate::env::EnvStackRefFFI; - #[cxx_name = "env_stack_set_result_t"] - type EnvStackSetResult = crate::env::EnvStackSetResult; - type wcstring_list_ffi_t = crate::ffi::wcstring_list_ffi_t; - type OperationContext<'a> = crate::operation_context::OperationContext<'a>; - type Statuses = crate::env::Statuses; - } - extern "Rust" { - type Block; - } - extern "Rust" { - type LibraryData; - fn exec_count(&self) -> u64; - #[cxx_name = "is_repaint"] - fn is_repaint_ffi(&self) -> bool; - fn set_status_vars_command(&mut self, s: &CxxWString); - fn set_status_vars_commandline(&mut self, s: &CxxWString); - - #[cxx_name = "transient_commandlines_empty"] - fn transient_commandlines_empty_ffi(&self) -> bool; - #[cxx_name = "transient_commandlines_back"] - fn transient_commandlines_back_ffi(&self) -> UniquePtr; - #[cxx_name = "transient_commandlines_push"] - fn transient_commandlines_push_ffi(&mut self, s: &CxxWString); - #[cxx_name = "transient_commandlines_pop"] - fn transient_commandlines_pop_ffi(&mut self); - } - extern "Rust" { - type EvalRes; - fn no_status(&self) -> bool; - } - extern "Rust" { - type Parser; - - fn get_last_status(&self) -> i32; - fn assert_can_execute(&self); - fn is_interactive(&self) -> bool; - #[cxx_name = "eval"] - fn ffi_eval(&self, cmd: &CxxWString, io: &IoChain) -> Box; - #[cxx_name = "eval_with"] - #[cxx_name = "eval_parsed_source"] - fn ffi_eval_parsed_source(&self, ps: &ParsedSourceRefFFI, io: &IoChain); - #[cxx_name = "is_breakpoint"] - fn ffi_is_breakpoint(&self) -> bool; - #[cxx_name = "libdata"] - fn ffi_libdata(&self) -> &LibraryData; - #[cxx_name = "libdata_mut"] - #[allow(clippy::mut_from_ref)] - fn ffi_libdata_mut(&self) -> &mut LibraryData; - #[cxx_name = "libdata_pods"] - fn ffi_libdata_pods(&self) -> &library_data_pod_t; - #[cxx_name = "libdata_pods_mut"] - #[allow(clippy::mut_from_ref)] - fn ffi_libdata_pods_mut(&self) -> &mut library_data_pod_t; - #[cxx_name = "get_backtrace"] - fn ffi_get_backtrace( - &self, - src: &CxxWString, - errors: &ParseErrorListFfi, - ) -> UniquePtr; - #[cxx_name = "set_last_statuses"] - fn ffi_set_last_statuses(&self, s: &Statuses); - #[cxx_name = "vars"] - fn ffi_vars(&self) -> &EnvStackRefFFI; - #[cxx_name = "vars_boxed"] - fn ffi_vars_boxed(&self) -> *mut u8; - fn sync_uvars_and_fire(&self, always: bool); - #[cxx_name = "parser_principal_parser"] - fn ffi_parser_principal_parser() -> Box; - #[cxx_name = "shared"] - fn ffi_shared(&self) -> Box; - #[cxx_name = "set_var_and_fire"] - fn ffi_set_var_and_fire( - &self, - key: &CxxWString, - mode: u16, - vals: &wcstring_list_ffi_t, - ) -> i32; - fn parser_expand_argument_list_ffi( - arg_list_src: &CxxWString, - flags: u16, - ctx: &OperationContext<'_>, - out: Pin<&mut wcstring_list_ffi_t>, - ); - } - extern "Rust" { - #[cxx_name = "ParserRef"] - type ParserRefFFI; - fn vars(&self) -> &EnvStackRefFFI; - fn deref(&self) -> &Parser; - } +/// Types of blocks. +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub enum BlockType { + /// While loop block + while_block, + /// For loop block + for_block, + /// If block + if_block, + /// Function invocation block + function_call, + /// Function invocation block with no variable shadowing + function_call_no_shadow, + /// Switch block + switch_block, + /// Command substitution scope + subst, + /// Outermost block + top, + /// Unconditional block + begin, + /// Block created by the . (source) builtin + source, + /// Block created on event notifier invocation + event, + /// Breakpoint block + breakpoint, + /// Variable assignment before a command + variable_assignment, } -impl LibraryData { - fn exec_count(&self) -> u64 { - self.pods.exec_count - } - fn is_repaint_ffi(&self) -> bool { - self.pods.is_repaint - } - fn set_status_vars_command(&mut self, s: &CxxWString) { - self.status_vars.command = s.from_ffi() - } - fn set_status_vars_commandline(&mut self, s: &CxxWString) { - self.status_vars.commandline = s.from_ffi() - } - fn transient_commandlines_empty_ffi(&self) -> bool { - self.transient_commandlines.is_empty() - } - fn transient_commandlines_back_ffi(&self) -> UniquePtr { - self.transient_commandlines.last().unwrap().to_ffi() - } - fn transient_commandlines_push_ffi(&mut self, s: &CxxWString) { - self.transient_commandlines.push(s.from_ffi()); - } - fn transient_commandlines_pop_ffi(&mut self) { - self.transient_commandlines.pop(); - } +/// Possible states for a loop. +#[derive(Clone, Copy, Eq, PartialEq)] +pub enum LoopStatus { + /// current loop block executed as normal + normals, + /// current loop block should be removed + breaks, + /// current loop block should be skipped + continues, } -impl EvalRes { - fn no_status(&self) -> bool { - self.no_status - } -} +/// Plain-Old-Data components of `struct library_data_t` that can be shared over FFI +#[derive(Default)] +pub struct library_data_pod_t { + /// A counter incremented every time a command executes. + pub exec_count: u64, -unsafe impl cxx::ExternType for Parser { - type Id = cxx::type_id!("Parser"); - type Kind = cxx::kind::Opaque; -} + /// A counter incremented every time a command produces a $status. + pub status_count: u64, -pub struct ParserRefFFI(pub ParserRef); + /// Last reader run count. + pub last_exec_run_counter: u64, -unsafe impl cxx::ExternType for ParserRefFFI { - type Id = cxx::type_id!("ParserRef"); // CXX name! - type Kind = cxx::kind::Opaque; -} + /// Number of recursive calls to the internal completion function. + pub complete_recursion_level: u32, -fn ffi_parser_principal_parser() -> Box { - Box::new(ParserRefFFI(Parser::principal_parser().shared())) -} + /// If set, we are currently within fish's initialization routines. + pub within_fish_init: bool, -impl Parser { - fn ffi_shared(&self) -> Box { - Box::new(ParserRefFFI(self.shared())) - } - fn ffi_set_var_and_fire( - &self, - key: &CxxWString, - mode: u16, - vals: &ffi::wcstring_list_ffi_t, - ) -> i32 { - let val: u8 = unsafe { - std::mem::transmute(self.set_var_and_fire( - key.as_wstr(), - EnvMode::from_bits(mode).unwrap(), - vals.from_ffi(), - )) - }; - val as _ - } - pub fn ffi_eval(&self, cmd: &CxxWString, io: &IoChain) -> Box { - Box::new(self.eval(cmd.as_wstr(), io)) - } - fn ffi_eval_parsed_source(&self, ps: &ParsedSourceRefFFI, io: &IoChain) { - self.eval_parsed_source(ps.0.as_ref().unwrap(), io, None, BlockType::top); - } - fn ffi_is_breakpoint(&self) -> bool { - self.is_breakpoint() - } - fn ffi_vars(&self) -> &EnvStackRefFFI { - &self.variables_ffi - } - fn ffi_vars_boxed(&self) -> *mut u8 { - Box::into_raw(Box::new(self.variables_ffi.clone())).cast() - } - fn ffi_libdata(&self) -> &LibraryData { - unsafe { self.library_data.try_borrow_unguarded() }.unwrap() - } - fn ffi_libdata_mut(&self) -> &mut LibraryData { - unsafe { &mut *self.library_data.as_ptr() } - } - fn ffi_libdata_pods(&self) -> &library_data_pod_t { - &unsafe { &*self.library_data.as_ptr() }.pods - } - fn ffi_libdata_pods_mut(&self) -> &mut library_data_pod_t { - &mut unsafe { &mut *self.library_data.as_ptr() }.pods - } - fn ffi_get_backtrace( - &self, - src: &CxxWString, - errors: &ParseErrorListFfi, - ) -> UniquePtr { - self.get_backtrace(&src.from_ffi(), &errors.0).to_ffi() - } - fn ffi_set_last_statuses(&self, s: &Statuses) { - self.set_last_statuses(s.clone()) - } -} + /// If we're currently repainting the commandline. + /// Useful to stop infinite loops. + pub is_repaint: bool, -fn parser_expand_argument_list_ffi( - arg_list_src: &CxxWString, - flags: u16, - ctx: &OperationContext<'_>, - mut out: Pin<&mut wcstring_list_ffi_t>, -) { - let arg_list_src = arg_list_src.as_wstr(); - let flags = ExpandFlags::from_bits(flags).unwrap(); - for c in Parser::expand_argument_list(arg_list_src, flags, ctx) { - out.as_mut().push(c.completion); - } -} + /// Whether we called builtin_complete -C without parameter. + pub builtin_complete_current_commandline: bool, -impl ParserRefFFI { - fn deref(&self) -> &Parser { - let ptr = self.0.as_ref() as *const _; - unsafe { &*ptr } - } - fn vars(&self) -> &EnvStackRefFFI { - &self.0.variables_ffi - } + /// Whether we are currently cleaning processes. + pub is_cleaning_procs: bool, + + /// The internal job id of the job being populated, or 0 if none. + /// This supports the '--on-job-exit caller' feature. + pub caller_id: u64, // TODO should be InternalJobId + + /// Whether we are running a subshell command. + pub is_subshell: bool, + + /// Whether we are running an event handler. This is not a bool because we keep count of the + /// event nesting level. + pub is_event: i32, + + /// Whether we are currently interactive. + pub is_interactive: bool, + + /// Whether to suppress fish_trace output. This occurs in the prompt, event handlers, and key + /// bindings. + pub suppress_fish_trace: bool, + + /// Whether we should break or continue the current loop. + /// This is set by the 'break' and 'continue' commands. + pub loop_status: LoopStatus, + + /// Whether we should return from the current function. + /// This is set by the 'return' command. + pub returning: bool, + + /// Whether we should stop executing. + /// This is set by the 'exit' command, and unset after 'reader_read'. + /// Note this only exits up to the "current script boundary." That is, a call to exit within a + /// 'source' or 'read' command will only exit up to that command. + pub exit_current_script: bool, + + /// The read limit to apply to captured subshell output, or 0 for none. + pub read_limit: usize, } diff --git a/fish-rust/src/print_help.rs b/fish-rust/src/print_help.rs index db0401f09..ffae47d41 100644 --- a/fish-rust/src/print_help.rs +++ b/fish-rust/src/print_help.rs @@ -1,19 +1,11 @@ //! Helper for executables (not builtins) to print a help message //! Uses the fish in PATH, not necessarily the matching fish binary -use libc::c_char; -use std::ffi::{CStr, OsStr, OsString}; +use std::ffi::{OsStr, OsString}; use std::process::Command; const HELP_ERR: &str = "Could not show help message"; -#[cxx::bridge] -mod ffi2 { - extern "Rust" { - unsafe fn unsafe_print_help(command: *const c_char); - } -} - pub fn print_help(command: &str) { let mut cmdline = OsString::new(); cmdline.push("__fish_print_help "); @@ -24,9 +16,3 @@ pub fn print_help(command: &str) { .spawn() .expect(HELP_ERR); } - -unsafe fn unsafe_print_help(command_buf: *const c_char) { - let command_cstr: &CStr = unsafe { CStr::from_ptr(command_buf) }; - let command = command_cstr.to_str().unwrap(); - print_help(command); -} diff --git a/fish-rust/src/proc.rs b/fish-rust/src/proc.rs index f623b7e0e..70302e19c 100644 --- a/fish-rust/src/proc.rs +++ b/fish-rust/src/proc.rs @@ -1787,86 +1787,3 @@ pub fn have_proc_stat() -> bool { /// The signals that signify crashes to us. const CRASHSIGNALS: [libc::c_int; 6] = [SIGABRT, SIGBUS, SIGFPE, SIGILL, SIGSEGV, SIGSYS]; - -pub struct JobRefFfi(JobRef); - -unsafe impl cxx::ExternType for JobRefFfi { - type Id = cxx::type_id!("JobRefFfi"); - type Kind = cxx::kind::Opaque; -} - -pub struct JobGroupRefFfi(JobGroupRef); - -unsafe impl cxx::ExternType for JobGroupRefFfi { - type Id = cxx::type_id!("JobGroupRefFfi"); - type Kind = cxx::kind::Opaque; -} - -#[cxx::bridge] -mod proc_ffi { - extern "C++" { - include!("parser.h"); - type Parser = crate::parser::Parser; - } - extern "Rust" { - fn set_interactive_session(flag: bool); - fn get_login() -> bool; - fn mark_login(); - fn mark_no_exec(); - fn proc_init(); - - type JobRefFfi; - type JobGroupRefFfi; - - fn job_reap(parser: &Parser, allow_interactive: bool) -> bool; - fn is_interactive_session() -> bool; - fn have_proc_stat() -> bool; - fn proc_update_jiffies(parser: &Parser); - } - extern "Rust" { - type TtyTransfer; - fn new_tty_transfer() -> Box; - #[cxx_name = "to_job_group"] - fn to_job_group_ffi(&mut self, jg: &JobGroupRefFfi); - fn save_tty_modes(&mut self); - fn reclaim(&mut self); - fn no_exec() -> bool; - } - extern "Rust" { - type JobListFFI; - #[cxx_name = "jobs_requiring_warning_on_exit"] - fn jobs_requiring_warning_on_exit_ffi(parser: &Parser) -> Box; - #[cxx_name = "print_exit_warning_for_jobs"] - fn print_exit_warning_for_jobs_ffi(jobs: &JobListFFI); - fn empty(&self) -> bool; - #[cxx_name = "hup_jobs"] - fn hup_jobs_ffi(parser: &Parser); - } -} -struct JobListFFI(JobList); -fn jobs_requiring_warning_on_exit_ffi(parser: &Parser) -> Box { - Box::new(JobListFFI(jobs_requiring_warning_on_exit(parser))) -} -fn print_exit_warning_for_jobs_ffi(jobs: &JobListFFI) { - print_exit_warning_for_jobs(&jobs.0) -} - -fn hup_jobs_ffi(parser: &Parser) { - hup_jobs(&parser.jobs()) -} -impl JobListFFI { - fn empty(&self) -> bool { - self.0.is_empty() - } -} - -fn new_tty_transfer() -> Box { - Box::new(TtyTransfer::new()) -} - -impl TtyTransfer { - #[allow(clippy::wrong_self_convention)] - fn to_job_group_ffi(&mut self, jg: &JobGroupRefFfi) { - self.to_job_group(&jg.0); - } -} diff --git a/fish-rust/src/reader.rs b/fish-rust/src/reader.rs index 7567b474d..78245598c 100644 --- a/fish-rust/src/reader.rs +++ b/fish-rust/src/reader.rs @@ -2988,7 +2988,6 @@ fn handle_readline_command(&mut self, c: ReadlineCmd, rls: &mut ReadlineLoopStat rl::SelfInsert | rl::SelfInsertNotFirst | rl::FuncAnd | rl::FuncOr => { panic!("should have been handled by inputter_t::readch"); } - _ => panic!("unhandled readline command {c:?}"), } } } @@ -4188,7 +4187,6 @@ fn fill_history_pager( index = match direction { SearchDirection::Forward => self.history_pager_history_index_start, SearchDirection::Backward => self.history_pager_history_index_end, - _ => unreachable!(), } } HistoryPagerInvocation::Refresh => { @@ -4227,7 +4225,6 @@ fn fill_history_pager( zelf.history_pager_history_index_start = index; zelf.history_pager_history_index_end = result.final_index; } - _ => unreachable!(), }; zelf.pager.extra_progress_text = if result.have_more_results { wgettext!("Search again for more results") @@ -5105,7 +5102,6 @@ fn compute_and_apply_completions(&mut self, c: ReadlineCmd, rls: &mut ReadlineLo ); return; } - _ => unreachable!(), } // Construct a copy of the string from the beginning of the command substitution @@ -5367,28 +5363,3 @@ fn event_is_normal_char(evt: &CharEvent) -> bool { let c = evt.get_char(); !fish_reserved_codepoint(c) && c > char::from(31) && c != char::from(127) } - -#[cxx::bridge] -mod reader_ffi { - extern "Rust" { - #[cxx_name = "check_exit_loop_maybe_warning"] - fn check_exit_loop_maybe_warning_ffi() -> bool; - fn reader_test_and_clear_interrupted() -> i32; - fn reader_init(); - fn restore_term_mode(); - fn reader_schedule_prompt_repaint(); - fn reader_reading_interrupted() -> i32; - fn reader_reset_interrupted(); - #[cxx_name = "reader_current_data"] - fn reader_current_data_ffi() -> *mut u8; - } -} - -fn check_exit_loop_maybe_warning_ffi() -> bool { - check_exit_loop_maybe_warning(None) -} - -fn reader_current_data_ffi() -> *mut u8 { - let data = current_data().unwrap(); - data as *mut ReaderData as *mut u8 -} diff --git a/fish-rust/src/redirection.rs b/fish-rust/src/redirection.rs index 410f75a71..e0903e91d 100644 --- a/fish-rust/src/redirection.rs +++ b/fish-rust/src/redirection.rs @@ -1,80 +1,35 @@ //! This file supports specifying and applying redirections. -use crate::ffi::wcharz_t; use crate::io::IoChain; use crate::wchar::prelude::*; -use crate::wchar_ffi::WCharToFFI; use crate::wutil::fish_wcstoi; -use cxx::{CxxVector, CxxWString, SharedPtr, UniquePtr}; use libc::{c_int, O_APPEND, O_CREAT, O_EXCL, O_RDONLY, O_TRUNC, O_WRONLY}; use std::os::fd::RawFd; -#[cxx::bridge] -mod redirection_ffi { - extern "C++" { - include!("wutil.h"); - type wcharz_t = super::wcharz_t; - } - - #[derive(Debug)] - enum RedirectionMode { - overwrite, // normal redirection: > file.txt - append, // appending redirection: >> file.txt - input, // input redirection: < file.txt - fd, // fd redirection: 2>&1 - noclob, // noclobber redirection: >? file.txt - } - - extern "Rust" { - type RedirectionSpec; - - fn is_close(self: &RedirectionSpec) -> bool; - #[cxx_name = "get_target_as_fd"] - fn get_target_as_fd_ffi(self: &RedirectionSpec) -> SharedPtr; - fn oflags(self: &RedirectionSpec) -> i32; - - fn fd(self: &RedirectionSpec) -> i32; - fn mode(self: &RedirectionSpec) -> RedirectionMode; - fn target(self: &RedirectionSpec) -> UniquePtr; - fn new_redirection_spec( - fd: i32, - mode: RedirectionMode, - target: wcharz_t, - ) -> Box; - - type RedirectionSpecListFfi; - fn new_redirection_spec_list() -> Box; - fn size(self: &RedirectionSpecListFfi) -> usize; - fn at(self: &RedirectionSpecListFfi, offset: usize) -> *const RedirectionSpec; - fn push_back(self: &mut RedirectionSpecListFfi, spec: Box); - fn clone(self: &RedirectionSpecListFfi) -> Box; - } - - /// A type that represents the action dup2(src, target). - /// If target is negative, this represents close(src). - /// Note none of the fds here are considered 'owned'. - #[derive(Clone, Copy)] - struct Dup2Action { - src: i32, - target: i32, - } - - /// A class representing a sequence of basic redirections. - #[derive(Default)] - struct Dup2List { - /// The list of actions. - pub actions: Vec, - } - - extern "Rust" { - fn get_actions(self: &Dup2List) -> &[Dup2Action]; - #[cxx_name = "dup2_list_resolve_chain"] - fn dup2_list_resolve_chain_ffi(io_chain: &CxxVector) -> Dup2List; - fn fd_for_target_fd(self: &Dup2List, target: i32) -> i32; - } +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub enum RedirectionMode { + overwrite, // normal redirection: > file.txt + append, // appending redirection: >> file.txt + input, // input redirection: < file.txt + fd, // fd redirection: 2>&1 + noclob, // noclobber redirection: >? file.txt } -pub use redirection_ffi::{Dup2Action, Dup2List, RedirectionMode}; +/// A type that represents the action dup2(src, target). +/// If target is negative, this represents close(src). +/// Note none of the fds here are considered 'owned'. +#[derive(Clone, Copy)] +pub struct Dup2Action { + pub src: i32, + pub target: i32, +} + +/// A class representing a sequence of basic redirections. +#[derive(Default)] +pub struct Dup2List { + /// The list of actions. + pub actions: Vec, +} impl RedirectionMode { /// The open flags for this redirection mode. @@ -126,12 +81,6 @@ pub fn is_close(&self) -> bool { pub fn get_target_as_fd(&self) -> Option { fish_wcstoi(&self.target).ok() } - fn get_target_as_fd_ffi(&self) -> SharedPtr { - match self.get_target_as_fd() { - Some(fd) => SharedPtr::new(fd), - None => SharedPtr::null(), - } - } /// \return the open flags for this redirection. pub fn oflags(&self) -> c_int { @@ -148,44 +97,10 @@ fn fd(&self) -> RawFd { fn mode(&self) -> RedirectionMode { self.mode } - - fn target(&self) -> UniquePtr { - self.target.to_ffi() - } -} - -fn new_redirection_spec(fd: i32, mode: RedirectionMode, target: wcharz_t) -> Box { - Box::new(RedirectionSpec { - fd, - mode, - target: target.into(), - }) } pub type RedirectionSpecList = Vec; -struct RedirectionSpecListFfi(RedirectionSpecList); - -fn new_redirection_spec_list() -> Box { - Box::new(RedirectionSpecListFfi(Vec::new())) -} - -impl RedirectionSpecListFfi { - fn size(&self) -> usize { - self.0.len() - } - fn at(&self, offset: usize) -> *const RedirectionSpec { - &self.0[offset] - } - #[allow(clippy::boxed_local)] - fn push_back(&mut self, spec: Box) { - self.0.push(*spec) - } - fn clone(&self) -> Box { - Box::new(RedirectionSpecListFfi(self.0.clone())) - } -} - /// Produce a dup_fd_list_t from an io_chain. This may not be called before fork(). /// The result contains the list of fd actions (dup2 and close), as well as the list /// of fds opened. @@ -201,18 +116,6 @@ pub fn dup2_list_resolve_chain(io_chain: &IoChain) -> Dup2List { result } -fn dup2_list_resolve_chain_ffi(io_chain: &CxxVector) -> Dup2List { - let mut result = Dup2List { actions: vec![] }; - for io in io_chain { - if io.src < 0 { - result.add_close(io.target) - } else { - result.add_dup2(io.src, io.target) - } - } - result -} - impl Dup2List { pub fn new() -> Self { Default::default() diff --git a/fish-rust/src/screen.rs b/fish-rust/src/screen.rs index 74c143b7c..32528ccc2 100644 --- a/fish-rust/src/screen.rs +++ b/fish-rust/src/screen.rs @@ -11,11 +11,9 @@ use std::collections::LinkedList; use std::ffi::{CStr, CString}; use std::io::Write; -use std::pin::Pin; use std::sync::atomic::{AtomicU32, Ordering}; use std::sync::Mutex; -use cxx::{CxxVector, CxxWString, UniquePtr}; use libc::{ONLCR, STDERR_FILENO, STDOUT_FILENO}; use crate::common::{ @@ -24,17 +22,16 @@ ScopeGuarding, }; use crate::curses::{term, tparm0, tparm1}; -use crate::env::{EnvStackRef, Environment, TERM_HAS_XN}; +use crate::env::{Environment, TERM_HAS_XN}; use crate::fallback::fish_wcwidth; use crate::flog::FLOGF; #[allow(unused_imports)] use crate::future::IsSomeAnd; use crate::global_safety::RelaxedAtomicBool; -use crate::highlight::{HighlightColorResolver, HighlightSpecListFFI}; +use crate::highlight::HighlightColorResolver; use crate::output::Outputter; use crate::termsize::{termsize_last, Termsize}; use crate::wchar::prelude::*; -use crate::wchar_ffi::{AsWstr, WCharFromFFI, WCharToFFI}; use crate::wcstringutil::string_prefixes_string; use crate::{highlight::HighlightSpec, wcstringutil::fish_wcwidth_visible}; @@ -1838,146 +1835,3 @@ fn compute_layout( result.autosuggestion = autosuggestion.to_owned(); result } - -#[allow(clippy::needless_lifetimes)] // add odds with cxx -#[cxx::bridge] -mod screen_ffi { - extern "C++" { - include!("screen.h"); - include!("pager.h"); - include!("highlight.h"); - pub type HighlightSpec = crate::highlight::HighlightSpec; - pub type HighlightSpecListFFI = crate::highlight::HighlightSpecListFFI; - // pub type pager_t = crate::ffi::pager_t; - // pub type page_rendering_t = crate::ffi::page_rendering_t; - pub type Pager = crate::pager::Pager; - pub type PageRendering = crate::pager::PageRendering; - pub type highlight_spec_t = crate::ffi::highlight_spec_t; - } - extern "Rust" { - type Line; - fn new_line() -> Box; - #[cxx_name = "append"] - fn append_ffi(&mut self, character: u32, highlight: &HighlightSpec); - #[cxx_name = "append_str"] - fn append_str_ffi(&mut self, txt: &CxxWString, highlight: &HighlightSpec); - fn append_line(&mut self, line: &Line); - fn text_characters_ffi(&self) -> UniquePtr; - } - extern "Rust" { - type ScreenData; - fn new_screen_data() -> Box; - #[cxx_name = "create_line"] - fn create_line_ffi(&mut self, index: usize) -> *mut Line; - fn add_line(&mut self) -> &mut Line; - fn insert_line_at_index(&mut self, index: usize) -> &mut Line; - fn empty(&self) -> bool; - fn resize(&mut self, size: usize); - fn line_count(&self) -> usize; - fn line_ffi(&self, index: usize) -> *const Line; - } - extern "Rust" { - type Screen; - fn new_screen() -> Box; - #[cxx_name = "write"] - fn write_ffi( - &mut self, - left_prompt: &CxxWString, - right_prompt: &CxxWString, - commandline: &CxxWString, - explicit_len: usize, - colors: &HighlightSpecListFFI, - indent: &CxxVector, - cursor_pos: usize, - vars: *mut u8, - pager: Pin<&mut Pager>, - page_rendering: Pin<&mut PageRendering>, - cursor_is_within_pager: bool, - ); - fn reset_abandoning_line(&mut self, screen_width: usize); - fn cursor_is_wrapped_to_own_line(&self) -> bool; - fn save_status(&mut self); - fn reset_line(&mut self, repaint_prompt: bool); - fn autosuggestion_is_truncated(&self) -> bool; - } - extern "Rust" { - type PromptLayout; - } - extern "Rust" { - type LayoutCache; - } - extern "Rust" { - #[cxx_name = "screen_clear"] - fn screen_clear_ffi() -> UniquePtr; - fn screen_force_clear_to_end(); - } -} -fn new_line() -> Box { - Box::new(Line::new()) -} -impl Line { - fn append_ffi(&mut self, character: u32, highlight: &HighlightSpec) { - self.append(char::try_from(character).unwrap(), *highlight) - } - fn append_str_ffi(&mut self, txt: &CxxWString, highlight: &HighlightSpec) { - self.append_str(&txt.from_ffi(), *highlight) - } - fn text_characters_ffi(&self) -> UniquePtr { - WString::from_iter(self.text.iter().map(|hc| hc.character)).to_ffi() - } -} -fn new_screen_data() -> Box { - Box::new(ScreenData::new()) -} -impl ScreenData { - fn create_line_ffi(&mut self, index: usize) -> *mut Line { - self.create_line(index); - self.line_mut(index) as *mut Line - } - fn empty(&self) -> bool { - self.is_empty() - } - fn line_ffi(&self, index: usize) -> *const Line { - self.line(index) as *const Line - } -} -fn new_screen() -> Box { - Box::new(Screen::new()) -} -impl Screen { - fn write_ffi( - &mut self, - left_prompt: &CxxWString, - right_prompt: &CxxWString, - commandline: &CxxWString, - explicit_len: usize, - colors: &HighlightSpecListFFI, - indent: &CxxVector, - cursor_pos: usize, - vars: *mut u8, - pager: Pin<&mut Pager>, - page_rendering: Pin<&mut PageRendering>, - cursor_is_within_pager: bool, - ) { - let vars = unsafe { Box::from_raw(vars as *mut EnvStackRef) }; - self.write( - left_prompt.as_wstr(), - right_prompt.as_wstr(), - commandline.as_wstr(), - explicit_len, - &colors.0, - indent.as_slice(), - cursor_pos, - vars.as_ref().as_ref().get_ref(), - pager.get_mut(), - page_rendering.get_mut(), - cursor_is_within_pager, - ); - } - fn autosuggestion_is_truncated(&self) -> bool { - self.autosuggestion_is_truncated - } -} -fn screen_clear_ffi() -> UniquePtr { - screen_clear().to_ffi() -} diff --git a/fish-rust/src/signal.rs b/fish-rust/src/signal.rs index 45a1a8268..eafe17a62 100644 --- a/fish-rust/src/signal.rs +++ b/fish-rust/src/signal.rs @@ -7,68 +7,10 @@ use crate::termsize::TermsizeContainer; use crate::topic_monitor::{generation_t, topic_monitor_principal, topic_t, GenerationsList}; use crate::wchar::prelude::*; -use crate::wchar_ffi::{AsWstr, WCharToFFI}; use crate::wutil::{fish_wcstoi, perror}; -use cxx::{CxxWString, UniquePtr}; use errno::{errno, set_errno}; use std::sync::atomic::{AtomicI32, Ordering}; -#[cxx::bridge] -mod signal_ffi { - extern "Rust" { - fn signal_set_handlers(interactive: bool); - fn signal_set_handlers_once(interactive: bool); - #[cxx_name = "signal_handle"] - fn signal_handle_ffi(sig: i32); - fn signal_unblock_all(); - - #[cxx_name = "sig2wcs"] - fn sig2wcs_ffi(sig: i32) -> UniquePtr; - - #[cxx_name = "wcs2sig"] - fn wcs2sig_ffi(sig: &CxxWString) -> i32; - - #[cxx_name = "signal_get_desc"] - fn signal_get_desc_ffi(sig: i32) -> UniquePtr; - - fn signal_check_cancel() -> i32; - fn signal_clear_cancel(); - fn signal_reset_handlers(); - - } - extern "Rust" { - type SigChecker; - fn new_sighupint_checker() -> Box; - fn check(&mut self) -> bool; - } -} - -fn sig2wcs_ffi(sig: i32) -> UniquePtr { - Signal::new(sig).name().to_ffi() -} - -fn wcs2sig_ffi(sig: &CxxWString) -> i32 { - if let Some(sig) = Signal::parse(sig.as_wstr()) { - sig.code() - } else { - -1 - } -} - -fn signal_get_desc_ffi(sig: i32) -> UniquePtr { - Signal::new(sig).desc().to_ffi() -} - -fn signal_handle_ffi(sig: i32) { - signal_handle(Signal::new(sig)); -} - -// This is extern "C" for FFI purposes, as this is used after fork(). -#[no_mangle] -pub extern "C" fn get_signals_with_handlers_ffi(set: *mut libc::sigset_t) { - get_signals_with_handlers(unsafe { &mut *set }); -} - /// Store the "main" pid. This allows us to reliably determine if we are in a forked child. static MAIN_PID: AtomicI32 = AtomicI32::new(0); diff --git a/fish-rust/src/termsize.rs b/fish-rust/src/termsize.rs index 17bfe4f68..f9e8c947f 100644 --- a/fish-rust/src/termsize.rs +++ b/fish-rust/src/termsize.rs @@ -2,6 +2,7 @@ use crate::common::assert_sync; use crate::env::{EnvMode, EnvVar, Environment}; use crate::flog::FLOG; +use crate::parser::Parser; #[cfg(test)] use crate::tests::prelude::*; use crate::wchar::prelude::*; @@ -9,36 +10,16 @@ use std::sync::atomic::{AtomicBool, AtomicU32, Ordering}; use std::sync::Mutex; -#[cxx::bridge] -mod termsize_ffi { - #[cxx_name = "termsize_t"] - #[derive(Copy, Clone, Debug, PartialEq, Eq)] - pub struct Termsize { - /// Width of the terminal, in columns. - // TODO: Change to u32 - pub width: isize, +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub struct Termsize { + /// Width of the terminal, in columns. + // TODO: Change to u32 + pub width: isize, - /// Height of the terminal, in rows. - // TODO: Change to u32 - pub height: isize, - } - - extern "C++" { - include!("env.h"); - include!("parser.h"); - #[cxx_name = "EnvStackRef"] - type EnvStackRefFFI = crate::env::EnvStackRefFFI; - type Parser = crate::parser::Parser; - } - - extern "Rust" { - pub fn termsize_default() -> Termsize; - pub fn termsize_last() -> Termsize; - pub fn termsize_invalidate_tty(); - pub fn termsize_update(parser: &Parser) -> Termsize; - } + /// Height of the terminal, in rows. + // TODO: Change to u32 + pub height: isize, } -pub use termsize_ffi::Termsize; // A counter which is incremented every SIGWINCH, or when the tty is otherwise invalidated. static TTY_TERMSIZE_GEN_COUNT: AtomicU32 = AtomicU32::new(0); @@ -290,7 +271,6 @@ pub fn termsize_invalidate_tty() { TermsizeContainer::invalidate_tty(); } -use self::termsize_ffi::Parser; #[test] #[serial] fn test_termsize() { diff --git a/fish-rust/src/tests/fd_monitor.rs b/fish-rust/src/tests/fd_monitor.rs index 43c2df313..17115b5a7 100644 --- a/fish-rust/src/tests/fd_monitor.rs +++ b/fish-rust/src/tests/fd_monitor.rs @@ -85,7 +85,6 @@ fn callback(&self, fd: &mut AutoCloseFd, reason: ItemWakeReason) { self.length_read.fetch_add(amt as usize, Ordering::Relaxed); was_closed = amt == 0; } - _ => unreachable!(), } self.total_calls.fetch_add(1, Ordering::Relaxed); diff --git a/fish-rust/src/threads.rs b/fish-rust/src/threads.rs index 10fae51dd..5b2900e15 100644 --- a/fish-rust/src/threads.rs +++ b/fish-rust/src/threads.rs @@ -45,72 +45,6 @@ impl FloggableDebug for ThreadId {} static NOTIFY_SIGNALLER: once_cell::sync::Lazy = once_cell::sync::Lazy::new(crate::fd_monitor::FdEventSignaller::new); -#[cxx::bridge] -mod ffi { - unsafe extern "C++" { - include!("callback.h"); - - #[rust_name = "CppCallback"] - type callback_t; - fn invoke(&self) -> *const u8; - fn invoke_with_param(&self, param: *const u8) -> *const u8; - } - - extern "Rust" { - #[cxx_name = "ASSERT_IS_MAIN_THREAD"] - fn assert_is_main_thread(); - #[cxx_name = "ASSERT_IS_BACKGROUND_THREAD"] - fn assert_is_background_thread(); - #[cxx_name = "ASSERT_IS_NOT_FORKED_CHILD"] - fn assert_is_not_forked_child(); - fn configure_thread_assertions_for_testing(); - fn is_main_thread() -> bool; - fn is_forked_child() -> bool; - } - - extern "Rust" { - #[cxx_name = "make_detached_pthread"] - fn spawn_ffi(callback: &SharedPtr) -> bool; - fn asan_before_exit(); - fn asan_maybe_exit(code: i32); - } - - extern "Rust" { - fn iothread_port() -> i32; - #[cxx_name = "iothread_service_main"] - fn iothread_service_main_ffi(ctx: *mut u8); - #[cxx_name = "iothread_perform"] - fn iothread_perform_ffi(callback: &SharedPtr); - #[cxx_name = "iothread_perform_cantwait"] - fn iothread_perform_cant_wait_ffi(callback: &SharedPtr); - } -} - -fn iothread_service_main_ffi(ctx: *mut u8) { - let ctx = unsafe { &mut *(ctx as *mut ReaderData) }; - iothread_service_main(ctx); -} - -pub use ffi::CppCallback; -unsafe impl Send for ffi::CppCallback {} -unsafe impl Sync for ffi::CppCallback {} - -fn iothread_perform_ffi(callback: &cxx::SharedPtr) { - let callback = callback.clone(); - - iothread_perform(move || { - callback.invoke(); - }); -} - -fn iothread_perform_cant_wait_ffi(callback: &cxx::SharedPtr) { - let callback = callback.clone(); - - iothread_perform_cant_wait(move || { - callback.invoke(); - }); -} - /// A [`ThreadPool`] work request. type WorkItem = Box; @@ -193,10 +127,6 @@ fn not_background_thread() -> ! { } } -pub fn configure_thread_assertions_for_testing() { - THREAD_ASSERTS_CFG_FOR_TESTING.store(true, Ordering::Relaxed); -} - pub fn is_forked_child() -> bool { IS_FORKED_PROC.load(Ordering::Relaxed) } @@ -278,13 +208,6 @@ pub fn spawn(callback: F) -> bool { result } -fn spawn_ffi(callback: &cxx::SharedPtr) -> bool { - let callback = callback.clone(); - spawn(move || { - callback.invoke(); - }) -} - /// Exits calling onexit handlers if running under ASAN, otherwise does nothing. /// /// This function is always defined but is a no-op if not running under ASAN. This is to make it diff --git a/fish-rust/src/timer.rs b/fish-rust/src/timer.rs index cbebe367b..15ee376b6 100644 --- a/fish-rust/src/timer.rs +++ b/fish-rust/src/timer.rs @@ -17,15 +17,6 @@ use std::io::Write; use std::time::{Duration, Instant}; -#[cxx::bridge] -mod timer_ffi { - extern "Rust" { - type PrintElapsedOnDropFfi; - #[cxx_name = "push_timer"] - fn push_timer_ffi(enabled: bool) -> Box; - } -} - enum Unit { Minutes, Seconds, @@ -52,22 +43,6 @@ pub fn push_timer(enabled: bool) -> Option { }) } -/// cxx bridge does not support UniquePtr so we can't use a null UniquePtr to -/// represent a None, and cxx bridge does not support Box> so we need to make -/// our own wrapper type that incorporates the Some/None states directly into it. -#[allow(clippy::large_enum_variant)] -enum PrintElapsedOnDropFfi { - Some(PrintElapsedOnDrop), - None, -} - -fn push_timer_ffi(enabled: bool) -> Box { - Box::new(match push_timer(enabled) { - Some(t) => PrintElapsedOnDropFfi::Some(t), - None => PrintElapsedOnDropFfi::None, - }) -} - /// An enumeration of supported libc rusage types used by [`getrusage()`]. /// NB: RUSAGE_THREAD is not supported on macOS. enum RUsage { diff --git a/fish-rust/src/tokenizer.rs b/fish-rust/src/tokenizer.rs index 92ab3e91f..71dd98a75 100644 --- a/fish-rust/src/tokenizer.rs +++ b/fish-rust/src/tokenizer.rs @@ -2,172 +2,110 @@ //! extended to support marks, tokenizing multiple strings and disposing of unused string segments. use crate::common::valid_var_name_char; -use crate::ffi::wcharz_t; use crate::future_feature_flags::{feature_test, FeatureFlag}; use crate::parse_constants::SOURCE_OFFSET_INVALID; use crate::redirection::RedirectionMode; use crate::wchar::prelude::*; -use crate::wchar_ffi::{wchar_t, AsWstr, WCharToFFI}; -use cxx::{CxxWString, SharedPtr, UniquePtr}; use libc::{c_int, STDIN_FILENO, STDOUT_FILENO}; use std::ops::{BitAnd, BitAndAssign, BitOr, BitOrAssign, Not}; use std::os::fd::RawFd; -#[cxx::bridge] -mod tokenizer_ffi { - extern "C++" { - include!("wutil.h"); - include!("redirection.h"); - type wcharz_t = super::wcharz_t; - type RedirectionMode = super::RedirectionMode; - } - - /// Token types. XXX Why this isn't ParseTokenType, I'm not really sure. - #[derive(Debug)] - enum TokenType { - /// Error reading token - error, - /// String token - string, - /// Pipe token - pipe, - /// && token - andand, - /// || token - oror, - /// End token (semicolon or newline, not literal end) - end, - /// redirection token - redirect, - /// send job to bg token - background, - /// comment token - comment, - } - - #[derive(Debug, Eq, PartialEq)] - enum TokenizerError { - none, - unterminated_quote, - unterminated_subshell, - unterminated_slice, - unterminated_escape, - invalid_redirect, - invalid_pipe, - invalid_pipe_ampersand, - closing_unopened_subshell, - illegal_slice, - closing_unopened_brace, - unterminated_brace, - expected_pclose_found_bclose, - expected_bclose_found_pclose, - } - - extern "Rust" { - fn tokenizer_get_error_message(err: TokenizerError) -> UniquePtr; - } - - struct Tok { - // Offset of the token. - offset: u32, - // Length of the token. - length: u32, - - // If an error, this is the offset of the error within the token. A value of 0 means it occurred - // at 'offset'. - error_offset_within_token: u32, - error_length: u32, - - // If an error, this is the error code. - error: TokenizerError, - - // The type of the token. - type_: TokenType, - } - // TODO static_assert(sizeof(Tok) <= 32, "Tok expected to be 32 bytes or less"); - - extern "Rust" { - fn location_in_or_at_end_of_source_range(self: &Tok, loc: usize) -> bool; - #[cxx_name = "get_source"] - fn get_source_ffi(self: &Tok, str: &CxxWString) -> UniquePtr; - } - - extern "Rust" { - type Tokenizer; - fn new_tokenizer(start: wcharz_t, flags: u8) -> Box; - #[cxx_name = "next"] - fn next_ffi(self: &mut Tokenizer) -> UniquePtr; - #[cxx_name = "text_of"] - fn text_of_ffi(self: &Tokenizer, tok: &Tok) -> UniquePtr; - #[cxx_name = "is_token_delimiter"] - fn is_token_delimiter_ffi(c: wchar_t, next: SharedPtr) -> bool; - } - - extern "Rust" { - #[cxx_name = "tok_command"] - fn tok_command_ffi(str: &CxxWString) -> UniquePtr; - } - - /// Struct wrapping up a parsed pipe or redirection. - struct PipeOrRedir { - // The redirected fd, or -1 on overflow. - // In the common case of a pipe, this is 1 (STDOUT_FILENO). - // For example, in the case of "3>&1" this will be 3. - fd: i32, - - // Whether we are a pipe (true) or redirection (false). - is_pipe: bool, - - // The redirection mode if the type is redirect. - // Ignored for pipes. - mode: RedirectionMode, - - // Whether, in addition to this redirection, stderr should also be dup'd to stdout - // For example &| or &> - stderr_merge: bool, - - // Number of characters consumed when parsing the string. - consumed: usize, - } - - extern "Rust" { - fn pipe_or_redir_from_string(buff: wcharz_t) -> UniquePtr; - fn is_valid(self: &PipeOrRedir) -> bool; - fn oflags(self: &PipeOrRedir) -> i32; - fn token_type(self: &PipeOrRedir) -> TokenType; - } - - enum MoveWordStyle { - /// stop at punctuation - Punctuation, - /// stops at path components - PathComponents, - /// stops at whitespace - Whitespace, - } - - /// Our state machine that implements "one word" movement or erasure. - struct MoveWordStateMachine { - state: u8, - style: MoveWordStyle, - } - - extern "Rust" { - fn new_move_word_state_machine(syl: MoveWordStyle) -> Box; - #[cxx_name = "consume_char"] - fn consume_char_ffi(self: &mut MoveWordStateMachine, c: wchar_t) -> bool; - fn reset(self: &mut MoveWordStateMachine); - } - - extern "Rust" { - #[cxx_name = "variable_assignment_equals_pos"] - fn variable_assignment_equals_pos_ffi(txt: &CxxWString) -> SharedPtr; - } +/// Token types. XXX Why this isn't ParseTokenType, I'm not really sure. +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub enum TokenType { + /// Error reading token + error, + /// String token + string, + /// Pipe token + pipe, + /// && token + andand, + /// || token + oror, + /// End token (semicolon or newline, not literal end) + end, + /// redirection token + redirect, + /// send job to bg token + background, + /// comment token + comment, } -pub use tokenizer_ffi::{ - MoveWordStateMachine, MoveWordStyle, PipeOrRedir, Tok, TokenType, TokenizerError, -}; +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +pub enum TokenizerError { + none, + unterminated_quote, + unterminated_subshell, + unterminated_slice, + unterminated_escape, + invalid_redirect, + invalid_pipe, + invalid_pipe_ampersand, + closing_unopened_subshell, + illegal_slice, + closing_unopened_brace, + unterminated_brace, + expected_pclose_found_bclose, + expected_bclose_found_pclose, +} + +pub struct Tok { + // Offset of the token. + pub offset: u32, + // Length of the token. + pub length: u32, + + // If an error, this is the offset of the error within the token. A value of 0 means it occurred + // at 'offset'. + pub error_offset_within_token: u32, + pub error_length: u32, + + // If an error, this is the error code. + pub error: TokenizerError, + + // The type of the token. + pub type_: TokenType, +} +// TODO static_assert(sizeof(Tok) <= 32, "Tok expected to be 32 bytes or less"); + +/// Struct wrapping up a parsed pipe or redirection. +pub struct PipeOrRedir { + // The redirected fd, or -1 on overflow. + // In the common case of a pipe, this is 1 (STDOUT_FILENO). + // For example, in the case of "3>&1" this will be 3. + pub fd: i32, + + // Whether we are a pipe (true) or redirection (false). + pub is_pipe: bool, + + // The redirection mode if the type is redirect. + // Ignored for pipes. + pub mode: RedirectionMode, + + // Whether, in addition to this redirection, stderr should also be dup'd to stdout + // For example &| or &> + pub stderr_merge: bool, + + // Number of characters consumed when parsing the string. + pub consumed: usize, +} + +pub enum MoveWordStyle { + /// stop at punctuation + Punctuation, + /// stops at path components + PathComponents, + /// stops at whitespace + Whitespace, +} + +/// Our state machine that implements "one word" movement or erasure. +pub struct MoveWordStateMachine { + state: u8, + style: MoveWordStyle, +} #[derive(Clone, Copy)] pub struct TokFlags(pub u8); @@ -204,12 +142,6 @@ fn bitor_assign(&mut self, rhs: Self) { /// Make an effort to continue after an error. pub const TOK_CONTINUE_AFTER_ERROR: TokFlags = TokFlags(8); -/// Get the error message for an error \p err. -pub fn tokenizer_get_error_message(err: TokenizerError) -> UniquePtr { - let s: &'static wstr = err.into(); - s.to_ffi() -} - impl From for &'static wstr { #[widestrs] fn from(err: TokenizerError) -> Self { @@ -254,9 +186,6 @@ fn from(err: TokenizerError) -> Self { TokenizerError::expected_bclose_found_pclose => { wgettext!("Unexpected ')' found, expecting '}'") } - _ => { - panic!("Unexpected tokenizer error"); - } } } } @@ -285,9 +214,6 @@ pub fn location_in_or_at_end_of_source_range(self: &Tok, loc: usize) -> bool { pub fn get_source<'a, 'b>(self: &'a Tok, str: &'b wstr) -> &'b wstr { &str[self.offset as usize..(self.offset + self.length) as usize] } - fn get_source_ffi(self: &Tok, str: &CxxWString) -> UniquePtr { - self.get_source(str.as_wstr()).to_ffi() - } pub fn set_offset(&mut self, value: usize) { self.offset = value.try_into().unwrap(); } @@ -359,10 +285,6 @@ pub fn new(start: &wstr, flags: TokFlags) -> Self { } } -fn new_tokenizer(start: wcharz_t, flags: u8) -> Box { - Box::new(Tokenizer::new(start.into(), TokFlags(flags))) -} - impl Iterator for Tokenizer { type Item = Tok; @@ -557,14 +479,6 @@ fn next(&mut self) -> Option { } } } -impl Tokenizer { - fn next_ffi(&mut self) -> UniquePtr { - match self.next() { - Some(tok) => UniquePtr::new(tok), - None => UniquePtr::null(), - } - } -} /// Test if a character is whitespace. Differs from iswspace in that it does not consider a /// newline to be whitespace. @@ -581,9 +495,6 @@ impl Tokenizer { pub fn text_of(&self, tok: &Tok) -> &wstr { tok.get_source(&self.start) } - fn text_of_ffi(&self, tok: &Tok) -> UniquePtr { - self.text_of(tok).to_ffi() - } /// Return an error token and mark that we no longer have a next token. fn call_error( @@ -950,14 +861,7 @@ pub fn is_token_delimiter(c: char, next: Option) -> bool { c == '(' || !tok_is_string_character(c, next) } -fn is_token_delimiter_ffi(c: wchar_t, next: SharedPtr) -> bool { - is_token_delimiter( - c.try_into().unwrap(), - next.as_ref().map(|c| (*c).try_into().unwrap()), - ) -} - -/// \return the_ffi first token from the string, skipping variable assignments like A=B. +/// \return the first token from the string, skipping variable assignments like A=B. pub fn tok_command(str: &wstr) -> WString { let mut t = Tokenizer::new(str, TokFlags(0)); while let Some(token) = t.next() { @@ -972,9 +876,6 @@ pub fn tok_command(str: &wstr) -> WString { } WString::new() } -fn tok_command_ffi(str: &CxxWString) -> UniquePtr { - tok_command(str.as_wstr()).to_ffi() -} impl TryFrom<&wstr> for PipeOrRedir { type Error = (); @@ -1139,13 +1040,6 @@ fn try_from(buff: &wstr) -> Result { } } -fn pipe_or_redir_from_string(buff: wcharz_t) -> UniquePtr { - match PipeOrRedir::try_from(Into::<&wstr>::into(buff)) { - Ok(p) => UniquePtr::new(p), - Err(()) => UniquePtr::null(), - } -} - impl PipeOrRedir { /// \return the oflags (as in open(2)) for this redirection. pub fn oflags(&self) -> c_int { @@ -1197,12 +1091,8 @@ pub fn consume_char(&mut self, c: char) -> bool { MoveWordStyle::Punctuation => self.consume_char_punctuation(c), MoveWordStyle::PathComponents => self.consume_char_path_components(c), MoveWordStyle::Whitespace => self.consume_char_whitespace(c), - _ => panic!(), } } - pub fn consume_char_ffi(&mut self, c: wchar_t) -> bool { - self.consume_char(c.try_into().unwrap()) - } pub fn reset(&mut self) { self.state = 0; @@ -1411,10 +1301,3 @@ pub fn variable_assignment_equals_pos(txt: &wstr) -> Option { } None } - -fn variable_assignment_equals_pos_ffi(txt: &CxxWString) -> SharedPtr { - match variable_assignment_equals_pos(txt.as_wstr()) { - Some(p) => SharedPtr::new(p), - None => SharedPtr::null(), - } -} diff --git a/fish-rust/src/topic_monitor.rs b/fish-rust/src/topic_monitor.rs index a0069f0b2..4a0f2f034 100644 --- a/fish-rust/src/topic_monitor.rs +++ b/fish-rust/src/topic_monitor.rs @@ -362,11 +362,6 @@ unsafe impl Sync for topic_monitor_t {} /// Do not attempt to move this into a lazy_static, it must be accessed from a signal handler. static mut s_principal: *const topic_monitor_t = std::ptr::null(); -/// Create a new topic monitor. Exposed for the FFI. -pub fn new_topic_monitor() -> Box { - Box::default() -} - impl topic_monitor_t { /// Initialize the principal monitor, and return it. /// This should be called only on the main thread. @@ -374,7 +369,7 @@ pub fn initialize() -> &'static Self { unsafe { if s_principal.is_null() { // We simply leak. - s_principal = Box::into_raw(new_topic_monitor()); + s_principal = Box::into_raw(Box::default()); } &*s_principal } diff --git a/fish-rust/src/trace.rs b/fish-rust/src/trace.rs index 5866caa6f..1d1fcefc6 100644 --- a/fish-rust/src/trace.rs +++ b/fish-rust/src/trace.rs @@ -1,30 +1,6 @@ use crate::flog::log_extra_to_flog_file; use crate::parser::Parser; -use crate::{ - common::escape, - ffi::{wcharz_t, wcstring_list_ffi_t}, - global_safety::RelaxedAtomicBool, - wchar::prelude::*, - wchar_ffi::WCharFromFFI, -}; - -#[cxx::bridge] -mod trace_ffi { - extern "C++" { - include!("wutil.h"); - include!("parser.h"); - type wcstring_list_ffi_t = super::wcstring_list_ffi_t; - type wcharz_t = super::wcharz_t; - type Parser = crate::parser::Parser; - } - - extern "Rust" { - fn trace_set_enabled(do_enable: bool); - fn trace_enabled(parser: &Parser) -> bool; - #[cxx_name = "trace_argv"] - fn trace_argv_ffi(parser: &Parser, command: wcharz_t, args: &wcstring_list_ffi_t); - } -} +use crate::{common::escape, global_safety::RelaxedAtomicBool, wchar::prelude::*}; static DO_TRACE: RelaxedAtomicBool = RelaxedAtomicBool::new(false); @@ -43,14 +19,6 @@ pub fn trace_enabled(parser: &Parser) -> bool { /// Trace an "argv": a list of arguments where the first is the command. // Allow the `&Vec` parameter as this function only exists temporarily for the FFI -#[allow(clippy::ptr_arg)] -fn trace_argv_ffi(parser: &Parser, command: wcharz_t, args: &wcstring_list_ffi_t) { - let command: WString = command.into(); - let args: Vec = args.from_ffi(); - let args_ref: Vec<&wstr> = args.iter().map(WString::as_utfstr).collect(); - trace_argv(parser, command.as_utfstr(), &args_ref); -} - pub fn trace_argv>(parser: &Parser, command: &wstr, args: &[S]) { // Format into a string to prevent interleaving with flog in other threads. // Add the + prefix. @@ -69,12 +37,6 @@ pub fn trace_argv>(parser: &Parser, command: &wstr, args: &[S]) { log_extra_to_flog_file(&trace_text); } -pub fn trace_if_enabled_ffi>(parser: &Parser, command: &wstr, args: &[S]) { - if trace_enabled(parser) { - trace_argv(parser, command, args); - } -} - /// Convenience helper to trace a single command if tracing is enabled. pub fn trace_if_enabled(parser: &Parser, command: &wstr) { if trace_enabled(parser) { diff --git a/fish-rust/src/universal_notifier/mod.rs b/fish-rust/src/universal_notifier/mod.rs index 10421a4c9..1b4e811bb 100644 --- a/fish-rust/src/universal_notifier/mod.rs +++ b/fish-rust/src/universal_notifier/mod.rs @@ -71,39 +71,3 @@ pub fn create_notifier() -> Box { pub fn default_notifier() -> &'static dyn UniversalNotifier { DEFAULT_NOTIFIER.get_or_init(create_notifier).as_ref() } - -struct UniversalNotifierFFI(&'static dyn UniversalNotifier); -fn default_notifier_ffi() -> Box { - Box::new(UniversalNotifierFFI(default_notifier())) -} - -impl UniversalNotifierFFI { - fn post_notification(&self) { - self.0.post_notification(); - } - - fn notification_fd_ffi(&self) -> i32 { - self.0.notification_fd().unwrap_or(-1) - } - - fn notification_fd_became_readable_ffi(&self, fd: i32) -> bool { - self.0.notification_fd_became_readable(fd) - } -} - -#[cxx::bridge] -mod ffi { - extern "Rust" { - type UniversalNotifierFFI; - #[cxx_name = "default_notifier"] - fn default_notifier_ffi() -> Box; - - fn post_notification(&self); - - #[cxx_name = "notification_fd"] - fn notification_fd_ffi(&self) -> i32; - - #[cxx_name = "notification_fd_became_readable"] - fn notification_fd_became_readable_ffi(&self, fd: i32) -> bool; - } -} diff --git a/fish-rust/src/util.rs b/fish-rust/src/util.rs index 6e009c5cd..b3646b078 100644 --- a/fish-rust/src/util.rs +++ b/fish-rust/src/util.rs @@ -1,26 +1,9 @@ //! Generic utilities library. -use crate::ffi::wcharz_t; use crate::wchar::prelude::*; use std::cmp::Ordering; use std::time; -#[cxx::bridge] -mod ffi { - extern "C++" { - include!("wutil.h"); - type wcharz_t = super::wcharz_t; - } - - extern "Rust" { - #[cxx_name = "wcsfilecmp"] - fn wcsfilecmp_ffi(a: wcharz_t, b: wcharz_t) -> i32; - #[cxx_name = "wcsfilecmp_glob"] - fn wcsfilecmp_glob_ffi(a: wcharz_t, b: wcharz_t) -> i32; - fn get_time() -> i64; - } -} - fn ordering_to_int(ord: Ordering) -> i32 { match ord { Ordering::Less => -1, @@ -29,14 +12,6 @@ fn ordering_to_int(ord: Ordering) -> i32 { } } -fn wcsfilecmp_glob_ffi(a: wcharz_t, b: wcharz_t) -> i32 { - ordering_to_int(wcsfilecmp_glob(a.into(), b.into())) -} - -fn wcsfilecmp_ffi(a: wcharz_t, b: wcharz_t) -> i32 { - ordering_to_int(wcsfilecmp(a.into(), b.into())) -} - /// Compares two wide character strings with an (arguably) intuitive ordering. This function tries /// to order strings in a way which is intuitive to humans with regards to sorting strings /// containing numbers. diff --git a/fish-rust/src/wchar_ffi.rs b/fish-rust/src/wchar_ffi.rs deleted file mode 100644 index 56acfe799..000000000 --- a/fish-rust/src/wchar_ffi.rs +++ /dev/null @@ -1,257 +0,0 @@ -//! Interfaces for various FFI string types. -//! -//! We have the following string types for FFI purposes: -//! - CxxWString: the Rust view of a C++ wstring. -//! - W0String: an owning string with a nul terminator. -//! - wcharz_t: a "newtyped" pointer to a nul-terminated string, implemented in C++. -//! This is useful for FFI boundaries, to work around autocxx limitations on pointers. - -pub use crate::ffi::{wchar_t, wcharz_t, wcstring_list_ffi_t, ToCppWString}; -use crate::wchar::{wstr, WString}; -use autocxx::WithinUniquePtr; -use once_cell::sync::Lazy; -pub use widestring::u32cstr; -pub use widestring::U32CString as W0String; - -/// \return the length of a nul-terminated raw string. -pub fn wcslen(str: *const wchar_t) -> usize { - assert!(!str.is_null(), "Null pointer"); - let mut len = 0; - unsafe { - while *str.offset(len) != 0 { - len += 1; - } - } - len as usize -} - -impl wcharz_t { - /// \return the chars of a wcharz_t. - pub fn chars(&self) -> &[char] { - assert!(!self.str_.is_null(), "Null wcharz"); - let data = self.str_ as *const char; - let len = self.size(); - unsafe { std::slice::from_raw_parts(data, len) } - } -} - -/// W0String can be cheaply converted to a wcharz_t (but be mindful that W0String is kept alive). -impl From<&W0String> for wcharz_t { - fn from(w0: &W0String) -> Self { - wcharz_t { - str_: w0.as_ptr() as *const wchar_t, - } - } -} - -/// Convert wcharz_t to an WString. -impl From<&wcharz_t> for WString { - fn from(wcharz: &wcharz_t) -> Self { - WString::from_chars(wcharz.chars()) - } -} - -/// Convert a wstr or WString to a W0String, which contains a nul-terminator. -/// This is useful for passing across FFI boundaries. -/// In general you don't need to use this directly - use the c_str macro below. -pub fn wstr_to_u32string>(str: Str) -> W0String { - W0String::from_ustr(str.as_ref()).expect("String contained intermediate NUL character") -} - -/// Convert a wstr to a nul-terminated pointer. -/// This needs to be a macro so we can create a temporary with the proper lifetime. -macro_rules! c_str { - ($string:expr) => { - crate::wchar_ffi::wstr_to_u32string($string) - .as_ucstr() - .as_ptr() - .cast::() - }; -} - -/// Convert a wstr to a wcharz_t. -macro_rules! wcharz { - ($string:expr) => { - crate::wchar_ffi::wcharz_t { - str_: crate::wchar_ffi::c_str!($string), - } - }; -} - -/// Convert a CxxVector of wcharz_t to a Vec. -pub fn wcharzs_to_vec(wcharz_vec: cxx::UniquePtr>) -> Vec { - wcharz_vec - .as_ref() - .expect("UniquePtr was null") - .iter() - .map(|s| s.into()) - .collect() -} - -pub(crate) use c_str; -pub(crate) use wcharz; - -static EMPTY_WSTRING: Lazy> = - Lazy::new(|| cxx::CxxWString::create(&[])); - -/// \return a reference to a shared empty wstring. -pub fn empty_wstring() -> &'static cxx::CxxWString { - &EMPTY_WSTRING -} - -/// Implement Debug for wcharz_t. -impl std::fmt::Debug for wcharz_t { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - if self.str_.is_null() { - write!(f, "((null))") - } else { - self.chars().fmt(f) - } - } -} - -/// Convert self to a CxxWString, in preparation for using over FFI. -/// We can't use "From" as WString is implemented in an external crate. -pub trait WCharToFFI { - type Target; - fn to_ffi(&self) -> Self::Target; -} - -impl ToCppWString for &wstr { - fn into_cpp(self) -> cxx::UniquePtr { - self.to_ffi() - } -} - -impl ToCppWString for WString { - fn into_cpp(self) -> cxx::UniquePtr { - self.to_ffi() - } -} - -impl ToCppWString for &WString { - fn into_cpp(self) -> cxx::UniquePtr { - self.to_ffi() - } -} - -/// WString may be converted to CxxWString. -impl WCharToFFI for WString { - type Target = cxx::UniquePtr; - fn to_ffi(&self) -> cxx::UniquePtr { - cxx::CxxWString::create(self.as_char_slice()) - } -} - -/// wstr (wide string slices) may be converted to CxxWString. -impl WCharToFFI for wstr { - type Target = cxx::UniquePtr; - fn to_ffi(&self) -> cxx::UniquePtr { - cxx::CxxWString::create(self.as_char_slice()) - } -} - -/// wcharz_t (wide char) may be converted to CxxWString. -impl WCharToFFI for wcharz_t { - type Target = cxx::UniquePtr; - fn to_ffi(&self) -> cxx::UniquePtr { - cxx::CxxWString::create(self.chars()) - } -} - -/// Convert from a slice of something that can be referenced as a wstr, -/// to unique_ptr. -impl> WCharToFFI for [T] { - type Target = cxx::UniquePtr; - fn to_ffi(&self) -> cxx::UniquePtr { - let mut list_ptr = wcstring_list_ffi_t::create(); - for s in self { - list_ptr.as_mut().unwrap().push(s.as_ref()); - } - list_ptr - } -} - -/// Convert from a CxxWString, in preparation for using over FFI. -pub trait WCharFromFFI { - /// Convert from a CxxWString for FFI purposes. - #[allow(clippy::wrong_self_convention)] - fn from_ffi(self) -> Target; -} - -impl WCharFromFFI for &cxx::CxxWString { - fn from_ffi(self) -> WString { - WString::from_chars(self.as_chars()) - } -} - -impl WCharFromFFI for &cxx::UniquePtr { - fn from_ffi(self) -> WString { - WString::from_chars(self.as_chars()) - } -} - -impl WCharFromFFI for &cxx::SharedPtr { - fn from_ffi(self) -> WString { - WString::from_chars(self.as_chars()) - } -} - -impl WCharFromFFI> for &cxx::UniquePtr { - fn from_ffi(self) -> Vec { - self.as_bytes().to_vec() - } -} - -impl WCharFromFFI> for &cxx::SharedPtr { - fn from_ffi(self) -> Vec { - self.as_bytes().to_vec() - } -} - -impl WCharFromFFI for &wcharz_t { - fn from_ffi(self) -> WString { - self.into() - } -} - -/// Convert wcstring_list_ffi_t to Vec. -impl WCharFromFFI> for &wcstring_list_ffi_t { - fn from_ffi(self) -> Vec { - let count: usize = self.size(); - (0..count).map(|i| self.at(i).from_ffi()).collect() - } -} - -/// Convert from the type we get back for C++ functions which return wcstring_list_ffi_t. -impl WCharFromFFI> for T -where - T: autocxx::moveit::new::New, -{ - fn from_ffi(self) -> Vec { - self.within_unique_ptr().as_ref().unwrap().from_ffi() - } -} - -/// Convert from FFI types to a reference to a wide string (i.e. a [`wstr`]) without allocating. -pub trait AsWstr<'a> { - fn as_wstr(&'a self) -> &'a wstr; -} - -impl<'a> AsWstr<'a> for cxx::UniquePtr { - fn as_wstr(&'a self) -> &'a wstr { - wstr::from_char_slice(self.as_chars()) - } -} - -impl<'a> AsWstr<'a> for cxx::CxxWString { - fn as_wstr(&'a self) -> &'a wstr { - wstr::from_char_slice(self.as_chars()) - } -} - -impl AsWstr<'_> for wcharz_t { - fn as_wstr(&self) -> &wstr { - wstr::from_char_slice(self.chars()) - } -} diff --git a/fish-rust/src/wildcard.rs b/fish-rust/src/wildcard.rs index 4c42f5b5b..82e2c51d8 100644 --- a/fish-rust/src/wildcard.rs +++ b/fish-rust/src/wildcard.rs @@ -1,6 +1,5 @@ // Enumeration of all wildcard types. -use cxx::CxxWString; use libc::X_OK; use std::cmp::Ordering; use std::collections::HashSet; @@ -16,7 +15,6 @@ use crate::future_feature_flags::feature_test; use crate::future_feature_flags::FeatureFlag; use crate::wchar::prelude::*; -use crate::wchar_ffi::WCharFromFFI; use crate::wcstringutil::{ string_fuzzy_match_string, string_suffixes_string_case_insensitive, CaseFold, }; @@ -1234,37 +1232,3 @@ fn test_wildcards() { }); } } - -#[cxx::bridge] -mod ffi { - extern "C++" { - include!("wutil.h"); - } - - extern "Rust" { - #[cxx_name = "wildcard_match"] - fn wildcard_match_ffi(str: &CxxWString, wc: &CxxWString) -> bool; - - #[cxx_name = "wildcard_has"] - fn wildcard_has_ffi(s: &CxxWString) -> bool; - - #[cxx_name = "wildcard_has_internal"] - fn wildcard_has_internal_ffi(s: &CxxWString) -> bool; - } -} - -fn wildcard_match_ffi(str: &CxxWString, wc: &CxxWString) -> bool { - wildcard_match( - str.from_ffi(), - wc.from_ffi(), - /*leading_dots_fail_to_match*/ false, - ) -} - -fn wildcard_has_ffi(s: &CxxWString) -> bool { - wildcard_has(s.from_ffi()) -} - -fn wildcard_has_internal_ffi(s: &CxxWString) -> bool { - wildcard_has_internal(s.from_ffi()) -} diff --git a/fish-rust/src/wutil/gettext.rs b/fish-rust/src/wutil/gettext.rs index 6cd1f3af4..933532b66 100644 --- a/fish-rust/src/wutil/gettext.rs +++ b/fish-rust/src/wutil/gettext.rs @@ -6,11 +6,11 @@ use std::sync::Mutex; use crate::common::{charptr2wcstring, wcs2zstring}; +use crate::ffi::wchar_t; use crate::fish::PACKAGE_NAME; #[cfg(test)] use crate::tests::prelude::*; use crate::wchar::prelude::*; -use crate::wchar_ffi::wchar_t; use errno::{errno, set_errno}; use once_cell::sync::{Lazy, OnceCell}; use widestring::U32CString; diff --git a/fish-rust/src/wutil/tests.rs b/fish-rust/src/wutil/tests.rs index 92b1e4aa6..c13ac9461 100644 --- a/fish-rust/src/wutil/tests.rs +++ b/fish-rust/src/wutil/tests.rs @@ -4,6 +4,7 @@ use std::{ffi::CString, ptr}; use crate::fallback::fish_mkstemp_cloexec; +use crate::tests::prelude::*; use super::*; @@ -68,7 +69,7 @@ fn test_wwrite_to_fd() { assert!(tmpfd.is_valid()); tmpfd.close(); } - let sizes = [0, 1, 2, 3, 5, 13, 23, 64, 128, 255, 4096, 4096 * 2]; + let sizes = [1, 2, 3, 5, 13, 23, 64, 128, 255, 4096, 4096 * 2]; for &size in &sizes { let fd = AutoCloseFd::new(unsafe { libc::open(filename.as_ptr(), O_RDWR | O_TRUNC | O_CREAT, 0o666) diff --git a/src/abbrs.h b/src/abbrs.h deleted file mode 100644 index b28ba1a0c..000000000 --- a/src/abbrs.h +++ /dev/null @@ -1,26 +0,0 @@ -// Support for abbreviations. -// -#ifndef FISH_ABBRS_H -#define FISH_ABBRS_H - -#include -#include - -#include "common.h" -#include "maybe.h" -#include "parse_constants.h" - -#if INCLUDE_RUST_HEADERS - -#include "abbrs.rs.h" - -#else -// Hacks to allow us to compile without Rust headers. -struct abbrs_replacer_t; - -struct abbrs_replacement_t; - -struct abbreviation_t; -#endif - -#endif diff --git a/src/ast.cpp b/src/ast.cpp deleted file mode 100644 index 4bb238aed..000000000 --- a/src/ast.cpp +++ /dev/null @@ -1,25 +0,0 @@ -#include "config.h" // IWYU pragma: keep - -#include "ast.h" - -#include -#include -#include -#include -#include - -#include "common.h" -#include "flog.h" -#include "parse_constants.h" -#include "parse_tree.h" -#include "tokenizer.h" -#include "wutil.h" // IWYU pragma: keep - -rust::Box ast_parse(const wcstring &src, parse_tree_flags_t flags, - parse_error_list_t *out_errors) { - return ast_parse_ffi(src, flags, out_errors); -} -rust::Box ast_parse_argument_list(const wcstring &src, parse_tree_flags_t flags, - parse_error_list_t *out_errors) { - return ast_parse_argument_list_ffi(src, flags, out_errors); -} diff --git a/src/ast.h b/src/ast.h deleted file mode 100644 index 2d67d740f..000000000 --- a/src/ast.h +++ /dev/null @@ -1,98 +0,0 @@ -// Programmatic representation of fish grammar. - -#ifndef FISH_AST_H -#define FISH_AST_H - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "common.h" -#include "cxx.h" -#include "maybe.h" -#include "parse_constants.h" - -#if INCLUDE_RUST_HEADERS -#include "ast.rs.h" -namespace ast { -using ast_t = Ast; -using category_t = Category; -using type_t = Type; - -using andor_job_list_t = AndorJobList; -using andor_job_t = AndorJob; -using argument_list_t = ArgumentList; -using argument_or_redirection_list_t = ArgumentOrRedirectionList; -using argument_or_redirection_t = ArgumentOrRedirection; -using argument_t = Argument; -using begin_header_t = BeginHeader; -using block_statement_t = BlockStatement; -using case_item_t = CaseItem; -using decorated_statement_t = DecoratedStatement; -using elseif_clause_list_t = ElseifClauseList; -using for_header_t = ForHeader; -using freestanding_argument_list_t = FreestandingArgumentList; -using function_header_t = FunctionHeader; -using if_clause_t = IfClause; -using if_statement_t = IfStatement; -using job_conjunction_continuation_t = JobConjunctionContinuation; -using job_conjunction_t = JobConjunction; -using job_continuation_t = JobContinuation; -using job_list_t = JobList; -using job_pipeline_t = JobPipeline; -using maybe_newlines_t = MaybeNewlines; -using not_statement_t = NotStatement; -using redirection_t = Redirection; -using semi_nl_t = SemiNl; -using statement_t = Statement; -using string_t = String_; -using switch_statement_t = SwitchStatement; -using variable_assignment_list_t = VariableAssignmentList; -using variable_assignment_t = VariableAssignment; -using while_header_t = WhileHeader; - -} // namespace ast - -#else -struct Ast; -struct NodeFfi; -struct BlockStatement; -namespace ast { -using ast_t = Ast; - -using block_statement_t = BlockStatement; - -struct argument_t; -struct statement_t; -struct string_t; -struct maybe_newlines_t; -struct redirection_t; -struct variable_assignment_t; -struct semi_nl_t; -struct decorated_statement_t; - -struct keyword_base_t; - -} // namespace ast - -#endif - -using DecoratedStatement = ast::decorated_statement_t; -using BlockStatement = ast::block_statement_t; - -namespace ast { -using node_t = ::NodeFfi; -} - -rust::Box ast_parse(const wcstring &src, parse_tree_flags_t flags = parse_flag_none, - parse_error_list_t *out_errors = nullptr); -rust::Box ast_parse_argument_list(const wcstring &src, - parse_tree_flags_t flags = parse_flag_none, - parse_error_list_t *out_errors = nullptr); - -#endif // FISH_AST_H diff --git a/src/builtin.cpp b/src/builtin.cpp deleted file mode 100644 index 741a6686e..000000000 --- a/src/builtin.cpp +++ /dev/null @@ -1,31 +0,0 @@ -#include "config.h" // IWYU pragma: keep - -#include "builtin.h" - -#include "wutil.h" // IWYU pragma: keep - -/// Counts the number of arguments in the specified null-terminated array -int builtin_count_args(const wchar_t *const *argv) { - int argc; - for (argc = 1; argv[argc] != nullptr;) { - argc++; - } - - assert(argv[argc] == nullptr); - return argc; -} - -/// This function works like wperror, but it prints its result into the streams.err string instead -/// to stderr. Used by the builtin commands. -void builtin_wperror(const wchar_t *program_name, io_streams_t &streams) { - char *err = std::strerror(errno); - if (program_name != nullptr) { - streams.err()->append(program_name); - streams.err()->append(L": "); - } - if (err != nullptr) { - const wcstring werr = str2wcstring(err); - streams.err()->append(werr); - streams.err()->push(L'\n'); - } -} diff --git a/src/builtin.h b/src/builtin.h deleted file mode 100644 index 1c2d6acb5..000000000 --- a/src/builtin.h +++ /dev/null @@ -1,91 +0,0 @@ -// Prototypes for functions for executing builtin functions. -#ifndef FISH_BUILTIN_H -#define FISH_BUILTIN_H - -#include - -#include "common.h" -#include "complete.h" -#include "maybe.h" -#include "wutil.h" - -struct Parser; -struct IoStreams; - -using parser_t = Parser; -using io_streams_t = IoStreams; - -class proc_status_t; -struct OutputStreamFfi; -using output_stream_t = OutputStreamFfi; -struct CompletionListFfi; -using completion_list_t = CompletionListFfi; - -/// Data structure to describe a builtin. -struct builtin_data_t { - // Name of the builtin. - const wchar_t *name; - // Function pointer to the builtin implementation. - maybe_t (*func)(const parser_t &parser, io_streams_t &streams, const wchar_t **argv); - // Description of what the builtin does. - const wchar_t *desc; -}; - -/// The default prompt for the read command. -#define DEFAULT_READ_PROMPT L"set_color green; echo -n read; set_color normal; echo -n \"> \"" - -/// Error message on missing argument. -#define BUILTIN_ERR_MISSING _(L"%ls: %ls: option requires an argument\n") - -/// Error message on missing man page. -#define BUILTIN_ERR_MISSING_HELP \ - _(L"fish: %ls: missing man page\nDocumentation may not be installed.\n`help %ls` will " \ - L"show an online version\n") - -/// Error message on invalid combination of options. -#define BUILTIN_ERR_COMBO _(L"%ls: invalid option combination\n") - -/// Error message on invalid combination of options. -#define BUILTIN_ERR_COMBO2 _(L"%ls: invalid option combination, %ls\n") -#define BUILTIN_ERR_COMBO2_EXCLUSIVE _(L"%ls: %ls %ls: options cannot be used together\n") - -/// Error message on multiple scope levels for variables. -#define BUILTIN_ERR_GLOCAL _(L"%ls: scope can be only one of: universal function global local\n") - -/// Error message for specifying both export and unexport to set/read. -#define BUILTIN_ERR_EXPUNEXP _(L"%ls: cannot both export and unexport\n") - -/// Error message for unknown switch. -#define BUILTIN_ERR_UNKNOWN _(L"%ls: %ls: unknown option\n") - -/// Error message for unexpected args. -#define BUILTIN_ERR_ARG_COUNT0 _(L"%ls: missing argument\n") -#define BUILTIN_ERR_ARG_COUNT1 _(L"%ls: expected %d arguments; got %d\n") -#define BUILTIN_ERR_ARG_COUNT2 _(L"%ls: %ls: expected %d arguments; got %d\n") -#define BUILTIN_ERR_MIN_ARG_COUNT1 _(L"%ls: expected >= %d arguments; got %d\n") -#define BUILTIN_ERR_MAX_ARG_COUNT1 _(L"%ls: expected <= %d arguments; got %d\n") - -/// Error message for invalid variable name. -#define BUILTIN_ERR_VARNAME _(L"%ls: %ls: invalid variable name. See `help identifiers`\n") - -/// Error message for invalid bind mode name. -#define BUILTIN_ERR_BIND_MODE _(L"%ls: %ls: invalid mode name. See `help identifiers`\n") - -/// Error message when too many arguments are supplied to a builtin. -#define BUILTIN_ERR_TOO_MANY_ARGUMENTS _(L"%ls: too many arguments\n") - -/// Error message when integer expected -#define BUILTIN_ERR_NOT_NUMBER _(L"%ls: %ls: invalid integer\n") - -/// Command that requires a subcommand was invoked without a recognized subcommand. -#define BUILTIN_ERR_MISSING_SUBCMD _(L"%ls: missing subcommand\n") -#define BUILTIN_ERR_INVALID_SUBCMD _(L"%ls: %ls: invalid subcommand\n") - -/// The send stuff to foreground message. -#define FG_MSG _(L"Send job %d (%ls) to foreground\n") - -int builtin_count_args(const wchar_t *const *argv); - -void builtin_wperror(const wchar_t *program_name, io_streams_t &streams); - -#endif diff --git a/src/callback.h b/src/callback.h deleted file mode 100644 index 6201bea7b..000000000 --- a/src/callback.h +++ /dev/null @@ -1,49 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include - -/// A RAII callback container that can be used when the rust code needs to (or might need to) free -/// up the resources allocated for a callback (either the type-erased std::function wrapping the -/// lambda itself or the parameter to it.) -struct callback_t { - std::function callback; - std::vector> cleanups; - - /// The default no-op constructor for the callback_t type. - callback_t() { - this->callback = [=](const void *) { return (void *)nullptr; }; - } - - /// Creates a new callback_t instance wrapping the specified type-erased std::function with an - /// optional parameter (defaulting to nullptr). - callback_t(std::function &&callback) { - this->callback = std::move(callback); - } - - /// Executes the wrapped callback with the parameter stored at the time of creation and returns - /// the type-erased (void *) result, but cast to a `const uint8_t *` to please cxx::bridge. - const uint8_t *invoke() const { - const void *result = callback(nullptr); - return (const uint8_t *)result; - } - - /// Executes the wrapped callback with the provided parameter and returns the type-erased - /// (void *) result, but cast to a `const uint8_t *` to please cxx::bridge. - const uint8_t *invoke_with_param(const uint8_t *param) const { - const void *result = callback((const void *)param); - return (const uint8_t *)result; - } - - ~callback_t() { - if (cleanups.size() > 0) { - for (const std::function &dtor : cleanups) { - (dtor)(); - } - cleanups.clear(); - } - } -}; diff --git a/src/color.cpp b/src/color.cpp deleted file mode 100644 index bb8e2ed42..000000000 --- a/src/color.cpp +++ /dev/null @@ -1,280 +0,0 @@ -// Color class implementation. -#include "config.h" // IWYU pragma: keep - -#include "color.h" - -#include -#include -#include -#include // IWYU pragma: keep -#include - -#include "common.h" -#include "fallback.h" // IWYU pragma: keep - -/// Compare wide strings with simple ASCII canonicalization. -/// \return -1, 0, or 1 if s1 is less than, equal to, or greater than s2, respectively. -static int simple_icase_compare(const wchar_t *s1, const wchar_t *s2) { - for (size_t idx = 0; s1[idx] || s2[idx]; idx++) { - wchar_t c1 = s1[idx]; - wchar_t c2 = s2[idx]; - - // "Canonicalize" to lower case. - if (L'A' <= c1 && c1 <= L'Z') c1 = L'a' + (c1 - L'A'); - if (L'A' <= c2 && c2 <= L'Z') c2 = L'a' + (c2 - L'A'); - - if (c1 != c2) { - return c1 < c2 ? -1 : 1; - } - } - // We must have equal lengths and equal values. - return 0; -} - -bool rgb_color_t::try_parse_special(const wcstring &special) { - std::memset(&data, 0, sizeof data); - const wchar_t *name = special.c_str(); - - // wcscasecmp is so slow that using it directly causes `try_parse_special` to consume up to - // 3% of all of fish's cpu time due to extremely inefficient invariant case lookups for wide - // characters (tested: Fedora Server 32 w/ glibc 2.31 with -O2). (This function is also called - // virtually non-stop while emitting output to determine colorization.) - - // Take advantage of the fact that std::string length is O(1) to speed things up, and perform - // what amounts to a simple memcmp before needing to access the invariant case lookup tables. - this->type = type_none; - if (special.size() == const_strlen(L"normal")) { - if (!simple_icase_compare(name, L"normal")) { - this->type = type_normal; - } - } else if (special.size() == const_strlen(L"reset")) { - if (!simple_icase_compare(name, L"reset")) { - this->type = type_reset; - } - } - return this->type != type_none; -} - -static unsigned long squared_difference(long p1, long p2) { - auto diff = static_cast(labs(p1 - p2)); - return diff * diff; -} - -static uint8_t convert_color(const uint8_t rgb[3], const uint32_t *colors, size_t color_count) { - long r = rgb[0], g = rgb[1], b = rgb[2]; - auto best_distance = static_cast(-1); - auto best_index = static_cast(-1); - for (size_t idx = 0; idx < color_count; idx++) { - uint32_t color = colors[idx]; - long test_r = (color >> 16) & 0xFF, test_g = (color >> 8) & 0xFF, - test_b = (color >> 0) & 0xFF; - unsigned long distance = squared_difference(r, test_r) + squared_difference(g, test_g) + - squared_difference(b, test_b); - if (distance <= best_distance) { - best_index = idx; - best_distance = distance; - } - } - return best_index; -} - -bool rgb_color_t::try_parse_rgb(const wcstring &name) { - std::memset(&data, 0, sizeof data); - // We support the following style of rgb formats (case insensitive): - // #FA3 - // #F3A035 - // FA3 - // F3A035 - size_t digit_idx = 0, len = name.size(); - - // Skip any leading #. - if (len > 0 && name.at(0) == L'#') digit_idx++; - - bool success = false; - size_t i; - if (len - digit_idx == 3) { - // Format: FA3 - for (i = 0; i < 3; i++) { - int val = convert_digit(name.at(digit_idx++), 16); - if (val < 0) break; - data.color.rgb[i] = val * 16 + val; - } - success = (i == 3); - } else if (len - digit_idx == 6) { - // Format: F3A035 - for (i = 0; i < 3; i++) { - int hi = convert_digit(name.at(digit_idx++), 16); - int lo = convert_digit(name.at(digit_idx++), 16); - if (lo < 0 || hi < 0) break; - data.color.rgb[i] = hi * 16 + lo; - } - success = (i == 3); - } - if (success) { - this->type = type_rgb; - } - return success; -} - -struct named_color_t { - const wchar_t *name; - uint8_t idx; - uint8_t rgb[3]; - bool hidden; -}; - -// Keep this sorted alphabetically -static constexpr named_color_t named_colors[] = { - {L"black", 0, {0x00, 0x00, 0x00}, false}, {L"blue", 4, {0x00, 0x00, 0x80}, false}, - {L"brblack", 8, {0x80, 0x80, 0x80}, false}, {L"brblue", 12, {0x00, 0x00, 0xFF}, false}, - {L"brbrown", 11, {0xFF, 0xFF, 0x00}, true}, {L"brcyan", 14, {0x00, 0xFF, 0xFF}, false}, - {L"brgreen", 10, {0x00, 0xFF, 0x00}, false}, {L"brgrey", 8, {0x55, 0x55, 0x55}, true}, - {L"brmagenta", 13, {0xFF, 0x00, 0xFF}, false}, {L"brown", 3, {0x72, 0x50, 0x00}, true}, - {L"brpurple", 13, {0xFF, 0x00, 0xFF}, true}, {L"brred", 9, {0xFF, 0x00, 0x00}, false}, - {L"brwhite", 15, {0xFF, 0xFF, 0xFF}, false}, {L"bryellow", 11, {0xFF, 0xFF, 0x00}, false}, - {L"cyan", 6, {0x00, 0x80, 0x80}, false}, {L"green", 2, {0x00, 0x80, 0x00}, false}, - {L"grey", 7, {0xE5, 0xE5, 0xE5}, true}, {L"magenta", 5, {0x80, 0x00, 0x80}, false}, - {L"purple", 5, {0x80, 0x00, 0x80}, true}, {L"red", 1, {0x80, 0x00, 0x00}, false}, - {L"white", 7, {0xC0, 0xC0, 0xC0}, false}, {L"yellow", 3, {0x80, 0x80, 0x00}, false}, -}; -ASSERT_SORTED_BY_NAME(named_colors); - -std::vector rgb_color_t::named_color_names() { - std::vector result; - constexpr size_t colors_count = sizeof(named_colors) / sizeof(named_colors[0]); - result.reserve(1 + colors_count); - for (const auto &named_color : named_colors) { - if (!named_color.hidden) { - result.push_back(named_color.name); - } - } - // "normal" isn't really a color and does not have a color palette index or - // RGB value. Therefore, it does not appear in the named_colors table. - // However, it is a legitimate color name for the "set_color" command so - // include it in the publicly known list of colors. This is primarily so it - // appears in the output of "set_color --print-colors". - result.push_back(L"normal"); - return result; -} - -bool rgb_color_t::try_parse_named(const wcstring &str) { - std::memset(&data, 0, sizeof data); - if (str.empty()) { - return false; - } - - // Binary search with simple case-insensitive compares. - auto is_less = [](const named_color_t &s1, const wchar_t *s2) -> bool { - return simple_icase_compare(s1.name, s2) < 0; - }; - auto start = std::begin(named_colors); - auto end = std::end(named_colors); - auto where = std::lower_bound(start, end, str.c_str(), is_less); - if (where != end && simple_icase_compare(where->name, str.c_str()) == 0) { - data.name_idx = where->idx; - this->type = type_named; - return true; - } - return false; -} - -rgb_color_t::rgb_color_t(uint8_t t, uint8_t i) : type(t), flags(), data() { data.name_idx = i; } - -rgb_color_t rgb_color_t::normal() { return rgb_color_t(type_normal); } - -rgb_color_t rgb_color_t::reset() { return rgb_color_t(type_reset); } - -rgb_color_t rgb_color_t::none() { return rgb_color_t(type_none); } - -rgb_color_t rgb_color_t::white() { return rgb_color_t(type_named, 7); } - -rgb_color_t rgb_color_t::black() { return rgb_color_t(type_named, 0); } - -static uint8_t term16_color_for_rgb(const uint8_t rgb[3]) { - const uint32_t kColors[] = { - 0x000000, // Black - 0x800000, // Red - 0x008000, // Green - 0x808000, // Yellow - 0x000080, // Blue - 0x800080, // Magenta - 0x008080, // Cyan - 0xc0c0c0, // White - 0x808080, // Bright Black - 0xFF0000, // Bright Red - 0x00FF00, // Bright Green - 0xFFFF00, // Bright Yellow - 0x0000FF, // Bright Blue - 0xFF00FF, // Bright Magenta - 0x00FFFF, // Bright Cyan - 0xFFFFFF // Bright White - }; - return convert_color(rgb, kColors, sizeof kColors / sizeof *kColors); -} - -static uint8_t term256_color_for_rgb(const uint8_t rgb[3]) { - const uint32_t kColors[240] = { - 0x000000, 0x00005f, 0x000087, 0x0000af, 0x0000d7, 0x0000ff, 0x005f00, 0x005f5f, 0x005f87, - 0x005faf, 0x005fd7, 0x005fff, 0x008700, 0x00875f, 0x008787, 0x0087af, 0x0087d7, 0x0087ff, - 0x00af00, 0x00af5f, 0x00af87, 0x00afaf, 0x00afd7, 0x00afff, 0x00d700, 0x00d75f, 0x00d787, - 0x00d7af, 0x00d7d7, 0x00d7ff, 0x00ff00, 0x00ff5f, 0x00ff87, 0x00ffaf, 0x00ffd7, 0x00ffff, - 0x5f0000, 0x5f005f, 0x5f0087, 0x5f00af, 0x5f00d7, 0x5f00ff, 0x5f5f00, 0x5f5f5f, 0x5f5f87, - 0x5f5faf, 0x5f5fd7, 0x5f5fff, 0x5f8700, 0x5f875f, 0x5f8787, 0x5f87af, 0x5f87d7, 0x5f87ff, - 0x5faf00, 0x5faf5f, 0x5faf87, 0x5fafaf, 0x5fafd7, 0x5fafff, 0x5fd700, 0x5fd75f, 0x5fd787, - 0x5fd7af, 0x5fd7d7, 0x5fd7ff, 0x5fff00, 0x5fff5f, 0x5fff87, 0x5fffaf, 0x5fffd7, 0x5fffff, - 0x870000, 0x87005f, 0x870087, 0x8700af, 0x8700d7, 0x8700ff, 0x875f00, 0x875f5f, 0x875f87, - 0x875faf, 0x875fd7, 0x875fff, 0x878700, 0x87875f, 0x878787, 0x8787af, 0x8787d7, 0x8787ff, - 0x87af00, 0x87af5f, 0x87af87, 0x87afaf, 0x87afd7, 0x87afff, 0x87d700, 0x87d75f, 0x87d787, - 0x87d7af, 0x87d7d7, 0x87d7ff, 0x87ff00, 0x87ff5f, 0x87ff87, 0x87ffaf, 0x87ffd7, 0x87ffff, - 0xaf0000, 0xaf005f, 0xaf0087, 0xaf00af, 0xaf00d7, 0xaf00ff, 0xaf5f00, 0xaf5f5f, 0xaf5f87, - 0xaf5faf, 0xaf5fd7, 0xaf5fff, 0xaf8700, 0xaf875f, 0xaf8787, 0xaf87af, 0xaf87d7, 0xaf87ff, - 0xafaf00, 0xafaf5f, 0xafaf87, 0xafafaf, 0xafafd7, 0xafafff, 0xafd700, 0xafd75f, 0xafd787, - 0xafd7af, 0xafd7d7, 0xafd7ff, 0xafff00, 0xafff5f, 0xafff87, 0xafffaf, 0xafffd7, 0xafffff, - 0xd70000, 0xd7005f, 0xd70087, 0xd700af, 0xd700d7, 0xd700ff, 0xd75f00, 0xd75f5f, 0xd75f87, - 0xd75faf, 0xd75fd7, 0xd75fff, 0xd78700, 0xd7875f, 0xd78787, 0xd787af, 0xd787d7, 0xd787ff, - 0xd7af00, 0xd7af5f, 0xd7af87, 0xd7afaf, 0xd7afd7, 0xd7afff, 0xd7d700, 0xd7d75f, 0xd7d787, - 0xd7d7af, 0xd7d7d7, 0xd7d7ff, 0xd7ff00, 0xd7ff5f, 0xd7ff87, 0xd7ffaf, 0xd7ffd7, 0xd7ffff, - 0xff0000, 0xff005f, 0xff0087, 0xff00af, 0xff00d7, 0xff00ff, 0xff5f00, 0xff5f5f, 0xff5f87, - 0xff5faf, 0xff5fd7, 0xff5fff, 0xff8700, 0xff875f, 0xff8787, 0xff87af, 0xff87d7, 0xff87ff, - 0xffaf00, 0xffaf5f, 0xffaf87, 0xffafaf, 0xffafd7, 0xffafff, 0xffd700, 0xffd75f, 0xffd787, - 0xffd7af, 0xffd7d7, 0xffd7ff, 0xffff00, 0xffff5f, 0xffff87, 0xffffaf, 0xffffd7, 0xffffff, - 0x080808, 0x121212, 0x1c1c1c, 0x262626, 0x303030, 0x3a3a3a, 0x444444, 0x4e4e4e, 0x585858, - 0x626262, 0x6c6c6c, 0x767676, 0x808080, 0x8a8a8a, 0x949494, 0x9e9e9e, 0xa8a8a8, 0xb2b2b2, - 0xbcbcbc, 0xc6c6c6, 0xd0d0d0, 0xdadada, 0xe4e4e4, 0xeeeeee}; - return 16 + convert_color(rgb, kColors, sizeof kColors / sizeof *kColors); -} - -uint8_t rgb_color_t::to_term256_index() const { - assert(type == type_rgb); - return term256_color_for_rgb(data.color.rgb); -} - -color24_t rgb_color_t::to_color24() const { - assert(type == type_rgb); - return data.color; -} - -uint8_t rgb_color_t::to_name_index() const { - // TODO: This should look for the nearest color. - assert(type == type_named || type == type_rgb); - if (type == type_named) return data.name_idx; - if (type == type_rgb) return term16_color_for_rgb(data.color.rgb); - return static_cast(-1); // this is an error -} - -void rgb_color_t::parse(const wcstring &str) { - bool success = false; - if (!success) success = try_parse_special(str); - if (!success) success = try_parse_named(str); - if (!success) success = try_parse_rgb(str); - if (!success) { - std::memset(&this->data, 0, sizeof this->data); - this->type = type_none; - } -} - -rgb_color_t::rgb_color_t(const wcstring &str) : type(), flags() { this->parse(str); } - -rgb_color_t::rgb_color_t(const std::string &str) : type(), flags() { - this->parse(str2wcstring(str)); -} diff --git a/src/color.h b/src/color.h deleted file mode 100644 index 937afe48c..000000000 --- a/src/color.h +++ /dev/null @@ -1,183 +0,0 @@ -// Color class. -#ifndef FISH_COLOR_H -#define FISH_COLOR_H - -#include -#include -#include - -#include "common.h" - -// 24-bit color. -struct color24_t { - uint8_t rgb[3]; -}; - -/// A type that represents a color. We work hard to keep it at a size of 4 bytes and verify with -/// static_assert -class rgb_color_t { - // Types - enum { type_none, type_named, type_rgb, type_normal, type_reset }; - uint8_t type : 3; - - // Flags - enum { - flag_bold = 1 << 0, - flag_underline = 1 << 1, - flag_italics = 1 << 2, - flag_dim = 1 << 3, - flag_reverse = 1 << 4 - }; - uint8_t flags : 5; - - union { - uint8_t name_idx; // 0-10 - color24_t color; - } data; - - /// Try parsing a special color name like "normal". - bool try_parse_special(const wcstring &special); - - /// Try parsing an rgb color like "#F0A030". - bool try_parse_rgb(const wcstring &name); - - /// Try parsing an explicit color name like "magenta". - bool try_parse_named(const wcstring &str); - - /// Parsing entry point. - void parse(const wcstring &str); - - /// Private constructor. - explicit rgb_color_t(uint8_t t, uint8_t i = 0); - - public: - /// Default constructor of type none. - explicit rgb_color_t() : type(type_none), flags(), data() {} - - /// Parse a color from a string. - explicit rgb_color_t(const wcstring &str); - explicit rgb_color_t(const std::string &str); - - /// Returns white. - static rgb_color_t white(); - - /// Returns black. - static rgb_color_t black(); - - /// Returns the reset special color. - static rgb_color_t reset(); - - /// Returns the normal special color. - static rgb_color_t normal(); - - /// Returns the none special color. - static rgb_color_t none(); - - /// Returns whether the color is the normal special color. - bool is_normal(void) const { return type == type_normal; } - - void set_is_named() { type = type_named; } - void set_is_rgb() { type = type_rgb; } - void set_is_normal() { type = type_normal; } - void set_is_reset() { type = type_reset; } - void set_name_idx(uint8_t idx) { data.name_idx = idx; } - void set_color(uint8_t r, uint8_t g, uint8_t b) { - data.color.rgb[0] = r; - data.color.rgb[1] = g; - data.color.rgb[2] = b; - } - - /// Returns whether the color is the reset special color. - bool is_reset(void) const { return type == type_reset; } - - /// Returns whether the color is the none special color. - bool is_none(void) const { return type == type_none; } - - /// Returns whether the color is a named color (like "magenta"). - bool is_named(void) const { return type == type_named; } - - /// Returns whether the color is specified via RGB components. - bool is_rgb(void) const { return type == type_rgb; } - - /// Returns whether the color is special, that is, not rgb or named. - bool is_special(void) const { return type != type_named && type != type_rgb; } - - /// Returns the name index for the given color. Requires that the color be named or RGB. - uint8_t to_name_index() const; - - /// Returns the term256 index for the given color. Requires that the color be RGB. - uint8_t to_term256_index() const; - - /// Returns the 24 bit color for the given color. Requires that the color be RGB. - color24_t to_color24() const; - - /// Returns whether the color is bold. - bool is_bold() const { return static_cast(flags & flag_bold); } - - /// Set whether the color is bold. - void set_bold(bool x) { - if (x) - flags |= flag_bold; - else - flags &= ~flag_bold; - } - - /// Returns whether the color is underlined. - bool is_underline() const { return static_cast(flags & flag_underline); } - - /// Set whether the color is underlined. - void set_underline(bool x) { - if (x) - flags |= flag_underline; - else - flags &= ~flag_underline; - } - - /// Returns whether the color is italics. - bool is_italics() const { return static_cast(flags & flag_italics); } - - /// Set whether the color is italics. - void set_italics(bool x) { - if (x) - flags |= flag_italics; - else - flags &= ~flag_italics; - } - - /// Returns whether the color is dim. - bool is_dim() const { return static_cast(flags & flag_dim); } - - /// Set whether the color is dim. - void set_dim(bool x) { - if (x) - flags |= flag_dim; - else - flags &= ~flag_dim; - } - - /// Returns whether the color is reverse. - bool is_reverse() const { return static_cast(flags & flag_reverse); } - - /// Set whether the color is reverse. - void set_reverse(bool x) { - if (x) - flags |= flag_reverse; - else - flags &= ~flag_reverse; - } - - /// Compare two colors for equality. - bool operator==(const rgb_color_t &other) const { - return type == other.type && !std::memcmp(&data, &other.data, sizeof data); - } - - /// Compare two colors for inequality. - bool operator!=(const rgb_color_t &other) const { return !(*this == other); } - - /// Returns the names of all named colors. - static std::vector named_color_names(void); -}; - -static_assert(sizeof(rgb_color_t) <= 4, "rgb_color_t is too big"); - -#endif diff --git a/src/common.cpp b/src/common.cpp deleted file mode 100644 index d3da1bbe4..000000000 --- a/src/common.cpp +++ /dev/null @@ -1,867 +0,0 @@ -// Various functions, mostly string utilities, that are used by most parts of fish. -#include "config.h" - -#ifdef HAVE_BACKTRACE_SYMBOLS -#include -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef HAVE_EXECINFO_H -#include -#endif - -#ifdef __linux__ -// Includes for WSL detection -#include -#endif - -#include -#include -#include -#include -#include -#include - -#include "common.h" -#if INCLUDE_RUST_HEADERS -#include "common.rs.h" -#endif -#include "expand.h" -#include "fallback.h" // IWYU pragma: keep -#include "flog.h" -#include "future_feature_flags.h" -#include "global_safety.h" -#include "iothread.h" -#include "signals.h" -#include "termsize.h" -#include "wcstringutil.h" -#include "wildcard.h" -#include "wutil.h" // IWYU pragma: keep - -// Keep after "common.h" -#ifdef HAVE_SYS_SYSCTL_H -#include // IWYU pragma: keep -#endif -#if defined(__APPLE__) -#include // IWYU pragma: keep -#endif - -struct termios shell_modes; - -const wcstring g_empty_string{}; -const std::vector g_empty_string_list{}; - -/// This allows us to notice when we've forked. -static relaxed_atomic_bool_t is_forked_proc{false}; -/// This allows us to bypass the main thread checks -static relaxed_atomic_bool_t thread_asserts_cfg_for_testing{false}; - -static relaxed_atomic_t ellipsis_char; -wchar_t get_ellipsis_char() { return ellipsis_char; } - -static relaxed_atomic_t ellipsis_str; -const wchar_t *get_ellipsis_str() { return ellipsis_str; } - -static relaxed_atomic_t omitted_newline_str; -const wchar_t *get_omitted_newline_str() { return omitted_newline_str; } - -static relaxed_atomic_t omitted_newline_width; -int get_omitted_newline_width() { return omitted_newline_width; } - -static relaxed_atomic_t obfuscation_read_char; -wchar_t get_obfuscation_read_char() { return obfuscation_read_char; } - -bool g_profiling_active = false; -void set_profiling_active(bool val) { g_profiling_active = val; } - -const wchar_t *program_name; - -/// Be able to restore the term's foreground process group. -/// This is set during startup and not modified after. -static relaxed_atomic_t initial_fg_process_group{-1}; - -#if defined(OS_IS_CYGWIN) || defined(WSL) -// MS Windows tty devices do not currently have either a read or write timestamp. Those -// respective fields of `struct stat` are always the current time. Which means we can't -// use them. So we assume no external program has written to the terminal behind our -// back. This makes multiline promptusable. See issue #2859 and -// https://github.com/Microsoft/BashOnWindows/issues/545 -const bool has_working_tty_timestamps = false; -#else -const bool has_working_tty_timestamps = true; -#endif - -/// Convert a character to its integer equivalent if it is a valid character for the requested base. -/// Return the integer value if it is valid else -1. -long convert_digit(wchar_t d, int base) { - long res = -1; - if ((d <= L'9') && (d >= L'0')) { - res = d - L'0'; - } else if ((d <= L'z') && (d >= L'a')) { - res = d + 10 - L'a'; - } else if ((d <= L'Z') && (d >= L'A')) { - res = d + 10 - L'A'; - } - if (res >= base) { - res = -1; - } - - return res; -} - -bool is_windows_subsystem_for_linux() { -#if defined(WSL) - return true; -#elif not defined(__linux__) - return false; -#else - // We are purposely not using std::call_once as it may invoke locking, which is an unnecessary - // overhead since there's no actual race condition here - even if multiple threads call this - // routine simultaneously the first time around, we just end up needlessly querying uname(2) one - // more time. - - static bool wsl_state = [] { - utsname info; - uname(&info); - - // Sample utsname.release under WSL, testing for something like `4.4.0-17763-Microsoft` - if (std::strstr(info.release, "Microsoft") != nullptr) { - const char *dash = std::strchr(info.release, '-'); - if (dash == nullptr || strtod(dash + 1, nullptr) < 17763) { - // #5298, #5661: There are acknowledged, published, and (later) fixed issues with - // job control under early WSL releases that prevent fish from running correctly, - // with unexpected failures when piping. Fish 3.0 nightly builds worked around this - // issue with some needlessly complicated code that was later stripped from the - // fish 3.0 release, so we just bail. Note that fish 2.0 was also broken, but we - // just didn't warn about it. - - // #6038 & 5101bde: It's been requested that there be some sort of way to disable - // this check: if the environment variable FISH_NO_WSL_CHECK is present, this test - // is bypassed. We intentionally do not include this in the error message because - // it'll only allow fish to run but not to actually work. Here be dragons! - if (getenv("FISH_NO_WSL_CHECK") == nullptr) { - FLOGF(error, - "This version of WSL has known bugs that prevent fish from working." - "Please upgrade to Windows 10 1809 (17763) or higher to use fish!"); - } - } - - return true; - } else { - return false; - } - }(); - - // Subsequent calls to this function may take place after fork() and before exec() in - // postfork.cpp. Make sure we never dynamically allocate any memory in the fast path! - return wsl_state; -#endif -} - -#ifdef HAVE_BACKTRACE_SYMBOLS -// This function produces a stack backtrace with demangled function & method names. It is based on -// https://gist.github.com/fmela/591333 but adapted to the style of the fish project. -[[gnu::noinline]] static std::vector demangled_backtrace(int max_frames, - int skip_levels) { - void *callstack[128]; - const int n_max_frames = sizeof(callstack) / sizeof(callstack[0]); - int n_frames = backtrace(callstack, n_max_frames); - char **symbols = backtrace_symbols(callstack, n_frames); - wchar_t text[1024]; - std::vector backtrace_text; - - if (skip_levels + max_frames < n_frames) n_frames = skip_levels + max_frames; - - for (int i = skip_levels; i < n_frames; i++) { - Dl_info info; - if (dladdr(callstack[i], &info) && info.dli_sname) { - char *demangled = nullptr; - int status = -1; - if (info.dli_sname[0] == '_') - demangled = abi::__cxa_demangle(info.dli_sname, nullptr, nullptr, &status); - swprintf(text, sizeof(text) / sizeof(wchar_t), L"%-3d %s + %td", i - skip_levels, - status == 0 ? demangled - : info.dli_sname == nullptr ? symbols[i] - : info.dli_sname, - static_cast(callstack[i]) - static_cast(info.dli_saddr)); - free(demangled); - } else { - swprintf(text, sizeof(text) / sizeof(wchar_t), L"%-3d %s", i - skip_levels, symbols[i]); - } - backtrace_text.push_back(text); - } - free(symbols); - return backtrace_text; -} - -[[gnu::noinline]] void show_stackframe(int frame_count, int skip_levels) { - if (frame_count < 1) return; - - std::vector bt = demangled_backtrace(frame_count, skip_levels + 2); - FLOG(error, L"Backtrace:\n" + join_strings(bt, L'\n') + L'\n'); -} - -#else // HAVE_BACKTRACE_SYMBOLS - -[[gnu::noinline]] void show_stackframe(int, int) { - FLOGF(error, L"Sorry, but your system does not support backtraces"); -} -#endif // HAVE_BACKTRACE_SYMBOLS - -/// \return the smallest pointer in the range [start, start + len] which is aligned to Align. -/// If there is no such pointer, return \p start + len. -/// alignment must be a power of 2 and in range [1, 64]. -/// This is intended to return the end point of the "unaligned prefix" of a vectorized loop. -template -static inline const char *align_start(const char *start, size_t len) { - static_assert(Align >= 1 && Align <= 64, "Alignment must be in range [1, 64]"); - static_assert((Align & (Align - 1)) == 0, "Alignment must be power of 2"); - uintptr_t startu = reinterpret_cast(start); - // How much do we have to add to start to make it 0 mod Align? - // To compute 17 up-aligned by 8, compute its skew 17 % 8, yielding 1, - // and then we will add 8 - 1. Of course if we align 16 with the same idea, we will - // add 8 instead of 0, so then mod the sum by Align again. - // Note all of these mods are optimized to masks. - uintptr_t add_which_aligns = Align - (startu % Align); - add_which_aligns %= Align; - // Add that much but not more than len. If we add 'add_which_aligns' we may overflow the - // pointer. - return start + std::min(static_cast(add_which_aligns), len); -} - -/// \return the largest pointer in the range [start, start + len] which is aligned to Align. -/// If there is no such pointer, return \p start. -/// This is intended to be the start point of the "unaligned suffix" of a vectorized loop. -template -static inline const char *align_end(const char *start, size_t len) { - static_assert(Align >= 1 && Align <= 64, "Alignment must be in range [1, 64]"); - static_assert((Align & (Align - 1)) == 0, "Alignment must be power of 2"); - // How much do we have to subtract to align it? Its value, mod Align. - uintptr_t endu = reinterpret_cast(start + len); - uintptr_t sub_which_aligns = endu % Align; - return start + len - std::min(static_cast(sub_which_aligns), len); -} - -/// \return the count of initial characters in \p in which are ASCII. -static size_t count_ascii_prefix(const char *in, size_t in_len) { - // We'll use aligned reads of this type. - using WordType = uint32_t; - const char *aligned_start = align_start(in, in_len); - const char *aligned_end = align_end(in, in_len); - - // Consume the unaligned prefix. - for (const char *cursor = in; cursor < aligned_start; cursor++) { - if (cursor[0] & 0x80) return &cursor[0] - in; - } - - // Consume the aligned middle. - for (const char *cursor = aligned_start; cursor < aligned_end; cursor += sizeof(WordType)) { - if (*reinterpret_cast(cursor) & 0x80808080) { - if (cursor[0] & 0x80) return &cursor[0] - in; - if (cursor[1] & 0x80) return &cursor[1] - in; - if (cursor[2] & 0x80) return &cursor[2] - in; - return &cursor[3] - in; - } - } - - // Consume the unaligned suffix. - for (const char *cursor = aligned_end; cursor < in + in_len; cursor++) { - if (cursor[0] & 0x80) return &cursor[0] - in; - } - return in_len; -} - -/// Converts the narrow character string \c in into its wide equivalent, and return it. -/// -/// The string may contain embedded nulls. -/// -/// This function encodes illegal character sequences in a reversible way using the private use -/// area. -static wcstring str2wcs_internal(const char *in, const size_t in_len) { - if (in_len == 0) return wcstring(); - assert(in != nullptr); - - wcstring result; - result.reserve(in_len); - - size_t in_pos = 0; - mbstate_t state = {}; - while (in_pos < in_len) { - // Append any initial sequence of ascii characters. - // Note we do not support character sets which are not supersets of ASCII. - size_t ascii_prefix_length = count_ascii_prefix(&in[in_pos], in_len - in_pos); - result.insert(result.end(), &in[in_pos], &in[in_pos + ascii_prefix_length]); - in_pos += ascii_prefix_length; - assert(in_pos <= in_len && "Position overflowed length"); - if (in_pos == in_len) break; - - // We have found a non-ASCII character. - bool use_encode_direct = false; - size_t ret = 0; - wchar_t wc = 0; - - if (false) { -#if defined(HAVE_BROKEN_MBRTOWC_UTF8) - } else if ((in[in_pos] & 0xF8) == 0xF8) { - // Protect against broken std::mbrtowc() implementations which attempt to encode UTF-8 - // sequences longer than four bytes (e.g., OS X Snow Leopard). - use_encode_direct = true; -#endif - } else if (sizeof(wchar_t) == 2 && //!OCLINT(constant if expression) - (in[in_pos] & 0xF8) == 0xF0) { - // Assume we are in a UTF-16 environment (e.g., Cygwin) using a UTF-8 encoding. - // The bits set check will be true for a four byte UTF-8 sequence that requires - // two UTF-16 chars. Something that doesn't work with our simple use of std::mbrtowc(). - use_encode_direct = true; - } else { - ret = std::mbrtowc(&wc, &in[in_pos], in_len - in_pos, &state); - // Determine whether to encode this character with our crazy scheme. - if (fish_reserved_codepoint(wc)) { - use_encode_direct = true; - } else if ((wc >= 0xD800 && wc <= 0xDFFF) || static_cast(wc) >= 0x110000) { - use_encode_direct = true; - } else if (ret == static_cast(-2)) { - // Incomplete sequence. - use_encode_direct = true; - } else if (ret == static_cast(-1)) { - // Invalid data. - use_encode_direct = true; - } else if (ret > in_len - in_pos) { - // Other error codes? Terrifying, should never happen. - use_encode_direct = true; - } else if (sizeof(wchar_t) == 2 && wc >= 0xD800 && //!OCLINT(constant if expression) - wc <= 0xDFFF) { - // If we get a surrogate pair char on a UTF-16 system (e.g., Cygwin) then - // it's guaranteed the UTF-8 decoding is wrong so use direct encoding. - use_encode_direct = true; - } - } - - if (use_encode_direct) { - wc = ENCODE_DIRECT_BASE + static_cast(in[in_pos]); - result.push_back(wc); - in_pos++; - std::memset(&state, 0, sizeof state); - } else if (ret == 0) { // embedded null byte! - result.push_back(L'\0'); - in_pos++; - std::memset(&state, 0, sizeof state); - } else { // normal case - result.push_back(wc); - in_pos += ret; - } - } - - return result; -} - -wcstring str2wcstring(const char *in, size_t len) { return str2wcs_internal(in, len); } - -wcstring str2wcstring(const char *in) { return str2wcs_internal(in, std::strlen(in)); } - -wcstring str2wcstring(const std::string &in) { - // Handles embedded nulls! - return str2wcs_internal(in.data(), in.size()); -} - -wcstring str2wcstring(const std::string &in, size_t len) { - // Handles embedded nulls! - return str2wcs_internal(in.data(), len); -} - -std::string wcs2string(const wcstring &input) { return wcs2string(input.data(), input.size()); } - -std::string wcs2string(const wchar_t *in, size_t len) { - if (len == 0) return std::string{}; - std::string result; - wcs2string_appending(in, len, &result); - return result; -} - -std::string wcs2zstring(const wcstring &input) { return wcs2zstring(input.data(), input.size()); } - -std::string wcs2zstring(const wchar_t *in, size_t len) { - if (len == 0) return std::string{}; - std::string result; - wcs2string_appending(in, len, &result); - return result; -} - -void wcs2string_appending(const wchar_t *in, size_t len, std::string *receiver) { - assert(receiver && "Null receiver"); - receiver->reserve(receiver->size() + len); - wcs2string_callback(in, len, [&](const char *buff, size_t bufflen) { - receiver->append(buff, bufflen); - return true; - }); -} - -/// Test if the character can be encoded using the current locale. -static bool can_be_encoded(wchar_t wc) { - char converted[MB_LEN_MAX]; - mbstate_t state = {}; - - return std::wcrtomb(converted, wc, &state) != static_cast(-1); -} - -wcstring format_string(const wchar_t *format, ...) { - va_list va; - va_start(va, format); - wcstring result = vformat_string(format, va); - va_end(va); - return result; -} - -void append_formatv(wcstring &target, const wchar_t *format, va_list va_orig) { - const int saved_err = errno; - // As far as I know, there is no way to check if a vswprintf-call failed because of a badly - // formated string option or because the supplied destination string was to small. In GLIBC, - // errno seems to be set to EINVAL either way. - // - // Because of this, on failure we try to increase the buffer size until the free space is - // larger than max_size, at which point it will conclude that the error was probably due to a - // badly formated string option, and return an error. Make sure to null terminate string before - // that, though. - const size_t max_size = (128 * 1024 * 1024); - wchar_t static_buff[256]; - size_t size = 0; - wchar_t *buff = nullptr; - int status = -1; - while (status < 0) { - // Reallocate if necessary. - if (size == 0) { - buff = static_buff; - size = sizeof static_buff; - } else { - size *= 2; - if (size >= max_size) { - buff[0] = '\0'; - break; - } - buff = static_cast(realloc((buff == static_buff ? nullptr : buff), size)); - assert(buff != nullptr); - } - - // Try printing. - va_list va; - va_copy(va, va_orig); - status = std::vswprintf(buff, size / sizeof(wchar_t), format, va); - va_end(va); - } - - target.append(buff); - - if (buff != static_buff) { - free(buff); - } - - errno = saved_err; -} - -wcstring vformat_string(const wchar_t *format, va_list va_orig) { - wcstring result; - append_formatv(result, format, va_orig); - return result; -} - -void append_format(wcstring &str, const wchar_t *format, ...) { - va_list va; - va_start(va, format); - append_formatv(str, format, va); - va_end(va); -} - -const wchar_t *quote_end(const wchar_t *pos, wchar_t quote) { - while (true) { - pos++; - - if (!*pos) return nullptr; - - if (*pos == L'\\') { - pos++; - if (!*pos) return nullptr; - } else { - if (*pos == quote || - // Command substitutions also end a double quoted string. This is how we - // support command substitutions inside double quotes. - (quote == L'"' && *pos == L'$' && *(pos + 1) == L'(')) { - return pos; - } - } - } - return nullptr; -} - -const wchar_t *comment_end(const wchar_t *pos) { - do { - pos++; - } while (*pos && *pos != L'\n'); - return pos; -} - -void fish_setlocale() { - // Use various Unicode symbols if they can be encoded using the current locale, else a simple - // ASCII char alternative. All of the can_be_encoded() invocations should return the same - // true/false value since the code points are in the BMP but we're going to be paranoid. This - // is also technically wrong if we're not in a Unicode locale but we expect (or hope) - // can_be_encoded() will return false in that case. - if (can_be_encoded(L'\u2026')) { - ellipsis_char = L'\u2026'; - ellipsis_str = L"\u2026"; - } else { - ellipsis_char = L'$'; // "horizontal ellipsis" - ellipsis_str = L"..."; - } - - if (is_windows_subsystem_for_linux()) { - // neither of \u23CE and \u25CF can be displayed in the default fonts on Windows, though - // they can be *encoded* just fine. Use alternative glyphs. - omitted_newline_str = L"\u00b6"; // "pilcrow" - omitted_newline_width = 1; - obfuscation_read_char = L'\u2022'; // "bullet" - } else if (is_console_session()) { - omitted_newline_str = L"^J"; - omitted_newline_width = 2; - obfuscation_read_char = L'*'; - } else { - if (can_be_encoded(L'\u23CE')) { - omitted_newline_str = L"\u23CE"; // "return symbol" (⏎) - omitted_newline_width = 1; - } else { - omitted_newline_str = L"^J"; - omitted_newline_width = 2; - } - obfuscation_read_char = can_be_encoded(L'\u25CF') ? L'\u25CF' : L'#'; // "black circle" - } -} - -long read_blocked(int fd, void *buf, size_t count) { - ssize_t res; - do { - res = read(fd, buf, count); - } while (res < 0 && errno == EINTR); - return res; -} - -/// Loop a write request while failure is non-critical. Return -1 and set errno in case of critical -/// error. -ssize_t write_loop(int fd, const char *buff, size_t count) { - size_t out_cum = 0; - while (out_cum < count) { - ssize_t out = write(fd, &buff[out_cum], count - out_cum); - if (out < 0) { - if (errno != EAGAIN && errno != EINTR) { - return -1; - } - } else { - out_cum += static_cast(out); - } - } - return static_cast(out_cum); -} - -/// Hack to not print error messages in the tests. Do not call this from functions in this module -/// like `debug()`. It is only intended to suppress diagnostic noise from testing things like the -/// fish parser where we expect a lot of diagnostic messages due to testing error conditions. -bool should_suppress_stderr_for_tests() { - return program_name && !std::wcscmp(program_name, TESTS_PROGRAM_NAME); -} - -// Careful to not negate LLONG_MIN. -static unsigned long long absolute_value(long long x) { - if (x >= 0) return static_cast(x); - x = -(x + 1); - return static_cast(x) + 1; -} - -template -static void format_safe_impl(CharT *buff, size_t size, unsigned long long val) { - size_t idx = 0; - if (val == 0) { - buff[idx++] = '0'; - } else { - // Generate the string backwards, then reverse it. - while (val != 0) { - buff[idx++] = (val % 10) + '0'; - val /= 10; - } - std::reverse(buff, buff + idx); - } - buff[idx++] = '\0'; - assert(idx <= size && "Buffer overflowed"); -} - -void format_long_safe(char buff[64], long val) { - unsigned long long uval = absolute_value(val); - if (val >= 0) { - format_safe_impl(buff, 64, uval); - } else { - buff[0] = '-'; - format_safe_impl(buff + 1, 63, uval); - } -} - -void format_long_safe(wchar_t buff[64], long val) { - unsigned long long uval = absolute_value(val); - if (val >= 0) { - format_safe_impl(buff, 64, uval); - } else { - buff[0] = '-'; - format_safe_impl(buff + 1, 63, uval); - } -} - -void format_llong_safe(wchar_t buff[64], long long val) { - unsigned long long uval = absolute_value(val); - if (val >= 0) { - format_safe_impl(buff, 64, uval); - } else { - buff[0] = '-'; - format_safe_impl(buff + 1, 63, uval); - } -} - -void format_ullong_safe(wchar_t buff[64], unsigned long long val) { - return format_safe_impl(buff, 64, val); -} - -/// Escape a string in a fashion suitable for using as a URL. Store the result in out_str. -static void escape_string_url(const wcstring &in, wcstring &out) { - auto result = escape_string_url(in.c_str(), in.size()); - if (result) { - out = *result; - } -} - -/// Escape a string in a fashion suitable for using as a fish var name. Store the result in out_str. -static void escape_string_var(const wcstring &in, wcstring &out) { - auto result = escape_string_var(in.c_str(), in.size()); - if (result) { - out = *result; - } -} - -/// Escape a string in a fashion suitable for using in fish script. Store the result in out_str. -static void escape_string_script(const wchar_t *orig_in, size_t in_len, wcstring &out, - escape_flags_t flags) { - auto result = escape_string_script(orig_in, in_len, flags); - if (result) { - out = *result; - } -} - -/// Escapes a string for use in a regex string. Not safe for use with `eval` as only -/// characters reserved by PCRE2 are escaped. -/// \param in is the raw string to be searched for literally when substituted in a PCRE2 expression. -static wcstring escape_string_pcre2(const wcstring &in) { - wcstring out; - out.reserve(in.size() * 1.3); // a wild guess - - for (auto c : in) { - switch (c) { - case L'.': - case L'^': - case L'$': - case L'*': - case L'+': - case L'(': - case L')': - case L'?': - case L'[': - case L'{': - case L'}': - case L'\\': - case L'|': - // these two only *need* to be escaped within a character class, and technically it - // makes no sense to ever use process substitution output to compose a character class, - // but... - case L'-': - case L']': - out.push_back('\\'); - __fallthrough__ default : out.push_back(c); - } - } - - return out; -} - -wcstring escape_string(const wchar_t *in, escape_flags_t flags, escape_string_style_t style) { - wcstring result; - - switch (style) { - case STRING_STYLE_SCRIPT: { - escape_string_script(in, std::wcslen(in), result, flags); - break; - } - case STRING_STYLE_URL: { - escape_string_url(in, result); - break; - } - case STRING_STYLE_VAR: { - escape_string_var(in, result); - break; - } - case STRING_STYLE_REGEX: { - result = escape_string_pcre2(in); - break; - } - } - - return result; -} - -wcstring escape_string(const wcstring &in, escape_flags_t flags, escape_string_style_t style) { - wcstring result; - - switch (style) { - case STRING_STYLE_SCRIPT: { - escape_string_script(in.c_str(), in.size(), result, flags); - break; - } - case STRING_STYLE_URL: { - escape_string_url(in, result); - break; - } - case STRING_STYLE_VAR: { - escape_string_var(in, result); - break; - } - case STRING_STYLE_REGEX: { - result = escape_string_pcre2(in); - break; - } - } - - return result; -} - -double timef() { - struct timeval tv; - assert_with_errno(gettimeofday(&tv, nullptr) != -1); - return static_cast(tv.tv_sec) + 1e-6 * tv.tv_usec; -} - -void exit_without_destructors(int code) { _exit(code); } - -extern "C" void debug_thread_error(); - -/// Test if the specified character is in a range that fish uses internally to store special tokens. -/// -/// NOTE: This is used when tokenizing the input. It is also used when reading input, before -/// tokenization, to replace such chars with REPLACEMENT_WCHAR if they're not part of a quoted -/// string. We don't want external input to be able to feed reserved characters into our -/// lexer/parser or code evaluator. -// -// TODO: Actually implement the replacement as documented above. -bool fish_reserved_codepoint(wchar_t c) { - return (c >= RESERVED_CHAR_BASE && c < RESERVED_CHAR_END) || - (c >= ENCODE_DIRECT_BASE && c < ENCODE_DIRECT_END); -} - -/// Reopen stdin, stdout and/or stderr on /dev/null. This is invoked when we find that our tty has -/// become invalid. -void redirect_tty_output() { - struct termios t; - int fd = open("/dev/null", O_WRONLY); - if (fd == -1) { - __fish_assert("Could not open /dev/null!", __FILE__, __LINE__, errno); - } - if (tcgetattr(STDIN_FILENO, &t) == -1 && errno == EIO) dup2(fd, STDIN_FILENO); - if (tcgetattr(STDOUT_FILENO, &t) == -1 && errno == EIO) dup2(fd, STDOUT_FILENO); - if (tcgetattr(STDERR_FILENO, &t) == -1 && errno == EIO) dup2(fd, STDERR_FILENO); - close(fd); -} - -/// Display a failed assertion message, dump a stack trace if possible, then die. -[[noreturn]] void __fish_assert(const char *msg, const char *file, size_t line, int error) { - if (unlikely(error)) { - FLOGF(error, L"%s:%zu: failed assertion: %s: errno %d (%s)", file, line, msg, error, - std::strerror(error)); - } else { - FLOGF(error, L"%s:%zu: failed assertion: %s", file, line, msg); - } - show_stackframe(99, 1); - abort(); -} - -/// Test if the given char is valid in a variable name. -bool valid_var_name_char(wchar_t chr) { return fish_iswalnum(chr) || chr == L'_'; } - -/// Test if the given string is a valid variable name. -bool valid_var_name(const wcstring &str) { - // Note do not use c_str(), we want to fail on embedded nul bytes. - return !str.empty() && std::all_of(str.begin(), str.end(), valid_var_name_char); -} - -bool valid_var_name(const wchar_t *str) { - if (str[0] == L'\0') return false; - for (size_t i = 0; str[i] != L'\0'; i++) { - if (!valid_var_name_char(str[i])) return false; - } - return true; -} - -/// Return a path to a directory where we can store temporary files. -std::string get_path_to_tmp_dir() { - char *env_tmpdir = getenv("TMPDIR"); - if (env_tmpdir) { - return env_tmpdir; - } -#if defined(_CS_DARWIN_USER_TEMP_DIR) - char osx_tmpdir[PATH_MAX]; - size_t n = confstr(_CS_DARWIN_USER_TEMP_DIR, osx_tmpdir, PATH_MAX); - if (0 < n && n <= PATH_MAX) { - return osx_tmpdir; - } else { - return "/tmp"; - } -#elif defined(P_tmpdir) - return P_tmpdir; -#elif defined(_PATH_TMP) - return _PATH_TMP; -#else - return "/tmp"; -#endif -} - -// This function attempts to distinguish between a console session (at the actual login vty) and a -// session within a terminal emulator inside a desktop environment or over SSH. Unfortunately -// there are few values of $TERM that we can interpret as being exclusively console sessions, and -// most common operating systems do not use them. The value is cached for the duration of the fish -// session. We err on the side of assuming it's not a console session. This approach isn't -// bullet-proof and that's OK. -bool is_console_session() { - static const bool console_session = [] { - char tty_name[PATH_MAX]; - if (ttyname_r(STDIN_FILENO, tty_name, sizeof tty_name) != 0) { - return false; - } - constexpr auto len = const_strlen("/dev/tty"); - const char *TERM = getenv("TERM"); - return - // Test that the tty matches /dev/(console|dcons|tty[uv\d]) - ((strncmp(tty_name, "/dev/tty", len) == 0 && - (tty_name[len] == 'u' || tty_name[len] == 'v' || isdigit(tty_name[len]))) || - strcmp(tty_name, "/dev/dcons") == 0 || strcmp(tty_name, "/dev/console") == 0) - // and that $TERM is simple, e.g. `xterm` or `vt100`, not `xterm-something` - && (!TERM || !strchr(TERM, '-') || !strcmp(TERM, "sun-color")); - }(); - return console_session; -} - -/// Expose the C++ version of fish_setlocale as fish_setlocale_ffi so the variables we initialize -/// can be init even if the rust version of the function is called instead. This is easier than -/// declaring all those variables as extern, which I'll do in a separate PR. -extern "C" { -void fish_setlocale_ffi() { fish_setlocale(); } -} diff --git a/src/common.h b/src/common.h index 4d0fe08ce..2001e1dde 100644 --- a/src/common.h +++ b/src/common.h @@ -25,413 +25,6 @@ #include #include -#include "fallback.h" // IWYU pragma: keep -#include "maybe.h" - -// Create a generic define for all BSD platforms -#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__DragonFly__) -#define __BSD__ -#endif - -// PATH_MAX may not exist. -#ifndef PATH_MAX -#define PATH_MAX 4096 -#endif - -// Define a symbol we can use elsewhere in our code to determine if we're being built on MS Windows -// under Cygwin. -#if defined(_WIN32) || defined(_WIN64) || defined(WIN32) || defined(__CYGWIN__) || \ - defined(__WIN32__) -#define OS_IS_CYGWIN -#endif - -// Check if Thread Sanitizer is enabled. -#if defined(__has_feature) -#if __has_feature(thread_sanitizer) -#define FISH_TSAN_WORKAROUNDS 1 -#endif -#endif -#ifdef __SANITIZE_THREAD__ -#define FISH_TSAN_WORKAROUNDS 1 -#endif - -// Common string type. -typedef std::wstring wcstring; - -struct termsize_t; - -// Highest legal ASCII value. -#define ASCII_MAX 127u - -// Highest legal 16-bit Unicode value. -#define UCS2_MAX 0xFFFFu - -// Highest legal byte value. -#define BYTE_MAX 0xFFu - -// Unicode BOM value. -#define UTF8_BOM_WCHAR 0xFEFFu - -// Use Unicode "non-characters" for internal characters as much as we can. This -// gives us 32 "characters" for internal use that we can guarantee should not -// appear in our input stream. See http://www.unicode.org/faq/private_use.html. -#define RESERVED_CHAR_BASE static_cast(0xFDD0) -#define RESERVED_CHAR_END static_cast(0xFDF0) -// Split the available non-character values into two ranges to ensure there are -// no conflicts among the places we use these special characters. -#define EXPAND_RESERVED_BASE RESERVED_CHAR_BASE -#define EXPAND_RESERVED_END (EXPAND_RESERVED_BASE + 16) -#define WILDCARD_RESERVED_BASE EXPAND_RESERVED_END -#define WILDCARD_RESERVED_END (WILDCARD_RESERVED_BASE + 16) -// Make sure the ranges defined above don't exceed the range for non-characters. -// This is to make sure we didn't do something stupid in subdividing the -// Unicode range for our needs. -// #if WILDCARD_RESERVED_END > RESERVED_CHAR_END -// #error -// #endif - -// These are in the Unicode private-use range. We really shouldn't use this -// range but have little choice in the matter given how our lexer/parser works. -// We can't use non-characters for these two ranges because there are only 66 of -// them and we need at least 256 + 64. -// -// If sizeof(wchar_t))==4 we could avoid using private-use chars; however, that -// would result in fish having different behavior on machines with 16 versus 32 -// bit wchar_t. It's better that fish behave the same on both types of systems. -// -// Note: We don't use the highest 8 bit range (0xF800 - 0xF8FF) because we know -// of at least one use of a codepoint in that range: the Apple symbol (0xF8FF) -// on Mac OS X. See http://www.unicode.org/faq/private_use.html. -#define ENCODE_DIRECT_BASE static_cast(0xF600) -#define ENCODE_DIRECT_END (ENCODE_DIRECT_BASE + 256) - -// NAME_MAX is not defined on Solaris -#if !defined(NAME_MAX) -#include -#if defined(MAXNAMELEN) -// MAXNAMELEN is defined on Linux, BSD, and Solaris among others -#define NAME_MAX MAXNAMELEN -#else -static_assert(false, "Neither NAME_MAX nor MAXNAMELEN is defined!"); -#endif -#endif - -// PATH_MAX may not exist. -#ifndef PATH_MAX -#ifdef MAXPATHLEN -#define PATH_MAX MAXPATHLEN -#else -/// Fallback length of MAXPATHLEN. Hopefully a sane value. -#define PATH_MAX 4096 -#endif -#endif - -enum escape_string_style_t { - STRING_STYLE_SCRIPT, - STRING_STYLE_URL, - STRING_STYLE_VAR, - STRING_STYLE_REGEX, -}; - -// Flags for unescape_string functions. -enum { - UNESCAPE_DEFAULT = 0, // default behavior - UNESCAPE_SPECIAL = 1 << 0, // escape special fish syntax characters like the semicolon - UNESCAPE_INCOMPLETE = 1 << 1, // allow incomplete escape sequences - UNESCAPE_NO_BACKSLASHES = 1 << 2, // don't handle backslash escapes -}; -typedef unsigned int unescape_flags_t; - -// Flags for the escape_string() function. These are only applicable when the escape style is -// "script" (i.e., STRING_STYLE_SCRIPT). -enum { - /// Do not escape special fish syntax characters like the semicolon. Only escape non-printable - /// characters and backslashes. - ESCAPE_NO_PRINTABLES = 1 << 0, - /// Do not try to use 'simplified' quoted escapes, and do not use empty quotes as the empty - /// string. - ESCAPE_NO_QUOTED = 1 << 1, - /// Do not escape tildes. - ESCAPE_NO_TILDE = 1 << 2, - /// Replace non-printable control characters with Unicode symbols. - ESCAPE_SYMBOLIC = 1 << 3 -}; -typedef unsigned int escape_flags_t; - -/// A user-visible job ID. -using job_id_t = int; - -/// The non user-visible, never-recycled job ID. -/// Every job has a unique positive value for this. -using internal_job_id_t = uint64_t; - -/// Exits without invoking destructors (via _exit), useful for code after fork. -[[noreturn]] void exit_without_destructors(int code); - -/// Save the shell mode on startup so we can restore them on exit. -extern struct termios shell_modes; - -/// The character to use where the text has been truncated. Is an ellipsis on unicode system and a $ -/// on other systems. -wchar_t get_ellipsis_char(); - -/// The character or string to use where text has been truncated (ellipsis if possible, otherwise -/// ...) -const wchar_t *get_ellipsis_str(); - -/// Character representing an omitted newline at the end of text. -const wchar_t *get_omitted_newline_str(); -int get_omitted_newline_width(); - -/// Character used for the silent mode of the read command -wchar_t get_obfuscation_read_char(); - -/// Profiling flag. True if commands should be profiled. -extern bool g_profiling_active; -void set_profiling_active(bool val); - -/// Name of the current program. Should be set at startup. Used by the debug function. -extern const wchar_t *program_name; - -/// Set to false if it's been determined we can't trust the last modified timestamp on the tty. -extern const bool has_working_tty_timestamps; - -/// A global, empty string. This is useful for functions which wish to return a reference to an -/// empty string. -extern const wcstring g_empty_string; - -/// A global, empty std::vector. This is useful for functions which wish to return a -/// reference to an empty string. -extern const std::vector g_empty_string_list; - -// Pause for input, then exit the program. If supported, print a backtrace first. -#define FATAL_EXIT() \ - do { \ - char exit_read_buff; \ - show_stackframe(); \ - ignore_result(read(0, &exit_read_buff, 1)); \ - exit_without_destructors(1); \ - } while (0) - -/// Exit the program at once after emitting an error message and stack trace if possible. -/// We use our own private implementation of `assert()` for two reasons. First, some implementations -/// are subtly broken. For example, using `printf()` which can cause problems when mixed with wide -/// stdio functions and should be writing the message to stderr rather than stdout. Second, if -/// possible it is useful to provide additional context such as a stack backtrace. -#undef assert -#define assert(e) likely(e) ? ((void)0) : __fish_assert(#e, __FILE__, __LINE__, 0) -#define assert_with_errno(e) likely(e) ? ((void)0) : __fish_assert(#e, __FILE__, __LINE__, errno) -#define DIE(msg) __fish_assert(msg, __FILE__, __LINE__, 0) -#define DIE_WITH_ERRNO(msg) __fish_assert(msg, __FILE__, __LINE__, errno) -/// This macro is meant to be used with functions that return zero on success otherwise return an -/// errno value. Most notably the pthread family of functions which we never expect to fail. -#define DIE_ON_FAILURE(e) \ - do { \ - int status = e; \ - if (unlikely(status != 0)) { \ - __fish_assert(#e, __FILE__, __LINE__, status); \ - } \ - } while (0) - -[[noreturn]] void __fish_assert(const char *msg, const char *file, size_t line, int error); - -/// Shorthand for wgettext call in situations where a C-style string is needed (e.g., -/// std::fwprintf()). -#define _(wstr) wgettext(wstr).c_str() - -/// Noop, used to tell xgettext that a string should be translated. Use this when a string cannot be -/// passed through wgettext() at the point where it is used. For example, when initializing a -/// static array or structure. You must pass the string through wgettext() when it is used. -/// See https://developer.gnome.org/glib/stable/glib-I18N.html#N-:CAPS -#define N_(wstr) wstr - -/// An empty struct which may be embedded (or inherited from) to prevent copying. -struct [[gnu::unused]] noncopyable_t { - noncopyable_t() = default; - noncopyable_t(noncopyable_t &&) = default; - noncopyable_t &operator=(noncopyable_t &&) = default; - noncopyable_t(const noncopyable_t &) = delete; - noncopyable_t &operator=(const noncopyable_t &) = delete; -}; - -struct [[gnu::unused]] nonmovable_t { - nonmovable_t() = default; - nonmovable_t(nonmovable_t &&) = delete; - nonmovable_t &operator=(nonmovable_t &&) = delete; -}; - -/// Test if a collection contains a value. -template -bool contains(const Col &col, const T2 &val) { - return std::find(std::begin(col), std::end(col), val) != std::end(col); -} - -template -bool contains(std::initializer_list col, const T2 &val) { - return std::find(std::begin(col), std::end(col), val) != std::end(col); -} - -/// Append a vector \p donator to the vector \p receiver. -template -void vec_append(std::vector &receiver, std::vector &&donator) { - if (receiver.empty()) { - receiver = std::move(donator); - } else { - receiver.insert(receiver.end(), std::make_move_iterator(donator.begin()), - std::make_move_iterator(donator.end())); - } -} - -/// A function type to check for cancellation. -/// \return true if execution should cancel. -using cancel_checker_t = std::function; - -/// Print a stack trace to stderr. -void show_stackframe(int frame_count = 100, int skip_levels = 0); - -/// Returns a wide character string equivalent of the specified multibyte character string. -/// -/// This function encodes illegal character sequences in a reversible way using the private use -/// area. -wcstring str2wcstring(const std::string &in); -wcstring str2wcstring(const std::string &in, size_t len); -wcstring str2wcstring(const char *in); -wcstring str2wcstring(const char *in, size_t len); - -/// Returns a newly allocated multibyte character string equivalent of the specified wide character -/// string. -/// -/// This function decodes illegal character sequences in a reversible way using the private use -/// area. -std::string wcs2string(const wcstring &input); -std::string wcs2string(const wchar_t *in, size_t len); - -/// Same as wcs2string. Meant to be used when we need a zero-terminated string to feed legacy APIs. -std::string wcs2zstring(const wcstring &input); -std::string wcs2zstring(const wchar_t *in, size_t len); - -/// Like wcs2string, but appends to \p receiver instead of returning a new string. -void wcs2string_appending(const wchar_t *in, size_t len, std::string *receiver); - -// Check if we are running in the test mode, where we should suppress error output -#define TESTS_PROGRAM_NAME L"(ignore)" -bool should_suppress_stderr_for_tests(); - -/// Branch prediction hints. Idea borrowed from Linux kernel. Just used for asserts. -#define likely(x) __builtin_expect(bool(x), 1) -#define unlikely(x) __builtin_expect(bool(x), 0) - -/// Writes out a long safely. -void format_long_safe(char buff[64], long val); -void format_long_safe(wchar_t buff[64], long val); -void format_llong_safe(wchar_t buff[64], long long val); -void format_ullong_safe(wchar_t buff[64], unsigned long long val); - -/// Stored in blocks to reference the file which created the block. -using filename_ref_t = std::shared_ptr; - -using scoped_lock = std::lock_guard; - -// An object wrapping a scoped lock and a value -// This is returned from owning_lock.acquire() -// Sample usage: -// owning_lock locked_name; -// acquired_lock name = name.acquire(); -// name.value = "derp" -// -// Or for simple cases: -// name.acquire().value = "derp" -// -template -class acquired_lock : noncopyable_t { - template - friend class owning_lock; - - template - friend class acquired_lock; - - acquired_lock(std::mutex &lk, Data *v) : lock(lk), value(v) {} - acquired_lock(std::unique_lock &&lk, Data *v) : lock(std::move(lk)), value(v) {} - - std::unique_lock lock; - Data *value; - - public: - Data *operator->() { return value; } - const Data *operator->() const { return value; } - Data &operator*() { return *value; } - const Data &operator*() const { return *value; } - - /// Implicit conversion to const version. - operator acquired_lock() { - // We're about to give up our lock, don't hold onto the data. - const Data *cvalue = value; - value = nullptr; - return acquired_lock(std::move(lock), cvalue); - } - - /// Create from a global lock. - /// This is used in weird cases where a global lock protects more than one piece of data. - static acquired_lock from_global(std::mutex &lk, Data *v) { return acquired_lock{lk, v}; } - - /// \return a reference to the lock, for use with a condition variable. - std::unique_lock &get_lock() { return lock; } -}; - -// A lock that owns a piece of data -// Access to the data is only provided by taking the lock -template -class owning_lock { - // No copying - owning_lock &operator=(const scoped_lock &) = delete; - owning_lock(const scoped_lock &) = delete; - owning_lock(owning_lock &&) = default; - owning_lock &operator=(owning_lock &&) = default; - - std::mutex lock; - Data data; - - public: - owning_lock(Data &&d) : data(std::move(d)) {} - owning_lock(const Data &d) : data(d) {} - owning_lock() : data() {} - - acquired_lock acquire() { return {lock, &data}; } -}; - -/// A scoped manager to save the current value of some variable, and optionally set it to a new -/// value. On destruction it restores the variable to its old value. -/// -/// This can be handy when there are multiple code paths to exit a block. -template -class scoped_push { - T *const ref; - T saved_value; - bool restored; - - public: - explicit scoped_push(T *r) : ref(r), saved_value(*r), restored(false) {} - - scoped_push(T *r, T new_value) : ref(r), restored(false) { - saved_value = std::move(*ref); - *ref = std::move(new_value); - } - - ~scoped_push() { restore(); } - - void restore() { - if (!restored) { - *ref = std::move(saved_value); - restored = true; - } - } -}; - -wcstring format_string(const wchar_t *format, ...); -wcstring vformat_string(const wchar_t *format, va_list va_orig); -void append_format(wcstring &str, const wchar_t *format, ...); -void append_formatv(wcstring &target, const wchar_t *format, va_list va_orig); - #ifndef HAVE_STD__MAKE_UNIQUE /// make_unique implementation namespace std { @@ -443,213 +36,4 @@ std::unique_ptr make_unique(Args &&...args) { #endif using std::make_unique; -/// This functions returns the end of the quoted substring beginning at \c pos. Returns 0 on error. -/// -/// \param pos the position of the opening quote. -/// \param quote the quote to use, usually pointed to by \c pos. -const wchar_t *quote_end(const wchar_t *pos, wchar_t quote); - -/// This functions returns the end of the comment substring beginning at \c pos. -/// -/// \param pos the position where the comment starts, including the '#' symbol. -const wchar_t *comment_end(const wchar_t *pos); - -/// This function should be called after calling `setlocale()` to perform fish specific locale -/// initialization. -void fish_setlocale(); - -/// Call read, blocking and repeating on EINTR. Exits on EAGAIN. -/// \return the number of bytes read, or 0 on EOF. On EAGAIN, returns -1 if nothing was read. -long read_blocked(int fd, void *buf, size_t count); - -/// Loop a write request while failure is non-critical. Return -1 and set errno in case of critical -/// error. -ssize_t write_loop(int fd, const char *buff, size_t count); - -/// Replace special characters with backslash escape sequences. Newline is replaced with \n, etc. -/// -/// \param in The string to be escaped -/// \param flags Flags to control the escaping -/// \return The escaped string -wcstring escape_string(const wchar_t *in, escape_flags_t flags = 0, - escape_string_style_t style = STRING_STYLE_SCRIPT); -wcstring escape_string(const wcstring &in, escape_flags_t flags = 0, - escape_string_style_t style = STRING_STYLE_SCRIPT); - -/// Expand backslashed escapes and substitute them with their unescaped counterparts. Also -/// optionally change the wildcards, the tilde character and a few more into constants which are -/// defined in a private use area of Unicode. This assumes wchar_t is a unicode character set. - -/// Return the number of seconds from the UNIX epoch, with subsecond precision. This function uses -/// the gettimeofday function and will have the same precision as that function. -using timepoint_t = double; -timepoint_t timef(); - -/// Determines if we are running under Microsoft's Windows Subsystem for Linux to work around -/// some known limitations and/or bugs. -/// See https://github.com/Microsoft/WSL/issues/423 and Microsoft/WSL#2997 -bool is_windows_subsystem_for_linux(); - -/// Detect if we are running under Cygwin or Cygwin64 -constexpr bool is_cygwin() { -#ifdef __CYGWIN__ - return true; -#else - return false; -#endif -} - -extern "C" { -[[gnu::noinline]] void debug_thread_error(void); -} - -/// Converts from wide char to digit in the specified base. If d is not a valid digit in the -/// specified base, return -1. -long convert_digit(wchar_t d, int base); - -/// This is a macro that can be used to silence "unused parameter" warnings from the compiler for -/// functions which need to accept parameters they do not use because they need to be compatible -/// with an interface. It's similar to the Python idiom of doing `_ = expr` at the top of a -/// function in the same situation. -#define UNUSED(expr) \ - do { \ - (void)(expr); \ - } while (0) - -// Return true if the character is in a range reserved for fish's private use. -bool fish_reserved_codepoint(wchar_t c); - -void redirect_tty_output(); - -std::string get_path_to_tmp_dir(); - -bool valid_var_name_char(wchar_t chr); -bool valid_var_name(const wcstring &str); -bool valid_var_name(const wchar_t *str); - -// Return values (`$status` values for fish scripts) for various situations. -enum { - /// The status code used for normal exit in a command. - STATUS_CMD_OK = 0, - /// The status code used for failure exit in a command (but not if the args were invalid). - STATUS_CMD_ERROR = 1, - /// The status code used for invalid arguments given to a command. This is distinct from valid - /// arguments that might result in a command failure. An invalid args condition is something - /// like an unrecognized flag, missing or too many arguments, an invalid integer, etc. But - STATUS_INVALID_ARGS = 2, - - /// The status code used when a command was not found. - STATUS_CMD_UNKNOWN = 127, - - /// The status code used when an external command can not be run. - STATUS_NOT_EXECUTABLE = 126, - - /// The status code used when a wildcard had no matches. - STATUS_UNMATCHED_WILDCARD = 124, - /// The status code used when illegal command name is encountered. - STATUS_ILLEGAL_CMD = 123, - /// The status code used when `read` is asked to consume too much data. - STATUS_READ_TOO_MUCH = 122, - /// The status code when an expansion fails, for example, "$foo[" - STATUS_EXPAND_ERROR = 121, -}; - -/* Normally casting an expression to void discards its value, but GCC - versions 3.4 and newer have __attribute__ ((__warn_unused_result__)) - which may cause unwanted diagnostics in that case. Use __typeof__ - and __extension__ to work around the problem, if the workaround is - known to be needed. */ -#if 3 < __GNUC__ + (4 <= __GNUC_MINOR__) -#define ignore_result(x) \ - (__extension__({ \ - __typeof__(x) __x = (x); \ - (void)__x; \ - })) -#else -#define ignore_result(x) ((void)(x)) -#endif - -// Custom hash function used by unordered_map/unordered_set when key is const -#ifndef CONST_WCSTRING_HASH -#define CONST_WCSTRING_HASH 1 -namespace std { -template <> -struct hash { - std::size_t operator()(const wcstring &w) const { - std::hash hasher; - return hasher(w); - } -}; -} // namespace std -#endif - -/// A RAII wrapper for resources that don't recur, so we don't have to create a separate RAII -/// wrapper for each function. Avoids needing to call "return cleanup()" or similar / everywhere. -struct cleanup_t { - private: - const std::function cleanup; - - public: - cleanup_t(std::function exit_actions) : cleanup{std::move(exit_actions)} {} - ~cleanup_t() { cleanup(); } -}; - -bool is_console_session(); - -/// Compile-time agnostic-size strcmp/wcscmp implementation. Unicode-unaware. -template -constexpr ssize_t const_strcmp(const T *lhs, const T *rhs) { - return (*lhs == *rhs) ? (*lhs == 0 ? 0 : const_strcmp(lhs + 1, rhs + 1)) - : (*lhs > *rhs ? 1 : -1); -} - -/// Compile-time agnostic-size strlen/wcslen implementation. Unicode-unaware. -template -constexpr size_t const_strlen(const T (&val)[N], size_t last_checked_idx = N, - size_t first_nul_idx = N) { - // Assume there's a nul char at the end (index N) but there may be one before that that. - return last_checked_idx == 0 - ? first_nul_idx - : const_strlen(val, last_checked_idx - 1, - val[last_checked_idx - 1] ? first_nul_idx : last_checked_idx - 1); -} - -/// \return true if the array \p vals is sorted by its name property. -template -constexpr bool is_sorted_by_name(const T (&vals)[N], size_t idx = 1) { - return idx >= N ? true - : (const_strcmp(vals[idx - 1].name, vals[idx].name) <= 0 && - is_sorted_by_name(vals, idx + 1)); -} -#define ASSERT_SORTED_BY_NAME(x) static_assert(is_sorted_by_name(x), #x " not sorted by name") - -/// \return a pointer to the first entry with the given name, assuming the entries are sorted by -/// name. \return nullptr if not found. -template -const T *get_by_sorted_name(const wchar_t *name, const T (&vals)[N]) { - assert(name && "Null name"); - auto is_less = [](const T &v, const wchar_t *n) -> bool { return std::wcscmp(v.name, n) < 0; }; - auto where = std::lower_bound(std::begin(vals), std::end(vals), name, is_less); - if (where != std::end(vals) && std::wcscmp(where->name, name) == 0) { - return &*where; - } - return nullptr; -} - -template -const T *get_by_sorted_name(const wcstring &name, const T (&vals)[N]) { - return get_by_sorted_name(name.c_str(), vals); -} - -/// As established in 1ab81ab90d1a408702e11f081fdaaafa30636c31, iswdigit() is very slow under glibc, -/// and does nothing more than establish whether or not the single specified character is in the -/// range ('0','9'). -__attribute__((always_inline)) bool inline iswdigit(const wchar_t c) { - return c >= L'0' && c <= L'9'; -} - -#if INCLUDE_RUST_HEADERS -#include "common.rs.h" -#endif - #endif // FISH_COMMON_H diff --git a/src/complete.h b/src/complete.h deleted file mode 100644 index 4b871f6f2..000000000 --- a/src/complete.h +++ /dev/null @@ -1,67 +0,0 @@ -/// Prototypes for functions related to tab-completion. -/// -/// These functions are used for storing and retrieving tab-completion data, as well as for -/// performing tab-completion. -#ifndef FISH_COMPLETE_H -#define FISH_COMPLETE_H - -#include "config.h" // IWYU pragma: keep - -#include -#include -#include -#include -#include - -#include "common.h" -#include "expand.h" -#include "parser.h" -#include "wcstringutil.h" - -struct completion_mode_t { - /// If set, skip file completions. - bool no_files{false}; - bool force_files{false}; - - /// If set, require a parameter after completion. - bool requires_param{false}; -}; - -/// Character that separates the completion and description on programmable completions. -#define PROG_COMPLETE_SEP L'\t' - -enum { - /// Do not insert space afterwards if this is the only completion. (The default is to try insert - /// a space). - COMPLETE_NO_SPACE = 1 << 0, - /// This is not the suffix of a token, but replaces it entirely. - COMPLETE_REPLACES_TOKEN = 1 << 1, - /// This completion may or may not want a space at the end - guess by checking the last - /// character of the completion. - COMPLETE_AUTO_SPACE = 1 << 2, - /// This completion should be inserted as-is, without escaping. - COMPLETE_DONT_ESCAPE = 1 << 3, - /// If you do escape, don't escape tildes. - COMPLETE_DONT_ESCAPE_TILDES = 1 << 4, - /// Do not sort supplied completions - COMPLETE_DONT_SORT = 1 << 5, - /// This completion looks to have the same string as an existing argument. - COMPLETE_DUPLICATES_ARGUMENT = 1 << 6, - /// This completes not just a token but replaces the entire commandline. - COMPLETE_REPLACES_COMMANDLINE = 1 << 7, -}; -using complete_flags_t = uint8_t; - -#if INCLUDE_RUST_HEADERS -#include "complete.rs.h" -#else -struct CompletionListFfi; -struct Completion; -struct CompletionRequestOptions; -#endif - -using completion_t = Completion; -using completion_request_options_t = CompletionRequestOptions; -using completion_list_t = CompletionListFfi; - -#endif diff --git a/src/editable_line.h b/src/editable_line.h deleted file mode 100644 index c0852a295..000000000 --- a/src/editable_line.h +++ /dev/null @@ -1,18 +0,0 @@ -#ifndef FISH_EDITABLE_LINE_H -#define FISH_EDITABLE_LINE_H - -struct HighlightSpecListFFI; - -#if INCLUDE_RUST_HEADERS -#include "editable_line.rs.h" -#else -struct Edit; -struct UndoHistory; -struct EditableLine; -#endif - -using edit_t = Edit; -using undo_history_t = UndoHistory; -using editable_line_t = EditableLine; - -#endif diff --git a/src/empty.cpp b/src/empty.cpp new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/src/empty.cpp @@ -0,0 +1 @@ + diff --git a/src/enum_set.h b/src/enum_set.h deleted file mode 100644 index 8e57704b5..000000000 --- a/src/enum_set.h +++ /dev/null @@ -1,100 +0,0 @@ -#ifndef FISH_ENUM_SET_H -#define FISH_ENUM_SET_H - -#include -#include -#include - -/// A type (to specialize) that provides a count for an enum. -/// Example: -/// template<> struct enum_info_t -/// { static constexpr auto count = MyEnum::COUNT; }; -template -struct enum_info_t {}; - -/// \return the count of an enum. -template -constexpr size_t enum_count() { - return static_cast(enum_info_t::count); -} - -/// A bit set indexed by an enum type. -template -class enum_set_t : private std::bitset()> { - private: - using super = std::bitset()>; - static size_t index_of(T t) { return static_cast(t); } - - explicit enum_set_t(unsigned long raw) : super(raw) {} - explicit enum_set_t(super sup) : super(std::move(sup)) {} - - public: - enum_set_t() = default; - - /*implicit*/ enum_set_t(T v) { set(v); } - - /*implicit*/ enum_set_t(std::initializer_list vs) { - for (T v : vs) set(v); - } - - static enum_set_t from_raw(unsigned long v) { return enum_set_t{v}; } - - unsigned long to_raw() const { return super::to_ulong(); } - - bool get(T t) const { return super::test(index_of(t)); } - - void set(T t, bool v = true) { super::set(index_of(t), v); } - - void clear(T t) { super::reset(index_of(t)); } - - bool none() const { return super::none(); } - - bool any() const { return super::any(); } - - bool operator==(const enum_set_t &rhs) const { return super::operator==(rhs); } - - bool operator!=(const enum_set_t &rhs) const { return super::operator!=(rhs); } - - /// OR in a single flag, returning a new set. - enum_set_t operator|(T rhs) const { - enum_set_t result = *this; - result.set(rhs); - return result; - } - - /// Compute the union of two sets. - enum_set_t operator|(enum_set_t rhs) const { return from_raw(to_raw() | rhs.to_raw()); } - - /// OR in a single flag, modifying the set in place. - enum_set_t operator|=(T rhs) { - *this = *this | rhs; - return *this; - } - - /// Set this to the union of two sets. - enum_set_t operator|=(enum_set_t rhs) { - *this = *this | rhs; - return *this; - } - - /// Test a value of a single flag. Note this does not return an enum_set_t; there is no such - /// boolean conversion. This simply makes flags work more naturally as bit masks. - bool operator&(T rhs) const { return get(rhs); } -}; - -/// An array of Elem indexed by an enum class. -template -class enum_array_t : public std::array()> { - using super = std::array()>; - using base_type_t = typename std::underlying_type::type; - - static int index_of(T t) { return static_cast(t); } - - public: - Elem &at(T t) { return super::at(index_of(t)); } - const Elem &at(T t) const { return super::at(index_of(t)); } - Elem &operator[](T t) { return super::operator[](index_of(t)); } - const Elem &operator[](T t) const { return super::operator[](index_of(t)); } -}; - -#endif diff --git a/src/env.cpp b/src/env.cpp deleted file mode 100644 index ff13f122d..000000000 --- a/src/env.cpp +++ /dev/null @@ -1,225 +0,0 @@ -// Functions for setting and getting environment variables. -#include "config.h" // IWYU pragma: keep - -#include "env.h" - -#include -#include - -#include "history.h" -#include "path.h" -#include "reader.h" - -/// At init, we read all the environment variables from this array. -extern char **environ; - -// static -env_var_t env_var_t::new_ffi(EnvVar *ptr) { - assert(ptr != nullptr && "env_var_t::new_ffi called with null pointer"); - return env_var_t(rust::Box::from_raw(ptr)); -} - -wchar_t env_var_t::get_delimiter() const { return impl_->get_delimiter(); } - -bool env_var_t::empty() const { return impl_->is_empty(); } -bool env_var_t::exports() const { return impl_->exports(); } -bool env_var_t::is_read_only() const { return impl_->is_read_only(); } -bool env_var_t::is_pathvar() const { return impl_->is_pathvar(); } -env_var_t::env_var_flags_t env_var_t::get_flags() const { return impl_->get_flags(); } - -wcstring env_var_t::as_string() const { - wcstring res = std::move(*impl_->as_string()); - return res; -} - -void env_var_t::to_list(std::vector &out) const { - wcstring_list_ffi_t list{}; - impl_->to_list(list); - out = std::move(list.vals); -} - -std::vector env_var_t::as_list() const { - std::vector res = std::move(impl_->as_list()->vals); - return res; -} - -env_var_t &env_var_t::operator=(const env_var_t &rhs) { - this->impl_ = rhs.impl_->clone_box(); - return *this; -} - -env_var_t::env_var_t(const wcstring_list_ffi_t &vals, uint8_t flags) - : impl_(env_var_create(vals, flags)) {} - -env_var_t::env_var_t(const env_var_t &rhs) : impl_(rhs.impl_->clone_box()) {} - -bool env_var_t::operator==(const env_var_t &rhs) const { return impl_->equals(*rhs.impl_); } - -environment_t::~environment_t() = default; - -env_var_t::env_var_flags_t env_var_t::flags_for(const wchar_t *name) { return env_flags_for(name); } - -wcstring environment_t::get_pwd_slash() const { - // Return "/" if PWD is missing. - // See https://github.com/fish-shell/fish-shell/issues/5080 - auto pwd_var = get_unless_empty(L"PWD"); - wcstring pwd; - if (pwd_var) { - pwd = pwd_var->as_string(); - } - if (!string_suffixes_string(L"/", pwd)) { - pwd.push_back(L'/'); - } - return pwd; -} - -maybe_t environment_t::get_unless_empty(const wcstring &key, - env_mode_flags_t mode) const { - if (auto variable = this->get(key, mode)) { - if (!variable->empty()) { - return variable; - } - } - return none(); -} - -std::unique_ptr environment_t::get_or_null(wcstring const &key, - env_mode_flags_t mode) const { - auto variable = this->get(key, mode); - if (!variable.has_value()) { - return nullptr; - } - return make_unique(variable.acquire()); -} - -null_environment_t::null_environment_t() : impl_(env_null_create()) {} -null_environment_t::~null_environment_t() = default; - -maybe_t null_environment_t::get(const wcstring &key, env_mode_flags_t mode) const { - if (auto *ptr = impl_->getf(key, mode)) { - return env_var_t::new_ffi(ptr); - } - return none(); -} - -std::vector null_environment_t::get_names(env_mode_flags_t flags) const { - wcstring_list_ffi_t names; - impl_->get_names(flags, names); - return std::move(names.vals); -} - -bool env_stack_t::is_principal() const { return impl_->is_principal(); } - -static std::map inheriteds; - -void set_inheriteds_ffi() { - wcstring key, val; - const char *const *envp = environ; - int i = 0; - while (envp && envp[i]) i++; - while (i--) { - const wcstring key_and_val = str2wcstring(envp[i]); - size_t eql = key_and_val.find(L'='); - if (eql == wcstring::npos) { - continue; - } - key.assign(key_and_val, 0, eql); - val.assign(key_and_val, eql + 1, wcstring::npos); - inheriteds[key] = val; - } -} - -/// Update the PWD variable directory from the result of getcwd(). -void env_stack_t::set_pwd_from_getcwd() { impl_->set_pwd_from_getcwd(); } - -maybe_t env_stack_t::get(const wcstring &key, env_mode_flags_t mode) const { - if (auto *ptr = impl_->getf(key, mode)) { - return env_var_t::new_ffi(ptr); - } - return none(); -} - -std::vector env_stack_t::get_names(env_mode_flags_t flags) const { - wcstring_list_ffi_t names; - impl_->get_names(flags, names); - return std::move(names.vals); -} - -int env_stack_t::set(const wcstring &key, env_mode_flags_t mode, std::vector vals) { - return static_cast(impl_->set(key, mode, std::move(vals))); -} - -int env_stack_t::set_ffi(const wcstring &key, env_mode_flags_t mode, const void *vals, - size_t count) { - const wchar_t *const *ptr = static_cast(vals); - return this->set(key, mode, std::vector(ptr, ptr + count)); -} - -int env_stack_t::set_one(const wcstring &key, env_mode_flags_t mode, wcstring val) { - std::vector vals; - vals.push_back(std::move(val)); - return set(key, mode, std::move(vals)); -} - -int env_stack_t::remove(const wcstring &key, int mode) { - return static_cast(impl_->remove(key, mode)); -} - -maybe_t env_dyn_t::get(const wcstring &key, env_mode_flags_t mode) const { - if (auto *ptr = impl_->getf(key, mode)) { - return env_var_t::new_ffi(ptr); - } - return none(); -} - -std::vector env_dyn_t::get_names(env_mode_flags_t flags) const { - wcstring_list_ffi_t names; - impl_->get_names(flags, names); - return std::move(names.vals); -} - -std::shared_ptr env_stack_t::snapshot() const { - auto res = std::make_shared(impl_->snapshot()); - return std::static_pointer_cast(res); -} - -wcstring env_stack_t::get_pwd_slash() const { - std::unique_ptr res = impl_->get_pwd_slash(); - return std::move(*res); -} - -void env_stack_t::push(bool new_scope) { impl_->push(new_scope); } - -void env_stack_t::pop() { impl_->pop(); } - -env_stack_t &env_stack_t::globals() { - static env_stack_t s_globals(env_get_globals_ffi()); - return s_globals; -} - -const std::shared_ptr &env_stack_t::principal_ref() { - static const std::shared_ptr s_principal{new env_stack_t(env_get_principal_ffi())}; - return s_principal; -} - -env_stack_t::~env_stack_t() = default; -env_stack_t::env_stack_t(env_stack_t &&) = default; -env_stack_t::env_stack_t(rust::Box imp) : impl_(std::move(imp)) {} -env_stack_t::env_stack_t(uint8_t *imp) - : impl_(rust::Box::from_raw(reinterpret_cast(imp))) {} - -static std::mutex s_setenv_lock{}; - -extern "C" { -void setenv_lock(const char *name, const char *value, int overwrite) { - scoped_lock locker(s_setenv_lock); - setenv(name, value, overwrite); -} - -void unsetenv_lock(const char *name) { - scoped_lock locker(s_setenv_lock); - unsetenv(name); -} -} - -const EnvStackRef &env_stack_t::get_impl_ffi() const { return *impl_; } diff --git a/src/env.h b/src/env.h deleted file mode 100644 index d1ecb02dc..000000000 --- a/src/env.h +++ /dev/null @@ -1,326 +0,0 @@ -// Prototypes for functions for manipulating fish script variables. -#ifndef FISH_ENV_H -#define FISH_ENV_H - -#include -#include - -#include -#include -#include -#include -#include -#include - -#include "common.h" -#include "cxx.h" -#include "maybe.h" -#include "wutil.h" - -#if INCLUDE_RUST_HEADERS -#include "env/env_ffi.rs.h" -#else -struct EnvVar; -struct EnvNull; -struct EnvStack; -struct EnvStackRef; -struct EnvDyn; -enum class env_stack_set_result_t : uint8_t; -struct Statuses; -#endif - -struct event_list_ffi_t; -struct function_properties_t; - -using statuses_t = Statuses; - -/// FFI helper for events. -struct Event; -struct event_list_ffi_t { - event_list_ffi_t(const event_list_ffi_t &) = delete; - event_list_ffi_t &operator=(const event_list_ffi_t &) = delete; - event_list_ffi_t(); -#if INCLUDE_RUST_HEADERS - std::vector> events{}; -#endif - - // Append an Event pointer, which came from Box::into_raw(). - void push(void *event); -}; - -struct owning_null_terminated_array_t; - -#if INCLUDE_RUST_HEADERS -#include "env/env_ffi.rs.h" -#else -struct EnvVar; -struct EnvNull; -struct EnvStackRef; -#endif - -struct owning_null_terminated_array_t; - -extern "C" { -extern bool CURSES_INITIALIZED; - -/// Does the terminal have the "eat_newline_glitch". -extern bool TERM_HAS_XN; - -extern size_t READ_BYTE_LIMIT; -} - -struct Event; - -// Flags that may be passed as the 'mode' in env_stack_t::set() / environment_t::get(). -enum : uint16_t { - /// Default mode. Used with `env_stack_t::get()` to indicate the caller doesn't care what scope - /// the var is in or whether it is exported or unexported. - ENV_DEFAULT = 0, - /// Flag for local (to the current block) variable. - ENV_LOCAL = 1 << 0, - ENV_FUNCTION = 1 << 1, - /// Flag for global variable. - ENV_GLOBAL = 1 << 2, - /// Flag for universal variable. - ENV_UNIVERSAL = 1 << 3, - /// Flag for exported (to commands) variable. - ENV_EXPORT = 1 << 4, - /// Flag for unexported variable. - ENV_UNEXPORT = 1 << 5, - /// Flag to mark a variable as a path variable. - ENV_PATHVAR = 1 << 6, - /// Flag to unmark a variable as a path variable. - ENV_UNPATHVAR = 1 << 7, - /// Flag for variable update request from the user. All variable changes that are made directly - /// by the user, such as those from the `read` and `set` builtin must have this flag set. It - /// serves one purpose: to indicate that an error should be returned if the user is attempting - /// to modify a var that should not be modified by direct user action; e.g., a read-only var. - ENV_USER = 1 << 8, -}; -using env_mode_flags_t = uint16_t; - -/// Return values for `env_stack_t::set()`. -enum { ENV_OK, ENV_PERM, ENV_SCOPE, ENV_INVALID, ENV_NOT_FOUND }; - -/// env_var_t is an immutable value-type data structure representing the value of an environment -/// variable. This wraps the EnvVar type from Rust. -class env_var_t { - public: - using env_var_flags_t = uint8_t; - enum { - flag_export = 1 << 0, // whether the variable is exported - flag_read_only = 1 << 1, // whether the variable is read only - flag_pathvar = 1 << 2, // whether the variable is a path variable - }; - env_var_t() : env_var_t(wcstring_list_ffi_t{}, 0) {} - env_var_t(const wcstring_list_ffi_t &vals, uint8_t flags); - env_var_t(const env_var_t &); - env_var_t(env_var_t &&) = default; - - env_var_t(wcstring val, env_var_flags_t flags) - : env_var_t{std::vector{std::move(val)}, flags} {} - - // Construct from FFI. This transfers ownership of the EnvVar, which should originate - // in Box::into_raw(). - static env_var_t new_ffi(EnvVar *ptr); - - // Get the underlying EnvVar pointer. - // Note you may need to mem::transmute this, since autocxx gets confused when going from Rust -> - // C++ -> Rust. - const EnvVar *ffi_ptr() const { return &*this->impl_; } - - bool empty() const; - bool exports() const; - bool is_read_only() const; - bool is_pathvar() const; - env_var_flags_t get_flags() const; - - wcstring as_string() const; - void to_list(std::vector &out) const; - std::vector as_list() const; - wcstring_list_ffi_t as_list_ffi() const { return as_list(); } - - /// \return the character used when delimiting quoted expansion. - wchar_t get_delimiter() const; - - static env_var_flags_t flags_for(const wchar_t *name); - - env_var_t &operator=(const env_var_t &); - env_var_t &operator=(env_var_t &&) = default; - - bool operator==(const env_var_t &rhs) const; - bool operator!=(const env_var_t &rhs) const { return !(*this == rhs); } - - private: - env_var_t(rust::Box &&impl) : impl_(std::move(impl)) {} - - rust::Box impl_; -}; - -/// An environment is read-only access to variable values. -class environment_t { - protected: - environment_t() = default; - - public: - virtual maybe_t get(const wcstring &key, - env_mode_flags_t mode = ENV_DEFAULT) const = 0; - virtual std::vector get_names(env_mode_flags_t flags) const = 0; - virtual ~environment_t(); - - maybe_t get_unless_empty(const wcstring &key, - env_mode_flags_t mode = ENV_DEFAULT) const; - /// \return a environment variable as a unique pointer, or nullptr if none. - std::unique_ptr get_or_null(const wcstring &key, - env_mode_flags_t mode = ENV_DEFAULT) const; - - /// Returns the PWD with a terminating slash. - virtual wcstring get_pwd_slash() const; -}; - -/// The null environment contains nothing. -class null_environment_t : public environment_t { - public: - null_environment_t(); - ~null_environment_t(); - - maybe_t get(const wcstring &key, env_mode_flags_t mode = ENV_DEFAULT) const override; - std::vector get_names(env_mode_flags_t flags) const override; - - private: - rust::Box impl_; -}; - -/// A mutable environment which allows scopes to be pushed and popped. -class env_stack_t final : public environment_t { - friend struct Parser; - - /// \return whether we are the principal stack. - bool is_principal() const; - - public: - ~env_stack_t() override; - env_stack_t(env_stack_t &&); - /* implicit */ env_stack_t(rust::Box imp); - /* implicit */ env_stack_t(uint8_t *imp); - - /// Implementation of environment_t. - maybe_t get(const wcstring &key, env_mode_flags_t mode = ENV_DEFAULT) const override; - - /// Implementation of environment_t. - std::vector get_names(env_mode_flags_t flags) const override; - - /// Sets the variable with the specified name to the given values. - int set(const wcstring &key, env_mode_flags_t mode, std::vector vals); - - /// Sets the variable with the specified name to the given values. - /// The values should have type const wchar_t *const * (but autocxx doesn't support that). - int set_ffi(const wcstring &key, env_mode_flags_t mode, const void *vals, size_t count); - - /// Sets the variable with the specified name to a single value. - int set_one(const wcstring &key, env_mode_flags_t mode, wcstring val); - - /// Sets the variable with the specified name to no values. - /// Update the PWD variable based on the result of getcwd. - void set_pwd_from_getcwd(); - - /// Remove environment variable. - /// - /// \param key The name of the variable to remove - /// \param mode should be ENV_USER if this is a remove request from the user, 0 otherwise. If - /// this is a user request, read-only variables can not be removed. The mode may also specify - /// the scope of the variable that should be erased. - /// - /// \return zero if the variable existed, and non-zero if the variable did not exist - int remove(const wcstring &key, int mode); - - /// Push the variable stack. Used for implementing local variables for functions and for-loops. - void push(bool new_scope); - - /// Pop the variable stack. Used for implementing local variables for functions and for-loops. - void pop(); - - /// Snapshot this environment. This means returning a read-only copy. Local variables are copied - /// but globals are shared (i.e. changes to global will be visible to this snapshot). This - /// returns a shared_ptr for convenience, since the most common reason to snapshot is because - /// you want to read from another thread. - std::shared_ptr snapshot() const; - - /// Slightly optimized implementation. - wcstring get_pwd_slash() const override; - - /// "Override" of get_or_null, as autocxx doesn't understand inheritance. - std::unique_ptr get_or_null(const wcstring &key, - env_mode_flags_t mode = ENV_DEFAULT) const { - return environment_t::get_or_null(key, mode); - } - - // Compatibility hack; access the "environment stack" from back when there was just one. - static const std::shared_ptr &principal_ref(); - static env_stack_t &principal() { return *principal_ref(); } - - // Access a variable stack that only represents globals. - // Do not push or pop from this. - static env_stack_t &globals(); - - /// Access the underlying Rust implementation. - /// This returns a const rust::Box *, or in Rust terms, a *const Box. - const EnvStackRef &get_impl_ffi() const; - - private: - /// The implementation. Do not access this directly. - rust::Box impl_; -}; - -#if INCLUDE_RUST_HEADERS -struct EnvDyn; -/// Wrapper around rust's `&dyn Environment` deriving from `environment_t`. -class env_dyn_t final : public environment_t { - public: - env_dyn_t(rust::Box impl) : impl_(std::move(impl)) {} - maybe_t get(const wcstring &key, env_mode_flags_t mode) const; - - std::vector get_names(env_mode_flags_t flags) const; - - private: - rust::Box impl_; -}; -#endif - -/// A struct of configuration directories, determined in main() that fish will optionally pass to -/// env_init. -struct config_paths_t { - wcstring data; // e.g., /usr/local/share - wcstring sysconf; // e.g., /usr/local/etc - wcstring doc; // e.g., /usr/local/share/doc/fish - wcstring bin; // e.g., /usr/local/bin -}; - -/// Initialize environment variable data. -void env_init(const struct config_paths_t *paths = nullptr, bool do_uvars = true, - bool default_paths = false); - -using env_var_flags_t = uint8_t; -enum { - env_var_flag_export = 1 << 0, // whether the variable is exported - env_var_flag_read_only = 1 << 1, // whether the variable is read only - env_var_flag_pathvar = 1 << 2, // whether the variable is a path variable -}; - -/// A mutable environment which allows scopes to be pushed and popped. - -#if INCLUDE_RUST_HEADERS -struct EnvDyn; -#endif - -/// A wrapper around setenv() and unsetenv() which use a lock. -/// In general setenv() and getenv() are highly incompatible with threads. This makes it only -/// slightly safer. -extern "C" { -void setenv_lock(const char *name, const char *value, int overwrite); -void unsetenv_lock(const char *name); -} - -void set_inheriteds_ffi(); - -#endif diff --git a/src/env_dispatch.h b/src/env_dispatch.h deleted file mode 100644 index e9038f230..000000000 --- a/src/env_dispatch.h +++ /dev/null @@ -1,16 +0,0 @@ -// Prototypes for functions that react to environment variable changes -#ifndef FISH_ENV_DISPATCH_H -#define FISH_ENV_DISPATCH_H - -#include "config.h" // IWYU pragma: keep - -#include "common.h" -#include "env.h" - -/// Initialize variable dispatch. -void env_dispatch_init(const environment_t &vars); - -/// React to changes in variables like LANG which require running some code. -void env_dispatch_var_change(const wcstring &key, env_stack_t &vars); - -#endif diff --git a/src/env_fwd.h b/src/env_fwd.h deleted file mode 100644 index 771b4562a..000000000 --- a/src/env_fwd.h +++ /dev/null @@ -1,2 +0,0 @@ -struct EnvStackRef; -struct EnvDyn; diff --git a/src/env_universal_common.h b/src/env_universal_common.h deleted file mode 100644 index d8739914f..000000000 --- a/src/env_universal_common.h +++ /dev/null @@ -1,22 +0,0 @@ -#ifndef FISH_ENV_UNIVERSAL_COMMON_H -#define FISH_ENV_UNIVERSAL_COMMON_H -#include "config.h" // IWYU pragma: keep - -#include -#include -#include -#include -#include -#include - -#include "common.h" -#include "env.h" -#include "fds.h" -#include "maybe.h" -#include "wutil.h" - -#if INCLUDE_RUST_HEADERS -#include "universal_notifier/mod.rs.h" -#endif - -#endif diff --git a/src/event.cpp b/src/event.cpp deleted file mode 100644 index a472e494b..000000000 --- a/src/event.cpp +++ /dev/null @@ -1,36 +0,0 @@ -// event.h and event.cpp only contain cpp-side ffi compat code to make event.rs.h a drop-in -// replacement. There is no logic still in here that needs to be ported to rust. - -#include "config.h" // IWYU pragma: keep - -#include "event.h" - -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#include "common.h" -#include "fallback.h" // IWYU pragma: keep -#include "flog.h" -#include "io.h" -#include "maybe.h" -#include "parser.h" -#include "proc.h" -#include "signals.h" -#include "termsize.h" -#include "wcstringutil.h" -#include "wutil.h" // IWYU pragma: keep - -void event_fire_generic(const parser_t &parser, const wcstring &name, - const std::vector &args) { - wcstring_list_ffi_t ffi_args; - for (const auto &arg : args) ffi_args.push(arg); - event_fire_generic_ffi(parser, name, ffi_args); -} diff --git a/src/event.h b/src/event.h deleted file mode 100644 index 30df347d1..000000000 --- a/src/event.h +++ /dev/null @@ -1,38 +0,0 @@ -// event.h and event.cpp only contain cpp-side ffi compat code to make event.rs.h a drop-in -// replacement. There is no logic still in here that needs to be ported to rust. - -#ifndef FISH_EVENT_H -#define FISH_EVENT_H - -#include - -#include -#include -#include -#include - -#include "common.h" -#include "cxx.h" -#include "global_safety.h" -#include "parser.h" -#include "wutil.h" - -#if INCLUDE_RUST_HEADERS -#include "event.rs.h" -#else -struct Event; -#endif - -/// The process id that is used to match any process id. -// TODO: Remove after porting functions.cpp -#define EVENT_ANY_PID 0 - -/// Null-terminated list of valid event filter names. -/// These are what are valid to pass to 'functions --handlers-type' -// TODO: Remove after porting functions.cpp -extern const wchar_t *const event_filter_names[]; - -void event_fire_generic(const parser_t &parser, const wcstring &name, - const std::vector &args = g_empty_string_list); - -#endif diff --git a/src/exec.h b/src/exec.h deleted file mode 100644 index 0ae87dea8..000000000 --- a/src/exec.h +++ /dev/null @@ -1,3 +0,0 @@ -#if INCLUDE_RUST_HEADERS -#include "exec.rs.h" -#endif diff --git a/src/expand.cpp b/src/expand.cpp deleted file mode 100644 index c65f7dfec..000000000 --- a/src/expand.cpp +++ /dev/null @@ -1,11 +0,0 @@ -#include "expand.h" - -/// \param input the string to tilde expand -void expand_tilde(wcstring &input, const env_stack_t &vars) { - // Avoid needless COW behavior by ensuring we use const at. - const wcstring &tmp = input; - if (!tmp.empty() && tmp.at(0) == L'~') { - input.at(0) = HOME_DIRECTORY; - input = *expand_home_directory(input, vars.get_impl_ffi()); - } -} diff --git a/src/expand.h b/src/expand.h deleted file mode 100644 index 38147b44e..000000000 --- a/src/expand.h +++ /dev/null @@ -1,102 +0,0 @@ -// Prototypes for string expansion functions. These functions perform several kinds of parameter -// expansion. There are a lot of issues with regards to memory allocation. Overall, these functions -// would benefit from using a more clever memory allocation scheme, perhaps an evil combination of -// talloc, string buffers and reference counting. -#ifndef FISH_EXPAND_H -#define FISH_EXPAND_H - -#include "config.h" - -#include -#include -#include -#include - -#include "common.h" -#include "enum_set.h" -#include "env.h" -#include "maybe.h" -#include "operation_context.h" -#include "parse_constants.h" - -/// Set of flags controlling expansions. -enum expand_flag { - /// Skip command substitutions. - skip_cmdsubst = 1 << 0, - /// Skip variable expansion. - skip_variables = 1 << 1, - /// Skip wildcard expansion. - skip_wildcards = 1 << 2, - /// The expansion is being done for tab or auto completions. Returned completions may have the - /// wildcard as a prefix instead of a match. - for_completions = 1 << 3, - /// Only match files that are executable by the current user. - executables_only = 1 << 4, - /// Only match directories. - directories_only = 1 << 5, - /// Generate descriptions, stored in the description field of completions. - gen_descriptions = 1 << 6, - /// Un-expand home directories to tildes after. - preserve_home_tildes = 1 << 7, - /// Allow fuzzy matching. - fuzzy_match = 1 << 8, - /// Disallow directory abbreviations like /u/l/b for /usr/local/bin. Only applicable if - /// fuzzy_match is set. - no_fuzzy_directories = 1 << 9, - /// Allows matching a leading dot even if the wildcard does not contain one. - /// By default, wildcards only match a leading dot literally; this is why e.g. '*' does not - /// match hidden files. - allow_nonliteral_leading_dot = 1 << 10, - /// Do expansions specifically to support cd. This means using CDPATH as a list of potential - /// working directories, and to use logical instead of physical paths. - special_for_cd = 1 << 11, - /// Do expansions specifically for cd autosuggestion. This is to differentiate between cd - /// completions and cd autosuggestions. - special_for_cd_autosuggestion = 1 << 12, - /// Do expansions specifically to support external command completions. This means using PATH as - /// a list of potential working directories. - special_for_command = 1 << 13, -}; - -using expand_flags_t = uint64_t; - -enum : wchar_t { - /// Character representing a home directory. - HOME_DIRECTORY = EXPAND_RESERVED_BASE, - /// Character representing process expansion for %self. - PROCESS_EXPAND_SELF, - /// Character representing variable expansion. - VARIABLE_EXPAND, - /// Character representing variable expansion into a single element. - VARIABLE_EXPAND_SINGLE, - /// Character representing the start of a bracket expansion. - BRACE_BEGIN, - /// Character representing the end of a bracket expansion. - BRACE_END, - /// Character representing separation between two bracket elements. - BRACE_SEP, - /// Character that takes the place of any whitespace within non-quoted text in braces - BRACE_SPACE, - /// Separate subtokens in a token with this character. - INTERNAL_SEPARATOR, - /// Character representing an empty variable expansion. Only used transitively while expanding - /// variables. - VARIABLE_EXPAND_EMPTY, - /// This is a special pseudo-char that is not used other than to mark the end of the the special - /// characters so we can sanity check the enum range. - EXPAND_SENTINEL -}; - -/// The string represented by PROCESS_EXPAND_SELF -#define PROCESS_EXPAND_SELF_STR L"%self" -#define PROCESS_EXPAND_SELF_STR_LEN 5 - -#if INCLUDE_RUST_HEADERS -#include "expand.rs.h" -using expand_result_t = ExpandResult; -#endif - -/// \param input the string to tilde expand -void expand_tilde(wcstring &input, const env_stack_t &vars); - -#endif diff --git a/src/fallback.cpp b/src/fallback.cpp deleted file mode 100644 index d538a936b..000000000 --- a/src/fallback.cpp +++ /dev/null @@ -1,181 +0,0 @@ -// This file only contains fallback implementations of functions which have been found to be missing -// or broken by the configuration scripts. -// -// Many of these functions are more or less broken and incomplete. -#include "config.h" - -// IWYU likes to recommend adding term.h when we want ncurses.h. -// IWYU pragma: no_include "term.h" -#include // IWYU pragma: keep -#include // IWYU pragma: keep -#include // IWYU pragma: keep -#include // IWYU pragma: keep -#include - -#include -#include -#if HAVE_GETTEXT -#include -#endif -#if defined(TPARM_SOLARIS_KLUDGE) -#if HAVE_CURSES_H -#include // IWYU pragma: keep -#elif HAVE_NCURSES_H -#include // IWYU pragma: keep -#elif HAVE_NCURSES_CURSES_H -#include // IWYU pragma: keep -#endif -#if HAVE_TERM_H -#include // IWYU pragma: keep -#elif HAVE_NCURSES_TERM_H -#include -#endif -#endif -#include // IWYU pragma: keep - -#include "common.h" // IWYU pragma: keep -#include "fallback.h" // IWYU pragma: keep - -#if defined(TPARM_SOLARIS_KLUDGE) -char *tparm_solaris_kludge(char *str, long p1, long p2, long p3, long p4, long p5, long p6, long p7, - long p8, long p9) { - return tparm(str, p1, p2, p3, p4, p5, p6, p7, p8, p9); -} -#endif - -/// Fallback implementations of wcsncasecmp and wcscasecmp. On systems where these are not needed -/// (e.g. building on Linux) these should end up just being stripped, as they are static functions -/// that are not referenced in this file. -// cppcheck-suppress unusedFunction -[[gnu::unused]] static int wcscasecmp_fallback(const wchar_t *a, const wchar_t *b) { - if (*a == 0) { - return *b == 0 ? 0 : -1; - } else if (*b == 0) { - return 1; - } - int diff = towlower(*a) - towlower(*b); - if (diff != 0) { - return diff; - } - return wcscasecmp_fallback(a + 1, b + 1); -} - -[[gnu::unused]] static int wcsncasecmp_fallback(const wchar_t *a, const wchar_t *b, size_t count) { - if (count == 0) return 0; - - if (*a == 0) { - return *b == 0 ? 0 : -1; - } else if (*b == 0) { - return 1; - } - int diff = towlower(*a) - towlower(*b); - if (diff != 0) return diff; - return wcsncasecmp_fallback(a + 1, b + 1, count - 1); -} - -#ifndef HAVE_WCSCASECMP -#ifndef HAVE_STD__WCSCASECMP -int wcscasecmp(const wchar_t *a, const wchar_t *b) { return wcscasecmp_fallback(a, b); } -#endif -#endif - -#ifndef HAVE_WCSNCASECMP -#ifndef HAVE_STD__WCSNCASECMP -int wcsncasecmp(const wchar_t *a, const wchar_t *b, size_t n) { - return wcsncasecmp_fallback(a, b, n); -} -#endif -#endif - -#if HAVE_GETTEXT -char *fish_gettext(const char *msgid) { return gettext(msgid); } - -char *fish_bindtextdomain(const char *domainname, const char *dirname) { - return bindtextdomain(domainname, dirname); -} - -char *fish_textdomain(const char *domainname) { return textdomain(domainname); } -#else -char *fish_gettext(const char *msgid) { return (char *)msgid; } -char *fish_bindtextdomain(const char *domainname, const char *dirname) { - UNUSED(domainname); - UNUSED(dirname); - return nullptr; -} -char *fish_textdomain(const char *domainname) { - UNUSED(domainname); - return nullptr; -} -#endif - -#ifndef HAVE_KILLPG -int killpg(int pgr, int sig) { - assert(pgr > 1); - return kill(-pgr, sig); -} -#endif - -static int fish_get_emoji_width(wchar_t c) { - (void)c; - return FISH_EMOJI_WIDTH; -} - -// Big hack to use our versions of wcswidth where we know them to be broken, which is -// EVERYWHERE (https://github.com/fish-shell/fish-shell/issues/2199) -#include "widecharwidth/widechar_width.h" - -int fish_wcwidth(wchar_t wc) { - // The system version of wcwidth should accurately reflect the ability to represent characters - // in the console session, but knows nothing about the capabilities of other terminal emulators - // or ttys. Use it from the start only if we are logged in to the physical console. - if (is_console_session()) { - return wcwidth(wc); - } - - // Check for VS16 which selects emoji presentation. This "promotes" a character like U+2764 - // (width 1) to an emoji (probably width 2). So treat it as width 1 so the sums work. See #2652. - // VS15 selects text presentation. - const wchar_t variation_selector_16 = L'\uFE0F', variation_selector_15 = L'\uFE0E'; - if (wc == variation_selector_16) - return 1; - else if (wc == variation_selector_15) - return 0; - - // Check for Emoji_Modifier property. Only the Fitzpatrick modifiers have this, in range - // 1F3FB..1F3FF. This is a hack because such an emoji appearing on its own would be drawn as - // width 2, but that's unlikely to be useful. See #8275. - if (wc >= 0x1F3FB && wc <= 0x1F3FF) return 0; - - int width = widechar_wcwidth(wc); - - switch (width) { - case widechar_non_character: - case widechar_nonprint: - case widechar_combining: - case widechar_unassigned: - // Fall back to system wcwidth in this case. - return wcwidth(wc); - case widechar_ambiguous: - case widechar_private_use: - // TR11: "All private-use characters are by default classified as Ambiguous". - return FISH_AMBIGUOUS_WIDTH; - case widechar_widened_in_9: - return fish_get_emoji_width(wc); - default: - assert(width > 0 && "Unexpectedly nonpositive width"); - return width; - } -} - -int fish_wcswidth(const wchar_t *str, size_t n) { - int result = 0; - for (size_t i = 0; i < n && str[i] != L'\0'; i++) { - int w = fish_wcwidth(str[i]); - if (w < 0) { - result = -1; - break; - } - result += w; - } - return result; -} diff --git a/src/fallback.h b/src/fallback.h deleted file mode 100644 index 2eee0cb1f..000000000 --- a/src/fallback.h +++ /dev/null @@ -1,113 +0,0 @@ -#ifndef FISH_FALLBACK_H -#define FISH_FALLBACK_H - -#include -#include "config.h" - -// The following include must be kept despite what IWYU says. That's because of the interaction -// between the weak linking of `wcscasecmp` via `#define`s below and the declarations -// in . At least on OS X if we don't do this we get compilation errors do to the macro -// substitution if wchar.h is included after this header. -#include // IWYU pragma: keep - // -// Width of ambiguous characters. 1 is typical default. -extern int32_t FISH_AMBIGUOUS_WIDTH; - -// Width of emoji characters. -// 1 is the typical emoji width in Unicode 8. -extern int32_t FISH_EMOJI_WIDTH; - - -/// fish's internal versions of wcwidth and wcswidth, which can use an internal implementation if -/// the system one is busted. -int fish_wcwidth(wchar_t wc); -int fish_wcswidth(const wchar_t *str, size_t n); - -/// thread_local support. -#if HAVE_CX11_THREAD_LOCAL -#define FISH_THREAD_LOCAL thread_local -#elif defined(__GNUC__) -#define FISH_THREAD_LOCAL __thread -#elif defined(_MSC_VER) -#define FISH_THREAD_LOCAL __declspec(thread) -#else // !C++11 && !__GNUC__ && !_MSC_VER -#error "No known thread local storage qualifier for this platform" -#endif - -#ifndef WCHAR_MAX -/// This _should_ be defined by wchar.h, but e.g. OpenBSD doesn't. -#define WCHAR_MAX INT_MAX -#endif - -/// Both ncurses and NetBSD curses expect an int (*func)(int) as the last parameter for tputs. -/// Apparently OpenIndiana's curses still uses int (*func)(char) here. -#if TPUTS_USES_INT_ARG -using tputs_arg_t = int; -#else -using tputs_arg_t = char; -#endif - -#ifndef HAVE_WINSIZE -/// Structure used to get the size of a terminal window. -struct winsize { - /// Number of rows. - unsigned short ws_row; - /// Number of columns. - unsigned short ws_col; -}; - -#endif - -#if defined(TPARM_SOLARIS_KLUDGE) -/// Solaris tparm has a set fixed of parameters in its curses implementation, work around this here. -#define fish_tparm tparm_solaris_kludge -char *tparm_solaris_kludge(char *str, long p1 = 0, long p2 = 0, long p3 = 0, long p4 = 0, - long p5 = 0, long p6 = 0, long p7 = 0, long p8 = 0, long p9 = 0); -#else -#define fish_tparm tparm -#endif - -/// These functions are missing from Solaris 10, and only accessible from -/// Solaris 11 in the std:: namespace. -#ifndef HAVE_WCSCASECMP -#ifdef HAVE_STD__WCSCASECMP -using std::wcscasecmp; -#else -int wcscasecmp(const wchar_t *a, const wchar_t *b); -#endif // HAVE_STD__WCSCASECMP -#endif // HAVE_WCSCASECMP - -#ifndef HAVE_WCSNCASECMP -#ifdef HAVE_STD__WCSNCASECMP -using std::wcsncasecmp; -#else -int wcsncasecmp(const wchar_t *s1, const wchar_t *s2, size_t n); -#endif // HAVE_STD__WCSNCASECMP -#endif // HAVE_WCSNCASECMP - -#ifndef HAVE_DIRFD -#ifndef __XOPEN_OR_POSIX -#define dirfd(d) (d->dd_fd) -#else -#define dirfd(d) (d->d_fd) -#endif -#endif - -// autoconf may fail to detect gettext (645), so don't define a function call gettext or we'll get -// build errors. - -/// Cover for gettext(). -char *fish_gettext(const char *msgid); - -/// Cover for bindtextdomain(). -char *fish_bindtextdomain(const char *domainname, const char *dirname); - -/// Cover for textdomain(). -char *fish_textdomain(const char *domainname); - -#ifndef HAVE_KILLPG -/// Send specified signal to specified process group. -int killpg(int pgr, int sig); -#endif - -#endif // FISH_FALLBACK_H diff --git a/src/fds.h b/src/fds.h deleted file mode 100644 index 7092242bf..000000000 --- a/src/fds.h +++ /dev/null @@ -1,30 +0,0 @@ -/** Facilities for working with file descriptors. */ - -#ifndef FISH_FDS_H -#define FISH_FDS_H - -#include "config.h" // IWYU pragma: keep - -#include // IWYU pragma: keep -#include // IWYU pragma: keep -#include - -#include -#include -#include -#include - -#include "common.h" -#include "maybe.h" - -#if INCLUDE_RUST_HEADERS -#include "fds.rs.h" -#endif - -/// A sentinel value indicating no timeout. -#define kNoTimeout (std::numeric_limits::max()) - -/// Mark an fd as blocking; returns errno or 0 on success. -int make_fd_blocking(int fd); - -#endif diff --git a/src/ffi.h b/src/ffi.h deleted file mode 100644 index 7dbb8c7e5..000000000 --- a/src/ffi.h +++ /dev/null @@ -1,23 +0,0 @@ -#include -#include - -#include "cxx.h" -#include "trace.rs.h" -#if INCLUDE_RUST_HEADERS -// For some unknown reason, the definition of rust::Box is in this particular header: -#include "parse_constants.rs.h" -#endif - -template -inline std::shared_ptr box_to_shared_ptr(rust::Box &&value) { - T *ptr = value.into_raw(); - std::shared_ptr shared(ptr, [](T *ptr) { rust::Box::from_raw(ptr); }); - return shared; -} - -inline static void trace_if_enabled(const parser_t &parser, wcharz_t command, - const std::vector &args = {}) { - if (trace_enabled(parser)) { - trace_argv(parser, command, args); - } -} diff --git a/src/ffi_baggage.h b/src/ffi_baggage.h deleted file mode 100644 index 3f0ce3d5f..000000000 --- a/src/ffi_baggage.h +++ /dev/null @@ -1,25 +0,0 @@ -#include "builtin.h" -#include "event.h" -#include "fds.h" -#include "highlight.h" -#include "parse_util.h" -#include "reader.h" -#include "screen.h" - -// Symbols that get autocxx bindings but are not used in a given binary, will cause "undefined -// reference" when trying to link that binary. Work around this by marking them as used in -// all binaries. -void mark_as_used(const parser_t& parser, env_stack_t& env_stack) { - wcstring s; - - event_fire_generic(parser, {}); - event_fire_generic(parser, {}, {}); - expand_tilde(s, env_stack); - highlight_spec_t{}; - rgb_color_t{}; - setenv_lock({}, {}, {}); - set_inheriteds_ffi(); - unsetenv_lock({}); - rgb_color_t::white(); - rgb_color_t{}; -} diff --git a/src/fish.cpp b/src/fish.cpp index a2619c3f0..a285d424a 100644 --- a/src/fish.cpp +++ b/src/fish.cpp @@ -16,11 +16,6 @@ You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ -#include "common.h" -#include "ffi_baggage.h" #include "fish.rs.h" -int main() { - program_name = L"fish"; - return rust_main(); -} +int main() { return rust_main(); } diff --git a/src/fish_indent.cpp b/src/fish_indent.cpp index 017e161c3..d85bb5500 100644 --- a/src/fish_indent.cpp +++ b/src/fish_indent.cpp @@ -15,44 +15,6 @@ You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA */ -#include "config.h" // IWYU pragma: keep - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "ast.h" -#include "env.h" -#include "env/env_ffi.rs.h" -#include "fds.h" -#include "ffi_baggage.h" -#include "ffi_init.rs.h" #include "fish_indent.rs.h" -#include "fish_version.h" -#include "flog.h" -#include "future_feature_flags.h" -#include "highlight.h" -#include "operation_context.h" -#include "print_help.rs.h" -#include "tokenizer.h" -#include "wcstringutil.h" -#include "wutil.h" // IWYU pragma: keep -int main() { - program_name = L"fish_indent"; - return fish_indent_main(); -} +int main() { return fish_indent_main(); } diff --git a/src/fish_key_reader.cpp b/src/fish_key_reader.cpp index fd11b4229..90b133e61 100644 --- a/src/fish_key_reader.cpp +++ b/src/fish_key_reader.cpp @@ -20,7 +20,6 @@ #include #include "common.h" -#include "ffi_baggage.h" #include "fish_key_reader.rs.h" int main() { return fish_key_reader_main(); } diff --git a/src/fish_version.cpp b/src/fish_version.cpp deleted file mode 100644 index da8bd4ea8..000000000 --- a/src/fish_version.cpp +++ /dev/null @@ -1,17 +0,0 @@ -// Fish version receiver. -// -// This file has a specific purpose of shortening compilation times when -// the only change is different `git describe` version. -#include "fish_version.h" - -#ifndef FISH_BUILD_VERSION -// The contents of FISH-BUILD-VERSION-FILE looks like: -// FISH_BUILD_VERSION="2.7.1-62-gc0480092-dirty" -// Arrange for it to become a variable. -static const char *const -#include "FISH-BUILD-VERSION-FILE" - ; -#endif - -/// Return fish shell version. -const char *get_fish_version() { return FISH_BUILD_VERSION; } diff --git a/src/fish_version.h b/src/fish_version.h deleted file mode 100644 index 419c83d56..000000000 --- a/src/fish_version.h +++ /dev/null @@ -1,2 +0,0 @@ -// Prototype for version receiver. -const char *get_fish_version(); diff --git a/src/flog.cpp b/src/flog.cpp deleted file mode 100644 index ea0bca843..000000000 --- a/src/flog.cpp +++ /dev/null @@ -1,206 +0,0 @@ -/// fish logging -#include "config.h" // IWYU pragma: keep - -#include "flog.h" - -#include -#include - -#include -#include -#include -#include -#include - -#include "common.h" -#include "global_safety.h" -#include "parse_util.h" -#include "wcstringutil.h" -#include "wildcard.h" - -namespace flog_details { - -// Note we are relying on the order of global initialization within this file. -// It is important that 'all' be initialized before 'g_categories', because g_categories wants to -// append to all in the ctor. -/// This is not modified after initialization. -static std::vector s_all_categories; - -/// The fd underlying the flog output file. -/// This is separated out for flogf_async_safe. -static int s_flog_file_fd{STDERR_FILENO}; - -/// When a category is instantiated it adds itself to the 'all' list. -category_t::category_t(const wchar_t *name, const wchar_t *desc, bool enabled) - : name(name), description(desc), enabled(enabled) { - s_all_categories.push_back(this); -} - -/// Instantiate all categories. -/// This is deliberately leaked to avoid pointless destructor registration. -category_list_t *const category_list_t::g_instance = new category_list_t(); - -logger_t::logger_t() : file_(stderr) {} - -owning_lock g_logger; - -void logger_t::log1(const wchar_t *s) { std::fputws(s, file_); } - -void logger_t::log1(const char *s) { - // Note glibc prohibits mixing narrow and wide I/O, so always use wide-printing functions. - // See #5900. - std::fwprintf(file_, L"%s", s); -} - -void logger_t::log1(wchar_t c) { std::fputwc(c, file_); } - -void logger_t::log1(char c) { std::fwprintf(file_, L"%c", c); } - -void logger_t::log1(int64_t v) { std::fwprintf(file_, L"%lld", v); } - -void logger_t::log1(uint64_t v) { std::fwprintf(file_, L"%llu", v); } - -void logger_t::log_fmt(const category_t &cat, const wchar_t *fmt, ...) { - va_list va; - va_start(va, fmt); - log1(cat.name); - log1(L": "); - std::vfwprintf(file_, fmt, va); - log1(L'\n'); - va_end(va); -} - -void logger_t::log_fmt(const category_t &cat, const char *fmt, ...) { - // glibc dislikes mixing wide and narrow output functions. - // So construct a narrow string in-place and output that via wide functions. - va_list va; - va_start(va, fmt); - int ret = vsnprintf(nullptr, 0, fmt, va); - va_end(va); - - if (ret < 0) { - perror("vsnprintf"); - return; - } - size_t len = static_cast(ret) + 1; - std::unique_ptr buff(new char[len]); - - va_start(va, fmt); - ret = vsnprintf(buff.get(), len, fmt, va); - va_end(va); - if (ret < 0) { - perror("vsnprintf"); - return; - } - log_fmt(cat, L"%s", buff.get()); -} - -inline void async_safe_write_str(const char *s, size_t len = std::string::npos) { - if (s_flog_file_fd < 0) return; - if (len == std::string::npos) len = std::strlen(s); - ignore_result(write(s_flog_file_fd, s, len)); -} - -// static -void logger_t::flogf_async_safe(const char *category, const char *fmt, const char *param1, - const char *param2, const char *param3, const char *param4, - const char *param5, const char *param6, const char *param7, - const char *param8, const char *param9, const char *param10, - const char *param11, const char *param12) { - const char *const params[] = {param1, param2, param3, param4, param5, param6, - param7, param8, param9, param10, param11, param12}; - const size_t max_params = sizeof params / sizeof *params; - - // Can't call fwprintf, that may allocate memory. - // Just call write() over and over. - async_safe_write_str(category); - async_safe_write_str(": "); - - size_t param_idx = 0; - const char *cursor = fmt; - while (*cursor != '\0') { - const char *end = std::strchr(cursor, '%'); - if (end == nullptr) end = cursor + std::strlen(cursor); - if (end > cursor) async_safe_write_str(cursor, end - cursor); - if (end[0] == '%' && end[1] == 's') { - // Handle a format string. - const char *param = (param_idx < max_params ? params[param_idx++] : nullptr); - if (!param) param = "(null)"; - async_safe_write_str(param); - cursor = end + 2; - } else if (end[0] == '\0') { - // Must be at the end of the string. - cursor = end; - } else { - // Some other format specifier, just skip it. - cursor = end + 1; - } - } - - // We always append a newline. - async_safe_write_str("\n"); -} - -} // namespace flog_details - -using namespace flog_details; - -/// For each category, if its name matches the wildcard, set its enabled to the given sense. -static void apply_one_wildcard(const wcstring &wc_esc, bool sense) { - wcstring wc = parse_util_unescape_wildcards(wc_esc); - bool match_found = false; - for (category_t *cat : s_all_categories) { - if (wildcard_match(cat->name, wc)) { - cat->enabled = sense; - match_found = true; - } - } - if (!match_found) { - fprintf(stderr, "Failed to match debug category: %ls\n", wc_esc.c_str()); - } -} - -void activate_flog_categories_by_pattern(wcstring wc) { - // Normalize underscores to dashes, allowing the user to be sloppy. - std::replace(wc.begin(), wc.end(), L'_', L'-'); - for (const wcstring &s : split_string(wc, L',')) { - if (string_prefixes_string(L"-", s)) { - apply_one_wildcard(s.substr(1), false); - } else { - apply_one_wildcard(s, true); - } - } -} - -// autocxx fails with FILE* -void set_flog_output_file_ffi(void* fp) { - auto f = static_cast(fp); - set_flog_output_file(f); -} - -// Rust libc does not contain setlinebuf -void flog_setlinebuf_ffi(void *f) { - auto fp = static_cast(f); - if (fp == nullptr) { - return; - } - setlinebuf(fp); -} - -void set_flog_output_file(FILE *f) { - assert(f && "Null file"); - g_logger.acquire()->set_file(f); - s_flog_file_fd = fileno(f); -} - -void log_extra_to_flog_file(const wcstring &s) { g_logger.acquire()->log_extra(s.c_str()); } - -int get_flog_file_fd() { return s_flog_file_fd; } - -std::vector get_flog_categories() { - std::vector result(s_all_categories.begin(), s_all_categories.end()); - std::sort(result.begin(), result.end(), [](const category_t *a, const category_t *b) { - return wcscmp(a->name, b->name) < 0; - }); - return result; -} diff --git a/src/flog.h b/src/flog.h deleted file mode 100644 index 9dbd25a96..000000000 --- a/src/flog.h +++ /dev/null @@ -1,243 +0,0 @@ -/// The flogger: debug logging support for fish. -#ifndef FISH_FLOG_H -#define FISH_FLOG_H - -#include "config.h" // IWYU pragma: keep - -#include -#include - -#include -#include -#include -#include - -#include "common.h" -#include "global_safety.h" - -using wcstring = std::wstring; - -namespace flog_details { - -class category_list_t; -class category_t { - friend category_list_t; - category_t(const wchar_t *name, const wchar_t *desc, bool enabled = false); - - public: - /// The name of this category. - const wchar_t *const name; - - /// A (non-localized) description of the category. - const wchar_t *const description; - - /// Whether the category is enabled. - relaxed_atomic_bool_t enabled; -}; - -class category_list_t { - category_list_t() = default; - - public: - /// The singleton global category list instance. - static category_list_t *const g_instance; - - /// What follows are the actual logging categories. - /// To add a new category simply define a new field, following the pattern. - - category_t error{L"error", L"Serious unexpected errors (on by default)", true}; - - category_t debug{L"debug", L"Debugging aid (on by default)", true}; - - category_t warning{L"warning", L"Warnings (on by default)", true}; - category_t warning_path{ - L"warning-path", L"Warnings about unusable paths for config/history (on by default)", true}; - - category_t config{L"config", L"Finding and reading configuration"}; - - category_t event{L"event", L"Firing events"}; - - category_t exec{L"exec", L"Errors reported by exec (on by default)", true}; - - category_t exec_job_status{L"exec-job-status", L"Jobs changing status"}; - - category_t exec_job_exec{L"exec-job-exec", L"Jobs being executed"}; - - category_t exec_fork{L"exec-fork", L"Calls to fork()"}; - - category_t output_invalid{L"output-invalid", L"Trying to print invalid output"}; - category_t ast_construction{L"ast-construction", L"Parsing fish AST"}; - - category_t proc_job_run{L"proc-job-run", L"Jobs getting started or continued"}; - - category_t proc_termowner{L"proc-termowner", L"Terminal ownership events"}; - - category_t proc_internal_proc{L"proc-internal-proc", L"Internal (non-forked) process events"}; - - category_t proc_reap_internal{L"proc-reap-internal", - L"Reaping internal (non-forked) processes"}; - - category_t proc_reap_external{L"proc-reap-external", L"Reaping external (forked) processes"}; - category_t proc_pgroup{L"proc-pgroup", L"Process groups"}; - - category_t env_locale{L"env-locale", L"Changes to locale variables"}; - - category_t env_export{L"env-export", L"Changes to exported variables"}; - - category_t env_dispatch{L"env-dispatch", L"Reacting to variables"}; - - category_t uvar_file{L"uvar-file", L"Writing/reading the universal variable store"}; - category_t uvar_notifier{L"uvar-notifier", L"Notifications about universal variable changes"}; - - category_t topic_monitor{L"topic-monitor", L"Internal details of the topic monitor"}; - category_t char_encoding{L"char-encoding", L"Character encoding issues"}; - - category_t history{L"history", L"Command history events"}; - category_t history_file{L"history-file", L"Reading/Writing the history file"}; - - category_t profile_history{L"profile-history", L"History performance measurements"}; - - category_t iothread{L"iothread", L"Background IO thread events"}; - category_t fd_monitor{L"fd-monitor", L"FD monitor events"}; - - category_t term_support{L"term-support", L"Terminal feature detection"}; - - category_t reader{L"reader", L"The interactive reader/input system"}; - category_t reader_render{L"reader-render", L"Rendering the command line"}; - category_t complete{L"complete", L"The completion system"}; - category_t path{L"path", L"Searching/using paths"}; - - category_t screen{L"screen", L"Screen repaints"}; - - category_t abbrs{L"abbrs", L"Abbreviation expansion"}; - - category_t refcell{L"refcell", L"Refcell dynamic borrowing"}; -}; - -/// The class responsible for logging. -/// This is protected by a lock. -class logger_t { - FILE *file_; - - void log1(const wchar_t *); - void log1(const char *); - void log1(wchar_t); - void log1(char); - void log1(int64_t); - void log1(uint64_t); - - void log1(const wcstring &s) { log1(s.c_str()); } - void log1(const std::string &s) { log1(s.c_str()); } - - template ::value>::type> - void log1(T v) { - if (std::is_signed::value) { - log1(static_cast(v)); - } else { - log1(static_cast(v)); - } - } - - template - void log_args_impl(const T &arg) { - log1(arg); - } - - template - void log_args_impl(const T &arg, const Ts &...rest) { - log1(arg); - log1(' '); - log_args_impl(rest...); - } - - public: - void set_file(FILE *f) { file_ = f; } - - logger_t(); - - template - void log_args(const category_t &cat, const Args &...args) { - log1(cat.name); - log1(": "); - log_args_impl(args...); - log1('\n'); - } - - void log_fmt(const category_t &cat, const wchar_t *fmt, ...); - void log_fmt(const category_t &cat, const char *fmt, ...); - - // Log outside of the usual flog usage. - void log_extra(const wchar_t *s) { log1(s); } - - // Variant of flogf which is async safe. This is intended to be used after fork(). - static void flogf_async_safe(const char *category, const char *fmt, - const char *param1 = nullptr, const char *param2 = nullptr, - const char *param3 = nullptr, const char *param4 = nullptr, - const char *param5 = nullptr, const char *param6 = nullptr, - const char *param7 = nullptr, const char *param8 = nullptr, - const char *param9 = nullptr, const char *param10 = nullptr, - const char *param11 = nullptr, const char *param12 = nullptr); -}; - -extern owning_lock g_logger; - -} // namespace flog_details - -/// Set the active flog categories according to the given wildcard \p wc. -void activate_flog_categories_by_pattern(wcstring wc); - -void set_flog_output_file_ffi(void *fp); -void flog_setlinebuf_ffi(void *f); -/// Set the file that flog should output to. -/// flog does not close this file. -void set_flog_output_file(FILE *f); - -/// \return a list of all categories, sorted by name. -std::vector get_flog_categories(); - -/// Print some extra stuff to the flog file (stderr by default). -/// This is used by the tracing machinery. -void log_extra_to_flog_file(const wcstring &s); - -/// \return the FD for the flog file. -/// This is exposed for the Rust bridge. -int get_flog_file_fd(); - -/// Output to the fish log a sequence of arguments, separated by spaces, and ending with a newline. -/// We save and restore errno because we don't want this to affect other code. -#define FLOG(wht, ...) \ - do { \ - if (flog_details::category_list_t::g_instance->wht.enabled) { \ - auto old_errno = errno; \ - flog_details::g_logger.acquire()->log_args( \ - flog_details::category_list_t::g_instance->wht, __VA_ARGS__); \ - errno = old_errno; \ - } \ - } while (0) - -/// Output to the fish log a printf-style formatted string. -#define FLOGF(wht, ...) \ - do { \ - if (flog_details::category_list_t::g_instance->wht.enabled) { \ - auto old_errno = errno; \ - flog_details::g_logger.acquire()->log_fmt( \ - flog_details::category_list_t::g_instance->wht, __VA_ARGS__); \ - errno = old_errno; \ - } \ - } while (0) - -/// Variant of FLOG which is safe to use after fork(). -/// Only %s specifiers are supported. -#define FLOGF_SAFE(wht, ...) \ - do { \ - if (flog_details::category_list_t::g_instance->wht.enabled) { \ - auto old_errno = errno; \ - flog_details::logger_t::flogf_async_safe(#wht, __VA_ARGS__); \ - errno = old_errno; \ - } \ - } while (0) - -#endif - -#define should_flog(wht) (flog_details::category_list_t::g_instance->wht.enabled) diff --git a/src/function.h b/src/function.h deleted file mode 100644 index 275d30109..000000000 --- a/src/function.h +++ /dev/null @@ -1,3 +0,0 @@ -#if INCLUDE_RUST_HEADERS -#include "function.rs.h" -#endif diff --git a/src/future_feature_flags.h b/src/future_feature_flags.h deleted file mode 100644 index a748a6324..000000000 --- a/src/future_feature_flags.h +++ /dev/null @@ -1,7 +0,0 @@ -#ifndef FISH_FUTURE_FEATURE_FLAGS_H -#define FISH_FUTURE_FEATURE_FLAGS_H - -#include "future_feature_flags.rs.h" -using feature_flag_t = FeatureFlag; - -#endif diff --git a/src/global_safety.h b/src/global_safety.h deleted file mode 100644 index 5038a7412..000000000 --- a/src/global_safety.h +++ /dev/null @@ -1,66 +0,0 @@ -// Support for enforcing correct access to globals. -#ifndef FISH_GLOBAL_SAFETY_H -#define FISH_GLOBAL_SAFETY_H - -#include "config.h" // IWYU pragma: keep - -#include - -#include "common.h" - -/// A latch variable may only be set once. -/// The value is immortal. -template -class latch_t : noncopyable_t, nonmovable_t { - T *value_{}; - - public: - operator T *() { return value_; } - operator const T *() const { return value_; } - - T *operator->() { return value_; } - const T *operator->() const { return value_; } - - bool is_set() const { return value_ != nullptr; } - - void operator=(std::unique_ptr value) { - assert(value_ == nullptr && "Latch variable initialized multiple times"); - assert(value != nullptr && "Latch variable initialized with null"); - // Note: deliberate leak. - value_ = value.release(); - } - - void operator=(T &&value) { *this = make_unique(std::move(value)); } -}; - -/// An atomic type that always use relaxed reads. -template -class relaxed_atomic_t { - std::atomic value_{}; - - public: - relaxed_atomic_t() = default; - relaxed_atomic_t(T value) : value_(value) {} - - operator T() const volatile { return value_.load(std::memory_order_relaxed); } - - void operator=(T v) { return value_.store(v, std::memory_order_relaxed); } - void operator=(T v) volatile { return value_.store(v, std::memory_order_relaxed); } - - // Perform a CAS operation, returning whether it succeeded. - bool compare_exchange(T expected, T desired) { - return value_.compare_exchange_strong(expected, desired, std::memory_order_relaxed); - } - - // postincrement - T operator++(int) { return value_.fetch_add(1, std::memory_order_relaxed); } - T operator--(int) { return value_.fetch_sub(1, std::memory_order_relaxed); } - - // preincrement - T operator++() { return value_.fetch_add(1, std::memory_order_relaxed) + 1; } - T operator--() { return value_.fetch_sub(1, std::memory_order_relaxed) - 1; } -}; - -using relaxed_atomic_bool_t = relaxed_atomic_t; - -#endif diff --git a/src/highlight.cpp b/src/highlight.cpp deleted file mode 100644 index 800d2ad06..000000000 --- a/src/highlight.cpp +++ /dev/null @@ -1,35 +0,0 @@ -#include "highlight.h" - -highlight_spec_t::highlight_spec_t() : val(new_highlight_spec()) {} -highlight_spec_t::highlight_spec_t(const highlight_spec_t &other) : val(new_highlight_spec()) { - *this = other; -} -highlight_spec_t &highlight_spec_t::operator=(const highlight_spec_t &other) { - this->val = other.val->clone(); - return *this; -} - -highlight_spec_t::highlight_spec_t(HighlightSpec other) : val(other.clone()) {} -highlight_spec_t::highlight_spec_t(highlight_role_t fg, highlight_role_t bg) - : val(new_highlight_spec()) { - val->foreground = fg; - val->background = bg; -} - -highlight_spec_t::operator HighlightSpec() const { return *val; } - -highlight_spec_t highlight_spec_t::make_background(highlight_role_t bg_role) { - return highlight_spec_t{highlight_role_t::normal, bg_role}; -} - -bool highlight_spec_t::operator==(const highlight_spec_t &other) const { - return *this->val == *other.val; -} -bool highlight_spec_t::operator!=(const highlight_spec_t &other) const { return !(*this == other); } - -void highlight_shell(const wcstring &buff, std::vector &colors, - const operation_context_t &ctx, bool io_ok, std::shared_ptr cursor) { - auto ffi_colors = highlight_shell_ffi(buff, ctx, io_ok, cursor); - colors.resize(ffi_colors->size()); - for (size_t i = 0; i < ffi_colors->size(); i++) colors[i] = ffi_colors->at(i); -} diff --git a/src/highlight.h b/src/highlight.h deleted file mode 100644 index 442d13ebd..000000000 --- a/src/highlight.h +++ /dev/null @@ -1,62 +0,0 @@ -// Prototypes for functions for syntax highlighting. -#ifndef FISH_HIGHLIGHT_H -#define FISH_HIGHLIGHT_H - -#include - -#include -#include -#include -#include -#include -#include - -#include "ast.h" -#include "color.h" -#include "cxx.h" -#include "env.h" -#include "flog.h" -#include "history.h" -#include "maybe.h" -#include "operation_context.h" -#include "parser.h" - -struct highlight_spec_t; - -#if INCLUDE_RUST_HEADERS -#include "highlight.rs.h" -#else -struct HighlightSpec; -enum class HighlightRole : uint8_t; -struct HighlightSpecListFFI; -#endif - -using highlight_role_t = HighlightRole; -// using highlight_spec_t = HighlightSpec; - -struct highlight_spec_t { - rust::Box val; - - highlight_spec_t(); - highlight_spec_t(const highlight_spec_t &other); - highlight_spec_t &operator=(const highlight_spec_t &other); - - highlight_spec_t(HighlightSpec other); - highlight_spec_t(highlight_role_t fg, highlight_role_t bg = {}); - - bool operator==(const highlight_spec_t &other) const; - bool operator!=(const highlight_spec_t &other) const; - - HighlightSpec *operator->() { return &*this->val; } - const HighlightSpec *operator->() const { return &*this->val; } - - operator HighlightSpec() const; - - static highlight_spec_t make_background(highlight_role_t bg_role); -}; - -void highlight_shell(const wcstring &buff, std::vector &colors, - const operation_context_t &ctx, bool io_ok = false, - std::shared_ptr cursor = {}); - -#endif diff --git a/src/history.h b/src/history.h deleted file mode 100644 index c254e3af5..000000000 --- a/src/history.h +++ /dev/null @@ -1,57 +0,0 @@ -// Prototypes for history functions, part of the user interface. -#ifndef FISH_HISTORY_H -#define FISH_HISTORY_H - -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "common.h" -#include "maybe.h" -#include "wutil.h" // IWYU pragma: keep - -using path_list_t = std::vector; -using history_identifier_t = uint64_t; - -#if INCLUDE_RUST_HEADERS - -#include "history.rs.h" - -#else - -struct HistoryItem; - -class HistorySharedPtr; -enum class PersistenceMode; -enum class SearchDirection; -enum class SearchType; - -#endif // INCLUDE_RUST_HEADERS - -using history_item_t = HistoryItem; -using history_persistence_mode_t = PersistenceMode; -using history_search_direction_t = SearchDirection; -using history_search_type_t = SearchType; -using history_search_flags_t = uint32_t; - -/// Flags for history searching. -enum { - /// If set, ignore case. - history_search_ignore_case = 1 << 0, - - /// If set, do not deduplicate, which can help performance. - history_search_no_dedup = 1 << 1 -}; -using history_search_flags_t = uint32_t; - -#endif diff --git a/src/io.h b/src/io.h deleted file mode 100644 index ec9af69cf..000000000 --- a/src/io.h +++ /dev/null @@ -1,182 +0,0 @@ -#ifndef FISH_IO_H -#define FISH_IO_H - -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#include "common.h" -#include "cxx.h" -#include "fds.h" -#include "global_safety.h" -#include "redirection.h" -#include "signals.h" -#if INCLUDE_RUST_HEADERS -#include "io.rs.h" -#else -struct IoChain; -struct IoStreams; -struct OutputStreamFfi; -#endif -using output_stream_t = OutputStreamFfi; -using io_streams_t = IoStreams; - -// null_output_stream_t - -using std::shared_ptr; - -struct job_group_t; - -/// separated_buffer_t represents a buffer of output from commands, prepared to be turned into a -/// variable. For example, command substitutions output into one of these. Most commands just -/// produce a stream of bytes, and those get stored directly. However other commands produce -/// explicitly separated output, in particular `string` like `string collect` and `string split0`. -/// The buffer tracks a sequence of elements. Some elements are explicitly separated and should not -/// be further split; other elements have inferred separation and may be split by IFS (or not, -/// depending on its value). -enum class separation_type_t { - inferred, // this element should be further separated by IFS - explicitly, // this element is explicitly separated and should not be further split -}; - -/// A separated_buffer_t contains a list of elements, some of which may be separated explicitly and -/// others which must be separated further by the user (e.g. via IFS). -class separated_buffer_t : noncopyable_t { - public: - struct element_t { - std::string contents; - separation_type_t separation; - - element_t(std::string contents, separation_type_t sep) - : contents(std::move(contents)), separation(sep) {} - - bool is_explicitly_separated() const { return separation == separation_type_t::explicitly; } - }; - - /// We not be copied but may be moved. - /// Note this leaves the moved-from value in a bogus state until clear() is called on it. - separated_buffer_t(separated_buffer_t &&) = default; - separated_buffer_t &operator=(separated_buffer_t &&) = default; - - /// Construct a separated_buffer_t with the given buffer limit \p limit, or 0 for no limit. - separated_buffer_t(size_t limit) : buffer_limit_(limit) {} - - /// \return the buffer limit size, or 0 for no limit. - size_t limit() const { return buffer_limit_; } - - /// \return the contents size. - size_t size() const { return contents_size_; } - - /// \return whether the output has been discarded. - bool discarded() const { return discard_; } - - /// Serialize the contents to a single string, where explicitly separated elements have a - /// newline appended. - std::string newline_serialized() const { - std::string result; - result.reserve(size()); - for (const auto &elem : elements_) { - result.append(elem.contents); - if (elem.is_explicitly_separated()) { - result.push_back('\n'); - } - } - return result; - } - - /// \return the list of elements. - const std::vector &elements() const { return elements_; } - - /// Append a string \p str of a given length \p len, with separation type \p sep. - bool append(const char *str, size_t len, separation_type_t sep = separation_type_t::inferred) { - if (!try_add_size(len)) return false; - // Try merging with the last element. - if (sep == separation_type_t::inferred && last_inferred()) { - elements_.back().contents.append(str, len); - } else { - elements_.emplace_back(std::string(str, len), sep); - } - return true; - } - - /// Append a string \p str with separation type \p sep. - bool append(std::string &&str, separation_type_t sep = separation_type_t::inferred) { - if (!try_add_size(str.size())) return false; - // Try merging with the last element. - if (sep == separation_type_t::inferred && last_inferred()) { - elements_.back().contents.append(str); - } else { - elements_.emplace_back(std::move(str), sep); - } - return true; - } - - /// Remove all elements and unset the discard flag. - void clear() { - elements_.clear(); - contents_size_ = 0; - discard_ = false; - } - - private: - /// \return true if our last element has an inferred separation type. - bool last_inferred() const { - return !elements_.empty() && !elements_.back().is_explicitly_separated(); - } - - /// If our last element has an inferred separation, return a pointer to it; else nullptr. - /// This is useful for appending one inferred separation to another. - element_t *last_if_inferred() { - if (!elements_.empty() && !elements_.back().is_explicitly_separated()) { - return &elements_.back(); - } - return nullptr; - } - - /// Mark that we are about to add the given size \p delta to the buffer. \return true if we - /// succeed, false if we exceed buffer_limit. - bool try_add_size(size_t delta) { - if (discard_) return false; - size_t proposed_size = contents_size_ + delta; - if ((proposed_size < delta) || (buffer_limit_ > 0 && proposed_size > buffer_limit_)) { - clear(); - discard_ = true; - return false; - } - contents_size_ = proposed_size; - return true; - } - - /// Limit on how much data we'll buffer. Zero means no limit. - size_t buffer_limit_; - - /// Current size of all contents. - size_t contents_size_{0}; - - /// List of buffer elements. - std::vector elements_; - - /// True if we're discarding input because our buffer_limit has been exceeded. - bool discard_{false}; -}; - -/// Describes what type of IO operation an io_data_t represents. -enum class io_mode_t { file, pipe, fd, close, bufferfill }; - -class io_buffer_t; - -struct callback_args_t; -struct autoclose_fd_t2; - -using io_chain_t = IoChain; - -dup2_list_t dup2_list_resolve_chain_shim(const io_chain_t &io_chain); - -#endif diff --git a/src/iothread.h b/src/iothread.h deleted file mode 100644 index c0b42b50e..000000000 --- a/src/iothread.h +++ /dev/null @@ -1,37 +0,0 @@ -#pragma once - -#include - -#include "callback.h" -#include "threads.rs.h" - -// iothread_perform invokes a handler on a background thread. -inline void iothread_perform(std::function &&func) { - std::shared_ptr callback = std::make_shared([=](const void *) { - func(); - return nullptr; - }); - - iothread_perform(callback); -} - -/// Variant of iothread_perform that disrespects the thread limit. -/// It does its best to spawn a new thread if all other threads are occupied. -/// This is for cases where deferring a new thread might lead to deadlock. -inline void iothread_perform_cantwait(std::function &&func) { - std::shared_ptr callback = std::make_shared([=](const void *) { - func(); - return nullptr; - }); - - iothread_perform_cantwait(callback); -} - -inline bool make_detached_pthread(const std::function &func) { - std::shared_ptr callback = std::make_shared([=](const void *) { - func(); - return nullptr; - }); - - return make_detached_pthread(callback); -} diff --git a/src/lru.h b/src/lru.h deleted file mode 100644 index 6f2469e24..000000000 --- a/src/lru.h +++ /dev/null @@ -1,313 +0,0 @@ -// Least-recently-used cache implementation. -#ifndef FISH_LRU_H -#define FISH_LRU_H - -#include -#include - -#include "common.h" - -// Least-recently-used cache class. -// -// This a map from wcstring to Contents, that will evict entries when the count exceeds the maximum. -// This uses the classic LRU cache structure: a dictionary mapping keys to nodes, where the nodes -// also form a linked list. Our linked list is circular and has a sentinel node (the "mouth" - -// picture a snake swallowing its tail). This simplifies the logic: no pointer is ever NULL! It also -// works well with C++'s iterator since the sentinel node is a natural value for end(). Our nodes -// also have the unusual property of having a "back pointer": they store an iterator to the entry in -// the map containing the node. This allows us, given a node, to immediately locate the node and its -// key in the dictionary. This allows us to avoid duplicating the key in the node. -template -class lru_cache_t { - struct lru_node_t; - struct lru_link_t : noncopyable_t { - // Our doubly linked list. - // The base class is used for the mouth only. - lru_link_t *prev = nullptr; - lru_link_t *next = nullptr; - }; - - // The node type in our LRU cache - struct lru_node_t : public lru_link_t { - // Our key in the map. This is owned by the map itself. - const wcstring *key = nullptr; - - // The value from the client - Contents value; - - explicit lru_node_t(Contents &&v) : value(std::move(v)) {} - }; - - // Max node count. This may be (transiently) exceeded by add_node_without_eviction, which is - // used from background threads. - const size_t max_node_count; - - // All of our nodes. - // Note that our linked list contains pointers to these nodes in the map. - // We are dependent on the iterator-noninvalidation guarantees of std::unordered_map. - std::unordered_map node_map; - - // Head of the linked list - // The list is circular! - // If "empty" the mouth just points at itself. - lru_link_t mouth; - - // Take a node and move it to the front of the list - void promote_node(lru_node_t *node) { - assert(node != &mouth); - - // First unhook us - node->prev->next = node->next; - node->next->prev = node->prev; - - // Put us after the mouth - node->next = mouth.next; - node->next->prev = node; - node->prev = &mouth; - mouth.next = node; - } - - // Remove the node - void evict_node(lru_node_t *node) { - // We should never evict the mouth. - assert(node != &mouth && node != nullptr && node->key != nullptr); - - auto iter = this->node_map.find(*node->key); - assert(iter != this->node_map.end()); - - // Remove it from the linked list. - node->prev->next = node->next; - node->next->prev = node->prev; - - // Remove us from the map. This deallocates node! - node_map.erase(iter); - } - - // Evicts the last node - void evict_last_node() { - assert(mouth.prev != &mouth); - evict_node(static_cast(mouth.prev)); - } - - // Implementation of merge step for mergesort. - // Given two singly linked lists left and right, and a binary func F implementing less-than, - // return the list in sorted order. - template - static lru_link_t *merge(lru_link_t *left, size_t left_len, lru_link_t *right, size_t right_len, - const F &func) { - assert(left_len > 0 && right_len > 0); - - auto popleft = [&]() { - lru_link_t *ret = left; - left = left->next; - left_len--; - return ret; - }; - - auto popright = [&]() { - lru_link_t *ret = right; - right = right->next; - right_len--; - return ret; - }; - - lru_link_t *head; - lru_link_t **cursor = &head; - while (left_len && right_len) { - bool goleft = !func(static_cast(left)->value, - static_cast(right)->value); - *cursor = goleft ? popleft() : popright(); - cursor = &(*cursor)->next; - } - while (left_len || right_len) { - *cursor = left_len ? popleft() : popright(); - cursor = &(*cursor)->next; - } - return head; - } - - // mergesort the given list of the given length. - // This only sets the next pointers, not the prev ones. - template - static lru_link_t *mergesort(lru_link_t *node, size_t length, const F &func) { - if (length <= 1) { - return node; - } - // divide us into two lists, left and right - const size_t left_len = length / 2; - const size_t right_len = length - left_len; - lru_link_t *left = node; - - lru_link_t *right = node; - for (size_t i = 0; i < left_len; i++) { - right = right->next; - } - - // Recursive sorting - left = mergesort(left, left_len, func); - right = mergesort(right, right_len, func); - - // Merge them - return merge(left, left_len, right, right_len, func); - } - - public: - // Constructor. Note our linked list is always circular. - explicit lru_cache_t(size_t max_size = 1024) : max_node_count(max_size) { - mouth.next = mouth.prev = &mouth; - } - - // Returns the value for a given key, or NULL. - // This counts as a "use" and so promotes the node - Contents *get(const wcstring &key) { - auto where = this->node_map.find(key); - if (where == this->node_map.end()) { - // not found - return nullptr; - } - promote_node(&where->second); - return &where->second.value; - } - - /// \return true if we contain a value for a key. - bool contains(const wcstring &key) const { - return this->node_map.find(key) != this->node_map.end(); - } - - // Evicts the node for a given key, returning true if a node was evicted. - bool evict_node(const wcstring &key) { - auto where = this->node_map.find(key); - if (where == this->node_map.end()) return false; - evict_node(&where->second); - return true; - } - - // Adds a node under the given key. Returns true if the node was added, false if the node was - // not because a node with that key is already in the set. - bool insert(wcstring key, Contents value) { - if (!this->insert_no_eviction(std::move(key), std::move(value))) { - return false; - } - - while (this->node_map.size() > max_node_count) { - evict_last_node(); - } - return true; - } - - // Adds a node under the given key without triggering eviction. Returns true if the node was - // added, false if the node was not because a node with that key is already in the set. - bool insert_no_eviction(wcstring &&key, Contents &&value) { - // Try inserting; return false if it was already in the set. - auto iter_inserted = this->node_map.emplace(std::move(key), lru_node_t(std::move(value))); - if (!iter_inserted.second) { - // already present - so promote it - promote_node(&iter_inserted.first->second); - return false; - } - - // Tell the node where it is in the map - auto iter = iter_inserted.first; - lru_node_t *node = &iter->second; - node->key = &iter->first; - - node->next = mouth.next; - node->next->prev = node; - node->prev = &mouth; - mouth.next = node; - return true; - } - - // Number of entries - size_t size() const { return this->node_map.size(); } - - // Given a binary function F implementing less-than on the contents, place the nodes in sorted - // order. - template - void stable_sort(const F &func) { - // Perform the sort. This sets forward pointers only - size_t length = this->size(); - if (length <= 1) { - return; - } - - lru_link_t *sorted = mergesort(this->mouth.next, length, func); - mouth.next = sorted; - // Go through and set back back pointers. - lru_link_t *cursor = sorted; - lru_link_t *prev = &mouth; - for (size_t i = 0; i < length; i++) { - cursor->prev = prev; - prev = cursor; - cursor = cursor->next; - } - // prev is now last element in list - // make the list circular - prev->next = &mouth; - mouth.prev = prev; - } - - void evict_all_nodes(void) { - while (this->size() > 0) { - evict_last_node(); - } - } - - // Iterator for walking nodes, from least recently used to most. - class iterator { - const lru_link_t *node; - - public: - using value_type = std::pair; - - explicit iterator(const lru_link_t *val) : node(val) {} - void operator++() { node = node->prev; } - bool operator==(const iterator &other) const { return node == other.node; } - bool operator!=(const iterator &other) const { return !(*this == other); } - value_type operator*() const { - const lru_node_t *dnode = static_cast(node); - return {*dnode->key, dnode->value}; - } - }; - - iterator begin() const { return iterator(mouth.prev); } - iterator end() const { return iterator(&mouth); } - - void check_sanity() const { - // Check linked list sanity - size_t expected_count = this->size(); - const lru_link_t *prev = &mouth; - const lru_link_t *cursor = mouth.next; - - size_t max = 1024 * 1024 * 64; - size_t count = 0; - while (cursor != &mouth) { - if (cursor->prev != prev) { - DIE("node busted previous link"); - } - prev = cursor; - cursor = cursor->next; - if (count++ > max) { - DIE("LRU cache unable to re-reach the mouth - not circularly linked?"); - } - } - if (mouth.prev != prev) { - DIE("mouth.prev does not connect to last node"); - } - if (count != expected_count) { - DIE("linked list count mismatch from map count"); - } - - // Count iterators - size_t iter_dist = 0; - for (auto p : *this) { - UNUSED(p); - iter_dist++; - } - if (iter_dist != count) { - DIE("linked list iterator mismatch from map count"); - } - } -}; - -#endif diff --git a/src/maybe.h b/src/maybe.h deleted file mode 100644 index 3dde986c2..000000000 --- a/src/maybe.h +++ /dev/null @@ -1,258 +0,0 @@ -#ifndef FISH_MAYBE_H -#define FISH_MAYBE_H - -#include -#include -#include -#include -#include - -namespace maybe_detail { -// Template magic to make maybe_t (trivially) copyable iff T is. - -// This is an unsafe implementation: it is always trivially copyable. -template -struct maybe_impl_trivially_copyable_t { - alignas(T) char storage[sizeof(T)]; - bool filled = false; - - T &value() { - assert(filled && "maybe_t does not have a value"); - return *reinterpret_cast(storage); - } - - const T &value() const { - assert(filled && "maybe_t does not have a value"); - return *reinterpret_cast(storage); - } - - void reset() { - if (this->filled) { - value().~T(); - this->filled = false; - } - } - - T acquire() { - assert(filled && "maybe_t does not have a value"); - T res = std::move(value()); - reset(); - return res; - } - - maybe_impl_trivially_copyable_t() = default; - - // Move construction/assignment from a T. - explicit maybe_impl_trivially_copyable_t(T &&v) : filled(true) { - new (storage) T(std::forward(v)); - } - maybe_impl_trivially_copyable_t &operator=(T &&v) { - if (filled) { - value() = std::move(v); - } else { - new (storage) T(std::move(v)); - filled = true; - } - return *this; - } - - // Copy construction/assignment from a T. - explicit maybe_impl_trivially_copyable_t(const T &v) : filled(true) { new (storage) T(v); } - maybe_impl_trivially_copyable_t &operator=(const T &v) { - if (filled) { - value() = v; - } else { - new (storage) T(v); - filled = true; - } - return *this; - } -}; - -// This is an unsafe implementation: it is always copyable. -template -struct maybe_impl_not_trivially_copyable_t : public maybe_impl_trivially_copyable_t { - using base_t = maybe_impl_trivially_copyable_t; - using base_t::maybe_impl_trivially_copyable_t; - using base_t::operator=; - using base_t::filled; - using base_t::reset; - using base_t::storage; - - // Move construction/assignment from another instance. - maybe_impl_not_trivially_copyable_t(maybe_impl_not_trivially_copyable_t &&v) { - filled = v.filled; - if (filled) { - new (storage) T(std::move(v.value())); - } - } - - maybe_impl_not_trivially_copyable_t &operator=(maybe_impl_not_trivially_copyable_t &&v) { - if (!v.filled) { - reset(); - } else { - *this = std::move(v.value()); - } - return *this; - } - - // Copy construction/assignment from another instance. - maybe_impl_not_trivially_copyable_t(const maybe_impl_not_trivially_copyable_t &v) : base_t() { - filled = v.filled; - if (v.filled) { - new (storage) T(v.value()); - } - } - - maybe_impl_not_trivially_copyable_t &operator=(const maybe_impl_not_trivially_copyable_t &v) { - if (&v == this) return *this; - if (!v.filled) { - reset(); - } else { - *this = v.value(); - } - return *this; - } - - maybe_impl_not_trivially_copyable_t() = default; - ~maybe_impl_not_trivially_copyable_t() { reset(); } -}; - -struct copyable_t {}; -struct noncopyable_t { - noncopyable_t() = default; - noncopyable_t(noncopyable_t &&) = default; - noncopyable_t &operator=(noncopyable_t &&) = default; - noncopyable_t(const noncopyable_t &) = delete; - noncopyable_t &operator=(const noncopyable_t &) = delete; -}; - -// conditionally_copyable_t is copyable iff T is copyable. -// This enables conditionally copyable wrapper types by inheriting from it. -template -using conditionally_copyable_t = typename std::conditional::value, - copyable_t, noncopyable_t>::type; - -}; // namespace maybe_detail - -// A none_t is a helper type used to implicitly initialize maybe_t. -// Example usage: -// maybe_t sqrt(int x) { -// if (x < 0) return none(); -// return (int)sqrt(x); -// } -enum class none_t { none = 1 }; -inline constexpr none_t none() { return none_t::none; } - -// Support for a maybe, also known as Optional. -// This is a value-type class that stores a value of T in aligned storage. -template -class maybe_t : private maybe_detail::conditionally_copyable_t { - // Making maybe_t trivially copyable results in some heisenbugs if compiled under gcc 8.3.0 - // targeting 32-bit armhf platforms (Debian 10 armhf toolchain). It's confirmed not to happen if - // using clang 7 (under Debian 10 armhf) or gcc 10 (under Debian 11 armhf) so we're also - // excluding gcc 9 unless/until 32-bit armhf builds under GCC 9 are observed to pass all tests - // with this version check lowered. As this is only an optimization, just disable it across the - // board rather than only for armhf targets out of an abundance of caution. -#if __GNUG__ && __GNUC__ < 10 - using maybe_impl_t = maybe_detail::maybe_impl_not_trivially_copyable_t; -#else - using maybe_impl_t = - typename std::conditional::value, - maybe_detail::maybe_impl_trivially_copyable_t, - maybe_detail::maybe_impl_not_trivially_copyable_t >::type; -#endif - maybe_impl_t impl_; - - public: - // return whether the receiver contains a value. - bool has_value() const { return impl_.filled; } - - // A bool operator as a shortcut to test if the maybe_t has a value. - // Not enabled if the type T is already bool-convertible to prevent accidental misuse, - // otherwise the "typename std::enable_if<....>::type" evaluates to bool, giving us a definition - // of `explicit operator bool() const { ... }` - template - explicit operator typename std::enable_if::value, bool>::type() - const { - return impl_.filled; - } - - // The default constructor constructs a maybe with no value. - maybe_t() = default; - - // Construct a maybe_t from a none_t - /* implicit */ maybe_t(none_t) {} - - // Construct a maybe_t from a value T. - /* implicit */ maybe_t(T &&v) : impl_(std::move(v)) {} - - // Construct a maybe_t from a value T. - /* implicit */ maybe_t(const T &v) : impl_(v) {} - - // Copy and move constructors. - maybe_t(const maybe_t &) = default; - maybe_t(maybe_t &&) = default; - - /* implicit */ maybe_t(std::unique_ptr v) : maybe_t() { - if (v) *this = std::move(*v); - } - - // Construct a value in-place. - template - void emplace(Args &&...args) { - reset(); - impl_.filled = true; - new (impl_.storage) T(std::forward(args)...); - } - - // Access the value. - T &value() { return impl_.value(); } - - const T &value() const { return impl_.value(); } - - // Transfer the value to the caller. - T acquire() { return impl_.acquire(); } - - // Return (a copy of) our value, or the given value if we are empty. - T value_or(T v) const { - if (this->has_value()) { - return this->value(); - } - return v; - } - - // Clear the value. - void reset() { impl_.reset(); } - - // Assign a new value. - maybe_t &operator=(T &&v) { - impl_ = std::move(v); - return *this; - } - - // Note that defaulting these allows these to be conditionally deleted via - // conditionally_copyable_t(). - maybe_t &operator=(const maybe_t &) = default; - maybe_t &operator=(maybe_t &&) = default; - - // Dereference support. - const T *operator->() const { return &value(); } - T *operator->() { return &value(); } - const T &operator*() const { return value(); } - T &operator*() { return value(); } - - // Compare values for equality. - bool operator==(const maybe_t &rhs) const { - if (this->has_value() && rhs.has_value()) return this->value() == rhs.value(); - return this->has_value() == rhs.has_value(); - } - - bool operator!=(const maybe_t &rhs) const { return !(*this == rhs); } - - bool operator==(const T &rhs) const { return this->has_value() && this->value() == rhs; } - - bool operator!=(const T &rhs) const { return !(*this == rhs); } -}; - -#endif diff --git a/src/operation_context.h b/src/operation_context.h deleted file mode 100644 index fa991c4c7..000000000 --- a/src/operation_context.h +++ /dev/null @@ -1,23 +0,0 @@ -#ifndef FISH_OPERATION_CONTEXT_H -#define FISH_OPERATION_CONTEXT_H - -#include - -struct OperationContext; -#if INCLUDE_RUST_HEADERS -#include "operation_context.rs.h" -#else -#endif - -using operation_context_t = OperationContext; - -/// Default limits for expansion. -enum expansion_limit_t : size_t { - /// The default maximum number of items from expansion. - kExpansionLimitDefault = 512 * 1024, - - /// A smaller limit for background operations like syntax highlighting. - kExpansionLimitBackground = 512, -}; - -#endif diff --git a/src/output.cpp b/src/output.cpp deleted file mode 100644 index b6054c30d..000000000 --- a/src/output.cpp +++ /dev/null @@ -1,147 +0,0 @@ -// Generic output functions. -#include "config.h" - -#include -#include - -#if HAVE_CURSES_H -#include // IWYU pragma: keep -#elif HAVE_NCURSES_H -#include -#elif HAVE_NCURSES_CURSES_H -#include -#endif -#if HAVE_TERM_H -#include -#elif HAVE_NCURSES_TERM_H -#include -#endif - -#include -#include -#include -#include - -#include "color.h" -#include "common.h" -#include "env.h" -#include "fallback.h" // IWYU pragma: keep -#include "flog.h" -#include "maybe.h" -#include "output.h" -#include "threads.rs.h" -#include "wcstringutil.h" -#include "wutil.h" // IWYU pragma: keep - -/// Given a list of rgb_color_t, pick the "best" one, as determined by the color support. Returns -/// rgb_color_t::none() if empty. -/// TODO: This is duplicated with Rust. -rgb_color_t best_color(const std::vector &candidates, color_support_t support) { - if (candidates.empty()) { - return rgb_color_t::none(); - } - - rgb_color_t first_rgb = rgb_color_t::none(), first_named = rgb_color_t::none(); - for (const auto &color : candidates) { - if (first_rgb.is_none() && color.is_rgb()) { - first_rgb = color; - } - if (first_named.is_none() && color.is_named()) { - first_named = color; - } - } - // If we have both RGB and named colors, then prefer rgb if term256 is supported. - rgb_color_t result = rgb_color_t::none(); - bool has_term256 = static_cast(support & color_support_term256); - if ((!first_rgb.is_none() && has_term256) || first_named.is_none()) { - result = first_rgb; - } else { - result = first_named; - } - if (result.is_none()) { - result = candidates.at(0); - } - return result; -} - -/// Return the internal color code representing the specified color. -/// TODO: This code should be refactored to enable sharing with builtin_set_color. -/// In particular, the argument parsing still isn't fully capable. -/// TODO: This is duplicated with Rust. -rgb_color_t parse_color(const env_var_t &var, bool is_background) { - bool is_bold = false; - bool is_underline = false; - bool is_italics = false; - bool is_dim = false; - bool is_reverse = false; - - std::vector candidates; - - const wchar_t *prefix = L"--background="; - // wcslen is not available as constexpr - size_t prefix_len = wcslen(prefix); - - bool next_is_background = false; - wcstring color_name; - auto vals = var.as_list(); - for (const wcstring &next : vals) { - color_name.clear(); - if (is_background) { - if (color_name.empty() && next_is_background) { - color_name = next; - next_is_background = false; - } else if (string_prefixes_string(prefix, next)) { - // Look for something like "--background=red". - color_name = wcstring(next, prefix_len); - } else if (next == L"--background" || next == L"-b") { - // Without argument attached the next token is the color - // - if it's another option it's an error. - next_is_background = true; - } else if (next == L"--reverse" || next == L"-r") { - // Reverse should be meaningful in either context - is_reverse = true; - } else if (string_prefixes_string(L"-b", next)) { - // Look for something like "-bred". - // Yes, that length is hardcoded. - color_name = wcstring(next, 2); - } - } else { - if (next == L"--bold" || next == L"-o") - is_bold = true; - else if (next == L"--underline" || next == L"-u") - is_underline = true; - else if (next == L"--italics" || next == L"-i") - is_italics = true; - else if (next == L"--dim" || next == L"-d") - is_dim = true; - else if (next == L"--reverse" || next == L"-r") - is_reverse = true; - else - color_name = next; - } - - if (!color_name.empty()) { - rgb_color_t color = rgb_color_t(color_name); - if (!color.is_none()) { - candidates.push_back(color); - } - } - } - rgb_color_t result = best_color(candidates, output_get_color_support()); - - if (result.is_none()) result = rgb_color_t::normal(); - - result.set_bold(is_bold); - result.set_underline(is_underline); - result.set_italics(is_italics); - result.set_dim(is_dim); - result.set_reverse(is_reverse); - return result; -} - -void writembs_nofail(outputter_t &outp, const char *str) { - assert(str != nullptr && "Null string"); - outp.writembs(str); -} - -void writembs(outputter_t &outp, const char *str) { writembs_nofail(outp, str); } diff --git a/src/output.h b/src/output.h deleted file mode 100755 index 6bad4c248..000000000 --- a/src/output.h +++ /dev/null @@ -1,37 +0,0 @@ -// Generic output functions. -// -// Constants for various character classifications. Each character of a command string can be -// classified as one of the following types. -#ifndef FISH_OUTPUT_H -#define FISH_OUTPUT_H - -#include - -#include "color.h" - -#if INCLUDE_RUST_HEADERS -#include "output.rs.h" -#else -// Hacks to allow us to compile without Rust headers. -struct outputter_t; -#endif - -#include "env.h" - -rgb_color_t parse_color(const env_var_t &var, bool is_background); - -/// Sets what colors are supported. -enum { color_support_term256 = 1 << 0, color_support_term24bit = 1 << 1 }; -using color_support_t = uint8_t; -extern "C" { -color_support_t output_get_color_support(); -void output_set_color_support(color_support_t val); -} - -rgb_color_t best_color(const std::vector &candidates, color_support_t support); - -// Temporary to support builtin set_color. -void writembs_nofail(outputter_t &outp, const char *str); -void writembs(outputter_t &outp, const char *str); - -#endif diff --git a/src/pager.h b/src/pager.h deleted file mode 100644 index 300e04288..000000000 --- a/src/pager.h +++ /dev/null @@ -1,32 +0,0 @@ -// Pager support. -#ifndef FISH_PAGER_H -#define FISH_PAGER_H - -#include - -#include -#include - -#include "common.h" -#include "complete.h" -#include "cxx.h" -#include "highlight.h" -#include "reader.h" -#include "screen.h" - -struct termsize_t; - -#define PAGER_SELECTION_NONE static_cast(-1) - -#if INCLUDE_RUST_HEADERS -#include "pager.rs.h" -#else -struct PageRendering; -enum class selection_motion_t; -struct Pager; -#endif - -using page_rendering_t = PageRendering; -using pager_t = Pager; - -#endif diff --git a/src/parse_constants.h b/src/parse_constants.h deleted file mode 100644 index 6fdf83a27..000000000 --- a/src/parse_constants.h +++ /dev/null @@ -1,205 +0,0 @@ -// Constants used in the programmatic representation of fish code. -#ifndef FISH_PARSE_CONSTANTS_H -#define FISH_PARSE_CONSTANTS_H - -#include "common.h" - -using source_offset_t = uint32_t; -constexpr source_offset_t SOURCE_OFFSET_INVALID = static_cast(-1); - -#define PARSER_DIE() \ - do { \ - FLOG(error, L"Parser dying!"); \ - exit_without_destructors(-1); \ - } while (0) - -#if INCLUDE_RUST_HEADERS - -#include "parse_constants.rs.h" - -using source_range_t = SourceRange; -using parse_token_type_t = ParseTokenType; -using parse_keyword_t = ParseKeyword; -using statement_decoration_t = StatementDecoration; -using parse_error_code_t = ParseErrorCode; -using pipeline_position_t = PipelinePosition; -using parse_error_list_t = ParseErrorListFfi; - -#else - -// Hacks to allow us to compile without Rust headers. - -#include "config.h" - -struct SourceRange { - source_offset_t start; - source_offset_t length; -}; -using source_range_t = SourceRange; - -enum class ParseTokenType : uint8_t { - invalid = 1, - string, - pipe, - redirection, - background, - andand, - oror, - end, - terminate, - error, - tokenizer_error, - comment, -}; -using parse_token_type_t = ParseTokenType; - -enum class ParseKeyword : uint8_t { - none, - kw_and, - kw_begin, - kw_builtin, - kw_case, - kw_command, - kw_else, - kw_end, - kw_exclam, - kw_exec, - kw_for, - kw_function, - kw_if, - kw_in, - kw_not, - kw_or, - kw_switch, - kw_time, - kw_while, -}; -using parse_keyword_t = ParseKeyword; - -enum class StatementDecoration : uint8_t { - none, - command, - builtin, - exec, -}; -using statement_decoration_t = StatementDecoration; - -enum class parse_error_code_t : uint8_t { - none, - syntax, - cmdsubst, - generic, - tokenizer_unterminated_quote, - tokenizer_unterminated_subshell, - tokenizer_unterminated_slice, - tokenizer_unterminated_escape, - tokenizer_other, - unbalancing_end, - unbalancing_else, - unbalancing_case, - bare_variable_assignment, - andor_in_pipeline, -}; - -struct ParseErrorListFfi; -using parse_error_list_t = ParseErrorListFfi; - -#endif - -// Special source_start value that means unknown. -#define SOURCE_LOCATION_UNKNOWN (static_cast(-1)) - -enum { - parse_flag_none = 0, - parse_flag_continue_after_error = 1 << 0, - parse_flag_include_comments = 1 << 1, - parse_flag_accept_incomplete_tokens = 1 << 2, - parse_flag_leave_unterminated = 1 << 3, - parse_flag_show_blank_lines = 1 << 4, - parse_flag_show_extra_semis = 1 << 5, -}; -using parse_tree_flags_t = uint8_t; - -enum { PARSER_TEST_ERROR = 1, PARSER_TEST_INCOMPLETE = 2 }; -using parser_test_error_bits_t = uint8_t; - -/// Maximum number of function calls. -#define FISH_MAX_STACK_DEPTH 128 - -/// Maximum number of nested string substitutions (in lieu of evals) -/// Reduced under TSAN: our CI test creates 500 jobs and this is very slow with TSAN. -#if FISH_TSAN_WORKAROUNDS -#define FISH_MAX_EVAL_DEPTH 250 -#else -#define FISH_MAX_EVAL_DEPTH 500 -#endif - -/// Error message on a function that calls itself immediately. -#define INFINITE_FUNC_RECURSION_ERR_MSG \ - _(L"The function '%ls' calls itself immediately, which would result in an infinite loop.") - -/// Error message on reaching maximum call stack depth. -#define CALL_STACK_LIMIT_EXCEEDED_ERR_MSG \ - _(L"The call stack limit has been exceeded. Do you have an accidental infinite loop?") - -/// Error message when encountering an unknown builtin name. -#define UNKNOWN_BUILTIN_ERR_MSG _(L"Unknown builtin '%ls'") - -/// Error message when encountering a failed expansion, e.g. for the variable name in for loops. -#define FAILED_EXPANSION_VARIABLE_NAME_ERR_MSG _(L"Unable to expand variable name '%ls'") - -/// Error message when encountering an illegal file descriptor. -#define ILLEGAL_FD_ERR_MSG _(L"Illegal file descriptor in redirection '%ls'") - -/// Error message for wildcards with no matches. -#define WILDCARD_ERR_MSG _(L"No matches for wildcard '%ls'. See `help wildcards-globbing`.") - -/// Error when using break outside of loop. -#define INVALID_BREAK_ERR_MSG _(L"'break' while not inside of loop") - -/// Error when using continue outside of loop. -#define INVALID_CONTINUE_ERR_MSG _(L"'continue' while not inside of loop") - -// Error messages. The number is a reminder of how many format specifiers are contained. - -/// Error for $^. -#define ERROR_BAD_VAR_CHAR1 _(L"$%lc is not a valid variable in fish.") - -/// Error for ${a}. -#define ERROR_BRACKETED_VARIABLE1 _(L"Variables cannot be bracketed. In fish, please use {$%ls}.") - -/// Error for "${a}". -#define ERROR_BRACKETED_VARIABLE_QUOTED1 \ - _(L"Variables cannot be bracketed. In fish, please use \"$%ls\".") - -/// Error issued on $?. -#define ERROR_NOT_STATUS _(L"$? is not the exit status. In fish, please use $status.") - -/// Error issued on $$. -#define ERROR_NOT_PID _(L"$$ is not the pid. In fish, please use $fish_pid.") - -/// Error issued on $#. -#define ERROR_NOT_ARGV_COUNT _(L"$# is not supported. In fish, please use 'count $argv'.") - -/// Error issued on $@. -#define ERROR_NOT_ARGV_AT _(L"$@ is not supported. In fish, please use $argv.") - -/// Error issued on $*. -#define ERROR_NOT_ARGV_STAR _(L"$* is not supported. In fish, please use $argv.") - -/// Error issued on $. -#define ERROR_NO_VAR_NAME _(L"Expected a variable name after this $.") - -/// Error message for Posix-style assignment: foo=bar. -#define ERROR_BAD_COMMAND_ASSIGN_ERR_MSG \ - _(L"Unsupported use of '='. In fish, please use 'set %ls %ls'.") - -/// Error message for a command like `time foo &`. -#define ERROR_TIME_BACKGROUND \ - _(L"'time' is not supported for background jobs. Consider using 'command time'.") - -/// Error issued on { echo; echo }. -#define ERROR_NO_BRACE_GROUPING \ - _(L"'{ ... }' is not supported for grouping commands. Please use 'begin; ...; end'") - -#endif diff --git a/src/parse_tree.h b/src/parse_tree.h deleted file mode 100644 index 85b557f66..000000000 --- a/src/parse_tree.h +++ /dev/null @@ -1,23 +0,0 @@ -// Programmatic representation of fish code. -#ifndef FISH_PARSE_PRODUCTIONS_H -#define FISH_PARSE_PRODUCTIONS_H - -#include - -#include "ast.h" -#include "common.h" -#include "parse_constants.h" -#include "tokenizer.h" - -#if INCLUDE_RUST_HEADERS -#include "parse_tree.rs.h" -using parsed_source_ref_t = ParsedSourceRefFFI; -#else -struct ParsedSourceRefFFI; -using parsed_source_ref_t = ParsedSourceRefFFI; -#endif - -/// Error message when a command may not be in a pipeline. -#define INVALID_PIPELINE_CMD_ERR_MSG _(L"The '%ls' command can not be used in a pipeline") - -#endif diff --git a/src/parse_util.cpp b/src/parse_util.cpp deleted file mode 100644 index c0bb989a7..000000000 --- a/src/parse_util.cpp +++ /dev/null @@ -1,1063 +0,0 @@ -// Various mostly unrelated utility functions related to parsing, loading and evaluating fish code. -// -// This library can be seen as a 'toolbox' for functions that are used in many places in fish and -// that are somehow related to parsing the code. -#include "config.h" // IWYU pragma: keep - -#include "parse_util.h" - -#include - -#include -#include -#include -#include -#include -#include - -#include "ast.h" -#include "common.h" -#include "expand.h" -#include "fallback.h" // IWYU pragma: keep -#include "future_feature_flags.h" -#include "operation_context.h" -#include "parse_constants.h" -#include "parse_tree.h" -#include "parse_util.rs.h" -#include "tokenizer.h" -#include "wcstringutil.h" -#include "wildcard.h" -#include "wutil.h" // IWYU pragma: keep - -/// Error message for use of backgrounded commands before and/or. -#define BOOL_AFTER_BACKGROUND_ERROR_MSG \ - _(L"The '%ls' command can not be used immediately after a backgrounded job") - -/// Error message for backgrounded commands as conditionals. -#define BACKGROUND_IN_CONDITIONAL_ERROR_MSG \ - _(L"Backgrounded commands can not be used as conditionals") - -/// Error message for arguments to 'end' -#define END_ARG_ERR_MSG _(L"'end' does not take arguments. Did you forget a ';'?") - -/// Maximum length of a variable name to show in error reports before truncation -static constexpr int var_err_len = 16; - -int parse_util_lineno(const wcstring &str, size_t offset) { - // Return the line number of position offset, starting with 1. - if (str.empty()) { - return 1; - } - - auto end = offset > str.length() ? str.end() : str.begin() + offset; - return std::count(str.begin(), end, L'\n') + 1; -} - -int parse_util_get_line_from_offset(const wcstring &str, size_t pos) { - // Return the line pos is on, or -1 if it's after the end. - if (pos > str.length()) return -1; - return std::count(str.begin(), str.begin() + pos, L'\n'); -} - -size_t parse_util_get_offset_from_line(const wcstring &str, int line) { - // Return the first position on line X, counting from 0. - if (line < 0) return static_cast(-1); - if (line == 0) return 0; - - size_t pos = -1; - int count = 0; - while ((pos = str.find(L'\n', pos + 1)) != wcstring::npos) { - count++; - if (count == line) return pos + 1; - } - return static_cast(-1); -} - -size_t parse_util_get_offset(const wcstring &str, int line, long line_offset) { - size_t off = parse_util_get_offset_from_line(str, line); - size_t off2 = parse_util_get_offset_from_line(str, line + 1); - - if (off == static_cast(-1)) return static_cast(-1); - if (off2 == static_cast(-1)) off2 = str.length() + 1; - if (line_offset < 0) line_offset = 0; //!OCLINT(parameter reassignment) - - if (static_cast(line_offset) >= off2 - off - 1) { - line_offset = off2 - off - 1; //!OCLINT(parameter reassignment) - } - - return off + line_offset; -} - -static int parse_util_locate_cmdsub(const wchar_t *in, const wchar_t **begin, const wchar_t **end, - bool allow_incomplete, bool *inout_is_quoted, - bool *out_has_dollar) { - bool escaped = false; - bool is_token_begin = true; - bool syntax_error = false; - int paran_count = 0; - std::vector quoted_cmdsubs; - - const wchar_t *paran_begin = nullptr, *paran_end = nullptr; - - assert(in && "null parameter"); - - const wchar_t *pos = in; - const wchar_t *last_dollar = nullptr; - auto process_opening_quote = [&](wchar_t quote) -> bool /* ok */ { - const wchar_t *q_end = quote_end(pos, quote); - if (!q_end) return false; - if (*q_end == L'$') { - last_dollar = q_end; - quoted_cmdsubs.push_back(paran_count); - } - // We want to report whether the outermost command substitution between - // paran_begin..paran_end is quoted. - if (paran_count == 0 && inout_is_quoted) { - *inout_is_quoted = *q_end == L'$'; - } - pos = q_end; - return true; - }; - - if (inout_is_quoted && *inout_is_quoted && *pos) { - if (!process_opening_quote(L'"')) pos += std::wcslen(pos); - } - - for (; *pos; pos++) { - if (!escaped) { - if (*pos == L'\'' || *pos == L'"') { - if (!process_opening_quote(*pos)) break; - } else if (*pos == L'\\') { - escaped = true; - } else if (*pos == L'#' && is_token_begin) { - pos = comment_end(pos) - 1; - } else if (*pos == L'$') { - last_dollar = pos; - } else { - if (*pos == L'(') { - if ((paran_count == 0) && (paran_begin == nullptr)) { - paran_begin = pos; - if (out_has_dollar) { - *out_has_dollar = last_dollar == pos - 1; - } - } - - paran_count++; - } else if (*pos == L')') { - paran_count--; - - if ((paran_count == 0) && (paran_end == nullptr)) { - paran_end = pos; - break; - } - - if (paran_count < 0) { - syntax_error = true; - break; - } - - // Check if the ) did complete a quoted command substitution. - if (!quoted_cmdsubs.empty() && quoted_cmdsubs.back() == paran_count) { - quoted_cmdsubs.pop_back(); - // Quoted command substitutions temporarily close double quotes. - // In "foo$(bar)baz$(qux)" - // We are here ^ - // After the ) in a quoted command substitution, we need to act as if - // there was an invisible double quote. - const wchar_t *q_end = quote_end(pos, L'"'); - if (q_end && *q_end) { // Found a valid closing quote. - // Stop at $(qux), which is another quoted command substitution. - if (*q_end == L'$') quoted_cmdsubs.push_back(paran_count); - pos = q_end; - } else { - break; - } - } - } - } - is_token_begin = is_token_delimiter(pos[0], std::make_shared(pos[1])); - } else { - escaped = false; - is_token_begin = false; - } - } - - syntax_error |= (paran_count < 0); - syntax_error |= ((paran_count > 0) && (!allow_incomplete)); - - if (syntax_error) { - return -1; - } - - if (paran_begin == nullptr) { - return 0; - } - - if (begin) { - *begin = paran_begin; - } - - if (end) { - *end = paran_count ? in + std::wcslen(in) : paran_end; - } - - return 1; -} - -int parse_util_locate_cmdsubst_range(const wcstring &str, size_t *inout_cursor_offset, - wcstring *out_contents, size_t *out_start, size_t *out_end, - bool accept_incomplete, bool *inout_is_quoted, - bool *out_has_dollar) { - // Clear the return values. - if (out_contents != nullptr) out_contents->clear(); - *out_start = 0; - *out_end = str.size(); - - // Nothing to do if the offset is at or past the end of the string. - if (*inout_cursor_offset >= str.size()) return 0; - - // Defer to the wonky version. - const wchar_t *const buff = str.c_str(); - const wchar_t *const valid_range_start = buff + *inout_cursor_offset, - *valid_range_end = buff + str.size(); - const wchar_t *bracket_range_begin = nullptr; - const wchar_t *bracket_range_end = nullptr; - - int ret = parse_util_locate_cmdsub(valid_range_start, &bracket_range_begin, &bracket_range_end, - accept_incomplete, inout_is_quoted, out_has_dollar); - if (ret <= 0) { - return ret; - } - - // The command substitutions must not be NULL and must be in the valid pointer range, and - // the end must be bigger than the beginning. - assert(bracket_range_begin != nullptr && bracket_range_begin >= valid_range_start && - bracket_range_begin <= valid_range_end); - assert(bracket_range_end != nullptr && bracket_range_end > bracket_range_begin && - bracket_range_end >= valid_range_start && bracket_range_end <= valid_range_end); - - // Assign the substring to the out_contents. - const wchar_t *interior_begin = bracket_range_begin + 1; - if (out_contents != nullptr) { - out_contents->assign(interior_begin, bracket_range_end - interior_begin); - } - - // Return the start and end. - *out_start = bracket_range_begin - buff; - *out_end = bracket_range_end - buff; - - // Update the inout_cursor_offset. Note this may cause it to exceed str.size(), though - // overflow is not likely. - *inout_cursor_offset = 1 + *out_end; - - return ret; -} - -void parse_util_cmdsubst_extent(const wchar_t *buff, size_t cursor_pos, const wchar_t **a, - const wchar_t **b) { - assert(buff && "Null buffer"); - const wchar_t *const cursor = buff + cursor_pos; - - const size_t bufflen = std::wcslen(buff); - assert(cursor_pos <= bufflen); - - // ap and bp are the beginning and end of the tightest command substitution found so far. - const wchar_t *ap = buff, *bp = buff + bufflen; - const wchar_t *pos = buff; - for (;;) { - const wchar_t *begin = nullptr, *end = nullptr; - if (parse_util_locate_cmdsub(pos, &begin, &end, true, nullptr, nullptr) <= 0) { - // No subshell found, all done. - break; - } - // Interpret NULL to mean the end. - if (end == nullptr) { - end = const_cast(buff) + bufflen; - } - - if (begin < cursor && end >= cursor) { - // This command substitution surrounds the cursor, so it's a tighter fit. - begin++; - ap = begin; - bp = end; - // pos is where to begin looking for the next one. But if we reached the end there's no - // next one. - if (begin >= end) break; - pos = begin + 1; - } else if (begin >= cursor) { - // This command substitution starts at or after the cursor. Since it was the first - // command substitution in the string, we're done. - break; - } else { - // This command substitution ends before the cursor. Skip it. - assert(end < cursor); - pos = end + 1; - assert(pos <= buff + bufflen); - } - } - - if (a != nullptr) *a = ap; - if (b != nullptr) *b = bp; -} - -/// Get the beginning and end of the job or process definition under the cursor. -static void job_or_process_extent(bool process, const wchar_t *buff, size_t cursor_pos, - const wchar_t **a, const wchar_t **b, - std::vector *tokens) { - assert(buff && "Null buffer"); - const wchar_t *begin = nullptr, *end = nullptr; - int finished = 0; - - if (a) *a = nullptr; - if (b) *b = nullptr; - parse_util_cmdsubst_extent(buff, cursor_pos, &begin, &end); - if (!end || !begin) { - return; - } - - assert(cursor_pos >= static_cast(begin - buff)); - const size_t pos = cursor_pos - (begin - buff); - - if (a) *a = begin; - if (b) *b = end; - - const wcstring buffcpy(begin, end); - auto tok = new_tokenizer(buffcpy.c_str(), TOK_ACCEPT_UNFINISHED | TOK_SHOW_COMMENTS); - std::unique_ptr token{}; - while ((token = tok->next()) && !finished) { - size_t tok_begin = token->offset; - - switch (token->type_) { - case token_type_t::pipe: { - if (!process) { - break; - } - __fallthrough__ - } - case token_type_t::end: - case token_type_t::background: - case token_type_t::andand: - case token_type_t::oror: { - if (tok_begin >= pos) { - finished = 1; - if (b) *b = const_cast(begin) + tok_begin; - } else { - // Statement at cursor might start after this token. - if (a) *a = const_cast(begin) + tok_begin + token->length; - if (tokens) tokens->clear(); - } - continue; // Do not add this to tokens - } - default: { - break; - } - } - if (tokens) tokens->push_back(*token); - } -} - -void parse_util_process_extent(const wchar_t *buff, size_t pos, const wchar_t **a, - const wchar_t **b, std::vector *tokens) { - job_or_process_extent(true, buff, pos, a, b, tokens); -} - -void parse_util_job_extent(const wchar_t *buff, size_t pos, const wchar_t **a, const wchar_t **b) { - job_or_process_extent(false, buff, pos, a, b, nullptr); -} - -void parse_util_token_extent(const wchar_t *buff, size_t cursor_pos, const wchar_t **tok_begin, - const wchar_t **tok_end, const wchar_t **prev_begin, - const wchar_t **prev_end) { - assert(buff && "Null buffer"); - const wchar_t *a = nullptr, *b = nullptr, *pa = nullptr, *pb = nullptr; - - const wchar_t *cmdsubst_begin, *cmdsubst_end; - parse_util_cmdsubst_extent(buff, cursor_pos, &cmdsubst_begin, &cmdsubst_end); - - if (!cmdsubst_end || !cmdsubst_begin) { - return; - } - - // pos is equivalent to cursor_pos within the range of the command substitution {begin, end}. - size_t offset_within_cmdsubst = cursor_pos - (cmdsubst_begin - buff); - - size_t bufflen = std::wcslen(buff); - - a = cmdsubst_begin + offset_within_cmdsubst; - b = a; - pa = cmdsubst_begin + offset_within_cmdsubst; - pb = pa; - - assert(cmdsubst_begin >= buff); - assert(cmdsubst_begin <= (buff + bufflen)); - assert(cmdsubst_end >= cmdsubst_begin); - assert(cmdsubst_end <= (buff + bufflen)); - - const wcstring buffcpy = wcstring(cmdsubst_begin, cmdsubst_end - cmdsubst_begin); - - auto tok = new_tokenizer(buffcpy.c_str(), TOK_ACCEPT_UNFINISHED); - while (std::unique_ptr token = tok->next()) { - size_t tok_begin = token->offset; - size_t tok_end = tok_begin; - - // Calculate end of token. - if (token->type_ == token_type_t::string) { - tok_end += token->length; - } - - // Cursor was before beginning of this token, means that the cursor is between two tokens, - // so we set it to a zero element string and break. - if (tok_begin > offset_within_cmdsubst) { - a = b = cmdsubst_begin + offset_within_cmdsubst; - break; - } - - // If cursor is inside the token, this is the token we are looking for. If so, set a and b - // and break. - if (token->type_ == token_type_t::string && tok_end >= offset_within_cmdsubst) { - a = cmdsubst_begin + token->offset; - b = a + token->length; - break; - } - - // Remember previous string token. - if (token->type_ == token_type_t::string) { - pa = cmdsubst_begin + token->offset; - pb = pa + token->length; - } - } - - if (tok_begin) *tok_begin = a; - if (tok_end) *tok_end = b; - if (prev_begin) *prev_begin = pa; - if (prev_end) *prev_end = pb; - - assert(pa >= buff); - assert(pa <= (buff + bufflen)); - assert(pb >= pa); - assert(pb <= (buff + bufflen)); -} - -wcstring parse_util_unescape_wildcards(const wcstring &str) { - wcstring result; - result.reserve(str.size()); - bool unesc_qmark = !feature_test(feature_flag_t::qmark_noglob); - - const wchar_t *const cs = str.c_str(); - for (size_t i = 0; cs[i] != L'\0'; i++) { - if (cs[i] == L'*') { - result.push_back(ANY_STRING); - } else if (cs[i] == L'?' && unesc_qmark) { - result.push_back(ANY_CHAR); - } else if (cs[i] == L'\\' && cs[i + 1] == L'*') { - result.push_back(cs[i + 1]); - i += 1; - } else if (cs[i] == L'\\' && cs[i + 1] == L'?' && unesc_qmark) { - result.push_back(cs[i + 1]); - i += 1; - } else if (cs[i] == L'\\' && cs[i + 1] == L'\\') { - // Not a wildcard, but ensure the next iteration doesn't see this escaped backslash. - result.append(L"\\\\"); - i += 1; - } else { - result.push_back(cs[i]); - } - } - return result; -} - -bool parse_util_contains_wildcards(const wcstring &str) { - bool unesc_qmark = !feature_test(feature_flag_t::qmark_noglob); - - const wchar_t *const cs = str.c_str(); - for (size_t i = 0; cs[i] != L'\0'; i++) { - if (cs[i] == L'*') { - return true; - } else if (cs[i] == L'?' && unesc_qmark) { - return true; - } else if (cs[i] == L'\\' && cs[i + 1] == L'*') { - i += 1; - } else if (cs[i] == L'\\' && cs[i + 1] == L'?' && unesc_qmark) { - i += 1; - } else if (cs[i] == L'\\' && cs[i + 1] == L'\\') { - // Not a wildcard, but ensure the next iteration doesn't see this escaped backslash. - i += 1; - } - } - return false; -} - -wcstring parse_util_escape_wildcards(const wcstring &str) { - wcstring result; - result.reserve(str.size()); - bool unesc_qmark = !feature_test(feature_flag_t::qmark_noglob); - - const wchar_t *const cs = str.c_str(); - for (size_t i = 0; cs[i] != L'\0'; i++) { - if (cs[i] == L'*') { - result.append(L"\\*"); - } else if (cs[i] == L'?' && unesc_qmark) { - result.append(L"\\?"); - } else if (cs[i] == L'\\') { - result.append(L"\\\\"); - } else { - result.push_back(cs[i]); - } - } - return result; -} - - -/// Find the outermost quoting style of current token. Returns 0 if token is not quoted. -static wchar_t get_quote(const wcstring &cmd_str, size_t len) { - size_t i = 0; - wchar_t res = 0; - const wchar_t *cmd = cmd_str.c_str(); - - while (true) { - if (!cmd[i]) break; - - if (cmd[i] == L'\\') { - i++; - if (!cmd[i]) break; - i++; - } else { - if (cmd[i] == L'\'' || cmd[i] == L'\"') { - const wchar_t *end = quote_end(&cmd[i], cmd[i]); - if ((end == nullptr) || (!*end) || (end > cmd + len)) { - res = cmd[i]; - break; - } - i = end - cmd + 1; - } else - i++; - } - } - - return res; -} - -wchar_t parse_util_get_quote_type(const wcstring &cmd, size_t pos) { - auto tok = new_tokenizer(cmd.c_str(), TOK_ACCEPT_UNFINISHED); - while (auto token = tok->next()) { - if (token->type_ == token_type_t::string && - token->location_in_or_at_end_of_source_range(pos)) { - return get_quote(*tok->text_of(*token), pos - token->offset); - } - } - return L'\0'; -} - -wcstring parse_util_escape_string_with_quote(const wcstring &cmd, wchar_t quote, bool no_tilde) { - wcstring result; - if (quote == L'\0') { - escape_flags_t flags = ESCAPE_NO_QUOTED | (no_tilde ? ESCAPE_NO_TILDE : 0); - result = escape_string(cmd, flags); - } else { - // Here we are going to escape a string with quotes. - // A few characters cannot be represented inside quotes, e.g. newlines. In that case, - // terminate the quote and then re-enter it. - result.reserve(cmd.size()); - for (wchar_t c : cmd) { - switch (c) { - case L'\n': - result.append({quote, L'\\', L'n', quote}); - break; - case L'\t': - result.append({quote, L'\\', L't', quote}); - break; - case L'\b': - result.append({quote, L'\\', L'b', quote}); - break; - case L'\r': - result.append({quote, L'\\', L'r', quote}); - break; - case L'\\': - result.append({L'\\', L'\\'}); - break; - case L'$': - if (quote == L'"') result.push_back(L'\\'); - result.push_back(L'$'); - break; - default: - if (c == quote) result.push_back(L'\\'); - result.push_back(c); - break; - } - } - } - return result; -} - -std::vector parse_util_compute_indents(const wcstring &src) { - auto indents = parse_util_compute_indents_ffi(src); - return {indents.begin(), indents.end()}; -} - -/// Append a syntax error to the given error list. -static bool append_syntax_error(parse_error_list_t *errors, size_t source_location, - size_t source_length, const wchar_t *fmt, ...) { - if (!errors) return true; - parse_error_t error; - error.source_start = source_location; - error.source_length = source_length; - error.code = parse_error_code_t::syntax; - - va_list va; - va_start(va, fmt); - error.text = std::make_unique(vformat_string(fmt, va)); - va_end(va); - - errors->push_back(std::move(error)); - return true; -} - -// \return a pointer to the first argument node of an argument_or_redirection_list_t, or nullptr if -// there are no arguments. -static const ast::argument_t *get_first_arg(const ast::argument_or_redirection_list_t &list) { - for (size_t i = 0; i < list.count(); i++) { - const ast::argument_or_redirection_t *v = list.at(i); - if (v->is_argument()) return &v->argument(); - } - return nullptr; -} - -/// Given a wide character immediately after a dollar sign, return the appropriate error message. -/// For example, if wc is @, then the variable name was $@ and we suggest $argv. -static const wchar_t *error_format_for_character(wchar_t wc) { - switch (wc) { - case L'?': { - return ERROR_NOT_STATUS; - } - case L'#': { - return ERROR_NOT_ARGV_COUNT; - } - case L'@': { - return ERROR_NOT_ARGV_AT; - } - case L'*': { - return ERROR_NOT_ARGV_STAR; - } - case L'$': - case VARIABLE_EXPAND: - case VARIABLE_EXPAND_SINGLE: - case VARIABLE_EXPAND_EMPTY: { - return ERROR_NOT_PID; - } - case BRACE_END: - case L'}': - case L',': - case BRACE_SEP: { - return ERROR_NO_VAR_NAME; - } - default: { - return ERROR_BAD_VAR_CHAR1; - } - } -} - -void parse_util_expand_variable_error(const wcstring &token, size_t global_token_pos, - size_t dollar_pos, parse_error_list_t *errors) { - // Note that dollar_pos is probably VARIABLE_EXPAND or VARIABLE_EXPAND_SINGLE, not a literal - // dollar sign. - assert(errors != nullptr); - assert(dollar_pos < token.size()); - const bool double_quotes = token.at(dollar_pos) == VARIABLE_EXPAND_SINGLE; - const size_t start_error_count = errors->size(); - const size_t global_dollar_pos = global_token_pos + dollar_pos; - const size_t global_after_dollar_pos = global_dollar_pos + 1; - wchar_t char_after_dollar = dollar_pos + 1 >= token.size() ? 0 : token.at(dollar_pos + 1); - - switch (char_after_dollar) { - case BRACE_BEGIN: - case L'{': { - // The BRACE_BEGIN is for unquoted, the { is for quoted. Anyways we have (possible - // quoted) ${. See if we have a }, and the stuff in between is variable material. If so, - // report a bracket error. Otherwise just complain about the ${. - bool looks_like_variable = false; - size_t closing_bracket = - token.find(char_after_dollar == L'{' ? L'}' : wchar_t(BRACE_END), dollar_pos + 2); - wcstring var_name; - if (closing_bracket != wcstring::npos) { - size_t var_start = dollar_pos + 2, var_end = closing_bracket; - var_name = wcstring(token, var_start, var_end - var_start); - looks_like_variable = valid_var_name(var_name); - } - if (looks_like_variable) { - append_syntax_error( - errors, global_after_dollar_pos, 1, - double_quotes ? ERROR_BRACKETED_VARIABLE_QUOTED1 : ERROR_BRACKETED_VARIABLE1, - truncate(var_name, var_err_len).c_str()); - } else { - append_syntax_error(errors, global_after_dollar_pos, 1, ERROR_BAD_VAR_CHAR1, L'{'); - } - break; - } - case INTERNAL_SEPARATOR: { - // e.g.: echo foo"$"baz - // These are only ever quotes, not command substitutions. Command substitutions are - // handled earlier. - append_syntax_error(errors, global_dollar_pos, 1, ERROR_NO_VAR_NAME); - break; - } - case L'\0': { - append_syntax_error(errors, global_dollar_pos, 1, ERROR_NO_VAR_NAME); - break; - } - default: { - wchar_t token_stop_char = char_after_dollar; - // Unescape (see issue #50). - if (token_stop_char == ANY_CHAR) - token_stop_char = L'?'; - else if (token_stop_char == ANY_STRING || token_stop_char == ANY_STRING_RECURSIVE) - token_stop_char = L'*'; - - // Determine which error message to use. The format string may not consume all the - // arguments we pass but that's harmless. - const wchar_t *error_fmt = error_format_for_character(token_stop_char); - - append_syntax_error(errors, global_after_dollar_pos, 1, error_fmt, token_stop_char); - break; - } - } - - // We should have appended exactly one error. - assert(errors->size() == start_error_count + 1); -} - -/// Test if this argument contains any errors. Detected errors include syntax errors in command -/// substitutions, improperly escaped characters and improper use of the variable expansion -/// operator. -parser_test_error_bits_t parse_util_detect_errors_in_argument(const ast::argument_t &arg, - const wcstring &arg_src, - parse_error_list_t *out_errors) { - if (!arg.try_source_range()) return 0; - auto source_range = arg.source_range(); - - size_t source_start = source_range.start; - parser_test_error_bits_t err = 0; - - auto check_subtoken = [&arg_src, &out_errors, source_start](size_t begin, size_t end) -> int { - auto maybe_unesc = unescape_string(arg_src.c_str() + begin, end - begin, UNESCAPE_SPECIAL, - STRING_STYLE_SCRIPT); - if (!maybe_unesc) { - if (out_errors) { - const wchar_t *fmt = L"Invalid token '%ls'"; - if (arg_src.length() == 2 && arg_src[0] == L'\\' && - (arg_src[1] == L'c' || towlower(arg_src[1]) == L'u' || - towlower(arg_src[1]) == L'x')) { - fmt = L"Incomplete escape sequence '%ls'"; - } - - append_syntax_error(out_errors, source_start + begin, end - begin, fmt, - arg_src.c_str()); - } - return 1; - } - const wcstring &unesc = *maybe_unesc; - - parser_test_error_bits_t err = 0; - // Check for invalid variable expansions. - const size_t unesc_size = unesc.size(); - for (size_t idx = 0; idx < unesc_size; idx++) { - if (unesc.at(idx) != VARIABLE_EXPAND && unesc.at(idx) != VARIABLE_EXPAND_SINGLE) { - continue; - } - - wchar_t next_char = idx + 1 < unesc_size ? unesc.at(idx + 1) : L'\0'; - if (next_char != VARIABLE_EXPAND && next_char != VARIABLE_EXPAND_SINGLE && - next_char != '(' && !valid_var_name_char(next_char)) { - err = 1; - if (out_errors) { - // We have something like $$$^.... Back up until we reach the first $. - size_t first_dollar = idx; - while (first_dollar > 0 && - (unesc.at(first_dollar - 1) == VARIABLE_EXPAND || - unesc.at(first_dollar - 1) == VARIABLE_EXPAND_SINGLE)) { - first_dollar--; - } - parse_util_expand_variable_error(unesc, source_start, first_dollar, out_errors); - } - } - } - - return err; - }; - - size_t cursor = 0; - size_t checked = 0; - wcstring subst; - - bool do_loop = true; - bool is_quoted = false; - while (do_loop) { - size_t paren_begin = 0; - size_t paren_end = 0; - bool has_dollar = false; - switch (parse_util_locate_cmdsubst_range(arg_src, &cursor, &subst, &paren_begin, &paren_end, - false, &is_quoted, &has_dollar)) { - case -1: { - err |= PARSER_TEST_ERROR; - if (out_errors) { - append_syntax_error(out_errors, source_start, 1, L"Mismatched parenthesis"); - } - return err; - } - case 0: { - do_loop = false; - break; - } - case 1: { - err |= check_subtoken(checked, paren_begin - has_dollar); - - assert(paren_begin < paren_end && "Parens out of order?"); - auto subst_errors = new_parse_error_list(); - err |= parse_util_detect_errors(subst, &*subst_errors); - - // Our command substitution produced error offsets relative to its source. Tweak the - // offsets of the errors in the command substitution to account for both its offset - // within the string, and the offset of the node. - size_t error_offset = paren_begin + 1 + source_start; - subst_errors->offset_source_start(error_offset); - - if (out_errors != nullptr) { - out_errors->append(&*subst_errors); - } - - checked = paren_end + 1; - break; - } - default: { - DIE("unexpected parse_util_locate_cmdsubst() return value"); - } - } - } - err |= check_subtoken(checked, arg_src.size()); - - return err; -} - -/// Given that the job given by node should be backgrounded, return true if we detect any errors. -static bool detect_errors_in_backgrounded_job(const ast::job_pipeline_t &job, - parse_error_list_t *parse_errors) { - using namespace ast; - if (!job.try_source_range()) return false; - auto source_range = job.source_range(); - - bool errored = false; - // Disallow background in the following cases: - // foo & ; and bar - // foo & ; or bar - // if foo & ; end - // while foo & ; end - const job_conjunction_t *job_conj = job.ptr()->parent()->try_as_job_conjunction(); - if (!job_conj) return false; - - if (job_conj->ptr()->parent()->try_as_if_clause()) { - errored = append_syntax_error(parse_errors, source_range.start, source_range.length, - BACKGROUND_IN_CONDITIONAL_ERROR_MSG); - } else if (job_conj->ptr()->parent()->try_as_while_header()) { - errored = append_syntax_error(parse_errors, source_range.start, source_range.length, - BACKGROUND_IN_CONDITIONAL_ERROR_MSG); - } else if (const ast::job_list_t *jlist = job_conj->ptr()->parent()->try_as_job_list()) { - // This isn't very complete, e.g. we don't catch 'foo & ; not and bar'. - // Find the index of ourselves in the job list. - size_t index; - for (index = 0; index < jlist->count(); index++) { - if (jlist->at(index) == job_conj) break; - } - assert(index < jlist->count() && "Should have found the job in the list"); - - // Try getting the next job and check its decorator. - if (const job_conjunction_t *next = jlist->at(index + 1)) { - if (next->has_decorator()) { - const auto &deco = next->decorator(); - assert((deco.keyword() == parse_keyword_t::kw_and || - deco.keyword() == parse_keyword_t::kw_or) && - "Unexpected decorator keyword"); - const wchar_t *deco_name = - (deco.keyword() == parse_keyword_t::kw_and ? L"and" : L"or"); - errored = append_syntax_error(parse_errors, deco.source_range().start, - deco.source_range().length, - BOOL_AFTER_BACKGROUND_ERROR_MSG, deco_name); - } - } - } - return errored; -} - -// Given we have a trailing argument_or_redirection_list, like `begin; end > /dev/null`, verify that -// there are no arguments in the list. -static bool detect_errors_in_block_redirection_list( - const ast::argument_or_redirection_list_t &args_or_redirs, parse_error_list_t *out_errors) { - if (const auto *first_arg = get_first_arg(args_or_redirs)) { - return append_syntax_error(out_errors, first_arg->source_range().start, - first_arg->source_range().length, END_ARG_ERR_MSG); - } - return false; -} - -parser_test_error_bits_t parse_util_detect_errors_ffi(const ast::ast_t *ast, - const wcstring &buff_src, - parse_error_list_t *out_errors) { - return parse_util_detect_errors(*ast, buff_src, out_errors); -} - -parser_test_error_bits_t parse_util_detect_errors(const ast::ast_t &ast, const wcstring &buff_src, - parse_error_list_t *out_errors) { - using namespace ast; - parser_test_error_bits_t res = 0; - - // Whether we encountered a parse error. - bool errored = false; - - // Whether we encountered an unclosed block. We detect this via an 'end_command' block without - // source. - bool has_unclosed_block = false; - - // Whether we encounter a missing statement, i.e. a newline after a pipe. This is found by - // detecting job_continuations that have source for pipes but not the statement. - bool has_unclosed_pipe = false; - - // Whether we encounter a missing job, i.e. a newline after && or ||. This is found by - // detecting job_conjunction_continuations that have source for && or || but not the job. - bool has_unclosed_conjunction = false; - - // Expand all commands. - // Verify 'or' and 'and' not used inside pipelines. - // Verify return only within a function. - // Verify no variable expansions. - wcstring storage; - - for (auto ast_traversal = new_ast_traversal(*ast.top());;) { - auto node = ast_traversal->next(); - if (!node->has_value()) break; - if (const auto *jc = node->try_as_job_continuation()) { - // Somewhat clumsy way of checking for a statement without source in a pipeline. - // See if our pipe has source but our statement does not. - if (jc->pipe().ptr()->has_source() && !jc->statement().ptr()->try_source_range()) { - has_unclosed_pipe = true; - } - } else if (const auto *jcc = node->try_as_job_conjunction_continuation()) { - // Somewhat clumsy way of checking for a job without source in a conjunction. - // See if our conjunction operator (&& or ||) has source but our job does not. - if (jcc->conjunction().ptr()->has_source() && !jcc->job().try_source_range()) { - has_unclosed_conjunction = true; - } - } else if (const argument_t *arg = node->try_as_argument()) { - wcstring arg_src = *arg->source(buff_src); - storage = arg_src; - res |= parse_util_detect_errors_in_argument(*arg, arg_src, out_errors); - } else if (const ast::job_pipeline_t *job = node->try_as_job_pipeline()) { - // Disallow background in the following cases: - // - // foo & ; and bar - // foo & ; or bar - // if foo & ; end - // while foo & ; end - // If it's not a background job, nothing to do. - if (job->has_bg()) { - errored |= detect_errors_in_backgrounded_job(*job, out_errors); - } - } else if (const auto *stmt = node->try_as_decorated_statement()) { - errored |= - detect_errors_in_decorated_statement(buff_src, (size_t)stmt, (size_t)out_errors); - } else if (const auto *block = node->try_as_block_statement()) { - // If our 'end' had no source, we are unsourced. - if (!block->end().ptr()->has_source()) has_unclosed_block = true; - errored |= detect_errors_in_block_redirection_list(block->args_or_redirs(), out_errors); - } else if (const auto *ifs = node->try_as_if_statement()) { - // If our 'end' had no source, we are unsourced. - if (!ifs->end().ptr()->has_source()) has_unclosed_block = true; - errored |= detect_errors_in_block_redirection_list(ifs->args_or_redirs(), out_errors); - } else if (const auto *switchs = node->try_as_switch_statement()) { - // If our 'end' had no source, we are unsourced. - if (!switchs->end().ptr()->has_source()) has_unclosed_block = true; - errored |= - detect_errors_in_block_redirection_list(switchs->args_or_redirs(), out_errors); - } - } - - if (errored) res |= PARSER_TEST_ERROR; - - if (has_unclosed_block || has_unclosed_pipe || has_unclosed_conjunction) - res |= PARSER_TEST_INCOMPLETE; - - return res; -} - -parser_test_error_bits_t parse_util_detect_errors(const wcstring &buff_src, - parse_error_list_t *out_errors, - bool allow_incomplete) { - // Whether there's an unclosed quote or subshell, and therefore unfinished. This is only set if - // allow_incomplete is set. - bool has_unclosed_quote_or_subshell = false; - - const parse_tree_flags_t parse_flags = - allow_incomplete ? parse_flag_leave_unterminated : parse_flag_none; - - // Parse the input string into an ast. Some errors are detected here. - using namespace ast; - auto parse_errors = new_parse_error_list(); - auto ast = ast_parse(buff_src, parse_flags, &*parse_errors); - if (allow_incomplete) { - // Issue #1238: If the only error was unterminated quote, then consider this to have parsed - // successfully. - size_t idx = parse_errors->size(); - while (idx--) { - if (parse_errors->at(idx)->code() == parse_error_code_t::tokenizer_unterminated_quote || - parse_errors->at(idx)->code() == - parse_error_code_t::tokenizer_unterminated_subshell) { - // Remove this error, since we don't consider it a real error. - has_unclosed_quote_or_subshell = true; - parse_errors->erase(idx); - } - } - } - - // has_unclosed_quote_or_subshell may only be set if allow_incomplete is true. - assert(!has_unclosed_quote_or_subshell || allow_incomplete); - if (has_unclosed_quote_or_subshell) { - // We do not bother to validate the rest of the tree in this case. - return PARSER_TEST_INCOMPLETE; - } - - // Early parse error, stop here. - if (!parse_errors->empty()) { - if (out_errors) out_errors->append(&*parse_errors); - return PARSER_TEST_ERROR; - } - - // Defer to the tree-walking version. - return parse_util_detect_errors(*ast, buff_src, out_errors); -} - -maybe_t parse_util_detect_errors_in_argument_list(const wcstring &arg_list_src, - const wcstring &prefix) { - // Helper to return a description of the first error. - auto get_error_text = [&](const parse_error_list_t &errors) { - assert(!errors.empty() && "Expected an error"); - return *errors.at(0)->describe_with_prefix( - arg_list_src, prefix, false /* not interactive */, false /* don't skip caret */); - }; - - // Parse the string as a freestanding argument list. - using namespace ast; - auto errors = new_parse_error_list(); - auto ast = ast_parse_argument_list(arg_list_src, parse_flag_none, &*errors); - if (!errors->empty()) { - return get_error_text(*errors); - } - - // Get the root argument list and extract arguments from it. - // Test each of these. - const auto &args = ast->top()->as_freestanding_argument_list().arguments(); - for (size_t i = 0; i < args.count(); i++) { - const argument_t *arg = args.at(i); - const wcstring arg_src = *arg->source(arg_list_src); - if (parse_util_detect_errors_in_argument(*arg, arg_src, &*errors)) { - return get_error_text(*errors); - } - } - return none(); -} diff --git a/src/parse_util.h b/src/parse_util.h deleted file mode 100644 index 7f6d786ed..000000000 --- a/src/parse_util.h +++ /dev/null @@ -1,158 +0,0 @@ -// Various mostly unrelated utility functions related to parsing, loading and evaluating fish code. -#ifndef FISH_PARSE_UTIL_H -#define FISH_PARSE_UTIL_H - -#include - -#include - -#include "ast.h" -#include "common.h" -#include "cxx.h" -#include "maybe.h" -#include "parse_constants.h" -#if INCLUDE_RUST_HEADERS -#include "parse_util.rs.h" -#endif - -struct Tok; -using tok_t = Tok; - -/// Alternative API. Iterate over command substitutions. -/// -/// \param str the string to search for subshells -/// \param inout_cursor_offset On input, the location to begin the search. On output, either the end -/// of the string, or just after the closed-paren. -/// \param out_contents On output, the contents of the command substitution -/// \param out_start On output, the offset of the start of the command substitution (open paren) -/// \param out_end On output, the offset of the end of the command substitution (close paren), or -/// the end of the string if it was incomplete -/// \param accept_incomplete whether to permit missing closing parenthesis -/// \param inout_is_quoted whether the cursor is in a double-quoted context. -/// \param out_has_dollar whether the command substitution has the optional leading $. -/// \return -1 on syntax error, 0 if no subshells exist and 1 on success -int parse_util_locate_cmdsubst_range(const wcstring &str, size_t *inout_cursor_offset, - wcstring *out_contents, size_t *out_start, size_t *out_end, - bool accept_incomplete, bool *inout_is_quoted = nullptr, - bool *out_has_dollar = nullptr); - -/// Find the beginning and end of the command substitution under the cursor. If no subshell is -/// found, the entire string is returned. If the current command substitution is not ended, i.e. the -/// closing parenthesis is missing, then the string from the beginning of the substitution to the -/// end of the string is returned. -/// -/// \param buff the string to search for subshells -/// \param cursor_pos the position of the cursor -/// \param a the start of the searched string -/// \param b the end of the searched string -void parse_util_cmdsubst_extent(const wchar_t *buff, size_t cursor_pos, const wchar_t **a, - const wchar_t **b); - -/// Find the beginning and end of the process definition under the cursor -/// -/// \param buff the string to search for subshells -/// \param cursor_pos the position of the cursor -/// \param a the start of the process -/// \param b the end of the process -/// \param tokens the tokens in the process -void parse_util_process_extent(const wchar_t *buff, size_t cursor_pos, const wchar_t **a, - const wchar_t **b, std::vector *tokens); - -/// Find the beginning and end of the job definition under the cursor -/// -/// \param buff the string to search for subshells -/// \param cursor_pos the position of the cursor -/// \param a the start of the searched string -/// \param b the end of the searched string -void parse_util_job_extent(const wchar_t *buff, size_t cursor_pos, const wchar_t **a, - const wchar_t **b); - -/// Find the beginning and end of the token under the cursor and the token before the current token. -/// Any combination of tok_begin, tok_end, prev_begin and prev_end may be null. -/// -/// \param buff the string to search for subshells -/// \param cursor_pos the position of the cursor -/// \param tok_begin the start of the current token -/// \param tok_end the end of the current token -/// \param prev_begin the start o the token before the current token -/// \param prev_end the end of the token before the current token -void parse_util_token_extent(const wchar_t *buff, size_t cursor_pos, const wchar_t **tok_begin, - const wchar_t **tok_end, const wchar_t **prev_begin, - const wchar_t **prev_end); - -/// Get the line number at the specified character offset. -int parse_util_lineno(const wcstring &str, size_t offset); - -/// Calculate the line number of the specified cursor position. -int parse_util_get_line_from_offset(const wcstring &str, size_t pos); - -/// Get the offset of the first character on the specified line. -size_t parse_util_get_offset_from_line(const wcstring &str, int line); - -/// Return the total offset of the buffer for the cursor position nearest to the specified position. -size_t parse_util_get_offset(const wcstring &str, int line, long line_offset); - -/// Return the given string, unescaping wildcard characters but not performing any other character -/// transformation. -wcstring parse_util_unescape_wildcards(const wcstring &str); - -/// Return if the given string contains wildcard characters. -bool parse_util_contains_wildcards(const wcstring &str); - -/// Escape any wildcard characters in the given string. e.g. convert -/// "a*b" to "a\*b". -wcstring parse_util_escape_wildcards(const wcstring &str); - - -/// Calculates information on the parameter at the specified index. -/// -/// \param cmd The command to be analyzed -/// \param pos An index in the string which is inside the parameter -/// \return the type of quote used by the parameter: either ' or " or \0. -wchar_t parse_util_get_quote_type(const wcstring &cmd, size_t pos); - -/// Attempts to escape the string 'cmd' using the given quote type, as determined by the quote -/// character. The quote can be a single quote or double quote, or L'\0' to indicate no quoting (and -/// thus escaping should be with backslashes). Optionally do not escape tildes. -wcstring parse_util_escape_string_with_quote(const wcstring &cmd, wchar_t quote, - bool no_tilde = false); - -/// Given a string, parse it as fish code and then return the indents. The return value has the same -/// size as the string. -std::vector parse_util_compute_indents(const wcstring &src); - -/// Given a string, detect parse errors in it. If allow_incomplete is set, then if the string is -/// incomplete (e.g. an unclosed quote), an error is not returned and the PARSER_TEST_INCOMPLETE bit -/// is set in the return value. If allow_incomplete is not set, then incomplete strings result in an -/// error. -parser_test_error_bits_t parse_util_detect_errors(const wcstring &buff_src, - parse_error_list_t *out_errors = nullptr, - bool allow_incomplete = false); - -parser_test_error_bits_t parse_util_detect_errors_ffi(const ast::ast_t *ast, - const wcstring &buff_src, - parse_error_list_t *out_errors); -/// Like parse_util_detect_errors but accepts an already-parsed ast. -/// The top of the ast is assumed to be a job list. -parser_test_error_bits_t parse_util_detect_errors(const ast::ast_t &ast, const wcstring &buff_src, - parse_error_list_t *out_errors); - -/// Detect errors in the specified string when parsed as an argument list. Returns the text of an -/// error, or none if no error occurred. -maybe_t parse_util_detect_errors_in_argument_list(const wcstring &arg_list_src, - const wcstring &prefix = {}); - -/// Test if this argument contains any errors. Detected errors include syntax errors in command -/// substitutions, improperly escaped characters and improper use of the variable expansion -/// operator. This does NOT currently detect unterminated quotes. - -parser_test_error_bits_t parse_util_detect_errors_in_argument( - const ast::argument_t &arg, const wcstring &arg_src, parse_error_list_t *out_errors = nullptr); - -/// Given a string containing a variable expansion error, append an appropriate error to the errors -/// list. The global_token_pos is the offset of the token in the larger source, and the dollar_pos -/// is the offset of the offending dollar sign within the token. -void parse_util_expand_variable_error(const wcstring &token, size_t global_token_pos, - size_t dollar_pos, parse_error_list_t *out_errors); - -#endif diff --git a/src/parser.h b/src/parser.h deleted file mode 100644 index 1cc609461..000000000 --- a/src/parser.h +++ /dev/null @@ -1,29 +0,0 @@ -// The fish parser. -#ifndef FISH_PARSER_H -#define FISH_PARSER_H - -#include - -#include -#include -#include -#include -#include -#include -#include - -#include "proc.h" - -struct Parser; -using parser_t = Parser; -struct ParserRef; - -#if INCLUDE_RUST_HEADERS -#include "parser.rs.h" -#else -struct EvalRes; -#endif - -using eval_res_t = EvalRes; - -#endif diff --git a/src/parser_keywords.h b/src/parser_keywords.h deleted file mode 100644 index c274645af..000000000 --- a/src/parser_keywords.h +++ /dev/null @@ -1,23 +0,0 @@ -// Functions having to do with parser keywords, like testing if a function is a block command. -#ifndef FISH_PARSER_KEYWORD_H -#define FISH_PARSER_KEYWORD_H - -#include "common.h" - -/// Tests if the specified commands parameters should be interpreted as another command, which will -/// be true if the command is either 'command', 'exec', 'if', 'while', or 'builtin'. This does not -/// handle "else if" which is more complicated. -/// -/// \param cmd The command name to test -/// \return 1 of the command parameter is a command, 0 otherwise -bool parser_keywords_is_subcommand(const wcstring &cmd); - -/// Tests if the specified command is a reserved word, i.e. if it is the name of one of the builtin -/// functions that change the block or command scope, like 'for', 'end' or 'command' or 'exec'. -/// These functions may not be overloaded, so their names are reserved. -/// -/// \param word The command name to test -/// \return 1 of the command parameter is a command, 0 otherwise -bool parser_keywords_is_reserved(const wcstring &word); - -#endif diff --git a/src/path.cpp b/src/path.cpp deleted file mode 100644 index d44ac1d88..000000000 --- a/src/path.cpp +++ /dev/null @@ -1,34 +0,0 @@ -// Directory utilities. This library contains functions for locating configuration directories, for -// testing if a command with a given name can be found in the PATH, and various other path-related -// issues. -#include "config.h" // IWYU pragma: keep - -#include "path.h" - -#include -#include -#include -#include - -#include "common.h" -#include "fallback.h" // IWYU pragma: keep -#include "wcstringutil.h" -#include "wutil.h" // IWYU pragma: keep - -void append_path_component(wcstring &path, const wcstring &component) { - if (path.empty() || component.empty()) { - path.append(component); - } else { - size_t path_len = path.size(); - bool path_slash = path.at(path_len - 1) == L'/'; - bool comp_slash = component.at(0) == L'/'; - if (!path_slash && !comp_slash) { - // Need a slash - path.push_back(L'/'); - } else if (path_slash && comp_slash) { - // Too many slashes. - path.erase(path_len - 1, 1); - } - path.append(component); - } -} diff --git a/src/path.h b/src/path.h deleted file mode 100644 index 781affe44..000000000 --- a/src/path.h +++ /dev/null @@ -1,15 +0,0 @@ -// Directory utilities. This library contains functions for locating configuration directories, for -// testing if a command with a given name can be found in the PATH, and various other path-related -// issues. -#ifndef FISH_PATH_H -#define FISH_PATH_H - -#include - -#include "common.h" -#include "wutil.h" - -/// Appends a path component, with a / if necessary. -void append_path_component(wcstring &path, const wcstring &component); - -#endif diff --git a/src/proc.h b/src/proc.h deleted file mode 100644 index a0c3ca83a..000000000 --- a/src/proc.h +++ /dev/null @@ -1,38 +0,0 @@ -// Prototypes for utilities for keeping track of jobs, processes and subshells, as well as signal -// handling functions for tracking children. These functions do not themselves launch new processes, -// the exec library will call proc to create representations of the running jobs as needed. -#ifndef FISH_PROC_H -#define FISH_PROC_H -#include "config.h" // IWYU pragma: keep - -#include // IWYU pragma: keep -#include // IWYU pragma: keep - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "ast.h" -#include "common.h" -#include "cxx.h" -#include "maybe.h" -#include "parse_tree.h" -#include "parser.h" -#include "redirection.h" - -struct Parser; - -#if INCLUDE_RUST_HEADERS -#include "proc.rs.h" -#else -struct JobRefFfi; -struct JobGroupRefFfi; -struct JobListFFI; -#endif - -#endif diff --git a/src/reader.h b/src/reader.h deleted file mode 100644 index 76f5d13a9..000000000 --- a/src/reader.h +++ /dev/null @@ -1,28 +0,0 @@ -#ifndef FISH_READER_H -#define FISH_READER_H - -#include -#include - -#include -#include -#include -#include - -#include "common.h" -#include "complete.h" -#include "env.h" -#include "highlight.h" -#include "io.h" -#include "maybe.h" -#include "parse_constants.h" -#include "parser.h" -#include "wutil.h" - -#if INCLUDE_RUST_HEADERS -#include "reader.rs.h" -#endif - -#include "editable_line.h" - -#endif diff --git a/src/redirection.h b/src/redirection.h deleted file mode 100644 index 4a7a4baeb..000000000 --- a/src/redirection.h +++ /dev/null @@ -1,32 +0,0 @@ -#ifndef FISH_REDIRECTION_H -#define FISH_REDIRECTION_H - -#if INCLUDE_RUST_HEADERS - -#include "redirection.rs.h" - -#else - -// Hacks to allow us to compile without Rust headers. - -enum class RedirectionMode { - overwrite, - append, - input, - fd, - noclob, -}; -struct Dup2Action; -struct Dup2List; -struct RedirectionSpec; -struct RedirectionSpecListFfi; - -#endif - -using redirection_mode_t = RedirectionMode; -using redirection_spec_t = RedirectionSpec; -using redirection_spec_list_t = RedirectionSpecListFfi; -using dup2_action_t = Dup2Action; -using dup2_list_t = Dup2List; - -#endif diff --git a/src/rustffi.cpp b/src/rustffi.cpp deleted file mode 100644 index d5e4980a6..000000000 --- a/src/rustffi.cpp +++ /dev/null @@ -1,21 +0,0 @@ -#include - -#include "wutil.h" - -extern "C" { -void fishffi$unique_ptr$wcstring$null(std::unique_ptr *ptr) noexcept { - new (ptr) std::unique_ptr(); -} -void fishffi$unique_ptr$wcstring$raw(std::unique_ptr *ptr, wcstring *raw) noexcept { - new (ptr) std::unique_ptr(raw); -} -const wcstring *fishffi$unique_ptr$wcstring$get(const std::unique_ptr &ptr) noexcept { - return ptr.get(); -} -wcstring *fishffi$unique_ptr$wcstring$release(std::unique_ptr &ptr) noexcept { - return ptr.release(); -} -void fishffi$unique_ptr$wcstring$drop(std::unique_ptr *ptr) noexcept { - ptr->~unique_ptr(); -} -} // extern "C" diff --git a/src/screen.h b/src/screen.h deleted file mode 100644 index 32779cb85..000000000 --- a/src/screen.h +++ /dev/null @@ -1,26 +0,0 @@ -#ifndef FISH_SCREEN_H -#define FISH_SCREEN_H -#include "config.h" // IWYU pragma: keep - -struct PageRendering; -struct Pager; -using page_rendering_t = PageRendering; -using pager_t = Pager; - -#if INCLUDE_RUST_HEADERS -#include "screen.rs.h" -#else -struct Line; -struct ScreenData; -struct Screen; -struct PromptLayout; -struct LayoutCache; -#endif - -using line_t = Line; -using screen_data_t = ScreenData; -using screen_t = Screen; -using prompt_layout_t = PromptLayout; -using layout_cache_t = LayoutCache; - -#endif diff --git a/src/signals.h b/src/signals.h deleted file mode 100644 index 60f28b15c..000000000 --- a/src/signals.h +++ /dev/null @@ -1,17 +0,0 @@ -// The library for various signal related issues. -#ifndef FISH_SIGNALH -#define FISH_SIGNALH - -#include -#include - -#if INCLUDE_RUST_HEADERS -#include "signal.rs.h" -#else -struct IoStreams; -struct SigChecker; -#endif - -using sigchecker_t = SigChecker; - -#endif diff --git a/src/termsize.h b/src/termsize.h deleted file mode 100644 index 8ea40a46d..000000000 --- a/src/termsize.h +++ /dev/null @@ -1,13 +0,0 @@ -// Support for exposing the terminal size. - -#include "config.h" // IWYU pragma: keep -#ifndef FISH_TERMSIZE_H -#define FISH_TERMSIZE_H - -#if INCLUDE_RUST_HEADERS -#include "termsize.rs.h" -#else -struct termsize_t; -#endif - -#endif // FISH_TERMSIZE_H diff --git a/src/tokenizer.h b/src/tokenizer.h deleted file mode 100644 index 5ad2ff3de..000000000 --- a/src/tokenizer.h +++ /dev/null @@ -1,52 +0,0 @@ -#ifndef FISH_TOKENIZER_H -#define FISH_TOKENIZER_H - -#include -#include - -#include "common.h" -#include "maybe.h" -#include "parse_constants.h" -#include "redirection.h" - -using tok_flags_t = unsigned int; - -#define TOK_ACCEPT_UNFINISHED 1 -#define TOK_SHOW_COMMENTS 2 -#define TOK_SHOW_BLANK_LINES 4 -#define TOK_CONTINUE_AFTER_ERROR 8 - -#if INCLUDE_RUST_HEADERS - -#include "tokenizer.rs.h" -using token_type_t = TokenType; -using tokenizer_error_t = TokenizerError; -using tok_t = Tok; -using tokenizer_t = Tokenizer; -using pipe_or_redir_t = PipeOrRedir; -using move_word_state_machine_t = MoveWordStateMachine; -using move_word_style_t = MoveWordStyle; - -#else - -// Hacks to allow us to compile without Rust headers. -enum class tokenizer_error_t : uint8_t { - none, - unterminated_quote, - unterminated_subshell, - unterminated_slice, - unterminated_escape, - invalid_redirect, - invalid_pipe, - invalid_pipe_ampersand, - closing_unopened_subshell, - illegal_slice, - closing_unopened_brace, - unterminated_brace, - expected_pclose_found_bclose, - expected_bclose_found_pclose, -}; - -#endif - -#endif diff --git a/src/util.h b/src/util.h deleted file mode 100644 index fcb9996b8..000000000 --- a/src/util.h +++ /dev/null @@ -1,17 +0,0 @@ -#ifndef FISH_UTIL_H -#define FISH_UTIL_H - -#if INCLUDE_RUST_HEADERS - -#include "util.rs.h" - -#else - -// Hacks to allow us to compile without Rust headers. -int wcsfilecmp(const wchar_t *a, const wchar_t *b); -int wcsfilecmp_glob(const wchar_t *a, const wchar_t *b); -long long get_time(); - -#endif - -#endif diff --git a/src/wcstringutil.cpp b/src/wcstringutil.cpp deleted file mode 100644 index 71c56b392..000000000 --- a/src/wcstringutil.cpp +++ /dev/null @@ -1,302 +0,0 @@ -// Helper functions for working with wcstring. -#include "config.h" // IWYU pragma: keep - -#include "wcstringutil.h" - -#include -#include - -#include -#include - -#include "common.h" -#include "fallback.h" // IWYU pragma: keep -#include "flog.h" - -wcstring truncate(const wcstring &input, int max_len, ellipsis_type etype) { - if (input.size() <= static_cast(max_len)) { - return input; - } - - if (etype == ellipsis_type::None) { - return input.substr(0, max_len); - } - if (etype == ellipsis_type::Prettiest) { - const wchar_t *ellipsis_str = get_ellipsis_str(); - return input.substr(0, max_len - std::wcslen(ellipsis_str)).append(ellipsis_str); - } - wcstring output = input.substr(0, max_len - 1); - output.push_back(get_ellipsis_char()); - return output; -} - -wcstring trim(wcstring input) { return trim(std::move(input), L"\t\v \r\n"); } - -wcstring trim(wcstring input, const wchar_t *any_of) { - wcstring result = std::move(input); - size_t suffix = result.find_last_not_of(any_of); - if (suffix == wcstring::npos) { - return wcstring{}; - } - result.erase(suffix + 1); - - auto prefix = result.find_first_not_of(any_of); - assert(prefix != wcstring::npos && "Should have one non-trimmed character"); - result.erase(0, prefix); - return result; -} - -wcstring wcstolower(wcstring input) { - wcstring result = std::move(input); - std::transform(result.begin(), result.end(), result.begin(), towlower); - return result; -} - -size_t count_preceding_backslashes(const wcstring &text, size_t idx) { - assert(idx <= text.size() && "Out of bounds"); - size_t backslashes = 0; - while (backslashes < idx && text.at(idx - backslashes - 1) == L'\\') { - backslashes++; - } - return backslashes; -} - -bool string_prefixes_string(const wchar_t *proposed_prefix, const wcstring &value) { - return string_prefixes_string(proposed_prefix, value.c_str()); -} - -bool string_prefixes_string(const wcstring &proposed_prefix, const wcstring &value) { - size_t prefix_size = proposed_prefix.size(); - return prefix_size <= value.size() && value.compare(0, prefix_size, proposed_prefix) == 0; -} - -bool string_prefixes_string(const wchar_t *proposed_prefix, const wchar_t *value) { - for (size_t idx = 0; proposed_prefix[idx] != L'\0'; idx++) { - // Note if the prefix is longer than value, then we will compare a nonzero prefix character - // against a zero value character, and so we'll return false; - if (proposed_prefix[idx] != value[idx]) return false; - } - // We must have that proposed_prefix[idx] == L'\0', so we have a prefix match. - return true; -} - -bool string_prefixes_string(const char *proposed_prefix, const std::string &value) { - return string_prefixes_string(proposed_prefix, value.c_str()); -} - -bool string_prefixes_string(const char *proposed_prefix, const char *value) { - for (size_t idx = 0; proposed_prefix[idx] != L'\0'; idx++) { - if (proposed_prefix[idx] != value[idx]) return false; - } - return true; -} - -bool string_prefixes_string_case_insensitive(const wcstring &proposed_prefix, - const wcstring &value) { - size_t prefix_size = proposed_prefix.size(); - return prefix_size <= value.size() && - wcsncasecmp(proposed_prefix.c_str(), value.c_str(), prefix_size) == 0; -} - -bool string_suffixes_string(const wcstring &proposed_suffix, const wcstring &value) { - size_t suffix_size = proposed_suffix.size(); - return suffix_size <= value.size() && - value.compare(value.size() - suffix_size, suffix_size, proposed_suffix) == 0; -} - -bool string_suffixes_string(const wchar_t *proposed_suffix, const wcstring &value) { - size_t suffix_size = std::wcslen(proposed_suffix); - return suffix_size <= value.size() && - value.compare(value.size() - suffix_size, suffix_size, proposed_suffix) == 0; -} - -bool string_suffixes_string_case_insensitive(const wcstring &proposed_suffix, - const wcstring &value) { - size_t suffix_size = proposed_suffix.size(); - return suffix_size <= value.size() && wcsncasecmp(value.c_str() + (value.size() - suffix_size), - proposed_suffix.c_str(), suffix_size) == 0; -} - -/// Returns true if needle, represented as a subsequence, is contained within haystack. -/// Note subsequence is not substring: "foo" is a subsequence of "follow" for example. -bool subsequence_in_string(const wcstring &needle, const wcstring &haystack) { - // Impossible if needle is larger than haystack. - if (needle.size() > haystack.size()) { - return false; - } - - // Empty strings are considered to be subsequences of everything. - if (needle.empty()) { - return true; - } - - auto ni = needle.begin(); - for (auto hi = haystack.begin(); ni != needle.end() && hi != haystack.end(); ++hi) { - if (*ni == *hi) { - ++ni; - } - } - // We succeeded if we exhausted our sequence. - assert(ni <= needle.end()); - return ni == needle.end(); -} - -// static -maybe_t string_fuzzy_match_t::try_create(const wcstring &string, - const wcstring &match_against, - bool anchor_start) { - // Helper to lazily compute if case insensitive matches should use icase or smartcase. - // Use icase if the input contains any uppercase characters, smartcase otherwise. - auto get_case_fold = [&] { - for (wchar_t c : string) { - if (towlower(c) != static_cast(c)) return case_fold_t::icase; - } - return case_fold_t::smartcase; - }; - - // A string cannot fuzzy match against a shorter string. - if (string.size() > match_against.size()) { - return none(); - } - - // exact samecase - if (string == match_against) { - return string_fuzzy_match_t{contain_type_t::exact, case_fold_t::samecase}; - } - - // prefix samecase - if (string_prefixes_string(string, match_against)) { - return string_fuzzy_match_t{contain_type_t::prefix, case_fold_t::samecase}; - } - - // exact icase - if (wcscasecmp(string.c_str(), match_against.c_str()) == 0) { - return string_fuzzy_match_t{contain_type_t::exact, get_case_fold()}; - } - - // prefix icase - if (string_prefixes_string_case_insensitive(string, match_against)) { - return string_fuzzy_match_t{contain_type_t::prefix, get_case_fold()}; - } - - // If anchor_start is set, this is as far as we go. - if (anchor_start) { - return none(); - } - - // substr samecase - if (match_against.find(string) != wcstring::npos) { - return string_fuzzy_match_t{contain_type_t::substr, case_fold_t::samecase}; - } - - // substr icase - if (ifind(match_against, string, true /* fuzzy */) != wcstring::npos) { - return string_fuzzy_match_t{contain_type_t::substr, get_case_fold()}; - } - - // subseq samecase - if (subsequence_in_string(string, match_against)) { - return string_fuzzy_match_t{contain_type_t::subseq, case_fold_t::samecase}; - } - - // We do not currently test subseq icase. - return none(); -} - -uint32_t string_fuzzy_match_t::rank() const { - // Combine our type and our case fold into a single number, such that better matches are - // smaller. Treat 'exact' types the same as 'prefix' types; this is because we do not - // prefer exact matches to prefix matches when presenting completions to the user. - // Treat smartcase the same as samecase; see #3978. - auto effective_type = (type == contain_type_t::exact ? contain_type_t::prefix : type); - auto effective_case = (case_fold == case_fold_t::smartcase ? case_fold_t::samecase : case_fold); - - // Type dominates fold. - return static_cast(effective_type) * 8 + static_cast(effective_case); -} - -template -static size_t ifind_impl(const T &haystack, const T &needle) { - using char_t = typename T::value_type; - std::locale locale; - - auto ieq = [&locale](char_t c1, char_t c2) { - if (c1 == c2 || std::toupper(c1, locale) == std::toupper(c2, locale)) return true; - - // In fuzzy matching treat treat `-` and `_` as equal (#3584). - if (Fuzzy) { - if ((c1 == '-' || c1 == '_') && (c2 == '-' || c2 == '_')) return true; - } - return false; - }; - - auto result = std::search(haystack.begin(), haystack.end(), needle.begin(), needle.end(), ieq); - if (result != haystack.end()) { - return result - haystack.begin(); - } - return T::npos; -} - -size_t ifind(const wcstring &haystack, const wcstring &needle, bool fuzzy) { - return fuzzy ? ifind_impl(haystack, needle) : ifind_impl(haystack, needle); -} - -size_t ifind(const std::string &haystack, const std::string &needle, bool fuzzy) { - return fuzzy ? ifind_impl(haystack, needle) : ifind_impl(haystack, needle); -} - -std::vector split_string(const wcstring &val, wchar_t sep) { - std::vector out; - size_t pos = 0, end = val.size(); - while (pos <= end) { - size_t next_pos = val.find(sep, pos); - if (next_pos == wcstring::npos) { - next_pos = end; - } - out.emplace_back(val, pos, next_pos - pos); - pos = next_pos + 1; // skip the separator, or skip past the end - } - return out; -} - -static wcstring join_strings_impl(const std::vector &vals, const wchar_t *sep, - size_t seplen) { - if (vals.empty()) return wcstring{}; - - // Reserve the size we will need. - // count-1 separators, plus the length of all strings. - size_t size = (vals.size() - 1) * seplen; - for (const wcstring &s : vals) { - size += s.size(); - } - - // Construct the string. - wcstring result; - result.reserve(size); - bool first = true; - for (const wcstring &s : vals) { - if (!first) { - result.append(sep, seplen); - } - result.append(s); - first = false; - } - return result; -} - -wcstring join_strings(const std::vector &vals, wchar_t c) { - return join_strings_impl(vals, &c, 1); -} - -wcstring join_strings(const std::vector &vals, const wchar_t *sep) { - return join_strings_impl(vals, sep, wcslen(sep)); -} - -void wcs2string_bad_char(wchar_t wc) { - FLOGF(char_encoding, L"Wide character U+%4X has no narrow representation", wc); -} - -int fish_wcwidth_visible(wchar_t widechar) { - if (widechar == L'\b') return -1; - return std::max(0, fish_wcwidth(widechar)); -} diff --git a/src/wcstringutil.h b/src/wcstringutil.h deleted file mode 100644 index 46c03abf3..000000000 --- a/src/wcstringutil.h +++ /dev/null @@ -1,307 +0,0 @@ -// Helper functions for working with wcstring. -#ifndef FISH_WCSTRINGUTIL_H -#define FISH_WCSTRINGUTIL_H - -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#include "common.h" -#include "expand.h" -#include "maybe.h" - -/// Test if a string prefixes another. Returns true if a is a prefix of b. -bool string_prefixes_string(const wcstring &proposed_prefix, const wcstring &value); -bool string_prefixes_string(const wchar_t *proposed_prefix, const wcstring &value); -bool string_prefixes_string(const wchar_t *proposed_prefix, const wchar_t *value); -bool string_prefixes_string(const char *proposed_prefix, const std::string &value); -bool string_prefixes_string(const char *proposed_prefix, const char *value); - -/// Test if a string is a suffix of another. -bool string_suffixes_string(const wcstring &proposed_suffix, const wcstring &value); -bool string_suffixes_string(const wchar_t *proposed_suffix, const wcstring &value); -bool string_suffixes_string_case_insensitive(const wcstring &proposed_suffix, - const wcstring &value); - -/// Test if a string prefixes another without regard to case. Returns true if a is a prefix of b. -bool string_prefixes_string_case_insensitive(const wcstring &proposed_prefix, - const wcstring &value); - -/// Test if a string matches a subsequence of another. -bool subsequence_in_string(const wcstring &needle, const wcstring &haystack); - -/// Case-insensitive string search, modeled after std::string::find(). -/// \param fuzzy indicates this is being used for fuzzy matching and case insensitivity is -/// expanded to include symbolic characters (#3584). -/// \return the offset of the first case-insensitive matching instance of `needle` within -/// `haystack`, or `string::npos()` if no results were found. -size_t ifind(const wcstring &haystack, const wcstring &needle, bool fuzzy = false); -size_t ifind(const std::string &haystack, const std::string &needle, bool fuzzy = false); - -/// A lightweight value-type describing how closely a string fuzzy-matches another string. -struct string_fuzzy_match_t { - // The ways one string can contain another. - enum class contain_type_t : uint8_t { - exact, // exact match: foobar matches foo - prefix, // prefix match: foo matches foobar - substr, // substring match: ooba matches foobar - subseq, // subsequence match: fbr matches foobar - }; - contain_type_t type; - - // The case-folding required for the match. - enum class case_fold_t : uint8_t { - samecase, // exact match: foobar matches foobar - smartcase, // case insensitive match with lowercase input. foobar matches FoBar. - icase, // case insensitive: FoBaR matches foobAr - }; - case_fold_t case_fold; - - // Constructor. - constexpr string_fuzzy_match_t(contain_type_t type, case_fold_t case_fold) - : type(type), case_fold(case_fold) {} - - // Helper to return an exact match. - static constexpr string_fuzzy_match_t exact_match() { - return string_fuzzy_match_t(contain_type_t::exact, case_fold_t::samecase); - } - - /// \return whether this is a samecase exact match. - bool is_samecase_exact() const { - return type == contain_type_t::exact && case_fold == case_fold_t::samecase; - } - - /// \return if we are exact or prefix match. - bool is_exact_or_prefix() const { - switch (type) { - case contain_type_t::exact: - case contain_type_t::prefix: - return true; - case contain_type_t::substr: - case contain_type_t::subseq: - return false; - } - DIE("Unreachable"); - return false; - } - - // \return if our match requires a full replacement, i.e. is not a strict extension of our - // existing string. This is false only if our case matches, and our type is prefix or exact. - bool requires_full_replacement() const { - if (case_fold != case_fold_t::samecase) return true; - switch (type) { - case contain_type_t::exact: - case contain_type_t::prefix: - return false; - case contain_type_t::substr: - case contain_type_t::subseq: - return true; - } - DIE("Unreachable"); - return false; - } - - /// Try creating a fuzzy match for \p string against \p match_against. - /// \p string is something like "foo" and \p match_against is like "FooBar". - /// If \p anchor_start is set, then only exact and prefix matches are permitted. - static maybe_t try_create(const wcstring &string, - const wcstring &match_against, - bool anchor_start); - - /// \return a rank for filtering matches. - /// Earlier (smaller) ranks are better matches. - uint32_t rank() const; -}; - -/// Cover over string_fuzzy_match_t::try_create(). -inline maybe_t string_fuzzy_match_string(const wcstring &string, - const wcstring &match_against, - bool anchor_start = false) { - return string_fuzzy_match_t::try_create(string, match_against, anchor_start); -} - -/// Split a string by a separator character. -std::vector split_string(const wcstring &val, wchar_t sep); - -/// Join a list of strings by a separator character or string. -wcstring join_strings(const std::vector &vals, wchar_t sep); -wcstring join_strings(const std::vector &vals, const wchar_t *sep); - -inline wcstring to_string(long x) { - wchar_t buff[64]; - format_long_safe(buff, x); - return wcstring(buff); -} - -inline wcstring to_string(long long x) { - wchar_t buff[64]; - format_llong_safe(buff, x); - return wcstring(buff); -} - -inline wcstring to_string(unsigned long long x) { - wchar_t buff[64]; - format_ullong_safe(buff, x); - return wcstring(buff); -} - -inline wcstring to_string(int x) { return to_string(static_cast(x)); } - -inline wcstring to_string(size_t x) { return to_string(static_cast(x)); } - -inline bool bool_from_string(const std::string &x) { - if (x.empty()) return false; - switch (x.front()) { - case 'Y': - case 'T': - case 'y': - case 't': - case '1': - return true; - default: - return false; - } -} - -inline bool bool_from_string(const wcstring &x) { - return !x.empty() && std::wcschr(L"YTyt1", x.at(0)); -} - -/// Given iterators into a string (forward or reverse), splits the haystack iterators -/// about the needle sequence, up to max times. Inserts splits into the output array. -/// If the iterators are forward, this does the normal thing. -/// If the iterators are backward, this returns reversed strings, in reversed order! -/// If the needle is empty, split on individual elements (characters). -/// Max output entries will be max + 1 (after max splits) -template -void split_about(ITER haystack_start, ITER haystack_end, ITER needle_start, ITER needle_end, - std::vector *output, long max = LONG_MAX, bool no_empty = false) { - long remaining = max; - ITER haystack_cursor = haystack_start; - while (remaining > 0 && haystack_cursor != haystack_end) { - ITER split_point; - if (needle_start == needle_end) { // empty needle, we split on individual elements - split_point = haystack_cursor + 1; - } else { - split_point = std::search(haystack_cursor, haystack_end, needle_start, needle_end); - } - if (split_point == haystack_end) { // not found - break; - } - if (!no_empty || haystack_cursor != split_point) { - output->emplace_back(haystack_cursor, split_point); - } - remaining--; - // Need to skip over the needle for the next search note that the needle may be empty. - haystack_cursor = split_point + std::distance(needle_start, needle_end); - } - // Trailing component, possibly empty. - if (!no_empty || haystack_cursor != haystack_end) { - output->emplace_back(haystack_cursor, haystack_end); - } -} - -enum class ellipsis_type { - None, - // Prefer niceness over minimalness - Prettiest, - // Make every character count ($ instead of ...) - Shortest, -}; - -wcstring truncate(const wcstring &input, int max_len, - ellipsis_type etype = ellipsis_type::Prettiest); -wcstring trim(wcstring input); -wcstring trim(wcstring input, const wchar_t *any_of); - -/// Converts a string to lowercase. -wcstring wcstolower(wcstring input); - -/// \return the number of escaping backslashes before a character. -/// \p idx may be "one past the end." -size_t count_preceding_backslashes(const wcstring &text, size_t idx); - -// Out-of-line helper for wcs2string_callback. -void wcs2string_bad_char(wchar_t); - -/// Implementation of wcs2string that accepts a callback. -/// This invokes \p func with (const char*, size_t) pairs. -/// If \p func returns false, it stops; otherwise it continues. -/// \return false if the callback returned false, otherwise true. -template -bool wcs2string_callback(const wchar_t *input, size_t len, const Func &func) { - mbstate_t state = {}; - char converted[MB_LEN_MAX]; - - for (size_t i = 0; i < len; i++) { - wchar_t wc = input[i]; - // TODO: this doesn't seem sound. - if (wc == INTERNAL_SEPARATOR) { - // do nothing - } else if (wc >= ENCODE_DIRECT_BASE && wc < ENCODE_DIRECT_BASE + 256) { - converted[0] = wc - ENCODE_DIRECT_BASE; - if (!func(converted, 1)) return false; - } else if (MB_CUR_MAX == 1) { // single-byte locale (C/POSIX/ISO-8859) - // If `wc` contains a wide character we emit a question-mark. - if (wc & ~0xFF) { - wc = '?'; - } - converted[0] = wc; - if (!func(converted, 1)) return false; - } else { - std::memset(converted, 0, sizeof converted); - size_t len = std::wcrtomb(converted, wc, &state); - if (len == static_cast(-1)) { - wcs2string_bad_char(wc); - std::memset(&state, 0, sizeof(state)); - } else { - if (!func(converted, len)) return false; - } - } - } - return true; -} - -/// Support for iterating over a newline-separated string. -template -class line_iterator_t { - // Storage for each line. - Collection storage; - - // The collection we're iterating. Note we hold this by reference. - const Collection &coll; - - // The current location in the iteration. - typename Collection::const_iterator current; - - public: - /// Construct from a collection (presumably std::string or std::wcstring). - line_iterator_t(const Collection &coll) : coll(coll), current(coll.cbegin()) {} - - /// Access the storage in which the last line was stored. - const Collection &line() const { return storage; } - - /// Advances to the next line. \return true on success, false if we have exhausted the string. - bool next() { - if (current == coll.end()) return false; - auto newline_or_end = std::find(current, coll.cend(), '\n'); - storage.assign(current, newline_or_end); - current = newline_or_end; - - // Skip the newline. - if (current != coll.cend()) ++current; - return true; - } -}; - -/// Like fish_wcwidth, but returns 0 for characters with no real width instead of -1. -int fish_wcwidth_visible(wchar_t widechar); - -#endif diff --git a/src/wgetopt.cpp b/src/wgetopt.cpp deleted file mode 100644 index c37bf57cd..000000000 --- a/src/wgetopt.cpp +++ /dev/null @@ -1,434 +0,0 @@ -// A version of the getopt library for use with wide character strings. -// -// This is simply the gnu getopt library, but converted for use with wchar_t instead of char. This -// is not usually useful since the argv array is always defined to be of type char**, but in fish, -// all internal commands use wide characters and hence this library is useful. -// -// If you want to use this version of getopt in your program, download the fish sourcecode, -// available at the fish homepage. Extract the sourcode, copy -// wgetopt.c and wgetopt.h into your program directory, include wgetopt.h in your program, and use -// all the regular getopt functions, prefixing every function, global variable and structure with a -// 'w', and use only wide character strings. There are no other functional changes in this version -// of getopt besides using wide character strings. -// -// For examples of how to use wgetopt, see the fish builtin functions, many of which are defined in -// builtin.c. - -// Getopt for GNU. -// -// NOTE: getopt is now part of the C library, so if you don't know what "Keep this file name-space -// clean" means, talk to roland@gnu.ai.mit.edu before changing it! -// -// Copyright (C) 1987, 88, 89, 90, 91, 92, 93, 94 -// Free Software Foundation, Inc. -// -// This file is part of the GNU C Library. Its master source is NOT part of the C library, however. -// The master source lives in /gd/gnu/lib. -// -// The GNU C Library is free software; you can redistribute it and/or modify it under the terms of -// the GNU Library General Public License as published by the Free Software Foundation; either -// version 2 of the License, or (at your option) any later version. -// -// The GNU C Library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; -// without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See -// the GNU Library General Public License for more details. -// -// You should have received a copy of the GNU Library General Public License along with the GNU C -// Library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 675 Mass -// Ave, Cambridge, MA 02139, USA. -#include "config.h" // IWYU pragma: keep - -#include -#include - -#include - -// This version of `getopt' appears to the caller like standard Unix `getopt' but it behaves -// differently for the user, since it allows the user to intersperse the options with the other -// arguments. -// -// As `getopt' works, it permutes the elements of ARGV so that, when it is done, all the options -// precede everything else. Thus all application programs are extended to handle flexible argument -// order. -// -// GNU application programs can use a third alternative mode in which they can distinguish the -// relative order of options and other arguments. -#include "common.h" -#include "fallback.h" // IWYU pragma: keep -#include "wgetopt.h" -#include "wutil.h" // IWYU pragma: keep - -// Exchange two adjacent subsequences of ARGV. One subsequence is elements -// [first_nonopt,last_nonopt) which contains all the non-options that have been skipped so far. The -// other is elements [last_nonopt,woptind), which contains all the options processed since those -// non-options were skipped. -// -// `first_nonopt' and `last_nonopt' are relocated so that they describe the new indices of the -// non-options in ARGV after they are moved. -void wgetopter_t::exchange(string_array_t argv) { - int bottom = first_nonopt; - int middle = last_nonopt; - int top = woptind; - const wchar_t *tem{}; - - // Exchange the shorter segment with the far end of the longer segment. That puts the shorter - // segment into the right place. It leaves the longer segment in the right place overall, but it - // consists of two parts that need to be swapped next. - - while (top > middle && middle > bottom) { - if (top - middle > middle - bottom) { - // Bottom segment is the short one. - int len = middle - bottom; - int i; - - // Swap it with the top part of the top segment. - for (i = 0; i < len; i++) { - tem = argv[bottom + i]; - argv[bottom + i] = argv[top - (middle - bottom) + i]; - argv[top - (middle - bottom) + i] = tem; - } - // Exclude the moved bottom segment from further swapping. - top -= len; - } else { - // Top segment is the short one. - int len = top - middle; - int i; - - // Swap it with the bottom part of the bottom segment. - for (i = 0; i < len; i++) { - tem = argv[bottom + i]; - argv[bottom + i] = argv[middle + i]; - argv[middle + i] = tem; - } - // Exclude the moved top segment from further swapping. - bottom += len; - } - } - - // Update records for the slots the non-options now occupy. - first_nonopt += (woptind - last_nonopt); - last_nonopt = woptind; -} - -// Initialize the internal data when the first call is made. -void wgetopter_t::_wgetopt_initialize(const wchar_t *optstring) { - // Start processing options with ARGV-element 1 (since ARGV-element 0 is the program name); the - // sequence of previously skipped non-option ARGV-elements is empty. - first_nonopt = last_nonopt = woptind = 1; - nextchar = nullptr; - - // Determine how to handle the ordering of options and nonoptions. - if (optstring[0] == '-') { - ordering = RETURN_IN_ORDER; - ++optstring; - } else if (optstring[0] == '+') { - ordering = REQUIRE_ORDER; - ++optstring; - } else { - ordering = PERMUTE; - } - - if (optstring[0] == ':') { - missing_arg_return_colon = true; - ++optstring; - } - - shortopts = optstring; - initialized = true; -} - -// Advance to the next ARGV-element. -int wgetopter_t::_advance_to_next_argv( //!OCLINT(high cyclomatic complexity) - int argc, string_array_t argv, const struct woption *longopts) { - if (ordering == PERMUTE) { - // If we have just processed some options following some non-options, exchange them so - // that the options come first. - if (first_nonopt != last_nonopt && last_nonopt != woptind) { - exchange(argv); - } else if (last_nonopt != woptind) { - first_nonopt = woptind; - } - - // Skip any additional non-options and extend the range of non-options previously - // skipped. - while (woptind < argc && (argv[woptind][0] != '-' || argv[woptind][1] == '\0')) { - woptind++; - } - last_nonopt = woptind; - } - - // The special ARGV-element `--' means premature end of options. Skip it like a null option, - // then exchange with previous non-options as if it were an option, then skip everything - // else like a non-option. - if (woptind != argc && !std::wcscmp(argv[woptind], L"--")) { - woptind++; - - if (first_nonopt != last_nonopt && last_nonopt != woptind) { - exchange(argv); - } else if (first_nonopt == last_nonopt) { - first_nonopt = woptind; - } - last_nonopt = argc; - woptind = argc; - } - - // If we have done all the ARGV-elements, stop the scan and back over any non-options that - // we skipped and permuted. - - if (woptind == argc) { - // Set the next-arg-index to point at the non-options that we previously skipped, so the - // caller will digest them. - if (first_nonopt != last_nonopt) woptind = first_nonopt; - return EOF; - } - - // If we have come to a non-option and did not permute it, either stop the scan or describe - // it to the caller and pass it by. - if ((argv[woptind][0] != '-' || argv[woptind][1] == '\0')) { - if (ordering == REQUIRE_ORDER) return EOF; - woptarg = argv[woptind++]; - return 1; - } - - // We have found another option-ARGV-element. Skip the initial punctuation. - nextchar = (argv[woptind] + 1 + (longopts != nullptr && argv[woptind][1] == '-')); - return 0; -} - -// Check for a matching short opt. -int wgetopter_t::_handle_short_opt(int argc, string_array_t argv) { - // Look at and handle the next short option-character. - wchar_t c = *nextchar++; - const wchar_t *temp = std::wcschr(shortopts, c); - - // Increment `woptind' when we start to process its last character. - if (*nextchar == '\0') ++woptind; - - if (temp == nullptr || c == ':') { - woptopt = c; - - if (*nextchar != '\0') woptind++; - return '?'; - } - - if (temp[1] != ':') { - return c; - } - - if (temp[2] == ':') { - // This is an option that accepts an argument optionally. - if (*nextchar != '\0') { - woptarg = nextchar; - woptind++; - } else { - woptarg = nullptr; - } - nextchar = nullptr; - } else { - // This is an option that requires an argument. - if (*nextchar != '\0') { - woptarg = nextchar; - // If we end this ARGV-element by taking the rest as an arg, we must advance to - // the next element now. - woptind++; - } else if (woptind == argc) { - woptopt = c; - c = missing_arg_return_colon ? ':' : '?'; - } else { - // We already incremented `woptind' once; increment it again when taking next - // ARGV-elt as argument. - woptarg = argv[woptind++]; - } - nextchar = nullptr; - } - - return c; -} - -void wgetopter_t::_update_long_opt(int argc, string_array_t argv, const struct woption *pfound, - size_t nameend, int *longind, int option_index, int *retval) { - woptind++; - assert(nextchar[nameend] == '\0' || nextchar[nameend] == '='); - if (nextchar[nameend] == '=') { - if (pfound->has_arg != no_argument) - woptarg = &nextchar[nameend + 1]; - else { - nextchar += std::wcslen(nextchar); - *retval = '?'; - return; - } - } else if (pfound->has_arg == required_argument) { - if (woptind < argc) - woptarg = argv[woptind++]; - else { - nextchar += std::wcslen(nextchar); - *retval = missing_arg_return_colon ? ':' : '?'; - return; - } - } - - nextchar += std::wcslen(nextchar); - if (longind != nullptr) *longind = option_index; - *retval = pfound->val; -} - -// Find a matching long opt. -const struct woption *wgetopter_t::_find_matching_long_opt(const struct woption *longopts, - size_t nameend, int *exact, int *ambig, - int *indfound) const { - const struct woption *pfound = nullptr; - int option_index = 0; - - // Test all long options for either exact match or abbreviated matches. - for (const struct woption *p = longopts; p->name; p++, option_index++) { - if (!std::wcsncmp(p->name, nextchar, nameend)) { - if (nameend == wcslen(p->name)) { - // Exact match found. - pfound = p; - *indfound = option_index; - *exact = 1; - break; - } else if (pfound == nullptr) { - // First nonexact match found. - pfound = p; - *indfound = option_index; - } else - // Second or later nonexact match found. - *ambig = 1; - } - } - - return pfound; -} - -// Check for a matching long opt. -bool wgetopter_t::_handle_long_opt(int argc, string_array_t argv, const struct woption *longopts, - int *longind, int long_only, int *retval) { - int exact = 0; - int ambig = 0; - int indfound = 0; - - size_t nameend = 0; - while (nextchar[nameend] && nextchar[nameend] != '=') { - nameend++; - } - - const struct woption *pfound = - _find_matching_long_opt(longopts, nameend, &exact, &ambig, &indfound); - - if (ambig && !exact) { - nextchar += std::wcslen(nextchar); - woptind++; - *retval = '?'; - return true; - } - - if (pfound) { - _update_long_opt(argc, argv, pfound, nameend, longind, indfound, retval); - return true; - } - - // Can't find it as a long option. If this is not getopt_long_only, or the option starts - // with '--' or is not a valid short option, then it's an error. Otherwise interpret it as a - // short option. - if (!long_only || argv[woptind][1] == '-' || std::wcschr(shortopts, *nextchar) == nullptr) { - nextchar = const_cast(L""); - woptind++; - *retval = '?'; - return true; - } - - return false; -} - -// Scan elements of ARGV (whose length is ARGC) for option characters given in OPTSTRING. -// -// If an element of ARGV starts with '-', and is not exactly "-" or "--", then it is an option -// element. The characters of this element (aside from the initial '-') are option characters. If -// `getopt' is called repeatedly, it returns successively each of the option characters from each of -// the option elements. -// -// If `getopt' finds another option character, it returns that character, updating `woptind' and -// `nextchar' so that the next call to `getopt' can resume the scan with the following option -// character or ARGV-element. -// -// If there are no more option characters, `getopt' returns `EOF'. Then `woptind' is the index in -// ARGV of the first ARGV-element that is not an option. (The ARGV-elements have been permuted so -// that those that are not options now come last.) -// -// OPTSTRING is a string containing the legitimate option characters. If an option character is seen -// that is not listed in OPTSTRING, return '?'. -// -// If a char in OPTSTRING is followed by a colon, that means it wants an arg, so the following text -// in the same ARGV-element, or the text of the following ARGV-element, is returned in `optarg'. -// Two colons mean an option that wants an optional arg; if there is text in the current -// ARGV-element, it is returned in `w.woptarg', otherwise `w.woptarg' is set to zero. -// -// If OPTSTRING starts with `-' or `+', it requests different methods of handling the non-option -// ARGV-elements. See the comments about RETURN_IN_ORDER and REQUIRE_ORDER, above. -// -// Long-named options begin with `--' instead of `-'. Their names may be abbreviated as long as the -// abbreviation is unique or is an exact match for some defined option. If they have an argument, -// it follows the option name in the same ARGV-element, separated from the option name by a `=', or -// else the in next ARGV-element. When `getopt' finds a long-named option, it returns 0 if that -// option's `flag' field is nonzero, the value of the option's `val' field if the `flag' field is -// zero. -// -// LONGOPTS is a vector of `struct option' terminated by an element containing a name which is zero. -// -// LONGIND returns the index in LONGOPT of the long-named option found. It is only valid when a -// long-named option has been found by the most recent call. -// -// If LONG_ONLY is nonzero, '-' as well as '--' can introduce long-named options. -int wgetopter_t::_wgetopt_internal(int argc, string_array_t argv, const wchar_t *optstring, - const struct woption *longopts, int *longind, int long_only) { - if (!initialized) _wgetopt_initialize(optstring); - woptarg = nullptr; - - if (nextchar == nullptr || *nextchar == '\0') { - int retval = _advance_to_next_argv(argc, argv, longopts); - if (retval != 0) return retval; - } - - // Decode the current option-ARGV-element. - - // Check whether the ARGV-element is a long option. - // - // If long_only and the ARGV-element has the form "-f", where f is a valid short option, don't - // consider it an abbreviated form of a long option that starts with f. Otherwise there would - // be no way to give the -f short option. - // - // On the other hand, if there's a long option "fubar" and the ARGV-element is "-fu", do - // consider that an abbreviation of the long option, just like "--fu", and not "-f" with arg - // "u". - // - // This distinction seems to be the most useful approach. - if (longopts && woptind < argc) { - const wchar_t *arg = argv[woptind]; - assert(arg && "Null arg"); - bool try_long = false; - if (arg[0] == '-' && arg[1] == '-') { - // Like --foo - try_long = true; - } else if (long_only && wcslen(arg) >= 3) { - // Like -fu - try_long = true; - } else if (!std::wcschr(shortopts, arg[1])) { - // Like -f, but f is not a short arg. - try_long = true; - } - if (try_long) { - int retval = 0; - if (_handle_long_opt(argc, argv, longopts, longind, long_only, &retval)) { - return retval; - } - } - } - return _handle_short_opt(argc, argv); -} - -int wgetopter_t::wgetopt_long(int argc, string_array_t argv, const wchar_t *options, - const struct woption *long_options, int *opt_index) { - assert(woptind <= argc && "woptind is out of range"); - return _wgetopt_internal(argc, argv, options, long_options, opt_index, 0); -} diff --git a/src/wgetopt.h b/src/wgetopt.h deleted file mode 100644 index 6c364f4fa..000000000 --- a/src/wgetopt.h +++ /dev/null @@ -1,169 +0,0 @@ -// A version of the getopt library for use with wide character strings. -// -// This is simply the gnu getopt library, but converted for use with wchar_t instead of char. This -// is not usually useful since the argv array is always defined to be of type char**, but in fish, -// all internal commands use wide characters and hence this library is useful. -// -// If you want to use this version of getopt in your program, -// 1. Download the fish sourcecode, available at https://fishshell.com -// 2. Extract the sourcode -// 3. Copy wgetopt.cpp and wgetopt.h into your program directory, -// 4. #include wgetopt.h in your program -// 5. Make use of all the regular getopt functions, prefixing every function, global variable -// and d structure with a 'w', and use only wide character strings. -// There are no other functional changes in this version of getopt besides using wide character -// strings. -// -// For examples of how to use wgetopt, see the fish builtin functions, which are defined in -// src/builtin_*.cpp - -/* Declarations for getopt. - Copyright (C) 1989, 90, 91, 92, 93, 94 Free Software Foundation, Inc. - -This file is part of the GNU C Library. Its master source is NOT part of -the C library, however. The master source lives in /gd/gnu/lib. - -The GNU C Library is free software; you can redistribute it and/or -modify it under the terms of the GNU Library General Public License as -published by the Free Software Foundation; either version 2 of the -License, or (at your option) any later version. - -The GNU C Library is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -Library General Public License for more details. - -You should have received a copy of the GNU Library General Public -License along with the GNU C Library; see the file COPYING.LIB. If -not, write to the Free Software Foundation, Inc., 675 Mass Ave, -Cambridge, MA 02139, USA. */ - -#ifndef FISH_WGETOPT_H -#define FISH_WGETOPT_H - -/// Instanced getopt() wrapper. -class wgetopter_t { - public: - /// Note wgetopter expects an mutable array of const strings. It modifies the order of the - /// strings, but not their contents. - using string_array_t = const wchar_t **; - - // For communication from `getopt' to the caller. When `getopt' finds an option that takes an - // argument, the argument value is returned here. Also, when `ordering' is RETURN_IN_ORDER, each - // non-option ARGV-element is returned here. - const wchar_t *woptarg = nullptr; - - const wchar_t *shortopts = nullptr; - - // The next char to be scanned in the option-element in which the last option character we - // returned was found. This allows us to pick up the scan where we left off. - // - // If this is zero, or a null string, it means resume the scan by advancing to the next - // ARGV-element. - const wchar_t *nextchar = nullptr; - - // Index in ARGV of the next element to be scanned. This is used for communication to and from - // the caller and for communication between successive calls to `getopt'. - // - // On entry to `getopt', zero means this is the first call; initialize. - // - // When `getopt' returns EOF, this is the index of the first of the non-option elements that the - // caller should itself scan. - // - // Otherwise, `woptind' communicates from one call to the next how much of ARGV has been scanned - // so far. - - // XXX 1003.2 says this must be 1 before any call. - int woptind = 0; - - // Set to an option character which was unrecognized. This must be initialized on some systems - // to avoid linking in the system's own getopt implementation. - int woptopt = '?'; - - // Describe how to deal with options that follow non-option ARGV-elements. - // - // If the caller did not specify anything, the default is PERMUTE. - // - // REQUIRE_ORDER means don't recognize them as options; stop option processing when the first - // non-option is seen. This is what Unix does. This mode of operation is selected by using `+' - // as the first character of the list of option characters. - // - // PERMUTE is the default. We permute the contents of ARGV as we scan, so that eventually all - // the non-options are at the end. This allows options to be given in any order, even with - // programs that were not written to expect this. - // - // RETURN_IN_ORDER is an option available to programs that were written to expect options and - // other ARGV-elements in any order and that care about the ordering of the two. We describe - // each non-option ARGV-element as if it were the argument of an option with character code 1. - // Using `-' as the first character of the list of option characters selects this mode of - // operation. - // - // The special argument `--' forces an end of option-scanning regardless of the value of - // `ordering'. In the case of RETURN_IN_ORDER, only `--' can cause `getopt' to return EOF with - // `woptind' != ARGC. - enum { REQUIRE_ORDER, PERMUTE, RETURN_IN_ORDER } ordering = PERMUTE; - - // Handle permutation of arguments. - - // Describe the part of ARGV that contains non-options that have been skipped. `first_nonopt' - // is the index in ARGV of the first of them; `last_nonopt' is the index after the last of them. - int first_nonopt = 0; - int last_nonopt = 0; - - wgetopter_t() {} - - int wgetopt_long(int argc, string_array_t argv, const wchar_t *options, - const struct woption *long_options, int *opt_index); - - private: - void exchange(string_array_t argv); - void _wgetopt_initialize(const wchar_t *optstring); - int _wgetopt_internal(int argc, string_array_t argv, const wchar_t *optstring, - const struct woption *longopts, int *longind, int long_only); - int _advance_to_next_argv(int argc, string_array_t argv, const struct woption *longopts); - int _handle_short_opt(int argc, string_array_t argv); - bool _handle_long_opt(int argc, string_array_t argv, const struct woption *longopts, - int *longind, int long_only, int *retval); - const struct woption *_find_matching_long_opt(const struct woption *longopts, size_t nameend, - int *exact, int *ambig, int *indfound) const; - void _update_long_opt(int argc, string_array_t argv, const struct woption *pfound, - size_t nameend, int *longind, int option_index, int *retval); - bool initialized = false; - bool missing_arg_return_colon = false; -}; - -// Names for the values of the `has_arg' field of `woption'. -enum woption_argument_t : int { no_argument = 0, required_argument = 1, optional_argument = 2 }; - -/// Describe the long-named options requested by the application. The LONG_OPTIONS argument to -/// getopt_long or getopt_long_only is a vector of `struct option' terminated by an element -/// containing a name which is zero. -/// -/// The field `has_arg' is: -/// no_argument (or 0) if the option does not take an argument, -/// required_argument (or 1) if the option requires an argument, -/// optional_argument (or 2) if the option takes an optional argument. -/// -/// If the field `flag' is not NULL, it points to a variable that is set to the value given in the -/// field `val' when the option is found, but left unchanged if the option is not found. -/// -/// To have a long-named option do something other than set an `int' to a compiled-in constant, such -/// as set a value from `optarg', set the option's `flag' field to zero and its `val' field to a -/// nonzero value (the equivalent single-letter option character, if there is one). For long -/// options that have a zero `flag' field, `getopt' returns the contents of the `val' field. -struct woption { - /// Long name for switch. - const wchar_t *name{nullptr}; - /// Must be one of no_argument, required_argument or optional_argument. - woption_argument_t has_arg{}; - /// If \c flag is non-null, this is the value that flag will be set to. Otherwise, this is the - /// return-value of the function call. - wchar_t val{L'\0'}; - - constexpr woption(const wchar_t *name, woption_argument_t has_arg, wchar_t val) - : name(name), has_arg(has_arg), val(val) {} - - constexpr woption() = default; -}; - -#endif /* FISH_WGETOPT_H */ diff --git a/src/widecharwidth/LICENSE b/src/widecharwidth/LICENSE deleted file mode 100644 index d3b1dd767..000000000 --- a/src/widecharwidth/LICENSE +++ /dev/null @@ -1,4 +0,0 @@ -widecharwidth - wcwidth implementation -Written in 2018 by ridiculous_fish -To the extent possible under law, the author(s) have dedicated all copyright and related and neighboring rights to this software to the public domain worldwide. This software is distributed without any warranty. -You should have received a copy of the CC0 Public Domain Dedication along with this software. If not, see . diff --git a/src/widecharwidth/widechar_width.h b/src/widecharwidth/widechar_width.h deleted file mode 100644 index e66e7a4e4..000000000 --- a/src/widecharwidth/widechar_width.h +++ /dev/null @@ -1,1522 +0,0 @@ -/** - * widechar_width.h for Unicode 15.0.0 - * See https://github.com/ridiculousfish/widecharwidth/ - * - * SHA1 file hashes: - * ( - * the hashes for generate.py and the template are git object hashes, - * use `git log --all --find-object=` in the widecharwidth repository - * to see which commit they correspond to, - * or run `git hash-object` on the file to compare. - * The other hashes are simple `sha1sum` style hashes. - * ) - * - * generate.py: 6d63502e0a28f40351524953141ea802a79dced9 - * template.js: 1249763c5b7c1e308aeb4ca64f1e15bce1fab9b3 - * UnicodeData.txt: 3e1900295af0978ad6be3153de4c97d55198ab4b - * EastAsianWidth.txt: 2637ce61d024cb25c768023fa4d7594b53474919 - * emoji-data.txt: 7754a51be6ebe38f906e4fe948720e0f3b78bfd7 - */ - -#ifndef WIDECHAR_WIDTH_H -#define WIDECHAR_WIDTH_H - -#include -#include -#include -#include - -namespace { - -/* Special width values */ -enum { - widechar_nonprint = -1, // The character is not printable. - widechar_combining = -2, // The character is a zero-width combiner. - widechar_ambiguous = -3, // The character is East-Asian ambiguous width. - widechar_private_use = -4, // The character is for private use. - widechar_unassigned = -5, // The character is unassigned. - widechar_widened_in_9 = -6, // Width is 1 in Unicode 8, 2 in Unicode 9+. - widechar_non_character = -7 // The character is a noncharacter. -}; - -/* An inclusive range of characters. */ -struct widechar_range { - uint32_t lo; - uint32_t hi; -}; - -/* Simple ASCII characters - used a lot, so we check them first. */ -static const struct widechar_range widechar_ascii_table[] = { - {0x00020, 0x0007E} -}; - -/* Private usage range. */ -static const struct widechar_range widechar_private_table[] = { - {0x0E000, 0x0F8FF}, - {0xF0000, 0xFFFFD}, - {0x100000, 0x10FFFD} -}; - -/* Nonprinting characters. */ -static const struct widechar_range widechar_nonprint_table[] = { - {0x00000, 0x0001F}, - {0x0007F, 0x0009F}, - {0x000AD, 0x000AD}, - {0x00600, 0x00605}, - {0x0061C, 0x0061C}, - {0x006DD, 0x006DD}, - {0x0070F, 0x0070F}, - {0x00890, 0x00891}, - {0x008E2, 0x008E2}, - {0x0180E, 0x0180E}, - {0x0200B, 0x0200F}, - {0x02028, 0x0202E}, - {0x02060, 0x02064}, - {0x02066, 0x0206F}, - {0x0D800, 0x0DFFF}, - {0x0FEFF, 0x0FEFF}, - {0x0FFF9, 0x0FFFB}, - {0x110BD, 0x110BD}, - {0x110CD, 0x110CD}, - {0x13430, 0x1343F}, - {0x1BCA0, 0x1BCA3}, - {0x1D173, 0x1D17A}, - {0xE0001, 0xE0001}, - {0xE0020, 0xE007F} -}; - -/* Width 0 combining marks. */ -static const struct widechar_range widechar_combining_table[] = { - {0x00300, 0x0036F}, - {0x00483, 0x00489}, - {0x00591, 0x005BD}, - {0x005BF, 0x005BF}, - {0x005C1, 0x005C2}, - {0x005C4, 0x005C5}, - {0x005C7, 0x005C7}, - {0x00610, 0x0061A}, - {0x0064B, 0x0065F}, - {0x00670, 0x00670}, - {0x006D6, 0x006DC}, - {0x006DF, 0x006E4}, - {0x006E7, 0x006E8}, - {0x006EA, 0x006ED}, - {0x00711, 0x00711}, - {0x00730, 0x0074A}, - {0x007A6, 0x007B0}, - {0x007EB, 0x007F3}, - {0x007FD, 0x007FD}, - {0x00816, 0x00819}, - {0x0081B, 0x00823}, - {0x00825, 0x00827}, - {0x00829, 0x0082D}, - {0x00859, 0x0085B}, - {0x00898, 0x0089F}, - {0x008CA, 0x008E1}, - {0x008E3, 0x00903}, - {0x0093A, 0x0093C}, - {0x0093E, 0x0094F}, - {0x00951, 0x00957}, - {0x00962, 0x00963}, - {0x00981, 0x00983}, - {0x009BC, 0x009BC}, - {0x009BE, 0x009C4}, - {0x009C7, 0x009C8}, - {0x009CB, 0x009CD}, - {0x009D7, 0x009D7}, - {0x009E2, 0x009E3}, - {0x009FE, 0x009FE}, - {0x00A01, 0x00A03}, - {0x00A3C, 0x00A3C}, - {0x00A3E, 0x00A42}, - {0x00A47, 0x00A48}, - {0x00A4B, 0x00A4D}, - {0x00A51, 0x00A51}, - {0x00A70, 0x00A71}, - {0x00A75, 0x00A75}, - {0x00A81, 0x00A83}, - {0x00ABC, 0x00ABC}, - {0x00ABE, 0x00AC5}, - {0x00AC7, 0x00AC9}, - {0x00ACB, 0x00ACD}, - {0x00AE2, 0x00AE3}, - {0x00AFA, 0x00AFF}, - {0x00B01, 0x00B03}, - {0x00B3C, 0x00B3C}, - {0x00B3E, 0x00B44}, - {0x00B47, 0x00B48}, - {0x00B4B, 0x00B4D}, - {0x00B55, 0x00B57}, - {0x00B62, 0x00B63}, - {0x00B82, 0x00B82}, - {0x00BBE, 0x00BC2}, - {0x00BC6, 0x00BC8}, - {0x00BCA, 0x00BCD}, - {0x00BD7, 0x00BD7}, - {0x00C00, 0x00C04}, - {0x00C3C, 0x00C3C}, - {0x00C3E, 0x00C44}, - {0x00C46, 0x00C48}, - {0x00C4A, 0x00C4D}, - {0x00C55, 0x00C56}, - {0x00C62, 0x00C63}, - {0x00C81, 0x00C83}, - {0x00CBC, 0x00CBC}, - {0x00CBE, 0x00CC4}, - {0x00CC6, 0x00CC8}, - {0x00CCA, 0x00CCD}, - {0x00CD5, 0x00CD6}, - {0x00CE2, 0x00CE3}, - {0x00CF3, 0x00CF3}, - {0x00D00, 0x00D03}, - {0x00D3B, 0x00D3C}, - {0x00D3E, 0x00D44}, - {0x00D46, 0x00D48}, - {0x00D4A, 0x00D4D}, - {0x00D57, 0x00D57}, - {0x00D62, 0x00D63}, - {0x00D81, 0x00D83}, - {0x00DCA, 0x00DCA}, - {0x00DCF, 0x00DD4}, - {0x00DD6, 0x00DD6}, - {0x00DD8, 0x00DDF}, - {0x00DF2, 0x00DF3}, - {0x00E31, 0x00E31}, - {0x00E34, 0x00E3A}, - {0x00E47, 0x00E4E}, - {0x00EB1, 0x00EB1}, - {0x00EB4, 0x00EBC}, - {0x00EC8, 0x00ECE}, - {0x00F18, 0x00F19}, - {0x00F35, 0x00F35}, - {0x00F37, 0x00F37}, - {0x00F39, 0x00F39}, - {0x00F3E, 0x00F3F}, - {0x00F71, 0x00F84}, - {0x00F86, 0x00F87}, - {0x00F8D, 0x00F97}, - {0x00F99, 0x00FBC}, - {0x00FC6, 0x00FC6}, - {0x0102B, 0x0103E}, - {0x01056, 0x01059}, - {0x0105E, 0x01060}, - {0x01062, 0x01064}, - {0x01067, 0x0106D}, - {0x01071, 0x01074}, - {0x01082, 0x0108D}, - {0x0108F, 0x0108F}, - {0x0109A, 0x0109D}, - {0x0135D, 0x0135F}, - {0x01712, 0x01715}, - {0x01732, 0x01734}, - {0x01752, 0x01753}, - {0x01772, 0x01773}, - {0x017B4, 0x017D3}, - {0x017DD, 0x017DD}, - {0x0180B, 0x0180D}, - {0x0180F, 0x0180F}, - {0x01885, 0x01886}, - {0x018A9, 0x018A9}, - {0x01920, 0x0192B}, - {0x01930, 0x0193B}, - {0x01A17, 0x01A1B}, - {0x01A55, 0x01A5E}, - {0x01A60, 0x01A7C}, - {0x01A7F, 0x01A7F}, - {0x01AB0, 0x01ACE}, - {0x01B00, 0x01B04}, - {0x01B34, 0x01B44}, - {0x01B6B, 0x01B73}, - {0x01B80, 0x01B82}, - {0x01BA1, 0x01BAD}, - {0x01BE6, 0x01BF3}, - {0x01C24, 0x01C37}, - {0x01CD0, 0x01CD2}, - {0x01CD4, 0x01CE8}, - {0x01CED, 0x01CED}, - {0x01CF4, 0x01CF4}, - {0x01CF7, 0x01CF9}, - {0x01DC0, 0x01DFF}, - {0x020D0, 0x020F0}, - {0x02CEF, 0x02CF1}, - {0x02D7F, 0x02D7F}, - {0x02DE0, 0x02DFF}, - {0x0302A, 0x0302F}, - {0x03099, 0x0309A}, - {0x0A66F, 0x0A672}, - {0x0A674, 0x0A67D}, - {0x0A69E, 0x0A69F}, - {0x0A6F0, 0x0A6F1}, - {0x0A802, 0x0A802}, - {0x0A806, 0x0A806}, - {0x0A80B, 0x0A80B}, - {0x0A823, 0x0A827}, - {0x0A82C, 0x0A82C}, - {0x0A880, 0x0A881}, - {0x0A8B4, 0x0A8C5}, - {0x0A8E0, 0x0A8F1}, - {0x0A8FF, 0x0A8FF}, - {0x0A926, 0x0A92D}, - {0x0A947, 0x0A953}, - {0x0A980, 0x0A983}, - {0x0A9B3, 0x0A9C0}, - {0x0A9E5, 0x0A9E5}, - {0x0AA29, 0x0AA36}, - {0x0AA43, 0x0AA43}, - {0x0AA4C, 0x0AA4D}, - {0x0AA7B, 0x0AA7D}, - {0x0AAB0, 0x0AAB0}, - {0x0AAB2, 0x0AAB4}, - {0x0AAB7, 0x0AAB8}, - {0x0AABE, 0x0AABF}, - {0x0AAC1, 0x0AAC1}, - {0x0AAEB, 0x0AAEF}, - {0x0AAF5, 0x0AAF6}, - {0x0ABE3, 0x0ABEA}, - {0x0ABEC, 0x0ABED}, - {0x0FB1E, 0x0FB1E}, - {0x0FE00, 0x0FE0F}, - {0x0FE20, 0x0FE2F}, - {0x101FD, 0x101FD}, - {0x102E0, 0x102E0}, - {0x10376, 0x1037A}, - {0x10A01, 0x10A03}, - {0x10A05, 0x10A06}, - {0x10A0C, 0x10A0F}, - {0x10A38, 0x10A3A}, - {0x10A3F, 0x10A3F}, - {0x10AE5, 0x10AE6}, - {0x10D24, 0x10D27}, - {0x10EAB, 0x10EAC}, - {0x10EFD, 0x10EFF}, - {0x10F46, 0x10F50}, - {0x10F82, 0x10F85}, - {0x11000, 0x11002}, - {0x11038, 0x11046}, - {0x11070, 0x11070}, - {0x11073, 0x11074}, - {0x1107F, 0x11082}, - {0x110B0, 0x110BA}, - {0x110C2, 0x110C2}, - {0x11100, 0x11102}, - {0x11127, 0x11134}, - {0x11145, 0x11146}, - {0x11173, 0x11173}, - {0x11180, 0x11182}, - {0x111B3, 0x111C0}, - {0x111C9, 0x111CC}, - {0x111CE, 0x111CF}, - {0x1122C, 0x11237}, - {0x1123E, 0x1123E}, - {0x11241, 0x11241}, - {0x112DF, 0x112EA}, - {0x11300, 0x11303}, - {0x1133B, 0x1133C}, - {0x1133E, 0x11344}, - {0x11347, 0x11348}, - {0x1134B, 0x1134D}, - {0x11357, 0x11357}, - {0x11362, 0x11363}, - {0x11366, 0x1136C}, - {0x11370, 0x11374}, - {0x11435, 0x11446}, - {0x1145E, 0x1145E}, - {0x114B0, 0x114C3}, - {0x115AF, 0x115B5}, - {0x115B8, 0x115C0}, - {0x115DC, 0x115DD}, - {0x11630, 0x11640}, - {0x116AB, 0x116B7}, - {0x1171D, 0x1172B}, - {0x1182C, 0x1183A}, - {0x11930, 0x11935}, - {0x11937, 0x11938}, - {0x1193B, 0x1193E}, - {0x11940, 0x11940}, - {0x11942, 0x11943}, - {0x119D1, 0x119D7}, - {0x119DA, 0x119E0}, - {0x119E4, 0x119E4}, - {0x11A01, 0x11A0A}, - {0x11A33, 0x11A39}, - {0x11A3B, 0x11A3E}, - {0x11A47, 0x11A47}, - {0x11A51, 0x11A5B}, - {0x11A8A, 0x11A99}, - {0x11C2F, 0x11C36}, - {0x11C38, 0x11C3F}, - {0x11C92, 0x11CA7}, - {0x11CA9, 0x11CB6}, - {0x11D31, 0x11D36}, - {0x11D3A, 0x11D3A}, - {0x11D3C, 0x11D3D}, - {0x11D3F, 0x11D45}, - {0x11D47, 0x11D47}, - {0x11D8A, 0x11D8E}, - {0x11D90, 0x11D91}, - {0x11D93, 0x11D97}, - {0x11EF3, 0x11EF6}, - {0x11F00, 0x11F01}, - {0x11F03, 0x11F03}, - {0x11F34, 0x11F3A}, - {0x11F3E, 0x11F42}, - {0x13440, 0x13440}, - {0x13447, 0x13455}, - {0x16AF0, 0x16AF4}, - {0x16B30, 0x16B36}, - {0x16F4F, 0x16F4F}, - {0x16F51, 0x16F87}, - {0x16F8F, 0x16F92}, - {0x16FE4, 0x16FE4}, - {0x16FF0, 0x16FF1}, - {0x1BC9D, 0x1BC9E}, - {0x1CF00, 0x1CF2D}, - {0x1CF30, 0x1CF46}, - {0x1D165, 0x1D169}, - {0x1D16D, 0x1D172}, - {0x1D17B, 0x1D182}, - {0x1D185, 0x1D18B}, - {0x1D1AA, 0x1D1AD}, - {0x1D242, 0x1D244}, - {0x1DA00, 0x1DA36}, - {0x1DA3B, 0x1DA6C}, - {0x1DA75, 0x1DA75}, - {0x1DA84, 0x1DA84}, - {0x1DA9B, 0x1DA9F}, - {0x1DAA1, 0x1DAAF}, - {0x1E000, 0x1E006}, - {0x1E008, 0x1E018}, - {0x1E01B, 0x1E021}, - {0x1E023, 0x1E024}, - {0x1E026, 0x1E02A}, - {0x1E08F, 0x1E08F}, - {0x1E130, 0x1E136}, - {0x1E2AE, 0x1E2AE}, - {0x1E2EC, 0x1E2EF}, - {0x1E4EC, 0x1E4EF}, - {0x1E8D0, 0x1E8D6}, - {0x1E944, 0x1E94A}, - {0xE0100, 0xE01EF} -}; - -/* Width 0 combining letters. */ -static const struct widechar_range widechar_combiningletters_table[] = { - {0x01160, 0x011FF}, - {0x0D7B0, 0x0D7FF} -}; - -/* Width 2 characters. */ -static const struct widechar_range widechar_doublewide_table[] = { - {0x01100, 0x0115F}, - {0x02329, 0x0232A}, - {0x02E80, 0x02E99}, - {0x02E9B, 0x02EF3}, - {0x02F00, 0x02FD5}, - {0x02FF0, 0x02FFB}, - {0x03000, 0x0303E}, - {0x03041, 0x03096}, - {0x03099, 0x030FF}, - {0x03105, 0x0312F}, - {0x03131, 0x0318E}, - {0x03190, 0x031E3}, - {0x031F0, 0x0321E}, - {0x03220, 0x03247}, - {0x03250, 0x04DBF}, - {0x04E00, 0x0A48C}, - {0x0A490, 0x0A4C6}, - {0x0A960, 0x0A97C}, - {0x0AC00, 0x0D7A3}, - {0x0F900, 0x0FAFF}, - {0x0FE10, 0x0FE19}, - {0x0FE30, 0x0FE52}, - {0x0FE54, 0x0FE66}, - {0x0FE68, 0x0FE6B}, - {0x0FF01, 0x0FF60}, - {0x0FFE0, 0x0FFE6}, - {0x16FE0, 0x16FE4}, - {0x16FF0, 0x16FF1}, - {0x17000, 0x187F7}, - {0x18800, 0x18CD5}, - {0x18D00, 0x18D08}, - {0x1AFF0, 0x1AFF3}, - {0x1AFF5, 0x1AFFB}, - {0x1AFFD, 0x1AFFE}, - {0x1B000, 0x1B122}, - {0x1B132, 0x1B132}, - {0x1B150, 0x1B152}, - {0x1B155, 0x1B155}, - {0x1B164, 0x1B167}, - {0x1B170, 0x1B2FB}, - {0x1F200, 0x1F200}, - {0x1F202, 0x1F202}, - {0x1F210, 0x1F219}, - {0x1F21B, 0x1F22E}, - {0x1F230, 0x1F231}, - {0x1F237, 0x1F237}, - {0x1F23B, 0x1F23B}, - {0x1F240, 0x1F248}, - {0x1F260, 0x1F265}, - {0x1F57A, 0x1F57A}, - {0x1F5A4, 0x1F5A4}, - {0x1F6D1, 0x1F6D2}, - {0x1F6D5, 0x1F6D7}, - {0x1F6DC, 0x1F6DF}, - {0x1F6F4, 0x1F6FC}, - {0x1F7E0, 0x1F7EB}, - {0x1F7F0, 0x1F7F0}, - {0x1F90C, 0x1F90F}, - {0x1F919, 0x1F93A}, - {0x1F93C, 0x1F945}, - {0x1F947, 0x1F97F}, - {0x1F985, 0x1F9BF}, - {0x1F9C1, 0x1F9FF}, - {0x1FA70, 0x1FA7C}, - {0x1FA80, 0x1FA88}, - {0x1FA90, 0x1FABD}, - {0x1FABF, 0x1FAC5}, - {0x1FACE, 0x1FADB}, - {0x1FAE0, 0x1FAE8}, - {0x1FAF0, 0x1FAF8}, - {0x20000, 0x2FFFD}, - {0x30000, 0x3FFFD} -}; - -/* Ambiguous-width characters. */ -static const struct widechar_range widechar_ambiguous_table[] = { - {0x000A1, 0x000A1}, - {0x000A4, 0x000A4}, - {0x000A7, 0x000A8}, - {0x000AA, 0x000AA}, - {0x000AD, 0x000AE}, - {0x000B0, 0x000B4}, - {0x000B6, 0x000BA}, - {0x000BC, 0x000BF}, - {0x000C6, 0x000C6}, - {0x000D0, 0x000D0}, - {0x000D7, 0x000D8}, - {0x000DE, 0x000E1}, - {0x000E6, 0x000E6}, - {0x000E8, 0x000EA}, - {0x000EC, 0x000ED}, - {0x000F0, 0x000F0}, - {0x000F2, 0x000F3}, - {0x000F7, 0x000FA}, - {0x000FC, 0x000FC}, - {0x000FE, 0x000FE}, - {0x00101, 0x00101}, - {0x00111, 0x00111}, - {0x00113, 0x00113}, - {0x0011B, 0x0011B}, - {0x00126, 0x00127}, - {0x0012B, 0x0012B}, - {0x00131, 0x00133}, - {0x00138, 0x00138}, - {0x0013F, 0x00142}, - {0x00144, 0x00144}, - {0x00148, 0x0014B}, - {0x0014D, 0x0014D}, - {0x00152, 0x00153}, - {0x00166, 0x00167}, - {0x0016B, 0x0016B}, - {0x001CE, 0x001CE}, - {0x001D0, 0x001D0}, - {0x001D2, 0x001D2}, - {0x001D4, 0x001D4}, - {0x001D6, 0x001D6}, - {0x001D8, 0x001D8}, - {0x001DA, 0x001DA}, - {0x001DC, 0x001DC}, - {0x00251, 0x00251}, - {0x00261, 0x00261}, - {0x002C4, 0x002C4}, - {0x002C7, 0x002C7}, - {0x002C9, 0x002CB}, - {0x002CD, 0x002CD}, - {0x002D0, 0x002D0}, - {0x002D8, 0x002DB}, - {0x002DD, 0x002DD}, - {0x002DF, 0x002DF}, - {0x00300, 0x0036F}, - {0x00391, 0x003A1}, - {0x003A3, 0x003A9}, - {0x003B1, 0x003C1}, - {0x003C3, 0x003C9}, - {0x00401, 0x00401}, - {0x00410, 0x0044F}, - {0x00451, 0x00451}, - {0x02010, 0x02010}, - {0x02013, 0x02016}, - {0x02018, 0x02019}, - {0x0201C, 0x0201D}, - {0x02020, 0x02022}, - {0x02024, 0x02027}, - {0x02030, 0x02030}, - {0x02032, 0x02033}, - {0x02035, 0x02035}, - {0x0203B, 0x0203B}, - {0x0203E, 0x0203E}, - {0x02074, 0x02074}, - {0x0207F, 0x0207F}, - {0x02081, 0x02084}, - {0x020AC, 0x020AC}, - {0x02103, 0x02103}, - {0x02105, 0x02105}, - {0x02109, 0x02109}, - {0x02113, 0x02113}, - {0x02116, 0x02116}, - {0x02121, 0x02122}, - {0x02126, 0x02126}, - {0x0212B, 0x0212B}, - {0x02153, 0x02154}, - {0x0215B, 0x0215E}, - {0x02160, 0x0216B}, - {0x02170, 0x02179}, - {0x02189, 0x02189}, - {0x02190, 0x02199}, - {0x021B8, 0x021B9}, - {0x021D2, 0x021D2}, - {0x021D4, 0x021D4}, - {0x021E7, 0x021E7}, - {0x02200, 0x02200}, - {0x02202, 0x02203}, - {0x02207, 0x02208}, - {0x0220B, 0x0220B}, - {0x0220F, 0x0220F}, - {0x02211, 0x02211}, - {0x02215, 0x02215}, - {0x0221A, 0x0221A}, - {0x0221D, 0x02220}, - {0x02223, 0x02223}, - {0x02225, 0x02225}, - {0x02227, 0x0222C}, - {0x0222E, 0x0222E}, - {0x02234, 0x02237}, - {0x0223C, 0x0223D}, - {0x02248, 0x02248}, - {0x0224C, 0x0224C}, - {0x02252, 0x02252}, - {0x02260, 0x02261}, - {0x02264, 0x02267}, - {0x0226A, 0x0226B}, - {0x0226E, 0x0226F}, - {0x02282, 0x02283}, - {0x02286, 0x02287}, - {0x02295, 0x02295}, - {0x02299, 0x02299}, - {0x022A5, 0x022A5}, - {0x022BF, 0x022BF}, - {0x02312, 0x02312}, - {0x02460, 0x024E9}, - {0x024EB, 0x0254B}, - {0x02550, 0x02573}, - {0x02580, 0x0258F}, - {0x02592, 0x02595}, - {0x025A0, 0x025A1}, - {0x025A3, 0x025A9}, - {0x025B2, 0x025B3}, - {0x025B6, 0x025B7}, - {0x025BC, 0x025BD}, - {0x025C0, 0x025C1}, - {0x025C6, 0x025C8}, - {0x025CB, 0x025CB}, - {0x025CE, 0x025D1}, - {0x025E2, 0x025E5}, - {0x025EF, 0x025EF}, - {0x02605, 0x02606}, - {0x02609, 0x02609}, - {0x0260E, 0x0260F}, - {0x0261C, 0x0261C}, - {0x0261E, 0x0261E}, - {0x02640, 0x02640}, - {0x02642, 0x02642}, - {0x02660, 0x02661}, - {0x02663, 0x02665}, - {0x02667, 0x0266A}, - {0x0266C, 0x0266D}, - {0x0266F, 0x0266F}, - {0x0269E, 0x0269F}, - {0x026BF, 0x026BF}, - {0x026C6, 0x026CD}, - {0x026CF, 0x026D3}, - {0x026D5, 0x026E1}, - {0x026E3, 0x026E3}, - {0x026E8, 0x026E9}, - {0x026EB, 0x026F1}, - {0x026F4, 0x026F4}, - {0x026F6, 0x026F9}, - {0x026FB, 0x026FC}, - {0x026FE, 0x026FF}, - {0x0273D, 0x0273D}, - {0x02776, 0x0277F}, - {0x02B56, 0x02B59}, - {0x03248, 0x0324F}, - {0x0E000, 0x0F8FF}, - {0x0FE00, 0x0FE0F}, - {0x0FFFD, 0x0FFFD}, - {0x1F100, 0x1F10A}, - {0x1F110, 0x1F12D}, - {0x1F130, 0x1F169}, - {0x1F170, 0x1F18D}, - {0x1F18F, 0x1F190}, - {0x1F19B, 0x1F1AC}, - {0xE0100, 0xE01EF}, - {0xF0000, 0xFFFFD}, - {0x100000, 0x10FFFD} -}; - -/* Unassigned characters. */ -static const struct widechar_range widechar_unassigned_table[] = { - {0x00378, 0x00379}, - {0x00380, 0x00383}, - {0x0038B, 0x0038B}, - {0x0038D, 0x0038D}, - {0x003A2, 0x003A2}, - {0x00530, 0x00530}, - {0x00557, 0x00558}, - {0x0058B, 0x0058C}, - {0x00590, 0x00590}, - {0x005C8, 0x005CF}, - {0x005EB, 0x005EE}, - {0x005F5, 0x005FF}, - {0x0070E, 0x0070E}, - {0x0074B, 0x0074C}, - {0x007B2, 0x007BF}, - {0x007FB, 0x007FC}, - {0x0082E, 0x0082F}, - {0x0083F, 0x0083F}, - {0x0085C, 0x0085D}, - {0x0085F, 0x0085F}, - {0x0086B, 0x0086F}, - {0x0088F, 0x0088F}, - {0x00892, 0x00897}, - {0x00984, 0x00984}, - {0x0098D, 0x0098E}, - {0x00991, 0x00992}, - {0x009A9, 0x009A9}, - {0x009B1, 0x009B1}, - {0x009B3, 0x009B5}, - {0x009BA, 0x009BB}, - {0x009C5, 0x009C6}, - {0x009C9, 0x009CA}, - {0x009CF, 0x009D6}, - {0x009D8, 0x009DB}, - {0x009DE, 0x009DE}, - {0x009E4, 0x009E5}, - {0x009FF, 0x00A00}, - {0x00A04, 0x00A04}, - {0x00A0B, 0x00A0E}, - {0x00A11, 0x00A12}, - {0x00A29, 0x00A29}, - {0x00A31, 0x00A31}, - {0x00A34, 0x00A34}, - {0x00A37, 0x00A37}, - {0x00A3A, 0x00A3B}, - {0x00A3D, 0x00A3D}, - {0x00A43, 0x00A46}, - {0x00A49, 0x00A4A}, - {0x00A4E, 0x00A50}, - {0x00A52, 0x00A58}, - {0x00A5D, 0x00A5D}, - {0x00A5F, 0x00A65}, - {0x00A77, 0x00A80}, - {0x00A84, 0x00A84}, - {0x00A8E, 0x00A8E}, - {0x00A92, 0x00A92}, - {0x00AA9, 0x00AA9}, - {0x00AB1, 0x00AB1}, - {0x00AB4, 0x00AB4}, - {0x00ABA, 0x00ABB}, - {0x00AC6, 0x00AC6}, - {0x00ACA, 0x00ACA}, - {0x00ACE, 0x00ACF}, - {0x00AD1, 0x00ADF}, - {0x00AE4, 0x00AE5}, - {0x00AF2, 0x00AF8}, - {0x00B00, 0x00B00}, - {0x00B04, 0x00B04}, - {0x00B0D, 0x00B0E}, - {0x00B11, 0x00B12}, - {0x00B29, 0x00B29}, - {0x00B31, 0x00B31}, - {0x00B34, 0x00B34}, - {0x00B3A, 0x00B3B}, - {0x00B45, 0x00B46}, - {0x00B49, 0x00B4A}, - {0x00B4E, 0x00B54}, - {0x00B58, 0x00B5B}, - {0x00B5E, 0x00B5E}, - {0x00B64, 0x00B65}, - {0x00B78, 0x00B81}, - {0x00B84, 0x00B84}, - {0x00B8B, 0x00B8D}, - {0x00B91, 0x00B91}, - {0x00B96, 0x00B98}, - {0x00B9B, 0x00B9B}, - {0x00B9D, 0x00B9D}, - {0x00BA0, 0x00BA2}, - {0x00BA5, 0x00BA7}, - {0x00BAB, 0x00BAD}, - {0x00BBA, 0x00BBD}, - {0x00BC3, 0x00BC5}, - {0x00BC9, 0x00BC9}, - {0x00BCE, 0x00BCF}, - {0x00BD1, 0x00BD6}, - {0x00BD8, 0x00BE5}, - {0x00BFB, 0x00BFF}, - {0x00C0D, 0x00C0D}, - {0x00C11, 0x00C11}, - {0x00C29, 0x00C29}, - {0x00C3A, 0x00C3B}, - {0x00C45, 0x00C45}, - {0x00C49, 0x00C49}, - {0x00C4E, 0x00C54}, - {0x00C57, 0x00C57}, - {0x00C5B, 0x00C5C}, - {0x00C5E, 0x00C5F}, - {0x00C64, 0x00C65}, - {0x00C70, 0x00C76}, - {0x00C8D, 0x00C8D}, - {0x00C91, 0x00C91}, - {0x00CA9, 0x00CA9}, - {0x00CB4, 0x00CB4}, - {0x00CBA, 0x00CBB}, - {0x00CC5, 0x00CC5}, - {0x00CC9, 0x00CC9}, - {0x00CCE, 0x00CD4}, - {0x00CD7, 0x00CDC}, - {0x00CDF, 0x00CDF}, - {0x00CE4, 0x00CE5}, - {0x00CF0, 0x00CF0}, - {0x00CF4, 0x00CFF}, - {0x00D0D, 0x00D0D}, - {0x00D11, 0x00D11}, - {0x00D45, 0x00D45}, - {0x00D49, 0x00D49}, - {0x00D50, 0x00D53}, - {0x00D64, 0x00D65}, - {0x00D80, 0x00D80}, - {0x00D84, 0x00D84}, - {0x00D97, 0x00D99}, - {0x00DB2, 0x00DB2}, - {0x00DBC, 0x00DBC}, - {0x00DBE, 0x00DBF}, - {0x00DC7, 0x00DC9}, - {0x00DCB, 0x00DCE}, - {0x00DD5, 0x00DD5}, - {0x00DD7, 0x00DD7}, - {0x00DE0, 0x00DE5}, - {0x00DF0, 0x00DF1}, - {0x00DF5, 0x00E00}, - {0x00E3B, 0x00E3E}, - {0x00E5C, 0x00E80}, - {0x00E83, 0x00E83}, - {0x00E85, 0x00E85}, - {0x00E8B, 0x00E8B}, - {0x00EA4, 0x00EA4}, - {0x00EA6, 0x00EA6}, - {0x00EBE, 0x00EBF}, - {0x00EC5, 0x00EC5}, - {0x00EC7, 0x00EC7}, - {0x00ECF, 0x00ECF}, - {0x00EDA, 0x00EDB}, - {0x00EE0, 0x00EFF}, - {0x00F48, 0x00F48}, - {0x00F6D, 0x00F70}, - {0x00F98, 0x00F98}, - {0x00FBD, 0x00FBD}, - {0x00FCD, 0x00FCD}, - {0x00FDB, 0x00FFF}, - {0x010C6, 0x010C6}, - {0x010C8, 0x010CC}, - {0x010CE, 0x010CF}, - {0x01249, 0x01249}, - {0x0124E, 0x0124F}, - {0x01257, 0x01257}, - {0x01259, 0x01259}, - {0x0125E, 0x0125F}, - {0x01289, 0x01289}, - {0x0128E, 0x0128F}, - {0x012B1, 0x012B1}, - {0x012B6, 0x012B7}, - {0x012BF, 0x012BF}, - {0x012C1, 0x012C1}, - {0x012C6, 0x012C7}, - {0x012D7, 0x012D7}, - {0x01311, 0x01311}, - {0x01316, 0x01317}, - {0x0135B, 0x0135C}, - {0x0137D, 0x0137F}, - {0x0139A, 0x0139F}, - {0x013F6, 0x013F7}, - {0x013FE, 0x013FF}, - {0x0169D, 0x0169F}, - {0x016F9, 0x016FF}, - {0x01716, 0x0171E}, - {0x01737, 0x0173F}, - {0x01754, 0x0175F}, - {0x0176D, 0x0176D}, - {0x01771, 0x01771}, - {0x01774, 0x0177F}, - {0x017DE, 0x017DF}, - {0x017EA, 0x017EF}, - {0x017FA, 0x017FF}, - {0x0181A, 0x0181F}, - {0x01879, 0x0187F}, - {0x018AB, 0x018AF}, - {0x018F6, 0x018FF}, - {0x0191F, 0x0191F}, - {0x0192C, 0x0192F}, - {0x0193C, 0x0193F}, - {0x01941, 0x01943}, - {0x0196E, 0x0196F}, - {0x01975, 0x0197F}, - {0x019AC, 0x019AF}, - {0x019CA, 0x019CF}, - {0x019DB, 0x019DD}, - {0x01A1C, 0x01A1D}, - {0x01A5F, 0x01A5F}, - {0x01A7D, 0x01A7E}, - {0x01A8A, 0x01A8F}, - {0x01A9A, 0x01A9F}, - {0x01AAE, 0x01AAF}, - {0x01ACF, 0x01AFF}, - {0x01B4D, 0x01B4F}, - {0x01B7F, 0x01B7F}, - {0x01BF4, 0x01BFB}, - {0x01C38, 0x01C3A}, - {0x01C4A, 0x01C4C}, - {0x01C89, 0x01C8F}, - {0x01CBB, 0x01CBC}, - {0x01CC8, 0x01CCF}, - {0x01CFB, 0x01CFF}, - {0x01F16, 0x01F17}, - {0x01F1E, 0x01F1F}, - {0x01F46, 0x01F47}, - {0x01F4E, 0x01F4F}, - {0x01F58, 0x01F58}, - {0x01F5A, 0x01F5A}, - {0x01F5C, 0x01F5C}, - {0x01F5E, 0x01F5E}, - {0x01F7E, 0x01F7F}, - {0x01FB5, 0x01FB5}, - {0x01FC5, 0x01FC5}, - {0x01FD4, 0x01FD5}, - {0x01FDC, 0x01FDC}, - {0x01FF0, 0x01FF1}, - {0x01FF5, 0x01FF5}, - {0x01FFF, 0x01FFF}, - {0x02065, 0x02065}, - {0x02072, 0x02073}, - {0x0208F, 0x0208F}, - {0x0209D, 0x0209F}, - {0x020C1, 0x020CF}, - {0x020F1, 0x020FF}, - {0x0218C, 0x0218F}, - {0x02427, 0x0243F}, - {0x0244B, 0x0245F}, - {0x02B74, 0x02B75}, - {0x02B96, 0x02B96}, - {0x02CF4, 0x02CF8}, - {0x02D26, 0x02D26}, - {0x02D28, 0x02D2C}, - {0x02D2E, 0x02D2F}, - {0x02D68, 0x02D6E}, - {0x02D71, 0x02D7E}, - {0x02D97, 0x02D9F}, - {0x02DA7, 0x02DA7}, - {0x02DAF, 0x02DAF}, - {0x02DB7, 0x02DB7}, - {0x02DBF, 0x02DBF}, - {0x02DC7, 0x02DC7}, - {0x02DCF, 0x02DCF}, - {0x02DD7, 0x02DD7}, - {0x02DDF, 0x02DDF}, - {0x02E5E, 0x02E7F}, - {0x02E9A, 0x02E9A}, - {0x02EF4, 0x02EFF}, - {0x02FD6, 0x02FEF}, - {0x02FFC, 0x02FFF}, - {0x03040, 0x03040}, - {0x03097, 0x03098}, - {0x03100, 0x03104}, - {0x03130, 0x03130}, - {0x0318F, 0x0318F}, - {0x031E4, 0x031EF}, - {0x0321F, 0x0321F}, - {0x03401, 0x04DBE}, - {0x04E01, 0x09FFE}, - {0x0A48D, 0x0A48F}, - {0x0A4C7, 0x0A4CF}, - {0x0A62C, 0x0A63F}, - {0x0A6F8, 0x0A6FF}, - {0x0A7CB, 0x0A7CF}, - {0x0A7D2, 0x0A7D2}, - {0x0A7D4, 0x0A7D4}, - {0x0A7DA, 0x0A7F1}, - {0x0A82D, 0x0A82F}, - {0x0A83A, 0x0A83F}, - {0x0A878, 0x0A87F}, - {0x0A8C6, 0x0A8CD}, - {0x0A8DA, 0x0A8DF}, - {0x0A954, 0x0A95E}, - {0x0A97D, 0x0A97F}, - {0x0A9CE, 0x0A9CE}, - {0x0A9DA, 0x0A9DD}, - {0x0A9FF, 0x0A9FF}, - {0x0AA37, 0x0AA3F}, - {0x0AA4E, 0x0AA4F}, - {0x0AA5A, 0x0AA5B}, - {0x0AAC3, 0x0AADA}, - {0x0AAF7, 0x0AB00}, - {0x0AB07, 0x0AB08}, - {0x0AB0F, 0x0AB10}, - {0x0AB17, 0x0AB1F}, - {0x0AB27, 0x0AB27}, - {0x0AB2F, 0x0AB2F}, - {0x0AB6C, 0x0AB6F}, - {0x0ABEE, 0x0ABEF}, - {0x0ABFA, 0x0ABFF}, - {0x0AC01, 0x0D7A2}, - {0x0D7A4, 0x0D7AF}, - {0x0D7C7, 0x0D7CA}, - {0x0D7FC, 0x0D7FF}, - {0x0FA6E, 0x0FA6F}, - {0x0FADA, 0x0FAFF}, - {0x0FB07, 0x0FB12}, - {0x0FB18, 0x0FB1C}, - {0x0FB37, 0x0FB37}, - {0x0FB3D, 0x0FB3D}, - {0x0FB3F, 0x0FB3F}, - {0x0FB42, 0x0FB42}, - {0x0FB45, 0x0FB45}, - {0x0FBC3, 0x0FBD2}, - {0x0FD90, 0x0FD91}, - {0x0FDC8, 0x0FDCE}, - {0x0FE1A, 0x0FE1F}, - {0x0FE53, 0x0FE53}, - {0x0FE67, 0x0FE67}, - {0x0FE6C, 0x0FE6F}, - {0x0FE75, 0x0FE75}, - {0x0FEFD, 0x0FEFE}, - {0x0FF00, 0x0FF00}, - {0x0FFBF, 0x0FFC1}, - {0x0FFC8, 0x0FFC9}, - {0x0FFD0, 0x0FFD1}, - {0x0FFD8, 0x0FFD9}, - {0x0FFDD, 0x0FFDF}, - {0x0FFE7, 0x0FFE7}, - {0x0FFEF, 0x0FFF8}, - {0x1000C, 0x1000C}, - {0x10027, 0x10027}, - {0x1003B, 0x1003B}, - {0x1003E, 0x1003E}, - {0x1004E, 0x1004F}, - {0x1005E, 0x1007F}, - {0x100FB, 0x100FF}, - {0x10103, 0x10106}, - {0x10134, 0x10136}, - {0x1018F, 0x1018F}, - {0x1019D, 0x1019F}, - {0x101A1, 0x101CF}, - {0x101FE, 0x1027F}, - {0x1029D, 0x1029F}, - {0x102D1, 0x102DF}, - {0x102FC, 0x102FF}, - {0x10324, 0x1032C}, - {0x1034B, 0x1034F}, - {0x1037B, 0x1037F}, - {0x1039E, 0x1039E}, - {0x103C4, 0x103C7}, - {0x103D6, 0x103FF}, - {0x1049E, 0x1049F}, - {0x104AA, 0x104AF}, - {0x104D4, 0x104D7}, - {0x104FC, 0x104FF}, - {0x10528, 0x1052F}, - {0x10564, 0x1056E}, - {0x1057B, 0x1057B}, - {0x1058B, 0x1058B}, - {0x10593, 0x10593}, - {0x10596, 0x10596}, - {0x105A2, 0x105A2}, - {0x105B2, 0x105B2}, - {0x105BA, 0x105BA}, - {0x105BD, 0x105FF}, - {0x10737, 0x1073F}, - {0x10756, 0x1075F}, - {0x10768, 0x1077F}, - {0x10786, 0x10786}, - {0x107B1, 0x107B1}, - {0x107BB, 0x107FF}, - {0x10806, 0x10807}, - {0x10809, 0x10809}, - {0x10836, 0x10836}, - {0x10839, 0x1083B}, - {0x1083D, 0x1083E}, - {0x10856, 0x10856}, - {0x1089F, 0x108A6}, - {0x108B0, 0x108DF}, - {0x108F3, 0x108F3}, - {0x108F6, 0x108FA}, - {0x1091C, 0x1091E}, - {0x1093A, 0x1093E}, - {0x10940, 0x1097F}, - {0x109B8, 0x109BB}, - {0x109D0, 0x109D1}, - {0x10A04, 0x10A04}, - {0x10A07, 0x10A0B}, - {0x10A14, 0x10A14}, - {0x10A18, 0x10A18}, - {0x10A36, 0x10A37}, - {0x10A3B, 0x10A3E}, - {0x10A49, 0x10A4F}, - {0x10A59, 0x10A5F}, - {0x10AA0, 0x10ABF}, - {0x10AE7, 0x10AEA}, - {0x10AF7, 0x10AFF}, - {0x10B36, 0x10B38}, - {0x10B56, 0x10B57}, - {0x10B73, 0x10B77}, - {0x10B92, 0x10B98}, - {0x10B9D, 0x10BA8}, - {0x10BB0, 0x10BFF}, - {0x10C49, 0x10C7F}, - {0x10CB3, 0x10CBF}, - {0x10CF3, 0x10CF9}, - {0x10D28, 0x10D2F}, - {0x10D3A, 0x10E5F}, - {0x10E7F, 0x10E7F}, - {0x10EAA, 0x10EAA}, - {0x10EAE, 0x10EAF}, - {0x10EB2, 0x10EFC}, - {0x10F28, 0x10F2F}, - {0x10F5A, 0x10F6F}, - {0x10F8A, 0x10FAF}, - {0x10FCC, 0x10FDF}, - {0x10FF7, 0x10FFF}, - {0x1104E, 0x11051}, - {0x11076, 0x1107E}, - {0x110C3, 0x110CC}, - {0x110CE, 0x110CF}, - {0x110E9, 0x110EF}, - {0x110FA, 0x110FF}, - {0x11135, 0x11135}, - {0x11148, 0x1114F}, - {0x11177, 0x1117F}, - {0x111E0, 0x111E0}, - {0x111F5, 0x111FF}, - {0x11212, 0x11212}, - {0x11242, 0x1127F}, - {0x11287, 0x11287}, - {0x11289, 0x11289}, - {0x1128E, 0x1128E}, - {0x1129E, 0x1129E}, - {0x112AA, 0x112AF}, - {0x112EB, 0x112EF}, - {0x112FA, 0x112FF}, - {0x11304, 0x11304}, - {0x1130D, 0x1130E}, - {0x11311, 0x11312}, - {0x11329, 0x11329}, - {0x11331, 0x11331}, - {0x11334, 0x11334}, - {0x1133A, 0x1133A}, - {0x11345, 0x11346}, - {0x11349, 0x1134A}, - {0x1134E, 0x1134F}, - {0x11351, 0x11356}, - {0x11358, 0x1135C}, - {0x11364, 0x11365}, - {0x1136D, 0x1136F}, - {0x11375, 0x113FF}, - {0x1145C, 0x1145C}, - {0x11462, 0x1147F}, - {0x114C8, 0x114CF}, - {0x114DA, 0x1157F}, - {0x115B6, 0x115B7}, - {0x115DE, 0x115FF}, - {0x11645, 0x1164F}, - {0x1165A, 0x1165F}, - {0x1166D, 0x1167F}, - {0x116BA, 0x116BF}, - {0x116CA, 0x116FF}, - {0x1171B, 0x1171C}, - {0x1172C, 0x1172F}, - {0x11747, 0x117FF}, - {0x1183C, 0x1189F}, - {0x118F3, 0x118FE}, - {0x11907, 0x11908}, - {0x1190A, 0x1190B}, - {0x11914, 0x11914}, - {0x11917, 0x11917}, - {0x11936, 0x11936}, - {0x11939, 0x1193A}, - {0x11947, 0x1194F}, - {0x1195A, 0x1199F}, - {0x119A8, 0x119A9}, - {0x119D8, 0x119D9}, - {0x119E5, 0x119FF}, - {0x11A48, 0x11A4F}, - {0x11AA3, 0x11AAF}, - {0x11AF9, 0x11AFF}, - {0x11B0A, 0x11BFF}, - {0x11C09, 0x11C09}, - {0x11C37, 0x11C37}, - {0x11C46, 0x11C4F}, - {0x11C6D, 0x11C6F}, - {0x11C90, 0x11C91}, - {0x11CA8, 0x11CA8}, - {0x11CB7, 0x11CFF}, - {0x11D07, 0x11D07}, - {0x11D0A, 0x11D0A}, - {0x11D37, 0x11D39}, - {0x11D3B, 0x11D3B}, - {0x11D3E, 0x11D3E}, - {0x11D48, 0x11D4F}, - {0x11D5A, 0x11D5F}, - {0x11D66, 0x11D66}, - {0x11D69, 0x11D69}, - {0x11D8F, 0x11D8F}, - {0x11D92, 0x11D92}, - {0x11D99, 0x11D9F}, - {0x11DAA, 0x11EDF}, - {0x11EF9, 0x11EFF}, - {0x11F11, 0x11F11}, - {0x11F3B, 0x11F3D}, - {0x11F5A, 0x11FAF}, - {0x11FB1, 0x11FBF}, - {0x11FF2, 0x11FFE}, - {0x1239A, 0x123FF}, - {0x1246F, 0x1246F}, - {0x12475, 0x1247F}, - {0x12544, 0x12F8F}, - {0x12FF3, 0x12FFF}, - {0x13456, 0x143FF}, - {0x14647, 0x167FF}, - {0x16A39, 0x16A3F}, - {0x16A5F, 0x16A5F}, - {0x16A6A, 0x16A6D}, - {0x16ABF, 0x16ABF}, - {0x16ACA, 0x16ACF}, - {0x16AEE, 0x16AEF}, - {0x16AF6, 0x16AFF}, - {0x16B46, 0x16B4F}, - {0x16B5A, 0x16B5A}, - {0x16B62, 0x16B62}, - {0x16B78, 0x16B7C}, - {0x16B90, 0x16E3F}, - {0x16E9B, 0x16EFF}, - {0x16F4B, 0x16F4E}, - {0x16F88, 0x16F8E}, - {0x16FA0, 0x16FDF}, - {0x16FE5, 0x16FEF}, - {0x16FF2, 0x16FFF}, - {0x17001, 0x187F6}, - {0x187F8, 0x187FF}, - {0x18CD6, 0x18CFF}, - {0x18D01, 0x18D07}, - {0x18D09, 0x1AFEF}, - {0x1AFF4, 0x1AFF4}, - {0x1AFFC, 0x1AFFC}, - {0x1AFFF, 0x1AFFF}, - {0x1B123, 0x1B131}, - {0x1B133, 0x1B14F}, - {0x1B153, 0x1B154}, - {0x1B156, 0x1B163}, - {0x1B168, 0x1B16F}, - {0x1B2FC, 0x1BBFF}, - {0x1BC6B, 0x1BC6F}, - {0x1BC7D, 0x1BC7F}, - {0x1BC89, 0x1BC8F}, - {0x1BC9A, 0x1BC9B}, - {0x1BCA4, 0x1CEFF}, - {0x1CF2E, 0x1CF2F}, - {0x1CF47, 0x1CF4F}, - {0x1CFC4, 0x1CFFF}, - {0x1D0F6, 0x1D0FF}, - {0x1D127, 0x1D128}, - {0x1D1EB, 0x1D1FF}, - {0x1D246, 0x1D2BF}, - {0x1D2D4, 0x1D2DF}, - {0x1D2F4, 0x1D2FF}, - {0x1D357, 0x1D35F}, - {0x1D379, 0x1D3FF}, - {0x1D455, 0x1D455}, - {0x1D49D, 0x1D49D}, - {0x1D4A0, 0x1D4A1}, - {0x1D4A3, 0x1D4A4}, - {0x1D4A7, 0x1D4A8}, - {0x1D4AD, 0x1D4AD}, - {0x1D4BA, 0x1D4BA}, - {0x1D4BC, 0x1D4BC}, - {0x1D4C4, 0x1D4C4}, - {0x1D506, 0x1D506}, - {0x1D50B, 0x1D50C}, - {0x1D515, 0x1D515}, - {0x1D51D, 0x1D51D}, - {0x1D53A, 0x1D53A}, - {0x1D53F, 0x1D53F}, - {0x1D545, 0x1D545}, - {0x1D547, 0x1D549}, - {0x1D551, 0x1D551}, - {0x1D6A6, 0x1D6A7}, - {0x1D7CC, 0x1D7CD}, - {0x1DA8C, 0x1DA9A}, - {0x1DAA0, 0x1DAA0}, - {0x1DAB0, 0x1DEFF}, - {0x1DF1F, 0x1DF24}, - {0x1DF2B, 0x1DFFF}, - {0x1E007, 0x1E007}, - {0x1E019, 0x1E01A}, - {0x1E022, 0x1E022}, - {0x1E025, 0x1E025}, - {0x1E02B, 0x1E02F}, - {0x1E06E, 0x1E08E}, - {0x1E090, 0x1E0FF}, - {0x1E12D, 0x1E12F}, - {0x1E13E, 0x1E13F}, - {0x1E14A, 0x1E14D}, - {0x1E150, 0x1E28F}, - {0x1E2AF, 0x1E2BF}, - {0x1E2FA, 0x1E2FE}, - {0x1E300, 0x1E4CF}, - {0x1E4FA, 0x1E7DF}, - {0x1E7E7, 0x1E7E7}, - {0x1E7EC, 0x1E7EC}, - {0x1E7EF, 0x1E7EF}, - {0x1E7FF, 0x1E7FF}, - {0x1E8C5, 0x1E8C6}, - {0x1E8D7, 0x1E8FF}, - {0x1E94C, 0x1E94F}, - {0x1E95A, 0x1E95D}, - {0x1E960, 0x1EC70}, - {0x1ECB5, 0x1ED00}, - {0x1ED3E, 0x1EDFF}, - {0x1EE04, 0x1EE04}, - {0x1EE20, 0x1EE20}, - {0x1EE23, 0x1EE23}, - {0x1EE25, 0x1EE26}, - {0x1EE28, 0x1EE28}, - {0x1EE33, 0x1EE33}, - {0x1EE38, 0x1EE38}, - {0x1EE3A, 0x1EE3A}, - {0x1EE3C, 0x1EE41}, - {0x1EE43, 0x1EE46}, - {0x1EE48, 0x1EE48}, - {0x1EE4A, 0x1EE4A}, - {0x1EE4C, 0x1EE4C}, - {0x1EE50, 0x1EE50}, - {0x1EE53, 0x1EE53}, - {0x1EE55, 0x1EE56}, - {0x1EE58, 0x1EE58}, - {0x1EE5A, 0x1EE5A}, - {0x1EE5C, 0x1EE5C}, - {0x1EE5E, 0x1EE5E}, - {0x1EE60, 0x1EE60}, - {0x1EE63, 0x1EE63}, - {0x1EE65, 0x1EE66}, - {0x1EE6B, 0x1EE6B}, - {0x1EE73, 0x1EE73}, - {0x1EE78, 0x1EE78}, - {0x1EE7D, 0x1EE7D}, - {0x1EE7F, 0x1EE7F}, - {0x1EE8A, 0x1EE8A}, - {0x1EE9C, 0x1EEA0}, - {0x1EEA4, 0x1EEA4}, - {0x1EEAA, 0x1EEAA}, - {0x1EEBC, 0x1EEEF}, - {0x1EEF2, 0x1EFFF}, - {0x1F02C, 0x1F02F}, - {0x1F094, 0x1F09F}, - {0x1F0AF, 0x1F0B0}, - {0x1F0C0, 0x1F0C0}, - {0x1F0D0, 0x1F0D0}, - {0x1F0F6, 0x1F0FF}, - {0x1F1AE, 0x1F1E5}, - {0x1F203, 0x1F20F}, - {0x1F23C, 0x1F23F}, - {0x1F249, 0x1F24F}, - {0x1F252, 0x1F25F}, - {0x1F266, 0x1F2FF}, - {0x1F6D8, 0x1F6DB}, - {0x1F6ED, 0x1F6EF}, - {0x1F6FD, 0x1F6FF}, - {0x1F777, 0x1F77A}, - {0x1F7DA, 0x1F7DF}, - {0x1F7EC, 0x1F7EF}, - {0x1F7F1, 0x1F7FF}, - {0x1F80C, 0x1F80F}, - {0x1F848, 0x1F84F}, - {0x1F85A, 0x1F85F}, - {0x1F888, 0x1F88F}, - {0x1F8AE, 0x1F8AF}, - {0x1F8B2, 0x1F8FF}, - {0x1FA54, 0x1FA5F}, - {0x1FA6E, 0x1FA6F}, - {0x1FA7D, 0x1FA7F}, - {0x1FA89, 0x1FA8F}, - {0x1FABE, 0x1FABE}, - {0x1FAC6, 0x1FACD}, - {0x1FADC, 0x1FADF}, - {0x1FAE9, 0x1FAEF}, - {0x1FAF9, 0x1FAFF}, - {0x1FB93, 0x1FB93}, - {0x1FBCB, 0x1FBEF}, - {0x1FBFA, 0x1FFFD}, - {0x20001, 0x2A6DE}, - {0x2A6E0, 0x2A6FF}, - {0x2A701, 0x2B738}, - {0x2B73A, 0x2B73F}, - {0x2B741, 0x2B81C}, - {0x2B81E, 0x2B81F}, - {0x2B821, 0x2CEA0}, - {0x2CEA2, 0x2CEAF}, - {0x2CEB1, 0x2EBDF}, - {0x2EBE1, 0x2F7FF}, - {0x2FA1E, 0x2FFFD}, - {0x30001, 0x31349}, - {0x3134B, 0x3134F}, - {0x31351, 0x323AE}, - {0x323B0, 0x3FFFD}, - {0x40000, 0x4FFFD}, - {0x50000, 0x5FFFD}, - {0x60000, 0x6FFFD}, - {0x70000, 0x7FFFD}, - {0x80000, 0x8FFFD}, - {0x90000, 0x9FFFD}, - {0xA0000, 0xAFFFD}, - {0xB0000, 0xBFFFD}, - {0xC0000, 0xCFFFD}, - {0xD0000, 0xDFFFD}, - {0xE0000, 0xE0000}, - {0xE0002, 0xE001F}, - {0xE0080, 0xE00FF}, - {0xE01F0, 0xEFFFD} -}; - -/* Non-characters. */ -static const struct widechar_range widechar_nonchar_table[] = { - {0x0FDD0, 0x0FDEF}, - {0x0FFFE, 0x0FFFF}, - {0x1FFFE, 0x1FFFF}, - {0x2FFFE, 0x2FFFF}, - {0x3FFFE, 0x3FFFF}, - {0x4FFFE, 0x4FFFF}, - {0x5FFFE, 0x5FFFF}, - {0x6FFFE, 0x6FFFF}, - {0x7FFFE, 0x7FFFF}, - {0x8FFFE, 0x8FFFF}, - {0x9FFFE, 0x9FFFF}, - {0xAFFFE, 0xAFFFF}, - {0xBFFFE, 0xBFFFF}, - {0xCFFFE, 0xCFFFF}, - {0xDFFFE, 0xDFFFF}, - {0xEFFFE, 0xEFFFF}, - {0xFFFFE, 0xFFFFF}, - {0x10FFFE, 0x10FFFF} -}; - -/* Characters that were widened from width 1 to 2 in Unicode 9. */ -static const struct widechar_range widechar_widened_table[] = { - {0x0231A, 0x0231B}, - {0x023E9, 0x023EC}, - {0x023F0, 0x023F0}, - {0x023F3, 0x023F3}, - {0x025FD, 0x025FE}, - {0x02614, 0x02615}, - {0x02648, 0x02653}, - {0x0267F, 0x0267F}, - {0x02693, 0x02693}, - {0x026A1, 0x026A1}, - {0x026AA, 0x026AB}, - {0x026BD, 0x026BE}, - {0x026C4, 0x026C5}, - {0x026CE, 0x026CE}, - {0x026D4, 0x026D4}, - {0x026EA, 0x026EA}, - {0x026F2, 0x026F3}, - {0x026F5, 0x026F5}, - {0x026FA, 0x026FA}, - {0x026FD, 0x026FD}, - {0x02705, 0x02705}, - {0x0270A, 0x0270B}, - {0x02728, 0x02728}, - {0x0274C, 0x0274C}, - {0x0274E, 0x0274E}, - {0x02753, 0x02755}, - {0x02757, 0x02757}, - {0x02795, 0x02797}, - {0x027B0, 0x027B0}, - {0x027BF, 0x027BF}, - {0x02B1B, 0x02B1C}, - {0x02B50, 0x02B50}, - {0x02B55, 0x02B55}, - {0x1F004, 0x1F004}, - {0x1F0CF, 0x1F0CF}, - {0x1F18E, 0x1F18E}, - {0x1F191, 0x1F19A}, - {0x1F201, 0x1F201}, - {0x1F21A, 0x1F21A}, - {0x1F22F, 0x1F22F}, - {0x1F232, 0x1F236}, - {0x1F238, 0x1F23A}, - {0x1F250, 0x1F251}, - {0x1F300, 0x1F320}, - {0x1F32D, 0x1F335}, - {0x1F337, 0x1F37C}, - {0x1F37E, 0x1F393}, - {0x1F3A0, 0x1F3CA}, - {0x1F3CF, 0x1F3D3}, - {0x1F3E0, 0x1F3F0}, - {0x1F3F4, 0x1F3F4}, - {0x1F3F8, 0x1F43E}, - {0x1F440, 0x1F440}, - {0x1F442, 0x1F4FC}, - {0x1F4FF, 0x1F53D}, - {0x1F54B, 0x1F54E}, - {0x1F550, 0x1F567}, - {0x1F595, 0x1F596}, - {0x1F5FB, 0x1F64F}, - {0x1F680, 0x1F6C5}, - {0x1F6CC, 0x1F6CC}, - {0x1F6D0, 0x1F6D0}, - {0x1F6EB, 0x1F6EC}, - {0x1F910, 0x1F918}, - {0x1F980, 0x1F984}, - {0x1F9C0, 0x1F9C0} -}; - -template -bool widechar_in_table(const Collection &arr, uint32_t c) { - auto where = std::lower_bound(std::begin(arr), std::end(arr), c, - [](widechar_range p, uint32_t c) { return p.hi < c; }); - return where != std::end(arr) && where->lo <= c; -} - -/* Return the width of character c, or a special negative value. */ -int widechar_wcwidth(uint32_t c) { - if (widechar_in_table(widechar_ascii_table, c)) - return 1; - if (widechar_in_table(widechar_private_table, c)) - return widechar_private_use; - if (widechar_in_table(widechar_nonprint_table, c)) - return widechar_nonprint; - if (widechar_in_table(widechar_nonchar_table, c)) - return widechar_non_character; - if (widechar_in_table(widechar_combining_table, c)) - return widechar_combining; - if (widechar_in_table(widechar_combiningletters_table, c)) - return widechar_combining; - if (widechar_in_table(widechar_doublewide_table, c)) - return 2; - if (widechar_in_table(widechar_ambiguous_table, c)) - return widechar_ambiguous; - if (widechar_in_table(widechar_unassigned_table, c)) - return widechar_unassigned; - if (widechar_in_table(widechar_widened_table, c)) - return widechar_widened_in_9; - return 1; -} - -} // namespace -#endif // WIDECHAR_WIDTH_H diff --git a/src/wildcard.h b/src/wildcard.h deleted file mode 100644 index b278b97f2..000000000 --- a/src/wildcard.h +++ /dev/null @@ -1,26 +0,0 @@ -// My own globbing implementation. Needed to implement this instead of using libs globbing to -// support tab-expansion of globbed parameters. -#ifndef FISH_WILDCARD_H -#define FISH_WILDCARD_H - -#include - -#include "common.h" -#if INCLUDE_RUST_HEADERS -#include "wildcard.rs.h" -#endif - -// Enumeration of all wildcard types. -enum { - /// Character representing any character except '/' (slash). - ANY_CHAR = WILDCARD_RESERVED_BASE, - /// Character representing any character string not containing '/' (slash). - ANY_STRING, - /// Character representing any character string. - ANY_STRING_RECURSIVE, - /// This is a special pseudo-char that is not used other than to mark the - /// end of the the special characters so we can sanity check the enum range. - ANY_SENTINEL -}; - -#endif diff --git a/src/wutil.cpp b/src/wutil.cpp deleted file mode 100644 index 12956a0a5..000000000 --- a/src/wutil.cpp +++ /dev/null @@ -1,709 +0,0 @@ -// Wide character equivalents of various standard unix functions. -#define FISH_NO_ISW_WRAPPERS -#include "config.h" - -#include "wutil.h" // IWYU pragma: keep - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef HAVE_XLOCALE_H -#include -#endif - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "common.h" -#include "fallback.h" // IWYU pragma: keep -#include "fds.h" -#include "flog.h" -#include "wcstringutil.h" - -using cstring = std::string; - -const file_id_t kInvalidFileID{}; - -wcstring_list_ffi_t::~wcstring_list_ffi_t() = default; - -/// Map used as cache by wgettext. -static owning_lock> wgettext_map; - -DIR *wopendir(const wcstring &name) { - const cstring tmp = wcs2zstring(name); - return opendir(tmp.c_str()); -} - -#ifdef HAVE_STRUCT_DIRENT_D_TYPE -static maybe_t dirent_type_to_entry_type(uint8_t dt) { - switch (dt) { - case DT_FIFO: - return dir_entry_type_t::fifo; - case DT_CHR: - return dir_entry_type_t::chr; - case DT_DIR: - return dir_entry_type_t::dir; - case DT_BLK: - return dir_entry_type_t::blk; - case DT_REG: - return dir_entry_type_t::reg; - case DT_LNK: - return dir_entry_type_t::lnk; - case DT_SOCK: - return dir_entry_type_t::sock; -#if defined(DT_WHT) - // OpenBSD doesn't have this one - case DT_WHT: - return dir_entry_type_t::whiteout; -#endif - case DT_UNKNOWN: - default: - return none(); - } -} -#endif - -static maybe_t stat_mode_to_entry_type(mode_t m) { - switch (m & S_IFMT) { - case S_IFIFO: - return dir_entry_type_t::fifo; - case S_IFCHR: - return dir_entry_type_t::chr; - case S_IFDIR: - return dir_entry_type_t::dir; - case S_IFBLK: - return dir_entry_type_t::blk; - case S_IFREG: - return dir_entry_type_t::reg; - case S_IFLNK: - return dir_entry_type_t::lnk; - case S_IFSOCK: - return dir_entry_type_t::sock; -#if defined(S_IFWHT) - case S_IFWHT: - return dir_entry_type_t::whiteout; -#endif - default: - return none(); - } -} - -dir_iter_t::entry_t::entry_t() = default; -dir_iter_t::entry_t::~entry_t() = default; - -void dir_iter_t::entry_t::reset() { - this->name.clear(); - this->inode = {}; - this->type_.reset(); - this->stat_.reset(); -} - -maybe_t dir_iter_t::entry_t::check_type() const { - // Call stat if needed to populate our type, swallowing errors. - if (!this->type_) { - this->do_stat(); - } - return this->type_; -} - -const maybe_t &dir_iter_t::entry_t::stat() const { - if (!stat_) { - (void)this->do_stat(); - } - return stat_; -} - -void dir_iter_t::entry_t::do_stat() const { - // We want to set both our type and our stat buffer. - // If we follow symlinks and stat() errors with a bad symlink, set the type to link, but do not - // populate the stat buffer. - if (this->dirfd_ < 0) { - return; - } - std::string narrow = wcs2zstring(this->name); - struct stat s {}; - if (fstatat(this->dirfd_, narrow.c_str(), &s, 0) == 0) { - this->stat_ = s; - this->type_ = stat_mode_to_entry_type(s.st_mode); - } else { - switch (errno) { - case ELOOP: - this->type_ = dir_entry_type_t::lnk; - break; - - case EACCES: - case EIO: - case ENOENT: - case ENOTDIR: - case ENAMETOOLONG: - case ENODEV: - // These are "expected" errors. - this->type_ = none(); - break; - - default: - this->type_ = none(); - // This used to print an error, but given that we have seen - // both ENODEV (above) and ENOTCONN, - // and that the error isn't actionable and shows up while typing, - // let's not do that. - // wperror(L"fstatat"); - break; - } - } -} - -dir_iter_t::dir_iter_t(const wcstring &path, bool withdot) { - dir_.reset(wopendir(path)); - if (!dir_) { - error_ = errno; - return; - } - withdot_ = withdot; - entry_.dirfd_ = dirfd(&*dir_); -} - -dir_iter_t::dir_iter_t(dir_iter_t &&rhs) { *this = std::move(rhs); } - -dir_iter_t &dir_iter_t::operator=(dir_iter_t &&rhs) { - // Steal the fields; ensure rhs no longer has DIR* and forgets its fd. - this->dir_ = std::move(rhs.dir_); - this->error_ = rhs.error_; - this->entry_ = std::move(rhs.entry_); - rhs.dir_ = nullptr; - rhs.entry_.dirfd_ = -1; - return *this; -} - -void dir_iter_t::rewind() { - if (dir_) { - rewinddir(&*dir_); - } -} - -dir_iter_t::~dir_iter_t() = default; - -const dir_iter_t::entry_t *dir_iter_t::next() { - if (!dir_) { - return nullptr; - } - errno = 0; - struct dirent *dent = readdir(&*dir_); - if (!dent) { - error_ = errno; - return nullptr; - } - // Skip . and .., - // unless we've been told not to. - if (!withdot_ && (!strcmp(dent->d_name, ".") || !strcmp(dent->d_name, ".."))) { - return next(); - } - entry_.reset(); - entry_.name = str2wcstring(dent->d_name); - entry_.inode = dent->d_ino; -#ifdef HAVE_STRUCT_DIRENT_D_TYPE - auto type = dirent_type_to_entry_type(dent->d_type); - // Do not store symlinks as type as we will need to resolve them. - if (type != dir_entry_type_t::lnk) { - entry_.type_ = type; - } else { - entry_.type_ = none(); - } - // This entry could be a link if it is a link or unknown. - if (type.has_value()) { - entry_.possible_link_ = type == dir_entry_type_t::lnk; - } else { - entry_.possible_link_ = none(); - } -#endif - return &entry_; -} - -int wstat(const wcstring &file_name, struct stat *buf) { - const cstring tmp = wcs2zstring(file_name); - return stat(tmp.c_str(), buf); -} - -int lwstat(const wcstring &file_name, struct stat *buf) { - const cstring tmp = wcs2zstring(file_name); - return lstat(tmp.c_str(), buf); -} - -int waccess(const wcstring &file_name, int mode) { - const cstring tmp = wcs2zstring(file_name); - return access(tmp.c_str(), mode); -} - -void wperror(wcharz_t s) { - int e = errno; - if (s.str[0] != L'\0') { - std::fwprintf(stderr, L"%ls: ", s.str); - } - std::fwprintf(stderr, L"%s\n", std::strerror(e)); -} - -int make_fd_blocking(int fd) { - int flags = fcntl(fd, F_GETFL, 0); - int err = 0; - bool nonblocking = flags & O_NONBLOCK; - if (nonblocking) { - err = fcntl(fd, F_SETFL, flags & ~O_NONBLOCK); - } - return err == -1 ? errno : 0; -} - -wcstring normalize_path(const wcstring &path, bool allow_leading_double_slashes) { - // Count the leading slashes. - const wchar_t sep = L'/'; - size_t leading_slashes = 0; - for (wchar_t c : path) { - if (c != sep) break; - leading_slashes++; - } - - std::vector comps = split_string(path, sep); - std::vector new_comps; - for (wcstring &comp : comps) { - if (comp.empty() || comp == L".") { - continue; - } else if (comp != L"..") { - new_comps.push_back(std::move(comp)); - } else if (!new_comps.empty() && new_comps.back() != L"..") { - // '..' with a real path component, drop that path component. - new_comps.pop_back(); - } else if (leading_slashes == 0) { - // We underflowed the .. and are a relative (not absolute) path. - new_comps.push_back(L".."); - } - } - wcstring result = join_strings(new_comps, sep); - // If we don't allow leading double slashes, collapse them to 1 if there are any. - int numslashes = leading_slashes > 0 ? 1 : 0; - // If we do, prepend one or two leading slashes. - // Yes, three+ slashes are collapsed to one. (!) - if (allow_leading_double_slashes && leading_slashes == 2) numslashes = 2; - result.insert(0, numslashes, sep); - // Ensure ./ normalizes to . and not empty. - if (result.empty()) result.push_back(L'.'); - return result; -} - -wcstring path_normalize_for_cd(const wcstring &wd, const wcstring &path) { - // Fast paths. - const wchar_t sep = L'/'; - assert(!wd.empty() && wd.front() == sep && wd.back() == sep && - "Invalid working directory, it must start and end with /"); - if (path.empty()) { - return wd; - } else if (path.front() == sep) { - return path; - } else if (path.front() != L'.') { - return wd + path; - } - - // Split our strings by the sep. - std::vector wd_comps = split_string(wd, sep); - std::vector path_comps = split_string(path, sep); - - // Remove empty segments from wd_comps. - // In particular this removes the leading and trailing empties. - wd_comps.erase(std::remove(wd_comps.begin(), wd_comps.end(), L""), wd_comps.end()); - - // Erase leading . and .. components from path_comps, popping from wd_comps as we go. - size_t erase_count = 0; - for (const wcstring &comp : path_comps) { - bool erase_it = false; - if (comp.empty() || comp == L".") { - erase_it = true; - } else if (comp == L".." && !wd_comps.empty()) { - erase_it = true; - wd_comps.pop_back(); - } - if (erase_it) { - erase_count++; - } else { - break; - } - } - // Append un-erased elements to wd_comps and join them, then prepend the leading /. - std::move(path_comps.begin() + erase_count, path_comps.end(), std::back_inserter(wd_comps)); - wcstring result = join_strings(wd_comps, sep); - result.insert(0, 1, L'/'); - return result; -} - -wcstring wdirname(wcstring path) { - // Do not use system-provided dirname (#7837). - // On Mac it's not thread safe, and will error for paths exceeding PATH_MAX. - // This follows OpenGroup dirname recipe. - // 1: Double-slash stays. - if (path == L"//") return path; - - // 2: All slashes => return slash. - if (!path.empty() && path.find_first_not_of(L'/') == wcstring::npos) return L"/"; - - // 3: Trim trailing slashes. - while (!path.empty() && path.back() == L'/') path.pop_back(); - - // 4: No slashes left => return period. - size_t last_slash = path.rfind(L'/'); - if (last_slash == wcstring::npos) return L"."; - - // 5: Remove trailing non-slashes. - path.erase(last_slash + 1, wcstring::npos); - - // 6: Skip as permitted. - // 7: Remove trailing slashes again. - while (!path.empty() && path.back() == L'/') path.pop_back(); - - // 8: Empty => return slash. - if (path.empty()) path = L"/"; - return path; -} - -wcstring wbasename(wcstring path) { - // This follows OpenGroup basename recipe. - // 1: empty => allowed to return ".". This is what system impls do. - if (path.empty()) return L"."; - - // 2: Skip as permitted. - // 3: All slashes => return slash. - if (!path.empty() && path.find_first_not_of(L'/') == wcstring::npos) return L"/"; - - // 4: Remove trailing slashes. - while (!path.empty() && path.back() == L'/') path.pop_back(); - - // 5: Remove up to and including last slash. - size_t last_slash = path.rfind(L'/'); - if (last_slash != wcstring::npos) path.erase(0, last_slash + 1); - return path; -} - -// Really init wgettext. -static void wgettext_really_init() { - fish_bindtextdomain(PACKAGE_NAME, LOCALEDIR); - fish_textdomain(PACKAGE_NAME); -} - -/// For wgettext: Internal init function. Automatically called when a translation is first -/// requested. -static void wgettext_init_if_necessary() { - static std::once_flag s_wgettext_init{}; - std::call_once(s_wgettext_init, wgettext_really_init); -} - -const wcstring &wgettext(const wchar_t *in) { - // Preserve errno across this since this is often used in printing error messages. - int err = errno; - wcstring key = in; - - wgettext_init_if_necessary(); - auto wmap = wgettext_map.acquire(); - wcstring &val = (*wmap)[key]; - if (val.empty()) { - cstring mbs_in = wcs2zstring(key); - char *out = fish_gettext(mbs_in.c_str()); - val = format_string(L"%s", out); - } - errno = err; - - // The returned string is stored in the map. - // TODO: If we want to shrink the map, this would be a problem. - return val; -} - -const wchar_t *wgettext_ptr(const wchar_t *in) { return wgettext(in).c_str(); } - -int wmkdir(const wcstring &name, int mode) { - cstring name_narrow = wcs2zstring(name); - return mkdir(name_narrow.c_str(), mode); -} - -ssize_t wwrite_to_fd(const wchar_t *input, size_t input_len, int fd) { - // Accumulate data in a local buffer. - char accum[512]; - size_t accumlen{0}; - constexpr size_t maxaccum = sizeof accum / sizeof *accum; - - // Helper to perform a write to 'fd', looping as necessary. - // \return true on success, false on error. - ssize_t total_written = 0; - auto do_write = [fd, &total_written](const char *cursor, size_t remaining) { - while (remaining > 0) { - ssize_t samt = write(fd, cursor, remaining); - if (samt < 0) return false; - total_written += samt; - auto amt = static_cast(samt); - assert(amt <= remaining && "Wrote more than requested"); - remaining -= amt; - cursor += amt; - } - return true; - }; - - // Helper to flush the accumulation buffer. - auto flush_accum = [&] { - if (!do_write(accum, accumlen)) return false; - accumlen = 0; - return true; - }; - - bool success = wcs2string_callback(input, input_len, [&](const char *buff, size_t len) { - if (len + accumlen > maxaccum) { - // We have to flush. - // Note this modifies 'accumlen'. - if (!flush_accum()) return false; - } - if (len + accumlen <= maxaccum) { - // Accumulate more. - memmove(accum + accumlen, buff, len); - accumlen += len; - return true; - } else { - // Too much data to even fit, just write it immediately. - return do_write(buff, len); - } - }); - // Flush any remaining. - if (success) success = flush_accum(); - return success ? total_written : -1; -} - -enum : wint_t { - PUA1_START = 0xE000, - PUA1_END = 0xF900, - PUA2_START = 0xF0000, - PUA2_END = 0xFFFFE, - PUA3_START = 0x100000, - PUA3_END = 0x10FFFE, -}; - -/// Return one if the code point is in a Unicode private use area. -static int fish_is_pua(wint_t wc) { - if (PUA1_START <= wc && wc < PUA1_END) return 1; - if (PUA2_START <= wc && wc < PUA2_END) return 1; - if (PUA3_START <= wc && wc < PUA3_END) return 1; - return 0; -} - -/// We need this because there are too many implementations that don't return the proper answer for -/// some code points. See issue #3050. -int fish_iswalnum(wint_t wc) { - if (fish_reserved_codepoint(wc)) return 0; - if (fish_is_pua(wc)) return 0; - return iswalnum(wc); -} - -/// Convenience variants on fish_wcwswidth(). -/// -/// See fallback.h for the normal definitions. -int fish_wcswidth(const wchar_t *str) { return fish_wcswidth(str, std::wcslen(str)); } - -/// Convenience variants on fish_wcwswidth(). -/// -/// See fallback.h for the normal definitions. -int fish_wcswidth(const wcstring &str) { return fish_wcswidth(str.c_str(), str.size()); } - -/// Like fish_wcstol(), but fails on a value outside the range of an int. -/// -/// This is needed because BSD and GNU implementations differ in several ways that make it really -/// annoying to use them in a portable fashion. -/// -/// The caller doesn't have to zero errno. Sets errno to -1 if the int ends with something other -/// than a digit. Leading whitespace is ignored (per the base wcstol implementation). Trailing -/// whitespace is also ignored. We also treat empty strings and strings containing only whitespace -/// as invalid. -int fish_wcstoi(const wchar_t *str, const wchar_t **endptr, int base) { - while (iswspace(*str)) ++str; // skip leading whitespace - if (!*str) { // this is because some implementations don't handle this sensibly - errno = EINVAL; - if (endptr) *endptr = str; - return 0; - } - - errno = 0; - wchar_t *_endptr; - long result = std::wcstol(str, &_endptr, base); - if (result > INT_MAX) { - result = INT_MAX; - errno = ERANGE; - } else if (result < INT_MIN) { - result = INT_MIN; - errno = ERANGE; - } - while (iswspace(*_endptr)) ++_endptr; // skip trailing whitespace - if (!errno && *_endptr) { - if (_endptr == str) { - errno = EINVAL; - } else { - errno = -1; - } - } - if (endptr) *endptr = _endptr; - return static_cast(result); -} - -/// An enhanced version of wcstol(). -/// -/// This is needed because BSD and GNU implementations differ in several ways that make it really -/// annoying to use them in a portable fashion. -/// -/// The caller doesn't have to zero errno. Sets errno to -1 if the int ends with something other -/// than a digit. Leading whitespace is ignored (per the base wcstol implementation). Trailing -/// whitespace is also ignored. -long fish_wcstol(const wchar_t *str, const wchar_t **endptr, int base) { - while (iswspace(*str)) ++str; // skip leading whitespace - if (!*str) { // this is because some implementations don't handle this sensibly - errno = EINVAL; - if (endptr) *endptr = str; - return 0; - } - - errno = 0; - wchar_t *_endptr; - long result = std::wcstol(str, &_endptr, base); - while (iswspace(*_endptr)) ++_endptr; // skip trailing whitespace - if (!errno && *_endptr) { - if (_endptr == str) { - errno = EINVAL; - } else { - errno = -1; - } - } - if (endptr) *endptr = _endptr; - return result; -} - -/// Like wcstod(), but wcstod() is enormously expensive on some platforms so this tries to have a -/// fast path. -double fish_wcstod(const wchar_t *str, wchar_t **endptr, size_t len) { - // We can ignore the locale because we use LC_NUMERIC=C! - // The "fast path." If we're all ASCII and we fit inline, use strtod(). - char narrow[128]; - size_t len_plus_0 = 1 + len; - auto is_ascii = [](wchar_t c) { return 0 <= c && c <= 127; }; - if (len_plus_0 <= sizeof narrow && std::all_of(str, str + len, is_ascii)) { - // Fast path. Copy the string into a local buffer and run strtod() on it. - std::copy(str, str + len_plus_0, narrow); - char *narrow_endptr = nullptr; - double ret = strtod(narrow, endptr ? &narrow_endptr : nullptr); - if (endptr) { - assert(narrow_endptr && "narrow_endptr should not be null"); - *endptr = const_cast(str + (narrow_endptr - narrow)); - } - return ret; - } - return std::wcstod(str, endptr); -} - -double fish_wcstod(const wchar_t *str, wchar_t **endptr) { - return fish_wcstod(str, endptr, std::wcslen(str)); -} -double fish_wcstod(const wcstring &str, wchar_t **endptr) { - return fish_wcstod(str.c_str(), endptr, str.size()); -} - -file_id_t file_id_t::from_stat(const struct stat &buf) { - file_id_t result = {}; - result.device = buf.st_dev; - result.inode = buf.st_ino; - result.size = buf.st_size; - result.change_seconds = buf.st_ctime; - result.mod_seconds = buf.st_mtime; - -#ifdef HAVE_STRUCT_STAT_ST_CTIME_NSEC - result.change_nanoseconds = buf.st_ctime_nsec; - result.mod_nanoseconds = buf.st_mtime_nsec; -#elif defined(__APPLE__) - result.change_nanoseconds = buf.st_ctimespec.tv_nsec; - result.mod_nanoseconds = buf.st_mtimespec.tv_nsec; -#elif defined(_BSD_SOURCE) || defined(_SVID_SOURCE) || defined(_XOPEN_SOURCE) - result.change_nanoseconds = buf.st_ctim.tv_nsec; - result.mod_nanoseconds = buf.st_mtim.tv_nsec; -#else - result.change_nanoseconds = 0; - result.mod_nanoseconds = 0; -#endif - - return result; -} - -file_id_t file_id_for_fd(int fd) { - file_id_t result = kInvalidFileID; - struct stat buf = {}; - if (fd >= 0 && 0 == fstat(fd, &buf)) { - result = file_id_t::from_stat(buf); - } - return result; -} - -bool file_id_t::operator==(const file_id_t &rhs) const { return this->compare_file_id(rhs) == 0; } - -bool file_id_t::operator!=(const file_id_t &rhs) const { return !(*this == rhs); } - -wcstring file_id_t::dump() const { - using llong = long long; - wcstring result; - append_format(result, L" device: %lld\n", llong(device)); - append_format(result, L" inode: %lld\n", llong(inode)); - append_format(result, L" size: %lld\n", llong(size)); - append_format(result, L" change: %lld\n", llong(change_seconds)); - append_format(result, L"change_nano: %lld\n", llong(change_nanoseconds)); - append_format(result, L" mod: %lld\n", llong(mod_seconds)); - append_format(result, L" mod_nano: %lld", llong(mod_nanoseconds)); - return result; -} - -template -static int compare(T a, T b) { - if (a < b) { - return -1; - } else if (a > b) { - return 1; - } - return 0; -} - -int file_id_t::compare_file_id(const file_id_t &rhs) const { - // Compare each field, stopping when we get to a non-equal field. - int ret = 0; - if (!ret) ret = compare(device, rhs.device); - if (!ret) ret = compare(inode, rhs.inode); - if (!ret) ret = compare(size, rhs.size); - if (!ret) ret = compare(change_seconds, rhs.change_seconds); - if (!ret) ret = compare(change_nanoseconds, rhs.change_nanoseconds); - if (!ret) ret = compare(mod_seconds, rhs.mod_seconds); - if (!ret) ret = compare(mod_nanoseconds, rhs.mod_nanoseconds); - return ret; -} - -bool file_id_t::operator<(const file_id_t &rhs) const { return this->compare_file_id(rhs) < 0; } - -// static -wcstring_list_ffi_t wcstring_list_ffi_t::get_test_data() { - return std::vector{L"foo", L"bar", L"baz"}; -} - -// static -void wcstring_list_ffi_t::check_test_data(wcstring_list_ffi_t data) { - assert(data.size() == 3); - assert(data.at(0) == L"foo"); - assert(data.at(1) == L"bar"); - assert(data.at(2) == L"baz"); -} diff --git a/src/wutil.h b/src/wutil.h deleted file mode 100644 index e923c6ce3..000000000 --- a/src/wutil.h +++ /dev/null @@ -1,309 +0,0 @@ -// Prototypes for wide character equivalents of various standard unix functions. -#ifndef FISH_WUTIL_H -#define FISH_WUTIL_H - -#include "config.h" // IWYU pragma: keep - -#include -#include -#include -#include -#include -#ifdef __APPLE__ -// This include is required on macOS 10.10 for locale_t -#include // IWYU pragma: keep -#endif - -#include -#include -#include -#include -#include -#include - -#include "common.h" -#include "maybe.h" - -/// A POD wrapper around a null-terminated string, for ffi purposes. -/// This trivial type may be converted to and from const wchar_t *. -struct wcharz_t { - const wchar_t *str; - - /* implicit */ wcharz_t(const wchar_t *s) : str(s) { assert(s && "wcharz_t must be non-null"); } - operator const wchar_t *() const { return str; } - operator wcstring() const { return str; } - - inline size_t size() const { return wcslen(str); } - inline size_t length() const { return size(); } -}; - -// A helper type for passing vectors of strings back to Rust. -// This hides the vector so that autocxx doesn't complain about templates. -struct wcstring_list_ffi_t { - std::vector vals{}; - - wcstring_list_ffi_t() = default; - /* implicit */ wcstring_list_ffi_t(std::vector vals) : vals(std::move(vals)) {} - ~wcstring_list_ffi_t(); - - bool empty() const { return vals.empty(); } - size_t size() const { return vals.size(); } - const wcstring &at(size_t idx) const { return vals.at(idx); } - void clear() { vals.clear(); } - - /// Helper to construct one. - static std::unique_ptr create() { - return std::unique_ptr(new wcstring_list_ffi_t()); - } - - /// Append a string. - void push(wcstring s) { vals.push_back(std::move(s)); } - - /// Helper functions used in tests only. - static wcstring_list_ffi_t get_test_data(); - static void check_test_data(wcstring_list_ffi_t data); -}; - -/// Convert an iterable of strings to a list of wcharz_t. -template -std::vector wcstring_list_to_ffi(const T &list) { - std::vector result; - for (const wcstring &str : list) { - result.push_back(str.c_str()); - } - return result; -} - -class autoclose_fd_t; - -/// Wide character version of opendir(). Note that opendir() is guaranteed to set close-on-exec by -/// POSIX (hooray). -DIR *wopendir(const wcstring &name); - -/// Wide character version of stat(). -int wstat(const wcstring &file_name, struct stat *buf); - -/// Wide character version of lstat(). -int lwstat(const wcstring &file_name, struct stat *buf); - -/// Wide character version of access(). -int waccess(const wcstring &file_name, int mode); - -/// Wide character version of perror(). -void wperror(wcharz_t s); - -/// Given an input path, "normalize" it: -/// 1. Collapse multiple /s into a single /, except maybe at the beginning. -/// 2. .. goes up a level. -/// 3. Remove /./ in the middle. -wcstring normalize_path(const wcstring &path, bool allow_leading_double_slashes = true); - -/// Given an input path \p path and a working directory \p wd, do a "normalizing join" in a way -/// appropriate for cd. That is, return effectively wd + path while resolving leading ../s from -/// path. The intent here is to allow 'cd' out of a directory which may no longer exist, without -/// allowing 'cd' into a directory that may not exist; see #5341. -wcstring path_normalize_for_cd(const wcstring &wd, const wcstring &path); - -/// Wide character version of dirname(). -std::wstring wdirname(std::wstring path); - -/// Wide character version of basename(). -std::wstring wbasename(std::wstring path); - -/// Wide character wrapper around the gettext function. For historic reasons, unlike the real -/// gettext function, wgettext takes care of setting the correct domain, etc. using the textdomain -/// and bindtextdomain functions. This should probably be moved out of wgettext, so that wgettext -/// will be nothing more than a wrapper around gettext, like all other functions in this file. -const wcstring &wgettext(const wchar_t *in); -const wchar_t *wgettext_ptr(const wchar_t *in); - -/// Wide character version of mkdir. -int wmkdir(const wcstring &name, int mode); - -/// Write a wide string to a file descriptor. This avoids doing any additional allocation. -/// This does NOT retry on EINTR or EAGAIN, it simply returns. -/// \return -1 on error in which case errno will have been set. In this event, the number of bytes -/// actually written cannot be obtained. -ssize_t wwrite_to_fd(const wchar_t *input, size_t len, int fd); - -/// Variant of above that accepts a wcstring. -inline ssize_t wwrite_to_fd(const wcstring &s, int fd) { - return wwrite_to_fd(s.c_str(), s.size(), fd); -} - -// We need this because there are too many implementations that don't return the proper answer for -// some code points. See issue #3050. -#ifndef FISH_NO_ISW_WRAPPERS -#define iswalnum fish_iswalnum -#endif -int fish_iswalnum(wint_t wc); - -int fish_wcswidth(const wchar_t *str); -int fish_wcswidth(const wcstring &str); - -int fish_wcstoi(const wchar_t *str, const wchar_t **endptr = nullptr, int base = 10); -long fish_wcstol(const wchar_t *str, const wchar_t **endptr = nullptr, int base = 10); -double fish_wcstod(const wchar_t *str, wchar_t **endptr, size_t len); -double fish_wcstod(const wchar_t *str, wchar_t **endptr); -double fish_wcstod(const wcstring &str, wchar_t **endptr); - -/// Class for representing a file's inode. We use this to detect and avoid symlink loops, among -/// other things. While an inode / dev pair is sufficient to distinguish co-existing files, Linux -/// seems to aggressively re-use inodes, so it cannot determine if a file has been deleted (ABA -/// problem). Therefore we include richer information. -struct file_id_t { - dev_t device{static_cast(-1LL)}; - ino_t inode{static_cast(-1LL)}; - uint64_t size{static_cast(-1LL)}; - time_t change_seconds{std::numeric_limits::min()}; - long change_nanoseconds{-1}; - time_t mod_seconds{std::numeric_limits::min()}; - long mod_nanoseconds{-1}; - - constexpr file_id_t() = default; - - bool operator==(const file_id_t &rhs) const; - bool operator!=(const file_id_t &rhs) const; - - // Used to permit these as keys in std::map. - bool operator<(const file_id_t &rhs) const; - - static file_id_t from_stat(const struct stat &buf); - wcstring dump() const; - - private: - int compare_file_id(const file_id_t &rhs) const; -}; - -/// Types of files that may be in a directory. -enum class dir_entry_type_t : uint8_t { - fifo = 1, // FIFO file - chr, // character device - dir, // directory - blk, // block device - reg, // regular file - lnk, // symlink - sock, // socket - whiteout, // whiteout (from BSD) -}; - -/// Class for iterating over a directory, wrapping readdir(). -/// This allows enumerating the contents of a directory, exposing the file type if the filesystem -/// itself exposes that from readdir(). stat() is incurred only if necessary: if the entry is a -/// symlink, or if the caller asks for the stat buffer. -/// Symlinks are followed. -class dir_iter_t : noncopyable_t { - private: - /// Whether this dir_iter considers the "." and ".." filesystem entries. - bool withdot_{false}; - - public: - struct entry_t; - - /// Open a directory at a given path. On failure, \p error() will return the error code. - /// Note opendir is guaranteed to set close-on-exec by POSIX (hooray). - explicit dir_iter_t(const wcstring &path, bool withdot = false); - - /// Advance this iterator. - /// \return a pointer to the entry, or nullptr if the entry is finished, or an error occurred. - /// The returned pointer is only valid until the next call to next(). - const entry_t *next(); - - /// \return the errno value for the last error, or 0 if none. - int error() const { return error_; } - - /// \return if we are valid: successfully opened a directory. - bool valid() const { return dir_ != nullptr; } - - /// \return the underlying file descriptor, or -1 if invalid. - int fd() const { return dir_ ? dirfd(&*dir_) : -1; } - - /// Rewind the directory to the beginning. - void rewind(); - - ~dir_iter_t(); - dir_iter_t(dir_iter_t &&); - dir_iter_t &operator=(dir_iter_t &&); - - /// An entry returned by dir_iter_t. - struct entry_t : noncopyable_t { - /// File name of this entry. - wcstring name{}; - - /// inode of this entry. - ino_t inode{}; - - /// \return the type of this entry if it is already available, otherwise none(). - maybe_t fast_type() const { return type_; } - - /// \return the type of this entry, falling back to stat() if necessary. - /// If stat() fails because the file has disappeared, this will return none(). - /// If stat() fails because of a broken symlink, this will return type lnk. - maybe_t check_type() const; - - /// \return whether this is a directory. This may call stat(). - bool is_dir() const { return check_type() == dir_entry_type_t::dir; } - - /// \return false if we know this can't be a link via d_type, true if it could be. - maybe_t is_possible_link() const { return possible_link_; } - - /// \return the stat buff for this entry, invoking stat() if necessary. - const maybe_t &stat() const; - - private: - // Reset our fields. - void reset(); - - // Populate our stat buffer, and type. Errors are silently ignored. - void do_stat() const; - - // Stat buff for this entry, or none if not yet computed. - mutable maybe_t stat_{}; - - // The type of the entry. This is initially none; it may be populated eagerly via readdir() - // on some filesystems, or later via stat(). If stat() fails, the error is silently ignored - // and the type is left as none(). Note this is an unavoidable race. - mutable maybe_t type_{}; - - /// whether this entry could be a link, false if we know definitively it isn't. - mutable maybe_t possible_link_{}; - - // fd of the DIR*, used for fstatat(). - int dirfd_{-1}; - - entry_t(); - ~entry_t(); - entry_t(entry_t &&) = default; - entry_t &operator=(entry_t &&) = default; - friend class dir_iter_t; - }; - - private: - struct dir_closer_t { - void operator()(DIR *dir) const { (void)closedir(dir); } - }; - std::unique_ptr dir_{nullptr}; - int error_{0}; - entry_t entry_; -}; - -#ifndef HASH_FILE_ID -#define HASH_FILE_ID 1 -namespace std { -template <> -struct hash { - size_t operator()(const file_id_t &f) const { - std::hash hasher1; - std::hash hasher2; - - return hasher1(f.device) ^ hasher2(f.inode); - } -}; -} // namespace std -#endif - -file_id_t file_id_for_fd(int fd); - -extern const file_id_t kInvalidFileID; - -#endif