diff --git a/src/header/locale/data.rs b/src/header/locale/data.rs index 645b05d3a4..aef2df910d 100644 --- a/src/header/locale/data.rs +++ b/src/header/locale/data.rs @@ -83,55 +83,55 @@ pub(crate) struct LocaleData { // Owned memory buffers pub decimal_point: CString, pub thousands_sep: CString, - pub grouping: Vec, + pub grouping: Vec, pub int_curr_symbol: CString, pub currency_symbol: CString, pub mon_decimal_point: CString, pub mon_thousands_sep: CString, - pub mon_grouping: Vec, + pub mon_grouping: Vec, pub positive_sign: CString, pub negative_sign: CString, } unsafe impl Sync for LocaleData {} impl LocaleData { - pub fn new(name: CString, toml: RawLocale) -> Box { + pub fn new(name: CString, defs: PosixLocaleDef) -> Box { let mut data = Box::new(LocaleData { name, - decimal_point: Self::to_cstring(toml.decimal_point), - thousands_sep: Self::to_cstring(toml.thousands_sep), - grouping: Self::to_grouping(toml.grouping), - int_curr_symbol: Self::to_cstring(toml.int_curr_symbol), - currency_symbol: Self::to_cstring(toml.currency_symbol), - mon_decimal_point: Self::to_cstring(toml.mon_decimal_point), - mon_thousands_sep: Self::to_cstring(toml.mon_thousands_sep), - mon_grouping: Self::to_grouping(toml.mon_grouping), - positive_sign: Self::to_cstring(toml.positive_sign), - negative_sign: Self::to_cstring(toml.negative_sign), + decimal_point: Self::to_cstring(defs.decimal_point), + thousands_sep: Self::to_cstring(defs.thousands_sep), + grouping: Self::to_grouping_char(defs.grouping), + int_curr_symbol: Self::to_cstring(defs.int_curr_symbol), + currency_symbol: Self::to_cstring(defs.currency_symbol), + mon_decimal_point: Self::to_cstring(defs.mon_decimal_point), + mon_thousands_sep: Self::to_cstring(defs.mon_thousands_sep), + mon_grouping: Self::to_grouping_char(defs.mon_grouping), + positive_sign: Self::to_cstring(defs.positive_sign), + negative_sign: Self::to_cstring(defs.negative_sign), lconv: unsafe { core::mem::zeroed() }, }); - data.lconv.int_frac_digits = toml.int_frac_digits.unwrap_or(c_char::MAX); - data.lconv.frac_digits = toml.frac_digits.unwrap_or(c_char::MAX); - data.lconv.p_cs_precedes = toml.p_cs_precedes.unwrap_or(c_char::MAX); - data.lconv.p_sep_by_space = toml.p_sep_by_space.unwrap_or(c_char::MAX); - data.lconv.n_cs_precedes = toml.n_cs_precedes.unwrap_or(c_char::MAX); - data.lconv.n_sep_by_space = toml.n_sep_by_space.unwrap_or(c_char::MAX); - data.lconv.p_sign_posn = toml.p_sign_posn.unwrap_or(c_char::MAX); - data.lconv.n_sign_posn = toml.n_sign_posn.unwrap_or(c_char::MAX); - data.lconv.int_p_cs_precedes = toml.int_p_cs_precedes.unwrap_or(c_char::MAX); - data.lconv.int_p_sep_by_space = toml.int_p_sep_by_space.unwrap_or(c_char::MAX); - data.lconv.int_n_cs_precedes = toml.int_n_cs_precedes.unwrap_or(c_char::MAX); - data.lconv.int_n_sep_by_space = toml.int_n_sep_by_space.unwrap_or(c_char::MAX); - data.lconv.int_p_sign_posn = toml.int_p_sign_posn.unwrap_or(c_char::MAX); - data.lconv.int_n_sign_posn = toml.int_n_sign_posn.unwrap_or(c_char::MAX); + data.lconv.int_frac_digits = defs.int_frac_digits.unwrap_or(c_char::MAX); + data.lconv.frac_digits = defs.frac_digits.unwrap_or(c_char::MAX); + data.lconv.p_cs_precedes = defs.p_cs_precedes.unwrap_or(c_char::MAX); + data.lconv.p_sep_by_space = defs.p_sep_by_space.unwrap_or(c_char::MAX); + data.lconv.n_cs_precedes = defs.n_cs_precedes.unwrap_or(c_char::MAX); + data.lconv.n_sep_by_space = defs.n_sep_by_space.unwrap_or(c_char::MAX); + data.lconv.p_sign_posn = defs.p_sign_posn.unwrap_or(c_char::MAX); + data.lconv.n_sign_posn = defs.n_sign_posn.unwrap_or(c_char::MAX); + data.lconv.int_p_cs_precedes = defs.int_p_cs_precedes.unwrap_or(c_char::MAX); + data.lconv.int_p_sep_by_space = defs.int_p_sep_by_space.unwrap_or(c_char::MAX); + data.lconv.int_n_cs_precedes = defs.int_n_cs_precedes.unwrap_or(c_char::MAX); + data.lconv.int_n_sep_by_space = defs.int_n_sep_by_space.unwrap_or(c_char::MAX); + data.lconv.int_p_sign_posn = defs.int_p_sign_posn.unwrap_or(c_char::MAX); + data.lconv.int_n_sign_posn = defs.int_n_sign_posn.unwrap_or(c_char::MAX); data.update_lconv_pointers(); data } pub fn posix() -> Box { - LocaleData::new(CString::from_str("C").unwrap(), RawLocale::default()) + LocaleData::new(CString::from_str("C").unwrap(), PosixLocaleDef::default()) } fn update_lconv_pointers(&mut self) { @@ -186,20 +186,19 @@ impl LocaleData { self.update_lconv_pointers(); } - fn to_cstring(opt: Option) -> CString { - opt.and_then(|s| CString::new(s).ok()) - .unwrap_or_else(|| CString::new("").unwrap()) + fn to_cstring(opt: Option) -> CString { + opt.unwrap_or_else(|| CString::new("").unwrap()) } - fn to_grouping(opt: Option>) -> Vec { - let mut v: Vec = opt - .unwrap_or_default() - .into_iter() - .map(|x| x as u8) - .collect(); + fn to_grouping_char(opt: Vec>) -> Vec { + let mut v: Vec = opt.into_iter().map(Self::to_char).collect(); v.push(0); v } + + fn to_char(opt: Option) -> c_char { + opt.unwrap_or(c_char::MAX) + } } impl Clone for LocaleData { @@ -262,17 +261,17 @@ impl GlobalLocaleData { unsafe impl Sync for GlobalLocaleData {} #[derive(Default)] -pub(crate) struct RawLocale { - pub decimal_point: Option, - pub thousands_sep: Option, - pub grouping: Option>, - pub int_curr_symbol: Option, - pub currency_symbol: Option, - pub mon_decimal_point: Option, - pub mon_thousands_sep: Option, - pub mon_grouping: Option>, - pub positive_sign: Option, - pub negative_sign: Option, +pub(crate) struct PosixLocaleDef { + pub decimal_point: Option, + pub thousands_sep: Option, + pub grouping: Vec>, + pub int_curr_symbol: Option, + pub currency_symbol: Option, + pub mon_decimal_point: Option, + pub mon_thousands_sep: Option, + pub mon_grouping: Vec>, + pub positive_sign: Option, + pub negative_sign: Option, pub int_frac_digits: Option, pub frac_digits: Option, pub p_cs_precedes: Option, @@ -288,59 +287,109 @@ pub(crate) struct RawLocale { pub int_p_sign_posn: Option, pub int_n_sign_posn: Option, } -impl RawLocale { +impl PosixLocaleDef { + //! See pub fn parse(content: &str) -> Self { - let mut locale = RawLocale::default(); + let mut locale = PosixLocaleDef::default(); + + let mut lines = content.lines(); + loop { + let Some(line) = lines.next() else { + break; + }; - for line in content.lines() { let trimmed = line.trim(); if trimmed.is_empty() || trimmed.starts_with('#') { continue; } - let mut parts = trimmed.splitn(2, '='); - let key = parts.next().unwrap_or("").trim(); - let val = parts.next().unwrap_or("").trim(); + let mut parts = trimmed.split_ascii_whitespace(); + let Some(key) = parts.next() else { + continue; + }; + let mut val: String = String::new(); + loop { + let Some(chunk) = parts.next() else { + break; + }; + if !chunk.ends_with('\\') { + val.push_str(chunk); + break; + } + // multiline values + val.push_str(&chunk[0..chunk.len() - 1]); + let Some(next_line) = lines.next() else { + break; + }; + parts = next_line.split_ascii_whitespace(); + } if key.is_empty() || val.is_empty() { continue; } match key { - "decimal_point" => locale.decimal_point = Some(val.to_string()), - "thousands_sep" => locale.thousands_sep = Some(val.to_string()), - "int_curr_symbol" => locale.int_curr_symbol = Some(val.to_string()), - "currency_symbol" => locale.currency_symbol = Some(val.to_string()), - "mon_decimal_point" => locale.mon_decimal_point = Some(val.to_string()), - "mon_thousands_sep" => locale.mon_thousands_sep = Some(val.to_string()), - "positive_sign" => locale.positive_sign = Some(val.to_string()), - "negative_sign" => locale.negative_sign = Some(val.to_string()), - "grouping" => locale.grouping = Some(Self::parse_grouping(val)), - "mon_grouping" => locale.mon_grouping = Some(Self::parse_grouping(val)), - "int_frac_digits" => locale.int_frac_digits = val.parse().ok(), - "frac_digits" => locale.frac_digits = val.parse().ok(), - "p_cs_precedes" => locale.p_cs_precedes = val.parse().ok(), - "p_sep_by_space" => locale.p_sep_by_space = val.parse().ok(), - "n_cs_precedes" => locale.n_cs_precedes = val.parse().ok(), - "n_sep_by_space" => locale.n_sep_by_space = val.parse().ok(), - "p_sign_posn" => locale.p_sign_posn = val.parse().ok(), - "n_sign_posn" => locale.n_sign_posn = val.parse().ok(), - "int_p_cs_precedes" => locale.int_p_cs_precedes = val.parse().ok(), - "int_p_sep_by_space" => locale.int_p_sep_by_space = val.parse().ok(), - "int_n_cs_precedes" => locale.int_n_cs_precedes = val.parse().ok(), - "int_n_sep_by_space" => locale.int_n_sep_by_space = val.parse().ok(), - "int_p_sign_posn" => locale.int_p_sign_posn = val.parse().ok(), - "int_n_sign_posn" => locale.int_n_sign_posn = val.parse().ok(), + "decimal_point" => locale.decimal_point = Self::parse_str(&val), + "thousands_sep" => locale.thousands_sep = Self::parse_str(&val), + "int_curr_symbol" => locale.int_curr_symbol = Self::parse_str(&val), + "currency_symbol" => locale.currency_symbol = Self::parse_str(&val), + "mon_decimal_point" => locale.mon_decimal_point = Self::parse_str(&val), + "mon_thousands_sep" => locale.mon_thousands_sep = Self::parse_str(&val), + "positive_sign" => locale.positive_sign = Self::parse_str(&val), + "negative_sign" => locale.negative_sign = Self::parse_str(&val), + "grouping" => locale.grouping = Self::parse_int_group(&val), + "mon_grouping" => locale.mon_grouping = Self::parse_int_group(&val), + "int_frac_digits" => locale.int_frac_digits = Self::parse_int(&val), + "frac_digits" => locale.frac_digits = Self::parse_int(&val), + "p_cs_precedes" => locale.p_cs_precedes = Self::parse_int(&val), + "p_sep_by_space" => locale.p_sep_by_space = Self::parse_int(&val), + "n_cs_precedes" => locale.n_cs_precedes = Self::parse_int(&val), + "n_sep_by_space" => locale.n_sep_by_space = Self::parse_int(&val), + "p_sign_posn" => locale.p_sign_posn = Self::parse_int(&val), + "n_sign_posn" => locale.n_sign_posn = Self::parse_int(&val), + "int_p_cs_precedes" => locale.int_p_cs_precedes = Self::parse_int(&val), + "int_p_sep_by_space" => locale.int_p_sep_by_space = Self::parse_int(&val), + "int_n_cs_precedes" => locale.int_n_cs_precedes = Self::parse_int(&val), + "int_n_sep_by_space" => locale.int_n_sep_by_space = Self::parse_int(&val), + "int_p_sign_posn" => locale.int_p_sign_posn = Self::parse_int(&val), + "int_n_sign_posn" => locale.int_n_sign_posn = Self::parse_int(&val), _ => {} } } locale } - /// parse e.g. "3,3,0" - fn parse_grouping(val: &str) -> Vec { - val.split(',') - .filter_map(|s| s.trim().parse::().ok()) - .collect() + /// parse e.g. `3;3;0` -> [ 3,3,0 ], `-1` -> [ None ] + fn parse_int_group(val: &str) -> Vec> { + val.split(';').map(Self::parse_int).collect() + } + + /// parse e.g. `-1` -> None, `1` -> Some(1) + fn parse_int(val: &str) -> Option { + let r = val.trim().parse::().ok(); + if r.is_some_and(|i| i < 0) { + return None; + } + r + } + + /// parse e.g. `""` + fn parse_str(val: &str) -> Option { + let mut r = String::new(); + let mut v = val.chars(); + if v.next() != Some('"') { + return None; + } + while let Some(c) = v.next() { + if c == '"' { + if v.next().is_some() { + return None; + } + break; + } + // TODO: Parse <..> + r.push(c); + } + CString::new(r).ok() } } diff --git a/src/header/locale/mod.rs b/src/header/locale/mod.rs index da839d7e32..f03ac3560f 100644 --- a/src/header/locale/mod.rs +++ b/src/header/locale/mod.rs @@ -1,7 +1,6 @@ //! `locale.h` implementation. //! //! See . -//! See use alloc::{boxed::Box, ffi::CString, string::String}; use core::{ptr, str::FromStr}; @@ -10,7 +9,7 @@ use crate::{ c_str::CStr, error::{Errno, ResultExtPtrMut}, fs::File, - header::errno, + header::{errno, fcntl}, io::Read, platform::types::{c_char, c_int, c_void}, sync::Once, @@ -24,9 +23,6 @@ mod constants; use constants::*; mod data; use data::*; -// TODO: No parsing implementation that's compatible with glibc -mod redox; -use redox::*; pub type locale_t = *mut c_void; /// constant struct to "C" or "POSIX" locale @@ -158,3 +154,18 @@ pub unsafe extern "C" fn duplocale(loc: locale_t) -> locale_t { Box::into_raw(Box::from((*loc).clone())) as locale_t } } + +pub fn load_locale_file(name: &str) -> Result, Errno> { + let mut path = String::from("/usr/share/i18n/locales/"); + path.push_str(name); + + let path_c = CString::new(path).map_err(|_| Errno(errno::EINVAL))?; + let mut content = String::new(); + + let mut file = File::open(path_c.as_c_str().into(), fcntl::O_RDONLY)?; + file.read_to_string(&mut content) + .map_err(|_| Errno(errno::EIO))?; + + let toml = PosixLocaleDef::parse(&content); + Ok(LocaleData::new(CString::from_str(name).unwrap(), toml)) +} diff --git a/src/header/locale/redox.rs b/src/header/locale/redox.rs deleted file mode 100644 index 88abaf3030..0000000000 --- a/src/header/locale/redox.rs +++ /dev/null @@ -1,32 +0,0 @@ -use core::str::FromStr; - -use alloc::{boxed::Box, ffi::CString, string::String}; - -use crate::{ - c_str::CStr, - error::Errno, - fs::File, - header::{ - errno, fcntl, - locale::{ - data::{LocaleData, RawLocale}, - locale_t, - }, - }, - io::Read, -}; - -pub fn load_locale_file(name: &str) -> Result, Errno> { - let mut path = String::from("/etc/locale.d/"); - path.push_str(name); - - let path_c = CString::new(path).map_err(|_| Errno(errno::EINVAL))?; - let mut content = String::new(); - - let mut file = File::open(path_c.as_c_str().into(), fcntl::O_RDONLY)?; - file.read_to_string(&mut content) - .map_err(|_| Errno(errno::EIO))?; - - let toml = RawLocale::parse(&content); - Ok(LocaleData::new(CString::from_str(name).unwrap(), toml)) -} diff --git a/tests/locale/newlocale.c b/tests/locale/newlocale.c index 0dbb70b5f1..26011386f4 100644 --- a/tests/locale/newlocale.c +++ b/tests/locale/newlocale.c @@ -10,7 +10,11 @@ int main() { locale_t locale1 = newlocale(LC_ALL_MASK, "non-existent-locale", (locale_t)0); assert(locale1 == (locale_t)0); - // TODO: test with existing custom locale + // TODO: locale files inside redoxer? +#ifndef __redox__ + locale_t locale2 = newlocale(LC_ALL_MASK, "en_US", (locale_t)0); + assert(locale2 != (locale_t)0); +#endif return 0; }