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