Commit fdecadb1 fdecadb1c7863e7aafd7e3c387c6ef239c09a1ca by Sergey Poznyakoff

Added to the repository

1 parent 35a260bb
...@@ -68,13 +68,14 @@ libmh_a_SOURCES= \ ...@@ -68,13 +68,14 @@ libmh_a_SOURCES= \
68 mh_error.c\ 68 mh_error.c\
69 mh_format.c\ 69 mh_format.c\
70 mh_init.c\ 70 mh_init.c\
71 mh_list.c\
71 mh_fmtgram.y\ 72 mh_fmtgram.y\
72 mh_msgset.c\ 73 mh_msgset.c\
73 mh_whatnow.c 74 mh_whatnow.c
74 noinst_HEADERS = mh.h mh_getopt.h 75 noinst_HEADERS = mh.h mh_getopt.h
75 76
76 mhlib_DATA = replcomps 77 mhlib_DATA = replcomps mhl.format
77 EXTRA_DIST = replcomps 78 EXTRA_DIST = replcomps mhl.format
78 79
79 INCLUDES = -I${top_srcdir}/include -I${top_srcdir}/lib -I${top_srcdir}/mailbox @INTLINCS@ 80 INCLUDES = -I${top_srcdir}/include -I${top_srcdir}/lib -I${top_srcdir}/mailbox @INTLINCS@
80 AM_CPPFLAGS = -DMHLIBDIR=\"$(mhlibdir)\" 81 AM_CPPFLAGS = -DMHLIBDIR=\"$(mhlibdir)\"
......
1 /* GNU Mailutils -- a suite of utilities for electronic mail
2 Copyright (C) 2003 Free Software Foundation, Inc.
3
4 GNU Mailutils is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2, or (at your option)
7 any later version.
8
9 GNU Mailutils is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with GNU Mailutils; if not, write to the Free Software
16 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
17
18 /* MH mhl command */
19
20 #include <mh.h>
21
22 /* *********************** Compiler for MHL formats *********************** */
23
24 typedef struct
25 {
26 char *filename;
27 int line;
28 }
29 locus_t;
30
31 enum mhl_type
32 {
33 stmt_cleartext,
34 stmt_component,
35 stmt_variable
36 };
37
38 enum mhl_datatype
39 {
40 dt_flag,
41 dt_integer,
42 dt_string,
43 dt_format
44 };
45
46 typedef union mhl_value {
47 char *str;
48 int num;
49 mh_format_t *fmt;
50 } mhl_value_t;
51
52 typedef struct mhl_variable
53 {
54 int id;
55 char *name;
56 int type;
57 }
58 mhl_variable_t;
59
60 typedef struct mhl_stmt mhl_stmt_t;
61 typedef struct mhl_stmt_variable mhl_stmt_variable_t;
62 typedef struct mhl_stmt_component mhl_stmt_component_t;
63
64 struct mhl_stmt_variable
65 {
66 mhl_variable_t *id;
67 mhl_value_t value;
68 };
69
70 struct mhl_stmt_component
71 {
72 char *name;
73 list_t format;
74 };
75
76 struct mhl_stmt
77 {
78 enum mhl_type type;
79 union
80 {
81 char *cleartext;
82 mhl_stmt_variable_t variable;
83 mhl_stmt_component_t component;
84 } v;
85 };
86
87 static mhl_variable_t *variable_lookup __P((char *name));
88
89 static mhl_stmt_t *
90 stmt_alloc (enum mhl_type type)
91 {
92 mhl_stmt_t *p = xmalloc (sizeof (*p));
93 p->type = type;
94 return p;
95 }
96
97 static int
98 compdecl (char **str, char **compname)
99 {
100 char *p;
101
102 for (p = *str; *p && !isspace (*p); p++)
103 {
104 if (*p == ':')
105 {
106 int len = p - *str;
107 *compname = xmalloc (len + 1);
108 memcpy (*compname, *str, len);
109 (*compname)[len] = 0;
110 *str = p + 1;
111 return 0;
112 }
113 }
114 return 1;
115 }
116
117 static void parse_variable (locus_t *loc, list_t formlist, char *str);
118
119 static void
120 parse_cleartext (locus_t *loc, list_t formlist, char *str)
121 {
122 int len;
123 mhl_stmt_t *stmt = stmt_alloc (stmt_cleartext);
124 stmt->v.cleartext = strdup (str);
125 len = strlen (stmt->v.cleartext);
126 if (len > 0 && stmt->v.cleartext[len-1] == '\n')
127 stmt->v.cleartext[len-1] = 0;
128 list_append (formlist, stmt);
129 }
130
131 static void
132 parse_component (locus_t *loc, list_t formlist, char *compname, char *str)
133 {
134 mhl_stmt_t *stmt = stmt_alloc (stmt_component);
135 stmt->v.component.name = compname;
136 if (list_create (&stmt->v.component.format))
137 {
138 mh_error (_("%s:%d: can't create list"),
139 loc->filename,
140 loc->line);
141 exit (1); /* FIXME */
142 }
143 parse_variable (loc, stmt->v.component.format, str);
144 list_append (formlist, stmt);
145 }
146
147 static void
148 parse_variable (locus_t *loc, list_t formlist, char *str)
149 {
150 int i;
151 int argc;
152 char **argv;
153 mh_format_t fmt;
154
155 if (argcv_get (str, ",=", NULL, &argc, &argv))
156 {
157 mh_error (_("%s:%d: cannot split string %s"),
158 loc->filename,
159 loc->line,
160 str);
161 exit (1);
162 }
163
164 for (i = 0; i < argc; i++)
165 {
166 mhl_stmt_t *stmt = stmt_alloc (stmt_variable);
167 char *name = argv[i];
168 char *value = NULL;
169 mhl_variable_t *var;
170
171 var = variable_lookup (name);
172 if (!var)
173 {
174 mh_error (_("%s:%d: unknown variable: %s"),
175 loc->filename,
176 loc->line,
177 argv[i]);
178 exit (1);
179 }
180
181 if (i + 1 < argc && argv[i+1][0] == '=')
182 {
183 i++;
184 value = argv[++i];
185 }
186
187 if ((var->type == dt_flag && value)
188 || (var->type != dt_flag && !value))
189 {
190 mh_error (_("%s:%d: wrong datatype for %s"),
191 loc->filename,
192 loc->line,
193 var->name);
194 exit (1);
195 }
196
197 switch (var->type)
198 {
199 case dt_string:
200 stmt->v.variable.value.str = strdup (value);
201 break;
202
203 case dt_integer:
204 stmt->v.variable.value.num = strtoul (value, NULL, 0);
205 break;
206
207 case dt_format:
208 if (mh_format_parse (value, &fmt))
209 {
210 mh_error (_("%s:%d: Bad format string"),
211 loc->filename,
212 loc->line);
213 exit (1);
214 }
215 stmt->v.variable.value.fmt = xmalloc (sizeof (mh_format_t));
216 *stmt->v.variable.value.fmt = fmt;
217 break;
218
219 case dt_flag:
220 stmt->v.variable.value.num = strcmp (var->name, name) == 0;
221 break;
222 }
223 stmt->v.variable.id = var;
224 list_append (formlist, stmt);
225
226 i++;
227 if (i < argc && argv[i][0] != ',')
228 {
229 mh_error (_("%s:%d: syntax error"), loc->filename, loc->line);
230 exit (1);
231 }
232 }
233
234 argcv_free (argc, argv);
235 }
236
237 static int
238 parse_line (locus_t *loc, list_t formlist, char *str)
239 {
240 char *compname;
241
242 if (*str == ':')
243 parse_cleartext (loc, formlist, str+1);
244 else if (compdecl (&str, &compname) == 0)
245 parse_component (loc, formlist, compname, str);
246 else
247 parse_variable (loc, formlist, str);
248 return 0;
249 }
250
251 list_t
252 mhl_format_compile (char *name)
253 {
254 FILE *fp;
255 list_t formlist;
256 char *buf = NULL;
257 size_t n = 0;
258 locus_t loc;
259
260 fp = fopen (name, "r");
261 if (!fp)
262 {
263 mh_error (_("cannot open file %s: %s"), name, mu_strerror (errno));
264 return NULL;
265 }
266
267 if (list_create (&formlist))
268 {
269 fclose (fp);
270 mh_error (_("can't create list"));
271 return NULL;
272 }
273
274 loc.filename = name;
275 loc.line = 1;
276 while (getline (&buf, &n, fp) > 0)
277 {
278 char *p = buf;
279
280 while (*p && isspace (*p))
281 ;
282
283 if (*p == 0 || *p == ';')
284 continue;
285
286 parse_line (&loc, formlist, p);
287 loc.line++;
288 }
289
290 free (buf);
291 fclose (fp);
292
293 return formlist;
294 }
295
296
297 /* ********************** Destroy compiled MHL format ********************** */
298
299 static void
300 _destroy_value (enum mhl_datatype type, mhl_value_t *val)
301 {
302 switch (type)
303 {
304 case dt_string:
305 free (val->str);
306 break;
307
308 case dt_flag:
309 case dt_integer:
310 break;
311
312 case dt_format:
313 mh_format_free (val->fmt);
314 free (val->fmt);
315 break;
316
317 default:
318 abort ();
319 }
320 }
321
322 static int
323 _destroy_stmt (void *item, void *data)
324 {
325 mhl_stmt_t *stmt = item;
326
327 switch (stmt->type)
328 {
329 case stmt_cleartext:
330 free (stmt->v.cleartext);
331 break;
332
333 case stmt_component:
334 free (stmt->v.component.name);
335 mhl_format_destroy (&stmt->v.component.format);
336 break;
337
338 case stmt_variable:
339 _destroy_value (stmt->v.variable.id->type, &stmt->v.variable.value);
340 break;
341
342 default:
343 abort ();
344 }
345 return 0;
346 }
347
348 void
349 mhl_format_destroy (list_t *fmt)
350 {
351 list_do (*fmt, _destroy_stmt, NULL);
352 list_destroy (fmt);
353 }
354
355
356 /* *************************** Runtime functions *************************** */
357 /* Integer variables */
358 #define I_WIDTH 0
359 #define I_LENGTH 1
360 #define I_OFFSET 2
361 #define I_OVERFLOWOFFSET 3
362 #define I_COMPWIDTH 4
363 #define I_MAX 5
364
365 /* Boolean (flag) variables */
366 #define B_UPPERCASE 0
367 #define B_CLEARSCREEN 1
368 #define B_BELL 2
369 #define B_NOCOMPONENT 3
370 #define B_CENTER 4
371 #define B_LEFTADJUST 5
372 #define B_COMPRESS 6
373 #define B_SPLIT 7
374 #define B_NEWLINE 8
375 #define B_ADDRFIELD 9
376 #define B_DATEFIELD 10
377 #define B_MAX 11
378
379 /* String variables */
380 #define S_OVERFLOWTEXT 0
381 #define S_COMPONENT 1
382 #define S_IGNORES 2
383 #define S_MAX 3
384
385 /* Format variables */
386 #define F_FORMATFIELD 0
387 #define F_MAX 1
388
389 static mhl_variable_t vartab[] = {
390 /* Integer variables */
391 { I_WIDTH, "width", dt_integer },
392 { I_LENGTH, "length", dt_integer },
393 { I_OFFSET, "offset", dt_integer },
394 { I_OVERFLOWOFFSET, "overflowoffset", dt_integer },
395 { I_COMPWIDTH, "compwidth", dt_integer },
396
397 /* Boolean (flag) variables */
398 { B_UPPERCASE, "uppercase", dt_flag },
399 { B_CLEARSCREEN, "clearscreen", dt_flag },
400 { B_BELL, "bell", dt_flag },
401 { B_NOCOMPONENT, "nocomponent", dt_flag },
402 { B_CENTER, "center", dt_flag },
403 { B_LEFTADJUST, "leftadjust", dt_flag },
404 { B_COMPRESS, "compress", dt_flag },
405 { B_SPLIT, "split", dt_flag },
406 { B_NEWLINE, "newline", dt_flag },
407 { B_ADDRFIELD, "addrfield", dt_flag },
408 { B_DATEFIELD, "datefield", dt_flag },
409
410 /* String variables */
411 { S_OVERFLOWTEXT, "overflowtext", dt_string },
412 { S_COMPONENT, "component", dt_string },
413 { S_IGNORES, "ignores", dt_string },
414
415 /* Format variables */
416 { F_FORMATFIELD, "formatfield", dt_format },
417 { 0 }
418 };
419
420 static mhl_variable_t *
421 variable_lookup (char *name)
422 {
423 mhl_variable_t *p;
424
425 for (p = vartab; p->name; p++)
426 {
427 if (p->type == dt_flag
428 && memcmp (name, "no", 2) == 0
429 && strcmp (p->name, name + 2) == 0)
430 return p;
431
432 if (strcmp (p->name, name) == 0)
433 return p;
434 }
435 return NULL;
436 }
437
438 struct eval_env
439 {
440 message_t msg;
441 stream_t output;
442 list_t printed_fields; /* A list of printed header names */
443 int pos;
444 int nlines;
445 int ivar[I_MAX];
446 int bvar[B_MAX];
447 char *svar[S_MAX];
448 mh_format_t *fvar[F_MAX];
449 char *prefix;
450 };
451
452 static int eval_stmt (void *item, void *data);
453 static void newline (struct eval_env *env);
454 static void goto_offset (struct eval_env *env, int count);
455 static void print (struct eval_env *env, char *str, int nloff);
456
457 static int
458 _comp_name (void *item, void *date)
459 {
460 return strcasecmp (item, date) == 0;
461 }
462
463 int
464 header_is_printed (struct eval_env *env, char *name)
465 {
466 return list_do (env->printed_fields, _comp_name, name) == 1;
467 }
468
469 int
470 want_header (struct eval_env *env, char *name)
471 {
472 char *p, *str = env->svar[S_IGNORES];
473
474 for (p = strchrnul (str, ','); *str; p = strchrnul (str, ','))
475 {
476 if (strncasecmp (name, str, p - str) == 0)
477 return 0;
478 str = p;
479 if (*str)
480 str++;
481 }
482 return 1;
483 }
484
485 static int
486 eval_var (struct eval_env *env, mhl_stmt_variable_t *var)
487 {
488 switch (var->id->type)
489 {
490 case dt_flag:
491 env->bvar[var->id->id] = var->value.num;
492 break;
493
494 case dt_integer:
495 env->ivar[var->id->id] = var->value.num;
496 break;
497
498 case dt_string:
499 env->svar[var->id->id] = var->value.str;
500 break;
501
502 case dt_format:
503 env->fvar[var->id->id] = var->value.fmt;
504 break;
505
506 default:
507 abort ();
508 }
509 return 0;
510 }
511
512 static void
513 ovf_print (struct eval_env *env, char *str, int size, int nloff)
514 {
515 int ovf = 0;
516
517 while (size)
518 {
519 int len = size;
520 if (ovf)
521 {
522 goto_offset (env, env->ivar[I_OVERFLOWOFFSET]);
523 if (env->svar[S_OVERFLOWTEXT])
524 {
525 int l = strlen (env->svar[S_OVERFLOWTEXT]);
526 stream_sequential_write (env->output,
527 env->svar[S_OVERFLOWTEXT], l);
528 env->pos += l;
529 }
530 }
531 else
532 {
533 if (env->prefix && !env->bvar[B_NOCOMPONENT])
534 {
535 goto_offset (env, env->ivar[I_OFFSET]);
536
537 stream_sequential_write (env->output, env->prefix,
538 strlen (env->prefix));
539 env->pos += strlen (env->prefix);
540
541 goto_offset (env, nloff);
542 }
543 }
544
545 if (env->pos + len > env->ivar[I_WIDTH])
546 {
547 ovf = 1;
548 len = env->ivar[I_WIDTH] - env->pos;
549 }
550
551 stream_sequential_write (env->output, str, len);
552 env->pos += len;
553 if (env->pos >= env->ivar[I_WIDTH])
554 newline (env);
555 str += len;
556 size -= len;
557 }
558 }
559
560 static void
561 print (struct eval_env *env, char *str, int nloff)
562 {
563 do
564 {
565 if (*str == '\n')
566 {
567 newline (env);
568 str++;
569 }
570 else
571 {
572 char *p = strchrnul (str, '\n');
573 ovf_print (env, str, p - str, nloff);
574 str = p;
575 if (*p)
576 {
577 newline (env);
578 for (str++; *str && isspace (*str); str++)
579 ;
580 }
581 }
582 }
583 while (*str);
584 }
585
586 static void
587 newline (struct eval_env *env)
588 {
589 stream_sequential_write (env->output, "\n", 1);
590 env->pos = 0;
591 if (env->ivar[I_LENGTH] && ++env->nlines >= env->ivar[I_LENGTH])
592 {
593 /* FIXME: Better to write it directly on the terminal */
594 if (env->bvar[B_BELL])
595 stream_sequential_write (env->output, "\a", 1);
596 if (env->bvar[B_CLEARSCREEN])
597 stream_sequential_write (env->output, "\f", 1);
598 env->nlines = 0;
599 }
600 }
601
602 static void
603 goto_offset (struct eval_env *env, int count)
604 {
605 for (; env->pos < count; env->pos++)
606 stream_sequential_write (env->output, " ", 1);
607 }
608
609 int
610 print_header_value (struct eval_env *env, char *val)
611 {
612 char *p;
613
614 if (env->fvar[F_FORMATFIELD])
615 {
616 if (mh_format_str (env->fvar[F_FORMATFIELD], val,
617 env->ivar[I_WIDTH], &p))
618 val = p;
619 }
620
621 if (env->bvar[B_UPPERCASE])
622 {
623 for (p = val; *p; p++)
624 *p = toupper (*p);
625 }
626
627 if (env->bvar[B_COMPRESS])
628 for (p = val; *p; p++)
629 if (*p == '\n')
630 *p = ' ';
631
632 if (env->bvar[B_LEFTADJUST])
633 {
634 for (p = val; *p && isspace (*p); p++)
635 ;
636 }
637 else
638 p = val;
639
640 print (env, p, env->ivar[I_COMPWIDTH]);
641 return 0;
642 }
643
644 int
645 eval_component (struct eval_env *env, char *name)
646 {
647 header_t hdr;
648 char *val;
649
650 message_get_header (env->msg, &hdr);
651 if (header_aget_value (hdr, name, &val))
652 return 0;
653
654 list_append (env->printed_fields, name);
655 print_header_value (env, val);
656 free (val);
657 return 0;
658 }
659
660 int
661 eval_body (struct eval_env *env)
662 {
663 body_t body = NULL;
664 stream_t input = NULL;
665 char buf[128]; /* FIXME: Fixed size. Bad */
666 size_t n;
667
668 env->prefix = env->svar[S_COMPONENT];
669 message_get_body (env->msg, &body);
670 body_get_stream (body, &input);
671 stream_seek (input, 0, SEEK_SET);
672 while (stream_sequential_readline (input, buf, sizeof buf, &n) == 0
673 && n > 0)
674 {
675 buf[n] = 0;
676 print (env, buf, 0);
677 }
678 return 0;
679 }
680
681 int
682 eval_extras (struct eval_env *env)
683 {
684 header_t hdr;
685 size_t i, num;
686 char buf[512];
687
688 message_get_header (env->msg, &hdr);
689 header_get_field_count (hdr, &num);
690 for (i = 1; i <= num; i++)
691 {
692 header_get_field_name (hdr, i, buf, sizeof buf, NULL);
693 if (want_header (env, buf)
694 && !header_is_printed (env, buf))
695 {
696 goto_offset (env, env->ivar[I_OFFSET]);
697 print (env, buf, 0);
698 print (env, ":", 0);
699 header_get_field_value (hdr, i, buf, sizeof buf, NULL);
700 print_header_value (env, buf);
701 if (env->bvar[B_NEWLINE])
702 newline (env);
703 }
704 }
705 return 0;
706 }
707
708 int
709 eval_comp (struct eval_env *env, char *compname, list_t format)
710 {
711 struct eval_env lenv = *env;
712
713 list_do (format, eval_stmt, &lenv);
714
715 goto_offset (&lenv, lenv.ivar[I_OFFSET]);
716
717 if (!lenv.svar[S_COMPONENT])
718 lenv.svar[S_COMPONENT] = compname;
719
720 if (!lenv.bvar[B_NOCOMPONENT])
721 {
722 print (&lenv, lenv.svar[S_COMPONENT], 0);
723 if (strcmp (compname, "body"))
724 print (&lenv, ":", 0);
725 }
726
727 if (strcmp (compname, "extras") == 0)
728 eval_extras (&lenv);
729 else if (strcmp (compname, "body") == 0)
730 eval_body (&lenv);
731 else
732 eval_component (&lenv, compname);
733
734 if (lenv.bvar[B_NEWLINE])
735 newline (&lenv);
736
737 env->pos = lenv.pos;
738 env->nlines = lenv.nlines;
739
740 return 0;
741 }
742
743 static int
744 eval_stmt (void *item, void *data)
745 {
746 mhl_stmt_t *stmt = item;
747 struct eval_env *env = data;
748
749 switch (stmt->type)
750 {
751 case stmt_cleartext:
752 print (env, stmt->v.cleartext, 0);
753 newline (env);
754 break;
755
756 case stmt_component:
757 eval_comp (env, stmt->v.component.name, stmt->v.component.format);
758 break;
759
760 case stmt_variable:
761 eval_var (env, &stmt->v.variable);
762 break;
763
764 default:
765 abort ();
766 }
767
768 return 0;
769 }
770
771 int
772 mhl_format_run (list_t fmt,
773 int width, int length, int clearscreen, int bell,
774 message_t msg, stream_t output)
775 {
776 int rc;
777 struct eval_env env;
778
779 /* Initialize the environment */
780 memset (&env, 0, sizeof (env));
781
782 env.bvar[B_NEWLINE] = 1;
783 list_create (&env.printed_fields);
784 env.ivar[I_WIDTH] = width;
785 env.ivar[I_LENGTH] = length;
786 env.bvar[B_CLEARSCREEN] = clearscreen;
787 env.bvar[B_BELL] = bell;
788 env.pos = 0;
789 env.nlines = 0;
790 env.msg = msg;
791 env.output = output;
792 rc = list_do (fmt, eval_stmt, &env);
793 list_destroy (&env.printed_fields);
794 return rc;
795 }
1 ; This is the default mhl.format file for Mailutils MH.
2 ;
3 ; GNU Mailutils -- a suite of utilities for electronic mail
4 ; Copyright (C) 2003 Free Software Foundation, Inc.
5 ; Distributed under GPL.
6 ;
7 : -- using template mhl.format --
8 overflowtext="***",overflowoffset=5
9 leftadjust,compwidth=9
10 ignores="msgid,message-id,received"
11 Date:formatfield="%<(nodate{text})%{text}%|%(pretty{text})%>"
12 To:
13 cc:
14 :
15 From:
16 Subject:
17 :
18 extras:nocomponent
19 :
20 body:nocomponent,overflowtext="",overflowoffset=0,noleftadjust
21 ; End of mhl.format
...\ No newline at end of file ...\ No newline at end of file