1284194Sdelphij/*
2284194Sdelphij * Copyright (c) Ian F. Darwin 1986-1995.
3284194Sdelphij * Software written by Ian F. Darwin and others;
4284194Sdelphij * maintained 1995-present by Christos Zoulas and others.
5284194Sdelphij *
6284194Sdelphij * Redistribution and use in source and binary forms, with or without
7284194Sdelphij * modification, are permitted provided that the following conditions
8284194Sdelphij * are met:
9284194Sdelphij * 1. Redistributions of source code must retain the above copyright
10284194Sdelphij *    notice immediately at the beginning of the file, without modification,
11284194Sdelphij *    this list of conditions, and the following disclaimer.
12284194Sdelphij * 2. Redistributions in binary form must reproduce the above copyright
13284194Sdelphij *    notice, this list of conditions and the following disclaimer in the
14284194Sdelphij *    documentation and/or other materials provided with the distribution.
15284194Sdelphij *
16284194Sdelphij * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17284194Sdelphij * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18284194Sdelphij * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19284194Sdelphij * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
20284194Sdelphij * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21284194Sdelphij * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22284194Sdelphij * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23284194Sdelphij * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24284194Sdelphij * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25284194Sdelphij * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26284194Sdelphij * SUCH DAMAGE.
27284194Sdelphij */
28284194Sdelphij/*
29284194Sdelphij * apprentice - make one pass through /etc/magic, learning its secrets.
30284194Sdelphij */
31284194Sdelphij
32284194Sdelphij#include "file.h"
33284194Sdelphij
34284194Sdelphij#ifndef	lint
35284194SdelphijFILE_RCSID("@(#)$File: apprentice.c,v 1.229 2015/01/01 17:07:34 christos Exp $")
36284194Sdelphij#endif	/* lint */
37284194Sdelphij
38284194Sdelphij#include "magic.h"
39284194Sdelphij#include <stdlib.h>
40284194Sdelphij#ifdef HAVE_UNISTD_H
41284194Sdelphij#include <unistd.h>
42284194Sdelphij#endif
43284194Sdelphij#ifdef HAVE_STDDEF_H
44284194Sdelphij#include <stddef.h>
45284194Sdelphij#endif
46284194Sdelphij#include <string.h>
47284194Sdelphij#include <assert.h>
48284194Sdelphij#include <ctype.h>
49284194Sdelphij#include <fcntl.h>
50284194Sdelphij#ifdef QUICK
51284194Sdelphij#include <sys/mman.h>
52284194Sdelphij#endif
53284194Sdelphij#include <dirent.h>
54284194Sdelphij#if defined(HAVE_LIMITS_H)
55284194Sdelphij#include <limits.h>
56284194Sdelphij#endif
57284194Sdelphij
58284194Sdelphij#ifndef SSIZE_MAX
59284194Sdelphij#define MAXMAGIC_SIZE        ((ssize_t)0x7fffffff)
60284194Sdelphij#else
61284194Sdelphij#define MAXMAGIC_SIZE        SSIZE_MAX
62284194Sdelphij#endif
63284194Sdelphij
64284194Sdelphij#define	EATAB {while (isascii((unsigned char) *l) && \
65284194Sdelphij		      isspace((unsigned char) *l))  ++l;}
66284194Sdelphij#define LOWCASE(l) (isupper((unsigned char) (l)) ? \
67284194Sdelphij			tolower((unsigned char) (l)) : (l))
68284194Sdelphij/*
69284194Sdelphij * Work around a bug in headers on Digital Unix.
70284194Sdelphij * At least confirmed for: OSF1 V4.0 878
71284194Sdelphij */
72284194Sdelphij#if defined(__osf__) && defined(__DECC)
73284194Sdelphij#ifdef MAP_FAILED
74284194Sdelphij#undef MAP_FAILED
75284194Sdelphij#endif
76284194Sdelphij#endif
77284194Sdelphij
78284194Sdelphij#ifndef MAP_FAILED
79284194Sdelphij#define MAP_FAILED (void *) -1
80284194Sdelphij#endif
81284194Sdelphij
82284194Sdelphij#ifndef MAP_FILE
83284194Sdelphij#define MAP_FILE 0
84284194Sdelphij#endif
85284194Sdelphij
86284194Sdelphij#define ALLOC_CHUNK	(size_t)10
87284194Sdelphij#define ALLOC_INCR	(size_t)200
88284194Sdelphij
89284194Sdelphij#define MAP_TYPE_MMAP	0
90284194Sdelphij#define MAP_TYPE_MALLOC	1
91284194Sdelphij#define MAP_TYPE_USER	2
92284194Sdelphij
93284194Sdelphijstruct magic_entry {
94284194Sdelphij	struct magic *mp;
95284194Sdelphij	uint32_t cont_count;
96284194Sdelphij	uint32_t max_count;
97284194Sdelphij};
98284194Sdelphij
99284194Sdelphijstruct magic_entry_set {
100284194Sdelphij	struct magic_entry *me;
101284194Sdelphij	uint32_t count;
102284194Sdelphij	uint32_t max;
103284194Sdelphij};
104284194Sdelphij
105284194Sdelphijstruct magic_map {
106284194Sdelphij	void *p;
107284194Sdelphij	size_t len;
108284194Sdelphij	int type;
109284194Sdelphij	struct magic *magic[MAGIC_SETS];
110284194Sdelphij	uint32_t nmagic[MAGIC_SETS];
111284194Sdelphij};
112284194Sdelphij
113284194Sdelphijint file_formats[FILE_NAMES_SIZE];
114284194Sdelphijconst size_t file_nformats = FILE_NAMES_SIZE;
115284194Sdelphijconst char *file_names[FILE_NAMES_SIZE];
116284194Sdelphijconst size_t file_nnames = FILE_NAMES_SIZE;
117284194Sdelphij
118284194Sdelphijprivate int getvalue(struct magic_set *ms, struct magic *, const char **, int);
119284194Sdelphijprivate int hextoint(int);
120284194Sdelphijprivate const char *getstr(struct magic_set *, struct magic *, const char *,
121284194Sdelphij    int);
122284194Sdelphijprivate int parse(struct magic_set *, struct magic_entry *, const char *,
123284194Sdelphij    size_t, int);
124284194Sdelphijprivate void eatsize(const char **);
125284194Sdelphijprivate int apprentice_1(struct magic_set *, const char *, int);
126284194Sdelphijprivate size_t apprentice_magic_strength(const struct magic *);
127284194Sdelphijprivate int apprentice_sort(const void *, const void *);
128284194Sdelphijprivate void apprentice_list(struct mlist *, int );
129284194Sdelphijprivate struct magic_map *apprentice_load(struct magic_set *,
130284194Sdelphij    const char *, int);
131284194Sdelphijprivate struct mlist *mlist_alloc(void);
132284194Sdelphijprivate void mlist_free(struct mlist *);
133284194Sdelphijprivate void byteswap(struct magic *, uint32_t);
134284194Sdelphijprivate void bs1(struct magic *);
135284194Sdelphijprivate uint16_t swap2(uint16_t);
136284194Sdelphijprivate uint32_t swap4(uint32_t);
137284194Sdelphijprivate uint64_t swap8(uint64_t);
138284194Sdelphijprivate char *mkdbname(struct magic_set *, const char *, int);
139284194Sdelphijprivate struct magic_map *apprentice_buf(struct magic_set *, struct magic *,
140284194Sdelphij    size_t);
141284194Sdelphijprivate struct magic_map *apprentice_map(struct magic_set *, const char *);
142284194Sdelphijprivate int check_buffer(struct magic_set *, struct magic_map *, const char *);
143284194Sdelphijprivate void apprentice_unmap(struct magic_map *);
144284194Sdelphijprivate int apprentice_compile(struct magic_set *, struct magic_map *,
145284194Sdelphij    const char *);
146284194Sdelphijprivate int check_format_type(const char *, int);
147284194Sdelphijprivate int check_format(struct magic_set *, struct magic *);
148284194Sdelphijprivate int get_op(char);
149284194Sdelphijprivate int parse_mime(struct magic_set *, struct magic_entry *, const char *);
150284194Sdelphijprivate int parse_strength(struct magic_set *, struct magic_entry *, const char *);
151284194Sdelphijprivate int parse_apple(struct magic_set *, struct magic_entry *, const char *);
152284194Sdelphij
153284194Sdelphij
154284194Sdelphijprivate size_t magicsize = sizeof(struct magic);
155284194Sdelphij
156284194Sdelphijprivate const char usg_hdr[] = "cont\toffset\ttype\topcode\tmask\tvalue\tdesc";
157284194Sdelphij
158284194Sdelphijprivate struct {
159284194Sdelphij	const char *name;
160284194Sdelphij	size_t len;
161284194Sdelphij	int (*fun)(struct magic_set *, struct magic_entry *, const char *);
162284194Sdelphij} bang[] = {
163284194Sdelphij#define	DECLARE_FIELD(name) { # name, sizeof(# name) - 1, parse_ ## name }
164284194Sdelphij	DECLARE_FIELD(mime),
165284194Sdelphij	DECLARE_FIELD(apple),
166284194Sdelphij	DECLARE_FIELD(strength),
167284194Sdelphij#undef	DECLARE_FIELD
168284194Sdelphij	{ NULL, 0, NULL }
169284194Sdelphij};
170284194Sdelphij
171284194Sdelphij#ifdef COMPILE_ONLY
172284194Sdelphij
173284194Sdelphijint main(int, char *[]);
174284194Sdelphij
175284194Sdelphijint
176284194Sdelphijmain(int argc, char *argv[])
177284194Sdelphij{
178284194Sdelphij	int ret;
179284194Sdelphij	struct magic_set *ms;
180284194Sdelphij	char *progname;
181284194Sdelphij
182284194Sdelphij	if ((progname = strrchr(argv[0], '/')) != NULL)
183284194Sdelphij		progname++;
184284194Sdelphij	else
185284194Sdelphij		progname = argv[0];
186284194Sdelphij
187284194Sdelphij	if (argc != 2) {
188284194Sdelphij		(void)fprintf(stderr, "Usage: %s file\n", progname);
189284194Sdelphij		return 1;
190284194Sdelphij	}
191284194Sdelphij
192284194Sdelphij	if ((ms = magic_open(MAGIC_CHECK)) == NULL) {
193284194Sdelphij		(void)fprintf(stderr, "%s: %s\n", progname, strerror(errno));
194284194Sdelphij		return 1;
195284194Sdelphij	}
196284194Sdelphij	ret = magic_compile(ms, argv[1]) == -1 ? 1 : 0;
197284194Sdelphij	if (ret == 1)
198284194Sdelphij		(void)fprintf(stderr, "%s: %s\n", progname, magic_error(ms));
199284194Sdelphij	magic_close(ms);
200284194Sdelphij	return ret;
201284194Sdelphij}
202284194Sdelphij#endif /* COMPILE_ONLY */
203284194Sdelphij
204284194Sdelphijstruct type_tbl_s {
205284194Sdelphij	const char name[16];
206284194Sdelphij	const size_t len;
207284194Sdelphij	const int type;
208284194Sdelphij	const int format;
209284194Sdelphij};
210284194Sdelphij
211284194Sdelphij/*
212284194Sdelphij * XXX - the actual Single UNIX Specification says that "long" means "long",
213284194Sdelphij * as in the C data type, but we treat it as meaning "4-byte integer".
214284194Sdelphij * Given that the OS X version of file 5.04 did the same, I guess that passes
215284194Sdelphij * the actual test; having "long" be dependent on how big a "long" is on
216284194Sdelphij * the machine running "file" is silly.
217284194Sdelphij */
218284194Sdelphijstatic const struct type_tbl_s type_tbl[] = {
219284194Sdelphij# define XX(s)		s, (sizeof(s) - 1)
220284194Sdelphij# define XX_NULL	"", 0
221284194Sdelphij	{ XX("invalid"),	FILE_INVALID,		FILE_FMT_NONE },
222284194Sdelphij	{ XX("byte"),		FILE_BYTE,		FILE_FMT_NUM },
223284194Sdelphij	{ XX("short"),		FILE_SHORT,		FILE_FMT_NUM },
224284194Sdelphij	{ XX("default"),	FILE_DEFAULT,		FILE_FMT_NONE },
225284194Sdelphij	{ XX("long"),		FILE_LONG,		FILE_FMT_NUM },
226284194Sdelphij	{ XX("string"),		FILE_STRING,		FILE_FMT_STR },
227284194Sdelphij	{ XX("date"),		FILE_DATE,		FILE_FMT_STR },
228284194Sdelphij	{ XX("beshort"),	FILE_BESHORT,		FILE_FMT_NUM },
229284194Sdelphij	{ XX("belong"),		FILE_BELONG,		FILE_FMT_NUM },
230284194Sdelphij	{ XX("bedate"),		FILE_BEDATE,		FILE_FMT_STR },
231284194Sdelphij	{ XX("leshort"),	FILE_LESHORT,		FILE_FMT_NUM },
232284194Sdelphij	{ XX("lelong"),		FILE_LELONG,		FILE_FMT_NUM },
233284194Sdelphij	{ XX("ledate"),		FILE_LEDATE,		FILE_FMT_STR },
234284194Sdelphij	{ XX("pstring"),	FILE_PSTRING,		FILE_FMT_STR },
235284194Sdelphij	{ XX("ldate"),		FILE_LDATE,		FILE_FMT_STR },
236284194Sdelphij	{ XX("beldate"),	FILE_BELDATE,		FILE_FMT_STR },
237284194Sdelphij	{ XX("leldate"),	FILE_LELDATE,		FILE_FMT_STR },
238284194Sdelphij	{ XX("regex"),		FILE_REGEX,		FILE_FMT_STR },
239284194Sdelphij	{ XX("bestring16"),	FILE_BESTRING16,	FILE_FMT_STR },
240284194Sdelphij	{ XX("lestring16"),	FILE_LESTRING16,	FILE_FMT_STR },
241284194Sdelphij	{ XX("search"),		FILE_SEARCH,		FILE_FMT_STR },
242284194Sdelphij	{ XX("medate"),		FILE_MEDATE,		FILE_FMT_STR },
243284194Sdelphij	{ XX("meldate"),	FILE_MELDATE,		FILE_FMT_STR },
244284194Sdelphij	{ XX("melong"),		FILE_MELONG,		FILE_FMT_NUM },
245284194Sdelphij	{ XX("quad"),		FILE_QUAD,		FILE_FMT_QUAD },
246284194Sdelphij	{ XX("lequad"),		FILE_LEQUAD,		FILE_FMT_QUAD },
247284194Sdelphij	{ XX("bequad"),		FILE_BEQUAD,		FILE_FMT_QUAD },
248284194Sdelphij	{ XX("qdate"),		FILE_QDATE,		FILE_FMT_STR },
249284194Sdelphij	{ XX("leqdate"),	FILE_LEQDATE,		FILE_FMT_STR },
250284194Sdelphij	{ XX("beqdate"),	FILE_BEQDATE,		FILE_FMT_STR },
251284194Sdelphij	{ XX("qldate"),		FILE_QLDATE,		FILE_FMT_STR },
252284194Sdelphij	{ XX("leqldate"),	FILE_LEQLDATE,		FILE_FMT_STR },
253284194Sdelphij	{ XX("beqldate"),	FILE_BEQLDATE,		FILE_FMT_STR },
254284194Sdelphij	{ XX("float"),		FILE_FLOAT,		FILE_FMT_FLOAT },
255284194Sdelphij	{ XX("befloat"),	FILE_BEFLOAT,		FILE_FMT_FLOAT },
256284194Sdelphij	{ XX("lefloat"),	FILE_LEFLOAT,		FILE_FMT_FLOAT },
257284194Sdelphij	{ XX("double"),		FILE_DOUBLE,		FILE_FMT_DOUBLE },
258284194Sdelphij	{ XX("bedouble"),	FILE_BEDOUBLE,		FILE_FMT_DOUBLE },
259284194Sdelphij	{ XX("ledouble"),	FILE_LEDOUBLE,		FILE_FMT_DOUBLE },
260284194Sdelphij	{ XX("leid3"),		FILE_LEID3,		FILE_FMT_NUM },
261284194Sdelphij	{ XX("beid3"),		FILE_BEID3,		FILE_FMT_NUM },
262284194Sdelphij	{ XX("indirect"),	FILE_INDIRECT,		FILE_FMT_NUM },
263284194Sdelphij	{ XX("qwdate"),		FILE_QWDATE,		FILE_FMT_STR },
264284194Sdelphij	{ XX("leqwdate"),	FILE_LEQWDATE,		FILE_FMT_STR },
265284194Sdelphij	{ XX("beqwdate"),	FILE_BEQWDATE,		FILE_FMT_STR },
266284194Sdelphij	{ XX("name"),		FILE_NAME,		FILE_FMT_NONE },
267284194Sdelphij	{ XX("use"),		FILE_USE,		FILE_FMT_NONE },
268284194Sdelphij	{ XX("clear"),		FILE_CLEAR,		FILE_FMT_NONE },
269284194Sdelphij	{ XX_NULL,		FILE_INVALID,		FILE_FMT_NONE },
270284194Sdelphij};
271284194Sdelphij
272284194Sdelphij/*
273284194Sdelphij * These are not types, and cannot be preceded by "u" to make them
274284194Sdelphij * unsigned.
275284194Sdelphij */
276284194Sdelphijstatic const struct type_tbl_s special_tbl[] = {
277284194Sdelphij	{ XX("name"),		FILE_NAME,		FILE_FMT_STR },
278284194Sdelphij	{ XX("use"),		FILE_USE,		FILE_FMT_STR },
279284194Sdelphij	{ XX_NULL,		FILE_INVALID,		FILE_FMT_NONE },
280284194Sdelphij};
281284194Sdelphij# undef XX
282284194Sdelphij# undef XX_NULL
283284194Sdelphij
284284194Sdelphijprivate int
285284194Sdelphijget_type(const struct type_tbl_s *tbl, const char *l, const char **t)
286284194Sdelphij{
287284194Sdelphij	const struct type_tbl_s *p;
288284194Sdelphij
289284194Sdelphij	for (p = tbl; p->len; p++) {
290284194Sdelphij		if (strncmp(l, p->name, p->len) == 0) {
291284194Sdelphij			if (t)
292284194Sdelphij				*t = l + p->len;
293284194Sdelphij			break;
294284194Sdelphij		}
295284194Sdelphij	}
296284194Sdelphij	return p->type;
297284194Sdelphij}
298284194Sdelphij
299284194Sdelphijprivate int
300284194Sdelphijget_standard_integer_type(const char *l, const char **t)
301284194Sdelphij{
302284194Sdelphij	int type;
303284194Sdelphij
304284194Sdelphij	if (isalpha((unsigned char)l[1])) {
305284194Sdelphij		switch (l[1]) {
306284194Sdelphij		case 'C':
307284194Sdelphij			/* "dC" and "uC" */
308284194Sdelphij			type = FILE_BYTE;
309284194Sdelphij			break;
310284194Sdelphij		case 'S':
311284194Sdelphij			/* "dS" and "uS" */
312284194Sdelphij			type = FILE_SHORT;
313284194Sdelphij			break;
314284194Sdelphij		case 'I':
315284194Sdelphij		case 'L':
316284194Sdelphij			/*
317284194Sdelphij			 * "dI", "dL", "uI", and "uL".
318284194Sdelphij			 *
319284194Sdelphij			 * XXX - the actual Single UNIX Specification says
320284194Sdelphij			 * that "L" means "long", as in the C data type,
321284194Sdelphij			 * but we treat it as meaning "4-byte integer".
322284194Sdelphij			 * Given that the OS X version of file 5.04 did
323284194Sdelphij			 * the same, I guess that passes the actual SUS
324284194Sdelphij			 * validation suite; having "dL" be dependent on
325284194Sdelphij			 * how big a "long" is on the machine running
326284194Sdelphij			 * "file" is silly.
327284194Sdelphij			 */
328284194Sdelphij			type = FILE_LONG;
329284194Sdelphij			break;
330284194Sdelphij		case 'Q':
331284194Sdelphij			/* "dQ" and "uQ" */
332284194Sdelphij			type = FILE_QUAD;
333284194Sdelphij			break;
334284194Sdelphij		default:
335284194Sdelphij			/* "d{anything else}", "u{anything else}" */
336284194Sdelphij			return FILE_INVALID;
337284194Sdelphij		}
338284194Sdelphij		l += 2;
339284194Sdelphij	} else if (isdigit((unsigned char)l[1])) {
340284194Sdelphij		/*
341284194Sdelphij		 * "d{num}" and "u{num}"; we only support {num} values
342284194Sdelphij		 * of 1, 2, 4, and 8 - the Single UNIX Specification
343284194Sdelphij		 * doesn't say anything about whether arbitrary
344284194Sdelphij		 * values should be supported, but both the Solaris 10
345284194Sdelphij		 * and OS X Mountain Lion versions of file passed the
346284194Sdelphij		 * Single UNIX Specification validation suite, and
347284194Sdelphij		 * neither of them support values bigger than 8 or
348284194Sdelphij		 * non-power-of-2 values.
349284194Sdelphij		 */
350284194Sdelphij		if (isdigit((unsigned char)l[2])) {
351284194Sdelphij			/* Multi-digit, so > 9 */
352284194Sdelphij			return FILE_INVALID;
353284194Sdelphij		}
354284194Sdelphij		switch (l[1]) {
355284194Sdelphij		case '1':
356284194Sdelphij			type = FILE_BYTE;
357284194Sdelphij			break;
358284194Sdelphij		case '2':
359284194Sdelphij			type = FILE_SHORT;
360284194Sdelphij			break;
361284194Sdelphij		case '4':
362284194Sdelphij			type = FILE_LONG;
363284194Sdelphij			break;
364284194Sdelphij		case '8':
365284194Sdelphij			type = FILE_QUAD;
366284194Sdelphij			break;
367284194Sdelphij		default:
368284194Sdelphij			/* XXX - what about 3, 5, 6, or 7? */
369284194Sdelphij			return FILE_INVALID;
370284194Sdelphij		}
371284194Sdelphij		l += 2;
372284194Sdelphij	} else {
373284194Sdelphij		/*
374284194Sdelphij		 * "d" or "u" by itself.
375284194Sdelphij		 */
376284194Sdelphij		type = FILE_LONG;
377284194Sdelphij		++l;
378284194Sdelphij	}
379284194Sdelphij	if (t)
380284194Sdelphij		*t = l;
381284194Sdelphij	return type;
382284194Sdelphij}
383284194Sdelphij
384284194Sdelphijprivate void
385284194Sdelphijinit_file_tables(void)
386284194Sdelphij{
387284194Sdelphij	static int done = 0;
388284194Sdelphij	const struct type_tbl_s *p;
389284194Sdelphij
390284194Sdelphij	if (done)
391284194Sdelphij		return;
392284194Sdelphij	done++;
393284194Sdelphij
394284194Sdelphij	for (p = type_tbl; p->len; p++) {
395284194Sdelphij		assert(p->type < FILE_NAMES_SIZE);
396284194Sdelphij		file_names[p->type] = p->name;
397284194Sdelphij		file_formats[p->type] = p->format;
398284194Sdelphij	}
399284194Sdelphij	assert(p - type_tbl == FILE_NAMES_SIZE);
400284194Sdelphij}
401284194Sdelphij
402284194Sdelphijprivate int
403284194Sdelphijadd_mlist(struct mlist *mlp, struct magic_map *map, size_t idx)
404284194Sdelphij{
405284194Sdelphij	struct mlist *ml;
406284194Sdelphij
407284194Sdelphij	mlp->map = idx == 0 ? map : NULL;
408284194Sdelphij	if ((ml = CAST(struct mlist *, malloc(sizeof(*ml)))) == NULL)
409284194Sdelphij		return -1;
410284194Sdelphij
411284194Sdelphij	ml->map = NULL;
412284194Sdelphij	ml->magic = map->magic[idx];
413284194Sdelphij	ml->nmagic = map->nmagic[idx];
414284194Sdelphij
415284194Sdelphij	mlp->prev->next = ml;
416284194Sdelphij	ml->prev = mlp->prev;
417284194Sdelphij	ml->next = mlp;
418284194Sdelphij	mlp->prev = ml;
419284194Sdelphij	return 0;
420284194Sdelphij}
421284194Sdelphij
422284194Sdelphij/*
423284194Sdelphij * Handle one file or directory.
424284194Sdelphij */
425284194Sdelphijprivate int
426284194Sdelphijapprentice_1(struct magic_set *ms, const char *fn, int action)
427284194Sdelphij{
428284194Sdelphij	struct magic_map *map;
429284194Sdelphij#ifndef COMPILE_ONLY
430284194Sdelphij	struct mlist *ml;
431284194Sdelphij	size_t i;
432284194Sdelphij#endif
433284194Sdelphij
434284194Sdelphij	if (magicsize != FILE_MAGICSIZE) {
435284194Sdelphij		file_error(ms, 0, "magic element size %lu != %lu",
436284194Sdelphij		    (unsigned long)sizeof(*map->magic[0]),
437284194Sdelphij		    (unsigned long)FILE_MAGICSIZE);
438284194Sdelphij		return -1;
439284194Sdelphij	}
440284194Sdelphij
441284194Sdelphij	if (action == FILE_COMPILE) {
442284194Sdelphij		map = apprentice_load(ms, fn, action);
443284194Sdelphij		if (map == NULL)
444284194Sdelphij			return -1;
445284194Sdelphij		return apprentice_compile(ms, map, fn);
446284194Sdelphij	}
447284194Sdelphij
448284194Sdelphij#ifndef COMPILE_ONLY
449284194Sdelphij	map = apprentice_map(ms, fn);
450284194Sdelphij	if (map == NULL) {
451284194Sdelphij		if (ms->flags & MAGIC_CHECK)
452284194Sdelphij			file_magwarn(ms, "using regular magic file `%s'", fn);
453284194Sdelphij		map = apprentice_load(ms, fn, action);
454284194Sdelphij		if (map == NULL)
455284194Sdelphij			return -1;
456284194Sdelphij	}
457284194Sdelphij
458284194Sdelphij	for (i = 0; i < MAGIC_SETS; i++) {
459284194Sdelphij		if (add_mlist(ms->mlist[i], map, i) == -1) {
460284194Sdelphij			file_oomem(ms, sizeof(*ml));
461284194Sdelphij			goto fail;
462284194Sdelphij		}
463284194Sdelphij	}
464284194Sdelphij
465284194Sdelphij	if (action == FILE_LIST) {
466284194Sdelphij		for (i = 0; i < MAGIC_SETS; i++) {
467284194Sdelphij			printf("Set %" SIZE_T_FORMAT "u:\nBinary patterns:\n",
468284194Sdelphij			    i);
469284194Sdelphij			apprentice_list(ms->mlist[i], BINTEST);
470284194Sdelphij			printf("Text patterns:\n");
471284194Sdelphij			apprentice_list(ms->mlist[i], TEXTTEST);
472284194Sdelphij		}
473284194Sdelphij	}
474284194Sdelphij	return 0;
475284194Sdelphijfail:
476284194Sdelphij	for (i = 0; i < MAGIC_SETS; i++) {
477284194Sdelphij		mlist_free(ms->mlist[i]);
478284194Sdelphij		ms->mlist[i] = NULL;
479284194Sdelphij	}
480284194Sdelphij	return -1;
481284194Sdelphij#else
482284194Sdelphij	return 0;
483284194Sdelphij#endif /* COMPILE_ONLY */
484284194Sdelphij}
485284194Sdelphij
486284194Sdelphijprotected void
487284194Sdelphijfile_ms_free(struct magic_set *ms)
488284194Sdelphij{
489284194Sdelphij	size_t i;
490284194Sdelphij	if (ms == NULL)
491284194Sdelphij		return;
492284194Sdelphij	for (i = 0; i < MAGIC_SETS; i++)
493284194Sdelphij		mlist_free(ms->mlist[i]);
494284194Sdelphij	free(ms->o.pbuf);
495284194Sdelphij	free(ms->o.buf);
496284194Sdelphij	free(ms->c.li);
497284194Sdelphij	free(ms);
498284194Sdelphij}
499284194Sdelphij
500284194Sdelphijprotected struct magic_set *
501284194Sdelphijfile_ms_alloc(int flags)
502284194Sdelphij{
503284194Sdelphij	struct magic_set *ms;
504284194Sdelphij	size_t i, len;
505284194Sdelphij
506284194Sdelphij	if ((ms = CAST(struct magic_set *, calloc((size_t)1,
507284194Sdelphij	    sizeof(struct magic_set)))) == NULL)
508284194Sdelphij		return NULL;
509284194Sdelphij
510284194Sdelphij	if (magic_setflags(ms, flags) == -1) {
511284194Sdelphij		errno = EINVAL;
512284194Sdelphij		goto free;
513284194Sdelphij	}
514284194Sdelphij
515284194Sdelphij	ms->o.buf = ms->o.pbuf = NULL;
516284194Sdelphij	len = (ms->c.len = 10) * sizeof(*ms->c.li);
517284194Sdelphij
518284194Sdelphij	if ((ms->c.li = CAST(struct level_info *, malloc(len))) == NULL)
519284194Sdelphij		goto free;
520284194Sdelphij
521284194Sdelphij	ms->event_flags = 0;
522284194Sdelphij	ms->error = -1;
523284194Sdelphij	for (i = 0; i < MAGIC_SETS; i++)
524284194Sdelphij		ms->mlist[i] = NULL;
525284194Sdelphij	ms->file = "unknown";
526284194Sdelphij	ms->line = 0;
527284194Sdelphij	ms->indir_max = FILE_INDIR_MAX;
528284194Sdelphij	ms->name_max = FILE_NAME_MAX;
529284194Sdelphij	ms->elf_shnum_max = FILE_ELF_SHNUM_MAX;
530284194Sdelphij	ms->elf_phnum_max = FILE_ELF_PHNUM_MAX;
531284194Sdelphij	ms->elf_notes_max = FILE_ELF_NOTES_MAX;
532284194Sdelphij	return ms;
533284194Sdelphijfree:
534284194Sdelphij	free(ms);
535284194Sdelphij	return NULL;
536284194Sdelphij}
537284194Sdelphij
538284194Sdelphijprivate void
539284194Sdelphijapprentice_unmap(struct magic_map *map)
540284194Sdelphij{
541284194Sdelphij	if (map == NULL)
542284194Sdelphij		return;
543284194Sdelphij
544284194Sdelphij	switch (map->type) {
545284194Sdelphij#ifdef QUICK
546284194Sdelphij	case MAP_TYPE_MMAP:
547284194Sdelphij		if (map->p)
548284194Sdelphij			(void)munmap(map->p, map->len);
549284194Sdelphij		break;
550284194Sdelphij#endif
551284194Sdelphij	case MAP_TYPE_MALLOC:
552284194Sdelphij		free(map->p);
553284194Sdelphij		break;
554284194Sdelphij	case MAP_TYPE_USER:
555284194Sdelphij		break;
556284194Sdelphij	default:
557284194Sdelphij		abort();
558284194Sdelphij	}
559284194Sdelphij	free(map);
560284194Sdelphij}
561284194Sdelphij
562284194Sdelphijprivate struct mlist *
563284194Sdelphijmlist_alloc(void)
564284194Sdelphij{
565284194Sdelphij	struct mlist *mlist;
566284194Sdelphij	if ((mlist = CAST(struct mlist *, calloc(1, sizeof(*mlist)))) == NULL) {
567284194Sdelphij		return NULL;
568284194Sdelphij	}
569284194Sdelphij	mlist->next = mlist->prev = mlist;
570284194Sdelphij	return mlist;
571284194Sdelphij}
572284194Sdelphij
573284194Sdelphijprivate void
574284194Sdelphijmlist_free(struct mlist *mlist)
575284194Sdelphij{
576284194Sdelphij	struct mlist *ml, *next;
577284194Sdelphij
578284194Sdelphij	if (mlist == NULL)
579284194Sdelphij		return;
580284194Sdelphij
581284194Sdelphij	ml = mlist->next;
582284194Sdelphij	for (ml = mlist->next; (next = ml->next) != NULL; ml = next) {
583284194Sdelphij		if (ml->map)
584284194Sdelphij			apprentice_unmap(ml->map);
585284194Sdelphij		free(ml);
586284194Sdelphij		if (ml == mlist)
587284194Sdelphij			break;
588284194Sdelphij	}
589284194Sdelphij}
590284194Sdelphij
591284194Sdelphij#ifndef COMPILE_ONLY
592284194Sdelphij/* void **bufs: an array of compiled magic files */
593284194Sdelphijprotected int
594284194Sdelphijbuffer_apprentice(struct magic_set *ms, struct magic **bufs,
595284194Sdelphij    size_t *sizes, size_t nbufs)
596284194Sdelphij{
597284194Sdelphij	size_t i, j;
598284194Sdelphij	struct mlist *ml;
599284194Sdelphij	struct magic_map *map;
600284194Sdelphij
601284194Sdelphij	if (nbufs == 0)
602284194Sdelphij		return -1;
603284194Sdelphij
604284194Sdelphij	if (ms->mlist[0] != NULL)
605284194Sdelphij		file_reset(ms);
606284194Sdelphij
607284194Sdelphij	init_file_tables();
608284194Sdelphij
609284194Sdelphij	for (i = 0; i < MAGIC_SETS; i++) {
610284194Sdelphij		mlist_free(ms->mlist[i]);
611284194Sdelphij		if ((ms->mlist[i] = mlist_alloc()) == NULL) {
612284194Sdelphij			file_oomem(ms, sizeof(*ms->mlist[i]));
613284194Sdelphij			goto fail;
614284194Sdelphij		}
615284194Sdelphij	}
616284194Sdelphij
617284194Sdelphij	for (i = 0; i < nbufs; i++) {
618284194Sdelphij		map = apprentice_buf(ms, bufs[i], sizes[i]);
619284194Sdelphij		if (map == NULL)
620284194Sdelphij			goto fail;
621284194Sdelphij
622284194Sdelphij		for (j = 0; j < MAGIC_SETS; j++) {
623284194Sdelphij			if (add_mlist(ms->mlist[j], map, j) == -1) {
624284194Sdelphij				file_oomem(ms, sizeof(*ml));
625284194Sdelphij				goto fail;
626284194Sdelphij			}
627284194Sdelphij		}
628284194Sdelphij	}
629284194Sdelphij
630284194Sdelphij	return 0;
631284194Sdelphijfail:
632284194Sdelphij	for (i = 0; i < MAGIC_SETS; i++) {
633284194Sdelphij		mlist_free(ms->mlist[i]);
634284194Sdelphij		ms->mlist[i] = NULL;
635284194Sdelphij	}
636284194Sdelphij	return -1;
637284194Sdelphij}
638284194Sdelphij#endif
639284194Sdelphij
640284194Sdelphij/* const char *fn: list of magic files and directories */
641284194Sdelphijprotected int
642284194Sdelphijfile_apprentice(struct magic_set *ms, const char *fn, int action)
643284194Sdelphij{
644284194Sdelphij	char *p, *mfn;
645284194Sdelphij	int file_err, errs = -1;
646284194Sdelphij	size_t i;
647284194Sdelphij
648284194Sdelphij	if (ms->mlist[0] != NULL)
649284194Sdelphij		file_reset(ms);
650284194Sdelphij
651284194Sdelphij	if ((fn = magic_getpath(fn, action)) == NULL)
652284194Sdelphij		return -1;
653284194Sdelphij
654284194Sdelphij	init_file_tables();
655284194Sdelphij
656284194Sdelphij	if ((mfn = strdup(fn)) == NULL) {
657284194Sdelphij		file_oomem(ms, strlen(fn));
658284194Sdelphij		return -1;
659284194Sdelphij	}
660284194Sdelphij
661284194Sdelphij	for (i = 0; i < MAGIC_SETS; i++) {
662284194Sdelphij		mlist_free(ms->mlist[i]);
663284194Sdelphij		if ((ms->mlist[i] = mlist_alloc()) == NULL) {
664284194Sdelphij			file_oomem(ms, sizeof(*ms->mlist[i]));
665284194Sdelphij			while (i-- > 0) {
666284194Sdelphij				mlist_free(ms->mlist[i]);
667284194Sdelphij				ms->mlist[i] = NULL;
668284194Sdelphij			}
669284194Sdelphij			free(mfn);
670284194Sdelphij			return -1;
671284194Sdelphij		}
672284194Sdelphij	}
673284194Sdelphij	fn = mfn;
674284194Sdelphij
675284194Sdelphij	while (fn) {
676284194Sdelphij		p = strchr(fn, PATHSEP);
677284194Sdelphij		if (p)
678284194Sdelphij			*p++ = '\0';
679284194Sdelphij		if (*fn == '\0')
680284194Sdelphij			break;
681284194Sdelphij		file_err = apprentice_1(ms, fn, action);
682284194Sdelphij		errs = MAX(errs, file_err);
683284194Sdelphij		fn = p;
684284194Sdelphij	}
685284194Sdelphij
686284194Sdelphij	free(mfn);
687284194Sdelphij
688284194Sdelphij	if (errs == -1) {
689284194Sdelphij		for (i = 0; i < MAGIC_SETS; i++) {
690284194Sdelphij			mlist_free(ms->mlist[i]);
691284194Sdelphij			ms->mlist[i] = NULL;
692284194Sdelphij		}
693284194Sdelphij		file_error(ms, 0, "could not find any valid magic files!");
694284194Sdelphij		return -1;
695284194Sdelphij	}
696284194Sdelphij
697284194Sdelphij#if 0
698284194Sdelphij	/*
699284194Sdelphij	 * Always leave the database loaded
700284194Sdelphij	 */
701284194Sdelphij	if (action == FILE_LOAD)
702284194Sdelphij		return 0;
703284194Sdelphij
704284194Sdelphij	for (i = 0; i < MAGIC_SETS; i++) {
705284194Sdelphij		mlist_free(ms->mlist[i]);
706284194Sdelphij		ms->mlist[i] = NULL;
707284194Sdelphij	}
708284194Sdelphij#endif
709284194Sdelphij
710284194Sdelphij	switch (action) {
711284194Sdelphij	case FILE_LOAD:
712284194Sdelphij	case FILE_COMPILE:
713284194Sdelphij	case FILE_CHECK:
714284194Sdelphij	case FILE_LIST:
715284194Sdelphij		return 0;
716284194Sdelphij	default:
717284194Sdelphij		file_error(ms, 0, "Invalid action %d", action);
718284194Sdelphij		return -1;
719284194Sdelphij	}
720284194Sdelphij}
721284194Sdelphij
722284194Sdelphij/*
723284194Sdelphij * Compute the real length of a magic expression, for the purposes
724284194Sdelphij * of determining how "strong" a magic expression is (approximating
725284194Sdelphij * how specific its matches are):
726284194Sdelphij *	- magic characters count 0 unless escaped.
727284194Sdelphij *	- [] expressions count 1
728284194Sdelphij *	- {} expressions count 0
729284194Sdelphij *	- regular characters or escaped magic characters count 1
730284194Sdelphij *	- 0 length expressions count as one
731284194Sdelphij */
732284194Sdelphijprivate size_t
733284194Sdelphijnonmagic(const char *str)
734284194Sdelphij{
735284194Sdelphij	const char *p;
736284194Sdelphij	size_t rv = 0;
737284194Sdelphij
738284194Sdelphij	for (p = str; *p; p++)
739284194Sdelphij		switch (*p) {
740284194Sdelphij		case '\\':	/* Escaped anything counts 1 */
741284194Sdelphij			if (!*++p)
742284194Sdelphij				p--;
743284194Sdelphij			rv++;
744284194Sdelphij			continue;
745284194Sdelphij		case '?':	/* Magic characters count 0 */
746284194Sdelphij		case '*':
747284194Sdelphij		case '.':
748284194Sdelphij		case '+':
749284194Sdelphij		case '^':
750284194Sdelphij		case '$':
751284194Sdelphij			continue;
752284194Sdelphij		case '[':	/* Bracketed expressions count 1 the ']' */
753284194Sdelphij			while (*p && *p != ']')
754284194Sdelphij				p++;
755284194Sdelphij			p--;
756284194Sdelphij			continue;
757284194Sdelphij		case '{':	/* Braced expressions count 0 */
758284194Sdelphij			while (*p && *p != '}')
759284194Sdelphij				p++;
760284194Sdelphij			if (!*p)
761284194Sdelphij				p--;
762284194Sdelphij			continue;
763284194Sdelphij		default:	/* Anything else counts 1 */
764284194Sdelphij			rv++;
765284194Sdelphij			continue;
766284194Sdelphij		}
767284194Sdelphij
768284194Sdelphij	return rv == 0 ? 1 : rv;	/* Return at least 1 */
769284194Sdelphij}
770284194Sdelphij
771284194Sdelphij/*
772284194Sdelphij * Get weight of this magic entry, for sorting purposes.
773284194Sdelphij */
774284194Sdelphijprivate size_t
775284194Sdelphijapprentice_magic_strength(const struct magic *m)
776284194Sdelphij{
777284194Sdelphij#define MULT 10
778284194Sdelphij	size_t v, val = 2 * MULT;	/* baseline strength */
779284194Sdelphij
780284194Sdelphij	switch (m->type) {
781284194Sdelphij	case FILE_DEFAULT:	/* make sure this sorts last */
782284194Sdelphij		if (m->factor_op != FILE_FACTOR_OP_NONE)
783284194Sdelphij			abort();
784284194Sdelphij		return 0;
785284194Sdelphij
786284194Sdelphij	case FILE_BYTE:
787284194Sdelphij		val += 1 * MULT;
788284194Sdelphij		break;
789284194Sdelphij
790284194Sdelphij	case FILE_SHORT:
791284194Sdelphij	case FILE_LESHORT:
792284194Sdelphij	case FILE_BESHORT:
793284194Sdelphij		val += 2 * MULT;
794284194Sdelphij		break;
795284194Sdelphij
796284194Sdelphij	case FILE_LONG:
797284194Sdelphij	case FILE_LELONG:
798284194Sdelphij	case FILE_BELONG:
799284194Sdelphij	case FILE_MELONG:
800284194Sdelphij		val += 4 * MULT;
801284194Sdelphij		break;
802284194Sdelphij
803284194Sdelphij	case FILE_PSTRING:
804284194Sdelphij	case FILE_STRING:
805284194Sdelphij		val += m->vallen * MULT;
806284194Sdelphij		break;
807284194Sdelphij
808284194Sdelphij	case FILE_BESTRING16:
809284194Sdelphij	case FILE_LESTRING16:
810284194Sdelphij		val += m->vallen * MULT / 2;
811284194Sdelphij		break;
812284194Sdelphij
813284194Sdelphij	case FILE_SEARCH:
814284194Sdelphij		val += m->vallen * MAX(MULT / m->vallen, 1);
815284194Sdelphij		break;
816284194Sdelphij
817284194Sdelphij	case FILE_REGEX:
818284194Sdelphij		v = nonmagic(m->value.s);
819284194Sdelphij		val += v * MAX(MULT / v, 1);
820284194Sdelphij		break;
821284194Sdelphij
822284194Sdelphij	case FILE_DATE:
823284194Sdelphij	case FILE_LEDATE:
824284194Sdelphij	case FILE_BEDATE:
825284194Sdelphij	case FILE_MEDATE:
826284194Sdelphij	case FILE_LDATE:
827284194Sdelphij	case FILE_LELDATE:
828284194Sdelphij	case FILE_BELDATE:
829284194Sdelphij	case FILE_MELDATE:
830284194Sdelphij	case FILE_FLOAT:
831284194Sdelphij	case FILE_BEFLOAT:
832284194Sdelphij	case FILE_LEFLOAT:
833284194Sdelphij		val += 4 * MULT;
834284194Sdelphij		break;
835284194Sdelphij
836284194Sdelphij	case FILE_QUAD:
837284194Sdelphij	case FILE_BEQUAD:
838284194Sdelphij	case FILE_LEQUAD:
839284194Sdelphij	case FILE_QDATE:
840284194Sdelphij	case FILE_LEQDATE:
841284194Sdelphij	case FILE_BEQDATE:
842284194Sdelphij	case FILE_QLDATE:
843284194Sdelphij	case FILE_LEQLDATE:
844284194Sdelphij	case FILE_BEQLDATE:
845284194Sdelphij	case FILE_QWDATE:
846284194Sdelphij	case FILE_LEQWDATE:
847284194Sdelphij	case FILE_BEQWDATE:
848284194Sdelphij	case FILE_DOUBLE:
849284194Sdelphij	case FILE_BEDOUBLE:
850284194Sdelphij	case FILE_LEDOUBLE:
851284194Sdelphij		val += 8 * MULT;
852284194Sdelphij		break;
853284194Sdelphij
854284194Sdelphij	case FILE_INDIRECT:
855284194Sdelphij	case FILE_NAME:
856284194Sdelphij	case FILE_USE:
857284194Sdelphij		break;
858284194Sdelphij
859284194Sdelphij	default:
860284194Sdelphij		(void)fprintf(stderr, "Bad type %d\n", m->type);
861284194Sdelphij		abort();
862284194Sdelphij	}
863284194Sdelphij
864284194Sdelphij	switch (m->reln) {
865284194Sdelphij	case 'x':	/* matches anything penalize */
866284194Sdelphij	case '!':       /* matches almost anything penalize */
867284194Sdelphij		val = 0;
868284194Sdelphij		break;
869284194Sdelphij
870284194Sdelphij	case '=':	/* Exact match, prefer */
871284194Sdelphij		val += MULT;
872284194Sdelphij		break;
873284194Sdelphij
874284194Sdelphij	case '>':
875284194Sdelphij	case '<':	/* comparison match reduce strength */
876284194Sdelphij		val -= 2 * MULT;
877284194Sdelphij		break;
878284194Sdelphij
879284194Sdelphij	case '^':
880284194Sdelphij	case '&':	/* masking bits, we could count them too */
881284194Sdelphij		val -= MULT;
882284194Sdelphij		break;
883284194Sdelphij
884284194Sdelphij	default:
885284194Sdelphij		(void)fprintf(stderr, "Bad relation %c\n", m->reln);
886284194Sdelphij		abort();
887284194Sdelphij	}
888284194Sdelphij
889284194Sdelphij	if (val == 0)	/* ensure we only return 0 for FILE_DEFAULT */
890284194Sdelphij		val = 1;
891284194Sdelphij
892284194Sdelphij	switch (m->factor_op) {
893284194Sdelphij	case FILE_FACTOR_OP_NONE:
894284194Sdelphij		break;
895284194Sdelphij	case FILE_FACTOR_OP_PLUS:
896284194Sdelphij		val += m->factor;
897284194Sdelphij		break;
898284194Sdelphij	case FILE_FACTOR_OP_MINUS:
899284194Sdelphij		val -= m->factor;
900284194Sdelphij		break;
901284194Sdelphij	case FILE_FACTOR_OP_TIMES:
902284194Sdelphij		val *= m->factor;
903284194Sdelphij		break;
904284194Sdelphij	case FILE_FACTOR_OP_DIV:
905284194Sdelphij		val /= m->factor;
906284194Sdelphij		break;
907284194Sdelphij	default:
908284194Sdelphij		abort();
909284194Sdelphij	}
910284194Sdelphij
911284194Sdelphij	/*
912284194Sdelphij	 * Magic entries with no description get a bonus because they depend
913284194Sdelphij	 * on subsequent magic entries to print something.
914284194Sdelphij	 */
915284194Sdelphij	if (m->desc[0] == '\0')
916284194Sdelphij		val++;
917284194Sdelphij	return val;
918284194Sdelphij}
919284194Sdelphij
920284194Sdelphij/*
921284194Sdelphij * Sort callback for sorting entries by "strength" (basically length)
922284194Sdelphij */
923284194Sdelphijprivate int
924284194Sdelphijapprentice_sort(const void *a, const void *b)
925284194Sdelphij{
926284194Sdelphij	const struct magic_entry *ma = CAST(const struct magic_entry *, a);
927284194Sdelphij	const struct magic_entry *mb = CAST(const struct magic_entry *, b);
928284194Sdelphij	size_t sa = apprentice_magic_strength(ma->mp);
929284194Sdelphij	size_t sb = apprentice_magic_strength(mb->mp);
930284194Sdelphij	if (sa == sb)
931284194Sdelphij		return 0;
932284194Sdelphij	else if (sa > sb)
933284194Sdelphij		return -1;
934284194Sdelphij	else
935284194Sdelphij		return 1;
936284194Sdelphij}
937284194Sdelphij
938284194Sdelphij/*
939284194Sdelphij * Shows sorted patterns list in the order which is used for the matching
940284194Sdelphij */
941284194Sdelphijprivate void
942284194Sdelphijapprentice_list(struct mlist *mlist, int mode)
943284194Sdelphij{
944284194Sdelphij	uint32_t magindex = 0;
945284194Sdelphij	struct mlist *ml;
946284194Sdelphij	for (ml = mlist->next; ml != mlist; ml = ml->next) {
947284194Sdelphij		for (magindex = 0; magindex < ml->nmagic; magindex++) {
948284194Sdelphij			struct magic *m = &ml->magic[magindex];
949284194Sdelphij			if ((m->flag & mode) != mode) {
950284194Sdelphij				/* Skip sub-tests */
951284194Sdelphij				while (magindex + 1 < ml->nmagic &&
952284194Sdelphij				       ml->magic[magindex + 1].cont_level != 0)
953284194Sdelphij					++magindex;
954284194Sdelphij				continue; /* Skip to next top-level test*/
955284194Sdelphij			}
956284194Sdelphij
957284194Sdelphij			/*
958284194Sdelphij			 * Try to iterate over the tree until we find item with
959284194Sdelphij			 * description/mimetype.
960284194Sdelphij			 */
961284194Sdelphij			while (magindex + 1 < ml->nmagic &&
962284194Sdelphij			       ml->magic[magindex + 1].cont_level != 0 &&
963284194Sdelphij			       *ml->magic[magindex].desc == '\0' &&
964284194Sdelphij			       *ml->magic[magindex].mimetype == '\0')
965284194Sdelphij				magindex++;
966284194Sdelphij
967284194Sdelphij			printf("Strength = %3" SIZE_T_FORMAT "u : %s [%s]\n",
968284194Sdelphij			    apprentice_magic_strength(m),
969284194Sdelphij			    ml->magic[magindex].desc,
970284194Sdelphij			    ml->magic[magindex].mimetype);
971284194Sdelphij		}
972284194Sdelphij	}
973284194Sdelphij}
974284194Sdelphij
975284194Sdelphijprivate void
976284194Sdelphijset_test_type(struct magic *mstart, struct magic *m)
977284194Sdelphij{
978284194Sdelphij	switch (m->type) {
979284194Sdelphij	case FILE_BYTE:
980284194Sdelphij	case FILE_SHORT:
981284194Sdelphij	case FILE_LONG:
982284194Sdelphij	case FILE_DATE:
983284194Sdelphij	case FILE_BESHORT:
984284194Sdelphij	case FILE_BELONG:
985284194Sdelphij	case FILE_BEDATE:
986284194Sdelphij	case FILE_LESHORT:
987284194Sdelphij	case FILE_LELONG:
988284194Sdelphij	case FILE_LEDATE:
989284194Sdelphij	case FILE_LDATE:
990284194Sdelphij	case FILE_BELDATE:
991284194Sdelphij	case FILE_LELDATE:
992284194Sdelphij	case FILE_MEDATE:
993284194Sdelphij	case FILE_MELDATE:
994284194Sdelphij	case FILE_MELONG:
995284194Sdelphij	case FILE_QUAD:
996284194Sdelphij	case FILE_LEQUAD:
997284194Sdelphij	case FILE_BEQUAD:
998284194Sdelphij	case FILE_QDATE:
999284194Sdelphij	case FILE_LEQDATE:
1000284194Sdelphij	case FILE_BEQDATE:
1001284194Sdelphij	case FILE_QLDATE:
1002284194Sdelphij	case FILE_LEQLDATE:
1003284194Sdelphij	case FILE_BEQLDATE:
1004284194Sdelphij	case FILE_QWDATE:
1005284194Sdelphij	case FILE_LEQWDATE:
1006284194Sdelphij	case FILE_BEQWDATE:
1007284194Sdelphij	case FILE_FLOAT:
1008284194Sdelphij	case FILE_BEFLOAT:
1009284194Sdelphij	case FILE_LEFLOAT:
1010284194Sdelphij	case FILE_DOUBLE:
1011284194Sdelphij	case FILE_BEDOUBLE:
1012284194Sdelphij	case FILE_LEDOUBLE:
1013284194Sdelphij		mstart->flag |= BINTEST;
1014284194Sdelphij		break;
1015284194Sdelphij	case FILE_STRING:
1016284194Sdelphij	case FILE_PSTRING:
1017284194Sdelphij	case FILE_BESTRING16:
1018284194Sdelphij	case FILE_LESTRING16:
1019284194Sdelphij		/* Allow text overrides */
1020284194Sdelphij		if (mstart->str_flags & STRING_TEXTTEST)
1021284194Sdelphij			mstart->flag |= TEXTTEST;
1022284194Sdelphij		else
1023284194Sdelphij			mstart->flag |= BINTEST;
1024284194Sdelphij		break;
1025284194Sdelphij	case FILE_REGEX:
1026284194Sdelphij	case FILE_SEARCH:
1027284194Sdelphij		/* Check for override */
1028284194Sdelphij		if (mstart->str_flags & STRING_BINTEST)
1029284194Sdelphij			mstart->flag |= BINTEST;
1030284194Sdelphij		if (mstart->str_flags & STRING_TEXTTEST)
1031284194Sdelphij			mstart->flag |= TEXTTEST;
1032284194Sdelphij
1033284194Sdelphij		if (mstart->flag & (TEXTTEST|BINTEST))
1034284194Sdelphij			break;
1035284194Sdelphij
1036284194Sdelphij		/* binary test if pattern is not text */
1037284194Sdelphij		if (file_looks_utf8(m->value.us, (size_t)m->vallen, NULL,
1038284194Sdelphij		    NULL) <= 0)
1039284194Sdelphij			mstart->flag |= BINTEST;
1040284194Sdelphij		else
1041284194Sdelphij			mstart->flag |= TEXTTEST;
1042284194Sdelphij		break;
1043284194Sdelphij	case FILE_DEFAULT:
1044284194Sdelphij		/* can't deduce anything; we shouldn't see this at the
1045284194Sdelphij		   top level anyway */
1046284194Sdelphij		break;
1047284194Sdelphij	case FILE_INVALID:
1048284194Sdelphij	default:
1049284194Sdelphij		/* invalid search type, but no need to complain here */
1050284194Sdelphij		break;
1051284194Sdelphij	}
1052284194Sdelphij}
1053284194Sdelphij
1054284194Sdelphijprivate int
1055284194Sdelphijaddentry(struct magic_set *ms, struct magic_entry *me,
1056284194Sdelphij   struct magic_entry_set *mset)
1057284194Sdelphij{
1058284194Sdelphij	size_t i = me->mp->type == FILE_NAME ? 1 : 0;
1059284194Sdelphij	if (mset[i].count == mset[i].max) {
1060284194Sdelphij		struct magic_entry *mp;
1061284194Sdelphij
1062284194Sdelphij		mset[i].max += ALLOC_INCR;
1063284194Sdelphij		if ((mp = CAST(struct magic_entry *,
1064284194Sdelphij		    realloc(mset[i].me, sizeof(*mp) * mset[i].max))) ==
1065284194Sdelphij		    NULL) {
1066284194Sdelphij			file_oomem(ms, sizeof(*mp) * mset[i].max);
1067284194Sdelphij			return -1;
1068284194Sdelphij		}
1069284194Sdelphij		(void)memset(&mp[mset[i].count], 0, sizeof(*mp) *
1070284194Sdelphij		    ALLOC_INCR);
1071284194Sdelphij		mset[i].me = mp;
1072284194Sdelphij	}
1073284194Sdelphij	mset[i].me[mset[i].count++] = *me;
1074284194Sdelphij	memset(me, 0, sizeof(*me));
1075284194Sdelphij	return 0;
1076284194Sdelphij}
1077284194Sdelphij
1078284194Sdelphij/*
1079284194Sdelphij * Load and parse one file.
1080284194Sdelphij */
1081284194Sdelphijprivate void
1082284194Sdelphijload_1(struct magic_set *ms, int action, const char *fn, int *errs,
1083284194Sdelphij   struct magic_entry_set *mset)
1084284194Sdelphij{
1085284194Sdelphij	size_t lineno = 0, llen = 0;
1086284194Sdelphij	char *line = NULL;
1087284194Sdelphij	ssize_t len;
1088284194Sdelphij	struct magic_entry me;
1089284194Sdelphij
1090284194Sdelphij	FILE *f = fopen(ms->file = fn, "r");
1091284194Sdelphij	if (f == NULL) {
1092284194Sdelphij		if (errno != ENOENT)
1093284194Sdelphij			file_error(ms, errno, "cannot read magic file `%s'",
1094284194Sdelphij				   fn);
1095284194Sdelphij		(*errs)++;
1096284194Sdelphij		return;
1097284194Sdelphij	}
1098284194Sdelphij
1099284194Sdelphij	memset(&me, 0, sizeof(me));
1100284194Sdelphij	/* read and parse this file */
1101284194Sdelphij	for (ms->line = 1; (len = getline(&line, &llen, f)) != -1;
1102284194Sdelphij	    ms->line++) {
1103284194Sdelphij		if (len == 0) /* null line, garbage, etc */
1104284194Sdelphij			continue;
1105284194Sdelphij		if (line[len - 1] == '\n') {
1106284194Sdelphij			lineno++;
1107284194Sdelphij			line[len - 1] = '\0'; /* delete newline */
1108284194Sdelphij		}
1109284194Sdelphij		switch (line[0]) {
1110284194Sdelphij		case '\0':	/* empty, do not parse */
1111284194Sdelphij		case '#':	/* comment, do not parse */
1112284194Sdelphij			continue;
1113284194Sdelphij		case '!':
1114284194Sdelphij			if (line[1] == ':') {
1115284194Sdelphij				size_t i;
1116284194Sdelphij
1117284194Sdelphij				for (i = 0; bang[i].name != NULL; i++) {
1118284194Sdelphij					if ((size_t)(len - 2) > bang[i].len &&
1119284194Sdelphij					    memcmp(bang[i].name, line + 2,
1120284194Sdelphij					    bang[i].len) == 0)
1121284194Sdelphij						break;
1122284194Sdelphij				}
1123284194Sdelphij				if (bang[i].name == NULL) {
1124284194Sdelphij					file_error(ms, 0,
1125284194Sdelphij					    "Unknown !: entry `%s'", line);
1126284194Sdelphij					(*errs)++;
1127284194Sdelphij					continue;
1128284194Sdelphij				}
1129284194Sdelphij				if (me.mp == NULL) {
1130284194Sdelphij					file_error(ms, 0,
1131284194Sdelphij					    "No current entry for :!%s type",
1132284194Sdelphij						bang[i].name);
1133284194Sdelphij					(*errs)++;
1134284194Sdelphij					continue;
1135284194Sdelphij				}
1136284194Sdelphij				if ((*bang[i].fun)(ms, &me,
1137284194Sdelphij				    line + bang[i].len + 2) != 0) {
1138284194Sdelphij					(*errs)++;
1139284194Sdelphij					continue;
1140284194Sdelphij				}
1141284194Sdelphij				continue;
1142284194Sdelphij			}
1143284194Sdelphij			/*FALLTHROUGH*/
1144284194Sdelphij		default:
1145284194Sdelphij		again:
1146284194Sdelphij			switch (parse(ms, &me, line, lineno, action)) {
1147284194Sdelphij			case 0:
1148284194Sdelphij				continue;
1149284194Sdelphij			case 1:
1150284194Sdelphij				(void)addentry(ms, &me, mset);
1151284194Sdelphij				goto again;
1152284194Sdelphij			default:
1153284194Sdelphij				(*errs)++;
1154284194Sdelphij				break;
1155284194Sdelphij			}
1156284194Sdelphij		}
1157284194Sdelphij	}
1158284194Sdelphij	if (me.mp)
1159284194Sdelphij		(void)addentry(ms, &me, mset);
1160284194Sdelphij	free(line);
1161284194Sdelphij	(void)fclose(f);
1162284194Sdelphij}
1163284194Sdelphij
1164284194Sdelphij/*
1165284194Sdelphij * parse a file or directory of files
1166284194Sdelphij * const char *fn: name of magic file or directory
1167284194Sdelphij */
1168284194Sdelphijprivate int
1169284194Sdelphijcmpstrp(const void *p1, const void *p2)
1170284194Sdelphij{
1171284194Sdelphij        return strcmp(*(char *const *)p1, *(char *const *)p2);
1172284194Sdelphij}
1173284194Sdelphij
1174284194Sdelphij
1175284194Sdelphijprivate uint32_t
1176284194Sdelphijset_text_binary(struct magic_set *ms, struct magic_entry *me, uint32_t nme,
1177284194Sdelphij    uint32_t starttest)
1178284194Sdelphij{
1179284194Sdelphij	static const char text[] = "text";
1180284194Sdelphij	static const char binary[] = "binary";
1181284194Sdelphij	static const size_t len = sizeof(text);
1182284194Sdelphij
1183284194Sdelphij	uint32_t i = starttest;
1184284194Sdelphij
1185284194Sdelphij	do {
1186284194Sdelphij		set_test_type(me[starttest].mp, me[i].mp);
1187284194Sdelphij		if ((ms->flags & MAGIC_DEBUG) == 0)
1188284194Sdelphij			continue;
1189284194Sdelphij		(void)fprintf(stderr, "%s%s%s: %s\n",
1190284194Sdelphij		    me[i].mp->mimetype,
1191284194Sdelphij		    me[i].mp->mimetype[0] == '\0' ? "" : "; ",
1192284194Sdelphij		    me[i].mp->desc[0] ? me[i].mp->desc : "(no description)",
1193284194Sdelphij		    me[i].mp->flag & BINTEST ? binary : text);
1194284194Sdelphij		if (me[i].mp->flag & BINTEST) {
1195284194Sdelphij			char *p = strstr(me[i].mp->desc, text);
1196284194Sdelphij			if (p && (p == me[i].mp->desc ||
1197284194Sdelphij			    isspace((unsigned char)p[-1])) &&
1198284194Sdelphij			    (p + len - me[i].mp->desc == MAXstring
1199284194Sdelphij			    || (p[len] == '\0' ||
1200284194Sdelphij			    isspace((unsigned char)p[len]))))
1201284194Sdelphij				(void)fprintf(stderr, "*** Possible "
1202284194Sdelphij				    "binary test for text type\n");
1203284194Sdelphij		}
1204284194Sdelphij	} while (++i < nme && me[i].mp->cont_level != 0);
1205284194Sdelphij	return i;
1206284194Sdelphij}
1207284194Sdelphij
1208284194Sdelphijprivate void
1209284194Sdelphijset_last_default(struct magic_set *ms, struct magic_entry *me, uint32_t nme)
1210284194Sdelphij{
1211284194Sdelphij	uint32_t i;
1212284194Sdelphij	for (i = 0; i < nme; i++) {
1213284194Sdelphij		if (me[i].mp->cont_level == 0 &&
1214284194Sdelphij		    me[i].mp->type == FILE_DEFAULT) {
1215284194Sdelphij			while (++i < nme)
1216284194Sdelphij				if (me[i].mp->cont_level == 0)
1217284194Sdelphij					break;
1218284194Sdelphij			if (i != nme) {
1219284194Sdelphij				/* XXX - Ugh! */
1220284194Sdelphij				ms->line = me[i].mp->lineno;
1221284194Sdelphij				file_magwarn(ms,
1222284194Sdelphij				    "level 0 \"default\" did not sort last");
1223284194Sdelphij			}
1224284194Sdelphij			return;
1225284194Sdelphij		}
1226284194Sdelphij	}
1227284194Sdelphij}
1228284194Sdelphij
1229284194Sdelphijprivate int
1230284194Sdelphijcoalesce_entries(struct magic_set *ms, struct magic_entry *me, uint32_t nme,
1231284194Sdelphij    struct magic **ma, uint32_t *nma)
1232284194Sdelphij{
1233284194Sdelphij	uint32_t i, mentrycount = 0;
1234284194Sdelphij	size_t slen;
1235284194Sdelphij
1236284194Sdelphij	for (i = 0; i < nme; i++)
1237284194Sdelphij		mentrycount += me[i].cont_count;
1238284194Sdelphij
1239284194Sdelphij	slen = sizeof(**ma) * mentrycount;
1240284194Sdelphij	if ((*ma = CAST(struct magic *, malloc(slen))) == NULL) {
1241284194Sdelphij		file_oomem(ms, slen);
1242284194Sdelphij		return -1;
1243284194Sdelphij	}
1244284194Sdelphij
1245284194Sdelphij	mentrycount = 0;
1246284194Sdelphij	for (i = 0; i < nme; i++) {
1247284194Sdelphij		(void)memcpy(*ma + mentrycount, me[i].mp,
1248284194Sdelphij		    me[i].cont_count * sizeof(**ma));
1249284194Sdelphij		mentrycount += me[i].cont_count;
1250284194Sdelphij	}
1251284194Sdelphij	*nma = mentrycount;
1252284194Sdelphij	return 0;
1253284194Sdelphij}
1254284194Sdelphij
1255284194Sdelphijprivate void
1256284194Sdelphijmagic_entry_free(struct magic_entry *me, uint32_t nme)
1257284194Sdelphij{
1258284194Sdelphij	uint32_t i;
1259284194Sdelphij	if (me == NULL)
1260284194Sdelphij		return;
1261284194Sdelphij	for (i = 0; i < nme; i++)
1262284194Sdelphij		free(me[i].mp);
1263284194Sdelphij	free(me);
1264284194Sdelphij}
1265284194Sdelphij
1266284194Sdelphijprivate struct magic_map *
1267284194Sdelphijapprentice_load(struct magic_set *ms, const char *fn, int action)
1268284194Sdelphij{
1269284194Sdelphij	int errs = 0;
1270284194Sdelphij	uint32_t i, j;
1271284194Sdelphij	size_t files = 0, maxfiles = 0;
1272284194Sdelphij	char **filearr = NULL, *mfn;
1273284194Sdelphij	struct stat st;
1274284194Sdelphij	struct magic_map *map;
1275284194Sdelphij	struct magic_entry_set mset[MAGIC_SETS];
1276284194Sdelphij	DIR *dir;
1277284194Sdelphij	struct dirent *d;
1278284194Sdelphij
1279284194Sdelphij	memset(mset, 0, sizeof(mset));
1280284194Sdelphij	ms->flags |= MAGIC_CHECK;	/* Enable checks for parsed files */
1281284194Sdelphij
1282284194Sdelphij
1283284194Sdelphij	if ((map = CAST(struct magic_map *, calloc(1, sizeof(*map)))) == NULL)
1284284194Sdelphij	{
1285284194Sdelphij		file_oomem(ms, sizeof(*map));
1286284194Sdelphij		return NULL;
1287284194Sdelphij	}
1288284194Sdelphij
1289284194Sdelphij	/* print silly verbose header for USG compat. */
1290284194Sdelphij	if (action == FILE_CHECK)
1291284194Sdelphij		(void)fprintf(stderr, "%s\n", usg_hdr);
1292284194Sdelphij
1293284194Sdelphij	/* load directory or file */
1294284194Sdelphij	if (stat(fn, &st) == 0 && S_ISDIR(st.st_mode)) {
1295284194Sdelphij		dir = opendir(fn);
1296284194Sdelphij		if (!dir) {
1297284194Sdelphij			errs++;
1298284194Sdelphij			goto out;
1299284194Sdelphij		}
1300284194Sdelphij		while ((d = readdir(dir)) != NULL) {
1301284194Sdelphij			if (asprintf(&mfn, "%s/%s", fn, d->d_name) < 0) {
1302284194Sdelphij				file_oomem(ms,
1303284194Sdelphij				    strlen(fn) + strlen(d->d_name) + 2);
1304284194Sdelphij				errs++;
1305284194Sdelphij				closedir(dir);
1306284194Sdelphij				goto out;
1307284194Sdelphij			}
1308284194Sdelphij			if (stat(mfn, &st) == -1 || !S_ISREG(st.st_mode)) {
1309284194Sdelphij				free(mfn);
1310284194Sdelphij				continue;
1311284194Sdelphij			}
1312284194Sdelphij			if (files >= maxfiles) {
1313284194Sdelphij				size_t mlen;
1314284194Sdelphij				maxfiles = (maxfiles + 1) * 2;
1315284194Sdelphij				mlen = maxfiles * sizeof(*filearr);
1316284194Sdelphij				if ((filearr = CAST(char **,
1317284194Sdelphij				    realloc(filearr, mlen))) == NULL) {
1318284194Sdelphij					file_oomem(ms, mlen);
1319284194Sdelphij					free(mfn);
1320284194Sdelphij					closedir(dir);
1321284194Sdelphij					errs++;
1322284194Sdelphij					goto out;
1323284194Sdelphij				}
1324284194Sdelphij			}
1325284194Sdelphij			filearr[files++] = mfn;
1326284194Sdelphij		}
1327284194Sdelphij		closedir(dir);
1328284194Sdelphij		qsort(filearr, files, sizeof(*filearr), cmpstrp);
1329284194Sdelphij		for (i = 0; i < files; i++) {
1330284194Sdelphij			load_1(ms, action, filearr[i], &errs, mset);
1331284194Sdelphij			free(filearr[i]);
1332284194Sdelphij		}
1333284194Sdelphij		free(filearr);
1334284194Sdelphij	} else
1335284194Sdelphij		load_1(ms, action, fn, &errs, mset);
1336284194Sdelphij	if (errs)
1337284194Sdelphij		goto out;
1338284194Sdelphij
1339284194Sdelphij	for (j = 0; j < MAGIC_SETS; j++) {
1340284194Sdelphij		/* Set types of tests */
1341284194Sdelphij		for (i = 0; i < mset[j].count; ) {
1342284194Sdelphij			if (mset[j].me[i].mp->cont_level != 0) {
1343284194Sdelphij				i++;
1344284194Sdelphij				continue;
1345284194Sdelphij			}
1346284194Sdelphij			i = set_text_binary(ms, mset[j].me, mset[j].count, i);
1347284194Sdelphij		}
1348284194Sdelphij		qsort(mset[j].me, mset[j].count, sizeof(*mset[j].me),
1349284194Sdelphij		    apprentice_sort);
1350284194Sdelphij
1351284194Sdelphij		/*
1352284194Sdelphij		 * Make sure that any level 0 "default" line is last
1353284194Sdelphij		 * (if one exists).
1354284194Sdelphij		 */
1355284194Sdelphij		set_last_default(ms, mset[j].me, mset[j].count);
1356284194Sdelphij
1357284194Sdelphij		/* coalesce per file arrays into a single one */
1358284194Sdelphij		if (coalesce_entries(ms, mset[j].me, mset[j].count,
1359284194Sdelphij		    &map->magic[j], &map->nmagic[j]) == -1) {
1360284194Sdelphij			errs++;
1361284194Sdelphij			goto out;
1362284194Sdelphij		}
1363284194Sdelphij	}
1364284194Sdelphij
1365284194Sdelphijout:
1366284194Sdelphij	for (j = 0; j < MAGIC_SETS; j++)
1367284194Sdelphij		magic_entry_free(mset[j].me, mset[j].count);
1368284194Sdelphij
1369284194Sdelphij	if (errs) {
1370284194Sdelphij		apprentice_unmap(map);
1371284194Sdelphij		return NULL;
1372284194Sdelphij	}
1373284194Sdelphij	return map;
1374284194Sdelphij}
1375284194Sdelphij
1376284194Sdelphij/*
1377284194Sdelphij * extend the sign bit if the comparison is to be signed
1378284194Sdelphij */
1379284194Sdelphijprotected uint64_t
1380284194Sdelphijfile_signextend(struct magic_set *ms, struct magic *m, uint64_t v)
1381284194Sdelphij{
1382284194Sdelphij	if (!(m->flag & UNSIGNED)) {
1383284194Sdelphij		switch(m->type) {
1384284194Sdelphij		/*
1385284194Sdelphij		 * Do not remove the casts below.  They are
1386284194Sdelphij		 * vital.  When later compared with the data,
1387284194Sdelphij		 * the sign extension must have happened.
1388284194Sdelphij		 */
1389284194Sdelphij		case FILE_BYTE:
1390284194Sdelphij			v = (signed char) v;
1391284194Sdelphij			break;
1392284194Sdelphij		case FILE_SHORT:
1393284194Sdelphij		case FILE_BESHORT:
1394284194Sdelphij		case FILE_LESHORT:
1395284194Sdelphij			v = (short) v;
1396284194Sdelphij			break;
1397284194Sdelphij		case FILE_DATE:
1398284194Sdelphij		case FILE_BEDATE:
1399284194Sdelphij		case FILE_LEDATE:
1400284194Sdelphij		case FILE_MEDATE:
1401284194Sdelphij		case FILE_LDATE:
1402284194Sdelphij		case FILE_BELDATE:
1403284194Sdelphij		case FILE_LELDATE:
1404284194Sdelphij		case FILE_MELDATE:
1405284194Sdelphij		case FILE_LONG:
1406284194Sdelphij		case FILE_BELONG:
1407284194Sdelphij		case FILE_LELONG:
1408284194Sdelphij		case FILE_MELONG:
1409284194Sdelphij		case FILE_FLOAT:
1410284194Sdelphij		case FILE_BEFLOAT:
1411284194Sdelphij		case FILE_LEFLOAT:
1412284194Sdelphij			v = (int32_t) v;
1413284194Sdelphij			break;
1414284194Sdelphij		case FILE_QUAD:
1415284194Sdelphij		case FILE_BEQUAD:
1416284194Sdelphij		case FILE_LEQUAD:
1417284194Sdelphij		case FILE_QDATE:
1418284194Sdelphij		case FILE_QLDATE:
1419284194Sdelphij		case FILE_QWDATE:
1420284194Sdelphij		case FILE_BEQDATE:
1421284194Sdelphij		case FILE_BEQLDATE:
1422284194Sdelphij		case FILE_BEQWDATE:
1423284194Sdelphij		case FILE_LEQDATE:
1424284194Sdelphij		case FILE_LEQLDATE:
1425284194Sdelphij		case FILE_LEQWDATE:
1426284194Sdelphij		case FILE_DOUBLE:
1427284194Sdelphij		case FILE_BEDOUBLE:
1428284194Sdelphij		case FILE_LEDOUBLE:
1429284194Sdelphij			v = (int64_t) v;
1430284194Sdelphij			break;
1431284194Sdelphij		case FILE_STRING:
1432284194Sdelphij		case FILE_PSTRING:
1433284194Sdelphij		case FILE_BESTRING16:
1434284194Sdelphij		case FILE_LESTRING16:
1435284194Sdelphij		case FILE_REGEX:
1436284194Sdelphij		case FILE_SEARCH:
1437284194Sdelphij		case FILE_DEFAULT:
1438284194Sdelphij		case FILE_INDIRECT:
1439284194Sdelphij		case FILE_NAME:
1440284194Sdelphij		case FILE_USE:
1441284194Sdelphij		case FILE_CLEAR:
1442284194Sdelphij			break;
1443284194Sdelphij		default:
1444284194Sdelphij			if (ms->flags & MAGIC_CHECK)
1445284194Sdelphij			    file_magwarn(ms, "cannot happen: m->type=%d\n",
1446284194Sdelphij				    m->type);
1447284194Sdelphij			return ~0U;
1448284194Sdelphij		}
1449284194Sdelphij	}
1450284194Sdelphij	return v;
1451284194Sdelphij}
1452284194Sdelphij
1453284194Sdelphijprivate int
1454284194Sdelphijstring_modifier_check(struct magic_set *ms, struct magic *m)
1455284194Sdelphij{
1456284194Sdelphij	if ((ms->flags & MAGIC_CHECK) == 0)
1457284194Sdelphij		return 0;
1458284194Sdelphij
1459284194Sdelphij	if ((m->type != FILE_REGEX || (m->str_flags & REGEX_LINE_COUNT) == 0) &&
1460284194Sdelphij	    (m->type != FILE_PSTRING && (m->str_flags & PSTRING_LEN) != 0)) {
1461284194Sdelphij		file_magwarn(ms,
1462284194Sdelphij		    "'/BHhLl' modifiers are only allowed for pascal strings\n");
1463284194Sdelphij		return -1;
1464284194Sdelphij	}
1465284194Sdelphij	switch (m->type) {
1466284194Sdelphij	case FILE_BESTRING16:
1467284194Sdelphij	case FILE_LESTRING16:
1468284194Sdelphij		if (m->str_flags != 0) {
1469284194Sdelphij			file_magwarn(ms,
1470284194Sdelphij			    "no modifiers allowed for 16-bit strings\n");
1471284194Sdelphij			return -1;
1472284194Sdelphij		}
1473284194Sdelphij		break;
1474284194Sdelphij	case FILE_STRING:
1475284194Sdelphij	case FILE_PSTRING:
1476284194Sdelphij		if ((m->str_flags & REGEX_OFFSET_START) != 0) {
1477284194Sdelphij			file_magwarn(ms,
1478284194Sdelphij			    "'/%c' only allowed on regex and search\n",
1479284194Sdelphij			    CHAR_REGEX_OFFSET_START);
1480284194Sdelphij			return -1;
1481284194Sdelphij		}
1482284194Sdelphij		break;
1483284194Sdelphij	case FILE_SEARCH:
1484284194Sdelphij		if (m->str_range == 0) {
1485284194Sdelphij			file_magwarn(ms,
1486284194Sdelphij			    "missing range; defaulting to %d\n",
1487284194Sdelphij                            STRING_DEFAULT_RANGE);
1488284194Sdelphij			m->str_range = STRING_DEFAULT_RANGE;
1489284194Sdelphij			return -1;
1490284194Sdelphij		}
1491284194Sdelphij		break;
1492284194Sdelphij	case FILE_REGEX:
1493284194Sdelphij		if ((m->str_flags & STRING_COMPACT_WHITESPACE) != 0) {
1494284194Sdelphij			file_magwarn(ms, "'/%c' not allowed on regex\n",
1495284194Sdelphij			    CHAR_COMPACT_WHITESPACE);
1496284194Sdelphij			return -1;
1497284194Sdelphij		}
1498284194Sdelphij		if ((m->str_flags & STRING_COMPACT_OPTIONAL_WHITESPACE) != 0) {
1499284194Sdelphij			file_magwarn(ms, "'/%c' not allowed on regex\n",
1500284194Sdelphij			    CHAR_COMPACT_OPTIONAL_WHITESPACE);
1501284194Sdelphij			return -1;
1502284194Sdelphij		}
1503284194Sdelphij		break;
1504284194Sdelphij	default:
1505284194Sdelphij		file_magwarn(ms, "coding error: m->type=%d\n",
1506284194Sdelphij		    m->type);
1507284194Sdelphij		return -1;
1508284194Sdelphij	}
1509284194Sdelphij	return 0;
1510284194Sdelphij}
1511284194Sdelphij
1512284194Sdelphijprivate int
1513284194Sdelphijget_op(char c)
1514284194Sdelphij{
1515284194Sdelphij	switch (c) {
1516284194Sdelphij	case '&':
1517284194Sdelphij		return FILE_OPAND;
1518284194Sdelphij	case '|':
1519284194Sdelphij		return FILE_OPOR;
1520284194Sdelphij	case '^':
1521284194Sdelphij		return FILE_OPXOR;
1522284194Sdelphij	case '+':
1523284194Sdelphij		return FILE_OPADD;
1524284194Sdelphij	case '-':
1525284194Sdelphij		return FILE_OPMINUS;
1526284194Sdelphij	case '*':
1527284194Sdelphij		return FILE_OPMULTIPLY;
1528284194Sdelphij	case '/':
1529284194Sdelphij		return FILE_OPDIVIDE;
1530284194Sdelphij	case '%':
1531284194Sdelphij		return FILE_OPMODULO;
1532284194Sdelphij	default:
1533284194Sdelphij		return -1;
1534284194Sdelphij	}
1535284194Sdelphij}
1536284194Sdelphij
1537284194Sdelphij#ifdef ENABLE_CONDITIONALS
1538284194Sdelphijprivate int
1539284194Sdelphijget_cond(const char *l, const char **t)
1540284194Sdelphij{
1541284194Sdelphij	static const struct cond_tbl_s {
1542284194Sdelphij		char name[8];
1543284194Sdelphij		size_t len;
1544284194Sdelphij		int cond;
1545284194Sdelphij	} cond_tbl[] = {
1546284194Sdelphij		{ "if",		2,	COND_IF },
1547284194Sdelphij		{ "elif",	4,	COND_ELIF },
1548284194Sdelphij		{ "else",	4,	COND_ELSE },
1549284194Sdelphij		{ "",		0,	COND_NONE },
1550284194Sdelphij	};
1551284194Sdelphij	const struct cond_tbl_s *p;
1552284194Sdelphij
1553284194Sdelphij	for (p = cond_tbl; p->len; p++) {
1554284194Sdelphij		if (strncmp(l, p->name, p->len) == 0 &&
1555284194Sdelphij		    isspace((unsigned char)l[p->len])) {
1556284194Sdelphij			if (t)
1557284194Sdelphij				*t = l + p->len;
1558284194Sdelphij			break;
1559284194Sdelphij		}
1560284194Sdelphij	}
1561284194Sdelphij	return p->cond;
1562284194Sdelphij}
1563284194Sdelphij
1564284194Sdelphijprivate int
1565284194Sdelphijcheck_cond(struct magic_set *ms, int cond, uint32_t cont_level)
1566284194Sdelphij{
1567284194Sdelphij	int last_cond;
1568284194Sdelphij	last_cond = ms->c.li[cont_level].last_cond;
1569284194Sdelphij
1570284194Sdelphij	switch (cond) {
1571284194Sdelphij	case COND_IF:
1572284194Sdelphij		if (last_cond != COND_NONE && last_cond != COND_ELIF) {
1573284194Sdelphij			if (ms->flags & MAGIC_CHECK)
1574284194Sdelphij				file_magwarn(ms, "syntax error: `if'");
1575284194Sdelphij			return -1;
1576284194Sdelphij		}
1577284194Sdelphij		last_cond = COND_IF;
1578284194Sdelphij		break;
1579284194Sdelphij
1580284194Sdelphij	case COND_ELIF:
1581284194Sdelphij		if (last_cond != COND_IF && last_cond != COND_ELIF) {
1582284194Sdelphij			if (ms->flags & MAGIC_CHECK)
1583284194Sdelphij				file_magwarn(ms, "syntax error: `elif'");
1584284194Sdelphij			return -1;
1585284194Sdelphij		}
1586284194Sdelphij		last_cond = COND_ELIF;
1587284194Sdelphij		break;
1588284194Sdelphij
1589284194Sdelphij	case COND_ELSE:
1590284194Sdelphij		if (last_cond != COND_IF && last_cond != COND_ELIF) {
1591284194Sdelphij			if (ms->flags & MAGIC_CHECK)
1592284194Sdelphij				file_magwarn(ms, "syntax error: `else'");
1593284194Sdelphij			return -1;
1594284194Sdelphij		}
1595284194Sdelphij		last_cond = COND_NONE;
1596284194Sdelphij		break;
1597284194Sdelphij
1598284194Sdelphij	case COND_NONE:
1599284194Sdelphij		last_cond = COND_NONE;
1600284194Sdelphij		break;
1601284194Sdelphij	}
1602284194Sdelphij
1603284194Sdelphij	ms->c.li[cont_level].last_cond = last_cond;
1604284194Sdelphij	return 0;
1605284194Sdelphij}
1606284194Sdelphij#endif /* ENABLE_CONDITIONALS */
1607284194Sdelphij
1608284194Sdelphijprivate int
1609284194Sdelphijparse_indirect_modifier(struct magic_set *ms, struct magic *m, const char **lp)
1610284194Sdelphij{
1611284194Sdelphij	const char *l = *lp;
1612284194Sdelphij
1613284194Sdelphij	while (!isspace((unsigned char)*++l))
1614284194Sdelphij		switch (*l) {
1615284194Sdelphij		case CHAR_INDIRECT_RELATIVE:
1616284194Sdelphij			m->str_flags |= INDIRECT_RELATIVE;
1617284194Sdelphij			break;
1618284194Sdelphij		default:
1619284194Sdelphij			if (ms->flags & MAGIC_CHECK)
1620284194Sdelphij				file_magwarn(ms, "indirect modifier `%c' "
1621284194Sdelphij					"invalid", *l);
1622284194Sdelphij			*lp = l;
1623284194Sdelphij			return -1;
1624284194Sdelphij		}
1625284194Sdelphij	*lp = l;
1626284194Sdelphij	return 0;
1627284194Sdelphij}
1628284194Sdelphij
1629284194Sdelphijprivate void
1630284194Sdelphijparse_op_modifier(struct magic_set *ms, struct magic *m, const char **lp,
1631284194Sdelphij    int op)
1632284194Sdelphij{
1633284194Sdelphij	const char *l = *lp;
1634284194Sdelphij	char *t;
1635284194Sdelphij	uint64_t val;
1636284194Sdelphij
1637284194Sdelphij	++l;
1638284194Sdelphij	m->mask_op |= op;
1639284194Sdelphij	val = (uint64_t)strtoull(l, &t, 0);
1640284194Sdelphij	l = t;
1641284194Sdelphij	m->num_mask = file_signextend(ms, m, val);
1642284194Sdelphij	eatsize(&l);
1643284194Sdelphij	*lp = l;
1644284194Sdelphij}
1645284194Sdelphij
1646284194Sdelphijprivate int
1647284194Sdelphijparse_string_modifier(struct magic_set *ms, struct magic *m, const char **lp)
1648284194Sdelphij{
1649284194Sdelphij	const char *l = *lp;
1650284194Sdelphij	char *t;
1651284194Sdelphij	int have_range = 0;
1652284194Sdelphij
1653284194Sdelphij	while (!isspace((unsigned char)*++l)) {
1654284194Sdelphij		switch (*l) {
1655284194Sdelphij		case '0':  case '1':  case '2':
1656284194Sdelphij		case '3':  case '4':  case '5':
1657284194Sdelphij		case '6':  case '7':  case '8':
1658284194Sdelphij		case '9':
1659284194Sdelphij			if (have_range && (ms->flags & MAGIC_CHECK))
1660284194Sdelphij				file_magwarn(ms, "multiple ranges");
1661284194Sdelphij			have_range = 1;
1662284194Sdelphij			m->str_range = CAST(uint32_t, strtoul(l, &t, 0));
1663284194Sdelphij			if (m->str_range == 0)
1664284194Sdelphij				file_magwarn(ms, "zero range");
1665284194Sdelphij			l = t - 1;
1666284194Sdelphij			break;
1667284194Sdelphij		case CHAR_COMPACT_WHITESPACE:
1668284194Sdelphij			m->str_flags |= STRING_COMPACT_WHITESPACE;
1669284194Sdelphij			break;
1670284194Sdelphij		case CHAR_COMPACT_OPTIONAL_WHITESPACE:
1671284194Sdelphij			m->str_flags |= STRING_COMPACT_OPTIONAL_WHITESPACE;
1672284194Sdelphij			break;
1673284194Sdelphij		case CHAR_IGNORE_LOWERCASE:
1674284194Sdelphij			m->str_flags |= STRING_IGNORE_LOWERCASE;
1675284194Sdelphij			break;
1676284194Sdelphij		case CHAR_IGNORE_UPPERCASE:
1677284194Sdelphij			m->str_flags |= STRING_IGNORE_UPPERCASE;
1678284194Sdelphij			break;
1679284194Sdelphij		case CHAR_REGEX_OFFSET_START:
1680284194Sdelphij			m->str_flags |= REGEX_OFFSET_START;
1681284194Sdelphij			break;
1682284194Sdelphij		case CHAR_BINTEST:
1683284194Sdelphij			m->str_flags |= STRING_BINTEST;
1684284194Sdelphij			break;
1685284194Sdelphij		case CHAR_TEXTTEST:
1686284194Sdelphij			m->str_flags |= STRING_TEXTTEST;
1687284194Sdelphij			break;
1688284194Sdelphij		case CHAR_TRIM:
1689284194Sdelphij			m->str_flags |= STRING_TRIM;
1690284194Sdelphij			break;
1691284194Sdelphij		case CHAR_PSTRING_1_LE:
1692284194Sdelphij#define SET_LENGTH(a) m->str_flags = (m->str_flags & ~PSTRING_LEN) | (a)
1693284194Sdelphij			if (m->type != FILE_PSTRING)
1694284194Sdelphij				goto bad;
1695284194Sdelphij			SET_LENGTH(PSTRING_1_LE);
1696284194Sdelphij			break;
1697284194Sdelphij		case CHAR_PSTRING_2_BE:
1698284194Sdelphij			if (m->type != FILE_PSTRING)
1699284194Sdelphij				goto bad;
1700284194Sdelphij			SET_LENGTH(PSTRING_2_BE);
1701284194Sdelphij			break;
1702284194Sdelphij		case CHAR_PSTRING_2_LE:
1703284194Sdelphij			if (m->type != FILE_PSTRING)
1704284194Sdelphij				goto bad;
1705284194Sdelphij			SET_LENGTH(PSTRING_2_LE);
1706284194Sdelphij			break;
1707284194Sdelphij		case CHAR_PSTRING_4_BE:
1708284194Sdelphij			if (m->type != FILE_PSTRING)
1709284194Sdelphij				goto bad;
1710284194Sdelphij			SET_LENGTH(PSTRING_4_BE);
1711284194Sdelphij			break;
1712284194Sdelphij		case CHAR_PSTRING_4_LE:
1713284194Sdelphij			switch (m->type) {
1714284194Sdelphij			case FILE_PSTRING:
1715284194Sdelphij			case FILE_REGEX:
1716284194Sdelphij				break;
1717284194Sdelphij			default:
1718284194Sdelphij				goto bad;
1719284194Sdelphij			}
1720284194Sdelphij			SET_LENGTH(PSTRING_4_LE);
1721284194Sdelphij			break;
1722284194Sdelphij		case CHAR_PSTRING_LENGTH_INCLUDES_ITSELF:
1723284194Sdelphij			if (m->type != FILE_PSTRING)
1724284194Sdelphij				goto bad;
1725284194Sdelphij			m->str_flags |= PSTRING_LENGTH_INCLUDES_ITSELF;
1726284194Sdelphij			break;
1727284194Sdelphij		default:
1728284194Sdelphij		bad:
1729284194Sdelphij			if (ms->flags & MAGIC_CHECK)
1730284194Sdelphij				file_magwarn(ms, "string modifier `%c' "
1731284194Sdelphij					"invalid", *l);
1732284194Sdelphij			goto out;
1733284194Sdelphij		}
1734284194Sdelphij		/* allow multiple '/' for readability */
1735284194Sdelphij		if (l[1] == '/' && !isspace((unsigned char)l[2]))
1736284194Sdelphij			l++;
1737284194Sdelphij	}
1738284194Sdelphij	if (string_modifier_check(ms, m) == -1)
1739284194Sdelphij		goto out;
1740284194Sdelphij	*lp = l;
1741284194Sdelphij	return 0;
1742284194Sdelphijout:
1743284194Sdelphij	*lp = l;
1744284194Sdelphij	return -1;
1745284194Sdelphij}
1746284194Sdelphij
1747284194Sdelphij/*
1748284194Sdelphij * parse one line from magic file, put into magic[index++] if valid
1749284194Sdelphij */
1750284194Sdelphijprivate int
1751284194Sdelphijparse(struct magic_set *ms, struct magic_entry *me, const char *line,
1752284194Sdelphij    size_t lineno, int action)
1753284194Sdelphij{
1754284194Sdelphij#ifdef ENABLE_CONDITIONALS
1755284194Sdelphij	static uint32_t last_cont_level = 0;
1756284194Sdelphij#endif
1757284194Sdelphij	size_t i;
1758284194Sdelphij	struct magic *m;
1759284194Sdelphij	const char *l = line;
1760284194Sdelphij	char *t;
1761284194Sdelphij	int op;
1762284194Sdelphij	uint32_t cont_level;
1763284194Sdelphij	int32_t diff;
1764284194Sdelphij
1765284194Sdelphij	cont_level = 0;
1766284194Sdelphij
1767284194Sdelphij	/*
1768284194Sdelphij	 * Parse the offset.
1769284194Sdelphij	 */
1770284194Sdelphij	while (*l == '>') {
1771284194Sdelphij		++l;		/* step over */
1772284194Sdelphij		cont_level++;
1773284194Sdelphij	}
1774284194Sdelphij#ifdef ENABLE_CONDITIONALS
1775284194Sdelphij	if (cont_level == 0 || cont_level > last_cont_level)
1776284194Sdelphij		if (file_check_mem(ms, cont_level) == -1)
1777284194Sdelphij			return -1;
1778284194Sdelphij	last_cont_level = cont_level;
1779284194Sdelphij#endif
1780284194Sdelphij	if (cont_level != 0) {
1781284194Sdelphij		if (me->mp == NULL) {
1782284194Sdelphij			file_magerror(ms, "No current entry for continuation");
1783284194Sdelphij			return -1;
1784284194Sdelphij		}
1785284194Sdelphij		if (me->cont_count == 0) {
1786284194Sdelphij			file_magerror(ms, "Continuations present with 0 count");
1787284194Sdelphij			return -1;
1788284194Sdelphij		}
1789284194Sdelphij		m = &me->mp[me->cont_count - 1];
1790284194Sdelphij		diff = (int32_t)cont_level - (int32_t)m->cont_level;
1791284194Sdelphij		if (diff > 1)
1792284194Sdelphij			file_magwarn(ms, "New continuation level %u is more "
1793284194Sdelphij			    "than one larger than current level %u", cont_level,
1794284194Sdelphij			    m->cont_level);
1795284194Sdelphij		if (me->cont_count == me->max_count) {
1796284194Sdelphij			struct magic *nm;
1797284194Sdelphij			size_t cnt = me->max_count + ALLOC_CHUNK;
1798284194Sdelphij			if ((nm = CAST(struct magic *, realloc(me->mp,
1799284194Sdelphij			    sizeof(*nm) * cnt))) == NULL) {
1800284194Sdelphij				file_oomem(ms, sizeof(*nm) * cnt);
1801284194Sdelphij				return -1;
1802284194Sdelphij			}
1803284194Sdelphij			me->mp = m = nm;
1804284194Sdelphij			me->max_count = CAST(uint32_t, cnt);
1805284194Sdelphij		}
1806284194Sdelphij		m = &me->mp[me->cont_count++];
1807284194Sdelphij		(void)memset(m, 0, sizeof(*m));
1808284194Sdelphij		m->cont_level = cont_level;
1809284194Sdelphij	} else {
1810284194Sdelphij		static const size_t len = sizeof(*m) * ALLOC_CHUNK;
1811284194Sdelphij		if (me->mp != NULL)
1812284194Sdelphij			return 1;
1813284194Sdelphij		if ((m = CAST(struct magic *, malloc(len))) == NULL) {
1814284194Sdelphij			file_oomem(ms, len);
1815284194Sdelphij			return -1;
1816284194Sdelphij		}
1817284194Sdelphij		me->mp = m;
1818284194Sdelphij		me->max_count = ALLOC_CHUNK;
1819284194Sdelphij		(void)memset(m, 0, sizeof(*m));
1820284194Sdelphij		m->factor_op = FILE_FACTOR_OP_NONE;
1821284194Sdelphij		m->cont_level = 0;
1822284194Sdelphij		me->cont_count = 1;
1823284194Sdelphij	}
1824284194Sdelphij	m->lineno = CAST(uint32_t, lineno);
1825284194Sdelphij
1826284194Sdelphij	if (*l == '&') {  /* m->cont_level == 0 checked below. */
1827284194Sdelphij                ++l;            /* step over */
1828284194Sdelphij                m->flag |= OFFADD;
1829284194Sdelphij        }
1830284194Sdelphij	if (*l == '(') {
1831284194Sdelphij		++l;		/* step over */
1832284194Sdelphij		m->flag |= INDIR;
1833284194Sdelphij		if (m->flag & OFFADD)
1834284194Sdelphij			m->flag = (m->flag & ~OFFADD) | INDIROFFADD;
1835284194Sdelphij
1836284194Sdelphij		if (*l == '&') {  /* m->cont_level == 0 checked below */
1837284194Sdelphij			++l;            /* step over */
1838284194Sdelphij			m->flag |= OFFADD;
1839284194Sdelphij		}
1840284194Sdelphij	}
1841284194Sdelphij	/* Indirect offsets are not valid at level 0. */
1842284194Sdelphij	if (m->cont_level == 0 && (m->flag & (OFFADD | INDIROFFADD)))
1843284194Sdelphij		if (ms->flags & MAGIC_CHECK)
1844284194Sdelphij			file_magwarn(ms, "relative offset at level 0");
1845284194Sdelphij
1846284194Sdelphij	/* get offset, then skip over it */
1847284194Sdelphij	m->offset = (uint32_t)strtoul(l, &t, 0);
1848284194Sdelphij        if (l == t)
1849284194Sdelphij		if (ms->flags & MAGIC_CHECK)
1850284194Sdelphij			file_magwarn(ms, "offset `%s' invalid", l);
1851284194Sdelphij        l = t;
1852284194Sdelphij
1853284194Sdelphij	if (m->flag & INDIR) {
1854284194Sdelphij		m->in_type = FILE_LONG;
1855284194Sdelphij		m->in_offset = 0;
1856284194Sdelphij		/*
1857284194Sdelphij		 * read [.lbs][+-]nnnnn)
1858284194Sdelphij		 */
1859284194Sdelphij		if (*l == '.') {
1860284194Sdelphij			l++;
1861284194Sdelphij			switch (*l) {
1862284194Sdelphij			case 'l':
1863284194Sdelphij				m->in_type = FILE_LELONG;
1864284194Sdelphij				break;
1865284194Sdelphij			case 'L':
1866284194Sdelphij				m->in_type = FILE_BELONG;
1867284194Sdelphij				break;
1868284194Sdelphij			case 'm':
1869284194Sdelphij				m->in_type = FILE_MELONG;
1870284194Sdelphij				break;
1871284194Sdelphij			case 'h':
1872284194Sdelphij			case 's':
1873284194Sdelphij				m->in_type = FILE_LESHORT;
1874284194Sdelphij				break;
1875284194Sdelphij			case 'H':
1876284194Sdelphij			case 'S':
1877284194Sdelphij				m->in_type = FILE_BESHORT;
1878284194Sdelphij				break;
1879284194Sdelphij			case 'c':
1880284194Sdelphij			case 'b':
1881284194Sdelphij			case 'C':
1882284194Sdelphij			case 'B':
1883284194Sdelphij				m->in_type = FILE_BYTE;
1884284194Sdelphij				break;
1885284194Sdelphij			case 'e':
1886284194Sdelphij			case 'f':
1887284194Sdelphij			case 'g':
1888284194Sdelphij				m->in_type = FILE_LEDOUBLE;
1889284194Sdelphij				break;
1890284194Sdelphij			case 'E':
1891284194Sdelphij			case 'F':
1892284194Sdelphij			case 'G':
1893284194Sdelphij				m->in_type = FILE_BEDOUBLE;
1894284194Sdelphij				break;
1895284194Sdelphij			case 'i':
1896284194Sdelphij				m->in_type = FILE_LEID3;
1897284194Sdelphij				break;
1898284194Sdelphij			case 'I':
1899284194Sdelphij				m->in_type = FILE_BEID3;
1900284194Sdelphij				break;
1901284194Sdelphij			default:
1902284194Sdelphij				if (ms->flags & MAGIC_CHECK)
1903284194Sdelphij					file_magwarn(ms,
1904284194Sdelphij					    "indirect offset type `%c' invalid",
1905284194Sdelphij					    *l);
1906284194Sdelphij				break;
1907284194Sdelphij			}
1908284194Sdelphij			l++;
1909284194Sdelphij		}
1910284194Sdelphij
1911284194Sdelphij		m->in_op = 0;
1912284194Sdelphij		if (*l == '~') {
1913284194Sdelphij			m->in_op |= FILE_OPINVERSE;
1914284194Sdelphij			l++;
1915284194Sdelphij		}
1916284194Sdelphij		if ((op = get_op(*l)) != -1) {
1917284194Sdelphij			m->in_op |= op;
1918284194Sdelphij			l++;
1919284194Sdelphij		}
1920284194Sdelphij		if (*l == '(') {
1921284194Sdelphij			m->in_op |= FILE_OPINDIRECT;
1922284194Sdelphij			l++;
1923284194Sdelphij		}
1924284194Sdelphij		if (isdigit((unsigned char)*l) || *l == '-') {
1925284194Sdelphij			m->in_offset = (int32_t)strtol(l, &t, 0);
1926284194Sdelphij			if (l == t)
1927284194Sdelphij				if (ms->flags & MAGIC_CHECK)
1928284194Sdelphij					file_magwarn(ms,
1929284194Sdelphij					    "in_offset `%s' invalid", l);
1930284194Sdelphij			l = t;
1931284194Sdelphij		}
1932284194Sdelphij		if (*l++ != ')' ||
1933284194Sdelphij		    ((m->in_op & FILE_OPINDIRECT) && *l++ != ')'))
1934284194Sdelphij			if (ms->flags & MAGIC_CHECK)
1935284194Sdelphij				file_magwarn(ms,
1936284194Sdelphij				    "missing ')' in indirect offset");
1937284194Sdelphij	}
1938284194Sdelphij	EATAB;
1939284194Sdelphij
1940284194Sdelphij#ifdef ENABLE_CONDITIONALS
1941284194Sdelphij	m->cond = get_cond(l, &l);
1942284194Sdelphij	if (check_cond(ms, m->cond, cont_level) == -1)
1943284194Sdelphij		return -1;
1944284194Sdelphij
1945284194Sdelphij	EATAB;
1946284194Sdelphij#endif
1947284194Sdelphij
1948284194Sdelphij	/*
1949284194Sdelphij	 * Parse the type.
1950284194Sdelphij	 */
1951284194Sdelphij	if (*l == 'u') {
1952284194Sdelphij		/*
1953284194Sdelphij		 * Try it as a keyword type prefixed by "u"; match what
1954284194Sdelphij		 * follows the "u".  If that fails, try it as an SUS
1955284194Sdelphij		 * integer type.
1956284194Sdelphij		 */
1957284194Sdelphij		m->type = get_type(type_tbl, l + 1, &l);
1958284194Sdelphij		if (m->type == FILE_INVALID) {
1959284194Sdelphij			/*
1960284194Sdelphij			 * Not a keyword type; parse it as an SUS type,
1961284194Sdelphij			 * 'u' possibly followed by a number or C/S/L.
1962284194Sdelphij			 */
1963284194Sdelphij			m->type = get_standard_integer_type(l, &l);
1964284194Sdelphij		}
1965284194Sdelphij		/* It's unsigned. */
1966284194Sdelphij		if (m->type != FILE_INVALID)
1967284194Sdelphij			m->flag |= UNSIGNED;
1968284194Sdelphij	} else {
1969284194Sdelphij		/*
1970284194Sdelphij		 * Try it as a keyword type.  If that fails, try it as
1971284194Sdelphij		 * an SUS integer type if it begins with "d" or as an
1972284194Sdelphij		 * SUS string type if it begins with "s".  In any case,
1973284194Sdelphij		 * it's not unsigned.
1974284194Sdelphij		 */
1975284194Sdelphij		m->type = get_type(type_tbl, l, &l);
1976284194Sdelphij		if (m->type == FILE_INVALID) {
1977284194Sdelphij			/*
1978284194Sdelphij			 * Not a keyword type; parse it as an SUS type,
1979284194Sdelphij			 * either 'd' possibly followed by a number or
1980284194Sdelphij			 * C/S/L, or just 's'.
1981284194Sdelphij			 */
1982284194Sdelphij			if (*l == 'd')
1983284194Sdelphij				m->type = get_standard_integer_type(l, &l);
1984284194Sdelphij			else if (*l == 's' && !isalpha((unsigned char)l[1])) {
1985284194Sdelphij				m->type = FILE_STRING;
1986284194Sdelphij				++l;
1987284194Sdelphij			}
1988284194Sdelphij		}
1989284194Sdelphij	}
1990284194Sdelphij
1991284194Sdelphij	if (m->type == FILE_INVALID) {
1992284194Sdelphij		/* Not found - try it as a special keyword. */
1993284194Sdelphij		m->type = get_type(special_tbl, l, &l);
1994284194Sdelphij	}
1995284194Sdelphij
1996284194Sdelphij	if (m->type == FILE_INVALID) {
1997284194Sdelphij		if (ms->flags & MAGIC_CHECK)
1998284194Sdelphij			file_magwarn(ms, "type `%s' invalid", l);
1999284194Sdelphij		return -1;
2000284194Sdelphij	}
2001284194Sdelphij
2002284194Sdelphij	/* New-style anding: "0 byte&0x80 =0x80 dynamically linked" */
2003284194Sdelphij	/* New and improved: ~ & | ^ + - * / % -- exciting, isn't it? */
2004284194Sdelphij
2005284194Sdelphij	m->mask_op = 0;
2006284194Sdelphij	if (*l == '~') {
2007284194Sdelphij		if (!IS_STRING(m->type))
2008284194Sdelphij			m->mask_op |= FILE_OPINVERSE;
2009284194Sdelphij		else if (ms->flags & MAGIC_CHECK)
2010284194Sdelphij			file_magwarn(ms, "'~' invalid for string types");
2011284194Sdelphij		++l;
2012284194Sdelphij	}
2013284194Sdelphij	m->str_range = 0;
2014284194Sdelphij	m->str_flags = m->type == FILE_PSTRING ? PSTRING_1_LE : 0;
2015284194Sdelphij	if ((op = get_op(*l)) != -1) {
2016284194Sdelphij		if (IS_STRING(m->type)) {
2017284194Sdelphij			int r;
2018284194Sdelphij
2019284194Sdelphij			if (op != FILE_OPDIVIDE) {
2020284194Sdelphij				if (ms->flags & MAGIC_CHECK)
2021284194Sdelphij					file_magwarn(ms,
2022284194Sdelphij					    "invalid string/indirect op: "
2023284194Sdelphij					    "`%c'", *t);
2024284194Sdelphij				return -1;
2025284194Sdelphij			}
2026284194Sdelphij
2027284194Sdelphij			if (m->type == FILE_INDIRECT)
2028284194Sdelphij				r = parse_indirect_modifier(ms, m, &l);
2029284194Sdelphij			else
2030284194Sdelphij				r = parse_string_modifier(ms, m, &l);
2031284194Sdelphij			if (r == -1)
2032284194Sdelphij				return -1;
2033284194Sdelphij		} else
2034284194Sdelphij			parse_op_modifier(ms, m, &l, op);
2035284194Sdelphij	}
2036284194Sdelphij
2037284194Sdelphij	/*
2038284194Sdelphij	 * We used to set mask to all 1's here, instead let's just not do
2039284194Sdelphij	 * anything if mask = 0 (unless you have a better idea)
2040284194Sdelphij	 */
2041284194Sdelphij	EATAB;
2042284194Sdelphij
2043284194Sdelphij	switch (*l) {
2044284194Sdelphij	case '>':
2045284194Sdelphij	case '<':
2046284194Sdelphij  		m->reln = *l;
2047284194Sdelphij  		++l;
2048284194Sdelphij		if (*l == '=') {
2049284194Sdelphij			if (ms->flags & MAGIC_CHECK) {
2050284194Sdelphij				file_magwarn(ms, "%c= not supported",
2051284194Sdelphij				    m->reln);
2052284194Sdelphij				return -1;
2053284194Sdelphij			}
2054284194Sdelphij		   ++l;
2055284194Sdelphij		}
2056284194Sdelphij		break;
2057284194Sdelphij	/* Old-style anding: "0 byte &0x80 dynamically linked" */
2058284194Sdelphij	case '&':
2059284194Sdelphij	case '^':
2060284194Sdelphij	case '=':
2061284194Sdelphij  		m->reln = *l;
2062284194Sdelphij  		++l;
2063284194Sdelphij		if (*l == '=') {
2064284194Sdelphij		   /* HP compat: ignore &= etc. */
2065284194Sdelphij		   ++l;
2066284194Sdelphij		}
2067284194Sdelphij		break;
2068284194Sdelphij	case '!':
2069284194Sdelphij		m->reln = *l;
2070284194Sdelphij		++l;
2071284194Sdelphij		break;
2072284194Sdelphij	default:
2073284194Sdelphij  		m->reln = '=';	/* the default relation */
2074284194Sdelphij		if (*l == 'x' && ((isascii((unsigned char)l[1]) &&
2075284194Sdelphij		    isspace((unsigned char)l[1])) || !l[1])) {
2076284194Sdelphij			m->reln = *l;
2077284194Sdelphij			++l;
2078284194Sdelphij		}
2079284194Sdelphij		break;
2080284194Sdelphij	}
2081284194Sdelphij	/*
2082284194Sdelphij	 * Grab the value part, except for an 'x' reln.
2083284194Sdelphij	 */
2084284194Sdelphij	if (m->reln != 'x' && getvalue(ms, m, &l, action))
2085284194Sdelphij		return -1;
2086284194Sdelphij
2087284194Sdelphij	/*
2088284194Sdelphij	 * TODO finish this macro and start using it!
2089284194Sdelphij	 * #define offsetcheck {if (offset > HOWMANY-1)
2090284194Sdelphij	 *	magwarn("offset too big"); }
2091284194Sdelphij	 */
2092284194Sdelphij
2093284194Sdelphij	/*
2094284194Sdelphij	 * Now get last part - the description
2095284194Sdelphij	 */
2096284194Sdelphij	EATAB;
2097284194Sdelphij	if (l[0] == '\b') {
2098284194Sdelphij		++l;
2099284194Sdelphij		m->flag |= NOSPACE;
2100284194Sdelphij	} else if ((l[0] == '\\') && (l[1] == 'b')) {
2101284194Sdelphij		++l;
2102284194Sdelphij		++l;
2103284194Sdelphij		m->flag |= NOSPACE;
2104284194Sdelphij	}
2105284194Sdelphij	for (i = 0; (m->desc[i++] = *l++) != '\0' && i < sizeof(m->desc); )
2106284194Sdelphij		continue;
2107284194Sdelphij	if (i == sizeof(m->desc)) {
2108284194Sdelphij		m->desc[sizeof(m->desc) - 1] = '\0';
2109284194Sdelphij		if (ms->flags & MAGIC_CHECK)
2110284194Sdelphij			file_magwarn(ms, "description `%s' truncated", m->desc);
2111284194Sdelphij	}
2112284194Sdelphij
2113284194Sdelphij        /*
2114284194Sdelphij	 * We only do this check while compiling, or if any of the magic
2115284194Sdelphij	 * files were not compiled.
2116284194Sdelphij         */
2117284194Sdelphij        if (ms->flags & MAGIC_CHECK) {
2118284194Sdelphij		if (check_format(ms, m) == -1)
2119284194Sdelphij			return -1;
2120284194Sdelphij	}
2121284194Sdelphij#ifndef COMPILE_ONLY
2122284194Sdelphij	if (action == FILE_CHECK) {
2123284194Sdelphij		file_mdump(m);
2124284194Sdelphij	}
2125284194Sdelphij#endif
2126284194Sdelphij	m->mimetype[0] = '\0';		/* initialise MIME type to none */
2127284194Sdelphij	return 0;
2128284194Sdelphij}
2129284194Sdelphij
2130284194Sdelphij/*
2131284194Sdelphij * parse a STRENGTH annotation line from magic file, put into magic[index - 1]
2132284194Sdelphij * if valid
2133284194Sdelphij */
2134284194Sdelphijprivate int
2135284194Sdelphijparse_strength(struct magic_set *ms, struct magic_entry *me, const char *line)
2136284194Sdelphij{
2137284194Sdelphij	const char *l = line;
2138284194Sdelphij	char *el;
2139284194Sdelphij	unsigned long factor;
2140284194Sdelphij	struct magic *m = &me->mp[0];
2141284194Sdelphij
2142284194Sdelphij	if (m->factor_op != FILE_FACTOR_OP_NONE) {
2143284194Sdelphij		file_magwarn(ms,
2144284194Sdelphij		    "Current entry already has a strength type: %c %d",
2145284194Sdelphij		    m->factor_op, m->factor);
2146284194Sdelphij		return -1;
2147284194Sdelphij	}
2148284194Sdelphij	if (m->type == FILE_NAME) {
2149284194Sdelphij		file_magwarn(ms, "%s: Strength setting is not supported in "
2150284194Sdelphij		    "\"name\" magic entries", m->value.s);
2151284194Sdelphij		return -1;
2152284194Sdelphij	}
2153284194Sdelphij	EATAB;
2154284194Sdelphij	switch (*l) {
2155284194Sdelphij	case FILE_FACTOR_OP_NONE:
2156284194Sdelphij	case FILE_FACTOR_OP_PLUS:
2157284194Sdelphij	case FILE_FACTOR_OP_MINUS:
2158284194Sdelphij	case FILE_FACTOR_OP_TIMES:
2159284194Sdelphij	case FILE_FACTOR_OP_DIV:
2160284194Sdelphij		m->factor_op = *l++;
2161284194Sdelphij		break;
2162284194Sdelphij	default:
2163284194Sdelphij		file_magwarn(ms, "Unknown factor op `%c'", *l);
2164284194Sdelphij		return -1;
2165284194Sdelphij	}
2166284194Sdelphij	EATAB;
2167284194Sdelphij	factor = strtoul(l, &el, 0);
2168284194Sdelphij	if (factor > 255) {
2169284194Sdelphij		file_magwarn(ms, "Too large factor `%lu'", factor);
2170284194Sdelphij		goto out;
2171284194Sdelphij	}
2172284194Sdelphij	if (*el && !isspace((unsigned char)*el)) {
2173284194Sdelphij		file_magwarn(ms, "Bad factor `%s'", l);
2174284194Sdelphij		goto out;
2175284194Sdelphij	}
2176284194Sdelphij	m->factor = (uint8_t)factor;
2177284194Sdelphij	if (m->factor == 0 && m->factor_op == FILE_FACTOR_OP_DIV) {
2178284194Sdelphij		file_magwarn(ms, "Cannot have factor op `%c' and factor %u",
2179284194Sdelphij		    m->factor_op, m->factor);
2180284194Sdelphij		goto out;
2181284194Sdelphij	}
2182284194Sdelphij	return 0;
2183284194Sdelphijout:
2184284194Sdelphij	m->factor_op = FILE_FACTOR_OP_NONE;
2185284194Sdelphij	m->factor = 0;
2186284194Sdelphij	return -1;
2187284194Sdelphij}
2188284194Sdelphij
2189284194Sdelphijprivate int
2190284194Sdelphijgoodchar(unsigned char x, const char *extra)
2191284194Sdelphij{
2192284194Sdelphij	return (isascii(x) && isalnum(x)) || strchr(extra, x);
2193284194Sdelphij}
2194284194Sdelphij
2195284194Sdelphijprivate int
2196284194Sdelphijparse_extra(struct magic_set *ms, struct magic_entry *me, const char *line,
2197284194Sdelphij    off_t off, size_t len, const char *name, const char *extra, int nt)
2198284194Sdelphij{
2199284194Sdelphij	size_t i;
2200284194Sdelphij	const char *l = line;
2201284194Sdelphij	struct magic *m = &me->mp[me->cont_count == 0 ? 0 : me->cont_count - 1];
2202284194Sdelphij	char *buf = (char *)m + off;
2203284194Sdelphij
2204284194Sdelphij	if (buf[0] != '\0') {
2205284194Sdelphij		len = nt ? strlen(buf) : len;
2206284194Sdelphij		file_magwarn(ms, "Current entry already has a %s type "
2207284194Sdelphij		    "`%.*s', new type `%s'", name, (int)len, buf, l);
2208284194Sdelphij		return -1;
2209284194Sdelphij	}
2210284194Sdelphij
2211284194Sdelphij	if (*m->desc == '\0') {
2212284194Sdelphij		file_magwarn(ms, "Current entry does not yet have a "
2213284194Sdelphij		    "description for adding a %s type", name);
2214284194Sdelphij		return -1;
2215284194Sdelphij	}
2216284194Sdelphij
2217284194Sdelphij	EATAB;
2218284194Sdelphij	for (i = 0; *l && i < len && goodchar(*l, extra); buf[i++] = *l++)
2219284194Sdelphij		continue;
2220284194Sdelphij
2221284194Sdelphij	if (i == len && *l) {
2222284194Sdelphij		if (nt)
2223284194Sdelphij			buf[len - 1] = '\0';
2224284194Sdelphij		if (ms->flags & MAGIC_CHECK)
2225284194Sdelphij			file_magwarn(ms, "%s type `%s' truncated %"
2226284194Sdelphij			    SIZE_T_FORMAT "u", name, line, i);
2227284194Sdelphij	} else {
2228284194Sdelphij		if (!isspace((unsigned char)*l) && !goodchar(*l, extra))
2229284194Sdelphij			file_magwarn(ms, "%s type `%s' has bad char '%c'",
2230284194Sdelphij			    name, line, *l);
2231284194Sdelphij		if (nt)
2232284194Sdelphij			buf[i] = '\0';
2233284194Sdelphij	}
2234284194Sdelphij
2235284194Sdelphij	if (i > 0)
2236284194Sdelphij		return 0;
2237284194Sdelphij
2238284194Sdelphij	file_magerror(ms, "Bad magic entry '%s'", line);
2239284194Sdelphij	return -1;
2240284194Sdelphij}
2241284194Sdelphij
2242284194Sdelphij/*
2243284194Sdelphij * Parse an Apple CREATOR/TYPE annotation from magic file and put it into
2244284194Sdelphij * magic[index - 1]
2245284194Sdelphij */
2246284194Sdelphijprivate int
2247284194Sdelphijparse_apple(struct magic_set *ms, struct magic_entry *me, const char *line)
2248284194Sdelphij{
2249284194Sdelphij	struct magic *m = &me->mp[0];
2250284194Sdelphij
2251284194Sdelphij	return parse_extra(ms, me, line, offsetof(struct magic, apple),
2252284194Sdelphij	    sizeof(m->apple), "APPLE", "!+-./", 0);
2253284194Sdelphij}
2254284194Sdelphij
2255284194Sdelphij/*
2256284194Sdelphij * parse a MIME annotation line from magic file, put into magic[index - 1]
2257284194Sdelphij * if valid
2258284194Sdelphij */
2259284194Sdelphijprivate int
2260284194Sdelphijparse_mime(struct magic_set *ms, struct magic_entry *me, const char *line)
2261284194Sdelphij{
2262284194Sdelphij	struct magic *m = &me->mp[0];
2263284194Sdelphij
2264284194Sdelphij	return parse_extra(ms, me, line, offsetof(struct magic, mimetype),
2265284194Sdelphij	    sizeof(m->mimetype), "MIME", "+-/.", 1);
2266284194Sdelphij}
2267284194Sdelphij
2268284194Sdelphijprivate int
2269284194Sdelphijcheck_format_type(const char *ptr, int type)
2270284194Sdelphij{
2271284194Sdelphij	int quad = 0, h;
2272284194Sdelphij	if (*ptr == '\0') {
2273284194Sdelphij		/* Missing format string; bad */
2274284194Sdelphij		return -1;
2275284194Sdelphij	}
2276284194Sdelphij
2277284194Sdelphij	switch (file_formats[type]) {
2278284194Sdelphij	case FILE_FMT_QUAD:
2279284194Sdelphij		quad = 1;
2280284194Sdelphij		/*FALLTHROUGH*/
2281284194Sdelphij	case FILE_FMT_NUM:
2282284194Sdelphij		if (quad == 0) {
2283284194Sdelphij			switch (type) {
2284284194Sdelphij			case FILE_BYTE:
2285284194Sdelphij				h = 2;
2286284194Sdelphij				break;
2287284194Sdelphij			case FILE_SHORT:
2288284194Sdelphij			case FILE_BESHORT:
2289284194Sdelphij			case FILE_LESHORT:
2290284194Sdelphij				h = 1;
2291284194Sdelphij				break;
2292284194Sdelphij			case FILE_LONG:
2293284194Sdelphij			case FILE_BELONG:
2294284194Sdelphij			case FILE_LELONG:
2295284194Sdelphij			case FILE_MELONG:
2296284194Sdelphij			case FILE_LEID3:
2297284194Sdelphij			case FILE_BEID3:
2298284194Sdelphij			case FILE_INDIRECT:
2299284194Sdelphij				h = 0;
2300284194Sdelphij				break;
2301284194Sdelphij			default:
2302284194Sdelphij				abort();
2303284194Sdelphij			}
2304284194Sdelphij		} else
2305284194Sdelphij			h = 0;
2306284194Sdelphij		if (*ptr == '-')
2307284194Sdelphij			ptr++;
2308284194Sdelphij		if (*ptr == '.')
2309284194Sdelphij			ptr++;
2310284194Sdelphij		while (isdigit((unsigned char)*ptr)) ptr++;
2311284194Sdelphij		if (*ptr == '.')
2312284194Sdelphij			ptr++;
2313284194Sdelphij		while (isdigit((unsigned char)*ptr)) ptr++;
2314284194Sdelphij		if (quad) {
2315284194Sdelphij			if (*ptr++ != 'l')
2316284194Sdelphij				return -1;
2317284194Sdelphij			if (*ptr++ != 'l')
2318284194Sdelphij				return -1;
2319284194Sdelphij		}
2320284194Sdelphij
2321284194Sdelphij		switch (*ptr++) {
2322284194Sdelphij#ifdef STRICT_FORMAT 	/* "long" formats are int formats for us */
2323284194Sdelphij		/* so don't accept the 'l' modifier */
2324284194Sdelphij		case 'l':
2325284194Sdelphij			switch (*ptr++) {
2326284194Sdelphij			case 'i':
2327284194Sdelphij			case 'd':
2328284194Sdelphij			case 'u':
2329284194Sdelphij			case 'o':
2330284194Sdelphij			case 'x':
2331284194Sdelphij			case 'X':
2332284194Sdelphij				return h != 0 ? -1 : 0;
2333284194Sdelphij			default:
2334284194Sdelphij				return -1;
2335284194Sdelphij			}
2336284194Sdelphij
2337284194Sdelphij		/*
2338284194Sdelphij		 * Don't accept h and hh modifiers. They make writing
2339284194Sdelphij		 * magic entries more complicated, for very little benefit
2340284194Sdelphij		 */
2341284194Sdelphij		case 'h':
2342284194Sdelphij			if (h-- <= 0)
2343284194Sdelphij				return -1;
2344284194Sdelphij			switch (*ptr++) {
2345284194Sdelphij			case 'h':
2346284194Sdelphij				if (h-- <= 0)
2347284194Sdelphij					return -1;
2348284194Sdelphij				switch (*ptr++) {
2349284194Sdelphij				case 'i':
2350284194Sdelphij				case 'd':
2351284194Sdelphij				case 'u':
2352284194Sdelphij				case 'o':
2353284194Sdelphij				case 'x':
2354284194Sdelphij				case 'X':
2355284194Sdelphij					return 0;
2356284194Sdelphij				default:
2357284194Sdelphij					return -1;
2358284194Sdelphij				}
2359284194Sdelphij			case 'i':
2360284194Sdelphij			case 'd':
2361284194Sdelphij			case 'u':
2362284194Sdelphij			case 'o':
2363284194Sdelphij			case 'x':
2364284194Sdelphij			case 'X':
2365284194Sdelphij				return h != 0 ? -1 : 0;
2366284194Sdelphij			default:
2367284194Sdelphij				return -1;
2368284194Sdelphij			}
2369284194Sdelphij#endif
2370284194Sdelphij		case 'c':
2371284194Sdelphij			return h != 2 ? -1 : 0;
2372284194Sdelphij		case 'i':
2373284194Sdelphij		case 'd':
2374284194Sdelphij		case 'u':
2375284194Sdelphij		case 'o':
2376284194Sdelphij		case 'x':
2377284194Sdelphij		case 'X':
2378284194Sdelphij#ifdef STRICT_FORMAT
2379284194Sdelphij			return h != 0 ? -1 : 0;
2380284194Sdelphij#else
2381284194Sdelphij			return 0;
2382284194Sdelphij#endif
2383284194Sdelphij		default:
2384284194Sdelphij			return -1;
2385284194Sdelphij		}
2386284194Sdelphij
2387284194Sdelphij	case FILE_FMT_FLOAT:
2388284194Sdelphij	case FILE_FMT_DOUBLE:
2389284194Sdelphij		if (*ptr == '-')
2390284194Sdelphij			ptr++;
2391284194Sdelphij		if (*ptr == '.')
2392284194Sdelphij			ptr++;
2393284194Sdelphij		while (isdigit((unsigned char)*ptr)) ptr++;
2394284194Sdelphij		if (*ptr == '.')
2395284194Sdelphij			ptr++;
2396284194Sdelphij		while (isdigit((unsigned char)*ptr)) ptr++;
2397284194Sdelphij
2398284194Sdelphij		switch (*ptr++) {
2399284194Sdelphij		case 'e':
2400284194Sdelphij		case 'E':
2401284194Sdelphij		case 'f':
2402284194Sdelphij		case 'F':
2403284194Sdelphij		case 'g':
2404284194Sdelphij		case 'G':
2405284194Sdelphij			return 0;
2406284194Sdelphij
2407284194Sdelphij		default:
2408284194Sdelphij			return -1;
2409284194Sdelphij		}
2410284194Sdelphij
2411284194Sdelphij
2412284194Sdelphij	case FILE_FMT_STR:
2413284194Sdelphij		if (*ptr == '-')
2414284194Sdelphij			ptr++;
2415284194Sdelphij		while (isdigit((unsigned char )*ptr))
2416284194Sdelphij			ptr++;
2417284194Sdelphij		if (*ptr == '.') {
2418284194Sdelphij			ptr++;
2419284194Sdelphij			while (isdigit((unsigned char )*ptr))
2420284194Sdelphij				ptr++;
2421284194Sdelphij		}
2422284194Sdelphij
2423284194Sdelphij		switch (*ptr++) {
2424284194Sdelphij		case 's':
2425284194Sdelphij			return 0;
2426284194Sdelphij		default:
2427284194Sdelphij			return -1;
2428284194Sdelphij		}
2429284194Sdelphij
2430284194Sdelphij	default:
2431284194Sdelphij		/* internal error */
2432284194Sdelphij		abort();
2433284194Sdelphij	}
2434284194Sdelphij	/*NOTREACHED*/
2435284194Sdelphij	return -1;
2436284194Sdelphij}
2437284194Sdelphij
2438284194Sdelphij/*
2439284194Sdelphij * Check that the optional printf format in description matches
2440284194Sdelphij * the type of the magic.
2441284194Sdelphij */
2442284194Sdelphijprivate int
2443284194Sdelphijcheck_format(struct magic_set *ms, struct magic *m)
2444284194Sdelphij{
2445284194Sdelphij	char *ptr;
2446284194Sdelphij
2447284194Sdelphij	for (ptr = m->desc; *ptr; ptr++)
2448284194Sdelphij		if (*ptr == '%')
2449284194Sdelphij			break;
2450284194Sdelphij	if (*ptr == '\0') {
2451284194Sdelphij		/* No format string; ok */
2452284194Sdelphij		return 1;
2453284194Sdelphij	}
2454284194Sdelphij
2455284194Sdelphij	assert(file_nformats == file_nnames);
2456284194Sdelphij
2457284194Sdelphij	if (m->type >= file_nformats) {
2458284194Sdelphij		file_magwarn(ms, "Internal error inconsistency between "
2459284194Sdelphij		    "m->type and format strings");
2460284194Sdelphij		return -1;
2461284194Sdelphij	}
2462284194Sdelphij	if (file_formats[m->type] == FILE_FMT_NONE) {
2463284194Sdelphij		file_magwarn(ms, "No format string for `%s' with description "
2464284194Sdelphij		    "`%s'", m->desc, file_names[m->type]);
2465284194Sdelphij		return -1;
2466284194Sdelphij	}
2467284194Sdelphij
2468284194Sdelphij	ptr++;
2469284194Sdelphij	if (check_format_type(ptr, m->type) == -1) {
2470284194Sdelphij		/*
2471284194Sdelphij		 * TODO: this error message is unhelpful if the format
2472284194Sdelphij		 * string is not one character long
2473284194Sdelphij		 */
2474284194Sdelphij		file_magwarn(ms, "Printf format `%c' is not valid for type "
2475284194Sdelphij		    "`%s' in description `%s'", *ptr ? *ptr : '?',
2476284194Sdelphij		    file_names[m->type], m->desc);
2477284194Sdelphij		return -1;
2478284194Sdelphij	}
2479284194Sdelphij
2480284194Sdelphij	for (; *ptr; ptr++) {
2481284194Sdelphij		if (*ptr == '%') {
2482284194Sdelphij			file_magwarn(ms,
2483284194Sdelphij			    "Too many format strings (should have at most one) "
2484284194Sdelphij			    "for `%s' with description `%s'",
2485284194Sdelphij			    file_names[m->type], m->desc);
2486284194Sdelphij			return -1;
2487284194Sdelphij		}
2488284194Sdelphij	}
2489284194Sdelphij	return 0;
2490284194Sdelphij}
2491284194Sdelphij
2492284194Sdelphij/*
2493284194Sdelphij * Read a numeric value from a pointer, into the value union of a magic
2494284194Sdelphij * pointer, according to the magic type.  Update the string pointer to point
2495284194Sdelphij * just after the number read.  Return 0 for success, non-zero for failure.
2496284194Sdelphij */
2497284194Sdelphijprivate int
2498284194Sdelphijgetvalue(struct magic_set *ms, struct magic *m, const char **p, int action)
2499284194Sdelphij{
2500284194Sdelphij	switch (m->type) {
2501284194Sdelphij	case FILE_BESTRING16:
2502284194Sdelphij	case FILE_LESTRING16:
2503284194Sdelphij	case FILE_STRING:
2504284194Sdelphij	case FILE_PSTRING:
2505284194Sdelphij	case FILE_REGEX:
2506284194Sdelphij	case FILE_SEARCH:
2507284194Sdelphij	case FILE_NAME:
2508284194Sdelphij	case FILE_USE:
2509284194Sdelphij		*p = getstr(ms, m, *p, action == FILE_COMPILE);
2510284194Sdelphij		if (*p == NULL) {
2511284194Sdelphij			if (ms->flags & MAGIC_CHECK)
2512284194Sdelphij				file_magwarn(ms, "cannot get string from `%s'",
2513284194Sdelphij				    m->value.s);
2514284194Sdelphij			return -1;
2515284194Sdelphij		}
2516284194Sdelphij		if (m->type == FILE_REGEX) {
2517284194Sdelphij			file_regex_t rx;
2518284194Sdelphij			int rc = file_regcomp(&rx, m->value.s, REG_EXTENDED);
2519284194Sdelphij			if (rc) {
2520284194Sdelphij				if (ms->flags & MAGIC_CHECK)
2521284194Sdelphij					file_regerror(&rx, rc, ms);
2522284194Sdelphij			}
2523284194Sdelphij			file_regfree(&rx);
2524284194Sdelphij			return rc ? -1 : 0;
2525284194Sdelphij		}
2526284194Sdelphij		return 0;
2527284194Sdelphij	case FILE_FLOAT:
2528284194Sdelphij	case FILE_BEFLOAT:
2529284194Sdelphij	case FILE_LEFLOAT:
2530284194Sdelphij		if (m->reln != 'x') {
2531284194Sdelphij			char *ep;
2532284194Sdelphij#ifdef HAVE_STRTOF
2533284194Sdelphij			m->value.f = strtof(*p, &ep);
2534284194Sdelphij#else
2535284194Sdelphij			m->value.f = (float)strtod(*p, &ep);
2536284194Sdelphij#endif
2537284194Sdelphij			*p = ep;
2538284194Sdelphij		}
2539284194Sdelphij		return 0;
2540284194Sdelphij	case FILE_DOUBLE:
2541284194Sdelphij	case FILE_BEDOUBLE:
2542284194Sdelphij	case FILE_LEDOUBLE:
2543284194Sdelphij		if (m->reln != 'x') {
2544284194Sdelphij			char *ep;
2545284194Sdelphij			m->value.d = strtod(*p, &ep);
2546284194Sdelphij			*p = ep;
2547284194Sdelphij		}
2548284194Sdelphij		return 0;
2549284194Sdelphij	default:
2550284194Sdelphij		if (m->reln != 'x') {
2551284194Sdelphij			char *ep;
2552284194Sdelphij			m->value.q = file_signextend(ms, m,
2553284194Sdelphij			    (uint64_t)strtoull(*p, &ep, 0));
2554284194Sdelphij			*p = ep;
2555284194Sdelphij			eatsize(p);
2556284194Sdelphij		}
2557284194Sdelphij		return 0;
2558284194Sdelphij	}
2559284194Sdelphij}
2560284194Sdelphij
2561284194Sdelphij/*
2562284194Sdelphij * Convert a string containing C character escapes.  Stop at an unescaped
2563284194Sdelphij * space or tab.
2564284194Sdelphij * Copy the converted version to "m->value.s", and the length in m->vallen.
2565284194Sdelphij * Return updated scan pointer as function result. Warn if set.
2566284194Sdelphij */
2567284194Sdelphijprivate const char *
2568284194Sdelphijgetstr(struct magic_set *ms, struct magic *m, const char *s, int warn)
2569284194Sdelphij{
2570284194Sdelphij	const char *origs = s;
2571284194Sdelphij	char	*p = m->value.s;
2572284194Sdelphij	size_t  plen = sizeof(m->value.s);
2573284194Sdelphij	char 	*origp = p;
2574284194Sdelphij	char	*pmax = p + plen - 1;
2575284194Sdelphij	int	c;
2576284194Sdelphij	int	val;
2577284194Sdelphij
2578284194Sdelphij	while ((c = *s++) != '\0') {
2579284194Sdelphij		if (isspace((unsigned char) c))
2580284194Sdelphij			break;
2581284194Sdelphij		if (p >= pmax) {
2582284194Sdelphij			file_error(ms, 0, "string too long: `%s'", origs);
2583284194Sdelphij			return NULL;
2584284194Sdelphij		}
2585284194Sdelphij		if (c == '\\') {
2586284194Sdelphij			switch(c = *s++) {
2587284194Sdelphij
2588284194Sdelphij			case '\0':
2589284194Sdelphij				if (warn)
2590284194Sdelphij					file_magwarn(ms, "incomplete escape");
2591284194Sdelphij				goto out;
2592284194Sdelphij
2593284194Sdelphij			case '\t':
2594284194Sdelphij				if (warn) {
2595284194Sdelphij					file_magwarn(ms,
2596284194Sdelphij					    "escaped tab found, use \\t instead");
2597284194Sdelphij					warn = 0;	/* already did */
2598284194Sdelphij				}
2599284194Sdelphij				/*FALLTHROUGH*/
2600284194Sdelphij			default:
2601284194Sdelphij				if (warn) {
2602284194Sdelphij					if (isprint((unsigned char)c)) {
2603284194Sdelphij						/* Allow escaping of
2604284194Sdelphij						 * ``relations'' */
2605284194Sdelphij						if (strchr("<>&^=!", c) == NULL
2606284194Sdelphij						    && (m->type != FILE_REGEX ||
2607284194Sdelphij						    strchr("[]().*?^$|{}", c)
2608284194Sdelphij						    == NULL)) {
2609284194Sdelphij							file_magwarn(ms, "no "
2610284194Sdelphij							    "need to escape "
2611284194Sdelphij							    "`%c'", c);
2612284194Sdelphij						}
2613284194Sdelphij					} else {
2614284194Sdelphij						file_magwarn(ms,
2615284194Sdelphij						    "unknown escape sequence: "
2616284194Sdelphij						    "\\%03o", c);
2617284194Sdelphij					}
2618284194Sdelphij				}
2619284194Sdelphij				/*FALLTHROUGH*/
2620284194Sdelphij			/* space, perhaps force people to use \040? */
2621284194Sdelphij			case ' ':
2622284194Sdelphij#if 0
2623284194Sdelphij			/*
2624284194Sdelphij			 * Other things people escape, but shouldn't need to,
2625284194Sdelphij			 * so we disallow them
2626284194Sdelphij			 */
2627284194Sdelphij			case '\'':
2628284194Sdelphij			case '"':
2629284194Sdelphij			case '?':
2630284194Sdelphij#endif
2631284194Sdelphij			/* Relations */
2632284194Sdelphij			case '>':
2633284194Sdelphij			case '<':
2634284194Sdelphij			case '&':
2635284194Sdelphij			case '^':
2636284194Sdelphij			case '=':
2637284194Sdelphij			case '!':
2638284194Sdelphij			/* and baskslash itself */
2639284194Sdelphij			case '\\':
2640284194Sdelphij				*p++ = (char) c;
2641284194Sdelphij				break;
2642284194Sdelphij
2643284194Sdelphij			case 'a':
2644284194Sdelphij				*p++ = '\a';
2645284194Sdelphij				break;
2646284194Sdelphij
2647284194Sdelphij			case 'b':
2648284194Sdelphij				*p++ = '\b';
2649284194Sdelphij				break;
2650284194Sdelphij
2651284194Sdelphij			case 'f':
2652284194Sdelphij				*p++ = '\f';
2653284194Sdelphij				break;
2654284194Sdelphij
2655284194Sdelphij			case 'n':
2656284194Sdelphij				*p++ = '\n';
2657284194Sdelphij				break;
2658284194Sdelphij
2659284194Sdelphij			case 'r':
2660284194Sdelphij				*p++ = '\r';
2661284194Sdelphij				break;
2662284194Sdelphij
2663284194Sdelphij			case 't':
2664284194Sdelphij				*p++ = '\t';
2665284194Sdelphij				break;
2666284194Sdelphij
2667284194Sdelphij			case 'v':
2668284194Sdelphij				*p++ = '\v';
2669284194Sdelphij				break;
2670284194Sdelphij
2671284194Sdelphij			/* \ and up to 3 octal digits */
2672284194Sdelphij			case '0':
2673284194Sdelphij			case '1':
2674284194Sdelphij			case '2':
2675284194Sdelphij			case '3':
2676284194Sdelphij			case '4':
2677284194Sdelphij			case '5':
2678284194Sdelphij			case '6':
2679284194Sdelphij			case '7':
2680284194Sdelphij				val = c - '0';
2681284194Sdelphij				c = *s++;  /* try for 2 */
2682284194Sdelphij				if (c >= '0' && c <= '7') {
2683284194Sdelphij					val = (val << 3) | (c - '0');
2684284194Sdelphij					c = *s++;  /* try for 3 */
2685284194Sdelphij					if (c >= '0' && c <= '7')
2686284194Sdelphij						val = (val << 3) | (c-'0');
2687284194Sdelphij					else
2688284194Sdelphij						--s;
2689284194Sdelphij				}
2690284194Sdelphij				else
2691284194Sdelphij					--s;
2692284194Sdelphij				*p++ = (char)val;
2693284194Sdelphij				break;
2694284194Sdelphij
2695284194Sdelphij			/* \x and up to 2 hex digits */
2696284194Sdelphij			case 'x':
2697284194Sdelphij				val = 'x';	/* Default if no digits */
2698284194Sdelphij				c = hextoint(*s++);	/* Get next char */
2699284194Sdelphij				if (c >= 0) {
2700284194Sdelphij					val = c;
2701284194Sdelphij					c = hextoint(*s++);
2702284194Sdelphij					if (c >= 0)
2703284194Sdelphij						val = (val << 4) + c;
2704284194Sdelphij					else
2705284194Sdelphij						--s;
2706284194Sdelphij				} else
2707284194Sdelphij					--s;
2708284194Sdelphij				*p++ = (char)val;
2709284194Sdelphij				break;
2710284194Sdelphij			}
2711284194Sdelphij		} else
2712284194Sdelphij			*p++ = (char)c;
2713284194Sdelphij	}
2714284194Sdelphijout:
2715284194Sdelphij	*p = '\0';
2716284194Sdelphij	m->vallen = CAST(unsigned char, (p - origp));
2717284194Sdelphij	if (m->type == FILE_PSTRING)
2718284194Sdelphij		m->vallen += (unsigned char)file_pstring_length_size(m);
2719284194Sdelphij	return s;
2720284194Sdelphij}
2721284194Sdelphij
2722284194Sdelphij
2723284194Sdelphij/* Single hex char to int; -1 if not a hex char. */
2724284194Sdelphijprivate int
2725284194Sdelphijhextoint(int c)
2726284194Sdelphij{
2727284194Sdelphij	if (!isascii((unsigned char) c))
2728284194Sdelphij		return -1;
2729284194Sdelphij	if (isdigit((unsigned char) c))
2730284194Sdelphij		return c - '0';
2731284194Sdelphij	if ((c >= 'a') && (c <= 'f'))
2732284194Sdelphij		return c + 10 - 'a';
2733284194Sdelphij	if (( c>= 'A') && (c <= 'F'))
2734284194Sdelphij		return c + 10 - 'A';
2735284194Sdelphij	return -1;
2736284194Sdelphij}
2737284194Sdelphij
2738284194Sdelphij
2739284194Sdelphij/*
2740284194Sdelphij * Print a string containing C character escapes.
2741284194Sdelphij */
2742284194Sdelphijprotected void
2743284194Sdelphijfile_showstr(FILE *fp, const char *s, size_t len)
2744284194Sdelphij{
2745284194Sdelphij	char	c;
2746284194Sdelphij
2747284194Sdelphij	for (;;) {
2748284194Sdelphij		if (len == ~0U) {
2749284194Sdelphij			c = *s++;
2750284194Sdelphij			if (c == '\0')
2751284194Sdelphij				break;
2752284194Sdelphij		}
2753284194Sdelphij		else  {
2754284194Sdelphij			if (len-- == 0)
2755284194Sdelphij				break;
2756284194Sdelphij			c = *s++;
2757284194Sdelphij		}
2758284194Sdelphij		if (c >= 040 && c <= 0176)	/* TODO isprint && !iscntrl */
2759284194Sdelphij			(void) fputc(c, fp);
2760284194Sdelphij		else {
2761284194Sdelphij			(void) fputc('\\', fp);
2762284194Sdelphij			switch (c) {
2763284194Sdelphij			case '\a':
2764284194Sdelphij				(void) fputc('a', fp);
2765284194Sdelphij				break;
2766284194Sdelphij
2767284194Sdelphij			case '\b':
2768284194Sdelphij				(void) fputc('b', fp);
2769284194Sdelphij				break;
2770284194Sdelphij
2771284194Sdelphij			case '\f':
2772284194Sdelphij				(void) fputc('f', fp);
2773284194Sdelphij				break;
2774284194Sdelphij
2775284194Sdelphij			case '\n':
2776284194Sdelphij				(void) fputc('n', fp);
2777284194Sdelphij				break;
2778284194Sdelphij
2779284194Sdelphij			case '\r':
2780284194Sdelphij				(void) fputc('r', fp);
2781284194Sdelphij				break;
2782284194Sdelphij
2783284194Sdelphij			case '\t':
2784284194Sdelphij				(void) fputc('t', fp);
2785284194Sdelphij				break;
2786284194Sdelphij
2787284194Sdelphij			case '\v':
2788284194Sdelphij				(void) fputc('v', fp);
2789284194Sdelphij				break;
2790284194Sdelphij
2791284194Sdelphij			default:
2792284194Sdelphij				(void) fprintf(fp, "%.3o", c & 0377);
2793284194Sdelphij				break;
2794284194Sdelphij			}
2795284194Sdelphij		}
2796284194Sdelphij	}
2797284194Sdelphij}
2798284194Sdelphij
2799284194Sdelphij/*
2800284194Sdelphij * eatsize(): Eat the size spec from a number [eg. 10UL]
2801284194Sdelphij */
2802284194Sdelphijprivate void
2803284194Sdelphijeatsize(const char **p)
2804284194Sdelphij{
2805284194Sdelphij	const char *l = *p;
2806284194Sdelphij
2807284194Sdelphij	if (LOWCASE(*l) == 'u')
2808284194Sdelphij		l++;
2809284194Sdelphij
2810284194Sdelphij	switch (LOWCASE(*l)) {
2811284194Sdelphij	case 'l':    /* long */
2812284194Sdelphij	case 's':    /* short */
2813284194Sdelphij	case 'h':    /* short */
2814284194Sdelphij	case 'b':    /* char/byte */
2815284194Sdelphij	case 'c':    /* char/byte */
2816284194Sdelphij		l++;
2817284194Sdelphij		/*FALLTHROUGH*/
2818284194Sdelphij	default:
2819284194Sdelphij		break;
2820284194Sdelphij	}
2821284194Sdelphij
2822284194Sdelphij	*p = l;
2823284194Sdelphij}
2824284194Sdelphij
2825284194Sdelphij/*
2826284194Sdelphij * handle a buffer containing a compiled file.
2827284194Sdelphij */
2828284194Sdelphijprivate struct magic_map *
2829284194Sdelphijapprentice_buf(struct magic_set *ms, struct magic *buf, size_t len)
2830284194Sdelphij{
2831284194Sdelphij	struct magic_map *map;
2832284194Sdelphij
2833284194Sdelphij	if ((map = CAST(struct magic_map *, calloc(1, sizeof(*map)))) == NULL) {
2834284194Sdelphij		file_oomem(ms, sizeof(*map));
2835284194Sdelphij		return NULL;
2836284194Sdelphij	}
2837284194Sdelphij	map->len = len;
2838284194Sdelphij	map->p = buf;
2839284194Sdelphij	map->type = MAP_TYPE_USER;
2840284194Sdelphij	if (check_buffer(ms, map, "buffer") != 0) {
2841284194Sdelphij		apprentice_unmap(map);
2842284194Sdelphij		return NULL;
2843284194Sdelphij	}
2844284194Sdelphij	return map;
2845284194Sdelphij}
2846284194Sdelphij
2847284194Sdelphij/*
2848284194Sdelphij * handle a compiled file.
2849284194Sdelphij */
2850284194Sdelphij
2851284194Sdelphijprivate struct magic_map *
2852284194Sdelphijapprentice_map(struct magic_set *ms, const char *fn)
2853284194Sdelphij{
2854284194Sdelphij	int fd;
2855284194Sdelphij	struct stat st;
2856284194Sdelphij	char *dbname = NULL;
2857284194Sdelphij	struct magic_map *map;
2858284194Sdelphij
2859284194Sdelphij	fd = -1;
2860284194Sdelphij	if ((map = CAST(struct magic_map *, calloc(1, sizeof(*map)))) == NULL) {
2861284194Sdelphij		file_oomem(ms, sizeof(*map));
2862284194Sdelphij		goto error;
2863284194Sdelphij	}
2864284194Sdelphij
2865284194Sdelphij	dbname = mkdbname(ms, fn, 0);
2866284194Sdelphij	if (dbname == NULL)
2867284194Sdelphij		goto error;
2868284194Sdelphij
2869284194Sdelphij	if ((fd = open(dbname, O_RDONLY|O_BINARY)) == -1)
2870284194Sdelphij		goto error;
2871284194Sdelphij
2872284194Sdelphij	if (fstat(fd, &st) == -1) {
2873284194Sdelphij		file_error(ms, errno, "cannot stat `%s'", dbname);
2874284194Sdelphij		goto error;
2875284194Sdelphij	}
2876284194Sdelphij	if (st.st_size < 8 || st.st_size > MAXMAGIC_SIZE) {
2877284194Sdelphij		file_error(ms, 0, "file `%s' is too %s", dbname,
2878284194Sdelphij		    st.st_size < 8 ? "small" : "large");
2879284194Sdelphij		goto error;
2880284194Sdelphij	}
2881284194Sdelphij
2882284194Sdelphij	map->len = (size_t)st.st_size;
2883284194Sdelphij#ifdef QUICK
2884284194Sdelphij	if ((map->p = mmap(0, (size_t)st.st_size, PROT_READ|PROT_WRITE,
2885284194Sdelphij	    MAP_PRIVATE|MAP_FILE, fd, (off_t)0)) == MAP_FAILED) {
2886284194Sdelphij		file_error(ms, errno, "cannot map `%s'", dbname);
2887284194Sdelphij		goto error;
2888284194Sdelphij	}
2889284194Sdelphij	map->type = MAP_TYPE_MMAP;
2890284194Sdelphij#else
2891284194Sdelphij	if ((map->p = CAST(void *, malloc(map->len))) == NULL) {
2892284194Sdelphij		file_oomem(ms, map->len);
2893284194Sdelphij		goto error;
2894284194Sdelphij	}
2895284194Sdelphij	if (read(fd, map->p, map->len) != (ssize_t)map->len) {
2896284194Sdelphij		file_badread(ms);
2897284194Sdelphij		goto error;
2898284194Sdelphij	}
2899284194Sdelphij	map->type = MAP_TYPE_MALLOC;
2900284194Sdelphij#define RET	1
2901284194Sdelphij#endif
2902284194Sdelphij	(void)close(fd);
2903284194Sdelphij	fd = -1;
2904284194Sdelphij
2905284194Sdelphij	if (check_buffer(ms, map, dbname) != 0)
2906284194Sdelphij		goto error;
2907284194Sdelphij
2908284194Sdelphij	free(dbname);
2909284194Sdelphij	return map;
2910284194Sdelphij
2911284194Sdelphijerror:
2912284194Sdelphij	if (fd != -1)
2913284194Sdelphij		(void)close(fd);
2914284194Sdelphij	apprentice_unmap(map);
2915284194Sdelphij	free(dbname);
2916284194Sdelphij	return NULL;
2917284194Sdelphij}
2918284194Sdelphij
2919284194Sdelphijprivate int
2920284194Sdelphijcheck_buffer(struct magic_set *ms, struct magic_map *map, const char *dbname)
2921284194Sdelphij{
2922284194Sdelphij	uint32_t *ptr;
2923284194Sdelphij	uint32_t entries, nentries;
2924284194Sdelphij	uint32_t version;
2925284194Sdelphij	int i, needsbyteswap;
2926284194Sdelphij
2927284194Sdelphij	ptr = CAST(uint32_t *, map->p);
2928284194Sdelphij	if (*ptr != MAGICNO) {
2929284194Sdelphij		if (swap4(*ptr) != MAGICNO) {
2930284194Sdelphij			file_error(ms, 0, "bad magic in `%s'", dbname);
2931284194Sdelphij			return -1;
2932284194Sdelphij		}
2933284194Sdelphij		needsbyteswap = 1;
2934284194Sdelphij	} else
2935284194Sdelphij		needsbyteswap = 0;
2936284194Sdelphij	if (needsbyteswap)
2937284194Sdelphij		version = swap4(ptr[1]);
2938284194Sdelphij	else
2939284194Sdelphij		version = ptr[1];
2940284194Sdelphij	if (version != VERSIONNO) {
2941284194Sdelphij		file_error(ms, 0, "File %s supports only version %d magic "
2942284194Sdelphij		    "files. `%s' is version %d", VERSION,
2943284194Sdelphij		    VERSIONNO, dbname, version);
2944284194Sdelphij		return -1;
2945284194Sdelphij	}
2946284194Sdelphij	entries = (uint32_t)(map->len / sizeof(struct magic));
2947284194Sdelphij	if ((entries * sizeof(struct magic)) != map->len) {
2948284194Sdelphij		file_error(ms, 0, "Size of `%s' %" SIZE_T_FORMAT "u is not "
2949284194Sdelphij		    "a multiple of %" SIZE_T_FORMAT "u",
2950284194Sdelphij		    dbname, map->len, sizeof(struct magic));
2951284194Sdelphij		return -1;
2952284194Sdelphij	}
2953284194Sdelphij	map->magic[0] = CAST(struct magic *, map->p) + 1;
2954284194Sdelphij	nentries = 0;
2955284194Sdelphij	for (i = 0; i < MAGIC_SETS; i++) {
2956284194Sdelphij		if (needsbyteswap)
2957284194Sdelphij			map->nmagic[i] = swap4(ptr[i + 2]);
2958284194Sdelphij		else
2959284194Sdelphij			map->nmagic[i] = ptr[i + 2];
2960284194Sdelphij		if (i != MAGIC_SETS - 1)
2961284194Sdelphij			map->magic[i + 1] = map->magic[i] + map->nmagic[i];
2962284194Sdelphij		nentries += map->nmagic[i];
2963284194Sdelphij	}
2964284194Sdelphij	if (entries != nentries + 1) {
2965284194Sdelphij		file_error(ms, 0, "Inconsistent entries in `%s' %u != %u",
2966284194Sdelphij		    dbname, entries, nentries + 1);
2967284194Sdelphij		return -1;
2968284194Sdelphij	}
2969284194Sdelphij	if (needsbyteswap)
2970284194Sdelphij		for (i = 0; i < MAGIC_SETS; i++)
2971284194Sdelphij			byteswap(map->magic[i], map->nmagic[i]);
2972284194Sdelphij	return 0;
2973284194Sdelphij}
2974284194Sdelphij
2975284194Sdelphij/*
2976284194Sdelphij * handle an mmaped file.
2977284194Sdelphij */
2978284194Sdelphijprivate int
2979284194Sdelphijapprentice_compile(struct magic_set *ms, struct magic_map *map, const char *fn)
2980284194Sdelphij{
2981284194Sdelphij	static const size_t nm = sizeof(*map->nmagic) * MAGIC_SETS;
2982284194Sdelphij	static const size_t m = sizeof(**map->magic);
2983284194Sdelphij	int fd = -1;
2984284194Sdelphij	size_t len;
2985284194Sdelphij	char *dbname;
2986284194Sdelphij	int rv = -1;
2987284194Sdelphij	uint32_t i;
2988284194Sdelphij	union {
2989284194Sdelphij		struct magic m;
2990284194Sdelphij		uint32_t h[2 + MAGIC_SETS];
2991284194Sdelphij	} hdr;
2992284194Sdelphij
2993284194Sdelphij	dbname = mkdbname(ms, fn, 1);
2994284194Sdelphij
2995284194Sdelphij	if (dbname == NULL)
2996284194Sdelphij		goto out;
2997284194Sdelphij
2998284194Sdelphij	if ((fd = open(dbname, O_WRONLY|O_CREAT|O_TRUNC|O_BINARY, 0644)) == -1)
2999284194Sdelphij	{
3000284194Sdelphij		file_error(ms, errno, "cannot open `%s'", dbname);
3001284194Sdelphij		goto out;
3002284194Sdelphij	}
3003284194Sdelphij	memset(&hdr, 0, sizeof(hdr));
3004284194Sdelphij	hdr.h[0] = MAGICNO;
3005284194Sdelphij	hdr.h[1] = VERSIONNO;
3006284194Sdelphij	memcpy(hdr.h + 2, map->nmagic, nm);
3007284194Sdelphij
3008284194Sdelphij	if (write(fd, &hdr, sizeof(hdr)) != (ssize_t)sizeof(hdr)) {
3009284194Sdelphij		file_error(ms, errno, "error writing `%s'", dbname);
3010284194Sdelphij		goto out;
3011284194Sdelphij	}
3012284194Sdelphij
3013284194Sdelphij	for (i = 0; i < MAGIC_SETS; i++) {
3014284194Sdelphij		len = m * map->nmagic[i];
3015284194Sdelphij		if (write(fd, map->magic[i], len) != (ssize_t)len) {
3016284194Sdelphij			file_error(ms, errno, "error writing `%s'", dbname);
3017284194Sdelphij			goto out;
3018284194Sdelphij		}
3019284194Sdelphij	}
3020284194Sdelphij
3021284194Sdelphij	if (fd != -1)
3022284194Sdelphij		(void)close(fd);
3023284194Sdelphij	rv = 0;
3024284194Sdelphijout:
3025284194Sdelphij	free(dbname);
3026284194Sdelphij	return rv;
3027284194Sdelphij}
3028284194Sdelphij
3029284194Sdelphijprivate const char ext[] = ".mgc";
3030284194Sdelphij/*
3031284194Sdelphij * make a dbname
3032284194Sdelphij */
3033284194Sdelphijprivate char *
3034284194Sdelphijmkdbname(struct magic_set *ms, const char *fn, int strip)
3035284194Sdelphij{
3036284194Sdelphij	const char *p, *q;
3037284194Sdelphij	char *buf;
3038284194Sdelphij
3039284194Sdelphij	if (strip) {
3040284194Sdelphij		if ((p = strrchr(fn, '/')) != NULL)
3041284194Sdelphij			fn = ++p;
3042284194Sdelphij	}
3043284194Sdelphij
3044284194Sdelphij	for (q = fn; *q; q++)
3045284194Sdelphij		continue;
3046284194Sdelphij	/* Look for .mgc */
3047284194Sdelphij	for (p = ext + sizeof(ext) - 1; p >= ext && q >= fn; p--, q--)
3048284194Sdelphij		if (*p != *q)
3049284194Sdelphij			break;
3050284194Sdelphij
3051284194Sdelphij	/* Did not find .mgc, restore q */
3052284194Sdelphij	if (p >= ext)
3053284194Sdelphij		while (*q)
3054284194Sdelphij			q++;
3055284194Sdelphij
3056284194Sdelphij	q++;
3057284194Sdelphij	/* Compatibility with old code that looked in .mime */
3058284194Sdelphij	if (ms->flags & MAGIC_MIME) {
3059284194Sdelphij		if (asprintf(&buf, "%.*s.mime%s", (int)(q - fn), fn, ext) < 0)
3060284194Sdelphij			return NULL;
3061284194Sdelphij		if (access(buf, R_OK) != -1) {
3062284194Sdelphij			ms->flags &= MAGIC_MIME_TYPE;
3063284194Sdelphij			return buf;
3064284194Sdelphij		}
3065284194Sdelphij		free(buf);
3066284194Sdelphij	}
3067284194Sdelphij	if (asprintf(&buf, "%.*s%s", (int)(q - fn), fn, ext) < 0)
3068284194Sdelphij		return NULL;
3069284194Sdelphij
3070284194Sdelphij	/* Compatibility with old code that looked in .mime */
3071284194Sdelphij	if (strstr(p, ".mime") != NULL)
3072284194Sdelphij		ms->flags &= MAGIC_MIME_TYPE;
3073284194Sdelphij	return buf;
3074284194Sdelphij}
3075284194Sdelphij
3076284194Sdelphij/*
3077284194Sdelphij * Byteswap an mmap'ed file if needed
3078284194Sdelphij */
3079284194Sdelphijprivate void
3080284194Sdelphijbyteswap(struct magic *magic, uint32_t nmagic)
3081284194Sdelphij{
3082284194Sdelphij	uint32_t i;
3083284194Sdelphij	for (i = 0; i < nmagic; i++)
3084284194Sdelphij		bs1(&magic[i]);
3085284194Sdelphij}
3086284194Sdelphij
3087284194Sdelphij/*
3088284194Sdelphij * swap a short
3089284194Sdelphij */
3090284194Sdelphijprivate uint16_t
3091284194Sdelphijswap2(uint16_t sv)
3092284194Sdelphij{
3093284194Sdelphij	uint16_t rv;
3094284194Sdelphij	uint8_t *s = (uint8_t *)(void *)&sv;
3095284194Sdelphij	uint8_t *d = (uint8_t *)(void *)&rv;
3096284194Sdelphij	d[0] = s[1];
3097284194Sdelphij	d[1] = s[0];
3098284194Sdelphij	return rv;
3099284194Sdelphij}
3100284194Sdelphij
3101284194Sdelphij/*
3102284194Sdelphij * swap an int
3103284194Sdelphij */
3104284194Sdelphijprivate uint32_t
3105284194Sdelphijswap4(uint32_t sv)
3106284194Sdelphij{
3107284194Sdelphij	uint32_t rv;
3108284194Sdelphij	uint8_t *s = (uint8_t *)(void *)&sv;
3109284194Sdelphij	uint8_t *d = (uint8_t *)(void *)&rv;
3110284194Sdelphij	d[0] = s[3];
3111284194Sdelphij	d[1] = s[2];
3112284194Sdelphij	d[2] = s[1];
3113284194Sdelphij	d[3] = s[0];
3114284194Sdelphij	return rv;
3115284194Sdelphij}
3116284194Sdelphij
3117284194Sdelphij/*
3118284194Sdelphij * swap a quad
3119284194Sdelphij */
3120284194Sdelphijprivate uint64_t
3121284194Sdelphijswap8(uint64_t sv)
3122284194Sdelphij{
3123284194Sdelphij	uint64_t rv;
3124284194Sdelphij	uint8_t *s = (uint8_t *)(void *)&sv;
3125284194Sdelphij	uint8_t *d = (uint8_t *)(void *)&rv;
3126284194Sdelphij#if 0
3127284194Sdelphij	d[0] = s[3];
3128284194Sdelphij	d[1] = s[2];
3129284194Sdelphij	d[2] = s[1];
3130284194Sdelphij	d[3] = s[0];
3131284194Sdelphij	d[4] = s[7];
3132284194Sdelphij	d[5] = s[6];
3133284194Sdelphij	d[6] = s[5];
3134284194Sdelphij	d[7] = s[4];
3135284194Sdelphij#else
3136284194Sdelphij	d[0] = s[7];
3137284194Sdelphij	d[1] = s[6];
3138284194Sdelphij	d[2] = s[5];
3139284194Sdelphij	d[3] = s[4];
3140284194Sdelphij	d[4] = s[3];
3141284194Sdelphij	d[5] = s[2];
3142284194Sdelphij	d[6] = s[1];
3143284194Sdelphij	d[7] = s[0];
3144284194Sdelphij#endif
3145284194Sdelphij	return rv;
3146284194Sdelphij}
3147284194Sdelphij
3148284194Sdelphij/*
3149284194Sdelphij * byteswap a single magic entry
3150284194Sdelphij */
3151284194Sdelphijprivate void
3152284194Sdelphijbs1(struct magic *m)
3153284194Sdelphij{
3154284194Sdelphij	m->cont_level = swap2(m->cont_level);
3155284194Sdelphij	m->offset = swap4((uint32_t)m->offset);
3156284194Sdelphij	m->in_offset = swap4((uint32_t)m->in_offset);
3157284194Sdelphij	m->lineno = swap4((uint32_t)m->lineno);
3158284194Sdelphij	if (IS_STRING(m->type)) {
3159284194Sdelphij		m->str_range = swap4(m->str_range);
3160284194Sdelphij		m->str_flags = swap4(m->str_flags);
3161284194Sdelphij	}
3162284194Sdelphij	else {
3163284194Sdelphij		m->value.q = swap8(m->value.q);
3164284194Sdelphij		m->num_mask = swap8(m->num_mask);
3165284194Sdelphij	}
3166284194Sdelphij}
3167284194Sdelphij
3168284194Sdelphijprotected size_t
3169284194Sdelphijfile_pstring_length_size(const struct magic *m)
3170284194Sdelphij{
3171284194Sdelphij	switch (m->str_flags & PSTRING_LEN) {
3172284194Sdelphij	case PSTRING_1_LE:
3173284194Sdelphij		return 1;
3174284194Sdelphij	case PSTRING_2_LE:
3175284194Sdelphij	case PSTRING_2_BE:
3176284194Sdelphij		return 2;
3177284194Sdelphij	case PSTRING_4_LE:
3178284194Sdelphij	case PSTRING_4_BE:
3179284194Sdelphij		return 4;
3180284194Sdelphij	default:
3181284194Sdelphij		abort();	/* Impossible */
3182284194Sdelphij		return 1;
3183284194Sdelphij	}
3184284194Sdelphij}
3185284194Sdelphijprotected size_t
3186284194Sdelphijfile_pstring_get_length(const struct magic *m, const char *s)
3187284194Sdelphij{
3188284194Sdelphij	size_t len = 0;
3189284194Sdelphij
3190284194Sdelphij	switch (m->str_flags & PSTRING_LEN) {
3191284194Sdelphij	case PSTRING_1_LE:
3192284194Sdelphij		len = *s;
3193284194Sdelphij		break;
3194284194Sdelphij	case PSTRING_2_LE:
3195284194Sdelphij		len = (s[1] << 8) | s[0];
3196284194Sdelphij		break;
3197284194Sdelphij	case PSTRING_2_BE:
3198284194Sdelphij		len = (s[0] << 8) | s[1];
3199284194Sdelphij		break;
3200284194Sdelphij	case PSTRING_4_LE:
3201284194Sdelphij		len = (s[3] << 24) | (s[2] << 16) | (s[1] << 8) | s[0];
3202284194Sdelphij		break;
3203284194Sdelphij	case PSTRING_4_BE:
3204284194Sdelphij		len = (s[0] << 24) | (s[1] << 16) | (s[2] << 8) | s[3];
3205284194Sdelphij		break;
3206284194Sdelphij	default:
3207284194Sdelphij		abort();	/* Impossible */
3208284194Sdelphij	}
3209284194Sdelphij
3210284194Sdelphij	if (m->str_flags & PSTRING_LENGTH_INCLUDES_ITSELF)
3211284194Sdelphij		len -= file_pstring_length_size(m);
3212284194Sdelphij
3213284194Sdelphij	return len;
3214284194Sdelphij}
3215284194Sdelphij
3216284194Sdelphijprotected int
3217284194Sdelphijfile_magicfind(struct magic_set *ms, const char *name, struct mlist *v)
3218284194Sdelphij{
3219284194Sdelphij	uint32_t i, j;
3220284194Sdelphij	struct mlist *mlist, *ml;
3221284194Sdelphij
3222284194Sdelphij	mlist = ms->mlist[1];
3223284194Sdelphij
3224284194Sdelphij	for (ml = mlist->next; ml != mlist; ml = ml->next) {
3225284194Sdelphij		struct magic *ma = ml->magic;
3226284194Sdelphij		uint32_t nma = ml->nmagic;
3227284194Sdelphij		for (i = 0; i < nma; i++) {
3228284194Sdelphij			if (ma[i].type != FILE_NAME)
3229284194Sdelphij				continue;
3230284194Sdelphij			if (strcmp(ma[i].value.s, name) == 0) {
3231284194Sdelphij				v->magic = &ma[i];
3232284194Sdelphij				for (j = i + 1; j < nma; j++)
3233284194Sdelphij				    if (ma[j].cont_level == 0)
3234284194Sdelphij					    break;
3235284194Sdelphij				v->nmagic = j - i;
3236284194Sdelphij				return 0;
3237284194Sdelphij			}
3238284194Sdelphij		}
3239284194Sdelphij	}
3240284194Sdelphij	return -1;
3241284194Sdelphij}
3242