diff --git a/locale/+linux.ha b/locale/+linux.ha new file mode 100644 index 0000000..0a87069 --- /dev/null +++ b/locale/+linux.ha @@ -0,0 +1,96 @@ +use bufio; +use errors; +use io; +use os; +use strings; + +def locale_conf_path = "/etc/locale.conf"; + +// Returns the locale to use for character classification and case conversion. +// The memory is statically allocated and must not be free'd. It may be +// overwritten later, so use [[locale_dup]] to extend its lifetime. +export fn get_ctype() locale = get_locale("LC_CTYPE"); + +// Returns the locale to use for collation order. +// The memory is statically allocated and must not be free'd. It may be +// overwritten later, so use [[locale_dup]] to extend its lifetime. +export fn get_collate() locale = get_locale("LC_COLLATE"); + +// Returns the locale to use for monetary formatting. +// The memory is statically allocated and must not be free'd. It may be +// overwritten later, so use [[locale_dup]] to extend its lifetime. +export fn get_monetary() locale = get_locale("LC_MONETARY"); + +// Returns the locale to use for numeric, non-monetary formatting. +// The memory is statically allocated and must not be free'd. It may be +// overwritten later, so use [[locale_dup]] to extend its lifetime. +export fn get_numeric() locale = get_locale("LC_NUMERIC"); + +// Returns the locale to use for date and time formats. +// The memory is statically allocated and must not be free'd. It may be +// overwritten later, so use [[locale_dup]] to extend its lifetime. +export fn get_time() locale = get_locale("LC_TIME"); + +// Returns the locale to use for formats of informative and diagnostic messages +// and interactive responses. +// The memory is statically allocated and must not be free'd. It may be +// overwritten later, so use [[locale_dup]] to extend its lifetime. +export fn get_messages() locale = get_locale("LC_MESSAGES"); + +// Returns the locale to use for linguistic translations. +// The memory is statically allocated and must not be free'd. It may be +// overwritten later, so use [[locale_dup]] to extend its lifetime. +export fn get_lang() locale = get_locale("LC_LANG"); + +// TODO +fn get_language() []locale; + +fn get_locale(var: str) locale = + match (get_env_locale(var)) { + case let local: locale => yield local; + case => + yield match (get_locale_conf_entry(var)) { + case let local: locale => yield local; + case => + yield c; + }; + }; + +fn get_env_locale(var: str) (locale | errors::invalid) = + match (os::getenv(var)) { + case let env: str => return parse_locale(env); + case => return errors::invalid; + }; + +let locale_conf: []str = []; + +@fini fn locale_conf() void = strings::freeall(locale_conf); + +fn get_locale_conf_entry(var: str) (locale | errors::invalid) = { + get_locale_conf(); + for (let entry .. locale_conf) { + let (key, value) = strings::cut(entry, "="); + if (key == var) return parse_locale(value); + }; + return errors::invalid; +}; + +fn get_locale_conf() []str = { + if (len(locale_conf) != 0) return locale_conf; + + let file = match (os::open(locale_conf_path)) { + case let file: io::file => yield file; + case => return locale_conf; + }; + defer io::close(file)!; // when the hell does closing a file fail???? + + let scanner = bufio::newscanner(file); + defer bufio::finish(&scanner); + + for (true) { + match (bufio::scan_line(&scanner)) { + case let line: const str => append(locale_conf, line); + case => return locale_conf; + }; + }; +}; diff --git a/locale/-linux.ha b/locale/-linux.ha new file mode 100644 index 0000000..b9eec62 --- /dev/null +++ b/locale/-linux.ha @@ -0,0 +1,40 @@ +use errors; + +// Returns the locale to use for character classification and case conversion. +// The memory is statically allocated and must not be free'd. It may be +// overwritten later, so use [[locale_dup]] to extend its lifetime. +export fn get_ctype() locale = c; + +// Returns the locale to use for collation order. +// The memory is statically allocated and must not be free'd. It may be +// overwritten later, so use [[locale_dup]] to extend its lifetime. +export fn get_collate() locale = c; + +// Returns the locale to use for monetary formatting. +// The memory is statically allocated and must not be free'd. It may be +// overwritten later, so use [[locale_dup]] to extend its lifetime. +export fn get_monetary() locale = c; + +// Returns the locale to use for numeric, non-monetary formatting. +// The memory is statically allocated and must not be free'd. It may be +// overwritten later, so use [[locale_dup]] to extend its lifetime. +export fn get_numeric() locale = c; + +// Returns the locale to use for date and time formats. +// The memory is statically allocated and must not be free'd. It may be +// overwritten later, so use [[locale_dup]] to extend its lifetime. +export fn get_time() locale = c; + +// Returns the locale to use for formats of informative and diagnostic messages +// and interactive responses. +// The memory is statically allocated and must not be free'd. It may be +// overwritten later, so use [[locale_dup]] to extend its lifetime. +export fn get_messages() locale = c; + +// Returns the locale to use for linguistic translations. +// The memory is statically allocated and must not be free'd. It may be +// overwritten later, so use [[locale_dup]] to extend its lifetime. +export fn get_lang() locale = c; + +// TODO +export fn get_language() []locale; diff --git a/locale/locale.ha b/locale/locale.ha index 931104e..5ae02e2 100644 --- a/locale/locale.ha +++ b/locale/locale.ha @@ -10,13 +10,20 @@ export type locale = struct { modifier: str, }; +// The default locale. Stands for computer, the C language, etc. +export def c = locale { + lang = "C", + ... +}; + // Parses a [[locale]] name of the form: // // lang_COUNTRY.ENCODING@MODIFIER // // Where _COUNTRY, .ENCODING, and @MODIFIER may be omitted. The function // returns a [[locale]], or [[errors::invalid]] if the input cannot be parsed. -// All memory is borrowed from the input. +// All memory is borrowed from the input, so [[locale_finish]] should not be +// used to free it. export fn parse_locale(in: str) (locale | errors::invalid) = { let (in, modifier) = strings::rcut(in, "@"); if (strings::compare(in, "") == 0) return void: errors::invalid; @@ -55,7 +62,23 @@ export fn format_locale(local: locale) str = { // Checks if two [[locale]]s are equal. export fn locale_equal(a: locale, b: locale) bool = - strings::compare(a.lang, b.lang ) == 0 && - strings::compare(a.country, b.country ) == 0 && - strings::compare(a.encoding, b.encoding) == 0 && - strings::compare(a.modifier, b.modifier) == 0; + a.lang == b.lang && + a.country == b.country && + a.encoding == b.encoding && + a.modifier == b.modifier; + +// Duplicates a [[locale]] structure. Use [[locale_finish]] to get rid of it. +export fn locale_dup(local: locale) locale = locale { + lang = strings::dup(local.lang), + country = strings::dup(local.country), + encoding = strings::dup(local.encoding), + modifier = strings::dup(local.modifier), +}; + +// Frees memory associated with a [[locale]] structure. +export fn locale_finish(local: locale) void = { + free(local.lang); + free(local.country); + free(local.encoding); + free(local.modifier); +};