1 /*
2 * Copyright 2008-2013 Various Authors
3 * Copyright 2005 Timo Hirvonen
4 *
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License as
7 * published by the Free Software Foundation; either version 2 of the
8 * License, or (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, see <http://www.gnu.org/licenses/>.
17 */
18
19 #include "filters.h"
20 #include "cmdline.h"
21 #include "expr.h"
22 #include "window.h"
23 #include "search.h"
24 #include "uchar.h"
25 #include "lib.h"
26 #include "misc.h"
27 #include "file.h"
28 #include "ui_curses.h"
29 #include "xmalloc.h"
30
31 #include <stdio.h>
32 #include <ctype.h>
33
34 struct window *filters_win;
35 struct searchable *filters_searchable;
36 LIST_HEAD(filters_head);
37
38 static const char *recursive_filter;
39
filter_entry_to_iter(struct filter_entry * e,struct iter * iter)40 static inline void filter_entry_to_iter(struct filter_entry *e, struct iter *iter)
41 {
42 iter->data0 = &filters_head;
43 iter->data1 = e;
44 iter->data2 = NULL;
45 }
46
GENERIC_ITER_PREV(filters_get_prev,struct filter_entry,node)47 static GENERIC_ITER_PREV(filters_get_prev, struct filter_entry, node)
48 static GENERIC_ITER_NEXT(filters_get_next, struct filter_entry, node)
49
50 static int filters_search_get_current(void *data, struct iter *iter)
51 {
52 return window_get_sel(filters_win, iter);
53 }
54
filters_search_matches(void * data,struct iter * iter,const char * text)55 static int filters_search_matches(void *data, struct iter *iter, const char *text)
56 {
57 char **words = get_words(text);
58 int matched = 0;
59
60 if (words[0] != NULL) {
61 struct filter_entry *e;
62 int i;
63
64 e = iter_to_filter_entry(iter);
65 for (i = 0; ; i++) {
66 if (words[i] == NULL) {
67 window_set_sel(filters_win, iter);
68 matched = 1;
69 break;
70 }
71 if (u_strcasestr(e->name, words[i]) == NULL)
72 break;
73 }
74 }
75 free_str_array(words);
76 return matched;
77 }
78
79 static const struct searchable_ops filters_search_ops = {
80 .get_prev = filters_get_prev,
81 .get_next = filters_get_next,
82 .get_current = filters_search_get_current,
83 .matches = filters_search_matches
84 };
85
free_filter(struct filter_entry * e)86 static void free_filter(struct filter_entry *e)
87 {
88 free(e->name);
89 free(e->filter);
90 free(e);
91 }
92
find_filter(const char * name)93 static struct filter_entry *find_filter(const char *name)
94 {
95 struct filter_entry *e;
96
97 list_for_each_entry(e, &filters_head, node) {
98 if (strcmp(e->name, name) == 0)
99 return e;
100 }
101 return NULL;
102 }
103
get_filter(const char * name)104 static const char *get_filter(const char *name)
105 {
106 struct filter_entry *e = find_filter(name);
107
108 if (e) {
109 if (e->visited) {
110 recursive_filter = e->name;
111 return NULL;
112 }
113 e->visited = 1;
114 return e->filter;
115 }
116 return NULL;
117 }
118
edit_sel_filter(void)119 static void edit_sel_filter(void)
120 {
121 struct iter sel;
122 struct filter_entry *e;
123 char buf[512];
124
125 if (!window_get_sel(filters_win, &sel))
126 return;
127
128 e = iter_to_filter_entry(&sel);
129 snprintf(buf, sizeof(buf), "fset %s=%s", e->name, e->filter);
130 cmdline_set_text(buf);
131 enter_command_mode();
132 }
133
filters_activate(int win_activate)134 void filters_activate(int win_activate)
135 {
136 struct filter_entry *f;
137 struct expr *e, *expr = NULL;
138 int unchanged = 1;
139
140 /* if no pending selection is to apply, edit currently select filter */
141 list_for_each_entry(f, &filters_head, node) {
142 if (f->act_stat != f->sel_stat)
143 unchanged = 0;
144 }
145
146 if (unchanged) {
147 if (win_activate)
148 edit_sel_filter();
149 else
150 return;
151 }
152
153 /* mark visited and AND together all selected filters
154 * mark any other filters unvisited */
155 list_for_each_entry(f, &filters_head, node) {
156 f->visited = 0;
157 if (f->sel_stat == FS_IGNORE)
158 continue;
159
160 f->visited = 1;
161 e = expr_parse(f->filter);
162 if (e == NULL) {
163 error_msg("error parsing filter %s: %s", f->name, expr_error());
164 if (expr)
165 expr_free(expr);
166 return;
167 }
168
169 if (f->sel_stat == FS_NO) {
170 /* add ! */
171 struct expr *not = xnew(struct expr, 1);
172
173 not->type = EXPR_NOT;
174 not->key = NULL;
175 not->left = e;
176 not->right = NULL;
177 e = not;
178 }
179 if (expr == NULL) {
180 expr = e;
181 } else {
182 struct expr *and = xnew(struct expr, 1);
183
184 and->type = EXPR_AND;
185 and->key = NULL;
186 and->left = expr;
187 and->right = e;
188 expr->parent = and;
189 e->parent = and;
190 expr = and;
191 }
192 }
193
194 recursive_filter = NULL;
195 if (expr && expr_check_leaves(&expr, get_filter)) {
196 if (recursive_filter) {
197 error_msg("recursion detected in filter %s", recursive_filter);
198 } else {
199 error_msg("error parsing filter: %s", expr_error());
200 }
201 expr_free(expr);
202 return;
203 }
204
205 /* update active flag */
206 list_for_each_entry(f, &filters_head, node) {
207 f->act_stat = f->sel_stat;
208 }
209 lib_set_filter(expr);
210 filters_win->changed = 1;
211 }
212
for_each_name(const char * str,int (* cb)(const char * name,int sel_stat))213 static int for_each_name(const char *str, int (*cb)(const char *name, int sel_stat))
214 {
215 char buf[64];
216 int s, e, len;
217
218 e = 0;
219 do {
220 int sel_stat = FS_YES;
221
222 s = e;
223 while (str[s] == ' ')
224 s++;
225 if (str[s] == '!') {
226 sel_stat = FS_NO;
227 s++;
228 }
229 e = s;
230 while (str[e] && str[e] != ' ')
231 e++;
232
233 len = e - s;
234 if (len == 0)
235 return 0;
236 if (len >= sizeof(buf)) {
237 error_msg("filter name too long");
238 return -1;
239 }
240
241 memcpy(buf, str + s, len);
242 buf[len] = 0;
243
244 if (cb(buf, sel_stat))
245 return -1;
246 } while (1);
247 }
248
ensure_filter_name(const char * name,int sel_stat)249 static int ensure_filter_name(const char *name, int sel_stat)
250 {
251 if (find_filter(name) == NULL) {
252 error_msg("no such filter %s", name);
253 return -1;
254 }
255 return 0;
256 }
257
select_filter(const char * name,int sel_stat)258 static int select_filter(const char *name, int sel_stat)
259 {
260 struct filter_entry *e = find_filter(name);
261
262 e->sel_stat = sel_stat;
263 return 0;
264 }
265
filters_activate_names(const char * str)266 void filters_activate_names(const char *str)
267 {
268 struct filter_entry *f;
269
270 /* first validate all filter names */
271 if (str && for_each_name(str, ensure_filter_name))
272 return;
273
274 /* mark all filters unselected */
275 list_for_each_entry(f, &filters_head, node)
276 f->sel_stat = FS_IGNORE;
277
278 /* select the filters */
279 if (str)
280 for_each_name(str, select_filter);
281
282 /* activate selected */
283 filters_activate(0);
284 }
285
filters_toggle_filter(void)286 void filters_toggle_filter(void)
287 {
288 struct iter iter;
289
290 if (window_get_sel(filters_win, &iter)) {
291 struct filter_entry *e;
292
293 e = iter_to_filter_entry(&iter);
294 e->sel_stat = (e->sel_stat + 1) % 3;
295 filters_win->changed = 1;
296 }
297 }
298
filters_delete_filter(void)299 void filters_delete_filter(void)
300 {
301 struct iter iter;
302
303 if (window_get_sel(filters_win, &iter)) {
304 struct filter_entry *e;
305
306 e = iter_to_filter_entry(&iter);
307 if (yes_no_query("Delete filter '%s'? [y/N]", e->name) == UI_QUERY_ANSWER_YES) {
308 window_row_vanishes(filters_win, &iter);
309 list_del(&e->node);
310 free_filter(e);
311 }
312 }
313 }
314
validate_filter_name(const char * name)315 static int validate_filter_name(const char *name)
316 {
317 int i;
318
319 for (i = 0; name[i]; i++) {
320 if (isalnum((unsigned char)name[i]))
321 continue;
322 if (name[i] == '_' || name[i] == '-')
323 continue;
324 return 0;
325 }
326 return i != 0;
327 }
328
do_filters_set_filter(const char * keyval)329 static void do_filters_set_filter(const char *keyval)
330 {
331 const char *eq = strchr(keyval, '=');
332 char *key, *val;
333 struct expr *expr;
334 struct filter_entry *new;
335 struct list_head *item;
336
337 if (eq == NULL) {
338 if (ui_initialized)
339 error_msg("invalid argument ('key=value' expected)");
340 return;
341 }
342 key = xstrndup(keyval, eq - keyval);
343 val = xstrdup(eq + 1);
344 if (!validate_filter_name(key)) {
345 if (ui_initialized)
346 error_msg("invalid filter name (can only contain 'a-zA-Z0-9_-' characters)");
347 free(key);
348 free(val);
349 return;
350 }
351 expr = expr_parse(val);
352 if (expr == NULL) {
353 if (ui_initialized)
354 error_msg("error parsing filter %s: %s", val, expr_error());
355 free(key);
356 free(val);
357 return;
358 }
359 expr_free(expr);
360
361 new = xnew(struct filter_entry, 1);
362 new->name = key;
363 new->filter = val;
364 new->act_stat = FS_IGNORE;
365 new->sel_stat = FS_IGNORE;
366
367 /* add or replace filter */
368 list_for_each(item, &filters_head) {
369 struct filter_entry *e = container_of(item, struct filter_entry, node);
370 int res = strcmp(key, e->name);
371
372 if (res < 0)
373 break;
374 if (res == 0) {
375 /* replace */
376 struct iter iter;
377
378 new->sel_stat = e->sel_stat;
379 if (ui_initialized) {
380 filter_entry_to_iter(e, &iter);
381 window_row_vanishes(filters_win, &iter);
382 }
383 item = item->next;
384 list_del(&e->node);
385 free_filter(e);
386 break;
387 }
388 }
389 /* add before item */
390 list_add_tail(&new->node, item);
391 if (ui_initialized)
392 window_changed(filters_win);
393 }
394
filters_init(void)395 void filters_init(void)
396 {
397 struct iter iter;
398
399 filters_win = window_new(filters_get_prev, filters_get_next);
400 window_set_contents(filters_win, &filters_head);
401 window_changed(filters_win);
402
403 iter.data0 = &filters_head;
404 iter.data1 = NULL;
405 iter.data2 = NULL;
406 filters_searchable = searchable_new(NULL, &iter, &filters_search_ops);
407 }
408
filters_exit(void)409 void filters_exit(void)
410 {
411 searchable_free(filters_searchable);
412 window_free(filters_win);
413 }
414
filters_set_filter(const char * keyval)415 void filters_set_filter(const char *keyval)
416 {
417 do_filters_set_filter(keyval);
418 }
419
parse_filter(const char * val)420 struct expr *parse_filter(const char *val)
421 {
422 struct expr *e = NULL;
423 struct filter_entry *f;
424
425 if (val) {
426 e = expr_parse(val);
427 if (e == NULL) {
428 error_msg("error parsing filter %s: %s", val, expr_error());
429 return NULL;
430 }
431 }
432
433 /* mark all unvisited so that we can check recursion */
434 list_for_each_entry(f, &filters_head, node)
435 f->visited = 0;
436
437 recursive_filter = NULL;
438 if (e && expr_check_leaves(&e, get_filter)) {
439 if (recursive_filter) {
440 error_msg("recursion detected in filter %s", recursive_filter);
441 } else {
442 error_msg("error parsing filter: %s", expr_error());
443 }
444 expr_free(e);
445 return NULL;
446 }
447 return e;
448 }
449
filters_set_anonymous(const char * val)450 void filters_set_anonymous(const char *val)
451 {
452 struct filter_entry *f;
453 struct expr *e = NULL;
454
455 if (val) {
456 e = parse_filter(val);
457 if (e == NULL)
458 return;
459 }
460
461 /* deactive all filters */
462 list_for_each_entry(f, &filters_head, node)
463 f->act_stat = FS_IGNORE;
464
465 lib_set_filter(e);
466
467 filters_win->changed = 1;
468 }
469
filters_set_live(const char * val)470 void filters_set_live(const char *val)
471 {
472 lib_set_live_filter(val);
473 update_filterline();
474 }
475