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