source: filter.c @ 0f1f388

barnowl_perlaimdebianrelease-1.10release-1.4release-1.5release-1.6release-1.7release-1.8release-1.9
Last change on this file since 0f1f388 was 0f1f388, checked in by Nelson Elhage <nelhage@mit.edu>, 17 years ago
Merging in filter regression tests from my local branch.
  • Property mode set to 100644
File size: 8.5 KB
Line 
1#include <string.h>
2#include "owl.h"
3
4static const char fileIdent[] = "$Id$";
5
6#define OWL_FILTER_MAXRECURSE 20
7
8static owl_filterelement fe_true;
9static owl_filterelement fe_false;
10static owl_filterelement fe_null;
11
12void owl_filterengine_init()
13{
14  owl_filterelement_create_true(&fe_true);
15  owl_filterelement_create_false(&fe_false);
16  owl_filterelement_create_null(&fe_null);
17}
18
19int owl_filter_init_fromstring(owl_filter *f, char *name, char *string)
20{
21  char **argv;
22  int argc, out;
23
24  argv=owl_parseline(string, &argc);
25  out=owl_filter_init(f, name, argc, argv);
26  /* owl_parsefree(argv, argc); */
27  return(out);
28}
29
30int owl_filter_init(owl_filter *f, char *name, int argc, char **argv)
31{
32  f->name=owl_strdup(name);
33  f->polarity=0;
34  f->fgcolor=OWL_COLOR_DEFAULT;
35  f->bgcolor=OWL_COLOR_DEFAULT;
36  f->cachedmsgid=-1;
37
38  /* first take arguments that have to come first */
39  /* set the color */
40  while ( argc>=2 && ( !strcmp(argv[0], "-c") ||
41                       !strcmp(argv[0], "-b") ) ) {
42    if (owl_util_string_to_color(argv[1])==-1) {
43      owl_function_error("The color '%s' is not available, using default.", argv[1]);
44    } else {
45      switch (argv[0][1]) {
46      case 'c':
47        f->fgcolor=owl_util_string_to_color(argv[1]);
48        break;
49      case 'b':
50        f->bgcolor=owl_util_string_to_color(argv[1]);
51        break;
52      }
53    }
54    argc-=2;
55    argv+=2;
56  }
57
58  if(!(f->root = owl_filter_parse_expression(argc, argv, NULL)))
59    return(-1);
60
61  /* Now check for recursion. */
62  if (owl_filter_is_toodeep(f)) {
63    owl_function_error("Filter loop or exceeds recursion depth");
64    owl_filter_free(f);
65    return(-1);
66  }
67
68  return(0);
69}
70
71
72/* A primitive expression is one without any toplevel ``and'' or ``or''s*/
73
74static owl_filterelement * owl_filter_parse_primitive_expression(int argc, char **argv, int *next)
75{
76  if(!argc) return NULL;
77
78  owl_filterelement * fe = owl_malloc(sizeof(owl_filterelement));
79  owl_filterelement *op;
80
81  owl_filterelement_create(fe);
82  int i = 0, skip;
83
84  if(!strcasecmp(argv[i], "(")) {
85    i++;
86    op = owl_filter_parse_expression(argc-i, argv+i, &skip);
87    if(!op) goto err;
88    i += skip;
89    if(strcasecmp(argv[i++], ")")) goto err;
90    owl_filterelement_create_group(fe, op);
91  } else if(!strcasecmp(argv[i], "not")) {
92    i++;
93    op = owl_filter_parse_primitive_expression(argc-i, argv+i, &skip);
94    if(!op) goto err;
95    i += skip;
96    owl_filterelement_create_not(fe, op);
97  } else if(!strcasecmp(argv[i], "true")) {
98    i++;
99    owl_filterelement_create_true(fe);
100  } else if(!strcasecmp(argv[i], "false")) {
101    i++;
102    owl_filterelement_create_false(fe);
103  } else {
104    if(argc == 1) goto err;
105    if(!strcasecmp(*argv, "filter")) {
106      owl_filterelement_create_filter(fe, *(argv+1));
107    } else if(!strcasecmp(*argv, "perl")) {
108      owl_filterelement_create_perl(fe, *(argv+1));
109    } else {
110      owl_filterelement_create_re(fe, *argv, *(argv+1));
111    }
112    i += 2;
113  }
114
115  if(next) {
116    *next = i;
117  } else if(i != argc) {
118    goto err;
119  }
120  return fe;
121err:
122  owl_filterelement_free(fe);
123  owl_free(fe);
124  return NULL;
125}
126
127owl_filterelement * owl_filter_parse_expression(int argc, char **argv, int *next)
128{
129  int i = 0, skip;
130  owl_filterelement * op1 = NULL, * op2 = NULL;
131
132  op1 = owl_filter_parse_primitive_expression(argc-i, argv+i, &skip);
133  i += skip;
134  if(!op1) goto err;
135
136  while(i < argc) {
137    if(strcasecmp(argv[i], "and") &&
138       strcasecmp(argv[i], "or")) break;
139    op2 = owl_filter_parse_primitive_expression(argc-i-1, argv+i+1, &skip);
140    if(!op2) goto err;
141    owl_filterelement * tmp = owl_malloc(sizeof(owl_filterelement));
142    if(!strcasecmp(argv[i], "and")) {
143      owl_filterelement_create_and(tmp, op1, op2);
144    } else {
145      owl_filterelement_create_or(tmp, op1, op2);
146    }
147    op1 = tmp;
148    op2 = NULL;
149    i += skip+1;
150  }
151
152  if(next) {
153    *next = i;
154  } else if(i != argc) {
155    goto err;
156  }
157  return op1;
158err:
159  if(op1) {
160    owl_filterelement_free(op1);
161    owl_free(op1);
162  }
163  return NULL;
164}
165
166char *owl_filter_get_name(owl_filter *f)
167{
168  return(f->name);
169}
170
171void owl_filter_set_polarity_match(owl_filter *f)
172{
173  f->polarity=0;
174}
175
176void owl_filter_set_polarity_unmatch(owl_filter *f)
177{
178  f->polarity=1;
179}
180
181void owl_filter_set_fgcolor(owl_filter *f, int color)
182{
183  f->fgcolor=color;
184}
185
186int owl_filter_get_fgcolor(owl_filter *f)
187{
188  return(f->fgcolor);
189}
190
191void owl_filter_set_bgcolor(owl_filter *f, int color)
192{
193  f->bgcolor=color;
194}
195
196int owl_filter_get_bgcolor(owl_filter *f)
197{
198  return(f->bgcolor);
199}
200
201void owl_filter_set_cachedmsgid(owl_filter *f, int cachedmsgid)
202{
203  f->cachedmsgid=cachedmsgid;
204}
205
206int owl_filter_get_cachedmsgid(owl_filter *f)
207{
208  return(f->cachedmsgid);
209}
210
211/* return 1 if the message matches the given filter, otherwise
212 * return 0.
213 */
214int owl_filter_message_match(owl_filter *f, owl_message *m)
215{
216  if(!f->root) return 0;
217  int ret = owl_filterelement_match(f->root, m);
218  if(f->polarity) ret = !ret;
219  return ret;
220}
221
222
223void owl_filter_print(owl_filter *f, char *out)
224{
225  strcpy(out, owl_filter_get_name(f));
226  strcat(out, ": ");
227
228  if (f->fgcolor!=OWL_COLOR_DEFAULT) {
229    strcat(out, "-c ");
230    strcat(out, owl_util_color_to_string(f->fgcolor));
231    strcat(out, " ");
232  }
233  if (f->bgcolor!=OWL_COLOR_DEFAULT) {
234    strcat(out, "-b ");
235    strcat(out, owl_util_color_to_string(f->bgcolor));
236    strcat(out, " ");
237  }
238  if(!f->root) return;
239  owl_filterelement_print(f->root, out);
240  strcat(out, "\n");
241}
242
243/* Return 1 if the filters 'a' and 'b' are equivalent, 0 otherwise */
244int owl_filter_equiv(owl_filter *a, owl_filter *b)
245{
246  char buff[LINE], buff2[LINE];
247
248  owl_filter_print(a, buff);
249  owl_filter_print(b, buff2);
250
251  if (!strcmp(buff, buff2)) return(1);
252  return(0);
253}
254
255
256int owl_filter_is_toodeep(owl_filter *f)
257{
258  return owl_filterelement_is_toodeep(f, f->root);
259}
260
261void owl_filter_free(owl_filter *f)
262{
263  if(f->root) {
264    owl_filterelement_free(f->root);
265    owl_free(f->root);
266  }
267  if (f->name) owl_free(f->name);
268}
269
270/**************************************************************************/
271/************************* REGRESSION TESTS *******************************/
272/**************************************************************************/
273
274#ifdef OWL_INCLUDE_REG_TESTS
275
276int owl_filter_test_string(char * filt, owl_message *m, int shouldmatch) /* noproto */ {
277  owl_filter f;
278  int ok;
279  int failed = 0;
280  if(owl_filter_init_fromstring(&f, "test-filter", filt)) {
281    printf("\tFAIL: parse %s\n", filt);
282    failed = 1;
283    goto out;
284  }
285  ok = owl_filter_message_match(&f, m);
286  if((shouldmatch && !ok) || (!shouldmatch && ok)) {
287    printf("\tFAIL: match %s (got %d, expected %d)\n", filt, ok, shouldmatch);
288    failed = 1;
289  }
290 out:
291  owl_filter_free(&f);
292  if(!failed) {
293    printf("\tok  : %s %s\n", shouldmatch ? "matches" : "doesn't match", filt);
294  }
295  return failed;
296}
297
298
299#include "test.h"
300
301
302int owl_filter_regtest(void) {
303  owl_list_create(&(g.filterlist));
304  int numfailed=0;
305  owl_message m;
306  owl_message_init(&m);
307  owl_message_set_type_zephyr(&m);
308  owl_message_set_direction_in(&m);
309  owl_message_set_class(&m, "owl");
310  owl_message_set_instance(&m, "tester");
311  owl_message_set_sender(&m, "owl-user");
312  owl_message_set_recipient(&m, "joe");
313  owl_message_set_attribute(&m, "foo", "bar");
314
315#define TEST_FILTER(f, e) numfailed += owl_filter_test_string(f, &m, e)
316
317
318  TEST_FILTER("true", 1);
319  TEST_FILTER("false", 0);
320  TEST_FILTER("( true )", 1);
321  TEST_FILTER("not false", 1);
322  TEST_FILTER("( true ) or ( false )", 1);
323  TEST_FILTER("true and false", 0);
324  TEST_FILTER("( true or true ) or ( ( false ) )", 1);
325
326  TEST_FILTER("class owl", 1);
327  TEST_FILTER("class ^owl$", 1);
328  TEST_FILTER("instance test", 1);
329  TEST_FILTER("instance ^test$", 0);
330  TEST_FILTER("instance ^tester$", 1);
331
332  TEST_FILTER("foo bar", 1);
333  TEST_FILTER("class owl and instance tester", 1);
334  TEST_FILTER("type ^zephyr$ and direction ^in$ and ( class ^owl$ or instance ^owl$ )", 1);
335
336  // Order of operations and precedence
337  TEST_FILTER("not true or false", 0);
338  TEST_FILTER("true or true and false", 0);
339  TEST_FILTER("true and true and false or true", 1);
340  TEST_FILTER("false and false or true", 1);
341  TEST_FILTER("true and false or false", 0);
342
343  owl_filter f1, f2, f3, f4;
344
345  owl_filter_init_fromstring(&f1, "f1", "class owl");
346  owl_global_add_filter(&g, &f1);
347  TEST_FILTER("filter f1", 1);
348
349  // Test recursion prevention
350  FAIL_UNLESS("self reference", owl_filter_init_fromstring(&f2, "test", "filter test"));
351
352  // mutual recursion
353  owl_filter_init_fromstring(&f3, "f3", "filter f4");
354  owl_global_add_filter(&g, &f3);
355  FAIL_UNLESS("mutual recursion",   owl_filter_init_fromstring(&f4, "f4", "filter f3"));
356
357  return 0;
358}
359
360
361#endif /* OWL_INCLUDE_REG_TESTS */
Note: See TracBrowser for help on using the repository browser.