relibc/header/locale/
mod.rs1use alloc::{boxed::Box, ffi::CString, string::String};
6use core::{ptr, str::FromStr};
7
8use crate::{
9 c_str::CStr,
10 error::{Errno, ResultExtPtrMut},
11 fs::File,
12 header::{errno, fcntl},
13 io::Read,
14 platform::types::{c_char, c_int},
15};
16
17static mut C_LOCALE: [c_char; 2] = [b'C' as c_char, 0];
19
20mod constants;
21use constants::*;
22mod data;
23use data::*;
24
25use super::bits_locale_t::locale_t;
26static mut POSIX_LOCALE: lconv = posix_lconv();
29pub const LC_GLOBAL_LOCALE: locale_t = -1isize as locale_t;
30static mut GLOBAL_LOCALE: *mut GlobalLocaleData = ptr::null_mut();
32#[thread_local]
34pub(crate) static mut THREAD_LOCALE: *mut LocaleData = ptr::null_mut();
35
36#[unsafe(no_mangle)]
38pub unsafe extern "C" fn localeconv() -> *mut lconv {
39 let current = unsafe { uselocale(ptr::null_mut()) };
40 if current == LC_GLOBAL_LOCALE || current.is_null() {
41 if !unsafe { GLOBAL_LOCALE.is_null() } {
42 unsafe { &raw mut (*GLOBAL_LOCALE).data.lconv }
44 } else {
45 &raw mut POSIX_LOCALE
46 }
47 } else {
48 let current = current.cast::<LocaleData>();
49 unsafe { &raw mut (*current).lconv }
50 }
51}
52
53#[unsafe(no_mangle)]
55pub unsafe extern "C" fn setlocale(category: c_int, locale: *const c_char) -> *mut c_char {
56 if unsafe { GLOBAL_LOCALE.is_null() } {
57 let new_global = GlobalLocaleData::new();
58 unsafe { GLOBAL_LOCALE = Box::into_raw(new_global) };
59 };
60 let Some(global) = (unsafe { GLOBAL_LOCALE.as_mut() }) else {
61 return ptr::null_mut();
62 };
63
64 if locale.is_null() {
65 let Some(name) = global.get_name(category) else {
66 return ptr::null_mut();
67 };
68 return name.as_ptr().cast_mut();
69 }
70
71 let name = unsafe { CStr::from_ptr(locale).to_str().unwrap_or("C") };
72
73 let locale_file = if name.is_empty() || name == "C" || name == "POSIX" {
74 Ok(LocaleData::posix())
76 } else {
77 load_locale_file(name)
78 };
79
80 match locale_file {
81 Ok(loc_ptr) => {
82 global.data.copy_category(&loc_ptr, category);
83 let Some(name) = global.set_name(category, CString::from_str(name).unwrap()) else {
84 return ptr::null_mut();
85 };
86 name.as_ptr().cast_mut()
87 }
88 Err(_) => ptr::null_mut(),
89 }
90}
91
92#[unsafe(no_mangle)]
94pub unsafe extern "C" fn uselocale(newloc: locale_t) -> locale_t {
95 let old_loc = if unsafe { THREAD_LOCALE.is_null() } {
96 LC_GLOBAL_LOCALE
97 } else {
98 (unsafe { THREAD_LOCALE }) as locale_t
99 };
100
101 if !newloc.is_null() {
102 unsafe {
103 THREAD_LOCALE = if newloc == LC_GLOBAL_LOCALE {
104 ptr::null_mut()
105 } else {
106 newloc.cast::<LocaleData>()
107 }
108 };
109 }
110
111 old_loc
112}
113
114#[unsafe(no_mangle)]
116pub unsafe extern "C" fn newlocale(mask: c_int, locale: *const c_char, base: locale_t) -> locale_t {
117 let name = unsafe { CStr::from_ptr(locale) }
118 .to_string_lossy()
119 .into_owned();
120 let name = name.as_str();
121 let mut new_locale = if name.is_empty() || name == "C" || name == "POSIX" {
122 Ok(LocaleData::posix())
124 } else {
125 load_locale_file(name)
126 };
127 if base != LC_GLOBAL_LOCALE {
128 let base = base.cast_const().cast::<LocaleData>();
130 if let Ok(new_locale) = new_locale.as_mut()
131 && let Some(base) = unsafe { base.as_ref() }
132 {
133 if (mask & LC_NUMERIC_MASK) == 0 {
135 new_locale.copy_category(base, LC_NUMERIC);
136 }
137 if (mask & LC_MONETARY_MASK) == 0 {
138 new_locale.copy_category(base, LC_MONETARY);
139 }
140 }
142 }
143 new_locale.or_errno_null_mut().cast()
144}
145
146#[unsafe(no_mangle)]
148pub unsafe extern "C" fn freelocale(loc: locale_t) {
149 if !loc.is_null() && loc != LC_GLOBAL_LOCALE {
150 drop(unsafe { Box::from_raw(loc.cast::<LocaleData>()) });
151 }
152}
153
154#[unsafe(no_mangle)]
156pub unsafe extern "C" fn duplocale(loc: locale_t) -> locale_t {
157 if loc.is_null() {
158 loc
160 } else if loc == LC_GLOBAL_LOCALE {
161 Box::into_raw(LocaleData::posix()) as locale_t
162 } else {
163 let loc = loc.cast_const().cast::<LocaleData>();
165 Box::into_raw(unsafe { Box::from((*loc).clone()) }) as locale_t
166 }
167}
168
169pub(crate) fn load_locale_file(name: &str) -> Result<Box<LocaleData>, Errno> {
170 let mut path = String::from("/usr/share/i18n/locales/");
171 path.push_str(name);
172
173 let path_c = CString::new(path).map_err(|_| Errno(errno::EINVAL))?;
174 let mut content = String::new();
175
176 let mut file = File::open(path_c.as_c_str().into(), fcntl::O_RDONLY)?;
177 file.read_to_string(&mut content)
178 .map_err(|_| Errno(errno::EIO))?;
179
180 let toml = PosixLocaleDef::parse(&content);
181 Ok(LocaleData::new(CString::from_str(name).unwrap(), toml))
182}