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