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