From d8cfcda53ac5b694fa1fff43e71b81fa6dc80c2b Mon Sep 17 00:00:00 2001 From: Navaneeth Yadamreddy Date: Thu, 27 Aug 2026 21:25:02 +0530 Subject: [PATCH 1/2] uucore: treat empty locale env vars as unset Empty LC_ALL (or LC_CTYPE, LANG) means 'use the next variable in the cascade', per POSIX. Previously get_locale_from_env parsed the empty string as the C/POSIX locale with ASCII encoding, which caused ls to escape non-ASCII filenames even when LC_CTYPE or LANG specified UTF-8. Fixes #13964 --- src/uucore/src/lib/features/i18n/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/uucore/src/lib/features/i18n/mod.rs b/src/uucore/src/lib/features/i18n/mod.rs index b834c17d4f4..c8db6dbb5e7 100644 --- a/src/uucore/src/lib/features/i18n/mod.rs +++ b/src/uucore/src/lib/features/i18n/mod.rs @@ -38,7 +38,7 @@ const DEFAULT_LOCALE: Locale = locale!("und"); pub fn get_locale_from_env(locale_name: &str) -> (Locale, UEncoding) { let locale_var = ["LC_ALL", locale_name, "LANG"] .iter() - .find_map(|&key| std::env::var(key).ok()); + .find_map(|&key| std::env::var(key).ok().filter(|v| !v.is_empty())); if let Some(locale_var_str) = locale_var { let mut split = locale_var_str.split(&['.', '@']); From 9307cb6b7c0abb76fee5cb056536b7c0ed20b76a Mon Sep 17 00:00:00 2001 From: Navaneeth Yadamreddy Date: Fri, 28 Aug 2026 09:08:12 +0530 Subject: [PATCH 2/2] ls: add test for empty LC_ALL falling through to LANG --- tests/by-util/test_ls.rs | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/tests/by-util/test_ls.rs b/tests/by-util/test_ls.rs index 186f942e1ae..f91f6a416f0 100644 --- a/tests/by-util/test_ls.rs +++ b/tests/by-util/test_ls.rs @@ -3384,6 +3384,27 @@ mod quoting { .succeeds() .stdout_is("é\n"); } + + #[test] + fn test_empty_lc_all_falls_through_to_lang() { + let scene = TestScenario::new(util_name!()); + let at = &scene.fixtures; + // Create a file with a non-ASCII character. + at.touch("café"); + + // When LC_ALL is empty it should be treated as unset, + // falling through to LANG for locale resolution. + // Without the fix, empty LC_ALL was treated as POSIX, + // causing the non-ASCII name to be octal-escaped. + scene + .ucmd() + .env("LC_ALL", "") + .env("LANG", "en_US.UTF-8") + .env("LC_CTYPE", "en_US.UTF-8") + .args(&["--quoting-style=literal"]) + .succeeds() + .stdout_contains("café"); + } } #[test]