xref: /openbsd-src/usr.bin/file/magic-test.c (revision cb39b41371628601fbe4c618205356d538b9d08a)
1 /* $OpenBSD: magic-test.c,v 1.3 2015/04/25 16:35:47 brynet Exp $ */
2 
3 /*
4  * Copyright (c) 2015 Nicholas Marriott <nicm@openbsd.org>
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
15  * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
16  * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 #include <sys/types.h>
20 
21 #include <ctype.h>
22 #include <errno.h>
23 #include <fcntl.h>
24 #include <limits.h>
25 #include <stdarg.h>
26 #include <stdio.h>
27 #include <stdint.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <unistd.h>
31 #include <vis.h>
32 
33 #include "magic.h"
34 #include "xmalloc.h"
35 
36 static int
37 magic_one_eq(char a, char b, int cflag)
38 {
39 	if (a == b)
40 		return (1);
41 	if (cflag && tolower((u_char)a) == tolower((u_char)b))
42 		return (1);
43 	return (0);
44 }
45 
46 static int
47 magic_test_eq(const char *ap, size_t asize, const char *bp, size_t bsize,
48     int cflag, int bflag, int Bflag)
49 {
50 	size_t	aoff, boff, aspaces, bspaces;
51 
52 	aoff = boff = 0;
53 	while (aoff != asize && boff != bsize) {
54 		if (Bflag && isspace((u_char)ap[aoff])) {
55 			aspaces = 0;
56 			while (aoff != asize && isspace((u_char)ap[aoff])) {
57 				aspaces++;
58 				aoff++;
59 			}
60 			bspaces = 0;
61 			while (boff != bsize && isspace((u_char)bp[boff])) {
62 				bspaces++;
63 				boff++;
64 			}
65 			if (bspaces >= aspaces)
66 				continue;
67 			return (1);
68 		}
69 		if (magic_one_eq(ap[aoff], bp[boff], cflag)) {
70 			aoff++;
71 			boff++;
72 			continue;
73 		}
74 		if (bflag && isspace((u_char)bp[boff])) {
75 			boff++;
76 			continue;
77 		}
78 		if (ap[aoff] < bp[boff])
79 			return (-1);
80 		return (1);
81 	}
82 	return (0);
83 }
84 
85 static int
86 magic_copy_from(struct magic_state *ms, ssize_t offset, void *dst, size_t size)
87 {
88 	if (offset < 0)
89 		offset = ms->offset;
90 	if (offset + size > ms->size)
91 		return (-1);
92 	memcpy(dst, ms->base + offset, size);
93 	return (0);
94 }
95 
96 static void
97 magic_add_result(struct magic_state *ms, struct magic_line *ml,
98     const char *fmt, ...)
99 {
100 	va_list	 ap;
101 	int	 separate;
102 	char	*s, *tmp, *add;
103 
104 	va_start(ap, fmt);
105 	if (ml->stringify) {
106 		if (vasprintf(&s, fmt, ap) == -1) {
107 			va_end(ap);
108 			return;
109 		}
110 		va_end(ap);
111 		if (asprintf(&tmp, ml->result, s) == -1) {
112 			free(s);
113 			return;
114 		}
115 		free(s);
116 	} else {
117 		if (vasprintf(&tmp, ml->result, ap) == -1) {
118 			va_end(ap);
119 			return;
120 		}
121 		va_end(ap);
122 	}
123 
124 	separate = 1;
125 	if (tmp[0] == '\\' && tmp[1] == 'b') {
126 		separate = 0;
127 		add = tmp + 2;
128 	} else
129 		add = tmp;
130 
131 	if (separate && *ms->out != '\0')
132 		strlcat(ms->out, " ", sizeof ms->out);
133 	strlcat(ms->out, add, sizeof ms->out);
134 
135 	free(tmp);
136 }
137 
138 static void
139 magic_add_string(struct magic_state *ms, struct magic_line *ml,
140     const char *s, size_t slen)
141 {
142 	char	*out;
143 	size_t	 outlen, offset;
144 
145 	outlen = MAGIC_STRING_SIZE;
146 	if (outlen > slen)
147 		outlen = slen;
148 	for (offset = 0; offset < outlen; offset++) {
149 		if (s[offset] == '\0' || !isprint((u_char)s[offset])) {
150 			outlen = offset;
151 			break;
152 		}
153 	}
154 	out = xreallocarray(NULL, 4, outlen + 1);
155 	strvisx(out, s, outlen, VIS_TAB|VIS_NL|VIS_CSTYLE|VIS_OCTAL);
156 	magic_add_result(ms, ml, "%s", out);
157 	free(out);
158 }
159 
160 static int
161 magic_test_signed(struct magic_line *ml, int64_t value, int64_t wanted)
162 {
163 	switch (ml->test_operator) {
164 	case 'x':
165 		return (1);
166 	case '<':
167 		return (value < wanted);
168 	case '[':
169 		return (value <= wanted);
170 	case '>':
171 		return (value > wanted);
172 	case ']':
173 		return (value >= wanted);
174 	case '=':
175 		return (value == wanted);
176 	case '&':
177 		return ((value & wanted) == wanted);
178 	case '^':
179 		return ((~value & wanted) == wanted);
180 	}
181 	return (-1);
182 }
183 
184 static int
185 magic_test_unsigned(struct magic_line *ml, uint64_t value, uint64_t wanted)
186 {
187 	switch (ml->test_operator) {
188 	case 'x':
189 		return (1);
190 	case '<':
191 		return (value < wanted);
192 	case '[':
193 		return (value <= wanted);
194 	case '>':
195 		return (value > wanted);
196 	case ']':
197 		return (value >= wanted);
198 	case '=':
199 		return (value == wanted);
200 	case '&':
201 		return ((value & wanted) == wanted);
202 	case '^':
203 		return ((~value & wanted) == wanted);
204 	}
205 	return (-1);
206 }
207 
208 static int
209 magic_test_type_none(__unused struct magic_line *ml,
210     __unused struct magic_state *ms)
211 {
212 	return (0);
213 }
214 
215 static int
216 magic_test_type_byte(struct magic_line *ml, struct magic_state *ms)
217 {
218 	int8_t	value;
219 	int	result;
220 
221 	if (magic_copy_from(ms, -1, &value, sizeof value) != 0)
222 		return (0);
223 
224 	if (ml->type_operator == '&')
225 		value &= (int8_t)ml->type_operand;
226 	else if (ml->type_operator != ' ')
227 		return (-1);
228 
229 	result = magic_test_signed(ml, value, (int8_t)ml->test_signed);
230 	if (result == !ml->test_not && ml->result != NULL) {
231 		magic_add_result(ms, ml, "%c", (int)value);
232 		ms->offset += sizeof value;
233 	}
234 	return (result);
235 }
236 
237 static int
238 magic_test_type_short(struct magic_line *ml, struct magic_state *ms)
239 {
240 	int16_t value;
241 	int	result;
242 
243 	if (magic_copy_from(ms, -1, &value, sizeof value) != 0)
244 		return (0);
245 	if (ml->type == MAGIC_TYPE_BESHORT)
246 		value = be16toh(value);
247 	if (ml->type == MAGIC_TYPE_LESHORT)
248 		value = le16toh(value);
249 
250 	if (ml->type_operator == '&')
251 		value &= (int16_t)ml->type_operand;
252 	else if (ml->type_operator != ' ')
253 		return (-1);
254 
255 	result = magic_test_signed(ml, value, (int16_t)ml->test_signed);
256 	if (result == !ml->test_not && ml->result != NULL) {
257 		magic_add_result(ms, ml, "%hd", (int)value);
258 		ms->offset += sizeof value;
259 	}
260 	return (result);
261 }
262 
263 static int
264 magic_test_type_long(struct magic_line *ml, struct magic_state *ms)
265 {
266 	int32_t value;
267 	int	result;
268 
269 	if (magic_copy_from(ms, -1, &value, sizeof value) != 0)
270 		return (0);
271 	if (ml->type == MAGIC_TYPE_BELONG)
272 		value = be32toh(value);
273 	if (ml->type == MAGIC_TYPE_LELONG)
274 		value = le32toh(value);
275 
276 	if (ml->type_operator == '&')
277 		value &= (int32_t)ml->type_operand;
278 	else if (ml->type_operator != ' ')
279 		return (-1);
280 
281 	result = magic_test_signed(ml, value, (int32_t)ml->test_signed);
282 	if (result == !ml->test_not && ml->result != NULL) {
283 		magic_add_result(ms, ml, "%d", (int)value);
284 		ms->offset += sizeof value;
285 	}
286 	return (result);
287 }
288 
289 static int
290 magic_test_type_quad(struct magic_line *ml, struct magic_state *ms)
291 {
292 	int64_t value;
293 	int	result;
294 
295 	if (magic_copy_from(ms, -1, &value, sizeof value) != 0)
296 		return (0);
297 	if (ml->type == MAGIC_TYPE_BEQUAD)
298 		value = be64toh(value);
299 	if (ml->type == MAGIC_TYPE_LEQUAD)
300 		value = le64toh(value);
301 
302 	if (ml->type_operator == '&')
303 		value &= (int64_t)ml->type_operand;
304 	else if (ml->type_operator != ' ')
305 		return (-1);
306 
307 	result = magic_test_signed(ml, value, (int64_t)ml->test_signed);
308 	if (result == !ml->test_not && ml->result != NULL) {
309 		magic_add_result(ms, ml, "%lld", (long long)value);
310 		ms->offset += sizeof value;
311 	}
312 	return (result);
313 }
314 
315 static int
316 magic_test_type_ubyte(struct magic_line *ml, struct magic_state *ms)
317 {
318 	uint8_t value;
319 	int	result;
320 
321 	if (magic_copy_from(ms, -1, &value, sizeof value) != 0)
322 		return (0);
323 
324 	if (ml->type_operator == '&')
325 		value &= (uint8_t)ml->type_operand;
326 	else if (ml->type_operator != ' ')
327 		return (-1);
328 
329 	result = magic_test_unsigned(ml, value, (uint8_t)ml->test_unsigned);
330 	if (result == !ml->test_not && ml->result != NULL) {
331 		magic_add_result(ms, ml, "%c", (unsigned int)value);
332 		ms->offset += sizeof value;
333 	}
334 	return (result);
335 }
336 
337 static int
338 magic_test_type_ushort(struct magic_line *ml, struct magic_state *ms)
339 {
340 	uint16_t	value;
341 	int		result;
342 
343 	if (magic_copy_from(ms, -1, &value, sizeof value) != 0)
344 		return (0);
345 	if (ml->type == MAGIC_TYPE_UBESHORT)
346 		value = be16toh(value);
347 	if (ml->type == MAGIC_TYPE_ULESHORT)
348 		value = le16toh(value);
349 
350 	if (ml->type_operator == '&')
351 		value &= (uint16_t)ml->type_operand;
352 	else if (ml->type_operator != ' ')
353 		return (-1);
354 
355 	result = magic_test_unsigned(ml, value, (uint16_t)ml->test_unsigned);
356 	if (result == !ml->test_not && ml->result != NULL) {
357 		magic_add_result(ms, ml, "%hu", (unsigned int)value);
358 		ms->offset += sizeof value;
359 	}
360 	return (result);
361 }
362 
363 static int
364 magic_test_type_ulong(struct magic_line *ml, struct magic_state *ms)
365 {
366 	uint32_t	value;
367 	int		result;
368 
369 	if (magic_copy_from(ms, -1, &value, sizeof value) != 0)
370 		return (0);
371 	if (ml->type == MAGIC_TYPE_UBELONG)
372 		value = be32toh(value);
373 	if (ml->type == MAGIC_TYPE_ULELONG)
374 		value = le32toh(value);
375 
376 	if (ml->type_operator == '&')
377 		value &= (uint32_t)ml->type_operand;
378 	else if (ml->type_operator != ' ')
379 		return (-1);
380 
381 	result = magic_test_unsigned(ml, value, (uint32_t)ml->test_unsigned);
382 	if (result == !ml->test_not && ml->result != NULL) {
383 		magic_add_result(ms, ml, "%u", (unsigned int)value);
384 		ms->offset += sizeof value;
385 	}
386 	return (result);
387 }
388 
389 static int
390 magic_test_type_uquad(struct magic_line *ml, struct magic_state *ms)
391 {
392 	uint64_t	value;
393 	int		result;
394 
395 	if (magic_copy_from(ms, -1, &value, sizeof value) != 0)
396 		return (0);
397 	if (ml->type == MAGIC_TYPE_UBEQUAD)
398 		value = be64toh(value);
399 	if (ml->type == MAGIC_TYPE_ULEQUAD)
400 		value = le64toh(value);
401 
402 	if (ml->type_operator == '&')
403 		value &= (uint64_t)ml->type_operand;
404 	else if (ml->type_operator != ' ')
405 		return (-1);
406 
407 	result = magic_test_unsigned(ml, value, (uint64_t)ml->test_unsigned);
408 	if (result == !ml->test_not && ml->result != NULL) {
409 		magic_add_result(ms, ml, "%llu", (unsigned long long)value);
410 		ms->offset += sizeof value;
411 	}
412 	return (result);
413 }
414 
415 static int
416 magic_test_type_float(struct magic_line *ml, struct magic_state *ms)
417 {
418 	uint32_t	value0;
419 	double		value;
420 
421 	if (magic_copy_from(ms, -1, &value0, sizeof value0) != 0)
422 		return (0);
423 	if (ml->type == MAGIC_TYPE_BEFLOAT)
424 		value0 = be32toh(value0);
425 	if (ml->type == MAGIC_TYPE_LEFLOAT)
426 		value0 = le32toh(value0);
427 	memcpy(&value, &value0, sizeof value);
428 
429 	if (ml->type_operator != ' ')
430 		return (-1);
431 
432 	if (ml->test_operator != 'x')
433 		return (-1);
434 
435 	magic_add_result(ms, ml, "%g", value);
436 	ms->offset += sizeof value0;
437 	return (1);
438 }
439 
440 static int
441 magic_test_type_double(struct magic_line *ml, struct magic_state *ms)
442 {
443 	uint64_t	value0;
444 	double		value;
445 
446 	if (magic_copy_from(ms, -1, &value0, sizeof value0) != 0)
447 		return (0);
448 	if (ml->type == MAGIC_TYPE_BEDOUBLE)
449 		value0 = be64toh(value0);
450 	if (ml->type == MAGIC_TYPE_LEDOUBLE)
451 		value0 = le64toh(value0);
452 	memcpy(&value, &value0, sizeof value);
453 
454 	if (ml->type_operator != ' ')
455 		return (-1);
456 
457 	if (ml->test_operator != 'x')
458 		return (-1);
459 
460 	magic_add_result(ms, ml, "%g", value);
461 	ms->offset += sizeof value0;
462 	return (1);
463 }
464 
465 static int
466 magic_test_type_string(struct magic_line *ml, struct magic_state *ms)
467 {
468 	const char	*s, *cp;
469 	size_t		 slen;
470 	int		 result, cflag = 0, bflag = 0, Bflag = 0;
471 
472 	cp = &ml->type_string[(sizeof "string") - 1];
473 	if (*cp != '\0') {
474 		if (*cp != '/')
475 			return (-1);
476 		cp++;
477 		for (; *cp != '\0'; cp++) {
478 			switch (*cp) {
479 			case 'B':
480 				Bflag = 1;
481 				break;
482 			case 'b':
483 				bflag = 1;
484 				break;
485 			case 'c':
486 				cflag = 1;
487 				break;
488 			default:
489 				return (-1);
490 			}
491 		}
492 	}
493 
494 	s = ms->base + ms->offset;
495 	slen = ms->size - ms->offset;
496 	if (slen < ml->test_string_size)
497 		return (0);
498 
499 	result = magic_test_eq(s, slen, ml->test_string, ml->test_string_size,
500 	    cflag, bflag, Bflag);
501 	switch (ml->test_operator) {
502 	case 'x':
503 		result = 1;
504 		break;
505 	case '<':
506 		result = result < 0;
507 		break;
508 	case '>':
509 		result = result > 0;
510 		break;
511 	case '=':
512 		result = result == 0;
513 		break;
514 	default:
515 		result = -1;
516 		break;
517 	}
518 	if (result == !ml->test_not) {
519 		if (ml->result != NULL)
520 			magic_add_string(ms, ml, s, slen);
521 		if (result && ml->test_operator == '=')
522 			ms->offset = s - ms->base + ml->test_string_size;
523 	}
524 	return (result);
525 }
526 
527 static int
528 magic_test_type_pstring(struct magic_line *ml, struct magic_state *ms)
529 {
530 	const char	*s;
531 	size_t		 slen;
532 	int		 result;
533 
534 	s = ms->base + ms->offset;
535 	if (ms->size - ms->offset < 1)
536 		return (-1);
537 	slen = *(u_char *)s;
538 	if (slen > ms->size - ms->offset)
539 		return (-1);
540 	s++;
541 
542 	if (slen < ml->test_string_size)
543 		result = -1;
544 	else if (slen > ml->test_string_size)
545 		result = 1;
546 	else
547 		result = memcmp(s, ml->test_string, ml->test_string_size);
548 	switch (ml->test_operator) {
549 	case 'x':
550 		result = 1;
551 		break;
552 	case '<':
553 		result = result < 0;
554 		break;
555 	case '>':
556 		result = result > 0;
557 		break;
558 	case '=':
559 		result = result == 0;
560 		break;
561 	default:
562 		result = -1;
563 		break;
564 	}
565 	if (result == !ml->test_not) {
566 		if (ml->result != NULL)
567 			magic_add_string(ms, ml, s, slen);
568 		if (result)
569 			ms->offset += slen + 1;
570 	}
571 	return (result);
572 }
573 
574 static int
575 magic_test_type_date(struct magic_line *ml, struct magic_state *ms)
576 {
577 	int32_t	value;
578 	int	result;
579 	time_t	t;
580 	char	s[64];
581 
582 	if (magic_copy_from(ms, -1, &value, sizeof value) != 0)
583 		return (0);
584 	if (ml->type == MAGIC_TYPE_BEDATE ||
585 	    ml->type == MAGIC_TYPE_BELDATE)
586 		value = be32toh(value);
587 	if (ml->type == MAGIC_TYPE_LEDATE ||
588 	    ml->type == MAGIC_TYPE_LELDATE)
589 		value = le32toh(value);
590 
591 	if (ml->type_operator == '&')
592 		value &= (int32_t)ml->type_operand;
593 	else if (ml->type_operator != ' ')
594 		return (-1);
595 
596 	result = magic_test_signed(ml, value, (int32_t)ml->test_signed);
597 	if (result == !ml->test_not && ml->result != NULL) {
598 		t = value;
599 		switch (ml->type) {
600 		case MAGIC_TYPE_LDATE:
601 		case MAGIC_TYPE_LELDATE:
602 		case MAGIC_TYPE_BELDATE:
603 			ctime_r(&t, s);
604 			break;
605 		default:
606 			asctime_r(localtime(&t), s);
607 			break;
608 		}
609 		s[strcspn(s, "\n")] = '\0';
610 		magic_add_result(ms, ml, "%s", s);
611 		ms->offset += sizeof value;
612 	}
613 	return (result);
614 }
615 
616 static int
617 magic_test_type_qdate(struct magic_line *ml, struct magic_state *ms)
618 {
619 	int64_t value;
620 	int	result;
621 	time_t	t;
622 	char	s[64];
623 
624 	if (magic_copy_from(ms, -1, &value, sizeof value) != 0)
625 		return (0);
626 	if (ml->type == MAGIC_TYPE_BEQDATE ||
627 	    ml->type == MAGIC_TYPE_BEQLDATE)
628 		value = be64toh(value);
629 	if (ml->type == MAGIC_TYPE_LEQDATE ||
630 	    ml->type == MAGIC_TYPE_LEQLDATE)
631 		value = le64toh(value);
632 
633 	if (ml->type_operator == '&')
634 		value &= (int64_t)ml->type_operand;
635 	else if (ml->type_operator != ' ')
636 		return (-1);
637 
638 	result = magic_test_signed(ml, value, (int64_t)ml->test_signed);
639 	if (result == !ml->test_not && ml->result != NULL) {
640 		t = value;
641 		switch (ml->type) {
642 		case MAGIC_TYPE_QLDATE:
643 		case MAGIC_TYPE_LEQLDATE:
644 		case MAGIC_TYPE_BEQLDATE:
645 			ctime_r(&t, s);
646 			break;
647 		default:
648 			asctime_r(localtime(&t), s);
649 			break;
650 		}
651 		s[strcspn(s, "\n")] = '\0';
652 		magic_add_result(ms, ml, "%s", s);
653 		ms->offset += sizeof value;
654 	}
655 	return (result);
656 }
657 
658 static int
659 magic_test_type_udate(struct magic_line *ml, struct magic_state *ms)
660 {
661 	uint32_t	value;
662 	int		result;
663 	time_t		t;
664 	char		s[64];
665 
666 	if (magic_copy_from(ms, -1, &value, sizeof value) != 0)
667 		return (0);
668 	if (ml->type == MAGIC_TYPE_BEDATE ||
669 	    ml->type == MAGIC_TYPE_BELDATE)
670 		value = be32toh(value);
671 	if (ml->type == MAGIC_TYPE_LEDATE ||
672 	    ml->type == MAGIC_TYPE_LELDATE)
673 		value = le32toh(value);
674 
675 	if (ml->type_operator == '&')
676 		value &= (uint32_t)ml->type_operand;
677 	else if (ml->type_operator != ' ')
678 		return (-1);
679 
680 	result = magic_test_unsigned(ml, value, (uint32_t)ml->test_unsigned);
681 	if (result == !ml->test_not && ml->result != NULL) {
682 		t = value;
683 		switch (ml->type) {
684 		case MAGIC_TYPE_LDATE:
685 		case MAGIC_TYPE_LELDATE:
686 		case MAGIC_TYPE_BELDATE:
687 			ctime_r(&t, s);
688 			break;
689 		default:
690 			asctime_r(gmtime(&t), s);
691 			break;
692 		}
693 		s[strcspn(s, "\n")] = '\0';
694 		magic_add_result(ms, ml, "%s", s);
695 		ms->offset += sizeof value;
696 	}
697 	return (result);
698 }
699 
700 static int
701 magic_test_type_uqdate(struct magic_line *ml, struct magic_state *ms)
702 {
703 	uint64_t	value;
704 	int		result;
705 	time_t		t;
706 	char		s[64];
707 
708 	if (magic_copy_from(ms, -1, &value, sizeof value) != 0)
709 		return (0);
710 	if (ml->type == MAGIC_TYPE_UBEQDATE ||
711 	    ml->type == MAGIC_TYPE_UBEQLDATE)
712 		value = be64toh(value);
713 	if (ml->type == MAGIC_TYPE_ULEQDATE ||
714 	    ml->type == MAGIC_TYPE_ULEQLDATE)
715 		value = le64toh(value);
716 
717 	if (ml->type_operator == '&')
718 		value &= (uint64_t)ml->type_operand;
719 	else if (ml->type_operator != ' ')
720 		return (-1);
721 
722 	result = magic_test_unsigned(ml, value, (uint64_t)ml->test_unsigned);
723 	if (result == !ml->test_not && ml->result != NULL) {
724 		t = value;
725 		switch (ml->type) {
726 		case MAGIC_TYPE_UQLDATE:
727 		case MAGIC_TYPE_ULEQLDATE:
728 		case MAGIC_TYPE_UBEQLDATE:
729 			ctime_r(&t, s);
730 			break;
731 		default:
732 			asctime_r(gmtime(&t), s);
733 			break;
734 		}
735 		s[strcspn(s, "\n")] = '\0';
736 		magic_add_result(ms, ml, "%s", s);
737 		ms->offset += sizeof value;
738 	}
739 	return (result);
740 }
741 
742 static int
743 magic_test_type_bestring16(__unused struct magic_line *ml,
744     __unused struct magic_state *ms)
745 {
746 	return (-2);
747 }
748 
749 static int
750 magic_test_type_lestring16(__unused struct magic_line *ml,
751     __unused struct magic_state *ms)
752 {
753 	return (-2);
754 }
755 
756 static int
757 magic_test_type_melong(__unused struct magic_line *ml,
758     __unused struct magic_state *ms)
759 {
760 	return (-2);
761 }
762 
763 static int
764 magic_test_type_medate(__unused struct magic_line *ml,
765     __unused struct magic_state *ms)
766 {
767 	return (-2);
768 }
769 
770 static int
771 magic_test_type_meldate(__unused struct magic_line *ml,
772     __unused struct magic_state *ms)
773 {
774 	return (-2);
775 }
776 
777 static int
778 magic_test_type_regex(struct magic_line *ml, struct magic_state *ms)
779 {
780 	const char	*cp;
781 	regex_t		 re;
782 	regmatch_t	 m;
783 	int		 result, flags = 0, sflag = 0;
784 
785 	cp = &ml->type_string[(sizeof "regex") - 1];
786 	if (*cp != '\0') {
787 		if (*cp != '/')
788 			return (-1);
789 		cp++;
790 		for (; *cp != '\0'; cp++) {
791 			switch (*cp) {
792 			case 's':
793 				sflag = 1;
794 				break;
795 			case 'c':
796 				flags |= REG_ICASE;
797 				break;
798 			default:
799 				return (-1);
800 			}
801 		}
802 	}
803 
804 	if (regcomp(&re, ml->test_string, REG_EXTENDED) != 0)
805 		return (-1);
806 	m.rm_so = ms->offset;
807 	m.rm_eo = ms->size;
808 
809 	result = (regexec(&re, ms->base, 1, &m, REG_STARTEND) == 0);
810 	if (result == !ml->test_not && ml->result != NULL) {
811 		magic_add_result(ms, ml, "%s", "");
812 		if (result) {
813 			if (sflag)
814 				ms->offset = m.rm_so;
815 			else
816 				ms->offset = m.rm_eo;
817 		}
818 	}
819 	regfree(&re);
820 	return (result);
821 }
822 
823 static int
824 magic_test_type_search(struct magic_line *ml, struct magic_state *ms)
825 {
826 	const char	*cp, *endptr, *start, *found;
827 	size_t		 size, end, i;
828 	uint64_t	 range;
829 	int		 result, n, cflag = 0, bflag = 0, Bflag = 0;
830 
831 	cp = &ml->type_string[(sizeof "search") - 1];
832 	if (*cp != '\0') {
833 		if (*cp != '/')
834 			return (-1);
835 		cp++;
836 
837 		endptr = magic_strtoull(cp, &range);
838 		if (endptr == NULL || (*endptr != '/' && *endptr != '\0'))
839 			return (-1);
840 
841 		if (*endptr == '/') {
842 			for (cp = endptr + 1; *cp != '\0'; cp++) {
843 				switch (*cp) {
844 				case 'B':
845 					Bflag = 1;
846 					break;
847 				case 'b':
848 					bflag = 1;
849 					break;
850 				case 'c':
851 					cflag = 1;
852 					break;
853 				default:
854 					return (-1);
855 				}
856 			}
857 		}
858 	} else
859 		range = UINT64_MAX;
860 	if (range > (uint64_t)ms->size - ms->offset)
861 		range = ms->size - ms->offset;
862 	size = ml->test_string_size;
863 
864 	/* Want to search every starting position from up to range + size. */
865 	end = range + size;
866 	if (end > ms->size - ms->offset) {
867 		if (size > ms->size - ms->offset)
868 			end = 0;
869 		else
870 			end = ms->size - ms->offset - size;
871 	}
872 
873 	/*
874 	 * < and > and the flags are only in /etc/magic with search/1 so don't
875 	 * support them with anything else.
876 	 */
877 	start = ms->base + ms->offset;
878 	if (end == 0)
879 		found = NULL;
880 	else if (ml->test_operator == 'x')
881 		found = start;
882 	else if (range == 1) {
883 		n = magic_test_eq(start, ms->size - ms->offset, ml->test_string,
884 		    size, cflag, bflag, Bflag);
885 		if (n == -1 && ml->test_operator == '<')
886 			found = start;
887 		else if (n == 1 && ml->test_operator == '>')
888 			found = start;
889 		else if (n == 0 && ml->test_operator == '=')
890 			found = start;
891 		else
892 			found = NULL;
893 	} else {
894 		if (ml->test_operator != '=')
895 			return (-2);
896 		for (i = 0; i < end; i++) {
897 			n = magic_test_eq(start + i, ms->size - ms->offset - i,
898 			    ml->test_string, size, cflag, bflag, Bflag);
899 			if (n == 0) {
900 				found = start + i;
901 				break;
902 			}
903 		}
904 		if (i == end)
905 			found = NULL;
906 	}
907 	result = (found != NULL);
908 
909 	if (result == !ml->test_not && ml->result != NULL && found != NULL) {
910 		magic_add_string(ms, ml, found, ms->size - ms->offset);
911 		ms->offset = found - start + size;
912 	}
913 	return (result);
914 }
915 
916 static int
917 magic_test_type_default(__unused struct magic_line *ml,
918     __unused struct magic_state *ms)
919 {
920 	return (1);
921 }
922 
923 static int (*magic_test_functions[])(struct magic_line *,
924     struct magic_state *) = {
925 	magic_test_type_none,
926 	magic_test_type_byte,
927 	magic_test_type_short,
928 	magic_test_type_long,
929 	magic_test_type_quad,
930 	magic_test_type_ubyte,
931 	magic_test_type_ushort,
932 	magic_test_type_ulong,
933 	magic_test_type_uquad,
934 	magic_test_type_float,
935 	magic_test_type_double,
936 	magic_test_type_string,
937 	magic_test_type_pstring,
938 	magic_test_type_date,
939 	magic_test_type_qdate,
940 	magic_test_type_date,
941 	magic_test_type_qdate,
942 	magic_test_type_udate,
943 	magic_test_type_uqdate,
944 	magic_test_type_udate,
945 	magic_test_type_qdate,
946 	magic_test_type_short,
947 	magic_test_type_long,
948 	magic_test_type_quad,
949 	magic_test_type_ushort,
950 	magic_test_type_ulong,
951 	magic_test_type_uquad,
952 	magic_test_type_float,
953 	magic_test_type_double,
954 	magic_test_type_date,
955 	magic_test_type_qdate,
956 	magic_test_type_date,
957 	magic_test_type_qdate,
958 	magic_test_type_udate,
959 	magic_test_type_uqdate,
960 	magic_test_type_udate,
961 	magic_test_type_uqdate,
962 	magic_test_type_bestring16,
963 	magic_test_type_short,
964 	magic_test_type_long,
965 	magic_test_type_quad,
966 	magic_test_type_ushort,
967 	magic_test_type_ulong,
968 	magic_test_type_uquad,
969 	magic_test_type_float,
970 	magic_test_type_double,
971 	magic_test_type_date,
972 	magic_test_type_qdate,
973 	magic_test_type_date,
974 	magic_test_type_qdate,
975 	magic_test_type_udate,
976 	magic_test_type_uqdate,
977 	magic_test_type_udate,
978 	magic_test_type_uqdate,
979 	magic_test_type_lestring16,
980 	magic_test_type_melong,
981 	magic_test_type_medate,
982 	magic_test_type_meldate,
983 	magic_test_type_regex,
984 	magic_test_type_search,
985 	magic_test_type_default,
986 };
987 
988 static int
989 magic_test_line(struct magic_line *ml, struct magic_state *ms)
990 {
991 	struct magic_line	*child;
992 	int64_t			 offset, wanted, next;
993 	int			 result;
994 	uint8_t			 b;
995 	uint16_t		 s;
996 	uint32_t		 l;
997 
998 	if (ml->indirect_type == ' ')
999 		wanted = ml->offset;
1000 	else {
1001 		wanted = ml->indirect_offset;
1002 		if (ml->indirect_relative) {
1003 			if (wanted < 0 && -wanted > ms->offset)
1004 				return (0);
1005 			if (wanted > 0 && ms->offset + wanted > ms->size)
1006 				return (0);
1007 			next = ms->offset + ml->indirect_offset;
1008 		} else
1009 			next = wanted;
1010 
1011 		switch (ml->indirect_type) {
1012 		case 'b':
1013 		case 'B':
1014 			if (magic_copy_from(ms, next, &b, sizeof b) != 0)
1015 				return (0);
1016 			wanted = b;
1017 			break;
1018 		case 's':
1019 			if (magic_copy_from(ms, next, &s, sizeof s) != 0)
1020 				return (0);
1021 			wanted = le16toh(s);
1022 			break;
1023 		case 'S':
1024 			if (magic_copy_from(ms, next, &s, sizeof s) != 0)
1025 				return (0);
1026 			wanted = be16toh(s);
1027 			break;
1028 		case 'l':
1029 			if (magic_copy_from(ms, next, &l, sizeof l) != 0)
1030 				return (0);
1031 			wanted = le16toh(l);
1032 			break;
1033 		case 'L':
1034 			if (magic_copy_from(ms, next, &l, sizeof l) != 0)
1035 				return (0);
1036 			wanted = be16toh(l);
1037 			break;
1038 		}
1039 
1040 		switch (ml->indirect_operator) {
1041 		case '+':
1042 			wanted += ml->indirect_operand;
1043 			break;
1044 		case '-':
1045 			wanted -= ml->indirect_operand;
1046 			break;
1047 		case '*':
1048 			wanted *= ml->indirect_operand;
1049 			break;
1050 		}
1051 	}
1052 
1053 	if (ml->offset_relative) {
1054 		if (wanted < 0 && -wanted > ms->offset)
1055 			return (0);
1056 		if (wanted > 0 && ms->offset + wanted > ms->size)
1057 			return (0);
1058 		offset = ms->offset + wanted;
1059 	} else
1060 		offset = wanted;
1061 	if (offset < 0 || offset > ms->size)
1062 		return (0);
1063 	ms->offset = offset;
1064 
1065 	result = magic_test_functions[ml->type](ml, ms);
1066 	if (result == -1) {
1067 		magic_warn(ml, "test %s/%c failed", ml->type_string,
1068 		    ml->test_operator);
1069 		return (0);
1070 	}
1071 	if (result == -2) {
1072 		magic_warn(ml, "test %s/%c not implemented", ml->type_string,
1073 		    ml->test_operator);
1074 		return (0);
1075 	}
1076 	if (result == ml->test_not)
1077 		return (0);
1078 	if (ml->mimetype != NULL)
1079 		ms->mimetype = ml->mimetype;
1080 
1081 	magic_warn(ml, "test %s/%c matched at offset %llu: '%s'",
1082 	    ml->type_string, ml->test_operator, ms->offset,
1083 	    ml->result == NULL ? "" : ml->result);
1084 
1085 	offset = ms->offset;
1086 	TAILQ_FOREACH(child, &ml->children, entry) {
1087 		ms->offset = offset;
1088 		magic_test_line(child, ms);
1089 	}
1090 	return (1);
1091 }
1092 
1093 const char *
1094 magic_test(struct magic *m, const void *base, size_t size, int flags)
1095 {
1096 	struct magic_line		*ml;
1097 	static struct magic_state	 ms;
1098 
1099 	memset(&ms, 0, sizeof ms);
1100 
1101 	ms.base = base;
1102 	ms.size = size;
1103 
1104 	ms.text = !!(flags & MAGIC_TEST_TEXT);
1105 
1106 	RB_FOREACH(ml, magic_tree, &m->tree) {
1107 		ms.offset = 0;
1108 		if (ml->text == ms.text && magic_test_line(ml, &ms))
1109 			break;
1110 	}
1111 
1112 	if (*ms.out != '\0') {
1113 		if (flags & MAGIC_TEST_MIME) {
1114 			if (ms.mimetype)
1115 				return (xstrdup(ms.mimetype));
1116 			return (NULL);
1117 		}
1118 		return (xstrdup(ms.out));
1119 	}
1120 	return (NULL);
1121 }
1122