aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--swaybar/tray/icon.c150
1 files changed, 90 insertions, 60 deletions
diff --git a/swaybar/tray/icon.c b/swaybar/tray/icon.c
index d0c3fa56..9c62911d 100644
--- a/swaybar/tray/icon.c
+++ b/swaybar/tray/icon.c
@@ -14,6 +14,10 @@
#include "log.h"
#include "stringop.h"
+static int cmp_id(const void *item, const void *cmp_to) {
+ return strcmp(item, cmp_to);
+}
+
static bool dir_exists(char *path) {
struct stat sb;
return stat(path, &sb) == 0 && S_ISDIR(sb.st_mode);
@@ -78,35 +82,37 @@ static void destroy_theme(struct icon_theme *theme) {
free(theme);
}
-static int cmp_group(const void *item, const void *cmp_to) {
- return strcmp(item, cmp_to);
-}
-
-static bool validate_icon_theme(struct icon_theme *theme) {
- return theme && theme->name && theme->comment && theme->directories;
-}
-
-static bool group_handler(char *old_group, char *new_group,
+static const char *group_handler(char *old_group, char *new_group,
struct icon_theme *theme) {
- if (!old_group) { // first group must be "Icon Theme"
- if (!new_group) {
- return true;
- }
- return strcmp(new_group, "Icon Theme") != 0;
+ if (!old_group) {
+ return new_group && strcmp(new_group, "Icon Theme") == 0 ? NULL :
+ "first group must be 'Icon Theme'";
}
if (strcmp(old_group, "Icon Theme") == 0) {
- if (!validate_icon_theme(theme)) {
- return true;
+ if (!theme->name) {
+ return "missing required key 'Name'";
+ } else if (!theme->comment) {
+ return "missing required key 'Comment'";
+ } else if (!theme->directories) {
+ return "missing required key 'Directories'";
+ } else {
+ for (char *c = theme->name; *c; ++c) {
+ if (*c == ',' || *c == ' ') {
+ return "malformed theme name";
+ }
+ }
}
} else {
if (theme->subdirs->length == 0) { // skip
- return false;
+ return NULL;
}
struct icon_theme_subdir *subdir =
theme->subdirs->items[theme->subdirs->length - 1];
- if (!subdir->size) return true;
+ if (!subdir->size) {
+ return "missing required key 'Size'";
+ }
switch (subdir->type) {
case FIXED: subdir->max_size = subdir->min_size = subdir->size;
@@ -122,20 +128,20 @@ static bool group_handler(char *old_group, char *new_group,
}
}
- if (new_group && list_seq_find(theme->directories, cmp_group, new_group) != -1) {
+ if (new_group && list_seq_find(theme->directories, cmp_id, new_group) != -1) {
struct icon_theme_subdir *subdir = calloc(1, sizeof(struct icon_theme_subdir));
if (!subdir) {
- return true;
+ return "out of memory";
}
subdir->name = strdup(new_group);
subdir->threshold = 2;
list_add(theme->subdirs, subdir);
}
- return false;
+ return NULL;
}
-static int entry_handler(char *group, char *key, char *value,
+static const char *entry_handler(char *group, char *key, char *value,
struct icon_theme *theme) {
if (strcmp(group, "Icon Theme") == 0) {
if (strcmp(key, "Name") == 0) {
@@ -149,20 +155,17 @@ static int entry_handler(char *group, char *key, char *value,
} // Ignored: ScaledDirectories, Hidden, Example
} else {
if (theme->subdirs->length == 0) { // skip
- return false;
+ return NULL;
}
struct icon_theme_subdir *subdir =
theme->subdirs->items[theme->subdirs->length - 1];
if (strcmp(subdir->name, group) != 0) { // skip
- return false;
+ return NULL;
}
- char *end;
- int n = strtol(value, &end, 10);
- if (strcmp(key, "Size") == 0) {
- subdir->size = n;
- return *end != '\0';
+ if (strcmp(key, "Context") == 0) {
+ return NULL; // ignored, but explicitly handled to not fail parsing
} else if (strcmp(key, "Type") == 0) {
if (strcmp(value, "Fixed") == 0) {
subdir->type = FIXED;
@@ -171,20 +174,28 @@ static int entry_handler(char *group, char *key, char *value,
} else if (strcmp(value, "Threshold") == 0) {
subdir->type = THRESHOLD;
} else {
- return true;
+ return "invalid value - expected 'Fixed', 'Scalable' or 'Threshold'";
}
+ return NULL;
+ }
+
+ char *end;
+ int n = strtol(value, &end, 10);
+ if (*end != '\0') {
+ return "invalid value - expected a number";
+ }
+
+ if (strcmp(key, "Size") == 0) {
+ subdir->size = n;
} else if (strcmp(key, "MaxSize") == 0) {
subdir->max_size = n;
- return *end != '\0';
} else if (strcmp(key, "MinSize") == 0) {
subdir->min_size = n;
- return *end != '\0';
} else if (strcmp(key, "Threshold") == 0) {
subdir->threshold = n;
- return *end != '\0';
- } // Ignored: Scale, Applications
+ } // Ignored: Scale
}
- return false;
+ return NULL;
}
/*
@@ -215,12 +226,16 @@ static struct icon_theme *read_theme_file(char *basedir, char *theme_name) {
}
theme->subdirs = create_list();
- bool error = false;
- char *group = NULL;
+ list_t *groups = create_list();
+
+ const char *error = NULL;
+ int line_no = 0;
char *full_line = NULL;
size_t full_len = 0;
ssize_t nread;
while ((nread = getline(&full_line, &full_len, theme_file)) != -1) {
+ ++line_no;
+
char *line = full_line - 1;
while (isspace(*++line)) {} // remove leading whitespace
if (!*line || line[0] == '#') continue; // ignore blank lines & comments
@@ -231,37 +246,42 @@ static struct icon_theme *read_theme_file(char *basedir, char *theme_name) {
if (line[0] == '[') { // group header
// check well-formed
- if (line[--len] != ']') {
- error = true;
- break;
- }
int i = 1;
for (; !iscntrl(line[i]) && line[i] != '[' && line[i] != ']'; ++i) {}
- if (i < len) {
- error = true;
+ if (i != --len || line[i] != ']') {
+ error = "malformed group header";
break;
}
- // call handler
line[len] = '\0';
- error = group_handler(group, &line[1], theme);
+
+ // check group is not duplicate
+ if (list_seq_find(groups, cmp_id, &line[1]) != -1) {
+ error = "duplicate group";
+ break;
+ }
+
+ // call handler
+ char *last_group = groups->length > 0 ? groups->items[groups->length - 1] : NULL;
+ error = group_handler(last_group, &line[1], theme);
if (error) {
break;
}
- free(group);
- group = strdup(&line[1]);
+
+ list_add(groups, strdup(&line[1]));
} else { // key-value pair
- if (!group) {
- error = true;
+ if (groups->length == 0) {
+ error = "unexpected content before first header";
break;
}
+
// check well-formed
int eok = 0;
for (; isalnum(line[eok]) || line[eok] == '-'; ++eok) {} // TODO locale?
int i = eok - 1;
while (isspace(line[++i])) {}
if (line[i] != '=') {
- error = true;
+ error = "malformed key-value pair";
break;
}
@@ -269,28 +289,38 @@ static struct icon_theme *read_theme_file(char *basedir, char *theme_name) {
char *value = &line[i];
while (isspace(*++value)) {}
// TODO unescape value
- error = entry_handler(group, line, value, theme);
+
+ error = entry_handler(groups->items[groups->length - 1], line,
+ value, theme);
if (error) {
break;
}
}
}
- if (!error && group) {
- error = group_handler(group, NULL, theme);
+ if (!error) {
+ if (groups->length > 0) {
+ error = group_handler(groups->items[groups->length - 1], NULL, theme);
+ } else {
+ error = "empty file";
+ }
}
- free(group);
- free(full_line);
- fclose(theme_file);
-
- if (!error && validate_icon_theme(theme)) {
+ if (!error) {
theme->dir = strdup(theme_name);
- return theme;
} else {
+ char *last_group = groups->length > 0 ? groups->items[groups->length-1] : "n/a";
+ sway_log(SWAY_DEBUG, "Failed to load theme '%s' - parsing of file "
+ "'%s/%s/index.theme' failed on line %d (group '%s'): %s",
+ theme_name, basedir, theme_name, line_no, last_group, error);
destroy_theme(theme);
- return NULL;
+ theme = NULL;
}
+
+ free(full_line);
+ list_free_items_and_destroy(groups);
+ fclose(theme_file);
+ return theme;
}
static list_t *load_themes_in_dir(char *basedir) {