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