mirror of
https://github.com/fish-shell/fish-shell.git
synced 2026-06-02 22:21:15 -03:00
Remove FFI code and C++ files
There's a lot more to remove, like - cxx/autocxx - now-unused CMake code - C++ pcre - C++ entry points - remaining mentions of "ffi"
This commit is contained in:
@@ -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.
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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<CxxWString>,
|
||||
is_function: bool,
|
||||
set_cursor_marker: UniquePtr<CxxWString>,
|
||||
has_cursor_marker: bool,
|
||||
}
|
||||
|
||||
struct abbrs_replacement_t {
|
||||
range: SourceRange,
|
||||
text: UniquePtr<CxxWString>,
|
||||
cursor: usize,
|
||||
has_cursor: bool,
|
||||
}
|
||||
|
||||
struct abbreviation_t {
|
||||
key: UniquePtr<CxxWString>,
|
||||
replacement: UniquePtr<CxxWString>,
|
||||
is_regex: bool,
|
||||
}
|
||||
|
||||
extern "Rust" {
|
||||
type GlobalAbbrs<'a>;
|
||||
|
||||
#[cxx_name = "abbrs_list"]
|
||||
fn abbrs_list_ffi() -> Vec<abbreviation_t>;
|
||||
|
||||
#[cxx_name = "abbrs_match"]
|
||||
fn abbrs_match_ffi(token: &CxxWString, position: abbrs_position_t)
|
||||
-> Vec<abbrs_replacer_t>;
|
||||
|
||||
#[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<GlobalAbbrs<'a>>;
|
||||
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<Mutex<AbbreviationSet>> = Lazy::new(|| Mutex::new(Default::default()));
|
||||
|
||||
pub fn with_abbrs<R>(cb: impl FnOnce(&AbbreviationSet) -> R) -> R {
|
||||
@@ -108,16 +35,6 @@ pub enum Position {
|
||||
Anywhere, // expand in any token
|
||||
}
|
||||
|
||||
impl From<abbrs_position_t> 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<WString>,
|
||||
}
|
||||
|
||||
impl From<Replacer> 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<Replacer> {
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn abbrs_match_ffi(token: &CxxWString, position: abbrs_position_t) -> Vec<abbrs_replacer_t> {
|
||||
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<abbreviation_t> {
|
||||
with_abbrs(|set| -> Vec<abbreviation_t> {
|
||||
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<GlobalAbbrs<'a>> {
|
||||
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() {
|
||||
|
||||
1872
fish-rust/src/ast.rs
1872
fish-rust/src/ast.rs
File diff suppressed because it is too large
Load Diff
@@ -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, *},
|
||||
|
||||
@@ -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<c_int> {
|
||||
EnvStackSetResult::ENV_SCOPE => 2,
|
||||
EnvStackSetResult::ENV_INVALID => 3,
|
||||
EnvStackSetResult::ENV_NOT_FOUND => 4,
|
||||
_ => panic!(),
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -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<c_int> {
|
||||
let mut zstrings = vec![];
|
||||
for arg in args {
|
||||
let mut zstring: Vec<char> = 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())
|
||||
}
|
||||
|
||||
@@ -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};
|
||||
|
||||
@@ -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<CxxWString>;
|
||||
|
||||
#[cxx_name = "escape_string_script"]
|
||||
fn escape_string_script_ffi(
|
||||
input: *const wchar_t,
|
||||
len: usize,
|
||||
flags: u32,
|
||||
) -> UniquePtr<CxxWString>;
|
||||
|
||||
#[cxx_name = "escape_string_url"]
|
||||
fn escape_string_url_ffi(input: *const wchar_t, len: usize) -> UniquePtr<CxxWString>;
|
||||
|
||||
#[cxx_name = "escape_string_var"]
|
||||
fn escape_string_var_ffi(input: *const wchar_t, len: usize) -> UniquePtr<CxxWString>;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
fn unescape_string_ffi(
|
||||
input: *const ffi::wchar_t,
|
||||
len: usize,
|
||||
escape_special: u32,
|
||||
style: ffi::escape_string_style_t,
|
||||
) -> UniquePtr<CxxWString> {
|
||||
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<CxxWString> {
|
||||
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<CxxWString> {
|
||||
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<CxxWString> {
|
||||
let input = unsafe { slice::from_raw_parts(input, len) };
|
||||
escape_string_url(wstr::from_slice(input).unwrap()).to_ffi()
|
||||
}
|
||||
|
||||
@@ -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<WString> {
|
||||
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<Completion>;
|
||||
fn new_completion_with(
|
||||
completion: &CxxWString,
|
||||
description: &CxxWString,
|
||||
flags: u8,
|
||||
) -> Box<Completion>;
|
||||
fn completion(self: &Completion) -> UniquePtr<CxxWString>;
|
||||
fn description(self: &Completion) -> UniquePtr<CxxWString>;
|
||||
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<Completion>;
|
||||
|
||||
fn new_completion_list() -> Box<CompletionListFfi>;
|
||||
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<wcstring_list_ffi_t>,
|
||||
) -> Box<CompletionListFfi>;
|
||||
#[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<crate::ffi::wcstring_list_ffi_t>,
|
||||
) -> Box<CompletionListFfi> {
|
||||
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<Completion> {
|
||||
Box::new(Completion::new(
|
||||
"".into(),
|
||||
"".into(),
|
||||
StringFuzzyMatch::exact_match(),
|
||||
CompleteFlags::default(),
|
||||
))
|
||||
}
|
||||
fn new_completion_with(
|
||||
completion: &CxxWString,
|
||||
description: &CxxWString,
|
||||
flags: u8,
|
||||
) -> Box<Completion> {
|
||||
Box::new(Completion::new(
|
||||
completion.from_ffi(),
|
||||
description.from_ffi(),
|
||||
StringFuzzyMatch::exact_match(),
|
||||
CompleteFlags::from_bits(flags).unwrap(),
|
||||
))
|
||||
}
|
||||
fn new_completion_list() -> Box<CompletionListFfi> {
|
||||
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<CxxWString> {
|
||||
self.completion.to_ffi()
|
||||
}
|
||||
fn description(&self) -> UniquePtr<CxxWString> {
|
||||
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<Completion> {
|
||||
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)
|
||||
}
|
||||
|
||||
@@ -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<Edit>;
|
||||
#[cxx_name = "apply_edit"]
|
||||
fn apply_edit_ffi(
|
||||
target: &CxxWString,
|
||||
mut colors: Pin<&mut HighlightSpecListFFI>,
|
||||
edit: Box<Edit>,
|
||||
) -> UniquePtr<CxxWString>;
|
||||
}
|
||||
extern "Rust" {
|
||||
type UndoHistory;
|
||||
}
|
||||
extern "Rust" {
|
||||
type EditableLine;
|
||||
fn new_editable_line() -> Box<EditableLine>;
|
||||
fn empty(&self) -> bool;
|
||||
#[cxx_name = "text"]
|
||||
fn text_ffi(&self) -> UniquePtr<CxxWString>;
|
||||
#[cxx_name = "clone"]
|
||||
fn clone_ffi(&self) -> Box<EditableLine>;
|
||||
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<Edit>, 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<Edit> {
|
||||
Box::new(Edit::new(start..end, replacement.from_ffi()))
|
||||
}
|
||||
fn new_editable_line() -> Box<EditableLine> {
|
||||
Box::default()
|
||||
}
|
||||
impl EditableLine {
|
||||
fn empty(&self) -> bool {
|
||||
self.is_empty()
|
||||
}
|
||||
fn text_ffi(&self) -> UniquePtr<CxxWString> {
|
||||
self.text().to_ffi()
|
||||
}
|
||||
fn clone_ffi(&self) -> Box<Self> {
|
||||
Box::new(self.clone())
|
||||
}
|
||||
fn size(&self) -> usize {
|
||||
self.len()
|
||||
}
|
||||
#[allow(clippy::boxed_local)]
|
||||
fn push_edit_ffi(&mut self, edit: Box<Edit>, 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<Edit>,
|
||||
) -> UniquePtr<CxxWString> {
|
||||
let mut target = target.from_ffi();
|
||||
apply_edit(&mut target, &mut colors.0, &edit);
|
||||
target.to_ffi()
|
||||
}
|
||||
|
||||
437
fish-rust/src/env/env_ffi.rs
vendored
437
fish-rust/src/env/env_ffi.rs
vendored
@@ -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<EnvStackSetResult> 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<CxxWString>;
|
||||
|
||||
#[cxx_name = "as_list"]
|
||||
fn as_list_ffi(&self) -> UniquePtr<wcstring_list_ffi_t>;
|
||||
|
||||
#[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<EnvVar>;
|
||||
|
||||
#[cxx_name = "env_var_create"]
|
||||
fn env_var_create_ffi(vals: &wcstring_list_ffi_t, flags: u8) -> Box<EnvVar>;
|
||||
|
||||
#[cxx_name = "env_var_create_from_name"]
|
||||
fn env_var_create_from_name_ffi(
|
||||
name: wcharz_t,
|
||||
values: &wcstring_list_ffi_t,
|
||||
) -> Box<EnvVar>;
|
||||
}
|
||||
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<EnvNull>;
|
||||
}
|
||||
|
||||
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<Statuses>;
|
||||
|
||||
#[cxx_name = "get_pipestatus"]
|
||||
fn get_pipestatus_ffi(&self) -> &Vec<i32>;
|
||||
|
||||
#[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<Statuses>;
|
||||
fn set_last_statuses(&self, status: i32, kill_signal: i32, pipestatus: &CxxVector<i32>);
|
||||
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<CxxWString>;
|
||||
fn set_pwd_from_getcwd(&self);
|
||||
|
||||
fn push(&self, new_scope: bool);
|
||||
fn pop(&self);
|
||||
|
||||
fn snapshot(&self) -> Box<EnvDynFFI>;
|
||||
|
||||
// Access a variable stack that only represents globals.
|
||||
// Do not push or pop from this.
|
||||
fn env_get_globals_ffi() -> Box<EnvStackRefFFI>;
|
||||
|
||||
// Access the principal variable stack.
|
||||
fn env_get_principal_ffi() -> Box<EnvStackRefFFI>;
|
||||
}
|
||||
|
||||
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<CxxWString> {
|
||||
self.as_string().to_ffi()
|
||||
}
|
||||
|
||||
pub fn as_list_ffi(&self) -> UniquePtr<wcstring_list_ffi_t> {
|
||||
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<Self> {
|
||||
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<EnvVar> {
|
||||
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<EnvVar> {
|
||||
Box::new(EnvVar::new_from_name_vec(name.as_wstr(), values.from_ffi()))
|
||||
}
|
||||
|
||||
fn env_null_create_ffi() -> Box<EnvNull> {
|
||||
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<CxxWString> {
|
||||
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<Statuses> {
|
||||
Box::new(self.0.get_last_statuses())
|
||||
}
|
||||
|
||||
fn set_last_statuses(&self, status: i32, kill_signal: i32, pipestatus: &CxxVector<i32>) {
|
||||
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<EnvDynFFI> {
|
||||
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<Statuses> {
|
||||
Box::new(Statuses::just(s))
|
||||
}
|
||||
impl Statuses {
|
||||
fn get_status_ffi(&self) -> i32 {
|
||||
self.status
|
||||
}
|
||||
|
||||
fn get_pipestatus_ffi(&self) -> &Vec<i32> {
|
||||
&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<EnvStackRefFFI> {
|
||||
Box::new(EnvStackRefFFI(EnvStack::globals().clone()))
|
||||
}
|
||||
|
||||
fn env_get_principal_ffi() -> Box<EnvStackRefFFI> {
|
||||
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<T: Environment> 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()
|
||||
}
|
||||
30
fish-rust/src/env/environment.rs
vendored
30
fish-rust/src/env/environment.rs
vendored
@@ -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<EnvStackSetResult> 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.
|
||||
|
||||
2
fish-rust/src/env/mod.rs
vendored
2
fish-rust/src/env/mod.rs
vendored
@@ -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},
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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<CxxWString>,
|
||||
}
|
||||
|
||||
extern "Rust" {
|
||||
type EventHandler;
|
||||
type Event;
|
||||
|
||||
fn new_event_generic(desc: wcharz_t) -> Box<Event>;
|
||||
fn new_event_variable_erase(name: &CxxWString) -> Box<Event>;
|
||||
fn new_event_variable_set(name: &CxxWString) -> Box<Event>;
|
||||
fn new_event_process_exit(pid: i32, status: i32) -> Box<Event>;
|
||||
fn new_event_job_exit(pgid: i32, jid: u64) -> Box<Event>;
|
||||
fn new_event_caller_exit(internal_job_id: u64, job_id: i32) -> Box<Event>;
|
||||
#[cxx_name = "clone"]
|
||||
fn clone_ffi(self: &Event) -> Box<Event>;
|
||||
|
||||
#[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<event_description_t>;
|
||||
|
||||
fn desc(self: &EventHandler) -> event_description_t;
|
||||
fn function_name(self: &EventHandler) -> UniquePtr<CxxWString>;
|
||||
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<Arc<EventHandler>>;
|
||||
|
||||
impl EventHandler {
|
||||
fn desc(&self) -> event_description_t {
|
||||
(&self.desc).into()
|
||||
}
|
||||
fn function_name(self: &EventHandler) -> UniquePtr<CxxWString> {
|
||||
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<Event> {
|
||||
Box::new(Event::generic(desc.into()))
|
||||
}
|
||||
|
||||
fn new_event_variable_erase(name: &CxxWString) -> Box<Event> {
|
||||
Box::new(Event::variable_erase(name.from_ffi()))
|
||||
}
|
||||
|
||||
fn new_event_variable_set(name: &CxxWString) -> Box<Event> {
|
||||
Box::new(Event::variable_set(name.from_ffi()))
|
||||
}
|
||||
|
||||
fn new_event_process_exit(pid: i32, status: i32) -> Box<Event> {
|
||||
Box::new(Event::process_exit(pid, status))
|
||||
}
|
||||
|
||||
fn new_event_job_exit(pgid: i32, jid: u64) -> Box<Event> {
|
||||
Box::new(Event::job_exit(pgid, jid))
|
||||
}
|
||||
|
||||
fn new_event_caller_exit(internal_job_id: u64, job_id: i32) -> Box<Event> {
|
||||
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<Event> {
|
||||
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<event_description_t> {
|
||||
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<WString>) {
|
||||
fire(
|
||||
@@ -883,7 +692,3 @@ pub fn fire_generic(parser: &Parser, name: WString, arguments: Vec<WString>) {
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
fn event_fire_generic_ffi(parser: &Parser, name: &CxxWString, arguments: &wcstring_list_ffi_t) {
|
||||
fire_generic(parser, name.from_ffi(), arguments.from_ffi());
|
||||
}
|
||||
|
||||
@@ -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<wcstring_list_ffi_t>,
|
||||
apply_exit_status: bool,
|
||||
) -> i32;
|
||||
}
|
||||
}
|
||||
|
||||
fn exec_subshell_ffi(
|
||||
cmd: &CxxWString,
|
||||
parser: &Parser,
|
||||
outputs: &mut UniquePtr<wcstring_list_ffi_t>,
|
||||
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
|
||||
}
|
||||
|
||||
@@ -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<CxxWString>;
|
||||
}
|
||||
#[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<CxxWString> {
|
||||
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,
|
||||
}
|
||||
|
||||
@@ -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<FdMonitorItem>;
|
||||
}
|
||||
|
||||
extern "Rust" {
|
||||
#[cxx_name = "fd_monitor_t"]
|
||||
type FdMonitor;
|
||||
|
||||
#[cxx_name = "make_fd_monitor_t"]
|
||||
fn new_fd_monitor_ffi() -> Box<FdMonitor>;
|
||||
|
||||
#[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<FdMonitorItem>) -> 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<dyn Fn(&mut AutoCloseFd, ItemWakeReason) + Send + Sync>;
|
||||
|
||||
/// 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<FdMonitor> {
|
||||
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<FdMonitorItem> {
|
||||
// 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<FdMonitorItem>) -> 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 {
|
||||
|
||||
@@ -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<fd_readable_set_t>;
|
||||
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<fd_readable_set_t> {
|
||||
Box::new(fd_readable_set_t::new())
|
||||
|
||||
@@ -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<AutoCloseFd>;
|
||||
#[cxx_name = "valid"]
|
||||
fn is_valid(&self) -> bool;
|
||||
fn close(&mut self);
|
||||
fn fd(&self) -> i32;
|
||||
}
|
||||
}
|
||||
fn new_autoclose_fd(fd: i32) -> Box<AutoCloseFd> {
|
||||
Box::new(AutoCloseFd::new(fd))
|
||||
}
|
||||
|
||||
@@ -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<wcharz_t> 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<wcharz_t> 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<WString>.
|
||||
impl From<&wcstring_list_ffi_t> for Vec<wchar::WString> {
|
||||
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<void_ptr> for *const u8 {
|
||||
fn from(value: void_ptr) -> Self {
|
||||
value.0 as *const _
|
||||
}
|
||||
}
|
||||
|
||||
impl core::convert::From<void_ptr> for *const core::ffi::c_void {
|
||||
fn from(value: void_ptr) -> Self {
|
||||
value.0 as *const _
|
||||
}
|
||||
}
|
||||
|
||||
impl core::convert::From<void_ptr> for *const autocxx::c_void {
|
||||
fn from(value: void_ptr) -> Self {
|
||||
value.0 as *const _
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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!(),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<FunctionProperties>);
|
||||
|
||||
impl FunctionPropertiesRefFFI {
|
||||
fn definition_file(&self) -> UniquePtr<CxxWString> {
|
||||
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<wcstring_list_ffi_t> {
|
||||
self.0.named_arguments.to_ffi()
|
||||
}
|
||||
|
||||
fn get_description(&self) -> UniquePtr<CxxWString> {
|
||||
self.0.description.to_ffi()
|
||||
}
|
||||
|
||||
fn annotated_definition(&self, name: &CxxWString) -> UniquePtr<CxxWString> {
|
||||
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<CxxWString> {
|
||||
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<FunctionPropertiesRefFFI>) {
|
||||
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<CxxWString>;
|
||||
fn definition_lineno(&self) -> i32;
|
||||
fn copy_definition_lineno(&self) -> i32;
|
||||
fn shadow_scope(&self) -> bool;
|
||||
fn named_arguments(&self) -> UniquePtr<wcstring_list_ffi_t>;
|
||||
fn get_description(&self) -> UniquePtr<CxxWString>;
|
||||
fn annotated_definition(&self, name: &CxxWString) -> UniquePtr<CxxWString>;
|
||||
fn is_autoload(&self) -> bool;
|
||||
fn is_copy(&self) -> bool;
|
||||
|
||||
#[cxx_name = "copy_definition_file"]
|
||||
fn copy_definition_file_ffi(&self) -> UniquePtr<CxxWString>;
|
||||
|
||||
/// 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<ParsedSourceRefFFI>::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<FunctionPropertiesRefFFI>);
|
||||
|
||||
#[cxx_name = "function_remove"]
|
||||
fn function_remove_ffi(name: &CxxWString);
|
||||
|
||||
/// Returns a Box<FunctionPropertiesRefFFI>::into_raw(), or nullptr if None.
|
||||
#[cxx_name = "function_get_props_raw"]
|
||||
fn function_get_props_ffi(name: &CxxWString) -> *mut FunctionPropertiesRefFFI;
|
||||
|
||||
/// Returns a Box<FunctionPropertiesRefFFI>::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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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<HighlightSpec>;
|
||||
fn new_highlight_spec() -> Box<HighlightSpec>;
|
||||
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<HighlightColorResolver>;
|
||||
#[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<HighlightSpecListFFI>;
|
||||
fn highlight_shell_ffi(
|
||||
bff: &CxxWString,
|
||||
ctx: &OperationContext<'_>,
|
||||
io_ok: bool,
|
||||
cursor: SharedPtr<usize>,
|
||||
) -> Box<HighlightSpecListFFI>;
|
||||
fn size(&self) -> usize;
|
||||
fn at(&self, index: usize) -> &HighlightSpec;
|
||||
#[cxx_name = "colorize"]
|
||||
fn colorize_ffi(
|
||||
text: &CxxWString,
|
||||
colors: &HighlightSpecListFFI,
|
||||
vars: &EnvStackRefFFI,
|
||||
) -> Vec<u8>;
|
||||
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<u8> {
|
||||
colorize(text.as_wstr(), &colors.0, &*vars.0)
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct HighlightSpecListFFI(pub Vec<HighlightSpec>);
|
||||
|
||||
unsafe impl cxx::ExternType for HighlightSpecListFFI {
|
||||
type Id = cxx::type_id!("HighlightSpecListFFI");
|
||||
type Kind = cxx::kind::Opaque;
|
||||
}
|
||||
|
||||
fn new_highlight_spec_list() -> Box<HighlightSpecListFFI> {
|
||||
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<usize>,
|
||||
) -> Box<HighlightSpecListFFI> {
|
||||
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<HighlightColorResolver> {
|
||||
Box::new(HighlightColorResolver::new())
|
||||
}
|
||||
impl HighlightSpec {
|
||||
fn clone_ffi(&self) -> Box<HighlightSpec> {
|
||||
Box::new(*self)
|
||||
}
|
||||
}
|
||||
fn new_highlight_spec() -> Box<HighlightSpec> {
|
||||
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,
|
||||
}
|
||||
|
||||
@@ -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<CxxWString>;
|
||||
fn rust_expand_and_detect_paths(
|
||||
paths: &wcstring_list_ffi_t,
|
||||
vars: &EnvStackRefFFI,
|
||||
) -> UniquePtr<wcstring_list_ffi_t>;
|
||||
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<CxxWString>;
|
||||
}
|
||||
|
||||
extern "Rust" {
|
||||
type HistoryItem;
|
||||
|
||||
fn rust_history_item_new(
|
||||
s: &CxxWString,
|
||||
when: i64,
|
||||
ident: u64,
|
||||
persist_mode: PersistenceMode,
|
||||
) -> Box<HistoryItem>;
|
||||
#[rust_name = "str_ffi"]
|
||||
fn str(&self) -> UniquePtr<CxxWString>;
|
||||
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<wcstring_list_ffi_t>;
|
||||
#[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<HistorySharedPtr>;
|
||||
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<wcstring_list_ffi_t>;
|
||||
fn items_at_indexes(&self, indexes: &[isize]) -> Box<ItemIndexes>;
|
||||
fn item_at_index(&self, idx: usize) -> Box<HistoryItem>;
|
||||
fn size(&self) -> usize;
|
||||
fn clone(&self) -> Box<HistorySharedPtr>;
|
||||
}
|
||||
|
||||
extern "Rust" {
|
||||
type HistorySearch;
|
||||
fn rust_history_search_new(
|
||||
hist: &HistorySharedPtr,
|
||||
s: &CxxWString,
|
||||
search_type: SearchType,
|
||||
flags: u32,
|
||||
starting_index: usize,
|
||||
) -> Box<HistorySearch>;
|
||||
#[rust_name = "original_term_ffi"]
|
||||
fn original_term(&self) -> UniquePtr<CxxWString>;
|
||||
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<CxxWString>;
|
||||
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<CxxWString> {
|
||||
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<wcstring_list_ffi_t> {
|
||||
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<WString>) {
|
||||
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<CxxWString> {
|
||||
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<CxxWString> {
|
||||
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<P: IntoIterator<Item = WString>>(
|
||||
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<usize, WString>);
|
||||
|
||||
impl ItemIndexes {
|
||||
fn get(&self, index: usize) -> UniquePtr<CxxWString> {
|
||||
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<HistoryItem> {
|
||||
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<History>);
|
||||
|
||||
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<CxxWString>,
|
||||
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<wcstring_list_ffi_t> {
|
||||
self.0.get_history().to_ffi()
|
||||
}
|
||||
fn items_at_indexes(&self, indexes: &[isize]) -> Box<ItemIndexes> {
|
||||
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<HistoryItem> {
|
||||
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<Self> {
|
||||
Box::new(Self(Arc::clone(&self.0)))
|
||||
}
|
||||
}
|
||||
|
||||
fn history_with_name(name: &CxxWString) -> Box<HistorySharedPtr> {
|
||||
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<HistorySearch> {
|
||||
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<CxxWString> {
|
||||
history_session_id(&*vars.0).to_ffi()
|
||||
}
|
||||
|
||||
fn rust_expand_and_detect_paths(
|
||||
paths: &wcstring_list_ffi_t,
|
||||
vars: &EnvStackRefFFI,
|
||||
) -> UniquePtr<wcstring_list_ffi_t> {
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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)]
|
||||
|
||||
@@ -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<CharEvent> {
|
||||
Box::new(CharEvent::from_readline(cmd))
|
||||
}
|
||||
|
||||
fn char_event_from_char_ffi(c: u8) -> Box<CharEvent> {
|
||||
Box::new(CharEvent::from_char(c.into()))
|
||||
}
|
||||
|
||||
fn make_inputter_ffi(parser: &ParserRefFFI, in_fd: i32) -> Box<Inputter> {
|
||||
Box::new(Inputter::new(parser.0.clone(), in_fd))
|
||||
}
|
||||
|
||||
fn make_input_event_queue_ffi(in_fd: i32) -> Box<InputEventQueue> {
|
||||
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<CharEvent>) {
|
||||
self.queue_char(*ch);
|
||||
}
|
||||
|
||||
fn read_char_ffi(&mut self, command_handler: &cxx::SharedPtr<CppCallback>) -> Box<CharEvent> {
|
||||
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<CharEvent>::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<CharEvent>;
|
||||
|
||||
#[cxx_name = "char_event_from_char"]
|
||||
fn char_event_from_char_ffi(c: u8) -> Box<CharEvent>;
|
||||
|
||||
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<Inputter>;
|
||||
|
||||
#[cxx_name = "queue_char"]
|
||||
fn queue_char_ffi(&mut self, ch: Box<CharEvent>);
|
||||
|
||||
fn queue_readline(&mut self, cmd: ReadlineCmd);
|
||||
|
||||
#[cxx_name = "read_char"]
|
||||
fn read_char_ffi(&mut self, command_handler: &SharedPtr<CppCallback>) -> Box<CharEvent>;
|
||||
|
||||
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<InputEventQueue>;
|
||||
|
||||
#[cxx_name = "readch_timed_esc"]
|
||||
fn readch_timed_esc_ffi(&mut self) -> *mut CharEvent;
|
||||
}
|
||||
}
|
||||
@@ -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<IoBuffer>, fd: AutoCloseFd) {
|
||||
#[derive(Clone, Default)]
|
||||
pub struct IoChain(pub Vec<IoDataRef>);
|
||||
|
||||
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<JobGroupRef>,
|
||||
}
|
||||
|
||||
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<IoChain>;
|
||||
|
||||
#[cxx_name = "out"]
|
||||
unsafe fn out_ffi<'a>(self: &'a mut IoStreams<'a>) -> Box<OutputStreamFfi<'a>>;
|
||||
#[cxx_name = "err"]
|
||||
unsafe fn err_ffi<'a>(self: &'a mut IoStreams<'a>) -> Box<OutputStreamFfi<'a>>;
|
||||
#[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<OutputStreamFfi<'a>> {
|
||||
Box::new(OutputStreamFfi(self.out))
|
||||
}
|
||||
fn err_ffi(&'a mut self) -> Box<OutputStreamFfi<'a>> {
|
||||
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<IoChain> {
|
||||
Box::new(IoChain::new())
|
||||
}
|
||||
|
||||
@@ -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<CxxWString>;
|
||||
#[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<pgid_t>;
|
||||
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<CxxWString> {
|
||||
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<Signal> {
|
||||
}
|
||||
}
|
||||
|
||||
/// 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<libc::pid_t> {
|
||||
*self.pgid.borrow()
|
||||
}
|
||||
|
||||
/// Returns the value of [`JobGroup::pgid`] in a `UniquePtr<T>` to take the place of an
|
||||
/// `Option<T>` for ffi purposes. A null `UniquePtr` is equivalent to `None`.
|
||||
pub fn get_pgid_ffi(&self) -> cxx::UniquePtr<pgid_t> {
|
||||
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::<libc::termios>(),
|
||||
"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::<libc::termios>(),
|
||||
"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.
|
||||
|
||||
@@ -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<CxxWString>;
|
||||
#[cxx_name = "kill_yank"]
|
||||
fn kill_yank_ffi() -> UniquePtr<CxxWString>;
|
||||
}
|
||||
}
|
||||
|
||||
struct KillRing(VecDeque<WString>);
|
||||
|
||||
@@ -104,22 +82,6 @@ pub fn kill_entries() -> Vec<WString> {
|
||||
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<CxxWString> {
|
||||
kill_yank().to_ffi()
|
||||
}
|
||||
|
||||
fn kill_yank_rotate_ffi() -> UniquePtr<CxxWString> {
|
||||
kill_yank_rotate().to_ffi()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
fn test_killring() {
|
||||
let mut kr = KillRing::new();
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -139,15 +139,6 @@ pub fn null_terminated_array_length<T>(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()];
|
||||
|
||||
@@ -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<OperationContext<'static>>;
|
||||
fn operation_context_globals() -> Box<OperationContext<'static>>;
|
||||
fn check_cancel(&self) -> bool;
|
||||
|
||||
#[cxx_name = "get_bg_context"]
|
||||
unsafe fn get_bg_context_ffi(
|
||||
env: &EnvDynFFI,
|
||||
generation_count: u32,
|
||||
) -> Box<OperationContext<'_>>;
|
||||
#[cxx_name = "parser_context"]
|
||||
fn parser_context_ffi(parser: &Parser) -> Box<OperationContext<'static>>;
|
||||
}
|
||||
}
|
||||
|
||||
fn get_bg_context_ffi(env: &EnvDynFFI, generation_count: u32) -> Box<OperationContext<'_>> {
|
||||
Box::new(get_bg_context(&env.0, generation_count))
|
||||
}
|
||||
|
||||
fn parser_context_ffi(parser: &Parser) -> Box<OperationContext<'static>> {
|
||||
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<OperationContext<'static>> {
|
||||
Box::new(OperationContext::empty())
|
||||
}
|
||||
fn operation_context_globals() -> Box<OperationContext<'static>> {
|
||||
Box::new(OperationContext::globals())
|
||||
}
|
||||
|
||||
@@ -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<Outputter> {
|
||||
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<Outputter>;
|
||||
|
||||
#[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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<PagerComp> {
|
||||
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<PageRendering>;
|
||||
fn remaining_to_disclose(&self) -> usize;
|
||||
}
|
||||
extern "Rust" {
|
||||
type Pager;
|
||||
fn new_pager() -> Box<Pager>;
|
||||
fn clear(&mut self);
|
||||
fn cursor_position(&self) -> usize;
|
||||
fn empty(&self) -> bool;
|
||||
fn extra_progress_text(&self) -> UniquePtr<CxxWString>;
|
||||
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<PageRendering> {
|
||||
Box::default()
|
||||
}
|
||||
impl PageRendering {
|
||||
fn remaining_to_disclose(&self) -> usize {
|
||||
self.remaining_to_disclose
|
||||
}
|
||||
}
|
||||
fn new_pager() -> Box<Pager> {
|
||||
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<CxxWString> {
|
||||
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,
|
||||
}
|
||||
|
||||
@@ -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<CxxWString>,
|
||||
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<CxxWString>;
|
||||
|
||||
#[cxx_name = "describe"]
|
||||
fn describe_ffi(
|
||||
self: &ParseError,
|
||||
src: &CxxWString,
|
||||
is_interactive: bool,
|
||||
) -> UniquePtr<CxxWString>;
|
||||
#[cxx_name = "describe_with_prefix"]
|
||||
fn describe_with_prefix_ffi(
|
||||
self: &ParseError,
|
||||
src: &CxxWString,
|
||||
prefix: &CxxWString,
|
||||
is_interactive: bool,
|
||||
skip_caret: bool,
|
||||
) -> UniquePtr<CxxWString>;
|
||||
|
||||
fn describe_with_prefix(
|
||||
self: &parse_error_t,
|
||||
src: &CxxWString,
|
||||
prefix: &CxxWString,
|
||||
is_interactive: bool,
|
||||
skip_caret: bool,
|
||||
) -> UniquePtr<CxxWString>;
|
||||
|
||||
type ParseErrorListFfi;
|
||||
fn new_parse_error_list() -> Box<ParseErrorListFfi>;
|
||||
#[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<SourceRange> for std::ops::Range<usize> {
|
||||
@@ -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<CxxWString> {
|
||||
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<CxxWString> {
|
||||
self.text.to_ffi()
|
||||
}
|
||||
|
||||
fn describe_ffi(
|
||||
self: &ParseError,
|
||||
src: &CxxWString,
|
||||
is_interactive: bool,
|
||||
) -> UniquePtr<CxxWString> {
|
||||
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<CxxWString> {
|
||||
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<ParseError>;
|
||||
|
||||
#[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<ParseErrorListFfi> {
|
||||
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;
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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<CxxWString>,
|
||||
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<ParsedSourceRef>);
|
||||
|
||||
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<ParsedSourceRefFFI>;
|
||||
fn has_value(&self) -> bool;
|
||||
fn new_parsed_source_ref(src: &CxxWString, ast: Pin<&mut Ast>) -> Box<ParsedSourceRefFFI>;
|
||||
#[cxx_name = "parse_source"]
|
||||
fn parse_source_ffi(
|
||||
src: &CxxWString,
|
||||
flags: u8,
|
||||
errors: *mut ParseErrorListFfi,
|
||||
) -> Box<ParsedSourceRefFFI>;
|
||||
fn clone(self: &ParsedSourceRefFFI) -> Box<ParsedSourceRefFFI>;
|
||||
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<ParsedSourceRefFFI> {
|
||||
Box::new(ParsedSourceRefFFI(None))
|
||||
}
|
||||
|
||||
fn new_parsed_source_ref(src: &CxxWString, ast: Pin<&mut Ast>) -> Box<ParsedSourceRefFFI> {
|
||||
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<ParsedSourceRefFFI> {
|
||||
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<ParsedSourceRefFFI> {
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<i32>;
|
||||
#[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<i32> {
|
||||
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())
|
||||
}
|
||||
|
||||
@@ -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<LibraryData>,
|
||||
@@ -355,7 +345,6 @@ fn get_base(&self) -> &SharedFromThisBase<Parser> {
|
||||
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<CxxWString>;
|
||||
#[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<EvalRes>;
|
||||
#[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<CxxWString>;
|
||||
#[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<ParserRefFFI>;
|
||||
#[cxx_name = "shared"]
|
||||
fn ffi_shared(&self) -> Box<ParserRefFFI>;
|
||||
#[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<CxxWString> {
|
||||
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<ParserRefFFI> {
|
||||
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<ParserRefFFI> {
|
||||
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<EvalRes> {
|
||||
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<CxxWString> {
|
||||
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,
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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<TtyTransfer>;
|
||||
#[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<JobListFFI>;
|
||||
#[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<JobListFFI> {
|
||||
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<TtyTransfer> {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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<i32>;
|
||||
fn oflags(self: &RedirectionSpec) -> i32;
|
||||
|
||||
fn fd(self: &RedirectionSpec) -> i32;
|
||||
fn mode(self: &RedirectionSpec) -> RedirectionMode;
|
||||
fn target(self: &RedirectionSpec) -> UniquePtr<CxxWString>;
|
||||
fn new_redirection_spec(
|
||||
fd: i32,
|
||||
mode: RedirectionMode,
|
||||
target: wcharz_t,
|
||||
) -> Box<RedirectionSpec>;
|
||||
|
||||
type RedirectionSpecListFfi;
|
||||
fn new_redirection_spec_list() -> Box<RedirectionSpecListFfi>;
|
||||
fn size(self: &RedirectionSpecListFfi) -> usize;
|
||||
fn at(self: &RedirectionSpecListFfi, offset: usize) -> *const RedirectionSpec;
|
||||
fn push_back(self: &mut RedirectionSpecListFfi, spec: Box<RedirectionSpec>);
|
||||
fn clone(self: &RedirectionSpecListFfi) -> Box<RedirectionSpecListFfi>;
|
||||
}
|
||||
|
||||
/// 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<Dup2Action>,
|
||||
}
|
||||
|
||||
extern "Rust" {
|
||||
fn get_actions(self: &Dup2List) -> &[Dup2Action];
|
||||
#[cxx_name = "dup2_list_resolve_chain"]
|
||||
fn dup2_list_resolve_chain_ffi(io_chain: &CxxVector<Dup2Action>) -> 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<Dup2Action>,
|
||||
}
|
||||
|
||||
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<RawFd> {
|
||||
fish_wcstoi(&self.target).ok()
|
||||
}
|
||||
fn get_target_as_fd_ffi(&self) -> SharedPtr<i32> {
|
||||
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<CxxWString> {
|
||||
self.target.to_ffi()
|
||||
}
|
||||
}
|
||||
|
||||
fn new_redirection_spec(fd: i32, mode: RedirectionMode, target: wcharz_t) -> Box<RedirectionSpec> {
|
||||
Box::new(RedirectionSpec {
|
||||
fd,
|
||||
mode,
|
||||
target: target.into(),
|
||||
})
|
||||
}
|
||||
|
||||
pub type RedirectionSpecList = Vec<RedirectionSpec>;
|
||||
|
||||
struct RedirectionSpecListFfi(RedirectionSpecList);
|
||||
|
||||
fn new_redirection_spec_list() -> Box<RedirectionSpecListFfi> {
|
||||
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<RedirectionSpec>) {
|
||||
self.0.push(*spec)
|
||||
}
|
||||
fn clone(&self) -> Box<RedirectionSpecListFfi> {
|
||||
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<Dup2Action>) -> 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()
|
||||
|
||||
@@ -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<Line>;
|
||||
#[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<CxxWString>;
|
||||
}
|
||||
extern "Rust" {
|
||||
type ScreenData;
|
||||
fn new_screen_data() -> Box<ScreenData>;
|
||||
#[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<Screen>;
|
||||
#[cxx_name = "write"]
|
||||
fn write_ffi(
|
||||
&mut self,
|
||||
left_prompt: &CxxWString,
|
||||
right_prompt: &CxxWString,
|
||||
commandline: &CxxWString,
|
||||
explicit_len: usize,
|
||||
colors: &HighlightSpecListFFI,
|
||||
indent: &CxxVector<i32>,
|
||||
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<CxxWString>;
|
||||
fn screen_force_clear_to_end();
|
||||
}
|
||||
}
|
||||
fn new_line() -> Box<Line> {
|
||||
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<CxxWString> {
|
||||
WString::from_iter(self.text.iter().map(|hc| hc.character)).to_ffi()
|
||||
}
|
||||
}
|
||||
fn new_screen_data() -> Box<ScreenData> {
|
||||
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<Screen> {
|
||||
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<i32>,
|
||||
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<CxxWString> {
|
||||
screen_clear().to_ffi()
|
||||
}
|
||||
|
||||
@@ -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<CxxWString>;
|
||||
|
||||
#[cxx_name = "wcs2sig"]
|
||||
fn wcs2sig_ffi(sig: &CxxWString) -> i32;
|
||||
|
||||
#[cxx_name = "signal_get_desc"]
|
||||
fn signal_get_desc_ffi(sig: i32) -> UniquePtr<CxxWString>;
|
||||
|
||||
fn signal_check_cancel() -> i32;
|
||||
fn signal_clear_cancel();
|
||||
fn signal_reset_handlers();
|
||||
|
||||
}
|
||||
extern "Rust" {
|
||||
type SigChecker;
|
||||
fn new_sighupint_checker() -> Box<SigChecker>;
|
||||
fn check(&mut self) -> bool;
|
||||
}
|
||||
}
|
||||
|
||||
fn sig2wcs_ffi(sig: i32) -> UniquePtr<CxxWString> {
|
||||
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<CxxWString> {
|
||||
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);
|
||||
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -45,72 +45,6 @@ impl FloggableDebug for ThreadId {}
|
||||
static NOTIFY_SIGNALLER: once_cell::sync::Lazy<crate::fd_monitor::FdEventSignaller> =
|
||||
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<CppCallback>) -> 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<CppCallback>);
|
||||
#[cxx_name = "iothread_perform_cantwait"]
|
||||
fn iothread_perform_cant_wait_ffi(callback: &SharedPtr<CppCallback>);
|
||||
}
|
||||
}
|
||||
|
||||
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<ffi::CppCallback>) {
|
||||
let callback = callback.clone();
|
||||
|
||||
iothread_perform(move || {
|
||||
callback.invoke();
|
||||
});
|
||||
}
|
||||
|
||||
fn iothread_perform_cant_wait_ffi(callback: &cxx::SharedPtr<ffi::CppCallback>) {
|
||||
let callback = callback.clone();
|
||||
|
||||
iothread_perform_cant_wait(move || {
|
||||
callback.invoke();
|
||||
});
|
||||
}
|
||||
|
||||
/// A [`ThreadPool`] work request.
|
||||
type WorkItem = Box<dyn FnOnce() + 'static + Send>;
|
||||
|
||||
@@ -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<F: FnOnce() + Send + 'static>(callback: F) -> bool {
|
||||
result
|
||||
}
|
||||
|
||||
fn spawn_ffi(callback: &cxx::SharedPtr<ffi::CppCallback>) -> 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
|
||||
|
||||
@@ -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<PrintElapsedOnDropFfi>;
|
||||
}
|
||||
}
|
||||
|
||||
enum Unit {
|
||||
Minutes,
|
||||
Seconds,
|
||||
@@ -52,22 +43,6 @@ pub fn push_timer(enabled: bool) -> Option<PrintElapsedOnDrop> {
|
||||
})
|
||||
}
|
||||
|
||||
/// cxx bridge does not support UniquePtr<NativeRustType> so we can't use a null UniquePtr to
|
||||
/// represent a None, and cxx bridge does not support Box<Option<NativeRustType>> 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<PrintElapsedOnDropFfi> {
|
||||
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 {
|
||||
|
||||
@@ -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<CxxWString>;
|
||||
}
|
||||
|
||||
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<CxxWString>;
|
||||
}
|
||||
|
||||
extern "Rust" {
|
||||
type Tokenizer;
|
||||
fn new_tokenizer(start: wcharz_t, flags: u8) -> Box<Tokenizer>;
|
||||
#[cxx_name = "next"]
|
||||
fn next_ffi(self: &mut Tokenizer) -> UniquePtr<Tok>;
|
||||
#[cxx_name = "text_of"]
|
||||
fn text_of_ffi(self: &Tokenizer, tok: &Tok) -> UniquePtr<CxxWString>;
|
||||
#[cxx_name = "is_token_delimiter"]
|
||||
fn is_token_delimiter_ffi(c: wchar_t, next: SharedPtr<wchar_t>) -> bool;
|
||||
}
|
||||
|
||||
extern "Rust" {
|
||||
#[cxx_name = "tok_command"]
|
||||
fn tok_command_ffi(str: &CxxWString) -> UniquePtr<CxxWString>;
|
||||
}
|
||||
|
||||
/// 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<PipeOrRedir>;
|
||||
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<MoveWordStateMachine>;
|
||||
#[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<usize>;
|
||||
}
|
||||
/// 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<CxxWString> {
|
||||
let s: &'static wstr = err.into();
|
||||
s.to_ffi()
|
||||
}
|
||||
|
||||
impl From<TokenizerError> 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<CxxWString> {
|
||||
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<Tokenizer> {
|
||||
Box::new(Tokenizer::new(start.into(), TokFlags(flags)))
|
||||
}
|
||||
|
||||
impl Iterator for Tokenizer {
|
||||
type Item = Tok;
|
||||
|
||||
@@ -557,14 +479,6 @@ fn next(&mut self) -> Option<Self::Item> {
|
||||
}
|
||||
}
|
||||
}
|
||||
impl Tokenizer {
|
||||
fn next_ffi(&mut self) -> UniquePtr<Tok> {
|
||||
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<CxxWString> {
|
||||
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<char>) -> bool {
|
||||
c == '(' || !tok_is_string_character(c, next)
|
||||
}
|
||||
|
||||
fn is_token_delimiter_ffi(c: wchar_t, next: SharedPtr<wchar_t>) -> 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<CxxWString> {
|
||||
tok_command(str.as_wstr()).to_ffi()
|
||||
}
|
||||
|
||||
impl TryFrom<&wstr> for PipeOrRedir {
|
||||
type Error = ();
|
||||
@@ -1139,13 +1040,6 @@ fn try_from(buff: &wstr) -> Result<PipeOrRedir, ()> {
|
||||
}
|
||||
}
|
||||
|
||||
fn pipe_or_redir_from_string(buff: wcharz_t) -> UniquePtr<PipeOrRedir> {
|
||||
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<usize> {
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
fn variable_assignment_equals_pos_ffi(txt: &CxxWString) -> SharedPtr<usize> {
|
||||
match variable_assignment_equals_pos(txt.as_wstr()) {
|
||||
Some(p) => SharedPtr::new(p),
|
||||
None => SharedPtr::null(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<topic_monitor_t> {
|
||||
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
|
||||
}
|
||||
|
||||
@@ -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<WString> = 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<S: AsRef<wstr>>(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<S: AsRef<wstr>>(parser: &Parser, command: &wstr, args: &[S]) {
|
||||
log_extra_to_flog_file(&trace_text);
|
||||
}
|
||||
|
||||
pub fn trace_if_enabled_ffi<S: AsRef<wstr>>(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) {
|
||||
|
||||
@@ -71,39 +71,3 @@ pub fn create_notifier() -> Box<dyn UniversalNotifier> {
|
||||
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<UniversalNotifierFFI> {
|
||||
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<UniversalNotifierFFI>;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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: AsRef<wstr>>(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::<crate::ffi::wchar_t>()
|
||||
};
|
||||
}
|
||||
|
||||
/// 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<WString>.
|
||||
pub fn wcharzs_to_vec(wcharz_vec: cxx::UniquePtr<cxx::CxxVector<wcharz_t>>) -> Vec<WString> {
|
||||
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<cxx::UniquePtr<cxx::CxxWString>> =
|
||||
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<cxx::CxxWString> {
|
||||
self.to_ffi()
|
||||
}
|
||||
}
|
||||
|
||||
impl ToCppWString for WString {
|
||||
fn into_cpp(self) -> cxx::UniquePtr<cxx::CxxWString> {
|
||||
self.to_ffi()
|
||||
}
|
||||
}
|
||||
|
||||
impl ToCppWString for &WString {
|
||||
fn into_cpp(self) -> cxx::UniquePtr<cxx::CxxWString> {
|
||||
self.to_ffi()
|
||||
}
|
||||
}
|
||||
|
||||
/// WString may be converted to CxxWString.
|
||||
impl WCharToFFI for WString {
|
||||
type Target = cxx::UniquePtr<cxx::CxxWString>;
|
||||
fn to_ffi(&self) -> cxx::UniquePtr<cxx::CxxWString> {
|
||||
cxx::CxxWString::create(self.as_char_slice())
|
||||
}
|
||||
}
|
||||
|
||||
/// wstr (wide string slices) may be converted to CxxWString.
|
||||
impl WCharToFFI for wstr {
|
||||
type Target = cxx::UniquePtr<cxx::CxxWString>;
|
||||
fn to_ffi(&self) -> cxx::UniquePtr<cxx::CxxWString> {
|
||||
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<cxx::CxxWString>;
|
||||
fn to_ffi(&self) -> cxx::UniquePtr<cxx::CxxWString> {
|
||||
cxx::CxxWString::create(self.chars())
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert from a slice of something that can be referenced as a wstr,
|
||||
/// to unique_ptr<wcstring_list_ffi_t>.
|
||||
impl<T: AsRef<wstr>> WCharToFFI for [T] {
|
||||
type Target = cxx::UniquePtr<wcstring_list_ffi_t>;
|
||||
fn to_ffi(&self) -> cxx::UniquePtr<wcstring_list_ffi_t> {
|
||||
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<Target> {
|
||||
/// Convert from a CxxWString for FFI purposes.
|
||||
#[allow(clippy::wrong_self_convention)]
|
||||
fn from_ffi(self) -> Target;
|
||||
}
|
||||
|
||||
impl WCharFromFFI<WString> for &cxx::CxxWString {
|
||||
fn from_ffi(self) -> WString {
|
||||
WString::from_chars(self.as_chars())
|
||||
}
|
||||
}
|
||||
|
||||
impl WCharFromFFI<WString> for &cxx::UniquePtr<cxx::CxxWString> {
|
||||
fn from_ffi(self) -> WString {
|
||||
WString::from_chars(self.as_chars())
|
||||
}
|
||||
}
|
||||
|
||||
impl WCharFromFFI<WString> for &cxx::SharedPtr<cxx::CxxWString> {
|
||||
fn from_ffi(self) -> WString {
|
||||
WString::from_chars(self.as_chars())
|
||||
}
|
||||
}
|
||||
|
||||
impl WCharFromFFI<Vec<u8>> for &cxx::UniquePtr<cxx::CxxString> {
|
||||
fn from_ffi(self) -> Vec<u8> {
|
||||
self.as_bytes().to_vec()
|
||||
}
|
||||
}
|
||||
|
||||
impl WCharFromFFI<Vec<u8>> for &cxx::SharedPtr<cxx::CxxString> {
|
||||
fn from_ffi(self) -> Vec<u8> {
|
||||
self.as_bytes().to_vec()
|
||||
}
|
||||
}
|
||||
|
||||
impl WCharFromFFI<WString> for &wcharz_t {
|
||||
fn from_ffi(self) -> WString {
|
||||
self.into()
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert wcstring_list_ffi_t to Vec<WString>.
|
||||
impl WCharFromFFI<Vec<WString>> for &wcstring_list_ffi_t {
|
||||
fn from_ffi(self) -> Vec<WString> {
|
||||
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<T> WCharFromFFI<Vec<WString>> for T
|
||||
where
|
||||
T: autocxx::moveit::new::New<Output = wcstring_list_ffi_t>,
|
||||
{
|
||||
fn from_ffi(self) -> Vec<WString> {
|
||||
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<cxx::CxxWString> {
|
||||
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())
|
||||
}
|
||||
}
|
||||
@@ -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())
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
|
||||
26
src/abbrs.h
26
src/abbrs.h
@@ -1,26 +0,0 @@
|
||||
// Support for abbreviations.
|
||||
//
|
||||
#ifndef FISH_ABBRS_H
|
||||
#define FISH_ABBRS_H
|
||||
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
|
||||
#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
|
||||
25
src/ast.cpp
25
src/ast.cpp
@@ -1,25 +0,0 @@
|
||||
#include "config.h" // IWYU pragma: keep
|
||||
|
||||
#include "ast.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cstdarg>
|
||||
#include <cstdlib>
|
||||
#include <string>
|
||||
|
||||
#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> 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> 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);
|
||||
}
|
||||
98
src/ast.h
98
src/ast.h
@@ -1,98 +0,0 @@
|
||||
// Programmatic representation of fish grammar.
|
||||
|
||||
#ifndef FISH_AST_H
|
||||
#define FISH_AST_H
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <initializer_list>
|
||||
#include <iterator>
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#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> ast_parse(const wcstring &src, parse_tree_flags_t flags = parse_flag_none,
|
||||
parse_error_list_t *out_errors = nullptr);
|
||||
rust::Box<Ast> 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
|
||||
@@ -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');
|
||||
}
|
||||
}
|
||||
@@ -1,91 +0,0 @@
|
||||
// Prototypes for functions for executing builtin functions.
|
||||
#ifndef FISH_BUILTIN_H
|
||||
#define FISH_BUILTIN_H
|
||||
|
||||
#include <vector>
|
||||
|
||||
#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<int> (*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
|
||||
@@ -1,49 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdlib>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
/// 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<void *(const void *param)> callback;
|
||||
std::vector<std::function<void()>> 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<void *(const void *param)> &&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<void()> &dtor : cleanups) {
|
||||
(dtor)();
|
||||
}
|
||||
cleanups.clear();
|
||||
}
|
||||
}
|
||||
};
|
||||
280
src/color.cpp
280
src/color.cpp
@@ -1,280 +0,0 @@
|
||||
// Color class implementation.
|
||||
#include "config.h" // IWYU pragma: keep
|
||||
|
||||
#include "color.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <cwchar> // IWYU pragma: keep
|
||||
#include <iterator>
|
||||
|
||||
#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<unsigned long>(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<unsigned long>(-1);
|
||||
auto best_index = static_cast<uint8_t>(-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<wcstring> rgb_color_t::named_color_names() {
|
||||
std::vector<wcstring> 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<uint8_t>(-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));
|
||||
}
|
||||
183
src/color.h
183
src/color.h
@@ -1,183 +0,0 @@
|
||||
// Color class.
|
||||
#ifndef FISH_COLOR_H
|
||||
#define FISH_COLOR_H
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
|
||||
#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<bool>(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<bool>(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<bool>(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<bool>(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<bool>(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<wcstring> named_color_names(void);
|
||||
};
|
||||
|
||||
static_assert(sizeof(rgb_color_t) <= 4, "rgb_color_t is too big");
|
||||
|
||||
#endif
|
||||
867
src/common.cpp
867
src/common.cpp
@@ -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 <cxxabi.h>
|
||||
#endif
|
||||
|
||||
#include <ctype.h>
|
||||
#include <dlfcn.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <limits.h>
|
||||
#include <pthread.h>
|
||||
#include <stdarg.h>
|
||||
#include <sys/time.h>
|
||||
#include <termios.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#ifdef HAVE_EXECINFO_H
|
||||
#include <execinfo.h>
|
||||
#endif
|
||||
|
||||
#ifdef __linux__
|
||||
// Includes for WSL detection
|
||||
#include <sys/utsname.h>
|
||||
#endif
|
||||
|
||||
#include <algorithm>
|
||||
#include <csignal>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <cwchar>
|
||||
#include <memory>
|
||||
|
||||
#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 <sys/sysctl.h> // IWYU pragma: keep
|
||||
#endif
|
||||
#if defined(__APPLE__)
|
||||
#include <mach-o/dyld.h> // IWYU pragma: keep
|
||||
#endif
|
||||
|
||||
struct termios shell_modes;
|
||||
|
||||
const wcstring g_empty_string{};
|
||||
const std::vector<wcstring> 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<wchar_t> ellipsis_char;
|
||||
wchar_t get_ellipsis_char() { return ellipsis_char; }
|
||||
|
||||
static relaxed_atomic_t<const wchar_t *> ellipsis_str;
|
||||
const wchar_t *get_ellipsis_str() { return ellipsis_str; }
|
||||
|
||||
static relaxed_atomic_t<const wchar_t *> omitted_newline_str;
|
||||
const wchar_t *get_omitted_newline_str() { return omitted_newline_str; }
|
||||
|
||||
static relaxed_atomic_t<int> omitted_newline_width;
|
||||
int get_omitted_newline_width() { return omitted_newline_width; }
|
||||
|
||||
static relaxed_atomic_t<wchar_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<pid_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<wcstring> 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<wcstring> 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<char *>(callstack[i]) - static_cast<const char *>(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<wcstring> 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 <size_t Align>
|
||||
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<uintptr_t>(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<size_t>(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 <size_t Align>
|
||||
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<uintptr_t>(start + len);
|
||||
uintptr_t sub_which_aligns = endu % Align;
|
||||
return start + len - std::min(static_cast<size_t>(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<alignof(WordType)>(in, in_len);
|
||||
const char *aligned_end = align_end<alignof(WordType)>(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<const WordType *>(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<uint32_t>(wc) >= 0x110000) {
|
||||
use_encode_direct = true;
|
||||
} else if (ret == static_cast<size_t>(-2)) {
|
||||
// Incomplete sequence.
|
||||
use_encode_direct = true;
|
||||
} else if (ret == static_cast<size_t>(-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<unsigned char>(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<size_t>(-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<wchar_t *>(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<size_t>(out);
|
||||
}
|
||||
}
|
||||
return static_cast<ssize_t>(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<unsigned long long>(x);
|
||||
x = -(x + 1);
|
||||
return static_cast<unsigned long long>(x) + 1;
|
||||
}
|
||||
|
||||
template <typename CharT>
|
||||
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<timepoint_t>(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(); }
|
||||
}
|
||||
616
src/common.h
616
src/common.h
@@ -25,413 +25,6 @@
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#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<wchar_t>(0xFDD0)
|
||||
#define RESERVED_CHAR_END static_cast<wchar_t>(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<wchar_t>(0xF600)
|
||||
#define ENCODE_DIRECT_END (ENCODE_DIRECT_BASE + 256)
|
||||
|
||||
// NAME_MAX is not defined on Solaris
|
||||
#if !defined(NAME_MAX)
|
||||
#include <sys/param.h>
|
||||
#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<wcstring>. This is useful for functions which wish to return a
|
||||
/// reference to an empty string.
|
||||
extern const std::vector<wcstring> 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 <typename Col, typename T2>
|
||||
bool contains(const Col &col, const T2 &val) {
|
||||
return std::find(std::begin(col), std::end(col), val) != std::end(col);
|
||||
}
|
||||
|
||||
template <typename T1, typename T2>
|
||||
bool contains(std::initializer_list<T1> 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 <typename T>
|
||||
void vec_append(std::vector<T> &receiver, std::vector<T> &&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<bool()>;
|
||||
|
||||
/// 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<wcstring>;
|
||||
|
||||
using scoped_lock = std::lock_guard<std::mutex>;
|
||||
|
||||
// An object wrapping a scoped lock and a value
|
||||
// This is returned from owning_lock.acquire()
|
||||
// Sample usage:
|
||||
// owning_lock<string> locked_name;
|
||||
// acquired_lock<string> name = name.acquire();
|
||||
// name.value = "derp"
|
||||
//
|
||||
// Or for simple cases:
|
||||
// name.acquire().value = "derp"
|
||||
//
|
||||
template <typename Data>
|
||||
class acquired_lock : noncopyable_t {
|
||||
template <typename T>
|
||||
friend class owning_lock;
|
||||
|
||||
template <typename T>
|
||||
friend class acquired_lock;
|
||||
|
||||
acquired_lock(std::mutex &lk, Data *v) : lock(lk), value(v) {}
|
||||
acquired_lock(std::unique_lock<std::mutex> &&lk, Data *v) : lock(std::move(lk)), value(v) {}
|
||||
|
||||
std::unique_lock<std::mutex> 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<const Data>() {
|
||||
// We're about to give up our lock, don't hold onto the data.
|
||||
const Data *cvalue = value;
|
||||
value = nullptr;
|
||||
return acquired_lock<const Data>(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<std::mutex> &get_lock() { return lock; }
|
||||
};
|
||||
|
||||
// A lock that owns a piece of data
|
||||
// Access to the data is only provided by taking the lock
|
||||
template <typename Data>
|
||||
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<Data> 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 <typename T>
|
||||
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<T> 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<const wcstring> {
|
||||
std::size_t operator()(const wcstring &w) const {
|
||||
std::hash<wcstring> 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<void()> cleanup;
|
||||
|
||||
public:
|
||||
cleanup_t(std::function<void()> exit_actions) : cleanup{std::move(exit_actions)} {}
|
||||
~cleanup_t() { cleanup(); }
|
||||
};
|
||||
|
||||
bool is_console_session();
|
||||
|
||||
/// Compile-time agnostic-size strcmp/wcscmp implementation. Unicode-unaware.
|
||||
template <typename T>
|
||||
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 <typename T, size_t N>
|
||||
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 <typename T, size_t N>
|
||||
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 <typename T, size_t N>
|
||||
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 <typename T, size_t N>
|
||||
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
|
||||
|
||||
@@ -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 <cstddef>
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#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
|
||||
@@ -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
|
||||
1
src/empty.cpp
Normal file
1
src/empty.cpp
Normal file
@@ -0,0 +1 @@
|
||||
|
||||
100
src/enum_set.h
100
src/enum_set.h
@@ -1,100 +0,0 @@
|
||||
#ifndef FISH_ENUM_SET_H
|
||||
#define FISH_ENUM_SET_H
|
||||
|
||||
#include <array>
|
||||
#include <bitset>
|
||||
#include <iterator>
|
||||
|
||||
/// A type (to specialize) that provides a count for an enum.
|
||||
/// Example:
|
||||
/// template<> struct enum_info_t<MyEnum>
|
||||
/// { static constexpr auto count = MyEnum::COUNT; };
|
||||
template <typename T>
|
||||
struct enum_info_t {};
|
||||
|
||||
/// \return the count of an enum.
|
||||
template <typename T>
|
||||
constexpr size_t enum_count() {
|
||||
return static_cast<size_t>(enum_info_t<T>::count);
|
||||
}
|
||||
|
||||
/// A bit set indexed by an enum type.
|
||||
template <typename T>
|
||||
class enum_set_t : private std::bitset<enum_count<T>()> {
|
||||
private:
|
||||
using super = std::bitset<enum_count<T>()>;
|
||||
static size_t index_of(T t) { return static_cast<size_t>(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<T> 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 <typename Elem, typename T>
|
||||
class enum_array_t : public std::array<Elem, enum_count<T>()> {
|
||||
using super = std::array<Elem, enum_count<T>()>;
|
||||
using base_type_t = typename std::underlying_type<T>::type;
|
||||
|
||||
static int index_of(T t) { return static_cast<base_type_t>(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
|
||||
225
src/env.cpp
225
src/env.cpp
@@ -1,225 +0,0 @@
|
||||
// Functions for setting and getting environment variables.
|
||||
#include "config.h" // IWYU pragma: keep
|
||||
|
||||
#include "env.h"
|
||||
|
||||
#include <pwd.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#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<EnvVar>::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<wcstring> &out) const {
|
||||
wcstring_list_ffi_t list{};
|
||||
impl_->to_list(list);
|
||||
out = std::move(list.vals);
|
||||
}
|
||||
|
||||
std::vector<wcstring> env_var_t::as_list() const {
|
||||
std::vector<wcstring> 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<env_var_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<env_var_t> 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<env_var_t>(variable.acquire());
|
||||
}
|
||||
|
||||
null_environment_t::null_environment_t() : impl_(env_null_create()) {}
|
||||
null_environment_t::~null_environment_t() = default;
|
||||
|
||||
maybe_t<env_var_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<wcstring> 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<wcstring, wcstring> 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_var_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<wcstring> 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<wcstring> vals) {
|
||||
return static_cast<int>(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<const wchar_t *const *>(vals);
|
||||
return this->set(key, mode, std::vector<wcstring>(ptr, ptr + count));
|
||||
}
|
||||
|
||||
int env_stack_t::set_one(const wcstring &key, env_mode_flags_t mode, wcstring val) {
|
||||
std::vector<wcstring> 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<int>(impl_->remove(key, mode));
|
||||
}
|
||||
|
||||
maybe_t<env_var_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<wcstring> 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<environment_t> env_stack_t::snapshot() const {
|
||||
auto res = std::make_shared<env_dyn_t>(impl_->snapshot());
|
||||
return std::static_pointer_cast<environment_t>(res);
|
||||
}
|
||||
|
||||
wcstring env_stack_t::get_pwd_slash() const {
|
||||
std::unique_ptr<wcstring> 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> &env_stack_t::principal_ref() {
|
||||
static const std::shared_ptr<env_stack_t> 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<EnvStackRef> imp) : impl_(std::move(imp)) {}
|
||||
env_stack_t::env_stack_t(uint8_t *imp)
|
||||
: impl_(rust::Box<EnvStackRef>::from_raw(reinterpret_cast<EnvStackRef *>(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_; }
|
||||
326
src/env.h
326
src/env.h
@@ -1,326 +0,0 @@
|
||||
// Prototypes for functions for manipulating fish script variables.
|
||||
#ifndef FISH_ENV_H
|
||||
#define FISH_ENV_H
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#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<rust::Box<Event>> 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<wcstring>{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<wcstring> &out) const;
|
||||
std::vector<wcstring> 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<EnvVar> &&impl) : impl_(std::move(impl)) {}
|
||||
|
||||
rust::Box<EnvVar> impl_;
|
||||
};
|
||||
|
||||
/// An environment is read-only access to variable values.
|
||||
class environment_t {
|
||||
protected:
|
||||
environment_t() = default;
|
||||
|
||||
public:
|
||||
virtual maybe_t<env_var_t> get(const wcstring &key,
|
||||
env_mode_flags_t mode = ENV_DEFAULT) const = 0;
|
||||
virtual std::vector<wcstring> get_names(env_mode_flags_t flags) const = 0;
|
||||
virtual ~environment_t();
|
||||
|
||||
maybe_t<env_var_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<env_var_t> 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<env_var_t> get(const wcstring &key, env_mode_flags_t mode = ENV_DEFAULT) const override;
|
||||
std::vector<wcstring> get_names(env_mode_flags_t flags) const override;
|
||||
|
||||
private:
|
||||
rust::Box<EnvNull> 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<EnvStackRef> imp);
|
||||
/* implicit */ env_stack_t(uint8_t *imp);
|
||||
|
||||
/// Implementation of environment_t.
|
||||
maybe_t<env_var_t> get(const wcstring &key, env_mode_flags_t mode = ENV_DEFAULT) const override;
|
||||
|
||||
/// Implementation of environment_t.
|
||||
std::vector<wcstring> 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<wcstring> 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<environment_t> 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<env_var_t> 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<env_stack_t> &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<EnvStackRef> *, or in Rust terms, a *const Box<EnvStackRef>.
|
||||
const EnvStackRef &get_impl_ffi() const;
|
||||
|
||||
private:
|
||||
/// The implementation. Do not access this directly.
|
||||
rust::Box<EnvStackRef> 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<EnvDyn> impl) : impl_(std::move(impl)) {}
|
||||
maybe_t<env_var_t> get(const wcstring &key, env_mode_flags_t mode) const;
|
||||
|
||||
std::vector<wcstring> get_names(env_mode_flags_t flags) const;
|
||||
|
||||
private:
|
||||
rust::Box<EnvDyn> 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
|
||||
@@ -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
|
||||
@@ -1,2 +0,0 @@
|
||||
struct EnvStackRef;
|
||||
struct EnvDyn;
|
||||
@@ -1,22 +0,0 @@
|
||||
#ifndef FISH_ENV_UNIVERSAL_COMMON_H
|
||||
#define FISH_ENV_UNIVERSAL_COMMON_H
|
||||
#include "config.h" // IWYU pragma: keep
|
||||
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <unordered_set>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#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
|
||||
@@ -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 <signal.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <atomic>
|
||||
#include <bitset>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#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<wcstring> &args) {
|
||||
wcstring_list_ffi_t ffi_args;
|
||||
for (const auto &arg : args) ffi_args.push(arg);
|
||||
event_fire_generic_ffi(parser, name, ffi_args);
|
||||
}
|
||||
38
src/event.h
38
src/event.h
@@ -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 <unistd.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#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<wcstring> &args = g_empty_string_list);
|
||||
|
||||
#endif
|
||||
@@ -1,3 +0,0 @@
|
||||
#if INCLUDE_RUST_HEADERS
|
||||
#include "exec.rs.h"
|
||||
#endif
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
102
src/expand.h
102
src/expand.h
@@ -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 <initializer_list>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#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
|
||||
181
src/fallback.cpp
181
src/fallback.cpp
@@ -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 <errno.h> // IWYU pragma: keep
|
||||
#include <fcntl.h> // IWYU pragma: keep
|
||||
#include <limits.h> // IWYU pragma: keep
|
||||
#include <unistd.h> // IWYU pragma: keep
|
||||
#include <wctype.h>
|
||||
|
||||
#include <cstdlib>
|
||||
#include <cwchar>
|
||||
#if HAVE_GETTEXT
|
||||
#include <libintl.h>
|
||||
#endif
|
||||
#if defined(TPARM_SOLARIS_KLUDGE)
|
||||
#if HAVE_CURSES_H
|
||||
#include <curses.h> // IWYU pragma: keep
|
||||
#elif HAVE_NCURSES_H
|
||||
#include <ncurses.h> // IWYU pragma: keep
|
||||
#elif HAVE_NCURSES_CURSES_H
|
||||
#include <ncurses/curses.h> // IWYU pragma: keep
|
||||
#endif
|
||||
#if HAVE_TERM_H
|
||||
#include <term.h> // IWYU pragma: keep
|
||||
#elif HAVE_NCURSES_TERM_H
|
||||
#include <ncurses/term.h>
|
||||
#endif
|
||||
#endif
|
||||
#include <signal.h> // 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;
|
||||
}
|
||||
113
src/fallback.h
113
src/fallback.h
@@ -1,113 +0,0 @@
|
||||
#ifndef FISH_FALLBACK_H
|
||||
#define FISH_FALLBACK_H
|
||||
|
||||
#include <stdint.h>
|
||||
#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 <wchar.h>. 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 <cwchar> // 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
|
||||
30
src/fds.h
30
src/fds.h
@@ -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 <poll.h> // IWYU pragma: keep
|
||||
#include <sys/select.h> // IWYU pragma: keep
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <limits>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#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<uint64_t>::max())
|
||||
|
||||
/// Mark an fd as blocking; returns errno or 0 on success.
|
||||
int make_fd_blocking(int fd);
|
||||
|
||||
#endif
|
||||
23
src/ffi.h
23
src/ffi.h
@@ -1,23 +0,0 @@
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
|
||||
#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 <typename T>
|
||||
inline std::shared_ptr<T> box_to_shared_ptr(rust::Box<T> &&value) {
|
||||
T *ptr = value.into_raw();
|
||||
std::shared_ptr<T> shared(ptr, [](T *ptr) { rust::Box<T>::from_raw(ptr); });
|
||||
return shared;
|
||||
}
|
||||
|
||||
inline static void trace_if_enabled(const parser_t &parser, wcharz_t command,
|
||||
const std::vector<wcstring> &args = {}) {
|
||||
if (trace_enabled(parser)) {
|
||||
trace_argv(parser, command, args);
|
||||
}
|
||||
}
|
||||
@@ -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{};
|
||||
}
|
||||
@@ -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(); }
|
||||
|
||||
@@ -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 <errno.h>
|
||||
#include <getopt.h>
|
||||
#include <locale.h>
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <cwchar>
|
||||
#include <cwctype>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#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(); }
|
||||
|
||||
@@ -20,7 +20,6 @@
|
||||
#include <vector>
|
||||
|
||||
#include "common.h"
|
||||
#include "ffi_baggage.h"
|
||||
#include "fish_key_reader.rs.h"
|
||||
|
||||
int main() { return fish_key_reader_main(); }
|
||||
|
||||
@@ -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; }
|
||||
@@ -1,2 +0,0 @@
|
||||
// Prototype for version receiver.
|
||||
const char *get_fish_version();
|
||||
206
src/flog.cpp
206
src/flog.cpp
@@ -1,206 +0,0 @@
|
||||
/// fish logging
|
||||
#include "config.h" // IWYU pragma: keep
|
||||
|
||||
#include "flog.h"
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstring>
|
||||
#include <cwchar>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#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<category_t *> 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<logger_t> 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<size_t>(ret) + 1;
|
||||
std::unique_ptr<char[]> 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<FILE *>(fp);
|
||||
set_flog_output_file(f);
|
||||
}
|
||||
|
||||
// Rust libc does not contain setlinebuf
|
||||
void flog_setlinebuf_ffi(void *f) {
|
||||
auto fp = static_cast<FILE *>(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<const category_t *> get_flog_categories() {
|
||||
std::vector<const category_t *> 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;
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user