Edit

IABSD.fr/src/usr.bin/file/magic-test.c

Branch :

  • Show log

    Commit

  • Author : nicm
    Date : 2019-01-15 09:24:59
    Hash : bb180e11
    Message : Regex flags should include REG_NEWLINE and REG_ICASE should actually be used if specified.

  • usr.bin/file/magic-test.c
  • /* $OpenBSD: magic-test.c,v 1.27 2019/01/15 09:24:59 nicm Exp $ */
    
    /*
     * Copyright (c) 2015 Nicholas Marriott <nicm@openbsd.org>
     *
     * Permission to use, copy, modify, and distribute this software for any
     * purpose with or without fee is hereby granted, provided that the above
     * copyright notice and this permission notice appear in all copies.
     *
     * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
     * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
     * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
     * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
     * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
     * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
     * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
     */
    
    #include <sys/types.h>
    
    #include <ctype.h>
    #include <errno.h>
    #include <fcntl.h>
    #include <limits.h>
    #include <stdarg.h>
    #include <stdio.h>
    #include <stdint.h>
    #include <stdlib.h>
    #include <string.h>
    #include <time.h>
    #include <unistd.h>
    #include <vis.h>
    
    #include "magic.h"
    #include "xmalloc.h"
    
    static int magic_test_line(struct magic_line *, struct magic_state *);
    
    static struct magic_line *
    magic_get_named(struct magic *m, const char *name)
    {
    	struct magic_line	ml;
    
    	ml.name = name;
    	return (RB_FIND(magic_named_tree, &m->named, &ml));
    }
    
    static enum magic_type
    magic_reverse_type(struct magic_state *ms, enum magic_type type)
    {
    	if (!ms->reverse)
    		return (type);
    	switch (type) {
    	case MAGIC_TYPE_BESHORT:
    		return (MAGIC_TYPE_LESHORT);
    	case MAGIC_TYPE_BELONG:
    		return (MAGIC_TYPE_LELONG);
    	case MAGIC_TYPE_BEQUAD:
    		return (MAGIC_TYPE_LEQUAD);
    	case MAGIC_TYPE_UBESHORT:
    		return (MAGIC_TYPE_ULESHORT);
    	case MAGIC_TYPE_UBELONG:
    		return (MAGIC_TYPE_ULELONG);
    	case MAGIC_TYPE_UBEQUAD:
    		return (MAGIC_TYPE_ULEQUAD);
    	case MAGIC_TYPE_BEFLOAT:
    		return (MAGIC_TYPE_LEFLOAT);
    	case MAGIC_TYPE_BEDOUBLE:
    		return (MAGIC_TYPE_LEDOUBLE);
    	case MAGIC_TYPE_BEDATE:
    		return (MAGIC_TYPE_LEDATE);
    	case MAGIC_TYPE_BEQDATE:
    		return (MAGIC_TYPE_LEQDATE);
    	case MAGIC_TYPE_BELDATE:
    		return (MAGIC_TYPE_LELDATE);
    	case MAGIC_TYPE_BEQLDATE:
    		return (MAGIC_TYPE_LEQLDATE);
    	case MAGIC_TYPE_UBEDATE:
    		return (MAGIC_TYPE_ULEDATE);
    	case MAGIC_TYPE_UBEQDATE:
    		return (MAGIC_TYPE_ULEQDATE);
    	case MAGIC_TYPE_UBELDATE:
    		return (MAGIC_TYPE_ULELDATE);
    	case MAGIC_TYPE_UBEQLDATE:
    		return (MAGIC_TYPE_ULEQLDATE);
    	case MAGIC_TYPE_LESHORT:
    		return (MAGIC_TYPE_BESHORT);
    	case MAGIC_TYPE_LELONG:
    		return (MAGIC_TYPE_LELONG);
    	case MAGIC_TYPE_LEQUAD:
    		return (MAGIC_TYPE_LEQUAD);
    	case MAGIC_TYPE_ULESHORT:
    		return (MAGIC_TYPE_UBESHORT);
    	case MAGIC_TYPE_ULELONG:
    		return (MAGIC_TYPE_UBELONG);
    	case MAGIC_TYPE_ULEQUAD:
    		return (MAGIC_TYPE_UBEQUAD);
    	case MAGIC_TYPE_LEFLOAT:
    		return (MAGIC_TYPE_BEFLOAT);
    	case MAGIC_TYPE_LEDOUBLE:
    		return (MAGIC_TYPE_BEDOUBLE);
    	case MAGIC_TYPE_LEDATE:
    		return (MAGIC_TYPE_BEDATE);
    	case MAGIC_TYPE_LEQDATE:
    		return (MAGIC_TYPE_BEQDATE);
    	case MAGIC_TYPE_LELDATE:
    		return (MAGIC_TYPE_BELDATE);
    	case MAGIC_TYPE_LEQLDATE:
    		return (MAGIC_TYPE_BEQLDATE);
    	case MAGIC_TYPE_ULEDATE:
    		return (MAGIC_TYPE_UBEDATE);
    	case MAGIC_TYPE_ULEQDATE:
    		return (MAGIC_TYPE_UBEQDATE);
    	case MAGIC_TYPE_ULELDATE:
    		return (MAGIC_TYPE_UBELDATE);
    	case MAGIC_TYPE_ULEQLDATE:
    		return (MAGIC_TYPE_UBEQLDATE);
    	default:
    		return (type);
    	}
    }
    
    static int
    magic_one_eq(char a, char b, int cflag)
    {
    	if (a == b)
    		return (1);
    	if (cflag && islower((u_char)b) && tolower((u_char)a) == (u_char)b)
    		return (1);
    	return (0);
    }
    
    static int
    magic_test_eq(const char *ap, size_t asize, const char *bp, size_t bsize,
        int cflag, int bflag, int Bflag)
    {
    	size_t	aoff, boff, aspaces, bspaces;
    
    	aoff = boff = 0;
    	while (aoff != asize && boff != bsize) {
    		if (Bflag && isspace((u_char)ap[aoff])) {
    			aspaces = 0;
    			while (aoff != asize && isspace((u_char)ap[aoff])) {
    				aspaces++;
    				aoff++;
    			}
    			bspaces = 0;
    			while (boff != bsize && isspace((u_char)bp[boff])) {
    				bspaces++;
    				boff++;
    			}
    			if (bspaces >= aspaces)
    				continue;
    			return (1);
    		}
    		if (magic_one_eq(ap[aoff], bp[boff], cflag)) {
    			aoff++;
    			boff++;
    			continue;
    		}
    		if (bflag && isspace((u_char)bp[boff])) {
    			boff++;
    			continue;
    		}
    		if (ap[aoff] < bp[boff])
    			return (-1);
    		return (1);
    	}
    	return (0);
    }
    
    static int
    magic_copy_from(struct magic_state *ms, ssize_t offset, void *dst, size_t size)
    {
    	if (offset < 0)
    		offset = ms->offset;
    	if (offset + size > ms->size)
    		return (-1);
    	memcpy(dst, ms->base + offset, size);
    	return (0);
    }
    
    static void
    magic_add_result(struct magic_state *ms, struct magic_line *ml,
        const char *fmt, ...)
    {
    	va_list	 ap;
    	int	 separate;
    	char	*s, *tmp, *add;
    
    	va_start(ap, fmt);
    	if (ml->stringify) {
    		if (vasprintf(&s, fmt, ap) == -1) {
    			va_end(ap);
    			return;
    		}
    		va_end(ap);
    		if (asprintf(&tmp, ml->result, s) == -1) {
    			free(s);
    			return;
    		}
    		free(s);
    	} else {
    		if (vasprintf(&tmp, ml->result, ap) == -1) {
    			va_end(ap);
    			return;
    		}
    		va_end(ap);
    	}
    
    	separate = 1;
    	if (tmp[0] == '\\' && tmp[1] == 'b') {
    		separate = 0;
    		add = tmp + 2;
    	} else
    		add = tmp;
    
    	if (separate && *ms->out != '\0')
    		strlcat(ms->out, " ", sizeof ms->out);
    	strlcat(ms->out, add, sizeof ms->out);
    
    	free(tmp);
    }
    
    static void
    magic_add_string(struct magic_state *ms, struct magic_line *ml,
        const char *s, size_t slen)
    {
    	char	*out;
    	size_t	 outlen, offset;
    
    	outlen = MAGIC_STRING_SIZE;
    	if (outlen > slen)
    		outlen = slen;
    	for (offset = 0; offset < outlen; offset++) {
    		if (s[offset] == '\0' || !isprint((u_char)s[offset])) {
    			outlen = offset;
    			break;
    		}
    	}
    	out = xreallocarray(NULL, 4, outlen + 1);
    	strvisx(out, s, outlen, VIS_TAB|VIS_NL|VIS_CSTYLE|VIS_OCTAL);
    	magic_add_result(ms, ml, "%s", out);
    	free(out);
    }
    
    static int
    magic_test_signed(struct magic_line *ml, int64_t value, int64_t wanted)
    {
    	switch (ml->test_operator) {
    	case 'x':
    		return (1);
    	case '<':
    		return (value < wanted);
    	case '[':
    		return (value <= wanted);
    	case '>':
    		return (value > wanted);
    	case ']':
    		return (value >= wanted);
    	case '=':
    		return (value == wanted);
    	case '&':
    		return ((value & wanted) == wanted);
    	case '^':
    		return ((~value & wanted) == wanted);
    	}
    	return (-1);
    }
    
    static int
    magic_test_unsigned(struct magic_line *ml, uint64_t value, uint64_t wanted)
    {
    	switch (ml->test_operator) {
    	case 'x':
    		return (1);
    	case '<':
    		return (value < wanted);
    	case '[':
    		return (value <= wanted);
    	case '>':
    		return (value > wanted);
    	case ']':
    		return (value >= wanted);
    	case '=':
    		return (value == wanted);
    	case '&':
    		return ((value & wanted) == wanted);
    	case '^':
    		return ((~value & wanted) == wanted);
    	}
    	return (-1);
    }
    
    static int
    magic_test_double(struct magic_line *ml, double value, double wanted)
    {
    	switch (ml->test_operator) {
    	case 'x':
    		return (1);
    	case '=':
    		return (value == wanted);
    	}
    	return (-1);
    }
    
    static int
    magic_test_type_none(__unused struct magic_line *ml,
        __unused struct magic_state *ms)
    {
    	return (0);
    }
    
    static int
    magic_test_type_byte(struct magic_line *ml, struct magic_state *ms)
    {
    	int8_t	value;
    	int	result;
    
    	if (magic_copy_from(ms, -1, &value, sizeof value) != 0)
    		return (0);
    
    	if (ml->type_operator == '&')
    		value &= (int8_t)ml->type_operand;
    	else if (ml->type_operator == '-')
    		value -= (int8_t)ml->type_operand;
    	else if (ml->type_operator == '+')
    		value += (int8_t)ml->type_operand;
    	else if (ml->type_operator == '/')
    		value /= (int8_t)ml->type_operand;
    	else if (ml->type_operator == '%')
    		value %= (int8_t)ml->type_operand;
    	else if (ml->type_operator == '*')
    		value *= (int8_t)ml->type_operand;
    	else if (ml->type_operator != ' ')
    		return (-1);
    
    	result = magic_test_signed(ml, value, (int8_t)ml->test_signed);
    	if (result == !ml->test_not && ml->result != NULL) {
    		magic_add_result(ms, ml, "%c", (int)value);
    		ms->offset += sizeof value;
    	}
    	return (result);
    }
    
    static int
    magic_test_type_short(struct magic_line *ml, struct magic_state *ms)
    {
    	int16_t value;
    	int	result;
    
    	if (magic_copy_from(ms, -1, &value, sizeof value) != 0)
    		return (0);
    	if (ml->type == magic_reverse_type(ms, MAGIC_TYPE_BESHORT))
    		value = be16toh(value);
    	if (ml->type == magic_reverse_type(ms, MAGIC_TYPE_LESHORT))
    		value = le16toh(value);
    
    	if (ml->type_operator == '&')
    		value &= (int16_t)ml->type_operand;
    	else if (ml->type_operator == '-')
    		value -= (int16_t)ml->type_operand;
    	else if (ml->type_operator == '+')
    		value += (int16_t)ml->type_operand;
    	else if (ml->type_operator == '/')
    		value /= (int16_t)ml->type_operand;
    	else if (ml->type_operator == '%')
    		value %= (int16_t)ml->type_operand;
    	else if (ml->type_operator == '*')
    		value *= (int16_t)ml->type_operand;
    	else if (ml->type_operator != ' ')
    		return (-1);
    
    	result = magic_test_signed(ml, value, (int16_t)ml->test_signed);
    	if (result == !ml->test_not && ml->result != NULL) {
    		magic_add_result(ms, ml, "%hd", (int)value);
    		ms->offset += sizeof value;
    	}
    	return (result);
    }
    
    static int
    magic_test_type_long(struct magic_line *ml, struct magic_state *ms)
    {
    	int32_t value;
    	int	result;
    
    	if (magic_copy_from(ms, -1, &value, sizeof value) != 0)
    		return (0);
    	if (ml->type == magic_reverse_type(ms, MAGIC_TYPE_BELONG))
    		value = be32toh(value);
    	if (ml->type == magic_reverse_type(ms, MAGIC_TYPE_LELONG))
    		value = le32toh(value);
    
    	if (ml->type_operator == '&')
    		value &= (int32_t)ml->type_operand;
    	else if (ml->type_operator == '-')
    		value -= (int32_t)ml->type_operand;
    	else if (ml->type_operator == '+')
    		value += (int32_t)ml->type_operand;
    	else if (ml->type_operator == '/')
    		value /= (int32_t)ml->type_operand;
    	else if (ml->type_operator == '%')
    		value %= (int32_t)ml->type_operand;
    	else if (ml->type_operator == '*')
    		value *= (int32_t)ml->type_operand;
    	else if (ml->type_operator != ' ')
    		return (-1);
    
    	result = magic_test_signed(ml, value, (int32_t)ml->test_signed);
    	if (result == !ml->test_not && ml->result != NULL) {
    		magic_add_result(ms, ml, "%d", (int)value);
    		ms->offset += sizeof value;
    	}
    	return (result);
    }
    
    static int
    magic_test_type_quad(struct magic_line *ml, struct magic_state *ms)
    {
    	int64_t value;
    	int	result;
    
    	if (magic_copy_from(ms, -1, &value, sizeof value) != 0)
    		return (0);
    	if (ml->type == magic_reverse_type(ms, MAGIC_TYPE_BEQUAD))
    		value = be64toh(value);
    	if (ml->type == magic_reverse_type(ms, MAGIC_TYPE_LEQUAD))
    		value = le64toh(value);
    
    	if (ml->type_operator == '&')
    		value &= (int64_t)ml->type_operand;
    	else if (ml->type_operator == '-')
    		value -= (int64_t)ml->type_operand;
    	else if (ml->type_operator == '+')
    		value += (int64_t)ml->type_operand;
    	else if (ml->type_operator == '/')
    		value /= (int64_t)ml->type_operand;
    	else if (ml->type_operator == '%')
    		value %= (int64_t)ml->type_operand;
    	else if (ml->type_operator == '*')
    		value *= (int64_t)ml->type_operand;
    	else if (ml->type_operator != ' ')
    		return (-1);
    
    	result = magic_test_signed(ml, value, (int64_t)ml->test_signed);
    	if (result == !ml->test_not && ml->result != NULL) {
    		magic_add_result(ms, ml, "%lld", (long long)value);
    		ms->offset += sizeof value;
    	}
    	return (result);
    }
    
    static int
    magic_test_type_ubyte(struct magic_line *ml, struct magic_state *ms)
    {
    	uint8_t value;
    	int	result;
    
    	if (magic_copy_from(ms, -1, &value, sizeof value) != 0)
    		return (0);
    
    	if (ml->type_operator == '&')
    		value &= (uint8_t)ml->type_operand;
    	else if (ml->type_operator == '-')
    		value -= (uint8_t)ml->type_operand;
    	else if (ml->type_operator == '+')
    		value += (uint8_t)ml->type_operand;
    	else if (ml->type_operator == '/')
    		value /= (uint8_t)ml->type_operand;
    	else if (ml->type_operator == '%')
    		value %= (uint8_t)ml->type_operand;
    	else if (ml->type_operator == '*')
    		value *= (uint8_t)ml->type_operand;
    	else if (ml->type_operator != ' ')
    		return (-1);
    
    	result = magic_test_unsigned(ml, value, (uint8_t)ml->test_unsigned);
    	if (result == !ml->test_not && ml->result != NULL) {
    		magic_add_result(ms, ml, "%c", (unsigned int)value);
    		ms->offset += sizeof value;
    	}
    	return (result);
    }
    
    static int
    magic_test_type_ushort(struct magic_line *ml, struct magic_state *ms)
    {
    	uint16_t	value;
    	int		result;
    
    	if (magic_copy_from(ms, -1, &value, sizeof value) != 0)
    		return (0);
    	if (ml->type == MAGIC_TYPE_UBESHORT)
    		value = be16toh(value);
    	if (ml->type == MAGIC_TYPE_ULESHORT)
    		value = le16toh(value);
    
    	if (ml->type_operator == '&')
    		value &= (uint16_t)ml->type_operand;
    	else if (ml->type_operator == '-')
    		value -= (uint16_t)ml->type_operand;
    	else if (ml->type_operator == '+')
    		value += (uint16_t)ml->type_operand;
    	else if (ml->type_operator == '/')
    		value /= (uint16_t)ml->type_operand;
    	else if (ml->type_operator == '%')
    		value %= (uint16_t)ml->type_operand;
    	else if (ml->type_operator == '*')
    		value *= (uint16_t)ml->type_operand;
    	else if (ml->type_operator != ' ')
    		return (-1);
    
    	result = magic_test_unsigned(ml, value, (uint16_t)ml->test_unsigned);
    	if (result == !ml->test_not && ml->result != NULL) {
    		magic_add_result(ms, ml, "%hu", (unsigned int)value);
    		ms->offset += sizeof value;
    	}
    	return (result);
    }
    
    static int
    magic_test_type_ulong(struct magic_line *ml, struct magic_state *ms)
    {
    	uint32_t	value;
    	int		result;
    
    	if (magic_copy_from(ms, -1, &value, sizeof value) != 0)
    		return (0);
    	if (ml->type == MAGIC_TYPE_UBELONG)
    		value = be32toh(value);
    	if (ml->type == MAGIC_TYPE_ULELONG)
    		value = le32toh(value);
    
    	if (ml->type_operator == '&')
    		value &= (uint32_t)ml->type_operand;
    	else if (ml->type_operator == '-')
    		value -= (uint32_t)ml->type_operand;
    	else if (ml->type_operator == '+')
    		value += (uint32_t)ml->type_operand;
    	else if (ml->type_operator == '/')
    		value /= (uint32_t)ml->type_operand;
    	else if (ml->type_operator == '%')
    		value %= (uint32_t)ml->type_operand;
    	else if (ml->type_operator == '*')
    		value *= (uint32_t)ml->type_operand;
    	else if (ml->type_operator != ' ')
    		return (-1);
    
    	result = magic_test_unsigned(ml, value, (uint32_t)ml->test_unsigned);
    	if (result == !ml->test_not && ml->result != NULL) {
    		magic_add_result(ms, ml, "%u", (unsigned int)value);
    		ms->offset += sizeof value;
    	}
    	return (result);
    }
    
    static int
    magic_test_type_uquad(struct magic_line *ml, struct magic_state *ms)
    {
    	uint64_t	value;
    	int		result;
    
    	if (magic_copy_from(ms, -1, &value, sizeof value) != 0)
    		return (0);
    	if (ml->type == MAGIC_TYPE_UBEQUAD)
    		value = be64toh(value);
    	if (ml->type == MAGIC_TYPE_ULEQUAD)
    		value = le64toh(value);
    
    	if (ml->type_operator == '&')
    		value &= (uint64_t)ml->type_operand;
    	else if (ml->type_operator == '-')
    		value -= (uint64_t)ml->type_operand;
    	else if (ml->type_operator == '+')
    		value += (uint64_t)ml->type_operand;
    	else if (ml->type_operator == '/')
    		value /= (uint64_t)ml->type_operand;
    	else if (ml->type_operator == '%')
    		value %= (uint64_t)ml->type_operand;
    	else if (ml->type_operator == '*')
    		value *= (uint64_t)ml->type_operand;
    	else if (ml->type_operator != ' ')
    		return (-1);
    
    	result = magic_test_unsigned(ml, value, (uint64_t)ml->test_unsigned);
    	if (result == !ml->test_not && ml->result != NULL) {
    		magic_add_result(ms, ml, "%llu", (unsigned long long)value);
    		ms->offset += sizeof value;
    	}
    	return (result);
    }
    
    static int
    magic_test_type_float(struct magic_line *ml, struct magic_state *ms)
    {
    	uint32_t	value0;
    	float		value;
    	int		result;
    
    	if (magic_copy_from(ms, -1, &value0, sizeof value0) != 0)
    		return (0);
    	if (ml->type == magic_reverse_type(ms, MAGIC_TYPE_BEFLOAT))
    		value0 = be32toh(value0);
    	if (ml->type == magic_reverse_type(ms, MAGIC_TYPE_LEFLOAT))
    		value0 = le32toh(value0);
    	memcpy(&value, &value0, sizeof value);
    
    	if (ml->type_operator != ' ')
    		return (-1);
    
    	result = magic_test_double(ml, value, (float)ml->test_double);
    	if (result == !ml->test_not && ml->result != NULL) {
    		magic_add_result(ms, ml, "%g", value);
    		ms->offset += sizeof value0;
    	}
    	return (1);
    }
    
    static int
    magic_test_type_double(struct magic_line *ml, struct magic_state *ms)
    {
    	uint64_t	value0;
    	double		value;
    	int		result;
    
    	if (magic_copy_from(ms, -1, &value0, sizeof value0) != 0)
    		return (0);
    	if (ml->type == magic_reverse_type(ms, MAGIC_TYPE_BEDOUBLE))
    		value0 = be64toh(value0);
    	if (ml->type == magic_reverse_type(ms, MAGIC_TYPE_LEDOUBLE))
    		value0 = le64toh(value0);
    	memcpy(&value, &value0, sizeof value);
    
    	if (ml->type_operator != ' ')
    		return (-1);
    
    	result = magic_test_double(ml, value, (double)ml->test_double);
    	if (result == !ml->test_not && ml->result != NULL) {
    		magic_add_result(ms, ml, "%g", value);
    		ms->offset += sizeof value0;
    	}
    	return (1);
    }
    
    static int
    magic_test_type_string(struct magic_line *ml, struct magic_state *ms)
    {
    	const char	*s, *cp;
    	size_t		 slen;
    	int		 result, cflag = 0, bflag = 0, Bflag = 0;
    
    	cp = &ml->type_string[(sizeof "string") - 1];
    	if (*cp != '\0') {
    		if (*cp != '/')
    			return (-1);
    		cp++;
    		for (; *cp != '\0'; cp++) {
    			switch (*cp) {
    			case 'B':
    			case 'W':
    				Bflag = 1;
    				break;
    			case 'b':
    			case 'w':
    				bflag = 1;
    				break;
    			case 'c':
    				cflag = 1;
    				break;
    			case 't':
    				break;
    			default:
    				return (-1);
    			}
    		}
    	}
    
    	s = ms->base + ms->offset;
    	slen = ms->size - ms->offset;
    	if (slen < ml->test_string_size)
    		return (0);
    
    	result = magic_test_eq(s, slen, ml->test_string, ml->test_string_size,
    	    cflag, bflag, Bflag);
    	switch (ml->test_operator) {
    	case 'x':
    		result = 1;
    		break;
    	case '<':
    		result = result < 0;
    		break;
    	case '>':
    		result = result > 0;
    		break;
    	case '=':
    		slen = ml->test_string_size; /* only print what was found */
    		result = result == 0;
    		break;
    	default:
    		result = -1;
    		break;
    	}
    	if (result == !ml->test_not) {
    		if (ml->result != NULL)
    			magic_add_string(ms, ml, s, slen);
    		if (result && ml->test_operator == '=')
    			ms->offset = s - ms->base + ml->test_string_size;
    	}
    	return (result);
    }
    
    static int
    magic_test_type_pstring(struct magic_line *ml, struct magic_state *ms)
    {
    	const char	*s, *cp;
    	size_t		 slen;
    	int		 result;
    
    	cp = &ml->type_string[(sizeof "pstring") - 1];
    	if (*cp != '\0') {
    		if (*cp != '/')
    			return (-1);
    		cp++;
    		for (; *cp != '\0'; cp++) {
    			switch (*cp) {
    			default:
    				return (-1);
    			}
    		}
    	}
    
    	s = ms->base + ms->offset;
    	if (ms->size - ms->offset < 1)
    		return (-1);
    	slen = *(u_char *)s;
    	if (slen + 1 > ms->size - ms->offset)
    		return (-1);
    	s++;
    
    	if (slen < ml->test_string_size)
    		result = -1;
    	else if (slen > ml->test_string_size)
    		result = 1;
    	else
    		result = memcmp(s, ml->test_string, ml->test_string_size);
    	switch (ml->test_operator) {
    	case 'x':
    		result = 1;
    		break;
    	case '<':
    		result = result < 0;
    		break;
    	case '>':
    		result = result > 0;
    		break;
    	case '=':
    		result = result == 0;
    		break;
    	default:
    		result = -1;
    		break;
    	}
    	if (result == !ml->test_not) {
    		if (ml->result != NULL)
    			magic_add_string(ms, ml, s, slen);
    		if (result && ml->test_operator == '=')
    			ms->offset += slen + 1;
    	}
    	return (result);
    }
    
    static int
    magic_test_type_date(struct magic_line *ml, struct magic_state *ms)
    {
    	int32_t	value;
    	int	result;
    	time_t	t;
    	char	s[64];
    
    	if (magic_copy_from(ms, -1, &value, sizeof value) != 0)
    		return (0);
    	if (ml->type == magic_reverse_type(ms, MAGIC_TYPE_BEDATE) ||
    	    ml->type == magic_reverse_type(ms, MAGIC_TYPE_BELDATE))
    		value = be32toh(value);
    	if (ml->type == magic_reverse_type(ms, MAGIC_TYPE_LEDATE) ||
    	    ml->type == magic_reverse_type(ms, MAGIC_TYPE_LELDATE))
    		value = le32toh(value);
    
    	if (ml->type_operator == '&')
    		value &= (int32_t)ml->type_operand;
    	else if (ml->type_operator == '-')
    		value -= (int32_t)ml->type_operand;
    	else if (ml->type_operator == '+')
    		value += (int32_t)ml->type_operand;
    	else if (ml->type_operator != ' ')
    		return (-1);
    
    	result = magic_test_signed(ml, value, (int32_t)ml->test_signed);
    	if (result == !ml->test_not && ml->result != NULL) {
    		t = value;
    		switch (ml->type) {
    		case MAGIC_TYPE_LDATE:
    		case MAGIC_TYPE_LELDATE:
    		case MAGIC_TYPE_BELDATE:
    			ctime_r(&t, s);
    			break;
    		default:
    			asctime_r(gmtime(&t), s);
    			break;
    		}
    		s[strcspn(s, "\n")] = '\0';
    		magic_add_result(ms, ml, "%s", s);
    		ms->offset += sizeof value;
    	}
    	return (result);
    }
    
    static int
    magic_test_type_qdate(struct magic_line *ml, struct magic_state *ms)
    {
    	int64_t value;
    	int	result;
    	time_t	t;
    	char	s[64];
    
    	if (magic_copy_from(ms, -1, &value, sizeof value) != 0)
    		return (0);
    	if (ml->type == magic_reverse_type(ms, MAGIC_TYPE_BEQDATE) ||
    	    ml->type == magic_reverse_type(ms, MAGIC_TYPE_BEQLDATE))
    		value = be64toh(value);
    	if (ml->type == magic_reverse_type(ms, MAGIC_TYPE_LEQDATE) ||
    	    ml->type == magic_reverse_type(ms, MAGIC_TYPE_LEQLDATE))
    		value = le64toh(value);
    
    	if (ml->type_operator == '&')
    		value &= (int64_t)ml->type_operand;
    	else if (ml->type_operator == '-')
    		value -= (int64_t)ml->type_operand;
    	else if (ml->type_operator == '+')
    		value += (int64_t)ml->type_operand;
    	else if (ml->type_operator != ' ')
    		return (-1);
    
    	result = magic_test_signed(ml, value, (int64_t)ml->test_signed);
    	if (result == !ml->test_not && ml->result != NULL) {
    		t = value;
    		switch (ml->type) {
    		case MAGIC_TYPE_QLDATE:
    		case MAGIC_TYPE_LEQLDATE:
    		case MAGIC_TYPE_BEQLDATE:
    			ctime_r(&t, s);
    			break;
    		default:
    			asctime_r(gmtime(&t), s);
    			break;
    		}
    		s[strcspn(s, "\n")] = '\0';
    		magic_add_result(ms, ml, "%s", s);
    		ms->offset += sizeof value;
    	}
    	return (result);
    }
    
    static int
    magic_test_type_udate(struct magic_line *ml, struct magic_state *ms)
    {
    	uint32_t	value;
    	int		result;
    	time_t		t;
    	char		s[64];
    
    	if (magic_copy_from(ms, -1, &value, sizeof value) != 0)
    		return (0);
    	if (ml->type == magic_reverse_type(ms, MAGIC_TYPE_BEDATE) ||
    	    ml->type == magic_reverse_type(ms, MAGIC_TYPE_BELDATE))
    		value = be32toh(value);
    	if (ml->type == magic_reverse_type(ms, MAGIC_TYPE_LEDATE) ||
    	    ml->type == magic_reverse_type(ms, MAGIC_TYPE_LELDATE))
    		value = le32toh(value);
    
    	if (ml->type_operator == '&')
    		value &= (uint32_t)ml->type_operand;
    	else if (ml->type_operator == '-')
    		value -= (uint32_t)ml->type_operand;
    	else if (ml->type_operator == '+')
    		value += (uint32_t)ml->type_operand;
    	else if (ml->type_operator != ' ')
    		return (-1);
    
    	result = magic_test_unsigned(ml, value, (uint32_t)ml->test_unsigned);
    	if (result == !ml->test_not && ml->result != NULL) {
    		t = value;
    		switch (ml->type) {
    		case MAGIC_TYPE_LDATE:
    		case MAGIC_TYPE_LELDATE:
    		case MAGIC_TYPE_BELDATE:
    			ctime_r(&t, s);
    			break;
    		default:
    			asctime_r(gmtime(&t), s);
    			break;
    		}
    		s[strcspn(s, "\n")] = '\0';
    		magic_add_result(ms, ml, "%s", s);
    		ms->offset += sizeof value;
    	}
    	return (result);
    }
    
    static int
    magic_test_type_uqdate(struct magic_line *ml, struct magic_state *ms)
    {
    	uint64_t	value;
    	int		result;
    	time_t		t;
    	char		s[64];
    
    	if (magic_copy_from(ms, -1, &value, sizeof value) != 0)
    		return (0);
    	if (ml->type == MAGIC_TYPE_UBEQDATE ||
    	    ml->type == MAGIC_TYPE_UBEQLDATE)
    		value = be64toh(value);
    	if (ml->type == MAGIC_TYPE_ULEQDATE ||
    	    ml->type == MAGIC_TYPE_ULEQLDATE)
    		value = le64toh(value);
    
    	if (ml->type_operator == '&')
    		value &= (uint64_t)ml->type_operand;
    	else if (ml->type_operator == '-')
    		value -= (uint64_t)ml->type_operand;
    	else if (ml->type_operator == '+')
    		value += (uint64_t)ml->type_operand;
    	else if (ml->type_operator != ' ')
    		return (-1);
    
    	result = magic_test_unsigned(ml, value, (uint64_t)ml->test_unsigned);
    	if (result == !ml->test_not && ml->result != NULL) {
    		t = value;
    		switch (ml->type) {
    		case MAGIC_TYPE_UQLDATE:
    		case MAGIC_TYPE_ULEQLDATE:
    		case MAGIC_TYPE_UBEQLDATE:
    			ctime_r(&t, s);
    			break;
    		default:
    			asctime_r(gmtime(&t), s);
    			break;
    		}
    		s[strcspn(s, "\n")] = '\0';
    		magic_add_result(ms, ml, "%s", s);
    		ms->offset += sizeof value;
    	}
    	return (result);
    }
    
    static int
    magic_test_type_bestring16(__unused struct magic_line *ml,
        __unused struct magic_state *ms)
    {
    	return (-2);
    }
    
    static int
    magic_test_type_lestring16(__unused struct magic_line *ml,
        __unused struct magic_state *ms)
    {
    	return (-2);
    }
    
    static int
    magic_test_type_melong(__unused struct magic_line *ml,
        __unused struct magic_state *ms)
    {
    	return (-2);
    }
    
    static int
    magic_test_type_medate(__unused struct magic_line *ml,
        __unused struct magic_state *ms)
    {
    	return (-2);
    }
    
    static int
    magic_test_type_meldate(__unused struct magic_line *ml,
        __unused struct magic_state *ms)
    {
    	return (-2);
    }
    
    static int
    magic_test_type_regex(struct magic_line *ml, struct magic_state *ms)
    {
    	const char	*cp;
    	regex_t		 re;
    	regmatch_t	 m;
    	int		 result, flags = 0, sflag = 0;
    
    	cp = &ml->type_string[(sizeof "regex") - 1];
    	if (*cp != '\0') {
    		if (*cp != '/')
    			return (-1);
    		cp++;
    		for (; *cp != '\0'; cp++) {
    			switch (*cp) {
    			case 's':
    				sflag = 1;
    				break;
    			case 'c':
    				flags |= REG_ICASE;
    				break;
    			default:
    				return (-1);
    			}
    		}
    	}
    
    	if (regcomp(&re, ml->test_string, REG_EXTENDED|REG_NEWLINE|flags) != 0)
    		return (-1);
    	m.rm_so = ms->offset;
    	m.rm_eo = ms->size;
    
    	result = (regexec(&re, ms->base, 1, &m, REG_STARTEND) == 0);
    	if (result == !ml->test_not) {
    		if (ml->result != NULL) {
    			magic_add_string(ms, ml, ms->base + m.rm_so,
    			    m.rm_eo - m.rm_so);
    		}
    		if (result) {
    			if (sflag)
    				ms->offset = m.rm_so;
    			else
    				ms->offset = m.rm_eo;
    		}
    	}
    	regfree(&re);
    	return (result);
    }
    
    static int
    magic_test_type_search(struct magic_line *ml, struct magic_state *ms)
    {
    	const char	*cp, *endptr, *start, *found;
    	size_t		 size, end, i;
    	uint64_t	 range;
    	int		 result, n, cflag = 0, bflag = 0, Bflag = 0;
    
    	cp = &ml->type_string[(sizeof "search") - 1];
    	if (*cp != '\0') {
    		if (*cp != '/')
    			return (-1);
    		cp++;
    
    		endptr = magic_strtoull(cp, &range);
    		if (endptr == NULL || (*endptr != '/' && *endptr != '\0'))
    			return (-1);
    
    		if (*endptr == '/') {
    			for (cp = endptr + 1; *cp != '\0'; cp++) {
    				switch (*cp) {
    				case 'B':
    				case 'W':
    					Bflag = 1;
    					break;
    				case 'b':
    				case 'w':
    					bflag = 1;
    					break;
    				case 'c':
    					cflag = 1;
    					break;
    				case 't':
    					break;
    				default:
    					return (-1);
    				}
    			}
    		}
    	} else
    		range = UINT64_MAX;
    	if (range > (uint64_t)ms->size - ms->offset)
    		range = ms->size - ms->offset;
    	size = ml->test_string_size;
    
    	/* Want to search every starting position from up to range + size. */
    	end = range + size;
    	if (end > ms->size - ms->offset) {
    		if (size > ms->size - ms->offset)
    			end = 0;
    		else
    			end = ms->size - ms->offset - size;
    	}
    
    	/*
    	 * < and > and the flags are only in /etc/magic with search/1 so don't
    	 * support them with anything else.
    	 */
    	start = ms->base + ms->offset;
    	if (end == 0)
    		found = NULL;
    	else if (ml->test_operator == 'x')
    		found = start;
    	else if (range == 1) {
    		n = magic_test_eq(start, ms->size - ms->offset, ml->test_string,
    		    size, cflag, bflag, Bflag);
    		if (n == -1 && ml->test_operator == '<')
    			found = start;
    		else if (n == 1 && ml->test_operator == '>')
    			found = start;
    		else if (n == 0 && ml->test_operator == '=')
    			found = start;
    		else
    			found = NULL;
    	} else {
    		if (ml->test_operator != '=')
    			return (-2);
    		for (i = 0; i < end; i++) {
    			n = magic_test_eq(start + i, ms->size - ms->offset - i,
    			    ml->test_string, size, cflag, bflag, Bflag);
    			if (n == 0) {
    				found = start + i;
    				break;
    			}
    		}
    		if (i == end)
    			found = NULL;
    	}
    	result = (found != NULL);
    
    	if (result == !ml->test_not) {
    		if (ml->result != NULL)
    			magic_add_string(ms, ml, found, ms->size - ms->offset);
    		if (result && found != NULL && ml->test_operator == '=')
    			ms->offset = (found + size) - ms->base;
    	}
    	return (result);
    }
    
    static int
    magic_test_type_default(struct magic_line *ml, struct magic_state *ms)
    {
    	if (!ms->matched && ml->result != NULL)
    		magic_add_result(ms, ml, "%s", "");
    	return (!ms->matched);
    }
    
    static int
    magic_test_type_clear(struct magic_line *ml, struct magic_state *ms)
    {
    	if (ml->result != NULL)
    		magic_add_result(ms, ml, "%s", "");
    	return (1);
    }
    
    static int
    magic_test_type_name(__unused struct magic_line *ml,
        __unused struct magic_state *ms)
    {
    	return (-1);
    }
    
    static int
    magic_test_type_use(__unused struct magic_line *ml,
        __unused struct magic_state *ms)
    {
    	return (1);
    }
    
    static int (*magic_test_functions[])(struct magic_line *,
        struct magic_state *) = {
    	magic_test_type_none,
    	magic_test_type_byte,
    	magic_test_type_short,
    	magic_test_type_long,
    	magic_test_type_quad,
    	magic_test_type_ubyte,
    	magic_test_type_ushort,
    	magic_test_type_ulong,
    	magic_test_type_uquad,
    	magic_test_type_float,
    	magic_test_type_double,
    	magic_test_type_string,
    	magic_test_type_pstring,
    	magic_test_type_date,
    	magic_test_type_qdate,
    	magic_test_type_date,
    	magic_test_type_qdate,
    	magic_test_type_udate,
    	magic_test_type_uqdate,
    	magic_test_type_udate,
    	magic_test_type_qdate,
    	magic_test_type_short,
    	magic_test_type_long,
    	magic_test_type_quad,
    	magic_test_type_ushort,
    	magic_test_type_ulong,
    	magic_test_type_uquad,
    	magic_test_type_float,
    	magic_test_type_double,
    	magic_test_type_date,
    	magic_test_type_qdate,
    	magic_test_type_date,
    	magic_test_type_qdate,
    	magic_test_type_udate,
    	magic_test_type_uqdate,
    	magic_test_type_udate,
    	magic_test_type_uqdate,
    	magic_test_type_bestring16,
    	magic_test_type_short,
    	magic_test_type_long,
    	magic_test_type_quad,
    	magic_test_type_ushort,
    	magic_test_type_ulong,
    	magic_test_type_uquad,
    	magic_test_type_float,
    	magic_test_type_double,
    	magic_test_type_date,
    	magic_test_type_qdate,
    	magic_test_type_date,
    	magic_test_type_qdate,
    	magic_test_type_udate,
    	magic_test_type_uqdate,
    	magic_test_type_udate,
    	magic_test_type_uqdate,
    	magic_test_type_lestring16,
    	magic_test_type_melong,
    	magic_test_type_medate,
    	magic_test_type_meldate,
    	magic_test_type_regex,
    	magic_test_type_search,
    	magic_test_type_default,
    	magic_test_type_clear,
    	magic_test_type_name,
    	magic_test_type_use,
    };
    
    static void
    magic_test_children(struct magic_line *ml, struct magic_state *ms, size_t start,
        int reverse)
    {
    	struct magic_line	*child;
    	size_t			 saved_start, saved_offset;
    	int			 saved_reverse;
    
    	saved_start = ms->start;
    	saved_reverse = ms->reverse;
    	saved_offset = ms->offset;
    
    	ms->matched = 0; /* no need to save, caller will set too */
    
    	TAILQ_FOREACH(child, &ml->children, entry) {
    		ms->start = start;
    		ms->reverse = reverse;
    		ms->offset = saved_offset;
    
    		magic_test_line(child, ms);
    	}
    
    	ms->start = saved_start;
    	ms->reverse = saved_reverse;
    	ms->offset = saved_offset;
    }
    
    static int
    magic_test_line(struct magic_line *ml, struct magic_state *ms)
    {
    	struct magic		*m = ml->root;
    	struct magic_line	*named;
    	int64_t			 offset, wanted, next;
    	int			 result;
    	uint8_t			 b;
    	uint16_t		 s;
    	uint32_t		 l;
    
    	if (ml->indirect_type == ' ')
    		wanted = ms->start + ml->offset;
    	else {
    		wanted = ml->indirect_offset;
    		if (ml->indirect_relative) {
    			if (wanted < 0 && (size_t)-wanted > ms->offset)
    				return (0);
    			if (wanted > 0 && ms->offset + wanted > ms->size)
    				return (0);
    			next = ms->offset + ml->indirect_offset;
    		} else
    			next = wanted;
    
    		switch (ml->indirect_type) {
    		case 'b':
    		case 'B':
    			if (magic_copy_from(ms, next, &b, sizeof b) != 0)
    				return (0);
    			wanted = b;
    			break;
    		case 's':
    			if (magic_copy_from(ms, next, &s, sizeof s) != 0)
    				return (0);
    			wanted = le16toh(s);
    			break;
    		case 'S':
    			if (magic_copy_from(ms, next, &s, sizeof s) != 0)
    				return (0);
    			wanted = be16toh(s);
    			break;
    		case 'l':
    			if (magic_copy_from(ms, next, &l, sizeof l) != 0)
    				return (0);
    			wanted = le16toh(l);
    			break;
    		case 'L':
    			if (magic_copy_from(ms, next, &l, sizeof l) != 0)
    				return (0);
    			wanted = be16toh(l);
    			break;
    		}
    
    		switch (ml->indirect_operator) {
    		case '+':
    			wanted += ml->indirect_operand;
    			break;
    		case '-':
    			wanted -= ml->indirect_operand;
    			break;
    		case '*':
    			wanted *= ml->indirect_operand;
    			break;
    		}
    	}
    
    	if (ml->offset_relative) {
    		if (wanted < 0 && (size_t)-wanted > ms->offset)
    			return (0);
    		if (wanted > 0 && ms->offset + wanted > ms->size)
    			return (0);
    		offset = ms->offset + wanted;
    	} else
    		offset = wanted;
    	if (offset < 0 || (size_t)offset > ms->size)
    		return (0);
    	ms->offset = offset; /* test function may update */
    
    	result = magic_test_functions[ml->type](ml, ms);
    	if (result == -1) {
    		magic_warn(ml, "test %s/%c failed", ml->type_string,
    		    ml->test_operator);
    		return (0);
    	}
    	if (result == -2) {
    		magic_warn(ml, "test %s/%c not implemented", ml->type_string,
    		    ml->test_operator);
    		return (0);
    	}
    	if (result == ml->test_not)
    		return (0);
    	if (ml->mimetype != NULL)
    		ms->mimetype = ml->mimetype;
    
    	magic_warn(ml, "test %s/%c matched at offset %lld (now %zu): "
    	    "'%s'", ml->type_string, ml->test_operator, offset,
    	    ms->offset, ml->result == NULL ? "" : ml->result);
    
    	if (ml->type == MAGIC_TYPE_USE) {
    		if (*ml->name == '^')
    			named = magic_get_named(m, ml->name + 1);
    		else
    			named = magic_get_named(m, ml->name);
    		if (named == NULL) {
    			magic_warn(ml, "no name found for use %s", ml->name);
    			return (0);
    		}
    		magic_warn(ml, "use %s at offset %lld", ml->name, offset);
    		magic_test_children(named, ms, offset, *ml->name == '^');
    	}
    
    	magic_test_children(ml, ms, ms->start, ms->reverse);
    
    	if (ml->type == MAGIC_TYPE_CLEAR)
    		ms->matched = 0;
    	else
    		ms->matched = 1;
    	return (ml->result != NULL);
    }
    
    const char *
    magic_test(struct magic *m, const void *base, size_t size, int flags)
    {
    	struct magic_line		*ml;
    	static struct magic_state	 ms;
    
    	memset(&ms, 0, sizeof ms);
    
    	ms.base = base;
    	ms.size = size;
    
    	ms.text = !!(flags & MAGIC_TEST_TEXT);
    
    	RB_FOREACH(ml, magic_tree, &m->tree) {
    		ms.offset = 0;
    		if (ml->text == ms.text && magic_test_line(ml, &ms))
    			break;
    	}
    
    	if (*ms.out != '\0') {
    		if (flags & MAGIC_TEST_MIME) {
    			if (ms.mimetype != NULL)
    				return (ms.mimetype);
    			return (NULL);
    		}
    		return (ms.out);
    	}
    	return (NULL);
    }