mirror of
https://github.com/fish-shell/fish-shell.git
synced 2026-05-10 09:41:16 -03:00
Compare commits
84 Commits
Integratio
...
docker-bui
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
08cd59727b | ||
|
|
8e5046061d | ||
|
|
e9f5982147 | ||
|
|
50819666b1 | ||
|
|
6ad13e35c0 | ||
|
|
39e2f1138b | ||
|
|
cd37c71e29 | ||
|
|
c1f3d93b3b | ||
|
|
0aa05032c4 | ||
|
|
174130fe2f | ||
|
|
d06f7f01d2 | ||
|
|
a04ddd9b17 | ||
|
|
12929fed74 | ||
|
|
87bf580f68 | ||
|
|
66bab5e767 | ||
|
|
4b12fb2887 | ||
|
|
623c14aed0 | ||
|
|
7d83dc4758 | ||
|
|
493d0bca95 | ||
|
|
983501ff8c | ||
|
|
20da9a2b51 | ||
|
|
7aec6c55f9 | ||
|
|
532f30e031 | ||
|
|
1d7ab57e3a | ||
|
|
8adc598e90 | ||
|
|
c884c08257 | ||
|
|
66dc734c11 | ||
|
|
77fee9acb9 | ||
|
|
6b66c2bc1d | ||
|
|
81b9f50dc2 | ||
|
|
fcd246064b | ||
|
|
86a0a348ee | ||
|
|
ed36e852d2 | ||
|
|
da5d93c1e2 | ||
|
|
7b59ae0d82 | ||
|
|
97acc12d62 | ||
|
|
db6a7d26cd | ||
|
|
6be03d7cc4 | ||
|
|
617a6edb13 | ||
|
|
31c85723e8 | ||
|
|
d22c905d9f | ||
|
|
216dc2d473 | ||
|
|
918e7abe6b | ||
|
|
c145ee6df3 | ||
|
|
62543b36a4 | ||
|
|
751aad5302 | ||
|
|
efabab492a | ||
|
|
c7cdbe60cd | ||
|
|
412149a5de | ||
|
|
abd23d2a1b | ||
|
|
b774c54a6f | ||
|
|
e4b797405b | ||
|
|
81a89a5dec | ||
|
|
0da12a6b55 | ||
|
|
86ec8994e6 | ||
|
|
caf426ddb2 | ||
|
|
508ae410a6 | ||
|
|
993b977c9b | ||
|
|
a7f0138fc7 | ||
|
|
ab3c932903 | ||
|
|
ae0fdadcff | ||
|
|
e3974989d8 | ||
|
|
080b1e0e4f | ||
|
|
a5db91dd85 | ||
|
|
b62f54631b | ||
|
|
d835c5252a | ||
|
|
a53db72564 | ||
|
|
61b0368dac | ||
|
|
568b4a22f9 | ||
|
|
8abba8a089 | ||
|
|
b3b789cd68 | ||
|
|
425a166111 | ||
|
|
1dcc290e29 | ||
|
|
863204dbfa | ||
|
|
4b21e7c9c7 | ||
|
|
df5230ff4a | ||
|
|
7cd0943056 | ||
|
|
6f0532460a | ||
|
|
29a35a7951 | ||
|
|
dd0d45f88f | ||
|
|
0ff0de7efe | ||
|
|
092ef99551 | ||
|
|
97ae05b69d | ||
|
|
3d8eca178e |
18
.cirrus.yml
18
.cirrus.yml
@@ -25,10 +25,7 @@ linux_task:
|
||||
# Unrestriced parallelism results in OOM
|
||||
- lscpu || true
|
||||
- (cat /proc/meminfo | grep MemTotal) || true
|
||||
- mkdir build && cd build
|
||||
- FISH_TEST_MAX_CONCURRENCY=6 cmake -G Ninja -DCMAKE_BUILD_TYPE=Debug ..
|
||||
- ninja -j 6 fish
|
||||
- ninja fish_run_tests
|
||||
- FISH_TEST_MAX_CONCURRENCY=6 build_tools/check.sh
|
||||
only_if: $CIRRUS_REPO_OWNER == 'fish-shell'
|
||||
|
||||
linux_arm_task:
|
||||
@@ -44,11 +41,7 @@ linux_arm_task:
|
||||
# Unrestriced parallelism results in OOM
|
||||
- lscpu || true
|
||||
- (cat /proc/meminfo | grep MemTotal) || true
|
||||
- mkdir build && cd build
|
||||
- FISH_TEST_MAX_CONCURRENCY=6 cmake -G Ninja -DCMAKE_BUILD_TYPE=Debug ..
|
||||
- ninja -j 6 fish
|
||||
- file ./fish
|
||||
- ninja fish_run_tests
|
||||
- FISH_TEST_MAX_CONCURRENCY=6 build_tools/check.sh
|
||||
# CI task disabled during RIIR transition
|
||||
only_if: false && $CIRRUS_REPO_OWNER == 'fish-shell'
|
||||
|
||||
@@ -68,10 +61,7 @@ freebsd_task:
|
||||
- pw user add -n fish-user -s /bin/csh -d /home/fish-user
|
||||
- mkdir -p /home/fish-user
|
||||
- chown -R fish-user /home/fish-user
|
||||
- mkdir build && cd build
|
||||
- chown -R fish-user ..
|
||||
- chown -R fish-user .
|
||||
- sudo -u fish-user -s whoami
|
||||
- sudo -u fish-user -s FISH_TEST_MAX_CONCURRENCY=1 cmake -G Ninja -DCMAKE_BUILD_TYPE=Debug ..
|
||||
- sudo -u fish-user -s ninja -j 6 fish
|
||||
- sudo -u fish-user -s ninja fish_run_tests
|
||||
- sudo -u fish-user -s FISH_TEST_MAX_CONCURRENCY=1 build_tools/check.sh
|
||||
only_if: $CIRRUS_REPO_OWNER == 'fish-shell'
|
||||
|
||||
2
.github/workflows/main.yml
vendored
2
.github/workflows/main.yml
vendored
@@ -163,7 +163,7 @@ jobs:
|
||||
# --break-system-packages because homebrew has now declared itself "externally managed".
|
||||
# this is CI so we don't actually care.
|
||||
sudo pip3 install --break-system-packages pexpect
|
||||
brew install tmux
|
||||
brew install gettext tmux
|
||||
- name: cmake
|
||||
run: |
|
||||
mkdir build && cd build
|
||||
|
||||
2
.github/workflows/release.yml
vendored
2
.github/workflows/release.yml
vendored
@@ -150,6 +150,8 @@ jobs:
|
||||
uses: ./.github/actions/rust-toolchain@stable
|
||||
with:
|
||||
targets: aarch64-apple-darwin
|
||||
- name: Install dependencies
|
||||
run: brew install gettext
|
||||
- name: Build and codesign
|
||||
run: |
|
||||
die() { echo >&2 "$*"; exit 1; }
|
||||
|
||||
@@ -1,22 +1,16 @@
|
||||
fish 4.1.3 (released ???)
|
||||
fish ?.?.? (released ???)
|
||||
=========================
|
||||
|
||||
fish 4.1.2 (released ???)
|
||||
=========================
|
||||
|
||||
This release fixes the following regressions identified in 4.1.0:
|
||||
|
||||
- Crash on invalid :doc:`function <cmds/function>` call (:issue:`11912`).
|
||||
|
||||
fish 4.1.2 (released October 7, 2025)
|
||||
=====================================
|
||||
|
||||
This release fixes the following regressions identified in 4.1.0:
|
||||
|
||||
- Fixed spurious error output when completing remote file paths for ``scp`` (:issue:`11860`).
|
||||
- Fixed the :kbd:`alt-l` binding not formatting ``ls`` output correctly (one entry per line, no colors) (:issue:`11888`).
|
||||
- Fixed an issue where focus events (currently only enabled in ``tmux``) would cause multiline prompts to be redrawn in the wrong line (:issue:`11870`).
|
||||
- Stopped printing output that would cause a glitch on old versions of Midnight Commander (:issue:`11869`).
|
||||
- Added a fix for some configurations of Zellij where :kbd:`escape` key processing was delayed (:issue:`11868`).
|
||||
- Added a workaround for old versions of Zellij where :kbd:`escape` processing was delayed (:issue:`11868`).
|
||||
- Fixed a case where the :doc:`web-based configuration tool <cmds/fish_config>` would generate invalid configuration (:issue:`11861`).
|
||||
- Fixed a case where pasting into ``fish -c read`` would fail with a noisy error (:issue:`11836`).
|
||||
- Fixed a case where upgrading fish would break old versions of fish that were still running.
|
||||
|
||||
In general, fish still needs to be restarted after it is upgraded,
|
||||
|
||||
25
Cargo.lock
generated
25
Cargo.lock
generated
@@ -105,7 +105,7 @@ checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be"
|
||||
|
||||
[[package]]
|
||||
name = "fish"
|
||||
version = "4.1.2"
|
||||
version = "4.1.0-snapshot"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"cc",
|
||||
@@ -118,6 +118,7 @@ dependencies = [
|
||||
"fish-printf",
|
||||
"libc",
|
||||
"lru",
|
||||
"macro_rules_attribute",
|
||||
"nix",
|
||||
"num-traits",
|
||||
"once_cell",
|
||||
@@ -254,6 +255,22 @@ dependencies = [
|
||||
"hashbrown",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "macro_rules_attribute"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "65049d7923698040cd0b1ddcced9b0eb14dd22c5f86ae59c3740eab64a676520"
|
||||
dependencies = [
|
||||
"macro_rules_attribute-proc_macro",
|
||||
"paste",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "macro_rules_attribute-proc_macro"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "670fdfda89751bc4a84ac13eaa63e205cf0fd22b4c9a5fbfa085b63c1f1d3a30"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.7.4"
|
||||
@@ -326,6 +343,12 @@ dependencies = [
|
||||
"windows-targets",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "paste"
|
||||
version = "1.0.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
|
||||
|
||||
[[package]]
|
||||
name = "pcre2"
|
||||
version = "0.2.9"
|
||||
|
||||
@@ -64,7 +64,7 @@ debug = true
|
||||
|
||||
[package]
|
||||
name = "fish"
|
||||
version = "4.1.2"
|
||||
version = "4.1.0-snapshot"
|
||||
edition.workspace = true
|
||||
rust-version.workspace = true
|
||||
default-run = "fish"
|
||||
@@ -84,6 +84,7 @@ fish-gettext-maps = { workspace = true, optional = true }
|
||||
fish-printf.workspace = true
|
||||
libc.workspace = true
|
||||
lru.workspace = true
|
||||
macro_rules_attribute = "0.2.2"
|
||||
nix.workspace = true
|
||||
num-traits.workspace = true
|
||||
once_cell.workspace = true
|
||||
|
||||
10
build.rs
10
build.rs
@@ -16,7 +16,7 @@ fn main() {
|
||||
// language server.
|
||||
|
||||
rsconf::set_env_value(
|
||||
"FISH_RESOLVED_BUILD_DIR",
|
||||
"FISH_BUILD_DIR",
|
||||
// If set by CMake, this might include symlinks. Since we want to compare this to the
|
||||
// dir fish is executed in we need to canonicalize it.
|
||||
canonicalize(fish_build_dir()).to_str().unwrap(),
|
||||
@@ -50,9 +50,6 @@ fn main() {
|
||||
#[cfg(feature = "gettext-extract")]
|
||||
rsconf::rebuild_if_env_changed("FISH_GETTEXT_EXTRACTION_FILE");
|
||||
|
||||
rsconf::rebuild_if_path_changed("src/libc.c");
|
||||
cc::Build::new().file("src/libc.c").compile("flibc.a");
|
||||
|
||||
let build = cc::Build::new();
|
||||
let mut target = Target::new_from(build).unwrap();
|
||||
// Keep verbose mode on until we've ironed out rust build script stuff
|
||||
@@ -172,10 +169,7 @@ fn has_small_stack(_: &Target) -> bool {
|
||||
// Modern macOS versions default to an 8 MiB main stack but legacy OS X have a 0.5 MiB one.
|
||||
let stack_size = unsafe { pthread_get_stacksize_np(pthread_self()) };
|
||||
const TWO_MIB: usize = 2 * 1024 * 1024 - 1;
|
||||
match stack_size {
|
||||
0..=TWO_MIB => true,
|
||||
_ => false,
|
||||
}
|
||||
stack_size <= TWO_MIB
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -43,6 +43,11 @@ fi
|
||||
# Currently, all builds are debug builds.
|
||||
build_dir="$target_dir/debug"
|
||||
|
||||
if [ -n "$FISH_TEST_MAX_CONCURRENCY" ]; then
|
||||
export RUST_TEST_THREADS="$FISH_TEST_MAX_CONCURRENCY"
|
||||
export CARGO_BUILD_JOBS="$FISH_TEST_MAX_CONCURRENCY"
|
||||
fi
|
||||
|
||||
template_file=$(mktemp)
|
||||
FISH_GETTEXT_EXTRACTION_FILE=$template_file cargo build --workspace --all-targets --features=gettext-extract
|
||||
if $lint; then
|
||||
|
||||
@@ -27,13 +27,21 @@ begin
|
||||
else
|
||||
set rust_extraction_file (mktemp)
|
||||
# We need to build to ensure that the proc macro for extracting strings runs.
|
||||
FISH_GETTEXT_EXTRACTION_FILE=$rust_extraction_file cargo check --features=gettext-extract
|
||||
FISH_GETTEXT_EXTRACTION_FILE=$rust_extraction_file cargo check --no-default-features --features=gettext-extract
|
||||
or exit 1
|
||||
end
|
||||
|
||||
echo '# fish-section-tier1-from-rust'
|
||||
function mark_section
|
||||
set -l section_name $argv[1]
|
||||
echo 'msgid "fish-section-'$section_name'"'
|
||||
echo 'msgstr ""'
|
||||
echo ''
|
||||
end
|
||||
|
||||
mark_section tier1-from-rust
|
||||
|
||||
# Get rid of duplicates and sort.
|
||||
msguniq --no-wrap --strict --sort-output $rust_extraction_file
|
||||
msguniq --no-wrap --sort-output $rust_extraction_file
|
||||
or exit 1
|
||||
|
||||
if not set -l --query _flag_use_existing_template
|
||||
@@ -77,13 +85,13 @@ begin
|
||||
# This regex handles explicit requests to translate a message. These are more important to translate
|
||||
# than messages which should be implicitly translated.
|
||||
set -l explicit_regex '.*\( *_ (([\'"]).+?(?<!\\\\)\\2) *\).*'
|
||||
echo "# fish-section-$tier-from-script-explicitly-added"
|
||||
mark_section "$tier-from-script-explicitly-added"
|
||||
extract_fish_script_messages_impl $explicit_regex $argv
|
||||
|
||||
# This regex handles descriptions for `complete` and `function` statements. These messages are not
|
||||
# particularly important to translate. Hence the "implicit" label.
|
||||
set -l implicit_regex '^(?:\s|and |or )*(?:complete|function).*? (?:-d|--description) (([\'"]).+?(?<!\\\\)\\2).*'
|
||||
echo "# fish-section-$tier-from-script-implicitly-added"
|
||||
mark_section "$tier-from-script-implicitly-added"
|
||||
extract_fish_script_messages_impl $implicit_regex $argv
|
||||
end
|
||||
|
||||
|
||||
@@ -32,7 +32,6 @@ done
|
||||
|
||||
repo_root="$(dirname "$0")/.."
|
||||
fish_site=$repo_root/../fish-site
|
||||
fish_site_repo=git@github.com:$repository_owner/fish-site
|
||||
|
||||
for path in . "$fish_site"
|
||||
do
|
||||
@@ -43,13 +42,6 @@ do
|
||||
fi
|
||||
done
|
||||
|
||||
(
|
||||
cd "$fish_site"
|
||||
[ "$(git rev-parse HEAD)" = \
|
||||
"$(git ls-remote "$fish_site_repo" refs/heads/master |
|
||||
awk '{print $1}')" ]
|
||||
)
|
||||
|
||||
if git tag | grep -qxF "$version"; then
|
||||
echo >&2 "$0: tag $version already exists"
|
||||
exit 1
|
||||
@@ -161,7 +153,7 @@ gh_api_repo() {
|
||||
command gh api \
|
||||
-H "Accept: application/vnd.github+json" \
|
||||
-H "X-GitHub-Api-Version: 2022-11-28" \
|
||||
"/repos/$repository_owner/fish-shell/$path" \
|
||||
"/repos/$repository_owner/fish-shell/$1" \
|
||||
"$@"
|
||||
}
|
||||
|
||||
@@ -212,7 +204,7 @@ done
|
||||
" | sed 's,^\s*| \?,,')"
|
||||
# This takes care to support remote names that are different from
|
||||
# fish-shell remote name. Also, support detached HEAD state.
|
||||
git push "$fish_site_repo" HEAD:master
|
||||
git push git@github.com:$repository_owner/fish-site HEAD:master
|
||||
)
|
||||
|
||||
if [ -n "$integration_branch" ]; then {
|
||||
@@ -233,7 +225,7 @@ milestone_number=$(
|
||||
gh_api_repo milestones?state=open |
|
||||
jq '.[] | select(.title == "fish '"$version"'") | .number'
|
||||
)
|
||||
gh_api_repo milestones/$milestone_number --method PATCH \
|
||||
gh_api_repo --method PATCH milestones/$milestone_number \
|
||||
--raw-field state=closed
|
||||
|
||||
next_patch_version=$(
|
||||
@@ -244,7 +236,7 @@ next_patch_version=$(
|
||||
'
|
||||
)
|
||||
if [ -n "$next_patch_version" ]; then
|
||||
gh_api_repo milestones --method POST \
|
||||
gh_api_repo --method POST milestones \
|
||||
--raw-field title="fish $next_patch_version"
|
||||
fi
|
||||
|
||||
|
||||
@@ -31,7 +31,6 @@
|
||||
set -gx LC_ALL C.UTF-8
|
||||
|
||||
set -l build_tools (status dirname)
|
||||
set -g tmpdir
|
||||
set -l po_dir $build_tools/../po
|
||||
|
||||
set -l extract
|
||||
@@ -46,8 +45,8 @@ if test -z $argv[1]
|
||||
else
|
||||
set -l po_dir_id (stat --format='%d:%i' -- $po_dir)
|
||||
for arg in $argv
|
||||
set -l arg_dir_id (stat --format='%d:%i' -- (dirname $arg))
|
||||
if test $po_dir_id != $arg_dir_id
|
||||
set -l arg_dir_id (stat --format='%d:%i' -- (dirname $arg) 2>/dev/null)
|
||||
if test $po_dir_id != "$arg_dir_id"
|
||||
echo "Argument $arg is not a file in the directory $(realpath $po_dir)."
|
||||
echo "Non-option arguments must specify paths to files in this directory."
|
||||
echo ""
|
||||
@@ -99,44 +98,34 @@ if set -l --query _flag_dry_run
|
||||
cp -r $po_dir/* $tmpdir
|
||||
end
|
||||
|
||||
# This is used to identify lines which should be set here via $header_lines.
|
||||
# Make sure that this prefix does not appear elsewhere in the file and only contains characters
|
||||
# without special meaning in a sed pattern.
|
||||
set -g header_prefix "# fish-note-sections: "
|
||||
|
||||
function print_header
|
||||
set -l header_lines \
|
||||
"Translations are divided into sections, each starting with a fish-section-* pseudo-message." \
|
||||
"The first few sections are more important." \
|
||||
"Ignore the tier3 sections unless you have a lot of time."
|
||||
for line in $header_lines
|
||||
printf '%s%s\n' $header_prefix $line
|
||||
end
|
||||
end
|
||||
|
||||
function merge_po_files --argument-names template_file po_file
|
||||
msgmerge --no-wrap --update --no-fuzzy-matching --backup=none --quiet \
|
||||
$po_file $template_file
|
||||
or cleanup_exit
|
||||
set -l new_po_file (mktemp) # TODO Remove on failure.
|
||||
# Remove obsolete messages instead of keeping them as #~ entries.
|
||||
and msgattrib --no-wrap --no-obsolete -o $new_po_file $po_file
|
||||
or cleanup_exit
|
||||
|
||||
begin
|
||||
echo "# fish-note-sections: Translations are divided into sections, each starting with a fish-section-* comment."
|
||||
echo "# fish-note-sections: The first few sections are more important."
|
||||
echo "# fish-note-sections: Ignore the tier3 sections unless you have a lot of time."
|
||||
sed -i '
|
||||
/^# fish-note-sections:/d;
|
||||
/^# fish-section-/d;
|
||||
' $new_po_file
|
||||
|
||||
set -l next_line 1
|
||||
set -l section
|
||||
awk <$template_file '
|
||||
/^# fish-section-\S*$/ {
|
||||
section = $0
|
||||
}
|
||||
section != "" && /^msgid ".+"$/ {
|
||||
print section
|
||||
print $0
|
||||
section = ""
|
||||
}
|
||||
' |
|
||||
while read -l section
|
||||
read -l msgid_line
|
||||
set -l line_number (grep -m1 -Fxn $msgid_line $new_po_file | string split :)[1]
|
||||
sed -n "$next_line,$(math $line_number - 1)"p $new_po_file
|
||||
echo $section
|
||||
set next_line $line_number
|
||||
# set section
|
||||
end
|
||||
sed -n "$next_line,\$"p $new_po_file
|
||||
print_header
|
||||
# Paste PO file without old header lines.
|
||||
sed '/^'$header_prefix'/d' $new_po_file
|
||||
end >$po_file
|
||||
rm $new_po_file
|
||||
end
|
||||
@@ -149,7 +138,10 @@ for po_file in $po_files
|
||||
if test -e $po_file
|
||||
merge_po_files $template_file $po_file
|
||||
else
|
||||
cp $template_file $po_file
|
||||
begin
|
||||
print_header
|
||||
cat $template_file
|
||||
end >$po_file
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -12,7 +12,7 @@ fn cargo_target_dir() -> Cow<'static, Path> {
|
||||
}
|
||||
|
||||
pub fn fish_build_dir() -> Cow<'static, Path> {
|
||||
// This is set if using CMake.
|
||||
// FISH_BUILD_DIR is set by CMake, if we are using it.
|
||||
option_env!("FISH_BUILD_DIR")
|
||||
.map(|d| Cow::Borrowed(Path::new(d)))
|
||||
.unwrap_or(cargo_target_dir())
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
env,
|
||||
ffi::OsStr,
|
||||
path::{Path, PathBuf},
|
||||
process::Command,
|
||||
process::{Command, Stdio},
|
||||
};
|
||||
|
||||
fn main() {
|
||||
@@ -34,7 +34,11 @@ fn embed_localizations(cache_dir: &Path) {
|
||||
// for the respective language.
|
||||
let mut catalogs = phf_codegen::Map::new();
|
||||
|
||||
match Command::new("msgfmt").arg("-h").status() {
|
||||
match Command::new("msgfmt")
|
||||
.arg("-h")
|
||||
.stdout(Stdio::null())
|
||||
.status()
|
||||
{
|
||||
Err(e) if e.kind() == std::io::ErrorKind::NotFound => {
|
||||
rsconf::warn!(
|
||||
"Cannot find msgfmt to build gettext message catalogs. Localization will not work."
|
||||
@@ -97,6 +101,12 @@ fn embed_localizations(cache_dir: &Path) {
|
||||
.arg(&po_file_path)
|
||||
.output()
|
||||
.unwrap();
|
||||
if !output.status.success() {
|
||||
panic!(
|
||||
"msgfmt failed:\n{}",
|
||||
String::from_utf8(output.stderr).unwrap()
|
||||
);
|
||||
}
|
||||
let mo_data = output.stdout;
|
||||
|
||||
// Extract map from MO data.
|
||||
|
||||
36
po/de.po
36
po/de.po
@@ -1,4 +1,4 @@
|
||||
# fish-note-sections: Translations are divided into sections, each starting with a fish-section-* comment.
|
||||
# fish-note-sections: Translations are divided into sections, each starting with a fish-section-* pseudo-message.
|
||||
# fish-note-sections: The first few sections are more important.
|
||||
# fish-note-sections: Ignore the tier3 sections unless you have a lot of time.
|
||||
# translation of de.po to deutsch
|
||||
@@ -21,6 +21,9 @@ msgstr ""
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Generator: Poedit 3.5\n"
|
||||
|
||||
msgid "fish-section-tier1-from-rust"
|
||||
msgstr ""
|
||||
|
||||
msgid ""
|
||||
"\n"
|
||||
" PID Command\n"
|
||||
@@ -29,7 +32,6 @@ msgstr ""
|
||||
" PID Befehl\n"
|
||||
|
||||
#, c-format
|
||||
# fish-section-tier1-from-rust
|
||||
msgid " (%ls)\n"
|
||||
msgstr " (%ls)\n"
|
||||
|
||||
@@ -200,7 +202,6 @@ msgstr "%ls: %ls: ungültiger Unterbefehl\n"
|
||||
msgid "%ls: %ls: invalid variable name. See `help identifiers`\n"
|
||||
msgstr ""
|
||||
|
||||
#
|
||||
#, c-format
|
||||
msgid "%ls: %ls: option does not take an argument\n"
|
||||
msgstr ""
|
||||
@@ -409,7 +410,6 @@ msgstr ""
|
||||
msgid "%ls: Invalid --min-args value '%ls'\n"
|
||||
msgstr ""
|
||||
|
||||
#
|
||||
#, c-format
|
||||
msgid "%ls: Invalid --unknown-arguments value '%ls'\n"
|
||||
msgstr ""
|
||||
@@ -702,15 +702,10 @@ msgstr ""
|
||||
msgid "%ls: expected a numeric value"
|
||||
msgstr "%ls: Erwartete numerischen Wert"
|
||||
|
||||
#
|
||||
#, c-format
|
||||
msgid "%ls: fish was not built with embedded files"
|
||||
msgstr ""
|
||||
|
||||
#, c-format
|
||||
msgid "%ls: function name required"
|
||||
msgstr "%ls: Brauche Funktionsnamen"
|
||||
|
||||
#, c-format
|
||||
msgid "%ls: given %d indexes but %d values\n"
|
||||
msgstr ""
|
||||
@@ -803,17 +798,14 @@ msgstr ""
|
||||
msgid "%s"
|
||||
msgstr ""
|
||||
|
||||
#
|
||||
#, c-format
|
||||
msgid "%s %s: unrecognized feature '%ls'"
|
||||
msgstr ""
|
||||
|
||||
#
|
||||
#, c-format
|
||||
msgid "%s and %s are mutually exclusive"
|
||||
msgstr ""
|
||||
|
||||
#
|
||||
#, c-format
|
||||
msgid "%s could not read response to Primary Device Attribute query after waiting for %d seconds. This is often due to a missing feature in your terminal. See 'help terminal-compatibility' or 'man fish-terminal-compatibility'. This %s process will no longer wait for outstanding queries, which disables some optional features."
|
||||
msgstr ""
|
||||
@@ -1121,7 +1113,6 @@ msgstr ""
|
||||
msgid "Failed to assign shell to its own process group"
|
||||
msgstr "Konnte Shell nicht einer eigenen Prozessgruppe zuweisen"
|
||||
|
||||
#
|
||||
#, c-format
|
||||
msgid "Failed to set terminal mode (%s)"
|
||||
msgstr ""
|
||||
@@ -1199,7 +1190,6 @@ msgstr "Illegale Instruktion"
|
||||
msgid "Incomplete escape sequence '%ls'"
|
||||
msgstr "Unvollständige Escapesequenz '%ls'"
|
||||
|
||||
#
|
||||
msgid "Information request"
|
||||
msgstr ""
|
||||
|
||||
@@ -1526,7 +1516,6 @@ msgstr "Derzeit ausführende Funktion stoppen"
|
||||
msgid "Stop the innermost loop"
|
||||
msgstr "Innerste Schleife beenden"
|
||||
|
||||
#
|
||||
msgid "Synchronized file access"
|
||||
msgstr ""
|
||||
|
||||
@@ -1714,7 +1703,6 @@ msgstr "Nicht passender Platzhalter"
|
||||
msgid "Unsupported use of '='. In fish, please use 'set %ls %ls'."
|
||||
msgstr ""
|
||||
|
||||
#
|
||||
msgid "Unused signal"
|
||||
msgstr ""
|
||||
|
||||
@@ -1742,7 +1730,6 @@ msgstr ""
|
||||
msgid "Variables cannot be bracketed. In fish, please use {$%ls}."
|
||||
msgstr ""
|
||||
|
||||
#
|
||||
msgid "Virtual timer expired"
|
||||
msgstr ""
|
||||
|
||||
@@ -1906,7 +1893,9 @@ msgstr "unbegrenzt\n"
|
||||
msgid "|& is not valid. In fish, use &| to pipe both stdout and stderr."
|
||||
msgstr "|& ist ungültig. In fish, nutze &| um stdout und stderr gleichzeitig zu pipen"
|
||||
|
||||
# fish-section-tier1-from-script-explicitly-added
|
||||
msgid "fish-section-tier1-from-script-explicitly-added"
|
||||
msgstr ""
|
||||
|
||||
msgid "%ls: %ls: expected %d arguments; got %d\\n"
|
||||
msgstr ""
|
||||
|
||||
@@ -2129,7 +2118,9 @@ msgstr "oder die Datei war leer"
|
||||
msgid "python executable not found"
|
||||
msgstr ""
|
||||
|
||||
# fish-section-tier1-from-script-implicitly-added
|
||||
msgid "fish-section-tier1-from-script-implicitly-added"
|
||||
msgstr ""
|
||||
|
||||
msgid "# comments"
|
||||
msgstr ""
|
||||
|
||||
@@ -3831,7 +3822,12 @@ msgstr ""
|
||||
msgid "~ expansion"
|
||||
msgstr ""
|
||||
|
||||
# fish-section-tier3-from-script-implicitly-added
|
||||
msgid "fish-section-tier3-from-script-explicitly-added"
|
||||
msgstr ""
|
||||
|
||||
msgid "fish-section-tier3-from-script-implicitly-added"
|
||||
msgstr ""
|
||||
|
||||
msgid " "
|
||||
msgstr ""
|
||||
|
||||
|
||||
36
po/en.po
36
po/en.po
@@ -1,4 +1,4 @@
|
||||
# fish-note-sections: Translations are divided into sections, each starting with a fish-section-* comment.
|
||||
# fish-note-sections: Translations are divided into sections, each starting with a fish-section-* pseudo-message.
|
||||
# fish-note-sections: The first few sections are more important.
|
||||
# fish-note-sections: Ignore the tier3 sections unless you have a lot of time.
|
||||
# SOME DESCRIPTIVE TITLE.
|
||||
@@ -21,13 +21,15 @@ msgstr ""
|
||||
"X-Poedit-Basepath: /home/david/src/fish-shell\n"
|
||||
"X-Generator: Poedit 1.8.11\n"
|
||||
|
||||
msgid "fish-section-tier1-from-rust"
|
||||
msgstr ""
|
||||
|
||||
msgid ""
|
||||
"\n"
|
||||
" PID Command\n"
|
||||
msgstr ""
|
||||
|
||||
#, c-format
|
||||
# fish-section-tier1-from-rust
|
||||
msgid " (%ls)\n"
|
||||
msgstr ""
|
||||
|
||||
@@ -198,7 +200,6 @@ msgstr ""
|
||||
msgid "%ls: %ls: invalid variable name. See `help identifiers`\n"
|
||||
msgstr ""
|
||||
|
||||
#
|
||||
#, c-format
|
||||
msgid "%ls: %ls: option does not take an argument\n"
|
||||
msgstr ""
|
||||
@@ -379,7 +380,6 @@ msgstr "%ls: Expected exactly two names (current function name, and new function
|
||||
msgid "%ls: Expected generic | variable | signal | exit | job-id for --handlers-type\n"
|
||||
msgstr ""
|
||||
|
||||
#
|
||||
#, c-format
|
||||
msgid "%ls: Function '%ls' already exists. Cannot create copy of '%ls'\n"
|
||||
msgstr ""
|
||||
@@ -408,7 +408,6 @@ msgstr ""
|
||||
msgid "%ls: Invalid --min-args value '%ls'\n"
|
||||
msgstr ""
|
||||
|
||||
#
|
||||
#, c-format
|
||||
msgid "%ls: Invalid --unknown-arguments value '%ls'\n"
|
||||
msgstr ""
|
||||
@@ -701,15 +700,10 @@ msgstr ""
|
||||
msgid "%ls: expected a numeric value"
|
||||
msgstr "%ls: expected a numeric value"
|
||||
|
||||
#
|
||||
#, c-format
|
||||
msgid "%ls: fish was not built with embedded files"
|
||||
msgstr ""
|
||||
|
||||
#, c-format
|
||||
msgid "%ls: function name required"
|
||||
msgstr ""
|
||||
|
||||
#, c-format
|
||||
msgid "%ls: given %d indexes but %d values\n"
|
||||
msgstr ""
|
||||
@@ -802,17 +796,14 @@ msgstr ""
|
||||
msgid "%s"
|
||||
msgstr ""
|
||||
|
||||
#
|
||||
#, c-format
|
||||
msgid "%s %s: unrecognized feature '%ls'"
|
||||
msgstr ""
|
||||
|
||||
#
|
||||
#, c-format
|
||||
msgid "%s and %s are mutually exclusive"
|
||||
msgstr ""
|
||||
|
||||
#
|
||||
#, c-format
|
||||
msgid "%s could not read response to Primary Device Attribute query after waiting for %d seconds. This is often due to a missing feature in your terminal. See 'help terminal-compatibility' or 'man fish-terminal-compatibility'. This %s process will no longer wait for outstanding queries, which disables some optional features."
|
||||
msgstr ""
|
||||
@@ -916,7 +907,6 @@ msgstr "An error occurred while setting up pipe"
|
||||
msgid "Argument is not a number: '%ls'"
|
||||
msgstr ""
|
||||
|
||||
#
|
||||
msgid "Await background process completion"
|
||||
msgstr ""
|
||||
|
||||
@@ -1121,7 +1111,6 @@ msgstr ""
|
||||
msgid "Failed to assign shell to its own process group"
|
||||
msgstr ""
|
||||
|
||||
#
|
||||
#, c-format
|
||||
msgid "Failed to set terminal mode (%s)"
|
||||
msgstr ""
|
||||
@@ -1525,7 +1514,6 @@ msgstr "Stop the currently evaluated function"
|
||||
msgid "Stop the innermost loop"
|
||||
msgstr "Stop the innermost loop"
|
||||
|
||||
#
|
||||
msgid "Synchronized file access"
|
||||
msgstr ""
|
||||
|
||||
@@ -1740,7 +1728,6 @@ msgstr ""
|
||||
msgid "Variables cannot be bracketed. In fish, please use {$%ls}."
|
||||
msgstr ""
|
||||
|
||||
#
|
||||
msgid "Virtual timer expired"
|
||||
msgstr ""
|
||||
|
||||
@@ -1904,7 +1891,9 @@ msgstr ""
|
||||
msgid "|& is not valid. In fish, use &| to pipe both stdout and stderr."
|
||||
msgstr ""
|
||||
|
||||
# fish-section-tier1-from-script-explicitly-added
|
||||
msgid "fish-section-tier1-from-script-explicitly-added"
|
||||
msgstr ""
|
||||
|
||||
msgid "%ls: %ls: expected %d arguments; got %d\\n"
|
||||
msgstr ""
|
||||
|
||||
@@ -2127,7 +2116,9 @@ msgstr ""
|
||||
msgid "python executable not found"
|
||||
msgstr ""
|
||||
|
||||
# fish-section-tier1-from-script-implicitly-added
|
||||
msgid "fish-section-tier1-from-script-implicitly-added"
|
||||
msgstr ""
|
||||
|
||||
msgid "# comments"
|
||||
msgstr ""
|
||||
|
||||
@@ -3829,7 +3820,12 @@ msgstr ""
|
||||
msgid "~ expansion"
|
||||
msgstr ""
|
||||
|
||||
# fish-section-tier3-from-script-implicitly-added
|
||||
msgid "fish-section-tier3-from-script-explicitly-added"
|
||||
msgstr ""
|
||||
|
||||
msgid "fish-section-tier3-from-script-implicitly-added"
|
||||
msgstr ""
|
||||
|
||||
msgid " "
|
||||
msgstr ""
|
||||
|
||||
|
||||
36
po/fr.po
36
po/fr.po
@@ -1,4 +1,4 @@
|
||||
# fish-note-sections: Translations are divided into sections, each starting with a fish-section-* comment.
|
||||
# fish-note-sections: Translations are divided into sections, each starting with a fish-section-* pseudo-message.
|
||||
# fish-note-sections: The first few sections are more important.
|
||||
# fish-note-sections: Ignore the tier3 sections unless you have a lot of time.
|
||||
# translation of fr.po to Français
|
||||
@@ -120,6 +120,9 @@ msgstr ""
|
||||
"X-Generator: Gtranslator 2.91.7\n"
|
||||
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
|
||||
|
||||
msgid "fish-section-tier1-from-rust"
|
||||
msgstr ""
|
||||
|
||||
msgid ""
|
||||
"\n"
|
||||
" PID Command\n"
|
||||
@@ -128,7 +131,6 @@ msgstr ""
|
||||
" PID Commande\n"
|
||||
|
||||
#, c-format
|
||||
# fish-section-tier1-from-rust
|
||||
msgid " (%ls)\n"
|
||||
msgstr ""
|
||||
|
||||
@@ -299,7 +301,6 @@ msgstr ""
|
||||
msgid "%ls: %ls: invalid variable name. See `help identifiers`\n"
|
||||
msgstr ""
|
||||
|
||||
#
|
||||
#, c-format
|
||||
msgid "%ls: %ls: option does not take an argument\n"
|
||||
msgstr ""
|
||||
@@ -480,7 +481,6 @@ msgstr "%ls : Exactement deux noms attendus (noms de fonctions actuel et projet
|
||||
msgid "%ls: Expected generic | variable | signal | exit | job-id for --handlers-type\n"
|
||||
msgstr ""
|
||||
|
||||
#
|
||||
#, c-format
|
||||
msgid "%ls: Function '%ls' already exists. Cannot create copy of '%ls'\n"
|
||||
msgstr ""
|
||||
@@ -509,7 +509,6 @@ msgstr "%ls : Valeur --max-args '%ls' invalide\n"
|
||||
msgid "%ls: Invalid --min-args value '%ls'\n"
|
||||
msgstr "%ls : Valeur --min-args '%ls' invalide\n"
|
||||
|
||||
#
|
||||
#, c-format
|
||||
msgid "%ls: Invalid --unknown-arguments value '%ls'\n"
|
||||
msgstr ""
|
||||
@@ -802,15 +801,10 @@ msgstr ""
|
||||
msgid "%ls: expected a numeric value"
|
||||
msgstr "%ls : valeur numérique attendue"
|
||||
|
||||
#
|
||||
#, c-format
|
||||
msgid "%ls: fish was not built with embedded files"
|
||||
msgstr ""
|
||||
|
||||
#, c-format
|
||||
msgid "%ls: function name required"
|
||||
msgstr ""
|
||||
|
||||
#, c-format
|
||||
msgid "%ls: given %d indexes but %d values\n"
|
||||
msgstr ""
|
||||
@@ -903,17 +897,14 @@ msgstr ""
|
||||
msgid "%s"
|
||||
msgstr ""
|
||||
|
||||
#
|
||||
#, c-format
|
||||
msgid "%s %s: unrecognized feature '%ls'"
|
||||
msgstr ""
|
||||
|
||||
#
|
||||
#, c-format
|
||||
msgid "%s and %s are mutually exclusive"
|
||||
msgstr ""
|
||||
|
||||
#
|
||||
#, c-format
|
||||
msgid "%s could not read response to Primary Device Attribute query after waiting for %d seconds. This is often due to a missing feature in your terminal. See 'help terminal-compatibility' or 'man fish-terminal-compatibility'. This %s process will no longer wait for outstanding queries, which disables some optional features."
|
||||
msgstr ""
|
||||
@@ -1017,7 +1008,6 @@ msgstr "Une erreur est survenue lors du paramétrage du tube"
|
||||
msgid "Argument is not a number: '%ls'"
|
||||
msgstr ""
|
||||
|
||||
#
|
||||
msgid "Await background process completion"
|
||||
msgstr ""
|
||||
|
||||
@@ -1222,7 +1212,6 @@ msgstr ""
|
||||
msgid "Failed to assign shell to its own process group"
|
||||
msgstr ""
|
||||
|
||||
#
|
||||
#, c-format
|
||||
msgid "Failed to set terminal mode (%s)"
|
||||
msgstr ""
|
||||
@@ -1626,7 +1615,6 @@ msgstr "Arrêter la fonction actuellement en évaluation"
|
||||
msgid "Stop the innermost loop"
|
||||
msgstr "Arrêter la boucle interne"
|
||||
|
||||
#
|
||||
msgid "Synchronized file access"
|
||||
msgstr ""
|
||||
|
||||
@@ -1841,7 +1829,6 @@ msgstr "Les variables ne peuvent être placées entre crochets. Dans fish, veuil
|
||||
msgid "Variables cannot be bracketed. In fish, please use {$%ls}."
|
||||
msgstr "Les variables ne peuvent être placées entre crochets. Dans fish, veuillez utiliser {$%ls}."
|
||||
|
||||
#
|
||||
msgid "Virtual timer expired"
|
||||
msgstr ""
|
||||
|
||||
@@ -2005,7 +1992,9 @@ msgstr ""
|
||||
msgid "|& is not valid. In fish, use &| to pipe both stdout and stderr."
|
||||
msgstr ""
|
||||
|
||||
# fish-section-tier1-from-script-explicitly-added
|
||||
msgid "fish-section-tier1-from-script-explicitly-added"
|
||||
msgstr ""
|
||||
|
||||
msgid "%ls: %ls: expected %d arguments; got %d\\n"
|
||||
msgstr ""
|
||||
|
||||
@@ -2228,7 +2217,9 @@ msgstr "ou le fichier était vide"
|
||||
msgid "python executable not found"
|
||||
msgstr "Exécutable python introuvable"
|
||||
|
||||
# fish-section-tier1-from-script-implicitly-added
|
||||
msgid "fish-section-tier1-from-script-implicitly-added"
|
||||
msgstr ""
|
||||
|
||||
msgid "# comments"
|
||||
msgstr ""
|
||||
|
||||
@@ -3930,7 +3921,12 @@ msgstr ""
|
||||
msgid "~ expansion"
|
||||
msgstr ""
|
||||
|
||||
# fish-section-tier3-from-script-implicitly-added
|
||||
msgid "fish-section-tier3-from-script-explicitly-added"
|
||||
msgstr ""
|
||||
|
||||
msgid "fish-section-tier3-from-script-implicitly-added"
|
||||
msgstr ""
|
||||
|
||||
msgid " "
|
||||
msgstr ""
|
||||
|
||||
|
||||
36
po/pl.po
36
po/pl.po
@@ -1,4 +1,4 @@
|
||||
# fish-note-sections: Translations are divided into sections, each starting with a fish-section-* comment.
|
||||
# fish-note-sections: Translations are divided into sections, each starting with a fish-section-* pseudo-message.
|
||||
# fish-note-sections: The first few sections are more important.
|
||||
# fish-note-sections: Ignore the tier3 sections unless you have a lot of time.
|
||||
# Translation of fish in Polish
|
||||
@@ -17,13 +17,15 @@ msgstr ""
|
||||
"Plural-Forms: nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n"
|
||||
"X-Generator: GlotPress/2.2.2\n"
|
||||
|
||||
msgid "fish-section-tier1-from-rust"
|
||||
msgstr ""
|
||||
|
||||
msgid ""
|
||||
"\n"
|
||||
" PID Command\n"
|
||||
msgstr ""
|
||||
|
||||
#, c-format
|
||||
# fish-section-tier1-from-rust
|
||||
msgid " (%ls)\n"
|
||||
msgstr ""
|
||||
|
||||
@@ -194,7 +196,6 @@ msgstr ""
|
||||
msgid "%ls: %ls: invalid variable name. See `help identifiers`\n"
|
||||
msgstr ""
|
||||
|
||||
#
|
||||
#, c-format
|
||||
msgid "%ls: %ls: option does not take an argument\n"
|
||||
msgstr ""
|
||||
@@ -375,7 +376,6 @@ msgstr "%ls: Oczekiwano dokładnie dwóch nazw (nazwa obecnej funkcji i nazwa no
|
||||
msgid "%ls: Expected generic | variable | signal | exit | job-id for --handlers-type\n"
|
||||
msgstr ""
|
||||
|
||||
#
|
||||
#, c-format
|
||||
msgid "%ls: Function '%ls' already exists. Cannot create copy of '%ls'\n"
|
||||
msgstr ""
|
||||
@@ -404,7 +404,6 @@ msgstr ""
|
||||
msgid "%ls: Invalid --min-args value '%ls'\n"
|
||||
msgstr ""
|
||||
|
||||
#
|
||||
#, c-format
|
||||
msgid "%ls: Invalid --unknown-arguments value '%ls'\n"
|
||||
msgstr ""
|
||||
@@ -697,15 +696,10 @@ msgstr ""
|
||||
msgid "%ls: expected a numeric value"
|
||||
msgstr "%ls: oczekiwano wartości liczbowej"
|
||||
|
||||
#
|
||||
#, c-format
|
||||
msgid "%ls: fish was not built with embedded files"
|
||||
msgstr ""
|
||||
|
||||
#, c-format
|
||||
msgid "%ls: function name required"
|
||||
msgstr ""
|
||||
|
||||
#, c-format
|
||||
msgid "%ls: given %d indexes but %d values\n"
|
||||
msgstr ""
|
||||
@@ -798,17 +792,14 @@ msgstr ""
|
||||
msgid "%s"
|
||||
msgstr ""
|
||||
|
||||
#
|
||||
#, c-format
|
||||
msgid "%s %s: unrecognized feature '%ls'"
|
||||
msgstr ""
|
||||
|
||||
#
|
||||
#, c-format
|
||||
msgid "%s and %s are mutually exclusive"
|
||||
msgstr ""
|
||||
|
||||
#
|
||||
#, c-format
|
||||
msgid "%s could not read response to Primary Device Attribute query after waiting for %d seconds. This is often due to a missing feature in your terminal. See 'help terminal-compatibility' or 'man fish-terminal-compatibility'. This %s process will no longer wait for outstanding queries, which disables some optional features."
|
||||
msgstr ""
|
||||
@@ -912,7 +903,6 @@ msgstr ""
|
||||
msgid "Argument is not a number: '%ls'"
|
||||
msgstr ""
|
||||
|
||||
#
|
||||
msgid "Await background process completion"
|
||||
msgstr ""
|
||||
|
||||
@@ -1117,7 +1107,6 @@ msgstr ""
|
||||
msgid "Failed to assign shell to its own process group"
|
||||
msgstr ""
|
||||
|
||||
#
|
||||
#, c-format
|
||||
msgid "Failed to set terminal mode (%s)"
|
||||
msgstr ""
|
||||
@@ -1521,7 +1510,6 @@ msgstr "Zatrzymaj obecnie używaną funkcję"
|
||||
msgid "Stop the innermost loop"
|
||||
msgstr ""
|
||||
|
||||
#
|
||||
msgid "Synchronized file access"
|
||||
msgstr ""
|
||||
|
||||
@@ -1736,7 +1724,6 @@ msgstr "Zmienne nie mogą znajdować się w nawiasach. W fish używane jest \"$%
|
||||
msgid "Variables cannot be bracketed. In fish, please use {$%ls}."
|
||||
msgstr "Zmienne nie mogą znajdować się w nawiasach. W fish używane jest {$%ls}."
|
||||
|
||||
#
|
||||
msgid "Virtual timer expired"
|
||||
msgstr ""
|
||||
|
||||
@@ -1900,7 +1887,9 @@ msgstr ""
|
||||
msgid "|& is not valid. In fish, use &| to pipe both stdout and stderr."
|
||||
msgstr ""
|
||||
|
||||
# fish-section-tier1-from-script-explicitly-added
|
||||
msgid "fish-section-tier1-from-script-explicitly-added"
|
||||
msgstr ""
|
||||
|
||||
msgid "%ls: %ls: expected %d arguments; got %d\\n"
|
||||
msgstr ""
|
||||
|
||||
@@ -2123,7 +2112,9 @@ msgstr ""
|
||||
msgid "python executable not found"
|
||||
msgstr ""
|
||||
|
||||
# fish-section-tier1-from-script-implicitly-added
|
||||
msgid "fish-section-tier1-from-script-implicitly-added"
|
||||
msgstr ""
|
||||
|
||||
msgid "# comments"
|
||||
msgstr ""
|
||||
|
||||
@@ -3825,7 +3816,12 @@ msgstr ""
|
||||
msgid "~ expansion"
|
||||
msgstr ""
|
||||
|
||||
# fish-section-tier3-from-script-implicitly-added
|
||||
msgid "fish-section-tier3-from-script-explicitly-added"
|
||||
msgstr ""
|
||||
|
||||
msgid "fish-section-tier3-from-script-implicitly-added"
|
||||
msgstr ""
|
||||
|
||||
msgid " "
|
||||
msgstr ""
|
||||
|
||||
|
||||
58
po/pt_BR.po
58
po/pt_BR.po
@@ -1,4 +1,4 @@
|
||||
# fish-note-sections: Translations are divided into sections, each starting with a fish-section-* comment.
|
||||
# fish-note-sections: Translations are divided into sections, each starting with a fish-section-* pseudo-message.
|
||||
# fish-note-sections: The first few sections are more important.
|
||||
# fish-note-sections: Ignore the tier3 sections unless you have a lot of time.
|
||||
# Portuguese translations for fish package.
|
||||
@@ -22,13 +22,15 @@ msgstr ""
|
||||
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
|
||||
"X-Generator: Poedit 1.8.4\n"
|
||||
|
||||
msgid "fish-section-tier1-from-rust"
|
||||
msgstr ""
|
||||
|
||||
msgid ""
|
||||
"\n"
|
||||
" PID Command\n"
|
||||
msgstr ""
|
||||
|
||||
#, c-format
|
||||
# fish-section-tier1-from-rust
|
||||
msgid " (%ls)\n"
|
||||
msgstr ""
|
||||
|
||||
@@ -199,7 +201,6 @@ msgstr ""
|
||||
msgid "%ls: %ls: invalid variable name. See `help identifiers`\n"
|
||||
msgstr ""
|
||||
|
||||
#
|
||||
#, c-format
|
||||
msgid "%ls: %ls: option does not take an argument\n"
|
||||
msgstr ""
|
||||
@@ -380,7 +381,6 @@ msgstr "%ls: Esperava exatamente dois nomes (nome atual da função, e novo nome
|
||||
msgid "%ls: Expected generic | variable | signal | exit | job-id for --handlers-type\n"
|
||||
msgstr ""
|
||||
|
||||
#
|
||||
#, c-format
|
||||
msgid "%ls: Function '%ls' already exists. Cannot create copy of '%ls'\n"
|
||||
msgstr ""
|
||||
@@ -409,7 +409,6 @@ msgstr ""
|
||||
msgid "%ls: Invalid --min-args value '%ls'\n"
|
||||
msgstr ""
|
||||
|
||||
#
|
||||
#, c-format
|
||||
msgid "%ls: Invalid --unknown-arguments value '%ls'\n"
|
||||
msgstr ""
|
||||
@@ -702,15 +701,10 @@ msgstr ""
|
||||
msgid "%ls: expected a numeric value"
|
||||
msgstr "%ls: esperava valor numérico"
|
||||
|
||||
#
|
||||
#, c-format
|
||||
msgid "%ls: fish was not built with embedded files"
|
||||
msgstr ""
|
||||
|
||||
#, c-format
|
||||
msgid "%ls: function name required"
|
||||
msgstr ""
|
||||
|
||||
#, c-format
|
||||
msgid "%ls: given %d indexes but %d values\n"
|
||||
msgstr ""
|
||||
@@ -803,17 +797,14 @@ msgstr ""
|
||||
msgid "%s"
|
||||
msgstr ""
|
||||
|
||||
#
|
||||
#, c-format
|
||||
msgid "%s %s: unrecognized feature '%ls'"
|
||||
msgstr ""
|
||||
|
||||
#
|
||||
#, c-format
|
||||
msgid "%s and %s are mutually exclusive"
|
||||
msgstr ""
|
||||
|
||||
#
|
||||
#, c-format
|
||||
msgid "%s could not read response to Primary Device Attribute query after waiting for %d seconds. This is often due to a missing feature in your terminal. See 'help terminal-compatibility' or 'man fish-terminal-compatibility'. This %s process will no longer wait for outstanding queries, which disables some optional features."
|
||||
msgstr ""
|
||||
@@ -917,7 +908,6 @@ msgstr "Ocorreu um erro ao preparar pipe"
|
||||
msgid "Argument is not a number: '%ls'"
|
||||
msgstr ""
|
||||
|
||||
#
|
||||
msgid "Await background process completion"
|
||||
msgstr ""
|
||||
|
||||
@@ -1122,7 +1112,6 @@ msgstr ""
|
||||
msgid "Failed to assign shell to its own process group"
|
||||
msgstr ""
|
||||
|
||||
#
|
||||
#, c-format
|
||||
msgid "Failed to set terminal mode (%s)"
|
||||
msgstr ""
|
||||
@@ -1526,7 +1515,6 @@ msgstr "Pára a função em execução"
|
||||
msgid "Stop the innermost loop"
|
||||
msgstr "Pára o laço mais interno"
|
||||
|
||||
#
|
||||
msgid "Synchronized file access"
|
||||
msgstr ""
|
||||
|
||||
@@ -1741,7 +1729,6 @@ msgstr ""
|
||||
msgid "Variables cannot be bracketed. In fish, please use {$%ls}."
|
||||
msgstr ""
|
||||
|
||||
#
|
||||
msgid "Virtual timer expired"
|
||||
msgstr ""
|
||||
|
||||
@@ -1905,7 +1892,9 @@ msgstr ""
|
||||
msgid "|& is not valid. In fish, use &| to pipe both stdout and stderr."
|
||||
msgstr ""
|
||||
|
||||
# fish-section-tier1-from-script-explicitly-added
|
||||
msgid "fish-section-tier1-from-script-explicitly-added"
|
||||
msgstr ""
|
||||
|
||||
msgid "%ls: %ls: expected %d arguments; got %d\\n"
|
||||
msgstr ""
|
||||
|
||||
@@ -2128,7 +2117,9 @@ msgstr ""
|
||||
msgid "python executable not found"
|
||||
msgstr "executável python não encontrado"
|
||||
|
||||
# fish-section-tier1-from-script-implicitly-added
|
||||
msgid "fish-section-tier1-from-script-implicitly-added"
|
||||
msgstr ""
|
||||
|
||||
msgid "# comments"
|
||||
msgstr ""
|
||||
|
||||
@@ -3830,7 +3821,12 @@ msgstr ""
|
||||
msgid "~ expansion"
|
||||
msgstr ""
|
||||
|
||||
# fish-section-tier3-from-script-implicitly-added
|
||||
msgid "fish-section-tier3-from-script-explicitly-added"
|
||||
msgstr ""
|
||||
|
||||
msgid "fish-section-tier3-from-script-implicitly-added"
|
||||
msgstr ""
|
||||
|
||||
msgid " "
|
||||
msgstr ""
|
||||
|
||||
@@ -19608,11 +19604,6 @@ msgstr ""
|
||||
msgid "Do not list files that match the given pattern"
|
||||
msgstr ""
|
||||
|
||||
# Notas:
|
||||
# Adicionar nota
|
||||
#
|
||||
# Caminhos:
|
||||
# share/functions/__fish_complete_ls.fish:49
|
||||
msgid "Do not list implied entries matching specified shell pattern"
|
||||
msgstr "Não lista entradas com o padrão especificado"
|
||||
|
||||
@@ -26021,11 +26012,6 @@ msgstr ""
|
||||
msgid "File is on filesystem of specified type"
|
||||
msgstr ""
|
||||
|
||||
# Notas:
|
||||
# Adicionar nota
|
||||
#
|
||||
# Caminhos:
|
||||
# share/functions/__fish_complete_ls.fish:49
|
||||
msgid "File is symlink matching specified case insensitive pattern"
|
||||
msgstr ""
|
||||
|
||||
@@ -36562,12 +36548,6 @@ msgstr ""
|
||||
msgid "Machine-readable description of custom origin"
|
||||
msgstr ""
|
||||
|
||||
# Notas:
|
||||
# Adicionar nota
|
||||
#
|
||||
# Caminhos:
|
||||
# proc.cpp:1279
|
||||
# proc.cpp:1305
|
||||
msgid "Mail address to send alerts to"
|
||||
msgstr ""
|
||||
|
||||
@@ -46066,12 +46046,6 @@ msgstr ""
|
||||
msgid "Process last file first"
|
||||
msgstr ""
|
||||
|
||||
# Notas:
|
||||
# Adicionar nota
|
||||
#
|
||||
# Caminhos:
|
||||
# proc.cpp:1279
|
||||
# proc.cpp:1305
|
||||
msgid "Process log file"
|
||||
msgstr ""
|
||||
|
||||
|
||||
38
po/sv.po
38
po/sv.po
@@ -1,4 +1,4 @@
|
||||
# fish-note-sections: Translations are divided into sections, each starting with a fish-section-* comment.
|
||||
# fish-note-sections: Translations are divided into sections, each starting with a fish-section-* pseudo-message.
|
||||
# fish-note-sections: The first few sections are more important.
|
||||
# fish-note-sections: Ignore the tier3 sections unless you have a lot of time.
|
||||
# Copyright © 2006
|
||||
@@ -18,13 +18,15 @@ msgstr ""
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
|
||||
msgid "fish-section-tier1-from-rust"
|
||||
msgstr ""
|
||||
|
||||
msgid ""
|
||||
"\n"
|
||||
" PID Command\n"
|
||||
msgstr ""
|
||||
|
||||
#, c-format
|
||||
# fish-section-tier1-from-rust
|
||||
msgid " (%ls)\n"
|
||||
msgstr ""
|
||||
|
||||
@@ -195,7 +197,6 @@ msgstr ""
|
||||
msgid "%ls: %ls: invalid variable name. See `help identifiers`\n"
|
||||
msgstr ""
|
||||
|
||||
#
|
||||
#, c-format
|
||||
msgid "%ls: %ls: option does not take an argument\n"
|
||||
msgstr ""
|
||||
@@ -376,7 +377,6 @@ msgstr ""
|
||||
msgid "%ls: Expected generic | variable | signal | exit | job-id for --handlers-type\n"
|
||||
msgstr ""
|
||||
|
||||
#
|
||||
#, c-format
|
||||
msgid "%ls: Function '%ls' already exists. Cannot create copy of '%ls'\n"
|
||||
msgstr ""
|
||||
@@ -405,7 +405,6 @@ msgstr ""
|
||||
msgid "%ls: Invalid --min-args value '%ls'\n"
|
||||
msgstr ""
|
||||
|
||||
#
|
||||
#, c-format
|
||||
msgid "%ls: Invalid --unknown-arguments value '%ls'\n"
|
||||
msgstr ""
|
||||
@@ -698,15 +697,10 @@ msgstr ""
|
||||
msgid "%ls: expected a numeric value"
|
||||
msgstr ""
|
||||
|
||||
#
|
||||
#, c-format
|
||||
msgid "%ls: fish was not built with embedded files"
|
||||
msgstr ""
|
||||
|
||||
#, c-format
|
||||
msgid "%ls: function name required"
|
||||
msgstr ""
|
||||
|
||||
#, c-format
|
||||
msgid "%ls: given %d indexes but %d values\n"
|
||||
msgstr ""
|
||||
@@ -799,17 +793,14 @@ msgstr ""
|
||||
msgid "%s"
|
||||
msgstr ""
|
||||
|
||||
#
|
||||
#, c-format
|
||||
msgid "%s %s: unrecognized feature '%ls'"
|
||||
msgstr ""
|
||||
|
||||
#
|
||||
#, c-format
|
||||
msgid "%s and %s are mutually exclusive"
|
||||
msgstr ""
|
||||
|
||||
#
|
||||
#, c-format
|
||||
msgid "%s could not read response to Primary Device Attribute query after waiting for %d seconds. This is often due to a missing feature in your terminal. See 'help terminal-compatibility' or 'man fish-terminal-compatibility'. This %s process will no longer wait for outstanding queries, which disables some optional features."
|
||||
msgstr ""
|
||||
@@ -913,7 +904,6 @@ msgstr "Ett fel inträffade under skapandet av ett rör"
|
||||
msgid "Argument is not a number: '%ls'"
|
||||
msgstr ""
|
||||
|
||||
#
|
||||
msgid "Await background process completion"
|
||||
msgstr ""
|
||||
|
||||
@@ -1118,7 +1108,6 @@ msgstr ""
|
||||
msgid "Failed to assign shell to its own process group"
|
||||
msgstr ""
|
||||
|
||||
#
|
||||
#, c-format
|
||||
msgid "Failed to set terminal mode (%s)"
|
||||
msgstr ""
|
||||
@@ -1522,7 +1511,6 @@ msgstr "Avbryt den nuvarande funktionen"
|
||||
msgid "Stop the innermost loop"
|
||||
msgstr "Avbryt den innersta loopen"
|
||||
|
||||
#
|
||||
msgid "Synchronized file access"
|
||||
msgstr ""
|
||||
|
||||
@@ -1737,7 +1725,6 @@ msgstr ""
|
||||
msgid "Variables cannot be bracketed. In fish, please use {$%ls}."
|
||||
msgstr ""
|
||||
|
||||
#
|
||||
msgid "Virtual timer expired"
|
||||
msgstr ""
|
||||
|
||||
@@ -1901,7 +1888,9 @@ msgstr ""
|
||||
msgid "|& is not valid. In fish, use &| to pipe both stdout and stderr."
|
||||
msgstr ""
|
||||
|
||||
# fish-section-tier1-from-script-explicitly-added
|
||||
msgid "fish-section-tier1-from-script-explicitly-added"
|
||||
msgstr ""
|
||||
|
||||
msgid "%ls: %ls: expected %d arguments; got %d\\n"
|
||||
msgstr ""
|
||||
|
||||
@@ -2073,14 +2062,12 @@ msgstr ""
|
||||
msgid "Too many args for cd command"
|
||||
msgstr ""
|
||||
|
||||
#
|
||||
msgid "Type %shelp%s for instructions on how to use fish"
|
||||
msgstr "Skriv %shelp%s för instruktioner om hur man använder fish"
|
||||
|
||||
msgid "Warning: the file containing this function has not been saved. Changes may be lost when fish is closed."
|
||||
msgstr ""
|
||||
|
||||
#
|
||||
msgid "Welcome to fish, the friendly interactive shell"
|
||||
msgstr "Välkommen till fish, det vänliga interaktiva skalet"
|
||||
|
||||
@@ -2126,7 +2113,9 @@ msgstr ""
|
||||
msgid "python executable not found"
|
||||
msgstr ""
|
||||
|
||||
# fish-section-tier1-from-script-implicitly-added
|
||||
msgid "fish-section-tier1-from-script-implicitly-added"
|
||||
msgstr ""
|
||||
|
||||
msgid "# comments"
|
||||
msgstr ""
|
||||
|
||||
@@ -3828,7 +3817,12 @@ msgstr ""
|
||||
msgid "~ expansion"
|
||||
msgstr ""
|
||||
|
||||
# fish-section-tier3-from-script-implicitly-added
|
||||
msgid "fish-section-tier3-from-script-explicitly-added"
|
||||
msgstr ""
|
||||
|
||||
msgid "fish-section-tier3-from-script-implicitly-added"
|
||||
msgstr ""
|
||||
|
||||
msgid " "
|
||||
msgstr ""
|
||||
|
||||
|
||||
60
po/zh_CN.po
60
po/zh_CN.po
@@ -1,20 +1,7 @@
|
||||
# fish-note-sections: Translations are divided into sections, each starting with a fish-section-* comment.
|
||||
# fish-note-sections: Translations are divided into sections, each starting with a fish-section-* pseudo-message.
|
||||
# fish-note-sections: The first few sections are more important.
|
||||
# fish-note-sections: Ignore the tier3 sections unless you have a lot of time.
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: fish 1.21.8\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2024-03-10 16:38+0100\n"
|
||||
"PO-Revision-Date: 2025-09-30 19:14+0800\n"
|
||||
"Last-Translator: Yuyi Wang <Strawberry_Str@hotmail.com>\n"
|
||||
"Language-Team: \n"
|
||||
"Language: zh_CN\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Generator: Poedit 3.7\n"
|
||||
|
||||
#
|
||||
# General Notices 凡例
|
||||
# abort: 中止
|
||||
# argument: 参数
|
||||
@@ -41,6 +28,23 @@ msgstr ""
|
||||
# token: 记号
|
||||
# universal (variable): 通用
|
||||
# valid & invalid: 有效 & 无效
|
||||
msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: fish 1.21.8\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2024-03-10 16:38+0100\n"
|
||||
"PO-Revision-Date: 2025-09-30 19:14+0800\n"
|
||||
"Last-Translator: Yuyi Wang <Strawberry_Str@hotmail.com>\n"
|
||||
"Language-Team: \n"
|
||||
"Language: zh_CN\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
"Content-Transfer-Encoding: 8bit\n"
|
||||
"X-Generator: Poedit 3.7\n"
|
||||
|
||||
msgid "fish-section-tier1-from-rust"
|
||||
msgstr ""
|
||||
|
||||
msgid ""
|
||||
"\n"
|
||||
" PID Command\n"
|
||||
@@ -49,7 +53,6 @@ msgstr ""
|
||||
" PID 命令\n"
|
||||
|
||||
#, c-format
|
||||
# fish-section-tier1-from-rust
|
||||
msgid " (%ls)\n"
|
||||
msgstr " (%ls)\n"
|
||||
|
||||
@@ -724,10 +727,6 @@ msgstr "%ls: 预期收到数值"
|
||||
msgid "%ls: fish was not built with embedded files"
|
||||
msgstr "%ls: fish 构建时未包含嵌入文件"
|
||||
|
||||
#, c-format
|
||||
msgid "%ls: function name required"
|
||||
msgstr "%ls: 函数名称是必须的"
|
||||
|
||||
#, c-format
|
||||
msgid "%ls: given %d indexes but %d values\n"
|
||||
msgstr "%ls: 给定索引 %d 但只有 %d 个值\n"
|
||||
@@ -1921,7 +1920,9 @@ msgstr "无限制\n"
|
||||
msgid "|& is not valid. In fish, use &| to pipe both stdout and stderr."
|
||||
msgstr "|& 无效。在 fish 中,用 &| 来同时管道链接 stdout 和 stderr。"
|
||||
|
||||
# fish-section-tier1-from-script-explicitly-added
|
||||
msgid "fish-section-tier1-from-script-explicitly-added"
|
||||
msgstr ""
|
||||
|
||||
msgid "%ls: %ls: expected %d arguments; got %d\\n"
|
||||
msgstr "%ls: %ls: 期望 %d 个参数;收到 %d 个\\n"
|
||||
|
||||
@@ -2144,7 +2145,9 @@ msgstr "或文件为空"
|
||||
msgid "python executable not found"
|
||||
msgstr "找不到 python 可执行文件"
|
||||
|
||||
# fish-section-tier1-from-script-implicitly-added
|
||||
msgid "fish-section-tier1-from-script-implicitly-added"
|
||||
msgstr ""
|
||||
|
||||
msgid "# comments"
|
||||
msgstr "# 注释"
|
||||
|
||||
@@ -3655,7 +3658,7 @@ msgid "Vi mode commands"
|
||||
msgstr "Vi 模式命令"
|
||||
|
||||
msgid "Vi-style bindings that inherit emacs-style bindings in all modes"
|
||||
msgstr "所有模式中的继承 emacs 样式绑定的 Vi 样式绑定"
|
||||
msgstr "所有模式中的继承 emacs 样式绑定的 vi 样式绑定"
|
||||
|
||||
msgid "View and pick from the sample prompts"
|
||||
msgstr "查看并从示例提示中选择"
|
||||
@@ -3670,7 +3673,7 @@ msgid "What characters are allowed in names"
|
||||
msgstr "名称中允许什么字符"
|
||||
|
||||
msgid "What set -x does"
|
||||
msgstr "设置 -x 做什么"
|
||||
msgstr "`set -x` 做什么"
|
||||
|
||||
msgid "Where to direct debug output to"
|
||||
msgstr "将调试输出导向何处"
|
||||
@@ -3846,7 +3849,12 @@ msgstr "{a,b} 大括号展开"
|
||||
msgid "~ expansion"
|
||||
msgstr "~ 展开"
|
||||
|
||||
# fish-section-tier3-from-script-implicitly-added
|
||||
msgid "fish-section-tier3-from-script-explicitly-added"
|
||||
msgstr ""
|
||||
|
||||
msgid "fish-section-tier3-from-script-implicitly-added"
|
||||
msgstr ""
|
||||
|
||||
msgid " "
|
||||
msgstr " "
|
||||
|
||||
@@ -40052,7 +40060,7 @@ msgid "Operate quietly and do not report progress"
|
||||
msgstr "安静操作,不报告进度"
|
||||
|
||||
msgid "Operate recursively"
|
||||
msgstr "递归操"
|
||||
msgstr "递归操作"
|
||||
|
||||
msgid "Operate recursively on datasets"
|
||||
msgstr "在数据集上递归操作"
|
||||
|
||||
@@ -10,8 +10,7 @@ set -l erase_line "$(
|
||||
function __fish_echo --inherit-variable erase_line --description 'run the given command after the current commandline and redraw the prompt'
|
||||
set -l line (commandline --line)
|
||||
string >&2 repeat -N \n --count=(math (commandline | count) - $line + 1)
|
||||
printf %s $erase_line >&2
|
||||
$argv >&2
|
||||
printf %s\n $erase_line($argv) >&2
|
||||
string >&2 repeat -N \n --count=(math (count (fish_prompt)) - 1)
|
||||
string >&2 repeat -N \n --count=(math $line - 1)
|
||||
commandline -f repaint
|
||||
|
||||
@@ -87,11 +87,11 @@ function fish_config --description "Launch fish's web based configuration"
|
||||
return 1
|
||||
end
|
||||
|
||||
set -l prompt_dir $__fish_data_dir/sample_prompts $__fish_data_dir/tools/web_config/sample_prompts
|
||||
set -l prompt_dir $__fish_data_dir/tools/web_config/sample_prompts
|
||||
switch $cmd
|
||||
case show
|
||||
set -l fish (status fish-path)
|
||||
set -l prompts (dirs=$prompt_dir __fish_config_matching tools/web_config/sample_prompts .fish $argv)
|
||||
set -l prompts (__fish_config_matching tools/web_config/sample_prompts .fish $argv)
|
||||
for p in $prompts
|
||||
set -l promptname (string replace -r '.*/([^/]*).fish$' '$1' $p)
|
||||
echo -s (set_color --underline) $promptname (set_color normal)
|
||||
@@ -240,7 +240,8 @@ function fish_config --description "Launch fish's web based configuration"
|
||||
case show
|
||||
set -l fish (status fish-path)
|
||||
set -l themes \
|
||||
(dirs=$dirs __fish_config_matching tools/web_config/themes .theme $argv)
|
||||
(path filter $dirs/$argv.theme) \
|
||||
(__fish_config_matching tools/web_config/themes .theme $argv)
|
||||
set -l used_themes
|
||||
|
||||
echo -s (set_color normal; set_color --underline) Current (set_color normal)
|
||||
@@ -383,16 +384,17 @@ function __fish_config_matching
|
||||
set -l suffix $argv[2]
|
||||
set -e argv[1..2]
|
||||
set -l paths
|
||||
if not set -q argv[1]
|
||||
set paths $dirs/*$suffix
|
||||
else
|
||||
set paths (path filter $dirs/$argv$suffix)
|
||||
end
|
||||
if not set -q __fish_data_dir[1]
|
||||
if set -q __fish_data_dir[1]
|
||||
if not set -q argv[1]
|
||||
set -a paths (status list-files $prefix)
|
||||
set paths $__fish_data_dir/$prefix/*$suffix
|
||||
else
|
||||
set -a paths (status list-files $prefix | grep -Fx -e"$prefix/"$argv$suffix)
|
||||
set paths (path filter $__fish_data_dir/$prefix/$argv$suffix)
|
||||
end
|
||||
else
|
||||
if not set -q argv[1]
|
||||
set paths (status list-files $prefix)
|
||||
else
|
||||
set paths (status list-files $prefix | grep -Fx -e"$prefix/"$argv$suffix)
|
||||
end
|
||||
end
|
||||
string join \n $paths
|
||||
|
||||
172
src/ast.rs
172
src/ast.rs
@@ -24,6 +24,7 @@
|
||||
TOK_ACCEPT_UNFINISHED, TOK_ARGUMENT_LIST, TOK_CONTINUE_AFTER_ERROR, TOK_SHOW_COMMENTS,
|
||||
};
|
||||
use crate::wchar::prelude::*;
|
||||
use macro_rules_attribute::derive;
|
||||
use std::borrow::Cow;
|
||||
use std::convert::AsMut;
|
||||
use std::ops::{ControlFlow, Deref};
|
||||
@@ -404,8 +405,8 @@ trait CheckParse: Default {
|
||||
}
|
||||
|
||||
/// Implement the node trait.
|
||||
macro_rules! implement_node {
|
||||
( $name:ident ) => {
|
||||
macro_rules! Node {
|
||||
($name:ident) => {
|
||||
impl Node for $name {
|
||||
fn kind(&self) -> Kind<'_> {
|
||||
Kind::$name(self)
|
||||
@@ -425,11 +426,19 @@ fn cast(node: &dyn Node) -> Option<&Self> {
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
( $(#[$_m:meta])* $_v:vis struct $name:ident $_:tt $(;)? ) => {
|
||||
Node!($name);
|
||||
};
|
||||
|
||||
( $(#[$_m:meta])* $_v:vis enum $name:ident $_:tt ) => {
|
||||
Node!($name);
|
||||
};
|
||||
}
|
||||
|
||||
/// Implement the leaf trait.
|
||||
macro_rules! implement_leaf {
|
||||
( $name:ident ) => {
|
||||
macro_rules! Leaf {
|
||||
($name:ident) => {
|
||||
impl Leaf for $name {
|
||||
fn range(&self) -> Option<SourceRange> {
|
||||
self.range
|
||||
@@ -450,17 +459,20 @@ fn accept_mut<V: NodeVisitorMut>(&mut self, visitor: &mut V) {
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
( $(#[$_m:meta])* $_v:vis struct $name:ident $_:tt $(;)? ) => {
|
||||
Leaf!($name);
|
||||
};
|
||||
}
|
||||
|
||||
/// Define a node that implements the keyword trait.
|
||||
macro_rules! define_keyword_node {
|
||||
( $name:ident, $($allowed:ident),* $(,)? ) => {
|
||||
#[derive(Default, Debug)]
|
||||
#[derive(Default, Debug, Leaf!)]
|
||||
pub struct $name {
|
||||
range: Option<SourceRange>,
|
||||
keyword: ParseKeyword,
|
||||
}
|
||||
implement_leaf!($name);
|
||||
impl Node for $name {
|
||||
fn kind(&self) -> Kind<'_> {
|
||||
Kind::Keyword(self)
|
||||
@@ -489,7 +501,7 @@ fn as_leaf(&self) -> &dyn Leaf {
|
||||
/// Define a node that implements the token trait.
|
||||
macro_rules! define_token_node {
|
||||
( $name:ident, $($allowed:ident),* $(,)? ) => {
|
||||
#[derive(Default, Debug)]
|
||||
#[derive(Default, Debug, Leaf!)]
|
||||
pub struct $name {
|
||||
range: Option<SourceRange>,
|
||||
parse_token_type: ParseTokenType,
|
||||
@@ -502,7 +514,6 @@ fn kind_mut(&mut self) -> KindMut<'_> {
|
||||
KindMut::Token(self)
|
||||
}
|
||||
}
|
||||
implement_leaf!($name);
|
||||
impl Token for $name {
|
||||
fn token_type(&self) -> ParseTokenType {
|
||||
self.parse_token_type
|
||||
@@ -535,11 +546,9 @@ macro_rules! define_list_node {
|
||||
$name:ident,
|
||||
$contents:ident
|
||||
) => {
|
||||
#[derive(Default, Debug)]
|
||||
#[derive(Default, Debug, Node!)]
|
||||
pub struct $name(Box<[$contents]>);
|
||||
|
||||
implement_node!($name);
|
||||
|
||||
impl Deref for $name {
|
||||
type Target = Box<[$contents]>;
|
||||
fn deref(&self) -> &Self::Target {
|
||||
@@ -582,11 +591,15 @@ fn accept_mut<V: NodeVisitorMut>(&mut self, visitor: &mut V) {
|
||||
}
|
||||
|
||||
/// Implement the acceptor trait for the given branch node.
|
||||
macro_rules! implement_acceptor_for_branch {
|
||||
macro_rules! Acceptor {
|
||||
(
|
||||
$name:ident
|
||||
$(, $field_name:ident )*
|
||||
$(,)?
|
||||
$(#[$_m:meta])*
|
||||
$_v:vis struct $name:ident {
|
||||
$(
|
||||
$(#[$_fm:meta])*
|
||||
$_fv:vis $field_name:ident : $_ft:ty
|
||||
),* $(,)?
|
||||
}
|
||||
) => {
|
||||
impl Acceptor for $name {
|
||||
#[allow(unused_variables)]
|
||||
@@ -612,18 +625,16 @@ fn accept_mut<V: NodeVisitorMut>(&mut self, visitor: &mut V) {
|
||||
visitor.did_visit_fields_of(self, flow);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// A redirection has an operator like > or 2>, and a target like /dev/null or &1.
|
||||
/// Note that pipes are not redirections.
|
||||
#[derive(Default, Debug)]
|
||||
#[derive(Default, Debug, Node!, Acceptor!)]
|
||||
pub struct Redirection {
|
||||
pub oper: TokenRedirection,
|
||||
pub target: String_,
|
||||
}
|
||||
implement_node!(Redirection);
|
||||
implement_acceptor_for_branch!(Redirection, oper, target);
|
||||
|
||||
impl CheckParse for Redirection {
|
||||
fn can_be_parsed(pop: &mut Populator<'_>) -> bool {
|
||||
@@ -633,7 +644,7 @@ fn can_be_parsed(pop: &mut Populator<'_>) -> bool {
|
||||
|
||||
define_list_node!(VariableAssignmentList, VariableAssignment);
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Node!)]
|
||||
pub enum ArgumentOrRedirection {
|
||||
Argument(Argument),
|
||||
Redirection(Box<Redirection>), // Boxed because it's bigger
|
||||
@@ -689,8 +700,6 @@ pub fn redirection(&self) -> &Redirection {
|
||||
}
|
||||
}
|
||||
|
||||
implement_node!(ArgumentOrRedirection);
|
||||
|
||||
impl CheckParse for ArgumentOrRedirection {
|
||||
fn can_be_parsed(pop: &mut Populator<'_>) -> bool {
|
||||
let typ = pop.peek_type(0);
|
||||
@@ -701,7 +710,7 @@ fn can_be_parsed(pop: &mut Populator<'_>) -> bool {
|
||||
define_list_node!(ArgumentOrRedirectionList, ArgumentOrRedirection);
|
||||
|
||||
/// A statement is a normal command, or an if / while / etc
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Node!)]
|
||||
pub enum Statement {
|
||||
Decorated(DecoratedStatement),
|
||||
Not(Box<NotStatement>),
|
||||
@@ -710,7 +719,6 @@ pub enum Statement {
|
||||
If(Box<IfStatement>),
|
||||
Switch(Box<SwitchStatement>),
|
||||
}
|
||||
implement_node!(Statement);
|
||||
|
||||
impl Default for Statement {
|
||||
fn default() -> Self {
|
||||
@@ -756,7 +764,7 @@ fn accept_mut<V: NodeVisitorMut>(&mut self, visitor: &mut V) {
|
||||
|
||||
/// A job is a non-empty list of statements, separated by pipes. (Non-empty is useful for cases
|
||||
/// like if statements, where we require a command).
|
||||
#[derive(Default, Debug)]
|
||||
#[derive(Default, Debug, Node!, Acceptor!)]
|
||||
pub struct JobPipeline {
|
||||
/// Maybe the time keyword.
|
||||
pub time: Option<KeywordTime>,
|
||||
@@ -769,11 +777,9 @@ pub struct JobPipeline {
|
||||
/// Maybe backgrounded.
|
||||
pub bg: Option<TokenBackground>,
|
||||
}
|
||||
implement_node!(JobPipeline);
|
||||
implement_acceptor_for_branch!(JobPipeline, time, variables, statement, continuation, bg);
|
||||
|
||||
/// A job_conjunction is a job followed by a && or || continuations.
|
||||
#[derive(Default, Debug)]
|
||||
#[derive(Default, Debug, Node!, Acceptor!)]
|
||||
pub struct JobConjunction {
|
||||
/// The job conjunction decorator.
|
||||
pub decorator: Option<JobConjunctionDecorator>,
|
||||
@@ -786,8 +792,6 @@ pub struct JobConjunction {
|
||||
/// only fail to be present if we ran out of tokens.
|
||||
pub semi_nl: Option<SemiNl>,
|
||||
}
|
||||
implement_node!(JobConjunction);
|
||||
implement_acceptor_for_branch!(JobConjunction, decorator, job, continuations, semi_nl);
|
||||
|
||||
impl CheckParse for JobConjunction {
|
||||
fn can_be_parsed(pop: &mut Populator<'_>) -> bool {
|
||||
@@ -802,7 +806,7 @@ fn can_be_parsed(pop: &mut Populator<'_>) -> bool {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default, Debug)]
|
||||
#[derive(Default, Debug, Node!, Acceptor!)]
|
||||
pub struct ForHeader {
|
||||
/// 'for'
|
||||
pub kw_for: KeywordFor,
|
||||
@@ -815,20 +819,16 @@ pub struct ForHeader {
|
||||
/// newline or semicolon
|
||||
pub semi_nl: SemiNl,
|
||||
}
|
||||
implement_node!(ForHeader);
|
||||
implement_acceptor_for_branch!(ForHeader, kw_for, var_name, kw_in, args, semi_nl);
|
||||
|
||||
#[derive(Default, Debug)]
|
||||
#[derive(Default, Debug, Node!, Acceptor!)]
|
||||
pub struct WhileHeader {
|
||||
/// 'while'
|
||||
pub kw_while: KeywordWhile,
|
||||
pub condition: JobConjunction,
|
||||
pub andor_tail: AndorJobList,
|
||||
}
|
||||
implement_node!(WhileHeader);
|
||||
implement_acceptor_for_branch!(WhileHeader, kw_while, condition, andor_tail);
|
||||
|
||||
#[derive(Default, Debug)]
|
||||
#[derive(Default, Debug, Node!, Acceptor!)]
|
||||
pub struct FunctionHeader {
|
||||
pub kw_function: KeywordFunction,
|
||||
/// functions require at least one argument.
|
||||
@@ -836,20 +836,16 @@ pub struct FunctionHeader {
|
||||
pub args: ArgumentList,
|
||||
pub semi_nl: SemiNl,
|
||||
}
|
||||
implement_node!(FunctionHeader);
|
||||
implement_acceptor_for_branch!(FunctionHeader, kw_function, first_arg, args, semi_nl);
|
||||
|
||||
#[derive(Default, Debug)]
|
||||
#[derive(Default, Debug, Node!, Acceptor!)]
|
||||
pub struct BeginHeader {
|
||||
pub kw_begin: KeywordBegin,
|
||||
/// Note that 'begin' does NOT require a semi or nl afterwards.
|
||||
/// This is valid: begin echo hi; end
|
||||
pub semi_nl: Option<SemiNl>,
|
||||
}
|
||||
implement_node!(BeginHeader);
|
||||
implement_acceptor_for_branch!(BeginHeader, kw_begin, semi_nl);
|
||||
|
||||
#[derive(Default, Debug)]
|
||||
#[derive(Default, Debug, Node!, Acceptor!)]
|
||||
pub struct BlockStatement {
|
||||
/// A header like for, while, etc.
|
||||
pub header: BlockStatementHeader,
|
||||
@@ -860,10 +856,8 @@ pub struct BlockStatement {
|
||||
/// Arguments and redirections associated with the block.
|
||||
pub args_or_redirs: ArgumentOrRedirectionList,
|
||||
}
|
||||
implement_node!(BlockStatement);
|
||||
implement_acceptor_for_branch!(BlockStatement, header, jobs, end, args_or_redirs);
|
||||
|
||||
#[derive(Default, Debug)]
|
||||
#[derive(Default, Debug, Node!, Acceptor!)]
|
||||
pub struct BraceStatement {
|
||||
/// The opening brace, in command position.
|
||||
pub left_brace: TokenLeftBrace,
|
||||
@@ -874,16 +868,8 @@ pub struct BraceStatement {
|
||||
/// Arguments and redirections associated with the block.
|
||||
pub args_or_redirs: ArgumentOrRedirectionList,
|
||||
}
|
||||
implement_node!(BraceStatement);
|
||||
implement_acceptor_for_branch!(
|
||||
BraceStatement,
|
||||
left_brace,
|
||||
jobs,
|
||||
right_brace,
|
||||
args_or_redirs
|
||||
);
|
||||
|
||||
#[derive(Default, Debug)]
|
||||
#[derive(Default, Debug, Node!, Acceptor!)]
|
||||
pub struct IfClause {
|
||||
/// The 'if' keyword.
|
||||
pub kw_if: KeywordIf,
|
||||
@@ -894,18 +880,14 @@ pub struct IfClause {
|
||||
/// The body to execute if the condition is true.
|
||||
pub body: JobList,
|
||||
}
|
||||
implement_node!(IfClause);
|
||||
implement_acceptor_for_branch!(IfClause, kw_if, condition, andor_tail, body);
|
||||
|
||||
#[derive(Default, Debug)]
|
||||
#[derive(Default, Debug, Node!, Acceptor!)]
|
||||
pub struct ElseifClause {
|
||||
/// The 'else' keyword.
|
||||
pub kw_else: KeywordElse,
|
||||
/// The 'if' clause following it.
|
||||
pub if_clause: IfClause,
|
||||
}
|
||||
implement_node!(ElseifClause);
|
||||
implement_acceptor_for_branch!(ElseifClause, kw_else, if_clause);
|
||||
impl CheckParse for ElseifClause {
|
||||
fn can_be_parsed(pop: &mut Populator<'_>) -> bool {
|
||||
pop.peek_token(0).keyword == ParseKeyword::Else
|
||||
@@ -915,22 +897,20 @@ fn can_be_parsed(pop: &mut Populator<'_>) -> bool {
|
||||
|
||||
define_list_node!(ElseifClauseList, ElseifClause);
|
||||
|
||||
#[derive(Default, Debug)]
|
||||
#[derive(Default, Debug, Node!, Acceptor!)]
|
||||
pub struct ElseClause {
|
||||
/// else ; body
|
||||
pub kw_else: KeywordElse,
|
||||
pub semi_nl: Option<SemiNl>,
|
||||
pub body: JobList,
|
||||
}
|
||||
implement_node!(ElseClause);
|
||||
implement_acceptor_for_branch!(ElseClause, kw_else, semi_nl, body);
|
||||
impl CheckParse for ElseClause {
|
||||
fn can_be_parsed(pop: &mut Populator<'_>) -> bool {
|
||||
pop.peek_token(0).keyword == ParseKeyword::Else
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default, Debug)]
|
||||
#[derive(Default, Debug, Node!, Acceptor!)]
|
||||
pub struct IfStatement {
|
||||
/// if part
|
||||
pub if_clause: IfClause,
|
||||
@@ -943,17 +923,8 @@ pub struct IfStatement {
|
||||
/// block args / redirs
|
||||
pub args_or_redirs: ArgumentOrRedirectionList,
|
||||
}
|
||||
implement_node!(IfStatement);
|
||||
implement_acceptor_for_branch!(
|
||||
IfStatement,
|
||||
if_clause,
|
||||
elseif_clauses,
|
||||
else_clause,
|
||||
end,
|
||||
args_or_redirs
|
||||
);
|
||||
|
||||
#[derive(Default, Debug)]
|
||||
#[derive(Default, Debug, Node!, Acceptor!)]
|
||||
pub struct CaseItem {
|
||||
/// case \<arguments\> ; body
|
||||
pub kw_case: KeywordCase,
|
||||
@@ -961,15 +932,13 @@ pub struct CaseItem {
|
||||
pub semi_nl: SemiNl,
|
||||
pub body: JobList,
|
||||
}
|
||||
implement_node!(CaseItem);
|
||||
implement_acceptor_for_branch!(CaseItem, kw_case, arguments, semi_nl, body);
|
||||
impl CheckParse for CaseItem {
|
||||
fn can_be_parsed(pop: &mut Populator<'_>) -> bool {
|
||||
pop.peek_token(0).keyword == ParseKeyword::Case
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default, Debug)]
|
||||
#[derive(Default, Debug, Node!, Acceptor!)]
|
||||
pub struct SwitchStatement {
|
||||
/// switch \<argument\> ; body ; end args_redirs
|
||||
pub kw_switch: KeywordSwitch,
|
||||
@@ -979,20 +948,10 @@ pub struct SwitchStatement {
|
||||
pub end: KeywordEnd,
|
||||
pub args_or_redirs: ArgumentOrRedirectionList,
|
||||
}
|
||||
implement_node!(SwitchStatement);
|
||||
implement_acceptor_for_branch!(
|
||||
SwitchStatement,
|
||||
kw_switch,
|
||||
argument,
|
||||
semi_nl,
|
||||
cases,
|
||||
end,
|
||||
args_or_redirs
|
||||
);
|
||||
|
||||
/// A decorated_statement is a command with a list of arguments_or_redirections, possibly with
|
||||
/// "builtin" or "command" or "exec"
|
||||
#[derive(Default, Debug)]
|
||||
#[derive(Default, Debug, Node!, Acceptor!)]
|
||||
pub struct DecoratedStatement {
|
||||
/// An optional decoration (command, builtin, exec, etc).
|
||||
pub opt_decoration: Option<DecoratedStatementDecorator>,
|
||||
@@ -1001,11 +960,9 @@ pub struct DecoratedStatement {
|
||||
/// Args and redirs
|
||||
pub args_or_redirs: ArgumentOrRedirectionList,
|
||||
}
|
||||
implement_node!(DecoratedStatement);
|
||||
implement_acceptor_for_branch!(DecoratedStatement, opt_decoration, command, args_or_redirs);
|
||||
|
||||
/// A not statement like `not true` or `! true`
|
||||
#[derive(Default, Debug)]
|
||||
#[derive(Default, Debug, Node!, Acceptor!)]
|
||||
pub struct NotStatement {
|
||||
/// Keyword, either not or exclam.
|
||||
pub kw: KeywordNot,
|
||||
@@ -1013,18 +970,14 @@ pub struct NotStatement {
|
||||
pub variables: VariableAssignmentList,
|
||||
pub contents: Statement,
|
||||
}
|
||||
implement_node!(NotStatement);
|
||||
implement_acceptor_for_branch!(NotStatement, kw, time, variables, contents);
|
||||
|
||||
#[derive(Default, Debug)]
|
||||
#[derive(Default, Debug, Node!, Acceptor!)]
|
||||
pub struct JobContinuation {
|
||||
pub pipe: TokenPipe,
|
||||
pub newlines: MaybeNewlines,
|
||||
pub variables: VariableAssignmentList,
|
||||
pub statement: Statement,
|
||||
}
|
||||
implement_node!(JobContinuation);
|
||||
implement_acceptor_for_branch!(JobContinuation, pipe, newlines, variables, statement);
|
||||
impl CheckParse for JobContinuation {
|
||||
fn can_be_parsed(pop: &mut Populator<'_>) -> bool {
|
||||
pop.peek_type(0) == ParseTokenType::pipe
|
||||
@@ -1033,7 +986,7 @@ fn can_be_parsed(pop: &mut Populator<'_>) -> bool {
|
||||
|
||||
define_list_node!(JobContinuationList, JobContinuation);
|
||||
|
||||
#[derive(Default, Debug)]
|
||||
#[derive(Default, Debug, Node!, Acceptor!)]
|
||||
pub struct JobConjunctionContinuation {
|
||||
/// The && or || token.
|
||||
pub conjunction: TokenConjunction,
|
||||
@@ -1041,8 +994,6 @@ pub struct JobConjunctionContinuation {
|
||||
/// The job itself.
|
||||
pub job: JobPipeline,
|
||||
}
|
||||
implement_node!(JobConjunctionContinuation);
|
||||
implement_acceptor_for_branch!(JobConjunctionContinuation, conjunction, newlines, job);
|
||||
impl CheckParse for JobConjunctionContinuation {
|
||||
fn can_be_parsed(pop: &mut Populator<'_>) -> bool {
|
||||
let typ = pop.peek_type(0);
|
||||
@@ -1053,12 +1004,10 @@ fn can_be_parsed(pop: &mut Populator<'_>) -> bool {
|
||||
/// An andor_job just wraps a job, but requires that the job have an 'and' or 'or' job_decorator.
|
||||
/// Note this is only used for andor_job_list; jobs that are not part of an andor_job_list are not
|
||||
/// instances of this.
|
||||
#[derive(Default, Debug)]
|
||||
#[derive(Default, Debug, Node!, Acceptor!)]
|
||||
pub struct AndorJob {
|
||||
pub job: JobConjunction,
|
||||
}
|
||||
implement_node!(AndorJob);
|
||||
implement_acceptor_for_branch!(AndorJob, job);
|
||||
impl CheckParse for AndorJob {
|
||||
fn can_be_parsed(pop: &mut Populator<'_>) -> bool {
|
||||
let keyword = pop.peek_token(0).keyword;
|
||||
@@ -1080,12 +1029,10 @@ fn can_be_parsed(pop: &mut Populator<'_>) -> bool {
|
||||
/// A freestanding_argument_list is equivalent to a normal argument list, except it may contain
|
||||
/// TOK_END (newlines, and even semicolons, for historical reasons).
|
||||
/// In practice the tok_ends are ignored by fish code so we do not bother to store them.
|
||||
#[derive(Default, Debug)]
|
||||
#[derive(Default, Debug, Node!, Acceptor!)]
|
||||
pub struct FreestandingArgumentList {
|
||||
pub arguments: ArgumentList,
|
||||
}
|
||||
implement_node!(FreestandingArgumentList);
|
||||
implement_acceptor_for_branch!(FreestandingArgumentList, arguments);
|
||||
|
||||
define_list_node!(JobConjunctionContinuationList, JobConjunctionContinuation);
|
||||
|
||||
@@ -1097,12 +1044,10 @@ pub struct FreestandingArgumentList {
|
||||
define_list_node!(CaseItemList, CaseItem);
|
||||
|
||||
/// A variable_assignment contains a source range like FOO=bar.
|
||||
#[derive(Default, Debug)]
|
||||
#[derive(Default, Debug, Node!, Leaf!)]
|
||||
pub struct VariableAssignment {
|
||||
range: Option<SourceRange>,
|
||||
}
|
||||
implement_node!(VariableAssignment);
|
||||
implement_leaf!(VariableAssignment);
|
||||
impl CheckParse for VariableAssignment {
|
||||
fn can_be_parsed(pop: &mut Populator<'_>) -> bool {
|
||||
// Do we have a variable assignment at all?
|
||||
@@ -1124,21 +1069,17 @@ fn can_be_parsed(pop: &mut Populator<'_>) -> bool {
|
||||
}
|
||||
|
||||
/// Zero or more newlines.
|
||||
#[derive(Default, Debug)]
|
||||
#[derive(Default, Debug, Node!, Leaf!)]
|
||||
pub struct MaybeNewlines {
|
||||
range: Option<SourceRange>,
|
||||
}
|
||||
implement_node!(MaybeNewlines);
|
||||
implement_leaf!(MaybeNewlines);
|
||||
|
||||
/// An argument is just a node whose source range determines its contents.
|
||||
/// This is a separate type because it is sometimes useful to find all arguments.
|
||||
#[derive(Default, Debug)]
|
||||
#[derive(Default, Debug, Node!, Leaf!)]
|
||||
pub struct Argument {
|
||||
range: Option<SourceRange>,
|
||||
}
|
||||
implement_node!(Argument);
|
||||
implement_leaf!(Argument);
|
||||
impl CheckParse for Argument {
|
||||
fn can_be_parsed(pop: &mut Populator<'_>) -> bool {
|
||||
pop.peek_type(0) == ParseTokenType::string
|
||||
@@ -1226,14 +1167,13 @@ pub fn decoration(&self) -> StatementDecoration {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Node!)]
|
||||
pub enum BlockStatementHeader {
|
||||
Begin(BeginHeader),
|
||||
For(ForHeader),
|
||||
While(WhileHeader),
|
||||
Function(FunctionHeader),
|
||||
}
|
||||
implement_node!(BlockStatementHeader);
|
||||
|
||||
impl Default for BlockStatementHeader {
|
||||
fn default() -> Self {
|
||||
|
||||
@@ -635,7 +635,7 @@ pub fn commandline(parser: &Parser, streams: &mut IoStreams, args: &mut [&wstr])
|
||||
transient = parser.libdata().transient_commandline.clone().unwrap();
|
||||
current_buffer = &transient;
|
||||
current_cursor_pos = transient.len();
|
||||
} else if parser.interactive_initialized.load() || is_interactive_session() {
|
||||
} else if is_interactive_session() {
|
||||
current_buffer = &rstate.text;
|
||||
current_cursor_pos = rstate.cursor_pos;
|
||||
} else {
|
||||
|
||||
@@ -458,7 +458,7 @@ pub fn complete(parser: &Parser, streams: &mut IoStreams, argv: &mut [&wstr]) ->
|
||||
None => {
|
||||
// No argument given, try to use the current commandline.
|
||||
let commandline_state = commandline_get_state(true);
|
||||
if !parser.interactive_initialized.load() && !is_interactive_session() {
|
||||
if !is_interactive_session() {
|
||||
streams.err.append(cmd);
|
||||
streams
|
||||
.err
|
||||
|
||||
@@ -243,19 +243,7 @@ fn parse_cmd_opts(
|
||||
STATUS_CMD_OK
|
||||
}
|
||||
|
||||
fn validate_function_name(
|
||||
argv: &mut [&wstr],
|
||||
function_name: &mut WString,
|
||||
cmd: &wstr,
|
||||
streams: &mut IoStreams,
|
||||
) -> c_int {
|
||||
if argv.len() < 2 {
|
||||
streams
|
||||
.err
|
||||
.append(wgettext_fmt!("%ls: function name required", cmd));
|
||||
return STATUS_INVALID_ARGS;
|
||||
}
|
||||
*function_name = argv[1].to_owned();
|
||||
fn validate_function_name(function_name: &wstr, cmd: &wstr, streams: &mut IoStreams) -> c_int {
|
||||
if !valid_func_name(function_name) {
|
||||
streams.err.append(wgettext_fmt!(
|
||||
"%ls: %ls: invalid function name",
|
||||
@@ -293,8 +281,8 @@ pub fn function(
|
||||
let cmd = argv[0];
|
||||
|
||||
// A valid function name has to be the first argument.
|
||||
let mut function_name = WString::new();
|
||||
let mut retval = validate_function_name(argv, &mut function_name, cmd, streams);
|
||||
let function_name = argv[1].to_owned();
|
||||
let mut retval = validate_function_name(&function_name, cmd, streams);
|
||||
if retval != STATUS_CMD_OK {
|
||||
return retval;
|
||||
}
|
||||
|
||||
@@ -246,20 +246,18 @@ fn handle_sprintf_error(&mut self, err: fish_printf::Error) {
|
||||
}
|
||||
}
|
||||
|
||||
/// Evaluate a printf conversion specification. SPEC is the start of the directive, and CONVERSION
|
||||
/// specifies the type of conversion. SPEC does not include any length modifier or the
|
||||
/// conversion specifier itself. FIELD_WIDTH and PRECISION are the field width and
|
||||
/// precision for '*' values, if HAVE_FIELD_WIDTH and HAVE_PRECISION are true, respectively.
|
||||
/// ARGUMENT is the argument to be formatted.
|
||||
/// Evaluate a printf conversion specification.
|
||||
/// `spec` is the start of the directive, and `conversion` specifies the type of conversion.
|
||||
/// `spec` does not include any length modifier or the conversion specifier itself.
|
||||
/// `field_width` and `precision` are the field width and precision for '*' values, if any.
|
||||
/// `argument` is the argument to be formatted.
|
||||
#[allow(clippy::collapsible_else_if, clippy::too_many_arguments)]
|
||||
fn print_direc(
|
||||
fn print_directive(
|
||||
&mut self,
|
||||
spec: &wstr,
|
||||
conversion: char,
|
||||
have_field_width: bool,
|
||||
field_width: i32,
|
||||
have_precision: bool,
|
||||
precision: i32,
|
||||
field_width: Option<i64>,
|
||||
precision: Option<i64>,
|
||||
argument: &wstr,
|
||||
) {
|
||||
/// Printf macro helper which provides our locale.
|
||||
@@ -283,21 +281,6 @@ macro_rules! append_output_fmt {
|
||||
// Start with everything except the conversion specifier.
|
||||
let mut fmt = spec.to_owned();
|
||||
|
||||
// Create a copy of the % directive, with a width modifier substituted for any
|
||||
// existing integer length modifier.
|
||||
match conversion {
|
||||
'x' | 'X' | 'd' | 'i' | 'o' | 'u' => {
|
||||
fmt.push_str("ll");
|
||||
}
|
||||
'a' | 'e' | 'f' | 'g' | 'A' | 'E' | 'F' | 'G' => {
|
||||
fmt.push_str("L");
|
||||
}
|
||||
's' | 'c' => {
|
||||
fmt.push_str("l");
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
// Append the conversion itself.
|
||||
fmt.push(conversion);
|
||||
|
||||
@@ -306,77 +289,96 @@ macro_rules! append_output_fmt {
|
||||
match conversion {
|
||||
'd' | 'i' => {
|
||||
let arg: i64 = string_to_scalar_type(argument, self);
|
||||
if !have_field_width {
|
||||
if !have_precision {
|
||||
append_output_fmt!(fmt, arg);
|
||||
} else {
|
||||
append_output_fmt!(fmt, precision, arg);
|
||||
}
|
||||
} else {
|
||||
if !have_precision {
|
||||
append_output_fmt!(fmt, field_width, arg);
|
||||
} else {
|
||||
append_output_fmt!(fmt, field_width, precision, arg);
|
||||
}
|
||||
match field_width {
|
||||
Some(field_width) => match precision {
|
||||
Some(precision) => {
|
||||
append_output_fmt!(fmt, field_width, precision, arg);
|
||||
}
|
||||
None => {
|
||||
append_output_fmt!(fmt, field_width, arg);
|
||||
}
|
||||
},
|
||||
None => match precision {
|
||||
Some(precision) => {
|
||||
append_output_fmt!(fmt, precision, arg);
|
||||
}
|
||||
None => {
|
||||
append_output_fmt!(fmt, arg);
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
'o' | 'u' | 'x' | 'X' => {
|
||||
let arg: u64 = string_to_scalar_type(argument, self);
|
||||
if !have_field_width {
|
||||
if !have_precision {
|
||||
append_output_fmt!(fmt, arg);
|
||||
} else {
|
||||
append_output_fmt!(fmt, precision, arg);
|
||||
}
|
||||
} else {
|
||||
if !have_precision {
|
||||
append_output_fmt!(fmt, field_width, arg);
|
||||
} else {
|
||||
append_output_fmt!(fmt, field_width, precision, arg);
|
||||
}
|
||||
match field_width {
|
||||
Some(field_width) => match precision {
|
||||
Some(precision) => {
|
||||
append_output_fmt!(fmt, field_width, precision, arg);
|
||||
}
|
||||
None => {
|
||||
append_output_fmt!(fmt, field_width, arg);
|
||||
}
|
||||
},
|
||||
None => match precision {
|
||||
Some(precision) => {
|
||||
append_output_fmt!(fmt, precision, arg);
|
||||
}
|
||||
None => {
|
||||
append_output_fmt!(fmt, arg);
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
'a' | 'A' | 'e' | 'E' | 'f' | 'F' | 'g' | 'G' => {
|
||||
let arg: f64 = string_to_scalar_type(argument, self);
|
||||
if !have_field_width {
|
||||
if !have_precision {
|
||||
append_output_fmt!(fmt, arg);
|
||||
} else {
|
||||
append_output_fmt!(fmt, precision, arg);
|
||||
}
|
||||
} else {
|
||||
if !have_precision {
|
||||
append_output_fmt!(fmt, field_width, arg);
|
||||
} else {
|
||||
append_output_fmt!(fmt, field_width, precision, arg);
|
||||
}
|
||||
match field_width {
|
||||
Some(field_width) => match precision {
|
||||
Some(precision) => {
|
||||
append_output_fmt!(fmt, field_width, precision, arg);
|
||||
}
|
||||
None => {
|
||||
append_output_fmt!(fmt, field_width, arg);
|
||||
}
|
||||
},
|
||||
None => match precision {
|
||||
Some(precision) => {
|
||||
append_output_fmt!(fmt, precision, arg);
|
||||
}
|
||||
None => {
|
||||
append_output_fmt!(fmt, arg);
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
'c' => {
|
||||
if !have_field_width {
|
||||
append_output_fmt!(fmt, argument.char_at(0));
|
||||
} else {
|
||||
'c' => match field_width {
|
||||
Some(field_width) => {
|
||||
append_output_fmt!(fmt, field_width, argument.char_at(0));
|
||||
}
|
||||
}
|
||||
None => {
|
||||
append_output_fmt!(fmt, argument.char_at(0));
|
||||
}
|
||||
},
|
||||
|
||||
's' => {
|
||||
if !have_field_width {
|
||||
if !have_precision {
|
||||
append_output_fmt!(fmt, argument);
|
||||
} else {
|
||||
append_output_fmt!(fmt, precision, argument);
|
||||
}
|
||||
} else {
|
||||
if !have_precision {
|
||||
append_output_fmt!(fmt, field_width, argument);
|
||||
} else {
|
||||
's' => match field_width {
|
||||
Some(field_width) => match precision {
|
||||
Some(precision) => {
|
||||
append_output_fmt!(fmt, field_width, precision, argument);
|
||||
}
|
||||
}
|
||||
}
|
||||
None => {
|
||||
append_output_fmt!(fmt, field_width, argument);
|
||||
}
|
||||
},
|
||||
None => match precision {
|
||||
Some(precision) => {
|
||||
append_output_fmt!(fmt, precision, argument);
|
||||
}
|
||||
None => {
|
||||
append_output_fmt!(fmt, argument);
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
_ => {
|
||||
panic!("unexpected opt: {}", conversion);
|
||||
@@ -384,18 +386,16 @@ macro_rules! append_output_fmt {
|
||||
}
|
||||
}
|
||||
|
||||
/// Print the text in FORMAT, using ARGV for arguments to any `%' directives.
|
||||
/// Return the number of elements of ARGV used.
|
||||
/// Print the text in `format`, using `argv` for arguments to any `%' directives.
|
||||
/// Return the number of elements of `argv` used.
|
||||
fn print_formatted(&mut self, format: &wstr, mut argv: &[&wstr]) -> usize {
|
||||
let mut argc = argv.len();
|
||||
let save_argc = argc; /* Preserve original value. */
|
||||
let mut f: &wstr; /* Pointer into `format'. */
|
||||
let mut direc_start: &wstr; /* Start of % directive. */
|
||||
let mut direc_length: usize; /* Length of % directive. */
|
||||
let mut have_field_width: bool; /* True if FIELD_WIDTH is valid. */
|
||||
let mut field_width: c_int = 0; /* Arg to first '*'. */
|
||||
let mut have_precision: bool; /* True if PRECISION is valid. */
|
||||
let mut precision = 0; /* Arg to second '*'. */
|
||||
let mut directive_start: &wstr; /* Start of % directive. */
|
||||
let mut directive_length: usize; /* Length of % directive. */
|
||||
let mut field_width: Option<i64>; /* Arg to first '*'. */
|
||||
let mut precision: Option<i64>; /* Arg to second '*'. */
|
||||
let mut ok = [false; 256]; /* ok['x'] is true if %x is allowed. */
|
||||
|
||||
// N.B. this was originally written as a loop like so:
|
||||
@@ -414,11 +414,11 @@ fn print_formatted(&mut self, format: &wstr, mut argv: &[&wstr]) -> usize {
|
||||
|
||||
match f.char_at(0) {
|
||||
'%' => {
|
||||
direc_start = f;
|
||||
directive_start = f;
|
||||
f = &f[1..];
|
||||
direc_length = 1;
|
||||
have_field_width = false;
|
||||
have_precision = false;
|
||||
directive_length = 1;
|
||||
field_width = None;
|
||||
precision = None;
|
||||
if f.char_at(0) == '%' {
|
||||
self.append_output('%');
|
||||
continue;
|
||||
@@ -460,17 +460,17 @@ fn print_formatted(&mut self, format: &wstr, mut argv: &[&wstr]) -> usize {
|
||||
}
|
||||
if continue_looking_for_flags {
|
||||
f = &f[1..];
|
||||
direc_length += 1;
|
||||
directive_length += 1;
|
||||
}
|
||||
}
|
||||
|
||||
if f.char_at(0) == '*' {
|
||||
f = &f[1..];
|
||||
direc_length += 1;
|
||||
directive_length += 1;
|
||||
if argc > 0 {
|
||||
let width: i64 = string_to_scalar_type(argv[0], self);
|
||||
if (c_int::MIN as i64) <= width && width <= (c_int::MAX as i64) {
|
||||
field_width = width as c_int;
|
||||
field_width = Some(width);
|
||||
} else {
|
||||
self.fatal_error(wgettext_fmt!(
|
||||
"invalid field width: %ls",
|
||||
@@ -480,47 +480,45 @@ fn print_formatted(&mut self, format: &wstr, mut argv: &[&wstr]) -> usize {
|
||||
argv = &argv[1..];
|
||||
argc -= 1;
|
||||
} else {
|
||||
field_width = 0;
|
||||
field_width = Some(0);
|
||||
}
|
||||
have_field_width = true;
|
||||
} else {
|
||||
while iswdigit(f.char_at(0)) {
|
||||
f = &f[1..];
|
||||
direc_length += 1;
|
||||
directive_length += 1;
|
||||
}
|
||||
}
|
||||
|
||||
if f.char_at(0) == '.' {
|
||||
f = &f[1..];
|
||||
direc_length += 1;
|
||||
directive_length += 1;
|
||||
modify_allowed_format_specifiers(&mut ok, "c", false);
|
||||
if f.char_at(0) == '*' {
|
||||
f = &f[1..];
|
||||
direc_length += 1;
|
||||
directive_length += 1;
|
||||
if argc > 0 {
|
||||
let prec: i64 = string_to_scalar_type(argv[0], self);
|
||||
if prec < 0 {
|
||||
// A negative precision is taken as if the precision were omitted,
|
||||
// so -1 is safe here even if prec < INT_MIN.
|
||||
precision = -1;
|
||||
precision = Some(-1);
|
||||
} else if (c_int::MAX as i64) < prec {
|
||||
self.fatal_error(wgettext_fmt!(
|
||||
"invalid precision: %ls",
|
||||
argv[0]
|
||||
));
|
||||
} else {
|
||||
precision = prec as c_int;
|
||||
precision = Some(prec);
|
||||
}
|
||||
argv = &argv[1..];
|
||||
argc -= 1;
|
||||
} else {
|
||||
precision = 0;
|
||||
precision = Some(0);
|
||||
}
|
||||
have_precision = true;
|
||||
} else {
|
||||
while iswdigit(f.char_at(0)) {
|
||||
f = &f[1..];
|
||||
direc_length += 1;
|
||||
directive_length += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -533,8 +531,8 @@ fn print_formatted(&mut self, format: &wstr, mut argv: &[&wstr]) -> usize {
|
||||
if (conversion as usize) > 0xFF || !ok[conversion as usize] {
|
||||
self.fatal_error(wgettext_fmt!(
|
||||
"%.*ls: invalid conversion specification",
|
||||
wstr_offset_in(f, direc_start) + 1,
|
||||
direc_start
|
||||
wstr_offset_in(f, directive_start) + 1,
|
||||
directive_start
|
||||
));
|
||||
return 0;
|
||||
}
|
||||
@@ -545,12 +543,10 @@ fn print_formatted(&mut self, format: &wstr, mut argv: &[&wstr]) -> usize {
|
||||
argv = &argv[1..];
|
||||
argc -= 1;
|
||||
}
|
||||
self.print_direc(
|
||||
&direc_start[..direc_length],
|
||||
self.print_directive(
|
||||
&directive_start[..directive_length],
|
||||
f.char_at(0),
|
||||
have_field_width,
|
||||
field_width,
|
||||
have_precision,
|
||||
precision,
|
||||
argument,
|
||||
);
|
||||
|
||||
@@ -5,11 +5,88 @@
|
||||
use once_cell::sync::Lazy;
|
||||
|
||||
use crate::fallback::{fish_wcswidth, wcscasecmp};
|
||||
use crate::libc::*;
|
||||
use crate::wutil::perror;
|
||||
|
||||
use super::prelude::*;
|
||||
|
||||
pub mod limits {
|
||||
/// Constants that exist everywhere (Linux, macOS, BSD).
|
||||
/// Note these are uints on Linux but ints everywhere else - we use -1 as a sentinel
|
||||
/// so cast to int.
|
||||
pub mod common {
|
||||
use libc;
|
||||
pub const CORE: libc::c_int = libc::RLIMIT_CORE as _;
|
||||
pub const DATA: libc::c_int = libc::RLIMIT_DATA as _;
|
||||
pub const FSIZE: libc::c_int = libc::RLIMIT_FSIZE as _;
|
||||
pub const MEMLOCK: libc::c_int = libc::RLIMIT_MEMLOCK as _;
|
||||
pub const NOFILE: libc::c_int = libc::RLIMIT_NOFILE as _;
|
||||
pub const STACK: libc::c_int = libc::RLIMIT_STACK as _;
|
||||
pub const CPU: libc::c_int = libc::RLIMIT_CPU as _;
|
||||
pub const NPROC: libc::c_int = libc::RLIMIT_NPROC as _;
|
||||
pub const AS: libc::c_int = libc::RLIMIT_AS as _;
|
||||
}
|
||||
pub use self::common::*;
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
pub mod linux {
|
||||
use libc;
|
||||
pub const SIGPENDING: libc::c_int = libc::RLIMIT_SIGPENDING as _;
|
||||
pub const MSGQUEUE: libc::c_int = libc::RLIMIT_MSGQUEUE as _;
|
||||
pub const RTPRIO: libc::c_int = libc::RLIMIT_RTPRIO as _;
|
||||
pub const RTTIME: libc::c_int = libc::RLIMIT_RTTIME as _;
|
||||
pub const RSS: libc::c_int = libc::RLIMIT_RSS as _;
|
||||
|
||||
pub const SBSIZE: libc::c_int = -1;
|
||||
pub const NICE: libc::c_int = -1;
|
||||
pub const SWAP: libc::c_int = -1;
|
||||
pub const KQUEUES: libc::c_int = -1;
|
||||
pub const NPTS: libc::c_int = -1;
|
||||
pub const NTHR: libc::c_int = -1;
|
||||
}
|
||||
#[cfg(target_os = "linux")]
|
||||
pub use self::linux::*;
|
||||
|
||||
#[cfg(any(target_os = "ios", target_os = "macos"))]
|
||||
pub mod macos {
|
||||
use libc;
|
||||
|
||||
pub const SIGPENDING: libc::c_int = -1;
|
||||
pub const MSGQUEUE: libc::c_int = -1;
|
||||
pub const RTPRIO: libc::c_int = -1;
|
||||
pub const RTTIME: libc::c_int = -1;
|
||||
|
||||
pub const SBSIZE: libc::c_int = -1;
|
||||
pub const NICE: libc::c_int = -1;
|
||||
pub const RSS: libc::c_int = -1;
|
||||
pub const SWAP: libc::c_int = -1;
|
||||
pub const KQUEUES: libc::c_int = -1;
|
||||
pub const NPTS: libc::c_int = -1;
|
||||
pub const NTHR: libc::c_int = -1;
|
||||
}
|
||||
#[cfg(any(target_os = "ios", target_os = "macos"))]
|
||||
pub use self::macos::*;
|
||||
|
||||
#[cfg(bsd)]
|
||||
pub mod bsd {
|
||||
use libc;
|
||||
|
||||
pub const SBSIZE: libc::c_int = libc::RLIMIT_SBSIZE;
|
||||
pub const NICE: libc::c_int = libc::RLIMIT_NICE;
|
||||
pub const RSS: libc::c_int = libc::RLIMIT_RSS;
|
||||
pub const NTHR: libc::c_int = libc::RLIMIT_NTHR;
|
||||
pub const SWAP: libc::c_int = libc::RLIMIT_SWAP;
|
||||
pub const KQUEUES: libc::c_int = libc::RLIMIT_KQUEUES;
|
||||
pub const NPTS: libc::c_int = libc::RLIMIT_NPTS;
|
||||
|
||||
pub const SIGPENDING: libc::c_int = -1;
|
||||
pub const MSGQUEUE: libc::c_int = -1;
|
||||
pub const RTPRIO: libc::c_int = -1;
|
||||
pub const RTTIME: libc::c_int = -1;
|
||||
}
|
||||
#[cfg(bsd)]
|
||||
pub use self::bsd::*;
|
||||
}
|
||||
|
||||
/// Calls getrlimit.
|
||||
fn getrlimit(resource: c_uint) -> Option<(rlim_t, rlim_t)> {
|
||||
let resource: i32 = resource.try_into().unwrap();
|
||||
@@ -57,7 +134,7 @@ fn print_all(hard: bool, streams: &mut IoStreams) {
|
||||
};
|
||||
let l = if hard { rlim_max } else { rlim_cur };
|
||||
|
||||
let unit = if resource.resource == RLIMIT_CPU() as c_uint {
|
||||
let unit = if resource.resource == limits::CPU as c_uint {
|
||||
"(seconds, "
|
||||
} else if get_multiplier(resource.resource) == 1 {
|
||||
"("
|
||||
@@ -160,7 +237,7 @@ struct Options {
|
||||
impl Default for Options {
|
||||
fn default() -> Self {
|
||||
Options {
|
||||
what: RLIMIT_FSIZE(),
|
||||
what: limits::FSIZE,
|
||||
report_all: false,
|
||||
hard: false,
|
||||
soft: false,
|
||||
@@ -209,26 +286,26 @@ pub fn ulimit(parser: &Parser, streams: &mut IoStreams, args: &mut [&wstr]) -> B
|
||||
'a' => opts.report_all = true,
|
||||
'H' => opts.hard = true,
|
||||
'S' => opts.soft = true,
|
||||
'b' => opts.what = RLIMIT_SBSIZE(),
|
||||
'c' => opts.what = RLIMIT_CORE(),
|
||||
'd' => opts.what = RLIMIT_DATA(),
|
||||
'e' => opts.what = RLIMIT_NICE(),
|
||||
'f' => opts.what = RLIMIT_FSIZE(),
|
||||
'i' => opts.what = RLIMIT_SIGPENDING(),
|
||||
'l' => opts.what = RLIMIT_MEMLOCK(),
|
||||
'm' => opts.what = RLIMIT_RSS(),
|
||||
'n' => opts.what = RLIMIT_NOFILE(),
|
||||
'q' => opts.what = RLIMIT_MSGQUEUE(),
|
||||
'r' => opts.what = RLIMIT_RTPRIO(),
|
||||
's' => opts.what = RLIMIT_STACK(),
|
||||
't' => opts.what = RLIMIT_CPU(),
|
||||
'u' => opts.what = RLIMIT_NPROC(),
|
||||
'v' => opts.what = RLIMIT_AS(),
|
||||
'w' => opts.what = RLIMIT_SWAP(),
|
||||
'y' => opts.what = RLIMIT_RTTIME(),
|
||||
'K' => opts.what = RLIMIT_KQUEUES(),
|
||||
'P' => opts.what = RLIMIT_NPTS(),
|
||||
'T' => opts.what = RLIMIT_NTHR(),
|
||||
'b' => opts.what = limits::SBSIZE,
|
||||
'c' => opts.what = limits::CORE,
|
||||
'd' => opts.what = limits::DATA,
|
||||
'e' => opts.what = limits::NICE,
|
||||
'f' => opts.what = limits::FSIZE,
|
||||
'i' => opts.what = limits::SIGPENDING,
|
||||
'l' => opts.what = limits::MEMLOCK,
|
||||
'm' => opts.what = limits::RSS,
|
||||
'n' => opts.what = limits::NOFILE,
|
||||
'q' => opts.what = limits::MSGQUEUE,
|
||||
'r' => opts.what = limits::RTPRIO,
|
||||
's' => opts.what = limits::STACK,
|
||||
't' => opts.what = limits::CPU,
|
||||
'u' => opts.what = limits::NPROC,
|
||||
'v' => opts.what = limits::AS,
|
||||
'w' => opts.what = limits::SWAP,
|
||||
'y' => opts.what = limits::RTTIME,
|
||||
'K' => opts.what = limits::KQUEUES,
|
||||
'P' => opts.what = limits::NPTS,
|
||||
'T' => opts.what = limits::NTHR,
|
||||
'h' => {
|
||||
builtin_print_help(parser, streams, cmd);
|
||||
return Ok(SUCCESS);
|
||||
@@ -360,101 +437,96 @@ fn new(
|
||||
static RESOURCE_ARR: Lazy<Box<[Resource]>> = Lazy::new(|| {
|
||||
let resources_info = [
|
||||
(
|
||||
RLIMIT_SBSIZE(),
|
||||
limits::SBSIZE,
|
||||
L!("Maximum size of socket buffers"),
|
||||
'b',
|
||||
1024,
|
||||
),
|
||||
(
|
||||
RLIMIT_CORE(),
|
||||
limits::CORE,
|
||||
L!("Maximum size of core files created"),
|
||||
'c',
|
||||
1024,
|
||||
),
|
||||
(
|
||||
RLIMIT_DATA(),
|
||||
limits::DATA,
|
||||
L!("Maximum size of a process’s data segment"),
|
||||
'd',
|
||||
1024,
|
||||
),
|
||||
(limits::NICE, L!("Control of maximum nice priority"), 'e', 1),
|
||||
(
|
||||
RLIMIT_NICE(),
|
||||
L!("Control of maximum nice priority"),
|
||||
'e',
|
||||
1,
|
||||
),
|
||||
(
|
||||
RLIMIT_FSIZE(),
|
||||
limits::FSIZE,
|
||||
L!("Maximum size of files created by the shell"),
|
||||
'f',
|
||||
1024,
|
||||
),
|
||||
(
|
||||
RLIMIT_SIGPENDING(),
|
||||
limits::SIGPENDING,
|
||||
L!("Maximum number of pending signals"),
|
||||
'i',
|
||||
1,
|
||||
),
|
||||
(
|
||||
RLIMIT_MEMLOCK(),
|
||||
limits::MEMLOCK,
|
||||
L!("Maximum size that may be locked into memory"),
|
||||
'l',
|
||||
1024,
|
||||
),
|
||||
(RLIMIT_RSS(), L!("Maximum resident set size"), 'm', 1024),
|
||||
(limits::RSS, L!("Maximum resident set size"), 'm', 1024),
|
||||
(
|
||||
RLIMIT_NOFILE(),
|
||||
limits::NOFILE,
|
||||
L!("Maximum number of open file descriptors"),
|
||||
'n',
|
||||
1,
|
||||
),
|
||||
(
|
||||
RLIMIT_MSGQUEUE(),
|
||||
limits::MSGQUEUE,
|
||||
L!("Maximum bytes in POSIX message queues"),
|
||||
'q',
|
||||
1024,
|
||||
),
|
||||
(
|
||||
RLIMIT_RTPRIO(),
|
||||
limits::RTPRIO,
|
||||
L!("Maximum realtime scheduling priority"),
|
||||
'r',
|
||||
1,
|
||||
),
|
||||
(RLIMIT_STACK(), L!("Maximum stack size"), 's', 1024),
|
||||
(limits::STACK, L!("Maximum stack size"), 's', 1024),
|
||||
(
|
||||
RLIMIT_CPU(),
|
||||
limits::CPU,
|
||||
L!("Maximum amount of CPU time in seconds"),
|
||||
't',
|
||||
1,
|
||||
),
|
||||
(
|
||||
RLIMIT_NPROC(),
|
||||
limits::NPROC,
|
||||
L!("Maximum number of processes available to current user"),
|
||||
'u',
|
||||
1,
|
||||
),
|
||||
(
|
||||
RLIMIT_AS(),
|
||||
limits::AS,
|
||||
L!("Maximum amount of virtual memory available to each process"),
|
||||
'v',
|
||||
1024,
|
||||
),
|
||||
(RLIMIT_SWAP(), L!("Maximum swap space"), 'w', 1024),
|
||||
(limits::SWAP, L!("Maximum swap space"), 'w', 1024),
|
||||
(
|
||||
RLIMIT_RTTIME(),
|
||||
limits::RTTIME,
|
||||
L!("Maximum contiguous realtime CPU time"),
|
||||
'y',
|
||||
1,
|
||||
),
|
||||
(RLIMIT_KQUEUES(), L!("Maximum number of kqueues"), 'K', 1),
|
||||
(limits::KQUEUES, L!("Maximum number of kqueues"), 'K', 1),
|
||||
(
|
||||
RLIMIT_NPTS(),
|
||||
limits::NPTS,
|
||||
L!("Maximum number of pseudo-terminals"),
|
||||
'P',
|
||||
1,
|
||||
),
|
||||
(
|
||||
RLIMIT_NTHR(),
|
||||
limits::NTHR,
|
||||
L!("Maximum number of simultaneous threads"),
|
||||
'T',
|
||||
1,
|
||||
|
||||
@@ -9,14 +9,16 @@
|
||||
use crate::global_safety::AtomicRef;
|
||||
use crate::global_safety::RelaxedAtomicBool;
|
||||
use crate::key;
|
||||
use crate::libc::MB_CUR_MAX;
|
||||
use crate::locale::invalidate_numeric_locale;
|
||||
use crate::parse_util::parse_util_escape_string_with_quote;
|
||||
use crate::terminal::Output;
|
||||
use crate::termsize::Termsize;
|
||||
use crate::wchar::{decode_byte_from_char, encode_byte_to_char, prelude::*};
|
||||
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::encoding::{
|
||||
mbrtowc, probe_is_multibyte_locale, wcrtomb, zero_mbstate, AT_LEAST_MB_LEN_MAX,
|
||||
};
|
||||
use crate::wutil::fish_iswalnum;
|
||||
use bitflags::bitflags;
|
||||
use libc::{SIGTTOU, SIG_IGN, STDIN_FILENO};
|
||||
@@ -32,8 +34,6 @@
|
||||
use std::sync::{Arc, MutexGuard};
|
||||
use std::time;
|
||||
|
||||
pub const BUILD_DIR: &str = env!("FISH_RESOLVED_BUILD_DIR");
|
||||
|
||||
pub const PACKAGE_NAME: &str = env!("CARGO_PKG_NAME");
|
||||
|
||||
// Highest legal ASCII value.
|
||||
@@ -188,7 +188,7 @@ fn escape_string_script(input: &wstr, flags: EscapeFlags) -> WString {
|
||||
let no_quoted = flags.contains(EscapeFlags::NO_QUOTED);
|
||||
let no_tilde = flags.contains(EscapeFlags::NO_TILDE);
|
||||
let no_qmark = feature_test(FeatureFlag::qmark_noglob);
|
||||
let symbolic = flags.contains(EscapeFlags::SYMBOLIC) && MB_CUR_MAX() > 1;
|
||||
let symbolic = flags.contains(EscapeFlags::SYMBOLIC) && get_is_multibyte_locale();
|
||||
|
||||
assert!(
|
||||
!symbolic || !escape_printables,
|
||||
@@ -1062,6 +1062,13 @@ pub fn get_obfuscation_read_char() -> char {
|
||||
char::from_u32(OBFUSCATION_READ_CHAR.load(Ordering::Relaxed)).unwrap()
|
||||
}
|
||||
|
||||
static IS_MB_LOCALE: RelaxedAtomicBool = RelaxedAtomicBool::new(false);
|
||||
|
||||
/// Whether we believe we are in a multibyte locale.
|
||||
pub fn get_is_multibyte_locale() -> bool {
|
||||
IS_MB_LOCALE.load()
|
||||
}
|
||||
|
||||
/// Profiling flag. True if commands should be profiled.
|
||||
pub static PROFILING_ACTIVE: RelaxedAtomicBool = RelaxedAtomicBool::new(false);
|
||||
|
||||
@@ -1280,6 +1287,9 @@ pub fn should_suppress_stderr_for_tests() -> bool {
|
||||
/// This function should be called after calling `setlocale()` to perform fish specific locale
|
||||
/// initialization.
|
||||
pub fn fish_setlocale() {
|
||||
// Invalidate the cached numeric locale.
|
||||
invalidate_numeric_locale();
|
||||
|
||||
// Helper to make a static reference to a static &'wstr, from a string literal.
|
||||
// This is necessary to store them in global atomics, as these can't handle fat pointers.
|
||||
macro_rules! LL {
|
||||
@@ -1289,6 +1299,9 @@ macro_rules! LL {
|
||||
}};
|
||||
}
|
||||
|
||||
// Mark if we are a multibyte locale.
|
||||
IS_MB_LOCALE.store(probe_is_multibyte_locale());
|
||||
|
||||
// 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
|
||||
|
||||
13
src/env/config_paths.rs
vendored
13
src/env/config_paths.rs
vendored
@@ -1,7 +1,7 @@
|
||||
use crate::common::wcs2string;
|
||||
use crate::common::BUILD_DIR;
|
||||
use crate::wchar::prelude::*;
|
||||
use crate::{common::get_executable_path, FLOG, FLOGF};
|
||||
#[cfg(not(feature = "embed-data"))]
|
||||
use fish_build_helper::workspace_root;
|
||||
use std::ffi::OsString;
|
||||
use std::os::unix::ffi::OsStringExt;
|
||||
@@ -63,14 +63,7 @@ fn static_paths() -> Self {
|
||||
fn from_exec_path(exec_path: PathBuf) -> Self {
|
||||
FLOG!(config, "embed-data feature is active, ignoring data paths");
|
||||
Self {
|
||||
sysconf: if exec_path
|
||||
.canonicalize()
|
||||
.is_ok_and(|exec_path| exec_path.starts_with(BUILD_DIR))
|
||||
{
|
||||
workspace_root().join("etc")
|
||||
} else {
|
||||
PathBuf::from(SYSCONF_DIR).join("fish")
|
||||
},
|
||||
sysconf: PathBuf::from(SYSCONF_DIR).join("fish"),
|
||||
bin: exec_path.parent().map(|x| x.to_path_buf()),
|
||||
}
|
||||
}
|
||||
@@ -123,7 +116,7 @@ fn from_exec_path(unresolved_exec_path: PathBuf) -> Self {
|
||||
}
|
||||
|
||||
// If we're in Cargo's target directory or in CMake's build directory, use the source files.
|
||||
if exec_path.starts_with(BUILD_DIR) {
|
||||
if exec_path.starts_with(env!("FISH_BUILD_DIR")) {
|
||||
FLOG!(
|
||||
config,
|
||||
format!(
|
||||
|
||||
23
src/env/environment.rs
vendored
23
src/env/environment.rs
vendored
@@ -13,8 +13,7 @@
|
||||
use crate::flog::FLOG;
|
||||
use crate::global_safety::RelaxedAtomicBool;
|
||||
use crate::input::{init_input, FISH_BIND_MODE_VAR};
|
||||
use crate::libc::{stdout_stream, C_PATH_BSHELL, _PATH_BSHELL};
|
||||
use crate::nix::{geteuid, getpid, isatty};
|
||||
use crate::nix::{geteuid, getpid};
|
||||
use crate::null_terminated_array::OwningNullTerminatedArray;
|
||||
use crate::path::{
|
||||
path_emit_config_directory_messages, path_get_cache, path_get_config, path_get_data,
|
||||
@@ -26,13 +25,11 @@
|
||||
use crate::wchar::prelude::*;
|
||||
use crate::wcstringutil::join_strings;
|
||||
use crate::wutil::{fish_wcstol, wgetcwd, wgettext};
|
||||
use std::sync::atomic::Ordering;
|
||||
|
||||
use libc::{c_int, confstr, uid_t, STDOUT_FILENO, _IONBF};
|
||||
use libc::{c_int, confstr, uid_t};
|
||||
use once_cell::sync::{Lazy, OnceCell};
|
||||
use std::collections::HashMap;
|
||||
use std::ffi::CStr;
|
||||
use std::io::Write;
|
||||
use std::mem::MaybeUninit;
|
||||
use std::os::unix::prelude::*;
|
||||
use std::sync::Arc;
|
||||
@@ -569,13 +566,12 @@ fn setup_user(vars: &EnvStack) {
|
||||
}
|
||||
|
||||
pub(crate) static FALLBACK_PATH: Lazy<&[WString]> = Lazy::new(|| {
|
||||
use crate::libc::_CS_PATH;
|
||||
// _CS_PATH: colon-separated paths to find POSIX utilities
|
||||
let buf_size = unsafe { confstr(_CS_PATH(), std::ptr::null_mut(), 0) };
|
||||
let buf_size = unsafe { confstr(libc::_CS_PATH, std::ptr::null_mut(), 0) };
|
||||
Box::leak(
|
||||
(if buf_size > 0 {
|
||||
let mut buf = vec![b'\0' as libc::c_char; buf_size];
|
||||
unsafe { confstr(_CS_PATH(), buf.as_mut_ptr(), buf_size) };
|
||||
unsafe { confstr(libc::_CS_PATH, buf.as_mut_ptr(), buf_size) };
|
||||
let buf = buf;
|
||||
// safety: buf should contain a null-byte, and is not mutable unless we move ownership
|
||||
let cstr = unsafe { CStr::from_ptr(buf.as_ptr()) };
|
||||
@@ -858,13 +854,4 @@ pub fn env_init(paths: Option<&ConfigPaths>, do_uvars: bool, default_paths: bool
|
||||
|
||||
/// Various things we need to initialize at run-time that don't really fit any of the other init
|
||||
/// routines.
|
||||
pub fn misc_init() {
|
||||
// If stdout is open on a tty ensure stdio is unbuffered. That's because those functions might
|
||||
// be intermixed with `write()` calls and we need to ensure the writes are not reordered. See
|
||||
// issue #3748.
|
||||
if isatty(STDOUT_FILENO) {
|
||||
let _ = std::io::stdout().flush();
|
||||
unsafe { libc::setvbuf(stdout_stream(), std::ptr::null_mut(), _IONBF, 0) };
|
||||
}
|
||||
_PATH_BSHELL.store(unsafe { C_PATH_BSHELL().cast_mut() }, Ordering::SeqCst);
|
||||
}
|
||||
pub fn misc_init() {}
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
use crate::terminal::ColorSupport;
|
||||
use crate::tty_handoff::xtversion;
|
||||
use crate::wchar::prelude::*;
|
||||
use crate::wutil::encoding::probe_is_multibyte_locale;
|
||||
use crate::wutil::fish_wcstoi;
|
||||
use crate::{function, terminal};
|
||||
use std::borrow::Cow;
|
||||
@@ -551,21 +552,20 @@ fn init_locale(vars: &EnvStack) {
|
||||
.map(|allow_c| !crate::wcstringutil::bool_from_string(&allow_c))
|
||||
.unwrap_or(true);
|
||||
|
||||
if fix_locale && crate::libc::MB_CUR_MAX() == 1 {
|
||||
FLOG!(env_locale, "Have singlebyte locale, trying to fix.");
|
||||
if fix_locale && !probe_is_multibyte_locale() {
|
||||
FLOG!(env_locale, "Have single byte locale, trying to fix.");
|
||||
let mut fixed = false;
|
||||
for locale in UTF8_LOCALES {
|
||||
{
|
||||
let locale = CString::new(locale.to_owned()).unwrap();
|
||||
// this can fail, that is fine
|
||||
unsafe { libc::setlocale(libc::LC_CTYPE, locale.as_ptr()) };
|
||||
}
|
||||
if crate::libc::MB_CUR_MAX() > 1 {
|
||||
let locale_cstr = CString::new(*locale).unwrap();
|
||||
// this can fail, that is fine
|
||||
unsafe { libc::setlocale(libc::LC_CTYPE, locale_cstr.as_ptr()) };
|
||||
if probe_is_multibyte_locale() {
|
||||
FLOG!(env_locale, "Fixed locale:", locale);
|
||||
fixed = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if crate::libc::MB_CUR_MAX() == 1 {
|
||||
if !fixed {
|
||||
FLOG!(env_locale, "Failed to fix locale.");
|
||||
}
|
||||
}
|
||||
@@ -575,8 +575,7 @@ fn init_locale(vars: &EnvStack) {
|
||||
// should never fail, the C locale should always be defined
|
||||
assert_ne!(loc_ptr, ptr::null_mut());
|
||||
|
||||
// See that we regenerate our special locale for numbers
|
||||
crate::locale::invalidate_numeric_locale();
|
||||
// Update cached locale information.
|
||||
crate::common::fish_setlocale();
|
||||
FLOG!(
|
||||
env_locale,
|
||||
|
||||
@@ -24,12 +24,12 @@
|
||||
};
|
||||
#[cfg(FISH_USE_POSIX_SPAWN)]
|
||||
use crate::fork_exec::spawn::PosixSpawner;
|
||||
use crate::fork_exec::PATH_BSHELL;
|
||||
use crate::function::{self, FunctionProperties};
|
||||
use crate::io::{
|
||||
BufferedOutputStream, FdOutputStream, IoBufferfill, IoChain, IoClose, IoMode, IoPipe,
|
||||
IoStreams, OutputStream, SeparatedBuffer, StringOutputStream,
|
||||
};
|
||||
use crate::libc::_PATH_BSHELL;
|
||||
use crate::nix::{getpid, isatty};
|
||||
use crate::null_terminated_array::OwningNullTerminatedArray;
|
||||
use crate::parser::{Block, BlockId, BlockType, EvalRes, Parser};
|
||||
@@ -49,7 +49,7 @@
|
||||
use crate::wutil::{fish_wcstol, perror};
|
||||
use errno::{errno, set_errno};
|
||||
use libc::{
|
||||
EACCES, ENOENT, ENOEXEC, ENOTDIR, EPIPE, EXIT_FAILURE, EXIT_SUCCESS, STDERR_FILENO,
|
||||
c_char, EACCES, ENOENT, ENOEXEC, ENOTDIR, EPIPE, EXIT_FAILURE, EXIT_SUCCESS, STDERR_FILENO,
|
||||
STDIN_FILENO, STDOUT_FILENO,
|
||||
};
|
||||
use nix::fcntl::OFlag;
|
||||
@@ -407,13 +407,14 @@ fn safe_launch_process(
|
||||
if nargs <= maxargs {
|
||||
// +1 for /bin/sh, +1 for terminating nullptr
|
||||
let mut argv2 = [std::ptr::null(); 1 + maxargs + 1];
|
||||
argv2[0] = _PATH_BSHELL.load(Ordering::Relaxed);
|
||||
let bshell = PATH_BSHELL.as_ptr() as *const c_char;
|
||||
argv2[0] = bshell as *mut c_char;
|
||||
argv2[1..argv.len() + 1].copy_from_slice(argv);
|
||||
// The command to call should use the full path,
|
||||
// not what we would pass as argv0.
|
||||
argv2[1] = actual_cmd.as_ptr();
|
||||
unsafe {
|
||||
libc::execve(_PATH_BSHELL.load(Ordering::Relaxed), &argv2[0], envv.get());
|
||||
libc::execve(bshell, &argv2[0], envv.get());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,3 +22,10 @@ pub fn blocked_signals_for_job(job: &Job, sigmask: &mut libc::sigset_t) -> bool
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
// Bravely define _PATH_BSHELL. On practice it's /bin/sh everywhere, except on Android.
|
||||
#[cfg(not(target_os = "android"))]
|
||||
pub static PATH_BSHELL: &[u8] = b"/bin/sh\0";
|
||||
|
||||
#[cfg(target_os = "android")]
|
||||
pub static PATH_BSHELL: &[u8] = b"/system/bin/sh\0";
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
//! Wrappers around posix_spawn.
|
||||
|
||||
use super::blocked_signals_for_job;
|
||||
use super::PATH_BSHELL;
|
||||
use crate::exec::{is_thompson_shell_script, PgroupPolicy};
|
||||
use crate::libc::_PATH_BSHELL;
|
||||
use crate::proc::Job;
|
||||
use crate::redirection::Dup2List;
|
||||
use crate::signal::signals_to_default;
|
||||
@@ -10,7 +10,6 @@
|
||||
use libc::{c_char, posix_spawn_file_actions_t, posix_spawnattr_t};
|
||||
use std::ffi::{CStr, CString};
|
||||
use std::mem::MaybeUninit;
|
||||
use std::sync::atomic::Ordering;
|
||||
|
||||
// The posix_spawn family of functions is unusual in that it returns errno codes directly in the return value, not via errno.
|
||||
// This converts to an error if nonzero.
|
||||
@@ -173,7 +172,7 @@ pub(crate) fn spawn(
|
||||
let cmdcstr = unsafe { CStr::from_ptr(cmd) };
|
||||
if spawn_err.0 == libc::ENOEXEC && is_thompson_shell_script(cmdcstr) {
|
||||
// Create a new argv with /bin/sh prepended.
|
||||
let mut argv2 = vec![_PATH_BSHELL.load(Ordering::Relaxed) as *mut c_char];
|
||||
let mut argv2 = vec![PATH_BSHELL.as_ptr() as *mut c_char];
|
||||
|
||||
// The command to call should use the full path,
|
||||
// not what we would pass as argv0.
|
||||
@@ -190,7 +189,7 @@ pub(crate) fn spawn(
|
||||
check_fail(unsafe {
|
||||
libc::posix_spawn(
|
||||
&mut pid,
|
||||
_PATH_BSHELL.load(Ordering::Relaxed),
|
||||
PATH_BSHELL.as_ptr() as *const c_char,
|
||||
&self.actions.0,
|
||||
&self.attr.0,
|
||||
argv2.as_ptr(),
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
VARIABLE_EXPAND, VARIABLE_EXPAND_SINGLE,
|
||||
};
|
||||
use crate::expand::{expand_tilde, ExpandFlags, HOME_DIRECTORY};
|
||||
use crate::libc::_PC_CASE_SENSITIVE;
|
||||
use crate::operation_context::OperationContext;
|
||||
use crate::path::path_apply_working_directory;
|
||||
use crate::redirection::RedirectionMode;
|
||||
@@ -23,7 +22,6 @@
|
||||
dir_iter::DirIter, fish_wcstoi, normalize_path, waccess, wbasename, wdirname, wstat,
|
||||
};
|
||||
use libc::PATH_MAX;
|
||||
use std::collections::hash_map::Entry;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::os::fd::RawFd;
|
||||
|
||||
@@ -391,27 +389,30 @@ pub fn is_potential_cd_path(
|
||||
/// false: the filesystem is not case insensitive
|
||||
/// true: the file system is case insensitive
|
||||
pub type CaseSensitivityCache = HashMap<WString, bool>;
|
||||
|
||||
#[cfg(any(target_os = "macos", target_os = "ios"))]
|
||||
fn fs_is_case_insensitive(
|
||||
path: &wstr,
|
||||
fd: RawFd,
|
||||
case_sensitivity_cache: &mut CaseSensitivityCache,
|
||||
) -> bool {
|
||||
let mut result = false;
|
||||
if *_PC_CASE_SENSITIVE != 0 {
|
||||
// Try the cache first.
|
||||
match case_sensitivity_cache.entry(path.to_owned()) {
|
||||
Entry::Occupied(e) => {
|
||||
/* Use the cached value */
|
||||
result = *e.get();
|
||||
}
|
||||
Entry::Vacant(e) => {
|
||||
// Ask the system. A -1 value means error (so assume case sensitive), a 1 value means case
|
||||
// sensitive, and a 0 value means case insensitive.
|
||||
let ret = unsafe { libc::fpathconf(fd, *_PC_CASE_SENSITIVE) };
|
||||
result = ret == 0;
|
||||
e.insert(result);
|
||||
}
|
||||
}
|
||||
}
|
||||
result
|
||||
if let Some(cached) = case_sensitivity_cache.get(path) {
|
||||
return *cached;
|
||||
};
|
||||
// Ask the system. A -1 value means error (so assume case sensitive), a 1 value means case
|
||||
// sensitive, and a 0 value means case insensitive.
|
||||
let ret = unsafe { libc::fpathconf(fd, libc::_PC_CASE_SENSITIVE) };
|
||||
let icase = ret == 0;
|
||||
case_sensitivity_cache.insert(path.to_owned(), icase);
|
||||
icase
|
||||
}
|
||||
|
||||
#[cfg(not(any(target_os = "macos", target_os = "ios")))]
|
||||
pub fn fs_is_case_insensitive(
|
||||
_path: &wstr,
|
||||
_fd: RawFd,
|
||||
_case_sensitivity_cache: &mut CaseSensitivityCache,
|
||||
) -> bool {
|
||||
// Other platforms don’t have _PC_CASE_SENSITIVE.
|
||||
false
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use crate::common::{
|
||||
fish_reserved_codepoint, is_windows_subsystem_for_linux, read_blocked, shell_modes,
|
||||
str2wcstring, WSL,
|
||||
fish_reserved_codepoint, get_is_multibyte_locale, is_windows_subsystem_for_linux, read_blocked,
|
||||
shell_modes, str2wcstring, WSL,
|
||||
};
|
||||
use crate::env::{EnvStack, Environment};
|
||||
use crate::fd_readable_set::{FdReadableSet, Timeout};
|
||||
@@ -12,8 +12,8 @@
|
||||
use crate::reader::reader_test_and_clear_interrupted;
|
||||
use crate::threads::iothread_port;
|
||||
use crate::tty_handoff::{
|
||||
maybe_set_kitty_keyboard_capability, maybe_set_scroll_content_up_capability,
|
||||
SCROLL_CONTENT_UP_TERMINFO_CODE, XTVERSION,
|
||||
get_kitty_keyboard_capability, maybe_set_kitty_keyboard_capability,
|
||||
maybe_set_scroll_content_up_capability, SCROLL_CONTENT_UP_TERMINFO_CODE, XTVERSION,
|
||||
};
|
||||
use crate::universal_notifier::default_notifier;
|
||||
use crate::wchar::{encode_byte_to_char, prelude::*};
|
||||
@@ -983,13 +983,16 @@ fn try_readb(&mut self, buffer: &mut Vec<u8>) -> Option<u8> {
|
||||
let fd = self.get_in_fd();
|
||||
if !check_fd_readable(
|
||||
fd,
|
||||
Duration::from_millis(if self.paste_is_buffering() || self.is_blocked_querying() {
|
||||
300
|
||||
} else if buffer == b"\x1b" {
|
||||
1 // distinguish legacy escape
|
||||
} else {
|
||||
30
|
||||
}),
|
||||
Duration::from_millis(
|
||||
if self.paste_is_buffering()
|
||||
|| self.is_blocked_querying()
|
||||
|| get_kitty_keyboard_capability() == Some(&true)
|
||||
{
|
||||
300
|
||||
} else {
|
||||
1
|
||||
},
|
||||
),
|
||||
) {
|
||||
FLOG!(
|
||||
reader,
|
||||
@@ -1696,10 +1699,8 @@ pub(crate) fn decode_input_byte(
|
||||
use DecodeState::*;
|
||||
let mut res: char = '\0';
|
||||
let read_byte = *buffer.last().unwrap();
|
||||
if crate::libc::MB_CUR_MAX() == 1 {
|
||||
if !get_is_multibyte_locale() {
|
||||
// single-byte locale, all values are legal
|
||||
// FIXME: this looks wrong, this falsely assumes that
|
||||
// the single-byte locale is compatible with Unicode upper-ASCII.
|
||||
res = read_byte.into();
|
||||
out_seq.push(res);
|
||||
return Complete;
|
||||
|
||||
@@ -64,8 +64,6 @@
|
||||
pub mod job_group;
|
||||
pub mod key;
|
||||
pub mod kill;
|
||||
#[allow(non_snake_case)]
|
||||
pub mod libc;
|
||||
pub mod locale;
|
||||
pub mod nix;
|
||||
pub mod null_terminated_array;
|
||||
|
||||
167
src/libc.c
167
src/libc.c
@@ -1,167 +0,0 @@
|
||||
#include <locale.h>
|
||||
#include <paths.h> // _PATH_BSHELL
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h> // MB_CUR_MAX
|
||||
#include <sys/mount.h> // MNT_LOCAL
|
||||
#include <sys/resource.h>
|
||||
#include <sys/statvfs.h> // ST_LOCAL
|
||||
#include <unistd.h> // _CS_PATH, _PC_CASE_SENSITIVE
|
||||
|
||||
size_t C_MB_CUR_MAX() { return MB_CUR_MAX; }
|
||||
|
||||
uint64_t C_ST_LOCAL() {
|
||||
#if defined(ST_LOCAL)
|
||||
return ST_LOCAL;
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
int C_CS_PATH() {
|
||||
#if defined(_CS_PATH)
|
||||
return _CS_PATH;
|
||||
#else
|
||||
return -1;
|
||||
#endif
|
||||
}
|
||||
|
||||
uint64_t C_MNT_LOCAL() {
|
||||
#if defined(MNT_LOCAL)
|
||||
return MNT_LOCAL;
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
const char* C_PATH_BSHELL() { return _PATH_BSHELL; }
|
||||
|
||||
int C_PC_CASE_SENSITIVE() {
|
||||
#ifdef _PC_CASE_SENSITIVE
|
||||
return _PC_CASE_SENSITIVE;
|
||||
#else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
FILE* stdout_stream() { return stdout; }
|
||||
|
||||
int C_RLIMIT_CORE() { return RLIMIT_CORE; }
|
||||
int C_RLIMIT_DATA() { return RLIMIT_DATA; }
|
||||
int C_RLIMIT_FSIZE() { return RLIMIT_FSIZE; }
|
||||
int C_RLIMIT_NOFILE() { return RLIMIT_NOFILE; }
|
||||
int C_RLIMIT_STACK() { return RLIMIT_STACK; }
|
||||
int C_RLIMIT_CPU() { return RLIMIT_CPU; }
|
||||
|
||||
int C_RLIMIT_SBSIZE() {
|
||||
#ifdef RLIMIT_SBSIZE
|
||||
return RLIMIT_SBSIZE;
|
||||
#else
|
||||
return -1;
|
||||
#endif
|
||||
}
|
||||
|
||||
int C_RLIMIT_NICE() {
|
||||
#ifdef RLIMIT_NICE
|
||||
return RLIMIT_NICE;
|
||||
#else
|
||||
return -1;
|
||||
#endif
|
||||
}
|
||||
|
||||
int C_RLIMIT_SIGPENDING() {
|
||||
#ifdef RLIMIT_SIGPENDING
|
||||
return RLIMIT_SIGPENDING;
|
||||
#else
|
||||
return -1;
|
||||
#endif
|
||||
}
|
||||
|
||||
int C_RLIMIT_MEMLOCK() {
|
||||
#ifdef RLIMIT_MEMLOCK
|
||||
return RLIMIT_MEMLOCK;
|
||||
#else
|
||||
return -1;
|
||||
#endif
|
||||
}
|
||||
|
||||
int C_RLIMIT_RSS() {
|
||||
#ifdef RLIMIT_RSS
|
||||
return RLIMIT_RSS;
|
||||
#else
|
||||
return -1;
|
||||
#endif
|
||||
}
|
||||
|
||||
int C_RLIMIT_MSGQUEUE() {
|
||||
#ifdef RLIMIT_MSGQUEUE
|
||||
return RLIMIT_MSGQUEUE;
|
||||
#else
|
||||
return -1;
|
||||
#endif
|
||||
}
|
||||
|
||||
int C_RLIMIT_RTPRIO() {
|
||||
#ifdef RLIMIT_RTPRIO
|
||||
return RLIMIT_RTPRIO;
|
||||
#else
|
||||
return -1;
|
||||
#endif
|
||||
}
|
||||
|
||||
int C_RLIMIT_NPROC() {
|
||||
#ifdef RLIMIT_NPROC
|
||||
return RLIMIT_NPROC;
|
||||
#else
|
||||
return -1;
|
||||
#endif
|
||||
}
|
||||
|
||||
int C_RLIMIT_AS() {
|
||||
#ifdef RLIMIT_AS
|
||||
return RLIMIT_AS;
|
||||
#else
|
||||
return -1;
|
||||
#endif
|
||||
}
|
||||
|
||||
int C_RLIMIT_SWAP() {
|
||||
#ifdef RLIMIT_SWAP
|
||||
return RLIMIT_SWAP;
|
||||
#else
|
||||
return -1;
|
||||
#endif
|
||||
}
|
||||
|
||||
int C_RLIMIT_RTTIME() {
|
||||
#ifdef RLIMIT_RTTIME
|
||||
return RLIMIT_RTTIME;
|
||||
#else
|
||||
return -1;
|
||||
#endif
|
||||
}
|
||||
|
||||
int C_RLIMIT_KQUEUES() {
|
||||
#ifdef RLIMIT_KQUEUES
|
||||
return RLIMIT_KQUEUES;
|
||||
#else
|
||||
return -1;
|
||||
#endif
|
||||
}
|
||||
|
||||
int C_RLIMIT_NPTS() {
|
||||
#ifdef RLIMIT_NPTS
|
||||
return RLIMIT_NPTS;
|
||||
#else
|
||||
return -1;
|
||||
#endif
|
||||
}
|
||||
|
||||
int C_RLIMIT_NTHR() {
|
||||
#ifdef RLIMIT_NTHR
|
||||
return RLIMIT_NTHR;
|
||||
#else
|
||||
return -1;
|
||||
#endif
|
||||
}
|
||||
56
src/libc.rs
56
src/libc.rs
@@ -1,56 +0,0 @@
|
||||
use std::sync::atomic::AtomicPtr;
|
||||
|
||||
use libc::{c_char, c_int};
|
||||
use once_cell::sync::Lazy;
|
||||
|
||||
pub static _PATH_BSHELL: AtomicPtr<c_char> = AtomicPtr::new(std::ptr::null_mut());
|
||||
extern "C" {
|
||||
pub fn C_PATH_BSHELL() -> *const c_char;
|
||||
}
|
||||
|
||||
pub static _PC_CASE_SENSITIVE: Lazy<c_int> = Lazy::new(|| unsafe { C_PC_CASE_SENSITIVE() });
|
||||
extern "C" {
|
||||
fn C_PC_CASE_SENSITIVE() -> c_int;
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
pub fn stdout_stream() -> *mut libc::FILE;
|
||||
pub fn setlinebuf(stream: *mut libc::FILE);
|
||||
}
|
||||
|
||||
macro_rules! CVAR {
|
||||
($cfn:ident, $cvar:ident, $type:ident) => {
|
||||
pub fn $cvar() -> $type {
|
||||
extern "C" {
|
||||
fn $cfn() -> $type;
|
||||
}
|
||||
unsafe { $cfn() }
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
CVAR!(C_MB_CUR_MAX, MB_CUR_MAX, usize);
|
||||
CVAR!(C_ST_LOCAL, ST_LOCAL, u64);
|
||||
CVAR!(C_MNT_LOCAL, MNT_LOCAL, u64);
|
||||
CVAR!(C_CS_PATH, _CS_PATH, i32);
|
||||
|
||||
CVAR!(C_RLIMIT_SBSIZE, RLIMIT_SBSIZE, i32);
|
||||
CVAR!(C_RLIMIT_CORE, RLIMIT_CORE, i32);
|
||||
CVAR!(C_RLIMIT_DATA, RLIMIT_DATA, i32);
|
||||
CVAR!(C_RLIMIT_NICE, RLIMIT_NICE, i32);
|
||||
CVAR!(C_RLIMIT_FSIZE, RLIMIT_FSIZE, i32);
|
||||
CVAR!(C_RLIMIT_SIGPENDING, RLIMIT_SIGPENDING, i32);
|
||||
CVAR!(C_RLIMIT_MEMLOCK, RLIMIT_MEMLOCK, i32);
|
||||
CVAR!(C_RLIMIT_RSS, RLIMIT_RSS, i32);
|
||||
CVAR!(C_RLIMIT_NOFILE, RLIMIT_NOFILE, i32);
|
||||
CVAR!(C_RLIMIT_MSGQUEUE, RLIMIT_MSGQUEUE, i32);
|
||||
CVAR!(C_RLIMIT_RTPRIO, RLIMIT_RTPRIO, i32);
|
||||
CVAR!(C_RLIMIT_STACK, RLIMIT_STACK, i32);
|
||||
CVAR!(C_RLIMIT_CPU, RLIMIT_CPU, i32);
|
||||
CVAR!(C_RLIMIT_NPROC, RLIMIT_NPROC, i32);
|
||||
CVAR!(C_RLIMIT_AS, RLIMIT_AS, i32);
|
||||
CVAR!(C_RLIMIT_SWAP, RLIMIT_SWAP, i32);
|
||||
CVAR!(C_RLIMIT_RTTIME, RLIMIT_RTTIME, i32);
|
||||
CVAR!(C_RLIMIT_KQUEUES, RLIMIT_KQUEUES, i32);
|
||||
CVAR!(C_RLIMIT_NPTS, RLIMIT_NPTS, i32);
|
||||
CVAR!(C_RLIMIT_NTHR, RLIMIT_NTHR, i32);
|
||||
@@ -4,14 +4,14 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use crate::common::{
|
||||
escape_string, get_ellipsis_char, get_ellipsis_str, EscapeFlags, EscapeStringStyle,
|
||||
escape_string, get_ellipsis_char, get_ellipsis_str, get_is_multibyte_locale, EscapeFlags,
|
||||
EscapeStringStyle,
|
||||
};
|
||||
use crate::complete::Completion;
|
||||
use crate::editable_line::EditableLine;
|
||||
#[allow(unused_imports)]
|
||||
use crate::future::IsSomeAnd;
|
||||
use crate::highlight::{highlight_shell, HighlightRole, HighlightSpec};
|
||||
use crate::libc::MB_CUR_MAX;
|
||||
use crate::operation_context::OperationContext;
|
||||
use crate::screen::{wcswidth_rendered, wcwidth_rendered, CharOffset, Line, ScreenData};
|
||||
use crate::termsize::Termsize;
|
||||
@@ -1243,7 +1243,7 @@ fn process_completions_into_infos(lst: &[Completion]) -> Vec<PagerComp> {
|
||||
// We should probably fix this by first highlighting the original completion, and
|
||||
// then writing a variant of escape_string() that adjusts highlighting according
|
||||
// so it matches the escaped string.
|
||||
&& MB_CUR_MAX() > 1
|
||||
&& get_is_multibyte_locale()
|
||||
{
|
||||
highlight_shell(
|
||||
&comp.completion,
|
||||
|
||||
59
src/path.rs
59
src/path.rs
@@ -585,10 +585,10 @@ fn make_base_directory(xdg_var: &wstr, non_xdg_homepath: &wstr) -> BaseDirectory
|
||||
// the actual $HOME or $XDG_XXX directories. This prevents the tests from failing and/or stops
|
||||
// the tests polluting the user's actual $HOME if a sandbox environment has not been set up.
|
||||
{
|
||||
use crate::common::{str2wcstring, BUILD_DIR};
|
||||
use crate::common::str2wcstring;
|
||||
use std::path::PathBuf;
|
||||
|
||||
let mut build_dir = PathBuf::from(BUILD_DIR);
|
||||
let mut build_dir = PathBuf::from(env!("FISH_BUILD_DIR"));
|
||||
build_dir.push("fish-test-home");
|
||||
|
||||
let err = match std::fs::create_dir_all(&build_dir) {
|
||||
@@ -694,49 +694,20 @@ pub fn path_remoteness(path: &wstr) -> DirRemoteness {
|
||||
}
|
||||
#[cfg(not(any(target_os = "linux", cygwin)))]
|
||||
{
|
||||
fn remoteness_via_statfs<StatFS, Flags>(
|
||||
statfn: unsafe extern "C" fn(*const libc::c_char, *mut StatFS) -> libc::c_int,
|
||||
flagsfn: fn(&StatFS) -> Flags,
|
||||
is_local_flag: u64,
|
||||
path: &std::ffi::CStr,
|
||||
) -> DirRemoteness
|
||||
where
|
||||
u64: From<Flags>,
|
||||
{
|
||||
if is_local_flag == 0 {
|
||||
return DirRemoteness::unknown;
|
||||
}
|
||||
let mut buf = MaybeUninit::uninit();
|
||||
if unsafe { (statfn)(path.as_ptr(), buf.as_mut_ptr()) } < 0 {
|
||||
return DirRemoteness::unknown;
|
||||
}
|
||||
let buf = unsafe { buf.assume_init() };
|
||||
// statfs::f_flag is hard-coded as 64-bits on 32/64-bit FreeBSD but it's a (4-byte)
|
||||
// long on 32-bit NetBSD.. and always 4-bytes on macOS (even on 64-bit builds).
|
||||
#[allow(clippy::useless_conversion)]
|
||||
if u64::from((flagsfn)(&buf)) & is_local_flag != 0 {
|
||||
DirRemoteness::local
|
||||
} else {
|
||||
DirRemoteness::remote
|
||||
}
|
||||
let mut buf = MaybeUninit::uninit();
|
||||
if unsafe { libc::statfs(narrow.as_ptr(), buf.as_mut_ptr()) } < 0 {
|
||||
return DirRemoteness::unknown;
|
||||
}
|
||||
let buf = unsafe { buf.assume_init() };
|
||||
// statfs::f_flag is hard-coded as 64-bits on 32/64-bit FreeBSD but it's a (4-byte)
|
||||
// long on 32-bit NetBSD.. and always 4-bytes on macOS (even on 64-bit builds).
|
||||
#[allow(clippy::useless_conversion)]
|
||||
let flags = u64::from(buf.f_flags);
|
||||
if flags & (libc::MNT_LOCAL as u64) != 0 {
|
||||
DirRemoteness::local
|
||||
} else {
|
||||
DirRemoteness::remote
|
||||
}
|
||||
// ST_LOCAL is a flag to statvfs, which is itself standardized.
|
||||
// In practice the only system to define it is NetBSD.
|
||||
#[cfg(target_os = "netbsd")]
|
||||
let remoteness = remoteness_via_statfs(
|
||||
libc::statvfs,
|
||||
|stat: &libc::statvfs| stat.f_flag,
|
||||
crate::libc::ST_LOCAL(),
|
||||
&narrow,
|
||||
);
|
||||
#[cfg(not(target_os = "netbsd"))]
|
||||
let remoteness = remoteness_via_statfs(
|
||||
libc::statfs,
|
||||
|stat: &libc::statfs| stat.f_flags,
|
||||
crate::libc::MNT_LOCAL(),
|
||||
&narrow,
|
||||
);
|
||||
remoteness
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -54,9 +54,10 @@
|
||||
use crate::builtins::shared::STATUS_CMD_OK;
|
||||
use crate::common::ScopeGuarding;
|
||||
use crate::common::{
|
||||
escape, escape_string, exit_without_destructors, get_ellipsis_char, get_obfuscation_read_char,
|
||||
restore_term_foreground_process_group_for_exit, shell_modes, str2wcstring, write_loop,
|
||||
EscapeFlags, EscapeStringStyle, ScopeGuard, PROGRAM_NAME, UTF8_BOM_WCHAR,
|
||||
escape, escape_string, exit_without_destructors, get_ellipsis_char, get_is_multibyte_locale,
|
||||
get_obfuscation_read_char, restore_term_foreground_process_group_for_exit, shell_modes,
|
||||
str2wcstring, write_loop, EscapeFlags, EscapeStringStyle, ScopeGuard, PROGRAM_NAME,
|
||||
UTF8_BOM_WCHAR,
|
||||
};
|
||||
use crate::complete::{
|
||||
complete, complete_load, sort_and_prioritize, CompleteFlags, Completion, CompletionList,
|
||||
@@ -97,7 +98,6 @@
|
||||
use crate::io::IoChain;
|
||||
use crate::key::ViewportPosition;
|
||||
use crate::kill::{kill_add, kill_replace, kill_yank, kill_yank_rotate};
|
||||
use crate::libc::MB_CUR_MAX;
|
||||
use crate::nix::{getpgrp, getpid, isatty};
|
||||
use crate::operation_context::{get_bg_context, OperationContext};
|
||||
use crate::pager::{PageRendering, Pager, SelectionMotion};
|
||||
@@ -1587,7 +1587,7 @@ pub fn mouse_left_click(&mut self, cursor: ViewportPosition, click_position: Vie
|
||||
self.pager.selected_completion_idx = Some(idx);
|
||||
self.pager_selection_changed();
|
||||
}
|
||||
CharOffset::Pager(_) | CharOffset::None => {}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3226,7 +3226,7 @@ fn handle_readline_command(&mut self, c: ReadlineCmd) {
|
||||
// Update the pager data.
|
||||
self.pager.set_search_field_shown(true);
|
||||
self.pager.set_prefix(
|
||||
if MB_CUR_MAX() > 1 {
|
||||
if get_is_multibyte_locale() {
|
||||
L!("► ")
|
||||
} else {
|
||||
L!("> ")
|
||||
|
||||
@@ -629,26 +629,8 @@ pub fn offset_in_cmdline_given_cursor(
|
||||
0
|
||||
});
|
||||
let y = y.min(self.actual.line_count() - 1);
|
||||
let Some(viewport_prompt_x) = viewport_cursor.x.checked_sub(self.actual.cursor.x) else {
|
||||
FLOGF!(
|
||||
reader,
|
||||
"Actual cursor x=%d exceeds reported cursor x=%d, \
|
||||
was the cursor moved by printing to the TTY?",
|
||||
self.actual.cursor.x,
|
||||
viewport_cursor.x
|
||||
);
|
||||
return CharOffset::None;
|
||||
};
|
||||
let Some(x) = viewport_position.x.checked_sub(viewport_prompt_x) else {
|
||||
FLOGF!(
|
||||
reader,
|
||||
"Computed prompt x=%d exceeds mouse click x=%d, \
|
||||
was the cursor moved by printing to the TTY?",
|
||||
viewport_prompt_x,
|
||||
viewport_position.x
|
||||
);
|
||||
return CharOffset::None;
|
||||
};
|
||||
let viewport_prompt_x = viewport_cursor.x - self.actual.cursor.x;
|
||||
let x = viewport_position.x - viewport_prompt_x;
|
||||
let line = self.actual.line(y);
|
||||
let x = x.max(line.indentation);
|
||||
let offset = line
|
||||
|
||||
@@ -29,7 +29,7 @@
|
||||
mod wgetopt;
|
||||
|
||||
pub mod prelude {
|
||||
use crate::common::{ScopeGuard, ScopeGuarding, BUILD_DIR};
|
||||
use crate::common::{ScopeGuard, ScopeGuarding};
|
||||
use crate::env::{env_init, misc_init};
|
||||
use crate::parser::{CancelBehavior, Parser};
|
||||
use crate::reader::{reader_deinit, reader_init};
|
||||
@@ -88,9 +88,9 @@ fn deref(&self) -> &Self::Target {
|
||||
pub fn test_init() -> impl ScopeGuarding<Target = ()> {
|
||||
static DONE: OnceCell<()> = OnceCell::new();
|
||||
DONE.get_or_init(|| {
|
||||
// If we are building with `cargo build` and have build w/ `cmake`, this might not
|
||||
// yet exist.
|
||||
let mut test_dir = PathBuf::from(BUILD_DIR);
|
||||
// If we are building with `cargo build` and have build w/ `cmake`, FISH_BUILD_DIR might
|
||||
// not yet exist.
|
||||
let mut test_dir = PathBuf::from(env!("FISH_BUILD_DIR"));
|
||||
test_dir.push("fish-test");
|
||||
std::fs::create_dir_all(&test_dir).unwrap();
|
||||
set_current_dir(&test_dir).unwrap();
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
use std::sync::MutexGuard;
|
||||
|
||||
use crate::common::{
|
||||
escape_string, str2wcstring, unescape_string, wcs2string, EscapeFlags, EscapeStringStyle,
|
||||
UnescapeStringStyle, ENCODE_DIRECT_BASE, ENCODE_DIRECT_END,
|
||||
escape_string, fish_setlocale, str2wcstring, unescape_string, wcs2string, EscapeFlags,
|
||||
EscapeStringStyle, UnescapeStringStyle, ENCODE_DIRECT_BASE, ENCODE_DIRECT_END,
|
||||
};
|
||||
use crate::locale::LOCALE_LOCK;
|
||||
use crate::util::{get_rng_seed, get_seeded_rng};
|
||||
use crate::wchar::{wstr, WString, L};
|
||||
use crate::wutil::encoding::{wcrtomb, zero_mbstate, AT_LEAST_MB_LEN_MAX};
|
||||
use crate::wutil::encoding::{
|
||||
probe_is_multibyte_locale, wcrtomb, zero_mbstate, AT_LEAST_MB_LEN_MAX,
|
||||
};
|
||||
use rand::{Rng, RngCore};
|
||||
|
||||
/// wcs2string is locale-dependent, so ensure we have a multibyte locale
|
||||
@@ -19,13 +21,14 @@ fn setlocale() -> MutexGuard<'static, ()> {
|
||||
const UTF8_LOCALES: &[&str] = &[
|
||||
"C.UTF-8", "en_US.UTF-8", "en_GB.UTF-8", "de_DE.UTF-8", "C.utf8", "UTF-8",
|
||||
];
|
||||
if crate::libc::MB_CUR_MAX() > 1 {
|
||||
if probe_is_multibyte_locale() {
|
||||
return guard;
|
||||
}
|
||||
for locale in UTF8_LOCALES {
|
||||
let locale = std::ffi::CString::new(locale.to_owned()).unwrap();
|
||||
unsafe { libc::setlocale(libc::LC_CTYPE, locale.as_ptr()) };
|
||||
if crate::libc::MB_CUR_MAX() > 1 {
|
||||
if probe_is_multibyte_locale() {
|
||||
fish_setlocale(); // Update cached locale information.
|
||||
return guard;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
use crate::threads::assert_is_main_thread;
|
||||
use crate::wchar::prelude::*;
|
||||
use crate::wchar_ext::ToWString;
|
||||
use crate::wutil::{perror, wcstoi};
|
||||
use crate::wutil::{fish_wcstoul, perror, wcstoi};
|
||||
use libc::{EINVAL, ENOTTY, EPERM, STDIN_FILENO, WNOHANG};
|
||||
use once_cell::sync::OnceCell;
|
||||
use std::mem::MaybeUninit;
|
||||
@@ -27,6 +27,11 @@
|
||||
/// Whether kitty keyboard protocol support is present in the TTY.
|
||||
static KITTY_KEYBOARD_SUPPORTED: OnceCell<bool> = OnceCell::new();
|
||||
|
||||
/// Get whether the TTY supports the kitty keyboard protocol.
|
||||
pub fn get_kitty_keyboard_capability() -> Option<&'static bool> {
|
||||
KITTY_KEYBOARD_SUPPORTED.get()
|
||||
}
|
||||
|
||||
/// Set that the TTY supports the kitty keyboard protocol.
|
||||
pub fn maybe_set_kitty_keyboard_capability() {
|
||||
KITTY_KEYBOARD_SUPPORTED.get_or_init(|| true);
|
||||
@@ -61,6 +66,8 @@ pub enum TtyQuirks {
|
||||
PreCsiMidnightCommander,
|
||||
// Running in iTerm2 before 3.5.12, which causes issues when using the kitty keyboard protocol.
|
||||
PreKittyIterm2,
|
||||
// Running in Zellij before 0.42.0 which fails to activate the kitty keyboard protocol.
|
||||
PreKittyZellij,
|
||||
// Whether we are running under tmux.
|
||||
Tmux,
|
||||
// Whether we are running under WezTerm.
|
||||
@@ -77,6 +84,8 @@ fn detect(vars: &dyn Environment, xtversion: &wstr) -> Self {
|
||||
PreCsiMidnightCommander
|
||||
} else if get_iterm2_version(xtversion).is_some_and(|v| v < (3, 5, 12)) {
|
||||
PreKittyIterm2
|
||||
} else if get_zellij_version(xtversion).is_some_and(|v| v < 4200) {
|
||||
PreKittyZellij
|
||||
} else if xtversion.starts_with(L!("tmux ")) {
|
||||
Tmux
|
||||
} else if xtversion.starts_with(L!("WezTerm ")) {
|
||||
@@ -151,11 +160,11 @@ impl TtyQuirks {
|
||||
// Determine which keyboard protocol.
|
||||
// This is used from a signal handler.
|
||||
fn safe_get_supported_protocol(&self) -> ProtocolKind {
|
||||
use TtyQuirks::{PreCsiMidnightCommander, PreKittyIterm2, Wezterm};
|
||||
use TtyQuirks::{PreCsiMidnightCommander, PreKittyIterm2, PreKittyZellij, Wezterm};
|
||||
if *self == PreCsiMidnightCommander {
|
||||
return ProtocolKind::None;
|
||||
}
|
||||
if *self == PreKittyIterm2 {
|
||||
if matches!(*self, PreKittyIterm2 | PreKittyZellij) {
|
||||
return ProtocolKind::Other;
|
||||
}
|
||||
match KITTY_KEYBOARD_SUPPORTED.get() {
|
||||
@@ -626,3 +635,8 @@ fn get_iterm2_version(xtversion: &wstr) -> Option<(u32, u32, u32)> {
|
||||
wcstoi(parts.next()?).ok()?,
|
||||
))
|
||||
}
|
||||
|
||||
fn get_zellij_version(xtversion: &wstr) -> Option<u64> {
|
||||
let number = xtversion.strip_prefix("Zellij(")?.strip_suffix(")")?;
|
||||
fish_wcstoul(number).ok()
|
||||
}
|
||||
|
||||
@@ -297,6 +297,14 @@ fn ends_with<Suffix: IntoCharIter>(&self, suffix: Suffix) -> bool {
|
||||
)
|
||||
}
|
||||
|
||||
fn strip_suffix<Suffix: IntoCharIter>(&self, suffix: Suffix) -> Option<&wstr> {
|
||||
// Note the above trait requires a double ended iterator.
|
||||
let iter = suffix.chars().rev();
|
||||
let suffix_len = iter.clone().count();
|
||||
iter_prefixes_iter(iter, self.as_char_slice().iter().rev().copied())
|
||||
.then(|| self.slice_to(self.char_count() - suffix_len))
|
||||
}
|
||||
|
||||
fn trim_matches(&self, pat: char) -> &wstr {
|
||||
let slice = self.as_char_slice();
|
||||
let leading_count = slice.chars().take_while(|&c| c == pat).count();
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
//! Helper functions for working with wcstring.
|
||||
|
||||
use crate::common::{get_ellipsis_char, get_ellipsis_str};
|
||||
use crate::common::{get_ellipsis_char, get_ellipsis_str, get_is_multibyte_locale};
|
||||
use crate::expand::INTERNAL_SEPARATOR;
|
||||
use crate::fallback::{fish_wcwidth, wcscasecmp, wcscasecmp_fuzzy};
|
||||
use crate::flog::FLOGF;
|
||||
use crate::libc::MB_CUR_MAX;
|
||||
use crate::wchar::{decode_byte_from_char, prelude::*};
|
||||
use crate::wutil::encoding::{wcrtomb, zero_mbstate, AT_LEAST_MB_LEN_MAX};
|
||||
|
||||
@@ -312,7 +311,7 @@ pub fn wcs2string_callback(input: &wstr, mut func: impl FnMut(&[u8]) -> bool) ->
|
||||
let mut state = zero_mbstate();
|
||||
let mut converted = [0_u8; AT_LEAST_MB_LEN_MAX];
|
||||
|
||||
let is_singlebyte_locale = MB_CUR_MAX() == 1;
|
||||
let is_singlebyte_locale = !get_is_multibyte_locale();
|
||||
|
||||
for c in input.chars() {
|
||||
// TODO: this doesn't seem sound.
|
||||
|
||||
@@ -8,9 +8,43 @@
|
||||
// HACK This should be mbstate_t from libc but that's not exposed. Since it's only written by
|
||||
// libc, we define it as opaque type that should be large enough for all implementations.
|
||||
pub type mbstate_t = [u64; 16];
|
||||
|
||||
#[inline]
|
||||
pub fn zero_mbstate() -> mbstate_t {
|
||||
[0; 16]
|
||||
}
|
||||
|
||||
// HACK This should be the MB_LEN_MAX macro from libc but that's not easy to get.
|
||||
pub const AT_LEAST_MB_LEN_MAX: usize = 32;
|
||||
|
||||
/// Return true if we believe we are in a multibyte locale.
|
||||
/// Note this reads the current locale and is modestly expensive - prefer the cached
|
||||
/// values in `common.rs` which is set by `fish_setlocale`.
|
||||
pub fn probe_is_multibyte_locale() -> bool {
|
||||
// In general we would like to read MB_CUR_MAX, but that is not exposed by Rust libc.
|
||||
// Instead, check if mbrtowc for any byte in the range 0-255 returns (size_t)(-2) which indicates
|
||||
// the presence of a multibyte locale.
|
||||
#[inline]
|
||||
fn is_mb_lead(b: u8) -> bool {
|
||||
let mut st = zero_mbstate();
|
||||
let mut wc: libc::wchar_t = 0;
|
||||
let c = b as libc::c_char;
|
||||
let n = unsafe {
|
||||
mbrtowc(
|
||||
std::ptr::addr_of_mut!(wc).cast::<u32>(),
|
||||
std::ptr::addr_of!(c),
|
||||
1,
|
||||
std::ptr::addr_of_mut!(st),
|
||||
)
|
||||
};
|
||||
n == (-2_i64 as libc::size_t)
|
||||
}
|
||||
|
||||
// Fast path: check common lead bytes.
|
||||
if is_mb_lead(0xE0) || is_mb_lead(0xC2) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Scan non-ASCII high bytes.
|
||||
(0x80_u8..=0xFF).any(is_mb_lead)
|
||||
}
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
# RUN: %fish -d config | grep -v ^Debug.enabled
|
||||
# REQUIRES: %fish -c 'status build-info' | grep '^Features:.*embed-data'
|
||||
|
||||
# CHECKERR: config: executable path: {{.*}}/fish
|
||||
# CHECKERR: config: embed-data feature is active, ignoring data paths
|
||||
|
||||
# NOTE: When our executable is located outside the workspace, this is "/etc".
|
||||
# CHECKERR: config: paths.sysconf: {{.+}}/etc
|
||||
|
||||
# CHECKERR: config: paths.bin: {{.*}}
|
||||
# CHECKERR: config: sourcing {{.+}}/etc/config.fish
|
||||
# CHECKERR: config: not sourcing {{.*}}/xdg_config_home/fish/config.fish (not readable or does not exist)
|
||||
@@ -1,13 +1,13 @@
|
||||
#RUN: %fish %s
|
||||
|
||||
# This still demos the current theme.
|
||||
fish_config theme show non-existant-theme
|
||||
fish_config theme show non-existent-theme
|
||||
# CHECK: {{\x1b\[m}}{{\x1b\[4m}}Current{{\x1b\[m}}
|
||||
# CHECK: /bright/vixens{{\x1b\[m}} jump{{\x1b\[m}} |{{\x1b\[m}} "fowl"{{\x1b\[m}} > quack{{\x1b\[m}} &{{\x1b\[m}} # This is a comment
|
||||
# CHECK: {{\x1b\[m}}echo{{\x1b\[m}} 'Errors are the portal to discovery
|
||||
# CHECK: {{\x1b\[m}}Th{{\x1b\[m}}is an autosuggestion
|
||||
|
||||
fish_config prompt show non-existant-prompt
|
||||
fish_config prompt show non-existent-prompt
|
||||
|
||||
fish_config prompt show default
|
||||
# CHECK: {{\x1b\[4m}}default{{\x1b\[m}}
|
||||
@@ -23,21 +23,6 @@ fish_config theme show "fish default"
|
||||
# CHECK: {{\x1b\[m}}{{\x1b\[m}}echo{{\x1b\[m}} {{\x1b\[91m}}'{{\x1b\[33m}}Errors are the portal to discovery
|
||||
# CHECK: {{\x1b\[m}}{{\x1b\[m}}Th{{\x1b\[m}}{{\x1b\[90m}}is an autosuggestion
|
||||
|
||||
mkdir $__fish_config_dir/themes
|
||||
touch $__fish_config_dir/themes/custom-from-userconf.theme
|
||||
fish_config theme show | grep -E 'fish default|Default Dark|custom-from-userconf'
|
||||
# CHECK: {{\x1b\[m}}{{\x1b\[4m}}custom-from-userconf{{\x1b\[m}}
|
||||
fish_config theme show | grep -E 'fish default|Default Dark'
|
||||
# CHECK: {{\x1b\[m}}{{\x1b\[4m}}Base16 Default Dark{{\x1b\[m}}
|
||||
# CHECK: {{\x1b\[m}}{{\x1b\[4m}}fish default{{\x1b\[m}}
|
||||
|
||||
# Override a default theme with different colors.
|
||||
{
|
||||
status get-file tools/web_config/themes/None.theme 2>/dev/null ||
|
||||
cat $__fish_data_dir/tools/web_config/themes/None.theme
|
||||
} >$__fish_config_dir/themes/"fish default.theme"
|
||||
fish_config theme show | grep -E 'fish default|Base16 Default Dark' -A1
|
||||
# CHECK: {{\x1b\[m}}{{\x1b\[4m}}fish default{{\x1b\[m}}
|
||||
# CHECK: {{\x1b\[m}}/bright/vixens{{\x1b\[m}} {{\x1b\[m}}jump{{\x1b\[m}}{{.*}}
|
||||
# CHECK: --
|
||||
# CHECK: {{\x1b\[m}}{{\x1b\[4m}}Base16 Default Dark{{\x1b\[m}}
|
||||
# CHECK: {{.*}}/bright/vixens{{.*}}
|
||||
|
||||
@@ -186,10 +186,4 @@ function foo; echo before; end
|
||||
foo (functions --erase foo)
|
||||
# CHECKERR: error: Unknown function 'foo'
|
||||
|
||||
function ()
|
||||
end
|
||||
# CHECKERR: {{.*}}/tests/checks/function.fish (line {{\d+}}): function: function name required
|
||||
# CHECKERR: function ()
|
||||
# CHECKERR: ^
|
||||
|
||||
exit 0
|
||||
|
||||
Reference in New Issue
Block a user