168349Sobrien/*
2133359Sobrien * Copyright (c) Ian F. Darwin 1986-1995.
3133359Sobrien * Software written by Ian F. Darwin and others;
4133359Sobrien * maintained 1995-present by Christos Zoulas and others.
5133359Sobrien *
6133359Sobrien * Redistribution and use in source and binary forms, with or without
7133359Sobrien * modification, are permitted provided that the following conditions
8133359Sobrien * are met:
9133359Sobrien * 1. Redistributions of source code must retain the above copyright
10133359Sobrien *    notice immediately at the beginning of the file, without modification,
11133359Sobrien *    this list of conditions, and the following disclaimer.
12133359Sobrien * 2. Redistributions in binary form must reproduce the above copyright
13133359Sobrien *    notice, this list of conditions and the following disclaimer in the
14133359Sobrien *    documentation and/or other materials provided with the distribution.
15133359Sobrien *
16133359Sobrien * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17133359Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18133359Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19133359Sobrien * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
20133359Sobrien * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21133359Sobrien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22133359Sobrien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23133359Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24133359Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25133359Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26133359Sobrien * SUCH DAMAGE.
27133359Sobrien */
28133359Sobrien/*
2968349Sobrien * apprentice - make one pass through /etc/magic, learning its secrets.
3068349Sobrien */
3168349Sobrien
3280588Sobrien#include "file.h"
33191736Sobrien
34191736Sobrien#ifndef	lint
35330569SgordonFILE_RCSID("@(#)$File: apprentice.c,v 1.262 2017/08/28 13:39:18 christos Exp $")
36191736Sobrien#endif	/* lint */
37191736Sobrien
38133359Sobrien#include "magic.h"
3968349Sobrien#include <stdlib.h>
4084685Sobrien#ifdef HAVE_UNISTD_H
4184685Sobrien#include <unistd.h>
4284685Sobrien#endif
43267843Sdelphij#ifdef HAVE_STDDEF_H
44267843Sdelphij#include <stddef.h>
45267843Sdelphij#endif
4668349Sobrien#include <string.h>
47169942Sobrien#include <assert.h>
4868349Sobrien#include <ctype.h>
49103373Sobrien#include <fcntl.h>
5074784Sobrien#ifdef QUICK
5174784Sobrien#include <sys/mman.h>
5274784Sobrien#endif
53186690Sobrien#include <dirent.h>
54267843Sdelphij#if defined(HAVE_LIMITS_H)
55267843Sdelphij#include <limits.h>
56267843Sdelphij#endif
5768349Sobrien
58267843Sdelphij#ifndef SSIZE_MAX
59267843Sdelphij#define MAXMAGIC_SIZE        ((ssize_t)0x7fffffff)
60267843Sdelphij#else
61267843Sdelphij#define MAXMAGIC_SIZE        SSIZE_MAX
62267843Sdelphij#endif
63267843Sdelphij
6468349Sobrien#define	EATAB {while (isascii((unsigned char) *l) && \
6568349Sobrien		      isspace((unsigned char) *l))  ++l;}
6668349Sobrien#define LOWCASE(l) (isupper((unsigned char) (l)) ? \
6768349Sobrien			tolower((unsigned char) (l)) : (l))
6875937Sobrien/*
6975937Sobrien * Work around a bug in headers on Digital Unix.
7075937Sobrien * At least confirmed for: OSF1 V4.0 878
7175937Sobrien */
7275937Sobrien#if defined(__osf__) && defined(__DECC)
7375937Sobrien#ifdef MAP_FAILED
7475937Sobrien#undef MAP_FAILED
7575937Sobrien#endif
7675937Sobrien#endif
7768349Sobrien
7875937Sobrien#ifndef MAP_FAILED
7975937Sobrien#define MAP_FAILED (void *) -1
8075937Sobrien#endif
8168349Sobrien
8275937Sobrien#ifndef MAP_FILE
8375937Sobrien#define MAP_FILE 0
8475937Sobrien#endif
8575937Sobrien
86267843Sdelphij#define ALLOC_CHUNK	(size_t)10
87267843Sdelphij#define ALLOC_INCR	(size_t)200
88267843Sdelphij
89330569Sgordon#define MAP_TYPE_USER	0
90276415Sdelphij#define MAP_TYPE_MALLOC	1
91330569Sgordon#define MAP_TYPE_MMAP	2
92276415Sdelphij
93159764Sobrienstruct magic_entry {
94159764Sobrien	struct magic *mp;
95159764Sobrien	uint32_t cont_count;
96159764Sobrien	uint32_t max_count;
97159764Sobrien};
98159764Sobrien
99267843Sdelphijstruct magic_entry_set {
100267843Sdelphij	struct magic_entry *me;
101267843Sdelphij	uint32_t count;
102267843Sdelphij	uint32_t max;
103267843Sdelphij};
104267843Sdelphij
105267843Sdelphijstruct magic_map {
106267843Sdelphij	void *p;
107267843Sdelphij	size_t len;
108276415Sdelphij	int type;
109267843Sdelphij	struct magic *magic[MAGIC_SETS];
110267843Sdelphij	uint32_t nmagic[MAGIC_SETS];
111267843Sdelphij};
112267843Sdelphij
113169962Sobrienint file_formats[FILE_NAMES_SIZE];
114169962Sobrienconst size_t file_nformats = FILE_NAMES_SIZE;
115169962Sobrienconst char *file_names[FILE_NAMES_SIZE];
116169962Sobrienconst size_t file_nnames = FILE_NAMES_SIZE;
117169942Sobrien
118169962Sobrienprivate int getvalue(struct magic_set *ms, struct magic *, const char **, int);
119133359Sobrienprivate int hextoint(int);
120192348Sdelphijprivate const char *getstr(struct magic_set *, struct magic *, const char *,
121192348Sdelphij    int);
122267843Sdelphijprivate int parse(struct magic_set *, struct magic_entry *, const char *,
123267843Sdelphij    size_t, int);
124159764Sobrienprivate void eatsize(const char **);
125267843Sdelphijprivate int apprentice_1(struct magic_set *, const char *, int);
126159764Sobrienprivate size_t apprentice_magic_strength(const struct magic *);
127159764Sobrienprivate int apprentice_sort(const void *, const void *);
128226048Sobrienprivate void apprentice_list(struct mlist *, int );
129267843Sdelphijprivate struct magic_map *apprentice_load(struct magic_set *,
130133359Sobrien    const char *, int);
131267843Sdelphijprivate struct mlist *mlist_alloc(void);
132267843Sdelphijprivate void mlist_free(struct mlist *);
133133359Sobrienprivate void byteswap(struct magic *, uint32_t);
134133359Sobrienprivate void bs1(struct magic *);
135133359Sobrienprivate uint16_t swap2(uint16_t);
136133359Sobrienprivate uint32_t swap4(uint32_t);
137169942Sobrienprivate uint64_t swap8(uint64_t);
138191736Sobrienprivate char *mkdbname(struct magic_set *, const char *, int);
139276415Sdelphijprivate struct magic_map *apprentice_buf(struct magic_set *, struct magic *,
140276415Sdelphij    size_t);
141267843Sdelphijprivate struct magic_map *apprentice_map(struct magic_set *, const char *);
142276415Sdelphijprivate int check_buffer(struct magic_set *, struct magic_map *, const char *);
143267843Sdelphijprivate void apprentice_unmap(struct magic_map *);
144267843Sdelphijprivate int apprentice_compile(struct magic_set *, struct magic_map *,
145133359Sobrien    const char *);
146330569Sgordonprivate int check_format_type(const char *, int, const char **);
147139368Sobrienprivate int check_format(struct magic_set *, struct magic *);
148175296Sobrienprivate int get_op(char);
149186690Sobrienprivate int parse_mime(struct magic_set *, struct magic_entry *, const char *);
150191736Sobrienprivate int parse_strength(struct magic_set *, struct magic_entry *, const char *);
151191736Sobrienprivate int parse_apple(struct magic_set *, struct magic_entry *, const char *);
152284778Sdelphijprivate int parse_ext(struct magic_set *, struct magic_entry *, const char *);
15368349Sobrien
154186690Sobrien
155133359Sobrienprivate size_t magicsize = sizeof(struct magic);
15668349Sobrien
157186690Sobrienprivate const char usg_hdr[] = "cont\toffset\ttype\topcode\tmask\tvalue\tdesc";
158159764Sobrien
159186690Sobrienprivate struct {
160186690Sobrien	const char *name;
161186690Sobrien	size_t len;
162186690Sobrien	int (*fun)(struct magic_set *, struct magic_entry *, const char *);
163186690Sobrien} bang[] = {
164186690Sobrien#define	DECLARE_FIELD(name) { # name, sizeof(# name) - 1, parse_ ## name }
165186690Sobrien	DECLARE_FIELD(mime),
166191736Sobrien	DECLARE_FIELD(apple),
167284778Sdelphij	DECLARE_FIELD(ext),
168186690Sobrien	DECLARE_FIELD(strength),
169186690Sobrien#undef	DECLARE_FIELD
170186690Sobrien	{ NULL, 0, NULL }
171186690Sobrien};
172186690Sobrien
17380588Sobrien#ifdef COMPILE_ONLY
17474784Sobrien
175103373Sobrienint main(int, char *[]);
17680588Sobrien
17780588Sobrienint
178103373Sobrienmain(int argc, char *argv[])
17980588Sobrien{
18080588Sobrien	int ret;
181133359Sobrien	struct magic_set *ms;
182133359Sobrien	char *progname;
18380588Sobrien
18480588Sobrien	if ((progname = strrchr(argv[0], '/')) != NULL)
18580588Sobrien		progname++;
18680588Sobrien	else
18780588Sobrien		progname = argv[0];
18880588Sobrien
18980588Sobrien	if (argc != 2) {
190133359Sobrien		(void)fprintf(stderr, "Usage: %s file\n", progname);
191133359Sobrien		return 1;
19280588Sobrien	}
19380588Sobrien
194133359Sobrien	if ((ms = magic_open(MAGIC_CHECK)) == NULL) {
195133359Sobrien		(void)fprintf(stderr, "%s: %s\n", progname, strerror(errno));
196133359Sobrien		return 1;
197133359Sobrien	}
198133359Sobrien	ret = magic_compile(ms, argv[1]) == -1 ? 1 : 0;
199133359Sobrien	if (ret == 1)
200133359Sobrien		(void)fprintf(stderr, "%s: %s\n", progname, magic_error(ms));
201133359Sobrien	magic_close(ms);
202133359Sobrien	return ret;
20380588Sobrien}
20480588Sobrien#endif /* COMPILE_ONLY */
20580588Sobrien
206267843Sdelphijstruct type_tbl_s {
207186690Sobrien	const char name[16];
208169962Sobrien	const size_t len;
209169962Sobrien	const int type;
210169962Sobrien	const int format;
211267843Sdelphij};
212267843Sdelphij
213267843Sdelphij/*
214267843Sdelphij * XXX - the actual Single UNIX Specification says that "long" means "long",
215267843Sdelphij * as in the C data type, but we treat it as meaning "4-byte integer".
216267843Sdelphij * Given that the OS X version of file 5.04 did the same, I guess that passes
217267843Sdelphij * the actual test; having "long" be dependent on how big a "long" is on
218267843Sdelphij * the machine running "file" is silly.
219267843Sdelphij */
220267843Sdelphijstatic const struct type_tbl_s type_tbl[] = {
221169962Sobrien# define XX(s)		s, (sizeof(s) - 1)
222186690Sobrien# define XX_NULL	"", 0
223267843Sdelphij	{ XX("invalid"),	FILE_INVALID,		FILE_FMT_NONE },
224169962Sobrien	{ XX("byte"),		FILE_BYTE,		FILE_FMT_NUM },
225169962Sobrien	{ XX("short"),		FILE_SHORT,		FILE_FMT_NUM },
226267843Sdelphij	{ XX("default"),	FILE_DEFAULT,		FILE_FMT_NONE },
227169962Sobrien	{ XX("long"),		FILE_LONG,		FILE_FMT_NUM },
228169962Sobrien	{ XX("string"),		FILE_STRING,		FILE_FMT_STR },
229169962Sobrien	{ XX("date"),		FILE_DATE,		FILE_FMT_STR },
230169962Sobrien	{ XX("beshort"),	FILE_BESHORT,		FILE_FMT_NUM },
231169962Sobrien	{ XX("belong"),		FILE_BELONG,		FILE_FMT_NUM },
232169962Sobrien	{ XX("bedate"),		FILE_BEDATE,		FILE_FMT_STR },
233169962Sobrien	{ XX("leshort"),	FILE_LESHORT,		FILE_FMT_NUM },
234169962Sobrien	{ XX("lelong"),		FILE_LELONG,		FILE_FMT_NUM },
235169962Sobrien	{ XX("ledate"),		FILE_LEDATE,		FILE_FMT_STR },
236169962Sobrien	{ XX("pstring"),	FILE_PSTRING,		FILE_FMT_STR },
237169962Sobrien	{ XX("ldate"),		FILE_LDATE,		FILE_FMT_STR },
238169962Sobrien	{ XX("beldate"),	FILE_BELDATE,		FILE_FMT_STR },
239169962Sobrien	{ XX("leldate"),	FILE_LELDATE,		FILE_FMT_STR },
240169962Sobrien	{ XX("regex"),		FILE_REGEX,		FILE_FMT_STR },
241169962Sobrien	{ XX("bestring16"),	FILE_BESTRING16,	FILE_FMT_STR },
242169962Sobrien	{ XX("lestring16"),	FILE_LESTRING16,	FILE_FMT_STR },
243169962Sobrien	{ XX("search"),		FILE_SEARCH,		FILE_FMT_STR },
244169962Sobrien	{ XX("medate"),		FILE_MEDATE,		FILE_FMT_STR },
245169962Sobrien	{ XX("meldate"),	FILE_MELDATE,		FILE_FMT_STR },
246169962Sobrien	{ XX("melong"),		FILE_MELONG,		FILE_FMT_NUM },
247169962Sobrien	{ XX("quad"),		FILE_QUAD,		FILE_FMT_QUAD },
248169962Sobrien	{ XX("lequad"),		FILE_LEQUAD,		FILE_FMT_QUAD },
249169962Sobrien	{ XX("bequad"),		FILE_BEQUAD,		FILE_FMT_QUAD },
250169962Sobrien	{ XX("qdate"),		FILE_QDATE,		FILE_FMT_STR },
251169962Sobrien	{ XX("leqdate"),	FILE_LEQDATE,		FILE_FMT_STR },
252169962Sobrien	{ XX("beqdate"),	FILE_BEQDATE,		FILE_FMT_STR },
253169962Sobrien	{ XX("qldate"),		FILE_QLDATE,		FILE_FMT_STR },
254169962Sobrien	{ XX("leqldate"),	FILE_LEQLDATE,		FILE_FMT_STR },
255169962Sobrien	{ XX("beqldate"),	FILE_BEQLDATE,		FILE_FMT_STR },
256175296Sobrien	{ XX("float"),		FILE_FLOAT,		FILE_FMT_FLOAT },
257175296Sobrien	{ XX("befloat"),	FILE_BEFLOAT,		FILE_FMT_FLOAT },
258175296Sobrien	{ XX("lefloat"),	FILE_LEFLOAT,		FILE_FMT_FLOAT },
259175296Sobrien	{ XX("double"),		FILE_DOUBLE,		FILE_FMT_DOUBLE },
260175296Sobrien	{ XX("bedouble"),	FILE_BEDOUBLE,		FILE_FMT_DOUBLE },
261175296Sobrien	{ XX("ledouble"),	FILE_LEDOUBLE,		FILE_FMT_DOUBLE },
262191736Sobrien	{ XX("leid3"),		FILE_LEID3,		FILE_FMT_NUM },
263191736Sobrien	{ XX("beid3"),		FILE_BEID3,		FILE_FMT_NUM },
264267843Sdelphij	{ XX("indirect"),	FILE_INDIRECT,		FILE_FMT_NUM },
265267843Sdelphij	{ XX("qwdate"),		FILE_QWDATE,		FILE_FMT_STR },
266267843Sdelphij	{ XX("leqwdate"),	FILE_LEQWDATE,		FILE_FMT_STR },
267267843Sdelphij	{ XX("beqwdate"),	FILE_BEQWDATE,		FILE_FMT_STR },
268267843Sdelphij	{ XX("name"),		FILE_NAME,		FILE_FMT_NONE },
269267843Sdelphij	{ XX("use"),		FILE_USE,		FILE_FMT_NONE },
270267843Sdelphij	{ XX("clear"),		FILE_CLEAR,		FILE_FMT_NONE },
271330569Sgordon	{ XX("der"),		FILE_DER,		FILE_FMT_STR },
272169962Sobrien	{ XX_NULL,		FILE_INVALID,		FILE_FMT_NONE },
273267843Sdelphij};
274267843Sdelphij
275267843Sdelphij/*
276267843Sdelphij * These are not types, and cannot be preceded by "u" to make them
277267843Sdelphij * unsigned.
278267843Sdelphij */
279267843Sdelphijstatic const struct type_tbl_s special_tbl[] = {
280330569Sgordon	{ XX("der"),		FILE_DER,		FILE_FMT_STR },
281267843Sdelphij	{ XX("name"),		FILE_NAME,		FILE_FMT_STR },
282267843Sdelphij	{ XX("use"),		FILE_USE,		FILE_FMT_STR },
283267843Sdelphij	{ XX_NULL,		FILE_INVALID,		FILE_FMT_NONE },
284267843Sdelphij};
285169962Sobrien# undef XX
286169962Sobrien# undef XX_NULL
28780588Sobrien
288169962Sobrienprivate int
289267843Sdelphijget_type(const struct type_tbl_s *tbl, const char *l, const char **t)
290169962Sobrien{
291169962Sobrien	const struct type_tbl_s *p;
292169962Sobrien
293267843Sdelphij	for (p = tbl; p->len; p++) {
294169962Sobrien		if (strncmp(l, p->name, p->len) == 0) {
295169962Sobrien			if (t)
296169962Sobrien				*t = l + p->len;
297169962Sobrien			break;
298169962Sobrien		}
299169962Sobrien	}
300169962Sobrien	return p->type;
301169962Sobrien}
302169962Sobrien
303267843Sdelphijprivate int
304267843Sdelphijget_standard_integer_type(const char *l, const char **t)
305267843Sdelphij{
306267843Sdelphij	int type;
307267843Sdelphij
308267843Sdelphij	if (isalpha((unsigned char)l[1])) {
309267843Sdelphij		switch (l[1]) {
310267843Sdelphij		case 'C':
311267843Sdelphij			/* "dC" and "uC" */
312267843Sdelphij			type = FILE_BYTE;
313267843Sdelphij			break;
314267843Sdelphij		case 'S':
315267843Sdelphij			/* "dS" and "uS" */
316267843Sdelphij			type = FILE_SHORT;
317267843Sdelphij			break;
318267843Sdelphij		case 'I':
319267843Sdelphij		case 'L':
320267843Sdelphij			/*
321267843Sdelphij			 * "dI", "dL", "uI", and "uL".
322267843Sdelphij			 *
323267843Sdelphij			 * XXX - the actual Single UNIX Specification says
324267843Sdelphij			 * that "L" means "long", as in the C data type,
325267843Sdelphij			 * but we treat it as meaning "4-byte integer".
326267843Sdelphij			 * Given that the OS X version of file 5.04 did
327267843Sdelphij			 * the same, I guess that passes the actual SUS
328267843Sdelphij			 * validation suite; having "dL" be dependent on
329267843Sdelphij			 * how big a "long" is on the machine running
330267843Sdelphij			 * "file" is silly.
331267843Sdelphij			 */
332267843Sdelphij			type = FILE_LONG;
333267843Sdelphij			break;
334267843Sdelphij		case 'Q':
335267843Sdelphij			/* "dQ" and "uQ" */
336267843Sdelphij			type = FILE_QUAD;
337267843Sdelphij			break;
338267843Sdelphij		default:
339267843Sdelphij			/* "d{anything else}", "u{anything else}" */
340267843Sdelphij			return FILE_INVALID;
341267843Sdelphij		}
342267843Sdelphij		l += 2;
343267843Sdelphij	} else if (isdigit((unsigned char)l[1])) {
344267843Sdelphij		/*
345267843Sdelphij		 * "d{num}" and "u{num}"; we only support {num} values
346267843Sdelphij		 * of 1, 2, 4, and 8 - the Single UNIX Specification
347267843Sdelphij		 * doesn't say anything about whether arbitrary
348267843Sdelphij		 * values should be supported, but both the Solaris 10
349267843Sdelphij		 * and OS X Mountain Lion versions of file passed the
350267843Sdelphij		 * Single UNIX Specification validation suite, and
351267843Sdelphij		 * neither of them support values bigger than 8 or
352267843Sdelphij		 * non-power-of-2 values.
353267843Sdelphij		 */
354267843Sdelphij		if (isdigit((unsigned char)l[2])) {
355267843Sdelphij			/* Multi-digit, so > 9 */
356267843Sdelphij			return FILE_INVALID;
357267843Sdelphij		}
358267843Sdelphij		switch (l[1]) {
359267843Sdelphij		case '1':
360267843Sdelphij			type = FILE_BYTE;
361267843Sdelphij			break;
362267843Sdelphij		case '2':
363267843Sdelphij			type = FILE_SHORT;
364267843Sdelphij			break;
365267843Sdelphij		case '4':
366267843Sdelphij			type = FILE_LONG;
367267843Sdelphij			break;
368267843Sdelphij		case '8':
369267843Sdelphij			type = FILE_QUAD;
370267843Sdelphij			break;
371267843Sdelphij		default:
372267843Sdelphij			/* XXX - what about 3, 5, 6, or 7? */
373267843Sdelphij			return FILE_INVALID;
374267843Sdelphij		}
375267843Sdelphij		l += 2;
376267843Sdelphij	} else {
377267843Sdelphij		/*
378267843Sdelphij		 * "d" or "u" by itself.
379267843Sdelphij		 */
380267843Sdelphij		type = FILE_LONG;
381267843Sdelphij		++l;
382267843Sdelphij	}
383267843Sdelphij	if (t)
384267843Sdelphij		*t = l;
385267843Sdelphij	return type;
386267843Sdelphij}
387267843Sdelphij
388169962Sobrienprivate void
389169962Sobrieninit_file_tables(void)
390169962Sobrien{
391169962Sobrien	static int done = 0;
392169962Sobrien	const struct type_tbl_s *p;
393169962Sobrien
394169962Sobrien	if (done)
395169962Sobrien		return;
396169962Sobrien	done++;
397169962Sobrien
398186690Sobrien	for (p = type_tbl; p->len; p++) {
399169962Sobrien		assert(p->type < FILE_NAMES_SIZE);
400169962Sobrien		file_names[p->type] = p->name;
401169962Sobrien		file_formats[p->type] = p->format;
402169962Sobrien	}
403267843Sdelphij	assert(p - type_tbl == FILE_NAMES_SIZE);
404169962Sobrien}
405169962Sobrien
406267843Sdelphijprivate int
407267843Sdelphijadd_mlist(struct mlist *mlp, struct magic_map *map, size_t idx)
408267843Sdelphij{
409267843Sdelphij	struct mlist *ml;
410267843Sdelphij
411330569Sgordon	mlp->map = NULL;
412267843Sdelphij	if ((ml = CAST(struct mlist *, malloc(sizeof(*ml)))) == NULL)
413267843Sdelphij		return -1;
414267843Sdelphij
415330569Sgordon	ml->map = idx == 0 ? map : NULL;
416267843Sdelphij	ml->magic = map->magic[idx];
417267843Sdelphij	ml->nmagic = map->nmagic[idx];
418267843Sdelphij
419267843Sdelphij	mlp->prev->next = ml;
420267843Sdelphij	ml->prev = mlp->prev;
421267843Sdelphij	ml->next = mlp;
422267843Sdelphij	mlp->prev = ml;
423267843Sdelphij	return 0;
424267843Sdelphij}
425267843Sdelphij
42674784Sobrien/*
427186690Sobrien * Handle one file or directory.
42874784Sobrien */
429133359Sobrienprivate int
430267843Sdelphijapprentice_1(struct magic_set *ms, const char *fn, int action)
43174784Sobrien{
432276415Sdelphij	struct magic_map *map;
433267897Sdelphij#ifndef COMPILE_ONLY
43474784Sobrien	struct mlist *ml;
435267843Sdelphij	size_t i;
436276415Sdelphij#endif
43774784Sobrien
438133359Sobrien	if (magicsize != FILE_MAGICSIZE) {
439133359Sobrien		file_error(ms, 0, "magic element size %lu != %lu",
440267843Sdelphij		    (unsigned long)sizeof(*map->magic[0]),
441133359Sobrien		    (unsigned long)FILE_MAGICSIZE);
442133359Sobrien		return -1;
44374784Sobrien	}
444133359Sobrien
445133359Sobrien	if (action == FILE_COMPILE) {
446267843Sdelphij		map = apprentice_load(ms, fn, action);
447267843Sdelphij		if (map == NULL)
448133359Sobrien			return -1;
449267843Sdelphij		return apprentice_compile(ms, map, fn);
450133359Sobrien	}
451159764Sobrien
45280588Sobrien#ifndef COMPILE_ONLY
453267843Sdelphij	map = apprentice_map(ms, fn);
454330569Sgordon	if (map == (struct magic_map *)-1)
455330569Sgordon		return -1;
456267843Sdelphij	if (map == NULL) {
457133359Sobrien		if (ms->flags & MAGIC_CHECK)
458139368Sobrien			file_magwarn(ms, "using regular magic file `%s'", fn);
459267843Sdelphij		map = apprentice_load(ms, fn, action);
460267843Sdelphij		if (map == NULL)
461133359Sobrien			return -1;
462133359Sobrien	}
46374784Sobrien
464267843Sdelphij	for (i = 0; i < MAGIC_SETS; i++) {
465267843Sdelphij		if (add_mlist(ms->mlist[i], map, i) == -1) {
466267843Sdelphij			file_oomem(ms, sizeof(*ml));
467330569Sgordon			return -1;
468267843Sdelphij		}
469133359Sobrien	}
470133359Sobrien
471226048Sobrien	if (action == FILE_LIST) {
472267843Sdelphij		for (i = 0; i < MAGIC_SETS; i++) {
473276415Sdelphij			printf("Set %" SIZE_T_FORMAT "u:\nBinary patterns:\n",
474276415Sdelphij			    i);
475267843Sdelphij			apprentice_list(ms->mlist[i], BINTEST);
476267843Sdelphij			printf("Text patterns:\n");
477267843Sdelphij			apprentice_list(ms->mlist[i], TEXTTEST);
478267843Sdelphij		}
479226048Sobrien	}
480276415Sdelphij	return 0;
481276415Sdelphij#else
482276415Sdelphij	return 0;
483267897Sdelphij#endif /* COMPILE_ONLY */
48474784Sobrien}
48574784Sobrien
486133359Sobrienprotected void
487267843Sdelphijfile_ms_free(struct magic_set *ms)
488133359Sobrien{
489267843Sdelphij	size_t i;
490267843Sdelphij	if (ms == NULL)
491133359Sobrien		return;
492267843Sdelphij	for (i = 0; i < MAGIC_SETS; i++)
493267843Sdelphij		mlist_free(ms->mlist[i]);
494267843Sdelphij	free(ms->o.pbuf);
495267843Sdelphij	free(ms->o.buf);
496267843Sdelphij	free(ms->c.li);
497267843Sdelphij	free(ms);
498267843Sdelphij}
499267843Sdelphij
500267843Sdelphijprotected struct magic_set *
501267843Sdelphijfile_ms_alloc(int flags)
502267843Sdelphij{
503267843Sdelphij	struct magic_set *ms;
504267843Sdelphij	size_t i, len;
505267843Sdelphij
506267843Sdelphij	if ((ms = CAST(struct magic_set *, calloc((size_t)1,
507267843Sdelphij	    sizeof(struct magic_set)))) == NULL)
508267843Sdelphij		return NULL;
509267843Sdelphij
510267843Sdelphij	if (magic_setflags(ms, flags) == -1) {
511267843Sdelphij		errno = EINVAL;
512267843Sdelphij		goto free;
513267843Sdelphij	}
514267843Sdelphij
515267843Sdelphij	ms->o.buf = ms->o.pbuf = NULL;
516267843Sdelphij	len = (ms->c.len = 10) * sizeof(*ms->c.li);
517267843Sdelphij
518267843Sdelphij	if ((ms->c.li = CAST(struct level_info *, malloc(len))) == NULL)
519267843Sdelphij		goto free;
520267843Sdelphij
521267843Sdelphij	ms->event_flags = 0;
522267843Sdelphij	ms->error = -1;
523267843Sdelphij	for (i = 0; i < MAGIC_SETS; i++)
524267843Sdelphij		ms->mlist[i] = NULL;
525267843Sdelphij	ms->file = "unknown";
526267843Sdelphij	ms->line = 0;
527276415Sdelphij	ms->indir_max = FILE_INDIR_MAX;
528276415Sdelphij	ms->name_max = FILE_NAME_MAX;
529276415Sdelphij	ms->elf_shnum_max = FILE_ELF_SHNUM_MAX;
530276415Sdelphij	ms->elf_phnum_max = FILE_ELF_PHNUM_MAX;
531277592Sdelphij	ms->elf_notes_max = FILE_ELF_NOTES_MAX;
532290152Sdelphij	ms->regex_max = FILE_REGEX_MAX;
533330569Sgordon	ms->bytes_max = FILE_BYTES_MAX;
534267843Sdelphij	return ms;
535267843Sdelphijfree:
536267843Sdelphij	free(ms);
537267843Sdelphij	return NULL;
538267843Sdelphij}
539267843Sdelphij
540267843Sdelphijprivate void
541267843Sdelphijapprentice_unmap(struct magic_map *map)
542267843Sdelphij{
543290152Sdelphij	size_t i;
544267843Sdelphij	if (map == NULL)
545267843Sdelphij		return;
546276415Sdelphij
547276415Sdelphij	switch (map->type) {
548330569Sgordon	case MAP_TYPE_USER:
549330569Sgordon		break;
550330569Sgordon	case MAP_TYPE_MALLOC:
551330569Sgordon		for (i = 0; i < MAGIC_SETS; i++) {
552330569Sgordon			void *b = map->magic[i];
553330569Sgordon			void *p = map->p;
554330569Sgordon			if (CAST(char *, b) >= CAST(char *, p) &&
555330569Sgordon			    CAST(char *, b) <= CAST(char *, p) + map->len)
556330569Sgordon				continue;
557330569Sgordon			free(map->magic[i]);
558330569Sgordon		}
559330569Sgordon		free(map->p);
560330569Sgordon		break;
561175296Sobrien#ifdef QUICK
562276415Sdelphij	case MAP_TYPE_MMAP:
563330569Sgordon		if (map->p && map->p != MAP_FAILED)
564267843Sdelphij			(void)munmap(map->p, map->len);
565276415Sdelphij		break;
566175296Sobrien#endif
567276415Sdelphij	default:
568276415Sdelphij		abort();
569133359Sobrien	}
570267843Sdelphij	free(map);
571133359Sobrien}
57274784Sobrien
573267843Sdelphijprivate struct mlist *
574267843Sdelphijmlist_alloc(void)
575267843Sdelphij{
576267843Sdelphij	struct mlist *mlist;
577267843Sdelphij	if ((mlist = CAST(struct mlist *, calloc(1, sizeof(*mlist)))) == NULL) {
578267843Sdelphij		return NULL;
579267843Sdelphij	}
580267843Sdelphij	mlist->next = mlist->prev = mlist;
581267843Sdelphij	return mlist;
582267843Sdelphij}
583267843Sdelphij
584267843Sdelphijprivate void
585267843Sdelphijmlist_free(struct mlist *mlist)
586267843Sdelphij{
587276415Sdelphij	struct mlist *ml, *next;
588267843Sdelphij
589267843Sdelphij	if (mlist == NULL)
590267843Sdelphij		return;
591267843Sdelphij
592276415Sdelphij	ml = mlist->next;
593276415Sdelphij	for (ml = mlist->next; (next = ml->next) != NULL; ml = next) {
594267843Sdelphij		if (ml->map)
595330569Sgordon			apprentice_unmap(CAST(struct magic_map *, ml->map));
596267843Sdelphij		free(ml);
597276415Sdelphij		if (ml == mlist)
598276415Sdelphij			break;
599267843Sdelphij	}
600267843Sdelphij}
601267843Sdelphij
602276415Sdelphij#ifndef COMPILE_ONLY
603276415Sdelphij/* void **bufs: an array of compiled magic files */
604276415Sdelphijprotected int
605276415Sdelphijbuffer_apprentice(struct magic_set *ms, struct magic **bufs,
606276415Sdelphij    size_t *sizes, size_t nbufs)
607276415Sdelphij{
608276415Sdelphij	size_t i, j;
609276415Sdelphij	struct mlist *ml;
610276415Sdelphij	struct magic_map *map;
611276415Sdelphij
612276415Sdelphij	if (nbufs == 0)
613276415Sdelphij		return -1;
614276415Sdelphij
615330569Sgordon	(void)file_reset(ms, 0);
616276415Sdelphij
617276415Sdelphij	init_file_tables();
618276415Sdelphij
619276415Sdelphij	for (i = 0; i < MAGIC_SETS; i++) {
620276415Sdelphij		mlist_free(ms->mlist[i]);
621276415Sdelphij		if ((ms->mlist[i] = mlist_alloc()) == NULL) {
622276415Sdelphij			file_oomem(ms, sizeof(*ms->mlist[i]));
623276415Sdelphij			goto fail;
624276415Sdelphij		}
625276415Sdelphij	}
626276415Sdelphij
627276415Sdelphij	for (i = 0; i < nbufs; i++) {
628276415Sdelphij		map = apprentice_buf(ms, bufs[i], sizes[i]);
629276415Sdelphij		if (map == NULL)
630276415Sdelphij			goto fail;
631276415Sdelphij
632276415Sdelphij		for (j = 0; j < MAGIC_SETS; j++) {
633276415Sdelphij			if (add_mlist(ms->mlist[j], map, j) == -1) {
634276415Sdelphij				file_oomem(ms, sizeof(*ml));
635276415Sdelphij				goto fail;
636276415Sdelphij			}
637276415Sdelphij		}
638276415Sdelphij	}
639276415Sdelphij
640276415Sdelphij	return 0;
641276415Sdelphijfail:
642276415Sdelphij	for (i = 0; i < MAGIC_SETS; i++) {
643276415Sdelphij		mlist_free(ms->mlist[i]);
644276415Sdelphij		ms->mlist[i] = NULL;
645276415Sdelphij	}
646276415Sdelphij	return -1;
647276415Sdelphij}
648276415Sdelphij#endif
649276415Sdelphij
650186690Sobrien/* const char *fn: list of magic files and directories */
651267843Sdelphijprotected int
652133359Sobrienfile_apprentice(struct magic_set *ms, const char *fn, int action)
65368349Sobrien{
654186690Sobrien	char *p, *mfn;
65568349Sobrien	int file_err, errs = -1;
656267843Sdelphij	size_t i;
65768349Sobrien
658330569Sgordon	(void)file_reset(ms, 0);
659267843Sdelphij
660226048Sobrien	if ((fn = magic_getpath(fn, action)) == NULL)
661267843Sdelphij		return -1;
662226048Sobrien
663169962Sobrien	init_file_tables();
664169962Sobrien
665169962Sobrien	if ((mfn = strdup(fn)) == NULL) {
666169942Sobrien		file_oomem(ms, strlen(fn));
667267843Sdelphij		return -1;
66868349Sobrien	}
669133359Sobrien
670267843Sdelphij	for (i = 0; i < MAGIC_SETS; i++) {
671267843Sdelphij		mlist_free(ms->mlist[i]);
672267843Sdelphij		if ((ms->mlist[i] = mlist_alloc()) == NULL) {
673267843Sdelphij			file_oomem(ms, sizeof(*ms->mlist[i]));
674276415Sdelphij			while (i-- > 0) {
675276415Sdelphij				mlist_free(ms->mlist[i]);
676276415Sdelphij				ms->mlist[i] = NULL;
677267843Sdelphij			}
678267843Sdelphij			free(mfn);
679267843Sdelphij			return -1;
680267843Sdelphij		}
681133359Sobrien	}
682267843Sdelphij	fn = mfn;
683133359Sobrien
68468349Sobrien	while (fn) {
68568349Sobrien		p = strchr(fn, PATHSEP);
68668349Sobrien		if (p)
68768349Sobrien			*p++ = '\0';
688133359Sobrien		if (*fn == '\0')
689133359Sobrien			break;
690267843Sdelphij		file_err = apprentice_1(ms, fn, action);
691186690Sobrien		errs = MAX(errs, file_err);
69268349Sobrien		fn = p;
69368349Sobrien	}
694267843Sdelphij
695267843Sdelphij	free(mfn);
696267843Sdelphij
697133359Sobrien	if (errs == -1) {
698267843Sdelphij		for (i = 0; i < MAGIC_SETS; i++) {
699267843Sdelphij			mlist_free(ms->mlist[i]);
700267843Sdelphij			ms->mlist[i] = NULL;
701267843Sdelphij		}
702267843Sdelphij		file_error(ms, 0, "could not find any valid magic files!");
703267843Sdelphij		return -1;
704133359Sobrien	}
705267843Sdelphij
706267843Sdelphij#if 0
707267843Sdelphij	/*
708267843Sdelphij	 * Always leave the database loaded
709267843Sdelphij	 */
710267843Sdelphij	if (action == FILE_LOAD)
711267843Sdelphij		return 0;
712267843Sdelphij
713267843Sdelphij	for (i = 0; i < MAGIC_SETS; i++) {
714267843Sdelphij		mlist_free(ms->mlist[i]);
715267843Sdelphij		ms->mlist[i] = NULL;
716267843Sdelphij	}
717267843Sdelphij#endif
718267843Sdelphij
719267843Sdelphij	switch (action) {
720267843Sdelphij	case FILE_LOAD:
721267843Sdelphij	case FILE_COMPILE:
722267843Sdelphij	case FILE_CHECK:
723267843Sdelphij	case FILE_LIST:
724267843Sdelphij		return 0;
725267843Sdelphij	default:
726267843Sdelphij		file_error(ms, 0, "Invalid action %d", action);
727267843Sdelphij		return -1;
728267843Sdelphij	}
72968349Sobrien}
73068349Sobrien
731169942Sobrien/*
732267843Sdelphij * Compute the real length of a magic expression, for the purposes
733267843Sdelphij * of determining how "strong" a magic expression is (approximating
734267843Sdelphij * how specific its matches are):
735267843Sdelphij *	- magic characters count 0 unless escaped.
736267843Sdelphij *	- [] expressions count 1
737267843Sdelphij *	- {} expressions count 0
738267843Sdelphij *	- regular characters or escaped magic characters count 1
739267843Sdelphij *	- 0 length expressions count as one
740267843Sdelphij */
741267843Sdelphijprivate size_t
742267843Sdelphijnonmagic(const char *str)
743267843Sdelphij{
744267843Sdelphij	const char *p;
745267843Sdelphij	size_t rv = 0;
746267843Sdelphij
747267843Sdelphij	for (p = str; *p; p++)
748267843Sdelphij		switch (*p) {
749267843Sdelphij		case '\\':	/* Escaped anything counts 1 */
750267843Sdelphij			if (!*++p)
751267843Sdelphij				p--;
752267843Sdelphij			rv++;
753267843Sdelphij			continue;
754267843Sdelphij		case '?':	/* Magic characters count 0 */
755267843Sdelphij		case '*':
756267843Sdelphij		case '.':
757267843Sdelphij		case '+':
758267843Sdelphij		case '^':
759267843Sdelphij		case '$':
760267843Sdelphij			continue;
761267843Sdelphij		case '[':	/* Bracketed expressions count 1 the ']' */
762267843Sdelphij			while (*p && *p != ']')
763267843Sdelphij				p++;
764267843Sdelphij			p--;
765267843Sdelphij			continue;
766267843Sdelphij		case '{':	/* Braced expressions count 0 */
767267843Sdelphij			while (*p && *p != '}')
768267843Sdelphij				p++;
769267843Sdelphij			if (!*p)
770267843Sdelphij				p--;
771267843Sdelphij			continue;
772267843Sdelphij		default:	/* Anything else counts 1 */
773267843Sdelphij			rv++;
774267843Sdelphij			continue;
775267843Sdelphij		}
776267843Sdelphij
777267843Sdelphij	return rv == 0 ? 1 : rv;	/* Return at least 1 */
778267843Sdelphij}
779267843Sdelphij
780330569Sgordon
781330569Sgordonprivate size_t
782330569Sgordontypesize(int type)
783330569Sgordon{
784330569Sgordon	switch (type) {
785330569Sgordon	case FILE_BYTE:
786330569Sgordon		return 1;
787330569Sgordon
788330569Sgordon	case FILE_SHORT:
789330569Sgordon	case FILE_LESHORT:
790330569Sgordon	case FILE_BESHORT:
791330569Sgordon		return 2;
792330569Sgordon
793330569Sgordon	case FILE_LONG:
794330569Sgordon	case FILE_LELONG:
795330569Sgordon	case FILE_BELONG:
796330569Sgordon	case FILE_MELONG:
797330569Sgordon		return 4;
798330569Sgordon
799330569Sgordon	case FILE_DATE:
800330569Sgordon	case FILE_LEDATE:
801330569Sgordon	case FILE_BEDATE:
802330569Sgordon	case FILE_MEDATE:
803330569Sgordon	case FILE_LDATE:
804330569Sgordon	case FILE_LELDATE:
805330569Sgordon	case FILE_BELDATE:
806330569Sgordon	case FILE_MELDATE:
807330569Sgordon	case FILE_FLOAT:
808330569Sgordon	case FILE_BEFLOAT:
809330569Sgordon	case FILE_LEFLOAT:
810330569Sgordon		return 4;
811330569Sgordon
812330569Sgordon	case FILE_QUAD:
813330569Sgordon	case FILE_BEQUAD:
814330569Sgordon	case FILE_LEQUAD:
815330569Sgordon	case FILE_QDATE:
816330569Sgordon	case FILE_LEQDATE:
817330569Sgordon	case FILE_BEQDATE:
818330569Sgordon	case FILE_QLDATE:
819330569Sgordon	case FILE_LEQLDATE:
820330569Sgordon	case FILE_BEQLDATE:
821330569Sgordon	case FILE_QWDATE:
822330569Sgordon	case FILE_LEQWDATE:
823330569Sgordon	case FILE_BEQWDATE:
824330569Sgordon	case FILE_DOUBLE:
825330569Sgordon	case FILE_BEDOUBLE:
826330569Sgordon	case FILE_LEDOUBLE:
827330569Sgordon		return 8;
828330569Sgordon	default:
829330569Sgordon		return (size_t)~0;
830330569Sgordon	}
831330569Sgordon}
832330569Sgordon
833267843Sdelphij/*
834169942Sobrien * Get weight of this magic entry, for sorting purposes.
835169942Sobrien */
836159764Sobrienprivate size_t
837159764Sobrienapprentice_magic_strength(const struct magic *m)
838159764Sobrien{
839169942Sobrien#define MULT 10
840330569Sgordon	size_t ts, v, val = 2 * MULT;	/* baseline strength */
841169942Sobrien
842159764Sobrien	switch (m->type) {
843169962Sobrien	case FILE_DEFAULT:	/* make sure this sorts last */
844186690Sobrien		if (m->factor_op != FILE_FACTOR_OP_NONE)
845186690Sobrien			abort();
846169962Sobrien		return 0;
847169962Sobrien
848159764Sobrien	case FILE_BYTE:
849159764Sobrien	case FILE_SHORT:
850159764Sobrien	case FILE_LESHORT:
851159764Sobrien	case FILE_BESHORT:
852159764Sobrien	case FILE_LONG:
853159764Sobrien	case FILE_LELONG:
854159764Sobrien	case FILE_BELONG:
855159764Sobrien	case FILE_MELONG:
856159764Sobrien	case FILE_DATE:
857159764Sobrien	case FILE_LEDATE:
858159764Sobrien	case FILE_BEDATE:
859159764Sobrien	case FILE_MEDATE:
860159764Sobrien	case FILE_LDATE:
861159764Sobrien	case FILE_LELDATE:
862159764Sobrien	case FILE_BELDATE:
863159764Sobrien	case FILE_MELDATE:
864175296Sobrien	case FILE_FLOAT:
865175296Sobrien	case FILE_BEFLOAT:
866175296Sobrien	case FILE_LEFLOAT:
867169942Sobrien	case FILE_QUAD:
868169942Sobrien	case FILE_BEQUAD:
869169942Sobrien	case FILE_LEQUAD:
870169942Sobrien	case FILE_QDATE:
871169942Sobrien	case FILE_LEQDATE:
872169942Sobrien	case FILE_BEQDATE:
873169942Sobrien	case FILE_QLDATE:
874169942Sobrien	case FILE_LEQLDATE:
875169942Sobrien	case FILE_BEQLDATE:
876267843Sdelphij	case FILE_QWDATE:
877267843Sdelphij	case FILE_LEQWDATE:
878267843Sdelphij	case FILE_BEQWDATE:
879175296Sobrien	case FILE_DOUBLE:
880175296Sobrien	case FILE_BEDOUBLE:
881175296Sobrien	case FILE_LEDOUBLE:
882330569Sgordon		ts = typesize(m->type);
883330569Sgordon		if (ts == (size_t)~0)
884330569Sgordon			abort();
885330569Sgordon		val += ts * MULT;
886169942Sobrien		break;
887169942Sobrien
888330569Sgordon	case FILE_PSTRING:
889330569Sgordon	case FILE_STRING:
890330569Sgordon		val += m->vallen * MULT;
891330569Sgordon		break;
892330569Sgordon
893330569Sgordon	case FILE_BESTRING16:
894330569Sgordon	case FILE_LESTRING16:
895330569Sgordon		val += m->vallen * MULT / 2;
896330569Sgordon		break;
897330569Sgordon
898330569Sgordon	case FILE_SEARCH:
899330569Sgordon		val += m->vallen * MAX(MULT / m->vallen, 1);
900330569Sgordon		break;
901330569Sgordon
902330569Sgordon	case FILE_REGEX:
903330569Sgordon		v = nonmagic(m->value.s);
904330569Sgordon		val += v * MAX(MULT / v, 1);
905330569Sgordon		break;
906330569Sgordon
907267843Sdelphij	case FILE_INDIRECT:
908267843Sdelphij	case FILE_NAME:
909267843Sdelphij	case FILE_USE:
910267843Sdelphij		break;
911267843Sdelphij
912330569Sgordon	case FILE_DER:
913330569Sgordon		val += MULT;
914330569Sgordon		break;
915330569Sgordon
916159764Sobrien	default:
917169942Sobrien		(void)fprintf(stderr, "Bad type %d\n", m->type);
918169942Sobrien		abort();
919159764Sobrien	}
920169942Sobrien
921169942Sobrien	switch (m->reln) {
922169942Sobrien	case 'x':	/* matches anything penalize */
923186690Sobrien	case '!':       /* matches almost anything penalize */
924169942Sobrien		val = 0;
925169942Sobrien		break;
926169942Sobrien
927169942Sobrien	case '=':	/* Exact match, prefer */
928169942Sobrien		val += MULT;
929169942Sobrien		break;
930169942Sobrien
931169942Sobrien	case '>':
932169942Sobrien	case '<':	/* comparison match reduce strength */
933169942Sobrien		val -= 2 * MULT;
934169942Sobrien		break;
935169942Sobrien
936169942Sobrien	case '^':
937169942Sobrien	case '&':	/* masking bits, we could count them too */
938169942Sobrien		val -= MULT;
939169942Sobrien		break;
940169942Sobrien
941169942Sobrien	default:
942169942Sobrien		(void)fprintf(stderr, "Bad relation %c\n", m->reln);
943169942Sobrien		abort();
944169942Sobrien	}
945169962Sobrien
946169962Sobrien	if (val == 0)	/* ensure we only return 0 for FILE_DEFAULT */
947169962Sobrien		val = 1;
948169962Sobrien
949186690Sobrien	switch (m->factor_op) {
950186690Sobrien	case FILE_FACTOR_OP_NONE:
951186690Sobrien		break;
952186690Sobrien	case FILE_FACTOR_OP_PLUS:
953186690Sobrien		val += m->factor;
954186690Sobrien		break;
955186690Sobrien	case FILE_FACTOR_OP_MINUS:
956186690Sobrien		val -= m->factor;
957186690Sobrien		break;
958186690Sobrien	case FILE_FACTOR_OP_TIMES:
959186690Sobrien		val *= m->factor;
960186690Sobrien		break;
961186690Sobrien	case FILE_FACTOR_OP_DIV:
962186690Sobrien		val /= m->factor;
963186690Sobrien		break;
964186690Sobrien	default:
965186690Sobrien		abort();
966186690Sobrien	}
967186690Sobrien
968186690Sobrien	/*
969186690Sobrien	 * Magic entries with no description get a bonus because they depend
970186690Sobrien	 * on subsequent magic entries to print something.
971186690Sobrien	 */
972186690Sobrien	if (m->desc[0] == '\0')
973186690Sobrien		val++;
974169942Sobrien	return val;
975159764Sobrien}
976159764Sobrien
977169942Sobrien/*
978169942Sobrien * Sort callback for sorting entries by "strength" (basically length)
979169942Sobrien */
980159764Sobrienprivate int
981159764Sobrienapprentice_sort(const void *a, const void *b)
982159764Sobrien{
983186690Sobrien	const struct magic_entry *ma = CAST(const struct magic_entry *, a);
984186690Sobrien	const struct magic_entry *mb = CAST(const struct magic_entry *, b);
985159764Sobrien	size_t sa = apprentice_magic_strength(ma->mp);
986159764Sobrien	size_t sb = apprentice_magic_strength(mb->mp);
987159764Sobrien	if (sa == sb)
988159764Sobrien		return 0;
989159764Sobrien	else if (sa > sb)
990159764Sobrien		return -1;
991159764Sobrien	else
992159764Sobrien		return 1;
993159764Sobrien}
994159764Sobrien
995226048Sobrien/*
996226048Sobrien * Shows sorted patterns list in the order which is used for the matching
997226048Sobrien */
998186690Sobrienprivate void
999226048Sobrienapprentice_list(struct mlist *mlist, int mode)
1000226048Sobrien{
1001226048Sobrien	uint32_t magindex = 0;
1002226048Sobrien	struct mlist *ml;
1003226048Sobrien	for (ml = mlist->next; ml != mlist; ml = ml->next) {
1004226048Sobrien		for (magindex = 0; magindex < ml->nmagic; magindex++) {
1005226048Sobrien			struct magic *m = &ml->magic[magindex];
1006226048Sobrien			if ((m->flag & mode) != mode) {
1007226048Sobrien				/* Skip sub-tests */
1008226048Sobrien				while (magindex + 1 < ml->nmagic &&
1009226048Sobrien				       ml->magic[magindex + 1].cont_level != 0)
1010226048Sobrien					++magindex;
1011226048Sobrien				continue; /* Skip to next top-level test*/
1012226048Sobrien			}
1013226048Sobrien
1014226048Sobrien			/*
1015226048Sobrien			 * Try to iterate over the tree until we find item with
1016226048Sobrien			 * description/mimetype.
1017226048Sobrien			 */
1018226048Sobrien			while (magindex + 1 < ml->nmagic &&
1019226048Sobrien			       ml->magic[magindex + 1].cont_level != 0 &&
1020226048Sobrien			       *ml->magic[magindex].desc == '\0' &&
1021226048Sobrien			       *ml->magic[magindex].mimetype == '\0')
1022226048Sobrien				magindex++;
1023226048Sobrien
1024284778Sdelphij			printf("Strength = %3" SIZE_T_FORMAT "u@%u: %s [%s]\n",
1025226048Sobrien			    apprentice_magic_strength(m),
1026284778Sdelphij			    ml->magic[magindex].lineno,
1027226048Sobrien			    ml->magic[magindex].desc,
1028226048Sobrien			    ml->magic[magindex].mimetype);
1029226048Sobrien		}
1030226048Sobrien	}
1031226048Sobrien}
1032226048Sobrien
1033226048Sobrienprivate void
1034186690Sobrienset_test_type(struct magic *mstart, struct magic *m)
1035186690Sobrien{
1036186690Sobrien	switch (m->type) {
1037186690Sobrien	case FILE_BYTE:
1038186690Sobrien	case FILE_SHORT:
1039186690Sobrien	case FILE_LONG:
1040186690Sobrien	case FILE_DATE:
1041186690Sobrien	case FILE_BESHORT:
1042186690Sobrien	case FILE_BELONG:
1043186690Sobrien	case FILE_BEDATE:
1044186690Sobrien	case FILE_LESHORT:
1045186690Sobrien	case FILE_LELONG:
1046186690Sobrien	case FILE_LEDATE:
1047186690Sobrien	case FILE_LDATE:
1048186690Sobrien	case FILE_BELDATE:
1049186690Sobrien	case FILE_LELDATE:
1050186690Sobrien	case FILE_MEDATE:
1051186690Sobrien	case FILE_MELDATE:
1052186690Sobrien	case FILE_MELONG:
1053186690Sobrien	case FILE_QUAD:
1054186690Sobrien	case FILE_LEQUAD:
1055186690Sobrien	case FILE_BEQUAD:
1056186690Sobrien	case FILE_QDATE:
1057186690Sobrien	case FILE_LEQDATE:
1058186690Sobrien	case FILE_BEQDATE:
1059186690Sobrien	case FILE_QLDATE:
1060186690Sobrien	case FILE_LEQLDATE:
1061186690Sobrien	case FILE_BEQLDATE:
1062267843Sdelphij	case FILE_QWDATE:
1063267843Sdelphij	case FILE_LEQWDATE:
1064267843Sdelphij	case FILE_BEQWDATE:
1065186690Sobrien	case FILE_FLOAT:
1066186690Sobrien	case FILE_BEFLOAT:
1067186690Sobrien	case FILE_LEFLOAT:
1068186690Sobrien	case FILE_DOUBLE:
1069186690Sobrien	case FILE_BEDOUBLE:
1070186690Sobrien	case FILE_LEDOUBLE:
1071330569Sgordon	case FILE_DER:
1072226048Sobrien		mstart->flag |= BINTEST;
1073226048Sobrien		break;
1074186690Sobrien	case FILE_STRING:
1075186690Sobrien	case FILE_PSTRING:
1076186690Sobrien	case FILE_BESTRING16:
1077186690Sobrien	case FILE_LESTRING16:
1078226048Sobrien		/* Allow text overrides */
1079226048Sobrien		if (mstart->str_flags & STRING_TEXTTEST)
1080226048Sobrien			mstart->flag |= TEXTTEST;
1081226048Sobrien		else
1082226048Sobrien			mstart->flag |= BINTEST;
1083186690Sobrien		break;
1084186690Sobrien	case FILE_REGEX:
1085186690Sobrien	case FILE_SEARCH:
1086226048Sobrien		/* Check for override */
1087226048Sobrien		if (mstart->str_flags & STRING_BINTEST)
1088226048Sobrien			mstart->flag |= BINTEST;
1089226048Sobrien		if (mstart->str_flags & STRING_TEXTTEST)
1090226048Sobrien			mstart->flag |= TEXTTEST;
1091226048Sobrien
1092226048Sobrien		if (mstart->flag & (TEXTTEST|BINTEST))
1093226048Sobrien			break;
1094226048Sobrien
1095186690Sobrien		/* binary test if pattern is not text */
1096191736Sobrien		if (file_looks_utf8(m->value.us, (size_t)m->vallen, NULL,
1097191736Sobrien		    NULL) <= 0)
1098186690Sobrien			mstart->flag |= BINTEST;
1099226048Sobrien		else
1100226048Sobrien			mstart->flag |= TEXTTEST;
1101186690Sobrien		break;
1102186690Sobrien	case FILE_DEFAULT:
1103186690Sobrien		/* can't deduce anything; we shouldn't see this at the
1104186690Sobrien		   top level anyway */
1105186690Sobrien		break;
1106186690Sobrien	case FILE_INVALID:
1107186690Sobrien	default:
1108186690Sobrien		/* invalid search type, but no need to complain here */
1109186690Sobrien		break;
1110186690Sobrien	}
1111186690Sobrien}
1112186690Sobrien
1113267843Sdelphijprivate int
1114267843Sdelphijaddentry(struct magic_set *ms, struct magic_entry *me,
1115267843Sdelphij   struct magic_entry_set *mset)
1116267843Sdelphij{
1117267843Sdelphij	size_t i = me->mp->type == FILE_NAME ? 1 : 0;
1118267843Sdelphij	if (mset[i].count == mset[i].max) {
1119267843Sdelphij		struct magic_entry *mp;
1120267843Sdelphij
1121267843Sdelphij		mset[i].max += ALLOC_INCR;
1122267843Sdelphij		if ((mp = CAST(struct magic_entry *,
1123267843Sdelphij		    realloc(mset[i].me, sizeof(*mp) * mset[i].max))) ==
1124267843Sdelphij		    NULL) {
1125267843Sdelphij			file_oomem(ms, sizeof(*mp) * mset[i].max);
1126267843Sdelphij			return -1;
1127267843Sdelphij		}
1128267843Sdelphij		(void)memset(&mp[mset[i].count], 0, sizeof(*mp) *
1129267843Sdelphij		    ALLOC_INCR);
1130267843Sdelphij		mset[i].me = mp;
1131267843Sdelphij	}
1132267843Sdelphij	mset[i].me[mset[i].count++] = *me;
1133267843Sdelphij	memset(me, 0, sizeof(*me));
1134267843Sdelphij	return 0;
1135267843Sdelphij}
1136267843Sdelphij
113774784Sobrien/*
1138186690Sobrien * Load and parse one file.
113974784Sobrien */
1140186690Sobrienprivate void
1141186690Sobrienload_1(struct magic_set *ms, int action, const char *fn, int *errs,
1142267843Sdelphij   struct magic_entry_set *mset)
1143186690Sobrien{
1144226048Sobrien	size_t lineno = 0, llen = 0;
1145226048Sobrien	char *line = NULL;
1146226048Sobrien	ssize_t len;
1147267843Sdelphij	struct magic_entry me;
1148226048Sobrien
1149186690Sobrien	FILE *f = fopen(ms->file = fn, "r");
1150186690Sobrien	if (f == NULL) {
1151186690Sobrien		if (errno != ENOENT)
1152186690Sobrien			file_error(ms, errno, "cannot read magic file `%s'",
1153186690Sobrien				   fn);
1154186690Sobrien		(*errs)++;
1155226048Sobrien		return;
1156226048Sobrien	}
1157226048Sobrien
1158267843Sdelphij	memset(&me, 0, sizeof(me));
1159226048Sobrien	/* read and parse this file */
1160226048Sobrien	for (ms->line = 1; (len = getline(&line, &llen, f)) != -1;
1161226048Sobrien	    ms->line++) {
1162226048Sobrien		if (len == 0) /* null line, garbage, etc */
1163226048Sobrien			continue;
1164226048Sobrien		if (line[len - 1] == '\n') {
1165226048Sobrien			lineno++;
1166226048Sobrien			line[len - 1] = '\0'; /* delete newline */
1167226048Sobrien		}
1168226048Sobrien		switch (line[0]) {
1169226048Sobrien		case '\0':	/* empty, do not parse */
1170226048Sobrien		case '#':	/* comment, do not parse */
1171226048Sobrien			continue;
1172226048Sobrien		case '!':
1173226048Sobrien			if (line[1] == ':') {
1174186690Sobrien				size_t i;
1175186690Sobrien
1176186690Sobrien				for (i = 0; bang[i].name != NULL; i++) {
1177226048Sobrien					if ((size_t)(len - 2) > bang[i].len &&
1178186690Sobrien					    memcmp(bang[i].name, line + 2,
1179186690Sobrien					    bang[i].len) == 0)
1180186690Sobrien						break;
1181186690Sobrien				}
1182186690Sobrien				if (bang[i].name == NULL) {
1183186690Sobrien					file_error(ms, 0,
1184186690Sobrien					    "Unknown !: entry `%s'", line);
1185186690Sobrien					(*errs)++;
1186186690Sobrien					continue;
1187186690Sobrien				}
1188267843Sdelphij				if (me.mp == NULL) {
1189186690Sobrien					file_error(ms, 0,
1190186690Sobrien					    "No current entry for :!%s type",
1191186690Sobrien						bang[i].name);
1192186690Sobrien					(*errs)++;
1193186690Sobrien					continue;
1194186690Sobrien				}
1195267843Sdelphij				if ((*bang[i].fun)(ms, &me,
1196186690Sobrien				    line + bang[i].len + 2) != 0) {
1197186690Sobrien					(*errs)++;
1198186690Sobrien					continue;
1199186690Sobrien				}
1200186690Sobrien				continue;
1201186690Sobrien			}
1202226048Sobrien			/*FALLTHROUGH*/
1203226048Sobrien		default:
1204267843Sdelphij		again:
1205267843Sdelphij			switch (parse(ms, &me, line, lineno, action)) {
1206267843Sdelphij			case 0:
1207267843Sdelphij				continue;
1208267843Sdelphij			case 1:
1209267843Sdelphij				(void)addentry(ms, &me, mset);
1210267843Sdelphij				goto again;
1211267843Sdelphij			default:
1212186690Sobrien				(*errs)++;
1213267843Sdelphij				break;
1214267843Sdelphij			}
1215186690Sobrien		}
1216186690Sobrien	}
1217267843Sdelphij	if (me.mp)
1218267843Sdelphij		(void)addentry(ms, &me, mset);
1219234250Sobrien	free(line);
1220226048Sobrien	(void)fclose(f);
1221186690Sobrien}
1222186690Sobrien
1223186690Sobrien/*
1224186690Sobrien * parse a file or directory of files
1225186690Sobrien * const char *fn: name of magic file or directory
1226186690Sobrien */
1227133359Sobrienprivate int
1228226048Sobriencmpstrp(const void *p1, const void *p2)
1229226048Sobrien{
1230226048Sobrien        return strcmp(*(char *const *)p1, *(char *const *)p2);
1231226048Sobrien}
1232226048Sobrien
1233267843Sdelphij
1234267843Sdelphijprivate uint32_t
1235267843Sdelphijset_text_binary(struct magic_set *ms, struct magic_entry *me, uint32_t nme,
1236267843Sdelphij    uint32_t starttest)
1237267843Sdelphij{
1238267843Sdelphij	static const char text[] = "text";
1239267843Sdelphij	static const char binary[] = "binary";
1240267843Sdelphij	static const size_t len = sizeof(text);
1241267843Sdelphij
1242267843Sdelphij	uint32_t i = starttest;
1243267843Sdelphij
1244267843Sdelphij	do {
1245267843Sdelphij		set_test_type(me[starttest].mp, me[i].mp);
1246267843Sdelphij		if ((ms->flags & MAGIC_DEBUG) == 0)
1247267843Sdelphij			continue;
1248267843Sdelphij		(void)fprintf(stderr, "%s%s%s: %s\n",
1249267843Sdelphij		    me[i].mp->mimetype,
1250267843Sdelphij		    me[i].mp->mimetype[0] == '\0' ? "" : "; ",
1251267843Sdelphij		    me[i].mp->desc[0] ? me[i].mp->desc : "(no description)",
1252267843Sdelphij		    me[i].mp->flag & BINTEST ? binary : text);
1253267843Sdelphij		if (me[i].mp->flag & BINTEST) {
1254267843Sdelphij			char *p = strstr(me[i].mp->desc, text);
1255267843Sdelphij			if (p && (p == me[i].mp->desc ||
1256267843Sdelphij			    isspace((unsigned char)p[-1])) &&
1257267843Sdelphij			    (p + len - me[i].mp->desc == MAXstring
1258267843Sdelphij			    || (p[len] == '\0' ||
1259267843Sdelphij			    isspace((unsigned char)p[len]))))
1260267843Sdelphij				(void)fprintf(stderr, "*** Possible "
1261267843Sdelphij				    "binary test for text type\n");
1262267843Sdelphij		}
1263267843Sdelphij	} while (++i < nme && me[i].mp->cont_level != 0);
1264267843Sdelphij	return i;
1265267843Sdelphij}
1266267843Sdelphij
1267267843Sdelphijprivate void
1268267843Sdelphijset_last_default(struct magic_set *ms, struct magic_entry *me, uint32_t nme)
1269267843Sdelphij{
1270267843Sdelphij	uint32_t i;
1271267843Sdelphij	for (i = 0; i < nme; i++) {
1272267843Sdelphij		if (me[i].mp->cont_level == 0 &&
1273267843Sdelphij		    me[i].mp->type == FILE_DEFAULT) {
1274267843Sdelphij			while (++i < nme)
1275267843Sdelphij				if (me[i].mp->cont_level == 0)
1276267843Sdelphij					break;
1277267843Sdelphij			if (i != nme) {
1278267843Sdelphij				/* XXX - Ugh! */
1279267843Sdelphij				ms->line = me[i].mp->lineno;
1280267843Sdelphij				file_magwarn(ms,
1281267843Sdelphij				    "level 0 \"default\" did not sort last");
1282267843Sdelphij			}
1283267843Sdelphij			return;
1284267843Sdelphij		}
1285267843Sdelphij	}
1286267843Sdelphij}
1287267843Sdelphij
1288226048Sobrienprivate int
1289267843Sdelphijcoalesce_entries(struct magic_set *ms, struct magic_entry *me, uint32_t nme,
1290267843Sdelphij    struct magic **ma, uint32_t *nma)
129168349Sobrien{
1292267843Sdelphij	uint32_t i, mentrycount = 0;
1293267843Sdelphij	size_t slen;
1294267843Sdelphij
1295267843Sdelphij	for (i = 0; i < nme; i++)
1296267843Sdelphij		mentrycount += me[i].cont_count;
1297267843Sdelphij
1298267843Sdelphij	slen = sizeof(**ma) * mentrycount;
1299267843Sdelphij	if ((*ma = CAST(struct magic *, malloc(slen))) == NULL) {
1300267843Sdelphij		file_oomem(ms, slen);
1301267843Sdelphij		return -1;
1302267843Sdelphij	}
1303267843Sdelphij
1304267843Sdelphij	mentrycount = 0;
1305267843Sdelphij	for (i = 0; i < nme; i++) {
1306267843Sdelphij		(void)memcpy(*ma + mentrycount, me[i].mp,
1307267843Sdelphij		    me[i].cont_count * sizeof(**ma));
1308267843Sdelphij		mentrycount += me[i].cont_count;
1309267843Sdelphij	}
1310267843Sdelphij	*nma = mentrycount;
1311267843Sdelphij	return 0;
1312267843Sdelphij}
1313267843Sdelphij
1314267843Sdelphijprivate void
1315267843Sdelphijmagic_entry_free(struct magic_entry *me, uint32_t nme)
1316267843Sdelphij{
1317267843Sdelphij	uint32_t i;
1318267843Sdelphij	if (me == NULL)
1319267843Sdelphij		return;
1320267843Sdelphij	for (i = 0; i < nme; i++)
1321267843Sdelphij		free(me[i].mp);
1322267843Sdelphij	free(me);
1323267843Sdelphij}
1324267843Sdelphij
1325267843Sdelphijprivate struct magic_map *
1326267843Sdelphijapprentice_load(struct magic_set *ms, const char *fn, int action)
1327267843Sdelphij{
132868349Sobrien	int errs = 0;
1329267843Sdelphij	uint32_t i, j;
1330267843Sdelphij	size_t files = 0, maxfiles = 0;
1331226048Sobrien	char **filearr = NULL, *mfn;
1332186690Sobrien	struct stat st;
1333267843Sdelphij	struct magic_map *map;
1334267843Sdelphij	struct magic_entry_set mset[MAGIC_SETS];
1335186690Sobrien	DIR *dir;
1336186690Sobrien	struct dirent *d;
133768349Sobrien
1338267843Sdelphij	memset(mset, 0, sizeof(mset));
1339169942Sobrien	ms->flags |= MAGIC_CHECK;	/* Enable checks for parsed files */
1340169942Sobrien
1341267843Sdelphij
1342267843Sdelphij	if ((map = CAST(struct magic_map *, calloc(1, sizeof(*map)))) == NULL)
1343267843Sdelphij	{
1344267843Sdelphij		file_oomem(ms, sizeof(*map));
1345267843Sdelphij		return NULL;
134674784Sobrien	}
1347290152Sdelphij	map->type = MAP_TYPE_MALLOC;
134874784Sobrien
1349133359Sobrien	/* print silly verbose header for USG compat. */
1350133359Sobrien	if (action == FILE_CHECK)
1351186690Sobrien		(void)fprintf(stderr, "%s\n", usg_hdr);
1352133359Sobrien
1353186690Sobrien	/* load directory or file */
1354186690Sobrien	if (stat(fn, &st) == 0 && S_ISDIR(st.st_mode)) {
1355186690Sobrien		dir = opendir(fn);
1356226048Sobrien		if (!dir) {
1357226048Sobrien			errs++;
1358226048Sobrien			goto out;
1359226048Sobrien		}
1360226048Sobrien		while ((d = readdir(dir)) != NULL) {
1361330569Sgordon			if (d->d_name[0] == '.')
1362330569Sgordon				continue;
1363226048Sobrien			if (asprintf(&mfn, "%s/%s", fn, d->d_name) < 0) {
1364226048Sobrien				file_oomem(ms,
1365226048Sobrien				    strlen(fn) + strlen(d->d_name) + 2);
1366226048Sobrien				errs++;
1367234250Sobrien				closedir(dir);
1368226048Sobrien				goto out;
1369226048Sobrien			}
1370226048Sobrien			if (stat(mfn, &st) == -1 || !S_ISREG(st.st_mode)) {
1371226048Sobrien				free(mfn);
1372226048Sobrien				continue;
1373226048Sobrien			}
1374226048Sobrien			if (files >= maxfiles) {
1375226048Sobrien				size_t mlen;
1376226048Sobrien				maxfiles = (maxfiles + 1) * 2;
1377226048Sobrien				mlen = maxfiles * sizeof(*filearr);
1378226048Sobrien				if ((filearr = CAST(char **,
1379226048Sobrien				    realloc(filearr, mlen))) == NULL) {
1380226048Sobrien					file_oomem(ms, mlen);
1381226048Sobrien					free(mfn);
1382234250Sobrien					closedir(dir);
1383226048Sobrien					errs++;
1384226048Sobrien					goto out;
1385186690Sobrien				}
1386186690Sobrien			}
1387226048Sobrien			filearr[files++] = mfn;
1388226048Sobrien		}
1389226048Sobrien		closedir(dir);
1390226048Sobrien		qsort(filearr, files, sizeof(*filearr), cmpstrp);
1391226048Sobrien		for (i = 0; i < files; i++) {
1392267843Sdelphij			load_1(ms, action, filearr[i], &errs, mset);
1393226048Sobrien			free(filearr[i]);
1394226048Sobrien		}
1395226048Sobrien		free(filearr);
1396186690Sobrien	} else
1397267843Sdelphij		load_1(ms, action, fn, &errs, mset);
1398186690Sobrien	if (errs)
1399186690Sobrien		goto out;
1400186690Sobrien
1401267843Sdelphij	for (j = 0; j < MAGIC_SETS; j++) {
1402267843Sdelphij		/* Set types of tests */
1403267843Sdelphij		for (i = 0; i < mset[j].count; ) {
1404267843Sdelphij			if (mset[j].me[i].mp->cont_level != 0) {
1405267843Sdelphij				i++;
1406186690Sobrien				continue;
1407186690Sobrien			}
1408267843Sdelphij			i = set_text_binary(ms, mset[j].me, mset[j].count, i);
1409267843Sdelphij		}
1410290152Sdelphij		if (mset[j].me)
1411290152Sdelphij			qsort(mset[j].me, mset[j].count, sizeof(*mset[j].me),
1412290152Sdelphij			    apprentice_sort);
141368349Sobrien
1414267843Sdelphij		/*
1415267843Sdelphij		 * Make sure that any level 0 "default" line is last
1416267843Sdelphij		 * (if one exists).
1417267843Sdelphij		 */
1418267843Sdelphij		set_last_default(ms, mset[j].me, mset[j].count);
1419159764Sobrien
1420267843Sdelphij		/* coalesce per file arrays into a single one */
1421267843Sdelphij		if (coalesce_entries(ms, mset[j].me, mset[j].count,
1422267843Sdelphij		    &map->magic[j], &map->nmagic[j]) == -1) {
1423267843Sdelphij			errs++;
1424267843Sdelphij			goto out;
1425169962Sobrien		}
1426169962Sobrien	}
1427159764Sobrien
1428267843Sdelphijout:
1429267843Sdelphij	for (j = 0; j < MAGIC_SETS; j++)
1430267843Sdelphij		magic_entry_free(mset[j].me, mset[j].count);
1431159764Sobrien
143274784Sobrien	if (errs) {
1433267843Sdelphij		apprentice_unmap(map);
1434267843Sdelphij		return NULL;
143574784Sobrien	}
1436267843Sdelphij	return map;
143768349Sobrien}
143868349Sobrien
143968349Sobrien/*
144068349Sobrien * extend the sign bit if the comparison is to be signed
144168349Sobrien */
1442169942Sobrienprotected uint64_t
1443169942Sobrienfile_signextend(struct magic_set *ms, struct magic *m, uint64_t v)
144468349Sobrien{
1445169962Sobrien	if (!(m->flag & UNSIGNED)) {
144668349Sobrien		switch(m->type) {
144768349Sobrien		/*
144868349Sobrien		 * Do not remove the casts below.  They are
144968349Sobrien		 * vital.  When later compared with the data,
145068349Sobrien		 * the sign extension must have happened.
145168349Sobrien		 */
1452133359Sobrien		case FILE_BYTE:
1453276415Sdelphij			v = (signed char) v;
145468349Sobrien			break;
1455133359Sobrien		case FILE_SHORT:
1456133359Sobrien		case FILE_BESHORT:
1457133359Sobrien		case FILE_LESHORT:
145868349Sobrien			v = (short) v;
145968349Sobrien			break;
1460133359Sobrien		case FILE_DATE:
1461133359Sobrien		case FILE_BEDATE:
1462133359Sobrien		case FILE_LEDATE:
1463159764Sobrien		case FILE_MEDATE:
1464133359Sobrien		case FILE_LDATE:
1465133359Sobrien		case FILE_BELDATE:
1466133359Sobrien		case FILE_LELDATE:
1467159764Sobrien		case FILE_MELDATE:
1468133359Sobrien		case FILE_LONG:
1469133359Sobrien		case FILE_BELONG:
1470133359Sobrien		case FILE_LELONG:
1471159764Sobrien		case FILE_MELONG:
1472175296Sobrien		case FILE_FLOAT:
1473175296Sobrien		case FILE_BEFLOAT:
1474175296Sobrien		case FILE_LEFLOAT:
1475103373Sobrien			v = (int32_t) v;
147668349Sobrien			break;
1477169942Sobrien		case FILE_QUAD:
1478169942Sobrien		case FILE_BEQUAD:
1479169942Sobrien		case FILE_LEQUAD:
1480169942Sobrien		case FILE_QDATE:
1481169942Sobrien		case FILE_QLDATE:
1482267843Sdelphij		case FILE_QWDATE:
1483169942Sobrien		case FILE_BEQDATE:
1484169942Sobrien		case FILE_BEQLDATE:
1485267843Sdelphij		case FILE_BEQWDATE:
1486169942Sobrien		case FILE_LEQDATE:
1487169942Sobrien		case FILE_LEQLDATE:
1488267843Sdelphij		case FILE_LEQWDATE:
1489175296Sobrien		case FILE_DOUBLE:
1490175296Sobrien		case FILE_BEDOUBLE:
1491175296Sobrien		case FILE_LEDOUBLE:
1492169942Sobrien			v = (int64_t) v;
1493169942Sobrien			break;
1494133359Sobrien		case FILE_STRING:
1495133359Sobrien		case FILE_PSTRING:
1496139368Sobrien		case FILE_BESTRING16:
1497139368Sobrien		case FILE_LESTRING16:
1498133359Sobrien		case FILE_REGEX:
1499159764Sobrien		case FILE_SEARCH:
1500169962Sobrien		case FILE_DEFAULT:
1501191736Sobrien		case FILE_INDIRECT:
1502267843Sdelphij		case FILE_NAME:
1503267843Sdelphij		case FILE_USE:
1504267843Sdelphij		case FILE_CLEAR:
1505330569Sgordon		case FILE_DER:
1506103373Sobrien			break;
150768349Sobrien		default:
1508133359Sobrien			if (ms->flags & MAGIC_CHECK)
1509139368Sobrien			    file_magwarn(ms, "cannot happen: m->type=%d\n",
1510133359Sobrien				    m->type);
1511133359Sobrien			return ~0U;
151268349Sobrien		}
1513169962Sobrien	}
151468349Sobrien	return v;
151568349Sobrien}
151668349Sobrien
1517169962Sobrienprivate int
1518186690Sobrienstring_modifier_check(struct magic_set *ms, struct magic *m)
1519169962Sobrien{
1520169962Sobrien	if ((ms->flags & MAGIC_CHECK) == 0)
1521169962Sobrien		return 0;
1522169962Sobrien
1523267843Sdelphij	if ((m->type != FILE_REGEX || (m->str_flags & REGEX_LINE_COUNT) == 0) &&
1524267843Sdelphij	    (m->type != FILE_PSTRING && (m->str_flags & PSTRING_LEN) != 0)) {
1525226048Sobrien		file_magwarn(ms,
1526226048Sobrien		    "'/BHhLl' modifiers are only allowed for pascal strings\n");
1527226048Sobrien		return -1;
1528226048Sobrien	}
1529169962Sobrien	switch (m->type) {
1530169962Sobrien	case FILE_BESTRING16:
1531169962Sobrien	case FILE_LESTRING16:
1532169962Sobrien		if (m->str_flags != 0) {
1533186690Sobrien			file_magwarn(ms,
1534186690Sobrien			    "no modifiers allowed for 16-bit strings\n");
1535169962Sobrien			return -1;
1536169962Sobrien		}
1537169962Sobrien		break;
1538169962Sobrien	case FILE_STRING:
1539169962Sobrien	case FILE_PSTRING:
1540169962Sobrien		if ((m->str_flags & REGEX_OFFSET_START) != 0) {
1541186690Sobrien			file_magwarn(ms,
1542186690Sobrien			    "'/%c' only allowed on regex and search\n",
1543169962Sobrien			    CHAR_REGEX_OFFSET_START);
1544169962Sobrien			return -1;
1545169962Sobrien		}
1546169962Sobrien		break;
1547169962Sobrien	case FILE_SEARCH:
1548186690Sobrien		if (m->str_range == 0) {
1549186690Sobrien			file_magwarn(ms,
1550186690Sobrien			    "missing range; defaulting to %d\n",
1551186690Sobrien                            STRING_DEFAULT_RANGE);
1552186690Sobrien			m->str_range = STRING_DEFAULT_RANGE;
1553186690Sobrien			return -1;
1554186690Sobrien		}
1555169962Sobrien		break;
1556169962Sobrien	case FILE_REGEX:
1557226048Sobrien		if ((m->str_flags & STRING_COMPACT_WHITESPACE) != 0) {
1558169962Sobrien			file_magwarn(ms, "'/%c' not allowed on regex\n",
1559226048Sobrien			    CHAR_COMPACT_WHITESPACE);
1560169962Sobrien			return -1;
1561169962Sobrien		}
1562226048Sobrien		if ((m->str_flags & STRING_COMPACT_OPTIONAL_WHITESPACE) != 0) {
1563169962Sobrien			file_magwarn(ms, "'/%c' not allowed on regex\n",
1564226048Sobrien			    CHAR_COMPACT_OPTIONAL_WHITESPACE);
1565169962Sobrien			return -1;
1566169962Sobrien		}
1567169962Sobrien		break;
1568169962Sobrien	default:
1569169962Sobrien		file_magwarn(ms, "coding error: m->type=%d\n",
1570169962Sobrien		    m->type);
1571169962Sobrien		return -1;
1572169962Sobrien	}
1573169962Sobrien	return 0;
1574169962Sobrien}
1575169962Sobrien
1576169962Sobrienprivate int
1577169962Sobrienget_op(char c)
1578169962Sobrien{
1579169962Sobrien	switch (c) {
1580169962Sobrien	case '&':
1581169962Sobrien		return FILE_OPAND;
1582169962Sobrien	case '|':
1583169962Sobrien		return FILE_OPOR;
1584169962Sobrien	case '^':
1585169962Sobrien		return FILE_OPXOR;
1586169962Sobrien	case '+':
1587169962Sobrien		return FILE_OPADD;
1588169962Sobrien	case '-':
1589169962Sobrien		return FILE_OPMINUS;
1590169962Sobrien	case '*':
1591169962Sobrien		return FILE_OPMULTIPLY;
1592169962Sobrien	case '/':
1593169962Sobrien		return FILE_OPDIVIDE;
1594169962Sobrien	case '%':
1595169962Sobrien		return FILE_OPMODULO;
1596169962Sobrien	default:
1597169962Sobrien		return -1;
1598169962Sobrien	}
1599169962Sobrien}
1600169962Sobrien
1601169962Sobrien#ifdef ENABLE_CONDITIONALS
1602169962Sobrienprivate int
1603169962Sobrienget_cond(const char *l, const char **t)
1604169962Sobrien{
1605186690Sobrien	static const struct cond_tbl_s {
1606186690Sobrien		char name[8];
1607186690Sobrien		size_t len;
1608186690Sobrien		int cond;
1609169962Sobrien	} cond_tbl[] = {
1610169962Sobrien		{ "if",		2,	COND_IF },
1611169962Sobrien		{ "elif",	4,	COND_ELIF },
1612169962Sobrien		{ "else",	4,	COND_ELSE },
1613186690Sobrien		{ "",		0,	COND_NONE },
1614169962Sobrien	};
1615186690Sobrien	const struct cond_tbl_s *p;
1616169962Sobrien
1617186690Sobrien	for (p = cond_tbl; p->len; p++) {
1618169962Sobrien		if (strncmp(l, p->name, p->len) == 0 &&
1619169962Sobrien		    isspace((unsigned char)l[p->len])) {
1620169962Sobrien			if (t)
1621169962Sobrien				*t = l + p->len;
1622169962Sobrien			break;
1623169962Sobrien		}
1624169962Sobrien	}
1625169962Sobrien	return p->cond;
1626169962Sobrien}
1627169962Sobrien
1628169962Sobrienprivate int
1629169962Sobriencheck_cond(struct magic_set *ms, int cond, uint32_t cont_level)
1630169962Sobrien{
1631169962Sobrien	int last_cond;
1632169962Sobrien	last_cond = ms->c.li[cont_level].last_cond;
1633169962Sobrien
1634169962Sobrien	switch (cond) {
1635169962Sobrien	case COND_IF:
1636169962Sobrien		if (last_cond != COND_NONE && last_cond != COND_ELIF) {
1637169962Sobrien			if (ms->flags & MAGIC_CHECK)
1638169962Sobrien				file_magwarn(ms, "syntax error: `if'");
1639169962Sobrien			return -1;
1640169962Sobrien		}
1641169962Sobrien		last_cond = COND_IF;
1642169962Sobrien		break;
1643169962Sobrien
1644169962Sobrien	case COND_ELIF:
1645169962Sobrien		if (last_cond != COND_IF && last_cond != COND_ELIF) {
1646169962Sobrien			if (ms->flags & MAGIC_CHECK)
1647169962Sobrien				file_magwarn(ms, "syntax error: `elif'");
1648169962Sobrien			return -1;
1649169962Sobrien		}
1650169962Sobrien		last_cond = COND_ELIF;
1651169962Sobrien		break;
1652169962Sobrien
1653169962Sobrien	case COND_ELSE:
1654169962Sobrien		if (last_cond != COND_IF && last_cond != COND_ELIF) {
1655169962Sobrien			if (ms->flags & MAGIC_CHECK)
1656169962Sobrien				file_magwarn(ms, "syntax error: `else'");
1657169962Sobrien			return -1;
1658169962Sobrien		}
1659169962Sobrien		last_cond = COND_NONE;
1660169962Sobrien		break;
1661169962Sobrien
1662169962Sobrien	case COND_NONE:
1663169962Sobrien		last_cond = COND_NONE;
1664169962Sobrien		break;
1665169962Sobrien	}
1666169962Sobrien
1667169962Sobrien	ms->c.li[cont_level].last_cond = last_cond;
1668169962Sobrien	return 0;
1669169962Sobrien}
1670169962Sobrien#endif /* ENABLE_CONDITIONALS */
1671169962Sobrien
1672277592Sdelphijprivate int
1673277592Sdelphijparse_indirect_modifier(struct magic_set *ms, struct magic *m, const char **lp)
1674277592Sdelphij{
1675277592Sdelphij	const char *l = *lp;
1676277592Sdelphij
1677277592Sdelphij	while (!isspace((unsigned char)*++l))
1678277592Sdelphij		switch (*l) {
1679277592Sdelphij		case CHAR_INDIRECT_RELATIVE:
1680277592Sdelphij			m->str_flags |= INDIRECT_RELATIVE;
1681277592Sdelphij			break;
1682277592Sdelphij		default:
1683277592Sdelphij			if (ms->flags & MAGIC_CHECK)
1684277592Sdelphij				file_magwarn(ms, "indirect modifier `%c' "
1685277592Sdelphij					"invalid", *l);
1686277592Sdelphij			*lp = l;
1687277592Sdelphij			return -1;
1688277592Sdelphij		}
1689277592Sdelphij	*lp = l;
1690277592Sdelphij	return 0;
1691277592Sdelphij}
1692277592Sdelphij
1693277592Sdelphijprivate void
1694277592Sdelphijparse_op_modifier(struct magic_set *ms, struct magic *m, const char **lp,
1695277592Sdelphij    int op)
1696277592Sdelphij{
1697277592Sdelphij	const char *l = *lp;
1698277592Sdelphij	char *t;
1699277592Sdelphij	uint64_t val;
1700277592Sdelphij
1701277592Sdelphij	++l;
1702277592Sdelphij	m->mask_op |= op;
1703277592Sdelphij	val = (uint64_t)strtoull(l, &t, 0);
1704277592Sdelphij	l = t;
1705277592Sdelphij	m->num_mask = file_signextend(ms, m, val);
1706277592Sdelphij	eatsize(&l);
1707277592Sdelphij	*lp = l;
1708277592Sdelphij}
1709277592Sdelphij
1710277592Sdelphijprivate int
1711277592Sdelphijparse_string_modifier(struct magic_set *ms, struct magic *m, const char **lp)
1712277592Sdelphij{
1713277592Sdelphij	const char *l = *lp;
1714277592Sdelphij	char *t;
1715277592Sdelphij	int have_range = 0;
1716277592Sdelphij
1717277592Sdelphij	while (!isspace((unsigned char)*++l)) {
1718277592Sdelphij		switch (*l) {
1719277592Sdelphij		case '0':  case '1':  case '2':
1720277592Sdelphij		case '3':  case '4':  case '5':
1721277592Sdelphij		case '6':  case '7':  case '8':
1722277592Sdelphij		case '9':
1723277592Sdelphij			if (have_range && (ms->flags & MAGIC_CHECK))
1724277592Sdelphij				file_magwarn(ms, "multiple ranges");
1725277592Sdelphij			have_range = 1;
1726277592Sdelphij			m->str_range = CAST(uint32_t, strtoul(l, &t, 0));
1727277592Sdelphij			if (m->str_range == 0)
1728277592Sdelphij				file_magwarn(ms, "zero range");
1729277592Sdelphij			l = t - 1;
1730277592Sdelphij			break;
1731277592Sdelphij		case CHAR_COMPACT_WHITESPACE:
1732277592Sdelphij			m->str_flags |= STRING_COMPACT_WHITESPACE;
1733277592Sdelphij			break;
1734277592Sdelphij		case CHAR_COMPACT_OPTIONAL_WHITESPACE:
1735277592Sdelphij			m->str_flags |= STRING_COMPACT_OPTIONAL_WHITESPACE;
1736277592Sdelphij			break;
1737277592Sdelphij		case CHAR_IGNORE_LOWERCASE:
1738277592Sdelphij			m->str_flags |= STRING_IGNORE_LOWERCASE;
1739277592Sdelphij			break;
1740277592Sdelphij		case CHAR_IGNORE_UPPERCASE:
1741277592Sdelphij			m->str_flags |= STRING_IGNORE_UPPERCASE;
1742277592Sdelphij			break;
1743277592Sdelphij		case CHAR_REGEX_OFFSET_START:
1744277592Sdelphij			m->str_flags |= REGEX_OFFSET_START;
1745277592Sdelphij			break;
1746277592Sdelphij		case CHAR_BINTEST:
1747277592Sdelphij			m->str_flags |= STRING_BINTEST;
1748277592Sdelphij			break;
1749277592Sdelphij		case CHAR_TEXTTEST:
1750277592Sdelphij			m->str_flags |= STRING_TEXTTEST;
1751277592Sdelphij			break;
1752277592Sdelphij		case CHAR_TRIM:
1753277592Sdelphij			m->str_flags |= STRING_TRIM;
1754277592Sdelphij			break;
1755277592Sdelphij		case CHAR_PSTRING_1_LE:
1756277592Sdelphij#define SET_LENGTH(a) m->str_flags = (m->str_flags & ~PSTRING_LEN) | (a)
1757277592Sdelphij			if (m->type != FILE_PSTRING)
1758277592Sdelphij				goto bad;
1759277592Sdelphij			SET_LENGTH(PSTRING_1_LE);
1760277592Sdelphij			break;
1761277592Sdelphij		case CHAR_PSTRING_2_BE:
1762277592Sdelphij			if (m->type != FILE_PSTRING)
1763277592Sdelphij				goto bad;
1764277592Sdelphij			SET_LENGTH(PSTRING_2_BE);
1765277592Sdelphij			break;
1766277592Sdelphij		case CHAR_PSTRING_2_LE:
1767277592Sdelphij			if (m->type != FILE_PSTRING)
1768277592Sdelphij				goto bad;
1769277592Sdelphij			SET_LENGTH(PSTRING_2_LE);
1770277592Sdelphij			break;
1771277592Sdelphij		case CHAR_PSTRING_4_BE:
1772277592Sdelphij			if (m->type != FILE_PSTRING)
1773277592Sdelphij				goto bad;
1774277592Sdelphij			SET_LENGTH(PSTRING_4_BE);
1775277592Sdelphij			break;
1776277592Sdelphij		case CHAR_PSTRING_4_LE:
1777277592Sdelphij			switch (m->type) {
1778277592Sdelphij			case FILE_PSTRING:
1779277592Sdelphij			case FILE_REGEX:
1780277592Sdelphij				break;
1781277592Sdelphij			default:
1782277592Sdelphij				goto bad;
1783277592Sdelphij			}
1784277592Sdelphij			SET_LENGTH(PSTRING_4_LE);
1785277592Sdelphij			break;
1786277592Sdelphij		case CHAR_PSTRING_LENGTH_INCLUDES_ITSELF:
1787277592Sdelphij			if (m->type != FILE_PSTRING)
1788277592Sdelphij				goto bad;
1789277592Sdelphij			m->str_flags |= PSTRING_LENGTH_INCLUDES_ITSELF;
1790277592Sdelphij			break;
1791277592Sdelphij		default:
1792277592Sdelphij		bad:
1793277592Sdelphij			if (ms->flags & MAGIC_CHECK)
1794277592Sdelphij				file_magwarn(ms, "string modifier `%c' "
1795277592Sdelphij					"invalid", *l);
1796277592Sdelphij			goto out;
1797277592Sdelphij		}
1798277592Sdelphij		/* allow multiple '/' for readability */
1799277592Sdelphij		if (l[1] == '/' && !isspace((unsigned char)l[2]))
1800277592Sdelphij			l++;
1801277592Sdelphij	}
1802277592Sdelphij	if (string_modifier_check(ms, m) == -1)
1803277592Sdelphij		goto out;
1804277592Sdelphij	*lp = l;
1805277592Sdelphij	return 0;
1806277592Sdelphijout:
1807277592Sdelphij	*lp = l;
1808277592Sdelphij	return -1;
1809277592Sdelphij}
1810277592Sdelphij
181168349Sobrien/*
181268349Sobrien * parse one line from magic file, put into magic[index++] if valid
181368349Sobrien */
1814133359Sobrienprivate int
1815267843Sdelphijparse(struct magic_set *ms, struct magic_entry *me, const char *line,
1816267843Sdelphij    size_t lineno, int action)
181768349Sobrien{
1818169962Sobrien#ifdef ENABLE_CONDITIONALS
1819169962Sobrien	static uint32_t last_cont_level = 0;
1820169962Sobrien#endif
1821169942Sobrien	size_t i;
182268349Sobrien	struct magic *m;
1823159764Sobrien	const char *l = line;
182484685Sobrien	char *t;
1825169962Sobrien	int op;
1826159825Sobrien	uint32_t cont_level;
1827267843Sdelphij	int32_t diff;
182868349Sobrien
1829159764Sobrien	cont_level = 0;
183068349Sobrien
1831267843Sdelphij	/*
1832267843Sdelphij	 * Parse the offset.
1833267843Sdelphij	 */
183468349Sobrien	while (*l == '>') {
183568349Sobrien		++l;		/* step over */
1836159764Sobrien		cont_level++;
183768349Sobrien	}
1838169962Sobrien#ifdef ENABLE_CONDITIONALS
1839169962Sobrien	if (cont_level == 0 || cont_level > last_cont_level)
1840169962Sobrien		if (file_check_mem(ms, cont_level) == -1)
1841169962Sobrien			return -1;
1842169962Sobrien	last_cont_level = cont_level;
1843169962Sobrien#endif
1844159764Sobrien	if (cont_level != 0) {
1845267843Sdelphij		if (me->mp == NULL) {
1846267843Sdelphij			file_magerror(ms, "No current entry for continuation");
1847159764Sobrien			return -1;
1848159764Sobrien		}
1849267843Sdelphij		if (me->cont_count == 0) {
1850267843Sdelphij			file_magerror(ms, "Continuations present with 0 count");
1851267843Sdelphij			return -1;
1852267843Sdelphij		}
1853267843Sdelphij		m = &me->mp[me->cont_count - 1];
1854267843Sdelphij		diff = (int32_t)cont_level - (int32_t)m->cont_level;
1855267843Sdelphij		if (diff > 1)
1856267843Sdelphij			file_magwarn(ms, "New continuation level %u is more "
1857267843Sdelphij			    "than one larger than current level %u", cont_level,
1858267843Sdelphij			    m->cont_level);
1859159764Sobrien		if (me->cont_count == me->max_count) {
1860159764Sobrien			struct magic *nm;
1861159764Sobrien			size_t cnt = me->max_count + ALLOC_CHUNK;
1862186690Sobrien			if ((nm = CAST(struct magic *, realloc(me->mp,
1863186690Sobrien			    sizeof(*nm) * cnt))) == NULL) {
1864169942Sobrien				file_oomem(ms, sizeof(*nm) * cnt);
1865159764Sobrien				return -1;
1866159764Sobrien			}
1867159764Sobrien			me->mp = m = nm;
1868226048Sobrien			me->max_count = CAST(uint32_t, cnt);
1869159764Sobrien		}
1870159764Sobrien		m = &me->mp[me->cont_count++];
1871169962Sobrien		(void)memset(m, 0, sizeof(*m));
1872159764Sobrien		m->cont_level = cont_level;
1873159764Sobrien	} else {
1874267843Sdelphij		static const size_t len = sizeof(*m) * ALLOC_CHUNK;
1875267843Sdelphij		if (me->mp != NULL)
1876267843Sdelphij			return 1;
1877267843Sdelphij		if ((m = CAST(struct magic *, malloc(len))) == NULL) {
1878267843Sdelphij			file_oomem(ms, len);
1879267843Sdelphij			return -1;
1880159764Sobrien		}
1881267843Sdelphij		me->mp = m;
1882267843Sdelphij		me->max_count = ALLOC_CHUNK;
1883169962Sobrien		(void)memset(m, 0, sizeof(*m));
1884186690Sobrien		m->factor_op = FILE_FACTOR_OP_NONE;
1885159764Sobrien		m->cont_level = 0;
1886159764Sobrien		me->cont_count = 1;
1887159764Sobrien	}
1888226048Sobrien	m->lineno = CAST(uint32_t, lineno);
1889159764Sobrien
1890169962Sobrien	if (*l == '&') {  /* m->cont_level == 0 checked below. */
1891159764Sobrien                ++l;            /* step over */
1892159764Sobrien                m->flag |= OFFADD;
1893159764Sobrien        }
1894169962Sobrien	if (*l == '(') {
189568349Sobrien		++l;		/* step over */
189668349Sobrien		m->flag |= INDIR;
1897159764Sobrien		if (m->flag & OFFADD)
1898159764Sobrien			m->flag = (m->flag & ~OFFADD) | INDIROFFADD;
1899169962Sobrien
1900169962Sobrien		if (*l == '&') {  /* m->cont_level == 0 checked below */
1901169962Sobrien			++l;            /* step over */
1902169962Sobrien			m->flag |= OFFADD;
1903169962Sobrien		}
190468349Sobrien	}
1905169962Sobrien	/* Indirect offsets are not valid at level 0. */
1906284778Sdelphij	if (m->cont_level == 0 && (m->flag & (OFFADD | INDIROFFADD))) {
1907169962Sobrien		if (ms->flags & MAGIC_CHECK)
1908169962Sobrien			file_magwarn(ms, "relative offset at level 0");
1909284778Sdelphij		return -1;
1910284778Sdelphij	}
191168349Sobrien
191268349Sobrien	/* get offset, then skip over it */
1913133359Sobrien	m->offset = (uint32_t)strtoul(l, &t, 0);
1914284778Sdelphij        if (l == t) {
1915133359Sobrien		if (ms->flags & MAGIC_CHECK)
1916139368Sobrien			file_magwarn(ms, "offset `%s' invalid", l);
1917284778Sdelphij		return -1;
1918284778Sdelphij	}
191968349Sobrien        l = t;
192068349Sobrien
192168349Sobrien	if (m->flag & INDIR) {
1922133359Sobrien		m->in_type = FILE_LONG;
192374784Sobrien		m->in_offset = 0;
1924330569Sgordon		m->in_op = 0;
192568349Sobrien		/*
1926330569Sgordon		 * read [.,lbs][+-]nnnnn)
192768349Sobrien		 */
1928330569Sgordon		if (*l == '.' || *l == ',') {
1929330569Sgordon			if (*l == ',')
1930330569Sgordon				m->in_op |= FILE_OPSIGNED;
193168349Sobrien			l++;
193268349Sobrien			switch (*l) {
193368349Sobrien			case 'l':
1934133359Sobrien				m->in_type = FILE_LELONG;
193568349Sobrien				break;
193668349Sobrien			case 'L':
1937133359Sobrien				m->in_type = FILE_BELONG;
193868349Sobrien				break;
1939159764Sobrien			case 'm':
1940159764Sobrien				m->in_type = FILE_MELONG;
1941159764Sobrien				break;
194268349Sobrien			case 'h':
194368349Sobrien			case 's':
1944133359Sobrien				m->in_type = FILE_LESHORT;
194568349Sobrien				break;
194668349Sobrien			case 'H':
194768349Sobrien			case 'S':
1948133359Sobrien				m->in_type = FILE_BESHORT;
194968349Sobrien				break;
195068349Sobrien			case 'c':
195168349Sobrien			case 'b':
195268349Sobrien			case 'C':
195368349Sobrien			case 'B':
1954133359Sobrien				m->in_type = FILE_BYTE;
195568349Sobrien				break;
1956175296Sobrien			case 'e':
1957175296Sobrien			case 'f':
1958175296Sobrien			case 'g':
1959175296Sobrien				m->in_type = FILE_LEDOUBLE;
1960175296Sobrien				break;
1961175296Sobrien			case 'E':
1962175296Sobrien			case 'F':
1963175296Sobrien			case 'G':
1964175296Sobrien				m->in_type = FILE_BEDOUBLE;
1965175296Sobrien				break;
1966191736Sobrien			case 'i':
1967191736Sobrien				m->in_type = FILE_LEID3;
1968191736Sobrien				break;
1969191736Sobrien			case 'I':
1970191736Sobrien				m->in_type = FILE_BEID3;
1971191736Sobrien				break;
197268349Sobrien			default:
1973133359Sobrien				if (ms->flags & MAGIC_CHECK)
1974139368Sobrien					file_magwarn(ms,
1975139368Sobrien					    "indirect offset type `%c' invalid",
1976133359Sobrien					    *l);
1977284778Sdelphij				return -1;
197868349Sobrien			}
197968349Sobrien			l++;
198068349Sobrien		}
1981169962Sobrien
198280588Sobrien		if (*l == '~') {
1983159764Sobrien			m->in_op |= FILE_OPINVERSE;
198480588Sobrien			l++;
198580588Sobrien		}
1986169962Sobrien		if ((op = get_op(*l)) != -1) {
1987169962Sobrien			m->in_op |= op;
198880588Sobrien			l++;
198980588Sobrien		}
1990159764Sobrien		if (*l == '(') {
1991159764Sobrien			m->in_op |= FILE_OPINDIRECT;
1992159764Sobrien			l++;
1993159764Sobrien		}
1994159764Sobrien		if (isdigit((unsigned char)*l) || *l == '-') {
1995159764Sobrien			m->in_offset = (int32_t)strtol(l, &t, 0);
1996284778Sdelphij			if (l == t) {
1997169962Sobrien				if (ms->flags & MAGIC_CHECK)
1998169962Sobrien					file_magwarn(ms,
1999169962Sobrien					    "in_offset `%s' invalid", l);
2000284778Sdelphij				return -1;
2001284778Sdelphij			}
2002159764Sobrien			l = t;
2003159764Sobrien		}
2004159764Sobrien		if (*l++ != ')' ||
2005284778Sdelphij		    ((m->in_op & FILE_OPINDIRECT) && *l++ != ')')) {
2006133359Sobrien			if (ms->flags & MAGIC_CHECK)
2007139368Sobrien				file_magwarn(ms,
2008139368Sobrien				    "missing ')' in indirect offset");
2009284778Sdelphij			return -1;
2010284778Sdelphij		}
201168349Sobrien	}
2012169962Sobrien	EATAB;
201368349Sobrien
2014169962Sobrien#ifdef ENABLE_CONDITIONALS
2015169962Sobrien	m->cond = get_cond(l, &l);
2016169962Sobrien	if (check_cond(ms, m->cond, cont_level) == -1)
2017169962Sobrien		return -1;
201868349Sobrien
201968349Sobrien	EATAB;
2020169962Sobrien#endif
202168349Sobrien
2022267843Sdelphij	/*
2023267843Sdelphij	 * Parse the type.
2024267843Sdelphij	 */
202568349Sobrien	if (*l == 'u') {
2026267843Sdelphij		/*
2027267843Sdelphij		 * Try it as a keyword type prefixed by "u"; match what
2028267843Sdelphij		 * follows the "u".  If that fails, try it as an SUS
2029267843Sdelphij		 * integer type.
2030267843Sdelphij		 */
2031267843Sdelphij		m->type = get_type(type_tbl, l + 1, &l);
2032267843Sdelphij		if (m->type == FILE_INVALID) {
2033267843Sdelphij			/*
2034267843Sdelphij			 * Not a keyword type; parse it as an SUS type,
2035267843Sdelphij			 * 'u' possibly followed by a number or C/S/L.
2036267843Sdelphij			 */
2037267843Sdelphij			m->type = get_standard_integer_type(l, &l);
2038267843Sdelphij		}
2039267843Sdelphij		/* It's unsigned. */
2040267843Sdelphij		if (m->type != FILE_INVALID)
2041267843Sdelphij			m->flag |= UNSIGNED;
2042267843Sdelphij	} else {
2043267843Sdelphij		/*
2044267843Sdelphij		 * Try it as a keyword type.  If that fails, try it as
2045267843Sdelphij		 * an SUS integer type if it begins with "d" or as an
2046267843Sdelphij		 * SUS string type if it begins with "s".  In any case,
2047267843Sdelphij		 * it's not unsigned.
2048267843Sdelphij		 */
2049267843Sdelphij		m->type = get_type(type_tbl, l, &l);
2050267843Sdelphij		if (m->type == FILE_INVALID) {
2051267843Sdelphij			/*
2052267843Sdelphij			 * Not a keyword type; parse it as an SUS type,
2053267843Sdelphij			 * either 'd' possibly followed by a number or
2054267843Sdelphij			 * C/S/L, or just 's'.
2055267843Sdelphij			 */
2056267843Sdelphij			if (*l == 'd')
2057267843Sdelphij				m->type = get_standard_integer_type(l, &l);
2058267843Sdelphij			else if (*l == 's' && !isalpha((unsigned char)l[1])) {
2059267843Sdelphij				m->type = FILE_STRING;
2060267843Sdelphij				++l;
2061267843Sdelphij			}
2062267843Sdelphij		}
206368349Sobrien	}
206468349Sobrien
2065169962Sobrien	if (m->type == FILE_INVALID) {
2066267843Sdelphij		/* Not found - try it as a special keyword. */
2067267843Sdelphij		m->type = get_type(special_tbl, l, &l);
2068267843Sdelphij	}
2069267843Sdelphij
2070267843Sdelphij	if (m->type == FILE_INVALID) {
2071133359Sobrien		if (ms->flags & MAGIC_CHECK)
2072139368Sobrien			file_magwarn(ms, "type `%s' invalid", l);
207368349Sobrien		return -1;
207468349Sobrien	}
2075169962Sobrien
207668349Sobrien	/* New-style anding: "0 byte&0x80 =0x80 dynamically linked" */
207780588Sobrien	/* New and improved: ~ & | ^ + - * / % -- exciting, isn't it? */
2078169962Sobrien
2079169962Sobrien	m->mask_op = 0;
208080588Sobrien	if (*l == '~') {
2081139368Sobrien		if (!IS_STRING(m->type))
2082159764Sobrien			m->mask_op |= FILE_OPINVERSE;
2083169962Sobrien		else if (ms->flags & MAGIC_CHECK)
2084169962Sobrien			file_magwarn(ms, "'~' invalid for string types");
208568349Sobrien		++l;
208680588Sobrien	}
2087186690Sobrien	m->str_range = 0;
2088226048Sobrien	m->str_flags = m->type == FILE_PSTRING ? PSTRING_1_LE : 0;
2089169962Sobrien	if ((op = get_op(*l)) != -1) {
2090277592Sdelphij		if (IS_STRING(m->type)) {
2091277592Sdelphij			int r;
2092277592Sdelphij
2093277592Sdelphij			if (op != FILE_OPDIVIDE) {
2094277592Sdelphij				if (ms->flags & MAGIC_CHECK)
2095277592Sdelphij					file_magwarn(ms,
2096277592Sdelphij					    "invalid string/indirect op: "
2097277592Sdelphij					    "`%c'", *t);
2098277592Sdelphij				return -1;
209968349Sobrien			}
2100277592Sdelphij
2101277592Sdelphij			if (m->type == FILE_INDIRECT)
2102277592Sdelphij				r = parse_indirect_modifier(ms, m, &l);
2103277592Sdelphij			else
2104277592Sdelphij				r = parse_string_modifier(ms, m, &l);
2105277592Sdelphij			if (r == -1)
2106169962Sobrien				return -1;
2107277592Sdelphij		} else
2108277592Sdelphij			parse_op_modifier(ms, m, &l, op);
210980588Sobrien	}
2110277592Sdelphij
2111133359Sobrien	/*
2112133359Sobrien	 * We used to set mask to all 1's here, instead let's just not do
2113133359Sobrien	 * anything if mask = 0 (unless you have a better idea)
2114133359Sobrien	 */
211568349Sobrien	EATAB;
211668349Sobrien
211768349Sobrien	switch (*l) {
211868349Sobrien	case '>':
211968349Sobrien	case '<':
2120186690Sobrien  		m->reln = *l;
2121186690Sobrien  		++l;
2122186690Sobrien		if (*l == '=') {
2123186690Sobrien			if (ms->flags & MAGIC_CHECK) {
2124186690Sobrien				file_magwarn(ms, "%c= not supported",
2125186690Sobrien				    m->reln);
2126186690Sobrien				return -1;
2127186690Sobrien			}
2128186690Sobrien		   ++l;
2129186690Sobrien		}
2130186690Sobrien		break;
213168349Sobrien	/* Old-style anding: "0 byte &0x80 dynamically linked" */
213268349Sobrien	case '&':
213368349Sobrien	case '^':
213468349Sobrien	case '=':
213568349Sobrien  		m->reln = *l;
213668349Sobrien  		++l;
213768349Sobrien		if (*l == '=') {
213868349Sobrien		   /* HP compat: ignore &= etc. */
213968349Sobrien		   ++l;
214068349Sobrien		}
214168349Sobrien		break;
214268349Sobrien	case '!':
2143159764Sobrien		m->reln = *l;
2144159764Sobrien		++l;
2145159764Sobrien		break;
214668349Sobrien	default:
2147169962Sobrien  		m->reln = '=';	/* the default relation */
2148159764Sobrien		if (*l == 'x' && ((isascii((unsigned char)l[1]) &&
2149159764Sobrien		    isspace((unsigned char)l[1])) || !l[1])) {
215068349Sobrien			m->reln = *l;
215168349Sobrien			++l;
215268349Sobrien		}
215368349Sobrien		break;
215468349Sobrien	}
2155169962Sobrien	/*
2156169962Sobrien	 * Grab the value part, except for an 'x' reln.
2157169962Sobrien	 */
2158169962Sobrien	if (m->reln != 'x' && getvalue(ms, m, &l, action))
215968349Sobrien		return -1;
2160169962Sobrien
216168349Sobrien	/*
216268349Sobrien	 * TODO finish this macro and start using it!
2163330569Sgordon	 * #define offsetcheck {if (offset > ms->bytes_max -1)
216468349Sobrien	 *	magwarn("offset too big"); }
216568349Sobrien	 */
216668349Sobrien
216768349Sobrien	/*
2168169962Sobrien	 * Now get last part - the description
216968349Sobrien	 */
217068349Sobrien	EATAB;
217168349Sobrien	if (l[0] == '\b') {
217268349Sobrien		++l;
2173186690Sobrien		m->flag |= NOSPACE;
217468349Sobrien	} else if ((l[0] == '\\') && (l[1] == 'b')) {
217568349Sobrien		++l;
217668349Sobrien		++l;
2177186690Sobrien		m->flag |= NOSPACE;
2178186690Sobrien	}
2179169942Sobrien	for (i = 0; (m->desc[i++] = *l++) != '\0' && i < sizeof(m->desc); )
2180169942Sobrien		continue;
2181169942Sobrien	if (i == sizeof(m->desc)) {
2182169942Sobrien		m->desc[sizeof(m->desc) - 1] = '\0';
2183169942Sobrien		if (ms->flags & MAGIC_CHECK)
2184169942Sobrien			file_magwarn(ms, "description `%s' truncated", m->desc);
2185169942Sobrien	}
218668349Sobrien
2187169942Sobrien        /*
2188169942Sobrien	 * We only do this check while compiling, or if any of the magic
2189169942Sobrien	 * files were not compiled.
2190169942Sobrien         */
2191169942Sobrien        if (ms->flags & MAGIC_CHECK) {
2192169942Sobrien		if (check_format(ms, m) == -1)
2193133359Sobrien			return -1;
2194133359Sobrien	}
2195103373Sobrien#ifndef COMPILE_ONLY
2196133359Sobrien	if (action == FILE_CHECK) {
2197133359Sobrien		file_mdump(m);
219868349Sobrien	}
2199103373Sobrien#endif
2200186690Sobrien	m->mimetype[0] = '\0';		/* initialise MIME type to none */
220168349Sobrien	return 0;
220268349Sobrien}
220368349Sobrien
2204186690Sobrien/*
2205186690Sobrien * parse a STRENGTH annotation line from magic file, put into magic[index - 1]
2206186690Sobrien * if valid
2207186690Sobrien */
2208169942Sobrienprivate int
2209186690Sobrienparse_strength(struct magic_set *ms, struct magic_entry *me, const char *line)
2210186690Sobrien{
2211186690Sobrien	const char *l = line;
2212186690Sobrien	char *el;
2213186690Sobrien	unsigned long factor;
2214186690Sobrien	struct magic *m = &me->mp[0];
2215186690Sobrien
2216186690Sobrien	if (m->factor_op != FILE_FACTOR_OP_NONE) {
2217186690Sobrien		file_magwarn(ms,
2218186690Sobrien		    "Current entry already has a strength type: %c %d",
2219186690Sobrien		    m->factor_op, m->factor);
2220186690Sobrien		return -1;
2221186690Sobrien	}
2222267843Sdelphij	if (m->type == FILE_NAME) {
2223267843Sdelphij		file_magwarn(ms, "%s: Strength setting is not supported in "
2224267843Sdelphij		    "\"name\" magic entries", m->value.s);
2225267843Sdelphij		return -1;
2226267843Sdelphij	}
2227186690Sobrien	EATAB;
2228186690Sobrien	switch (*l) {
2229186690Sobrien	case FILE_FACTOR_OP_NONE:
2230186690Sobrien	case FILE_FACTOR_OP_PLUS:
2231186690Sobrien	case FILE_FACTOR_OP_MINUS:
2232186690Sobrien	case FILE_FACTOR_OP_TIMES:
2233186690Sobrien	case FILE_FACTOR_OP_DIV:
2234186690Sobrien		m->factor_op = *l++;
2235186690Sobrien		break;
2236186690Sobrien	default:
2237186690Sobrien		file_magwarn(ms, "Unknown factor op `%c'", *l);
2238186690Sobrien		return -1;
2239186690Sobrien	}
2240186690Sobrien	EATAB;
2241186690Sobrien	factor = strtoul(l, &el, 0);
2242186690Sobrien	if (factor > 255) {
2243186690Sobrien		file_magwarn(ms, "Too large factor `%lu'", factor);
2244186690Sobrien		goto out;
2245186690Sobrien	}
2246186690Sobrien	if (*el && !isspace((unsigned char)*el)) {
2247186690Sobrien		file_magwarn(ms, "Bad factor `%s'", l);
2248186690Sobrien		goto out;
2249186690Sobrien	}
2250186690Sobrien	m->factor = (uint8_t)factor;
2251186690Sobrien	if (m->factor == 0 && m->factor_op == FILE_FACTOR_OP_DIV) {
2252186690Sobrien		file_magwarn(ms, "Cannot have factor op `%c' and factor %u",
2253186690Sobrien		    m->factor_op, m->factor);
2254186690Sobrien		goto out;
2255186690Sobrien	}
2256186690Sobrien	return 0;
2257186690Sobrienout:
2258186690Sobrien	m->factor_op = FILE_FACTOR_OP_NONE;
2259186690Sobrien	m->factor = 0;
2260186690Sobrien	return -1;
2261186690Sobrien}
2262186690Sobrien
2263191736Sobrienprivate int
2264276415Sdelphijgoodchar(unsigned char x, const char *extra)
2265276415Sdelphij{
2266276415Sdelphij	return (isascii(x) && isalnum(x)) || strchr(extra, x);
2267276415Sdelphij}
2268276415Sdelphij
2269276415Sdelphijprivate int
2270267843Sdelphijparse_extra(struct magic_set *ms, struct magic_entry *me, const char *line,
2271276415Sdelphij    off_t off, size_t len, const char *name, const char *extra, int nt)
2272191736Sobrien{
2273191736Sobrien	size_t i;
2274191736Sobrien	const char *l = line;
2275191736Sobrien	struct magic *m = &me->mp[me->cont_count == 0 ? 0 : me->cont_count - 1];
2276284778Sdelphij	char *buf = CAST(char *, CAST(void *, m)) + off;
2277191736Sobrien
2278267843Sdelphij	if (buf[0] != '\0') {
2279267843Sdelphij		len = nt ? strlen(buf) : len;
2280267843Sdelphij		file_magwarn(ms, "Current entry already has a %s type "
2281267843Sdelphij		    "`%.*s', new type `%s'", name, (int)len, buf, l);
2282191736Sobrien		return -1;
2283191736Sobrien	}
2284191736Sobrien
2285267843Sdelphij	if (*m->desc == '\0') {
2286267843Sdelphij		file_magwarn(ms, "Current entry does not yet have a "
2287267843Sdelphij		    "description for adding a %s type", name);
2288267843Sdelphij		return -1;
2289267843Sdelphij	}
2290267843Sdelphij
2291191736Sobrien	EATAB;
2292276415Sdelphij	for (i = 0; *l && i < len && goodchar(*l, extra); buf[i++] = *l++)
2293191736Sobrien		continue;
2294267843Sdelphij
2295267843Sdelphij	if (i == len && *l) {
2296267843Sdelphij		if (nt)
2297267843Sdelphij			buf[len - 1] = '\0';
2298191736Sobrien		if (ms->flags & MAGIC_CHECK)
2299267843Sdelphij			file_magwarn(ms, "%s type `%s' truncated %"
2300267843Sdelphij			    SIZE_T_FORMAT "u", name, line, i);
2301267843Sdelphij	} else {
2302276415Sdelphij		if (!isspace((unsigned char)*l) && !goodchar(*l, extra))
2303276415Sdelphij			file_magwarn(ms, "%s type `%s' has bad char '%c'",
2304276415Sdelphij			    name, line, *l);
2305267843Sdelphij		if (nt)
2306267843Sdelphij			buf[i] = '\0';
2307191736Sobrien	}
2308191736Sobrien
2309191736Sobrien	if (i > 0)
2310191736Sobrien		return 0;
2311276415Sdelphij
2312276415Sdelphij	file_magerror(ms, "Bad magic entry '%s'", line);
2313276415Sdelphij	return -1;
2314191736Sobrien}
2315191736Sobrien
2316191736Sobrien/*
2317267843Sdelphij * Parse an Apple CREATOR/TYPE annotation from magic file and put it into
2318267843Sdelphij * magic[index - 1]
2319267843Sdelphij */
2320267843Sdelphijprivate int
2321267843Sdelphijparse_apple(struct magic_set *ms, struct magic_entry *me, const char *line)
2322267843Sdelphij{
2323267843Sdelphij	struct magic *m = &me->mp[0];
2324267843Sdelphij
2325284778Sdelphij	return parse_extra(ms, me, line,
2326284778Sdelphij	    CAST(off_t, offsetof(struct magic, apple)),
2327330569Sgordon	    sizeof(m->apple), "APPLE", "!+-./?", 0);
2328267843Sdelphij}
2329267843Sdelphij
2330267843Sdelphij/*
2331284778Sdelphij * Parse a comma-separated list of extensions
2332284778Sdelphij */
2333284778Sdelphijprivate int
2334284778Sdelphijparse_ext(struct magic_set *ms, struct magic_entry *me, const char *line)
2335284778Sdelphij{
2336284778Sdelphij	struct magic *m = &me->mp[0];
2337284778Sdelphij
2338284778Sdelphij	return parse_extra(ms, me, line,
2339284778Sdelphij	    CAST(off_t, offsetof(struct magic, ext)),
2340330569Sgordon	    sizeof(m->ext), "EXTENSION", ",!+-/@", 0);
2341284778Sdelphij}
2342284778Sdelphij
2343284778Sdelphij/*
2344186690Sobrien * parse a MIME annotation line from magic file, put into magic[index - 1]
2345186690Sobrien * if valid
2346186690Sobrien */
2347186690Sobrienprivate int
2348186690Sobrienparse_mime(struct magic_set *ms, struct magic_entry *me, const char *line)
2349186690Sobrien{
2350267843Sdelphij	struct magic *m = &me->mp[0];
2351186690Sobrien
2352284778Sdelphij	return parse_extra(ms, me, line,
2353284778Sdelphij	    CAST(off_t, offsetof(struct magic, mimetype)),
2354276415Sdelphij	    sizeof(m->mimetype), "MIME", "+-/.", 1);
2355186690Sobrien}
2356186690Sobrien
2357186690Sobrienprivate int
2358330569Sgordoncheck_format_type(const char *ptr, int type, const char **estr)
2359169942Sobrien{
2360267843Sdelphij	int quad = 0, h;
2361330569Sgordon	size_t len, cnt;
2362169942Sobrien	if (*ptr == '\0') {
2363169942Sobrien		/* Missing format string; bad */
2364330569Sgordon		*estr = "missing format spec";
2365169942Sobrien		return -1;
2366169942Sobrien	}
2367169942Sobrien
2368267843Sdelphij	switch (file_formats[type]) {
2369169942Sobrien	case FILE_FMT_QUAD:
2370169942Sobrien		quad = 1;
2371169942Sobrien		/*FALLTHROUGH*/
2372169942Sobrien	case FILE_FMT_NUM:
2373267843Sdelphij		if (quad == 0) {
2374267843Sdelphij			switch (type) {
2375267843Sdelphij			case FILE_BYTE:
2376267843Sdelphij				h = 2;
2377267843Sdelphij				break;
2378267843Sdelphij			case FILE_SHORT:
2379267843Sdelphij			case FILE_BESHORT:
2380267843Sdelphij			case FILE_LESHORT:
2381267843Sdelphij				h = 1;
2382267843Sdelphij				break;
2383267843Sdelphij			case FILE_LONG:
2384267843Sdelphij			case FILE_BELONG:
2385267843Sdelphij			case FILE_LELONG:
2386267843Sdelphij			case FILE_MELONG:
2387267843Sdelphij			case FILE_LEID3:
2388267843Sdelphij			case FILE_BEID3:
2389267843Sdelphij			case FILE_INDIRECT:
2390267843Sdelphij				h = 0;
2391267843Sdelphij				break;
2392267843Sdelphij			default:
2393267843Sdelphij				abort();
2394267843Sdelphij			}
2395267843Sdelphij		} else
2396267843Sdelphij			h = 0;
2397169942Sobrien		if (*ptr == '-')
2398169942Sobrien			ptr++;
2399169942Sobrien		if (*ptr == '.')
2400169942Sobrien			ptr++;
2401330569Sgordon		if (*ptr == '#')
2402330569Sgordon			ptr++;
2403330569Sgordon#define CHECKLEN() do { \
2404330569Sgordon	for (len = cnt = 0; isdigit((unsigned char)*ptr); ptr++, cnt++) \
2405330569Sgordon		len = len * 10 + (*ptr - '0'); \
2406330569Sgordon	if (cnt > 5 || len > 1024) \
2407330569Sgordon		goto toolong; \
2408330569Sgordon} while (/*CONSTCOND*/0)
2409330569Sgordon
2410330569Sgordon		CHECKLEN();
2411169942Sobrien		if (*ptr == '.')
2412169942Sobrien			ptr++;
2413330569Sgordon		CHECKLEN();
2414169942Sobrien		if (quad) {
2415169942Sobrien			if (*ptr++ != 'l')
2416330569Sgordon				goto invalid;
2417169942Sobrien			if (*ptr++ != 'l')
2418330569Sgordon				goto invalid;
2419169942Sobrien		}
2420169942Sobrien
2421169942Sobrien		switch (*ptr++) {
2422267843Sdelphij#ifdef STRICT_FORMAT 	/* "long" formats are int formats for us */
2423267843Sdelphij		/* so don't accept the 'l' modifier */
2424169942Sobrien		case 'l':
2425169942Sobrien			switch (*ptr++) {
2426169942Sobrien			case 'i':
2427169942Sobrien			case 'd':
2428169942Sobrien			case 'u':
2429267843Sdelphij			case 'o':
2430169942Sobrien			case 'x':
2431169942Sobrien			case 'X':
2432330569Sgordon				if (h == 0)
2433330569Sgordon					return 0;
2434330569Sgordon				/*FALLTHROUGH*/
2435169942Sobrien			default:
2436330569Sgordon				goto invalid;
2437169942Sobrien			}
2438169942Sobrien
2439267843Sdelphij		/*
2440267843Sdelphij		 * Don't accept h and hh modifiers. They make writing
2441267843Sdelphij		 * magic entries more complicated, for very little benefit
2442267843Sdelphij		 */
2443169942Sobrien		case 'h':
2444267843Sdelphij			if (h-- <= 0)
2445330569Sgordon				goto invalid;
2446169942Sobrien			switch (*ptr++) {
2447169942Sobrien			case 'h':
2448267843Sdelphij				if (h-- <= 0)
2449330569Sgordon					goto invalid;
2450169942Sobrien				switch (*ptr++) {
2451169942Sobrien				case 'i':
2452169942Sobrien				case 'd':
2453169942Sobrien				case 'u':
2454267843Sdelphij				case 'o':
2455169942Sobrien				case 'x':
2456169942Sobrien				case 'X':
2457169942Sobrien					return 0;
2458169942Sobrien				default:
2459330569Sgordon					goto invalid;
2460169942Sobrien				}
2461267843Sdelphij			case 'i':
2462169942Sobrien			case 'd':
2463267843Sdelphij			case 'u':
2464267843Sdelphij			case 'o':
2465267843Sdelphij			case 'x':
2466267843Sdelphij			case 'X':
2467330569Sgordon				if (h == 0)
2468330569Sgordon					return 0;
2469330569Sgordon				/*FALLTHROUGH*/
2470169942Sobrien			default:
2471330569Sgordon				goto invalid;
2472169942Sobrien			}
2473267843Sdelphij#endif
2474267843Sdelphij		case 'c':
2475330569Sgordon			if (h == 2)
2476330569Sgordon				return 0;
2477330569Sgordon			goto invalid;
2478169942Sobrien		case 'i':
2479169942Sobrien		case 'd':
2480169942Sobrien		case 'u':
2481267843Sdelphij		case 'o':
2482169942Sobrien		case 'x':
2483169942Sobrien		case 'X':
2484267843Sdelphij#ifdef STRICT_FORMAT
2485330569Sgordon			if (h == 0)
2486330569Sgordon				return 0;
2487330569Sgordon			/*FALLTHROUGH*/
2488267843Sdelphij#else
2489169942Sobrien			return 0;
2490267843Sdelphij#endif
2491169942Sobrien		default:
2492330569Sgordon			goto invalid;
2493169942Sobrien		}
2494169942Sobrien
2495175296Sobrien	case FILE_FMT_FLOAT:
2496175296Sobrien	case FILE_FMT_DOUBLE:
2497175296Sobrien		if (*ptr == '-')
2498175296Sobrien			ptr++;
2499175296Sobrien		if (*ptr == '.')
2500175296Sobrien			ptr++;
2501330569Sgordon		CHECKLEN();
2502175296Sobrien		if (*ptr == '.')
2503175296Sobrien			ptr++;
2504330569Sgordon		CHECKLEN();
2505175296Sobrien		switch (*ptr++) {
2506175296Sobrien		case 'e':
2507175296Sobrien		case 'E':
2508175296Sobrien		case 'f':
2509175296Sobrien		case 'F':
2510175296Sobrien		case 'g':
2511175296Sobrien		case 'G':
2512175296Sobrien			return 0;
2513175296Sobrien
2514175296Sobrien		default:
2515330569Sgordon			goto invalid;
2516175296Sobrien		}
2517175296Sobrien
2518175296Sobrien
2519169942Sobrien	case FILE_FMT_STR:
2520169942Sobrien		if (*ptr == '-')
2521169942Sobrien			ptr++;
2522169942Sobrien		while (isdigit((unsigned char )*ptr))
2523169942Sobrien			ptr++;
2524169942Sobrien		if (*ptr == '.') {
2525169942Sobrien			ptr++;
2526169942Sobrien			while (isdigit((unsigned char )*ptr))
2527169942Sobrien				ptr++;
2528169942Sobrien		}
2529169942Sobrien
2530169942Sobrien		switch (*ptr++) {
2531169942Sobrien		case 's':
2532169942Sobrien			return 0;
2533169942Sobrien		default:
2534330569Sgordon			goto invalid;
2535169942Sobrien		}
2536169942Sobrien
2537169942Sobrien	default:
2538169942Sobrien		/* internal error */
2539169942Sobrien		abort();
2540169942Sobrien	}
2541330569Sgordoninvalid:
2542330569Sgordon	*estr = "not valid";
2543330569Sgordontoolong:
2544330569Sgordon	*estr = "too long";
2545169942Sobrien	return -1;
2546169942Sobrien}
2547169942Sobrien
2548133359Sobrien/*
2549133359Sobrien * Check that the optional printf format in description matches
2550133359Sobrien * the type of the magic.
2551133359Sobrien */
2552133359Sobrienprivate int
2553139368Sobriencheck_format(struct magic_set *ms, struct magic *m)
2554133359Sobrien{
2555133359Sobrien	char *ptr;
2556330569Sgordon	const char *estr;
2557133359Sobrien
2558133359Sobrien	for (ptr = m->desc; *ptr; ptr++)
2559133359Sobrien		if (*ptr == '%')
2560133359Sobrien			break;
2561133359Sobrien	if (*ptr == '\0') {
2562133359Sobrien		/* No format string; ok */
2563133359Sobrien		return 1;
2564133359Sobrien	}
2565169942Sobrien
2566169942Sobrien	assert(file_nformats == file_nnames);
2567169942Sobrien
2568169942Sobrien	if (m->type >= file_nformats) {
2569186690Sobrien		file_magwarn(ms, "Internal error inconsistency between "
2570169942Sobrien		    "m->type and format strings");
2571169942Sobrien		return -1;
2572133359Sobrien	}
2573169942Sobrien	if (file_formats[m->type] == FILE_FMT_NONE) {
2574186690Sobrien		file_magwarn(ms, "No format string for `%s' with description "
2575169942Sobrien		    "`%s'", m->desc, file_names[m->type]);
2576169942Sobrien		return -1;
2577133359Sobrien	}
2578169942Sobrien
2579169942Sobrien	ptr++;
2580330569Sgordon	if (check_format_type(ptr, m->type, &estr) == -1) {
2581169942Sobrien		/*
2582169942Sobrien		 * TODO: this error message is unhelpful if the format
2583169942Sobrien		 * string is not one character long
2584169942Sobrien		 */
2585330569Sgordon		file_magwarn(ms, "Printf format is %s for type "
2586330569Sgordon		    "`%s' in description `%s'", estr,
2587169942Sobrien		    file_names[m->type], m->desc);
2588169942Sobrien		return -1;
2589169942Sobrien	}
2590169942Sobrien
2591133359Sobrien	for (; *ptr; ptr++) {
2592169942Sobrien		if (*ptr == '%') {
2593186690Sobrien			file_magwarn(ms,
2594169942Sobrien			    "Too many format strings (should have at most one) "
2595169942Sobrien			    "for `%s' with description `%s'",
2596169942Sobrien			    file_names[m->type], m->desc);
2597169942Sobrien			return -1;
2598133359Sobrien		}
2599133359Sobrien	}
2600169942Sobrien	return 0;
2601133359Sobrien}
2602133359Sobrien
260368349Sobrien/*
260468349Sobrien * Read a numeric value from a pointer, into the value union of a magic
260568349Sobrien * pointer, according to the magic type.  Update the string pointer to point
260668349Sobrien * just after the number read.  Return 0 for success, non-zero for failure.
260768349Sobrien */
2608133359Sobrienprivate int
2609169962Sobriengetvalue(struct magic_set *ms, struct magic *m, const char **p, int action)
261068349Sobrien{
2611133359Sobrien	switch (m->type) {
2612139368Sobrien	case FILE_BESTRING16:
2613139368Sobrien	case FILE_LESTRING16:
2614133359Sobrien	case FILE_STRING:
2615133359Sobrien	case FILE_PSTRING:
2616133359Sobrien	case FILE_REGEX:
2617159764Sobrien	case FILE_SEARCH:
2618267843Sdelphij	case FILE_NAME:
2619267843Sdelphij	case FILE_USE:
2620330569Sgordon	case FILE_DER:
2621192348Sdelphij		*p = getstr(ms, m, *p, action == FILE_COMPILE);
2622133359Sobrien		if (*p == NULL) {
2623133359Sobrien			if (ms->flags & MAGIC_CHECK)
2624139368Sobrien				file_magwarn(ms, "cannot get string from `%s'",
2625133359Sobrien				    m->value.s);
2626133359Sobrien			return -1;
2627133359Sobrien		}
2628267843Sdelphij		if (m->type == FILE_REGEX) {
2629267843Sdelphij			file_regex_t rx;
2630267843Sdelphij			int rc = file_regcomp(&rx, m->value.s, REG_EXTENDED);
2631267843Sdelphij			if (rc) {
2632267843Sdelphij				if (ms->flags & MAGIC_CHECK)
2633267843Sdelphij					file_regerror(&rx, rc, ms);
2634267843Sdelphij			}
2635267843Sdelphij			file_regfree(&rx);
2636267843Sdelphij			return rc ? -1 : 0;
2637267843Sdelphij		}
2638133359Sobrien		return 0;
2639175296Sobrien	case FILE_FLOAT:
2640175296Sobrien	case FILE_BEFLOAT:
2641175296Sobrien	case FILE_LEFLOAT:
2642175296Sobrien		if (m->reln != 'x') {
2643175296Sobrien			char *ep;
2644290152Sdelphij			errno = 0;
2645175296Sobrien#ifdef HAVE_STRTOF
2646175296Sobrien			m->value.f = strtof(*p, &ep);
2647175296Sobrien#else
2648175296Sobrien			m->value.f = (float)strtod(*p, &ep);
2649175296Sobrien#endif
2650290152Sdelphij			if (errno == 0)
2651290152Sdelphij				*p = ep;
2652175296Sobrien		}
2653175296Sobrien		return 0;
2654175296Sobrien	case FILE_DOUBLE:
2655175296Sobrien	case FILE_BEDOUBLE:
2656175296Sobrien	case FILE_LEDOUBLE:
2657175296Sobrien		if (m->reln != 'x') {
2658175296Sobrien			char *ep;
2659290152Sdelphij			errno = 0;
2660175296Sobrien			m->value.d = strtod(*p, &ep);
2661290152Sdelphij			if (errno == 0)
2662290152Sdelphij				*p = ep;
2663175296Sobrien		}
2664175296Sobrien		return 0;
2665133359Sobrien	default:
266668349Sobrien		if (m->reln != 'x') {
2667159764Sobrien			char *ep;
2668330569Sgordon			uint64_t ull;
2669290152Sdelphij			errno = 0;
2670330569Sgordon			ull = (uint64_t)strtoull(*p, &ep, 0);
2671330569Sgordon			m->value.q = file_signextend(ms, m, ull);
2672330569Sgordon			if (*p == ep) {
2673330569Sgordon				file_magwarn(ms, "Unparseable number `%s'", *p);
2674330569Sgordon			} else {
2675330569Sgordon				size_t ts = typesize(m->type);
2676330569Sgordon				uint64_t x;
2677330569Sgordon				const char *q;
2678330569Sgordon
2679330569Sgordon				if (ts == (size_t)~0) {
2680330569Sgordon					file_magwarn(ms, "Expected numeric type got `%s'",
2681330569Sgordon					    type_tbl[m->type].name);
2682330569Sgordon				}
2683330569Sgordon				for (q = *p; isspace((unsigned char)*q); q++)
2684330569Sgordon					continue;
2685330569Sgordon				if (*q == '-')
2686330569Sgordon					ull = -(int64_t)ull;
2687330569Sgordon				switch (ts) {
2688330569Sgordon				case 1:
2689330569Sgordon					x = ull & ~0xffULL;
2690330569Sgordon					break;
2691330569Sgordon				case 2:
2692330569Sgordon					x = ull & ~0xffffULL;
2693330569Sgordon					break;
2694330569Sgordon				case 4:
2695330569Sgordon					x = ull & ~0xffffffffULL;
2696330569Sgordon					break;
2697330569Sgordon				case 8:
2698330569Sgordon					x = 0;
2699330569Sgordon					break;
2700330569Sgordon				default:
2701330569Sgordon					abort();
2702330569Sgordon				}
2703330569Sgordon				if (x) {
2704330569Sgordon					file_magwarn(ms, "Overflow for numeric type `%s' value %#" PRIx64,
2705330569Sgordon					    type_tbl[m->type].name, ull);
2706330569Sgordon				}
2707330569Sgordon			}
2708290152Sdelphij			if (errno == 0) {
2709290152Sdelphij				*p = ep;
2710290152Sdelphij				eatsize(p);
2711290152Sdelphij			}
271268349Sobrien		}
2713133359Sobrien		return 0;
2714133359Sobrien	}
271568349Sobrien}
271668349Sobrien
271768349Sobrien/*
271868349Sobrien * Convert a string containing C character escapes.  Stop at an unescaped
271968349Sobrien * space or tab.
2720192348Sdelphij * Copy the converted version to "m->value.s", and the length in m->vallen.
2721192348Sdelphij * Return updated scan pointer as function result. Warn if set.
272268349Sobrien */
2723159764Sobrienprivate const char *
2724192348Sdelphijgetstr(struct magic_set *ms, struct magic *m, const char *s, int warn)
272568349Sobrien{
2726159764Sobrien	const char *origs = s;
2727192348Sdelphij	char	*p = m->value.s;
2728192348Sdelphij	size_t  plen = sizeof(m->value.s);
2729159764Sobrien	char 	*origp = p;
273068349Sobrien	char	*pmax = p + plen - 1;
273168349Sobrien	int	c;
273268349Sobrien	int	val;
273368349Sobrien
273468349Sobrien	while ((c = *s++) != '\0') {
273568349Sobrien		if (isspace((unsigned char) c))
273668349Sobrien			break;
273768349Sobrien		if (p >= pmax) {
2738133359Sobrien			file_error(ms, 0, "string too long: `%s'", origs);
2739133359Sobrien			return NULL;
274068349Sobrien		}
2741169962Sobrien		if (c == '\\') {
274268349Sobrien			switch(c = *s++) {
274368349Sobrien
274468349Sobrien			case '\0':
2745192348Sdelphij				if (warn)
2746169962Sobrien					file_magwarn(ms, "incomplete escape");
2747290152Sdelphij				s--;
274868349Sobrien				goto out;
274968349Sobrien
2750169962Sobrien			case '\t':
2751192348Sdelphij				if (warn) {
2752169962Sobrien					file_magwarn(ms,
2753169962Sobrien					    "escaped tab found, use \\t instead");
2754192348Sdelphij					warn = 0;	/* already did */
2755169962Sobrien				}
2756169962Sobrien				/*FALLTHROUGH*/
275768349Sobrien			default:
2758192348Sdelphij				if (warn) {
2759192348Sdelphij					if (isprint((unsigned char)c)) {
2760192348Sdelphij						/* Allow escaping of
2761192348Sdelphij						 * ``relations'' */
2762226048Sobrien						if (strchr("<>&^=!", c) == NULL
2763226048Sobrien						    && (m->type != FILE_REGEX ||
2764226048Sobrien						    strchr("[]().*?^$|{}", c)
2765226048Sobrien						    == NULL)) {
2766192348Sdelphij							file_magwarn(ms, "no "
2767192348Sdelphij							    "need to escape "
2768192348Sdelphij							    "`%c'", c);
2769192348Sdelphij						}
2770192348Sdelphij					} else {
2771192348Sdelphij						file_magwarn(ms,
2772192348Sdelphij						    "unknown escape sequence: "
2773192348Sdelphij						    "\\%03o", c);
2774192348Sdelphij					}
2775169962Sobrien				}
2776169962Sobrien				/*FALLTHROUGH*/
2777169962Sobrien			/* space, perhaps force people to use \040? */
2778169962Sobrien			case ' ':
2779169962Sobrien#if 0
2780169962Sobrien			/*
2781169962Sobrien			 * Other things people escape, but shouldn't need to,
2782169962Sobrien			 * so we disallow them
2783169962Sobrien			 */
2784169962Sobrien			case '\'':
2785169962Sobrien			case '"':
2786169962Sobrien			case '?':
2787169962Sobrien#endif
2788169962Sobrien			/* Relations */
2789169962Sobrien			case '>':
2790169962Sobrien			case '<':
2791169962Sobrien			case '&':
2792169962Sobrien			case '^':
2793169962Sobrien			case '=':
2794169962Sobrien			case '!':
2795169962Sobrien			/* and baskslash itself */
2796169962Sobrien			case '\\':
279768349Sobrien				*p++ = (char) c;
279868349Sobrien				break;
279968349Sobrien
2800169962Sobrien			case 'a':
2801169962Sobrien				*p++ = '\a';
2802169962Sobrien				break;
2803169962Sobrien
2804169962Sobrien			case 'b':
2805169962Sobrien				*p++ = '\b';
2806169962Sobrien				break;
2807169962Sobrien
2808169962Sobrien			case 'f':
2809169962Sobrien				*p++ = '\f';
2810169962Sobrien				break;
2811169962Sobrien
281268349Sobrien			case 'n':
281368349Sobrien				*p++ = '\n';
281468349Sobrien				break;
281568349Sobrien
281668349Sobrien			case 'r':
281768349Sobrien				*p++ = '\r';
281868349Sobrien				break;
281968349Sobrien
282068349Sobrien			case 't':
282168349Sobrien				*p++ = '\t';
282268349Sobrien				break;
282368349Sobrien
282468349Sobrien			case 'v':
282568349Sobrien				*p++ = '\v';
282668349Sobrien				break;
282768349Sobrien
282868349Sobrien			/* \ and up to 3 octal digits */
282968349Sobrien			case '0':
283068349Sobrien			case '1':
283168349Sobrien			case '2':
283268349Sobrien			case '3':
283368349Sobrien			case '4':
283468349Sobrien			case '5':
283568349Sobrien			case '6':
283668349Sobrien			case '7':
283768349Sobrien				val = c - '0';
283868349Sobrien				c = *s++;  /* try for 2 */
2839169962Sobrien				if (c >= '0' && c <= '7') {
2840169962Sobrien					val = (val << 3) | (c - '0');
284168349Sobrien					c = *s++;  /* try for 3 */
2842169962Sobrien					if (c >= '0' && c <= '7')
2843169962Sobrien						val = (val << 3) | (c-'0');
284468349Sobrien					else
284568349Sobrien						--s;
284668349Sobrien				}
284768349Sobrien				else
284868349Sobrien					--s;
284968349Sobrien				*p++ = (char)val;
285068349Sobrien				break;
285168349Sobrien
285268349Sobrien			/* \x and up to 2 hex digits */
285368349Sobrien			case 'x':
285468349Sobrien				val = 'x';	/* Default if no digits */
285568349Sobrien				c = hextoint(*s++);	/* Get next char */
285668349Sobrien				if (c >= 0) {
285768349Sobrien					val = c;
285868349Sobrien					c = hextoint(*s++);
285968349Sobrien					if (c >= 0)
286068349Sobrien						val = (val << 4) + c;
286168349Sobrien					else
286268349Sobrien						--s;
286368349Sobrien				} else
286468349Sobrien					--s;
286568349Sobrien				*p++ = (char)val;
286668349Sobrien				break;
286768349Sobrien			}
286868349Sobrien		} else
286968349Sobrien			*p++ = (char)c;
287068349Sobrien	}
2871290152Sdelphij	--s;
287268349Sobrienout:
287368349Sobrien	*p = '\0';
2874226048Sobrien	m->vallen = CAST(unsigned char, (p - origp));
2875192348Sdelphij	if (m->type == FILE_PSTRING)
2876226048Sobrien		m->vallen += (unsigned char)file_pstring_length_size(m);
287768349Sobrien	return s;
287868349Sobrien}
287968349Sobrien
288068349Sobrien
288168349Sobrien/* Single hex char to int; -1 if not a hex char. */
2882133359Sobrienprivate int
2883103373Sobrienhextoint(int c)
288468349Sobrien{
288568349Sobrien	if (!isascii((unsigned char) c))
288668349Sobrien		return -1;
288768349Sobrien	if (isdigit((unsigned char) c))
288868349Sobrien		return c - '0';
2889169962Sobrien	if ((c >= 'a') && (c <= 'f'))
289068349Sobrien		return c + 10 - 'a';
2891169962Sobrien	if (( c>= 'A') && (c <= 'F'))
289268349Sobrien		return c + 10 - 'A';
289368349Sobrien	return -1;
289468349Sobrien}
289568349Sobrien
289668349Sobrien
289768349Sobrien/*
289868349Sobrien * Print a string containing C character escapes.
289968349Sobrien */
2900133359Sobrienprotected void
2901133359Sobrienfile_showstr(FILE *fp, const char *s, size_t len)
290268349Sobrien{
290368349Sobrien	char	c;
290468349Sobrien
290568349Sobrien	for (;;) {
2906133359Sobrien		if (len == ~0U) {
2907226048Sobrien			c = *s++;
290868349Sobrien			if (c == '\0')
290968349Sobrien				break;
291068349Sobrien		}
291168349Sobrien		else  {
291268349Sobrien			if (len-- == 0)
291368349Sobrien				break;
2914226048Sobrien			c = *s++;
291568349Sobrien		}
2916169962Sobrien		if (c >= 040 && c <= 0176)	/* TODO isprint && !iscntrl */
291768349Sobrien			(void) fputc(c, fp);
291868349Sobrien		else {
291968349Sobrien			(void) fputc('\\', fp);
292068349Sobrien			switch (c) {
2921169962Sobrien			case '\a':
2922169962Sobrien				(void) fputc('a', fp);
2923169962Sobrien				break;
2924169962Sobrien
2925169962Sobrien			case '\b':
2926169962Sobrien				(void) fputc('b', fp);
2927169962Sobrien				break;
2928169962Sobrien
2929169962Sobrien			case '\f':
2930169962Sobrien				(void) fputc('f', fp);
2931169962Sobrien				break;
2932169962Sobrien
293368349Sobrien			case '\n':
293468349Sobrien				(void) fputc('n', fp);
293568349Sobrien				break;
293668349Sobrien
293768349Sobrien			case '\r':
293868349Sobrien				(void) fputc('r', fp);
293968349Sobrien				break;
294068349Sobrien
294168349Sobrien			case '\t':
294268349Sobrien				(void) fputc('t', fp);
294368349Sobrien				break;
294468349Sobrien
294568349Sobrien			case '\v':
294668349Sobrien				(void) fputc('v', fp);
294768349Sobrien				break;
294868349Sobrien
294968349Sobrien			default:
295068349Sobrien				(void) fprintf(fp, "%.3o", c & 0377);
295168349Sobrien				break;
295268349Sobrien			}
295368349Sobrien		}
295468349Sobrien	}
295568349Sobrien}
295668349Sobrien
295768349Sobrien/*
295868349Sobrien * eatsize(): Eat the size spec from a number [eg. 10UL]
295968349Sobrien */
2960133359Sobrienprivate void
2961159764Sobrieneatsize(const char **p)
296268349Sobrien{
2963159764Sobrien	const char *l = *p;
296468349Sobrien
296568349Sobrien	if (LOWCASE(*l) == 'u')
296668349Sobrien		l++;
296768349Sobrien
296868349Sobrien	switch (LOWCASE(*l)) {
296968349Sobrien	case 'l':    /* long */
297068349Sobrien	case 's':    /* short */
297168349Sobrien	case 'h':    /* short */
297268349Sobrien	case 'b':    /* char/byte */
297368349Sobrien	case 'c':    /* char/byte */
297468349Sobrien		l++;
297568349Sobrien		/*FALLTHROUGH*/
297668349Sobrien	default:
297768349Sobrien		break;
297868349Sobrien	}
297968349Sobrien
298068349Sobrien	*p = l;
298168349Sobrien}
298274784Sobrien
298374784Sobrien/*
2984276415Sdelphij * handle a buffer containing a compiled file.
2985276415Sdelphij */
2986276415Sdelphijprivate struct magic_map *
2987276415Sdelphijapprentice_buf(struct magic_set *ms, struct magic *buf, size_t len)
2988276415Sdelphij{
2989276415Sdelphij	struct magic_map *map;
2990276415Sdelphij
2991276415Sdelphij	if ((map = CAST(struct magic_map *, calloc(1, sizeof(*map)))) == NULL) {
2992276415Sdelphij		file_oomem(ms, sizeof(*map));
2993276415Sdelphij		return NULL;
2994276415Sdelphij	}
2995276415Sdelphij	map->len = len;
2996276415Sdelphij	map->p = buf;
2997276415Sdelphij	map->type = MAP_TYPE_USER;
2998276415Sdelphij	if (check_buffer(ms, map, "buffer") != 0) {
2999276415Sdelphij		apprentice_unmap(map);
3000276415Sdelphij		return NULL;
3001276415Sdelphij	}
3002276415Sdelphij	return map;
3003276415Sdelphij}
3004276415Sdelphij
3005276415Sdelphij/*
3006103373Sobrien * handle a compiled file.
300774784Sobrien */
3008267843Sdelphij
3009267843Sdelphijprivate struct magic_map *
3010267843Sdelphijapprentice_map(struct magic_set *ms, const char *fn)
301174784Sobrien{
301274784Sobrien	int fd;
301374784Sobrien	struct stat st;
3014186690Sobrien	char *dbname = NULL;
3015267843Sdelphij	struct magic_map *map;
3016330569Sgordon	struct magic_map *rv = NULL;
301774784Sobrien
3018267843Sdelphij	fd = -1;
3019267843Sdelphij	if ((map = CAST(struct magic_map *, calloc(1, sizeof(*map)))) == NULL) {
3020267843Sdelphij		file_oomem(ms, sizeof(*map));
3021267843Sdelphij		goto error;
3022267843Sdelphij	}
3023330569Sgordon	map->type = MAP_TYPE_USER;	/* unspecified */
3024267843Sdelphij
3025191736Sobrien	dbname = mkdbname(ms, fn, 0);
302680588Sobrien	if (dbname == NULL)
3027267843Sdelphij		goto error;
302880588Sobrien
3029159764Sobrien	if ((fd = open(dbname, O_RDONLY|O_BINARY)) == -1)
3030267843Sdelphij		goto error;
303174784Sobrien
303274784Sobrien	if (fstat(fd, &st) == -1) {
3033133359Sobrien		file_error(ms, errno, "cannot stat `%s'", dbname);
3034267843Sdelphij		goto error;
303574784Sobrien	}
3036267843Sdelphij	if (st.st_size < 8 || st.st_size > MAXMAGIC_SIZE) {
3037267843Sdelphij		file_error(ms, 0, "file `%s' is too %s", dbname,
3038267843Sdelphij		    st.st_size < 8 ? "small" : "large");
3039267843Sdelphij		goto error;
3040133359Sobrien	}
304174784Sobrien
3042267843Sdelphij	map->len = (size_t)st.st_size;
304380588Sobrien#ifdef QUICK
3044330569Sgordon	map->type = MAP_TYPE_MMAP;
3045267843Sdelphij	if ((map->p = mmap(0, (size_t)st.st_size, PROT_READ|PROT_WRITE,
304674784Sobrien	    MAP_PRIVATE|MAP_FILE, fd, (off_t)0)) == MAP_FAILED) {
3047133359Sobrien		file_error(ms, errno, "cannot map `%s'", dbname);
3048267843Sdelphij		goto error;
304974784Sobrien	}
305080588Sobrien#else
3051330569Sgordon	map->type = MAP_TYPE_MALLOC;
3052267843Sdelphij	if ((map->p = CAST(void *, malloc(map->len))) == NULL) {
3053267843Sdelphij		file_oomem(ms, map->len);
3054267843Sdelphij		goto error;
305580588Sobrien	}
3056267843Sdelphij	if (read(fd, map->p, map->len) != (ssize_t)map->len) {
3057133359Sobrien		file_badread(ms);
3058267843Sdelphij		goto error;
305980588Sobrien	}
3060133359Sobrien#define RET	1
306180588Sobrien#endif
306274784Sobrien	(void)close(fd);
306375937Sobrien	fd = -1;
3064276415Sdelphij
3065330569Sgordon	if (check_buffer(ms, map, dbname) != 0) {
3066330569Sgordon		rv = (struct magic_map *)-1;
3067276415Sdelphij		goto error;
3068330569Sgordon	}
3069330569Sgordon#ifdef QUICK
3070330569Sgordon	if (mprotect(map->p, (size_t)st.st_size, PROT_READ) == -1) {
3071330569Sgordon		file_error(ms, errno, "cannot mprotect `%s'", dbname);
3072330569Sgordon		goto error;
3073330569Sgordon	}
3074330569Sgordon#endif
3075276415Sdelphij
3076276415Sdelphij	free(dbname);
3077276415Sdelphij	return map;
3078276415Sdelphij
3079276415Sdelphijerror:
3080276415Sdelphij	if (fd != -1)
3081276415Sdelphij		(void)close(fd);
3082276415Sdelphij	apprentice_unmap(map);
3083276415Sdelphij	free(dbname);
3084330569Sgordon	return rv;
3085276415Sdelphij}
3086276415Sdelphij
3087276415Sdelphijprivate int
3088276415Sdelphijcheck_buffer(struct magic_set *ms, struct magic_map *map, const char *dbname)
3089276415Sdelphij{
3090276415Sdelphij	uint32_t *ptr;
3091276415Sdelphij	uint32_t entries, nentries;
3092276415Sdelphij	uint32_t version;
3093276415Sdelphij	int i, needsbyteswap;
3094276415Sdelphij
3095267843Sdelphij	ptr = CAST(uint32_t *, map->p);
309674784Sobrien	if (*ptr != MAGICNO) {
309774784Sobrien		if (swap4(*ptr) != MAGICNO) {
3098186690Sobrien			file_error(ms, 0, "bad magic in `%s'", dbname);
3099276415Sdelphij			return -1;
310074784Sobrien		}
310174784Sobrien		needsbyteswap = 1;
310274784Sobrien	} else
310374784Sobrien		needsbyteswap = 0;
310474784Sobrien	if (needsbyteswap)
310574784Sobrien		version = swap4(ptr[1]);
310674784Sobrien	else
310774784Sobrien		version = ptr[1];
310874784Sobrien	if (version != VERSIONNO) {
3109226048Sobrien		file_error(ms, 0, "File %s supports only version %d magic "
3110226048Sobrien		    "files. `%s' is version %d", VERSION,
3111175296Sobrien		    VERSIONNO, dbname, version);
3112276415Sdelphij		return -1;
311374784Sobrien	}
3114276415Sdelphij	entries = (uint32_t)(map->len / sizeof(struct magic));
3115276415Sdelphij	if ((entries * sizeof(struct magic)) != map->len) {
3116276415Sdelphij		file_error(ms, 0, "Size of `%s' %" SIZE_T_FORMAT "u is not "
3117267843Sdelphij		    "a multiple of %" SIZE_T_FORMAT "u",
3118276415Sdelphij		    dbname, map->len, sizeof(struct magic));
3119276415Sdelphij		return -1;
3120267843Sdelphij	}
3121267843Sdelphij	map->magic[0] = CAST(struct magic *, map->p) + 1;
3122267843Sdelphij	nentries = 0;
3123267843Sdelphij	for (i = 0; i < MAGIC_SETS; i++) {
3124267843Sdelphij		if (needsbyteswap)
3125267843Sdelphij			map->nmagic[i] = swap4(ptr[i + 2]);
3126267843Sdelphij		else
3127267843Sdelphij			map->nmagic[i] = ptr[i + 2];
3128267843Sdelphij		if (i != MAGIC_SETS - 1)
3129267843Sdelphij			map->magic[i + 1] = map->magic[i] + map->nmagic[i];
3130267843Sdelphij		nentries += map->nmagic[i];
3131267843Sdelphij	}
3132267843Sdelphij	if (entries != nentries + 1) {
3133267843Sdelphij		file_error(ms, 0, "Inconsistent entries in `%s' %u != %u",
3134267843Sdelphij		    dbname, entries, nentries + 1);
3135276415Sdelphij		return -1;
3136267843Sdelphij	}
313774784Sobrien	if (needsbyteswap)
3138267843Sdelphij		for (i = 0; i < MAGIC_SETS; i++)
3139267843Sdelphij			byteswap(map->magic[i], map->nmagic[i]);
3140276415Sdelphij	return 0;
314174784Sobrien}
314274784Sobrien
314374784Sobrien/*
314474784Sobrien * handle an mmaped file.
314574784Sobrien */
3146133359Sobrienprivate int
3147267843Sdelphijapprentice_compile(struct magic_set *ms, struct magic_map *map, const char *fn)
314874784Sobrien{
3149267843Sdelphij	static const size_t nm = sizeof(*map->nmagic) * MAGIC_SETS;
3150267843Sdelphij	static const size_t m = sizeof(**map->magic);
3151234250Sobrien	int fd = -1;
3152267843Sdelphij	size_t len;
3153186690Sobrien	char *dbname;
3154186690Sobrien	int rv = -1;
3155267843Sdelphij	uint32_t i;
3156267843Sdelphij	union {
3157267843Sdelphij		struct magic m;
3158267843Sdelphij		uint32_t h[2 + MAGIC_SETS];
3159267843Sdelphij	} hdr;
316074784Sobrien
3161191736Sobrien	dbname = mkdbname(ms, fn, 1);
3162186690Sobrien
316380588Sobrien	if (dbname == NULL)
3164186690Sobrien		goto out;
316580588Sobrien
3166267843Sdelphij	if ((fd = open(dbname, O_WRONLY|O_CREAT|O_TRUNC|O_BINARY, 0644)) == -1)
3167267843Sdelphij	{
3168133359Sobrien		file_error(ms, errno, "cannot open `%s'", dbname);
3169186690Sobrien		goto out;
317074784Sobrien	}
3171267843Sdelphij	memset(&hdr, 0, sizeof(hdr));
3172267843Sdelphij	hdr.h[0] = MAGICNO;
3173267843Sdelphij	hdr.h[1] = VERSIONNO;
3174267843Sdelphij	memcpy(hdr.h + 2, map->nmagic, nm);
317574784Sobrien
3176267843Sdelphij	if (write(fd, &hdr, sizeof(hdr)) != (ssize_t)sizeof(hdr)) {
3177133359Sobrien		file_error(ms, errno, "error writing `%s'", dbname);
3178186690Sobrien		goto out;
317974784Sobrien	}
318074784Sobrien
3181267843Sdelphij	for (i = 0; i < MAGIC_SETS; i++) {
3182267843Sdelphij		len = m * map->nmagic[i];
3183267843Sdelphij		if (write(fd, map->magic[i], len) != (ssize_t)len) {
3184267843Sdelphij			file_error(ms, errno, "error writing `%s'", dbname);
3185267843Sdelphij			goto out;
3186267843Sdelphij		}
318774784Sobrien	}
318874784Sobrien
3189234250Sobrien	if (fd != -1)
3190234250Sobrien		(void)close(fd);
3191186690Sobrien	rv = 0;
3192186690Sobrienout:
3193330569Sgordon	apprentice_unmap(map);
3194186690Sobrien	free(dbname);
3195186690Sobrien	return rv;
319674784Sobrien}
319774784Sobrien
3198133359Sobrienprivate const char ext[] = ".mgc";
319974784Sobrien/*
320074784Sobrien * make a dbname
320174784Sobrien */
3202191736Sobrienprivate char *
3203191736Sobrienmkdbname(struct magic_set *ms, const char *fn, int strip)
320474784Sobrien{
3205191736Sobrien	const char *p, *q;
3206191736Sobrien	char *buf;
3207191736Sobrien
3208139368Sobrien	if (strip) {
3209139368Sobrien		if ((p = strrchr(fn, '/')) != NULL)
3210139368Sobrien			fn = ++p;
3211139368Sobrien	}
3212139368Sobrien
3213191736Sobrien	for (q = fn; *q; q++)
3214191736Sobrien		continue;
3215191736Sobrien	/* Look for .mgc */
3216191736Sobrien	for (p = ext + sizeof(ext) - 1; p >= ext && q >= fn; p--, q--)
3217191736Sobrien		if (*p != *q)
3218191736Sobrien			break;
3219186690Sobrien
3220191736Sobrien	/* Did not find .mgc, restore q */
3221191736Sobrien	if (p >= ext)
3222191736Sobrien		while (*q)
3223191736Sobrien			q++;
3224191736Sobrien
3225191736Sobrien	q++;
3226191736Sobrien	/* Compatibility with old code that looked in .mime */
3227191736Sobrien	if (ms->flags & MAGIC_MIME) {
3228267843Sdelphij		if (asprintf(&buf, "%.*s.mime%s", (int)(q - fn), fn, ext) < 0)
3229267843Sdelphij			return NULL;
3230191736Sobrien		if (access(buf, R_OK) != -1) {
3231191736Sobrien			ms->flags &= MAGIC_MIME_TYPE;
3232191736Sobrien			return buf;
3233191736Sobrien		}
3234191736Sobrien		free(buf);
3235186690Sobrien	}
3236267843Sdelphij	if (asprintf(&buf, "%.*s%s", (int)(q - fn), fn, ext) < 0)
3237267843Sdelphij		return NULL;
3238191736Sobrien
3239191736Sobrien	/* Compatibility with old code that looked in .mime */
3240330569Sgordon	if (strstr(fn, ".mime") != NULL)
3241191736Sobrien		ms->flags &= MAGIC_MIME_TYPE;
3242191736Sobrien	return buf;
324374784Sobrien}
324474784Sobrien
324574784Sobrien/*
324674784Sobrien * Byteswap an mmap'ed file if needed
324774784Sobrien */
3248133359Sobrienprivate void
3249103373Sobrienbyteswap(struct magic *magic, uint32_t nmagic)
325074784Sobrien{
3251103373Sobrien	uint32_t i;
325274784Sobrien	for (i = 0; i < nmagic; i++)
325374784Sobrien		bs1(&magic[i]);
325474784Sobrien}
325574784Sobrien
325674784Sobrien/*
325774784Sobrien * swap a short
325874784Sobrien */
3259133359Sobrienprivate uint16_t
3260103373Sobrienswap2(uint16_t sv)
326174784Sobrien{
3262103373Sobrien	uint16_t rv;
3263133359Sobrien	uint8_t *s = (uint8_t *)(void *)&sv;
3264133359Sobrien	uint8_t *d = (uint8_t *)(void *)&rv;
326574784Sobrien	d[0] = s[1];
326674784Sobrien	d[1] = s[0];
326774784Sobrien	return rv;
326874784Sobrien}
326974784Sobrien
327074784Sobrien/*
327174784Sobrien * swap an int
327274784Sobrien */
3273133359Sobrienprivate uint32_t
3274103373Sobrienswap4(uint32_t sv)
327574784Sobrien{
3276103373Sobrien	uint32_t rv;
3277133359Sobrien	uint8_t *s = (uint8_t *)(void *)&sv;
3278133359Sobrien	uint8_t *d = (uint8_t *)(void *)&rv;
327974784Sobrien	d[0] = s[3];
328074784Sobrien	d[1] = s[2];
328174784Sobrien	d[2] = s[1];
328274784Sobrien	d[3] = s[0];
328374784Sobrien	return rv;
328474784Sobrien}
328574784Sobrien
328674784Sobrien/*
3287169942Sobrien * swap a quad
3288169942Sobrien */
3289169942Sobrienprivate uint64_t
3290169942Sobrienswap8(uint64_t sv)
3291169942Sobrien{
3292186690Sobrien	uint64_t rv;
3293169942Sobrien	uint8_t *s = (uint8_t *)(void *)&sv;
3294169942Sobrien	uint8_t *d = (uint8_t *)(void *)&rv;
3295186690Sobrien#if 0
3296169942Sobrien	d[0] = s[3];
3297169942Sobrien	d[1] = s[2];
3298169942Sobrien	d[2] = s[1];
3299169942Sobrien	d[3] = s[0];
3300169942Sobrien	d[4] = s[7];
3301169942Sobrien	d[5] = s[6];
3302169942Sobrien	d[6] = s[5];
3303169942Sobrien	d[7] = s[4];
3304186690Sobrien#else
3305186690Sobrien	d[0] = s[7];
3306186690Sobrien	d[1] = s[6];
3307186690Sobrien	d[2] = s[5];
3308186690Sobrien	d[3] = s[4];
3309186690Sobrien	d[4] = s[3];
3310186690Sobrien	d[5] = s[2];
3311186690Sobrien	d[6] = s[1];
3312186690Sobrien	d[7] = s[0];
3313186690Sobrien#endif
3314169942Sobrien	return rv;
3315169942Sobrien}
3316169942Sobrien
3317169942Sobrien/*
331874784Sobrien * byteswap a single magic entry
331974784Sobrien */
3320133359Sobrienprivate void
3321133359Sobrienbs1(struct magic *m)
332274784Sobrien{
332374784Sobrien	m->cont_level = swap2(m->cont_level);
3324133359Sobrien	m->offset = swap4((uint32_t)m->offset);
3325133359Sobrien	m->in_offset = swap4((uint32_t)m->in_offset);
3326169962Sobrien	m->lineno = swap4((uint32_t)m->lineno);
3327169962Sobrien	if (IS_STRING(m->type)) {
3328186690Sobrien		m->str_range = swap4(m->str_range);
3329169962Sobrien		m->str_flags = swap4(m->str_flags);
3330169962Sobrien	}
3331169962Sobrien	else {
3332169942Sobrien		m->value.q = swap8(m->value.q);
3333169962Sobrien		m->num_mask = swap8(m->num_mask);
3334169962Sobrien	}
333574784Sobrien}
3336226048Sobrien
3337226048Sobrienprotected size_t
3338226048Sobrienfile_pstring_length_size(const struct magic *m)
3339226048Sobrien{
3340226048Sobrien	switch (m->str_flags & PSTRING_LEN) {
3341226048Sobrien	case PSTRING_1_LE:
3342226048Sobrien		return 1;
3343226048Sobrien	case PSTRING_2_LE:
3344226048Sobrien	case PSTRING_2_BE:
3345226048Sobrien		return 2;
3346226048Sobrien	case PSTRING_4_LE:
3347226048Sobrien	case PSTRING_4_BE:
3348226048Sobrien		return 4;
3349226048Sobrien	default:
3350226048Sobrien		abort();	/* Impossible */
3351226048Sobrien		return 1;
3352226048Sobrien	}
3353226048Sobrien}
3354226048Sobrienprotected size_t
3355290152Sdelphijfile_pstring_get_length(const struct magic *m, const char *ss)
3356226048Sobrien{
3357226048Sobrien	size_t len = 0;
3358290152Sdelphij	const unsigned char *s = (const unsigned char *)ss;
3359330569Sgordon	unsigned int s3, s2, s1, s0;
3360226048Sobrien
3361226048Sobrien	switch (m->str_flags & PSTRING_LEN) {
3362226048Sobrien	case PSTRING_1_LE:
3363226048Sobrien		len = *s;
3364226048Sobrien		break;
3365226048Sobrien	case PSTRING_2_LE:
3366330569Sgordon		s0 = s[0];
3367330569Sgordon		s1 = s[1];
3368330569Sgordon		len = (s1 << 8) | s0;
3369226048Sobrien		break;
3370226048Sobrien	case PSTRING_2_BE:
3371330569Sgordon		s0 = s[0];
3372330569Sgordon		s1 = s[1];
3373330569Sgordon		len = (s0 << 8) | s1;
3374226048Sobrien		break;
3375226048Sobrien	case PSTRING_4_LE:
3376330569Sgordon		s0 = s[0];
3377330569Sgordon		s1 = s[1];
3378330569Sgordon		s2 = s[2];
3379330569Sgordon		s3 = s[3];
3380330569Sgordon		len = (s3 << 24) | (s2 << 16) | (s1 << 8) | s0;
3381226048Sobrien		break;
3382226048Sobrien	case PSTRING_4_BE:
3383330569Sgordon		s0 = s[0];
3384330569Sgordon		s1 = s[1];
3385330569Sgordon		s2 = s[2];
3386330569Sgordon		s3 = s[3];
3387330569Sgordon		len = (s0 << 24) | (s1 << 16) | (s2 << 8) | s3;
3388226048Sobrien		break;
3389226048Sobrien	default:
3390226048Sobrien		abort();	/* Impossible */
3391226048Sobrien	}
3392226048Sobrien
3393226048Sobrien	if (m->str_flags & PSTRING_LENGTH_INCLUDES_ITSELF)
3394226048Sobrien		len -= file_pstring_length_size(m);
3395226048Sobrien
3396226048Sobrien	return len;
3397226048Sobrien}
3398267843Sdelphij
3399267843Sdelphijprotected int
3400267843Sdelphijfile_magicfind(struct magic_set *ms, const char *name, struct mlist *v)
3401267843Sdelphij{
3402267843Sdelphij	uint32_t i, j;
3403267843Sdelphij	struct mlist *mlist, *ml;
3404267843Sdelphij
3405267843Sdelphij	mlist = ms->mlist[1];
3406267843Sdelphij
3407267843Sdelphij	for (ml = mlist->next; ml != mlist; ml = ml->next) {
3408267843Sdelphij		struct magic *ma = ml->magic;
3409267843Sdelphij		uint32_t nma = ml->nmagic;
3410267843Sdelphij		for (i = 0; i < nma; i++) {
3411267843Sdelphij			if (ma[i].type != FILE_NAME)
3412267843Sdelphij				continue;
3413267843Sdelphij			if (strcmp(ma[i].value.s, name) == 0) {
3414267843Sdelphij				v->magic = &ma[i];
3415267843Sdelphij				for (j = i + 1; j < nma; j++)
3416267843Sdelphij				    if (ma[j].cont_level == 0)
3417267843Sdelphij					    break;
3418267843Sdelphij				v->nmagic = j - i;
3419267843Sdelphij				return 0;
3420267843Sdelphij			}
3421267843Sdelphij		}
3422267843Sdelphij	}
3423267843Sdelphij	return -1;
3424267843Sdelphij}
3425