e666e9108384373094d14684781f659a310edc5b
[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 3 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, see <http://www.gnu.org/licenses/>.
17  * 
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 "addr_compl.h"
35 #include "gtk/inputdialog.h"
36
37 #include "quote_fmt.h"
38 #include "quote_fmt_lex.h"
39
40 /* decl */
41 /*
42 flex quote_fmt.l
43 bison -p quote_fmt quote_fmt.y
44 */
45
46 int yylex(void);
47
48 static MsgInfo *msginfo = NULL;
49 static PrefsAccount *account = NULL;
50 #ifdef USE_ASPELL
51 static gchar default_dictionary[BUFFSIZE];
52 #endif
53 static gboolean *visible = NULL;
54 static gboolean dry_run = FALSE;
55 static gint maxsize = 0;
56 static gint stacksize = 0;
57 static GHashTable *var_table = NULL;
58 static GList *attachments = NULL;
59
60 typedef struct st_buffer
61 {
62         gchar *buffer;
63         gint bufsize;
64         gint bufmax;
65 } st_buffer;
66
67 static struct st_buffer main_expr = { NULL, 0, 0 };
68 static struct st_buffer sub_expr = { NULL, 0, 0 };
69 static struct st_buffer* current = NULL;
70
71 static const gchar *quote_str = NULL;
72 static const gchar *body = NULL;
73 static gint error = 0;
74
75 static gint cursor_pos = -1;
76
77 extern int quote_fmt_firsttime;
78 extern int line;
79 extern int escaped_string;
80
81 static void add_visibility(gboolean val)
82 {
83         stacksize++;
84         if (maxsize < stacksize) {
85                 maxsize += 128;
86                 visible = g_realloc(visible, maxsize * sizeof(gboolean));
87                 if (visible == NULL)
88                         maxsize = 0;
89         }
90
91         visible[stacksize - 1] = val;
92 }
93
94 static void remove_visibility(void)
95 {
96         stacksize--;
97         if (stacksize < 0) {
98                 g_warning("Error: visibility stack underflow\n");
99                 stacksize = 0;
100         }
101 }
102
103 static void add_buffer(const gchar *s)
104 {
105         gint len;
106
107         if (s == NULL)
108                 return;
109
110         len = strlen(s);
111         if (current->bufsize + len + 1 > current->bufmax) {
112                 if (current->bufmax == 0)
113                         current->bufmax = 128;
114                 while (current->bufsize + len + 1 > current->bufmax)
115                         current->bufmax *= 2;
116                 current->buffer = g_realloc(current->buffer, current->bufmax);
117         }
118         strcpy(current->buffer + current->bufsize, s);
119         current->bufsize += len;
120 }
121
122 static void clear_buffer(void)
123 {
124         if (current->buffer)
125                 *current->buffer = '\0';
126         else
127                 /* force to an empty string, as buffer should not be left unallocated */
128                 add_buffer("");
129         current->bufsize = 0;
130 }
131
132 gchar *quote_fmt_get_buffer(void)
133 {
134         if (current != &main_expr)
135                 g_warning("Error: parser still in sub-expr mode\n");
136
137         if (error != 0)
138                 return NULL;
139         else
140                 return current->buffer;
141 }
142
143 GList *quote_fmt_get_attachments_list(void)
144 {
145         return attachments;
146 }
147
148 gint quote_fmt_get_line(void)
149 {
150         return line;
151 }
152
153 gint quote_fmt_get_cursor_pos(void)
154 {
155         return cursor_pos;      
156 }
157
158 #define INSERT(buf) \
159         if (stacksize != 0 && visible[stacksize - 1])\
160                 add_buffer(buf); \
161
162 #define INSERT_CHARACTER(chr) \
163         if (stacksize != 0 && visible[stacksize - 1]) { \
164                 gchar tmp[2]; \
165                 tmp[0] = (chr); \
166                 tmp[1] = '\0'; \
167                 add_buffer(tmp); \
168         }
169
170 void quote_fmt_reset_vartable(void)
171 {
172         if (var_table) {
173                 g_hash_table_destroy(var_table);
174                 var_table = NULL;
175         }
176         if (attachments) {
177                 GList *cur = attachments;
178                 while (cur) {
179                         g_free(cur->data);
180                         cur = g_list_next(cur);
181                 }
182                 g_list_free(attachments);
183                 attachments = NULL;
184         }
185 }
186
187 #ifdef USE_ASPELL
188 void quote_fmt_init(MsgInfo *info, const gchar *my_quote_str,
189                     const gchar *my_body, gboolean my_dry_run,
190                         PrefsAccount *compose_account,
191                         gboolean string_is_escaped,
192                         GtkAspell *compose_gtkaspell)
193 #else
194 void quote_fmt_init(MsgInfo *info, const gchar *my_quote_str,
195                     const gchar *my_body, gboolean my_dry_run,
196                         PrefsAccount *compose_account,
197                         gboolean string_is_escaped)
198 #endif
199 {
200         quote_str = my_quote_str;
201         body = my_body;
202         msginfo = info;
203         account = compose_account;
204 #ifdef USE_ASPELL
205         gchar *dict = gtkaspell_get_default_dictionary(compose_gtkaspell);
206         if (dict)
207                 strncpy2(default_dictionary, dict, sizeof(default_dictionary));
208         else
209                 *default_dictionary = '\0';
210 #endif
211         dry_run = my_dry_run;
212         stacksize = 0;
213         add_visibility(TRUE);
214         main_expr.bufmax = 0;
215         sub_expr.bufmax = 0;
216         current = &main_expr;
217         clear_buffer();
218         error = 0;
219         line = 1;
220         escaped_string = string_is_escaped;
221
222         if (!var_table)
223                 var_table = g_hash_table_new_full(g_str_hash, g_str_equal, 
224                                 g_free, g_free);
225
226         /*
227          * force LEX initialization
228          */
229         quote_fmt_firsttime = 1;
230         cursor_pos = -1;
231 }
232
233 void quote_fmterror(char *str)
234 {
235         g_warning("Error: %s at line %d\n", str, line);
236         error = 1;
237 }
238
239 int quote_fmtwrap(void)
240 {
241         return 1;
242 }
243
244 static int isseparator(int ch)
245 {
246         return g_ascii_isspace(ch) || ch == '.' || ch == '-';
247 }
248
249 static void quote_fmt_show_date(const MsgInfo *msginfo, const gchar *format)
250 {
251         char  result[100];
252         char *rptr;
253         char  zone[6];
254         struct tm lt;
255         const char *fptr;
256         const char *zptr;
257
258         if (!msginfo->date)
259                 return;
260         
261         /* 
262          * ALF - GNU C's strftime() has a nice format specifier 
263          * for time zone offset (%z). Non-standard however, so 
264          * emulate it.
265          */
266
267 #define RLEFT (sizeof result) - (rptr - result) 
268 #define STR_SIZE(x) (sizeof (x) - 1)
269
270         zone[0] = 0;
271
272         if (procheader_date_parse_to_tm(msginfo->date, &lt, zone)) {
273                 /*
274                  * break up format string in tiny bits delimited by valid %z's and 
275                  * feed it to strftime(). don't forget that '%%z' mean literal '%z'.
276                  */
277                 for (rptr = result, fptr = format; fptr && *fptr && rptr < &result[sizeof result - 1];) {
278                         int         perc;
279                         const char *p;
280                         char       *tmp;
281                         
282                         if (NULL != (zptr = strstr(fptr, "%z"))) {
283                                 /*
284                                  * count nr. of prepended percent chars
285                                  */
286                                 for (perc = 0, p = zptr; p && p >= format && *p == '%'; p--, perc++)
287                                         ;
288                                 /*
289                                  * feed to strftime()
290                                  */
291                                 tmp = g_strndup(fptr, zptr - fptr + (perc % 2 ? 0 : STR_SIZE("%z")));
292                                 if (tmp) {
293                                         rptr += strftime(rptr, RLEFT, tmp, &lt);
294                                         g_free(tmp);
295                                 }
296                                 /*
297                                  * append time zone offset
298                                  */
299                                 if (zone[0] && perc % 2) 
300                                         rptr += g_snprintf(rptr, RLEFT, "%s", zone);
301                                 fptr = zptr + STR_SIZE("%z");
302                         } else {
303                                 rptr += strftime(rptr, RLEFT, fptr, &lt);
304                                 fptr  = NULL;
305                         }
306                 }
307                 
308                 if (g_utf8_validate(result, -1, NULL)) {
309                         INSERT(result);
310                 } else {
311                         gchar *utf = conv_codeset_strdup(result, 
312                                 conv_get_locale_charset_str_no_utf8(),
313                                 CS_INTERNAL);
314                         if (utf == NULL || 
315                             !g_utf8_validate(utf, -1, NULL)) {
316                                 g_free(utf);
317                                 utf = g_malloc(strlen(result)*2+1);
318                                 conv_localetodisp(utf, 
319                                         strlen(result)*2+1, result);
320                         }
321                         if (g_utf8_validate(utf, -1, NULL)) {
322                                 INSERT(utf);
323                         }
324                         g_free(utf);
325                 }
326         }
327 #undef STR_SIZE                 
328 #undef RLEFT                    
329 }               
330
331 static void quote_fmt_show_first_name(const MsgInfo *msginfo)
332 {
333         guchar *p;
334         gchar *str;
335
336         if (!msginfo->fromname)
337                 return; 
338         
339         p = (guchar*)strchr(msginfo->fromname, ',');
340         if (p != NULL) {
341                 /* fromname is like "Duck, Donald" */
342                 p++;
343                 while (*p && isspace(*p)) p++;
344                 str = alloca(strlen((char *)p) + 1);
345                 if (str != NULL) {
346                         strcpy(str, (char *)p);
347                         INSERT(str);
348                 }
349         } else {
350                 /* fromname is like "Donald Duck" */
351                 str = alloca(strlen(msginfo->fromname) + 1);
352                 if (str != NULL) {
353                         strcpy(str, msginfo->fromname);
354                         p = (guchar *)str;
355                         while (*p && !isspace(*p)) p++;
356                         *p = '\0';
357                         INSERT(str);
358                 }
359         }
360 }
361
362 static void quote_fmt_show_last_name(const MsgInfo *msginfo)
363 {
364         gchar *p;
365         gchar *str;
366
367         /* This probably won't work together very well with Middle
368            names and the like - thth */
369         if (!msginfo->fromname) 
370                 return;
371
372         str = alloca(strlen(msginfo->fromname) + 1);
373         if (str != NULL) {
374                 strcpy(str, msginfo->fromname);
375                 p = strchr(str, ',');
376                 if (p != NULL) {
377                         /* fromname is like "Duck, Donald" */
378                         *p = '\0';
379                         INSERT(str);
380                 } else {
381                         /* fromname is like "Donald Duck" */
382                         p = str;
383                         while (*p && !isspace(*p)) p++;
384                         if (*p) {
385                             /* We found a space. Get first 
386                              none-space char and insert
387                              rest of string from there. */
388                             while (*p && isspace(*p)) p++;
389                             if (*p) {
390                                 INSERT(p);
391                             } else {
392                                 /* If there is no none-space 
393                                  char, just insert whole 
394                                  fromname. */
395                                 INSERT(str);
396                             }
397                         } else {
398                             /* If there is no space, just 
399                              insert whole fromname. */
400                             INSERT(str);
401                         }
402                 }
403         }
404 }
405
406 static void quote_fmt_show_sender_initial(const MsgInfo *msginfo)
407 {
408 #define MAX_SENDER_INITIAL 20
409         gchar tmp[MAX_SENDER_INITIAL];
410         guchar *p;
411         gchar *cur;
412         gint len = 0;
413
414         if (!msginfo->fromname) 
415                 return;
416
417         p = (guchar *)msginfo->fromname;
418         cur = tmp;
419         while (*p) {
420                 if (*p && g_utf8_validate((gchar *)p, 1, NULL)) {
421                         *cur = toupper(*p);
422                                 cur++;
423                         len++;
424                         if (len >= MAX_SENDER_INITIAL - 1)
425                                 break;
426                 } else
427                         break;
428                 while (*p && !isseparator(*p)) p++;
429                 while (*p && isseparator(*p)) p++;
430         }
431         *cur = '\0';
432         INSERT(tmp);
433 }
434
435 static void quote_fmt_show_msg(MsgInfo *msginfo, const gchar *body,
436                                gboolean quoted, gboolean signature,
437                                const gchar *quote_str)
438 {
439         gchar buf[BUFFSIZE];
440         FILE *fp;
441
442         if (!(msginfo->folder || body))
443                 return;
444
445         if (body)
446                 fp = str_open_as_stream(body);
447         else {
448                 if (MSG_IS_ENCRYPTED(msginfo->flags))
449                         fp = procmime_get_first_encrypted_text_content(msginfo);
450                 else
451                         fp = procmime_get_first_text_content(msginfo);
452         }
453
454         if (fp == NULL)
455                 g_warning("Can't get text part\n");
456         else {
457                 while (fgets(buf, sizeof(buf), fp) != NULL) {
458                         strcrchomp(buf);
459                         
460                         if (!signature && strncmp(buf, "-- \n", 4) == 0)
461                                 break;
462                 
463                         if (quoted && quote_str)
464                                 INSERT(quote_str);
465                         
466                         INSERT(buf);
467                 }
468                 fclose(fp);
469         }
470 }
471
472 static void quote_fmt_insert_file(const gchar *filename)
473 {
474         FILE *file;
475         char buffer[256];
476         
477         if ((file = g_fopen(filename, "rb")) != NULL) {
478                 while (fgets(buffer, sizeof(buffer), file)) {
479                         INSERT(buffer);
480                 }
481                 fclose(file);
482         }
483
484 }
485
486 static void quote_fmt_insert_program_output(const gchar *progname)
487 {
488         FILE *file;
489         char buffer[256];
490
491         if ((file = popen(progname, "r")) != NULL) {
492                 while (fgets(buffer, sizeof(buffer), file)) {
493                         INSERT(buffer);
494                 }
495                 pclose(file);
496         }
497 }
498
499 static void quote_fmt_insert_user_input(const gchar *varname)
500 {
501         gchar *buf = NULL;
502         gchar *text = NULL;
503         
504         if (dry_run) 
505                 return;
506
507         if ((text = g_hash_table_lookup(var_table, varname)) == NULL) {
508                 buf = g_strdup_printf(_("Enter text to replace '%s'"), varname);
509                 text = input_dialog(_("Enter variable"), buf, "");
510                 g_free(buf);
511                 if (!text)
512                         return;
513                 g_hash_table_insert(var_table, g_strdup(varname), g_strdup(text));
514         } else {
515                 /* don't free the one in hashtable at the end */
516                 text = g_strdup(text);
517         }
518
519         if (!text)
520                 return;
521         INSERT(text);
522         g_free(text);
523 }
524
525 static void quote_fmt_attach_file(const gchar *filename)
526 {
527         attachments = g_list_append(attachments, g_strdup(filename));
528 }
529
530 static gchar *quote_fmt_complete_address(const gchar *addr)
531 {
532         gint count;
533         gchar *res, *tmp, *email_addr;
534         gchar **split;
535
536         debug_print("quote_fmt_complete_address: %s\n", addr);
537         if (addr == NULL)
538                 return NULL;
539
540         /* if addr is a list of message, try the 1st element only */
541         split = g_strsplit(addr, ",", -1);
542         if (!split || !split[0] || *split[0] == '\0') {
543                 g_strfreev(split);
544                 return NULL;
545         }
546
547         Xstrdup_a(email_addr, split[0], return NULL);
548         extract_address(email_addr);
549         if (!*email_addr) {
550                 g_strfreev(split);
551                 return NULL;
552         }
553
554         res = NULL;
555         start_address_completion(NULL);
556         if (1 < (count = complete_address(email_addr))) {
557                 tmp = get_complete_address(1);
558                 res = procheader_get_fromname(tmp);
559                 g_free(tmp);
560         }
561         end_address_completion();
562         g_strfreev(split);
563
564         debug_print("quote_fmt_complete_address: matched %s\n", res);
565         return res;
566 }
567
568 %}
569
570 %union {
571         char chr;
572         char str[256];
573 }
574
575 /* tokens SHOW */
576 %token SHOW_NEWSGROUPS
577 %token SHOW_DATE SHOW_FROM SHOW_FULLNAME SHOW_FIRST_NAME SHOW_LAST_NAME
578 %token SHOW_SENDER_INITIAL SHOW_SUBJECT SHOW_TO SHOW_MESSAGEID
579 %token SHOW_PERCENT SHOW_CC SHOW_REFERENCES SHOW_MESSAGE
580 %token SHOW_QUOTED_MESSAGE SHOW_BACKSLASH SHOW_TAB SHOW_MAIL_ADDRESS
581 %token SHOW_QUOTED_MESSAGE_NO_SIGNATURE SHOW_MESSAGE_NO_SIGNATURE
582 %token SHOW_EOL SHOW_QUESTION_MARK SHOW_EXCLAMATION_MARK SHOW_PIPE SHOW_OPARENT SHOW_CPARENT
583 %token SHOW_ACCOUNT_FULL_NAME SHOW_ACCOUNT_MAIL_ADDRESS SHOW_ACCOUNT_NAME SHOW_ACCOUNT_ORGANIZATION
584 %token SHOW_ACCOUNT_DICT
585 %token SHOW_DICT SHOW_TAGS
586 %token SHOW_ADDRESSBOOK_COMPLETION_FOR_CC
587 %token SHOW_ADDRESSBOOK_COMPLETION_FOR_FROM
588 %token SHOW_ADDRESSBOOK_COMPLETION_FOR_TO
589 /* tokens QUERY */
590 %token QUERY_DATE QUERY_FROM
591 %token QUERY_FULLNAME QUERY_SUBJECT QUERY_TO QUERY_NEWSGROUPS
592 %token QUERY_MESSAGEID QUERY_CC QUERY_REFERENCES
593 %token QUERY_ACCOUNT_FULL_NAME QUERY_ACCOUNT_ORGANIZATION QUERY_ACCOUNT_DICT
594 %token QUERY_DICT
595 %token QUERY_CC_FOUND_IN_ADDRESSBOOK
596 %token QUERY_FROM_FOUND_IN_ADDRESSBOOK
597 %token QUERY_TO_FOUND_IN_ADDRESSBOOK
598 /* tokens QUERY_NOT */
599 %token QUERY_NOT_DATE QUERY_NOT_FROM
600 %token QUERY_NOT_FULLNAME QUERY_NOT_SUBJECT QUERY_NOT_TO QUERY_NOT_NEWSGROUPS
601 %token QUERY_NOT_MESSAGEID QUERY_NOT_CC QUERY_NOT_REFERENCES
602 %token QUERY_NOT_ACCOUNT_FULL_NAME QUERY_NOT_ACCOUNT_ORGANIZATION QUERY_NOT_ACCOUNT_DICT
603 %token QUERY_NOT_DICT
604 %token QUERY_NOT_CC_FOUND_IN_ADDRESSBOOK
605 %token QUERY_NOT_FROM_FOUND_IN_ADDRESSBOOK
606 %token QUERY_NOT_TO_FOUND_IN_ADDRESSBOOK
607 /* other tokens */
608 %token INSERT_FILE INSERT_PROGRAMOUTPUT INSERT_USERINPUT
609 %token ATTACH_FILE
610 %token OPARENT CPARENT
611 %token CHARACTER
612 %token SHOW_DATE_EXPR
613 %token SET_CURSOR_POS
614
615 %start quote_fmt
616
617 %token <chr> CHARACTER
618 %type <chr> character
619 %type <str> string
620
621 %%
622
623 quote_fmt:
624         character_or_special_or_insert_or_query_list ;
625
626 sub_expr:
627         character_or_special_list ;
628
629 character_or_special_or_insert_or_query_list:
630         character_or_special_or_insert_or_query character_or_special_or_insert_or_query_list
631         | character_or_special_or_insert_or_query ;
632
633 character_or_special_list:
634         character_or_special character_or_special_list
635         | character_or_special ;
636
637 character_or_special_or_insert_or_query:
638         character_or_special
639         | query
640         | query_not
641         | insert
642         | attach ;
643
644 character_or_special:
645         special
646         | character
647         {
648                 INSERT_CHARACTER($1);
649         };
650
651 character:
652         CHARACTER
653         ;
654
655 string:
656         CHARACTER
657         {
658                 $$[0] = $1;
659                 $$[1] = '\0';
660         }
661         | string CHARACTER
662         {
663                 size_t len;
664                 
665                 strncpy($$, $1, sizeof($$));
666                 $$[sizeof($$) - 1] = '\0';
667                 len = strlen($$);
668                 if (len + 1 < sizeof($$)) {
669                         $$[len + 1] = '\0';
670                         $$[len] = $2;
671                 }
672         };
673
674 special:
675         SHOW_NEWSGROUPS
676         {
677                 if (msginfo->newsgroups)
678                         INSERT(msginfo->newsgroups);
679         }
680         | SHOW_DATE_EXPR OPARENT string CPARENT
681         {
682                 quote_fmt_show_date(msginfo, $3);
683         }
684         | SHOW_DATE
685         {
686                 if (msginfo->date)
687                         INSERT(msginfo->date);
688         }
689         | SHOW_FROM
690         {
691                 if (msginfo->from)
692                         INSERT(msginfo->from);
693         }
694         | SHOW_MAIL_ADDRESS
695         {
696                 if (msginfo->from) {
697                         gchar *stripped_address = g_strdup(msginfo->from);
698                         extract_address(stripped_address);
699                         INSERT(stripped_address);
700                         g_free(stripped_address);
701                 }
702         }
703         | SHOW_FULLNAME
704         {
705                 if (msginfo->fromname)
706                         INSERT(msginfo->fromname);
707         }
708         | SHOW_FIRST_NAME
709         {
710                 quote_fmt_show_first_name(msginfo);
711         }
712         | SHOW_LAST_NAME
713     {
714                 quote_fmt_show_last_name(msginfo);
715         }
716         | SHOW_SENDER_INITIAL
717         {
718                 quote_fmt_show_sender_initial(msginfo);
719         }
720         | SHOW_SUBJECT
721         {
722                 if (msginfo->subject)
723                         INSERT(msginfo->subject);
724         }
725         | SHOW_TO
726         {
727                 if (msginfo->to)
728                         INSERT(msginfo->to);
729         }
730         | SHOW_MESSAGEID
731         {
732                 if (msginfo->msgid)
733                         INSERT(msginfo->msgid);
734         }
735         | SHOW_PERCENT
736         {
737                 INSERT("%");
738         }
739         | SHOW_CC
740         {
741                 if (msginfo->cc)
742                         INSERT(msginfo->cc);
743         }
744         | SHOW_REFERENCES
745         {
746                 GSList *item;
747
748                 INSERT(msginfo->inreplyto);
749                 for (item = msginfo->references; item != NULL; item = g_slist_next(item))
750                         if (item->data)
751                                 INSERT(item->data);
752         }
753         | SHOW_MESSAGE
754         {
755                 quote_fmt_show_msg(msginfo, body, FALSE, TRUE, quote_str);
756         }
757         | SHOW_QUOTED_MESSAGE
758         {
759                 quote_fmt_show_msg(msginfo, body, TRUE, TRUE, quote_str);
760         }
761         | SHOW_MESSAGE_NO_SIGNATURE
762         {
763                 quote_fmt_show_msg(msginfo, body, FALSE, FALSE, quote_str);
764         }
765         | SHOW_QUOTED_MESSAGE_NO_SIGNATURE
766         {
767                 quote_fmt_show_msg(msginfo, body, TRUE, FALSE, quote_str);
768         }
769         | SHOW_ACCOUNT_FULL_NAME
770         {
771                 if (account && account->name)
772                         INSERT(account->name);
773         }
774         | SHOW_ACCOUNT_MAIL_ADDRESS
775         {
776                 if (account && account->address)
777                         INSERT(account->address);
778         }
779         | SHOW_ACCOUNT_NAME
780         {
781                 if (account && account->account_name)
782                         INSERT(account->account_name);
783         }
784         | SHOW_ACCOUNT_ORGANIZATION
785         {
786                 if (account && account->organization)
787                         INSERT(account->organization);
788         }
789         | SHOW_ACCOUNT_DICT
790         {
791 #ifdef USE_ASPELL
792                 if (account && account->enable_default_dictionary) {
793                         gchar *dictname = g_path_get_basename(account->default_dictionary);
794                         INSERT(dictname);
795                         g_free(dictname);
796                 }
797 #endif
798         }
799         | SHOW_DICT
800         {
801 #ifdef USE_ASPELL
802                 INSERT(default_dictionary);
803 #endif
804         }
805         | SHOW_TAGS
806         {
807                 gchar *tags = procmsg_msginfo_get_tags_str(msginfo);
808                 if (tags) {
809                         INSERT(tags);
810                 }
811                 g_free(tags);
812         }
813         | SHOW_BACKSLASH
814         {
815                 INSERT("\\");
816         }
817         | SHOW_TAB
818         {
819                 INSERT("\t");
820         }
821         | SHOW_EOL
822         {
823                 INSERT("\n");
824         }
825         | SHOW_QUESTION_MARK
826         {
827                 INSERT("?");
828         }
829         | SHOW_EXCLAMATION_MARK
830         {
831                 INSERT("!");
832         }
833         | SHOW_PIPE
834         {
835                 INSERT("|");
836         }
837         | SHOW_OPARENT
838         {
839                 INSERT("{");
840         }
841         | SHOW_CPARENT
842         {
843                 INSERT("}");
844         }
845         | SET_CURSOR_POS
846         {
847                 if (current->buffer)
848                         cursor_pos = g_utf8_strlen(current->buffer, -1);
849                 else
850                         cursor_pos = 0;
851         }
852         | SHOW_ADDRESSBOOK_COMPLETION_FOR_CC
853         {
854                 gchar *tmp = quote_fmt_complete_address(msginfo->cc);
855                 if (tmp) {
856                         INSERT(tmp);
857                         g_free(tmp);
858                 }
859         }
860         | SHOW_ADDRESSBOOK_COMPLETION_FOR_FROM
861         {
862                 gchar *tmp = quote_fmt_complete_address(msginfo->from);
863                 if (tmp) {
864                         INSERT(tmp);
865                         g_free(tmp);
866                 }
867         }
868         | SHOW_ADDRESSBOOK_COMPLETION_FOR_TO
869         {
870                 gchar *tmp = quote_fmt_complete_address(msginfo->to);
871                 if (tmp) {
872                         INSERT(tmp);
873                         g_free(tmp);
874                 }
875         };
876
877 query:
878         QUERY_DATE
879         {
880                 add_visibility(msginfo->date != NULL);
881         }
882         OPARENT quote_fmt CPARENT
883         {
884                 remove_visibility();
885         }
886         | QUERY_FROM
887         {
888                 add_visibility(msginfo->from != NULL);
889         }
890         OPARENT quote_fmt CPARENT
891         {
892                 remove_visibility();
893         }
894         | QUERY_FULLNAME
895         {
896                 add_visibility(msginfo->fromname != NULL);
897         }
898         OPARENT quote_fmt CPARENT
899         {
900                 remove_visibility();
901         }
902         | QUERY_SUBJECT
903         {
904                 add_visibility(msginfo->subject != NULL);
905         }
906         OPARENT quote_fmt CPARENT
907         {
908                 remove_visibility();
909         }
910         | QUERY_TO
911         {
912                 add_visibility(msginfo->to != NULL);
913         }
914         OPARENT quote_fmt CPARENT
915         {
916                 remove_visibility();
917         }
918         | QUERY_NEWSGROUPS
919         {
920                 add_visibility(msginfo->newsgroups != NULL);
921         }
922         OPARENT quote_fmt CPARENT
923         {
924                 remove_visibility();
925         }
926         | QUERY_MESSAGEID
927         {
928                 add_visibility(msginfo->msgid != NULL);
929         }
930         OPARENT quote_fmt CPARENT
931         {
932                 remove_visibility();
933         }
934         | QUERY_CC
935         {
936                 add_visibility(msginfo->cc != NULL);
937         }
938         OPARENT quote_fmt CPARENT
939         {
940                 remove_visibility();
941         }
942         | QUERY_REFERENCES
943         {
944                 gboolean found;
945                 GSList *item;
946
947                 found = (msginfo->inreplyto != NULL);
948                 for (item = msginfo->references; found == FALSE && item != NULL; item = g_slist_next(item))
949                         if (item->data)
950                                 found = TRUE;
951                 add_visibility(found == TRUE);
952         }
953         OPARENT quote_fmt CPARENT
954         {
955                 remove_visibility();
956         }
957         | QUERY_ACCOUNT_FULL_NAME
958         {
959                 add_visibility(account != NULL && account->name != NULL);
960         }
961         OPARENT quote_fmt CPARENT
962         {
963                 remove_visibility();
964         }
965         | QUERY_ACCOUNT_ORGANIZATION
966         {
967                 add_visibility(account != NULL && account->organization != NULL);
968         }
969         OPARENT quote_fmt CPARENT
970         {
971                 remove_visibility();
972         }
973         | QUERY_ACCOUNT_DICT
974         {
975 #ifdef USE_ASPELL
976                 add_visibility(account != NULL && account->enable_default_dictionary == TRUE &&
977                                 account->default_dictionary != NULL && *account->default_dictionary != '\0');
978 #else
979                 add_visibility(FALSE);
980 #endif
981         }
982         OPARENT quote_fmt CPARENT
983         {
984                 remove_visibility();
985         }
986         | QUERY_DICT
987         {
988 #ifdef USE_ASPELL
989                 add_visibility(*default_dictionary != '\0');
990 #else
991                 add_visibility(FALSE);
992 #endif
993         }
994         OPARENT quote_fmt CPARENT
995         {
996                 remove_visibility();
997         }
998         | QUERY_CC_FOUND_IN_ADDRESSBOOK
999         {
1000                 gchar *tmp = quote_fmt_complete_address(msginfo->cc);
1001                 add_visibility(tmp != NULL && *tmp != '\0');
1002                 g_free(tmp);
1003         }
1004         OPARENT quote_fmt CPARENT
1005         {
1006                 remove_visibility();
1007         }
1008         | QUERY_FROM_FOUND_IN_ADDRESSBOOK
1009         {
1010                 gchar *tmp = quote_fmt_complete_address(msginfo->from);
1011                 add_visibility(tmp != NULL && *tmp != '\0');
1012                 g_free(tmp);
1013         }
1014         OPARENT quote_fmt CPARENT
1015         {
1016                 remove_visibility();
1017         }
1018         | QUERY_TO_FOUND_IN_ADDRESSBOOK
1019         {
1020                 gchar *tmp = quote_fmt_complete_address(msginfo->to);
1021                 add_visibility(tmp != NULL && *tmp != '\0');
1022                 g_free(tmp);
1023         }
1024         OPARENT quote_fmt CPARENT
1025         {
1026                 remove_visibility();
1027         };
1028
1029 query_not:
1030         QUERY_NOT_DATE
1031         {
1032                 add_visibility(msginfo->date == NULL);
1033         }
1034         OPARENT quote_fmt CPARENT
1035         {
1036                 remove_visibility();
1037         }
1038         | QUERY_NOT_FROM
1039         {
1040                 add_visibility(msginfo->from == NULL);
1041         }
1042         OPARENT quote_fmt CPARENT
1043         {
1044                 remove_visibility();
1045         }
1046         | QUERY_NOT_FULLNAME
1047         {
1048                 add_visibility(msginfo->fromname == NULL);
1049         }
1050         OPARENT quote_fmt CPARENT
1051         {
1052                 remove_visibility();
1053         }
1054         | QUERY_NOT_SUBJECT
1055         {
1056                 add_visibility(msginfo->subject == NULL);
1057         }
1058         OPARENT quote_fmt CPARENT
1059         {
1060                 remove_visibility();
1061         }
1062         | QUERY_NOT_TO
1063         {
1064                 add_visibility(msginfo->to == NULL);
1065         }
1066         OPARENT quote_fmt CPARENT
1067         {
1068                 remove_visibility();
1069         }
1070         | QUERY_NOT_NEWSGROUPS
1071         {
1072                 add_visibility(msginfo->newsgroups == NULL);
1073         }
1074         OPARENT quote_fmt CPARENT
1075         {
1076                 remove_visibility();
1077         }
1078         | QUERY_NOT_MESSAGEID
1079         {
1080                 add_visibility(msginfo->msgid == NULL);
1081         }
1082         OPARENT quote_fmt CPARENT
1083         {
1084                 remove_visibility();
1085         }
1086         | QUERY_NOT_CC
1087         {
1088                 add_visibility(msginfo->cc == NULL);
1089         }
1090         OPARENT quote_fmt CPARENT
1091         {
1092                 remove_visibility();
1093         }
1094         | QUERY_NOT_REFERENCES
1095         {
1096                 gboolean found;
1097                 GSList *item;
1098
1099                 found = (msginfo->inreplyto != NULL);
1100                 for (item = msginfo->references; found == FALSE && item != NULL; item = g_slist_next(item))
1101                         if (item->data)
1102                                 found = TRUE;
1103                 add_visibility(found == FALSE);
1104         }
1105         OPARENT quote_fmt CPARENT
1106         {
1107                 remove_visibility();
1108         }
1109         | QUERY_NOT_ACCOUNT_FULL_NAME
1110         {
1111                 add_visibility(account == NULL || account->name == NULL);
1112         }
1113         OPARENT quote_fmt CPARENT
1114         {
1115                 remove_visibility();
1116         }
1117         | QUERY_NOT_ACCOUNT_ORGANIZATION
1118         {
1119                 add_visibility(account == NULL || account->organization == NULL);
1120         }
1121         OPARENT quote_fmt CPARENT
1122         {
1123                 remove_visibility();
1124         }
1125         | QUERY_NOT_ACCOUNT_DICT
1126         {
1127 #ifdef USE_ASPELL
1128                 add_visibility(account == NULL || account->enable_default_dictionary == FALSE
1129                                 || *account->default_dictionary == '\0');
1130 #else
1131                 add_visibility(FALSE);
1132 #endif
1133         }
1134         OPARENT quote_fmt CPARENT
1135         {
1136                 remove_visibility();
1137         }
1138         | QUERY_NOT_DICT
1139         {
1140 #ifdef USE_ASPELL
1141                 add_visibility(*default_dictionary == '\0');
1142 #else
1143                 add_visibility(FALSE);
1144 #endif
1145         }
1146         OPARENT quote_fmt CPARENT
1147         {
1148                 remove_visibility();
1149         }
1150         | QUERY_NOT_CC_FOUND_IN_ADDRESSBOOK
1151         {
1152                 gchar *tmp = quote_fmt_complete_address(msginfo->cc);
1153                 add_visibility(tmp == NULL || *tmp == '\0');
1154                 g_free(tmp);
1155         }
1156         OPARENT quote_fmt CPARENT
1157         {
1158                 remove_visibility();
1159         }
1160         | QUERY_NOT_FROM_FOUND_IN_ADDRESSBOOK
1161         {
1162                 gchar *tmp = quote_fmt_complete_address(msginfo->from);
1163                 add_visibility(tmp == NULL || *tmp == '\0');
1164                 g_free(tmp);
1165         }
1166         OPARENT quote_fmt CPARENT
1167         {
1168                 remove_visibility();
1169         }
1170         | QUERY_NOT_TO_FOUND_IN_ADDRESSBOOK
1171         {
1172                 gchar *tmp = quote_fmt_complete_address(msginfo->to);
1173                 add_visibility(tmp == NULL || *tmp == '\0');
1174                 g_free(tmp);
1175         }
1176         OPARENT quote_fmt CPARENT
1177         {
1178                 remove_visibility();
1179         };
1180
1181 insert:
1182         INSERT_FILE
1183         {
1184                 current = &sub_expr;
1185                 clear_buffer();
1186         }
1187         OPARENT sub_expr CPARENT
1188         {
1189                 current = &main_expr;
1190                 if (!dry_run) {
1191                         quote_fmt_insert_file(sub_expr.buffer);
1192                 }
1193         }
1194         | INSERT_PROGRAMOUTPUT
1195         {
1196                 current = &sub_expr;
1197                 clear_buffer();
1198         }
1199         OPARENT sub_expr CPARENT
1200         {
1201                 current = &main_expr;
1202                 if (!dry_run) {
1203                         quote_fmt_insert_program_output(sub_expr.buffer);
1204                 }
1205         }
1206         | INSERT_USERINPUT
1207         {
1208                 current = &sub_expr;
1209                 clear_buffer();
1210         }
1211         OPARENT sub_expr CPARENT
1212         {
1213                 current = &main_expr;
1214                 if (!dry_run) {
1215                         quote_fmt_insert_user_input(sub_expr.buffer);
1216                 }
1217         };
1218
1219 attach:
1220         ATTACH_FILE
1221         {
1222                 current = &sub_expr;
1223                 clear_buffer();
1224         }
1225         OPARENT sub_expr CPARENT
1226         {
1227                 current = &main_expr;
1228                 if (!dry_run) {
1229                         quote_fmt_attach_file(sub_expr.buffer);
1230                 }
1231         };