use locale; use strings; // The information from a desktop entry file. export type file = struct { typ: str, version: str, name: locale::string, generic_name: locale::string, no_display: bool, comment: locale::string, icon: locale::string, hidden: bool, only_show_in: []str, not_show_in: []str, dbus_activatable: bool, try_exec: str, exec: str, path: str, terminal: bool, actions: []str, mime_type: []str, categories: []str, implements: []str, keywords: locale::strings, startup_notify: bool, startup_wm_class: str, url: str, prefers_non_default_gpu: bool, single_main_window: bool, }; // Parses a desktop entry file. Use [[file_finish]] to get rid of it. export fn parse(input: io::handle) (file | error) = { let state = parse_state { ... }; let scanne = scan(input); defer finish(&scanne); for (let entr => next_entry(&scanne)) { match(parse_handle_entry(entr)) { case let err: error => file_finish(&state.file); return err; case void => void; }; }; }; // Frees resources associated with a [[file]]. export fn file_finish(file: &file) void = { free(file.typ); free(file.version); locale_string_finish(file.name); locale_string_finish(file.generic_name); locale_string_finish(file.comment); locale_string_finish(file.icon); strings::freeall(file.only_show_in); strings::freeall(file.not_show_in); free(file.try_exec); free(file.exec); free(file.path); strings::freeall(file.actions); strings::freeall(file.mime_type); strings::freeall(file.categories); strings::freeall(file.implements); locale_strings_finish(file.keywords); free(file.startup_wm_class); free(file.url); }; type parse_state = struct { file: file, }; fn parse_handle_entry(this: *parse_state, entr: entry) (void | error) = { if (entr.group != "Desktop Entry") return void; if (entr.key == "Type") { if parse_is_localized(entr) return void; this.typ = parse_string(entr.value)?; } else if (entr.key == "Version") { if parse_is_localized(entr) return void; this.version = parse_string(entr.value)?; } else if (entr.key == "Name") { parse_set_locale_string( *this.name, entr.locale, parse_localestring(entr.value)?)?; } else if (entr.key == "GenericName") { parse_set_locale_string( *this.generic_name, entr.locale, parse_localestring(entr.value)?)?; } else if (entr.key == "NoDisplay") { if parse_is_localized(entr) return void; this.no_display = parse_boolean(entr.value)?; } else if (entr.key == "Comment") { parse_set_locale_string( *this.comment, entr.locale, parse_localestring(entr.value)?)?; } else if (entr.key == "Icon") { parse_set_locale_string( *this.icon, entr.locale, parse_iconstring(entr.value)?)?; } else if (entr.key == "Hidden") { if parse_is_localized(entr) return void; this.hidden = parse_boolean(entr.value)?; } else if (entr.key == "OnlyShowIn") { if parse_is_localized(entr) return void; this.only_show_in = parse_strings(entr.value)?; } else if (entr.key == "NotShowIn") { if parse_is_localized(entr) return void; this.not_show_in = parse_strings(entr.value)?; } else if (entr.key == "DBusActivatable") { if parse_is_localized(entr) return void; this.dbus_activatable = parse_bool(entr.value)?; } else if (entr.key == "TryExec") { if parse_is_localized(entr) return void; this.try_exec = parse_string(entr.value)?; } else if (entr.key == "Path") { if parse_is_localized(entr) return void; this.path = parse_string(entr.value)?; } else if (entr.key == "Terminal") { if parse_is_localized(entr) return void; this.terminal = parse_boolean(entr.value)?; } else if (entr.key == "Actions") { if parse_is_localized(entr) return void; this.actions = parse_strings(entr.value)?; } else if (entr.key == "MimeType") { if parse_is_localized(entr) return void; this.mime_type = parse_strings(entr.value)?; } else if (entr.key == "Categories") { if parse_is_localized(entr) return void; this.categories = parse_strings(entr.value)?; } else if (entr.key == "Implements") { if parse_is_localized(entr) return void; this.implements = parse_strings(entr.value)?; } else if (entr.key == "Keywords") { parse_set_locale_strings( *this.keywords, entr.locale, parse_localestring(entr.value)?)?; } else if (entr.key == "StartupWMClass") { if parse_is_localized(entr) return void; this.startup_wm_class = parse_string(entr.value)?; } else if (entr.key == "URL") { if parse_is_localized(entr) return void; this.url = parse_string(entr.value)?; } else if (entr.key == "PrefersNonDefaultGPU") { if parse_is_localized(entr) return void; this.prefers_non_default_gpu = parse_bool(entr.value)?; } else if (entr.key == "SingleMainWindow") { if parse_is_localized(entr) return void; this.single_main_window = parse_bool(entr.value)?; }; }; fn parse_is_localized(entr: entry) bool = { return !locale::equal(entr.locale, locale::c); }; fn parse_set_locale_string(dest *locale::string, local: locale::locale, value: str) void = { for (let existing &.. *dest) { if (locale::equal(existing.0, locale)) { existing.1 = value; return; }; }; append(*dest, (locale::dup(local), strings::dup(value))); }; fn parse_set_locale_strings(dest *locale::strings, local: locale::locale, value: []str) void = { for (let existing &.. *dest) { if (locale::equal(existing.0, locale)) { existing.1 = value; return; }; }; append(*dest, (locale::dup(local), strings::dupall(value))); }; fn locale_string_finish(string: locale::string) void = { for (let pair .. string) { locale::finish(pair.0); free(pair.1); }; free(string); }; fn locale_strings_finish(strings: locale::strings) void = { for (string .. strings) locale_string_finish(string); };