2007-10-17 [wwp] 3.0.2cvs79
[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
80 static void add_visibility(gboolean val)
81 {
82         stacksize++;
83         if (maxsize < stacksize) {
84                 maxsize += 128;
85                 visible = g_realloc(visible, maxsize * sizeof(gboolean));
86                 if (visible == NULL)
87                         maxsize = 0;
88         }
89
90         visible[stacksize - 1] = val;
91 }
92
93 static void remove_visibility(void)
94 {
95         stacksize--;
96         if (stacksize < 0) {
97                 g_warning("Error: visibility stack underflow\n");
98                 stacksize = 0;
99         }
100 }
101
102 static void add_buffer(const gchar *s)
103 {
104         gint len;
105
106         if (s == NULL)
107                 return;
108
109         len = strlen(s);
110         if (current->bufsize + len + 1 > current->bufmax) {
111                 if (current->bufmax == 0)
112                         current->bufmax = 128;
113                 while (current->bufsize + len + 1 > current->bufmax)
114                         current->bufmax *= 2;
115                 current->buffer = g_realloc(current->buffer, current->bufmax);
116         }
117         strcpy(current->buffer + current->bufsize, s);
118         current->bufsize += len;
119 }
120
121 static void clear_buffer(void)
122 {
123         if (current->buffer)
124                 *current->buffer = '\0';
125         else
126                 /* force to an empty string, as buffer should not be left unallocated */
127                 add_buffer("");
128         current->bufsize = 0;
129 }
130
131 gchar *quote_fmt_get_buffer(void)
132 {
133         if (current != &main_expr)
134                 g_warning("Error: parser still in sub-expr mode\n");
135
136         if (error != 0)
137                 return NULL;
138         else
139                 return current->buffer;
140 }
141
142 GList *quote_fmt_get_attachments_list(void)
143 {
144         fprintf(stderr, "+ get attachment list %p\n", attachments);
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                 fprintf(stderr, "+ freeing attachment list\n");
178                 GList *cur = attachments;
179                 while (cur) {
180                         fprintf(stderr, "-> %s\n", (gchar*)cur->data);
181                         g_free(cur->data);
182                         cur = g_list_next(cur);
183                 }
184                 g_list_free(attachments);
185                 attachments = NULL;
186         }
187 }
188
189 #ifdef USE_ASPELL
190 void quote_fmt_init(MsgInfo *info, const gchar *my_quote_str,
191                     const gchar *my_body, gboolean my_dry_run,
192                         PrefsAccount *compose_account,
193                         GtkAspell *compose_gtkaspell)
194 #else
195 void quote_fmt_init(MsgInfo *info, const gchar *my_quote_str,
196                     const gchar *my_body, gboolean my_dry_run,
197                         PrefsAccount *compose_account)
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
221         if (!var_table)
222                 var_table = g_hash_table_new_full(g_str_hash, g_str_equal, 
223                                 g_free, g_free);
224
225         /*
226          * force LEX initialization
227          */
228         quote_fmt_firsttime = 1;
229         cursor_pos = -1;
230 }
231
232 void quote_fmterror(char *str)
233 {
234         g_warning("Error: %s at line %d\n", str, line);
235         error = 1;
236 }
237
238 int quote_fmtwrap(void)
239 {
240         return 1;
241 }
242
243 static int isseparator(int ch)
244 {
245         return g_ascii_isspace(ch) || ch == '.' || ch == '-';
246 }
247
248 static void quote_fmt_show_date(const MsgInfo *msginfo, const gchar *format)
249 {
250         char  result[100];
251         char *rptr;
252         char  zone[6];
253         struct tm lt;
254         const char *fptr;
255         const char *zptr;
256
257         if (!msginfo->date)
258                 return;
259         
260         /* 
261          * ALF - GNU C's strftime() has a nice format specifier 
262          * for time zone offset (%z). Non-standard however, so 
263          * emulate it.
264          */
265
266 #define RLEFT (sizeof result) - (rptr - result) 
267 #define STR_SIZE(x) (sizeof (x) - 1)
268
269         zone[0] = 0;
270
271         if (procheader_date_parse_to_tm(msginfo->date, &lt, zone)) {
272                 /*
273                  * break up format string in tiny bits delimited by valid %z's and 
274                  * feed it to strftime(). don't forget that '%%z' mean literal '%z'.
275                  */
276                 for (rptr = result, fptr = format; fptr && *fptr && rptr < &result[sizeof result - 1];) {
277                         int         perc;
278                         const char *p;
279                         char       *tmp;
280                         
281                         if (NULL != (zptr = strstr(fptr, "%z"))) {
282                                 /*
283                                  * count nr. of prepended percent chars
284                                  */
285                                 for (perc = 0, p = zptr; p && p >= format && *p == '%'; p--, perc++)
286                                         ;
287                                 /*
288                                  * feed to strftime()
289                                  */
290                                 tmp = g_strndup(fptr, zptr - fptr + (perc % 2 ? 0 : STR_SIZE("%z")));
291                                 if (tmp) {
292                                         rptr += strftime(rptr, RLEFT, tmp, &lt);
293                                         g_free(tmp);
294                                 }
295                                 /*
296                                  * append time zone offset
297                                  */
298                                 if (zone[0] && perc % 2) 
299                                         rptr += g_snprintf(rptr, RLEFT, "%s", zone);
300                                 fptr = zptr + STR_SIZE("%z");
301                         } else {
302                                 rptr += strftime(rptr, RLEFT, fptr, &lt);
303                                 fptr  = NULL;
304                         }
305                 }
306                 
307                 if (g_utf8_validate(result, -1, NULL)) {
308                         INSERT(result);
309                 } else {
310                         gchar *utf = conv_codeset_strdup(result, 
311                                 conv_get_locale_charset_str_no_utf8(),
312                                 CS_INTERNAL);
313                         if (utf == NULL || 
314                             !g_utf8_validate(utf, -1, NULL)) {
315                                 g_free(utf);
316                                 utf = g_malloc(strlen(result)*2+1);
317                                 conv_localetodisp(utf, 
318                                         strlen(result)*2+1, result);
319                         }
320                         if (g_utf8_validate(utf, -1, NULL)) {
321                                 INSERT(utf);
322                         }
323                         g_free(utf);
324                 }
325         }
326 #undef STR_SIZE                 
327 #undef RLEFT                    
328 }               
329
330 static void quote_fmt_show_first_name(const MsgInfo *msginfo)
331 {
332         guchar *p;
333         gchar *str;
334
335         if (!msginfo->fromname)
336                 return; 
337         
338         p = (guchar*)strchr(msginfo->fromname, ',');
339         if (p != NULL) {
340                 /* fromname is like "Duck, Donald" */
341                 p++;
342                 while (*p && isspace(*p)) p++;
343                 str = alloca(strlen((char *)p) + 1);
344                 if (str != NULL) {
345                         strcpy(str, (char *)p);
346                         INSERT(str);
347                 }
348         } else {
349                 /* fromname is like "Donald Duck" */
350                 str = alloca(strlen(msginfo->fromname) + 1);
351                 if (str != NULL) {
352                         strcpy(str, msginfo->fromname);
353                         p = (guchar *)str;
354                         while (*p && !isspace(*p)) p++;
355                         *p = '\0';
356                         INSERT(str);
357                 }
358         }
359 }
360
361 static void quote_fmt_show_last_name(const MsgInfo *msginfo)
362 {
363         gchar *p;
364         gchar *str;
365
366         /* This probably won't work together very well with Middle
367            names and the like - thth */
368         if (!msginfo->fromname) 
369                 return;
370
371         str = alloca(strlen(msginfo->fromname) + 1);
372         if (str != NULL) {
373                 strcpy(str, msginfo->fromname);
374                 p = strchr(str, ',');
375                 if (p != NULL) {
376                         /* fromname is like "Duck, Donald" */
377                         *p = '\0';
378                         INSERT(str);
379                 } else {
380                         /* fromname is like "Donald Duck" */
381                         p = str;
382                         while (*p && !isspace(*p)) p++;
383                         if (*p) {
384                             /* We found a space. Get first 
385                              none-space char and insert
386                              rest of string from there. */
387                             while (*p && isspace(*p)) p++;
388                             if (*p) {
389                                 INSERT(p);
390                             } else {
391                                 /* If there is no none-space 
392                                  char, just insert whole 
393                                  fromname. */
394                                 INSERT(str);
395                             }
396                         } else {
397                             /* If there is no space, just 
398                              insert whole fromname. */
399                             INSERT(str);
400                         }
401                 }
402         }
403 }
404
405 static void quote_fmt_show_sender_initial(const MsgInfo *msginfo)
406 {
407 #define MAX_SENDER_INITIAL 20
408         gchar tmp[MAX_SENDER_INITIAL];
409         guchar *p;
410         gchar *cur;
411         gint len = 0;
412
413         if (!msginfo->fromname) 
414                 return;
415
416         p = (guchar *)msginfo->fromname;
417         cur = tmp;
418         while (*p) {
419                 if (*p && g_utf8_validate((gchar *)p, 1, NULL)) {
420                         *cur = toupper(*p);
421                                 cur++;
422                         len++;
423                         if (len >= MAX_SENDER_INITIAL - 1)
424                                 break;
425                 } else
426                         break;
427                 while (*p && !isseparator(*p)) p++;
428                 while (*p && isseparator(*p)) p++;
429         }
430         *cur = '\0';
431         INSERT(tmp);
432 }
433
434 static void quote_fmt_show_msg(MsgInfo *msginfo, const gchar *body,
435                                gboolean quoted, gboolean signature,
436                                const gchar *quote_str)
437 {
438         gchar buf[BUFFSIZE];
439         FILE *fp;
440
441         if (!(msginfo->folder || body))
442                 return;
443
444         if (body)
445                 fp = str_open_as_stream(body);
446         else {
447                 if (MSG_IS_ENCRYPTED(msginfo->flags))
448                         fp = procmime_get_first_encrypted_text_content(msginfo);
449                 else
450                         fp = procmime_get_first_text_content(msginfo);
451         }
452
453         if (fp == NULL)
454                 g_warning("Can't get text part\n");
455         else {
456                 while (fgets(buf, sizeof(buf), fp) != NULL) {
457                         strcrchomp(buf);
458                         
459                         if (!signature && strncmp(buf, "-- \n", 4) == 0)
460                                 break;
461                 
462                         if (quoted && quote_str)
463                                 INSERT(quote_str);
464                         
465                         INSERT(buf);
466                 }
467                 fclose(fp);
468         }
469 }
470
471 static void quote_fmt_insert_file(const gchar *filename)
472 {
473         FILE *file;
474         char buffer[256];
475         
476         if ((file = g_fopen(filename, "rb")) != NULL) {
477                 while (fgets(buffer, sizeof(buffer), file)) {
478                         INSERT(buffer);
479                 }
480                 fclose(file);
481         }
482
483 }
484
485 static void quote_fmt_insert_program_output(const gchar *progname)
486 {
487         FILE *file;
488         char buffer[256];
489
490         if ((file = popen(progname, "r")) != NULL) {
491                 while (fgets(buffer, sizeof(buffer), file)) {
492                         INSERT(buffer);
493                 }
494                 pclose(file);
495         }
496 }
497
498 static void quote_fmt_insert_user_input(const gchar *varname)
499 {
500         gchar *buf = NULL;
501         gchar *text = NULL;
502         
503         if (dry_run) 
504                 return;
505
506         if ((text = g_hash_table_lookup(var_table, varname)) == NULL) {
507                 buf = g_strdup_printf(_("Enter text to replace '%s'"), varname);
508                 text = input_dialog(_("Enter variable"), buf, "");
509                 g_free(buf);
510                 if (!text)
511                         return;
512                 g_hash_table_insert(var_table, g_strdup(varname), g_strdup(text));
513         } else {
514                 /* don't free the one in hashtable at the end */
515                 text = g_strdup(text);
516         }
517
518         if (!text)
519                 return;
520         INSERT(text);
521         g_free(text);
522 }
523
524 static void quote_fmt_attach_file(const gchar *filename)
525 {
526         fprintf(stderr, "+ adding attachment %s\n", filename);
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                 int 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         };