#!/bin/sh

#
# This script is designed to quickly create lots of files in underlying
# test-* directories, do lots of other magic stuff and exit cleanly.
#

set -e

if [ "x$1" = "x" ]; then
	echo "Usage: $0 <check-NN.c>"
	exit
fi

srcdir="${srcdir:-.}"
abs_top_srcdir="${abs_top_srcdir:-$(pwd)/../../}"
abs_top_builddir="${abs_top_builddir:-$(pwd)/../../}"

if echo "$*" | grep -q -- -- ; then
    TEST_DRIVER=$(echo "$*"  | sed -e 's/ -- .*/ -- /g')
    source_full=$(echo "$*"  | sed -e 's/.* //g')
else
    TEST_DRIVER=""
    source_full="$1"
fi

# Compute the .asn1 spec name by the given file name.
source_short=$(echo "$source_full" | sed -e 's/.*\///')
testno=$(echo "$source_short" | cut -f2 -d'-' | cut -f1 -d'.')

CODECS_FLAGS=""
has_oer=$(echo "$source_short" | grep "gen-OER" || :)
if [ ! "$has_oer" ]; then
    CODECS_FLAGS="${CODECS_FLAGS} -DASN_DISABLE_OER_SUPPORT"
fi
has_per=$(echo "$source_short" | grep "gen-PER" || :)
if [ ! "$has_per" ]; then
    CODECS_FLAGS="${CODECS_FLAGS} -DASN_DISABLE_PER_SUPPORT"
fi

args=$(echo "$source_short" | sed -E -e 's/\.c+$//')
source_obj=$(echo "$source_short" | sed -E -e 's/\.c+$/.o/')
ext=$(echo "$source_short" | sed -E -e 's/.*\.(c+)$/\1/')

if [ "$ext" = "cc" ]; then
    COMPILER='$(CXX) $(CXX_CFLAGS)'
else
    COMPILER='$(CC)'
fi

OFS=$IFS
IFS="."
set $args
data_dir=$(echo "$1" | sed -e s/check-/data-/)
shift
IFS=$OFS
AFLAGS="$*"

if [ -d ${data_dir} ]; then
    OPT_DATA_DIR="../${data_dir}"
else
    OPT_DATA_DIR=""
fi

if test "${LIBFUZZER_CFLAGS}" && grep LLVMFuzzer ${source_full} > /dev/null;
then
    MAKE_FUZZER=yes
else
    MAKE_FUZZER=no
fi

# Assume the test fails. Will be removed when it passes well.
testdir=test-${args}
if [ -f "${testdir}-FAILED" ]; then
    rm -rf "${testdir}"
fi
touch "${testdir}-FAILED"

mkdir -p "${testdir}"
ln -fns "../${source_full}" "${testdir}"

asn_module=$(echo "${abs_top_srcdir}/tests/tests-asn1c-compiler/${testno}"-*.asn1)

AUTOGENERATED="# This file is autogenerated by $0 ${source_full} ${AFLAGS}"

# Create a common Makefile for the project
cat <<TARGETS > "${testdir}/Makefile.targets"
${AUTOGENERATED}

COMMON_FLAGS= -I.
CFLAGS = \${COMMON_FLAGS} ${CFLAGS:-} -g -O1
CFLAGS += -DSRCDIR=../${srcdir} ${CODECS_FLAGS}
CXXFLAGS = \${CFLAGS} ${CXXFLAGS}
LIBFUZZER_CFLAGS = ${LIBFUZZER_CFLAGS}
LDFLAGS = ${LDFLAGS:-}

CC ?= ${CC}
CXX ?= ${CXX}

all: compiled-module
	\$(MAKE) check-executable

check-executable: \$(OBJS)
	@rm -f *.core
	${COMPILER} \$(CFLAGS) \$(LDFLAGS) -o check-executable \$(OBJS) -lm

# Compile the corresponding .asn1 spec.
compiled-module: ${asn_module} ${abs_top_builddir}/asn1c/asn1c
	${abs_top_builddir}/asn1c/asn1c		\\
		-S ${abs_top_srcdir}/skeletons	\\
		-Wdebug-compiler		\\
		${AFLAGS} ${asn_module}
	rm -f converter-sample.c
	@touch compiled-module

TARGETS


if [ "${MAKE_FUZZER}" != "yes" ]; then
    CHECK_FUZZER="@echo \"No fuzzer defined, skipping.\""
cat <<TARGETS >> "${testdir}/Makefile.targets"
.PHONY: check-fuzzer
check-fuzzer:
TARGETS
else
    CHECK_FUZZER="ASAN_OPTIONS=detect_leaks=1 UBSAN_OPTIONS=print_stacktrace=1 ./check-fuzzer -timeout=3 -max_total_time=60 -max_len=512 ${OPT_DATA_DIR}"
cat <<TARGETS >> "${testdir}/Makefile.targets"
check-fuzzer: \$(OBJS)
	rm -f ${source_obj}
	${COMPILER} \$(CFLAGS) \$(LIBFUZZER_CFLAGS) -c -o ${source_obj} ${source_short}
	${COMPILER} \$(CFLAGS) \$(LIBFUZZER_CFLAGS) \$(LDFLAGS) -o check-fuzzer \$(OBJS)
	rm -f ${source_obj}
TARGETS
fi

cat <<TARGETS >> "${testdir}/Makefile.targets"

check-succeeded: compiled-module
	\$(MAKE) check-executable
	\$(MAKE) check-fuzzer
	@rm -f check-succeeded
	./check-executable
	\$(MAKE) fuzz
	@touch check-succeeded
	@echo "OK: ${source_full}"

.PHONY: fuzz
fuzz:
	${CHECK_FUZZER}

check: check-succeeded

clean:
	@rm -f *.o check-executable
TARGETS

# Create a BSD- or GNU-specific Makefile for the project.
produce_specific_makefile() {
    local make_type=$1
    local make_file="$testdir/${make_type}makefile"

    if [ ${make_type} = "BSD" ]; then
		cat <<-OBJECTS > ${make_file}
			${AUTOGENERATED}
			SRCS_C!=find . -name \*.c
			SRCS_CXX!=find . -name \*.cc
			SRCS=\$(SRCS_C) \$(SRCS_CXX)
			OBJS=\${SRCS_C:.c=.o} \${SRCS_CXX:.cc=.o}
			include Makefile.targets
		OBJECTS
	else
		cat <<-OBJECTS > ${make_file}
			${AUTOGENERATED}
			SRCS_C := \$(wildcard *.c)
			SRCS_CXX := \$(wildcard *.cc)
			SRCS = \$(SRCS_C) \$(SRCS_CXX)
			OBJS =\$(patsubst %.c,%.o,\$(SRCS_C))
			OBJS+=\$(patsubst %.cc,%.o,\$(SRCS_CXX))
			include Makefile.targets
		OBJECTS
    fi

}

produce_specific_makefile BSD
produce_specific_makefile GNU

# Perform building and checking
${TEST_DRIVER} make -C "$testdir" check

# Make sure the test is not marked as failed any longer.
rm -f "${testdir}-FAILED"