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