From 38f454d5ab98aaf2c4833c33d964dead732a4042 Mon Sep 17 00:00:00 2001 From: Skyler Hawthorne Date: Mon, 28 Aug 2023 02:53:25 -0400 Subject: [PATCH] Support Termux (#10013) --- crates/nu-path/Cargo.toml | 2 +- crates/nu-path/src/tilde.rs | 119 ++++++++++++++++++++---------------- 2 files changed, 69 insertions(+), 52 deletions(-) diff --git a/crates/nu-path/Cargo.toml b/crates/nu-path/Cargo.toml index a9bd345365..b1fba82318 100644 --- a/crates/nu-path/Cargo.toml +++ b/crates/nu-path/Cargo.toml @@ -16,5 +16,5 @@ dirs-next = "2.0" [target.'cfg(windows)'.dependencies] omnipath = "0.1" -[target.'cfg(all(unix, not(target_os = "macos")))'.dependencies] +[target.'cfg(all(unix, not(target_os = "macos"), not(target_os = "android")))'.dependencies] pwd = "1.3" diff --git a/crates/nu-path/src/tilde.rs b/crates/nu-path/src/tilde.rs index 0d5b3312ed..48c43a74a2 100644 --- a/crates/nu-path/src/tilde.rs +++ b/crates/nu-path/src/tilde.rs @@ -1,7 +1,22 @@ -#[cfg(all(unix, not(target_os = "macos")))] +#[cfg(all(unix, not(target_os = "macos"), not(target_os = "android")))] use pwd::Passwd; use std::path::{Path, PathBuf}; +#[cfg(target_os = "macos")] +const FALLBACK_USER_HOME_BASE_DIR: &str = "/Users"; + +#[cfg(target_os = "windows")] +const FALLBACK_USER_HOME_BASE_DIR: &str = "C:\\Users\\"; + +#[cfg(all(unix, not(target_os = "macos"), not(target_os = "android")))] +const FALLBACK_USER_HOME_BASE_DIR: &str = "/home"; + +#[cfg(all(unix, target_os = "android"))] +const FALLBACK_USER_HOME_BASE_DIR: &str = "/data"; + +#[cfg(target_os = "android")] +const TERMUX_HOME: &str = "/data/data/com.termux/files/home"; + fn expand_tilde_with_home(path: impl AsRef, home: Option) -> PathBuf { let path = path.as_ref(); @@ -45,77 +60,61 @@ fn expand_tilde_with_home(path: impl AsRef, home: Option) -> Path } } -#[cfg(all(unix, not(target_os = "macos")))] +fn fallback_home_dir(username: &str) -> PathBuf { + PathBuf::from_iter([FALLBACK_USER_HOME_BASE_DIR, username]) +} + +#[cfg(all(unix, not(target_os = "macos"), not(target_os = "android")))] fn user_home_dir(username: &str) -> PathBuf { let passwd = Passwd::from_name(username); match &passwd.ok() { Some(Some(dir)) => PathBuf::from(&dir.dir), - _ => { - let mut file = String::from("/home/"); - file.push_str(username); - PathBuf::from(file) - } + _ => fallback_home_dir(username), } - // PathBuf::from(concat!("/home/", username)), - // Returns home dir of user. } -#[cfg(target_os = "macos")] +#[cfg(any(target_os = "android", target_os = "windows", target_os = "macos"))] fn user_home_dir(username: &str) -> PathBuf { + use std::path::Component; + match dirs_next::home_dir() { None => { - let mut expected_path = String::from("/Users/"); - expected_path.push_str(username); - let path = Path::new(&expected_path); - let mut home = PathBuf::new(); - home.push(path); - home + // Termux always has the same home directory + #[cfg(target_os = "android")] + if is_termux() { + return PathBuf::from(TERMUX_HOME); + } + + fallback_home_dir(username) } Some(user) => { let mut expected_path = user; - expected_path.pop(); - expected_path.push(Path::new(username)); + + if !cfg!(target_os = "android") + && expected_path + .components() + .last() + .map(|last| last != Component::Normal(username.as_ref())) + .unwrap_or(false) + { + expected_path.pop(); + expected_path.push(Path::new(username)); + } + if expected_path.is_dir() { expected_path } else { - let mut expected_path_as_string = String::from("/Users/"); - expected_path_as_string.push_str(username); - let path = Path::new(&expected_path_as_string); - let mut home = PathBuf::new(); - home.push(path); - home + fallback_home_dir(username) } } } } -#[cfg(target_os = "windows")] -fn user_home_dir(username: &str) -> PathBuf { - match dirs_next::home_dir() { - None => { - let mut expected_path = String::from("C:\\Users\\"); - expected_path.push_str(username); - let path = Path::new(&expected_path); - let mut home = PathBuf::new(); - home.push(path); - home - } - Some(user) => { - let mut expected_path = user; - expected_path.pop(); - expected_path.push(Path::new(username)); - if expected_path.is_dir() { - expected_path - } else { - let mut expected_path_as_string = String::from("C:\\Users\\"); - expected_path_as_string.push_str(username); - let path = Path::new(&expected_path_as_string); - let mut home = PathBuf::new(); - home.push(path); - home - } - } - } +/// Returns true if the shell is running inside the Termux terminal emulator +/// app. +#[cfg(target_os = "android")] +fn is_termux() -> bool { + std::env::var("TERMUX_VERSION").is_ok() } fn expand_tilde_with_another_user_home(path: &Path) -> PathBuf { @@ -211,4 +210,22 @@ mod tests { fn string_with_double_tilde_backslash() { check_expanded("~\\\\test\\test2/test3"); } + + // [TODO] Figure out how to reliably test with real users. + #[test] + fn user_home_dir_fallback() { + let user = "nonexistent"; + let expected_home = PathBuf::from_iter([FALLBACK_USER_HOME_BASE_DIR, user]); + + #[cfg(target_os = "android")] + let expected_home = if is_termux() { + PathBuf::from(TERMUX_HOME) + } else { + expected_home + }; + + let actual_home = super::user_home_dir(user); + + assert_eq!(expected_home, actual_home, "wrong home"); + } }