Commit 071c0552 071c05523b657a805576f7bfc9562445b43afc20 by Sergey Poznyakoff

Added to the repository

1 parent 09617f83
1 ## Process this file with GNU Automake to create Makefile.in
2
3 ## Copyright (C) 2005 Free Software Foundation, Inc.
4 ##
5 ## GNU Mailutils is free software; you can redistribute it and/or
6 ## modify it under the terms of the GNU General Public License as
7 ## published by the Free Software Foundation; either version 2, or (at
8 ## your option) any later version.
9 ##
10 ## This program is distributed in the hope that it will be useful, but
11 ## WITHOUT ANY WARRANTY; without even the implied warranty of
12 ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 ## 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 INCLUDES = -I$(top_srcdir)/include -I$(top_srcdir)/lib\
20 -I${top_builddir}/include/mailutils/gnu @INTLINCS@
21
22 AM_CFLAGS = -DSYSCONFDIR=\"$(sysconfdir)\"
23
24 bin_PROGRAMS = mimeview
25 mimeview_SOURCES = \
26 mimeview.c \
27 mimetypes-gram.c \
28 mimetypes-lex.c \
29 mimetypes-decl.h \
30 mimeview.h
31
32 YLWRAP = $(SHELL) $(top_srcdir)/scripts/ylwrap
33 AM_YFLAGS=-vt
34 AM_LEXFLAGS=-d
35 EXTRA_DIST = mimetypes.y mimetypes.l
36
37 mimetypes-gram.c mimetypes-decl.h: $(srcdir)/mimetypes.y
38 $(YLWRAP) "$(YACC) $(AM_YFLAGS) -d" $< \
39 y.tab.c mimetypes-gram.c y.tab.h mimetypes-decl.h \
40 y.output mimetypes.output \
41 -- -yy mimetypes_yy
42
43 mimetypes-lex.c: $(srcdir)/mimetypes.l mimetypes-decl.h
44 $(YLWRAP) "$(LEX) $(AM_LEXFLAGS) $(LEXFLAGS)" \
45 $(srcdir)/mimetypes.l lex.yy.c mimetypes-lex.c \
46 -- -yy mimetypes_yy
47
48 BUILT_SOURCES = mimetypes-gram.c mimetypes-lex.c mimetypes-decl.h
49
50 mimeview_LDADD = \
51 ../mailbox/libmailbox.la\
52 ../lib/libmailutils.la \
53 @LTLIBINTL@
54
1 %{
2 /* GNU Mailutils -- a suite of utilities for electronic mail
3 Copyright (C) 2005 Free Software Foundation, Inc.
4
5 GNU Mailutils 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, or (at your option)
8 any later version.
9
10 GNU Mailutils 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 GNU Mailutils; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
18
19 #ifdef HAVE_CONFIG_H
20 # include <config.h>
21 #endif
22
23 #include <mimeview.h>
24 #include <mimetypes-decl.h>
25 #include <mu_asprintf.h>
26 #include <unistd.h>
27 #include <sys/stat.h>
28
29 static int line_num;
30 static const char *file_name;
31 static int file_name_alloc;
32
33 static struct obstack stack;
34 static int prev_state;
35
36 static unsigned
37 digit_to_number (char c)
38 {
39 return (unsigned) (c >= '0' && c <= '9' ? c-'0' :
40 c >= 'A' && c <= 'Z' ? c-'A'+10 :
41 c-'a'+10);
42 }
43 %}
44 %x ARGS HEX
45 X [0-9a-fA-F]
46 IDENT [a-zA-Z_\.][a-zA-Z0-9_\.-]*
47 WS [ \t]*
48 %%
49 /* Comments */
50 <INITIAL>#.*\n { line_num++; }
51 <INITIAL>#.* /* end-of-file comment */;
52 /* Tokens */
53 \\\n { line_num++; }
54 \n { line_num++; return EOL; }
55 {WS} ;
56 {IDENT} {
57 obstack_grow (&stack, yytext, yyleng);
58 yylval.string.len = obstack_object_size (&stack);
59 obstack_1grow (&stack, 0);
60 yylval.string.ptr = obstack_finish (&stack);
61 return IDENT;
62 }
63 <INITIAL>{IDENT}"(" {
64 obstack_grow (&stack, yytext, yyleng-1);
65 yylval.string.len = obstack_object_size (&stack);
66 obstack_1grow (&stack, 0);
67 yylval.string.ptr = obstack_finish (&stack);
68 BEGIN(ARGS);
69 return IDENT_L;
70 }
71 <INITIAL,ARGS>\"[^\\"\n]*\" {
72 obstack_grow (&stack, yytext+1, yyleng-2);
73 yylval.string.len = obstack_object_size (&stack);
74 obstack_1grow (&stack, 0);
75 yylval.string.ptr = obstack_finish (&stack);
76 return STRING;
77 }
78 <INITIAL,ARGS>"<" {
79 prev_state = YYSTATE;
80 BEGIN(HEX);
81 }
82 <ARGS>[^ \t<\\\n),]+/[),] {
83 obstack_grow (&stack, yytext, yyleng);
84 yylval.string.len = obstack_object_size (&stack);
85 obstack_1grow (&stack, 0);
86 yylval.string.ptr = obstack_finish (&stack);
87 return STRING;
88 }
89 <ARGS>[^ \t<\\\n),]+< {
90 obstack_grow (&stack, yytext, yyleng);
91 prev_state = YYSTATE;
92 BEGIN(HEX);
93 }
94 <INITIAL>[^ \t<\\\n)+&]/[ \t\\\n)+&] {
95 obstack_grow (&stack, yytext, yyleng);
96 yylval.string.len = obstack_object_size (&stack);
97 obstack_1grow (&stack, 0);
98 yylval.string.ptr = obstack_finish (&stack);
99 return STRING;
100 }
101 <ARGS>[^ \t<\\\n),]/[ \t\\\n] {
102 obstack_grow (&stack, yytext, yyleng);
103 yylval.string.len = obstack_object_size (&stack);
104 obstack_1grow (&stack, 0);
105 yylval.string.ptr = obstack_finish (&stack);
106 return STRING;
107 }
108 <HEX>{X}{X} {
109 int c = digit_to_number (yytext[0]*16 + yytext[1]);
110 obstack_1grow (&stack, c);
111 }
112 <HEX>">"/[ \t\\\n,)] {
113 BEGIN(prev_state);
114 yylval.string.len = obstack_object_size (&stack);
115 obstack_1grow (&stack, 0);
116 yylval.string.ptr = obstack_finish (&stack);
117 return STRING;
118 }
119 <HEX>">" {
120 BEGIN(prev_state);
121 }
122 /* Special cases: && and ||. Docs don't say anything about them, but
123 I've found them in my mime.types file... --Sergey */
124 "&&" return '+';
125 "||" return ',';
126 /* Operators */
127 "!"|"+"|"("|")"|"/" return yytext[0];
128 <ARGS>"," return yytext[0];
129 <ARGS>")" { BEGIN(INITIAL); return yytext[0]; }
130 <INITIAL,ARGS,HEX>. {
131 fprintf (stderr, "Invalid character '%c', state %d\n", yytext[0], YYSTATE);
132 abort();
133 }
134 %%
135
136 void
137 mimetypes_lex_debug (int level)
138 {
139 yy_flex_debug = level;
140 }
141
142 int
143 mimetypes_open (const char *name)
144 {
145 struct stat st;
146 if (stat (name, &st))
147 {
148 mu_error (_("Cannot stat `%s': %s"), name, mu_strerror (errno));
149 return -1;
150 }
151
152 if (S_ISDIR (st.st_mode))
153 {
154 asprintf (&file_name, "%s/mime.types", name);
155 file_name_alloc = 1;
156 }
157 else
158 {
159 file_name = name;
160 file_name_alloc = 0;
161 }
162
163 yyin = fopen (file_name, "r");
164 if (!yyin)
165 {
166 mu_error (_("Cannot open `%s': %s"), file_name, mu_strerror (errno));
167 if (file_name_alloc)
168 {
169 free (file_name);
170 file_name_alloc = 0;
171 }
172 return -1;
173 }
174 line_num = 1;
175 obstack_init (&stack);
176 return 0;
177 }
178
179 void
180 mimetypes_close ()
181 {
182 fclose (yyin);
183 if (file_name_alloc)
184 {
185 free (file_name);
186 file_name_alloc = 0;
187 }
188 }
189
190 int
191 yyerror (char *s)
192 {
193 mu_error ("%s:%lu: %s", file_name, line_num, s);
194 return 0;
195 }
196
197 int
198 yywrap ()
199 {
200 return 1;
201 }
202
203 struct mimetypes_string
204 mimetypes_append_string2 (struct mimetypes_string *s1,
205 char c,
206 struct mimetypes_string *s2)
207 {
208 struct mimetypes_string r;
209
210 r.len = s1->len + s2->len + 1;
211 obstack_grow (&stack, s1->ptr, s1->len);
212 obstack_1grow (&stack, c);
213 obstack_grow (&stack, s2->ptr, s2->len);
214 obstack_1grow (&stack, 0);
215 r.ptr = obstack_finish (&stack);
216 return r;
217 }
218
219 struct mimetypes_string *
220 mimetypes_string_dup (struct mimetypes_string *s)
221 {
222 obstack_grow (&stack, s, sizeof *s);
223 return obstack_finish (&stack);
224 }
225
226 void *
227 mimetypes_malloc (size_t size)
228 {
229 return obstack_alloc(&stack, size);
230 }
231
232 void
233 reset_lex ()
234 {
235 BEGIN(INITIAL);
236 }
237
238
1 %{
2 /* GNU Mailutils -- a suite of utilities for electronic mail
3 Copyright (C) 2005 Free Software Foundation, Inc.
4
5 GNU Mailutils 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, or (at your option)
8 any later version.
9
10 GNU Mailutils 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 GNU Mailutils; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
18
19 #ifdef HAVE_CONFIG_H
20 # include <config.h>
21 #endif
22
23 #include <mimeview.h>
24 #include <mimetypes-decl.h>
25
26 static void
27 yyprint (FILE *output, unsigned short toknum, YYSTYPE val)
28 {
29 switch (toknum)
30 {
31 case IDENT:
32 case IDENT_L:
33 case STRING:
34 fprintf (output, "[%lu] %s", (unsigned long) val.string.len,
35 val.string.ptr);
36 break;
37
38 case EOL:
39 default:
40 break;
41 }
42 }
43
44 #define YYPRINT yyprint
45
46 static list_t arg_list; /* For error recovery */
47
48 #define L_OR 0
49 #define L_AND 1
50
51 enum node_type
52 {
53 functional_node,
54 binary_node,
55 negation_node,
56 suffix_node
57 };
58
59 union argument
60 {
61 struct mimetypes_string *string;
62 unsigned number;
63 int c;
64 };
65
66 typedef int (*builtin_t) (union argument *args);
67
68 struct node
69 {
70 enum node_type type;
71 union
72 {
73 struct
74 {
75 builtin_t fun;
76 union argument *args;
77 } function;
78 struct node *arg;
79 struct
80 {
81 int op;
82 struct node *arg1;
83 struct node *arg2;
84 } bin;
85 struct mimetypes_string suffix;
86 } v;
87 };
88
89 static struct node *make_binary_node (int op,
90 struct node *left, struct node *rigth);
91 static struct node *make_negation_node (struct node *p);
92
93 static struct node *make_suffix_node (struct mimetypes_string *suffix);
94 static struct node *make_functional_node (char *ident, list_t list);
95
96 static int eval_rule (struct node *root);
97
98 struct rule_tab
99 {
100 char *type;
101 struct node *node;
102 };
103
104 static list_t rule_list;
105
106 %}
107
108 %token <string> IDENT IDENT_L
109 %token <string> STRING
110 %token EOL BOGUS
111
112 %type <string> string arg type
113 %type <list> arglist
114 %type <node> function stmt rule
115
116 %union {
117 struct mimetypes_string string;
118 list_t list;
119 int result;
120 struct node *node;
121 }
122
123 %%
124
125 input : list
126 ;
127
128 list : rule_line
129 | list eol rule_line
130 ;
131
132 rule_line: /* empty */
133 | type rule
134 {
135 struct rule_tab *p = mimetypes_malloc (sizeof (*p));
136 if (!rule_list)
137 list_create (&rule_list);
138 p->type = $1.ptr;
139 p->node = $2;
140 list_append (rule_list, p);
141 }
142 | error eol
143 {
144 if (arg_list)
145 list_destroy (&arg_list);
146 arg_list = NULL;
147 reset_lex ();
148 }
149 ;
150
151 eol : EOL
152 | eol EOL
153 ;
154
155 type : IDENT '/' IDENT
156 {
157 $$ = mimetypes_append_string2 (&$1, '/', &$3);
158 }
159 ;
160
161 rule : stmt
162 | rule stmt
163 {
164 $$ = make_binary_node (L_OR, $1, $2);
165 }
166 | rule ',' stmt
167 {
168 $$ = make_binary_node (L_OR, $1, $3);
169 }
170 | rule '+' stmt
171 {
172 $$ = make_binary_node (L_AND, $1, $3);
173 }
174 ;
175
176 stmt : '!' stmt
177 {
178 $$ = make_negation_node ($2);
179 }
180 | '(' rule ')'
181 {
182 $$ = $2;
183 }
184 | string
185 {
186 $$ = make_suffix_node (&$1);
187 }
188 | function
189 ;
190
191 string : STRING
192 | IDENT
193 ;
194
195 function : IDENT_L arglist ')'
196 {
197 reset_lex ();
198 $$ = make_functional_node ($1.ptr, $2);
199 if (!$$)
200 YYERROR;
201 }
202 ;
203
204 arglist : arg
205 {
206 list_create (&arg_list);
207 $$ = arg_list;
208 list_append ($$, mimetypes_string_dup (&$1));
209 }
210 | arglist ',' arg
211 {
212 list_append ($1, mimetypes_string_dup (&$3));
213 $$ = $1;
214 }
215 ;
216
217 arg : string
218 ;
219
220 %%
221
222 int
223 mimetypes_parse (const char *name)
224 {
225 int rc;
226 if (mimetypes_open (name))
227 return 1;
228 rc = yyparse ();
229 mimetypes_close ();
230 return rule_list == NULL;
231 }
232
233 void
234 mimetypes_gram_debug (int level)
235 {
236 yydebug = level;
237 }
238
239
240 static struct node *
241 make_node (enum node_type type)
242 {
243 struct node *p = mimetypes_malloc (sizeof *p);
244 p->type = type;
245 return p;
246 }
247
248 static struct node *
249 make_binary_node (int op, struct node *left, struct node *right)
250 {
251 struct node *node = make_node (binary_node);
252
253 node->v.bin.op = op;
254 node->v.bin.arg1 = left;
255 node->v.bin.arg2 = right;
256 return node;
257 }
258
259 struct node *
260 make_negation_node (struct node *p)
261 {
262 struct node *node = make_node (negation_node);
263 node->v.arg = p;
264 return node;
265 }
266
267 struct node *
268 make_suffix_node (struct mimetypes_string *suffix)
269 {
270 struct node *node = make_node (suffix_node);
271 node->v.suffix = *suffix;
272 return node;
273 }
274
275 struct builtin_tab
276 {
277 char *name;
278 char *args;
279 builtin_t handler;
280 };
281
282 /* match("pattern")
283 Pattern match on filename
284 */
285 static int
286 b_match (union argument *args)
287 {
288 return fnmatch (args[0].string->ptr, mimeview_file, 0) == 0;
289 }
290
291 /* ascii(offset,length)
292 True if bytes are valid printable ASCII (CR, NL, TAB,
293 BS, 32-126)
294 */
295 static int
296 b_ascii (union argument *args)
297 {
298 int i;
299 if (fseek (mimeview_fp, args[0].number, SEEK_SET) == -1)
300 {
301 mu_error ("fseek: %s", mu_strerror (errno));
302 return 0;
303 }
304
305 for (i = 0; i < args[1].number; i++)
306 {
307 int c = getc (mimeview_fp);
308 if (c == EOF)
309 break;
310 if (!isascii (c))
311 return 0;
312 }
313
314 return 1;
315 }
316
317 /* printable(offset,length)
318 True if bytes are printable 8-bit chars (CR, NL, TAB,
319 BS, 32-126, 128-254)
320 */
321 #define ISPRINT(c) ((c) &&\
322 (strchr ("\n\r\t\b",c) \
323 || (32<=(c) && (c)<=126) \
324 || (128<=(c) && (c)<=254)))
325 static int
326 b_printable (union argument *args)
327 {
328 int i;
329
330 if (fseek (mimeview_fp, args[0].number, SEEK_SET) == -1)
331 {
332 mu_error ("fseek: %s", mu_strerror (errno));
333 return 0;
334 }
335
336 for (i = 0; i < args[1].number; i++)
337 {
338 int c = getc (mimeview_fp);
339 if (c == EOF)
340 break;
341 if (!ISPRINT ((unsigned)c))
342 return 0;
343 }
344 return 1;
345 }
346
347 /* string(offset,"string")
348 True if bytes are identical to string
349 */
350 static int
351 b_string (union argument *args)
352 {
353 struct mimetypes_string *str = args[1].string;
354 int i;
355
356 if (fseek (mimeview_fp, args[0].number, SEEK_SET) == -1)
357 {
358 mu_error ("fseek: %s", mu_strerror (errno));
359 return 0;
360 }
361
362 for (i = 0; i < str->len; i++)
363 {
364 int c = getc (mimeview_fp);
365 if (c == EOF || c != str->ptr[i])
366 return 0;
367 }
368 return 1;
369 }
370
371 /* istring(offset,"string")
372 True if a case-insensitive comparison of the bytes is
373 identical
374 */
375 static int
376 b_istring (union argument *args)
377 {
378 int i;
379 struct mimetypes_string *str = args[1].string;
380
381 if (fseek (mimeview_fp, args[0].number, SEEK_SET) == -1)
382 {
383 mu_error ("fseek: %s", mu_strerror (errno));
384 return 0;
385 }
386
387 for (i = 0; i < str->len; i++)
388 {
389 int c = getc (mimeview_fp);
390 if (c == EOF || tolower (c) != tolower (str->ptr[i]))
391 return 0;
392 }
393 return 1;
394 }
395
396 /* char(offset,value)
397 True if byte is identical
398 */
399 static int
400 b_char (union argument *args)
401 {
402 if (fseek (mimeview_fp, args[0].number, SEEK_SET) == -1)
403 {
404 mu_error ("fseek: %s", mu_strerror (errno));
405 return 0;
406 }
407 return getc (mimeview_fp) == args[1].number;
408 }
409
410 /* short(offset,value)
411 True if 16-bit integer is identical
412 FIXME: Byte order
413 */
414 static int
415 b_short (union argument *args)
416 {
417 unsigned short val;
418 int rc;
419
420 if (fseek (mimeview_fp, args[0].number, SEEK_SET) == -1)
421 {
422 mu_error ("fseek: %s", mu_strerror (errno));
423 return 0;
424 }
425 rc = fread (&val, sizeof val, 1, mimeview_fp);
426
427 if (rc == -1)
428 {
429 mu_error ("fread: %s", mu_strerror (errno));
430 return 0;
431 }
432 else if (rc == 0)
433 return 0;
434 return val == args[1].number;
435 }
436
437 /* int(offset,value)
438 True if 32-bit integer is identical
439 FIXME: Byte order
440 */
441 static int
442 b_int (union argument *args)
443 {
444 unsigned int val;
445 int rc;
446
447 if (fseek (mimeview_fp, args[0].number, SEEK_SET) == -1)
448 {
449 mu_error ("fseek: %s", mu_strerror (errno));
450 return 0;
451 }
452 rc = fread (&val, sizeof val, 1, mimeview_fp);
453 if (rc == -1)
454 {
455 mu_error ("fread: %s", mu_strerror (errno));
456 return 0;
457 }
458 else if (rc == 0)
459 return 0;
460 return val == args[1].number;
461 }
462
463 /* locale("string")
464 True if current locale matches string
465 */
466 static int
467 b_locale (union argument *args)
468 {
469 abort (); /* FIXME */
470 return 0;
471 }
472
473 /* contains(offset,range,"string")
474 True if the range contains the string
475 */
476 static int
477 b_contains (union argument *args)
478 {
479 int i, count;
480 char *buf;
481 struct mimetypes_string *str = args[2].string;
482
483 if (fseek (mimeview_fp, args[0].number, SEEK_SET) == -1)
484 {
485 mu_error ("fseek: %s", mu_strerror (errno));
486 return 0;
487 }
488 buf = xmalloc (args[1].number);
489 count = fread (buf, 1, args[1].number, mimeview_fp);
490 if (count == -1)
491 {
492 mu_error ("fread: %s", mu_strerror (errno));
493 }
494 else if (count > str->len)
495 for (i = 0; i < count - str->len; i++)
496 if (buf[i] == str->ptr[0] && memcmp (buf + i, str->ptr, str->len) == 0)
497 {
498 free (buf);
499 return 1;
500 }
501 free (buf);
502 return 0;
503 }
504
505 static struct builtin_tab builtin_tab[] = {
506 { "match", "s", b_match },
507 { "ascii", "dd", b_ascii },
508 { "printable", "dd", b_printable },
509 { "string", "ds", b_string },
510 { "istring", "ds", b_istring },
511 { "char", "dc", b_char },
512 { "short", "dd", b_short },
513 { "int", "dd", b_int },
514 { "locale", "s", b_locale },
515 { "contains", "dds", b_contains },
516 { NULL }
517 };
518
519 struct node *
520 make_functional_node (char *ident, list_t list)
521 {
522 size_t count, i;
523 struct builtin_tab *p;
524 struct node *node;
525 union argument *args;
526 iterator_t itr;
527
528 for (p = builtin_tab; ; p++)
529 {
530 if (!p->name)
531 {
532 char *s;
533 asprintf (&s, _("%s: unknown function"), ident);
534 yyerror (s);
535 free (s);
536 return NULL;
537 }
538
539 if (strcmp (ident, p->name) == 0)
540 break;
541 }
542
543 list_count (list, &count);
544 i = strlen (p->args);
545
546 if (count < i)
547 {
548 char *s;
549 asprintf (&s, _("too few arguments in call to `%s'"), ident);
550 yyerror (s);
551 free (s);
552 return NULL;
553 }
554 else if (count > i)
555 {
556 char *s;
557 asprintf (&s, _("too many arguments in call to `%s'"), ident);
558 yyerror (s);
559 free (s);
560 return NULL;
561 }
562
563 args = mimetypes_malloc (count * sizeof *args);
564
565 list_get_iterator (list, &itr);
566 for (i = 0, iterator_first (itr); !iterator_is_done (itr);
567 iterator_next (itr), i++)
568 {
569 struct mimetypes_string *data;
570 char *tmp;
571
572 iterator_current (itr, (void **)&data);
573 switch (p->args[i])
574 {
575 case 'd':
576 args[i].number = strtoul (data->ptr, &tmp, 0);
577 if (*tmp)
578 goto err;
579 break;
580
581 case 's':
582 args[i].string = data;
583 break;
584
585 case 'c':
586 args[i].c = strtoul (data->ptr, &tmp, 0);
587 if (*tmp)
588 goto err;
589 break;
590
591 default:
592 abort ();
593 }
594 }
595
596 node = make_node (functional_node);
597 node->v.function.fun = p->handler;
598 node->v.function.args = args;
599 return node;
600
601 err:
602 {
603 char *s;
604 asprintf (&s,
605 _("argument %d has wrong type in call to `%s'"),
606 i, ident);
607 yyerror (s);
608 free (s);
609 return NULL;
610 }
611 }
612
613 static int
614 check_suffix (char *suf)
615 {
616 char *p = strrchr (mimeview_file, '.');
617 if (!p)
618 return 0;
619 return strcmp (p+1, suf) == 0;
620 }
621
622 static int
623 eval_rule (struct node *root)
624 {
625 int result;
626
627 switch (root->type)
628 {
629 case functional_node:
630 result = root->v.function.fun (root->v.function.args);
631 break;
632
633 case binary_node:
634 result = eval_rule (root->v.bin.arg1);
635 switch (root->v.bin.op)
636 {
637 case L_OR:
638 if (!result)
639 result |= eval_rule (root->v.bin.arg2);
640 break;
641
642 case L_AND:
643 if (result)
644 result &= eval_rule (root->v.bin.arg2);
645 break;
646
647 default:
648 abort ();
649 }
650 break;
651
652 case negation_node:
653 result = !eval_rule (root->v.arg);
654 break;
655
656 case suffix_node:
657 result = check_suffix (root->v.suffix.ptr);
658 break;
659
660 default:
661 abort ();
662 }
663 return result;
664 }
665
666 static int
667 evaluate (void *item, void *data)
668 {
669 struct rule_tab *p = item;
670 char **ptype = data;
671
672 if (eval_rule (p->node))
673 {
674 *ptype = p->type;
675 return 1;
676 }
677 return 0;
678 }
679
680 const char *
681 get_file_type ()
682 {
683 const char *type = NULL;
684 list_do (rule_list, evaluate, &type);
685 return type;
686 }
687
1 /* GNU Mailutils -- a suite of utilities for electronic mail
2 Copyright (C) 2005 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 #ifdef HAVE_CONFIG_H
19 # include <config.h>
20 #endif
21
22 #include <mimeview.h>
23 #include <sys/stat.h>
24
25 const char *program_version = "mimeview (" PACKAGE_STRING ")";
26 static char doc[] = N_("GNU mimeview -- display MIME files \
27 Default mime.types file is ") SYSCONFDIR "/cups/mime.types"
28 N_("\nDebug flags are:\n\
29 g - Mime.types parser traces\n\
30 l - Mime.types lexical analyzer traces\n\
31 0-9 - Set debugging level\n");
32
33 #define OPT_METAMAIL 256
34
35 static struct argp_option options[] = {
36 {"debug", 'd', N_("FLAGS"), OPTION_ARG_OPTIONAL,
37 N_("Enable debugging output"), 0},
38 {"mimetypes", 't', N_("FILE"), 0,
39 N_("Use this mime.types file"), 0},
40 {"dry-run", 'n', NULL, 0,
41 N_("Do not do anything, just print what whould be done"), 0},
42 {"metamail", OPT_METAMAIL, N_("FILE"), OPTION_ARG_OPTIONAL,
43 N_("Use metamail to display files"), 0},
44 {0, 0, 0, 0}
45 };
46
47 int debug_level; /* Debugging level set by --debug option */
48 static int dry_run; /* Dry run mode */
49 static char *metamail; /* Name of metamail program, if requested */
50 static char *mimetypes_config = SYSCONFDIR "/cups";
51
52 char *mimeview_file; /* Name of the file to view */
53 FILE *mimeview_fp; /* Its descriptor */
54
55 /* Default mailcap path, the $HOME/.mailcap: entry is prepended to it */
56 #define DEFAULT_MAILCAP \
57 "/usr/local/etc/mailcap:"\
58 "/usr/etc/mailcap:"\
59 "/etc/mailcap:"\
60 "/etc/mail/mailcap:"\
61 "/usr/public/lib/mailcap"
62
63
64 static error_t
65 parse_opt (int key, char *arg, struct argp_state *state)
66 {
67 switch (key)
68 {
69 case ARGP_KEY_INIT:
70 mimetypes_lex_debug (0);
71 mimetypes_gram_debug (0);
72 break;
73
74 case ARGP_KEY_FINI:
75 if (dry_run && !debug_level)
76 debug_level = 1;
77 break;
78
79 case 'd':
80 if (!arg)
81 arg = "9";
82 for (; *arg; arg++)
83 {
84 switch (*arg)
85 {
86 case 'l':
87 mimetypes_lex_debug (1);
88 break;
89
90 case 'g':
91 mimetypes_gram_debug (1);
92 break;
93
94 default:
95 debug_level = *arg - '0';
96 }
97 }
98 break;
99
100 case 'n':
101 dry_run = 1;
102 break;
103
104 case 't':
105 mimetypes_config = arg;
106 break;
107
108 case OPT_METAMAIL:
109 metamail = arg ? arg : "metamail";
110 break;
111
112 default:
113 return ARGP_ERR_UNKNOWN;
114 }
115 return 0;
116 }
117
118 static struct argp argp = {
119 options,
120 parse_opt,
121 N_("FILE [FILE ...]"),
122 doc,
123 NULL,
124 NULL, NULL
125 };
126
127 static const char *capa[] = {
128 "common",
129 "license",
130 NULL
131 };
132
133 static int
134 open_file (char *name)
135 {
136 struct stat st;
137
138 if (stat (name, &st))
139 {
140 mu_error (_("Cannot stat `%s': %s"), name, mu_strerror (errno));
141 return -1;
142 }
143 if (!S_ISREG (st.st_mode) && !S_ISLNK (st.st_mode))
144 {
145 mu_error (_("Not a regular file or symbolic link: `%s'"), name);
146 return -1;
147 }
148
149 mimeview_file = name;
150 mimeview_fp = fopen (name, "r");
151 if (mimeview_fp == NULL)
152 {
153 mu_error (_("Cannot open `%s': %s"), name, mu_strerror (errno));
154 return -1;
155 }
156 return 0;
157 }
158
159 void
160 close_file ()
161 {
162 fclose (mimeview_fp);
163 }
164
165 static struct obstack expand_stack;
166
167 static void
168 expand_string (char **pstr, const char *filename, const char *type)
169 {
170 char *p;
171 size_t namelen = strlen (filename);
172 size_t typelen = strlen (type);
173
174 for (p = *pstr; *p; )
175 {
176 switch (p[0])
177 {
178 case '%':
179 switch (p[1])
180 {
181 case 's':
182 obstack_grow (&expand_stack, filename, namelen);
183 p += 2;
184 break;
185
186 case 't':
187 obstack_grow (&expand_stack, type, typelen);
188 p += 2;
189 break;
190
191 case '{':
192 /* Hmm, we don't have content-type field, sorry... */
193 while (*p && *p != '}')
194 p++;
195 if (*p)
196 p++;
197 break;
198
199 /* FIXME: Handle %F and %n */
200 default:
201 obstack_1grow (&expand_stack, p[0]);
202 }
203 break;
204
205 case '\\':
206 if (p[1])
207 {
208 obstack_1grow (&expand_stack, p[1]);
209 p += 2;
210 }
211 else
212 {
213 obstack_1grow (&expand_stack, p[0]);
214 p++;
215 }
216 break;
217
218 case '"':
219 if (p[1] == p[0])
220 {
221 obstack_1grow (&expand_stack, '%');
222 p++;
223 }
224 else
225 {
226 obstack_1grow (&expand_stack, p[0]);
227 p++;
228 }
229 break;
230
231 default:
232 obstack_1grow (&expand_stack, p[0]);
233 p++;
234 }
235 }
236 obstack_1grow (&expand_stack, 0);
237 free (*pstr);
238 *pstr = obstack_finish (&expand_stack);
239 }
240
241 static int
242 find_entry (const char *file, const char *type,
243 mu_mailcap_entry_t *pentry,
244 mu_mailcap_t *pmc)
245 {
246 mu_mailcap_t mailcap;
247 int status;
248 stream_t stream;
249
250 DEBUG (2, (_("Trying %s...\n"), file));
251 status = file_stream_create (&stream, file, MU_STREAM_READ);
252 if (status)
253 {
254 mu_error ("cannot create file stream %s: %s",
255 file, mu_strerror (status));
256 return 0;
257 }
258
259 status = stream_open (stream);
260 if (status)
261 {
262 stream_destroy (&stream, stream_get_owner (stream));
263 if (status != ENOENT)
264 mu_error ("cannot open file stream %s: %s",
265 file, mu_strerror (status));
266 return 0;
267 }
268
269 status = mu_mailcap_create (&mailcap, stream);
270 if (status == 0)
271 {
272 size_t i, count = 0;
273
274 mu_mailcap_entries_count (mailcap, &count);
275 for (i = 1; i <= count; i++)
276 {
277 mu_mailcap_entry_t entry;
278 char buffer[256];
279
280 if (mu_mailcap_get_entry (mailcap, i, &entry))
281 continue;
282
283 /* typefield. */
284 mu_mailcap_entry_get_typefield (entry,
285 buffer, sizeof (buffer), NULL);
286
287 if (fnmatch (buffer, type, FNM_CASEFOLD) == 0)
288 {
289 DEBUG (2, (_("Found in %s\n"), file));
290 /* FIXME: Run test entry, if any */
291 *pmc = mailcap;
292 *pentry = entry;
293 return 1; /* We leave stream open! */
294 }
295 }
296 mu_mailcap_destroy (&mailcap);
297 }
298 else
299 {
300 mu_error ("cannot create mailcap for %s: %s",
301 file, mu_strerror (status));
302 }
303 return 0;
304 }
305
306 static void
307 dump_mailcap_entry (mu_mailcap_entry_t entry)
308 {
309 char buffer[256];
310 size_t i, count;
311
312 mu_mailcap_entry_get_typefield (entry, buffer,
313 sizeof (buffer), NULL);
314 printf ("typefield: %s\n", buffer);
315
316 /* view-command. */
317 mu_mailcap_entry_get_viewcommand (entry, buffer,
318 sizeof (buffer), NULL);
319 printf ("view-command: %s\n", buffer);
320
321 /* fields. */
322 mu_mailcap_entry_fields_count (entry, &count);
323 for (i = 1; i <= count; i++)
324 {
325 int status = mu_mailcap_entry_get_field (entry, i, buffer,
326 sizeof (buffer), NULL);
327 if (status)
328 {
329 mu_error (_("cannot retrieve field %lu: %s"),
330 (unsigned long) i,
331 mu_strerror (status));
332 break;
333 }
334 printf ("fields[%d]: %s\n", i, buffer);
335 }
336 printf ("\n");
337 }
338
339 int
340 run_mailcap (mu_mailcap_entry_t entry, const char *type)
341 {
342 char *view_command;
343 size_t size;
344 int flag;
345 int status;
346 int argc;
347 char **argv;
348 /* pid_t pager = -1; */
349
350 if (debug_level > 1)
351 dump_mailcap_entry (entry);
352
353 mu_mailcap_entry_get_viewcommand (entry, NULL, 0, &size);
354 size++;
355 view_command = xmalloc (size);
356 mu_mailcap_entry_get_viewcommand (entry, view_command, size, NULL);
357
358 /* NOTE: We don't create temporary file for %s, we just use
359 mimeview_file instead */
360 expand_string (&view_command, mimeview_file, type);
361 DEBUG (0, (_("Executing %s...\n"), view_command));
362
363 if (dry_run)
364 return 0;
365
366 status = argcv_get (view_command, "", NULL, &argc, &argv);
367 free (view_command);
368 if (status)
369 {
370 mu_error (_("Cannot parse command line: %s"), mu_strerror (status));
371 return 1;
372 }
373
374 /*
375 if (mu_mailcap_entry_coupiousoutput (entry, &flag) == 0 && flag)
376 pager = open_pager ();
377 */
378
379 if (pager <= 0)
380 {
381 if (mu_spawnvp (argv[0], argv, &status))
382 mu_error (_("Cannot execute command: %s"), mu_strerror (status));
383
384 if (debug_level)
385 {
386 if (WIFEXITED (status))
387 printf (_("Command exited with status %d\n"), WEXITSTATUS(status));
388 else if (WIFSIGNALED (status))
389 printf(_("Command terminated on signal %d\n"), WTERMSIG(status));
390 else
391 printf (_("Command terminated"));
392 }
393 }
394 argcv_free (argc, argv);
395 /* close_pager (pager); */
396 }
397
398 void
399 display_file_mailcap (const char *type)
400 {
401 char *p, *sp;
402 char *mailcap_path;
403 mu_mailcap_t mailcap = NULL;
404 mu_mailcap_entry_t entry = NULL;
405
406 mailcap_path = getenv ("MAILCAP");
407 if (!mailcap_path)
408 {
409 char *home = mu_get_homedir ();
410 asprintf (&mailcap_path, "%s/.mailcap:%s", home, DEFAULT_MAILCAP);
411 }
412 else
413 mailcap_path = strdup (mailcap_path);
414
415 obstack_init (&expand_stack);
416
417 for (p = strtok_r (mailcap_path, ":", &sp); p; p = strtok_r (NULL, ":", &sp))
418 {
419 if (find_entry (p, type, &entry, &mailcap))
420 {
421 run_mailcap (entry, type);
422 mu_mailcap_destroy (&mailcap);
423 break;
424 }
425 }
426 }
427
428 void
429 display_file (const char *type)
430 {
431 if (metamail)
432 {
433 int status;
434 const char *argv[6];
435
436 argv[0] = "metamail";
437 argv[1] = "-b";
438 argv[2] = "-c";
439 argv[3] = type;
440 argv[4] = mimeview_file;
441 argv[5] = NULL;
442
443 if (debug_level)
444 {
445 char *string;
446 argcv_string (5, argv, &string);
447 printf (_("Executing %s...\n"), string);
448 free (string);
449 }
450
451 if (!dry_run)
452 mu_spawnvp (metamail, argv, &status);
453 }
454 else
455 display_file_mailcap (type);
456 }
457
458 int
459 main (int argc, char **argv)
460 {
461 int index;
462
463 mu_init_nls ();
464 mu_argp_init (program_version, NULL);
465 mu_argp_parse (&argp, &argc, &argv, 0, capa, &index, NULL);
466
467 argc -= index;
468 argv += index;
469
470 if (argc == 0)
471 {
472 mu_error (_("No files given"));
473 return 1;
474 }
475
476 if (mimetypes_parse (mimetypes_config))
477 return 1;
478
479 while (argc--)
480 {
481 const char *type;
482
483 if (open_file (*argv++))
484 continue;
485 type = get_file_type ();
486 DEBUG (1, ("%s: %s\n", mimeview_file, type ? type : "?"));
487 if (type)
488 display_file (type);
489 close_file ();
490 }
491
492 return 0;
493 }
1 /* GNU Mailutils -- a suite of utilities for electronic mail
2 Copyright (C) 2005 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 #include <stdlib.h>
19 #include <stdio.h>
20 #include <string.h>
21 #ifdef HAVE_STRINGS_H
22 # include <strings.h>
23 #endif
24 #include <mailutils/mailutils.h>
25 #include <xalloc.h>
26 #include <fnmatch.h>
27 #define obstack_chunk_alloc malloc
28 #define obstack_chunk_free free
29 #include <obstack.h>
30
31 struct mimetypes_string
32 {
33 char *ptr;
34 size_t len;
35 };
36
37 int mimetypes_yylex (void);
38 int mimetypes_yyerror (char *s);
39
40 int mimetypes_open (const char *name);
41 void mimetypes_close (void);
42 int mimetypes_parse (const char *name);
43 void mimetypes_gram_debug (int level);
44 void mimetypes_lex_debug (int level);
45 void mimetypes_lex_init (void);
46 void reset_lex (void);
47 void *mimetypes_malloc (size_t size);
48
49 struct mimetypes_string mimetypes_append_string2 (struct mimetypes_string *s1,
50 char c,
51 struct mimetypes_string *s2);
52 struct mimetypes_string *mimetypes_string_dup (struct mimetypes_string *s);
53
54 const char *get_file_type (void);
55
56 extern char *mimeview_file;
57 extern FILE *mimeview_fp;
58 extern int debug_level;
59
60 #define DEBUG(l,f) if (debug_level > (l)) printf f