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