File: [local] / src / usr.bin / file / magic-test.c (download)
Revision 1.25, Tue Apr 18 14:16:48 2017 UTC (7 years, 1 month ago) by nicm
Branch: MAIN
CVS Tags: OPENBSD_6_3_BASE, OPENBSD_6_3, OPENBSD_6_2_BASE, OPENBSD_6_2 Changes since 1.24: +2 -2 lines
Style nits; no binary change.
|
/* $OpenBSD: magic-test.c,v 1.25 2017/04/18 14:16:48 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) != 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 (xstrdup(ms.mimetype));
return (NULL);
}
return (xstrdup(ms.out));
}
return (NULL);
}