summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--configure.ac1
-rw-r--r--include/mailutils/Makefile.am1
-rw-r--r--include/mailutils/mailutils.h1
-rw-r--r--include/mailutils/mimetypes.h (renamed from mimeview/mimeview.h)24
-rw-r--r--include/mailutils/stream.h2
-rw-r--r--include/mailutils/sys/Makefile.am1
-rw-r--r--include/mailutils/sys/mimetypes.h102
-rw-r--r--libmailutils/Makefile.am2
-rw-r--r--libmailutils/diag/debcat1
-rw-r--r--libmailutils/locus/linetrack.c2
-rw-r--r--libmailutils/mimetypes/.gitignore9
-rw-r--r--libmailutils/mimetypes/eval.c551
-rw-r--r--libmailutils/mimetypes/grammar.y627
-rw-r--r--libmailutils/mimetypes/lexer.l (renamed from mimeview/lexer.l)33
-rw-r--r--libmailutils/stream/file_stream.c2
-rw-r--r--mimeview/Makefile.am25
-rw-r--r--mimeview/grammar.y1111
-rw-r--r--mimeview/mimeview.c34
-rw-r--r--mimeview/tests/testsuite.at2
-rw-r--r--po/POTFILES.in6
20 files changed, 1357 insertions, 1180 deletions
diff --git a/configure.ac b/configure.ac
index 452b611cc..4fa4ab65a 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1568,6 +1568,7 @@ AC_CONFIG_FILES([
libmailutils/mailcap/Makefile
libmailutils/mailer/Makefile
libmailutils/mime/Makefile
+ libmailutils/mimetypes/Makefile
libmailutils/msgset/Makefile
libmailutils/opt/Makefile
libmailutils/property/Makefile
diff --git a/include/mailutils/Makefile.am b/include/mailutils/Makefile.am
index cbbbfbf35..7d4f00400 100644
--- a/include/mailutils/Makefile.am
+++ b/include/mailutils/Makefile.am
@@ -77,6 +77,7 @@ pkginclude_HEADERS = \
message.h\
mh.h\
mime.h\
+ mimetypes.h\
monitor.h\
msgset.h\
mu_auth.h\
diff --git a/include/mailutils/mailutils.h b/include/mailutils/mailutils.h
index 32cb36e84..d53e1d0de 100644
--- a/include/mailutils/mailutils.h
+++ b/include/mailutils/mailutils.h
@@ -72,5 +72,6 @@
#include <mailutils/sockaddr.h>
#include <mailutils/cidr.h>
#include <mailutils/msgset.h>
+#include <mailutils/mimetypes.h>
/* EOF */
diff --git a/mimeview/mimeview.h b/include/mailutils/mimetypes.h
index 867de1b20..1e75e81d0 100644
--- a/mimeview/mimeview.h
+++ b/include/mailutils/mimetypes.h
@@ -1,34 +1,32 @@
/* GNU Mailutils -- a suite of utilities for electronic mail
Copyright (C) 2005-2021 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
+ This library is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser 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,
+ This library 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.
+ GNU Lesser General Public License for more details.
- You should have received a copy of the GNU General Public License
+ You should have received a copy of the GNU Lesser General Public License
along with GNU Mailutils. If not, see <http://www.gnu.org/licenses/>. */
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#include <mailutils/mailutils.h>
-#include <mailutils/locus.h>
-#include <mailutils/yyloc.h>
-#include <fnmatch.h>
+#ifndef _MAILUTILS_MIMETYPES_H
+#define _MAILUTILS_MIMETYPES_H
+
+#include <mailutils/types.h>
typedef struct mu_mimetypes *mu_mimetypes_t;
-mu_mimetypes_t mimetypes_open (const char *name);
+mu_mimetypes_t mu_mimetypes_open (const char *name);
void mu_mimetypes_close (mu_mimetypes_t mt);
const char *mu_mimetypes_stream_type (mu_mimetypes_t mt,
char const *name, mu_stream_t str);
const char *mu_mimetypes_file_type (mu_mimetypes_t mt, const char *file);
const char *mu_mimetypes_fd_type (mu_mimetypes_t mt, const char *file, int fd);
+#endif
diff --git a/include/mailutils/stream.h b/include/mailutils/stream.h
index f5c8a673e..608a355dd 100644
--- a/include/mailutils/stream.h
+++ b/include/mailutils/stream.h
@@ -431,7 +431,7 @@ int mu_file_stream_create (mu_stream_t *pstream, const char *filename, int flags
struct mu_tempfile_hints;
int mu_temp_file_stream_create (mu_stream_t *pstream,
struct mu_tempfile_hints *hints, int flags);
-int mu_fd_stream_create (mu_stream_t *pstream, char *filename, int fd,
+int mu_fd_stream_create (mu_stream_t *pstream, char const *filename, int fd,
int flags);
#define MU_STDIN_FD 0
diff --git a/include/mailutils/sys/Makefile.am b/include/mailutils/sys/Makefile.am
index f5db48ffc..b5f68e15a 100644
--- a/include/mailutils/sys/Makefile.am
+++ b/include/mailutils/sys/Makefile.am
@@ -45,6 +45,7 @@ sysinclude_HEADERS = \
message_stream.h\
message.h\
mime.h\
+ mimetypes.h\
monitor.h\
msgset.h\
nullstream.h\
diff --git a/include/mailutils/sys/mimetypes.h b/include/mailutils/sys/mimetypes.h
new file mode 100644
index 000000000..254d8167b
--- /dev/null
+++ b/include/mailutils/sys/mimetypes.h
@@ -0,0 +1,102 @@
+/* GNU Mailutils -- a suite of utilities for electronic mail
+ Copyright (C) 2009-2021 Free Software Foundation, Inc.
+
+ This library is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ This library 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 Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with GNU Mailutils. If not, see <http://www.gnu.org/licenses/>. */
+
+#ifndef _MAILUTILS_SYS_MIMETYPES_H
+#define _MAILUTILS_SYS_MIMETYPES_H
+
+#include <regex.h>
+#include <mailutils/locus.h>
+
+#define L_OR 0
+#define L_AND 1
+
+enum node_type
+ {
+ true_node,
+ functional_node,
+ binary_node,
+ negation_node,
+ suffix_node
+ };
+
+struct mimetypes_string
+{
+ char *ptr;
+ size_t len;
+};
+
+union argument
+{
+ struct mimetypes_string *string;
+ unsigned number;
+ int c;
+ regex_t rx;
+};
+
+struct input_file
+{
+ char const *name;
+ mu_stream_t stream;
+};
+
+typedef int (*builtin_t) (union argument *args, struct input_file *input);
+
+struct builtin_tab
+{
+ char *name;
+ char *args;
+ builtin_t handler;
+};
+
+struct node
+{
+ enum node_type type;
+ struct mu_locus_range loc;
+ union
+ {
+ struct
+ {
+ struct builtin_tab const *builtin;
+ union argument *args;
+ } function;
+ struct node *arg;
+ struct
+ {
+ int op;
+ struct node *arg1;
+ struct node *arg2;
+ } bin;
+ struct mimetypes_string suffix;
+ } v;
+};
+
+struct rule_tab
+{
+ char *type;
+ int priority;
+ struct mu_locus_range loc;
+ struct node *node;
+};
+
+struct mu_mimetypes
+{
+ mu_list_t rule_list;
+ mu_opool_t pool;
+};
+
+struct builtin_tab const *mu_mimetypes_builtin (char const *ident);
+
+#endif
diff --git a/libmailutils/Makefile.am b/libmailutils/Makefile.am
index 6e7fb09f6..501f2bba9 100644
--- a/libmailutils/Makefile.am
+++ b/libmailutils/Makefile.am
@@ -31,6 +31,7 @@ SUBDIRS = \
mailcap\
mailer\
mime\
+ mimetypes\
msgset\
opt\
server\
@@ -68,6 +69,7 @@ libmailutils_la_LIBADD = \
mailcap/libmailcap.la\
mailer/libmailer.la\
mime/libmime.la\
+ mimetypes/libmimetypes.la\
msgset/libmsgset.la\
opt/libopt.la\
property/libproperty.la\
diff --git a/libmailutils/diag/debcat b/libmailutils/diag/debcat
index 600668085..6666222bc 100644
--- a/libmailutils/diag/debcat
+++ b/libmailutils/diag/debcat
@@ -31,6 +31,7 @@ mailbox
mailer
message
mime
+mimetypes
mailcap
property
remote
diff --git a/libmailutils/locus/linetrack.c b/libmailutils/locus/linetrack.c
index 3cd5be6e0..88141574f 100644
--- a/libmailutils/locus/linetrack.c
+++ b/libmailutils/locus/linetrack.c
@@ -238,7 +238,7 @@ mu_linetrack_create (mu_linetrack_t *ret,
free (trk);
return rc;
}
-
+
*ret = trk;
return 0;
}
diff --git a/libmailutils/mimetypes/.gitignore b/libmailutils/mimetypes/.gitignore
new file mode 100644
index 000000000..4a207381a
--- /dev/null
+++ b/libmailutils/mimetypes/.gitignore
@@ -0,0 +1,9 @@
+.deps
+.gdbinit
+.libs
+Makefile
+Makefile.in
+grammar.c
+grammar.h
+grammar.output
+lexer.c
diff --git a/libmailutils/mimetypes/eval.c b/libmailutils/mimetypes/eval.c
new file mode 100644
index 000000000..4babff4ef
--- /dev/null
+++ b/libmailutils/mimetypes/eval.c
@@ -0,0 +1,551 @@
+/* GNU Mailutils -- a suite of utilities for electronic mail
+ Copyright (C) 2009-2021 Free Software Foundation, Inc.
+
+ This library is free software; you can redistribute it and/or modify
+ it under the terms of the GNU Lesser General Public License as published by
+ the Free Software Foundation; either version 3, or (at your option)
+ any later version.
+
+ This library 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 Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General Public License
+ along with GNU Mailutils. If not, see <http://www.gnu.org/licenses/>. */
+
+#include <config.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <fnmatch.h>
+#include <regex.h>
+#include <inttypes.h>
+#include <mailutils/stream.h>
+#include <mailutils/locus.h>
+#include <mailutils/diag.h>
+#include <mailutils/cctype.h>
+#include <mailutils/cstr.h>
+#include <mailutils/stdstream.h>
+#include <mailutils/list.h>
+#include <mailutils/nls.h>
+#include <mailutils/errno.h>
+#include <mailutils/sys/mimetypes.h>
+#include <mailutils/mimetypes.h>
+
+/*
+ * match("pattern")
+ * Pattern match on filename
+ */
+static int
+b_match (union argument *args, struct input_file *input)
+{
+ return fnmatch (args[0].string->ptr, input->name, 0) == 0;
+}
+
+/*
+ * ascii(offset,length)
+ * 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, struct input_file *input)
+{
+ int i;
+ int rc;
+
+ rc = mu_stream_seek (input->stream, args[0].number, MU_SEEK_SET, NULL);
+ if (rc)
+ {
+ mu_debug (MU_DEBCAT_MIMETYPES, MU_DEBUG_ERROR,
+ ("mu_stream_seek: %s", mu_strerror (rc)));
+ return 0;
+ }
+
+ for (i = 0; i < args[1].number; i++)
+ {
+ unsigned char c;
+ size_t n;
+
+ rc = mu_stream_read (input->stream, &c, 1, &n);
+ if (rc || n == 0)
+ 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) (ISASCII (c) \
+ || (128<=((unsigned) c) && ((unsigned) c)<=254))
+static int
+b_printable (union argument *args, struct input_file *input)
+{
+ int i;
+ int rc;
+
+ rc = mu_stream_seek (input->stream, args[0].number, MU_SEEK_SET, NULL);
+ if (rc)
+ {
+ mu_debug (MU_DEBCAT_MIMETYPES, MU_DEBUG_ERROR,
+ ("mu_stream_seek: %s", mu_strerror (rc)));
+ return 0;
+ }
+
+ for (i = 0; i < args[1].number; i++)
+ {
+ unsigned char c;
+ size_t n;
+
+ rc = mu_stream_read (input->stream, &c, 1, &n);
+ if (rc || n == 0)
+ break;
+ if (!ISPRINT (c))
+ return 0;
+ }
+ return 1;
+}
+
+/*
+ * string(offset,"string")
+ * True if bytes are identical to string
+ */
+static int
+b_string (union argument *args, struct input_file *input)
+{
+ struct mimetypes_string *str = args[1].string;
+ int i;
+ int rc;
+
+ rc = mu_stream_seek (input->stream, args[0].number, MU_SEEK_SET, NULL);
+ if (rc)
+ {
+ mu_debug (MU_DEBCAT_MIMETYPES, MU_DEBUG_ERROR,
+ ("mu_stream_seek: %s", mu_strerror (rc)));
+ return 0;
+ }
+
+ for (i = 0; i < str->len; i++)
+ {
+ char c;
+ size_t n;
+
+ rc = mu_stream_read (input->stream, &c, 1, &n);
+ if (rc || n == 0 || 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, struct input_file *input)
+{
+ int i;
+ struct mimetypes_string *str = args[1].string;
+
+ int rc;
+
+ rc = mu_stream_seek (input->stream, args[0].number, MU_SEEK_SET, NULL);
+ if (rc)
+ {
+ mu_debug (MU_DEBCAT_MIMETYPES, MU_DEBUG_ERROR,
+ ("mu_stream_seek: %s", mu_strerror (rc)));
+ return 0;
+ }
+
+ for (i = 0; i < str->len; i++)
+ {
+ char c;
+ size_t n;
+
+ rc = mu_stream_read (input->stream, &c, 1, &n);
+ if (rc || n == 0 || mu_tolower (c) != mu_tolower (str->ptr[i]))
+ return 0;
+ }
+ return 1;
+}
+
+int
+compare_bytes (union argument *args, struct input_file *input,
+ void *sample, void *buf, size_t size)
+{
+ int rc;
+ size_t n;
+
+ rc = mu_stream_seek (input->stream, args[0].number, MU_SEEK_SET, NULL);
+ if (rc)
+ {
+ mu_debug (MU_DEBCAT_MIMETYPES, MU_DEBUG_ERROR,
+ ("mu_stream_seek: %s", mu_strerror (rc)));
+ return 0;
+ }
+
+ rc = mu_stream_read (input->stream, buf, size, &n);
+ if (rc)
+ {
+ mu_debug (MU_DEBCAT_MIMETYPES, MU_DEBUG_ERROR,
+ ("mu_stream_read: %s", mu_strerror (rc)));
+ return 0;
+ }
+ else if (n != size)
+ return 0;
+ return memcmp (sample, buf, size) == 0;
+}
+
+/*
+ * char(offset,value)
+ * True if byte is identical
+ */
+static int
+b_char (union argument *args, struct input_file *input)
+{
+ char val = args[1].number;
+ char buf;
+ return compare_bytes (args, input, &val, &buf, sizeof (buf));
+}
+
+/*
+ * short(offset,value)
+ * True if 16-bit integer is identical
+ * FIXME: Byte order
+ */
+static int
+b_short (union argument *args, struct input_file *input)
+{
+ uint16_t val = args[1].number;
+ uint16_t buf;
+ return compare_bytes (args, input, &val, &buf, sizeof (buf));
+}
+
+/*
+ * int(offset,value)
+ * True if 32-bit integer is identical
+ * FIXME: Byte order
+ */
+static int
+b_int (union argument *args, struct input_file *input)
+{
+ uint32_t val = args[1].number;
+ uint32_t buf;
+ return compare_bytes (args, input, &val, &buf, sizeof (buf));
+}
+
+/*
+ * locale("string")
+ * True if current locale matches string
+ */
+static int
+b_locale (union argument *args, struct input_file *input)
+{
+ abort (); /* FIXME */
+ return 0;
+}
+
+/*
+ * contains(offset,range,"string")
+ * True if the range contains the string
+ */
+static int
+b_contains (union argument *args, struct input_file *input)
+{
+ size_t i, count;
+ char *buf;
+ struct mimetypes_string *str = args[2].string;
+ int rc;
+
+ rc = mu_stream_seek (input->stream, args[0].number, MU_SEEK_SET, NULL);
+ if (rc)
+ {
+ mu_debug (MU_DEBCAT_MIMETYPES, MU_DEBUG_ERROR,
+ ("mu_stream_seek: %s", mu_strerror (rc)));
+ return 0;
+ }
+
+ if ((buf = malloc (args[1].number)) == NULL)
+ {
+ mu_debug (MU_DEBCAT_MIMETYPES, MU_DEBUG_ERROR,
+ ("malloc: %s", mu_strerror (rc)));
+ return 0;
+ }
+
+ rc = mu_stream_read (input->stream, buf, args[1].number, &count);
+ if (rc)
+ {
+ mu_debug (MU_DEBCAT_MIMETYPES, MU_DEBUG_ERROR,
+ ("mu_stream_read: %s", mu_strerror (rc)));
+ }
+ 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;
+}
+
+#define MIME_MAX_BUFFER 4096
+
+/*
+ * regex(offset,"regex")
+ * True if bytes match regular expression
+ */
+static int
+b_regex (union argument *args, struct input_file *input)
+{
+ size_t count;
+ int rc;
+ char buf[MIME_MAX_BUFFER];
+
+ rc = mu_stream_seek (input->stream, args[0].number, MU_SEEK_SET, NULL);
+ if (rc)
+ {
+ mu_debug (MU_DEBCAT_MIMETYPES, MU_DEBUG_ERROR,
+ ("mu_stream_seek: %s", mu_strerror (rc)));
+ return 0;
+ }
+
+ rc = mu_stream_read (input->stream, buf, sizeof buf - 1, &count);
+ if (rc)
+ {
+ mu_debug (MU_DEBCAT_MIMETYPES, MU_DEBUG_ERROR,
+ ("mu_stream_read: %s", mu_strerror (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 },
+ { "short", "dd", b_short },
+ { "int", "dd", b_int },
+ { "locale", "s", b_locale },
+ { "contains", "dds", b_contains },
+ { NULL }
+};
+
+struct builtin_tab const *
+mu_mimetypes_builtin (char const *ident)
+{
+ struct builtin_tab *p;
+ for (p = builtin_tab; p->name; p++)
+ if (strcmp (ident, p->name) == 0)
+ return p;
+ return NULL;
+}
+
+static int
+check_suffix (char *suf, struct input_file *input)
+{
+ char *p = strrchr (input->name, '.');
+ if (!p)
+ return 0;
+ 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_MIMETYPES, 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, struct input_file *input)
+{
+ int result;
+
+ switch (root->type)
+ {
+ case true_node:
+ result = 1;
+ break;
+
+ case functional_node:
+ result = root->v.function.builtin->handler (root->v.function.args, input);
+ break;
+
+ case binary_node:
+ result = eval_rule (root->v.bin.arg1, input);
+ switch (root->v.bin.op)
+ {
+ case L_OR:
+ if (!result)
+ result |= eval_rule (root->v.bin.arg2, input);
+ break;
+
+ case L_AND:
+ if (result)
+ result &= eval_rule (root->v.bin.arg2, input);
+ break;
+
+ default:
+ abort ();
+ }
+ break;
+
+ case negation_node:
+ result = !eval_rule (root->v.arg, input);
+ break;
+
+ case suffix_node:
+ result = check_suffix (root->v.suffix.ptr, input);
+ break;
+
+ default:
+ abort ();
+ }
+ mime_debug (MU_DEBUG_TRACE2, &root->loc, "result %s", result ? "true" : "false");
+ return result;
+}
+
+static int
+evaluate (void **itmv, size_t itmc, void *call_data)
+{
+ struct rule_tab *p = itmv[0];
+ if (eval_rule (p->node, call_data))
+ {
+ itmv[0] = p;
+ mime_debug (MU_DEBUG_TRACE1, &p->loc, "rule %s matches", p->type);
+ return MU_LIST_MAP_OK;
+ }
+ 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)
+ {
+ 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;
+}
+
+const char *
+mu_mimetypes_stream_type (mu_mimetypes_t mt, char const *name, mu_stream_t str)
+{
+ mu_list_t res = NULL;
+ const char *type = NULL;
+ struct input_file input;
+
+ input.name = name;
+ input.stream = str;
+
+ mu_stream_seek (str, 0, MU_SEEK_SET, NULL);
+ mu_list_map (mt->rule_list, evaluate, &input, 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;
+}
+
+const char *
+mu_mimetypes_file_type (mu_mimetypes_t mt, const char *file)
+{
+ int rc;
+ mu_stream_t str;
+ const char *res;
+
+ rc = mu_file_stream_create (&str, file, MU_STREAM_READ);
+ if (rc)
+ {
+ mu_debug (MU_DEBCAT_MIMETYPES, MU_DEBUG_ERROR,
+ ("cannot open %s: %s", file, mu_strerror (rc)));
+ return NULL;
+ }
+ res = mu_mimetypes_stream_type (mt, file, str);
+ mu_stream_destroy (&str);
+ return res;
+}
+
+const char *
+mu_mimetypes_fd_type (mu_mimetypes_t mt, const char *file, int fd)
+{
+ int rc;
+ mu_stream_t str;
+ const char *res;
+
+ rc = mu_fd_stream_create (&str, file, fd, MU_STREAM_READ);
+ if (rc)
+ {
+ mu_debug (MU_DEBCAT_MIMETYPES, MU_DEBUG_ERROR,
+ ("cannot open %s: %s", file, mu_strerror (rc)));
+ return NULL;
+ }
+ res = mu_mimetypes_stream_type (mt, file, str);
+ mu_stream_destroy (&str);
+ return res;
+}
diff --git a/libmailutils/mimetypes/grammar.y b/libmailutils/mimetypes/grammar.y
new file mode 100644
index 000000000..31979002b
--- /dev/null
+++ b/libmailutils/mimetypes/grammar.y
@@ -0,0 +1,627 @@
+%{
+/* GNU Mailutils -- a suite of utilities for electronic mail
+ Copyright (C) 2005-2021 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/>. */
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <mailutils/mimetypes.h>
+#include <grammar.h>
+
+static void
+yyprint (FILE *output, unsigned short toknum, YYSTYPE val)
+{
+ switch (toknum)
+ {
+ case TYPE:
+ case IDENT:
+ 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;
+ }
+}
+
+#define YYPRINT yyprint
+
+static struct node *make_node (mu_mimetypes_t mth,
+ enum node_type type,
+ struct mu_locus_range const *loc);
+static struct node *make_binary_node (mu_mimetypes_t mth,
+ int op,
+ struct node *left, struct node *rigth,
+ struct mu_locus_range const *loc);
+static struct node *make_negation_node (mu_mimetypes_t mth,
+ struct node *p,
+ struct mu_locus_range const *loc);
+
+static struct node *make_suffix_node (mu_mimetypes_t mth,
+ struct mimetypes_string *suffix,
+ struct mu_locus_range const *loc);
+static struct node *make_functional_node (mu_mimetypes_t mth,
+ char *ident, mu_list_t list,
+ struct mu_locus_range const *loc);
+
+static void *
+mimetypes_malloc (mu_mimetypes_t mth, size_t size)
+{
+ mu_opool_alloc (mth->pool, size);
+ return mu_opool_finish (mth->pool, NULL);
+}
+
+static struct mimetypes_string *
+mimetypes_string_dup (mu_mimetypes_t mth, struct mimetypes_string *s)
+{
+ mu_opool_append (mth->pool, s, sizeof *s);
+ return mu_opool_finish (mth->pool, NULL);
+}
+
+static void rule_destroy_item (void *ptr);
+
+%}
+
+%define api.pure full
+%define api.prefix {mimetypes_yy}
+
+%code requires {
+#include <stdio.h>
+#include <regex.h>
+#include <mailutils/stream.h>
+#include <mailutils/cctype.h>
+#include <mailutils/cstr.h>
+#include <mailutils/locus.h>
+#include <mailutils/yyloc.h>
+#include <mailutils/opool.h>
+#include <mailutils/list.h>
+#include <mailutils/nls.h>
+#include <mailutils/diag.h>
+#include <mailutils/stdstream.h>
+#include <mailutils/iterator.h>
+#include <mailutils/util.h>
+#include <mailutils/mimetypes.h>
+#include <mailutils/sys/mimetypes.h>
+
+#define MIMETYPES_YYLTYPE struct mu_locus_range
+
+typedef void *yyscan_t;
+
+struct parser_control
+{
+ mu_linetrack_t trk;
+ struct mu_locus_point string_beg;
+ size_t errors;
+ mu_mimetypes_t mth;
+};
+
+}
+
+%code provides {
+int mimetypes_yylex (MIMETYPES_YYSTYPE *lvalp, MIMETYPES_YYLTYPE *llocp,
+ yyscan_t yyscanner);
+int mimetypes_yylex_init_extra (struct parser_control *, yyscan_t *);
+int mimetypes_yylex_destroy (yyscan_t);
+void mimetypes_yyerror (MIMETYPES_YYLTYPE const *llocp,
+ struct parser_control *pctl, yyscan_t scanner,
+ char const *fmt, ...)
+ MU_PRINTFLIKE(4,5);
+int mimetypes_scanner_open (yyscan_t scanner, const char *name);
+
+void lex_next_rule (MIMETYPES_YYLTYPE *llocp, yyscan_t scanner);
+}
+
+%parse-param { struct parser_control *pctl } { void *yyscanner }
+%lex-param { yyscan_t yyscanner }
+%initial-action {
+ /*
+ * yylloc is a local variable of yyparse. It is maintained in sync with
+ * the trk member of struct parser_control, and need to be deallocated
+ * before returning from yyparse. Since Bison does not provide any
+ * %final-action, the variable is deinited in the <<EOF>> scanner rule.
+ */
+ mu_locus_range_init (&yylloc);
+}
+%locations
+%expect 15
+
+%token <string> TYPE IDENT
+%token <string> STRING
+%token EOL BOGUS PRIORITY
+
+%left ','
+%left '+'
+
+%type <string> arg
+%type <list> arglist
+%type <node> function stmt rule maybe_rule
+%type <result> priority maybe_priority
+
+%union {
+ struct mimetypes_string string;
+ char *s;
+ mu_list_t list;
+ int result;
+ struct node *node;
+}
+
+%destructor { mu_lis