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