use locale; use strings; // Represents a file of the generic key/value format used by desktop entries. // Specification: §3 export type file = struct { preceeding_lines: []line, groups: []group, }; // A named group of key/value entries. // Specification: §3.2 export type group = struct { name: str, lines: []line, }; // A line in the file, which can be a comment (or a blank line), an entry, or // a localized entry. export type line = (comment | entry | localized_entry); // A comment. // Specification: §3.1 export type comment = str; // A key/value pair. // Specification: §3.3 export type entry = struct { key: str, value: value, }; // A localized entry. // Specification: §5 export type localized_entry = struct { key: str, value: value, locale: locale::locale, }; // An entry value. Values that reference memory (such as [[str]]) are borrowed // from the [[file]]. These may be free'd or overwritten when other functions in // this module are called, so [[value_dup]] is required to extend its lifetime. export type value = (str | bool | f32); // 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. export fn file_get(fil: *file, group_name: str, key: str) (value | void) = { let grou = match (file_find_group(fil, group_name)) { case let index: size => yield &fil.groups[index]; case void => return void; }; let lin = match (group_find_entry(grou, key)) { case let index: size => yield grou.lines[index]; case void => return void; }; match (lin) { case let entr: entry => return entr.value; case => abort(); }; }; // 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, the non-localized value is // returned. export fn file_get_localized( fil: *file, group_name: str, key: str, local: locale::locale, ) (value | void) = { let grou = match (file_find_group(fil, group_name)) { case let index: size => yield &fil.groups[index]; case void => return void; }; let lin = match (group_find_localized_entry(grou, key, local)) { 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; }; }; match (lin) { case let entr: localized_entry => return entr.value; case let entr: entry => return entr.value; case => abort(); }; }; //export fn file_add //export fn file_add_localized //export fn file_remove //export fn file_remove_localized //export fn file_encode //export fn file_finish fn file_find_group(fil: *file, group_name: str) (size | void) = { let index = 0z; for (let grou .. fil.groups) { if (grou.name == group_name) { return index; }; index += 1; }; }; fn group_find_entry(grou: *group, key: str) (size | void) = { let index = 0z; for (let lin .. grou.lines) { match (lin) { case let entr: entry => if (entr.key == key) { return index; }; 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; }; fn group_find_localized_entry_exact(grou: *group, key: str, local: locale::locale) (size | void) = { 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; }; }; // Duplicates a [[value]]. Use [[value_finish]] to get rid of it. export fn value_dup(valu: value) value = match (valu) { case let valu: str => yield strings::dup(valu); case => yield valu; }; // Frees an [[entry]] previously duplicated with [[entry_dup]]. export fn value_finish(valu: value) void = match (valu) { case let valu: str => free(valu); case => void; };