Commit 58cc7c8f 58cc7c8f6bd1956a47f56c3aba1be8a417916064 by Sergey Poznyakoff

Revise the mime.types lexer; provide the testsuite

* am/testsuite.m4: New file.
* configure.ac: Use the MU_CONFIG_TESTSUITE macro.
* mimeview/Makefile.am (SUBDIRS): Add tests
* mimeview/mimetypes.l: Rewrite in three exclusive states.
* mimeview/mimetypes.y: Simplify grammar.
* mimeview/mimeview.c: New option --identify (-i).
* mimeview/mimeview.h: Update.

* mimeview/tests/Makefile.am: New file.
* mimeview/tests/atlocal.in: New file.
* mimeview/tests/bf.c: New file.
* mimeview/tests/testsuite.at: New file.

* README: Update.
* doc/texinfo/programs.texi
1 parent be0acbf5
GNU mailutils NEWS -- history of user-visible changes. 2017-05-31
GNU mailutils NEWS -- history of user-visible changes. 2017-06-01
Copyright (C) 2002-2017 Free Software Foundation, Inc.
See the end of file for copying conditions.
......@@ -145,6 +145,9 @@ 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.
New option '-i' ('--identify') identifies and prints the MIME type for
each input file, but not starts viewer.
Added support for priority and regex functions.
Debugging considerably improved.
......
# Initialize the (autotest) test suite.
AC_DEFUN([MU_CONFIG_TESTSUITE],
[AC_CONFIG_TESTDIR([$1/tests])
AC_CONFIG_FILES([$1/tests/Makefile $1/tests/atlocal])
])
......@@ -1324,40 +1324,22 @@ test -z "$server_list" && server_list=" [NONE]"
test -z "$client_list" && client_list=" [NONE]"
# Initialize the (autotest) test suite.
AC_CONFIG_TESTDIR(libmailutils/tests)
AC_CONFIG_TESTDIR(testsuite)
AC_CONFIG_TESTDIR(frm/tests)
AC_CONFIG_TESTDIR(maidag/tests)
AC_CONFIG_TESTDIR(mail/tests)
AC_CONFIG_TESTDIR(messages/tests)
AC_CONFIG_TESTDIR(readmsg/tests)
AC_CONFIG_TESTDIR(sieve/tests)
AC_CONFIG_TESTDIR(mh/tests)
AC_CONFIG_TESTDIR(comsat/tests)
AC_CONFIG_TESTDIR(imap4d/tests)
AC_CONFIG_FILES([libmailutils/tests/Makefile
libmailutils/tests/atlocal
testsuite/Makefile
testsuite/atlocal
comsat/tests/Makefile
comsat/tests/atlocal
frm/tests/Makefile
frm/tests/atlocal
imap4d/tests/Makefile
imap4d/tests/atlocal
maidag/tests/Makefile
maidag/tests/atlocal
mail/tests/Makefile
mail/tests/atlocal
messages/tests/Makefile
messages/tests/atlocal
readmsg/tests/Makefile
readmsg/tests/atlocal
sieve/tests/Makefile
sieve/tests/atlocal
mh/tests/Makefile
mh/tests/atlocal])
AC_CONFIG_FILES([testsuite/Makefile testsuite/atlocal])
MU_CONFIG_TESTSUITE(libmailutils)
MU_CONFIG_TESTSUITE(frm)
MU_CONFIG_TESTSUITE(maidag)
MU_CONFIG_TESTSUITE(mail)
MU_CONFIG_TESTSUITE(messages)
MU_CONFIG_TESTSUITE(readmsg)
MU_CONFIG_TESTSUITE(sieve)
MU_CONFIG_TESTSUITE(mh)
MU_CONFIG_TESTSUITE(comsat)
MU_CONFIG_TESTSUITE(imap4d)
MU_CONFIG_TESTSUITE(mimeview)
AM_MISSING_PROG([AUTOM4TE], [autom4te])
dnl Make sysconfdir available to the application
......
......@@ -7359,6 +7359,10 @@ By default @command{mimeview} behaves as if given
@option{--no-interactive} option whenever its standard input is not
a @asis{tty} device.
@item -i
@itemx --identify
Identifies and prints the MIME type for each input file.
@item -n
@itemx --dry-run
Do not do anything, just print what would be done. Implies
......
......@@ -15,6 +15,8 @@
## You should have received a copy of the GNU General Public License
## along with GNU Mailutils. If not, see <http://www.gnu.org/licenses/>.
SUBDIRS = tests
AM_CPPFLAGS = \
@MU_APP_COMMON_INCLUDES@\
-D_GNU_SOURCE=1\
......
......@@ -42,9 +42,16 @@ digit_to_number (char c)
c-'a'+10);
}
static struct mu_locus prev_loc;
static struct mu_locus string_beg;
static int prev_newline;
static void
advance_locus (void)
{
prev_loc = loc;
prev_newline = newline;
if (newline)
{
loc.mu_line++;
......@@ -54,99 +61,174 @@ advance_locus (void)
loc.mu_col += yyleng;
yylloc.end = loc;
yylloc.end.mu_col--;
#if 0
printf ("+%2d> %u:%u-%u:%u: %s\n",
yyleng,
yylloc.beg.mu_line, yylloc.beg.mu_col,
yylloc.end.mu_line, yylloc.end.mu_col, yytext);
#endif
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 ();
static void
retreat_locus (void)
{
loc = prev_loc;
newline = prev_newline;
}
static void
finish_string (void)
{
mu_opool_append_char (pool, 0);
yylval.string.ptr = mu_opool_finish (pool, &yylval.string.len);
yylval.string.len--;
yylloc.end = yylloc.beg;
yylloc.end.mu_col--;
yylloc.beg = string_beg;
if (mu_debug_level_p (MU_DEBCAT_APP, MU_DEBUG_TRACE5))
{
size_t i;
mu_debug_log_begin ("string %d: ", yylval.string.len);
for (i = 0; i < yylval.string.len; i++)
if (mu_isprint (yylval.string.ptr[i]))
mu_debug_log_cont ("%c", yylval.string.ptr[i]);
else
mu_debug_log_cont ("\\%03o", yylval.string.ptr[i]);
mu_debug_log_nl ();
}
#if 0
YY_LOCATION_PRINT (stderr, yylloc);
fprintf (stderr, ": %s\n", yylval.string.ptr);
#endif
}
#define YY_USER_ACTION advance_locus ();
%}
%option nounput
%option noinput
%s RULE ARGS
%x RULE ARGS ASTRING
X [0-9a-fA-F]
IDENT [a-zA-Z_\.][a-zA-Z0-9_\.-]*
WS [ \t]*
WS [ \t][ \t]*
%%
<INITIAL>{
/* Comments */
^#.*\n ;
/* Tokens */
\\\n ;
\n+ { loc.mu_line += yyleng - 1; return EOL; }
{WS} ;
<INITIAL,RULE>^[^ \t\n/]+"/"[^ \t\n]+ {
\n ;
^[^ \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);
yylval.string.len--;
BEGIN (RULE);
return TYPE;
}
}
<RULE>{
\\\n ;
\n {
BEGIN (INITIAL);
return EOL;
}
{WS} ;
/* Operators */
"!"|"+"|","|"("|")"|"/" return yytext[0];
/* Special cases: && and ||. Docs don't say anything about them, but
I've found them in my mime.types file... --Sergey */
"&&" return '+';
"||" return ',';
<RULE>"priority"/"(" {
"priority"/"(" {
return PRIORITY;
}
<RULE>{IDENT}/"(" {
{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;
}
<RULE>{IDENT} {
}
[a-zA-Z0-9_.-]+/[^(] {
mu_opool_append (pool, yytext, yyleng);
mu_opool_append_char (pool, 0);
yylval.string.ptr = mu_opool_finish (pool, &yylval.string.len);
return STRING;
}
<RULE,ARGS>\"[^"\n]*\" {
. mu_error("unexpected character '%c'", yytext[0]);
}
<ARGS>{
"("|"," return yytext[0];
")" {
BEGIN (RULE);
return yytext[0];
}
{WS} mu_error ("unexpected whitespace in argument list");
\n {
mu_error ("unexpected newline in argument list");
BEGIN (RULE);
return EOL;
}
. {
string_beg = yylloc.beg;
retreat_locus ();
yyless (0);
BEGIN (ASTRING);
}
}
<ASTRING>{
/* Quoted string */
\"[^"\n]*\" {
mu_opool_append (pool, yytext+1, yyleng-2);
}
"'"[^'\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;
}
<RULE,ARGS>"<"({X}{X})+">" {
/* Hex string */
"<"({X}{X})+">" {
int i;
for (i = 0; i < yyleng; i += 2)
for (i = 1; i < yyleng - 2; 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;
}
}
<ARGS>[^ \t<\n),<"]+/[),<"] {
/* Unquoted character sequence */
[^ \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;
}
<RULE>[^ \t<\\\n)+,&]/[ \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;
}
<ARGS>[^ \t<\\\n),]/[ \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);
}
. {
retreat_locus ();
yyless (0);
BEGIN (ARGS);
finish_string ();
return STRING;
}
/* Special cases: && and ||. Docs don't say anything about them, but
I've found them in my mime.types file... --Sergey */
"&&" return '+';
"||" return ',';
/* Operators */
<RULE>"!"|"+"|","|"("|")"|"/" return yytext[0];
<ARGS>"," return yytext[0];
<ARGS>")" { BEGIN (RULE); return yytext[0]; }
<*>. {
mu_error ("invalid character '%c', state %d", yytext[0], YYSTATE);
return BOGUS;
}
%%
int
mimetypes_open (const char *name)
......@@ -236,21 +318,7 @@ mimetypes_malloc (size_t size)
}
void
lex_arglist (int enable)
{
if (enable)
BEGIN (ARGS);
else
BEGIN (RULE);
}
void
lex_concat (struct concat_segm *p, struct mimetypes_string *ret)
lex_reset (void)
{
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);
BEGIN (INITIAL);
}
......
......@@ -132,12 +132,10 @@ static mu_list_t rule_list;
%left ','
%left '+'
%type <string> string arg
%type <string> arg
%type <list> arglist
%type <node> function stmt rule maybe_rule
%type <result> priority maybe_priority
%type <concat> concat;
%type <segment> simple_string
%union {
struct mimetypes_string string;
......@@ -145,8 +143,6 @@ static mu_list_t rule_list;
mu_list_t list;
int result;
struct node *node;
struct { struct concat_segm *head, *tail; } concat;
struct concat_segm *segment;
}
%%
......@@ -176,7 +172,7 @@ rule_line: /* empty */
if (arg_list)
mu_list_destroy (&arg_list);
arg_list = NULL;
lex_arglist (0);
lex_reset ();
}
;
......@@ -219,39 +215,14 @@ stmt : '!' stmt
{
$$ = $2;
}
| string
| STRING
{
$$ = make_suffix_node (&$1, &@1);
}
| function
;
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
priority : PRIORITY '(' arglist ')'
{
size_t count = 0;
struct mimetypes_string *arg;
......@@ -275,19 +246,7 @@ maybe_priority: /* empty */
| priority
;
oparen : '('
{
lex_arglist (1);
}
;
cparen : ')'
{
lex_arglist (0);
}
;
function : IDENT oparen arglist cparen
function : IDENT '(' arglist ')'
{
struct mu_locus_range lr;
lr.beg = @1.beg;
......@@ -312,7 +271,7 @@ arglist : arg
}
;
arg : string
arg : STRING
;
%%
......@@ -387,6 +346,9 @@ b_match (union argument *args)
True if bytes are valid printable ASCII (CR, NL, TAB,
BS, 32-126)
*/
#define ISASCII(c) ((c) &&\
(strchr ("\n\r\t\b",c) \
|| (32<=((unsigned) c) && ((unsigned) c)<=126)))
static int
b_ascii (union argument *args)
{
......@@ -402,13 +364,13 @@ b_ascii (union argument *args)
for (i = 0; i < args[1].number; i++)
{
char c;
unsigned char c;
size_t n;
rc = mu_stream_read (mimeview_stream, &c, 1, &n);
if (rc || n == 0)
break;
if (!mu_isascii (c))
if (!ISASCII (c))
return 0;
}
......@@ -419,10 +381,8 @@ b_ascii (union argument *args)
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)))
#define ISPRINT(c) (ISASCII (c) \
|| (128<=((unsigned) c) && ((unsigned) c)<=254))
static int
b_printable (union argument *args)
{
......@@ -438,13 +398,13 @@ b_printable (union argument *args)
for (i = 0; i < args[1].number; i++)
{
char c;
unsigned char c;
size_t n;
rc = mu_stream_read (mimeview_stream, &c, 1, &n);
if (rc || n == 0)
break;
if (!ISPRINT ((unsigned)c))
if (!ISPRINT (c))
return 0;
}
return 1;
......@@ -552,8 +512,8 @@ b_char (union argument *args)
static int
b_short (union argument *args)
{
unsigned short val = args[1].number;
unsigned short buf;
uint16_t val = args[1].number;
uint16_t buf;
return compare_bytes (args, &val, &buf, sizeof (buf));
}
......@@ -564,8 +524,8 @@ b_short (union argument *args)
static int
b_int (union argument *args)
{
unsigned int val = args[1].number;
unsigned int buf;
uint32_t val = args[1].number;
uint32_t buf;
return compare_bytes (args, &val, &buf, sizeof (buf));
}
......@@ -604,7 +564,7 @@ b_contains (union argument *args)
mu_diag_funcall (MU_DIAG_ERROR, "mu_stream_read", NULL, rc);
}
else if (count > str->len)
for (i = 0; i < count - str->len; i++)
for (i = 0; i <= count - str->len; i++)
if (buf[i] == str->ptr[0] && memcmp (buf + i, str->ptr, str->len) == 0)
{
free (buf);
......@@ -896,7 +856,16 @@ rule_cmp (const void *a, const void *b)
struct rule_tab const *brule = b;
if (arule->priority == brule->priority)
return mu_c_strcasecmp (arule->type, brule->type);
{
if (arule->node->type == true_node
&& brule->node->type != true_node)
return 1;
else if (brule->node->type == true_node
&& arule->node->type != true_node)
return -1;
else
return mu_c_strcasecmp (arule->type, brule->type);
}
return arule->priority - brule->priority;
}
......
......@@ -33,12 +33,13 @@
static int dry_run; /* Dry run mode */
static int lint; /* Syntax check mode */
static int identify; /* Print only the file's type */
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
should be asked */
static int interactive = -1;
char *mimeview_file; /* Name of the file to view */
char const *mimeview_file; /* Name of the file to view */
mu_stream_t mimeview_stream; /* The corresponding stream */
......@@ -121,6 +122,10 @@ static struct mu_option mimeview_options[] = {
{ "lint", 't', NULL, MU_OPTION_DEFAULT,
N_("test mime.types syntax and exit"),
mu_c_bool, &lint },
{ "identify", 'i', NULL, MU_OPTION_DEFAULT,
N_("identify MIME type of each file"),
mu_c_bool, &identify },
{ "metamail", 0, N_("FILE"), MU_OPTION_ARG_OPTIONAL,
N_("use metamail to display files"),
......@@ -157,7 +162,7 @@ static char *capa[] = {
};
static int
open_file (char *name)
open_file (char const *name)
{
int rc;
struct stat st;
......@@ -190,9 +195,18 @@ close_file ()
}
void
display_file (const char *type)
display_file (const char *file, const char *type)
{
int status;
if (identify)
{
printf ("%s: %s\n", file, type ? type : "unknown");
return;
}
if (!type)
return;
if (metamail)
{
......@@ -205,7 +219,7 @@ display_file (const char *type)
argv[3] = "-c";
argv[4] = (char*) type;
argv[5] = mimeview_file;
argv[5] = (char*) mimeview_file;
argv[6] = NULL;
if (mu_debug_level_p (MU_DEBCAT_MIME, MU_DEBUG_TRACE0))
......@@ -269,12 +283,11 @@ main (int argc, char **argv)
while (argc--)
{
const char *type;
if (open_file (*argv++))
char const *file = *argv++;
if (open_file (file))
continue;
type = get_file_type ();
if (type)
display_file (type);
display_file (file, type);
close_file ();
}
......
......@@ -37,24 +37,17 @@ int mimetypes_open (const char *name);
void mimetypes_close (void);
int mimetypes_parse (const char *name);
void mimetypes_lex_init (void);
void lex_arglist (int);
void lex_reset (void);
void *mimetypes_malloc (size_t size);
struct mimetypes_string *mimetypes_string_dup (struct mimetypes_string *s);
const char *get_file_type (void);
extern char *mimeview_file;
extern char const *mimeview_file;
extern mu_stream_t mimeview_stream;
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;
......
atconfig
atlocal
bf
package.m4
testsuite
testsuite.dir
testsuite.log
# This file is part of GNU Mailutils.
# Copyright (C) 2017 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 3, 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, see <http://www.gnu.org/licenses/>.
EXTRA_DIST = $(TESTSUITE_AT) testsuite package.m4
DISTCLEANFILES = atconfig $(check_SCRIPTS)
MAINTAINERCLEANFILES = Makefile.in $(TESTSUITE)
## ------------ ##
## package.m4. ##
## ------------ ##
$(srcdir)/package.m4: $(top_srcdir)/configure.ac
$(AM_V_GEN){ \
echo '# Signature of the current package.'; \
echo 'm4_define([AT_PACKAGE_NAME], [@PACKAGE_NAME@])'; \
echo 'm4_define([AT_PACKAGE_TARNAME], [@PACKAGE_TARNAME@])'; \
echo 'm4_define([AT_PACKAGE_VERSION], [@PACKAGE_VERSION@])'; \
echo 'm4_define([AT_PACKAGE_STRING], [@PACKAGE_STRING@])'; \
echo 'm4_define([AT_PACKAGE_BUGREPORT], [@PACKAGE_BUGREPORT@])'; \
} >$(srcdir)/package.m4
## -------------------------- ##
## Non-installable programs ##
## -------------------------- ##
noinst_PROGRAMS = bf
## ------------ ##
## Test suite. ##
## ------------ ##
TESTSUITE_AT = testsuite.at
TESTSUITE = $(srcdir)/testsuite
M4=m4
AUTOTEST = $(AUTOM4TE) --language=autotest
$(TESTSUITE): package.m4 $(TESTSUITE_AT) $(top_srcdir)/testsuite/testsuite.inc
$(AM_V_GEN)$(AUTOTEST) -I $(srcdir) -I $(top_srcdir)/testsuite testsuite.at -o $@.tmp
$(AM_V_at)mv $@.tmp $@
atconfig: $(top_builddir)/config.status
cd $(top_builddir) && ./config.status tests/$@
clean-local:
@test ! -f $(TESTSUITE) || $(SHELL) $(TESTSUITE) --clean
check-local: atconfig atlocal $(TESTSUITE)
@$(SHELL) $(TESTSUITE)
# Run the test suite on the *installed* tree.
#installcheck-local:
# $(SHELL) $(TESTSUITE) AUTOTEST_PATH=$(exec_prefix)/bin
# @configure_input@ -*- shell-script -*-
# Configurable variable values for Mailutils test suite.
PATH=@abs_builddir@:@abs_top_builddir@/mimeview:$top_srcdir:$srcdir:$PATH
/* This file is part of the GNU Mailutils testsuite.
Copyright (C) 2017 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 3, 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, see <http://www.gnu.org/licenses/>. */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include <errno.h>
#include <assert.h>
#include <inttypes.h>
typedef int (*ACTION) (FILE *, char *);
int
seek_file (FILE *fp, char *arg)
{
long off;
int whence;
switch (arg[0])
{
case '+':
whence = SEEK_CUR;
arg++;
break;
case '-':
whence = SEEK_CUR;
break;
case '$':
whence = SEEK_END;
arg++;
break;
default:
whence = SEEK_SET;
}
if (sscanf (arg, "%ld", &off) != 1)
{
fprintf (stderr, "bad offset: %s\n", arg);
abort ();
}
return fseek (fp, off, whence);
}
int
write_string (FILE *fp, char *arg)
{
size_t n = strlen (arg);
return fwrite (arg, n, 1, fp) != 1;
}
int
write_byte (FILE *fp, char *arg)
{
int c;
if (strlen (arg) == 3
&& (arg[0] == '\'' || arg[0] == '"'))
c = arg[1];
else
{
char *p;
unsigned long n = strtoul (arg, &p, 0);
if (*p || n > UCHAR_MAX)
{
errno = EINVAL;
return -1;
}
c = n;
}
return fwrite (&c, 1, 1, fp) != 1;
}
int
write_short (FILE *fp, char *arg)
{
uint16_t val;
char *p;
unsigned long n = strtoul (arg, &p, 0);
if (*p || n > UINT16_MAX)
{
errno = EINVAL;
return -1;
}
val = n;
return fwrite (&val, sizeof(val), 1, fp) != 1;
}
int
write_int (FILE *fp, char *arg)
{
uint32_t val;
char *p;
unsigned long n = strtoul (arg, &p, 0);
if (*p || n > UINT32_MAX)
{
errno = EINVAL;
return -1;
}
val = n;
return fwrite (&val, sizeof(val), 1, fp) != 1;
}
static struct dispatch {
char *opt;
int (*act) (FILE *, char *);
} dispatch[] = {
{ "-seek", seek_file },
{ "-string", write_string },
{ "-byte", write_byte },
{ "-short", write_short },
{ "-int", write_int },
{ NULL }
};
static ACTION
find_action (char const *opt)
{
struct dispatch *p;
for (p = dispatch; p->opt; p++)
if (strcmp (p->opt, opt) == 0)
return p->act;
return NULL;
}
/*
-seek OFF
-string STRING
-byte BYTE
-short SHORT
-int INT
-repeat N
*/
int
main (int argc, char **argv)
{
char *name;
FILE *fp;
ACTION action;
char **pp;
assert (argc > 1);
name = argv[1];
fp = fopen (name, "w");
if (!fp)
{
perror (name);
abort ();
}
pp = argv + 2;
while (*pp)
{
char *opt = *pp++, *arg;
if (opt[0] != '-')
{
fprintf (stderr, "not an option: %s\n", opt);
abort ();
}
action = find_action (opt);
if (!action)
{
fprintf (stderr, "unknown action: %s\n", opt);
abort ();
}
if (!*pp)
{
fprintf (stderr, "argument to %s missing\n", opt);
abort ();
}
arg = *pp++;
if (action (fp, arg))
{
fprintf (stderr, "%s %s: %s\n", opt, arg, strerror (errno));
abort ();
}
}
fclose (fp);
return 0;
}
# This file is part of GNU Mailutils. -*- Autotest -*-
# Copyright (C) 2017 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 3, 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, see <http://www.gnu.org/licenses/>.
m4_include([testsuite.inc])
dnl ------------------------------------------------------------
dnl MIMEVIEW_OPTIONS -- default options for mimeview
m4_pushdef([MIMEVIEW_OPTIONS],[--no-site --no-user])
m4_pushdef([__prepare_input],[
m4_if([$1],[],[],[bf $1 $2
__prepare_input(m4_shift(m4_shift(m4_shift($@))))])])
m4_pushdef([prepare_input],[__prepare_input(m4_shift(m4_shift($@)))])
m4_pushdef([__select_args],[dnl
m4_if([$2],[],[$1],[dnl
__select_args([$1 $2], m4_shift(m4_shift(m4_shift(m4_shift($@)))))])])
m4_pushdef([select_args],[__select_args([],m4_shift(m4_shift($@)))])
m4_pushdef([__build_expect],[dnl
m4_if([$2],[],[$1],[__build_expect([dnl
$1[]dnl
$2: $4
],m4_shift(m4_shift(m4_shift(m4_shift($@)))))])])
m4_pushdef([build_expect],[__build_expect([],m4_shift(m4_shift($@)))])
# MIMETEST(NAME,TYPES,FILE,CONTENT,RES)
m4_pushdef([MIMETEST],[
AT_SETUP([$1])
AT_KEYWORDS([mimeview])
AT_CHECK([
AT_DATA([mime.types],[$2
])
prepare_input($@)
mimeview MIMEVIEW_OPTIONS --identify -f mime.types select_args($@)
],
[0],
[build_expect($@)])
AT_CLEANUP
])
dnl ------------------------------------------------------------
AT_INIT
AT_TESTED([mimeview])
MUT_VERSION([mimeview])
MIMETEST([default],
[application/octet-stream],
[input], [], [application/octet-stream])
MIMETEST([suffixes],
[foo/x-bar bar baz
foo/x-qux qux quux
],
[a.bar], [], [foo/x-bar],
[a.baz], [], [foo/x-bar],
[a.quux], [], [foo/x-qux],
[a.qx], [], [unknown])
MIMETEST([default ordering],
[text/foo bar
text/bar bar
],
[a.bar], [], [text/bar])
MIMETEST([priority],
[text/bar bar
text/foo bar priority(20)
],
[a.bar], [], [text/foo])
AT_BANNER([Functions])
# match("pattern") Pattern match on filename
MIMETEST([match],
[application/x-csource match(*.c)
],
[a.c],[],[application/x-csource],
[a.c1],[],[unknown])
# ascii(offset,length) True if bytes are valid printable ASCII
# (CR, NL, TAB, BS, 32-126)
MIMETEST([ascii],
[application/x-bar ascii(16,6)
],
[one],[-seek 16 -string foobar -int 100],[application/x-bar],
[two],[-seek 16 -string fooba -byte 129],[unknown])
# printable(offset,length) True if bytes are printable 8-bit chars
# (CR, NL, TAB, BS, 32-126, 128-254)
MIMETEST([printable],
[application/x-bar printable(16,6)
],
[one],[-seek 16 -string foobar -int 100],[application/x-bar],
[two],[-seek 16 -string fooba -byte 129],[application/x-bar],
[three],[-seek 16 -string fooba -byte 127],[unknown])
# regex(offset,"regex") True if bytes match regular expression
MIMETEST([regex],
[application/pdf regex(0,^[[\n\r]]*%PDF)
],
[one],[-byte 10 -byte 10 -byte 13 -byte 10 -string %PDF],[application/pdf],
[two],[-byte 10 -byte 10 -byte 13 -byte 7 -string %PDF],[unknown])
# string(offset,"string") True if bytes are identical to string
MIMETEST([string],
[application/x-foo string(5,FOO)
],
[one],[-seek 5 -string FOO],[application/x-foo],
[two],[-seek 4 -string FOO],[unknown])
# istring(offset,"string") True if bytes are identical to
# case-insensitive string
MIMETEST([istring],
[application/x-foo istring(5,FOO)
],
[one],[-seek 5 -string foO],[application/x-foo],
[two],[-seek 4 -string FOO],[unknown])
# char(offset,value) True if byte is identical
MIMETEST([char],
[application/x-foo char(5,15)
],
[one],[-seek 5 -byte 15],[application/x-foo],
[two],[-seek 5 -byte 1],[unknown])
# short(offset,value) True if 16-bit integer is identical
MIMETEST([short],
[application/x-foo short(5,1600)
],
[one],[-seek 5 -short 1600],[application/x-foo],
[two],[-seek 5 -short 1601],[unknown])
# int(offset,value) True if 32-bit integer is identical
MIMETEST([int],
[application/x-foo int(5,16578)
],
[one],[-seek 5 -int 16578],[application/x-foo],
[two],[-seek 5 -int 16579],[unknown])
# locale("string") True if current locale matches string
# FIXME
# contains(offset,range,"string") True if the range contains the string
MIMETEST([contains],
[application/x-foo contains(10,1024,"TESTSTRING")
],
[one],[-seek 512 -string TESTSTRING],[application/x-foo],
[two],[-seek 512 -string TEST],[unknown])
MIMETEST([argument strings],
[application/x-foo string(0,"FOO")
application/x-bar string(0,'B A R')
application/x-baz string(0,"B A"Z<1B0103>BAZ)
application/x-qux string(0,<1B>45" Q "<01>)
],
[one],[-string FOO],[application/x-foo],
[two],[-string 'B A R'],[application/x-bar],
[three],[-string "B AZ" -byte 0x1b -byte 0x01 -byte 0x03 -string BAZ],[application/x-baz],
[four],[-byte 0x1b -string '45 Q ' -byte 0x01],[application/x-qux])
MIMETEST([logical or],
[text/x-bar bar baz string(0,bar) printable(3,10)
],
[one.bar],[],[text/x-bar],
[one.baz],[],[text/x-bar],
[foo],[-string bar],[text/x-bar],
[bar],[-seek 3 -string teststring],[text/x-bar],
[baz],[-seek 3 -string test -byte 0 -string tring],[unknown])
MIMETEST([logical and],
[text/x-foo bar + string(0,bar<10>) + printable(4,10)
],
[one.bar],[-string bar -byte 0x10 -string TESTSTRING],[text/x-foo],
[one],[-string bar -byte 0x10 -string TESTSTRING],[unknown],
[two.bar],[-string bar -byte 0x13 -byte 0x10 -string TEST],[unknown])
MIMETEST([grouping],
[text/x-foo bar (string(0,bar) + printable(4,10))
],
[one.bar],[-string foo],[text/x-foo],
[two.baz],[-string bar -byte 0x10 -string TESTSTRING],[text/x-foo],
[three],[-string bar -byte 0x13 -byte 0x10 -string TESTSTRING],[unknown])