aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLibravatar Ian Fan <ianfan0@gmail.com>2019-06-15 09:30:22 +0100
committerLibravatar Simon Ser <contact@emersion.fr>2020-03-30 17:31:00 +0200
commit66f0c91bb846472a8fb5edb2dde1b67f3a636ccf (patch)
tree32b27e76117cb686a86133d2b4a75244f71ee158
parentswaybar: fix memory leaks (diff)
downloadsway-66f0c91bb846472a8fb5edb2dde1b67f3a636ccf.tar.gz
sway-66f0c91bb846472a8fb5edb2dde1b67f3a636ccf.tar.zst
sway-66f0c91bb846472a8fb5edb2dde1b67f3a636ccf.zip
tray: better errors when parsing index.theme
-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 @@
14#include "log.h" 14#include "log.h"
15#include "stringop.h" 15#include "stringop.h"
16 16
17static int cmp_id(const void *item, const void *cmp_to) {
18 return strcmp(item, cmp_to);
19}
20
17static bool dir_exists(char *path) { 21static bool dir_exists(char *path) {
18 struct stat sb; 22 struct stat sb;
19 return stat(path, &sb) == 0 && S_ISDIR(sb.st_mode); 23 return stat(path, &sb) == 0 && S_ISDIR(sb.st_mode);
@@ -78,35 +82,37 @@ static void destroy_theme(struct icon_theme *theme) {
78 free(theme); 82 free(theme);
79} 83}
80 84
81static int cmp_group(const void *item, const void *cmp_to) { 85static const char *group_handler(char *old_group, char *new_group,
82 return strcmp(item, cmp_to);
83}
84
85static bool validate_icon_theme(struct icon_theme *theme) {
86 return theme && theme->name && theme->comment && theme->directories;
87}
88
89static bool group_handler(char *old_group, char *new_group,
90 struct icon_theme *theme) { 86 struct icon_theme *theme) {
91 if (!old_group) { // first group must be "Icon Theme" 87 if (!old_group) {
92 if (!new_group) { 88 return new_group && strcmp(new_group, "Icon Theme") == 0 ? NULL :
93 return true; 89 "first group must be 'Icon Theme'";
94 }
95 return strcmp(new_group, "Icon Theme") != 0;
96 } 90 }
97 91
98 if (strcmp(old_group, "Icon Theme") == 0) { 92 if (strcmp(old_group, "Icon Theme") == 0) {
99 if (!validate_icon_theme(theme)) { 93 if (!theme->name) {
100 return true; 94 return "missing required key 'Name'";
95 } else if (!theme->comment) {
96 return "missing required key 'Comment'";
97 } else if (!theme->directories) {
98 return "missing required key 'Directories'";
99 } else {
100 for (char *c = theme->name; *c; ++c) {
101 if (*c == ',' || *c == ' ') {
102 return "malformed theme name";
103 }
104 }
101 } 105 }
102 } else { 106 } else {
103 if (theme->subdirs->length == 0) { // skip 107 if (theme->subdirs->length == 0) { // skip
104 return false; 108 return NULL;
105 } 109 }
106 110
107 struct icon_theme_subdir *subdir = 111 struct icon_theme_subdir *subdir =
108 theme->subdirs->items[theme->subdirs->length - 1]; 112 theme->subdirs->items[theme->subdirs->length - 1];
109 if (!subdir->size) return true; 113 if (!subdir->size) {
114 return "missing required key 'Size'";
115 }
110 116
111 switch (subdir->type) { 117 switch (subdir->type) {
112 case FIXED: subdir->max_size = subdir->min_size = subdir->size; 118 case FIXED: subdir->max_size = subdir->min_size = subdir->size;
@@ -122,20 +128,20 @@ static bool group_handler(char *old_group, char *new_group,
122 } 128 }
123 } 129 }
124 130
125 if (new_group && list_seq_find(theme->directories, cmp_group, new_group) != -1) { 131 if (new_group && list_seq_find(theme->directories, cmp_id, new_group) != -1) {
126 struct icon_theme_subdir *subdir = calloc(1, sizeof(struct icon_theme_subdir)); 132 struct icon_theme_subdir *subdir = calloc(1, sizeof(struct icon_theme_subdir));
127 if (!subdir) { 133 if (!subdir) {
128 return true; 134 return "out of memory";
129 } 135 }
130 subdir->name = strdup(new_group); 136 subdir->name = strdup(new_group);
131 subdir->threshold = 2; 137 subdir->threshold = 2;
132 list_add(theme->subdirs, subdir); 138 list_add(theme->subdirs, subdir);
133 } 139 }
134 140
135 return false; 141 return NULL;
136} 142}
137 143
138static int entry_handler(char *group, char *key, char *value, 144static const char *entry_handler(char *group, char *key, char *value,
139 struct icon_theme *theme) { 145 struct icon_theme *theme) {
140 if (strcmp(group, "Icon Theme") == 0) { 146 if (strcmp(group, "Icon Theme") == 0) {
141 if (strcmp(key, "Name") == 0) { 147 if (strcmp(key, "Name") == 0) {
@@ -149,20 +155,17 @@ static int entry_handler(char *group, char *key, char *value,
149 } // Ignored: ScaledDirectories, Hidden, Example 155 } // Ignored: ScaledDirectories, Hidden, Example
150 } else { 156 } else {
151 if (theme->subdirs->length == 0) { // skip 157 if (theme->subdirs->length == 0) { // skip
152 return false; 158 return NULL;
153 } 159 }
154 160
155 struct icon_theme_subdir *subdir = 161 struct icon_theme_subdir *subdir =
156 theme->subdirs->items[theme->subdirs->length - 1]; 162 theme->subdirs->items[theme->subdirs->length - 1];
157 if (strcmp(subdir->name, group) != 0) { // skip 163 if (strcmp(subdir->name, group) != 0) { // skip
158 return false; 164 return NULL;
159 } 165 }
160 166
161 char *end; 167 if (strcmp(key, "Context") == 0) {
162 int n = strtol(value, &end, 10); 168 return NULL; // ignored, but explicitly handled to not fail parsing
163 if (strcmp(key, "Size") == 0) {
164 subdir->size = n;
165 return *end != '\0';
166 } else if (strcmp(key, "Type") == 0) { 169 } else if (strcmp(key, "Type") == 0) {
167 if (strcmp(value, "Fixed") == 0) { 170 if (strcmp(value, "Fixed") == 0) {
168 subdir->type = FIXED; 171 subdir->type = FIXED;
@@ -171,20 +174,28 @@ static int entry_handler(char *group, char *key, char *value,
171 } else if (strcmp(value, "Threshold") == 0) { 174 } else if (strcmp(value, "Threshold") == 0) {
172 subdir->type = THRESHOLD; 175 subdir->type = THRESHOLD;
173 } else { 176 } else {
174 return true; 177 return "invalid value - expected 'Fixed', 'Scalable' or 'Threshold'";
175 } 178 }
179 return NULL;
180 }
181
182 char *end;
183 int n = strtol(value, &end, 10);
184 if (*end != '\0') {
185 return "invalid value - expected a number";
186 }
187
188 if (strcmp(key, "Size") == 0) {
189 subdir->size = n;
176 } else if (strcmp(key, "MaxSize") == 0) { 190 } else if (strcmp(key, "MaxSize") == 0) {
177 subdir->max_size = n; 191 subdir->max_size = n;
178 return *end != '\0';
179 } else if (strcmp(key, "MinSize") == 0) { 192 } else if (strcmp(key, "MinSize") == 0) {
180 subdir->min_size = n; 193 subdir->min_size = n;
181 return *end != '\0';
182 } else if (strcmp(key, "Threshold") == 0) { 194 } else if (strcmp(key, "Threshold") == 0) {
183 subdir->threshold = n; 195 subdir->threshold = n;
184 return *end != '\0'; 196 } // Ignored: Scale
185 } // Ignored: Scale, Applications
186 } 197 }
187 return false; 198 return NULL;
188} 199}
189 200
190/* 201/*
@@ -215,12 +226,16 @@ static struct icon_theme *read_theme_file(char *basedir, char *theme_name) {
215 } 226 }
216 theme->subdirs = create_list(); 227 theme->subdirs = create_list();
217 228
218 bool error = false; 229 list_t *groups = create_list();
219 char *group = NULL; 230
231 const char *error = NULL;
232 int line_no = 0;
220 char *full_line = NULL; 233 char *full_line = NULL;
221 size_t full_len = 0; 234 size_t full_len = 0;
222 ssize_t nread; 235 ssize_t nread;
223 while ((nread = getline(&full_line, &full_len, theme_file)) != -1) { 236 while ((nread = getline(&full_line, &full_len, theme_file)) != -1) {
237 ++line_no;
238
224 char *line = full_line - 1; 239 char *line = full_line - 1;
225 while (isspace(*++line)) {} // remove leading whitespace 240 while (isspace(*++line)) {} // remove leading whitespace
226 if (!*line || line[0] == '#') continue; // ignore blank lines & comments 241 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) {
231 246
232 if (line[0] == '[') { // group header 247 if (line[0] == '[') { // group header
233 // check well-formed 248 // check well-formed
234 if (line[--len] != ']') {
235 error = true;
236 break;
237 }
238 int i = 1; 249 int i = 1;
239 for (; !iscntrl(line[i]) && line[i] != '[' && line[i] != ']'; ++i) {} 250 for (; !iscntrl(line[i]) && line[i] != '[' && line[i] != ']'; ++i) {}
240 if (i < len) { 251 if (i != --len || line[i] != ']') {
241 error = true; 252 error = "malformed group header";
242 break; 253 break;
243 } 254 }
244 255
245 // call handler
246 line[len] = '\0'; 256 line[len] = '\0';
247 error = group_handler(group, &line[1], theme); 257
258 // check group is not duplicate
259 if (list_seq_find(groups, cmp_id, &line[1]) != -1) {
260 error = "duplicate group";
261 break;
262 }
263
264 // call handler
265 char *last_group = groups->length > 0 ? groups->items[groups->length - 1] : NULL;
266 error = group_handler(last_group, &line[1], theme);
248 if (error) { 267 if (error) {
249 break; 268 break;
250 } 269 }
251 free(group); 270
252 group = strdup(&line[1]); 271 list_add(groups, strdup(&line[1]));
253 } else { // key-value pair 272 } else { // key-value pair
254 if (!group) { 273 if (groups->length == 0) {
255 error = true; 274 error = "unexpected content before first header";
256 break; 275 break;
257 } 276 }
277
258 // check well-formed 278 // check well-formed
259 int eok = 0; 279 int eok = 0;
260 for (; isalnum(line[eok]) || line[eok] == '-'; ++eok) {} // TODO locale? 280 for (; isalnum(line[eok]) || line[eok] == '-'; ++eok) {} // TODO locale?
261 int i = eok - 1; 281 int i = eok - 1;
262 while (isspace(line[++i])) {} 282 while (isspace(line[++i])) {}
263 if (line[i] != '=') { 283 if (line[i] != '=') {
264 error = true; 284 error = "malformed key-value pair";
265 break; 285 break;
266 } 286 }
267 287
@@ -269,28 +289,38 @@ static struct icon_theme *read_theme_file(char *basedir, char *theme_name) {
269 char *value = &line[i]; 289 char *value = &line[i];
270 while (isspace(*++value)) {} 290 while (isspace(*++value)) {}
271 // TODO unescape value 291 // TODO unescape value
272 error = entry_handler(group, line, value, theme); 292
293 error = entry_handler(groups->items[groups->length - 1], line,
294 value, theme);
273 if (error) { 295 if (error) {
274 break; 296 break;
275 } 297 }
276 } 298 }
277 } 299 }
278 300
279 if (!error && group) { 301 if (!error) {
280 error = group_handler(group, NULL, theme); 302 if (groups->length > 0) {
303 error = group_handler(groups->items[groups->length - 1], NULL, theme);
304 } else {
305 error = "empty file";
306 }
281 } 307 }
282 308
283 free(group); 309 if (!error) {
284 free(full_line);
285 fclose(theme_file);
286
287 if (!error && validate_icon_theme(theme)) {
288 theme->dir = strdup(theme_name); 310 theme->dir = strdup(theme_name);
289 return theme;
290 } else { 311 } else {
312 char *last_group = groups->length > 0 ? groups->items[groups->length-1] : "n/a";
313 sway_log(SWAY_DEBUG, "Failed to load theme '%s' - parsing of file "
314 "'%s/%s/index.theme' failed on line %d (group '%s'): %s",
315 theme_name, basedir, theme_name, line_no, last_group, error);
291 destroy_theme(theme); 316 destroy_theme(theme);
292 return NULL; 317 theme = NULL;
293 } 318 }
319
320 free(full_line);
321 list_free_items_and_destroy(groups);
322 fclose(theme_file);
323 return theme;
294} 324}
295 325
296static list_t *load_themes_in_dir(char *basedir) { 326static list_t *load_themes_in_dir(char *basedir) {