diff options
author | Sergey Poznyakoff <gray@gnu.org.ua> | 2005-03-08 16:54:11 +0000 |
---|---|---|
committer | Sergey Poznyakoff <gray@gnu.org.ua> | 2005-03-08 16:54:11 +0000 |
commit | 071c05523b657a805576f7bfc9562445b43afc20 (patch) | |
tree | ca1a367c6ae8643a1d2226f4e344560790aa2baa /mimeview | |
parent | 09617f838be0120dda02d162ddf9e74c75a30fcb (diff) | |
download | mailutils-071c05523b657a805576f7bfc9562445b43afc20.tar.gz mailutils-071c05523b657a805576f7bfc9562445b43afc20.tar.bz2 |
Added to the repository
Diffstat (limited to 'mimeview')
-rw-r--r-- | mimeview/Makefile.am | 54 | ||||
-rw-r--r-- | mimeview/mimetypes.l | 238 | ||||
-rw-r--r-- | mimeview/mimetypes.y | 687 | ||||
-rw-r--r-- | mimeview/mimeview.c | 493 | ||||
-rw-r--r-- | mimeview/mimeview.h | 60 |
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 % |