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