2006-11-13 [wwp] 2.6.0cvs39
[claws.git] / src / quote_fmt_parse.y
1 /*
2  * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright (C) 1999-2006 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
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_FULLNAME
593         {
594                 if (msginfo->fromname)
595                         INSERT(msginfo->fromname);
596         }
597         | SHOW_FIRST_NAME
598         {
599                 quote_fmt_show_first_name(msginfo);
600         }
601         | SHOW_LAST_NAME
602     {
603                 quote_fmt_show_last_name(msginfo);
604         }
605         | SHOW_SENDER_INITIAL
606         {
607                 quote_fmt_show_sender_initial(msginfo);
608         }
609         | SHOW_SUBJECT
610         {
611                 if (msginfo->subject)
612                         INSERT(msginfo->subject);
613         }
614         | SHOW_TO
615         {
616                 if (msginfo->to)
617                         INSERT(msginfo->to);
618         }
619         | SHOW_MESSAGEID
620         {
621                 if (msginfo->msgid)
622                         INSERT(msginfo->msgid);
623         }
624         | SHOW_PERCENT
625         {
626                 INSERT("%");
627         }
628         | SHOW_CC
629         {
630                 if (msginfo->cc)
631                         INSERT(msginfo->cc);
632         }
633         | SHOW_REFERENCES
634         {
635                 GSList *item;
636
637                 INSERT(msginfo->inreplyto);
638                 for (item = msginfo->references; item != NULL; item = g_slist_next(item))
639                         if (item->data)
640                                 INSERT(item->data);
641         }
642         | SHOW_MESSAGE
643         {
644                 quote_fmt_show_msg(msginfo, body, FALSE, TRUE, quote_str);
645         }
646         | SHOW_QUOTED_MESSAGE
647         {
648                 quote_fmt_show_msg(msginfo, body, TRUE, TRUE, quote_str);
649         }
650         | SHOW_MESSAGE_NO_SIGNATURE
651         {
652                 quote_fmt_show_msg(msginfo, body, FALSE, FALSE, quote_str);
653         }
654         | SHOW_QUOTED_MESSAGE_NO_SIGNATURE
655         {
656                 quote_fmt_show_msg(msginfo, body, TRUE, FALSE, quote_str);
657         }
658         | SHOW_ACCOUNT_FULL_NAME
659         {
660                 if (account && account->name)
661                         INSERT(account->name);
662         }
663         | SHOW_ACCOUNT_MAIL_ADDRESS
664         {
665                 if (account && account->address)
666                         INSERT(account->address);
667         }
668         | SHOW_ACCOUNT_NAME
669         {
670                 if (account && account->account_name)
671                         INSERT(account->account_name);
672         }
673         | SHOW_ACCOUNT_ORGANIZATION
674         {
675                 if (account && account->organization)
676                         INSERT(account->organization);
677         }
678         | SHOW_BACKSLASH
679         {
680                 INSERT("\\");
681         }
682         | SHOW_TAB
683         {
684                 INSERT("\t");
685         }
686         | SHOW_EOL
687         {
688                 INSERT("\n");
689         }
690         | SHOW_QUESTION_MARK
691         {
692                 INSERT("?");
693         }
694         | SHOW_EXCLAMATION_MARK
695         {
696                 INSERT("!");
697         }
698         | SHOW_PIPE
699         {
700                 INSERT("|");
701         }
702         | SHOW_OPARENT
703         {
704                 INSERT("{");
705         }
706         | SHOW_CPARENT
707         {
708                 INSERT("}");
709         }
710         | SET_CURSOR_POS
711         {
712                 cursor_pos = current->bufsize;
713         };
714
715 query:
716         QUERY_DATE
717         {
718                 add_visibility(msginfo->date != NULL);
719         }
720         OPARENT quote_fmt CPARENT
721         {
722                 remove_visibility();
723         }
724         | QUERY_FROM
725         {
726                 add_visibility(msginfo->from != NULL);
727         }
728         OPARENT quote_fmt CPARENT
729         {
730                 remove_visibility();
731         }
732         | QUERY_FULLNAME
733         {
734                 add_visibility(msginfo->fromname != NULL);
735         }
736         OPARENT quote_fmt CPARENT
737         {
738                 remove_visibility();
739         }
740         | QUERY_SUBJECT
741         {
742                 add_visibility(msginfo->subject != NULL);
743         }
744         OPARENT quote_fmt CPARENT
745         {
746                 remove_visibility();
747         }
748         | QUERY_TO
749         {
750                 add_visibility(msginfo->to != NULL);
751         }
752         OPARENT quote_fmt CPARENT
753         {
754                 remove_visibility();
755         }
756         | QUERY_NEWSGROUPS
757         {
758                 add_visibility(msginfo->newsgroups != NULL);
759         }
760         OPARENT quote_fmt CPARENT
761         {
762                 remove_visibility();
763         }
764         | QUERY_MESSAGEID
765         {
766                 add_visibility(msginfo->msgid != NULL);
767         }
768         OPARENT quote_fmt CPARENT
769         {
770                 remove_visibility();
771         }
772         | QUERY_CC
773         {
774                 add_visibility(msginfo->cc != NULL);
775         }
776         OPARENT quote_fmt CPARENT
777         {
778                 remove_visibility();
779         }
780         | QUERY_REFERENCES
781         {
782                 gboolean found;
783                 GSList *item;
784
785                 found = (msginfo->inreplyto != NULL);
786                 for (item = msginfo->references; found == FALSE && item != NULL; item = g_slist_next(item))
787                         if (item->data)
788                                 found = TRUE;
789                 add_visibility(found == TRUE);
790         }
791         OPARENT quote_fmt CPARENT
792         {
793                 remove_visibility();
794         }
795         | QUERY_ACCOUNT_FULL_NAME
796         {
797                 add_visibility(account != NULL && account->name != NULL);
798         }
799         OPARENT quote_fmt CPARENT
800         {
801                 remove_visibility();
802         }
803         | QUERY_ACCOUNT_ORGANIZATION
804         {
805                 add_visibility(account != NULL && account->organization != NULL);
806         }
807         OPARENT quote_fmt CPARENT
808         {
809                 remove_visibility();
810         };
811
812 query_not:
813         QUERY_NOT_DATE
814         {
815                 add_visibility(msginfo->date == NULL);
816         }
817         OPARENT quote_fmt CPARENT
818         {
819                 remove_visibility();
820         }
821         | QUERY_NOT_FROM
822         {
823                 add_visibility(msginfo->from == NULL);
824         }
825         OPARENT quote_fmt CPARENT
826         {
827                 remove_visibility();
828         }
829         | QUERY_NOT_FULLNAME
830         {
831                 add_visibility(msginfo->fromname == NULL);
832         }
833         OPARENT quote_fmt CPARENT
834         {
835                 remove_visibility();
836         }
837         | QUERY_NOT_SUBJECT
838         {
839                 add_visibility(msginfo->subject == NULL);
840         }
841         OPARENT quote_fmt CPARENT
842         {
843                 remove_visibility();
844         }
845         | QUERY_NOT_TO
846         {
847                 add_visibility(msginfo->to == NULL);
848         }
849         OPARENT quote_fmt CPARENT
850         {
851                 remove_visibility();
852         }
853         | QUERY_NOT_NEWSGROUPS
854         {
855                 add_visibility(msginfo->newsgroups == NULL);
856         }
857         OPARENT quote_fmt CPARENT
858         {
859                 remove_visibility();
860         }
861         | QUERY_NOT_MESSAGEID
862         {
863                 add_visibility(msginfo->msgid == NULL);
864         }
865         OPARENT quote_fmt CPARENT
866         {
867                 remove_visibility();
868         }
869         | QUERY_NOT_CC
870         {
871                 add_visibility(msginfo->cc == NULL);
872         }
873         OPARENT quote_fmt CPARENT
874         {
875                 remove_visibility();
876         }
877         | QUERY_NOT_REFERENCES
878         {
879                 gboolean found;
880                 GSList *item;
881
882                 found = (msginfo->inreplyto != NULL);
883                 for (item = msginfo->references; found == FALSE && item != NULL; item = g_slist_next(item))
884                         if (item->data)
885                                 found = TRUE;
886                 add_visibility(found == FALSE);
887         }
888         OPARENT quote_fmt CPARENT
889         {
890                 remove_visibility();
891         }
892         | QUERY_NOT_ACCOUNT_FULL_NAME
893         {
894                 add_visibility(account == NULL || account->name == NULL);
895         }
896         OPARENT quote_fmt CPARENT
897         {
898                 remove_visibility();
899         }
900         | QUERY_NOT_ACCOUNT_ORGANIZATION
901         {
902                 add_visibility(account == NULL || account->organization == NULL);
903         }
904         OPARENT quote_fmt CPARENT
905         {
906                 remove_visibility();
907         };
908
909 insert:
910         INSERT_FILE
911         {
912                 current = &sub_expr;
913                 clear_buffer();
914         }
915         OPARENT sub_expr CPARENT
916         {
917                 current = &main_expr;
918                 if (!dry_run) {
919                         quote_fmt_insert_file(sub_expr.buffer);
920                 }
921         }
922         | INSERT_PROGRAMOUTPUT
923         {
924                 current = &sub_expr;
925                 clear_buffer();
926         }
927         OPARENT sub_expr CPARENT
928         {
929                 current = &main_expr;
930                 if (!dry_run) {
931                         quote_fmt_insert_program_output(sub_expr.buffer);
932                 }
933         }
934         | INSERT_USERINPUT
935         {
936                 current = &sub_expr;
937                 clear_buffer();
938         }
939         OPARENT sub_expr CPARENT
940         {
941                 current = &main_expr;
942                 if (!dry_run) {
943                         quote_fmt_insert_user_input(sub_expr.buffer);
944                 }
945         };