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