0.9.12 release
[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                         str = alloca(strlen(msginfo->fromname) + 1);
300                         if (str != NULL) {
301                                 strcpy(str, msginfo->fromname);
302                                 p = str;
303                                 while (*p && !isspace(*p)) p++;
304                                 *p = '\0';
305                                 INSERT(str);
306                         }
307                 }
308         }
309         | SHOW_LAST_NAME
310         {
311                 /* This probably won't work together very well with Middle
312                  names and the like - thth */
313                 if (msginfo->fromname) {
314                         gchar *p;
315                         gchar *str;
316
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                                 if (*p) {
323                                     /* We found a space. Get first none-space char and
324                                      insert rest of string from there. */
325                                     while (*p && isspace(*p)) p++;
326                                     if (*p) {
327                                         INSERT(p);
328                                     } else {
329                                         /* If there is no none-space char, just insert
330                                          whole fromname. */
331                                         INSERT(str);
332                                     }
333                                 } else {
334                                     /* If there is no space, just insert whole fromname. */
335                                     INSERT(str);
336                                 }
337                         }
338                 }
339         }
340         | SHOW_SENDER_INITIAL
341         {
342 #define MAX_SENDER_INITIAL 20
343                 if (msginfo->fromname) {
344                         gchar tmp[MAX_SENDER_INITIAL];
345                         guchar *p;
346                         gchar *cur;
347                         gint len = 0;
348
349                         p = msginfo->fromname;
350                         cur = tmp;
351                         while (*p) {
352                                 if (*p && isalnum(*p)) {
353                                         *cur = toupper(*p);
354                                                 cur++;
355                                         len++;
356                                         if (len >= MAX_SENDER_INITIAL - 1)
357                                                 break;
358                                 } else
359                                         break;
360                                 while (*p && !isseparator(*p)) p++;
361                                 while (*p && isseparator(*p)) p++;
362                         }
363                         *cur = '\0';
364                         INSERT(tmp);
365                 }
366         }
367         | SHOW_SUBJECT
368         {
369                 if (msginfo->subject)
370                         INSERT(msginfo->subject);
371         }
372         | SHOW_TO
373         {
374                 if (msginfo->to)
375                         INSERT(msginfo->to);
376         }
377         | SHOW_MESSAGEID
378         {
379                 if (msginfo->msgid)
380                         INSERT(msginfo->msgid);
381         }
382         | SHOW_PERCENT
383         {
384                 INSERT("%");
385         }
386         | SHOW_CC
387         {
388                 if (msginfo->cc)
389                         INSERT(msginfo->cc);
390         }
391         | SHOW_REFERENCES
392         {
393                 /* if (msginfo->references)
394                         INSERT(msginfo->references); */
395         }
396         | SHOW_MESSAGE
397         {
398                 if (msginfo->folder || body) {
399                         gchar buf[BUFFSIZE];
400                         FILE *fp;
401
402                         if (body)
403                                 fp = str_open_as_stream(body);
404                         else
405                                 fp = procmime_get_first_text_content(msginfo);
406
407                         if (fp == NULL)
408                                 g_warning("Can't get text part\n");
409                         else {
410                                 while (fgets(buf, sizeof(buf), fp) != NULL) {
411                                         strcrchomp(buf);
412                                         INSERT(buf);
413                                 }
414                                 fclose(fp);
415                         }
416                 }
417         }
418         | SHOW_QUOTED_MESSAGE
419         {
420                 if (msginfo->folder || body) {
421                         gchar buf[BUFFSIZE];
422                         FILE *fp;
423
424                         if (body)
425                                 fp = str_open_as_stream(body);
426                         else
427                                 fp = procmime_get_first_text_content(msginfo);
428
429                         if (fp == NULL)
430                                 g_warning("Can't get text part\n");
431                         else {
432                                 while (fgets(buf, sizeof(buf), fp) != NULL) {
433                                         strcrchomp(buf);
434                                         if (quote_str)
435                                                 INSERT(quote_str);
436                                         INSERT(buf);
437                                 }
438                                 fclose(fp);
439                         }
440                 }
441         }
442         | SHOW_MESSAGE_NO_SIGNATURE
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 (strncmp(buf, "-- \n", 4) == 0)
459                                                 break;
460                                         INSERT(buf);
461                                 }
462                                 fclose(fp);
463                         }
464                 }
465         }
466         | SHOW_QUOTED_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                                         if (quote_str)
485                                                 INSERT(quote_str);
486                                         INSERT(buf);
487                                 }
488                                 fclose(fp);
489                         }
490                 }
491         }
492         | SHOW_BACKSLASH
493         {
494                 INSERT("\\");
495         }
496         | SHOW_TAB
497         {
498                 INSERT("\t");
499         }
500         | SHOW_EOL
501         {
502                 INSERT("\n");
503         }
504         | SHOW_QUESTION_MARK
505         {
506                 INSERT("?");
507         }
508         | SHOW_PIPE
509         {
510                 INSERT("|");
511         }
512         | SHOW_OPARENT
513         {
514                 INSERT("{");
515         }
516         | SHOW_CPARENT
517         {
518                 INSERT("}");
519         }
520         | SET_CURSOR_POS
521         {
522                 cursor_pos = bufsize;
523         };
524
525 query:
526         QUERY_DATE
527         {
528                 add_visibility(msginfo->date != NULL);
529         }
530         OPARENT quote_fmt CPARENT
531         {
532                 remove_visibility();
533         }
534         | QUERY_FROM
535         {
536                 add_visibility(msginfo->from != NULL);
537         }
538         OPARENT quote_fmt CPARENT
539         {
540                 remove_visibility();
541         }
542         | QUERY_FULLNAME
543         {
544                 add_visibility(msginfo->fromname != NULL);
545         }
546         OPARENT quote_fmt CPARENT
547         {
548                 remove_visibility();
549         }
550         | QUERY_SUBJECT
551         {
552                 add_visibility(msginfo->subject != NULL);
553         }
554         OPARENT quote_fmt CPARENT
555         {
556                 remove_visibility();
557         }
558         | QUERY_TO
559         {
560                 add_visibility(msginfo->to != NULL);
561         }
562         OPARENT quote_fmt CPARENT
563         {
564                 remove_visibility();
565         }
566         | QUERY_NEWSGROUPS
567         {
568                 add_visibility(msginfo->newsgroups != NULL);
569         }
570         OPARENT quote_fmt CPARENT
571         {
572                 remove_visibility();
573         }
574         | QUERY_MESSAGEID
575         {
576                 add_visibility(msginfo->msgid != NULL);
577         }
578         OPARENT quote_fmt CPARENT
579         {
580                 remove_visibility();
581         }
582         | QUERY_CC
583         {
584                 add_visibility(msginfo->cc != NULL);
585         }
586         OPARENT quote_fmt CPARENT
587         {
588                 remove_visibility();
589         }
590         | QUERY_REFERENCES
591         {
592                 /* add_visibility(msginfo->references != NULL); */
593         }
594         OPARENT quote_fmt CPARENT
595         {
596                 remove_visibility();
597         };
598
599 insert:
600         INSERT_FILE OPARENT string CPARENT
601         {
602                 {
603                         FILE *file;
604                         char buffer[256];
605                         
606                         if((file = fopen($3, "rb")) != NULL) {
607                                 while(fgets(buffer, sizeof(buffer), file)) {
608                                         INSERT(buffer);
609                                 }
610                                 fclose(file);
611                         }
612                 }
613         }
614         | INSERT_PROGRAMOUTPUT OPARENT string CPARENT
615         {
616                 {
617                         FILE *file;
618                         char buffer[256];
619
620                         if((file = popen($3, "r")) != NULL) {
621                                 while(fgets(buffer, sizeof(buffer), file)) {
622                                         INSERT(buffer);
623                                 }
624                                 fclose(file);
625                         }
626                 }
627         };