allow quote / reply date & time format to be set using %D{format}.
[claws.git] / src / quote_fmt_parse.y
1 %{
2
3 #include "defs.h"
4
5 #include <glib.h>
6 #include <ctype.h>
7
8 #include "procmsg.h"
9 #include "procmime.h"
10 #include "utils.h"
11 #include "intl.h"
12
13 #include "quote_fmt.h"
14 #include "quote_fmt_lex.h"
15
16 /* decl */
17 /*
18 flex quote_fmt.l
19 bison -p quote_fmt quote_fmt.y
20 */
21
22 int yylex(void);
23
24 static MsgInfo *msginfo = NULL;
25 static gboolean *visible = NULL;
26 static gint maxsize = 0;
27 static gint stacksize = 0;
28
29 static gchar *buffer = NULL;
30 static gint bufmax = 0;
31 static gint bufsize = 0;
32 static const gchar *quote_str = NULL;
33 static const gchar *body = NULL;
34 static gint error = 0;
35
36 static void add_visibility(gboolean val)
37 {
38         stacksize++;
39         if (maxsize < stacksize) {
40                 maxsize += 128;
41                 visible = g_realloc(visible, maxsize * sizeof(gboolean));
42                 if (visible == NULL)
43                         maxsize = 0;
44         }
45
46         visible[stacksize - 1] = val;
47 }
48
49 static void remove_visibility(void)
50 {
51         stacksize--;
52 }
53
54 static void add_buffer(const gchar *s)
55 {
56         gint len;
57
58         len = strlen(s);
59         if (bufsize + len + 1 > bufmax) {
60                 if (bufmax == 0)
61                         bufmax = 128;
62                 while (bufsize + len + 1 > bufmax)
63                         bufmax *= 2;
64                 buffer = g_realloc(buffer, bufmax);
65         }
66         strcpy(buffer + bufsize, s);
67         bufsize += len;
68 }
69
70 static void flush_buffer(void)
71 {
72         if (buffer != NULL)
73                 *buffer = '\0';
74         bufsize = 0;
75 }
76
77 gchar *quote_fmt_get_buffer(void)
78 {
79         if (error != 0)
80                 return NULL;
81         else
82                 return buffer;
83 }
84
85 #define INSERT(buf) \
86         if (stacksize != 0 && visible[stacksize - 1]) \
87                 add_buffer(buf)
88
89 #define INSERT_CHARACTER(chr) \
90         if (stacksize != 0 && visible[stacksize - 1]) { \
91                 gchar tmp[2]; \
92                 tmp[0] = (chr); \
93                 tmp[1] = '\0'; \
94                 add_buffer(tmp); \
95         }
96
97 void quote_fmt_init(MsgInfo *info, const gchar *my_quote_str,
98                     const gchar *my_body)
99 {
100         quote_str = my_quote_str;
101         body = my_body;
102         msginfo = info;
103         stacksize = 0;
104         add_visibility(TRUE);
105         if (buffer != NULL)
106                 *buffer = 0;
107         bufsize = 0;
108         error = 0;
109 }
110
111 void quote_fmterror(char *str)
112 {
113         g_warning(_("Error: %s\n"), str);
114         error = 1;
115 }
116
117 int quote_fmtwrap(void)
118 {
119         return 1;
120 }
121
122 static int isseparator(char ch)
123 {
124         return isspace(ch) || ch == '.' || ch == '-';
125 }
126 %}
127
128 %union {
129         char chr;
130         char str[256];
131 }
132
133 %token SHOW_NEWSGROUPS
134 %token SHOW_DATE SHOW_FROM SHOW_FULLNAME SHOW_FIRST_NAME SHOW_LAST_NAME
135 %token SHOW_SENDER_INITIAL SHOW_SUBJECT SHOW_TO SHOW_MESSAGEID
136 %token SHOW_PERCENT SHOW_CC SHOW_REFERENCES SHOW_MESSAGE
137 %token SHOW_QUOTED_MESSAGE SHOW_BACKSLASH SHOW_TAB
138 %token SHOW_QUOTED_MESSAGE_NO_SIGNATURE SHOW_MESSAGE_NO_SIGNATURE
139 %token SHOW_EOL SHOW_QUESTION_MARK SHOW_PIPE SHOW_OPARENT SHOW_CPARENT
140 %token QUERY_DATE QUERY_FROM
141 %token QUERY_FULLNAME QUERY_SUBJECT QUERY_TO QUERY_NEWSGROUPS
142 %token QUERY_MESSAGEID QUERY_CC QUERY_REFERENCES
143 %token INSERT_FILE INSERT_PROGRAMOUTPUT
144 %token OPARENT CPARENT
145 %token CHARACTER
146 %token SHOW_DATE_EXPR
147
148 %start quote_fmt
149
150 %token <chr> CHARACTER
151 %type <chr> character
152 %type <str> string
153
154 %%
155
156 quote_fmt:
157         character_or_special_or_insert_or_query_list;
158
159 character_or_special_or_insert_or_query_list:
160         character_or_special_or_insert_or_query character_or_special_or_insert_or_query_list
161         | character_or_special_or_insert_or_query ;
162
163 character_or_special_or_insert_or_query:
164         special
165         | character
166         {
167                 INSERT_CHARACTER($1);
168         }
169         | query ;
170         | insert ;
171
172 character:
173         CHARACTER
174         ;
175
176 string:
177         CHARACTER
178         {
179                 $$[0] = $1;
180                 $$[1] = '\0';
181         }
182         | string CHARACTER
183         {
184                 strcpy($$, $1);
185                 $$[strlen($$) + 1] = '\0';
186                 $$[strlen($$)] = $2;
187         };
188
189 special:
190         SHOW_NEWSGROUPS
191         {
192                 if (msginfo->newsgroups)
193                         INSERT(msginfo->newsgroups);
194         }
195         | SHOW_DATE_EXPR OPARENT string CPARENT
196         {
197                 if (msginfo->date_t) {
198                         char timef[128];
199                         struct tm *lt;
200
201                         if (NULL != (lt = localtime(&msginfo->date_t))) {
202                                 strftime(timef, sizeof timef, $3, lt);
203                                 INSERT(timef);
204                         }
205                 }
206         }
207         | SHOW_DATE
208         {
209                 if (msginfo->date)
210                         INSERT(msginfo->date);
211         }
212         | SHOW_FROM
213         {
214                 if (msginfo->from)
215                         INSERT(msginfo->from);
216         }
217         | SHOW_FULLNAME
218         {
219                 if (msginfo->fromname)
220                         INSERT(msginfo->fromname);
221         }
222         | SHOW_FIRST_NAME
223         {
224                 if (msginfo->fromname) {
225                         gchar *p;
226                         gchar *str;
227
228                         str = alloca(strlen(msginfo->fromname) + 1);
229                         if (str != NULL) {
230                                 strcpy(str, msginfo->fromname);
231                                 p = str;
232                                 while (*p && !isspace(*p)) p++;
233                                 *p = '\0';
234                                 INSERT(str);
235                         }
236                 }
237         }
238         | SHOW_LAST_NAME
239         {
240                 /* This probably won't work together very well with Middle
241                  names and the like - thth */
242                 if (msginfo->fromname) {
243                         gchar *p;
244                         gchar *str;
245
246                         str = alloca(strlen(msginfo->fromname) + 1);
247                         if (str != NULL) {
248                                 strcpy(str, msginfo->fromname);
249                                 p = str;
250                                 while (*p && !isspace(*p)) p++;
251                                 if (*p) {
252                                     /* We found a space. Get first none-space char and
253                                      insert rest of string from there. */
254                                     while (*p && isspace(*p)) p++;
255                                     if (*p) {
256                                         INSERT(p);
257                                     } else {
258                                         /* If there is no none-space char, just insert
259                                          whole fromname. */
260                                         INSERT(str);
261                                     }
262                                 } else {
263                                     /* If there is no space, just insert whole fromname. */
264                                     INSERT(str);
265                                 }
266                         }
267                 }
268         }
269         | SHOW_SENDER_INITIAL
270         {
271 #define MAX_SENDER_INITIAL 20
272                 if (msginfo->fromname) {
273                         gchar tmp[MAX_SENDER_INITIAL];
274                         gchar *p;
275                         gchar *cur;
276                         gint len = 0;
277
278                         p = msginfo->fromname;
279                         cur = tmp;
280                         while (*p) {
281                                 if (*p && isalnum(*p)) {
282                                         *cur = toupper(*p);
283                                                 cur++;
284                                         len++;
285                                         if (len >= MAX_SENDER_INITIAL - 1)
286                                                 break;
287                                 } else
288                                         break;
289                                 while (*p && !isseparator(*p)) p++;
290                                 while (*p && isseparator(*p)) p++;
291                         }
292                         *cur = '\0';
293                         INSERT(tmp);
294                 }
295         }
296         | SHOW_SUBJECT
297         {
298                 if (msginfo->subject)
299                         INSERT(msginfo->subject);
300         }
301         | SHOW_TO
302         {
303                 if (msginfo->to)
304                         INSERT(msginfo->to);
305         }
306         | SHOW_MESSAGEID
307         {
308                 if (msginfo->msgid)
309                         INSERT(msginfo->msgid);
310         }
311         | SHOW_PERCENT
312         {
313                 INSERT("%");
314         }
315         | SHOW_CC
316         {
317                 if (msginfo->cc)
318                         INSERT(msginfo->cc);
319         }
320         | SHOW_REFERENCES
321         {
322                 /* if (msginfo->references)
323                         INSERT(msginfo->references); */
324         }
325         | SHOW_MESSAGE
326         {
327                 if (msginfo->folder) {
328                         gchar buf[BUFFSIZE];
329                         FILE *fp;
330
331                         if (body)
332                                 fp = str_open_as_stream(body);
333                         else
334                                 fp = procmime_get_first_text_content(msginfo);
335
336                         if (fp == NULL)
337                                 g_warning(_("Can't get text part\n"));
338                         else {
339                                 while (fgets(buf, sizeof(buf), fp) != NULL) {
340                                         strcrchomp(buf);
341                                         INSERT(buf);
342                                 }
343                                 fclose(fp);
344                         }
345                 }
346         }
347         | SHOW_QUOTED_MESSAGE
348         {
349                 if (msginfo->folder) {
350                         gchar buf[BUFFSIZE];
351                         FILE *fp;
352
353                         if (body)
354                                 fp = str_open_as_stream(body);
355                         else
356                                 fp = procmime_get_first_text_content(msginfo);
357
358                         if (fp == NULL)
359                                 g_warning(_("Can't get text part\n"));
360                         else {
361                                 while (fgets(buf, sizeof(buf), fp) != NULL) {
362                                         strcrchomp(buf);
363                                         if (quote_str)
364                                                 INSERT(quote_str);
365                                         INSERT(buf);
366                                 }
367                                 fclose(fp);
368                         }
369                 }
370         }
371         | SHOW_MESSAGE_NO_SIGNATURE
372         {
373                 if (msginfo->folder) {
374                         gchar buf[BUFFSIZE];
375                         FILE *fp;
376
377                         if (body)
378                                 fp = str_open_as_stream(body);
379                         else
380                                 fp = procmime_get_first_text_content(msginfo);
381
382                         if (fp == NULL)
383                                 g_warning(_("Can't get text part\n"));
384                         else {
385                                 while (fgets(buf, sizeof(buf), fp) != NULL) {
386                                         strcrchomp(buf);
387                                         if (strncmp(buf, "-- ", 3) == 0)
388                                                 break;
389                                         INSERT(buf);
390                                 }
391                                 fclose(fp);
392                         }
393                 }
394         }
395         | SHOW_QUOTED_MESSAGE_NO_SIGNATURE
396         {
397                 if (msginfo->folder) {
398                         gchar buf[BUFFSIZE];
399                         FILE *fp;
400
401                         if (body)
402                                 fp = str_open_as_stream(body);
403                         else
404                                 fp = procmime_get_first_text_content(msginfo);
405
406                         if (fp == NULL)
407                                 g_warning(_("Can't get text part\n"));
408                         else {
409                                 while (fgets(buf, sizeof(buf), fp) != NULL) {
410                                         strcrchomp(buf);
411                                         if (strncmp(buf, "-- ", 3) == 0)
412                                                 break;
413                                         if (quote_str)
414                                                 INSERT(quote_str);
415                                         INSERT(buf);
416                                 }
417                                 fclose(fp);
418                         }
419                 }
420         }
421         | SHOW_BACKSLASH
422         {
423                 INSERT("\\");
424         }
425         | SHOW_TAB
426         {
427                 INSERT("\t");
428         }
429         | SHOW_EOL
430         {
431                 INSERT("\n");
432         }
433         | SHOW_QUESTION_MARK
434         {
435                 INSERT("?");
436         }
437         | SHOW_PIPE
438         {
439                 INSERT("|");
440         }
441         | SHOW_OPARENT
442         {
443                 INSERT("{");
444         }
445         | SHOW_CPARENT
446         {
447                 INSERT("}");
448         };
449
450 query:
451         QUERY_DATE
452         {
453                 add_visibility(msginfo->date != NULL);
454         }
455         OPARENT quote_fmt CPARENT
456         {
457                 remove_visibility();
458         }
459         | QUERY_FROM
460         {
461                 add_visibility(msginfo->from != NULL);
462         }
463         OPARENT quote_fmt CPARENT
464         {
465                 remove_visibility();
466         }
467         | QUERY_FULLNAME
468         {
469                 add_visibility(msginfo->fromname != NULL);
470         }
471         OPARENT quote_fmt CPARENT
472         {
473                 remove_visibility();
474         }
475         | QUERY_SUBJECT
476         {
477                 add_visibility(msginfo->subject != NULL);
478         }
479         OPARENT quote_fmt CPARENT
480         {
481                 remove_visibility();
482         }
483         | QUERY_TO
484         {
485                 add_visibility(msginfo->to != NULL);
486         }
487         OPARENT quote_fmt CPARENT
488         {
489                 remove_visibility();
490         }
491         | QUERY_NEWSGROUPS
492         {
493                 add_visibility(msginfo->newsgroups != NULL);
494         }
495         OPARENT quote_fmt CPARENT
496         {
497                 remove_visibility();
498         }
499         | QUERY_MESSAGEID
500         {
501                 add_visibility(msginfo->msgid != NULL);
502         }
503         OPARENT quote_fmt CPARENT
504         {
505                 remove_visibility();
506         }
507         | QUERY_CC
508         {
509                 add_visibility(msginfo->cc != NULL);
510         }
511         OPARENT quote_fmt CPARENT
512         {
513                 remove_visibility();
514         }
515         | QUERY_REFERENCES
516         {
517                 /* add_visibility(msginfo->references != NULL); */
518         }
519         OPARENT quote_fmt CPARENT
520         {
521                 remove_visibility();
522         };
523
524 insert:
525         INSERT_FILE OPARENT string CPARENT
526         {
527                 {
528                         FILE *file;
529                         char buffer[256];
530                         
531                         if(file = fopen($3, "rb")) {
532                                 while(fgets(buffer, sizeof(buffer), file)) {
533                                         INSERT(buffer);
534                                 }
535                                 fclose(file);
536                         }
537                 }
538         }
539         | INSERT_PROGRAMOUTPUT OPARENT string CPARENT
540         {
541                 {
542                         FILE *file;
543                         char buffer[256];
544
545                         if(file = popen($3, "r")) {
546                                 while(fgets(buffer, sizeof(buffer), file)) {
547                                         INSERT(buffer);
548                                 }
549                                 fclose(file);
550                         }
551                 }
552         };