Commit a79ec559 a79ec559859084cfe8a357bf9076d472fefbeeee by Sergey Poznyakoff

Improve mimeview

Old short option '-t' renamed to '-f' for consistency (-f stands for
--file in most other utilities).  Thus, 'mimevief -f my.types' reads
the file 'my.types'.

The '-t' option is reused as a short equivalent of the new
'--lint' option, which instructs the tool to check the syntax of the
mime.types file and exit, ignoring any surplus command line arguments.

Added support for priority and regex functions.

Improved debugging output.

* mimeview/mimetypes.l: Rewrite lexer in a cleaner way.
* mimeview/mimetypes.y: Rewrite parser.  Track locations.  Add line
information to nodes and rules.  Improve debugging
* mimeview/mimeview.c: Remove the debug configuration statement.  Using
debug { level } provides the same functionality.  Use mu_debug for debugging.
New option -t (--lint).  Rename the -t short option to -f.
* mimeview/mimeview.h: Define lexer and parser macros for location tracking.

* NEWS: Document changes to mimeview utility.
* doc/texinfo/programs.texi: Document changes to mimeview utility.

* lib/mailcap.c: Use standart mu_debug calls for debugging.
* lib/mailcap.h (display_stream_mailcap): Change last argument: take a
mu_debug_handle_t
* libmailutils/diag/debcat (app): New category.  Application-specific
debug
* mail/decode.c (display_submessage): Change invocation of
display_stream_mailcap.
* mail/mailvar.c: Setting "verbose" enables app.trace7, unsetting it
clears app.*
1 parent d2952d1c
GNU mailutils NEWS -- history of user-visible changes. 2017-04-19
GNU mailutils NEWS -- history of user-visible changes. 2017-05-31
Copyright (C) 2002-2017 Free Software Foundation, Inc.
See the end of file for copying conditions.
......@@ -136,6 +136,19 @@ the implementation in Scheme. The decision has therefore been taken to
discontinue the latter and to concentrate all efforts on the further
development of the former.
* mimeview
Old short option '-t' renamed to '-f' for consistency.
Thus, 'mimevief -f my.types' reads the file 'my.types'.
New option '--lint' (short '-t') instructs the tool to check the
syntax of the mime.types file and exit, ignoring any surplus command
line arguments.
Added support for priority and regex functions.
Debugging considerably improved.
Version 3.2 - 2017-03-11
......
......@@ -7309,22 +7309,28 @@ Enables debugging of @file{mime.types} parser
Enables debugging of @file{mime.types} lexical analyzer (warning:
produces @emph{very} copious output)
@item 1
@item 0
Prints basic information about actions to be executed and reports
about exit status of executed commands.
@item 2
@item 1
Additionally displays each file name along with its MIME type
@item 3
@item 2
Additionally traces the process of looking up the matching entry
in @code{mailcap} files.
@item digits from 4 to 9
The same as 3, currently.
@item 3
Additionally, enables debugging of @file{mime.types} parser (@samp{g}).
@item 4
Additionally, enables debugging of @file{mime.types} lexer (@samp{l}).
@item digits from 5 to 9
The same as 4, currently.
@end table
If @var{flags} are not given, the default @samp{9} is assumed.
If @var{flags} are not given, the default @samp{2} is assumed.
@item --metamail[=@var{file}]
Run @command{metamail} to display files, instead of using the internal
......@@ -7358,10 +7364,15 @@ a @asis{tty} device.
Do not do anything, just print what would be done. Implies
@option{--debug=1}, unless the debugging level is set up explicitly.
@item -t @var{file}
@item -f @var{file}
@itemx --mimetypes @var{file}
Use @var{file} as @file{mime.types} file. If @var{file} is a
directory, use @file{@var{file}/mime.types}
@item -t
@itemx --lint
Check syntax of the @file{mime.types} file and exit. Command line
arguments are ignored.
@end table
@node Mimeview Config
......@@ -7375,11 +7386,6 @@ The following configuration statements affect the behavior of
@item debug @tab @xref{Debug Statement}.
@end multitable
@deffn {Mimeview Config} debug @var{number}
Set @command{mimeview} debug level. @xref{Mimeview Invocation,
--debug}, for a description of debug levels.
@end deffn
@deffn {Mimeview Config} mimetypes @var{file}
Read @var{file} instead of the default @file{mime.types}.
@end deffn
......
......@@ -51,16 +51,14 @@ struct mime_context
int unlink_temp_file;
mu_list_t no_ask_types;
int debug_level;
int dh;
int flags;
};
#define DEBUG(c,l,f) if ((c)->debug_level > (l)) printf f
static int
mime_context_fill (struct mime_context *ctx, const char *file,
mu_stream_t input, mu_header_t hdr, const char *no_ask,
int interactive, int dry_run, int debug_level)
int interactive, int dry_run, mu_debug_handle_t dh)
{
struct mu_wordsplit ws;
size_t i;
......@@ -91,7 +89,7 @@ mime_context_fill (struct mime_context *ctx, const char *file,
ctx->flags |= FLAGS_INTERACTIVE;
if (dry_run)
ctx->flags |= FLAGS_DRY_RUN;
ctx->debug_level = debug_level;
ctx->dh = dh;
mu_list_create (&ctx->values);
......@@ -339,7 +337,7 @@ confirm_action (struct mime_context *ctx, const char *str)
char *type;
mime_context_get_content_type (ctx, &type);
if (dry_run_p (ctx) || !interactive_p (ctx) || mime_context_do_not_ask (ctx))
if (!interactive_p (ctx) || mime_context_do_not_ask (ctx))
return 1;
printf (_("Run `%s'?"), str);
......@@ -540,7 +538,7 @@ run_mailcap (mu_mailcap_entry_t entry, struct mime_context *ctx)
int outfd = -1;
pid_t pid;
if (ctx->debug_level > 1)
if (mu_debug_level_p (ctx->dh, MU_DEBUG_TRACE3))
dump_mailcap_entry (entry);
if (run_test (entry, ctx))
......@@ -571,10 +569,12 @@ run_mailcap (mu_mailcap_entry_t entry, struct mime_context *ctx)
pfd = NULL;
else
pfd = &fd;
DEBUG (ctx, 0, (_("Executing %s...\n"), view_command));
mu_debug (ctx->dh, MU_DEBUG_TRACE0, (_("executing %s...\n"), view_command));
if (!confirm_action (ctx, view_command))
return 1;
if (dry_run_p (ctx))
return 0;
flag = 0;
if (interactive_p (ctx)
......@@ -596,7 +596,7 @@ run_mailcap (mu_mailcap_entry_t entry, struct mime_context *ctx)
mu_error ("waitpid: %s", mu_strerror (errno));
break;
}
if (ctx->debug_level)
if (mu_debug_level_p (ctx->dh, MU_DEBUG_TRACE0))
print_exit_status (status);
}
return 0;
......@@ -610,7 +610,7 @@ find_entry (const char *file, struct mime_context *ctx)
mu_stream_t stream;
int rc = 1;
DEBUG (ctx, 2, (_("Trying %s...\n"), file));
mu_debug (ctx->dh, MU_DEBUG_TRACE1, (_("trying %s...\n"), file));
status = mu_file_stream_create (&stream, file, MU_STREAM_READ);
if (status)
{
......@@ -643,7 +643,7 @@ find_entry (const char *file, struct mime_context *ctx)
if (fnmatch (buffer, type, FNM_CASEFOLD) == 0)
{
DEBUG (ctx, 2, (_("Found in %s\n"), file));
mu_debug (ctx->dh, MU_DEBUG_TRACE1, (_("found in %s\n"), file));
if (run_mailcap (entry, ctx) == 0)
{
rc = 0;
......@@ -664,7 +664,7 @@ find_entry (const char *file, struct mime_context *ctx)
int
display_stream_mailcap (const char *ident, mu_stream_t stream, mu_header_t hdr,
const char *no_ask, int interactive, int dry_run,
int debug_level)
mu_debug_handle_t dh)
{
char *mailcap_path, *mailcap_path_tmp = NULL;
struct mu_wordsplit ws;
......@@ -672,7 +672,7 @@ display_stream_mailcap (const char *ident, mu_stream_t stream, mu_header_t hdr,
int rc = 1;
if (mime_context_fill (&ctx, ident, stream, hdr,
no_ask, interactive, dry_run, debug_level))
no_ask, interactive, dry_run, dh))
return 1;
mailcap_path = getenv ("MAILCAP");
if (!mailcap_path)
......
extern int display_stream_mailcap (const char *ident, mu_stream_t stream,
mu_header_t hdr, const char *no_ask,
int interactive, int dry_run,
int debug_level);
mu_debug_handle_t dh);
......
......@@ -42,3 +42,4 @@ assoc
acl
server
tls
app
......
......@@ -284,14 +284,11 @@ display_submessage (struct mime_descend_closure *closure, void *data)
if (mailvar_is_true ("metamail"))
{
char *no_ask = NULL;
int debug = 0;
mailvar_get (&no_ask, "mimenoask", mailvar_type_string, 0);
if (mailvar_is_true ("verbose"))
debug = 9;
builtin_display = display_stream_mailcap (NULL, stream, hdr, no_ask,
interactive, 0, debug);
interactive, 0,
MU_DEBCAT_APP);
}
if (builtin_display)
......
......@@ -44,6 +44,8 @@ static int set_replyregex (enum mailvar_cmd cmd,
struct mailvar_variable *);
static int set_screen (enum mailvar_cmd cmd,
struct mailvar_variable *);
static int set_verbose (enum mailvar_cmd cmd,
struct mailvar_variable *);
static int set_debug (enum mailvar_cmd cmd,
struct mailvar_variable *);
static int set_folder (enum mailvar_cmd cmd,
......@@ -272,7 +274,8 @@ struct mailvar_symbol mailvar_tab[] =
{ { "varstrict", }, MAILVAR_ALIAS },
{ { "verbose", },
MAILVAR_TYPEMASK (mailvar_type_boolean),
N_("verbosely trace the process of message delivery") },
N_("verbosely trace the process of message delivery"),
set_verbose },
{ { "xmailer", },
MAILVAR_TYPEMASK (mailvar_type_boolean),
N_("add the `X-Mailer' header to the outgoing messages") },
......@@ -666,6 +669,21 @@ set_screen (enum mailvar_cmd cmd, struct mailvar_variable *var)
#define DEFAULT_DEBUG_LEVEL MU_DEBUG_LEVEL_UPTO (MU_DEBUG_TRACE7)
static int
set_verbose (enum mailvar_cmd cmd, struct mailvar_variable *var)
{
switch (cmd)
{
case mailvar_cmd_set:
mu_debug_set_category_level (MU_DEBCAT_APP, DEFAULT_DEBUG_LEVEL);
break;
case mailvar_cmd_unset:
mu_debug_set_category_level (MU_DEBCAT_APP, 0);
}
return 0;
}
static int
set_debug (enum mailvar_cmd cmd, struct mailvar_variable *var)
{
mu_debug_clear_all ();
......
......@@ -29,12 +29,10 @@
#include <mimetypes-decl.h>
#include <mailutils/io.h>
static int line_num;
static char *file_name;
static int file_name_alloc;
static struct mu_locus loc;
static int newline;
static mu_opool_t pool;
static int prev_state;
static unsigned
digit_to_number (char c)
......@@ -43,106 +41,121 @@ digit_to_number (char c)
c >= 'A' && c <= 'Z' ? c-'A'+10 :
c-'a'+10);
}
static void
advance_locus (void)
{
if (newline)
{
loc.mu_line++;
loc.mu_col = 1;
}
yylloc.beg = loc;
loc.mu_col += yyleng;
yylloc.end = loc;
yylloc.end.mu_col--;
newline = yytext[yyleng-1] == '\n';
mu_stream_ioctl (mu_strerr, MU_IOCTL_LOGSTREAM,
MU_IOCTL_LOGSTREAM_SET_LOCUS, &loc);
}
#define YY_USER_ACTION advance_locus ();
%}
%option nounput
%option noinput
%x ARGS HEX
%s RULE ARGS
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 */;
^#.*\n ;
/* Tokens */
\\\n { line_num++; }
\n { line_num++; return EOL; }
\\\n ;
\n+ { loc.mu_line += yyleng - 1; return EOL; }
{WS} ;
{IDENT} {
<INITIAL,RULE>^[^ \t\n/]+"/"[^ \t\n]+ {
mu_opool_append (pool, yytext, yyleng);
mu_opool_append_char (pool, 0);
yylval.string.ptr = mu_opool_finish (pool, &yylval.string.len);
return IDENT;
BEGIN (RULE);
return TYPE;
}
<RULE>"priority"/"(" {
return PRIORITY;
}
<INITIAL>{IDENT}"(" {
mu_opool_append (pool, yytext, yyleng-1);
<RULE>{IDENT}/"(" {
mu_opool_append (pool, yytext, yyleng);
mu_opool_append_char (pool, 0);
yylval.string.ptr = mu_opool_finish (pool, &yylval.string.len);
BEGIN(ARGS);
return IDENT_L;
return IDENT;
}
<INITIAL,ARGS>\"[^\\"\n]*\" {
mu_opool_append (pool, yytext+1, yyleng-2);
<RULE>{IDENT} {
mu_opool_append (pool, yytext, yyleng);
mu_opool_append_char (pool, 0);
yylval.string.ptr = mu_opool_finish (pool, &yylval.string.len);
return STRING;
}
<INITIAL,ARGS>"<" {
prev_state = YYSTATE;
BEGIN(HEX);
}
<ARGS>[^ \t<\\\n),]+/[),] {
mu_opool_append (pool, yytext, yyleng);
<RULE,ARGS>\"[^"\n]*\" {
mu_opool_append (pool, yytext+1, yyleng-2);
mu_opool_append_char (pool, 0);
yylval.string.ptr = mu_opool_finish (pool, &yylval.string.len);
return STRING;
}
<ARGS>[^ \t<\\\n),]+< {
mu_opool_append (pool, yytext, yyleng);
prev_state = YYSTATE;
BEGIN(HEX);
<RULE,ARGS>"<"({X}{X})+">" {
int i;
for (i = 0; i < yyleng; i += 2)
{
mu_opool_append_char (pool, digit_to_number (yytext[i])*16
+ digit_to_number (yytext[i+1]));
}
yylval.string.ptr = mu_opool_finish (pool, &yylval.string.len);
return STRING;
}
<INITIAL>[^ \t<\\\n)+,&]/[ \t\\\n)+,&] {
<ARGS>[^ \t<\n),<"]+/[),<"] {
mu_opool_append (pool, yytext, yyleng);
mu_opool_append_char (pool, 0);
yylval.string.ptr = mu_opool_finish (pool, &yylval.string.len);
return STRING;
}
<ARGS>[^ \t<\\\n),]/[ \t\\\n] {
<RULE>[^ \t<\\\n)+,&]/[ \t\\\n)+,&] {
mu_opool_append (pool, yytext, yyleng);
mu_opool_append_char (pool, 0);
yylval.string.ptr = mu_opool_finish (pool, &yylval.string.len);
return STRING;
}
<HEX>{X}{X} {
int c = digit_to_number (yytext[0])*16 + digit_to_number (yytext[1]);
mu_opool_append_char (pool, c);
}
<HEX>">"/[ \t\\\n,)] {
BEGIN(prev_state);
<ARGS>[^ \t<\\\n),]/[ \t\\\n] {
mu_opool_append (pool, yytext, yyleng);
mu_opool_append_char (pool, 0);
yylval.string.ptr = mu_opool_finish (pool, &yylval.string.len);
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];
<RULE>"!"|"+"|","|"("|")"|"/" 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();
<ARGS>")" { BEGIN (RULE); return yytext[0]; }
<*>. {
mu_error ("invalid character '%c', state %d", yytext[0], YYSTATE);
return BOGUS;
}
%%
void
mimetypes_lex_debug (int level)
{
yy_flex_debug = level;
}
int
mimetypes_open (const char *name)
{
struct stat st;
int mode;
yy_flex_debug = mu_debug_level_p (MU_DEBCAT_MIME, MU_DEBUG_TRACE4);
if (stat (name, &st))
{
mu_error (_("cannot stat `%s': %s"), name, mu_strerror (errno));
......@@ -150,28 +163,29 @@ mimetypes_open (const char *name)
}
if (S_ISDIR (st.st_mode))
{
file_name = mu_make_file_name (name, "mime.types");
file_name_alloc = 1;
}
loc.mu_file = mu_make_file_name (name, "mime.types");
else
{
file_name = (char*) name;
file_name_alloc = 0;
}
loc.mu_file = mu_strdup (name);
loc.mu_line = 1;
loc.mu_col = 1;
newline = 0;
yyin = fopen (file_name, "r");
yyin = fopen (loc.mu_file, "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;
}
mu_error (_("cannot open `%s': %s"), loc.mu_file, mu_strerror (errno));
free (loc.mu_file);
return -1;
}
line_num = 1;
mu_stream_ioctl (mu_strerr, MU_IOCTL_LOGSTREAM,
MU_IOCTL_LOGSTREAM_GET_MODE, &mode);
mode |= MU_LOGMODE_LOCUS;
mu_stream_ioctl (mu_strerr, MU_IOCTL_LOGSTREAM,
MU_IOCTL_LOGSTREAM_SET_MODE, &mode);
mu_stream_ioctl (mu_strerr, MU_IOCTL_LOGSTREAM,
MU_IOCTL_LOGSTREAM_SET_LOCUS, &loc);
mu_opool_create (&pool, MU_OPOOL_ENOMEMABRT);
return 0;
}
......@@ -179,18 +193,25 @@ mimetypes_open (const char *name)
void
mimetypes_close ()
{
int mode;
fclose (yyin);
if (file_name_alloc)
{
free (file_name);
file_name_alloc = 0;
}
/* FIXME: Don't free (loc.mu_file), because it is referenced by
mu_locus structures in the parse tree */
mu_stream_ioctl (mu_strerr, MU_IOCTL_LOGSTREAM,
MU_IOCTL_LOGSTREAM_GET_MODE, &mode);
mode &= ~MU_LOGMODE_LOCUS;
mu_stream_ioctl (mu_strerr, MU_IOCTL_LOGSTREAM,
MU_IOCTL_LOGSTREAM_SET_MODE, &mode);
mu_stream_ioctl (mu_strerr, MU_IOCTL_LOGSTREAM,
MU_IOCTL_LOGSTREAM_SET_LOCUS, NULL);
}
int
yyerror (char *s)
{
mu_error ("%s:%d: %s", file_name, line_num, s);
mu_error (s);
return 0;
}
......@@ -200,22 +221,6 @@ 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;
mu_opool_append (pool, s1->ptr, s1->len);
mu_opool_append_char (pool, c);
mu_opool_append (pool, s2->ptr, s2->len);
mu_opool_append_char (pool, 0);
r.ptr = mu_opool_finish (pool, NULL);
return r;
}
struct mimetypes_string *
mimetypes_string_dup (struct mimetypes_string *s)
{
......@@ -231,9 +236,21 @@ mimetypes_malloc (size_t size)
}
void
reset_lex ()
lex_arglist (int enable)
{
BEGIN(INITIAL);
if (enable)
BEGIN (ARGS);
else
BEGIN (RULE);
}
void
lex_concat (struct concat_segm *p, struct mimetypes_string *ret)
{
for (; p; p = p->next)
{
mu_opool_appendz (pool, p->val);
}
mu_opool_append_char (pool, 0);
ret->ptr = mu_opool_finish (pool, &ret->len);
}
......
......@@ -23,6 +23,7 @@
#include <mailutils/cctype.h>
#include <mimeview.h>
#include <mimetypes-decl.h>
#include <regex.h>
static void
yyprint (FILE *output, unsigned short toknum, YYSTYPE val)
......@@ -30,14 +31,20 @@ 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:
fprintf (output, "\\n");
break;
default:
if (mu_isprint (toknum))
fprintf (output, "'%c'", toknum);
else
fprintf (output, "tok(%d)", toknum);
break;
}
}
......@@ -51,6 +58,7 @@ static mu_list_t arg_list; /* For error recovery */
enum node_type
{
true_node,
functional_node,
binary_node,
negation_node,
......@@ -62,6 +70,7 @@ union argument
struct mimetypes_string *string;
unsigned number;
int c;
regex_t rx;
};
typedef int (*builtin_t) (union argument *args);
......@@ -69,6 +78,7 @@ typedef int (*builtin_t) (union argument *args);
struct node
{
enum node_type type;
struct mu_locus_range loc;
union
{
struct
......@@ -87,41 +97,56 @@ struct node
} v;
};
static struct node *make_node (enum node_type type,
struct mu_locus_range const *loc);
static struct node *make_binary_node (int op,
struct node *left, struct node *rigth);
static struct node *make_negation_node (struct node *p);
struct node *left, struct node *rigth,
struct mu_locus_range const *loc);
static struct node *make_negation_node (struct node *p,
struct mu_locus_range const *loc);
static struct node *make_suffix_node (struct mimetypes_string *suffix);
static struct node *make_functional_node (char *ident, mu_list_t list);
static struct node *make_suffix_node (struct mimetypes_string *suffix,
struct mu_locus_range const *loc);
static struct node *make_functional_node (char *ident, mu_list_t list,
struct mu_locus_range const *loc);
static int eval_rule (struct node *root);
struct rule_tab
{
char *type;
int priority;
struct mu_locus_range loc;
struct node *node;
};
static mu_list_t rule_list;
%}
%token <string> IDENT IDENT_L
%locations
%token <string> TYPE IDENT
%token <string> STRING
%token EOL BOGUS
%token EOL BOGUS PRIORITY
%left ','
%left '+'
%type <string> string arg type
%type <string> string arg
%type <list> arglist
%type <node> function stmt rule
%type <node> function stmt rule maybe_rule
%type <result> priority maybe_priority
%type <concat> concat;
%type <segment> simple_string
%union {
struct mimetypes_string string;
char *s;
mu_list_t list;
int result;
struct node *node;
struct { struct concat_segm *head, *tail; } concat;
struct concat_segm *segment;
}
%%
......@@ -130,56 +155,65 @@ input : list
;
list : rule_line
| list eol rule_line
| list EOL rule_line
;
rule_line: /* empty */
| type rule
| TYPE maybe_rule maybe_priority
{
struct rule_tab *p = mimetypes_malloc (sizeof (*p));
if (!rule_list)
mu_list_create (&rule_list);
p->type = $1.ptr;
p->node = $2;
p->priority = $3;
p->loc.beg = @1.beg;
p->loc.end = @3.end;
mu_list_append (rule_list, p);
}
| error eol
| error EOL
{
if (arg_list)
mu_list_destroy (&arg_list);
arg_list = NULL;
reset_lex ();
lex_arglist (0);
}
;
eol : EOL
| eol EOL
;
type : IDENT '/' IDENT
maybe_rule: /* empty */
{
$$ = mimetypes_append_string2 (&$1, '/', &$3);
$$ = make_node (true_node, &yylloc);
}
| rule
;
rule : stmt
| rule rule %prec ','
{
$$ = make_binary_node (L_OR, $1, $2);
struct mu_locus_range lr;
lr.beg = @1.beg;
lr.end = @2.end;
$$ = make_binary_node (L_OR, $1, $2, &lr);
}
| rule ',' rule
{
$$ = make_binary_node (L_OR, $1, $3);
struct mu_locus_range lr;
lr.beg = @1.beg;
lr.end = @3.end;
$$ = make_binary_node (L_OR, $1, $3, &lr);
}
| rule '+' rule
{
$$ = make_binary_node (L_AND, $1, $3);
struct mu_locus_range lr;
lr.beg = @1.beg;
lr.end = @3.end;
$$ = make_binary_node (L_AND, $1, $3, &lr);
}
;
stmt : '!' stmt
{
$$ = make_negation_node ($2);
$$ = make_negation_node ($2, &@2);
}
| '(' rule ')'
{
......@@ -187,19 +221,79 @@ stmt : '!' stmt
}
| string
{
$$ = make_suffix_node (&$1);
$$ = make_suffix_node (&$1, &@1);
}
| function
;
string : STRING
| IDENT
string : concat
{
lex_concat ($1.head, &$$);
}
;
concat : simple_string
{
$$.head = $$.tail = $1;
}
| concat simple_string
{
$$.tail->next = $2;
$$.tail = $2;
}
;
simple_string : STRING
{
$$ = mu_alloc (sizeof $$);
$$->next = NULL;
$$->val = $1.ptr;
}
;
priority : PRIORITY oparen arglist cparen
{
size_t count = 0;
struct mimetypes_string *arg;
mu_list_count ($3, &count);
if (count != 1)
{
yyerror (_("priority takes single numberic argument"));
YYERROR;
}
mu_list_head ($3, (void**) &arg);
$$ = atoi (arg->ptr);
mu_list_destroy (&$3);
}
;
maybe_priority: /* empty */
{
$$ = 100;
}
| priority
;
function : IDENT_L arglist ')'
oparen : '('
{
reset_lex ();
$$ = make_functional_node ($1.ptr, $2);
lex_arglist (1);
}
;
cparen : ')'
{
lex_arglist (0);
}
;
function : IDENT oparen arglist cparen
{
struct mu_locus_range lr;
lr.beg = @1.beg;
lr.end = @4.end;
$$ = make_functional_node ($1.ptr, $3, &lr);
if (!$$)
YYERROR;
}
......@@ -229,30 +323,26 @@ mimetypes_parse (const char *name)
int rc;
if (mimetypes_open (name))
return 1;
yydebug = mu_debug_level_p (MU_DEBCAT_MIME, MU_DEBUG_TRACE3);
rc = yyparse ();
mimetypes_close ();
return rc || rule_list == NULL;
}
void
mimetypes_gram_debug (int level)
{
yydebug = level;
}
static struct node *
make_node (enum node_type type)
make_node (enum node_type type, struct mu_locus_range const *loc)
{
struct node *p = mimetypes_malloc (sizeof *p);
p->type = type;
p->loc = *loc;
return p;
}
static struct node *
make_binary_node (int op, struct node *left, struct node *right)
make_binary_node (int op, struct node *left, struct node *right,
struct mu_locus_range const *loc)
{
struct node *node = make_node (binary_node);
struct node *node = make_node (binary_node, loc);
node->v.bin.op = op;
node->v.bin.arg1 = left;
......@@ -261,17 +351,18 @@ make_binary_node (int op, struct node *left, struct node *right)
}
struct node *
make_negation_node (struct node *p)
make_negation_node (struct node *p, struct mu_locus_range const *loc)
{
struct node *node = make_node (negation_node);
struct node *node = make_node (negation_node, loc);
node->v.arg = p;
return node;
}
struct node *
make_suffix_node (struct mimetypes_string *suffix)
make_suffix_node (struct mimetypes_string *suffix,
struct mu_locus_range const *loc)
{
struct node *node = make_node (suffix_node);
struct node *node = make_node (suffix_node, loc);
node->v.suffix = *suffix;
return node;
}
......@@ -508,7 +599,7 @@ b_contains (union argument *args)
buf = mu_alloc (args[1].number);
rc = mu_stream_read (mimeview_stream, buf, args[1].number, &count);
if (count != args[1].number)
if (rc)
{
mu_diag_funcall (MU_DIAG_ERROR, "mu_stream_read", NULL, rc);
}
......@@ -523,10 +614,41 @@ b_contains (union argument *args)
return 0;
}
#define MIME_MAX_BUFFER 4096
/* regex(offset,"regex") True if bytes match regular expression
*/
static int
b_regex (union argument *args)
{
size_t count;
int rc;
char buf[MIME_MAX_BUFFER];
rc = mu_stream_seek (mimeview_stream, args[0].number, MU_SEEK_SET, NULL);
if (rc)
{
mu_diag_funcall (MU_DIAG_ERROR, "mu_stream_seek", NULL, rc);
return 0;
}
rc = mu_stream_read (mimeview_stream, buf, sizeof buf - 1, &count);
if (rc)
{
mu_diag_funcall (MU_DIAG_ERROR, "mu_stream_read", NULL, rc);
return 0;
}
buf[count] = 0;
return regexec (&args[1].rx, buf, 0, NULL, 0) == 0;
}
static struct builtin_tab builtin_tab[] = {
{ "match", "s", b_match },
{ "ascii", "dd", b_ascii },
{ "printable", "dd", b_printable },
{ "regex", "dx", b_regex },
{ "string", "ds", b_string },
{ "istring", "ds", b_istring },
{ "char", "dc", b_char },
......@@ -538,13 +660,15 @@ static struct builtin_tab builtin_tab[] = {
};
struct node *
make_functional_node (char *ident, mu_list_t list)
make_functional_node (char *ident, mu_list_t list,
struct mu_locus_range const *loc)
{
size_t count, i;
struct builtin_tab *p;
struct node *node;
union argument *args;
mu_iterator_t itr;
int rc;
for (p = builtin_tab; ; p++)
{
......@@ -603,6 +727,30 @@ make_functional_node (char *ident, mu_list_t list)
args[i].string = data;
break;
case 'x':
{
char *s;
rc = mu_c_str_unescape_trans (data->ptr,
"\\\\\"\"a\ab\bf\fn\nr\rt\tv\v", &s);
if (rc)
{
mu_diag_funcall (MU_DIAG_ERROR, "mu_c_str_unescape_trans",
data->ptr, rc);
return NULL;
}
rc = regcomp (&args[i].rx, s, REG_EXTENDED|REG_NOSUB);
free (s);
if (rc)
{
char errbuf[512];
regerror (rc, &args[i].rx, errbuf, sizeof errbuf);
yyerror (errbuf);
return NULL;
}
}
break;
case 'c':
args[i].c = strtoul (data->ptr, &tmp, 0);
if (*tmp)
......@@ -614,7 +762,7 @@ make_functional_node (char *ident, mu_list_t list)
}
}
node = make_node (functional_node);
node = make_node (functional_node, loc);
node->v.function.fun = p->handler;
node->v.function.args = args;
return node;
......@@ -640,6 +788,45 @@ check_suffix (char *suf)
return strcmp (p+1, suf) == 0;
}
void
mime_debug (int lev, struct mu_locus_range const *loc, char const *fmt, ...)
{
if (mu_debug_level_p (MU_DEBCAT_MIME, lev))
{
va_list ap;
if (loc->beg.mu_col == 0)
mu_debug_log_begin ("%s:%u", loc->beg.mu_file, loc->beg.mu_line);
else if (strcmp(loc->beg.mu_file, loc->end.mu_file))
mu_debug_log_begin ("%s:%u.%u-%s:%u.%u",
loc->beg.mu_file,
loc->beg.mu_line, loc->beg.mu_col,
loc->end.mu_file,
loc->end.mu_line, loc->end.mu_col);
else if (loc->beg.mu_line != loc->end.mu_line)
mu_debug_log_begin ("%s:%u.%u-%u.%u",
loc->beg.mu_file,
loc->beg.mu_line, loc->beg.mu_col,
loc->end.mu_line, loc->end.mu_col);
else if (loc->beg.mu_col != loc->end.mu_col)
mu_debug_log_begin ("%s:%u.%u-%u",
loc->beg.mu_file,
loc->beg.mu_line, loc->beg.mu_col,
loc->end.mu_col);
else
mu_debug_log_begin ("%s:%u.%u",
loc->beg.mu_file,
loc->beg.mu_line, loc->beg.mu_col);
mu_stream_write (mu_strerr, ": ", 2, NULL);
va_start (ap, fmt);
mu_stream_vprintf (mu_strerr, fmt, ap);
va_end (ap);
mu_debug_log_nl ();
}
}
static int
eval_rule (struct node *root)
{
......@@ -647,6 +834,10 @@ eval_rule (struct node *root)
switch (root->type)
{
case true_node:
result = 1;
break;
case functional_node:
result = root->v.function.fun (root->v.function.args);
break;
......@@ -681,28 +872,50 @@ eval_rule (struct node *root)
default:
abort ();
}
mime_debug (MU_DEBUG_TRACE2, &root->loc, "result %s", result ? "true" : "false");
return result;
}
static int
evaluate (void *item, void *data)
evaluate (void **itmv, size_t itmc, void *call_data)
{
struct rule_tab *p = item;
char **ptype = data;
struct rule_tab *p = itmv[0];
if (eval_rule (p->node))
{
*ptype = p->type;
return MU_ERR_USER0;
itmv[0] = p;
mime_debug (MU_DEBUG_TRACE1, &p->loc, "rule %s matches", p->type);
return MU_LIST_MAP_OK;
}
return 0;
return MU_LIST_MAP_SKIP;
}
static int
rule_cmp (const void *a, const void *b)
{
struct rule_tab const *arule = a;
struct rule_tab const *brule = b;
if (arule->priority == brule->priority)
return mu_c_strcasecmp (arule->type, brule->type);
return arule->priority - brule->priority;
}
const char *
get_file_type ()
{
mu_list_t res = NULL;
const char *type = NULL;
mu_list_foreach (rule_list, evaluate, &type);
mu_list_map (rule_list, evaluate, NULL, 1, &res);
if (!mu_list_is_empty (res))
{
struct rule_tab *rule;
mu_list_sort (res, rule_cmp);
mu_list_head (res, (void**) &rule);
mime_debug (MU_DEBUG_TRACE0, &rule->loc, "selected rule %s", rule->type);
type = rule->type;
}
mu_list_destroy (&res);
return type;
}
......
......@@ -31,8 +31,8 @@
#include "mailcap.h"
int debug_level; /* Debugging level set by --debug option */
static int dry_run; /* Dry run mode */
static int lint; /* Syntax check mode */
static char *metamail; /* Name of metamail program, if requested */
static char *mimetypes_config = DEFAULT_CUPS_CONFDIR;
static char *no_ask_types; /* List of MIME types for which no questions
......@@ -41,26 +41,6 @@ static int interactive = -1;
char *mimeview_file; /* Name of the file to view */
mu_stream_t mimeview_stream; /* The corresponding stream */
static void
set_debug_flags (const char *arg)
{
for (; *arg; arg++)
{
switch (*arg)
{
case 'l':
mimetypes_lex_debug (1);
break;
case 'g':
mimetypes_gram_debug (1);
break;
default:
debug_level = *arg - '0';
}
}
}
static void
cli_no_ask (struct mu_parseopt *po, struct mu_option *opt, char const *arg)
......@@ -80,7 +60,34 @@ static void
cli_debug (struct mu_parseopt *po, struct mu_option *opt,
char const *arg)
{
set_debug_flags (arg);
mu_debug_level_t lev;
if (!arg)
lev = MU_DEBUG_LEVEL_UPTO (MU_DEBUG_TRACE2);
else
{
mu_debug_get_category_level (MU_DEBCAT_MIME, &lev);
for (; *arg; arg++)
{
switch (*arg)
{
case 'l':
lev |= MU_DEBUG_LEVEL_MASK (MU_DEBUG_TRACE4);
break;
case 'g':
lev |= MU_DEBUG_LEVEL_MASK (MU_DEBUG_TRACE3);
break;
default:
if (mu_isdigit (*arg))
lev |= MU_DEBUG_LEVEL_UPTO (MU_DEBUG_TRACE0 + *arg - '0');
else
mu_parseopt_error (po, _("ignoring invalid debug flag: %c"),
*arg);
}
}
}
mu_debug_set_category_level (MU_DEBCAT_MIME, lev);
}
static void
......@@ -103,7 +110,7 @@ static struct mu_option mimeview_options[] = {
{ "debug", 'd', N_("FLAGS"), MU_OPTION_ARG_OPTIONAL,
N_("enable debugging output"),
mu_c_string, NULL, cli_debug },
{ "mimetypes", 't', N_("FILE"), MU_OPTION_DEFAULT,
{ "mimetypes", 'f', N_("FILE"), MU_OPTION_DEFAULT,
N_("use this mime.types file"),
mu_c_string, &mimetypes_config },
......@@ -111,6 +118,10 @@ static struct mu_option mimeview_options[] = {
N_("do nothing, just print what would have been done"),
mu_c_bool, &dry_run },
{ "lint", 't', NULL, MU_OPTION_DEFAULT,
N_("test mime.types syntax and exit"),
mu_c_bool, &lint },
{ "metamail", 0, N_("FILE"), MU_OPTION_ARG_OPTIONAL,
N_("use metamail to display files"),
mu_c_string, NULL, cli_metamail },
......@@ -118,19 +129,7 @@ static struct mu_option mimeview_options[] = {
MU_OPTION_END
}, *options[] = { mimeview_options, NULL };
static int
cb_debug (void *data, mu_config_value_t *val)
{
if (mu_cfg_assert_value_type (val, MU_CFG_STRING))
return 1;
set_debug_flags (val->v.string);
return 0;
}
struct mu_cfg_param mimeview_cfg_param[] = {
{ "debug", mu_cfg_callback, NULL, 0, cb_debug,
N_("Set debug verbosity level."),
N_("flags: string") },
{ "mimetypes", mu_c_string, &mimetypes_config, 0, NULL,
N_("Use this mime.types file."),
N_("file") },
......@@ -209,11 +208,12 @@ display_file (const char *type)
argv[5] = mimeview_file;
argv[6] = NULL;
if (debug_level)
if (mu_debug_level_p (MU_DEBCAT_MIME, MU_DEBUG_TRACE0))
{
char *string;
mu_argcv_string (6, argv, &string);
printf (_("Executing %s...\n"), string);
mu_debug (MU_DEBCAT_MIME, MU_DEBUG_TRACE0,
(_("executing %s...\n"), string));
free (string);
}
......@@ -233,7 +233,7 @@ display_file (const char *type)
{
display_stream_mailcap (mimeview_file, mimeview_stream, hdr,
no_ask_types, interactive, dry_run,
debug_level);
MU_DEBCAT_MIME);
mu_header_destroy (&hdr);
}
}
......@@ -244,15 +244,18 @@ main (int argc, char **argv)
{
MU_APP_INIT_NLS ();
mimetypes_lex_debug (0);
mimetypes_gram_debug (0);
interactive = isatty (fileno (stdin));
mu_cli (argc, argv, &cli, capa, NULL, &argc, &argv);
if (dry_run && !debug_level)
debug_level = 1;
if (dry_run)
{
mu_debug_level_t lev;
mu_debug_get_category_level (MU_DEBCAT_MIME, &lev);
lev |= MU_DEBUG_LEVEL_UPTO (MU_DEBUG_TRACE2);
mu_debug_set_category_level (MU_DEBCAT_MIME, lev);
}
if (argc == 0)
if (argc == 0 && !lint)
{
mu_error (_("no files given"));
return 1;
......@@ -260,6 +263,8 @@ main (int argc, char **argv)
if (mimetypes_parse (mimetypes_config))
return 1;
if (lint)
return 0;
while (argc--)
{
......@@ -268,7 +273,6 @@ main (int argc, char **argv)
if (open_file (*argv++))
continue;
type = get_file_type ();
DEBUG (1, ("%s: %s\n", mimeview_file, type ? type : "?"));
if (type)
display_file (type);
close_file ();
......
......@@ -36,21 +36,48 @@ int mimetypes_yyerror (char *s);
int mimetypes_open (const char *name);
void mimetypes_close (void);
int mimetypes_parse (const char *name);
void mimetypes_gram_debug (int level);
void mimetypes_lex_debug (int level);
void mimetypes_lex_init (void);
void reset_lex (void);
void lex_arglist (int);
void *mimetypes_malloc (size_t size);
struct mimetypes_string mimetypes_append_string2 (struct mimetypes_string *s1,
char c,
struct mimetypes_string *s2);
struct mimetypes_string *mimetypes_string_dup (struct mimetypes_string *s);
const char *get_file_type (void);
extern char *mimeview_file;
extern mu_stream_t mimeview_stream;
extern int debug_level;
#define DEBUG(l,f) if (debug_level > (l)) printf f
struct concat_segm
{
struct concat_segm *next;
char const *val;
};
void lex_concat (struct concat_segm *p, struct mimetypes_string *ret);
struct mu_locus_range
{
struct mu_locus beg;
struct mu_locus end;
};
#define YYLTYPE struct mu_locus_range
#define YYLLOC_DEFAULT(Current, Rhs, N) \
do \
{ \
if (N) \
{ \
(Current).beg = YYRHSLOC(Rhs, 1).beg; \
(Current).end = YYRHSLOC(Rhs, N).end; \
} \
else \
{ \
(Current).beg = YYRHSLOC(Rhs, 0).end; \
(Current).end = (Current).beg; \
} \
} while (0)
#define YY_LOCATION_PRINT(File, Loc) \
fprintf(File, "%s:%u.%u-%u.%u", \
(Loc).beg.mu_file, \
(Loc).beg.mu_line, (Loc).beg.mu_col, \
(Loc).end.mu_line, (Loc).end.mu_col)
......
/Makefile.in.in
/Makevars.template~
/Makefile.in.in~
*.gmo
......