mirror of
https://github.com/fish-shell/fish-shell.git
synced 2024-12-18 20:53:40 +08:00
Make fish installable
When built with the default "installable" feature, the data files (share/) are included in the fish binary itself. Run `fish --install` or `fish --install=noconfirm` (for non-interactive use) to install fish's data files into ~/.local/share/fish/install To figure out if the data files are out of date, we write the current version to a file on install, and read it on start. CMake disables the default features so nothing changes for that, but this allows installing via `cargo install`, and even making a static binary that you can then just upload and have extract itself. We set $__fish_help_dir to empty for installable builds, because we do not have a way to generate html docs (because we need fish_indent for highlighting). The man pages are found via $__fish_data_dir/man
This commit is contained in:
parent
7827a8e533
commit
7c73c5fec0
|
@ -58,6 +58,7 @@ function(CREATE_TARGET target)
|
|||
$<$<CONFIG:Release>:--release>
|
||||
$<$<CONFIG:RelWithDebInfo>:--release>
|
||||
--target ${Rust_CARGO_TARGET}
|
||||
--no-default-features
|
||||
${CARGO_FLAGS}
|
||||
${FEATURES_ARG}
|
||||
&&
|
||||
|
|
158
Cargo.lock
generated
158
Cargo.lock
generated
|
@ -20,6 +20,15 @@ version = "2.6.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de"
|
||||
|
||||
[[package]]
|
||||
name = "block-buffer"
|
||||
version = "0.10.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
|
||||
dependencies = [
|
||||
"generic-array",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.1.30"
|
||||
|
@ -43,6 +52,25 @@ version = "0.2.1"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724"
|
||||
|
||||
[[package]]
|
||||
name = "cpufeatures"
|
||||
version = "0.2.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "608697df725056feaccfa42cffdaeeec3fccc4ffc38358ecd19b243e716a78e0"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crypto-common"
|
||||
version = "0.1.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3"
|
||||
dependencies = [
|
||||
"generic-array",
|
||||
"typenum",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dashmap"
|
||||
version = "5.5.3"
|
||||
|
@ -56,6 +84,16 @@ dependencies = [
|
|||
"parking_lot_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "digest"
|
||||
version = "0.10.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
|
||||
dependencies = [
|
||||
"block-buffer",
|
||||
"crypto-common",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "equivalent"
|
||||
version = "1.0.1"
|
||||
|
@ -69,7 +107,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||
checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys",
|
||||
"windows-sys 0.52.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -90,6 +128,7 @@ dependencies = [
|
|||
"portable-atomic",
|
||||
"rand",
|
||||
"rsconf",
|
||||
"rust-embed",
|
||||
"serial_test",
|
||||
"terminfo",
|
||||
"widestring",
|
||||
|
@ -115,6 +154,16 @@ version = "0.1.3"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f81ec6369c545a7d40e4589b5597581fa1c441fe1cce96dd1de43159910a36a2"
|
||||
|
||||
[[package]]
|
||||
name = "generic-array"
|
||||
version = "0.14.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
|
||||
dependencies = [
|
||||
"typenum",
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.14.5"
|
||||
|
@ -371,6 +420,49 @@ dependencies = [
|
|||
"cc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rust-embed"
|
||||
version = "8.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fa66af4a4fdd5e7ebc276f115e895611a34739a9c1c01028383d612d550953c0"
|
||||
dependencies = [
|
||||
"rust-embed-impl",
|
||||
"rust-embed-utils",
|
||||
"walkdir",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rust-embed-impl"
|
||||
version = "8.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6125dbc8867951125eec87294137f4e9c2c96566e61bf72c45095a7c77761478"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"rust-embed-utils",
|
||||
"syn 2.0.79",
|
||||
"walkdir",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rust-embed-utils"
|
||||
version = "8.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2e5347777e9aacb56039b0e1f28785929a8a3b709e87482e7442c72e7c12529d"
|
||||
dependencies = [
|
||||
"sha2",
|
||||
"walkdir",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "same-file"
|
||||
version = "1.0.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
|
||||
dependencies = [
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "scopeguard"
|
||||
version = "1.2.0"
|
||||
|
@ -397,7 +489,18 @@ checksum = "079a83df15f85d89a68d64ae1238f142f172b1fa915d0d76b26a7cba1b659a69"
|
|||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"syn 1.0.109",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sha2"
|
||||
version = "0.10.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"cpufeatures",
|
||||
"digest",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -429,6 +532,17 @@ dependencies = [
|
|||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.79"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "89132cd0bf050864e1d38dc3bbc07a0eb8e7530af26344d3d2bbbef83499f590"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "terminfo"
|
||||
version = "0.9.0"
|
||||
|
@ -441,18 +555,49 @@ dependencies = [
|
|||
"phf_codegen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "typenum"
|
||||
version = "1.17.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe"
|
||||
|
||||
[[package]]
|
||||
name = "version_check"
|
||||
version = "0.9.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
|
||||
|
||||
[[package]]
|
||||
name = "walkdir"
|
||||
version = "2.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b"
|
||||
dependencies = [
|
||||
"same-file",
|
||||
"winapi-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "widestring"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7219d36b6eac893fa81e84ebe06485e7dcbb616177469b142df14f1f4deb1311"
|
||||
|
||||
[[package]]
|
||||
name = "winapi-util"
|
||||
version = "0.1.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb"
|
||||
dependencies = [
|
||||
"windows-sys 0.59.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.52.0"
|
||||
|
@ -462,6 +607,15 @@ dependencies = [
|
|||
"windows-targets",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.59.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
|
||||
dependencies = [
|
||||
"windows-targets",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.52.6"
|
||||
|
|
|
@ -53,6 +53,7 @@ rand = { version = "0.8.5", default-features = false, features = ["small_rng"] }
|
|||
widestring = "1.1.0"
|
||||
# We need 0.9.0 specifically for some crash fixes.
|
||||
terminfo = "0.9.0"
|
||||
rust-embed = { version = "8.2.0", optional = true }
|
||||
|
||||
[target.'cfg(not(target_has_atomic = "64"))'.dependencies]
|
||||
portable-atomic = { version = "1", default-features = false, features = [
|
||||
|
@ -83,8 +84,9 @@ name = "fish_key_reader"
|
|||
path = "src/bin/fish_key_reader.rs"
|
||||
|
||||
[features]
|
||||
default = []
|
||||
default = ["installable"]
|
||||
benchmark = []
|
||||
installable = ["dep:rust-embed"]
|
||||
|
||||
# The following features are auto-detected by the build-script and should not be enabled manually.
|
||||
asan = []
|
||||
|
|
27
build.rs
27
build.rs
|
@ -223,10 +223,18 @@ fn setup_paths() {
|
|||
var
|
||||
}
|
||||
|
||||
let prefix = PathBuf::from(env::var("PREFIX").unwrap_or("/usr/local".to_string()));
|
||||
if prefix.is_relative() {
|
||||
let (prefix_from_home, prefix) = if let Ok(pre) = env::var("PREFIX") {
|
||||
(false, PathBuf::from(pre))
|
||||
} else {
|
||||
(true, PathBuf::from(".local/"))
|
||||
};
|
||||
|
||||
// If someone gives us a $PREFIX, we need it to be absolute.
|
||||
// Otherwise we would try to get it from $HOME and that won't really work.
|
||||
if !prefix_from_home && prefix.is_relative() {
|
||||
panic!("Can't have relative prefix");
|
||||
}
|
||||
|
||||
rsconf::rebuild_if_env_changed("PREFIX");
|
||||
rsconf::set_env_value("PREFIX", prefix.to_str().unwrap());
|
||||
|
||||
|
@ -234,11 +242,24 @@ fn setup_paths() {
|
|||
rsconf::set_env_value("DATADIR", datadir.to_str().unwrap());
|
||||
rsconf::rebuild_if_env_changed("DATADIR");
|
||||
|
||||
let datadir_subdir = if prefix_from_home {
|
||||
"fish/install"
|
||||
} else {
|
||||
"fish"
|
||||
};
|
||||
rsconf::set_env_value("DATADIR_SUBDIR", datadir_subdir);
|
||||
|
||||
let bindir = get_path("BINDIR", "bin/", prefix.clone());
|
||||
rsconf::set_env_value("BINDIR", bindir.to_str().unwrap());
|
||||
rsconf::rebuild_if_env_changed("BINDIR");
|
||||
|
||||
let sysconfdir = get_path("SYSCONFDIR", "etc/", datadir.clone());
|
||||
let sysconfdir = get_path(
|
||||
"SYSCONFDIR",
|
||||
// If we get our prefix from $HOME, we should use the system's /etc/
|
||||
// ~/.local/share/etc/ makes no sense
|
||||
if prefix_from_home { "/etc/" } else { "etc/" },
|
||||
datadir.clone(),
|
||||
);
|
||||
rsconf::set_env_value("SYSCONFDIR", sysconfdir.to_str().unwrap());
|
||||
rsconf::rebuild_if_env_changed("SYSCONFDIR");
|
||||
|
||||
|
|
|
@ -181,7 +181,7 @@ function help --description 'Show help for the fish shell'
|
|||
set -l version_string (string split . -f 1,2 -- $version | string join .)
|
||||
set -l ext_url https://fishshell.com/docs/$version_string/$fish_help_page
|
||||
set -l page_url
|
||||
if test -f $__fish_help_dir/index.html; and not set -lq chromeos_linux_garcon
|
||||
if set -q __fish_help_dir[1]; and test -f $__fish_help_dir/index.html; and not set -lq chromeos_linux_garcon
|
||||
# Help is installed, use it
|
||||
set page_url file://$__fish_help_dir/$fish_help_page
|
||||
|
||||
|
|
188
src/bin/fish.rs
188
src/bin/fish.rs
|
@ -21,6 +21,8 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
|||
#![allow(unstable_name_collisions)]
|
||||
#![allow(clippy::uninlined_format_args)]
|
||||
|
||||
#[allow(unused_imports)]
|
||||
use fish::future::IsSomeAnd;
|
||||
use fish::{
|
||||
ast::Ast,
|
||||
builtins::shared::{
|
||||
|
@ -71,9 +73,103 @@ use std::{env, ops::ControlFlow};
|
|||
|
||||
const DOC_DIR: &str = env!("DOCDIR");
|
||||
const DATA_DIR: &str = env!("DATADIR");
|
||||
const DATA_DIR_SUBDIR: &str = env!("DATADIR_SUBDIR");
|
||||
const SYSCONF_DIR: &str = env!("SYSCONFDIR");
|
||||
const BIN_DIR: &str = env!("BINDIR");
|
||||
|
||||
#[cfg(feature = "installable")]
|
||||
fn install(confirm: bool) {
|
||||
use rust_embed::RustEmbed;
|
||||
|
||||
#[derive(RustEmbed)]
|
||||
#[folder = "share/"]
|
||||
struct Asset;
|
||||
|
||||
use std::fs;
|
||||
use std::io::ErrorKind;
|
||||
use std::io::Write;
|
||||
use std::io::{stderr, stdin};
|
||||
let Some(home) = fish::env::get_home() else {
|
||||
eprintln!("Can't find $HOME",);
|
||||
std::process::exit(1);
|
||||
};
|
||||
let dir = PathBuf::from(home).join(DATA_DIR).join(DATA_DIR_SUBDIR);
|
||||
|
||||
// TODO: Translation,
|
||||
// FLOG?
|
||||
// - Install: Translations
|
||||
// - Install: Manpages (build via build.rs)
|
||||
// - Don't install: __fish_build_paths.fish.in
|
||||
if confirm {
|
||||
if isatty(libc::STDIN_FILENO) {
|
||||
eprintln!(
|
||||
"This will write fish's data files to '{}'.\n\
|
||||
Please enter 'yes' to continue.",
|
||||
dir.display()
|
||||
);
|
||||
eprint!("> ");
|
||||
let _ = stderr().flush();
|
||||
}
|
||||
|
||||
let mut input = String::new();
|
||||
if let Err(error) = stdin().read_line(&mut input) {
|
||||
eprintln!("error: {error}")
|
||||
}
|
||||
|
||||
if input != "yes\n" {
|
||||
eprintln!("Exiting without writing any files\n");
|
||||
std::process::exit(1);
|
||||
}
|
||||
} else {
|
||||
eprintln!("Installing fish's data files to '{}'.", dir.display());
|
||||
}
|
||||
|
||||
// Remove the install directory first, to clean out any removed files.
|
||||
if let Err(err) = fs::remove_dir_all(dir.clone()) {
|
||||
if err.kind() != ErrorKind::NotFound {
|
||||
eprintln!("Removing '{}' failed: {}", dir.display(), err);
|
||||
std::process::exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
for file in Asset::iter() {
|
||||
let path = dir.join(file.as_ref());
|
||||
let Ok(_) = fs::create_dir_all(path.parent().unwrap()) else {
|
||||
eprintln!(
|
||||
"Creating directory '{}' failed",
|
||||
path.parent().unwrap().display()
|
||||
);
|
||||
std::process::exit(1);
|
||||
};
|
||||
let res = File::create(&path);
|
||||
let Ok(mut f) = res else {
|
||||
eprintln!("Creating file '{}' failed", path.display());
|
||||
continue;
|
||||
};
|
||||
// This should be impossible.
|
||||
let d = Asset::get(&file).expect("File was somehow not included???");
|
||||
if let Err(error) = f.write_all(&d.data) {
|
||||
eprintln!("error: {error}");
|
||||
std::process::exit(1);
|
||||
}
|
||||
}
|
||||
let verfile = dir.join("fish-install-version");
|
||||
let res = File::create(&verfile);
|
||||
if let Ok(mut f) = res {
|
||||
f.write_all(fish::BUILD_VERSION.as_bytes())
|
||||
.expect("FAILED TO WRITE");
|
||||
} else {
|
||||
eprintln!("Creating file '{}' failed", verfile.display());
|
||||
};
|
||||
std::process::exit(0);
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "installable"))]
|
||||
fn install(_confirm: bool) {
|
||||
eprintln!("Fish was built without support for self-installation");
|
||||
std::process::exit(1);
|
||||
}
|
||||
|
||||
/// container to hold the options specified within the command line
|
||||
#[derive(Default, Debug)]
|
||||
struct FishCmdOpts {
|
||||
|
@ -207,12 +303,31 @@ fn determine_config_directory_paths(argv0: impl AsRef<Path>) -> ConfigPaths {
|
|||
|
||||
if !done {
|
||||
// Fall back to what got compiled in.
|
||||
let data = if cfg!(feature = "installable") {
|
||||
let Some(home) = fish::env::get_home() else {
|
||||
FLOG!(
|
||||
error,
|
||||
"Cannot find home directory and will refuse to read configuration"
|
||||
);
|
||||
return paths;
|
||||
};
|
||||
|
||||
PathBuf::from(home).join(DATA_DIR).join(DATA_DIR_SUBDIR)
|
||||
} else {
|
||||
PathBuf::from(DATA_DIR).join(DATA_DIR_SUBDIR)
|
||||
};
|
||||
let bin = if cfg!(feature = "installable") {
|
||||
exec_path.parent().map(|x| x.to_path_buf())
|
||||
} else {
|
||||
Some(PathBuf::from(BIN_DIR))
|
||||
};
|
||||
|
||||
FLOG!(config, "Using compiled in paths:");
|
||||
paths = ConfigPaths {
|
||||
data: PathBuf::from(DATA_DIR).join("fish"),
|
||||
data,
|
||||
sysconf: PathBuf::from(SYSCONF_DIR).join("fish"),
|
||||
doc: DOC_DIR.into(),
|
||||
bin: Some(BIN_DIR.into()),
|
||||
bin,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -266,6 +381,49 @@ fn source_config_in_directory(parser: &Parser, dir: &wstr) -> bool {
|
|||
/// Parse init files. exec_path is the path of fish executable as determined by argv[0].
|
||||
fn read_init(parser: &Parser, paths: &ConfigPaths) {
|
||||
let datapath = str2wcstring(paths.data.as_os_str().as_bytes());
|
||||
|
||||
#[cfg(feature = "installable")]
|
||||
{
|
||||
// (false-positive, is_none_or is a backport, this builds with 1.70)
|
||||
#[allow(clippy::incompatible_msrv)]
|
||||
if paths
|
||||
.bin
|
||||
.clone()
|
||||
.is_none_or(|x| !x.starts_with(env!("CARGO_MANIFEST_DIR")))
|
||||
{
|
||||
// When fish is installable, we write the version to a file,
|
||||
// now we check it.
|
||||
let verfile =
|
||||
PathBuf::from(fish::common::wcs2osstring(&datapath)).join("fish-install-version");
|
||||
let version = match std::fs::read_to_string(verfile) {
|
||||
Ok(x) => x,
|
||||
Err(err) => {
|
||||
let escaped_pathname = escape(&datapath);
|
||||
FLOGF!(
|
||||
error,
|
||||
"Fish cannot find its asset files in '%ls'.\n\
|
||||
Refusing to read configuration because of this.\n\
|
||||
The underlying error is: '%ls'",
|
||||
escaped_pathname,
|
||||
err.to_string()
|
||||
);
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
if version != fish::BUILD_VERSION {
|
||||
FLOGF!(
|
||||
error,
|
||||
"Asset files are version %s, this fish is version %s. Please run `fish --install` again",
|
||||
version,
|
||||
fish::BUILD_VERSION
|
||||
);
|
||||
// We could refuse to read any config,
|
||||
// but that seems a bit harsh.
|
||||
// return;
|
||||
}
|
||||
}
|
||||
}
|
||||
if !source_config_in_directory(parser, &datapath) {
|
||||
// If we cannot read share/config.fish, our internal configuration,
|
||||
// something is wrong.
|
||||
|
@ -274,8 +432,14 @@ fn read_init(parser: &Parser, paths: &ConfigPaths) {
|
|||
let escaped_pathname = escape(&datapath);
|
||||
FLOGF!(
|
||||
error,
|
||||
"Fish cannot find its asset files in '%ls'. Refusing to read configuration.",
|
||||
escaped_pathname
|
||||
"Fish cannot find its asset files in '%ls'.\n\
|
||||
Refusing to read configuration because of this.",
|
||||
escaped_pathname,
|
||||
);
|
||||
#[cfg(feature = "installable")]
|
||||
FLOG!(
|
||||
error,
|
||||
"If you installed via `cargo install`, please run `fish --install` and restart fish."
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
@ -336,6 +500,7 @@ fn fish_parse_opt(args: &mut [WString], opts: &mut FishCmdOpts) -> ControlFlow<i
|
|||
wopt(L!("no-config"), NoArgument, 'N'),
|
||||
wopt(L!("no-execute"), NoArgument, 'n'),
|
||||
wopt(L!("print-rusage-self"), NoArgument, RUSAGE_ARG),
|
||||
wopt(L!("install"), OptionalArgument, 'I'),
|
||||
wopt(
|
||||
L!("print-debug-categories"),
|
||||
NoArgument,
|
||||
|
@ -370,6 +535,21 @@ fn fish_parse_opt(args: &mut [WString], opts: &mut FishCmdOpts) -> ControlFlow<i
|
|||
'f' => opts.features = w.woptarg.unwrap().to_owned(),
|
||||
'h' => opts.batch_cmds.push("__fish_print_help fish".into()),
|
||||
'i' => opts.is_interactive_session = true,
|
||||
'I' => {
|
||||
let noconfirm = match w.woptarg {
|
||||
None => false,
|
||||
Some(n) if n == L!("noconfirm") => true,
|
||||
_ => {
|
||||
FLOGF!(
|
||||
error,
|
||||
"Unknown argument to --install: '%ls'",
|
||||
w.woptarg.unwrap()
|
||||
);
|
||||
std::process::exit(1);
|
||||
}
|
||||
};
|
||||
install(!noconfirm);
|
||||
}
|
||||
'l' => opts.is_login = true,
|
||||
'N' => {
|
||||
opts.no_config = true;
|
||||
|
|
78
src/env/environment.rs
vendored
78
src/env/environment.rs
vendored
|
@ -452,6 +452,39 @@ fn get_hostname_identifier() -> Option<WString> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Get values for $HOME via getpwuid,
|
||||
/// without trusting $USER or $HOME.
|
||||
pub fn get_home() -> Option<String> {
|
||||
let uid: uid_t = geteuid();
|
||||
|
||||
let mut userinfo: MaybeUninit<libc::passwd> = MaybeUninit::uninit();
|
||||
let mut result: *mut libc::passwd = std::ptr::null_mut();
|
||||
let mut buf = [0 as libc::c_char; 8192];
|
||||
|
||||
// We need to get the data via the uid and don't trust $USER.
|
||||
let retval = unsafe {
|
||||
libc::getpwuid_r(
|
||||
uid,
|
||||
userinfo.as_mut_ptr(),
|
||||
buf.as_mut_ptr(),
|
||||
buf.len(),
|
||||
&mut result,
|
||||
)
|
||||
};
|
||||
if retval != 0 || result.is_null() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let userinfo = unsafe { userinfo.assume_init() };
|
||||
if !userinfo.pw_dir.is_null() {
|
||||
let home = unsafe { CStr::from_ptr(userinfo.pw_dir) };
|
||||
let home = home.to_str().ok().map(|x| x.to_owned());
|
||||
home
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Set up the USER and HOME variable.
|
||||
fn setup_user(vars: &EnvStack) {
|
||||
let uid: uid_t = geteuid();
|
||||
|
@ -595,22 +628,30 @@ pub fn env_init(paths: Option<&ConfigPaths>, do_uvars: bool, default_paths: bool
|
|||
.set(inherited_vars)
|
||||
.expect("env_init is being called multiple times");
|
||||
|
||||
// Set $USER, $HOME and $EUID
|
||||
// This involves going to passwd and stuff.
|
||||
vars.set_one(L!("EUID"), EnvMode::GLOBAL, geteuid().to_wstring());
|
||||
setup_user(vars);
|
||||
|
||||
if let Some(paths) = paths {
|
||||
vars.set_one(
|
||||
FISH_DATADIR_VAR,
|
||||
EnvMode::GLOBAL,
|
||||
str2wcstring(paths.data.as_os_str().as_bytes()),
|
||||
);
|
||||
let datadir = str2wcstring(paths.data.as_os_str().as_bytes());
|
||||
|
||||
vars.set_one(FISH_DATADIR_VAR, EnvMode::GLOBAL, datadir.clone());
|
||||
vars.set_one(
|
||||
FISH_SYSCONFDIR_VAR,
|
||||
EnvMode::GLOBAL,
|
||||
str2wcstring(paths.sysconf.as_os_str().as_bytes()),
|
||||
);
|
||||
vars.set_one(
|
||||
FISH_HELPDIR_VAR,
|
||||
EnvMode::GLOBAL,
|
||||
str2wcstring(paths.doc.as_os_str().as_bytes()),
|
||||
);
|
||||
|
||||
if !cfg!(feature = "installable") {
|
||||
vars.set_one(
|
||||
FISH_HELPDIR_VAR,
|
||||
EnvMode::GLOBAL,
|
||||
str2wcstring(paths.doc.as_os_str().as_bytes()),
|
||||
);
|
||||
} else {
|
||||
vars.set_empty(FISH_HELPDIR_VAR, EnvMode::GLOBAL);
|
||||
}
|
||||
if let Some(bp) = &paths.bin {
|
||||
vars.set_one(
|
||||
FISH_BIN_DIR,
|
||||
|
@ -622,21 +663,14 @@ pub fn env_init(paths: Option<&ConfigPaths>, do_uvars: bool, default_paths: bool
|
|||
};
|
||||
|
||||
if default_paths {
|
||||
let mut scstr = paths.data.clone();
|
||||
scstr.push("functions");
|
||||
vars.set_one(
|
||||
L!("fish_function_path"),
|
||||
EnvMode::GLOBAL,
|
||||
str2wcstring(scstr.as_os_str().as_bytes()),
|
||||
);
|
||||
let mut scstr = datadir;
|
||||
// This is generated by PathBuf.join() everywhere currently
|
||||
assert!(!scstr.ends_with("/"));
|
||||
scstr.push_str("/functions");
|
||||
vars.set_one(L!("fish_function_path"), EnvMode::GLOBAL, scstr);
|
||||
}
|
||||
}
|
||||
|
||||
// Set $USER, $HOME and $EUID
|
||||
// This involves going to passwd and stuff.
|
||||
vars.set_one(L!("EUID"), EnvMode::GLOBAL, geteuid().to_wstring());
|
||||
setup_user(vars);
|
||||
|
||||
let user_config_dir = path_get_config();
|
||||
vars.set_one(
|
||||
FISH_CONFIG_DIR,
|
||||
|
|
Loading…
Reference in New Issue
Block a user