From 0362ae4515803382e1ae24ab6c5e0f3bd18d3fdb Mon Sep 17 00:00:00 2001 From: Sasha Koshka Date: Tue, 22 Oct 2024 01:10:05 -0400 Subject: [PATCH] format::desktop_entry: Add desktop entry file parsing --- format/desktop_entry/desktop_entry.ha | 189 ++++++++++++++++++++++++++ 1 file changed, 189 insertions(+) create mode 100644 format/desktop_entry/desktop_entry.ha diff --git a/format/desktop_entry/desktop_entry.ha b/format/desktop_entry/desktop_entry.ha new file mode 100644 index 0000000..6d7694b --- /dev/null +++ b/format/desktop_entry/desktop_entry.ha @@ -0,0 +1,189 @@ +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); +};