diff --git a/format/desktop_entry/desktop_entry.ha b/format/desktop_entry/desktop_entry.ha index 6d7694b..daf765a 100644 --- a/format/desktop_entry/desktop_entry.ha +++ b/format/desktop_entry/desktop_entry.ha @@ -1,3 +1,4 @@ +use io; use locale; use strings; @@ -18,7 +19,6 @@ export type file = struct { exec: str, path: str, terminal: bool, - actions: []str, mime_type: []str, categories: []str, implements: []str, @@ -28,127 +28,179 @@ export type file = struct { url: str, prefers_non_default_gpu: bool, single_main_window: bool, + + actions: []action, +}; + +export type action = struct { + key: str, + name: locale::string, + icon: locale::string, + exec: str, }; // 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 file = file { ... }; let scanne = scan(input); defer finish(&scanne); - - for (let entr => next_entry(&scanne)) { - match(parse_handle_entry(entr)) { + + for (let entr => next_entry(&scanne)?) { + match(parse_handle_entry(&file, entr)) { case let err: error => - file_finish(&state.file); + file_finish(&file); return err; case void => void; }; }; + + return file; }; // 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); +export fn file_finish(this: *file) void = { + free(this.typ); + free(this.version); + locale_string_finish(this.name); + locale_string_finish(this.generic_name); + locale_string_finish(this.comment); + locale_string_finish(this.icon); + strings::freeall(this.only_show_in); + strings::freeall(this.not_show_in); + free(this.try_exec); + free(this.exec); + free(this.path); + strings::freeall(this.mime_type); + strings::freeall(this.categories); + strings::freeall(this.implements); + locale_strings_finish(this.keywords); + free(this.startup_wm_class); + free(this.url); + + for (let actio .. this.actions) { + free(actio.key); + locale_string_finish(actio.name); + locale_string_finish(actio.icon); + free(actio.exec); + }; + free(this.actions); }; -type parse_state = struct { - file: file, +fn parse_handle_entry(this: *file, entr: entry) (void | error) = { + const desktop_action_prefix = "Desktop Action "; + if (entr.group == "Desktop Entry") { + return parse_handle_desktop_entry_entry(this, entr); + } else if (strings::hasprefix(entr.group, desktop_action_prefix)) { + let key = strings::trimprefix(entr.group, desktop_action_prefix); + return parse_handle_desktop_action_entry(this, entr, key); + }; }; -fn parse_handle_entry(this: *parse_state, entr: entry) (void | error) = { - if (entr.group != "Desktop Entry") return void; - +fn parse_handle_desktop_action_entry(this: *file, entr: entry, key: str) (void | error) = { + let actio = match(parse_find_action(this, key)) { + case let actio: *action => yield actio; + case void => return void; + }; + if (entr.key == "Name") { + parse_set_locale_string( + &actio.name, entr.locale, + parse_localestring(entr.value)?); + } else if (entr.key == "Icon") { + parse_set_locale_string( + &actio.icon, entr.locale, + parse_iconstring(entr.value)?); + } else if (entr.key == "Exec") { + if (parse_is_localized(entr)) return void; + actio.exec = parse_string(entr.value)?; + }; +}; + +fn parse_handle_desktop_entry_entry(this: *file, entr: entry) (void | error) = { if (entr.key == "Type") { - if parse_is_localized(entr) return void; + 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; + 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)?)?; + &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)?)?; + &this.generic_name, entr.locale, + parse_localestring(entr.value)?); } else if (entr.key == "NoDisplay") { - if parse_is_localized(entr) return void; + 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)?)?; + &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)?)?; + &this.icon, entr.locale, + parse_iconstring(entr.value)?); } else if (entr.key == "Hidden") { - if parse_is_localized(entr) return void; + 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; + 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; + 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)?; + if (parse_is_localized(entr)) return void; + this.dbus_activatable = parse_boolean(entr.value)?; } else if (entr.key == "TryExec") { - if parse_is_localized(entr) return void; + if (parse_is_localized(entr)) return void; this.try_exec = parse_string(entr.value)?; + } else if (entr.key == "Exec") { + if (parse_is_localized(entr)) return void; + this.exec = parse_string(entr.value)?; } else if (entr.key == "Path") { - if parse_is_localized(entr) return void; + 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; + 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)?; + if (parse_is_localized(entr)) return void; + let strings = parse_strings(entr.value)?; + defer free(strings); + this.actions = alloc([], len(strings)); + for (let string .. strings) { + append(this.actions, action { + key = string, + ... + }); + }; } else if (entr.key == "MimeType") { - if parse_is_localized(entr) return void; + 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; + 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; + 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)?)?; + &this.keywords, entr.locale, + parse_localestrings(entr.value)?); } else if (entr.key == "StartupWMClass") { - if parse_is_localized(entr) return void; + 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; + 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)?; + if (parse_is_localized(entr)) return void; + this.prefers_non_default_gpu = parse_boolean(entr.value)?; } else if (entr.key == "SingleMainWindow") { - if parse_is_localized(entr) return void; - this.single_main_window = parse_bool(entr.value)?; + if (parse_is_localized(entr)) return void; + this.single_main_window = parse_boolean(entr.value)?; }; }; @@ -156,24 +208,30 @@ 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 = { +fn parse_set_locale_string(dest: *locale::string, local: locale::locale, value: str) void = { for (let existing &.. *dest) { - if (locale::equal(existing.0, locale)) { + if (locale::equal(existing.0, local)) { existing.1 = value; return; }; }; - append(*dest, (locale::dup(local), strings::dup(value))); + append(dest, (locale::dup(local), strings::dup(value))); }; -fn parse_set_locale_strings(dest *locale::strings, local: locale::locale, value: []str) void = { +fn parse_set_locale_strings(dest: *locale::strings, local: locale::locale, value: []str) void = { for (let existing &.. *dest) { - if (locale::equal(existing.0, locale)) { + if (locale::equal(existing.0, local)) { existing.1 = value; return; }; }; - append(*dest, (locale::dup(local), strings::dupall(value))); + append(dest, (locale::dup(local), strings::dupall(value))); +}; + +fn parse_find_action(this: *file, key: str) (*action | void) = { + for (let actio &.. this.actions) { + if (actio.key == key) return actio; + }; }; fn locale_string_finish(string: locale::string) void = { @@ -185,5 +243,9 @@ fn locale_string_finish(string: locale::string) void = { }; fn locale_strings_finish(strings: locale::strings) void = { - for (string .. strings) locale_string_finish(string); + for (let pair .. strings) { + locale::finish(pair.0); + strings::freeall(pair.1); + }; + free(strings); };