diff options
Diffstat (limited to 'vendor/gix-path/src')
-rw-r--r-- | vendor/gix-path/src/convert.rs | 10 | ||||
-rw-r--r-- | vendor/gix-path/src/env/git.rs | 89 | ||||
-rw-r--r-- | vendor/gix-path/src/env/mod.rs | 117 | ||||
-rw-r--r-- | vendor/gix-path/src/lib.rs | 3 |
4 files changed, 219 insertions, 0 deletions
diff --git a/vendor/gix-path/src/convert.rs b/vendor/gix-path/src/convert.rs index 6a949529f..d8bf70353 100644 --- a/vendor/gix-path/src/convert.rs +++ b/vendor/gix-path/src/convert.rs @@ -82,6 +82,16 @@ pub fn into_bstr<'a>(path: impl Into<Cow<'a, Path>>) -> Cow<'a, BStr> { try_into_bstr(path).expect("prefix path doesn't contain ill-formed UTF-8") } +/// Join `path` to `base` such that they are separated with a `/`, i.e. `base/path`. +pub fn join_bstr_unix_pathsep<'a, 'b>(base: impl Into<Cow<'a, BStr>>, path: impl Into<&'b BStr>) -> Cow<'a, BStr> { + let mut base = base.into(); + if !base.is_empty() && base.last() != Some(&b'/') { + base.to_mut().push(b'/'); + } + base.to_mut().extend_from_slice(path.into()); + base +} + /// Given `input` bytes, produce a `Path` from them ignoring encoding entirely if on unix. /// /// On windows, the input is required to be valid UTF-8, which is guaranteed if we wrote it before. There are some potential diff --git a/vendor/gix-path/src/env/git.rs b/vendor/gix-path/src/env/git.rs new file mode 100644 index 000000000..2e5d867bb --- /dev/null +++ b/vendor/gix-path/src/env/git.rs @@ -0,0 +1,89 @@ +use std::{ + path::Path, + process::{Command, Stdio}, +}; + +use bstr::{BStr, BString, ByteSlice}; + +/// Returns the file that contains git configuration coming with the installation of the `git` file in the current `PATH`, or `None` +/// if no `git` executable was found or there were other errors during execution. +pub(crate) fn install_config_path() -> Option<&'static BStr> { + static PATH: once_cell::sync::Lazy<Option<BString>> = once_cell::sync::Lazy::new(|| { + // Shortcut: in Msys shells this variable is set which allows to deduce the installation directory + // so we can save the `git` invocation. + #[cfg(windows)] + if let Some(mut exec_path) = std::env::var_os("EXEPATH").map(std::path::PathBuf::from) { + exec_path.push("etc"); + exec_path.push("gitconfig"); + return crate::os_string_into_bstring(exec_path.into()).ok(); + } + let mut cmd = Command::new(if cfg!(windows) { "git.exe" } else { "git" }); + cmd.args(["config", "-l", "--show-origin"]) + .stdin(Stdio::null()) + .stderr(Stdio::null()); + first_file_from_config_with_origin(cmd.output().ok()?.stdout.as_slice().into()).map(ToOwned::to_owned) + }); + PATH.as_ref().map(|b| b.as_ref()) +} + +fn first_file_from_config_with_origin(source: &BStr) -> Option<&BStr> { + let file = source.strip_prefix(b"file:")?; + let end_pos = file.find_byte(b'\t')?; + file[..end_pos].trim_with(|c| c == '"').as_bstr().into() +} + +/// Given `config_path` as obtained from `install_config_path()`, return the path of the git installation base. +pub(crate) fn config_to_base_path(config_path: &Path) -> &Path { + config_path + .parent() + .expect("config file paths always have a file name to pop") +} + +#[cfg(test)] +mod tests { + use std::path::Path; + + #[test] + fn config_to_base_path() { + for (input, expected) in [ + ( + "/Applications/Xcode.app/Contents/Developer/usr/share/git-core/gitconfig", + "/Applications/Xcode.app/Contents/Developer/usr/share/git-core", + ), + ("C:/git-sdk-64/etc/gitconfig", "C:/git-sdk-64/etc"), + ("C:\\ProgramData/Git/config", "C:\\ProgramData/Git"), + ("C:/Program Files/Git/etc/gitconfig", "C:/Program Files/Git/etc"), + ] { + assert_eq!(super::config_to_base_path(Path::new(input)), Path::new(expected)); + } + } + #[test] + fn first_file_from_config_with_origin() { + let macos = "file:/Applications/Xcode.app/Contents/Developer/usr/share/git-core/gitconfig credential.helper=osxkeychain\nfile:/Users/byron/.gitconfig push.default=simple\n"; + let win_msys = + "file:C:/git-sdk-64/etc/gitconfig core.symlinks=false\r\nfile:C:/git-sdk-64/etc/gitconfig core.autocrlf=true"; + let win_cmd = "file:C:/Program Files/Git/etc/gitconfig diff.astextplain.textconv=astextplain\r\nfile:C:/Program Files/Git/etc/gitconfig filter.lfs.clean=gix-lfs clean -- %f\r\n"; + let win_msys_old = "file:\"C:\\ProgramData/Git/config\" diff.astextplain.textconv=astextplain\r\nfile:\"C:\\ProgramData/Git/config\" filter.lfs.clean=git-lfs clean -- %f\r\n"; + let linux = "file:/home/parallels/.gitconfig core.excludesfile=~/.gitignore\n"; + let bogus = "something unexpected"; + let empty = ""; + + for (source, expected) in [ + ( + macos, + Some("/Applications/Xcode.app/Contents/Developer/usr/share/git-core/gitconfig"), + ), + (win_msys, Some("C:/git-sdk-64/etc/gitconfig")), + (win_msys_old, Some("C:\\ProgramData/Git/config")), + (win_cmd, Some("C:/Program Files/Git/etc/gitconfig")), + (linux, Some("/home/parallels/.gitconfig")), + (bogus, None), + (empty, None), + ] { + assert_eq!( + super::first_file_from_config_with_origin(source.into()), + expected.map(Into::into) + ); + } + } +} diff --git a/vendor/gix-path/src/env/mod.rs b/vendor/gix-path/src/env/mod.rs new file mode 100644 index 000000000..170222c7a --- /dev/null +++ b/vendor/gix-path/src/env/mod.rs @@ -0,0 +1,117 @@ +use std::{ + ffi::OsString, + path::{Path, PathBuf}, +}; + +use bstr::{BString, ByteSlice}; + +mod git; + +/// Return the location at which installation specific git configuration file can be found, or `None` +/// if the binary could not be executed or its results could not be parsed. +/// +/// ### Performance +/// +/// This invokes the git binary which is slow on windows. +pub fn installation_config() -> Option<&'static Path> { + git::install_config_path().and_then(|p| crate::try_from_byte_slice(p).ok()) +} + +/// Return the location at which git installation specific configuration files are located, or `None` if the binary +/// could not be executed or its results could not be parsed. +/// +/// ### Performance +/// +/// This invokes the git binary which is slow on windows. +pub fn installation_config_prefix() -> Option<&'static Path> { + installation_config().map(git::config_to_base_path) +} + +/// Returns the fully qualified path in the *xdg-home* directory (or equivalent in the home dir) to `file`, +/// accessing `env_var(<name>)` to learn where these bases are. +/// +/// Note that the `HOME` directory should ultimately come from [`home_dir()`] as it handles windows correctly. +/// The same can be achieved by using [`var()`] as `env_var`. +pub fn xdg_config(file: &str, env_var: &mut dyn FnMut(&str) -> Option<OsString>) -> Option<PathBuf> { + env_var("XDG_CONFIG_HOME") + .map(|home| { + let mut p = PathBuf::from(home); + p.push("git"); + p.push(file); + p + }) + .or_else(|| { + env_var("HOME").map(|home| { + let mut p = PathBuf::from(home); + p.push(".config"); + p.push("git"); + p.push(file); + p + }) + }) +} + +/// Returns the platform dependent system prefix or `None` if it cannot be found (right now only on windows). +/// +/// ### Performance +/// +/// On windows, the slowest part is the launch of the `git.exe` executable in the PATH, which only happens when launched +/// from outside of the `msys2` shell. +/// +/// ### When `None` is returned +/// +/// This happens only windows if the git binary can't be found at all for obtaining its executable path, or if the git binary +/// wasn't built with a well-known directory structure or environment. +pub fn system_prefix() -> Option<&'static Path> { + if cfg!(windows) { + static PREFIX: once_cell::sync::Lazy<Option<PathBuf>> = once_cell::sync::Lazy::new(|| { + if let Some(root) = std::env::var_os("EXEPATH").map(PathBuf::from) { + for candidate in ["mingw64", "mingw32"] { + let candidate = root.join(candidate); + if candidate.is_dir() { + return Some(candidate); + } + } + } + + let path = std::process::Command::new("git.exe") + .arg("--exec-path") + .stderr(std::process::Stdio::null()) + .output() + .ok()? + .stdout; + let path = BString::new(path) + .trim_with(|b| b.is_ascii_whitespace()) + .to_path() + .ok()? + .to_owned(); + + let one_past_prefix = path.components().enumerate().find_map(|(idx, c)| { + matches!(c,std::path::Component::Normal(name) if name.to_str() == Some("libexec")).then_some(idx) + })?; + Some(path.components().take(one_past_prefix.checked_sub(1)?).collect()) + }); + PREFIX.as_deref() + } else { + Path::new("/").into() + } +} + +/// Returns `$HOME` or `None` if it cannot be found. +#[cfg(target_family = "wasm")] +pub fn home_dir() -> Option<PathBuf> { + std::env::var("HOME").map(PathBuf::from).ok() +} + +#[cfg(not(target_family = "wasm"))] +pub use home::home_dir; + +/// Returns the contents of an environment variable of `name` with some special handling +/// for certain environment variables (like `HOME`) for platform compatibility. +pub fn var(name: &str) -> Option<OsString> { + if name == "HOME" { + home_dir().map(PathBuf::into_os_string) + } else { + std::env::var_os(name) + } +} diff --git a/vendor/gix-path/src/lib.rs b/vendor/gix-path/src/lib.rs index 70a9bc53f..6895aca46 100644 --- a/vendor/gix-path/src/lib.rs +++ b/vendor/gix-path/src/lib.rs @@ -66,3 +66,6 @@ mod spec; /// pub mod realpath; pub use realpath::function::{realpath, realpath_opts}; + +/// Information about the environment in terms of locations of resources. +pub mod env; |