42e92ab42558af56cf5e7743805cf088f55a2bb2
[claws.git] / src / quote_fmt_parse.y
1 /*
2  * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright (C) 1999-2007 Hiroyuki Yamamoto and the Claws Mail Team
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU 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, write to the Free Software
17  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18  */
19
20 %{
21
22 #include "defs.h"
23
24 #include <glib.h>
25 #include <glib/gi18n.h>
26
27 #include <ctype.h>
28
29 #include "procmsg.h"
30 #include "procmime.h"
31 #include "utils.h"
32 #include "codeconv.h"
33 #include "procheader.h"
34 #include "gtk/inputdialog.h"
35
36 #include "quote_fmt.h"
37 #include "quote_fmt_lex.h"
38
39 /* decl */
40 /*
41 flex quote_fmt.l
42 bison -p quote_fmt quote_fmt.y
43 */
44
45 int yylex(void);
46
47 static MsgInfo *msginfo = NULL;
48 static PrefsAccount *account = NULL;
49 static gboolean *visible = NULL;
50 static gboolean dry_run = FALSE;
51 static gint maxsize = 0;
52 static gint stacksize = 0;
53 static GHashTable *var_table = NULL;
54
55 typedef struct st_buffer
56 {
57         gchar *buffer;
58         gint bufsize;
59         gint bufmax;
60 } st_buffer;
61
62 static struct st_buffer main_expr = { NULL, 0, 0 };
63 static struct st_buffer sub_expr = { NULL, 0, 0 };
64 static struct st_buffer* current = NULL;
65
66 static const gchar *quote_str = NULL;
67 static const gchar *body = NULL;
68 static gint error = 0;
69
70 static gint cursor_pos = -1;
71
72 extern int quote_fmt_firsttime;
73
74 static void add_visibility(gboolean val)
75 {
76         stacksize++;
77         if (maxsize < stacksize) {
78                 maxsize += 128;
79                 visible = g_realloc(visible, maxsize * sizeof(gboolean));
80                 if (visible == NULL)
81                         maxsize = 0;
82         }
83
84         visible[stacksize - 1] = val;
85 }
86
87 static void remove_visibility(void)
88 {
89         stacksize--;
90         if (stacksize < 0) {
91                 g_warning("Error: visibility stack underflow\n");
92                 stacksize = 0;
93         }
94 }
95
96 static void add_buffer(const gchar *s)
97 {
98         gint len;
99
100         if (s == NULL)
101                 return;
102
103         len = strlen(s);
104         if (current->bufsize + len + 1 > current->bufmax) {
105                 if (current->bufmax == 0)
106                         current->bufmax = 128;
107                 while (current->bufsize + len + 1 > current->bufmax)
108                         current->bufmax *= 2;
109                 current->buffer = g_realloc(current->buffer, current->bufmax);
110         }
111         strcpy(current->buffer + current->bufsize, s);
112         current->bufsize += len;
113 }
114
115 static void clear_buffer(void)
116 {
117         if (current->buffer)
118                 *current->buffer = '\0';
119         else
120                 /* force to an empty string, as buffer should not be left unallocated */
121                 add_buffer("");
122         current->bufsize = 0;
123 }
124
125 gchar *quote_fmt_get_buffer(void)
126 {
127         if (current != &main_expr)
128                 g_warning("Error: parser still in sub-expr mode\n");
129
130         if (error != 0)
131                 return NULL;
132         else
133                 return current->buffer;
134 }
135
136 gint quote_fmt_get_cursor_pos(void)
137 {
138         return cursor_pos;      
139 }
140
141 #define INSERT(buf) \
142         if (stacksize != 0 && visible[stacksize - 1])\
143                 add_buffer(buf); \
144
145 #define INSERT_CHARACTER(chr) \
146         if (stacksize != 0 && visible[stacksize - 1]) { \
147                 gchar tmp[2]; \
148                 tmp[0] = (chr); \
149                 tmp[1] = '\0'; \
150                 add_buffer(tmp); \
151         }
152
153 void quote_fmt_reset_vartable(void)
154 {
155         if (var_table) {
156                 g_hash_table_destroy(var_table);
157                 var_table = NULL;
158         }
159 }
160
161 void quote_fmt_init(MsgInfo *info, const gchar *my_quote_str,
162                     const gchar *my_body, gboolean my_dry_run,
163                         PrefsAccount *compose_account)
164 {
165         quote_str = my_quote_str;
166         body = my_body;
167         msginfo = info;
168         account = compose_account;
169         dry_run = my_dry_run;
170         stacksize = 0;
171         add_visibility(TRUE);
172         main_expr.bufmax = 0;
173         sub_expr.bufmax = 0;
174         current = &main_expr;
175         clear_buffer();
176         error = 0;
177
178         if (!var_table)
179                 var_table = g_hash_table_new_full(g_str_hash, g_str_equal, 
180                                 g_free, g_free);
181
182         /*
183          * force LEX initialization
184          */
185         quote_fmt_firsttime = 1;
186         cursor_pos = -1;
187 }
188
189 void quote_fmterror(char *str)
190 {
191         g_warning("Error: %s\n", str);
192         error = 1;
193 }
194
195 int quote_fmtwrap(void)
196 {
197         return 1;
198 }
199
200 static int isseparator(int ch)
201 {
202         return g_ascii_isspace(ch) || ch == '.' || ch == '-';
203 }
204
205 static void quote_fmt_show_date(const MsgInfo *msginfo, const gchar *format)
206 {
207         char  result[100];
208         char *rptr;
209         char  zone[6];
210         struct tm lt;
211         const char *fptr;
212         const char *zptr;
213
214         if (!msginfo->date)
215                 return;
216         
217         /* 
218          * ALF - GNU C's strftime() has a nice format specifier 
219          * for time zone offset (%z). Non-standard however, so 
220          * emulate it.
221          */
222
223 #define RLEFT (sizeof result) - (rptr - result) 
224 #define STR_SIZE(x) (sizeof (x) - 1)
225
226         zone[0] = 0;
227
228         if (procheader_date_parse_to_tm(msginfo->date, &lt, zone)) {
229                 /*
230                  * break up format string in tiny bits delimited by valid %z's and 
231                  * feed it to strftime(). don't forget that '%%z' mean literal '%z'.
232                  */
233                 for (rptr = result, fptr = format; fptr && *fptr && rptr < &result[sizeof result - 1];) {
234                         int         perc;
235                         const char *p;
236                         char       *tmp;
237                         
238                         if (NULL != (zptr = strstr(fptr, "%z"))) {
239                                 /*
240                                  * count nr. of prepended percent chars
241                                  */
242                                 for (perc = 0, p = zptr; p && p >= format && *p == '%'; p--, perc++)
243                                         ;
244                                 /*
245                                  * feed to strftime()
246                                  */
247                                 tmp = g_strndup(fptr, zptr - fptr + (perc % 2 ? 0 : STR_SIZE("%z")));
248                                 if (tmp) {
249                                         rptr += strftime(rptr, RLEFT, tmp, &lt);
250                                         g_free(tmp);
251                                 }
252                                 /*
253                                  * append time zone offset
254                                  */
255                                 if (zone[0] && perc % 2) 
256                                         rptr += g_snprintf(rptr, RLEFT, "%s", zone);
257                                 fptr = zptr + STR_SIZE("%z");
258                         } else {
259                                 rptr += strftime(rptr, RLEFT, fptr, &lt);
260                                 fptr  = NULL;
261                         }
262                 }
263                 
264                 if (g_utf8_validate(result, -1, NULL)) {
265                         INSERT(result);
266                 } else {
267                         gchar *utf = conv_codeset_strdup(result, 
268                                 conv_get_locale_charset_str_no_utf8(),
269                                 CS_INTERNAL);
270                         if (utf == NULL || 
271                             !g_utf8_validate(utf, -1, NULL)) {
272                                 g_free(utf);
273                                 utf = g_malloc(strlen(result)*2+1);
274                                 conv_localetodisp(utf, 
275                                         strlen(result)*2+1, result);
276                         }
277                         if (g_utf8_validate(utf, -1, NULL)) {
278                                 INSERT(utf);
279                         }
280                         g_free(utf);
281                 }
282         }
283 #undef STR_SIZE                 
284 #undef RLEFT                    
285 }               
286
287 static void quote_fmt_show_first_name(const MsgInfo *msginfo)
288 {
289         guchar *p;
290         gchar *str;
291
292         if (!msginfo->fromname)
293                 return; 
294         
295         p = (guchar*)strchr(msginfo->fromname, ',');
296         if (p != NULL) {
297                 /* fromname is like "Duck, Donald" */
298                 p++;
299                 while (*p && isspace(*p)) p++;
300                 str = alloca(strlen((char *)p) + 1);
301                 if (str != NULL) {
302                         strcpy(str, (char *)p);
303                         INSERT(str);
304                 }
305         } else {
306                 /* fromname is like "Donald Duck" */
307                 str = alloca(strlen(msginfo->fromname) + 1);
308                 if (str != NULL) {
309                         strcpy(str, msginfo->fromname);
310                         p = (guchar *)str;
311                         while (*p && !isspace(*p)) p++;
312                         *p = '\0';
313                         INSERT(str);
314                 }
315         }
316 }
317
318 static void quote_fmt_show_last_name(const MsgInfo *msginfo)
319 {
320         gchar *p;
321         gchar *str;
322
323         /* This probably won't work together very well with Middle
324            names and the like - thth */
325         if (!msginfo->fromname) 
326                 return;
327
328         str = alloca(strlen(msginfo->fromname) + 1);
329         if (str != NULL) {
330                 strcpy(str, msginfo->fromname);
331                 p = strchr(str, ',');
332                 if (p != NULL) {
333                         /* fromname is like "Duck, Donald" */
334                         *p = '\0';
335                         INSERT(str);
336                 } else {
337                         /* fromname is like "Donald Duck" */
338                         p = str;
339                         while (*p && !isspace(*p)) p++;
340                         if (*p) {
341                             /* We found a space. Get first 
342                              none-space char and insert
343                              rest of string from there. */
344                             while (*p && isspace(*p)) p++;
345                             if (*p) {
346                                 INSERT(p);
347                             } else {
348                                 /* If there is no none-space 
349                                  char, just insert whole 
350                                  fromname. */
351                                 INSERT(str);
352                             }
353                         } else {
354                             /* If there is no space, just 
355                              insert whole fromname. */
356                             INSERT(str);
357                         }
358                 }
359         }
360 }
361
362 static void quote_fmt_show_sender_initial(const MsgInfo *msginfo)
363 {
364 #define MAX_SENDER_INITIAL 20
365         gchar tmp[MAX_SENDER_INITIAL];
366         guchar *p;
367         gchar *cur;
368         gint len = 0;
369
370         if (!msginfo->fromname) 
371                 return;
372
373         p = (guchar *)msginfo->fromname;
374         cur = tmp;
375         while (*p) {
376                 if (*p && g_utf8_validate((gchar *)p, 1, NULL)) {
377                         *cur = toupper(*p);
378                                 cur++;
379                         len++;
380                         if (len >= MAX_SENDER_INITIAL - 1)
381                                 break;
382                 } else
383                         break;
384                 while (*p && !isseparator(*p)) p++;
385                 while (*p && isseparator(*p)) p++;
386         }
387         *cur = '\0';
388         INSERT(tmp);
389 }
390
391 static void quote_fmt_show_msg(MsgInfo *msginfo, const gchar *body,
392                                gboolean quoted, gboolean signature,
393                                const gchar *quote_str)
394 {
395         gchar buf[BUFFSIZE];
396         FILE *fp;
397
398         if (!(msginfo->folder || body))
399                 return;
400
401         if (body)
402                 fp = str_open_as_stream(body);
403         else {
404                 if (procmime_msginfo_is_encrypted(msginfo))
405                         fp = procmime_get_first_encrypted_text_content(msginfo);
406                 else
407                         fp = procmime_get_first_text_content(msginfo);
408         }
409
410         if (fp == NULL)
411                 g_warning("Can't get text part\n");
412         else {
413                 while (fgets(buf, sizeof(buf), fp) != NULL) {
414                         strcrchomp(buf);
415                         
416                         if (!signature && strncmp(buf, "-- \n", 4) == 0)
417                                 break;
418                 
419                         if (quoted && quote_str)
420                                 INSERT(quote_str);
421                         
422                         INSERT(buf);
423                 }
424                 fclose(fp);
425         }
426 }
427
428 static void quote_fmt_insert_file(const gchar *filename)
429 {
430         FILE *file;
431         char buffer[256];
432         
433         if ((file = g_fopen(filename, "rb")) != NULL) {
434                 while (fgets(buffer, sizeof(buffer), file)) {
435                         INSERT(buffer);
436                 }
437                 fclose(file);
438         }
439
440 }
441
442 static void quote_fmt_insert_program_output(const gchar *progname)
443 {
444         FILE *file;
445         char buffer[256];
446
447         if ((file = popen(progname, "r")) != NULL) {
448                 while (fgets(buffer, sizeof(buffer), file)) {
449                         INSERT(buffer);
450                 }
451                 pclose(file);
452         }
453 }
454
455 static void quote_fmt_insert_user_input(const gchar *varname)
456 {
457         gchar *buf = NULL;
458         gchar *text = NULL;
459         
460         if (dry_run) 
461                 return;
462
463         if ((text = g_hash_table_lookup(var_table, varname)) == NULL) {
464                 buf = g_strdup_printf(_("Enter text to replace '%s'"), varname);
465                 text = input_dialog(_("Enter variable"), buf, "");
466                 g_free(buf);
467                 if (!text)
468                         return;
469                 g_hash_table_insert(var_table, g_strdup(varname), g_strdup(text));
470         } else {
471                 /* don't free the one in hashtable at the end */
472                 text = g_strdup(text);
473         }
474
475         if (!text)
476                 return;
477         INSERT(text);
478         g_free(text);
479 }
480
481 %}
482
483 %union {
484         char chr;
485         char str[256];
486 }
487
488 /* tokens SHOW */
489 %token SHOW_NEWSGROUPS
490 %token SHOW_DATE SHOW_FROM SHOW_FULLNAME SHOW_FIRST_NAME SHOW_LAST_NAME
491 %token SHOW_SENDER_INITIAL SHOW_SUBJECT SHOW_TO SHOW_MESSAGEID
492 %token SHOW_PERCENT SHOW_CC SHOW_REFERENCES SHOW_MESSAGE
493 %token SHOW_QUOTED_MESSAGE SHOW_BACKSLASH SHOW_TAB SHOW_MAIL_ADDRESS
494 %token SHOW_QUOTED_MESSAGE_NO_SIGNATURE SHOW_MESSAGE_NO_SIGNATURE
495 %token SHOW_EOL SHOW_QUESTION_MARK SHOW_EXCLAMATION_MARK SHOW_PIPE SHOW_OPARENT SHOW_CPARENT
496 %token SHOW_ACCOUNT_FULL_NAME SHOW_ACCOUNT_MAIL_ADDRESS SHOW_ACCOUNT_NAME SHOW_ACCOUNT_ORGANIZATION
497 /* tokens QUERY */
498 %token QUERY_DATE QUERY_FROM
499 %token QUERY_FULLNAME QUERY_SUBJECT QUERY_TO QUERY_NEWSGROUPS
500 %token QUERY_MESSAGEID QUERY_CC QUERY_REFERENCES
501 %token QUERY_ACCOUNT_FULL_NAME QUERY_ACCOUNT_ORGANIZATION
502 /* tokens QUERY_NOT */
503 %token QUERY_NOT_DATE QUERY_NOT_FROM
504 %token QUERY_NOT_FULLNAME QUERY_NOT_SUBJECT QUERY_NOT_TO QUERY_NOT_NEWSGROUPS
505 %token QUERY_NOT_MESSAGEID QUERY_NOT_CC QUERY_NOT_REFERENCES
506 %token QUERY_NOT_ACCOUNT_FULL_NAME QUERY_NOT_ACCOUNT_ORGANIZATION
507 /* other tokens */
508 %token INSERT_FILE INSERT_PROGRAMOUTPUT INSERT_USERINPUT
509 %token OPARENT CPARENT
510 %token CHARACTER
511 %token SHOW_DATE_EXPR
512 %token SET_CURSOR_POS
513
514 %start quote_fmt
515
516 %token <chr> CHARACTER
517 %type <chr> character
518 %type <str> string
519
520 %%
521
522 quote_fmt:
523         character_or_special_or_insert_or_query_list ;
524
525 sub_expr:
526         character_or_special_list ;
527
528 character_or_special_or_insert_or_query_list:
529         character_or_special_or_insert_or_query character_or_special_or_insert_or_query_list
530         | character_or_special_or_insert_or_query ;
531
532 character_or_special_list:
533         character_or_special character_or_special_list
534         | character_or_special ;
535
536 character_or_special_or_insert_or_query:
537         character_or_special
538         | query
539         | query_not
540         | insert ;
541
542 character_or_special:
543         special
544         | character
545         {
546                 INSERT_CHARACTER($1);
547         };
548
549 character:
550         CHARACTER
551         ;
552
553 string:
554         CHARACTER
555         {
556                 $$[0] = $1;
557                 $$[1] = '\0';
558         }
559         | string CHARACTER
560         {
561                 int len;
562                 
563                 strncpy($$, $1, sizeof($$));
564                 $$[sizeof($$) - 1] = '\0';
565                 len = strlen($$);
566                 if (len + 1 < sizeof($$)) {
567                         $$[len + 1] = '\0';
568                         $$[len] = $2;
569                 }
570         };
571
572 special:
573         SHOW_NEWSGROUPS
574         {
575                 if (msginfo->newsgroups)
576                         INSERT(msginfo->newsgroups);
577         }
578         | SHOW_DATE_EXPR OPARENT string CPARENT
579         {
580                 quote_fmt_show_date(msginfo, $3);
581         }
582         | SHOW_DATE
583         {
584                 if (msginfo->date)
585                         INSERT(msginfo->date);
586         }
587         | SHOW_FROM
588         {
589                 if (msginfo->from)
590                         INSERT(msginfo->from);
591         }
592         | SHOW_MAIL_ADDRESS
593         {
594                 if (msginfo->from) {
595                         gchar *stripped_address = g_strdup(msginfo->from);
596                         extract_address(stripped_address);
597                         INSERT(stripped_address);
598                         g_free(stripped_address);
599                 }
600         }
601         | SHOW_FULLNAME
602         {
603                 if (msginfo->fromname)
604                         INSERT(msginfo->fromname);
605         }
606         | SHOW_FIRST_NAME
607         {
608                 quote_fmt_show_first_name(msginfo);
609         }
610         | SHOW_LAST_NAME
611     {
612                 quote_fmt_show_last_name(msginfo);
613         }
614         | SHOW_SENDER_INITIAL
615         {
616                 quote_fmt_show_sender_initial(msginfo);
617         }
618         | SHOW_SUBJECT
619         {
620                 if (msginfo->subject)
621                         INSERT(msginfo->subject);
622         }
623         | SHOW_TO
624         {
625                 if (msginfo->to)
626                         INSERT(msginfo->to);
627         }
628         | SHOW_MESSAGEID
629         {
630                 if (msginfo->msgid)
631                         INSERT(msginfo->msgid);
632         }
633         | SHOW_PERCENT
634         {
635                 INSERT("%");
636         }
637         | SHOW_CC
638         {
639                 if (msginfo->cc)
640                         INSERT(msginfo->cc);
641         }
642         | SHOW_REFERENCES
643         {
644                 GSList *item;
645
646                 INSERT(msginfo->inreplyto);
647                 for (item = msginfo->references; item != NULL; item = g_slist_next(item))
648                         if (item->data)
649                                 INSERT(item->data);
650         }
651         | SHOW_MESSAGE
652         {
653                 quote_fmt_show_msg(msginfo, body, FALSE, TRUE, quote_str);
654         }
655         | SHOW_QUOTED_MESSAGE
656         {
657                 quote_fmt_show_msg(msginfo, body, TRUE, TRUE, quote_str);
658         }
659         | SHOW_MESSAGE_NO_SIGNATURE
660         {
661                 quote_fmt_show_msg(msginfo, body, FALSE, FALSE, quote_str);
662         }
663         | SHOW_QUOTED_MESSAGE_NO_SIGNATURE
664         {
665                 quote_fmt_show_msg(msginfo, body, TRUE, FALSE, quote_str);
666         }
667         | SHOW_ACCOUNT_FULL_NAME
668         {
669                 if (account && account->name)
670                         INSERT(account->name);
671         }
672         | SHOW_ACCOUNT_MAIL_ADDRESS
673         {
674                 if (account && account->address)
675                         INSERT(account->address);
676         }
677         | SHOW_ACCOUNT_NAME
678         {
679                 if (account && account->account_name)
680                         INSERT(account->account_name);
681         }
682         | SHOW_ACCOUNT_ORGANIZATION
683         {
684                 if (account && account->organization)
685                         INSERT(account->organization);
686         }
687         | SHOW_BACKSLASH
688         {
689                 INSERT("\\");
690         }
691         | SHOW_TAB
692         {
693                 INSERT("\t");
694         }
695         | SHOW_EOL
696         {
697                 INSERT("\n");
698         }
699         | SHOW_QUESTION_MARK
700         {
701                 INSERT("?");
702         }
703         | SHOW_EXCLAMATION_MARK
704         {
705                 INSERT("!");
706         }
707         | SHOW_PIPE
708         {
709                 INSERT("|");
710         }
711         | SHOW_OPARENT
712         {
713                 INSERT("{");
714         }
715         | SHOW_CPARENT
716         {
717                 INSERT("}");
718         }
719         | SET_CURSOR_POS
720         {
721                 cursor_pos = current->bufsize;
722         };
723
724 query:
725         QUERY_DATE
726         {
727                 add_visibility(msginfo->date != NULL);
728         }
729         OPARENT quote_fmt CPARENT
730         {
731                 remove_visibility();
732         }
733         | QUERY_FROM
734         {
735                 add_visibility(msginfo->from != NULL);
736         }
737         OPARENT quote_fmt CPARENT
738         {
739                 remove_visibility();
740         }
741         | QUERY_FULLNAME
742         {
743                 add_visibility(msginfo->fromname != NULL);
744         }
745         OPARENT quote_fmt CPARENT
746         {
747                 remove_visibility();
748         }
749         | QUERY_SUBJECT
750         {
751                 add_visibility(msginfo->subject != NULL);
752         }
753         OPARENT quote_fmt CPARENT
754         {
755                 remove_visibility();
756         }
757         | QUERY_TO
758         {
759                 add_visibility(msginfo->to != NULL);
760         }
761         OPARENT quote_fmt CPARENT
762         {
763                 remove_visibility();
764         }
765         | QUERY_NEWSGROUPS
766         {
767                 add_visibility(msginfo->newsgroups != NULL);
768         }
769         OPARENT quote_fmt CPARENT
770         {
771                 remove_visibility();
772         }
773         | QUERY_MESSAGEID
774         {
775                 add_visibility(msginfo->msgid != NULL);
776         }
777         OPARENT quote_fmt CPARENT
778         {
779                 remove_visibility();
780         }
781         | QUERY_CC
782         {
783                 add_visibility(msginfo->cc != NULL);
784         }
785         OPARENT quote_fmt CPARENT
786         {
787                 remove_visibility();
788         }
789         | QUERY_REFERENCES
790         {
791                 gboolean found;
792                 GSList *item;
793
794                 found = (msginfo->inreplyto != NULL);
795                 for (item = msginfo->references; found == FALSE && item != NULL; item = g_slist_next(item))
796                         if (item->data)
797                                 found = TRUE;
798                 add_visibility(found == TRUE);
799         }
800         OPARENT quote_fmt CPARENT
801         {
802                 remove_visibility();
803         }
804         | QUERY_ACCOUNT_FULL_NAME
805         {
806                 add_visibility(account != NULL && account->name != NULL);
807         }
808         OPARENT quote_fmt CPARENT
809         {
810                 remove_visibility();
811         }
812         | QUERY_ACCOUNT_ORGANIZATION
813         {
814                 add_visibility(account != NULL && account->organization != NULL);
815         }
816         OPARENT quote_fmt CPARENT
817         {
818                 remove_visibility();
819         };
820
821 query_not:
822         QUERY_NOT_DATE
823         {
824                 add_visibility(msginfo->date == NULL);
825         }
826         OPARENT quote_fmt CPARENT
827         {
828                 remove_visibility();
829         }
830         | QUERY_NOT_FROM
831         {
832                 add_visibility(msginfo->from == NULL);
833         }
834         OPARENT quote_fmt CPARENT
835         {
836                 remove_visibility();
837         }
838         | QUERY_NOT_FULLNAME
839         {
840                 add_visibility(msginfo->fromname == NULL);
841         }
842         OPARENT quote_fmt CPARENT
843         {
844                 remove_visibility();
845         }
846         | QUERY_NOT_SUBJECT
847         {
848                 add_visibility(msginfo->subject == NULL);
849         }
850         OPARENT quote_fmt CPARENT
851         {
852                 remove_visibility();
853         }
854         | QUERY_NOT_TO
855         {
856                 add_visibility(msginfo->to == NULL);
857         }
858         OPARENT quote_fmt CPARENT
859         {
860                 remove_visibility();
861         }
862         | QUERY_NOT_NEWSGROUPS
863         {
864                 add_visibility(msginfo->newsgroups == NULL);
865         }
866         OPARENT quote_fmt CPARENT
867         {
868                 remove_visibility();
869         }
870         | QUERY_NOT_MESSAGEID
871         {
872                 add_visibility(msginfo->msgid == NULL);
873         }
874         OPARENT quote_fmt CPARENT
875         {
876                 remove_visibility();
877         }
878         | QUERY_NOT_CC
879         {
880                 add_visibility(msginfo->cc == NULL);
881         }
882         OPARENT quote_fmt CPARENT
883         {
884                 remove_visibility();
885         }
886         | QUERY_NOT_REFERENCES
887         {
888                 gboolean found;
889                 GSList *item;
890
891                 found = (msginfo->inreplyto != NULL);
892                 for (item = msginfo->references; found == FALSE && item != NULL; item = g_slist_next(item))
893                         if (item->data)
894                                 found = TRUE;
895                 add_visibility(found == FALSE);
896         }
897         OPARENT quote_fmt CPARENT
898         {
899                 remove_visibility();
900         }
901         | QUERY_NOT_ACCOUNT_FULL_NAME
902         {
903                 add_visibility(account == NULL || account->name == NULL);
904         }
905         OPARENT quote_fmt CPARENT
906         {
907                 remove_visibility();
908         }
909         | QUERY_NOT_ACCOUNT_ORGANIZATION
910         {
911                 add_visibility(account == NULL || account->organization == NULL);
912         }
913         OPARENT quote_fmt CPARENT
914         {
915                 remove_visibility();
916         };
917
918 insert:
919         INSERT_FILE
920         {
921                 current = &sub_expr;
922                 clear_buffer();
923         }
924         OPARENT sub_expr CPARENT
925         {
926                 current = &main_expr;
927                 if (!dry_run) {
928                         quote_fmt_insert_file(sub_expr.buffer);
929                 }
930         }
931         | INSERT_PROGRAMOUTPUT
932         {
933                 current = &sub_expr;
934                 clear_buffer();
935         }
936         OPARENT sub_expr CPARENT
937         {
938                 current = &main_expr;
939                 if (!dry_run) {
940                         quote_fmt_insert_program_output(sub_expr.buffer);
941                 }
942         }
943         | INSERT_USERINPUT
944         {
945                 current = &sub_expr;
946                 clear_buffer();
947         }
948         OPARENT sub_expr CPARENT
949         {
950                 current = &main_expr;
951                 if (!dry_run) {
952                         quote_fmt_insert_user_input(sub_expr.buffer);
953                 }
954         };