2006-03-26 [colin] 2.0.0cvs173
[claws.git] / src / quote_fmt_parse.y
1 /*
2  * Sylpheed -- a GTK+ based, lightweight, and fast e-mail client
3  * Copyright (C) 1999-2006 Hiroyuki Yamamoto and the Sylpheed-Claws 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 <ctype.h>
26
27 #include "procmsg.h"
28 #include "procmime.h"
29 #include "utils.h"
30 #include "codeconv.h"
31 #include "procheader.h"
32
33 #include "quote_fmt.h"
34 #include "quote_fmt_lex.h"
35
36 /* decl */
37 /*
38 flex quote_fmt.l
39 bison -p quote_fmt quote_fmt.y
40 */
41
42 int yylex(void);
43
44 static MsgInfo *msginfo = NULL;
45 static gboolean *visible = NULL;
46 static gboolean dry_run = FALSE;
47 static gint maxsize = 0;
48 static gint stacksize = 0;
49
50 static gchar *buffer = NULL;
51 static gint bufmax = 0;
52 static gint bufsize = 0;
53 static const gchar *quote_str = NULL;
54 static const gchar *body = NULL;
55 static gint error = 0;
56
57 static gint cursor_pos  = -1;
58
59 extern int quote_fmt_firsttime;
60
61 static void add_visibility(gboolean val)
62 {
63         stacksize++;
64         if (maxsize < stacksize) {
65                 maxsize += 128;
66                 visible = g_realloc(visible, maxsize * sizeof(gboolean));
67                 if (visible == NULL)
68                         maxsize = 0;
69         }
70
71         visible[stacksize - 1] = val;
72 }
73
74 static void remove_visibility(void)
75 {
76         stacksize--;
77 }
78
79 static void add_buffer(const gchar *s)
80 {
81         gint len;
82
83         len = strlen(s);
84         if (bufsize + len + 1 > bufmax) {
85                 if (bufmax == 0)
86                         bufmax = 128;
87                 while (bufsize + len + 1 > bufmax)
88                         bufmax *= 2;
89                 buffer = g_realloc(buffer, bufmax);
90         }
91         strcpy(buffer + bufsize, s);
92         bufsize += len;
93 }
94
95 gchar *quote_fmt_get_buffer(void)
96 {
97         if (error != 0)
98                 return NULL;
99         else
100                 return buffer;
101 }
102
103 gint quote_fmt_get_cursor_pos(void)
104 {
105         return cursor_pos;      
106 }
107
108 #define INSERT(buf) \
109         if (stacksize != 0 && visible[stacksize - 1]) \
110                 add_buffer(buf)
111
112 #define INSERT_CHARACTER(chr) \
113         if (stacksize != 0 && visible[stacksize - 1]) { \
114                 gchar tmp[2]; \
115                 tmp[0] = (chr); \
116                 tmp[1] = '\0'; \
117                 add_buffer(tmp); \
118         }
119
120 void quote_fmt_init(MsgInfo *info, const gchar *my_quote_str,
121                     const gchar *my_body, gboolean my_dry_run)
122 {
123         quote_str = my_quote_str;
124         body = my_body;
125         msginfo = info;
126         dry_run = my_dry_run;
127         stacksize = 0;
128         add_visibility(TRUE);
129         if (buffer != NULL)
130                 *buffer = 0;
131         bufsize = 0;
132         error = 0;
133         /*
134          * force LEX initialization
135          */
136         quote_fmt_firsttime = 1;
137         cursor_pos = -1;
138 }
139
140 void quote_fmterror(char *str)
141 {
142         g_warning("Error: %s\n", str);
143         error = 1;
144 }
145
146 int quote_fmtwrap(void)
147 {
148         return 1;
149 }
150
151 static int isseparator(int ch)
152 {
153         return g_ascii_isspace(ch) || ch == '.' || ch == '-';
154 }
155
156 static void quote_fmt_show_date(const MsgInfo *msginfo, const gchar *format)
157 {
158         char  result[100];
159         char *rptr;
160         char  zone[6];
161         struct tm lt;
162         const char *fptr;
163         const char *zptr;
164
165         if (!msginfo->date)
166                 return;
167         
168         /* 
169          * ALF - GNU C's strftime() has a nice format specifier 
170          * for time zone offset (%z). Non-standard however, so 
171          * emulate it.
172          */
173
174 #define RLEFT (sizeof result) - (rptr - result) 
175 #define STR_SIZE(x) (sizeof (x) - 1)
176
177         zone[0] = 0;
178
179         if (procheader_date_parse_to_tm(msginfo->date, &lt, zone)) {
180                 /*
181                  * break up format string in tiny bits delimited by valid %z's and 
182                  * feed it to strftime(). don't forget that '%%z' mean literal '%z'.
183                  */
184                 for (rptr = result, fptr = format; fptr && *fptr && rptr < &result[sizeof result - 1];) {
185                         int         perc;
186                         const char *p;
187                         char       *tmp;
188                         
189                         if (NULL != (zptr = strstr(fptr, "%z"))) {
190                                 /*
191                                  * count nr. of prepended percent chars
192                                  */
193                                 for (perc = 0, p = zptr; p && p >= format && *p == '%'; p--, perc++)
194                                         ;
195                                 /*
196                                  * feed to strftime()
197                                  */
198                                 tmp = g_strndup(fptr, zptr - fptr + (perc % 2 ? 0 : STR_SIZE("%z")));
199                                 if (tmp) {
200                                         rptr += strftime(rptr, RLEFT, tmp, &lt);
201                                         g_free(tmp);
202                                 }
203                                 /*
204                                  * append time zone offset
205                                  */
206                                 if (zone[0] && perc % 2) 
207                                         rptr += g_snprintf(rptr, RLEFT, "%s", zone);
208                                 fptr = zptr + STR_SIZE("%z");
209                         } else {
210                                 rptr += strftime(rptr, RLEFT, fptr, &lt);
211                                 fptr  = NULL;
212                         }
213                 }
214                 
215                 if (g_utf8_validate(result, -1, NULL)) {
216                         INSERT(result);
217                 } else {
218                         gchar *utf = conv_codeset_strdup(result, 
219                                 conv_get_locale_charset_str_no_utf8(),
220                                 CS_INTERNAL);
221                         if (utf == NULL || 
222                             !g_utf8_validate(utf, -1, NULL)) {
223                                 g_free(utf);
224                                 utf = g_malloc(strlen(result)*2+1);
225                                 conv_localetodisp(utf, 
226                                         strlen(result)*2+1, result);
227                         }
228                         if (g_utf8_validate(utf, -1, NULL)) {
229                                 INSERT(utf);
230                         }
231                         g_free(utf);
232                 }
233         }
234 #undef STR_SIZE                 
235 #undef RLEFT                    
236 }               
237
238 static void quote_fmt_show_first_name(const MsgInfo *msginfo)
239 {
240         guchar *p;
241         gchar *str;
242
243         if (!msginfo->fromname)
244                 return; 
245         
246         p = strchr(msginfo->fromname, ',');
247         if (p != NULL) {
248                 /* fromname is like "Duck, Donald" */
249                 p++;
250                 while (*p && isspace(*p)) p++;
251                 str = alloca(strlen(p) + 1);
252                 if (str != NULL) {
253                         strcpy(str, p);
254                         INSERT(str);
255                 }
256         } else {
257                 /* fromname is like "Donald Duck" */
258                 str = alloca(strlen(msginfo->fromname) + 1);
259                 if (str != NULL) {
260                         strcpy(str, msginfo->fromname);
261                         p = str;
262                         while (*p && !isspace(*p)) p++;
263                         *p = '\0';
264                         INSERT(str);
265                 }
266         }
267 }
268
269 static void quote_fmt_show_last_name(const MsgInfo *msginfo)
270 {
271         gchar *p;
272         gchar *str;
273
274         /* This probably won't work together very well with Middle
275            names and the like - thth */
276         if (!msginfo->fromname) 
277                 return;
278
279         str = alloca(strlen(msginfo->fromname) + 1);
280         if (str != NULL) {
281                 strcpy(str, msginfo->fromname);
282                 p = strchr(str, ',');
283                 if (p != NULL) {
284                         /* fromname is like "Duck, Donald" */
285                         *p = '\0';
286                         INSERT(str);
287                 } else {
288                         /* fromname is like "Donald Duck" */
289                         p = str;
290                         while (*p && !isspace(*p)) p++;
291                         if (*p) {
292                             /* We found a space. Get first 
293                              none-space char and insert
294                              rest of string from there. */
295                             while (*p && isspace(*p)) p++;
296                             if (*p) {
297                                 INSERT(p);
298                             } else {
299                                 /* If there is no none-space 
300                                  char, just insert whole 
301                                  fromname. */
302                                 INSERT(str);
303                             }
304                         } else {
305                             /* If there is no space, just 
306                              insert whole fromname. */
307                             INSERT(str);
308                         }
309                 }
310         }
311 }
312
313 static void quote_fmt_show_sender_initial(const MsgInfo *msginfo)
314 {
315 #define MAX_SENDER_INITIAL 20
316         gchar tmp[MAX_SENDER_INITIAL];
317         guchar *p;
318         gchar *cur;
319         gint len = 0;
320
321         if (!msginfo->fromname) 
322                 return;
323
324         p = msginfo->fromname;
325         cur = tmp;
326         while (*p) {
327                 if (*p && g_utf8_validate(p, 1, NULL)) {
328                         *cur = toupper(*p);
329                                 cur++;
330                         len++;
331                         if (len >= MAX_SENDER_INITIAL - 1)
332                                 break;
333                 } else
334                         break;
335                 while (*p && !isseparator(*p)) p++;
336                 while (*p && isseparator(*p)) p++;
337         }
338         *cur = '\0';
339         INSERT(tmp);
340 }
341
342 static void quote_fmt_show_msg(MsgInfo *msginfo, const gchar *body,
343                                gboolean quoted, gboolean signature,
344                                const gchar *quote_str)
345 {
346         gchar buf[BUFFSIZE];
347         FILE *fp;
348
349         if (!(msginfo->folder || body))
350                 return;
351
352         if (body)
353                 fp = str_open_as_stream(body);
354         else {
355                 if (procmime_msginfo_is_encrypted(msginfo))
356                         fp = procmime_get_first_encrypted_text_content(msginfo);
357                 else
358                         fp = procmime_get_first_text_content(msginfo);
359         }
360
361         if (fp == NULL)
362                 g_warning("Can't get text part\n");
363         else {
364                 while (fgets(buf, sizeof(buf), fp) != NULL) {
365                         strcrchomp(buf);
366                         
367                         if (!signature && strncmp(buf, "-- \n", 4) == 0)
368                                 break;
369                 
370                         if (quoted && quote_str)
371                                 INSERT(quote_str);
372                         
373                         INSERT(buf);
374                 }
375                 fclose(fp);
376         }
377 }
378
379 static void quote_fmt_insert_file(const gchar *filename)
380 {
381         FILE *file;
382         char buffer[256];
383         
384         if ((file = g_fopen(filename, "rb")) != NULL) {
385                 while (fgets(buffer, sizeof(buffer), file)) {
386                         INSERT(buffer);
387                 }
388                 fclose(file);
389         }
390
391 }
392
393 static void quote_fmt_insert_program_output(const gchar *progname)
394 {
395         FILE *file;
396         char buffer[256];
397
398         if ((file = popen(progname, "r")) != NULL) {
399                 while (fgets(buffer, sizeof(buffer), file)) {
400                         INSERT(buffer);
401                 }
402                 pclose(file);
403         }
404 }
405
406 %}
407
408 %union {
409         char chr;
410         char str[256];
411 }
412
413 %token SHOW_NEWSGROUPS
414 %token SHOW_DATE SHOW_FROM SHOW_FULLNAME SHOW_FIRST_NAME SHOW_LAST_NAME
415 %token SHOW_SENDER_INITIAL SHOW_SUBJECT SHOW_TO SHOW_MESSAGEID
416 %token SHOW_PERCENT SHOW_CC SHOW_REFERENCES SHOW_MESSAGE
417 %token SHOW_QUOTED_MESSAGE SHOW_BACKSLASH SHOW_TAB
418 %token SHOW_QUOTED_MESSAGE_NO_SIGNATURE SHOW_MESSAGE_NO_SIGNATURE
419 %token SHOW_EOL SHOW_QUESTION_MARK SHOW_PIPE SHOW_OPARENT SHOW_CPARENT
420 %token QUERY_DATE QUERY_FROM
421 %token QUERY_FULLNAME QUERY_SUBJECT QUERY_TO QUERY_NEWSGROUPS
422 %token QUERY_MESSAGEID QUERY_CC QUERY_REFERENCES
423 %token INSERT_FILE INSERT_PROGRAMOUTPUT
424 %token OPARENT CPARENT
425 %token CHARACTER
426 %token SHOW_DATE_EXPR
427 %token SET_CURSOR_POS
428
429 %start quote_fmt
430
431 %token <chr> CHARACTER
432 %type <chr> character
433 %type <str> string
434
435 %%
436
437 quote_fmt:
438         character_or_special_or_insert_or_query_list;
439
440 character_or_special_or_insert_or_query_list:
441         character_or_special_or_insert_or_query character_or_special_or_insert_or_query_list
442         | character_or_special_or_insert_or_query ;
443
444 character_or_special_or_insert_or_query:
445         special
446         | character
447         {
448                 INSERT_CHARACTER($1);
449         }
450         | query
451         | insert ;
452
453 character:
454         CHARACTER
455         ;
456
457 string:
458         CHARACTER
459         {
460                 $$[0] = $1;
461                 $$[1] = '\0';
462         }
463         | string CHARACTER
464         {
465                 int len;
466                 
467                 strncpy($$, $1, sizeof($$));
468                 $$[sizeof($$) - 1] = '\0';
469                 len = strlen($$);
470                 if (len + 1 < sizeof($$)) {
471                         $$[len + 1] = '\0';
472                         $$[len] = $2;
473                 }
474         };
475
476 special:
477         SHOW_NEWSGROUPS
478         {
479                 if (msginfo->newsgroups)
480                         INSERT(msginfo->newsgroups);
481         }
482         | SHOW_DATE_EXPR OPARENT string CPARENT
483         {
484                 quote_fmt_show_date(msginfo, $3);
485         }
486         | SHOW_DATE
487         {
488                 if (msginfo->date)
489                         INSERT(msginfo->date);
490         }
491         | SHOW_FROM
492         {
493                 if (msginfo->from)
494                         INSERT(msginfo->from);
495         }
496         | SHOW_FULLNAME
497         {
498                 if (msginfo->fromname)
499                         INSERT(msginfo->fromname);
500         }
501         | SHOW_FIRST_NAME
502         {
503                 quote_fmt_show_first_name(msginfo);
504         }
505         | SHOW_LAST_NAME
506         {
507                 quote_fmt_show_last_name(msginfo);
508         }
509         | SHOW_SENDER_INITIAL
510         {
511                 quote_fmt_show_sender_initial(msginfo);
512         }
513         | SHOW_SUBJECT
514         {
515                 if (msginfo->subject)
516                         INSERT(msginfo->subject);
517         }
518         | SHOW_TO
519         {
520                 if (msginfo->to)
521                         INSERT(msginfo->to);
522         }
523         | SHOW_MESSAGEID
524         {
525                 if (msginfo->msgid)
526                         INSERT(msginfo->msgid);
527         }
528         | SHOW_PERCENT
529         {
530                 INSERT("%");
531         }
532         | SHOW_CC
533         {
534                 if (msginfo->cc)
535                         INSERT(msginfo->cc);
536         }
537         | SHOW_REFERENCES
538         {
539                 /* CLAWS: use in reply to header */
540                 /* if (msginfo->references)
541                         INSERT(msginfo->references); */
542         }
543         | SHOW_MESSAGE
544         {
545                 quote_fmt_show_msg(msginfo, body, FALSE, TRUE, quote_str);
546         }
547         | SHOW_QUOTED_MESSAGE
548         {
549                 quote_fmt_show_msg(msginfo, body, TRUE, TRUE, quote_str);
550         }
551         | SHOW_MESSAGE_NO_SIGNATURE
552         {
553                 quote_fmt_show_msg(msginfo, body, FALSE, FALSE, quote_str);
554         }
555         | SHOW_QUOTED_MESSAGE_NO_SIGNATURE
556         {
557                 quote_fmt_show_msg(msginfo, body, TRUE, FALSE, quote_str);
558         }
559         | SHOW_BACKSLASH
560         {
561                 INSERT("\\");
562         }
563         | SHOW_TAB
564         {
565                 INSERT("\t");
566         }
567         | SHOW_EOL
568         {
569                 INSERT("\n");
570         }
571         | SHOW_QUESTION_MARK
572         {
573                 INSERT("?");
574         }
575         | SHOW_PIPE
576         {
577                 INSERT("|");
578         }
579         | SHOW_OPARENT
580         {
581                 INSERT("{");
582         }
583         | SHOW_CPARENT
584         {
585                 INSERT("}");
586         }
587         | SET_CURSOR_POS
588         {
589                 cursor_pos = bufsize;
590         };
591
592 query:
593         QUERY_DATE
594         {
595                 add_visibility(msginfo->date != NULL);
596         }
597         OPARENT quote_fmt CPARENT
598         {
599                 remove_visibility();
600         }
601         | QUERY_FROM
602         {
603                 add_visibility(msginfo->from != NULL);
604         }
605         OPARENT quote_fmt CPARENT
606         {
607                 remove_visibility();
608         }
609         | QUERY_FULLNAME
610         {
611                 add_visibility(msginfo->fromname != NULL);
612         }
613         OPARENT quote_fmt CPARENT
614         {
615                 remove_visibility();
616         }
617         | QUERY_SUBJECT
618         {
619                 add_visibility(msginfo->subject != NULL);
620         }
621         OPARENT quote_fmt CPARENT
622         {
623                 remove_visibility();
624         }
625         | QUERY_TO
626         {
627                 add_visibility(msginfo->to != NULL);
628         }
629         OPARENT quote_fmt CPARENT
630         {
631                 remove_visibility();
632         }
633         | QUERY_NEWSGROUPS
634         {
635                 add_visibility(msginfo->newsgroups != NULL);
636         }
637         OPARENT quote_fmt CPARENT
638         {
639                 remove_visibility();
640         }
641         | QUERY_MESSAGEID
642         {
643                 add_visibility(msginfo->msgid != NULL);
644         }
645         OPARENT quote_fmt CPARENT
646         {
647                 remove_visibility();
648         }
649         | QUERY_CC
650         {
651                 add_visibility(msginfo->cc != NULL);
652         }
653         OPARENT quote_fmt CPARENT
654         {
655                 remove_visibility();
656         }
657         | QUERY_REFERENCES
658         {
659                 /* CLAWS: use in-reply-to header */
660                 /* add_visibility(msginfo->references != NULL); */
661         }
662         OPARENT quote_fmt CPARENT
663         {
664                 remove_visibility();
665         };
666
667 insert:
668         INSERT_FILE OPARENT string CPARENT
669         {
670                 if (!dry_run) {
671                         quote_fmt_insert_file($3);
672                 }
673         }
674         | INSERT_PROGRAMOUTPUT OPARENT string CPARENT
675         {
676                 if (!dry_run) {
677                         quote_fmt_insert_program_output($3);
678                 }
679         };