Add way to query the system locales

This commit is contained in:
Sasha Koshka 2024-10-04 23:12:58 -04:00
parent 27b7252580
commit 409aab92ff
3 changed files with 164 additions and 5 deletions

96
locale/+linux.ha Normal file
View File

@ -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;
};
};
};

40
locale/-linux.ha Normal file
View File

@ -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;

View File

@ -10,13 +10,20 @@ export type locale = struct {
modifier: str, 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: // Parses a [[locale]] name of the form:
// //
// lang_COUNTRY.ENCODING@MODIFIER // lang_COUNTRY.ENCODING@MODIFIER
// //
// Where _COUNTRY, .ENCODING, and @MODIFIER may be omitted. The function // Where _COUNTRY, .ENCODING, and @MODIFIER may be omitted. The function
// returns a [[locale]], or [[errors::invalid]] if the input cannot be parsed. // 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) = { export fn parse_locale(in: str) (locale | errors::invalid) = {
let (in, modifier) = strings::rcut(in, "@"); let (in, modifier) = strings::rcut(in, "@");
if (strings::compare(in, "") == 0) return void: errors::invalid; 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. // Checks if two [[locale]]s are equal.
export fn locale_equal(a: locale, b: locale) bool = export fn locale_equal(a: locale, b: locale) bool =
strings::compare(a.lang, b.lang ) == 0 && a.lang == b.lang &&
strings::compare(a.country, b.country ) == 0 && a.country == b.country &&
strings::compare(a.encoding, b.encoding) == 0 && a.encoding == b.encoding &&
strings::compare(a.modifier, b.modifier) == 0; 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);
};