Fix buffer overflow when completing variable value
[cmus.git] / filters.c
blobfc513ce904067dec924744c99c32308eebd16ea6
1 /*
2 * Copyright 2005 Timo Hirvonen
3 */
5 #include <filters.h>
6 #include <expr.h>
7 #include <window.h>
8 #include <search.h>
9 #include <uchar.h>
10 #include <lib.h>
11 #include <misc.h>
12 #include <file.h>
13 #include <ui_curses.h>
14 #include <xmalloc.h>
16 #include <ctype.h>
18 struct window *filters_win;
19 struct searchable *filters_searchable;
20 LIST_HEAD(filters_head);
22 static const char *recursive_filter;
24 static inline void filter_entry_to_iter(struct filter_entry *e, struct iter *iter)
26 iter->data0 = &filters_head;
27 iter->data1 = e;
28 iter->data2 = NULL;
31 static GENERIC_ITER_PREV(filters_get_prev, struct filter_entry, node)
32 static GENERIC_ITER_NEXT(filters_get_next, struct filter_entry, node)
34 static int filters_search_get_current(void *data, struct iter *iter)
36 return window_get_sel(filters_win, iter);
39 static int filters_search_matches(void *data, struct iter *iter, const char *text)
41 char **words = get_words(text);
42 int matched = 0;
44 if (words[0] != NULL) {
45 struct filter_entry *e;
46 int i;
48 e = iter_to_filter_entry(iter);
49 for (i = 0; ; i++) {
50 if (words[i] == NULL) {
51 window_set_sel(filters_win, iter);
52 matched = 1;
53 break;
55 if (u_strcasestr(e->name, words[i]) == NULL)
56 break;
59 free_str_array(words);
60 return matched;
63 static const struct searchable_ops filters_search_ops = {
64 .get_prev = filters_get_prev,
65 .get_next = filters_get_next,
66 .get_current = filters_search_get_current,
67 .matches = filters_search_matches
70 static void free_filter(struct filter_entry *e)
72 free(e->name);
73 free(e->filter);
74 free(e);
77 static struct filter_entry *find_filter(const char *name)
79 struct filter_entry *e;
81 list_for_each_entry(e, &filters_head, node) {
82 if (strcmp(e->name, name) == 0)
83 return e;
85 return NULL;
88 static const char *get_filter(const char *name)
90 struct filter_entry *e = find_filter(name);
92 if (e) {
93 if (e->visited) {
94 recursive_filter = e->name;
95 return NULL;
97 e->visited = 1;
98 return e->filter;
100 return NULL;
103 void filters_activate(void)
105 struct filter_entry *f;
106 struct expr *e, *expr = NULL;
108 /* mark visited and AND together all selected filters
109 * mark any other filters unvisited */
110 list_for_each_entry(f, &filters_head, node) {
111 f->visited = 0;
112 if (!f->selected)
113 continue;
115 f->visited = 1;
116 e = expr_parse(f->filter);
117 if (e == NULL) {
118 error_msg("error parsing filter %s: %s", f->name, expr_error());
119 if (expr)
120 expr_free(expr);
121 return;
124 if (expr == NULL) {
125 expr = e;
126 } else {
127 struct expr *and = xnew(struct expr, 1);
129 and->type = EXPR_AND;
130 and->key = NULL;
131 and->left = expr;
132 and->right = e;
133 expr->parent = and;
134 e->parent = and;
135 expr = and;
139 recursive_filter = NULL;
140 if (expr && expr_check_leaves(&expr, get_filter)) {
141 if (recursive_filter) {
142 error_msg("recursion detected in filter %s", recursive_filter);
143 } else {
144 error_msg("error parsing filter: %s", expr_error());
146 expr_free(expr);
147 return;
150 /* update active flag */
151 list_for_each_entry(f, &filters_head, node) {
152 f->active = 0;
153 if (f->selected)
154 f->active = 1;
156 lib_set_filter(expr);
157 filters_win->changed = 1;
160 static int for_each_name(const char *str, int (*cb)(const char *name))
162 char buf[64];
163 int s, e, len;
165 e = 0;
166 do {
167 s = e;
168 while (str[s] == ' ')
169 s++;
170 e = s;
171 while (str[e] && str[e] != ' ')
172 e++;
174 len = e - s;
175 if (len == 0)
176 return 0;
177 if (len >= sizeof(buf)) {
178 error_msg("filter name too long");
179 return -1;
182 memcpy(buf, str + s, len);
183 buf[len] = 0;
185 if (cb(buf))
186 return -1;
187 } while (1);
190 static int ensure_filter_name(const char *name)
192 if (find_filter(name) == NULL) {
193 error_msg("no such filter %s", name);
194 return -1;
196 return 0;
199 static int select_filter(const char *name)
201 struct filter_entry *e = find_filter(name);
203 e->selected = 1;
204 return 0;
207 void filters_activate_names(const char *str)
209 struct filter_entry *f;
211 /* first validate all filter names */
212 if (str && for_each_name(str, ensure_filter_name))
213 return;
215 /* mark all filters unselected */
216 list_for_each_entry(f, &filters_head, node)
217 f->selected = 0;
219 /* select the filters */
220 if (str)
221 for_each_name(str, select_filter);
223 /* activate selected */
224 filters_activate();
227 void filters_toggle_filter(void)
229 struct iter iter;
231 if (window_get_sel(filters_win, &iter)) {
232 struct filter_entry *e;
234 e = iter_to_filter_entry(&iter);
235 e->selected ^= 1;
236 filters_win->changed = 1;
240 void filters_delete_filter(void)
242 struct iter iter;
244 if (window_get_sel(filters_win, &iter)) {
245 struct filter_entry *e;
247 e = iter_to_filter_entry(&iter);
248 if (yes_no_query("Delete filter '%s'? [y/N]", e->name)) {
249 window_row_vanishes(filters_win, &iter);
250 list_del(&e->node);
251 free_filter(e);
256 static int validate_filter_name(const char *name)
258 int i;
260 for (i = 0; name[i]; i++) {
261 if (isalnum(name[i]))
262 continue;
263 if (name[i] == '_' || name[i] == '-')
264 continue;
265 return 0;
267 return i != 0;
270 static void do_filters_set_filter(const char *keyval, int active)
272 const char *eq = strchr(keyval, '=');
273 char *key, *val;
274 struct expr *expr;
275 struct filter_entry *new;
276 struct list_head *item;
278 if (eq == NULL) {
279 if (ui_initialized)
280 error_msg("invalid argument ('key=value' expected)");
281 return;
283 key = xstrndup(keyval, eq - keyval);
284 val = xstrdup(eq + 1);
285 if (!validate_filter_name(key)) {
286 if (ui_initialized)
287 error_msg("invalid filter name (can only contain 'a-zA-Z0-9_-' characters)");
288 free(key);
289 free(val);
290 return;
292 expr = expr_parse(val);
293 if (expr == NULL) {
294 if (ui_initialized)
295 error_msg("error parsing filter %s: %s", val, expr_error());
296 free(key);
297 free(val);
298 return;
300 expr_free(expr);
302 new = xnew(struct filter_entry, 1);
303 new->name = key;
304 new->filter = val;
305 new->active = active;
306 new->selected = active;
308 /* add or replace filter */
309 list_for_each(item, &filters_head) {
310 struct filter_entry *e = container_of(item, struct filter_entry, node);
311 int res = strcmp(key, e->name);
313 if (res < 0)
314 break;
315 if (res == 0) {
316 /* replace */
317 struct iter iter;
319 if (ui_initialized) {
320 filter_entry_to_iter(e, &iter);
321 window_row_vanishes(filters_win, &iter);
323 item = item->next;
324 list_del(&e->node);
325 free_filter(e);
326 break;
329 /* add before item */
330 list_add_tail(&new->node, item);
331 if (ui_initialized)
332 window_changed(filters_win);
335 void filters_init(void)
337 struct iter iter;
339 filters_win = window_new(filters_get_prev, filters_get_next);
340 window_set_contents(filters_win, &filters_head);
341 window_changed(filters_win);
343 iter.data0 = &filters_head;
344 iter.data1 = NULL;
345 iter.data2 = NULL;
346 filters_searchable = searchable_new(NULL, &iter, &filters_search_ops);
349 void filters_exit(void)
351 searchable_free(filters_searchable);
352 window_free(filters_win);
355 void filters_set_filter(const char *keyval)
357 do_filters_set_filter(keyval, 0);
360 struct expr *parse_filter(const char *val)
362 struct expr *e = NULL;
363 struct filter_entry *f;
365 if (val) {
366 e = expr_parse(val);
367 if (e == NULL) {
368 error_msg("error parsing filter %s: %s", val, expr_error());
369 return NULL;
373 /* mark all unvisited so that we can check recursion */
374 list_for_each_entry(f, &filters_head, node)
375 f->visited = 0;
377 recursive_filter = NULL;
378 if (e && expr_check_leaves(&e, get_filter)) {
379 if (recursive_filter) {
380 error_msg("recursion detected in filter %s", recursive_filter);
381 } else {
382 error_msg("error parsing filter: %s", expr_error());
384 expr_free(e);
385 return NULL;
387 return e;
390 void filters_set_anonymous(const char *val)
392 struct filter_entry *f;
393 struct expr *e = NULL;
395 if (val) {
396 e = parse_filter(val);
397 if (e == NULL)
398 return;
401 /* deactive all filters */
402 list_for_each_entry(f, &filters_head, node)
403 f->active = 0;
405 lib_set_filter(e);
407 filters_win->changed = 1;