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