2024-10-04 22:40:43 -06:00
|
|
|
use locale;
|
|
|
|
use strings;
|
|
|
|
|
2024-10-03 17:52:17 -06:00
|
|
|
// Represents a file of the generic key/value format used by desktop entries.
|
|
|
|
// Specification: §3
|
2024-10-04 22:40:43 -06:00
|
|
|
export type file = struct {
|
|
|
|
preceeding_lines: []line,
|
|
|
|
groups: []group,
|
2024-10-03 17:52:17 -06:00
|
|
|
};
|
|
|
|
|
|
|
|
// A named group of key/value entries.
|
|
|
|
// Specification: §3.2
|
2024-10-04 22:40:43 -06:00
|
|
|
export type group = struct {
|
|
|
|
name: str,
|
|
|
|
lines: []line,
|
|
|
|
};
|
2024-10-03 17:52:17 -06:00
|
|
|
|
|
|
|
// A line in the file, which can be a comment (or a blank line), an entry, or
|
|
|
|
// a localized entry.
|
2024-10-12 20:12:43 -06:00
|
|
|
export type line = (blank | comment | entry | localized_entry);
|
|
|
|
|
|
|
|
// A blank line.
|
|
|
|
// Specification: §3.1
|
|
|
|
export type blank = void;
|
2024-10-03 17:52:17 -06:00
|
|
|
|
|
|
|
// A comment.
|
|
|
|
// Specification: §3.1
|
2024-10-04 22:40:43 -06:00
|
|
|
export type comment = str;
|
2024-10-03 17:52:17 -06:00
|
|
|
|
|
|
|
// A key/value pair.
|
|
|
|
// Specification: §3.3
|
2024-10-04 22:40:43 -06:00
|
|
|
export type entry = struct {
|
|
|
|
key: str,
|
|
|
|
value: value,
|
2024-10-03 17:52:17 -06:00
|
|
|
};
|
|
|
|
|
2024-10-04 22:51:10 -06:00
|
|
|
// A localized key/value pair.
|
2024-10-03 17:52:17 -06:00
|
|
|
// Specification: §5
|
2024-10-04 22:40:43 -06:00
|
|
|
export type localized_entry = struct {
|
|
|
|
key: str,
|
|
|
|
value: value,
|
|
|
|
locale: locale::locale,
|
2024-10-03 17:52:17 -06:00
|
|
|
};
|
|
|
|
|
2024-10-12 20:12:43 -06:00
|
|
|
// Gets a non-localized [[value]] from a [[file]]. If the group does not exist,
|
|
|
|
// or the group exists but the key isn't in it, it returns void.
|
2024-10-04 22:40:43 -06:00
|
|
|
export fn file_get(fil: *file, group_name: str, key: str) (value | void) = {
|
2024-10-03 17:52:17 -06:00
|
|
|
let grou = match (file_find_group(fil, group_name)) {
|
2024-10-04 22:40:43 -06:00
|
|
|
case let index: size => yield &fil.groups[index];
|
|
|
|
case void => return void;
|
2024-10-03 17:52:17 -06:00
|
|
|
};
|
|
|
|
let lin = match (group_find_entry(grou, key)) {
|
2024-10-04 22:40:43 -06:00
|
|
|
case let index: size => yield grou.lines[index];
|
|
|
|
case void => return void;
|
2024-10-03 17:52:17 -06:00
|
|
|
};
|
|
|
|
match (lin) {
|
2024-10-04 22:40:43 -06:00
|
|
|
case let entr: entry => return entr.value;
|
|
|
|
case => abort();
|
2024-10-03 17:52:17 -06:00
|
|
|
};
|
|
|
|
};
|
|
|
|
|
2024-10-04 22:51:10 -06:00
|
|
|
// Gets a localized [[value]] from a [[file]]. If the group does not exist, or
|
|
|
|
// the group exists but the key isnt in it, it returns void. If the key is in
|
|
|
|
// the group but is not localized to the specified [[locale::locale]], the
|
|
|
|
// non-localized value is returned. Refer to the specification for the exact way
|
|
|
|
// which the locale is matched.
|
|
|
|
// Specification: §5
|
2024-10-03 17:52:17 -06:00
|
|
|
export fn file_get_localized(
|
|
|
|
fil: *file,
|
|
|
|
group_name: str,
|
|
|
|
key: str,
|
2024-10-04 22:40:43 -06:00
|
|
|
local: locale::locale,
|
|
|
|
) (value | void) = {
|
2024-10-03 17:52:17 -06:00
|
|
|
let grou = match (file_find_group(fil, group_name)) {
|
2024-10-04 22:40:43 -06:00
|
|
|
case let index: size => yield &fil.groups[index];
|
|
|
|
case void => return void;
|
2024-10-03 17:52:17 -06:00
|
|
|
};
|
|
|
|
let lin = match (group_find_localized_entry(grou, key, local)) {
|
2024-10-04 22:40:43 -06:00
|
|
|
case let index: size => yield grou.lines[index];
|
|
|
|
case void =>
|
|
|
|
yield match(group_find_entry(grou, key)) {
|
|
|
|
case let index: size => yield index;
|
|
|
|
case void => return void;
|
|
|
|
};
|
2024-10-03 17:52:17 -06:00
|
|
|
};
|
|
|
|
match (lin) {
|
2024-10-04 22:40:43 -06:00
|
|
|
case let entr: localized_entry => return entr.value;
|
|
|
|
case let entr: entry => return entr.value;
|
|
|
|
case => abort();
|
2024-10-03 17:52:17 -06:00
|
|
|
};
|
|
|
|
};
|
|
|
|
|
2024-10-12 20:12:43 -06:00
|
|
|
// TODO
|
|
|
|
|
2024-10-04 22:40:43 -06:00
|
|
|
//export fn file_add
|
2024-10-03 17:52:17 -06:00
|
|
|
|
2024-10-04 22:40:43 -06:00
|
|
|
//export fn file_add_localized
|
2024-10-03 17:52:17 -06:00
|
|
|
|
2024-10-04 22:40:43 -06:00
|
|
|
//export fn file_remove
|
2024-10-03 17:52:17 -06:00
|
|
|
|
2024-10-04 22:40:43 -06:00
|
|
|
//export fn file_remove_localized
|
2024-10-03 17:52:17 -06:00
|
|
|
|
2024-10-04 22:40:43 -06:00
|
|
|
//export fn file_encode
|
2024-10-03 17:52:17 -06:00
|
|
|
|
2024-10-12 20:12:43 -06:00
|
|
|
// Frees memory associated with a [[file]].
|
|
|
|
export fn file_finish(fil *file) void = {
|
|
|
|
for (let lin .. file.lines) {
|
|
|
|
line_finish(lin);
|
|
|
|
};
|
|
|
|
free(file.lines);
|
|
|
|
for (let grou .. file.groups) {
|
|
|
|
group_finish(grou);
|
|
|
|
};
|
|
|
|
free(file.groups);
|
|
|
|
};
|
2024-10-03 17:52:17 -06:00
|
|
|
|
2024-10-04 22:40:43 -06:00
|
|
|
fn file_find_group(fil: *file, group_name: str) (size | void) = {
|
|
|
|
let index = 0z;
|
|
|
|
for (let grou .. fil.groups) {
|
|
|
|
if (grou.name == group_name) {
|
2024-10-03 17:52:17 -06:00
|
|
|
return index;
|
2024-10-04 22:40:43 -06:00
|
|
|
};
|
|
|
|
index += 1;
|
|
|
|
};
|
|
|
|
};
|
2024-10-03 17:52:17 -06:00
|
|
|
|
2024-10-04 22:40:43 -06:00
|
|
|
fn group_find_entry(grou: *group, key: str) (size | void) = {
|
|
|
|
let index = 0z;
|
2024-10-03 17:52:17 -06:00
|
|
|
for (let lin .. grou.lines) {
|
|
|
|
match (lin) {
|
2024-10-04 22:40:43 -06:00
|
|
|
case let entr: entry =>
|
|
|
|
if (entr.key == key) {
|
2024-10-03 17:52:17 -06:00
|
|
|
return index;
|
2024-10-04 22:40:43 -06:00
|
|
|
};
|
|
|
|
case => void;
|
|
|
|
};
|
|
|
|
index += 1;
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
fn group_find_localized_entry(grou: *group, key: str, local: locale::locale) (size | void) = {
|
|
|
|
// The matching is done as follows. If LC_MESSAGES is of the form
|
|
|
|
// lang_COUNTRY.ENCODING@MODIFIER, then it will match a key of the form
|
|
|
|
// lang_COUNTRY@MODIFIER. If such a key does not exist, it will attempt
|
|
|
|
// to match lang_COUNTRY followed by lang@MODIFIER. Then, a match
|
|
|
|
// against lang by itself will be attempted. Finally, if no matching
|
|
|
|
// key is found the required key without a locale specified is used.
|
|
|
|
// The encoding from the LC_MESSAGES value is ignored when matching.
|
|
|
|
//
|
|
|
|
// If LC_MESSAGES does not have a MODIFIER field, then no key with a
|
|
|
|
// modifier will be matched. Similarly, if LC_MESSAGES does not have a
|
|
|
|
// COUNTRY field, then no key with a country specified will be matched.
|
|
|
|
// If LC_MESSAGES just has a lang field, then it will do a straight
|
|
|
|
// match to a key with a similar value.
|
|
|
|
|
|
|
|
let lang_country_modifier = local;
|
|
|
|
lang_country_modifier.encoding = "";
|
|
|
|
match(group_find_localized_entry_exact(grou, key, lang_country_modifier)) {
|
|
|
|
case let index: size => return index;
|
|
|
|
case => void;
|
|
|
|
};
|
|
|
|
|
|
|
|
let lang_country = lang_country_modifier;
|
|
|
|
lang_country.modifier = "";
|
|
|
|
match(group_find_localized_entry_exact(grou, key, lang_country)) {
|
|
|
|
case let index: size => return index;
|
|
|
|
case => void;
|
|
|
|
};
|
|
|
|
|
|
|
|
let lang_modifier = lang_country_modifier;
|
|
|
|
lang_modifier.country = "";
|
|
|
|
match(group_find_localized_entry_exact(grou, key, lang_modifier)) {
|
|
|
|
case let index: size => return index;
|
|
|
|
case => void;
|
|
|
|
};
|
|
|
|
|
|
|
|
let lang = lang_modifier;
|
|
|
|
lang.modifier = "";
|
|
|
|
match(group_find_localized_entry_exact(grou, key, lang)) {
|
|
|
|
case let index: size => return index;
|
|
|
|
case => void;
|
|
|
|
};
|
|
|
|
|
|
|
|
return void;
|
|
|
|
};
|
|
|
|
|
2024-10-12 20:12:43 -06:00
|
|
|
fn group_find_localized_entry_exact(grou: group, key: str, local: locale::locale) (size | void) = {
|
2024-10-04 22:40:43 -06:00
|
|
|
let index = 0z;
|
|
|
|
for (let lin .. grou.lines) {
|
|
|
|
match (lin) {
|
|
|
|
case let entr: localized_entry =>
|
|
|
|
if (entr.key == key && locale::equal(entr.locale, local)) {
|
|
|
|
return index;
|
|
|
|
};
|
|
|
|
case => void;
|
|
|
|
};
|
|
|
|
index += 1;
|
|
|
|
};
|
|
|
|
};
|