1use alloc::vec::Vec;
2use core::{ffi::CStr, ptr};
3
4pub use libc::{LC_ALL, LC_COLLATE, LC_CTYPE, LC_MONETARY, LC_NUMERIC, LC_TIME};
5
6#[cfg(all(unix, not(any(target_os = "ios", target_os = "redox"))))]
7pub use libc::LC_MESSAGES;
8
9#[cfg(all(
10 unix,
11 not(any(target_os = "ios", target_os = "android", target_os = "redox"))
12))]
13pub use libc::{
14 ABDAY_1, ABDAY_2, ABDAY_3, ABDAY_4, ABDAY_5, ABDAY_6, ABDAY_7, ABMON_1, ABMON_2, ABMON_3,
15 ABMON_4, ABMON_5, ABMON_6, ABMON_7, ABMON_8, ABMON_9, ABMON_10, ABMON_11, ABMON_12, ALT_DIGITS,
16 AM_STR, CODESET, CRNCYSTR, D_FMT, D_T_FMT, DAY_1, DAY_2, DAY_3, DAY_4, DAY_5, DAY_6, DAY_7,
17 ERA, ERA_D_FMT, ERA_D_T_FMT, ERA_T_FMT, MON_1, MON_2, MON_3, MON_4, MON_5, MON_6, MON_7, MON_8,
18 MON_9, MON_10, MON_11, MON_12, NOEXPR, PM_STR, RADIXCHAR, T_FMT, T_FMT_AMPM, THOUSEP, YESEXPR,
19};
20
21#[cfg(windows)]
22#[repr(C)]
23struct RawLconv {
24 decimal_point: *mut libc::c_char,
25 thousands_sep: *mut libc::c_char,
26 grouping: *mut libc::c_char,
27 int_curr_symbol: *mut libc::c_char,
28 currency_symbol: *mut libc::c_char,
29 mon_decimal_point: *mut libc::c_char,
30 mon_thousands_sep: *mut libc::c_char,
31 mon_grouping: *mut libc::c_char,
32 positive_sign: *mut libc::c_char,
33 negative_sign: *mut libc::c_char,
34 int_frac_digits: libc::c_char,
35 frac_digits: libc::c_char,
36 p_cs_precedes: libc::c_char,
37 p_sep_by_space: libc::c_char,
38 n_cs_precedes: libc::c_char,
39 n_sep_by_space: libc::c_char,
40 p_sign_posn: libc::c_char,
41 n_sign_posn: libc::c_char,
42}
43
44#[cfg(windows)]
45unsafe extern "C" {
46 fn localeconv() -> *mut RawLconv;
47}
48
49#[cfg(unix)]
50use libc::localeconv;
51
52#[derive(Debug, Clone)]
53pub struct LocaleConv {
54 pub decimal_point: Vec<u8>,
55 pub thousands_sep: Vec<u8>,
56 pub grouping: Vec<libc::c_char>,
57 pub int_curr_symbol: Vec<u8>,
58 pub currency_symbol: Vec<u8>,
59 pub mon_decimal_point: Vec<u8>,
60 pub mon_thousands_sep: Vec<u8>,
61 pub mon_grouping: Vec<libc::c_char>,
62 pub positive_sign: Vec<u8>,
63 pub negative_sign: Vec<u8>,
64 pub int_frac_digits: libc::c_char,
65 pub frac_digits: libc::c_char,
66 pub p_cs_precedes: libc::c_char,
67 pub p_sep_by_space: libc::c_char,
68 pub n_cs_precedes: libc::c_char,
69 pub n_sep_by_space: libc::c_char,
70 pub p_sign_posn: libc::c_char,
71 pub n_sign_posn: libc::c_char,
72}
73
74fn copy_cstr(ptr: *const libc::c_char) -> Vec<u8> {
75 if ptr.is_null() {
76 Vec::new()
77 } else {
78 unsafe { CStr::from_ptr(ptr) }.to_bytes().to_vec()
79 }
80}
81
82fn copy_grouping(ptr: *const libc::c_char) -> Vec<libc::c_char> {
83 if ptr.is_null() {
84 return Vec::new();
85 }
86 let mut out = Vec::new();
87 let mut cur = ptr;
88 unsafe {
89 while ![0, libc::c_char::MAX].contains(&*cur) {
90 out.push(*cur);
91 cur = cur.add(1);
92 }
93 }
94 out
95}
96
97pub unsafe fn charp2bytes(ptr: *const libc::c_char) -> Vec<u8> {
108 let mut out = Vec::new();
109 if !ptr.is_null() {
110 let mut cur = ptr;
111 unsafe {
112 while *cur != 0 {
113 out.push(*cur as u8);
114 cur = cur.add(1);
115 }
116 }
117 }
118 out
119}
120
121pub fn localeconv_numeric() -> (Vec<u8>, Vec<u8>, Vec<u8>) {
125 let lc = unsafe { localeconv() };
126 if lc.is_null() {
127 return (b".".to_vec(), Vec::new(), Vec::new());
128 }
129 unsafe {
130 (
131 charp2bytes((*lc).decimal_point),
132 charp2bytes((*lc).thousands_sep),
133 charp2bytes((*lc).grouping),
134 )
135 }
136}
137
138pub fn localeconv_data() -> LocaleConv {
139 let lc = unsafe { localeconv() };
140 unsafe {
141 LocaleConv {
142 decimal_point: copy_cstr((*lc).decimal_point),
143 thousands_sep: copy_cstr((*lc).thousands_sep),
144 grouping: copy_grouping((*lc).grouping),
145 int_curr_symbol: copy_cstr((*lc).int_curr_symbol),
146 currency_symbol: copy_cstr((*lc).currency_symbol),
147 mon_decimal_point: copy_cstr((*lc).mon_decimal_point),
148 mon_thousands_sep: copy_cstr((*lc).mon_thousands_sep),
149 mon_grouping: copy_grouping((*lc).mon_grouping),
150 positive_sign: copy_cstr((*lc).positive_sign),
151 negative_sign: copy_cstr((*lc).negative_sign),
152 int_frac_digits: (*lc).int_frac_digits,
153 frac_digits: (*lc).frac_digits,
154 p_cs_precedes: (*lc).p_cs_precedes,
155 p_sep_by_space: (*lc).p_sep_by_space,
156 n_cs_precedes: (*lc).n_cs_precedes,
157 n_sep_by_space: (*lc).n_sep_by_space,
158 p_sign_posn: (*lc).p_sign_posn,
159 n_sign_posn: (*lc).n_sign_posn,
160 }
161 }
162}
163
164pub fn strcoll(string1: &CStr, string2: &CStr) -> libc::c_int {
165 unsafe { libc::strcoll(string1.as_ptr(), string2.as_ptr()) }
166}
167
168pub fn strxfrm(string: &CStr, _initial_len: usize) -> Vec<u8> {
169 let len = unsafe { libc::strxfrm(ptr::null_mut(), string.as_ptr(), 0) };
170 let mut buff = vec![0u8; len + 1];
171 unsafe {
172 libc::strxfrm(buff.as_mut_ptr() as _, string.as_ptr(), buff.len());
173 }
174 buff.truncate(len);
175 buff
176}
177
178pub fn setlocale(category: i32, locale: Option<&CStr>) -> Option<Vec<u8>> {
179 let result = unsafe {
180 match locale {
181 None => libc::setlocale(category, ptr::null()),
182 Some(locale) => libc::setlocale(category, locale.as_ptr()),
183 }
184 };
185 (!result.is_null()).then(|| unsafe { CStr::from_ptr(result) }.to_bytes().to_vec())
186}
187
188#[cfg(windows)]
189pub fn acp() -> u32 {
190 unsafe { windows_sys::Win32::Globalization::GetACP() }
191}
192
193#[cfg(windows)]
194pub fn user_default_lcid() -> u32 {
195 unsafe { windows_sys::Win32::Globalization::GetUserDefaultLCID() }
196}
197
198#[cfg(windows)]
199pub const LOCALE_SISO639LANGNAME: u32 = 0x0000_0059;
200#[cfg(windows)]
201pub const LOCALE_SISO3166CTRYNAME: u32 = 0x0000_005A;
202
203#[cfg(windows)]
205pub fn locale_info(lcid: u32, lctype: u32) -> Option<String> {
206 use windows_sys::Win32::Globalization::GetLocaleInfoW;
207
208 let mut buffer = [0u16; 16];
209 let len = unsafe { GetLocaleInfoW(lcid, lctype, buffer.as_mut_ptr(), buffer.len() as i32) };
210 if len <= 1 {
211 None
212 } else {
213 Some(String::from_utf16_lossy(&buffer[..len as usize - 1]))
214 }
215}
216
217#[cfg(windows)]
218fn wide_cstr(units: &[u16]) -> alloc::borrow::Cow<'_, [u16]> {
219 if units.last() == Some(&0) {
220 alloc::borrow::Cow::Borrowed(units)
221 } else {
222 let mut owned = units.to_vec();
223 owned.push(0);
224 alloc::borrow::Cow::Owned(owned)
225 }
226}
227
228#[cfg(windows)]
229pub fn wcscoll(s1: &[u16], s2: &[u16]) -> i32 {
230 unsafe extern "C" {
231 fn wcscoll(s1: *const u16, s2: *const u16) -> i32;
232 }
233 let s1 = wide_cstr(s1);
234 let s2 = wide_cstr(s2);
235 unsafe { wcscoll(s1.as_ptr(), s2.as_ptr()) }
236}
237
238#[cfg(windows)]
239pub fn wcsxfrm(src: &[u16]) -> Vec<u16> {
240 unsafe extern "C" {
241 fn wcsxfrm(dst: *mut u16, src: *const u16, count: usize) -> usize;
242 }
243 let src = wide_cstr(src);
244 let needed = unsafe { wcsxfrm(core::ptr::null_mut(), src.as_ptr(), 0) };
245 let mut dst = vec![0u16; needed + 1];
246 let written = unsafe { wcsxfrm(dst.as_mut_ptr(), src.as_ptr(), dst.len()) };
247 dst.truncate(written);
248 dst
249}
250
251#[cfg(windows)]
252pub fn decode_ansi_bytes(bytes: &[u8]) -> Option<String> {
253 use core::ptr;
254 use windows_sys::Win32::Globalization::{CP_ACP, MultiByteToWideChar};
255
256 if bytes.is_empty() {
257 return Some(String::new());
258 }
259 let len_i32 = i32::try_from(bytes.len()).ok()?;
260
261 let len =
262 unsafe { MultiByteToWideChar(CP_ACP, 0, bytes.as_ptr(), len_i32, ptr::null_mut(), 0) };
263 if len <= 0 {
264 return None;
265 }
266 let mut wide = vec![0u16; len as usize];
267 unsafe {
268 MultiByteToWideChar(CP_ACP, 0, bytes.as_ptr(), len_i32, wide.as_mut_ptr(), len);
269 }
270 Some(String::from_utf16_lossy(&wide))
271}
272
273#[cfg(all(
274 unix,
275 not(any(target_os = "ios", target_os = "android", target_os = "redox"))
276))]
277pub fn nl_langinfo_codeset() -> Option<Vec<u8>> {
278 let codeset = unsafe { libc::nl_langinfo(libc::CODESET) };
279 (!codeset.is_null()).then(|| unsafe { CStr::from_ptr(codeset) }.to_bytes().to_vec())
280}