tray: parse locale strings in icon themes

This commit is contained in:
Ian Fan 2020-04-02 15:52:09 +01:00
parent 83e7d68e31
commit 5133013fb1
2 changed files with 114 additions and 19 deletions

View file

@ -20,6 +20,7 @@ struct icon_theme_subdir {
struct icon_theme { struct icon_theme {
char *name; char *name;
int name_locale_level;
char *comment; char *comment;
list_t *inherits; // char * list_t *inherits; // char *
list_t *directories; // char * list_t *directories; // char *

View file

@ -1,6 +1,7 @@
#define _POSIX_C_SOURCE 200809L #define _POSIX_C_SOURCE 200809L
#include <ctype.h> #include <ctype.h>
#include <dirent.h> #include <dirent.h>
#include <locale.h>
#include <stdbool.h> #include <stdbool.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
@ -96,12 +97,6 @@ static const char *group_handler(char *old_group, char *new_group,
return "missing required key 'Comment'"; return "missing required key 'Comment'";
} else if (!theme->directories) { } else if (!theme->directories) {
return "missing required key 'Directories'"; return "missing required key 'Directories'";
} else {
for (char *c = theme->name; *c; ++c) {
if (*c == ',' || *c == ' ') {
return "malformed theme name";
}
}
} }
} else { } else {
if (theme->subdirs->length == 0) { // skip if (theme->subdirs->length == 0) { // skip
@ -142,12 +137,22 @@ static const char *group_handler(char *old_group, char *new_group,
} }
static const char *entry_handler(char *group, char *key, char *value, static const char *entry_handler(char *group, char *key, char *value,
struct icon_theme *theme) { int locale_level, struct icon_theme *theme) {
if (strcmp(group, "Icon Theme") == 0) { if (strcmp(group, "Icon Theme") == 0) {
if (strcmp(key, "Name") == 0) { if (strcmp(key, "Name") == 0) {
theme->name = strdup(value); if (locale_level > theme->name_locale_level) {
for (char *c = value; *c; ++c) {
if (iscntrl(*c)) {
return "malformed theme name";
}
}
theme->name = strdup(value);
theme->name_locale_level = locale_level;
}
} else if (strcmp(key, "Comment") == 0) { } else if (strcmp(key, "Comment") == 0) {
theme->comment = strdup(value); if (!theme->comment && locale_level >= 0) {
theme->comment = strdup(value);
}
} else if (strcmp(key, "Inherits") == 0) { } else if (strcmp(key, "Inherits") == 0) {
theme->inherits = split_string(value, ","); theme->inherits = split_string(value, ",");
} else if (strcmp(key, "Directories") == 0) { } else if (strcmp(key, "Directories") == 0) {
@ -198,6 +203,50 @@ static const char *entry_handler(char *group, char *key, char *value,
return NULL; return NULL;
} }
// TODO make this better?
// The C standard claims that setlocale returns an opaque string, but doesn't
// provide any mechanisms for retrieving useful information from the string
void get_locale_matchings(char *locale_matchings[6]) {
char *locale_ref = setlocale(LC_MESSAGES, NULL);
if (locale_ref == NULL || strcmp(locale_ref, "") == 0 ||
strcmp(locale_ref, "C") == 0 || strcmp(locale_ref, "POSIX") == 0) {
return;
}
// duplicate locale without encoding
char *locale = strdup(locale_ref);
char *encoding = strchr(locale, '.');
char *modifier = strchr(locale_ref, '@');
if (encoding) {
if (modifier) {
modifier = strcpy(encoding, modifier);
} else {
*encoding = '\0';
}
}
char *country = strchr(locale, '_');
if (modifier && country) {
locale_matchings[5] = locale;
locale_matchings[4] = strndup(locale, modifier - locale);
locale_matchings[3] = strdup(locale);
strcpy(locale_matchings[3] + (country - locale), modifier);
locale_matchings[2] = strndup(locale, country - locale);
} else if (country) {
locale_matchings[4] = locale;
locale_matchings[2] = strndup(locale, country - locale);
} else if (modifier) {
locale_matchings[3] = locale;
locale_matchings[2] = strndup(locale, modifier - locale);
} else {
locale_matchings[2] = locale;
}
for (int i = 0; i < 6; ++i) {
sway_log(SWAY_INFO, "%d %s", i, locale_matchings[i]);
}
}
/* /*
* This is a Freedesktop Desktop Entry parser (essentially INI) * This is a Freedesktop Desktop Entry parser (essentially INI)
* It calls entry_handler for every entry * It calls entry_handler for every entry
@ -226,7 +275,12 @@ static struct icon_theme *read_theme_file(char *basedir, char *theme_name) {
} }
theme->subdirs = create_list(); theme->subdirs = create_list();
// parse locale
char *locale_matchings[6] = { NULL };
get_locale_matchings(locale_matchings);
list_t *groups = create_list(); list_t *groups = create_list();
list_t *entries = create_list(); // per group
const char *error = NULL; const char *error = NULL;
int line_no = 0; int line_no = 0;
@ -269,6 +323,10 @@ static struct icon_theme *read_theme_file(char *basedir, char *theme_name) {
} }
list_add(groups, strdup(&line[1])); list_add(groups, strdup(&line[1]));
for (int i = 0; i < entries->length; ++i) {
free(entries->items[i]);
}
entries->length = 0;
} else { // key-value pair } else { // key-value pair
if (groups->length == 0) { if (groups->length == 0) {
error = "unexpected content before first header"; error = "unexpected content before first header";
@ -276,22 +334,52 @@ static struct icon_theme *read_theme_file(char *basedir, char *theme_name) {
} }
// check well-formed // check well-formed
int eok = 0; char *p = line;
for (; isalnum(line[eok]) || line[eok] == '-'; ++eok) {} // TODO locale? for (; isalnum(*p) || *p == '-'; ++p) {}
int i = eok - 1;
while (isspace(line[++i])) {} int locale_level = 1;
if (line[i] != '=') { if (*p == '[') {
*p = '\0'; // remove locale from key
char *locale = p + 1;
while (*++p != ']') {
if (*p == '\0') {
error = "malformed key-value pair";
break;
}
}
if (error) {
break;
}
*p++ = '\0'; // split into key-value pair
// match locale
for (locale_level = sizeof(locale_matchings) / sizeof(locale_matchings[0]);
--locale_level >= 0;) {
if (locale_matchings[locale_level] &&
strcmp(locale, locale_matchings[locale_level]) == 0) {
break;
}
}
list_add(entries, strdup(line));
}
for (; isspace(*p); ++p) {}
if (*p != '=') {
sway_log(SWAY_INFO, "%s", p);
error = "malformed key-value pair"; error = "malformed key-value pair";
break; break;
} }
*p = '\0'; // split into key-value pair
line[eok] = '\0'; // split into key-value pair while (isspace(*++p)) {}
char *value = &line[i];
while (isspace(*++value)) {}
// TODO unescape value // TODO unescape value
error = entry_handler(groups->items[groups->length - 1], line, // sway_log(SWAY_INFO, "%s %s %d", line, p, locale_level);
value, theme); error = entry_handler(groups->items[groups->length - 1], line, p,
locale_level, theme);
if (error) { if (error) {
break; break;
} }
@ -317,7 +405,12 @@ static struct icon_theme *read_theme_file(char *basedir, char *theme_name) {
theme = NULL; theme = NULL;
} }
for (size_t i = 0; i < sizeof(locale_matchings) / sizeof(locale_matchings[0]); ++i) {
free(locale_matchings[i]);
}
free(full_line); free(full_line);
list_free_items_and_destroy(entries);
list_free_items_and_destroy(groups); list_free_items_and_destroy(groups);
fclose(theme_file); fclose(theme_file);
return theme; return theme;
@ -486,6 +579,7 @@ static char *find_icon_with_theme(list_t *basedirs, list_t *themes, char *name,
char *test_icon = find_icon_in_subdir(name, basedirs->items[i], char *test_icon = find_icon_in_subdir(name, basedirs->items[i],
theme->dir, subdir->name); theme->dir, subdir->name);
if (test_icon) { if (test_icon) {
free(icon);
icon = test_icon; icon = test_icon;
smallest_error = error; smallest_error = error;
*min_size = subdir->min_size; *min_size = subdir->min_size;