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"
33191771Sobrien
34191771Sobrien#ifndef	lint
35234449SobrienFILE_RCSID("@(#)$File: apprentice.c,v 1.173 2011/12/08 12:38:24 rrt Exp $")
36191771Sobrien#endif	/* lint */
37191771Sobrien
38133359Sobrien#include "magic.h"
3968349Sobrien#include <stdlib.h>
4084685Sobrien#ifdef HAVE_UNISTD_H
4184685Sobrien#include <unistd.h>
4284685Sobrien#endif
4368349Sobrien#include <string.h>
44169942Sobrien#include <assert.h>
4568349Sobrien#include <ctype.h>
46103373Sobrien#include <fcntl.h>
4774784Sobrien#ifdef QUICK
4874784Sobrien#include <sys/mman.h>
4974784Sobrien#endif
50186691Sobrien#include <dirent.h>
5168349Sobrien
5268349Sobrien#define	EATAB {while (isascii((unsigned char) *l) && \
5368349Sobrien		      isspace((unsigned char) *l))  ++l;}
5468349Sobrien#define LOWCASE(l) (isupper((unsigned char) (l)) ? \
5568349Sobrien			tolower((unsigned char) (l)) : (l))
5675937Sobrien/*
5775937Sobrien * Work around a bug in headers on Digital Unix.
5875937Sobrien * At least confirmed for: OSF1 V4.0 878
5975937Sobrien */
6075937Sobrien#if defined(__osf__) && defined(__DECC)
6175937Sobrien#ifdef MAP_FAILED
6275937Sobrien#undef MAP_FAILED
6375937Sobrien#endif
6475937Sobrien#endif
6568349Sobrien
6675937Sobrien#ifndef MAP_FAILED
6775937Sobrien#define MAP_FAILED (void *) -1
6875937Sobrien#endif
6968349Sobrien
7075937Sobrien#ifndef MAP_FILE
7175937Sobrien#define MAP_FILE 0
7275937Sobrien#endif
7375937Sobrien
74159764Sobrienstruct magic_entry {
75159764Sobrien	struct magic *mp;
76159764Sobrien	uint32_t cont_count;
77159764Sobrien	uint32_t max_count;
78159764Sobrien};
79159764Sobrien
80169962Sobrienint file_formats[FILE_NAMES_SIZE];
81169962Sobrienconst size_t file_nformats = FILE_NAMES_SIZE;
82169962Sobrienconst char *file_names[FILE_NAMES_SIZE];
83169962Sobrienconst size_t file_nnames = FILE_NAMES_SIZE;
84169942Sobrien
85169962Sobrienprivate int getvalue(struct magic_set *ms, struct magic *, const char **, int);
86133359Sobrienprivate int hextoint(int);
87192350Sdelphijprivate const char *getstr(struct magic_set *, struct magic *, const char *,
88192350Sdelphij    int);
89159764Sobrienprivate int parse(struct magic_set *, struct magic_entry **, uint32_t *,
90169942Sobrien    const char *, size_t, int);
91159764Sobrienprivate void eatsize(const char **);
92133359Sobrienprivate int apprentice_1(struct magic_set *, const char *, int, struct mlist *);
93159764Sobrienprivate size_t apprentice_magic_strength(const struct magic *);
94159764Sobrienprivate int apprentice_sort(const void *, const void *);
95234449Sobrien#ifndef COMPILE_ONLY
96234449Sobrienprivate void apprentice_list(struct mlist *, int );
97234449Sobrien#endif
98186691Sobrienprivate int apprentice_load(struct magic_set *, struct magic **, uint32_t *,
99133359Sobrien    const char *, int);
100210761Srpaulo#ifndef COMPILE_ONLY
101133359Sobrienprivate void byteswap(struct magic *, uint32_t);
102133359Sobrienprivate void bs1(struct magic *);
103133359Sobrienprivate uint16_t swap2(uint16_t);
104133359Sobrienprivate uint32_t swap4(uint32_t);
105169942Sobrienprivate uint64_t swap8(uint64_t);
106210761Srpaulo#endif
107191771Sobrienprivate char *mkdbname(struct magic_set *, const char *, int);
108210761Srpaulo#ifndef COMPILE_ONLY
109133359Sobrienprivate int apprentice_map(struct magic_set *, struct magic **, uint32_t *,
110133359Sobrien    const char *);
111210761Srpaulo#endif
112133359Sobrienprivate int apprentice_compile(struct magic_set *, struct magic **, uint32_t *,
113133359Sobrien    const char *);
114169942Sobrienprivate int check_format_type(const char *, int);
115139368Sobrienprivate int check_format(struct magic_set *, struct magic *);
116175296Sobrienprivate int get_op(char);
117186691Sobrienprivate int parse_mime(struct magic_set *, struct magic_entry *, const char *);
118191771Sobrienprivate int parse_strength(struct magic_set *, struct magic_entry *, const char *);
119191771Sobrienprivate int parse_apple(struct magic_set *, struct magic_entry *, const char *);
12068349Sobrien
121186691Sobrien
122133359Sobrienprivate size_t maxmagic = 0;
123133359Sobrienprivate size_t magicsize = sizeof(struct magic);
12468349Sobrien
125186691Sobrienprivate const char usg_hdr[] = "cont\toffset\ttype\topcode\tmask\tvalue\tdesc";
126159764Sobrien
127186691Sobrienprivate struct {
128186691Sobrien	const char *name;
129186691Sobrien	size_t len;
130186691Sobrien	int (*fun)(struct magic_set *, struct magic_entry *, const char *);
131186691Sobrien} bang[] = {
132186691Sobrien#define	DECLARE_FIELD(name) { # name, sizeof(# name) - 1, parse_ ## name }
133186691Sobrien	DECLARE_FIELD(mime),
134191771Sobrien	DECLARE_FIELD(apple),
135186691Sobrien	DECLARE_FIELD(strength),
136186691Sobrien#undef	DECLARE_FIELD
137186691Sobrien	{ NULL, 0, NULL }
138186691Sobrien};
139186691Sobrien
14080588Sobrien#ifdef COMPILE_ONLY
14174784Sobrien
142103373Sobrienint main(int, char *[]);
14380588Sobrien
14480588Sobrienint
145103373Sobrienmain(int argc, char *argv[])
14680588Sobrien{
14780588Sobrien	int ret;
148133359Sobrien	struct magic_set *ms;
149133359Sobrien	char *progname;
15080588Sobrien
15180588Sobrien	if ((progname = strrchr(argv[0], '/')) != NULL)
15280588Sobrien		progname++;
15380588Sobrien	else
15480588Sobrien		progname = argv[0];
15580588Sobrien
15680588Sobrien	if (argc != 2) {
157133359Sobrien		(void)fprintf(stderr, "Usage: %s file\n", progname);
158133359Sobrien		return 1;
15980588Sobrien	}
16080588Sobrien
161133359Sobrien	if ((ms = magic_open(MAGIC_CHECK)) == NULL) {
162133359Sobrien		(void)fprintf(stderr, "%s: %s\n", progname, strerror(errno));
163133359Sobrien		return 1;
164133359Sobrien	}
165133359Sobrien	ret = magic_compile(ms, argv[1]) == -1 ? 1 : 0;
166133359Sobrien	if (ret == 1)
167133359Sobrien		(void)fprintf(stderr, "%s: %s\n", progname, magic_error(ms));
168133359Sobrien	magic_close(ms);
169133359Sobrien	return ret;
17080588Sobrien}
17180588Sobrien#endif /* COMPILE_ONLY */
17280588Sobrien
173169962Sobrienstatic const struct type_tbl_s {
174186691Sobrien	const char name[16];
175169962Sobrien	const size_t len;
176169962Sobrien	const int type;
177169962Sobrien	const int format;
178169962Sobrien} type_tbl[] = {
179169962Sobrien# define XX(s)		s, (sizeof(s) - 1)
180186691Sobrien# define XX_NULL	"", 0
181169962Sobrien	{ XX("byte"),		FILE_BYTE,		FILE_FMT_NUM },
182169962Sobrien	{ XX("short"),		FILE_SHORT,		FILE_FMT_NUM },
183169962Sobrien	{ XX("default"),	FILE_DEFAULT,		FILE_FMT_STR },
184169962Sobrien	{ XX("long"),		FILE_LONG,		FILE_FMT_NUM },
185169962Sobrien	{ XX("string"),		FILE_STRING,		FILE_FMT_STR },
186169962Sobrien	{ XX("date"),		FILE_DATE,		FILE_FMT_STR },
187169962Sobrien	{ XX("beshort"),	FILE_BESHORT,		FILE_FMT_NUM },
188169962Sobrien	{ XX("belong"),		FILE_BELONG,		FILE_FMT_NUM },
189169962Sobrien	{ XX("bedate"),		FILE_BEDATE,		FILE_FMT_STR },
190169962Sobrien	{ XX("leshort"),	FILE_LESHORT,		FILE_FMT_NUM },
191169962Sobrien	{ XX("lelong"),		FILE_LELONG,		FILE_FMT_NUM },
192169962Sobrien	{ XX("ledate"),		FILE_LEDATE,		FILE_FMT_STR },
193169962Sobrien	{ XX("pstring"),	FILE_PSTRING,		FILE_FMT_STR },
194169962Sobrien	{ XX("ldate"),		FILE_LDATE,		FILE_FMT_STR },
195169962Sobrien	{ XX("beldate"),	FILE_BELDATE,		FILE_FMT_STR },
196169962Sobrien	{ XX("leldate"),	FILE_LELDATE,		FILE_FMT_STR },
197169962Sobrien	{ XX("regex"),		FILE_REGEX,		FILE_FMT_STR },
198169962Sobrien	{ XX("bestring16"),	FILE_BESTRING16,	FILE_FMT_STR },
199169962Sobrien	{ XX("lestring16"),	FILE_LESTRING16,	FILE_FMT_STR },
200169962Sobrien	{ XX("search"),		FILE_SEARCH,		FILE_FMT_STR },
201169962Sobrien	{ XX("medate"),		FILE_MEDATE,		FILE_FMT_STR },
202169962Sobrien	{ XX("meldate"),	FILE_MELDATE,		FILE_FMT_STR },
203169962Sobrien	{ XX("melong"),		FILE_MELONG,		FILE_FMT_NUM },
204169962Sobrien	{ XX("quad"),		FILE_QUAD,		FILE_FMT_QUAD },
205169962Sobrien	{ XX("lequad"),		FILE_LEQUAD,		FILE_FMT_QUAD },
206169962Sobrien	{ XX("bequad"),		FILE_BEQUAD,		FILE_FMT_QUAD },
207169962Sobrien	{ XX("qdate"),		FILE_QDATE,		FILE_FMT_STR },
208169962Sobrien	{ XX("leqdate"),	FILE_LEQDATE,		FILE_FMT_STR },
209169962Sobrien	{ XX("beqdate"),	FILE_BEQDATE,		FILE_FMT_STR },
210169962Sobrien	{ XX("qldate"),		FILE_QLDATE,		FILE_FMT_STR },
211169962Sobrien	{ XX("leqldate"),	FILE_LEQLDATE,		FILE_FMT_STR },
212169962Sobrien	{ XX("beqldate"),	FILE_BEQLDATE,		FILE_FMT_STR },
213175296Sobrien	{ XX("float"),		FILE_FLOAT,		FILE_FMT_FLOAT },
214175296Sobrien	{ XX("befloat"),	FILE_BEFLOAT,		FILE_FMT_FLOAT },
215175296Sobrien	{ XX("lefloat"),	FILE_LEFLOAT,		FILE_FMT_FLOAT },
216175296Sobrien	{ XX("double"),		FILE_DOUBLE,		FILE_FMT_DOUBLE },
217175296Sobrien	{ XX("bedouble"),	FILE_BEDOUBLE,		FILE_FMT_DOUBLE },
218175296Sobrien	{ XX("ledouble"),	FILE_LEDOUBLE,		FILE_FMT_DOUBLE },
219191771Sobrien	{ XX("leid3"),		FILE_LEID3,		FILE_FMT_NUM },
220191771Sobrien	{ XX("beid3"),		FILE_BEID3,		FILE_FMT_NUM },
221191771Sobrien	{ XX("indirect"),	FILE_INDIRECT,		FILE_FMT_NONE },
222169962Sobrien	{ XX_NULL,		FILE_INVALID,		FILE_FMT_NONE },
223169962Sobrien# undef XX
224169962Sobrien# undef XX_NULL
225169962Sobrien};
22680588Sobrien
227169962Sobrienprivate int
228169962Sobrienget_type(const char *l, const char **t)
229169962Sobrien{
230169962Sobrien	const struct type_tbl_s *p;
231169962Sobrien
232186691Sobrien	for (p = type_tbl; p->len; p++) {
233169962Sobrien		if (strncmp(l, p->name, p->len) == 0) {
234169962Sobrien			if (t)
235169962Sobrien				*t = l + p->len;
236169962Sobrien			break;
237169962Sobrien		}
238169962Sobrien	}
239169962Sobrien	return p->type;
240169962Sobrien}
241169962Sobrien
242169962Sobrienprivate void
243169962Sobrieninit_file_tables(void)
244169962Sobrien{
245169962Sobrien	static int done = 0;
246169962Sobrien	const struct type_tbl_s *p;
247169962Sobrien
248169962Sobrien	if (done)
249169962Sobrien		return;
250169962Sobrien	done++;
251169962Sobrien
252186691Sobrien	for (p = type_tbl; p->len; p++) {
253169962Sobrien		assert(p->type < FILE_NAMES_SIZE);
254169962Sobrien		file_names[p->type] = p->name;
255169962Sobrien		file_formats[p->type] = p->format;
256169962Sobrien	}
257169962Sobrien}
258169962Sobrien
25974784Sobrien/*
260186691Sobrien * Handle one file or directory.
26174784Sobrien */
262133359Sobrienprivate int
263133359Sobrienapprentice_1(struct magic_set *ms, const char *fn, int action,
264133359Sobrien    struct mlist *mlist)
26574784Sobrien{
26674784Sobrien	struct magic *magic = NULL;
267103373Sobrien	uint32_t nmagic = 0;
268210761Srpaulo#ifndef COMPILE_ONLY
26974784Sobrien	struct mlist *ml;
270210761Srpaulo#endif
27174784Sobrien	int rv = -1;
272210761Srpaulo#ifndef COMPILE_ONLY
273133359Sobrien	int mapped;
274210761Srpaulo#endif
27574784Sobrien
276133359Sobrien	if (magicsize != FILE_MAGICSIZE) {
277133359Sobrien		file_error(ms, 0, "magic element size %lu != %lu",
278133359Sobrien		    (unsigned long)sizeof(*magic),
279133359Sobrien		    (unsigned long)FILE_MAGICSIZE);
280133359Sobrien		return -1;
28174784Sobrien	}
282133359Sobrien
283133359Sobrien	if (action == FILE_COMPILE) {
284186691Sobrien		rv = apprentice_load(ms, &magic, &nmagic, fn, action);
285133359Sobrien		if (rv != 0)
286133359Sobrien			return -1;
287133359Sobrien		rv = apprentice_compile(ms, &magic, &nmagic, fn);
288133359Sobrien		free(magic);
289133359Sobrien		return rv;
290133359Sobrien	}
291159764Sobrien
29280588Sobrien#ifndef COMPILE_ONLY
293133359Sobrien	if ((rv = apprentice_map(ms, &magic, &nmagic, fn)) == -1) {
294133359Sobrien		if (ms->flags & MAGIC_CHECK)
295139368Sobrien			file_magwarn(ms, "using regular magic file `%s'", fn);
296186691Sobrien		rv = apprentice_load(ms, &magic, &nmagic, fn, action);
297133359Sobrien		if (rv != 0)
298133359Sobrien			return -1;
299133359Sobrien	}
30074784Sobrien
301133359Sobrien	mapped = rv;
30274784Sobrien
303186691Sobrien	if (magic == NULL) {
304133359Sobrien		file_delmagic(magic, mapped, nmagic);
305133359Sobrien		return -1;
306133359Sobrien	}
307133359Sobrien
308186691Sobrien	if ((ml = CAST(struct mlist *, malloc(sizeof(*ml)))) == NULL) {
309133359Sobrien		file_delmagic(magic, mapped, nmagic);
310169942Sobrien		file_oomem(ms, sizeof(*ml));
311133359Sobrien		return -1;
31274784Sobrien	}
31374784Sobrien
31474784Sobrien	ml->magic = magic;
31574784Sobrien	ml->nmagic = nmagic;
316133359Sobrien	ml->mapped = mapped;
31774784Sobrien
318133359Sobrien	mlist->prev->next = ml;
319133359Sobrien	ml->prev = mlist->prev;
320133359Sobrien	ml->next = mlist;
321133359Sobrien	mlist->prev = ml;
32274784Sobrien
323234449Sobrien	if (action == FILE_LIST) {
324234449Sobrien		printf("Binary patterns:\n");
325234449Sobrien		apprentice_list(mlist, BINTEST);
326234449Sobrien		printf("Text patterns:\n");
327234449Sobrien		apprentice_list(mlist, TEXTTEST);
328234449Sobrien	}
329210761Srpaulo#endif /* COMPILE_ONLY */
330133359Sobrien	return 0;
33174784Sobrien}
33274784Sobrien
333133359Sobrienprotected void
334133359Sobrienfile_delmagic(struct magic *p, int type, size_t entries)
335133359Sobrien{
336133359Sobrien	if (p == NULL)
337133359Sobrien		return;
338133359Sobrien	switch (type) {
339192350Sdelphij	case 2:
340175296Sobrien#ifdef QUICK
341133359Sobrien		p--;
342133359Sobrien		(void)munmap((void *)p, sizeof(*p) * (entries + 1));
343133359Sobrien		break;
344192350Sdelphij#else
345192350Sdelphij		(void)&entries;
346192350Sdelphij		abort();
347192350Sdelphij		/*NOTREACHED*/
348175296Sobrien#endif
349133359Sobrien	case 1:
350133359Sobrien		p--;
351133359Sobrien		/*FALLTHROUGH*/
352133359Sobrien	case 0:
353133359Sobrien		free(p);
354133359Sobrien		break;
355133359Sobrien	default:
356133359Sobrien		abort();
357133359Sobrien	}
358133359Sobrien}
35974784Sobrien
360186691Sobrien/* const char *fn: list of magic files and directories */
361133359Sobrienprotected struct mlist *
362133359Sobrienfile_apprentice(struct magic_set *ms, const char *fn, int action)
36368349Sobrien{
364186691Sobrien	char *p, *mfn;
36568349Sobrien	int file_err, errs = -1;
366133359Sobrien	struct mlist *mlist;
36768349Sobrien
368234449Sobrien	if ((fn = magic_getpath(fn, action)) == NULL)
369234449Sobrien		return NULL;
370234449Sobrien
371169962Sobrien	init_file_tables();
372169962Sobrien
373169962Sobrien	if ((mfn = strdup(fn)) == NULL) {
374169942Sobrien		file_oomem(ms, strlen(fn));
375133359Sobrien		return NULL;
37668349Sobrien	}
377169962Sobrien	fn = mfn;
378133359Sobrien
379186691Sobrien	if ((mlist = CAST(struct mlist *, malloc(sizeof(*mlist)))) == NULL) {
380133359Sobrien		free(mfn);
381169942Sobrien		file_oomem(ms, sizeof(*mlist));
382133359Sobrien		return NULL;
383133359Sobrien	}
384133359Sobrien	mlist->next = mlist->prev = mlist;
385133359Sobrien
38668349Sobrien	while (fn) {
38768349Sobrien		p = strchr(fn, PATHSEP);
38868349Sobrien		if (p)
38968349Sobrien			*p++ = '\0';
390133359Sobrien		if (*fn == '\0')
391133359Sobrien			break;
392133359Sobrien		file_err = apprentice_1(ms, fn, action, mlist);
393186691Sobrien		errs = MAX(errs, file_err);
39468349Sobrien		fn = p;
39568349Sobrien	}
396133359Sobrien	if (errs == -1) {
397133359Sobrien		free(mfn);
398133359Sobrien		free(mlist);
399133359Sobrien		mlist = NULL;
400133359Sobrien		file_error(ms, 0, "could not find any magic files!");
401133359Sobrien		return NULL;
402133359Sobrien	}
40368349Sobrien	free(mfn);
404133359Sobrien	return mlist;
40568349Sobrien}
40668349Sobrien
407169942Sobrien/*
408169942Sobrien * Get weight of this magic entry, for sorting purposes.
409169942Sobrien */
410159764Sobrienprivate size_t
411159764Sobrienapprentice_magic_strength(const struct magic *m)
412159764Sobrien{
413169942Sobrien#define MULT 10
414169942Sobrien	size_t val = 2 * MULT;	/* baseline strength */
415169942Sobrien
416159764Sobrien	switch (m->type) {
417169962Sobrien	case FILE_DEFAULT:	/* make sure this sorts last */
418186691Sobrien		if (m->factor_op != FILE_FACTOR_OP_NONE)
419186691Sobrien			abort();
420169962Sobrien		return 0;
421169962Sobrien
422159764Sobrien	case FILE_BYTE:
423169942Sobrien		val += 1 * MULT;
424169942Sobrien		break;
425159764Sobrien
426159764Sobrien	case FILE_SHORT:
427159764Sobrien	case FILE_LESHORT:
428159764Sobrien	case FILE_BESHORT:
429169942Sobrien		val += 2 * MULT;
430169942Sobrien		break;
431159764Sobrien
432159764Sobrien	case FILE_LONG:
433159764Sobrien	case FILE_LELONG:
434159764Sobrien	case FILE_BELONG:
435159764Sobrien	case FILE_MELONG:
436169942Sobrien		val += 4 * MULT;
437169942Sobrien		break;
438159764Sobrien
439159764Sobrien	case FILE_PSTRING:
440159764Sobrien	case FILE_STRING:
441169942Sobrien		val += m->vallen * MULT;
442169942Sobrien		break;
443169942Sobrien
444159764Sobrien	case FILE_BESTRING16:
445159764Sobrien	case FILE_LESTRING16:
446169942Sobrien		val += m->vallen * MULT / 2;
447169942Sobrien		break;
448169942Sobrien
449159764Sobrien	case FILE_SEARCH:
450169942Sobrien	case FILE_REGEX:
451186691Sobrien		val += m->vallen * MAX(MULT / m->vallen, 1);
452169942Sobrien		break;
453159764Sobrien
454159764Sobrien	case FILE_DATE:
455159764Sobrien	case FILE_LEDATE:
456159764Sobrien	case FILE_BEDATE:
457159764Sobrien	case FILE_MEDATE:
458159764Sobrien	case FILE_LDATE:
459159764Sobrien	case FILE_LELDATE:
460159764Sobrien	case FILE_BELDATE:
461159764Sobrien	case FILE_MELDATE:
462175296Sobrien	case FILE_FLOAT:
463175296Sobrien	case FILE_BEFLOAT:
464175296Sobrien	case FILE_LEFLOAT:
465169942Sobrien		val += 4 * MULT;
466169942Sobrien		break;
467159764Sobrien
468169942Sobrien	case FILE_QUAD:
469169942Sobrien	case FILE_BEQUAD:
470169942Sobrien	case FILE_LEQUAD:
471169942Sobrien	case FILE_QDATE:
472169942Sobrien	case FILE_LEQDATE:
473169942Sobrien	case FILE_BEQDATE:
474169942Sobrien	case FILE_QLDATE:
475169942Sobrien	case FILE_LEQLDATE:
476169942Sobrien	case FILE_BEQLDATE:
477175296Sobrien	case FILE_DOUBLE:
478175296Sobrien	case FILE_BEDOUBLE:
479175296Sobrien	case FILE_LEDOUBLE:
480169942Sobrien		val += 8 * MULT;
481169942Sobrien		break;
482169942Sobrien
483159764Sobrien	default:
484169942Sobrien		val = 0;
485169942Sobrien		(void)fprintf(stderr, "Bad type %d\n", m->type);
486169942Sobrien		abort();
487159764Sobrien	}
488169942Sobrien
489169942Sobrien	switch (m->reln) {
490169942Sobrien	case 'x':	/* matches anything penalize */
491186691Sobrien	case '!':       /* matches almost anything penalize */
492169942Sobrien		val = 0;
493169942Sobrien		break;
494169942Sobrien
495169942Sobrien	case '=':	/* Exact match, prefer */
496169942Sobrien		val += MULT;
497169942Sobrien		break;
498169942Sobrien
499169942Sobrien	case '>':
500169942Sobrien	case '<':	/* comparison match reduce strength */
501169942Sobrien		val -= 2 * MULT;
502169942Sobrien		break;
503169942Sobrien
504169942Sobrien	case '^':
505169942Sobrien	case '&':	/* masking bits, we could count them too */
506169942Sobrien		val -= MULT;
507169942Sobrien		break;
508169942Sobrien
509169942Sobrien	default:
510169942Sobrien		(void)fprintf(stderr, "Bad relation %c\n", m->reln);
511169942Sobrien		abort();
512169942Sobrien	}
513169962Sobrien
514169962Sobrien	if (val == 0)	/* ensure we only return 0 for FILE_DEFAULT */
515169962Sobrien		val = 1;
516169962Sobrien
517186691Sobrien	switch (m->factor_op) {
518186691Sobrien	case FILE_FACTOR_OP_NONE:
519186691Sobrien		break;
520186691Sobrien	case FILE_FACTOR_OP_PLUS:
521186691Sobrien		val += m->factor;
522186691Sobrien		break;
523186691Sobrien	case FILE_FACTOR_OP_MINUS:
524186691Sobrien		val -= m->factor;
525186691Sobrien		break;
526186691Sobrien	case FILE_FACTOR_OP_TIMES:
527186691Sobrien		val *= m->factor;
528186691Sobrien		break;
529186691Sobrien	case FILE_FACTOR_OP_DIV:
530186691Sobrien		val /= m->factor;
531186691Sobrien		break;
532186691Sobrien	default:
533186691Sobrien		abort();
534186691Sobrien	}
535186691Sobrien
536186691Sobrien	/*
537186691Sobrien	 * Magic entries with no description get a bonus because they depend
538186691Sobrien	 * on subsequent magic entries to print something.
539186691Sobrien	 */
540186691Sobrien	if (m->desc[0] == '\0')
541186691Sobrien		val++;
542169942Sobrien	return val;
543159764Sobrien}
544159764Sobrien
545169942Sobrien/*
546169942Sobrien * Sort callback for sorting entries by "strength" (basically length)
547169942Sobrien */
548159764Sobrienprivate int
549159764Sobrienapprentice_sort(const void *a, const void *b)
550159764Sobrien{
551186691Sobrien	const struct magic_entry *ma = CAST(const struct magic_entry *, a);
552186691Sobrien	const struct magic_entry *mb = CAST(const struct magic_entry *, b);
553159764Sobrien	size_t sa = apprentice_magic_strength(ma->mp);
554159764Sobrien	size_t sb = apprentice_magic_strength(mb->mp);
555159764Sobrien	if (sa == sb)
556159764Sobrien		return 0;
557159764Sobrien	else if (sa > sb)
558159764Sobrien		return -1;
559159764Sobrien	else
560159764Sobrien		return 1;
561159764Sobrien}
562159764Sobrien
563234449Sobrien/*
564234449Sobrien * Shows sorted patterns list in the order which is used for the matching
565234449Sobrien */
566234449Sobrien#ifndef COMPILE_ONLY
567186691Sobrienprivate void
568234449Sobrienapprentice_list(struct mlist *mlist, int mode)
569234449Sobrien{
570234449Sobrien	uint32_t magindex = 0;
571234449Sobrien	struct mlist *ml;
572234449Sobrien	for (ml = mlist->next; ml != mlist; ml = ml->next) {
573234449Sobrien		for (magindex = 0; magindex < ml->nmagic; magindex++) {
574234449Sobrien			struct magic *m = &ml->magic[magindex];
575234449Sobrien			if ((m->flag & mode) != mode) {
576234449Sobrien				/* Skip sub-tests */
577234449Sobrien				while (magindex + 1 < ml->nmagic &&
578234449Sobrien				       ml->magic[magindex + 1].cont_level != 0)
579234449Sobrien					++magindex;
580234449Sobrien				continue; /* Skip to next top-level test*/
581234449Sobrien			}
582234449Sobrien
583234449Sobrien			/*
584234449Sobrien			 * Try to iterate over the tree until we find item with
585234449Sobrien			 * description/mimetype.
586234449Sobrien			 */
587234449Sobrien			while (magindex + 1 < ml->nmagic &&
588234449Sobrien			       ml->magic[magindex + 1].cont_level != 0 &&
589234449Sobrien			       *ml->magic[magindex].desc == '\0' &&
590234449Sobrien			       *ml->magic[magindex].mimetype == '\0')
591234449Sobrien				magindex++;
592234449Sobrien
593234449Sobrien			printf("Strength = %3" SIZE_T_FORMAT "u : %s [%s]\n",
594234449Sobrien			    apprentice_magic_strength(m),
595234449Sobrien			    ml->magic[magindex].desc,
596234449Sobrien			    ml->magic[magindex].mimetype);
597234449Sobrien		}
598234449Sobrien	}
599234449Sobrien}
600234449Sobrien#endif /* COMPILE_ONLY */
601234449Sobrien
602234449Sobrienprivate void
603186691Sobrienset_test_type(struct magic *mstart, struct magic *m)
604186691Sobrien{
605186691Sobrien	switch (m->type) {
606186691Sobrien	case FILE_BYTE:
607186691Sobrien	case FILE_SHORT:
608186691Sobrien	case FILE_LONG:
609186691Sobrien	case FILE_DATE:
610186691Sobrien	case FILE_BESHORT:
611186691Sobrien	case FILE_BELONG:
612186691Sobrien	case FILE_BEDATE:
613186691Sobrien	case FILE_LESHORT:
614186691Sobrien	case FILE_LELONG:
615186691Sobrien	case FILE_LEDATE:
616186691Sobrien	case FILE_LDATE:
617186691Sobrien	case FILE_BELDATE:
618186691Sobrien	case FILE_LELDATE:
619186691Sobrien	case FILE_MEDATE:
620186691Sobrien	case FILE_MELDATE:
621186691Sobrien	case FILE_MELONG:
622186691Sobrien	case FILE_QUAD:
623186691Sobrien	case FILE_LEQUAD:
624186691Sobrien	case FILE_BEQUAD:
625186691Sobrien	case FILE_QDATE:
626186691Sobrien	case FILE_LEQDATE:
627186691Sobrien	case FILE_BEQDATE:
628186691Sobrien	case FILE_QLDATE:
629186691Sobrien	case FILE_LEQLDATE:
630186691Sobrien	case FILE_BEQLDATE:
631186691Sobrien	case FILE_FLOAT:
632186691Sobrien	case FILE_BEFLOAT:
633186691Sobrien	case FILE_LEFLOAT:
634186691Sobrien	case FILE_DOUBLE:
635186691Sobrien	case FILE_BEDOUBLE:
636186691Sobrien	case FILE_LEDOUBLE:
637234449Sobrien		mstart->flag |= BINTEST;
638234449Sobrien		break;
639186691Sobrien	case FILE_STRING:
640186691Sobrien	case FILE_PSTRING:
641186691Sobrien	case FILE_BESTRING16:
642186691Sobrien	case FILE_LESTRING16:
643234449Sobrien		/* Allow text overrides */
644234449Sobrien		if (mstart->str_flags & STRING_TEXTTEST)
645234449Sobrien			mstart->flag |= TEXTTEST;
646234449Sobrien		else
647234449Sobrien			mstart->flag |= BINTEST;
648186691Sobrien		break;
649186691Sobrien	case FILE_REGEX:
650186691Sobrien	case FILE_SEARCH:
651234449Sobrien		/* Check for override */
652234449Sobrien		if (mstart->str_flags & STRING_BINTEST)
653234449Sobrien			mstart->flag |= BINTEST;
654234449Sobrien		if (mstart->str_flags & STRING_TEXTTEST)
655234449Sobrien			mstart->flag |= TEXTTEST;
656234449Sobrien
657234449Sobrien		if (mstart->flag & (TEXTTEST|BINTEST))
658234449Sobrien			break;
659234449Sobrien
660186691Sobrien		/* binary test if pattern is not text */
661191771Sobrien		if (file_looks_utf8(m->value.us, (size_t)m->vallen, NULL,
662191771Sobrien		    NULL) <= 0)
663186691Sobrien			mstart->flag |= BINTEST;
664234449Sobrien		else
665234449Sobrien			mstart->flag |= TEXTTEST;
666186691Sobrien		break;
667186691Sobrien	case FILE_DEFAULT:
668186691Sobrien		/* can't deduce anything; we shouldn't see this at the
669186691Sobrien		   top level anyway */
670186691Sobrien		break;
671186691Sobrien	case FILE_INVALID:
672186691Sobrien	default:
673186691Sobrien		/* invalid search type, but no need to complain here */
674186691Sobrien		break;
675186691Sobrien	}
676186691Sobrien}
677186691Sobrien
67874784Sobrien/*
679186691Sobrien * Load and parse one file.
68074784Sobrien */
681186691Sobrienprivate void
682186691Sobrienload_1(struct magic_set *ms, int action, const char *fn, int *errs,
683186691Sobrien   struct magic_entry **marray, uint32_t *marraycount)
684186691Sobrien{
685234449Sobrien	size_t lineno = 0, llen = 0;
686234449Sobrien	char *line = NULL;
687234449Sobrien	ssize_t len;
688234449Sobrien
689186691Sobrien	FILE *f = fopen(ms->file = fn, "r");
690186691Sobrien	if (f == NULL) {
691186691Sobrien		if (errno != ENOENT)
692186691Sobrien			file_error(ms, errno, "cannot read magic file `%s'",
693186691Sobrien				   fn);
694186691Sobrien		(*errs)++;
695234449Sobrien		return;
696234449Sobrien	}
697234449Sobrien
698234449Sobrien	/* read and parse this file */
699234449Sobrien	for (ms->line = 1; (len = getline(&line, &llen, f)) != -1;
700234449Sobrien	    ms->line++) {
701234449Sobrien		if (len == 0) /* null line, garbage, etc */
702234449Sobrien			continue;
703234449Sobrien		if (line[len - 1] == '\n') {
704234449Sobrien			lineno++;
705234449Sobrien			line[len - 1] = '\0'; /* delete newline */
706234449Sobrien		}
707234449Sobrien		switch (line[0]) {
708234449Sobrien		case '\0':	/* empty, do not parse */
709234449Sobrien		case '#':	/* comment, do not parse */
710234449Sobrien			continue;
711234449Sobrien		case '!':
712234449Sobrien			if (line[1] == ':') {
713186691Sobrien				size_t i;
714186691Sobrien
715186691Sobrien				for (i = 0; bang[i].name != NULL; i++) {
716234449Sobrien					if ((size_t)(len - 2) > bang[i].len &&
717186691Sobrien					    memcmp(bang[i].name, line + 2,
718186691Sobrien					    bang[i].len) == 0)
719186691Sobrien						break;
720186691Sobrien				}
721186691Sobrien				if (bang[i].name == NULL) {
722186691Sobrien					file_error(ms, 0,
723186691Sobrien					    "Unknown !: entry `%s'", line);
724186691Sobrien					(*errs)++;
725186691Sobrien					continue;
726186691Sobrien				}
727186691Sobrien				if (*marraycount == 0) {
728186691Sobrien					file_error(ms, 0,
729186691Sobrien					    "No current entry for :!%s type",
730186691Sobrien						bang[i].name);
731186691Sobrien					(*errs)++;
732186691Sobrien					continue;
733186691Sobrien				}
734186691Sobrien				if ((*bang[i].fun)(ms,
735186691Sobrien				    &(*marray)[*marraycount - 1],
736186691Sobrien				    line + bang[i].len + 2) != 0) {
737186691Sobrien					(*errs)++;
738186691Sobrien					continue;
739186691Sobrien				}
740186691Sobrien				continue;
741186691Sobrien			}
742234449Sobrien			/*FALLTHROUGH*/
743234449Sobrien		default:
744186691Sobrien			if (parse(ms, marray, marraycount, line, lineno,
745186691Sobrien			    action) != 0)
746186691Sobrien				(*errs)++;
747234449Sobrien			break;
748186691Sobrien		}
749186691Sobrien	}
750234449Sobrien	free(line);
751234449Sobrien	(void)fclose(f);
752186691Sobrien}
753186691Sobrien
754186691Sobrien/*
755186691Sobrien * parse a file or directory of files
756186691Sobrien * const char *fn: name of magic file or directory
757186691Sobrien */
758133359Sobrienprivate int
759234449Sobriencmpstrp(const void *p1, const void *p2)
760234449Sobrien{
761234449Sobrien        return strcmp(*(char *const *)p1, *(char *const *)p2);
762234449Sobrien}
763234449Sobrien
764234449Sobrienprivate int
765186691Sobrienapprentice_load(struct magic_set *ms, struct magic **magicp, uint32_t *nmagicp,
766133359Sobrien    const char *fn, int action)
76768349Sobrien{
76868349Sobrien	int errs = 0;
769159764Sobrien	struct magic_entry *marray;
770186691Sobrien	uint32_t marraycount, i, mentrycount = 0, starttest;
771234449Sobrien	size_t slen, files = 0, maxfiles = 0;
772234449Sobrien	char **filearr = NULL, *mfn;
773186691Sobrien	struct stat st;
774186691Sobrien	DIR *dir;
775186691Sobrien	struct dirent *d;
77668349Sobrien
777169942Sobrien	ms->flags |= MAGIC_CHECK;	/* Enable checks for parsed files */
778169942Sobrien
779169942Sobrien        maxmagic = MAXMAGIS;
780186691Sobrien	if ((marray = CAST(struct magic_entry *, calloc(maxmagic,
781186691Sobrien	    sizeof(*marray)))) == NULL) {
782169942Sobrien		file_oomem(ms, maxmagic * sizeof(*marray));
783133359Sobrien		return -1;
78474784Sobrien	}
785159764Sobrien	marraycount = 0;
78674784Sobrien
787133359Sobrien	/* print silly verbose header for USG compat. */
788133359Sobrien	if (action == FILE_CHECK)
789186691Sobrien		(void)fprintf(stderr, "%s\n", usg_hdr);
790133359Sobrien
791186691Sobrien	/* load directory or file */
792186691Sobrien	if (stat(fn, &st) == 0 && S_ISDIR(st.st_mode)) {
793186691Sobrien		dir = opendir(fn);
794234449Sobrien		if (!dir) {
795234449Sobrien			errs++;
796234449Sobrien			goto out;
797234449Sobrien		}
798234449Sobrien		while ((d = readdir(dir)) != NULL) {
799234449Sobrien			if (asprintf(&mfn, "%s/%s", fn, d->d_name) < 0) {
800234449Sobrien				file_oomem(ms,
801234449Sobrien				    strlen(fn) + strlen(d->d_name) + 2);
802234449Sobrien				errs++;
803234449Sobrien				closedir(dir);
804234449Sobrien				goto out;
805234449Sobrien			}
806234449Sobrien			if (stat(mfn, &st) == -1 || !S_ISREG(st.st_mode)) {
807234449Sobrien				free(mfn);
808234449Sobrien				continue;
809234449Sobrien			}
810234449Sobrien			if (files >= maxfiles) {
811234449Sobrien				size_t mlen;
812234449Sobrien				maxfiles = (maxfiles + 1) * 2;
813234449Sobrien				mlen = maxfiles * sizeof(*filearr);
814234449Sobrien				if ((filearr = CAST(char **,
815234449Sobrien				    realloc(filearr, mlen))) == NULL) {
816234449Sobrien					file_oomem(ms, mlen);
817234449Sobrien					free(mfn);
818234449Sobrien					closedir(dir);
819234449Sobrien					errs++;
820234449Sobrien					goto out;
821186691Sobrien				}
822186691Sobrien			}
823234449Sobrien			filearr[files++] = mfn;
824234449Sobrien		}
825234449Sobrien		closedir(dir);
826234449Sobrien		qsort(filearr, files, sizeof(*filearr), cmpstrp);
827234449Sobrien		for (i = 0; i < files; i++) {
828234449Sobrien			load_1(ms, action, filearr[i], &errs, &marray,
829234449Sobrien			    &marraycount);
830234449Sobrien			free(filearr[i]);
831234449Sobrien		}
832234449Sobrien		free(filearr);
833186691Sobrien	} else
834186691Sobrien		load_1(ms, action, fn, &errs, &marray, &marraycount);
835186691Sobrien	if (errs)
836186691Sobrien		goto out;
837186691Sobrien
838186691Sobrien	/* Set types of tests */
839186691Sobrien	for (i = 0; i < marraycount; ) {
840186691Sobrien		if (marray[i].mp->cont_level != 0) {
841186691Sobrien			i++;
84268349Sobrien			continue;
843169942Sobrien		}
844186691Sobrien
845186691Sobrien		starttest = i;
846186691Sobrien		do {
847186691Sobrien			static const char text[] = "text";
848186691Sobrien			static const char binary[] = "binary";
849186691Sobrien			static const size_t len = sizeof(text);
850186691Sobrien			set_test_type(marray[starttest].mp, marray[i].mp);
851186691Sobrien			if ((ms->flags & MAGIC_DEBUG) == 0)
852186691Sobrien				continue;
853186691Sobrien			(void)fprintf(stderr, "%s%s%s: %s\n",
854186691Sobrien			    marray[i].mp->mimetype,
855186691Sobrien			    marray[i].mp->mimetype[0] == '\0' ? "" : "; ",
856186691Sobrien			    marray[i].mp->desc[0] ? marray[i].mp->desc :
857186691Sobrien			    "(no description)",
858186691Sobrien			    marray[i].mp->flag & BINTEST ? binary : text);
859186691Sobrien			if (marray[i].mp->flag & BINTEST) {
860186691Sobrien				char *p = strstr(marray[i].mp->desc, text);
861186691Sobrien				if (p && (p == marray[i].mp->desc ||
862186691Sobrien				    isspace((unsigned char)p[-1])) &&
863186691Sobrien				    (p + len - marray[i].mp->desc ==
864186691Sobrien				    MAXstring || (p[len] == '\0' ||
865186691Sobrien				    isspace((unsigned char)p[len]))))
866186691Sobrien					(void)fprintf(stderr, "*** Possible "
867186691Sobrien					    "binary test for text type\n");
868186691Sobrien			}
869186691Sobrien		} while (++i < marraycount && marray[i].mp->cont_level != 0);
87068349Sobrien	}
87168349Sobrien
872186691Sobrien	qsort(marray, marraycount, sizeof(*marray), apprentice_sort);
873159764Sobrien
874169962Sobrien	/*
875169962Sobrien	 * Make sure that any level 0 "default" line is last (if one exists).
876169962Sobrien	 */
877169962Sobrien	for (i = 0; i < marraycount; i++) {
878169962Sobrien		if (marray[i].mp->cont_level == 0 &&
879169962Sobrien		    marray[i].mp->type == FILE_DEFAULT) {
880169962Sobrien			while (++i < marraycount)
881169962Sobrien				if (marray[i].mp->cont_level == 0)
882169962Sobrien					break;
883169962Sobrien			if (i != marraycount) {
884234449Sobrien				/* XXX - Ugh! */
885234449Sobrien				ms->line = marray[i].mp->lineno;
886169962Sobrien				file_magwarn(ms,
887169962Sobrien				    "level 0 \"default\" did not sort last");
888169962Sobrien			}
889169962Sobrien			break;
890169962Sobrien		}
891169962Sobrien	}
892159764Sobrien
893169942Sobrien	for (i = 0; i < marraycount; i++)
894159764Sobrien		mentrycount += marray[i].cont_count;
895159764Sobrien
896186691Sobrien	slen = sizeof(**magicp) * mentrycount;
897186691Sobrien	if ((*magicp = CAST(struct magic *, malloc(slen))) == NULL) {
898186691Sobrien		file_oomem(ms, slen);
899159764Sobrien		errs++;
900159764Sobrien		goto out;
901159764Sobrien	}
902159764Sobrien
903159764Sobrien	mentrycount = 0;
904159764Sobrien	for (i = 0; i < marraycount; i++) {
905159764Sobrien		(void)memcpy(*magicp + mentrycount, marray[i].mp,
906159764Sobrien		    marray[i].cont_count * sizeof(**magicp));
907159764Sobrien		mentrycount += marray[i].cont_count;
908159764Sobrien	}
909159764Sobrienout:
910159764Sobrien	for (i = 0; i < marraycount; i++)
911159764Sobrien		free(marray[i].mp);
912159764Sobrien	free(marray);
91374784Sobrien	if (errs) {
91474784Sobrien		*magicp = NULL;
91574784Sobrien		*nmagicp = 0;
916159764Sobrien		return errs;
917159764Sobrien	} else {
918159764Sobrien		*nmagicp = mentrycount;
919159764Sobrien		return 0;
92074784Sobrien	}
921159764Sobrien
92268349Sobrien}
92368349Sobrien
92468349Sobrien/*
92568349Sobrien * extend the sign bit if the comparison is to be signed
92668349Sobrien */
927169942Sobrienprotected uint64_t
928169942Sobrienfile_signextend(struct magic_set *ms, struct magic *m, uint64_t v)
92968349Sobrien{
930169962Sobrien	if (!(m->flag & UNSIGNED)) {
93168349Sobrien		switch(m->type) {
93268349Sobrien		/*
93368349Sobrien		 * Do not remove the casts below.  They are
93468349Sobrien		 * vital.  When later compared with the data,
93568349Sobrien		 * the sign extension must have happened.
93668349Sobrien		 */
937133359Sobrien		case FILE_BYTE:
93868349Sobrien			v = (char) v;
93968349Sobrien			break;
940133359Sobrien		case FILE_SHORT:
941133359Sobrien		case FILE_BESHORT:
942133359Sobrien		case FILE_LESHORT:
94368349Sobrien			v = (short) v;
94468349Sobrien			break;
945133359Sobrien		case FILE_DATE:
946133359Sobrien		case FILE_BEDATE:
947133359Sobrien		case FILE_LEDATE:
948159764Sobrien		case FILE_MEDATE:
949133359Sobrien		case FILE_LDATE:
950133359Sobrien		case FILE_BELDATE:
951133359Sobrien		case FILE_LELDATE:
952159764Sobrien		case FILE_MELDATE:
953133359Sobrien		case FILE_LONG:
954133359Sobrien		case FILE_BELONG:
955133359Sobrien		case FILE_LELONG:
956159764Sobrien		case FILE_MELONG:
957175296Sobrien		case FILE_FLOAT:
958175296Sobrien		case FILE_BEFLOAT:
959175296Sobrien		case FILE_LEFLOAT:
960103373Sobrien			v = (int32_t) v;
96168349Sobrien			break;
962169942Sobrien		case FILE_QUAD:
963169942Sobrien		case FILE_BEQUAD:
964169942Sobrien		case FILE_LEQUAD:
965169942Sobrien		case FILE_QDATE:
966169942Sobrien		case FILE_QLDATE:
967169942Sobrien		case FILE_BEQDATE:
968169942Sobrien		case FILE_BEQLDATE:
969169942Sobrien		case FILE_LEQDATE:
970169942Sobrien		case FILE_LEQLDATE:
971175296Sobrien		case FILE_DOUBLE:
972175296Sobrien		case FILE_BEDOUBLE:
973175296Sobrien		case FILE_LEDOUBLE:
974169942Sobrien			v = (int64_t) v;
975169942Sobrien			break;
976133359Sobrien		case FILE_STRING:
977133359Sobrien		case FILE_PSTRING:
978139368Sobrien		case FILE_BESTRING16:
979139368Sobrien		case FILE_LESTRING16:
980133359Sobrien		case FILE_REGEX:
981159764Sobrien		case FILE_SEARCH:
982169962Sobrien		case FILE_DEFAULT:
983191771Sobrien		case FILE_INDIRECT:
984103373Sobrien			break;
98568349Sobrien		default:
986133359Sobrien			if (ms->flags & MAGIC_CHECK)
987139368Sobrien			    file_magwarn(ms, "cannot happen: m->type=%d\n",
988133359Sobrien				    m->type);
989133359Sobrien			return ~0U;
99068349Sobrien		}
991169962Sobrien	}
99268349Sobrien	return v;
99368349Sobrien}
99468349Sobrien
995169962Sobrienprivate int
996186691Sobrienstring_modifier_check(struct magic_set *ms, struct magic *m)
997169962Sobrien{
998169962Sobrien	if ((ms->flags & MAGIC_CHECK) == 0)
999169962Sobrien		return 0;
1000169962Sobrien
1001234449Sobrien	if (m->type != FILE_PSTRING && (m->str_flags & PSTRING_LEN) != 0) {
1002234449Sobrien		file_magwarn(ms,
1003234449Sobrien		    "'/BHhLl' modifiers are only allowed for pascal strings\n");
1004234449Sobrien		return -1;
1005234449Sobrien	}
1006169962Sobrien	switch (m->type) {
1007169962Sobrien	case FILE_BESTRING16:
1008169962Sobrien	case FILE_LESTRING16:
1009169962Sobrien		if (m->str_flags != 0) {
1010186691Sobrien			file_magwarn(ms,
1011186691Sobrien			    "no modifiers allowed for 16-bit strings\n");
1012169962Sobrien			return -1;
1013169962Sobrien		}
1014169962Sobrien		break;
1015169962Sobrien	case FILE_STRING:
1016169962Sobrien	case FILE_PSTRING:
1017169962Sobrien		if ((m->str_flags & REGEX_OFFSET_START) != 0) {
1018186691Sobrien			file_magwarn(ms,
1019186691Sobrien			    "'/%c' only allowed on regex and search\n",
1020169962Sobrien			    CHAR_REGEX_OFFSET_START);
1021169962Sobrien			return -1;
1022169962Sobrien		}
1023169962Sobrien		break;
1024169962Sobrien	case FILE_SEARCH:
1025186691Sobrien		if (m->str_range == 0) {
1026186691Sobrien			file_magwarn(ms,
1027186691Sobrien			    "missing range; defaulting to %d\n",
1028186691Sobrien                            STRING_DEFAULT_RANGE);
1029186691Sobrien			m->str_range = STRING_DEFAULT_RANGE;
1030186691Sobrien			return -1;
1031186691Sobrien		}
1032169962Sobrien		break;
1033169962Sobrien	case FILE_REGEX:
1034234449Sobrien		if ((m->str_flags & STRING_COMPACT_WHITESPACE) != 0) {
1035169962Sobrien			file_magwarn(ms, "'/%c' not allowed on regex\n",
1036234449Sobrien			    CHAR_COMPACT_WHITESPACE);
1037169962Sobrien			return -1;
1038169962Sobrien		}
1039234449Sobrien		if ((m->str_flags & STRING_COMPACT_OPTIONAL_WHITESPACE) != 0) {
1040169962Sobrien			file_magwarn(ms, "'/%c' not allowed on regex\n",
1041234449Sobrien			    CHAR_COMPACT_OPTIONAL_WHITESPACE);
1042169962Sobrien			return -1;
1043169962Sobrien		}
1044169962Sobrien		break;
1045169962Sobrien	default:
1046169962Sobrien		file_magwarn(ms, "coding error: m->type=%d\n",
1047169962Sobrien		    m->type);
1048169962Sobrien		return -1;
1049169962Sobrien	}
1050169962Sobrien	return 0;
1051169962Sobrien}
1052169962Sobrien
1053169962Sobrienprivate int
1054169962Sobrienget_op(char c)
1055169962Sobrien{
1056169962Sobrien	switch (c) {
1057169962Sobrien	case '&':
1058169962Sobrien		return FILE_OPAND;
1059169962Sobrien	case '|':
1060169962Sobrien		return FILE_OPOR;
1061169962Sobrien	case '^':
1062169962Sobrien		return FILE_OPXOR;
1063169962Sobrien	case '+':
1064169962Sobrien		return FILE_OPADD;
1065169962Sobrien	case '-':
1066169962Sobrien		return FILE_OPMINUS;
1067169962Sobrien	case '*':
1068169962Sobrien		return FILE_OPMULTIPLY;
1069169962Sobrien	case '/':
1070169962Sobrien		return FILE_OPDIVIDE;
1071169962Sobrien	case '%':
1072169962Sobrien		return FILE_OPMODULO;
1073169962Sobrien	default:
1074169962Sobrien		return -1;
1075169962Sobrien	}
1076169962Sobrien}
1077169962Sobrien
1078169962Sobrien#ifdef ENABLE_CONDITIONALS
1079169962Sobrienprivate int
1080169962Sobrienget_cond(const char *l, const char **t)
1081169962Sobrien{
1082186691Sobrien	static const struct cond_tbl_s {
1083186691Sobrien		char name[8];
1084186691Sobrien		size_t len;
1085186691Sobrien		int cond;
1086169962Sobrien	} cond_tbl[] = {
1087169962Sobrien		{ "if",		2,	COND_IF },
1088169962Sobrien		{ "elif",	4,	COND_ELIF },
1089169962Sobrien		{ "else",	4,	COND_ELSE },
1090186691Sobrien		{ "",		0,	COND_NONE },
1091169962Sobrien	};
1092186691Sobrien	const struct cond_tbl_s *p;
1093169962Sobrien
1094186691Sobrien	for (p = cond_tbl; p->len; p++) {
1095169962Sobrien		if (strncmp(l, p->name, p->len) == 0 &&
1096169962Sobrien		    isspace((unsigned char)l[p->len])) {
1097169962Sobrien			if (t)
1098169962Sobrien				*t = l + p->len;
1099169962Sobrien			break;
1100169962Sobrien		}
1101169962Sobrien	}
1102169962Sobrien	return p->cond;
1103169962Sobrien}
1104169962Sobrien
1105169962Sobrienprivate int
1106169962Sobriencheck_cond(struct magic_set *ms, int cond, uint32_t cont_level)
1107169962Sobrien{
1108169962Sobrien	int last_cond;
1109169962Sobrien	last_cond = ms->c.li[cont_level].last_cond;
1110169962Sobrien
1111169962Sobrien	switch (cond) {
1112169962Sobrien	case COND_IF:
1113169962Sobrien		if (last_cond != COND_NONE && last_cond != COND_ELIF) {
1114169962Sobrien			if (ms->flags & MAGIC_CHECK)
1115169962Sobrien				file_magwarn(ms, "syntax error: `if'");
1116169962Sobrien			return -1;
1117169962Sobrien		}
1118169962Sobrien		last_cond = COND_IF;
1119169962Sobrien		break;
1120169962Sobrien
1121169962Sobrien	case COND_ELIF:
1122169962Sobrien		if (last_cond != COND_IF && last_cond != COND_ELIF) {
1123169962Sobrien			if (ms->flags & MAGIC_CHECK)
1124169962Sobrien				file_magwarn(ms, "syntax error: `elif'");
1125169962Sobrien			return -1;
1126169962Sobrien		}
1127169962Sobrien		last_cond = COND_ELIF;
1128169962Sobrien		break;
1129169962Sobrien
1130169962Sobrien	case COND_ELSE:
1131169962Sobrien		if (last_cond != COND_IF && last_cond != COND_ELIF) {
1132169962Sobrien			if (ms->flags & MAGIC_CHECK)
1133169962Sobrien				file_magwarn(ms, "syntax error: `else'");
1134169962Sobrien			return -1;
1135169962Sobrien		}
1136169962Sobrien		last_cond = COND_NONE;
1137169962Sobrien		break;
1138169962Sobrien
1139169962Sobrien	case COND_NONE:
1140169962Sobrien		last_cond = COND_NONE;
1141169962Sobrien		break;
1142169962Sobrien	}
1143169962Sobrien
1144169962Sobrien	ms->c.li[cont_level].last_cond = last_cond;
1145169962Sobrien	return 0;
1146169962Sobrien}
1147169962Sobrien#endif /* ENABLE_CONDITIONALS */
1148169962Sobrien
114968349Sobrien/*
115068349Sobrien * parse one line from magic file, put into magic[index++] if valid
115168349Sobrien */
1152133359Sobrienprivate int
1153159764Sobrienparse(struct magic_set *ms, struct magic_entry **mentryp, uint32_t *nmentryp,
1154169942Sobrien    const char *line, size_t lineno, int action)
115568349Sobrien{
1156169962Sobrien#ifdef ENABLE_CONDITIONALS
1157169962Sobrien	static uint32_t last_cont_level = 0;
1158169962Sobrien#endif
1159169942Sobrien	size_t i;
1160159764Sobrien	struct magic_entry *me;
116168349Sobrien	struct magic *m;
1162159764Sobrien	const char *l = line;
116384685Sobrien	char *t;
1164169962Sobrien	int op;
1165159825Sobrien	uint32_t cont_level;
116668349Sobrien
1167159764Sobrien	cont_level = 0;
116868349Sobrien
116968349Sobrien	while (*l == '>') {
117068349Sobrien		++l;		/* step over */
1171159764Sobrien		cont_level++;
117268349Sobrien	}
1173169962Sobrien#ifdef ENABLE_CONDITIONALS
1174169962Sobrien	if (cont_level == 0 || cont_level > last_cont_level)
1175169962Sobrien		if (file_check_mem(ms, cont_level) == -1)
1176169962Sobrien			return -1;
1177169962Sobrien	last_cont_level = cont_level;
1178169962Sobrien#endif
117968349Sobrien
1180159764Sobrien#define ALLOC_CHUNK	(size_t)10
1181159764Sobrien#define ALLOC_INCR	(size_t)200
1182159764Sobrien
1183159764Sobrien	if (cont_level != 0) {
1184159764Sobrien		if (*nmentryp == 0) {
1185159764Sobrien			file_error(ms, 0, "No current entry for continuation");
1186159764Sobrien			return -1;
1187159764Sobrien		}
1188159764Sobrien		me = &(*mentryp)[*nmentryp - 1];
1189159764Sobrien		if (me->cont_count == me->max_count) {
1190159764Sobrien			struct magic *nm;
1191159764Sobrien			size_t cnt = me->max_count + ALLOC_CHUNK;
1192186691Sobrien			if ((nm = CAST(struct magic *, realloc(me->mp,
1193186691Sobrien			    sizeof(*nm) * cnt))) == NULL) {
1194169942Sobrien				file_oomem(ms, sizeof(*nm) * cnt);
1195159764Sobrien				return -1;
1196159764Sobrien			}
1197159764Sobrien			me->mp = m = nm;
1198234449Sobrien			me->max_count = CAST(uint32_t, cnt);
1199159764Sobrien		}
1200159764Sobrien		m = &me->mp[me->cont_count++];
1201169962Sobrien		(void)memset(m, 0, sizeof(*m));
1202159764Sobrien		m->cont_level = cont_level;
1203159764Sobrien	} else {
1204159764Sobrien		if (*nmentryp == maxmagic) {
1205159764Sobrien			struct magic_entry *mp;
1206159764Sobrien
1207159764Sobrien			maxmagic += ALLOC_INCR;
1208186691Sobrien			if ((mp = CAST(struct magic_entry *,
1209186691Sobrien			    realloc(*mentryp, sizeof(*mp) * maxmagic))) ==
1210159764Sobrien			    NULL) {
1211169942Sobrien				file_oomem(ms, sizeof(*mp) * maxmagic);
1212159764Sobrien				return -1;
1213159764Sobrien			}
1214159764Sobrien			(void)memset(&mp[*nmentryp], 0, sizeof(*mp) *
1215159764Sobrien			    ALLOC_INCR);
1216159764Sobrien			*mentryp = mp;
1217159764Sobrien		}
1218159764Sobrien		me = &(*mentryp)[*nmentryp];
1219159764Sobrien		if (me->mp == NULL) {
1220186691Sobrien			size_t len = sizeof(*m) * ALLOC_CHUNK;
1221186691Sobrien			if ((m = CAST(struct magic *, malloc(len))) == NULL) {
1222186691Sobrien				file_oomem(ms, len);
1223159764Sobrien				return -1;
1224159764Sobrien			}
1225159764Sobrien			me->mp = m;
1226159764Sobrien			me->max_count = ALLOC_CHUNK;
1227159764Sobrien		} else
1228159764Sobrien			m = me->mp;
1229169962Sobrien		(void)memset(m, 0, sizeof(*m));
1230186691Sobrien		m->factor_op = FILE_FACTOR_OP_NONE;
1231159764Sobrien		m->cont_level = 0;
1232159764Sobrien		me->cont_count = 1;
1233159764Sobrien	}
1234234449Sobrien	m->lineno = CAST(uint32_t, lineno);
1235159764Sobrien
1236169962Sobrien	if (*l == '&') {  /* m->cont_level == 0 checked below. */
1237159764Sobrien                ++l;            /* step over */
1238159764Sobrien                m->flag |= OFFADD;
1239159764Sobrien        }
1240169962Sobrien	if (*l == '(') {
124168349Sobrien		++l;		/* step over */
124268349Sobrien		m->flag |= INDIR;
1243159764Sobrien		if (m->flag & OFFADD)
1244159764Sobrien			m->flag = (m->flag & ~OFFADD) | INDIROFFADD;
1245169962Sobrien
1246169962Sobrien		if (*l == '&') {  /* m->cont_level == 0 checked below */
1247169962Sobrien			++l;            /* step over */
1248169962Sobrien			m->flag |= OFFADD;
1249169962Sobrien		}
125068349Sobrien	}
1251169962Sobrien	/* Indirect offsets are not valid at level 0. */
1252169962Sobrien	if (m->cont_level == 0 && (m->flag & (OFFADD | INDIROFFADD)))
1253169962Sobrien		if (ms->flags & MAGIC_CHECK)
1254169962Sobrien			file_magwarn(ms, "relative offset at level 0");
125568349Sobrien
125668349Sobrien	/* get offset, then skip over it */
1257133359Sobrien	m->offset = (uint32_t)strtoul(l, &t, 0);
125868349Sobrien        if (l == t)
1259133359Sobrien		if (ms->flags & MAGIC_CHECK)
1260139368Sobrien			file_magwarn(ms, "offset `%s' invalid", l);
126168349Sobrien        l = t;
126268349Sobrien
126368349Sobrien	if (m->flag & INDIR) {
1264133359Sobrien		m->in_type = FILE_LONG;
126574784Sobrien		m->in_offset = 0;
126668349Sobrien		/*
126768349Sobrien		 * read [.lbs][+-]nnnnn)
126868349Sobrien		 */
126968349Sobrien		if (*l == '.') {
127068349Sobrien			l++;
127168349Sobrien			switch (*l) {
127268349Sobrien			case 'l':
1273133359Sobrien				m->in_type = FILE_LELONG;
127468349Sobrien				break;
127568349Sobrien			case 'L':
1276133359Sobrien				m->in_type = FILE_BELONG;
127768349Sobrien				break;
1278159764Sobrien			case 'm':
1279159764Sobrien				m->in_type = FILE_MELONG;
1280159764Sobrien				break;
128168349Sobrien			case 'h':
128268349Sobrien			case 's':
1283133359Sobrien				m->in_type = FILE_LESHORT;
128468349Sobrien				break;
128568349Sobrien			case 'H':
128668349Sobrien			case 'S':
1287133359Sobrien				m->in_type = FILE_BESHORT;
128868349Sobrien				break;
128968349Sobrien			case 'c':
129068349Sobrien			case 'b':
129168349Sobrien			case 'C':
129268349Sobrien			case 'B':
1293133359Sobrien				m->in_type = FILE_BYTE;
129468349Sobrien				break;
1295175296Sobrien			case 'e':
1296175296Sobrien			case 'f':
1297175296Sobrien			case 'g':
1298175296Sobrien				m->in_type = FILE_LEDOUBLE;
1299175296Sobrien				break;
1300175296Sobrien			case 'E':
1301175296Sobrien			case 'F':
1302175296Sobrien			case 'G':
1303175296Sobrien				m->in_type = FILE_BEDOUBLE;
1304175296Sobrien				break;
1305191771Sobrien			case 'i':
1306191771Sobrien				m->in_type = FILE_LEID3;
1307191771Sobrien				break;
1308191771Sobrien			case 'I':
1309191771Sobrien				m->in_type = FILE_BEID3;
1310191771Sobrien				break;
131168349Sobrien			default:
1312133359Sobrien				if (ms->flags & MAGIC_CHECK)
1313139368Sobrien					file_magwarn(ms,
1314139368Sobrien					    "indirect offset type `%c' invalid",
1315133359Sobrien					    *l);
131668349Sobrien				break;
131768349Sobrien			}
131868349Sobrien			l++;
131968349Sobrien		}
1320169962Sobrien
1321169962Sobrien		m->in_op = 0;
132280588Sobrien		if (*l == '~') {
1323159764Sobrien			m->in_op |= FILE_OPINVERSE;
132480588Sobrien			l++;
132580588Sobrien		}
1326169962Sobrien		if ((op = get_op(*l)) != -1) {
1327169962Sobrien			m->in_op |= op;
132880588Sobrien			l++;
132980588Sobrien		}
1330159764Sobrien		if (*l == '(') {
1331159764Sobrien			m->in_op |= FILE_OPINDIRECT;
1332159764Sobrien			l++;
1333159764Sobrien		}
1334159764Sobrien		if (isdigit((unsigned char)*l) || *l == '-') {
1335159764Sobrien			m->in_offset = (int32_t)strtol(l, &t, 0);
1336169962Sobrien			if (l == t)
1337169962Sobrien				if (ms->flags & MAGIC_CHECK)
1338169962Sobrien					file_magwarn(ms,
1339169962Sobrien					    "in_offset `%s' invalid", l);
1340159764Sobrien			l = t;
1341159764Sobrien		}
1342159764Sobrien		if (*l++ != ')' ||
1343159764Sobrien		    ((m->in_op & FILE_OPINDIRECT) && *l++ != ')'))
1344133359Sobrien			if (ms->flags & MAGIC_CHECK)
1345139368Sobrien				file_magwarn(ms,
1346139368Sobrien				    "missing ')' in indirect offset");
134768349Sobrien	}
1348169962Sobrien	EATAB;
134968349Sobrien
1350169962Sobrien#ifdef ENABLE_CONDITIONALS
1351169962Sobrien	m->cond = get_cond(l, &l);
1352169962Sobrien	if (check_cond(ms, m->cond, cont_level) == -1)
1353169962Sobrien		return -1;
135468349Sobrien
135568349Sobrien	EATAB;
1356169962Sobrien#endif
135768349Sobrien
135868349Sobrien	if (*l == 'u') {
135968349Sobrien		++l;
136068349Sobrien		m->flag |= UNSIGNED;
136168349Sobrien	}
136268349Sobrien
1363169962Sobrien	m->type = get_type(l, &l);
1364169962Sobrien	if (m->type == FILE_INVALID) {
1365133359Sobrien		if (ms->flags & MAGIC_CHECK)
1366139368Sobrien			file_magwarn(ms, "type `%s' invalid", l);
136768349Sobrien		return -1;
136868349Sobrien	}
1369169962Sobrien
137068349Sobrien	/* New-style anding: "0 byte&0x80 =0x80 dynamically linked" */
137180588Sobrien	/* New and improved: ~ & | ^ + - * / % -- exciting, isn't it? */
1372169962Sobrien
1373169962Sobrien	m->mask_op = 0;
137480588Sobrien	if (*l == '~') {
1375139368Sobrien		if (!IS_STRING(m->type))
1376159764Sobrien			m->mask_op |= FILE_OPINVERSE;
1377169962Sobrien		else if (ms->flags & MAGIC_CHECK)
1378169962Sobrien			file_magwarn(ms, "'~' invalid for string types");
137968349Sobrien		++l;
138080588Sobrien	}
1381186691Sobrien	m->str_range = 0;
1382234449Sobrien	m->str_flags = m->type == FILE_PSTRING ? PSTRING_1_LE : 0;
1383169962Sobrien	if ((op = get_op(*l)) != -1) {
1384169962Sobrien		if (!IS_STRING(m->type)) {
1385169962Sobrien			uint64_t val;
138680588Sobrien			++l;
1387133359Sobrien			m->mask_op |= op;
1388169942Sobrien			val = (uint64_t)strtoull(l, &t, 0);
1389159764Sobrien			l = t;
1390169962Sobrien			m->num_mask = file_signextend(ms, m, val);
139180588Sobrien			eatsize(&l);
1392169962Sobrien		}
1393169962Sobrien		else if (op == FILE_OPDIVIDE) {
1394186691Sobrien			int have_range = 0;
1395133359Sobrien			while (!isspace((unsigned char)*++l)) {
139668349Sobrien				switch (*l) {
1397169962Sobrien				case '0':  case '1':  case '2':
1398169962Sobrien				case '3':  case '4':  case '5':
1399169962Sobrien				case '6':  case '7':  case '8':
1400186691Sobrien				case '9':
1401186691Sobrien					if (have_range &&
1402186691Sobrien					    (ms->flags & MAGIC_CHECK))
1403169962Sobrien						file_magwarn(ms,
1404186691Sobrien						    "multiple ranges");
1405186691Sobrien					have_range = 1;
1406234449Sobrien					m->str_range = CAST(uint32_t,
1407234449Sobrien					    strtoul(l, &t, 0));
1408186691Sobrien					if (m->str_range == 0)
1409186691Sobrien						file_magwarn(ms,
1410186691Sobrien						    "zero range");
1411169962Sobrien					l = t - 1;
141268349Sobrien					break;
1413234449Sobrien				case CHAR_COMPACT_WHITESPACE:
1414234449Sobrien					m->str_flags |=
1415234449Sobrien					    STRING_COMPACT_WHITESPACE;
141668349Sobrien					break;
1417234449Sobrien				case CHAR_COMPACT_OPTIONAL_WHITESPACE:
1418169962Sobrien					m->str_flags |=
1419234449Sobrien					    STRING_COMPACT_OPTIONAL_WHITESPACE;
142068349Sobrien					break;
1421169962Sobrien				case CHAR_IGNORE_LOWERCASE:
1422169962Sobrien					m->str_flags |= STRING_IGNORE_LOWERCASE;
1423169962Sobrien					break;
1424169962Sobrien				case CHAR_IGNORE_UPPERCASE:
1425169962Sobrien					m->str_flags |= STRING_IGNORE_UPPERCASE;
1426169962Sobrien					break;
1427169962Sobrien				case CHAR_REGEX_OFFSET_START:
1428169962Sobrien					m->str_flags |= REGEX_OFFSET_START;
1429169962Sobrien					break;
1430234449Sobrien				case CHAR_BINTEST:
1431234449Sobrien					m->str_flags |= STRING_BINTEST;
1432234449Sobrien					break;
1433234449Sobrien				case CHAR_TEXTTEST:
1434234449Sobrien					m->str_flags |= STRING_TEXTTEST;
1435234449Sobrien					break;
1436234449Sobrien				case CHAR_PSTRING_1_LE:
1437234449Sobrien					if (m->type != FILE_PSTRING)
1438234449Sobrien						goto bad;
1439234449Sobrien					m->str_flags = (m->str_flags & ~PSTRING_LEN) | PSTRING_1_LE;
1440234449Sobrien					break;
1441234449Sobrien				case CHAR_PSTRING_2_BE:
1442234449Sobrien					if (m->type != FILE_PSTRING)
1443234449Sobrien						goto bad;
1444234449Sobrien					m->str_flags = (m->str_flags & ~PSTRING_LEN) | PSTRING_2_BE;
1445234449Sobrien					break;
1446234449Sobrien				case CHAR_PSTRING_2_LE:
1447234449Sobrien					if (m->type != FILE_PSTRING)
1448234449Sobrien						goto bad;
1449234449Sobrien					m->str_flags = (m->str_flags & ~PSTRING_LEN) | PSTRING_2_LE;
1450234449Sobrien					break;
1451234449Sobrien				case CHAR_PSTRING_4_BE:
1452234449Sobrien					if (m->type != FILE_PSTRING)
1453234449Sobrien						goto bad;
1454234449Sobrien					m->str_flags = (m->str_flags & ~PSTRING_LEN) | PSTRING_4_BE;
1455234449Sobrien					break;
1456234449Sobrien				case CHAR_PSTRING_4_LE:
1457234449Sobrien					if (m->type != FILE_PSTRING)
1458234449Sobrien						goto bad;
1459234449Sobrien					m->str_flags = (m->str_flags & ~PSTRING_LEN) | PSTRING_4_LE;
1460234449Sobrien					break;
1461234449Sobrien				case CHAR_PSTRING_LENGTH_INCLUDES_ITSELF:
1462234449Sobrien					if (m->type != FILE_PSTRING)
1463234449Sobrien						goto bad;
1464234449Sobrien					m->str_flags |= PSTRING_LENGTH_INCLUDES_ITSELF;
1465234449Sobrien					break;
146668349Sobrien				default:
1467234449Sobrien				bad:
1468133359Sobrien					if (ms->flags & MAGIC_CHECK)
1469139368Sobrien						file_magwarn(ms,
1470234449Sobrien						    "string extension `%c' "
1471234449Sobrien						    "invalid", *l);
147268349Sobrien					return -1;
147368349Sobrien				}
1474169962Sobrien				/* allow multiple '/' for readability */
1475186691Sobrien				if (l[1] == '/' &&
1476186691Sobrien				    !isspace((unsigned char)l[2]))
1477169962Sobrien					l++;
147868349Sobrien			}
1479169962Sobrien			if (string_modifier_check(ms, m) == -1)
1480169962Sobrien				return -1;
148168349Sobrien		}
1482169962Sobrien		else {
1483169962Sobrien			if (ms->flags & MAGIC_CHECK)
1484169962Sobrien				file_magwarn(ms, "invalid string op: %c", *t);
1485169962Sobrien			return -1;
1486169962Sobrien		}
148780588Sobrien	}
1488133359Sobrien	/*
1489133359Sobrien	 * We used to set mask to all 1's here, instead let's just not do
1490133359Sobrien	 * anything if mask = 0 (unless you have a better idea)
1491133359Sobrien	 */
149268349Sobrien	EATAB;
149368349Sobrien
149468349Sobrien	switch (*l) {
149568349Sobrien	case '>':
149668349Sobrien	case '<':
1497186691Sobrien  		m->reln = *l;
1498186691Sobrien  		++l;
1499186691Sobrien		if (*l == '=') {
1500186691Sobrien			if (ms->flags & MAGIC_CHECK) {
1501186691Sobrien				file_magwarn(ms, "%c= not supported",
1502186691Sobrien				    m->reln);
1503186691Sobrien				return -1;
1504186691Sobrien			}
1505186691Sobrien		   ++l;
1506186691Sobrien		}
1507186691Sobrien		break;
150868349Sobrien	/* Old-style anding: "0 byte &0x80 dynamically linked" */
150968349Sobrien	case '&':
151068349Sobrien	case '^':
151168349Sobrien	case '=':
151268349Sobrien  		m->reln = *l;
151368349Sobrien  		++l;
151468349Sobrien		if (*l == '=') {
151568349Sobrien		   /* HP compat: ignore &= etc. */
151668349Sobrien		   ++l;
151768349Sobrien		}
151868349Sobrien		break;
151968349Sobrien	case '!':
1520159764Sobrien		m->reln = *l;
1521159764Sobrien		++l;
1522159764Sobrien		break;
152368349Sobrien	default:
1524169962Sobrien  		m->reln = '=';	/* the default relation */
1525159764Sobrien		if (*l == 'x' && ((isascii((unsigned char)l[1]) &&
1526159764Sobrien		    isspace((unsigned char)l[1])) || !l[1])) {
152768349Sobrien			m->reln = *l;
152868349Sobrien			++l;
152968349Sobrien		}
153068349Sobrien		break;
153168349Sobrien	}
1532169962Sobrien	/*
1533169962Sobrien	 * Grab the value part, except for an 'x' reln.
1534169962Sobrien	 */
1535169962Sobrien	if (m->reln != 'x' && getvalue(ms, m, &l, action))
153668349Sobrien		return -1;
1537169962Sobrien
153868349Sobrien	/*
153968349Sobrien	 * TODO finish this macro and start using it!
154068349Sobrien	 * #define offsetcheck {if (offset > HOWMANY-1)
154168349Sobrien	 *	magwarn("offset too big"); }
154268349Sobrien	 */
154368349Sobrien
154468349Sobrien	/*
1545169962Sobrien	 * Now get last part - the description
154668349Sobrien	 */
154768349Sobrien	EATAB;
154868349Sobrien	if (l[0] == '\b') {
154968349Sobrien		++l;
1550186691Sobrien		m->flag |= NOSPACE;
155168349Sobrien	} else if ((l[0] == '\\') && (l[1] == 'b')) {
155268349Sobrien		++l;
155368349Sobrien		++l;
1554186691Sobrien		m->flag |= NOSPACE;
1555186691Sobrien	}
1556169942Sobrien	for (i = 0; (m->desc[i++] = *l++) != '\0' && i < sizeof(m->desc); )
1557169942Sobrien		continue;
1558169942Sobrien	if (i == sizeof(m->desc)) {
1559169942Sobrien		m->desc[sizeof(m->desc) - 1] = '\0';
1560169942Sobrien		if (ms->flags & MAGIC_CHECK)
1561169942Sobrien			file_magwarn(ms, "description `%s' truncated", m->desc);
1562169942Sobrien	}
156368349Sobrien
1564169942Sobrien        /*
1565169942Sobrien	 * We only do this check while compiling, or if any of the magic
1566169942Sobrien	 * files were not compiled.
1567169942Sobrien         */
1568169942Sobrien        if (ms->flags & MAGIC_CHECK) {
1569169942Sobrien		if (check_format(ms, m) == -1)
1570133359Sobrien			return -1;
1571133359Sobrien	}
1572103373Sobrien#ifndef COMPILE_ONLY
1573133359Sobrien	if (action == FILE_CHECK) {
1574133359Sobrien		file_mdump(m);
157568349Sobrien	}
1576103373Sobrien#endif
1577186691Sobrien	m->mimetype[0] = '\0';		/* initialise MIME type to none */
1578159764Sobrien	if (m->cont_level == 0)
1579159764Sobrien		++(*nmentryp);		/* make room for next */
158068349Sobrien	return 0;
158168349Sobrien}
158268349Sobrien
1583186691Sobrien/*
1584186691Sobrien * parse a STRENGTH annotation line from magic file, put into magic[index - 1]
1585186691Sobrien * if valid
1586186691Sobrien */
1587169942Sobrienprivate int
1588186691Sobrienparse_strength(struct magic_set *ms, struct magic_entry *me, const char *line)
1589186691Sobrien{
1590186691Sobrien	const char *l = line;
1591186691Sobrien	char *el;
1592186691Sobrien	unsigned long factor;
1593186691Sobrien	struct magic *m = &me->mp[0];
1594186691Sobrien
1595186691Sobrien	if (m->factor_op != FILE_FACTOR_OP_NONE) {
1596186691Sobrien		file_magwarn(ms,
1597186691Sobrien		    "Current entry already has a strength type: %c %d",
1598186691Sobrien		    m->factor_op, m->factor);
1599186691Sobrien		return -1;
1600186691Sobrien	}
1601186691Sobrien	EATAB;
1602186691Sobrien	switch (*l) {
1603186691Sobrien	case FILE_FACTOR_OP_NONE:
1604186691Sobrien	case FILE_FACTOR_OP_PLUS:
1605186691Sobrien	case FILE_FACTOR_OP_MINUS:
1606186691Sobrien	case FILE_FACTOR_OP_TIMES:
1607186691Sobrien	case FILE_FACTOR_OP_DIV:
1608186691Sobrien		m->factor_op = *l++;
1609186691Sobrien		break;
1610186691Sobrien	default:
1611186691Sobrien		file_magwarn(ms, "Unknown factor op `%c'", *l);
1612186691Sobrien		return -1;
1613186691Sobrien	}
1614186691Sobrien	EATAB;
1615186691Sobrien	factor = strtoul(l, &el, 0);
1616186691Sobrien	if (factor > 255) {
1617186691Sobrien		file_magwarn(ms, "Too large factor `%lu'", factor);
1618186691Sobrien		goto out;
1619186691Sobrien	}
1620186691Sobrien	if (*el && !isspace((unsigned char)*el)) {
1621186691Sobrien		file_magwarn(ms, "Bad factor `%s'", l);
1622186691Sobrien		goto out;
1623186691Sobrien	}
1624186691Sobrien	m->factor = (uint8_t)factor;
1625186691Sobrien	if (m->factor == 0 && m->factor_op == FILE_FACTOR_OP_DIV) {
1626186691Sobrien		file_magwarn(ms, "Cannot have factor op `%c' and factor %u",
1627186691Sobrien		    m->factor_op, m->factor);
1628186691Sobrien		goto out;
1629186691Sobrien	}
1630186691Sobrien	return 0;
1631186691Sobrienout:
1632186691Sobrien	m->factor_op = FILE_FACTOR_OP_NONE;
1633186691Sobrien	m->factor = 0;
1634186691Sobrien	return -1;
1635186691Sobrien}
1636186691Sobrien
1637186691Sobrien/*
1638234449Sobrien * Parse an Apple CREATOR/TYPE annotation from magic file and put it into
1639234449Sobrien * magic[index - 1]
1640191771Sobrien */
1641191771Sobrienprivate int
1642191771Sobrienparse_apple(struct magic_set *ms, struct magic_entry *me, const char *line)
1643191771Sobrien{
1644191771Sobrien	size_t i;
1645191771Sobrien	const char *l = line;
1646191771Sobrien	struct magic *m = &me->mp[me->cont_count == 0 ? 0 : me->cont_count - 1];
1647191771Sobrien
1648191771Sobrien	if (m->apple[0] != '\0') {
1649234449Sobrien		file_magwarn(ms, "Current entry already has a APPLE type "
1650234449Sobrien		    "`%.8s', new type `%s'", m->mimetype, l);
1651191771Sobrien		return -1;
1652191771Sobrien	}
1653191771Sobrien
1654191771Sobrien	EATAB;
1655234449Sobrien	for (i = 0; *l && ((isascii((unsigned char)*l) &&
1656234449Sobrien	    isalnum((unsigned char)*l)) || strchr("-+/.", *l)) &&
1657234449Sobrien	    i < sizeof(m->apple); m->apple[i++] = *l++)
1658191771Sobrien		continue;
1659191771Sobrien	if (i == sizeof(m->apple) && *l) {
1660234449Sobrien		/* We don't need to NUL terminate here, printing handles it */
1661191771Sobrien		if (ms->flags & MAGIC_CHECK)
1662234449Sobrien			file_magwarn(ms, "APPLE type `%s' truncated %"
1663234449Sobrien			    SIZE_T_FORMAT "u", line, i);
1664191771Sobrien	}
1665191771Sobrien
1666191771Sobrien	if (i > 0)
1667191771Sobrien		return 0;
1668191771Sobrien	else
1669191771Sobrien		return -1;
1670191771Sobrien}
1671191771Sobrien
1672191771Sobrien/*
1673186691Sobrien * parse a MIME annotation line from magic file, put into magic[index - 1]
1674186691Sobrien * if valid
1675186691Sobrien */
1676186691Sobrienprivate int
1677186691Sobrienparse_mime(struct magic_set *ms, struct magic_entry *me, const char *line)
1678186691Sobrien{
1679186691Sobrien	size_t i;
1680186691Sobrien	const char *l = line;
1681186691Sobrien	struct magic *m = &me->mp[me->cont_count == 0 ? 0 : me->cont_count - 1];
1682186691Sobrien
1683186691Sobrien	if (m->mimetype[0] != '\0') {
1684186691Sobrien		file_magwarn(ms, "Current entry already has a MIME type `%s',"
1685186691Sobrien		    " new type `%s'", m->mimetype, l);
1686186691Sobrien		return -1;
1687186691Sobrien	}
1688186691Sobrien
1689186691Sobrien	EATAB;
1690234449Sobrien	for (i = 0; *l && ((isascii((unsigned char)*l) &&
1691234449Sobrien	    isalnum((unsigned char)*l)) || strchr("-+/.", *l)) &&
1692234449Sobrien	    i < sizeof(m->mimetype); m->mimetype[i++] = *l++)
1693186691Sobrien		continue;
1694186691Sobrien	if (i == sizeof(m->mimetype)) {
1695234449Sobrien		m->mimetype[sizeof(m->mimetype) - 1] = '\0';
1696186691Sobrien		if (ms->flags & MAGIC_CHECK)
1697234449Sobrien			file_magwarn(ms, "MIME type `%s' truncated %"
1698234449Sobrien			    SIZE_T_FORMAT "u", m->mimetype, i);
1699186691Sobrien	} else
1700186691Sobrien		m->mimetype[i] = '\0';
1701186691Sobrien
1702186691Sobrien	if (i > 0)
1703186691Sobrien		return 0;
1704186691Sobrien	else
1705186691Sobrien		return -1;
1706186691Sobrien}
1707186691Sobrien
1708186691Sobrienprivate int
1709169942Sobriencheck_format_type(const char *ptr, int type)
1710169942Sobrien{
1711169942Sobrien	int quad = 0;
1712169942Sobrien	if (*ptr == '\0') {
1713169942Sobrien		/* Missing format string; bad */
1714169942Sobrien		return -1;
1715169942Sobrien	}
1716169942Sobrien
1717169942Sobrien	switch (type) {
1718169942Sobrien	case FILE_FMT_QUAD:
1719169942Sobrien		quad = 1;
1720169942Sobrien		/*FALLTHROUGH*/
1721169942Sobrien	case FILE_FMT_NUM:
1722169942Sobrien		if (*ptr == '-')
1723169942Sobrien			ptr++;
1724169942Sobrien		if (*ptr == '.')
1725169942Sobrien			ptr++;
1726169942Sobrien		while (isdigit((unsigned char)*ptr)) ptr++;
1727169942Sobrien		if (*ptr == '.')
1728169942Sobrien			ptr++;
1729169942Sobrien		while (isdigit((unsigned char)*ptr)) ptr++;
1730169942Sobrien		if (quad) {
1731169942Sobrien			if (*ptr++ != 'l')
1732169942Sobrien				return -1;
1733169942Sobrien			if (*ptr++ != 'l')
1734169942Sobrien				return -1;
1735169942Sobrien		}
1736169942Sobrien
1737169942Sobrien		switch (*ptr++) {
1738169942Sobrien		case 'l':
1739169942Sobrien			switch (*ptr++) {
1740169942Sobrien			case 'i':
1741169942Sobrien			case 'd':
1742169942Sobrien			case 'u':
1743169942Sobrien			case 'x':
1744169942Sobrien			case 'X':
1745169942Sobrien				return 0;
1746169942Sobrien			default:
1747169942Sobrien				return -1;
1748169942Sobrien			}
1749169942Sobrien
1750169942Sobrien		case 'h':
1751169942Sobrien			switch (*ptr++) {
1752169942Sobrien			case 'h':
1753169942Sobrien				switch (*ptr++) {
1754169942Sobrien				case 'i':
1755169942Sobrien				case 'd':
1756169942Sobrien				case 'u':
1757169942Sobrien				case 'x':
1758169942Sobrien				case 'X':
1759169942Sobrien					return 0;
1760169942Sobrien				default:
1761169942Sobrien					return -1;
1762169942Sobrien				}
1763169942Sobrien			case 'd':
1764169942Sobrien				return 0;
1765169942Sobrien			default:
1766169942Sobrien				return -1;
1767169942Sobrien			}
1768169942Sobrien
1769169942Sobrien		case 'i':
1770169942Sobrien		case 'c':
1771169942Sobrien		case 'd':
1772169942Sobrien		case 'u':
1773169942Sobrien		case 'x':
1774169942Sobrien		case 'X':
1775169942Sobrien			return 0;
1776169942Sobrien
1777169942Sobrien		default:
1778169942Sobrien			return -1;
1779169942Sobrien		}
1780169942Sobrien
1781175296Sobrien	case FILE_FMT_FLOAT:
1782175296Sobrien	case FILE_FMT_DOUBLE:
1783175296Sobrien		if (*ptr == '-')
1784175296Sobrien			ptr++;
1785175296Sobrien		if (*ptr == '.')
1786175296Sobrien			ptr++;
1787175296Sobrien		while (isdigit((unsigned char)*ptr)) ptr++;
1788175296Sobrien		if (*ptr == '.')
1789175296Sobrien			ptr++;
1790175296Sobrien		while (isdigit((unsigned char)*ptr)) ptr++;
1791175296Sobrien
1792175296Sobrien		switch (*ptr++) {
1793175296Sobrien		case 'e':
1794175296Sobrien		case 'E':
1795175296Sobrien		case 'f':
1796175296Sobrien		case 'F':
1797175296Sobrien		case 'g':
1798175296Sobrien		case 'G':
1799175296Sobrien			return 0;
1800175296Sobrien
1801175296Sobrien		default:
1802175296Sobrien			return -1;
1803175296Sobrien		}
1804175296Sobrien
1805175296Sobrien
1806169942Sobrien	case FILE_FMT_STR:
1807169942Sobrien		if (*ptr == '-')
1808169942Sobrien			ptr++;
1809169942Sobrien		while (isdigit((unsigned char )*ptr))
1810169942Sobrien			ptr++;
1811169942Sobrien		if (*ptr == '.') {
1812169942Sobrien			ptr++;
1813169942Sobrien			while (isdigit((unsigned char )*ptr))
1814169942Sobrien				ptr++;
1815169942Sobrien		}
1816169942Sobrien
1817169942Sobrien		switch (*ptr++) {
1818169942Sobrien		case 's':
1819169942Sobrien			return 0;
1820169942Sobrien		default:
1821169942Sobrien			return -1;
1822169942Sobrien		}
1823169942Sobrien
1824169942Sobrien	default:
1825169942Sobrien		/* internal error */
1826169942Sobrien		abort();
1827169942Sobrien	}
1828169942Sobrien	/*NOTREACHED*/
1829169942Sobrien	return -1;
1830169942Sobrien}
1831169942Sobrien
1832133359Sobrien/*
1833133359Sobrien * Check that the optional printf format in description matches
1834133359Sobrien * the type of the magic.
1835133359Sobrien */
1836133359Sobrienprivate int
1837139368Sobriencheck_format(struct magic_set *ms, struct magic *m)
1838133359Sobrien{
1839133359Sobrien	char *ptr;
1840133359Sobrien
1841133359Sobrien	for (ptr = m->desc; *ptr; ptr++)
1842133359Sobrien		if (*ptr == '%')
1843133359Sobrien			break;
1844133359Sobrien	if (*ptr == '\0') {
1845133359Sobrien		/* No format string; ok */
1846133359Sobrien		return 1;
1847133359Sobrien	}
1848169942Sobrien
1849169942Sobrien	assert(file_nformats == file_nnames);
1850169942Sobrien
1851169942Sobrien	if (m->type >= file_nformats) {
1852186691Sobrien		file_magwarn(ms, "Internal error inconsistency between "
1853169942Sobrien		    "m->type and format strings");
1854169942Sobrien		return -1;
1855133359Sobrien	}
1856169942Sobrien	if (file_formats[m->type] == FILE_FMT_NONE) {
1857186691Sobrien		file_magwarn(ms, "No format string for `%s' with description "
1858169942Sobrien		    "`%s'", m->desc, file_names[m->type]);
1859169942Sobrien		return -1;
1860133359Sobrien	}
1861169942Sobrien
1862169942Sobrien	ptr++;
1863169942Sobrien	if (check_format_type(ptr, file_formats[m->type]) == -1) {
1864169942Sobrien		/*
1865169942Sobrien		 * TODO: this error message is unhelpful if the format
1866169942Sobrien		 * string is not one character long
1867169942Sobrien		 */
1868186691Sobrien		file_magwarn(ms, "Printf format `%c' is not valid for type "
1869192350Sdelphij		    "`%s' in description `%s'", *ptr ? *ptr : '?',
1870169942Sobrien		    file_names[m->type], m->desc);
1871169942Sobrien		return -1;
1872169942Sobrien	}
1873169942Sobrien
1874133359Sobrien	for (; *ptr; ptr++) {
1875169942Sobrien		if (*ptr == '%') {
1876186691Sobrien			file_magwarn(ms,
1877169942Sobrien			    "Too many format strings (should have at most one) "
1878169942Sobrien			    "for `%s' with description `%s'",
1879169942Sobrien			    file_names[m->type], m->desc);
1880169942Sobrien			return -1;
1881133359Sobrien		}
1882133359Sobrien	}
1883169942Sobrien	return 0;
1884133359Sobrien}
1885133359Sobrien
188668349Sobrien/*
188768349Sobrien * Read a numeric value from a pointer, into the value union of a magic
188868349Sobrien * pointer, according to the magic type.  Update the string pointer to point
188968349Sobrien * just after the number read.  Return 0 for success, non-zero for failure.
189068349Sobrien */
1891133359Sobrienprivate int
1892169962Sobriengetvalue(struct magic_set *ms, struct magic *m, const char **p, int action)
189368349Sobrien{
1894133359Sobrien	switch (m->type) {
1895139368Sobrien	case FILE_BESTRING16:
1896139368Sobrien	case FILE_LESTRING16:
1897133359Sobrien	case FILE_STRING:
1898133359Sobrien	case FILE_PSTRING:
1899133359Sobrien	case FILE_REGEX:
1900159764Sobrien	case FILE_SEARCH:
1901192350Sdelphij		*p = getstr(ms, m, *p, action == FILE_COMPILE);
1902133359Sobrien		if (*p == NULL) {
1903133359Sobrien			if (ms->flags & MAGIC_CHECK)
1904139368Sobrien				file_magwarn(ms, "cannot get string from `%s'",
1905133359Sobrien				    m->value.s);
1906133359Sobrien			return -1;
1907133359Sobrien		}
1908133359Sobrien		return 0;
1909175296Sobrien	case FILE_FLOAT:
1910175296Sobrien	case FILE_BEFLOAT:
1911175296Sobrien	case FILE_LEFLOAT:
1912175296Sobrien		if (m->reln != 'x') {
1913175296Sobrien			char *ep;
1914175296Sobrien#ifdef HAVE_STRTOF
1915175296Sobrien			m->value.f = strtof(*p, &ep);
1916175296Sobrien#else
1917175296Sobrien			m->value.f = (float)strtod(*p, &ep);
1918175296Sobrien#endif
1919175296Sobrien			*p = ep;
1920175296Sobrien		}
1921175296Sobrien		return 0;
1922175296Sobrien	case FILE_DOUBLE:
1923175296Sobrien	case FILE_BEDOUBLE:
1924175296Sobrien	case FILE_LEDOUBLE:
1925175296Sobrien		if (m->reln != 'x') {
1926175296Sobrien			char *ep;
1927175296Sobrien			m->value.d = strtod(*p, &ep);
1928175296Sobrien			*p = ep;
1929175296Sobrien		}
1930175296Sobrien		return 0;
1931133359Sobrien	default:
193268349Sobrien		if (m->reln != 'x') {
1933159764Sobrien			char *ep;
1934169942Sobrien			m->value.q = file_signextend(ms, m,
1935169942Sobrien			    (uint64_t)strtoull(*p, &ep, 0));
1936159764Sobrien			*p = ep;
193768349Sobrien			eatsize(p);
193868349Sobrien		}
1939133359Sobrien		return 0;
1940133359Sobrien	}
194168349Sobrien}
194268349Sobrien
194368349Sobrien/*
194468349Sobrien * Convert a string containing C character escapes.  Stop at an unescaped
194568349Sobrien * space or tab.
1946192350Sdelphij * Copy the converted version to "m->value.s", and the length in m->vallen.
1947192350Sdelphij * Return updated scan pointer as function result. Warn if set.
194868349Sobrien */
1949159764Sobrienprivate const char *
1950192350Sdelphijgetstr(struct magic_set *ms, struct magic *m, const char *s, int warn)
195168349Sobrien{
1952159764Sobrien	const char *origs = s;
1953192350Sdelphij	char	*p = m->value.s;
1954192350Sdelphij	size_t  plen = sizeof(m->value.s);
1955159764Sobrien	char 	*origp = p;
195668349Sobrien	char	*pmax = p + plen - 1;
195768349Sobrien	int	c;
195868349Sobrien	int	val;
195968349Sobrien
196068349Sobrien	while ((c = *s++) != '\0') {
196168349Sobrien		if (isspace((unsigned char) c))
196268349Sobrien			break;
196368349Sobrien		if (p >= pmax) {
1964133359Sobrien			file_error(ms, 0, "string too long: `%s'", origs);
1965133359Sobrien			return NULL;
196668349Sobrien		}
1967169962Sobrien		if (c == '\\') {
196868349Sobrien			switch(c = *s++) {
196968349Sobrien
197068349Sobrien			case '\0':
1971192350Sdelphij				if (warn)
1972169962Sobrien					file_magwarn(ms, "incomplete escape");
197368349Sobrien				goto out;
197468349Sobrien
1975169962Sobrien			case '\t':
1976192350Sdelphij				if (warn) {
1977169962Sobrien					file_magwarn(ms,
1978169962Sobrien					    "escaped tab found, use \\t instead");
1979192350Sdelphij					warn = 0;	/* already did */
1980169962Sobrien				}
1981169962Sobrien				/*FALLTHROUGH*/
198268349Sobrien			default:
1983192350Sdelphij				if (warn) {
1984192350Sdelphij					if (isprint((unsigned char)c)) {
1985192350Sdelphij						/* Allow escaping of
1986192350Sdelphij						 * ``relations'' */
1987234449Sobrien						if (strchr("<>&^=!", c) == NULL
1988234449Sobrien						    && (m->type != FILE_REGEX ||
1989234449Sobrien						    strchr("[]().*?^$|{}", c)
1990234449Sobrien						    == NULL)) {
1991192350Sdelphij							file_magwarn(ms, "no "
1992192350Sdelphij							    "need to escape "
1993192350Sdelphij							    "`%c'", c);
1994192350Sdelphij						}
1995192350Sdelphij					} else {
1996192350Sdelphij						file_magwarn(ms,
1997192350Sdelphij						    "unknown escape sequence: "
1998192350Sdelphij						    "\\%03o", c);
1999192350Sdelphij					}
2000169962Sobrien				}
2001169962Sobrien				/*FALLTHROUGH*/
2002169962Sobrien			/* space, perhaps force people to use \040? */
2003169962Sobrien			case ' ':
2004169962Sobrien#if 0
2005169962Sobrien			/*
2006169962Sobrien			 * Other things people escape, but shouldn't need to,
2007169962Sobrien			 * so we disallow them
2008169962Sobrien			 */
2009169962Sobrien			case '\'':
2010169962Sobrien			case '"':
2011169962Sobrien			case '?':
2012169962Sobrien#endif
2013169962Sobrien			/* Relations */
2014169962Sobrien			case '>':
2015169962Sobrien			case '<':
2016169962Sobrien			case '&':
2017169962Sobrien			case '^':
2018169962Sobrien			case '=':
2019169962Sobrien			case '!':
2020169962Sobrien			/* and baskslash itself */
2021169962Sobrien			case '\\':
202268349Sobrien				*p++ = (char) c;
202368349Sobrien				break;
202468349Sobrien
2025169962Sobrien			case 'a':
2026169962Sobrien				*p++ = '\a';
2027169962Sobrien				break;
2028169962Sobrien
2029169962Sobrien			case 'b':
2030169962Sobrien				*p++ = '\b';
2031169962Sobrien				break;
2032169962Sobrien
2033169962Sobrien			case 'f':
2034169962Sobrien				*p++ = '\f';
2035169962Sobrien				break;
2036169962Sobrien
203768349Sobrien			case 'n':
203868349Sobrien				*p++ = '\n';
203968349Sobrien				break;
204068349Sobrien
204168349Sobrien			case 'r':
204268349Sobrien				*p++ = '\r';
204368349Sobrien				break;
204468349Sobrien
204568349Sobrien			case 't':
204668349Sobrien				*p++ = '\t';
204768349Sobrien				break;
204868349Sobrien
204968349Sobrien			case 'v':
205068349Sobrien				*p++ = '\v';
205168349Sobrien				break;
205268349Sobrien
205368349Sobrien			/* \ and up to 3 octal digits */
205468349Sobrien			case '0':
205568349Sobrien			case '1':
205668349Sobrien			case '2':
205768349Sobrien			case '3':
205868349Sobrien			case '4':
205968349Sobrien			case '5':
206068349Sobrien			case '6':
206168349Sobrien			case '7':
206268349Sobrien				val = c - '0';
206368349Sobrien				c = *s++;  /* try for 2 */
2064169962Sobrien				if (c >= '0' && c <= '7') {
2065169962Sobrien					val = (val << 3) | (c - '0');
206668349Sobrien					c = *s++;  /* try for 3 */
2067169962Sobrien					if (c >= '0' && c <= '7')
2068169962Sobrien						val = (val << 3) | (c-'0');
206968349Sobrien					else
207068349Sobrien						--s;
207168349Sobrien				}
207268349Sobrien				else
207368349Sobrien					--s;
207468349Sobrien				*p++ = (char)val;
207568349Sobrien				break;
207668349Sobrien
207768349Sobrien			/* \x and up to 2 hex digits */
207868349Sobrien			case 'x':
207968349Sobrien				val = 'x';	/* Default if no digits */
208068349Sobrien				c = hextoint(*s++);	/* Get next char */
208168349Sobrien				if (c >= 0) {
208268349Sobrien					val = c;
208368349Sobrien					c = hextoint(*s++);
208468349Sobrien					if (c >= 0)
208568349Sobrien						val = (val << 4) + c;
208668349Sobrien					else
208768349Sobrien						--s;
208868349Sobrien				} else
208968349Sobrien					--s;
209068349Sobrien				*p++ = (char)val;
209168349Sobrien				break;
209268349Sobrien			}
209368349Sobrien		} else
209468349Sobrien			*p++ = (char)c;
209568349Sobrien	}
209668349Sobrienout:
209768349Sobrien	*p = '\0';
2098234449Sobrien	m->vallen = CAST(unsigned char, (p - origp));
2099192350Sdelphij	if (m->type == FILE_PSTRING)
2100234449Sobrien		m->vallen += (unsigned char)file_pstring_length_size(m);
210168349Sobrien	return s;
210268349Sobrien}
210368349Sobrien
210468349Sobrien
210568349Sobrien/* Single hex char to int; -1 if not a hex char. */
2106133359Sobrienprivate int
2107103373Sobrienhextoint(int c)
210868349Sobrien{
210968349Sobrien	if (!isascii((unsigned char) c))
211068349Sobrien		return -1;
211168349Sobrien	if (isdigit((unsigned char) c))
211268349Sobrien		return c - '0';
2113169962Sobrien	if ((c >= 'a') && (c <= 'f'))
211468349Sobrien		return c + 10 - 'a';
2115169962Sobrien	if (( c>= 'A') && (c <= 'F'))
211668349Sobrien		return c + 10 - 'A';
211768349Sobrien	return -1;
211868349Sobrien}
211968349Sobrien
212068349Sobrien
212168349Sobrien/*
212268349Sobrien * Print a string containing C character escapes.
212368349Sobrien */
2124133359Sobrienprotected void
2125133359Sobrienfile_showstr(FILE *fp, const char *s, size_t len)
212668349Sobrien{
212768349Sobrien	char	c;
212868349Sobrien
212968349Sobrien	for (;;) {
2130133359Sobrien		if (len == ~0U) {
2131234449Sobrien			c = *s++;
213268349Sobrien			if (c == '\0')
213368349Sobrien				break;
213468349Sobrien		}
213568349Sobrien		else  {
213668349Sobrien			if (len-- == 0)
213768349Sobrien				break;
2138234449Sobrien			c = *s++;
213968349Sobrien		}
2140169962Sobrien		if (c >= 040 && c <= 0176)	/* TODO isprint && !iscntrl */
214168349Sobrien			(void) fputc(c, fp);
214268349Sobrien		else {
214368349Sobrien			(void) fputc('\\', fp);
214468349Sobrien			switch (c) {
2145169962Sobrien			case '\a':
2146169962Sobrien				(void) fputc('a', fp);
2147169962Sobrien				break;
2148169962Sobrien
2149169962Sobrien			case '\b':
2150169962Sobrien				(void) fputc('b', fp);
2151169962Sobrien				break;
2152169962Sobrien
2153169962Sobrien			case '\f':
2154169962Sobrien				(void) fputc('f', fp);
2155169962Sobrien				break;
2156169962Sobrien
215768349Sobrien			case '\n':
215868349Sobrien				(void) fputc('n', fp);
215968349Sobrien				break;
216068349Sobrien
216168349Sobrien			case '\r':
216268349Sobrien				(void) fputc('r', fp);
216368349Sobrien				break;
216468349Sobrien
216568349Sobrien			case '\t':
216668349Sobrien				(void) fputc('t', fp);
216768349Sobrien				break;
216868349Sobrien
216968349Sobrien			case '\v':
217068349Sobrien				(void) fputc('v', fp);
217168349Sobrien				break;
217268349Sobrien
217368349Sobrien			default:
217468349Sobrien				(void) fprintf(fp, "%.3o", c & 0377);
217568349Sobrien				break;
217668349Sobrien			}
217768349Sobrien		}
217868349Sobrien	}
217968349Sobrien}
218068349Sobrien
218168349Sobrien/*
218268349Sobrien * eatsize(): Eat the size spec from a number [eg. 10UL]
218368349Sobrien */
2184133359Sobrienprivate void
2185159764Sobrieneatsize(const char **p)
218668349Sobrien{
2187159764Sobrien	const char *l = *p;
218868349Sobrien
218968349Sobrien	if (LOWCASE(*l) == 'u')
219068349Sobrien		l++;
219168349Sobrien
219268349Sobrien	switch (LOWCASE(*l)) {
219368349Sobrien	case 'l':    /* long */
219468349Sobrien	case 's':    /* short */
219568349Sobrien	case 'h':    /* short */
219668349Sobrien	case 'b':    /* char/byte */
219768349Sobrien	case 'c':    /* char/byte */
219868349Sobrien		l++;
219968349Sobrien		/*FALLTHROUGH*/
220068349Sobrien	default:
220168349Sobrien		break;
220268349Sobrien	}
220368349Sobrien
220468349Sobrien	*p = l;
220568349Sobrien}
220674784Sobrien
2207210761Srpaulo#ifndef COMPILE_ONLY
220874784Sobrien/*
2209103373Sobrien * handle a compiled file.
221074784Sobrien */
2211133359Sobrienprivate int
2212133359Sobrienapprentice_map(struct magic_set *ms, struct magic **magicp, uint32_t *nmagicp,
2213133359Sobrien    const char *fn)
221474784Sobrien{
221574784Sobrien	int fd;
221674784Sobrien	struct stat st;
2217103373Sobrien	uint32_t *ptr;
2218103373Sobrien	uint32_t version;
221974784Sobrien	int needsbyteswap;
2220186691Sobrien	char *dbname = NULL;
2221133359Sobrien	void *mm = NULL;
222274784Sobrien
2223191771Sobrien	dbname = mkdbname(ms, fn, 0);
222480588Sobrien	if (dbname == NULL)
2225186691Sobrien		goto error2;
222680588Sobrien
2227159764Sobrien	if ((fd = open(dbname, O_RDONLY|O_BINARY)) == -1)
2228186691Sobrien		goto error2;
222974784Sobrien
223074784Sobrien	if (fstat(fd, &st) == -1) {
2231133359Sobrien		file_error(ms, errno, "cannot stat `%s'", dbname);
2232186691Sobrien		goto error1;
223374784Sobrien	}
2234186691Sobrien	if (st.st_size < 8) {
2235133359Sobrien		file_error(ms, 0, "file `%s' is too small", dbname);
2236186691Sobrien		goto error1;
2237133359Sobrien	}
223874784Sobrien
223980588Sobrien#ifdef QUICK
2240103373Sobrien	if ((mm = mmap(0, (size_t)st.st_size, PROT_READ|PROT_WRITE,
224174784Sobrien	    MAP_PRIVATE|MAP_FILE, fd, (off_t)0)) == MAP_FAILED) {
2242133359Sobrien		file_error(ms, errno, "cannot map `%s'", dbname);
2243186691Sobrien		goto error1;
224474784Sobrien	}
2245133359Sobrien#define RET	2
224680588Sobrien#else
2247186691Sobrien	if ((mm = CAST(void *, malloc((size_t)st.st_size))) == NULL) {
2248169942Sobrien		file_oomem(ms, (size_t)st.st_size);
2249186691Sobrien		goto error1;
225080588Sobrien	}
2251192350Sdelphij	if (read(fd, mm, (size_t)st.st_size) != (ssize_t)st.st_size) {
2252133359Sobrien		file_badread(ms);
2253186691Sobrien		goto error1;
225480588Sobrien	}
2255133359Sobrien#define RET	1
225680588Sobrien#endif
2257186691Sobrien	*magicp = CAST(struct magic *, mm);
225874784Sobrien	(void)close(fd);
225975937Sobrien	fd = -1;
2260133359Sobrien	ptr = (uint32_t *)(void *)*magicp;
226174784Sobrien	if (*ptr != MAGICNO) {
226274784Sobrien		if (swap4(*ptr) != MAGICNO) {
2263186691Sobrien			file_error(ms, 0, "bad magic in `%s'", dbname);
2264186691Sobrien			goto error1;
226574784Sobrien		}
226674784Sobrien		needsbyteswap = 1;
226774784Sobrien	} else
226874784Sobrien		needsbyteswap = 0;
226974784Sobrien	if (needsbyteswap)
227074784Sobrien		version = swap4(ptr[1]);
227174784Sobrien	else
227274784Sobrien		version = ptr[1];
227374784Sobrien	if (version != VERSIONNO) {
2274234449Sobrien		file_error(ms, 0, "File %s supports only version %d magic "
2275234449Sobrien		    "files. `%s' is version %d", VERSION,
2276175296Sobrien		    VERSIONNO, dbname, version);
2277186691Sobrien		goto error1;
227874784Sobrien	}
2279186691Sobrien	*nmagicp = (uint32_t)(st.st_size / sizeof(struct magic));
2280186691Sobrien	if (*nmagicp > 0)
2281186691Sobrien		(*nmagicp)--;
228274784Sobrien	(*magicp)++;
228374784Sobrien	if (needsbyteswap)
228474784Sobrien		byteswap(*magicp, *nmagicp);
2285186691Sobrien	free(dbname);
2286133359Sobrien	return RET;
228774784Sobrien
2288186691Sobrienerror1:
228974784Sobrien	if (fd != -1)
229074784Sobrien		(void)close(fd);
2291103373Sobrien	if (mm) {
229280588Sobrien#ifdef QUICK
2293133359Sobrien		(void)munmap((void *)mm, (size_t)st.st_size);
229480588Sobrien#else
2295103373Sobrien		free(mm);
229680588Sobrien#endif
229780588Sobrien	} else {
229874784Sobrien		*magicp = NULL;
229974784Sobrien		*nmagicp = 0;
230074784Sobrien	}
2301186691Sobrienerror2:
2302186691Sobrien	free(dbname);
230374784Sobrien	return -1;
230474784Sobrien}
2305210761Srpaulo#endif /* COMPILE_ONLY */
230674784Sobrien
2307133359Sobrienprivate const uint32_t ar[] = {
2308133359Sobrien    MAGICNO, VERSIONNO
2309133359Sobrien};
231074784Sobrien/*
231174784Sobrien * handle an mmaped file.
231274784Sobrien */
2313133359Sobrienprivate int
2314133359Sobrienapprentice_compile(struct magic_set *ms, struct magic **magicp,
2315133359Sobrien    uint32_t *nmagicp, const char *fn)
231674784Sobrien{
2317234449Sobrien	int fd = -1;
2318186691Sobrien	char *dbname;
2319186691Sobrien	int rv = -1;
232074784Sobrien
2321191771Sobrien	dbname = mkdbname(ms, fn, 1);
2322186691Sobrien
232380588Sobrien	if (dbname == NULL)
2324186691Sobrien		goto out;
232580588Sobrien
2326159764Sobrien	if ((fd = open(dbname, O_WRONLY|O_CREAT|O_TRUNC|O_BINARY, 0644)) == -1) {
2327133359Sobrien		file_error(ms, errno, "cannot open `%s'", dbname);
2328186691Sobrien		goto out;
232974784Sobrien	}
233074784Sobrien
2331133359Sobrien	if (write(fd, ar, sizeof(ar)) != (ssize_t)sizeof(ar)) {
2332133359Sobrien		file_error(ms, errno, "error writing `%s'", dbname);
2333186691Sobrien		goto out;
233474784Sobrien	}
233574784Sobrien
2336133359Sobrien	if (lseek(fd, (off_t)sizeof(struct magic), SEEK_SET)
2337133359Sobrien	    != sizeof(struct magic)) {
2338133359Sobrien		file_error(ms, errno, "error seeking `%s'", dbname);
2339186691Sobrien		goto out;
234074784Sobrien	}
234174784Sobrien
2342133359Sobrien	if (write(fd, *magicp, (sizeof(struct magic) * *nmagicp))
2343133359Sobrien	    != (ssize_t)(sizeof(struct magic) * *nmagicp)) {
2344133359Sobrien		file_error(ms, errno, "error writing `%s'", dbname);
2345186691Sobrien		goto out;
234674784Sobrien	}
234774784Sobrien
2348234449Sobrien	if (fd != -1)
2349234449Sobrien		(void)close(fd);
2350186691Sobrien	rv = 0;
2351186691Sobrienout:
2352186691Sobrien	free(dbname);
2353186691Sobrien	return rv;
235474784Sobrien}
235574784Sobrien
2356133359Sobrienprivate const char ext[] = ".mgc";
235774784Sobrien/*
235874784Sobrien * make a dbname
235974784Sobrien */
2360191771Sobrienprivate char *
2361191771Sobrienmkdbname(struct magic_set *ms, const char *fn, int strip)
236274784Sobrien{
2363191771Sobrien	const char *p, *q;
2364191771Sobrien	char *buf;
2365191771Sobrien
2366139368Sobrien	if (strip) {
2367139368Sobrien		if ((p = strrchr(fn, '/')) != NULL)
2368139368Sobrien			fn = ++p;
2369139368Sobrien	}
2370139368Sobrien
2371191771Sobrien	for (q = fn; *q; q++)
2372191771Sobrien		continue;
2373191771Sobrien	/* Look for .mgc */
2374191771Sobrien	for (p = ext + sizeof(ext) - 1; p >= ext && q >= fn; p--, q--)
2375191771Sobrien		if (*p != *q)
2376191771Sobrien			break;
2377186691Sobrien
2378191771Sobrien	/* Did not find .mgc, restore q */
2379191771Sobrien	if (p >= ext)
2380191771Sobrien		while (*q)
2381191771Sobrien			q++;
2382191771Sobrien
2383191771Sobrien	q++;
2384191771Sobrien	/* Compatibility with old code that looked in .mime */
2385191771Sobrien	if (ms->flags & MAGIC_MIME) {
2386191771Sobrien		asprintf(&buf, "%.*s.mime%s", (int)(q - fn), fn, ext);
2387191771Sobrien		if (access(buf, R_OK) != -1) {
2388191771Sobrien			ms->flags &= MAGIC_MIME_TYPE;
2389191771Sobrien			return buf;
2390191771Sobrien		}
2391191771Sobrien		free(buf);
2392186691Sobrien	}
2393191771Sobrien	asprintf(&buf, "%.*s%s", (int)(q - fn), fn, ext);
2394191771Sobrien
2395191771Sobrien	/* Compatibility with old code that looked in .mime */
2396191771Sobrien	if (strstr(p, ".mime") != NULL)
2397191771Sobrien		ms->flags &= MAGIC_MIME_TYPE;
2398191771Sobrien	return buf;
239974784Sobrien}
240074784Sobrien
2401210761Srpaulo#ifndef COMPILE_ONLY
240274784Sobrien/*
240374784Sobrien * Byteswap an mmap'ed file if needed
240474784Sobrien */
2405133359Sobrienprivate void
2406103373Sobrienbyteswap(struct magic *magic, uint32_t nmagic)
240774784Sobrien{
2408103373Sobrien	uint32_t i;
240974784Sobrien	for (i = 0; i < nmagic; i++)
241074784Sobrien		bs1(&magic[i]);
241174784Sobrien}
241274784Sobrien
241374784Sobrien/*
241474784Sobrien * swap a short
241574784Sobrien */
2416133359Sobrienprivate uint16_t
2417103373Sobrienswap2(uint16_t sv)
241874784Sobrien{
2419103373Sobrien	uint16_t rv;
2420133359Sobrien	uint8_t *s = (uint8_t *)(void *)&sv;
2421133359Sobrien	uint8_t *d = (uint8_t *)(void *)&rv;
242274784Sobrien	d[0] = s[1];
242374784Sobrien	d[1] = s[0];
242474784Sobrien	return rv;
242574784Sobrien}
242674784Sobrien
242774784Sobrien/*
242874784Sobrien * swap an int
242974784Sobrien */
2430133359Sobrienprivate uint32_t
2431103373Sobrienswap4(uint32_t sv)
243274784Sobrien{
2433103373Sobrien	uint32_t rv;
2434133359Sobrien	uint8_t *s = (uint8_t *)(void *)&sv;
2435133359Sobrien	uint8_t *d = (uint8_t *)(void *)&rv;
243674784Sobrien	d[0] = s[3];
243774784Sobrien	d[1] = s[2];
243874784Sobrien	d[2] = s[1];
243974784Sobrien	d[3] = s[0];
244074784Sobrien	return rv;
244174784Sobrien}
244274784Sobrien
244374784Sobrien/*
2444169942Sobrien * swap a quad
2445169942Sobrien */
2446169942Sobrienprivate uint64_t
2447169942Sobrienswap8(uint64_t sv)
2448169942Sobrien{
2449186691Sobrien	uint64_t rv;
2450169942Sobrien	uint8_t *s = (uint8_t *)(void *)&sv;
2451169942Sobrien	uint8_t *d = (uint8_t *)(void *)&rv;
2452186691Sobrien#if 0
2453169942Sobrien	d[0] = s[3];
2454169942Sobrien	d[1] = s[2];
2455169942Sobrien	d[2] = s[1];
2456169942Sobrien	d[3] = s[0];
2457169942Sobrien	d[4] = s[7];
2458169942Sobrien	d[5] = s[6];
2459169942Sobrien	d[6] = s[5];
2460169942Sobrien	d[7] = s[4];
2461186691Sobrien#else
2462186691Sobrien	d[0] = s[7];
2463186691Sobrien	d[1] = s[6];
2464186691Sobrien	d[2] = s[5];
2465186691Sobrien	d[3] = s[4];
2466186691Sobrien	d[4] = s[3];
2467186691Sobrien	d[5] = s[2];
2468186691Sobrien	d[6] = s[1];
2469186691Sobrien	d[7] = s[0];
2470186691Sobrien#endif
2471169942Sobrien	return rv;
2472169942Sobrien}
2473169942Sobrien
2474169942Sobrien/*
247574784Sobrien * byteswap a single magic entry
247674784Sobrien */
2477133359Sobrienprivate void
2478133359Sobrienbs1(struct magic *m)
247974784Sobrien{
248074784Sobrien	m->cont_level = swap2(m->cont_level);
2481133359Sobrien	m->offset = swap4((uint32_t)m->offset);
2482133359Sobrien	m->in_offset = swap4((uint32_t)m->in_offset);
2483169962Sobrien	m->lineno = swap4((uint32_t)m->lineno);
2484169962Sobrien	if (IS_STRING(m->type)) {
2485186691Sobrien		m->str_range = swap4(m->str_range);
2486169962Sobrien		m->str_flags = swap4(m->str_flags);
2487169962Sobrien	}
2488169962Sobrien	else {
2489169942Sobrien		m->value.q = swap8(m->value.q);
2490169962Sobrien		m->num_mask = swap8(m->num_mask);
2491169962Sobrien	}
249274784Sobrien}
2493210761Srpaulo#endif /* COMPILE_ONLY */
2494234449Sobrien
2495234449Sobrienprotected size_t
2496234449Sobrienfile_pstring_length_size(const struct magic *m)
2497234449Sobrien{
2498234449Sobrien	switch (m->str_flags & PSTRING_LEN) {
2499234449Sobrien	case PSTRING_1_LE:
2500234449Sobrien		return 1;
2501234449Sobrien	case PSTRING_2_LE:
2502234449Sobrien	case PSTRING_2_BE:
2503234449Sobrien		return 2;
2504234449Sobrien	case PSTRING_4_LE:
2505234449Sobrien	case PSTRING_4_BE:
2506234449Sobrien		return 4;
2507234449Sobrien	default:
2508234449Sobrien		abort();	/* Impossible */
2509234449Sobrien		return 1;
2510234449Sobrien	}
2511234449Sobrien}
2512234449Sobrienprotected size_t
2513234449Sobrienfile_pstring_get_length(const struct magic *m, const char *s)
2514234449Sobrien{
2515234449Sobrien	size_t len = 0;
2516234449Sobrien
2517234449Sobrien	switch (m->str_flags & PSTRING_LEN) {
2518234449Sobrien	case PSTRING_1_LE:
2519234449Sobrien		len = *s;
2520234449Sobrien		break;
2521234449Sobrien	case PSTRING_2_LE:
2522234449Sobrien		len = (s[1] << 8) | s[0];
2523234449Sobrien		break;
2524234449Sobrien	case PSTRING_2_BE:
2525234449Sobrien		len = (s[0] << 8) | s[1];
2526234449Sobrien		break;
2527234449Sobrien	case PSTRING_4_LE:
2528234449Sobrien		len = (s[3] << 24) | (s[2] << 16) | (s[1] << 8) | s[0];
2529234449Sobrien		break;
2530234449Sobrien	case PSTRING_4_BE:
2531234449Sobrien		len = (s[0] << 24) | (s[1] << 16) | (s[2] << 8) | s[3];
2532234449Sobrien		break;
2533234449Sobrien	default:
2534234449Sobrien		abort();	/* Impossible */
2535234449Sobrien	}
2536234449Sobrien
2537234449Sobrien	if (m->str_flags & PSTRING_LENGTH_INCLUDES_ITSELF)
2538234449Sobrien		len -= file_pstring_length_size(m);
2539234449Sobrien
2540234449Sobrien	return len;
2541234449Sobrien}
2542