summaryrefslogtreecommitdiff
path: root/mimeview
diff options
context:
space:
mode:
authorSergey Poznyakoff <gray@gnu.org.ua>2005-03-08 16:54:11 +0000
committerSergey Poznyakoff <gray@gnu.org.ua>2005-03-08 16:54:11 +0000
commit071c05523b657a805576f7bfc9562445b43afc20 (patch)
treeca1a367c6ae8643a1d2226f4e344560790aa2baa /mimeview
parent09617f838be0120dda02d162ddf9e74c75a30fcb (diff)
downloadmailutils-071c05523b657a805576f7bfc9562445b43afc20.tar.gz
mailutils-071c05523b657a805576f7bfc9562445b43afc20.tar.bz2
Added to the repository
Diffstat (limited to 'mimeview')
-rw-r--r--mimeview/Makefile.am54
-rw-r--r--mimeview/mimetypes.l238
-rw-r--r--mimeview/mimetypes.y687
-rw-r--r--mimeview/mimeview.c493
-rw-r--r--mimeview/mimeview.h60
5 files changed, 1532 insertions, 0 deletions
diff --git a/mimeview/Makefile.am b/mimeview/Makefile.am
new file mode 100644
index 000000000..316d41031
--- /dev/null
+++ b/mimeview/Makefile.am
@@ -0,0 +1,54 @@
+## Process this file with GNU Automake to create Makefile.in
+
+## Copyright (C) 2005 Free Software Foundation, Inc.
+##
+## GNU Mailutils is free software; you can redistribute it and/or
+## modify it under the terms of the GNU General Public License as
+## published by the Free Software Foundation; either version 2, or (at
+## your option) any later version.
+##
+## This program is distributed in the hope that it will be useful, but
+## WITHOUT ANY WARRANTY; without even the implied warranty of
+## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+## General Public License for more details.
+##
+## You should have received a copy of the GNU General Public License
+## along with this program; if not, write to the Free Software
+## Foundation, Inc. 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+INCLUDES = -I$(top_srcdir)/include -I$(top_srcdir)/lib\
+ -I${top_builddir}/include/mailutils/gnu @INTLINCS@
+
+AM_CFLAGS = -DSYSCONFDIR=\"$(sysconfdir)\"
+
+bin_PROGRAMS = mimeview
+mimeview_SOURCES = \
+ mimeview.c \
+ mimetypes-gram.c \
+ mimetypes-lex.c \
+ mimetypes-decl.h \
+ mimeview.h
+
+YLWRAP = $(SHELL) $(top_srcdir)/scripts/ylwrap
+AM_YFLAGS=-vt
+AM_LEXFLAGS=-d
+EXTRA_DIST = mimetypes.y mimetypes.l
+
+mimetypes-gram.c mimetypes-decl.h: $(srcdir)/mimetypes.y
+ $(YLWRAP) "$(YACC) $(AM_YFLAGS) -d" $< \
+ y.tab.c mimetypes-gram.c y.tab.h mimetypes-decl.h \
+ y.output mimetypes.output \
+ -- -yy mimetypes_yy
+
+mimetypes-lex.c: $(srcdir)/mimetypes.l mimetypes-decl.h
+ $(YLWRAP) "$(LEX) $(AM_LEXFLAGS) $(LEXFLAGS)" \
+ $(srcdir)/mimetypes.l lex.yy.c mimetypes-lex.c \
+ -- -yy mimetypes_yy
+
+BUILT_SOURCES = mimetypes-gram.c mimetypes-lex.c mimetypes-decl.h
+
+mimeview_LDADD = \
+ ../mailbox/libmailbox.la\
+ ../lib/libmailutils.la \
+ @LTLIBINTL@
+
diff --git a/mimeview/mimetypes.l b/mimeview/mimetypes.l
new file mode 100644
index 000000000..5d5a90bcd
--- /dev/null
+++ b/mimeview/mimetypes.l
@@ -0,0 +1,238 @@
+%{
+/* GNU Mailutils -- a suite of utilities for electronic mail
+ Copyright (C) 2005 Free Software Foundation, Inc.
+
+ GNU Mailutils is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ GNU Mailutils is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GNU Mailutils; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <mimeview.h>
+#include <mimetypes-decl.h>
+#include <mu_asprintf.h>
+#include <unistd.h>
+#include <sys/stat.h>
+
+static int line_num;
+static const char *file_name;
+static int file_name_alloc;
+
+static struct obstack stack;
+static int prev_state;
+
+static unsigned
+digit_to_number (char c)
+{
+ return (unsigned) (c >= '0' && c <= '9' ? c-'0' :
+ c >= 'A' && c <= 'Z' ? c-'A'+10 :
+ c-'a'+10);
+}
+%}
+%x ARGS HEX
+X [0-9a-fA-F]
+IDENT [a-zA-Z_\.][a-zA-Z0-9_\.-]*
+WS [ \t]*
+%%
+ /* Comments */
+<INITIAL>#.*\n { line_num++; }
+<INITIAL>#.* /* end-of-file comment */;
+ /* Tokens */
+\\\n { line_num++; }
+\n { line_num++; return EOL; }
+{WS} ;
+{IDENT} {
+ obstack_grow (&stack, yytext, yyleng);
+ yylval.string.len = obstack_object_size (&stack);
+ obstack_1grow (&stack, 0);
+ yylval.string.ptr = obstack_finish (&stack);
+ return IDENT;
+}
+<INITIAL>{IDENT}"(" {
+ obstack_grow (&stack, yytext, yyleng-1);
+ yylval.string.len = obstack_object_size (&stack);
+ obstack_1grow (&stack, 0);
+ yylval.string.ptr = obstack_finish (&stack);
+ BEGIN(ARGS);
+ return IDENT_L;
+}
+<INITIAL,ARGS>\"[^\\"\n]*\" {
+ obstack_grow (&stack, yytext+1, yyleng-2);
+ yylval.string.len = obstack_object_size (&stack);
+ obstack_1grow (&stack, 0);
+ yylval.string.ptr = obstack_finish (&stack);
+ return STRING;
+}
+<INITIAL,ARGS>"<" {
+ prev_state = YYSTATE;
+ BEGIN(HEX);
+}
+<ARGS>[^ \t<\\\n),]+/[),] {
+ obstack_grow (&stack, yytext, yyleng);
+ yylval.string.len = obstack_object_size (&stack);
+ obstack_1grow (&stack, 0);
+ yylval.string.ptr = obstack_finish (&stack);
+ return STRING;
+}
+<ARGS>[^ \t<\\\n),]+< {
+ obstack_grow (&stack, yytext, yyleng);
+ prev_state = YYSTATE;
+ BEGIN(HEX);
+}
+<INITIAL>[^ \t<\\\n)+&]/[ \t\\\n)+&] {
+ obstack_grow (&stack, yytext, yyleng);
+ yylval.string.len = obstack_object_size (&stack);
+ obstack_1grow (&stack, 0);
+ yylval.string.ptr = obstack_finish (&stack);
+ return STRING;
+}
+<ARGS>[^ \t<\\\n),]/[ \t\\\n] {
+ obstack_grow (&stack, yytext, yyleng);
+ yylval.string.len = obstack_object_size (&stack);
+ obstack_1grow (&stack, 0);
+ yylval.string.ptr = obstack_finish (&stack);
+ return STRING;
+}
+<HEX>{X}{X} {
+ int c = digit_to_number (yytext[0]*16 + yytext[1]);
+ obstack_1grow (&stack, c);
+}
+<HEX>">"/[ \t\\\n,)] {
+ BEGIN(prev_state);
+ yylval.string.len = obstack_object_size (&stack);
+ obstack_1grow (&stack, 0);
+ yylval.string.ptr = obstack_finish (&stack);
+ return STRING;
+}
+<HEX>">" {
+ BEGIN(prev_state);
+}
+ /* Special cases: && and ||. Docs don't say anything about them, but
+ I've found them in my mime.types file... --Sergey */
+"&&" return '+';
+"||" return ',';
+ /* Operators */
+"!"|"+"|"("|")"|"/" return yytext[0];
+<ARGS>"," return yytext[0];
+<ARGS>")" { BEGIN(INITIAL); return yytext[0]; }
+<INITIAL,ARGS,HEX>. {
+ fprintf (stderr, "Invalid character '%c', state %d\n", yytext[0], YYSTATE);
+ abort();
+}
+%%
+
+void
+mimetypes_lex_debug (int level)
+{
+ yy_flex_debug = level;
+}
+
+int
+mimetypes_open (const char *name)
+{
+ struct stat st;
+ if (stat (name, &st))
+ {
+ mu_error (_("Cannot stat `%s': %s"), name, mu_strerror (errno));
+ return -1;
+ }
+
+ if (S_ISDIR (st.st_mode))
+ {
+ asprintf (&file_name, "%s/mime.types", name);
+ file_name_alloc = 1;
+ }
+ else
+ {
+ file_name = name;
+ file_name_alloc = 0;
+ }
+
+ yyin = fopen (file_name, "r");
+ if (!yyin)
+ {
+ mu_error (_("Cannot open `%s': %s"), file_name, mu_strerror (errno));
+ if (file_name_alloc)
+ {
+ free (file_name);
+ file_name_alloc = 0;
+ }
+ return -1;
+ }
+ line_num = 1;
+ obstack_init (&stack);
+ return 0;
+}
+
+void
+mimetypes_close ()
+{
+ fclose (yyin);
+ if (file_name_alloc)
+ {
+ free (file_name);
+ file_name_alloc = 0;
+ }
+}
+
+int
+yyerror (char *s)
+{
+ mu_error ("%s:%lu: %s", file_name, line_num, s);
+ return 0;
+}
+
+int
+yywrap ()
+{
+ return 1;
+}
+
+struct mimetypes_string
+mimetypes_append_string2 (struct mimetypes_string *s1,
+ char c,
+ struct mimetypes_string *s2)
+{
+ struct mimetypes_string r;
+
+ r.len = s1->len + s2->len + 1;
+ obstack_grow (&stack, s1->ptr, s1->len);
+ obstack_1grow (&stack, c);
+ obstack_grow (&stack, s2->ptr, s2->len);
+ obstack_1grow (&stack, 0);
+ r.ptr = obstack_finish (&stack);
+ return r;
+}
+
+struct mimetypes_string *
+mimetypes_string_dup (struct mimetypes_string *s)
+{
+ obstack_grow (&stack, s, sizeof *s);
+ return obstack_finish (&stack);
+}
+
+void *
+mimetypes_malloc (size_t size)
+{
+ return obstack_alloc(&stack, size);
+}
+
+void
+reset_lex ()
+{
+ BEGIN(INITIAL);
+}
+
+
diff --git a/mimeview/mimetypes.y b/mimeview/mimetypes.y
new file mode 100644
index 000000000..149936df6
--- /dev/null
+++ b/mimeview/mimetypes.y
@@ -0,0 +1,687 @@
+%{
+/* GNU Mailutils -- a suite of utilities for electronic mail
+ Copyright (C) 2005 Free Software Foundation, Inc.
+
+ GNU Mailutils is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ GNU Mailutils is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GNU Mailutils; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <mimeview.h>
+#include <mimetypes-decl.h>
+
+static void
+yyprint (FILE *output, unsigned short toknum, YYSTYPE val)
+{
+ switch (toknum)
+ {
+ case IDENT:
+ case IDENT_L:
+ case STRING:
+ fprintf (output, "[%lu] %s", (unsigned long) val.string.len,
+ val.string.ptr);
+ break;
+
+ case EOL:
+ default:
+ break;
+ }
+}
+
+#define YYPRINT yyprint
+
+static list_t arg_list; /* For error recovery */
+
+#define L_OR 0
+#define L_AND 1
+
+enum node_type
+ {
+ functional_node,
+ binary_node,
+ negation_node,
+ suffix_node
+ };
+
+union argument
+{
+ struct mimetypes_string *string;
+ unsigned number;
+ int c;
+};
+
+typedef int (*builtin_t) (union argument *args);
+
+struct node
+{
+ enum node_type type;
+ union
+ {
+ struct
+ {
+ builtin_t fun;
+ union argument *args;
+ } function;
+ struct node *arg;
+ struct
+ {
+ int op;
+ struct node *arg1;
+ struct node *arg2;
+ } bin;
+ struct mimetypes_string suffix;
+ } v;
+};
+
+static struct node *make_binary_node (int op,
+ struct node *left, struct node *rigth);
+static struct node *make_negation_node (struct node *p);
+
+static struct node *make_suffix_node (struct mimetypes_string *suffix);
+static struct node *make_functional_node (char *ident, list_t list);
+
+static int eval_rule (struct node *root);
+
+struct rule_tab
+{
+ char *type;
+ struct node *node;
+};
+
+static list_t rule_list;
+
+%}
+
+%token <string> IDENT IDENT_L
+%token <string> STRING
+%token EOL BOGUS
+
+%type <string> string arg type
+%type <list> arglist
+%type <node> function stmt rule
+
+%union {
+ struct mimetypes_string string;
+ list_t list;
+ int result;
+ struct node *node;
+}
+
+%%
+
+input : list
+ ;
+
+list : rule_line
+ | list eol rule_line
+ ;
+
+rule_line: /* empty */
+ | type rule
+ {
+ struct rule_tab *p = mimetypes_malloc (sizeof (*p));
+ if (!rule_list)
+ list_create (&rule_list);
+ p->type = $1.ptr;
+ p->node = $2;
+ list_append (rule_list, p);
+ }
+ | error eol
+ {
+ if (arg_list)
+ list_destroy (&arg_list);
+ arg_list = NULL;
+ reset_lex ();
+ }
+ ;
+
+eol : EOL
+ | eol EOL
+ ;
+
+type : IDENT '/' IDENT
+ {
+ $$ = mimetypes_append_string2 (&$1, '/', &$3);
+ }
+ ;
+
+rule : stmt
+ | rule stmt
+ {
+ $$ = make_binary_node (L_OR, $1, $2);
+ }
+ | rule ',' stmt
+ {
+ $$ = make_binary_node (L_OR, $1, $3);
+ }
+ | rule '+' stmt
+ {
+ $$ = make_binary_node (L_AND, $1, $3);
+ }
+ ;
+
+stmt : '!' stmt
+ {
+ $$ = make_negation_node ($2);
+ }
+ | '(' rule ')'
+ {
+ $$ = $2;
+ }
+ | string
+ {
+ $$ = make_suffix_node (&$1);
+ }
+ | function
+ ;
+
+string : STRING
+ | IDENT
+ ;
+
+function : IDENT_L arglist ')'
+ {
+ reset_lex ();
+ $$ = make_functional_node ($1.ptr, $2);
+ if (!$$)
+ YYERROR;
+ }
+ ;
+
+arglist : arg
+ {
+ list_create (&arg_list);
+ $$ = arg_list;
+ list_append ($$, mimetypes_string_dup (&$1));
+ }
+ | arglist ',' arg
+ {
+ list_append ($1, mimetypes_string_dup (&$3));
+ $$ = $1;
+ }
+ ;
+
+arg : string
+ ;
+
+%%
+
+int
+mimetypes_parse (const char *name)
+{
+ int rc;
+ if (mimetypes_open (name))
+ return 1;
+ rc = yyparse ();
+ mimetypes_close ();
+ return rule_list == NULL;
+}
+
+void
+mimetypes_gram_debug (int level)
+{
+ yydebug = level;
+}
+
+
+static struct node *
+make_node (enum node_type type)
+{
+ struct node *p = mimetypes_malloc (sizeof *p);
+ p->type = type;
+ return p;
+}
+
+static struct node *
+make_binary_node (int op, struct node *left, struct node *right)
+{
+ struct node *node = make_node (binary_node);
+
+ node->v.bin.op = op;
+ node->v.bin.arg1 = left;
+ node->v.bin.arg2 = right;
+ return node;
+}
+
+struct node *
+make_negation_node (struct node *p)
+{
+ struct node *node = make_node (negation_node);
+ node->v.arg = p;
+ return node;
+}
+
+struct node *
+make_suffix_node (struct mimetypes_string *suffix)
+{
+ struct node *node = make_node (suffix_node);
+ node->v.suffix = *suffix;
+ return node;
+}
+
+struct builtin_tab
+{
+ char *name;
+ char *args;
+ builtin_t handler;
+};
+
+/* match("pattern")
+ Pattern match on filename
+*/
+static int
+b_match (union argument *args)
+{
+ return fnmatch (args[0].string->ptr, mimeview_file, 0) == 0;
+}
+
+/* ascii(offset,length)
+ True if bytes are valid printable ASCII (CR, NL, TAB,
+ BS, 32-126)
+*/
+static int
+b_ascii (union argument *args)
+{
+ int i;
+ if (fseek (mimeview_fp, args[0].number, SEEK_SET) == -1)
+ {
+ mu_error ("fseek: %s", mu_strerror (errno));
+ return 0;
+ }
+
+ for (i = 0; i < args[1].number; i++)
+ {
+ int c = getc (mimeview_fp);
+ if (c == EOF)
+ break;
+ if (!isascii (c))
+ return 0;
+ }
+
+ return 1;
+}
+
+/* printable(offset,length)
+ True if bytes are printable 8-bit chars (CR, NL, TAB,
+ BS, 32-126, 128-254)
+*/
+#define ISPRINT(c) ((c) &&\
+ (strchr ("\n\r\t\b",c) \
+ || (32<=(c) && (c)<=126) \
+ || (128<=(c) && (c)<=254)))
+static int
+b_printable (union argument *args)
+{
+ int i;
+
+ if (fseek (mimeview_fp, args[0].number, SEEK_SET) == -1)
+ {
+ mu_error ("fseek: %s", mu_strerror (errno));
+ return 0;
+ }
+
+ for (i = 0; i < args[1].number; i++)
+ {
+ int c = getc (mimeview_fp);
+ if (c == EOF)
+ break;
+ if (!ISPRINT ((unsigned)c))
+ return 0;
+ }
+ return 1;
+}
+
+/* string(offset,"string")
+ True if bytes are identical to string
+*/
+static int
+b_string (union argument *args)
+{
+ struct mimetypes_string *str = args[1].string;
+ int i;
+
+ if (fseek (mimeview_fp, args[0].number, SEEK_SET) == -1)
+ {
+ mu_error ("fseek: %s", mu_strerror (errno));
+ return 0;
+ }
+
+ for (i = 0; i < str->len; i++)
+ {
+ int c = getc (mimeview_fp);
+ if (c == EOF || c != str->ptr[i])
+ return 0;
+ }
+ return 1;
+}
+
+/* istring(offset,"string")
+ True if a case-insensitive comparison of the bytes is
+ identical
+*/
+static int
+b_istring (union argument *args)
+{
+ int i;
+ struct mimetypes_string *str = args[1].string;
+
+ if (fseek (mimeview_fp, args[0].number, SEEK_SET) == -1)
+ {
+ mu_error ("fseek: %s", mu_strerror (errno));
+ return 0;
+ }
+
+ for (i = 0; i < str->len; i++)
+ {
+ int c = getc (mimeview_fp);
+ if (c == EOF || tolower (c) != tolower (str->ptr[i]))
+ return 0;
+ }
+ return 1;
+}
+
+/* char(offset,value)
+ True if byte is identical
+*/
+static int
+b_char (union argument *args)
+{
+ if (fseek (mimeview_fp, args[0].number, SEEK_SET) == -1)
+ {
+ mu_error ("fseek: %s", mu_strerror (errno));
+ return 0;
+ }
+ return getc (mimeview_fp) == args[1].number;
+}
+
+/* short(offset,value)
+ True if 16-bit integer is identical
+ FIXME: Byte order
+*/
+static int
+b_short (union argument *args)
+{
+ unsigned short val;
+ int rc;
+
+ if (fseek (mimeview_fp, args[0].number, SEEK_SET) == -1)
+ {
+ mu_error ("fseek: %s", mu_strerror (errno));
+ return 0;
+ }
+ rc = fread (&val, sizeof val, 1, mimeview_fp);
+
+ if (rc == -1)
+ {
+ mu_error ("fread: %s", mu_strerror (errno));
+ return 0;
+ }
+ else if (rc == 0)
+ return 0;
+ return val == args[1].number;
+}
+
+/* int(offset,value)
+ True if 32-bit integer is identical
+ FIXME: Byte order
+*/
+static int
+b_int (union argument *args)
+{
+ unsigned int val;
+ int rc;
+
+ if (fseek (mimeview_fp, args[0].number, SEEK_SET) == -1)
+ {
+ mu_error ("fseek: %s", mu_strerror (errno));
+ return 0;
+ }
+ rc = fread (&val, sizeof val, 1, mimeview_fp);
+ if (rc == -1)
+ {
+ mu_error ("fread: %s", mu_strerror (errno));
+ return 0;
+ }
+ else if (rc == 0)
+ return 0;
+ return val == args[1].number;
+}
+
+/* locale("string")
+ True if current locale matches string
+*/
+static int
+b_locale (union argument *args)
+{
+ abort (); /* FIXME */
+ return 0;
+}
+
+/* contains(offset,range,"string")
+ True if the range contains the string
+*/
+static int
+b_contains (union argument *args)
+{
+ int i, count;
+ char *buf;
+ struct mimetypes_string *str = args[2].string;
+
+ if (fseek (mimeview_fp, args[0].number, SEEK_SET) == -1)
+ {
+ mu_error ("fseek: %s", mu_strerror (errno));
+ return 0;
+ }
+ buf = xmalloc (args[1].number);
+ count = fread (buf, 1, args[1].number, mimeview_fp);
+ if (count == -1)
+ {
+ mu_error ("fread: %s", mu_strerror (errno));
+ }
+ else if (count > str->len)
+ for (i = 0; i < count - str->len; i++)
+ if (buf[i] == str->ptr[0] && memcmp (buf + i, str->ptr, str->len) == 0)
+ {
+ free (buf);
+ return 1;
+ }
+ free (buf);
+ return 0;
+}
+
+static struct builtin_tab builtin_tab[] = {
+ { "match", "s", b_match },
+ { "ascii", "dd", b_ascii },
+ { "printable", "dd", b_printable },
+ { "string", "ds", b_string },
+ { "istring", "ds", b_istring },
+ { "char", "dc", b_char },
+ { "short", "dd", b_short },
+ { "int", "dd", b_int },
+ { "locale", "s", b_locale },
+ { "contains", "dds", b_contains },
+ { NULL }
+};
+
+struct node *
+make_functional_node (char *ident, list_t list)
+{
+ size_t count, i;
+ struct builtin_tab *p;
+ struct node *node;
+ union argument *args;
+ iterator_t itr;
+
+ for (p = builtin_tab; ; p++)
+ {
+ if (!p->name)
+ {
+ char *s;
+ asprintf (&s, _("%s: unknown function"), ident);
+ yyerror (s);
+ free (s);
+ return NULL;
+ }
+
+ if (strcmp (ident, p->name) == 0)
+ break;
+ }
+
+ list_count (list, &count);
+ i = strlen (p->args);
+
+ if (count < i)
+ {
+ char *s;
+ asprintf (&s, _("too few arguments in call to `%s'"), ident);
+ yyerror (s);
+ free (s);
+ return NULL;
+ }
+ else if (count > i)
+ {
+ char *s;
+ asprintf (&s, _("too many arguments in call to `%s'"), ident);
+ yyerror (s);
+ free (s);
+ return NULL;
+ }
+
+ args = mimetypes_malloc (count * sizeof *args);
+
+ list_get_iterator (list, &itr);
+ for (i = 0, iterator_first (itr); !iterator_is_done (itr);
+ iterator_next (itr), i++)
+ {
+ struct mimetypes_string *data;
+ char *tmp;
+
+ iterator_current (itr, (void **)&data);
+ switch (p->args[i])
+ {
+ case 'd':
+ args[i].number = strtoul (data->ptr, &tmp, 0);
+ if (*tmp)
+ goto err;
+ break;
+
+ case 's':
+ args[i].string = data;
+ break;
+
+ case 'c':
+ args[i].c = strtoul (data->ptr, &tmp, 0);
+ if (*tmp)
+ goto err;
+ break;
+
+ default:
+ abort ();
+ }
+ }
+
+ node = make_node (functional_node);
+ node->v.function.fun = p->handler;
+ node->v.function.args = args;
+ return node;
+
+ err:
+ {
+ char *s;
+ asprintf (&s,
+ _("argument %d has wrong type in call to `%s'"),
+ i, ident);
+ yyerror (s);
+ free (s);
+ return NULL;
+ }
+}
+
+static int
+check_suffix (char *suf)
+{
+ char *p = strrchr (mimeview_file, '.');
+ if (!p)
+ return 0;
+ return strcmp (p+1, suf) == 0;
+}
+
+static int
+eval_rule (struct node *root)
+{
+ int result;
+
+ switch (root->type)
+ {
+ case functional_node:
+ result = root->v.function.fun (root->v.function.args);
+ break;
+
+ case binary_node:
+ result = eval_rule (root->v.bin.arg1);
+ switch (root->v.bin.op)
+ {
+ case L_OR:
+ if (!result)
+ result |= eval_rule (root->v.bin.arg2);
+ break;
+
+ case L_AND:
+ if (result)
+ result &= eval_rule (root->v.bin.arg2);
+ break;
+
+ default:
+ abort ();
+ }
+ break;
+
+ case negation_node:
+ result = !eval_rule (root->v.arg);
+ break;
+
+ case suffix_node:
+ result = check_suffix (root->v.suffix.ptr);
+ break;
+
+ default:
+ abort ();
+ }
+ return result;
+}
+
+static int
+evaluate (void *item, void *data)
+{
+ struct rule_tab *p = item;
+ char **ptype = data;
+
+ if (eval_rule (p->node))
+ {
+ *ptype = p->type;
+ return 1;
+ }
+ return 0;
+}
+
+const char *
+get_file_type ()
+{
+ const char *type = NULL;
+ list_do (rule_list, evaluate, &type);
+ return type;
+}
+
diff --git a/mimeview/mimeview.c b/mimeview/mimeview.c
new file mode 100644
index 000000000..a6ee82697
--- /dev/null
+++ b/mimeview/mimeview.c
@@ -0,0 +1,493 @@
+/* GNU Mailutils -- a suite of utilities for electronic mail
+ Copyright (C) 2005 Free Software Foundation, Inc.
+
+ GNU Mailutils is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2, or (at your option)
+ any later version.
+
+ GNU Mailutils is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GNU Mailutils; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <mimeview.h>
+#include <sys/stat.h>
+
+const char *program_version = "mimeview (" PACKAGE_STRING ")";
+static char doc[] = N_("GNU mimeview -- display MIME files \
+Default mime.types file is ") SYSCONFDIR "/cups/mime.types"
+N_("\nDebug flags are:\n\
+ g - Mime.types parser traces\n\
+ l - Mime.types lexical analyzer traces\n\
+ 0-9 - Set debugging level\n");
+
+#define OPT_METAMAIL 256
+
+static struct argp_option options[] = {
+ {"debug", 'd', N_("FLAGS"), OPTION_ARG_OPTIONAL,
+ N_("Enable debugging output"), 0},
+ {"mimetypes", 't', N_("FILE"), 0,
+ N_("Use this mime.types file"), 0},
+ {"dry-run", 'n', NULL, 0,
+ N_("Do not do anything, just print what whould be done"), 0},
+ {"metamail", OPT_METAMAIL, N_("FILE"), OPTION_ARG_OPTIONAL,
+ N_("Use metamail to display files"), 0},
+ {0, 0, 0, 0}
+};
+
+int debug_level; /* Debugging level set by --debug option */
+static int dry_run; /* Dry run mode */
+static char *metamail; /* Name of metamail program, if requested */
+static char *mimetypes_config = SYSCONFDIR "/cups";
+
+char *mimeview_file; /* Name of the file to view */
+FILE *mimeview_fp; /* Its descriptor */
+
+/* Default mailcap path, the $HOME/.mailcap: entry is prepended to it */
+#define DEFAULT_MAILCAP \
+ "/usr/local/etc/mailcap:"\
+ "/usr/etc/mailcap:"\
+ "/etc/mailcap:"\
+ "/etc/mail/mailcap:"\
+ "/usr/public/lib/mailcap"
+
+
+static error_t
+parse_opt (int key, char *arg, struct argp_state *state)
+{
+ switch (key)
+ {
+ case ARGP_KEY_INIT:
+ mimetypes_lex_debug (0);
+ mimetypes_gram_debug (0);
+ break;
+
+ case ARGP_KEY_FINI:
+ if (dry_run && !debug_level)
+ debug_level = 1;
+ break;
+
+ case 'd':
+ if (!arg)
+ arg = "9";
+ for (; *arg; arg++)
+ {
+ switch (*arg)
+ {
+ case 'l':
+ mimetypes_lex_debug (1);
+ break;
+
+ case 'g':
+ mimetypes_gram_debug (1);
+ break;
+
+ default:
+ debug_level = *arg - '0';
+ }
+ }
+ break;
+
+ case 'n':
+ dry_run = 1;
+ break;
+
+ case 't':
+ mimetypes_config = arg;
+ break;
+
+ case OPT_METAMAIL:
+ metamail = arg ? arg : "metamail";
+ break;
+
+ default:
+ return ARGP_ERR_UNKNOWN;
+ }
+ return 0;
+}
+
+static struct argp argp = {
+ options,
+ parse_opt,
+ N_("FILE [FILE ...]"),
+ doc,
+ NULL,
+ NULL, NULL
+};
+
+static const char *capa[] = {
+ "common",
+ "license",
+ NULL
+};
+
+static int
+open_file (char *name)
+{
+ struct stat st;
+
+ if (stat (name, &st))
+ {
+ mu_error (_("Cannot stat `%s': %s"), name, mu_strerror (errno));
+ return -1;
+ }
+ if (!S_ISREG (st.st_mode) && !S_ISLNK (st.st_mode))
+ {
+ mu_error (_("Not a regular file or symbolic link: `%s'"), name);
+ return -1;
+ }
+
+ mimeview_file = name;
+ mimeview_fp = fopen (name, "r");
+ if (mimeview_fp == NULL)
+ {
+ mu_error (_("Cannot open `%s': %s"), name, mu_strerror (errno));
+ return -1;
+ }
+ return 0;
+}
+
+void
+close_file ()
+{
+ fclose (mimeview_fp);
+}
+
+static struct obstack expand_stack;
+
+static void
+expand_string (char **pstr, const char *filename, const char *type)
+{
+ char *p;
+ size_t namelen = strlen (filename);
+ size_t typelen = strlen (type);
+
+ for (p = *pstr; *p; )
+ {
+ switch (p[0])
+ {
+ case '%':
+ switch (p[1])
+ {
+ case 's':
+ obstack_grow (&expand_stack, filename, namelen);
+ p += 2;
+ break;
+
+ case 't':
+ obstack_grow (&expand_stack, type, typelen);
+ p += 2;
+ break;
+
+ case '{':
+ /* Hmm, we don't have content-type field, sorry... */
+ while (*p && *p != '}')
+ p++;
+ if (*p)
+ p++;
+ break;
+
+ /* FIXME: Handle %F and %n */
+ default:
+ obstack_1grow (&expand_stack, p[0]);
+ }
+ break;
+
+ case '\\':
+ if (p[1])
+ {
+ obstack_1grow (&expand_stack, p[1]);
+ p += 2;
+ }
+ else
+ {
+ obstack_1grow (&expand_stack, p[0]);
+ p++;
+ }
+ break;
+
+ case '"':
+ if (p[1] == p[0])
+ {
+ obstack_1grow (&expand_stack, '%');
+ p++;
+ }
+ else
+ {
+ obstack_1grow (&expand_stack, p[0]);
+ p++;
+ }
+ break;
+
+ default:
+ obstack_1grow (&expand_stack, p[0]);
+ p++;
+ }
+ }
+ obstack_1grow (&expand_stack, 0);
+ free (*pstr);
+ *pstr = obstack_finish (&expand_stack);
+}
+
+static int
+find_entry (const char *file, const char *type,
+ mu_mailcap_entry_t *pentry,
+ mu_mailcap_t *pmc)
+{
+ mu_mailcap_t mailcap;
+ int status;
+ stream_t stream;
+
+ DEBUG (2, (_("Trying %s...\n"), file));
+ status = file_stream_create (&stream, file, MU_STREAM_READ);
+ if (status)
+ {
+ mu_error ("cannot create file stream %