252 lines
7.6 KiB
Hare
252 lines
7.6 KiB
Hare
use io;
|
|
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,
|
|
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,
|
|
|
|
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 file = file { ... };
|
|
let scanne = scan(input);
|
|
defer finish(&scanne);
|
|
|
|
for (let entr => next_entry(&scanne)?) {
|
|
match(parse_handle_entry(&file, entr)) {
|
|
case let err: error =>
|
|
file_finish(&file);
|
|
return err;
|
|
case void => void;
|
|
};
|
|
};
|
|
|
|
return file;
|
|
};
|
|
|
|
// Frees resources associated with a [[file]].
|
|
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);
|
|
};
|
|
|
|
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_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;
|
|
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_boolean(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 == "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;
|
|
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;
|
|
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;
|
|
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_localestrings(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_boolean(entr.value)?;
|
|
} else if (entr.key == "SingleMainWindow") {
|
|
if (parse_is_localized(entr)) return void;
|
|
this.single_main_window = parse_boolean(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, local)) {
|
|
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, local)) {
|
|
existing.1 = value;
|
|
return;
|
|
};
|
|
};
|
|
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 = {
|
|
for (let pair .. string) {
|
|
locale::finish(pair.0);
|
|
free(pair.1);
|
|
};
|
|
free(string);
|
|
};
|
|
|
|
fn locale_strings_finish(strings: locale::strings) void = {
|
|
for (let pair .. strings) {
|
|
locale::finish(pair.0);
|
|
strings::freeall(pair.1);
|
|
};
|
|
free(strings);
|
|
};
|