Merge branch 'vendor/FILE'
[dragonfly.git] / contrib / file / src / apprentice.c
index 53f8c28..c180e68 100644 (file)
@@ -32,7 +32,7 @@
 #include "file.h"
 
 #ifndef        lint
-FILE_RCSID("@(#)$File: apprentice.c,v 1.173 2011/12/08 12:38:24 rrt Exp $")
+FILE_RCSID("@(#)$File: apprentice.c,v 1.202 2014/03/14 18:48:11 christos Exp $")
 #endif /* lint */
 
 #include "magic.h"
@@ -40,6 +40,9 @@ FILE_RCSID("@(#)$File: apprentice.c,v 1.173 2011/12/08 12:38:24 rrt Exp $")
 #ifdef HAVE_UNISTD_H
 #include <unistd.h>
 #endif
+#ifdef HAVE_STDDEF_H
+#include <stddef.h>
+#endif
 #include <string.h>
 #include <assert.h>
 #include <ctype.h>
@@ -48,6 +51,15 @@ FILE_RCSID("@(#)$File: apprentice.c,v 1.173 2011/12/08 12:38:24 rrt Exp $")
 #include <sys/mman.h>
 #endif
 #include <dirent.h>
+#if defined(HAVE_LIMITS_H)
+#include <limits.h>
+#endif
+
+#ifndef SSIZE_MAX
+#define MAXMAGIC_SIZE        ((ssize_t)0x7fffffff)
+#else
+#define MAXMAGIC_SIZE        SSIZE_MAX
+#endif
 
 #define        EATAB {while (isascii((unsigned char) *l) && \
                      isspace((unsigned char) *l))  ++l;}
@@ -71,12 +83,28 @@ FILE_RCSID("@(#)$File: apprentice.c,v 1.173 2011/12/08 12:38:24 rrt Exp $")
 #define MAP_FILE 0
 #endif
 
+#define ALLOC_CHUNK    (size_t)10
+#define ALLOC_INCR     (size_t)200
+
 struct magic_entry {
        struct magic *mp;       
        uint32_t cont_count;
        uint32_t max_count;
 };
 
+struct magic_entry_set {
+       struct magic_entry *me;
+       uint32_t count;
+       uint32_t max;
+};
+
+struct magic_map {
+       void *p;
+       size_t len;
+       struct magic *magic[MAGIC_SETS];
+       uint32_t nmagic[MAGIC_SETS];
+};
+
 int file_formats[FILE_NAMES_SIZE];
 const size_t file_nformats = FILE_NAMES_SIZE;
 const char *file_names[FILE_NAMES_SIZE];
@@ -86,24 +114,26 @@ private int getvalue(struct magic_set *ms, struct magic *, const char **, int);
 private int hextoint(int);
 private const char *getstr(struct magic_set *, struct magic *, const char *,
     int);
-private int parse(struct magic_set *, struct magic_entry **, uint32_t *,
-    const char *, size_t, int);
+private int parse(struct magic_set *, struct magic_entry *, const char *,
+    size_t, int);
 private void eatsize(const char **);
-private int apprentice_1(struct magic_set *, const char *, int, struct mlist *);
+private int apprentice_1(struct magic_set *, const char *, int);
 private size_t apprentice_magic_strength(const struct magic *);
 private int apprentice_sort(const void *, const void *);
 private void apprentice_list(struct mlist *, int );
-private int apprentice_load(struct magic_set *, struct magic **, uint32_t *,
+private struct magic_map *apprentice_load(struct magic_set *, 
     const char *, int);
+private struct mlist *mlist_alloc(void);
+private void mlist_free(struct mlist *);
 private void byteswap(struct magic *, uint32_t);
 private void bs1(struct magic *);
 private uint16_t swap2(uint16_t);
 private uint32_t swap4(uint32_t);
 private uint64_t swap8(uint64_t);
 private char *mkdbname(struct magic_set *, const char *, int);
-private int apprentice_map(struct magic_set *, struct magic **, uint32_t *,
-    const char *);
-private int apprentice_compile(struct magic_set *, struct magic **, uint32_t *,
+private struct magic_map *apprentice_map(struct magic_set *, const char *);
+private void apprentice_unmap(struct magic_map *);
+private int apprentice_compile(struct magic_set *, struct magic_map *,
     const char *);
 private int check_format_type(const char *, int);
 private int check_format(struct magic_set *, struct magic *);
@@ -113,7 +143,6 @@ private int parse_strength(struct magic_set *, struct magic_entry *, const char
 private int parse_apple(struct magic_set *, struct magic_entry *, const char *);
 
 
-private size_t maxmagic = 0;
 private size_t magicsize = sizeof(struct magic);
 
 private const char usg_hdr[] = "cont\toffset\ttype\topcode\tmask\tvalue\tdesc";
@@ -164,17 +193,27 @@ main(int argc, char *argv[])
 }
 #endif /* COMPILE_ONLY */
 
-static const struct type_tbl_s {
+struct type_tbl_s {
        const char name[16];
        const size_t len;
        const int type;
        const int format;
-} type_tbl[] = {
+};
+
+/*
+ * XXX - the actual Single UNIX Specification says that "long" means "long",
+ * as in the C data type, but we treat it as meaning "4-byte integer".
+ * Given that the OS X version of file 5.04 did the same, I guess that passes
+ * the actual test; having "long" be dependent on how big a "long" is on
+ * the machine running "file" is silly.
+ */
+static const struct type_tbl_s type_tbl[] = {
 # define XX(s)         s, (sizeof(s) - 1)
 # define XX_NULL       "", 0
+       { XX("invalid"),        FILE_INVALID,           FILE_FMT_NONE },
        { XX("byte"),           FILE_BYTE,              FILE_FMT_NUM },
        { XX("short"),          FILE_SHORT,             FILE_FMT_NUM },
-       { XX("default"),        FILE_DEFAULT,           FILE_FMT_STR },
+       { XX("default"),        FILE_DEFAULT,           FILE_FMT_NONE },
        { XX("long"),           FILE_LONG,              FILE_FMT_NUM },
        { XX("string"),         FILE_STRING,            FILE_FMT_STR },
        { XX("date"),           FILE_DATE,              FILE_FMT_STR },
@@ -212,18 +251,34 @@ static const struct type_tbl_s {
        { XX("ledouble"),       FILE_LEDOUBLE,          FILE_FMT_DOUBLE },
        { XX("leid3"),          FILE_LEID3,             FILE_FMT_NUM },
        { XX("beid3"),          FILE_BEID3,             FILE_FMT_NUM },
-       { XX("indirect"),       FILE_INDIRECT,          FILE_FMT_NONE },
+       { XX("indirect"),       FILE_INDIRECT,          FILE_FMT_NUM },
+       { XX("qwdate"),         FILE_QWDATE,            FILE_FMT_STR },
+       { XX("leqwdate"),       FILE_LEQWDATE,          FILE_FMT_STR },
+       { XX("beqwdate"),       FILE_BEQWDATE,          FILE_FMT_STR },
+       { XX("name"),           FILE_NAME,              FILE_FMT_NONE },
+       { XX("use"),            FILE_USE,               FILE_FMT_NONE },
+       { XX("clear"),          FILE_CLEAR,             FILE_FMT_NONE },
+       { XX_NULL,              FILE_INVALID,           FILE_FMT_NONE },
+};
+
+/*
+ * These are not types, and cannot be preceded by "u" to make them
+ * unsigned.
+ */
+static const struct type_tbl_s special_tbl[] = {
+       { XX("name"),           FILE_NAME,              FILE_FMT_STR },
+       { XX("use"),            FILE_USE,               FILE_FMT_STR },
        { XX_NULL,              FILE_INVALID,           FILE_FMT_NONE },
+};
 # undef XX
 # undef XX_NULL
-};
 
 private int
-get_type(const char *l, const char **t)
+get_type(const struct type_tbl_s *tbl, const char *l, const char **t)
 {
        const struct type_tbl_s *p;
 
-       for (p = type_tbl; p->len; p++) {
+       for (p = tbl; p->len; p++) {
                if (strncmp(l, p->name, p->len) == 0) {
                        if (t)
                                *t = l + p->len;
@@ -233,6 +288,91 @@ get_type(const char *l, const char **t)
        return p->type;
 }
 
+private int
+get_standard_integer_type(const char *l, const char **t)
+{
+       int type;
+
+       if (isalpha((unsigned char)l[1])) {
+               switch (l[1]) {
+               case 'C':
+                       /* "dC" and "uC" */
+                       type = FILE_BYTE;
+                       break;
+               case 'S':
+                       /* "dS" and "uS" */
+                       type = FILE_SHORT;
+                       break;
+               case 'I':
+               case 'L':
+                       /*
+                        * "dI", "dL", "uI", and "uL".
+                        *
+                        * XXX - the actual Single UNIX Specification says
+                        * that "L" means "long", as in the C data type,
+                        * but we treat it as meaning "4-byte integer".
+                        * Given that the OS X version of file 5.04 did
+                        * the same, I guess that passes the actual SUS
+                        * validation suite; having "dL" be dependent on
+                        * how big a "long" is on the machine running
+                        * "file" is silly.
+                        */
+                       type = FILE_LONG;
+                       break;
+               case 'Q':
+                       /* "dQ" and "uQ" */
+                       type = FILE_QUAD;
+                       break;
+               default:
+                       /* "d{anything else}", "u{anything else}" */
+                       return FILE_INVALID;
+               }
+               l += 2;
+       } else if (isdigit((unsigned char)l[1])) {
+               /*
+                * "d{num}" and "u{num}"; we only support {num} values
+                * of 1, 2, 4, and 8 - the Single UNIX Specification
+                * doesn't say anything about whether arbitrary
+                * values should be supported, but both the Solaris 10
+                * and OS X Mountain Lion versions of file passed the
+                * Single UNIX Specification validation suite, and
+                * neither of them support values bigger than 8 or
+                * non-power-of-2 values.
+                */
+               if (isdigit((unsigned char)l[2])) {
+                       /* Multi-digit, so > 9 */
+                       return FILE_INVALID;
+               }
+               switch (l[1]) {
+               case '1':
+                       type = FILE_BYTE;
+                       break;
+               case '2':
+                       type = FILE_SHORT;
+                       break;
+               case '4':
+                       type = FILE_LONG;
+                       break;
+               case '8':
+                       type = FILE_QUAD;
+                       break;
+               default:
+                       /* XXX - what about 3, 5, 6, or 7? */
+                       return FILE_INVALID;
+               }
+               l += 2;
+       } else {
+               /*
+                * "d" or "u" by itself.
+                */
+               type = FILE_LONG;
+               ++l;
+       }
+       if (t)
+               *t = l;
+       return type;
+}
+
 private void
 init_file_tables(void)
 {
@@ -248,73 +388,77 @@ init_file_tables(void)
                file_names[p->type] = p->name;
                file_formats[p->type] = p->format;
        }
+       assert(p - type_tbl == FILE_NAMES_SIZE);
+}
+
+private int
+add_mlist(struct mlist *mlp, struct magic_map *map, size_t idx)
+{
+       struct mlist *ml;
+
+       if ((ml = CAST(struct mlist *, malloc(sizeof(*ml)))) == NULL)
+               return -1;
+
+       ml->map = idx == 0 ? map : NULL;
+       ml->magic = map->magic[idx];
+       ml->nmagic = map->nmagic[idx];
+
+       mlp->prev->next = ml;
+       ml->prev = mlp->prev;
+       ml->next = mlp;
+       mlp->prev = ml;
+       return 0;
 }
 
 /*
  * Handle one file or directory.
  */
 private int
-apprentice_1(struct magic_set *ms, const char *fn, int action,
-    struct mlist *mlist)
+apprentice_1(struct magic_set *ms, const char *fn, int action)
 {
-       struct magic *magic = NULL;
-       uint32_t nmagic = 0;
        struct mlist *ml;
-       int rv = -1;
-       int mapped;
+       struct magic_map *map;
+       size_t i;
 
        if (magicsize != FILE_MAGICSIZE) {
                file_error(ms, 0, "magic element size %lu != %lu",
-                   (unsigned long)sizeof(*magic),
+                   (unsigned long)sizeof(*map->magic[0]),
                    (unsigned long)FILE_MAGICSIZE);
                return -1;
        }
 
        if (action == FILE_COMPILE) {
-               rv = apprentice_load(ms, &magic, &nmagic, fn, action);
-               if (rv != 0)
+               map = apprentice_load(ms, fn, action);
+               if (map == NULL)
                        return -1;
-               rv = apprentice_compile(ms, &magic, &nmagic, fn);
-               free(magic);
-               return rv;
+               return apprentice_compile(ms, map, fn);
        }
 
 #ifndef COMPILE_ONLY
-       if ((rv = apprentice_map(ms, &magic, &nmagic, fn)) == -1) {
+       map = apprentice_map(ms, fn);
+       if (map == NULL) {
                if (ms->flags & MAGIC_CHECK)
                        file_magwarn(ms, "using regular magic file `%s'", fn);
-               rv = apprentice_load(ms, &magic, &nmagic, fn, action);
-               if (rv != 0)
+               map = apprentice_load(ms, fn, action);
+               if (map == NULL)
                        return -1;
        }
 
-       mapped = rv;
-            
-       if (magic == NULL) {
-               file_delmagic(magic, mapped, nmagic);
-               return -1;
-       }
-
-       if ((ml = CAST(struct mlist *, malloc(sizeof(*ml)))) == NULL) {
-               file_delmagic(magic, mapped, nmagic);
-               file_oomem(ms, sizeof(*ml));
-               return -1;
+       for (i = 0; i < MAGIC_SETS; i++) {
+               if (add_mlist(ms->mlist[i], map, i) == -1) {
+                       file_oomem(ms, sizeof(*ml));
+                       apprentice_unmap(map);
+                       return -1;
+               }
        }
 
-       ml->magic = magic;
-       ml->nmagic = nmagic;
-       ml->mapped = mapped;
-
-       mlist->prev->next = ml;
-       ml->prev = mlist->prev;
-       ml->next = mlist;
-       mlist->prev = ml;
-
        if (action == FILE_LIST) {
-               printf("Binary patterns:\n");
-               apprentice_list(mlist, BINTEST);
-               printf("Text patterns:\n");
-               apprentice_list(mlist, TEXTTEST);
+               for (i = 0; i < MAGIC_SETS; i++) {
+                       printf("Set %zu:\nBinary patterns:\n", i);
+                       apprentice_list(ms->mlist[i], BINTEST);
+                       printf("Text patterns:\n");
+                       apprentice_list(ms->mlist[i], TEXTTEST);
+               }
        }
        
        return 0;
@@ -322,57 +466,133 @@ apprentice_1(struct magic_set *ms, const char *fn, int action,
 }
 
 protected void
-file_delmagic(struct magic *p, int type, size_t entries)
+file_ms_free(struct magic_set *ms)
 {
-       if (p == NULL)
+       size_t i;
+       if (ms == NULL)
+               return;
+       for (i = 0; i < MAGIC_SETS; i++)
+               mlist_free(ms->mlist[i]);
+       free(ms->o.pbuf);
+       free(ms->o.buf);
+       free(ms->c.li);
+       free(ms);
+}
+
+protected struct magic_set *
+file_ms_alloc(int flags)
+{
+       struct magic_set *ms;
+       size_t i, len;
+
+       if ((ms = CAST(struct magic_set *, calloc((size_t)1,
+           sizeof(struct magic_set)))) == NULL)
+               return NULL;
+
+       if (magic_setflags(ms, flags) == -1) {
+               errno = EINVAL;
+               goto free;
+       }
+
+       ms->o.buf = ms->o.pbuf = NULL;
+       len = (ms->c.len = 10) * sizeof(*ms->c.li);
+
+       if ((ms->c.li = CAST(struct level_info *, malloc(len))) == NULL)
+               goto free;
+
+       ms->event_flags = 0;
+       ms->error = -1;
+       for (i = 0; i < MAGIC_SETS; i++)
+               ms->mlist[i] = NULL;
+       ms->file = "unknown";
+       ms->line = 0;
+       return ms;
+free:
+       free(ms);
+       return NULL;
+}
+
+private void
+apprentice_unmap(struct magic_map *map)
+{
+       if (map == NULL)
+               return;
+       if (map->p == NULL)
                return;
-       switch (type) {
-       case 2:
 #ifdef QUICK
-               p--;
-               (void)munmap((void *)p, sizeof(*p) * (entries + 1));
-               break;
-#else
-               (void)&entries;
-               abort();
-               /*NOTREACHED*/
+       if (map->len)
+               (void)munmap(map->p, map->len);
+       else
 #endif
-       case 1:
-               p--;
-               /*FALLTHROUGH*/
-       case 0:
-               free(p);
-               break;
-       default:
-               abort();
+               free(map->p);
+       free(map);
+}
+
+private struct mlist *
+mlist_alloc(void)
+{
+       struct mlist *mlist;
+       if ((mlist = CAST(struct mlist *, calloc(1, sizeof(*mlist)))) == NULL) {
+               return NULL;
+       }
+       mlist->next = mlist->prev = mlist;
+       return mlist;
+}
+
+private void
+mlist_free(struct mlist *mlist)
+{
+       struct mlist *ml;
+
+       if (mlist == NULL)
+               return;
+
+       for (ml = mlist->next; ml != mlist;) {
+               struct mlist *next = ml->next;
+               if (ml->map)
+                       apprentice_unmap(ml->map);
+               free(ml);
+               ml = next;
        }
+       free(ml);
 }
 
 /* const char *fn: list of magic files and directories */
-protected struct mlist *
+protected int
 file_apprentice(struct magic_set *ms, const char *fn, int action)
 {
        char *p, *mfn;
        int file_err, errs = -1;
-       struct mlist *mlist;
+       size_t i;
+
+       if (ms->mlist[0] != NULL)
+               file_reset(ms);
 
        if ((fn = magic_getpath(fn, action)) == NULL)
-               return NULL;
+               return -1;
 
        init_file_tables();
 
        if ((mfn = strdup(fn)) == NULL) {
                file_oomem(ms, strlen(fn));
-               return NULL;
+               return -1;
        }
-       fn = mfn;
 
-       if ((mlist = CAST(struct mlist *, malloc(sizeof(*mlist)))) == NULL) {
-               free(mfn);
-               file_oomem(ms, sizeof(*mlist));
-               return NULL;
+       for (i = 0; i < MAGIC_SETS; i++) {
+               mlist_free(ms->mlist[i]);
+               if ((ms->mlist[i] = mlist_alloc()) == NULL) {
+                       file_oomem(ms, sizeof(*ms->mlist[i]));
+                       if (i != 0) {
+                               --i;
+                               do
+                                       mlist_free(ms->mlist[i]);
+                               while (i != 0);
+                       }
+                       free(mfn);
+                       return -1;
+               }
        }
-       mlist->next = mlist->prev = mlist;
+       fn = mfn;
 
        while (fn) {
                p = strchr(fn, PATHSEP);
@@ -380,19 +600,94 @@ file_apprentice(struct magic_set *ms, const char *fn, int action)
                        *p++ = '\0';
                if (*fn == '\0')
                        break;
-               file_err = apprentice_1(ms, fn, action, mlist);
+               file_err = apprentice_1(ms, fn, action);
                errs = MAX(errs, file_err);
                fn = p;
        }
+
+       free(mfn);
+
        if (errs == -1) {
-               free(mfn);
-               free(mlist);
-               mlist = NULL;
-               file_error(ms, 0, "could not find any magic files!");
-               return NULL;
+               for (i = 0; i < MAGIC_SETS; i++) {
+                       mlist_free(ms->mlist[i]);
+                       ms->mlist[i] = NULL;
+               }
+               file_error(ms, 0, "could not find any valid magic files!");
+               return -1;
        }
-       free(mfn);
-       return mlist;
+
+#if 0
+       /*
+        * Always leave the database loaded
+        */
+       if (action == FILE_LOAD)
+               return 0;
+
+       for (i = 0; i < MAGIC_SETS; i++) {
+               mlist_free(ms->mlist[i]);
+               ms->mlist[i] = NULL;
+       }
+#endif
+
+       switch (action) {
+       case FILE_LOAD:
+       case FILE_COMPILE:
+       case FILE_CHECK:
+       case FILE_LIST:
+               return 0;
+       default:
+               file_error(ms, 0, "Invalid action %d", action);
+               return -1;
+       }
+}
+
+/*
+ * Compute the real length of a magic expression, for the purposes
+ * of determining how "strong" a magic expression is (approximating
+ * how specific its matches are):
+ *     - magic characters count 0 unless escaped.
+ *     - [] expressions count 1
+ *     - {} expressions count 0
+ *     - regular characters or escaped magic characters count 1
+ *     - 0 length expressions count as one
+ */
+private size_t
+nonmagic(const char *str)
+{
+       const char *p;
+       size_t rv = 0;
+
+       for (p = str; *p; p++)
+               switch (*p) {
+               case '\\':      /* Escaped anything counts 1 */
+                       if (!*++p)
+                               p--;
+                       rv++;
+                       continue;
+               case '?':       /* Magic characters count 0 */
+               case '*':
+               case '.':
+               case '+':
+               case '^':
+               case '$':
+                       continue;
+               case '[':       /* Bracketed expressions count 1 the ']' */
+                       while (*p && *p != ']')
+                               p++;
+                       p--;
+                       continue;
+               case '{':       /* Braced expressions count 0 */
+                       while (*p && *p != '}')
+                               p++;
+                       if (!*p)
+                               p--;
+                       continue;
+               default:        /* Anything else counts 1 */
+                       rv++;
+                       continue;
+               }
+
+       return rv == 0 ? 1 : rv;        /* Return at least 1 */
 }
 
 /*
@@ -402,7 +697,7 @@ private size_t
 apprentice_magic_strength(const struct magic *m)
 {
 #define MULT 10
-       size_t val = 2 * MULT;  /* baseline strength */
+       size_t v, val = 2 * MULT;       /* baseline strength */
 
        switch (m->type) {
        case FILE_DEFAULT:      /* make sure this sorts last */
@@ -438,10 +733,14 @@ apprentice_magic_strength(const struct magic *m)
                break;
 
        case FILE_SEARCH:
-       case FILE_REGEX:
                val += m->vallen * MAX(MULT / m->vallen, 1);
                break;
 
+       case FILE_REGEX:
+               v = nonmagic(m->value.s);
+               val += v * MAX(MULT / v, 1);
+               break;
+
        case FILE_DATE:
        case FILE_LEDATE:
        case FILE_BEDATE:
@@ -465,12 +764,20 @@ apprentice_magic_strength(const struct magic *m)
        case FILE_QLDATE:
        case FILE_LEQLDATE:
        case FILE_BEQLDATE:
+       case FILE_QWDATE:
+       case FILE_LEQWDATE:
+       case FILE_BEQWDATE:
        case FILE_DOUBLE:
        case FILE_BEDOUBLE:
        case FILE_LEDOUBLE:
                val += 8 * MULT;
                break;
 
+       case FILE_INDIRECT:
+       case FILE_NAME:
+       case FILE_USE:
+               break;
+
        default:
                val = 0;
                (void)fprintf(stderr, "Bad type %d\n", m->type);
@@ -617,6 +924,9 @@ set_test_type(struct magic *mstart, struct magic *m)
        case FILE_QLDATE:
        case FILE_LEQLDATE:
        case FILE_BEQLDATE:
+       case FILE_QWDATE:
+       case FILE_LEQWDATE:
+       case FILE_BEQWDATE:
        case FILE_FLOAT:
        case FILE_BEFLOAT:
        case FILE_LEFLOAT:
@@ -665,16 +975,41 @@ set_test_type(struct magic *mstart, struct magic *m)
        }
 }
 
+private int
+addentry(struct magic_set *ms, struct magic_entry *me,
+   struct magic_entry_set *mset)
+{
+       size_t i = me->mp->type == FILE_NAME ? 1 : 0;
+       if (mset[i].count == mset[i].max) {
+               struct magic_entry *mp;
+
+               mset[i].max += ALLOC_INCR;
+               if ((mp = CAST(struct magic_entry *,
+                   realloc(mset[i].me, sizeof(*mp) * mset[i].max))) ==
+                   NULL) {
+                       file_oomem(ms, sizeof(*mp) * mset[i].max);
+                       return -1;
+               }
+               (void)memset(&mp[mset[i].count], 0, sizeof(*mp) *
+                   ALLOC_INCR);
+               mset[i].me = mp;
+       }
+       mset[i].me[mset[i].count++] = *me;
+       memset(me, 0, sizeof(*me));
+       return 0;
+}
+
 /*
  * Load and parse one file.
  */
 private void
 load_1(struct magic_set *ms, int action, const char *fn, int *errs,
-   struct magic_entry **marray, uint32_t *marraycount)
+   struct magic_entry_set *mset)
 {
        size_t lineno = 0, llen = 0;
        char *line = NULL;
        ssize_t len;
+       struct magic_entry me;
 
        FILE *f = fopen(ms->file = fn, "r");
        if (f == NULL) {
@@ -685,6 +1020,7 @@ load_1(struct magic_set *ms, int action, const char *fn, int *errs,
                return;
        }
 
+       memset(&me, 0, sizeof(me));
        /* read and parse this file */
        for (ms->line = 1; (len = getline(&line, &llen, f)) != -1;
            ms->line++) {
@@ -714,15 +1050,14 @@ load_1(struct magic_set *ms, int action, const char *fn, int *errs,
                                        (*errs)++;
                                        continue;
                                }
-                               if (*marraycount == 0) {
+                               if (me.mp == NULL) {
                                        file_error(ms, 0,
                                            "No current entry for :!%s type",
                                                bang[i].name);
                                        (*errs)++;
                                        continue;
                                }
-                               if ((*bang[i].fun)(ms, 
-                                   &(*marray)[*marraycount - 1],
+                               if ((*bang[i].fun)(ms, &me,
                                    line + bang[i].len + 2) != 0) {
                                        (*errs)++;
                                        continue;
@@ -731,12 +1066,21 @@ load_1(struct magic_set *ms, int action, const char *fn, int *errs,
                        }
                        /*FALLTHROUGH*/
                default:
-                       if (parse(ms, marray, marraycount, line, lineno,
-                           action) != 0)
+               again:
+                       switch (parse(ms, &me, line, lineno, action)) {
+                       case 0:
+                               continue;
+                       case 1:
+                               (void)addentry(ms, &me, mset);
+                               goto again;
+                       default:
                                (*errs)++;
-                       break;
+                               break;
+                       }
                }
        }
+       if (me.mp)
+               (void)addentry(ms, &me, mset);
        free(line);
        (void)fclose(f);
 }
@@ -751,28 +1095,120 @@ cmpstrp(const void *p1, const void *p2)
         return strcmp(*(char *const *)p1, *(char *const *)p2);
 }
 
+
+private uint32_t
+set_text_binary(struct magic_set *ms, struct magic_entry *me, uint32_t nme,
+    uint32_t starttest)
+{
+       static const char text[] = "text";
+       static const char binary[] = "binary";
+       static const size_t len = sizeof(text);
+
+       uint32_t i = starttest;
+
+       do {
+               set_test_type(me[starttest].mp, me[i].mp);
+               if ((ms->flags & MAGIC_DEBUG) == 0)
+                       continue;
+               (void)fprintf(stderr, "%s%s%s: %s\n",
+                   me[i].mp->mimetype,
+                   me[i].mp->mimetype[0] == '\0' ? "" : "; ",
+                   me[i].mp->desc[0] ? me[i].mp->desc : "(no description)",
+                   me[i].mp->flag & BINTEST ? binary : text);
+               if (me[i].mp->flag & BINTEST) {
+                       char *p = strstr(me[i].mp->desc, text);
+                       if (p && (p == me[i].mp->desc ||
+                           isspace((unsigned char)p[-1])) &&
+                           (p + len - me[i].mp->desc == MAXstring
+                           || (p[len] == '\0' ||
+                           isspace((unsigned char)p[len]))))
+                               (void)fprintf(stderr, "*** Possible "
+                                   "binary test for text type\n");
+               }
+       } while (++i < nme && me[i].mp->cont_level != 0);
+       return i;
+}
+
+private void
+set_last_default(struct magic_set *ms, struct magic_entry *me, uint32_t nme)
+{
+       uint32_t i;
+       for (i = 0; i < nme; i++) {
+               if (me[i].mp->cont_level == 0 &&
+                   me[i].mp->type == FILE_DEFAULT) {
+                       while (++i < nme)
+                               if (me[i].mp->cont_level == 0)
+                                       break;
+                       if (i != nme) {
+                               /* XXX - Ugh! */
+                               ms->line = me[i].mp->lineno;
+                               file_magwarn(ms,
+                                   "level 0 \"default\" did not sort last");
+                       }
+                       return;                                     
+               }
+       }
+}
+
 private int
-apprentice_load(struct magic_set *ms, struct magic **magicp, uint32_t *nmagicp,
-    const char *fn, int action)
+coalesce_entries(struct magic_set *ms, struct magic_entry *me, uint32_t nme,
+    struct magic **ma, uint32_t *nma)
+{
+       uint32_t i, mentrycount = 0;
+       size_t slen;
+
+       for (i = 0; i < nme; i++)
+               mentrycount += me[i].cont_count;
+
+       slen = sizeof(**ma) * mentrycount;
+       if ((*ma = CAST(struct magic *, malloc(slen))) == NULL) {
+               file_oomem(ms, slen);
+               return -1;
+       }
+
+       mentrycount = 0;
+       for (i = 0; i < nme; i++) {
+               (void)memcpy(*ma + mentrycount, me[i].mp,
+                   me[i].cont_count * sizeof(**ma));
+               mentrycount += me[i].cont_count;
+       }
+       *nma = mentrycount;
+       return 0;
+}
+
+private void
+magic_entry_free(struct magic_entry *me, uint32_t nme)
+{
+       uint32_t i;
+       if (me == NULL)
+               return;
+       for (i = 0; i < nme; i++)
+               free(me[i].mp);
+       free(me);
+}
+
+private struct magic_map *
+apprentice_load(struct magic_set *ms, const char *fn, int action)
 {
        int errs = 0;
-       struct magic_entry *marray;
-       uint32_t marraycount, i, mentrycount = 0, starttest;
-       size_t slen, files = 0, maxfiles = 0;
+       uint32_t i, j;
+       size_t files = 0, maxfiles = 0;
        char **filearr = NULL, *mfn;
        struct stat st;
+       struct magic_map *map;
+       struct magic_entry_set mset[MAGIC_SETS];
        DIR *dir;
        struct dirent *d;
 
+       memset(mset, 0, sizeof(mset));
        ms->flags |= MAGIC_CHECK;       /* Enable checks for parsed files */
 
-        maxmagic = MAXMAGIS;
-       if ((marray = CAST(struct magic_entry *, calloc(maxmagic,
-           sizeof(*marray)))) == NULL) {
-               file_oomem(ms, maxmagic * sizeof(*marray));
-               return -1;
+
+       if ((map = CAST(struct magic_map *, calloc(1, sizeof(*map)))) == NULL)
+       {
+               file_oomem(ms, sizeof(*map));
+               return NULL;
        }
-       marraycount = 0;
 
        /* print silly verbose header for USG compat. */
        if (action == FILE_CHECK)
@@ -815,100 +1251,54 @@ apprentice_load(struct magic_set *ms, struct magic **magicp, uint32_t *nmagicp,
                closedir(dir);
                qsort(filearr, files, sizeof(*filearr), cmpstrp);
                for (i = 0; i < files; i++) {
-                       load_1(ms, action, filearr[i], &errs, &marray,
-                           &marraycount);
+                       load_1(ms, action, filearr[i], &errs, mset);
                        free(filearr[i]);
                }
                free(filearr);
        } else
-               load_1(ms, action, fn, &errs, &marray, &marraycount);
+               load_1(ms, action, fn, &errs, mset);
        if (errs)
                goto out;
 
-       /* Set types of tests */
-       for (i = 0; i < marraycount; ) {
-               if (marray[i].mp->cont_level != 0) {
-                       i++;
-                       continue;
-               }
-
-               starttest = i;
-               do {
-                       static const char text[] = "text";
-                       static const char binary[] = "binary";
-                       static const size_t len = sizeof(text);
-                       set_test_type(marray[starttest].mp, marray[i].mp);
-                       if ((ms->flags & MAGIC_DEBUG) == 0)
+       for (j = 0; j < MAGIC_SETS; j++) {
+               /* Set types of tests */
+               for (i = 0; i < mset[j].count; ) {
+                       if (mset[j].me[i].mp->cont_level != 0) {
+                               i++;
                                continue;
-                       (void)fprintf(stderr, "%s%s%s: %s\n",
-                           marray[i].mp->mimetype,
-                           marray[i].mp->mimetype[0] == '\0' ? "" : "; ",
-                           marray[i].mp->desc[0] ? marray[i].mp->desc :
-                           "(no description)",
-                           marray[i].mp->flag & BINTEST ? binary : text);
-                       if (marray[i].mp->flag & BINTEST) {
-                               char *p = strstr(marray[i].mp->desc, text);
-                               if (p && (p == marray[i].mp->desc ||
-                                   isspace((unsigned char)p[-1])) &&
-                                   (p + len - marray[i].mp->desc == 
-                                   MAXstring || (p[len] == '\0' ||
-                                   isspace((unsigned char)p[len]))))
-                                       (void)fprintf(stderr, "*** Possible "
-                                           "binary test for text type\n");
                        }
-               } while (++i < marraycount && marray[i].mp->cont_level != 0);
-       }
-
-       qsort(marray, marraycount, sizeof(*marray), apprentice_sort);
-
-       /*
-        * Make sure that any level 0 "default" line is last (if one exists).
-        */
-       for (i = 0; i < marraycount; i++) {
-               if (marray[i].mp->cont_level == 0 &&
-                   marray[i].mp->type == FILE_DEFAULT) {
-                       while (++i < marraycount)
-                               if (marray[i].mp->cont_level == 0)
-                                       break;
-                       if (i != marraycount) {
-                               /* XXX - Ugh! */
-                               ms->line = marray[i].mp->lineno;
-                               file_magwarn(ms,
-                                   "level 0 \"default\" did not sort last");
-                       }
-                       break;                                      
+                       i = set_text_binary(ms, mset[j].me, mset[j].count, i);
                }
-       }
+               qsort(mset[j].me, mset[j].count, sizeof(*mset[j].me),
+                   apprentice_sort);
 
-       for (i = 0; i < marraycount; i++)
-               mentrycount += marray[i].cont_count;
+               /*
+                * Make sure that any level 0 "default" line is last
+                * (if one exists).
+                */
+               set_last_default(ms, mset[j].me, mset[j].count);
 
-       slen = sizeof(**magicp) * mentrycount;
-       if ((*magicp = CAST(struct magic *, malloc(slen))) == NULL) {
-               file_oomem(ms, slen);
-               errs++;
-               goto out;
+               /* coalesce per file arrays into a single one */
+               if (coalesce_entries(ms, mset[j].me, mset[j].count,
+                   &map->magic[j], &map->nmagic[j]) == -1) {
+                       errs++;
+                       goto out;
+               }
        }
 
-       mentrycount = 0;
-       for (i = 0; i < marraycount; i++) {
-               (void)memcpy(*magicp + mentrycount, marray[i].mp,
-                   marray[i].cont_count * sizeof(**magicp));
-               mentrycount += marray[i].cont_count;
-       }
 out:
-       for (i = 0; i < marraycount; i++)
-               free(marray[i].mp);
-       free(marray);
+       for (j = 0; j < MAGIC_SETS; j++)
+               magic_entry_free(mset[j].me, mset[j].count);
+
        if (errs) {
-               *magicp = NULL;
-               *nmagicp = 0;
-               return errs;
-       } else {
-               *nmagicp = mentrycount;
-               return 0;
+               for (j = 0; j < MAGIC_SETS; j++) {
+                       if (map->magic[j])
+                               free(map->magic[j]);
+               }
+               free(map);
+               return NULL;
        }
-
+       return map;
 }
 
 /*
@@ -954,10 +1344,13 @@ file_signextend(struct magic_set *ms, struct magic *m, uint64_t v)
                case FILE_LEQUAD:
                case FILE_QDATE:
                case FILE_QLDATE:
+               case FILE_QWDATE:
                case FILE_BEQDATE:
                case FILE_BEQLDATE:
+               case FILE_BEQWDATE:
                case FILE_LEQDATE:
                case FILE_LEQLDATE:
+               case FILE_LEQWDATE:
                case FILE_DOUBLE:
                case FILE_BEDOUBLE:
                case FILE_LEDOUBLE:
@@ -971,6 +1364,9 @@ file_signextend(struct magic_set *ms, struct magic *m, uint64_t v)
                case FILE_SEARCH:
                case FILE_DEFAULT:
                case FILE_INDIRECT:
+               case FILE_NAME:
+               case FILE_USE:
+               case FILE_CLEAR:
                        break;
                default:
                        if (ms->flags & MAGIC_CHECK)
@@ -1140,22 +1536,25 @@ check_cond(struct magic_set *ms, int cond, uint32_t cont_level)
  * parse one line from magic file, put into magic[index++] if valid
  */
 private int
-parse(struct magic_set *ms, struct magic_entry **mentryp, uint32_t *nmentryp, 
-    const char *line, size_t lineno, int action)
+parse(struct magic_set *ms, struct magic_entry *me, const char *line,
+    size_t lineno, int action)
 {
 #ifdef ENABLE_CONDITIONALS
        static uint32_t last_cont_level = 0;
 #endif
        size_t i;
-       struct magic_entry *me;
        struct magic *m;
        const char *l = line;
        char *t;
        int op;
        uint32_t cont_level;
+       int32_t diff;
 
        cont_level = 0;
 
+       /*
+        * Parse the offset.
+        */
        while (*l == '>') {
                ++l;            /* step over */
                cont_level++; 
@@ -1166,16 +1565,21 @@ parse(struct magic_set *ms, struct magic_entry **mentryp, uint32_t *nmentryp,
                        return -1;
        last_cont_level = cont_level;
 #endif
-
-#define ALLOC_CHUNK    (size_t)10
-#define ALLOC_INCR     (size_t)200
-
        if (cont_level != 0) {
-               if (*nmentryp == 0) {
-                       file_error(ms, 0, "No current entry for continuation");
+               if (me->mp == NULL) {
+                       file_magerror(ms, "No current entry for continuation");
+                       return -1;
+               }
+               if (me->cont_count == 0) {
+                       file_magerror(ms, "Continuations present with 0 count");
                        return -1;
                }
-               me = &(*mentryp)[*nmentryp - 1];
+               m = &me->mp[me->cont_count - 1];
+               diff = (int32_t)cont_level - (int32_t)m->cont_level;
+               if (diff > 1)
+                       file_magwarn(ms, "New continuation level %u is more "
+                           "than one larger than current level %u", cont_level,
+                           m->cont_level);
                if (me->cont_count == me->max_count) {
                        struct magic *nm;
                        size_t cnt = me->max_count + ALLOC_CHUNK;
@@ -1191,31 +1595,15 @@ parse(struct magic_set *ms, struct magic_entry **mentryp, uint32_t *nmentryp,
                (void)memset(m, 0, sizeof(*m));
                m->cont_level = cont_level;
        } else {
-               if (*nmentryp == maxmagic) {
-                       struct magic_entry *mp;
-
-                       maxmagic += ALLOC_INCR;
-                       if ((mp = CAST(struct magic_entry *,
-                           realloc(*mentryp, sizeof(*mp) * maxmagic))) ==
-                           NULL) {
-                               file_oomem(ms, sizeof(*mp) * maxmagic);
-                               return -1;
-                       }
-                       (void)memset(&mp[*nmentryp], 0, sizeof(*mp) *
-                           ALLOC_INCR);
-                       *mentryp = mp;
+               static const size_t len = sizeof(*m) * ALLOC_CHUNK;
+               if (me->mp != NULL)
+                       return 1;
+               if ((m = CAST(struct magic *, malloc(len))) == NULL) {
+                       file_oomem(ms, len);
+                       return -1;
                }
-               me = &(*mentryp)[*nmentryp];
-               if (me->mp == NULL) {
-                       size_t len = sizeof(*m) * ALLOC_CHUNK;
-                       if ((m = CAST(struct magic *, malloc(len))) == NULL) {
-                               file_oomem(ms, len);
-                               return -1;
-                       }
-                       me->mp = m;
-                       me->max_count = ALLOC_CHUNK;
-               } else
-                       m = me->mp;
+               me->mp = m;
+               me->max_count = ALLOC_CHUNK;
                (void)memset(m, 0, sizeof(*m));
                m->factor_op = FILE_FACTOR_OP_NONE;
                m->cont_level = 0;
@@ -1345,12 +1733,54 @@ parse(struct magic_set *ms, struct magic_entry **mentryp, uint32_t *nmentryp,
        EATAB;
 #endif
 
+       /*
+        * Parse the type.
+        */
        if (*l == 'u') {
-               ++l;
-               m->flag |= UNSIGNED;
+               /*
+                * Try it as a keyword type prefixed by "u"; match what
+                * follows the "u".  If that fails, try it as an SUS
+                * integer type. 
+                */
+               m->type = get_type(type_tbl, l + 1, &l);
+               if (m->type == FILE_INVALID) {
+                       /*
+                        * Not a keyword type; parse it as an SUS type,
+                        * 'u' possibly followed by a number or C/S/L.
+                        */
+                       m->type = get_standard_integer_type(l, &l);
+               }
+               // It's unsigned.
+               if (m->type != FILE_INVALID)
+                       m->flag |= UNSIGNED;
+       } else {
+               /*
+                * Try it as a keyword type.  If that fails, try it as
+                * an SUS integer type if it begins with "d" or as an
+                * SUS string type if it begins with "s".  In any case,
+                * it's not unsigned.
+                */
+               m->type = get_type(type_tbl, l, &l);
+               if (m->type == FILE_INVALID) {
+                       /*
+                        * Not a keyword type; parse it as an SUS type,
+                        * either 'd' possibly followed by a number or
+                        * C/S/L, or just 's'.
+                        */
+                       if (*l == 'd')
+                               m->type = get_standard_integer_type(l, &l);
+                       else if (*l == 's' && !isalpha((unsigned char)l[1])) {
+                               m->type = FILE_STRING;
+                               ++l;
+                       }
+               }
        }
 
-       m->type = get_type(l, &l);
+       if (m->type == FILE_INVALID) {
+               /* Not found - try it as a special keyword. */
+               m->type = get_type(special_tbl, l, &l);
+       }
+                       
        if (m->type == FILE_INVALID) {
                if (ms->flags & MAGIC_CHECK)
                        file_magwarn(ms, "type `%s' invalid", l);
@@ -1423,6 +1853,9 @@ parse(struct magic_set *ms, struct magic_entry **mentryp, uint32_t *nmentryp,
                                case CHAR_TEXTTEST:
                                        m->str_flags |= STRING_TEXTTEST;
                                        break;
+                               case CHAR_TRIM:
+                                       m->str_flags |= STRING_TRIM;
+                                       break;
                                case CHAR_PSTRING_1_LE:
                                        if (m->type != FILE_PSTRING)
                                                goto bad;
@@ -1565,8 +1998,6 @@ parse(struct magic_set *ms, struct magic_entry **mentryp, uint32_t *nmentryp,
        }
 #endif
        m->mimetype[0] = '\0';          /* initialise MIME type to none */
-       if (m->cont_level == 0)
-               ++(*nmentryp);          /* make room for next */
        return 0;
 }
 
@@ -1588,6 +2019,11 @@ parse_strength(struct magic_set *ms, struct magic_entry *me, const char *line)
                    m->factor_op, m->factor);
                return -1;
        }
+       if (m->type == FILE_NAME) {
+               file_magwarn(ms, "%s: Strength setting is not supported in "
+                   "\"name\" magic entries", m->value.s);
+               return -1;
+       }
        EATAB;
        switch (*l) {
        case FILE_FACTOR_OP_NONE:
@@ -1624,33 +2060,43 @@ out:
        return -1;
 }
 
-/*
- * Parse an Apple CREATOR/TYPE annotation from magic file and put it into
- * magic[index - 1]
- */
 private int
-parse_apple(struct magic_set *ms, struct magic_entry *me, const char *line)
+parse_extra(struct magic_set *ms, struct magic_entry *me, const char *line,
+    off_t off, size_t len, const char *name, int nt)
 {
        size_t i;
        const char *l = line;
        struct magic *m = &me->mp[me->cont_count == 0 ? 0 : me->cont_count - 1];
+       char *buf = (char *)m + off;
 
-       if (m->apple[0] != '\0') {
-               file_magwarn(ms, "Current entry already has a APPLE type "
-                   "`%.8s', new type `%s'", m->mimetype, l);
+       if (buf[0] != '\0') {
+               len = nt ? strlen(buf) : len;
+               file_magwarn(ms, "Current entry already has a %s type "
+                   "`%.*s', new type `%s'", name, (int)len, buf, l);
                return -1;
        }       
 
+       if (*m->desc == '\0') {
+               file_magwarn(ms, "Current entry does not yet have a "
+                   "description for adding a %s type", name);
+               return -1;
+       }
+
        EATAB;
        for (i = 0; *l && ((isascii((unsigned char)*l) &&
            isalnum((unsigned char)*l)) || strchr("-+/.", *l)) &&
-           i < sizeof(m->apple); m->apple[i++] = *l++)
+           i < len; buf[i++] = *l++)
                continue;
-       if (i == sizeof(m->apple) && *l) {
-               /* We don't need to NUL terminate here, printing handles it */
+
+       if (i == len && *l) {
+               if (nt)
+                       buf[len - 1] = '\0';
                if (ms->flags & MAGIC_CHECK)
-                       file_magwarn(ms, "APPLE type `%s' truncated %"
-                           SIZE_T_FORMAT "u", line, i);
+                       file_magwarn(ms, "%s type `%s' truncated %"
+                           SIZE_T_FORMAT "u", name, line, i);
+       } else {
+               if (nt)
+                       buf[i] = '\0';
        }
 
        if (i > 0)
@@ -1659,6 +2105,19 @@ parse_apple(struct magic_set *ms, struct magic_entry *me, const char *line)
                return -1;
 }
 
+/*
+ * Parse an Apple CREATOR/TYPE annotation from magic file and put it into
+ * magic[index - 1]
+ */
+private int
+parse_apple(struct magic_set *ms, struct magic_entry *me, const char *line)
+{
+       struct magic *m = &me->mp[0];
+
+       return parse_extra(ms, me, line, offsetof(struct magic, apple),
+           sizeof(m->apple), "APPLE", 0);
+}
+
 /*
  * parse a MIME annotation line from magic file, put into magic[index - 1]
  * if valid
@@ -1666,33 +2125,10 @@ parse_apple(struct magic_set *ms, struct magic_entry *me, const char *line)
 private int
 parse_mime(struct magic_set *ms, struct magic_entry *me, const char *line)
 {
-       size_t i;
-       const char *l = line;
-       struct magic *m = &me->mp[me->cont_count == 0 ? 0 : me->cont_count - 1];
-
-       if (m->mimetype[0] != '\0') {
-               file_magwarn(ms, "Current entry already has a MIME type `%s',"
-                   " new type `%s'", m->mimetype, l);
-               return -1;
-       }       
-
-       EATAB;
-       for (i = 0; *l && ((isascii((unsigned char)*l) &&
-           isalnum((unsigned char)*l)) || strchr("-+/.", *l)) &&
-           i < sizeof(m->mimetype); m->mimetype[i++] = *l++)
-               continue;
-       if (i == sizeof(m->mimetype)) {
-               m->mimetype[sizeof(m->mimetype) - 1] = '\0';
-               if (ms->flags & MAGIC_CHECK)
-                       file_magwarn(ms, "MIME type `%s' truncated %"
-                           SIZE_T_FORMAT "u", m->mimetype, i);
-       } else
-               m->mimetype[i] = '\0';
+       struct magic *m = &me->mp[0];
 
-       if (i > 0)
-               return 0;
-       else
-               return -1;
+       return parse_extra(ms, me, line, offsetof(struct magic, mimetype),
+           sizeof(m->mimetype), "MIME", 1);
 }
 
 private int
@@ -1730,6 +2166,7 @@ check_format_type(const char *ptr, int type)
                        case 'i':
                        case 'd':
                        case 'u':
+                       case 'o':
                        case 'x':
                        case 'X':
                                return 0;
@@ -1744,6 +2181,7 @@ check_format_type(const char *ptr, int type)
                                case 'i':
                                case 'd':
                                case 'u':
+                               case 'o':
                                case 'x':
                                case 'X':
                                        return 0;
@@ -1760,6 +2198,7 @@ check_format_type(const char *ptr, int type)
                case 'c':
                case 'd':
                case 'u':
+               case 'o':
                case 'x':
                case 'X':
                        return 0;
@@ -1888,6 +2327,8 @@ getvalue(struct magic_set *ms, struct magic *m, const char **p, int action)
        case FILE_PSTRING:
        case FILE_REGEX:
        case FILE_SEARCH:
+       case FILE_NAME:
+       case FILE_USE:
                *p = getstr(ms, m, *p, action == FILE_COMPILE);
                if (*p == NULL) {
                        if (ms->flags & MAGIC_CHECK)
@@ -2197,60 +2638,68 @@ eatsize(const char **p)
 /*
  * handle a compiled file.
  */
-private int
-apprentice_map(struct magic_set *ms, struct magic **magicp, uint32_t *nmagicp,
-    const char *fn)
+
+private struct magic_map *
+apprentice_map(struct magic_set *ms, const char *fn)
 {
        int fd;
        struct stat st;
        uint32_t *ptr;
-       uint32_t version;
+       uint32_t version, entries, nentries;
        int needsbyteswap;
        char *dbname = NULL;
-       void *mm = NULL;
+       struct magic_map *map;
+       size_t i;
+
+       fd = -1;
+       if ((map = CAST(struct magic_map *, calloc(1, sizeof(*map)))) == NULL) {
+               file_oomem(ms, sizeof(*map));
+               goto error;
+       }
 
        dbname = mkdbname(ms, fn, 0);
        if (dbname == NULL)
-               goto error2;
+               goto error;
 
        if ((fd = open(dbname, O_RDONLY|O_BINARY)) == -1)
-               goto error2;
+               goto error;
 
        if (fstat(fd, &st) == -1) {
                file_error(ms, errno, "cannot stat `%s'", dbname);
-               goto error1;
+               goto error;
        }
-       if (st.st_size < 8) {
-               file_error(ms, 0, "file `%s' is too small", dbname);
-               goto error1;
+       if (st.st_size < 8 || st.st_size > MAXMAGIC_SIZE) {
+               file_error(ms, 0, "file `%s' is too %s", dbname,
+                   st.st_size < 8 ? "small" : "large");
+               goto error;
        }
 
+       map->len = (size_t)st.st_size;
 #ifdef QUICK
-       if ((mm = mmap(0, (size_t)st.st_size, PROT_READ|PROT_WRITE,
+       if ((map->p = mmap(0, (size_t)st.st_size, PROT_READ|PROT_WRITE,
            MAP_PRIVATE|MAP_FILE, fd, (off_t)0)) == MAP_FAILED) {
                file_error(ms, errno, "cannot map `%s'", dbname);
-               goto error1;
+               goto error;
        }
-#define RET    2
 #else
-       if ((mm = CAST(void *, malloc((size_t)st.st_size))) == NULL) {
-               file_oomem(ms, (size_t)st.st_size);
-               goto error1;
+       if ((map->p = CAST(void *, malloc(map->len))) == NULL) {
+               file_oomem(ms, map->len);
+               goto error;
        }
-       if (read(fd, mm, (size_t)st.st_size) != (ssize_t)st.st_size) {
+       if (read(fd, map->p, map->len) != (ssize_t)map->len) {
                file_badread(ms);
-               goto error1;
+               goto error;
        }
+       map->len = 0;
 #define RET    1
 #endif
-       *magicp = CAST(struct magic *, mm);
        (void)close(fd);
        fd = -1;
-       ptr = (uint32_t *)(void *)*magicp;
+       ptr = CAST(uint32_t *, map->p);
        if (*ptr != MAGICNO) {
                if (swap4(*ptr) != MAGICNO) {
                        file_error(ms, 0, "bad magic in `%s'", dbname);
-                       goto error1;
+                       goto error;
                }
                needsbyteswap = 1;
        } else
@@ -2263,55 +2712,70 @@ apprentice_map(struct magic_set *ms, struct magic **magicp, uint32_t *nmagicp,
                file_error(ms, 0, "File %s supports only version %d magic "
                    "files. `%s' is version %d", VERSION,
                    VERSIONNO, dbname, version);
-               goto error1;
+               goto error;
+       }
+       entries = (uint32_t)(st.st_size / sizeof(struct magic));
+       if ((off_t)(entries * sizeof(struct magic)) != st.st_size) {
+               file_error(ms, 0, "Size of `%s' %llu is not a multiple of %zu",
+                   dbname, (unsigned long long)st.st_size,
+                   sizeof(struct magic));
+               goto error;
+       }
+       map->magic[0] = CAST(struct magic *, map->p) + 1;
+       nentries = 0;
+       for (i = 0; i < MAGIC_SETS; i++) {
+               if (needsbyteswap)
+                       map->nmagic[i] = swap4(ptr[i + 2]);
+               else
+                       map->nmagic[i] = ptr[i + 2];
+               if (i != MAGIC_SETS - 1)
+                       map->magic[i + 1] = map->magic[i] + map->nmagic[i];
+               nentries += map->nmagic[i];
+       }
+       if (entries != nentries + 1) {
+               file_error(ms, 0, "Inconsistent entries in `%s' %u != %u",
+                   dbname, entries, nentries + 1);
+               goto error;
        }
-       *nmagicp = (uint32_t)(st.st_size / sizeof(struct magic));
-       if (*nmagicp > 0)
-               (*nmagicp)--;
-       (*magicp)++;
        if (needsbyteswap)
-               byteswap(*magicp, *nmagicp);
+               for (i = 0; i < MAGIC_SETS; i++)
+                       byteswap(map->magic[i], map->nmagic[i]);
        free(dbname);
-       return RET;
+       return map;
 
-error1:
+error:
        if (fd != -1)
                (void)close(fd);
-       if (mm) {
-#ifdef QUICK
-               (void)munmap((void *)mm, (size_t)st.st_size);
-#else
-               free(mm);
-#endif
-       } else {
-               *magicp = NULL;
-               *nmagicp = 0;
-       }
-error2:
+       apprentice_unmap(map);
        free(dbname);
-       return -1;
+       return NULL;
 }
 
 private const uint32_t ar[] = {
     MAGICNO, VERSIONNO
 };
+
 /*
  * handle an mmaped file.
  */
 private int
-apprentice_compile(struct magic_set *ms, struct magic **magicp,
-    uint32_t *nmagicp, const char *fn)
+apprentice_compile(struct magic_set *ms, struct magic_map *map, const char *fn)
 {
+       static const size_t nm = sizeof(*map->nmagic) * MAGIC_SETS;
+       static const size_t m = sizeof(**map->magic);
        int fd = -1;
+       size_t len;
        char *dbname;
        int rv = -1;
+       uint32_t i;
 
        dbname = mkdbname(ms, fn, 1);
 
        if (dbname == NULL) 
                goto out;
 
-       if ((fd = open(dbname, O_WRONLY|O_CREAT|O_TRUNC|O_BINARY, 0644)) == -1) {
+       if ((fd = open(dbname, O_WRONLY|O_CREAT|O_TRUNC|O_BINARY, 0644)) == -1) 
+       {
                file_error(ms, errno, "cannot open `%s'", dbname);
                goto out;
        }
@@ -2321,18 +2785,26 @@ apprentice_compile(struct magic_set *ms, struct magic **magicp,
                goto out;
        }
 
-       if (lseek(fd, (off_t)sizeof(struct magic), SEEK_SET)
-           != sizeof(struct magic)) {
-               file_error(ms, errno, "error seeking `%s'", dbname);
+       if (write(fd, map->nmagic, nm) != (ssize_t)nm) {
+               file_error(ms, errno, "error writing `%s'", dbname);
                goto out;
        }
 
-       if (write(fd, *magicp, (sizeof(struct magic) * *nmagicp)) 
-           != (ssize_t)(sizeof(struct magic) * *nmagicp)) {
-               file_error(ms, errno, "error writing `%s'", dbname);
+       assert(nm + sizeof(ar) < m);
+
+       if (lseek(fd, (off_t)m, SEEK_SET) != (off_t)m) {
+               file_error(ms, errno, "error seeking `%s'", dbname);
                goto out;
        }
 
+       for (i = 0; i < MAGIC_SETS; i++) {
+               len = m * map->nmagic[i];
+               if (write(fd, map->magic[i], len) != (ssize_t)len) {
+                       file_error(ms, errno, "error writing `%s'", dbname);
+                       goto out;
+               }
+       }
+
        if (fd != -1)
                (void)close(fd);
        rv = 0;
@@ -2371,14 +2843,16 @@ mkdbname(struct magic_set *ms, const char *fn, int strip)
        q++;
        /* Compatibility with old code that looked in .mime */
        if (ms->flags & MAGIC_MIME) {
-               asprintf(&buf, "%.*s.mime%s", (int)(q - fn), fn, ext);
+               if (asprintf(&buf, "%.*s.mime%s", (int)(q - fn), fn, ext) < 0)
+                       return NULL;
                if (access(buf, R_OK) != -1) {
                        ms->flags &= MAGIC_MIME_TYPE;
                        return buf;
                }
                free(buf);
        }
-       asprintf(&buf, "%.*s%s", (int)(q - fn), fn, ext);
+       if (asprintf(&buf, "%.*s%s", (int)(q - fn), fn, ext) < 0)
+               return NULL;
 
        /* Compatibility with old code that looked in .mime */
        if (strstr(p, ".mime") != NULL)
@@ -2525,3 +2999,30 @@ file_pstring_get_length(const struct magic *m, const char *s)
 
        return len;
 }
+
+protected int
+file_magicfind(struct magic_set *ms, const char *name, struct mlist *v)
+{
+       uint32_t i, j;
+       struct mlist *mlist, *ml;
+
+       mlist = ms->mlist[1];
+
+       for (ml = mlist->next; ml != mlist; ml = ml->next) {
+               struct magic *ma = ml->magic;
+               uint32_t nma = ml->nmagic;
+               for (i = 0; i < nma; i++) {
+                       if (ma[i].type != FILE_NAME)
+                               continue;
+                       if (strcmp(ma[i].value.s, name) == 0) {
+                               v->magic = &ma[i];
+                               for (j = i + 1; j < nma; j++)
+                                   if (ma[j].cont_level == 0)
+                                           break;
+                               v->nmagic = j - i;
+                               return 0;
+                       }
+               }
+       }
+       return -1;
+}