0.9.4claws65
[claws.git] / src / matcher_parser_parse.y
1
2 %{
3
4 /*
5  * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
6  * Copyright (c) 2001-2002 by Hiroyuki Yamamoto & The Sylpheed Claws Team.
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
21  */
22
23 #include "defs.h"
24
25 #include <glib.h>
26
27 #include "intl.h"
28 #include "utils.h"
29 #include "filtering.h"
30 #include "scoring.h"
31 #include "matcher.h"
32 #include "matcher_parser.h"
33 #include "matcher_parser_lex.h"
34 #include "procmsg.h"
35
36 #define MAX_COLORLABELS (MSG_CLABEL_7 - MSG_CLABEL_NONE)
37
38 static gint error = 0;
39 static gint bool_op = 0;
40 static gint match_type = 0;
41 static gchar *header = NULL;
42
43 static MatcherProp *prop;
44
45 static GSList *matchers_list = NULL;
46
47 static MatcherList *cond;
48 static gint score = 0;
49 static GSList *action_list = NULL;
50 static FilteringAction *action = NULL;
51
52 static FilteringProp *filtering;
53 static ScoringProp *scoring = NULL;
54
55 static GSList **prefs_scoring = NULL;
56 static GSList **prefs_filtering = NULL;
57
58 #if 0
59 static int matcher_parser_dialog = 0;
60 #endif
61
62 enum {
63         MATCHER_PARSE_NONE,
64         MATCHER_PARSE_NO_EOL,
65         MATCHER_PARSE_CONDITION,
66         MATCHER_PARSE_FILTERING_OR_SCORING,
67 };
68
69 static int matcher_parse_op = MATCHER_PARSE_NONE;
70
71
72 /* ******************************************************************** */
73
74 void matcher_parser_start_parsing(FILE *f)
75 {
76         matcher_parserrestart(f);
77         matcher_parserparse();
78 }
79  
80 FilteringProp *matcher_parser_get_filtering(gchar *str)
81 {
82         void *bufstate;
83
84         /* bad coding to enable the sub-grammar matching
85            in yacc */
86         matcher_parserlineno = 1;
87         matcher_parse_op = MATCHER_PARSE_NO_EOL;
88         matcher_parserrestart(NULL);
89         matcher_parser_init();
90         bufstate = matcher_parser_scan_string(str);
91         matcher_parser_switch_to_buffer(bufstate);
92         if (matcher_parserparse() != 0)
93                 filtering = NULL;
94         matcher_parse_op = MATCHER_PARSE_NONE;
95         matcher_parser_delete_buffer(bufstate);
96         return filtering;
97 }
98
99 ScoringProp *matcher_parser_get_scoring(gchar *str)
100 {
101         void *bufstate;
102
103         /* bad coding to enable the sub-grammar matching
104            in yacc */
105         matcher_parserlineno = 1;
106         matcher_parse_op = MATCHER_PARSE_NO_EOL;
107         matcher_parserrestart(NULL);
108         matcher_parser_init();
109         bufstate = matcher_parser_scan_string(str);
110         matcher_parser_switch_to_buffer(bufstate);
111         if (matcher_parserparse() != 0)
112                 scoring = NULL;
113         matcher_parse_op = MATCHER_PARSE_NONE;
114         matcher_parser_delete_buffer(bufstate);
115         return scoring;
116 }
117
118 static gboolean check_quote_symetry(gchar *str)
119 {
120         gchar *walk;
121         int ret = 0;
122         
123         if (str == NULL)
124                 return TRUE; /* heh, that's symetric */
125         if (*str == '\0')
126                 return TRUE;
127         for (walk = str; *walk; walk++) {
128                 if (*walk == '\"') {
129                         if (walk == str         /* first char */
130                         || *(walk - 1) != '\\') /* not escaped */
131                                 ret ++;
132                 }
133         }
134         return !(ret % 2);
135 }
136
137 MatcherList *matcher_parser_get_cond(gchar *str)
138 {
139         void *bufstate;
140
141         if (!check_quote_symetry(str)) {
142                 cond = NULL;
143                 return cond;
144         }
145         
146         /* bad coding to enable the sub-grammar matching
147            in yacc */
148         matcher_parserlineno = 1;
149         matcher_parse_op = MATCHER_PARSE_CONDITION;
150         matcher_parserrestart(NULL);
151         matcher_parser_init();
152         bufstate = matcher_parser_scan_string(str);
153         matcher_parserparse();
154         matcher_parse_op = MATCHER_PARSE_NONE;
155         matcher_parser_delete_buffer(bufstate);
156         return cond;
157 }
158
159 GSList *matcher_parser_get_action_list(gchar *str)
160 {
161         void *bufstate;
162
163         if (!check_quote_symetry(str)) {
164                 action_list = NULL;
165                 return action_list;
166         }
167         
168         /* bad coding to enable the sub-grammar matching
169            in yacc */
170         matcher_parserlineno = 1;
171         matcher_parse_op = MATCHER_PARSE_FILTERING_OR_SCORING;
172         matcher_parserrestart(NULL);
173         matcher_parser_init();
174         bufstate = matcher_parser_scan_string(str);
175         matcher_parserparse();
176         matcher_parse_op = MATCHER_PARSE_NONE;
177         matcher_parser_delete_buffer(bufstate);
178         return action_list;
179 }
180
181 MatcherProp *matcher_parser_get_prop(gchar *str)
182 {
183         MatcherList *list;
184         MatcherProp *prop;
185
186         matcher_parserlineno = 1;
187         list = matcher_parser_get_cond(str);
188         if (list == NULL)
189                 return NULL;
190
191         if (list->matchers == NULL)
192                 return NULL;
193
194         if (list->matchers->next != NULL)
195                 return NULL;
196
197         prop = list->matchers->data;
198
199         g_slist_free(list->matchers);
200         g_free(list);
201
202         return prop;
203 }
204
205 void matcher_parsererror(char *str)
206 {
207         GSList *l;
208
209         if (matchers_list) {
210                 for (l = matchers_list; l != NULL; l = g_slist_next(l)) {
211                         matcherprop_free((MatcherProp *)
212                                          l->data);
213                         l->data = NULL;
214                 }
215                 g_slist_free(matchers_list);
216                 matchers_list = NULL;
217         }
218         cond = NULL;
219         g_warning("scoring / filtering parsing: %i: %s\n",
220                   matcher_parserlineno, str);
221         error = 1;
222 }
223
224 int matcher_parserwrap(void)
225 {
226         return 1;
227 }
228 %}
229
230 %union {
231         char *str;
232         int value;
233 }
234
235 %token MATCHER_ALL MATCHER_UNREAD  MATCHER_NOT_UNREAD 
236 %token MATCHER_NEW  MATCHER_NOT_NEW  MATCHER_MARKED
237 %token MATCHER_NOT_MARKED  MATCHER_DELETED  MATCHER_NOT_DELETED
238 %token MATCHER_REPLIED  MATCHER_NOT_REPLIED  MATCHER_FORWARDED
239 %token MATCHER_NOT_FORWARDED  MATCHER_SUBJECT  MATCHER_NOT_SUBJECT
240 %token MATCHER_FROM  MATCHER_NOT_FROM  MATCHER_TO  MATCHER_NOT_TO
241 %token MATCHER_CC  MATCHER_NOT_CC  MATCHER_TO_OR_CC  MATCHER_NOT_TO_AND_NOT_CC
242 %token MATCHER_AGE_GREATER  MATCHER_AGE_LOWER  MATCHER_NEWSGROUPS
243 %token MATCHER_NOT_NEWSGROUPS  MATCHER_INREPLYTO  MATCHER_NOT_INREPLYTO
244 %token MATCHER_REFERENCES  MATCHER_NOT_REFERENCES  MATCHER_SCORE_GREATER
245 %token MATCHER_SCORE_LOWER  MATCHER_HEADER  MATCHER_NOT_HEADER
246 %token MATCHER_HEADERS_PART  MATCHER_NOT_HEADERS_PART  MATCHER_MESSAGE
247 %token MATCHER_NOT_MESSAGE  MATCHER_BODY_PART  MATCHER_NOT_BODY_PART
248 %token MATCHER_EXECUTE  MATCHER_NOT_EXECUTE  MATCHER_MATCHCASE  MATCHER_MATCH
249 %token MATCHER_REGEXPCASE  MATCHER_REGEXP  MATCHER_SCORE  MATCHER_MOVE
250 %token MATCHER_COPY  MATCHER_DELETE  MATCHER_MARK  MATCHER_UNMARK MATCHER_LOCK MATCHER_UNLOCK
251 %token MATCHER_MARK_AS_READ  MATCHER_MARK_AS_UNREAD  MATCHER_FORWARD
252 %token MATCHER_FORWARD_AS_ATTACHMENT  MATCHER_EOL  MATCHER_STRING  
253 %token MATCHER_OR MATCHER_AND  
254 %token MATCHER_COLOR MATCHER_SCORE_EQUAL MATCHER_REDIRECT 
255 %token MATCHER_SIZE_GREATER MATCHER_SIZE_SMALLER MATCHER_SIZE_EQUAL
256 %token MATCHER_LOCKED MATCHER_NOT_LOCKED
257 %token MATCHER_COLORLABEL MATCHER_NOT_COLORLABEL
258 %token MATCHER_IGNORE_THREAD MATCHER_NOT_IGNORE_THREAD
259 %token MATCHER_CHANGE_SCORE
260
261 %start file
262
263 %token <str> MATCHER_STRING
264 %token <str> MATCHER_SECTION
265 %token <str> MATCHER_INTEGER
266
267 %%
268
269 file:
270 {
271         if (matcher_parse_op == MATCHER_PARSE_NONE) {
272                 prefs_scoring = &global_scoring;
273                 prefs_filtering = &global_processing;
274         }
275 }
276 file_line_list;
277
278 file_line_list:
279 file_line
280 file_line_list
281 | file_line
282 ;
283
284 file_line:
285 section_notification
286
287 { action_list = NULL; }
288 instruction
289 | error MATCHER_EOL
290 {
291         yyerrok;
292 };
293
294 section_notification:
295 MATCHER_SECTION MATCHER_EOL
296 {
297         gchar *folder = $1;
298         FolderItem *item = NULL;
299
300         if (matcher_parse_op == MATCHER_PARSE_NONE) {
301                 if (!strcmp(folder, "global")) {
302                         prefs_scoring = &global_scoring;
303                         prefs_filtering = &global_processing;
304                 } else {
305                         item = folder_find_item_from_identifier(folder);
306                         if (item != NULL) {
307                                 prefs_scoring = &item->prefs->scoring;
308                                 prefs_filtering = &item->prefs->processing;
309                         } else {
310                                 prefs_scoring = NULL;
311                                 prefs_filtering = NULL;
312                         }
313                 }
314         }
315 }
316 ;
317
318 instruction:
319 condition filtering_or_scoring MATCHER_EOL
320 | condition filtering_or_scoring
321 {
322         if (matcher_parse_op == MATCHER_PARSE_NO_EOL)
323                 YYACCEPT;
324         else {
325                 matcher_parsererror("parse error");
326                 YYERROR;
327         }
328 }
329 | condition
330 {
331         if (matcher_parse_op == MATCHER_PARSE_CONDITION)
332                 YYACCEPT;
333         else {
334                 matcher_parsererror("parse error");
335                 YYERROR;
336         }
337 }
338 | filtering_action_list
339 {
340         if (matcher_parse_op == MATCHER_PARSE_FILTERING_OR_SCORING)
341                 YYACCEPT;
342         else {
343                 matcher_parsererror("parse error");
344                 YYERROR;
345         }
346 }
347 | MATCHER_EOL
348 ;
349
350 filtering_or_scoring:
351 filtering_action_list
352 {
353         filtering = filteringprop_new(cond, action_list);
354         cond = NULL;
355         action_list = NULL;
356         if ((matcher_parse_op == MATCHER_PARSE_NONE) && (prefs_filtering != NULL)) {
357                 *prefs_filtering = g_slist_append(*prefs_filtering,
358                                                   filtering);
359                 filtering = NULL;
360         }
361 }
362 | scoring_rule
363 {
364         scoring = scoringprop_new(cond, score);
365         cond = NULL;
366         score = 0;
367         if ((matcher_parse_op == MATCHER_PARSE_NONE)
368             && (prefs_scoring != NULL)) {
369                 *prefs_scoring = g_slist_append(*prefs_scoring, scoring);
370                 scoring = NULL;
371         }
372 }
373 ;
374
375 filtering_action_list:
376 filtering_action_b filtering_action_list
377 | filtering_action_b
378 ;
379
380 filtering_action_b:
381 filtering_action
382 {
383         action_list = g_slist_append(action_list, action);
384         action = NULL;
385 }
386 ;
387
388 match_type:
389 MATCHER_MATCHCASE
390 {
391         match_type = MATCHTYPE_MATCHCASE;
392 }
393 | MATCHER_MATCH
394 {
395         match_type = MATCHTYPE_MATCH;
396 }
397 | MATCHER_REGEXPCASE
398 {
399         match_type = MATCHTYPE_REGEXPCASE;
400 }
401 | MATCHER_REGEXP
402 {
403         match_type = MATCHTYPE_REGEXP;
404 }
405 ;
406
407 condition:
408 condition_list
409 {
410         cond = matcherlist_new(matchers_list, (bool_op == MATCHERBOOL_AND));
411         matchers_list = NULL;
412 }
413 ;
414
415 condition_list:
416 condition_list bool_op one_condition
417 {
418         matchers_list = g_slist_append(matchers_list, prop);
419 }
420 | one_condition
421 {
422         matchers_list = NULL;
423         matchers_list = g_slist_append(matchers_list, prop);
424 }
425 ;
426
427 bool_op:
428 MATCHER_AND
429 {
430         bool_op = MATCHERBOOL_AND;
431 }
432 | MATCHER_OR
433 {
434         bool_op = MATCHERBOOL_OR;
435 }
436 ;
437
438 one_condition:
439 MATCHER_ALL
440 {
441         gint criteria = 0;
442
443         criteria = MATCHCRITERIA_ALL;
444         prop = matcherprop_unquote_new(criteria, NULL, 0, NULL, 0);
445 }
446 | MATCHER_UNREAD
447 {
448         gint criteria = 0;
449
450         criteria = MATCHCRITERIA_UNREAD;
451         prop = matcherprop_unquote_new(criteria, NULL, 0, NULL, 0);
452 }
453 | MATCHER_NOT_UNREAD 
454 {
455         gint criteria = 0;
456
457         criteria = MATCHCRITERIA_NOT_UNREAD;
458         prop = matcherprop_unquote_new(criteria, NULL, 0, NULL, 0);
459 }
460 | MATCHER_NEW
461 {
462         gint criteria = 0;
463
464         criteria = MATCHCRITERIA_NEW;
465         prop = matcherprop_unquote_new(criteria, NULL, 0, NULL, 0);
466 }
467 | MATCHER_NOT_NEW
468 {
469         gint criteria = 0;
470
471         criteria = MATCHCRITERIA_NOT_NEW;
472         prop = matcherprop_unquote_new(criteria, NULL, 0, NULL, 0);
473 }
474 | MATCHER_MARKED
475 {
476         gint criteria = 0;
477
478         criteria = MATCHCRITERIA_MARKED;
479         prop = matcherprop_unquote_new(criteria, NULL, 0, NULL, 0);
480 }
481 | MATCHER_NOT_MARKED
482 {
483         gint criteria = 0;
484
485         criteria = MATCHCRITERIA_NOT_MARKED;
486         prop = matcherprop_unquote_new(criteria, NULL, 0, NULL, 0);
487 }
488 | MATCHER_DELETED
489 {
490         gint criteria = 0;
491
492         criteria = MATCHCRITERIA_DELETED;
493         prop = matcherprop_unquote_new(criteria, NULL, 0, NULL, 0);
494 }
495 | MATCHER_NOT_DELETED
496 {
497         gint criteria = 0;
498
499         criteria = MATCHCRITERIA_NOT_DELETED;
500         prop = matcherprop_unquote_new(criteria, NULL, 0, NULL, 0);
501 }
502 | MATCHER_REPLIED
503 {
504         gint criteria = 0;
505
506         criteria = MATCHCRITERIA_REPLIED;
507         prop = matcherprop_unquote_new(criteria, NULL, 0, NULL, 0);
508 }
509 | MATCHER_NOT_REPLIED
510 {
511         gint criteria = 0;
512
513         criteria = MATCHCRITERIA_NOT_REPLIED;
514         prop = matcherprop_unquote_new(criteria, NULL, 0, NULL, 0);
515 }
516 | MATCHER_FORWARDED
517 {
518         gint criteria = 0;
519
520         criteria = MATCHCRITERIA_FORWARDED;
521         prop = matcherprop_unquote_new(criteria, NULL, 0, NULL, 0);
522 }
523 | MATCHER_NOT_FORWARDED
524 {
525         gint criteria = 0;
526
527         criteria = MATCHCRITERIA_NOT_FORWARDED;
528         prop = matcherprop_unquote_new(criteria, NULL, 0, NULL, 0);
529 }
530 | MATCHER_LOCKED
531 {
532         gint criteria = 0;
533
534         criteria = MATCHCRITERIA_LOCKED;
535         prop = matcherprop_unquote_new(criteria, NULL, 0, NULL, 0);
536 }
537 | MATCHER_NOT_LOCKED
538 {
539         gint criteria = 0;
540
541         criteria = MATCHCRITERIA_NOT_LOCKED;
542         prop = matcherprop_unquote_new(criteria, NULL, 0, NULL, 0);
543 }
544 | MATCHER_COLORLABEL MATCHER_INTEGER
545 {
546         gint criteria = 0;
547         gint value = 0;
548
549         criteria = MATCHCRITERIA_COLORLABEL;
550         value = atoi($2);
551         if (value < 1) value = 1;
552         else if (value > MAX_COLORLABELS) value = MAX_COLORLABELS;
553         prop = matcherprop_unquote_new(criteria, NULL, 0, NULL, value);
554 }
555 | MATCHER_NOT_COLORLABEL MATCHER_INTEGER
556 {
557         gint criteria = 0;
558         gint value = 0;
559
560         criteria = MATCHCRITERIA_NOT_COLORLABEL;
561         value = atoi($2);
562         if (value < 1) value = 1;
563         else if (value > MAX_COLORLABELS) value = MAX_COLORLABELS;
564         prop = matcherprop_unquote_new(criteria, NULL, 0, NULL, value);
565 }
566 | MATCHER_IGNORE_THREAD
567 {
568         gint criteria = 0;
569
570         criteria = MATCHCRITERIA_IGNORE_THREAD;
571         prop = matcherprop_unquote_new(criteria, NULL, 0, NULL, 0);
572 }
573 | MATCHER_NOT_IGNORE_THREAD
574 {
575         gint criteria = 0;
576
577         criteria = MATCHCRITERIA_NOT_IGNORE_THREAD;
578         prop = matcherprop_unquote_new(criteria, NULL, 0, NULL, 0);
579 }
580 | MATCHER_SUBJECT match_type MATCHER_STRING
581 {
582         gint criteria = 0;
583         gchar *expr = NULL;
584
585         criteria = MATCHCRITERIA_SUBJECT;
586         expr = $3;
587         prop = matcherprop_unquote_new(criteria, NULL, match_type, expr, 0);
588 }
589 | MATCHER_NOT_SUBJECT match_type MATCHER_STRING
590 {
591         gint criteria = 0;
592         gchar *expr = NULL;
593
594         criteria = MATCHCRITERIA_NOT_SUBJECT;
595         expr = $3;
596         prop = matcherprop_unquote_new(criteria, NULL, match_type, expr, 0);
597 }
598 | MATCHER_FROM match_type MATCHER_STRING
599 {
600         gint criteria = 0;
601         gchar *expr = NULL;
602
603         criteria = MATCHCRITERIA_FROM;
604         expr = $3;
605         prop = matcherprop_unquote_new(criteria, NULL, match_type, expr, 0);
606 }
607 | MATCHER_NOT_FROM match_type MATCHER_STRING
608 {
609         gint criteria = 0;
610         gchar *expr = NULL;
611
612         criteria = MATCHCRITERIA_NOT_FROM;
613         expr = $3;
614         prop = matcherprop_unquote_new(criteria, NULL, match_type, expr, 0);
615 }
616 | MATCHER_TO match_type MATCHER_STRING
617 {
618         gint criteria = 0;
619         gchar *expr = NULL;
620
621         criteria = MATCHCRITERIA_TO;
622         expr = $3;
623         prop = matcherprop_unquote_new(criteria, NULL, match_type, expr, 0);
624 }
625 | MATCHER_NOT_TO match_type MATCHER_STRING
626 {
627         gint criteria = 0;
628         gchar *expr = NULL;
629
630         criteria = MATCHCRITERIA_NOT_TO;
631         expr = $3;
632         prop = matcherprop_unquote_new(criteria, NULL, match_type, expr, 0);
633 }
634 | MATCHER_CC match_type MATCHER_STRING
635 {
636         gint criteria = 0;
637         gchar *expr = NULL;
638
639         criteria = MATCHCRITERIA_CC;
640         expr = $3;
641         prop = matcherprop_unquote_new(criteria, NULL, match_type, expr, 0);
642 }
643 | MATCHER_NOT_CC match_type MATCHER_STRING
644 {
645         gint criteria = 0;
646         gchar *expr = NULL;
647
648         criteria = MATCHCRITERIA_NOT_CC;
649         expr = $3;
650         prop = matcherprop_unquote_new(criteria, NULL, match_type, expr, 0);
651 }
652 | MATCHER_TO_OR_CC match_type MATCHER_STRING
653 {
654         gint criteria = 0;
655         gchar *expr = NULL;
656
657         criteria = MATCHCRITERIA_TO_OR_CC;
658         expr = $3;
659         prop = matcherprop_unquote_new(criteria, NULL, match_type, expr, 0);
660 }
661 | MATCHER_NOT_TO_AND_NOT_CC match_type MATCHER_STRING
662 {
663         gint criteria = 0;
664         gchar *expr = NULL;
665
666         criteria = MATCHCRITERIA_NOT_TO_AND_NOT_CC;
667         expr = $3;
668         prop = matcherprop_unquote_new(criteria, NULL, match_type, expr, 0);
669 }
670 | MATCHER_AGE_GREATER MATCHER_INTEGER
671 {
672         gint criteria = 0;
673         gint value = 0;
674
675         criteria = MATCHCRITERIA_AGE_GREATER;
676         value = atoi($2);
677         prop = matcherprop_unquote_new(criteria, NULL, 0, NULL, value);
678 }
679 | MATCHER_AGE_LOWER MATCHER_INTEGER
680 {
681         gint criteria = 0;
682         gint value = 0;
683
684         criteria = MATCHCRITERIA_AGE_LOWER;
685         value = atoi($2);
686         prop = matcherprop_unquote_new(criteria, NULL, 0, NULL, value);
687 }
688 | MATCHER_NEWSGROUPS match_type MATCHER_STRING
689 {
690         gint criteria = 0;
691         gchar *expr = NULL;
692
693         criteria = MATCHCRITERIA_NEWSGROUPS;
694         expr = $3;
695         prop = matcherprop_unquote_new(criteria, NULL, match_type, expr, 0);
696 }
697 | MATCHER_NOT_NEWSGROUPS match_type MATCHER_STRING
698 {
699         gint criteria = 0;
700         gchar *expr = NULL;
701
702         criteria = MATCHCRITERIA_NOT_NEWSGROUPS;
703         expr = $3;
704         prop = matcherprop_unquote_new(criteria, NULL, match_type, expr, 0);
705 }
706 | MATCHER_INREPLYTO match_type MATCHER_STRING
707 {
708         gint criteria = 0;
709         gchar *expr = NULL;
710
711         criteria = MATCHCRITERIA_INREPLYTO;
712         expr = $3;
713         prop = matcherprop_unquote_new(criteria, NULL, match_type, expr, 0);
714 }
715 | MATCHER_NOT_INREPLYTO match_type MATCHER_STRING
716 {
717         gint criteria = 0;
718         gchar *expr = NULL;
719
720         criteria = MATCHCRITERIA_NOT_INREPLYTO;
721         expr = $3;
722         prop = matcherprop_unquote_new(criteria, NULL, match_type, expr, 0);
723 }
724 | MATCHER_REFERENCES match_type MATCHER_STRING
725 {
726         gint criteria = 0;
727         gchar *expr = NULL;
728
729         criteria = MATCHCRITERIA_REFERENCES;
730         expr = $3;
731         prop = matcherprop_unquote_new(criteria, NULL, match_type, expr, 0);
732 }
733 | MATCHER_NOT_REFERENCES match_type MATCHER_STRING
734 {
735         gint criteria = 0;
736         gchar *expr = NULL;
737
738         criteria = MATCHCRITERIA_NOT_REFERENCES;
739         expr = $3;
740         prop = matcherprop_unquote_new(criteria, NULL, match_type, expr, 0);
741 }
742 | MATCHER_SCORE_GREATER MATCHER_INTEGER
743 {
744         gint criteria = 0;
745         gint value = 0;
746
747         criteria = MATCHCRITERIA_SCORE_GREATER;
748         value = atoi($2);
749         prop = matcherprop_unquote_new(criteria, NULL, 0, NULL, value);
750 }
751 | MATCHER_SCORE_LOWER MATCHER_INTEGER
752 {
753         gint criteria = 0;
754         gint value = 0;
755
756         criteria = MATCHCRITERIA_SCORE_LOWER;
757         value = atoi($2);
758         prop = matcherprop_unquote_new(criteria, NULL, 0, NULL, value);
759 }
760 | MATCHER_SCORE_EQUAL MATCHER_INTEGER
761 {
762         gint criteria = 0;
763         gint value = 0;
764
765         criteria = MATCHCRITERIA_SCORE_EQUAL;
766         value = atoi($2);
767         prop = matcherprop_unquote_new(criteria, NULL, 0, NULL, value);
768 }
769 | MATCHER_SIZE_GREATER MATCHER_INTEGER 
770 {
771         gint criteria = 0;
772         gint value    = 0;
773         criteria = MATCHCRITERIA_SIZE_GREATER;
774         value = atoi($2);
775         prop = matcherprop_unquote_new(criteria, NULL, 0, NULL, value);
776 }
777 | MATCHER_SIZE_SMALLER MATCHER_INTEGER
778 {
779         gint criteria = 0;
780         gint value    = 0;
781         criteria = MATCHCRITERIA_SIZE_SMALLER;
782         value = atoi($2);
783         prop = matcherprop_unquote_new(criteria, NULL, 0, NULL, value);
784 }
785 | MATCHER_SIZE_EQUAL MATCHER_INTEGER
786 {
787         gint criteria = 0;
788         gint value    = 0;
789         criteria = MATCHCRITERIA_SIZE_EQUAL;
790         value = atoi($2);
791         prop = matcherprop_unquote_new(criteria, NULL, 0, NULL, value);
792 }
793 | MATCHER_HEADER MATCHER_STRING
794 {
795         header = g_strdup($2);
796 } match_type MATCHER_STRING
797 {
798         gint criteria = 0;
799         gchar *expr = NULL;
800
801         criteria = MATCHCRITERIA_HEADER;
802         expr = $2;
803         prop = matcherprop_unquote_new(criteria, header, match_type, expr, 0);
804         g_free(header);
805 }
806 | MATCHER_NOT_HEADER MATCHER_STRING
807 {
808         header = g_strdup($2);
809 } match_type MATCHER_STRING
810 {
811         gint criteria = 0;
812         gchar *expr = NULL;
813
814         criteria = MATCHCRITERIA_NOT_HEADER;
815         expr = $2;
816         prop = matcherprop_unquote_new(criteria, header, match_type, expr, 0);
817         g_free(header);
818 }
819 | MATCHER_HEADERS_PART match_type MATCHER_STRING
820 {
821         gint criteria = 0;
822         gchar *expr = NULL;
823
824         criteria = MATCHCRITERIA_HEADERS_PART;
825         expr = $3;
826         prop = matcherprop_unquote_new(criteria, NULL, match_type, expr, 0);
827 }
828 | MATCHER_NOT_HEADERS_PART match_type MATCHER_STRING
829 {
830         gint criteria = 0;
831         gchar *expr = NULL;
832
833         criteria = MATCHCRITERIA_NOT_HEADERS_PART;
834         expr = $3;
835         prop = matcherprop_unquote_new(criteria, NULL, match_type, expr, 0);
836 }
837 | MATCHER_MESSAGE match_type MATCHER_STRING
838 {
839         gint criteria = 0;
840         gchar *expr = NULL;
841
842         criteria = MATCHCRITERIA_MESSAGE;
843         expr = $3;
844         prop = matcherprop_unquote_new(criteria, NULL, match_type, expr, 0);
845 }
846 | MATCHER_NOT_MESSAGE match_type MATCHER_STRING
847 {
848         gint criteria = 0;
849         gchar *expr = NULL;
850
851         criteria = MATCHCRITERIA_NOT_MESSAGE;
852         expr = $3;
853         prop = matcherprop_unquote_new(criteria, NULL, match_type, expr, 0);
854 }
855 | MATCHER_BODY_PART match_type MATCHER_STRING
856 {
857         gint criteria = 0;
858         gchar *expr = NULL;
859
860         criteria = MATCHCRITERIA_BODY_PART;
861         expr = $3;
862         prop = matcherprop_unquote_new(criteria, NULL, match_type, expr, 0);
863 }
864 | MATCHER_NOT_BODY_PART match_type MATCHER_STRING
865 {
866         gint criteria = 0;
867         gchar *expr = NULL;
868
869         criteria = MATCHCRITERIA_NOT_BODY_PART;
870         expr = $3;
871         prop = matcherprop_unquote_new(criteria, NULL, match_type, expr, 0);
872 }
873 | MATCHER_EXECUTE MATCHER_STRING
874 {
875         gint criteria = 0;
876         gchar *expr = NULL;
877
878         criteria = MATCHCRITERIA_EXECUTE;
879         expr = $2;
880         prop = matcherprop_unquote_new(criteria, NULL, 0, expr, 0);
881 }
882 | MATCHER_NOT_EXECUTE MATCHER_STRING
883 {
884         gint criteria = 0;
885         gchar *expr = NULL;
886
887         criteria = MATCHCRITERIA_NOT_EXECUTE;
888         expr = $2;
889         prop = matcherprop_unquote_new(criteria, NULL, 0, expr, 0);
890 }
891 ;
892
893 filtering_action:
894 MATCHER_EXECUTE MATCHER_STRING
895 {
896         gchar *cmd = NULL;
897         gint action_type = 0;
898
899         action_type = MATCHACTION_EXECUTE;
900         cmd = $2;
901         action = filteringaction_new(action_type, 0, cmd, 0);
902 }
903 | MATCHER_MOVE MATCHER_STRING
904 {
905         gchar *destination = NULL;
906         gint action_type = 0;
907
908         action_type = MATCHACTION_MOVE;
909         destination = $2;
910         action = filteringaction_new(action_type, 0, destination, 0);
911 }
912 | MATCHER_COPY MATCHER_STRING
913 {
914         gchar *destination = NULL;
915         gint action_type = 0;
916
917         action_type = MATCHACTION_COPY;
918         destination = $2;
919         action = filteringaction_new(action_type, 0, destination, 0);
920 }
921 | MATCHER_DELETE
922 {
923         gint action_type = 0;
924
925         action_type = MATCHACTION_DELETE;
926         action = filteringaction_new(action_type, 0, NULL, 0);
927 }
928 | MATCHER_MARK
929 {
930         gint action_type = 0;
931
932         action_type = MATCHACTION_MARK;
933         action = filteringaction_new(action_type, 0, NULL, 0);
934 }
935 | MATCHER_UNMARK
936 {
937         gint action_type = 0;
938
939         action_type = MATCHACTION_UNMARK;
940         action = filteringaction_new(action_type, 0, NULL, 0);
941 }
942 | MATCHER_LOCK
943 {
944         gint action_type = 0;
945
946         action_type = MATCHACTION_LOCK;
947         action = filteringaction_new(action_type, 0, NULL, 0);
948 }
949 | MATCHER_UNLOCK
950 {
951         gint action_type = 0;
952
953         action_type = MATCHACTION_UNLOCK;
954         action = filteringaction_new(action_type, 0, NULL, 0);
955 }
956 | MATCHER_MARK_AS_READ
957 {
958         gint action_type = 0;
959
960         action_type = MATCHACTION_MARK_AS_READ;
961         action = filteringaction_new(action_type, 0, NULL, 0);
962 }
963 | MATCHER_MARK_AS_UNREAD
964 {
965         gint action_type = 0;
966
967         action_type = MATCHACTION_MARK_AS_UNREAD;
968         action = filteringaction_new(action_type, 0, NULL, 0);
969 }
970 | MATCHER_FORWARD MATCHER_INTEGER MATCHER_STRING
971 {
972         gchar *destination = NULL;
973         gint action_type = 0;
974         gint account_id = 0;
975
976         action_type = MATCHACTION_FORWARD;
977         account_id = atoi($2);
978         destination = $3;
979         action = filteringaction_new(action_type, account_id, destination, 0);
980 }
981 | MATCHER_FORWARD_AS_ATTACHMENT MATCHER_INTEGER MATCHER_STRING
982 {
983         gchar *destination = NULL;
984         gint action_type = 0;
985         gint account_id = 0;
986
987         action_type = MATCHACTION_FORWARD_AS_ATTACHMENT;
988         account_id = atoi($2);
989         destination = $3;
990         action = filteringaction_new(action_type, account_id, destination, 0);
991 }
992 | MATCHER_REDIRECT MATCHER_INTEGER MATCHER_STRING
993 {
994         gchar *destination = NULL;
995         gint action_type = 0;
996         gint account_id = 0;
997
998         action_type = MATCHACTION_REDIRECT;
999         account_id = atoi($2);
1000         destination = $3;
1001         action = filteringaction_new(action_type, account_id, destination, 0);
1002 }
1003 | MATCHER_COLOR MATCHER_INTEGER
1004 {
1005         gint action_type = 0;
1006         gint color = 0;
1007
1008         action_type = MATCHACTION_COLOR;
1009         color = atoi($2);
1010         action = filteringaction_new(action_type, 0, NULL, color);
1011 }
1012 | MATCHER_CHANGE_SCORE MATCHER_STRING
1013 {
1014         gint action_type = MATCHACTION_CHANGE_SCORE;
1015         char *last_tok = NULL;
1016         const gchar *first_tok;
1017         long int chscore;
1018         int change_type; /* -1 == decrement, 0 == assign, 1 == increment */     
1019
1020 #define is_number(x) ( ((x) == '+') || ((x) == '-') || (isdigit((x))) ) 
1021         /* find start */
1022         for (first_tok = $2; *first_tok && !is_number(*first_tok); first_tok++)
1023                 ;            
1024 #undef is_number                
1025         
1026         chscore = strtol(first_tok, &last_tok, 10);
1027         if (last_tok == first_tok || *last_tok == 0) 
1028                 chscore = 0;
1029         change_type = *first_tok == '+' ? 1 : *first_tok == '-' ? -1 : 0;
1030         action = filteringaction_new(MATCHACTION_CHANGE_SCORE, change_type,
1031                                      NULL, chscore);
1032 }
1033 ;
1034
1035 scoring_rule:
1036 MATCHER_SCORE MATCHER_INTEGER
1037 {
1038         score = atoi($2);
1039 }
1040 ;