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