From 3f94a3e4d7fbd70656179f3716279b36dfe0eb1c Mon Sep 17 00:00:00 2001 From: Kevin Burke Date: Fri, 20 Jun 2025 13:47:15 -0700 Subject: [PATCH] lib/mods/locale: fix locale loading I'm not sure exactly why this is happening, but when I install coreutils with `make install PREFIX=some-prefix`, before this patch coreutils would look in the wrong place for locales - it would look in e.g. "bin/cut/en-US.ftl" instead of in share/locales/cut/en-US.ftl. This patch fixes the issue, add a test as well. Fixes #8115. --- src/uucore/src/lib/mods/locale.rs | 71 ++++++++++++++++++++++++------- 1 file changed, 56 insertions(+), 15 deletions(-) diff --git a/src/uucore/src/lib/mods/locale.rs b/src/uucore/src/lib/mods/locale.rs index 3bce3a3d7..246d9c975 100644 --- a/src/uucore/src/lib/mods/locale.rs +++ b/src/uucore/src/lib/mods/locale.rs @@ -324,6 +324,31 @@ pub fn setup_localization(p: &str) -> Result<(), LocalizationError> { init_localization(&locale, &locales_dir) } +#[cfg(not(debug_assertions))] +fn resolve_locales_dir_from_exe_dir(exe_dir: &Path, p: &str) -> Option { + // 1. /locales/ + let coreutils = exe_dir.join("locales").join(p); + if coreutils.exists() { + return Some(coreutils); + } + + // 2. /share/locales/ + if let Some(prefix) = exe_dir.parent() { + let fhs = prefix.join("share").join("locales").join(p); + if fhs.exists() { + return Some(fhs); + } + } + + // 3. / (legacy fall-back) + let fallback = exe_dir.join(p); + if fallback.exists() { + return Some(fallback); + } + + None +} + /// Helper function to get the locales directory based on the build configuration fn get_locales_dir(p: &str) -> Result { #[cfg(debug_assertions)] @@ -365,23 +390,14 @@ fn get_locales_dir(p: &str) -> Result { LocalizationError::PathResolution("Failed to get executable directory".to_string()) })?; - // Try the coreutils-style path first - let coreutils_path = exe_dir.join("locales").join(p); - if coreutils_path.exists() { - return Ok(coreutils_path); + if let Some(dir) = resolve_locales_dir_from_exe_dir(exe_dir, p) { + return Ok(dir); } - // Fallback to just the parameter as a relative path - let fallback_path = exe_dir.join(p); - if fallback_path.exists() { - return Ok(fallback_path); - } - - return Err(LocalizationError::LocalesDirNotFound(format!( - "Release locales directory not found at {} or {}", - coreutils_path.display(), - fallback_path.display() - ))); + Err(LocalizationError::LocalesDirNotFound(format!( + "Release locales directory not found starting from {}", + exe_dir.display() + ))) } } @@ -1088,3 +1104,28 @@ invalid-syntax = This is { $missing } } } + +#[cfg(all(test, not(debug_assertions)))] +mod fhs_tests { + use super::*; + use tempfile::TempDir; + + #[test] + fn resolves_fhs_share_locales_layout() { + // 1. Set up a fake installation prefix in a temp directory + let prefix = TempDir::new().unwrap(); // e.g. /tmp/xyz + let bin_dir = prefix.path().join("bin"); // /tmp/xyz/bin + let share_dir = prefix.path().join("share").join("locales").join("cut"); // /tmp/xyz/share/locales/cut + std::fs::create_dir_all(&share_dir).unwrap(); + std::fs::create_dir_all(&bin_dir).unwrap(); + + // 2. Pretend the executable lives in /bin + let exe_dir = bin_dir.as_path(); + + // 3. Ask the helper to resolve the locales dir + let result = resolve_locales_dir_from_exe_dir(exe_dir, "cut") + .expect("should find locales via FHS path"); + + assert_eq!(result, share_dir); + } +}