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