/*
 * This file and its contents are supplied under the terms of the
 * Common Development and Distribution License ("CDDL"), version 1.0.
 * You may only use this file in accordance with the terms of version
 * 1.0 of the CDDL.
 *
 * A full copy of the text of the CDDL should have accompanied this
 * source.  A copy of the CDDL is also available via the Internet at
 * http://www.illumos.org/license/CDDL.
 */

/*
 * Copyright 2020 Joyent, Inc.
 */

/*
 * Collection of common utilities for CTF testing.
 */

#include <strings.h>
#include <libctf.h>
#include "check-common.h"

typedef struct ctftests_lookup_cb {
	ctf_file_t *clc_fp;
	ctf_id_t clc_id;
	const char *clc_name;
} ctftests_lookup_cb_t;

typedef struct ctftest_member_cb {
	ctf_file_t *cmc_fp;
	const check_member_t *cmc_members;
	const char *cmc_name;
} ctftest_member_cb_t;

static int
ctftest_lookup_type_cb(ctf_id_t id, boolean_t root, void *arg)
{
	char buf[2048];
	ctftests_lookup_cb_t *clc = arg;

	if (ctf_type_name(clc->clc_fp, id, buf, sizeof (buf)) == NULL)
		return (0);

	if (strcmp(buf, clc->clc_name) != 0)
		return (0);

	clc->clc_id = id;
	return (1);
}

/*
 * This is a variant on the classic ctf_lookup_by_name(). ctf_lookup_by_name()
 * skips qualifiers, which makes sense given what the consumers of it are trying
 * to do. However, that's not what we want here. So instead we basically have to
 * walk the type table.
 */
static ctf_id_t
ctftest_lookup_type(ctf_file_t *fp, const char *name)
{
	ctftests_lookup_cb_t clc;

	clc.clc_fp = fp;
	clc.clc_id = CTF_ERR;
	clc.clc_name = name;

	(void) ctf_type_iter(fp, B_TRUE, ctftest_lookup_type_cb, &clc);
	return (clc.clc_id);
}

static int
ctftest_lookup_object_cb(const char *obj, ctf_id_t type, ulong_t idx, void *arg)
{
	ctftests_lookup_cb_t *clc = arg;

	if (strcmp(obj, clc->clc_name) == 0) {
		clc->clc_id = type;
		return (1);
	}

	return (0);
}

static ctf_id_t
ctftest_lookup_symbol(ctf_file_t *fp, const char *name)
{
	ctftests_lookup_cb_t clc;

	clc.clc_fp = fp;
	clc.clc_id = CTF_ERR;
	clc.clc_name = name;

	(void) ctf_object_iter(fp, ctftest_lookup_object_cb, &clc);
	return (clc.clc_id);
}

typedef struct ctf_function_cb {
	const char *cfc_name;
	ulong_t *cfc_symp;
	ctf_funcinfo_t *cfc_fip;
} ctf_function_cb_t;

static int
ctftest_lookup_function_cb(const char *name, ulong_t symidx,
    ctf_funcinfo_t *fip, void *arg)
{
	ctf_function_cb_t *cfc = arg;
	if (strcmp(name, cfc->cfc_name) != 0)
		return (0);

	*cfc->cfc_symp = symidx;
	*cfc->cfc_fip = *fip;

	return (1);
}

/*
 * Note, this function finds the first one with a matching name. This must not
 * be used when performing searches where a given name may occur more than once.
 */
static boolean_t
ctftest_lookup_function(ctf_file_t *fp, const char *name, ulong_t *symp,
    ctf_funcinfo_t *fip)
{
	ctf_function_cb_t cfc;

	*symp = 0;
	cfc.cfc_name = name;
	cfc.cfc_symp = symp;
	cfc.cfc_fip = fip;
	(void) ctf_function_iter(fp, ctftest_lookup_function_cb, &cfc);
	return (*symp == 0 ? B_FALSE : B_TRUE);
}

boolean_t
ctftest_check_numbers(ctf_file_t *fp, const check_number_t *tests)
{
	uint_t i;
	boolean_t ret = B_TRUE;

	for (i = 0; tests[i].cn_tname != NULL; i++) {
		ctf_id_t id;
		ctf_encoding_t enc;

		if (ctftest_skip(tests[i].cn_skips)) {
			warnx("skipping check numbers test %s due to known "
			    "compiler issue", tests[i].cn_tname);
			continue;
		}

		id = ctftest_lookup_type(fp, tests[i].cn_tname);
		if (id == CTF_ERR) {
			warnx("failed to look up %s", tests[i].cn_tname);
			ret = B_FALSE;
			continue;
		}

		if (ctf_type_kind(fp, id) != tests[i].cn_kind) {
			warnx("type kind mismatch for %s: got %u, expected %u",
			    tests[i].cn_tname, ctf_type_kind(fp, id),
			    tests[i].cn_kind);
			ret = B_FALSE;
			continue;
		}

		if (ctf_type_encoding(fp, id, &enc) == CTF_ERR) {
			warnx("failed to get type encoding for %s: %s",
			    tests[i].cn_tname, ctf_errmsg(ctf_errno(fp)));
			ret = B_FALSE;
			continue;
		}

		if (enc.cte_format != tests[i].cn_flags) {
			warnx("encoding flags mismatch for %s: got 0x%x, "
			    "expected 0x%x", tests[i].cn_tname, enc.cte_format,
			    tests[i].cn_flags);
			ret = B_FALSE;
			continue;
		}

		if (enc.cte_offset != tests[i].cn_offset) {
			warnx("encoding offset mismatch for %s: got 0x%x, "
			    "expected 0x%x", tests[i].cn_tname, enc.cte_offset,
			    tests[i].cn_offset);
			ret = B_FALSE;
			continue;
		}

		if (enc.cte_bits != tests[i].cn_size) {
			warnx("encoding size mismatch for %s: got 0x%x, "
			    "expected 0x%x", tests[i].cn_tname, enc.cte_bits,
			    tests[i].cn_size);
			ret = B_FALSE;
			continue;
		}
	}

	return (ret);
}

typedef struct ctftests_symbol_cb {
	ctf_file_t	*csc_fp;
	boolean_t	csc_ret;
	const check_symbol_t *csc_tests;
} ctftest_symbol_cb_t;

static int
ctftest_check_symbol_cb(const char *obj, ctf_id_t type, ulong_t idx, void *arg)
{
	ctftest_symbol_cb_t *cb = arg;
	const check_symbol_t *tests = cb->csc_tests;
	ctf_file_t *fp = cb->csc_fp;
	uint_t i;

	for (i = 0; tests[i].cs_symbol != NULL; i++) {
		ctf_id_t id;

		if (strcmp(obj, tests[i].cs_symbol) != 0)
			continue;

		id = ctftest_lookup_type(fp, tests[i].cs_type);
		if (id == CTF_ERR) {
			warnx("failed to lookup type %s for symbol %s",
			    tests[i].cs_type, tests[i].cs_symbol);
			cb->csc_ret = B_FALSE;
			return (0);
		}

		if (id != type) {
			warnx("type mismatch for symbol %s, has type id %u, "
			    "but specified type %s has id %u",
			    tests[i].cs_symbol, type, tests[i].cs_type, id);
			cb->csc_ret = B_FALSE;
			return (0);
		}
	}

	return (0);
}

boolean_t
ctftest_check_symbols(ctf_file_t *fp, const check_symbol_t *tests)
{
	ctftest_symbol_cb_t cb;

	cb.csc_fp = fp;
	cb.csc_ret = B_TRUE;
	cb.csc_tests = tests;
	if (ctf_object_iter(fp, ctftest_check_symbol_cb, &cb) != 0)
		return (B_FALSE);
	return (cb.csc_ret);
}


boolean_t
ctftest_check_descent(const char *symbol, ctf_file_t *fp,
    const check_descent_t *tests, boolean_t quiet)
{
	ctf_id_t base;
	uint_t layer = 0;

	/*
	 * First, find the initial type of the symbol.
	 */
	base = ctftest_lookup_symbol(fp, symbol);
	if (base == CTF_ERR) {
		warnx("failed to lookup type for symbol %s", symbol);
		return (B_FALSE);
	}

	while (tests->cd_tname != NULL) {
		ctf_id_t tid;
		int kind;
		ctf_arinfo_t ari;

		if (base == CTF_ERR) {
			if (!quiet) {
				warnx("encountered non-reference type at layer "
				    "%u while still expecting type %s for "
				    "symbol %s", layer,
				    tests->cd_tname, symbol);
			}
			return (B_FALSE);
		}

		tid = ctftest_lookup_type(fp, tests->cd_tname);
		if (tid == CTF_ERR) {
			if (!quiet) {
				warnx("failed to lookup type %s",
				    tests->cd_tname);
			}
			return (B_FALSE);
		}

		if (tid != base) {
			if (!quiet) {
				warnx("type mismatch at layer %u: found id %u, "
				    "but expecting type id %u for type %s, "
				    "symbol %s", layer, base, tid,
				    tests->cd_tname, symbol);
			}
			return (B_FALSE);
		}

		kind = ctf_type_kind(fp, base);
		if (kind != tests->cd_kind) {
			if (!quiet) {
				warnx("type kind mismatch at layer %u: found "
				    "kind %u, but expected kind %u for %s, "
				    "symbol %s", layer, kind, tests->cd_kind,
				    tests->cd_tname, symbol);
			}
			return (B_FALSE);
		}

		switch (kind) {
		case CTF_K_ARRAY:
			if (ctf_array_info(fp, base, &ari) == CTF_ERR) {
				if (!quiet) {
					warnx("failed to lookup array info at "
					    "layer %u for type %s, symbol "
					    "%s: %s", base, tests->cd_tname,
					    symbol, ctf_errmsg(ctf_errno(fp)));
				}
				return (B_FALSE);
			}

			if (tests->cd_nents != ari.ctr_nelems) {
				if (!quiet) {
					warnx("array element mismatch at layer "
					    "%u for type %s, symbol %s: found "
					    "%u, expected %u", layer,
					    tests->cd_tname, symbol,
					    ari.ctr_nelems, tests->cd_nents);
				}
				return (B_FALSE);
			}

			tid = ctftest_lookup_type(fp, tests->cd_contents);
			if (tid == CTF_ERR) {
				if (!quiet) {
					warnx("failed to look up type %s",
					    tests->cd_contents);
				}
				return (B_FALSE);
			}

			if (ari.ctr_contents != tid) {
				if (!quiet) {
					warnx("array contents mismatch at "
					    "layer %u for type %s, symbol %s: "
					    "found %u, expected %s/%u", layer,
					    tests->cd_tname, symbol,
					    ari.ctr_contents,
					    tests->cd_contents, tid);
				}
				return (B_FALSE);
			}
			base = ari.ctr_contents;
			break;
		default:
			base = ctf_type_reference(fp, base);
			break;
		}

		tests++;
		layer++;
	}

	if (base != CTF_ERR) {
		if (!quiet) {
			warnx("found additional type %u in chain, "
			    "but expected no more", base);
		}
		return (B_FALSE);
	}

	return (B_TRUE);
}

int
ctftest_check_enum_count(const char *name, int value, void *arg)
{
	uint_t *u = arg;
	*u = *u + 1;
	return (0);
}

int
ctftest_check_enum_value(const char *name, int value, void *arg)
{
	uint_t i;
	const check_enum_t *enums = arg;

	for (i = 0; enums[i].ce_name != NULL; i++) {
		if (strcmp(enums[i].ce_name, name) != 0)
			continue;
		if (enums[i].ce_value == (int64_t)value)
			return (0);
		warnx("enum %s value mismatch: found %d, expected %" PRId64,
		    name, value, enums[i].ce_value);
		return (1);
	}

	warnx("found no matching entry for enum member %s", name);
	return (1);
}

boolean_t
ctftest_check_enum(const char *type, ctf_file_t *fp, const check_enum_t *enums)
{
	int ret;
	uint_t tcount, ecount;
	ctf_id_t base;

	if ((base = ctftest_lookup_type(fp, type)) == CTF_ERR) {
		warnx("Failed to look up type %s", type);
		return (B_FALSE);
	}

	if (ctf_type_kind(fp, base) != CTF_K_ENUM) {
		warnx("%s is not an enum", type);
		return (B_FALSE);
	}

	/*
	 * First count how many entries we have.
	 */
	tcount = 0;
	while (enums[tcount].ce_name != NULL) {
		tcount++;
	}

	ecount = 0;
	if (ctf_enum_iter(fp, base, ctftest_check_enum_count, &ecount) != 0) {
		warnx("failed to walk enum %s: %s", type,
		    ctf_errmsg(ctf_errno(fp)));
		return (B_FALSE);
	}

	if (tcount != ecount) {
		warnx("enum value mismatch: expected %u values, but found %u",
		    tcount, ecount);
		return (B_FALSE);
	}

	if ((ret = ctf_enum_iter(fp, base, ctftest_check_enum_value,
	    (void *)enums)) != 0) {
		if (ret == -1) {
			warnx("failed to walk enum %s: %s", type,
			    ctf_errmsg(ctf_errno(fp)));
		}
		return (B_FALSE);
	}

	return (B_TRUE);
}

int
ctftest_check_member_count(const char *mname, ctf_id_t mtype, ulong_t bitoff,
    void *arg)
{
	uint_t *countp = arg;
	*countp = *countp + 1;
	return (0);
}

int
ctftest_check_members_cb(const char *mname, ctf_id_t mtype, ulong_t bitoff,
    void *arg)
{
	uint_t i;
	const ctftest_member_cb_t *cmc = arg;
	const check_member_t *members = cmc->cmc_members;
	ctf_file_t *fp = cmc->cmc_fp;

	for (i = 0; members[i].cm_name != NULL; i++) {
		boolean_t bad = B_FALSE;
		char buf[2048];

		if (strcmp(mname, members[i].cm_name) != 0)
			continue;

		if (bitoff != members[i].cm_offset) {
			warnx("member %s of type %s has mismatched bit offset: "
			    "found %lu, expected %lu", mname, cmc->cmc_name,
			    bitoff, members[i].cm_offset);
			bad = B_TRUE;
		}

		if (ctf_type_name(fp, mtype, buf, sizeof (buf)) == NULL) {
			warnx("failed to obtain type name for member %s",
			    mname, ctf_errmsg(ctf_errno(fp)));
			bad = B_TRUE;
		} else if (strcmp(buf, members[i].cm_type) != 0) {
			warnx("member %s has bad type, found %s, expected %s",
			    mname, buf, members[i].cm_type);
			bad = B_TRUE;
		}

		return (bad ? 1 : 0);
	}

	warnx("found no matching entry for member %s of type %s", mname,
	    cmc->cmc_name);
	return (1);
}

boolean_t
ctftest_check_members(const char *type, ctf_file_t *fp, int kind,
    size_t size, const check_member_t *members)
{
	int ret;
	uint_t tcount, mcount;
	ctf_id_t base;
	ctftest_member_cb_t cmc;

	if ((base = ctftest_lookup_type(fp, type)) == CTF_ERR) {
		warnx("failed to look up type %s", type);
		return (B_FALSE);
	}

	if (ctf_type_kind(fp, base) != kind) {
		warnx("%s has kind %s, expected %s", type,
		    ctf_kind_name(fp, ctf_type_kind(fp, base)),
		    ctf_kind_name(fp, kind));
		return (B_FALSE);
	}

	if (size != ctf_type_size(fp, base)) {
		warnx("%s has bad size, expected %lu, found %lu",
		    type, size, ctf_type_size(fp, base));
		return (B_FALSE);
	}

	/*
	 * First count how many entries we have.
	 */
	tcount = 0;
	while (members[tcount].cm_name != NULL) {
		tcount++;
	}

	mcount = 0;
	if (ctf_member_iter(fp, base, ctftest_check_member_count, &mcount) !=
	    0) {
		warnx("failed to walk members of %s: %s", type,
		    ctf_errmsg(ctf_errno(fp)));
		return (B_FALSE);
	}

	if (tcount != mcount) {
		warnx("type member mismatch: expected %u values, but found %u",
		    tcount, mcount);
		return (B_FALSE);
	}

	cmc.cmc_fp = fp;
	cmc.cmc_members = members;
	cmc.cmc_name = type;
	if ((ret = ctf_member_iter(fp, base, ctftest_check_members_cb,
	    &cmc)) != 0) {
		if (ret == -1) {
			warnx("failed to walk type %s: %s", type,
			    ctf_errmsg(ctf_errno(fp)));
		}
		return (B_FALSE);
	}

	return (B_TRUE);
}

boolean_t
ctftest_check_function(const char *symbol, ctf_file_t *fp, const char *rtype,
    uint_t nargs, uint_t flags, const char **argv)
{
	ulong_t sym;
	ctf_funcinfo_t fi;
	uint_t i;
	boolean_t ret = B_TRUE;
	ctf_id_t *args;
	char buf[2048];


	if (!ctftest_lookup_function(fp, symbol, &sym, &fi)) {
		warnx("failed to look up function %s", symbol);
		return (B_FALSE);
	}

	if (ctf_type_name(fp, fi.ctc_return, buf, sizeof (buf)) == NULL) {
		warnx("failed to lookup return type name for function %s",
		    symbol);
		ret = B_FALSE;
	} else if (strcmp(rtype, buf) != 0) {
		warnx("return type has wrong type: found %s, expected %s",
		    buf, rtype);
		ret = B_FALSE;
	}

	if (nargs != fi.ctc_argc) {
		warnx("function argument mismatch: found %u, expected %u",
		    fi.ctc_argc, nargs);
		ret = B_FALSE;
	}

	if (flags != fi.ctc_flags) {
		warnx("function flags mismatch, found 0x%x, expected 0x%x",
		    fi.ctc_flags, flags);
		ret = B_FALSE;
	}

	if (!ret || fi.ctc_argc == 0) {
		return (ret);
	}

	if ((args = calloc(fi.ctc_argc, sizeof (ctf_id_t))) == NULL) {
		warnx("failed to allocate memory for function arguments");
		return (B_FALSE);
	}

	if (ctf_func_args(fp, sym, fi.ctc_argc, args) != 0) {
		warnx("failed to get function information: %s",
		    ctf_errmsg(ctf_errno(fp)));
		free(args);
		return (B_FALSE);
	}

	for (i = 0; i < fi.ctc_argc; i++) {
		if (ctf_type_name(fp, args[i], buf, sizeof (buf)) == NULL) {
			warnx("failed to obtain type name for argument %u",
			    i, ctf_errmsg(ctf_errno(fp)));
			ret = B_FALSE;
			break;
		}

		if (strcmp(buf, argv[i]) != 0) {
			warnx("argument %u has wrong type: found %s, "
			    "expected %s", i, buf, argv[i]);
			ret = B_FALSE;
			break;
		}
	}

	free(args);
	return (ret);
}

boolean_t
ctftest_check_fptr(const char *type, ctf_file_t *fp, const char *rtype,
    uint_t nargs, uint_t flags, const char **argv)
{
	ctf_id_t tid;
	ctf_funcinfo_t fi;
	uint_t i;
	boolean_t ret = B_TRUE;
	ctf_id_t *args;
	char buf[2048];


	if ((tid = ctf_lookup_by_name(fp, type)) == CTF_ERR) {
		warnx("failed to look up type %s: %s", type,
		    ctf_errmsg(ctf_errno(fp)));
		return (B_FALSE);
	}

	/*
	 * Perform two CTF type resolves, one for the function pointer and one
	 * for the typedef that gets passed in.
	 */
	if ((tid = ctf_type_resolve(fp, tid)) == CTF_ERR) {
		warnx("failed to convert type %s to base type: %s", type,
		    ctf_errmsg(ctf_errno(fp)));
		return (B_FALSE);
	}

	if (ctf_type_kind(fp, tid) == CTF_K_POINTER &&
	    (tid = ctf_type_reference(fp, tid)) == CTF_ERR) {
		warnx("failed to convert type %s to base type: %s", type,
		    ctf_errmsg(ctf_errno(fp)));
		return (B_FALSE);
	}

	if (ctf_func_info_by_id(fp, tid, &fi) != 0) {
		warnx("failed to get function information for type %s: %s",
		    type, ctf_errmsg(ctf_errno(fp)));
		return (B_FALSE);
	}

	if (ctf_type_name(fp, fi.ctc_return, buf, sizeof (buf)) == NULL) {
		warnx("failed to lookup return type name for function %s",
		    type);
		ret = B_FALSE;
	} else if (strcmp(rtype, buf) != 0) {
		warnx("return type has wrong type: found %s, expected %s",
		    buf, rtype);
		ret = B_FALSE;
	}

	if (nargs != fi.ctc_argc) {
		warnx("function argument mismatch: found %u, expected %u",
		    fi.ctc_argc, nargs);
		ret = B_FALSE;
	}

	if (flags != fi.ctc_flags) {
		warnx("function flags mismatch, found 0x%x, expected 0x%x",
		    fi.ctc_flags, flags);
		ret = B_FALSE;
	}

	if (!ret || fi.ctc_argc == 0) {
		return (ret);
	}

	if ((args = calloc(fi.ctc_argc, sizeof (ctf_id_t))) == NULL) {
		warnx("failed to allocate memory for function arguments");
		return (B_FALSE);
	}

	if (ctf_func_args_by_id(fp, tid, fi.ctc_argc, args) != 0) {
		warnx("failed to get function information: %s",
		    ctf_errmsg(ctf_errno(fp)));
		free(args);
		return (B_FALSE);
	}

	for (i = 0; i < fi.ctc_argc; i++) {
		if (ctf_type_name(fp, args[i], buf, sizeof (buf)) == NULL) {
			warnx("failed to obtain type name for argument %u",
			    i, ctf_errmsg(ctf_errno(fp)));
			ret = B_FALSE;
			break;
		}

		if (strcmp(buf, argv[i]) != 0) {
			warnx("argument %u has wrong type: found %s, "
			    "expected %s", i, buf, argv[i]);
			ret = B_FALSE;
			break;
		}
	}

	free(args);
	return (ret);
}

boolean_t
ctftest_check_size(const char *type, ctf_file_t *fp, size_t size)
{
	ctf_id_t base;

	if ((base = ctftest_lookup_type(fp, type)) == CTF_ERR) {
		warnx("Failed to look up type %s", type);
		return (B_FALSE);
	}

	if (size != ctf_type_size(fp, base)) {
		warnx("%s has bad size, expected %lu, found %lu",
		    type, size, ctf_type_size(fp, base));
		return (B_FALSE);
	}

	return (B_TRUE);
}

typedef struct ctftest_duplicates {
	ctf_file_t *ctd_fp;
	char **ctd_names;
	size_t ctd_len;
	size_t ctd_curent;
	boolean_t ctd_ret;
} ctftest_duplicates_t;

static int
ctftest_duplicates_cb(ctf_id_t id, boolean_t root, void *arg)
{
	char buf[2048];
	ctftest_duplicates_t *dup = arg;
	size_t i;

	if (ctf_type_name(dup->ctd_fp, id, buf, sizeof (buf)) == NULL) {
		warnx("failed to lookup name for id %ld", id);
		dup->ctd_ret = B_FALSE;
		return (1);
	}

	for (i = 0; i < dup->ctd_curent; i++) {
		if (strcmp(buf, dup->ctd_names[i]) == 0) {
			warnx("encountered duplicate type '%s'", buf);
			dup->ctd_ret = B_FALSE;
			/*
			 * Don't break out of the loop and keep going in case we
			 * find another duplicate.
			 */
			return (0);
		}
	}

	if (dup->ctd_curent == dup->ctd_len) {
		char **n;
		size_t newlen = dup->ctd_len * 2;

		n = recallocarray(dup->ctd_names, dup->ctd_len, newlen,
		    sizeof (char *));
		if (n == NULL) {
			warnx("failed to resize type name array");
			dup->ctd_ret = B_FALSE;
			return (1);
		}

		dup->ctd_names = n;
		dup->ctd_len = newlen;
	}

	dup->ctd_names[dup->ctd_curent] = strdup(buf);
	if (dup->ctd_names[dup->ctd_curent] == NULL) {
		warn("failed to duplicate type name");
		dup->ctd_ret = B_FALSE;
		return (1);
	}
	dup->ctd_curent++;

	return (0);
}

boolean_t
ctftest_duplicates(ctf_file_t *fp)
{
	size_t i;
	ctftest_duplicates_t d;

	bzero(&d, sizeof (d));
	d.ctd_fp = fp;
	d.ctd_len = 4;
	d.ctd_ret = B_TRUE;
	d.ctd_names = recallocarray(NULL, 0, d.ctd_len, sizeof (char *));
	if (d.ctd_names == NULL) {
		warnx("failed to allocate duplicate name storage");
		return (B_FALSE);
	}

	(void) ctf_type_iter(fp, B_TRUE, ctftest_duplicates_cb, &d);

	for (i = 0; i < d.ctd_curent; i++) {
		free(d.ctd_names[i]);
	}
	free(d.ctd_names);

	return (d.ctd_ret);
}

boolean_t
ctftest_skip(check_skip_t skip)
{
	const char *compiler;

	if (skip == 0) {
		return (B_FALSE);
	}

	compiler = getenv("ctf_cc_type");
	if (compiler == NULL) {
		return (B_FALSE);
	}

	if ((skip & SKIP_CLANG) != 0 && strcmp(compiler, "clang") == 0)
		return (B_TRUE);

	return (B_FALSE);
}