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