Skip to content

Commit 17755d0

Browse files
committed
locale.rs: move more code outside of the unsafe block
and refactor a few things
1 parent 16f7350 commit 17755d0

File tree

1 file changed

+55
-45
lines changed

1 file changed

+55
-45
lines changed

src/uu/date/src/locale.rs

Lines changed: 55 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -34,70 +34,80 @@ cfg_langinfo! {
3434
/// Cached result of locale time format detection
3535
static TIME_FORMAT_CACHE: OnceLock<bool> = OnceLock::new();
3636

37-
/// Internal function that performs the actual locale detection
38-
fn detect_12_hour_format() -> bool {
37+
/// Safe wrapper around libc setlocale
38+
fn set_time_locale() {
3939
unsafe {
40-
// Set locale from environment variables (empty string = use LC_TIME/LANG env vars)
41-
libc::setlocale(libc::LC_TIME, c"".as_ptr());
40+
nix::libc::setlocale(nix::libc::LC_TIME, c"".as_ptr());
41+
}
42+
}
4243

43-
// Get the date/time format string from locale
44-
let d_t_fmt_ptr = libc::nl_langinfo(libc::D_T_FMT);
45-
if d_t_fmt_ptr.is_null() {
46-
return false;
44+
/// Safe wrapper around libc nl_langinfo that returns `Option<String>`
45+
fn get_locale_info(item: nix::libc::nl_item) -> Option<String> {
46+
unsafe {
47+
let ptr = nix::libc::nl_langinfo(item);
48+
if ptr.is_null() {
49+
None
50+
} else {
51+
CStr::from_ptr(ptr).to_str().ok().map(String::from)
52+
}
4753
}
54+
}
4855

49-
let Ok(format) = CStr::from_ptr(d_t_fmt_ptr).to_str() else {
50-
return false;
51-
};
56+
/// Internal function that performs the actual locale detection
57+
fn detect_12_hour_format() -> bool {
58+
// Helper function to check for 12-hour format indicators
59+
fn has_12_hour_indicators(format_str: &str) -> bool {
60+
const INDICATORS: &[&str] = &["%I", "%l", "%r"];
61+
INDICATORS.iter().any(|&indicator| format_str.contains(indicator))
62+
}
5263

53-
// Check for 12-hour indicators first (higher priority)
54-
// %I = hour (01-12), %l = hour (1-12) space-padded, %r = 12-hour time with AM/PM
55-
if format.contains("%I") || format.contains("%l") || format.contains("%r") {
56-
return true;
64+
// Helper function to check for 24-hour format indicators
65+
fn has_24_hour_indicators(format_str: &str) -> bool {
66+
const INDICATORS: &[&str] = &["%H", "%k", "%R", "%T"];
67+
INDICATORS.iter().any(|&indicator| format_str.contains(indicator))
5768
}
5869

59-
// If we find 24-hour indicators, it's definitely not 12-hour
60-
// %H = hour (00-23), %k = hour (0-23) space-padded, %R = %H:%M, %T = %H:%M:%S
61-
if format.contains("%H")
62-
|| format.contains("%k")
63-
|| format.contains("%R")
64-
|| format.contains("%T")
65-
{
66-
return false;
70+
// Set locale from environment variables (empty string = use LC_TIME/LANG env vars)
71+
set_time_locale();
72+
73+
// Get locale format strings using safe wrappers
74+
let d_t_fmt = get_locale_info(nix::libc::D_T_FMT);
75+
let t_fmt_opt = get_locale_info(nix::libc::T_FMT);
76+
let t_fmt_ampm_opt = get_locale_info(nix::libc::T_FMT_AMPM);
77+
78+
// Check D_T_FMT first
79+
if let Some(ref format) = d_t_fmt {
80+
// Check for 12-hour indicators first (higher priority)
81+
if has_12_hour_indicators(format) {
82+
return true;
83+
}
84+
85+
// If we find 24-hour indicators, it's definitely not 12-hour
86+
if has_24_hour_indicators(format) {
87+
return false;
88+
}
6789
}
6890

6991
// Also check the time-only format as a fallback
70-
let t_fmt_ptr = libc::nl_langinfo(libc::T_FMT);
71-
let mut time_fmt_opt = None;
72-
if !t_fmt_ptr.is_null() {
73-
if let Ok(time_format) = CStr::from_ptr(t_fmt_ptr).to_str() {
74-
time_fmt_opt = Some(time_format);
75-
if time_format.contains("%I")
76-
|| time_format.contains("%l")
77-
|| time_format.contains("%r")
78-
{
79-
return true;
80-
}
92+
if let Some(ref time_format) = t_fmt_opt {
93+
if has_12_hour_indicators(time_format) {
94+
return true;
8195
}
8296
}
8397

8498
// Check if there's a specific 12-hour format defined
85-
let t_fmt_ampm_ptr = libc::nl_langinfo(libc::T_FMT_AMPM);
86-
if !t_fmt_ampm_ptr.is_null() {
87-
if let Ok(ampm_format) = CStr::from_ptr(t_fmt_ampm_ptr).to_str() {
88-
// If T_FMT_AMPM is non-empty and different from T_FMT, locale supports 12-hour
89-
if !ampm_format.is_empty() {
90-
if let Some(time_format) = time_fmt_opt {
91-
if ampm_format != time_format {
92-
return true;
93-
}
94-
} else {
99+
if let Some(ref ampm_format) = t_fmt_ampm_opt {
100+
// If T_FMT_AMPM is non-empty and different from T_FMT, locale supports 12-hour
101+
if !ampm_format.is_empty() {
102+
if let Some(ref time_format) = t_fmt_opt {
103+
if ampm_format != time_format {
95104
return true;
96105
}
106+
} else {
107+
return true;
97108
}
98109
}
99110
}
100-
}
101111

102112
// Default to 24-hour format if we can't determine
103113
false

0 commit comments

Comments
 (0)