private int
magiccheck(struct magic_set *ms, struct magic *m)
{
	uint64_t l = m->value.q;
	uint64_t v;
	float fl, fv;
	double dl, dv;
	int matched;
	union VALUETYPE *p = &ms->ms_value;

	switch (m->type) {
	case FILE_BYTE:
		v = p->b;
		break;

	case FILE_SHORT:
	case FILE_BESHORT:
	case FILE_LESHORT:
		v = p->h;
		break;

	case FILE_LONG:
	case FILE_BELONG:
	case FILE_LELONG:
	case FILE_MELONG:
	case FILE_DATE:
	case FILE_BEDATE:
	case FILE_LEDATE:
	case FILE_MEDATE:
	case FILE_LDATE:
	case FILE_BELDATE:
	case FILE_LELDATE:
	case FILE_MELDATE:
		v = p->l;
		break;

	case FILE_QUAD:
	case FILE_LEQUAD:
	case FILE_BEQUAD:
	case FILE_QDATE:
	case FILE_BEQDATE:
	case FILE_LEQDATE:
	case FILE_QLDATE:
	case FILE_BEQLDATE:
	case FILE_LEQLDATE:
	case FILE_QWDATE:
	case FILE_BEQWDATE:
	case FILE_LEQWDATE:
	case FILE_OFFSET:
		v = p->q;
		break;

	case FILE_FLOAT:
	case FILE_BEFLOAT:
	case FILE_LEFLOAT:
		fl = m->value.f;
		fv = p->f;
		switch (m->reln) {
		case 'x':
			matched = 1;
			break;

		case '!':
			matched = fv != fl;
			break;

		case '=':
			matched = fv == fl;
			break;

		case '>':
			matched = fv > fl;
			break;

		case '<':
			matched = fv < fl;
			break;

		default:
			file_magerror(ms, "cannot happen with float: invalid relation `%c'",
			    m->reln);
			return -1;
		}
		return matched;

	case FILE_DOUBLE:
	case FILE_BEDOUBLE:
	case FILE_LEDOUBLE:
		dl = m->value.d;
		dv = p->d;
		switch (m->reln) {
		case 'x':
			matched = 1;
			break;

		case '!':
			matched = dv != dl;
			break;

		case '=':
			matched = dv == dl;
			break;

		case '>':
			matched = dv > dl;
			break;

		case '<':
			matched = dv < dl;
			break;

		default:
			file_magerror(ms, "cannot happen with double: invalid relation `%c'", m->reln);
			return -1;
		}
		return matched;

	case FILE_DEFAULT:
	case FILE_CLEAR:
		l = 0;
		v = 0;
		break;

	case FILE_STRING:
	case FILE_PSTRING:
		l = 0;
		v = file_strncmp(m->value.s, p->s, CAST(size_t, m->vallen),
		    sizeof(p->s), m->str_flags);
		break;

	case FILE_BESTRING16:
	case FILE_LESTRING16:
		l = 0;
		v = file_strncmp16(m->value.s, p->s, CAST(size_t, m->vallen),
		    sizeof(p->s), m->str_flags);
		break;

	case FILE_SEARCH: { /* search ms->search.s for the string m->value.s */
		size_t slen;
		size_t idx;

		if (ms->search.s == NULL)
			return 0;

		slen = MIN(m->vallen, sizeof(m->value.s));
		l = 0;
		v = 0;
#ifdef HAVE_MEMMEM
		if (slen > 0 && m->str_flags == 0) {
			const char *found;
			idx = m->str_range + slen;
			if (m->str_range == 0 || ms->search.s_len < idx)
				idx = ms->search.s_len;
			found = CAST(const char *, memmem(ms->search.s, idx,
			    m->value.s, slen));
			if (!found)
				return 0;
			idx = found - ms->search.s;
			ms->search.offset += idx;
			ms->search.rm_len = ms->search.s_len - idx;
			break;
		}
#endif

		for (idx = 0; m->str_range == 0 || idx < m->str_range; idx++) {
			if (slen + idx > ms->search.s_len)
				return 0;

			v = file_strncmp(m->value.s, ms->search.s + idx, slen,
			    ms->search.s_len - idx, m->str_flags);
			if (v == 0) {	/* found match */
				ms->search.offset += idx;
				ms->search.rm_len = ms->search.s_len - idx;
				break;
			}
		}
		break;
	}
	case FILE_REGEX: {
		zend_string *pattern;
		uint32_t options = 0;
		pcre_cache_entry *pce;

		options |= PCRE2_MULTILINE;

		if (m->str_flags & STRING_IGNORE_CASE) {
			options |= PCRE2_CASELESS;
		}

		pattern = convert_libmagic_pattern((char *)m->value.s, m->vallen, options);

		l = v = 0;
		if ((pce = pcre_get_compiled_regex_cache(pattern)) == NULL) {
			zend_string_release(pattern);
			return -1;
		} else {
			/* pce now contains the compiled regex */
			zval retval;
			zval subpats;
			zend_string *haystack;

			ZVAL_NULL(&retval);
			ZVAL_NULL(&subpats);

			/* Cut the search len from haystack, equals to REG_STARTEND */
			haystack = zend_string_init(ms->search.s, ms->search.s_len, 0);

			/* match v = 0, no match v = 1 */
			php_pcre_match_impl(pce, haystack, &retval, &subpats, 0, 1, PREG_OFFSET_CAPTURE, 0);
			/* Free haystack */
			zend_string_release(haystack);

			if (Z_LVAL(retval) < 0) {
				zval_ptr_dtor(&subpats);
				zend_string_release(pattern);
				return -1;
			} else if ((Z_LVAL(retval) > 0) && (Z_TYPE(subpats) == IS_ARRAY)) {
				/* Need to fetch global match which equals pmatch[0] */
				zval *pzval;
				HashTable *ht = Z_ARRVAL(subpats);
				if ((pzval = zend_hash_index_find(ht, 0)) != NULL && Z_TYPE_P(pzval) == IS_ARRAY) {
					/* If everything goes according to the master plan
					   tmpcopy now contains two elements:
					   0 = the match
					   1 = starting position of the match */
					zval *match, *offset;
					if ((match = zend_hash_index_find(Z_ARRVAL_P(pzval), 0)) &&
							(offset = zend_hash_index_find(Z_ARRVAL_P(pzval), 1))) {
						if (Z_TYPE_P(match) != IS_STRING && Z_TYPE_P(offset) != IS_LONG) {
							goto error_out;
						}
						ms->search.s += Z_LVAL_P(offset); /* this is where the match starts */
						ms->search.offset += Z_LVAL_P(offset); /* this is where the match starts as size_t */
						ms->search.rm_len = Z_STRLEN_P(match) /* This is the length of the matched pattern */;
						v = 0;
					} else {
						goto error_out;
					}
				} else {
error_out:
					zval_ptr_dtor(&subpats);
					zend_string_release(pattern);
					return -1;
				}
			} else {
				v = 1;
			}
			zval_ptr_dtor(&subpats);
			zend_string_release(pattern);
		}
		break;
	}
	case FILE_USE:
		return ms->ms_value.q != 0;
	case FILE_NAME:
	case FILE_INDIRECT:
		return 1;
	case FILE_DER:
		matched = der_cmp(ms, m);
		if (matched == -1) {
			if ((ms->flags & MAGIC_DEBUG) != 0) {
				(void) fprintf(stderr,
				    "EOF comparing DER entries");
			}
			return 0;
		}
		return matched;
	case FILE_GUID:
		l = 0;
		v = memcmp(m->value.guid, p->guid, sizeof(p->guid));
		break;
	default:
		file_magerror(ms, "invalid type %d in magiccheck()", m->type);
		return -1;
	}

	v = file_signextend(ms, m, v);

	switch (m->reln) {
	case 'x':
		if ((ms->flags & MAGIC_DEBUG) != 0)
			(void) fprintf(stderr, "%" INT64_T_FORMAT
			    "u == *any* = 1\n", CAST(unsigned long long, v));
		matched = 1;
		break;

	case '!':
		matched = v != l;
		if ((ms->flags & MAGIC_DEBUG) != 0)
			(void) fprintf(stderr, "%" INT64_T_FORMAT "u != %"
			    INT64_T_FORMAT "u = %d\n",
			    CAST(unsigned long long, v),
			    CAST(unsigned long long, l), matched);
		break;

	case '=':
		matched = v == l;
		if ((ms->flags & MAGIC_DEBUG) != 0)
			(void) fprintf(stderr, "%" INT64_T_FORMAT "u == %"
			    INT64_T_FORMAT "u = %d\n",
			    CAST(unsigned long long, v),
			    CAST(unsigned long long, l), matched);
		break;

	case '>':
		if (m->flag & UNSIGNED) {
			matched = v > l;
			if ((ms->flags & MAGIC_DEBUG) != 0)
				(void) fprintf(stderr, "%" INT64_T_FORMAT
				    "u > %" INT64_T_FORMAT "u = %d\n",
				    CAST(unsigned long long, v),
				    CAST(unsigned long long, l), matched);
		}
		else {
			matched = CAST(int64_t, v) > CAST(int64_t, l);
			if ((ms->flags & MAGIC_DEBUG) != 0)
				(void) fprintf(stderr, "%" INT64_T_FORMAT
				    "d > %" INT64_T_FORMAT "d = %d\n",
				    CAST(long long, v),
				    CAST(long long, l), matched);
		}
		break;

	case '<':
		if (m->flag & UNSIGNED) {
			matched = v < l;
			if ((ms->flags & MAGIC_DEBUG) != 0)
				(void) fprintf(stderr, "%" INT64_T_FORMAT
				    "u < %" INT64_T_FORMAT "u = %d\n",
				    CAST(unsigned long long, v),
				    CAST(unsigned long long, l), matched);
		}
		else {
			matched = CAST(int64_t, v) < CAST(int64_t, l);
			if ((ms->flags & MAGIC_DEBUG) != 0)
				(void) fprintf(stderr, "%" INT64_T_FORMAT
				    "d < %" INT64_T_FORMAT "d = %d\n",
				     CAST(long long, v),
				     CAST(long long, l), matched);
		}
		break;

	case '&':
		matched = (v & l) == l;
		if ((ms->flags & MAGIC_DEBUG) != 0)
			(void) fprintf(stderr, "((%" INT64_T_FORMAT "x & %"
			    INT64_T_FORMAT "x) == %" INT64_T_FORMAT
			    "x) = %d\n", CAST(unsigned long long, v),
			    CAST(unsigned long long, l),
			    CAST(unsigned long long, l),
			    matched);
		break;

	case '^':
		matched = (v & l) != l;
		if ((ms->flags & MAGIC_DEBUG) != 0)
			(void) fprintf(stderr, "((%" INT64_T_FORMAT "x & %"
			    INT64_T_FORMAT "x) != %" INT64_T_FORMAT
			    "x) = %d\n", CAST(unsigned long long, v),
			    CAST(unsigned long long, l),
			    CAST(unsigned long long, l), matched);
		break;

	default:
		file_magerror(ms, "cannot happen: invalid relation `%c'",
		    m->reln);
		return -1;
	}