apprentice.c revision 175296
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"
33133359Sobrien#include "magic.h"
34175296Sobrien#include "patchlevel.h"
3568349Sobrien#include <stdlib.h>
3684685Sobrien#ifdef HAVE_UNISTD_H
3784685Sobrien#include <unistd.h>
3884685Sobrien#endif
3968349Sobrien#include <string.h>
40169942Sobrien#include <assert.h>
4168349Sobrien#include <ctype.h>
42103373Sobrien#include <fcntl.h>
43133359Sobrien#include <sys/stat.h>
44133359Sobrien#include <sys/param.h>
4574784Sobrien#ifdef QUICK
4674784Sobrien#include <sys/mman.h>
4774784Sobrien#endif
4868349Sobrien
4968349Sobrien#ifndef	lint
50175296SobrienFILE_RCSID("@(#)$File: apprentice.c,v 1.109 2007/12/27 20:52:36 christos Exp $")
5168349Sobrien#endif	/* lint */
5268349Sobrien
5368349Sobrien#define	EATAB {while (isascii((unsigned char) *l) && \
5468349Sobrien		      isspace((unsigned char) *l))  ++l;}
5568349Sobrien#define LOWCASE(l) (isupper((unsigned char) (l)) ? \
5668349Sobrien			tolower((unsigned char) (l)) : (l))
5775937Sobrien/*
5875937Sobrien * Work around a bug in headers on Digital Unix.
5975937Sobrien * At least confirmed for: OSF1 V4.0 878
6075937Sobrien */
6175937Sobrien#if defined(__osf__) && defined(__DECC)
6275937Sobrien#ifdef MAP_FAILED
6375937Sobrien#undef MAP_FAILED
6475937Sobrien#endif
6575937Sobrien#endif
6668349Sobrien
6775937Sobrien#ifndef MAP_FAILED
6875937Sobrien#define MAP_FAILED (void *) -1
6975937Sobrien#endif
7068349Sobrien
7175937Sobrien#ifndef MAP_FILE
7275937Sobrien#define MAP_FILE 0
7375937Sobrien#endif
7475937Sobrien
75133359Sobrien#ifndef MAXPATHLEN
76133359Sobrien#define MAXPATHLEN	1024
7768349Sobrien#endif
7868349Sobrien
79159764Sobrienstruct magic_entry {
80159764Sobrien	struct magic *mp;
81159764Sobrien	uint32_t cont_count;
82159764Sobrien	uint32_t max_count;
83159764Sobrien};
84159764Sobrien
85169962Sobrienint file_formats[FILE_NAMES_SIZE];
86169962Sobrienconst size_t file_nformats = FILE_NAMES_SIZE;
87169962Sobrienconst char *file_names[FILE_NAMES_SIZE];
88169962Sobrienconst size_t file_nnames = FILE_NAMES_SIZE;
89169942Sobrien
90169962Sobrienprivate int getvalue(struct magic_set *ms, struct magic *, const char **, int);
91133359Sobrienprivate int hextoint(int);
92159764Sobrienprivate const char *getstr(struct magic_set *, const char *, char *, int,
93169962Sobrien    int *, int);
94159764Sobrienprivate int parse(struct magic_set *, struct magic_entry **, uint32_t *,
95169942Sobrien    const char *, size_t, int);
96159764Sobrienprivate void eatsize(const char **);
97133359Sobrienprivate int apprentice_1(struct magic_set *, const char *, int, struct mlist *);
98159764Sobrienprivate size_t apprentice_magic_strength(const struct magic *);
99159764Sobrienprivate int apprentice_sort(const void *, const void *);
100133359Sobrienprivate int apprentice_file(struct magic_set *, struct magic **, uint32_t *,
101133359Sobrien    const char *, int);
102133359Sobrienprivate void byteswap(struct magic *, uint32_t);
103133359Sobrienprivate void bs1(struct magic *);
104133359Sobrienprivate uint16_t swap2(uint16_t);
105133359Sobrienprivate uint32_t swap4(uint32_t);
106169942Sobrienprivate uint64_t swap8(uint64_t);
107139368Sobrienprivate char *mkdbname(const char *, char *, size_t, int);
108133359Sobrienprivate int apprentice_map(struct magic_set *, struct magic **, uint32_t *,
109133359Sobrien    const char *);
110133359Sobrienprivate int apprentice_compile(struct magic_set *, struct magic **, uint32_t *,
111133359Sobrien    const char *);
112169942Sobrienprivate int check_format_type(const char *, int);
113139368Sobrienprivate int check_format(struct magic_set *, struct magic *);
114175296Sobrienprivate int get_op(char);
11568349Sobrien
116133359Sobrienprivate size_t maxmagic = 0;
117133359Sobrienprivate size_t magicsize = sizeof(struct magic);
11868349Sobrien
119159764Sobrien
12080588Sobrien#ifdef COMPILE_ONLY
12174784Sobrien
122103373Sobrienint main(int, char *[]);
12380588Sobrien
12480588Sobrienint
125103373Sobrienmain(int argc, char *argv[])
12680588Sobrien{
12780588Sobrien	int ret;
128133359Sobrien	struct magic_set *ms;
129133359Sobrien	char *progname;
13080588Sobrien
13180588Sobrien	if ((progname = strrchr(argv[0], '/')) != NULL)
13280588Sobrien		progname++;
13380588Sobrien	else
13480588Sobrien		progname = argv[0];
13580588Sobrien
13680588Sobrien	if (argc != 2) {
137133359Sobrien		(void)fprintf(stderr, "Usage: %s file\n", progname);
138133359Sobrien		return 1;
13980588Sobrien	}
14080588Sobrien
141133359Sobrien	if ((ms = magic_open(MAGIC_CHECK)) == NULL) {
142133359Sobrien		(void)fprintf(stderr, "%s: %s\n", progname, strerror(errno));
143133359Sobrien		return 1;
144133359Sobrien	}
145133359Sobrien	ret = magic_compile(ms, argv[1]) == -1 ? 1 : 0;
146133359Sobrien	if (ret == 1)
147133359Sobrien		(void)fprintf(stderr, "%s: %s\n", progname, magic_error(ms));
148133359Sobrien	magic_close(ms);
149133359Sobrien	return ret;
15080588Sobrien}
15180588Sobrien#endif /* COMPILE_ONLY */
15280588Sobrien
153169962Sobrienstatic const struct type_tbl_s {
154169962Sobrien	const char *name;
155169962Sobrien	const size_t len;
156169962Sobrien	const int type;
157169962Sobrien	const int format;
158169962Sobrien} type_tbl[] = {
159169962Sobrien# define XX(s)		s, (sizeof(s) - 1)
160169962Sobrien# define XX_NULL	NULL, 0
161169962Sobrien	{ XX("byte"),		FILE_BYTE,		FILE_FMT_NUM },
162169962Sobrien	{ XX("short"),		FILE_SHORT,		FILE_FMT_NUM },
163169962Sobrien	{ XX("default"),	FILE_DEFAULT,		FILE_FMT_STR },
164169962Sobrien	{ XX("long"),		FILE_LONG,		FILE_FMT_NUM },
165169962Sobrien	{ XX("string"),		FILE_STRING,		FILE_FMT_STR },
166169962Sobrien	{ XX("date"),		FILE_DATE,		FILE_FMT_STR },
167169962Sobrien	{ XX("beshort"),	FILE_BESHORT,		FILE_FMT_NUM },
168169962Sobrien	{ XX("belong"),		FILE_BELONG,		FILE_FMT_NUM },
169169962Sobrien	{ XX("bedate"),		FILE_BEDATE,		FILE_FMT_STR },
170169962Sobrien	{ XX("leshort"),	FILE_LESHORT,		FILE_FMT_NUM },
171169962Sobrien	{ XX("lelong"),		FILE_LELONG,		FILE_FMT_NUM },
172169962Sobrien	{ XX("ledate"),		FILE_LEDATE,		FILE_FMT_STR },
173169962Sobrien	{ XX("pstring"),	FILE_PSTRING,		FILE_FMT_STR },
174169962Sobrien	{ XX("ldate"),		FILE_LDATE,		FILE_FMT_STR },
175169962Sobrien	{ XX("beldate"),	FILE_BELDATE,		FILE_FMT_STR },
176169962Sobrien	{ XX("leldate"),	FILE_LELDATE,		FILE_FMT_STR },
177169962Sobrien	{ XX("regex"),		FILE_REGEX,		FILE_FMT_STR },
178169962Sobrien	{ XX("bestring16"),	FILE_BESTRING16,	FILE_FMT_STR },
179169962Sobrien	{ XX("lestring16"),	FILE_LESTRING16,	FILE_FMT_STR },
180169962Sobrien	{ XX("search"),		FILE_SEARCH,		FILE_FMT_STR },
181169962Sobrien	{ XX("medate"),		FILE_MEDATE,		FILE_FMT_STR },
182169962Sobrien	{ XX("meldate"),	FILE_MELDATE,		FILE_FMT_STR },
183169962Sobrien	{ XX("melong"),		FILE_MELONG,		FILE_FMT_NUM },
184169962Sobrien	{ XX("quad"),		FILE_QUAD,		FILE_FMT_QUAD },
185169962Sobrien	{ XX("lequad"),		FILE_LEQUAD,		FILE_FMT_QUAD },
186169962Sobrien	{ XX("bequad"),		FILE_BEQUAD,		FILE_FMT_QUAD },
187169962Sobrien	{ XX("qdate"),		FILE_QDATE,		FILE_FMT_STR },
188169962Sobrien	{ XX("leqdate"),	FILE_LEQDATE,		FILE_FMT_STR },
189169962Sobrien	{ XX("beqdate"),	FILE_BEQDATE,		FILE_FMT_STR },
190169962Sobrien	{ XX("qldate"),		FILE_QLDATE,		FILE_FMT_STR },
191169962Sobrien	{ XX("leqldate"),	FILE_LEQLDATE,		FILE_FMT_STR },
192169962Sobrien	{ XX("beqldate"),	FILE_BEQLDATE,		FILE_FMT_STR },
193175296Sobrien	{ XX("float"),		FILE_FLOAT,		FILE_FMT_FLOAT },
194175296Sobrien	{ XX("befloat"),	FILE_BEFLOAT,		FILE_FMT_FLOAT },
195175296Sobrien	{ XX("lefloat"),	FILE_LEFLOAT,		FILE_FMT_FLOAT },
196175296Sobrien	{ XX("double"),		FILE_DOUBLE,		FILE_FMT_DOUBLE },
197175296Sobrien	{ XX("bedouble"),	FILE_BEDOUBLE,		FILE_FMT_DOUBLE },
198175296Sobrien	{ XX("ledouble"),	FILE_LEDOUBLE,		FILE_FMT_DOUBLE },
199169962Sobrien	{ XX_NULL,		FILE_INVALID,		FILE_FMT_NONE },
200169962Sobrien# undef XX
201169962Sobrien# undef XX_NULL
202169962Sobrien};
20380588Sobrien
204169962Sobrienprivate int
205169962Sobrienget_type(const char *l, const char **t)
206169962Sobrien{
207169962Sobrien	const struct type_tbl_s *p;
208169962Sobrien
209169962Sobrien	for (p = type_tbl; p->name; p++) {
210169962Sobrien		if (strncmp(l, p->name, p->len) == 0) {
211169962Sobrien			if (t)
212169962Sobrien				*t = l + p->len;
213169962Sobrien			break;
214169962Sobrien		}
215169962Sobrien	}
216169962Sobrien	return p->type;
217169962Sobrien}
218169962Sobrien
219169962Sobrienprivate void
220169962Sobrieninit_file_tables(void)
221169962Sobrien{
222169962Sobrien	static int done = 0;
223169962Sobrien	const struct type_tbl_s *p;
224169962Sobrien
225169962Sobrien	if (done)
226169962Sobrien		return;
227169962Sobrien	done++;
228169962Sobrien
229169962Sobrien	for (p = type_tbl; p->name; p++) {
230169962Sobrien		assert(p->type < FILE_NAMES_SIZE);
231169962Sobrien		file_names[p->type] = p->name;
232169962Sobrien		file_formats[p->type] = p->format;
233169962Sobrien	}
234169962Sobrien}
235169962Sobrien
23674784Sobrien/*
23774784Sobrien * Handle one file.
23874784Sobrien */
239133359Sobrienprivate int
240133359Sobrienapprentice_1(struct magic_set *ms, const char *fn, int action,
241133359Sobrien    struct mlist *mlist)
24274784Sobrien{
24374784Sobrien	struct magic *magic = NULL;
244103373Sobrien	uint32_t nmagic = 0;
24574784Sobrien	struct mlist *ml;
24674784Sobrien	int rv = -1;
247133359Sobrien	int mapped;
24874784Sobrien
249133359Sobrien	if (magicsize != FILE_MAGICSIZE) {
250133359Sobrien		file_error(ms, 0, "magic element size %lu != %lu",
251133359Sobrien		    (unsigned long)sizeof(*magic),
252133359Sobrien		    (unsigned long)FILE_MAGICSIZE);
253133359Sobrien		return -1;
25474784Sobrien	}
255133359Sobrien
256133359Sobrien	if (action == FILE_COMPILE) {
257133359Sobrien		rv = apprentice_file(ms, &magic, &nmagic, fn, action);
258133359Sobrien		if (rv != 0)
259133359Sobrien			return -1;
260133359Sobrien		rv = apprentice_compile(ms, &magic, &nmagic, fn);
261133359Sobrien		free(magic);
262133359Sobrien		return rv;
263133359Sobrien	}
264159764Sobrien
26580588Sobrien#ifndef COMPILE_ONLY
266133359Sobrien	if ((rv = apprentice_map(ms, &magic, &nmagic, fn)) == -1) {
267133359Sobrien		if (ms->flags & MAGIC_CHECK)
268139368Sobrien			file_magwarn(ms, "using regular magic file `%s'", fn);
269133359Sobrien		rv = apprentice_file(ms, &magic, &nmagic, fn, action);
270133359Sobrien		if (rv != 0)
271133359Sobrien			return -1;
272133359Sobrien	}
27374784Sobrien
274133359Sobrien	mapped = rv;
27574784Sobrien
276133359Sobrien	if (magic == NULL || nmagic == 0) {
277133359Sobrien		file_delmagic(magic, mapped, nmagic);
278133359Sobrien		return -1;
279133359Sobrien	}
280133359Sobrien
28174784Sobrien	if ((ml = malloc(sizeof(*ml))) == NULL) {
282133359Sobrien		file_delmagic(magic, mapped, nmagic);
283169942Sobrien		file_oomem(ms, sizeof(*ml));
284133359Sobrien		return -1;
28574784Sobrien	}
28674784Sobrien
28774784Sobrien	ml->magic = magic;
28874784Sobrien	ml->nmagic = nmagic;
289133359Sobrien	ml->mapped = mapped;
29074784Sobrien
291133359Sobrien	mlist->prev->next = ml;
292133359Sobrien	ml->prev = mlist->prev;
293133359Sobrien	ml->next = mlist;
294133359Sobrien	mlist->prev = ml;
29574784Sobrien
296133359Sobrien	return 0;
29780588Sobrien#endif /* COMPILE_ONLY */
29874784Sobrien}
29974784Sobrien
300133359Sobrienprotected void
301133359Sobrienfile_delmagic(struct magic *p, int type, size_t entries)
302133359Sobrien{
303133359Sobrien	if (p == NULL)
304133359Sobrien		return;
305133359Sobrien	switch (type) {
306175296Sobrien#ifdef QUICK
307133359Sobrien	case 2:
308133359Sobrien		p--;
309133359Sobrien		(void)munmap((void *)p, sizeof(*p) * (entries + 1));
310133359Sobrien		break;
311175296Sobrien#endif
312133359Sobrien	case 1:
313133359Sobrien		p--;
314133359Sobrien		/*FALLTHROUGH*/
315133359Sobrien	case 0:
316133359Sobrien		free(p);
317133359Sobrien		break;
318133359Sobrien	default:
319133359Sobrien		abort();
320133359Sobrien	}
321133359Sobrien}
32274784Sobrien
323103373Sobrien/* const char *fn: list of magic files */
324133359Sobrienprotected struct mlist *
325133359Sobrienfile_apprentice(struct magic_set *ms, const char *fn, int action)
32668349Sobrien{
327133359Sobrien	char *p, *mfn, *afn = NULL;
32868349Sobrien	int file_err, errs = -1;
329133359Sobrien	struct mlist *mlist;
330169942Sobrien	static const char mime[] = ".mime";
33168349Sobrien
332169962Sobrien	init_file_tables();
333169962Sobrien
334133359Sobrien	if (fn == NULL)
335133359Sobrien		fn = getenv("MAGIC");
336133359Sobrien	if (fn == NULL)
337133359Sobrien		fn = MAGIC;
338133359Sobrien
339169962Sobrien	if ((mfn = strdup(fn)) == NULL) {
340169942Sobrien		file_oomem(ms, strlen(fn));
341133359Sobrien		return NULL;
34268349Sobrien	}
343169962Sobrien	fn = mfn;
344133359Sobrien
345133359Sobrien	if ((mlist = malloc(sizeof(*mlist))) == NULL) {
346133359Sobrien		free(mfn);
347169942Sobrien		file_oomem(ms, sizeof(*mlist));
348133359Sobrien		return NULL;
349133359Sobrien	}
350133359Sobrien	mlist->next = mlist->prev = mlist;
351133359Sobrien
35268349Sobrien	while (fn) {
35368349Sobrien		p = strchr(fn, PATHSEP);
35468349Sobrien		if (p)
35568349Sobrien			*p++ = '\0';
356133359Sobrien		if (*fn == '\0')
357133359Sobrien			break;
358133359Sobrien		if (ms->flags & MAGIC_MIME) {
359169942Sobrien			size_t len = strlen(fn) + sizeof(mime);
360169942Sobrien			if ((afn = malloc(len)) == NULL) {
361133359Sobrien				free(mfn);
362133359Sobrien				free(mlist);
363169942Sobrien				file_oomem(ms, len);
364133359Sobrien				return NULL;
365133359Sobrien			}
366133359Sobrien			(void)strcpy(afn, fn);
367169942Sobrien			(void)strcat(afn, mime);
368133359Sobrien			fn = afn;
369133359Sobrien		}
370133359Sobrien		file_err = apprentice_1(ms, fn, action, mlist);
37168349Sobrien		if (file_err > errs)
37268349Sobrien			errs = file_err;
373133359Sobrien		if (afn) {
374133359Sobrien			free(afn);
375133359Sobrien			afn = NULL;
376133359Sobrien		}
37768349Sobrien		fn = p;
37868349Sobrien	}
379133359Sobrien	if (errs == -1) {
380133359Sobrien		free(mfn);
381133359Sobrien		free(mlist);
382133359Sobrien		mlist = NULL;
383133359Sobrien		file_error(ms, 0, "could not find any magic files!");
384133359Sobrien		return NULL;
385133359Sobrien	}
38668349Sobrien	free(mfn);
387133359Sobrien	return mlist;
38868349Sobrien}
38968349Sobrien
390169942Sobrien/*
391169942Sobrien * Get weight of this magic entry, for sorting purposes.
392169942Sobrien */
393159764Sobrienprivate size_t
394159764Sobrienapprentice_magic_strength(const struct magic *m)
395159764Sobrien{
396169942Sobrien#define MULT 10
397169942Sobrien	size_t val = 2 * MULT;	/* baseline strength */
398169942Sobrien
399159764Sobrien	switch (m->type) {
400169962Sobrien	case FILE_DEFAULT:	/* make sure this sorts last */
401169962Sobrien		return 0;
402169962Sobrien
403159764Sobrien	case FILE_BYTE:
404169942Sobrien		val += 1 * MULT;
405169942Sobrien		break;
406159764Sobrien
407159764Sobrien	case FILE_SHORT:
408159764Sobrien	case FILE_LESHORT:
409159764Sobrien	case FILE_BESHORT:
410169942Sobrien		val += 2 * MULT;
411169942Sobrien		break;
412159764Sobrien
413159764Sobrien	case FILE_LONG:
414159764Sobrien	case FILE_LELONG:
415159764Sobrien	case FILE_BELONG:
416159764Sobrien	case FILE_MELONG:
417169942Sobrien		val += 4 * MULT;
418169942Sobrien		break;
419159764Sobrien
420159764Sobrien	case FILE_PSTRING:
421159764Sobrien	case FILE_STRING:
422169942Sobrien		val += m->vallen * MULT;
423169942Sobrien		break;
424169942Sobrien
425159764Sobrien	case FILE_BESTRING16:
426159764Sobrien	case FILE_LESTRING16:
427169942Sobrien		val += m->vallen * MULT / 2;
428169942Sobrien		break;
429169942Sobrien
430159764Sobrien	case FILE_SEARCH:
431169942Sobrien	case FILE_REGEX:
432169942Sobrien		val += m->vallen;
433169942Sobrien		break;
434159764Sobrien
435159764Sobrien	case FILE_DATE:
436159764Sobrien	case FILE_LEDATE:
437159764Sobrien	case FILE_BEDATE:
438159764Sobrien	case FILE_MEDATE:
439159764Sobrien	case FILE_LDATE:
440159764Sobrien	case FILE_LELDATE:
441159764Sobrien	case FILE_BELDATE:
442159764Sobrien	case FILE_MELDATE:
443175296Sobrien	case FILE_FLOAT:
444175296Sobrien	case FILE_BEFLOAT:
445175296Sobrien	case FILE_LEFLOAT:
446169942Sobrien		val += 4 * MULT;
447169942Sobrien		break;
448159764Sobrien
449169942Sobrien	case FILE_QUAD:
450169942Sobrien	case FILE_BEQUAD:
451169942Sobrien	case FILE_LEQUAD:
452169942Sobrien	case FILE_QDATE:
453169942Sobrien	case FILE_LEQDATE:
454169942Sobrien	case FILE_BEQDATE:
455169942Sobrien	case FILE_QLDATE:
456169942Sobrien	case FILE_LEQLDATE:
457169942Sobrien	case FILE_BEQLDATE:
458175296Sobrien	case FILE_DOUBLE:
459175296Sobrien	case FILE_BEDOUBLE:
460175296Sobrien	case FILE_LEDOUBLE:
461169942Sobrien		val += 8 * MULT;
462169942Sobrien		break;
463169942Sobrien
464159764Sobrien	default:
465169942Sobrien		val = 0;
466169942Sobrien		(void)fprintf(stderr, "Bad type %d\n", m->type);
467169942Sobrien		abort();
468159764Sobrien	}
469169942Sobrien
470169942Sobrien	switch (m->reln) {
471169942Sobrien	case 'x':	/* matches anything penalize */
472169942Sobrien		val = 0;
473169942Sobrien		break;
474169942Sobrien
475169942Sobrien	case '!':
476169942Sobrien	case '=':	/* Exact match, prefer */
477169942Sobrien		val += MULT;
478169942Sobrien		break;
479169942Sobrien
480169942Sobrien	case '>':
481169942Sobrien	case '<':	/* comparison match reduce strength */
482169942Sobrien		val -= 2 * MULT;
483169942Sobrien		break;
484169942Sobrien
485169942Sobrien	case '^':
486169942Sobrien	case '&':	/* masking bits, we could count them too */
487169942Sobrien		val -= MULT;
488169942Sobrien		break;
489169942Sobrien
490169942Sobrien	default:
491169942Sobrien		(void)fprintf(stderr, "Bad relation %c\n", m->reln);
492169942Sobrien		abort();
493169942Sobrien	}
494169962Sobrien
495169962Sobrien	if (val == 0)	/* ensure we only return 0 for FILE_DEFAULT */
496169962Sobrien		val = 1;
497169962Sobrien
498169942Sobrien	return val;
499159764Sobrien}
500159764Sobrien
501169942Sobrien/*
502169942Sobrien * Sort callback for sorting entries by "strength" (basically length)
503169942Sobrien */
504159764Sobrienprivate int
505159764Sobrienapprentice_sort(const void *a, const void *b)
506159764Sobrien{
507159764Sobrien	const struct magic_entry *ma = a;
508159764Sobrien	const struct magic_entry *mb = b;
509159764Sobrien	size_t sa = apprentice_magic_strength(ma->mp);
510159764Sobrien	size_t sb = apprentice_magic_strength(mb->mp);
511159764Sobrien	if (sa == sb)
512159764Sobrien		return 0;
513159764Sobrien	else if (sa > sb)
514159764Sobrien		return -1;
515159764Sobrien	else
516159764Sobrien		return 1;
517159764Sobrien}
518159764Sobrien
51974784Sobrien/*
52074784Sobrien * parse from a file
521103373Sobrien * const char *fn: name of magic file
52274784Sobrien */
523133359Sobrienprivate int
524133359Sobrienapprentice_file(struct magic_set *ms, struct magic **magicp, uint32_t *nmagicp,
525133359Sobrien    const char *fn, int action)
52668349Sobrien{
527133359Sobrien	private const char hdr[] =
52868349Sobrien		"cont\toffset\ttype\topcode\tmask\tvalue\tdesc";
52968349Sobrien	FILE *f;
530175296Sobrien	char line[BUFSIZ];
53168349Sobrien	int errs = 0;
532159764Sobrien	struct magic_entry *marray;
533169942Sobrien	uint32_t marraycount, i, mentrycount = 0;
534169942Sobrien	size_t lineno = 0;
53568349Sobrien
536169942Sobrien	ms->flags |= MAGIC_CHECK;	/* Enable checks for parsed files */
537169942Sobrien
538139368Sobrien	f = fopen(ms->file = fn, "r");
53974784Sobrien	if (f == NULL) {
54068349Sobrien		if (errno != ENOENT)
541133359Sobrien			file_error(ms, errno, "cannot read magic file `%s'",
542133359Sobrien			    fn);
54368349Sobrien		return -1;
54468349Sobrien	}
54568349Sobrien
546169942Sobrien        maxmagic = MAXMAGIS;
547159825Sobrien	if ((marray = calloc(maxmagic, sizeof(*marray))) == NULL) {
548133359Sobrien		(void)fclose(f);
549169942Sobrien		file_oomem(ms, maxmagic * sizeof(*marray));
550133359Sobrien		return -1;
55174784Sobrien	}
552159764Sobrien	marraycount = 0;
55374784Sobrien
554133359Sobrien	/* print silly verbose header for USG compat. */
555133359Sobrien	if (action == FILE_CHECK)
556133359Sobrien		(void)fprintf(stderr, "%s\n", hdr);
557133359Sobrien
558169942Sobrien	/* read and parse this file */
559175296Sobrien	for (ms->line = 1; fgets(line, sizeof(line), f) != NULL; ms->line++) {
560139368Sobrien		size_t len;
561139368Sobrien		len = strlen(line);
562169942Sobrien		if (len == 0) /* null line, garbage, etc */
56368349Sobrien			continue;
564169942Sobrien		if (line[len - 1] == '\n') {
565169942Sobrien			lineno++;
566159764Sobrien			line[len - 1] = '\0'; /* delete newline */
567169942Sobrien		}
568169942Sobrien		if (line[0] == '\0')	/* empty, do not parse */
569169942Sobrien			continue;
570169942Sobrien		if (line[0] == '#')	/* comment, do not parse */
571169942Sobrien			continue;
572169942Sobrien		if (parse(ms, &marray, &marraycount, line, lineno, action) != 0)
573159764Sobrien			errs++;
57468349Sobrien	}
57568349Sobrien
576133359Sobrien	(void)fclose(f);
577159764Sobrien	if (errs)
578159764Sobrien		goto out;
579159764Sobrien
580159764Sobrien#ifndef NOORDER
581159764Sobrien	qsort(marray, marraycount, sizeof(*marray), apprentice_sort);
582169962Sobrien	/*
583169962Sobrien	 * Make sure that any level 0 "default" line is last (if one exists).
584169962Sobrien	 */
585169962Sobrien	for (i = 0; i < marraycount; i++) {
586169962Sobrien		if (marray[i].mp->cont_level == 0 &&
587169962Sobrien		    marray[i].mp->type == FILE_DEFAULT) {
588169962Sobrien			while (++i < marraycount)
589169962Sobrien				if (marray[i].mp->cont_level == 0)
590169962Sobrien					break;
591169962Sobrien			if (i != marraycount) {
592169962Sobrien				ms->line = marray[i].mp->lineno; /* XXX - Ugh! */
593169962Sobrien				file_magwarn(ms,
594169962Sobrien				    "level 0 \"default\" did not sort last");
595169962Sobrien			}
596169962Sobrien			break;
597169962Sobrien		}
598169962Sobrien	}
599159764Sobrien#endif
600159764Sobrien
601169942Sobrien	for (i = 0; i < marraycount; i++)
602159764Sobrien		mentrycount += marray[i].cont_count;
603159764Sobrien
604159764Sobrien	if ((*magicp = malloc(sizeof(**magicp) * mentrycount)) == NULL) {
605169942Sobrien		file_oomem(ms, sizeof(**magicp) * mentrycount);
606159764Sobrien		errs++;
607159764Sobrien		goto out;
608159764Sobrien	}
609159764Sobrien
610159764Sobrien	mentrycount = 0;
611159764Sobrien	for (i = 0; i < marraycount; i++) {
612159764Sobrien		(void)memcpy(*magicp + mentrycount, marray[i].mp,
613159764Sobrien		    marray[i].cont_count * sizeof(**magicp));
614159764Sobrien		mentrycount += marray[i].cont_count;
615159764Sobrien	}
616159764Sobrienout:
617159764Sobrien	for (i = 0; i < marraycount; i++)
618159764Sobrien		free(marray[i].mp);
619159764Sobrien	free(marray);
62074784Sobrien	if (errs) {
62174784Sobrien		*magicp = NULL;
62274784Sobrien		*nmagicp = 0;
623159764Sobrien		return errs;
624159764Sobrien	} else {
625159764Sobrien		*nmagicp = mentrycount;
626159764Sobrien		return 0;
62774784Sobrien	}
628159764Sobrien
62968349Sobrien}
63068349Sobrien
63168349Sobrien/*
63268349Sobrien * extend the sign bit if the comparison is to be signed
63368349Sobrien */
634169942Sobrienprotected uint64_t
635169942Sobrienfile_signextend(struct magic_set *ms, struct magic *m, uint64_t v)
63668349Sobrien{
637169962Sobrien	if (!(m->flag & UNSIGNED)) {
63868349Sobrien		switch(m->type) {
63968349Sobrien		/*
64068349Sobrien		 * Do not remove the casts below.  They are
64168349Sobrien		 * vital.  When later compared with the data,
64268349Sobrien		 * the sign extension must have happened.
64368349Sobrien		 */
644133359Sobrien		case FILE_BYTE:
64568349Sobrien			v = (char) v;
64668349Sobrien			break;
647133359Sobrien		case FILE_SHORT:
648133359Sobrien		case FILE_BESHORT:
649133359Sobrien		case FILE_LESHORT:
65068349Sobrien			v = (short) v;
65168349Sobrien			break;
652133359Sobrien		case FILE_DATE:
653133359Sobrien		case FILE_BEDATE:
654133359Sobrien		case FILE_LEDATE:
655159764Sobrien		case FILE_MEDATE:
656133359Sobrien		case FILE_LDATE:
657133359Sobrien		case FILE_BELDATE:
658133359Sobrien		case FILE_LELDATE:
659159764Sobrien		case FILE_MELDATE:
660133359Sobrien		case FILE_LONG:
661133359Sobrien		case FILE_BELONG:
662133359Sobrien		case FILE_LELONG:
663159764Sobrien		case FILE_MELONG:
664175296Sobrien		case FILE_FLOAT:
665175296Sobrien		case FILE_BEFLOAT:
666175296Sobrien		case FILE_LEFLOAT:
667103373Sobrien			v = (int32_t) v;
66868349Sobrien			break;
669169942Sobrien		case FILE_QUAD:
670169942Sobrien		case FILE_BEQUAD:
671169942Sobrien		case FILE_LEQUAD:
672169942Sobrien		case FILE_QDATE:
673169942Sobrien		case FILE_QLDATE:
674169942Sobrien		case FILE_BEQDATE:
675169942Sobrien		case FILE_BEQLDATE:
676169942Sobrien		case FILE_LEQDATE:
677169942Sobrien		case FILE_LEQLDATE:
678175296Sobrien		case FILE_DOUBLE:
679175296Sobrien		case FILE_BEDOUBLE:
680175296Sobrien		case FILE_LEDOUBLE:
681169942Sobrien			v = (int64_t) v;
682169942Sobrien			break;
683133359Sobrien		case FILE_STRING:
684133359Sobrien		case FILE_PSTRING:
685139368Sobrien		case FILE_BESTRING16:
686139368Sobrien		case FILE_LESTRING16:
687133359Sobrien		case FILE_REGEX:
688159764Sobrien		case FILE_SEARCH:
689169962Sobrien		case FILE_DEFAULT:
690103373Sobrien			break;
69168349Sobrien		default:
692133359Sobrien			if (ms->flags & MAGIC_CHECK)
693139368Sobrien			    file_magwarn(ms, "cannot happen: m->type=%d\n",
694133359Sobrien				    m->type);
695133359Sobrien			return ~0U;
69668349Sobrien		}
697169962Sobrien	}
69868349Sobrien	return v;
69968349Sobrien}
70068349Sobrien
701169962Sobrienprivate int
702169962Sobrienstring_modifier_check(struct magic_set *ms, struct magic const *m)
703169962Sobrien{
704169962Sobrien	if ((ms->flags & MAGIC_CHECK) == 0)
705169962Sobrien		return 0;
706169962Sobrien
707169962Sobrien	switch (m->type) {
708169962Sobrien	case FILE_BESTRING16:
709169962Sobrien	case FILE_LESTRING16:
710169962Sobrien		if (m->str_flags != 0) {
711169962Sobrien			file_magwarn(ms, "no modifiers allowed for 16-bit strings\n");
712169962Sobrien			return -1;
713169962Sobrien		}
714169962Sobrien		break;
715169962Sobrien	case FILE_STRING:
716169962Sobrien	case FILE_PSTRING:
717169962Sobrien		if ((m->str_flags & REGEX_OFFSET_START) != 0) {
718169962Sobrien			file_magwarn(ms, "'/%c' only allowed on regex and search\n",
719169962Sobrien			    CHAR_REGEX_OFFSET_START);
720169962Sobrien			return -1;
721169962Sobrien		}
722169962Sobrien		break;
723169962Sobrien	case FILE_SEARCH:
724169962Sobrien		break;
725169962Sobrien	case FILE_REGEX:
726169962Sobrien		if ((m->str_flags & STRING_COMPACT_BLANK) != 0) {
727169962Sobrien			file_magwarn(ms, "'/%c' not allowed on regex\n",
728169962Sobrien			    CHAR_COMPACT_BLANK);
729169962Sobrien			return -1;
730169962Sobrien		}
731169962Sobrien		if ((m->str_flags & STRING_COMPACT_OPTIONAL_BLANK) != 0) {
732169962Sobrien			file_magwarn(ms, "'/%c' not allowed on regex\n",
733169962Sobrien			    CHAR_COMPACT_OPTIONAL_BLANK);
734169962Sobrien			return -1;
735169962Sobrien		}
736169962Sobrien		break;
737169962Sobrien	default:
738169962Sobrien		file_magwarn(ms, "coding error: m->type=%d\n",
739169962Sobrien		    m->type);
740169962Sobrien		return -1;
741169962Sobrien	}
742169962Sobrien	return 0;
743169962Sobrien}
744169962Sobrien
745169962Sobrienprivate int
746169962Sobrienget_op(char c)
747169962Sobrien{
748169962Sobrien	switch (c) {
749169962Sobrien	case '&':
750169962Sobrien		return FILE_OPAND;
751169962Sobrien	case '|':
752169962Sobrien		return FILE_OPOR;
753169962Sobrien	case '^':
754169962Sobrien		return FILE_OPXOR;
755169962Sobrien	case '+':
756169962Sobrien		return FILE_OPADD;
757169962Sobrien	case '-':
758169962Sobrien		return FILE_OPMINUS;
759169962Sobrien	case '*':
760169962Sobrien		return FILE_OPMULTIPLY;
761169962Sobrien	case '/':
762169962Sobrien		return FILE_OPDIVIDE;
763169962Sobrien	case '%':
764169962Sobrien		return FILE_OPMODULO;
765169962Sobrien	default:
766169962Sobrien		return -1;
767169962Sobrien	}
768169962Sobrien}
769169962Sobrien
770169962Sobrien#ifdef ENABLE_CONDITIONALS
771169962Sobrienprivate int
772169962Sobrienget_cond(const char *l, const char **t)
773169962Sobrien{
774169962Sobrien	static struct cond_tbl_s {
775169962Sobrien		const char *name;
776169962Sobrien		const size_t len;
777169962Sobrien		const int cond;
778169962Sobrien	} cond_tbl[] = {
779169962Sobrien		{ "if",		2,	COND_IF },
780169962Sobrien		{ "elif",	4,	COND_ELIF },
781169962Sobrien		{ "else",	4,	COND_ELSE },
782169962Sobrien		{ NULL, 	0,	COND_NONE },
783169962Sobrien	};
784169962Sobrien	struct cond_tbl_s *p;
785169962Sobrien
786169962Sobrien	for (p = cond_tbl; p->name; p++) {
787169962Sobrien		if (strncmp(l, p->name, p->len) == 0 &&
788169962Sobrien		    isspace((unsigned char)l[p->len])) {
789169962Sobrien			if (t)
790169962Sobrien				*t = l + p->len;
791169962Sobrien			break;
792169962Sobrien		}
793169962Sobrien	}
794169962Sobrien	return p->cond;
795169962Sobrien}
796169962Sobrien
797169962Sobrienprivate int
798169962Sobriencheck_cond(struct magic_set *ms, int cond, uint32_t cont_level)
799169962Sobrien{
800169962Sobrien	int last_cond;
801169962Sobrien	last_cond = ms->c.li[cont_level].last_cond;
802169962Sobrien
803169962Sobrien	switch (cond) {
804169962Sobrien	case COND_IF:
805169962Sobrien		if (last_cond != COND_NONE && last_cond != COND_ELIF) {
806169962Sobrien			if (ms->flags & MAGIC_CHECK)
807169962Sobrien				file_magwarn(ms, "syntax error: `if'");
808169962Sobrien			return -1;
809169962Sobrien		}
810169962Sobrien		last_cond = COND_IF;
811169962Sobrien		break;
812169962Sobrien
813169962Sobrien	case COND_ELIF:
814169962Sobrien		if (last_cond != COND_IF && last_cond != COND_ELIF) {
815169962Sobrien			if (ms->flags & MAGIC_CHECK)
816169962Sobrien				file_magwarn(ms, "syntax error: `elif'");
817169962Sobrien			return -1;
818169962Sobrien		}
819169962Sobrien		last_cond = COND_ELIF;
820169962Sobrien		break;
821169962Sobrien
822169962Sobrien	case COND_ELSE:
823169962Sobrien		if (last_cond != COND_IF && last_cond != COND_ELIF) {
824169962Sobrien			if (ms->flags & MAGIC_CHECK)
825169962Sobrien				file_magwarn(ms, "syntax error: `else'");
826169962Sobrien			return -1;
827169962Sobrien		}
828169962Sobrien		last_cond = COND_NONE;
829169962Sobrien		break;
830169962Sobrien
831169962Sobrien	case COND_NONE:
832169962Sobrien		last_cond = COND_NONE;
833169962Sobrien		break;
834169962Sobrien	}
835169962Sobrien
836169962Sobrien	ms->c.li[cont_level].last_cond = last_cond;
837169962Sobrien	return 0;
838169962Sobrien}
839169962Sobrien#endif /* ENABLE_CONDITIONALS */
840169962Sobrien
84168349Sobrien/*
84268349Sobrien * parse one line from magic file, put into magic[index++] if valid
84368349Sobrien */
844133359Sobrienprivate int
845159764Sobrienparse(struct magic_set *ms, struct magic_entry **mentryp, uint32_t *nmentryp,
846169942Sobrien    const char *line, size_t lineno, int action)
84768349Sobrien{
848169962Sobrien#ifdef ENABLE_CONDITIONALS
849169962Sobrien	static uint32_t last_cont_level = 0;
850169962Sobrien#endif
851169942Sobrien	size_t i;
852159764Sobrien	struct magic_entry *me;
85368349Sobrien	struct magic *m;
854159764Sobrien	const char *l = line;
85584685Sobrien	char *t;
856169962Sobrien	int op;
857159825Sobrien	uint32_t cont_level;
85868349Sobrien
859159764Sobrien	cont_level = 0;
86068349Sobrien
86168349Sobrien	while (*l == '>') {
86268349Sobrien		++l;		/* step over */
863159764Sobrien		cont_level++;
86468349Sobrien	}
865169962Sobrien#ifdef ENABLE_CONDITIONALS
866169962Sobrien	if (cont_level == 0 || cont_level > last_cont_level)
867169962Sobrien		if (file_check_mem(ms, cont_level) == -1)
868169962Sobrien			return -1;
869169962Sobrien	last_cont_level = cont_level;
870169962Sobrien#endif
87168349Sobrien
872159764Sobrien#define ALLOC_CHUNK	(size_t)10
873159764Sobrien#define ALLOC_INCR	(size_t)200
874159764Sobrien
875159764Sobrien	if (cont_level != 0) {
876159764Sobrien		if (*nmentryp == 0) {
877159764Sobrien			file_error(ms, 0, "No current entry for continuation");
878159764Sobrien			return -1;
879159764Sobrien		}
880159764Sobrien		me = &(*mentryp)[*nmentryp - 1];
881159764Sobrien		if (me->cont_count == me->max_count) {
882159764Sobrien			struct magic *nm;
883159764Sobrien			size_t cnt = me->max_count + ALLOC_CHUNK;
884159764Sobrien			if ((nm = realloc(me->mp, sizeof(*nm) * cnt)) == NULL) {
885169942Sobrien				file_oomem(ms, sizeof(*nm) * cnt);
886159764Sobrien				return -1;
887159764Sobrien			}
888159764Sobrien			me->mp = m = nm;
889159764Sobrien			me->max_count = cnt;
890159764Sobrien		}
891159764Sobrien		m = &me->mp[me->cont_count++];
892169962Sobrien		(void)memset(m, 0, sizeof(*m));
893159764Sobrien		m->cont_level = cont_level;
894159764Sobrien	} else {
895159764Sobrien		if (*nmentryp == maxmagic) {
896159764Sobrien			struct magic_entry *mp;
897159764Sobrien
898159764Sobrien			maxmagic += ALLOC_INCR;
899159764Sobrien			if ((mp = realloc(*mentryp, sizeof(*mp) * maxmagic)) ==
900159764Sobrien			    NULL) {
901169942Sobrien				file_oomem(ms, sizeof(*mp) * maxmagic);
902159764Sobrien				return -1;
903159764Sobrien			}
904159764Sobrien			(void)memset(&mp[*nmentryp], 0, sizeof(*mp) *
905159764Sobrien			    ALLOC_INCR);
906159764Sobrien			*mentryp = mp;
907159764Sobrien		}
908159764Sobrien		me = &(*mentryp)[*nmentryp];
909159764Sobrien		if (me->mp == NULL) {
910159764Sobrien			if ((m = malloc(sizeof(*m) * ALLOC_CHUNK)) == NULL) {
911169942Sobrien				file_oomem(ms, sizeof(*m) * ALLOC_CHUNK);
912159764Sobrien				return -1;
913159764Sobrien			}
914159764Sobrien			me->mp = m;
915159764Sobrien			me->max_count = ALLOC_CHUNK;
916159764Sobrien		} else
917159764Sobrien			m = me->mp;
918169962Sobrien		(void)memset(m, 0, sizeof(*m));
919159764Sobrien		m->cont_level = 0;
920159764Sobrien		me->cont_count = 1;
921159764Sobrien	}
922169942Sobrien	m->lineno = lineno;
923159764Sobrien
924169962Sobrien	if (*l == '&') {  /* m->cont_level == 0 checked below. */
925159764Sobrien                ++l;            /* step over */
926159764Sobrien                m->flag |= OFFADD;
927159764Sobrien        }
928169962Sobrien	if (*l == '(') {
92968349Sobrien		++l;		/* step over */
93068349Sobrien		m->flag |= INDIR;
931159764Sobrien		if (m->flag & OFFADD)
932159764Sobrien			m->flag = (m->flag & ~OFFADD) | INDIROFFADD;
933169962Sobrien
934169962Sobrien		if (*l == '&') {  /* m->cont_level == 0 checked below */
935169962Sobrien			++l;            /* step over */
936169962Sobrien			m->flag |= OFFADD;
937169962Sobrien		}
93868349Sobrien	}
939169962Sobrien	/* Indirect offsets are not valid at level 0. */
940169962Sobrien	if (m->cont_level == 0 && (m->flag & (OFFADD | INDIROFFADD)))
941169962Sobrien		if (ms->flags & MAGIC_CHECK)
942169962Sobrien			file_magwarn(ms, "relative offset at level 0");
94368349Sobrien
94468349Sobrien	/* get offset, then skip over it */
945133359Sobrien	m->offset = (uint32_t)strtoul(l, &t, 0);
94668349Sobrien        if (l == t)
947133359Sobrien		if (ms->flags & MAGIC_CHECK)
948139368Sobrien			file_magwarn(ms, "offset `%s' invalid", l);
94968349Sobrien        l = t;
95068349Sobrien
95168349Sobrien	if (m->flag & INDIR) {
952133359Sobrien		m->in_type = FILE_LONG;
95374784Sobrien		m->in_offset = 0;
95468349Sobrien		/*
95568349Sobrien		 * read [.lbs][+-]nnnnn)
95668349Sobrien		 */
95768349Sobrien		if (*l == '.') {
95868349Sobrien			l++;
95968349Sobrien			switch (*l) {
96068349Sobrien			case 'l':
961133359Sobrien				m->in_type = FILE_LELONG;
96268349Sobrien				break;
96368349Sobrien			case 'L':
964133359Sobrien				m->in_type = FILE_BELONG;
96568349Sobrien				break;
966159764Sobrien			case 'm':
967159764Sobrien				m->in_type = FILE_MELONG;
968159764Sobrien				break;
96968349Sobrien			case 'h':
97068349Sobrien			case 's':
971133359Sobrien				m->in_type = FILE_LESHORT;
97268349Sobrien				break;
97368349Sobrien			case 'H':
97468349Sobrien			case 'S':
975133359Sobrien				m->in_type = FILE_BESHORT;
97668349Sobrien				break;
97768349Sobrien			case 'c':
97868349Sobrien			case 'b':
97968349Sobrien			case 'C':
98068349Sobrien			case 'B':
981133359Sobrien				m->in_type = FILE_BYTE;
98268349Sobrien				break;
983175296Sobrien			case 'e':
984175296Sobrien			case 'f':
985175296Sobrien			case 'g':
986175296Sobrien				m->in_type = FILE_LEDOUBLE;
987175296Sobrien				break;
988175296Sobrien			case 'E':
989175296Sobrien			case 'F':
990175296Sobrien			case 'G':
991175296Sobrien				m->in_type = FILE_BEDOUBLE;
992175296Sobrien				break;
99368349Sobrien			default:
994133359Sobrien				if (ms->flags & MAGIC_CHECK)
995139368Sobrien					file_magwarn(ms,
996139368Sobrien					    "indirect offset type `%c' invalid",
997133359Sobrien					    *l);
99868349Sobrien				break;
99968349Sobrien			}
100068349Sobrien			l++;
100168349Sobrien		}
1002169962Sobrien
1003169962Sobrien		m->in_op = 0;
100480588Sobrien		if (*l == '~') {
1005159764Sobrien			m->in_op |= FILE_OPINVERSE;
100680588Sobrien			l++;
100780588Sobrien		}
1008169962Sobrien		if ((op = get_op(*l)) != -1) {
1009169962Sobrien			m->in_op |= op;
101080588Sobrien			l++;
101180588Sobrien		}
1012159764Sobrien		if (*l == '(') {
1013159764Sobrien			m->in_op |= FILE_OPINDIRECT;
1014159764Sobrien			l++;
1015159764Sobrien		}
1016159764Sobrien		if (isdigit((unsigned char)*l) || *l == '-') {
1017159764Sobrien			m->in_offset = (int32_t)strtol(l, &t, 0);
1018169962Sobrien			if (l == t)
1019169962Sobrien				if (ms->flags & MAGIC_CHECK)
1020169962Sobrien					file_magwarn(ms,
1021169962Sobrien					    "in_offset `%s' invalid", l);
1022159764Sobrien			l = t;
1023159764Sobrien		}
1024159764Sobrien		if (*l++ != ')' ||
1025159764Sobrien		    ((m->in_op & FILE_OPINDIRECT) && *l++ != ')'))
1026133359Sobrien			if (ms->flags & MAGIC_CHECK)
1027139368Sobrien				file_magwarn(ms,
1028139368Sobrien				    "missing ')' in indirect offset");
102968349Sobrien	}
1030169962Sobrien	EATAB;
103168349Sobrien
1032169962Sobrien#ifdef ENABLE_CONDITIONALS
1033169962Sobrien	m->cond = get_cond(l, &l);
1034169962Sobrien	if (check_cond(ms, m->cond, cont_level) == -1)
1035169962Sobrien		return -1;
103668349Sobrien
103768349Sobrien	EATAB;
1038169962Sobrien#endif
103968349Sobrien
104068349Sobrien	if (*l == 'u') {
104168349Sobrien		++l;
104268349Sobrien		m->flag |= UNSIGNED;
104368349Sobrien	}
104468349Sobrien
1045169962Sobrien	m->type = get_type(l, &l);
1046169962Sobrien	if (m->type == FILE_INVALID) {
1047133359Sobrien		if (ms->flags & MAGIC_CHECK)
1048139368Sobrien			file_magwarn(ms, "type `%s' invalid", l);
104968349Sobrien		return -1;
105068349Sobrien	}
1051169962Sobrien
105268349Sobrien	/* New-style anding: "0 byte&0x80 =0x80 dynamically linked" */
105380588Sobrien	/* New and improved: ~ & | ^ + - * / % -- exciting, isn't it? */
1054169962Sobrien
1055169962Sobrien	m->mask_op = 0;
105680588Sobrien	if (*l == '~') {
1057139368Sobrien		if (!IS_STRING(m->type))
1058159764Sobrien			m->mask_op |= FILE_OPINVERSE;
1059169962Sobrien		else if (ms->flags & MAGIC_CHECK)
1060169962Sobrien			file_magwarn(ms, "'~' invalid for string types");
106168349Sobrien		++l;
106280588Sobrien	}
1063169962Sobrien	m->str_count = 0;
1064169962Sobrien	m->str_flags = 0;
1065169962Sobrien	m->num_mask = 0;
1066169962Sobrien	if ((op = get_op(*l)) != -1) {
1067169962Sobrien		if (!IS_STRING(m->type)) {
1068169962Sobrien			uint64_t val;
106980588Sobrien			++l;
1070133359Sobrien			m->mask_op |= op;
1071169942Sobrien			val = (uint64_t)strtoull(l, &t, 0);
1072159764Sobrien			l = t;
1073169962Sobrien			m->num_mask = file_signextend(ms, m, val);
107480588Sobrien			eatsize(&l);
1075169962Sobrien		}
1076169962Sobrien		else if (op == FILE_OPDIVIDE) {
1077169962Sobrien			int have_count = 0;
1078133359Sobrien			while (!isspace((unsigned char)*++l)) {
107968349Sobrien				switch (*l) {
1080169962Sobrien				/* for portability avoid "case '0' ... '9':" */
1081169962Sobrien				case '0':  case '1':  case '2':
1082169962Sobrien				case '3':  case '4':  case '5':
1083169962Sobrien				case '6':  case '7':  case '8':
1084169962Sobrien				case '9': {
1085169962Sobrien					if (have_count && ms->flags & MAGIC_CHECK)
1086169962Sobrien						file_magwarn(ms,
1087169962Sobrien						    "multiple counts");
1088169962Sobrien					have_count = 1;
1089169962Sobrien					m->str_count = strtoul(l, &t, 0);
1090169962Sobrien					l = t - 1;
109168349Sobrien					break;
1092169962Sobrien				}
109368349Sobrien				case CHAR_COMPACT_BLANK:
1094169962Sobrien					m->str_flags |= STRING_COMPACT_BLANK;
109568349Sobrien					break;
109668349Sobrien				case CHAR_COMPACT_OPTIONAL_BLANK:
1097169962Sobrien					m->str_flags |=
109868349Sobrien					    STRING_COMPACT_OPTIONAL_BLANK;
109968349Sobrien					break;
1100169962Sobrien				case CHAR_IGNORE_LOWERCASE:
1101169962Sobrien					m->str_flags |= STRING_IGNORE_LOWERCASE;
1102169962Sobrien					break;
1103169962Sobrien				case CHAR_IGNORE_UPPERCASE:
1104169962Sobrien					m->str_flags |= STRING_IGNORE_UPPERCASE;
1105169962Sobrien					break;
1106169962Sobrien				case CHAR_REGEX_OFFSET_START:
1107169962Sobrien					m->str_flags |= REGEX_OFFSET_START;
1108169962Sobrien					break;
110968349Sobrien				default:
1110133359Sobrien					if (ms->flags & MAGIC_CHECK)
1111139368Sobrien						file_magwarn(ms,
1112139368Sobrien						"string extension `%c' invalid",
1113133359Sobrien						*l);
111468349Sobrien					return -1;
111568349Sobrien				}
1116169962Sobrien				/* allow multiple '/' for readability */
1117169962Sobrien				if (l[1] == '/' && !isspace((unsigned char)l[2]))
1118169962Sobrien					l++;
111968349Sobrien			}
1120169962Sobrien			if (string_modifier_check(ms, m) == -1)
1121169962Sobrien				return -1;
112268349Sobrien		}
1123169962Sobrien		else {
1124169962Sobrien			if (ms->flags & MAGIC_CHECK)
1125169962Sobrien				file_magwarn(ms, "invalid string op: %c", *t);
1126169962Sobrien			return -1;
1127169962Sobrien		}
112880588Sobrien	}
1129133359Sobrien	/*
1130133359Sobrien	 * We used to set mask to all 1's here, instead let's just not do
1131133359Sobrien	 * anything if mask = 0 (unless you have a better idea)
1132133359Sobrien	 */
113368349Sobrien	EATAB;
113468349Sobrien
113568349Sobrien	switch (*l) {
113668349Sobrien	case '>':
113768349Sobrien	case '<':
113868349Sobrien	/* Old-style anding: "0 byte &0x80 dynamically linked" */
113968349Sobrien	case '&':
114068349Sobrien	case '^':
114168349Sobrien	case '=':
114268349Sobrien  		m->reln = *l;
114368349Sobrien  		++l;
114468349Sobrien		if (*l == '=') {
114568349Sobrien		   /* HP compat: ignore &= etc. */
114668349Sobrien		   ++l;
114768349Sobrien		}
114868349Sobrien		break;
114968349Sobrien	case '!':
1150159764Sobrien		m->reln = *l;
1151159764Sobrien		++l;
1152159764Sobrien		break;
115368349Sobrien	default:
1154169962Sobrien  		m->reln = '=';	/* the default relation */
1155159764Sobrien		if (*l == 'x' && ((isascii((unsigned char)l[1]) &&
1156159764Sobrien		    isspace((unsigned char)l[1])) || !l[1])) {
115768349Sobrien			m->reln = *l;
115868349Sobrien			++l;
115968349Sobrien		}
116068349Sobrien		break;
116168349Sobrien	}
1162169962Sobrien	/*
1163169962Sobrien	 * Grab the value part, except for an 'x' reln.
1164169962Sobrien	 */
1165169962Sobrien	if (m->reln != 'x' && getvalue(ms, m, &l, action))
116668349Sobrien		return -1;
1167169962Sobrien
116868349Sobrien	/*
116968349Sobrien	 * TODO finish this macro and start using it!
117068349Sobrien	 * #define offsetcheck {if (offset > HOWMANY-1)
117168349Sobrien	 *	magwarn("offset too big"); }
117268349Sobrien	 */
117368349Sobrien
117468349Sobrien	/*
1175169962Sobrien	 * Now get last part - the description
117668349Sobrien	 */
117768349Sobrien	EATAB;
117868349Sobrien	if (l[0] == '\b') {
117968349Sobrien		++l;
118068349Sobrien		m->nospflag = 1;
118168349Sobrien	} else if ((l[0] == '\\') && (l[1] == 'b')) {
118268349Sobrien		++l;
118368349Sobrien		++l;
118468349Sobrien		m->nospflag = 1;
118568349Sobrien	} else
118668349Sobrien		m->nospflag = 0;
1187169942Sobrien	for (i = 0; (m->desc[i++] = *l++) != '\0' && i < sizeof(m->desc); )
1188169942Sobrien		continue;
1189169942Sobrien	if (i == sizeof(m->desc)) {
1190169942Sobrien		m->desc[sizeof(m->desc) - 1] = '\0';
1191169942Sobrien		if (ms->flags & MAGIC_CHECK)
1192169942Sobrien			file_magwarn(ms, "description `%s' truncated", m->desc);
1193169942Sobrien	}
119468349Sobrien
1195169942Sobrien        /*
1196169942Sobrien	 * We only do this check while compiling, or if any of the magic
1197169942Sobrien	 * files were not compiled.
1198169942Sobrien         */
1199169942Sobrien        if (ms->flags & MAGIC_CHECK) {
1200169942Sobrien		if (check_format(ms, m) == -1)
1201133359Sobrien			return -1;
1202133359Sobrien	}
1203103373Sobrien#ifndef COMPILE_ONLY
1204133359Sobrien	if (action == FILE_CHECK) {
1205133359Sobrien		file_mdump(m);
120668349Sobrien	}
1207103373Sobrien#endif
1208159764Sobrien	if (m->cont_level == 0)
1209159764Sobrien		++(*nmentryp);		/* make room for next */
121068349Sobrien	return 0;
121168349Sobrien}
121268349Sobrien
1213169942Sobrienprivate int
1214169942Sobriencheck_format_type(const char *ptr, int type)
1215169942Sobrien{
1216169942Sobrien	int quad = 0;
1217169942Sobrien	if (*ptr == '\0') {
1218169942Sobrien		/* Missing format string; bad */
1219169942Sobrien		return -1;
1220169942Sobrien	}
1221169942Sobrien
1222169942Sobrien	switch (type) {
1223169942Sobrien	case FILE_FMT_QUAD:
1224169942Sobrien		quad = 1;
1225169942Sobrien		/*FALLTHROUGH*/
1226169942Sobrien	case FILE_FMT_NUM:
1227169942Sobrien		if (*ptr == '-')
1228169942Sobrien			ptr++;
1229169942Sobrien		if (*ptr == '.')
1230169942Sobrien			ptr++;
1231169942Sobrien		while (isdigit((unsigned char)*ptr)) ptr++;
1232169942Sobrien		if (*ptr == '.')
1233169942Sobrien			ptr++;
1234169942Sobrien		while (isdigit((unsigned char)*ptr)) ptr++;
1235169942Sobrien		if (quad) {
1236169942Sobrien			if (*ptr++ != 'l')
1237169942Sobrien				return -1;
1238169942Sobrien			if (*ptr++ != 'l')
1239169942Sobrien				return -1;
1240169942Sobrien		}
1241169942Sobrien
1242169942Sobrien		switch (*ptr++) {
1243169942Sobrien		case 'l':
1244169942Sobrien			switch (*ptr++) {
1245169942Sobrien			case 'i':
1246169942Sobrien			case 'd':
1247169942Sobrien			case 'u':
1248169942Sobrien			case 'x':
1249169942Sobrien			case 'X':
1250169942Sobrien				return 0;
1251169942Sobrien			default:
1252169942Sobrien				return -1;
1253169942Sobrien			}
1254169942Sobrien
1255169942Sobrien		case 'h':
1256169942Sobrien			switch (*ptr++) {
1257169942Sobrien			case 'h':
1258169942Sobrien				switch (*ptr++) {
1259169942Sobrien				case 'i':
1260169942Sobrien				case 'd':
1261169942Sobrien				case 'u':
1262169942Sobrien				case 'x':
1263169942Sobrien				case 'X':
1264169942Sobrien					return 0;
1265169942Sobrien				default:
1266169942Sobrien					return -1;
1267169942Sobrien				}
1268169942Sobrien			case 'd':
1269169942Sobrien				return 0;
1270169942Sobrien			default:
1271169942Sobrien				return -1;
1272169942Sobrien			}
1273169942Sobrien
1274169942Sobrien		case 'i':
1275169942Sobrien		case 'c':
1276169942Sobrien		case 'd':
1277169942Sobrien		case 'u':
1278169942Sobrien		case 'x':
1279169942Sobrien		case 'X':
1280169942Sobrien			return 0;
1281169942Sobrien
1282169942Sobrien		default:
1283169942Sobrien			return -1;
1284169942Sobrien		}
1285169942Sobrien
1286175296Sobrien	case FILE_FMT_FLOAT:
1287175296Sobrien	case FILE_FMT_DOUBLE:
1288175296Sobrien		if (*ptr == '-')
1289175296Sobrien			ptr++;
1290175296Sobrien		if (*ptr == '.')
1291175296Sobrien			ptr++;
1292175296Sobrien		while (isdigit((unsigned char)*ptr)) ptr++;
1293175296Sobrien		if (*ptr == '.')
1294175296Sobrien			ptr++;
1295175296Sobrien		while (isdigit((unsigned char)*ptr)) ptr++;
1296175296Sobrien
1297175296Sobrien		switch (*ptr++) {
1298175296Sobrien		case 'e':
1299175296Sobrien		case 'E':
1300175296Sobrien		case 'f':
1301175296Sobrien		case 'F':
1302175296Sobrien		case 'g':
1303175296Sobrien		case 'G':
1304175296Sobrien			return 0;
1305175296Sobrien
1306175296Sobrien		default:
1307175296Sobrien			return -1;
1308175296Sobrien		}
1309175296Sobrien
1310175296Sobrien
1311169942Sobrien	case FILE_FMT_STR:
1312169942Sobrien		if (*ptr == '-')
1313169942Sobrien			ptr++;
1314169942Sobrien		while (isdigit((unsigned char )*ptr))
1315169942Sobrien			ptr++;
1316169942Sobrien		if (*ptr == '.') {
1317169942Sobrien			ptr++;
1318169942Sobrien			while (isdigit((unsigned char )*ptr))
1319169942Sobrien				ptr++;
1320169942Sobrien		}
1321169942Sobrien
1322169942Sobrien		switch (*ptr++) {
1323169942Sobrien		case 's':
1324169942Sobrien			return 0;
1325169942Sobrien		default:
1326169942Sobrien			return -1;
1327169942Sobrien		}
1328169942Sobrien
1329169942Sobrien	default:
1330169942Sobrien		/* internal error */
1331169942Sobrien		abort();
1332169942Sobrien	}
1333169942Sobrien	/*NOTREACHED*/
1334169942Sobrien	return -1;
1335169942Sobrien}
1336169942Sobrien
1337133359Sobrien/*
1338133359Sobrien * Check that the optional printf format in description matches
1339133359Sobrien * the type of the magic.
1340133359Sobrien */
1341133359Sobrienprivate int
1342139368Sobriencheck_format(struct magic_set *ms, struct magic *m)
1343133359Sobrien{
1344133359Sobrien	char *ptr;
1345133359Sobrien
1346133359Sobrien	for (ptr = m->desc; *ptr; ptr++)
1347133359Sobrien		if (*ptr == '%')
1348133359Sobrien			break;
1349133359Sobrien	if (*ptr == '\0') {
1350133359Sobrien		/* No format string; ok */
1351133359Sobrien		return 1;
1352133359Sobrien	}
1353169942Sobrien
1354169942Sobrien	assert(file_nformats == file_nnames);
1355169942Sobrien
1356169942Sobrien	if (m->type >= file_nformats) {
1357169942Sobrien		file_error(ms, 0, "Internal error inconsistency between "
1358169942Sobrien		    "m->type and format strings");
1359169942Sobrien		return -1;
1360133359Sobrien	}
1361169942Sobrien	if (file_formats[m->type] == FILE_FMT_NONE) {
1362169942Sobrien		file_error(ms, 0, "No format string for `%s' with description "
1363169942Sobrien		    "`%s'", m->desc, file_names[m->type]);
1364169942Sobrien		return -1;
1365133359Sobrien	}
1366169942Sobrien
1367169942Sobrien	ptr++;
1368169942Sobrien	if (check_format_type(ptr, file_formats[m->type]) == -1) {
1369169942Sobrien		/*
1370169942Sobrien		 * TODO: this error message is unhelpful if the format
1371169942Sobrien		 * string is not one character long
1372169942Sobrien		 */
1373169942Sobrien		file_error(ms, 0, "Printf format `%c' is not valid for type "
1374169942Sobrien		    " `%s' in description `%s'", *ptr,
1375169942Sobrien		    file_names[m->type], m->desc);
1376169942Sobrien		return -1;
1377169942Sobrien	}
1378169942Sobrien
1379133359Sobrien	for (; *ptr; ptr++) {
1380169942Sobrien		if (*ptr == '%') {
1381169942Sobrien			file_error(ms, 0,
1382169942Sobrien			    "Too many format strings (should have at most one) "
1383169942Sobrien			    "for `%s' with description `%s'",
1384169942Sobrien			    file_names[m->type], m->desc);
1385169942Sobrien			return -1;
1386133359Sobrien		}
1387133359Sobrien	}
1388169942Sobrien	return 0;
1389133359Sobrien}
1390133359Sobrien
139168349Sobrien/*
139268349Sobrien * Read a numeric value from a pointer, into the value union of a magic
139368349Sobrien * pointer, according to the magic type.  Update the string pointer to point
139468349Sobrien * just after the number read.  Return 0 for success, non-zero for failure.
139568349Sobrien */
1396133359Sobrienprivate int
1397169962Sobriengetvalue(struct magic_set *ms, struct magic *m, const char **p, int action)
139868349Sobrien{
139968349Sobrien	int slen;
140068349Sobrien
1401133359Sobrien	switch (m->type) {
1402139368Sobrien	case FILE_BESTRING16:
1403139368Sobrien	case FILE_LESTRING16:
1404133359Sobrien	case FILE_STRING:
1405133359Sobrien	case FILE_PSTRING:
1406133359Sobrien	case FILE_REGEX:
1407159764Sobrien	case FILE_SEARCH:
1408169962Sobrien		*p = getstr(ms, *p, m->value.s, sizeof(m->value.s), &slen, action);
1409133359Sobrien		if (*p == NULL) {
1410133359Sobrien			if (ms->flags & MAGIC_CHECK)
1411139368Sobrien				file_magwarn(ms, "cannot get string from `%s'",
1412133359Sobrien				    m->value.s);
1413133359Sobrien			return -1;
1414133359Sobrien		}
141568349Sobrien		m->vallen = slen;
1416133359Sobrien		return 0;
1417175296Sobrien	case FILE_FLOAT:
1418175296Sobrien	case FILE_BEFLOAT:
1419175296Sobrien	case FILE_LEFLOAT:
1420175296Sobrien		if (m->reln != 'x') {
1421175296Sobrien			char *ep;
1422175296Sobrien#ifdef HAVE_STRTOF
1423175296Sobrien			m->value.f = strtof(*p, &ep);
1424175296Sobrien#else
1425175296Sobrien			m->value.f = (float)strtod(*p, &ep);
1426175296Sobrien#endif
1427175296Sobrien			*p = ep;
1428175296Sobrien		}
1429175296Sobrien		return 0;
1430175296Sobrien	case FILE_DOUBLE:
1431175296Sobrien	case FILE_BEDOUBLE:
1432175296Sobrien	case FILE_LEDOUBLE:
1433175296Sobrien		if (m->reln != 'x') {
1434175296Sobrien			char *ep;
1435175296Sobrien			m->value.d = strtod(*p, &ep);
1436175296Sobrien			*p = ep;
1437175296Sobrien		}
1438175296Sobrien		return 0;
1439133359Sobrien	default:
144068349Sobrien		if (m->reln != 'x') {
1441159764Sobrien			char *ep;
1442169942Sobrien			m->value.q = file_signextend(ms, m,
1443169942Sobrien			    (uint64_t)strtoull(*p, &ep, 0));
1444159764Sobrien			*p = ep;
144568349Sobrien			eatsize(p);
144668349Sobrien		}
1447133359Sobrien		return 0;
1448133359Sobrien	}
144968349Sobrien}
145068349Sobrien
145168349Sobrien/*
145268349Sobrien * Convert a string containing C character escapes.  Stop at an unescaped
145368349Sobrien * space or tab.
145468349Sobrien * Copy the converted version to "p", returning its length in *slen.
145568349Sobrien * Return updated scan pointer as function result.
145668349Sobrien */
1457159764Sobrienprivate const char *
1458169962Sobriengetstr(struct magic_set *ms, const char *s, char *p, int plen, int *slen, int action)
145968349Sobrien{
1460159764Sobrien	const char *origs = s;
1461159764Sobrien	char 	*origp = p;
146268349Sobrien	char	*pmax = p + plen - 1;
146368349Sobrien	int	c;
146468349Sobrien	int	val;
146568349Sobrien
146668349Sobrien	while ((c = *s++) != '\0') {
146768349Sobrien		if (isspace((unsigned char) c))
146868349Sobrien			break;
146968349Sobrien		if (p >= pmax) {
1470133359Sobrien			file_error(ms, 0, "string too long: `%s'", origs);
1471133359Sobrien			return NULL;
147268349Sobrien		}
1473169962Sobrien		if (c == '\\') {
147468349Sobrien			switch(c = *s++) {
147568349Sobrien
147668349Sobrien			case '\0':
1477169962Sobrien				if (action == FILE_COMPILE)
1478169962Sobrien					file_magwarn(ms, "incomplete escape");
147968349Sobrien				goto out;
148068349Sobrien
1481169962Sobrien			case '\t':
1482169962Sobrien				if (action == FILE_COMPILE) {
1483169962Sobrien					file_magwarn(ms,
1484169962Sobrien					    "escaped tab found, use \\t instead");
1485169962Sobrien					action++;
1486169962Sobrien				}
1487169962Sobrien				/*FALLTHROUGH*/
148868349Sobrien			default:
1489169962Sobrien				if (action == FILE_COMPILE) {
1490169962Sobrien					if (isprint((unsigned char)c))
1491169962Sobrien					    file_magwarn(ms,
1492169962Sobrien						"no need to escape `%c'", c);
1493169962Sobrien					else
1494169962Sobrien					    file_magwarn(ms,
1495169962Sobrien						"unknown escape sequence: \\%03o", c);
1496169962Sobrien				}
1497169962Sobrien				/*FALLTHROUGH*/
1498169962Sobrien			/* space, perhaps force people to use \040? */
1499169962Sobrien			case ' ':
1500169962Sobrien#if 0
1501169962Sobrien			/*
1502169962Sobrien			 * Other things people escape, but shouldn't need to,
1503169962Sobrien			 * so we disallow them
1504169962Sobrien			 */
1505169962Sobrien			case '\'':
1506169962Sobrien			case '"':
1507169962Sobrien			case '?':
1508169962Sobrien#endif
1509169962Sobrien			/* Relations */
1510169962Sobrien			case '>':
1511169962Sobrien			case '<':
1512169962Sobrien			case '&':
1513169962Sobrien			case '^':
1514169962Sobrien			case '=':
1515169962Sobrien			case '!':
1516169962Sobrien			/* and baskslash itself */
1517169962Sobrien			case '\\':
151868349Sobrien				*p++ = (char) c;
151968349Sobrien				break;
152068349Sobrien
1521169962Sobrien			case 'a':
1522169962Sobrien				*p++ = '\a';
1523169962Sobrien				break;
1524169962Sobrien
1525169962Sobrien			case 'b':
1526169962Sobrien				*p++ = '\b';
1527169962Sobrien				break;
1528169962Sobrien
1529169962Sobrien			case 'f':
1530169962Sobrien				*p++ = '\f';
1531169962Sobrien				break;
1532169962Sobrien
153368349Sobrien			case 'n':
153468349Sobrien				*p++ = '\n';
153568349Sobrien				break;
153668349Sobrien
153768349Sobrien			case 'r':
153868349Sobrien				*p++ = '\r';
153968349Sobrien				break;
154068349Sobrien
154168349Sobrien			case 't':
154268349Sobrien				*p++ = '\t';
154368349Sobrien				break;
154468349Sobrien
154568349Sobrien			case 'v':
154668349Sobrien				*p++ = '\v';
154768349Sobrien				break;
154868349Sobrien
154968349Sobrien			/* \ and up to 3 octal digits */
155068349Sobrien			case '0':
155168349Sobrien			case '1':
155268349Sobrien			case '2':
155368349Sobrien			case '3':
155468349Sobrien			case '4':
155568349Sobrien			case '5':
155668349Sobrien			case '6':
155768349Sobrien			case '7':
155868349Sobrien				val = c - '0';
155968349Sobrien				c = *s++;  /* try for 2 */
1560169962Sobrien				if (c >= '0' && c <= '7') {
1561169962Sobrien					val = (val << 3) | (c - '0');
156268349Sobrien					c = *s++;  /* try for 3 */
1563169962Sobrien					if (c >= '0' && c <= '7')
1564169962Sobrien						val = (val << 3) | (c-'0');
156568349Sobrien					else
156668349Sobrien						--s;
156768349Sobrien				}
156868349Sobrien				else
156968349Sobrien					--s;
157068349Sobrien				*p++ = (char)val;
157168349Sobrien				break;
157268349Sobrien
157368349Sobrien			/* \x and up to 2 hex digits */
157468349Sobrien			case 'x':
157568349Sobrien				val = 'x';	/* Default if no digits */
157668349Sobrien				c = hextoint(*s++);	/* Get next char */
157768349Sobrien				if (c >= 0) {
157868349Sobrien					val = c;
157968349Sobrien					c = hextoint(*s++);
158068349Sobrien					if (c >= 0)
158168349Sobrien						val = (val << 4) + c;
158268349Sobrien					else
158368349Sobrien						--s;
158468349Sobrien				} else
158568349Sobrien					--s;
158668349Sobrien				*p++ = (char)val;
158768349Sobrien				break;
158868349Sobrien			}
158968349Sobrien		} else
159068349Sobrien			*p++ = (char)c;
159168349Sobrien	}
159268349Sobrienout:
159368349Sobrien	*p = '\0';
159468349Sobrien	*slen = p - origp;
159568349Sobrien	return s;
159668349Sobrien}
159768349Sobrien
159868349Sobrien
159968349Sobrien/* Single hex char to int; -1 if not a hex char. */
1600133359Sobrienprivate int
1601103373Sobrienhextoint(int c)
160268349Sobrien{
160368349Sobrien	if (!isascii((unsigned char) c))
160468349Sobrien		return -1;
160568349Sobrien	if (isdigit((unsigned char) c))
160668349Sobrien		return c - '0';
1607169962Sobrien	if ((c >= 'a') && (c <= 'f'))
160868349Sobrien		return c + 10 - 'a';
1609169962Sobrien	if (( c>= 'A') && (c <= 'F'))
161068349Sobrien		return c + 10 - 'A';
161168349Sobrien	return -1;
161268349Sobrien}
161368349Sobrien
161468349Sobrien
161568349Sobrien/*
161668349Sobrien * Print a string containing C character escapes.
161768349Sobrien */
1618133359Sobrienprotected void
1619133359Sobrienfile_showstr(FILE *fp, const char *s, size_t len)
162068349Sobrien{
162168349Sobrien	char	c;
162268349Sobrien
162368349Sobrien	for (;;) {
162468349Sobrien		c = *s++;
1625133359Sobrien		if (len == ~0U) {
162668349Sobrien			if (c == '\0')
162768349Sobrien				break;
162868349Sobrien		}
162968349Sobrien		else  {
163068349Sobrien			if (len-- == 0)
163168349Sobrien				break;
163268349Sobrien		}
1633169962Sobrien		if (c >= 040 && c <= 0176)	/* TODO isprint && !iscntrl */
163468349Sobrien			(void) fputc(c, fp);
163568349Sobrien		else {
163668349Sobrien			(void) fputc('\\', fp);
163768349Sobrien			switch (c) {
1638169962Sobrien			case '\a':
1639169962Sobrien				(void) fputc('a', fp);
1640169962Sobrien				break;
1641169962Sobrien
1642169962Sobrien			case '\b':
1643169962Sobrien				(void) fputc('b', fp);
1644169962Sobrien				break;
1645169962Sobrien
1646169962Sobrien			case '\f':
1647169962Sobrien				(void) fputc('f', fp);
1648169962Sobrien				break;
1649169962Sobrien
165068349Sobrien			case '\n':
165168349Sobrien				(void) fputc('n', fp);
165268349Sobrien				break;
165368349Sobrien
165468349Sobrien			case '\r':
165568349Sobrien				(void) fputc('r', fp);
165668349Sobrien				break;
165768349Sobrien
165868349Sobrien			case '\t':
165968349Sobrien				(void) fputc('t', fp);
166068349Sobrien				break;
166168349Sobrien
166268349Sobrien			case '\v':
166368349Sobrien				(void) fputc('v', fp);
166468349Sobrien				break;
166568349Sobrien
166668349Sobrien			default:
166768349Sobrien				(void) fprintf(fp, "%.3o", c & 0377);
166868349Sobrien				break;
166968349Sobrien			}
167068349Sobrien		}
167168349Sobrien	}
167268349Sobrien}
167368349Sobrien
167468349Sobrien/*
167568349Sobrien * eatsize(): Eat the size spec from a number [eg. 10UL]
167668349Sobrien */
1677133359Sobrienprivate void
1678159764Sobrieneatsize(const char **p)
167968349Sobrien{
1680159764Sobrien	const char *l = *p;
168168349Sobrien
168268349Sobrien	if (LOWCASE(*l) == 'u')
168368349Sobrien		l++;
168468349Sobrien
168568349Sobrien	switch (LOWCASE(*l)) {
168668349Sobrien	case 'l':    /* long */
168768349Sobrien	case 's':    /* short */
168868349Sobrien	case 'h':    /* short */
168968349Sobrien	case 'b':    /* char/byte */
169068349Sobrien	case 'c':    /* char/byte */
169168349Sobrien		l++;
169268349Sobrien		/*FALLTHROUGH*/
169368349Sobrien	default:
169468349Sobrien		break;
169568349Sobrien	}
169668349Sobrien
169768349Sobrien	*p = l;
169868349Sobrien}
169974784Sobrien
170074784Sobrien/*
1701103373Sobrien * handle a compiled file.
170274784Sobrien */
1703133359Sobrienprivate int
1704133359Sobrienapprentice_map(struct magic_set *ms, struct magic **magicp, uint32_t *nmagicp,
1705133359Sobrien    const char *fn)
170674784Sobrien{
170774784Sobrien	int fd;
170874784Sobrien	struct stat st;
1709103373Sobrien	uint32_t *ptr;
1710103373Sobrien	uint32_t version;
171174784Sobrien	int needsbyteswap;
1712133359Sobrien	char buf[MAXPATHLEN];
1713139368Sobrien	char *dbname = mkdbname(fn, buf, sizeof(buf), 0);
1714133359Sobrien	void *mm = NULL;
171574784Sobrien
171680588Sobrien	if (dbname == NULL)
171780588Sobrien		return -1;
171880588Sobrien
1719159764Sobrien	if ((fd = open(dbname, O_RDONLY|O_BINARY)) == -1)
172074784Sobrien		return -1;
172174784Sobrien
172274784Sobrien	if (fstat(fd, &st) == -1) {
1723133359Sobrien		file_error(ms, errno, "cannot stat `%s'", dbname);
172474784Sobrien		goto error;
172574784Sobrien	}
1726133359Sobrien	if (st.st_size < 16) {
1727133359Sobrien		file_error(ms, 0, "file `%s' is too small", dbname);
1728133359Sobrien		goto error;
1729133359Sobrien	}
173074784Sobrien
173180588Sobrien#ifdef QUICK
1732103373Sobrien	if ((mm = mmap(0, (size_t)st.st_size, PROT_READ|PROT_WRITE,
173374784Sobrien	    MAP_PRIVATE|MAP_FILE, fd, (off_t)0)) == MAP_FAILED) {
1734133359Sobrien		file_error(ms, errno, "cannot map `%s'", dbname);
173574784Sobrien		goto error;
173674784Sobrien	}
1737133359Sobrien#define RET	2
173880588Sobrien#else
1739103373Sobrien	if ((mm = malloc((size_t)st.st_size)) == NULL) {
1740169942Sobrien		file_oomem(ms, (size_t)st.st_size);
174180588Sobrien		goto error;
174280588Sobrien	}
1743103373Sobrien	if (read(fd, mm, (size_t)st.st_size) != (size_t)st.st_size) {
1744133359Sobrien		file_badread(ms);
174580588Sobrien		goto error;
174680588Sobrien	}
1747133359Sobrien#define RET	1
174880588Sobrien#endif
1749103373Sobrien	*magicp = mm;
175074784Sobrien	(void)close(fd);
175175937Sobrien	fd = -1;
1752133359Sobrien	ptr = (uint32_t *)(void *)*magicp;
175374784Sobrien	if (*ptr != MAGICNO) {
175474784Sobrien		if (swap4(*ptr) != MAGICNO) {
1755133359Sobrien			file_error(ms, 0, "bad magic in `%s'");
175674784Sobrien			goto error;
175774784Sobrien		}
175874784Sobrien		needsbyteswap = 1;
175974784Sobrien	} else
176074784Sobrien		needsbyteswap = 0;
176174784Sobrien	if (needsbyteswap)
176274784Sobrien		version = swap4(ptr[1]);
176374784Sobrien	else
176474784Sobrien		version = ptr[1];
176574784Sobrien	if (version != VERSIONNO) {
1766175296Sobrien		file_error(ms, 0, "File %d.%d supports only %d version magic "
1767175296Sobrien		    "files. `%s' is version %d", FILE_VERSION_MAJOR, patchlevel,
1768175296Sobrien		    VERSIONNO, dbname, version);
176974784Sobrien		goto error;
177074784Sobrien	}
1771133359Sobrien	*nmagicp = (uint32_t)(st.st_size / sizeof(struct magic)) - 1;
177274784Sobrien	(*magicp)++;
177374784Sobrien	if (needsbyteswap)
177474784Sobrien		byteswap(*magicp, *nmagicp);
1775133359Sobrien	return RET;
177674784Sobrien
177774784Sobrienerror:
177874784Sobrien	if (fd != -1)
177974784Sobrien		(void)close(fd);
1780103373Sobrien	if (mm) {
178180588Sobrien#ifdef QUICK
1782133359Sobrien		(void)munmap((void *)mm, (size_t)st.st_size);
178380588Sobrien#else
1784103373Sobrien		free(mm);
178580588Sobrien#endif
178680588Sobrien	} else {
178774784Sobrien		*magicp = NULL;
178874784Sobrien		*nmagicp = 0;
178974784Sobrien	}
179074784Sobrien	return -1;
179174784Sobrien}
179274784Sobrien
1793133359Sobrienprivate const uint32_t ar[] = {
1794133359Sobrien    MAGICNO, VERSIONNO
1795133359Sobrien};
179674784Sobrien/*
179774784Sobrien * handle an mmaped file.
179874784Sobrien */
1799133359Sobrienprivate int
1800133359Sobrienapprentice_compile(struct magic_set *ms, struct magic **magicp,
1801133359Sobrien    uint32_t *nmagicp, const char *fn)
180274784Sobrien{
180374784Sobrien	int fd;
1804133359Sobrien	char buf[MAXPATHLEN];
1805139368Sobrien	char *dbname = mkdbname(fn, buf, sizeof(buf), 1);
180674784Sobrien
180780588Sobrien	if (dbname == NULL)
180880588Sobrien		return -1;
180980588Sobrien
1810159764Sobrien	if ((fd = open(dbname, O_WRONLY|O_CREAT|O_TRUNC|O_BINARY, 0644)) == -1) {
1811133359Sobrien		file_error(ms, errno, "cannot open `%s'", dbname);
181274784Sobrien		return -1;
181374784Sobrien	}
181474784Sobrien
1815133359Sobrien	if (write(fd, ar, sizeof(ar)) != (ssize_t)sizeof(ar)) {
1816133359Sobrien		file_error(ms, errno, "error writing `%s'", dbname);
181774784Sobrien		return -1;
181874784Sobrien	}
181974784Sobrien
1820133359Sobrien	if (lseek(fd, (off_t)sizeof(struct magic), SEEK_SET)
1821133359Sobrien	    != sizeof(struct magic)) {
1822133359Sobrien		file_error(ms, errno, "error seeking `%s'", dbname);
182374784Sobrien		return -1;
182474784Sobrien	}
182574784Sobrien
1826133359Sobrien	if (write(fd, *magicp, (sizeof(struct magic) * *nmagicp))
1827133359Sobrien	    != (ssize_t)(sizeof(struct magic) * *nmagicp)) {
1828133359Sobrien		file_error(ms, errno, "error writing `%s'", dbname);
182974784Sobrien		return -1;
183074784Sobrien	}
183174784Sobrien
183274784Sobrien	(void)close(fd);
183374784Sobrien	return 0;
183474784Sobrien}
183574784Sobrien
1836133359Sobrienprivate const char ext[] = ".mgc";
183774784Sobrien/*
183874784Sobrien * make a dbname
183974784Sobrien */
1840133359Sobrienprivate char *
1841139368Sobrienmkdbname(const char *fn, char *buf, size_t bufsiz, int strip)
184274784Sobrien{
1843139368Sobrien	if (strip) {
1844139368Sobrien		const char *p;
1845139368Sobrien		if ((p = strrchr(fn, '/')) != NULL)
1846139368Sobrien			fn = ++p;
1847139368Sobrien	}
1848139368Sobrien
1849133359Sobrien	(void)snprintf(buf, bufsiz, "%s%s", fn, ext);
185074784Sobrien	return buf;
185174784Sobrien}
185274784Sobrien
185374784Sobrien/*
185474784Sobrien * Byteswap an mmap'ed file if needed
185574784Sobrien */
1856133359Sobrienprivate void
1857103373Sobrienbyteswap(struct magic *magic, uint32_t nmagic)
185874784Sobrien{
1859103373Sobrien	uint32_t i;
186074784Sobrien	for (i = 0; i < nmagic; i++)
186174784Sobrien		bs1(&magic[i]);
186274784Sobrien}
186374784Sobrien
186474784Sobrien/*
186574784Sobrien * swap a short
186674784Sobrien */
1867133359Sobrienprivate uint16_t
1868103373Sobrienswap2(uint16_t sv)
186974784Sobrien{
1870103373Sobrien	uint16_t rv;
1871133359Sobrien	uint8_t *s = (uint8_t *)(void *)&sv;
1872133359Sobrien	uint8_t *d = (uint8_t *)(void *)&rv;
187374784Sobrien	d[0] = s[1];
187474784Sobrien	d[1] = s[0];
187574784Sobrien	return rv;
187674784Sobrien}
187774784Sobrien
187874784Sobrien/*
187974784Sobrien * swap an int
188074784Sobrien */
1881133359Sobrienprivate uint32_t
1882103373Sobrienswap4(uint32_t sv)
188374784Sobrien{
1884103373Sobrien	uint32_t rv;
1885133359Sobrien	uint8_t *s = (uint8_t *)(void *)&sv;
1886133359Sobrien	uint8_t *d = (uint8_t *)(void *)&rv;
188774784Sobrien	d[0] = s[3];
188874784Sobrien	d[1] = s[2];
188974784Sobrien	d[2] = s[1];
189074784Sobrien	d[3] = s[0];
189174784Sobrien	return rv;
189274784Sobrien}
189374784Sobrien
189474784Sobrien/*
1895169942Sobrien * swap a quad
1896169942Sobrien */
1897169942Sobrienprivate uint64_t
1898169942Sobrienswap8(uint64_t sv)
1899169942Sobrien{
1900169942Sobrien	uint32_t rv;
1901169942Sobrien	uint8_t *s = (uint8_t *)(void *)&sv;
1902169942Sobrien	uint8_t *d = (uint8_t *)(void *)&rv;
1903169942Sobrien	d[0] = s[3];
1904169942Sobrien	d[1] = s[2];
1905169942Sobrien	d[2] = s[1];
1906169942Sobrien	d[3] = s[0];
1907169942Sobrien	d[4] = s[7];
1908169942Sobrien	d[5] = s[6];
1909169942Sobrien	d[6] = s[5];
1910169942Sobrien	d[7] = s[4];
1911169942Sobrien	return rv;
1912169942Sobrien}
1913169942Sobrien
1914169942Sobrien/*
191574784Sobrien * byteswap a single magic entry
191674784Sobrien */
1917133359Sobrienprivate void
1918133359Sobrienbs1(struct magic *m)
191974784Sobrien{
192074784Sobrien	m->cont_level = swap2(m->cont_level);
1921133359Sobrien	m->offset = swap4((uint32_t)m->offset);
1922133359Sobrien	m->in_offset = swap4((uint32_t)m->in_offset);
1923169962Sobrien	m->lineno = swap4((uint32_t)m->lineno);
1924169962Sobrien	if (IS_STRING(m->type)) {
1925169962Sobrien		m->str_count = swap4(m->str_count);
1926169962Sobrien		m->str_flags = swap4(m->str_flags);
1927169962Sobrien	}
1928169962Sobrien	else {
1929169942Sobrien		m->value.q = swap8(m->value.q);
1930169962Sobrien		m->num_mask = swap8(m->num_mask);
1931169962Sobrien	}
193274784Sobrien}
1933