// SPDX-FileCopyrightText: 2011-2014 pancake <pancake@nopcode.org>
// SPDX-License-Identifier: LGPL-3.0-only

#ifndef RZ_MAGIC_H
#define RZ_MAGIC_H

#include <rz_types.h>

#ifdef __cplusplus
extern "C" {
#endif

RZ_LIB_VERSION_HEADER(rz_magic);

#ifndef MAGICFILE
#define MAGICFILE "/etc/magic"
#endif

#ifdef RZ_API

#ifdef __EMX__
#define PATHSEP ';'
#else
#define PATHSEP ':'
#endif

/* limits */
#ifndef HOWMANY
#define HOWMANY (256 * 1024) /* how much of the file to look at */
#endif
#define MAXDESC   64
#define MAXMAGIS  8192 /* max entries in any one magic file or directory */
#define MAXstring 32 /* max leng of "string" types */

/* define this outside to fix build for g++ */
union VALUETYPE {
	ut8 b;
	ut16 h;
	ut32 l;
	ut64 q;
	ut8 hs[2]; /* 2 bytes of a fixed-endian "short" */
	ut8 hl[4]; /* 4 bytes of a fixed-endian "long" */
	ut8 hq[8]; /* 8 bytes of a fixed-endian "quad" */
	char s[MAXstring]; /* the search string or regex pattern */
	float f;
	double d;
}; /* either number or string */

/* constants */
#define MAGICNO        0xF11E041C
#define VERSIONNO      5
#define FILE_MAGICSIZE (32 * 6)

#define FILE_LOAD    0
#define FILE_CHECK   1
#define FILE_COMPILE 2

struct rz_magic {
	/* Word 1 */
	ut16 cont_level; /* level of ">" */
	ut8 flag;

#define INDIR       0x01 /* if '(...)' appears */
#define OFFADD      0x02 /* if '>&' or '>...(&' appears */
#define INDIROFFADD 0x04 /* if '>&(' appears */
#define UNSIGNED    0x08 /* comparison is unsigned */
#define NOSPACE     0x10 /* suppress space character before output */
#define BINTEST     0x20 /* test is for a binary type (set only \
			    for top-level tests) */
#define TEXTTEST 0 /* for passing to file_softmagic */

	ut8 dummy1;

	/* Word 2 */
	ut8 reln; /* relation (0=eq, '>'=gt, etc) */
	ut8 vallen; /* length of string value, if any */
	ut8 type; /* comparison type (FILE_*) */
	ut8 in_type; /* type of indirection */
#define FILE_INVALID    0
#define FILE_BYTE       1
#define FILE_SHORT      2
#define FILE_DEFAULT    3
#define FILE_LONG       4
#define FILE_STRING     5
#define FILE_DATE       6
#define FILE_BESHORT    7
#define FILE_BELONG     8
#define FILE_BEDATE     9
#define FILE_LESHORT    10
#define FILE_LELONG     11
#define FILE_LEDATE     12
#define FILE_PSTRING    13
#define FILE_LDATE      14
#define FILE_BELDATE    15
#define FILE_LELDATE    16
#define FILE_REGEX      17
#define FILE_BESTRING16 18
#define FILE_LESTRING16 19
#define FILE_SEARCH     20
#define FILE_MEDATE     21
#define FILE_MELDATE    22
#define FILE_MELONG     23
#define FILE_QUAD       24
#define FILE_LEQUAD     25
#define FILE_BEQUAD     26
#define FILE_QDATE      27
#define FILE_LEQDATE    28
#define FILE_BEQDATE    29
#define FILE_QLDATE     30
#define FILE_LEQLDATE   31
#define FILE_BEQLDATE   32
#define FILE_FLOAT      33
#define FILE_BEFLOAT    34
#define FILE_LEFLOAT    35
#define FILE_DOUBLE     36
#define FILE_BEDOUBLE   37
#define FILE_LEDOUBLE   38
#define FILE_NAMES_SIZE 39 /* size of array to contain all names */

#define MAGIC_IS_STRING(t) \
	((t) == FILE_STRING || \
		(t) == FILE_PSTRING || \
		(t) == FILE_BESTRING16 || \
		(t) == FILE_LESTRING16 || \
		(t) == FILE_REGEX || \
		(t) == FILE_SEARCH || \
		(t) == FILE_DEFAULT)

#define FILE_FMT_NONE   0
#define FILE_FMT_NUM    1 /* "cduxXi" */
#define FILE_FMT_STR    2 /* "s" */
#define FILE_FMT_QUAD   3 /* "ll" */
#define FILE_FMT_FLOAT  4 /* "eEfFgG" */
#define FILE_FMT_DOUBLE 5 /* "eEfFgG" */

	/* Word 3 */
	ut8 in_op; /* operator for indirection */
	ut8 mask_op; /* operator for mask */
	ut8 cond; /* conditional type */
	ut8 dummy2;

#define FILE_OPS        "&|^+-*/%"
#define FILE_OPAND      0
#define FILE_OPOR       1
#define FILE_OPXOR      2
#define FILE_OPADD      3
#define FILE_OPMINUS    4
#define FILE_OPMULTIPLY 5
#define FILE_OPDIVIDE   6
#define FILE_OPMODULO   7
#define FILE_OPS_MASK   0x07 /* mask for above ops */
#define FILE_UNUSED_1   0x08
#define FILE_UNUSED_2   0x10
#define FILE_UNUSED_3   0x20
#define FILE_OPINVERSE  0x40
#define FILE_OPINDIRECT 0x80

#define COND_NONE 0
#define COND_IF   1
#define COND_ELIF 2
#define COND_ELSE 3

	/* Word 4 */
	ut32 offset; /* offset to magic number */
	/* Word 5 */
	ut32 in_offset; /* offset from indirection */
	/* Word 6 */
	ut32 lineno; /* line number in magic file */
	/* Word 7,8 */
	union {
		ut64 _mask; /* for use with numeric and date types */
		struct {
			ut32 _count; /* repeat/line count */
			ut32 _flags; /* modifier flags */
		} _s; /* for use with string types */
	} _u;

#define num_mask  _u._mask
#define str_range _u._s._count
#define str_flags _u._s._flags

	/* Words 9-16 */
	union VALUETYPE value;
	/* Words 17..31 */
	char desc[MAXDESC]; /* description */
	/* Words 32..47 */
	char mimetype[MAXDESC]; /* MIME type */
};

#define BIT(A)                        (1 << (A))
#define STRING_COMPACT_BLANK          BIT(0)
#define STRING_COMPACT_OPTIONAL_BLANK BIT(1)
#define STRING_IGNORE_LOWERCASE       BIT(2)
#define STRING_IGNORE_UPPERCASE       BIT(3)
#define REGEX_OFFSET_START            BIT(4)
#define CHAR_COMPACT_BLANK            'B'
#define CHAR_COMPACT_OPTIONAL_BLANK   'b'
#define CHAR_IGNORE_LOWERCASE         'c'
#define CHAR_IGNORE_UPPERCASE         'C'
#define CHAR_REGEX_OFFSET_START       's'
#define STRING_IGNORE_CASE            (STRING_IGNORE_LOWERCASE | STRING_IGNORE_UPPERCASE)
#define STRING_DEFAULT_RANGE          100

/* list of magic entries */
struct mlist {
	struct rz_magic *magic; /* array of magic entries */
	ut32 nmagic; /* number of entries in array */
	int mapped; /* allocation type: 0 => apprentice_file
		     *                  1 => apprentice_map + malloc
		     *                  2 => apprentice_map + mmap */
	struct mlist *next, *prev;
};

#define RZ_MAGIC_NONE              0x000000 /* No flags */
#define RZ_MAGIC_DEBUG             0x000001 /* Turn on debugging */
#define RZ_MAGIC_SYMLINK           0x000002 /* Follow symlinks */
#define RZ_MAGIC_COMPRESS          0x000004 /* Check inside compressed files */
#define RZ_MAGIC_DEVICES           0x000008 /* Look at the contents of devices */
#define RZ_MAGIC_MIME_TYPE         0x000010 /* Return only the MIME type */
#define RZ_MAGIC_CONTINUE          0x000020 /* Return all matches */
#define RZ_MAGIC_CHECK             0x000040 /* Print warnings to stderr */
#define RZ_MAGIC_PRESERVE_ATIME    0x000080 /* Restore access time on exit */
#define RZ_MAGIC_RAW               0x000100 /* Don't translate unprint chars */
#define RZ_MAGIC_ERROR             0x000200 /* Handle ENOENT etc as real errors */
#define RZ_MAGIC_MIME_ENCODING     0x000400 /* Return only the MIME encoding */
#define RZ_MAGIC_MIME              (RZ_MAGIC_MIME_TYPE | RZ_MAGIC_MIME_ENCODING)
#define RZ_MAGIC_NO_CHECK_COMPRESS 0x001000 /* Don't check for compressed files */
#define RZ_MAGIC_NO_CHECK_TAR      0x002000 /* Don't check for tar files */
#define RZ_MAGIC_NO_CHECK_SOFT     0x004000 /* Don't check magic entries */
#define RZ_MAGIC_NO_CHECK_APPTYPE  0x008000 /* Don't check application type */
#define RZ_MAGIC_NO_CHECK_ELF      0x010000 /* Don't check for elf details */
#define RZ_MAGIC_NO_CHECK_ASCII    0x020000 /* Don't check for ascii files */
#define RZ_MAGIC_NO_CHECK_TOKENS   0x100000 /* Don't check ascii/tokens */

/* Defined for backwards compatibility; do nothing */
#define MAGIC_NO_CHECK_FORTRAN 0x000000 /* Don't check ascii/fortran */
#define MAGIC_NO_CHECK_TROFF   0x000000 /* Don't check ascii/troff */

struct rz_magic_set {
	struct mlist *mlist;
	struct cont {
		size_t len;
		struct level_info {
			st32 off;
			int got_match;
			int last_match;
			int last_cond; /* used for error checking by parse() */
		} * li;
	} c;
	struct out {
		char *buf; /* Accumulation buffer */
		char *pbuf; /* Printable buffer */
	} o;
	ut32 offset;
	int error;
	int flags;
	int haderr;
	const char *file;
	size_t line; /* current magic line number */

	/* data for searches */
	struct {
		const char *s; /* start of search in original source */
		size_t s_len; /* length of search region */
		size_t offset; /* starting offset in source: XXX - should this be off_t? */
		size_t rm_len; /* match length */
	} search;

	/* FIXME: Make the string dynamically allocated so that e.g.
	   strings matched in files can be longer than MAXstring */
	union VALUETYPE ms_value; /* either number or string */
};

#if USE_LIB_MAGIC
#define RzMagic struct magic_set
#else
typedef struct rz_magic_set RzMagic;
#endif

#ifdef RZ_API
RZ_API RzMagic *rz_magic_new(int flags);
RZ_API void rz_magic_free(RzMagic *);

RZ_API const char *rz_magic_file(RzMagic *, const char *);
RZ_API const char *rz_magic_descriptor(RzMagic *, int);
RZ_API const char *rz_magic_buffer(RzMagic *, const void *, size_t);

RZ_API const char *rz_magic_error(RzMagic *);
RZ_API void rz_magic_setflags(RzMagic *, int);

RZ_API bool rz_magic_load(RzMagic *, const char *);
RZ_API bool rz_magic_load_buffer(RzMagic *, const char *);
RZ_API bool rz_magic_compile(RzMagic *, const char *);
RZ_API bool rz_magic_check(RzMagic *, const char *);
RZ_API int rz_magic_errno(RzMagic *);
#endif

#endif

#ifdef __cplusplus
}
#endif

#endif /* _MAGIC_H */
