use alloc::vec::Vec;
use core::{ffi::CStr, ptr};
pub use libc::{LC_ALL, LC_COLLATE, LC_CTYPE, LC_MONETARY, LC_NUMERIC, LC_TIME};
#[cfg(all(unix, not(any(target_os = "ios", target_os = "redox"))))]
pub use libc::LC_MESSAGES;
#[cfg(all(
unix,
not(any(target_os = "ios", target_os = "android", target_os = "redox"))
))]
pub use libc::{
ABDAY_1, ABDAY_2, ABDAY_3, ABDAY_4, ABDAY_5, ABDAY_6, ABDAY_7, ABMON_1, ABMON_2, ABMON_3,
ABMON_4, ABMON_5, ABMON_6, ABMON_7, ABMON_8, ABMON_9, ABMON_10, ABMON_11, ABMON_12, ALT_DIGITS,
AM_STR, CODESET, CRNCYSTR, D_FMT, D_T_FMT, DAY_1, DAY_2, DAY_3, DAY_4, DAY_5, DAY_6, DAY_7,
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,
MON_9, MON_10, MON_11, MON_12, NOEXPR, PM_STR, RADIXCHAR, T_FMT, T_FMT_AMPM, THOUSEP, YESEXPR,
};
#[cfg(windows)]
#[repr(C)]
struct RawLconv {
decimal_point: *mut libc::c_char,
thousands_sep: *mut libc::c_char,
grouping: *mut libc::c_char,
int_curr_symbol: *mut libc::c_char,
currency_symbol: *mut libc::c_char,
mon_decimal_point: *mut libc::c_char,
mon_thousands_sep: *mut libc::c_char,
mon_grouping: *mut libc::c_char,
positive_sign: *mut libc::c_char,
negative_sign: *mut libc::c_char,
int_frac_digits: libc::c_char,
frac_digits: libc::c_char,
p_cs_precedes: libc::c_char,
p_sep_by_space: libc::c_char,
n_cs_precedes: libc::c_char,
n_sep_by_space: libc::c_char,
p_sign_posn: libc::c_char,
n_sign_posn: libc::c_char,
}
#[cfg(windows)]
unsafe extern "C" {
fn localeconv() -> *mut RawLconv;
}
#[cfg(unix)]
use libc::localeconv;
#[derive(Debug, Clone)]
pub struct LocaleConv {
pub decimal_point: Vec<u8>,
pub thousands_sep: Vec<u8>,
pub grouping: Vec<libc::c_char>,
pub int_curr_symbol: Vec<u8>,
pub currency_symbol: Vec<u8>,
pub mon_decimal_point: Vec<u8>,
pub mon_thousands_sep: Vec<u8>,
pub mon_grouping: Vec<libc::c_char>,
pub positive_sign: Vec<u8>,
pub negative_sign: Vec<u8>,
pub int_frac_digits: libc::c_char,
pub frac_digits: libc::c_char,
pub p_cs_precedes: libc::c_char,
pub p_sep_by_space: libc::c_char,
pub n_cs_precedes: libc::c_char,
pub n_sep_by_space: libc::c_char,
pub p_sign_posn: libc::c_char,
pub n_sign_posn: libc::c_char,
}
fn copy_cstr(ptr: *const libc::c_char) -> Vec<u8> {
if ptr.is_null() {
Vec::new()
} else {
unsafe { CStr::from_ptr(ptr) }.to_bytes().to_vec()
}
}
fn copy_grouping(ptr: *const libc::c_char) -> Vec<libc::c_char> {
if ptr.is_null() {
return Vec::new();
}
let mut out = Vec::new();
let mut cur = ptr;
unsafe {
while ![0, libc::c_char::MAX].contains(&*cur) {
out.push(*cur);
cur = cur.add(1);
}
}
out
}
pub unsafe fn charp2bytes(ptr: *const libc::c_char) -> Vec<u8> {
let mut out = Vec::new();
if !ptr.is_null() {
let mut cur = ptr;
unsafe {
while *cur != 0 {
out.push(*cur as u8);
cur = cur.add(1);
}
}
}
out
}
pub fn localeconv_numeric() -> (Vec<u8>, Vec<u8>, Vec<u8>) {
let lc = unsafe { localeconv() };
if lc.is_null() {
return (b".".to_vec(), Vec::new(), Vec::new());
}
unsafe {
(
charp2bytes((*lc).decimal_point),
charp2bytes((*lc).thousands_sep),
charp2bytes((*lc).grouping),
)
}
}
pub fn localeconv_data() -> LocaleConv {
let lc = unsafe { localeconv() };
unsafe {
LocaleConv {
decimal_point: copy_cstr((*lc).decimal_point),
thousands_sep: copy_cstr((*lc).thousands_sep),
grouping: copy_grouping((*lc).grouping),
int_curr_symbol: copy_cstr((*lc).int_curr_symbol),
currency_symbol: copy_cstr((*lc).currency_symbol),
mon_decimal_point: copy_cstr((*lc).mon_decimal_point),
mon_thousands_sep: copy_cstr((*lc).mon_thousands_sep),
mon_grouping: copy_grouping((*lc).mon_grouping),
positive_sign: copy_cstr((*lc).positive_sign),
negative_sign: copy_cstr((*lc).negative_sign),
int_frac_digits: (*lc).int_frac_digits,
frac_digits: (*lc).frac_digits,
p_cs_precedes: (*lc).p_cs_precedes,
p_sep_by_space: (*lc).p_sep_by_space,
n_cs_precedes: (*lc).n_cs_precedes,
n_sep_by_space: (*lc).n_sep_by_space,
p_sign_posn: (*lc).p_sign_posn,
n_sign_posn: (*lc).n_sign_posn,
}
}
}
pub fn strcoll(string1: &CStr, string2: &CStr) -> libc::c_int {
unsafe { libc::strcoll(string1.as_ptr(), string2.as_ptr()) }
}
pub fn strxfrm(string: &CStr, _initial_len: usize) -> Vec<u8> {
let len = unsafe { libc::strxfrm(ptr::null_mut(), string.as_ptr(), 0) };
let mut buff = vec![0u8; len + 1];
unsafe {
libc::strxfrm(buff.as_mut_ptr() as _, string.as_ptr(), buff.len());
}
buff.truncate(len);
buff
}
pub fn setlocale(category: i32, locale: Option<&CStr>) -> Option<Vec<u8>> {
let result = unsafe {
match locale {
None => libc::setlocale(category, ptr::null()),
Some(locale) => libc::setlocale(category, locale.as_ptr()),
}
};
(!result.is_null()).then(|| unsafe { CStr::from_ptr(result) }.to_bytes().to_vec())
}
#[cfg(windows)]
pub fn acp() -> u32 {
unsafe { windows_sys::Win32::Globalization::GetACP() }
}
#[cfg(windows)]
pub fn user_default_lcid() -> u32 {
unsafe { windows_sys::Win32::Globalization::GetUserDefaultLCID() }
}
#[cfg(windows)]
pub const LOCALE_SISO639LANGNAME: u32 = 0x0000_0059;
#[cfg(windows)]
pub const LOCALE_SISO3166CTRYNAME: u32 = 0x0000_005A;
#[cfg(windows)]
pub fn locale_info(lcid: u32, lctype: u32) -> Option<String> {
use windows_sys::Win32::Globalization::GetLocaleInfoW;
let mut buffer = [0u16; 16];
let len = unsafe { GetLocaleInfoW(lcid, lctype, buffer.as_mut_ptr(), buffer.len() as i32) };
if len <= 1 {
None
} else {
Some(String::from_utf16_lossy(&buffer[..len as usize - 1]))
}
}
#[cfg(windows)]
fn wide_cstr(units: &[u16]) -> alloc::borrow::Cow<'_, [u16]> {
if units.last() == Some(&0) {
alloc::borrow::Cow::Borrowed(units)
} else {
let mut owned = units.to_vec();
owned.push(0);
alloc::borrow::Cow::Owned(owned)
}
}
#[cfg(windows)]
pub fn wcscoll(s1: &[u16], s2: &[u16]) -> i32 {
unsafe extern "C" {
fn wcscoll(s1: *const u16, s2: *const u16) -> i32;
}
let s1 = wide_cstr(s1);
let s2 = wide_cstr(s2);
unsafe { wcscoll(s1.as_ptr(), s2.as_ptr()) }
}
#[cfg(windows)]
pub fn wcsxfrm(src: &[u16]) -> Vec<u16> {
unsafe extern "C" {
fn wcsxfrm(dst: *mut u16, src: *const u16, count: usize) -> usize;
}
let src = wide_cstr(src);
let needed = unsafe { wcsxfrm(core::ptr::null_mut(), src.as_ptr(), 0) };
let mut dst = vec![0u16; needed + 1];
let written = unsafe { wcsxfrm(dst.as_mut_ptr(), src.as_ptr(), dst.len()) };
dst.truncate(written);
dst
}
#[cfg(windows)]
pub fn decode_ansi_bytes(bytes: &[u8]) -> Option<String> {
use core::ptr;
use windows_sys::Win32::Globalization::{CP_ACP, MultiByteToWideChar};
if bytes.is_empty() {
return Some(String::new());
}
let len_i32 = i32::try_from(bytes.len()).ok()?;
let len =
unsafe { MultiByteToWideChar(CP_ACP, 0, bytes.as_ptr(), len_i32, ptr::null_mut(), 0) };
if len <= 0 {
return None;
}
let mut wide = vec![0u16; len as usize];
unsafe {
MultiByteToWideChar(CP_ACP, 0, bytes.as_ptr(), len_i32, wide.as_mut_ptr(), len);
}
Some(String::from_utf16_lossy(&wide))
}
#[cfg(all(
unix,
not(any(target_os = "ios", target_os = "android", target_os = "redox"))
))]
pub fn nl_langinfo_codeset() -> Option<Vec<u8>> {
let codeset = unsafe { libc::nl_langinfo(libc::CODESET) };
(!codeset.is_null()).then(|| unsafe { CStr::from_ptr(codeset) }.to_bytes().to_vec())
}