Made the desktop entry format more... convenient to work with

This commit is contained in:
Sasha Koshka 2024-10-19 16:15:23 -04:00
parent b90f0c5378
commit cabc7c1e19
3 changed files with 44 additions and 52 deletions

View File

@ -3,7 +3,7 @@ use strings;
// A line in the file, which can be a comment (or a blank line), an entry, or // A line in the file, which can be a comment (or a blank line), an entry, or
// a localized entry. // a localized entry.
export type line = (blank | comment | entry | localized_entry); export type line = (blank | comment | group_header | entry);
// A blank line. // A blank line.
// Specification: §3.1 // Specification: §3.1
@ -17,35 +17,27 @@ export type comment = str;
// Specification: §3.2 // Specification: §3.2
export type group_header = str; export type group_header = str;
// A key/value pair. // An entry in a desktop file.
// Specification: §3.3 // Specification: §3.3, §5
export type entry = (str, str); export type entry = struct {
group: str,
// A localized key/value pair. key: str,
// Specification: §5 value: str,
export type localized_entry = (str, str, locale::locale); locale: locale::locale,
};
// Duplicates an [[entry]]. Use [[entry_finish]] to get rid of it. // Duplicates an [[entry]]. Use [[entry_finish]] to get rid of it.
export fn entry_dup(entr: entry) entry = ( export fn entry_dup(entr: entry) entry = entry {
strings::dup(entr.0), group = strings::dup(entr.group),
strings::dup(entr.1)); key = strings::dup(entr.key),
value = strings::dup(entr.value),
locale = locale::dup(entr.locale),
};
// Frees memory associated with an [[entry]]. // Frees memory associated with an [[entry]].
export fn entry_finish(entr: entry) void = { export fn entry_finish(entr: entry) void = {
free(entr.0); free(entr.group);
free(entr.1); free(entr.key);
}; free(entr.value);
locale::finish(entr.locale);
// Duplicates a [[localized_entry]]. Use [[localized_entry_finish]] to get rid
// of it.
export fn localized_entry_dup(entr: localized_entry) localized_entry = (
strings::dup(entr.0),
strings::dup(entr.1),
locale::dup(entr.2));
// Frees memory associated with an [[localized_entry]].
export fn localized_entry_finish(entr: localized_entry) void = {
free(entr.0);
free(entr.1);
locale::finish(entr.2);
}; };

View File

@ -17,7 +17,7 @@ export type invalid_group_header = !void;
// Returned when a malformed entry is encountered while parsing. // Returned when a malformed entry is encountered while parsing.
export type invalid_entry = !void; export type invalid_entry = !void;
// Returned when ASCII text was expected, while parsing, but something else was // Returned when ASCII text was expected while parsing, but something else was
// given. // given.
export type invalid_ascii = !void; export type invalid_ascii = !void;

View File

@ -8,8 +8,7 @@ use strings;
export type scanner = struct { export type scanner = struct {
scanner: bufio::scanner, scanner: bufio::scanner,
entry: entry, group: str,
localized_entry: localized_entry,
}; };
// Creates a desktop entry file scanner. Use [[next]] to read lines. The caller // Creates a desktop entry file scanner. Use [[next]] to read lines. The caller
@ -41,30 +40,25 @@ export fn next(this: *scanner) (line | io::EOF | error) = {
} else if (strings::hasprefix(text, '[')) { } else if (strings::hasprefix(text, '[')) {
// group header // group header
return parse_group_header(text)?; let header = parse_group_header(text)?;
free(this.group);
this.group = header: str;
return header;
} else { } else {
// key/value pair // key/value pair
let (key, valu, local) = parse_entry(text)?; let entry = parse_entry(text)?;
entry.group = this.group;
match (local) { return entry;
case let local: locale::locale =>
localized_entry_finish(this.localized_entry);
this.localized_entry = (key, valu, local);
return this.localized_entry;
case void =>
entry_finish(this.entry);
this.entry = (key, valu);
return this.entry;
}; };
};
}; };
// TODO: have next_entry that auto-skips over blank lines and comments
// Frees resources associated with a [[scanner]]. // Frees resources associated with a [[scanner]].
export fn finish(this: *scanner) void = { export fn finish(this: *scanner) void = {
bufio::finish(&this.scanner); bufio::finish(&this.scanner);
entry_finish(this.entry); free(this.group);
}; };
// memory is borrowed from the input // memory is borrowed from the input
@ -80,26 +74,32 @@ fn parse_group_header(text: str) (group_header | error) = {
return text: group_header; return text: group_header;
}; };
// memory must be freed by the caller // memory is borrowed from the input
fn parse_entry(line: str) ((str, str, (locale::locale | void)) | error) = { fn parse_entry(line: str) (entry | error) = {
if (!strings::contains(line, '=')) return invalid_entry; if (!strings::contains(line, '=')) return invalid_entry;
let (key, valu) = strings::cut(line, "="); let (key, valu) = strings::cut(line, "=");
key = strings::ltrim(strings::rtrim(key)); key = strings::ltrim(strings::rtrim(key));
if (!validate_entry_key(key)) return invalid_entry; if (!validate_entry_key(key)) return invalid_entry;
let local: (locale::locale | void) = void;
let (key, local_string) = strings::cut(key, "["); let (key, local_string) = strings::cut(key, "[");
if (local_string != "") { let local = if (local_string != "") {
yield locale::c;
} else {
local_string = strings::rtrim(local_string, ']'); local_string = strings::rtrim(local_string, ']');
if (!validate_entry_locale(local_string)) return invalid_entry; if (!validate_entry_locale(local_string)) return invalid_entry;
local = match(locale::parse(local_string)) { yield match(locale::parse(local_string)) {
case let local: locale::locale => yield local; case let local: locale::locale => yield local;
case locale::invalid => return invalid_entry; case locale::invalid => return invalid_entry;
}; };
}; };
return (strings::dup(key), strings::dup(valu), local); return entry {
key = key,
value = valu,
locale = local,
...
};
}; };
fn validate_entry_key(key: str) bool = { fn validate_entry_key(key: str) bool = {